mirror of
https://github.com/argoproj/argo-cd.git
synced 2026-03-11 19:08:49 +01:00
Compare commits
21 Commits
v3.0.19
...
release-3.
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
7cedae735c | ||
|
|
5f9dc64088 | ||
|
|
9a495627ce | ||
|
|
6c9b1cb046 | ||
|
|
1eaf112dbb | ||
|
|
f9212b0961 | ||
|
|
e5b85eff35 | ||
|
|
14cd1c1412 | ||
|
|
73b21ff4f2 | ||
|
|
8910d47425 | ||
|
|
38b108e255 | ||
|
|
a24b8ec7d2 | ||
|
|
97dc75ee80 | ||
|
|
a9a7868dc4 | ||
|
|
b53d2a2443 | ||
|
|
ba78f8cdeb | ||
|
|
ad1eacbe93 | ||
|
|
653f7adb97 | ||
|
|
bdab094f78 | ||
|
|
3e09844ca5 | ||
|
|
a7a88fd43d |
2
.github/workflows/ci-build.yaml
vendored
2
.github/workflows/ci-build.yaml
vendored
@@ -487,7 +487,7 @@ jobs:
|
||||
run: |
|
||||
docker pull ghcr.io/dexidp/dex:v2.41.1
|
||||
docker pull argoproj/argo-cd-ci-builder:v1.0.0
|
||||
docker pull redis:7.2.7-alpine
|
||||
docker pull redis:7.2.11-alpine
|
||||
- name: Create target directory for binaries in the build-process
|
||||
run: |
|
||||
mkdir -p dist
|
||||
|
||||
@@ -71,6 +71,7 @@ const (
|
||||
var defaultPreservedAnnotations = []string{
|
||||
NotifiedAnnotationKey,
|
||||
argov1alpha1.AnnotationKeyRefresh,
|
||||
argov1alpha1.AnnotationKeyHydrate,
|
||||
}
|
||||
|
||||
// ApplicationSetReconciler reconciles a ApplicationSet object
|
||||
|
||||
@@ -588,6 +588,72 @@ func TestCreateOrUpdateInCluster(t *testing.T) {
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "Ensure that hydrate annotation is preserved from an existing app",
|
||||
appSet: v1alpha1.ApplicationSet{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "name",
|
||||
Namespace: "namespace",
|
||||
},
|
||||
Spec: v1alpha1.ApplicationSetSpec{
|
||||
Template: v1alpha1.ApplicationSetTemplate{
|
||||
Spec: v1alpha1.ApplicationSpec{
|
||||
Project: "project",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
existingApps: []v1alpha1.Application{
|
||||
{
|
||||
TypeMeta: metav1.TypeMeta{
|
||||
Kind: application.ApplicationKind,
|
||||
APIVersion: "argoproj.io/v1alpha1",
|
||||
},
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "app1",
|
||||
Namespace: "namespace",
|
||||
ResourceVersion: "2",
|
||||
Annotations: map[string]string{
|
||||
"annot-key": "annot-value",
|
||||
v1alpha1.AnnotationKeyHydrate: string(v1alpha1.RefreshTypeNormal),
|
||||
},
|
||||
},
|
||||
Spec: v1alpha1.ApplicationSpec{
|
||||
Project: "project",
|
||||
},
|
||||
},
|
||||
},
|
||||
desiredApps: []v1alpha1.Application{
|
||||
{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "app1",
|
||||
Namespace: "namespace",
|
||||
},
|
||||
Spec: v1alpha1.ApplicationSpec{
|
||||
Project: "project",
|
||||
},
|
||||
},
|
||||
},
|
||||
expected: []v1alpha1.Application{
|
||||
{
|
||||
TypeMeta: metav1.TypeMeta{
|
||||
Kind: application.ApplicationKind,
|
||||
APIVersion: "argoproj.io/v1alpha1",
|
||||
},
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "app1",
|
||||
Namespace: "namespace",
|
||||
ResourceVersion: "3",
|
||||
Annotations: map[string]string{
|
||||
v1alpha1.AnnotationKeyHydrate: string(v1alpha1.RefreshTypeNormal),
|
||||
},
|
||||
},
|
||||
Spec: v1alpha1.ApplicationSpec{
|
||||
Project: "project",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "Ensure that configured preserved annotations are preserved from an existing app",
|
||||
appSet: v1alpha1.ApplicationSet{
|
||||
|
||||
@@ -25,10 +25,14 @@ import (
|
||||
"github.com/go-playground/webhooks/v6/github"
|
||||
"github.com/go-playground/webhooks/v6/gitlab"
|
||||
log "github.com/sirupsen/logrus"
|
||||
|
||||
"github.com/argoproj/argo-cd/v3/util/guard"
|
||||
)
|
||||
|
||||
const payloadQueueSize = 50000
|
||||
|
||||
const panicMsgAppSet = "panic while processing applicationset-controller webhook event"
|
||||
|
||||
type WebhookHandler struct {
|
||||
sync.WaitGroup // for testing
|
||||
namespace string
|
||||
@@ -103,6 +107,7 @@ func NewWebhookHandler(namespace string, webhookParallelism int, argocdSettingsM
|
||||
}
|
||||
|
||||
func (h *WebhookHandler) startWorkerPool(webhookParallelism int) {
|
||||
compLog := log.WithField("component", "applicationset-webhook")
|
||||
for i := 0; i < webhookParallelism; i++ {
|
||||
h.Add(1)
|
||||
go func() {
|
||||
@@ -112,7 +117,7 @@ func (h *WebhookHandler) startWorkerPool(webhookParallelism int) {
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
h.HandleEvent(payload)
|
||||
guard.RecoverAndLog(func() { h.HandleEvent(payload) }, compLog, panicMsgAppSet)
|
||||
}
|
||||
}()
|
||||
}
|
||||
|
||||
@@ -247,7 +247,7 @@ func (m *appStateManager) GetRepoObjs(app *v1alpha1.Application, sources []v1alp
|
||||
Revision: revision,
|
||||
SyncedRevision: syncedRevision,
|
||||
NoRevisionCache: noRevisionCache,
|
||||
Paths: path.GetAppRefreshPaths(app),
|
||||
Paths: path.GetSourceRefreshPaths(app, source),
|
||||
AppLabelKey: appLabelKey,
|
||||
AppName: app.InstanceName(m.namespace),
|
||||
Namespace: appNamespace,
|
||||
|
||||
@@ -35,14 +35,26 @@ metadata:
|
||||
name: argocd-notifications-cm
|
||||
data:
|
||||
trigger.sync-operation-change: |
|
||||
- when: app.status.operationState.phase in ['Succeeded']
|
||||
- when: app.status?.operationState.phase in ['Succeeded']
|
||||
send: [github-commit-status]
|
||||
- when: app.status.operationState.phase in ['Running']
|
||||
- when: app.status?.operationState.phase in ['Running']
|
||||
send: [github-commit-status]
|
||||
- when: app.status.operationState.phase in ['Error', 'Failed']
|
||||
- when: app.status?.operationState.phase in ['Error', 'Failed']
|
||||
send: [app-sync-failed, github-commit-status]
|
||||
```
|
||||
|
||||
|
||||
## Accessing Optional Manifest Sections and Fields
|
||||
|
||||
Note that in the trigger example above, the `?.` (optional chaining) operator is used to access the Application's
|
||||
`status.operationState` section. This section is optional; it is not present when an operation has been initiated but has not yet
|
||||
started by the Application Controller.
|
||||
|
||||
If the `?.` operator were not used, `status.operationState` would resolve to `nil` and the evaluation of the
|
||||
`app.status.operationState.phase` expression would fail. The `app.status?.operationState.phase` expression is equivalent to
|
||||
`app.status.operationState != nil ? app.status.operationState.phase : nil`.
|
||||
|
||||
|
||||
## Avoid Sending Same Notification Too Often
|
||||
|
||||
In some cases, the trigger condition might be "flapping". The example below illustrates the problem.
|
||||
@@ -60,14 +72,14 @@ data:
|
||||
# Optional 'oncePer' property ensure that notification is sent only once per specified field value
|
||||
# E.g. following is triggered once per sync revision
|
||||
trigger.on-deployed: |
|
||||
when: app.status.operationState.phase in ['Succeeded'] and app.status.health.status == 'Healthy'
|
||||
when: app.status?.operationState.phase in ['Succeeded'] and app.status.health.status == 'Healthy'
|
||||
oncePer: app.status.sync.revision
|
||||
send: [app-sync-succeeded]
|
||||
```
|
||||
|
||||
**Mono Repo Usage**
|
||||
|
||||
When one repo is used to sync multiple applications, the `oncePer: app.status.sync.revision` field will trigger a notification for each commit. For mono repos, the better approach will be using `oncePer: app.status.operationState.syncResult.revision` statement. This way a notification will be sent only for a particular Application's revision.
|
||||
When one repo is used to sync multiple applications, the `oncePer: app.status.sync.revision` field will trigger a notification for each commit. For mono repos, the better approach will be using `oncePer: app.status?.operationState.syncResult.revision` statement. This way a notification will be sent only for a particular Application's revision.
|
||||
|
||||
### oncePer
|
||||
|
||||
@@ -122,7 +134,7 @@ Triggers have access to the set of built-in functions.
|
||||
Example:
|
||||
|
||||
```yaml
|
||||
when: time.Now().Sub(time.Parse(app.status.operationState.startedAt)).Minutes() >= 5
|
||||
when: time.Now().Sub(time.Parse(app.status?.operationState.startedAt)).Minutes() >= 5
|
||||
```
|
||||
|
||||
{!docs/operator-manual/notifications/functions.md!}
|
||||
|
||||
@@ -287,6 +287,9 @@ resources.
|
||||
resource being deleted, Argo CD will fail to recognize that the resource is managed by the Application and will not
|
||||
delete it. To avoid this edge case, it is recommended to perform a sync operation on your Applications, even if
|
||||
they are not out of sync, so that orphan resource detection will work as expected on the next sync.
|
||||
|
||||
After upgrading to version 3.0, the Argo CD tracking annotation will only appear on an Application’s resources when
|
||||
either a new Git commit is made or the Application is explicitly synced.
|
||||
|
||||
##### Users who rely on label-based for resources that are not managed by Argo CD
|
||||
Some users rely on label-based tracking to track resources that are not managed by Argo CD. They may set annotations
|
||||
@@ -496,4 +499,4 @@ More details for ignored resource updates in the [Diffing customization](../../u
|
||||
|
||||
Due to security reasons ([GHSA-786q-9hcg-v9ff](https://github.com/argoproj/argo-cd/security/advisories/GHSA-786q-9hcg-v9ff)),
|
||||
the project API response was sanitized to remove sensitive information. This includes
|
||||
credentials of project-scoped repositories and clusters.
|
||||
credentials of project-scoped repositories and clusters.
|
||||
|
||||
21
go.mod
21
go.mod
@@ -12,7 +12,7 @@ require (
|
||||
github.com/Masterminds/sprig/v3 v3.3.0
|
||||
github.com/TomOnTime/utfutil v1.0.0
|
||||
github.com/alicebob/miniredis/v2 v2.34.0
|
||||
github.com/argoproj/gitops-engine v0.7.1-0.20250905153922-d96c3d51e4c4
|
||||
github.com/argoproj/gitops-engine v0.7.1-0.20250905171100-0882c168faa3
|
||||
github.com/argoproj/notifications-engine v0.4.1-0.20250309174002-87bf0576a872
|
||||
github.com/argoproj/pkg v0.13.7-0.20250305113207-cbc37dc61de5
|
||||
github.com/aws/aws-sdk-go v1.55.6
|
||||
@@ -28,7 +28,7 @@ require (
|
||||
github.com/dlclark/regexp2 v1.11.5
|
||||
github.com/dustin/go-humanize v1.0.1
|
||||
github.com/evanphx/json-patch v5.9.11+incompatible
|
||||
github.com/expr-lang/expr v1.16.9
|
||||
github.com/expr-lang/expr v1.17.7
|
||||
github.com/felixge/httpsnoop v1.0.4
|
||||
github.com/fsnotify/fsnotify v1.8.0
|
||||
github.com/gfleury/go-bitbucket-v1 v0.0.0-20240917142304-df385efaac68
|
||||
@@ -88,12 +88,12 @@ require (
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.32.0
|
||||
go.opentelemetry.io/otel/sdk v1.34.0
|
||||
go.uber.org/automaxprocs v1.6.0
|
||||
golang.org/x/crypto v0.38.0
|
||||
golang.org/x/crypto v0.46.0
|
||||
golang.org/x/exp v0.0.0-20241108190413-2d47ceb2692f
|
||||
golang.org/x/net v0.40.0
|
||||
golang.org/x/net v0.47.0
|
||||
golang.org/x/oauth2 v0.28.0
|
||||
golang.org/x/sync v0.14.0
|
||||
golang.org/x/term v0.32.0
|
||||
golang.org/x/sync v0.19.0
|
||||
golang.org/x/term v0.38.0
|
||||
golang.org/x/time v0.11.0
|
||||
google.golang.org/genproto/googleapis/api v0.0.0-20250106144421-5f5ef82da422
|
||||
google.golang.org/grpc v1.71.0
|
||||
@@ -263,10 +263,11 @@ require (
|
||||
go.opentelemetry.io/otel/metric v1.35.0 // indirect
|
||||
go.opentelemetry.io/otel/trace v1.35.0 // indirect
|
||||
go.opentelemetry.io/proto/otlp v1.3.1 // indirect
|
||||
golang.org/x/mod v0.22.0 // indirect
|
||||
golang.org/x/sys v0.33.0 // indirect
|
||||
golang.org/x/text v0.25.0 // indirect
|
||||
golang.org/x/tools v0.27.0 // indirect
|
||||
golang.org/x/mod v0.30.0 // indirect
|
||||
golang.org/x/sys v0.39.0 // indirect
|
||||
golang.org/x/text v0.32.0 // indirect
|
||||
golang.org/x/tools v0.39.0 // indirect
|
||||
golang.org/x/tools/go/packages/packagestest v0.1.1-deprecated // indirect
|
||||
gomodules.xyz/envconfig v1.3.1-0.20190308184047-426f31af0d45 // indirect
|
||||
gomodules.xyz/jsonpatch/v2 v2.4.0 // indirect
|
||||
gomodules.xyz/notify v0.1.1 // indirect
|
||||
|
||||
44
go.sum
44
go.sum
@@ -114,8 +114,8 @@ github.com/anmitsu/go-shlex v0.0.0-20200514113438-38f4b401e2be h1:9AeTilPcZAjCFI
|
||||
github.com/anmitsu/go-shlex v0.0.0-20200514113438-38f4b401e2be/go.mod h1:ySMOLuWl6zY27l47sB3qLNK6tF2fkHG55UZxx8oIVo4=
|
||||
github.com/antihax/optional v1.0.0/go.mod h1:uupD/76wgC+ih3iEmQUL+0Ugr19nfwCT1kdvxnR2qWY=
|
||||
github.com/appscode/go v0.0.0-20191119085241-0887d8ec2ecc/go.mod h1:OawnOmAL4ZX3YaPdN+8HTNwBveT1jMsqP74moa9XUbE=
|
||||
github.com/argoproj/gitops-engine v0.7.1-0.20250905153922-d96c3d51e4c4 h1:OsQxWX8UHdwXuy72Y1Js8gQY3xmOzFEieCSpMoXKFb8=
|
||||
github.com/argoproj/gitops-engine v0.7.1-0.20250905153922-d96c3d51e4c4/go.mod h1:duVhxDW7M7M7+19IBCVth2REOS11gmqzTWwj4u8N7aQ=
|
||||
github.com/argoproj/gitops-engine v0.7.1-0.20250905171100-0882c168faa3 h1:Nw5ZqatjlxUgzWMLZ3Josj7csW2TQSYLIP6D9IGz+kY=
|
||||
github.com/argoproj/gitops-engine v0.7.1-0.20250905171100-0882c168faa3/go.mod h1:duVhxDW7M7M7+19IBCVth2REOS11gmqzTWwj4u8N7aQ=
|
||||
github.com/argoproj/notifications-engine v0.4.1-0.20250309174002-87bf0576a872 h1:ADGAdyN9ty0+RmTT/yn+xV9vwkqvLn9O1ccqeP0Zeas=
|
||||
github.com/argoproj/notifications-engine v0.4.1-0.20250309174002-87bf0576a872/go.mod h1:d1RazGXWvKRFv9//rg4MRRR7rbvbE7XLgTSMT5fITTE=
|
||||
github.com/argoproj/pkg v0.13.7-0.20250305113207-cbc37dc61de5 h1:YBoLSjpoaJXaXAldVvBRKJuOPvIXz9UOv6S96gMJM/Q=
|
||||
@@ -243,8 +243,8 @@ github.com/evanphx/json-patch/v5 v5.9.11 h1:/8HVnzMq13/3x9TPvjG08wUGqBTmZBsCWzjT
|
||||
github.com/evanphx/json-patch/v5 v5.9.11/go.mod h1:3j+LviiESTElxA4p3EMKAB9HXj3/XEtnUf6OZxqIQTM=
|
||||
github.com/exponent-io/jsonpath v0.0.0-20210407135951-1de76d718b3f h1:Wl78ApPPB2Wvf/TIe2xdyJxTlb6obmF18d8QdkxNDu4=
|
||||
github.com/exponent-io/jsonpath v0.0.0-20210407135951-1de76d718b3f/go.mod h1:OSYXu++VVOHnXeitef/D8n/6y4QV8uLHSFXX4NeXMGc=
|
||||
github.com/expr-lang/expr v1.16.9 h1:WUAzmR0JNI9JCiF0/ewwHB1gmcGw5wW7nWt8gc6PpCI=
|
||||
github.com/expr-lang/expr v1.16.9/go.mod h1:8/vRC7+7HBzESEqt5kKpYXxrxkr31SaO8r40VO/1IT4=
|
||||
github.com/expr-lang/expr v1.17.7 h1:Q0xY/e/2aCIp8g9s/LGvMDCC5PxYlvHgDZRQ4y16JX8=
|
||||
github.com/expr-lang/expr v1.17.7/go.mod h1:8/vRC7+7HBzESEqt5kKpYXxrxkr31SaO8r40VO/1IT4=
|
||||
github.com/facebookgo/ensure v0.0.0-20160127193407-b4ab57deab51/go.mod h1:Yg+htXGokKKdzcwhuNDwVvN+uBxDGXJ7G/VN1d8fa64=
|
||||
github.com/facebookgo/stack v0.0.0-20160209184415-751773369052/go.mod h1:UbMTZqLaRiH3MsBH8va0n7s1pQYcu3uTb8G4tygF4Zg=
|
||||
github.com/facebookgo/subset v0.0.0-20150612182917-8dac2c3c4870/go.mod h1:5tD+neXqOorC30/tWg0LCSkrqj/AR6gu8yY8/fpw1q0=
|
||||
@@ -881,8 +881,8 @@ golang.org/x/crypto v0.6.0/go.mod h1:OFC/31mSvZgRz0V1QTNCzfAI1aIRzbiufJtkMIlEp58
|
||||
golang.org/x/crypto v0.19.0/go.mod h1:Iy9bg/ha4yyC70EfRS8jz+B6ybOBKMaSxLj6P6oBDfU=
|
||||
golang.org/x/crypto v0.21.0/go.mod h1:0BP7YvVV9gBbVKyeTG0Gyn+gZm94bibOW5BjDEYAOMs=
|
||||
golang.org/x/crypto v0.23.0/go.mod h1:CKFgDieR+mRhux2Lsu27y0fO304Db0wZe70UKqHu0v8=
|
||||
golang.org/x/crypto v0.38.0 h1:jt+WWG8IZlBnVbomuhg2Mdq0+BBQaHbtqHEFEigjUV8=
|
||||
golang.org/x/crypto v0.38.0/go.mod h1:MvrbAqul58NNYPKnOra203SB9vpuZW0e+RRZV+Ggqjw=
|
||||
golang.org/x/crypto v0.46.0 h1:cKRW/pmt1pKAfetfu+RCEvjvZkA9RimPbh7bhFjGVBU=
|
||||
golang.org/x/crypto v0.46.0/go.mod h1:Evb/oLKmMraqjZ2iQTwDwvCtJkczlDuTmdJXoZVzqU0=
|
||||
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
|
||||
golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
|
||||
golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8=
|
||||
@@ -921,8 +921,8 @@ golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91
|
||||
golang.org/x/mod v0.6.0/go.mod h1:4mET923SAdbXp2ki8ey+zGs1SLqsuM2Y0uvdZR/fUNI=
|
||||
golang.org/x/mod v0.7.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
|
||||
golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
|
||||
golang.org/x/mod v0.22.0 h1:D4nJWe9zXqHOmWqj4VMOJhvzj7bEZg4wEYa759z1pH4=
|
||||
golang.org/x/mod v0.22.0/go.mod h1:6SkKJ3Xj0I0BrPOZoBy3bdMptDDU9oJrpohJ3eWZ1fY=
|
||||
golang.org/x/mod v0.30.0 h1:fDEXFVZ/fmCKProc/yAXXUijritrDzahmwwefnjoPFk=
|
||||
golang.org/x/mod v0.30.0/go.mod h1:lAsf5O2EvJeSFMiBxXDki7sCgAxEUcZHXoXMKT4GJKc=
|
||||
golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
@@ -975,8 +975,8 @@ golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg=
|
||||
golang.org/x/net v0.21.0/go.mod h1:bIjVDfnllIU7BJ2DNgfnXvpSvtn8VRwhlsaeUTyUS44=
|
||||
golang.org/x/net v0.23.0/go.mod h1:JKghWKKOSdJwpW2GEx0Ja7fmaKnMsbu+MWVZTokSYmg=
|
||||
golang.org/x/net v0.25.0/go.mod h1:JkAGAh7GEvH74S6FOH42FLoXpXbE/aqXSrIQjXgsiwM=
|
||||
golang.org/x/net v0.40.0 h1:79Xs7wF06Gbdcg4kdCCIQArK11Z1hr5POQ6+fIYHNuY=
|
||||
golang.org/x/net v0.40.0/go.mod h1:y0hY0exeL2Pku80/zKK7tpntoX23cqL3Oa6njdgRtds=
|
||||
golang.org/x/net v0.47.0 h1:Mx+4dIFzqraBXUugkia1OOvlD6LemFo1ALMHjrXDOhY=
|
||||
golang.org/x/net v0.47.0/go.mod h1:/jNxtkgq5yWUGYkaZGqo27cfGZ1c5Nen03aYrrKpVRU=
|
||||
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
|
||||
golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
|
||||
golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
|
||||
@@ -999,8 +999,8 @@ golang.org/x/sync v0.0.0-20201207232520-09787c993a3a/go.mod h1:RxMgew5VJxzue5/jJ
|
||||
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.14.0 h1:woo0S4Yywslg6hp4eUFjTVOyKt0RookbpAHG4c1HmhQ=
|
||||
golang.org/x/sync v0.14.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA=
|
||||
golang.org/x/sync v0.19.0 h1:vV+1eWNmZ5geRlYjzm2adRgW2/mcpevXNg50YZtPCE4=
|
||||
golang.org/x/sync v0.19.0/go.mod h1:9KTHXmSnoGruLpwFjVSX0lNNA75CykiMECbovNTZqGI=
|
||||
golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
@@ -1069,8 +1069,8 @@ golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.17.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||
golang.org/x/sys v0.18.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||
golang.org/x/sys v0.20.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||
golang.org/x/sys v0.33.0 h1:q3i8TbbEz+JRD9ywIRlyRAQbM0qF7hu24q3teo2hbuw=
|
||||
golang.org/x/sys v0.33.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k=
|
||||
golang.org/x/sys v0.39.0 h1:CvCKL8MeisomCi6qNZ+wbb0DN9E5AATixKsvNtMoMFk=
|
||||
golang.org/x/sys v0.39.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks=
|
||||
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
||||
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
|
||||
golang.org/x/term v0.1.0/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
|
||||
@@ -1082,8 +1082,8 @@ golang.org/x/term v0.8.0/go.mod h1:xPskH00ivmX89bAKVGSKKtLOWNx2+17Eiy94tnKShWo=
|
||||
golang.org/x/term v0.17.0/go.mod h1:lLRBjIVuehSbZlaOtGMbcMncT+aqLLLmKrsjNrUguwk=
|
||||
golang.org/x/term v0.18.0/go.mod h1:ILwASektA3OnRv7amZ1xhE/KTR+u50pbXfZ03+6Nx58=
|
||||
golang.org/x/term v0.20.0/go.mod h1:8UkIAJTvZgivsXaD6/pH6U9ecQzZ45awqEOzuCvwpFY=
|
||||
golang.org/x/term v0.32.0 h1:DR4lr0TjUs3epypdhTOkMmuF5CDFJ/8pOnbzMZPQ7bg=
|
||||
golang.org/x/term v0.32.0/go.mod h1:uZG1FhGx848Sqfsq4/DlJr3xGGsYMu/L5GW4abiaEPQ=
|
||||
golang.org/x/term v0.38.0 h1:PQ5pkm/rLO6HnxFR7N2lJHOZX6Kez5Y1gDSJla6jo7Q=
|
||||
golang.org/x/term v0.38.0/go.mod h1:bSEAKrOT1W+VSu9TSCMtoGEOUcKxOKgl3LE5QEF/xVg=
|
||||
golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
@@ -1098,8 +1098,8 @@ golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
|
||||
golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8=
|
||||
golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
|
||||
golang.org/x/text v0.15.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
|
||||
golang.org/x/text v0.25.0 h1:qVyWApTSYLk/drJRO5mDlNYskwQznZmkpV2c8q9zls4=
|
||||
golang.org/x/text v0.25.0/go.mod h1:WEdwpYrmk1qmdHvhkSTNPm3app7v4rsT8F2UD6+VHIA=
|
||||
golang.org/x/text v0.32.0 h1:ZD01bjUt1FQ9WJ0ClOL5vxgxOI/sVCNgX1YtKwcY0mU=
|
||||
golang.org/x/text v0.32.0/go.mod h1:o/rUWzghvpD5TXrTIBuJU77MTaN0ljMWE47kxGJQ7jY=
|
||||
golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
||||
golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
||||
golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
||||
@@ -1156,8 +1156,12 @@ golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc
|
||||
golang.org/x/tools v0.2.0/go.mod h1:y4OqIKeOV/fWJetJ8bXPU1sEVniLMIyDAZWeHdV+NTA=
|
||||
golang.org/x/tools v0.4.0/go.mod h1:UE5sM2OK9E/d67R0ANs2xJizIymRP5gJU295PvKXxjQ=
|
||||
golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU=
|
||||
golang.org/x/tools v0.27.0 h1:qEKojBykQkQ4EynWy4S8Weg69NumxKdn40Fce3uc/8o=
|
||||
golang.org/x/tools v0.27.0/go.mod h1:sUi0ZgbwW9ZPAq26Ekut+weQPR5eIM6GQLQ1Yjm1H0Q=
|
||||
golang.org/x/tools v0.39.0 h1:ik4ho21kwuQln40uelmciQPp9SipgNDdrafrYA4TmQQ=
|
||||
golang.org/x/tools v0.39.0/go.mod h1:JnefbkDPyD8UU2kI5fuf8ZX4/yUeh9W877ZeBONxUqQ=
|
||||
golang.org/x/tools/go/expect v0.1.0-deprecated h1:jY2C5HGYR5lqex3gEniOQL0r7Dq5+VGVgY1nudX5lXY=
|
||||
golang.org/x/tools/go/expect v0.1.0-deprecated/go.mod h1:eihoPOH+FgIqa3FpoTwguz/bVUSGBlGQU67vpBeOrBY=
|
||||
golang.org/x/tools/go/packages/packagestest v0.1.1-deprecated h1:1h2MnaIAIXISqTFKdENegdpAgUXz6NrPEsbIeWaBRvM=
|
||||
golang.org/x/tools/go/packages/packagestest v0.1.1-deprecated/go.mod h1:RVAQXBGNv1ib0J382/DPCRS/BPnsGebyM1Gj5VSDpG8=
|
||||
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
|
||||
@@ -12,4 +12,4 @@ resources:
|
||||
images:
|
||||
- name: quay.io/argoproj/argocd
|
||||
newName: quay.io/argoproj/argocd
|
||||
newTag: v3.0.19
|
||||
newTag: v3.0.23
|
||||
|
||||
@@ -5,7 +5,7 @@ kind: Kustomization
|
||||
images:
|
||||
- name: quay.io/argoproj/argocd
|
||||
newName: quay.io/argoproj/argocd
|
||||
newTag: v3.0.19
|
||||
newTag: v3.0.23
|
||||
resources:
|
||||
- ./application-controller
|
||||
- ./dex
|
||||
|
||||
@@ -40,7 +40,7 @@ spec:
|
||||
serviceAccountName: argocd-redis
|
||||
containers:
|
||||
- name: redis
|
||||
image: redis:7.2.7-alpine
|
||||
image: redis:7.2.11-alpine
|
||||
imagePullPolicy: Always
|
||||
args:
|
||||
- "--save"
|
||||
|
||||
16
manifests/core-install-with-hydrator.yaml
generated
16
manifests/core-install-with-hydrator.yaml
generated
@@ -24615,7 +24615,7 @@ spec:
|
||||
key: applicationsetcontroller.status.max.resources.count
|
||||
name: argocd-cmd-params-cm
|
||||
optional: true
|
||||
image: quay.io/argoproj/argocd:v3.0.19
|
||||
image: quay.io/argoproj/argocd:v3.0.23
|
||||
imagePullPolicy: Always
|
||||
name: argocd-applicationset-controller
|
||||
ports:
|
||||
@@ -24741,7 +24741,7 @@ spec:
|
||||
key: log.format.timestamp
|
||||
name: argocd-cmd-params-cm
|
||||
optional: true
|
||||
image: quay.io/argoproj/argocd:v3.0.19
|
||||
image: quay.io/argoproj/argocd:v3.0.23
|
||||
imagePullPolicy: Always
|
||||
livenessProbe:
|
||||
failureThreshold: 3
|
||||
@@ -24787,7 +24787,7 @@ spec:
|
||||
- -n
|
||||
- /usr/local/bin/argocd
|
||||
- /var/run/argocd/argocd-cmp-server
|
||||
image: quay.io/argoproj/argocd:v3.0.19
|
||||
image: quay.io/argoproj/argocd:v3.0.23
|
||||
name: copyutil
|
||||
securityContext:
|
||||
allowPrivilegeEscalation: false
|
||||
@@ -24875,7 +24875,7 @@ spec:
|
||||
secretKeyRef:
|
||||
key: auth
|
||||
name: argocd-redis
|
||||
image: redis:7.2.7-alpine
|
||||
image: redis:7.2.11-alpine
|
||||
imagePullPolicy: Always
|
||||
name: redis
|
||||
ports:
|
||||
@@ -24891,7 +24891,7 @@ spec:
|
||||
- argocd
|
||||
- admin
|
||||
- redis-initial-password
|
||||
image: quay.io/argoproj/argocd:v3.0.19
|
||||
image: quay.io/argoproj/argocd:v3.0.23
|
||||
imagePullPolicy: IfNotPresent
|
||||
name: secret-init
|
||||
securityContext:
|
||||
@@ -25164,7 +25164,7 @@ spec:
|
||||
value: /helm-working-dir
|
||||
- name: HELM_DATA_HOME
|
||||
value: /helm-working-dir
|
||||
image: quay.io/argoproj/argocd:v3.0.19
|
||||
image: quay.io/argoproj/argocd:v3.0.23
|
||||
imagePullPolicy: Always
|
||||
livenessProbe:
|
||||
failureThreshold: 3
|
||||
@@ -25216,7 +25216,7 @@ spec:
|
||||
- -n
|
||||
- /usr/local/bin/argocd
|
||||
- /var/run/argocd/argocd-cmp-server
|
||||
image: quay.io/argoproj/argocd:v3.0.19
|
||||
image: quay.io/argoproj/argocd:v3.0.23
|
||||
name: copyutil
|
||||
securityContext:
|
||||
allowPrivilegeEscalation: false
|
||||
@@ -25558,7 +25558,7 @@ spec:
|
||||
optional: true
|
||||
- name: KUBECACHEDIR
|
||||
value: /tmp/kubecache
|
||||
image: quay.io/argoproj/argocd:v3.0.19
|
||||
image: quay.io/argoproj/argocd:v3.0.23
|
||||
imagePullPolicy: Always
|
||||
name: argocd-application-controller
|
||||
ports:
|
||||
|
||||
12
manifests/core-install.yaml
generated
12
manifests/core-install.yaml
generated
@@ -24583,7 +24583,7 @@ spec:
|
||||
key: applicationsetcontroller.status.max.resources.count
|
||||
name: argocd-cmd-params-cm
|
||||
optional: true
|
||||
image: quay.io/argoproj/argocd:v3.0.19
|
||||
image: quay.io/argoproj/argocd:v3.0.23
|
||||
imagePullPolicy: Always
|
||||
name: argocd-applicationset-controller
|
||||
ports:
|
||||
@@ -24687,7 +24687,7 @@ spec:
|
||||
secretKeyRef:
|
||||
key: auth
|
||||
name: argocd-redis
|
||||
image: redis:7.2.7-alpine
|
||||
image: redis:7.2.11-alpine
|
||||
imagePullPolicy: Always
|
||||
name: redis
|
||||
ports:
|
||||
@@ -24703,7 +24703,7 @@ spec:
|
||||
- argocd
|
||||
- admin
|
||||
- redis-initial-password
|
||||
image: quay.io/argoproj/argocd:v3.0.19
|
||||
image: quay.io/argoproj/argocd:v3.0.23
|
||||
imagePullPolicy: IfNotPresent
|
||||
name: secret-init
|
||||
securityContext:
|
||||
@@ -24976,7 +24976,7 @@ spec:
|
||||
value: /helm-working-dir
|
||||
- name: HELM_DATA_HOME
|
||||
value: /helm-working-dir
|
||||
image: quay.io/argoproj/argocd:v3.0.19
|
||||
image: quay.io/argoproj/argocd:v3.0.23
|
||||
imagePullPolicy: Always
|
||||
livenessProbe:
|
||||
failureThreshold: 3
|
||||
@@ -25028,7 +25028,7 @@ spec:
|
||||
- -n
|
||||
- /usr/local/bin/argocd
|
||||
- /var/run/argocd/argocd-cmp-server
|
||||
image: quay.io/argoproj/argocd:v3.0.19
|
||||
image: quay.io/argoproj/argocd:v3.0.23
|
||||
name: copyutil
|
||||
securityContext:
|
||||
allowPrivilegeEscalation: false
|
||||
@@ -25370,7 +25370,7 @@ spec:
|
||||
optional: true
|
||||
- name: KUBECACHEDIR
|
||||
value: /tmp/kubecache
|
||||
image: quay.io/argoproj/argocd:v3.0.19
|
||||
image: quay.io/argoproj/argocd:v3.0.23
|
||||
imagePullPolicy: Always
|
||||
name: argocd-application-controller
|
||||
ports:
|
||||
|
||||
@@ -12,4 +12,4 @@ resources:
|
||||
images:
|
||||
- name: quay.io/argoproj/argocd
|
||||
newName: quay.io/argoproj/argocd
|
||||
newTag: v3.0.19
|
||||
newTag: v3.0.23
|
||||
|
||||
@@ -12,7 +12,7 @@ patches:
|
||||
images:
|
||||
- name: quay.io/argoproj/argocd
|
||||
newName: quay.io/argoproj/argocd
|
||||
newTag: v3.0.19
|
||||
newTag: v3.0.23
|
||||
resources:
|
||||
- ../../base/application-controller
|
||||
- ../../base/applicationset-controller
|
||||
|
||||
@@ -1250,7 +1250,7 @@ spec:
|
||||
automountServiceAccountToken: false
|
||||
initContainers:
|
||||
- name: config-init
|
||||
image: public.ecr.aws/docker/library/redis:7.2.7-alpine
|
||||
image: public.ecr.aws/docker/library/redis:7.2.11-alpine
|
||||
imagePullPolicy: IfNotPresent
|
||||
resources:
|
||||
{}
|
||||
@@ -1290,7 +1290,7 @@ spec:
|
||||
|
||||
containers:
|
||||
- name: redis
|
||||
image: public.ecr.aws/docker/library/redis:7.2.7-alpine
|
||||
image: public.ecr.aws/docker/library/redis:7.2.11-alpine
|
||||
imagePullPolicy: IfNotPresent
|
||||
command:
|
||||
- redis-server
|
||||
@@ -1364,7 +1364,7 @@ spec:
|
||||
- /bin/sh
|
||||
- /readonly-config/trigger-failover-if-master.sh
|
||||
- name: sentinel
|
||||
image: public.ecr.aws/docker/library/redis:7.2.7-alpine
|
||||
image: public.ecr.aws/docker/library/redis:7.2.11-alpine
|
||||
imagePullPolicy: IfNotPresent
|
||||
command:
|
||||
- redis-sentinel
|
||||
@@ -1437,7 +1437,7 @@ spec:
|
||||
- sleep 30; redis-cli -p 26379 sentinel reset argocd
|
||||
|
||||
- name: split-brain-fix
|
||||
image: public.ecr.aws/docker/library/redis:7.2.7-alpine
|
||||
image: public.ecr.aws/docker/library/redis:7.2.11-alpine
|
||||
imagePullPolicy: IfNotPresent
|
||||
command:
|
||||
- sh
|
||||
|
||||
@@ -27,7 +27,7 @@ redis-ha:
|
||||
serviceAccount:
|
||||
automountToken: true
|
||||
image:
|
||||
tag: 7.2.7-alpine
|
||||
tag: 7.2.11-alpine
|
||||
sentinel:
|
||||
bind: '0.0.0.0'
|
||||
lifecycle:
|
||||
|
||||
28
manifests/ha/install-with-hydrator.yaml
generated
28
manifests/ha/install-with-hydrator.yaml
generated
@@ -25981,7 +25981,7 @@ spec:
|
||||
key: applicationsetcontroller.status.max.resources.count
|
||||
name: argocd-cmd-params-cm
|
||||
optional: true
|
||||
image: quay.io/argoproj/argocd:v3.0.19
|
||||
image: quay.io/argoproj/argocd:v3.0.23
|
||||
imagePullPolicy: Always
|
||||
name: argocd-applicationset-controller
|
||||
ports:
|
||||
@@ -26107,7 +26107,7 @@ spec:
|
||||
key: log.format.timestamp
|
||||
name: argocd-cmd-params-cm
|
||||
optional: true
|
||||
image: quay.io/argoproj/argocd:v3.0.19
|
||||
image: quay.io/argoproj/argocd:v3.0.23
|
||||
imagePullPolicy: Always
|
||||
livenessProbe:
|
||||
failureThreshold: 3
|
||||
@@ -26153,7 +26153,7 @@ spec:
|
||||
- -n
|
||||
- /usr/local/bin/argocd
|
||||
- /var/run/argocd/argocd-cmp-server
|
||||
image: quay.io/argoproj/argocd:v3.0.19
|
||||
image: quay.io/argoproj/argocd:v3.0.23
|
||||
name: copyutil
|
||||
securityContext:
|
||||
allowPrivilegeEscalation: false
|
||||
@@ -26280,7 +26280,7 @@ spec:
|
||||
- -n
|
||||
- /usr/local/bin/argocd
|
||||
- /shared/argocd-dex
|
||||
image: quay.io/argoproj/argocd:v3.0.19
|
||||
image: quay.io/argoproj/argocd:v3.0.23
|
||||
imagePullPolicy: Always
|
||||
name: copyutil
|
||||
securityContext:
|
||||
@@ -26376,7 +26376,7 @@ spec:
|
||||
key: notificationscontroller.repo.server.plaintext
|
||||
name: argocd-cmd-params-cm
|
||||
optional: true
|
||||
image: quay.io/argoproj/argocd:v3.0.19
|
||||
image: quay.io/argoproj/argocd:v3.0.23
|
||||
imagePullPolicy: Always
|
||||
livenessProbe:
|
||||
tcpSocket:
|
||||
@@ -26500,7 +26500,7 @@ spec:
|
||||
- argocd
|
||||
- admin
|
||||
- redis-initial-password
|
||||
image: quay.io/argoproj/argocd:v3.0.19
|
||||
image: quay.io/argoproj/argocd:v3.0.23
|
||||
imagePullPolicy: IfNotPresent
|
||||
name: secret-init
|
||||
securityContext:
|
||||
@@ -26799,7 +26799,7 @@ spec:
|
||||
value: /helm-working-dir
|
||||
- name: HELM_DATA_HOME
|
||||
value: /helm-working-dir
|
||||
image: quay.io/argoproj/argocd:v3.0.19
|
||||
image: quay.io/argoproj/argocd:v3.0.23
|
||||
imagePullPolicy: Always
|
||||
livenessProbe:
|
||||
failureThreshold: 3
|
||||
@@ -26851,7 +26851,7 @@ spec:
|
||||
- -n
|
||||
- /usr/local/bin/argocd
|
||||
- /var/run/argocd/argocd-cmp-server
|
||||
image: quay.io/argoproj/argocd:v3.0.19
|
||||
image: quay.io/argoproj/argocd:v3.0.23
|
||||
name: copyutil
|
||||
securityContext:
|
||||
allowPrivilegeEscalation: false
|
||||
@@ -27225,7 +27225,7 @@ spec:
|
||||
key: server.sync.replace.allowed
|
||||
name: argocd-cmd-params-cm
|
||||
optional: true
|
||||
image: quay.io/argoproj/argocd:v3.0.19
|
||||
image: quay.io/argoproj/argocd:v3.0.23
|
||||
imagePullPolicy: Always
|
||||
livenessProbe:
|
||||
httpGet:
|
||||
@@ -27603,7 +27603,7 @@ spec:
|
||||
optional: true
|
||||
- name: KUBECACHEDIR
|
||||
value: /tmp/kubecache
|
||||
image: quay.io/argoproj/argocd:v3.0.19
|
||||
image: quay.io/argoproj/argocd:v3.0.23
|
||||
imagePullPolicy: Always
|
||||
name: argocd-application-controller
|
||||
ports:
|
||||
@@ -27701,7 +27701,7 @@ spec:
|
||||
secretKeyRef:
|
||||
key: auth
|
||||
name: argocd-redis
|
||||
image: public.ecr.aws/docker/library/redis:7.2.7-alpine
|
||||
image: public.ecr.aws/docker/library/redis:7.2.11-alpine
|
||||
imagePullPolicy: IfNotPresent
|
||||
lifecycle:
|
||||
preStop:
|
||||
@@ -27772,7 +27772,7 @@ spec:
|
||||
secretKeyRef:
|
||||
key: auth
|
||||
name: argocd-redis
|
||||
image: public.ecr.aws/docker/library/redis:7.2.7-alpine
|
||||
image: public.ecr.aws/docker/library/redis:7.2.11-alpine
|
||||
imagePullPolicy: IfNotPresent
|
||||
lifecycle:
|
||||
postStart:
|
||||
@@ -27847,7 +27847,7 @@ spec:
|
||||
secretKeyRef:
|
||||
key: auth
|
||||
name: argocd-redis
|
||||
image: public.ecr.aws/docker/library/redis:7.2.7-alpine
|
||||
image: public.ecr.aws/docker/library/redis:7.2.11-alpine
|
||||
imagePullPolicy: IfNotPresent
|
||||
name: split-brain-fix
|
||||
resources: {}
|
||||
@@ -27882,7 +27882,7 @@ spec:
|
||||
secretKeyRef:
|
||||
key: auth
|
||||
name: argocd-redis
|
||||
image: public.ecr.aws/docker/library/redis:7.2.7-alpine
|
||||
image: public.ecr.aws/docker/library/redis:7.2.11-alpine
|
||||
imagePullPolicy: IfNotPresent
|
||||
name: config-init
|
||||
securityContext:
|
||||
|
||||
24
manifests/ha/install.yaml
generated
24
manifests/ha/install.yaml
generated
@@ -25951,7 +25951,7 @@ spec:
|
||||
key: applicationsetcontroller.status.max.resources.count
|
||||
name: argocd-cmd-params-cm
|
||||
optional: true
|
||||
image: quay.io/argoproj/argocd:v3.0.19
|
||||
image: quay.io/argoproj/argocd:v3.0.23
|
||||
imagePullPolicy: Always
|
||||
name: argocd-applicationset-controller
|
||||
ports:
|
||||
@@ -26094,7 +26094,7 @@ spec:
|
||||
- -n
|
||||
- /usr/local/bin/argocd
|
||||
- /shared/argocd-dex
|
||||
image: quay.io/argoproj/argocd:v3.0.19
|
||||
image: quay.io/argoproj/argocd:v3.0.23
|
||||
imagePullPolicy: Always
|
||||
name: copyutil
|
||||
securityContext:
|
||||
@@ -26190,7 +26190,7 @@ spec:
|
||||
key: notificationscontroller.repo.server.plaintext
|
||||
name: argocd-cmd-params-cm
|
||||
optional: true
|
||||
image: quay.io/argoproj/argocd:v3.0.19
|
||||
image: quay.io/argoproj/argocd:v3.0.23
|
||||
imagePullPolicy: Always
|
||||
livenessProbe:
|
||||
tcpSocket:
|
||||
@@ -26314,7 +26314,7 @@ spec:
|
||||
- argocd
|
||||
- admin
|
||||
- redis-initial-password
|
||||
image: quay.io/argoproj/argocd:v3.0.19
|
||||
image: quay.io/argoproj/argocd:v3.0.23
|
||||
imagePullPolicy: IfNotPresent
|
||||
name: secret-init
|
||||
securityContext:
|
||||
@@ -26613,7 +26613,7 @@ spec:
|
||||
value: /helm-working-dir
|
||||
- name: HELM_DATA_HOME
|
||||
value: /helm-working-dir
|
||||
image: quay.io/argoproj/argocd:v3.0.19
|
||||
image: quay.io/argoproj/argocd:v3.0.23
|
||||
imagePullPolicy: Always
|
||||
livenessProbe:
|
||||
failureThreshold: 3
|
||||
@@ -26665,7 +26665,7 @@ spec:
|
||||
- -n
|
||||
- /usr/local/bin/argocd
|
||||
- /var/run/argocd/argocd-cmp-server
|
||||
image: quay.io/argoproj/argocd:v3.0.19
|
||||
image: quay.io/argoproj/argocd:v3.0.23
|
||||
name: copyutil
|
||||
securityContext:
|
||||
allowPrivilegeEscalation: false
|
||||
@@ -27039,7 +27039,7 @@ spec:
|
||||
key: server.sync.replace.allowed
|
||||
name: argocd-cmd-params-cm
|
||||
optional: true
|
||||
image: quay.io/argoproj/argocd:v3.0.19
|
||||
image: quay.io/argoproj/argocd:v3.0.23
|
||||
imagePullPolicy: Always
|
||||
livenessProbe:
|
||||
httpGet:
|
||||
@@ -27417,7 +27417,7 @@ spec:
|
||||
optional: true
|
||||
- name: KUBECACHEDIR
|
||||
value: /tmp/kubecache
|
||||
image: quay.io/argoproj/argocd:v3.0.19
|
||||
image: quay.io/argoproj/argocd:v3.0.23
|
||||
imagePullPolicy: Always
|
||||
name: argocd-application-controller
|
||||
ports:
|
||||
@@ -27515,7 +27515,7 @@ spec:
|
||||
secretKeyRef:
|
||||
key: auth
|
||||
name: argocd-redis
|
||||
image: public.ecr.aws/docker/library/redis:7.2.7-alpine
|
||||
image: public.ecr.aws/docker/library/redis:7.2.11-alpine
|
||||
imagePullPolicy: IfNotPresent
|
||||
lifecycle:
|
||||
preStop:
|
||||
@@ -27586,7 +27586,7 @@ spec:
|
||||
secretKeyRef:
|
||||
key: auth
|
||||
name: argocd-redis
|
||||
image: public.ecr.aws/docker/library/redis:7.2.7-alpine
|
||||
image: public.ecr.aws/docker/library/redis:7.2.11-alpine
|
||||
imagePullPolicy: IfNotPresent
|
||||
lifecycle:
|
||||
postStart:
|
||||
@@ -27661,7 +27661,7 @@ spec:
|
||||
secretKeyRef:
|
||||
key: auth
|
||||
name: argocd-redis
|
||||
image: public.ecr.aws/docker/library/redis:7.2.7-alpine
|
||||
image: public.ecr.aws/docker/library/redis:7.2.11-alpine
|
||||
imagePullPolicy: IfNotPresent
|
||||
name: split-brain-fix
|
||||
resources: {}
|
||||
@@ -27696,7 +27696,7 @@ spec:
|
||||
secretKeyRef:
|
||||
key: auth
|
||||
name: argocd-redis
|
||||
image: public.ecr.aws/docker/library/redis:7.2.7-alpine
|
||||
image: public.ecr.aws/docker/library/redis:7.2.11-alpine
|
||||
imagePullPolicy: IfNotPresent
|
||||
name: config-init
|
||||
securityContext:
|
||||
|
||||
28
manifests/ha/namespace-install-with-hydrator.yaml
generated
28
manifests/ha/namespace-install-with-hydrator.yaml
generated
@@ -1868,7 +1868,7 @@ spec:
|
||||
key: applicationsetcontroller.status.max.resources.count
|
||||
name: argocd-cmd-params-cm
|
||||
optional: true
|
||||
image: quay.io/argoproj/argocd:v3.0.19
|
||||
image: quay.io/argoproj/argocd:v3.0.23
|
||||
imagePullPolicy: Always
|
||||
name: argocd-applicationset-controller
|
||||
ports:
|
||||
@@ -1994,7 +1994,7 @@ spec:
|
||||
key: log.format.timestamp
|
||||
name: argocd-cmd-params-cm
|
||||
optional: true
|
||||
image: quay.io/argoproj/argocd:v3.0.19
|
||||
image: quay.io/argoproj/argocd:v3.0.23
|
||||
imagePullPolicy: Always
|
||||
livenessProbe:
|
||||
failureThreshold: 3
|
||||
@@ -2040,7 +2040,7 @@ spec:
|
||||
- -n
|
||||
- /usr/local/bin/argocd
|
||||
- /var/run/argocd/argocd-cmp-server
|
||||
image: quay.io/argoproj/argocd:v3.0.19
|
||||
image: quay.io/argoproj/argocd:v3.0.23
|
||||
name: copyutil
|
||||
securityContext:
|
||||
allowPrivilegeEscalation: false
|
||||
@@ -2167,7 +2167,7 @@ spec:
|
||||
- -n
|
||||
- /usr/local/bin/argocd
|
||||
- /shared/argocd-dex
|
||||
image: quay.io/argoproj/argocd:v3.0.19
|
||||
image: quay.io/argoproj/argocd:v3.0.23
|
||||
imagePullPolicy: Always
|
||||
name: copyutil
|
||||
securityContext:
|
||||
@@ -2263,7 +2263,7 @@ spec:
|
||||
key: notificationscontroller.repo.server.plaintext
|
||||
name: argocd-cmd-params-cm
|
||||
optional: true
|
||||
image: quay.io/argoproj/argocd:v3.0.19
|
||||
image: quay.io/argoproj/argocd:v3.0.23
|
||||
imagePullPolicy: Always
|
||||
livenessProbe:
|
||||
tcpSocket:
|
||||
@@ -2387,7 +2387,7 @@ spec:
|
||||
- argocd
|
||||
- admin
|
||||
- redis-initial-password
|
||||
image: quay.io/argoproj/argocd:v3.0.19
|
||||
image: quay.io/argoproj/argocd:v3.0.23
|
||||
imagePullPolicy: IfNotPresent
|
||||
name: secret-init
|
||||
securityContext:
|
||||
@@ -2686,7 +2686,7 @@ spec:
|
||||
value: /helm-working-dir
|
||||
- name: HELM_DATA_HOME
|
||||
value: /helm-working-dir
|
||||
image: quay.io/argoproj/argocd:v3.0.19
|
||||
image: quay.io/argoproj/argocd:v3.0.23
|
||||
imagePullPolicy: Always
|
||||
livenessProbe:
|
||||
failureThreshold: 3
|
||||
@@ -2738,7 +2738,7 @@ spec:
|
||||
- -n
|
||||
- /usr/local/bin/argocd
|
||||
- /var/run/argocd/argocd-cmp-server
|
||||
image: quay.io/argoproj/argocd:v3.0.19
|
||||
image: quay.io/argoproj/argocd:v3.0.23
|
||||
name: copyutil
|
||||
securityContext:
|
||||
allowPrivilegeEscalation: false
|
||||
@@ -3112,7 +3112,7 @@ spec:
|
||||
key: server.sync.replace.allowed
|
||||
name: argocd-cmd-params-cm
|
||||
optional: true
|
||||
image: quay.io/argoproj/argocd:v3.0.19
|
||||
image: quay.io/argoproj/argocd:v3.0.23
|
||||
imagePullPolicy: Always
|
||||
livenessProbe:
|
||||
httpGet:
|
||||
@@ -3490,7 +3490,7 @@ spec:
|
||||
optional: true
|
||||
- name: KUBECACHEDIR
|
||||
value: /tmp/kubecache
|
||||
image: quay.io/argoproj/argocd:v3.0.19
|
||||
image: quay.io/argoproj/argocd:v3.0.23
|
||||
imagePullPolicy: Always
|
||||
name: argocd-application-controller
|
||||
ports:
|
||||
@@ -3588,7 +3588,7 @@ spec:
|
||||
secretKeyRef:
|
||||
key: auth
|
||||
name: argocd-redis
|
||||
image: public.ecr.aws/docker/library/redis:7.2.7-alpine
|
||||
image: public.ecr.aws/docker/library/redis:7.2.11-alpine
|
||||
imagePullPolicy: IfNotPresent
|
||||
lifecycle:
|
||||
preStop:
|
||||
@@ -3659,7 +3659,7 @@ spec:
|
||||
secretKeyRef:
|
||||
key: auth
|
||||
name: argocd-redis
|
||||
image: public.ecr.aws/docker/library/redis:7.2.7-alpine
|
||||
image: public.ecr.aws/docker/library/redis:7.2.11-alpine
|
||||
imagePullPolicy: IfNotPresent
|
||||
lifecycle:
|
||||
postStart:
|
||||
@@ -3734,7 +3734,7 @@ spec:
|
||||
secretKeyRef:
|
||||
key: auth
|
||||
name: argocd-redis
|
||||
image: public.ecr.aws/docker/library/redis:7.2.7-alpine
|
||||
image: public.ecr.aws/docker/library/redis:7.2.11-alpine
|
||||
imagePullPolicy: IfNotPresent
|
||||
name: split-brain-fix
|
||||
resources: {}
|
||||
@@ -3769,7 +3769,7 @@ spec:
|
||||
secretKeyRef:
|
||||
key: auth
|
||||
name: argocd-redis
|
||||
image: public.ecr.aws/docker/library/redis:7.2.7-alpine
|
||||
image: public.ecr.aws/docker/library/redis:7.2.11-alpine
|
||||
imagePullPolicy: IfNotPresent
|
||||
name: config-init
|
||||
securityContext:
|
||||
|
||||
24
manifests/ha/namespace-install.yaml
generated
24
manifests/ha/namespace-install.yaml
generated
@@ -1838,7 +1838,7 @@ spec:
|
||||
key: applicationsetcontroller.status.max.resources.count
|
||||
name: argocd-cmd-params-cm
|
||||
optional: true
|
||||
image: quay.io/argoproj/argocd:v3.0.19
|
||||
image: quay.io/argoproj/argocd:v3.0.23
|
||||
imagePullPolicy: Always
|
||||
name: argocd-applicationset-controller
|
||||
ports:
|
||||
@@ -1981,7 +1981,7 @@ spec:
|
||||
- -n
|
||||
- /usr/local/bin/argocd
|
||||
- /shared/argocd-dex
|
||||
image: quay.io/argoproj/argocd:v3.0.19
|
||||
image: quay.io/argoproj/argocd:v3.0.23
|
||||
imagePullPolicy: Always
|
||||
name: copyutil
|
||||
securityContext:
|
||||
@@ -2077,7 +2077,7 @@ spec:
|
||||
key: notificationscontroller.repo.server.plaintext
|
||||
name: argocd-cmd-params-cm
|
||||
optional: true
|
||||
image: quay.io/argoproj/argocd:v3.0.19
|
||||
image: quay.io/argoproj/argocd:v3.0.23
|
||||
imagePullPolicy: Always
|
||||
livenessProbe:
|
||||
tcpSocket:
|
||||
@@ -2201,7 +2201,7 @@ spec:
|
||||
- argocd
|
||||
- admin
|
||||
- redis-initial-password
|
||||
image: quay.io/argoproj/argocd:v3.0.19
|
||||
image: quay.io/argoproj/argocd:v3.0.23
|
||||
imagePullPolicy: IfNotPresent
|
||||
name: secret-init
|
||||
securityContext:
|
||||
@@ -2500,7 +2500,7 @@ spec:
|
||||
value: /helm-working-dir
|
||||
- name: HELM_DATA_HOME
|
||||
value: /helm-working-dir
|
||||
image: quay.io/argoproj/argocd:v3.0.19
|
||||
image: quay.io/argoproj/argocd:v3.0.23
|
||||
imagePullPolicy: Always
|
||||
livenessProbe:
|
||||
failureThreshold: 3
|
||||
@@ -2552,7 +2552,7 @@ spec:
|
||||
- -n
|
||||
- /usr/local/bin/argocd
|
||||
- /var/run/argocd/argocd-cmp-server
|
||||
image: quay.io/argoproj/argocd:v3.0.19
|
||||
image: quay.io/argoproj/argocd:v3.0.23
|
||||
name: copyutil
|
||||
securityContext:
|
||||
allowPrivilegeEscalation: false
|
||||
@@ -2926,7 +2926,7 @@ spec:
|
||||
key: server.sync.replace.allowed
|
||||
name: argocd-cmd-params-cm
|
||||
optional: true
|
||||
image: quay.io/argoproj/argocd:v3.0.19
|
||||
image: quay.io/argoproj/argocd:v3.0.23
|
||||
imagePullPolicy: Always
|
||||
livenessProbe:
|
||||
httpGet:
|
||||
@@ -3304,7 +3304,7 @@ spec:
|
||||
optional: true
|
||||
- name: KUBECACHEDIR
|
||||
value: /tmp/kubecache
|
||||
image: quay.io/argoproj/argocd:v3.0.19
|
||||
image: quay.io/argoproj/argocd:v3.0.23
|
||||
imagePullPolicy: Always
|
||||
name: argocd-application-controller
|
||||
ports:
|
||||
@@ -3402,7 +3402,7 @@ spec:
|
||||
secretKeyRef:
|
||||
key: auth
|
||||
name: argocd-redis
|
||||
image: public.ecr.aws/docker/library/redis:7.2.7-alpine
|
||||
image: public.ecr.aws/docker/library/redis:7.2.11-alpine
|
||||
imagePullPolicy: IfNotPresent
|
||||
lifecycle:
|
||||
preStop:
|
||||
@@ -3473,7 +3473,7 @@ spec:
|
||||
secretKeyRef:
|
||||
key: auth
|
||||
name: argocd-redis
|
||||
image: public.ecr.aws/docker/library/redis:7.2.7-alpine
|
||||
image: public.ecr.aws/docker/library/redis:7.2.11-alpine
|
||||
imagePullPolicy: IfNotPresent
|
||||
lifecycle:
|
||||
postStart:
|
||||
@@ -3548,7 +3548,7 @@ spec:
|
||||
secretKeyRef:
|
||||
key: auth
|
||||
name: argocd-redis
|
||||
image: public.ecr.aws/docker/library/redis:7.2.7-alpine
|
||||
image: public.ecr.aws/docker/library/redis:7.2.11-alpine
|
||||
imagePullPolicy: IfNotPresent
|
||||
name: split-brain-fix
|
||||
resources: {}
|
||||
@@ -3583,7 +3583,7 @@ spec:
|
||||
secretKeyRef:
|
||||
key: auth
|
||||
name: argocd-redis
|
||||
image: public.ecr.aws/docker/library/redis:7.2.7-alpine
|
||||
image: public.ecr.aws/docker/library/redis:7.2.11-alpine
|
||||
imagePullPolicy: IfNotPresent
|
||||
name: config-init
|
||||
securityContext:
|
||||
|
||||
22
manifests/install-with-hydrator.yaml
generated
22
manifests/install-with-hydrator.yaml
generated
@@ -25075,7 +25075,7 @@ spec:
|
||||
key: applicationsetcontroller.status.max.resources.count
|
||||
name: argocd-cmd-params-cm
|
||||
optional: true
|
||||
image: quay.io/argoproj/argocd:v3.0.19
|
||||
image: quay.io/argoproj/argocd:v3.0.23
|
||||
imagePullPolicy: Always
|
||||
name: argocd-applicationset-controller
|
||||
ports:
|
||||
@@ -25201,7 +25201,7 @@ spec:
|
||||
key: log.format.timestamp
|
||||
name: argocd-cmd-params-cm
|
||||
optional: true
|
||||
image: quay.io/argoproj/argocd:v3.0.19
|
||||
image: quay.io/argoproj/argocd:v3.0.23
|
||||
imagePullPolicy: Always
|
||||
livenessProbe:
|
||||
failureThreshold: 3
|
||||
@@ -25247,7 +25247,7 @@ spec:
|
||||
- -n
|
||||
- /usr/local/bin/argocd
|
||||
- /var/run/argocd/argocd-cmp-server
|
||||
image: quay.io/argoproj/argocd:v3.0.19
|
||||
image: quay.io/argoproj/argocd:v3.0.23
|
||||
name: copyutil
|
||||
securityContext:
|
||||
allowPrivilegeEscalation: false
|
||||
@@ -25374,7 +25374,7 @@ spec:
|
||||
- -n
|
||||
- /usr/local/bin/argocd
|
||||
- /shared/argocd-dex
|
||||
image: quay.io/argoproj/argocd:v3.0.19
|
||||
image: quay.io/argoproj/argocd:v3.0.23
|
||||
imagePullPolicy: Always
|
||||
name: copyutil
|
||||
securityContext:
|
||||
@@ -25470,7 +25470,7 @@ spec:
|
||||
key: notificationscontroller.repo.server.plaintext
|
||||
name: argocd-cmd-params-cm
|
||||
optional: true
|
||||
image: quay.io/argoproj/argocd:v3.0.19
|
||||
image: quay.io/argoproj/argocd:v3.0.23
|
||||
imagePullPolicy: Always
|
||||
livenessProbe:
|
||||
tcpSocket:
|
||||
@@ -25556,7 +25556,7 @@ spec:
|
||||
secretKeyRef:
|
||||
key: auth
|
||||
name: argocd-redis
|
||||
image: redis:7.2.7-alpine
|
||||
image: redis:7.2.11-alpine
|
||||
imagePullPolicy: Always
|
||||
name: redis
|
||||
ports:
|
||||
@@ -25572,7 +25572,7 @@ spec:
|
||||
- argocd
|
||||
- admin
|
||||
- redis-initial-password
|
||||
image: quay.io/argoproj/argocd:v3.0.19
|
||||
image: quay.io/argoproj/argocd:v3.0.23
|
||||
imagePullPolicy: IfNotPresent
|
||||
name: secret-init
|
||||
securityContext:
|
||||
@@ -25845,7 +25845,7 @@ spec:
|
||||
value: /helm-working-dir
|
||||
- name: HELM_DATA_HOME
|
||||
value: /helm-working-dir
|
||||
image: quay.io/argoproj/argocd:v3.0.19
|
||||
image: quay.io/argoproj/argocd:v3.0.23
|
||||
imagePullPolicy: Always
|
||||
livenessProbe:
|
||||
failureThreshold: 3
|
||||
@@ -25897,7 +25897,7 @@ spec:
|
||||
- -n
|
||||
- /usr/local/bin/argocd
|
||||
- /var/run/argocd/argocd-cmp-server
|
||||
image: quay.io/argoproj/argocd:v3.0.19
|
||||
image: quay.io/argoproj/argocd:v3.0.23
|
||||
name: copyutil
|
||||
securityContext:
|
||||
allowPrivilegeEscalation: false
|
||||
@@ -26269,7 +26269,7 @@ spec:
|
||||
key: server.sync.replace.allowed
|
||||
name: argocd-cmd-params-cm
|
||||
optional: true
|
||||
image: quay.io/argoproj/argocd:v3.0.19
|
||||
image: quay.io/argoproj/argocd:v3.0.23
|
||||
imagePullPolicy: Always
|
||||
livenessProbe:
|
||||
httpGet:
|
||||
@@ -26647,7 +26647,7 @@ spec:
|
||||
optional: true
|
||||
- name: KUBECACHEDIR
|
||||
value: /tmp/kubecache
|
||||
image: quay.io/argoproj/argocd:v3.0.19
|
||||
image: quay.io/argoproj/argocd:v3.0.23
|
||||
imagePullPolicy: Always
|
||||
name: argocd-application-controller
|
||||
ports:
|
||||
|
||||
18
manifests/install.yaml
generated
18
manifests/install.yaml
generated
@@ -25043,7 +25043,7 @@ spec:
|
||||
key: applicationsetcontroller.status.max.resources.count
|
||||
name: argocd-cmd-params-cm
|
||||
optional: true
|
||||
image: quay.io/argoproj/argocd:v3.0.19
|
||||
image: quay.io/argoproj/argocd:v3.0.23
|
||||
imagePullPolicy: Always
|
||||
name: argocd-applicationset-controller
|
||||
ports:
|
||||
@@ -25186,7 +25186,7 @@ spec:
|
||||
- -n
|
||||
- /usr/local/bin/argocd
|
||||
- /shared/argocd-dex
|
||||
image: quay.io/argoproj/argocd:v3.0.19
|
||||
image: quay.io/argoproj/argocd:v3.0.23
|
||||
imagePullPolicy: Always
|
||||
name: copyutil
|
||||
securityContext:
|
||||
@@ -25282,7 +25282,7 @@ spec:
|
||||
key: notificationscontroller.repo.server.plaintext
|
||||
name: argocd-cmd-params-cm
|
||||
optional: true
|
||||
image: quay.io/argoproj/argocd:v3.0.19
|
||||
image: quay.io/argoproj/argocd:v3.0.23
|
||||
imagePullPolicy: Always
|
||||
livenessProbe:
|
||||
tcpSocket:
|
||||
@@ -25368,7 +25368,7 @@ spec:
|
||||
secretKeyRef:
|
||||
key: auth
|
||||
name: argocd-redis
|
||||
image: redis:7.2.7-alpine
|
||||
image: redis:7.2.11-alpine
|
||||
imagePullPolicy: Always
|
||||
name: redis
|
||||
ports:
|
||||
@@ -25384,7 +25384,7 @@ spec:
|
||||
- argocd
|
||||
- admin
|
||||
- redis-initial-password
|
||||
image: quay.io/argoproj/argocd:v3.0.19
|
||||
image: quay.io/argoproj/argocd:v3.0.23
|
||||
imagePullPolicy: IfNotPresent
|
||||
name: secret-init
|
||||
securityContext:
|
||||
@@ -25657,7 +25657,7 @@ spec:
|
||||
value: /helm-working-dir
|
||||
- name: HELM_DATA_HOME
|
||||
value: /helm-working-dir
|
||||
image: quay.io/argoproj/argocd:v3.0.19
|
||||
image: quay.io/argoproj/argocd:v3.0.23
|
||||
imagePullPolicy: Always
|
||||
livenessProbe:
|
||||
failureThreshold: 3
|
||||
@@ -25709,7 +25709,7 @@ spec:
|
||||
- -n
|
||||
- /usr/local/bin/argocd
|
||||
- /var/run/argocd/argocd-cmp-server
|
||||
image: quay.io/argoproj/argocd:v3.0.19
|
||||
image: quay.io/argoproj/argocd:v3.0.23
|
||||
name: copyutil
|
||||
securityContext:
|
||||
allowPrivilegeEscalation: false
|
||||
@@ -26081,7 +26081,7 @@ spec:
|
||||
key: server.sync.replace.allowed
|
||||
name: argocd-cmd-params-cm
|
||||
optional: true
|
||||
image: quay.io/argoproj/argocd:v3.0.19
|
||||
image: quay.io/argoproj/argocd:v3.0.23
|
||||
imagePullPolicy: Always
|
||||
livenessProbe:
|
||||
httpGet:
|
||||
@@ -26459,7 +26459,7 @@ spec:
|
||||
optional: true
|
||||
- name: KUBECACHEDIR
|
||||
value: /tmp/kubecache
|
||||
image: quay.io/argoproj/argocd:v3.0.19
|
||||
image: quay.io/argoproj/argocd:v3.0.23
|
||||
imagePullPolicy: Always
|
||||
name: argocd-application-controller
|
||||
ports:
|
||||
|
||||
22
manifests/namespace-install-with-hydrator.yaml
generated
22
manifests/namespace-install-with-hydrator.yaml
generated
@@ -962,7 +962,7 @@ spec:
|
||||
key: applicationsetcontroller.status.max.resources.count
|
||||
name: argocd-cmd-params-cm
|
||||
optional: true
|
||||
image: quay.io/argoproj/argocd:v3.0.19
|
||||
image: quay.io/argoproj/argocd:v3.0.23
|
||||
imagePullPolicy: Always
|
||||
name: argocd-applicationset-controller
|
||||
ports:
|
||||
@@ -1088,7 +1088,7 @@ spec:
|
||||
key: log.format.timestamp
|
||||
name: argocd-cmd-params-cm
|
||||
optional: true
|
||||
image: quay.io/argoproj/argocd:v3.0.19
|
||||
image: quay.io/argoproj/argocd:v3.0.23
|
||||
imagePullPolicy: Always
|
||||
livenessProbe:
|
||||
failureThreshold: 3
|
||||
@@ -1134,7 +1134,7 @@ spec:
|
||||
- -n
|
||||
- /usr/local/bin/argocd
|
||||
- /var/run/argocd/argocd-cmp-server
|
||||
image: quay.io/argoproj/argocd:v3.0.19
|
||||
image: quay.io/argoproj/argocd:v3.0.23
|
||||
name: copyutil
|
||||
securityContext:
|
||||
allowPrivilegeEscalation: false
|
||||
@@ -1261,7 +1261,7 @@ spec:
|
||||
- -n
|
||||
- /usr/local/bin/argocd
|
||||
- /shared/argocd-dex
|
||||
image: quay.io/argoproj/argocd:v3.0.19
|
||||
image: quay.io/argoproj/argocd:v3.0.23
|
||||
imagePullPolicy: Always
|
||||
name: copyutil
|
||||
securityContext:
|
||||
@@ -1357,7 +1357,7 @@ spec:
|
||||
key: notificationscontroller.repo.server.plaintext
|
||||
name: argocd-cmd-params-cm
|
||||
optional: true
|
||||
image: quay.io/argoproj/argocd:v3.0.19
|
||||
image: quay.io/argoproj/argocd:v3.0.23
|
||||
imagePullPolicy: Always
|
||||
livenessProbe:
|
||||
tcpSocket:
|
||||
@@ -1443,7 +1443,7 @@ spec:
|
||||
secretKeyRef:
|
||||
key: auth
|
||||
name: argocd-redis
|
||||
image: redis:7.2.7-alpine
|
||||
image: redis:7.2.11-alpine
|
||||
imagePullPolicy: Always
|
||||
name: redis
|
||||
ports:
|
||||
@@ -1459,7 +1459,7 @@ spec:
|
||||
- argocd
|
||||
- admin
|
||||
- redis-initial-password
|
||||
image: quay.io/argoproj/argocd:v3.0.19
|
||||
image: quay.io/argoproj/argocd:v3.0.23
|
||||
imagePullPolicy: IfNotPresent
|
||||
name: secret-init
|
||||
securityContext:
|
||||
@@ -1732,7 +1732,7 @@ spec:
|
||||
value: /helm-working-dir
|
||||
- name: HELM_DATA_HOME
|
||||
value: /helm-working-dir
|
||||
image: quay.io/argoproj/argocd:v3.0.19
|
||||
image: quay.io/argoproj/argocd:v3.0.23
|
||||
imagePullPolicy: Always
|
||||
livenessProbe:
|
||||
failureThreshold: 3
|
||||
@@ -1784,7 +1784,7 @@ spec:
|
||||
- -n
|
||||
- /usr/local/bin/argocd
|
||||
- /var/run/argocd/argocd-cmp-server
|
||||
image: quay.io/argoproj/argocd:v3.0.19
|
||||
image: quay.io/argoproj/argocd:v3.0.23
|
||||
name: copyutil
|
||||
securityContext:
|
||||
allowPrivilegeEscalation: false
|
||||
@@ -2156,7 +2156,7 @@ spec:
|
||||
key: server.sync.replace.allowed
|
||||
name: argocd-cmd-params-cm
|
||||
optional: true
|
||||
image: quay.io/argoproj/argocd:v3.0.19
|
||||
image: quay.io/argoproj/argocd:v3.0.23
|
||||
imagePullPolicy: Always
|
||||
livenessProbe:
|
||||
httpGet:
|
||||
@@ -2534,7 +2534,7 @@ spec:
|
||||
optional: true
|
||||
- name: KUBECACHEDIR
|
||||
value: /tmp/kubecache
|
||||
image: quay.io/argoproj/argocd:v3.0.19
|
||||
image: quay.io/argoproj/argocd:v3.0.23
|
||||
imagePullPolicy: Always
|
||||
name: argocd-application-controller
|
||||
ports:
|
||||
|
||||
18
manifests/namespace-install.yaml
generated
18
manifests/namespace-install.yaml
generated
@@ -930,7 +930,7 @@ spec:
|
||||
key: applicationsetcontroller.status.max.resources.count
|
||||
name: argocd-cmd-params-cm
|
||||
optional: true
|
||||
image: quay.io/argoproj/argocd:v3.0.19
|
||||
image: quay.io/argoproj/argocd:v3.0.23
|
||||
imagePullPolicy: Always
|
||||
name: argocd-applicationset-controller
|
||||
ports:
|
||||
@@ -1073,7 +1073,7 @@ spec:
|
||||
- -n
|
||||
- /usr/local/bin/argocd
|
||||
- /shared/argocd-dex
|
||||
image: quay.io/argoproj/argocd:v3.0.19
|
||||
image: quay.io/argoproj/argocd:v3.0.23
|
||||
imagePullPolicy: Always
|
||||
name: copyutil
|
||||
securityContext:
|
||||
@@ -1169,7 +1169,7 @@ spec:
|
||||
key: notificationscontroller.repo.server.plaintext
|
||||
name: argocd-cmd-params-cm
|
||||
optional: true
|
||||
image: quay.io/argoproj/argocd:v3.0.19
|
||||
image: quay.io/argoproj/argocd:v3.0.23
|
||||
imagePullPolicy: Always
|
||||
livenessProbe:
|
||||
tcpSocket:
|
||||
@@ -1255,7 +1255,7 @@ spec:
|
||||
secretKeyRef:
|
||||
key: auth
|
||||
name: argocd-redis
|
||||
image: redis:7.2.7-alpine
|
||||
image: redis:7.2.11-alpine
|
||||
imagePullPolicy: Always
|
||||
name: redis
|
||||
ports:
|
||||
@@ -1271,7 +1271,7 @@ spec:
|
||||
- argocd
|
||||
- admin
|
||||
- redis-initial-password
|
||||
image: quay.io/argoproj/argocd:v3.0.19
|
||||
image: quay.io/argoproj/argocd:v3.0.23
|
||||
imagePullPolicy: IfNotPresent
|
||||
name: secret-init
|
||||
securityContext:
|
||||
@@ -1544,7 +1544,7 @@ spec:
|
||||
value: /helm-working-dir
|
||||
- name: HELM_DATA_HOME
|
||||
value: /helm-working-dir
|
||||
image: quay.io/argoproj/argocd:v3.0.19
|
||||
image: quay.io/argoproj/argocd:v3.0.23
|
||||
imagePullPolicy: Always
|
||||
livenessProbe:
|
||||
failureThreshold: 3
|
||||
@@ -1596,7 +1596,7 @@ spec:
|
||||
- -n
|
||||
- /usr/local/bin/argocd
|
||||
- /var/run/argocd/argocd-cmp-server
|
||||
image: quay.io/argoproj/argocd:v3.0.19
|
||||
image: quay.io/argoproj/argocd:v3.0.23
|
||||
name: copyutil
|
||||
securityContext:
|
||||
allowPrivilegeEscalation: false
|
||||
@@ -1968,7 +1968,7 @@ spec:
|
||||
key: server.sync.replace.allowed
|
||||
name: argocd-cmd-params-cm
|
||||
optional: true
|
||||
image: quay.io/argoproj/argocd:v3.0.19
|
||||
image: quay.io/argoproj/argocd:v3.0.23
|
||||
imagePullPolicy: Always
|
||||
livenessProbe:
|
||||
httpGet:
|
||||
@@ -2346,7 +2346,7 @@ spec:
|
||||
optional: true
|
||||
- name: KUBECACHEDIR
|
||||
value: /tmp/kubecache
|
||||
image: quay.io/argoproj/argocd:v3.0.19
|
||||
image: quay.io/argoproj/argocd:v3.0.23
|
||||
imagePullPolicy: Always
|
||||
name: argocd-application-controller
|
||||
ports:
|
||||
|
||||
@@ -24,14 +24,23 @@ if obj.status ~= nil then
|
||||
|
||||
if obj.status.conditions ~= nil then
|
||||
for i, condition in pairs(obj.status.conditions) do
|
||||
-- Check if the InferenceService is Stopped
|
||||
if condition.type == "Stopped" and condition.status == "True" then
|
||||
health_status.status = "Suspended"
|
||||
health_status.message = "InferenceService is Stopped"
|
||||
return health_status
|
||||
end
|
||||
|
||||
-- Check for unhealthy statuses
|
||||
-- Note: The Stopped condition's healthy status is False
|
||||
if condition.status == "Unknown" then
|
||||
status_unknown = status_unknown + 1
|
||||
elseif condition.status == "False" then
|
||||
elseif condition.status == "False" and condition.type ~= "Stopped" then
|
||||
status_false = status_false + 1
|
||||
end
|
||||
|
||||
if condition.status ~= "True" then
|
||||
-- Add the error messages if the status is unhealthy
|
||||
if condition.status ~= "True" and condition.type ~= "Stopped" then
|
||||
msg = msg .. " | " .. i .. ": " .. condition.type .. " | " .. condition.status
|
||||
if condition.reason ~= nil and condition.reason ~= "" then
|
||||
msg = msg .. " | " .. condition.reason
|
||||
|
||||
@@ -23,6 +23,10 @@ tests:
|
||||
status: Degraded
|
||||
message: "0: transitionStatus | BlockedByFailedLoad"
|
||||
inputPath: testdata/degraded_modelmesh.yaml
|
||||
- healthStatus:
|
||||
status: Suspended
|
||||
message: InferenceService is Stopped
|
||||
inputPath: testdata/stopped.yaml
|
||||
- healthStatus:
|
||||
status: Healthy
|
||||
message: InferenceService is healthy.
|
||||
|
||||
@@ -23,3 +23,7 @@ status:
|
||||
- lastTransitionTime: "2023-06-20T22:44:51Z"
|
||||
status: "True"
|
||||
type: Ready
|
||||
- lastTransitionTime: "2023-06-20T22:44:51Z"
|
||||
severity: Info
|
||||
status: 'False'
|
||||
type: Stopped
|
||||
|
||||
@@ -31,5 +31,9 @@ status:
|
||||
severity: Info
|
||||
status: 'True'
|
||||
type: RoutesReady
|
||||
- lastTransitionTime: '2024-05-30T22:14:31Z'
|
||||
severity: Info
|
||||
status: 'False'
|
||||
type: Stopped
|
||||
modelStatus:
|
||||
transitionStatus: UpToDate
|
||||
|
||||
@@ -17,3 +17,7 @@ status:
|
||||
- lastTransitionTime: '2024-05-16T18:48:56Z'
|
||||
status: 'True'
|
||||
type: Ready
|
||||
- lastTransitionTime: '2024-05-16T18:48:56Z'
|
||||
severity: Info
|
||||
status: 'False'
|
||||
type: Stopped
|
||||
|
||||
23
resource_customizations/serving.kserve.io/InferenceService/testdata/stopped.yaml
vendored
Normal file
23
resource_customizations/serving.kserve.io/InferenceService/testdata/stopped.yaml
vendored
Normal file
@@ -0,0 +1,23 @@
|
||||
apiVersion: serving.kserve.io/v1beta1
|
||||
kind: InferenceService
|
||||
metadata:
|
||||
name: helloworld
|
||||
namespace: default
|
||||
annotations:
|
||||
serving.kserve.io/deploymentMode: RawDeployment
|
||||
serving.kserve.io/stop: 'true'
|
||||
spec: {}
|
||||
status:
|
||||
conditions:
|
||||
- lastTransitionTime: '2024-05-16T18:48:56Z'
|
||||
reason: Stopped
|
||||
status: 'False'
|
||||
type: PredictorReady
|
||||
- lastTransitionTime: '2024-05-16T18:48:56Z'
|
||||
reason: Stopped
|
||||
status: 'False'
|
||||
type: Ready
|
||||
- lastTransitionTime: '2024-05-16T18:48:56Z'
|
||||
severity: Info
|
||||
status: 'True'
|
||||
type: Stopped
|
||||
@@ -744,9 +744,8 @@ func (s *Server) Get(ctx context.Context, q *application.ApplicationQuery) (*v1a
|
||||
return nil, err
|
||||
}
|
||||
|
||||
s.inferResourcesStatusHealth(a)
|
||||
|
||||
if q.Refresh == nil {
|
||||
s.inferResourcesStatusHealth(a)
|
||||
return a, nil
|
||||
}
|
||||
|
||||
@@ -827,7 +826,9 @@ func (s *Server) Get(ctx context.Context, q *application.ApplicationQuery) (*v1a
|
||||
annotations = make(map[string]string)
|
||||
}
|
||||
if _, ok := annotations[v1alpha1.AnnotationKeyRefresh]; !ok {
|
||||
return event.Application.DeepCopy(), nil
|
||||
refreshedApp := event.Application.DeepCopy()
|
||||
s.inferResourcesStatusHealth(refreshedApp)
|
||||
return refreshedApp, nil
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2527,6 +2527,99 @@ func TestGetAppRefresh_HardRefresh(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestGetApp_HealthStatusPropagation(t *testing.T) {
|
||||
newServerWithTree := func(t *testing.T) (*Server, *v1alpha1.Application) {
|
||||
t.Helper()
|
||||
cacheClient := cache.NewCache(cache.NewInMemoryCache(1 * time.Hour))
|
||||
|
||||
testApp := newTestApp()
|
||||
testApp.Status.ResourceHealthSource = v1alpha1.ResourceHealthLocationAppTree
|
||||
testApp.Status.Resources = []v1alpha1.ResourceStatus{
|
||||
{
|
||||
Group: "apps",
|
||||
Kind: "Deployment",
|
||||
Name: "guestbook",
|
||||
Namespace: "default",
|
||||
},
|
||||
}
|
||||
|
||||
appServer := newTestAppServer(t, testApp)
|
||||
|
||||
appStateCache := appstate.NewCache(cacheClient, time.Minute)
|
||||
appInstanceName := testApp.InstanceName(appServer.appNamespaceOrDefault(testApp.Namespace))
|
||||
err := appStateCache.SetAppResourcesTree(appInstanceName, &v1alpha1.ApplicationTree{
|
||||
Nodes: []v1alpha1.ResourceNode{{
|
||||
ResourceRef: v1alpha1.ResourceRef{
|
||||
Group: "apps",
|
||||
Kind: "Deployment",
|
||||
Name: "guestbook",
|
||||
Namespace: "default",
|
||||
},
|
||||
Health: &v1alpha1.HealthStatus{Status: health.HealthStatusDegraded},
|
||||
}},
|
||||
})
|
||||
require.NoError(t, err)
|
||||
appServer.cache = servercache.NewCache(appStateCache, time.Minute, time.Minute, time.Minute)
|
||||
|
||||
return appServer, testApp
|
||||
}
|
||||
|
||||
t.Run("propagated health status on get with no refresh", func(t *testing.T) {
|
||||
appServer, testApp := newServerWithTree(t)
|
||||
fetchedApp, err := appServer.Get(t.Context(), &application.ApplicationQuery{
|
||||
Name: &testApp.Name,
|
||||
})
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, health.HealthStatusDegraded, fetchedApp.Status.Resources[0].Health.Status)
|
||||
})
|
||||
|
||||
t.Run("propagated health status on normal refresh", func(t *testing.T) {
|
||||
appServer, testApp := newServerWithTree(t)
|
||||
var patched int32
|
||||
ch := make(chan string, 1)
|
||||
ctx, cancel := context.WithCancel(t.Context())
|
||||
defer cancel()
|
||||
go refreshAnnotationRemover(t, ctx, &patched, appServer, testApp.Name, ch)
|
||||
|
||||
fetchedApp, err := appServer.Get(t.Context(), &application.ApplicationQuery{
|
||||
Name: &testApp.Name,
|
||||
Refresh: ptr.To(string(v1alpha1.RefreshTypeNormal)),
|
||||
})
|
||||
require.NoError(t, err)
|
||||
|
||||
select {
|
||||
case <-ch:
|
||||
assert.Equal(t, int32(1), atomic.LoadInt32(&patched))
|
||||
case <-time.After(10 * time.Second):
|
||||
assert.Fail(t, "Out of time ( 10 seconds )")
|
||||
}
|
||||
assert.Equal(t, health.HealthStatusDegraded, fetchedApp.Status.Resources[0].Health.Status)
|
||||
})
|
||||
|
||||
t.Run("propagated health status on hard refresh", func(t *testing.T) {
|
||||
appServer, testApp := newServerWithTree(t)
|
||||
var patched int32
|
||||
ch := make(chan string, 1)
|
||||
ctx, cancel := context.WithCancel(t.Context())
|
||||
defer cancel()
|
||||
go refreshAnnotationRemover(t, ctx, &patched, appServer, testApp.Name, ch)
|
||||
|
||||
fetchedApp, err := appServer.Get(t.Context(), &application.ApplicationQuery{
|
||||
Name: &testApp.Name,
|
||||
Refresh: ptr.To(string(v1alpha1.RefreshTypeHard)),
|
||||
})
|
||||
require.NoError(t, err)
|
||||
|
||||
select {
|
||||
case <-ch:
|
||||
assert.Equal(t, int32(1), atomic.LoadInt32(&patched))
|
||||
case <-time.After(10 * time.Second):
|
||||
assert.Fail(t, "Out of time ( 10 seconds )")
|
||||
}
|
||||
assert.Equal(t, health.HealthStatusDegraded, fetchedApp.Status.Resources[0].Health.Status)
|
||||
})
|
||||
}
|
||||
|
||||
func TestInferResourcesStatusHealth(t *testing.T) {
|
||||
cacheClient := cache.NewCache(cache.NewInMemoryCache(1 * time.Hour))
|
||||
|
||||
|
||||
@@ -6,7 +6,6 @@ import (
|
||||
"os"
|
||||
"slices"
|
||||
"strconv"
|
||||
"time"
|
||||
|
||||
rbacv1 "k8s.io/api/rbac/v1"
|
||||
|
||||
@@ -83,6 +82,18 @@ func (a *Actions) AddTag(name string) *Actions {
|
||||
return a
|
||||
}
|
||||
|
||||
func (a *Actions) AddAnnotatedTag(name string, message string) *Actions {
|
||||
a.context.t.Helper()
|
||||
fixture.AddAnnotatedTag(a.context.t, name, message)
|
||||
return a
|
||||
}
|
||||
|
||||
func (a *Actions) AddTagWithForce(name string) *Actions {
|
||||
a.context.t.Helper()
|
||||
fixture.AddTagWithForce(a.context.t, name)
|
||||
return a
|
||||
}
|
||||
|
||||
func (a *Actions) RemoveSubmodule() *Actions {
|
||||
a.context.t.Helper()
|
||||
fixture.RemoveSubmodule(a.context.t)
|
||||
@@ -493,7 +504,6 @@ func (a *Actions) And(block func()) *Actions {
|
||||
|
||||
func (a *Actions) Then() *Consequences {
|
||||
a.context.t.Helper()
|
||||
time.Sleep(fixture.WhenThenSleepInterval)
|
||||
return &Consequences{a.context, a, 15}
|
||||
}
|
||||
|
||||
|
||||
@@ -1145,6 +1145,25 @@ 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")
|
||||
defer t.Setenv("GNUPGHOME", prevGnuPGHome)
|
||||
errors.NewHandler(t).FailOnErr(Run(repoDirectory(), "git", "tag", "-f", name))
|
||||
if IsRemote() {
|
||||
errors.NewHandler(t).FailOnErr(Run(repoDirectory(), "git", "push", "--tags", "-f", "origin", "master"))
|
||||
}
|
||||
}
|
||||
|
||||
func AddAnnotatedTag(t *testing.T, name string, message string) {
|
||||
t.Helper()
|
||||
errors.NewHandler(t).FailOnErr(Run(repoDirectory(), "git", "tag", "-f", "-a", name, "-m", message))
|
||||
if IsRemote() {
|
||||
errors.NewHandler(t).FailOnErr(Run(repoDirectory(), "git", "push", "--tags", "-f", "origin", "master"))
|
||||
}
|
||||
}
|
||||
|
||||
// create the resource by creating using "kubectl apply", with bonus templating
|
||||
func Declarative(t *testing.T, filename string, values any) (string, error) {
|
||||
t.Helper()
|
||||
|
||||
@@ -1,14 +1,22 @@
|
||||
package e2e
|
||||
|
||||
import (
|
||||
"context"
|
||||
"strings"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
corev1 "k8s.io/api/core/v1"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/apimachinery/pkg/types"
|
||||
"k8s.io/apimachinery/pkg/util/wait"
|
||||
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
. "github.com/argoproj/argo-cd/v3/pkg/apis/application/v1alpha1"
|
||||
"github.com/argoproj/argo-cd/v3/test/e2e/fixture"
|
||||
. "github.com/argoproj/argo-cd/v3/test/e2e/fixture/app"
|
||||
"github.com/argoproj/argo-cd/v3/util/errors"
|
||||
)
|
||||
|
||||
func TestGitSemverResolutionNotUsingConstraint(t *testing.T) {
|
||||
@@ -90,3 +98,145 @@ func TestGitSemverResolutionUsingConstraintWithLeadingZero(t *testing.T) {
|
||||
Expect(SyncStatusIs(SyncStatusCodeSynced)).
|
||||
Expect(Pod(func(p corev1.Pod) bool { return strings.HasPrefix(p.Name, "new-app") }))
|
||||
}
|
||||
|
||||
func TestAnnotatedTagInStatusSyncRevision(t *testing.T) {
|
||||
Given(t).
|
||||
Path(guestbookPath).
|
||||
When().
|
||||
// Create annotated tag name 'annotated-tag'
|
||||
AddAnnotatedTag("annotated-tag", "my-generic-tag-message").
|
||||
// Create Application targeting annotated-tag, with automatedSync: true
|
||||
CreateFromFile(func(app *Application) {
|
||||
app.Spec.Source.TargetRevision = "annotated-tag"
|
||||
app.Spec.SyncPolicy = &SyncPolicy{Automated: &SyncPolicyAutomated{Prune: true, SelfHeal: false}}
|
||||
}).
|
||||
Then().
|
||||
Expect(SyncStatusIs(SyncStatusCodeSynced)).
|
||||
And(func(app *Application) {
|
||||
annotatedTagIDOutput, err := fixture.Run(fixture.TmpDir+"/testdata.git", "git", "show-ref", "annotated-tag")
|
||||
require.NoError(t, err)
|
||||
require.NotEmpty(t, annotatedTagIDOutput)
|
||||
// example command output:
|
||||
// "569798c430515ffe170bdb23e3aafaf8ae24b9ff refs/tags/annotated-tag"
|
||||
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}")
|
||||
// example command output:
|
||||
// "bcd35965e494273355265b9f0bf85075b6bc5163"
|
||||
require.NoError(t, err)
|
||||
require.NotEmpty(t, targetCommitID)
|
||||
|
||||
require.NotEmpty(t, app.Status.Sync.Revision, "revision in sync status should be set by sync operation")
|
||||
|
||||
require.NotEqual(t, app.Status.Sync.Revision, annotatedTagIDFields[0], "revision should not match the annotated tag id")
|
||||
require.Equal(t, app.Status.Sync.Revision, strings.TrimSpace(string(targetCommitID)), "revision SHOULD match the target commit SHA")
|
||||
})
|
||||
}
|
||||
|
||||
// Test updates to K8s resources should not trigger a self-heal when self-heal is false.
|
||||
func TestAutomatedSelfHealingAgainstAnnotatedTag(t *testing.T) {
|
||||
Given(t).
|
||||
Path(guestbookPath).
|
||||
When().
|
||||
AddAnnotatedTag("annotated-tag", "my-generic-tag-message").
|
||||
// App should be auto-synced once created
|
||||
CreateFromFile(func(app *Application) {
|
||||
app.Spec.Source.TargetRevision = "annotated-tag"
|
||||
app.Spec.SyncPolicy = &SyncPolicy{Automated: &SyncPolicyAutomated{Prune: true, SelfHeal: false}}
|
||||
}).
|
||||
Then().
|
||||
Expect(SyncStatusIs(SyncStatusCodeSynced)).
|
||||
ExpectConsistently(SyncStatusIs(SyncStatusCodeSynced), WaitDuration, time.Second*10).
|
||||
When().
|
||||
// Update the annotated tag to a new git commit, that has a new revisionHistoryLimit.
|
||||
PatchFile("guestbook-ui-deployment.yaml", `[{"op": "replace", "path": "/spec/revisionHistoryLimit", "value": 10}]`).
|
||||
AddAnnotatedTag("annotated-tag", "my-generic-tag-message").
|
||||
Refresh(RefreshTypeHard).
|
||||
// The Application should update to the new annotated tag value within 10 seconds.
|
||||
And(func() {
|
||||
// Deployment revisionHistoryLimit should switch to 10
|
||||
timeoutErr := wait.PollUntilContextTimeout(t.Context(), 1*time.Second, 10*time.Second, true, func(context.Context) (done bool, err error) {
|
||||
deployment, err := fixture.KubeClientset.AppsV1().Deployments(fixture.DeploymentNamespace()).Get(t.Context(), "guestbook-ui", metav1.GetOptions{})
|
||||
if err != nil {
|
||||
return false, nil
|
||||
}
|
||||
|
||||
revisionHistoryLimit := deployment.Spec.RevisionHistoryLimit
|
||||
return revisionHistoryLimit != nil && *revisionHistoryLimit == 10, nil
|
||||
})
|
||||
require.NoError(t, timeoutErr)
|
||||
}).
|
||||
// Update the Deployment to a different revisionHistoryLimit
|
||||
And(func() {
|
||||
errors.NewHandler(t).FailOnErr(fixture.KubeClientset.AppsV1().Deployments(fixture.DeploymentNamespace()).Patch(t.Context(),
|
||||
"guestbook-ui", types.MergePatchType, []byte(`{"spec": {"revisionHistoryLimit": 9}}`), metav1.PatchOptions{}))
|
||||
}).
|
||||
// The revisionHistoryLimit should NOT be self-healed, because selfHealing: false. It should remain at 9.
|
||||
And(func() {
|
||||
// Wait up to 10 seconds to ensure that deployment revisionHistoryLimit does NOT should switch to 10, it should remain at 9.
|
||||
waitErr := wait.PollUntilContextTimeout(t.Context(), 1*time.Second, 10*time.Second, true, func(context.Context) (done bool, err error) {
|
||||
deployment, err := fixture.KubeClientset.AppsV1().Deployments(fixture.DeploymentNamespace()).Get(t.Context(), "guestbook-ui", metav1.GetOptions{})
|
||||
if err != nil {
|
||||
return false, nil
|
||||
}
|
||||
|
||||
revisionHistoryLimit := deployment.Spec.RevisionHistoryLimit
|
||||
return revisionHistoryLimit != nil && *revisionHistoryLimit != 9, nil
|
||||
})
|
||||
require.Error(t, waitErr, "A timeout error should occur, indicating that revisionHistoryLimit never changed from 9")
|
||||
})
|
||||
}
|
||||
|
||||
func TestAutomatedSelfHealingAgainstLightweightTag(t *testing.T) {
|
||||
Given(t).
|
||||
Path(guestbookPath).
|
||||
When().
|
||||
AddTag("annotated-tag").
|
||||
// App should be auto-synced once created
|
||||
CreateFromFile(func(app *Application) {
|
||||
app.Spec.Source.TargetRevision = "annotated-tag"
|
||||
app.Spec.SyncPolicy = &SyncPolicy{Automated: &SyncPolicyAutomated{Prune: true, SelfHeal: false}}
|
||||
}).
|
||||
Then().
|
||||
Expect(SyncStatusIs(SyncStatusCodeSynced)).
|
||||
ExpectConsistently(SyncStatusIs(SyncStatusCodeSynced), WaitDuration, time.Second*10).
|
||||
When().
|
||||
// Update the annotated tag to a new git commit, that has a new revisionHistoryLimit.
|
||||
PatchFile("guestbook-ui-deployment.yaml", `[{"op": "replace", "path": "/spec/revisionHistoryLimit", "value": 10}]`).
|
||||
AddTagWithForce("annotated-tag").
|
||||
Refresh(RefreshTypeHard).
|
||||
// The Application should update to the new annotated tag value within 10 seconds.
|
||||
And(func() {
|
||||
// Deployment revisionHistoryLimit should switch to 10
|
||||
timeoutErr := wait.PollUntilContextTimeout(t.Context(), 1*time.Second, 10*time.Second, true, func(context.Context) (done bool, err error) {
|
||||
deployment, err := fixture.KubeClientset.AppsV1().Deployments(fixture.DeploymentNamespace()).Get(t.Context(), "guestbook-ui", metav1.GetOptions{})
|
||||
if err != nil {
|
||||
return false, nil
|
||||
}
|
||||
|
||||
revisionHistoryLimit := deployment.Spec.RevisionHistoryLimit
|
||||
return revisionHistoryLimit != nil && *revisionHistoryLimit == 10, nil
|
||||
})
|
||||
require.NoError(t, timeoutErr)
|
||||
}).
|
||||
// Update the Deployment to a different revisionHistoryLimit
|
||||
And(func() {
|
||||
errors.NewHandler(t).FailOnErr(fixture.KubeClientset.AppsV1().Deployments(fixture.DeploymentNamespace()).Patch(t.Context(),
|
||||
"guestbook-ui", types.MergePatchType, []byte(`{"spec": {"revisionHistoryLimit": 9}}`), metav1.PatchOptions{}))
|
||||
}).
|
||||
// The revisionHistoryLimit should NOT be self-healed, because selfHealing: false
|
||||
And(func() {
|
||||
// Wait up to 10 seconds to ensure that deployment revisionHistoryLimit does NOT should switch to 10, it should remain at 9.
|
||||
waitErr := wait.PollUntilContextTimeout(t.Context(), 1*time.Second, 10*time.Second, true, func(context.Context) (done bool, err error) {
|
||||
deployment, err := fixture.KubeClientset.AppsV1().Deployments(fixture.DeploymentNamespace()).Get(t.Context(), "guestbook-ui", metav1.GetOptions{})
|
||||
if err != nil {
|
||||
return false, nil
|
||||
}
|
||||
|
||||
revisionHistoryLimit := deployment.Spec.RevisionHistoryLimit
|
||||
return revisionHistoryLimit != nil && *revisionHistoryLimit != 9, nil
|
||||
})
|
||||
require.Error(t, waitErr, "A timeout error should occur, indicating that revisionHistoryLimit never changed from 9")
|
||||
})
|
||||
}
|
||||
|
||||
@@ -37,15 +37,17 @@ export const ApplicationHydrateOperationState: React.FunctionComponent<Props> =
|
||||
if (hydrateOperationState.finishedAt && hydrateOperationState.phase !== 'Hydrating') {
|
||||
operationAttributes.push({title: 'FINISHED AT', value: <Timestamp date={hydrateOperationState.finishedAt} />});
|
||||
}
|
||||
operationAttributes.push({
|
||||
title: 'DRY REVISION',
|
||||
value: (
|
||||
<div>
|
||||
<Revision repoUrl={hydrateOperationState.sourceHydrator.drySource.repoURL} revision={hydrateOperationState.drySHA} />
|
||||
</div>
|
||||
)
|
||||
});
|
||||
if (hydrateOperationState.finishedAt) {
|
||||
if (hydrateOperationState.drySHA) {
|
||||
operationAttributes.push({
|
||||
title: 'DRY REVISION',
|
||||
value: (
|
||||
<div>
|
||||
<Revision repoUrl={hydrateOperationState.sourceHydrator.drySource.repoURL} revision={hydrateOperationState.drySHA} />
|
||||
</div>
|
||||
)
|
||||
});
|
||||
}
|
||||
if (hydrateOperationState.finishedAt && hydrateOperationState.hydratedSHA) {
|
||||
operationAttributes.push({
|
||||
title: 'HYDRATED REVISION',
|
||||
value: (
|
||||
|
||||
@@ -275,13 +275,15 @@ export const ApplicationNodeInfo = (props: {
|
||||
Resource not found in cluster:{' '}
|
||||
{`${props?.controlled?.state?.targetState?.apiVersion}/${props?.controlled?.state?.targetState?.kind}:${props.node.name}`}
|
||||
<br />
|
||||
{props?.controlled?.state?.normalizedLiveState?.apiVersion && (
|
||||
<span>
|
||||
Please update your resource specification to use the latest Kubernetes API resources supported by the target cluster. The
|
||||
recommended syntax is{' '}
|
||||
{`${props.controlled.state.normalizedLiveState.apiVersion}/${props?.controlled.state.normalizedLiveState?.kind}:${props.node.name}`}
|
||||
</span>
|
||||
)}
|
||||
{props?.controlled?.state?.normalizedLiveState?.apiVersion &&
|
||||
`${props?.controlled?.state?.targetState?.apiVersion}/${props?.controlled?.state?.targetState?.kind}:${props.node.name}` !==
|
||||
`${props.controlled.state.normalizedLiveState.apiVersion}/${props?.controlled.state.normalizedLiveState?.kind}:${props.node.name}` && (
|
||||
<span>
|
||||
Please update your resource specification to use the latest Kubernetes API resources supported by the target cluster. The
|
||||
recommended syntax is{' '}
|
||||
{`${props.controlled.state.normalizedLiveState.apiVersion}/${props?.controlled.state.normalizedLiveState?.kind}:${props.node.name}`}
|
||||
</span>
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
</React.Fragment>
|
||||
|
||||
@@ -243,7 +243,7 @@ export const ApplicationStatusPanel = ({application, showDiff, showOperation, sh
|
||||
}}>
|
||||
{(data: models.ApplicationSyncWindowState) => (
|
||||
<React.Fragment>
|
||||
{data.assignedWindows && (
|
||||
{data?.assignedWindows && (
|
||||
<div className='application-status-panel__item' style={{position: 'relative'}}>
|
||||
{sectionLabel({
|
||||
title: 'SYNC WINDOWS',
|
||||
|
||||
@@ -95,7 +95,7 @@ export const ApplicationsRefreshPanel = ({show, apps, hide}: {show: boolean; app
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
<ApplicationSelector apps={apps} formApi={formApi} />
|
||||
{show && <ApplicationSelector apps={apps} formApi={formApi} />}
|
||||
</div>
|
||||
</React.Fragment>
|
||||
)}
|
||||
|
||||
@@ -147,7 +147,7 @@ export const ApplicationsSyncPanel = ({show, apps, hide}: {show: boolean; apps:
|
||||
|
||||
<ApplicationRetryOptions id='applications-sync-panel' formApi={formApi} />
|
||||
|
||||
<ApplicationSelector apps={apps} formApi={formApi} />
|
||||
{show && <ApplicationSelector apps={apps} formApi={formApi} />}
|
||||
</div>
|
||||
</React.Fragment>
|
||||
)}
|
||||
|
||||
@@ -1400,13 +1400,13 @@ export const SyncWindowStatusIcon = ({state, window}: {state: appModels.SyncWind
|
||||
);
|
||||
};
|
||||
|
||||
export const ApplicationSyncWindowStatusIcon = ({project, state}: {project: string; state: appModels.ApplicationSyncWindowState}) => {
|
||||
export const ApplicationSyncWindowStatusIcon = ({project, state}: {project: string; state?: appModels.ApplicationSyncWindowState}) => {
|
||||
let className = '';
|
||||
let color = '';
|
||||
let deny = false;
|
||||
let allow = false;
|
||||
let inactiveAllow = false;
|
||||
if (state.assignedWindows !== undefined && state.assignedWindows.length > 0) {
|
||||
if (state?.assignedWindows !== undefined && state?.assignedWindows.length > 0) {
|
||||
if (state.activeWindows !== undefined && state.activeWindows.length > 0) {
|
||||
for (const w of state.activeWindows) {
|
||||
if (w.kind === 'deny') {
|
||||
|
||||
@@ -481,7 +481,9 @@ export interface HydrateOperation {
|
||||
finishedAt?: models.Time;
|
||||
phase: HydrateOperationPhase;
|
||||
message: string;
|
||||
// drySHA is the sha of the DRY commit being hydrated. This will be empty if the operation is not successful.
|
||||
drySHA: string;
|
||||
// hydratedSHA is the sha of the hydrated commit. This will be empty if the operation is not successful.
|
||||
hydratedSHA: string;
|
||||
sourceHydrator: SourceHydrator;
|
||||
}
|
||||
|
||||
@@ -97,23 +97,41 @@ func CheckOutOfBoundsSymlinks(basePath string) error {
|
||||
})
|
||||
}
|
||||
|
||||
// GetAppRefreshPaths returns the list of paths that should trigger a refresh for an application
|
||||
func GetAppRefreshPaths(app *v1alpha1.Application) []string {
|
||||
// GetSourceRefreshPaths returns the list of paths that should trigger a refresh for an application.
|
||||
// The source parameter influences the returned refresh paths:
|
||||
// - if source hydrator configured AND source is syncSource: use sync source path (ignores annotation)
|
||||
// - if source hydrator configured AND source is drySource WITH annotation: use annotation paths with drySource base
|
||||
// - if source hydrator not configured: use annotation paths with source base, or empty if no annotation
|
||||
func GetSourceRefreshPaths(app *v1alpha1.Application, source v1alpha1.ApplicationSource) []string {
|
||||
annotationPaths, hasAnnotation := app.Annotations[v1alpha1.AnnotationKeyManifestGeneratePaths]
|
||||
|
||||
if app.Spec.SourceHydrator != nil {
|
||||
syncSource := app.Spec.SourceHydrator.GetSyncSource()
|
||||
|
||||
// if source is syncSource use the source path
|
||||
if (source).Equals(&syncSource) {
|
||||
return []string{source.Path}
|
||||
}
|
||||
}
|
||||
|
||||
var paths []string
|
||||
if val, ok := app.Annotations[v1alpha1.AnnotationKeyManifestGeneratePaths]; ok && val != "" {
|
||||
for _, item := range strings.Split(val, ";") {
|
||||
if hasAnnotation && annotationPaths != "" {
|
||||
for _, item := range strings.Split(annotationPaths, ";") {
|
||||
// skip empty paths
|
||||
if item == "" {
|
||||
continue
|
||||
}
|
||||
// if absolute path, add as is
|
||||
if filepath.IsAbs(item) {
|
||||
paths = append(paths, item[1:])
|
||||
} else {
|
||||
for _, source := range app.Spec.GetSources() {
|
||||
paths = append(paths, filepath.Clean(filepath.Join(source.Path, item)))
|
||||
}
|
||||
continue
|
||||
}
|
||||
|
||||
// add the path relative to the source path
|
||||
paths = append(paths, filepath.Clean(filepath.Join(source.Path, item)))
|
||||
}
|
||||
}
|
||||
|
||||
return paths
|
||||
}
|
||||
|
||||
|
||||
@@ -9,6 +9,7 @@ import (
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/utils/ptr"
|
||||
|
||||
"github.com/argoproj/argo-cd/v3/pkg/apis/application/v1alpha1"
|
||||
fileutil "github.com/argoproj/argo-cd/v3/test/fixture/path"
|
||||
@@ -100,117 +101,102 @@ func TestAbsSymlink(t *testing.T) {
|
||||
assert.Equal(t, "abslink", oobError.File)
|
||||
}
|
||||
|
||||
func getApp(annotation string, sourcePath string) *v1alpha1.Application {
|
||||
return &v1alpha1.Application{
|
||||
func getApp(annotation *string, sourcePath *string) *v1alpha1.Application {
|
||||
app := &v1alpha1.Application{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Annotations: map[string]string{
|
||||
v1alpha1.AnnotationKeyManifestGeneratePaths: annotation,
|
||||
},
|
||||
},
|
||||
Spec: v1alpha1.ApplicationSpec{
|
||||
Source: &v1alpha1.ApplicationSource{
|
||||
Path: sourcePath,
|
||||
},
|
||||
Name: "test-app",
|
||||
},
|
||||
}
|
||||
if annotation != nil {
|
||||
app.Annotations = make(map[string]string)
|
||||
app.Annotations[v1alpha1.AnnotationKeyManifestGeneratePaths] = *annotation
|
||||
}
|
||||
|
||||
if sourcePath != nil {
|
||||
app.Spec.Source = &v1alpha1.ApplicationSource{
|
||||
Path: *sourcePath,
|
||||
}
|
||||
}
|
||||
|
||||
return app
|
||||
}
|
||||
|
||||
func getMultiSourceApp(annotation string, paths ...string) *v1alpha1.Application {
|
||||
var sources v1alpha1.ApplicationSources
|
||||
for _, path := range paths {
|
||||
sources = append(sources, v1alpha1.ApplicationSource{Path: path})
|
||||
}
|
||||
return &v1alpha1.Application{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Annotations: map[string]string{
|
||||
v1alpha1.AnnotationKeyManifestGeneratePaths: annotation,
|
||||
},
|
||||
func getSourceHydratorApp(annotation *string, drySourcePath string, syncSourcePath string) *v1alpha1.Application {
|
||||
app := getApp(annotation, nil)
|
||||
app.Spec.SourceHydrator = &v1alpha1.SourceHydrator{
|
||||
DrySource: v1alpha1.DrySource{
|
||||
Path: drySourcePath,
|
||||
},
|
||||
Spec: v1alpha1.ApplicationSpec{
|
||||
Sources: sources,
|
||||
SyncSource: v1alpha1.SyncSource{
|
||||
Path: syncSourcePath,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func Test_AppFilesHaveChanged(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
app *v1alpha1.Application
|
||||
files []string
|
||||
changeExpected bool
|
||||
}{
|
||||
{"default no path", &v1alpha1.Application{}, []string{"README.md"}, true},
|
||||
{"no files changed", getApp(".", "source/path"), []string{}, true},
|
||||
{"relative path - matching", getApp(".", "source/path"), []string{"source/path/my-deployment.yaml"}, true},
|
||||
{"relative path, multi source - matching #1", getMultiSourceApp(".", "source/path", "other/path"), []string{"source/path/my-deployment.yaml"}, true},
|
||||
{"relative path, multi source - matching #2", getMultiSourceApp(".", "other/path", "source/path"), []string{"source/path/my-deployment.yaml"}, true},
|
||||
{"relative path - not matching", getApp(".", "source/path"), []string{"README.md"}, false},
|
||||
{"relative path, multi source - not matching", getMultiSourceApp(".", "other/path", "unrelated/path"), []string{"README.md"}, false},
|
||||
{"absolute path - matching", getApp("/source/path", "source/path"), []string{"source/path/my-deployment.yaml"}, true},
|
||||
{"absolute path, multi source - matching #1", getMultiSourceApp("/source/path", "source/path", "other/path"), []string{"source/path/my-deployment.yaml"}, true},
|
||||
{"absolute path, multi source - matching #2", getMultiSourceApp("/source/path", "other/path", "source/path"), []string{"source/path/my-deployment.yaml"}, true},
|
||||
{"absolute path - not matching", getApp("/source/path1", "source/path"), []string{"source/path/my-deployment.yaml"}, false},
|
||||
{"absolute path, multi source - not matching", getMultiSourceApp("/source/path1", "other/path", "source/path"), []string{"source/path/my-deployment.yaml"}, false},
|
||||
{"glob path * - matching", getApp("/source/**/my-deployment.yaml", "source/path"), []string{"source/path/my-deployment.yaml"}, true},
|
||||
{"glob path * - not matching", getApp("/source/**/my-service.yaml", "source/path"), []string{"source/path/my-deployment.yaml"}, false},
|
||||
{"glob path ? - matching", getApp("/source/path/my-deployment-?.yaml", "source/path"), []string{"source/path/my-deployment-0.yaml"}, true},
|
||||
{"glob path ? - not matching", getApp("/source/path/my-deployment-?.yaml", "source/path"), []string{"source/path/my-deployment.yaml"}, false},
|
||||
{"glob path char range - matching", getApp("/source/path[0-9]/my-deployment.yaml", "source/path"), []string{"source/path1/my-deployment.yaml"}, true},
|
||||
{"glob path char range - not matching", getApp("/source/path[0-9]/my-deployment.yaml", "source/path"), []string{"source/path/my-deployment.yaml"}, false},
|
||||
{"mixed glob path - matching", getApp("/source/path[0-9]/my-*.yaml", "source/path"), []string{"source/path1/my-deployment.yaml"}, true},
|
||||
{"mixed glob path - not matching", getApp("/source/path[0-9]/my-*.yaml", "source/path"), []string{"README.md"}, false},
|
||||
{"two relative paths - matching", getApp(".;../shared", "my-app"), []string{"shared/my-deployment.yaml"}, true},
|
||||
{"two relative paths, multi source - matching #1", getMultiSourceApp(".;../shared", "my-app", "other/path"), []string{"shared/my-deployment.yaml"}, true},
|
||||
{"two relative paths, multi source - matching #2", getMultiSourceApp(".;../shared", "my-app", "other/path"), []string{"shared/my-deployment.yaml"}, true},
|
||||
{"two relative paths - not matching", getApp(".;../shared", "my-app"), []string{"README.md"}, false},
|
||||
{"two relative paths, multi source - not matching", getMultiSourceApp(".;../shared", "my-app", "other/path"), []string{"README.md"}, false},
|
||||
{"file relative path - matching", getApp("./my-deployment.yaml", "source/path"), []string{"source/path/my-deployment.yaml"}, true},
|
||||
{"file relative path, multi source - matching #1", getMultiSourceApp("./my-deployment.yaml", "source/path", "other/path"), []string{"source/path/my-deployment.yaml"}, true},
|
||||
{"file relative path, multi source - matching #2", getMultiSourceApp("./my-deployment.yaml", "other/path", "source/path"), []string{"source/path/my-deployment.yaml"}, true},
|
||||
{"file relative path - not matching", getApp("./my-deployment.yaml", "source/path"), []string{"README.md"}, false},
|
||||
{"file relative path, multi source - not matching", getMultiSourceApp("./my-deployment.yaml", "source/path", "other/path"), []string{"README.md"}, false},
|
||||
{"file absolute path - matching", getApp("/source/path/my-deployment.yaml", "source/path"), []string{"source/path/my-deployment.yaml"}, true},
|
||||
{"file absolute path, multi source - matching #1", getMultiSourceApp("/source/path/my-deployment.yaml", "source/path", "other/path"), []string{"source/path/my-deployment.yaml"}, true},
|
||||
{"file absolute path, multi source - matching #2", getMultiSourceApp("/source/path/my-deployment.yaml", "other/path", "source/path"), []string{"source/path/my-deployment.yaml"}, true},
|
||||
{"file absolute path - not matching", getApp("/source/path1/README.md", "source/path"), []string{"source/path/my-deployment.yaml"}, false},
|
||||
{"file absolute path, multi source - not matching", getMultiSourceApp("/source/path1/README.md", "source/path", "other/path"), []string{"source/path/my-deployment.yaml"}, false},
|
||||
{"file two relative paths - matching", getApp("./README.md;../shared/my-deployment.yaml", "my-app"), []string{"shared/my-deployment.yaml"}, true},
|
||||
{"file two relative paths, multi source - matching", getMultiSourceApp("./README.md;../shared/my-deployment.yaml", "my-app", "other-path"), []string{"shared/my-deployment.yaml"}, true},
|
||||
{"file two relative paths - not matching", getApp(".README.md;../shared/my-deployment.yaml", "my-app"), []string{"kustomization.yaml"}, false},
|
||||
{"file two relative paths, multi source - not matching", getMultiSourceApp(".README.md;../shared/my-deployment.yaml", "my-app", "other-path"), []string{"kustomization.yaml"}, false},
|
||||
{"changed file absolute path - matching", getApp(".", "source/path"), []string{"/source/path/my-deployment.yaml"}, true},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
ttc := tt
|
||||
t.Run(ttc.name, func(t *testing.T) {
|
||||
t.Parallel()
|
||||
refreshPaths := GetAppRefreshPaths(ttc.app)
|
||||
assert.Equal(t, ttc.changeExpected, AppFilesHaveChanged(refreshPaths, ttc.files), "AppFilesHaveChanged()")
|
||||
})
|
||||
}
|
||||
return app
|
||||
}
|
||||
|
||||
func Test_GetAppRefreshPaths(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
app *v1alpha1.Application
|
||||
source v1alpha1.ApplicationSource
|
||||
expectedPaths []string
|
||||
}{
|
||||
{"default no path", &v1alpha1.Application{}, []string{}},
|
||||
{"relative path", getApp(".", "source/path"), []string{"source/path"}},
|
||||
{"absolute path - multi source", getMultiSourceApp("/source/path", "source/path", "other/path"), []string{"source/path"}},
|
||||
{"two relative paths ", getApp(".;../shared", "my-app"), []string{"my-app", "shared"}},
|
||||
{"file relative path", getApp("./my-deployment.yaml", "source/path"), []string{"source/path/my-deployment.yaml"}},
|
||||
{"file absolute path", getApp("/source/path/my-deployment.yaml", "source/path"), []string{"source/path/my-deployment.yaml"}},
|
||||
{"file two relative paths", getApp("./README.md;../shared/my-deployment.yaml", "my-app"), []string{"my-app/README.md", "shared/my-deployment.yaml"}},
|
||||
{"glob path", getApp("/source/*/my-deployment.yaml", "source/path"), []string{"source/*/my-deployment.yaml"}},
|
||||
{"empty path", getApp(".;", "source/path"), []string{"source/path"}},
|
||||
{
|
||||
name: "single source without annotation",
|
||||
app: getApp(nil, ptr.To("source/path")),
|
||||
source: v1alpha1.ApplicationSource{Path: "source/path"},
|
||||
expectedPaths: []string{},
|
||||
},
|
||||
{
|
||||
name: "single source with annotation",
|
||||
app: getApp(ptr.To(".;dev/deploy;other/path"), ptr.To("source/path")),
|
||||
source: v1alpha1.ApplicationSource{Path: "source/path"},
|
||||
expectedPaths: []string{"source/path", "source/path/dev/deploy", "source/path/other/path"},
|
||||
},
|
||||
{
|
||||
name: "single source with empty annotation",
|
||||
app: getApp(ptr.To(".;;"), ptr.To("source/path")),
|
||||
source: v1alpha1.ApplicationSource{Path: "source/path"},
|
||||
expectedPaths: []string{"source/path"},
|
||||
},
|
||||
{
|
||||
name: "single source with absolute path annotation",
|
||||
app: getApp(ptr.To("/fullpath/deploy;other/path"), ptr.To("source/path")),
|
||||
source: v1alpha1.ApplicationSource{Path: "source/path"},
|
||||
expectedPaths: []string{"fullpath/deploy", "source/path/other/path"},
|
||||
},
|
||||
{
|
||||
name: "source hydrator sync source without annotation",
|
||||
app: getSourceHydratorApp(nil, "dry/path", "sync/path"),
|
||||
source: v1alpha1.ApplicationSource{Path: "sync/path"},
|
||||
expectedPaths: []string{"sync/path"},
|
||||
},
|
||||
{
|
||||
name: "source hydrator dry source without annotation",
|
||||
app: getSourceHydratorApp(nil, "dry/path", "sync/path"),
|
||||
source: v1alpha1.ApplicationSource{Path: "dry/path"},
|
||||
expectedPaths: []string{},
|
||||
},
|
||||
{
|
||||
name: "source hydrator sync source with annotation",
|
||||
app: getSourceHydratorApp(ptr.To("deploy"), "dry/path", "sync/path"),
|
||||
source: v1alpha1.ApplicationSource{Path: "sync/path"},
|
||||
expectedPaths: []string{"sync/path"},
|
||||
},
|
||||
{
|
||||
name: "source hydrator dry source with annotation",
|
||||
app: getSourceHydratorApp(ptr.To("deploy"), "dry/path", "sync/path"),
|
||||
source: v1alpha1.ApplicationSource{Path: "dry/path"},
|
||||
expectedPaths: []string{"dry/path/deploy"},
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
ttc := tt
|
||||
t.Run(ttc.name, func(t *testing.T) {
|
||||
t.Parallel()
|
||||
assert.ElementsMatch(t, ttc.expectedPaths, GetAppRefreshPaths(ttc.app), "GetAppRefreshPath()")
|
||||
assert.ElementsMatch(t, ttc.expectedPaths, GetSourceRefreshPaths(ttc.app, ttc.source), "GetAppRefreshPath()")
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@@ -13,6 +13,7 @@ import (
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/go-git/go-git/v5/plumbing"
|
||||
"github.com/go-git/go-git/v5/plumbing/transport"
|
||||
githttp "github.com/go-git/go-git/v5/plumbing/transport/http"
|
||||
"github.com/stretchr/testify/assert"
|
||||
@@ -135,6 +136,19 @@ func Test_IsAnnotatedTag(t *testing.T) {
|
||||
assert.False(t, atag)
|
||||
}
|
||||
|
||||
func Test_resolveTagReference(t *testing.T) {
|
||||
// Setup
|
||||
commitHash := plumbing.NewHash("0123456789abcdef0123456789abcdef01234567")
|
||||
tagRef := plumbing.NewReferenceFromStrings("refs/tags/v1.0.0", "sometaghash")
|
||||
|
||||
// Test single function
|
||||
resolvedRef := plumbing.NewHashReference(tagRef.Name(), commitHash)
|
||||
|
||||
// Verify
|
||||
assert.Equal(t, commitHash, resolvedRef.Hash())
|
||||
assert.Equal(t, tagRef.Name(), resolvedRef.Name())
|
||||
}
|
||||
|
||||
func Test_ChangedFiles(t *testing.T) {
|
||||
tempDir := t.TempDir()
|
||||
|
||||
|
||||
@@ -501,3 +501,27 @@ func TestLsFiles(t *testing.T) {
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, nilResult, lsResult)
|
||||
}
|
||||
|
||||
func TestAnnotatedTagHandling(t *testing.T) {
|
||||
dir := t.TempDir()
|
||||
|
||||
client, err := NewClientExt("https://github.com/argoproj/argo-cd.git", dir, NopCreds{}, false, false, "", "")
|
||||
require.NoError(t, err)
|
||||
|
||||
err = client.Init()
|
||||
require.NoError(t, err)
|
||||
|
||||
// Test annotated tag resolution
|
||||
commitSHA, err := client.LsRemote("v1.0.0") // Known annotated tag
|
||||
require.NoError(t, err)
|
||||
|
||||
// Verify we get commit SHA, not tag SHA
|
||||
assert.True(t, IsCommitSHA(commitSHA))
|
||||
|
||||
// Test tag reference handling
|
||||
refs, err := client.LsRefs()
|
||||
require.NoError(t, err)
|
||||
|
||||
// Verify tag exists in the list and points to a valid commit SHA
|
||||
assert.Contains(t, refs.Tags, "v1.0.0", "Tag v1.0.0 should exist in refs")
|
||||
}
|
||||
|
||||
@@ -85,6 +85,12 @@ func listRemote(r *git.Remote, o *git.ListOptions, insecure bool, creds Creds, p
|
||||
|
||||
var resultRefs []*plumbing.Reference
|
||||
_ = refs.ForEach(func(ref *plumbing.Reference) error {
|
||||
if ref.Name().IsTag() {
|
||||
if peeled, ok := ar.Peeled[ref.Name().String()]; ok {
|
||||
resultRefs = append(resultRefs, plumbing.NewHashReference(ref.Name(), peeled))
|
||||
return nil
|
||||
}
|
||||
}
|
||||
resultRefs = append(resultRefs, ref)
|
||||
return nil
|
||||
})
|
||||
|
||||
20
util/guard/guard.go
Normal file
20
util/guard/guard.go
Normal file
@@ -0,0 +1,20 @@
|
||||
package guard
|
||||
|
||||
import (
|
||||
"runtime/debug"
|
||||
)
|
||||
|
||||
// Minimal logger contract; avoids depending on any specific logging package.
|
||||
type Logger interface{ Errorf(string, ...any) }
|
||||
|
||||
// Run executes fn and recovers a panic, logging a component-specific message.
|
||||
func RecoverAndLog(fn func(), log Logger, msg string) {
|
||||
defer func() {
|
||||
if r := recover(); r != nil {
|
||||
if log != nil {
|
||||
log.Errorf("%s: %v %s", msg, r, debug.Stack())
|
||||
}
|
||||
}
|
||||
}()
|
||||
fn()
|
||||
}
|
||||
92
util/guard/guard_test.go
Normal file
92
util/guard/guard_test.go
Normal file
@@ -0,0 +1,92 @@
|
||||
package guard
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
"sync"
|
||||
"testing"
|
||||
)
|
||||
|
||||
type nop struct{}
|
||||
|
||||
func (nop) Errorf(string, ...any) {}
|
||||
|
||||
// recorder is a thread-safe logger that captures formatted messages.
|
||||
type recorder struct {
|
||||
mu sync.Mutex
|
||||
calls int
|
||||
msgs []string
|
||||
}
|
||||
|
||||
func (r *recorder) Errorf(format string, args ...any) {
|
||||
r.mu.Lock()
|
||||
defer r.mu.Unlock()
|
||||
r.calls++
|
||||
r.msgs = append(r.msgs, fmt.Sprintf(format, args...))
|
||||
}
|
||||
|
||||
func TestRun_Recovers(_ *testing.T) {
|
||||
RecoverAndLog(func() { panic("boom") }, nop{}, "msg") // fails if panic escapes
|
||||
}
|
||||
|
||||
func TestRun_AllowsNextCall(t *testing.T) {
|
||||
ran := false
|
||||
RecoverAndLog(func() { panic("boom") }, nop{}, "msg")
|
||||
RecoverAndLog(func() { ran = true }, nop{}, "msg")
|
||||
if !ran {
|
||||
t.Fatal("expected second callback to run")
|
||||
}
|
||||
}
|
||||
|
||||
func TestRun_LogsMessageAndStack(t *testing.T) {
|
||||
r := &recorder{}
|
||||
RecoverAndLog(func() { panic("boom") }, r, "msg")
|
||||
if r.calls != 1 {
|
||||
t.Fatalf("expected 1 log call, got %d", r.calls)
|
||||
}
|
||||
got := strings.Join(r.msgs, "\n")
|
||||
if !strings.Contains(got, "msg") {
|
||||
t.Errorf("expected log to contain message %q; got %q", "msg", got)
|
||||
}
|
||||
if !strings.Contains(got, "boom") {
|
||||
t.Errorf("expected log to contain panic value %q; got %q", "boom", got)
|
||||
}
|
||||
// Heuristic check that a stack trace was included.
|
||||
if !strings.Contains(got, "guard.go") && !strings.Contains(got, "runtime/panic.go") && !strings.Contains(got, "goroutine") {
|
||||
t.Errorf("expected log to contain a stack trace; got %q", got)
|
||||
}
|
||||
}
|
||||
|
||||
func TestRun_NilLoggerDoesNotPanic(_ *testing.T) {
|
||||
var l Logger // nil
|
||||
RecoverAndLog(func() { panic("boom") }, l, "ignored")
|
||||
}
|
||||
|
||||
func TestRun_NoPanicDoesNotLog(t *testing.T) {
|
||||
r := &recorder{}
|
||||
ran := false
|
||||
RecoverAndLog(func() { ran = true }, r, "msg")
|
||||
if !ran {
|
||||
t.Fatal("expected fn to run")
|
||||
}
|
||||
if r.calls != 0 {
|
||||
t.Fatalf("expected 0 log calls, got %d", r.calls)
|
||||
}
|
||||
}
|
||||
|
||||
func TestRun_ConcurrentPanicsLogged(t *testing.T) {
|
||||
r := &recorder{}
|
||||
const n = 10
|
||||
var wg sync.WaitGroup
|
||||
wg.Add(n)
|
||||
for i := 0; i < n; i++ {
|
||||
go func(i int) {
|
||||
defer wg.Done()
|
||||
RecoverAndLog(func() { panic(fmt.Sprintf("boom-%d", i)) }, r, "msg")
|
||||
}(i)
|
||||
}
|
||||
wg.Wait()
|
||||
if r.calls != n {
|
||||
t.Fatalf("expected %d log calls, got %d", n, r.calls)
|
||||
}
|
||||
}
|
||||
@@ -3,6 +3,7 @@ package healthz
|
||||
import (
|
||||
"fmt"
|
||||
"net/http"
|
||||
"time"
|
||||
|
||||
log "github.com/sirupsen/logrus"
|
||||
)
|
||||
@@ -11,9 +12,13 @@ import (
|
||||
// ServeHealthCheck relies on the provided function to return an error if unhealthy and nil otherwise.
|
||||
func ServeHealthCheck(mux *http.ServeMux, f func(r *http.Request) error) {
|
||||
mux.HandleFunc("/healthz", func(w http.ResponseWriter, r *http.Request) {
|
||||
startTs := time.Now()
|
||||
if err := f(r); err != nil {
|
||||
w.WriteHeader(http.StatusServiceUnavailable)
|
||||
log.Errorln(w, err)
|
||||
log.WithFields(log.Fields{
|
||||
"duration": time.Since(startTs),
|
||||
"component": "healthcheck",
|
||||
}).WithError(err).Error("Error serving health check request")
|
||||
} else {
|
||||
fmt.Fprintln(w, "ok")
|
||||
}
|
||||
|
||||
@@ -5,16 +5,22 @@ import (
|
||||
"net"
|
||||
"net/http"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
log "github.com/sirupsen/logrus"
|
||||
"github.com/sirupsen/logrus/hooks/test"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func TestHealthCheck(t *testing.T) {
|
||||
sentinel := false
|
||||
|
||||
lc := &net.ListenConfig{}
|
||||
ctx := t.Context()
|
||||
svcErrMsg := "This is a dummy error"
|
||||
serve := func(c chan<- string) {
|
||||
// listen on first available dynamic (unprivileged) port
|
||||
listener, err := net.Listen("tcp", ":0")
|
||||
listener, err := lc.Listen(ctx, "tcp", ":0")
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
@@ -25,7 +31,7 @@ func TestHealthCheck(t *testing.T) {
|
||||
mux := http.NewServeMux()
|
||||
ServeHealthCheck(mux, func(_ *http.Request) error {
|
||||
if sentinel {
|
||||
return errors.New("This is a dummy error")
|
||||
return errors.New(svcErrMsg)
|
||||
}
|
||||
return nil
|
||||
})
|
||||
@@ -47,7 +53,23 @@ func TestHealthCheck(t *testing.T) {
|
||||
require.Equalf(t, http.StatusOK, resp.StatusCode, "Was expecting status code 200 from health check, but got %d instead", resp.StatusCode)
|
||||
|
||||
sentinel = true
|
||||
hook := test.NewGlobal()
|
||||
|
||||
resp, _ = http.Get(server + "/healthz")
|
||||
require.Equalf(t, http.StatusServiceUnavailable, resp.StatusCode, "Was expecting status code 503 from health check, but got %d instead", resp.StatusCode)
|
||||
assert.NotEmpty(t, hook.Entries, "Was expecting at least one log entry from health check, but got none")
|
||||
expectedMsg := "Error serving health check request"
|
||||
var foundEntry log.Entry
|
||||
for _, entry := range hook.Entries {
|
||||
if entry.Level == log.ErrorLevel &&
|
||||
entry.Message == expectedMsg {
|
||||
foundEntry = entry
|
||||
break
|
||||
}
|
||||
}
|
||||
require.NotEmpty(t, foundEntry, "Expected an error message '%s', but it was't found", expectedMsg)
|
||||
actualErr, ok := foundEntry.Data["error"].(error)
|
||||
require.True(t, ok, "Expected 'error' field to contain an error, but it doesn't")
|
||||
assert.Equal(t, svcErrMsg, actualErr.Error(), "expected original error message '"+svcErrMsg+"', but got '"+actualErr.Error()+"'")
|
||||
assert.Greater(t, foundEntry.Data["duration"].(time.Duration), time.Duration(0))
|
||||
}
|
||||
|
||||
@@ -33,6 +33,7 @@ import (
|
||||
"github.com/argoproj/argo-cd/v3/util/argo"
|
||||
"github.com/argoproj/argo-cd/v3/util/db"
|
||||
"github.com/argoproj/argo-cd/v3/util/glob"
|
||||
"github.com/argoproj/argo-cd/v3/util/guard"
|
||||
"github.com/argoproj/argo-cd/v3/util/settings"
|
||||
)
|
||||
|
||||
@@ -48,6 +49,8 @@ const usernameRegex = `[\w\.][\w\.-]{0,30}[\w\.\$-]?`
|
||||
|
||||
const payloadQueueSize = 50000
|
||||
|
||||
const panicMsgServer = "panic while processing api-server webhook event"
|
||||
|
||||
var _ settingsSource = &settings.SettingsManager{}
|
||||
|
||||
type ArgoCDWebhookHandler struct {
|
||||
@@ -121,6 +124,7 @@ func NewHandler(namespace string, applicationNamespaces []string, webhookParalle
|
||||
}
|
||||
|
||||
func (a *ArgoCDWebhookHandler) startWorkerPool(webhookParallelism int) {
|
||||
compLog := log.WithField("component", "api-server-webhook")
|
||||
for i := 0; i < webhookParallelism; i++ {
|
||||
a.Add(1)
|
||||
go func() {
|
||||
@@ -130,7 +134,7 @@ func (a *ArgoCDWebhookHandler) startWorkerPool(webhookParallelism int) {
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
a.HandleEvent(payload)
|
||||
guard.RecoverAndLog(func() { a.HandleEvent(payload) }, compLog, panicMsgServer)
|
||||
}
|
||||
}()
|
||||
}
|
||||
@@ -281,23 +285,23 @@ func (a *ArgoCDWebhookHandler) HandleEvent(payload any) {
|
||||
appIf := a.appsLister.Applications(nsFilter)
|
||||
apps, err := appIf.List(labels.Everything())
|
||||
if err != nil {
|
||||
log.Warnf("Failed to list applications: %v", err)
|
||||
log.Errorf("Failed to list applications: %v", err)
|
||||
return
|
||||
}
|
||||
|
||||
installationID, err := a.settingsSrc.GetInstallationID()
|
||||
if err != nil {
|
||||
log.Warnf("Failed to get installation ID: %v", err)
|
||||
log.Errorf("Failed to get installation ID: %v", err)
|
||||
return
|
||||
}
|
||||
trackingMethod, err := a.settingsSrc.GetTrackingMethod()
|
||||
if err != nil {
|
||||
log.Warnf("Failed to get trackingMethod: %v", err)
|
||||
log.Errorf("Failed to get trackingMethod: %v", err)
|
||||
return
|
||||
}
|
||||
appInstanceLabelKey, err := a.settingsSrc.GetAppInstanceLabelKey()
|
||||
if err != nil {
|
||||
log.Warnf("Failed to get appInstanceLabelKey: %v", err)
|
||||
log.Errorf("Failed to get appInstanceLabelKey: %v", err)
|
||||
return
|
||||
}
|
||||
|
||||
@@ -313,41 +317,47 @@ func (a *ArgoCDWebhookHandler) HandleEvent(payload any) {
|
||||
for _, webURL := range webURLs {
|
||||
repoRegexp, err := GetWebURLRegex(webURL)
|
||||
if err != nil {
|
||||
log.Warnf("Failed to get repoRegexp: %s", err)
|
||||
log.Errorf("Failed to get repoRegexp: %s", err)
|
||||
continue
|
||||
}
|
||||
|
||||
// iterate over apps and check if any files specified in their sources have changed
|
||||
for _, app := range filteredApps {
|
||||
// get all sources, including sync source and dry source if source hydrator is configured
|
||||
sources := app.Spec.GetSources()
|
||||
if app.Spec.SourceHydrator != nil {
|
||||
drySource := app.Spec.SourceHydrator.GetDrySource()
|
||||
if sourceRevisionHasChanged(drySource, revision, touchedHead) && sourceUsesURL(drySource, webURL, repoRegexp) {
|
||||
refreshPaths := path.GetAppRefreshPaths(&app)
|
||||
if path.AppFilesHaveChanged(refreshPaths, changedFiles) {
|
||||
namespacedAppInterface := a.appClientset.ArgoprojV1alpha1().Applications(app.ObjectMeta.Namespace)
|
||||
log.Infof("webhook trigger refresh app to hydrate '%s'", app.ObjectMeta.Name)
|
||||
_, err = argo.RefreshApp(namespacedAppInterface, app.ObjectMeta.Name, v1alpha1.RefreshTypeNormal, true)
|
||||
if err != nil {
|
||||
log.Warnf("Failed to hydrate app '%s' for controller reprocessing: %v", app.ObjectMeta.Name, err)
|
||||
continue
|
||||
}
|
||||
}
|
||||
}
|
||||
// we already have sync source, so add dry source if source hydrator is configured
|
||||
sources = append(sources, app.Spec.SourceHydrator.GetDrySource())
|
||||
}
|
||||
|
||||
for _, source := range app.Spec.GetSources() {
|
||||
// iterate over all sources and check if any files specified in refresh paths have changed
|
||||
for _, source := range sources {
|
||||
if sourceRevisionHasChanged(source, revision, touchedHead) && sourceUsesURL(source, webURL, repoRegexp) {
|
||||
refreshPaths := path.GetAppRefreshPaths(&app)
|
||||
refreshPaths := path.GetSourceRefreshPaths(&app, source)
|
||||
if path.AppFilesHaveChanged(refreshPaths, changedFiles) {
|
||||
namespacedAppInterface := a.appClientset.ArgoprojV1alpha1().Applications(app.ObjectMeta.Namespace)
|
||||
_, err = argo.RefreshApp(namespacedAppInterface, app.ObjectMeta.Name, v1alpha1.RefreshTypeNormal, true)
|
||||
if err != nil {
|
||||
log.Warnf("Failed to refresh app '%s' for controller reprocessing: %v", app.ObjectMeta.Name, err)
|
||||
continue
|
||||
hydrate := false
|
||||
if app.Spec.SourceHydrator != nil {
|
||||
drySource := app.Spec.SourceHydrator.GetDrySource()
|
||||
if (&source).Equals(&drySource) {
|
||||
hydrate = true
|
||||
}
|
||||
}
|
||||
// No need to refresh multiple times if multiple sources match.
|
||||
break
|
||||
|
||||
// refresh paths have changed, so we need to refresh the app
|
||||
log.Infof("refreshing app '%s' from webhook", app.Name)
|
||||
if hydrate {
|
||||
// log if we need to hydrate the app
|
||||
log.Infof("webhook trigger refresh app to hydrate '%s'", app.Name)
|
||||
}
|
||||
namespacedAppInterface := a.appClientset.ArgoprojV1alpha1().Applications(app.Namespace)
|
||||
if _, err := argo.RefreshApp(namespacedAppInterface, app.Name, v1alpha1.RefreshTypeNormal, hydrate); err != nil {
|
||||
log.Errorf("Failed to refresh app '%s': %v", app.Name, err)
|
||||
}
|
||||
break // we don't need to check other sources
|
||||
} else if change.shaBefore != "" && change.shaAfter != "" {
|
||||
if err := a.storePreviouslyCachedManifests(&app, change, trackingMethod, appInstanceLabelKey, installationID); err != nil {
|
||||
log.Warnf("Failed to store cached manifests of previous revision for app '%s': %v", app.Name, err)
|
||||
// update the cached manifests with the new revision cache key
|
||||
if err := a.storePreviouslyCachedManifests(&app, change, trackingMethod, appInstanceLabelKey, installationID, source); err != nil {
|
||||
log.Errorf("Failed to store cached manifests of previous revision for app '%s': %v", app.Name, err)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -399,7 +409,7 @@ func getURLRegex(originalURL string, regexpFormat string) (*regexp.Regexp, error
|
||||
return repoRegexp, nil
|
||||
}
|
||||
|
||||
func (a *ArgoCDWebhookHandler) storePreviouslyCachedManifests(app *v1alpha1.Application, change changeInfo, trackingMethod string, appInstanceLabelKey string, installationID string) error {
|
||||
func (a *ArgoCDWebhookHandler) storePreviouslyCachedManifests(app *v1alpha1.Application, change changeInfo, trackingMethod string, appInstanceLabelKey string, installationID string, source v1alpha1.ApplicationSource) error {
|
||||
destCluster, err := argo.GetDestinationCluster(context.Background(), app.Spec.Destination, a.db)
|
||||
if err != nil {
|
||||
return fmt.Errorf("error validating destination: %w", err)
|
||||
@@ -422,7 +432,7 @@ func (a *ArgoCDWebhookHandler) storePreviouslyCachedManifests(app *v1alpha1.Appl
|
||||
if err != nil {
|
||||
return fmt.Errorf("error getting ref sources: %w", err)
|
||||
}
|
||||
source := app.Spec.GetSource()
|
||||
|
||||
cache.LogDebugManifestCacheKeyFields("moving manifests cache", "webhook app revision changed", change.shaBefore, &source, refSources, &clusterInfo, app.Spec.Destination.Namespace, trackingMethod, appInstanceLabelKey, app.Name, nil)
|
||||
|
||||
if err := a.repoCache.SetNewRevisionManifests(change.shaAfter, change.shaBefore, &source, refSources, &clusterInfo, app.Spec.Destination.Namespace, trackingMethod, appInstanceLabelKey, app.Name, nil, installationID); err != nil {
|
||||
|
||||
@@ -34,11 +34,13 @@ import (
|
||||
|
||||
"github.com/sirupsen/logrus/hooks/test"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/mock"
|
||||
"github.com/stretchr/testify/require"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
|
||||
"github.com/argoproj/argo-cd/v3/pkg/apis/application/v1alpha1"
|
||||
appclientset "github.com/argoproj/argo-cd/v3/pkg/client/clientset/versioned/fake"
|
||||
"github.com/argoproj/argo-cd/v3/reposerver/apiclient"
|
||||
"github.com/argoproj/argo-cd/v3/reposerver/cache"
|
||||
cacheutil "github.com/argoproj/argo-cd/v3/util/cache"
|
||||
"github.com/argoproj/argo-cd/v3/util/settings"
|
||||
@@ -150,64 +152,6 @@ func TestAzureDevOpsCommitEvent(t *testing.T) {
|
||||
hook.Reset()
|
||||
}
|
||||
|
||||
// TestGitHubCommitEvent_MultiSource_Refresh makes sure that a webhook will refresh a multi-source app when at least
|
||||
// one source matches.
|
||||
func TestGitHubCommitEvent_MultiSource_Refresh(t *testing.T) {
|
||||
hook := test.NewGlobal()
|
||||
var patched bool
|
||||
reaction := func(action kubetesting.Action) (handled bool, ret runtime.Object, err error) {
|
||||
patchAction := action.(kubetesting.PatchAction)
|
||||
assert.Equal(t, "app-to-refresh", patchAction.GetName())
|
||||
patched = true
|
||||
return true, nil, nil
|
||||
}
|
||||
h := NewMockHandler(&reactorDef{"patch", "applications", reaction}, []string{}, &v1alpha1.Application{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "app-to-refresh",
|
||||
Namespace: "argocd",
|
||||
},
|
||||
Spec: v1alpha1.ApplicationSpec{
|
||||
Sources: v1alpha1.ApplicationSources{
|
||||
{
|
||||
RepoURL: "https://github.com/some/unrelated-repo",
|
||||
Path: ".",
|
||||
},
|
||||
{
|
||||
RepoURL: "https://github.com/jessesuen/test-repo",
|
||||
Path: ".",
|
||||
},
|
||||
},
|
||||
},
|
||||
}, &v1alpha1.Application{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "app-to-ignore",
|
||||
},
|
||||
Spec: v1alpha1.ApplicationSpec{
|
||||
Sources: v1alpha1.ApplicationSources{
|
||||
{
|
||||
RepoURL: "https://github.com/some/unrelated-repo",
|
||||
Path: ".",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
)
|
||||
req := httptest.NewRequest(http.MethodPost, "/api/webhook", nil)
|
||||
req.Header.Set("X-GitHub-Event", "push")
|
||||
eventJSON, err := os.ReadFile("testdata/github-commit-event.json")
|
||||
require.NoError(t, err)
|
||||
req.Body = io.NopCloser(bytes.NewReader(eventJSON))
|
||||
w := httptest.NewRecorder()
|
||||
h.Handler(w, req)
|
||||
close(h.queue)
|
||||
h.Wait()
|
||||
assert.Equal(t, http.StatusOK, w.Code)
|
||||
expectedLogResult := "Requested app 'app-to-refresh' refresh"
|
||||
assert.Equal(t, expectedLogResult, hook.LastEntry().Message)
|
||||
assert.True(t, patched)
|
||||
hook.Reset()
|
||||
}
|
||||
|
||||
// TestGitHubCommitEvent_AppsInOtherNamespaces makes sure that webhooks properly find apps in the configured set of
|
||||
// allowed namespaces when Apps are allowed in any namespace
|
||||
func TestGitHubCommitEvent_AppsInOtherNamespaces(t *testing.T) {
|
||||
@@ -306,72 +250,6 @@ func TestGitHubCommitEvent_AppsInOtherNamespaces(t *testing.T) {
|
||||
hook.Reset()
|
||||
}
|
||||
|
||||
// TestGitHubCommitEvent_Hydrate makes sure that a webhook will hydrate an app when dry source changed.
|
||||
func TestGitHubCommitEvent_Hydrate(t *testing.T) {
|
||||
hook := test.NewGlobal()
|
||||
var patched bool
|
||||
reaction := func(action kubetesting.Action) (handled bool, ret runtime.Object, err error) {
|
||||
patchAction := action.(kubetesting.PatchAction)
|
||||
assert.Equal(t, "app-to-hydrate", patchAction.GetName())
|
||||
patched = true
|
||||
return true, nil, nil
|
||||
}
|
||||
h := NewMockHandler(&reactorDef{"patch", "applications", reaction}, []string{}, &v1alpha1.Application{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "app-to-hydrate",
|
||||
Namespace: "argocd",
|
||||
},
|
||||
Spec: v1alpha1.ApplicationSpec{
|
||||
SourceHydrator: &v1alpha1.SourceHydrator{
|
||||
DrySource: v1alpha1.DrySource{
|
||||
RepoURL: "https://github.com/jessesuen/test-repo",
|
||||
TargetRevision: "HEAD",
|
||||
Path: ".",
|
||||
},
|
||||
SyncSource: v1alpha1.SyncSource{
|
||||
TargetBranch: "environments/dev",
|
||||
Path: ".",
|
||||
},
|
||||
HydrateTo: nil,
|
||||
},
|
||||
},
|
||||
}, &v1alpha1.Application{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "app-to-ignore",
|
||||
},
|
||||
Spec: v1alpha1.ApplicationSpec{
|
||||
Sources: v1alpha1.ApplicationSources{
|
||||
{
|
||||
RepoURL: "https://github.com/some/unrelated-repo",
|
||||
Path: ".",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
)
|
||||
req := httptest.NewRequest(http.MethodPost, "/api/webhook", nil)
|
||||
req.Header.Set("X-GitHub-Event", "push")
|
||||
eventJSON, err := os.ReadFile("testdata/github-commit-event.json")
|
||||
require.NoError(t, err)
|
||||
req.Body = io.NopCloser(bytes.NewReader(eventJSON))
|
||||
w := httptest.NewRecorder()
|
||||
h.Handler(w, req)
|
||||
close(h.queue)
|
||||
h.Wait()
|
||||
assert.Equal(t, http.StatusOK, w.Code)
|
||||
assert.True(t, patched)
|
||||
|
||||
logMessages := make([]string, 0, len(hook.Entries))
|
||||
for _, entry := range hook.Entries {
|
||||
logMessages = append(logMessages, entry.Message)
|
||||
}
|
||||
|
||||
assert.Contains(t, logMessages, "webhook trigger refresh app to hydrate 'app-to-hydrate'")
|
||||
assert.NotContains(t, logMessages, "webhook trigger refresh app to hydrate 'app-to-ignore'")
|
||||
|
||||
hook.Reset()
|
||||
}
|
||||
|
||||
func TestGitHubTagEvent(t *testing.T) {
|
||||
hook := test.NewGlobal()
|
||||
h := NewMockHandler(nil, []string{})
|
||||
@@ -610,7 +488,8 @@ func Test_affectedRevisionInfo_appRevisionHasChanged(t *testing.T) {
|
||||
// The payload's "push.changes[0].new.name" member seems to only have the branch name (based on the example payload).
|
||||
// https://support.atlassian.com/bitbucket-cloud/docs/event-payloads/#EventPayloads-Push
|
||||
var pl bitbucket.RepoPushPayload
|
||||
_ = json.Unmarshal([]byte(fmt.Sprintf(`{"push":{"changes":[{"new":{"name":"%s"}}]}}`, branchName)), &pl)
|
||||
err := json.Unmarshal([]byte(fmt.Sprintf(`{"push":{"changes":[{"new":{"name":%q}}]}}`, branchName)), &pl)
|
||||
require.NoError(t, err)
|
||||
return pl
|
||||
}
|
||||
|
||||
@@ -829,3 +708,551 @@ func TestGitHubCommitEventMaxPayloadSize(t *testing.T) {
|
||||
assert.Equal(t, expectedLogResult, hook.LastEntry().Message)
|
||||
hook.Reset()
|
||||
}
|
||||
|
||||
func TestHandleEvent(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
tests := []struct {
|
||||
name string
|
||||
app *v1alpha1.Application
|
||||
changedFile string // file that was changed in the webhook payload
|
||||
hasRefresh bool // application has refresh annotation applied
|
||||
hasHydrate bool // application has hydrate annotation applied
|
||||
updateCache bool // cache should be updated with the new revision
|
||||
}{
|
||||
{
|
||||
name: "single source without annotation - always refreshes",
|
||||
app: &v1alpha1.Application{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "test-app",
|
||||
Namespace: "argocd",
|
||||
},
|
||||
Spec: v1alpha1.ApplicationSpec{
|
||||
Sources: v1alpha1.ApplicationSources{
|
||||
{
|
||||
RepoURL: "https://github.com/jessesuen/test-repo",
|
||||
Path: "source/path",
|
||||
TargetRevision: "HEAD",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
changedFile: "source/path/app.yaml",
|
||||
hasRefresh: true,
|
||||
hasHydrate: false,
|
||||
updateCache: false,
|
||||
},
|
||||
{
|
||||
name: "single source with annotation - matching file triggers refresh",
|
||||
app: &v1alpha1.Application{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "test-app",
|
||||
Namespace: "argocd",
|
||||
Annotations: map[string]string{
|
||||
"argocd.argoproj.io/manifest-generate-paths": "deploy",
|
||||
},
|
||||
},
|
||||
Spec: v1alpha1.ApplicationSpec{
|
||||
Sources: v1alpha1.ApplicationSources{
|
||||
{
|
||||
RepoURL: "https://github.com/jessesuen/test-repo",
|
||||
Path: "source/path",
|
||||
TargetRevision: "HEAD",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
changedFile: "source/path/deploy/app.yaml",
|
||||
hasRefresh: true,
|
||||
hasHydrate: false,
|
||||
updateCache: false,
|
||||
},
|
||||
{
|
||||
name: "single source with annotation - non-matching file updates cache",
|
||||
app: &v1alpha1.Application{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "test-app",
|
||||
Namespace: "argocd",
|
||||
Annotations: map[string]string{
|
||||
"argocd.argoproj.io/manifest-generate-paths": "manifests",
|
||||
},
|
||||
},
|
||||
Spec: v1alpha1.ApplicationSpec{
|
||||
Sources: v1alpha1.ApplicationSources{
|
||||
{
|
||||
RepoURL: "https://github.com/jessesuen/test-repo",
|
||||
Path: "source/path",
|
||||
TargetRevision: "HEAD",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
changedFile: "source/path/other/app.yaml",
|
||||
hasRefresh: false,
|
||||
hasHydrate: false,
|
||||
updateCache: true,
|
||||
},
|
||||
{
|
||||
name: "single source with multiple paths annotation - matching subpath triggers refresh",
|
||||
app: &v1alpha1.Application{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "test-app",
|
||||
Namespace: "argocd",
|
||||
Annotations: map[string]string{
|
||||
"argocd.argoproj.io/manifest-generate-paths": "manifests;dev/deploy;other/path",
|
||||
},
|
||||
},
|
||||
Spec: v1alpha1.ApplicationSpec{
|
||||
Sources: v1alpha1.ApplicationSources{
|
||||
{
|
||||
RepoURL: "https://github.com/jessesuen/test-repo",
|
||||
Path: "source/path",
|
||||
TargetRevision: "HEAD",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
changedFile: "source/path/dev/deploy/app.yaml",
|
||||
hasRefresh: true,
|
||||
hasHydrate: false,
|
||||
updateCache: false,
|
||||
},
|
||||
{
|
||||
name: "multi-source without annotation - always refreshes",
|
||||
app: &v1alpha1.Application{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "test-app",
|
||||
Namespace: "argocd",
|
||||
},
|
||||
Spec: v1alpha1.ApplicationSpec{
|
||||
Sources: v1alpha1.ApplicationSources{
|
||||
{
|
||||
RepoURL: "https://github.com/jessesuen/test-repo",
|
||||
Path: "helm-charts",
|
||||
TargetRevision: "HEAD",
|
||||
},
|
||||
{
|
||||
RepoURL: "https://github.com/jessesuen/test-repo",
|
||||
Path: "ksapps",
|
||||
TargetRevision: "HEAD",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
changedFile: "ksapps/app.yaml",
|
||||
hasRefresh: true,
|
||||
hasHydrate: false,
|
||||
updateCache: false,
|
||||
},
|
||||
{
|
||||
name: "multi-source with annotation - matching file triggers refresh",
|
||||
app: &v1alpha1.Application{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "test-app",
|
||||
Namespace: "argocd",
|
||||
Annotations: map[string]string{
|
||||
"argocd.argoproj.io/manifest-generate-paths": "components",
|
||||
},
|
||||
},
|
||||
Spec: v1alpha1.ApplicationSpec{
|
||||
Sources: v1alpha1.ApplicationSources{
|
||||
{
|
||||
RepoURL: "https://github.com/jessesuen/test-repo",
|
||||
Path: "helm-charts",
|
||||
TargetRevision: "HEAD",
|
||||
},
|
||||
{
|
||||
RepoURL: "https://github.com/jessesuen/test-repo",
|
||||
Path: "ksapps",
|
||||
TargetRevision: "HEAD",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
changedFile: "ksapps/components/app.yaml",
|
||||
hasRefresh: true,
|
||||
hasHydrate: false,
|
||||
updateCache: false,
|
||||
},
|
||||
{
|
||||
name: "source hydrator sync source without annotation - refreshes when sync path matches",
|
||||
app: &v1alpha1.Application{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "test-app",
|
||||
Namespace: "argocd",
|
||||
},
|
||||
Spec: v1alpha1.ApplicationSpec{
|
||||
SourceHydrator: &v1alpha1.SourceHydrator{
|
||||
DrySource: v1alpha1.DrySource{
|
||||
RepoURL: "https://github.com/jessesuen/test-repo",
|
||||
TargetRevision: "HEAD",
|
||||
Path: "dry/path",
|
||||
},
|
||||
SyncSource: v1alpha1.SyncSource{
|
||||
TargetBranch: "master",
|
||||
Path: "sync/path",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
changedFile: "sync/path/app.yaml",
|
||||
hasRefresh: true,
|
||||
hasHydrate: false,
|
||||
updateCache: false,
|
||||
},
|
||||
{
|
||||
name: "source hydrator dry source without annotation - always refreshes and hydrates",
|
||||
app: &v1alpha1.Application{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "test-app",
|
||||
Namespace: "argocd",
|
||||
},
|
||||
Spec: v1alpha1.ApplicationSpec{
|
||||
SourceHydrator: &v1alpha1.SourceHydrator{
|
||||
DrySource: v1alpha1.DrySource{
|
||||
RepoURL: "https://github.com/jessesuen/test-repo",
|
||||
TargetRevision: "HEAD",
|
||||
Path: "dry/path",
|
||||
},
|
||||
SyncSource: v1alpha1.SyncSource{
|
||||
TargetBranch: "master",
|
||||
Path: "sync/path",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
changedFile: "other/path/app.yaml",
|
||||
hasRefresh: true,
|
||||
hasHydrate: true,
|
||||
updateCache: false,
|
||||
},
|
||||
{
|
||||
name: "source hydrator sync source with annotation - refresh only",
|
||||
app: &v1alpha1.Application{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "test-app",
|
||||
Namespace: "argocd",
|
||||
Annotations: map[string]string{
|
||||
"argocd.argoproj.io/manifest-generate-paths": "deploy",
|
||||
},
|
||||
},
|
||||
Spec: v1alpha1.ApplicationSpec{
|
||||
SourceHydrator: &v1alpha1.SourceHydrator{
|
||||
DrySource: v1alpha1.DrySource{
|
||||
RepoURL: "https://github.com/jessesuen/test-repo",
|
||||
TargetRevision: "HEAD",
|
||||
Path: "dry/path",
|
||||
},
|
||||
SyncSource: v1alpha1.SyncSource{
|
||||
TargetBranch: "master",
|
||||
Path: "sync/path",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
changedFile: "sync/path/deploy/app.yaml",
|
||||
hasRefresh: true,
|
||||
hasHydrate: false,
|
||||
updateCache: false,
|
||||
},
|
||||
{
|
||||
name: "source hydrator dry source with annotation - refresh and hydrate",
|
||||
app: &v1alpha1.Application{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "test-app",
|
||||
Namespace: "argocd",
|
||||
Annotations: map[string]string{
|
||||
"argocd.argoproj.io/manifest-generate-paths": "deploy",
|
||||
},
|
||||
},
|
||||
Spec: v1alpha1.ApplicationSpec{
|
||||
SourceHydrator: &v1alpha1.SourceHydrator{
|
||||
DrySource: v1alpha1.DrySource{
|
||||
RepoURL: "https://github.com/jessesuen/test-repo",
|
||||
TargetRevision: "HEAD",
|
||||
Path: "dry/path",
|
||||
},
|
||||
SyncSource: v1alpha1.SyncSource{
|
||||
TargetBranch: "master",
|
||||
Path: "sync/path",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
changedFile: "dry/path/deploy/app.yaml",
|
||||
hasRefresh: true,
|
||||
hasHydrate: true,
|
||||
updateCache: false,
|
||||
},
|
||||
{
|
||||
name: "source hydrator dry source with annotation - non-matching file updates cache",
|
||||
app: &v1alpha1.Application{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "test-app",
|
||||
Namespace: "argocd",
|
||||
Annotations: map[string]string{
|
||||
"argocd.argoproj.io/manifest-generate-paths": "deploy",
|
||||
},
|
||||
},
|
||||
Spec: v1alpha1.ApplicationSpec{
|
||||
SourceHydrator: &v1alpha1.SourceHydrator{
|
||||
DrySource: v1alpha1.DrySource{
|
||||
RepoURL: "https://github.com/jessesuen/test-repo",
|
||||
TargetRevision: "HEAD",
|
||||
Path: "dry/path",
|
||||
},
|
||||
SyncSource: v1alpha1.SyncSource{
|
||||
TargetBranch: "master",
|
||||
Path: "sync/path",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
changedFile: "dry/path/other/app.yaml",
|
||||
hasRefresh: false,
|
||||
hasHydrate: false,
|
||||
updateCache: true,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
ttc := tt
|
||||
t.Run(ttc.name, func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
var patchData []byte
|
||||
var patched bool
|
||||
reaction := func(action kubetesting.Action) (handled bool, ret runtime.Object, err error) {
|
||||
if action.GetVerb() == "patch" {
|
||||
patchAction := action.(kubetesting.PatchAction)
|
||||
patchData = patchAction.GetPatch()
|
||||
patched = true
|
||||
}
|
||||
return true, nil, nil
|
||||
}
|
||||
|
||||
// Setup cache
|
||||
inMemoryCache := cacheutil.NewInMemoryCache(1 * time.Hour)
|
||||
cacheClient := cacheutil.NewCache(inMemoryCache)
|
||||
repoCache := cache.NewCache(
|
||||
cacheClient,
|
||||
1*time.Minute,
|
||||
1*time.Minute,
|
||||
10*time.Second,
|
||||
)
|
||||
|
||||
// Pre-populate cache with beforeSHA if we're testing cache updates
|
||||
if ttc.updateCache {
|
||||
var source *v1alpha1.ApplicationSource
|
||||
if ttc.app.Spec.SourceHydrator != nil {
|
||||
drySource := ttc.app.Spec.SourceHydrator.GetDrySource()
|
||||
source = &drySource
|
||||
} else if len(ttc.app.Spec.Sources) > 0 {
|
||||
source = &ttc.app.Spec.Sources[0]
|
||||
}
|
||||
if source != nil {
|
||||
setupTestCache(t, repoCache, ttc.app.Name, source, []string{"test-manifest"})
|
||||
}
|
||||
}
|
||||
|
||||
// Setup server cache with cluster info
|
||||
serverCache := servercache.NewCache(appstate.NewCache(cacheClient, time.Minute), time.Minute, time.Minute, time.Minute)
|
||||
mockDB := &mocks.ArgoDB{}
|
||||
|
||||
// Set destination if not present (required for cache updates)
|
||||
if ttc.app.Spec.Destination.Server == "" {
|
||||
ttc.app.Spec.Destination.Server = testClusterURL
|
||||
}
|
||||
|
||||
mockDB.On("GetCluster", mock.Anything, testClusterURL).Return(&v1alpha1.Cluster{
|
||||
Server: testClusterURL,
|
||||
Info: v1alpha1.ClusterInfo{
|
||||
ServerVersion: "1.28.0",
|
||||
ConnectionState: v1alpha1.ConnectionState{Status: v1alpha1.ConnectionStatusSuccessful},
|
||||
APIVersions: []string{},
|
||||
},
|
||||
}, nil).Maybe()
|
||||
|
||||
err := serverCache.SetClusterInfo(testClusterURL, &v1alpha1.ClusterInfo{
|
||||
ServerVersion: "1.28.0",
|
||||
ConnectionState: v1alpha1.ConnectionState{Status: v1alpha1.ConnectionStatusSuccessful},
|
||||
APIVersions: []string{},
|
||||
})
|
||||
require.NoError(t, err)
|
||||
|
||||
// Create handler with reaction
|
||||
appClientset := appclientset.NewSimpleClientset(ttc.app)
|
||||
defaultReactor := appClientset.ReactionChain[0]
|
||||
appClientset.ReactionChain = nil
|
||||
appClientset.AddReactor("list", "*", func(action kubetesting.Action) (handled bool, ret runtime.Object, err error) {
|
||||
return defaultReactor.React(action)
|
||||
})
|
||||
appClientset.AddReactor("patch", "applications", reaction)
|
||||
|
||||
h := NewHandler(
|
||||
"argocd",
|
||||
[]string{},
|
||||
10,
|
||||
appClientset,
|
||||
&fakeAppsLister{clientset: appClientset},
|
||||
&settings.ArgoCDSettings{},
|
||||
&fakeSettingsSrc{},
|
||||
repoCache,
|
||||
serverCache,
|
||||
mockDB,
|
||||
int64(50)*1024*1024,
|
||||
)
|
||||
|
||||
// Create payload with the changed file
|
||||
payload := createTestPayload(ttc.changedFile)
|
||||
req := httptest.NewRequest(http.MethodPost, "/api/webhook", http.NoBody)
|
||||
req.Header.Set("X-GitHub-Event", "push")
|
||||
req.Body = io.NopCloser(bytes.NewReader(payload))
|
||||
|
||||
w := httptest.NewRecorder()
|
||||
h.Handler(w, req)
|
||||
close(h.queue)
|
||||
h.Wait()
|
||||
assert.Equal(t, http.StatusOK, w.Code)
|
||||
|
||||
// Verify refresh behavior
|
||||
assert.Equal(t, ttc.hasRefresh, patched, "patch status mismatch for test: %s", ttc.name)
|
||||
if patched && patchData != nil {
|
||||
verifyAnnotations(t, patchData, ttc.hasRefresh, ttc.hasHydrate)
|
||||
}
|
||||
|
||||
// Verify cache update behavior
|
||||
if ttc.updateCache {
|
||||
var source *v1alpha1.ApplicationSource
|
||||
if ttc.app.Spec.SourceHydrator != nil {
|
||||
drySource := ttc.app.Spec.SourceHydrator.GetDrySource()
|
||||
source = &drySource
|
||||
} else if len(ttc.app.Spec.Sources) > 0 {
|
||||
source = &ttc.app.Spec.Sources[0]
|
||||
}
|
||||
if source != nil {
|
||||
// Verify cache was updated with afterSHA
|
||||
clusterInfo := &mockClusterInfo{}
|
||||
var afterManifests cache.CachedManifestResponse
|
||||
err := repoCache.GetManifests(testAfterSHA, source, nil, clusterInfo, "", "", testAppLabelKey, ttc.app.Name, &afterManifests, nil, "")
|
||||
require.NoError(t, err, "cache should be updated with afterSHA")
|
||||
if err == nil {
|
||||
assert.Equal(t, testAfterSHA, afterManifests.ManifestResponse.Revision, "cached revision should match afterSHA")
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// createTestPayload creates a GitHub push event payload with the specified changed file
|
||||
func createTestPayload(changedFile string) []byte {
|
||||
payload := fmt.Sprintf(`{
|
||||
"ref": "refs/heads/master",
|
||||
"before": "%s",
|
||||
"after": "%s",
|
||||
"repository": {
|
||||
"html_url": "https://github.com/jessesuen/test-repo",
|
||||
"default_branch": "master"
|
||||
},
|
||||
"commits": [
|
||||
{
|
||||
"added": [],
|
||||
"modified": ["%s"],
|
||||
"removed": []
|
||||
}
|
||||
]
|
||||
}`, testBeforeSHA, testAfterSHA, changedFile)
|
||||
return []byte(payload)
|
||||
}
|
||||
|
||||
func Test_affectedRevisionInfo_bitbucket_changed_files(t *testing.T) {
|
||||
t.Skip("TODO: implement bitbucket client and helper functions")
|
||||
}
|
||||
|
||||
func TestLookupRepository(t *testing.T) {
|
||||
t.Skip("TODO: implement lookupRepository method")
|
||||
}
|
||||
|
||||
func TestCreateBitbucketClient(t *testing.T) {
|
||||
t.Skip("TODO: implement newBitbucketClient function")
|
||||
}
|
||||
|
||||
func TestFetchDiffStatBitbucketClient(t *testing.T) {
|
||||
t.Skip("TODO: implement fetchDiffStatFromBitbucket function and bitbucket client")
|
||||
}
|
||||
|
||||
func TestIsHeadTouched(t *testing.T) {
|
||||
t.Skip("TODO: implement isHeadTouched function and bitbucket client")
|
||||
}
|
||||
|
||||
// getRepositoryResponderFn and getDiffstatResponderFn removed - TODO: implement when bitbucket client is added
|
||||
|
||||
// mockClusterInfo implements cache.ClusterRuntimeInfo for testing
|
||||
type mockClusterInfo struct{}
|
||||
|
||||
func (m *mockClusterInfo) GetApiVersions() []string { return []string{} } //nolint:revive // interface method name
|
||||
func (m *mockClusterInfo) GetKubeVersion() string { return "1.28.0" }
|
||||
|
||||
// Common test constants
|
||||
const (
|
||||
testBeforeSHA = "d5c1ffa8e294bc18c639bfb4e0df499251034414"
|
||||
testAfterSHA = "63738bb582c8b540af7bcfc18f87c575c3ed66e0"
|
||||
testClusterURL = "https://kubernetes.default.svc"
|
||||
testAppLabelKey = "mycompany.com/appname"
|
||||
)
|
||||
|
||||
// verifyAnnotations is a helper that checks if the expected annotations are present in patch data
|
||||
func verifyAnnotations(t *testing.T, patchData []byte, expectRefresh bool, expectHydrate bool) {
|
||||
t.Helper()
|
||||
if patchData == nil {
|
||||
if expectRefresh {
|
||||
t.Error("expected app to be patched but patchData is nil")
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
var patchMap map[string]any
|
||||
err := json.Unmarshal(patchData, &patchMap)
|
||||
require.NoError(t, err)
|
||||
|
||||
metadata, hasMetadata := patchMap["metadata"].(map[string]any)
|
||||
require.True(t, hasMetadata, "patch should have metadata")
|
||||
|
||||
annotations, hasAnnotations := metadata["annotations"].(map[string]any)
|
||||
require.True(t, hasAnnotations, "patch should have annotations")
|
||||
|
||||
// Check refresh annotation
|
||||
refreshValue, hasRefresh := annotations["argocd.argoproj.io/refresh"]
|
||||
if expectRefresh {
|
||||
assert.True(t, hasRefresh, "should have refresh annotation")
|
||||
assert.Equal(t, "normal", refreshValue, "refresh annotation should be 'normal'")
|
||||
} else {
|
||||
assert.False(t, hasRefresh, "should not have refresh annotation")
|
||||
}
|
||||
|
||||
// Check hydrate annotation
|
||||
hydrateValue, hasHydrate := annotations["argocd.argoproj.io/hydrate"]
|
||||
if expectHydrate {
|
||||
assert.True(t, hasHydrate, "should have hydrate annotation")
|
||||
assert.Equal(t, "normal", hydrateValue, "hydrate annotation should be 'normal'")
|
||||
} else {
|
||||
assert.False(t, hasHydrate, "should not have hydrate annotation")
|
||||
}
|
||||
}
|
||||
|
||||
// setupTestCache is a helper that creates and populates a test cache
|
||||
func setupTestCache(t *testing.T, repoCache *cache.Cache, appName string, source *v1alpha1.ApplicationSource, manifests []string) {
|
||||
t.Helper()
|
||||
clusterInfo := &mockClusterInfo{}
|
||||
dummyManifests := &cache.CachedManifestResponse{
|
||||
ManifestResponse: &apiclient.ManifestResponse{
|
||||
Revision: testBeforeSHA,
|
||||
Manifests: manifests,
|
||||
Namespace: "",
|
||||
Server: testClusterURL,
|
||||
},
|
||||
}
|
||||
err := repoCache.SetManifests(testBeforeSHA, source, nil, clusterInfo, "", "", testAppLabelKey, appName, dummyManifests, nil, "")
|
||||
require.NoError(t, err)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user