diff --git a/cmd/argocd/commands/headless/headless.go b/cmd/argocd/commands/headless/headless.go index f9a744423b076..fd34a2bab9aa7 100644 --- a/cmd/argocd/commands/headless/headless.go +++ b/cmd/argocd/commands/headless/headless.go @@ -55,6 +55,14 @@ type forwardCacheClient struct { func (c *forwardCacheClient) doLazy(action func(client cache.CacheClient) error) error { c.init.Do(func() { + // Check for external Redis server address to bypass in-cluster pod discovery and port-forwarding. + externalRedis := os.Getenv("ARGOCD_REDIS_SERVER") + if externalRedis != "" { + redisClient := redis.NewClient(&redis.Options{Addr: externalRedis, Password: c.redisPassword}) + c.client = cache.NewRedisCache(redisClient, time.Hour, c.compression) + return + } + overrides := clientcmd.ConfigOverrides{ CurrentContext: c.context, } diff --git a/cmd/argocd/commands/headless/headless_test.go b/cmd/argocd/commands/headless/headless_test.go new file mode 100644 index 0000000000000..aa6af84feb641 --- /dev/null +++ b/cmd/argocd/commands/headless/headless_test.go @@ -0,0 +1,82 @@ +package headless + +import ( + "testing" + + "github.com/alicebob/miniredis/v2" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + + "github.com/argoproj/argo-cd/v3/util/cache" +) + +func TestDoLazy_ExternalRedisConnection(t *testing.T) { + mr, err := miniredis.Run() + require.NoError(t, err) + defer mr.Close() + + // Set ARGOCD_REDIS_SERVER to simulate external Redis (miniredis) + t.Setenv("ARGOCD_REDIS_SERVER", mr.Addr()) + + client := &forwardCacheClient{ + compression: cache.RedisCompressionNone, + } + + err = client.doLazy(func(c cache.CacheClient) error { + assert.NotNil(t, c) + return nil + }) + + require.NoError(t, err) + assert.NotNil(t, client.client) +} + +func TestDoLazy_FallbackPath(t *testing.T) { + // Just in case + t.Setenv("ARGOCD_REDIS_SERVER", "") + + client := &forwardCacheClient{ + context: "invalid-context", + } + + err := client.doLazy(func(_ cache.CacheClient) error { + return nil + }) + + // Verify failure in finding Kubernetes context, confirming the fallback logic attempted to use in cluster Redis discovery. + require.Error(t, err) +} + +func TestDoLazy_CacheOperations(t *testing.T) { + mr, err := miniredis.Run() + require.NoError(t, err) + defer mr.Close() + + t.Setenv("ARGOCD_REDIS_SERVER", mr.Addr()) + + client := &forwardCacheClient{} + + err = client.doLazy(func(cacheClient cache.CacheClient) error { + testKey := "argocd-test" + testVal := "hello-argo" + + // Verify basic cache operations with the external Redis client + err := cacheClient.Set(&cache.Item{Key: testKey, Object: testVal}) + require.NoError(t, err) + + var result string + err = cacheClient.Get(testKey, &result) + require.NoError(t, err) + assert.Equal(t, testVal, result) + + err = cacheClient.Delete(testKey) + require.NoError(t, err) + + err = cacheClient.Get(testKey, &result) + require.Error(t, err) + + return nil + }) + + require.NoError(t, err) +} diff --git a/docs/user-guide/environment-variables.md b/docs/user-guide/environment-variables.md index ee3f40006fa19..ddd5b5756433b 100644 --- a/docs/user-guide/environment-variables.md +++ b/docs/user-guide/environment-variables.md @@ -12,5 +12,6 @@ The following environment variables can be used with `argocd` CLI: | `ARGOCD_REPO_SERVER_NAME` | the Argo CD Repository Server name (default "argocd-repo-server") | | `ARGOCD_APPLICATION_CONTROLLER_NAME` | the Argo CD Application Controller name (default "argocd-application-controller") | | `ARGOCD_REDIS_NAME` | the Argo CD Redis name (default "argocd-redis") | +| `ARGOCD_REDIS_SERVER` | the address of an external Redis server to use in --core mode. If set, the CLI bypasses in-cluster pod discovery and port-forwarding.
eg. `ARGOCD_REDIS_SERVER=localhost:6379` | | `ARGOCD_REDIS_HAPROXY_NAME` | the Argo CD Redis HA Proxy name (default "argocd-redis-ha-haproxy") | | `ARGOCD_GRPC_KEEP_ALIVE_MIN` | defines the GRPCKeepAliveEnforcementMinimum, used in the grpc.KeepaliveEnforcementPolicy. Expects a "Duration" format (default `10s`). |