Compare commits

...

18 Commits

Author SHA1 Message Date
gcp-cherry-pick-bot[bot]
e1284e19e0 remove unwanted updating of source-position in app set command (#18887) (#18896)
Signed-off-by: ishitasequeira <ishiseq29@gmail.com>
Co-authored-by: Ishita Sequeira <46771830+ishitasequeira@users.noreply.github.com>
2024-07-02 13:14:11 -04:00
github-actions[bot]
9e313e539b Bump version to 2.11.4 (#18894)
Signed-off-by: GitHub <noreply@github.com>
Co-authored-by: crenshaw-dev <crenshaw-dev@users.noreply.github.com>
2024-07-02 13:04:34 -04:00
Michael Crenshaw
0d1709f73b fix: update static schemas (#18889)
Signed-off-by: Michael Crenshaw <350466+crenshaw-dev@users.noreply.github.com>
2024-07-02 13:00:41 -04:00
Michael Crenshaw
bfbceff5da fix(controller): bad server-side diffs (#18213) (2.11) (#18868)
* fix(controller): bad server-side diffs (#18213) (2.11)

Signed-off-by: Michael Crenshaw <350466+crenshaw-dev@users.noreply.github.com>

* hopefully the right hash now

Signed-off-by: Michael Crenshaw <350466+crenshaw-dev@users.noreply.github.com>

---------

Signed-off-by: Michael Crenshaw <350466+crenshaw-dev@users.noreply.github.com>
2024-07-01 21:27:36 -04:00
gcp-cherry-pick-bot[bot]
0e71f09990 docs: Fix .path to .path.segments go template (#18872) (#18874)
Signed-off-by: Jaeseok Lee <devsunb@gmail.com>
Co-authored-by: Jaeseok Lee <devsunb@gmail.com>
2024-07-01 10:53:31 -04:00
gcp-cherry-pick-bot[bot]
07880f3c1d fix(webhook): bitbucket and azure not triggering refresh (#18289) (#18765) (#18819)
* fix(webhook): bitbucket and azure webhook not triggering refresh



* update unit test



* fix merge



* adjust logic for reposerver using ls-remote



---------

Signed-off-by: Alexandre Gaudreault <alexandre_gaudreault@intuit.com>
Co-authored-by: Alexandre Gaudreault <alexandre_gaudreault@intuit.com>
2024-06-26 08:48:45 -04:00
Michael Crenshaw
24b198bf51 fix(appset): revert "keep reconciling even when params error occurred" (#17062) (#18781)
This reverts commit 86369ca71d.

Signed-off-by: Michael Crenshaw <350466+crenshaw-dev@users.noreply.github.com>
2024-06-25 15:34:37 -04:00
gcp-cherry-pick-bot[bot]
5fd645feac fix: docs site version selector broken (#18378) (#18724)
Signed-off-by: xiaowu.zhu <xiaowu.zhu@daocloud.io>
Signed-off-by: root <root@daocloud.io>
Co-authored-by: yyzxw <34639446+yyzxw@users.noreply.github.com>
2024-06-18 15:45:06 -04:00
gcp-cherry-pick-bot[bot]
b5c13b6139 fix: Update braces package to 3.0.3 (#18459) (#18663)
Signed-off-by: Keith Chong <kykchong@redhat.com>
Co-authored-by: Keith Chong <kykchong@redhat.com>
2024-06-14 09:18:42 -04:00
gcp-cherry-pick-bot[bot]
d75b23bf92 Revert "feat(server): log app Spec along with event (#16416)" (#18458) (#18639)
This reverts commit 820f4d861a.

Signed-off-by: jannfis <jann@mistrust.net>
Co-authored-by: Jann Fischer <jann@mistrust.net>
2024-06-13 14:49:34 -04:00
gcp-cherry-pick-bot[bot]
ac80860eda test: fix e2e tests after GHSA-3cqf-953p-h5cp (#18543) (#18641)
Co-authored-by: Michael Crenshaw <350466+crenshaw-dev@users.noreply.github.com>
2024-06-13 14:09:28 -04:00
Justin Marquis
c2bd38a11a chore(deps): upgrade redis to 7.2.15-alpine (cherry-pick release-2.11) (#18640)
Signed-off-by: Justin Marquis <justin@akuity.io>
2024-06-13 13:45:24 -04:00
Justin Marquis
13844b90ad chore: bump go version to 1.21.10 (#18540)
Signed-off-by: Justin Marquis <justin@akuity.io>
2024-06-07 08:49:08 -07:00
github-actions[bot]
3f344d54a4 Bump version to 2.11.3 (#18520)
Signed-off-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
Co-authored-by: pasha-codefresh <39732895+pasha-codefresh@users.noreply.github.com>
2024-06-06 11:36:25 +03:00
pasha-codefresh
e01bb5303a Merge pull request from GHSA-3cqf-953p-h5cp
* fix: prevent enumerating by cluster name, return exact error for case when cluster exists and not

Signed-off-by: pashakostohrys <pavel@codefresh.io>

* fix: prevent cluster enumeration by name

Signed-off-by: pashakostohrys <pavel@codefresh.io>

* fix: prevent cluster enumeration by name

Signed-off-by: pashakostohrys <pavel@codefresh.io>

* fix linter and add unit test

Signed-off-by: pashakostohrys <pavel@codefresh.io>

* fix linter and add unit test

Signed-off-by: pashakostohrys <pavel@codefresh.io>

---------

Signed-off-by: pashakostohrys <pavel@codefresh.io>
2024-06-06 11:30:10 +03:00
Blake Pettersson
320abb8d64 Merge pull request from GHSA-87p9-x75h-p4j2
Signed-off-by: Blake Pettersson <blake.pettersson@gmail.com>
2024-06-06 11:25:55 +03:00
gcp-cherry-pick-bot[bot]
46342a9e82 fix: app names with non-alphanumeric characters in position 63 break syncs (issue #18237) (#18256) (#18439)
* Ensure truncated app label does not end in a special character



* Move regex to global variable and add out of bounds check



* Add test for out-of-bounds check



---------

Signed-off-by: Zack Robinson <robinsoz@arcesium.com>
Co-authored-by: Zack Robinson <zkislakrobinson@gmail.com>
2024-05-28 21:08:52 +03:00
gcp-cherry-pick-bot[bot]
cf17283ebe fix source ordering issue in manifest generation for multi-source app while using manifests and diff commands (#18395) (#18408) 2024-05-24 16:52:42 -04:00
34 changed files with 422 additions and 351 deletions

View File

@@ -4,7 +4,7 @@ ARG BASE_IMAGE=docker.io/library/ubuntu:22.04@sha256:0bced47fffa3361afa981854fca
# Initial stage which pulls prepares build dependencies and CLI tooling we need for our final image
# Also used as the image in CI jobs so needs all dependencies
####################################################################################################
FROM docker.io/library/golang:1.21.9@sha256:7d0dcbe5807b1ad7272a598fbf9d7af15b5e2bed4fd6c4c2b5b3684df0b317dd AS builder
FROM docker.io/library/golang:1.21.10@sha256:16438a8e66c0c984f732e815ee5b7d715b8e33e81bac6d6a3750b1067744e7ca AS builder
RUN echo 'deb http://archive.debian.org/debian buster-backports main' >> /etc/apt/sources.list
@@ -101,7 +101,7 @@ RUN HOST_ARCH=$TARGETARCH NODE_ENV='production' NODE_ONLINE_ENV='online' NODE_OP
####################################################################################################
# Argo CD Build stage which performs the actual build of Argo CD binaries
####################################################################################################
FROM --platform=$BUILDPLATFORM docker.io/library/golang:1.21.9@sha256:7d0dcbe5807b1ad7272a598fbf9d7af15b5e2bed4fd6c4c2b5b3684df0b317dd AS argocd-build
FROM --platform=$BUILDPLATFORM docker.io/library/golang:1.21.10@sha256:16438a8e66c0c984f732e815ee5b7d715b8e33e81bac6d6a3750b1067744e7ca AS argocd-build
WORKDIR /go/src/github.com/argoproj/argo-cd

View File

@@ -1 +1 @@
2.11.2
2.11.4

View File

@@ -127,20 +127,18 @@ func (r *ApplicationSetReconciler) Reconcile(ctx context.Context, req ctrl.Reque
// Log a warning if there are unrecognized generators
_ = utils.CheckInvalidGenerators(&applicationSetInfo)
// desiredApplications is the main list of all expected Applications from all generators in this appset.
desiredApplications, applicationSetReason, generatorsErr := r.generateApplications(logCtx, applicationSetInfo)
if generatorsErr != nil {
desiredApplications, applicationSetReason, err := r.generateApplications(logCtx, applicationSetInfo)
if err != nil {
_ = r.setApplicationSetStatusCondition(ctx,
&applicationSetInfo,
argov1alpha1.ApplicationSetCondition{
Type: argov1alpha1.ApplicationSetConditionErrorOccurred,
Message: generatorsErr.Error(),
Message: err.Error(),
Reason: string(applicationSetReason),
Status: argov1alpha1.ApplicationSetConditionStatusTrue,
}, parametersGenerated,
)
if len(desiredApplications) < 1 {
return ctrl.Result{}, generatorsErr
}
return ctrl.Result{}, err
}
parametersGenerated = true
@@ -314,7 +312,7 @@ func (r *ApplicationSetReconciler) Reconcile(ctx context.Context, req ctrl.Reque
requeueAfter := r.getMinRequeueAfter(&applicationSetInfo)
if len(validateErrors) == 0 && generatorsErr == nil {
if len(validateErrors) == 0 {
if err := r.setApplicationSetStatusCondition(ctx,
&applicationSetInfo,
argov1alpha1.ApplicationSetCondition{

View File

@@ -2488,91 +2488,6 @@ func TestReconcilerValidationProjectErrorBehaviour(t *testing.T) {
assert.Error(t, err)
}
func TestReconcilerCreateAppsRecoveringRenderError(t *testing.T) {
scheme := runtime.NewScheme()
err := v1alpha1.AddToScheme(scheme)
assert.Nil(t, err)
err = v1alpha1.AddToScheme(scheme)
assert.Nil(t, err)
project := v1alpha1.AppProject{
ObjectMeta: metav1.ObjectMeta{Name: "default", Namespace: "argocd"},
}
appSet := v1alpha1.ApplicationSet{
ObjectMeta: metav1.ObjectMeta{
Name: "name",
Namespace: "argocd",
},
Spec: v1alpha1.ApplicationSetSpec{
GoTemplate: true,
Generators: []v1alpha1.ApplicationSetGenerator{
{
List: &v1alpha1.ListGenerator{
Elements: []apiextensionsv1.JSON{{
Raw: []byte(`{"name": "very-good-app"}`),
}, {
Raw: []byte(`{"name": "bad-app"}`),
}},
},
},
},
Template: v1alpha1.ApplicationSetTemplate{
ApplicationSetTemplateMeta: v1alpha1.ApplicationSetTemplateMeta{
Name: "{{ index (splitList \"-\" .name ) 2 }}",
Namespace: "argocd",
},
Spec: v1alpha1.ApplicationSpec{
Source: &v1alpha1.ApplicationSource{RepoURL: "https://github.com/argoproj/argocd-example-apps", Path: "guestbook"},
Project: "default",
Destination: v1alpha1.ApplicationDestination{Server: "https://kubernetes.default.svc"},
},
},
},
}
kubeclientset := kubefake.NewSimpleClientset()
argoDBMock := dbmocks.ArgoDB{}
argoObjs := []runtime.Object{&project}
client := fake.NewClientBuilder().WithScheme(scheme).WithObjects(&appSet).WithIndex(&v1alpha1.Application{}, ".metadata.controller", appControllerIndexer).Build()
r := ApplicationSetReconciler{
Client: client,
Scheme: scheme,
Renderer: &utils.Render{},
Recorder: record.NewFakeRecorder(1),
Cache: &fakeCache{},
Generators: map[string]generators.Generator{
"List": generators.NewListGenerator(),
},
ArgoDB: &argoDBMock,
ArgoAppClientset: appclientset.NewSimpleClientset(argoObjs...),
KubeClientset: kubeclientset,
Policy: v1alpha1.ApplicationsSyncPolicySync,
ArgoCDNamespace: "argocd",
}
req := ctrl.Request{
NamespacedName: types.NamespacedName{
Namespace: "argocd",
Name: "name",
},
}
// Verify that on generatorsError, no error is returned, but the object is requeued
res, err := r.Reconcile(context.Background(), req)
assert.Nil(t, err)
assert.True(t, res.RequeueAfter == ReconcileRequeueOnValidationError)
var app v1alpha1.Application
// make sure good app got created
err = r.Client.Get(context.TODO(), crtclient.ObjectKey{Namespace: "argocd", Name: "app"}, &app)
assert.NoError(t, err)
assert.Equal(t, app.Name, "app")
}
func TestSetApplicationSetStatusCondition(t *testing.T) {
scheme := runtime.NewScheme()
err := v1alpha1.AddToScheme(scheme)

View File

@@ -781,8 +781,6 @@ func NewApplicationSetCommand(clientOpts *argocdclient.ClientOptions) *cobra.Com
}
}
// sourcePosition startes with 1, thus, it needs to be decreased by 1 to find the correct index in the list of sources
sourcePosition = sourcePosition - 1
visited := cmdutil.SetAppSpecOptions(c.Flags(), &app.Spec, &appOpts, sourcePosition)
if visited == 0 {
log.Error("Please set at least one option to update")

View File

@@ -1,48 +1,75 @@
setTimeout(function() {
const callbackName = 'callback_' + new Date().getTime();
window[callbackName] = function (response) {
const div = document.createElement('div');
div.innerHTML = response.html;
document.querySelector(".md-header__inner > .md-header__title").appendChild(div);
const container = div.querySelector('.rst-versions');
var caret = document.createElement('div');
caret.innerHTML = "<i class='fa fa-caret-down dropdown-caret'></i>"
caret.classList.add('dropdown-caret')
div.querySelector('.rst-current-version').appendChild(caret);
const targetNode = document.querySelector('.md-header__inner');
const observerOptions = {
childList: true,
subtree: true
};
const observerCallback = function(mutationsList, observer) {
for (let mutation of mutationsList) {
if (mutation.type === 'childList') {
const titleElement = document.querySelector('.md-header__inner > .md-header__title');
if (titleElement) {
initializeVersionDropdown();
observer.disconnect();
}
}
}
};
const observer = new MutationObserver(observerCallback);
observer.observe(targetNode, observerOptions);
function initializeVersionDropdown() {
const callbackName = 'callback_' + new Date().getTime();
window[callbackName] = function(response) {
const div = document.createElement('div');
div.innerHTML = response.html;
document.querySelector(".md-header__inner > .md-header__title").appendChild(div);
const container = div.querySelector('.rst-versions');
var caret = document.createElement('div');
caret.innerHTML = "<i class='fa fa-caret-down dropdown-caret'></i>";
caret.classList.add('dropdown-caret');
div.querySelector('.rst-current-version').appendChild(caret);
div.querySelector('.rst-current-version').addEventListener('click', function() {
container.classList.toggle('shift-up');
});
};
var CSSLink = document.createElement('link');
CSSLink.rel='stylesheet';
CSSLink.rel = 'stylesheet';
CSSLink.href = '/assets/versions.css';
document.getElementsByTagName('head')[0].appendChild(CSSLink);
var script = document.createElement('script');
script.src = 'https://argo-cd.readthedocs.io/_/api/v2/footer_html/?'+
script.src = 'https://argo-cd.readthedocs.io/_/api/v2/footer_html/?' +
'callback=' + callbackName + '&project=argo-cd&page=&theme=mkdocs&format=jsonp&docroot=docs&source_suffix=.md&version=' + (window['READTHEDOCS_DATA'] || { version: 'latest' }).version;
document.getElementsByTagName('head')[0].appendChild(script);
}, 0);
}
// VERSION WARNINGS
window.addEventListener("DOMContentLoaded", function() {
var rtdData = window['READTHEDOCS_DATA'] || { version: 'latest' };
var currentVersion = window.location.href.match(/\/en\/(release-(?:v\d+|\w+)|latest|stable)\//);
var margin = 30;
var headerHeight = document.getElementsByClassName("md-header")[0].offsetHeight;
if (rtdData.version === "latest") {
document.querySelector("div[data-md-component=announce]").innerHTML = "<div id='announce-msg'>You are viewing the docs for an unreleased version of Argo CD, <a href='https://argo-cd.readthedocs.io/en/stable/'>click here to go to the latest stable version.</a></div>"
var bannerHeight = document.getElementById('announce-msg').offsetHeight + margin
document.querySelector("header.md-header").style.top = bannerHeight +"px";
document.querySelector('style').textContent +=
"@media screen and (min-width: 76.25em){ .md-sidebar { height: 0; top:"+ (bannerHeight+headerHeight)+"px !important; }}"
document.querySelector('style').textContent +=
"@media screen and (min-width: 60em){ .md-sidebar--secondary { height: 0; top:"+ (bannerHeight+headerHeight)+"px !important; }}"
var headerHeight = document.getElementsByClassName("md-header")[0].offsetHeight;
if (currentVersion && currentVersion.length > 1) {
currentVersion = currentVersion[1];
if (currentVersion === "latest") {
document.querySelector("div[data-md-component=announce]").innerHTML = "<div id='announce-msg'>You are viewing the docs for an unreleased version of Argo CD, <a href='https://argo-cd.readthedocs.io/en/stable/'>click here to go to the latest stable version.</a></div>";
var bannerHeight = document.getElementById('announce-msg').offsetHeight + margin;
document.querySelector("header.md-header").style.top = bannerHeight + "px";
document.querySelector('style').textContent +=
"@media screen and (min-width: 76.25em){ .md-sidebar { height: 0; top:" + (bannerHeight + headerHeight) + "px !important; }}";
document.querySelector('style').textContent +=
"@media screen and (min-width: 60em){ .md-sidebar--secondary { height: 0; top:" + (bannerHeight + headerHeight) + "px !important; }}";
} else if (currentVersion !== "stable") {
document.querySelector("div[data-md-component=announce]").innerHTML = "<div id='announce-msg'>You are viewing the docs for a previous version of Argo CD, <a href='https://argo-cd.readthedocs.io/en/stable/'>click here to go to the latest stable version.</a></div>";
var bannerHeight = document.getElementById('announce-msg').offsetHeight + margin;
document.querySelector("header.md-header").style.top = bannerHeight + "px";
document.querySelector('style').textContent +=
"@media screen and (min-width: 76.25em){ .md-sidebar { height: 0; top:" + (bannerHeight + headerHeight) + "px !important; }}";
document.querySelector('style').textContent +=
"@media screen and (min-width: 60em){ .md-sidebar--secondary { height: 0; top:" + (bannerHeight + headerHeight) + "px !important; }}";
}
}
else if (rtdData.version !== "stable") {
document.querySelector("div[data-md-component=announce]").innerHTML = "<div id='announce-msg'>You are viewing the docs for a previous version of Argo CD, <a href='https://argo-cd.readthedocs.io/en/stable/'>click here to go to the latest stable version.</a></div>"
var bannerHeight = document.getElementById('announce-msg').offsetHeight + margin
document.querySelector("header.md-header").style.top = bannerHeight +"px";
document.querySelector('style').textContent +=
"@media screen and (min-width: 76.25em){ .md-sidebar { height: 0; top:"+ (bannerHeight+headerHeight)+"px !important; }}"
document.querySelector('style').textContent +=
"@media screen and (min-width: 60em){ .md-sidebar--secondary { height: 0; top:"+ (bannerHeight+headerHeight)+"px !important; }}"
}
});
});

View File

@@ -326,7 +326,7 @@ As with other generators, clusters *must* already be defined within Argo CD, in
In addition to the flattened key/value pairs from the configuration file, the following generator parameters are provided:
- `{{.path.path}}`: The path to the directory containing matching configuration file within the Git repository. Example: `/clusters/clusterA`, if the config file was `/clusters/clusterA/config.json`
- `{{index .path n}}`: The path to the matching configuration file within the Git repository, split into array elements (`n` - array index). Example: `index .path 0: clusters`, `index .path 1: clusterA`
- `{{index .path.segments n}}`: The path to the matching configuration file within the Git repository, split into array elements (`n` - array index). Example: `index .path.segments 0: clusters`, `index .path.segments 1: clusterA`
- `{{.path.basename}}`: Basename of the path to the directory containing the configuration file (e.g. `clusterA`, with the above example.)
- `{{.path.basenameNormalized}}`: This field is the same as `.path.basename` with unsupported characters replaced with `-` (e.g. a `path` of `/directory/directory_2`, and `.path.basename` of `directory_2` would produce `directory-2` here).
- `{{.path.filename}}`: The matched filename. e.g., `config.json` in the above example.
@@ -360,7 +360,7 @@ spec:
files:
- path: "applicationset/examples/git-generator-files-discovery/cluster-config/**/config.json"
values:
base_dir: "{{index .path 0}}/{{index .path 1}}/{{index .path 2}}"
base_dir: "{{index .path.segments 0}}/{{index .path.segments 1}}/{{index .path.segments 2}}"
template:
metadata:
name: '{{.cluster.name}}-guestbook'

2
go.mod
View File

@@ -13,7 +13,7 @@ require (
github.com/TomOnTime/utfutil v0.0.0-20180511104225-09c41003ee1d
github.com/alicebob/miniredis/v2 v2.30.4
github.com/antonmedv/expr v1.15.2
github.com/argoproj/gitops-engine v0.7.1-0.20240416142647-fbecbb86e412
github.com/argoproj/gitops-engine v0.7.1-0.20240702153804-5995eca2fb63
github.com/argoproj/notifications-engine v0.4.1-0.20240403133627-f48567108f01
github.com/argoproj/pkg v0.13.7-0.20230626144333-d56162821bd1
github.com/aws/aws-sdk-go v1.50.8

4
go.sum
View File

@@ -696,8 +696,8 @@ github.com/apache/thrift v0.12.0/go.mod h1:cp2SuWMxlEZw2r+iP2GNCdIi4C1qmUzdZFSVb
github.com/apache/thrift v0.13.0/go.mod h1:cp2SuWMxlEZw2r+iP2GNCdIi4C1qmUzdZFSVb+bacwQ=
github.com/apache/thrift v0.16.0/go.mod h1:PHK3hniurgQaNMZYaCLEqXKsYK8upmhPbmdP2FXSqgU=
github.com/appscode/go v0.0.0-20191119085241-0887d8ec2ecc/go.mod h1:OawnOmAL4ZX3YaPdN+8HTNwBveT1jMsqP74moa9XUbE=
github.com/argoproj/gitops-engine v0.7.1-0.20240416142647-fbecbb86e412 h1:je2wJpWtaoS55mA5MBPCeDnKMeF42pkxO9Oa5KbWrdg=
github.com/argoproj/gitops-engine v0.7.1-0.20240416142647-fbecbb86e412/go.mod h1:gWE8uROi7hIkWGNAVM+8FWkMfo0vZ03SLx/aFw/DBzg=
github.com/argoproj/gitops-engine v0.7.1-0.20240702153804-5995eca2fb63 h1:HsXZiXycvpK/+HENrSyUEGnH1afvvPJiBfbQowwbRXg=
github.com/argoproj/gitops-engine v0.7.1-0.20240702153804-5995eca2fb63/go.mod h1:d4eLldeEFyZIcVySAMhXhnh1tTa4qfvPYfut9B8UClw=
github.com/argoproj/notifications-engine v0.4.1-0.20240403133627-f48567108f01 h1:/V8+HM0VPPTrdjTwUrkIj5a+SjaU//tJwfIXJ1QAOvg=
github.com/argoproj/notifications-engine v0.4.1-0.20240403133627-f48567108f01/go.mod h1:N0A4sEws2soZjEpY4hgZpQS8mRIEw6otzwfkgc3g9uQ=
github.com/argoproj/pkg v0.13.7-0.20230626144333-d56162821bd1 h1:qsHwwOJ21K2Ao0xPju1sNuqphyMnMYkyB3ZLoLtxWpo=

View File

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

View File

@@ -40,7 +40,7 @@ spec:
serviceAccountName: argocd-redis
containers:
- name: redis
image: redis:7.0.14-alpine
image: redis:7.0.15-alpine
imagePullPolicy: Always
args:
- "--save"

View File

@@ -21224,7 +21224,7 @@ spec:
key: applicationsetcontroller.enable.scm.providers
name: argocd-cmd-params-cm
optional: true
image: quay.io/argoproj/argocd:v2.11.2
image: quay.io/argoproj/argocd:v2.11.4
imagePullPolicy: Always
name: argocd-applicationset-controller
ports:
@@ -21326,7 +21326,7 @@ spec:
secretKeyRef:
key: auth
name: argocd-redis
image: redis:7.0.14-alpine
image: redis:7.0.15-alpine
imagePullPolicy: Always
name: redis
ports:
@@ -21342,7 +21342,7 @@ spec:
- argocd
- admin
- redis-initial-password
image: quay.io/argoproj/argocd:v2.11.2
image: quay.io/argoproj/argocd:v2.11.4
imagePullPolicy: IfNotPresent
name: secret-init
securityContext:
@@ -21583,7 +21583,7 @@ spec:
value: /helm-working-dir
- name: HELM_DATA_HOME
value: /helm-working-dir
image: quay.io/argoproj/argocd:v2.11.2
image: quay.io/argoproj/argocd:v2.11.4
imagePullPolicy: Always
livenessProbe:
failureThreshold: 3
@@ -21635,7 +21635,7 @@ spec:
- -n
- /usr/local/bin/argocd
- /var/run/argocd/argocd-cmp-server
image: quay.io/argoproj/argocd:v2.11.2
image: quay.io/argoproj/argocd:v2.11.4
name: copyutil
securityContext:
allowPrivilegeEscalation: false
@@ -21907,7 +21907,7 @@ spec:
key: controller.ignore.normalizer.jq.timeout
name: argocd-cmd-params-cm
optional: true
image: quay.io/argoproj/argocd:v2.11.2
image: quay.io/argoproj/argocd:v2.11.4
imagePullPolicy: Always
name: argocd-application-controller
ports:

View File

@@ -12,4 +12,4 @@ resources:
images:
- name: quay.io/argoproj/argocd
newName: quay.io/argoproj/argocd
newTag: v2.11.2
newTag: v2.11.4

View File

@@ -12,7 +12,7 @@ patches:
images:
- name: quay.io/argoproj/argocd
newName: quay.io/argoproj/argocd
newTag: v2.11.2
newTag: v2.11.4
resources:
- ../../base/application-controller
- ../../base/applicationset-controller

View File

@@ -1219,7 +1219,7 @@ spec:
automountServiceAccountToken: false
initContainers:
- name: config-init
image: redis:7.0.14-alpine
image: redis:7.0.15-alpine
imagePullPolicy: IfNotPresent
resources:
{}
@@ -1258,7 +1258,7 @@ spec:
containers:
- name: redis
image: redis:7.0.14-alpine
image: redis:7.0.15-alpine
imagePullPolicy: IfNotPresent
command:
- redis-server
@@ -1321,7 +1321,7 @@ spec:
- /bin/sh
- /readonly-config/trigger-failover-if-master.sh
- name: sentinel
image: redis:7.0.14-alpine
image: redis:7.0.15-alpine
imagePullPolicy: IfNotPresent
command:
- redis-sentinel
@@ -1378,7 +1378,7 @@ spec:
{}
- name: split-brain-fix
image: redis:7.0.14-alpine
image: redis:7.0.15-alpine
imagePullPolicy: IfNotPresent
command:
- sh

View File

@@ -25,7 +25,7 @@ redis-ha:
enabled: true
image:
repository: redis
tag: 7.0.14-alpine
tag: 7.0.15-alpine
containerSecurityContext: null
sentinel:
bind: "0.0.0.0"

View File

@@ -22565,7 +22565,7 @@ spec:
key: applicationsetcontroller.enable.scm.providers
name: argocd-cmd-params-cm
optional: true
image: quay.io/argoproj/argocd:v2.11.2
image: quay.io/argoproj/argocd:v2.11.4
imagePullPolicy: Always
name: argocd-applicationset-controller
ports:
@@ -22688,7 +22688,7 @@ spec:
- -n
- /usr/local/bin/argocd
- /shared/argocd-dex
image: quay.io/argoproj/argocd:v2.11.2
image: quay.io/argoproj/argocd:v2.11.4
imagePullPolicy: Always
name: copyutil
securityContext:
@@ -22770,7 +22770,7 @@ spec:
key: notificationscontroller.selfservice.enabled
name: argocd-cmd-params-cm
optional: true
image: quay.io/argoproj/argocd:v2.11.2
image: quay.io/argoproj/argocd:v2.11.4
imagePullPolicy: Always
livenessProbe:
tcpSocket:
@@ -22889,7 +22889,7 @@ spec:
- argocd
- admin
- redis-initial-password
image: quay.io/argoproj/argocd:v2.11.2
image: quay.io/argoproj/argocd:v2.11.4
imagePullPolicy: IfNotPresent
name: secret-init
securityContext:
@@ -23158,7 +23158,7 @@ spec:
value: /helm-working-dir
- name: HELM_DATA_HOME
value: /helm-working-dir
image: quay.io/argoproj/argocd:v2.11.2
image: quay.io/argoproj/argocd:v2.11.4
imagePullPolicy: Always
livenessProbe:
failureThreshold: 3
@@ -23210,7 +23210,7 @@ spec:
- -n
- /usr/local/bin/argocd
- /var/run/argocd/argocd-cmp-server
image: quay.io/argoproj/argocd:v2.11.2
image: quay.io/argoproj/argocd:v2.11.4
name: copyutil
securityContext:
allowPrivilegeEscalation: false
@@ -23534,7 +23534,7 @@ spec:
key: server.api.content.types
name: argocd-cmd-params-cm
optional: true
image: quay.io/argoproj/argocd:v2.11.2
image: quay.io/argoproj/argocd:v2.11.4
imagePullPolicy: Always
livenessProbe:
httpGet:
@@ -23833,7 +23833,7 @@ spec:
key: controller.ignore.normalizer.jq.timeout
name: argocd-cmd-params-cm
optional: true
image: quay.io/argoproj/argocd:v2.11.2
image: quay.io/argoproj/argocd:v2.11.4
imagePullPolicy: Always
name: argocd-application-controller
ports:
@@ -23916,7 +23916,7 @@ spec:
secretKeyRef:
key: auth
name: argocd-redis
image: redis:7.0.14-alpine
image: redis:7.0.15-alpine
imagePullPolicy: IfNotPresent
lifecycle:
preStop:
@@ -23976,7 +23976,7 @@ spec:
secretKeyRef:
key: auth
name: argocd-redis
image: redis:7.0.14-alpine
image: redis:7.0.15-alpine
imagePullPolicy: IfNotPresent
lifecycle: {}
livenessProbe:
@@ -24034,7 +24034,7 @@ spec:
secretKeyRef:
key: auth
name: argocd-redis
image: redis:7.0.14-alpine
image: redis:7.0.15-alpine
imagePullPolicy: IfNotPresent
name: split-brain-fix
resources: {}
@@ -24069,7 +24069,7 @@ spec:
secretKeyRef:
key: auth
name: argocd-redis
image: redis:7.0.14-alpine
image: redis:7.0.15-alpine
imagePullPolicy: IfNotPresent
name: config-init
securityContext:

View File

@@ -1686,7 +1686,7 @@ spec:
key: applicationsetcontroller.enable.scm.providers
name: argocd-cmd-params-cm
optional: true
image: quay.io/argoproj/argocd:v2.11.2
image: quay.io/argoproj/argocd:v2.11.4
imagePullPolicy: Always
name: argocd-applicationset-controller
ports:
@@ -1809,7 +1809,7 @@ spec:
- -n
- /usr/local/bin/argocd
- /shared/argocd-dex
image: quay.io/argoproj/argocd:v2.11.2
image: quay.io/argoproj/argocd:v2.11.4
imagePullPolicy: Always
name: copyutil
securityContext:
@@ -1891,7 +1891,7 @@ spec:
key: notificationscontroller.selfservice.enabled
name: argocd-cmd-params-cm
optional: true
image: quay.io/argoproj/argocd:v2.11.2
image: quay.io/argoproj/argocd:v2.11.4
imagePullPolicy: Always
livenessProbe:
tcpSocket:
@@ -2010,7 +2010,7 @@ spec:
- argocd
- admin
- redis-initial-password
image: quay.io/argoproj/argocd:v2.11.2
image: quay.io/argoproj/argocd:v2.11.4
imagePullPolicy: IfNotPresent
name: secret-init
securityContext:
@@ -2279,7 +2279,7 @@ spec:
value: /helm-working-dir
- name: HELM_DATA_HOME
value: /helm-working-dir
image: quay.io/argoproj/argocd:v2.11.2
image: quay.io/argoproj/argocd:v2.11.4
imagePullPolicy: Always
livenessProbe:
failureThreshold: 3
@@ -2331,7 +2331,7 @@ spec:
- -n
- /usr/local/bin/argocd
- /var/run/argocd/argocd-cmp-server
image: quay.io/argoproj/argocd:v2.11.2
image: quay.io/argoproj/argocd:v2.11.4
name: copyutil
securityContext:
allowPrivilegeEscalation: false
@@ -2655,7 +2655,7 @@ spec:
key: server.api.content.types
name: argocd-cmd-params-cm
optional: true
image: quay.io/argoproj/argocd:v2.11.2
image: quay.io/argoproj/argocd:v2.11.4
imagePullPolicy: Always
livenessProbe:
httpGet:
@@ -2954,7 +2954,7 @@ spec:
key: controller.ignore.normalizer.jq.timeout
name: argocd-cmd-params-cm
optional: true
image: quay.io/argoproj/argocd:v2.11.2
image: quay.io/argoproj/argocd:v2.11.4
imagePullPolicy: Always
name: argocd-application-controller
ports:
@@ -3037,7 +3037,7 @@ spec:
secretKeyRef:
key: auth
name: argocd-redis
image: redis:7.0.14-alpine
image: redis:7.0.15-alpine
imagePullPolicy: IfNotPresent
lifecycle:
preStop:
@@ -3097,7 +3097,7 @@ spec:
secretKeyRef:
key: auth
name: argocd-redis
image: redis:7.0.14-alpine
image: redis:7.0.15-alpine
imagePullPolicy: IfNotPresent
lifecycle: {}
livenessProbe:
@@ -3155,7 +3155,7 @@ spec:
secretKeyRef:
key: auth
name: argocd-redis
image: redis:7.0.14-alpine
image: redis:7.0.15-alpine
imagePullPolicy: IfNotPresent
name: split-brain-fix
resources: {}
@@ -3190,7 +3190,7 @@ spec:
secretKeyRef:
key: auth
name: argocd-redis
image: redis:7.0.14-alpine
image: redis:7.0.15-alpine
imagePullPolicy: IfNotPresent
name: config-init
securityContext:

View File

@@ -21682,7 +21682,7 @@ spec:
key: applicationsetcontroller.enable.scm.providers
name: argocd-cmd-params-cm
optional: true
image: quay.io/argoproj/argocd:v2.11.2
image: quay.io/argoproj/argocd:v2.11.4
imagePullPolicy: Always
name: argocd-applicationset-controller
ports:
@@ -21805,7 +21805,7 @@ spec:
- -n
- /usr/local/bin/argocd
- /shared/argocd-dex
image: quay.io/argoproj/argocd:v2.11.2
image: quay.io/argoproj/argocd:v2.11.4
imagePullPolicy: Always
name: copyutil
securityContext:
@@ -21887,7 +21887,7 @@ spec:
key: notificationscontroller.selfservice.enabled
name: argocd-cmd-params-cm
optional: true
image: quay.io/argoproj/argocd:v2.11.2
image: quay.io/argoproj/argocd:v2.11.4
imagePullPolicy: Always
livenessProbe:
tcpSocket:
@@ -21971,7 +21971,7 @@ spec:
secretKeyRef:
key: auth
name: argocd-redis
image: redis:7.0.14-alpine
image: redis:7.0.15-alpine
imagePullPolicy: Always
name: redis
ports:
@@ -21987,7 +21987,7 @@ spec:
- argocd
- admin
- redis-initial-password
image: quay.io/argoproj/argocd:v2.11.2
image: quay.io/argoproj/argocd:v2.11.4
imagePullPolicy: IfNotPresent
name: secret-init
securityContext:
@@ -22228,7 +22228,7 @@ spec:
value: /helm-working-dir
- name: HELM_DATA_HOME
value: /helm-working-dir
image: quay.io/argoproj/argocd:v2.11.2
image: quay.io/argoproj/argocd:v2.11.4
imagePullPolicy: Always
livenessProbe:
failureThreshold: 3
@@ -22280,7 +22280,7 @@ spec:
- -n
- /usr/local/bin/argocd
- /var/run/argocd/argocd-cmp-server
image: quay.io/argoproj/argocd:v2.11.2
image: quay.io/argoproj/argocd:v2.11.4
name: copyutil
securityContext:
allowPrivilegeEscalation: false
@@ -22602,7 +22602,7 @@ spec:
key: server.api.content.types
name: argocd-cmd-params-cm
optional: true
image: quay.io/argoproj/argocd:v2.11.2
image: quay.io/argoproj/argocd:v2.11.4
imagePullPolicy: Always
livenessProbe:
httpGet:
@@ -22901,7 +22901,7 @@ spec:
key: controller.ignore.normalizer.jq.timeout
name: argocd-cmd-params-cm
optional: true
image: quay.io/argoproj/argocd:v2.11.2
image: quay.io/argoproj/argocd:v2.11.4
imagePullPolicy: Always
name: argocd-application-controller
ports:

View File

@@ -803,7 +803,7 @@ spec:
key: applicationsetcontroller.enable.scm.providers
name: argocd-cmd-params-cm
optional: true
image: quay.io/argoproj/argocd:v2.11.2
image: quay.io/argoproj/argocd:v2.11.4
imagePullPolicy: Always
name: argocd-applicationset-controller
ports:
@@ -926,7 +926,7 @@ spec:
- -n
- /usr/local/bin/argocd
- /shared/argocd-dex
image: quay.io/argoproj/argocd:v2.11.2
image: quay.io/argoproj/argocd:v2.11.4
imagePullPolicy: Always
name: copyutil
securityContext:
@@ -1008,7 +1008,7 @@ spec:
key: notificationscontroller.selfservice.enabled
name: argocd-cmd-params-cm
optional: true
image: quay.io/argoproj/argocd:v2.11.2
image: quay.io/argoproj/argocd:v2.11.4
imagePullPolicy: Always
livenessProbe:
tcpSocket:
@@ -1092,7 +1092,7 @@ spec:
secretKeyRef:
key: auth
name: argocd-redis
image: redis:7.0.14-alpine
image: redis:7.0.15-alpine
imagePullPolicy: Always
name: redis
ports:
@@ -1108,7 +1108,7 @@ spec:
- argocd
- admin
- redis-initial-password
image: quay.io/argoproj/argocd:v2.11.2
image: quay.io/argoproj/argocd:v2.11.4
imagePullPolicy: IfNotPresent
name: secret-init
securityContext:
@@ -1349,7 +1349,7 @@ spec:
value: /helm-working-dir
- name: HELM_DATA_HOME
value: /helm-working-dir
image: quay.io/argoproj/argocd:v2.11.2
image: quay.io/argoproj/argocd:v2.11.4
imagePullPolicy: Always
livenessProbe:
failureThreshold: 3
@@ -1401,7 +1401,7 @@ spec:
- -n
- /usr/local/bin/argocd
- /var/run/argocd/argocd-cmp-server
image: quay.io/argoproj/argocd:v2.11.2
image: quay.io/argoproj/argocd:v2.11.4
name: copyutil
securityContext:
allowPrivilegeEscalation: false
@@ -1723,7 +1723,7 @@ spec:
key: server.api.content.types
name: argocd-cmd-params-cm
optional: true
image: quay.io/argoproj/argocd:v2.11.2
image: quay.io/argoproj/argocd:v2.11.4
imagePullPolicy: Always
livenessProbe:
httpGet:
@@ -2022,7 +2022,7 @@ spec:
key: controller.ignore.normalizer.jq.timeout
name: argocd-cmd-params-cm
optional: true
image: quay.io/argoproj/argocd:v2.11.2
image: quay.io/argoproj/argocd:v2.11.4
imagePullPolicy: Always
name: argocd-application-controller
ports:

View File

@@ -2731,7 +2731,10 @@ func (s *Service) UpdateRevisionForPaths(_ context.Context, request *apiclient.U
return nil, status.Errorf(codes.Internal, "unable to get changed files for repo %s with revision %s: %v", repo.Repo, revision, err)
}
changed := apppathutil.AppFilesHaveChanged(refreshPaths, files)
changed := false
if len(files) != 0 {
changed = apppathutil.AppFilesHaveChanged(refreshPaths, files)
}
if !changed {
logCtx.Debugf("no changes found for application %s in repo %s from revision %s to revision %s", request.AppName, repo.Repo, syncedRevision, revision)

View File

@@ -469,15 +469,16 @@ func (s *Server) GetManifests(ctx context.Context, q *application.ApplicationMan
}
sources := make([]appv1.ApplicationSource, 0)
appSpec := a.Spec.DeepCopy()
if a.Spec.HasMultipleSources() {
numOfSources := int64(len(a.Spec.GetSources()))
for i, pos := range q.SourcePositions {
if pos <= 0 || pos > numOfSources {
return fmt.Errorf("source position is out of range")
}
a.Spec.Sources[pos-1].TargetRevision = q.Revisions[i]
appSpec.Sources[pos-1].TargetRevision = q.Revisions[i]
}
sources = a.Spec.GetSources()
sources = appSpec.GetSources()
} else {
source := a.Spec.GetSource()
if q.GetRevision() != "" {
@@ -487,7 +488,7 @@ func (s *Server) GetManifests(ctx context.Context, q *application.ApplicationMan
}
// Store the map of all sources having ref field into a map for applications with sources field
refSources, err := argo.GetRefSources(context.Background(), a.Spec, s.db)
refSources, err := argo.GetRefSources(context.Background(), *appSpec, s.db)
if err != nil {
return fmt.Errorf("failed to get ref sources: %v", err)
}
@@ -560,7 +561,7 @@ func (s *Server) GetManifests(ctx context.Context, q *application.ApplicationMan
manifestInfo.Manifests[i] = string(data)
}
}
manifests.Manifests = manifestInfo.Manifests
manifests.Manifests = append(manifests.Manifests, manifestInfo.Manifests...)
}
return manifests, nil

View File

@@ -187,15 +187,11 @@ func (s *Server) Create(ctx context.Context, q *cluster.ClusterCreateRequest) (*
// Get returns a cluster from a query
func (s *Server) Get(ctx context.Context, q *cluster.ClusterQuery) (*appv1.Cluster, error) {
c, err := s.getClusterWith403IfNotExist(ctx, q)
c, err := s.getClusterAndVerifyAccess(ctx, q, rbacpolicy.ActionGet)
if err != nil {
return nil, err
}
if err := s.enf.EnforceErr(ctx.Value("claims"), rbacpolicy.ResourceClusters, rbacpolicy.ActionGet, CreateClusterRBACObject(c.Project, q.Server)); err != nil {
return nil, err
}
return s.toAPIResponse(c), nil
}
@@ -207,6 +203,21 @@ func (s *Server) getClusterWith403IfNotExist(ctx context.Context, q *cluster.Clu
return repo, nil
}
func (s *Server) getClusterAndVerifyAccess(ctx context.Context, q *cluster.ClusterQuery, action string) (*appv1.Cluster, error) {
c, err := s.getClusterWith403IfNotExist(ctx, q)
if err != nil {
return nil, err
}
// verify that user can do the specified action inside project where cluster is located
if !s.enf.Enforce(ctx.Value("claims"), rbacpolicy.ResourceClusters, action, CreateClusterRBACObject(c.Project, c.Server)) {
log.WithField("cluster", q.Server).Warnf("encountered permissions issue while processing request: %v", err)
return nil, common.PermissionDeniedAPIError
}
return c, nil
}
func (s *Server) getCluster(ctx context.Context, q *cluster.ClusterQuery) (*appv1.Cluster, error) {
if q.Id != nil {
q.Server = ""
@@ -278,20 +289,16 @@ var clusterFieldsByPath = map[string]func(updated *appv1.Cluster, existing *appv
// Update updates a cluster
func (s *Server) Update(ctx context.Context, q *cluster.ClusterUpdateRequest) (*appv1.Cluster, error) {
c, err := s.getClusterWith403IfNotExist(ctx, &cluster.ClusterQuery{
c, err := s.getClusterAndVerifyAccess(ctx, &cluster.ClusterQuery{
Server: q.Cluster.Server,
Name: q.Cluster.Name,
Id: q.Id,
})
}, rbacpolicy.ActionUpdate)
if err != nil {
return nil, err
}
// verify that user can do update inside project where cluster is located
if !s.enf.Enforce(ctx.Value("claims"), rbacpolicy.ResourceClusters, rbacpolicy.ActionUpdate, CreateClusterRBACObject(c.Project, c.Server)) {
return nil, common.PermissionDeniedAPIError
}
if len(q.UpdatedFields) == 0 || sets.NewString(q.UpdatedFields...).Has("project") {
// verify that user can do update inside project where cluster will be located
if !s.enf.Enforce(ctx.Value("claims"), rbacpolicy.ResourceClusters, rbacpolicy.ActionUpdate, CreateClusterRBACObject(q.Cluster.Project, c.Server)) {
@@ -341,7 +348,8 @@ func (s *Server) Delete(ctx context.Context, q *cluster.ClusterQuery) (*cluster.
if q.Name != "" {
servers, err := s.db.GetClusterServersByName(ctx, q.Name)
if err != nil {
return nil, err
log.WithField("cluster", q.Name).Warnf("failed to get cluster servers by name: %v", err)
return nil, common.PermissionDeniedAPIError
}
for _, server := range servers {
if err := enforceAndDelete(s, ctx, server, c.Project); err != nil {
@@ -359,7 +367,8 @@ func (s *Server) Delete(ctx context.Context, q *cluster.ClusterQuery) (*cluster.
func enforceAndDelete(s *Server, ctx context.Context, server, project string) error {
if err := s.enf.EnforceErr(ctx.Value("claims"), rbacpolicy.ResourceClusters, rbacpolicy.ActionDelete, CreateClusterRBACObject(project, server)); err != nil {
return err
log.WithField("cluster", server).Warnf("encountered permissions issue while processing request: %v", err)
return common.PermissionDeniedAPIError
}
if err := s.db.DeleteCluster(ctx, server); err != nil {
return err
@@ -378,16 +387,19 @@ func (s *Server) RotateAuth(ctx context.Context, q *cluster.ClusterQuery) (*clus
if q.Name != "" {
servers, err = s.db.GetClusterServersByName(ctx, q.Name)
if err != nil {
return nil, status.Errorf(codes.NotFound, "failed to get cluster servers by name: %v", err)
log.WithField("cluster", q.Name).Warnf("failed to get cluster servers by name: %v", err)
return nil, common.PermissionDeniedAPIError
}
for _, server := range servers {
if err := s.enf.EnforceErr(ctx.Value("claims"), rbacpolicy.ResourceClusters, rbacpolicy.ActionUpdate, CreateClusterRBACObject(clust.Project, server)); err != nil {
return nil, status.Errorf(codes.PermissionDenied, "encountered permissions issue while processing request: %v", err)
log.WithField("cluster", server).Warnf("encountered permissions issue while processing request: %v", err)
return nil, common.PermissionDeniedAPIError
}
}
} else {
if err := s.enf.EnforceErr(ctx.Value("claims"), rbacpolicy.ResourceClusters, rbacpolicy.ActionUpdate, CreateClusterRBACObject(clust.Project, q.Server)); err != nil {
return nil, status.Errorf(codes.PermissionDenied, "encountered permissions issue while processing request: %v", err)
log.WithField("cluster", q.Server).Warnf("encountered permissions issue while processing request: %v", err)
return nil, common.PermissionDeniedAPIError
}
servers = append(servers, q.Server)
}
@@ -467,13 +479,10 @@ func (s *Server) toAPIResponse(clust *appv1.Cluster) *appv1.Cluster {
// InvalidateCache invalidates cluster cache
func (s *Server) InvalidateCache(ctx context.Context, q *cluster.ClusterQuery) (*appv1.Cluster, error) {
cls, err := s.getClusterWith403IfNotExist(ctx, q)
cls, err := s.getClusterAndVerifyAccess(ctx, q, rbacpolicy.ActionUpdate)
if err != nil {
return nil, err
}
if err := s.enf.EnforceErr(ctx.Value("claims"), rbacpolicy.ResourceClusters, rbacpolicy.ActionUpdate, CreateClusterRBACObject(cls.Project, q.Server)); err != nil {
return nil, err
}
now := v1.Now()
cls.RefreshRequestedAt = &now
cls, err = s.db.UpdateCluster(ctx, cls)

View File

@@ -4,6 +4,9 @@ import (
"context"
"encoding/json"
"fmt"
"github.com/argoproj/argo-cd/v2/server/rbacpolicy"
"github.com/argoproj/argo-cd/v2/util/assets"
"github.com/golang-jwt/jwt/v4"
"reflect"
"testing"
"time"
@@ -51,6 +54,16 @@ func newNoopEnforcer() *rbac.Enforcer {
return enf
}
func newEnforcer() *rbac.Enforcer {
enforcer := rbac.NewEnforcer(fake.NewSimpleClientset(test.NewFakeConfigMap()), test.FakeArgoCDNamespace, common.ArgoCDRBACConfigMapName, nil)
_ = enforcer.SetBuiltinPolicy(assets.BuiltinPolicyCSV)
enforcer.SetDefaultRole("role:test")
enforcer.SetClaimsEnforcerFunc(func(claims jwt.Claims, rvals ...interface{}) bool {
return true
})
return enforcer
}
func TestUpdateCluster_RejectInvalidParams(t *testing.T) {
testCases := []struct {
name string
@@ -604,3 +617,152 @@ func TestListCluster(t *testing.T) {
})
}
}
func TestGetClusterAndVerifyAccess(t *testing.T) {
t.Run("GetClusterAndVerifyAccess - No Cluster", func(t *testing.T) {
db := &dbmocks.ArgoDB{}
mockCluster := v1alpha1.Cluster{
Name: "test/ing",
Server: "https://127.0.0.1",
Namespaces: []string{"default", "kube-system"},
}
mockClusterList := v1alpha1.ClusterList{
ListMeta: v1.ListMeta{},
Items: []v1alpha1.Cluster{
mockCluster,
},
}
db.On("ListClusters", mock.Anything).Return(&mockClusterList, nil)
server := NewServer(db, newNoopEnforcer(), newServerInMemoryCache(), &kubetest.MockKubectlCmd{})
cluster, err := server.getClusterAndVerifyAccess(context.Background(), &clusterapi.ClusterQuery{
Name: "test/not-exists",
}, rbacpolicy.ActionGet)
assert.Nil(t, cluster)
assert.ErrorIs(t, err, common.PermissionDeniedAPIError)
})
t.Run("GetClusterAndVerifyAccess - Permissions Denied", func(t *testing.T) {
db := &dbmocks.ArgoDB{}
mockCluster := v1alpha1.Cluster{
Name: "test/ing",
Server: "https://127.0.0.1",
Namespaces: []string{"default", "kube-system"},
}
mockClusterList := v1alpha1.ClusterList{
ListMeta: v1.ListMeta{},
Items: []v1alpha1.Cluster{
mockCluster,
},
}
db.On("ListClusters", mock.Anything).Return(&mockClusterList, nil)
server := NewServer(db, newEnforcer(), newServerInMemoryCache(), &kubetest.MockKubectlCmd{})
cluster, err := server.getClusterAndVerifyAccess(context.Background(), &clusterapi.ClusterQuery{
Name: "test/ing",
}, rbacpolicy.ActionGet)
assert.Nil(t, cluster)
assert.ErrorIs(t, err, common.PermissionDeniedAPIError)
})
}
func TestNoClusterEnumeration(t *testing.T) {
db := &dbmocks.ArgoDB{}
mockCluster := v1alpha1.Cluster{
Name: "test/ing",
Server: "https://127.0.0.1",
Namespaces: []string{"default", "kube-system"},
}
mockClusterList := v1alpha1.ClusterList{
ListMeta: v1.ListMeta{},
Items: []v1alpha1.Cluster{
mockCluster,
},
}
db.On("ListClusters", mock.Anything).Return(&mockClusterList, nil)
db.On("GetCluster", mock.Anything, mock.Anything).Return(&mockCluster, nil)
server := NewServer(db, newEnforcer(), newServerInMemoryCache(), &kubetest.MockKubectlCmd{})
t.Run("Get", func(t *testing.T) {
_, err := server.Get(context.Background(), &clusterapi.ClusterQuery{
Name: "cluster-not-exists",
})
assert.Error(t, err)
assert.Equal(t, common.PermissionDeniedAPIError.Error(), err.Error(), "error message must be _only_ the permission error, to avoid leaking information about cluster existence")
_, err = server.Get(context.Background(), &clusterapi.ClusterQuery{
Name: "test/ing",
})
assert.Error(t, err)
assert.Equal(t, common.PermissionDeniedAPIError.Error(), err.Error(), "error message must be _only_ the permission error, to avoid leaking information about cluster existence")
})
t.Run("Update", func(t *testing.T) {
_, err := server.Update(context.Background(), &clusterapi.ClusterUpdateRequest{
Cluster: &v1alpha1.Cluster{
Name: "cluster-not-exists",
},
})
assert.Error(t, err)
assert.Equal(t, common.PermissionDeniedAPIError.Error(), err.Error(), "error message must be _only_ the permission error, to avoid leaking information about cluster existence")
_, err = server.Update(context.Background(), &clusterapi.ClusterUpdateRequest{
Cluster: &v1alpha1.Cluster{
Name: "test/ing",
},
})
assert.Error(t, err)
assert.Equal(t, common.PermissionDeniedAPIError.Error(), err.Error(), "error message must be _only_ the permission error, to avoid leaking information about cluster existence")
})
t.Run("Delete", func(t *testing.T) {
_, err := server.Delete(context.Background(), &clusterapi.ClusterQuery{
Server: "https://127.0.0.2",
})
assert.Error(t, err)
assert.Equal(t, common.PermissionDeniedAPIError.Error(), err.Error(), "error message must be _only_ the permission error, to avoid leaking information about cluster existence")
_, err = server.Delete(context.Background(), &clusterapi.ClusterQuery{
Server: "https://127.0.0.1",
})
assert.Error(t, err)
assert.Equal(t, common.PermissionDeniedAPIError.Error(), err.Error(), "error message must be _only_ the permission error, to avoid leaking information about cluster existence")
})
t.Run("RotateAuth", func(t *testing.T) {
_, err := server.RotateAuth(context.Background(), &clusterapi.ClusterQuery{
Server: "https://127.0.0.2",
})
assert.Error(t, err)
assert.Equal(t, common.PermissionDeniedAPIError.Error(), err.Error(), "error message must be _only_ the permission error, to avoid leaking information about cluster existence")
_, err = server.RotateAuth(context.Background(), &clusterapi.ClusterQuery{
Server: "https://127.0.0.1",
})
assert.Error(t, err)
assert.Equal(t, common.PermissionDeniedAPIError.Error(), err.Error(), "error message must be _only_ the permission error, to avoid leaking information about cluster existence")
})
t.Run("InvalidateCache", func(t *testing.T) {
_, err := server.InvalidateCache(context.Background(), &clusterapi.ClusterQuery{
Server: "https://127.0.0.2",
})
assert.Error(t, err)
assert.Equal(t, common.PermissionDeniedAPIError.Error(), err.Error(), "error message must be _only_ the permission error, to avoid leaking information about cluster existence")
_, err = server.InvalidateCache(context.Background(), &clusterapi.ClusterQuery{
Server: "https://127.0.0.1",
})
assert.Error(t, err)
assert.Equal(t, common.PermissionDeniedAPIError.Error(), err.Error(), "error message must be _only_ the permission error, to avoid leaking information about cluster existence")
})
}

View File

@@ -109,7 +109,6 @@ func (s *Server) Get(ctx context.Context, q *settingspkg.SettingsQuery) (*settin
UserLoginsDisabled: userLoginsDisabled,
KustomizeVersions: kustomizeVersions,
UiCssURL: argoCDSettings.UiCssURL,
PasswordPattern: argoCDSettings.PasswordPattern,
TrackingMethod: trackingMethod,
ExecEnabled: argoCDSettings.ExecEnabled,
AppsInAnyNamespaceEnabled: s.appsInAnyNamespaceEnabled,
@@ -122,6 +121,9 @@ func (s *Server) Get(ctx context.Context, q *settingspkg.SettingsQuery) (*settin
set.UiBannerPosition = argoCDSettings.UiBannerPosition
set.ControllerNamespace = s.mgr.GetNamespace()
}
if sessionmgr.LoggedIn(ctx) {
set.PasswordPattern = argoCDSettings.PasswordPattern
}
if argoCDSettings.DexConfig != "" {
var cfg settingspkg.DexConfig
err = yaml.Unmarshal([]byte(argoCDSettings.DexConfig), &cfg)

View File

@@ -8,7 +8,7 @@ RUN ln -s /usr/lib/$(uname -m)-linux-gnu /usr/lib/linux-gnu
# Please make sure to also check the contained yarn version and update the references below when upgrading this image's version
FROM docker.io/library/node:21.7.1@sha256:b9ccc4aca32eebf124e0ca0fd573dacffba2b9236987a1d4d2625ce3c162ecc8 as node
FROM docker.io/library/golang:1.21.9@sha256:7d0dcbe5807b1ad7272a598fbf9d7af15b5e2bed4fd6c4c2b5b3684df0b317dd as golang
FROM docker.io/library/golang:1.21.10@sha256:16438a8e66c0c984f732e815ee5b7d715b8e33e81bac6d6a3750b1067744e7ca as golang
FROM docker.io/library/registry:2.8@sha256:fb9c9aef62af3955f6014613456551c92e88a67dcf1fc51f5f91bcbd1832813f as registry

View File

@@ -523,100 +523,6 @@ func TestSimpleListGeneratorGoTemplate(t *testing.T) {
}
func TestCreateApplicationDespiteParamsError(t *testing.T) {
expectedErrorMessage := `failed to execute go template {{.cluster}}-guestbook: template: :1:2: executing "" at <.cluster>: map has no entry for key "cluster"`
expectedConditionsParamsError := []v1alpha1.ApplicationSetCondition{
{
Type: v1alpha1.ApplicationSetConditionErrorOccurred,
Status: v1alpha1.ApplicationSetConditionStatusTrue,
Message: expectedErrorMessage,
Reason: v1alpha1.ApplicationSetReasonRenderTemplateParamsError,
},
{
Type: v1alpha1.ApplicationSetConditionParametersGenerated,
Status: v1alpha1.ApplicationSetConditionStatusFalse,
Message: expectedErrorMessage,
Reason: v1alpha1.ApplicationSetReasonErrorOccurred,
},
{
Type: v1alpha1.ApplicationSetConditionResourcesUpToDate,
Status: v1alpha1.ApplicationSetConditionStatusFalse,
Message: expectedErrorMessage,
Reason: v1alpha1.ApplicationSetReasonRenderTemplateParamsError,
},
}
expectedApp := argov1alpha1.Application{
TypeMeta: metav1.TypeMeta{
Kind: application.ApplicationKind,
APIVersion: "argoproj.io/v1alpha1",
},
ObjectMeta: metav1.ObjectMeta{
Name: "my-cluster-guestbook",
Namespace: fixture.TestNamespace(),
Finalizers: []string{"resources-finalizer.argocd.argoproj.io"},
},
Spec: argov1alpha1.ApplicationSpec{
Project: "default",
Source: &argov1alpha1.ApplicationSource{
RepoURL: "https://github.com/argoproj/argocd-example-apps.git",
TargetRevision: "HEAD",
Path: "guestbook",
},
Destination: argov1alpha1.ApplicationDestination{
Server: "https://kubernetes.default.svc",
Namespace: "guestbook",
},
},
}
Given(t).
// Create a ListGenerator-based ApplicationSet
When().Create(v1alpha1.ApplicationSet{ObjectMeta: metav1.ObjectMeta{
Name: "simple-list-generator",
},
Spec: v1alpha1.ApplicationSetSpec{
GoTemplate: true,
GoTemplateOptions: []string{"missingkey=error"},
Template: v1alpha1.ApplicationSetTemplate{
ApplicationSetTemplateMeta: v1alpha1.ApplicationSetTemplateMeta{Name: "{{.cluster}}-guestbook"},
Spec: argov1alpha1.ApplicationSpec{
Project: "default",
Source: &argov1alpha1.ApplicationSource{
RepoURL: "https://github.com/argoproj/argocd-example-apps.git",
TargetRevision: "HEAD",
Path: "guestbook",
},
Destination: argov1alpha1.ApplicationDestination{
Server: "{{.url}}",
Namespace: "guestbook",
},
},
},
Generators: []v1alpha1.ApplicationSetGenerator{
{
List: &v1alpha1.ListGenerator{
Elements: []apiextensionsv1.JSON{
{
Raw: []byte(`{"cluster": "my-cluster","url": "https://kubernetes.default.svc"}`),
},
{
Raw: []byte(`{"invalidCluster": "invalid-cluster","url": "https://kubernetes.default.svc"}`),
}},
},
},
},
},
}).Then().Expect(ApplicationsExist([]argov1alpha1.Application{expectedApp})).
// verify the ApplicationSet status conditions were set correctly
Expect(ApplicationSetHasConditions("simple-list-generator", expectedConditionsParamsError)).
// Delete the ApplicationSet, and verify it deletes the Applications
When().
Delete().Then().Expect(ApplicationsDoNotExist([]argov1alpha1.Application{expectedApp}))
}
func TestRenderHelmValuesObject(t *testing.T) {
expectedApp := argov1alpha1.Application{

View File

@@ -91,7 +91,7 @@ func TestClusterAddPermissionDenied(t *testing.T) {
Create().
Then().
AndCLIOutput(func(output string, err error) {
assert.True(t, strings.Contains(err.Error(), "PermissionDenied desc = permission denied: clusters, create"))
assert.Contains(t, err.Error(), "PermissionDenied desc = permission denied")
})
}
@@ -256,7 +256,7 @@ func TestClusterDeleteDenied(t *testing.T) {
DeleteByName().
Then().
AndCLIOutput(func(output string, err error) {
assert.True(t, strings.Contains(err.Error(), "PermissionDenied desc = permission denied: clusters, delete"))
assert.Contains(t, err.Error(), "PermissionDenied desc = permission denied")
})
// Attempt to remove cluster creds by server
@@ -270,7 +270,7 @@ func TestClusterDeleteDenied(t *testing.T) {
DeleteByServer().
Then().
AndCLIOutput(func(output string, err error) {
assert.True(t, strings.Contains(err.Error(), "PermissionDenied desc = permission denied: clusters, delete"))
assert.Contains(t, err.Error(), "PermissionDenied desc = permission denied")
})
}

View File

@@ -2841,11 +2841,11 @@ braces@^2.3.1:
to-regex "^3.0.1"
braces@^3.0.1, braces@^3.0.2, braces@~3.0.2:
version "3.0.2"
resolved "https://registry.yarnpkg.com/braces/-/braces-3.0.2.tgz#3454e1a462ee8d599e236df336cd9ea4f8afe107"
integrity sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==
version "3.0.3"
resolved "https://registry.yarnpkg.com/braces/-/braces-3.0.3.tgz#490332f40919452272d55a8480adc0c441358789"
integrity sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==
dependencies:
fill-range "^7.0.1"
fill-range "^7.1.1"
browser-process-hrtime@^1.0.0:
version "1.0.0"
@@ -4474,10 +4474,10 @@ fill-range@^4.0.0:
repeat-string "^1.6.1"
to-regex-range "^2.1.0"
fill-range@^7.0.1:
version "7.0.1"
resolved "https://registry.yarnpkg.com/fill-range/-/fill-range-7.0.1.tgz#1919a6a7c75fe38b2c7c77e5198535da9acdda40"
integrity sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==
fill-range@^7.1.1:
version "7.1.1"
resolved "https://registry.yarnpkg.com/fill-range/-/fill-range-7.1.1.tgz#44265d3cac07e3ea7dc247516380643754a05292"
integrity sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==
dependencies:
to-regex-range "^5.0.1"

View File

@@ -112,12 +112,12 @@ func GetAppRefreshPaths(app *v1alpha1.Application) []string {
}
// AppFilesHaveChanged returns true if any of the changed files are under the given refresh paths
// If refreshPaths is empty, it will always return true
// If refreshPaths or changedFiles are empty, it will always return true
func AppFilesHaveChanged(refreshPaths []string, changedFiles []string) bool {
// empty slice means there was no changes to any files
// so we should not refresh
// an empty slice of changed files means that the payload didn't include a list
// of changed files and we have to assume that a refresh is required
if len(changedFiles) == 0 {
return false
return true
}
if len(refreshPaths) == 0 {

View File

@@ -133,7 +133,7 @@ func Test_AppFilesHaveChanged(t *testing.T) {
changeExpected bool
}{
{"default no path", &v1alpha1.Application{}, []string{"README.md"}, true},
{"no files changed", getApp(".", "source/path"), []string{}, false},
{"no files changed", getApp(".", "source/path"), []string{}, true},
{"relative path - matching", getApp(".", "source/path"), []string{"source/path/my-deployment.yaml"}, true},
{"relative path, multi source - matching #1", getMultiSourceApp(".", "source/path", "other/path"), []string{"source/path/my-deployment.yaml"}, true},
{"relative path, multi source - matching #2", getMultiSourceApp(".", "other/path", "source/path"), []string{"source/path/my-deployment.yaml"}, true},

View File

@@ -2,7 +2,6 @@ package argo
import (
"context"
"encoding/json"
"fmt"
"time"
@@ -46,7 +45,7 @@ const (
EventReasonOperationCompleted = "OperationCompleted"
)
func (l *AuditLogger) logEvent(objMeta ObjectRef, gvk schema.GroupVersionKind, info EventInfo, message string, logFields map[string]interface{}) {
func (l *AuditLogger) logEvent(objMeta ObjectRef, gvk schema.GroupVersionKind, info EventInfo, message string, logFields map[string]string) {
logCtx := log.WithFields(log.Fields{
"type": info.Type,
"reason": info.Reason,
@@ -54,19 +53,6 @@ func (l *AuditLogger) logEvent(objMeta ObjectRef, gvk schema.GroupVersionKind, i
for field, val := range logFields {
logCtx = logCtx.WithField(field, val)
}
logFieldStrings := make(map[string]string)
for field, val := range logFields {
if valStr, ok := val.(string); ok {
logFieldStrings[field] = valStr
continue
}
vJsonStr, err := json.Marshal(val)
if err != nil {
logCtx.Errorf("Unable to marshal audit event field %v: %v", field, err)
continue
}
logFieldStrings[field] = string(vJsonStr)
}
switch gvk.Kind {
case application.ApplicationKind:
@@ -80,7 +66,7 @@ func (l *AuditLogger) logEvent(objMeta ObjectRef, gvk schema.GroupVersionKind, i
event := v1.Event{
ObjectMeta: metav1.ObjectMeta{
Name: fmt.Sprintf("%v.%x", objMeta.Name, t.UnixNano()),
Annotations: logFieldStrings,
Annotations: logFields,
},
Source: v1.EventSource{
Component: l.component,
@@ -115,14 +101,13 @@ func (l *AuditLogger) LogAppEvent(app *v1alpha1.Application, info EventInfo, mes
ResourceVersion: app.ObjectMeta.ResourceVersion,
UID: app.ObjectMeta.UID,
}
fields := map[string]interface{}{
fields := map[string]string{
"dest-server": app.Spec.Destination.Server,
"dest-namespace": app.Spec.Destination.Namespace,
}
if user != "" {
fields["user"] = user
}
fields["spec"] = app.Spec
l.logEvent(objectMeta, v1alpha1.ApplicationSchemaGroupVersionKind, info, message, fields)
}
@@ -133,7 +118,7 @@ func (l *AuditLogger) LogAppSetEvent(app *v1alpha1.ApplicationSet, info EventInf
ResourceVersion: app.ObjectMeta.ResourceVersion,
UID: app.ObjectMeta.UID,
}
fields := make(map[string]interface{})
fields := map[string]string{}
if user != "" {
fields["user"] = user
}
@@ -147,7 +132,7 @@ func (l *AuditLogger) LogResourceEvent(res *v1alpha1.ResourceNode, info EventInf
ResourceVersion: res.ResourceRef.Version,
UID: types.UID(res.ResourceRef.UID),
}
fields := make(map[string]interface{})
fields := map[string]string{}
if user != "" {
fields["user"] = user
}

View File

@@ -1,7 +1,9 @@
package argo
import (
"errors"
"fmt"
"regexp"
"strings"
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
@@ -21,6 +23,7 @@ const (
var WrongResourceTrackingFormat = fmt.Errorf("wrong resource tracking format, should be <application-name>:<group>/<kind>:<namespace>/<name>")
var LabelMaxLength = 63
var OkEndPattern = regexp.MustCompile("[a-zA-Z0-9]$")
// ResourceTracking defines methods which allow setup and retrieve tracking information to resource
type ResourceTracking interface {
@@ -155,6 +158,14 @@ func (rt *resourceTracking) SetAppInstance(un *unstructured.Unstructured, key, v
}
if len(val) > LabelMaxLength {
val = val[:LabelMaxLength]
// Prevent errors if the truncated name ends in a special character.
// See https://github.com/argoproj/argo-cd/issues/18237.
for !OkEndPattern.MatchString(val) {
if len(val) <= 1 {
return errors.New("failed to set app instance label: unable to truncate label to not end with a special character")
}
val = val[:len(val)-1]
}
}
err = argokube.SetAppInstanceLabel(un, key, val)
if err != nil {

View File

@@ -62,6 +62,60 @@ func TestSetAppInstanceAnnotationAndLabel(t *testing.T) {
assert.Equal(t, "my-app", app)
}
func TestSetAppInstanceAnnotationAndLabelLongName(t *testing.T) {
yamlBytes, err := os.ReadFile("testdata/svc.yaml")
assert.Nil(t, err)
var obj unstructured.Unstructured
err = yaml.Unmarshal(yamlBytes, &obj)
assert.Nil(t, err)
resourceTracking := NewResourceTracking()
err = resourceTracking.SetAppInstance(&obj, common.LabelKeyAppInstance, "my-app-with-an-extremely-long-name-that-is-over-sixty-three-characters", "", TrackingMethodAnnotationAndLabel)
assert.Nil(t, err)
// the annotation should still work, so the name from GetAppName should not be truncated
app := resourceTracking.GetAppName(&obj, common.LabelKeyAppInstance, TrackingMethodAnnotationAndLabel)
assert.Equal(t, "my-app-with-an-extremely-long-name-that-is-over-sixty-three-characters", app)
// the label should be truncated to 63 characters
assert.Equal(t, obj.GetLabels()[common.LabelKeyAppInstance], "my-app-with-an-extremely-long-name-that-is-over-sixty-three-cha")
}
func TestSetAppInstanceAnnotationAndLabelLongNameBadEnding(t *testing.T) {
yamlBytes, err := os.ReadFile("testdata/svc.yaml")
assert.Nil(t, err)
var obj unstructured.Unstructured
err = yaml.Unmarshal(yamlBytes, &obj)
assert.Nil(t, err)
resourceTracking := NewResourceTracking()
err = resourceTracking.SetAppInstance(&obj, common.LabelKeyAppInstance, "the-very-suspicious-name-with-precisely-sixty-three-characters-with-hyphen", "", TrackingMethodAnnotationAndLabel)
assert.Nil(t, err)
// the annotation should still work, so the name from GetAppName should not be truncated
app := resourceTracking.GetAppName(&obj, common.LabelKeyAppInstance, TrackingMethodAnnotationAndLabel)
assert.Equal(t, "the-very-suspicious-name-with-precisely-sixty-three-characters-with-hyphen", app)
// the label should be truncated to 63 characters, AND the hyphen should be removed
assert.Equal(t, obj.GetLabels()[common.LabelKeyAppInstance], "the-very-suspicious-name-with-precisely-sixty-three-characters")
}
func TestSetAppInstanceAnnotationAndLabelOutOfBounds(t *testing.T) {
yamlBytes, err := os.ReadFile("testdata/svc.yaml")
assert.Nil(t, err)
var obj unstructured.Unstructured
err = yaml.Unmarshal(yamlBytes, &obj)
assert.Nil(t, err)
resourceTracking := NewResourceTracking()
err = resourceTracking.SetAppInstance(&obj, common.LabelKeyAppInstance, "----------------------------------------------------------------", "", TrackingMethodAnnotationAndLabel)
// this should error because it can't truncate to a valid value
assert.EqualError(t, err, "failed to set app instance label: unable to truncate label to not end with a special character")
}
func TestSetAppInstanceAnnotationNotFound(t *testing.T) {
yamlBytes, err := os.ReadFile("testdata/svc.yaml")
assert.Nil(t, err)