mirror of
https://github.com/argoproj/argo-cd.git
synced 2026-03-18 14:28:53 +01:00
Compare commits
44 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 | ||
|
|
2bc94af7bd | ||
|
|
b9a32bb86e | ||
|
|
356e33ac29 | ||
|
|
9ffef110da | ||
|
|
af721bbb63 | ||
|
|
91d249ec84 | ||
|
|
004ca26b9e | ||
|
|
2a17ca57ec | ||
|
|
89495d72df | ||
|
|
885bb57ae8 | ||
|
|
d6d7b1452d | ||
|
|
490c78b75f | ||
|
|
092e55e279 | ||
|
|
fe526dd33c | ||
|
|
cade0e970d | ||
|
|
e58eaaf36f | ||
|
|
0c4e249922 | ||
|
|
8ea6650dd7 |
@@ -1,4 +1,4 @@
|
||||
ARG BASE_IMAGE=docker.io/library/ubuntu:22.04@sha256:ac58ff7fe25edc58bdf0067ca99df00014dbd032e2246d30a722fa348fd799a5
|
||||
ARG BASE_IMAGE=docker.io/library/ubuntu:22.04@sha256:0bced47fffa3361afa981854fcabcd4577cd43cebbb808cea2b1f33a3dd7f508
|
||||
####################################################################################################
|
||||
# Builder image
|
||||
# Initial stage which pulls prepares build dependencies and CLI tooling we need for our final image
|
||||
|
||||
@@ -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()))
|
||||
|
||||
@@ -97,6 +97,14 @@ func runCommand(ctx context.Context, command Command, path string, env []string)
|
||||
<-ctx.Done()
|
||||
// Kill by group ID to make sure child processes are killed. The - tells `kill` that it's a group ID.
|
||||
// Since we didn't set Pgid in SysProcAttr, the group ID is the same as the process ID. https://pkg.go.dev/syscall#SysProcAttr
|
||||
|
||||
// Sending a TERM signal first to allow any potential cleanup if needed, and then sending a KILL signal
|
||||
_ = sysCallTerm(-cmd.Process.Pid)
|
||||
|
||||
// modify cleanup timeout to allow process to cleanup
|
||||
cleanupTimeout := 5 * time.Second
|
||||
time.Sleep(cleanupTimeout)
|
||||
|
||||
_ = sysCallKill(-cmd.Process.Pid)
|
||||
}()
|
||||
|
||||
|
||||
@@ -369,6 +369,28 @@ func TestRunCommandEmptyCommand(t *testing.T) {
|
||||
assert.ErrorContains(t, err, "Command is empty")
|
||||
}
|
||||
|
||||
// TestRunCommandContextTimeoutWithGracefulTermination makes sure that the process is given enough time to cleanup before sending SIGKILL.
|
||||
func TestRunCommandContextTimeoutWithCleanup(t *testing.T) {
|
||||
ctx, cancel := context.WithTimeout(context.Background(), 900*time.Millisecond)
|
||||
defer cancel()
|
||||
|
||||
// Use a subshell so there's a child command.
|
||||
// This command sleeps for 4 seconds which is currently less than the 5 second delay between SIGTERM and SIGKILL signal and then exits successfully.
|
||||
command := Command{
|
||||
Command: []string{"sh", "-c"},
|
||||
Args: []string{`(trap 'echo "cleanup completed"; exit' TERM; sleep 4)`},
|
||||
}
|
||||
|
||||
before := time.Now()
|
||||
output, err := runCommand(ctx, command, "", []string{})
|
||||
after := time.Now()
|
||||
|
||||
assert.Error(t, err) // The command should time out, causing an error.
|
||||
assert.Less(t, after.Sub(before), 1*time.Second)
|
||||
// The command should still have completed the cleanup after termination.
|
||||
assert.Contains(t, output, "cleanup completed")
|
||||
}
|
||||
|
||||
func Test_getParametersAnnouncement_empty_command(t *testing.T) {
|
||||
staticYAML := `
|
||||
- name: static-a
|
||||
|
||||
@@ -14,3 +14,7 @@ func newSysProcAttr(setpgid bool) *syscall.SysProcAttr {
|
||||
func sysCallKill(pid int) error {
|
||||
return syscall.Kill(pid, syscall.SIGKILL)
|
||||
}
|
||||
|
||||
func sysCallTerm(pid int) error {
|
||||
return syscall.Kill(pid, syscall.SIGTERM)
|
||||
}
|
||||
|
||||
@@ -14,3 +14,7 @@ func newSysProcAttr(setpgid bool) *syscall.SysProcAttr {
|
||||
func sysCallKill(pid int) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func sysCallTerm(pid int) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -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: "*"
|
||||
|
||||
@@ -157,7 +157,7 @@ Or, a shorter way (using [path.Match](https://golang.org/pkg/path/#Match) syntax
|
||||
|
||||
```yaml
|
||||
- path: /d/*
|
||||
- path: /d/[f|g]
|
||||
- path: /d/[fg]
|
||||
exclude: true
|
||||
```
|
||||
|
||||
|
||||
@@ -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:
|
||||
|
||||
|
||||
@@ -15,7 +15,7 @@ As an experimental feature, progressive syncs must be explicitly enabled, in one
|
||||
|
||||
1. Pass `--enable-progressive-syncs` to the ApplicationSet controller args.
|
||||
1. Set `ARGOCD_APPLICATIONSET_CONTROLLER_ENABLE_PROGRESSIVE_SYNCS=true` in the ApplicationSet controller environment variables.
|
||||
1. Set `applicationsetcontroller.enable.progressive.syncs: true` in the Argo CD ConfigMap.
|
||||
1. Set `applicationsetcontroller.enable.progressive.syncs: true` in the Argo CD `argocd-cmd-params-cm` ConfigMap.
|
||||
|
||||
## Strategies
|
||||
|
||||
|
||||
@@ -12,7 +12,7 @@ All resources, including `Application` and `AppProject` specs, have to be instal
|
||||
|-----------------------------------------------------------------------|------------------------------------------------------------------------------------|-----------|--------------------------------------------------------------------------------------|
|
||||
| [`argocd-cm.yaml`](argocd-cm-yaml.md) | argocd-cm | ConfigMap | General Argo CD configuration |
|
||||
| [`argocd-repositories.yaml`](argocd-repositories-yaml.md) | my-private-repo / istio-helm-repo / private-helm-repo / private-repo | Secrets | Sample repository connection details |
|
||||
| [`argocd-repo-creds.yaml`](argocd-repo-creds.yaml) | argoproj-https-creds / argoproj-ssh-creds / github-creds / github-enterprise-creds | Secrets | Sample repository credential templates |
|
||||
| [`argocd-repo-creds.yaml`](argocd-repo-creds-yaml.md) | argoproj-https-creds / argoproj-ssh-creds / github-creds / github-enterprise-creds | Secrets | Sample repository credential templates |
|
||||
| [`argocd-cmd-params-cm.yaml`](argocd-cmd-params-cm-yaml.md) | argocd-cmd-params-cm | ConfigMap | Argo CD env variables configuration |
|
||||
| [`argocd-secret.yaml`](argocd-secret-yaml.md) | argocd-secret | Secret | User Passwords, Certificates (deprecated), Signing Key, Dex secrets, Webhook secrets |
|
||||
| [`argocd-rbac-cm.yaml`](argocd-rbac-cm-yaml.md) | argocd-rbac-cm | ConfigMap | RBAC Configuration |
|
||||
|
||||
@@ -86,3 +86,13 @@ spec:
|
||||
clusters:
|
||||
- in-cluster
|
||||
- cluster1
|
||||
|
||||
# By default, apps may sync to any cluster specified under the `destinations` field, even if they are not
|
||||
# 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")
|
||||
|
||||
26
docs/user-guide/annotations-and-labels.md
Normal file
26
docs/user-guide/annotations-and-labels.md
Normal file
@@ -0,0 +1,26 @@
|
||||
# Annotations and Labels used by Argo CD
|
||||
|
||||
## Annotations
|
||||
|
||||
| Annotation key | Target resource(es) | Possible values | Description |
|
||||
|--------------------------------------------|---------------------|---------------------------------------------------------------------------------------------------|--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
|
||||
| argocd.argoproj.io/application-set-refresh | ApplicationSet | `"true"` | Added when an ApplicationSet is requested to be refreshed by a webhook. The ApplicationSet controller will remove this annotation at the end of reconciliation. |
|
||||
| argocd.argoproj.io/compare-options | any | [see compare options docs](compare-options.md) | Configures how an app's current state is compared to its desired state. |
|
||||
| argocd.argoproj.io/hook | any | [see resource hooks docs](resource_hooks.md) | Used to configure [resource hooks](resource_hooks.md). |
|
||||
| argocd.argoproj.io/hook-delete-policy | any | [see resource hooks docs](resource_hooks.md#hook-deletion-policies) | Used to set a [resource hook's deletion policy](resource_hooks.md#hook-deletion-policies). |
|
||||
| argocd.argoproj.io/manifest-generate-paths | Application | [see scaling docs](../operator-manual/high_availability.md#webhook-and-manifest-paths-annotation) | Used to avoid unnecessary Application refreshes, especially in mono-repos. |
|
||||
| argocd.argoproj.io/refresh | Application | `normal`, `hard` | Indicates that app needs to be refreshed. Removed by application controller after app is refreshed. Value `"hard"` means manifest cache and target cluster state cache should be invalidated before refresh. |
|
||||
| argocd.argoproj.io/skip-reconcile | Application | `"true"` | Indicates to the Argo CD application controller that the Application should not be reconciled. See the [skip reconcile documentation](skip_reconcile.md) for use cases. |
|
||||
| argocd.argoproj.io/sync-options | any | [see sync options docs](sync-options.md) | Provides a variety of settings to determine how an Application's resources are synced. |
|
||||
| argocd.argoproj.io/sync-wave | any | [see sync waves docs](sync-waves.md) | |
|
||||
| argocd.argoproj.io/tracking-id | any | any | Used by Argo CD to track resources it manages. See [resource tracking docs](resource_tracking.md) for details. |
|
||||
| link.argocd.argoproj.io/{some link name} | any | An http(s) URL | Adds a link to the Argo CD UI for the resource. See [external URL docs](external-url.md) for details. |
|
||||
| pref.argocd.argoproj.io/default-pod-sort | Application | [see UI customization docs](../operator-manual/ui-customization.md) | Sets the Application's default grouping mechanism. |
|
||||
| pref.argocd.argoproj.io/default-view | Application | [see UI customization docs](../operator-manual/ui-customization.md) | Sets the Application's default view mode (e.g. "tree" or "list") |
|
||||
|
||||
## Labels
|
||||
|
||||
| Label key | Target resource(es) | Possible values | Description |
|
||||
|--------------------------------|---------------------|---------------------------------------|-----------------------------------------------------------------------------------------------------------------------------------------------------------------|
|
||||
| argocd.argoproj.io/instance | Application | any | Recommended tracking label to [avoid conflicts with other tools which use `app.kubernetes.io/instance`](../faq.md#why-is-my-app-out-of-sync-even-after-syncing. |
|
||||
| argocd.argoproj.io/secret-type | Secret | `cluster`, `repository`, `repo-creds` | Identifies certain types of Secrets used by Argo CD. See the [Declarative Setup docs](../operator-manual/declarative-setup.md) for details. |
|
||||
@@ -54,7 +54,7 @@ source:
|
||||
|
||||
Argo CD supports the equivalent of a values file directly in the Application manifest using the `source.helm.valuesObject` key.
|
||||
|
||||
```
|
||||
```yaml
|
||||
source:
|
||||
helm:
|
||||
valuesObject:
|
||||
@@ -75,7 +75,7 @@ source:
|
||||
|
||||
Alternatively, values can be passed in as a string using the `source.helm.values` key.
|
||||
|
||||
```
|
||||
```yaml
|
||||
source:
|
||||
helm:
|
||||
values: |
|
||||
@@ -254,7 +254,7 @@ One way to use this plugin is to prepare your own ArgoCD image where it is inclu
|
||||
|
||||
Example `Dockerfile`:
|
||||
|
||||
```
|
||||
```dockerfile
|
||||
FROM argoproj/argocd:v1.5.7
|
||||
|
||||
USER root
|
||||
@@ -284,7 +284,7 @@ Some users find this pattern preferable to maintaining their own version of the
|
||||
|
||||
Below is an example of how to add Helm plugins when installing ArgoCD with the [official ArgoCD helm chart](https://github.com/argoproj/argo-helm/tree/master/charts/argo-cd):
|
||||
|
||||
```
|
||||
```yaml
|
||||
repoServer:
|
||||
volumes:
|
||||
- name: gcp-credentials
|
||||
|
||||
@@ -279,7 +279,7 @@ It is possible to add and remove TLS certificates using the ArgoCD web UI:
|
||||
|
||||
### Managing TLS certificates using declarative configuration
|
||||
|
||||
You can also manage TLS certificates in a declarative, self-managed ArgoCD setup. All TLS certificates are stored in the ConfigMap object `argocd-tls-cert-cm`.
|
||||
You can also manage TLS certificates in a declarative, self-managed ArgoCD setup. All TLS certificates are stored in the ConfigMap object `argocd-tls-certs-cm`.
|
||||
Please refer to the [Operator Manual](../../operator-manual/declarative-setup/#repositories-using-self-signed-tls-certificates-or-are-signed-by-custom-ca) for more information.
|
||||
|
||||
## Unknown SSH Hosts
|
||||
|
||||
144
docs/user-guide/sync-kubectl.md
Normal file
144
docs/user-guide/sync-kubectl.md
Normal file
@@ -0,0 +1,144 @@
|
||||
# Sync Applications with Kubectl
|
||||
|
||||
You can use "kubectl" to ask Argo CD to synchronize applications the same way you can use the CLI or UI. Many configurations like "force", "prune", "apply" and even synchronize a specific list of resources are equally supported. This is done by applying or patching the Argo CD application with a document that defines an "operation".
|
||||
|
||||
This "operation" defines how a synchronization should be done and for what resources these synchronization is to be done.
|
||||
|
||||
There are many configuration options that can be added to the "operation". Next, a few of them are explained. For more details, you can have a look at the CRD [applications.argoproj.io](https://github.com/argoproj/argo-cd/blob/master/manifests/crds/application-crd.yaml). Some of them are required, whereas others are optional.
|
||||
|
||||
To ask Argo CD to synchronize all resources of a given application, we can do:
|
||||
|
||||
```yaml
|
||||
apiVersion: argoproj.io/v1alpha1
|
||||
kind: Application
|
||||
metadata:
|
||||
name: <app-name>
|
||||
namespace: <namespace>
|
||||
spec:
|
||||
...
|
||||
operation:
|
||||
initiatedBy:
|
||||
username: <username>
|
||||
sync:
|
||||
syncStrategy:
|
||||
hook: {}
|
||||
```
|
||||
|
||||
```bash
|
||||
$ kubectl apply -f <apply-file>
|
||||
```
|
||||
|
||||
The most important part is the "sync" definition in the "operation" field. You can pass optional information like "info" or "initiatedBy". "info" allows you to add information about the operation in the form of a list. "initiatedBy" contains information about who initiated the operation request.
|
||||
|
||||
Or if you prefer, you also can patch:
|
||||
|
||||
```yaml
|
||||
operation:
|
||||
initiatedBy:
|
||||
username: <username>
|
||||
sync:
|
||||
syncStrategy:
|
||||
hook: {}
|
||||
```
|
||||
|
||||
```bash
|
||||
$ kubectl patch -n <namespace> app <app-name> --patch-file <patch-file> --type merge
|
||||
```
|
||||
|
||||
Be aware that patches, specially with merge strategies, may not work the way you expect especially if you change sync strategies or options.
|
||||
In these cases, "kubectl apply" gives better results.
|
||||
|
||||
Either with a "kubectl patch" or "kubectl apply", the state of the synchronization is reported in the "operationState" field in the application object.
|
||||
|
||||
```bash
|
||||
$ kubectl get -n <namespace> get app <app-name> -o yaml
|
||||
...
|
||||
status:
|
||||
operationState:
|
||||
finishedAt: "2023-08-03T11:16:17Z"
|
||||
message: successfully synced (all tasks run)
|
||||
phase: Succeeded
|
||||
```
|
||||
|
||||
# Apply and Hook synchronization strategies
|
||||
|
||||
There are two types of synchronization strategies: "hook", which is the default value, and "apply".
|
||||
|
||||
An "apply" sync strategy tells Argo CD to "kubectl apply", whereas a "hook" sync strategy informs Argo CD to submit any resource that's referenced in the operation. This way the synchronization of these resources will take into consideration any hook the resource has been annotated with.
|
||||
|
||||
```yaml
|
||||
operation:
|
||||
sync:
|
||||
syncStrategy:
|
||||
apply: {}
|
||||
```
|
||||
|
||||
```yaml
|
||||
operation:
|
||||
sync:
|
||||
syncStrategy:
|
||||
hook: {}
|
||||
```
|
||||
|
||||
Both strategies support "force". However, you need to be aware that a force operation deletes the resource when patch encounters a conflict after having retried 5 times.
|
||||
|
||||
```yaml
|
||||
operation:
|
||||
sync:
|
||||
syncStrategy:
|
||||
apply:
|
||||
force: true
|
||||
```
|
||||
|
||||
```yaml
|
||||
operation:
|
||||
sync:
|
||||
syncStrategy:
|
||||
hook:
|
||||
force: true
|
||||
```
|
||||
|
||||
# Prune
|
||||
|
||||
If you want to prune your resources before applying, you can instruct Argo CD to do so:
|
||||
|
||||
```yaml
|
||||
operation:
|
||||
sync:
|
||||
prune: true
|
||||
```
|
||||
|
||||
# List of resources
|
||||
|
||||
There's always the possibility to pass a list of resources. This list can be all resources the application manages or only a subset, for example resources that remained out of sync for some reason.
|
||||
|
||||
Only "kind" and "name" are required fields when referencing resources, but the fields "groups" and "namespace" can also be defined:
|
||||
|
||||
```yaml
|
||||
operation:
|
||||
sync:
|
||||
resources:
|
||||
- kind: Namespace
|
||||
name: namespace-name
|
||||
- kind: ServiceAccount
|
||||
name: service-account-name
|
||||
namespace: namespace-name
|
||||
- group: networking.k8s.io
|
||||
kind: NetworkPolicy
|
||||
name: network-policy-name
|
||||
namespace: namespace-name
|
||||
```
|
||||
|
||||
# Sync Options
|
||||
|
||||
In an operation, you can also pass sync-options. Each of these options is passed as "name=value" pairs. For example:
|
||||
|
||||
```yaml
|
||||
operations:
|
||||
sync:
|
||||
syncOptions:
|
||||
- Validate=false
|
||||
- Prune=false
|
||||
```
|
||||
|
||||
For more information about sync options, please refer to [sync-options](https://argo-cd.readthedocs.io/en/stable/user-guide/sync-options/)
|
||||
4
go.mod
4
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
|
||||
@@ -115,6 +115,7 @@ require (
|
||||
github.com/pkg/browser v0.0.0-20210911075715-681adbf594b8 // indirect
|
||||
gopkg.in/retry.v1 v1.0.3 // indirect
|
||||
k8s.io/klog v1.0.0 // indirect
|
||||
nhooyr.io/websocket v1.8.7 // indirect
|
||||
)
|
||||
|
||||
require (
|
||||
@@ -261,7 +262,6 @@ require (
|
||||
k8s.io/gengo v0.0.0-20211129171323-c02415ce4185 // indirect
|
||||
k8s.io/kube-aggregator v0.24.2 // indirect
|
||||
k8s.io/kubernetes v1.24.2 // indirect
|
||||
nhooyr.io/websocket v1.8.6 // indirect
|
||||
sigs.k8s.io/json v0.0.0-20220525155127-227cbc7cc124 // indirect
|
||||
sigs.k8s.io/kustomize/api v0.11.5 // indirect
|
||||
sigs.k8s.io/kustomize/kyaml v0.13.7 // indirect
|
||||
|
||||
6
go.sum
6
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=
|
||||
@@ -1963,8 +1964,9 @@ modernc.org/golex v1.0.0/go.mod h1:b/QX9oBD/LhixY6NDh+IdGv17hgB+51fET1i2kPSmvk=
|
||||
modernc.org/mathutil v1.0.0/go.mod h1:wU0vUrJsVWBZ4P6e7xtFJEhFSNsfRLJ8H458uRjg03k=
|
||||
modernc.org/strutil v1.0.0/go.mod h1:lstksw84oURvj9y3tn8lGvRxyRC1S2+g5uuIzNfIOBs=
|
||||
modernc.org/xc v1.0.0/go.mod h1:mRNCo0bvLjGhHO9WsyuKVU4q0ceiDDDoEeWDJHrNx8I=
|
||||
nhooyr.io/websocket v1.8.6 h1:s+C3xAMLwGmlI31Nyn/eAehUlZPwfYZu2JXM621Q5/k=
|
||||
nhooyr.io/websocket v1.8.6/go.mod h1:B70DZP8IakI65RVQ51MsWP/8jndNma26DVA/nFSCgW0=
|
||||
nhooyr.io/websocket v1.8.7 h1:usjR2uOr/zjjkVMy0lW+PPohFok7PCow5sDjLgX4P4g=
|
||||
nhooyr.io/websocket v1.8.7/go.mod h1:B70DZP8IakI65RVQ51MsWP/8jndNma26DVA/nFSCgW0=
|
||||
oras.land/oras-go/v2 v2.2.0 h1:E1fqITD56Eg5neZbxBtAdZVgDHD6wBabJo6xESTcQyo=
|
||||
oras.land/oras-go/v2 v2.2.0/go.mod h1:pXjn0+KfarspMHHNR3A56j3tgvr+mxArHuI8qVn59v8=
|
||||
rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8=
|
||||
|
||||
@@ -5,7 +5,7 @@ kind: Kustomization
|
||||
images:
|
||||
- name: quay.io/argoproj/argocd
|
||||
newName: quay.io/argoproj/argocd
|
||||
newTag: v2.8.0
|
||||
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.0
|
||||
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.0
|
||||
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.0
|
||||
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.0
|
||||
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.0
|
||||
newTag: v2.8.4
|
||||
|
||||
@@ -12,7 +12,7 @@ patches:
|
||||
images:
|
||||
- name: quay.io/argoproj/argocd
|
||||
newName: quay.io/argoproj/argocd
|
||||
newTag: v2.8.0
|
||||
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.0
|
||||
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.0
|
||||
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.0
|
||||
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.0
|
||||
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.0
|
||||
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.0
|
||||
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.0
|
||||
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.0
|
||||
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.0
|
||||
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.0
|
||||
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.0
|
||||
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.0
|
||||
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.0
|
||||
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.0
|
||||
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.0
|
||||
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.0
|
||||
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.0
|
||||
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.0
|
||||
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.0
|
||||
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.0
|
||||
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.0
|
||||
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.0
|
||||
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.0
|
||||
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.0
|
||||
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.0
|
||||
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.0
|
||||
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.0
|
||||
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.0
|
||||
image: quay.io/argoproj/argocd:v2.8.4
|
||||
imagePullPolicy: Always
|
||||
name: argocd-application-controller
|
||||
ports:
|
||||
|
||||
@@ -167,6 +167,7 @@ nav:
|
||||
- user-guide/selective_sync.md
|
||||
- user-guide/sync-waves.md
|
||||
- user-guide/sync_windows.md
|
||||
- user-guide/sync-kubectl.md
|
||||
- user-guide/skip_reconcile.md
|
||||
- Generating Applications with ApplicationSet: user-guide/application-set.md
|
||||
- user-guide/ci_automation.md
|
||||
@@ -176,6 +177,7 @@ nav:
|
||||
- user-guide/external-url.md
|
||||
- user-guide/extra_info.md
|
||||
- Notification subscriptions: user-guide/subscriptions.md
|
||||
- user-guide/annotations-and-labels.md
|
||||
- Command Reference: user-guide/commands/argocd.md
|
||||
- Application Specification Reference: user-guide/application-specification.md
|
||||
- Developer Guide:
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
|
||||
@@ -2,3 +2,6 @@ actionTests:
|
||||
- action: create-workflow
|
||||
inputPath: testdata/cronworkflow.yaml
|
||||
expectedOutputPath: testdata/workflow.yaml
|
||||
- action: create-workflow
|
||||
inputPath: testdata/cronworkflow-without-label.yaml
|
||||
expectedOutputPath: testdata/workflow-without-label.yaml
|
||||
|
||||
@@ -50,7 +50,7 @@ if (obj.spec.workflowMetadata ~= nil) then
|
||||
end
|
||||
end
|
||||
workflow.metadata.labels["workflows.argoproj.io/cron-workflow"] = obj.metadata.name
|
||||
if (obj.metadata.labels["workflows.argoproj.io/controller-instanceid"] ~= nil) then
|
||||
if (obj.metadata.labels ~= nil and obj.metadata.labels["workflows.argoproj.io/controller-instanceid"] ~= nil) then
|
||||
workflow.metadata.labels["workflows.argoproj.io/controller-instanceid"] = obj.metadata.labels["workflows.argoproj.io/controller-instanceid"]
|
||||
end
|
||||
workflow.metadata.annotations["workflows.argoproj.io/scheduled-time"] = os.date("!%Y-%m-%dT%d:%H:%MZ")
|
||||
|
||||
@@ -0,0 +1,31 @@
|
||||
apiVersion: argoproj.io/v1alpha1
|
||||
kind: CronWorkflow
|
||||
metadata:
|
||||
annotations:
|
||||
cronworkflows.argoproj.io/last-used-schedule: CRON_TZ=America/Los_Angeles * * * * *
|
||||
name: hello-world
|
||||
namespace: default
|
||||
spec:
|
||||
concurrencyPolicy: Replace
|
||||
failedJobsHistoryLimit: 4
|
||||
schedule: '* * * * *'
|
||||
startingDeadlineSeconds: 0
|
||||
successfulJobsHistoryLimit: 4
|
||||
suspend: true
|
||||
timezone: America/Los_Angeles
|
||||
workflowSpec:
|
||||
entrypoint: whalesay
|
||||
templates:
|
||||
- container:
|
||||
args:
|
||||
- "\U0001F553 hello world. Scheduled on: {{workflow.scheduledTime}}"
|
||||
command:
|
||||
- cowsay
|
||||
image: 'docker/whalesay:latest'
|
||||
name: whalesay
|
||||
workflowMetadata:
|
||||
labels:
|
||||
example: test
|
||||
annotations:
|
||||
another-example: another-test
|
||||
finalizers: [test-finalizer]
|
||||
26
resource_customizations/argoproj.io/CronWorkflow/actions/testdata/workflow-without-label.yaml
vendored
Normal file
26
resource_customizations/argoproj.io/CronWorkflow/actions/testdata/workflow-without-label.yaml
vendored
Normal file
@@ -0,0 +1,26 @@
|
||||
- k8sOperation: create
|
||||
unstructuredObj:
|
||||
apiVersion: argoproj.io/v1alpha1
|
||||
kind: Workflow
|
||||
metadata:
|
||||
annotations:
|
||||
another-example: another-test
|
||||
labels:
|
||||
example: test
|
||||
name: hello-world-202306221736
|
||||
namespace: default
|
||||
ownerReferences:
|
||||
- apiVersion: argoproj.io/v1alpha1
|
||||
kind: CronWorkflow
|
||||
name: hello-world
|
||||
finalizers: [test-finalizer]
|
||||
spec:
|
||||
entrypoint: whalesay
|
||||
templates:
|
||||
- container:
|
||||
args:
|
||||
- "\U0001F553 hello world. Scheduled on: {{workflow.scheduledTime}}"
|
||||
command:
|
||||
- cowsay
|
||||
image: 'docker/whalesay:latest'
|
||||
name: whalesay
|
||||
@@ -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
|
||||
@@ -6,6 +6,7 @@ import (
|
||||
"net/http"
|
||||
"time"
|
||||
|
||||
util_session "github.com/argoproj/argo-cd/v2/util/session"
|
||||
"github.com/argoproj/gitops-engine/pkg/utils/kube"
|
||||
log "github.com/sirupsen/logrus"
|
||||
v1 "k8s.io/api/core/v1"
|
||||
@@ -37,11 +38,12 @@ type terminalHandler struct {
|
||||
allowedShells []string
|
||||
namespace string
|
||||
enabledNamespaces []string
|
||||
sessionManager util_session.SessionManager
|
||||
}
|
||||
|
||||
// NewHandler returns a new terminal handler.
|
||||
func NewHandler(appLister applisters.ApplicationLister, namespace string, enabledNamespaces []string, db db.ArgoDB, enf *rbac.Enforcer, cache *servercache.Cache,
|
||||
appResourceTree AppResourceTreeFn, allowedShells []string) *terminalHandler {
|
||||
appResourceTree AppResourceTreeFn, allowedShells []string, sessionManager util_session.SessionManager) *terminalHandler {
|
||||
return &terminalHandler{
|
||||
appLister: appLister,
|
||||
db: db,
|
||||
@@ -51,6 +53,7 @@ func NewHandler(appLister applisters.ApplicationLister, namespace string, enable
|
||||
allowedShells: allowedShells,
|
||||
namespace: namespace,
|
||||
enabledNamespaces: enabledNamespaces,
|
||||
sessionManager: sessionManager,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -222,7 +225,7 @@ func (s *terminalHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
|
||||
|
||||
fieldLog.Info("terminal session starting")
|
||||
|
||||
session, err := newTerminalSession(w, r, nil)
|
||||
session, err := newTerminalSession(w, r, nil, s.sessionManager)
|
||||
if err != nil {
|
||||
http.Error(w, "Failed to start terminal session", http.StatusBadRequest)
|
||||
return
|
||||
@@ -282,6 +285,11 @@ type TerminalMessage struct {
|
||||
Cols uint16 `json:"cols"`
|
||||
}
|
||||
|
||||
// TerminalCommand is the struct for websocket commands,For example you need ask client to reconnect
|
||||
type TerminalCommand struct {
|
||||
Code int
|
||||
}
|
||||
|
||||
// startProcess executes specified commands in the container and connects it up with the ptyHandler (a session)
|
||||
func startProcess(k8sClient kubernetes.Interface, cfg *rest.Config, namespace, podName, containerName string, cmd []string, ptyHandler PtyHandler) error {
|
||||
req := k8sClient.CoreV1().RESTClient().Post().
|
||||
|
||||
@@ -3,6 +3,9 @@ package application
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"github.com/argoproj/argo-cd/v2/common"
|
||||
httputil "github.com/argoproj/argo-cd/v2/util/http"
|
||||
util_session "github.com/argoproj/argo-cd/v2/util/session"
|
||||
"net/http"
|
||||
"sync"
|
||||
"time"
|
||||
@@ -12,6 +15,11 @@ import (
|
||||
"k8s.io/client-go/tools/remotecommand"
|
||||
)
|
||||
|
||||
const (
|
||||
ReconnectCode = 1
|
||||
ReconnectMessage = "\nReconnect because the token was refreshed...\n"
|
||||
)
|
||||
|
||||
var upgrader = func() websocket.Upgrader {
|
||||
upgrader := websocket.Upgrader{}
|
||||
upgrader.HandshakeTimeout = time.Second * 2
|
||||
@@ -23,25 +31,40 @@ var upgrader = func() websocket.Upgrader {
|
||||
|
||||
// terminalSession implements PtyHandler
|
||||
type terminalSession struct {
|
||||
wsConn *websocket.Conn
|
||||
sizeChan chan remotecommand.TerminalSize
|
||||
doneChan chan struct{}
|
||||
tty bool
|
||||
readLock sync.Mutex
|
||||
writeLock sync.Mutex
|
||||
wsConn *websocket.Conn
|
||||
sizeChan chan remotecommand.TerminalSize
|
||||
doneChan chan struct{}
|
||||
tty bool
|
||||
readLock sync.Mutex
|
||||
writeLock sync.Mutex
|
||||
sessionManager util_session.SessionManager
|
||||
token *string
|
||||
}
|
||||
|
||||
// getToken get auth token from web socket request
|
||||
func getToken(r *http.Request) (string, error) {
|
||||
cookies := r.Cookies()
|
||||
return httputil.JoinCookies(common.AuthCookieName, cookies)
|
||||
}
|
||||
|
||||
// newTerminalSession create terminalSession
|
||||
func newTerminalSession(w http.ResponseWriter, r *http.Request, responseHeader http.Header) (*terminalSession, error) {
|
||||
func newTerminalSession(w http.ResponseWriter, r *http.Request, responseHeader http.Header, sessionManager util_session.SessionManager) (*terminalSession, error) {
|
||||
token, err := getToken(r)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
conn, err := upgrader.Upgrade(w, r, responseHeader)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
session := &terminalSession{
|
||||
wsConn: conn,
|
||||
tty: true,
|
||||
sizeChan: make(chan remotecommand.TerminalSize),
|
||||
doneChan: make(chan struct{}),
|
||||
wsConn: conn,
|
||||
tty: true,
|
||||
sizeChan: make(chan remotecommand.TerminalSize),
|
||||
doneChan: make(chan struct{}),
|
||||
sessionManager: sessionManager,
|
||||
token: &token,
|
||||
}
|
||||
return session, nil
|
||||
}
|
||||
@@ -78,8 +101,40 @@ func (t *terminalSession) Next() *remotecommand.TerminalSize {
|
||||
}
|
||||
}
|
||||
|
||||
// reconnect send reconnect code to client and ask them init new ws session
|
||||
func (t *terminalSession) reconnect() (int, error) {
|
||||
reconnectCommand, _ := json.Marshal(TerminalCommand{
|
||||
Code: ReconnectCode,
|
||||
})
|
||||
reconnectMessage, _ := json.Marshal(TerminalMessage{
|
||||
Operation: "stdout",
|
||||
Data: ReconnectMessage,
|
||||
})
|
||||
t.writeLock.Lock()
|
||||
err := t.wsConn.WriteMessage(websocket.TextMessage, reconnectMessage)
|
||||
if err != nil {
|
||||
log.Errorf("write message err: %v", err)
|
||||
return 0, err
|
||||
}
|
||||
err = t.wsConn.WriteMessage(websocket.TextMessage, reconnectCommand)
|
||||
if err != nil {
|
||||
log.Errorf("write message err: %v", err)
|
||||
return 0, err
|
||||
}
|
||||
t.writeLock.Unlock()
|
||||
return 0, nil
|
||||
}
|
||||
|
||||
// Read called in a loop from remotecommand as long as the process is running
|
||||
func (t *terminalSession) Read(p []byte) (int, error) {
|
||||
// check if token still valid
|
||||
_, newToken, err := t.sessionManager.VerifyToken(*t.token)
|
||||
// err in case if token is revoked, newToken in case if refresh happened
|
||||
if err != nil || newToken != "" {
|
||||
// need to send reconnect code in case if token was refreshed
|
||||
return t.reconnect()
|
||||
}
|
||||
|
||||
t.readLock.Lock()
|
||||
_, message, err := t.wsConn.ReadMessage()
|
||||
t.readLock.Unlock()
|
||||
|
||||
46
server/application/websocket_test.go
Normal file
46
server/application/websocket_test.go
Normal file
@@ -0,0 +1,46 @@
|
||||
package application
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"github.com/gorilla/websocket"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"strings"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func reconnect(w http.ResponseWriter, r *http.Request) {
|
||||
var upgrader = websocket.Upgrader{}
|
||||
c, err := upgrader.Upgrade(w, r, nil)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
ts := terminalSession{wsConn: c}
|
||||
_, _ = ts.reconnect()
|
||||
}
|
||||
|
||||
func TestReconnect(t *testing.T) {
|
||||
|
||||
s := httptest.NewServer(http.HandlerFunc(reconnect))
|
||||
defer s.Close()
|
||||
|
||||
u := "ws" + strings.TrimPrefix(s.URL, "http")
|
||||
|
||||
// Connect to the server
|
||||
ws, _, err := websocket.DefaultDialer.Dial(u, nil)
|
||||
assert.NoError(t, err)
|
||||
|
||||
defer ws.Close()
|
||||
|
||||
_, p, _ := ws.ReadMessage()
|
||||
|
||||
var message TerminalMessage
|
||||
|
||||
err = json.Unmarshal(p, &message)
|
||||
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, message.Data, ReconnectMessage)
|
||||
|
||||
}
|
||||
@@ -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{
|
||||
@@ -977,7 +976,7 @@ func (a *ArgoCDServer) newHTTPServer(ctx context.Context, port int, grpcWebHandl
|
||||
}
|
||||
mux.Handle("/api/", handler)
|
||||
|
||||
terminal := application.NewHandler(a.appLister, a.Namespace, a.ApplicationNamespaces, a.db, a.enf, a.Cache, appResourceTreeFn, a.settings.ExecShells).
|
||||
terminal := application.NewHandler(a.appLister, a.Namespace, a.ApplicationNamespaces, a.db, a.enf, a.Cache, appResourceTreeFn, a.settings.ExecShells, *a.sessionMgr).
|
||||
WithFeatureFlagMiddleware(a.settingsMgr.GetSettings)
|
||||
th := util_session.WithAuthMiddleware(a.DisableAuth, a.sessionMgr, terminal)
|
||||
mux.Handle("/terminal", th)
|
||||
@@ -990,6 +989,7 @@ func (a *ArgoCDServer) newHTTPServer(ctx context.Context, port int, grpcWebHandl
|
||||
// will be added in mux.
|
||||
registerExtensions(mux, a)
|
||||
}
|
||||
|
||||
mustRegisterGWHandler(versionpkg.RegisterVersionServiceHandler, ctx, gwmux, conn)
|
||||
mustRegisterGWHandler(clusterpkg.RegisterClusterServiceHandler, ctx, gwmux, conn)
|
||||
mustRegisterGWHandler(applicationpkg.RegisterApplicationServiceHandler, ctx, gwmux, conn)
|
||||
|
||||
@@ -14,7 +14,7 @@ FROM docker.io/library/registry:2.8@sha256:41f413c22d6156587e2a51f3e80c09808b8c7
|
||||
|
||||
FROM docker.io/bitnami/kubectl:1.27@sha256:670fe3f50d45c0511bb0f2af018e2fc082ac8cdfaea02dba4e32866296036926 as kubectl
|
||||
|
||||
FROM docker.io/library/ubuntu:22.04@sha256:ac58ff7fe25edc58bdf0067ca99df00014dbd032e2246d30a722fa348fd799a5
|
||||
FROM docker.io/library/ubuntu:22.04@sha256:0bced47fffa3361afa981854fcabcd4577cd43cebbb808cea2b1f33a3dd7f508
|
||||
|
||||
ENV DEBIAN_FRONTEND=noninteractive
|
||||
RUN apt-get update && apt-get install --fix-missing -y \
|
||||
|
||||
@@ -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]);
|
||||
|
||||
|
||||
@@ -72,7 +72,13 @@ export const PodTerminalViewer: React.FC<PodTerminalViewerProps> = ({
|
||||
|
||||
const onConnectionMessage = (e: MessageEvent) => {
|
||||
const msg = JSON.parse(e.data);
|
||||
connSubject.next(msg);
|
||||
if (!msg?.Code) {
|
||||
connSubject.next(msg);
|
||||
} else {
|
||||
// Do reconnect due to refresh token event
|
||||
onConnectionClose();
|
||||
setupConnection();
|
||||
}
|
||||
};
|
||||
|
||||
const onConnectionOpen = () => {
|
||||
|
||||
@@ -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.`
|
||||
};
|
||||
|
||||
@@ -105,7 +105,7 @@ export class VersionPanel extends React.Component<VersionPanelProps, {copyState:
|
||||
}
|
||||
|
||||
private async onCopy(version: VersionMessage): Promise<void> {
|
||||
const stringifiedVersion = JSON.stringify(version, undefined, 4);
|
||||
const stringifiedVersion = JSON.stringify(version, undefined, 4) + '\n';
|
||||
try {
|
||||
await navigator.clipboard.writeText(stringifiedVersion);
|
||||
this.setState({copyState: 'success'});
|
||||
|
||||
@@ -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