diff --git a/.github/workflows/ci-build.yaml b/.github/workflows/ci-build.yaml index 5954d34ef7924..986eeae4917a0 100644 --- a/.github/workflows/ci-build.yaml +++ b/.github/workflows/ci-build.yaml @@ -427,14 +427,14 @@ jobs: # latest: true means that this version mush upload the coverage report to codecov.io # We designate the latest version because we only collect code coverage for that version. k3s: - - version: v1.33.1 + - version: v1.34.2 latest: true + - version: v1.33.1 + latest: false - version: v1.32.1 latest: false - version: v1.31.0 latest: false - - version: v1.30.4 - latest: false needs: - build-go - changes diff --git a/.github/workflows/renovate.yaml b/.github/workflows/renovate.yaml index a7c9379143af5..4b2d31031fc84 100644 --- a/.github/workflows/renovate.yaml +++ b/.github/workflows/renovate.yaml @@ -30,7 +30,7 @@ jobs: go-version: 1.25.5 - name: Self-hosted Renovate - uses: renovatebot/github-action@8b7941943a108b2cc2150730963164aa8baeab8c #44.2.2 + uses: renovatebot/github-action@a7e89c349a53ab0c9d8458eb85f4b415e55848e7 #44.2.3 with: configurationFile: .github/configs/renovate-config.js token: '${{ steps.get_token.outputs.token }}' diff --git a/Makefile b/Makefile index 3472f5a0f21b4..bedfd0266a5bf 100644 --- a/Makefile +++ b/Makefile @@ -429,13 +429,19 @@ test: test-tools-image # Run all unit tests (local version) .PHONY: test-local -test-local: +test-local: test-gitops-engine if test "$(TEST_MODULE)" = ""; then \ DIST_DIR=${DIST_DIR} RERUN_FAILS=0 PACKAGES=`go list ./... | grep -v 'test/e2e'` ./hack/test.sh -args -test.gocoverdir="$(PWD)/test-results"; \ else \ DIST_DIR=${DIST_DIR} RERUN_FAILS=0 PACKAGES="$(TEST_MODULE)" ./hack/test.sh -args -test.gocoverdir="$(PWD)/test-results" "$(TEST_MODULE)"; \ fi +# Run gitops-engine unit tests +.PHONY: test-gitops-engine +test-gitops-engine: + mkdir -p $(PWD)/test-results + cd gitops-engine && go test -race -cover ./... -args -test.gocoverdir="$(PWD)/test-results" + .PHONY: test-race test-race: test-tools-image mkdir -p $(GOCACHE) @@ -507,6 +513,7 @@ start-e2e-local: mod-vendor-local dep-ui-local cli-local ARGOCD_GPG_ENABLED=$(ARGOCD_GPG_ENABLED) \ ARGOCD_PLUGINCONFIGFILEPATH=/tmp/argo-e2e/app/config/plugin \ ARGOCD_PLUGINSOCKFILEPATH=/tmp/argo-e2e/app/config/plugin \ + ARGOCD_GIT_CONFIG=$(PWD)/test/e2e/fixture/gitconfig \ ARGOCD_E2E_DISABLE_AUTH=false \ ARGOCD_ZJWT_FEATURE_FLAG=always \ ARGOCD_IN_CI=$(ARGOCD_IN_CI) \ diff --git a/Procfile b/Procfile index b3f57144a1bf1..f5bddd2086546 100644 --- a/Procfile +++ b/Procfile @@ -2,7 +2,7 @@ controller: [ "$BIN_MODE" = 'true' ] && COMMAND=./dist/argocd || COMMAND='go run api-server: [ "$BIN_MODE" = 'true' ] && COMMAND=./dist/argocd || COMMAND='go run ./cmd/main.go' && sh -c "GOCOVERDIR=${ARGOCD_COVERAGE_DIR:-/tmp/coverage/api-server} FORCE_LOG_COLORS=1 ARGOCD_FAKE_IN_CLUSTER=true ARGOCD_TLS_DATA_PATH=${ARGOCD_TLS_DATA_PATH:-/tmp/argocd-local/tls} ARGOCD_SSH_DATA_PATH=${ARGOCD_SSH_DATA_PATH:-/tmp/argocd-local/ssh} ARGOCD_BINARY_NAME=argocd-server $COMMAND --loglevel debug --redis localhost:${ARGOCD_E2E_REDIS_PORT:-6379} --disable-auth=${ARGOCD_E2E_DISABLE_AUTH:-'true'} --insecure --dex-server http://localhost:${ARGOCD_E2E_DEX_PORT:-5556} --repo-server localhost:${ARGOCD_E2E_REPOSERVER_PORT:-8081} --port ${ARGOCD_E2E_APISERVER_PORT:-8080} --otlp-address=${ARGOCD_OTLP_ADDRESS} --application-namespaces=${ARGOCD_APPLICATION_NAMESPACES:-''} --hydrator-enabled=${ARGOCD_HYDRATOR_ENABLED:='false'}" dex: sh -c "ARGOCD_BINARY_NAME=argocd-dex go run github.com/argoproj/argo-cd/v3/cmd gendexcfg -o `pwd`/dist/dex.yaml && (test -f dist/dex.yaml || { echo 'Failed to generate dex configuration'; exit 1; }) && docker run --rm -p ${ARGOCD_E2E_DEX_PORT:-5556}:${ARGOCD_E2E_DEX_PORT:-5556} -v `pwd`/dist/dex.yaml:/dex.yaml ghcr.io/dexidp/dex:$(grep "image: ghcr.io/dexidp/dex" manifests/base/dex/argocd-dex-server-deployment.yaml | cut -d':' -f3) dex serve /dex.yaml" redis: hack/start-redis-with-password.sh -repo-server: [ "$BIN_MODE" = 'true' ] && COMMAND=./dist/argocd || COMMAND='go run ./cmd/main.go' && sh -c "GOCOVERDIR=${ARGOCD_COVERAGE_DIR:-/tmp/coverage/repo-server} FORCE_LOG_COLORS=1 ARGOCD_FAKE_IN_CLUSTER=true ARGOCD_GNUPGHOME=${ARGOCD_GNUPGHOME:-/tmp/argocd-local/gpg/keys} ARGOCD_PLUGINSOCKFILEPATH=${ARGOCD_PLUGINSOCKFILEPATH:-./test/cmp} ARGOCD_GPG_DATA_PATH=${ARGOCD_GPG_DATA_PATH:-/tmp/argocd-local/gpg/source} ARGOCD_TLS_DATA_PATH=${ARGOCD_TLS_DATA_PATH:-/tmp/argocd-local/tls} ARGOCD_SSH_DATA_PATH=${ARGOCD_SSH_DATA_PATH:-/tmp/argocd-local/ssh} ARGOCD_BINARY_NAME=argocd-repo-server ARGOCD_GPG_ENABLED=${ARGOCD_GPG_ENABLED:-false} $COMMAND --loglevel debug --port ${ARGOCD_E2E_REPOSERVER_PORT:-8081} --redis localhost:${ARGOCD_E2E_REDIS_PORT:-6379} --otlp-address=${ARGOCD_OTLP_ADDRESS}" +repo-server: [ "$BIN_MODE" = 'true' ] && COMMAND=./dist/argocd || COMMAND='go run ./cmd/main.go' && sh -c "export PATH=./dist:\$PATH && [ -n \"\$ARGOCD_GIT_CONFIG\" ] && export GIT_CONFIG_GLOBAL=\$ARGOCD_GIT_CONFIG && export GIT_CONFIG_NOSYSTEM=1; GOCOVERDIR=${ARGOCD_COVERAGE_DIR:-/tmp/coverage/repo-server} FORCE_LOG_COLORS=1 ARGOCD_FAKE_IN_CLUSTER=true ARGOCD_GNUPGHOME=${ARGOCD_GNUPGHOME:-/tmp/argocd-local/gpg/keys} ARGOCD_PLUGINSOCKFILEPATH=${ARGOCD_PLUGINSOCKFILEPATH:-./test/cmp} ARGOCD_GPG_DATA_PATH=${ARGOCD_GPG_DATA_PATH:-/tmp/argocd-local/gpg/source} ARGOCD_TLS_DATA_PATH=${ARGOCD_TLS_DATA_PATH:-/tmp/argocd-local/tls} ARGOCD_SSH_DATA_PATH=${ARGOCD_SSH_DATA_PATH:-/tmp/argocd-local/ssh} ARGOCD_BINARY_NAME=argocd-repo-server ARGOCD_GPG_ENABLED=${ARGOCD_GPG_ENABLED:-false} $COMMAND --loglevel debug --port ${ARGOCD_E2E_REPOSERVER_PORT:-8081} --redis localhost:${ARGOCD_E2E_REDIS_PORT:-6379} --otlp-address=${ARGOCD_OTLP_ADDRESS}" cmp-server: [ "$ARGOCD_E2E_TEST" = 'true' ] && exit 0 || [ "$BIN_MODE" = 'true' ] && COMMAND=./dist/argocd || COMMAND='go run ./cmd/main.go' && sh -c "FORCE_LOG_COLORS=1 ARGOCD_FAKE_IN_CLUSTER=true ARGOCD_BINARY_NAME=argocd-cmp-server ARGOCD_PLUGINSOCKFILEPATH=${ARGOCD_PLUGINSOCKFILEPATH:-./test/cmp} $COMMAND --config-dir-path ./test/cmp --loglevel debug --otlp-address=${ARGOCD_OTLP_ADDRESS}" commit-server: [ "$BIN_MODE" = 'true' ] && COMMAND=./dist/argocd || COMMAND='go run ./cmd/main.go' && sh -c "GOCOVERDIR=${ARGOCD_COVERAGE_DIR:-/tmp/coverage/commit-server} FORCE_LOG_COLORS=1 ARGOCD_BINARY_NAME=argocd-commit-server $COMMAND --loglevel debug --port ${ARGOCD_E2E_COMMITSERVER_PORT:-8086}" ui: sh -c 'cd ui && ${ARGOCD_E2E_YARN_CMD:-yarn} start' @@ -11,4 +11,4 @@ helm-registry: test/fixture/testrepos/start-helm-registry.sh oci-registry: test/fixture/testrepos/start-authenticated-helm-registry.sh dev-mounter: [ "$ARGOCD_E2E_TEST" != "true" ] && go run hack/dev-mounter/main.go --configmap argocd-ssh-known-hosts-cm=${ARGOCD_SSH_DATA_PATH:-/tmp/argocd-local/ssh} --configmap argocd-tls-certs-cm=${ARGOCD_TLS_DATA_PATH:-/tmp/argocd-local/tls} --configmap argocd-gpg-keys-cm=${ARGOCD_GPG_DATA_PATH:-/tmp/argocd-local/gpg/source} applicationset-controller: [ "$BIN_MODE" = 'true' ] && COMMAND=./dist/argocd || COMMAND='go run ./cmd/main.go' && sh -c "GOCOVERDIR=${ARGOCD_COVERAGE_DIR:-/tmp/coverage/applicationset-controller} FORCE_LOG_COLORS=4 ARGOCD_FAKE_IN_CLUSTER=true ARGOCD_TLS_DATA_PATH=${ARGOCD_TLS_DATA_PATH:-/tmp/argocd-local/tls} ARGOCD_SSH_DATA_PATH=${ARGOCD_SSH_DATA_PATH:-/tmp/argocd-local/ssh} ARGOCD_BINARY_NAME=argocd-applicationset-controller $COMMAND --loglevel debug --metrics-addr localhost:12345 --probe-addr localhost:12346 --argocd-repo-server localhost:${ARGOCD_E2E_REPOSERVER_PORT:-8081}" -notification: [ "$BIN_MODE" = 'true' ] && COMMAND=./dist/argocd || COMMAND='go run ./cmd/main.go' && sh -c "GOCOVERDIR=${ARGOCD_COVERAGE_DIR:-/tmp/coverage/notification} FORCE_LOG_COLORS=4 ARGOCD_FAKE_IN_CLUSTER=true ARGOCD_TLS_DATA_PATH=${ARGOCD_TLS_DATA_PATH:-/tmp/argocd-local/tls} ARGOCD_BINARY_NAME=argocd-notifications $COMMAND --loglevel debug --application-namespaces=${ARGOCD_APPLICATION_NAMESPACES:-''} --self-service-notification-enabled=${ARGOCD_NOTIFICATION_CONTROLLER_SELF_SERVICE_NOTIFICATION_ENABLED:-'false'}" \ No newline at end of file +notification: [ "$BIN_MODE" = 'true' ] && COMMAND=./dist/argocd || COMMAND='go run ./cmd/main.go' && sh -c "GOCOVERDIR=${ARGOCD_COVERAGE_DIR:-/tmp/coverage/notification} FORCE_LOG_COLORS=4 ARGOCD_FAKE_IN_CLUSTER=true ARGOCD_TLS_DATA_PATH=${ARGOCD_TLS_DATA_PATH:-/tmp/argocd-local/tls} ARGOCD_BINARY_NAME=argocd-notifications $COMMAND --loglevel debug --application-namespaces=${ARGOCD_APPLICATION_NAMESPACES:-''} --self-service-notification-enabled=${ARGOCD_NOTIFICATION_CONTROLLER_SELF_SERVICE_NOTIFICATION_ENABLED:-'false'}" diff --git a/docs/index.md b/docs/index.md index 484381c195e24..0c194682f5578 100644 --- a/docs/index.md +++ b/docs/index.md @@ -80,7 +80,7 @@ For additional details, see [architecture overview](operator-manual/architecture * CLI for automation and CI integration * Webhook integration (GitHub, BitBucket, GitLab) * Access tokens for automation -* PreSync, Sync, PostSync hooks to support complex application rollouts (e.g.blue/green & canary upgrades) +* PreSync, Sync, PostSync hooks to support complex application rollouts (e.g. blue/green & canary upgrades) * Audit trails for application events and API calls * Prometheus metrics * Parameter overrides for overriding helm parameters in Git diff --git a/docs/operator-manual/applicationset/Generators-Pull-Request.md b/docs/operator-manual/applicationset/Generators-Pull-Request.md index 34661fce55d09..6285e2f879c22 100644 --- a/docs/operator-manual/applicationset/Generators-Pull-Request.md +++ b/docs/operator-manual/applicationset/Generators-Pull-Request.md @@ -217,7 +217,7 @@ If you want to access a private repository, you must also provide the credential In case of Bitbucket App Token, go with `bearerToken` section. * `tokenRef`: A `Secret` name and key containing the app token to use for requests. -In case self-signed BitBucket Server certificates, the following options can be usefully: +In case of self-signed BitBucket Server certificates, the following options can be useful: * `insecure`: By default (false) - Skip checking the validity of the SCM's certificate - useful for self-signed TLS certificates. * `caRef`: Optional `ConfigMap` name and key containing the BitBucket server certificates to trust - useful for self-signed TLS certificates. Possibly reference the ArgoCD CM holding the trusted certs. diff --git a/docs/operator-manual/applicationset/Generators-SCM-Provider.md b/docs/operator-manual/applicationset/Generators-SCM-Provider.md index e71b91461d7e9..82c433d6e009a 100644 --- a/docs/operator-manual/applicationset/Generators-SCM-Provider.md +++ b/docs/operator-manual/applicationset/Generators-SCM-Provider.md @@ -221,7 +221,7 @@ If you want to access a private repository, you must also provide the credential In case of Bitbucket App Token, go with `bearerToken` section. * `tokenRef`: A `Secret` name and key containing the app token to use for requests. -In case self-signed BitBucket Server certificates, the following options can be usefully: +In case of self-signed BitBucket Server certificates, the following options can be useful: * `insecure`: By default (false) - Skip checking the validity of the SCM's certificate - useful for self-signed TLS certificates. * `caRef`: Optional `ConfigMap` name and key containing the BitBucket server certificates to trust - useful for self-signed TLS certificates. Possibly reference the ArgoCD CM holding the trusted certs. diff --git a/docs/operator-manual/declarative-setup.md b/docs/operator-manual/declarative-setup.md index 3f6ae51fdcf56..dca80db37a282 100644 --- a/docs/operator-manual/declarative-setup.md +++ b/docs/operator-manual/declarative-setup.md @@ -504,7 +504,7 @@ stringData: username: my-username ``` -A note on noProxy: Argo CD uses exec to interact with different tools such as helm and kustomize. Not all of these tools support the same noProxy syntax as the [httpproxy go package](https://cs.opensource.google/go/x/net/+/internal-branch.go1.21-vendor:http/httpproxy/proxy.go;l=38-50) does. In case you run in trouble with noProxy not beeing respected you might want to try using the full domain instead of a wildcard pattern or IP range to find a common syntax that all tools support. +A note on noProxy: Argo CD uses exec to interact with different tools such as helm and kustomize. Not all of these tools support the same noProxy syntax as the [httpproxy go package](https://cs.opensource.google/go/x/net/+/internal-branch.go1.21-vendor:http/httpproxy/proxy.go;l=38-50) does. In case you run in trouble with noProxy not being respected you might want to try using the full domain instead of a wildcard pattern or IP range to find a common syntax that all tools support. ## Clusters @@ -630,7 +630,7 @@ This setup requires: 3. A role created for each cluster being added to Argo CD that is assumable by the Argo CD management role 4. An [Access Entry](https://docs.aws.amazon.com/eks/latest/userguide/access-entries.html) within each EKS cluster added to Argo CD that gives the cluster's role (from point 3) RBAC permissions to perform actions within the cluster - - Or, alternatively, an entry within the `aws-auth` ConfigMap within the cluster added to Argo CD ([depreciated by EKS](https://docs.aws.amazon.com/eks/latest/userguide/auth-configmap.html)) + - Or, alternatively, an entry within the `aws-auth` ConfigMap within the cluster added to Argo CD ([deprecated by EKS](https://docs.aws.amazon.com/eks/latest/userguide/auth-configmap.html)) #### Argo CD Management Role @@ -880,7 +880,7 @@ associated EKS cluster. **AWS Auth (Deprecated)** -Instead of using Access Entries, you may need to use the depreciated `aws-auth`. +Instead of using Access Entries, you may need to use the deprecated `aws-auth`. If so, the `roleARN` of each managed cluster needs to be added to each respective cluster's `aws-auth` config map (see [Enabling IAM principal access to your cluster](https://docs.aws.amazon.com/eks/latest/userguide/add-user-role.html)), as diff --git a/docs/operator-manual/reconcile.md b/docs/operator-manual/reconcile.md index 1d825cf18155a..6c78cd0c896c1 100644 --- a/docs/operator-manual/reconcile.md +++ b/docs/operator-manual/reconcile.md @@ -126,7 +126,7 @@ data: ## Ignoring updates for untracked resources -ArgoCD will only apply `ignoreResourceUpdates` configuration to tracked resources of an application. This means dependant resources, such as a `ReplicaSet` and `Pod` created by a `Deployment`, will not ignore any updates and trigger a reconcile of the application for any changes. +ArgoCD will only apply `ignoreResourceUpdates` configuration to tracked resources of an application. This means dependent resources, such as a `ReplicaSet` and `Pod` created by a `Deployment`, will not ignore any updates and trigger a reconcile of the application for any changes. If you want to apply the `ignoreResourceUpdates` configuration to an untracked resource, you can add the `argocd.argoproj.io/ignore-resource-updates=true` annotation in the dependent resources manifest. diff --git a/docs/operator-manual/user-management/github-actions.md b/docs/operator-manual/user-management/github-actions.md index e471638f8e83d..1175f50719171 100644 --- a/docs/operator-manual/user-management/github-actions.md +++ b/docs/operator-manual/user-management/github-actions.md @@ -137,4 +137,4 @@ More info: [RBAC Configuration](../rbac.md) > [!NOTE] > Defining policies are not supported on ArgoCD v2. > To define policies, please [upgrade](../upgrading/overview.md) -> to to v3.0.0 or later. +> to v3.0.0 or later. diff --git a/docs/proposals/server-side-pagination.md b/docs/proposals/server-side-pagination.md index 32098187180d1..926334897c73f 100644 --- a/docs/proposals/server-side-pagination.md +++ b/docs/proposals/server-side-pagination.md @@ -55,7 +55,7 @@ to reduce amount of data returned by the API server and improve the UI responsiv **Pagination Cursor** It is proposed to add `offset` and `limit` fields for pagination support in Application List API. -The The Watch API is a bit more complex. Both Argo CD user interface and CLI are relying on the Watch API to display real time updates of Argo CD applications. +The Watch API is a bit more complex. Both Argo CD user interface and CLI are relying on the Watch API to display real time updates of Argo CD applications. The Watch API currently supports filtering by a project and an application name. In order to effectively implement server side pagination for the Watch API we cannot rely on the order of the applications returned by the API server. Instead of relying on the order it is proposed to rely on the application name and use it as a cursor for pagination. Both the Applications List and Watch diff --git a/docs/security_considerations.md b/docs/security_considerations.md index e51012e29a606..cbf8196d0a3e7 100644 --- a/docs/security_considerations.md +++ b/docs/security_considerations.md @@ -81,7 +81,7 @@ When you are running `v1.4.x`, you can upgrade to `v1.4.3` by simply changing th tags for `argocd-server`, `argocd-repo-server` and `argocd-controller` to `v1.4.3`. The `v1.4.3` release does not contain additional functional bug fixes. -Likewise, hen you are running `v1.5.x`, you can upgrade to `v1.5.2` by simply changing +Likewise, when you are running `v1.5.x`, you can upgrade to `v1.5.2` by simply changing the image tags for `argocd-server`, `argocd-repo-server` and `argocd-controller` to `v1.5.2`. The `v1.5.2` release does not contain additional functional bug fixes. diff --git a/docs/user-guide/sync-waves.md b/docs/user-guide/sync-waves.md index 130777a0c9804..0024f0136ef60 100644 --- a/docs/user-guide/sync-waves.md +++ b/docs/user-guide/sync-waves.md @@ -156,7 +156,7 @@ Hooks and resources are assigned to wave zero by default. The wave can be negati ### Send message to Slack when sync completes -The following example uses the Slack API to send a a Slack message when sync completes: +The following example uses the Slack API to send a Slack message when sync completes: ```yaml apiVersion: batch/v1 diff --git a/gitops-engine/agent/manifests/base/gitops-agent-deploy.yaml b/gitops-engine/agent/manifests/base/gitops-agent-deploy.yaml index 4cd077dafcfd6..15880602d807c 100644 --- a/gitops-engine/agent/manifests/base/gitops-agent-deploy.yaml +++ b/gitops-engine/agent/manifests/base/gitops-agent-deploy.yaml @@ -35,7 +35,7 @@ spec: - http://localhost:9001/api/v1/sync - --dest - repo - image: k8s.gcr.io/git-sync:v3.1.6 + image: registry.k8s.io/git-sync:v3.1.6 volumeMounts: - name: git mountPath: /tmp/git diff --git a/gitops-engine/agent/manifests/install-namespaced.yaml b/gitops-engine/agent/manifests/install-namespaced.yaml index 856366165f258..199290ff8063b 100644 --- a/gitops-engine/agent/manifests/install-namespaced.yaml +++ b/gitops-engine/agent/manifests/install-namespaced.yaml @@ -62,7 +62,7 @@ spec: env: - name: GIT_SYNC_REPO value: https://github.com/argoproj/argocd-example-apps - image: k8s.gcr.io/git-sync:v3.1.6 + image: registry.k8s.io/git-sync:v3.1.6 name: git-sync volumeMounts: - mountPath: /tmp/git diff --git a/gitops-engine/agent/manifests/install.yaml b/gitops-engine/agent/manifests/install.yaml index 5cb0e276960be..5a0dd8d1e49b3 100644 --- a/gitops-engine/agent/manifests/install.yaml +++ b/gitops-engine/agent/manifests/install.yaml @@ -66,7 +66,7 @@ spec: env: - name: GIT_SYNC_REPO value: https://github.com/argoproj/argocd-example-apps - image: k8s.gcr.io/git-sync:v3.1.6 + image: registry.k8s.io/git-sync:v3.1.6 name: git-sync volumeMounts: - mountPath: /tmp/git diff --git a/gitops-engine/pkg/sync/sync_context_test.go b/gitops-engine/pkg/sync/sync_context_test.go index 7dd1d1e132dc5..bd21cf9de813d 100644 --- a/gitops-engine/pkg/sync/sync_context_test.go +++ b/gitops-engine/pkg/sync/sync_context_test.go @@ -1385,26 +1385,42 @@ func TestSync_ExistingHooksWithFinalizer(t *testing.T) { func TestRunSyncFailHooksFailed(t *testing.T) { // Tests that other SyncFail Hooks run even if one of them fail. - syncCtx := newTestSyncCtx(nil) pod := testingutils.NewPod() successfulSyncFailHook := newHook(synccommon.HookTypeSyncFail, synccommon.HookDeletePolicyBeforeHookCreation) successfulSyncFailHook.SetName("successful-sync-fail-hook") failedSyncFailHook := newHook(synccommon.HookTypeSyncFail, synccommon.HookDeletePolicyBeforeHookCreation) failedSyncFailHook.SetName("failed-sync-fail-hook") + + // Mark successful hook as healthy so it completes + syncCtx := newTestSyncCtx(nil, + WithHealthOverride(resourceNameHealthOverride(map[string]health.HealthStatusCode{ + successfulSyncFailHook.GetName(): health.HealthStatusHealthy, + })), + ) syncCtx.resources = groupResources(ReconciliationResult{ Live: []*unstructured.Unstructured{nil}, Target: []*unstructured.Unstructured{pod}, }) syncCtx.hooks = []*unstructured.Unstructured{successfulSyncFailHook, failedSyncFailHook} - mockKubectl := &kubetest.MockKubectlCmd{ + mockKubectl := (&kubetest.MockKubectlCmd{ Commands: map[string]kubetest.KubectlOutput{ // Fail operation pod.GetName(): {Err: errors.New("")}, // Fail a single SyncFail hook failedSyncFailHook.GetName(): {Err: errors.New("")}, + // Succeed for the successful hook + successfulSyncFailHook.GetName(): {}, }, - } + }).WithGetResourceFunc(func(_ context.Context, _ *rest.Config, _ schema.GroupVersionKind, name string, _ string) (*unstructured.Unstructured, error) { + // Return a completed Pod for the successful hook so health check marks it as Succeeded + if name == successfulSyncFailHook.GetName() { + completedPod := successfulSyncFailHook.DeepCopy() + _ = unstructured.SetNestedField(completedPod.Object, "Succeeded", "status", "phase") + return completedPod, nil + } + return nil, nil + }) syncCtx.kubectl = mockKubectl mockResourceOps := kubetest.MockResourceOps{ Commands: map[string]kubetest.KubectlOutput{ @@ -1412,22 +1428,51 @@ func TestRunSyncFailHooksFailed(t *testing.T) { pod.GetName(): {Err: errors.New("")}, // Fail a single SyncFail hook failedSyncFailHook.GetName(): {Err: errors.New("")}, + // Succeed for the successful hook + successfulSyncFailHook.GetName(): {}, }, } syncCtx.resourceOps = &mockResourceOps syncCtx.Sync() + + // After first sync, the successful hook is applied (Running state). + // We need to manually mark it as completed since there's no real cluster + // to provide the live object with healthy status. + _, _, results := syncCtx.GetState() + for _, res := range results { + if res.ResourceKey.Name == successfulSyncFailHook.GetName() { + res.HookPhase = synccommon.OperationSucceeded + syncCtx.syncRes[resourceResultKey(res.ResourceKey, synccommon.SyncPhaseSyncFail)] = res + } + } + syncCtx.Sync() phase, _, resources := syncCtx.GetState() // Operation as a whole should fail assert.Equal(t, synccommon.OperationFailed, phase) + + // Find the hook results by name since order depends on Order field, not alphabetical + var failedHookResult, successfulHookResult *synccommon.ResourceSyncResult + for i := range resources { + switch resources[i].ResourceKey.Name { + case failedSyncFailHook.GetName(): + failedHookResult = &resources[i] + case successfulSyncFailHook.GetName(): + successfulHookResult = &resources[i] + } + } + // failedSyncFailHook should fail - assert.Equal(t, synccommon.OperationFailed, resources[1].HookPhase) - assert.Equal(t, synccommon.ResultCodeSyncFailed, resources[1].Status) - // successfulSyncFailHook should be synced running (it is an nginx pod) - assert.Equal(t, synccommon.OperationRunning, resources[2].HookPhase) - assert.Equal(t, synccommon.ResultCodeSynced, resources[2].Status) + require.NotNil(t, failedHookResult, "failed hook result not found") + assert.Equal(t, synccommon.OperationFailed, failedHookResult.HookPhase) + assert.Equal(t, synccommon.ResultCodeSyncFailed, failedHookResult.Status) + + // successfulSyncFailHook should succeed + require.NotNil(t, successfulHookResult, "successful hook result not found") + assert.Equal(t, synccommon.OperationSucceeded, successfulHookResult.HookPhase) + assert.Equal(t, synccommon.ResultCodeSynced, successfulHookResult.Status) } func TestRunSync_HooksNotDeletedIfPhaseNotCompleted(t *testing.T) { @@ -1663,7 +1708,7 @@ func Test_setRunningPhase(t *testing.T) { return &syncTask{targetObj: pod} } newHookTask := func(name string, hookType synccommon.HookType) *syncTask { - hook := newHook(hookType) + hook := newHook(hookType, synccommon.HookDeletePolicyBeforeHookCreation) hook.SetName(name) return &syncTask{targetObj: hook} } diff --git a/go.mod b/go.mod index 83ca8058b539e..a472217f1c9db 100644 --- a/go.mod +++ b/go.mod @@ -88,7 +88,7 @@ require ( github.com/stretchr/testify v1.11.1 github.com/valyala/fasttemplate v1.2.2 github.com/yuin/gopher-lua v1.1.1 - gitlab.com/gitlab-org/api/client-go v1.10.0 + gitlab.com/gitlab-org/api/client-go v1.11.0 go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.64.0 go.opentelemetry.io/otel v1.39.0 go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.39.0 @@ -207,7 +207,7 @@ require ( github.com/golang/glog v1.2.5 // indirect github.com/golang/groupcache v0.0.0-20241129210726-2c02b8208cf8 // indirect github.com/google/go-github/v75 v75.0.0 // indirect - github.com/google/go-querystring v1.1.0 // indirect + github.com/google/go-querystring v1.2.0 // indirect github.com/google/s2a-go v0.1.9 // indirect github.com/googleapis/enterprise-certificate-proxy v0.3.4 // indirect github.com/googleapis/gax-go/v2 v2.14.1 // indirect diff --git a/go.sum b/go.sum index 752d89d24e7a7..a541c78a4f302 100644 --- a/go.sum +++ b/go.sum @@ -457,7 +457,6 @@ github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/ github.com/google/go-cmp v0.4.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.4/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.7/go.mod h1:n+brtR0CgQNWTVd5ZUFpTBC8YFBDLK/h/bpaJ8/DtOE= @@ -473,8 +472,8 @@ github.com/google/go-github/v75 v75.0.0/go.mod h1:H3LUJEA1TCrzuUqtdAQniBNwuKiQIq github.com/google/go-jsonnet v0.21.0 h1:43Bk3K4zMRP/aAZm9Po2uSEjY6ALCkYUVIcz9HLGMvA= github.com/google/go-jsonnet v0.21.0/go.mod h1:tCGAu8cpUpEZcdGMmdOu37nh8bGgqubhI5v2iSk3KJQ= github.com/google/go-querystring v1.0.0/go.mod h1:odCYkC5MyYFN7vkCjXpyrEuKhc/BUO6wN/zVPAxq5ck= -github.com/google/go-querystring v1.1.0 h1:AnCroh3fv4ZBgVIf1Iwtovgjaw/GiKJo8M8yD/fhyJ8= -github.com/google/go-querystring v1.1.0/go.mod h1:Kcdr2DB4koayq7X8pmAG4sNG59So17icRSOU623lUBU= +github.com/google/go-querystring v1.2.0 h1:yhqkPbu2/OH+V9BfpCVPZkNmUXhb2gBxJArfhIxNtP0= +github.com/google/go-querystring v1.2.0/go.mod h1:8IFJqpSRITyJ8QhQ13bmbeMBDfmeEJZD5A0egEOmkqU= github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= github.com/google/gofuzz v1.2.0 h1:xRy4A+RhZaiKjJ1bPfwQ8sedCA+YS2YcCHW6ec7JMi0= github.com/google/gofuzz v1.2.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= @@ -925,8 +924,8 @@ github.com/xlab/treeprint v1.2.0/go.mod h1:gj5Gd3gPdKtR1ikdDK6fnFLdmIS0X30kTTuNd github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= github.com/yuin/gopher-lua v1.1.1 h1:kYKnWBjvbNP4XLT3+bPEwAXJx262OhaHDWDVOPjL46M= github.com/yuin/gopher-lua v1.1.1/go.mod h1:GBR0iDaNXjAgGg9zfCvksxSRnQx76gclCIb7kdAd1Pw= -gitlab.com/gitlab-org/api/client-go v1.10.0 h1:VlB9gXQdG6w643lH53VduUHVnCWQG5Ty86VbXnyi70A= -gitlab.com/gitlab-org/api/client-go v1.10.0/go.mod h1:U3QKvjbT1J1FrgLsA7w/XlhoBIendUqB4o3/Ht3UhEQ= +gitlab.com/gitlab-org/api/client-go v1.11.0 h1:L+qzw4kiCf3jKdKHQAwiqYKITvzBrW/tl8ampxNLlv0= +gitlab.com/gitlab-org/api/client-go v1.11.0/go.mod h1:adtVJ4zSTEJ2fP5Pb1zF4Ox1OKFg0MH43yxpb0T0248= go.mongodb.org/mongo-driver v1.17.6 h1:87JUG1wZfWsr6rIz3ZmpH90rL5tea7O3IHuSwHUpsss= go.mongodb.org/mongo-driver v1.17.6/go.mod h1:Hy04i7O2kC4RS06ZrhPRqj/u4DTYkFDAAccj+rVKqgQ= go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU= diff --git a/resource_customizations/rabbitmq.com/RabbitmqCluster/health.lua b/resource_customizations/rabbitmq.com/RabbitmqCluster/health.lua index 22916ad6f84cd..643c01ee27414 100644 --- a/resource_customizations/rabbitmq.com/RabbitmqCluster/health.lua +++ b/resource_customizations/rabbitmq.com/RabbitmqCluster/health.lua @@ -21,9 +21,12 @@ if obj.status ~= nil then end end + -- Treat transient/initial 'Unknown' condition as Progressing instead of Degraded. + -- The RabbitMQ operator sets these conditions to Unknown briefly while forming the cluster, + -- so mapping Unknown->Progressing prevents false Degraded states during normal reconciliation. if clusterAvailable.status == "Unknown" or allReplicasReady.status == "Unknown" then - hs.status = "Degraded" - hs.message = "No statefulset or endpoints found" + hs.status = "Progressing" + hs.message = "Waiting for RabbitMQ cluster readiness (conditions unknown)" return hs end diff --git a/resource_customizations/rabbitmq.com/RabbitmqCluster/health_test.yaml b/resource_customizations/rabbitmq.com/RabbitmqCluster/health_test.yaml index 7e7c44e4b57ce..02bcebd257745 100644 --- a/resource_customizations/rabbitmq.com/RabbitmqCluster/health_test.yaml +++ b/resource_customizations/rabbitmq.com/RabbitmqCluster/health_test.yaml @@ -4,11 +4,11 @@ tests: message: Unknown 'foo' parameter inputPath: testdata/degraded_badconfig.yaml - healthStatus: - status: Degraded + status: Progressing message: No statefulset or endpoints found inputPath: testdata/degraded_cluster_unknown.yaml - healthStatus: - status: Degraded + status: Progressing message: No statefulset or endpoints found inputPath: testdata/degraded_replicas_unknown.yaml - healthStatus: diff --git a/test/e2e/custom_tool_test.go b/test/e2e/custom_tool_test.go index f80dbaa328e64..0bc2322f7324d 100644 --- a/test/e2e/custom_tool_test.go +++ b/test/e2e/custom_tool_test.go @@ -1,12 +1,10 @@ package e2e import ( - "os" "path/filepath" "sort" "strings" "testing" - "time" "github.com/argoproj/gitops-engine/pkg/health" . "github.com/argoproj/gitops-engine/pkg/sync/common" @@ -23,11 +21,7 @@ import ( func TestCustomToolWithGitCreds(t *testing.T) { ctx := Given(t) ctx. - And(func() { - go startCMPServer(t, "./testdata/cmp-gitcreds") - time.Sleep(100 * time.Millisecond) - t.Setenv("ARGOCD_BINARY_NAME", "argocd") - }). + RunningCMPServer("./testdata/cmp-gitcreds"). CustomCACertAdded(). // add the private repo with credentials HTTPSRepoURLAdded(true). @@ -51,11 +45,7 @@ func TestCustomToolWithGitCreds(t *testing.T) { func TestCustomToolWithGitCredsTemplate(t *testing.T) { ctx := Given(t) ctx. - And(func() { - go startCMPServer(t, "./testdata/cmp-gitcredstemplate") - time.Sleep(100 * time.Millisecond) - t.Setenv("ARGOCD_BINARY_NAME", "argocd") - }). + RunningCMPServer("./testdata/cmp-gitcredstemplate"). CustomCACertAdded(). // add the git creds template HTTPSCredentialsUserPassAdded(). @@ -92,11 +82,7 @@ func TestCustomToolWithSSHGitCreds(t *testing.T) { ctx := Given(t) // path does not matter, we ignore it ctx. - And(func() { - go startCMPServer(t, "./testdata/cmp-gitsshcreds") - time.Sleep(100 * time.Millisecond) - t.Setenv("ARGOCD_BINARY_NAME", "argocd") - }). + RunningCMPServer("./testdata/cmp-gitsshcreds"). // add the private repo with ssh credentials CustomSSHKnownHostsAdded(). SSHRepoURLAdded(true). @@ -126,11 +112,7 @@ func TestCustomToolWithSSHGitCredsDisabled(t *testing.T) { ctx := Given(t) // path does not matter, we ignore it ctx. - And(func() { - go startCMPServer(t, "./testdata/cmp-gitsshcreds-disable-provide") - time.Sleep(100 * time.Millisecond) - t.Setenv("ARGOCD_BINARY_NAME", "argocd") - }). + RunningCMPServer("./testdata/cmp-gitsshcreds-disable-provide"). CustomCACertAdded(). // add the private repo with ssh credentials CustomSSHKnownHostsAdded(). @@ -150,11 +132,7 @@ func TestCustomToolWithSSHGitCredsDisabled(t *testing.T) { func TestCustomToolWithEnv(t *testing.T) { ctx := Given(t) ctx. - And(func() { - go startCMPServer(t, "./testdata/cmp-fileName") - time.Sleep(100 * time.Millisecond) - t.Setenv("ARGOCD_BINARY_NAME", "argocd") - }). + RunningCMPServer("./testdata/cmp-fileName"). // does not matter what the path is Path("cmp-fileName"). When(). @@ -211,11 +189,7 @@ func TestCustomToolSyncAndDiffLocal(t *testing.T) { ctx := Given(t) appPath := filepath.Join(testdataPath, "guestbook") ctx. - And(func() { - go startCMPServer(t, "./testdata/cmp-kustomize") - time.Sleep(100 * time.Millisecond) - t.Setenv("ARGOCD_BINARY_NAME", "argocd") - }). + RunningCMPServer("./testdata/cmp-kustomize"). // does not matter what the path is Path("guestbook"). When(). @@ -233,29 +207,11 @@ func TestCustomToolSyncAndDiffLocal(t *testing.T) { }) } -func startCMPServer(t *testing.T, configFile string) { - t.Helper() - pluginSockFilePath := 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) - if _, err := os.Stat(pluginSockFilePath); os.IsNotExist(err) { - // path/to/whatever does not exist - err := os.Mkdir(pluginSockFilePath, 0o700) - require.NoError(t, err) - } - errors.NewHandler(t).FailOnErr(fixture.RunWithStdin("", "", "../../dist/argocd", "--config-dir-path", configFile)) -} - // Discover by fileName func TestCMPDiscoverWithFileName(t *testing.T) { pluginName := "cmp-fileName" Given(t). - And(func() { - go startCMPServer(t, "./testdata/cmp-fileName") - time.Sleep(100 * time.Millisecond) - t.Setenv("ARGOCD_BINARY_NAME", "argocd") - }). + RunningCMPServer("./testdata/cmp-fileName"). Path(pluginName + "/subdir"). When(). CreateApp(). @@ -269,11 +225,7 @@ func TestCMPDiscoverWithFileName(t *testing.T) { // Discover by Find glob func TestCMPDiscoverWithFindGlob(t *testing.T) { Given(t). - And(func() { - go startCMPServer(t, "./testdata/cmp-find-glob") - time.Sleep(100 * time.Millisecond) - t.Setenv("ARGOCD_BINARY_NAME", "argocd") - }). + RunningCMPServer("./testdata/cmp-find-glob"). Path("guestbook"). When(). CreateApp(). @@ -287,11 +239,7 @@ func TestCMPDiscoverWithFindGlob(t *testing.T) { // Discover by Plugin Name func TestCMPDiscoverWithPluginName(t *testing.T) { Given(t). - And(func() { - go startCMPServer(t, "./testdata/cmp-find-glob") - time.Sleep(100 * time.Millisecond) - t.Setenv("ARGOCD_BINARY_NAME", "argocd") - }). + RunningCMPServer("./testdata/cmp-find-glob"). Path("guestbook"). When(). CreateFromFile(func(app *Application) { @@ -310,11 +258,7 @@ func TestCMPDiscoverWithFindCommandWithEnv(t *testing.T) { pluginName := "cmp-find-command" ctx := Given(t) ctx. - And(func() { - go startCMPServer(t, "./testdata/cmp-find-command") - time.Sleep(100 * time.Millisecond) - t.Setenv("ARGOCD_BINARY_NAME", "argocd") - }). + RunningCMPServer("./testdata/cmp-find-command"). Path(pluginName). When(). CreateApp(). @@ -349,12 +293,9 @@ func TestCMPDiscoverWithFindCommandWithEnv(t *testing.T) { } func TestPruneResourceFromCMP(t *testing.T) { - Given(t). - And(func() { - go startCMPServer(t, "./testdata/cmp-find-glob") - time.Sleep(100 * time.Millisecond) - t.Setenv("ARGOCD_BINARY_NAME", "argocd") - }). + ctx := Given(t) + ctx. + RunningCMPServer("./testdata/cmp-find-glob"). Path("guestbook"). When(). CreateApp(). @@ -373,11 +314,7 @@ func TestPruneResourceFromCMP(t *testing.T) { func TestPreserveFileModeForCMP(t *testing.T) { Given(t). - And(func() { - go startCMPServer(t, "./testdata/cmp-preserve-file-mode") - time.Sleep(100 * time.Millisecond) - t.Setenv("ARGOCD_BINARY_NAME", "argocd") - }). + RunningCMPServer("./testdata/cmp-preserve-file-mode"). Path("cmp-preserve-file-mode"). When(). CreateFromFile(func(app *Application) { @@ -393,11 +330,7 @@ func TestPreserveFileModeForCMP(t *testing.T) { func TestCMPWithSymlinkPartialFiles(t *testing.T) { Given(t, fixture.WithTestData("testdata2")). - And(func() { - go startCMPServer(t, "./testdata2/cmp-symlink") - time.Sleep(100 * time.Millisecond) - t.Setenv("ARGOCD_BINARY_NAME", "argocd") - }). + RunningCMPServer("./testdata2/cmp-symlink"). Path("guestbook-partial-symlink-files"). When(). CreateApp(). @@ -410,11 +343,7 @@ func TestCMPWithSymlinkPartialFiles(t *testing.T) { func TestCMPWithSymlinkFiles(t *testing.T) { Given(t, fixture.WithTestData("testdata2")). - And(func() { - go startCMPServer(t, "./testdata2/cmp-symlink") - time.Sleep(100 * time.Millisecond) - t.Setenv("ARGOCD_BINARY_NAME", "argocd") - }). + RunningCMPServer("./testdata2/cmp-symlink"). Path("guestbook-symlink-files"). When(). CreateApp(). @@ -427,11 +356,7 @@ func TestCMPWithSymlinkFiles(t *testing.T) { func TestCMPWithSymlinkFolder(t *testing.T) { Given(t, fixture.WithTestData("testdata2")). - And(func() { - go startCMPServer(t, "./testdata2/cmp-symlink") - time.Sleep(100 * time.Millisecond) - t.Setenv("ARGOCD_BINARY_NAME", "argocd") - }). + RunningCMPServer("./testdata2/cmp-symlink"). Path("guestbook-symlink-folder"). When(). CreateApp(). diff --git a/test/e2e/fixture/app/context_cmp.go b/test/e2e/fixture/app/context_cmp.go new file mode 100644 index 0000000000000..990309563fbf7 --- /dev/null +++ b/test/e2e/fixture/app/context_cmp.go @@ -0,0 +1,87 @@ +package app + +import ( + "os" + "path" + "testing" + "time" + + "github.com/stretchr/testify/require" + + "github.com/argoproj/argo-cd/v3/cmpserver/plugin" + "github.com/argoproj/argo-cd/v3/test/e2e/fixture" + "github.com/argoproj/argo-cd/v3/util/errors" +) + +// RunningCMPServer starts a CMP server with the given config directory and waits for it to be ready. +// It blocks until the CMP socket is created or times out after 10 seconds. +func (c *Context) RunningCMPServer(configFile string) *Context { + c.t.Helper() + startCMPServer(c.t, configFile) + c.t.Setenv("ARGOCD_BINARY_NAME", "argocd") + return c +} + +// startCMPServer starts the CMP server and waits for its socket to be ready. +// 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) + 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) + if _, err := os.Stat(pluginSockFilePath); os.IsNotExist(err) { + err := os.Mkdir(pluginSockFilePath, 0o700) + require.NoError(t, err) + } + + // Read plugin config to get expected socket path + cfg, err := plugin.ReadPluginConfig(configDir) + require.NoError(t, err, "failed to read plugin config from %s", configDir) + expectedSocket := cfg.Address() + + // Remove stale socket if it exists from a previous test run + if err := os.Remove(expectedSocket); err != nil && !os.IsNotExist(err) { + require.NoError(t, err, "failed to remove stale socket") + } + + // Start CMP server in goroutine (non-blocking) + go func() { + errors.NewHandler(t).FailOnErr(fixture.RunWithStdin("", "", "../../dist/argocd", "--config-dir-path", configDir)) + }() + + // Wait for socket to be created + waitForSocket(t, expectedSocket, 10*time.Second) +} + +// waitForSocket polls for a socket file to exist with exponential backoff +func waitForSocket(t *testing.T, socketPath string, timeout time.Duration) { + t.Helper() + deadline := time.Now().Add(timeout) + + sleepIntervals := []time.Duration{ + 10 * time.Millisecond, + 20 * time.Millisecond, + 50 * time.Millisecond, + 100 * time.Millisecond, + 200 * time.Millisecond, + 500 * time.Millisecond, + } + sleepIdx := 0 + + for time.Now().Before(deadline) { + if info, err := os.Stat(socketPath); err == nil { + if info.Mode()&os.ModeSocket != 0 { + return // Socket exists and is a socket! + } + } + if sleepIdx < len(sleepIntervals) { + time.Sleep(sleepIntervals[sleepIdx]) + sleepIdx++ + } else { + time.Sleep(500 * time.Millisecond) + } + } + + t.Fatalf("CMP socket %s did not appear within %v", socketPath, timeout) +} diff --git a/test/e2e/fixture/gitconfig b/test/e2e/fixture/gitconfig new file mode 100644 index 0000000000000..56e8f992eb616 --- /dev/null +++ b/test/e2e/fixture/gitconfig @@ -0,0 +1,8 @@ +# Git configuration for e2e tests +# This file ensures reproducible test behavior by disabling system credential helpers + +[credential] + helper = + +[core] + askPass = diff --git a/test/e2e/hydrator_test.go b/test/e2e/hydrator_test.go index bc2111e92b96a..4dbe196b6995c 100644 --- a/test/e2e/hydrator_test.go +++ b/test/e2e/hydrator_test.go @@ -2,7 +2,6 @@ package e2e import ( "testing" - "time" "github.com/stretchr/testify/require" @@ -255,13 +254,9 @@ func TestHydratorWithDirectory(t *testing.T) { } func TestHydratorWithPlugin(t *testing.T) { - Given(t). - Path("hydrator-plugin"). - And(func() { - go startCMPServer(t, "./testdata/hydrator-plugin") - time.Sleep(100 * time.Millisecond) - t.Setenv("ARGOCD_BINARY_NAME", "argocd") - }). + ctx := Given(t) + ctx.Path("hydrator-plugin"). + RunningCMPServer("./testdata/hydrator-plugin"). When(). CreateFromFile(func(app *Application) { app.Spec.Source = nil diff --git a/test/remote/Dockerfile b/test/remote/Dockerfile index 9ae9ce9530ab9..977fa57b8bd2a 100644 --- a/test/remote/Dockerfile +++ b/test/remote/Dockerfile @@ -1,6 +1,6 @@ ARG BASE_IMAGE=docker.io/library/ubuntu:25.10@sha256:5922638447b1e3ba114332c896a2c7288c876bb94adec923d70d58a17d2fec5e -FROM docker.io/library/golang:1.25.5@sha256:31c1e53dfc1cc2d269deec9c83f58729fa3c53dc9a576f6426109d1e319e9e9a AS go +FROM docker.io/library/golang:1.25.5@sha256:6cc2338c038bc20f96ab32848da2b5c0641bb9bb5363f2c33e9b7c8838f9a208 AS go RUN go install github.com/mattn/goreman@latest && \ go install github.com/kisielk/godepgraph@latest diff --git a/ui/src/app/settings/components/certs-list/certs-list.tsx b/ui/src/app/settings/components/certs-list/certs-list.tsx index 223bac2e8ee8a..611353ced3fd4 100644 --- a/ui/src/app/settings/components/certs-list/certs-list.tsx +++ b/ui/src/app/settings/components/certs-list/certs-list.tsx @@ -187,7 +187,7 @@ export const CertsList = ({match, location}: RouteComponentProps) => { )) || (

No certificates configured

-
You can add further certificates below..
+
You can add further certificates below.
{' '} diff --git a/ui/src/app/settings/components/gpgkeys-list/gpgkeys-list.tsx b/ui/src/app/settings/components/gpgkeys-list/gpgkeys-list.tsx index aabdb77fcb872..f3795e5ddf8f9 100644 --- a/ui/src/app/settings/components/gpgkeys-list/gpgkeys-list.tsx +++ b/ui/src/app/settings/components/gpgkeys-list/gpgkeys-list.tsx @@ -139,7 +139,7 @@ export const GpgKeysList = ({match, location}: RouteComponentProps) => { )) || (

No GnuPG public keys currently configured

-
You can add GnuPG public keys below..
+
You can add GnuPG public keys below.