mirror of
https://github.com/argoproj/argo-cd.git
synced 2026-04-03 23:38:46 +02:00
Compare commits
26 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
c279299281 | ||
|
|
24f8d82954 | ||
|
|
1ab570ad96 | ||
|
|
8612a99fe8 | ||
|
|
33fa128bad | ||
|
|
4e79aab03d | ||
|
|
7b89ae4424 | ||
|
|
2577d51796 | ||
|
|
2f8533efeb | ||
|
|
c698426a5c | ||
|
|
77556d9e64 | ||
|
|
1391ba7214 | ||
|
|
44e52c4ae7 | ||
|
|
4b55084a8f | ||
|
|
c689ea4957 | ||
|
|
ea9827791f | ||
|
|
dbdfc71270 | ||
|
|
31473491f5 | ||
|
|
016c2f25cd | ||
|
|
31e6e28ca4 | ||
|
|
f07088281b | ||
|
|
bf77b09b7b | ||
|
|
e982e0b80e | ||
|
|
3a468c6862 | ||
|
|
383f2a288b | ||
|
|
e006908fe9 |
@@ -58,10 +58,6 @@ const (
|
||||
// https://github.com/argoproj-labs/argocd-notifications/blob/33d345fa838829bb50fca5c08523aba380d2c12b/pkg/controller/state.go#L17
|
||||
NotifiedAnnotationKey = "notified.notifications.argoproj.io"
|
||||
ReconcileRequeueOnValidationError = time.Minute * 3
|
||||
|
||||
// LabelKeyAppSetInstance is the label key to use to uniquely identify the apps of an applicationset
|
||||
// The ArgoCD applicationset name is used as the instance name
|
||||
LabelKeyAppSetInstance = "argocd.argoproj.io/application-set-name"
|
||||
)
|
||||
|
||||
var (
|
||||
@@ -295,7 +291,6 @@ func (r *ApplicationSetReconciler) Reconcile(ctx context.Context, req ctrl.Reque
|
||||
}
|
||||
|
||||
requeueAfter := r.getMinRequeueAfter(&applicationSetInfo)
|
||||
logCtx.WithField("requeueAfter", requeueAfter).Info("end reconcile")
|
||||
|
||||
if len(validateErrors) == 0 {
|
||||
if err := r.setApplicationSetStatusCondition(ctx,
|
||||
@@ -309,8 +304,13 @@ func (r *ApplicationSetReconciler) Reconcile(ctx context.Context, req ctrl.Reque
|
||||
); err != nil {
|
||||
return ctrl.Result{}, err
|
||||
}
|
||||
} else if requeueAfter == time.Duration(0) {
|
||||
// Ensure that the request is requeued if there are validation errors.
|
||||
requeueAfter = ReconcileRequeueOnValidationError
|
||||
}
|
||||
|
||||
logCtx.WithField("requeueAfter", requeueAfter).Info("end reconcile")
|
||||
|
||||
return ctrl.Result{
|
||||
RequeueAfter: requeueAfter,
|
||||
}, nil
|
||||
@@ -512,10 +512,6 @@ func (r *ApplicationSetReconciler) generateApplications(applicationSetInfo argov
|
||||
|
||||
for _, a := range t {
|
||||
tmplApplication := getTempApplication(a.Template)
|
||||
if tmplApplication.Labels == nil {
|
||||
tmplApplication.Labels = make(map[string]string)
|
||||
}
|
||||
tmplApplication.Labels[LabelKeyAppSetInstance] = applicationSetInfo.Name
|
||||
|
||||
for _, p := range a.Params {
|
||||
app, err := r.Renderer.RenderTemplateParams(tmplApplication, applicationSetInfo.Spec.SyncPolicy, p, applicationSetInfo.Spec.GoTemplate, applicationSetInfo.Spec.GoTemplateOptions)
|
||||
|
||||
@@ -165,9 +165,6 @@ func TestExtractApplications(t *testing.T) {
|
||||
if cc.generateParamsError == nil {
|
||||
for _, p := range cc.params {
|
||||
|
||||
tmpApplication := getTempApplication(cc.template)
|
||||
tmpApplication.Labels[LabelKeyAppSetInstance] = appSet.Name
|
||||
|
||||
if cc.rendererError != nil {
|
||||
rendererMock.On("RenderTemplateParams", getTempApplication(cc.template), p, false, []string(nil)).
|
||||
Return(nil, cc.rendererError)
|
||||
@@ -289,21 +286,7 @@ func TestMergeTemplateApplications(t *testing.T) {
|
||||
|
||||
rendererMock := rendererMock{}
|
||||
|
||||
appSet := &v1alpha1.ApplicationSet{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "name",
|
||||
Namespace: "namespace",
|
||||
},
|
||||
Spec: v1alpha1.ApplicationSetSpec{
|
||||
Generators: []v1alpha1.ApplicationSetGenerator{generator},
|
||||
Template: cc.template,
|
||||
},
|
||||
}
|
||||
|
||||
tmpApplication := getTempApplication(cc.expectedMerged)
|
||||
tmpApplication.Labels[LabelKeyAppSetInstance] = appSet.Name
|
||||
|
||||
rendererMock.On("RenderTemplateParams", tmpApplication, cc.params[0], false, []string(nil)).
|
||||
rendererMock.On("RenderTemplateParams", getTempApplication(cc.expectedMerged), cc.params[0], false, []string(nil)).
|
||||
Return(&cc.expectedApps[0], nil)
|
||||
|
||||
r := ApplicationSetReconciler{
|
||||
@@ -317,7 +300,17 @@ func TestMergeTemplateApplications(t *testing.T) {
|
||||
KubeClientset: kubefake.NewSimpleClientset(),
|
||||
}
|
||||
|
||||
got, _, _ := r.generateApplications(*appSet)
|
||||
got, _, _ := r.generateApplications(v1alpha1.ApplicationSet{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "name",
|
||||
Namespace: "namespace",
|
||||
},
|
||||
Spec: v1alpha1.ApplicationSetSpec{
|
||||
Generators: []v1alpha1.ApplicationSetGenerator{generator},
|
||||
Template: cc.template,
|
||||
},
|
||||
},
|
||||
)
|
||||
|
||||
assert.Equal(t, cc.expectedApps, got)
|
||||
})
|
||||
@@ -2003,7 +1996,7 @@ func TestReconcilerValidationErrorBehaviour(t *testing.T) {
|
||||
// Verify that on validation error, no error is returned, but the object is requeued
|
||||
res, err := r.Reconcile(context.Background(), req)
|
||||
assert.Nil(t, err)
|
||||
assert.True(t, res.RequeueAfter == 0)
|
||||
assert.True(t, res.RequeueAfter == ReconcileRequeueOnValidationError)
|
||||
|
||||
var app v1alpha1.Application
|
||||
|
||||
@@ -2169,7 +2162,7 @@ func applicationsUpdateSyncPolicyTest(t *testing.T, applicationsSyncPolicy v1alp
|
||||
assert.Nil(t, err)
|
||||
|
||||
retrievedApplicationSet.Spec.Template.Annotations = map[string]string{"annotation-key": "annotation-value"}
|
||||
retrievedApplicationSet.Spec.Template.Labels = map[string]string{"argocd.argoproj.io/application-set-name": "name", "label-key": "label-value"}
|
||||
retrievedApplicationSet.Spec.Template.Labels = map[string]string{"label-key": "label-value"}
|
||||
|
||||
retrievedApplicationSet.Spec.Template.Spec.Source.Helm = &v1alpha1.ApplicationSourceHelm{
|
||||
Values: "global.test: test",
|
||||
@@ -2197,7 +2190,6 @@ func TestUpdateNotPerformedWithSyncPolicyCreateOnly(t *testing.T) {
|
||||
|
||||
assert.Nil(t, app.Spec.Source.Helm)
|
||||
assert.Nil(t, app.ObjectMeta.Annotations)
|
||||
assert.Equal(t, map[string]string{"argocd.argoproj.io/application-set-name": "name"}, app.ObjectMeta.Labels)
|
||||
}
|
||||
|
||||
func TestUpdateNotPerformedWithSyncPolicyCreateDelete(t *testing.T) {
|
||||
@@ -2208,7 +2200,6 @@ func TestUpdateNotPerformedWithSyncPolicyCreateDelete(t *testing.T) {
|
||||
|
||||
assert.Nil(t, app.Spec.Source.Helm)
|
||||
assert.Nil(t, app.ObjectMeta.Annotations)
|
||||
assert.Equal(t, map[string]string{"argocd.argoproj.io/application-set-name": "name"}, app.ObjectMeta.Labels)
|
||||
}
|
||||
|
||||
func TestUpdatePerformedWithSyncPolicyCreateUpdate(t *testing.T) {
|
||||
@@ -2219,7 +2210,7 @@ func TestUpdatePerformedWithSyncPolicyCreateUpdate(t *testing.T) {
|
||||
|
||||
assert.Equal(t, "global.test: test", app.Spec.Source.Helm.Values)
|
||||
assert.Equal(t, map[string]string{"annotation-key": "annotation-value"}, app.ObjectMeta.Annotations)
|
||||
assert.Equal(t, map[string]string{"argocd.argoproj.io/application-set-name": "name", "label-key": "label-value"}, app.ObjectMeta.Labels)
|
||||
assert.Equal(t, map[string]string{"label-key": "label-value"}, app.ObjectMeta.Labels)
|
||||
}
|
||||
|
||||
func TestUpdatePerformedWithSyncPolicySync(t *testing.T) {
|
||||
@@ -2230,7 +2221,7 @@ func TestUpdatePerformedWithSyncPolicySync(t *testing.T) {
|
||||
|
||||
assert.Equal(t, "global.test: test", app.Spec.Source.Helm.Values)
|
||||
assert.Equal(t, map[string]string{"annotation-key": "annotation-value"}, app.ObjectMeta.Annotations)
|
||||
assert.Equal(t, map[string]string{"argocd.argoproj.io/application-set-name": "name", "label-key": "label-value"}, app.ObjectMeta.Labels)
|
||||
assert.Equal(t, map[string]string{"label-key": "label-value"}, app.ObjectMeta.Labels)
|
||||
}
|
||||
|
||||
func TestUpdatePerformedWithSyncPolicyCreateOnlyAndAllowPolicyOverrideFalse(t *testing.T) {
|
||||
@@ -2241,7 +2232,7 @@ func TestUpdatePerformedWithSyncPolicyCreateOnlyAndAllowPolicyOverrideFalse(t *t
|
||||
|
||||
assert.Equal(t, "global.test: test", app.Spec.Source.Helm.Values)
|
||||
assert.Equal(t, map[string]string{"annotation-key": "annotation-value"}, app.ObjectMeta.Annotations)
|
||||
assert.Equal(t, map[string]string{"argocd.argoproj.io/application-set-name": "name", "label-key": "label-value"}, app.ObjectMeta.Labels)
|
||||
assert.Equal(t, map[string]string{"label-key": "label-value"}, app.ObjectMeta.Labels)
|
||||
}
|
||||
|
||||
func applicationsDeleteSyncPolicyTest(t *testing.T, applicationsSyncPolicy v1alpha1.ApplicationsSyncPolicy, recordBuffer int, allowPolicyOverride bool) v1alpha1.ApplicationList {
|
||||
@@ -2449,8 +2440,7 @@ func TestGenerateAppsUsingPullRequestGenerator(t *testing.T) {
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "AppSet-branch1-1",
|
||||
Labels: map[string]string{
|
||||
"app1": "label1",
|
||||
LabelKeyAppSetInstance: "",
|
||||
"app1": "label1",
|
||||
},
|
||||
},
|
||||
Spec: v1alpha1.ApplicationSpec{
|
||||
|
||||
@@ -153,7 +153,7 @@ func InterpolateGenerator(requestedGenerator *argoprojiov1alpha1.ApplicationSetG
|
||||
interpolatedGenerator, err := render.RenderGeneratorParams(requestedGenerator, params, useGoTemplate, goTemplateOptions)
|
||||
if err != nil {
|
||||
log.WithError(err).WithField("interpolatedGenerator", interpolatedGenerator).Error("error interpolating generator with other generator's parameter")
|
||||
return *interpolatedGenerator, err
|
||||
return argoprojiov1alpha1.ApplicationSetGenerator{}, err
|
||||
}
|
||||
|
||||
return *interpolatedGenerator, nil
|
||||
|
||||
@@ -501,3 +501,60 @@ func TestInterpolateGenerator_go(t *testing.T) {
|
||||
assert.Equal(t, "production_01/west", interpolatedGenerator.Git.Files[0].Path)
|
||||
assert.Equal(t, "https://production-01.example.com", interpolatedGenerator.Git.Files[1].Path)
|
||||
}
|
||||
|
||||
func TestInterpolateGeneratorError(t *testing.T) {
|
||||
type args struct {
|
||||
requestedGenerator *argov1alpha1.ApplicationSetGenerator
|
||||
params map[string]interface{}
|
||||
useGoTemplate bool
|
||||
goTemplateOptions []string
|
||||
}
|
||||
tests := []struct {
|
||||
name string
|
||||
args args
|
||||
want argov1alpha1.ApplicationSetGenerator
|
||||
expectedErrStr string
|
||||
}{
|
||||
{name: "Empty Gen", args: args{
|
||||
requestedGenerator: nil,
|
||||
params: nil,
|
||||
useGoTemplate: false,
|
||||
goTemplateOptions: nil,
|
||||
}, want: argov1alpha1.ApplicationSetGenerator{}, expectedErrStr: "generator is empty"},
|
||||
{name: "No Params", args: args{
|
||||
requestedGenerator: &argov1alpha1.ApplicationSetGenerator{},
|
||||
params: map[string]interface{}{},
|
||||
useGoTemplate: false,
|
||||
goTemplateOptions: nil,
|
||||
}, want: argov1alpha1.ApplicationSetGenerator{}, expectedErrStr: ""},
|
||||
{name: "Error templating", args: args{
|
||||
requestedGenerator: &argov1alpha1.ApplicationSetGenerator{Git: &argov1alpha1.GitGenerator{
|
||||
RepoURL: "foo",
|
||||
Files: []argov1alpha1.GitFileGeneratorItem{{Path: "bar/"}},
|
||||
Revision: "main",
|
||||
Values: map[string]string{
|
||||
"git_test": "{{ toPrettyJson . }}",
|
||||
"selection": "{{ default .override .test }}",
|
||||
"resolved": "{{ index .rmap (default .override .test) }}",
|
||||
},
|
||||
}},
|
||||
params: map[string]interface{}{
|
||||
"name": "in-cluster",
|
||||
"override": "foo",
|
||||
},
|
||||
useGoTemplate: true,
|
||||
goTemplateOptions: []string{},
|
||||
}, want: argov1alpha1.ApplicationSetGenerator{}, expectedErrStr: "failed to replace parameters in generator: failed to execute go template {{ index .rmap (default .override .test) }}: template: :1:3: executing \"\" at <index .rmap (default .override .test)>: error calling index: index of untyped nil"},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
got, err := InterpolateGenerator(tt.args.requestedGenerator, tt.args.params, tt.args.useGoTemplate, tt.args.goTemplateOptions)
|
||||
if tt.expectedErrStr != "" {
|
||||
assert.EqualError(t, err, tt.expectedErrStr)
|
||||
} else {
|
||||
require.NoError(t, err)
|
||||
}
|
||||
assert.Equalf(t, tt.want, got, "InterpolateGenerator(%v, %v, %v, %v)", tt.args.requestedGenerator, tt.args.params, tt.args.useGoTemplate, tt.args.goTemplateOptions)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@@ -26,11 +26,13 @@ func NewGiteaService(ctx context.Context, token, url, owner, repo string, insecu
|
||||
if insecure {
|
||||
cookieJar, _ := cookiejar.New(nil)
|
||||
|
||||
tr := http.DefaultTransport.(*http.Transport).Clone()
|
||||
tr.TLSClientConfig = &tls.Config{InsecureSkipVerify: true}
|
||||
|
||||
httpClient = &http.Client{
|
||||
Jar: cookieJar,
|
||||
Transport: &http.Transport{
|
||||
TLSClientConfig: &tls.Config{InsecureSkipVerify: true},
|
||||
}}
|
||||
Jar: cookieJar,
|
||||
Transport: tr,
|
||||
}
|
||||
}
|
||||
client, err := gitea.NewClient(url, gitea.SetToken(token), gitea.SetHTTPClient(httpClient))
|
||||
if err != nil {
|
||||
|
||||
@@ -32,9 +32,9 @@ func NewGitLabService(ctx context.Context, token, url, project string, labels []
|
||||
token = os.Getenv("GITLAB_TOKEN")
|
||||
}
|
||||
|
||||
tr := &http.Transport{
|
||||
TLSClientConfig: utils.GetTlsConfig(scmRootCAPath, insecure),
|
||||
}
|
||||
tr := http.DefaultTransport.(*http.Transport).Clone()
|
||||
tr.TLSClientConfig = utils.GetTlsConfig(scmRootCAPath, insecure)
|
||||
|
||||
retryClient := retryablehttp.NewClient()
|
||||
retryClient.HTTPClient.Transport = tr
|
||||
|
||||
|
||||
@@ -3,6 +3,7 @@ package scm_provider
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"io"
|
||||
|
||||
"github.com/argoproj/argo-cd/v2/applicationset/utils"
|
||||
bitbucketv1 "github.com/gfleury/go-bitbucket-v1"
|
||||
@@ -183,8 +184,9 @@ func (b *BitbucketServerProvider) listBranches(repo *Repository) ([]bitbucketv1.
|
||||
|
||||
func (b *BitbucketServerProvider) getDefaultBranch(org string, repo string) (*bitbucketv1.Branch, error) {
|
||||
response, err := b.client.DefaultApi.GetDefaultBranch(org, repo)
|
||||
if response != nil && response.StatusCode == 404 {
|
||||
// There's no default branch i.e. empty repo, not an error
|
||||
// The API will return 404 if a default branch is set but doesn't exist. In case the repo is empty and default branch is unset,
|
||||
// we will get an EOF and a nil response.
|
||||
if (response != nil && response.StatusCode == 404) || (response == nil && err == io.EOF) {
|
||||
return nil, nil
|
||||
}
|
||||
if err != nil {
|
||||
|
||||
@@ -365,6 +365,28 @@ func TestGetBranchesMissingDefault(t *testing.T) {
|
||||
assert.Empty(t, repos)
|
||||
}
|
||||
|
||||
func TestGetBranchesEmptyRepo(t *testing.T) {
|
||||
ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
assert.Empty(t, r.Header.Get("Authorization"))
|
||||
switch r.RequestURI {
|
||||
case "/rest/api/1.0/projects/PROJECT/repos/REPO/branches/default":
|
||||
return
|
||||
}
|
||||
}))
|
||||
defer ts.Close()
|
||||
provider, err := NewBitbucketServerProviderNoAuth(context.Background(), ts.URL, "PROJECT", false)
|
||||
assert.NoError(t, err)
|
||||
repos, err := provider.GetBranches(context.Background(), &Repository{
|
||||
Organization: "PROJECT",
|
||||
Repository: "REPO",
|
||||
URL: "ssh://git@mycompany.bitbucket.org/PROJECT/REPO.git",
|
||||
Labels: []string{},
|
||||
RepositoryId: 1,
|
||||
})
|
||||
assert.Empty(t, repos)
|
||||
assert.NoError(t, err)
|
||||
}
|
||||
|
||||
func TestGetBranchesErrorDefaultBranch(t *testing.T) {
|
||||
ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
assert.Empty(t, r.Header.Get("Authorization"))
|
||||
|
||||
@@ -27,11 +27,13 @@ func NewGiteaProvider(ctx context.Context, owner, token, url string, allBranches
|
||||
if insecure {
|
||||
cookieJar, _ := cookiejar.New(nil)
|
||||
|
||||
tr := http.DefaultTransport.(*http.Transport).Clone()
|
||||
tr.TLSClientConfig = &tls.Config{InsecureSkipVerify: true}
|
||||
|
||||
httpClient = &http.Client{
|
||||
Jar: cookieJar,
|
||||
Transport: &http.Transport{
|
||||
TLSClientConfig: &tls.Config{InsecureSkipVerify: true},
|
||||
}}
|
||||
Jar: cookieJar,
|
||||
Transport: tr,
|
||||
}
|
||||
}
|
||||
client, err := gitea.NewClient(url, gitea.SetToken(token), gitea.SetHTTPClient(httpClient))
|
||||
if err != nil {
|
||||
|
||||
@@ -28,9 +28,9 @@ func NewGitlabProvider(ctx context.Context, organization string, token string, u
|
||||
}
|
||||
var client *gitlab.Client
|
||||
|
||||
tr := &http.Transport{
|
||||
TLSClientConfig: utils.GetTlsConfig(scmRootCAPath, insecure),
|
||||
}
|
||||
tr := http.DefaultTransport.(*http.Transport).Clone()
|
||||
tr.TLSClientConfig = utils.GetTlsConfig(scmRootCAPath, insecure)
|
||||
|
||||
retryClient := retryablehttp.NewClient()
|
||||
retryClient.HTTPClient.Transport = tr
|
||||
|
||||
|
||||
@@ -17,6 +17,7 @@ import (
|
||||
|
||||
"github.com/Masterminds/sprig/v3"
|
||||
"github.com/valyala/fasttemplate"
|
||||
"sigs.k8s.io/yaml"
|
||||
|
||||
log "github.com/sirupsen/logrus"
|
||||
|
||||
@@ -51,6 +52,22 @@ func copyUnexported(copy, original reflect.Value) {
|
||||
copyValueIntoUnexported(copy, unexported)
|
||||
}
|
||||
|
||||
func IsJSONStr(str string) bool {
|
||||
str = strings.TrimSpace(str)
|
||||
return len(str) > 0 && str[0] == '{'
|
||||
}
|
||||
|
||||
func ConvertYAMLToJSON(str string) (string, error) {
|
||||
if !IsJSONStr(str) {
|
||||
jsonStr, err := yaml.YAMLToJSON([]byte(str))
|
||||
if err != nil {
|
||||
return str, err
|
||||
}
|
||||
return string(jsonStr), nil
|
||||
}
|
||||
return str, nil
|
||||
}
|
||||
|
||||
// This function is in charge of searching all String fields of the object recursively and apply templating
|
||||
// thanks to https://gist.github.com/randallmlough/1fd78ec8a1034916ca52281e3b886dc7
|
||||
func (r *Render) deeplyReplace(copy, original reflect.Value, replaceMap map[string]interface{}, useGoTemplate bool, goTemplateOptions []string) error {
|
||||
@@ -86,11 +103,18 @@ func (r *Render) deeplyReplace(copy, original reflect.Value, replaceMap map[stri
|
||||
originalValue := original.Elem()
|
||||
// Create a new object. Now new gives us a pointer, but we want the value it
|
||||
// points to, so we have to call Elem() to unwrap it
|
||||
copyValue := reflect.New(originalValue.Type()).Elem()
|
||||
if err := r.deeplyReplace(copyValue, originalValue, replaceMap, useGoTemplate, goTemplateOptions); err != nil {
|
||||
return err
|
||||
|
||||
if originalValue.IsValid() {
|
||||
reflectType := originalValue.Type()
|
||||
|
||||
reflectValue := reflect.New(reflectType)
|
||||
|
||||
copyValue := reflectValue.Elem()
|
||||
if err := r.deeplyReplace(copyValue, originalValue, replaceMap, useGoTemplate, goTemplateOptions); err != nil {
|
||||
return err
|
||||
}
|
||||
copy.Set(copyValue)
|
||||
}
|
||||
copy.Set(copyValue)
|
||||
|
||||
// If it is a struct we translate each field
|
||||
case reflect.Struct:
|
||||
@@ -99,10 +123,14 @@ func (r *Render) deeplyReplace(copy, original reflect.Value, replaceMap map[stri
|
||||
// specific case time
|
||||
if currentType == "time.Time" {
|
||||
copy.Field(i).Set(original.Field(i))
|
||||
} else if currentType == "Raw.k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1" {
|
||||
} else if currentType == "Raw.k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1" || currentType == "Raw.k8s.io/apimachinery/pkg/runtime" {
|
||||
var unmarshaled interface{}
|
||||
originalBytes := original.Field(i).Bytes()
|
||||
err := json.Unmarshal(originalBytes, &unmarshaled)
|
||||
convertedToJson, err := ConvertYAMLToJSON(string(originalBytes))
|
||||
if err != nil {
|
||||
return fmt.Errorf("error while converting template to json %q: %w", convertedToJson, err)
|
||||
}
|
||||
err = json.Unmarshal([]byte(convertedToJson), &unmarshaled)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to unmarshal JSON field: %w", err)
|
||||
}
|
||||
|
||||
@@ -2,6 +2,7 @@ package utils
|
||||
|
||||
import (
|
||||
"crypto/x509"
|
||||
"encoding/json"
|
||||
"os"
|
||||
"path"
|
||||
"testing"
|
||||
@@ -198,6 +199,113 @@ func TestRenderTemplateParams(t *testing.T) {
|
||||
|
||||
}
|
||||
|
||||
func TestRenderHelmValuesObjectJson(t *testing.T) {
|
||||
|
||||
params := map[string]interface{}{
|
||||
"test": "Hello world",
|
||||
}
|
||||
|
||||
application := &argoappsv1.Application{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Annotations: map[string]string{"annotation-key": "annotation-value", "annotation-key2": "annotation-value2"},
|
||||
Labels: map[string]string{"label-key": "label-value", "label-key2": "label-value2"},
|
||||
CreationTimestamp: metav1.NewTime(time.Now()),
|
||||
UID: types.UID("d546da12-06b7-4f9a-8ea2-3adb16a20e2b"),
|
||||
Name: "application-one",
|
||||
Namespace: "default",
|
||||
},
|
||||
Spec: argoappsv1.ApplicationSpec{
|
||||
Source: &argoappsv1.ApplicationSource{
|
||||
Path: "",
|
||||
RepoURL: "",
|
||||
TargetRevision: "",
|
||||
Chart: "",
|
||||
Helm: &argoappsv1.ApplicationSourceHelm{
|
||||
ValuesObject: &runtime.RawExtension{
|
||||
Raw: []byte(`{
|
||||
"some": {
|
||||
"string": "{{.test}}"
|
||||
}
|
||||
}`),
|
||||
},
|
||||
},
|
||||
},
|
||||
Destination: argoappsv1.ApplicationDestination{
|
||||
Server: "",
|
||||
Namespace: "",
|
||||
Name: "",
|
||||
},
|
||||
Project: "",
|
||||
},
|
||||
}
|
||||
|
||||
// Render the cloned application, into a new application
|
||||
render := Render{}
|
||||
newApplication, err := render.RenderTemplateParams(application, nil, params, true, []string{})
|
||||
|
||||
assert.NoError(t, err)
|
||||
assert.NotNil(t, newApplication)
|
||||
|
||||
var unmarshaled interface{}
|
||||
err = json.Unmarshal(newApplication.Spec.Source.Helm.ValuesObject.Raw, &unmarshaled)
|
||||
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, unmarshaled.(map[string]interface{})["some"].(map[string]interface{})["string"], "Hello world")
|
||||
|
||||
}
|
||||
|
||||
func TestRenderHelmValuesObjectYaml(t *testing.T) {
|
||||
|
||||
params := map[string]interface{}{
|
||||
"test": "Hello world",
|
||||
}
|
||||
|
||||
application := &argoappsv1.Application{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Annotations: map[string]string{"annotation-key": "annotation-value", "annotation-key2": "annotation-value2"},
|
||||
Labels: map[string]string{"label-key": "label-value", "label-key2": "label-value2"},
|
||||
CreationTimestamp: metav1.NewTime(time.Now()),
|
||||
UID: types.UID("d546da12-06b7-4f9a-8ea2-3adb16a20e2b"),
|
||||
Name: "application-one",
|
||||
Namespace: "default",
|
||||
},
|
||||
Spec: argoappsv1.ApplicationSpec{
|
||||
Source: &argoappsv1.ApplicationSource{
|
||||
Path: "",
|
||||
RepoURL: "",
|
||||
TargetRevision: "",
|
||||
Chart: "",
|
||||
Helm: &argoappsv1.ApplicationSourceHelm{
|
||||
ValuesObject: &runtime.RawExtension{
|
||||
Raw: []byte(`some:
|
||||
string: "{{.test}}"`),
|
||||
},
|
||||
},
|
||||
},
|
||||
Destination: argoappsv1.ApplicationDestination{
|
||||
Server: "",
|
||||
Namespace: "",
|
||||
Name: "",
|
||||
},
|
||||
Project: "",
|
||||
},
|
||||
}
|
||||
|
||||
// Render the cloned application, into a new application
|
||||
render := Render{}
|
||||
newApplication, err := render.RenderTemplateParams(application, nil, params, true, []string{})
|
||||
|
||||
assert.NoError(t, err)
|
||||
assert.NotNil(t, newApplication)
|
||||
|
||||
var unmarshaled interface{}
|
||||
err = json.Unmarshal(newApplication.Spec.Source.Helm.ValuesObject.Raw, &unmarshaled)
|
||||
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, unmarshaled.(map[string]interface{})["some"].(map[string]interface{})["string"], "Hello world")
|
||||
|
||||
}
|
||||
|
||||
func TestRenderTemplateParamsGoTemplate(t *testing.T) {
|
||||
|
||||
// Believe it or not, this is actually less complex than the equivalent solution using reflection
|
||||
|
||||
@@ -82,6 +82,8 @@ func NewCommand() *cobra.Command {
|
||||
allowOutOfBoundsSymlinks bool
|
||||
streamedManifestMaxTarSize string
|
||||
streamedManifestMaxExtractedSize string
|
||||
helmManifestMaxExtractedSize string
|
||||
disableManifestMaxExtractedSize bool
|
||||
)
|
||||
var command = cobra.Command{
|
||||
Use: cliName,
|
||||
@@ -120,6 +122,9 @@ func NewCommand() *cobra.Command {
|
||||
streamedManifestMaxExtractedSizeQuantity, err := resource.ParseQuantity(streamedManifestMaxExtractedSize)
|
||||
errors.CheckError(err)
|
||||
|
||||
helmManifestMaxExtractedSizeQuantity, err := resource.ParseQuantity(helmManifestMaxExtractedSize)
|
||||
errors.CheckError(err)
|
||||
|
||||
askPassServer := askpass.NewServer()
|
||||
metricsServer := metrics.NewMetricsServer()
|
||||
cacheutil.CollectMetrics(redisClient, metricsServer)
|
||||
@@ -134,6 +139,7 @@ func NewCommand() *cobra.Command {
|
||||
AllowOutOfBoundsSymlinks: allowOutOfBoundsSymlinks,
|
||||
StreamedManifestMaxExtractedSize: streamedManifestMaxExtractedSizeQuantity.ToDec().Value(),
|
||||
StreamedManifestMaxTarSize: streamedManifestMaxTarSizeQuantity.ToDec().Value(),
|
||||
HelmManifestMaxExtractedSize: helmManifestMaxExtractedSizeQuantity.ToDec().Value(),
|
||||
}, askPassServer)
|
||||
errors.CheckError(err)
|
||||
|
||||
@@ -216,6 +222,8 @@ func NewCommand() *cobra.Command {
|
||||
command.Flags().BoolVar(&allowOutOfBoundsSymlinks, "allow-oob-symlinks", env.ParseBoolFromEnv("ARGOCD_REPO_SERVER_ALLOW_OUT_OF_BOUNDS_SYMLINKS", false), "Allow out-of-bounds symlinks in repositories (not recommended)")
|
||||
command.Flags().StringVar(&streamedManifestMaxTarSize, "streamed-manifest-max-tar-size", env.StringFromEnv("ARGOCD_REPO_SERVER_STREAMED_MANIFEST_MAX_TAR_SIZE", "100M"), "Maximum size of streamed manifest archives")
|
||||
command.Flags().StringVar(&streamedManifestMaxExtractedSize, "streamed-manifest-max-extracted-size", env.StringFromEnv("ARGOCD_REPO_SERVER_STREAMED_MANIFEST_MAX_EXTRACTED_SIZE", "1G"), "Maximum size of streamed manifest archives when extracted")
|
||||
command.Flags().StringVar(&helmManifestMaxExtractedSize, "helm-manifest-max-extracted-size", env.StringFromEnv("ARGOCD_REPO_SERVER_HELM_MANIFEST_MAX_EXTRACTED_SIZE", "1G"), "Maximum size of helm manifest archives when extracted")
|
||||
command.Flags().BoolVar(&disableManifestMaxExtractedSize, "disable-helm-manifest-max-extracted-size", env.ParseBoolFromEnv("ARGOCD_REPO_SERVER_DISABLE_HELM_MANIFEST_MAX_EXTRACTED_SIZE", false), "Disable maximum size of helm manifest archives when extracted")
|
||||
tlsConfigCustomizerSrc = tls.AddTLSFlagsToCmd(&command)
|
||||
cacheSrc = reposervercache.AddCacheFlagsToCmd(&command, func(client *redis.Client) {
|
||||
redisClient = client
|
||||
|
||||
@@ -7,7 +7,6 @@ import (
|
||||
grpc_middleware "github.com/grpc-ecosystem/go-grpc-middleware"
|
||||
grpc_retry "github.com/grpc-ecosystem/go-grpc-middleware/retry"
|
||||
log "github.com/sirupsen/logrus"
|
||||
"go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc"
|
||||
"google.golang.org/grpc"
|
||||
"google.golang.org/grpc/credentials/insecure"
|
||||
|
||||
@@ -47,8 +46,8 @@ func NewConnection(address string) (*grpc.ClientConn, error) {
|
||||
grpc.WithStreamInterceptor(grpc_retry.StreamClientInterceptor(retryOpts...)),
|
||||
grpc.WithUnaryInterceptor(grpc_middleware.ChainUnaryClient(unaryInterceptors...)),
|
||||
grpc.WithDefaultCallOptions(grpc.MaxCallRecvMsgSize(MaxGRPCMessageSize), grpc.MaxCallSendMsgSize(MaxGRPCMessageSize)),
|
||||
grpc.WithUnaryInterceptor(otelgrpc.UnaryClientInterceptor()),
|
||||
grpc.WithStreamInterceptor(otelgrpc.StreamClientInterceptor()),
|
||||
grpc.WithUnaryInterceptor(grpc_util.OTELUnaryClientInterceptor()),
|
||||
grpc.WithStreamInterceptor(grpc_util.OTELStreamClientInterceptor()),
|
||||
}
|
||||
|
||||
dialOpts = append(dialOpts, grpc.WithTransportCredentials(insecure.NewCredentials()))
|
||||
|
||||
@@ -4,6 +4,7 @@ import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
v1 "k8s.io/api/core/v1"
|
||||
"reflect"
|
||||
"strings"
|
||||
"time"
|
||||
@@ -335,6 +336,10 @@ func verifyGnuPGSignature(revision string, project *v1alpha1.AppProject, manifes
|
||||
return conditions
|
||||
}
|
||||
|
||||
func isManagedNamespace(ns *unstructured.Unstructured, app *v1alpha1.Application) bool {
|
||||
return ns != nil && ns.GetKind() == kubeutil.NamespaceKind && ns.GetName() == app.Spec.Destination.Namespace && app.Spec.SyncPolicy != nil && app.Spec.SyncPolicy.ManagedNamespaceMetadata != nil
|
||||
}
|
||||
|
||||
// CompareAppState compares application git state to the live app state, using the specified
|
||||
// revision and supplied source. If revision or overrides are empty, then compares against
|
||||
// revision and overrides in the app spec.
|
||||
@@ -494,6 +499,35 @@ func (m *appStateManager) CompareAppState(app *v1alpha1.Application, project *v1
|
||||
LastTransitionTime: &now,
|
||||
})
|
||||
}
|
||||
|
||||
// For the case when a namespace is managed with `managedNamespaceMetadata` AND it has resource tracking
|
||||
// enabled (e.g. someone manually adds resource tracking labels or annotations), we need to do some
|
||||
// bookkeeping in order to prevent the managed namespace from being pruned.
|
||||
//
|
||||
// Live namespaces which are managed namespaces (i.e. application namespaces which are managed with
|
||||
// CreateNamespace=true and has non-nil managedNamespaceMetadata) will (usually) not have a corresponding
|
||||
// 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.
|
||||
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)
|
||||
|
||||
if err != nil {
|
||||
conditions = append(conditions, v1alpha1.ApplicationCondition{Type: v1alpha1.ApplicationConditionComparisonError, Message: err.Error(), LastTransitionTime: &now})
|
||||
failedToLoadObjs = true
|
||||
continue
|
||||
}
|
||||
|
||||
// No need to care about the return value here, we just want the modified managedNs
|
||||
_, err = syncNamespace(m.resourceTracking, appLabelKey, trackingMethod, app.Name, app.Spec.SyncPolicy)(managedNs, liveObj)
|
||||
if err != nil {
|
||||
conditions = append(conditions, v1alpha1.ApplicationCondition{Type: v1alpha1.ApplicationConditionComparisonError, Message: err.Error(), LastTransitionTime: &now})
|
||||
failedToLoadObjs = true
|
||||
} else {
|
||||
targetObjs = append(targetObjs, managedNs)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -588,12 +622,22 @@ func (m *appStateManager) CompareAppState(app *v1alpha1.Application, project *v1
|
||||
} else {
|
||||
diffResult = diff.DiffResult{Modified: false, NormalizedLive: []byte("{}"), PredictedLive: []byte("{}")}
|
||||
}
|
||||
|
||||
// For the case when a namespace is managed with `managedNamespaceMetadata` AND it has resource tracking
|
||||
// enabled (e.g. someone manually adds resource tracking labels or annotations), we need to do some
|
||||
// bookkeeping in order to ensure that it's not considered `OutOfSync` (since it does not exist in source
|
||||
// control).
|
||||
//
|
||||
// This is in addition to the bookkeeping we do (see `isManagedNamespace` and its references) to prevent said
|
||||
// namespace from being pruned.
|
||||
isManagedNs := isManagedNamespace(targetObj, app) && liveObj == nil
|
||||
|
||||
if resState.Hook || ignore.Ignore(obj) || (targetObj != nil && hookutil.Skip(targetObj)) || !isSelfReferencedObj {
|
||||
// For resource hooks, skipped resources or objects that may have
|
||||
// been created by another controller with annotations copied from
|
||||
// the source object, don't store sync status, and do not affect
|
||||
// overall sync status
|
||||
} else if diffResult.Modified || targetObj == nil || liveObj == nil {
|
||||
} else if !isManagedNs && (diffResult.Modified || targetObj == nil || liveObj == nil) {
|
||||
// Set resource state to OutOfSync since one of the following is true:
|
||||
// * target and live resource are different
|
||||
// * target resource not defined and live resource is extra
|
||||
|
||||
@@ -433,6 +433,47 @@ func TestCompareAppStateDuplicatedNamespacedResources(t *testing.T) {
|
||||
assert.Equal(t, 4, len(compRes.resources))
|
||||
}
|
||||
|
||||
func TestCompareAppStateManagedNamespaceMetadataWithLiveNsDoesNotGetPruned(t *testing.T) {
|
||||
app := newFakeApp()
|
||||
app.Spec.SyncPolicy = &argoappv1.SyncPolicy{
|
||||
ManagedNamespaceMetadata: &argoappv1.ManagedNamespaceMetadata{
|
||||
Labels: nil,
|
||||
Annotations: nil,
|
||||
},
|
||||
}
|
||||
|
||||
ns := NewNamespace()
|
||||
ns.SetName(test.FakeDestNamespace)
|
||||
ns.SetNamespace(test.FakeDestNamespace)
|
||||
ns.SetAnnotations(map[string]string{"argocd.argoproj.io/sync-options": "ServerSideApply=true"})
|
||||
|
||||
data := fakeData{
|
||||
manifestResponse: &apiclient.ManifestResponse{
|
||||
Manifests: []string{},
|
||||
Namespace: test.FakeDestNamespace,
|
||||
Server: test.FakeClusterURL,
|
||||
Revision: "abc123",
|
||||
},
|
||||
managedLiveObjs: map[kube.ResourceKey]*unstructured.Unstructured{
|
||||
kube.GetResourceKey(ns): ns,
|
||||
},
|
||||
}
|
||||
ctrl := newFakeController(&data)
|
||||
compRes := ctrl.appStateManager.CompareAppState(app, &defaultProj, []string{}, app.Spec.Sources, false, false, nil, false)
|
||||
|
||||
assert.NotNil(t, compRes)
|
||||
assert.Equal(t, 0, len(app.Status.Conditions))
|
||||
assert.NotNil(t, compRes)
|
||||
assert.NotNil(t, compRes.syncStatus)
|
||||
// Ensure that ns does not get pruned
|
||||
assert.NotNil(t, compRes.reconciliationResult.Target[0])
|
||||
assert.Equal(t, compRes.reconciliationResult.Target[0].GetName(), ns.GetName())
|
||||
assert.Equal(t, compRes.reconciliationResult.Target[0].GetAnnotations(), ns.GetAnnotations())
|
||||
assert.Equal(t, compRes.reconciliationResult.Target[0].GetLabels(), ns.GetLabels())
|
||||
assert.Len(t, compRes.resources, 1)
|
||||
assert.Len(t, compRes.managedResources, 1)
|
||||
}
|
||||
|
||||
var defaultProj = argoappv1.AppProject{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "default",
|
||||
|
||||
@@ -214,6 +214,9 @@ spec:
|
||||
kind: Deployment
|
||||
jsonPointers:
|
||||
- /spec/replicas
|
||||
- kind: ConfigMap
|
||||
jqPathExpressions:
|
||||
- '.data["config.yaml"].auth'
|
||||
# for the specified managedFields managers
|
||||
- group: "*"
|
||||
kind: "*"
|
||||
|
||||
@@ -38,16 +38,16 @@ With the ApplicationSet v0.1.0 release, one could *only* specify `url` and `clus
|
||||
spec:
|
||||
generators:
|
||||
- list:
|
||||
elements:
|
||||
# v0.1.0 form - requires cluster/url keys:
|
||||
- cluster: engineering-dev
|
||||
url: https://kubernetes.default.svc
|
||||
values:
|
||||
additional: value
|
||||
# v0.2.0+ form - does not require cluster/URL keys
|
||||
# (but they are still supported).
|
||||
- staging: "true"
|
||||
gitRepo: https://kubernetes.default.svc
|
||||
elements:
|
||||
# v0.1.0 form - requires cluster/url keys:
|
||||
- cluster: engineering-dev
|
||||
url: https://kubernetes.default.svc
|
||||
values:
|
||||
additional: value
|
||||
# v0.2.0+ form - does not require cluster/URL keys
|
||||
# (but they are still supported).
|
||||
- staging: "true"
|
||||
gitRepo: https://kubernetes.default.svc
|
||||
# (...)
|
||||
```
|
||||
|
||||
@@ -74,7 +74,6 @@ spec:
|
||||
files:
|
||||
- path: applicationset/examples/list-generator/list-elementsYaml-example.yaml
|
||||
- list:
|
||||
elements: []
|
||||
elementsYaml: "{{ .key.components | toJson }}"
|
||||
template:
|
||||
metadata:
|
||||
|
||||
@@ -406,7 +406,7 @@ spec:
|
||||
* `sha`: The Git commit SHA for the branch.
|
||||
* `short_sha`: The abbreviated Git commit SHA for the branch (8 chars or the length of the `sha` if it's shorter).
|
||||
* `short_sha_7`: The abbreviated Git commit SHA for the branch (7 chars or the length of the `sha` if it's shorter).
|
||||
* `labels`: A comma-separated list of repository labels.
|
||||
* `labels`: A comma-separated list of repository labels in case of Gitea, repository topics in case of Gitlab and Github. Not supported by Bitbucket Cloud, Bitbucket Server, or Azure DevOps.
|
||||
* `branchNormalized`: The value of `branch` normalized to contain only lowercase alphanumeric characters, '-' or '.'.
|
||||
|
||||
## Pass additional key-value pairs via `values` field
|
||||
|
||||
@@ -103,6 +103,7 @@ generators' templating:
|
||||
- `{{ path.filename }}` becomes `{{ .path.filename }}`
|
||||
- `{{ path.filenameNormalized }}` becomes `{{ .path.filenameNormalized }}`
|
||||
- `{{ path[n] }}` becomes `{{ index .path.segments n }}`
|
||||
- `{{ values }}` if being used in the file generator becomes `{{ .values }}`
|
||||
|
||||
Here is an example:
|
||||
|
||||
|
||||
@@ -91,3 +91,8 @@ spec:
|
||||
# scoped to this project. Set the following field to `true` to restrict apps in this cluster to only clusters
|
||||
# scoped to this project.
|
||||
permitOnlyProjectScopedClusters: false
|
||||
|
||||
# When using Applications-in-any-namespace, this field determines which namespaces this AppProject permits
|
||||
# Applications to reside in. Details: https://argo-cd.readthedocs.io/en/stable/operator-manual/app-any-namespace/
|
||||
sourceNamespaces:
|
||||
- "argocd-apps-*"
|
||||
|
||||
@@ -16,7 +16,9 @@ argocd-repo-server [flags]
|
||||
--address string Listen on given address for incoming connections (default "0.0.0.0")
|
||||
--allow-oob-symlinks Allow out-of-bounds symlinks in repositories (not recommended)
|
||||
--default-cache-expiration duration Cache expiration default (default 24h0m0s)
|
||||
--disable-helm-manifest-max-extracted-size Disable maximum size of helm manifest archives when extracted
|
||||
--disable-tls Disable TLS on the gRPC endpoint
|
||||
--helm-manifest-max-extracted-size string Maximum size of helm manifest archives when extracted (default "1G")
|
||||
-h, --help help for argocd-repo-server
|
||||
--logformat string Set the logging format. One of: text|json (default "text")
|
||||
--loglevel string Set the logging level. One of: debug|info|warn|error (default "info")
|
||||
|
||||
2
go.mod
2
go.mod
@@ -20,7 +20,7 @@ require (
|
||||
github.com/bradleyfalzon/ghinstallation/v2 v2.5.0
|
||||
github.com/casbin/casbin/v2 v2.71.1
|
||||
github.com/coreos/go-oidc/v3 v3.6.0
|
||||
github.com/cyphar/filepath-securejoin v0.2.3
|
||||
github.com/cyphar/filepath-securejoin v0.2.4
|
||||
github.com/dustin/go-humanize v1.0.1
|
||||
github.com/evanphx/json-patch v5.6.0+incompatible
|
||||
github.com/fsnotify/fsnotify v1.6.0
|
||||
|
||||
3
go.sum
3
go.sum
@@ -275,8 +275,9 @@ github.com/creack/pty v1.1.7/go.mod h1:lj5s0c3V2DBrqTV7llrYr5NG6My20zk30Fl46Y7Do
|
||||
github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
|
||||
github.com/creack/pty v1.1.11 h1:07n33Z8lZxZ2qwegKbObQohDhXDQxiMMz1NOUGYlesw=
|
||||
github.com/creack/pty v1.1.11/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
|
||||
github.com/cyphar/filepath-securejoin v0.2.3 h1:YX6ebbZCZP7VkM3scTTokDgBL2TY741X51MTk3ycuNI=
|
||||
github.com/cyphar/filepath-securejoin v0.2.3/go.mod h1:aPGpWjXOXUn2NCNjFvBE6aRxGGx79pTxQpKOJNYHHl4=
|
||||
github.com/cyphar/filepath-securejoin v0.2.4 h1:Ugdm7cg7i6ZK6x3xDF1oEu1nfkyfH53EtKeQYTC3kyg=
|
||||
github.com/cyphar/filepath-securejoin v0.2.4/go.mod h1:aPGpWjXOXUn2NCNjFvBE6aRxGGx79pTxQpKOJNYHHl4=
|
||||
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
||||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
|
||||
@@ -5,7 +5,7 @@ kind: Kustomization
|
||||
images:
|
||||
- name: quay.io/argoproj/argocd
|
||||
newName: quay.io/argoproj/argocd
|
||||
newTag: v2.8.1
|
||||
newTag: v2.8.4
|
||||
resources:
|
||||
- ./application-controller
|
||||
- ./dex
|
||||
|
||||
@@ -150,6 +150,18 @@ spec:
|
||||
key: reposerver.streamed.manifest.max.extracted.size
|
||||
name: argocd-cmd-params-cm
|
||||
optional: true
|
||||
- name: ARGOCD_REPO_SERVER_HELM_MANIFEST_MAX_EXTRACTED_SIZE
|
||||
valueFrom:
|
||||
configMapKeyRef:
|
||||
key: reposerver.helm.manifest.max.extracted.size
|
||||
name: argocd-cmd-params-cm
|
||||
optional: true
|
||||
- name: ARGOCD_REPO_SERVER_DISABLE_HELM_MANIFEST_MAX_EXTRACTED_SIZE
|
||||
valueFrom:
|
||||
configMapKeyRef:
|
||||
name: argocd-cmd-params-cm
|
||||
key: reposerver.disable.helm.manifest.max.extracted.size
|
||||
optional: true
|
||||
- name: ARGOCD_GIT_MODULES_ENABLED
|
||||
valueFrom:
|
||||
configMapKeyRef:
|
||||
|
||||
@@ -36,3 +36,15 @@ rules:
|
||||
- get
|
||||
- list
|
||||
- watch
|
||||
- apiGroups:
|
||||
- batch
|
||||
resources:
|
||||
- jobs
|
||||
verbs:
|
||||
- create # supports triggering jobs from UI
|
||||
- apiGroups:
|
||||
- argoproj.io
|
||||
resources:
|
||||
- workflows
|
||||
verbs:
|
||||
- create # supports triggering workflows from UI
|
||||
|
||||
@@ -18880,7 +18880,7 @@ spec:
|
||||
key: applicationsetcontroller.allowed.scm.providers
|
||||
name: argocd-cmd-params-cm
|
||||
optional: true
|
||||
image: quay.io/argoproj/argocd:v2.8.1
|
||||
image: quay.io/argoproj/argocd:v2.8.4
|
||||
imagePullPolicy: Always
|
||||
name: argocd-applicationset-controller
|
||||
ports:
|
||||
@@ -19156,6 +19156,18 @@ spec:
|
||||
key: reposerver.streamed.manifest.max.extracted.size
|
||||
name: argocd-cmd-params-cm
|
||||
optional: true
|
||||
- name: ARGOCD_REPO_SERVER_HELM_MANIFEST_MAX_EXTRACTED_SIZE
|
||||
valueFrom:
|
||||
configMapKeyRef:
|
||||
key: reposerver.helm.manifest.max.extracted.size
|
||||
name: argocd-cmd-params-cm
|
||||
optional: true
|
||||
- name: ARGOCD_REPO_SERVER_DISABLE_HELM_MANIFEST_MAX_EXTRACTED_SIZE
|
||||
valueFrom:
|
||||
configMapKeyRef:
|
||||
key: reposerver.disable.helm.manifest.max.extracted.size
|
||||
name: argocd-cmd-params-cm
|
||||
optional: true
|
||||
- name: ARGOCD_GIT_MODULES_ENABLED
|
||||
valueFrom:
|
||||
configMapKeyRef:
|
||||
@@ -19168,7 +19180,7 @@ spec:
|
||||
value: /helm-working-dir
|
||||
- name: HELM_DATA_HOME
|
||||
value: /helm-working-dir
|
||||
image: quay.io/argoproj/argocd:v2.8.1
|
||||
image: quay.io/argoproj/argocd:v2.8.4
|
||||
imagePullPolicy: Always
|
||||
livenessProbe:
|
||||
failureThreshold: 3
|
||||
@@ -19220,7 +19232,7 @@ spec:
|
||||
- -n
|
||||
- /usr/local/bin/argocd
|
||||
- /var/run/argocd/argocd-cmp-server
|
||||
image: quay.io/argoproj/argocd:v2.8.1
|
||||
image: quay.io/argoproj/argocd:v2.8.4
|
||||
name: copyutil
|
||||
securityContext:
|
||||
allowPrivilegeEscalation: false
|
||||
@@ -19439,7 +19451,7 @@ spec:
|
||||
key: controller.kubectl.parallelism.limit
|
||||
name: argocd-cmd-params-cm
|
||||
optional: true
|
||||
image: quay.io/argoproj/argocd:v2.8.1
|
||||
image: quay.io/argoproj/argocd:v2.8.4
|
||||
imagePullPolicy: Always
|
||||
name: argocd-application-controller
|
||||
ports:
|
||||
|
||||
@@ -12,4 +12,4 @@ resources:
|
||||
images:
|
||||
- name: quay.io/argoproj/argocd
|
||||
newName: quay.io/argoproj/argocd
|
||||
newTag: v2.8.1
|
||||
newTag: v2.8.4
|
||||
|
||||
@@ -12,7 +12,7 @@ patches:
|
||||
images:
|
||||
- name: quay.io/argoproj/argocd
|
||||
newName: quay.io/argoproj/argocd
|
||||
newTag: v2.8.1
|
||||
newTag: v2.8.4
|
||||
resources:
|
||||
- ../../base/application-controller
|
||||
- ../../base/applicationset-controller
|
||||
|
||||
@@ -18763,6 +18763,18 @@ rules:
|
||||
- get
|
||||
- list
|
||||
- watch
|
||||
- apiGroups:
|
||||
- batch
|
||||
resources:
|
||||
- jobs
|
||||
verbs:
|
||||
- create
|
||||
- apiGroups:
|
||||
- argoproj.io
|
||||
resources:
|
||||
- workflows
|
||||
verbs:
|
||||
- create
|
||||
---
|
||||
apiVersion: rbac.authorization.k8s.io/v1
|
||||
kind: RoleBinding
|
||||
@@ -20117,7 +20129,7 @@ spec:
|
||||
key: applicationsetcontroller.allowed.scm.providers
|
||||
name: argocd-cmd-params-cm
|
||||
optional: true
|
||||
image: quay.io/argoproj/argocd:v2.8.1
|
||||
image: quay.io/argoproj/argocd:v2.8.4
|
||||
imagePullPolicy: Always
|
||||
name: argocd-applicationset-controller
|
||||
ports:
|
||||
@@ -20240,7 +20252,7 @@ spec:
|
||||
- -n
|
||||
- /usr/local/bin/argocd
|
||||
- /shared/argocd-dex
|
||||
image: quay.io/argoproj/argocd:v2.8.1
|
||||
image: quay.io/argoproj/argocd:v2.8.4
|
||||
imagePullPolicy: Always
|
||||
name: copyutil
|
||||
securityContext:
|
||||
@@ -20310,7 +20322,7 @@ spec:
|
||||
key: notificationscontroller.log.level
|
||||
name: argocd-cmd-params-cm
|
||||
optional: true
|
||||
image: quay.io/argoproj/argocd:v2.8.1
|
||||
image: quay.io/argoproj/argocd:v2.8.4
|
||||
imagePullPolicy: Always
|
||||
livenessProbe:
|
||||
tcpSocket:
|
||||
@@ -20612,6 +20624,18 @@ spec:
|
||||
key: reposerver.streamed.manifest.max.extracted.size
|
||||
name: argocd-cmd-params-cm
|
||||
optional: true
|
||||
- name: ARGOCD_REPO_SERVER_HELM_MANIFEST_MAX_EXTRACTED_SIZE
|
||||
valueFrom:
|
||||
configMapKeyRef:
|
||||
key: reposerver.helm.manifest.max.extracted.size
|
||||
name: argocd-cmd-params-cm
|
||||
optional: true
|
||||
- name: ARGOCD_REPO_SERVER_DISABLE_HELM_MANIFEST_MAX_EXTRACTED_SIZE
|
||||
valueFrom:
|
||||
configMapKeyRef:
|
||||
key: reposerver.disable.helm.manifest.max.extracted.size
|
||||
name: argocd-cmd-params-cm
|
||||
optional: true
|
||||
- name: ARGOCD_GIT_MODULES_ENABLED
|
||||
valueFrom:
|
||||
configMapKeyRef:
|
||||
@@ -20624,7 +20648,7 @@ spec:
|
||||
value: /helm-working-dir
|
||||
- name: HELM_DATA_HOME
|
||||
value: /helm-working-dir
|
||||
image: quay.io/argoproj/argocd:v2.8.1
|
||||
image: quay.io/argoproj/argocd:v2.8.4
|
||||
imagePullPolicy: Always
|
||||
livenessProbe:
|
||||
failureThreshold: 3
|
||||
@@ -20676,7 +20700,7 @@ spec:
|
||||
- -n
|
||||
- /usr/local/bin/argocd
|
||||
- /var/run/argocd/argocd-cmp-server
|
||||
image: quay.io/argoproj/argocd:v2.8.1
|
||||
image: quay.io/argoproj/argocd:v2.8.4
|
||||
name: copyutil
|
||||
securityContext:
|
||||
allowPrivilegeEscalation: false
|
||||
@@ -20965,7 +20989,7 @@ spec:
|
||||
key: server.enable.proxy.extension
|
||||
name: argocd-cmd-params-cm
|
||||
optional: true
|
||||
image: quay.io/argoproj/argocd:v2.8.1
|
||||
image: quay.io/argoproj/argocd:v2.8.4
|
||||
imagePullPolicy: Always
|
||||
livenessProbe:
|
||||
httpGet:
|
||||
@@ -21211,7 +21235,7 @@ spec:
|
||||
key: controller.kubectl.parallelism.limit
|
||||
name: argocd-cmd-params-cm
|
||||
optional: true
|
||||
image: quay.io/argoproj/argocd:v2.8.1
|
||||
image: quay.io/argoproj/argocd:v2.8.4
|
||||
imagePullPolicy: Always
|
||||
name: argocd-application-controller
|
||||
ports:
|
||||
|
||||
@@ -1635,7 +1635,7 @@ spec:
|
||||
key: applicationsetcontroller.allowed.scm.providers
|
||||
name: argocd-cmd-params-cm
|
||||
optional: true
|
||||
image: quay.io/argoproj/argocd:v2.8.1
|
||||
image: quay.io/argoproj/argocd:v2.8.4
|
||||
imagePullPolicy: Always
|
||||
name: argocd-applicationset-controller
|
||||
ports:
|
||||
@@ -1758,7 +1758,7 @@ spec:
|
||||
- -n
|
||||
- /usr/local/bin/argocd
|
||||
- /shared/argocd-dex
|
||||
image: quay.io/argoproj/argocd:v2.8.1
|
||||
image: quay.io/argoproj/argocd:v2.8.4
|
||||
imagePullPolicy: Always
|
||||
name: copyutil
|
||||
securityContext:
|
||||
@@ -1828,7 +1828,7 @@ spec:
|
||||
key: notificationscontroller.log.level
|
||||
name: argocd-cmd-params-cm
|
||||
optional: true
|
||||
image: quay.io/argoproj/argocd:v2.8.1
|
||||
image: quay.io/argoproj/argocd:v2.8.4
|
||||
imagePullPolicy: Always
|
||||
livenessProbe:
|
||||
tcpSocket:
|
||||
@@ -2130,6 +2130,18 @@ spec:
|
||||
key: reposerver.streamed.manifest.max.extracted.size
|
||||
name: argocd-cmd-params-cm
|
||||
optional: true
|
||||
- name: ARGOCD_REPO_SERVER_HELM_MANIFEST_MAX_EXTRACTED_SIZE
|
||||
valueFrom:
|
||||
configMapKeyRef:
|
||||
key: reposerver.helm.manifest.max.extracted.size
|
||||
name: argocd-cmd-params-cm
|
||||
optional: true
|
||||
- name: ARGOCD_REPO_SERVER_DISABLE_HELM_MANIFEST_MAX_EXTRACTED_SIZE
|
||||
valueFrom:
|
||||
configMapKeyRef:
|
||||
key: reposerver.disable.helm.manifest.max.extracted.size
|
||||
name: argocd-cmd-params-cm
|
||||
optional: true
|
||||
- name: ARGOCD_GIT_MODULES_ENABLED
|
||||
valueFrom:
|
||||
configMapKeyRef:
|
||||
@@ -2142,7 +2154,7 @@ spec:
|
||||
value: /helm-working-dir
|
||||
- name: HELM_DATA_HOME
|
||||
value: /helm-working-dir
|
||||
image: quay.io/argoproj/argocd:v2.8.1
|
||||
image: quay.io/argoproj/argocd:v2.8.4
|
||||
imagePullPolicy: Always
|
||||
livenessProbe:
|
||||
failureThreshold: 3
|
||||
@@ -2194,7 +2206,7 @@ spec:
|
||||
- -n
|
||||
- /usr/local/bin/argocd
|
||||
- /var/run/argocd/argocd-cmp-server
|
||||
image: quay.io/argoproj/argocd:v2.8.1
|
||||
image: quay.io/argoproj/argocd:v2.8.4
|
||||
name: copyutil
|
||||
securityContext:
|
||||
allowPrivilegeEscalation: false
|
||||
@@ -2483,7 +2495,7 @@ spec:
|
||||
key: server.enable.proxy.extension
|
||||
name: argocd-cmd-params-cm
|
||||
optional: true
|
||||
image: quay.io/argoproj/argocd:v2.8.1
|
||||
image: quay.io/argoproj/argocd:v2.8.4
|
||||
imagePullPolicy: Always
|
||||
livenessProbe:
|
||||
httpGet:
|
||||
@@ -2729,7 +2741,7 @@ spec:
|
||||
key: controller.kubectl.parallelism.limit
|
||||
name: argocd-cmd-params-cm
|
||||
optional: true
|
||||
image: quay.io/argoproj/argocd:v2.8.1
|
||||
image: quay.io/argoproj/argocd:v2.8.4
|
||||
imagePullPolicy: Always
|
||||
name: argocd-application-controller
|
||||
ports:
|
||||
|
||||
@@ -18722,6 +18722,18 @@ rules:
|
||||
- get
|
||||
- list
|
||||
- watch
|
||||
- apiGroups:
|
||||
- batch
|
||||
resources:
|
||||
- jobs
|
||||
verbs:
|
||||
- create
|
||||
- apiGroups:
|
||||
- argoproj.io
|
||||
resources:
|
||||
- workflows
|
||||
verbs:
|
||||
- create
|
||||
---
|
||||
apiVersion: rbac.authorization.k8s.io/v1
|
||||
kind: RoleBinding
|
||||
@@ -19218,7 +19230,7 @@ spec:
|
||||
key: applicationsetcontroller.allowed.scm.providers
|
||||
name: argocd-cmd-params-cm
|
||||
optional: true
|
||||
image: quay.io/argoproj/argocd:v2.8.1
|
||||
image: quay.io/argoproj/argocd:v2.8.4
|
||||
imagePullPolicy: Always
|
||||
name: argocd-applicationset-controller
|
||||
ports:
|
||||
@@ -19341,7 +19353,7 @@ spec:
|
||||
- -n
|
||||
- /usr/local/bin/argocd
|
||||
- /shared/argocd-dex
|
||||
image: quay.io/argoproj/argocd:v2.8.1
|
||||
image: quay.io/argoproj/argocd:v2.8.4
|
||||
imagePullPolicy: Always
|
||||
name: copyutil
|
||||
securityContext:
|
||||
@@ -19411,7 +19423,7 @@ spec:
|
||||
key: notificationscontroller.log.level
|
||||
name: argocd-cmd-params-cm
|
||||
optional: true
|
||||
image: quay.io/argoproj/argocd:v2.8.1
|
||||
image: quay.io/argoproj/argocd:v2.8.4
|
||||
imagePullPolicy: Always
|
||||
livenessProbe:
|
||||
tcpSocket:
|
||||
@@ -19669,6 +19681,18 @@ spec:
|
||||
key: reposerver.streamed.manifest.max.extracted.size
|
||||
name: argocd-cmd-params-cm
|
||||
optional: true
|
||||
- name: ARGOCD_REPO_SERVER_HELM_MANIFEST_MAX_EXTRACTED_SIZE
|
||||
valueFrom:
|
||||
configMapKeyRef:
|
||||
key: reposerver.helm.manifest.max.extracted.size
|
||||
name: argocd-cmd-params-cm
|
||||
optional: true
|
||||
- name: ARGOCD_REPO_SERVER_DISABLE_HELM_MANIFEST_MAX_EXTRACTED_SIZE
|
||||
valueFrom:
|
||||
configMapKeyRef:
|
||||
key: reposerver.disable.helm.manifest.max.extracted.size
|
||||
name: argocd-cmd-params-cm
|
||||
optional: true
|
||||
- name: ARGOCD_GIT_MODULES_ENABLED
|
||||
valueFrom:
|
||||
configMapKeyRef:
|
||||
@@ -19681,7 +19705,7 @@ spec:
|
||||
value: /helm-working-dir
|
||||
- name: HELM_DATA_HOME
|
||||
value: /helm-working-dir
|
||||
image: quay.io/argoproj/argocd:v2.8.1
|
||||
image: quay.io/argoproj/argocd:v2.8.4
|
||||
imagePullPolicy: Always
|
||||
livenessProbe:
|
||||
failureThreshold: 3
|
||||
@@ -19733,7 +19757,7 @@ spec:
|
||||
- -n
|
||||
- /usr/local/bin/argocd
|
||||
- /var/run/argocd/argocd-cmp-server
|
||||
image: quay.io/argoproj/argocd:v2.8.1
|
||||
image: quay.io/argoproj/argocd:v2.8.4
|
||||
name: copyutil
|
||||
securityContext:
|
||||
allowPrivilegeEscalation: false
|
||||
@@ -20020,7 +20044,7 @@ spec:
|
||||
key: server.enable.proxy.extension
|
||||
name: argocd-cmd-params-cm
|
||||
optional: true
|
||||
image: quay.io/argoproj/argocd:v2.8.1
|
||||
image: quay.io/argoproj/argocd:v2.8.4
|
||||
imagePullPolicy: Always
|
||||
livenessProbe:
|
||||
httpGet:
|
||||
@@ -20266,7 +20290,7 @@ spec:
|
||||
key: controller.kubectl.parallelism.limit
|
||||
name: argocd-cmd-params-cm
|
||||
optional: true
|
||||
image: quay.io/argoproj/argocd:v2.8.1
|
||||
image: quay.io/argoproj/argocd:v2.8.4
|
||||
imagePullPolicy: Always
|
||||
name: argocd-application-controller
|
||||
ports:
|
||||
|
||||
@@ -736,7 +736,7 @@ spec:
|
||||
key: applicationsetcontroller.allowed.scm.providers
|
||||
name: argocd-cmd-params-cm
|
||||
optional: true
|
||||
image: quay.io/argoproj/argocd:v2.8.1
|
||||
image: quay.io/argoproj/argocd:v2.8.4
|
||||
imagePullPolicy: Always
|
||||
name: argocd-applicationset-controller
|
||||
ports:
|
||||
@@ -859,7 +859,7 @@ spec:
|
||||
- -n
|
||||
- /usr/local/bin/argocd
|
||||
- /shared/argocd-dex
|
||||
image: quay.io/argoproj/argocd:v2.8.1
|
||||
image: quay.io/argoproj/argocd:v2.8.4
|
||||
imagePullPolicy: Always
|
||||
name: copyutil
|
||||
securityContext:
|
||||
@@ -929,7 +929,7 @@ spec:
|
||||
key: notificationscontroller.log.level
|
||||
name: argocd-cmd-params-cm
|
||||
optional: true
|
||||
image: quay.io/argoproj/argocd:v2.8.1
|
||||
image: quay.io/argoproj/argocd:v2.8.4
|
||||
imagePullPolicy: Always
|
||||
livenessProbe:
|
||||
tcpSocket:
|
||||
@@ -1187,6 +1187,18 @@ spec:
|
||||
key: reposerver.streamed.manifest.max.extracted.size
|
||||
name: argocd-cmd-params-cm
|
||||
optional: true
|
||||
- name: ARGOCD_REPO_SERVER_HELM_MANIFEST_MAX_EXTRACTED_SIZE
|
||||
valueFrom:
|
||||
configMapKeyRef:
|
||||
key: reposerver.helm.manifest.max.extracted.size
|
||||
name: argocd-cmd-params-cm
|
||||
optional: true
|
||||
- name: ARGOCD_REPO_SERVER_DISABLE_HELM_MANIFEST_MAX_EXTRACTED_SIZE
|
||||
valueFrom:
|
||||
configMapKeyRef:
|
||||
key: reposerver.disable.helm.manifest.max.extracted.size
|
||||
name: argocd-cmd-params-cm
|
||||
optional: true
|
||||
- name: ARGOCD_GIT_MODULES_ENABLED
|
||||
valueFrom:
|
||||
configMapKeyRef:
|
||||
@@ -1199,7 +1211,7 @@ spec:
|
||||
value: /helm-working-dir
|
||||
- name: HELM_DATA_HOME
|
||||
value: /helm-working-dir
|
||||
image: quay.io/argoproj/argocd:v2.8.1
|
||||
image: quay.io/argoproj/argocd:v2.8.4
|
||||
imagePullPolicy: Always
|
||||
livenessProbe:
|
||||
failureThreshold: 3
|
||||
@@ -1251,7 +1263,7 @@ spec:
|
||||
- -n
|
||||
- /usr/local/bin/argocd
|
||||
- /var/run/argocd/argocd-cmp-server
|
||||
image: quay.io/argoproj/argocd:v2.8.1
|
||||
image: quay.io/argoproj/argocd:v2.8.4
|
||||
name: copyutil
|
||||
securityContext:
|
||||
allowPrivilegeEscalation: false
|
||||
@@ -1538,7 +1550,7 @@ spec:
|
||||
key: server.enable.proxy.extension
|
||||
name: argocd-cmd-params-cm
|
||||
optional: true
|
||||
image: quay.io/argoproj/argocd:v2.8.1
|
||||
image: quay.io/argoproj/argocd:v2.8.4
|
||||
imagePullPolicy: Always
|
||||
livenessProbe:
|
||||
httpGet:
|
||||
@@ -1784,7 +1796,7 @@ spec:
|
||||
key: controller.kubectl.parallelism.limit
|
||||
name: argocd-cmd-params-cm
|
||||
optional: true
|
||||
image: quay.io/argoproj/argocd:v2.8.1
|
||||
image: quay.io/argoproj/argocd:v2.8.4
|
||||
imagePullPolicy: Always
|
||||
name: argocd-application-controller
|
||||
ports:
|
||||
|
||||
@@ -22,7 +22,6 @@ import (
|
||||
grpc_retry "github.com/grpc-ecosystem/go-grpc-middleware/retry"
|
||||
"github.com/hashicorp/go-retryablehttp"
|
||||
log "github.com/sirupsen/logrus"
|
||||
"go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc"
|
||||
"golang.org/x/oauth2"
|
||||
"google.golang.org/grpc"
|
||||
"google.golang.org/grpc/codes"
|
||||
@@ -222,10 +221,6 @@ func NewClient(opts *ClientOptions) (Client, error) {
|
||||
if c.ServerAddr == "" {
|
||||
return nil, errors.New("Argo CD server address unspecified")
|
||||
}
|
||||
if parts := strings.Split(c.ServerAddr, ":"); len(parts) == 1 {
|
||||
// If port is unspecified, assume the most likely port
|
||||
c.ServerAddr += ":443"
|
||||
}
|
||||
// Override auth-token if specified in env variable or CLI flag
|
||||
c.AuthToken = env.StringFromEnv(EnvArgoCDAuthToken, c.AuthToken)
|
||||
if opts.AuthToken != "" {
|
||||
@@ -281,6 +276,10 @@ func NewClient(opts *ClientOptions) (Client, error) {
|
||||
}
|
||||
}
|
||||
if !c.GRPCWeb {
|
||||
if parts := strings.Split(c.ServerAddr, ":"); len(parts) == 1 {
|
||||
// If port is unspecified, assume the most likely port
|
||||
c.ServerAddr += ":443"
|
||||
}
|
||||
// test if we need to set it to true
|
||||
// if a call to grpc failed, then try again with GRPCWeb
|
||||
conn, versionIf, err := c.NewVersionClient()
|
||||
@@ -520,8 +519,8 @@ func (c *client) newConn() (*grpc.ClientConn, io.Closer, error) {
|
||||
dialOpts = append(dialOpts, grpc.WithDefaultCallOptions(grpc.MaxCallRecvMsgSize(MaxGRPCMessageSize), grpc.MaxCallSendMsgSize(MaxGRPCMessageSize)))
|
||||
dialOpts = append(dialOpts, grpc.WithStreamInterceptor(grpc_retry.StreamClientInterceptor(retryOpts...)))
|
||||
dialOpts = append(dialOpts, grpc.WithUnaryInterceptor(grpc_middleware.ChainUnaryClient(grpc_retry.UnaryClientInterceptor(retryOpts...))))
|
||||
dialOpts = append(dialOpts, grpc.WithUnaryInterceptor(otelgrpc.UnaryClientInterceptor()))
|
||||
dialOpts = append(dialOpts, grpc.WithStreamInterceptor(otelgrpc.StreamClientInterceptor()))
|
||||
dialOpts = append(dialOpts, grpc.WithUnaryInterceptor(grpc_util.OTELUnaryClientInterceptor()))
|
||||
dialOpts = append(dialOpts, grpc.WithStreamInterceptor(grpc_util.OTELStreamClientInterceptor()))
|
||||
|
||||
ctx := context.Background()
|
||||
|
||||
|
||||
@@ -9,7 +9,6 @@ import (
|
||||
grpc_middleware "github.com/grpc-ecosystem/go-grpc-middleware"
|
||||
grpc_retry "github.com/grpc-ecosystem/go-grpc-middleware/retry"
|
||||
log "github.com/sirupsen/logrus"
|
||||
"go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc"
|
||||
"google.golang.org/grpc"
|
||||
"google.golang.org/grpc/credentials"
|
||||
"google.golang.org/grpc/credentials/insecure"
|
||||
@@ -67,8 +66,8 @@ func NewConnection(address string, timeoutSeconds int, tlsConfig *TLSConfigurati
|
||||
grpc.WithStreamInterceptor(grpc_retry.StreamClientInterceptor(retryOpts...)),
|
||||
grpc.WithUnaryInterceptor(grpc_middleware.ChainUnaryClient(unaryInterceptors...)),
|
||||
grpc.WithDefaultCallOptions(grpc.MaxCallRecvMsgSize(MaxGRPCMessageSize), grpc.MaxCallSendMsgSize(MaxGRPCMessageSize)),
|
||||
grpc.WithUnaryInterceptor(otelgrpc.UnaryClientInterceptor()),
|
||||
grpc.WithStreamInterceptor(otelgrpc.StreamClientInterceptor()),
|
||||
grpc.WithUnaryInterceptor(argogrpc.OTELUnaryClientInterceptor()),
|
||||
grpc.WithStreamInterceptor(argogrpc.OTELStreamClientInterceptor()),
|
||||
}
|
||||
|
||||
tlsC := &tls.Config{}
|
||||
|
||||
@@ -107,6 +107,8 @@ type RepoServerInitConstants struct {
|
||||
AllowOutOfBoundsSymlinks bool
|
||||
StreamedManifestMaxExtractedSize int64
|
||||
StreamedManifestMaxTarSize int64
|
||||
HelmManifestMaxExtractedSize int64
|
||||
DisableHelmManifestMaxExtractedSize bool
|
||||
}
|
||||
|
||||
// NewService returns a new instance of the Manifest service
|
||||
@@ -346,7 +348,7 @@ func (s *Service) runRepoOperation(
|
||||
if source.Helm != nil {
|
||||
helmPassCredentials = source.Helm.PassCredentials
|
||||
}
|
||||
chartPath, closer, err := helmClient.ExtractChart(source.Chart, revision, helmPassCredentials)
|
||||
chartPath, closer, err := helmClient.ExtractChart(source.Chart, revision, helmPassCredentials, s.initConstants.HelmManifestMaxExtractedSize, s.initConstants.DisableHelmManifestMaxExtractedSize)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -2233,7 +2235,7 @@ func (s *Service) GetRevisionChartDetails(ctx context.Context, q *apiclient.Repo
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("helm client error: %v", err)
|
||||
}
|
||||
chartPath, closer, err := helmClient.ExtractChart(q.Name, revision, false)
|
||||
chartPath, closer, err := helmClient.ExtractChart(q.Name, revision, false, s.initConstants.HelmManifestMaxExtractedSize, s.initConstants.DisableHelmManifestMaxExtractedSize)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("error extracting chart: %v", err)
|
||||
}
|
||||
|
||||
@@ -5,10 +5,10 @@ infinity = 2^1024-1
|
||||
local function executor_range_api()
|
||||
min_executor_instances = 0
|
||||
max_executor_instances = infinity
|
||||
if obj.spec.dynamicAllocation.maxExecutors then
|
||||
if obj.spec.dynamicAllocation.maxExecutors then
|
||||
max_executor_instances = obj.spec.dynamicAllocation.maxExecutors
|
||||
end
|
||||
if obj.spec.dynamicAllocation.minExecutors then
|
||||
if obj.spec.dynamicAllocation.minExecutors then
|
||||
min_executor_instances = obj.spec.dynamicAllocation.minExecutors
|
||||
end
|
||||
return min_executor_instances, max_executor_instances
|
||||
@@ -17,7 +17,7 @@ end
|
||||
local function maybe_executor_range_spark_conf()
|
||||
min_executor_instances = 0
|
||||
max_executor_instances = infinity
|
||||
if obj.spec.sparkConf["spark.streaming.dynamicAllocation.enabled"] ~= nil and
|
||||
if obj.spec.sparkConf["spark.streaming.dynamicAllocation.enabled"] ~= nil and
|
||||
obj.spec.sparkConf["spark.streaming.dynamicAllocation.enabled"] == "true" then
|
||||
if(obj.spec.sparkConf["spark.streaming.dynamicAllocation.maxExecutors"] ~= nil) then
|
||||
max_executor_instances = tonumber(obj.spec.sparkConf["spark.streaming.dynamicAllocation.maxExecutors"])
|
||||
@@ -26,7 +26,7 @@ local function maybe_executor_range_spark_conf()
|
||||
min_executor_instances = tonumber(obj.spec.sparkConf["spark.streaming.dynamicAllocation.minExecutors"])
|
||||
end
|
||||
return min_executor_instances, max_executor_instances
|
||||
elseif obj.spec.sparkConf["spark.dynamicAllocation.enabled"] ~= nil and
|
||||
elseif obj.spec.sparkConf["spark.dynamicAllocation.enabled"] ~= nil and
|
||||
obj.spec.sparkConf["spark.dynamicAllocation.enabled"] == "true" then
|
||||
if(obj.spec.sparkConf["spark.dynamicAllocation.maxExecutors"] ~= nil) then
|
||||
max_executor_instances = tonumber(obj.spec.sparkConf["spark.dynamicAllocation.maxExecutors"])
|
||||
@@ -45,11 +45,19 @@ local function maybe_executor_range()
|
||||
return executor_range_api()
|
||||
elseif obj.spec["sparkConf"] ~= nil then
|
||||
return maybe_executor_range_spark_conf()
|
||||
else
|
||||
else
|
||||
return nil
|
||||
end
|
||||
end
|
||||
|
||||
local function dynamic_executors_without_spec_config()
|
||||
if obj.spec.dynamicAllocation == nil and obj.spec.executor.instances == nil then
|
||||
return true
|
||||
else
|
||||
return false
|
||||
end
|
||||
end
|
||||
|
||||
if obj.status ~= nil then
|
||||
if obj.status.applicationState.state ~= nil then
|
||||
if obj.status.applicationState.state == "" then
|
||||
@@ -60,23 +68,26 @@ if obj.status ~= nil then
|
||||
if obj.status.applicationState.state == "RUNNING" then
|
||||
if obj.status.executorState ~= nil then
|
||||
count=0
|
||||
executor_instances = obj.spec.executor.instances
|
||||
for i, executorState in pairs(obj.status.executorState) do
|
||||
if executorState == "RUNNING" then
|
||||
count=count+1
|
||||
end
|
||||
end
|
||||
if executor_instances == count then
|
||||
if obj.spec.executor.instances ~= nil and obj.spec.executor.instances == count then
|
||||
health_status.status = "Healthy"
|
||||
health_status.message = "SparkApplication is Running"
|
||||
return health_status
|
||||
elseif maybe_executor_range() then
|
||||
min_executor_instances, max_executor_instances = maybe_executor_range()
|
||||
if count >= min_executor_instances and count <= max_executor_instances then
|
||||
if count >= min_executor_instances and count <= max_executor_instances then
|
||||
health_status.status = "Healthy"
|
||||
health_status.message = "SparkApplication is Running"
|
||||
return health_status
|
||||
end
|
||||
elseif dynamic_executors_without_spec_config() and count >= 1 then
|
||||
health_status.status = "Healthy"
|
||||
health_status.message = "SparkApplication is Running"
|
||||
return health_status
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
@@ -23,3 +23,7 @@ tests:
|
||||
status: Healthy
|
||||
message: "SparkApplication is Running"
|
||||
inputPath: testdata/healthy_dynamic_alloc_operator_api.yaml
|
||||
- healthStatus:
|
||||
status: Healthy
|
||||
message: "SparkApplication is Running"
|
||||
inputPath: testdata/healthy_dynamic_alloc_without_spec_config.yaml
|
||||
|
||||
@@ -0,0 +1,31 @@
|
||||
apiVersion: sparkoperator.k8s.io/v1beta2
|
||||
kind: SparkApplication
|
||||
metadata:
|
||||
generation: 4
|
||||
labels:
|
||||
argocd.argoproj.io/instance: spark-job
|
||||
name: spark-job-app
|
||||
namespace: spark-cluster
|
||||
resourceVersion: "31812990"
|
||||
uid: bfee52b0-74ca-4465-8005-f6643097ed64
|
||||
spec:
|
||||
executor: {}
|
||||
status:
|
||||
applicationState:
|
||||
state: RUNNING
|
||||
driverInfo:
|
||||
podName: ingestion-datalake-news-app-driver
|
||||
webUIAddress: 172.20.207.161:4040
|
||||
webUIPort: 4040
|
||||
webUIServiceName: ingestion-datalake-news-app-ui-svc
|
||||
executionAttempts: 13
|
||||
executorState:
|
||||
ingestion-datalake-news-app-1591613851251-exec-1: RUNNING
|
||||
ingestion-datalake-news-app-1591613851251-exec-2: RUNNING
|
||||
ingestion-datalake-news-app-1591613851251-exec-4: RUNNING
|
||||
ingestion-datalake-news-app-1591613851251-exec-5: RUNNING
|
||||
lastSubmissionAttemptTime: "2020-06-08T10:57:32Z"
|
||||
sparkApplicationId: spark-a5920b2a5aa04d22a737c60759b5bf82
|
||||
submissionAttempts: 1
|
||||
submissionID: 3e713ec8-9f6c-4e78-ac28-749797c846f0
|
||||
terminationTime: null
|
||||
@@ -9,25 +9,28 @@ import (
|
||||
|
||||
healthutil "github.com/argoproj/gitops-engine/pkg/health"
|
||||
"k8s.io/apimachinery/pkg/api/errors"
|
||||
validation "k8s.io/apimachinery/pkg/api/validation"
|
||||
v1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
|
||||
appv1 "github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1"
|
||||
"github.com/argoproj/argo-cd/v2/pkg/client/clientset/versioned"
|
||||
"github.com/argoproj/argo-cd/v2/util/argo"
|
||||
"github.com/argoproj/argo-cd/v2/util/assets"
|
||||
"github.com/argoproj/argo-cd/v2/util/security"
|
||||
"github.com/argoproj/argo-cd/v2/util/settings"
|
||||
)
|
||||
|
||||
//NewHandler creates handler serving to do api/badge endpoint
|
||||
func NewHandler(appClientset versioned.Interface, settingsMrg *settings.SettingsManager, namespace string) http.Handler {
|
||||
return &Handler{appClientset: appClientset, namespace: namespace, settingsMgr: settingsMrg}
|
||||
// NewHandler creates handler serving to do api/badge endpoint
|
||||
func NewHandler(appClientset versioned.Interface, settingsMrg *settings.SettingsManager, namespace string, enabledNamespaces []string) http.Handler {
|
||||
return &Handler{appClientset: appClientset, namespace: namespace, settingsMgr: settingsMrg, enabledNamespaces: enabledNamespaces}
|
||||
}
|
||||
|
||||
//Handler used to get application in order to access health/sync
|
||||
// Handler used to get application in order to access health/sync
|
||||
type Handler struct {
|
||||
namespace string
|
||||
appClientset versioned.Interface
|
||||
settingsMgr *settings.SettingsManager
|
||||
namespace string
|
||||
appClientset versioned.Interface
|
||||
settingsMgr *settings.SettingsManager
|
||||
enabledNamespaces []string
|
||||
}
|
||||
|
||||
var (
|
||||
@@ -62,8 +65,8 @@ func replaceFirstGroupSubMatch(re *regexp.Regexp, str string, repl string) strin
|
||||
return result + str[lastIndex:]
|
||||
}
|
||||
|
||||
//ServeHTTP returns badge with health and sync status for application
|
||||
//(or an error badge if wrong query or application name is given)
|
||||
// ServeHTTP returns badge with health and sync status for application
|
||||
// (or an error badge if wrong query or application name is given)
|
||||
func (h *Handler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
|
||||
health := healthutil.HealthStatusUnknown
|
||||
status := appv1.SyncStatusCodeUnknown
|
||||
@@ -75,21 +78,50 @@ func (h *Handler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
|
||||
enabled = sets.StatusBadgeEnabled
|
||||
}
|
||||
|
||||
//Sample url: http://localhost:8080/api/badge?name=123
|
||||
if name, ok := r.URL.Query()["name"]; ok && enabled {
|
||||
if app, err := h.appClientset.ArgoprojV1alpha1().Applications(h.namespace).Get(context.Background(), name[0], v1.GetOptions{}); err == nil {
|
||||
health = app.Status.Health.Status
|
||||
status = app.Status.Sync.Status
|
||||
if app.Status.OperationState != nil && app.Status.OperationState.SyncResult != nil {
|
||||
revision = app.Status.OperationState.SyncResult.Revision
|
||||
reqNs := ""
|
||||
if ns, ok := r.URL.Query()["namespace"]; ok && enabled {
|
||||
if errs := validation.NameIsDNSSubdomain(strings.ToLower(ns[0]), false); len(errs) == 0 {
|
||||
if security.IsNamespaceEnabled(ns[0], h.namespace, h.enabledNamespaces) {
|
||||
reqNs = ns[0]
|
||||
} else {
|
||||
notFound = true
|
||||
}
|
||||
} else if errors.IsNotFound(err) {
|
||||
notFound = true
|
||||
} else {
|
||||
w.WriteHeader(http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
} else {
|
||||
reqNs = h.namespace
|
||||
}
|
||||
|
||||
//Sample url: http://localhost:8080/api/badge?name=123
|
||||
if name, ok := r.URL.Query()["name"]; ok && enabled && !notFound {
|
||||
if errs := validation.NameIsDNSLabel(strings.ToLower(name[0]), false); len(errs) == 0 {
|
||||
if app, err := h.appClientset.ArgoprojV1alpha1().Applications(reqNs).Get(context.Background(), name[0], v1.GetOptions{}); err == nil {
|
||||
health = app.Status.Health.Status
|
||||
status = app.Status.Sync.Status
|
||||
if app.Status.OperationState != nil && app.Status.OperationState.SyncResult != nil {
|
||||
revision = app.Status.OperationState.SyncResult.Revision
|
||||
}
|
||||
} else {
|
||||
if errors.IsNotFound(err) {
|
||||
notFound = true
|
||||
}
|
||||
}
|
||||
} else {
|
||||
w.WriteHeader(http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
}
|
||||
//Sample url: http://localhost:8080/api/badge?project=default
|
||||
if projects, ok := r.URL.Query()["project"]; ok && enabled {
|
||||
if apps, err := h.appClientset.ArgoprojV1alpha1().Applications(h.namespace).List(context.Background(), v1.ListOptions{}); err == nil {
|
||||
if projects, ok := r.URL.Query()["project"]; ok && enabled && !notFound {
|
||||
for _, p := range projects {
|
||||
if errs := validation.NameIsDNSLabel(strings.ToLower(p), false); len(p) > 0 && len(errs) != 0 {
|
||||
w.WriteHeader(http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
}
|
||||
if apps, err := h.appClientset.ArgoprojV1alpha1().Applications(reqNs).List(context.Background(), v1.ListOptions{}); err == nil {
|
||||
applicationSet := argo.FilterByProjects(apps.Items, projects)
|
||||
for _, a := range applicationSet {
|
||||
if a.Status.Sync.Status != appv1.SyncStatusCodeSynced {
|
||||
|
||||
@@ -15,6 +15,7 @@ import (
|
||||
|
||||
"github.com/argoproj/gitops-engine/pkg/health"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
corev1 "k8s.io/api/core/v1"
|
||||
v1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/client-go/kubernetes/fake"
|
||||
@@ -41,7 +42,19 @@ var (
|
||||
},
|
||||
}
|
||||
testApp = v1alpha1.Application{
|
||||
ObjectMeta: v1.ObjectMeta{Name: "testApp", Namespace: "default"},
|
||||
ObjectMeta: v1.ObjectMeta{Name: "test-app", Namespace: "default"},
|
||||
Status: v1alpha1.ApplicationStatus{
|
||||
Sync: v1alpha1.SyncStatus{Status: v1alpha1.SyncStatusCodeSynced},
|
||||
Health: v1alpha1.HealthStatus{Status: health.HealthStatusHealthy},
|
||||
OperationState: &v1alpha1.OperationState{
|
||||
SyncResult: &v1alpha1.SyncOperationResult{
|
||||
Revision: "aa29b85",
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
testApp2 = v1alpha1.Application{
|
||||
ObjectMeta: v1.ObjectMeta{Name: "test-app", Namespace: "argocd-test"},
|
||||
Status: v1alpha1.ApplicationStatus{
|
||||
Sync: v1alpha1.SyncStatus{Status: v1alpha1.SyncStatusCodeSynced},
|
||||
Health: v1alpha1.HealthStatus{Status: health.HealthStatusHealthy},
|
||||
@@ -53,15 +66,15 @@ var (
|
||||
},
|
||||
}
|
||||
testProject = v1alpha1.AppProject{
|
||||
ObjectMeta: v1.ObjectMeta{Name: "testProject", Namespace: "default"},
|
||||
ObjectMeta: v1.ObjectMeta{Name: "test-project", Namespace: "default"},
|
||||
Spec: v1alpha1.AppProjectSpec{},
|
||||
}
|
||||
)
|
||||
|
||||
func TestHandlerFeatureIsEnabled(t *testing.T) {
|
||||
settingsMgr := settings.NewSettingsManager(context.Background(), fake.NewSimpleClientset(&argoCDCm, &argoCDSecret), "default")
|
||||
handler := NewHandler(appclientset.NewSimpleClientset(&testApp), settingsMgr, "default")
|
||||
req, err := http.NewRequest(http.MethodGet, "/api/badge?name=testApp", nil)
|
||||
handler := NewHandler(appclientset.NewSimpleClientset(&testApp), settingsMgr, "default", []string{})
|
||||
req, err := http.NewRequest(http.MethodGet, "/api/badge?name=test-app", nil)
|
||||
assert.NoError(t, err)
|
||||
|
||||
rr := httptest.NewRecorder()
|
||||
@@ -81,6 +94,7 @@ func TestHandlerFeatureIsEnabled(t *testing.T) {
|
||||
func TestHandlerFeatureProjectIsEnabled(t *testing.T) {
|
||||
projectTests := []struct {
|
||||
testApp []*v1alpha1.Application
|
||||
response int
|
||||
apiEndPoint string
|
||||
namespace string
|
||||
health string
|
||||
@@ -89,42 +103,105 @@ func TestHandlerFeatureProjectIsEnabled(t *testing.T) {
|
||||
statusColor color.RGBA
|
||||
}{
|
||||
{createApplications([]string{"Healthy:Synced", "Healthy:Synced"}, []string{"default", "default"}, "test"),
|
||||
"/api/badge?project=default", "test", "Healthy", "Synced", Green, Green},
|
||||
{createApplications([]string{"Healthy:Synced", "Healthy:OutOfSync"}, []string{"testProject", "testProject"}, "default"),
|
||||
"/api/badge?project=testProject", "default", "Healthy", "OutOfSync", Green, Orange},
|
||||
http.StatusOK, "/api/badge?project=default", "test", "Healthy", "Synced", Green, Green},
|
||||
{createApplications([]string{"Healthy:Synced", "Healthy:OutOfSync"}, []string{"test-project", "test-project"}, "default"),
|
||||
http.StatusOK, "/api/badge?project=test-project", "default", "Healthy", "OutOfSync", Green, Orange},
|
||||
{createApplications([]string{"Healthy:Synced", "Degraded:Synced"}, []string{"default", "default"}, "test"),
|
||||
"/api/badge?project=default", "test", "Degraded", "Synced", Red, Green},
|
||||
{createApplications([]string{"Healthy:Synced", "Degraded:OutOfSync"}, []string{"testProject", "testProject"}, "default"),
|
||||
"/api/badge?project=testProject", "default", "Degraded", "OutOfSync", Red, Orange},
|
||||
{createApplications([]string{"Healthy:Synced", "Healthy:Synced"}, []string{"testProject", "default"}, "test"),
|
||||
"/api/badge?project=default&project=testProject", "test", "Healthy", "Synced", Green, Green},
|
||||
{createApplications([]string{"Healthy:OutOfSync", "Healthy:Synced"}, []string{"testProject", "default"}, "default"),
|
||||
"/api/badge?project=default&project=testProject", "default", "Healthy", "OutOfSync", Green, Orange},
|
||||
{createApplications([]string{"Degraded:Synced", "Healthy:Synced"}, []string{"testProject", "default"}, "test"),
|
||||
"/api/badge?project=default&project=testProject", "test", "Degraded", "Synced", Red, Green},
|
||||
{createApplications([]string{"Degraded:OutOfSync", "Healthy:OutOfSync"}, []string{"testProject", "default"}, "default"),
|
||||
"/api/badge?project=default&project=testProject", "default", "Degraded", "OutOfSync", Red, Orange},
|
||||
{createApplications([]string{"Unknown:Unknown", "Unknown:Unknown"}, []string{"testProject", "default"}, "default"),
|
||||
"/api/badge?project=", "default", "Unknown", "Unknown", Purple, Purple},
|
||||
http.StatusOK, "/api/badge?project=default", "test", "Degraded", "Synced", Red, Green},
|
||||
{createApplications([]string{"Healthy:Synced", "Degraded:OutOfSync"}, []string{"test-project", "test-project"}, "default"),
|
||||
http.StatusOK, "/api/badge?project=test-project", "default", "Degraded", "OutOfSync", Red, Orange},
|
||||
{createApplications([]string{"Healthy:Synced", "Healthy:Synced"}, []string{"test-project", "default"}, "test"),
|
||||
http.StatusOK, "/api/badge?project=default&project=test-project", "test", "Healthy", "Synced", Green, Green},
|
||||
{createApplications([]string{"Healthy:OutOfSync", "Healthy:Synced"}, []string{"test-project", "default"}, "default"),
|
||||
http.StatusOK, "/api/badge?project=default&project=test-project", "default", "Healthy", "OutOfSync", Green, Orange},
|
||||
{createApplications([]string{"Degraded:Synced", "Healthy:Synced"}, []string{"test-project", "default"}, "test"),
|
||||
http.StatusOK, "/api/badge?project=default&project=test-project", "test", "Degraded", "Synced", Red, Green},
|
||||
{createApplications([]string{"Degraded:OutOfSync", "Healthy:OutOfSync"}, []string{"test-project", "default"}, "default"),
|
||||
http.StatusOK, "/api/badge?project=default&project=test-project", "default", "Degraded", "OutOfSync", Red, Orange},
|
||||
{createApplications([]string{"Unknown:Unknown", "Unknown:Unknown"}, []string{"test-project", "default"}, "default"),
|
||||
http.StatusOK, "/api/badge?project=", "default", "Unknown", "Unknown", Purple, Purple},
|
||||
{createApplications([]string{"Unknown:Unknown", "Unknown:Unknown"}, []string{"test-project", "default"}, "default"),
|
||||
http.StatusBadRequest, "/api/badge?project=test$project", "default", "Unknown", "Unknown", Purple, Purple},
|
||||
{createApplications([]string{"Unknown:Unknown", "Unknown:Unknown"}, []string{"test-project", "default"}, "default"),
|
||||
http.StatusOK, "/api/badge?project=unknown", "default", "Unknown", "Unknown", Purple, Purple},
|
||||
{createApplications([]string{"Unknown:Unknown", "Unknown:Unknown"}, []string{"test-project", "default"}, "default"),
|
||||
http.StatusBadRequest, "/api/badge?name=foo_bar", "default", "Unknown", "Unknown", Purple, Purple},
|
||||
{createApplications([]string{"Unknown:Unknown", "Unknown:Unknown"}, []string{"test-project", "default"}, "default"),
|
||||
http.StatusOK, "/api/badge?name=foobar", "default", "Not Found", "", Purple, Purple},
|
||||
}
|
||||
for _, tt := range projectTests {
|
||||
argoCDCm.ObjectMeta.Namespace = tt.namespace
|
||||
argoCDSecret.ObjectMeta.Namespace = tt.namespace
|
||||
settingsMgr := settings.NewSettingsManager(context.Background(), fake.NewSimpleClientset(&argoCDCm, &argoCDSecret), tt.namespace)
|
||||
handler := NewHandler(appclientset.NewSimpleClientset(&testProject, tt.testApp[0], tt.testApp[1]), settingsMgr, tt.namespace)
|
||||
handler := NewHandler(appclientset.NewSimpleClientset(&testProject, tt.testApp[0], tt.testApp[1]), settingsMgr, tt.namespace, []string{})
|
||||
rr := httptest.NewRecorder()
|
||||
req, err := http.NewRequest(http.MethodGet, tt.apiEndPoint, nil)
|
||||
assert.NoError(t, err)
|
||||
handler.ServeHTTP(rr, req)
|
||||
require.Equal(t, tt.response, rr.Result().StatusCode)
|
||||
if rr.Result().StatusCode != 400 {
|
||||
assert.Equal(t, "private, no-store", rr.Header().Get("Cache-Control"))
|
||||
assert.Equal(t, "*", rr.Header().Get("Access-Control-Allow-Origin"))
|
||||
response := rr.Body.String()
|
||||
require.Greater(t, len(response), 2)
|
||||
assert.Equal(t, toRGBString(tt.healthColor), leftRectColorPattern.FindStringSubmatch(response)[1])
|
||||
assert.Equal(t, toRGBString(tt.statusColor), rightRectColorPattern.FindStringSubmatch(response)[1])
|
||||
assert.Equal(t, tt.health, leftTextPattern.FindStringSubmatch(response)[1])
|
||||
assert.Equal(t, tt.status, rightTextPattern.FindStringSubmatch(response)[1])
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestHandlerNamespacesIsEnabled(t *testing.T) {
|
||||
t.Run("Application in allowed namespace", func(t *testing.T) {
|
||||
settingsMgr := settings.NewSettingsManager(context.Background(), fake.NewSimpleClientset(&argoCDCm, &argoCDSecret), "default")
|
||||
handler := NewHandler(appclientset.NewSimpleClientset(&testApp2), settingsMgr, "default", []string{"argocd-test"})
|
||||
req, err := http.NewRequest(http.MethodGet, "/api/badge?name=test-app&namespace=argocd-test", nil)
|
||||
assert.NoError(t, err)
|
||||
|
||||
rr := httptest.NewRecorder()
|
||||
handler.ServeHTTP(rr, req)
|
||||
|
||||
assert.Equal(t, "private, no-store", rr.Header().Get("Cache-Control"))
|
||||
assert.Equal(t, "*", rr.Header().Get("Access-Control-Allow-Origin"))
|
||||
response := rr.Body.String()
|
||||
assert.Equal(t, toRGBString(tt.healthColor), leftRectColorPattern.FindStringSubmatch(response)[1])
|
||||
assert.Equal(t, toRGBString(tt.statusColor), rightRectColorPattern.FindStringSubmatch(response)[1])
|
||||
assert.Equal(t, tt.health, leftTextPattern.FindStringSubmatch(response)[1])
|
||||
assert.Equal(t, tt.status, rightTextPattern.FindStringSubmatch(response)[1])
|
||||
|
||||
}
|
||||
response := rr.Body.String()
|
||||
assert.Equal(t, toRGBString(Green), leftRectColorPattern.FindStringSubmatch(response)[1])
|
||||
assert.Equal(t, toRGBString(Green), rightRectColorPattern.FindStringSubmatch(response)[1])
|
||||
assert.Equal(t, "Healthy", leftTextPattern.FindStringSubmatch(response)[1])
|
||||
assert.Equal(t, "Synced", rightTextPattern.FindStringSubmatch(response)[1])
|
||||
assert.NotContains(t, response, "(aa29b85)")
|
||||
})
|
||||
|
||||
t.Run("Application in disallowed namespace", func(t *testing.T) {
|
||||
settingsMgr := settings.NewSettingsManager(context.Background(), fake.NewSimpleClientset(&argoCDCm, &argoCDSecret), "default")
|
||||
handler := NewHandler(appclientset.NewSimpleClientset(&testApp2), settingsMgr, "default", []string{"argocd-test"})
|
||||
req, err := http.NewRequest(http.MethodGet, "/api/badge?name=test-app&namespace=kube-system", nil)
|
||||
assert.NoError(t, err)
|
||||
|
||||
rr := httptest.NewRecorder()
|
||||
handler.ServeHTTP(rr, req)
|
||||
|
||||
assert.Equal(t, http.StatusOK, rr.Result().StatusCode)
|
||||
response := rr.Body.String()
|
||||
assert.Equal(t, toRGBString(Purple), leftRectColorPattern.FindStringSubmatch(response)[1])
|
||||
assert.Equal(t, toRGBString(Purple), rightRectColorPattern.FindStringSubmatch(response)[1])
|
||||
assert.Equal(t, "Not Found", leftTextPattern.FindStringSubmatch(response)[1])
|
||||
assert.Equal(t, "", rightTextPattern.FindStringSubmatch(response)[1])
|
||||
|
||||
})
|
||||
|
||||
t.Run("Request with illegal namespace", func(t *testing.T) {
|
||||
settingsMgr := settings.NewSettingsManager(context.Background(), fake.NewSimpleClientset(&argoCDCm, &argoCDSecret), "default")
|
||||
handler := NewHandler(appclientset.NewSimpleClientset(&testApp2), settingsMgr, "default", []string{"argocd-test"})
|
||||
req, err := http.NewRequest(http.MethodGet, "/api/badge?name=test-app&namespace=kube()system", nil)
|
||||
assert.NoError(t, err)
|
||||
|
||||
rr := httptest.NewRecorder()
|
||||
handler.ServeHTTP(rr, req)
|
||||
|
||||
assert.Equal(t, http.StatusBadRequest, rr.Result().StatusCode)
|
||||
})
|
||||
}
|
||||
|
||||
func createApplicationFeatureProjectIsEnabled(healthStatus health.HealthStatusCode, syncStatus v1alpha1.SyncStatusCode, appName, projectName, namespace string) *v1alpha1.Application {
|
||||
@@ -176,8 +253,8 @@ func createApplications(appCombo, projectName []string, namespace string) []*v1a
|
||||
}
|
||||
func TestHandlerFeatureIsEnabledRevisionIsEnabled(t *testing.T) {
|
||||
settingsMgr := settings.NewSettingsManager(context.Background(), fake.NewSimpleClientset(&argoCDCm, &argoCDSecret), "default")
|
||||
handler := NewHandler(appclientset.NewSimpleClientset(&testApp), settingsMgr, "default")
|
||||
req, err := http.NewRequest(http.MethodGet, "/api/badge?name=testApp&revision=true", nil)
|
||||
handler := NewHandler(appclientset.NewSimpleClientset(&testApp), settingsMgr, "default", []string{})
|
||||
req, err := http.NewRequest(http.MethodGet, "/api/badge?name=test-app&revision=true", nil)
|
||||
assert.NoError(t, err)
|
||||
|
||||
rr := httptest.NewRecorder()
|
||||
@@ -199,8 +276,8 @@ func TestHandlerRevisionIsEnabledNoOperationState(t *testing.T) {
|
||||
app.Status.OperationState = nil
|
||||
|
||||
settingsMgr := settings.NewSettingsManager(context.Background(), fake.NewSimpleClientset(&argoCDCm, &argoCDSecret), "default")
|
||||
handler := NewHandler(appclientset.NewSimpleClientset(app), settingsMgr, "default")
|
||||
req, err := http.NewRequest(http.MethodGet, "/api/badge?name=testApp&revision=true", nil)
|
||||
handler := NewHandler(appclientset.NewSimpleClientset(app), settingsMgr, "default", []string{})
|
||||
req, err := http.NewRequest(http.MethodGet, "/api/badge?name=test-app&revision=true", nil)
|
||||
assert.NoError(t, err)
|
||||
|
||||
rr := httptest.NewRecorder()
|
||||
@@ -222,8 +299,8 @@ func TestHandlerRevisionIsEnabledShortCommitSHA(t *testing.T) {
|
||||
app.Status.OperationState.SyncResult.Revision = "abc"
|
||||
|
||||
settingsMgr := settings.NewSettingsManager(context.Background(), fake.NewSimpleClientset(&argoCDCm, &argoCDSecret), "default")
|
||||
handler := NewHandler(appclientset.NewSimpleClientset(app), settingsMgr, "default")
|
||||
req, err := http.NewRequest(http.MethodGet, "/api/badge?name=testApp&revision=true", nil)
|
||||
handler := NewHandler(appclientset.NewSimpleClientset(app), settingsMgr, "default", []string{})
|
||||
req, err := http.NewRequest(http.MethodGet, "/api/badge?name=test-app&revision=true", nil)
|
||||
assert.NoError(t, err)
|
||||
|
||||
rr := httptest.NewRecorder()
|
||||
@@ -239,8 +316,8 @@ func TestHandlerFeatureIsDisabled(t *testing.T) {
|
||||
delete(argoCDCmDisabled.Data, "statusbadge.enabled")
|
||||
|
||||
settingsMgr := settings.NewSettingsManager(context.Background(), fake.NewSimpleClientset(argoCDCmDisabled, &argoCDSecret), "default")
|
||||
handler := NewHandler(appclientset.NewSimpleClientset(&testApp), settingsMgr, "default")
|
||||
req, err := http.NewRequest(http.MethodGet, "/api/badge?name=testApp", nil)
|
||||
handler := NewHandler(appclientset.NewSimpleClientset(&testApp), settingsMgr, "default", []string{})
|
||||
req, err := http.NewRequest(http.MethodGet, "/api/badge?name=test-app", nil)
|
||||
assert.NoError(t, err)
|
||||
|
||||
rr := httptest.NewRecorder()
|
||||
|
||||
@@ -63,7 +63,6 @@ import (
|
||||
"github.com/argoproj/argo-cd/v2/pkg/apiclient"
|
||||
accountpkg "github.com/argoproj/argo-cd/v2/pkg/apiclient/account"
|
||||
applicationpkg "github.com/argoproj/argo-cd/v2/pkg/apiclient/application"
|
||||
|
||||
applicationsetpkg "github.com/argoproj/argo-cd/v2/pkg/apiclient/applicationset"
|
||||
certificatepkg "github.com/argoproj/argo-cd/v2/pkg/apiclient/certificate"
|
||||
clusterpkg "github.com/argoproj/argo-cd/v2/pkg/apiclient/cluster"
|
||||
@@ -437,8 +436,8 @@ func (a *ArgoCDServer) Listen() (*Listeners, error) {
|
||||
var dOpts []grpc.DialOption
|
||||
dOpts = append(dOpts, grpc.WithDefaultCallOptions(grpc.MaxCallRecvMsgSize(apiclient.MaxGRPCMessageSize)))
|
||||
dOpts = append(dOpts, grpc.WithUserAgent(fmt.Sprintf("%s/%s", common.ArgoCDUserAgentName, common.GetVersion().Version)))
|
||||
dOpts = append(dOpts, grpc.WithUnaryInterceptor(otelgrpc.UnaryClientInterceptor()))
|
||||
dOpts = append(dOpts, grpc.WithStreamInterceptor(otelgrpc.StreamClientInterceptor()))
|
||||
dOpts = append(dOpts, grpc.WithUnaryInterceptor(grpc_util.OTELUnaryClientInterceptor()))
|
||||
dOpts = append(dOpts, grpc.WithStreamInterceptor(grpc_util.OTELStreamClientInterceptor()))
|
||||
if a.useTLS() {
|
||||
// The following sets up the dial Options for grpc-gateway to talk to gRPC server over TLS.
|
||||
// grpc-gateway is just translating HTTP/HTTPS requests as gRPC requests over localhost,
|
||||
@@ -952,7 +951,7 @@ func (a *ArgoCDServer) newHTTPServer(ctx context.Context, port int, grpcWebHandl
|
||||
Handler: &handlerSwitcher{
|
||||
handler: mux,
|
||||
urlToHandler: map[string]http.Handler{
|
||||
"/api/badge": badge.NewHandler(a.AppClientset, a.settingsMgr, a.Namespace),
|
||||
"/api/badge": badge.NewHandler(a.AppClientset, a.settingsMgr, a.Namespace, a.ApplicationNamespaces),
|
||||
common.LogoutEndpoint: logout.NewHandler(a.AppClientset, a.settingsMgr, a.sessionMgr, a.ArgoCDServerOpts.RootPath, a.ArgoCDServerOpts.BaseHRef, a.Namespace),
|
||||
},
|
||||
contentTypeToHandler: map[string]http.Handler{
|
||||
|
||||
@@ -1754,6 +1754,40 @@ func TestCompareOptionIgnoreExtraneous(t *testing.T) {
|
||||
Expect(SyncStatusIs(SyncStatusCodeSynced))
|
||||
}
|
||||
|
||||
func TestSourceNamespaceCanBeMigratedToManagedNamespaceWithoutBeingPrunedOrOutOfSync(t *testing.T) {
|
||||
Given(t).
|
||||
Prune(true).
|
||||
Path("guestbook-with-plain-namespace-manifest").
|
||||
When().
|
||||
PatchFile("guestbook-ui-namespace.yaml", fmt.Sprintf(`[{"op": "replace", "path": "/metadata/name", "value": "%s"}]`, DeploymentNamespace())).
|
||||
CreateApp().
|
||||
Sync().
|
||||
Then().
|
||||
Expect(OperationPhaseIs(OperationSucceeded)).
|
||||
Expect(SyncStatusIs(SyncStatusCodeSynced)).
|
||||
When().
|
||||
PatchApp(`[{
|
||||
"op": "add",
|
||||
"path": "/spec/syncPolicy",
|
||||
"value": { "prune": true, "syncOptions": ["PrunePropagationPolicy=foreground"], "managedNamespaceMetadata": { "labels": { "foo": "bar" } } }
|
||||
}]`).
|
||||
Sync().
|
||||
Then().
|
||||
Expect(OperationPhaseIs(OperationSucceeded)).
|
||||
Expect(SyncStatusIs(SyncStatusCodeSynced)).
|
||||
And(func(app *Application) {
|
||||
assert.Equal(t, &ManagedNamespaceMetadata{Labels: map[string]string{"foo": "bar"}}, app.Spec.SyncPolicy.ManagedNamespaceMetadata)
|
||||
}).
|
||||
When().
|
||||
DeleteFile("guestbook-ui-namespace.yaml").
|
||||
Refresh(RefreshTypeHard).
|
||||
Sync().
|
||||
Wait().
|
||||
Then().
|
||||
Expect(OperationPhaseIs(OperationSucceeded)).
|
||||
Expect(SyncStatusIs(SyncStatusCodeSynced))
|
||||
}
|
||||
|
||||
func TestSelfManagedApps(t *testing.T) {
|
||||
|
||||
Given(t).
|
||||
|
||||
@@ -13,6 +13,7 @@ import (
|
||||
corev1 "k8s.io/api/core/v1"
|
||||
apiextensionsv1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/apimachinery/pkg/runtime"
|
||||
|
||||
"github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1"
|
||||
argov1alpha1 "github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1"
|
||||
@@ -47,7 +48,6 @@ var (
|
||||
Reason: v1alpha1.ApplicationSetReasonApplicationSetUpToDate,
|
||||
},
|
||||
}
|
||||
LabelKeyAppSetInstance = "argocd.argoproj.io/application-set-name"
|
||||
)
|
||||
|
||||
func TestSimpleListGeneratorExternalNamespace(t *testing.T) {
|
||||
@@ -58,11 +58,8 @@ func TestSimpleListGeneratorExternalNamespace(t *testing.T) {
|
||||
APIVersion: "argoproj.io/v1alpha1",
|
||||
},
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "my-cluster-guestbook",
|
||||
Namespace: utils.ArgoCDExternalNamespace,
|
||||
Labels: map[string]string{
|
||||
LabelKeyAppSetInstance: "simple-list-generator-external",
|
||||
},
|
||||
Name: "my-cluster-guestbook",
|
||||
Namespace: utils.ArgoCDExternalNamespace,
|
||||
Finalizers: []string{"resources-finalizer.argocd.argoproj.io"},
|
||||
},
|
||||
Spec: argov1alpha1.ApplicationSpec{
|
||||
@@ -134,15 +131,13 @@ func TestSimpleListGeneratorExternalNamespace(t *testing.T) {
|
||||
expectedAppNewMetadata = expectedAppNewNamespace.DeepCopy()
|
||||
expectedAppNewMetadata.ObjectMeta.Annotations = map[string]string{"annotation-key": "annotation-value"}
|
||||
expectedAppNewMetadata.ObjectMeta.Labels = map[string]string{
|
||||
"label-key": "label-value",
|
||||
LabelKeyAppSetInstance: "simple-list-generator-external",
|
||||
"label-key": "label-value",
|
||||
}
|
||||
}).
|
||||
Update(func(appset *v1alpha1.ApplicationSet) {
|
||||
appset.Spec.Template.Annotations = map[string]string{"annotation-key": "annotation-value"}
|
||||
appset.Spec.Template.Labels = map[string]string{
|
||||
"label-key": "label-value",
|
||||
LabelKeyAppSetInstance: "simple-list-generator-external",
|
||||
"label-key": "label-value",
|
||||
}
|
||||
}).Then().Expect(ApplicationsExist([]argov1alpha1.Application{*expectedAppNewMetadata})).
|
||||
|
||||
@@ -163,11 +158,8 @@ func TestSimpleListGenerator(t *testing.T) {
|
||||
APIVersion: "argoproj.io/v1alpha1",
|
||||
},
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "my-cluster-guestbook",
|
||||
Namespace: fixture.TestNamespace(),
|
||||
Labels: map[string]string{
|
||||
LabelKeyAppSetInstance: "simple-list-generator",
|
||||
},
|
||||
Name: "my-cluster-guestbook",
|
||||
Namespace: fixture.TestNamespace(),
|
||||
Finalizers: []string{"resources-finalizer.argocd.argoproj.io"},
|
||||
},
|
||||
Spec: argov1alpha1.ApplicationSpec{
|
||||
@@ -234,10 +226,7 @@ func TestSimpleListGenerator(t *testing.T) {
|
||||
And(func() {
|
||||
expectedAppNewMetadata = expectedAppNewNamespace.DeepCopy()
|
||||
expectedAppNewMetadata.ObjectMeta.Annotations = map[string]string{"annotation-key": "annotation-value"}
|
||||
expectedAppNewMetadata.ObjectMeta.Labels = map[string]string{
|
||||
LabelKeyAppSetInstance: "simple-list-generator",
|
||||
"label-key": "label-value",
|
||||
}
|
||||
expectedAppNewMetadata.ObjectMeta.Labels = map[string]string{"label-key": "label-value"}
|
||||
}).
|
||||
Update(func(appset *v1alpha1.ApplicationSet) {
|
||||
appset.Spec.Template.Annotations = map[string]string{"annotation-key": "annotation-value"}
|
||||
@@ -264,9 +253,6 @@ func TestSimpleListGeneratorGoTemplate(t *testing.T) {
|
||||
Name: "my-cluster-guestbook",
|
||||
Namespace: fixture.TestNamespace(),
|
||||
Finalizers: []string{"resources-finalizer.argocd.argoproj.io"},
|
||||
Labels: map[string]string{
|
||||
LabelKeyAppSetInstance: "simple-list-generator",
|
||||
},
|
||||
},
|
||||
Spec: argov1alpha1.ApplicationSpec{
|
||||
Project: "default",
|
||||
@@ -333,10 +319,7 @@ func TestSimpleListGeneratorGoTemplate(t *testing.T) {
|
||||
And(func() {
|
||||
expectedAppNewMetadata = expectedAppNewNamespace.DeepCopy()
|
||||
expectedAppNewMetadata.ObjectMeta.Annotations = map[string]string{"annotation-key": "annotation-value"}
|
||||
expectedAppNewMetadata.ObjectMeta.Labels = map[string]string{
|
||||
LabelKeyAppSetInstance: "simple-list-generator",
|
||||
"label-key": "label-value",
|
||||
}
|
||||
expectedAppNewMetadata.ObjectMeta.Labels = map[string]string{"label-key": "label-value"}
|
||||
}).
|
||||
Update(func(appset *v1alpha1.ApplicationSet) {
|
||||
appset.Spec.Template.Annotations = map[string]string{"annotation-key": "annotation-value"}
|
||||
@@ -352,6 +335,82 @@ func TestSimpleListGeneratorGoTemplate(t *testing.T) {
|
||||
|
||||
}
|
||||
|
||||
func TestRenderHelmValuesObject(t *testing.T) {
|
||||
|
||||
expectedApp := argov1alpha1.Application{
|
||||
TypeMeta: metav1.TypeMeta{
|
||||
Kind: application.ApplicationKind,
|
||||
APIVersion: "argoproj.io/v1alpha1",
|
||||
},
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "my-cluster-guestbook",
|
||||
Namespace: fixture.TestNamespace(),
|
||||
Finalizers: []string{"resources-finalizer.argocd.argoproj.io"},
|
||||
},
|
||||
Spec: argov1alpha1.ApplicationSpec{
|
||||
Project: "default",
|
||||
Source: &argov1alpha1.ApplicationSource{
|
||||
RepoURL: "https://github.com/argoproj/argocd-example-apps.git",
|
||||
TargetRevision: "HEAD",
|
||||
Path: "helm-guestbook",
|
||||
Helm: &argov1alpha1.ApplicationSourceHelm{
|
||||
ValuesObject: &runtime.RawExtension{
|
||||
// This will always be converted as yaml
|
||||
Raw: []byte(`{"some":{"string":"Hello world"}}`),
|
||||
},
|
||||
},
|
||||
},
|
||||
Destination: argov1alpha1.ApplicationDestination{
|
||||
Server: "https://kubernetes.default.svc",
|
||||
Namespace: "guestbook",
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
Given(t).
|
||||
// Create a ListGenerator-based ApplicationSet
|
||||
When().Create(v1alpha1.ApplicationSet{ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "test-values-object",
|
||||
},
|
||||
Spec: v1alpha1.ApplicationSetSpec{
|
||||
GoTemplate: true,
|
||||
Template: v1alpha1.ApplicationSetTemplate{
|
||||
ApplicationSetTemplateMeta: v1alpha1.ApplicationSetTemplateMeta{Name: "{{.cluster}}-guestbook"},
|
||||
Spec: argov1alpha1.ApplicationSpec{
|
||||
Project: "default",
|
||||
Source: &argov1alpha1.ApplicationSource{
|
||||
RepoURL: "https://github.com/argoproj/argocd-example-apps.git",
|
||||
TargetRevision: "HEAD",
|
||||
Path: "helm-guestbook",
|
||||
Helm: &argov1alpha1.ApplicationSourceHelm{
|
||||
ValuesObject: &runtime.RawExtension{
|
||||
Raw: []byte(`{"some":{"string":"{{.test}}"}}`),
|
||||
},
|
||||
},
|
||||
},
|
||||
Destination: argov1alpha1.ApplicationDestination{
|
||||
Server: "{{.url}}",
|
||||
Namespace: "guestbook",
|
||||
},
|
||||
},
|
||||
},
|
||||
Generators: []v1alpha1.ApplicationSetGenerator{
|
||||
{
|
||||
List: &v1alpha1.ListGenerator{
|
||||
Elements: []apiextensionsv1.JSON{{
|
||||
Raw: []byte(`{"cluster": "my-cluster","url": "https://kubernetes.default.svc", "test": "Hello world"}`),
|
||||
}},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}).Then().Expect(ApplicationsExist([]argov1alpha1.Application{expectedApp})).
|
||||
// Delete the ApplicationSet, and verify it deletes the Applications
|
||||
When().
|
||||
Delete().Then().Expect(ApplicationsDoNotExist([]argov1alpha1.Application{expectedApp}))
|
||||
|
||||
}
|
||||
|
||||
func TestSyncPolicyCreateUpdate(t *testing.T) {
|
||||
|
||||
expectedApp := argov1alpha1.Application{
|
||||
@@ -363,9 +422,6 @@ func TestSyncPolicyCreateUpdate(t *testing.T) {
|
||||
Name: "my-cluster-guestbook-sync-policy-create-update",
|
||||
Namespace: utils.ArgoCDNamespace,
|
||||
Finalizers: []string{"resources-finalizer.argocd.argoproj.io"},
|
||||
Labels: map[string]string{
|
||||
LabelKeyAppSetInstance: "sync-policy-create-update",
|
||||
},
|
||||
},
|
||||
Spec: argov1alpha1.ApplicationSpec{
|
||||
Project: "default",
|
||||
@@ -435,15 +491,13 @@ func TestSyncPolicyCreateUpdate(t *testing.T) {
|
||||
expectedAppNewMetadata = expectedAppNewNamespace.DeepCopy()
|
||||
expectedAppNewMetadata.ObjectMeta.Annotations = map[string]string{"annotation-key": "annotation-value"}
|
||||
expectedAppNewMetadata.ObjectMeta.Labels = map[string]string{
|
||||
LabelKeyAppSetInstance: "sync-policy-create-update",
|
||||
"label-key": "label-value",
|
||||
"label-key": "label-value",
|
||||
}
|
||||
}).
|
||||
Update(func(appset *v1alpha1.ApplicationSet) {
|
||||
appset.Spec.Template.Annotations = map[string]string{"annotation-key": "annotation-value"}
|
||||
appset.Spec.Template.Labels = map[string]string{
|
||||
LabelKeyAppSetInstance: "sync-policy-create-update",
|
||||
"label-key": "label-value",
|
||||
"label-key": "label-value",
|
||||
}
|
||||
applicationsSyncPolicy := argov1alpha1.ApplicationsSyncPolicyCreateUpdate
|
||||
appset.Spec.SyncPolicy = &argov1alpha1.ApplicationSetSyncPolicy{
|
||||
@@ -478,9 +532,6 @@ func TestSyncPolicyCreateDelete(t *testing.T) {
|
||||
Name: "my-cluster-guestbook-sync-policy-create-delete",
|
||||
Namespace: utils.ArgoCDNamespace,
|
||||
Finalizers: []string{"resources-finalizer.argocd.argoproj.io"},
|
||||
Labels: map[string]string{
|
||||
LabelKeyAppSetInstance: "sync-policy-create-delete",
|
||||
},
|
||||
},
|
||||
Spec: argov1alpha1.ApplicationSpec{
|
||||
Project: "default",
|
||||
@@ -581,9 +632,6 @@ func TestSyncPolicyCreateOnly(t *testing.T) {
|
||||
Name: "my-cluster-guestbook-sync-policy-create-only",
|
||||
Namespace: utils.ArgoCDNamespace,
|
||||
Finalizers: []string{"resources-finalizer.argocd.argoproj.io"},
|
||||
Labels: map[string]string{
|
||||
LabelKeyAppSetInstance: "sync-policy-create-only",
|
||||
},
|
||||
},
|
||||
Spec: argov1alpha1.ApplicationSpec{
|
||||
Project: "default",
|
||||
@@ -684,9 +732,6 @@ func TestSimpleGitDirectoryGenerator(t *testing.T) {
|
||||
Name: name,
|
||||
Namespace: fixture.TestNamespace(),
|
||||
Finalizers: []string{"resources-finalizer.argocd.argoproj.io"},
|
||||
Labels: map[string]string{
|
||||
LabelKeyAppSetInstance: "simple-git-generator",
|
||||
},
|
||||
},
|
||||
Spec: argov1alpha1.ApplicationSpec{
|
||||
Project: "default",
|
||||
@@ -768,10 +813,7 @@ func TestSimpleGitDirectoryGenerator(t *testing.T) {
|
||||
for _, expectedApp := range expectedAppsNewNamespace {
|
||||
expectedAppNewMetadata := expectedApp.DeepCopy()
|
||||
expectedAppNewMetadata.ObjectMeta.Annotations = map[string]string{"annotation-key": "annotation-value"}
|
||||
expectedAppNewMetadata.ObjectMeta.Labels = map[string]string{
|
||||
LabelKeyAppSetInstance: "simple-git-generator",
|
||||
"label-key": "label-value",
|
||||
}
|
||||
expectedAppNewMetadata.ObjectMeta.Labels = map[string]string{"label-key": "label-value"}
|
||||
expectedAppsNewMetadata = append(expectedAppsNewMetadata, *expectedAppNewMetadata)
|
||||
}
|
||||
}).
|
||||
@@ -799,9 +841,6 @@ func TestSimpleGitDirectoryGeneratorGoTemplate(t *testing.T) {
|
||||
Name: name,
|
||||
Namespace: fixture.TestNamespace(),
|
||||
Finalizers: []string{"resources-finalizer.argocd.argoproj.io"},
|
||||
Labels: map[string]string{
|
||||
LabelKeyAppSetInstance: "simple-git-generator",
|
||||
},
|
||||
},
|
||||
Spec: argov1alpha1.ApplicationSpec{
|
||||
Project: "default",
|
||||
@@ -884,10 +923,7 @@ func TestSimpleGitDirectoryGeneratorGoTemplate(t *testing.T) {
|
||||
for _, expectedApp := range expectedAppsNewNamespace {
|
||||
expectedAppNewMetadata := expectedApp.DeepCopy()
|
||||
expectedAppNewMetadata.ObjectMeta.Annotations = map[string]string{"annotation-key": "annotation-value"}
|
||||
expectedAppNewMetadata.ObjectMeta.Labels = map[string]string{
|
||||
LabelKeyAppSetInstance: "simple-git-generator",
|
||||
"label-key": "label-value",
|
||||
}
|
||||
expectedAppNewMetadata.ObjectMeta.Labels = map[string]string{"label-key": "label-value"}
|
||||
expectedAppsNewMetadata = append(expectedAppsNewMetadata, *expectedAppNewMetadata)
|
||||
}
|
||||
}).
|
||||
@@ -916,9 +952,6 @@ func TestSimpleGitFilesGenerator(t *testing.T) {
|
||||
Name: name,
|
||||
Namespace: fixture.TestNamespace(),
|
||||
Finalizers: []string{"resources-finalizer.argocd.argoproj.io"},
|
||||
Labels: map[string]string{
|
||||
LabelKeyAppSetInstance: "simple-git-generator",
|
||||
},
|
||||
},
|
||||
Spec: argov1alpha1.ApplicationSpec{
|
||||
Project: "default",
|
||||
@@ -999,10 +1032,7 @@ func TestSimpleGitFilesGenerator(t *testing.T) {
|
||||
for _, expectedApp := range expectedAppsNewNamespace {
|
||||
expectedAppNewMetadata := expectedApp.DeepCopy()
|
||||
expectedAppNewMetadata.ObjectMeta.Annotations = map[string]string{"annotation-key": "annotation-value"}
|
||||
expectedAppNewMetadata.ObjectMeta.Labels = map[string]string{
|
||||
LabelKeyAppSetInstance: "simple-git-generator",
|
||||
"label-key": "label-value",
|
||||
}
|
||||
expectedAppNewMetadata.ObjectMeta.Labels = map[string]string{"label-key": "label-value"}
|
||||
expectedAppsNewMetadata = append(expectedAppsNewMetadata, *expectedAppNewMetadata)
|
||||
}
|
||||
}).
|
||||
@@ -1031,9 +1061,6 @@ func TestSimpleGitFilesGeneratorGoTemplate(t *testing.T) {
|
||||
Name: name,
|
||||
Namespace: fixture.TestNamespace(),
|
||||
Finalizers: []string{"resources-finalizer.argocd.argoproj.io"},
|
||||
Labels: map[string]string{
|
||||
LabelKeyAppSetInstance: "simple-git-generator",
|
||||
},
|
||||
},
|
||||
Spec: argov1alpha1.ApplicationSpec{
|
||||
Project: "default",
|
||||
@@ -1115,10 +1142,7 @@ func TestSimpleGitFilesGeneratorGoTemplate(t *testing.T) {
|
||||
for _, expectedApp := range expectedAppsNewNamespace {
|
||||
expectedAppNewMetadata := expectedApp.DeepCopy()
|
||||
expectedAppNewMetadata.ObjectMeta.Annotations = map[string]string{"annotation-key": "annotation-value"}
|
||||
expectedAppNewMetadata.ObjectMeta.Labels = map[string]string{
|
||||
LabelKeyAppSetInstance: "simple-git-generator",
|
||||
"label-key": "label-value",
|
||||
}
|
||||
expectedAppNewMetadata.ObjectMeta.Labels = map[string]string{"label-key": "label-value"}
|
||||
expectedAppsNewMetadata = append(expectedAppsNewMetadata, *expectedAppNewMetadata)
|
||||
}
|
||||
}).
|
||||
@@ -1477,9 +1501,6 @@ func TestSimpleSCMProviderGenerator(t *testing.T) {
|
||||
Name: "argo-cd-guestbook",
|
||||
Namespace: fixture.TestNamespace(),
|
||||
Finalizers: []string{"resources-finalizer.argocd.argoproj.io"},
|
||||
Labels: map[string]string{
|
||||
LabelKeyAppSetInstance: "simple-scm-provider-generator",
|
||||
},
|
||||
},
|
||||
Spec: argov1alpha1.ApplicationSpec{
|
||||
Project: "default",
|
||||
@@ -1554,9 +1575,6 @@ func TestSimpleSCMProviderGeneratorGoTemplate(t *testing.T) {
|
||||
Name: "argo-cd-guestbook",
|
||||
Namespace: fixture.TestNamespace(),
|
||||
Finalizers: []string{"resources-finalizer.argocd.argoproj.io"},
|
||||
Labels: map[string]string{
|
||||
LabelKeyAppSetInstance: "simple-scm-provider-generator",
|
||||
},
|
||||
},
|
||||
Spec: argov1alpha1.ApplicationSpec{
|
||||
Project: "default",
|
||||
@@ -1626,9 +1644,6 @@ func TestSCMProviderGeneratorSCMProviderNotAllowed(t *testing.T) {
|
||||
Name: "argo-cd-guestbook",
|
||||
Namespace: fixture.TestNamespace(),
|
||||
Finalizers: []string{"resources-finalizer.argocd.argoproj.io"},
|
||||
Labels: map[string]string{
|
||||
LabelKeyAppSetInstance: "simple-scm-provider-generator",
|
||||
},
|
||||
},
|
||||
Spec: argov1alpha1.ApplicationSpec{
|
||||
Project: "default",
|
||||
@@ -1704,9 +1719,6 @@ func TestCustomApplicationFinalizers(t *testing.T) {
|
||||
Name: "my-cluster-guestbook",
|
||||
Namespace: fixture.TestNamespace(),
|
||||
Finalizers: []string{"resources-finalizer.argocd.argoproj.io/background"},
|
||||
Labels: map[string]string{
|
||||
LabelKeyAppSetInstance: "simple-list-generator",
|
||||
},
|
||||
},
|
||||
Spec: argov1alpha1.ApplicationSpec{
|
||||
Project: "default",
|
||||
@@ -1773,9 +1785,6 @@ func TestCustomApplicationFinalizersGoTemplate(t *testing.T) {
|
||||
Name: "my-cluster-guestbook",
|
||||
Namespace: fixture.TestNamespace(),
|
||||
Finalizers: []string{"resources-finalizer.argocd.argoproj.io/background"},
|
||||
Labels: map[string]string{
|
||||
LabelKeyAppSetInstance: "simple-list-generator",
|
||||
},
|
||||
},
|
||||
Spec: argov1alpha1.ApplicationSpec{
|
||||
Project: "default",
|
||||
@@ -1883,9 +1892,6 @@ func TestSimplePullRequestGenerator(t *testing.T) {
|
||||
Name: "guestbook-1",
|
||||
Namespace: fixture.TestNamespace(),
|
||||
Finalizers: []string{"resources-finalizer.argocd.argoproj.io"},
|
||||
Labels: map[string]string{
|
||||
LabelKeyAppSetInstance: "simple-pull-request-generator",
|
||||
},
|
||||
},
|
||||
Spec: argov1alpha1.ApplicationSpec{
|
||||
Project: "default",
|
||||
@@ -1963,10 +1969,7 @@ func TestSimplePullRequestGeneratorGoTemplate(t *testing.T) {
|
||||
Name: "guestbook-1",
|
||||
Namespace: fixture.TestNamespace(),
|
||||
Finalizers: []string{"resources-finalizer.argocd.argoproj.io"},
|
||||
Labels: map[string]string{
|
||||
"app": "preview",
|
||||
LabelKeyAppSetInstance: "simple-pull-request-generator",
|
||||
},
|
||||
Labels: map[string]string{"app": "preview"},
|
||||
},
|
||||
Spec: argov1alpha1.ApplicationSpec{
|
||||
Project: "default",
|
||||
@@ -2042,8 +2045,7 @@ func TestPullRequestGeneratorNotAllowedSCMProvider(t *testing.T) {
|
||||
Namespace: fixture.TestNamespace(),
|
||||
Finalizers: []string{"resources-finalizer.argocd.argoproj.io"},
|
||||
Labels: map[string]string{
|
||||
"app": "preview",
|
||||
LabelKeyAppSetInstance: "simple-pull-request-generator",
|
||||
"app": "preview",
|
||||
},
|
||||
},
|
||||
Spec: argov1alpha1.ApplicationSpec{
|
||||
@@ -2126,9 +2128,6 @@ func TestGitGeneratorPrivateRepo(t *testing.T) {
|
||||
Name: name,
|
||||
Namespace: fixture.TestNamespace(),
|
||||
Finalizers: []string{"resources-finalizer.argocd.argoproj.io"},
|
||||
Labels: map[string]string{
|
||||
LabelKeyAppSetInstance: "simple-git-generator-private",
|
||||
},
|
||||
},
|
||||
Spec: argov1alpha1.ApplicationSpec{
|
||||
Project: "default",
|
||||
@@ -2204,9 +2203,6 @@ func TestGitGeneratorPrivateRepoGoTemplate(t *testing.T) {
|
||||
Name: name,
|
||||
Namespace: fixture.TestNamespace(),
|
||||
Finalizers: []string{"resources-finalizer.argocd.argoproj.io"},
|
||||
Labels: map[string]string{
|
||||
LabelKeyAppSetInstance: "simple-git-generator-private",
|
||||
},
|
||||
},
|
||||
Spec: argov1alpha1.ApplicationSpec{
|
||||
Project: "default",
|
||||
|
||||
@@ -24,11 +24,8 @@ func TestSimpleClusterGeneratorExternalNamespace(t *testing.T) {
|
||||
APIVersion: "argoproj.io/v1alpha1",
|
||||
},
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "cluster1-guestbook",
|
||||
Namespace: utils.ArgoCDExternalNamespace,
|
||||
Labels: map[string]string{
|
||||
LabelKeyAppSetInstance: "simple-cluster-generator",
|
||||
},
|
||||
Name: "cluster1-guestbook",
|
||||
Namespace: utils.ArgoCDExternalNamespace,
|
||||
Finalizers: []string{"resources-finalizer.argocd.argoproj.io"},
|
||||
},
|
||||
Spec: argov1alpha1.ApplicationSpec{
|
||||
@@ -104,15 +101,13 @@ func TestSimpleClusterGeneratorExternalNamespace(t *testing.T) {
|
||||
expectedAppNewMetadata = expectedAppNewNamespace.DeepCopy()
|
||||
expectedAppNewMetadata.ObjectMeta.Annotations = map[string]string{"annotation-key": "annotation-value"}
|
||||
expectedAppNewMetadata.ObjectMeta.Labels = map[string]string{
|
||||
"label-key": "label-value",
|
||||
LabelKeyAppSetInstance: "simple-cluster-generator",
|
||||
"label-key": "label-value",
|
||||
}
|
||||
}).
|
||||
Update(func(appset *v1alpha1.ApplicationSet) {
|
||||
appset.Spec.Template.Annotations = map[string]string{"annotation-key": "annotation-value"}
|
||||
appset.Spec.Template.Labels = map[string]string{
|
||||
"label-key": "label-value",
|
||||
LabelKeyAppSetInstance: "simple-cluster-generator",
|
||||
"label-key": "label-value",
|
||||
}
|
||||
}).Then().Expect(ApplicationsExist([]argov1alpha1.Application{*expectedAppNewMetadata})).
|
||||
|
||||
@@ -132,9 +127,6 @@ func TestSimpleClusterGenerator(t *testing.T) {
|
||||
Name: "cluster1-guestbook",
|
||||
Namespace: fixture.TestNamespace(),
|
||||
Finalizers: []string{"resources-finalizer.argocd.argoproj.io"},
|
||||
Labels: map[string]string{
|
||||
LabelKeyAppSetInstance: "simple-cluster-generator",
|
||||
},
|
||||
},
|
||||
Spec: argov1alpha1.ApplicationSpec{
|
||||
Project: "default",
|
||||
@@ -206,10 +198,7 @@ func TestSimpleClusterGenerator(t *testing.T) {
|
||||
And(func() {
|
||||
expectedAppNewMetadata = expectedAppNewNamespace.DeepCopy()
|
||||
expectedAppNewMetadata.ObjectMeta.Annotations = map[string]string{"annotation-key": "annotation-value"}
|
||||
expectedAppNewMetadata.ObjectMeta.Labels = map[string]string{
|
||||
LabelKeyAppSetInstance: "simple-cluster-generator",
|
||||
"label-key": "label-value",
|
||||
}
|
||||
expectedAppNewMetadata.ObjectMeta.Labels = map[string]string{"label-key": "label-value"}
|
||||
}).
|
||||
Update(func(appset *v1alpha1.ApplicationSet) {
|
||||
appset.Spec.Template.Annotations = map[string]string{"annotation-key": "annotation-value"}
|
||||
@@ -231,9 +220,6 @@ func TestClusterGeneratorWithLocalCluster(t *testing.T) {
|
||||
Name: "in-cluster-guestbook",
|
||||
Namespace: fixture.TestNamespace(),
|
||||
Finalizers: []string{"resources-finalizer.argocd.argoproj.io"},
|
||||
Labels: map[string]string{
|
||||
LabelKeyAppSetInstance: "in-cluster-generator",
|
||||
},
|
||||
},
|
||||
Spec: argov1alpha1.ApplicationSpec{
|
||||
Project: "default",
|
||||
@@ -326,10 +312,7 @@ func TestClusterGeneratorWithLocalCluster(t *testing.T) {
|
||||
And(func() {
|
||||
expectedAppNewMetadata = expectedAppNewNamespace.DeepCopy()
|
||||
expectedAppNewMetadata.ObjectMeta.Annotations = map[string]string{"annotation-key": "annotation-value"}
|
||||
expectedAppNewMetadata.ObjectMeta.Labels = map[string]string{
|
||||
"label-key": "label-value",
|
||||
LabelKeyAppSetInstance: "in-cluster-generator",
|
||||
}
|
||||
expectedAppNewMetadata.ObjectMeta.Labels = map[string]string{"label-key": "label-value"}
|
||||
}).
|
||||
Update(func(appset *v1alpha1.ApplicationSet) {
|
||||
appset.Spec.Template.Annotations = map[string]string{"annotation-key": "annotation-value"}
|
||||
@@ -354,9 +337,6 @@ func TestSimpleClusterGeneratorAddingCluster(t *testing.T) {
|
||||
Name: "{{name}}-guestbook",
|
||||
Namespace: fixture.TestNamespace(),
|
||||
Finalizers: []string{"resources-finalizer.argocd.argoproj.io"},
|
||||
Labels: map[string]string{
|
||||
LabelKeyAppSetInstance: "simple-cluster-generator",
|
||||
},
|
||||
},
|
||||
Spec: argov1alpha1.ApplicationSpec{
|
||||
Project: "default",
|
||||
@@ -439,9 +419,6 @@ func TestSimpleClusterGeneratorDeletingCluster(t *testing.T) {
|
||||
Name: "{{name}}-guestbook",
|
||||
Namespace: fixture.TestNamespace(),
|
||||
Finalizers: []string{"resources-finalizer.argocd.argoproj.io"},
|
||||
Labels: map[string]string{
|
||||
LabelKeyAppSetInstance: "simple-cluster-generator",
|
||||
},
|
||||
},
|
||||
Spec: argov1alpha1.ApplicationSpec{
|
||||
Project: "default",
|
||||
|
||||
@@ -25,11 +25,8 @@ func TestSimpleClusterDecisionResourceGeneratorExternalNamespace(t *testing.T) {
|
||||
APIVersion: "argoproj.io/v1alpha1",
|
||||
},
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "cluster1-guestbook",
|
||||
Namespace: utils.ArgoCDExternalNamespace,
|
||||
Labels: map[string]string{
|
||||
LabelKeyAppSetInstance: "simple-cluster-generator",
|
||||
},
|
||||
Name: "cluster1-guestbook",
|
||||
Namespace: utils.ArgoCDExternalNamespace,
|
||||
Finalizers: []string{"resources-finalizer.argocd.argoproj.io"},
|
||||
},
|
||||
Spec: argov1alpha1.ApplicationSpec{
|
||||
@@ -113,15 +110,13 @@ func TestSimpleClusterDecisionResourceGeneratorExternalNamespace(t *testing.T) {
|
||||
expectedAppNewMetadata = expectedAppNewNamespace.DeepCopy()
|
||||
expectedAppNewMetadata.ObjectMeta.Annotations = map[string]string{"annotation-key": "annotation-value"}
|
||||
expectedAppNewMetadata.ObjectMeta.Labels = map[string]string{
|
||||
"label-key": "label-value",
|
||||
LabelKeyAppSetInstance: "simple-cluster-generator",
|
||||
"label-key": "label-value",
|
||||
}
|
||||
}).
|
||||
Update(func(appset *v1alpha1.ApplicationSet) {
|
||||
appset.Spec.Template.Annotations = map[string]string{"annotation-key": "annotation-value"}
|
||||
appset.Spec.Template.Labels = map[string]string{
|
||||
"label-key": "label-value",
|
||||
LabelKeyAppSetInstance: "simple-cluster-generator",
|
||||
"label-key": "label-value",
|
||||
}
|
||||
}).Then().Expect(ApplicationsExist([]argov1alpha1.Application{*expectedAppNewMetadata})).
|
||||
|
||||
@@ -141,9 +136,6 @@ func TestSimpleClusterDecisionResourceGenerator(t *testing.T) {
|
||||
Name: "cluster1-guestbook",
|
||||
Namespace: fixture.TestNamespace(),
|
||||
Finalizers: []string{"resources-finalizer.argocd.argoproj.io"},
|
||||
Labels: map[string]string{
|
||||
LabelKeyAppSetInstance: "simple-cluster-generator",
|
||||
},
|
||||
},
|
||||
Spec: argov1alpha1.ApplicationSpec{
|
||||
Project: "default",
|
||||
@@ -223,10 +215,7 @@ func TestSimpleClusterDecisionResourceGenerator(t *testing.T) {
|
||||
And(func() {
|
||||
expectedAppNewMetadata = expectedAppNewNamespace.DeepCopy()
|
||||
expectedAppNewMetadata.ObjectMeta.Annotations = map[string]string{"annotation-key": "annotation-value"}
|
||||
expectedAppNewMetadata.ObjectMeta.Labels = map[string]string{
|
||||
LabelKeyAppSetInstance: "simple-cluster-generator",
|
||||
"label-key": "label-value",
|
||||
}
|
||||
expectedAppNewMetadata.ObjectMeta.Labels = map[string]string{"label-key": "label-value"}
|
||||
}).
|
||||
Update(func(appset *v1alpha1.ApplicationSet) {
|
||||
appset.Spec.Template.Annotations = map[string]string{"annotation-key": "annotation-value"}
|
||||
@@ -249,9 +238,6 @@ func TestSimpleClusterDecisionResourceGeneratorAddingCluster(t *testing.T) {
|
||||
Name: "{{name}}-guestbook",
|
||||
Namespace: fixture.TestNamespace(),
|
||||
Finalizers: []string{"resources-finalizer.argocd.argoproj.io"},
|
||||
Labels: map[string]string{
|
||||
LabelKeyAppSetInstance: "simple-cluster-generator",
|
||||
},
|
||||
},
|
||||
Spec: argov1alpha1.ApplicationSpec{
|
||||
Project: "default",
|
||||
@@ -347,9 +333,6 @@ func TestSimpleClusterDecisionResourceGeneratorDeletingClusterSecret(t *testing.
|
||||
Name: "{{name}}-guestbook",
|
||||
Namespace: fixture.TestNamespace(),
|
||||
Finalizers: []string{"resources-finalizer.argocd.argoproj.io"},
|
||||
Labels: map[string]string{
|
||||
LabelKeyAppSetInstance: "simple-cluster-generator",
|
||||
},
|
||||
},
|
||||
Spec: argov1alpha1.ApplicationSpec{
|
||||
Project: "default",
|
||||
@@ -447,9 +430,6 @@ func TestSimpleClusterDecisionResourceGeneratorDeletingClusterFromResource(t *te
|
||||
Name: "{{name}}-guestbook",
|
||||
Namespace: fixture.TestNamespace(),
|
||||
Finalizers: []string{"resources-finalizer.argocd.argoproj.io"},
|
||||
Labels: map[string]string{
|
||||
LabelKeyAppSetInstance: "simple-cluster-generator",
|
||||
},
|
||||
},
|
||||
Spec: argov1alpha1.ApplicationSpec{
|
||||
Project: "default",
|
||||
|
||||
@@ -26,9 +26,6 @@ func TestListMatrixGenerator(t *testing.T) {
|
||||
Name: fmt.Sprintf("%s-%s", cluster, name),
|
||||
Namespace: utils.TestNamespace(),
|
||||
Finalizers: []string{"resources-finalizer.argocd.argoproj.io"},
|
||||
Labels: map[string]string{
|
||||
LabelKeyAppSetInstance: "matrix-generator",
|
||||
},
|
||||
},
|
||||
Spec: argov1alpha1.ApplicationSpec{
|
||||
Project: "default",
|
||||
@@ -128,10 +125,7 @@ func TestListMatrixGenerator(t *testing.T) {
|
||||
for _, expectedApp := range expectedAppsNewNamespace {
|
||||
expectedAppNewMetadata := expectedApp.DeepCopy()
|
||||
expectedAppNewMetadata.ObjectMeta.Annotations = map[string]string{"annotation-key": "annotation-value"}
|
||||
expectedAppNewMetadata.ObjectMeta.Labels = map[string]string{
|
||||
"label-key": "label-value",
|
||||
LabelKeyAppSetInstance: "matrix-generator",
|
||||
}
|
||||
expectedAppNewMetadata.ObjectMeta.Labels = map[string]string{"label-key": "label-value"}
|
||||
expectedAppsNewMetadata = append(expectedAppsNewMetadata, *expectedAppNewMetadata)
|
||||
}
|
||||
}).
|
||||
@@ -156,9 +150,6 @@ func TestClusterMatrixGenerator(t *testing.T) {
|
||||
Name: fmt.Sprintf("%s-%s", cluster, name),
|
||||
Namespace: utils.TestNamespace(),
|
||||
Finalizers: []string{"resources-finalizer.argocd.argoproj.io"},
|
||||
Labels: map[string]string{
|
||||
LabelKeyAppSetInstance: "matrix-generator",
|
||||
},
|
||||
},
|
||||
Spec: argov1alpha1.ApplicationSpec{
|
||||
Project: "default",
|
||||
@@ -261,10 +252,7 @@ func TestClusterMatrixGenerator(t *testing.T) {
|
||||
for _, expectedApp := range expectedAppsNewNamespace {
|
||||
expectedAppNewMetadata := expectedApp.DeepCopy()
|
||||
expectedAppNewMetadata.ObjectMeta.Annotations = map[string]string{"annotation-key": "annotation-value"}
|
||||
expectedAppNewMetadata.ObjectMeta.Labels = map[string]string{
|
||||
"label-key": "label-value",
|
||||
LabelKeyAppSetInstance: "matrix-generator",
|
||||
}
|
||||
expectedAppNewMetadata.ObjectMeta.Labels = map[string]string{"label-key": "label-value"}
|
||||
expectedAppsNewMetadata = append(expectedAppsNewMetadata, *expectedAppNewMetadata)
|
||||
}
|
||||
}).
|
||||
@@ -289,9 +277,6 @@ func TestMatrixTerminalMatrixGeneratorSelector(t *testing.T) {
|
||||
Name: fmt.Sprintf("%s-%s", cluster, name),
|
||||
Namespace: utils.TestNamespace(),
|
||||
Finalizers: []string{"resources-finalizer.argocd.argoproj.io"},
|
||||
Labels: map[string]string{
|
||||
LabelKeyAppSetInstance: "matrix-generator-nested-matrix",
|
||||
},
|
||||
},
|
||||
Spec: argov1alpha1.ApplicationSpec{
|
||||
Project: "default",
|
||||
@@ -443,9 +428,6 @@ func TestMatrixTerminalMergeGeneratorSelector(t *testing.T) {
|
||||
Name: fmt.Sprintf("%s-%s", name, nameSuffix),
|
||||
Namespace: utils.TestNamespace(),
|
||||
Finalizers: []string{"resources-finalizer.argocd.argoproj.io"},
|
||||
Labels: map[string]string{
|
||||
LabelKeyAppSetInstance: "matrix-generator-nested-merge",
|
||||
},
|
||||
},
|
||||
Spec: argov1alpha1.ApplicationSpec{
|
||||
Project: "default",
|
||||
|
||||
@@ -27,9 +27,6 @@ func TestListMergeGenerator(t *testing.T) {
|
||||
Name: fmt.Sprintf("%s-%s", name, nameSuffix),
|
||||
Namespace: utils.TestNamespace(),
|
||||
Finalizers: []string{"resources-finalizer.argocd.argoproj.io"},
|
||||
Labels: map[string]string{
|
||||
LabelKeyAppSetInstance: "merge-generator",
|
||||
},
|
||||
},
|
||||
Spec: argov1alpha1.ApplicationSpec{
|
||||
Project: "default",
|
||||
@@ -125,10 +122,7 @@ func TestListMergeGenerator(t *testing.T) {
|
||||
for _, expectedApp := range expectedAppsNewNamespace {
|
||||
expectedAppNewMetadata := expectedApp.DeepCopy()
|
||||
expectedAppNewMetadata.ObjectMeta.Annotations = map[string]string{"annotation-key": "annotation-value"}
|
||||
expectedAppNewMetadata.ObjectMeta.Labels = map[string]string{
|
||||
"label-key": "label-value",
|
||||
LabelKeyAppSetInstance: "merge-generator",
|
||||
}
|
||||
expectedAppNewMetadata.ObjectMeta.Labels = map[string]string{"label-key": "label-value"}
|
||||
expectedAppsNewMetadata = append(expectedAppsNewMetadata, *expectedAppNewMetadata)
|
||||
}
|
||||
}).
|
||||
@@ -153,9 +147,6 @@ func TestClusterMergeGenerator(t *testing.T) {
|
||||
Name: fmt.Sprintf("%s-%s-%s", cluster, name, nameSuffix),
|
||||
Namespace: utils.TestNamespace(),
|
||||
Finalizers: []string{"resources-finalizer.argocd.argoproj.io"},
|
||||
Labels: map[string]string{
|
||||
LabelKeyAppSetInstance: "merge-generator",
|
||||
},
|
||||
},
|
||||
Spec: argov1alpha1.ApplicationSpec{
|
||||
Project: "default",
|
||||
@@ -276,10 +267,7 @@ func TestClusterMergeGenerator(t *testing.T) {
|
||||
for _, expectedApp := range expectedAppsNewNamespace {
|
||||
expectedAppNewMetadata := expectedApp.DeepCopy()
|
||||
expectedAppNewMetadata.ObjectMeta.Annotations = map[string]string{"annotation-key": "annotation-value"}
|
||||
expectedAppNewMetadata.ObjectMeta.Labels = map[string]string{
|
||||
"label-key": "label-value",
|
||||
LabelKeyAppSetInstance: "merge-generator",
|
||||
}
|
||||
expectedAppNewMetadata.ObjectMeta.Labels = map[string]string{"label-key": "label-value"}
|
||||
expectedAppsNewMetadata = append(expectedAppsNewMetadata, *expectedAppNewMetadata)
|
||||
}
|
||||
}).
|
||||
@@ -304,9 +292,6 @@ func TestMergeTerminalMergeGeneratorSelector(t *testing.T) {
|
||||
Name: fmt.Sprintf("%s-%s", name, nameSuffix),
|
||||
Namespace: utils.TestNamespace(),
|
||||
Finalizers: []string{"resources-finalizer.argocd.argoproj.io"},
|
||||
Labels: map[string]string{
|
||||
LabelKeyAppSetInstance: "merge-generator-nested-merge",
|
||||
},
|
||||
},
|
||||
Spec: argov1alpha1.ApplicationSpec{
|
||||
Project: "default",
|
||||
|
||||
23
test/e2e/testdata/guestbook-with-plain-namespace-manifest/guestbook-ui-deployment.yaml
vendored
Normal file
23
test/e2e/testdata/guestbook-with-plain-namespace-manifest/guestbook-ui-deployment.yaml
vendored
Normal file
@@ -0,0 +1,23 @@
|
||||
apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
metadata:
|
||||
name: guestbook-ui
|
||||
labels:
|
||||
test: "true"
|
||||
spec:
|
||||
replicas: 0
|
||||
revisionHistoryLimit: 3
|
||||
selector:
|
||||
matchLabels:
|
||||
app: guestbook-ui
|
||||
template:
|
||||
metadata:
|
||||
labels:
|
||||
app: guestbook-ui
|
||||
spec:
|
||||
containers:
|
||||
- image: quay.io/argoprojlabs/argocd-e2e-container:0.2
|
||||
imagePullPolicy: IfNotPresent
|
||||
name: guestbook-ui
|
||||
ports:
|
||||
- containerPort: 80
|
||||
9
test/e2e/testdata/guestbook-with-plain-namespace-manifest/guestbook-ui-namespace.yaml
vendored
Normal file
9
test/e2e/testdata/guestbook-with-plain-namespace-manifest/guestbook-ui-namespace.yaml
vendored
Normal file
@@ -0,0 +1,9 @@
|
||||
apiVersion: v1
|
||||
kind: Namespace
|
||||
metadata:
|
||||
name: guestbook-ui-with-namespace-manifest
|
||||
labels:
|
||||
test: "true"
|
||||
annotations:
|
||||
foo: bar
|
||||
something: else
|
||||
10
test/e2e/testdata/guestbook-with-plain-namespace-manifest/guestbook-ui-svc.yaml
vendored
Normal file
10
test/e2e/testdata/guestbook-with-plain-namespace-manifest/guestbook-ui-svc.yaml
vendored
Normal file
@@ -0,0 +1,10 @@
|
||||
apiVersion: v1
|
||||
kind: Service
|
||||
metadata:
|
||||
name: guestbook-ui
|
||||
spec:
|
||||
ports:
|
||||
- port: 80
|
||||
targetPort: 80
|
||||
selector:
|
||||
app: guestbook-ui
|
||||
@@ -32,7 +32,7 @@ export const RevisionMetadataRows = (props: {applicationName: string; applicatio
|
||||
<div className='columns small-9'>{m.description}</div>
|
||||
</div>
|
||||
)}
|
||||
{m.maintainers.length > 0 && (
|
||||
{m.maintainers && m.maintainers.length > 0 && (
|
||||
<div className='row'>
|
||||
<div className='columns small-3'>Maintainers:</div>
|
||||
<div className='columns small-9'>{m.maintainers.join(', ')}</div>
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import {DropDownMenu, NotificationType, SlidingPanel} from 'argo-ui';
|
||||
import {DropDownMenu, NotificationType, SlidingPanel, Tooltip} from 'argo-ui';
|
||||
import * as classNames from 'classnames';
|
||||
import * as PropTypes from 'prop-types';
|
||||
import * as React from 'react';
|
||||
@@ -147,7 +147,8 @@ export class ApplicationDetails extends React.Component<RouteComponentProps<{app
|
||||
this.setState({slidingPanelPage: 0});
|
||||
}
|
||||
|
||||
private toggleCompactView(pref: AppDetailsPreferences) {
|
||||
private toggleCompactView(appName: string, pref: AppDetailsPreferences) {
|
||||
pref.userHelpTipMsgs = pref.userHelpTipMsgs.map(usrMsg => (usrMsg.appName === appName && usrMsg.msgKey === 'groupNodes' ? {...usrMsg, display: true} : usrMsg));
|
||||
services.viewPreferences.updatePreferences({appDetails: {...pref, groupNodes: !pref.groupNodes}});
|
||||
}
|
||||
|
||||
@@ -231,6 +232,7 @@ export class ApplicationDetails extends React.Component<RouteComponentProps<{app
|
||||
const syncResourceKey = new URLSearchParams(this.props.history.location.search).get('deploy');
|
||||
const tab = new URLSearchParams(this.props.history.location.search).get('tab');
|
||||
const source = getAppDefaultSource(application);
|
||||
const showToolTip = pref?.userHelpTipMsgs.find(usrMsg => usrMsg.appName === application.metadata.name);
|
||||
const resourceNodes = (): any[] => {
|
||||
const statusByKey = new Map<string, models.ResourceStatus>();
|
||||
application.status.resources.forEach(res => statusByKey.set(AppUtils.nodeKey(res), res));
|
||||
@@ -296,6 +298,14 @@ export class ApplicationDetails extends React.Component<RouteComponentProps<{app
|
||||
const setShowCompactNodes = (showCompactView: boolean) => {
|
||||
services.viewPreferences.updatePreferences({appDetails: {...pref, groupNodes: showCompactView}});
|
||||
};
|
||||
const updateHelpTipState = (usrHelpTip: models.UserMessages) => {
|
||||
const existingIndex = pref.userHelpTipMsgs.findIndex(msg => msg.appName === usrHelpTip.appName && msg.msgKey === usrHelpTip.msgKey);
|
||||
if (existingIndex !== -1) {
|
||||
pref.userHelpTipMsgs[existingIndex] = usrHelpTip;
|
||||
} else {
|
||||
(pref.userHelpTipMsgs || []).push(usrHelpTip);
|
||||
}
|
||||
};
|
||||
const toggleNameDirection = () => {
|
||||
this.setState({truncateNameOnRight: !this.state.truncateNameOnRight});
|
||||
};
|
||||
@@ -446,13 +456,19 @@ export class ApplicationDetails extends React.Component<RouteComponentProps<{app
|
||||
/>
|
||||
</a>
|
||||
{(pref.view === 'tree' || pref.view === 'network') && (
|
||||
<a
|
||||
className={`group-nodes-button group-nodes-button${!pref.groupNodes ? '' : '-on'}`}
|
||||
title={pref.view === 'tree' ? 'Group Nodes' : 'Collapse Pods'}
|
||||
onClick={() => this.toggleCompactView(pref)}>
|
||||
<i className={classNames('fa fa-object-group fa-fw')} />
|
||||
</a>
|
||||
<Tooltip
|
||||
content={AppUtils.userMsgsList[showToolTip?.msgKey] || 'Group Nodes'}
|
||||
visible={pref.groupNodes && showToolTip !== undefined && !showToolTip?.display}
|
||||
duration={showToolTip?.duration}>
|
||||
<a
|
||||
className={`group-nodes-button group-nodes-button${!pref.groupNodes ? '' : '-on'}`}
|
||||
title={pref.view === 'tree' ? 'Group Nodes' : 'Collapse Pods'}
|
||||
onClick={() => this.toggleCompactView(application.metadata.name, pref)}>
|
||||
<i className={classNames('fa fa-object-group fa-fw')} />
|
||||
</a>
|
||||
</Tooltip>
|
||||
)}
|
||||
|
||||
<span className={`separator`} />
|
||||
<a className={`group-nodes-button`} onClick={() => expandAll()} title='Expand all child nodes of all parent nodes'>
|
||||
<i className='fa fa-plus fa-fw' />
|
||||
@@ -481,6 +497,7 @@ export class ApplicationDetails extends React.Component<RouteComponentProps<{app
|
||||
)
|
||||
}
|
||||
showCompactNodes={pref.groupNodes}
|
||||
userMsgs={pref.userHelpTipMsgs}
|
||||
tree={tree}
|
||||
app={application}
|
||||
showOrphanedResources={pref.orphanedResources}
|
||||
@@ -493,6 +510,7 @@ export class ApplicationDetails extends React.Component<RouteComponentProps<{app
|
||||
nameDirection={this.state.truncateNameOnRight}
|
||||
filters={pref.resourceFilter}
|
||||
setTreeFilterGraph={setFilterGraph}
|
||||
updateUsrHelpTipMsgs={updateHelpTipState}
|
||||
setShowCompactNodes={setShowCompactNodes}
|
||||
setNodeExpansion={(node, isExpanded) => this.setNodeExpansion(node, isExpanded)}
|
||||
getNodeExpansion={node => this.getNodeExpansion(node)}
|
||||
@@ -651,7 +669,7 @@ export class ApplicationDetails extends React.Component<RouteComponentProps<{app
|
||||
<div className='columns small-9'>{m.description}</div>
|
||||
</div>
|
||||
)}
|
||||
{m.maintainers.length > 0 && (
|
||||
{m.maintainers && m.maintainers.length > 0 && (
|
||||
<div className='row white-box__details-row'>
|
||||
<div className='columns small-3'>Maintainers:</div>
|
||||
<div className='columns small-9'>{m.maintainers.join(', ')}</div>
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import {DropDown, DropDownMenu, NotificationType, Tooltip} from 'argo-ui';
|
||||
import {DropDown, DropDownMenu, Tooltip} from 'argo-ui';
|
||||
import * as classNames from 'classnames';
|
||||
import * as dagre from 'dagre';
|
||||
import * as React from 'react';
|
||||
@@ -22,7 +22,8 @@ import {
|
||||
isYoungerThanXMinutes,
|
||||
NodeId,
|
||||
nodeKey,
|
||||
PodHealthIcon
|
||||
PodHealthIcon,
|
||||
getUsrMsgKeyToDisplay
|
||||
} from '../utils';
|
||||
import {NodeUpdateAnimation} from './node-update-animation';
|
||||
import {PodGroup} from '../application-pod-view/pod-view';
|
||||
@@ -59,6 +60,8 @@ export interface ApplicationResourceTreeProps {
|
||||
appContext?: AppContext;
|
||||
showOrphanedResources: boolean;
|
||||
showCompactNodes: boolean;
|
||||
userMsgs: models.UserMessages[];
|
||||
updateUsrHelpTipMsgs: (userMsgs: models.UserMessages) => void;
|
||||
setShowCompactNodes: (showCompactNodes: boolean) => void;
|
||||
zoom: number;
|
||||
podGroupCount: number;
|
||||
@@ -927,6 +930,7 @@ export const ApplicationResourceTree = (props: ApplicationResourceTreeProps) =>
|
||||
const [filters, setFilters] = React.useState(props.filters);
|
||||
const [filteredGraph, setFilteredGraph] = React.useState([]);
|
||||
const filteredNodes: any[] = [];
|
||||
|
||||
React.useEffect(() => {
|
||||
if (props.filters !== filters) {
|
||||
setFilters(props.filters);
|
||||
@@ -934,19 +938,16 @@ export const ApplicationResourceTree = (props: ApplicationResourceTreeProps) =>
|
||||
props.setTreeFilterGraph(filteredGraph);
|
||||
}
|
||||
}, [props.filters]);
|
||||
|
||||
const {podGroupCount, userMsgs, updateUsrHelpTipMsgs, setShowCompactNodes} = props;
|
||||
const podCount = nodes.filter(node => node.kind === 'Pod').length;
|
||||
|
||||
React.useEffect(() => {
|
||||
const {podGroupCount, setShowCompactNodes, appContext} = props;
|
||||
if (podCount > podGroupCount) {
|
||||
setShowCompactNodes(true);
|
||||
appContext.apis.notifications.show({
|
||||
content: `Since the number of pods has surpassed the threshold pod count of ${podGroupCount}, you will now be switched to the group node view.
|
||||
If you prefer the tree view, you can simply click on the Group Nodes toolbar button to deselect the current view.`,
|
||||
type: NotificationType.Success
|
||||
});
|
||||
} else {
|
||||
props.setShowCompactNodes(false);
|
||||
const userMsg = getUsrMsgKeyToDisplay(appNode.name, 'groupNodes', userMsgs);
|
||||
updateUsrHelpTipMsgs(userMsg);
|
||||
if (!userMsg.display) {
|
||||
setShowCompactNodes(true);
|
||||
}
|
||||
}
|
||||
}, [podCount]);
|
||||
|
||||
|
||||
@@ -1252,3 +1252,17 @@ export function formatCreationTimestamp(creationTimestamp: string) {
|
||||
}
|
||||
|
||||
export const selectPostfix = (arr: string[], singular: string, plural: string) => (arr.length > 1 ? plural : singular);
|
||||
|
||||
export function getUsrMsgKeyToDisplay(appName: string, msgKey: string, usrMessages: appModels.UserMessages[]) {
|
||||
const usrMsg = usrMessages?.find((msg: appModels.UserMessages) => msg.appName === appName && msg.msgKey === msgKey);
|
||||
if (usrMsg !== undefined) {
|
||||
return {...usrMsg, display: true};
|
||||
} else {
|
||||
return {appName, msgKey, display: false, duration: 1} as appModels.UserMessages;
|
||||
}
|
||||
}
|
||||
|
||||
export const userMsgsList: {[key: string]: string} = {
|
||||
groupNodes: `Since the number of pods has surpassed the threshold pod count of 15, you will now be switched to the group node view.
|
||||
If you prefer the tree view, you can simply click on the Group Nodes toolbar button to deselect the current view.`
|
||||
};
|
||||
|
||||
@@ -955,3 +955,12 @@ export interface LinkInfo {
|
||||
export interface LinksResponse {
|
||||
items: LinkInfo[];
|
||||
}
|
||||
|
||||
export interface UserMessages {
|
||||
appName: string;
|
||||
msgKey: string;
|
||||
display: boolean;
|
||||
condition?: HealthStatusCode;
|
||||
duration?: number;
|
||||
animation?: string;
|
||||
}
|
||||
|
||||
@@ -2,6 +2,7 @@ import * as deepMerge from 'deepmerge';
|
||||
import {BehaviorSubject, Observable} from 'rxjs';
|
||||
|
||||
import {PodGroupType} from '../../applications/components/application-pod-view/pod-view';
|
||||
import {UserMessages} from '../models';
|
||||
|
||||
export type AppsDetailsViewType = 'tree' | 'network' | 'list' | 'pods';
|
||||
|
||||
@@ -28,6 +29,7 @@ export interface AppDetailsPreferences {
|
||||
groupNodes?: boolean;
|
||||
zoom: number;
|
||||
podGroupCount: number;
|
||||
userHelpTipMsgs: UserMessages[];
|
||||
}
|
||||
|
||||
export interface PodViewPreferences {
|
||||
@@ -122,7 +124,8 @@ const DEFAULT_PREFERENCES: ViewPreferences = {
|
||||
followLogs: false,
|
||||
wrapLines: false,
|
||||
zoom: 1.0,
|
||||
podGroupCount: 15.0
|
||||
podGroupCount: 15.0,
|
||||
userHelpTipMsgs: []
|
||||
},
|
||||
appList: {
|
||||
view: 'tiles' as AppsListViewType,
|
||||
|
||||
@@ -165,7 +165,7 @@ func (rt *resourceTracking) BuildAppInstanceValue(value AppInstanceValue) string
|
||||
//ParseAppInstanceValue parse resource tracking id from format <application-name>:<group>/<kind>:<namespace>/<name> to struct
|
||||
func (rt *resourceTracking) ParseAppInstanceValue(value string) (*AppInstanceValue, error) {
|
||||
var appInstanceValue AppInstanceValue
|
||||
parts := strings.Split(value, ":")
|
||||
parts := strings.SplitN(value, ":", 3)
|
||||
appInstanceValue.ApplicationName = parts[0]
|
||||
if len(parts) != 3 {
|
||||
return nil, WrongResourceTrackingFormat
|
||||
|
||||
@@ -89,6 +89,19 @@ func TestParseAppInstanceValue(t *testing.T) {
|
||||
assert.Equal(t, appInstanceValue.Name, "<name>")
|
||||
}
|
||||
|
||||
func TestParseAppInstanceValueColon(t *testing.T) {
|
||||
resourceTracking := NewResourceTracking()
|
||||
appInstanceValue, err := resourceTracking.ParseAppInstanceValue("app:<group>/<kind>:<namespace>/<name>:<colon>")
|
||||
if !assert.NoError(t, err) {
|
||||
t.Fatal()
|
||||
}
|
||||
assert.Equal(t, appInstanceValue.ApplicationName, "app")
|
||||
assert.Equal(t, appInstanceValue.Group, "<group>")
|
||||
assert.Equal(t, appInstanceValue.Kind, "<kind>")
|
||||
assert.Equal(t, appInstanceValue.Namespace, "<namespace>")
|
||||
assert.Equal(t, appInstanceValue.Name, "<name>:<colon>")
|
||||
}
|
||||
|
||||
func TestParseAppInstanceValueWrongFormat1(t *testing.T) {
|
||||
resourceTracking := NewResourceTracking()
|
||||
_, err := resourceTracking.ParseAppInstanceValue("app")
|
||||
|
||||
@@ -345,6 +345,9 @@ func clusterToSecret(c *appv1.Cluster, secret *apiv1.Secret) error {
|
||||
secret.Data = data
|
||||
|
||||
secret.Labels = c.Labels
|
||||
if c.Annotations != nil && c.Annotations[apiv1.LastAppliedConfigAnnotation] != "" {
|
||||
return status.Errorf(codes.InvalidArgument, "annotation %s cannot be set", apiv1.LastAppliedConfigAnnotation)
|
||||
}
|
||||
secret.Annotations = c.Annotations
|
||||
|
||||
if secret.Annotations == nil {
|
||||
@@ -403,6 +406,8 @@ func SecretToCluster(s *apiv1.Secret) (*appv1.Cluster, error) {
|
||||
annotations := map[string]string{}
|
||||
if s.Annotations != nil {
|
||||
annotations = collections.CopyStringMap(s.Annotations)
|
||||
// delete system annotations
|
||||
delete(annotations, apiv1.LastAppliedConfigAnnotation)
|
||||
delete(annotations, common.AnnotationKeyManagedBy)
|
||||
}
|
||||
|
||||
|
||||
@@ -7,6 +7,8 @@ import (
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
"google.golang.org/grpc/codes"
|
||||
"google.golang.org/grpc/status"
|
||||
v1 "k8s.io/api/core/v1"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/client-go/kubernetes/fake"
|
||||
@@ -56,6 +58,24 @@ func Test_secretToCluster(t *testing.T) {
|
||||
})
|
||||
}
|
||||
|
||||
func Test_secretToCluster_LastAppliedConfigurationDropped(t *testing.T) {
|
||||
secret := &v1.Secret{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "mycluster",
|
||||
Namespace: fakeNamespace,
|
||||
Annotations: map[string]string{v1.LastAppliedConfigAnnotation: "val2"},
|
||||
},
|
||||
Data: map[string][]byte{
|
||||
"name": []byte("test"),
|
||||
"server": []byte("http://mycluster"),
|
||||
"config": []byte("{\"username\":\"foo\"}"),
|
||||
},
|
||||
}
|
||||
cluster, err := SecretToCluster(secret)
|
||||
require.NoError(t, err)
|
||||
assert.Len(t, cluster.Annotations, 0)
|
||||
}
|
||||
|
||||
func TestClusterToSecret(t *testing.T) {
|
||||
cluster := &appv1.Cluster{
|
||||
Server: "server",
|
||||
@@ -78,6 +98,21 @@ func TestClusterToSecret(t *testing.T) {
|
||||
assert.Equal(t, cluster.Labels, s.Labels)
|
||||
}
|
||||
|
||||
func TestClusterToSecret_LastAppliedConfigurationRejected(t *testing.T) {
|
||||
cluster := &appv1.Cluster{
|
||||
Server: "server",
|
||||
Annotations: map[string]string{v1.LastAppliedConfigAnnotation: "val2"},
|
||||
Name: "test",
|
||||
Config: v1alpha1.ClusterConfig{},
|
||||
Project: "project",
|
||||
Namespaces: []string{"default"},
|
||||
}
|
||||
s := &v1.Secret{}
|
||||
err := clusterToSecret(cluster, s)
|
||||
require.Error(t, err)
|
||||
require.Equal(t, codes.InvalidArgument, status.Code(err))
|
||||
}
|
||||
|
||||
func Test_secretToCluster_NoConfig(t *testing.T) {
|
||||
secret := &v1.Secret{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
|
||||
33
util/grpc/trace.go
Normal file
33
util/grpc/trace.go
Normal file
@@ -0,0 +1,33 @@
|
||||
package grpc
|
||||
|
||||
import (
|
||||
"sync"
|
||||
|
||||
"go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc"
|
||||
"google.golang.org/grpc"
|
||||
)
|
||||
|
||||
var (
|
||||
otelUnaryInterceptor grpc.UnaryClientInterceptor
|
||||
otelStreamInterceptor grpc.StreamClientInterceptor
|
||||
interceptorsInitialized = sync.Once{}
|
||||
)
|
||||
|
||||
// otel interceptors must be created once to avoid memory leak
|
||||
// see https://github.com/open-telemetry/opentelemetry-go-contrib/issues/4226 for details
|
||||
func ensureInitialized() {
|
||||
interceptorsInitialized.Do(func() {
|
||||
otelUnaryInterceptor = otelgrpc.UnaryClientInterceptor()
|
||||
otelStreamInterceptor = otelgrpc.StreamClientInterceptor()
|
||||
})
|
||||
}
|
||||
|
||||
func OTELUnaryClientInterceptor() grpc.UnaryClientInterceptor {
|
||||
ensureInitialized()
|
||||
return otelUnaryInterceptor
|
||||
}
|
||||
|
||||
func OTELStreamClientInterceptor() grpc.StreamClientInterceptor {
|
||||
ensureInitialized()
|
||||
return otelStreamInterceptor
|
||||
}
|
||||
@@ -8,6 +8,7 @@ import (
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
executil "github.com/argoproj/argo-cd/v2/util/exec"
|
||||
"io"
|
||||
"net/http"
|
||||
"net/url"
|
||||
@@ -25,7 +26,6 @@ import (
|
||||
"oras.land/oras-go/v2/registry/remote/auth"
|
||||
|
||||
"github.com/argoproj/argo-cd/v2/util/cache"
|
||||
executil "github.com/argoproj/argo-cd/v2/util/exec"
|
||||
argoio "github.com/argoproj/argo-cd/v2/util/io"
|
||||
"github.com/argoproj/argo-cd/v2/util/io/files"
|
||||
"github.com/argoproj/argo-cd/v2/util/proxy"
|
||||
@@ -52,7 +52,7 @@ type indexCache interface {
|
||||
|
||||
type Client interface {
|
||||
CleanChartCache(chart string, version string) error
|
||||
ExtractChart(chart string, version string, passCredentials bool) (string, argoio.Closer, error)
|
||||
ExtractChart(chart string, version string, passCredentials bool, manifestMaxExtractedSize int64, disableManifestMaxExtractedSize bool) (string, argoio.Closer, error)
|
||||
GetIndex(noCache bool) (*Index, error)
|
||||
GetTags(chart string, noCache bool) (*TagsList, error)
|
||||
TestHelmOCI() (bool, error)
|
||||
@@ -122,7 +122,21 @@ func (c *nativeHelmChart) CleanChartCache(chart string, version string) error {
|
||||
return os.RemoveAll(cachePath)
|
||||
}
|
||||
|
||||
func (c *nativeHelmChart) ExtractChart(chart string, version string, passCredentials bool) (string, argoio.Closer, error) {
|
||||
func untarChart(tempDir string, cachedChartPath string, manifestMaxExtractedSize int64, disableManifestMaxExtractedSize bool) error {
|
||||
if disableManifestMaxExtractedSize {
|
||||
cmd := exec.Command("tar", "-zxvf", cachedChartPath)
|
||||
cmd.Dir = tempDir
|
||||
_, err := executil.Run(cmd)
|
||||
return err
|
||||
}
|
||||
reader, err := os.Open(cachedChartPath)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return files.Untgz(tempDir, reader, manifestMaxExtractedSize, false)
|
||||
}
|
||||
|
||||
func (c *nativeHelmChart) ExtractChart(chart string, version string, passCredentials bool, manifestMaxExtractedSize int64, disableManifestMaxExtractedSize bool) (string, argoio.Closer, error) {
|
||||
// always use Helm V3 since we don't have chart content to determine correct Helm version
|
||||
helmCmd, err := NewCmdWithVersion("", HelmV3, c.enableOci, c.proxy)
|
||||
|
||||
@@ -196,15 +210,14 @@ func (c *nativeHelmChart) ExtractChart(chart string, version string, passCredent
|
||||
if len(infos) != 1 {
|
||||
return "", nil, fmt.Errorf("expected 1 file, found %v", len(infos))
|
||||
}
|
||||
|
||||
err = os.Rename(filepath.Join(tempDest, infos[0].Name()), cachedChartPath)
|
||||
if err != nil {
|
||||
return "", nil, err
|
||||
}
|
||||
}
|
||||
|
||||
cmd := exec.Command("tar", "-zxvf", cachedChartPath)
|
||||
cmd.Dir = tempDir
|
||||
_, err = executil.Run(cmd)
|
||||
err = untarChart(tempDir, cachedChartPath, manifestMaxExtractedSize, disableManifestMaxExtractedSize)
|
||||
if err != nil {
|
||||
_ = os.RemoveAll(tempDir)
|
||||
return "", nil, err
|
||||
|
||||
@@ -4,6 +4,7 @@ import (
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"math"
|
||||
"os"
|
||||
"strings"
|
||||
"testing"
|
||||
@@ -71,7 +72,7 @@ func TestIndex(t *testing.T) {
|
||||
|
||||
func Test_nativeHelmChart_ExtractChart(t *testing.T) {
|
||||
client := NewClient("https://argoproj.github.io/argo-helm", Creds{}, false, "")
|
||||
path, closer, err := client.ExtractChart("argo-cd", "0.7.1", false)
|
||||
path, closer, err := client.ExtractChart("argo-cd", "0.7.1", false, math.MaxInt64, true)
|
||||
assert.NoError(t, err)
|
||||
defer io.Close(closer)
|
||||
info, err := os.Stat(path)
|
||||
@@ -79,9 +80,15 @@ func Test_nativeHelmChart_ExtractChart(t *testing.T) {
|
||||
assert.True(t, info.IsDir())
|
||||
}
|
||||
|
||||
func Test_nativeHelmChart_ExtractChartWithLimiter(t *testing.T) {
|
||||
client := NewClient("https://argoproj.github.io/argo-helm", Creds{}, false, "")
|
||||
_, _, err := client.ExtractChart("argo-cd", "0.7.1", false, 100, false)
|
||||
assert.Error(t, err, "error while iterating on tar reader: unexpected EOF")
|
||||
}
|
||||
|
||||
func Test_nativeHelmChart_ExtractChart_insecure(t *testing.T) {
|
||||
client := NewClient("https://argoproj.github.io/argo-helm", Creds{InsecureSkipVerify: true}, false, "")
|
||||
path, closer, err := client.ExtractChart("argo-cd", "0.7.1", false)
|
||||
path, closer, err := client.ExtractChart("argo-cd", "0.7.1", false, math.MaxInt64, true)
|
||||
assert.NoError(t, err)
|
||||
defer io.Close(closer)
|
||||
info, err := os.Stat(path)
|
||||
|
||||
@@ -29,7 +29,7 @@ func (_m *Client) CleanChartCache(chart string, version string) error {
|
||||
}
|
||||
|
||||
// ExtractChart provides a mock function with given fields: chart, version
|
||||
func (_m *Client) ExtractChart(chart string, version string, passCredentials bool) (string, io.Closer, error) {
|
||||
func (_m *Client) ExtractChart(chart string, version string, passCredentials bool, manifestMaxExtractedSize int64, disableManifestMaxExtractedSize bool) (string, io.Closer, error) {
|
||||
ret := _m.Called(chart, version)
|
||||
|
||||
var r0 string
|
||||
|
||||
@@ -18,8 +18,8 @@ import (
|
||||
const maxCookieLength = 4093
|
||||
|
||||
// max number of chunks a cookie can be broken into. To be compatible with
|
||||
// widest range of browsers, we shouldn't create more than 30 cookies per domain
|
||||
var maxCookieNumber = env.ParseNumFromEnv(common.EnvMaxCookieNumber, 10, 0, 30)
|
||||
// widest range of browsers, you shouldn't create more than 30 cookies per domain
|
||||
var maxCookieNumber = env.ParseNumFromEnv(common.EnvMaxCookieNumber, 20, 0, math.MaxInt64)
|
||||
|
||||
// MakeCookieMetadata generates a string representing a Web cookie. Yum!
|
||||
func MakeCookieMetadata(key, value string, flags ...string) ([]string, error) {
|
||||
|
||||
@@ -15,10 +15,18 @@ func TestCookieMaxLength(t *testing.T) {
|
||||
|
||||
// keys will be of format foo, foo-1, foo-2 ..
|
||||
cookies, err = MakeCookieMetadata("foo", strings.Repeat("_", (maxCookieLength-5)*maxCookieNumber))
|
||||
assert.EqualError(t, err, "the authentication token is 40880 characters long and requires 11 cookies but the max number of cookies is 10. Contact your Argo CD administrator to increase the max number of cookies")
|
||||
assert.EqualError(t, err, "the authentication token is 81760 characters long and requires 21 cookies but the max number of cookies is 20. Contact your Argo CD administrator to increase the max number of cookies")
|
||||
assert.Equal(t, 0, len(cookies))
|
||||
}
|
||||
|
||||
func TestCookieWithAttributes(t *testing.T) {
|
||||
flags := []string{"SameSite=lax", "httpOnly"}
|
||||
|
||||
cookies, err := MakeCookieMetadata("foo", "bar", flags...)
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, "foo=bar; SameSite=lax; httpOnly", cookies[0])
|
||||
}
|
||||
|
||||
func TestSplitCookie(t *testing.T) {
|
||||
cookieValue := strings.Repeat("_", (maxCookieLength-6)*4)
|
||||
cookies, err := MakeCookieMetadata("foo", cookieValue)
|
||||
|
||||
Reference in New Issue
Block a user