Compare commits

..

6 Commits

Author SHA1 Message Date
github-actions[bot]
bc2b6f414f Bump version to 2.9.22 (#19646)
Signed-off-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
Co-authored-by: jessesuen <12677113+jessesuen@users.noreply.github.com>
2024-08-22 10:23:46 -07:00
Alexander Matyushentsev
90c83dc0c3 fix: ArgoCD 2.11 - Loop of PATCH calls to Application objects (#19340) (#19569)
Signed-off-by: Alexander Matyushentsev <AMatyushentsev@gmail.com>
2024-08-21 08:23:41 -06:00
Jae Ryong Song
dd3545b79b fix: docs version regex changed (#18756) (#19356)
Signed-off-by: jasong <jasong@student.42seoul.kr>
2024-08-04 13:14:23 -07:00
Mangaal
73f9171107 upgrade github.com/hashicorp/go-retryablehttp to v0.7.7 (#19239)
Signed-off-by: Mangaal <angommeeteimangaal@gmail.com>
2024-07-25 20:57:05 -04:00
github-actions[bot]
1804306f94 Bump version to 2.9.21 (#19176)
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-07-24 12:34:19 +03:00
pasha-codefresh
e96f32d233 Merge commit from fork
* feat: verify rbac on each message and not just during handshake

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

* feat: verify rbac on each message and not just during handshake

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

* fix: linter and e2e tests

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

* fix: linter and e2e tests

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

---------

Signed-off-by: pashakostohrys <pavel@codefresh.io>
2024-07-24 12:18:16 +03:00
21 changed files with 371 additions and 150 deletions

View File

@@ -1 +1 @@
2.9.20
2.9.22

View File

@@ -1643,6 +1643,22 @@ func (ctrl *ApplicationController) normalizeApplication(orig, app *appv1.Applica
}
}
func createMergePatch(orig, new interface{}) ([]byte, bool, error) {
origBytes, err := json.Marshal(orig)
if err != nil {
return nil, false, err
}
newBytes, err := json.Marshal(new)
if err != nil {
return nil, false, err
}
patch, err := jsonpatch.CreateMergePatch(origBytes, newBytes)
if err != nil {
return nil, false, err
}
return patch, string(patch) != "{}", nil
}
// persistAppStatus persists updates to application status. If no changes were made, it is a no-op
func (ctrl *ApplicationController) persistAppStatus(orig *appv1.Application, newStatus *appv1.ApplicationStatus) (patchMs time.Duration) {
logCtx := log.WithFields(log.Fields{"application": orig.QualifiedName()})
@@ -1662,9 +1678,9 @@ func (ctrl *ApplicationController) persistAppStatus(orig *appv1.Application, new
}
delete(newAnnotations, appv1.AnnotationKeyRefresh)
}
patch, modified, err := diff.CreateTwoWayMergePatch(
patch, modified, err := createMergePatch(
&appv1.Application{ObjectMeta: metav1.ObjectMeta{Annotations: orig.GetAnnotations()}, Status: orig.Status},
&appv1.Application{ObjectMeta: metav1.ObjectMeta{Annotations: newAnnotations}, Status: *newStatus}, appv1.Application{})
&appv1.Application{ObjectMeta: metav1.ObjectMeta{Annotations: newAnnotations}, Status: *newStatus})
if err != nil {
logCtx.Errorf("Error constructing app status patch: %v", err)
return

View File

@@ -21,6 +21,7 @@ import (
"github.com/argoproj/gitops-engine/pkg/utils/kube/kubetest"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/mock"
"github.com/stretchr/testify/require"
corev1 "k8s.io/api/core/v1"
apierr "k8s.io/apimachinery/pkg/api/errors"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
@@ -801,7 +802,7 @@ func TestNormalizeApplication(t *testing.T) {
normalized := false
fakeAppCs.AddReactor("patch", "*", func(action kubetesting.Action) (handled bool, ret runtime.Object, err error) {
if patchAction, ok := action.(kubetesting.PatchAction); ok {
if string(patchAction.GetPatch()) == `{"spec":{"project":"default"},"status":{"sync":{"comparedTo":{"destination":{},"source":{"repoURL":""}}}}}` {
if string(patchAction.GetPatch()) == `{"spec":{"project":"default"}}` {
normalized = true
}
}
@@ -1728,3 +1729,65 @@ func TestAddControllerNamespace(t *testing.T) {
assert.Equal(t, test.FakeArgoCDNamespace, updatedApp.Status.ControllerNamespace)
})
}
func TestHelmValuesObjectHasReplaceStrategy(t *testing.T) {
app := v1alpha1.Application{
Status: v1alpha1.ApplicationStatus{Sync: v1alpha1.SyncStatus{ComparedTo: v1alpha1.ComparedTo{
Source: v1alpha1.ApplicationSource{
Helm: &v1alpha1.ApplicationSourceHelm{
ValuesObject: &runtime.RawExtension{
Object: &unstructured.Unstructured{Object: map[string]interface{}{"key": []string{"value"}}},
},
},
},
}}},
}
appModified := v1alpha1.Application{
Status: v1alpha1.ApplicationStatus{Sync: v1alpha1.SyncStatus{ComparedTo: v1alpha1.ComparedTo{
Source: v1alpha1.ApplicationSource{
Helm: &v1alpha1.ApplicationSourceHelm{
ValuesObject: &runtime.RawExtension{
Object: &unstructured.Unstructured{Object: map[string]interface{}{"key": []string{"value-modified1"}}},
},
},
},
}}},
}
patch, _, err := createMergePatch(
app,
appModified)
require.NoError(t, err)
assert.Equal(t, `{"status":{"sync":{"comparedTo":{"source":{"helm":{"valuesObject":{"key":["value-modified1"]}}}}}}}`, string(patch))
}
func TestAppStatusIsReplaced(t *testing.T) {
original := &v1alpha1.ApplicationStatus{Sync: v1alpha1.SyncStatus{
ComparedTo: v1alpha1.ComparedTo{
Destination: v1alpha1.ApplicationDestination{
Server: "https://mycluster",
},
},
}}
updated := &v1alpha1.ApplicationStatus{Sync: v1alpha1.SyncStatus{
ComparedTo: v1alpha1.ComparedTo{
Destination: v1alpha1.ApplicationDestination{
Name: "mycluster",
},
},
}}
patchData, ok, err := createMergePatch(original, updated)
require.NoError(t, err)
require.True(t, ok)
patchObj := map[string]interface{}{}
require.NoError(t, json.Unmarshal(patchData, &patchObj))
val, has, err := unstructured.NestedFieldNoCopy(patchObj, "sync", "comparedTo", "destination", "server")
require.NoError(t, err)
require.True(t, has)
require.Nil(t, val)
}

View File

@@ -1,48 +1,83 @@
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 getCurrentVersion() {
const currentVersion = window.location.href.match(/\/en\/(release-(?:v\d+|[\d\.]+|\w+)|latest|stable)\//);
if (currentVersion && currentVersion.length > 1) {
return currentVersion[1];
}
return null;
}
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/?'+
'callback=' + callbackName + '&project=argo-cd&page=&theme=mkdocs&format=jsonp&docroot=docs&source_suffix=.md&version=' + (window['READTHEDOCS_DATA'] || { version: 'latest' }).version;
const currentVersion = getCurrentVersion();
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=' + (currentVersion || 'latest');
document.getElementsByTagName('head')[0].appendChild(script);
}, 0);
}
// VERSION WARNINGS
window.addEventListener("DOMContentLoaded", function() {
var rtdData = window['READTHEDOCS_DATA'] || { version: 'latest' };
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; }}"
}
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; }}"
var headerHeight = document.getElementsByClassName("md-header")[0].offsetHeight;
const currentVersion = getCurrentVersion();
if (currentVersion) {
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; }}";
}
}
});

6
go.mod
View File

@@ -48,14 +48,14 @@ require (
github.com/grpc-ecosystem/go-grpc-middleware v1.4.0
github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0
github.com/grpc-ecosystem/grpc-gateway v1.16.0
github.com/hashicorp/go-retryablehttp v0.7.4
github.com/hashicorp/go-retryablehttp v0.7.7
github.com/imdario/mergo v0.3.16
github.com/improbable-eng/grpc-web v0.15.0
github.com/itchyny/gojq v0.12.13
github.com/jeremywohl/flatten v1.0.1
github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51
github.com/ktrysmt/go-bitbucket v0.9.67
github.com/mattn/go-isatty v0.0.19
github.com/mattn/go-isatty v0.0.20
github.com/mattn/go-zglob v0.0.4
github.com/microsoft/azure-devops-go-api/azuredevops v1.0.0-b5
github.com/olekukonko/tablewriter v0.0.5
@@ -260,7 +260,7 @@ require (
go.starlark.net v0.0.0-20220328144851-d1966c6b9fcd // indirect
golang.org/x/mod v0.12.0 // indirect
golang.org/x/net v0.19.0
golang.org/x/sys v0.17.0 // indirect
golang.org/x/sys v0.20.0 // indirect
golang.org/x/text v0.14.0 // indirect
golang.org/x/time v0.3.0 // indirect
golang.org/x/tools v0.13.0 // indirect

16
go.sum
View File

@@ -886,6 +886,7 @@ github.com/facebookgo/subset v0.0.0-20150612182917-8dac2c3c4870/go.mod h1:5tD+ne
github.com/fatih/camelcase v1.0.0 h1:hxNvNX/xYBp0ovncs8WyWZrOrpBNub/JfaMvbURyft8=
github.com/fatih/camelcase v1.0.0/go.mod h1:yN2Sb0lFhZJUdVvtELVWefmrXpuZESvPmqwoZc+/fpc=
github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4=
github.com/fatih/color v1.16.0 h1:zmkK9Ngbjj+K0yRhTVONQh1p/HknKYSlNT+vZCzyokM=
github.com/felixge/httpsnoop v1.0.1/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U=
github.com/felixge/httpsnoop v1.0.3 h1:s/nj+GCswXYzN5v2DpNMuMQYe+0DDwt5WVCU6CWBdXk=
github.com/felixge/httpsnoop v1.0.3/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U=
@@ -1232,14 +1233,13 @@ github.com/hashicorp/go-cleanhttp v0.5.0/go.mod h1:JpRdi6/HCYpAwUzNwuwqhbovhLtng
github.com/hashicorp/go-cleanhttp v0.5.1/go.mod h1:JpRdi6/HCYpAwUzNwuwqhbovhLtngrth3wmdIIUrZ80=
github.com/hashicorp/go-cleanhttp v0.5.2 h1:035FKYIWjmULyFRBKPs8TBQoi0x6d9G4xc9neXJWAZQ=
github.com/hashicorp/go-cleanhttp v0.5.2/go.mod h1:kO/YDlP8L1346E6Sodw+PrpBSV4/SoxCXGY6BqNFT48=
github.com/hashicorp/go-hclog v0.9.2 h1:CG6TE5H9/JXsFWJCfoIVpKFIkFe6ysEuHirp4DxCsHI=
github.com/hashicorp/go-hclog v0.9.2/go.mod h1:5CU+agLiy3J7N7QjHK5d05KxGsuXiQLrjA0H7acj2lQ=
github.com/hashicorp/go-hclog v1.6.3 h1:Qr2kF+eVWjTiYmU7Y31tYlP1h0q/X3Nl3tPGdaB11/k=
github.com/hashicorp/go-immutable-radix v1.0.0/go.mod h1:0y9vanUI8NX6FsYoO3zeMjhV/C5i9g4Q3DwcSNZ4P60=
github.com/hashicorp/go-msgpack v0.5.3/go.mod h1:ahLV/dePpqEmjfWmKiqvPkv/twdG7iPBM1vqhUKIvfM=
github.com/hashicorp/go-multierror v1.0.0/go.mod h1:dHtQlpGsu+cZNNAkkCN/P3hoUDHhCYQXV3UM06sGGrk=
github.com/hashicorp/go-retryablehttp v0.5.1/go.mod h1:9B5zBasrRhHXnJnui7y6sL7es7NDiJgTc6Er0maI1Xs=
github.com/hashicorp/go-retryablehttp v0.7.4 h1:ZQgVdpTdAL7WpMIwLzCfbalOcSUdkDZnpUv3/+BxzFA=
github.com/hashicorp/go-retryablehttp v0.7.4/go.mod h1:Jy/gPYAdjqffZ/yFGCFV2doI5wjtH1ewM9u8iYVjtX8=
github.com/hashicorp/go-retryablehttp v0.7.7 h1:C8hUCYzor8PIfXHa4UrZkU4VvK8o9ISHxT2Q8+VepXU=
github.com/hashicorp/go-retryablehttp v0.7.7/go.mod h1:pkQpWZeYWskR+D1tR2O5OcBFOxfA7DoAO6xtkuQnHTk=
github.com/hashicorp/go-rootcerts v1.0.0/go.mod h1:K6zTfqpRlCUIjkwsN4Z+hiSfzSTQa6eBIzfwKfwNnHU=
github.com/hashicorp/go-sockaddr v1.0.0/go.mod h1:7Xibr9yA9JjQq1JpNB2Vw7kxv8xerXegt+ozgdvDeDU=
github.com/hashicorp/go-syslog v1.0.0/go.mod h1:qPfqrKkXGihmCqbJM2mZgkZGvKG1dFdvsLplgctolz4=
@@ -1378,14 +1378,15 @@ github.com/markbates/oncer v0.0.0-20181203154359-bf2de49a0be2/go.mod h1:Ld9puTsI
github.com/markbates/safe v1.0.1/go.mod h1:nAqgmRi7cY2nqMc92/bSEeQA+R4OheNU2T1kNSCBdG0=
github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU=
github.com/mattn/go-colorable v0.1.2/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE=
github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA=
github.com/mattn/go-isatty v0.0.3/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4=
github.com/mattn/go-isatty v0.0.4/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4=
github.com/mattn/go-isatty v0.0.8/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s=
github.com/mattn/go-isatty v0.0.9/go.mod h1:YNRxwqDuOph6SZLI9vUUz6OYw3QyUt7WiY2yME+cCiQ=
github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU=
github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM=
github.com/mattn/go-isatty v0.0.19 h1:JITubQf0MOLdlGRuRq+jtsDlekdYPia9ZFsB8h/APPA=
github.com/mattn/go-isatty v0.0.19/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY=
github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
github.com/mattn/go-runewidth v0.0.2/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU=
github.com/mattn/go-runewidth v0.0.4/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU=
github.com/mattn/go-runewidth v0.0.9/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI=
@@ -2147,8 +2148,9 @@ golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.9.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.11.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.12.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.17.0 h1:25cE3gD+tdBA7lp7QfhuV+rJiE9YXTcS3VG1SqssI/Y=
golang.org/x/sys v0.17.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/sys v0.20.0 h1:Od9JTbYCk261bKm4M/mw7AklTlFYIa0bIp9BgSm1S8Y=
golang.org/x/sys v0.20.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
golang.org/x/term v0.1.0/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=

View File

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

View File

@@ -20784,7 +20784,7 @@ spec:
key: applicationsetcontroller.allowed.scm.providers
name: argocd-cmd-params-cm
optional: true
image: quay.io/argoproj/argocd:v2.9.20
image: quay.io/argoproj/argocd:v2.9.22
imagePullPolicy: Always
name: argocd-applicationset-controller
ports:
@@ -20902,7 +20902,7 @@ spec:
- argocd
- admin
- redis-initial-password
image: quay.io/argoproj/argocd:v2.9.20
image: quay.io/argoproj/argocd:v2.9.22
imagePullPolicy: IfNotPresent
name: secret-init
securityContext:
@@ -21113,7 +21113,7 @@ spec:
value: /helm-working-dir
- name: HELM_DATA_HOME
value: /helm-working-dir
image: quay.io/argoproj/argocd:v2.9.20
image: quay.io/argoproj/argocd:v2.9.22
imagePullPolicy: Always
livenessProbe:
failureThreshold: 3
@@ -21165,7 +21165,7 @@ spec:
- -n
- /usr/local/bin/argocd
- /var/run/argocd/argocd-cmp-server
image: quay.io/argoproj/argocd:v2.9.20
image: quay.io/argoproj/argocd:v2.9.22
name: copyutil
securityContext:
allowPrivilegeEscalation: false
@@ -21395,7 +21395,7 @@ spec:
key: controller.ignore.normalizer.jq.timeout
name: argocd-cmd-params-cm
optional: true
image: quay.io/argoproj/argocd:v2.9.20
image: quay.io/argoproj/argocd:v2.9.22
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.9.20
newTag: v2.9.22

View File

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

View File

@@ -22019,7 +22019,7 @@ spec:
key: applicationsetcontroller.allowed.scm.providers
name: argocd-cmd-params-cm
optional: true
image: quay.io/argoproj/argocd:v2.9.20
image: quay.io/argoproj/argocd:v2.9.22
imagePullPolicy: Always
name: argocd-applicationset-controller
ports:
@@ -22142,7 +22142,7 @@ spec:
- -n
- /usr/local/bin/argocd
- /shared/argocd-dex
image: quay.io/argoproj/argocd:v2.9.20
image: quay.io/argoproj/argocd:v2.9.22
imagePullPolicy: Always
name: copyutil
securityContext:
@@ -22218,7 +22218,7 @@ spec:
key: application.namespaces
name: argocd-cmd-params-cm
optional: true
image: quay.io/argoproj/argocd:v2.9.20
image: quay.io/argoproj/argocd:v2.9.22
imagePullPolicy: Always
livenessProbe:
tcpSocket:
@@ -22337,7 +22337,7 @@ spec:
- argocd
- admin
- redis-initial-password
image: quay.io/argoproj/argocd:v2.9.20
image: quay.io/argoproj/argocd:v2.9.22
imagePullPolicy: IfNotPresent
name: secret-init
securityContext:
@@ -22576,7 +22576,7 @@ spec:
value: /helm-working-dir
- name: HELM_DATA_HOME
value: /helm-working-dir
image: quay.io/argoproj/argocd:v2.9.20
image: quay.io/argoproj/argocd:v2.9.22
imagePullPolicy: Always
livenessProbe:
failureThreshold: 3
@@ -22628,7 +22628,7 @@ spec:
- -n
- /usr/local/bin/argocd
- /var/run/argocd/argocd-cmp-server
image: quay.io/argoproj/argocd:v2.9.20
image: quay.io/argoproj/argocd:v2.9.22
name: copyutil
securityContext:
allowPrivilegeEscalation: false
@@ -22928,7 +22928,7 @@ spec:
key: server.api.content.types
name: argocd-cmd-params-cm
optional: true
image: quay.io/argoproj/argocd:v2.9.20
image: quay.io/argoproj/argocd:v2.9.22
imagePullPolicy: Always
livenessProbe:
httpGet:
@@ -23185,7 +23185,7 @@ spec:
key: controller.ignore.normalizer.jq.timeout
name: argocd-cmd-params-cm
optional: true
image: quay.io/argoproj/argocd:v2.9.20
image: quay.io/argoproj/argocd:v2.9.22
imagePullPolicy: Always
name: argocd-application-controller
ports:

View File

@@ -1680,7 +1680,7 @@ spec:
key: applicationsetcontroller.allowed.scm.providers
name: argocd-cmd-params-cm
optional: true
image: quay.io/argoproj/argocd:v2.9.20
image: quay.io/argoproj/argocd:v2.9.22
imagePullPolicy: Always
name: argocd-applicationset-controller
ports:
@@ -1803,7 +1803,7 @@ spec:
- -n
- /usr/local/bin/argocd
- /shared/argocd-dex
image: quay.io/argoproj/argocd:v2.9.20
image: quay.io/argoproj/argocd:v2.9.22
imagePullPolicy: Always
name: copyutil
securityContext:
@@ -1879,7 +1879,7 @@ spec:
key: application.namespaces
name: argocd-cmd-params-cm
optional: true
image: quay.io/argoproj/argocd:v2.9.20
image: quay.io/argoproj/argocd:v2.9.22
imagePullPolicy: Always
livenessProbe:
tcpSocket:
@@ -1998,7 +1998,7 @@ spec:
- argocd
- admin
- redis-initial-password
image: quay.io/argoproj/argocd:v2.9.20
image: quay.io/argoproj/argocd:v2.9.22
imagePullPolicy: IfNotPresent
name: secret-init
securityContext:
@@ -2237,7 +2237,7 @@ spec:
value: /helm-working-dir
- name: HELM_DATA_HOME
value: /helm-working-dir
image: quay.io/argoproj/argocd:v2.9.20
image: quay.io/argoproj/argocd:v2.9.22
imagePullPolicy: Always
livenessProbe:
failureThreshold: 3
@@ -2289,7 +2289,7 @@ spec:
- -n
- /usr/local/bin/argocd
- /var/run/argocd/argocd-cmp-server
image: quay.io/argoproj/argocd:v2.9.20
image: quay.io/argoproj/argocd:v2.9.22
name: copyutil
securityContext:
allowPrivilegeEscalation: false
@@ -2589,7 +2589,7 @@ spec:
key: server.api.content.types
name: argocd-cmd-params-cm
optional: true
image: quay.io/argoproj/argocd:v2.9.20
image: quay.io/argoproj/argocd:v2.9.22
imagePullPolicy: Always
livenessProbe:
httpGet:
@@ -2846,7 +2846,7 @@ spec:
key: controller.ignore.normalizer.jq.timeout
name: argocd-cmd-params-cm
optional: true
image: quay.io/argoproj/argocd:v2.9.20
image: quay.io/argoproj/argocd:v2.9.22
imagePullPolicy: Always
name: argocd-application-controller
ports:

View File

@@ -21136,7 +21136,7 @@ spec:
key: applicationsetcontroller.allowed.scm.providers
name: argocd-cmd-params-cm
optional: true
image: quay.io/argoproj/argocd:v2.9.20
image: quay.io/argoproj/argocd:v2.9.22
imagePullPolicy: Always
name: argocd-applicationset-controller
ports:
@@ -21259,7 +21259,7 @@ spec:
- -n
- /usr/local/bin/argocd
- /shared/argocd-dex
image: quay.io/argoproj/argocd:v2.9.20
image: quay.io/argoproj/argocd:v2.9.22
imagePullPolicy: Always
name: copyutil
securityContext:
@@ -21335,7 +21335,7 @@ spec:
key: application.namespaces
name: argocd-cmd-params-cm
optional: true
image: quay.io/argoproj/argocd:v2.9.20
image: quay.io/argoproj/argocd:v2.9.22
imagePullPolicy: Always
livenessProbe:
tcpSocket:
@@ -21435,7 +21435,7 @@ spec:
- argocd
- admin
- redis-initial-password
image: quay.io/argoproj/argocd:v2.9.20
image: quay.io/argoproj/argocd:v2.9.22
imagePullPolicy: IfNotPresent
name: secret-init
securityContext:
@@ -21646,7 +21646,7 @@ spec:
value: /helm-working-dir
- name: HELM_DATA_HOME
value: /helm-working-dir
image: quay.io/argoproj/argocd:v2.9.20
image: quay.io/argoproj/argocd:v2.9.22
imagePullPolicy: Always
livenessProbe:
failureThreshold: 3
@@ -21698,7 +21698,7 @@ spec:
- -n
- /usr/local/bin/argocd
- /var/run/argocd/argocd-cmp-server
image: quay.io/argoproj/argocd:v2.9.20
image: quay.io/argoproj/argocd:v2.9.22
name: copyutil
securityContext:
allowPrivilegeEscalation: false
@@ -21996,7 +21996,7 @@ spec:
key: server.api.content.types
name: argocd-cmd-params-cm
optional: true
image: quay.io/argoproj/argocd:v2.9.20
image: quay.io/argoproj/argocd:v2.9.22
imagePullPolicy: Always
livenessProbe:
httpGet:
@@ -22253,7 +22253,7 @@ spec:
key: controller.ignore.normalizer.jq.timeout
name: argocd-cmd-params-cm
optional: true
image: quay.io/argoproj/argocd:v2.9.20
image: quay.io/argoproj/argocd:v2.9.22
imagePullPolicy: Always
name: argocd-application-controller
ports:

View File

@@ -797,7 +797,7 @@ spec:
key: applicationsetcontroller.allowed.scm.providers
name: argocd-cmd-params-cm
optional: true
image: quay.io/argoproj/argocd:v2.9.20
image: quay.io/argoproj/argocd:v2.9.22
imagePullPolicy: Always
name: argocd-applicationset-controller
ports:
@@ -920,7 +920,7 @@ spec:
- -n
- /usr/local/bin/argocd
- /shared/argocd-dex
image: quay.io/argoproj/argocd:v2.9.20
image: quay.io/argoproj/argocd:v2.9.22
imagePullPolicy: Always
name: copyutil
securityContext:
@@ -996,7 +996,7 @@ spec:
key: application.namespaces
name: argocd-cmd-params-cm
optional: true
image: quay.io/argoproj/argocd:v2.9.20
image: quay.io/argoproj/argocd:v2.9.22
imagePullPolicy: Always
livenessProbe:
tcpSocket:
@@ -1096,7 +1096,7 @@ spec:
- argocd
- admin
- redis-initial-password
image: quay.io/argoproj/argocd:v2.9.20
image: quay.io/argoproj/argocd:v2.9.22
imagePullPolicy: IfNotPresent
name: secret-init
securityContext:
@@ -1307,7 +1307,7 @@ spec:
value: /helm-working-dir
- name: HELM_DATA_HOME
value: /helm-working-dir
image: quay.io/argoproj/argocd:v2.9.20
image: quay.io/argoproj/argocd:v2.9.22
imagePullPolicy: Always
livenessProbe:
failureThreshold: 3
@@ -1359,7 +1359,7 @@ spec:
- -n
- /usr/local/bin/argocd
- /var/run/argocd/argocd-cmp-server
image: quay.io/argoproj/argocd:v2.9.20
image: quay.io/argoproj/argocd:v2.9.22
name: copyutil
securityContext:
allowPrivilegeEscalation: false
@@ -1657,7 +1657,7 @@ spec:
key: server.api.content.types
name: argocd-cmd-params-cm
optional: true
image: quay.io/argoproj/argocd:v2.9.20
image: quay.io/argoproj/argocd:v2.9.22
imagePullPolicy: Always
livenessProbe:
httpGet:
@@ -1914,7 +1914,7 @@ spec:
key: controller.ignore.normalizer.jq.timeout
name: argocd-cmd-params-cm
optional: true
image: quay.io/argoproj/argocd:v2.9.20
image: quay.io/argoproj/argocd:v2.9.22
imagePullPolicy: Always
name: argocd-application-controller
ports:

View File

@@ -2213,7 +2213,6 @@ message SyncStatus {
optional string status = 1;
// ComparedTo contains information about what has been compared
// +patchStrategy=replace
optional ComparedTo comparedTo = 2;
// Revision contains information about the revision the comparison has been performed to

View File

@@ -7663,11 +7663,6 @@ func schema_pkg_apis_application_v1alpha1_SyncStatus(ref common.ReferenceCallbac
},
},
"comparedTo": {
VendorExtensible: spec.VendorExtensible{
Extensions: spec.Extensions{
"x-kubernetes-patch-strategy": "replace",
},
},
SchemaProps: spec.SchemaProps{
Description: "ComparedTo contains information about what has been compared",
Default: map[string]interface{}{},

View File

@@ -1493,8 +1493,7 @@ type SyncStatus struct {
// Status is the sync state of the comparison
Status SyncStatusCode `json:"status" protobuf:"bytes,1,opt,name=status,casttype=SyncStatusCode"`
// ComparedTo contains information about what has been compared
// +patchStrategy=replace
ComparedTo ComparedTo `json:"comparedTo,omitempty" protobuf:"bytes,2,opt,name=comparedTo" patchStrategy:"replace"`
ComparedTo ComparedTo `json:"comparedTo,omitempty" protobuf:"bytes,2,opt,name=comparedTo"`
// Revision contains information about the revision the comparison has been performed to
Revision string `json:"revision,omitempty" protobuf:"bytes,3,opt,name=revision"`
// Revisions contains information about the revisions of multiple sources the comparison has been performed to

View File

@@ -11,13 +11,9 @@ import (
"testing"
"time"
"github.com/argoproj/gitops-engine/pkg/diff"
"github.com/stretchr/testify/require"
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/utils/pointer"
argocdcommon "github.com/argoproj/argo-cd/v2/common"
"github.com/stretchr/testify/require"
"k8s.io/utils/pointer"
"github.com/argoproj/gitops-engine/pkg/sync/common"
"github.com/stretchr/testify/assert"
@@ -3623,35 +3619,3 @@ func TestOptionalMapEquality(t *testing.T) {
})
}
}
func TestHelmValuesObjectHasReplaceStrategy(t *testing.T) {
app := Application{
Status: ApplicationStatus{Sync: SyncStatus{ComparedTo: ComparedTo{
Source: ApplicationSource{
Helm: &ApplicationSourceHelm{
ValuesObject: &runtime.RawExtension{
Object: &unstructured.Unstructured{Object: map[string]interface{}{"key": []string{"value"}}},
},
},
},
}}},
}
appModified := Application{
Status: ApplicationStatus{Sync: SyncStatus{ComparedTo: ComparedTo{
Source: ApplicationSource{
Helm: &ApplicationSourceHelm{
ValuesObject: &runtime.RawExtension{
Object: &unstructured.Unstructured{Object: map[string]interface{}{"key": []string{"value-modified1"}}},
},
},
},
}}},
}
patch, _, err := diff.CreateTwoWayMergePatch(
app,
appModified, Application{})
require.NoError(t, err)
assert.Equal(t, `{"status":{"sync":{"comparedTo":{"destination":{},"source":{"helm":{"valuesObject":{"key":["value-modified1"]}},"repoURL":""}}}}}`, string(patch))
}

View File

@@ -225,7 +225,7 @@ func (s *terminalHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
fieldLog.Info("terminal session starting")
session, err := newTerminalSession(w, r, nil, s.sessionManager)
session, err := newTerminalSession(ctx, w, r, nil, s.sessionManager, appRBACName, s.enf)
if err != nil {
http.Error(w, "Failed to start terminal session", http.StatusBadRequest)
return

View File

@@ -1,15 +1,20 @@
package application
import (
"context"
"encoding/json"
"fmt"
"github.com/argoproj/argo-cd/v2/common"
httputil "github.com/argoproj/argo-cd/v2/util/http"
util_session "github.com/argoproj/argo-cd/v2/util/session"
"net/http"
"sync"
"time"
"github.com/argoproj/argo-cd/v2/server/rbacpolicy"
"github.com/argoproj/argo-cd/v2/util/rbac"
"github.com/argoproj/argo-cd/v2/common"
httputil "github.com/argoproj/argo-cd/v2/util/http"
util_session "github.com/argoproj/argo-cd/v2/util/session"
"github.com/gorilla/websocket"
log "github.com/sirupsen/logrus"
"k8s.io/client-go/tools/remotecommand"
@@ -31,6 +36,7 @@ var upgrader = func() websocket.Upgrader {
// terminalSession implements PtyHandler
type terminalSession struct {
ctx context.Context
wsConn *websocket.Conn
sizeChan chan remotecommand.TerminalSize
doneChan chan struct{}
@@ -39,6 +45,8 @@ type terminalSession struct {
writeLock sync.Mutex
sessionManager *util_session.SessionManager
token *string
appRBACName string
enf *rbac.Enforcer
}
// getToken get auth token from web socket request
@@ -48,7 +56,7 @@ func getToken(r *http.Request) (string, error) {
}
// newTerminalSession create terminalSession
func newTerminalSession(w http.ResponseWriter, r *http.Request, responseHeader http.Header, sessionManager *util_session.SessionManager) (*terminalSession, error) {
func newTerminalSession(ctx context.Context, w http.ResponseWriter, r *http.Request, responseHeader http.Header, sessionManager *util_session.SessionManager, appRBACName string, enf *rbac.Enforcer) (*terminalSession, error) {
token, err := getToken(r)
if err != nil {
return nil, err
@@ -59,12 +67,15 @@ func newTerminalSession(w http.ResponseWriter, r *http.Request, responseHeader h
return nil, err
}
session := &terminalSession{
ctx: ctx,
wsConn: conn,
tty: true,
sizeChan: make(chan remotecommand.TerminalSize),
doneChan: make(chan struct{}),
sessionManager: sessionManager,
token: &token,
appRBACName: appRBACName,
enf: enf,
}
return session, nil
}
@@ -125,6 +136,29 @@ func (t *terminalSession) reconnect() (int, error) {
return 0, nil
}
func (t *terminalSession) validatePermissions(p []byte) (int, error) {
permissionDeniedMessage, _ := json.Marshal(TerminalMessage{
Operation: "stdout",
Data: "Permission denied",
})
if err := t.enf.EnforceErr(t.ctx.Value("claims"), rbacpolicy.ResourceApplications, rbacpolicy.ActionGet, t.appRBACName); err != nil {
err = t.wsConn.WriteMessage(websocket.TextMessage, permissionDeniedMessage)
if err != nil {
log.Errorf("permission denied message err: %v", err)
}
return copy(p, EndOfTransmission), permissionDeniedErr
}
if err := t.enf.EnforceErr(t.ctx.Value("claims"), rbacpolicy.ResourceExec, rbacpolicy.ActionCreate, t.appRBACName); err != nil {
err = t.wsConn.WriteMessage(websocket.TextMessage, permissionDeniedMessage)
if err != nil {
log.Errorf("permission denied message err: %v", err)
}
return copy(p, EndOfTransmission), permissionDeniedErr
}
return 0, nil
}
// Read called in a loop from remotecommand as long as the process is running
func (t *terminalSession) Read(p []byte) (int, error) {
// check if token still valid
@@ -135,6 +169,12 @@ func (t *terminalSession) Read(p []byte) (int, error) {
return t.reconnect()
}
// validate permissions
code, err := t.validatePermissions(p)
if err != nil {
return code, err
}
t.readLock.Lock()
_, message, err := t.wsConn.ReadMessage()
t.readLock.Unlock()

View File

@@ -1,28 +1,69 @@
package application
import (
"context"
"encoding/json"
"github.com/gorilla/websocket"
"github.com/stretchr/testify/assert"
"net/http"
"net/http/httptest"
"strings"
"testing"
v1 "k8s.io/api/core/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/client-go/kubernetes/fake"
"github.com/argoproj/argo-cd/v2/common"
"github.com/argoproj/argo-cd/v2/util/assets"
"github.com/argoproj/argo-cd/v2/util/rbac"
"github.com/golang-jwt/jwt/v4"
"github.com/gorilla/websocket"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)
func reconnect(w http.ResponseWriter, r *http.Request) {
var upgrader = websocket.Upgrader{}
func newTestTerminalSession(w http.ResponseWriter, r *http.Request) terminalSession {
upgrader := websocket.Upgrader{}
c, err := upgrader.Upgrade(w, r, nil)
if err != nil {
return
return terminalSession{}
}
ts := terminalSession{wsConn: c}
return terminalSession{wsConn: c}
}
func newEnforcer() *rbac.Enforcer {
additionalConfig := make(map[string]string, 0)
kubeclientset := fake.NewSimpleClientset(&v1.ConfigMap{
ObjectMeta: metav1.ObjectMeta{
Namespace: testNamespace,
Name: "argocd-cm",
Labels: map[string]string{
"app.kubernetes.io/part-of": "argocd",
},
},
Data: additionalConfig,
}, &v1.Secret{
ObjectMeta: metav1.ObjectMeta{
Name: "argocd-secret",
Namespace: testNamespace,
},
Data: map[string][]byte{
"admin.password": []byte("test"),
"server.secretkey": []byte("test"),
},
})
enforcer := rbac.NewEnforcer(kubeclientset, testNamespace, common.ArgoCDRBACConfigMapName, nil)
return enforcer
}
func reconnect(w http.ResponseWriter, r *http.Request) {
ts := newTestTerminalSession(w, r)
_, _ = ts.reconnect()
}
func TestReconnect(t *testing.T) {
s := httptest.NewServer(http.HandlerFunc(reconnect))
defer s.Close()
@@ -30,7 +71,7 @@ func TestReconnect(t *testing.T) {
// Connect to the server
ws, _, err := websocket.DefaultDialer.Dial(u, nil)
assert.NoError(t, err)
require.NoError(t, err)
defer ws.Close()
@@ -40,7 +81,74 @@ func TestReconnect(t *testing.T) {
err = json.Unmarshal(p, &message)
assert.NoError(t, err)
assert.Equal(t, message.Data, ReconnectMessage)
require.NoError(t, err)
assert.Equal(t, ReconnectMessage, message.Data)
}
func TestValidateWithAdminPermissions(t *testing.T) {
validate := func(w http.ResponseWriter, r *http.Request) {
enf := newEnforcer()
_ = enf.SetBuiltinPolicy(assets.BuiltinPolicyCSV)
enf.SetDefaultRole("role:admin")
enf.SetClaimsEnforcerFunc(func(claims jwt.Claims, rvals ...interface{}) bool {
return true
})
ts := newTestTerminalSession(w, r)
ts.enf = enf
ts.appRBACName = "test"
// nolint:staticcheck
ts.ctx = context.WithValue(context.Background(), "claims", &jwt.MapClaims{"groups": []string{"admin"}})
_, err := ts.validatePermissions([]byte{})
require.NoError(t, err)
}
s := httptest.NewServer(http.HandlerFunc(validate))
defer s.Close()
u := "ws" + strings.TrimPrefix(s.URL, "http")
// Connect to the server
ws, _, err := websocket.DefaultDialer.Dial(u, nil)
require.NoError(t, err)
defer ws.Close()
}
func TestValidateWithoutPermissions(t *testing.T) {
validate := func(w http.ResponseWriter, r *http.Request) {
enf := newEnforcer()
_ = enf.SetBuiltinPolicy(assets.BuiltinPolicyCSV)
enf.SetDefaultRole("role:test")
enf.SetClaimsEnforcerFunc(func(claims jwt.Claims, rvals ...interface{}) bool {
return false
})
ts := newTestTerminalSession(w, r)
ts.enf = enf
ts.appRBACName = "test"
// nolint:staticcheck
ts.ctx = context.WithValue(context.Background(), "claims", &jwt.MapClaims{"groups": []string{"test"}})
_, err := ts.validatePermissions([]byte{})
require.Error(t, err)
assert.Equal(t, permissionDeniedErr.Error(), err.Error())
}
s := httptest.NewServer(http.HandlerFunc(validate))
defer s.Close()
u := "ws" + strings.TrimPrefix(s.URL, "http")
// Connect to the server
ws, _, err := websocket.DefaultDialer.Dial(u, nil)
require.NoError(t, err)
defer ws.Close()
_, p, _ := ws.ReadMessage()
var message TerminalMessage
err = json.Unmarshal(p, &message)
require.NoError(t, err)
assert.Equal(t, "Permission denied", message.Data)
}