diff --git a/.github/workflows/ci-build.yaml b/.github/workflows/ci-build.yaml index 986eeae4917a0..6b57b1e6bab2b 100644 --- a/.github/workflows/ci-build.yaml +++ b/.github/workflows/ci-build.yaml @@ -194,7 +194,7 @@ jobs: - name: Checkout code uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1 - name: Create symlink in GOPATH - run: ln -s $(pwd) ~/go/src/github.com/argoproj/argo-cd + run: ln -s $(pwd) ~/go/src/github.com/argoproj/argo-cd - name: Setup Golang uses: actions/setup-go@4dc6199c7b1a012772edbd06daecab0f50c9053c # v6.1.0 with: @@ -271,13 +271,13 @@ jobs: # We need to vendor go modules for codegen yet go mod download go mod vendor -v - # generalizing repo name for forks: ${{ github.event.repository.name }} - working-directory: /home/runner/go/src/github.com/argoproj/${{ github.event.repository.name }} + # generalizing repo name for forks: ${{ github.event.repository.name }} + working-directory: /home/runner/go/src/github.com/argoproj/${{ github.event.repository.name }} - name: Install toolchain for codegen run: | make install-codegen-tools-local make install-go-tools-local - # generalizing repo name for forks: ${{ github.event.repository.name }} + # generalizing repo name for forks: ${{ github.event.repository.name }} working-directory: /home/runner/go/src/github.com/argoproj/${{ github.event.repository.name }} # We install kustomize in the dist directory - name: Add dist to PATH @@ -440,9 +440,6 @@ jobs: - changes env: ARGOCD_FAKE_IN_CLUSTER: 'true' - ARGOCD_SSH_DATA_PATH: '/tmp/argo-e2e/app/config/ssh' - ARGOCD_TLS_DATA_PATH: '/tmp/argo-e2e/app/config/tls' - ARGOCD_E2E_SSH_KNOWN_HOSTS: '../fixture/certs/ssh_known_hosts' ARGOCD_E2E_K3S: 'true' ARGOCD_IN_CI: 'true' ARGOCD_E2E_APISERVER_PORT: '8088' diff --git a/Makefile b/Makefile index bedfd0266a5bf..c9b3361d82e6b 100644 --- a/Makefile +++ b/Makefile @@ -76,8 +76,10 @@ ARGOCD_E2E_REDIS_PORT?=6379 ARGOCD_E2E_DEX_PORT?=5556 ARGOCD_E2E_YARN_HOST?=localhost ARGOCD_E2E_DISABLE_AUTH?= +ARGOCD_E2E_DIR?=/tmp/argo-e2e ARGOCD_E2E_TEST_TIMEOUT?=90m +ARGOCD_E2E_RERUN_FAILS?=5 ARGOCD_IN_CI?=false ARGOCD_TEST_E2E?=true @@ -467,7 +469,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 - DIST_DIR=${DIST_DIR} RERUN_FAILS=5 PACKAGES="./test/e2e" ARGOCD_E2E_RECORD=${ARGOCD_E2E_RECORD} ARGOCD_CONFIG_DIR=$(HOME)/.config/argocd-e2e ARGOCD_GPG_ENABLED=true NO_PROXY=* ./hack/test.sh -timeout $(ARGOCD_E2E_TEST_TIMEOUT) -v -args -test.gocoverdir="$(PWD)/test-results" + DIST_DIR=${DIST_DIR} RERUN_FAILS=$(ARGOCD_E2E_RERUN_FAILS) PACKAGES="./test/e2e" ARGOCD_E2E_RECORD=${ARGOCD_E2E_RECORD} ARGOCD_CONFIG_DIR=$(HOME)/.config/argocd-e2e ARGOCD_GPG_ENABLED=true NO_PROXY=* ./hack/test.sh -timeout $(ARGOCD_E2E_TEST_TIMEOUT) -v -args -test.gocoverdir="$(PWD)/test-results" # Spawns a shell in the test server container for debugging purposes debug-test-server: test-tools-image @@ -491,13 +493,13 @@ start-e2e-local: mod-vendor-local dep-ui-local cli-local kubectl create ns argocd-e2e-external || true kubectl create ns argocd-e2e-external-2 || true kubectl config set-context --current --namespace=argocd-e2e - kustomize build test/manifests/base | kubectl apply --server-side -f - + kustomize build test/manifests/base | kubectl apply --server-side --force-conflicts -f - kubectl apply -f https://raw.githubusercontent.com/open-cluster-management/api/a6845f2ebcb186ec26b832f60c988537a58f3859/cluster/v1alpha1/0000_04_clusters.open-cluster-management.io_placementdecisions.crd.yaml # Create GPG keys and source directories - if test -d /tmp/argo-e2e/app/config/gpg; then rm -rf /tmp/argo-e2e/app/config/gpg/*; fi - mkdir -p /tmp/argo-e2e/app/config/gpg/keys && chmod 0700 /tmp/argo-e2e/app/config/gpg/keys - mkdir -p /tmp/argo-e2e/app/config/gpg/source && chmod 0700 /tmp/argo-e2e/app/config/gpg/source - mkdir -p /tmp/argo-e2e/app/config/plugin && chmod 0700 /tmp/argo-e2e/app/config/plugin + if test -d $(ARGOCD_E2E_DIR)/app/config/gpg; then rm -rf $(ARGOCD_E2E_DIR)/app/config/gpg/*; fi + mkdir -p $(ARGOCD_E2E_DIR)/app/config/gpg/keys && chmod 0700 $(ARGOCD_E2E_DIR)/app/config/gpg/keys + mkdir -p $(ARGOCD_E2E_DIR)/app/config/gpg/source && chmod 0700 $(ARGOCD_E2E_DIR)/app/config/gpg/source + mkdir -p $(ARGOCD_E2E_DIR)/app/config/plugin && chmod 0700 $(ARGOCD_E2E_DIR)/app/config/plugin # create folders to hold go coverage results for each component mkdir -p /tmp/coverage/app-controller mkdir -p /tmp/coverage/api-server @@ -506,13 +508,14 @@ start-e2e-local: mod-vendor-local dep-ui-local cli-local mkdir -p /tmp/coverage/notification mkdir -p /tmp/coverage/commit-server # set paths for locally managed ssh known hosts and tls certs data - ARGOCD_SSH_DATA_PATH=/tmp/argo-e2e/app/config/ssh \ - ARGOCD_TLS_DATA_PATH=/tmp/argo-e2e/app/config/tls \ - ARGOCD_GPG_DATA_PATH=/tmp/argo-e2e/app/config/gpg/source \ - ARGOCD_GNUPGHOME=/tmp/argo-e2e/app/config/gpg/keys \ + ARGOCD_E2E_DIR=$(ARGOCD_E2E_DIR) \ + ARGOCD_SSH_DATA_PATH=$(ARGOCD_E2E_DIR)/app/config/ssh \ + ARGOCD_TLS_DATA_PATH=$(ARGOCD_E2E_DIR)/app/config/tls \ + ARGOCD_GPG_DATA_PATH=$(ARGOCD_E2E_DIR)/app/config/gpg/source \ + ARGOCD_GNUPGHOME=$(ARGOCD_E2E_DIR)/app/config/gpg/keys \ ARGOCD_GPG_ENABLED=$(ARGOCD_GPG_ENABLED) \ - ARGOCD_PLUGINCONFIGFILEPATH=/tmp/argo-e2e/app/config/plugin \ - ARGOCD_PLUGINSOCKFILEPATH=/tmp/argo-e2e/app/config/plugin \ + ARGOCD_PLUGINCONFIGFILEPATH=$(ARGOCD_E2E_DIR)/app/config/plugin \ + ARGOCD_PLUGINSOCKFILEPATH=$(ARGOCD_E2E_DIR)/app/config/plugin \ ARGOCD_GIT_CONFIG=$(PWD)/test/e2e/fixture/gitconfig \ ARGOCD_E2E_DISABLE_AUTH=false \ ARGOCD_ZJWT_FEATURE_FLAG=always \ diff --git a/docs/developer-guide/test-e2e.md b/docs/developer-guide/test-e2e.md index 22f9c76236044..17cea2eaa6b72 100644 --- a/docs/developer-guide/test-e2e.md +++ b/docs/developer-guide/test-e2e.md @@ -3,29 +3,33 @@ The test [directory](https://github.com/argoproj/argo-cd/tree/master/test) contains E2E tests and test applications. The tests assume that Argo CD services are installed into `argocd-e2e` namespace or cluster in current context. A throw-away namespace `argocd-e2e***` is created prior to the execution of the tests. The throw-away namespace is used as a target namespace for test applications. -The [/test/e2e/testdata](https://github.com/argoproj/argo-cd/tree/master/test/e2e/testdata) directory contains various Argo CD applications. Before test execution, the directory is copied into `/tmp/argo-e2e***` temp directory and used in tests as a +The [/test/e2e/testdata](https://github.com/argoproj/argo-cd/tree/master/test/e2e/testdata) directory contains various Argo CD applications. Before test execution, the directory is copied into `/tmp/argo-e2e***` temp directory (configurable by `ARGOCD_E2E_DIR`) and used in tests as a Git repository via file url: `file:///tmp/argo-e2e***`. > [!NOTE] +> You might get an error such as `unable to ls-remote HEAD on repository: failed to list refs: repository not found` when querying the local repository exposed through the e2e server running in a container. +> This is often caused by `/tmp` directoring sharing protection. You can configure a different directory with `ARGOCD_E2E_DIR`, or disable the directory sharing protection. +> > **Rancher Desktop Volume Sharing** > -> The e2e git server runs in a container. If you are using Rancher Desktop, you will need to enable volume sharing for -> the e2e container to access the testdata directory. To do this, add the following to +> To do enable `/tmp` sharing, add the following to > `~/Library/Application\ Support/rancher-desktop/lima/_config/override.yaml` and restart Rancher Desktop: > > ```yaml > mounts: -> - location: /private/tmp -> writable: true +> - location: /private/tmp +> writable: true > ``` ## Running Tests Locally ### With virtualized chain + 1. Start the e2e version `make start-e2e` 2. Run the tests: `make test-e2e` ### With local chain + 1. Start the e2e version `make start-e2e-local` 2. Run the tests: `make test-e2e-local` @@ -37,32 +41,32 @@ You can observe the tests by using the UI [http://localhost:4000/applications](h The Makefile's `start-e2e` target starts instances of ArgoCD on your local machine, of which the most will require a network listener. If, for any reason, your machine already has network services listening on the same ports, then the e2e tests will not run. You can derive from the defaults by setting the following environment variables before you run `make start-e2e`: -* `ARGOCD_E2E_APISERVER_PORT`: Listener port for `argocd-server` (default: `8080`) -* `ARGOCD_E2E_REPOSERVER_PORT`: Listener port for `argocd-reposerver` (default: `8081`) -* `ARGOCD_E2E_DEX_PORT`: Listener port for `dex` (default: `5556`) -* `ARGOCD_E2E_REDIS_PORT`: Listener port for `redis` (default: `6379`) -* `ARGOCD_E2E_YARN_CMD`: Command to use for starting the UI via Yarn (default: `yarn`) +- `ARGOCD_E2E_APISERVER_PORT`: Listener port for `argocd-server` (default: `8080`) +- `ARGOCD_E2E_REPOSERVER_PORT`: Listener port for `argocd-reposerver` (default: `8081`) +- `ARGOCD_E2E_DEX_PORT`: Listener port for `dex` (default: `5556`) +- `ARGOCD_E2E_REDIS_PORT`: Listener port for `redis` (default: `6379`) +- `ARGOCD_E2E_YARN_CMD`: Command to use for starting the UI via Yarn (default: `yarn`) +- `ARGOCD_E2E_DIR`: Local path to the repository to use for ephemeral test data If you have changed the port for `argocd-server`, be sure to also set `ARGOCD_SERVER` environment variable to point to that port, e.g. `export ARGOCD_SERVER=localhost:8888` before running `make test-e2e` so that the test will communicate to the correct server component. - ## Test Isolation Some effort has been made to balance test isolation with speed. Tests are isolated as follows as each test gets: -* A random 5 character ID. -* A unique Git repository containing the `testdata` in `/tmp/argo-e2e/${id}`. -* A namespace `argocd-e2e-ns-${id}`. -* A primary name for the app `argocd-e2e-${id}`. +- A random 5 character ID. +- A unique Git repository containing the `testdata` in `/tmp/argo-e2e/${id}`. +- A namespace `argocd-e2e-ns-${id}`. +- A primary name for the app `argocd-e2e-${id}`. ## Run only a subset of tests -Running all tests locally is a time-consuming process. To run only a subset of tests, you can set the `TEST_MODULE` environment variable. -For example, to run only the OCI tests, you can set the variable as follows: `make TEST_MODULE=./test/e2e/oci_test.go test-e2e-local` +Running all tests locally is a time-consuming process. To run only a subset of tests, you can set the `TEST_MODULE` environment variable. +For example, to run only the OCI tests, you can set the variable as follows: `make TEST_MODULE=./test/e2e/oci_test.go test-e2e-local` If you want to get a more fine-grained control over which tests to run, you can also try `make TEST_FLAGS="-run " test-e2e-local` -For individual tests you can run them using the IDE run test feature - +For individual tests you can run them using the IDE run test feature + ## Troubleshooting **Tests fails to delete `argocd-e2e-ns-*` namespaces.** diff --git a/test/e2e/fixture/app/context_cmp.go b/test/e2e/fixture/app/context_cmp.go index 990309563fbf7..883c1f59a40ba 100644 --- a/test/e2e/fixture/app/context_cmp.go +++ b/test/e2e/fixture/app/context_cmp.go @@ -26,7 +26,7 @@ func (c *Context) RunningCMPServer(configFile string) *Context { // It blocks until the socket file is created or times out after 10 seconds. func startCMPServer(t *testing.T, configDir string) { t.Helper() - pluginSockFilePath := path.Join(fixture.TmpDir, fixture.PluginSockFilePath) + pluginSockFilePath := path.Join(fixture.TmpDir(), fixture.PluginSockFilePath) t.Setenv("ARGOCD_BINARY_NAME", "argocd-cmp-server") // ARGOCD_PLUGINSOCKFILEPATH should be set as the same value as repo server env var t.Setenv("ARGOCD_PLUGINSOCKFILEPATH", pluginSockFilePath) diff --git a/test/e2e/fixture/applicationsets/context.go b/test/e2e/fixture/applicationsets/context.go index 8e33d935cd57d..952b373656168 100644 --- a/test/e2e/fixture/applicationsets/context.go +++ b/test/e2e/fixture/applicationsets/context.go @@ -23,6 +23,10 @@ type Context struct { func Given(t *testing.T) *Context { t.Helper() + + fixture.EnsureCleanState(t) + + // TODO: Appset EnsureCleanState specific logic should be moved to the main EnsureCleanState function (https://github.com/argoproj/argo-cd/issues/24307) utils.EnsureCleanState(t) return &Context{t: t} } diff --git a/test/e2e/fixture/applicationsets/utils/cmd.go b/test/e2e/fixture/applicationsets/utils/cmd.go deleted file mode 100644 index a69759649ed29..0000000000000 --- a/test/e2e/fixture/applicationsets/utils/cmd.go +++ /dev/null @@ -1,25 +0,0 @@ -package utils - -import ( - "context" - "os" - "os/exec" - "strings" - - argoexec "github.com/argoproj/argo-cd/v3/util/exec" -) - -func Run(workDir, name string, args ...string) (string, error) { - return RunWithStdin("", workDir, name, args...) -} - -func RunWithStdin(stdin, workDir, name string, args ...string) (string, error) { - cmd := exec.CommandContext(context.Background(), name, args...) - if stdin != "" { - cmd.Stdin = strings.NewReader(stdin) - } - cmd.Env = os.Environ() - cmd.Dir = workDir - - return argoexec.RunCommandExt(cmd, argoexec.CmdOpts{}) -} diff --git a/test/e2e/fixture/applicationsets/utils/fixture.go b/test/e2e/fixture/applicationsets/utils/fixture.go index da49c61cddc68..6a6272519301b 100644 --- a/test/e2e/fixture/applicationsets/utils/fixture.go +++ b/test/e2e/fixture/applicationsets/utils/fixture.go @@ -28,7 +28,6 @@ import ( "github.com/argoproj/argo-cd/v3/pkg/apis/application/v1alpha1" appclientset "github.com/argoproj/argo-cd/v3/pkg/client/clientset/versioned" "github.com/argoproj/argo-cd/v3/test/e2e/fixture" - "github.com/argoproj/argo-cd/v3/util/errors" ) type ExternalNamespace string @@ -49,7 +48,6 @@ const ( // Note: this is NOT the namespace the ApplicationSet controller is deployed to; see ArgoCDNamespace. ApplicationsResourcesNamespace = "applicationset-e2e" - TmpDir = "/tmp/applicationset-e2e" TestingLabel = "e2e.argoproj.io" ) @@ -209,12 +207,6 @@ func EnsureCleanState(t *testing.T) { require.NoError(t, waitForExpectedClusterState(t)) - // remove tmp dir - require.NoError(t, os.RemoveAll(TmpDir)) - - // create tmp dir - errors.NewHandler(t).FailOnErr(Run("", "mkdir", "-p", TmpDir)) - // We can switch user and as result in previous state we will have non-admin user, this case should be reset require.NoError(t, fixture.LoginAs("admin")) diff --git a/test/e2e/fixture/certs/certs.go b/test/e2e/fixture/certs/certs.go index 556e559224b49..05cbe22708f1e 100644 --- a/test/e2e/fixture/certs/certs.go +++ b/test/e2e/fixture/certs/certs.go @@ -29,9 +29,9 @@ func AddCustomCACert(t *testing.T) { errors.NewHandler(t).FailOnErr(fixture.RunCli(args...)) certData, err := os.ReadFile(caCertPath) require.NoError(t, err) - err = os.WriteFile(fixture.TmpDir+"/app/config/tls/localhost", certData, 0o644) + err = os.WriteFile(fixture.TmpDir()+"/app/config/tls/localhost", certData, 0o644) require.NoError(t, err) - err = os.WriteFile(fixture.TmpDir+"/app/config/tls/127.0.0.1", certData, 0o644) + err = os.WriteFile(fixture.TmpDir()+"/app/config/tls/127.0.0.1", certData, 0o644) require.NoError(t, err) } else { args := []string{"cert", "add-tls", "argocd-e2e-server", "--upsert", "--from", caCertPath} @@ -58,7 +58,7 @@ func AddCustomSSHKnownHostsKeys(t *testing.T) { if fixture.IsLocal() { knownHostsData, err := os.ReadFile(knownHostsPath) require.NoError(t, err) - err = os.WriteFile(fixture.TmpDir+"/app/config/ssh/ssh_known_hosts", knownHostsData, 0o644) + err = os.WriteFile(fixture.TmpDir()+"/app/config/ssh/ssh_known_hosts", knownHostsData, 0o644) require.NoError(t, err) } else { fixture.RestartAPIServer(t) diff --git a/test/e2e/fixture/fixture.go b/test/e2e/fixture/fixture.go index 564cdb1d7364e..bd57f656fc679 100644 --- a/test/e2e/fixture/fixture.go +++ b/test/e2e/fixture/fixture.go @@ -56,7 +56,8 @@ const ( defaultNotificationServer = "localhost:9001" // ensure all repos are in one directory tree, so we can easily clean them up - TmpDir = "/tmp/argo-e2e" + // TmpDir can be overridden via ARGOCD_E2E_DIR environment variable + defaultTmpDir = "/tmp/argo-e2e" repoDir = "testdata.git" submoduleDir = "submodule.git" submoduleParentDir = "submoduleParent.git" @@ -156,6 +157,12 @@ func AppNamespace() string { return GetEnvWithDefault("ARGOCD_E2E_APP_NAMESPACE", ArgoCDAppNamespace) } +// TmpDir returns the base directory for e2e test data. +// It can be overridden via the ARGOCD_E2E_DIR environment variable. +func TmpDir() string { + return GetEnvWithDefault("ARGOCD_E2E_DIR", defaultTmpDir) +} + // 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() @@ -307,15 +314,15 @@ func ShortId() string { } func repoDirectory() string { - return path.Join(TmpDir, repoDir) + return path.Join(TmpDir(), repoDir) } func submoduleDirectory() string { - return path.Join(TmpDir, submoduleDir) + return path.Join(TmpDir(), submoduleDir) } func submoduleParentDirectory() string { - return path.Join(TmpDir, submoduleParentDir) + return path.Join(TmpDir(), submoduleParentDir) } const ( @@ -332,16 +339,18 @@ const ( ) func RepoURL(urlType RepoURLType) string { + // SSH URLs use the container path (defaultTmpDir) because sshd runs inside Docker + // where $ARGOCD_E2E_DIR is mounted to /tmp/argo-e2e switch urlType { // Git server via SSH case RepoURLTypeSSH: - return GetEnvWithDefault(EnvRepoURLTypeSSH, "ssh://root@localhost:2222/tmp/argo-e2e/testdata.git") + return GetEnvWithDefault(EnvRepoURLTypeSSH, "ssh://root@localhost:2222"+defaultTmpDir+"/testdata.git") // Git submodule repo case RepoURLTypeSSHSubmodule: - return GetEnvWithDefault(EnvRepoURLTypeSSHSubmodule, "ssh://root@localhost:2222/tmp/argo-e2e/submodule.git") + return GetEnvWithDefault(EnvRepoURLTypeSSHSubmodule, "ssh://root@localhost:2222"+defaultTmpDir+"/submodule.git") // Git submodule parent repo case RepoURLTypeSSHSubmoduleParent: - return GetEnvWithDefault(EnvRepoURLTypeSSHSubmoduleParent, "ssh://root@localhost:2222/tmp/argo-e2e/submoduleParent.git") + return GetEnvWithDefault(EnvRepoURLTypeSSHSubmoduleParent, "ssh://root@localhost:2222"+defaultTmpDir+"/submoduleParent.git") // Git server via HTTPS case RepoURLTypeHTTPS: return GetEnvWithDefault(EnvRepoURLTypeHTTPS, "https://localhost:9443/argo-e2e/testdata.git") @@ -957,38 +966,39 @@ func EnsureCleanState(t *testing.T, opts ...TestOption) { return err }, func() error { - err := os.RemoveAll(TmpDir) + tmpDir := TmpDir() + err := os.RemoveAll(tmpDir) if err != nil { return err } - _, err = Run("", "mkdir", "-p", TmpDir) + _, err = Run("", "mkdir", "-p", tmpDir) if err != nil { return err } // create TLS and SSH certificate directories if IsLocal() { - _, err = Run("", "mkdir", "-p", TmpDir+"/app/config/tls") + _, err = Run("", "mkdir", "-p", tmpDir+"/app/config/tls") if err != nil { return err } - _, err = Run("", "mkdir", "-p", TmpDir+"/app/config/ssh") + _, err = Run("", "mkdir", "-p", tmpDir+"/app/config/ssh") if err != nil { return err } } // For signing during the tests - _, err = Run("", "mkdir", "-p", TmpDir+"/gpg") + _, err = Run("", "mkdir", "-p", tmpDir+"/gpg") if err != nil { return err } - _, err = Run("", "chmod", "0700", TmpDir+"/gpg") + _, err = Run("", "chmod", "0700", tmpDir+"/gpg") if err != nil { return err } prevGnuPGHome := os.Getenv("GNUPGHOME") - t.Setenv("GNUPGHOME", TmpDir+"/gpg") + t.Setenv("GNUPGHOME", tmpDir+"/gpg") //nolint:errcheck Run("", "pkill", "-9", "gpg-agent") _, err = Run("", "gpg", "--import", "../fixture/gpg/signingkey.asc") @@ -999,23 +1009,23 @@ func EnsureCleanState(t *testing.T, opts ...TestOption) { // recreate GPG directories if IsLocal() { - _, err = Run("", "mkdir", "-p", TmpDir+"/app/config/gpg/source") + _, err = Run("", "mkdir", "-p", tmpDir+"/app/config/gpg/source") if err != nil { return err } - _, err = Run("", "mkdir", "-p", TmpDir+"/app/config/gpg/keys") + _, err = Run("", "mkdir", "-p", tmpDir+"/app/config/gpg/keys") if err != nil { return err } - _, err = Run("", "chmod", "0700", TmpDir+"/app/config/gpg/keys") + _, err = Run("", "chmod", "0700", tmpDir+"/app/config/gpg/keys") if err != nil { return err } - _, err = Run("", "mkdir", "-p", TmpDir+PluginSockFilePath) + _, err = Run("", "mkdir", "-p", tmpDir+PluginSockFilePath) if err != nil { return err } - _, err = Run("", "chmod", "0700", TmpDir+PluginSockFilePath) + _, err = Run("", "chmod", "0700", tmpDir+PluginSockFilePath) if err != nil { return err } @@ -1217,7 +1227,7 @@ func AddSignedFile(t *testing.T, path, contents string) { WriteFile(t, path, contents) prevGnuPGHome := os.Getenv("GNUPGHOME") - t.Setenv("GNUPGHOME", TmpDir+"/gpg") + t.Setenv("GNUPGHOME", TmpDir()+"/gpg") errors.NewHandler(t).FailOnErr(Run(repoDirectory(), "git", "diff")) errors.NewHandler(t).FailOnErr(Run(repoDirectory(), "git", "add", ".")) errors.NewHandler(t).FailOnErr(Run(repoDirectory(), "git", "-c", "user.signingkey="+GpgGoodKeyID, "commit", "-S", "-am", "add file")) @@ -1230,7 +1240,7 @@ func AddSignedFile(t *testing.T, path, contents string) { func AddSignedTag(t *testing.T, name string) { t.Helper() prevGnuPGHome := os.Getenv("GNUPGHOME") - t.Setenv("GNUPGHOME", TmpDir+"/gpg") + t.Setenv("GNUPGHOME", TmpDir()+"/gpg") defer t.Setenv("GNUPGHOME", prevGnuPGHome) errors.NewHandler(t).FailOnErr(Run(repoDirectory(), "git", "-c", "user.signingkey="+GpgGoodKeyID, "tag", "-sm", "add signed tag", name)) if IsRemote() { @@ -1241,7 +1251,7 @@ func AddSignedTag(t *testing.T, name string) { func AddTag(t *testing.T, name string) { t.Helper() prevGnuPGHome := os.Getenv("GNUPGHOME") - t.Setenv("GNUPGHOME", TmpDir+"/gpg") + t.Setenv("GNUPGHOME", TmpDir()+"/gpg") defer t.Setenv("GNUPGHOME", prevGnuPGHome) errors.NewHandler(t).FailOnErr(Run(repoDirectory(), "git", "tag", name)) if IsRemote() { @@ -1252,7 +1262,7 @@ func AddTag(t *testing.T, name string) { func AddTagWithForce(t *testing.T, name string) { t.Helper() prevGnuPGHome := os.Getenv("GNUPGHOME") - t.Setenv("GNUPGHOME", TmpDir+"/gpg") + t.Setenv("GNUPGHOME", TmpDir()+"/gpg") defer t.Setenv("GNUPGHOME", prevGnuPGHome) errors.NewHandler(t).FailOnErr(Run(repoDirectory(), "git", "tag", "-f", name)) if IsRemote() { diff --git a/test/e2e/fixture/gpgkeys/gpgkeys.go b/test/e2e/fixture/gpgkeys/gpgkeys.go index 78e51a7e0036a..1ff3c900e3437 100644 --- a/test/e2e/fixture/gpgkeys/gpgkeys.go +++ b/test/e2e/fixture/gpgkeys/gpgkeys.go @@ -23,7 +23,7 @@ func AddGPGPublicKey(t *testing.T) { if fixture.IsLocal() { keyData, err := os.ReadFile(keyPath) require.NoError(t, err) - err = os.WriteFile(fmt.Sprintf("%s/app/config/gpg/source/%s", fixture.TmpDir, fixture.GpgGoodKeyID), keyData, 0o644) + err = os.WriteFile(fmt.Sprintf("%s/app/config/gpg/source/%s", fixture.TmpDir(), fixture.GpgGoodKeyID), keyData, 0o644) require.NoError(t, err) } else { fixture.RestartRepoServer(t) @@ -35,7 +35,7 @@ func DeleteGPGPublicKey(t *testing.T) { args := []string{"gpg", "rm", fixture.GpgGoodKeyID} errors.NewHandler(t).FailOnErr(fixture.RunCli(args...)) if fixture.IsLocal() { - require.NoError(t, os.Remove(fmt.Sprintf("%s/app/config/gpg/source/%s", fixture.TmpDir, fixture.GpgGoodKeyID))) + require.NoError(t, os.Remove(fmt.Sprintf("%s/app/config/gpg/source/%s", fixture.TmpDir(), fixture.GpgGoodKeyID))) } else { fixture.RestartRepoServer(t) } diff --git a/test/e2e/fixture/util.go b/test/e2e/fixture/util.go index d6f423f753570..7aa64fac9ea33 100644 --- a/test/e2e/fixture/util.go +++ b/test/e2e/fixture/util.go @@ -20,6 +20,7 @@ func DnsFriendly(str string, postfix string) string { //nolint:revive //FIXME(va str = matchFirstCap.ReplaceAllString(str, "${1}-${2}") str = matchAllCap.ReplaceAllString(str, "${1}-${2}") str = strings.ToLower(str) + str = strings.ReplaceAll(str, "/", "-") if diff := len(str) + len(postfix) - 63; diff > 0 { str = str[:len(str)-diff] diff --git a/test/e2e/git_test.go b/test/e2e/git_test.go index 622701e681edb..e3f3df4205b2a 100644 --- a/test/e2e/git_test.go +++ b/test/e2e/git_test.go @@ -134,7 +134,7 @@ func TestAnnotatedTagInStatusSyncRevision(t *testing.T) { Then(). Expect(SyncStatusIs(SyncStatusCodeSynced)). And(func(app *Application) { - annotatedTagIDOutput, err := fixture.Run(fixture.TmpDir+"/testdata.git", "git", "show-ref", "annotated-tag") + annotatedTagIDOutput, err := fixture.Run(fixture.TmpDir()+"/testdata.git", "git", "show-ref", "annotated-tag") require.NoError(t, err) require.NotEmpty(t, annotatedTagIDOutput) // example command output: @@ -142,7 +142,7 @@ func TestAnnotatedTagInStatusSyncRevision(t *testing.T) { annotatedTagIDFields := strings.Fields(string(annotatedTagIDOutput)) require.Len(t, annotatedTagIDFields, 2) - targetCommitID, err := fixture.Run(fixture.TmpDir+"/testdata.git", "git", "rev-parse", "--verify", "annotated-tag^{commit}") + targetCommitID, err := fixture.Run(fixture.TmpDir()+"/testdata.git", "git", "rev-parse", "--verify", "annotated-tag^{commit}") // example command output: // "bcd35965e494273355265b9f0bf85075b6bc5163" require.NoError(t, err) diff --git a/test/fixture/certs/argocd-e2e-server.csr b/test/fixture/certs/argocd-e2e-server.csr deleted file mode 100644 index a4c55da34ab32..0000000000000 --- a/test/fixture/certs/argocd-e2e-server.csr +++ /dev/null @@ -1,16 +0,0 @@ ------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/ssh_known_hosts b/test/fixture/certs/ssh_known_hosts deleted file mode 100644 index c98597b633b21..0000000000000 --- a/test/fixture/certs/ssh_known_hosts +++ /dev/null @@ -1,3 +0,0 @@ -[localhost]:2222 ecdsa-sha2-nistp256 AAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAAAIbmlzdHAyNTYAAABBBLle3IiLWy+Cwz6/JT3K8PSGAEZAJnaxiWk0u9wkAvbZ9wHTffctg25coBa8J4Oo1l5GTIkezib2C4PjCE01BZM= -[localhost]:2222 ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQDhRWyu6rg0Kd0ugLxNGZ8gzUjasF4Z0oT16RUC/L9EkJWATAu4TkkoozZ5AcejlS29jUZXTkKt0La4dmIooeMDNd8b5vg1dWzSDDHwxd8Wa/4XZsUlL6zkUFrnqOPaFc/7EwM3I30064zT/Gt0BVvQUxKoT/TTea2KhQqeLmlWh4cVWJBuhZ8YODUf2VD4TSYfvpcqW/jVw2oG8Pj3WIaaG2+Bcp4Q4sJS2K+2kkiqmZ/hiPK1X/UbMRN2zWQBp5UPWFY2ctuC9B8yhLwAyMkHzuWLfB39dNEdn1jTjDsOUWbC3kDsWHsY5gtBxN30NizBWC+83NpaWbrzAlGb0JV1 -[localhost]:2222 ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIG2t7Tcavp5oUqbbSwEKRaGwEq94b8BFK16AEBbgRCTp diff --git a/test/fixture/testrepos/Procfile b/test/fixture/testrepos/Procfile index fdec8b755e9a5..af6de55186924 100644 --- a/test/fixture/testrepos/Procfile +++ b/test/fixture/testrepos/Procfile @@ -1,5 +1,6 @@ # To prevent regression of https://github.com/argoproj/argo-cd/pull/6253, we # start sshd with -o KexAlgorithms=diffie-hellman-group-exchange-sha256 -sshd: mkdir -p /var/run/sshd && mkdir -p ~/.ssh && cat ./test/fixture/testrepos/id_rsa.pub > ~/.ssh/authorized_keys && /usr/sbin/sshd -p 2222 -D -e -o KexAlgorithms=diffie-hellman-group-exchange-sha256 +# Copy fixed SSH host keys so ssh_known_hosts stays valid across container restarts +sshd: mkdir -p /var/run/sshd && mkdir -p ~/.ssh && cat ./test/fixture/testrepos/id_rsa.pub > ~/.ssh/authorized_keys && cp ./test/fixture/testrepos/ssh_host_*_key /etc/ssh/ && cp ./test/fixture/testrepos/ssh_host_*_key.pub /etc/ssh/ && chmod 600 /etc/ssh/ssh_host_*_key && /usr/sbin/sshd -p 2222 -D -e -o KexAlgorithms=diffie-hellman-group-exchange-sha256 fcgiwrap: fcgiwrap -s unix:/var/run/fcgiwrap.socket & sleep 1 && chmod 777 /var/run/fcgiwrap.socket && wait nginx: nginx -prefix=$(pwd) -g 'daemon off;' -c $(pwd)/test/fixture/testrepos/nginx.conf diff --git a/test/fixture/testrepos/README.md b/test/fixture/testrepos/README.md new file mode 100644 index 0000000000000..abf17607ef244 --- /dev/null +++ b/test/fixture/testrepos/README.md @@ -0,0 +1,121 @@ +# Test Repository Server Files + +This directory contains configuration and cryptographic keys for running the E2E test Git server in a Docker container. The server provides Git repositories accessible via SSH, HTTP, and HTTPS. + +## Overview + +The test server runs in Docker (`argoproj/argo-cd-ci-builder:v1.0.0`) using goreman to manage three processes: sshd (port 2222), nginx (ports 9080, 9443-9445), and fcgiwrap (Git HTTP backend). + +## Files + +### `start-git.sh` + +Starts the Docker container with the test Git server. Mounts the current directory and `ARGOCD_E2E_DIR` (default: `/tmp/argo-e2e`), exposing ports for SSH and HTTP/HTTPS access. + +### `start-helm-registry.sh` / `start-authenticated-helm-registry.sh` + +Start Helm registries for testing Helm chart functionality, with and without authentication. + +### `Procfile` + +Defines processes managed by goreman: + +- **sshd**: Copies fixed SSH host keys to `/etc/ssh/` before starting on port 2222 +- **fcgiwrap**: FastCGI wrapper for Git HTTP backend +- **nginx**: Web server for HTTP/HTTPS Git access + +### SSH Keys + +#### SSH Host Keys + +- `ssh_host_rsa_key`, `ssh_host_ecdsa_key`, `ssh_host_ed25519_key` (with `.pub` files) + +By copying pre-generated keys before starting sshd, the same keys are used every time, keeping the `ssh_known_hosts` the same on restarts. + +#### `ssh_known_hosts` + +Contains SSH host key fingerprints for: + +- `[localhost]:2222` - Local development/testing +- `[argocd-e2e-server]:2222` - Remote/in-cluster testing + +Both entries have identical fingerprints since they use the same keys from this directory. + +#### `id_rsa.pub` + +Public key added to `~/.ssh/authorized_keys` in the container for SSH authentication. + +### `nginx.conf` + +Provides HTTP (9080), HTTPS (9443/9444/9445), and Helm repository access. Proxies Git operations to git-http-backend via fcgiwrap. + +### `sudoers.conf` + +Sudo configuration allowing test users to start services with elevated privileges. + +## Regenerating SSH Keys + +If you need to regenerate the SSH host keys: + +### 1. Generate New Keys + +```bash +cd test/fixture/testrepos + +# Generate RSA key +ssh-keygen -t rsa -b 2048 -f ssh_host_rsa_key -N "" -C "root@argocd-e2e" + +# Generate ECDSA key +ssh-keygen -t ecdsa -f ssh_host_ecdsa_key -N "" -C "root@argocd-e2e" + +# Generate Ed25519 key +ssh-keygen -t ed25519 -f ssh_host_ed25519_key -N "" -C "root@argocd-e2e" +``` + +### 2. Update ssh_known_hosts + +```bash +# Start temporary sshd with new keys +sudo mkdir -p /tmp/test-sshd +sudo cp ssh_host_*_key* /tmp/test-sshd/ +sudo chmod 600 /tmp/test-sshd/ssh_host_*_key + +sudo /usr/sbin/sshd -p 2222 \ + -h /tmp/test-sshd/ssh_host_rsa_key \ + -h /tmp/test-sshd/ssh_host_ecdsa_key \ + -h /tmp/test-sshd/ssh_host_ed25519_key \ + -D & +SSHD_PID=$! + +# Scan to get new fingerprints +ssh-keyscan -p 2222 localhost > ssh_known_hosts.tmp + +# Stop temporary sshd +sudo kill $SSHD_PID +sudo rm -rf /tmp/test-sshd + +# Create new ssh_known_hosts with both localhost and argocd-e2e-server entries +cat > ssh_known_hosts << 'EOF' +# localhost:2222 SSH-2.0-OpenSSH_X.Xp1 +EOF +cat ssh_known_hosts.tmp >> ssh_known_hosts +echo "# For in-cluster tests" >> ssh_known_hosts +sed 's/\[localhost\]/[argocd-e2e-server]/g' ssh_known_hosts.tmp >> ssh_known_hosts + +rm ssh_known_hosts.tmp +``` + +### 3. Verify + +```bash +# Start the test server +./test/fixture/testrepos/start-git.sh + +# In another terminal, test SSH connection +ssh -p 2222 -o UserKnownHostsFile=test/fixture/testrepos/ssh_known_hosts root@localhost +``` + +## Related Documentation + +- [TLS Certificates](../certs/README.md) - HTTPS certificates (separate from SSH keys) +- [Remote Testing](../../remote/README.md) - Running tests against remote clusters diff --git a/test/fixture/testrepos/start-git.sh b/test/fixture/testrepos/start-git.sh index d9037285107f0..37d0bf65c832b 100755 --- a/test/fixture/testrepos/start-git.sh +++ b/test/fixture/testrepos/start-git.sh @@ -1,6 +1,15 @@ #!/usr/bin/env bash +# ARGOCD_E2E_DIR can be set to customize the e2e test data directory on the host +# This is useful on macOS where Docker doesn't share /tmp with the host +# The host directory is mounted to /tmp/argo-e2e inside the container +ARGOCD_E2E_DIR="${ARGOCD_E2E_DIR:-/tmp/argo-e2e}" + docker run --name e2e-git --rm -i \ -p 2222:2222 -p 9080:9080 -p 9443:9443 -p 9444:9444 -p 9445:9445 \ - -w /go/src/github.com/argoproj/argo-cd -v "$(pwd)":/go/src/github.com/argoproj/argo-cd -v /tmp:/tmp docker.io/argoproj/argo-cd-ci-builder:v1.0.0 \ + -w /go/src/github.com/argoproj/argo-cd \ + -v /tmp:/tmp \ + -v "$ARGOCD_E2E_DIR":/tmp/argo-e2e \ + -v "$(pwd)":/go/src/github.com/argoproj/argo-cd \ + docker.io/argoproj/argo-cd-ci-builder:v1.0.0 \ bash -c "goreman -f ./test/fixture/testrepos/Procfile start"