Compare commits

...

11 Commits

Author SHA1 Message Date
argo-bot
ecc2af9dca Bump version to 2.3.2 2022-03-23 00:32:13 +00:00
argo-bot
c5b0279050 Bump version to 2.3.2 2022-03-23 00:31:57 +00:00
Michael Crenshaw
6df17e7c56 chore: fix imports (#8859)
Signed-off-by: Michael Crenshaw <michael@crenshaw.dev>
2022-03-22 12:55:09 -07:00
Michael Crenshaw
e55ecf9107 chore: remove lint-docs CI task (#8722) (#8858)
* chore: remove lint-docs CI task

Signed-off-by: Alexander Matyushentsev <AMatyushentsev@gmail.com>

* chore: remove not longer necessary url-allow-list

Signed-off-by: Alexander Matyushentsev <AMatyushentsev@gmail.com>

Co-authored-by: Alexander Matyushentsev <AMatyushentsev@gmail.com>
2022-03-22 12:54:37 -07:00
Alexander Matyushentsev
21f208f17e Merge pull request from GHSA-2f5v-8r3f-8pww
* fix: application resource APIs must enforce project restrictions

Signed-off-by: Alexander Matyushentsev <AMatyushentsev@gmail.com>

* Fix unit tests

Signed-off-by: jannfis <jann@mistrust.net>

Co-authored-by: jannfis <jann@mistrust.net>
2022-03-22 10:57:31 -07:00
argo-bot
b65c1699fa Bump version to 2.3.1 2022-03-10 22:43:44 +00:00
argo-bot
4c1428a6be Bump version to 2.3.1 2022-03-10 22:43:24 +00:00
Keith Chong
bca190bd0c fix: Retry checkbox unchecked unexpectedly; Sync up with YAML (#8682) (#8720)
Signed-off-by: Keith Chong <kykchong@redhat.com>
2022-03-10 14:23:30 -08:00
Alexander Matyushentsev
88ca5aabf2 chore: Bump stable version of application set addon (#8744)
Signed-off-by: Alexander Matyushentsev <AMatyushentsev@gmail.com>
2022-03-10 13:19:44 -08:00
Alexander Matyushentsev
8297f827a8 fix: correct jsonnet paths resolution (#8721)
Signed-off-by: Alexander Matyushentsev <AMatyushentsev@gmail.com>
2022-03-08 15:59:03 -08:00
Yuan Tang
b85ef39e0d fix(ui): Applications page incorrectly resets to tiles view. Fixes #8702 (#8718)
Signed-off-by: Yuan Tang <terrytangyuan@gmail.com>
2022-03-08 11:24:02 -08:00
28 changed files with 274 additions and 222 deletions

View File

@@ -427,20 +427,3 @@ jobs:
name: e2e-server-k8s${{ matrix.k3s-version }}.log
path: /tmp/e2e-server.log
if: ${{ failure() }}
lint-docs:
name: Lint docs
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v2
- name: Setup Python
uses: actions/setup-python@v2
with:
python-version: '3.x'
- name: Install dependencies
run: |
pip install -r docs/requirements.txt
- name: Lint docs
run: |
make lint-docs

View File

@@ -509,10 +509,6 @@ serve-docs-local:
serve-docs:
docker run ${MKDOCS_RUN_ARGS} --rm -it -p 8000:8000 -v ${CURRENT_DIR}:/docs ${MKDOCS_DOCKER_IMAGE} serve -a 0.0.0.0:8000
.PHONY: lint-docs
lint-docs:
# https://github.com/dkhamsing/awesome_bot
find docs -name '*.md' -exec grep -l http {} + | xargs docker run --rm -v $(PWD):/mnt:ro dkhamsing/awesome_bot -t 3 --allow-dupe --allow-redirect --allow-timeout --allow-ssl --allow 502,500,429,400 --white-list `cat docs/url-allow-list | grep -v "#" | tr "\n" ','` --skip-save-results --
# Verify that kubectl can connect to your K8s cluster from Docker
.PHONY: verify-kube-connect

View File

@@ -1 +1 @@
2.3.0
2.3.2

View File

@@ -28,6 +28,7 @@ import (
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
"k8s.io/apimachinery/pkg/labels"
apiruntime "k8s.io/apimachinery/pkg/runtime"
"k8s.io/apimachinery/pkg/runtime/schema"
"k8s.io/apimachinery/pkg/types"
"k8s.io/apimachinery/pkg/util/runtime"
"k8s.io/apimachinery/pkg/util/wait"
@@ -421,8 +422,12 @@ func (ctrl *ApplicationController) getResourceTree(a *appv1.Application, managed
},
})
} else {
err := ctrl.stateCache.IterateHierarchy(a.Spec.Destination.Server, kube.GetResourceKey(live), func(child appv1.ResourceNode, appName string) {
err := ctrl.stateCache.IterateHierarchy(a.Spec.Destination.Server, kube.GetResourceKey(live), func(child appv1.ResourceNode, appName string) bool {
if !proj.IsResourcePermitted(schema.GroupKind{Group: child.ResourceRef.Group, Kind: child.ResourceRef.Kind}, child.Namespace, a.Spec.Destination) {
return false
}
nodes = append(nodes, child)
return true
})
if err != nil {
return nil, err
@@ -432,16 +437,18 @@ func (ctrl *ApplicationController) getResourceTree(a *appv1.Application, managed
orphanedNodes := make([]appv1.ResourceNode, 0)
for k := range orphanedNodesMap {
if k.Namespace != "" && proj.IsGroupKindPermitted(k.GroupKind(), true) && !isKnownOrphanedResourceExclusion(k, proj) {
err := ctrl.stateCache.IterateHierarchy(a.Spec.Destination.Server, k, func(child appv1.ResourceNode, appName string) {
err := ctrl.stateCache.IterateHierarchy(a.Spec.Destination.Server, k, func(child appv1.ResourceNode, appName string) bool {
belongToAnotherApp := false
if appName != "" {
if _, exists, err := ctrl.appInformer.GetIndexer().GetByKey(ctrl.namespace + "/" + appName); exists && err == nil {
belongToAnotherApp = true
}
}
if !belongToAnotherApp {
orphanedNodes = append(orphanedNodes, child)
if belongToAnotherApp || !proj.IsResourcePermitted(schema.GroupKind{Group: child.ResourceRef.Group, Kind: child.ResourceRef.Kind}, child.Namespace, a.Spec.Destination) {
return false
}
orphanedNodes = append(orphanedNodes, child)
return true
})
if err != nil {
return nil, err
@@ -1291,6 +1298,13 @@ func (ctrl *ApplicationController) processAppRefreshQueueItem() (processNext boo
app.Status.Sync.Status = appv1.SyncStatusCodeUnknown
app.Status.Health.Status = health.HealthStatusUnknown
ctrl.persistAppStatus(origApp, &app.Status)
if err := ctrl.cache.SetAppResourcesTree(app.Name, &appv1.ApplicationTree{}); err != nil {
log.Warnf("failed to set app resource tree: %v", err)
}
if err := ctrl.cache.SetAppManagedResources(app.Name, nil); err != nil {
log.Warnf("failed to set app managed resources tree: %v", err)
}
return
}

View File

@@ -136,12 +136,12 @@ func newFakeController(data *fakeData) *ApplicationController {
mockStateCache.On("GetClusterCache", mock.Anything).Return(&clusterCacheMock, nil)
mockStateCache.On("IterateHierarchy", mock.Anything, mock.Anything, mock.Anything).Run(func(args mock.Arguments) {
key := args[1].(kube.ResourceKey)
action := args[2].(func(child argoappv1.ResourceNode, appName string))
action := args[2].(func(child argoappv1.ResourceNode, appName string) bool)
appName := ""
if res, ok := data.namespacedResources[key]; ok {
appName = res.AppName
}
action(argoappv1.ResourceNode{ResourceRef: argoappv1.ResourceRef{Kind: key.Kind, Group: key.Group, Namespace: key.Namespace, Name: key.Name}}, appName)
_ = action(argoappv1.ResourceNode{ResourceRef: argoappv1.ResourceRef{Kind: key.Kind, Group: key.Group, Namespace: key.Namespace, Name: key.Name}}, appName)
}).Return(nil)
return ctrl
}

View File

@@ -104,7 +104,7 @@ type LiveStateCache interface {
// Returns synced cluster cache
GetClusterCache(server string) (clustercache.ClusterCache, error)
// Executes give callback against resource specified by the key and all its children
IterateHierarchy(server string, key kube.ResourceKey, action func(child appv1.ResourceNode, appName string)) error
IterateHierarchy(server string, key kube.ResourceKey, action func(child appv1.ResourceNode, appName string) bool) error
// Returns state of live nodes which correspond for target nodes of specified application.
GetManagedLiveObjs(a *appv1.Application, targetObjs []*unstructured.Unstructured) (map[kube.ResourceKey]*unstructured.Unstructured, error)
// IterateResources iterates all resource stored in cache
@@ -437,13 +437,13 @@ func (c *liveStateCache) IsNamespaced(server string, gk schema.GroupKind) (bool,
return clusterInfo.IsNamespaced(gk)
}
func (c *liveStateCache) IterateHierarchy(server string, key kube.ResourceKey, action func(child appv1.ResourceNode, appName string)) error {
func (c *liveStateCache) IterateHierarchy(server string, key kube.ResourceKey, action func(child appv1.ResourceNode, appName string) bool) error {
clusterInfo, err := c.getSyncedCluster(server)
if err != nil {
return err
}
clusterInfo.IterateHierarchy(key, func(resource *clustercache.Resource, namespaceResources map[kube.ResourceKey]*clustercache.Resource) {
action(asResourceNode(resource), getApp(resource, namespaceResources))
clusterInfo.IterateHierarchy(key, func(resource *clustercache.Resource, namespaceResources map[kube.ResourceKey]*clustercache.Resource) bool {
return action(asResourceNode(resource), getApp(resource, namespaceResources))
})
return nil
}

View File

@@ -176,11 +176,11 @@ func (_m *LiveStateCache) IsNamespaced(server string, gk schema.GroupKind) (bool
}
// IterateHierarchy provides a mock function with given fields: server, key, action
func (_m *LiveStateCache) IterateHierarchy(server string, key kube.ResourceKey, action func(v1alpha1.ResourceNode, string)) error {
func (_m *LiveStateCache) IterateHierarchy(server string, key kube.ResourceKey, action func(v1alpha1.ResourceNode, string) bool) error {
ret := _m.Called(server, key, action)
var r0 error
if rf, ok := ret.Get(0).(func(string, kube.ResourceKey, func(v1alpha1.ResourceNode, string)) error); ok {
if rf, ok := ret.Get(0).(func(string, kube.ResourceKey, func(v1alpha1.ResourceNode, string) bool) error); ok {
r0 = rf(server, key, action)
} else {
r0 = ret.Error(0)

View File

@@ -1,70 +0,0 @@
# a list of sites we ignore when checking for broken links in mkdocs
10.97.164.88
192.168.0.20
argocd.example.com
api.github.com/user
cd.apps.argoproj.io
docker-build
docker-build:443
git.example.com
git.example.com:443
github.com/argoproj/another-private-repo
github.com/argoproj/my-private-repository
github.com/argoproj/other-private-repo
github.com/argoproj/private-repo
github.com/otherproj/another-private-repo
ksonnet.io
raw.githubusercontent.com/argoproj/argo-cd
repo.example.com
repo.example.com:443
server.example.com
kubernetes.default.svc
kubernetes.default.svc:443
localhost:4000
localhost:6443
localhost:8080
localhost:8085
mycluster.com
storage.googleapis.com
ui.argocd.yourorganization.net
ui.argocd.yourorganization.net:443
your-kubernetes-cluster-addr
yourorganization.oktapreview.com
yourorganization.oktapreview.com:443
example-OIDC-provider.com
argocd-dex-server:5556
ghe.example.com
proxy-server-url:8888
keycloak.example.com
argocd.myproject.com
argocd.apps.domain.com
k8sou.apps.192-168-2-144.nip.io
your.argoingress.address
your.domain
external.path.to.argocd.io
my-argo-cd-url
my-login-url
login.microsoftonline.com/xxxxx
accounts.google.com/o/saml2/idp?idpid=Abcde0
accounts.google.com/o/saml2?idpid=Abcde0
sso-url
google-entity-id
github.com/argoproj/argo-cd/manifests/crds
example.com
form.example.com
grafana.example.com
10.5.39.39
chat.googleapis.com/v1/spaces/
mattermost.example.com
my-grafana.com
github.my-company.com
1.2.3.4
2.4.6.8
9.8.7.6
ghe.example.com
12.34.567.89
192.168.99.100:8443
github.com/yourghuser/argo-cd
github.com/argoproj/argo-cd/releases/download/
https://github.com/hayorov/helm-gcs.git;
grafana.apps.argoproj.io

2
go.mod
View File

@@ -8,7 +8,7 @@ require (
github.com/TomOnTime/utfutil v0.0.0-20180511104225-09c41003ee1d
github.com/alicebob/miniredis v2.5.0+incompatible
github.com/alicebob/miniredis/v2 v2.14.2
github.com/argoproj/gitops-engine v0.6.0
github.com/argoproj/gitops-engine v0.6.1
github.com/argoproj/notifications-engine v0.3.1-0.20220127183449-91deed20b998
github.com/argoproj/pkg v0.11.1-0.20211203175135-36c59d8fafe0
github.com/bombsimon/logrusr/v2 v2.0.1

4
go.sum
View File

@@ -125,8 +125,8 @@ github.com/antonmedv/expr v1.8.9/go.mod h1:5qsM3oLGDND7sDmQGDXHkYfkjYMUX14qsgqmH
github.com/apache/thrift v0.12.0/go.mod h1:cp2SuWMxlEZw2r+iP2GNCdIi4C1qmUzdZFSVb+bacwQ=
github.com/apache/thrift v0.13.0/go.mod h1:cp2SuWMxlEZw2r+iP2GNCdIi4C1qmUzdZFSVb+bacwQ=
github.com/appscode/go v0.0.0-20190808133642-1d4ef1f1c1e0/go.mod h1:iy07dV61Z7QQdCKJCIvUoDL21u6AIceRhZzyleh2ymc=
github.com/argoproj/gitops-engine v0.6.0 h1:Tnh6kUUVuBV0m3gueYIymAeErWl9XNN9O9JcOoNM0vU=
github.com/argoproj/gitops-engine v0.6.0/go.mod h1:pRgVpLW7pZqf7n3COJ7UcDepk4cI61LAcJd64Q3Jq/c=
github.com/argoproj/gitops-engine v0.6.1 h1:fiaMQ+0OfBHQWInekgkO645i4dolvl3KA//0F7n4PK8=
github.com/argoproj/gitops-engine v0.6.1/go.mod h1:pRgVpLW7pZqf7n3COJ7UcDepk4cI61LAcJd64Q3Jq/c=
github.com/argoproj/notifications-engine v0.3.1-0.20220127183449-91deed20b998 h1:V9RDg+IZeebnm3XjkfkbN07VM21Fu1Cy/RJNoHO++VM=
github.com/argoproj/notifications-engine v0.3.1-0.20220127183449-91deed20b998/go.mod h1:5mKv7zEgI3NO0L+fsuRSwBSY9EIXSuyIsDND8O8TTIw=
github.com/argoproj/pkg v0.11.1-0.20211203175135-36c59d8fafe0 h1:Cfp7rO/HpVxnwlRqJe0jHiBbZ77ZgXhB6HWlYD02Xdc=

View File

@@ -1,4 +1,4 @@
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
resources:
- https://raw.githubusercontent.com/argoproj/applicationset/v0.4.0/manifests/install.yaml
- https://raw.githubusercontent.com/argoproj/applicationset/v0.4.1/manifests/install.yaml

View File

@@ -5,7 +5,7 @@ kind: Kustomization
images:
- name: quay.io/argoproj/argocd
newName: quay.io/argoproj/argocd
newTag: v2.3.0
newTag: v2.3.2
resources:
- ./application-controller
- ./dex

View File

@@ -9497,7 +9497,7 @@ spec:
valueFrom:
fieldRef:
fieldPath: metadata.namespace
image: quay.io/argoproj/argocd-applicationset:v0.4.0
image: quay.io/argoproj/argocd-applicationset:v0.4.1
imagePullPolicy: Always
name: argocd-applicationset-controller
ports:
@@ -9692,7 +9692,7 @@ spec:
value: /helm-working-dir
- name: HELM_DATA_HOME
value: /helm-working-dir
image: quay.io/argoproj/argocd:v2.3.0
image: quay.io/argoproj/argocd:v2.3.2
imagePullPolicy: Always
livenessProbe:
failureThreshold: 3
@@ -9741,7 +9741,7 @@ spec:
- -n
- /usr/local/bin/argocd
- /var/run/argocd/argocd-cmp-server
image: quay.io/argoproj/argocd:v2.3.0
image: quay.io/argoproj/argocd:v2.3.2
name: copyutil
volumeMounts:
- mountPath: /var/run/argocd
@@ -9906,7 +9906,7 @@ spec:
key: controller.default.cache.expiration
name: argocd-cmd-params-cm
optional: true
image: quay.io/argoproj/argocd:v2.3.0
image: quay.io/argoproj/argocd:v2.3.2
imagePullPolicy: Always
livenessProbe:
httpGet:

View File

@@ -11,4 +11,4 @@ resources:
images:
- name: quay.io/argoproj/argocd
newName: quay.io/argoproj/argocd
newTag: v2.3.0
newTag: v2.3.2

View File

@@ -11,7 +11,7 @@ patchesStrategicMerge:
images:
- name: quay.io/argoproj/argocd
newName: quay.io/argoproj/argocd
newTag: v2.3.0
newTag: v2.3.2
resources:
- ../../base/application-controller
- ../../base/dex

View File

@@ -10435,7 +10435,7 @@ spec:
valueFrom:
fieldRef:
fieldPath: metadata.namespace
image: quay.io/argoproj/argocd-applicationset:v0.4.0
image: quay.io/argoproj/argocd-applicationset:v0.4.1
imagePullPolicy: Always
name: argocd-applicationset-controller
ports:
@@ -10516,7 +10516,7 @@ spec:
- -n
- /usr/local/bin/argocd
- /shared/argocd-dex
image: quay.io/argoproj/argocd:v2.3.0
image: quay.io/argoproj/argocd:v2.3.2
imagePullPolicy: Always
name: copyutil
volumeMounts:
@@ -10549,7 +10549,7 @@ spec:
containers:
- command:
- argocd-notifications
image: quay.io/argoproj/argocd:v2.3.0
image: quay.io/argoproj/argocd:v2.3.2
imagePullPolicy: Always
livenessProbe:
tcpSocket:
@@ -10782,7 +10782,7 @@ spec:
value: /helm-working-dir
- name: HELM_DATA_HOME
value: /helm-working-dir
image: quay.io/argoproj/argocd:v2.3.0
image: quay.io/argoproj/argocd:v2.3.2
imagePullPolicy: Always
livenessProbe:
failureThreshold: 3
@@ -10831,7 +10831,7 @@ spec:
- -n
- /usr/local/bin/argocd
- /var/run/argocd/argocd-cmp-server
image: quay.io/argoproj/argocd:v2.3.0
image: quay.io/argoproj/argocd:v2.3.2
name: copyutil
volumeMounts:
- mountPath: /var/run/argocd
@@ -11058,7 +11058,7 @@ spec:
key: server.http.cookie.maxnumber
name: argocd-cmd-params-cm
optional: true
image: quay.io/argoproj/argocd:v2.3.0
image: quay.io/argoproj/argocd:v2.3.2
imagePullPolicy: Always
livenessProbe:
httpGet:
@@ -11254,7 +11254,7 @@ spec:
key: controller.default.cache.expiration
name: argocd-cmd-params-cm
optional: true
image: quay.io/argoproj/argocd:v2.3.0
image: quay.io/argoproj/argocd:v2.3.2
imagePullPolicy: Always
livenessProbe:
httpGet:

View File

@@ -7731,7 +7731,7 @@ spec:
valueFrom:
fieldRef:
fieldPath: metadata.namespace
image: quay.io/argoproj/argocd-applicationset:v0.4.0
image: quay.io/argoproj/argocd-applicationset:v0.4.1
imagePullPolicy: Always
name: argocd-applicationset-controller
ports:
@@ -7812,7 +7812,7 @@ spec:
- -n
- /usr/local/bin/argocd
- /shared/argocd-dex
image: quay.io/argoproj/argocd:v2.3.0
image: quay.io/argoproj/argocd:v2.3.2
imagePullPolicy: Always
name: copyutil
volumeMounts:
@@ -7845,7 +7845,7 @@ spec:
containers:
- command:
- argocd-notifications
image: quay.io/argoproj/argocd:v2.3.0
image: quay.io/argoproj/argocd:v2.3.2
imagePullPolicy: Always
livenessProbe:
tcpSocket:
@@ -8078,7 +8078,7 @@ spec:
value: /helm-working-dir
- name: HELM_DATA_HOME
value: /helm-working-dir
image: quay.io/argoproj/argocd:v2.3.0
image: quay.io/argoproj/argocd:v2.3.2
imagePullPolicy: Always
livenessProbe:
failureThreshold: 3
@@ -8127,7 +8127,7 @@ spec:
- -n
- /usr/local/bin/argocd
- /var/run/argocd/argocd-cmp-server
image: quay.io/argoproj/argocd:v2.3.0
image: quay.io/argoproj/argocd:v2.3.2
name: copyutil
volumeMounts:
- mountPath: /var/run/argocd
@@ -8354,7 +8354,7 @@ spec:
key: server.http.cookie.maxnumber
name: argocd-cmd-params-cm
optional: true
image: quay.io/argoproj/argocd:v2.3.0
image: quay.io/argoproj/argocd:v2.3.2
imagePullPolicy: Always
livenessProbe:
httpGet:
@@ -8550,7 +8550,7 @@ spec:
key: controller.default.cache.expiration
name: argocd-cmd-params-cm
optional: true
image: quay.io/argoproj/argocd:v2.3.0
image: quay.io/argoproj/argocd:v2.3.2
imagePullPolicy: Always
livenessProbe:
httpGet:

View File

@@ -9805,7 +9805,7 @@ spec:
valueFrom:
fieldRef:
fieldPath: metadata.namespace
image: quay.io/argoproj/argocd-applicationset:v0.4.0
image: quay.io/argoproj/argocd-applicationset:v0.4.1
imagePullPolicy: Always
name: argocd-applicationset-controller
ports:
@@ -9886,7 +9886,7 @@ spec:
- -n
- /usr/local/bin/argocd
- /shared/argocd-dex
image: quay.io/argoproj/argocd:v2.3.0
image: quay.io/argoproj/argocd:v2.3.2
imagePullPolicy: Always
name: copyutil
volumeMounts:
@@ -9919,7 +9919,7 @@ spec:
containers:
- command:
- argocd-notifications
image: quay.io/argoproj/argocd:v2.3.0
image: quay.io/argoproj/argocd:v2.3.2
imagePullPolicy: Always
livenessProbe:
tcpSocket:
@@ -10116,7 +10116,7 @@ spec:
value: /helm-working-dir
- name: HELM_DATA_HOME
value: /helm-working-dir
image: quay.io/argoproj/argocd:v2.3.0
image: quay.io/argoproj/argocd:v2.3.2
imagePullPolicy: Always
livenessProbe:
failureThreshold: 3
@@ -10165,7 +10165,7 @@ spec:
- -n
- /usr/local/bin/argocd
- /var/run/argocd/argocd-cmp-server
image: quay.io/argoproj/argocd:v2.3.0
image: quay.io/argoproj/argocd:v2.3.2
name: copyutil
volumeMounts:
- mountPath: /var/run/argocd
@@ -10388,7 +10388,7 @@ spec:
key: server.http.cookie.maxnumber
name: argocd-cmd-params-cm
optional: true
image: quay.io/argoproj/argocd:v2.3.0
image: quay.io/argoproj/argocd:v2.3.2
imagePullPolicy: Always
livenessProbe:
httpGet:
@@ -10578,7 +10578,7 @@ spec:
key: controller.default.cache.expiration
name: argocd-cmd-params-cm
optional: true
image: quay.io/argoproj/argocd:v2.3.0
image: quay.io/argoproj/argocd:v2.3.2
imagePullPolicy: Always
livenessProbe:
httpGet:

View File

@@ -7101,7 +7101,7 @@ spec:
valueFrom:
fieldRef:
fieldPath: metadata.namespace
image: quay.io/argoproj/argocd-applicationset:v0.4.0
image: quay.io/argoproj/argocd-applicationset:v0.4.1
imagePullPolicy: Always
name: argocd-applicationset-controller
ports:
@@ -7182,7 +7182,7 @@ spec:
- -n
- /usr/local/bin/argocd
- /shared/argocd-dex
image: quay.io/argoproj/argocd:v2.3.0
image: quay.io/argoproj/argocd:v2.3.2
imagePullPolicy: Always
name: copyutil
volumeMounts:
@@ -7215,7 +7215,7 @@ spec:
containers:
- command:
- argocd-notifications
image: quay.io/argoproj/argocd:v2.3.0
image: quay.io/argoproj/argocd:v2.3.2
imagePullPolicy: Always
livenessProbe:
tcpSocket:
@@ -7412,7 +7412,7 @@ spec:
value: /helm-working-dir
- name: HELM_DATA_HOME
value: /helm-working-dir
image: quay.io/argoproj/argocd:v2.3.0
image: quay.io/argoproj/argocd:v2.3.2
imagePullPolicy: Always
livenessProbe:
failureThreshold: 3
@@ -7461,7 +7461,7 @@ spec:
- -n
- /usr/local/bin/argocd
- /var/run/argocd/argocd-cmp-server
image: quay.io/argoproj/argocd:v2.3.0
image: quay.io/argoproj/argocd:v2.3.2
name: copyutil
volumeMounts:
- mountPath: /var/run/argocd
@@ -7684,7 +7684,7 @@ spec:
key: server.http.cookie.maxnumber
name: argocd-cmd-params-cm
optional: true
image: quay.io/argoproj/argocd:v2.3.0
image: quay.io/argoproj/argocd:v2.3.2
imagePullPolicy: Always
livenessProbe:
httpGet:
@@ -7874,7 +7874,7 @@ spec:
key: controller.default.cache.expiration
name: argocd-cmd-params-cm
optional: true
image: quay.io/argoproj/argocd:v2.3.0
image: quay.io/argoproj/argocd:v2.3.2
imagePullPolicy: Always
livenessProbe:
httpGet:

View File

@@ -313,11 +313,15 @@ func (proj AppProject) IsGroupKindPermitted(gk schema.GroupKind, namespaced bool
// IsLiveResourcePermitted returns whether a live resource found in the cluster is permitted by an AppProject
func (proj AppProject) IsLiveResourcePermitted(un *unstructured.Unstructured, server string, name string) bool {
if !proj.IsGroupKindPermitted(un.GroupVersionKind().GroupKind(), un.GetNamespace() != "") {
return proj.IsResourcePermitted(un.GroupVersionKind().GroupKind(), un.GetNamespace(), ApplicationDestination{Server: server, Name: name})
}
func (proj AppProject) IsResourcePermitted(groupKind schema.GroupKind, namespace string, dest ApplicationDestination) bool {
if !proj.IsGroupKindPermitted(groupKind, namespace != "") {
return false
}
if un.GetNamespace() != "" {
return proj.IsDestinationPermitted(ApplicationDestination{Server: server, Namespace: un.GetNamespace(), Name: name})
if namespace != "" {
return proj.IsDestinationPermitted(ApplicationDestination{Server: dest.Server, Name: dest.Name, Namespace: namespace})
}
return true
}

View File

@@ -1136,7 +1136,8 @@ func makeJsonnetVm(appPath string, repoRoot string, sourceJsonnet v1alpha1.Appli
// Jsonnet Imports relative to the repository path
jpaths := []string{appPath}
for _, p := range sourceJsonnet.Libs {
jpath, _, err := pathutil.ResolveFilePath(appPath, repoRoot, p, nil)
// the jsonnet library path is relative to the repository root, not application path
jpath, _, err := pathutil.ResolveFilePath(repoRoot, repoRoot, p, nil)
if err != nil {
return nil, err
}

View File

@@ -254,7 +254,7 @@ func TestGenerateJsonnetManifestInDir(t *testing.T) {
Jsonnet: argoappv1.ApplicationSourceJsonnet{
ExtVars: []argoappv1.JsonnetVar{{Name: "extVarString", Value: "extVarString"}, {Name: "extVarCode", Value: "\"extVarCode\"", Code: true}},
TLAs: []argoappv1.JsonnetVar{{Name: "tlaString", Value: "tlaString"}, {Name: "tlaCode", Value: "\"tlaCode\"", Code: true}},
Libs: []string{"./vendor"},
Libs: []string{"testdata/jsonnet/vendor"},
},
},
},

View File

@@ -488,6 +488,21 @@ func (s *Server) ListResourceEvents(ctx context.Context, q *application.Applicat
"involvedObject.namespace": a.Namespace,
}).String()
} else {
tree, err := s.getAppResources(ctx, a)
if err != nil {
return nil, err
}
found := false
for _, n := range append(tree.Nodes, tree.OrphanedNodes...) {
if n.ResourceRef.UID == q.ResourceUID && n.ResourceRef.Name == q.ResourceName && n.ResourceRef.Namespace == q.ResourceNamespace {
found = true
break
}
}
if !found {
return nil, status.Errorf(codes.InvalidArgument, "%s not found as part of application %s", q.ResourceName, *q.Name)
}
namespace = q.ResourceNamespace
var config *rest.Config
config, err = s.getApplicationClusterConfig(ctx, a)
@@ -937,7 +952,7 @@ func (s *Server) getAppResources(ctx context.Context, a *appv1.Application) (*ap
return &tree, err
}
func (s *Server) getAppResource(ctx context.Context, action string, q *application.ApplicationResourceRequest) (*appv1.ResourceNode, *rest.Config, *appv1.Application, error) {
func (s *Server) getAppLiveResource(ctx context.Context, action string, q *application.ApplicationResourceRequest) (*appv1.ResourceNode, *rest.Config, *appv1.Application, error) {
a, err := s.appLister.Get(*q.Name)
if err != nil {
return nil, nil, nil, err
@@ -952,7 +967,7 @@ func (s *Server) getAppResource(ctx context.Context, action string, q *applicati
}
found := tree.FindNode(q.Group, q.Kind, q.Namespace, q.ResourceName)
if found == nil {
if found == nil || found.ResourceRef.UID == "" {
return nil, nil, nil, status.Errorf(codes.InvalidArgument, "%s %s %s not found as part of application %s", q.Kind, q.Group, q.ResourceName, *q.Name)
}
config, err := s.getApplicationClusterConfig(ctx, a)
@@ -963,7 +978,7 @@ func (s *Server) getAppResource(ctx context.Context, action string, q *applicati
}
func (s *Server) GetResource(ctx context.Context, q *application.ApplicationResourceRequest) (*application.ApplicationResourceResponse, error) {
res, config, _, err := s.getAppResource(ctx, rbacpolicy.ActionGet, q)
res, config, _, err := s.getAppLiveResource(ctx, rbacpolicy.ActionGet, q)
if err != nil {
return nil, err
}
@@ -1008,7 +1023,7 @@ func (s *Server) PatchResource(ctx context.Context, q *application.ApplicationRe
Version: q.Version,
Group: q.Group,
}
res, config, a, err := s.getAppResource(ctx, rbacpolicy.ActionUpdate, resourceRequest)
res, config, a, err := s.getAppLiveResource(ctx, rbacpolicy.ActionUpdate, resourceRequest)
if err != nil {
return nil, err
}
@@ -1048,7 +1063,7 @@ func (s *Server) DeleteResource(ctx context.Context, q *application.ApplicationR
Version: q.Version,
Group: q.Group,
}
res, config, a, err := s.getAppResource(ctx, rbacpolicy.ActionDelete, resourceRequest)
res, config, a, err := s.getAppLiveResource(ctx, rbacpolicy.ActionDelete, resourceRequest)
if err != nil {
return nil, err
}
@@ -1319,7 +1334,7 @@ func getSelectedPods(treeNodes []appv1.ResourceNode, q *application.ApplicationP
var pods []appv1.ResourceNode
isTheOneMap := make(map[string]bool)
for _, treeNode := range treeNodes {
if treeNode.Kind == kube.PodKind && treeNode.Group == "" {
if treeNode.Kind == kube.PodKind && treeNode.Group == "" && treeNode.UID != "" {
if isTheSelectedOne(&treeNode, q, treeNodes, isTheOneMap) {
pods = append(pods, treeNode)
}
@@ -1609,7 +1624,7 @@ func (s *Server) logResourceEvent(res *appv1.ResourceNode, ctx context.Context,
}
func (s *Server) ListResourceActions(ctx context.Context, q *application.ApplicationResourceRequest) (*application.ResourceActionsListResponse, error) {
res, config, _, err := s.getAppResource(ctx, rbacpolicy.ActionGet, q)
res, config, _, err := s.getAppLiveResource(ctx, rbacpolicy.ActionGet, q)
if err != nil {
return nil, err
}
@@ -1660,7 +1675,7 @@ func (s *Server) RunResourceAction(ctx context.Context, q *application.ResourceA
Group: q.Group,
}
actionRequest := fmt.Sprintf("%s/%s/%s/%s", rbacpolicy.ActionAction, q.Group, q.Kind, q.Action)
res, config, a, err := s.getAppResource(ctx, actionRequest, resourceRequest)
res, config, a, err := s.getAppLiveResource(ctx, actionRequest, resourceRequest)
if err != nil {
return nil, err
}

View File

@@ -909,64 +909,125 @@ func TestSyncAsync(t *testing.T) {
Expect(SyncStatusIs(SyncStatusCodeSynced))
}
func TestPermissions(t *testing.T) {
EnsureCleanState(t)
appName := Name()
_, err := RunCli("proj", "create", "test")
assert.NoError(t, err)
// make sure app cannot be created without permissions in project
_, err = RunCli("app", "create", appName, "--repo", RepoURL(RepoURLTypeFile),
"--path", guestbookPath, "--project", "test", "--dest-server", KubernetesInternalAPIServerAddr, "--dest-namespace", DeploymentNamespace())
assert.Error(t, err)
sourceError := fmt.Sprintf("application repo %s is not permitted in project 'test'", RepoURL(RepoURLTypeFile))
destinationError := fmt.Sprintf("application destination {%s %s} is not permitted in project 'test'", KubernetesInternalAPIServerAddr, DeploymentNamespace())
assert.Contains(t, err.Error(), sourceError)
assert.Contains(t, err.Error(), destinationError)
proj, err := AppClientset.ArgoprojV1alpha1().AppProjects(ArgoCDNamespace).Get(context.Background(), "test", metav1.GetOptions{})
assert.NoError(t, err)
proj.Spec.Destinations = []ApplicationDestination{{Server: "*", Namespace: "*"}}
proj.Spec.SourceRepos = []string{"*"}
proj, err = AppClientset.ArgoprojV1alpha1().AppProjects(ArgoCDNamespace).Update(context.Background(), proj, metav1.UpdateOptions{})
assert.NoError(t, err)
// make sure controller report permissions issues in conditions
_, err = RunCli("app", "create", appName, "--repo", RepoURL(RepoURLTypeFile),
"--path", guestbookPath, "--project", "test", "--dest-server", KubernetesInternalAPIServerAddr, "--dest-namespace", DeploymentNamespace())
assert.NoError(t, err)
defer func() {
err = AppClientset.ArgoprojV1alpha1().Applications(ArgoCDNamespace).Delete(context.Background(), appName, metav1.DeleteOptions{})
assert.NoError(t, err)
}()
proj.Spec.Destinations = []ApplicationDestination{}
proj.Spec.SourceRepos = []string{}
_, err = AppClientset.ArgoprojV1alpha1().AppProjects(ArgoCDNamespace).Update(context.Background(), proj, metav1.UpdateOptions{})
assert.NoError(t, err)
time.Sleep(1 * time.Second)
closer, client, err := ArgoCDClientset.NewApplicationClient()
assert.NoError(t, err)
defer io.Close(closer)
refresh := string(RefreshTypeNormal)
app, err := client.Get(context.Background(), &applicationpkg.ApplicationQuery{Name: &appName, Refresh: &refresh})
assert.NoError(t, err)
destinationErrorExist := false
sourceErrorExist := false
for i := range app.Status.Conditions {
if strings.Contains(app.Status.Conditions[i].Message, destinationError) {
destinationErrorExist = true
}
if strings.Contains(app.Status.Conditions[i].Message, sourceError) {
sourceErrorExist = true
// assertResourceActions verifies if view/modify resource actions are successful/failing for given application
func assertResourceActions(t *testing.T, appName string, successful bool) {
assertError := func(err error, message string) {
if successful {
assert.NoError(t, err)
} else {
if assert.Error(t, err) {
assert.Contains(t, err.Error(), message)
}
}
}
assert.True(t, destinationErrorExist)
assert.True(t, sourceErrorExist)
closer, cdClient := ArgoCDClientset.NewApplicationClientOrDie()
defer io.Close(closer)
deploymentResource, err := KubeClientset.AppsV1().Deployments(DeploymentNamespace()).Get(context.Background(), "guestbook-ui", metav1.GetOptions{})
require.NoError(t, err)
logs, err := cdClient.PodLogs(context.Background(), &applicationpkg.ApplicationPodLogsQuery{
Group: pointer.String("apps"), Kind: pointer.String("Deployment"), Name: &appName, Namespace: DeploymentNamespace(),
})
require.NoError(t, err)
_, err = logs.Recv()
assertError(err, "EOF")
expectedError := fmt.Sprintf("Deployment apps guestbook-ui not found as part of application %s", appName)
_, err = cdClient.ListResourceEvents(context.Background(), &applicationpkg.ApplicationResourceEventsQuery{
Name: &appName, ResourceName: "guestbook-ui", ResourceNamespace: DeploymentNamespace(), ResourceUID: string(deploymentResource.UID)})
assertError(err, fmt.Sprintf("%s not found as part of application %s", "guestbook-ui", appName))
_, err = cdClient.GetResource(context.Background(), &applicationpkg.ApplicationResourceRequest{
Name: &appName, ResourceName: "guestbook-ui", Namespace: DeploymentNamespace(), Version: "v1", Group: "apps", Kind: "Deployment"})
assertError(err, expectedError)
_, err = cdClient.DeleteResource(context.Background(), &applicationpkg.ApplicationResourceDeleteRequest{
Name: &appName, ResourceName: "guestbook-ui", Namespace: DeploymentNamespace(), Version: "v1", Group: "apps", Kind: "Deployment",
})
assertError(err, expectedError)
_, err = cdClient.RunResourceAction(context.Background(), &applicationpkg.ResourceActionRunRequest{
Name: &appName, ResourceName: "guestbook-ui", Namespace: DeploymentNamespace(), Version: "v1", Group: "apps", Kind: "Deployment", Action: "restart",
})
assertError(err, expectedError)
}
func TestPermissions(t *testing.T) {
appCtx := Given(t)
projName := "argo-project"
projActions := projectFixture.
Given(t).
Name(projName).
When().
Create()
sourceError := fmt.Sprintf("application repo %s is not permitted in project 'argo-project'", RepoURL(RepoURLTypeFile))
destinationError := fmt.Sprintf("application destination {%s %s} is not permitted in project 'argo-project'", KubernetesInternalAPIServerAddr, DeploymentNamespace())
appCtx.
Path("guestbook-logs").
Project(projName).
When().
IgnoreErrors().
// ensure app is not created if project permissions are missing
CreateApp().
Then().
Expect(Error("", sourceError)).
Expect(Error("", destinationError)).
When().
DoNotIgnoreErrors().
// add missing permissions, create and sync app
And(func() {
projActions.AddDestination("*", "*")
projActions.AddSource("*")
}).
CreateApp().
Sync().
Then().
// make sure application resource actiions are successful
And(func(app *Application) {
assertResourceActions(t, app.Name, true)
}).
When().
// remove projet permissions and "refresh" app
And(func() {
projActions.UpdateProject(func(proj *AppProject) {
proj.Spec.Destinations = nil
proj.Spec.SourceRepos = nil
})
}).
Refresh(RefreshTypeNormal).
Then().
// ensure app resource tree is empty when source/destination permissions are missing
Expect(Condition(ApplicationConditionInvalidSpecError, destinationError)).
Expect(Condition(ApplicationConditionInvalidSpecError, sourceError)).
And(func(app *Application) {
closer, cdClient := ArgoCDClientset.NewApplicationClientOrDie()
defer io.Close(closer)
tree, err := cdClient.ResourceTree(context.Background(), &applicationpkg.ResourcesQuery{ApplicationName: &app.Name})
require.NoError(t, err)
assert.Len(t, tree.Nodes, 0)
assert.Len(t, tree.OrphanedNodes, 0)
}).
When().
// add missing permissions but deny management of Deployment kind
And(func() {
projActions.
AddDestination("*", "*").
AddSource("*").
UpdateProject(func(proj *AppProject) {
proj.Spec.NamespaceResourceBlacklist = []metav1.GroupKind{{Group: "*", Kind: "Deployment"}}
})
}).
Refresh(RefreshTypeNormal).
Then().
// make sure application resource actiions are failing
And(func(app *Application) {
assertResourceActions(t, "test-permissions", false)
})
}
func TestPermissionWithScopedRepo(t *testing.T) {

View File

@@ -1,6 +1,12 @@
package project
import (
"context"
"github.com/stretchr/testify/require"
v1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1"
"github.com/argoproj/argo-cd/v2/test/e2e/fixture"
)
@@ -34,6 +40,25 @@ func (a *Actions) Create(args ...string) *Actions {
return a
}
func (a *Actions) AddDestination(cluster string, namespace string) *Actions {
a.runCli("proj", "add-destination", a.context.name, cluster, namespace)
return a
}
func (a *Actions) AddSource(repo string) *Actions {
a.runCli("proj", "add-source", a.context.name, repo)
return a
}
func (a *Actions) UpdateProject(updater func(project *v1alpha1.AppProject)) *Actions {
proj, err := fixture.AppClientset.ArgoprojV1alpha1().AppProjects(fixture.ArgoCDNamespace).Get(context.TODO(), a.context.name, v1.GetOptions{})
require.NoError(a.context.t, err)
updater(proj)
_, err = fixture.AppClientset.ArgoprojV1alpha1().AppProjects(fixture.ArgoCDNamespace).Update(context.TODO(), proj, v1.UpdateOptions{})
require.NoError(a.context.t, err)
return a
}
func (a *Actions) Name(name string) *Actions {
a.context.name = name
return a
@@ -72,4 +97,7 @@ func (a *Actions) Then() *Consequences {
func (a *Actions) runCli(args ...string) {
a.context.t.Helper()
a.lastOutput, a.lastError = fixture.RunCli(args...)
if !a.ignoreErrors {
require.Empty(a.context.t, a.lastError)
}
}

View File

@@ -105,6 +105,7 @@ export const ApplicationCreatePanel = (props: {
const [yamlMode, setYamlMode] = React.useState(false);
const [explicitPathType, setExplicitPathType] = React.useState<{path: string; type: models.AppSourceType}>(null);
const [destFormat, setDestFormat] = React.useState('URL');
const [retry, setRetry] = React.useState(false);
function normalizeTypeFields(formApi: FormApi, type: models.AppSourceType) {
const app = formApi.getFormState().values;
@@ -222,7 +223,13 @@ export const ApplicationCreatePanel = (props: {
<div className='argo-form-row'>
<label>Sync Options</label>
<FormField formApi={api} field='spec.syncPolicy.syncOptions' component={ApplicationSyncOptionsField} />
<ApplicationRetryOptions formApi={api} field='spec.syncPolicy.retry' />
<ApplicationRetryOptions
formApi={api}
field='spec.syncPolicy.retry'
retry={retry || (api.getFormState().values.spec.syncPolicy && api.getFormState().values.spec.syncPolicy.retry)}
setRetry={setRetry}
initValues={api.getFormState().values.spec.syncPolicy ? api.getFormState().values.spec.syncPolicy.retry : null}
/>
</div>
</div>
);

View File

@@ -79,8 +79,20 @@ export const ApplicationRetryForm = ({initValues, field = 'retryStrategy'}: {ini
);
};
export const ApplicationRetryOptions = ({formApi, initValues, field = 'retryStrategy'}: {formApi: FormApi; field?: string; initValues?: models.RetryStrategy}) => {
const [retry, setRetry] = React.useState(!!initValues);
export const ApplicationRetryOptions = ({
formApi,
initValues,
field = 'retryStrategy',
retry,
setRetry
}: {
formApi: FormApi;
field?: string;
initValues?: models.RetryStrategy;
retry?: boolean;
setRetry?: (value: boolean) => any;
}) => {
const [retryInternal, setRetryInternal] = React.useState(!!initValues);
const toggleRetry = (value: boolean) => {
if (!value) {
@@ -97,15 +109,18 @@ export const ApplicationRetryOptions = ({formApi, initValues, field = 'retryStra
errors: newErrors
});
}
setRetry(value);
if (setRetry != null) {
setRetry(value);
} else {
setRetryInternal(value);
}
};
const isChecked = setRetry != null ? retry : retryInternal;
return (
<div className='application-retry-options'>
<Checkbox id='retry' checked={retry} onChange={val => toggleRetry(val)} />
<Checkbox id='retry' checked={isChecked} onChange={val => toggleRetry(val)} />
<label htmlFor='retry'>Retry</label>
{retry && <ApplicationRetryForm initValues={initValues} field={field} />}
{isChecked && <ApplicationRetryForm initValues={initValues} field={field} />}
</div>
);
};

View File

@@ -127,8 +127,6 @@ const ViewPref = ({children}: {children: (pref: AppsListPreferences & {page: num
}
if (params.get('view') != null) {
viewPref.view = params.get('view') as AppsListViewType;
} else {
viewPref.view = 'tiles' as AppsListViewType;
}
if (params.get('labels') != null) {
viewPref.labelsFilter = params