Compare commits

...

1 Commits

Author SHA1 Message Date
Victor Sollerhed
0e96b1b362 sibling of 35a9e0cc6d 2023-10-23 13:30:39 +00:00
296 changed files with 1422 additions and 33922 deletions

View File

@@ -18,10 +18,8 @@ hack/
docs/
examples/
.github/
!test/container
!test/e2e/testdata
!test/fixture
!test/remote
!test/container
!hack/installers
!hack/gpg-wrapper.sh
!hack/git-verify-wrapper.sh

View File

@@ -360,7 +360,6 @@ jobs:
name: Run end-to-end tests
runs-on: ubuntu-22.04
strategy:
fail-fast: false
matrix:
k3s-version: [v1.28.2, v1.27.6, v1.26.9, v1.25.14]
needs:

View File

@@ -2,8 +2,7 @@
** @argoproj/argocd-approvers
# Docs
/docs/** @argoproj/argocd-approvers @argoproj/argocd-approvers-docs
/USERS.md @argoproj/argocd-approvers @argoproj/argocd-approvers-docs
/docs/** @argoproj/argocd-approvers @argoproj/argocd-approvers-docs
# CI
/.github/** @argoproj/argocd-approvers @argoproj/argocd-approvers-ci

View File

@@ -1 +0,0 @@
Please refer to [the Contribution Guide](https://argo-cd.readthedocs.io/en/latest/developer-guide/code-contributions/)

View File

@@ -49,7 +49,7 @@ ARGOCD_E2E_DEX_PORT?=5556
ARGOCD_E2E_YARN_HOST?=localhost
ARGOCD_E2E_DISABLE_AUTH?=
ARGOCD_E2E_TEST_TIMEOUT?=60m
ARGOCD_E2E_TEST_TIMEOUT?=45m
ARGOCD_IN_CI?=false
ARGOCD_TEST_E2E?=true
@@ -386,9 +386,9 @@ test: test-tools-image
.PHONY: test-local
test-local:
if test "$(TEST_MODULE)" = ""; then \
DIST_DIR=${DIST_DIR} RERUN_FAILS=0 PACKAGES=`go list ./... | grep -v 'test/e2e'` ./hack/test.sh -coverprofile=coverage.out; \
./hack/test.sh -coverprofile=coverage.out `go list ./... | grep -v 'test/e2e'`; \
else \
DIST_DIR=${DIST_DIR} RERUN_FAILS=0 PACKAGES="$(TEST_MODULE)" ./hack/test.sh -coverprofile=coverage.out "$(TEST_MODULE)"; \
./hack/test.sh -coverprofile=coverage.out "$(TEST_MODULE)"; \
fi
.PHONY: test-race
@@ -400,9 +400,9 @@ test-race: test-tools-image
.PHONY: test-race-local
test-race-local:
if test "$(TEST_MODULE)" = ""; then \
DIST_DIR=${DIST_DIR} RERUN_FAILS=0 PACKAGES=`go list ./... | grep -v 'test/e2e'` ./hack/test.sh -race -coverprofile=coverage.out; \
./hack/test.sh -race -coverprofile=coverage.out `go list ./... | grep -v 'test/e2e'`; \
else \
DIST_DIR=${DIST_DIR} RERUN_FAILS=0 PACKAGES="$(TEST_MODULE)" ./hack/test.sh -race -coverprofile=coverage.out; \
./hack/test.sh -race -coverprofile=coverage.out "$(TEST_MODULE)"; \
fi
# Run the E2E test suite. E2E test servers (see start-e2e target) must be
@@ -416,7 +416,7 @@ test-e2e:
test-e2e-local: cli-local
# NO_PROXY ensures all tests don't go out through a proxy if one is configured on the test system
export GO111MODULE=off
DIST_DIR=${DIST_DIR} RERUN_FAILS=5 PACKAGES="./test/e2e" ARGOCD_E2E_RECORD=${ARGOCD_E2E_RECORD} ARGOCD_GPG_ENABLED=true NO_PROXY=* ./hack/test.sh -timeout $(ARGOCD_E2E_TEST_TIMEOUT) -v
ARGOCD_E2E_RECORD=${ARGOCD_E2E_RECORD} ARGOCD_GPG_ENABLED=true NO_PROXY=* ./hack/test.sh -timeout $(ARGOCD_E2E_TEST_TIMEOUT) -v ./test/e2e
# Spawns a shell in the test server container for debugging purposes
debug-test-server: test-tools-image
@@ -557,7 +557,6 @@ install-tools-local: install-test-tools-local install-codegen-tools-local instal
install-test-tools-local:
./hack/install.sh kustomize
./hack/install.sh helm-linux
./hack/install.sh gotestsum
# Installs all tools required for running codegen (Linux packages)
.PHONY: install-codegen-tools-local

View File

@@ -169,7 +169,6 @@ Currently, the following organizations are **officially** using Argo CD:
1. [Meican](https://meican.com/)
1. [Meilleurs Agents](https://www.meilleursagents.com/)
1. [Mercedes-Benz Tech Innovation](https://www.mercedes-benz-techinnovation.com/)
1. [Mercedes-Benz.io](https://www.mercedes-benz.io/)
1. [Metanet](http://www.metanet.co.kr/en/)
1. [MindSpore](https://mindspore.cn)
1. [Mirantis](https://mirantis.com/)
@@ -244,7 +243,6 @@ Currently, the following organizations are **officially** using Argo CD:
1. [Sap Labs](http://sap.com)
1. [Sauce Labs](https://saucelabs.com/)
1. [Schwarz IT](https://jobs.schwarz/it-mission)
1. [SCRM Lidl International Hub](https://scrm.lidl)
1. [SEEK](https://seek.com.au)
1. [SI Analytics](https://si-analytics.ai)
1. [Skit](https://skit.ai/)

View File

@@ -1 +1 @@
2.9.0
2.9.0-rc2

View File

@@ -544,24 +544,22 @@ func ignoreNotAllowedNamespaces(namespaces []string) predicate.Predicate {
}
}
func appControllerIndexer(rawObj client.Object) []string {
// grab the job object, extract the owner...
app := rawObj.(*argov1alpha1.Application)
owner := metav1.GetControllerOf(app)
if owner == nil {
return nil
}
// ...make sure it's a application set...
if owner.APIVersion != argov1alpha1.SchemeGroupVersion.String() || owner.Kind != "ApplicationSet" {
return nil
}
// ...and if so, return it
return []string{owner.Name}
}
func (r *ApplicationSetReconciler) SetupWithManager(mgr ctrl.Manager, enableProgressiveSyncs bool, maxConcurrentReconciliations int) error {
if err := mgr.GetFieldIndexer().IndexField(context.TODO(), &argov1alpha1.Application{}, ".metadata.controller", appControllerIndexer); err != nil {
if err := mgr.GetFieldIndexer().IndexField(context.TODO(), &argov1alpha1.Application{}, ".metadata.controller", func(rawObj client.Object) []string {
// grab the job object, extract the owner...
app := rawObj.(*argov1alpha1.Application)
owner := metav1.GetControllerOf(app)
if owner == nil {
return nil
}
// ...make sure it's a application set...
if owner.APIVersion != argov1alpha1.SchemeGroupVersion.String() || owner.Kind != "ApplicationSet" {
return nil
}
// ...and if so, return it
return []string{owner.Name}
}); err != nil {
return fmt.Errorf("error setting up with manager: %w", err)
}
@@ -699,16 +697,8 @@ func (r *ApplicationSetReconciler) createOrUpdateInCluster(ctx context.Context,
continue
}
r.updateCache(ctx, found, appLog)
if action != controllerutil.OperationResultNone {
// Don't pollute etcd with "unchanged Application" events
r.Recorder.Eventf(&applicationSet, corev1.EventTypeNormal, fmt.Sprint(action), "%s Application %q", action, generatedApp.Name)
appLog.Logf(log.InfoLevel, "%s Application", action)
} else {
// "unchanged Application" can be inferred by Reconcile Complete with no action being listed
// Or enable debug logging
appLog.Logf(log.DebugLevel, "%s Application", action)
}
r.Recorder.Eventf(&applicationSet, corev1.EventTypeNormal, fmt.Sprint(action), "%s Application %q", action, generatedApp.Name)
appLog.Logf(log.InfoLevel, "%s Application", action)
}
return firstError
}

View File

@@ -994,7 +994,7 @@ func TestCreateOrUpdateInCluster(t *testing.T) {
initObjs = append(initObjs, &a)
}
client := fake.NewClientBuilder().WithScheme(scheme).WithObjects(initObjs...).WithIndex(&v1alpha1.Application{}, ".metadata.controller", appControllerIndexer).Build()
client := fake.NewClientBuilder().WithScheme(scheme).WithObjects(initObjs...).Build()
r := ApplicationSetReconciler{
Client: client,
@@ -1088,7 +1088,7 @@ func TestRemoveFinalizerOnInvalidDestination_FinalizerTypes(t *testing.T) {
initObjs := []crtclient.Object{&app, &appSet}
client := fake.NewClientBuilder().WithScheme(scheme).WithObjects(initObjs...).WithIndex(&v1alpha1.Application{}, ".metadata.controller", appControllerIndexer).Build()
client := fake.NewClientBuilder().WithScheme(scheme).WithObjects(initObjs...).Build()
secret := &corev1.Secret{
ObjectMeta: metav1.ObjectMeta{
Name: "my-secret",
@@ -1250,7 +1250,7 @@ func TestRemoveFinalizerOnInvalidDestination_DestinationTypes(t *testing.T) {
initObjs := []crtclient.Object{&app, &appSet}
client := fake.NewClientBuilder().WithScheme(scheme).WithObjects(initObjs...).WithIndex(&v1alpha1.Application{}, ".metadata.controller", appControllerIndexer).Build()
client := fake.NewClientBuilder().WithScheme(scheme).WithObjects(initObjs...).Build()
secret := &corev1.Secret{
ObjectMeta: metav1.ObjectMeta{
Name: "my-secret",
@@ -1482,7 +1482,7 @@ func TestCreateApplications(t *testing.T) {
initObjs = append(initObjs, &a)
}
client := fake.NewClientBuilder().WithScheme(scheme).WithObjects(initObjs...).WithIndex(&v1alpha1.Application{}, ".metadata.controller", appControllerIndexer).Build()
client := fake.NewClientBuilder().WithScheme(scheme).WithObjects(initObjs...).Build()
r := ApplicationSetReconciler{
Client: client,
@@ -1626,7 +1626,7 @@ func TestDeleteInCluster(t *testing.T) {
initObjs = append(initObjs, &temp)
}
client := fake.NewClientBuilder().WithScheme(scheme).WithObjects(initObjs...).WithIndex(&v1alpha1.Application{}, ".metadata.controller", appControllerIndexer).Build()
client := fake.NewClientBuilder().WithScheme(scheme).WithObjects(initObjs...).Build()
r := ApplicationSetReconciler{
Client: client,
@@ -2000,15 +2000,7 @@ func TestReconcilerValidationProjectErrorBehaviour(t *testing.T) {
argoDBMock := dbmocks.ArgoDB{}
argoObjs := []runtime.Object{&project}
client := fake.NewClientBuilder().WithScheme(scheme).WithObjects(&appSet).WithIndex(&v1alpha1.Application{}, ".metadata.controller", appControllerIndexer).Build()
goodCluster := v1alpha1.Cluster{Server: "https://good-cluster", Name: "good-cluster"}
badCluster := v1alpha1.Cluster{Server: "https://bad-cluster", Name: "bad-cluster"}
argoDBMock.On("GetCluster", mock.Anything, "https://good-cluster").Return(&goodCluster, nil)
argoDBMock.On("GetCluster", mock.Anything, "https://bad-cluster").Return(&badCluster, nil)
argoDBMock.On("ListClusters", mock.Anything).Return(&v1alpha1.ClusterList{Items: []v1alpha1.Cluster{
goodCluster,
}}, nil)
client := fake.NewClientBuilder().WithScheme(scheme).WithObjects(&appSet).Build()
r := ApplicationSetReconciler{
Client: client,
Scheme: scheme,
@@ -2084,7 +2076,7 @@ func TestSetApplicationSetStatusCondition(t *testing.T) {
argoDBMock := dbmocks.ArgoDB{}
argoObjs := []runtime.Object{}
client := fake.NewClientBuilder().WithScheme(scheme).WithObjects(&appSet).WithIndex(&v1alpha1.Application{}, ".metadata.controller", appControllerIndexer).Build()
client := fake.NewClientBuilder().WithScheme(scheme).WithObjects(&appSet).Build()
r := ApplicationSetReconciler{
Client: client,
@@ -2154,7 +2146,7 @@ func applicationsUpdateSyncPolicyTest(t *testing.T, applicationsSyncPolicy v1alp
argoDBMock := dbmocks.ArgoDB{}
argoObjs := []runtime.Object{&defaultProject}
client := fake.NewClientBuilder().WithScheme(scheme).WithObjects(&appSet).WithIndex(&v1alpha1.Application{}, ".metadata.controller", appControllerIndexer).Build()
client := fake.NewClientBuilder().WithScheme(scheme).WithObjects(&appSet).Build()
goodCluster := v1alpha1.Cluster{Server: "https://good-cluster", Name: "good-cluster"}
argoDBMock.On("GetCluster", mock.Anything, "https://good-cluster").Return(&goodCluster, nil)
argoDBMock.On("ListClusters", mock.Anything).Return(&v1alpha1.ClusterList{Items: []v1alpha1.Cluster{
@@ -2324,7 +2316,7 @@ func applicationsDeleteSyncPolicyTest(t *testing.T, applicationsSyncPolicy v1alp
argoDBMock := dbmocks.ArgoDB{}
argoObjs := []runtime.Object{&defaultProject}
client := fake.NewClientBuilder().WithScheme(scheme).WithObjects(&appSet).WithIndex(&v1alpha1.Application{}, ".metadata.controller", appControllerIndexer).Build()
client := fake.NewClientBuilder().WithScheme(scheme).WithObjects(&appSet).Build()
goodCluster := v1alpha1.Cluster{Server: "https://good-cluster", Name: "good-cluster"}
argoDBMock.On("GetCluster", mock.Anything, "https://good-cluster").Return(&goodCluster, nil)
argoDBMock.On("ListClusters", mock.Anything).Return(&v1alpha1.ClusterList{Items: []v1alpha1.Cluster{
@@ -2635,7 +2627,7 @@ func TestPolicies(t *testing.T) {
},
}
client := fake.NewClientBuilder().WithScheme(scheme).WithObjects(&appSet).WithIndex(&v1alpha1.Application{}, ".metadata.controller", appControllerIndexer).Build()
client := fake.NewClientBuilder().WithScheme(scheme).WithObjects(&appSet).Build()
r := ApplicationSetReconciler{
Client: client,

View File

@@ -60,9 +60,9 @@ func TestRequeueAfter(t *testing.T) {
"List": generators.NewListGenerator(),
"Clusters": generators.NewClusterGenerator(k8sClient, ctx, appClientset, "argocd"),
"Git": generators.NewGitGenerator(mockServer),
"SCMProvider": generators.NewSCMProviderGenerator(fake.NewClientBuilder().WithObjects(&corev1.Secret{}).Build(), generators.SCMAuthProviders{}, "", []string{""}, true),
"SCMProvider": generators.NewSCMProviderGenerator(fake.NewClientBuilder().WithObjects(&corev1.Secret{}).Build(), generators.SCMAuthProviders{}, "", []string{""}),
"ClusterDecisionResource": generators.NewDuckTypeGenerator(ctx, fakeDynClient, appClientset, "argocd"),
"PullRequest": generators.NewPullRequestGenerator(k8sClient, generators.SCMAuthProviders{}, "", []string{""}, true),
"PullRequest": generators.NewPullRequestGenerator(k8sClient, generators.SCMAuthProviders{}, "", []string{""}),
}
nestedGenerators := map[string]generators.Generator{

View File

@@ -148,9 +148,6 @@ func (g *GitGenerator) generateParamsFromGitFile(filePath string, fileContent []
return nil, fmt.Errorf("unable to parse file: %v", err)
}
objectsFound = append(objectsFound, singleObj)
} else if len(objectsFound) == 0 {
// If file is valid but empty, add a default empty item
objectsFound = append(objectsFound, map[string]interface{}{})
}
res := []map[string]interface{}{}

View File

@@ -4,173 +4,119 @@ import (
"fmt"
"testing"
"github.com/argoproj/argo-cd/v2/applicationset/services/mocks"
argoprojiov1alpha1 "github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/mock"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"github.com/argoproj/argo-cd/v2/applicationset/services/mocks"
argoprojiov1alpha1 "github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1"
)
func Test_generateParamsFromGitFile(t *testing.T) {
defaultContent := []byte(`
values := map[string]string{}
params, err := (*GitGenerator)(nil).generateParamsFromGitFile("path/dir/file_name.yaml", []byte(`
foo:
bar: baz
`)
type args struct {
filePath string
fileContent []byte
values map[string]string
useGoTemplate bool
goTemplateOptions []string
pathParamPrefix string
`), values, false, nil, "")
if err != nil {
t.Fatal(err)
}
tests := []struct {
name string
args args
want []map[string]interface{}
wantErr bool
}{
assert.Equal(t, []map[string]interface{}{
{
name: "empty file returns path parameters",
args: args{
filePath: "path/dir/file_name.yaml",
fileContent: []byte(""),
values: map[string]string{},
useGoTemplate: false,
},
want: []map[string]interface{}{
{
"path": "path/dir",
"path.basename": "dir",
"path.filename": "file_name.yaml",
"path.basenameNormalized": "dir",
"path.filenameNormalized": "file-name.yaml",
"path[0]": "path",
"path[1]": "dir",
},
},
},
{
name: "invalid json/yaml file returns error",
args: args{
filePath: "path/dir/file_name.yaml",
fileContent: []byte("this is not json or yaml"),
values: map[string]string{},
useGoTemplate: false,
},
wantErr: true,
},
{
name: "file parameters are added to params",
args: args{
filePath: "path/dir/file_name.yaml",
fileContent: defaultContent,
values: map[string]string{},
useGoTemplate: false,
},
want: []map[string]interface{}{
{
"foo.bar": "baz",
"path": "path/dir",
"path.basename": "dir",
"path.filename": "file_name.yaml",
"path.basenameNormalized": "dir",
"path.filenameNormalized": "file-name.yaml",
"path[0]": "path",
"path[1]": "dir",
},
},
},
{
name: "path parameter are prefixed",
args: args{
filePath: "path/dir/file_name.yaml",
fileContent: defaultContent,
values: map[string]string{},
useGoTemplate: false,
pathParamPrefix: "myRepo",
},
want: []map[string]interface{}{
{
"foo.bar": "baz",
"myRepo.path": "path/dir",
"myRepo.path.basename": "dir",
"myRepo.path.filename": "file_name.yaml",
"myRepo.path.basenameNormalized": "dir",
"myRepo.path.filenameNormalized": "file-name.yaml",
"myRepo.path[0]": "path",
"myRepo.path[1]": "dir",
},
},
},
{
name: "file parameters are added to params with go template",
args: args{
filePath: "path/dir/file_name.yaml",
fileContent: defaultContent,
values: map[string]string{},
useGoTemplate: true,
},
want: []map[string]interface{}{
{
"foo": map[string]interface{}{
"bar": "baz",
},
"path": map[string]interface{}{
"path": "path/dir",
"basename": "dir",
"filename": "file_name.yaml",
"basenameNormalized": "dir",
"filenameNormalized": "file-name.yaml",
"segments": []string{
"path",
"dir",
},
},
},
},
},
{
name: "path parameter are prefixed with go template",
args: args{
filePath: "path/dir/file_name.yaml",
fileContent: defaultContent,
values: map[string]string{},
useGoTemplate: true,
pathParamPrefix: "myRepo",
},
want: []map[string]interface{}{
{
"foo": map[string]interface{}{
"bar": "baz",
},
"myRepo": map[string]interface{}{
"path": map[string]interface{}{
"path": "path/dir",
"basename": "dir",
"filename": "file_name.yaml",
"basenameNormalized": "dir",
"filenameNormalized": "file-name.yaml",
"segments": []string{
"path",
"dir",
},
},
},
},
},
"foo.bar": "baz",
"path": "path/dir",
"path.basename": "dir",
"path.filename": "file_name.yaml",
"path.basenameNormalized": "dir",
"path.filenameNormalized": "file-name.yaml",
"path[0]": "path",
"path[1]": "dir",
},
}, params)
}
func Test_generatePrefixedParamsFromGitFile(t *testing.T) {
values := map[string]string{}
params, err := (*GitGenerator)(nil).generateParamsFromGitFile("path/dir/file_name.yaml", []byte(`
foo:
bar: baz
`), values, false, nil, "myRepo")
if err != nil {
t.Fatal(err)
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
params, err := (*GitGenerator)(nil).generateParamsFromGitFile(tt.args.filePath, tt.args.fileContent, tt.args.values, tt.args.useGoTemplate, tt.args.goTemplateOptions, tt.args.pathParamPrefix)
if (err != nil) != tt.wantErr {
t.Errorf("GitGenerator.generateParamsFromGitFile() error = %v, wantErr %v", err, tt.wantErr)
return
}
assert.Equal(t, tt.want, params)
})
assert.Equal(t, []map[string]interface{}{
{
"foo.bar": "baz",
"myRepo.path": "path/dir",
"myRepo.path.basename": "dir",
"myRepo.path.filename": "file_name.yaml",
"myRepo.path.basenameNormalized": "dir",
"myRepo.path.filenameNormalized": "file-name.yaml",
"myRepo.path[0]": "path",
"myRepo.path[1]": "dir",
},
}, params)
}
func Test_generateParamsFromGitFileGoTemplate(t *testing.T) {
values := map[string]string{}
params, err := (*GitGenerator)(nil).generateParamsFromGitFile("path/dir/file_name.yaml", []byte(`
foo:
bar: baz
`), values, true, nil, "")
if err != nil {
t.Fatal(err)
}
assert.Equal(t, []map[string]interface{}{
{
"foo": map[string]interface{}{
"bar": "baz",
},
"path": map[string]interface{}{
"path": "path/dir",
"basename": "dir",
"filename": "file_name.yaml",
"basenameNormalized": "dir",
"filenameNormalized": "file-name.yaml",
"segments": []string{
"path",
"dir",
},
},
},
}, params)
}
func Test_generatePrefixedParamsFromGitFileGoTemplate(t *testing.T) {
values := map[string]string{}
params, err := (*GitGenerator)(nil).generateParamsFromGitFile("path/dir/file_name.yaml", []byte(`
foo:
bar: baz
`), values, true, nil, "myRepo")
if err != nil {
t.Fatal(err)
}
assert.Equal(t, []map[string]interface{}{
{
"foo": map[string]interface{}{
"bar": "baz",
},
"myRepo": map[string]interface{}{
"path": map[string]interface{}{
"path": "path/dir",
"basename": "dir",
"filename": "file_name.yaml",
"basenameNormalized": "dir",
"filenameNormalized": "file-name.yaml",
"segments": []string{
"path",
"dir",
},
},
},
},
}, params)
}
func TestGitGenerateParamsFromDirectories(t *testing.T) {

View File

@@ -27,16 +27,14 @@ type PullRequestGenerator struct {
auth SCMAuthProviders
scmRootCAPath string
allowedSCMProviders []string
enableSCMProviders bool
}
func NewPullRequestGenerator(client client.Client, auth SCMAuthProviders, scmRootCAPath string, allowedScmProviders []string, enableSCMProviders bool) Generator {
func NewPullRequestGenerator(client client.Client, auth SCMAuthProviders, scmRootCAPath string, allowedScmProviders []string) Generator {
g := &PullRequestGenerator{
client: client,
auth: auth,
scmRootCAPath: scmRootCAPath,
allowedSCMProviders: allowedScmProviders,
enableSCMProviders: enableSCMProviders,
}
g.selectServiceProviderFunc = g.selectServiceProvider
return g
@@ -68,7 +66,7 @@ func (g *PullRequestGenerator) GenerateParams(appSetGenerator *argoprojiov1alpha
ctx := context.Background()
svc, err := g.selectServiceProviderFunc(ctx, appSetGenerator.PullRequest, applicationSetInfo)
if err != nil {
return nil, fmt.Errorf("failed to select pull request service provider: %w", err)
return nil, fmt.Errorf("failed to select pull request service provider: %v", err)
}
pulls, err := pullrequest.ListPullRequests(ctx, svc, appSetGenerator.PullRequest.Filters)
@@ -123,18 +121,17 @@ func (g *PullRequestGenerator) GenerateParams(appSetGenerator *argoprojiov1alpha
// selectServiceProvider selects the provider to get pull requests from the configuration
func (g *PullRequestGenerator) selectServiceProvider(ctx context.Context, generatorConfig *argoprojiov1alpha1.PullRequestGenerator, applicationSetInfo *argoprojiov1alpha1.ApplicationSet) (pullrequest.PullRequestService, error) {
if !g.enableSCMProviders {
return nil, ErrSCMProvidersDisabled
}
if err := ScmProviderAllowed(applicationSetInfo, generatorConfig, g.allowedSCMProviders); err != nil {
return nil, fmt.Errorf("scm provider not allowed: %w", err)
}
if generatorConfig.Github != nil {
if !ScmProviderAllowed(applicationSetInfo, generatorConfig.Github.API, g.allowedSCMProviders) {
return nil, fmt.Errorf("scm provider not allowed: %s", generatorConfig.Github.API)
}
return g.github(ctx, generatorConfig.Github, applicationSetInfo)
}
if generatorConfig.GitLab != nil {
providerConfig := generatorConfig.GitLab
if !ScmProviderAllowed(applicationSetInfo, providerConfig.API, g.allowedSCMProviders) {
return nil, fmt.Errorf("scm provider not allowed: %s", providerConfig.API)
}
token, err := g.getSecretRef(ctx, providerConfig.TokenRef, applicationSetInfo.Namespace)
if err != nil {
return nil, fmt.Errorf("error fetching Secret token: %v", err)
@@ -143,6 +140,9 @@ func (g *PullRequestGenerator) selectServiceProvider(ctx context.Context, genera
}
if generatorConfig.Gitea != nil {
providerConfig := generatorConfig.Gitea
if !ScmProviderAllowed(applicationSetInfo, providerConfig.API, g.allowedSCMProviders) {
return nil, fmt.Errorf("scm provider not allowed: %s", generatorConfig.Gitea.API)
}
token, err := g.getSecretRef(ctx, providerConfig.TokenRef, applicationSetInfo.Namespace)
if err != nil {
return nil, fmt.Errorf("error fetching Secret token: %v", err)
@@ -151,6 +151,9 @@ func (g *PullRequestGenerator) selectServiceProvider(ctx context.Context, genera
}
if generatorConfig.BitbucketServer != nil {
providerConfig := generatorConfig.BitbucketServer
if !ScmProviderAllowed(applicationSetInfo, providerConfig.API, g.allowedSCMProviders) {
return nil, fmt.Errorf("scm provider not allowed: %s", providerConfig.API)
}
if providerConfig.BasicAuth != nil {
password, err := g.getSecretRef(ctx, providerConfig.BasicAuth.PasswordRef, applicationSetInfo.Namespace)
if err != nil {

View File

@@ -278,7 +278,7 @@ func TestAllowedSCMProviderPullRequest(t *testing.T) {
cases := []struct {
name string
providerConfig *argoprojiov1alpha1.PullRequestGenerator
expectedError error
expectedError string
}{
{
name: "Error Github",
@@ -287,7 +287,7 @@ func TestAllowedSCMProviderPullRequest(t *testing.T) {
API: "https://myservice.mynamespace.svc.cluster.local",
},
},
expectedError: &ErrDisallowedSCMProvider{},
expectedError: "failed to select pull request service provider: scm provider not allowed: https://myservice.mynamespace.svc.cluster.local",
},
{
name: "Error Gitlab",
@@ -296,7 +296,7 @@ func TestAllowedSCMProviderPullRequest(t *testing.T) {
API: "https://myservice.mynamespace.svc.cluster.local",
},
},
expectedError: &ErrDisallowedSCMProvider{},
expectedError: "failed to select pull request service provider: scm provider not allowed: https://myservice.mynamespace.svc.cluster.local",
},
{
name: "Error Gitea",
@@ -305,7 +305,7 @@ func TestAllowedSCMProviderPullRequest(t *testing.T) {
API: "https://myservice.mynamespace.svc.cluster.local",
},
},
expectedError: &ErrDisallowedSCMProvider{},
expectedError: "failed to select pull request service provider: scm provider not allowed: https://myservice.mynamespace.svc.cluster.local",
},
{
name: "Error Bitbucket",
@@ -314,7 +314,7 @@ func TestAllowedSCMProviderPullRequest(t *testing.T) {
API: "https://myservice.mynamespace.svc.cluster.local",
},
},
expectedError: &ErrDisallowedSCMProvider{},
expectedError: "failed to select pull request service provider: scm provider not allowed: https://myservice.mynamespace.svc.cluster.local",
},
}
@@ -330,7 +330,7 @@ func TestAllowedSCMProviderPullRequest(t *testing.T) {
"gitea.myorg.com",
"bitbucket.myorg.com",
"azuredevops.myorg.com",
}, true)
})
applicationSetInfo := argoprojiov1alpha1.ApplicationSet{
ObjectMeta: metav1.ObjectMeta{
@@ -346,29 +346,7 @@ func TestAllowedSCMProviderPullRequest(t *testing.T) {
_, err := pullRequestGenerator.GenerateParams(&applicationSetInfo.Spec.Generators[0], &applicationSetInfo)
assert.Error(t, err, "Must return an error")
assert.ErrorAs(t, err, testCaseCopy.expectedError)
assert.Equal(t, testCaseCopy.expectedError, err.Error())
})
}
}
func TestSCMProviderDisabled_PRGenerator(t *testing.T) {
generator := NewPullRequestGenerator(nil, SCMAuthProviders{}, "", []string{}, false)
applicationSetInfo := argoprojiov1alpha1.ApplicationSet{
ObjectMeta: metav1.ObjectMeta{
Name: "set",
},
Spec: argoprojiov1alpha1.ApplicationSetSpec{
Generators: []argoprojiov1alpha1.ApplicationSetGenerator{{
PullRequest: &argoprojiov1alpha1.PullRequestGenerator{
Github: &argoprojiov1alpha1.PullRequestGeneratorGithub{
API: "https://myservice.mynamespace.svc.cluster.local",
},
},
}},
},
}
_, err := generator.GenerateParams(&applicationSetInfo.Spec.Generators[0], &applicationSetInfo)
assert.ErrorIs(t, err, ErrSCMProvidersDisabled)
}

View File

@@ -2,7 +2,6 @@ package generators
import (
"context"
"errors"
"fmt"
"strings"
"time"
@@ -32,26 +31,24 @@ type SCMProviderGenerator struct {
SCMAuthProviders
scmRootCAPath string
allowedSCMProviders []string
enableSCMProviders bool
}
type SCMAuthProviders struct {
GitHubApps github_app_auth.Credentials
}
func NewSCMProviderGenerator(client client.Client, providers SCMAuthProviders, scmRootCAPath string, allowedSCMProviders []string, enableSCMProviders bool) Generator {
func NewSCMProviderGenerator(client client.Client, providers SCMAuthProviders, scmRootCAPath string, allowedSCMProviders []string) Generator {
return &SCMProviderGenerator{
client: client,
SCMAuthProviders: providers,
scmRootCAPath: scmRootCAPath,
allowedSCMProviders: allowedSCMProviders,
enableSCMProviders: enableSCMProviders,
}
}
// Testing generator
func NewTestSCMProviderGenerator(overrideProvider scm_provider.SCMProviderService) Generator {
return &SCMProviderGenerator{overrideProvider: overrideProvider, enableSCMProviders: true}
return &SCMProviderGenerator{overrideProvider: overrideProvider}
}
func (g *SCMProviderGenerator) GetRequeueAfter(appSetGenerator *argoprojiov1alpha1.ApplicationSetGenerator) time.Duration {
@@ -68,34 +65,14 @@ func (g *SCMProviderGenerator) GetTemplate(appSetGenerator *argoprojiov1alpha1.A
return &appSetGenerator.SCMProvider.Template
}
var ErrSCMProvidersDisabled = errors.New("scm providers are disabled")
type ErrDisallowedSCMProvider struct {
Provider string
Allowed []string
}
func NewErrDisallowedSCMProvider(provider string, allowed []string) ErrDisallowedSCMProvider {
return ErrDisallowedSCMProvider{
Provider: provider,
Allowed: allowed,
}
}
func (e ErrDisallowedSCMProvider) Error() string {
return fmt.Sprintf("scm provider %q not allowed, must use one of the following: %s", e.Provider, strings.Join(e.Allowed, ", "))
}
func ScmProviderAllowed(applicationSetInfo *argoprojiov1alpha1.ApplicationSet, generator SCMGeneratorWithCustomApiUrl, allowedScmProviders []string) error {
url := generator.CustomApiUrl()
func ScmProviderAllowed(applicationSetInfo *argoprojiov1alpha1.ApplicationSet, url string, allowedScmProviders []string) bool {
if url == "" || len(allowedScmProviders) == 0 {
return nil
return true
}
for _, allowedScmProvider := range allowedScmProviders {
if url == allowedScmProvider {
return nil
return true
}
}
@@ -103,9 +80,9 @@ func ScmProviderAllowed(applicationSetInfo *argoprojiov1alpha1.ApplicationSet, g
common.SecurityField: common.SecurityMedium,
"applicationset": applicationSetInfo.Name,
"appSetNamespace": applicationSetInfo.Namespace,
}).Debugf("attempted to use disallowed SCM %q, must use one of the following: %s", url, strings.Join(allowedScmProviders, ", "))
}).Debugf("attempted to use disallowed SCM %q", url)
return NewErrDisallowedSCMProvider(url, allowedScmProviders)
return false
}
func (g *SCMProviderGenerator) GenerateParams(appSetGenerator *argoprojiov1alpha1.ApplicationSetGenerator, applicationSetInfo *argoprojiov1alpha1.ApplicationSet) ([]map[string]interface{}, error) {
@@ -117,28 +94,26 @@ func (g *SCMProviderGenerator) GenerateParams(appSetGenerator *argoprojiov1alpha
return nil, EmptyAppSetGeneratorError
}
if !g.enableSCMProviders {
return nil, ErrSCMProvidersDisabled
}
ctx := context.Background()
// Create the SCM provider helper.
providerConfig := appSetGenerator.SCMProvider
if err := ScmProviderAllowed(applicationSetInfo, providerConfig, g.allowedSCMProviders); err != nil {
return nil, fmt.Errorf("scm provider not allowed: %w", err)
}
ctx := context.Background()
var provider scm_provider.SCMProviderService
if g.overrideProvider != nil {
provider = g.overrideProvider
} else if providerConfig.Github != nil {
if !ScmProviderAllowed(applicationSetInfo, providerConfig.Github.API, g.allowedSCMProviders) {
return nil, fmt.Errorf("scm provider not allowed: %s", providerConfig.Github.API)
}
var err error
provider, err = g.githubProvider(ctx, providerConfig.Github, applicationSetInfo)
if err != nil {
return nil, fmt.Errorf("scm provider: %w", err)
}
} else if providerConfig.Gitlab != nil {
if !ScmProviderAllowed(applicationSetInfo, providerConfig.Gitlab.API, g.allowedSCMProviders) {
return nil, fmt.Errorf("scm provider not allowed: %s", providerConfig.Gitlab.API)
}
token, err := g.getSecretRef(ctx, providerConfig.Gitlab.TokenRef, applicationSetInfo.Namespace)
if err != nil {
return nil, fmt.Errorf("error fetching Gitlab token: %v", err)
@@ -148,6 +123,9 @@ func (g *SCMProviderGenerator) GenerateParams(appSetGenerator *argoprojiov1alpha
return nil, fmt.Errorf("error initializing Gitlab service: %v", err)
}
} else if providerConfig.Gitea != nil {
if !ScmProviderAllowed(applicationSetInfo, providerConfig.Gitea.API, g.allowedSCMProviders) {
return nil, fmt.Errorf("scm provider not allowed: %s", providerConfig.Gitea.API)
}
token, err := g.getSecretRef(ctx, providerConfig.Gitea.TokenRef, applicationSetInfo.Namespace)
if err != nil {
return nil, fmt.Errorf("error fetching Gitea token: %v", err)
@@ -158,6 +136,9 @@ func (g *SCMProviderGenerator) GenerateParams(appSetGenerator *argoprojiov1alpha
}
} else if providerConfig.BitbucketServer != nil {
providerConfig := providerConfig.BitbucketServer
if !ScmProviderAllowed(applicationSetInfo, providerConfig.API, g.allowedSCMProviders) {
return nil, fmt.Errorf("scm provider not allowed: %s", providerConfig.API)
}
var scmError error
if providerConfig.BasicAuth != nil {
password, err := g.getSecretRef(ctx, providerConfig.BasicAuth.PasswordRef, applicationSetInfo.Namespace)
@@ -172,6 +153,9 @@ func (g *SCMProviderGenerator) GenerateParams(appSetGenerator *argoprojiov1alpha
return nil, fmt.Errorf("error initializing Bitbucket Server service: %v", scmError)
}
} else if providerConfig.AzureDevOps != nil {
if !ScmProviderAllowed(applicationSetInfo, providerConfig.AzureDevOps.API, g.allowedSCMProviders) {
return nil, fmt.Errorf("scm provider not allowed: %s", providerConfig.AzureDevOps.API)
}
token, err := g.getSecretRef(ctx, providerConfig.AzureDevOps.AccessTokenRef, applicationSetInfo.Namespace)
if err != nil {
return nil, fmt.Errorf("error fetching Azure Devops access token: %v", err)

View File

@@ -174,7 +174,7 @@ func TestSCMProviderGenerateParams(t *testing.T) {
mockProvider := &scm_provider.MockProvider{
Repos: testCaseCopy.repos,
}
scmGenerator := &SCMProviderGenerator{overrideProvider: mockProvider, enableSCMProviders: true}
scmGenerator := &SCMProviderGenerator{overrideProvider: mockProvider}
applicationSetInfo := argoprojiov1alpha1.ApplicationSet{
ObjectMeta: metav1.ObjectMeta{
Name: "set",
@@ -205,7 +205,7 @@ func TestAllowedSCMProvider(t *testing.T) {
cases := []struct {
name string
providerConfig *argoprojiov1alpha1.SCMProviderGenerator
expectedError error
expectedError string
}{
{
name: "Error Github",
@@ -214,7 +214,7 @@ func TestAllowedSCMProvider(t *testing.T) {
API: "https://myservice.mynamespace.svc.cluster.local",
},
},
expectedError: &ErrDisallowedSCMProvider{},
expectedError: "scm provider not allowed: https://myservice.mynamespace.svc.cluster.local",
},
{
name: "Error Gitlab",
@@ -223,7 +223,7 @@ func TestAllowedSCMProvider(t *testing.T) {
API: "https://myservice.mynamespace.svc.cluster.local",
},
},
expectedError: &ErrDisallowedSCMProvider{},
expectedError: "scm provider not allowed: https://myservice.mynamespace.svc.cluster.local",
},
{
name: "Error Gitea",
@@ -232,7 +232,7 @@ func TestAllowedSCMProvider(t *testing.T) {
API: "https://myservice.mynamespace.svc.cluster.local",
},
},
expectedError: &ErrDisallowedSCMProvider{},
expectedError: "scm provider not allowed: https://myservice.mynamespace.svc.cluster.local",
},
{
name: "Error Bitbucket",
@@ -241,7 +241,7 @@ func TestAllowedSCMProvider(t *testing.T) {
API: "https://myservice.mynamespace.svc.cluster.local",
},
},
expectedError: &ErrDisallowedSCMProvider{},
expectedError: "scm provider not allowed: https://myservice.mynamespace.svc.cluster.local",
},
{
name: "Error AzureDevops",
@@ -250,7 +250,7 @@ func TestAllowedSCMProvider(t *testing.T) {
API: "https://myservice.mynamespace.svc.cluster.local",
},
},
expectedError: &ErrDisallowedSCMProvider{},
expectedError: "scm provider not allowed: https://myservice.mynamespace.svc.cluster.local",
},
}
@@ -260,16 +260,13 @@ func TestAllowedSCMProvider(t *testing.T) {
t.Run(testCaseCopy.name, func(t *testing.T) {
t.Parallel()
scmGenerator := &SCMProviderGenerator{
allowedSCMProviders: []string{
"github.myorg.com",
"gitlab.myorg.com",
"gitea.myorg.com",
"bitbucket.myorg.com",
"azuredevops.myorg.com",
},
enableSCMProviders: true,
}
scmGenerator := &SCMProviderGenerator{allowedSCMProviders: []string{
"github.myorg.com",
"gitlab.myorg.com",
"gitea.myorg.com",
"bitbucket.myorg.com",
"azuredevops.myorg.com",
}}
applicationSetInfo := argoprojiov1alpha1.ApplicationSet{
ObjectMeta: metav1.ObjectMeta{
@@ -285,29 +282,7 @@ func TestAllowedSCMProvider(t *testing.T) {
_, err := scmGenerator.GenerateParams(&applicationSetInfo.Spec.Generators[0], &applicationSetInfo)
assert.Error(t, err, "Must return an error")
assert.ErrorAs(t, err, testCaseCopy.expectedError)
assert.Equal(t, testCaseCopy.expectedError, err.Error())
})
}
}
func TestSCMProviderDisabled_SCMGenerator(t *testing.T) {
generator := &SCMProviderGenerator{enableSCMProviders: false}
applicationSetInfo := argoprojiov1alpha1.ApplicationSet{
ObjectMeta: metav1.ObjectMeta{
Name: "set",
},
Spec: argoprojiov1alpha1.ApplicationSetSpec{
Generators: []argoprojiov1alpha1.ApplicationSetGenerator{{
SCMProvider: &argoprojiov1alpha1.SCMProviderGenerator{
Github: &argoprojiov1alpha1.SCMProviderGeneratorGithub{
API: "https://myservice.mynamespace.svc.cluster.local",
},
},
}},
},
}
_, err := generator.GenerateParams(&applicationSetInfo.Spec.Generators[0], &applicationSetInfo)
assert.ErrorIs(t, err, ErrSCMProvidersDisabled)
}

View File

@@ -1,5 +0,0 @@
package generators
type SCMGeneratorWithCustomApiUrl interface {
CustomApiUrl() string
}

View File

@@ -180,7 +180,7 @@ func secretToCluster(s *corev1.Secret) (*appv1.Cluster, error) {
if val, err := strconv.Atoi(string(shardStr)); err != nil {
log.Warnf("Error while parsing shard in cluster secret '%s': %v", s.Name, err)
} else {
shard = pointer.Int64(int64(val))
shard = pointer.Int64Ptr(int64(val))
}
}
cluster := appv1.Cluster{

View File

@@ -1,71 +0,0 @@
package utils
import (
"regexp"
"strings"
"sigs.k8s.io/yaml"
)
// SanitizeName sanitizes the name in accordance with the below rules
// 1. contain no more than 253 characters
// 2. contain only lowercase alphanumeric characters, '-' or '.'
// 3. start and end with an alphanumeric character
func SanitizeName(name string) string {
invalidDNSNameChars := regexp.MustCompile("[^-a-z0-9.]")
maxDNSNameLength := 253
name = strings.ToLower(name)
name = invalidDNSNameChars.ReplaceAllString(name, "-")
if len(name) > maxDNSNameLength {
name = name[:maxDNSNameLength]
}
return strings.Trim(name, "-.")
}
// This has been copied from helm and may be removed as soon as it is retrofited in sprig
// toYAML takes an interface, marshals it to yaml, and returns a string. It will
// always return a string, even on marshal error (empty string).
//
// This is designed to be called from a template.
func toYAML(v interface{}) (string, error) {
data, err := yaml.Marshal(v)
if err != nil {
// Swallow errors inside of a template.
return "", err
}
return strings.TrimSuffix(string(data), "\n"), nil
}
// This has been copied from helm and may be removed as soon as it is retrofited in sprig
// fromYAML converts a YAML document into a map[string]interface{}.
//
// This is not a general-purpose YAML parser, and will not parse all valid
// YAML documents. Additionally, because its intended use is within templates
// it tolerates errors. It will insert the returned error message string into
// m["Error"] in the returned map.
func fromYAML(str string) (map[string]interface{}, error) {
m := map[string]interface{}{}
if err := yaml.Unmarshal([]byte(str), &m); err != nil {
return nil, err
}
return m, nil
}
// This has been copied from helm and may be removed as soon as it is retrofited in sprig
// fromYAMLArray converts a YAML array into a []interface{}.
//
// This is not a general-purpose YAML parser, and will not parse all valid
// YAML documents. Additionally, because its intended use is within templates
// it tolerates errors. It will insert the returned error message string as
// the first and only item in the returned array.
func fromYAMLArray(str string) ([]interface{}, error) {
a := []interface{}{}
if err := yaml.Unmarshal([]byte(str), &a); err != nil {
return nil, err
}
return a, nil
}

View File

@@ -32,9 +32,6 @@ func init() {
delete(sprigFuncMap, "expandenv")
delete(sprigFuncMap, "getHostByName")
sprigFuncMap["normalize"] = SanitizeName
sprigFuncMap["toYaml"] = toYAML
sprigFuncMap["fromYaml"] = fromYAML
sprigFuncMap["fromYamlArray"] = fromYAMLArray
}
type Renderer interface {
@@ -434,6 +431,23 @@ func NormalizeBitbucketBasePath(basePath string) string {
return basePath
}
// SanitizeName sanitizes the name in accordance with the below rules
// 1. contain no more than 253 characters
// 2. contain only lowercase alphanumeric characters, '-' or '.'
// 3. start and end with an alphanumeric character
func SanitizeName(name string) string {
invalidDNSNameChars := regexp.MustCompile("[^-a-z0-9.]")
maxDNSNameLength := 253
name = strings.ToLower(name)
name = invalidDNSNameChars.ReplaceAllString(name, "-")
if len(name) > maxDNSNameLength {
name = name[:maxDNSNameLength]
}
return strings.Trim(name, "-.")
}
func getTlsConfigWithCACert(scmRootCAPath string) *tls.Config {
tlsConfig := &tls.Config{}

View File

@@ -555,64 +555,6 @@ func TestRenderTemplateParamsGoTemplate(t *testing.T) {
templateOptions: []string{"missingkey=error"},
errorMessage: `failed to execute go template --> {{.doesnotexist}} <--: template: :1:6: executing "" at <.doesnotexist>: map has no entry for key "doesnotexist"`,
},
{
name: "toYaml",
fieldVal: `{{ toYaml . | indent 2 }}`,
expectedVal: " foo:\n bar:\n bool: true\n number: 2\n str: Hello world",
params: map[string]interface{}{
"foo": map[string]interface{}{
"bar": map[string]interface{}{
"bool": true,
"number": 2,
"str": "Hello world",
},
},
},
},
{
name: "toYaml Error",
fieldVal: `{{ toYaml . | indent 2 }}`,
expectedVal: " foo:\n bar:\n bool: true\n number: 2\n str: Hello world",
errorMessage: "failed to execute go template {{ toYaml . | indent 2 }}: template: :1:3: executing \"\" at <toYaml .>: error calling toYaml: error marshaling into JSON: json: unsupported type: func(*string)",
params: map[string]interface{}{
"foo": func(test *string) {
},
},
},
{
name: "fromYaml",
fieldVal: `{{ get (fromYaml .value) "hello" }}`,
expectedVal: "world",
params: map[string]interface{}{
"value": "hello: world",
},
},
{
name: "fromYaml error",
fieldVal: `{{ get (fromYaml .value) "hello" }}`,
expectedVal: "world",
errorMessage: "failed to execute go template {{ get (fromYaml .value) \"hello\" }}: template: :1:8: executing \"\" at <fromYaml .value>: error calling fromYaml: error unmarshaling JSON: while decoding JSON: json: cannot unmarshal string into Go value of type map[string]interface {}",
params: map[string]interface{}{
"value": "non\n compliant\n yaml",
},
},
{
name: "fromYamlArray",
fieldVal: `{{ fromYamlArray .value | last }}`,
expectedVal: "bonjour tout le monde",
params: map[string]interface{}{
"value": "- hello world\n- bonjour tout le monde",
},
},
{
name: "fromYamlArray error",
fieldVal: `{{ fromYamlArray .value | last }}`,
expectedVal: "bonjour tout le monde",
errorMessage: "failed to execute go template {{ fromYamlArray .value | last }}: template: :1:3: executing \"\" at <fromYamlArray .value>: error calling fromYamlArray: error unmarshaling JSON: while decoding JSON: json: cannot unmarshal string into Go value of type []interface {}",
params: map[string]interface{}{
"value": "non\n compliant\n yaml",
},
},
}
for _, test := range tests {

View File

@@ -234,7 +234,7 @@
},
{
"type": "string",
"description": "forces application reconciliation if set to 'hard'.",
"description": "forces application reconciliation if set to true.",
"name": "refresh",
"in": "query"
},
@@ -573,7 +573,7 @@
},
{
"type": "string",
"description": "forces application reconciliation if set to 'hard'.",
"description": "forces application reconciliation if set to true.",
"name": "refresh",
"in": "query"
},
@@ -3816,7 +3816,7 @@
},
{
"type": "string",
"description": "forces application reconciliation if set to 'hard'.",
"description": "forces application reconciliation if set to true.",
"name": "refresh",
"in": "query"
},
@@ -5089,7 +5089,7 @@
}
},
"runtimeRawExtension": {
"description": "RawExtension is used to hold extensions in external versions.\n\nTo use this, make a field which has RawExtension as its type in your external, versioned\nstruct, and Object in your internal struct. You also need to register your\nvarious plugin types.\n\n// Internal package:\n\n\ttype MyAPIObject struct {\n\t\truntime.TypeMeta `json:\",inline\"`\n\t\tMyPlugin runtime.Object `json:\"myPlugin\"`\n\t}\n\n\ttype PluginA struct {\n\t\tAOption string `json:\"aOption\"`\n\t}\n\n// External package:\n\n\ttype MyAPIObject struct {\n\t\truntime.TypeMeta `json:\",inline\"`\n\t\tMyPlugin runtime.RawExtension `json:\"myPlugin\"`\n\t}\n\n\ttype PluginA struct {\n\t\tAOption string `json:\"aOption\"`\n\t}\n\n// On the wire, the JSON will look something like this:\n\n\t{\n\t\t\"kind\":\"MyAPIObject\",\n\t\t\"apiVersion\":\"v1\",\n\t\t\"myPlugin\": {\n\t\t\t\"kind\":\"PluginA\",\n\t\t\t\"aOption\":\"foo\",\n\t\t},\n\t}\n\nSo what happens? Decode first uses json or yaml to unmarshal the serialized data into\nyour external MyAPIObject. That causes the raw JSON to be stored, but not unpacked.\nThe next step is to copy (using pkg/conversion) into the internal struct. The runtime\npackage's DefaultScheme has conversion functions installed which will unpack the\nJSON stored in RawExtension, turning it into the correct object type, and storing it\nin the Object. (TODO: In the case where the object is of an unknown type, a\nruntime.Unknown object will be created and stored.)\n\n+k8s:deepcopy-gen=true\n+protobuf=true\n+k8s:openapi-gen=true",
"description": "RawExtension is used to hold extensions in external versions.\n\nTo use this, make a field which has RawExtension as its type in your external, versioned\nstruct, and Object in your internal struct. You also need to register your\nvarious plugin types.\n\n// Internal package:\ntype MyAPIObject struct {\n\truntime.TypeMeta `json:\",inline\"`\n\tMyPlugin runtime.Object `json:\"myPlugin\"`\n}\ntype PluginA struct {\n\tAOption string `json:\"aOption\"`\n}\n\n// External package:\ntype MyAPIObject struct {\n\truntime.TypeMeta `json:\",inline\"`\n\tMyPlugin runtime.RawExtension `json:\"myPlugin\"`\n}\ntype PluginA struct {\n\tAOption string `json:\"aOption\"`\n}\n\n// On the wire, the JSON will look something like this:\n{\n\t\"kind\":\"MyAPIObject\",\n\t\"apiVersion\":\"v1\",\n\t\"myPlugin\": {\n\t\t\"kind\":\"PluginA\",\n\t\t\"aOption\":\"foo\",\n\t},\n}\n\nSo what happens? Decode first uses json or yaml to unmarshal the serialized data into\nyour external MyAPIObject. That causes the raw JSON to be stored, but not unpacked.\nThe next step is to copy (using pkg/conversion) into the internal struct. The runtime\npackage's DefaultScheme has conversion functions installed which will unpack the\nJSON stored in RawExtension, turning it into the correct object type, and storing it\nin the Object. (TODO: In the case where the object is of an unknown type, a\nruntime.Unknown object will be created and stored.)\n\n+k8s:deepcopy-gen=true\n+protobuf=true\n+k8s:openapi-gen=true",
"type": "object",
"properties": {
"raw": {
@@ -5496,6 +5496,10 @@
"type": "string"
}
},
"clusterName": {
"description": "Deprecated: ClusterName is a legacy field that was always cleared by\nthe system and never used; it will be removed completely in 1.25.\n\nThe name in the go struct is changed to help clients detect\naccidental use.\n\n+optional",
"type": "string"
},
"creationTimestamp": {
"$ref": "#/definitions/v1Time"
},
@@ -5567,8 +5571,8 @@
}
},
"v1ObjectReference": {
"description": "ObjectReference contains enough information to let you inspect or modify the referred object.\n---\nNew uses of this type are discouraged because of difficulty describing its usage when embedded in APIs.\n 1. Ignored fields. It includes many fields which are not generally honored. For instance, ResourceVersion and FieldPath are both very rarely valid in actual usage.\n 2. Invalid usage help. It is impossible to add specific help for individual usage. In most embedded usages, there are particular\n restrictions like, \"must refer only to types A and B\" or \"UID not honored\" or \"name must be restricted\".\n Those cannot be well described when embedded.\n 3. Inconsistent validation. Because the usages are different, the validation rules are different by usage, which makes it hard for users to predict what will happen.\n 4. The fields are both imprecise and overly precise. Kind is not a precise mapping to a URL. This can produce ambiguity\n during interpretation and require a REST mapping. In most cases, the dependency is on the group,resource tuple\n and the version of the actual struct is irrelevant.\n 5. We cannot easily change it. Because this type is embedded in many locations, updates to this type\n will affect numerous schemas. Don't make new APIs embed an underspecified API type they do not control.\n\nInstead of using this type, create a locally provided and used type that is well-focused on your reference.\nFor example, ServiceReferences for admission registration: https://github.com/kubernetes/api/blob/release-1.17/admissionregistration/v1/types.go#L533 .\n+k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object\n+structType=atomic",
"type": "object",
"title": "ObjectReference contains enough information to let you inspect or modify the referred object.\n---\nNew uses of this type are discouraged because of difficulty describing its usage when embedded in APIs.\n 1. Ignored fields. It includes many fields which are not generally honored. For instance, ResourceVersion and FieldPath are both very rarely valid in actual usage.\n 2. Invalid usage help. It is impossible to add specific help for individual usage. In most embedded usages, there are particular\n restrictions like, \"must refer only to types A and B\" or \"UID not honored\" or \"name must be restricted\".\n Those cannot be well described when embedded.\n 3. Inconsistent validation. Because the usages are different, the validation rules are different by usage, which makes it hard for users to predict what will happen.\n 4. The fields are both imprecise and overly precise. Kind is not a precise mapping to a URL. This can produce ambiguity\n during interpretation and require a REST mapping. In most cases, the dependency is on the group,resource tuple\n and the version of the actual struct is irrelevant.\n 5. We cannot easily change it. Because this type is embedded in many locations, updates to this type\n will affect numerous schemas. Don't make new APIs embed an underspecified API type they do not control.\nInstead of using this type, create a locally provided and used type that is well-focused on your reference.\nFor example, ServiceReferences for admission registration: https://github.com/kubernetes/api/blob/release-1.17/admissionregistration/v1/types.go#L533 .\n+k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object\n+structType=atomic",
"properties": {
"apiVersion": {
"type": "string",

View File

@@ -6,7 +6,6 @@ import (
"math"
"time"
"github.com/argoproj/argo-cd/v2/pkg/ratelimiter"
"github.com/argoproj/pkg/stats"
"github.com/redis/go-redis/v9"
log "github.com/sirupsen/logrus"
@@ -46,7 +45,6 @@ const (
func NewCommand() *cobra.Command {
var (
workqueueRateLimit ratelimiter.AppControllerRateLimiterConfig
clientConfig clientcmd.ClientConfig
appResyncPeriod int64
appHardResyncPeriod int64
@@ -162,7 +160,6 @@ func NewCommand() *cobra.Command {
persistResourceHealth,
clusterFilter,
applicationNamespaces,
&workqueueRateLimit,
)
errors.CheckError(err)
cacheutil.CollectMetrics(redisClient, appController.GetMetricsServer())
@@ -208,15 +205,6 @@ func NewCommand() *cobra.Command {
command.Flags().StringSliceVar(&applicationNamespaces, "application-namespaces", env.StringsFromEnv("ARGOCD_APPLICATION_NAMESPACES", []string{}, ","), "List of additional namespaces that applications are allowed to be reconciled from")
command.Flags().BoolVar(&persistResourceHealth, "persist-resource-health", env.ParseBoolFromEnv("ARGOCD_APPLICATION_CONTROLLER_PERSIST_RESOURCE_HEALTH", true), "Enables storing the managed resources health in the Application CRD")
command.Flags().StringVar(&shardingAlgorithm, "sharding-method", env.StringFromEnv(common.EnvControllerShardingAlgorithm, common.DefaultShardingAlgorithm), "Enables choice of sharding method. Supported sharding methods are : [legacy, round-robin] ")
// global queue rate limit config
command.Flags().Int64Var(&workqueueRateLimit.BucketSize, "wq-bucket-size", env.ParseInt64FromEnv("WORKQUEUE_BUCKET_SIZE", 500, 1, math.MaxInt64), "Set Workqueue Rate Limiter Bucket Size, default 500")
command.Flags().Int64Var(&workqueueRateLimit.BucketQPS, "wq-bucket-qps", env.ParseInt64FromEnv("WORKQUEUE_BUCKET_QPS", 50, 1, math.MaxInt64), "Set Workqueue Rate Limiter Bucket QPS, default 50")
// individual item rate limit config
// when WORKQUEUE_FAILURE_COOLDOWN is 0 per item rate limiting is disabled(default)
command.Flags().DurationVar(&workqueueRateLimit.FailureCoolDown, "wq-cooldown-ns", time.Duration(env.ParseInt64FromEnv("WORKQUEUE_FAILURE_COOLDOWN_NS", 0, 0, (24*time.Hour).Nanoseconds())), "Set Workqueue Per Item Rate Limiter Cooldown duration in ns, default 0(per item rate limiter disabled)")
command.Flags().DurationVar(&workqueueRateLimit.BaseDelay, "wq-basedelay-ns", time.Duration(env.ParseInt64FromEnv("WORKQUEUE_BASE_DELAY_NS", time.Millisecond.Nanoseconds(), time.Nanosecond.Nanoseconds(), (24*time.Hour).Nanoseconds())), "Set Workqueue Per Item Rate Limiter Base Delay duration in nanoseconds, default 1000000 (1ms)")
command.Flags().DurationVar(&workqueueRateLimit.MaxDelay, "wq-maxdelay-ns", time.Duration(env.ParseInt64FromEnv("WORKQUEUE_MAX_DELAY_NS", time.Second.Nanoseconds(), 1*time.Millisecond.Nanoseconds(), (24*time.Hour).Nanoseconds())), "Set Workqueue Per Item Rate Limiter Max Delay duration in nanoseconds, default 1000000000 (1s)")
command.Flags().Float64Var(&workqueueRateLimit.BackoffFactor, "wq-backoff-factor", env.ParseFloat64FromEnv("WORKQUEUE_BACKOFF_FACTOR", 1.5, 0, math.MaxFloat64), "Set Workqueue Per Item Rate Limiter Backoff Factor, default is 1.5")
command.Flags().BoolVar(&enableDynamicClusterDistribution, "dynamic-cluster-distribution-enabled", env.ParseBoolFromEnv(common.EnvEnableDynamicClusterDistribution, false), "Enables dynamic cluster distribution.")
cacheSource = appstatecache.AddCacheFlagsToCmd(&command, func(client *redis.Client) {
redisClient = client

View File

@@ -65,7 +65,6 @@ func NewCommand() *cobra.Command {
allowedScmProviders []string
globalPreservedAnnotations []string
globalPreservedLabels []string
enableScmProviders bool
)
scheme := runtime.NewScheme()
_ = clientgoscheme.AddToScheme(scheme)
@@ -108,8 +107,8 @@ func NewCommand() *cobra.Command {
// If the applicationset-namespaces contains only one namespace it corresponds to the current namespace
if len(applicationSetNamespaces) == 1 {
watchedNamespace = (applicationSetNamespaces)[0]
} else if enableScmProviders && len(allowedScmProviders) == 0 {
log.Error("When enabling applicationset in any namespace using applicationset-namespaces, you must either set --enable-scm-providers=false or specify --allowed-scm-providers")
} else if len(allowedScmProviders) == 0 {
log.Error("When enabling applicationset in any namespace using applicationset-namespaces, allowed-scm-providers is required")
os.Exit(1)
}
@@ -163,9 +162,9 @@ func NewCommand() *cobra.Command {
"List": generators.NewListGenerator(),
"Clusters": generators.NewClusterGenerator(mgr.GetClient(), ctx, k8sClient, namespace),
"Git": generators.NewGitGenerator(argoCDService),
"SCMProvider": generators.NewSCMProviderGenerator(mgr.GetClient(), scmAuth, scmRootCAPath, allowedScmProviders, enableScmProviders),
"SCMProvider": generators.NewSCMProviderGenerator(mgr.GetClient(), scmAuth, scmRootCAPath, allowedScmProviders),
"ClusterDecisionResource": generators.NewDuckTypeGenerator(ctx, dynamicClient, k8sClient, namespace),
"PullRequest": generators.NewPullRequestGenerator(mgr.GetClient(), scmAuth, scmRootCAPath, allowedScmProviders, enableScmProviders),
"PullRequest": generators.NewPullRequestGenerator(mgr.GetClient(), scmAuth, scmRootCAPath, allowedScmProviders),
"Plugin": generators.NewPluginGenerator(mgr.GetClient(), ctx, k8sClient, namespace),
}
@@ -248,8 +247,7 @@ func NewCommand() *cobra.Command {
command.Flags().BoolVar(&debugLog, "debug", env.ParseBoolFromEnv("ARGOCD_APPLICATIONSET_CONTROLLER_DEBUG", false), "Print debug logs. Takes precedence over loglevel")
command.Flags().StringVar(&cmdutil.LogFormat, "logformat", env.StringFromEnv("ARGOCD_APPLICATIONSET_CONTROLLER_LOGFORMAT", "text"), "Set the logging format. One of: text|json")
command.Flags().StringVar(&cmdutil.LogLevel, "loglevel", env.StringFromEnv("ARGOCD_APPLICATIONSET_CONTROLLER_LOGLEVEL", "info"), "Set the logging level. One of: debug|info|warn|error")
command.Flags().StringSliceVar(&allowedScmProviders, "allowed-scm-providers", env.StringsFromEnv("ARGOCD_APPLICATIONSET_CONTROLLER_ALLOWED_SCM_PROVIDERS", []string{}, ","), "The list of allowed custom SCM provider API URLs. This restriction does not apply to SCM or PR generators which do not accept a custom API URL. (Default: Empty = all)")
command.Flags().BoolVar(&enableScmProviders, "enable-scm-providers", env.ParseBoolFromEnv("ARGOCD_APPLICATIONSET_CONTROLLER_ENABLE_SCM_PROVIDERS", true), "Enable retrieving information from SCM providers, used by the SCM and PR generators (Default: true)")
command.Flags().StringSliceVar(&allowedScmProviders, "allowed-scm-providers", env.StringsFromEnv("ARGOCD_APPLICATIONSET_CONTROLLER_ALLOWED_SCM_PROVIDERS", []string{}, ","), "The list of allowed scm providers. (Default: Empty = all)")
command.Flags().BoolVar(&dryRun, "dry-run", env.ParseBoolFromEnv("ARGOCD_APPLICATIONSET_CONTROLLER_DRY_RUN", false), "Enable dry run mode")
command.Flags().BoolVar(&enableProgressiveSyncs, "enable-progressive-syncs", env.ParseBoolFromEnv("ARGOCD_APPLICATIONSET_CONTROLLER_ENABLE_PROGRESSIVE_SYNCS", false), "Enable use of the experimental progressive syncs feature.")
command.Flags().BoolVar(&enableNewGitFileGlobbing, "enable-new-git-file-globbing", env.ParseBoolFromEnv("ARGOCD_APPLICATIONSET_CONTROLLER_ENABLE_NEW_GIT_FILE_GLOBBING", false), "Enable new globbing in Git files generator.")

View File

@@ -55,7 +55,6 @@ func NewCommand() *cobra.Command {
argocdRepoServerStrictTLS bool
configMapName string
secretName string
applicationNamespaces []string
)
var command = cobra.Command{
Use: "controller",
@@ -139,7 +138,7 @@ func NewCommand() *cobra.Command {
log.Infof("serving metrics on port %d", metricsPort)
log.Infof("loading configuration %d", metricsPort)
ctrl := notificationscontroller.NewController(k8sClient, dynamicClient, argocdService, namespace, applicationNamespaces, appLabelSelector, registry, secretName, configMapName)
ctrl := notificationscontroller.NewController(k8sClient, dynamicClient, argocdService, namespace, appLabelSelector, registry, secretName, configMapName)
err = ctrl.Init(ctx)
if err != nil {
return fmt.Errorf("failed to initialize controller: %w", err)
@@ -162,6 +161,5 @@ func NewCommand() *cobra.Command {
command.Flags().BoolVar(&argocdRepoServerStrictTLS, "argocd-repo-server-strict-tls", false, "Perform strict validation of TLS certificates when connecting to repo server")
command.Flags().StringVar(&configMapName, "config-map-name", "argocd-notifications-cm", "Set notifications ConfigMap name")
command.Flags().StringVar(&secretName, "secret-name", "argocd-notifications-secret", "Set notifications Secret name")
command.Flags().StringSliceVar(&applicationNamespaces, "application-namespaces", env.StringsFromEnv("ARGOCD_APPLICATION_NAMESPACES", []string{}, ","), "List of additional namespaces that this controller should send notifications for")
return &command
}

View File

@@ -26,26 +26,12 @@ import (
"github.com/argoproj/argo-cd/v2/util/io"
"github.com/argoproj/argo-cd/v2/util/localconfig"
sessionutil "github.com/argoproj/argo-cd/v2/util/session"
"github.com/argoproj/argo-cd/v2/util/templates"
)
func NewAccountCommand(clientOpts *argocdclient.ClientOptions) *cobra.Command {
var command = &cobra.Command{
Use: "account",
Short: "Manage account settings",
Example: templates.Examples(`
# List accounts
argocd account list
# Update the current user's password
argocd account update-password
# Can I sync any app?
argocd account can-i sync applications '*'
# Get User information
argocd account get-user-info
`),
Run: func(c *cobra.Command, args []string) {
c.HelpFunc()(c, args)
os.Exit(1)
@@ -157,13 +143,6 @@ func NewAccountGetUserInfoCommand(clientOpts *argocdclient.ClientOptions) *cobra
var command = &cobra.Command{
Use: "get-user-info",
Short: "Get user info",
Example: templates.Examples(`
# Get User information for the currently logged-in user (see 'argocd login')
argocd account get-user-info
# Get User information in yaml format
argocd account get-user-info -o yaml
`),
Run: func(c *cobra.Command, args []string) {
ctx := c.Context()

View File

@@ -124,7 +124,7 @@ func loadClusters(ctx context.Context, kubeClient *kubernetes.Clientset, appClie
if replicas > 0 {
distributionFunction := sharding.GetDistributionFunction(argoDB, common.DefaultShardingAlgorithm)
distributionFunction(&cluster)
cluster.Shard = pointer.Int64(int64(clusterShard))
cluster.Shard = pointer.Int64Ptr(int64(clusterShard))
log.Infof("Cluster with uid: %s will be processed by shard %d", cluster.ID, clusterShard)
}

View File

@@ -28,19 +28,10 @@ func NewDashboardCommand() *cobra.Command {
compression, err := cache.CompressionTypeFromString(compressionStr)
errors.CheckError(err)
errors.CheckError(headless.MaybeStartLocalServer(ctx, &argocdclient.ClientOptions{Core: true}, initialize.RetrieveContextIfChanged(cmd.Flag("context")), &port, &address, compression))
errors.CheckError(headless.StartLocalServer(ctx, &argocdclient.ClientOptions{Core: true}, initialize.RetrieveContextIfChanged(cmd.Flag("context")), &port, &address, compression))
println(fmt.Sprintf("Argo CD UI is available at http://%s:%d", address, port))
<-ctx.Done()
},
Example: `# Start the Argo CD Web UI locally on the default port and address
$ argocd admin dashboard
# Start the Argo CD Web UI locally on a custom port and address
$ argocd admin dashboard --port 8080 --address 127.0.0.1
# Start the Argo CD Web UI with GZip compression
$ argocd admin dashboard --redis-compress gzip
`,
}
initialize.InitCommand(cmd)
cmd.Flags().IntVar(&port, "port", common.DefaultPortAPIServer, "Listen on given port")

View File

@@ -14,7 +14,6 @@ import (
"github.com/argoproj/argo-cd/v2/util/cli"
"github.com/argoproj/argo-cd/v2/util/errors"
"github.com/argoproj/argo-cd/v2/util/io"
"github.com/argoproj/argo-cd/v2/util/templates"
"github.com/argoproj/gitops-engine/pkg/utils/kube"
"github.com/spf13/cobra"
@@ -48,17 +47,6 @@ func NewGenProjectSpecCommand() *cobra.Command {
var command = &cobra.Command{
Use: "generate-spec PROJECT",
Short: "Generate declarative config for a project",
Example: templates.Examples(`
# Generate a YAML configuration for a project named "myproject"
argocd admin projects generate-spec myproject
# Generate a JSON configuration for a project named "anotherproject" and specify an output file
argocd admin projects generate-spec anotherproject --output json --file config.json
# Generate a YAML configuration for a project named "someproject" and write it back to the input file
argocd admin projects generate-spec someproject --inline
`),
Run: func(c *cobra.Command, args []string) {
proj, err := cmdutil.ConstructAppProj(fileURL, args, opts, c)
errors.CheckError(err)

View File

@@ -50,7 +50,6 @@ import (
"github.com/argoproj/argo-cd/v2/util/grpc"
argoio "github.com/argoproj/argo-cd/v2/util/io"
"github.com/argoproj/argo-cd/v2/util/manifeststream"
"github.com/argoproj/argo-cd/v2/util/templates"
"github.com/argoproj/argo-cd/v2/util/text/label"
)
@@ -318,35 +317,6 @@ func NewApplicationGetCommand(clientOpts *argocdclient.ClientOptions) *cobra.Com
var command = &cobra.Command{
Use: "get APPNAME",
Short: "Get application details",
Example: templates.Examples(`
# Get basic details about the application "my-app" in wide format
argocd app get my-app -o wide
# Get detailed information about the application "my-app" in YAML format
argocd app get my-app -o yaml
# Get details of the application "my-app" in JSON format
argocd get my-app -o json
# Get application details and include information about the current operation
argocd app get my-app --show-operation
# Show application parameters and overrides
argocd app get my-app --show-params
# Refresh application data when retrieving
argocd app get my-app --refresh
# Perform a hard refresh, including refreshing application data and target manifests cache
argocd app get my-app --hard-refresh
# Get application details and display them in a tree format
argocd app get my-app --output tree
# Get application details and display them in a detailed tree format
argocd app get my-app --output tree=detailed
`),
Run: func(c *cobra.Command, args []string) {
ctx := c.Context()
if len(args) == 0 {
@@ -431,44 +401,6 @@ func NewApplicationLogsCommand(clientOpts *argocdclient.ClientOptions) *cobra.Co
var command = &cobra.Command{
Use: "logs APPNAME",
Short: "Get logs of application pods",
Example: templates.Examples(`
# Get logs of pods associated with the application "my-app"
argocd app logs my-app
# Get logs of pods associated with the application "my-app" in a specific resource group
argocd app logs my-app --group my-group
# Get logs of pods associated with the application "my-app" in a specific resource kind
argocd app logs my-app --kind my-kind
# Get logs of pods associated with the application "my-app" in a specific namespace
argocd app logs my-app --namespace my-namespace
# Get logs of pods associated with the application "my-app" for a specific resource name
argocd app logs my-app --name my-resource
# Stream logs in real-time for the application "my-app"
argocd app logs my-app -f
# Get the last N lines of logs for the application "my-app"
argocd app logs my-app --tail 100
# Get logs since a specified number of seconds ago
argocd app logs my-app --since-seconds 3600
# Get logs until a specified time (format: "2023-10-10T15:30:00Z")
argocd app logs my-app --until-time "2023-10-10T15:30:00Z"
# Filter logs to show only those containing a specific string
argocd app logs my-app --filter "error"
# Get logs for a specific container within the pods
argocd app logs my-app -c my-container
# Get previously terminated container logs
argocd app logs my-app -p
`),
Run: func(c *cobra.Command, args []string) {
ctx := c.Context()
@@ -524,8 +456,8 @@ func NewApplicationLogsCommand(clientOpts *argocdclient.ClientOptions) *cobra.Co
} else {
return
}
} // Done with receive message
} // Done with retry
} //Done with receive message
} //Done with retry
},
}
@@ -716,23 +648,6 @@ func NewApplicationSetCommand(clientOpts *argocdclient.ClientOptions) *cobra.Com
var command = &cobra.Command{
Use: "set APPNAME",
Short: "Set application parameters",
Example: templates.Examples(`
# Set application parameters for the application "my-app"
argocd app set my-app --parameter key1=value1 --parameter key2=value2
# Set and validate application parameters for "my-app"
argocd app set my-app --parameter key1=value1 --parameter key2=value2 --validate
# Set and override application parameters with JSON or YAML file
argocd app set my-app --from-file path/to/parameters.json
# Set and override application parameters with a parameter file
argocd app set my-app --parameter-file path/to/parameter-file.yaml
# Set application parameters and specify the namespace
argocd app set my-app --parameter key1=value1 --parameter key2=value2 --namespace my-namespace
`),
Run: func(c *cobra.Command, args []string) {
ctx := c.Context()
@@ -889,7 +804,7 @@ func unset(source *argoappv1.ApplicationSource, opts unsetOpts) (updated bool, n
for i, item := range source.Kustomize.Images {
if argoappv1.KustomizeImage(kustomizeImage).Match(item) {
updated = true
// remove i
//remove i
a := source.Kustomize.Images
copy(a[i:], a[i+1:]) // Shift a[i+1:] left one index.
a[len(a)-1] = "" // Erase last element (write zero value).
@@ -1904,7 +1819,7 @@ func NewApplicationSyncCommand(clientOpts *argocdclient.ClientOptions) *cobra.Co
Backoff: &argoappv1.Backoff{
Duration: retryBackoffDuration.String(),
MaxDuration: retryBackoffMaxDuration.String(),
Factor: pointer.Int64(retryBackoffFactor),
Factor: pointer.Int64Ptr(retryBackoffFactor),
},
}
}
@@ -2143,7 +2058,7 @@ func checkResourceStatus(watch watchOpts, healthStatus string, syncStatus string
} else if watch.degraded && watch.health {
healthCheckPassed = healthStatus == string(health.HealthStatusHealthy) ||
healthStatus == string(health.HealthStatusDegraded)
// below are good
//below are good
} else if watch.suspended && watch.health {
healthCheckPassed = healthStatus == string(health.HealthStatusHealthy) ||
healthStatus == string(health.HealthStatusSuspended)

View File

@@ -4,7 +4,6 @@ import (
"context"
"encoding/json"
"fmt"
"github.com/argoproj/argo-cd/v2/util/templates"
"os"
"strconv"
"text/tabwriter"
@@ -34,22 +33,11 @@ type DisplayedAction struct {
Disabled bool
}
var (
appActionExample = templates.Examples(`
# List all the available actions for an application
argocd app actions list APPNAME
# Run an available action for an application
argocd app actions run APPNAME ACTION --kind KIND [--resource-name RESOURCE] [--namespace NAMESPACE] [--group GROUP]
`)
)
// NewApplicationResourceActionsCommand returns a new instance of an `argocd app actions` command
func NewApplicationResourceActionsCommand(clientOpts *argocdclient.ClientOptions) *cobra.Command {
var command = &cobra.Command{
Use: "actions",
Short: "Manage Resource actions",
Example: appActionExample,
Use: "actions",
Short: "Manage Resource actions",
Run: func(c *cobra.Command, args []string) {
c.HelpFunc()(c, args)
os.Exit(1)
@@ -70,10 +58,6 @@ func NewApplicationResourceActionsListCommand(clientOpts *argocdclient.ClientOpt
var command = &cobra.Command{
Use: "list APPNAME",
Short: "Lists available actions on a resource",
Example: templates.Examples(`
# List all the available actions for an application
argocd app actions list APPNAME
`),
}
command.Run = func(c *cobra.Command, args []string) {
ctx := c.Context()
@@ -152,10 +136,6 @@ func NewApplicationResourceActionsRunCommand(clientOpts *argocdclient.ClientOpti
var command = &cobra.Command{
Use: "run APPNAME ACTION",
Short: "Runs an available action on resource(s)",
Example: templates.Examples(`
# Run an available action for an application
argocd app actions run APPNAME ACTION --kind KIND [--resource-name RESOURCE] [--namespace NAMESPACE] [--group GROUP]
`),
}
command.Flags().StringVar(&resourceName, "resource-name", "", "Name of resource")

View File

@@ -67,10 +67,6 @@ func NewApplicationSetGetCommand(clientOpts *argocdclient.ClientOptions) *cobra.
var command = &cobra.Command{
Use: "get APPSETNAME",
Short: "Get ApplicationSet details",
Example: templates.Examples(`
# Get ApplicationSets
argocd appset get APPSETNAME
`),
Run: func(c *cobra.Command, args []string) {
ctx := c.Context()

View File

@@ -15,9 +15,7 @@ func NewBcryptCmd() *cobra.Command {
)
var bcryptCmd = &cobra.Command{
Use: "bcrypt",
Short: "Generate bcrypt hash for any password",
Example: `# Generate bcrypt hash for any password
argocd account bcrypt --password YOUR_PASSWORD`,
Short: "Generate bcrypt hash for the admin password",
Run: func(cmd *cobra.Command, args []string) {
bytePassword := []byte(password)
// Hashing the password

View File

@@ -485,23 +485,6 @@ func NewClusterListCommand(clientOpts *argocdclient.ClientOptions) *cobra.Comman
errors.CheckError(fmt.Errorf("unknown output format: %s", output))
}
},
Example: `
# List Clusters in Default "Wide" Format
argocd cluster list
# List Cluster via specifing the server
argocd cluster list --server <ARGOCD_SERVER_ADDRESS>
# List Clusters in JSON Format
argocd cluster list -o json --server <ARGOCD_SERVER_ADDRESS>
# List Clusters in YAML Format
argocd cluster list -o yaml --server <ARGOCD_SERVER_ADDRESS>
# List Clusters that have been added to your Argo CD
argocd cluster list -o server <ARGOCD_SERVER_ADDRESS>
`,
}
command.Flags().StringVarP(&output, "output", "o", "wide", "Output format. One of: json|yaml|wide|server")
return command

View File

@@ -211,13 +211,6 @@ compdef _argocd argocd
Optionally, also add the following, in case you are getting errors involving compdef & compinit such as command not found: compdef:
autoload -Uz compinit
compinit
`,
Example: `# For bash
$ source <(argocd completion bash)
# For zsh
$ argocd completion zsh > _argocd
$ source _argocd
`,
Run: func(cmd *cobra.Command, args []string) {
if len(args) != 1 {

View File

@@ -22,14 +22,6 @@ func NewContextCommand(clientOpts *argocdclient.ClientOptions) *cobra.Command {
Use: "context [CONTEXT]",
Aliases: []string{"ctx"},
Short: "Switch between contexts",
Example: `# List Argo CD Contexts
argocd context
# Switch Argo CD context
argocd context cd.argoproj.io
# Delete Argo CD context
argocd context cd.argoproj.io --delete`,
Run: func(c *cobra.Command, args []string) {
localCfg, err := localconfig.ReadLocalConfig(clientOpts.ConfigPath)

View File

@@ -142,23 +142,16 @@ func testAPI(ctx context.Context, clientOpts *apiclient.ClientOptions) error {
}
defer io.Close(closer)
_, err = versionClient.Version(ctx, &empty.Empty{})
if err != nil {
return fmt.Errorf("failed to get version: %w", err)
}
return nil
return fmt.Errorf("failed to get version: %w", err)
}
// MaybeStartLocalServer allows executing command in a headless mode. If we're in core mode, starts the Argo CD API
// server on the fly and changes provided client options to use started API server port.
//
// If the clientOpts enables core mode, but the local config does not have core mode enabled, this function will
// not start the local server.
func MaybeStartLocalServer(ctx context.Context, clientOpts *apiclient.ClientOptions, ctxStr string, port *int, address *string, compression cache.RedisCompressionType) error {
// StartLocalServer allows executing command in a headless mode: on the fly starts Argo CD API server and
// changes provided client options to use started API server port
func StartLocalServer(ctx context.Context, clientOpts *apiclient.ClientOptions, ctxStr string, port *int, address *string, compression cache.RedisCompressionType) error {
flags := pflag.NewFlagSet("tmp", pflag.ContinueOnError)
clientConfig := cli.AddKubectlFlagsToSet(flags)
startInProcessAPI := clientOpts.Core
if !startInProcessAPI {
// Core mode is enabled on client options. Check the local config to see if we should start the API server.
localCfg, err := localconfig.ReadLocalConfig(clientOpts.ConfigPath)
if err != nil {
return fmt.Errorf("error reading local config: %w", err)
@@ -168,11 +161,9 @@ func MaybeStartLocalServer(ctx context.Context, clientOpts *apiclient.ClientOpti
if err != nil {
return fmt.Errorf("error resolving context: %w", err)
}
// There was a local config file, so determine whether core mode is enabled per the config file.
startInProcessAPI = configCtx.Server.Core
}
}
// If we're in core mode, start the API server on the fly.
if !startInProcessAPI {
return nil
}
@@ -252,10 +243,7 @@ func MaybeStartLocalServer(ctx context.Context, clientOpts *apiclient.ClientOpti
}
time.Sleep(time.Second)
}
if err != nil {
return fmt.Errorf("all retries failed: %w", err)
}
return nil
return fmt.Errorf("all retries failed: %w", err)
}
// NewClientOrDie creates a new API client from a set of config options, or fails fatally if the new client creation fails.
@@ -263,9 +251,7 @@ func NewClientOrDie(opts *apiclient.ClientOptions, c *cobra.Command) apiclient.C
ctx := c.Context()
ctxStr := initialize.RetrieveContextIfChanged(c.Flag("context"))
// If we're in core mode, start the API server on the fly and configure the client `opts` to use it.
// If we're not in core mode, this function call will do nothing.
err := MaybeStartLocalServer(ctx, opts, ctxStr, nil, nil, cache.RedisCompressionNone)
err := StartLocalServer(ctx, opts, ctxStr, nil, nil, cache.RedisCompressionNone)
if err != nil {
log.Fatal(err)
}

View File

@@ -18,10 +18,6 @@ func NewLogoutCommand(globalClientOpts *argocdclient.ClientOptions) *cobra.Comma
Use: "logout CONTEXT",
Short: "Log out from Argo CD",
Long: "Log out from Argo CD",
Example: `# To log out of argocd
$ argocd logout
# This can be helpful for security reasons or when you want to switch between different Argo CD contexts or accounts.
`,
Run: func(c *cobra.Command, args []string) {
if len(args) == 0 {
c.HelpFunc()(c, args)

View File

@@ -26,7 +26,6 @@ import (
"github.com/argoproj/argo-cd/v2/util/git"
"github.com/argoproj/argo-cd/v2/util/gpg"
argoio "github.com/argoproj/argo-cd/v2/util/io"
"github.com/argoproj/argo-cd/v2/util/templates"
)
type policyOpts struct {
@@ -40,19 +39,6 @@ func NewProjectCommand(clientOpts *argocdclient.ClientOptions) *cobra.Command {
var command = &cobra.Command{
Use: "proj",
Short: "Manage projects",
Example: templates.Examples(`
# List all available projects
argocd proj list
# Create a new project with name PROJECT
argocd proj create PROJECT
# Delete the project with name PROJECT
argocd proj delete PROJECT
# Edit the information on project with name PROJECT
argocd proj edit PROJECT
`),
Run: func(c *cobra.Command, args []string) {
c.HelpFunc()(c, args)
os.Exit(1)
@@ -102,13 +88,6 @@ func NewProjectCreateCommand(clientOpts *argocdclient.ClientOptions) *cobra.Comm
var command = &cobra.Command{
Use: "create PROJECT",
Short: "Create a project",
Example: templates.Examples(`
# Create a new project with name PROJECT
argocd proj create PROJECT
# Create a new project with name PROJECT from a file or URL to a kubernetes manifest
argocd proj create PROJECT -f FILE|URL
`),
Run: func(c *cobra.Command, args []string) {
ctx := c.Context()
@@ -139,13 +118,6 @@ func NewProjectSetCommand(clientOpts *argocdclient.ClientOptions) *cobra.Command
var command = &cobra.Command{
Use: "set PROJECT",
Short: "Set project parameters",
Example: templates.Examples(`
# Set project parameters with some allowed cluster resources [RES1,RES2,...] for project with name PROJECT
argocd proj set PROJECT --allow-cluster-resource [RES1,RES2,...]
# Set project parameters with some denied namespaced resources [RES1,RES2,...] for project with name PROJECT
argocd proj set PROJECT ---deny-namespaced-resource [RES1,RES2,...]
`),
Run: func(c *cobra.Command, args []string) {
ctx := c.Context()
@@ -179,10 +151,6 @@ func NewProjectAddSignatureKeyCommand(clientOpts *argocdclient.ClientOptions) *c
var command = &cobra.Command{
Use: "add-signature-key PROJECT KEY-ID",
Short: "Add GnuPG signature key to project",
Example: templates.Examples(`
# Add GnuPG signature key KEY-ID to project PROJECT
argocd proj add-signature-key PROJECT KEY-ID
`),
Run: func(c *cobra.Command, args []string) {
ctx := c.Context()
@@ -221,10 +189,6 @@ func NewProjectRemoveSignatureKeyCommand(clientOpts *argocdclient.ClientOptions)
var command = &cobra.Command{
Use: "remove-signature-key PROJECT KEY-ID",
Short: "Remove GnuPG signature key from project",
Example: templates.Examples(`
# Remove GnuPG signature key KEY-ID from project PROJECT
argocd proj remove-signature-key PROJECT KEY-ID
`),
Run: func(c *cobra.Command, args []string) {
ctx := c.Context()
@@ -275,13 +239,6 @@ func NewProjectAddDestinationCommand(clientOpts *argocdclient.ClientOptions) *co
var command = &cobra.Command{
Use: "add-destination PROJECT SERVER/NAME NAMESPACE",
Short: "Add project destination",
Example: templates.Examples(`
# Add project destination using a server URL (SERVER) in the specified namespace (NAMESPACE) on the project with name PROJECT
argocd proj add-destination PROJECT SERVER NAMESPACE
# Add project destination using a server name (NAME) in the specified namespace (NAMESPACE) on the project with name PROJECT
argocd proj add-destination PROJECT NAME NAMESPACE --name
`),
Run: func(c *cobra.Command, args []string) {
ctx := c.Context()
@@ -319,10 +276,6 @@ func NewProjectRemoveDestinationCommand(clientOpts *argocdclient.ClientOptions)
var command = &cobra.Command{
Use: "remove-destination PROJECT SERVER NAMESPACE",
Short: "Remove project destination",
Example: templates.Examples(`
# Remove the destination (SERVER) from the specified namespace (NAMESPACE) on the project with name PROJECT
argocd proj remove-destination PROJECT SERVER NAMESPACE
`),
Run: func(c *cobra.Command, args []string) {
ctx := c.Context()
@@ -367,13 +320,6 @@ func NewProjectAddOrphanedIgnoreCommand(clientOpts *argocdclient.ClientOptions)
var command = &cobra.Command{
Use: "add-orphaned-ignore PROJECT GROUP KIND",
Short: "Add a resource to orphaned ignore list",
Example: templates.Examples(`
# Add a resource of the specified GROUP and KIND to orphaned ignore list on the project with name PROJECT
argocd proj add-orphaned-ignore PROJECT GROUP KIND
# Add resources of the specified GROUP and KIND using a NAME pattern to orphaned ignore list on the project with name PROJECT
argocd proj add-orphaned-ignore PROJECT GROUP KIND --name NAME
`),
Run: func(c *cobra.Command, args []string) {
ctx := c.Context()
@@ -417,15 +363,8 @@ func NewProjectRemoveOrphanedIgnoreCommand(clientOpts *argocdclient.ClientOption
name string
)
var command = &cobra.Command{
Use: "remove-orphaned-ignore PROJECT GROUP KIND",
Use: "remove-orphaned-ignore PROJECT GROUP KIND NAME",
Short: "Remove a resource from orphaned ignore list",
Example: templates.Examples(`
# Remove a resource of the specified GROUP and KIND from orphaned ignore list on the project with name PROJECT
argocd proj remove-orphaned-ignore PROJECT GROUP KIND
# Remove resources of the specified GROUP and KIND using a NAME pattern from orphaned ignore list on the project with name PROJECT
argocd proj remove-orphaned-ignore PROJECT GROUP KIND --name NAME
`),
Run: func(c *cobra.Command, args []string) {
ctx := c.Context()
@@ -472,10 +411,6 @@ func NewProjectAddSourceCommand(clientOpts *argocdclient.ClientOptions) *cobra.C
var command = &cobra.Command{
Use: "add-source PROJECT URL",
Short: "Add project source repository",
Example: templates.Examples(`
# Add a source repository (URL) to the project with name PROJECT
argocd proj add-source PROJECT URL
`),
Run: func(c *cobra.Command, args []string) {
ctx := c.Context()
@@ -538,7 +473,7 @@ func modifyResourcesList(list *[]metav1.GroupKind, add bool, listDesc string, gr
}
}
func modifyResourceListCmd(cmdUse, cmdDesc, examples string, clientOpts *argocdclient.ClientOptions, allow bool, namespacedList bool) *cobra.Command {
func modifyResourceListCmd(cmdUse, cmdDesc string, clientOpts *argocdclient.ClientOptions, allow bool, namespacedList bool) *cobra.Command {
var (
listType string
defaultList string
@@ -549,9 +484,8 @@ func modifyResourceListCmd(cmdUse, cmdDesc, examples string, clientOpts *argocdc
defaultList = "allow"
}
var command = &cobra.Command{
Use: cmdUse,
Short: cmdDesc,
Example: templates.Examples(examples),
Use: cmdUse,
Short: cmdDesc,
Run: func(c *cobra.Command, args []string) {
ctx := c.Context()
@@ -600,44 +534,28 @@ func modifyResourceListCmd(cmdUse, cmdDesc, examples string, clientOpts *argocdc
func NewProjectAllowNamespaceResourceCommand(clientOpts *argocdclient.ClientOptions) *cobra.Command {
use := "allow-namespace-resource PROJECT GROUP KIND"
desc := "Removes a namespaced API resource from the deny list or add a namespaced API resource to the allow list"
examples := `
# Removes a namespaced API resource with specified GROUP and KIND from the deny list or add a namespaced API resource to the allow list for project PROJECT
argocd proj allow-namespace-resource PROJECT GROUP KIND
`
return modifyResourceListCmd(use, desc, examples, clientOpts, true, true)
return modifyResourceListCmd(use, desc, clientOpts, true, true)
}
// NewProjectDenyNamespaceResourceCommand returns a new instance of an `argocd proj deny-namespace-resource` command
func NewProjectDenyNamespaceResourceCommand(clientOpts *argocdclient.ClientOptions) *cobra.Command {
use := "deny-namespace-resource PROJECT GROUP KIND"
desc := "Adds a namespaced API resource to the deny list or removes a namespaced API resource from the allow list"
examples := `
# Adds a namespaced API resource with specified GROUP and KIND from the deny list or removes a namespaced API resource from the allow list for project PROJECT
argocd proj deny-namespace-resource PROJECT GROUP KIND
`
return modifyResourceListCmd(use, desc, examples, clientOpts, false, true)
return modifyResourceListCmd(use, desc, clientOpts, false, true)
}
// NewProjectDenyClusterResourceCommand returns a new instance of an `deny-cluster-resource` command
func NewProjectDenyClusterResourceCommand(clientOpts *argocdclient.ClientOptions) *cobra.Command {
use := "deny-cluster-resource PROJECT GROUP KIND"
desc := "Removes a cluster-scoped API resource from the allow list and adds it to deny list"
examples := `
# Removes a cluster-scoped API resource with specified GROUP and KIND from the allow list and adds it to deny list for project PROJECT
argocd proj deny-cluster-resource PROJECT GROUP KIND
`
return modifyResourceListCmd(use, desc, examples, clientOpts, false, false)
return modifyResourceListCmd(use, desc, clientOpts, false, false)
}
// NewProjectAllowClusterResourceCommand returns a new instance of an `argocd proj allow-cluster-resource` command
func NewProjectAllowClusterResourceCommand(clientOpts *argocdclient.ClientOptions) *cobra.Command {
use := "allow-cluster-resource PROJECT GROUP KIND"
desc := "Adds a cluster-scoped API resource to the allow list and removes it from deny list"
examples := `
# Adds a cluster-scoped API resource with specified GROUP and KIND to the allow list and removes it from deny list for project PROJECT
argocd proj allow-cluster-resource PROJECT GROUP KIND
`
return modifyResourceListCmd(use, desc, examples, clientOpts, true, false)
return modifyResourceListCmd(use, desc, clientOpts, true, false)
}
// NewProjectRemoveSourceCommand returns a new instance of an `argocd proj remove-src` command
@@ -645,10 +563,6 @@ func NewProjectRemoveSourceCommand(clientOpts *argocdclient.ClientOptions) *cobr
var command = &cobra.Command{
Use: "remove-source PROJECT URL",
Short: "Remove project source repository",
Example: templates.Examples(`
# Remove URL source repository to project PROJECT
argocd proj remove-source PROJECT URL
`),
Run: func(c *cobra.Command, args []string) {
ctx := c.Context()
@@ -689,10 +603,6 @@ func NewProjectDeleteCommand(clientOpts *argocdclient.ClientOptions) *cobra.Comm
var command = &cobra.Command{
Use: "delete PROJECT",
Short: "Delete project",
Example: templates.Examples(`
# Delete the project with name PROJECT
argocd proj delete PROJECT
`),
Run: func(c *cobra.Command, args []string) {
ctx := c.Context()
@@ -736,13 +646,6 @@ func NewProjectListCommand(clientOpts *argocdclient.ClientOptions) *cobra.Comman
var command = &cobra.Command{
Use: "list",
Short: "List projects",
Example: templates.Examples(`
# List all available projects
argocd proj list
# List all available projects in yaml format
argocd proj list -o yaml
`),
Run: func(c *cobra.Command, args []string) {
ctx := c.Context()
@@ -908,14 +811,6 @@ func NewProjectGetCommand(clientOpts *argocdclient.ClientOptions) *cobra.Command
var command = &cobra.Command{
Use: "get PROJECT",
Short: "Get project details",
Example: templates.Examples(`
# Get details from project PROJECT
argocd proj get PROJECT
# Get details from project PROJECT in yaml format
argocd proj get PROJECT -o yaml
`),
Run: func(c *cobra.Command, args []string) {
ctx := c.Context()
@@ -953,10 +848,6 @@ func NewProjectEditCommand(clientOpts *argocdclient.ClientOptions) *cobra.Comman
var command = &cobra.Command{
Use: "edit PROJECT",
Short: "Edit project",
Example: templates.Examples(`
# Edit the information on project with name PROJECT
argocd proj edit PROJECT
`),
Run: func(c *cobra.Command, args []string) {
ctx := c.Context()

View File

@@ -18,7 +18,6 @@ import (
"github.com/argoproj/argo-cd/v2/util/errors"
"github.com/argoproj/argo-cd/v2/util/io"
"github.com/argoproj/argo-cd/v2/util/jwt"
"github.com/argoproj/argo-cd/v2/util/templates"
)
const (
@@ -177,11 +176,6 @@ func NewProjectRoleDeleteCommand(clientOpts *argocdclient.ClientOptions) *cobra.
var command = &cobra.Command{
Use: "delete PROJECT ROLE-NAME",
Short: "Delete a project role",
Example: templates.Examples(`
# Delete a project role from the "my-project" project with the name "my-role".
argocd proj role delete my-project my-role
`),
Run: func(c *cobra.Command, args []string) {
ctx := c.Context()

View File

@@ -125,23 +125,6 @@ func NewProjectWindowsAddWindowCommand(clientOpts *argocdclient.ClientOptions) *
var command = &cobra.Command{
Use: "add PROJECT",
Short: "Add a sync window to a project",
Example: `# Add a 1 hour allow sync window
argocd proj windows add PROJECT \
--kind allow \
--schedule "0 22 * * *" \
--duration 1h \
--applications "*"
# Add a deny sync window with the ability to manually sync.
argocd proj windows add PROJECT \
--kind deny \
--schedule "30 10 * * *" \
--duration 30m \
--applications "prod-\\*,website" \
--namespaces "default,\\*-prod" \
--clusters "prod,staging" \
--manual-sync
`,
Run: func(c *cobra.Command, args []string) {
ctx := c.Context()
@@ -175,7 +158,7 @@ argocd proj windows add PROJECT \
return command
}
// NewProjectWindowsDeleteCommand returns a new instance of an `argocd proj windows delete` command
// NewProjectWindowsAddWindowCommand returns a new instance of an `argocd proj windows delete` command
func NewProjectWindowsDeleteCommand(clientOpts *argocdclient.ClientOptions) *cobra.Command {
var command = &cobra.Command{
Use: "delete PROJECT ID",
@@ -222,10 +205,6 @@ func NewProjectWindowsUpdateCommand(clientOpts *argocdclient.ClientOptions) *cob
Use: "update PROJECT ID",
Short: "Update a project sync window",
Long: "Update a project sync window. Requires ID which can be found by running \"argocd proj windows list PROJECT\"",
Example: `# Change a sync window's schedule
argocd proj windows update PROJECT ID \
--schedule "0 20 * * *"
`,
Run: func(c *cobra.Command, args []string) {
ctx := c.Context()

View File

@@ -84,18 +84,6 @@ func NewReloginCommand(globalClientOpts *argocdclient.ClientOptions) *cobra.Comm
errors.CheckError(err)
fmt.Printf("Context '%s' updated\n", localCfg.CurrentContext)
},
Example: `
# Reinitiates the login with previous contexts
argocd relogin
# Reinitiates the login with password
argocd relogin --password YOUR_PASSWORD
# Configure direct access using Kubernetes API server
argocd login cd.argoproj.io --core
# If user logged in with - "argocd login cd.argoproj.io" with sso login
# The command - "argocd relogin" will Reinitiates SSO login and updates the server context`,
}
command.Flags().StringVar(&password, "password", "", "The password of an account to authenticate")
command.Flags().IntVar(&ssoPort, "sso-port", DefaultSSOLocalPort, "Port to run local OAuth2 login application")

View File

@@ -29,19 +29,6 @@ func NewRepoCommand(clientOpts *argocdclient.ClientOptions) *cobra.Command {
c.HelpFunc()(c, args)
os.Exit(1)
},
Example: `
# Add git repository connection parameters
argocd repo add git@git.example.com:repos/repo
# Get a Configured Repository by URL
argocd repo get https://github.com/yourusername/your-repo.git
# List Configured Repositories
argocd repo list
# Remove Repository Credentials
argocd repo rm https://github.com/yourusername/your-repo.git
`,
}
command.AddCommand(NewRepoAddCommand(clientOpts))

View File

@@ -17,7 +17,6 @@ import (
"github.com/argoproj/argo-cd/v2/util/errors"
"github.com/argoproj/argo-cd/v2/util/git"
"github.com/argoproj/argo-cd/v2/util/io"
"github.com/argoproj/argo-cd/v2/util/templates"
)
// NewRepoCredsCommand returns a new instance of an `argocd repocreds` command
@@ -25,16 +24,6 @@ func NewRepoCredsCommand(clientOpts *argocdclient.ClientOptions) *cobra.Command
var command = &cobra.Command{
Use: "repocreds",
Short: "Manage repository connection parameters",
Example: templates.Examples(`
# Add credentials with user/pass authentication to use for all repositories under the specified URL
argocd repocreds add URL --username USERNAME --password PASSWORD
# List all the configured repository credentials
argocd repocreds list
# Remove credentials for the repositories with speficied URL
argocd repocreds rm URL
`),
Run: func(c *cobra.Command, args []string) {
c.HelpFunc()(c, args)
os.Exit(1)
@@ -195,10 +184,6 @@ func NewRepoCredsRemoveCommand(clientOpts *argocdclient.ClientOptions) *cobra.Co
var command = &cobra.Command{
Use: "rm CREDSURL",
Short: "Remove repository credentials",
Example: templates.Examples(`
# Remove credentials for the repositories with URL https://git.example.com/repos
argocd repocreds rm https://git.example.com/repos/
`),
Run: func(c *cobra.Command, args []string) {
ctx := c.Context()
@@ -246,19 +231,6 @@ func NewRepoCredsListCommand(clientOpts *argocdclient.ClientOptions) *cobra.Comm
var command = &cobra.Command{
Use: "list",
Short: "List configured repository credentials",
Example: templates.Examples(`
# List all repo urls
argocd repocreds list
# List all repo urls in json format
argocd repocreds list -o json
# List all repo urls in yaml format
argocd repocreds list -o yaml
# List all repo urls in url format
argocd repocreds list -o url
`),
Run: func(c *cobra.Command, args []string) {
ctx := c.Context()

View File

@@ -295,7 +295,7 @@ func SetAppSpecOptions(flags *pflag.FlagSet, spec *argoappv1.ApplicationSpec, ap
Backoff: &argoappv1.Backoff{
Duration: appOpts.retryBackoffDuration.String(),
MaxDuration: appOpts.retryBackoffMaxDuration.String(),
Factor: pointer.Int64(appOpts.retryBackoffFactor),
Factor: pointer.Int64Ptr(appOpts.retryBackoffFactor),
},
}
} else if appOpts.retryLimit == 0 {

View File

@@ -115,7 +115,7 @@ func GetOrphanedResourcesSettings(flagSet *pflag.FlagSet, opts ProjectOpts) *v1a
if opts.orphanedResourcesEnabled || warnChanged {
settings := v1alpha1.OrphanedResourcesMonitorSettings{}
if warnChanged {
settings.Warn = pointer.Bool(opts.orphanedResourcesWarn)
settings.Warn = pointer.BoolPtr(opts.orphanedResourcesWarn)
}
return &settings
}

View File

@@ -65,7 +65,7 @@ func NewServer(initConstants plugin.CMPServerInitConstants) (*ArgoCDCMPServer, e
grpc.MaxSendMsgSize(apiclient.MaxGRPCMessageSize),
grpc.KeepaliveEnforcementPolicy(
keepalive.EnforcementPolicy{
MinTime: common.GetGRPCKeepAliveEnforcementMinimum(),
MinTime: common.GRPCKeepAliveEnforcementMinimum,
},
),
}

View File

@@ -258,8 +258,6 @@ const (
EnvRedisName = "ARGOCD_REDIS_NAME"
// EnvRedisHaProxyName is the name of the Argo CD Redis HA proxy component, as specified by the value under the LabelKeyAppName label key.
EnvRedisHaProxyName = "ARGOCD_REDIS_HAPROXY_NAME"
// EnvGRPCKeepAliveMin defines the GRPCKeepAliveEnforcementMinimum, used in the grpc.KeepaliveEnforcementPolicy. Expects a "Duration" format (e.g. 10s).
EnvGRPCKeepAliveMin = "ARGOCD_GRPC_KEEP_ALIVE_MIN"
)
// Config Management Plugin related constants
@@ -353,25 +351,10 @@ const (
// gRPC settings
const (
defaultGRPCKeepAliveEnforcementMinimum = 10 * time.Second
)
func GetGRPCKeepAliveEnforcementMinimum() time.Duration {
if GRPCKeepAliveMinStr := os.Getenv(EnvGRPCKeepAliveMin); GRPCKeepAliveMinStr != "" {
GRPCKeepAliveMin, err := time.ParseDuration(GRPCKeepAliveMinStr)
if err != nil {
logrus.Warnf("invalid env var value for %s: cannot parse: %s. Default value %s will be used.", EnvGRPCKeepAliveMin, err, defaultGRPCKeepAliveEnforcementMinimum)
return defaultGRPCKeepAliveEnforcementMinimum
}
return GRPCKeepAliveMin
}
return defaultGRPCKeepAliveEnforcementMinimum
}
func GetGRPCKeepAliveTime() time.Duration {
GRPCKeepAliveEnforcementMinimum = 10 * time.Second
// GRPCKeepAliveTime is 2x enforcement minimum to ensure network jitter does not introduce ENHANCE_YOUR_CALM errors
return 2 * GetGRPCKeepAliveEnforcementMinimum()
}
GRPCKeepAliveTime = 2 * GRPCKeepAliveEnforcementMinimum
)
// Security severity logging
const (

View File

@@ -1,46 +0,0 @@
package common
import (
"fmt"
"os"
"testing"
"time"
"github.com/stretchr/testify/assert"
)
// Test env var not set for EnvGRPCKeepAliveMin
func Test_GRPCKeepAliveMinNotSet(t *testing.T) {
grpcKeepAliveMin := GetGRPCKeepAliveEnforcementMinimum()
grpcKeepAliveExpectedMin := defaultGRPCKeepAliveEnforcementMinimum
assert.Equal(t, grpcKeepAliveExpectedMin, grpcKeepAliveMin)
grpcKeepAliveTime := GetGRPCKeepAliveTime()
assert.Equal(t, 2*grpcKeepAliveExpectedMin, grpcKeepAliveTime)
}
// Test valid env var set for EnvGRPCKeepAliveMin
func Test_GRPCKeepAliveMinIsSet(t *testing.T) {
numSeconds := 15
os.Setenv(EnvGRPCKeepAliveMin, fmt.Sprintf("%ds", numSeconds))
grpcKeepAliveMin := GetGRPCKeepAliveEnforcementMinimum()
grpcKeepAliveExpectedMin := time.Duration(numSeconds) * time.Second
assert.Equal(t, grpcKeepAliveExpectedMin, grpcKeepAliveMin)
grpcKeepAliveTime := GetGRPCKeepAliveTime()
assert.Equal(t, 2*grpcKeepAliveExpectedMin, grpcKeepAliveTime)
}
// Test invalid env var set for EnvGRPCKeepAliveMin
func Test_GRPCKeepAliveMinIncorrectlySet(t *testing.T) {
numSeconds := 15
os.Setenv(EnvGRPCKeepAliveMin, fmt.Sprintf("%d", numSeconds))
grpcKeepAliveMin := GetGRPCKeepAliveEnforcementMinimum()
grpcKeepAliveExpectedMin := defaultGRPCKeepAliveEnforcementMinimum
assert.Equal(t, grpcKeepAliveExpectedMin, grpcKeepAliveMin)
grpcKeepAliveTime := GetGRPCKeepAliveTime()
assert.Equal(t, 2*grpcKeepAliveExpectedMin, grpcKeepAliveTime)
}

View File

@@ -14,7 +14,6 @@ import (
"sync"
"time"
"github.com/argoproj/argo-cd/v2/pkg/ratelimiter"
clustercache "github.com/argoproj/gitops-engine/pkg/cache"
"github.com/argoproj/gitops-engine/pkg/diff"
"github.com/argoproj/gitops-engine/pkg/health"
@@ -56,8 +55,6 @@ import (
argodiff "github.com/argoproj/argo-cd/v2/util/argo/diff"
"github.com/argoproj/argo-cd/v2/util/env"
kubeerrors "k8s.io/apimachinery/pkg/api/errors"
appstatecache "github.com/argoproj/argo-cd/v2/util/cache/appstate"
"github.com/argoproj/argo-cd/v2/util/db"
"github.com/argoproj/argo-cd/v2/util/errors"
@@ -65,6 +62,7 @@ import (
"github.com/argoproj/argo-cd/v2/util/helm"
logutils "github.com/argoproj/argo-cd/v2/util/log"
settings_util "github.com/argoproj/argo-cd/v2/util/settings"
kubeerrors "k8s.io/apimachinery/pkg/api/errors"
)
const (
@@ -149,14 +147,9 @@ func NewApplicationController(
persistResourceHealth bool,
clusterFilter func(cluster *appv1.Cluster) bool,
applicationNamespaces []string,
rateLimiterConfig *ratelimiter.AppControllerRateLimiterConfig,
) (*ApplicationController, error) {
log.Infof("appResyncPeriod=%v, appHardResyncPeriod=%v", appResyncPeriod, appHardResyncPeriod)
db := db.NewDB(namespace, settingsMgr, kubeClientset)
if rateLimiterConfig == nil {
rateLimiterConfig = ratelimiter.GetDefaultAppRateLimiterConfig()
log.Info("Using default workqueue rate limiter config")
}
ctrl := ApplicationController{
cache: argoCache,
namespace: namespace,
@@ -164,10 +157,10 @@ func NewApplicationController(
kubectl: kubectl,
applicationClientset: applicationClientset,
repoClientset: repoClientset,
appRefreshQueue: workqueue.NewNamedRateLimitingQueue(ratelimiter.NewCustomAppControllerRateLimiter(rateLimiterConfig), "app_reconciliation_queue"),
appOperationQueue: workqueue.NewNamedRateLimitingQueue(ratelimiter.NewCustomAppControllerRateLimiter(rateLimiterConfig), "app_operation_processing_queue"),
projectRefreshQueue: workqueue.NewNamedRateLimitingQueue(ratelimiter.NewCustomAppControllerRateLimiter(rateLimiterConfig), "project_reconciliation_queue"),
appComparisonTypeRefreshQueue: workqueue.NewRateLimitingQueue(ratelimiter.NewCustomAppControllerRateLimiter(rateLimiterConfig)),
appRefreshQueue: workqueue.NewNamedRateLimitingQueue(workqueue.DefaultControllerRateLimiter(), "app_reconciliation_queue"),
appOperationQueue: workqueue.NewNamedRateLimitingQueue(workqueue.DefaultControllerRateLimiter(), "app_operation_processing_queue"),
projectRefreshQueue: workqueue.NewNamedRateLimitingQueue(workqueue.DefaultControllerRateLimiter(), "project_reconciliation_queue"),
appComparisonTypeRefreshQueue: workqueue.NewRateLimitingQueue(workqueue.DefaultControllerRateLimiter()),
db: db,
statusRefreshTimeout: appResyncPeriod,
statusHardRefreshTimeout: appHardResyncPeriod,
@@ -187,11 +180,10 @@ func NewApplicationController(
appInformer, appLister := ctrl.newApplicationInformerAndLister()
indexers := cache.Indexers{cache.NamespaceIndex: cache.MetaNamespaceIndexFunc}
projInformer := v1alpha1.NewAppProjectInformer(applicationClientset, namespace, appResyncPeriod, indexers)
var err error
_, err = projInformer.AddEventHandler(cache.ResourceEventHandlerFuncs{
projInformer.AddEventHandler(cache.ResourceEventHandlerFuncs{
AddFunc: func(obj interface{}) {
if key, err := cache.MetaNamespaceKeyFunc(obj); err == nil {
ctrl.projectRefreshQueue.AddRateLimited(key)
ctrl.projectRefreshQueue.Add(key)
if projMeta, ok := obj.(metav1.Object); ok {
ctrl.InvalidateProjectsCache(projMeta.GetName())
}
@@ -200,7 +192,7 @@ func NewApplicationController(
},
UpdateFunc: func(old, new interface{}) {
if key, err := cache.MetaNamespaceKeyFunc(new); err == nil {
ctrl.projectRefreshQueue.AddRateLimited(key)
ctrl.projectRefreshQueue.Add(key)
if projMeta, ok := new.(metav1.Object); ok {
ctrl.InvalidateProjectsCache(projMeta.GetName())
}
@@ -208,7 +200,6 @@ func NewApplicationController(
},
DeleteFunc: func(obj interface{}) {
if key, err := cache.DeletionHandlingMetaNamespaceKeyFunc(obj); err == nil {
// immediately push to queue for deletes
ctrl.projectRefreshQueue.Add(key)
if projMeta, ok := obj.(metav1.Object); ok {
ctrl.InvalidateProjectsCache(projMeta.GetName())
@@ -216,9 +207,6 @@ func NewApplicationController(
}
},
})
if err != nil {
return nil, err
}
factory := informers.NewSharedInformerFactoryWithOptions(ctrl.kubeClientset, defaultDeploymentInformerResyncDuration, informers.WithNamespace(settingsMgr.GetNamespace()))
deploymentInformer := factory.Apps().V1().Deployments()
@@ -246,7 +234,7 @@ func NewApplicationController(
}
metricsAddr := fmt.Sprintf("0.0.0.0:%d", metricsPort)
var err error
ctrl.metricsServer, err = metrics.NewMetricsServer(metricsAddr, appLister, ctrl.canProcessApp, readinessHealthCheck, metricsApplicationLabels)
if err != nil {
return nil, err
@@ -822,8 +810,8 @@ func (ctrl *ApplicationController) requestAppRefresh(appName string, compareWith
ctrl.appRefreshQueue.AddAfter(key, *after)
ctrl.appOperationQueue.AddAfter(key, *after)
} else {
ctrl.appRefreshQueue.AddRateLimited(key)
ctrl.appOperationQueue.AddRateLimited(key)
ctrl.appRefreshQueue.Add(key)
ctrl.appOperationQueue.Add(key)
}
}
}
@@ -1405,22 +1393,20 @@ func (ctrl *ApplicationController) processAppRefreshQueueItem() (processNext boo
return
}
app := origApp.DeepCopy()
logCtx := log.WithFields(log.Fields{
"application": app.QualifiedName(),
"level": comparisonLevel,
"dest-server": origApp.Spec.Destination.Server,
"dest-name": origApp.Spec.Destination.Name,
"dest-namespace": origApp.Spec.Destination.Namespace,
})
logCtx := log.WithFields(log.Fields{"application": app.QualifiedName()})
startTime := time.Now()
defer func() {
reconcileDuration := time.Since(startTime)
ctrl.metricsServer.IncReconcile(origApp, reconcileDuration)
logCtx.WithFields(log.Fields{
"time_ms": reconcileDuration.Milliseconds(),
"patch_ms": patchMs.Milliseconds(),
"setop_ms": setOpMs.Milliseconds(),
"time_ms": reconcileDuration.Milliseconds(),
"patch_ms": patchMs.Milliseconds(),
"setop_ms": setOpMs.Milliseconds(),
"level": comparisonLevel,
"dest-server": origApp.Spec.Destination.Server,
"dest-name": origApp.Spec.Destination.Name,
"dest-namespace": origApp.Spec.Destination.Namespace,
}).Info("Reconciliation completed")
}()
@@ -1580,7 +1566,7 @@ func (ctrl *ApplicationController) needRefreshAppStatus(app *appv1.Application,
} else if hardExpired || softExpired {
// The commented line below mysteriously crashes if app.Status.ReconciledAt is nil
// reason = fmt.Sprintf("comparison expired. reconciledAt: %v, expiry: %v", app.Status.ReconciledAt, statusRefreshTimeout)
// TODO: find existing Golang bug or create a new one
//TODO: find existing Golang bug or create a new one
reconciledAtStr := "never"
if app.Status.ReconciledAt != nil {
reconciledAtStr = app.Status.ReconciledAt.String()
@@ -1990,7 +1976,7 @@ func (ctrl *ApplicationController) newApplicationInformerAndLister() (cache.Shar
},
)
lister := applisters.NewApplicationLister(informer.GetIndexer())
_, err := informer.AddEventHandler(
informer.AddEventHandler(
cache.ResourceEventHandlerFuncs{
AddFunc: func(obj interface{}) {
if !ctrl.canProcessApp(obj) {
@@ -1998,8 +1984,8 @@ func (ctrl *ApplicationController) newApplicationInformerAndLister() (cache.Shar
}
key, err := cache.MetaNamespaceKeyFunc(obj)
if err == nil {
ctrl.appRefreshQueue.AddRateLimited(key)
ctrl.appOperationQueue.AddRateLimited(key)
ctrl.appRefreshQueue.Add(key)
ctrl.appOperationQueue.Add(key)
}
},
UpdateFunc: func(old, new interface{}) {
@@ -2019,7 +2005,7 @@ func (ctrl *ApplicationController) newApplicationInformerAndLister() (cache.Shar
compareWith = CompareWithLatest.Pointer()
}
ctrl.requestAppRefresh(newApp.QualifiedName(), compareWith, nil)
ctrl.appOperationQueue.AddRateLimited(key)
ctrl.appOperationQueue.Add(key)
},
DeleteFunc: func(obj interface{}) {
if !ctrl.canProcessApp(obj) {
@@ -2029,15 +2015,11 @@ func (ctrl *ApplicationController) newApplicationInformerAndLister() (cache.Shar
// key function.
key, err := cache.DeletionHandlingMetaNamespaceKeyFunc(obj)
if err == nil {
// for deletes, we immediately add to the refresh queue
ctrl.appRefreshQueue.Add(key)
}
},
},
)
if err != nil {
return nil, nil
}
return informer, lister
}

View File

@@ -123,7 +123,6 @@ func newFakeController(data *fakeData) *ApplicationController {
true,
nil,
data.applicationNamespaces,
nil,
)
if err != nil {
panic(err)
@@ -794,7 +793,7 @@ func TestNormalizeApplication(t *testing.T) {
// Verify we normalize the app because project is missing
ctrl := newFakeController(&data)
key, _ := cache.MetaNamespaceKeyFunc(app)
ctrl.appRefreshQueue.AddRateLimited(key)
ctrl.appRefreshQueue.Add(key)
fakeAppCs := ctrl.applicationClientset.(*appclientset.Clientset)
fakeAppCs.ReactionChain = nil
normalized := false
@@ -816,7 +815,7 @@ func TestNormalizeApplication(t *testing.T) {
data.apps[0] = app
ctrl := newFakeController(&data)
key, _ := cache.MetaNamespaceKeyFunc(app)
ctrl.appRefreshQueue.AddRateLimited(key)
ctrl.appRefreshQueue.Add(key)
fakeAppCs := ctrl.applicationClientset.(*appclientset.Clientset)
fakeAppCs.ReactionChain = nil
normalized := false
@@ -1279,7 +1278,7 @@ func TestUpdateReconciledAt(t *testing.T) {
t.Run("UpdatedOnFullReconciliation", func(t *testing.T) {
receivedPatch = map[string]interface{}{}
ctrl.requestAppRefresh(app.Name, CompareWithLatest.Pointer(), nil)
ctrl.appRefreshQueue.AddRateLimited(key)
ctrl.appRefreshQueue.Add(key)
ctrl.processAppRefreshQueueItem()
@@ -1294,7 +1293,7 @@ func TestUpdateReconciledAt(t *testing.T) {
t.Run("NotUpdatedOnPartialReconciliation", func(t *testing.T) {
receivedPatch = map[string]interface{}{}
ctrl.appRefreshQueue.AddRateLimited(key)
ctrl.appRefreshQueue.Add(key)
ctrl.requestAppRefresh(app.Name, CompareWithRecent.Pointer(), nil)
ctrl.processAppRefreshQueueItem()
@@ -1324,7 +1323,7 @@ func TestProjectErrorToCondition(t *testing.T) {
managedLiveObjs: make(map[kube.ResourceKey]*unstructured.Unstructured),
})
key, _ := cache.MetaNamespaceKeyFunc(app)
ctrl.appRefreshQueue.AddRateLimited(key)
ctrl.appRefreshQueue.Add(key)
ctrl.requestAppRefresh(app.Name, CompareWithRecent.Pointer(), nil)
ctrl.processAppRefreshQueueItem()

View File

@@ -120,7 +120,7 @@ func TestHandleDeleteEvent_CacheDeadlock(t *testing.T) {
}
fakeClient := fake.NewSimpleClientset()
settingsMgr := argosettings.NewSettingsManager(context.TODO(), fakeClient, "argocd")
liveStateCacheLock := sync.RWMutex{}
externalLockRef := sync.RWMutex{}
gitopsEngineClusterCache := &mocks.ClusterCache{}
clustersCache := liveStateCache{
clusters: map[string]cache.ClusterCache{
@@ -132,14 +132,11 @@ func TestHandleDeleteEvent_CacheDeadlock(t *testing.T) {
settingsMgr: settingsMgr,
// Set the lock here so we can reference it later
// nolint We need to overwrite here to have access to the lock
lock: liveStateCacheLock,
lock: externalLockRef,
}
channel := make(chan string)
// Mocked lock held by the gitops-engine cluster cache
gitopsEngineClusterCacheLock := sync.Mutex{}
// Ensure completion of both EnsureSynced and Invalidate
ensureSyncedCompleted := sync.Mutex{}
invalidateCompleted := sync.Mutex{}
mockMutex := sync.RWMutex{}
// Locks to force trigger condition during test
// Condition order:
// EnsuredSynced -> Locks gitops-engine
@@ -147,39 +144,40 @@ func TestHandleDeleteEvent_CacheDeadlock(t *testing.T) {
// EnsureSynced via sync, newResource, populateResourceInfoHandler -> attempts to Lock liveStateCache
// handleDeleteEvent via cluster.Invalidate -> attempts to Lock gitops-engine
handleDeleteWasCalled := sync.Mutex{}
engineHoldsEngineLock := sync.Mutex{}
ensureSyncedCompleted.Lock()
invalidateCompleted.Lock()
engineHoldsLock := sync.Mutex{}
handleDeleteWasCalled.Lock()
engineHoldsEngineLock.Lock()
engineHoldsLock.Lock()
gitopsEngineClusterCache.On("EnsureSynced").Run(func(args mock.Arguments) {
gitopsEngineClusterCacheLock.Lock()
t.Log("EnsureSynced: Engine has engine lock")
engineHoldsEngineLock.Unlock()
defer gitopsEngineClusterCacheLock.Unlock()
// Wait until handleDeleteEvent holds the liveStateCache lock
// Held by EnsureSync calling into sync and watchEvents
mockMutex.Lock()
defer mockMutex.Unlock()
// Continue Execution of timer func
engineHoldsLock.Unlock()
// Wait for handleDeleteEvent to be called triggering the lock
// on the liveStateCache
handleDeleteWasCalled.Lock()
// Try and obtain the liveStateCache lock
clustersCache.lock.Lock()
t.Log("EnsureSynced: Engine has LiveStateCache lock")
clustersCache.lock.Unlock()
ensureSyncedCompleted.Unlock()
}).Return(nil).Once()
gitopsEngineClusterCache.On("Invalidate").Run(func(args mock.Arguments) {
// Allow EnsureSynced to continue now that we're in the deadlock condition
t.Logf("handleDelete was called, EnsureSynced continuing...")
handleDeleteWasCalled.Unlock()
// Wait until gitops engine holds the gitops lock
// This prevents timing issues if we reach this point before EnsureSynced has obtained the lock
engineHoldsEngineLock.Lock()
t.Log("Invalidate: Engine has engine lock")
engineHoldsEngineLock.Unlock()
// Lock engine lock
gitopsEngineClusterCacheLock.Lock()
t.Log("Invalidate: Invalidate has engine lock")
gitopsEngineClusterCacheLock.Unlock()
invalidateCompleted.Unlock()
// Try and obtain the lock on the liveStateCache
alreadyFailed := !externalLockRef.TryLock()
if alreadyFailed {
channel <- "DEADLOCKED -- EnsureSynced could not obtain lock on liveStateCache"
return
}
externalLockRef.Lock()
t.Logf("EnsureSynce was able to lock liveStateCache")
externalLockRef.Unlock()
}).Return(nil).Once()
gitopsEngineClusterCache.On("Invalidate").Run(func(args mock.Arguments) {
// If deadlock is fixed should be able to acquire lock here
alreadyFailed := !mockMutex.TryLock()
if alreadyFailed {
channel <- "DEADLOCKED -- Invalidate could not obtain lock on gitops-engine"
return
}
mockMutex.Lock()
t.Logf("Invalidate was able to lock gitops-engine cache")
mockMutex.Unlock()
}).Return()
go func() {
// Start the gitops-engine lock holds
@@ -189,14 +187,14 @@ func TestHandleDeleteEvent_CacheDeadlock(t *testing.T) {
assert.Fail(t, err.Error())
}
}()
// Wait for EnsureSynced to grab the lock for gitops-engine
engineHoldsLock.Lock()
t.Log("EnsureSynced has obtained lock on gitops-engine")
engineHoldsLock.Unlock()
// Run in background
go clustersCache.handleDeleteEvent(testCluster.Server)
// Allow execution to continue on clusters cache call to trigger lock
ensureSyncedCompleted.Lock()
invalidateCompleted.Lock()
t.Log("Competing functions were able to obtain locks")
invalidateCompleted.Unlock()
ensureSyncedCompleted.Unlock()
handleDeleteWasCalled.Unlock()
channel <- "PASSED"
}()
select {

View File

@@ -37,16 +37,6 @@ func populateNodeInfo(un *unstructured.Unstructured, res *ResourceInfo, customLa
}
}
}
for k, v := range un.GetAnnotations() {
if strings.HasPrefix(k, common.AnnotationKeyLinkPrefix) {
if res.NetworkingInfo == nil {
res.NetworkingInfo = &v1alpha1.ResourceNetworkingInfo{}
}
res.NetworkingInfo.ExternalURLs = append(res.NetworkingInfo.ExternalURLs, v)
}
}
switch gvk.Group {
case "":
switch gvk.Kind {
@@ -68,6 +58,15 @@ func populateNodeInfo(un *unstructured.Unstructured, res *ResourceInfo, customLa
populateIstioVirtualServiceInfo(un, res)
}
}
for k, v := range un.GetAnnotations() {
if strings.HasPrefix(k, common.AnnotationKeyLinkPrefix) {
if res.NetworkingInfo == nil {
res.NetworkingInfo = &v1alpha1.ResourceNetworkingInfo{}
}
res.NetworkingInfo.ExternalURLs = append(res.NetworkingInfo.ExternalURLs, v)
}
}
}
func getIngress(un *unstructured.Unstructured) []v1.LoadBalancerIngress {
@@ -94,13 +93,7 @@ func populateServiceInfo(un *unstructured.Unstructured, res *ResourceInfo) {
if serviceType, ok, err := unstructured.NestedString(un.Object, "spec", "type"); ok && err == nil && serviceType == string(v1.ServiceTypeLoadBalancer) {
ingress = getIngress(un)
}
var urls []string
if res.NetworkingInfo != nil {
urls = res.NetworkingInfo.ExternalURLs
}
res.NetworkingInfo = &v1alpha1.ResourceNetworkingInfo{TargetLabels: targetLabels, Ingress: ingress, ExternalURLs: urls}
res.NetworkingInfo = &v1alpha1.ResourceNetworkingInfo{TargetLabels: targetLabels, Ingress: ingress}
}
func getServiceName(backend map[string]interface{}, gvk schema.GroupVersionKind) (string, error) {
@@ -270,12 +263,7 @@ func populateIstioVirtualServiceInfo(un *unstructured.Unstructured, res *Resourc
targets = append(targets, target)
}
var urls []string
if res.NetworkingInfo != nil {
urls = res.NetworkingInfo.ExternalURLs
}
res.NetworkingInfo = &v1alpha1.ResourceNetworkingInfo{TargetRefs: targets, ExternalURLs: urls}
res.NetworkingInfo = &v1alpha1.ResourceNetworkingInfo{TargetRefs: targets}
}
func populatePodInfo(un *unstructured.Unstructured, res *ResourceInfo) {
@@ -386,13 +374,7 @@ func populatePodInfo(un *unstructured.Unstructured, res *ResourceInfo) {
if restarts > 0 {
res.Info = append(res.Info, v1alpha1.InfoItem{Name: "Restart Count", Value: fmt.Sprintf("%d", restarts)})
}
var urls []string
if res.NetworkingInfo != nil {
urls = res.NetworkingInfo.ExternalURLs
}
res.NetworkingInfo = &v1alpha1.ResourceNetworkingInfo{Labels: un.GetLabels(), ExternalURLs: urls}
res.NetworkingInfo = &v1alpha1.ResourceNetworkingInfo{Labels: un.GetLabels()}
}
func populateHostNodeInfo(un *unstructured.Unstructured, res *ResourceInfo) {

View File

@@ -406,7 +406,7 @@ func TestGetLinkAnnotatedIngressInfo(t *testing.T) {
Kind: kube.ServiceKind,
Name: "helm-guestbook",
}},
ExternalURLs: []string{"http://my-grafana.com/ingress-link", "https://helm-guestbook.com/"},
ExternalURLs: []string{"https://helm-guestbook.com/", "http://my-grafana.com/ingress-link"},
}, info.NetworkingInfo)
}

View File

@@ -391,7 +391,6 @@ func (m *appStateManager) CompareAppState(app *v1alpha1.Application, project *v1
now := metav1.Now()
var manifestInfos []*apiclient.ManifestResponse
targetNsExists := false
if len(localManifests) == 0 {
// If the length of revisions is not same as the length of sources,
@@ -454,13 +453,6 @@ func (m *appStateManager) CompareAppState(app *v1alpha1.Application, project *v1
LastTransitionTime: &now,
})
}
// If we reach this path, this means that a namespace has been both defined in Git, as well in the
// application's managedNamespaceMetadata. We want to ensure that this manifest is the one being used instead
// of what is present in managedNamespaceMetadata.
if isManagedNamespace(targetObj, app) {
targetNsExists = true
}
}
ts.AddCheckpoint("dedup_ms")
@@ -519,10 +511,7 @@ func (m *appStateManager) CompareAppState(app *v1alpha1.Application, project *v1
// entry in source control. In order for the namespace not to risk being pruned, we'll need to generate a
// namespace which we can compare the live namespace with. For that, we'll do the same as is done in
// gitops-engine, the difference here being that we create a managed namespace which is only used for comparison.
//
// targetNsExists == true implies that it already exists as a target, so no need to add the namespace to the
// targetObjs array.
if isManagedNamespace(liveObj, app) && !targetNsExists {
if isManagedNamespace(liveObj, app) {
nsSpec := &v1.Namespace{TypeMeta: metav1.TypeMeta{APIVersion: "v1", Kind: kubeutil.NamespaceKind}, ObjectMeta: metav1.ObjectMeta{Name: liveObj.GetName()}}
managedNs, err := kubeutil.ToUnstructured(nsSpec)

Binary file not shown.

Before

Width:  |  Height:  |  Size: 56 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 126 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 114 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 49 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 48 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 74 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 68 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 7.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 32 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 47 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 55 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 17 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 67 KiB

View File

@@ -66,7 +66,7 @@ make builder-image IMAGE_NAMESPACE=argoproj IMAGE_TAG=v1.0.0
## Public CD
Every commit to master is built and published to `ghcr.io/argoproj/argo-cd/argocd:<version>-<short-sha>`. The list of images is available at
[https://github.com/argoproj/argo-cd/packages](https://github.com/argoproj/argo-cd/packages).
https://github.com/argoproj/argo-cd/packages.
!!! note
GitHub docker registry [requires](https://github.community/t5/GitHub-Actions/docker-pull-from-public-GitHub-Package-Registry-fail-with-quot/m-p/32888#M1294) authentication to read

View File

@@ -205,11 +205,10 @@ you should edit your `~/.kube/config` and modify the `server` option to point to
4. Finally, so that you don't have to keep updating your kube-config whenever you spin up a new k3d cluster, add `--api-port $IP:6550` to your **k3d cluster create** command, where $IP is the value from step 1. An example command is provided here:
```
k3d cluster create my-cluster --wait --k3s-arg '--disable=traefik@server:*' --api-port $IP:6550 -p 443:443@loadbalancer
k3d cluster create my-cluster --wait --k3s-server-arg '--disable=traefik' --api-port $IP:6550 -p 443:443@loadbalancer
```
!!!note
For k3d versions less than v5.0.0, the example command flags `--k3s-arg` and `'--disable=traefik@server:*'` should change to `--k3s-server-arg` and `'--disable=traefik'`, respectively.
Starting from k3d v5.0.0 the example command flags `--k3s-server-arg` and `'--disable=traefik'` would have to be changed to `--k3s-arg` and `'--disable=traefik@server:*'`, respectively.
## The development cycle

View File

@@ -20,7 +20,7 @@ Some manual steps will need to be performed by the Argo CD administrator in orde
### Cluster-scoped Argo CD installation
This feature can only be enabled and used when your Argo CD is installed as a cluster-wide instance, so it has permissions to list and manipulate resources on a cluster scope. It will not work with an Argo CD installed in namespace-scoped mode.
This feature can only be enabled and used when your Argo CD is installed as a cluster-wide instance, so it has permissions to list and manipulate resources on a cluster scope. It will *not* work with an Argo CD installed in namespace-scoped mode.
### Switch resource tracking method
@@ -71,8 +71,6 @@ We supply a `ClusterRole` and `ClusterRoleBinding` suitable for this purpose in
kubectl apply -k examples/k8s-rbac/argocd-server-applications/
```
`argocd-notifications-controller-rbac-clusterrole.yaml` and `argocd-notifications-controller-rbac-clusterrolebinding.yaml` are used to support notifications controller to notify apps in all namespaces.
!!! note
At some later point in time, we may make this cluster role part of the default installation manifests.

View File

@@ -0,0 +1,9 @@
# Add support for self-signed TLS / Certificates for Gitlab SCM/PR Provider
## Implementation details
### Overview
In order for a self-signed TLS certificate be used by an ApplicationSet's SCM / PR Gitlab Generator, the certificate needs to be mounted on the application-controller. The path of the mounted certificate must be explicitly set using the environment variable `ARGOCD_APPLICATIONSET_CONTROLLER_SCM_ROOT_CA_PATH` or alternatively using parameter `--scm-root-ca-path`. The applicationset controller will read the mounted certificate to create the Gitlab client for SCM/PR Providers
This can be achieved conveniently by setting `applicationsetcontroller.scm.root.ca.path` in the argocd-cmd-params-cm ConfigMap. Be sure to restart the ApplicationSet controller after setting this value.

View File

@@ -53,6 +53,7 @@ spec:
Therefore administrator must restrict the urls of the allowed SCM Providers (example: `https://git.mydomain.com/,https://gitlab.mydomain.com/`) by setting the environment variable `ARGOCD_APPLICATIONSET_CONTROLLER_ALLOWED_SCM_PROVIDERS` to argocd-cmd-params-cm `applicationsetcontroller.allowed.scm.providers`. If another url is used, it will be rejected by the applicationset controller.
For example:
```yaml
apiVersion: v1
@@ -63,14 +64,7 @@ data:
applicationsetcontroller.allowed.scm.providers: https://git.mydomain.com/,https://gitlab.mydomain.com/
```
!!! note
Please note url used in the `api` field of the `ApplicationSet` must match the url declared by the Administrator including the protocol
!!! warning
The allow-list only applies to SCM providers for which the user may configure a custom `api`. Where an SCM or PR
generator does not accept a custom API URL, the provider is implicitly allowed.
If you do not intend to allow users to use the SCM or PR generators, you can disable them entirely by setting the environment variable `ARGOCD_APPLICATIONSET_CONTROLLER_ALLOW_SCM_PROVIDERS` to argocd-cmd-params-cm `applicationsetcontroller.allow.scm.providers` to `false`.
> Please note url used in the `api` field of the `ApplicationSet` must match the url declared by the Administrator including the protocol
### Overview

View File

@@ -105,7 +105,7 @@ spec:
* `pullRequestState`: PullRequestState is an additional MRs filter to get only those with a certain state. Default: "" (all states)
* `insecure`: By default (false) - Skip checking the validity of the SCM's certificate - useful for self-signed TLS certificates.
As a preferable alternative to setting `insecure` to true, you can configure self-signed TLS certificates for Gitlab by [mounting self-signed certificate to the applicationset controller](./Generators-SCM-Provider.md#self-signed-tls-certificates).
As a preferable alternative to setting `insecure` to true, you can configure self-signed TLS certificates for Gitlab by [mounting self-signed certificate to the applicationset controller](./Add-self-signed-TLS-Certs.md).
## Gitea

View File

@@ -111,18 +111,12 @@ spec:
* `tokenRef`: A `Secret` name and key containing the GitLab access token to use for requests. If not specified, will make anonymous requests which have a lower rate limit and can only see public repositories.
* `insecure`: By default (false) - Skip checking the validity of the SCM's certificate - useful for self-signed TLS certificates.
As a preferable alternative to setting `insecure` to true, you can configure self-signed TLS certificates for Gitlab by [mounting self-signed certificate to the applicationset controller](./Add-self-signed-TLS-Certs.md).
For label filtering, the repository tags are used.
Available clone protocols are `ssh` and `https`.
### Self-signed TLS Certificates
As a preferable alternative to setting `insecure` to true, you can configure self-signed TLS certificates for Gitlab.
In order for a self-signed TLS certificate be used by an ApplicationSet's SCM / PR Gitlab Generator, the certificate needs to be mounted on the applicationset-controller. The path of the mounted certificate must be explicitly set using the environment variable `ARGOCD_APPLICATIONSET_CONTROLLER_SCM_ROOT_CA_PATH` or alternatively using parameter `--scm-root-ca-path`. The applicationset controller will read the mounted certificate to create the Gitlab client for SCM/PR Providers
This can be achieved conveniently by setting `applicationsetcontroller.scm.root.ca.path` in the argocd-cmd-params-cm ConfigMap. Be sure to restart the ApplicationSet controller after setting this value.
## Gitea
The Gitea mode uses the Gitea API to scan organizations in your instance

View File

@@ -174,18 +174,6 @@ It is also possible to use Sprig functions to construct the path variables manua
| `{{path.filenameNormalized}}` | `{{.path.filenameNormalized}}` | `{{normalize .path.filename}}` |
| `{{path[N]}}` | `-` | `{{index .path.segments N}}` |
## Available template functions
ApplicationSet controller provides:
- all [sprig](http://masterminds.github.io/sprig/) Go templates function except `env`, `expandenv` and `getHostByName`
- `normalize`: sanitizes the input so that it complies with the following rules:
1. contains no more than 253 characters
2. contains only lowercase alphanumeric characters, '-' or '.'
3. starts and ends with an alphanumeric character
- `toYaml` / `fromYaml` / `fromYamlArray` helm like functions
## Examples
### Basic Go template usage

View File

@@ -184,8 +184,6 @@ data:
# sending secrets from `tokenRef`s to disallowed `api` domains.
# The url used in the scm generator must exactly match one in the list
applicationsetcontroller.allowed.scm.providers: "https://git.example.com/,https://gitlab.example.com/"
# To disable SCM providers entirely (i.e. disable the SCM and PR generators), set this to "false". Default is "true".
applicationsetcontroller.enable.scm.providers: "false"
## Argo CD Notifications Controller Properties
# Set the logging level. One of: debug|info|warn|error (default "info")

View File

@@ -173,36 +173,3 @@ To test the implemented custom health checks, run `go test -v ./util/lua/`.
The [PR#1139](https://github.com/argoproj/argo-cd/pull/1139) is an example of Cert Manager CRDs custom health check.
Please note that bundled health checks with wildcards are not supported.
## Health Checks
An Argo CD App's health is inferred from the health of its immediate child resources (the resources represented in
source control).
But the health of a resource is not inherited from child resources - it is calculated using only information about the
resource itself. A resource's status field may or may not contain information about the health of a child resource, and
the resource's health check may or may not take that information into account.
The lack of inheritance is by design. A resource's health can't be inferred from its children because the health of a
child resource may not be relevant to the health of the parent resource. For example, a Deployment's health is not
necessarily affected by the health of its Pods.
```
App (healthy)
└── Deployment (healthy)
└── ReplicaSet (healthy)
└── Pod (healthy)
└── ReplicaSet (unhealthy)
└── Pod (unhealthy)
```
If you want the health of a child resource to affect the health of its parent, you need to configure the parent's health
check to take the child's health into account. Since only the parent resource's state is available to the health check,
the parent resource's controller needs to make the child resource's health available in the parent resource's status
field.
```
App (healthy)
└── CustomResource (healthy) <- This resource's health check needs to be fixed to mark the App as unhealthy
└── CustomChildResource (unhealthy)
```

View File

@@ -243,56 +243,3 @@ spec:
path: my-application
# ...
```
## Rate Limiting Application Reconciliations
To prevent high controller resource usage or sync loops caused either due to misbehaving apps or other environment specific factors,
we can configure rate limits on the workqueues used by the application controller. There are two types of rate limits that can be configured:
* Global rate limits
* Per item rate limits
The final rate limiter uses a combination of both and calculates the final backoff as `max(globalBackoff, perItemBackoff)`.
### Global rate limits
This is enabled by default, it is a simple bucket based rate limiter that limits the number of items that can be queued per second.
This is useful to prevent a large number of apps from being queued at the same time.
To configure the bucket limiter you can set the following environment variables:
* `WORKQUEUE_BUCKET_SIZE` - The number of items that can be queued in a single burst. Defaults to 500.
* `WORKQUEUE_BUCKET_QPS` - The number of items that can be queued per second. Defaults to 50.
### Per item rate limits
This by default returns a fixed base delay/backoff value but can be configured to return exponential values, read further to understand it's working.
Per item rate limiter limits the number of times a particular item can be queued. This is based on exponential backoff where the backoff time for an item keeps increasing exponentially
if it is queued multiple times in a short period, but the backoff is reset automatically if a configured `cool down` period has elapsed since the last time the item was queued.
To configure the per item limiter you can set the following environment variables:
* `WORKQUEUE_FAILURE_COOLDOWN_NS` : The cool down period in nanoseconds, once period has elapsed for an item the backoff is reset. Exponential backoff is disabled if set to 0(default), eg. values : 10 * 10^9 (=10s)
* `WORKQUEUE_BASE_DELAY_NS` : The base delay in nanoseconds, this is the initial backoff used in the exponential backoff formula. Defaults to 1000 (=1μs)
* `WORKQUEUE_MAX_DELAY_NS` : The max delay in nanoseconds, this is the max backoff limit. Defaults to 3 * 10^9 (=3s)
* `WORKQUEUE_BACKOFF_FACTOR` : The backoff factor, this is the factor by which the backoff is increased for each retry. Defaults to 1.5
The formula used to calculate the backoff time for an item, where `numRequeue` is the number of times the item has been queued
and `lastRequeueTime` is the time at which the item was last queued:
- When `WORKQUEUE_FAILURE_COOLDOWN_NS` != 0 :
```
backoff = time.Since(lastRequeueTime) >= WORKQUEUE_FAILURE_COOLDOWN_NS ?
WORKQUEUE_BASE_DELAY_NS :
min(
WORKQUEUE_MAX_DELAY_NS,
WORKQUEUE_BASE_DELAY_NS * WORKQUEUE_BACKOFF_FACTOR ^ (numRequeue)
)
```
- When `WORKQUEUE_FAILURE_COOLDOWN_NS` = 0 :
```
backoff = WORKQUEUE_BASE_DELAY_NS
```

View File

@@ -1,4 +1,4 @@
# Notifications Overview
# Overview
Argo CD Notifications continuously monitors Argo CD applications and provides a flexible way to notify
users about important changes in the application state. Using a flexible mechanism of
@@ -10,38 +10,38 @@ So you can just use them instead of reinventing new ones.
* Install Triggers and Templates from the catalog
```bash
kubectl apply -n argocd -f https://raw.githubusercontent.com/argoproj/argo-cd/stable/notifications_catalog/install.yaml
```
```
kubectl apply -n argocd -f https://raw.githubusercontent.com/argoproj/argo-cd/stable/notifications_catalog/install.yaml
```
* Add Email username and password token to `argocd-notifications-secret` secret
```bash
EMAIL_USER=<your-username>
PASSWORD=<your-password>
kubectl apply -n argocd -f - << EOF
apiVersion: v1
kind: Secret
metadata:
name: argocd-notifications-secret
stringData:
email-username: $EMAIL_USER
email-password: $PASSWORD
type: Opaque
EOF
```
```bash
EMAIL_USER=<your-username>
PASSWORD=<your-password>
kubectl apply -n argocd -f - << EOF
apiVersion: v1
kind: Secret
metadata:
name: argocd-notifications-secret
stringData:
email-username: $EMAIL_USER
email-password: $PASSWORD
type: Opaque
EOF
```
* Register Email notification service
```bash
kubectl patch cm argocd-notifications-cm -n argocd --type merge -p '{"data": {"service.email.gmail": "{ username: $email-username, password: $email-password, host: smtp.gmail.com, port: 465, from: $email-username }" }}'
```
```bash
kubectl patch cm argocd-notifications-cm -n argocd --type merge -p '{"data": {"service.email.gmail": "{ username: $email-username, password: $email-password, host: smtp.gmail.com, port: 465, from: $email-username }" }}'
```
* Subscribe to notifications by adding the `notifications.argoproj.io/subscribe.on-sync-succeeded.slack` annotation to the Argo CD application or project:
```bash
kubectl patch app <my-app> -n argocd -p '{"metadata": {"annotations": {"notifications.argoproj.io/subscribe.on-sync-succeeded.slack":"<my-channel>"}}}' --type merge
```
```bash
kubectl patch app <my-app> -n argocd -p '{"metadata": {"annotations": {"notifications.argoproj.io/subscribe.on-sync-succeeded.slack":"<my-channel>"}}}' --type merge
```
Try syncing an application to get notified when the sync is completed.

View File

@@ -3,7 +3,7 @@
The Argo CD Notification controller serves Prometheus metrics on port 9001.
!!! note
The metrics port can be changed using the `--metrics-port` flag in `argocd-notifications-controller` deployment.
Metrics port might be changed using the `--metrics-port` flag in `argocd-notifications-controller` deployment.
## Metrics
The following metrics are available:
@@ -15,7 +15,7 @@ The following metrics are available:
* `template` - notification template name
* `notifier` - notification service name
* `succeeded` - flag that indicates if notification was successfully sent or failed
* `succeeded` - flag that indicates if notification was successfully sent or failed.
### `argocd_notifications_trigger_eval_total`
@@ -23,8 +23,8 @@ The following metrics are available:
Labels:
* `name` - trigger name
* `triggered` - flag that indicates if trigger condition returned true of false
* `triggered` - flag that indicates if trigger condition returned true of false.
## Examples
# Examples:
* Grafana Dashboard: [grafana-dashboard.json](grafana-dashboard.json)
* Grafana Dashboard: [grafana-dashboard.json](grafana-dashboard.json)

View File

@@ -9,13 +9,13 @@ metadata:
notifications.argoproj.io/subscribe.on-sync-succeeded.slack: my-channel1;my-channel2
```
The annotation key consists of following parts:
Annotation key consists of following parts:
* `on-sync-succeeded` - trigger name
* `slack` - notification service name
* `my-channel1;my-channel2` - a semicolon separated list of recipients
You can create subscriptions for all applications of an Argo CD project by adding the same annotation to the AppProject resource:
You can create subscriptions for all applications of the Argo CD project by adding the same annotation to AppProject CRD:
```yaml
apiVersion: argoproj.io/v1alpha1
@@ -27,7 +27,7 @@ metadata:
## Default Subscriptions
The subscriptions might be configured globally in the `argocd-notifications-cm` ConfigMap using the `subscriptions` field. The default subscriptions
The subscriptions might be configured globally in the `argocd-notifications-cm` ConfigMap using `subscriptions` field. The default subscriptions
are applied to all applications. The trigger and applications might be configured using the
`triggers` and `selector` fields:
@@ -53,7 +53,7 @@ data:
- on-sync-status-unknown
```
If you want to use webhook in subscriptions, you need to store the custom webhook name in the subscription's `recipients` field.
If you want to use webhook in subscriptions, you need to store the custom name to recipients.
```yaml
apiVersion: v1

View File

@@ -1,5 +1,5 @@
The notification template is used to generate the notification content and is configured in the `argocd-notifications-cm` ConfigMap. The template is leveraging
the [html/template](https://golang.org/pkg/html/template/) golang package and allows customization of the notification message.
The notification template is used to generate the notification content and configured in `argocd-notifications-cm` ConfigMap. The template is leveraging
[html/template](https://golang.org/pkg/html/template/) golang package and allow to customize notification message.
Templates are meant to be reusable and can be referenced by multiple triggers.
The following template is used to notify the user about application sync status.
@@ -19,9 +19,9 @@ data:
Each template has access to the following fields:
- `app` holds the application object.
- `context` is a user-defined string map and might include any string keys and values.
- `serviceType` holds the notification service type name (such as "slack" or "email). The field can be used to conditionally
render service-specific fields.
- `context` is user defined string map and might include any string keys and values.
- `serviceType` holds the notification service type name. The field can be used to conditionally
render service specific fields.
- `recipient` holds the recipient name.
## Defining user-defined `context`
@@ -51,30 +51,30 @@ See corresponding service [documentation](services/overview.md) for more informa
## Change the timezone
You can change the timezone to show in notifications as follows.
You can change the timezone to show it as follows.
1. Call time functions.
```
{{ (call .time.Parse .app.status.operationState.startedAt).Local.Format "2006-01-02T15:04:05Z07:00" }}
```
```
{{ (call .time.Parse .app.status.operationState.startedAt).Local.Format "2006-01-02T15:04:05Z07:00" }}
```
2. Set the `TZ` environment variable on the argocd-notifications-controller container.
2. Set environment to container.
```yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: argocd-notifications-controller
```yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: argocd-notifications-controller
spec:
(snip)
spec:
template:
spec:
containers:
- name: argocd-notifications-controller
env:
- name: TZ
value: Asia/Tokyo
```
containers:
- name: argocd-notifications-controller
env:
- name: TZ
value: Asia/Tokyo
```
## Functions

View File

@@ -3,7 +3,7 @@ and notification templates reference. The condition is a predicate expression th
should be sent. The trigger condition evaluation is powered by [antonmedv/expr](https://github.com/antonmedv/expr).
The condition language syntax is described at [Language-Definition.md](https://github.com/antonmedv/expr/blob/master/docs/Language-Definition.md).
The trigger is configured in the `argocd-notifications-cm` ConfigMap. For example the following trigger sends a notification
The trigger is configured in `argocd-notifications-cm` ConfigMap. For example the following trigger sends a notification
when application sync status changes to `Unknown` using the `app-sync-status` template:
```yaml
@@ -17,9 +17,9 @@ data:
send: [app-sync-status, github-commit-status] # template names
```
Each condition might use several templates. Typically, each template is responsible for generating a service-specific notification part.
In the example above, the `app-sync-status` template "knows" how to create email and Slack notification, and `github-commit-status` knows how to
generate the payload for GitHub webhook.
Each condition might use several templates. Typically each template is responsible for generating a service-specific notification part.
In the example above `app-sync-status` template "knows" how to create email and slack notification and `github-commit-status` knows how to
generate payload for Github webhook.
## Conditions Bundles

View File

@@ -39,7 +39,6 @@ argocd admin notifications template get app-sync-succeeded -o=yaml
--cluster string The name of the kubeconfig cluster to use
--config-map string argocd-notifications-cm.yaml file path
--context string The name of the kubeconfig context to use
--disable-compression If true, opt-out of response compression for all requests to the server
--insecure-skip-tls-verify If true, the server's certificate will not be checked for validity. This will make your HTTPS connections insecure
--kubeconfig string Path to a kube config. Only required if out-of-cluster
-n, --namespace string If present, the namespace scope for this CLI request
@@ -96,7 +95,6 @@ argocd admin notifications template notify app-sync-succeeded guestbook
--cluster string The name of the kubeconfig cluster to use
--config-map string argocd-notifications-cm.yaml file path
--context string The name of the kubeconfig context to use
--disable-compression If true, opt-out of response compression for all requests to the server
--insecure-skip-tls-verify If true, the server's certificate will not be checked for validity. This will make your HTTPS connections insecure
--kubeconfig string Path to a kube config. Only required if out-of-cluster
-n, --namespace string If present, the namespace scope for this CLI request
@@ -152,7 +150,6 @@ argocd admin notifications trigger get on-sync-failed -o=yaml
--cluster string The name of the kubeconfig cluster to use
--config-map string argocd-notifications-cm.yaml file path
--context string The name of the kubeconfig context to use
--disable-compression If true, opt-out of response compression for all requests to the server
--insecure-skip-tls-verify If true, the server's certificate will not be checked for validity. This will make your HTTPS connections insecure
--kubeconfig string Path to a kube config. Only required if out-of-cluster
-n, --namespace string If present, the namespace scope for this CLI request
@@ -208,7 +205,6 @@ argocd admin notifications trigger run on-sync-status-unknown ./sample-app.yaml
--cluster string The name of the kubeconfig cluster to use
--config-map string argocd-notifications-cm.yaml file path
--context string The name of the kubeconfig context to use
--disable-compression If true, opt-out of response compression for all requests to the server
--insecure-skip-tls-verify If true, the server's certificate will not be checked for validity. This will make your HTTPS connections insecure
--kubeconfig string Path to a kube config. Only required if out-of-cluster
-n, --namespace string If present, the namespace scope for this CLI request

View File

@@ -36,6 +36,6 @@ You need to check your argocd-notifications controller version. For instance, th
## Failed to notify recipient
### notification service 'xxxx' is not supported
### notification service 'xxxx' is not supported"
You have not defined `xxxx` in `argocd-notifications-cm` or to fail to parse settings.

View File

@@ -1,30 +1,32 @@
`argocd admin notifications` is a CLI command group that helps to configure the controller
settings and troubleshoot issues. Full command details are available in the [command reference](../../user-guide/commands/argocd_admin_notifications.md).
## Troubleshooting
The `argocd admin notifications` is a CLI command group that helps to configure the controller
settings and troubleshoot issues.
## Global flags
The following global flags are available for all sub-commands:
Following global flags are available for all sub-commands:
* `--config-map` - path to the file containing `argocd-notifications-cm` ConfigMap. If not specified
* `config-map` - path to the file containing `argocd-notifications-cm` ConfigMap. If not specified
then the command loads `argocd-notification-cm` ConfigMap using the local Kubernetes config file.
* `--secret` - path to the file containing `argocd-notifications-secret` ConfigMap. If not
* `secret` - path to the file containing `argocd-notifications-secret` ConfigMap. If not
specified then the command loads `argocd-notification-secret` Secret using the local Kubernetes config file.
Additionally, you can specify `:empty` to use empty secret with no notification service settings.
Additionally, you can specify `:empty` value to use empty secret with no notification service settings.
**Examples:**
* Get a list of triggers configured in the local config map:
* Get list of triggers configured in the local config map:
```bash
argocd admin notifications trigger get \
--config-map ./argocd admin notifications-cm.yaml --secret :empty
```
```bash
argocd admin notifications trigger get \
--config-map ./argocd admin notifications-cm.yaml --secret :empty
```
* Trigger notification using in-cluster config map and secret:
```bash
argocd admin notifications template notify \
app-sync-succeeded guestbook --recipient slack:argocd admin notifications
```
```bash
argocd admin notifications template notify \
app-sync-succeeded guestbook --recipient slack:argocd admin notifications
```
## Kustomize
@@ -42,17 +44,17 @@ kustomize build ./argocd-notifications | \
### On your laptop
You can download the `argocd` CLI from the GitHub [release](https://github.com/argoproj/argo-cd/releases)
You can download the `argocd` CLI from the github [release](https://github.com/argoproj/argo-cd/releases)
attachments.
The binary is available in the `quay.io/argoproj/argocd` image. Use the `docker run` and volume mount
The binary is available in `argoproj/argo-cd` image. Use the `docker run` and volume mount
to execute binary on any platform.
**Example:**
```bash
docker run --rm -it -w /src -v $(pwd):/src \
quay.io/argoproj/argocd:<version> \
argoproj/argo-cd:<version> \
/app/argocd admin notifications trigger get \
--config-map ./argocd-notifications-cm.yaml --secret :empty
```
@@ -70,12 +72,7 @@ kubectl exec -it argocd-notifications-controller-<pod-hash> \
## Commands
The following commands may help debug issues with notifications:
* [`argocd admin notifications template get`](../../user-guide/commands/argocd_admin_notifications_template_get.md)
* [`argocd admin notifications template notify`](../../user-guide/commands/argocd_admin_notifications_template_notify.md)
* [`argocd admin notifications trigger get`](../../user-guide/commands/argocd_admin_notifications_trigger_get.md)
* [`argocd admin notifications trigger run`](../../user-guide/commands/argocd_admin_notifications_trigger_run.md)
{!docs/operator-manual/notifications/troubleshooting-commands.md!}
## Errors

View File

@@ -28,7 +28,6 @@ argocd-application-controller [flags]
--cluster string The name of the kubeconfig cluster to use
--context string The name of the kubeconfig context to use
--default-cache-expiration duration Cache expiration default (default 24h0m0s)
--disable-compression If true, opt-out of response compression for all requests to the server
--dynamic-cluster-distribution-enabled Enables dynamic cluster distribution.
--gloglevel int Set the glog logging level
-h, --help help for argocd-application-controller
@@ -70,11 +69,5 @@ argocd-application-controller [flags]
--token string Bearer token for authentication to the API server
--user string The name of the kubeconfig user to use
--username string Username for basic authentication to the API server
--wq-backoff-factor float Set Workqueue Per Item Rate Limiter Backoff Factor, default is 1.5 (default 1.5)
--wq-basedelay-ns duration Set Workqueue Per Item Rate Limiter Base Delay duration in nanoseconds, default 1000000 (1ms) (default 1ms)
--wq-bucket-qps int Set Workqueue Rate Limiter Bucket QPS, default 50 (default 50)
--wq-bucket-size int Set Workqueue Rate Limiter Bucket Size, default 500 (default 500)
--wq-cooldown-ns duration Set Workqueue Per Item Rate Limiter Cooldown duration in ns, default 0(per item rate limiter disabled)
--wq-maxdelay-ns duration Set Workqueue Per Item Rate Limiter Max Delay duration in nanoseconds, default 1000000000 (1s) (default 1s)
```

View File

@@ -19,7 +19,6 @@ argocd-dex gendexcfg [flags]
--client-key string Path to a client key file for TLS
--cluster string The name of the kubeconfig cluster to use
--context string The name of the kubeconfig context to use
--disable-compression If true, opt-out of response compression for all requests to the server
--disable-tls Disable TLS on the HTTP endpoint
-h, --help help for gendexcfg
--insecure-skip-tls-verify If true, the server's certificate will not be checked for validity. This will make your HTTPS connections insecure

View File

@@ -19,7 +19,6 @@ argocd-dex rundex [flags]
--client-key string Path to a client key file for TLS
--cluster string The name of the kubeconfig cluster to use
--context string The name of the kubeconfig context to use
--disable-compression If true, opt-out of response compression for all requests to the server
--disable-tls Disable TLS on the HTTP endpoint
-h, --help help for rundex
--insecure-skip-tls-verify If true, the server's certificate will not be checked for validity. This will make your HTTPS connections insecure

View File

@@ -34,7 +34,6 @@ argocd-server [flags]
--dex-server-plaintext Use a plaintext client (non-TLS) to connect to dex server
--dex-server-strict-tls Perform strict validation of TLS certificates when connecting to dex server
--disable-auth Disable client authentication
--disable-compression If true, opt-out of response compression for all requests to the server
--enable-gzip Enable GZIP compression (default true)
--enable-proxy-extension Enable Proxy Extension feature
--gloglevel int Set the glog logging level

View File

@@ -26,7 +26,6 @@ argocd-server version [flags]
--client-key string Path to a client key file for TLS
--cluster string The name of the kubeconfig cluster to use
--context string The name of the kubeconfig context to use
--disable-compression If true, opt-out of response compression for all requests to the server
--insecure-skip-tls-verify If true, the server's certificate will not be checked for validity. This will make your HTTPS connections insecure
--kubeconfig string Path to a kube config. Only required if out-of-cluster
-n, --namespace string If present, the namespace scope for this CLI request

View File

@@ -1,6 +1,5 @@
| Argo CD version | Kubernetes versions |
|-----------------|---------------------|
| 2.9 | v1.27, v1.26, v1.25, v1.24 |
| 2.8 | v1.27, v1.26, v1.25, v1.24 |
| 2.7 | v1.26, v1.25, v1.24, v1.23 |
| 2.6 | v1.24, v1.23, v1.22 |
| 2.5 | v1.24, v1.23, v1.22 |

View File

@@ -1,12 +0,0 @@
# v2.8 to 2.9
## `managedNamespaceMetadata` no longer preserves client-side-applied labels or annotations
Argo CD 2.9 upgraded kubectl from 1.24 to 1.26. This upgrade introduced a change where client-side-applied labels and
annotations are no longer preserved when using a server-side kubectl apply. This change affects the
`managedNamespaceMetadata` field of the `Application` CRD. Previously, labels and annotations applied via a client-side
apply would be preserved when `managedNamespaceMetadata` was enabled. Now, those existing labels and annotation will be
removed.
To avoid unexpected behavior, follow the [client-side to server-side resource upgrade guide](https://kubernetes.io/docs/reference/using-api/server-side-apply/#upgrading-from-client-side-apply-to-server-side-apply)
before enabling `managedNamespaceMetadata` on an existing namespace.

View File

@@ -37,7 +37,6 @@ kubectl apply -n argocd -f https://raw.githubusercontent.com/argoproj/argo-cd/<v
<hr/>
* [v2.8 to v2.9](./2.8-2.9.md)
* [v2.7 to v2.8](./2.7-2.8.md)
* [v2.6 to v2.7](./2.6-2.7.md)
* [v2.5 to v2.6](./2.5-2.6.md)

View File

@@ -1,210 +0,0 @@
# Zitadel
Please also consult the [Zitadel Documentation](https://zitadel.com/docs).
## Integrating Zitadel and ArgoCD
These instructions will take you through the entire process of getting your ArgoCD application authenticating and authorizing with Zitadel. You will create an application within Zitadel and configure ArgoCD to use Zitadel for authentication using roles set in Zitadel to determine privileges in ArgoCD.
The following steps are required to integrate ArgoCD with Zitadel:
1. Create a new project and a new application in Zitadel
2. Configure the application in Zitadel
3. Set up roles in Zitadel
4. Set up an action in Zitadel
5. Configure ArgoCD configmaps
6. Test the setup
The following values will be used in this example:
- Zitadel FQDN: `auth.example.com`
- Zitadel Project: `argocd-project`
- Zitadel Application: `argocd-application`
- Zitadel Action: `groupsClaim`
- ArgoCD FQDN: `argocd.example.com`
- ArgoCD Administrator Role: `argocd_administrators`
- ArgoCD User Role: `argocd_users`
You may choose different values in your setup; these are used to keep the guide consistent.
## Setting up your project and application in Zitadel
First, we will create a new project within Zitadel. Go to **Projects** and select **Create New Project**.
You should now see the following screen.
![Zitadel Project](../../assets/zitadel-project.png "Zitadel Project")
Check the following options:
- Assert Roles on Authentication
- Check authorization on Authentication
![Zitadel Project Settings](../../assets/zitadel-project-settings.png "Zitadel Project Settings")
### Roles
Go to **Roles** and click **New**. Create the following two roles. Use the specified values below for both fields **Key** and **Group**.
- `argocd_administrators`
- `argocd_users`
Your roles should now look like this:
![Zitadel Project Roles](../../assets/zitadel-project-roles.png "Zitadel Project Roles")
### Authorizations
Next, go to **Authorizations** and assign your user the role `argocd_administrators`.
Click **New**, enter the name of your user and click **Continue**. Select the role `argocd_administrators` and click **Save**.
Your authorizations should now look like this:
![Zitadel Project Authorizations](../../assets/zitadel-project-authorizations.png "Zitadel Project Authorizations")
### Creating an application
Go to **General** and create a new application. Name the application `argocd-application`.
For type of the application, select **WEB** and click continue.
![Zitadel Application Setup Step 1](../../assets/zitadel-application-1.png "Zitadel Application Setup Step 1")
Select **CODE** and continue.
![Zitadel Application Setup Step 2](../../assets/zitadel-application-2.png "Zitadel Application Setup Step 2")
Next, we will set up the redirect and post-logout URIs. Set the following values:
- Redirect URI: `https://argocd.example.com/auth/callback`
- Post Logout URI: `https://argocd.example.com`
The post logout URI is optional. In the example setup users will be taken back to the ArgoCD login page after logging out.
![Zitadel Application Setup Step 3](../../assets/zitadel-application-3.png "Zitadel Application Setup Step 3")
Verify your configuration on the next screen and click **Create** to create the application.
![Zitadel Application Setup Step 4](../../assets/zitadel-application-4.png "Zitadel Application Setup Step 4")
After clicking **Create** you will be shown the `ClientId` and the `ClientSecret` for your application. Make sure to copy the ClientSecret as you will not be able to retrieve it after closing this window.
For our example, the following values are used:
- ClientId: `227060711795262483@argocd-project`
- ClientSecret: `UGvTjXVFAQ8EkMv2x4GbPcrEwrJGWZ0sR2KbwHRNfYxeLsDurCiVEpa5bkgW0pl0`
![Zitadel Application Secrets](../../assets/zitadel-application-secrets.png "Zitadel Application Secrets")
Once you have saved the ClientSecret in a safe place, click **Close** to complete creating the application.
Go to **Token Settings** and enable the following options:
- User roles inside ID Token
- User Info inside ID Token
![Zitadel Application Settings](../../assets/zitadel-application-settings.png "Zitadel Application Settings")
## Setting up an action in Zitadel
To include the role of the user in the token issued by Zitadel, we will need to set up a Zitadel Action. The authorization in ArgoCD will be determined by the role contained within the auth token.
Go to **Actions**, click **New** and choose `groupsClaim` as the name of your action.
Paste the following code into the action:
```javascript
/**
* sets the roles an additional claim in the token with roles as value an project as key
*
* The role claims of the token look like the following:
*
* // added by the code below
* "groups": ["{roleName}", "{roleName}", ...],
*
* Flow: Complement token, Triggers: Pre Userinfo creation, Pre access token creation
*
* @param ctx
* @param api
*/
function groupsClaim(ctx, api) {
if (ctx.v1.user.grants === undefined || ctx.v1.user.grants.count == 0) {
return;
}
let grants = [];
ctx.v1.user.grants.grants.forEach((claim) => {
claim.roles.forEach((role) => {
grants.push(role);
});
});
api.v1.claims.setClaim("groups", grants);
}
```
Check **Allowed To Fail** and click **Add** to add your action.
*Note: If **Allowed To Fail** is not checked and a user does not have a role assigned, it may be possible that the user is no longer able to log in to Zitadel as the login flow fails when the action fails.*
Next, add your action to the **Complement Token** flow. Select the **Complement Token** flow from the dropdown and click **Add trigger**.
Add your action to both triggers **Pre Userinfo creation** and **Pre access token creation**.
Your Actions page should now look like the following screenshot:
![Zitadel Actions](../../assets/zitadel-actions.png "Zitadel Actions")
## Configuring the ArgoCD configmaps
Next, we will configure two ArgoCD configmaps:
- [argocd-cm.yaml](https://github.com/argoproj/argo-cd/blob/master/docs/operator-manual/argocd-cm.yaml)
- [argocd-rbac-cm.yaml](https://github.com/argoproj/argo-cd/blob/master/docs/operator-manual/argocd-rbac-cm.yaml)
Configure your configmaps as follows while making sure to replace the relevant values such as `url`, `issuer`, `clientID`, `clientSecret` and `logoutURL` with ones matching your setup.
### argocd-cm.yaml
```yaml
---
apiVersion: v1
kind: ConfigMap
metadata:
name: argocd-cm
namespace: argocd
labels:
app.kubernetes.io/part-of: argocd
data:
admin.enabled: "false"
url: https://argocd.example.com
oidc.config: |
name: Zitadel
issuer: https://auth.example.com
clientID: 227060711795262483@argocd-project
clientSecret: UGvTjXVFAQ8EkMv2x4GbPcrEwrJGWZ0sR2KbwHRNfYxeLsDurCiVEpa5bkgW0pl0
requestedScopes:
- openid
- profile
- email
- groups
logoutURL: https://auth.example.com/oidc/v1/end_session
```
### argocd-rbac-cm.yaml
```yaml
---
apiVersion: v1
kind: ConfigMap
metadata:
name: argocd-rbac-cm
namespace: argocd
labels:
app.kubernetes.io/part-of: argocd
data:
scopes: '[groups]'
policy.csv: |
g, argocd_administrators, role:admin
g, argocd_users, role:readonly
policy.default: ''
```
The roles specified under `policy.csv` must match the roles configured in Zitadel.
The Zitadel role `argocd_administrators` will be assigned the ArgoCD role `admin` granting admin access to ArgoCD.
The Zitadel role `argocd_users` will be assigned the ArgoCD role `readonly` granting read-only access to ArgoCD.
Deploy your ArgoCD configmaps. ArgoCD and Zitadel should now be set up correctly to allow users to log in to ArgoCD using Zitadel.
## Testing the setup
Go to your ArgoCD instance. You should now see the **LOG IN WITH ZITADEL** button above the usual username/password login.
![Zitadel ArgoCD Login](../../assets/zitadel-argocd-login.png "Zitadel ArgoCD Login")
After logging in with your Zitadel user go to **User Info**. If everything is set up correctly you should now see the group `argocd_administrators` as shown below.
![Zitadel ArgoCD User Info](../../assets/zitadel-argocd-user-info.png "Zitadel ArgoCD User Info")

View File

@@ -3,5 +3,4 @@ mkdocs-material==7.1.8
markdown_include==0.6.0
pygments==2.15.0
jinja2==3.0.3
markdown==3.3.7
pymdown-extensions==10.2.1
markdown==3.3.7

View File

@@ -17,33 +17,20 @@ recent minor releases.
| [ui/yarn.lock](master/argocd-test.html) | 0 | 0 | 0 | 0 |
| [dex:v2.37.0](master/ghcr.io_dexidp_dex_v2.37.0.html) | 1 | 0 | 3 | 0 |
| [haproxy:2.6.14-alpine](master/haproxy_2.6.14-alpine.html) | 0 | 0 | 0 | 0 |
| [argocd:latest](master/quay.io_argoproj_argocd_latest.html) | 0 | 0 | 3 | 19 |
| [argocd:latest](master/quay.io_argoproj_argocd_latest.html) | 0 | 0 | 3 | 17 |
| [redis:7.0.11-alpine](master/redis_7.0.11-alpine.html) | 1 | 0 | 3 | 0 |
| [install.yaml](master/argocd-iac-install.html) | - | - | - | - |
| [namespace-install.yaml](master/argocd-iac-namespace-install.html) | - | - | - | - |
### v2.9.0-rc2
| | Critical | High | Medium | Low |
|---:|:--------:|:----:|:------:|:---:|
| [go.mod](v2.9.0-rc2/argocd-test.html) | 0 | 2 | 5 | 0 |
| [ui/yarn.lock](v2.9.0-rc2/argocd-test.html) | 0 | 0 | 0 | 0 |
| [dex:v2.37.0](v2.9.0-rc2/ghcr.io_dexidp_dex_v2.37.0.html) | 1 | 0 | 3 | 0 |
| [haproxy:2.6.14-alpine](v2.9.0-rc2/haproxy_2.6.14-alpine.html) | 0 | 0 | 0 | 0 |
| [argocd:v2.9.0-rc2](v2.9.0-rc2/quay.io_argoproj_argocd_v2.9.0-rc2.html) | 0 | 2 | 6 | 20 |
| [redis:7.0.11-alpine](v2.9.0-rc2/redis_7.0.11-alpine.html) | 1 | 0 | 3 | 0 |
| [install.yaml](v2.9.0-rc2/argocd-iac-install.html) | - | - | - | - |
| [namespace-install.yaml](v2.9.0-rc2/argocd-iac-namespace-install.html) | - | - | - | - |
### v2.8.4
| | Critical | High | Medium | Low |
|---:|:--------:|:----:|:------:|:---:|
| [go.mod](v2.8.4/argocd-test.html) | 0 | 2 | 5 | 0 |
| [go.mod](v2.8.4/argocd-test.html) | 0 | 0 | 5 | 0 |
| [ui/yarn.lock](v2.8.4/argocd-test.html) | 0 | 0 | 0 | 0 |
| [dex:v2.37.0](v2.8.4/ghcr.io_dexidp_dex_v2.37.0.html) | 1 | 0 | 3 | 0 |
| [haproxy:2.6.14-alpine](v2.8.4/haproxy_2.6.14-alpine.html) | 0 | 0 | 0 | 0 |
| [argocd:v2.8.4](v2.8.4/quay.io_argoproj_argocd_v2.8.4.html) | 0 | 2 | 6 | 20 |
| [argocd:v2.8.4](v2.8.4/quay.io_argoproj_argocd_v2.8.4.html) | 0 | 0 | 3 | 17 |
| [redis:7.0.11-alpine](v2.8.4/redis_7.0.11-alpine.html) | 1 | 0 | 3 | 0 |
| [install.yaml](v2.8.4/argocd-iac-install.html) | - | - | - | - |
| [namespace-install.yaml](v2.8.4/argocd-iac-namespace-install.html) | - | - | - | - |
@@ -52,11 +39,11 @@ recent minor releases.
| | Critical | High | Medium | Low |
|---:|:--------:|:----:|:------:|:---:|
| [go.mod](v2.7.14/argocd-test.html) | 0 | 3 | 5 | 0 |
| [go.mod](v2.7.14/argocd-test.html) | 0 | 1 | 5 | 0 |
| [ui/yarn.lock](v2.7.14/argocd-test.html) | 0 | 1 | 0 | 0 |
| [dex:v2.37.0](v2.7.14/ghcr.io_dexidp_dex_v2.37.0.html) | 1 | 0 | 3 | 0 |
| [haproxy:2.6.14-alpine](v2.7.14/haproxy_2.6.14-alpine.html) | 0 | 0 | 0 | 0 |
| [argocd:v2.7.14](v2.7.14/quay.io_argoproj_argocd_v2.7.14.html) | 0 | 2 | 6 | 20 |
| [argocd:v2.7.14](v2.7.14/quay.io_argoproj_argocd_v2.7.14.html) | 0 | 0 | 3 | 17 |
| [redis:7.0.11-alpine](v2.7.14/redis_7.0.11-alpine.html) | 1 | 0 | 3 | 0 |
| [install.yaml](v2.7.14/argocd-iac-install.html) | - | - | - | - |
| [namespace-install.yaml](v2.7.14/argocd-iac-namespace-install.html) | - | - | - | - |
@@ -65,11 +52,11 @@ recent minor releases.
| | Critical | High | Medium | Low |
|---:|:--------:|:----:|:------:|:---:|
| [go.mod](v2.6.15/argocd-test.html) | 0 | 3 | 5 | 0 |
| [go.mod](v2.6.15/argocd-test.html) | 0 | 1 | 5 | 0 |
| [ui/yarn.lock](v2.6.15/argocd-test.html) | 0 | 1 | 0 | 0 |
| [dex:v2.37.0](v2.6.15/ghcr.io_dexidp_dex_v2.37.0.html) | 1 | 0 | 3 | 0 |
| [haproxy:2.6.14-alpine](v2.6.15/haproxy_2.6.14-alpine.html) | 0 | 0 | 0 | 0 |
| [argocd:v2.6.15](v2.6.15/quay.io_argoproj_argocd_v2.6.15.html) | 0 | 2 | 6 | 20 |
| [argocd:v2.6.15](v2.6.15/quay.io_argoproj_argocd_v2.6.15.html) | 0 | 0 | 3 | 17 |
| [redis:7.0.11-alpine](v2.6.15/redis_7.0.11-alpine.html) | 1 | 0 | 3 | 0 |
| [install.yaml](v2.6.15/argocd-iac-install.html) | - | - | - | - |
| [namespace-install.yaml](v2.6.15/argocd-iac-namespace-install.html) | - | - | - | - |

View File

@@ -456,7 +456,7 @@
<div class="header-wrap">
<h1 class="project__header__title">Snyk test report</h1>
<p class="timestamp">October 15th 2023, 12:17:18 am (UTC+00:00)</p>
<p class="timestamp">September 17th 2023, 12:18:15 am (UTC+00:00)</p>
</div>
<div class="source-panel">
<span>Scanned the following path:</span>
@@ -507,7 +507,7 @@
</li>
<li class="card__meta__item">
Line number: 20316
Line number: 18488
</li>
</ul>
@@ -553,7 +553,7 @@
</li>
<li class="card__meta__item">
Line number: 20393
Line number: 18565
</li>
</ul>
@@ -599,7 +599,7 @@
</li>
<li class="card__meta__item">
Line number: 20421
Line number: 18593
</li>
</ul>
@@ -645,7 +645,7 @@
</li>
<li class="card__meta__item">
Line number: 20469
Line number: 18641
</li>
</ul>
@@ -691,7 +691,7 @@
</li>
<li class="card__meta__item">
Line number: 20451
Line number: 18623
</li>
</ul>
@@ -737,7 +737,7 @@
</li>
<li class="card__meta__item">
Line number: 20485
Line number: 18657
</li>
</ul>
@@ -789,7 +789,7 @@
</li>
<li class="card__meta__item">
Line number: 21630
Line number: 19790
</li>
</ul>
@@ -847,7 +847,7 @@
</li>
<li class="card__meta__item">
Line number: 20969
Line number: 19141
</li>
</ul>
@@ -905,7 +905,7 @@
</li>
<li class="card__meta__item">
Line number: 21220
Line number: 19386
</li>
</ul>
@@ -963,7 +963,7 @@
</li>
<li class="card__meta__item">
Line number: 21186
Line number: 19352
</li>
</ul>
@@ -1021,7 +1021,7 @@
</li>
<li class="card__meta__item">
Line number: 21280
Line number: 19446
</li>
</ul>
@@ -1079,7 +1079,7 @@
</li>
<li class="card__meta__item">
Line number: 21373
Line number: 19533
</li>
</ul>
@@ -1137,7 +1137,7 @@
</li>
<li class="card__meta__item">
Line number: 21630
Line number: 19790
</li>
</ul>
@@ -1195,7 +1195,7 @@
</li>
<li class="card__meta__item">
Line number: 21430
Line number: 19590
</li>
</ul>
@@ -1253,7 +1253,7 @@
</li>
<li class="card__meta__item">
Line number: 21715
Line number: 19875
</li>
</ul>
@@ -1311,7 +1311,7 @@
</li>
<li class="card__meta__item">
Line number: 22031
Line number: 20191
</li>
</ul>
@@ -1363,7 +1363,7 @@
</li>
<li class="card__meta__item">
Line number: 21200
Line number: 19366
</li>
</ul>
@@ -1415,59 +1415,7 @@
</li>
<li class="card__meta__item">
Line number: 20969
</li>
</ul>
<hr/>
<h2>Impact</h2>
<p>Kubernetes will not be able to detect if application is able to service requests, and will not restart unhealthy pods</p>
<h2>Remediation</h2>
<p>Add &#x60;livenessProbe&#x60; attribute</p>
<hr/>
</div><!-- .card__section -->
<div class="cta card__cta">
<p><a href="https://security.snyk.io/rules/cloud/SNYK-CC-K8S-41">More about this issue</a></p>
</div>
</div><!-- .card -->
<div class="card card--vuln disclosure--not-new severity--low" data-snyk-test="low">
<h2 class="card__title">Container is running without liveness probe</h2>
<div class="card__section">
<div class="label label--low">
<span class="label__text">low severity</span>
</div>
<hr/>
<ul class="card__meta">
<li class="card__meta__item">
Public ID: <a href="https://security.snyk.io/rules/cloud/SNYK-CC-K8S-41">SNYK-CC-K8S-41</a>
</li>
<li class="card__meta__item">Introduced through:
[DocId: 42]
<span class="list-paths__item__arrow"></span>
spec
<span class="list-paths__item__arrow"></span>
template
<span class="list-paths__item__arrow"></span>
spec
<span class="list-paths__item__arrow"></span>
initContainers[copyutil]
<span class="list-paths__item__arrow"></span>
livenessProbe
</li>
<li class="card__meta__item">
Line number: 21220
Line number: 19141
</li>
</ul>
@@ -1519,7 +1467,59 @@
</li>
<li class="card__meta__item">
Line number: 21186
Line number: 19352
</li>
</ul>
<hr/>
<h2>Impact</h2>
<p>Kubernetes will not be able to detect if application is able to service requests, and will not restart unhealthy pods</p>
<h2>Remediation</h2>
<p>Add &#x60;livenessProbe&#x60; attribute</p>
<hr/>
</div><!-- .card__section -->
<div class="cta card__cta">
<p><a href="https://security.snyk.io/rules/cloud/SNYK-CC-K8S-41">More about this issue</a></p>
</div>
</div><!-- .card -->
<div class="card card--vuln disclosure--not-new severity--low" data-snyk-test="low">
<h2 class="card__title">Container is running without liveness probe</h2>
<div class="card__section">
<div class="label label--low">
<span class="label__text">low severity</span>
</div>
<hr/>
<ul class="card__meta">
<li class="card__meta__item">
Public ID: <a href="https://security.snyk.io/rules/cloud/SNYK-CC-K8S-41">SNYK-CC-K8S-41</a>
</li>
<li class="card__meta__item">Introduced through:
[DocId: 42]
<span class="list-paths__item__arrow"></span>
spec
<span class="list-paths__item__arrow"></span>
template
<span class="list-paths__item__arrow"></span>
spec
<span class="list-paths__item__arrow"></span>
initContainers[copyutil]
<span class="list-paths__item__arrow"></span>
livenessProbe
</li>
<li class="card__meta__item">
Line number: 19386
</li>
</ul>
@@ -1571,7 +1571,7 @@
</li>
<li class="card__meta__item">
Line number: 21373
Line number: 19533
</li>
</ul>
@@ -1623,7 +1623,7 @@
</li>
<li class="card__meta__item">
Line number: 21630
Line number: 19790
</li>
</ul>
@@ -1681,7 +1681,7 @@
</li>
<li class="card__meta__item">
Line number: 20969
Line number: 19141
</li>
</ul>
@@ -1739,7 +1739,7 @@
</li>
<li class="card__meta__item">
Line number: 21186
Line number: 19352
</li>
</ul>
@@ -1797,7 +1797,7 @@
</li>
<li class="card__meta__item">
Line number: 21220
Line number: 19386
</li>
</ul>
@@ -1855,7 +1855,7 @@
</li>
<li class="card__meta__item">
Line number: 21280
Line number: 19446
</li>
</ul>
@@ -1913,7 +1913,7 @@
</li>
<li class="card__meta__item">
Line number: 21373
Line number: 19533
</li>
</ul>
@@ -1971,7 +1971,7 @@
</li>
<li class="card__meta__item">
Line number: 21630
Line number: 19790
</li>
</ul>
@@ -2029,7 +2029,7 @@
</li>
<li class="card__meta__item">
Line number: 21430
Line number: 19590
</li>
</ul>
@@ -2087,7 +2087,7 @@
</li>
<li class="card__meta__item">
Line number: 21715
Line number: 19875
</li>
</ul>
@@ -2145,7 +2145,7 @@
</li>
<li class="card__meta__item">
Line number: 22031
Line number: 20191
</li>
</ul>
@@ -2201,7 +2201,7 @@
</li>
<li class="card__meta__item">
Line number: 21110
Line number: 19276
</li>
</ul>
@@ -2257,7 +2257,7 @@
</li>
<li class="card__meta__item">
Line number: 21228
Line number: 19394
</li>
</ul>
@@ -2313,7 +2313,7 @@
</li>
<li class="card__meta__item">
Line number: 21203
Line number: 19369
</li>
</ul>
@@ -2369,7 +2369,7 @@
</li>
<li class="card__meta__item">
Line number: 21307
Line number: 19467
</li>
</ul>
@@ -2425,7 +2425,7 @@
</li>
<li class="card__meta__item">
Line number: 21383
Line number: 19543
</li>
</ul>
@@ -2481,7 +2481,7 @@
</li>
<li class="card__meta__item">
Line number: 21637
Line number: 19797
</li>
</ul>
@@ -2537,7 +2537,7 @@
</li>
<li class="card__meta__item">
Line number: 21603
Line number: 19763
</li>
</ul>
@@ -2593,7 +2593,7 @@
</li>
<li class="card__meta__item">
Line number: 21941
Line number: 20101
</li>
</ul>
@@ -2649,7 +2649,7 @@
</li>
<li class="card__meta__item">
Line number: 22179
Line number: 20339
</li>
</ul>

View File

@@ -456,7 +456,7 @@
<div class="header-wrap">
<h1 class="project__header__title">Snyk test report</h1>
<p class="timestamp">October 15th 2023, 12:17:30 am (UTC+00:00)</p>
<p class="timestamp">September 17th 2023, 12:18:27 am (UTC+00:00)</p>
</div>
<div class="source-panel">
<span>Scanned the following path:</span>
@@ -789,7 +789,7 @@
</li>
<li class="card__meta__item">
Line number: 1286
Line number: 1274
</li>
</ul>
@@ -905,7 +905,7 @@
</li>
<li class="card__meta__item">
Line number: 876
Line number: 870
</li>
</ul>
@@ -963,7 +963,7 @@
</li>
<li class="card__meta__item">
Line number: 842
Line number: 836
</li>
</ul>
@@ -1021,7 +1021,7 @@
</li>
<li class="card__meta__item">
Line number: 936
Line number: 930
</li>
</ul>
@@ -1079,7 +1079,7 @@
</li>
<li class="card__meta__item">
Line number: 1029
Line number: 1017
</li>
</ul>
@@ -1137,7 +1137,7 @@
</li>
<li class="card__meta__item">
Line number: 1286
Line number: 1274
</li>
</ul>
@@ -1195,7 +1195,7 @@
</li>
<li class="card__meta__item">
Line number: 1086
Line number: 1074
</li>
</ul>
@@ -1253,7 +1253,7 @@
</li>
<li class="card__meta__item">
Line number: 1371
Line number: 1359
</li>
</ul>
@@ -1311,7 +1311,7 @@
</li>
<li class="card__meta__item">
Line number: 1687
Line number: 1675
</li>
</ul>
@@ -1363,7 +1363,7 @@
</li>
<li class="card__meta__item">
Line number: 856
Line number: 850
</li>
</ul>
@@ -1460,14 +1460,14 @@
<span class="list-paths__item__arrow"></span>
spec
<span class="list-paths__item__arrow"></span>
initContainers[copyutil]
containers[dex]
<span class="list-paths__item__arrow"></span>
livenessProbe
</li>
<li class="card__meta__item">
Line number: 876
Line number: 836
</li>
</ul>
@@ -1512,14 +1512,14 @@
<span class="list-paths__item__arrow"></span>
spec
<span class="list-paths__item__arrow"></span>
containers[dex]
initContainers[copyutil]
<span class="list-paths__item__arrow"></span>
livenessProbe
</li>
<li class="card__meta__item">
Line number: 842
Line number: 870
</li>
</ul>
@@ -1571,7 +1571,7 @@
</li>
<li class="card__meta__item">
Line number: 1029
Line number: 1017
</li>
</ul>
@@ -1623,7 +1623,7 @@
</li>
<li class="card__meta__item">
Line number: 1286
Line number: 1274
</li>
</ul>
@@ -1739,7 +1739,7 @@
</li>
<li class="card__meta__item">
Line number: 842
Line number: 836
</li>
</ul>
@@ -1797,7 +1797,7 @@
</li>
<li class="card__meta__item">
Line number: 876
Line number: 870
</li>
</ul>
@@ -1855,7 +1855,7 @@
</li>
<li class="card__meta__item">
Line number: 936
Line number: 930
</li>
</ul>
@@ -1913,7 +1913,7 @@
</li>
<li class="card__meta__item">
Line number: 1029
Line number: 1017
</li>
</ul>
@@ -1971,7 +1971,7 @@
</li>
<li class="card__meta__item">
Line number: 1286
Line number: 1274
</li>
</ul>
@@ -2029,7 +2029,7 @@
</li>
<li class="card__meta__item">
Line number: 1086
Line number: 1074
</li>
</ul>
@@ -2087,7 +2087,7 @@
</li>
<li class="card__meta__item">
Line number: 1371
Line number: 1359
</li>
</ul>
@@ -2145,7 +2145,7 @@
</li>
<li class="card__meta__item">
Line number: 1687
Line number: 1675
</li>
</ul>
@@ -2201,7 +2201,7 @@
</li>
<li class="card__meta__item">
Line number: 766
Line number: 760
</li>
</ul>
@@ -2257,7 +2257,7 @@
</li>
<li class="card__meta__item">
Line number: 884
Line number: 878
</li>
</ul>
@@ -2313,7 +2313,7 @@
</li>
<li class="card__meta__item">
Line number: 859
Line number: 853
</li>
</ul>
@@ -2369,7 +2369,7 @@
</li>
<li class="card__meta__item">
Line number: 963
Line number: 951
</li>
</ul>
@@ -2425,7 +2425,7 @@
</li>
<li class="card__meta__item">
Line number: 1039
Line number: 1027
</li>
</ul>
@@ -2481,7 +2481,7 @@
</li>
<li class="card__meta__item">
Line number: 1293
Line number: 1281
</li>
</ul>
@@ -2537,7 +2537,7 @@
</li>
<li class="card__meta__item">
Line number: 1259
Line number: 1247
</li>
</ul>
@@ -2593,7 +2593,7 @@
</li>
<li class="card__meta__item">
Line number: 1597
Line number: 1585
</li>
</ul>
@@ -2649,7 +2649,7 @@
</li>
<li class="card__meta__item">
Line number: 1835
Line number: 1823
</li>
</ul>

Some files were not shown because too many files have changed in this diff Show More