package cache import ( "errors" "net" "net/url" "testing" apierr "k8s.io/apimachinery/pkg/api/errors" "k8s.io/apimachinery/pkg/runtime/schema" "github.com/stretchr/testify/assert" "github.com/argoproj/gitops-engine/pkg/cache" "github.com/argoproj/gitops-engine/pkg/cache/mocks" "github.com/stretchr/testify/mock" appv1 "github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1" ) type netError string func (n netError) Error() string { return string(n) } func (n netError) Timeout() bool { return false } func (n netError) Temporary() bool { return false } func TestHandleModEvent_HasChanges(t *testing.T) { clusterCache := &mocks.ClusterCache{} clusterCache.On("Invalidate", mock.Anything, mock.Anything).Return(nil).Once() clusterCache.On("EnsureSynced").Return(nil).Once() clustersCache := liveStateCache{ clusters: map[string]cache.ClusterCache{ "https://mycluster": clusterCache, }, } clustersCache.handleModEvent(&appv1.Cluster{ Server: "https://mycluster", Config: appv1.ClusterConfig{Username: "foo"}, }, &appv1.Cluster{ Server: "https://mycluster", Config: appv1.ClusterConfig{Username: "bar"}, Namespaces: []string{"default"}, }) } func TestHandleModEvent_ClusterExcluded(t *testing.T) { clusterCache := &mocks.ClusterCache{} clusterCache.On("Invalidate", mock.Anything, mock.Anything).Return(nil).Once() clusterCache.On("EnsureSynced").Return(nil).Once() clustersCache := liveStateCache{ clusters: map[string]cache.ClusterCache{ "https://mycluster": clusterCache, }, clusterFilter: func(cluster *appv1.Cluster) bool { return false }, } clustersCache.handleModEvent(&appv1.Cluster{ Server: "https://mycluster", Config: appv1.ClusterConfig{Username: "foo"}, }, &appv1.Cluster{ Server: "https://mycluster", Config: appv1.ClusterConfig{Username: "bar"}, Namespaces: []string{"default"}, }) assert.Len(t, clustersCache.clusters, 0) } func TestHandleModEvent_NoChanges(t *testing.T) { clusterCache := &mocks.ClusterCache{} clusterCache.On("Invalidate", mock.Anything).Panic("should not invalidate") clusterCache.On("EnsureSynced").Return(nil).Panic("should not re-sync") clustersCache := liveStateCache{ clusters: map[string]cache.ClusterCache{ "https://mycluster": clusterCache, }, } clustersCache.handleModEvent(&appv1.Cluster{ Server: "https://mycluster", Config: appv1.ClusterConfig{Username: "bar"}, }, &appv1.Cluster{ Server: "https://mycluster", Config: appv1.ClusterConfig{Username: "bar"}, }) } func TestHandleAddEvent_ClusterExcluded(t *testing.T) { clustersCache := liveStateCache{ clusters: map[string]cache.ClusterCache{}, clusterFilter: func(cluster *appv1.Cluster) bool { return false }, } clustersCache.handleAddEvent(&appv1.Cluster{ Server: "https://mycluster", Config: appv1.ClusterConfig{Username: "bar"}, }) assert.Len(t, clustersCache.clusters, 0) } func TestIsRetryableError(t *testing.T) { var ( tlsHandshakeTimeoutErr net.Error = netError("net/http: TLS handshake timeout") ioTimeoutErr net.Error = netError("i/o timeout") connectionTimedout net.Error = netError("connection timed out") connectionReset net.Error = netError("connection reset by peer") ) t.Run("Nil", func(t *testing.T) { assert.False(t, isRetryableError(nil)) }) t.Run("ResourceQuotaConflictErr", func(t *testing.T) { assert.False(t, isRetryableError(apierr.NewConflict(schema.GroupResource{}, "", nil))) assert.True(t, isRetryableError(apierr.NewConflict(schema.GroupResource{Group: "v1", Resource: "resourcequotas"}, "", nil))) }) t.Run("ExceededQuotaErr", func(t *testing.T) { assert.False(t, isRetryableError(apierr.NewForbidden(schema.GroupResource{}, "", nil))) assert.True(t, isRetryableError(apierr.NewForbidden(schema.GroupResource{Group: "v1", Resource: "pods"}, "", errors.New("exceeded quota")))) }) t.Run("TooManyRequestsDNS", func(t *testing.T) { assert.True(t, isRetryableError(apierr.NewTooManyRequests("", 0))) }) t.Run("DNSError", func(t *testing.T) { assert.True(t, isRetryableError(&net.DNSError{})) }) t.Run("OpError", func(t *testing.T) { assert.True(t, isRetryableError(&net.OpError{})) }) t.Run("UnknownNetworkError", func(t *testing.T) { assert.True(t, isRetryableError(net.UnknownNetworkError(""))) }) t.Run("ConnectionClosedErr", func(t *testing.T) { assert.False(t, isRetryableError(&url.Error{Err: errors.New("")})) assert.True(t, isRetryableError(&url.Error{Err: errors.New("Connection closed by foreign host")})) }) t.Run("TLSHandshakeTimeout", func(t *testing.T) { assert.True(t, isRetryableError(tlsHandshakeTimeoutErr)) }) t.Run("IOHandshakeTimeout", func(t *testing.T) { assert.True(t, isRetryableError(ioTimeoutErr)) }) t.Run("ConnectionTimeout", func(t *testing.T) { assert.True(t, isRetryableError(connectionTimedout)) }) t.Run("ConnectionReset", func(t *testing.T) { assert.True(t, isRetryableError(connectionReset)) }) }