mirror of
https://github.com/argoproj/argo-cd
synced 2026-04-21 17:07:16 +00:00
* feat: ignore watched resource update Signed-off-by: Alexandre Gaudreault <alexandre.gaudreault@logmein.com> * add doc and CLI Signed-off-by: Alexandre Gaudreault <alexandre.gaudreault@logmein.com> * update doc index Signed-off-by: Alexandre Gaudreault <alexandre.gaudreault@logmein.com> * add command Signed-off-by: Alexandre Gaudreault <alexandre.gaudreault@logmein.com> * codegen Signed-off-by: Alexandre Gaudreault <alexandre.gaudreault@logmein.com> * revert formatting Signed-off-by: Alexandre Gaudreault <alexandre.gaudreault@logmein.com> * do not skip on health change Signed-off-by: Alexandre Gaudreault <alexandre.gaudreault@logmein.com> * update doc Signed-off-by: Alexandre Gaudreault <alexandre.gaudreault@logmein.com> * update logging to use context Signed-off-by: Alexandre Gaudreault <alexandre.gaudreault@logmein.com> * fix typos. local build broken... Signed-off-by: Alexandre Gaudreault <alexandre.gaudreault@logmein.com> * change after review Signed-off-by: Alexandre Gaudreault <alexandre.gaudreault@logmein.com> * manifestHash to string Signed-off-by: Alexandre Gaudreault <alexandre.gaudreault@logmein.com> * more doc Signed-off-by: Alexandre Gaudreault <alexandre.gaudreault@logmein.com> * example for argoproj Application Signed-off-by: Alexandre Gaudreault <alexandre.gaudreault@logmein.com> * add unit test for ignored logs Signed-off-by: Alexandre Gaudreault <alexandre.gaudreault@logmein.com> * codegen Signed-off-by: Alexandre Gaudreault <alexandre.gaudreault@logmein.com> * Update docs/operator-manual/reconcile.md Co-authored-by: Michael Crenshaw <350466+crenshaw-dev@users.noreply.github.com> Signed-off-by: Alexandre Gaudreault <alexandre.gaudreault@logmein.com> * move hash and set log to debug Signed-off-by: Alexandre Gaudreault <alexandre.gaudreault@logmein.com> * Update util/settings/settings.go Co-authored-by: Michael Crenshaw <350466+crenshaw-dev@users.noreply.github.com> Signed-off-by: Alexandre Gaudreault <alexandre.gaudreault@logmein.com> * Update util/settings/settings.go Co-authored-by: Michael Crenshaw <350466+crenshaw-dev@users.noreply.github.com> Signed-off-by: Alexandre Gaudreault <alexandre.gaudreault@logmein.com> * feature flag Signed-off-by: Michael Crenshaw <350466+crenshaw-dev@users.noreply.github.com> * fix Signed-off-by: Michael Crenshaw <350466+crenshaw-dev@users.noreply.github.com> * less aggressive managedFields ignore rule Signed-off-by: Michael Crenshaw <350466+crenshaw-dev@users.noreply.github.com> * Update docs/operator-manual/reconcile.md Co-authored-by: Alexandre Gaudreault <alexandre.gaudreault@logmein.com> * use local settings Signed-off-by: Michael Crenshaw <350466+crenshaw-dev@users.noreply.github.com> * latest settings Signed-off-by: Michael Crenshaw <350466+crenshaw-dev@users.noreply.github.com> * safety first Signed-off-by: Michael Crenshaw <350466+crenshaw-dev@users.noreply.github.com> * since it's behind a feature flag, go aggressive on overrides Signed-off-by: Michael Crenshaw <350466+crenshaw-dev@users.noreply.github.com> --------- Signed-off-by: Alexandre Gaudreault <alexandre.gaudreault@logmein.com> Signed-off-by: Michael Crenshaw <350466+crenshaw-dev@users.noreply.github.com> Co-authored-by: Michael Crenshaw <350466+crenshaw-dev@users.noreply.github.com>
328 lines
9.1 KiB
Go
328 lines
9.1 KiB
Go
package cache
|
|
|
|
import (
|
|
"errors"
|
|
"net"
|
|
"net/url"
|
|
"testing"
|
|
|
|
"github.com/stretchr/testify/assert"
|
|
"k8s.io/api/core/v1"
|
|
apierr "k8s.io/apimachinery/pkg/api/errors"
|
|
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
|
"k8s.io/apimachinery/pkg/runtime/schema"
|
|
|
|
"github.com/argoproj/gitops-engine/pkg/cache"
|
|
"github.com/argoproj/gitops-engine/pkg/cache/mocks"
|
|
"github.com/argoproj/gitops-engine/pkg/health"
|
|
"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))
|
|
})
|
|
}
|
|
|
|
func Test_asResourceNode_owner_refs(t *testing.T) {
|
|
resNode := asResourceNode(&cache.Resource{
|
|
ResourceVersion: "",
|
|
Ref: v1.ObjectReference{
|
|
APIVersion: "v1",
|
|
},
|
|
OwnerRefs: []metav1.OwnerReference{
|
|
{
|
|
APIVersion: "v1",
|
|
Kind: "ConfigMap",
|
|
Name: "cm-1",
|
|
},
|
|
{
|
|
APIVersion: "v1",
|
|
Kind: "ConfigMap",
|
|
Name: "cm-2",
|
|
},
|
|
},
|
|
CreationTimestamp: nil,
|
|
Info: nil,
|
|
Resource: nil,
|
|
})
|
|
expected := appv1.ResourceNode{
|
|
ResourceRef: appv1.ResourceRef{
|
|
Version: "v1",
|
|
},
|
|
ParentRefs: []appv1.ResourceRef{
|
|
{
|
|
Group: "",
|
|
Kind: "ConfigMap",
|
|
Name: "cm-1",
|
|
},
|
|
{
|
|
Group: "",
|
|
Kind: "ConfigMap",
|
|
Name: "cm-2",
|
|
},
|
|
},
|
|
Info: nil,
|
|
NetworkingInfo: nil,
|
|
ResourceVersion: "",
|
|
Images: nil,
|
|
Health: nil,
|
|
CreatedAt: nil,
|
|
}
|
|
assert.Equal(t, expected, resNode)
|
|
}
|
|
|
|
func TestSkipResourceUpdate(t *testing.T) {
|
|
var (
|
|
hash1_x string = "x"
|
|
hash2_y string = "y"
|
|
hash3_x string = "x"
|
|
)
|
|
info := &ResourceInfo{
|
|
manifestHash: hash1_x,
|
|
Health: &health.HealthStatus{
|
|
Status: health.HealthStatusHealthy,
|
|
Message: "default",
|
|
},
|
|
}
|
|
t.Run("Nil", func(t *testing.T) {
|
|
assert.False(t, skipResourceUpdate(nil, nil))
|
|
})
|
|
t.Run("From Nil", func(t *testing.T) {
|
|
assert.False(t, skipResourceUpdate(nil, info))
|
|
})
|
|
t.Run("To Nil", func(t *testing.T) {
|
|
assert.False(t, skipResourceUpdate(info, nil))
|
|
})
|
|
t.Run("No hash", func(t *testing.T) {
|
|
assert.False(t, skipResourceUpdate(&ResourceInfo{}, &ResourceInfo{}))
|
|
})
|
|
t.Run("Same hash", func(t *testing.T) {
|
|
assert.True(t, skipResourceUpdate(&ResourceInfo{
|
|
manifestHash: hash1_x,
|
|
}, &ResourceInfo{
|
|
manifestHash: hash1_x,
|
|
}))
|
|
})
|
|
t.Run("Same hash value", func(t *testing.T) {
|
|
assert.True(t, skipResourceUpdate(&ResourceInfo{
|
|
manifestHash: hash1_x,
|
|
}, &ResourceInfo{
|
|
manifestHash: hash3_x,
|
|
}))
|
|
})
|
|
t.Run("Different hash value", func(t *testing.T) {
|
|
assert.False(t, skipResourceUpdate(&ResourceInfo{
|
|
manifestHash: hash1_x,
|
|
}, &ResourceInfo{
|
|
manifestHash: hash2_y,
|
|
}))
|
|
})
|
|
t.Run("Same hash, empty health", func(t *testing.T) {
|
|
assert.True(t, skipResourceUpdate(&ResourceInfo{
|
|
manifestHash: hash1_x,
|
|
Health: &health.HealthStatus{},
|
|
}, &ResourceInfo{
|
|
manifestHash: hash3_x,
|
|
Health: &health.HealthStatus{},
|
|
}))
|
|
})
|
|
t.Run("Same hash, old health", func(t *testing.T) {
|
|
assert.False(t, skipResourceUpdate(&ResourceInfo{
|
|
manifestHash: hash1_x,
|
|
Health: &health.HealthStatus{
|
|
Status: health.HealthStatusHealthy},
|
|
}, &ResourceInfo{
|
|
manifestHash: hash3_x,
|
|
Health: nil,
|
|
}))
|
|
})
|
|
t.Run("Same hash, new health", func(t *testing.T) {
|
|
assert.False(t, skipResourceUpdate(&ResourceInfo{
|
|
manifestHash: hash1_x,
|
|
Health: &health.HealthStatus{},
|
|
}, &ResourceInfo{
|
|
manifestHash: hash3_x,
|
|
Health: &health.HealthStatus{
|
|
Status: health.HealthStatusHealthy,
|
|
},
|
|
}))
|
|
})
|
|
t.Run("Same hash, same health", func(t *testing.T) {
|
|
assert.True(t, skipResourceUpdate(&ResourceInfo{
|
|
manifestHash: hash1_x,
|
|
Health: &health.HealthStatus{
|
|
Status: health.HealthStatusHealthy,
|
|
Message: "same",
|
|
},
|
|
}, &ResourceInfo{
|
|
manifestHash: hash3_x,
|
|
Health: &health.HealthStatus{
|
|
Status: health.HealthStatusHealthy,
|
|
Message: "same",
|
|
},
|
|
}))
|
|
})
|
|
t.Run("Same hash, different health status", func(t *testing.T) {
|
|
assert.False(t, skipResourceUpdate(&ResourceInfo{
|
|
manifestHash: hash1_x,
|
|
Health: &health.HealthStatus{
|
|
Status: health.HealthStatusHealthy,
|
|
Message: "same",
|
|
},
|
|
}, &ResourceInfo{
|
|
manifestHash: hash3_x,
|
|
Health: &health.HealthStatus{
|
|
Status: health.HealthStatusDegraded,
|
|
Message: "same",
|
|
},
|
|
}))
|
|
})
|
|
t.Run("Same hash, different health message", func(t *testing.T) {
|
|
assert.True(t, skipResourceUpdate(&ResourceInfo{
|
|
manifestHash: hash1_x,
|
|
Health: &health.HealthStatus{
|
|
Status: health.HealthStatusHealthy,
|
|
Message: "same",
|
|
},
|
|
}, &ResourceInfo{
|
|
manifestHash: hash3_x,
|
|
Health: &health.HealthStatus{
|
|
Status: health.HealthStatusHealthy,
|
|
Message: "different",
|
|
},
|
|
}))
|
|
})
|
|
}
|