mirror of
https://github.com/argoproj/argo-cd.git
synced 2026-03-19 23:08:48 +01:00
Compare commits
14 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
96797ba846 | ||
|
|
b46a57ab82 | ||
|
|
2b3df7f5a8 | ||
|
|
4ef56634b4 | ||
|
|
cb9574597e | ||
|
|
468870f65d | ||
|
|
cfeed49105 | ||
|
|
c21141a51f | ||
|
|
0415c60af9 | ||
|
|
9a3235ef92 | ||
|
|
3320f1ed7a | ||
|
|
20dd73af34 | ||
|
|
206d57b0de | ||
|
|
c1467b81bc |
@@ -29,10 +29,10 @@ type GitGenerator struct {
|
||||
}
|
||||
|
||||
// NewGitGenerator creates a new instance of Git Generator
|
||||
func NewGitGenerator(repos services.Repos, namespace string) Generator {
|
||||
func NewGitGenerator(repos services.Repos, controllerNamespace string) Generator {
|
||||
g := &GitGenerator{
|
||||
repos: repos,
|
||||
namespace: namespace,
|
||||
namespace: controllerNamespace,
|
||||
}
|
||||
|
||||
return g
|
||||
@@ -78,11 +78,11 @@ func (g *GitGenerator) GenerateParams(appSetGenerator *argoprojiov1alpha1.Applic
|
||||
if !strings.Contains(appSet.Spec.Template.Spec.Project, "{{") {
|
||||
project := appSet.Spec.Template.Spec.Project
|
||||
appProject := &argoprojiov1alpha1.AppProject{}
|
||||
namespace := g.namespace
|
||||
if namespace == "" {
|
||||
namespace = appSet.Namespace
|
||||
controllerNamespace := g.namespace
|
||||
if controllerNamespace == "" {
|
||||
controllerNamespace = appSet.Namespace
|
||||
}
|
||||
if err := client.Get(context.TODO(), types.NamespacedName{Name: project, Namespace: namespace}, appProject); err != nil {
|
||||
if err := client.Get(context.TODO(), types.NamespacedName{Name: project, Namespace: controllerNamespace}, appProject); err != nil {
|
||||
return nil, fmt.Errorf("error getting project %s: %w", project, err)
|
||||
}
|
||||
// we need to verify the signature on the Git revision if GPG is enabled
|
||||
|
||||
@@ -10,15 +10,15 @@ import (
|
||||
"github.com/argoproj/argo-cd/v3/applicationset/services"
|
||||
)
|
||||
|
||||
func GetGenerators(ctx context.Context, c client.Client, k8sClient kubernetes.Interface, namespace string, argoCDService services.Repos, dynamicClient dynamic.Interface, scmConfig SCMConfig) map[string]Generator {
|
||||
func GetGenerators(ctx context.Context, c client.Client, k8sClient kubernetes.Interface, controllerNamespace string, argoCDService services.Repos, dynamicClient dynamic.Interface, scmConfig SCMConfig) map[string]Generator {
|
||||
terminalGenerators := map[string]Generator{
|
||||
"List": NewListGenerator(),
|
||||
"Clusters": NewClusterGenerator(ctx, c, k8sClient, namespace),
|
||||
"Git": NewGitGenerator(argoCDService, namespace),
|
||||
"Clusters": NewClusterGenerator(ctx, c, k8sClient, controllerNamespace),
|
||||
"Git": NewGitGenerator(argoCDService, controllerNamespace),
|
||||
"SCMProvider": NewSCMProviderGenerator(c, scmConfig),
|
||||
"ClusterDecisionResource": NewDuckTypeGenerator(ctx, dynamicClient, k8sClient, namespace),
|
||||
"ClusterDecisionResource": NewDuckTypeGenerator(ctx, dynamicClient, k8sClient, controllerNamespace),
|
||||
"PullRequest": NewPullRequestGenerator(c, scmConfig),
|
||||
"Plugin": NewPluginGenerator(c, namespace),
|
||||
"Plugin": NewPluginGenerator(c, controllerNamespace),
|
||||
}
|
||||
|
||||
nestedGenerators := map[string]Generator{
|
||||
|
||||
@@ -1202,7 +1202,7 @@ func (ctrl *ApplicationController) finalizeApplicationDeletion(app *appv1.Applic
|
||||
if err != nil {
|
||||
logCtx.Warnf("Unable to get destination cluster: %v", err)
|
||||
app.UnSetCascadedDeletion()
|
||||
app.UnSetPostDeleteFinalizer()
|
||||
app.UnSetPostDeleteFinalizerAll()
|
||||
if err := ctrl.updateFinalizers(app); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
Argo CD is largely stateless. All data is persisted as Kubernetes objects, which in turn is stored in Kubernetes' etcd. Redis is only used as a throw-away cache and can be lost. When lost, it will be rebuilt without loss of service.
|
||||
|
||||
A set of [HA manifests](https://github.com/argoproj/argo-cd/tree/master/manifests/ha) are provided for users who wish to run Argo CD in a highly available manner. This runs more containers, and runs Redis in HA mode.
|
||||
A set of [HA manifests](https://github.com/argoproj/argo-cd/tree/stable/manifests/ha) are provided for users who wish to run Argo CD in a highly available manner. This runs more containers, and runs Redis in HA mode.
|
||||
|
||||
!!! note
|
||||
|
||||
|
||||
@@ -188,7 +188,7 @@ git commit -m "Bump image to v1.2.3" \
|
||||
```
|
||||
|
||||
!!!note Newlines are not allowed
|
||||
The commit trailers must not contain newlines. The
|
||||
The commit trailers must not contain newlines.
|
||||
|
||||
So the full CI script might look something like this:
|
||||
|
||||
|
||||
2
go.mod
2
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.35.0
|
||||
github.com/argoproj/gitops-engine v0.7.1-0.20250617174952-093aef0dad58
|
||||
github.com/argoproj/gitops-engine v0.7.1-0.20250905160054-e48120133eec
|
||||
github.com/argoproj/notifications-engine v0.4.1-0.20250309174002-87bf0576a872
|
||||
github.com/argoproj/pkg v0.13.6
|
||||
github.com/argoproj/pkg/v2 v2.0.1
|
||||
|
||||
4
go.sum
4
go.sum
@@ -113,8 +113,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.20250617174952-093aef0dad58 h1:9ESamu44v3dR9j/I4/4Aa1Fx3QSIE8ElK1CR8Z285uk=
|
||||
github.com/argoproj/gitops-engine v0.7.1-0.20250617174952-093aef0dad58/go.mod h1:aIBEG3ohgaC1gh/sw2On6knkSnXkqRLDoBj234Dqczw=
|
||||
github.com/argoproj/gitops-engine v0.7.1-0.20250905160054-e48120133eec h1:rNAwbRQFvRIuW/e2bU+B10mlzghYXsnwZedYeA7Drz4=
|
||||
github.com/argoproj/gitops-engine v0.7.1-0.20250905160054-e48120133eec/go.mod h1:aIBEG3ohgaC1gh/sw2On6knkSnXkqRLDoBj234Dqczw=
|
||||
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.6 h1:36WPD9MNYECHcO1/R1pj6teYspiK7uMQLCgLGft2abM=
|
||||
|
||||
@@ -12,4 +12,4 @@ resources:
|
||||
images:
|
||||
- name: quay.io/argoproj/argocd
|
||||
newName: quay.io/argoproj/argocd
|
||||
newTag: v3.1.2
|
||||
newTag: v3.1.6
|
||||
|
||||
@@ -5,7 +5,7 @@ kind: Kustomization
|
||||
images:
|
||||
- name: quay.io/argoproj/argocd
|
||||
newName: quay.io/argoproj/argocd
|
||||
newTag: v3.1.2
|
||||
newTag: v3.1.6
|
||||
resources:
|
||||
- ./application-controller
|
||||
- ./dex
|
||||
|
||||
12
manifests/core-install-with-hydrator.yaml
generated
12
manifests/core-install-with-hydrator.yaml
generated
@@ -24699,7 +24699,7 @@ spec:
|
||||
key: applicationsetcontroller.requeue.after
|
||||
name: argocd-cmd-params-cm
|
||||
optional: true
|
||||
image: quay.io/argoproj/argocd:v3.1.2
|
||||
image: quay.io/argoproj/argocd:v3.1.6
|
||||
imagePullPolicy: Always
|
||||
name: argocd-applicationset-controller
|
||||
ports:
|
||||
@@ -24825,7 +24825,7 @@ spec:
|
||||
key: log.format.timestamp
|
||||
name: argocd-cmd-params-cm
|
||||
optional: true
|
||||
image: quay.io/argoproj/argocd:v3.1.2
|
||||
image: quay.io/argoproj/argocd:v3.1.6
|
||||
imagePullPolicy: Always
|
||||
livenessProbe:
|
||||
failureThreshold: 3
|
||||
@@ -24953,7 +24953,7 @@ spec:
|
||||
- argocd
|
||||
- admin
|
||||
- redis-initial-password
|
||||
image: quay.io/argoproj/argocd:v3.1.2
|
||||
image: quay.io/argoproj/argocd:v3.1.6
|
||||
imagePullPolicy: IfNotPresent
|
||||
name: secret-init
|
||||
securityContext:
|
||||
@@ -25244,7 +25244,7 @@ spec:
|
||||
value: /helm-working-dir
|
||||
- name: HELM_DATA_HOME
|
||||
value: /helm-working-dir
|
||||
image: quay.io/argoproj/argocd:v3.1.2
|
||||
image: quay.io/argoproj/argocd:v3.1.6
|
||||
imagePullPolicy: Always
|
||||
livenessProbe:
|
||||
failureThreshold: 3
|
||||
@@ -25296,7 +25296,7 @@ spec:
|
||||
- -n
|
||||
- /usr/local/bin/argocd
|
||||
- /var/run/argocd/argocd-cmp-server
|
||||
image: quay.io/argoproj/argocd:v3.1.2
|
||||
image: quay.io/argoproj/argocd:v3.1.6
|
||||
name: copyutil
|
||||
securityContext:
|
||||
allowPrivilegeEscalation: false
|
||||
@@ -25638,7 +25638,7 @@ spec:
|
||||
optional: true
|
||||
- name: KUBECACHEDIR
|
||||
value: /tmp/kubecache
|
||||
image: quay.io/argoproj/argocd:v3.1.2
|
||||
image: quay.io/argoproj/argocd:v3.1.6
|
||||
imagePullPolicy: Always
|
||||
name: argocd-application-controller
|
||||
ports:
|
||||
|
||||
10
manifests/core-install.yaml
generated
10
manifests/core-install.yaml
generated
@@ -24667,7 +24667,7 @@ spec:
|
||||
key: applicationsetcontroller.requeue.after
|
||||
name: argocd-cmd-params-cm
|
||||
optional: true
|
||||
image: quay.io/argoproj/argocd:v3.1.2
|
||||
image: quay.io/argoproj/argocd:v3.1.6
|
||||
imagePullPolicy: Always
|
||||
name: argocd-applicationset-controller
|
||||
ports:
|
||||
@@ -24787,7 +24787,7 @@ spec:
|
||||
- argocd
|
||||
- admin
|
||||
- redis-initial-password
|
||||
image: quay.io/argoproj/argocd:v3.1.2
|
||||
image: quay.io/argoproj/argocd:v3.1.6
|
||||
imagePullPolicy: IfNotPresent
|
||||
name: secret-init
|
||||
securityContext:
|
||||
@@ -25078,7 +25078,7 @@ spec:
|
||||
value: /helm-working-dir
|
||||
- name: HELM_DATA_HOME
|
||||
value: /helm-working-dir
|
||||
image: quay.io/argoproj/argocd:v3.1.2
|
||||
image: quay.io/argoproj/argocd:v3.1.6
|
||||
imagePullPolicy: Always
|
||||
livenessProbe:
|
||||
failureThreshold: 3
|
||||
@@ -25130,7 +25130,7 @@ spec:
|
||||
- -n
|
||||
- /usr/local/bin/argocd
|
||||
- /var/run/argocd/argocd-cmp-server
|
||||
image: quay.io/argoproj/argocd:v3.1.2
|
||||
image: quay.io/argoproj/argocd:v3.1.6
|
||||
name: copyutil
|
||||
securityContext:
|
||||
allowPrivilegeEscalation: false
|
||||
@@ -25472,7 +25472,7 @@ spec:
|
||||
optional: true
|
||||
- name: KUBECACHEDIR
|
||||
value: /tmp/kubecache
|
||||
image: quay.io/argoproj/argocd:v3.1.2
|
||||
image: quay.io/argoproj/argocd:v3.1.6
|
||||
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.1.2
|
||||
newTag: v3.1.6
|
||||
|
||||
@@ -12,7 +12,7 @@ patches:
|
||||
images:
|
||||
- name: quay.io/argoproj/argocd
|
||||
newName: quay.io/argoproj/argocd
|
||||
newTag: v3.1.2
|
||||
newTag: v3.1.6
|
||||
resources:
|
||||
- ../../base/application-controller
|
||||
- ../../base/applicationset-controller
|
||||
|
||||
18
manifests/ha/install-with-hydrator.yaml
generated
18
manifests/ha/install-with-hydrator.yaml
generated
@@ -26065,7 +26065,7 @@ spec:
|
||||
key: applicationsetcontroller.requeue.after
|
||||
name: argocd-cmd-params-cm
|
||||
optional: true
|
||||
image: quay.io/argoproj/argocd:v3.1.2
|
||||
image: quay.io/argoproj/argocd:v3.1.6
|
||||
imagePullPolicy: Always
|
||||
name: argocd-applicationset-controller
|
||||
ports:
|
||||
@@ -26191,7 +26191,7 @@ spec:
|
||||
key: log.format.timestamp
|
||||
name: argocd-cmd-params-cm
|
||||
optional: true
|
||||
image: quay.io/argoproj/argocd:v3.1.2
|
||||
image: quay.io/argoproj/argocd:v3.1.6
|
||||
imagePullPolicy: Always
|
||||
livenessProbe:
|
||||
failureThreshold: 3
|
||||
@@ -26342,7 +26342,7 @@ spec:
|
||||
- -n
|
||||
- /usr/local/bin/argocd
|
||||
- /shared/argocd-dex
|
||||
image: quay.io/argoproj/argocd:v3.1.2
|
||||
image: quay.io/argoproj/argocd:v3.1.6
|
||||
imagePullPolicy: Always
|
||||
name: copyutil
|
||||
securityContext:
|
||||
@@ -26438,7 +26438,7 @@ spec:
|
||||
key: notificationscontroller.repo.server.plaintext
|
||||
name: argocd-cmd-params-cm
|
||||
optional: true
|
||||
image: quay.io/argoproj/argocd:v3.1.2
|
||||
image: quay.io/argoproj/argocd:v3.1.6
|
||||
imagePullPolicy: Always
|
||||
livenessProbe:
|
||||
tcpSocket:
|
||||
@@ -26562,7 +26562,7 @@ spec:
|
||||
- argocd
|
||||
- admin
|
||||
- redis-initial-password
|
||||
image: quay.io/argoproj/argocd:v3.1.2
|
||||
image: quay.io/argoproj/argocd:v3.1.6
|
||||
imagePullPolicy: IfNotPresent
|
||||
name: secret-init
|
||||
securityContext:
|
||||
@@ -26879,7 +26879,7 @@ spec:
|
||||
value: /helm-working-dir
|
||||
- name: HELM_DATA_HOME
|
||||
value: /helm-working-dir
|
||||
image: quay.io/argoproj/argocd:v3.1.2
|
||||
image: quay.io/argoproj/argocd:v3.1.6
|
||||
imagePullPolicy: Always
|
||||
livenessProbe:
|
||||
failureThreshold: 3
|
||||
@@ -26931,7 +26931,7 @@ spec:
|
||||
- -n
|
||||
- /usr/local/bin/argocd
|
||||
- /var/run/argocd/argocd-cmp-server
|
||||
image: quay.io/argoproj/argocd:v3.1.2
|
||||
image: quay.io/argoproj/argocd:v3.1.6
|
||||
name: copyutil
|
||||
securityContext:
|
||||
allowPrivilegeEscalation: false
|
||||
@@ -27305,7 +27305,7 @@ spec:
|
||||
key: server.sync.replace.allowed
|
||||
name: argocd-cmd-params-cm
|
||||
optional: true
|
||||
image: quay.io/argoproj/argocd:v3.1.2
|
||||
image: quay.io/argoproj/argocd:v3.1.6
|
||||
imagePullPolicy: Always
|
||||
livenessProbe:
|
||||
httpGet:
|
||||
@@ -27683,7 +27683,7 @@ spec:
|
||||
optional: true
|
||||
- name: KUBECACHEDIR
|
||||
value: /tmp/kubecache
|
||||
image: quay.io/argoproj/argocd:v3.1.2
|
||||
image: quay.io/argoproj/argocd:v3.1.6
|
||||
imagePullPolicy: Always
|
||||
name: argocd-application-controller
|
||||
ports:
|
||||
|
||||
16
manifests/ha/install.yaml
generated
16
manifests/ha/install.yaml
generated
@@ -26035,7 +26035,7 @@ spec:
|
||||
key: applicationsetcontroller.requeue.after
|
||||
name: argocd-cmd-params-cm
|
||||
optional: true
|
||||
image: quay.io/argoproj/argocd:v3.1.2
|
||||
image: quay.io/argoproj/argocd:v3.1.6
|
||||
imagePullPolicy: Always
|
||||
name: argocd-applicationset-controller
|
||||
ports:
|
||||
@@ -26178,7 +26178,7 @@ spec:
|
||||
- -n
|
||||
- /usr/local/bin/argocd
|
||||
- /shared/argocd-dex
|
||||
image: quay.io/argoproj/argocd:v3.1.2
|
||||
image: quay.io/argoproj/argocd:v3.1.6
|
||||
imagePullPolicy: Always
|
||||
name: copyutil
|
||||
securityContext:
|
||||
@@ -26274,7 +26274,7 @@ spec:
|
||||
key: notificationscontroller.repo.server.plaintext
|
||||
name: argocd-cmd-params-cm
|
||||
optional: true
|
||||
image: quay.io/argoproj/argocd:v3.1.2
|
||||
image: quay.io/argoproj/argocd:v3.1.6
|
||||
imagePullPolicy: Always
|
||||
livenessProbe:
|
||||
tcpSocket:
|
||||
@@ -26398,7 +26398,7 @@ spec:
|
||||
- argocd
|
||||
- admin
|
||||
- redis-initial-password
|
||||
image: quay.io/argoproj/argocd:v3.1.2
|
||||
image: quay.io/argoproj/argocd:v3.1.6
|
||||
imagePullPolicy: IfNotPresent
|
||||
name: secret-init
|
||||
securityContext:
|
||||
@@ -26715,7 +26715,7 @@ spec:
|
||||
value: /helm-working-dir
|
||||
- name: HELM_DATA_HOME
|
||||
value: /helm-working-dir
|
||||
image: quay.io/argoproj/argocd:v3.1.2
|
||||
image: quay.io/argoproj/argocd:v3.1.6
|
||||
imagePullPolicy: Always
|
||||
livenessProbe:
|
||||
failureThreshold: 3
|
||||
@@ -26767,7 +26767,7 @@ spec:
|
||||
- -n
|
||||
- /usr/local/bin/argocd
|
||||
- /var/run/argocd/argocd-cmp-server
|
||||
image: quay.io/argoproj/argocd:v3.1.2
|
||||
image: quay.io/argoproj/argocd:v3.1.6
|
||||
name: copyutil
|
||||
securityContext:
|
||||
allowPrivilegeEscalation: false
|
||||
@@ -27141,7 +27141,7 @@ spec:
|
||||
key: server.sync.replace.allowed
|
||||
name: argocd-cmd-params-cm
|
||||
optional: true
|
||||
image: quay.io/argoproj/argocd:v3.1.2
|
||||
image: quay.io/argoproj/argocd:v3.1.6
|
||||
imagePullPolicy: Always
|
||||
livenessProbe:
|
||||
httpGet:
|
||||
@@ -27519,7 +27519,7 @@ spec:
|
||||
optional: true
|
||||
- name: KUBECACHEDIR
|
||||
value: /tmp/kubecache
|
||||
image: quay.io/argoproj/argocd:v3.1.2
|
||||
image: quay.io/argoproj/argocd:v3.1.6
|
||||
imagePullPolicy: Always
|
||||
name: argocd-application-controller
|
||||
ports:
|
||||
|
||||
18
manifests/ha/namespace-install-with-hydrator.yaml
generated
18
manifests/ha/namespace-install-with-hydrator.yaml
generated
@@ -1868,7 +1868,7 @@ spec:
|
||||
key: applicationsetcontroller.requeue.after
|
||||
name: argocd-cmd-params-cm
|
||||
optional: true
|
||||
image: quay.io/argoproj/argocd:v3.1.2
|
||||
image: quay.io/argoproj/argocd:v3.1.6
|
||||
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.1.2
|
||||
image: quay.io/argoproj/argocd:v3.1.6
|
||||
imagePullPolicy: Always
|
||||
livenessProbe:
|
||||
failureThreshold: 3
|
||||
@@ -2145,7 +2145,7 @@ spec:
|
||||
- -n
|
||||
- /usr/local/bin/argocd
|
||||
- /shared/argocd-dex
|
||||
image: quay.io/argoproj/argocd:v3.1.2
|
||||
image: quay.io/argoproj/argocd:v3.1.6
|
||||
imagePullPolicy: Always
|
||||
name: copyutil
|
||||
securityContext:
|
||||
@@ -2241,7 +2241,7 @@ spec:
|
||||
key: notificationscontroller.repo.server.plaintext
|
||||
name: argocd-cmd-params-cm
|
||||
optional: true
|
||||
image: quay.io/argoproj/argocd:v3.1.2
|
||||
image: quay.io/argoproj/argocd:v3.1.6
|
||||
imagePullPolicy: Always
|
||||
livenessProbe:
|
||||
tcpSocket:
|
||||
@@ -2365,7 +2365,7 @@ spec:
|
||||
- argocd
|
||||
- admin
|
||||
- redis-initial-password
|
||||
image: quay.io/argoproj/argocd:v3.1.2
|
||||
image: quay.io/argoproj/argocd:v3.1.6
|
||||
imagePullPolicy: IfNotPresent
|
||||
name: secret-init
|
||||
securityContext:
|
||||
@@ -2682,7 +2682,7 @@ spec:
|
||||
value: /helm-working-dir
|
||||
- name: HELM_DATA_HOME
|
||||
value: /helm-working-dir
|
||||
image: quay.io/argoproj/argocd:v3.1.2
|
||||
image: quay.io/argoproj/argocd:v3.1.6
|
||||
imagePullPolicy: Always
|
||||
livenessProbe:
|
||||
failureThreshold: 3
|
||||
@@ -2734,7 +2734,7 @@ spec:
|
||||
- -n
|
||||
- /usr/local/bin/argocd
|
||||
- /var/run/argocd/argocd-cmp-server
|
||||
image: quay.io/argoproj/argocd:v3.1.2
|
||||
image: quay.io/argoproj/argocd:v3.1.6
|
||||
name: copyutil
|
||||
securityContext:
|
||||
allowPrivilegeEscalation: false
|
||||
@@ -3108,7 +3108,7 @@ spec:
|
||||
key: server.sync.replace.allowed
|
||||
name: argocd-cmd-params-cm
|
||||
optional: true
|
||||
image: quay.io/argoproj/argocd:v3.1.2
|
||||
image: quay.io/argoproj/argocd:v3.1.6
|
||||
imagePullPolicy: Always
|
||||
livenessProbe:
|
||||
httpGet:
|
||||
@@ -3486,7 +3486,7 @@ spec:
|
||||
optional: true
|
||||
- name: KUBECACHEDIR
|
||||
value: /tmp/kubecache
|
||||
image: quay.io/argoproj/argocd:v3.1.2
|
||||
image: quay.io/argoproj/argocd:v3.1.6
|
||||
imagePullPolicy: Always
|
||||
name: argocd-application-controller
|
||||
ports:
|
||||
|
||||
16
manifests/ha/namespace-install.yaml
generated
16
manifests/ha/namespace-install.yaml
generated
@@ -1838,7 +1838,7 @@ spec:
|
||||
key: applicationsetcontroller.requeue.after
|
||||
name: argocd-cmd-params-cm
|
||||
optional: true
|
||||
image: quay.io/argoproj/argocd:v3.1.2
|
||||
image: quay.io/argoproj/argocd:v3.1.6
|
||||
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.1.2
|
||||
image: quay.io/argoproj/argocd:v3.1.6
|
||||
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.1.2
|
||||
image: quay.io/argoproj/argocd:v3.1.6
|
||||
imagePullPolicy: Always
|
||||
livenessProbe:
|
||||
tcpSocket:
|
||||
@@ -2201,7 +2201,7 @@ spec:
|
||||
- argocd
|
||||
- admin
|
||||
- redis-initial-password
|
||||
image: quay.io/argoproj/argocd:v3.1.2
|
||||
image: quay.io/argoproj/argocd:v3.1.6
|
||||
imagePullPolicy: IfNotPresent
|
||||
name: secret-init
|
||||
securityContext:
|
||||
@@ -2518,7 +2518,7 @@ spec:
|
||||
value: /helm-working-dir
|
||||
- name: HELM_DATA_HOME
|
||||
value: /helm-working-dir
|
||||
image: quay.io/argoproj/argocd:v3.1.2
|
||||
image: quay.io/argoproj/argocd:v3.1.6
|
||||
imagePullPolicy: Always
|
||||
livenessProbe:
|
||||
failureThreshold: 3
|
||||
@@ -2570,7 +2570,7 @@ spec:
|
||||
- -n
|
||||
- /usr/local/bin/argocd
|
||||
- /var/run/argocd/argocd-cmp-server
|
||||
image: quay.io/argoproj/argocd:v3.1.2
|
||||
image: quay.io/argoproj/argocd:v3.1.6
|
||||
name: copyutil
|
||||
securityContext:
|
||||
allowPrivilegeEscalation: false
|
||||
@@ -2944,7 +2944,7 @@ spec:
|
||||
key: server.sync.replace.allowed
|
||||
name: argocd-cmd-params-cm
|
||||
optional: true
|
||||
image: quay.io/argoproj/argocd:v3.1.2
|
||||
image: quay.io/argoproj/argocd:v3.1.6
|
||||
imagePullPolicy: Always
|
||||
livenessProbe:
|
||||
httpGet:
|
||||
@@ -3322,7 +3322,7 @@ spec:
|
||||
optional: true
|
||||
- name: KUBECACHEDIR
|
||||
value: /tmp/kubecache
|
||||
image: quay.io/argoproj/argocd:v3.1.2
|
||||
image: quay.io/argoproj/argocd:v3.1.6
|
||||
imagePullPolicy: Always
|
||||
name: argocd-application-controller
|
||||
ports:
|
||||
|
||||
18
manifests/install-with-hydrator.yaml
generated
18
manifests/install-with-hydrator.yaml
generated
@@ -25159,7 +25159,7 @@ spec:
|
||||
key: applicationsetcontroller.requeue.after
|
||||
name: argocd-cmd-params-cm
|
||||
optional: true
|
||||
image: quay.io/argoproj/argocd:v3.1.2
|
||||
image: quay.io/argoproj/argocd:v3.1.6
|
||||
imagePullPolicy: Always
|
||||
name: argocd-applicationset-controller
|
||||
ports:
|
||||
@@ -25285,7 +25285,7 @@ spec:
|
||||
key: log.format.timestamp
|
||||
name: argocd-cmd-params-cm
|
||||
optional: true
|
||||
image: quay.io/argoproj/argocd:v3.1.2
|
||||
image: quay.io/argoproj/argocd:v3.1.6
|
||||
imagePullPolicy: Always
|
||||
livenessProbe:
|
||||
failureThreshold: 3
|
||||
@@ -25436,7 +25436,7 @@ spec:
|
||||
- -n
|
||||
- /usr/local/bin/argocd
|
||||
- /shared/argocd-dex
|
||||
image: quay.io/argoproj/argocd:v3.1.2
|
||||
image: quay.io/argoproj/argocd:v3.1.6
|
||||
imagePullPolicy: Always
|
||||
name: copyutil
|
||||
securityContext:
|
||||
@@ -25532,7 +25532,7 @@ spec:
|
||||
key: notificationscontroller.repo.server.plaintext
|
||||
name: argocd-cmd-params-cm
|
||||
optional: true
|
||||
image: quay.io/argoproj/argocd:v3.1.2
|
||||
image: quay.io/argoproj/argocd:v3.1.6
|
||||
imagePullPolicy: Always
|
||||
livenessProbe:
|
||||
tcpSocket:
|
||||
@@ -25634,7 +25634,7 @@ spec:
|
||||
- argocd
|
||||
- admin
|
||||
- redis-initial-password
|
||||
image: quay.io/argoproj/argocd:v3.1.2
|
||||
image: quay.io/argoproj/argocd:v3.1.6
|
||||
imagePullPolicy: IfNotPresent
|
||||
name: secret-init
|
||||
securityContext:
|
||||
@@ -25925,7 +25925,7 @@ spec:
|
||||
value: /helm-working-dir
|
||||
- name: HELM_DATA_HOME
|
||||
value: /helm-working-dir
|
||||
image: quay.io/argoproj/argocd:v3.1.2
|
||||
image: quay.io/argoproj/argocd:v3.1.6
|
||||
imagePullPolicy: Always
|
||||
livenessProbe:
|
||||
failureThreshold: 3
|
||||
@@ -25977,7 +25977,7 @@ spec:
|
||||
- -n
|
||||
- /usr/local/bin/argocd
|
||||
- /var/run/argocd/argocd-cmp-server
|
||||
image: quay.io/argoproj/argocd:v3.1.2
|
||||
image: quay.io/argoproj/argocd:v3.1.6
|
||||
name: copyutil
|
||||
securityContext:
|
||||
allowPrivilegeEscalation: false
|
||||
@@ -26349,7 +26349,7 @@ spec:
|
||||
key: server.sync.replace.allowed
|
||||
name: argocd-cmd-params-cm
|
||||
optional: true
|
||||
image: quay.io/argoproj/argocd:v3.1.2
|
||||
image: quay.io/argoproj/argocd:v3.1.6
|
||||
imagePullPolicy: Always
|
||||
livenessProbe:
|
||||
httpGet:
|
||||
@@ -26727,7 +26727,7 @@ spec:
|
||||
optional: true
|
||||
- name: KUBECACHEDIR
|
||||
value: /tmp/kubecache
|
||||
image: quay.io/argoproj/argocd:v3.1.2
|
||||
image: quay.io/argoproj/argocd:v3.1.6
|
||||
imagePullPolicy: Always
|
||||
name: argocd-application-controller
|
||||
ports:
|
||||
|
||||
16
manifests/install.yaml
generated
16
manifests/install.yaml
generated
@@ -25127,7 +25127,7 @@ spec:
|
||||
key: applicationsetcontroller.requeue.after
|
||||
name: argocd-cmd-params-cm
|
||||
optional: true
|
||||
image: quay.io/argoproj/argocd:v3.1.2
|
||||
image: quay.io/argoproj/argocd:v3.1.6
|
||||
imagePullPolicy: Always
|
||||
name: argocd-applicationset-controller
|
||||
ports:
|
||||
@@ -25270,7 +25270,7 @@ spec:
|
||||
- -n
|
||||
- /usr/local/bin/argocd
|
||||
- /shared/argocd-dex
|
||||
image: quay.io/argoproj/argocd:v3.1.2
|
||||
image: quay.io/argoproj/argocd:v3.1.6
|
||||
imagePullPolicy: Always
|
||||
name: copyutil
|
||||
securityContext:
|
||||
@@ -25366,7 +25366,7 @@ spec:
|
||||
key: notificationscontroller.repo.server.plaintext
|
||||
name: argocd-cmd-params-cm
|
||||
optional: true
|
||||
image: quay.io/argoproj/argocd:v3.1.2
|
||||
image: quay.io/argoproj/argocd:v3.1.6
|
||||
imagePullPolicy: Always
|
||||
livenessProbe:
|
||||
tcpSocket:
|
||||
@@ -25468,7 +25468,7 @@ spec:
|
||||
- argocd
|
||||
- admin
|
||||
- redis-initial-password
|
||||
image: quay.io/argoproj/argocd:v3.1.2
|
||||
image: quay.io/argoproj/argocd:v3.1.6
|
||||
imagePullPolicy: IfNotPresent
|
||||
name: secret-init
|
||||
securityContext:
|
||||
@@ -25759,7 +25759,7 @@ spec:
|
||||
value: /helm-working-dir
|
||||
- name: HELM_DATA_HOME
|
||||
value: /helm-working-dir
|
||||
image: quay.io/argoproj/argocd:v3.1.2
|
||||
image: quay.io/argoproj/argocd:v3.1.6
|
||||
imagePullPolicy: Always
|
||||
livenessProbe:
|
||||
failureThreshold: 3
|
||||
@@ -25811,7 +25811,7 @@ spec:
|
||||
- -n
|
||||
- /usr/local/bin/argocd
|
||||
- /var/run/argocd/argocd-cmp-server
|
||||
image: quay.io/argoproj/argocd:v3.1.2
|
||||
image: quay.io/argoproj/argocd:v3.1.6
|
||||
name: copyutil
|
||||
securityContext:
|
||||
allowPrivilegeEscalation: false
|
||||
@@ -26183,7 +26183,7 @@ spec:
|
||||
key: server.sync.replace.allowed
|
||||
name: argocd-cmd-params-cm
|
||||
optional: true
|
||||
image: quay.io/argoproj/argocd:v3.1.2
|
||||
image: quay.io/argoproj/argocd:v3.1.6
|
||||
imagePullPolicy: Always
|
||||
livenessProbe:
|
||||
httpGet:
|
||||
@@ -26561,7 +26561,7 @@ spec:
|
||||
optional: true
|
||||
- name: KUBECACHEDIR
|
||||
value: /tmp/kubecache
|
||||
image: quay.io/argoproj/argocd:v3.1.2
|
||||
image: quay.io/argoproj/argocd:v3.1.6
|
||||
imagePullPolicy: Always
|
||||
name: argocd-application-controller
|
||||
ports:
|
||||
|
||||
18
manifests/namespace-install-with-hydrator.yaml
generated
18
manifests/namespace-install-with-hydrator.yaml
generated
@@ -962,7 +962,7 @@ spec:
|
||||
key: applicationsetcontroller.requeue.after
|
||||
name: argocd-cmd-params-cm
|
||||
optional: true
|
||||
image: quay.io/argoproj/argocd:v3.1.2
|
||||
image: quay.io/argoproj/argocd:v3.1.6
|
||||
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.1.2
|
||||
image: quay.io/argoproj/argocd:v3.1.6
|
||||
imagePullPolicy: Always
|
||||
livenessProbe:
|
||||
failureThreshold: 3
|
||||
@@ -1239,7 +1239,7 @@ spec:
|
||||
- -n
|
||||
- /usr/local/bin/argocd
|
||||
- /shared/argocd-dex
|
||||
image: quay.io/argoproj/argocd:v3.1.2
|
||||
image: quay.io/argoproj/argocd:v3.1.6
|
||||
imagePullPolicy: Always
|
||||
name: copyutil
|
||||
securityContext:
|
||||
@@ -1335,7 +1335,7 @@ spec:
|
||||
key: notificationscontroller.repo.server.plaintext
|
||||
name: argocd-cmd-params-cm
|
||||
optional: true
|
||||
image: quay.io/argoproj/argocd:v3.1.2
|
||||
image: quay.io/argoproj/argocd:v3.1.6
|
||||
imagePullPolicy: Always
|
||||
livenessProbe:
|
||||
tcpSocket:
|
||||
@@ -1437,7 +1437,7 @@ spec:
|
||||
- argocd
|
||||
- admin
|
||||
- redis-initial-password
|
||||
image: quay.io/argoproj/argocd:v3.1.2
|
||||
image: quay.io/argoproj/argocd:v3.1.6
|
||||
imagePullPolicy: IfNotPresent
|
||||
name: secret-init
|
||||
securityContext:
|
||||
@@ -1728,7 +1728,7 @@ spec:
|
||||
value: /helm-working-dir
|
||||
- name: HELM_DATA_HOME
|
||||
value: /helm-working-dir
|
||||
image: quay.io/argoproj/argocd:v3.1.2
|
||||
image: quay.io/argoproj/argocd:v3.1.6
|
||||
imagePullPolicy: Always
|
||||
livenessProbe:
|
||||
failureThreshold: 3
|
||||
@@ -1780,7 +1780,7 @@ spec:
|
||||
- -n
|
||||
- /usr/local/bin/argocd
|
||||
- /var/run/argocd/argocd-cmp-server
|
||||
image: quay.io/argoproj/argocd:v3.1.2
|
||||
image: quay.io/argoproj/argocd:v3.1.6
|
||||
name: copyutil
|
||||
securityContext:
|
||||
allowPrivilegeEscalation: false
|
||||
@@ -2152,7 +2152,7 @@ spec:
|
||||
key: server.sync.replace.allowed
|
||||
name: argocd-cmd-params-cm
|
||||
optional: true
|
||||
image: quay.io/argoproj/argocd:v3.1.2
|
||||
image: quay.io/argoproj/argocd:v3.1.6
|
||||
imagePullPolicy: Always
|
||||
livenessProbe:
|
||||
httpGet:
|
||||
@@ -2530,7 +2530,7 @@ spec:
|
||||
optional: true
|
||||
- name: KUBECACHEDIR
|
||||
value: /tmp/kubecache
|
||||
image: quay.io/argoproj/argocd:v3.1.2
|
||||
image: quay.io/argoproj/argocd:v3.1.6
|
||||
imagePullPolicy: Always
|
||||
name: argocd-application-controller
|
||||
ports:
|
||||
|
||||
16
manifests/namespace-install.yaml
generated
16
manifests/namespace-install.yaml
generated
@@ -930,7 +930,7 @@ spec:
|
||||
key: applicationsetcontroller.requeue.after
|
||||
name: argocd-cmd-params-cm
|
||||
optional: true
|
||||
image: quay.io/argoproj/argocd:v3.1.2
|
||||
image: quay.io/argoproj/argocd:v3.1.6
|
||||
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.1.2
|
||||
image: quay.io/argoproj/argocd:v3.1.6
|
||||
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.1.2
|
||||
image: quay.io/argoproj/argocd:v3.1.6
|
||||
imagePullPolicy: Always
|
||||
livenessProbe:
|
||||
tcpSocket:
|
||||
@@ -1271,7 +1271,7 @@ spec:
|
||||
- argocd
|
||||
- admin
|
||||
- redis-initial-password
|
||||
image: quay.io/argoproj/argocd:v3.1.2
|
||||
image: quay.io/argoproj/argocd:v3.1.6
|
||||
imagePullPolicy: IfNotPresent
|
||||
name: secret-init
|
||||
securityContext:
|
||||
@@ -1562,7 +1562,7 @@ spec:
|
||||
value: /helm-working-dir
|
||||
- name: HELM_DATA_HOME
|
||||
value: /helm-working-dir
|
||||
image: quay.io/argoproj/argocd:v3.1.2
|
||||
image: quay.io/argoproj/argocd:v3.1.6
|
||||
imagePullPolicy: Always
|
||||
livenessProbe:
|
||||
failureThreshold: 3
|
||||
@@ -1614,7 +1614,7 @@ spec:
|
||||
- -n
|
||||
- /usr/local/bin/argocd
|
||||
- /var/run/argocd/argocd-cmp-server
|
||||
image: quay.io/argoproj/argocd:v3.1.2
|
||||
image: quay.io/argoproj/argocd:v3.1.6
|
||||
name: copyutil
|
||||
securityContext:
|
||||
allowPrivilegeEscalation: false
|
||||
@@ -1986,7 +1986,7 @@ spec:
|
||||
key: server.sync.replace.allowed
|
||||
name: argocd-cmd-params-cm
|
||||
optional: true
|
||||
image: quay.io/argoproj/argocd:v3.1.2
|
||||
image: quay.io/argoproj/argocd:v3.1.6
|
||||
imagePullPolicy: Always
|
||||
livenessProbe:
|
||||
httpGet:
|
||||
@@ -2364,7 +2364,7 @@ spec:
|
||||
optional: true
|
||||
- name: KUBECACHEDIR
|
||||
value: /tmp/kubecache
|
||||
image: quay.io/argoproj/argocd:v3.1.2
|
||||
image: quay.io/argoproj/argocd:v3.1.6
|
||||
imagePullPolicy: Always
|
||||
name: argocd-application-controller
|
||||
ports:
|
||||
|
||||
@@ -3277,6 +3277,14 @@ func (app *Application) SetPostDeleteFinalizer(stage ...string) {
|
||||
setFinalizer(&app.ObjectMeta, strings.Join(append([]string{PostDeleteFinalizerName}, stage...), "/"), true)
|
||||
}
|
||||
|
||||
func (app *Application) UnSetPostDeleteFinalizerAll() {
|
||||
for _, finalizer := range app.Finalizers {
|
||||
if strings.HasPrefix(finalizer, PostDeleteFinalizerName) {
|
||||
setFinalizer(&app.ObjectMeta, finalizer, false)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (app *Application) UnSetPostDeleteFinalizer(stage ...string) {
|
||||
setFinalizer(&app.ObjectMeta, strings.Join(append([]string{PostDeleteFinalizerName}, stage...), "/"), false)
|
||||
}
|
||||
|
||||
@@ -2517,6 +2517,7 @@ func (s *Server) RunResourceAction(ctx context.Context, q *application.ResourceA
|
||||
Kind: q.Kind,
|
||||
Version: q.Version,
|
||||
Group: q.Group,
|
||||
Action: q.Action,
|
||||
Project: q.Project,
|
||||
}
|
||||
return s.RunResourceActionV2(ctx, qV2)
|
||||
|
||||
@@ -988,7 +988,21 @@ func TestNoAppEnumeration(t *testing.T) {
|
||||
assert.EqualError(t, err, "rpc error: code = NotFound desc = applications.argoproj.io \"doest-not-exist\" not found", "when the request specifies a project, we can return the standard k8s error message")
|
||||
})
|
||||
|
||||
//nolint:staticcheck,SA1019 // RunResourceAction is deprecated, but we still need to support it for backward compatibility.
|
||||
t.Run("RunResourceAction", func(t *testing.T) {
|
||||
_, err := appServer.RunResourceAction(adminCtx, &application.ResourceActionRunRequest{Name: ptr.To("test"), ResourceName: ptr.To("test"), Group: ptr.To("apps"), Kind: ptr.To("Deployment"), Namespace: ptr.To("test"), Action: ptr.To("restart")})
|
||||
require.NoError(t, err)
|
||||
_, err = appServer.RunResourceAction(noRoleCtx, &application.ResourceActionRunRequest{Name: ptr.To("test")})
|
||||
require.EqualError(t, err, common.PermissionDeniedAPIError.Error(), "error message must be _only_ the permission error, to avoid leaking information about app existence")
|
||||
_, err = appServer.RunResourceAction(noRoleCtx, &application.ResourceActionRunRequest{Group: ptr.To("argoproj.io"), Kind: ptr.To("Application"), Name: ptr.To("test")})
|
||||
require.EqualError(t, err, common.PermissionDeniedAPIError.Error(), "error message must be _only_ the permission error, to avoid leaking information about app existence")
|
||||
_, err = appServer.RunResourceAction(adminCtx, &application.ResourceActionRunRequest{Name: ptr.To("doest-not-exist")})
|
||||
require.EqualError(t, err, common.PermissionDeniedAPIError.Error(), "error message must be _only_ the permission error, to avoid leaking information about app existence")
|
||||
_, err = appServer.RunResourceAction(adminCtx, &application.ResourceActionRunRequest{Name: ptr.To("doest-not-exist"), Project: ptr.To("test")})
|
||||
assert.EqualError(t, err, "rpc error: code = NotFound desc = applications.argoproj.io \"doest-not-exist\" not found", "when the request specifies a project, we can return the standard k8s error message")
|
||||
})
|
||||
|
||||
t.Run("RunResourceActionV2", func(t *testing.T) {
|
||||
_, err := appServer.RunResourceActionV2(adminCtx, &application.ResourceActionRunRequestV2{Name: ptr.To("test"), ResourceName: ptr.To("test"), Group: ptr.To("apps"), Kind: ptr.To("Deployment"), Namespace: ptr.To("test"), Action: ptr.To("restart")})
|
||||
require.NoError(t, err)
|
||||
_, err = appServer.RunResourceActionV2(noRoleCtx, &application.ResourceActionRunRequestV2{Name: ptr.To("test")})
|
||||
|
||||
@@ -200,7 +200,7 @@ func (s *Server) Create(ctx context.Context, q *applicationset.ApplicationSetCre
|
||||
}
|
||||
|
||||
if q.GetDryRun() {
|
||||
apps, err := s.generateApplicationSetApps(ctx, log.WithField("applicationset", appset.Name), *appset, namespace)
|
||||
apps, err := s.generateApplicationSetApps(ctx, log.WithField("applicationset", appset.Name), *appset)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("unable to generate Applications of ApplicationSet: %w", err)
|
||||
}
|
||||
@@ -260,12 +260,12 @@ func (s *Server) Create(ctx context.Context, q *applicationset.ApplicationSetCre
|
||||
return updated, nil
|
||||
}
|
||||
|
||||
func (s *Server) generateApplicationSetApps(ctx context.Context, logEntry *log.Entry, appset v1alpha1.ApplicationSet, namespace string) ([]v1alpha1.Application, error) {
|
||||
func (s *Server) generateApplicationSetApps(ctx context.Context, logEntry *log.Entry, appset v1alpha1.ApplicationSet) ([]v1alpha1.Application, error) {
|
||||
argoCDDB := s.db
|
||||
|
||||
scmConfig := generators.NewSCMConfig(s.ScmRootCAPath, s.AllowedScmProviders, s.EnableScmProviders, s.EnableGitHubAPIMetrics, github_app.NewAuthCredentials(argoCDDB.(db.RepoCredsDB)), true)
|
||||
argoCDService := services.NewArgoCDService(s.db, s.GitSubmoduleEnabled, s.repoClientSet, s.EnableNewGitFileGlobbing)
|
||||
appSetGenerators := generators.GetGenerators(ctx, s.client, s.k8sClient, namespace, argoCDService, s.dynamicClient, scmConfig)
|
||||
appSetGenerators := generators.GetGenerators(ctx, s.client, s.k8sClient, s.ns, argoCDService, s.dynamicClient, scmConfig)
|
||||
|
||||
apps, _, err := appsettemplate.GenerateApplications(logEntry, appset, appSetGenerators, &appsetutils.Render{}, s.client)
|
||||
if err != nil {
|
||||
@@ -363,11 +363,15 @@ func (s *Server) Generate(ctx context.Context, q *applicationset.ApplicationSetG
|
||||
if appset == nil {
|
||||
return nil, errors.New("error creating ApplicationSets: ApplicationSets is nil in request")
|
||||
}
|
||||
namespace := s.appsetNamespaceOrDefault(appset.Namespace)
|
||||
|
||||
// The RBAC check needs to be performed against the appset namespace
|
||||
// However, when trying to generate params, the server namespace needs
|
||||
// to be passed.
|
||||
namespace := s.appsetNamespaceOrDefault(appset.Namespace)
|
||||
if !s.isNamespaceEnabled(namespace) {
|
||||
return nil, security.NamespaceNotPermittedError(namespace)
|
||||
}
|
||||
|
||||
projectName, err := s.validateAppSet(appset)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("error validating ApplicationSets: %w", err)
|
||||
@@ -380,7 +384,16 @@ func (s *Server) Generate(ctx context.Context, q *applicationset.ApplicationSetG
|
||||
logger := log.New()
|
||||
logger.SetOutput(logs)
|
||||
|
||||
apps, err := s.generateApplicationSetApps(ctx, logger.WithField("applicationset", appset.Name), *appset, namespace)
|
||||
// The server namespace will be used in the function
|
||||
// since this is the exact namespace that is being used
|
||||
// to generate parameters (especially for git generator).
|
||||
//
|
||||
// In case of Git generator, if the namespace is set to
|
||||
// appset namespace, we'll look for a project in the appset
|
||||
// namespace that would lead to error when generating params
|
||||
// for an appset in any namespace feature.
|
||||
// See https://github.com/argoproj/argo-cd/issues/22942
|
||||
apps, err := s.generateApplicationSetApps(ctx, logger.WithField("applicationset", appset.Name), *appset)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("unable to generate Applications of ApplicationSet: %w\n%s", err, logs.String())
|
||||
}
|
||||
|
||||
@@ -4,6 +4,9 @@ import (
|
||||
"sort"
|
||||
"testing"
|
||||
|
||||
"sigs.k8s.io/controller-runtime/pkg/client"
|
||||
cr_fake "sigs.k8s.io/controller-runtime/pkg/client/fake"
|
||||
|
||||
"github.com/argoproj/gitops-engine/pkg/health"
|
||||
"github.com/argoproj/pkg/v2/sync"
|
||||
"github.com/stretchr/testify/assert"
|
||||
@@ -50,7 +53,7 @@ func fakeCluster() *appsv1.Cluster {
|
||||
}
|
||||
|
||||
// return an ApplicationServiceServer which returns fake data
|
||||
func newTestAppSetServer(t *testing.T, objects ...runtime.Object) *Server {
|
||||
func newTestAppSetServer(t *testing.T, objects ...client.Object) *Server {
|
||||
t.Helper()
|
||||
f := func(enf *rbac.Enforcer) {
|
||||
_ = enf.SetBuiltinPolicy(assets.BuiltinPolicyCSV)
|
||||
@@ -61,7 +64,7 @@ func newTestAppSetServer(t *testing.T, objects ...runtime.Object) *Server {
|
||||
}
|
||||
|
||||
// return an ApplicationServiceServer which returns fake data
|
||||
func newTestNamespacedAppSetServer(t *testing.T, objects ...runtime.Object) *Server {
|
||||
func newTestNamespacedAppSetServer(t *testing.T, objects ...client.Object) *Server {
|
||||
t.Helper()
|
||||
f := func(enf *rbac.Enforcer) {
|
||||
_ = enf.SetBuiltinPolicy(assets.BuiltinPolicyCSV)
|
||||
@@ -71,7 +74,7 @@ func newTestNamespacedAppSetServer(t *testing.T, objects ...runtime.Object) *Ser
|
||||
return newTestAppSetServerWithEnforcerConfigure(t, f, scopedNamespaces, objects...)
|
||||
}
|
||||
|
||||
func newTestAppSetServerWithEnforcerConfigure(t *testing.T, f func(*rbac.Enforcer), namespace string, objects ...runtime.Object) *Server {
|
||||
func newTestAppSetServerWithEnforcerConfigure(t *testing.T, f func(*rbac.Enforcer), namespace string, objects ...client.Object) *Server {
|
||||
t.Helper()
|
||||
kubeclientset := fake.NewClientset(&corev1.ConfigMap{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
@@ -115,7 +118,11 @@ func newTestAppSetServerWithEnforcerConfigure(t *testing.T, f func(*rbac.Enforce
|
||||
|
||||
objects = append(objects, defaultProj, myProj)
|
||||
|
||||
fakeAppsClientset := apps.NewSimpleClientset(objects...)
|
||||
runtimeObjects := make([]runtime.Object, len(objects))
|
||||
for i := range objects {
|
||||
runtimeObjects[i] = objects[i]
|
||||
}
|
||||
fakeAppsClientset := apps.NewSimpleClientset(runtimeObjects...)
|
||||
factory := appinformer.NewSharedInformerFactoryWithOptions(fakeAppsClientset, 0, appinformer.WithNamespace(namespace), appinformer.WithTweakListOptions(func(_ *metav1.ListOptions) {}))
|
||||
fakeProjLister := factory.Argoproj().V1alpha1().AppProjects().Lister().AppProjects(testNamespace)
|
||||
|
||||
@@ -138,6 +145,13 @@ func newTestAppSetServerWithEnforcerConfigure(t *testing.T, f func(*rbac.Enforce
|
||||
panic("Timed out waiting for caches to sync")
|
||||
}
|
||||
|
||||
scheme := runtime.NewScheme()
|
||||
err = appsv1.AddToScheme(scheme)
|
||||
require.NoError(t, err)
|
||||
err = corev1.AddToScheme(scheme)
|
||||
require.NoError(t, err)
|
||||
crClient := cr_fake.NewClientBuilder().WithScheme(scheme).WithObjects(objects...).Build()
|
||||
|
||||
projInformer := factory.Argoproj().V1alpha1().AppProjects().Informer()
|
||||
go projInformer.Run(ctx.Done())
|
||||
if !k8scache.WaitForCacheSync(ctx.Done(), projInformer.HasSynced) {
|
||||
@@ -148,7 +162,7 @@ func newTestAppSetServerWithEnforcerConfigure(t *testing.T, f func(*rbac.Enforce
|
||||
db,
|
||||
kubeclientset,
|
||||
nil,
|
||||
nil,
|
||||
crClient,
|
||||
enforcer,
|
||||
nil,
|
||||
fakeAppsClientset,
|
||||
@@ -640,3 +654,54 @@ func TestResourceTree(t *testing.T) {
|
||||
assert.EqualError(t, err, "namespace 'NOT-ALLOWED' is not permitted")
|
||||
})
|
||||
}
|
||||
|
||||
func TestAppSet_Generate_Cluster(t *testing.T) {
|
||||
appSet1 := newTestAppSet(func(appset *appsv1.ApplicationSet) {
|
||||
appset.Name = "AppSet1"
|
||||
appset.Spec.Template.Name = "{{name}}"
|
||||
appset.Spec.Generators = []appsv1.ApplicationSetGenerator{
|
||||
{
|
||||
Clusters: &appsv1.ClusterGenerator{},
|
||||
},
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("Generate in default namespace", func(t *testing.T) {
|
||||
appSetServer := newTestAppSetServer(t, appSet1)
|
||||
appsetQuery := applicationset.ApplicationSetGenerateRequest{
|
||||
ApplicationSet: appSet1,
|
||||
}
|
||||
|
||||
res, err := appSetServer.Generate(t.Context(), &appsetQuery)
|
||||
require.NoError(t, err)
|
||||
require.Len(t, res.Applications, 2)
|
||||
assert.Equal(t, "fake-cluster", res.Applications[0].Name)
|
||||
assert.Equal(t, "in-cluster", res.Applications[1].Name)
|
||||
})
|
||||
|
||||
t.Run("Generate in different namespace", func(t *testing.T) {
|
||||
appSetServer := newTestAppSetServer(t, appSet1)
|
||||
|
||||
appSet1Ns := appSet1.DeepCopy()
|
||||
appSet1Ns.Namespace = "external-namespace"
|
||||
appsetQuery := applicationset.ApplicationSetGenerateRequest{ApplicationSet: appSet1Ns}
|
||||
|
||||
res, err := appSetServer.Generate(t.Context(), &appsetQuery)
|
||||
require.NoError(t, err)
|
||||
require.Len(t, res.Applications, 2)
|
||||
assert.Equal(t, "fake-cluster", res.Applications[0].Name)
|
||||
assert.Equal(t, "in-cluster", res.Applications[1].Name)
|
||||
})
|
||||
|
||||
t.Run("Generate in not allowed namespace", func(t *testing.T) {
|
||||
appSetServer := newTestAppSetServer(t, appSet1)
|
||||
|
||||
appSet1Ns := appSet1.DeepCopy()
|
||||
appSet1Ns.Namespace = "NOT-ALLOWED"
|
||||
|
||||
appsetQuery := applicationset.ApplicationSetGenerateRequest{ApplicationSet: appSet1Ns}
|
||||
|
||||
_, err := appSetServer.Generate(t.Context(), &appsetQuery)
|
||||
assert.EqualError(t, err, "namespace 'NOT-ALLOWED' is not permitted")
|
||||
})
|
||||
}
|
||||
|
||||
@@ -420,7 +420,8 @@ func (s *Server) Update(ctx context.Context, q *project.ProjectUpdateRequest) (*
|
||||
destCluster, err := argo.GetDestinationCluster(ctx, a.Spec.Destination, s.db)
|
||||
if err != nil {
|
||||
if err.Error() != argo.ErrDestinationMissing {
|
||||
return nil, err
|
||||
// If cluster is not found, we should discard this app, as it's most likely already in error
|
||||
continue
|
||||
}
|
||||
invalidDstCount++
|
||||
}
|
||||
|
||||
@@ -743,6 +743,35 @@ p, role:admin, projects, update, *, allow`)
|
||||
_, err := projectServer.GetSyncWindowsState(ctx, &project.SyncWindowsQuery{Name: projectWithSyncWindows.Name})
|
||||
assert.EqualError(t, err, "rpc error: code = PermissionDenied desc = permission denied: projects, get, test")
|
||||
})
|
||||
|
||||
t.Run("TestAddSyncWindowWhenAnAppReferencesAClusterThatDoesNotExist", func(t *testing.T) {
|
||||
_ = enforcer.SetBuiltinPolicy(`p, role:admin, projects, get, *, allow
|
||||
p, role:admin, projects, update, *, allow`)
|
||||
sessionMgr := session.NewSessionManager(settingsMgr, test.NewFakeProjLister(), "", nil, session.NewUserStateStorage(nil))
|
||||
projectWithAppWithInvalidCluster := existingProj.DeepCopy()
|
||||
|
||||
argoDB := db.NewDB("default", settingsMgr, kubeclientset)
|
||||
invalidApp := v1alpha1.Application{
|
||||
ObjectMeta: metav1.ObjectMeta{Name: "test-invalid", Namespace: "default"},
|
||||
Spec: v1alpha1.ApplicationSpec{Source: &v1alpha1.ApplicationSource{}, Project: "test", Destination: v1alpha1.ApplicationDestination{Namespace: "ns3", Server: "https://server4"}},
|
||||
}
|
||||
projectServer := NewServer("default", fake.NewSimpleClientset(), apps.NewSimpleClientset(projectWithAppWithInvalidCluster, &invalidApp), enforcer, sync.NewKeyLock(), sessionMgr, nil, projInformer, settingsMgr, argoDB, testEnableEventList)
|
||||
|
||||
// Add sync window
|
||||
syncWindow := v1alpha1.SyncWindow{
|
||||
Kind: "deny",
|
||||
Schedule: "* * * * *",
|
||||
Duration: "1h",
|
||||
Applications: []string{"*"},
|
||||
Clusters: []string{"*"},
|
||||
}
|
||||
projectWithAppWithInvalidCluster.Spec.SyncWindows = append(projectWithAppWithInvalidCluster.Spec.SyncWindows, &syncWindow)
|
||||
res, err := projectServer.Update(ctx, &project.ProjectUpdateRequest{
|
||||
Project: projectWithAppWithInvalidCluster,
|
||||
})
|
||||
require.NoError(t, err)
|
||||
assert.Len(t, res.Spec.SyncWindows, 1)
|
||||
})
|
||||
}
|
||||
|
||||
func newEnforcer(kubeclientset *fake.Clientset) *rbac.Enforcer {
|
||||
|
||||
@@ -1238,7 +1238,7 @@ func (server *ArgoCDServer) newHTTPServer(ctx context.Context, port int, grpcWeb
|
||||
|
||||
// Webhook handler for git events (Note: cache timeouts are hardcoded because API server does not write to cache and not really using them)
|
||||
argoDB := db.NewDB(server.Namespace, server.settingsMgr, server.KubeClientset)
|
||||
acdWebhookHandler := webhook.NewHandler(server.Namespace, server.ApplicationNamespaces, server.WebhookParallelism, server.AppClientset, server.settings, server.settingsMgr, server.RepoServerCache, server.Cache, argoDB, server.settingsMgr.GetMaxWebhookPayloadSize())
|
||||
acdWebhookHandler := webhook.NewHandler(server.Namespace, server.ApplicationNamespaces, server.WebhookParallelism, server.AppClientset, server.appLister, server.settings, server.settingsMgr, server.RepoServerCache, server.Cache, argoDB, server.settingsMgr.GetMaxWebhookPayloadSize())
|
||||
|
||||
mux.HandleFunc("/api/webhook", acdWebhookHandler.Handler)
|
||||
|
||||
|
||||
@@ -212,7 +212,7 @@ func PushChartToOCIRegistry(t *testing.T, chartPathName, chartName, chartVersion
|
||||
require.NoError(t, err1)
|
||||
defer func() { _ = os.RemoveAll(tempDest) }()
|
||||
|
||||
chartAbsPath, err2 := filepath.Abs("./testdata/" + chartPathName)
|
||||
chartAbsPath, err2 := filepath.Abs("./" + chartPathName)
|
||||
require.NoError(t, err2)
|
||||
|
||||
t.Setenv("HELM_EXPERIMENTAL_OCI", "1")
|
||||
@@ -236,7 +236,7 @@ func PushChartToAuthenticatedOCIRegistry(t *testing.T, chartPathName, chartName,
|
||||
require.NoError(t, err1)
|
||||
defer func() { _ = os.RemoveAll(tempDest) }()
|
||||
|
||||
chartAbsPath, err2 := filepath.Abs("./testdata/" + chartPathName)
|
||||
chartAbsPath, err2 := filepath.Abs("./" + chartPathName)
|
||||
require.NoError(t, err2)
|
||||
|
||||
t.Setenv("HELM_EXPERIMENTAL_OCI", "1")
|
||||
@@ -274,13 +274,13 @@ func PushChartToAuthenticatedOCIRegistry(t *testing.T, chartPathName, chartName,
|
||||
// PushImageToOCIRegistry adds a helm chart to helm OCI registry
|
||||
func PushImageToOCIRegistry(t *testing.T, pathName, tag string) {
|
||||
t.Helper()
|
||||
imagePath := "./testdata/" + pathName
|
||||
imagePath := "./" + pathName
|
||||
|
||||
errors.NewHandler(t).FailOnErr(fixture.Run(
|
||||
imagePath,
|
||||
"oras",
|
||||
"push",
|
||||
fmt.Sprintf("%s:%s", fmt.Sprintf("%s/%s", strings.TrimPrefix(fixture.OCIHostURL, "oci://"), pathName), tag),
|
||||
fmt.Sprintf("%s:%s", fmt.Sprintf("%s/%s", strings.TrimPrefix(fixture.OCIHostURL, "oci://"), filepath.Base(pathName)), tag),
|
||||
".",
|
||||
))
|
||||
}
|
||||
@@ -288,13 +288,13 @@ func PushImageToOCIRegistry(t *testing.T, pathName, tag string) {
|
||||
// PushImageToAuthenticatedOCIRegistry adds a helm chart to helm OCI registry
|
||||
func PushImageToAuthenticatedOCIRegistry(t *testing.T, pathName, tag string) {
|
||||
t.Helper()
|
||||
imagePath := "./testdata/" + pathName
|
||||
imagePath := "./" + pathName
|
||||
|
||||
errors.NewHandler(t).FailOnErr(fixture.Run(
|
||||
imagePath,
|
||||
"oras",
|
||||
"push",
|
||||
fmt.Sprintf("%s:%s", fmt.Sprintf("%s/%s", strings.TrimPrefix(fixture.AuthenticatedOCIHostURL, "oci://"), pathName), tag),
|
||||
fmt.Sprintf("%s:%s", fmt.Sprintf("%s/%s", strings.TrimPrefix(fixture.AuthenticatedOCIHostURL, "oci://"), filepath.Base(pathName)), tag),
|
||||
".",
|
||||
))
|
||||
}
|
||||
|
||||
@@ -552,7 +552,7 @@ func TestHelmRepoDiffLocal(t *testing.T) {
|
||||
|
||||
func TestHelmOCIRegistry(t *testing.T) {
|
||||
Given(t).
|
||||
PushChartToOCIRegistry("helm-values", "helm-values", "1.0.0").
|
||||
PushChartToOCIRegistry("testdata/helm-values", "helm-values", "1.0.0").
|
||||
HelmOCIRepoAdded("myrepo").
|
||||
RepoURLType(fixture.RepoURLTypeHelmOCI).
|
||||
Chart("helm-values").
|
||||
@@ -570,7 +570,7 @@ func TestHelmOCIRegistry(t *testing.T) {
|
||||
|
||||
func TestGitWithHelmOCIRegistryDependencies(t *testing.T) {
|
||||
Given(t).
|
||||
PushChartToOCIRegistry("helm-values", "helm-values", "1.0.0").
|
||||
PushChartToOCIRegistry("testdata/helm-values", "helm-values", "1.0.0").
|
||||
HelmOCIRepoAdded("myrepo").
|
||||
Path("helm-oci-with-dependencies").
|
||||
When().
|
||||
@@ -586,8 +586,8 @@ func TestGitWithHelmOCIRegistryDependencies(t *testing.T) {
|
||||
|
||||
func TestHelmOCIRegistryWithDependencies(t *testing.T) {
|
||||
Given(t).
|
||||
PushChartToOCIRegistry("helm-values", "helm-values", "1.0.0").
|
||||
PushChartToOCIRegistry("helm-oci-with-dependencies", "helm-oci-with-dependencies", "1.0.0").
|
||||
PushChartToOCIRegistry("testdata/helm-values", "helm-values", "1.0.0").
|
||||
PushChartToOCIRegistry("testdata/helm-oci-with-dependencies", "helm-oci-with-dependencies", "1.0.0").
|
||||
HelmOCIRepoAdded("myrepo").
|
||||
RepoURLType(fixture.RepoURLTypeHelmOCI).
|
||||
Chart("helm-oci-with-dependencies").
|
||||
@@ -605,7 +605,7 @@ func TestHelmOCIRegistryWithDependencies(t *testing.T) {
|
||||
|
||||
func TestTemplatesGitWithHelmOCIDependencies(t *testing.T) {
|
||||
Given(t).
|
||||
PushChartToOCIRegistry("helm-values", "helm-values", "1.0.0").
|
||||
PushChartToOCIRegistry("testdata/helm-values", "helm-values", "1.0.0").
|
||||
HelmoOCICredentialsWithoutUserPassAdded().
|
||||
Path("helm-oci-with-dependencies").
|
||||
When().
|
||||
@@ -621,8 +621,8 @@ func TestTemplatesGitWithHelmOCIDependencies(t *testing.T) {
|
||||
|
||||
func TestTemplatesHelmOCIWithDependencies(t *testing.T) {
|
||||
Given(t).
|
||||
PushChartToOCIRegistry("helm-values", "helm-values", "1.0.0").
|
||||
PushChartToOCIRegistry("helm-oci-with-dependencies", "helm-oci-with-dependencies", "1.0.0").
|
||||
PushChartToOCIRegistry("testdata/helm-values", "helm-values", "1.0.0").
|
||||
PushChartToOCIRegistry("testdata/helm-oci-with-dependencies", "helm-oci-with-dependencies", "1.0.0").
|
||||
HelmoOCICredentialsWithoutUserPassAdded().
|
||||
RepoURLType(fixture.RepoURLTypeHelmOCI).
|
||||
Chart("helm-oci-with-dependencies").
|
||||
|
||||
@@ -14,7 +14,7 @@ import (
|
||||
func TestOCIImage(t *testing.T) {
|
||||
Given(t).
|
||||
RepoURLType(fixture.RepoURLTypeOCI).
|
||||
PushImageToOCIRegistry("guestbook", "1.0.0").
|
||||
PushImageToOCIRegistry("testdata/guestbook", "1.0.0").
|
||||
OCIRepoAdded("guestbook", "guestbook").
|
||||
Revision("1.0.0").
|
||||
OCIRegistry(fixture.OCIHostURL).
|
||||
@@ -37,8 +37,8 @@ func TestOCIImage(t *testing.T) {
|
||||
func TestOCIWithOCIHelmRegistryDependencies(t *testing.T) {
|
||||
Given(t).
|
||||
RepoURLType(fixture.RepoURLTypeOCI).
|
||||
PushChartToOCIRegistry("helm-values", "helm-values", "1.0.0").
|
||||
PushImageToOCIRegistry("helm-oci-with-dependencies", "1.0.0").
|
||||
PushChartToOCIRegistry("testdata/helm-values", "helm-values", "1.0.0").
|
||||
PushImageToOCIRegistry("testdata/helm-oci-with-dependencies", "1.0.0").
|
||||
OCIRegistry(fixture.OCIHostURL).
|
||||
OCIRepoAdded("helm-oci-with-dependencies", "helm-oci-with-dependencies").
|
||||
OCIRegistryPath("helm-oci-with-dependencies").
|
||||
@@ -58,8 +58,8 @@ func TestOCIWithOCIHelmRegistryDependencies(t *testing.T) {
|
||||
func TestOCIWithAuthedOCIHelmRegistryDeps(t *testing.T) {
|
||||
Given(t).
|
||||
RepoURLType(fixture.RepoURLTypeOCI).
|
||||
PushChartToAuthenticatedOCIRegistry("helm-values", "helm-values", "1.0.0").
|
||||
PushImageToOCIRegistry("helm-oci-authed-with-dependencies", "1.0.0").
|
||||
PushChartToAuthenticatedOCIRegistry("testdata/helm-values", "helm-values", "1.0.0").
|
||||
PushImageToOCIRegistry("testdata/helm-oci-authed-with-dependencies", "1.0.0").
|
||||
OCIRepoAdded("helm-oci-authed-with-dependencies", "helm-oci-authed-with-dependencies").
|
||||
AuthenticatedOCIRepoAdded("helm-values", "myrepo/helm-values").
|
||||
OCIRegistry(fixture.OCIHostURL).
|
||||
@@ -76,3 +76,19 @@ func TestOCIWithAuthedOCIHelmRegistryDeps(t *testing.T) {
|
||||
Expect(HealthIs(health.HealthStatusHealthy)).
|
||||
Expect(SyncStatusIs(SyncStatusCodeSynced))
|
||||
}
|
||||
|
||||
func TestOCIImageWithOutOfBoundsSymlink(t *testing.T) {
|
||||
Given(t).
|
||||
RepoURLType(fixture.RepoURLTypeOCI).
|
||||
PushImageToOCIRegistry("testdata3/symlink-out-of-bounds", "1.0.0").
|
||||
OCIRepoAdded("symlink-out-of-bounds", "symlink-out-of-bounds").
|
||||
Revision("1.0.0").
|
||||
OCIRegistry(fixture.OCIHostURL).
|
||||
OCIRegistryPath("symlink-out-of-bounds").
|
||||
Path(".").
|
||||
When().
|
||||
IgnoreErrors().
|
||||
CreateApp().
|
||||
Then().
|
||||
Expect(Error("", "could not decompress layer: illegal filepath in symlink"))
|
||||
}
|
||||
|
||||
1
test/e2e/testdata3/README.md
Normal file
1
test/e2e/testdata3/README.md
Normal file
@@ -0,0 +1 @@
|
||||
The out-of-bounds symlinks can negatively affect the other testcases, therefore it is separated in its own testdata directory.
|
||||
1
test/e2e/testdata3/symlink-out-of-bounds/out-of-bounds
Symbolic link
1
test/e2e/testdata3/symlink-out-of-bounds/out-of-bounds
Symbolic link
@@ -0,0 +1 @@
|
||||
..
|
||||
@@ -49,7 +49,7 @@ func ReceiveRepoStream(ctx context.Context, receiver StreamReceiver, destDir str
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("error receiving tgz file: %w", err)
|
||||
}
|
||||
err = files.Untgz(destDir, tgzFile, math.MaxInt64, preserveFileMode)
|
||||
err = files.Untgz(destDir, tgzFile, math.MaxInt64, preserveFileMode, false)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("error decompressing tgz file: %w", err)
|
||||
}
|
||||
|
||||
@@ -135,7 +135,7 @@ func untarChart(tempDir string, cachedChartPath string, manifestMaxExtractedSize
|
||||
if err != nil {
|
||||
return fmt.Errorf("error opening cached chart path %s: %w", cachedChartPath, err)
|
||||
}
|
||||
return files.Untgz(tempDir, reader, manifestMaxExtractedSize, false)
|
||||
return files.Untgz(tempDir, reader, manifestMaxExtractedSize, false, false)
|
||||
}
|
||||
|
||||
func (c *nativeHelmChart) ExtractChart(chart string, version string, passCredentials bool, manifestMaxExtractedSize int64, disableManifestMaxExtractedSize bool) (string, utilio.Closer, error) {
|
||||
|
||||
@@ -71,7 +71,7 @@ func writeFile(srcPath string, inclusions []string, exclusions []string, writer
|
||||
// - a full path
|
||||
// - points to an empty directory or
|
||||
// - points to a non-existing directory
|
||||
func Untgz(dstPath string, r io.Reader, maxSize int64, preserveFileMode bool) error {
|
||||
func Untgz(dstPath string, r io.Reader, maxSize int64, preserveFileMode bool, relativizeSymlinks bool) error {
|
||||
if !filepath.IsAbs(dstPath) {
|
||||
return fmt.Errorf("dstPath points to a relative path: %s", dstPath)
|
||||
}
|
||||
@@ -81,7 +81,7 @@ func Untgz(dstPath string, r io.Reader, maxSize int64, preserveFileMode bool) er
|
||||
return fmt.Errorf("error reading file: %w", err)
|
||||
}
|
||||
defer gzr.Close()
|
||||
return untar(dstPath, io.LimitReader(gzr, maxSize), preserveFileMode)
|
||||
return untar(dstPath, io.LimitReader(gzr, maxSize), preserveFileMode, relativizeSymlinks)
|
||||
}
|
||||
|
||||
// Untar will loop over the tar reader creating the file structure at dstPath.
|
||||
@@ -89,12 +89,12 @@ func Untgz(dstPath string, r io.Reader, maxSize int64, preserveFileMode bool) er
|
||||
// - a full path
|
||||
// - points to an empty directory or
|
||||
// - points to a non-existing directory
|
||||
func Untar(dstPath string, r io.Reader, maxSize int64, preserveFileMode bool) error {
|
||||
func Untar(dstPath string, r io.Reader, maxSize int64, preserveFileMode bool, relativizeSymlinks bool) error {
|
||||
if !filepath.IsAbs(dstPath) {
|
||||
return fmt.Errorf("dstPath points to a relative path: %s", dstPath)
|
||||
}
|
||||
|
||||
return untar(dstPath, io.LimitReader(r, maxSize), preserveFileMode)
|
||||
return untar(dstPath, io.LimitReader(r, maxSize), preserveFileMode, relativizeSymlinks)
|
||||
}
|
||||
|
||||
// untar will loop over the tar reader creating the file structure at dstPath.
|
||||
@@ -102,7 +102,7 @@ func Untar(dstPath string, r io.Reader, maxSize int64, preserveFileMode bool) er
|
||||
// - a full path
|
||||
// - points to an empty directory or
|
||||
// - points to a non existing directory
|
||||
func untar(dstPath string, r io.Reader, preserveFileMode bool) error {
|
||||
func untar(dstPath string, r io.Reader, preserveFileMode bool, relativizeSymlinks bool) error {
|
||||
tr := tar.NewReader(r)
|
||||
|
||||
for {
|
||||
@@ -136,16 +136,27 @@ func untar(dstPath string, r io.Reader, preserveFileMode bool) error {
|
||||
case tar.TypeSymlink:
|
||||
// Sanity check to protect against symlink exploit
|
||||
linkTarget := filepath.Join(filepath.Dir(target), header.Linkname)
|
||||
realPath, err := filepath.EvalSymlinks(linkTarget)
|
||||
realLinkTarget, err := filepath.EvalSymlinks(linkTarget)
|
||||
if os.IsNotExist(err) {
|
||||
realPath = linkTarget
|
||||
realLinkTarget = linkTarget
|
||||
} else if err != nil {
|
||||
return fmt.Errorf("error checking symlink realpath: %w", err)
|
||||
}
|
||||
if !Inbound(realPath, dstPath) {
|
||||
if !Inbound(realLinkTarget, dstPath) {
|
||||
return fmt.Errorf("illegal filepath in symlink: %s", linkTarget)
|
||||
}
|
||||
err = os.Symlink(realPath, target)
|
||||
|
||||
if relativizeSymlinks {
|
||||
// Relativizing all symlink targets because path.CheckOutOfBoundsSymlinks disallows any absolute symlinks
|
||||
// and it makes more sense semantically to view symlinks in archives as relative.
|
||||
// Inbound ensures that we never allow symlinks that break out of the target directory.
|
||||
realLinkTarget, err = filepath.Rel(filepath.Dir(target), realLinkTarget)
|
||||
if err != nil {
|
||||
return fmt.Errorf("error relativizing link target: %w", err)
|
||||
}
|
||||
}
|
||||
|
||||
err = os.Symlink(realLinkTarget, target)
|
||||
if err != nil {
|
||||
return fmt.Errorf("error creating symlink: %w", err)
|
||||
}
|
||||
|
||||
@@ -159,7 +159,7 @@ func TestUntgz(t *testing.T) {
|
||||
destDir := filepath.Join(tmpDir, "untgz1")
|
||||
|
||||
// when
|
||||
err := files.Untgz(destDir, tgzFile, math.MaxInt64, false)
|
||||
err := files.Untgz(destDir, tgzFile, math.MaxInt64, false, false)
|
||||
|
||||
// then
|
||||
require.NoError(t, err)
|
||||
@@ -182,7 +182,23 @@ func TestUntgz(t *testing.T) {
|
||||
destDir := filepath.Join(tmpDir, "untgz2")
|
||||
|
||||
// when
|
||||
err := files.Untgz(destDir, tgzFile, math.MaxInt64, false)
|
||||
err := files.Untgz(destDir, tgzFile, math.MaxInt64, false, false)
|
||||
|
||||
// then
|
||||
assert.ErrorContains(t, err, "illegal filepath in symlink")
|
||||
})
|
||||
t.Run("will protect against symlink exploit when relativizing symlinks", func(t *testing.T) {
|
||||
// given
|
||||
tmpDir := createTmpDir(t)
|
||||
defer deleteTmpDir(t, tmpDir)
|
||||
tgzFile := createTgz(t, filepath.Join(getTestDataDir(t), "symlink-exploit"), tmpDir)
|
||||
|
||||
defer tgzFile.Close()
|
||||
|
||||
destDir := filepath.Join(tmpDir, "untgz2")
|
||||
|
||||
// when
|
||||
err := files.Untgz(destDir, tgzFile, math.MaxInt64, false, true)
|
||||
|
||||
// then
|
||||
assert.ErrorContains(t, err, "illegal filepath in symlink")
|
||||
@@ -198,14 +214,31 @@ func TestUntgz(t *testing.T) {
|
||||
destDir := filepath.Join(tmpDir, "untgz1")
|
||||
|
||||
// when
|
||||
err := files.Untgz(destDir, tgzFile, math.MaxInt64, false)
|
||||
err := files.Untgz(destDir, tgzFile, math.MaxInt64, true, false)
|
||||
require.NoError(t, err)
|
||||
|
||||
// then
|
||||
|
||||
scriptFileInfo, err := os.Stat(path.Join(destDir, "script.sh"))
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, os.FileMode(0o644), scriptFileInfo.Mode())
|
||||
assert.Equal(t, os.FileMode(0o755), scriptFileInfo.Mode())
|
||||
})
|
||||
t.Run("relativizes symlinks", func(t *testing.T) {
|
||||
// given
|
||||
tmpDir := createTmpDir(t)
|
||||
defer deleteTmpDir(t, tmpDir)
|
||||
tgzFile := createTgz(t, getTestAppDir(t), tmpDir)
|
||||
defer tgzFile.Close()
|
||||
|
||||
destDir := filepath.Join(tmpDir, "symlink-relativize")
|
||||
|
||||
// when
|
||||
err := files.Untgz(destDir, tgzFile, math.MaxInt64, false, true)
|
||||
|
||||
// then
|
||||
require.NoError(t, err)
|
||||
names := readFiles(t, destDir)
|
||||
assert.Equal(t, "../README.md", names["applicationset/readme-symlink"])
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
@@ -183,7 +183,7 @@ func ReceiveManifestFileStream(ctx context.Context, receiver RepoStreamReceiver,
|
||||
if err != nil {
|
||||
return nil, nil, fmt.Errorf("error receiving tgz file: %w", err)
|
||||
}
|
||||
err = files.Untgz(destDir, tgzFile, maxExtractedSize, false)
|
||||
err = files.Untgz(destDir, tgzFile, maxExtractedSize, false, false)
|
||||
if err != nil {
|
||||
return nil, nil, fmt.Errorf("error decompressing tgz file: %w", err)
|
||||
}
|
||||
|
||||
@@ -232,12 +232,28 @@ func (c *nativeOCIClient) Extract(ctx context.Context, digest string) (string, u
|
||||
return "", nil, err
|
||||
}
|
||||
|
||||
if len(ociManifest.Layers) != 1 {
|
||||
return "", nil, fmt.Errorf("expected only a single oci layer, got %d", len(ociManifest.Layers))
|
||||
// Add a guard to defend against a ridiculous amount of layers. No idea what a good amount is, but normally we
|
||||
// shouldn't expect more than 2-3 in most real world use cases.
|
||||
if len(ociManifest.Layers) > 10 {
|
||||
return "", nil, fmt.Errorf("expected no more than 10 oci layers, got %d", len(ociManifest.Layers))
|
||||
}
|
||||
|
||||
if !slices.Contains(c.allowedMediaTypes, ociManifest.Layers[0].MediaType) {
|
||||
return "", nil, fmt.Errorf("oci layer media type %s is not in the list of allowed media types", ociManifest.Layers[0].MediaType)
|
||||
contentLayers := 0
|
||||
|
||||
// Strictly speaking we only allow for a single content layer. There are images which contains extra layers, such
|
||||
// as provenance/attestation layers. Pending a better story to do this natively, we will skip such layers for now.
|
||||
for _, layer := range ociManifest.Layers {
|
||||
if isContentLayer(layer.MediaType) {
|
||||
if !slices.Contains(c.allowedMediaTypes, layer.MediaType) {
|
||||
return "", nil, fmt.Errorf("oci layer media type %s is not in the list of allowed media types", layer.MediaType)
|
||||
}
|
||||
|
||||
contentLayers++
|
||||
}
|
||||
}
|
||||
|
||||
if contentLayers != 1 {
|
||||
return "", nil, fmt.Errorf("expected only a single oci content layer, got %d", contentLayers)
|
||||
}
|
||||
|
||||
err = saveCompressedImageToPath(ctx, digest, c.repo, cachedPath)
|
||||
@@ -405,7 +421,15 @@ func fileExists(filePath string) (bool, error) {
|
||||
return true, nil
|
||||
}
|
||||
|
||||
// TODO: A content layer could in theory be something that is not a compressed file, e.g a single yaml file or like.
|
||||
// While IMO the utility in the context of Argo CD is limited, I'd at least like to make it known here and add an extensibility
|
||||
// point for it in case we decide to loosen the current requirements.
|
||||
func isContentLayer(mediaType string) bool {
|
||||
return isCompressedLayer(mediaType)
|
||||
}
|
||||
|
||||
func isCompressedLayer(mediaType string) bool {
|
||||
// TODO: Is zstd something which is used in the wild? For now let's stick to these suffixes
|
||||
return strings.HasSuffix(mediaType, "tar+gzip") || strings.HasSuffix(mediaType, "tar")
|
||||
}
|
||||
|
||||
@@ -500,7 +524,7 @@ func isHelmOCI(mediaType string) bool {
|
||||
// Push looks in all the layers of an OCI image. Once it finds a layer that is compressed, it extracts the layer to a tempDir
|
||||
// and then renames the temp dir to the directory where the repo-server expects to find k8s manifests.
|
||||
func (s *compressedLayerExtracterStore) Push(ctx context.Context, desc imagev1.Descriptor, content io.Reader) error {
|
||||
if isCompressedLayer(desc.MediaType) {
|
||||
if isContentLayer(desc.MediaType) {
|
||||
srcDir, err := files.CreateTempDir(os.TempDir())
|
||||
if err != nil {
|
||||
return err
|
||||
@@ -508,9 +532,9 @@ func (s *compressedLayerExtracterStore) Push(ctx context.Context, desc imagev1.D
|
||||
defer os.RemoveAll(srcDir)
|
||||
|
||||
if strings.HasSuffix(desc.MediaType, "tar+gzip") {
|
||||
err = files.Untgz(srcDir, content, s.maxSize, false)
|
||||
err = files.Untgz(srcDir, content, s.maxSize, false, true)
|
||||
} else {
|
||||
err = files.Untar(srcDir, content, s.maxSize, false)
|
||||
err = files.Untar(srcDir, content, s.maxSize, false, true)
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
|
||||
@@ -122,7 +122,7 @@ func Test_nativeOCIClient_Extract(t *testing.T) {
|
||||
expectedError: errors.New("cannot extract contents of oci image with revision sha256:1b6dfd71e2b35c2f35dffc39007c2276f3c0e235cbae4c39cba74bd406174e22: failed to perform \"Push\" on destination: could not decompress layer: error while iterating on tar reader: unexpected EOF"),
|
||||
},
|
||||
{
|
||||
name: "extraction fails due to multiple layers",
|
||||
name: "extraction fails due to multiple content layers",
|
||||
fields: fields{
|
||||
allowedMediaTypes: []string{imagev1.MediaTypeImageLayerGzip},
|
||||
},
|
||||
@@ -135,7 +135,21 @@ func Test_nativeOCIClient_Extract(t *testing.T) {
|
||||
manifestMaxExtractedSize: 1000,
|
||||
disableManifestMaxExtractedSize: false,
|
||||
},
|
||||
expectedError: errors.New("expected only a single oci layer, got 2"),
|
||||
expectedError: errors.New("expected only a single oci content layer, got 2"),
|
||||
},
|
||||
{
|
||||
name: "extraction with multiple layers, but just a single content layer",
|
||||
fields: fields{
|
||||
allowedMediaTypes: []string{imagev1.MediaTypeImageLayerGzip},
|
||||
},
|
||||
args: args{
|
||||
digestFunc: func(store *memory.Store) string {
|
||||
layerBlob := createGzippedTarWithContent(t, "some-path", "some content")
|
||||
return generateManifest(t, store, layerConf{content.NewDescriptorFromBytes(imagev1.MediaTypeImageLayerGzip, layerBlob), layerBlob}, layerConf{content.NewDescriptorFromBytes("application/vnd.cncf.helm.chart.provenance.v1.prov", []byte{}), []byte{}})
|
||||
},
|
||||
manifestMaxExtractedSize: 1000,
|
||||
disableManifestMaxExtractedSize: false,
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "extraction fails due to invalid media type",
|
||||
|
||||
@@ -13,6 +13,9 @@ import (
|
||||
"time"
|
||||
|
||||
bb "github.com/ktrysmt/go-bitbucket"
|
||||
"k8s.io/apimachinery/pkg/labels"
|
||||
|
||||
alpha1 "github.com/argoproj/argo-cd/v3/pkg/client/listers/application/v1alpha1"
|
||||
|
||||
"github.com/Masterminds/semver/v3"
|
||||
"github.com/go-playground/webhooks/v6/azuredevops"
|
||||
@@ -23,7 +26,6 @@ import (
|
||||
"github.com/go-playground/webhooks/v6/gogs"
|
||||
gogsclient "github.com/gogits/go-gogs-client"
|
||||
log "github.com/sirupsen/logrus"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
|
||||
"github.com/argoproj/argo-cd/v3/common"
|
||||
"github.com/argoproj/argo-cd/v3/pkg/apis/application/v1alpha1"
|
||||
@@ -60,6 +62,7 @@ type ArgoCDWebhookHandler struct {
|
||||
ns string
|
||||
appNs []string
|
||||
appClientset appclientset.Interface
|
||||
appsLister alpha1.ApplicationLister
|
||||
github *github.Webhook
|
||||
gitlab *gitlab.Webhook
|
||||
bitbucket *bitbucket.Webhook
|
||||
@@ -72,7 +75,7 @@ type ArgoCDWebhookHandler struct {
|
||||
maxWebhookPayloadSizeB int64
|
||||
}
|
||||
|
||||
func NewHandler(namespace string, applicationNamespaces []string, webhookParallelism int, appClientset appclientset.Interface, set *settings.ArgoCDSettings, settingsSrc settingsSource, repoCache *cache.Cache, serverCache *servercache.Cache, argoDB db.ArgoDB, maxWebhookPayloadSizeB int64) *ArgoCDWebhookHandler {
|
||||
func NewHandler(namespace string, applicationNamespaces []string, webhookParallelism int, appClientset appclientset.Interface, appsLister alpha1.ApplicationLister, set *settings.ArgoCDSettings, settingsSrc settingsSource, repoCache *cache.Cache, serverCache *servercache.Cache, argoDB db.ArgoDB, maxWebhookPayloadSizeB int64) *ArgoCDWebhookHandler {
|
||||
githubWebhook, err := github.New(github.Options.Secret(set.WebhookGitHubSecret))
|
||||
if err != nil {
|
||||
log.Warnf("Unable to init the GitHub webhook")
|
||||
@@ -115,6 +118,7 @@ func NewHandler(namespace string, applicationNamespaces []string, webhookParalle
|
||||
db: argoDB,
|
||||
queue: make(chan any, payloadQueueSize),
|
||||
maxWebhookPayloadSizeB: maxWebhookPayloadSizeB,
|
||||
appsLister: appsLister,
|
||||
}
|
||||
|
||||
acdWebhook.startWorkerPool(webhookParallelism)
|
||||
@@ -313,8 +317,8 @@ func (a *ArgoCDWebhookHandler) HandleEvent(payload any) {
|
||||
nsFilter = ""
|
||||
}
|
||||
|
||||
appIf := a.appClientset.ArgoprojV1alpha1().Applications(nsFilter)
|
||||
apps, err := appIf.List(context.Background(), metav1.ListOptions{})
|
||||
appIf := a.appsLister.Applications(nsFilter)
|
||||
apps, err := appIf.List(labels.Everything())
|
||||
if err != nil {
|
||||
log.Warnf("Failed to list applications: %v", err)
|
||||
return
|
||||
@@ -339,9 +343,9 @@ func (a *ArgoCDWebhookHandler) HandleEvent(payload any) {
|
||||
// Skip any application that is neither in the control plane's namespace
|
||||
// nor in the list of enabled namespaces.
|
||||
var filteredApps []v1alpha1.Application
|
||||
for _, app := range apps.Items {
|
||||
for _, app := range apps {
|
||||
if app.Namespace == a.ns || glob.MatchStringInList(a.appNs, app.Namespace, glob.REGEXP) {
|
||||
filteredApps = append(filteredApps, app)
|
||||
filteredApps = append(filteredApps, *app)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -17,6 +17,7 @@ import (
|
||||
|
||||
bb "github.com/ktrysmt/go-bitbucket"
|
||||
"github.com/stretchr/testify/mock"
|
||||
"k8s.io/apimachinery/pkg/labels"
|
||||
"k8s.io/apimachinery/pkg/types"
|
||||
|
||||
"github.com/go-playground/webhooks/v6/bitbucket"
|
||||
@@ -28,6 +29,7 @@ import (
|
||||
"k8s.io/apimachinery/pkg/runtime"
|
||||
kubetesting "k8s.io/client-go/testing"
|
||||
|
||||
argov1 "github.com/argoproj/argo-cd/v3/pkg/client/listers/application/v1alpha1"
|
||||
servercache "github.com/argoproj/argo-cd/v3/server/cache"
|
||||
"github.com/argoproj/argo-cd/v3/util/cache/appstate"
|
||||
"github.com/argoproj/argo-cd/v3/util/db"
|
||||
@@ -98,6 +100,31 @@ func NewMockHandlerForBitbucketCallback(reactor *reactorDef, applicationNamespac
|
||||
return newMockHandler(reactor, applicationNamespaces, defaultMaxPayloadSize, &mockDB, &argoSettings, objects...)
|
||||
}
|
||||
|
||||
type fakeAppsLister struct {
|
||||
argov1.ApplicationLister
|
||||
argov1.ApplicationNamespaceLister
|
||||
namespace string
|
||||
clientset *appclientset.Clientset
|
||||
}
|
||||
|
||||
func (f *fakeAppsLister) Applications(namespace string) argov1.ApplicationNamespaceLister {
|
||||
return &fakeAppsLister{namespace: namespace, clientset: f.clientset}
|
||||
}
|
||||
|
||||
func (f *fakeAppsLister) List(selector labels.Selector) ([]*v1alpha1.Application, error) {
|
||||
res, err := f.clientset.ArgoprojV1alpha1().Applications(f.namespace).List(context.Background(), metav1.ListOptions{
|
||||
LabelSelector: selector.String(),
|
||||
})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
var apps []*v1alpha1.Application
|
||||
for i := range res.Items {
|
||||
apps = append(apps, &res.Items[i])
|
||||
}
|
||||
return apps, nil
|
||||
}
|
||||
|
||||
func newMockHandler(reactor *reactorDef, applicationNamespaces []string, maxPayloadSize int64, argoDB db.ArgoDB, argoSettings *settings.ArgoCDSettings, objects ...runtime.Object) *ArgoCDWebhookHandler {
|
||||
appClientset := appclientset.NewSimpleClientset(objects...)
|
||||
if reactor != nil {
|
||||
@@ -109,8 +136,7 @@ func newMockHandler(reactor *reactorDef, applicationNamespaces []string, maxPayl
|
||||
appClientset.AddReactor(reactor.verb, reactor.resource, reactor.reaction)
|
||||
}
|
||||
cacheClient := cacheutil.NewCache(cacheutil.NewInMemoryCache(1 * time.Hour))
|
||||
|
||||
return NewHandler("argocd", applicationNamespaces, 10, appClientset, argoSettings, &fakeSettingsSrc{}, cache.NewCache(
|
||||
return NewHandler("argocd", applicationNamespaces, 10, appClientset, &fakeAppsLister{clientset: appClientset}, argoSettings, &fakeSettingsSrc{}, cache.NewCache(
|
||||
cacheClient,
|
||||
1*time.Minute,
|
||||
1*time.Minute,
|
||||
|
||||
Reference in New Issue
Block a user