Compare commits

...

10 Commits

Author SHA1 Message Date
github-actions[bot]
dbdfc71270 Bump version to 2.8.2 (#15232)
Signed-off-by: GitHub <noreply@github.com>
Co-authored-by: crenshaw-dev <crenshaw-dev@users.noreply.github.com>
2023-08-24 16:03:15 -04:00
gcp-cherry-pick-bot[bot]
31473491f5 fix(ui): Update default and max count for maxCookieNumber (#14979) (#15230)
* Update default and max count for maxCookieNumber

Signed-off-by: zvlb <vl.zemtsov@gmail.com>
Co-authored-by: Vladimir <31961982+zvlb@users.noreply.github.com>
2023-08-24 15:16:01 -04:00
gcp-cherry-pick-bot[bot]
016c2f25cd docs: document sourceNamespaces field (#15195) (#15212)
Signed-off-by: Michael Crenshaw <350466+crenshaw-dev@users.noreply.github.com>
Co-authored-by: Michael Crenshaw <350466+crenshaw-dev@users.noreply.github.com>
2023-08-24 11:30:55 -04:00
gcp-cherry-pick-bot[bot]
31e6e28ca4 chore: add example jq path expression (#15130) (#15209)
Signed-off-by: Michael Crenshaw <350466+crenshaw-dev@users.noreply.github.com>
Co-authored-by: Michael Crenshaw <350466+crenshaw-dev@users.noreply.github.com>
2023-08-24 11:29:53 -04:00
gcp-cherry-pick-bot[bot]
f07088281b fix: requeue ApplicationSet if there are validation errors (#14429) (#15206)
Signed-off-by: Chetan Banavikalmutt <chetanrns1997@gmail.com>
Co-authored-by: Chetan Banavikalmutt <chetanrns1997@gmail.com>
2023-08-24 11:29:05 -04:00
gcp-cherry-pick-bot[bot]
bf77b09b7b fix(appset): bitbucket server scm provider EOF on empty repo (#14411) (#15203)
* fix bitbucket server scm provider EOF on empty repo default branch check



* add unit test for bitbucketServer empty repo



* check for EOF explicitly



---------

Signed-off-by: Jedrzej Kotkowski <jedrzejk143@gmail.com>
Co-authored-by: jjsiv <96917147+jjsiv@users.noreply.github.com>
2023-08-24 11:28:21 -04:00
gcp-cherry-pick-bot[bot]
e982e0b80e fix(health): spec.executor.instances is Optional, Support a flexible number of executors (#11877) (#15200)
Support a flexible number of executors. For example with a mounted ConfigMap inside the Spark-Operator.

Signed-off-by: Philipp Dallig <philipp.dallig@gmail.com>
Co-authored-by: Philipp Dallig <philipp.dallig@gmail.com>
2023-08-24 11:27:53 -04:00
gcp-cherry-pick-bot[bot]
3a468c6862 fix(ui): switch podgroup notification to tooltip message (#14821) (#15224)
* improve pod grouping ux



fix: update log view on container select



* fix(ui): improve pod grouping ux



* fix(ui):update the pod grouping messages to tooltip



* fix(ui):update the pod grouping messages to tooltip



* fix: GroupNodes notification



* fix: GroupNodes notification



---------

Signed-off-by: ashutosh16 <11219262+ashutosh16@users.noreply.github.com>
Signed-off-by: AS <11219262+ashutosh16@users.noreply.github.com>
Co-authored-by: AS <11219262+ashutosh16@users.noreply.github.com>
2023-08-24 11:14:42 -04:00
gcp-cherry-pick-bot[bot]
383f2a288b fix: stop creating new otel interceptor to avoid memory leak (#15174) (#15178)
Signed-off-by: Alexander Matyushentsev <AMatyushentsev@gmail.com>
Co-authored-by: Alexander Matyushentsev <AMatyushentsev@gmail.com>
2023-08-22 20:09:28 -07:00
gcp-cherry-pick-bot[bot]
e006908fe9 fix(appset): Fix helm valuesObject with ApplicationSet (#14912) (#14920) (#15175)
Signed-off-by: Geoffrey Muselli <geoffrey.muselli@gmail.com>
Co-authored-by: Geoffrey MUSELLI <geoffrey.muselli@gmail.com>
2023-08-22 16:15:57 -07:00
33 changed files with 470 additions and 90 deletions

View File

@@ -1 +1 @@
2.8.1
2.8.2

View File

@@ -295,7 +295,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 +308,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

View File

@@ -2003,7 +2003,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

View File

@@ -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 {

View File

@@ -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"))

View File

@@ -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)
}

View File

@@ -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

View File

@@ -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()))

View File

@@ -214,6 +214,9 @@ spec:
kind: Deployment
jsonPointers:
- /spec/replicas
- kind: ConfigMap
jqPathExpressions:
- '.data["config.yaml"].auth'
# for the specified managedFields managers
- group: "*"
kind: "*"

View File

@@ -91,3 +91,8 @@ spec:
# scoped to this project. Set the following field to `true` to restrict apps in this cluster to only clusters
# scoped to this project.
permitOnlyProjectScopedClusters: false
# When using Applications-in-any-namespace, this field determines which namespaces this AppProject permits
# Applications to reside in. Details: https://argo-cd.readthedocs.io/en/stable/operator-manual/app-any-namespace/
sourceNamespaces:
- "argocd-apps-*"

View File

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

View File

@@ -18880,7 +18880,7 @@ spec:
key: applicationsetcontroller.allowed.scm.providers
name: argocd-cmd-params-cm
optional: true
image: quay.io/argoproj/argocd:v2.8.1
image: quay.io/argoproj/argocd:v2.8.2
imagePullPolicy: Always
name: argocd-applicationset-controller
ports:
@@ -19168,7 +19168,7 @@ spec:
value: /helm-working-dir
- name: HELM_DATA_HOME
value: /helm-working-dir
image: quay.io/argoproj/argocd:v2.8.1
image: quay.io/argoproj/argocd:v2.8.2
imagePullPolicy: Always
livenessProbe:
failureThreshold: 3
@@ -19220,7 +19220,7 @@ spec:
- -n
- /usr/local/bin/argocd
- /var/run/argocd/argocd-cmp-server
image: quay.io/argoproj/argocd:v2.8.1
image: quay.io/argoproj/argocd:v2.8.2
name: copyutil
securityContext:
allowPrivilegeEscalation: false
@@ -19439,7 +19439,7 @@ spec:
key: controller.kubectl.parallelism.limit
name: argocd-cmd-params-cm
optional: true
image: quay.io/argoproj/argocd:v2.8.1
image: quay.io/argoproj/argocd:v2.8.2
imagePullPolicy: Always
name: argocd-application-controller
ports:

View File

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

View File

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

View File

@@ -20117,7 +20117,7 @@ spec:
key: applicationsetcontroller.allowed.scm.providers
name: argocd-cmd-params-cm
optional: true
image: quay.io/argoproj/argocd:v2.8.1
image: quay.io/argoproj/argocd:v2.8.2
imagePullPolicy: Always
name: argocd-applicationset-controller
ports:
@@ -20240,7 +20240,7 @@ spec:
- -n
- /usr/local/bin/argocd
- /shared/argocd-dex
image: quay.io/argoproj/argocd:v2.8.1
image: quay.io/argoproj/argocd:v2.8.2
imagePullPolicy: Always
name: copyutil
securityContext:
@@ -20310,7 +20310,7 @@ spec:
key: notificationscontroller.log.level
name: argocd-cmd-params-cm
optional: true
image: quay.io/argoproj/argocd:v2.8.1
image: quay.io/argoproj/argocd:v2.8.2
imagePullPolicy: Always
livenessProbe:
tcpSocket:
@@ -20624,7 +20624,7 @@ spec:
value: /helm-working-dir
- name: HELM_DATA_HOME
value: /helm-working-dir
image: quay.io/argoproj/argocd:v2.8.1
image: quay.io/argoproj/argocd:v2.8.2
imagePullPolicy: Always
livenessProbe:
failureThreshold: 3
@@ -20676,7 +20676,7 @@ spec:
- -n
- /usr/local/bin/argocd
- /var/run/argocd/argocd-cmp-server
image: quay.io/argoproj/argocd:v2.8.1
image: quay.io/argoproj/argocd:v2.8.2
name: copyutil
securityContext:
allowPrivilegeEscalation: false
@@ -20965,7 +20965,7 @@ spec:
key: server.enable.proxy.extension
name: argocd-cmd-params-cm
optional: true
image: quay.io/argoproj/argocd:v2.8.1
image: quay.io/argoproj/argocd:v2.8.2
imagePullPolicy: Always
livenessProbe:
httpGet:
@@ -21211,7 +21211,7 @@ spec:
key: controller.kubectl.parallelism.limit
name: argocd-cmd-params-cm
optional: true
image: quay.io/argoproj/argocd:v2.8.1
image: quay.io/argoproj/argocd:v2.8.2
imagePullPolicy: Always
name: argocd-application-controller
ports:

View File

@@ -1635,7 +1635,7 @@ spec:
key: applicationsetcontroller.allowed.scm.providers
name: argocd-cmd-params-cm
optional: true
image: quay.io/argoproj/argocd:v2.8.1
image: quay.io/argoproj/argocd:v2.8.2
imagePullPolicy: Always
name: argocd-applicationset-controller
ports:
@@ -1758,7 +1758,7 @@ spec:
- -n
- /usr/local/bin/argocd
- /shared/argocd-dex
image: quay.io/argoproj/argocd:v2.8.1
image: quay.io/argoproj/argocd:v2.8.2
imagePullPolicy: Always
name: copyutil
securityContext:
@@ -1828,7 +1828,7 @@ spec:
key: notificationscontroller.log.level
name: argocd-cmd-params-cm
optional: true
image: quay.io/argoproj/argocd:v2.8.1
image: quay.io/argoproj/argocd:v2.8.2
imagePullPolicy: Always
livenessProbe:
tcpSocket:
@@ -2142,7 +2142,7 @@ spec:
value: /helm-working-dir
- name: HELM_DATA_HOME
value: /helm-working-dir
image: quay.io/argoproj/argocd:v2.8.1
image: quay.io/argoproj/argocd:v2.8.2
imagePullPolicy: Always
livenessProbe:
failureThreshold: 3
@@ -2194,7 +2194,7 @@ spec:
- -n
- /usr/local/bin/argocd
- /var/run/argocd/argocd-cmp-server
image: quay.io/argoproj/argocd:v2.8.1
image: quay.io/argoproj/argocd:v2.8.2
name: copyutil
securityContext:
allowPrivilegeEscalation: false
@@ -2483,7 +2483,7 @@ spec:
key: server.enable.proxy.extension
name: argocd-cmd-params-cm
optional: true
image: quay.io/argoproj/argocd:v2.8.1
image: quay.io/argoproj/argocd:v2.8.2
imagePullPolicy: Always
livenessProbe:
httpGet:
@@ -2729,7 +2729,7 @@ spec:
key: controller.kubectl.parallelism.limit
name: argocd-cmd-params-cm
optional: true
image: quay.io/argoproj/argocd:v2.8.1
image: quay.io/argoproj/argocd:v2.8.2
imagePullPolicy: Always
name: argocd-application-controller
ports:

View File

@@ -19218,7 +19218,7 @@ spec:
key: applicationsetcontroller.allowed.scm.providers
name: argocd-cmd-params-cm
optional: true
image: quay.io/argoproj/argocd:v2.8.1
image: quay.io/argoproj/argocd:v2.8.2
imagePullPolicy: Always
name: argocd-applicationset-controller
ports:
@@ -19341,7 +19341,7 @@ spec:
- -n
- /usr/local/bin/argocd
- /shared/argocd-dex
image: quay.io/argoproj/argocd:v2.8.1
image: quay.io/argoproj/argocd:v2.8.2
imagePullPolicy: Always
name: copyutil
securityContext:
@@ -19411,7 +19411,7 @@ spec:
key: notificationscontroller.log.level
name: argocd-cmd-params-cm
optional: true
image: quay.io/argoproj/argocd:v2.8.1
image: quay.io/argoproj/argocd:v2.8.2
imagePullPolicy: Always
livenessProbe:
tcpSocket:
@@ -19681,7 +19681,7 @@ spec:
value: /helm-working-dir
- name: HELM_DATA_HOME
value: /helm-working-dir
image: quay.io/argoproj/argocd:v2.8.1
image: quay.io/argoproj/argocd:v2.8.2
imagePullPolicy: Always
livenessProbe:
failureThreshold: 3
@@ -19733,7 +19733,7 @@ spec:
- -n
- /usr/local/bin/argocd
- /var/run/argocd/argocd-cmp-server
image: quay.io/argoproj/argocd:v2.8.1
image: quay.io/argoproj/argocd:v2.8.2
name: copyutil
securityContext:
allowPrivilegeEscalation: false
@@ -20020,7 +20020,7 @@ spec:
key: server.enable.proxy.extension
name: argocd-cmd-params-cm
optional: true
image: quay.io/argoproj/argocd:v2.8.1
image: quay.io/argoproj/argocd:v2.8.2
imagePullPolicy: Always
livenessProbe:
httpGet:
@@ -20266,7 +20266,7 @@ spec:
key: controller.kubectl.parallelism.limit
name: argocd-cmd-params-cm
optional: true
image: quay.io/argoproj/argocd:v2.8.1
image: quay.io/argoproj/argocd:v2.8.2
imagePullPolicy: Always
name: argocd-application-controller
ports:

View File

@@ -736,7 +736,7 @@ spec:
key: applicationsetcontroller.allowed.scm.providers
name: argocd-cmd-params-cm
optional: true
image: quay.io/argoproj/argocd:v2.8.1
image: quay.io/argoproj/argocd:v2.8.2
imagePullPolicy: Always
name: argocd-applicationset-controller
ports:
@@ -859,7 +859,7 @@ spec:
- -n
- /usr/local/bin/argocd
- /shared/argocd-dex
image: quay.io/argoproj/argocd:v2.8.1
image: quay.io/argoproj/argocd:v2.8.2
imagePullPolicy: Always
name: copyutil
securityContext:
@@ -929,7 +929,7 @@ spec:
key: notificationscontroller.log.level
name: argocd-cmd-params-cm
optional: true
image: quay.io/argoproj/argocd:v2.8.1
image: quay.io/argoproj/argocd:v2.8.2
imagePullPolicy: Always
livenessProbe:
tcpSocket:
@@ -1199,7 +1199,7 @@ spec:
value: /helm-working-dir
- name: HELM_DATA_HOME
value: /helm-working-dir
image: quay.io/argoproj/argocd:v2.8.1
image: quay.io/argoproj/argocd:v2.8.2
imagePullPolicy: Always
livenessProbe:
failureThreshold: 3
@@ -1251,7 +1251,7 @@ spec:
- -n
- /usr/local/bin/argocd
- /var/run/argocd/argocd-cmp-server
image: quay.io/argoproj/argocd:v2.8.1
image: quay.io/argoproj/argocd:v2.8.2
name: copyutil
securityContext:
allowPrivilegeEscalation: false
@@ -1538,7 +1538,7 @@ spec:
key: server.enable.proxy.extension
name: argocd-cmd-params-cm
optional: true
image: quay.io/argoproj/argocd:v2.8.1
image: quay.io/argoproj/argocd:v2.8.2
imagePullPolicy: Always
livenessProbe:
httpGet:
@@ -1784,7 +1784,7 @@ spec:
key: controller.kubectl.parallelism.limit
name: argocd-cmd-params-cm
optional: true
image: quay.io/argoproj/argocd:v2.8.1
image: quay.io/argoproj/argocd:v2.8.2
imagePullPolicy: Always
name: argocd-application-controller
ports:

View File

@@ -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"
@@ -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()

View File

@@ -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{}

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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,

View File

@@ -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"
@@ -352,6 +353,85 @@ 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"},
Labels: map[string]string{
LabelKeyAppSetInstance: "test-values-object",
},
},
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{

View File

@@ -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)}

View File

@@ -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]);

View File

@@ -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.`
};

View File

@@ -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;
}

View File

@@ -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,

33
util/grpc/trace.go Normal file
View 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
}

View File

@@ -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) {

View File

@@ -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)