mirror of
https://github.com/argoproj/argo-cd.git
synced 2026-02-20 09:38:49 +01:00
Compare commits
82 Commits
commit-ser
...
v2.12.10
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
6c2db334e9 | ||
|
|
a9d8027d4a | ||
|
|
84ace16689 | ||
|
|
4ba830f5dd | ||
|
|
b6e1080936 | ||
|
|
c26ee6921b | ||
|
|
b4f6a551dd | ||
|
|
6934ace329 | ||
|
|
041133a272 | ||
|
|
9c3b45f5da | ||
|
|
7642db8ddf | ||
|
|
60d0786c82 | ||
|
|
05c1dd7d60 | ||
|
|
b32d50da45 | ||
|
|
57cef1d140 | ||
|
|
4d70c51e64 | ||
|
|
0cae929ae1 | ||
|
|
e48878b11e | ||
|
|
cacb06a5e5 | ||
|
|
a41f868dc0 | ||
|
|
32ef2e5f1e | ||
|
|
db5876fb3b | ||
|
|
4dab5bd6a6 | ||
|
|
358930be06 | ||
|
|
68f63e7a2b | ||
|
|
85be9a4f22 | ||
|
|
53bb2e4109 | ||
|
|
6e6857ee8b | ||
|
|
5f5fb0d2de | ||
|
|
e2eb54c102 | ||
|
|
3ff57d288d | ||
|
|
a05b042876 | ||
|
|
50271e10c0 | ||
|
|
ee4f09ebd2 | ||
|
|
27d1e641b6 | ||
|
|
b76a09e070 | ||
|
|
ff3ef717e2 | ||
|
|
08fe6f5aea | ||
|
|
1568165166 | ||
|
|
8590550a22 | ||
|
|
d56ef7641c | ||
|
|
02b8336890 | ||
|
|
6b9cd828c6 | ||
|
|
cafd35cea7 | ||
|
|
343dec049a | ||
|
|
560953c37b | ||
|
|
7244c2d5e5 | ||
|
|
b068220503 | ||
|
|
c873d5c68a | ||
|
|
88f85daf52 | ||
|
|
26b2039a55 | ||
|
|
952838cdde | ||
|
|
7af4526666 | ||
|
|
b156b61e22 | ||
|
|
fd478450e6 | ||
|
|
ec30a48bce | ||
|
|
57e61b2d8a | ||
|
|
6f2ae0dd46 | ||
|
|
3e31ce9470 | ||
|
|
dee59f3002 | ||
|
|
d6c37aab88 | ||
|
|
eaa1972e69 | ||
|
|
004cabba47 | ||
|
|
d9263101fc | ||
|
|
cec8504df2 | ||
|
|
0704aa6506 | ||
|
|
511d6e371c | ||
|
|
065f8494cf | ||
|
|
beacacc43d | ||
|
|
81d454215d | ||
|
|
1237d4ea17 | ||
|
|
b211d3e038 | ||
|
|
50c32b53f0 | ||
|
|
444d332d0a | ||
|
|
573c771d2b | ||
|
|
e616dff2d1 | ||
|
|
e36e19de4e | ||
|
|
bbfa79ad9e | ||
|
|
00fc93b8b2 | ||
|
|
16c20a84b8 | ||
|
|
2a9a62eeb7 | ||
|
|
fe60670885 |
@@ -2,6 +2,7 @@ version: 2
|
||||
formats: all
|
||||
mkdocs:
|
||||
fail_on_warning: false
|
||||
configuration: mkdocs.yml
|
||||
python:
|
||||
install:
|
||||
- requirements: docs/requirements.txt
|
||||
|
||||
1
USERS.md
1
USERS.md
@@ -39,6 +39,7 @@ Currently, the following organizations are **officially** using Argo CD:
|
||||
1. [Beez Innovation Labs](https://www.beezlabs.com/)
|
||||
1. [Bedag Informatik AG](https://www.bedag.ch/)
|
||||
1. [Beleza Na Web](https://www.belezanaweb.com.br/)
|
||||
1. [Believable Bots](https://believablebots.io)
|
||||
1. [BigPanda](https://bigpanda.io)
|
||||
1. [BioBox Analytics](https://biobox.io)
|
||||
1. [BMW Group](https://www.bmwgroup.com/)
|
||||
|
||||
@@ -18,6 +18,7 @@ import (
|
||||
"context"
|
||||
"fmt"
|
||||
"reflect"
|
||||
"sort"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
@@ -31,11 +32,9 @@ import (
|
||||
"k8s.io/apimachinery/pkg/types"
|
||||
"k8s.io/apimachinery/pkg/util/intstr"
|
||||
"k8s.io/client-go/kubernetes"
|
||||
k8scache "k8s.io/client-go/tools/cache"
|
||||
"k8s.io/client-go/tools/record"
|
||||
ctrl "sigs.k8s.io/controller-runtime"
|
||||
"sigs.k8s.io/controller-runtime/pkg/builder"
|
||||
"sigs.k8s.io/controller-runtime/pkg/cache"
|
||||
"sigs.k8s.io/controller-runtime/pkg/client"
|
||||
"sigs.k8s.io/controller-runtime/pkg/controller"
|
||||
"sigs.k8s.io/controller-runtime/pkg/controller/controllerutil"
|
||||
@@ -88,7 +87,6 @@ type ApplicationSetReconciler struct {
|
||||
SCMRootCAPath string
|
||||
GlobalPreservedAnnotations []string
|
||||
GlobalPreservedLabels []string
|
||||
Cache cache.Cache
|
||||
}
|
||||
|
||||
// +kubebuilder:rbac:groups=argoproj.io,resources=applicationsets,verbs=get;list;watch;create;update;patch;delete
|
||||
@@ -126,23 +124,26 @@ func (r *ApplicationSetReconciler) Reconcile(ctx context.Context, req ctrl.Reque
|
||||
return ctrl.Result{}, nil
|
||||
}
|
||||
|
||||
if err := r.migrateStatus(ctx, &applicationSetInfo); err != nil {
|
||||
logCtx.Errorf("failed to migrate status subresource %v", err)
|
||||
return ctrl.Result{}, err
|
||||
}
|
||||
|
||||
// Log a warning if there are unrecognized generators
|
||||
_ = utils.CheckInvalidGenerators(&applicationSetInfo)
|
||||
// desiredApplications is the main list of all expected Applications from all generators in this appset.
|
||||
desiredApplications, applicationSetReason, generatorsErr := r.generateApplications(logCtx, applicationSetInfo)
|
||||
if generatorsErr != nil {
|
||||
desiredApplications, applicationSetReason, err := r.generateApplications(logCtx, applicationSetInfo)
|
||||
if err != nil {
|
||||
_ = r.setApplicationSetStatusCondition(ctx,
|
||||
&applicationSetInfo,
|
||||
argov1alpha1.ApplicationSetCondition{
|
||||
Type: argov1alpha1.ApplicationSetConditionErrorOccurred,
|
||||
Message: generatorsErr.Error(),
|
||||
Message: err.Error(),
|
||||
Reason: string(applicationSetReason),
|
||||
Status: argov1alpha1.ApplicationSetConditionStatusTrue,
|
||||
}, parametersGenerated,
|
||||
)
|
||||
if len(desiredApplications) < 1 {
|
||||
return ctrl.Result{}, generatorsErr
|
||||
}
|
||||
return ctrl.Result{RequeueAfter: ReconcileRequeueOnValidationError}, err
|
||||
}
|
||||
|
||||
parametersGenerated = true
|
||||
@@ -320,7 +321,7 @@ func (r *ApplicationSetReconciler) Reconcile(ctx context.Context, req ctrl.Reque
|
||||
|
||||
requeueAfter := r.getMinRequeueAfter(&applicationSetInfo)
|
||||
|
||||
if len(validateErrors) == 0 && generatorsErr == nil {
|
||||
if len(validateErrors) == 0 {
|
||||
if err := r.setApplicationSetStatusCondition(ctx,
|
||||
&applicationSetInfo,
|
||||
argov1alpha1.ApplicationSetCondition{
|
||||
@@ -578,11 +579,9 @@ func (r *ApplicationSetReconciler) applyTemplatePatch(app *argov1alpha1.Applicat
|
||||
}
|
||||
|
||||
func ignoreNotAllowedNamespaces(namespaces []string) predicate.Predicate {
|
||||
return predicate.Funcs{
|
||||
CreateFunc: func(e event.CreateEvent) bool {
|
||||
return glob.MatchStringInList(namespaces, e.Object.GetNamespace(), false)
|
||||
},
|
||||
}
|
||||
return predicate.NewPredicateFuncs(func(object client.Object) bool {
|
||||
return glob.MatchStringInList(namespaces, object.GetNamespace(), glob.REGEXP)
|
||||
})
|
||||
}
|
||||
|
||||
func appControllerIndexer(rawObj client.Object) []string {
|
||||
@@ -623,25 +622,6 @@ func (r *ApplicationSetReconciler) SetupWithManager(mgr ctrl.Manager, enableProg
|
||||
Complete(r)
|
||||
}
|
||||
|
||||
func (r *ApplicationSetReconciler) updateCache(ctx context.Context, obj client.Object, logger *log.Entry) {
|
||||
informer, err := r.Cache.GetInformer(ctx, obj)
|
||||
if err != nil {
|
||||
logger.Errorf("failed to get informer: %v", err)
|
||||
return
|
||||
}
|
||||
// The controller runtime abstract away informers creation
|
||||
// so unfortunately could not find any other way to access informer store.
|
||||
k8sInformer, ok := informer.(k8scache.SharedInformer)
|
||||
if !ok {
|
||||
logger.Error("informer is not a kubernetes informer")
|
||||
return
|
||||
}
|
||||
if err := k8sInformer.GetStore().Update(obj); err != nil {
|
||||
logger.Errorf("failed to update cache: %v", err)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
// createOrUpdateInCluster will create / update application resources in the cluster.
|
||||
// - For new applications, it will call create
|
||||
// - For existing application, it will call update
|
||||
@@ -743,7 +723,6 @@ func (r *ApplicationSetReconciler) createOrUpdateInCluster(ctx context.Context,
|
||||
}
|
||||
continue
|
||||
}
|
||||
r.updateCache(ctx, found, appLog)
|
||||
|
||||
if action != controllerutil.OperationResultNone {
|
||||
// Don't pollute etcd with "unchanged Application" events
|
||||
@@ -910,7 +889,6 @@ func (r *ApplicationSetReconciler) removeFinalizerOnInvalidDestination(ctx conte
|
||||
if err := r.Client.Patch(ctx, updated, patch); err != nil {
|
||||
return fmt.Errorf("error updating finalizers: %w", err)
|
||||
}
|
||||
r.updateCache(ctx, updated, appLog)
|
||||
// Application must have updated list of finalizers
|
||||
updated.DeepCopyInto(app)
|
||||
|
||||
@@ -1153,6 +1131,13 @@ func (r *ApplicationSetReconciler) updateApplicationSetApplicationStatus(ctx con
|
||||
} else {
|
||||
// we have an existing AppStatus
|
||||
currentAppStatus = applicationSet.Status.ApplicationStatus[idx]
|
||||
|
||||
// upgrade any existing AppStatus that might have been set by an older argo-cd version
|
||||
// note: currentAppStatus.TargetRevisions may be set to empty list earlier during migrations,
|
||||
// to prevent other usage of r.Client.Status().Update to fail before reaching here.
|
||||
if currentAppStatus.TargetRevisions == nil || len(currentAppStatus.TargetRevisions) == 0 {
|
||||
currentAppStatus.TargetRevisions = app.Status.GetRevisions()
|
||||
}
|
||||
}
|
||||
|
||||
appOutdated := false
|
||||
@@ -1350,6 +1335,27 @@ func findApplicationStatusIndex(appStatuses []argov1alpha1.ApplicationSetApplica
|
||||
return -1
|
||||
}
|
||||
|
||||
// migrateStatus run migrations on the status subresource of ApplicationSet early during the run of ApplicationSetReconciler.Reconcile
|
||||
// this handles any defaulting of values - which would otherwise cause the references to r.Client.Status().Update to fail given missing required fields.
|
||||
func (r *ApplicationSetReconciler) migrateStatus(ctx context.Context, appset *argov1alpha1.ApplicationSet) error {
|
||||
update := false
|
||||
if statusList := appset.Status.ApplicationStatus; statusList != nil {
|
||||
for idx := range statusList {
|
||||
if statusList[idx].TargetRevisions == nil {
|
||||
statusList[idx].TargetRevisions = []string{}
|
||||
update = true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if update {
|
||||
if err := r.Client.Status().Update(ctx, appset); err != nil {
|
||||
return fmt.Errorf("unable to set application set status: %w", err)
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (r *ApplicationSetReconciler) updateResourcesStatus(ctx context.Context, logCtx *log.Entry, appset *argov1alpha1.ApplicationSet, apps []argov1alpha1.Application) error {
|
||||
statusMap := getResourceStatusMap(appset)
|
||||
statusMap = buildResourceStatus(statusMap, apps)
|
||||
@@ -1358,6 +1364,9 @@ func (r *ApplicationSetReconciler) updateResourcesStatus(ctx context.Context, lo
|
||||
for _, status := range statusMap {
|
||||
statuses = append(statuses, status)
|
||||
}
|
||||
sort.Slice(statuses, func(i, j int) bool {
|
||||
return statuses[i].Name < statuses[j].Name
|
||||
})
|
||||
appset.Status.Resources = statuses
|
||||
|
||||
namespacedName := types.NamespacedName{Namespace: appset.Namespace, Name: appset.Name}
|
||||
|
||||
@@ -5,10 +5,13 @@ import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"reflect"
|
||||
"strconv"
|
||||
"strings"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/argoproj/argo-cd/v2/applicationset/generators/mocks"
|
||||
|
||||
log "github.com/sirupsen/logrus"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/mock"
|
||||
@@ -20,11 +23,8 @@ import (
|
||||
"k8s.io/apimachinery/pkg/types"
|
||||
"k8s.io/apimachinery/pkg/util/intstr"
|
||||
kubefake "k8s.io/client-go/kubernetes/fake"
|
||||
k8scache "k8s.io/client-go/tools/cache"
|
||||
"k8s.io/client-go/tools/record"
|
||||
ctrl "sigs.k8s.io/controller-runtime"
|
||||
"sigs.k8s.io/controller-runtime/pkg/cache"
|
||||
crtcache "sigs.k8s.io/controller-runtime/pkg/cache"
|
||||
"sigs.k8s.io/controller-runtime/pkg/client"
|
||||
crtclient "sigs.k8s.io/controller-runtime/pkg/client"
|
||||
"sigs.k8s.io/controller-runtime/pkg/client/fake"
|
||||
@@ -45,34 +45,6 @@ import (
|
||||
"github.com/argoproj/argo-cd/v2/pkg/apis/application"
|
||||
)
|
||||
|
||||
type fakeStore struct {
|
||||
k8scache.Store
|
||||
}
|
||||
|
||||
func (f *fakeStore) Update(obj interface{}) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
type fakeInformer struct {
|
||||
k8scache.SharedInformer
|
||||
}
|
||||
|
||||
func (f *fakeInformer) AddIndexers(indexers k8scache.Indexers) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (f *fakeInformer) GetStore() k8scache.Store {
|
||||
return &fakeStore{}
|
||||
}
|
||||
|
||||
type fakeCache struct {
|
||||
cache.Cache
|
||||
}
|
||||
|
||||
func (f *fakeCache) GetInformer(ctx context.Context, obj crtclient.Object, opt ...crtcache.InformerGetOption) (cache.Informer, error) {
|
||||
return &fakeInformer{}, nil
|
||||
}
|
||||
|
||||
type generatorMock struct {
|
||||
mock.Mock
|
||||
}
|
||||
@@ -224,7 +196,6 @@ func TestExtractApplications(t *testing.T) {
|
||||
},
|
||||
Renderer: &rendererMock,
|
||||
KubeClientset: kubefake.NewSimpleClientset(),
|
||||
Cache: &fakeCache{},
|
||||
}
|
||||
|
||||
got, reason, err := r.generateApplications(log.NewEntry(log.StandardLogger()), v1alpha1.ApplicationSet{
|
||||
@@ -1361,7 +1332,6 @@ func TestCreateOrUpdateInCluster(t *testing.T) {
|
||||
Client: client,
|
||||
Scheme: scheme,
|
||||
Recorder: record.NewFakeRecorder(len(initObjs) + len(c.expected)),
|
||||
Cache: &fakeCache{},
|
||||
}
|
||||
|
||||
err = r.createOrUpdateInCluster(context.TODO(), log.NewEntry(log.StandardLogger()), c.appSet, c.desiredApps)
|
||||
@@ -1472,7 +1442,6 @@ func TestRemoveFinalizerOnInvalidDestination_FinalizerTypes(t *testing.T) {
|
||||
Scheme: scheme,
|
||||
Recorder: record.NewFakeRecorder(10),
|
||||
KubeClientset: kubeclientset,
|
||||
Cache: &fakeCache{},
|
||||
}
|
||||
// settingsMgr := settings.NewSettingsManager(context.TODO(), kubeclientset, "namespace")
|
||||
// argoDB := db.NewDB("namespace", settingsMgr, r.KubeClientset)
|
||||
@@ -1630,7 +1599,6 @@ func TestRemoveFinalizerOnInvalidDestination_DestinationTypes(t *testing.T) {
|
||||
Scheme: scheme,
|
||||
Recorder: record.NewFakeRecorder(10),
|
||||
KubeClientset: kubeclientset,
|
||||
Cache: &fakeCache{},
|
||||
}
|
||||
// settingsMgr := settings.NewSettingsManager(context.TODO(), kubeclientset, "argocd")
|
||||
// argoDB := db.NewDB("argocd", settingsMgr, r.KubeClientset)
|
||||
@@ -1718,7 +1686,6 @@ func TestRemoveOwnerReferencesOnDeleteAppSet(t *testing.T) {
|
||||
Scheme: scheme,
|
||||
Recorder: record.NewFakeRecorder(10),
|
||||
KubeClientset: nil,
|
||||
Cache: &fakeCache{},
|
||||
}
|
||||
|
||||
err = r.removeOwnerReferencesOnDeleteAppSet(context.Background(), appSet)
|
||||
@@ -1915,7 +1882,6 @@ func TestCreateApplications(t *testing.T) {
|
||||
Client: client,
|
||||
Scheme: scheme,
|
||||
Recorder: record.NewFakeRecorder(len(initObjs) + len(c.expected)),
|
||||
Cache: &fakeCache{},
|
||||
}
|
||||
|
||||
err = r.createInCluster(context.TODO(), log.NewEntry(log.StandardLogger()), c.appSet, c.apps)
|
||||
@@ -2122,7 +2088,6 @@ func TestGetMinRequeueAfter(t *testing.T) {
|
||||
Client: client,
|
||||
Scheme: scheme,
|
||||
Recorder: record.NewFakeRecorder(0),
|
||||
Cache: &fakeCache{},
|
||||
Generators: map[string]generators.Generator{
|
||||
"List": &generatorMock10,
|
||||
"Git": &generatorMock1,
|
||||
@@ -2139,6 +2104,57 @@ func TestGetMinRequeueAfter(t *testing.T) {
|
||||
assert.Equal(t, time.Duration(1)*time.Second, got)
|
||||
}
|
||||
|
||||
func TestRequeueGeneratorFails(t *testing.T) {
|
||||
scheme := runtime.NewScheme()
|
||||
err := v1alpha1.AddToScheme(scheme)
|
||||
require.NoError(t, err)
|
||||
err = v1alpha1.AddToScheme(scheme)
|
||||
require.NoError(t, err)
|
||||
|
||||
appSet := v1alpha1.ApplicationSet{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "name",
|
||||
Namespace: "argocd",
|
||||
},
|
||||
Spec: v1alpha1.ApplicationSetSpec{
|
||||
Generators: []v1alpha1.ApplicationSetGenerator{{
|
||||
PullRequest: &v1alpha1.PullRequestGenerator{},
|
||||
}},
|
||||
},
|
||||
}
|
||||
client := fake.NewClientBuilder().WithScheme(scheme).WithObjects(&appSet).Build()
|
||||
|
||||
generator := v1alpha1.ApplicationSetGenerator{
|
||||
PullRequest: &v1alpha1.PullRequestGenerator{},
|
||||
}
|
||||
|
||||
generatorMock := mocks.Generator{}
|
||||
generatorMock.On("GetTemplate", &generator).
|
||||
Return(&v1alpha1.ApplicationSetTemplate{})
|
||||
generatorMock.On("GenerateParams", &generator, mock.AnythingOfType("*v1alpha1.ApplicationSet"), mock.Anything).
|
||||
Return([]map[string]interface{}{}, fmt.Errorf("Simulated error generating params that could be related to an external service/API call"))
|
||||
|
||||
r := ApplicationSetReconciler{
|
||||
Client: client,
|
||||
Scheme: scheme,
|
||||
Recorder: record.NewFakeRecorder(0),
|
||||
Generators: map[string]generators.Generator{
|
||||
"PullRequest": &generatorMock,
|
||||
},
|
||||
}
|
||||
|
||||
req := ctrl.Request{
|
||||
NamespacedName: types.NamespacedName{
|
||||
Namespace: "argocd",
|
||||
Name: "name",
|
||||
},
|
||||
}
|
||||
|
||||
res, err := r.Reconcile(context.Background(), req)
|
||||
require.Error(t, err)
|
||||
assert.Equal(t, ReconcileRequeueOnValidationError, res.RequeueAfter)
|
||||
}
|
||||
|
||||
func TestValidateGeneratedApplications(t *testing.T) {
|
||||
scheme := runtime.NewScheme()
|
||||
err := v1alpha1.AddToScheme(scheme)
|
||||
@@ -2333,7 +2349,6 @@ func TestValidateGeneratedApplications(t *testing.T) {
|
||||
Client: client,
|
||||
Scheme: scheme,
|
||||
Recorder: record.NewFakeRecorder(1),
|
||||
Cache: &fakeCache{},
|
||||
Generators: map[string]generators.Generator{},
|
||||
ArgoDB: &argoDBMock,
|
||||
ArgoCDNamespace: "namespace",
|
||||
@@ -2436,7 +2451,6 @@ func TestReconcilerValidationProjectErrorBehaviour(t *testing.T) {
|
||||
Scheme: scheme,
|
||||
Renderer: &utils.Render{},
|
||||
Recorder: record.NewFakeRecorder(1),
|
||||
Cache: &fakeCache{},
|
||||
Generators: map[string]generators.Generator{
|
||||
"List": generators.NewListGenerator(),
|
||||
},
|
||||
@@ -2471,90 +2485,6 @@ func TestReconcilerValidationProjectErrorBehaviour(t *testing.T) {
|
||||
require.Error(t, err)
|
||||
}
|
||||
|
||||
func TestReconcilerCreateAppsRecoveringRenderError(t *testing.T) {
|
||||
scheme := runtime.NewScheme()
|
||||
err := v1alpha1.AddToScheme(scheme)
|
||||
require.NoError(t, err)
|
||||
err = v1alpha1.AddToScheme(scheme)
|
||||
require.NoError(t, err)
|
||||
|
||||
project := v1alpha1.AppProject{
|
||||
ObjectMeta: metav1.ObjectMeta{Name: "default", Namespace: "argocd"},
|
||||
}
|
||||
appSet := v1alpha1.ApplicationSet{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "name",
|
||||
Namespace: "argocd",
|
||||
},
|
||||
Spec: v1alpha1.ApplicationSetSpec{
|
||||
GoTemplate: true,
|
||||
Generators: []v1alpha1.ApplicationSetGenerator{
|
||||
{
|
||||
List: &v1alpha1.ListGenerator{
|
||||
Elements: []apiextensionsv1.JSON{{
|
||||
Raw: []byte(`{"name": "very-good-app"}`),
|
||||
}, {
|
||||
Raw: []byte(`{"name": "bad-app"}`),
|
||||
}},
|
||||
},
|
||||
},
|
||||
},
|
||||
Template: v1alpha1.ApplicationSetTemplate{
|
||||
ApplicationSetTemplateMeta: v1alpha1.ApplicationSetTemplateMeta{
|
||||
Name: "{{ index (splitList \"-\" .name ) 2 }}",
|
||||
Namespace: "argocd",
|
||||
},
|
||||
Spec: v1alpha1.ApplicationSpec{
|
||||
Source: &v1alpha1.ApplicationSource{RepoURL: "https://github.com/argoproj/argocd-example-apps", Path: "guestbook"},
|
||||
Project: "default",
|
||||
Destination: v1alpha1.ApplicationDestination{Server: "https://kubernetes.default.svc"},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
kubeclientset := kubefake.NewSimpleClientset()
|
||||
argoDBMock := dbmocks.ArgoDB{}
|
||||
argoObjs := []runtime.Object{&project}
|
||||
|
||||
client := fake.NewClientBuilder().WithScheme(scheme).WithObjects(&appSet).WithStatusSubresource(&appSet).WithIndex(&v1alpha1.Application{}, ".metadata.controller", appControllerIndexer).Build()
|
||||
|
||||
r := ApplicationSetReconciler{
|
||||
Client: client,
|
||||
Scheme: scheme,
|
||||
Renderer: &utils.Render{},
|
||||
Recorder: record.NewFakeRecorder(1),
|
||||
Cache: &fakeCache{},
|
||||
Generators: map[string]generators.Generator{
|
||||
"List": generators.NewListGenerator(),
|
||||
},
|
||||
ArgoDB: &argoDBMock,
|
||||
ArgoAppClientset: appclientset.NewSimpleClientset(argoObjs...),
|
||||
KubeClientset: kubeclientset,
|
||||
Policy: v1alpha1.ApplicationsSyncPolicySync,
|
||||
ArgoCDNamespace: "argocd",
|
||||
}
|
||||
|
||||
req := ctrl.Request{
|
||||
NamespacedName: types.NamespacedName{
|
||||
Namespace: "argocd",
|
||||
Name: "name",
|
||||
},
|
||||
}
|
||||
|
||||
// Verify that on generatorsError, no error is returned, but the object is requeued
|
||||
res, err := r.Reconcile(context.Background(), req)
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, ReconcileRequeueOnValidationError, res.RequeueAfter)
|
||||
|
||||
var app v1alpha1.Application
|
||||
|
||||
// make sure good app got created
|
||||
err = r.Client.Get(context.TODO(), crtclient.ObjectKey{Namespace: "argocd", Name: "app"}, &app)
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, "app", app.Name)
|
||||
}
|
||||
|
||||
func TestSetApplicationSetStatusCondition(t *testing.T) {
|
||||
scheme := runtime.NewScheme()
|
||||
err := v1alpha1.AddToScheme(scheme)
|
||||
@@ -2597,7 +2527,6 @@ func TestSetApplicationSetStatusCondition(t *testing.T) {
|
||||
Scheme: scheme,
|
||||
Renderer: &utils.Render{},
|
||||
Recorder: record.NewFakeRecorder(1),
|
||||
Cache: &fakeCache{},
|
||||
Generators: map[string]generators.Generator{
|
||||
"List": generators.NewListGenerator(),
|
||||
},
|
||||
@@ -2671,7 +2600,6 @@ func applicationsUpdateSyncPolicyTest(t *testing.T, applicationsSyncPolicy v1alp
|
||||
Scheme: scheme,
|
||||
Renderer: &utils.Render{},
|
||||
Recorder: record.NewFakeRecorder(recordBuffer),
|
||||
Cache: &fakeCache{},
|
||||
Generators: map[string]generators.Generator{
|
||||
"List": generators.NewListGenerator(),
|
||||
},
|
||||
@@ -2835,7 +2763,6 @@ func applicationsDeleteSyncPolicyTest(t *testing.T, applicationsSyncPolicy v1alp
|
||||
Scheme: scheme,
|
||||
Renderer: &utils.Render{},
|
||||
Recorder: record.NewFakeRecorder(recordBuffer),
|
||||
Cache: &fakeCache{},
|
||||
Generators: map[string]generators.Generator{
|
||||
"List": generators.NewListGenerator(),
|
||||
},
|
||||
@@ -3021,7 +2948,6 @@ func TestGenerateAppsUsingPullRequestGenerator(t *testing.T) {
|
||||
Client: client,
|
||||
Scheme: scheme,
|
||||
Recorder: record.NewFakeRecorder(1),
|
||||
Cache: &fakeCache{},
|
||||
Generators: map[string]generators.Generator{
|
||||
"PullRequest": &generatorMock,
|
||||
},
|
||||
@@ -3146,7 +3072,6 @@ func TestPolicies(t *testing.T) {
|
||||
Scheme: scheme,
|
||||
Renderer: &utils.Render{},
|
||||
Recorder: record.NewFakeRecorder(10),
|
||||
Cache: &fakeCache{},
|
||||
Generators: map[string]generators.Generator{
|
||||
"List": generators.NewListGenerator(),
|
||||
},
|
||||
@@ -3307,7 +3232,6 @@ func TestSetApplicationSetApplicationStatus(t *testing.T) {
|
||||
Scheme: scheme,
|
||||
Renderer: &utils.Render{},
|
||||
Recorder: record.NewFakeRecorder(1),
|
||||
Cache: &fakeCache{},
|
||||
Generators: map[string]generators.Generator{
|
||||
"List": generators.NewListGenerator(),
|
||||
},
|
||||
@@ -4069,7 +3993,6 @@ func TestBuildAppDependencyList(t *testing.T) {
|
||||
Client: client,
|
||||
Scheme: scheme,
|
||||
Recorder: record.NewFakeRecorder(1),
|
||||
Cache: &fakeCache{},
|
||||
Generators: map[string]generators.Generator{},
|
||||
ArgoDB: &argoDBMock,
|
||||
ArgoAppClientset: appclientset.NewSimpleClientset(argoObjs...),
|
||||
@@ -4660,7 +4583,6 @@ func TestBuildAppSyncMap(t *testing.T) {
|
||||
Client: client,
|
||||
Scheme: scheme,
|
||||
Recorder: record.NewFakeRecorder(1),
|
||||
Cache: &fakeCache{},
|
||||
Generators: map[string]generators.Generator{},
|
||||
ArgoDB: &argoDBMock,
|
||||
ArgoAppClientset: appclientset.NewSimpleClientset(argoObjs...),
|
||||
@@ -4791,6 +4713,58 @@ func TestUpdateApplicationSetApplicationStatus(t *testing.T) {
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "handles an outdated list of statuses with a healthy application, setting required variables",
|
||||
appSet: v1alpha1.ApplicationSet{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "name",
|
||||
Namespace: "argocd",
|
||||
},
|
||||
Spec: v1alpha1.ApplicationSetSpec{
|
||||
Strategy: &v1alpha1.ApplicationSetStrategy{
|
||||
Type: "RollingSync",
|
||||
RollingSync: &v1alpha1.ApplicationSetRolloutStrategy{},
|
||||
},
|
||||
},
|
||||
Status: v1alpha1.ApplicationSetStatus{
|
||||
ApplicationStatus: []v1alpha1.ApplicationSetApplicationStatus{
|
||||
{
|
||||
Application: "app1",
|
||||
Message: "Application resource is already Healthy, updating status from Waiting to Healthy.",
|
||||
Status: "Healthy",
|
||||
Step: "1",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
apps: []v1alpha1.Application{
|
||||
{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "app1",
|
||||
},
|
||||
Status: v1alpha1.ApplicationStatus{
|
||||
Health: v1alpha1.HealthStatus{
|
||||
Status: health.HealthStatusHealthy,
|
||||
},
|
||||
OperationState: &v1alpha1.OperationState{
|
||||
Phase: common.OperationSucceeded,
|
||||
},
|
||||
Sync: v1alpha1.SyncStatus{
|
||||
Status: v1alpha1.SyncStatusCodeSynced,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
expectedAppStatus: []v1alpha1.ApplicationSetApplicationStatus{
|
||||
{
|
||||
Application: "app1",
|
||||
Message: "Application resource is already Healthy, updating status from Waiting to Healthy.",
|
||||
Status: "Healthy",
|
||||
Step: "1",
|
||||
TargetRevisions: []string{},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "progresses an OutOfSync RollingSync application to waiting",
|
||||
appSet: v1alpha1.ApplicationSet{
|
||||
@@ -4880,10 +4854,11 @@ func TestUpdateApplicationSetApplicationStatus(t *testing.T) {
|
||||
Status: v1alpha1.ApplicationSetStatus{
|
||||
ApplicationStatus: []v1alpha1.ApplicationSetApplicationStatus{
|
||||
{
|
||||
Application: "app1",
|
||||
Message: "",
|
||||
Status: "Pending",
|
||||
Step: "1",
|
||||
Application: "app1",
|
||||
Message: "",
|
||||
Status: "Pending",
|
||||
Step: "1",
|
||||
TargetRevisions: []string{"Next"},
|
||||
},
|
||||
},
|
||||
},
|
||||
@@ -4902,15 +4877,16 @@ func TestUpdateApplicationSetApplicationStatus(t *testing.T) {
|
||||
},
|
||||
expectedAppStatus: []v1alpha1.ApplicationSetApplicationStatus{
|
||||
{
|
||||
Application: "app1",
|
||||
Message: "Application resource became Progressing, updating status from Pending to Progressing.",
|
||||
Status: "Progressing",
|
||||
Step: "1",
|
||||
Application: "app1",
|
||||
Message: "Application resource became Progressing, updating status from Pending to Progressing.",
|
||||
Status: "Progressing",
|
||||
Step: "1",
|
||||
TargetRevisions: []string{"Next"},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "progresses a pending syncing application to progressing",
|
||||
name: "progresses a pending synced application to progressing",
|
||||
appSet: v1alpha1.ApplicationSet{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "name",
|
||||
@@ -4925,10 +4901,11 @@ func TestUpdateApplicationSetApplicationStatus(t *testing.T) {
|
||||
Status: v1alpha1.ApplicationSetStatus{
|
||||
ApplicationStatus: []v1alpha1.ApplicationSetApplicationStatus{
|
||||
{
|
||||
Application: "app1",
|
||||
Message: "",
|
||||
Status: "Pending",
|
||||
Step: "1",
|
||||
Application: "app1",
|
||||
Message: "",
|
||||
Status: "Pending",
|
||||
Step: "1",
|
||||
TargetRevisions: []string{"Current"},
|
||||
},
|
||||
},
|
||||
},
|
||||
@@ -4953,10 +4930,11 @@ func TestUpdateApplicationSetApplicationStatus(t *testing.T) {
|
||||
},
|
||||
expectedAppStatus: []v1alpha1.ApplicationSetApplicationStatus{
|
||||
{
|
||||
Application: "app1",
|
||||
Message: "Application resource became Progressing, updating status from Pending to Progressing.",
|
||||
Status: "Progressing",
|
||||
Step: "1",
|
||||
Application: "app1",
|
||||
Message: "Application resource became Progressing, updating status from Pending to Progressing.",
|
||||
Status: "Progressing",
|
||||
Step: "1",
|
||||
TargetRevisions: []string{"Current"},
|
||||
},
|
||||
},
|
||||
},
|
||||
@@ -4976,10 +4954,11 @@ func TestUpdateApplicationSetApplicationStatus(t *testing.T) {
|
||||
Status: v1alpha1.ApplicationSetStatus{
|
||||
ApplicationStatus: []v1alpha1.ApplicationSetApplicationStatus{
|
||||
{
|
||||
Application: "app1",
|
||||
Message: "",
|
||||
Status: "Progressing",
|
||||
Step: "1",
|
||||
Application: "app1",
|
||||
Message: "",
|
||||
Status: "Progressing",
|
||||
Step: "1",
|
||||
TargetRevisions: []string{"Next"},
|
||||
},
|
||||
},
|
||||
},
|
||||
@@ -5004,10 +4983,11 @@ func TestUpdateApplicationSetApplicationStatus(t *testing.T) {
|
||||
},
|
||||
expectedAppStatus: []v1alpha1.ApplicationSetApplicationStatus{
|
||||
{
|
||||
Application: "app1",
|
||||
Message: "Application resource became Healthy, updating status from Progressing to Healthy.",
|
||||
Status: "Healthy",
|
||||
Step: "1",
|
||||
Application: "app1",
|
||||
Message: "Application resource became Healthy, updating status from Progressing to Healthy.",
|
||||
Status: "Healthy",
|
||||
Step: "1",
|
||||
TargetRevisions: []string{"Next"},
|
||||
},
|
||||
},
|
||||
},
|
||||
@@ -5027,10 +5007,11 @@ func TestUpdateApplicationSetApplicationStatus(t *testing.T) {
|
||||
Status: v1alpha1.ApplicationSetStatus{
|
||||
ApplicationStatus: []v1alpha1.ApplicationSetApplicationStatus{
|
||||
{
|
||||
Application: "app1",
|
||||
Message: "",
|
||||
Status: "Waiting",
|
||||
Step: "1",
|
||||
Application: "app1",
|
||||
Message: "",
|
||||
Status: "Waiting",
|
||||
Step: "1",
|
||||
TargetRevisions: []string{"Current"},
|
||||
},
|
||||
},
|
||||
},
|
||||
@@ -5055,10 +5036,11 @@ func TestUpdateApplicationSetApplicationStatus(t *testing.T) {
|
||||
},
|
||||
expectedAppStatus: []v1alpha1.ApplicationSetApplicationStatus{
|
||||
{
|
||||
Application: "app1",
|
||||
Message: "Application resource is already Healthy, updating status from Waiting to Healthy.",
|
||||
Status: "Healthy",
|
||||
Step: "1",
|
||||
Application: "app1",
|
||||
Message: "Application resource is already Healthy, updating status from Waiting to Healthy.",
|
||||
Status: "Healthy",
|
||||
Step: "1",
|
||||
TargetRevisions: []string{"Current"},
|
||||
},
|
||||
},
|
||||
},
|
||||
@@ -5334,16 +5316,18 @@ func TestUpdateApplicationSetApplicationStatus(t *testing.T) {
|
||||
Status: v1alpha1.ApplicationSetStatus{
|
||||
ApplicationStatus: []v1alpha1.ApplicationSetApplicationStatus{
|
||||
{
|
||||
Application: "app1",
|
||||
Message: "Application has pending changes, setting status to Waiting.",
|
||||
Status: "Waiting",
|
||||
Step: "1",
|
||||
Application: "app1",
|
||||
Message: "Application has pending changes, setting status to Waiting.",
|
||||
Status: "Waiting",
|
||||
Step: "1",
|
||||
TargetRevisions: []string{"Current"},
|
||||
},
|
||||
{
|
||||
Application: "app2",
|
||||
Message: "Application has pending changes, setting status to Waiting.",
|
||||
Status: "Waiting",
|
||||
Step: "1",
|
||||
Application: "app2",
|
||||
Message: "Application has pending changes, setting status to Waiting.",
|
||||
Status: "Waiting",
|
||||
Step: "1",
|
||||
TargetRevisions: []string{"Current"},
|
||||
},
|
||||
},
|
||||
},
|
||||
@@ -5368,10 +5352,11 @@ func TestUpdateApplicationSetApplicationStatus(t *testing.T) {
|
||||
},
|
||||
expectedAppStatus: []v1alpha1.ApplicationSetApplicationStatus{
|
||||
{
|
||||
Application: "app1",
|
||||
Message: "Application resource is already Healthy, updating status from Waiting to Healthy.",
|
||||
Status: "Healthy",
|
||||
Step: "1",
|
||||
Application: "app1",
|
||||
Message: "Application resource is already Healthy, updating status from Waiting to Healthy.",
|
||||
Status: "Healthy",
|
||||
Step: "1",
|
||||
TargetRevisions: []string{"Current"},
|
||||
},
|
||||
},
|
||||
},
|
||||
@@ -5387,7 +5372,6 @@ func TestUpdateApplicationSetApplicationStatus(t *testing.T) {
|
||||
Client: client,
|
||||
Scheme: scheme,
|
||||
Recorder: record.NewFakeRecorder(1),
|
||||
Cache: &fakeCache{},
|
||||
Generators: map[string]generators.Generator{},
|
||||
ArgoDB: &argoDBMock,
|
||||
ArgoAppClientset: appclientset.NewSimpleClientset(argoObjs...),
|
||||
@@ -5533,9 +5517,10 @@ func TestUpdateApplicationSetApplicationStatusProgress(t *testing.T) {
|
||||
Status: v1alpha1.ApplicationSetStatus{
|
||||
ApplicationStatus: []v1alpha1.ApplicationSetApplicationStatus{
|
||||
{
|
||||
Application: "app1",
|
||||
Message: "Application is out of date with the current AppSet generation, setting status to Waiting.",
|
||||
Status: "Waiting",
|
||||
Application: "app1",
|
||||
Message: "Application is out of date with the current AppSet generation, setting status to Waiting.",
|
||||
Status: "Waiting",
|
||||
TargetRevisions: []string{"Next"},
|
||||
},
|
||||
},
|
||||
},
|
||||
@@ -5553,6 +5538,7 @@ func TestUpdateApplicationSetApplicationStatusProgress(t *testing.T) {
|
||||
Message: "Application moved to Pending status, watching for the Application resource to start Progressing.",
|
||||
Status: "Pending",
|
||||
Step: "1",
|
||||
TargetRevisions: []string{"Next"},
|
||||
},
|
||||
},
|
||||
},
|
||||
@@ -6138,7 +6124,6 @@ func TestUpdateApplicationSetApplicationStatusProgress(t *testing.T) {
|
||||
Client: client,
|
||||
Scheme: scheme,
|
||||
Recorder: record.NewFakeRecorder(1),
|
||||
Cache: &fakeCache{},
|
||||
Generators: map[string]generators.Generator{},
|
||||
ArgoDB: &argoDBMock,
|
||||
ArgoAppClientset: appclientset.NewSimpleClientset(argoObjs...),
|
||||
@@ -6353,7 +6338,6 @@ func TestUpdateResourceStatus(t *testing.T) {
|
||||
Client: client,
|
||||
Scheme: scheme,
|
||||
Recorder: record.NewFakeRecorder(1),
|
||||
Cache: &fakeCache{},
|
||||
Generators: map[string]generators.Generator{},
|
||||
ArgoDB: &argoDBMock,
|
||||
ArgoAppClientset: appclientset.NewSimpleClientset(argoObjs...),
|
||||
@@ -6368,6 +6352,102 @@ func TestUpdateResourceStatus(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func generateNAppResourceStatuses(n int) []v1alpha1.ResourceStatus {
|
||||
var r []v1alpha1.ResourceStatus
|
||||
for i := 0; i < n; i++ {
|
||||
r = append(r, v1alpha1.ResourceStatus{
|
||||
Name: "app" + strconv.Itoa(i),
|
||||
Status: v1alpha1.SyncStatusCodeSynced,
|
||||
Health: &v1alpha1.HealthStatus{
|
||||
Status: health.HealthStatusHealthy,
|
||||
Message: "OK",
|
||||
},
|
||||
},
|
||||
)
|
||||
}
|
||||
return r
|
||||
}
|
||||
|
||||
func generateNHealthyApps(n int) []v1alpha1.Application {
|
||||
var r []v1alpha1.Application
|
||||
for i := 0; i < n; i++ {
|
||||
r = append(r, v1alpha1.Application{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "app" + strconv.Itoa(i),
|
||||
},
|
||||
Status: v1alpha1.ApplicationStatus{
|
||||
Sync: v1alpha1.SyncStatus{
|
||||
Status: v1alpha1.SyncStatusCodeSynced,
|
||||
},
|
||||
Health: v1alpha1.HealthStatus{
|
||||
Status: health.HealthStatusHealthy,
|
||||
Message: "OK",
|
||||
},
|
||||
},
|
||||
})
|
||||
}
|
||||
return r
|
||||
}
|
||||
|
||||
func TestResourceStatusAreOrdered(t *testing.T) {
|
||||
scheme := runtime.NewScheme()
|
||||
err := v1alpha1.AddToScheme(scheme)
|
||||
require.NoError(t, err)
|
||||
|
||||
err = v1alpha1.AddToScheme(scheme)
|
||||
require.NoError(t, err)
|
||||
for _, cc := range []struct {
|
||||
name string
|
||||
appSet v1alpha1.ApplicationSet
|
||||
apps []v1alpha1.Application
|
||||
expectedResources []v1alpha1.ResourceStatus
|
||||
}{
|
||||
{
|
||||
name: "Ensures AppSet is always ordered",
|
||||
appSet: v1alpha1.ApplicationSet{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "name",
|
||||
Namespace: "argocd",
|
||||
},
|
||||
Status: v1alpha1.ApplicationSetStatus{
|
||||
Resources: []v1alpha1.ResourceStatus{},
|
||||
},
|
||||
},
|
||||
apps: generateNHealthyApps(10),
|
||||
expectedResources: generateNAppResourceStatuses(10),
|
||||
},
|
||||
} {
|
||||
t.Run(cc.name, func(t *testing.T) {
|
||||
kubeclientset := kubefake.NewSimpleClientset([]runtime.Object{}...)
|
||||
argoDBMock := dbmocks.ArgoDB{}
|
||||
argoObjs := []runtime.Object{}
|
||||
|
||||
client := fake.NewClientBuilder().WithScheme(scheme).WithStatusSubresource(&cc.appSet).WithObjects(&cc.appSet).Build()
|
||||
|
||||
r := ApplicationSetReconciler{
|
||||
Client: client,
|
||||
Scheme: scheme,
|
||||
Recorder: record.NewFakeRecorder(1),
|
||||
Generators: map[string]generators.Generator{},
|
||||
ArgoDB: &argoDBMock,
|
||||
ArgoAppClientset: appclientset.NewSimpleClientset(argoObjs...),
|
||||
KubeClientset: kubeclientset,
|
||||
}
|
||||
|
||||
err := r.updateResourcesStatus(context.TODO(), log.NewEntry(log.StandardLogger()), &cc.appSet, cc.apps)
|
||||
require.NoError(t, err, "expected no errors, but errors occurred")
|
||||
|
||||
err = r.updateResourcesStatus(context.TODO(), log.NewEntry(log.StandardLogger()), &cc.appSet, cc.apps)
|
||||
require.NoError(t, err, "expected no errors, but errors occurred")
|
||||
|
||||
err = r.updateResourcesStatus(context.TODO(), log.NewEntry(log.StandardLogger()), &cc.appSet, cc.apps)
|
||||
require.NoError(t, err, "expected no errors, but errors occurred")
|
||||
|
||||
assert.Equal(t, cc.expectedResources, cc.appSet.Status.Resources, "expected resources did not match actual")
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestOwnsHandler(t *testing.T) {
|
||||
// progressive syncs do not affect create, delete, or generic
|
||||
ownsHandler := getOwnsHandlerPredicates(true)
|
||||
@@ -6559,3 +6639,157 @@ func TestOwnsHandler(t *testing.T) {
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestMigrateStatus(t *testing.T) {
|
||||
scheme := runtime.NewScheme()
|
||||
err := v1alpha1.AddToScheme(scheme)
|
||||
require.NoError(t, err)
|
||||
|
||||
err = v1alpha1.AddToScheme(scheme)
|
||||
require.NoError(t, err)
|
||||
|
||||
for _, tc := range []struct {
|
||||
name string
|
||||
appset v1alpha1.ApplicationSet
|
||||
expectedStatus v1alpha1.ApplicationSetStatus
|
||||
}{
|
||||
{
|
||||
name: "status without applicationstatus target revisions set will default to empty list",
|
||||
appset: v1alpha1.ApplicationSet{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "test",
|
||||
Namespace: "test",
|
||||
},
|
||||
Status: v1alpha1.ApplicationSetStatus{
|
||||
ApplicationStatus: []v1alpha1.ApplicationSetApplicationStatus{
|
||||
{},
|
||||
},
|
||||
},
|
||||
},
|
||||
expectedStatus: v1alpha1.ApplicationSetStatus{
|
||||
ApplicationStatus: []v1alpha1.ApplicationSetApplicationStatus{
|
||||
{
|
||||
TargetRevisions: []string{},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "status with applicationstatus target revisions set will do nothing",
|
||||
appset: v1alpha1.ApplicationSet{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "test",
|
||||
Namespace: "test",
|
||||
},
|
||||
Status: v1alpha1.ApplicationSetStatus{
|
||||
ApplicationStatus: []v1alpha1.ApplicationSetApplicationStatus{
|
||||
{
|
||||
TargetRevisions: []string{"Current"},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
expectedStatus: v1alpha1.ApplicationSetStatus{
|
||||
ApplicationStatus: []v1alpha1.ApplicationSetApplicationStatus{
|
||||
{
|
||||
TargetRevisions: []string{"Current"},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
} {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
client := fake.NewClientBuilder().WithScheme(scheme).WithStatusSubresource(&tc.appset).WithObjects(&tc.appset).Build()
|
||||
r := ApplicationSetReconciler{
|
||||
Client: client,
|
||||
}
|
||||
|
||||
err := r.migrateStatus(context.Background(), &tc.appset)
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, tc.expectedStatus, tc.appset.Status)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestIgnoreNotAllowedNamespaces(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
namespaces []string
|
||||
objectNS string
|
||||
expected bool
|
||||
}{
|
||||
{
|
||||
name: "Namespace allowed",
|
||||
namespaces: []string{"allowed-namespace"},
|
||||
objectNS: "allowed-namespace",
|
||||
expected: true,
|
||||
},
|
||||
{
|
||||
name: "Namespace not allowed",
|
||||
namespaces: []string{"allowed-namespace"},
|
||||
objectNS: "not-allowed-namespace",
|
||||
expected: false,
|
||||
},
|
||||
{
|
||||
name: "Empty allowed namespaces",
|
||||
namespaces: []string{},
|
||||
objectNS: "any-namespace",
|
||||
expected: false,
|
||||
},
|
||||
{
|
||||
name: "Multiple allowed namespaces",
|
||||
namespaces: []string{"allowed-namespace-1", "allowed-namespace-2"},
|
||||
objectNS: "allowed-namespace-2",
|
||||
expected: true,
|
||||
},
|
||||
{
|
||||
name: "Namespace not in multiple allowed namespaces",
|
||||
namespaces: []string{"allowed-namespace-1", "allowed-namespace-2"},
|
||||
objectNS: "not-allowed-namespace",
|
||||
expected: false,
|
||||
},
|
||||
{
|
||||
name: "Namespace matched by glob pattern",
|
||||
namespaces: []string{"allowed-namespace-*"},
|
||||
objectNS: "allowed-namespace-1",
|
||||
expected: true,
|
||||
},
|
||||
{
|
||||
name: "Namespace matched by regex pattern",
|
||||
namespaces: []string{"/^allowed-namespace-[^-]+$/"},
|
||||
objectNS: "allowed-namespace-1",
|
||||
expected: true,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
predicate := ignoreNotAllowedNamespaces(tt.namespaces)
|
||||
object := &v1alpha1.ApplicationSet{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Namespace: tt.objectNS,
|
||||
},
|
||||
}
|
||||
|
||||
t.Run(tt.name+":Create", func(t *testing.T) {
|
||||
result := predicate.Create(event.CreateEvent{Object: object})
|
||||
assert.Equal(t, tt.expected, result)
|
||||
})
|
||||
|
||||
t.Run(tt.name+":Update", func(t *testing.T) {
|
||||
result := predicate.Update(event.UpdateEvent{ObjectNew: object})
|
||||
assert.Equal(t, tt.expected, result)
|
||||
})
|
||||
|
||||
t.Run(tt.name+":Delete", func(t *testing.T) {
|
||||
result := predicate.Delete(event.DeleteEvent{Object: object})
|
||||
assert.Equal(t, tt.expected, result)
|
||||
})
|
||||
|
||||
t.Run(tt.name+":Generic", func(t *testing.T) {
|
||||
result := predicate.Generic(event.GenericEvent{Object: object})
|
||||
assert.Equal(t, tt.expected, result)
|
||||
})
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@@ -60,7 +60,7 @@ func TestRequeueAfter(t *testing.T) {
|
||||
terminalGenerators := map[string]generators.Generator{
|
||||
"List": generators.NewListGenerator(),
|
||||
"Clusters": generators.NewClusterGenerator(k8sClient, ctx, appClientset, "argocd"),
|
||||
"Git": generators.NewGitGenerator(mockServer),
|
||||
"Git": generators.NewGitGenerator(mockServer, "namespace"),
|
||||
"SCMProvider": generators.NewSCMProviderGenerator(fake.NewClientBuilder().WithObjects(&corev1.Secret{}).Build(), generators.SCMAuthProviders{}, "", []string{""}, true),
|
||||
"ClusterDecisionResource": generators.NewDuckTypeGenerator(ctx, fakeDynClient, appClientset, "argocd"),
|
||||
"PullRequest": generators.NewPullRequestGenerator(k8sClient, generators.SCMAuthProviders{}, "", []string{""}, true),
|
||||
|
||||
@@ -346,7 +346,7 @@ func getMockClusterGenerator() Generator {
|
||||
func getMockGitGenerator() Generator {
|
||||
argoCDServiceMock := mocks.Repos{}
|
||||
argoCDServiceMock.On("GetDirectories", mock.Anything, mock.Anything, mock.Anything).Return([]string{"app1", "app2", "app_3", "p1/app4"}, nil)
|
||||
gitGenerator := NewGitGenerator(&argoCDServiceMock)
|
||||
gitGenerator := NewGitGenerator(&argoCDServiceMock, "namespace")
|
||||
return gitGenerator
|
||||
}
|
||||
|
||||
|
||||
@@ -24,13 +24,16 @@ import (
|
||||
var _ Generator = (*GitGenerator)(nil)
|
||||
|
||||
type GitGenerator struct {
|
||||
repos services.Repos
|
||||
repos services.Repos
|
||||
namespace string
|
||||
}
|
||||
|
||||
func NewGitGenerator(repos services.Repos) Generator {
|
||||
func NewGitGenerator(repos services.Repos, namespace string) Generator {
|
||||
g := &GitGenerator{
|
||||
repos: repos,
|
||||
repos: repos,
|
||||
namespace: namespace,
|
||||
}
|
||||
|
||||
return g
|
||||
}
|
||||
|
||||
@@ -59,21 +62,25 @@ func (g *GitGenerator) GenerateParams(appSetGenerator *argoprojiov1alpha1.Applic
|
||||
|
||||
noRevisionCache := appSet.RefreshRequired()
|
||||
|
||||
var project string
|
||||
if strings.Contains(appSet.Spec.Template.Spec.Project, "{{") {
|
||||
project = appSetGenerator.Git.Template.Spec.Project
|
||||
} else {
|
||||
project = appSet.Spec.Template.Spec.Project
|
||||
}
|
||||
verifyCommit := false
|
||||
|
||||
appProject := &argoprojiov1alpha1.AppProject{}
|
||||
if err := client.Get(context.TODO(), types.NamespacedName{Name: appSet.Spec.Template.Spec.Project, Namespace: appSet.Namespace}, appProject); err != nil {
|
||||
return nil, fmt.Errorf("error getting project %s: %w", project, err)
|
||||
// When the project field is templated, the contents of the git repo are required to run the git generator and get the templated value,
|
||||
// but git generator cannot be called without verifying the commit signature.
|
||||
// In this case, we skip the signature verification.
|
||||
if !strings.Contains(appSet.Spec.Template.Spec.Project, "{{") {
|
||||
project := appSet.Spec.Template.Spec.Project
|
||||
appProject := &argoprojiov1alpha1.AppProject{}
|
||||
namespace := g.namespace
|
||||
if namespace == "" {
|
||||
namespace = appSet.Namespace
|
||||
}
|
||||
if err := client.Get(context.TODO(), types.NamespacedName{Name: project, Namespace: namespace}, appProject); err != nil {
|
||||
return nil, fmt.Errorf("error getting project %s: %w", project, err)
|
||||
}
|
||||
// we need to verify the signature on the Git revision if GPG is enabled
|
||||
verifyCommit = appProject.Spec.SignatureKeys != nil && len(appProject.Spec.SignatureKeys) > 0 && gpg.IsGPGEnabled()
|
||||
}
|
||||
|
||||
// we need to verify the signature on the Git revision if GPG is enabled
|
||||
verifyCommit := appProject.Spec.SignatureKeys != nil && len(appProject.Spec.SignatureKeys) > 0 && gpg.IsGPGEnabled()
|
||||
|
||||
var err error
|
||||
var res []map[string]interface{}
|
||||
if len(appSetGenerator.Git.Directories) != 0 {
|
||||
|
||||
@@ -323,7 +323,7 @@ func TestGitGenerateParamsFromDirectories(t *testing.T) {
|
||||
|
||||
argoCDServiceMock.On("GetDirectories", mock.Anything, mock.Anything, mock.Anything, mock.Anything, mock.Anything).Return(testCaseCopy.repoApps, testCaseCopy.repoError)
|
||||
|
||||
gitGenerator := NewGitGenerator(&argoCDServiceMock)
|
||||
gitGenerator := NewGitGenerator(&argoCDServiceMock, "")
|
||||
applicationSetInfo := argoprojiov1alpha1.ApplicationSet{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "set",
|
||||
@@ -624,7 +624,7 @@ func TestGitGenerateParamsFromDirectoriesGoTemplate(t *testing.T) {
|
||||
|
||||
argoCDServiceMock.On("GetDirectories", mock.Anything, mock.Anything, mock.Anything, mock.Anything, mock.Anything).Return(testCaseCopy.repoApps, testCaseCopy.repoError)
|
||||
|
||||
gitGenerator := NewGitGenerator(&argoCDServiceMock)
|
||||
gitGenerator := NewGitGenerator(&argoCDServiceMock, "")
|
||||
applicationSetInfo := argoprojiov1alpha1.ApplicationSet{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "set",
|
||||
@@ -989,7 +989,7 @@ cluster:
|
||||
argoCDServiceMock.On("GetFiles", mock.Anything, mock.Anything, mock.Anything, mock.Anything, mock.Anything, mock.Anything).
|
||||
Return(testCaseCopy.repoFileContents, testCaseCopy.repoPathsError)
|
||||
|
||||
gitGenerator := NewGitGenerator(&argoCDServiceMock)
|
||||
gitGenerator := NewGitGenerator(&argoCDServiceMock, "")
|
||||
applicationSetInfo := argoprojiov1alpha1.ApplicationSet{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "set",
|
||||
@@ -1345,7 +1345,7 @@ cluster:
|
||||
argoCDServiceMock.On("GetFiles", mock.Anything, mock.Anything, mock.Anything, mock.Anything, mock.Anything, mock.Anything).
|
||||
Return(testCaseCopy.repoFileContents, testCaseCopy.repoPathsError)
|
||||
|
||||
gitGenerator := NewGitGenerator(&argoCDServiceMock)
|
||||
gitGenerator := NewGitGenerator(&argoCDServiceMock, "")
|
||||
applicationSetInfo := argoprojiov1alpha1.ApplicationSet{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "set",
|
||||
@@ -1383,3 +1383,114 @@ cluster:
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestGitGenerator_GenerateParams(t *testing.T) {
|
||||
cases := []struct {
|
||||
name string
|
||||
directories []argoprojiov1alpha1.GitDirectoryGeneratorItem
|
||||
pathParamPrefix string
|
||||
repoApps []string
|
||||
repoPathsError error
|
||||
repoFileContents map[string][]byte
|
||||
values map[string]string
|
||||
expected []map[string]interface{}
|
||||
expectedError error
|
||||
appset argoprojiov1alpha1.ApplicationSet
|
||||
callGetDirectories bool
|
||||
}{
|
||||
{
|
||||
name: "Signature Verification - ignores templated project field",
|
||||
repoApps: []string{
|
||||
"app1",
|
||||
},
|
||||
repoPathsError: nil,
|
||||
appset: argoprojiov1alpha1.ApplicationSet{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "set",
|
||||
Namespace: "namespace",
|
||||
},
|
||||
Spec: argoprojiov1alpha1.ApplicationSetSpec{
|
||||
Generators: []argoprojiov1alpha1.ApplicationSetGenerator{{
|
||||
Git: &argoprojiov1alpha1.GitGenerator{
|
||||
RepoURL: "RepoURL",
|
||||
Revision: "Revision",
|
||||
Directories: []argoprojiov1alpha1.GitDirectoryGeneratorItem{{Path: "*"}},
|
||||
PathParamPrefix: "",
|
||||
Values: map[string]string{
|
||||
"foo": "bar",
|
||||
},
|
||||
},
|
||||
}},
|
||||
Template: argoprojiov1alpha1.ApplicationSetTemplate{
|
||||
Spec: argoprojiov1alpha1.ApplicationSpec{
|
||||
Project: "{{.project}}",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
callGetDirectories: true,
|
||||
expected: []map[string]interface{}{{"path": "app1", "path.basename": "app1", "path.basenameNormalized": "app1", "path[0]": "app1", "values.foo": "bar"}},
|
||||
expectedError: nil,
|
||||
},
|
||||
{
|
||||
name: "Signature Verification - Checks for non-templated project field",
|
||||
repoApps: []string{
|
||||
"app1",
|
||||
},
|
||||
repoPathsError: nil,
|
||||
appset: argoprojiov1alpha1.ApplicationSet{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "set",
|
||||
Namespace: "namespace",
|
||||
},
|
||||
Spec: argoprojiov1alpha1.ApplicationSetSpec{
|
||||
Generators: []argoprojiov1alpha1.ApplicationSetGenerator{{
|
||||
Git: &argoprojiov1alpha1.GitGenerator{
|
||||
RepoURL: "RepoURL",
|
||||
Revision: "Revision",
|
||||
Directories: []argoprojiov1alpha1.GitDirectoryGeneratorItem{{Path: "*"}},
|
||||
PathParamPrefix: "",
|
||||
Values: map[string]string{
|
||||
"foo": "bar",
|
||||
},
|
||||
},
|
||||
}},
|
||||
Template: argoprojiov1alpha1.ApplicationSetTemplate{
|
||||
Spec: argoprojiov1alpha1.ApplicationSpec{
|
||||
Project: "project",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
callGetDirectories: false,
|
||||
expected: []map[string]interface{}{{"path": "app1", "path.basename": "app1", "path.basenameNormalized": "app1", "path[0]": "app1", "values.foo": "bar"}},
|
||||
expectedError: fmt.Errorf("error getting project project: appprojects.argoproj.io \"project\" not found"),
|
||||
},
|
||||
}
|
||||
for _, testCase := range cases {
|
||||
argoCDServiceMock := mocks.Repos{}
|
||||
|
||||
if testCase.callGetDirectories {
|
||||
argoCDServiceMock.On("GetDirectories", mock.Anything, mock.Anything, mock.Anything, mock.Anything, mock.Anything).Return(testCase.repoApps, testCase.repoPathsError)
|
||||
}
|
||||
gitGenerator := NewGitGenerator(&argoCDServiceMock, "namespace")
|
||||
|
||||
scheme := runtime.NewScheme()
|
||||
err := v1alpha1.AddToScheme(scheme)
|
||||
require.NoError(t, err)
|
||||
appProject := argoprojiov1alpha1.AppProject{}
|
||||
|
||||
client := fake.NewClientBuilder().WithScheme(scheme).WithObjects(&appProject).Build()
|
||||
|
||||
got, err := gitGenerator.GenerateParams(&testCase.appset.Spec.Generators[0], &testCase.appset, client)
|
||||
|
||||
if testCase.expectedError != nil {
|
||||
require.EqualError(t, err, testCase.expectedError.Error())
|
||||
} else {
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, testCase.expected, got)
|
||||
}
|
||||
|
||||
argoCDServiceMock.AssertExpectations(t)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -9,6 +9,8 @@ import (
|
||||
argoprojiov1alpha1 "github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1"
|
||||
)
|
||||
|
||||
//go:generate go run github.com/vektra/mockery/v2@v2.40.2 --name=Generator
|
||||
|
||||
// Generator defines the interface implemented by all ApplicationSet generators.
|
||||
type Generator interface {
|
||||
// GenerateParams interprets the ApplicationSet and generates all relevant parameters for the application template.
|
||||
|
||||
@@ -1089,7 +1089,7 @@ func TestGitGenerator_GenerateParams_list_x_git_matrix_generator(t *testing.T) {
|
||||
repoServiceMock.On("GetFiles", mock.Anything, mock.Anything, mock.Anything, mock.Anything, mock.Anything, mock.Anything).Return(map[string][]byte{
|
||||
"some/path.json": []byte("test: content"),
|
||||
}, nil)
|
||||
gitGenerator := NewGitGenerator(repoServiceMock)
|
||||
gitGenerator := NewGitGenerator(repoServiceMock, "")
|
||||
|
||||
matrixGenerator := NewMatrixGenerator(map[string]Generator{
|
||||
"List": listGeneratorMock,
|
||||
|
||||
100
applicationset/generators/mocks/Generator.go
Normal file
100
applicationset/generators/mocks/Generator.go
Normal file
@@ -0,0 +1,100 @@
|
||||
// Code generated by mockery v2.40.2. DO NOT EDIT.
|
||||
|
||||
package mocks
|
||||
|
||||
import (
|
||||
client "sigs.k8s.io/controller-runtime/pkg/client"
|
||||
|
||||
mock "github.com/stretchr/testify/mock"
|
||||
|
||||
time "time"
|
||||
|
||||
v1alpha1 "github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1"
|
||||
)
|
||||
|
||||
// Generator is an autogenerated mock type for the Generator type
|
||||
type Generator struct {
|
||||
mock.Mock
|
||||
}
|
||||
|
||||
// GenerateParams provides a mock function with given fields: appSetGenerator, applicationSetInfo, _a2
|
||||
func (_m *Generator) GenerateParams(appSetGenerator *v1alpha1.ApplicationSetGenerator, applicationSetInfo *v1alpha1.ApplicationSet, _a2 client.Client) ([]map[string]interface{}, error) {
|
||||
ret := _m.Called(appSetGenerator, applicationSetInfo, _a2)
|
||||
|
||||
if len(ret) == 0 {
|
||||
panic("no return value specified for GenerateParams")
|
||||
}
|
||||
|
||||
var r0 []map[string]interface{}
|
||||
var r1 error
|
||||
if rf, ok := ret.Get(0).(func(*v1alpha1.ApplicationSetGenerator, *v1alpha1.ApplicationSet, client.Client) ([]map[string]interface{}, error)); ok {
|
||||
return rf(appSetGenerator, applicationSetInfo, _a2)
|
||||
}
|
||||
if rf, ok := ret.Get(0).(func(*v1alpha1.ApplicationSetGenerator, *v1alpha1.ApplicationSet, client.Client) []map[string]interface{}); ok {
|
||||
r0 = rf(appSetGenerator, applicationSetInfo, _a2)
|
||||
} else {
|
||||
if ret.Get(0) != nil {
|
||||
r0 = ret.Get(0).([]map[string]interface{})
|
||||
}
|
||||
}
|
||||
|
||||
if rf, ok := ret.Get(1).(func(*v1alpha1.ApplicationSetGenerator, *v1alpha1.ApplicationSet, client.Client) error); ok {
|
||||
r1 = rf(appSetGenerator, applicationSetInfo, _a2)
|
||||
} else {
|
||||
r1 = ret.Error(1)
|
||||
}
|
||||
|
||||
return r0, r1
|
||||
}
|
||||
|
||||
// GetRequeueAfter provides a mock function with given fields: appSetGenerator
|
||||
func (_m *Generator) GetRequeueAfter(appSetGenerator *v1alpha1.ApplicationSetGenerator) time.Duration {
|
||||
ret := _m.Called(appSetGenerator)
|
||||
|
||||
if len(ret) == 0 {
|
||||
panic("no return value specified for GetRequeueAfter")
|
||||
}
|
||||
|
||||
var r0 time.Duration
|
||||
if rf, ok := ret.Get(0).(func(*v1alpha1.ApplicationSetGenerator) time.Duration); ok {
|
||||
r0 = rf(appSetGenerator)
|
||||
} else {
|
||||
r0 = ret.Get(0).(time.Duration)
|
||||
}
|
||||
|
||||
return r0
|
||||
}
|
||||
|
||||
// GetTemplate provides a mock function with given fields: appSetGenerator
|
||||
func (_m *Generator) GetTemplate(appSetGenerator *v1alpha1.ApplicationSetGenerator) *v1alpha1.ApplicationSetTemplate {
|
||||
ret := _m.Called(appSetGenerator)
|
||||
|
||||
if len(ret) == 0 {
|
||||
panic("no return value specified for GetTemplate")
|
||||
}
|
||||
|
||||
var r0 *v1alpha1.ApplicationSetTemplate
|
||||
if rf, ok := ret.Get(0).(func(*v1alpha1.ApplicationSetGenerator) *v1alpha1.ApplicationSetTemplate); ok {
|
||||
r0 = rf(appSetGenerator)
|
||||
} else {
|
||||
if ret.Get(0) != nil {
|
||||
r0 = ret.Get(0).(*v1alpha1.ApplicationSetTemplate)
|
||||
}
|
||||
}
|
||||
|
||||
return r0
|
||||
}
|
||||
|
||||
// NewGenerator creates a new instance of Generator. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations.
|
||||
// The first argument is typically a *testing.T value.
|
||||
func NewGenerator(t interface {
|
||||
mock.TestingT
|
||||
Cleanup(func())
|
||||
}) *Generator {
|
||||
mock := &Generator{}
|
||||
mock.Mock.Test(t)
|
||||
|
||||
t.Cleanup(func() { mock.AssertExpectations(t) })
|
||||
|
||||
return mock
|
||||
}
|
||||
@@ -4653,6 +4653,9 @@
|
||||
"help": {
|
||||
"$ref": "#/definitions/clusterHelp"
|
||||
},
|
||||
"installationID": {
|
||||
"type": "string"
|
||||
},
|
||||
"kustomizeOptions": {
|
||||
"$ref": "#/definitions/v1alpha1KustomizeOptions"
|
||||
},
|
||||
@@ -6960,7 +6963,7 @@
|
||||
},
|
||||
"serverVersion": {
|
||||
"type": "string",
|
||||
"title": "DEPRECATED: use Info.ServerVersion field instead.\nThe server version"
|
||||
"title": "Deprecated: use Info.ServerVersion field instead.\nThe server version"
|
||||
},
|
||||
"shard": {
|
||||
"description": "Shard contains optional shard number. Calculated on the fly by the application controller if not specified.",
|
||||
@@ -9020,6 +9023,11 @@
|
||||
"description": "SyncOperation contains details about a sync operation.",
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"autoHealAttemptsCount": {
|
||||
"type": "integer",
|
||||
"format": "int64",
|
||||
"title": "SelfHealAttemptsCount contains the number of auto-heal attempts"
|
||||
},
|
||||
"dryRun": {
|
||||
"type": "boolean",
|
||||
"title": "DryRun specifies to perform a `kubectl apply --dry-run` without actually performing the sync"
|
||||
|
||||
@@ -10,6 +10,7 @@ import (
|
||||
"github.com/redis/go-redis/v9"
|
||||
log "github.com/sirupsen/logrus"
|
||||
"github.com/spf13/cobra"
|
||||
"k8s.io/apimachinery/pkg/util/wait"
|
||||
"k8s.io/client-go/kubernetes"
|
||||
"k8s.io/client-go/tools/clientcmd"
|
||||
|
||||
@@ -53,6 +54,9 @@ func NewCommand() *cobra.Command {
|
||||
repoServerAddress string
|
||||
repoServerTimeoutSeconds int
|
||||
selfHealTimeoutSeconds int
|
||||
selfHealBackoffTimeoutSeconds int
|
||||
selfHealBackoffFactor int
|
||||
selfHealBackoffCapSeconds int
|
||||
statusProcessors int
|
||||
operationProcessors int
|
||||
glogLevel int
|
||||
@@ -148,6 +152,14 @@ func NewCommand() *cobra.Command {
|
||||
kubectl := kubeutil.NewKubectl()
|
||||
clusterSharding, err := sharding.GetClusterSharding(kubeClient, settingsMgr, shardingAlgorithm, enableDynamicClusterDistribution)
|
||||
errors.CheckError(err)
|
||||
var selfHealBackoff *wait.Backoff
|
||||
if selfHealBackoffTimeoutSeconds != 0 {
|
||||
selfHealBackoff = &wait.Backoff{
|
||||
Duration: time.Duration(selfHealBackoffTimeoutSeconds) * time.Second,
|
||||
Factor: float64(selfHealBackoffFactor),
|
||||
Cap: time.Duration(selfHealBackoffCapSeconds) * time.Second,
|
||||
}
|
||||
}
|
||||
appController, err = controller.NewApplicationController(
|
||||
namespace,
|
||||
settingsMgr,
|
||||
@@ -160,6 +172,7 @@ func NewCommand() *cobra.Command {
|
||||
hardResyncDuration,
|
||||
time.Duration(appResyncJitter)*time.Second,
|
||||
time.Duration(selfHealTimeoutSeconds)*time.Second,
|
||||
selfHealBackoff,
|
||||
time.Duration(repoErrorGracePeriod)*time.Second,
|
||||
metricsPort,
|
||||
metricsCacheExpiration,
|
||||
@@ -209,7 +222,10 @@ func NewCommand() *cobra.Command {
|
||||
command.Flags().IntVar(&glogLevel, "gloglevel", 0, "Set the glog logging level")
|
||||
command.Flags().IntVar(&metricsPort, "metrics-port", common.DefaultPortArgoCDMetrics, "Start metrics server on given port")
|
||||
command.Flags().DurationVar(&metricsCacheExpiration, "metrics-cache-expiration", env.ParseDurationFromEnv("ARGOCD_APPLICATION_CONTROLLER_METRICS_CACHE_EXPIRATION", 0*time.Second, 0, math.MaxInt64), "Prometheus metrics cache expiration (disabled by default. e.g. 24h0m0s)")
|
||||
command.Flags().IntVar(&selfHealTimeoutSeconds, "self-heal-timeout-seconds", env.ParseNumFromEnv("ARGOCD_APPLICATION_CONTROLLER_SELF_HEAL_TIMEOUT_SECONDS", 5, 0, math.MaxInt32), "Specifies timeout between application self heal attempts")
|
||||
command.Flags().IntVar(&selfHealTimeoutSeconds, "self-heal-timeout-seconds", env.ParseNumFromEnv("ARGOCD_APPLICATION_CONTROLLER_SELF_HEAL_TIMEOUT_SECONDS", 0, 0, math.MaxInt32), "Specifies timeout between application self heal attempts")
|
||||
command.Flags().IntVar(&selfHealBackoffTimeoutSeconds, "self-heal-backoff-timeout-seconds", env.ParseNumFromEnv("ARGOCD_APPLICATION_CONTROLLER_SELF_HEAL_BACKOFF_TIMEOUT_SECONDS", 2, 0, math.MaxInt32), "Specifies initial timeout of exponential backoff between self heal attempts")
|
||||
command.Flags().IntVar(&selfHealBackoffFactor, "self-heal-backoff-factor", env.ParseNumFromEnv("ARGOCD_APPLICATION_CONTROLLER_SELF_HEAL_BACKOFF_FACTOR", 3, 0, math.MaxInt32), "Specifies factor of exponential timeout between application self heal attempts")
|
||||
command.Flags().IntVar(&selfHealBackoffCapSeconds, "self-heal-backoff-cap-seconds", env.ParseNumFromEnv("ARGOCD_APPLICATION_CONTROLLER_SELF_HEAL_BACKOFF_CAP_SECONDS", 300, 0, math.MaxInt32), "Specifies max timeout of exponential backoff between application self heal attempts")
|
||||
command.Flags().Int64Var(&kubectlParallelismLimit, "kubectl-parallelism-limit", env.ParseInt64FromEnv("ARGOCD_APPLICATION_CONTROLLER_KUBECTL_PARALLELISM_LIMIT", 20, 0, math.MaxInt64), "Number of allowed concurrent kubectl fork/execs. Any value less than 1 means no limit.")
|
||||
command.Flags().BoolVar(&repoServerPlaintext, "repo-server-plaintext", env.ParseBoolFromEnv("ARGOCD_APPLICATION_CONTROLLER_REPO_SERVER_PLAINTEXT", false), "Disable TLS on connections to repo server")
|
||||
command.Flags().BoolVar(&repoServerStrictTLS, "repo-server-strict-tls", env.ParseBoolFromEnv("ARGOCD_APPLICATION_CONTROLLER_REPO_SERVER_STRICT_TLS", false), "Whether to use strict validation of the TLS cert presented by the repo server")
|
||||
|
||||
@@ -177,7 +177,7 @@ func NewCommand() *cobra.Command {
|
||||
terminalGenerators := map[string]generators.Generator{
|
||||
"List": generators.NewListGenerator(),
|
||||
"Clusters": generators.NewClusterGenerator(mgr.GetClient(), ctx, k8sClient, namespace),
|
||||
"Git": generators.NewGitGenerator(argoCDService),
|
||||
"Git": generators.NewGitGenerator(argoCDService, namespace),
|
||||
"SCMProvider": generators.NewSCMProviderGenerator(mgr.GetClient(), scmAuth, scmRootCAPath, allowedScmProviders, enableScmProviders),
|
||||
"ClusterDecisionResource": generators.NewDuckTypeGenerator(ctx, dynamicClient, k8sClient, namespace),
|
||||
"PullRequest": generators.NewPullRequestGenerator(mgr.GetClient(), scmAuth, scmRootCAPath, allowedScmProviders, enableScmProviders),
|
||||
@@ -234,7 +234,6 @@ func NewCommand() *cobra.Command {
|
||||
SCMRootCAPath: scmRootCAPath,
|
||||
GlobalPreservedAnnotations: globalPreservedAnnotations,
|
||||
GlobalPreservedLabels: globalPreservedLabels,
|
||||
Cache: mgr.GetCache(),
|
||||
}).SetupWithManager(mgr, enableProgressiveSyncs, maxConcurrentReconciliations); err != nil {
|
||||
log.Error(err, "unable to create controller", "controller", "ApplicationSet")
|
||||
os.Exit(1)
|
||||
|
||||
@@ -104,7 +104,12 @@ func loadClusters(ctx context.Context, kubeClient *kubernetes.Clientset, appClie
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
client := redis.NewClient(&redis.Options{Addr: fmt.Sprintf("localhost:%d", port)})
|
||||
|
||||
redisOptions := &redis.Options{Addr: fmt.Sprintf("localhost:%d", port)}
|
||||
if err = common.SetOptionalRedisPasswordFromKubeConfig(ctx, kubeClient, namespace, redisOptions); err != nil {
|
||||
log.Warnf("Failed to fetch & set redis password for namespace %s: %v", namespace, err)
|
||||
}
|
||||
client := redis.NewClient(redisOptions)
|
||||
compressionType, err := cacheutil.CompressionTypeFromString(redisCompressionStr)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
|
||||
@@ -6,25 +6,18 @@ import (
|
||||
"fmt"
|
||||
"math/big"
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
corev1 "k8s.io/api/core/v1"
|
||||
apierr "k8s.io/apimachinery/pkg/api/errors"
|
||||
|
||||
"github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1"
|
||||
"github.com/argoproj/argo-cd/v2/util/cli"
|
||||
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
v1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/client-go/kubernetes"
|
||||
"k8s.io/client-go/tools/clientcmd"
|
||||
|
||||
"github.com/argoproj/argo-cd/v2/common"
|
||||
"github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1"
|
||||
"github.com/argoproj/argo-cd/v2/util/cli"
|
||||
"github.com/argoproj/argo-cd/v2/util/errors"
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
corev1 "k8s.io/api/core/v1"
|
||||
)
|
||||
|
||||
const (
|
||||
defaulRedisInitialPasswordSecretName = "argocd-redis"
|
||||
defaultResisInitialPasswordKey = "auth"
|
||||
)
|
||||
|
||||
func generateRandomPassword() (string, error) {
|
||||
@@ -52,8 +45,8 @@ func NewRedisInitialPasswordCommand() *cobra.Command {
|
||||
namespace, _, err := clientConfig.Namespace()
|
||||
errors.CheckError(err)
|
||||
|
||||
redisInitialPasswordSecretName := defaulRedisInitialPasswordSecretName
|
||||
redisInitialPasswordKey := defaultResisInitialPasswordKey
|
||||
redisInitialPasswordSecretName := common.DefaultRedisInitialPasswordSecretName
|
||||
redisInitialPasswordKey := common.DefaultRedisInitialPasswordKey
|
||||
fmt.Printf("Checking for initial Redis password in secret %s/%s at key %s. \n", namespace, redisInitialPasswordSecretName, redisInitialPasswordKey)
|
||||
|
||||
config, err := clientConfig.ClientConfig()
|
||||
|
||||
@@ -572,8 +572,8 @@ func printAppSummaryTable(app *argoappv1.Application, appURL string, windows *ar
|
||||
var status string
|
||||
var allow, deny, inactiveAllows bool
|
||||
if windows.HasWindows() {
|
||||
active := windows.Active()
|
||||
if active.HasWindows() {
|
||||
active, err := windows.Active()
|
||||
if err == nil && active.HasWindows() {
|
||||
for _, w := range *active {
|
||||
if w.Kind == "deny" {
|
||||
deny = true
|
||||
@@ -582,13 +582,14 @@ func printAppSummaryTable(app *argoappv1.Application, appURL string, windows *ar
|
||||
}
|
||||
}
|
||||
}
|
||||
if windows.InactiveAllows().HasWindows() {
|
||||
inactiveAllowWindows, err := windows.InactiveAllows()
|
||||
if err == nil && inactiveAllowWindows.HasWindows() {
|
||||
inactiveAllows = true
|
||||
}
|
||||
|
||||
s := windows.CanSync(true)
|
||||
if deny || !deny && !allow && inactiveAllows {
|
||||
if s {
|
||||
s, err := windows.CanSync(true)
|
||||
if err == nil && s {
|
||||
status = "Manual Allowed"
|
||||
} else {
|
||||
status = "Sync Denied"
|
||||
@@ -776,8 +777,6 @@ func NewApplicationSetCommand(clientOpts *argocdclient.ClientOptions) *cobra.Com
|
||||
}
|
||||
}
|
||||
|
||||
// sourcePosition startes with 1, thus, it needs to be decreased by 1 to find the correct index in the list of sources
|
||||
sourcePosition = sourcePosition - 1
|
||||
visited := cmdutil.SetAppSpecOptions(c.Flags(), &app.Spec, &appOpts, sourcePosition)
|
||||
if visited == 0 {
|
||||
log.Error("Please set at least one option to update")
|
||||
@@ -1350,7 +1349,7 @@ func groupObjsForDiff(resources *application.ManagedResourcesResponse, objs map[
|
||||
}
|
||||
if local, ok := objs[key]; ok || live != nil {
|
||||
if local != nil && !kube.IsCRD(local) {
|
||||
err = resourceTracking.SetAppInstance(local, argoSettings.AppLabelKey, appName, namespace, argoappv1.TrackingMethod(argoSettings.GetTrackingMethod()))
|
||||
err = resourceTracking.SetAppInstance(local, argoSettings.AppLabelKey, appName, namespace, argoappv1.TrackingMethod(argoSettings.GetTrackingMethod()), argoSettings.GetInstallationID())
|
||||
errors.CheckError(err)
|
||||
}
|
||||
|
||||
|
||||
@@ -8,15 +8,11 @@ import (
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
|
||||
"github.com/argoproj/argo-cd/v2/cmd/argocd/commands/initialize"
|
||||
"github.com/argoproj/argo-cd/v2/common"
|
||||
|
||||
"github.com/alicebob/miniredis/v2"
|
||||
"github.com/golang/protobuf/ptypes/empty"
|
||||
"github.com/redis/go-redis/v9"
|
||||
log "github.com/sirupsen/logrus"
|
||||
"github.com/spf13/cobra"
|
||||
"github.com/spf13/pflag"
|
||||
v1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/apimachinery/pkg/util/runtime"
|
||||
@@ -25,6 +21,8 @@ import (
|
||||
"k8s.io/client-go/tools/clientcmd"
|
||||
"k8s.io/utils/ptr"
|
||||
|
||||
"github.com/argoproj/argo-cd/v2/cmd/argocd/commands/initialize"
|
||||
"github.com/argoproj/argo-cd/v2/common"
|
||||
"github.com/argoproj/argo-cd/v2/pkg/apiclient"
|
||||
"github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1"
|
||||
appclientset "github.com/argoproj/argo-cd/v2/pkg/client/clientset/versioned"
|
||||
@@ -48,6 +46,7 @@ type forwardCacheClient struct {
|
||||
err error
|
||||
redisHaProxyName string
|
||||
redisName string
|
||||
redisPassword string
|
||||
}
|
||||
|
||||
func (c *forwardCacheClient) doLazy(action func(client cache.CacheClient) error) error {
|
||||
@@ -64,7 +63,7 @@ func (c *forwardCacheClient) doLazy(action func(client cache.CacheClient) error)
|
||||
return
|
||||
}
|
||||
|
||||
redisClient := redis.NewClient(&redis.Options{Addr: fmt.Sprintf("localhost:%d", redisPort)})
|
||||
redisClient := redis.NewClient(&redis.Options{Addr: fmt.Sprintf("localhost:%d", redisPort), Password: c.redisPassword})
|
||||
c.client = cache.NewRedisCache(redisClient, time.Hour, c.compression)
|
||||
})
|
||||
if c.err != nil {
|
||||
@@ -240,14 +239,19 @@ func MaybeStartLocalServer(ctx context.Context, clientOpts *apiclient.ClientOpti
|
||||
if err != nil {
|
||||
return fmt.Errorf("error running miniredis: %w", err)
|
||||
}
|
||||
appstateCache := appstatecache.NewCache(cache.NewCache(&forwardCacheClient{namespace: namespace, context: ctxStr, compression: compression, redisHaProxyName: clientOpts.RedisHaProxyName, redisName: clientOpts.RedisName}), time.Hour)
|
||||
redisOptions := &redis.Options{Addr: mr.Addr()}
|
||||
if err = common.SetOptionalRedisPasswordFromKubeConfig(ctx, kubeClientset, namespace, redisOptions); err != nil {
|
||||
log.Warnf("Failed to fetch & set redis password for namespace %s: %v", namespace, err)
|
||||
}
|
||||
|
||||
appstateCache := appstatecache.NewCache(cache.NewCache(&forwardCacheClient{namespace: namespace, context: ctxStr, compression: compression, redisHaProxyName: clientOpts.RedisHaProxyName, redisName: clientOpts.RedisName, redisPassword: redisOptions.Password}), time.Hour)
|
||||
srv := server.NewServer(ctx, server.ArgoCDServerOpts{
|
||||
EnableGZip: false,
|
||||
Namespace: namespace,
|
||||
ListenPort: *port,
|
||||
AppClientset: appClientset,
|
||||
DisableAuth: true,
|
||||
RedisClient: redis.NewClient(&redis.Options{Addr: mr.Addr()}),
|
||||
RedisClient: redis.NewClient(redisOptions),
|
||||
Cache: servercache.NewCache(appstateCache, 0, 0, 0),
|
||||
KubeClientset: kubeClientset,
|
||||
Insecure: true,
|
||||
|
||||
@@ -352,9 +352,10 @@ func printSyncWindows(proj *v1alpha1.AppProject) {
|
||||
fmt.Fprintf(w, fmtStr, headers...)
|
||||
if proj.Spec.SyncWindows.HasWindows() {
|
||||
for i, window := range proj.Spec.SyncWindows {
|
||||
isActive, _ := window.Active()
|
||||
vals := []interface{}{
|
||||
strconv.Itoa(i),
|
||||
formatBoolOutput(window.Active()),
|
||||
formatBoolOutput(isActive),
|
||||
window.Kind,
|
||||
window.Schedule,
|
||||
window.Duration,
|
||||
|
||||
@@ -1,15 +1,20 @@
|
||||
package common
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"context"
|
||||
"fmt"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strconv"
|
||||
"time"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
"github.com/redis/go-redis/v9"
|
||||
"github.com/sirupsen/logrus"
|
||||
"google.golang.org/grpc/codes"
|
||||
"google.golang.org/grpc/status"
|
||||
v1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/client-go/kubernetes"
|
||||
)
|
||||
|
||||
// Component names
|
||||
@@ -172,6 +177,7 @@ const (
|
||||
|
||||
// AnnotationKeyAppInstance is the Argo CD application name is used as the instance name
|
||||
AnnotationKeyAppInstance = "argocd.argoproj.io/tracking-id"
|
||||
AnnotationInstallationID = "argocd.argoproj.io/installation-id"
|
||||
|
||||
// AnnotationCompareOptions is a comma-separated list of options for comparison
|
||||
AnnotationCompareOptions = "argocd.argoproj.io/compare-options"
|
||||
@@ -414,3 +420,30 @@ const TokenVerificationError = "failed to verify the token"
|
||||
var TokenVerificationErr = errors.New(TokenVerificationError)
|
||||
|
||||
var PermissionDeniedAPIError = status.Error(codes.PermissionDenied, "permission denied")
|
||||
|
||||
// Redis password consts
|
||||
const (
|
||||
DefaultRedisInitialPasswordSecretName = "argocd-redis"
|
||||
DefaultRedisInitialPasswordKey = "auth"
|
||||
)
|
||||
|
||||
/*
|
||||
SetOptionalRedisPasswordFromKubeConfig sets the optional Redis password if it exists in the k8s namespace's secrets.
|
||||
|
||||
We specify kubeClient as kubernetes.Interface to allow for mocking in tests, but this should be treated as a kubernetes.Clientset param.
|
||||
*/
|
||||
func SetOptionalRedisPasswordFromKubeConfig(ctx context.Context, kubeClient kubernetes.Interface, namespace string, redisOptions *redis.Options) error {
|
||||
secret, err := kubeClient.CoreV1().Secrets(namespace).Get(ctx, DefaultRedisInitialPasswordSecretName, v1.GetOptions{})
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to get secret %s/%s: %w", namespace, DefaultRedisInitialPasswordSecretName, err)
|
||||
}
|
||||
if secret == nil {
|
||||
return fmt.Errorf("failed to get secret %s/%s: secret is nil", namespace, DefaultRedisInitialPasswordSecretName)
|
||||
}
|
||||
_, ok := secret.Data[DefaultRedisInitialPasswordKey]
|
||||
if !ok {
|
||||
return fmt.Errorf("secret %s/%s does not contain key %s", namespace, DefaultRedisInitialPasswordSecretName, DefaultRedisInitialPasswordKey)
|
||||
}
|
||||
redisOptions.Password = string(secret.Data[DefaultRedisInitialPasswordKey])
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -1,12 +1,18 @@
|
||||
package common
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"os"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/redis/go-redis/v9"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
corev1 "k8s.io/api/core/v1"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
kubefake "k8s.io/client-go/kubernetes/fake"
|
||||
)
|
||||
|
||||
// Test env var not set for EnvGRPCKeepAliveMin
|
||||
@@ -44,3 +50,63 @@ func Test_GRPCKeepAliveMinIncorrectlySet(t *testing.T) {
|
||||
grpcKeepAliveTime := GetGRPCKeepAliveTime()
|
||||
assert.Equal(t, 2*grpcKeepAliveExpectedMin, grpcKeepAliveTime)
|
||||
}
|
||||
|
||||
func TestSetOptionalRedisPasswordFromKubeConfig(t *testing.T) {
|
||||
t.Parallel()
|
||||
testCases := []struct {
|
||||
name, namespace, expectedPassword, expectedErr string
|
||||
secret *corev1.Secret
|
||||
}{
|
||||
{
|
||||
name: "Secret exists with correct key",
|
||||
namespace: "default",
|
||||
expectedPassword: "password123",
|
||||
expectedErr: "",
|
||||
secret: &corev1.Secret{
|
||||
ObjectMeta: metav1.ObjectMeta{Name: DefaultRedisInitialPasswordSecretName},
|
||||
Data: map[string][]byte{DefaultRedisInitialPasswordKey: []byte("password123")},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "Secret does not exist",
|
||||
namespace: "default",
|
||||
expectedPassword: "",
|
||||
expectedErr: fmt.Sprintf("failed to get secret default/%s", DefaultRedisInitialPasswordSecretName),
|
||||
secret: nil,
|
||||
},
|
||||
{
|
||||
name: "Secret exists without correct key",
|
||||
namespace: "default",
|
||||
expectedPassword: "",
|
||||
expectedErr: fmt.Sprintf("secret default/%s does not contain key %s", DefaultRedisInitialPasswordSecretName, DefaultRedisInitialPasswordKey),
|
||||
secret: &corev1.Secret{
|
||||
ObjectMeta: metav1.ObjectMeta{Name: DefaultRedisInitialPasswordSecretName},
|
||||
Data: map[string][]byte{},
|
||||
},
|
||||
},
|
||||
}
|
||||
for _, tc := range testCases {
|
||||
tc := tc
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
t.Parallel()
|
||||
var (
|
||||
ctx = context.TODO()
|
||||
kubeClient = kubefake.NewSimpleClientset()
|
||||
redisOptions = &redis.Options{}
|
||||
)
|
||||
if tc.secret != nil {
|
||||
if _, err := kubeClient.CoreV1().Secrets(tc.namespace).Create(ctx, tc.secret, metav1.CreateOptions{}); err != nil {
|
||||
t.Fatalf("Failed to create secret: %v", err)
|
||||
}
|
||||
}
|
||||
err := SetOptionalRedisPasswordFromKubeConfig(ctx, kubeClient, tc.namespace, redisOptions)
|
||||
if tc.expectedErr != "" {
|
||||
require.Error(t, err)
|
||||
require.Contains(t, err.Error(), tc.expectedErr)
|
||||
} else {
|
||||
require.NoError(t, err)
|
||||
}
|
||||
require.Equal(t, tc.expectedPassword, redisOptions.Password)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@@ -129,6 +129,7 @@ type ApplicationController struct {
|
||||
statusHardRefreshTimeout time.Duration
|
||||
statusRefreshJitter time.Duration
|
||||
selfHealTimeout time.Duration
|
||||
selfHealBackOff *wait.Backoff
|
||||
repoClientset apiclient.Clientset
|
||||
db db.ArgoDB
|
||||
settingsMgr *settings_util.SettingsManager
|
||||
@@ -159,6 +160,7 @@ func NewApplicationController(
|
||||
appHardResyncPeriod time.Duration,
|
||||
appResyncJitter time.Duration,
|
||||
selfHealTimeout time.Duration,
|
||||
selfHealBackoff *wait.Backoff,
|
||||
repoErrorGracePeriod time.Duration,
|
||||
metricsPort int,
|
||||
metricsCacheExpiration time.Duration,
|
||||
@@ -198,6 +200,7 @@ func NewApplicationController(
|
||||
auditLogger: argo.NewAuditLogger(namespace, kubeClientset, common.ApplicationController),
|
||||
settingsMgr: settingsMgr,
|
||||
selfHealTimeout: selfHealTimeout,
|
||||
selfHealBackOff: selfHealBackoff,
|
||||
clusterSharding: clusterSharding,
|
||||
projByNameCache: sync.Map{},
|
||||
applicationNamespaces: applicationNamespaces,
|
||||
@@ -1609,7 +1612,8 @@ func (ctrl *ApplicationController) processAppRefreshQueueItem() (processNext boo
|
||||
app.Status.Summary = tree.GetSummary(app)
|
||||
}
|
||||
|
||||
if project.Spec.SyncWindows.Matches(app).CanSync(false) {
|
||||
canSync, _ := project.Spec.SyncWindows.Matches(app).CanSync(false)
|
||||
if canSync {
|
||||
syncErrCond, opMS := ctrl.autoSync(app, compareResult.syncStatus, compareResult.resources)
|
||||
setOpMs = opMS
|
||||
if syncErrCond != nil {
|
||||
@@ -1767,6 +1771,22 @@ func (ctrl *ApplicationController) normalizeApplication(orig, app *appv1.Applica
|
||||
}
|
||||
}
|
||||
|
||||
func createMergePatch(orig, new interface{}) ([]byte, bool, error) {
|
||||
origBytes, err := json.Marshal(orig)
|
||||
if err != nil {
|
||||
return nil, false, err
|
||||
}
|
||||
newBytes, err := json.Marshal(new)
|
||||
if err != nil {
|
||||
return nil, false, err
|
||||
}
|
||||
patch, err := jsonpatch.CreateMergePatch(origBytes, newBytes)
|
||||
if err != nil {
|
||||
return nil, false, err
|
||||
}
|
||||
return patch, string(patch) != "{}", nil
|
||||
}
|
||||
|
||||
// persistAppStatus persists updates to application status. If no changes were made, it is a no-op
|
||||
func (ctrl *ApplicationController) persistAppStatus(orig *appv1.Application, newStatus *appv1.ApplicationStatus) (patchMs time.Duration) {
|
||||
logCtx := getAppLog(orig)
|
||||
@@ -1786,9 +1806,9 @@ func (ctrl *ApplicationController) persistAppStatus(orig *appv1.Application, new
|
||||
}
|
||||
delete(newAnnotations, appv1.AnnotationKeyRefresh)
|
||||
}
|
||||
patch, modified, err := diff.CreateTwoWayMergePatch(
|
||||
patch, modified, err := createMergePatch(
|
||||
&appv1.Application{ObjectMeta: metav1.ObjectMeta{Annotations: orig.GetAnnotations()}, Status: orig.Status},
|
||||
&appv1.Application{ObjectMeta: metav1.ObjectMeta{Annotations: newAnnotations}, Status: *newStatus}, appv1.Application{})
|
||||
&appv1.Application{ObjectMeta: metav1.ObjectMeta{Annotations: newAnnotations}, Status: *newStatus})
|
||||
if err != nil {
|
||||
logCtx.Errorf("Error constructing app status patch: %v", err)
|
||||
return
|
||||
@@ -1862,6 +1882,9 @@ func (ctrl *ApplicationController) autoSync(app *appv1.Application, syncStatus *
|
||||
InitiatedBy: appv1.OperationInitiator{Automated: true},
|
||||
Retry: appv1.RetryStrategy{Limit: 5},
|
||||
}
|
||||
if app.Status.OperationState != nil && app.Status.OperationState.Operation.Sync != nil {
|
||||
op.Sync.SelfHealAttemptsCount = app.Status.OperationState.Operation.Sync.SelfHealAttemptsCount
|
||||
}
|
||||
if app.Spec.SyncPolicy.Retry != nil {
|
||||
op.Retry = *app.Spec.SyncPolicy.Retry
|
||||
}
|
||||
@@ -1879,6 +1902,7 @@ func (ctrl *ApplicationController) autoSync(app *appv1.Application, syncStatus *
|
||||
return nil, 0
|
||||
} else if alreadyAttempted && selfHeal {
|
||||
if shouldSelfHeal, retryAfter := ctrl.shouldSelfHeal(app); shouldSelfHeal {
|
||||
op.Sync.SelfHealAttemptsCount++
|
||||
for _, resource := range resources {
|
||||
if resource.Status != appv1.SyncStatusCodeSynced {
|
||||
op.Sync.Resources = append(op.Sync.Resources, appv1.SyncOperationResource{
|
||||
@@ -1984,10 +2008,24 @@ func (ctrl *ApplicationController) shouldSelfHeal(app *appv1.Application) (bool,
|
||||
}
|
||||
|
||||
var retryAfter time.Duration
|
||||
if app.Status.OperationState.FinishedAt == nil {
|
||||
retryAfter = ctrl.selfHealTimeout
|
||||
if ctrl.selfHealBackOff == nil {
|
||||
if app.Status.OperationState.FinishedAt == nil {
|
||||
retryAfter = ctrl.selfHealTimeout
|
||||
} else {
|
||||
retryAfter = ctrl.selfHealTimeout - time.Since(app.Status.OperationState.FinishedAt.Time)
|
||||
}
|
||||
} else {
|
||||
retryAfter = ctrl.selfHealTimeout - time.Since(app.Status.OperationState.FinishedAt.Time)
|
||||
backOff := *ctrl.selfHealBackOff
|
||||
backOff.Steps = int(app.Status.OperationState.Operation.Sync.SelfHealAttemptsCount)
|
||||
var delay time.Duration
|
||||
for backOff.Steps > 0 {
|
||||
delay = backOff.Step()
|
||||
}
|
||||
if app.Status.OperationState.FinishedAt == nil {
|
||||
retryAfter = delay
|
||||
} else {
|
||||
retryAfter = delay - time.Since(app.Status.OperationState.FinishedAt.Time)
|
||||
}
|
||||
}
|
||||
return retryAfter <= 0, retryAfter
|
||||
}
|
||||
@@ -1995,7 +2033,7 @@ func (ctrl *ApplicationController) shouldSelfHeal(app *appv1.Application) (bool,
|
||||
// isAppNamespaceAllowed returns whether the application is allowed in the
|
||||
// namespace it's residing in.
|
||||
func (ctrl *ApplicationController) isAppNamespaceAllowed(app *appv1.Application) bool {
|
||||
return app.Namespace == ctrl.namespace || glob.MatchStringInList(ctrl.applicationNamespaces, app.Namespace, false)
|
||||
return app.Namespace == ctrl.namespace || glob.MatchStringInList(ctrl.applicationNamespaces, app.Namespace, glob.REGEXP)
|
||||
}
|
||||
|
||||
func (ctrl *ApplicationController) canProcessApp(obj interface{}) bool {
|
||||
|
||||
@@ -4,16 +4,18 @@ import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
clustercache "github.com/argoproj/gitops-engine/pkg/cache"
|
||||
"github.com/argoproj/gitops-engine/pkg/utils/kube/kubetest"
|
||||
"github.com/sirupsen/logrus"
|
||||
"github.com/stretchr/testify/require"
|
||||
"k8s.io/apimachinery/pkg/api/resource"
|
||||
"k8s.io/apimachinery/pkg/util/wait"
|
||||
"k8s.io/client-go/rest"
|
||||
|
||||
clustercache "github.com/argoproj/gitops-engine/pkg/cache"
|
||||
"k8s.io/utils/ptr"
|
||||
|
||||
"github.com/argoproj/argo-cd/v2/common"
|
||||
statecache "github.com/argoproj/argo-cd/v2/controller/cache"
|
||||
@@ -151,6 +153,7 @@ func newFakeController(data *fakeData, repoErr error) *ApplicationController {
|
||||
time.Hour,
|
||||
time.Second,
|
||||
time.Minute,
|
||||
nil,
|
||||
time.Second*10,
|
||||
common.DefaultPortArgoCDMetrics,
|
||||
data.metricsCacheExpiration,
|
||||
@@ -374,8 +377,8 @@ data:
|
||||
|
||||
var fakePostDeleteHook = `
|
||||
{
|
||||
"apiVersion": "v1",
|
||||
"kind": "Pod",
|
||||
"apiVersion": "batch/v1",
|
||||
"kind": "Job",
|
||||
"metadata": {
|
||||
"name": "post-delete-hook",
|
||||
"namespace": "default",
|
||||
@@ -388,22 +391,93 @@ var fakePostDeleteHook = `
|
||||
}
|
||||
},
|
||||
"spec": {
|
||||
"containers": [
|
||||
{
|
||||
"name": "post-delete-hook",
|
||||
"image": "busybox",
|
||||
"restartPolicy": "Never",
|
||||
"command": [
|
||||
"/bin/sh",
|
||||
"-c",
|
||||
"sleep 5 && echo hello from the post-delete-hook pod"
|
||||
]
|
||||
"template": {
|
||||
"metadata": {
|
||||
"name": "post-delete-hook"
|
||||
},
|
||||
"spec": {
|
||||
"containers": [
|
||||
{
|
||||
"name": "post-delete-hook",
|
||||
"image": "busybox",
|
||||
"command": [
|
||||
"/bin/sh",
|
||||
"-c",
|
||||
"sleep 5 && echo hello from the post-delete-hook job"
|
||||
]
|
||||
}
|
||||
],
|
||||
"restartPolicy": "Never"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
`
|
||||
|
||||
var fakeServiceAccount = `
|
||||
{
|
||||
"apiVersion": "v1",
|
||||
"kind": "ServiceAccount",
|
||||
"metadata": {
|
||||
"name": "hook-serviceaccount",
|
||||
"namespace": "default",
|
||||
"annotations": {
|
||||
"argocd.argoproj.io/hook": "PostDelete",
|
||||
"argocd.argoproj.io/hook-delete-policy": "BeforeHookCreation,HookSucceeded"
|
||||
}
|
||||
}
|
||||
}
|
||||
`
|
||||
|
||||
var fakeRole = `
|
||||
{
|
||||
"apiVersion": "rbac.authorization.k8s.io/v1",
|
||||
"kind": "Role",
|
||||
"metadata": {
|
||||
"name": "hook-role",
|
||||
"namespace": "default",
|
||||
"annotations": {
|
||||
"argocd.argoproj.io/hook": "PostDelete",
|
||||
"argocd.argoproj.io/hook-delete-policy": "BeforeHookCreation,HookSucceeded"
|
||||
}
|
||||
},
|
||||
"rules": [
|
||||
{
|
||||
"apiGroups": [""],
|
||||
"resources": ["secrets"],
|
||||
"verbs": ["get", "delete", "list"]
|
||||
}
|
||||
]
|
||||
}
|
||||
`
|
||||
|
||||
var fakeRoleBinding = `
|
||||
{
|
||||
"apiVersion": "rbac.authorization.k8s.io/v1",
|
||||
"kind": "RoleBinding",
|
||||
"metadata": {
|
||||
"name": "hook-rolebinding",
|
||||
"namespace": "default",
|
||||
"annotations": {
|
||||
"argocd.argoproj.io/hook": "PostDelete",
|
||||
"argocd.argoproj.io/hook-delete-policy": "BeforeHookCreation,HookSucceeded"
|
||||
}
|
||||
},
|
||||
"roleRef": {
|
||||
"apiGroup": "rbac.authorization.k8s.io",
|
||||
"kind": "Role",
|
||||
"name": "hook-role"
|
||||
},
|
||||
"subjects": [
|
||||
{
|
||||
"kind": "ServiceAccount",
|
||||
"name": "hook-serviceaccount",
|
||||
"namespace": "default"
|
||||
}
|
||||
]
|
||||
}
|
||||
`
|
||||
|
||||
func newFakeApp() *v1alpha1.Application {
|
||||
return createFakeApp(fakeApp)
|
||||
}
|
||||
@@ -439,12 +513,39 @@ func newFakeCM() map[string]interface{} {
|
||||
}
|
||||
|
||||
func newFakePostDeleteHook() map[string]interface{} {
|
||||
var cm map[string]interface{}
|
||||
err := yaml.Unmarshal([]byte(fakePostDeleteHook), &cm)
|
||||
var hook map[string]interface{}
|
||||
err := yaml.Unmarshal([]byte(fakePostDeleteHook), &hook)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
return cm
|
||||
return hook
|
||||
}
|
||||
|
||||
func newFakeRoleBinding() map[string]interface{} {
|
||||
var roleBinding map[string]interface{}
|
||||
err := yaml.Unmarshal([]byte(fakeRoleBinding), &roleBinding)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
return roleBinding
|
||||
}
|
||||
|
||||
func newFakeRole() map[string]interface{} {
|
||||
var role map[string]interface{}
|
||||
err := yaml.Unmarshal([]byte(fakeRole), &role)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
return role
|
||||
}
|
||||
|
||||
func newFakeServiceAccount() map[string]interface{} {
|
||||
var serviceAccount map[string]interface{}
|
||||
err := yaml.Unmarshal([]byte(fakeServiceAccount), &serviceAccount)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
return serviceAccount
|
||||
}
|
||||
|
||||
func TestAutoSync(t *testing.T) {
|
||||
@@ -721,7 +822,7 @@ func TestFinalizeAppDeletion(t *testing.T) {
|
||||
|
||||
// Ensure any stray resources irregularly labeled with instance label of app are not deleted upon deleting,
|
||||
// when app project restriction is in place
|
||||
t.Run("ProjectRestrictionEnforced", func(*testing.T) {
|
||||
t.Run("ProjectRestrictionEnforced", func(t *testing.T) {
|
||||
restrictedProj := v1alpha1.AppProject{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "restricted",
|
||||
@@ -882,7 +983,13 @@ func TestFinalizeAppDeletion(t *testing.T) {
|
||||
app.SetPostDeleteFinalizer()
|
||||
app.Spec.Destination.Namespace = test.FakeArgoCDNamespace
|
||||
liveHook := &unstructured.Unstructured{Object: newFakePostDeleteHook()}
|
||||
require.NoError(t, unstructured.SetNestedField(liveHook.Object, "Succeeded", "status", "phase"))
|
||||
conditions := []interface{}{
|
||||
map[string]interface{}{
|
||||
"type": "Complete",
|
||||
"status": "True",
|
||||
},
|
||||
}
|
||||
require.NoError(t, unstructured.SetNestedField(liveHook.Object, conditions, "status", "conditions"))
|
||||
ctrl := newFakeController(&fakeData{
|
||||
manifestResponses: []*apiclient.ManifestResponse{{
|
||||
Manifests: []string{fakePostDeleteHook},
|
||||
@@ -916,15 +1023,27 @@ func TestFinalizeAppDeletion(t *testing.T) {
|
||||
app := newFakeApp()
|
||||
app.SetPostDeleteFinalizer("cleanup")
|
||||
app.Spec.Destination.Namespace = test.FakeArgoCDNamespace
|
||||
liveRoleBinding := &unstructured.Unstructured{Object: newFakeRoleBinding()}
|
||||
liveRole := &unstructured.Unstructured{Object: newFakeRole()}
|
||||
liveServiceAccount := &unstructured.Unstructured{Object: newFakeServiceAccount()}
|
||||
liveHook := &unstructured.Unstructured{Object: newFakePostDeleteHook()}
|
||||
require.NoError(t, unstructured.SetNestedField(liveHook.Object, "Succeeded", "status", "phase"))
|
||||
conditions := []interface{}{
|
||||
map[string]interface{}{
|
||||
"type": "Complete",
|
||||
"status": "True",
|
||||
},
|
||||
}
|
||||
require.NoError(t, unstructured.SetNestedField(liveHook.Object, conditions, "status", "conditions"))
|
||||
ctrl := newFakeController(&fakeData{
|
||||
manifestResponses: []*apiclient.ManifestResponse{{
|
||||
Manifests: []string{fakePostDeleteHook},
|
||||
Manifests: []string{fakeRoleBinding, fakeRole, fakeServiceAccount, fakePostDeleteHook},
|
||||
}},
|
||||
apps: []runtime.Object{app, &defaultProj},
|
||||
managedLiveObjs: map[kube.ResourceKey]*unstructured.Unstructured{
|
||||
kube.GetResourceKey(liveHook): liveHook,
|
||||
kube.GetResourceKey(liveRoleBinding): liveRoleBinding,
|
||||
kube.GetResourceKey(liveRole): liveRole,
|
||||
kube.GetResourceKey(liveServiceAccount): liveServiceAccount,
|
||||
kube.GetResourceKey(liveHook): liveHook,
|
||||
},
|
||||
}, nil)
|
||||
|
||||
@@ -943,9 +1062,14 @@ func TestFinalizeAppDeletion(t *testing.T) {
|
||||
return []*v1alpha1.Cluster{}, nil
|
||||
})
|
||||
require.NoError(t, err)
|
||||
// post-delete hook is deleted
|
||||
require.Len(t, ctrl.kubectl.(*MockKubectl).DeletedResources, 1)
|
||||
require.Equal(t, "post-delete-hook", ctrl.kubectl.(*MockKubectl).DeletedResources[0].Name)
|
||||
// post-delete hooks are deleted
|
||||
require.Len(t, ctrl.kubectl.(*MockKubectl).DeletedResources, 4)
|
||||
deletedResources := []string{}
|
||||
for _, res := range ctrl.kubectl.(*MockKubectl).DeletedResources {
|
||||
deletedResources = append(deletedResources, res.Name)
|
||||
}
|
||||
expectedNames := []string{"hook-rolebinding", "hook-role", "hook-serviceaccount", "post-delete-hook"}
|
||||
require.ElementsMatch(t, expectedNames, deletedResources, "Deleted resources should match expected names")
|
||||
// finalizer is not removed
|
||||
assert.False(t, patched)
|
||||
})
|
||||
@@ -992,7 +1116,7 @@ func TestNormalizeApplication(t *testing.T) {
|
||||
normalized := false
|
||||
fakeAppCs.AddReactor("patch", "*", func(action kubetesting.Action) (handled bool, ret runtime.Object, err error) {
|
||||
if patchAction, ok := action.(kubetesting.PatchAction); ok {
|
||||
if string(patchAction.GetPatch()) == `{"spec":{"project":"default"},"status":{"sync":{"comparedTo":{"destination":{},"source":{"repoURL":""}}}}}` {
|
||||
if string(patchAction.GetPatch()) == `{"spec":{"project":"default"}}` {
|
||||
normalized = true
|
||||
}
|
||||
}
|
||||
@@ -1949,3 +2073,128 @@ func TestAddControllerNamespace(t *testing.T) {
|
||||
assert.Equal(t, test.FakeArgoCDNamespace, updatedApp.Status.ControllerNamespace)
|
||||
})
|
||||
}
|
||||
|
||||
func TestHelmValuesObjectHasReplaceStrategy(t *testing.T) {
|
||||
app := v1alpha1.Application{
|
||||
Status: v1alpha1.ApplicationStatus{Sync: v1alpha1.SyncStatus{ComparedTo: v1alpha1.ComparedTo{
|
||||
Source: v1alpha1.ApplicationSource{
|
||||
Helm: &v1alpha1.ApplicationSourceHelm{
|
||||
ValuesObject: &runtime.RawExtension{
|
||||
Object: &unstructured.Unstructured{Object: map[string]interface{}{"key": []string{"value"}}},
|
||||
},
|
||||
},
|
||||
},
|
||||
}}},
|
||||
}
|
||||
|
||||
appModified := v1alpha1.Application{
|
||||
Status: v1alpha1.ApplicationStatus{Sync: v1alpha1.SyncStatus{ComparedTo: v1alpha1.ComparedTo{
|
||||
Source: v1alpha1.ApplicationSource{
|
||||
Helm: &v1alpha1.ApplicationSourceHelm{
|
||||
ValuesObject: &runtime.RawExtension{
|
||||
Object: &unstructured.Unstructured{Object: map[string]interface{}{"key": []string{"value-modified1"}}},
|
||||
},
|
||||
},
|
||||
},
|
||||
}}},
|
||||
}
|
||||
|
||||
patch, _, err := createMergePatch(
|
||||
app,
|
||||
appModified)
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, `{"status":{"sync":{"comparedTo":{"source":{"helm":{"valuesObject":{"key":["value-modified1"]}}}}}}}`, string(patch))
|
||||
}
|
||||
|
||||
func TestAppStatusIsReplaced(t *testing.T) {
|
||||
original := &v1alpha1.ApplicationStatus{Sync: v1alpha1.SyncStatus{
|
||||
ComparedTo: v1alpha1.ComparedTo{
|
||||
Destination: v1alpha1.ApplicationDestination{
|
||||
Server: "https://mycluster",
|
||||
},
|
||||
},
|
||||
}}
|
||||
|
||||
updated := &v1alpha1.ApplicationStatus{Sync: v1alpha1.SyncStatus{
|
||||
ComparedTo: v1alpha1.ComparedTo{
|
||||
Destination: v1alpha1.ApplicationDestination{
|
||||
Name: "mycluster",
|
||||
},
|
||||
},
|
||||
}}
|
||||
|
||||
patchData, ok, err := createMergePatch(original, updated)
|
||||
|
||||
require.NoError(t, err)
|
||||
require.True(t, ok)
|
||||
patchObj := map[string]interface{}{}
|
||||
require.NoError(t, json.Unmarshal(patchData, &patchObj))
|
||||
|
||||
val, has, err := unstructured.NestedFieldNoCopy(patchObj, "sync", "comparedTo", "destination", "server")
|
||||
require.NoError(t, err)
|
||||
require.True(t, has)
|
||||
require.Nil(t, val)
|
||||
}
|
||||
|
||||
func assertDurationAround(t *testing.T, expected time.Duration, actual time.Duration) {
|
||||
delta := time.Second / 2
|
||||
assert.GreaterOrEqual(t, expected, actual-delta)
|
||||
assert.LessOrEqual(t, expected, actual+delta)
|
||||
}
|
||||
|
||||
func TestSelfHealExponentialBackoff(t *testing.T) {
|
||||
ctrl := newFakeController(&fakeData{}, nil)
|
||||
ctrl.selfHealBackOff = &wait.Backoff{
|
||||
Factor: 3,
|
||||
Duration: 2 * time.Second,
|
||||
Cap: 5 * time.Minute,
|
||||
}
|
||||
|
||||
app := &v1alpha1.Application{
|
||||
Status: v1alpha1.ApplicationStatus{
|
||||
OperationState: &v1alpha1.OperationState{
|
||||
Operation: v1alpha1.Operation{
|
||||
Sync: &v1alpha1.SyncOperation{},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
testCases := []struct {
|
||||
attempts int64
|
||||
finishedAt *metav1.Time
|
||||
expectedDuration time.Duration
|
||||
shouldSelfHeal bool
|
||||
}{{
|
||||
attempts: 0,
|
||||
finishedAt: ptr.To(metav1.Now()),
|
||||
expectedDuration: 0,
|
||||
shouldSelfHeal: true,
|
||||
}, {
|
||||
attempts: 1,
|
||||
finishedAt: ptr.To(metav1.Now()),
|
||||
expectedDuration: 2 * time.Second,
|
||||
shouldSelfHeal: false,
|
||||
}, {
|
||||
attempts: 2,
|
||||
finishedAt: ptr.To(metav1.Now()),
|
||||
expectedDuration: 6 * time.Second,
|
||||
shouldSelfHeal: false,
|
||||
}, {
|
||||
attempts: 3,
|
||||
finishedAt: nil,
|
||||
expectedDuration: 18 * time.Second,
|
||||
shouldSelfHeal: false,
|
||||
}}
|
||||
|
||||
for i := range testCases {
|
||||
tc := testCases[i]
|
||||
t.Run(fmt.Sprintf("test case %d", i), func(t *testing.T) {
|
||||
app.Status.OperationState.Operation.Sync.SelfHealAttemptsCount = tc.attempts
|
||||
app.Status.OperationState.FinishedAt = tc.finishedAt
|
||||
ok, duration := ctrl.shouldSelfHeal(app)
|
||||
require.Equal(t, ok, tc.shouldSelfHeal)
|
||||
assertDurationAround(t, tc.expectedDuration, duration)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
9
controller/cache/cache.go
vendored
9
controller/cache/cache.go
vendored
@@ -192,6 +192,7 @@ type cacheSettings struct {
|
||||
clusterSettings clustercache.Settings
|
||||
appInstanceLabelKey string
|
||||
trackingMethod appv1.TrackingMethod
|
||||
installationID string
|
||||
// resourceOverrides provides a list of ignored differences to ignore watched resource updates
|
||||
resourceOverrides map[string]appv1.ResourceOverride
|
||||
|
||||
@@ -220,6 +221,10 @@ func (c *liveStateCache) loadCacheSettings() (*cacheSettings, error) {
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
installationID, err := c.settingsMgr.GetInstallationID()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
resourceUpdatesOverrides, err := c.settingsMgr.GetIgnoreResourceUpdatesOverrides()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
@@ -241,7 +246,7 @@ func (c *liveStateCache) loadCacheSettings() (*cacheSettings, error) {
|
||||
ResourcesFilter: resourcesFilter,
|
||||
}
|
||||
|
||||
return &cacheSettings{clusterSettings, appInstanceLabelKey, argo.GetTrackingMethod(c.settingsMgr), resourceUpdatesOverrides, ignoreResourceUpdatesEnabled}, nil
|
||||
return &cacheSettings{clusterSettings, appInstanceLabelKey, argo.GetTrackingMethod(c.settingsMgr), installationID, resourceUpdatesOverrides, ignoreResourceUpdatesEnabled}, nil
|
||||
}
|
||||
|
||||
func asResourceNode(r *clustercache.Resource) appv1.ResourceNode {
|
||||
@@ -501,7 +506,7 @@ func (c *liveStateCache) getCluster(server string) (clustercache.ClusterCache, e
|
||||
|
||||
res.Health, _ = health.GetResourceHealth(un, cacheSettings.clusterSettings.ResourceHealthOverride)
|
||||
|
||||
appName := c.resourceTracking.GetAppName(un, cacheSettings.appInstanceLabelKey, cacheSettings.trackingMethod)
|
||||
appName := c.resourceTracking.GetAppName(un, cacheSettings.appInstanceLabelKey, cacheSettings.trackingMethod, cacheSettings.installationID)
|
||||
if isRoot && appName != "" {
|
||||
res.AppName = appName
|
||||
}
|
||||
|
||||
57
controller/cache/info.go
vendored
57
controller/cache/info.go
vendored
@@ -278,6 +278,32 @@ func populateIstioVirtualServiceInfo(un *unstructured.Unstructured, res *Resourc
|
||||
res.NetworkingInfo = &v1alpha1.ResourceNetworkingInfo{TargetRefs: targets, ExternalURLs: urls}
|
||||
}
|
||||
|
||||
func isPodInitializedConditionTrue(status *v1.PodStatus) bool {
|
||||
for _, condition := range status.Conditions {
|
||||
if condition.Type != v1.PodInitialized {
|
||||
continue
|
||||
}
|
||||
|
||||
return condition.Status == v1.ConditionTrue
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func isRestartableInitContainer(initContainer *v1.Container) bool {
|
||||
if initContainer == nil {
|
||||
return false
|
||||
}
|
||||
if initContainer.RestartPolicy == nil {
|
||||
return false
|
||||
}
|
||||
|
||||
return *initContainer.RestartPolicy == v1.ContainerRestartPolicyAlways
|
||||
}
|
||||
|
||||
func isPodPhaseTerminal(phase v1.PodPhase) bool {
|
||||
return phase == v1.PodFailed || phase == v1.PodSucceeded
|
||||
}
|
||||
|
||||
func populatePodInfo(un *unstructured.Unstructured, res *ResourceInfo) {
|
||||
pod := v1.Pod{}
|
||||
err := runtime.DefaultUnstructuredConverter.FromUnstructured(un.Object, &pod)
|
||||
@@ -288,7 +314,8 @@ func populatePodInfo(un *unstructured.Unstructured, res *ResourceInfo) {
|
||||
totalContainers := len(pod.Spec.Containers)
|
||||
readyContainers := 0
|
||||
|
||||
reason := string(pod.Status.Phase)
|
||||
podPhase := pod.Status.Phase
|
||||
reason := string(podPhase)
|
||||
if pod.Status.Reason != "" {
|
||||
reason = pod.Status.Reason
|
||||
}
|
||||
@@ -306,6 +333,21 @@ func populatePodInfo(un *unstructured.Unstructured, res *ResourceInfo) {
|
||||
res.Images = append(res.Images, image)
|
||||
}
|
||||
|
||||
// If the Pod carries {type:PodScheduled, reason:SchedulingGated}, set reason to 'SchedulingGated'.
|
||||
for _, condition := range pod.Status.Conditions {
|
||||
if condition.Type == v1.PodScheduled && condition.Reason == v1.PodReasonSchedulingGated {
|
||||
reason = v1.PodReasonSchedulingGated
|
||||
}
|
||||
}
|
||||
|
||||
initContainers := make(map[string]*v1.Container)
|
||||
for i := range pod.Spec.InitContainers {
|
||||
initContainers[pod.Spec.InitContainers[i].Name] = &pod.Spec.InitContainers[i]
|
||||
if isRestartableInitContainer(&pod.Spec.InitContainers[i]) {
|
||||
totalContainers++
|
||||
}
|
||||
}
|
||||
|
||||
initializing := false
|
||||
for i := range pod.Status.InitContainerStatuses {
|
||||
container := pod.Status.InitContainerStatuses[i]
|
||||
@@ -313,6 +355,12 @@ func populatePodInfo(un *unstructured.Unstructured, res *ResourceInfo) {
|
||||
switch {
|
||||
case container.State.Terminated != nil && container.State.Terminated.ExitCode == 0:
|
||||
continue
|
||||
case isRestartableInitContainer(initContainers[container.Name]) &&
|
||||
container.Started != nil && *container.Started:
|
||||
if container.Ready {
|
||||
readyContainers++
|
||||
}
|
||||
continue
|
||||
case container.State.Terminated != nil:
|
||||
// initialization is failed
|
||||
if len(container.State.Terminated.Reason) == 0 {
|
||||
@@ -334,8 +382,7 @@ func populatePodInfo(un *unstructured.Unstructured, res *ResourceInfo) {
|
||||
}
|
||||
break
|
||||
}
|
||||
if !initializing {
|
||||
restarts = 0
|
||||
if !initializing || isPodInitializedConditionTrue(&pod.Status) {
|
||||
hasRunning := false
|
||||
for i := len(pod.Status.ContainerStatuses) - 1; i >= 0; i-- {
|
||||
container := pod.Status.ContainerStatuses[i]
|
||||
@@ -370,7 +417,9 @@ func populatePodInfo(un *unstructured.Unstructured, res *ResourceInfo) {
|
||||
// and https://github.com/kubernetes/kubernetes/issues/90358#issuecomment-617859364
|
||||
if pod.DeletionTimestamp != nil && pod.Status.Reason == "NodeLost" {
|
||||
reason = "Unknown"
|
||||
} else if pod.DeletionTimestamp != nil {
|
||||
// If the pod is being deleted and the pod phase is not succeeded or failed, set the reason to "Terminating".
|
||||
// See https://github.com/kubernetes/kubectl/issues/1595#issuecomment-2080001023
|
||||
} else if pod.DeletionTimestamp != nil && !isPodPhaseTerminal(podPhase) {
|
||||
reason = "Terminating"
|
||||
}
|
||||
|
||||
|
||||
546
controller/cache/info_test.go
vendored
546
controller/cache/info_test.go
vendored
@@ -285,6 +285,552 @@ func TestGetPodInfo(t *testing.T) {
|
||||
assert.Equal(t, &v1alpha1.ResourceNetworkingInfo{Labels: map[string]string{"app": "guestbook"}}, info.NetworkingInfo)
|
||||
}
|
||||
|
||||
func TestGetPodWithInitialContainerInfo(t *testing.T) {
|
||||
pod := strToUnstructured(`
|
||||
apiVersion: "v1"
|
||||
kind: "Pod"
|
||||
metadata:
|
||||
labels:
|
||||
app: "app-with-initial-container"
|
||||
name: "app-with-initial-container-5f46976fdb-vd6rv"
|
||||
namespace: "default"
|
||||
ownerReferences:
|
||||
- apiVersion: "apps/v1"
|
||||
kind: "ReplicaSet"
|
||||
name: "app-with-initial-container-5f46976fdb"
|
||||
spec:
|
||||
containers:
|
||||
- image: "alpine:latest"
|
||||
imagePullPolicy: "Always"
|
||||
name: "app-with-initial-container"
|
||||
initContainers:
|
||||
- image: "alpine:latest"
|
||||
imagePullPolicy: "Always"
|
||||
name: "app-with-initial-container-logshipper"
|
||||
nodeName: "minikube"
|
||||
status:
|
||||
containerStatuses:
|
||||
- image: "alpine:latest"
|
||||
name: "app-with-initial-container"
|
||||
ready: true
|
||||
restartCount: 0
|
||||
started: true
|
||||
state:
|
||||
running:
|
||||
startedAt: "2024-10-08T08:44:25Z"
|
||||
initContainerStatuses:
|
||||
- image: "alpine:latest"
|
||||
name: "app-with-initial-container-logshipper"
|
||||
ready: true
|
||||
restartCount: 0
|
||||
started: false
|
||||
state:
|
||||
terminated:
|
||||
exitCode: 0
|
||||
reason: "Completed"
|
||||
phase: "Running"
|
||||
`)
|
||||
|
||||
info := &ResourceInfo{}
|
||||
populateNodeInfo(pod, info, []string{})
|
||||
assert.Equal(t, []v1alpha1.InfoItem{
|
||||
{Name: "Status Reason", Value: "Running"},
|
||||
{Name: "Node", Value: "minikube"},
|
||||
{Name: "Containers", Value: "1/1"},
|
||||
}, info.Info)
|
||||
}
|
||||
|
||||
func TestGetPodInfoWithSidecar(t *testing.T) {
|
||||
pod := strToUnstructured(`
|
||||
apiVersion: v1
|
||||
kind: Pod
|
||||
metadata:
|
||||
labels:
|
||||
app: app-with-sidecar
|
||||
name: app-with-sidecar-6664cc788c-lqlrp
|
||||
namespace: default
|
||||
ownerReferences:
|
||||
- apiVersion: apps/v1
|
||||
kind: ReplicaSet
|
||||
name: app-with-sidecar-6664cc788c
|
||||
spec:
|
||||
containers:
|
||||
- image: 'docker.m.daocloud.io/library/alpine:latest'
|
||||
imagePullPolicy: Always
|
||||
name: app-with-sidecar
|
||||
initContainers:
|
||||
- image: 'docker.m.daocloud.io/library/alpine:latest'
|
||||
imagePullPolicy: Always
|
||||
name: logshipper
|
||||
restartPolicy: Always
|
||||
nodeName: minikube
|
||||
status:
|
||||
containerStatuses:
|
||||
- image: 'docker.m.daocloud.io/library/alpine:latest'
|
||||
name: app-with-sidecar
|
||||
ready: true
|
||||
restartCount: 0
|
||||
started: true
|
||||
state:
|
||||
running:
|
||||
startedAt: '2024-10-08T08:39:43Z'
|
||||
initContainerStatuses:
|
||||
- image: 'docker.m.daocloud.io/library/alpine:latest'
|
||||
name: logshipper
|
||||
ready: true
|
||||
restartCount: 0
|
||||
started: true
|
||||
state:
|
||||
running:
|
||||
startedAt: '2024-10-08T08:39:40Z'
|
||||
phase: Running
|
||||
`)
|
||||
|
||||
info := &ResourceInfo{}
|
||||
populateNodeInfo(pod, info, []string{})
|
||||
assert.Equal(t, []v1alpha1.InfoItem{
|
||||
{Name: "Status Reason", Value: "Running"},
|
||||
{Name: "Node", Value: "minikube"},
|
||||
{Name: "Containers", Value: "2/2"},
|
||||
}, info.Info)
|
||||
}
|
||||
|
||||
func TestGetPodInfoWithInitialContainer(t *testing.T) {
|
||||
pod := strToUnstructured(`
|
||||
apiVersion: v1
|
||||
kind: Pod
|
||||
metadata:
|
||||
generateName: myapp-long-exist-56b7d8794d-
|
||||
labels:
|
||||
app: myapp-long-exist
|
||||
name: myapp-long-exist-56b7d8794d-pbgrd
|
||||
namespace: linghao
|
||||
ownerReferences:
|
||||
- apiVersion: apps/v1
|
||||
kind: ReplicaSet
|
||||
name: myapp-long-exist-56b7d8794d
|
||||
spec:
|
||||
containers:
|
||||
- image: alpine:latest
|
||||
imagePullPolicy: Always
|
||||
name: myapp-long-exist
|
||||
initContainers:
|
||||
- image: alpine:latest
|
||||
imagePullPolicy: Always
|
||||
name: myapp-long-exist-logshipper
|
||||
nodeName: minikube
|
||||
status:
|
||||
containerStatuses:
|
||||
- image: alpine:latest
|
||||
name: myapp-long-exist
|
||||
ready: false
|
||||
restartCount: 0
|
||||
started: false
|
||||
state:
|
||||
waiting:
|
||||
reason: PodInitializing
|
||||
initContainerStatuses:
|
||||
- image: alpine:latest
|
||||
name: myapp-long-exist-logshipper
|
||||
ready: false
|
||||
restartCount: 0
|
||||
started: true
|
||||
state:
|
||||
running:
|
||||
startedAt: '2024-10-09T08:03:45Z'
|
||||
phase: Pending
|
||||
startTime: '2024-10-09T08:02:39Z'
|
||||
`)
|
||||
|
||||
info := &ResourceInfo{}
|
||||
populateNodeInfo(pod, info, []string{})
|
||||
assert.Equal(t, []v1alpha1.InfoItem{
|
||||
{Name: "Status Reason", Value: "Init:0/1"},
|
||||
{Name: "Node", Value: "minikube"},
|
||||
{Name: "Containers", Value: "0/1"},
|
||||
}, info.Info)
|
||||
}
|
||||
|
||||
// Test pod has 2 restartable init containers, the first one running but not started.
|
||||
func TestGetPodInfoWithRestartableInitContainer(t *testing.T) {
|
||||
pod := strToUnstructured(`
|
||||
apiVersion: v1
|
||||
kind: Pod
|
||||
metadata:
|
||||
name: test1
|
||||
spec:
|
||||
initContainers:
|
||||
- name: restartable-init-1
|
||||
restartPolicy: Always
|
||||
- name: restartable-init-2
|
||||
restartPolicy: Always
|
||||
containers:
|
||||
- name: container
|
||||
nodeName: minikube
|
||||
status:
|
||||
phase: Pending
|
||||
initContainerStatuses:
|
||||
- name: restartable-init-1
|
||||
ready: false
|
||||
restartCount: 3
|
||||
state:
|
||||
running: {}
|
||||
started: false
|
||||
lastTerminationState:
|
||||
terminated:
|
||||
finishedAt: "2023-10-01T00:00:00Z" # Replace with actual time
|
||||
- name: restartable-init-2
|
||||
ready: false
|
||||
state:
|
||||
waiting: {}
|
||||
started: false
|
||||
containerStatuses:
|
||||
- ready: false
|
||||
restartCount: 0
|
||||
state:
|
||||
waiting: {}
|
||||
conditions:
|
||||
- type: ContainersReady
|
||||
status: "False"
|
||||
- type: Initialized
|
||||
status: "False"
|
||||
`)
|
||||
|
||||
info := &ResourceInfo{}
|
||||
populateNodeInfo(pod, info, []string{})
|
||||
assert.Equal(t, []v1alpha1.InfoItem{
|
||||
{Name: "Status Reason", Value: "Init:0/2"},
|
||||
{Name: "Node", Value: "minikube"},
|
||||
{Name: "Containers", Value: "0/3"},
|
||||
{Name: "Restart Count", Value: "3"},
|
||||
}, info.Info)
|
||||
}
|
||||
|
||||
// Test pod has 2 restartable init containers, the first one started and the second one running but not started.
|
||||
func TestGetPodInfoWithPartiallyStartedInitContainers(t *testing.T) {
|
||||
pod := strToUnstructured(`
|
||||
apiVersion: v1
|
||||
kind: Pod
|
||||
metadata:
|
||||
name: test1
|
||||
spec:
|
||||
initContainers:
|
||||
- name: restartable-init-1
|
||||
restartPolicy: Always
|
||||
- name: restartable-init-2
|
||||
restartPolicy: Always
|
||||
containers:
|
||||
- name: container
|
||||
nodeName: minikube
|
||||
status:
|
||||
phase: Pending
|
||||
initContainerStatuses:
|
||||
- name: restartable-init-1
|
||||
ready: false
|
||||
restartCount: 3
|
||||
state:
|
||||
running: {}
|
||||
started: true
|
||||
lastTerminationState:
|
||||
terminated:
|
||||
finishedAt: "2023-10-01T00:00:00Z" # Replace with actual time
|
||||
- name: restartable-init-2
|
||||
ready: false
|
||||
state:
|
||||
running: {}
|
||||
started: false
|
||||
containerStatuses:
|
||||
- ready: false
|
||||
restartCount: 0
|
||||
state:
|
||||
waiting: {}
|
||||
conditions:
|
||||
- type: ContainersReady
|
||||
status: "False"
|
||||
- type: Initialized
|
||||
status: "False"
|
||||
`)
|
||||
|
||||
info := &ResourceInfo{}
|
||||
populateNodeInfo(pod, info, []string{})
|
||||
assert.Equal(t, []v1alpha1.InfoItem{
|
||||
{Name: "Status Reason", Value: "Init:1/2"},
|
||||
{Name: "Node", Value: "minikube"},
|
||||
{Name: "Containers", Value: "0/3"},
|
||||
{Name: "Restart Count", Value: "3"},
|
||||
}, info.Info)
|
||||
}
|
||||
|
||||
// Test pod has 2 restartable init containers started and 1 container running
|
||||
func TestGetPodInfoWithStartedInitContainers(t *testing.T) {
|
||||
pod := strToUnstructured(`
|
||||
apiVersion: v1
|
||||
kind: Pod
|
||||
metadata:
|
||||
name: test2
|
||||
spec:
|
||||
initContainers:
|
||||
- name: restartable-init-1
|
||||
restartPolicy: Always
|
||||
- name: restartable-init-2
|
||||
restartPolicy: Always
|
||||
containers:
|
||||
- name: container
|
||||
nodeName: minikube
|
||||
status:
|
||||
phase: Running
|
||||
initContainerStatuses:
|
||||
- name: restartable-init-1
|
||||
ready: false
|
||||
restartCount: 3
|
||||
state:
|
||||
running: {}
|
||||
started: true
|
||||
lastTerminationState:
|
||||
terminated:
|
||||
finishedAt: "2023-10-01T00:00:00Z" # Replace with actual time
|
||||
- name: restartable-init-2
|
||||
ready: false
|
||||
state:
|
||||
running: {}
|
||||
started: true
|
||||
containerStatuses:
|
||||
- ready: true
|
||||
restartCount: 4
|
||||
state:
|
||||
running: {}
|
||||
lastTerminationState:
|
||||
terminated:
|
||||
finishedAt: "2023-10-01T00:00:00Z" # Replace with actual time
|
||||
conditions:
|
||||
- type: ContainersReady
|
||||
status: "False"
|
||||
- type: Initialized
|
||||
status: "True"
|
||||
`)
|
||||
|
||||
info := &ResourceInfo{}
|
||||
populateNodeInfo(pod, info, []string{})
|
||||
assert.Equal(t, []v1alpha1.InfoItem{
|
||||
{Name: "Status Reason", Value: "Running"},
|
||||
{Name: "Node", Value: "minikube"},
|
||||
{Name: "Containers", Value: "1/3"},
|
||||
{Name: "Restart Count", Value: "7"},
|
||||
}, info.Info)
|
||||
}
|
||||
|
||||
// Test pod has 1 init container restarting and 1 container not running
|
||||
func TestGetPodInfoWithNormalInitContainer(t *testing.T) {
|
||||
pod := strToUnstructured(`
|
||||
apiVersion: v1
|
||||
kind: Pod
|
||||
metadata:
|
||||
name: test7
|
||||
spec:
|
||||
initContainers:
|
||||
- name: init-container
|
||||
containers:
|
||||
- name: main-container
|
||||
nodeName: minikube
|
||||
status:
|
||||
phase: podPhase
|
||||
initContainerStatuses:
|
||||
- ready: false
|
||||
restartCount: 3
|
||||
state:
|
||||
running: {}
|
||||
lastTerminationState:
|
||||
terminated:
|
||||
finishedAt: "2023-10-01T00:00:00Z" # Replace with the actual time
|
||||
containerStatuses:
|
||||
- ready: false
|
||||
restartCount: 0
|
||||
state:
|
||||
waiting: {}
|
||||
`)
|
||||
|
||||
info := &ResourceInfo{}
|
||||
populateNodeInfo(pod, info, []string{})
|
||||
assert.Equal(t, []v1alpha1.InfoItem{
|
||||
{Name: "Status Reason", Value: "Init:0/1"},
|
||||
{Name: "Node", Value: "minikube"},
|
||||
{Name: "Containers", Value: "0/1"},
|
||||
{Name: "Restart Count", Value: "3"},
|
||||
}, info.Info)
|
||||
}
|
||||
|
||||
// Test pod condition succeed
|
||||
func TestPodConditionSucceeded(t *testing.T) {
|
||||
pod := strToUnstructured(`
|
||||
apiVersion: v1
|
||||
kind: Pod
|
||||
metadata:
|
||||
name: test8
|
||||
spec:
|
||||
nodeName: minikube
|
||||
containers:
|
||||
- name: container
|
||||
status:
|
||||
phase: Succeeded
|
||||
containerStatuses:
|
||||
- ready: false
|
||||
restartCount: 0
|
||||
state:
|
||||
terminated:
|
||||
reason: Completed
|
||||
exitCode: 0
|
||||
`)
|
||||
info := &ResourceInfo{}
|
||||
populateNodeInfo(pod, info, []string{})
|
||||
assert.Equal(t, []v1alpha1.InfoItem{
|
||||
{Name: "Status Reason", Value: "Completed"},
|
||||
{Name: "Node", Value: "minikube"},
|
||||
{Name: "Containers", Value: "0/1"},
|
||||
}, info.Info)
|
||||
}
|
||||
|
||||
// Test pod condition failed
|
||||
func TestPodConditionFailed(t *testing.T) {
|
||||
pod := strToUnstructured(`
|
||||
apiVersion: v1
|
||||
kind: Pod
|
||||
metadata:
|
||||
name: test9
|
||||
spec:
|
||||
nodeName: minikube
|
||||
containers:
|
||||
- name: container
|
||||
status:
|
||||
phase: Failed
|
||||
containerStatuses:
|
||||
- ready: false
|
||||
restartCount: 0
|
||||
state:
|
||||
terminated:
|
||||
reason: Error
|
||||
exitCode: 1
|
||||
`)
|
||||
info := &ResourceInfo{}
|
||||
populateNodeInfo(pod, info, []string{})
|
||||
assert.Equal(t, []v1alpha1.InfoItem{
|
||||
{Name: "Status Reason", Value: "Error"},
|
||||
{Name: "Node", Value: "minikube"},
|
||||
{Name: "Containers", Value: "0/1"},
|
||||
}, info.Info)
|
||||
}
|
||||
|
||||
// Test pod condition succeed with deletion
|
||||
func TestPodConditionSucceededWithDeletion(t *testing.T) {
|
||||
pod := strToUnstructured(`
|
||||
apiVersion: v1
|
||||
kind: Pod
|
||||
metadata:
|
||||
name: test10
|
||||
deletionTimestamp: "2023-10-01T00:00:00Z"
|
||||
spec:
|
||||
nodeName: minikube
|
||||
containers:
|
||||
- name: container
|
||||
status:
|
||||
phase: Succeeded
|
||||
containerStatuses:
|
||||
- ready: false
|
||||
restartCount: 0
|
||||
state:
|
||||
terminated:
|
||||
reason: Completed
|
||||
exitCode: 0
|
||||
`)
|
||||
info := &ResourceInfo{}
|
||||
populateNodeInfo(pod, info, []string{})
|
||||
assert.Equal(t, []v1alpha1.InfoItem{
|
||||
{Name: "Status Reason", Value: "Completed"},
|
||||
{Name: "Node", Value: "minikube"},
|
||||
{Name: "Containers", Value: "0/1"},
|
||||
}, info.Info)
|
||||
}
|
||||
|
||||
// Test pod condition running with deletion
|
||||
func TestPodConditionRunningWithDeletion(t *testing.T) {
|
||||
pod := strToUnstructured(`
|
||||
apiVersion: v1
|
||||
kind: Pod
|
||||
metadata:
|
||||
name: test11
|
||||
deletionTimestamp: "2023-10-01T00:00:00Z"
|
||||
spec:
|
||||
nodeName: minikube
|
||||
containers:
|
||||
- name: container
|
||||
status:
|
||||
phase: Running
|
||||
containerStatuses:
|
||||
- ready: false
|
||||
restartCount: 0
|
||||
state:
|
||||
running: {}
|
||||
`)
|
||||
info := &ResourceInfo{}
|
||||
populateNodeInfo(pod, info, []string{})
|
||||
assert.Equal(t, []v1alpha1.InfoItem{
|
||||
{Name: "Status Reason", Value: "Terminating"},
|
||||
{Name: "Node", Value: "minikube"},
|
||||
{Name: "Containers", Value: "0/1"},
|
||||
}, info.Info)
|
||||
}
|
||||
|
||||
// Test pod condition pending with deletion
|
||||
func TestPodConditionPendingWithDeletion(t *testing.T) {
|
||||
pod := strToUnstructured(`
|
||||
apiVersion: v1
|
||||
kind: Pod
|
||||
metadata:
|
||||
name: test12
|
||||
deletionTimestamp: "2023-10-01T00:00:00Z"
|
||||
spec:
|
||||
nodeName: minikube
|
||||
containers:
|
||||
- name: container
|
||||
status:
|
||||
phase: Pending
|
||||
`)
|
||||
info := &ResourceInfo{}
|
||||
populateNodeInfo(pod, info, []string{})
|
||||
assert.Equal(t, []v1alpha1.InfoItem{
|
||||
{Name: "Status Reason", Value: "Terminating"},
|
||||
{Name: "Node", Value: "minikube"},
|
||||
{Name: "Containers", Value: "0/1"},
|
||||
}, info.Info)
|
||||
}
|
||||
|
||||
// Test PodScheduled condition with reason SchedulingGated
|
||||
func TestPodScheduledWithSchedulingGated(t *testing.T) {
|
||||
pod := strToUnstructured(`
|
||||
apiVersion: v1
|
||||
kind: Pod
|
||||
metadata:
|
||||
name: test13
|
||||
spec:
|
||||
nodeName: minikube
|
||||
containers:
|
||||
- name: container1
|
||||
- name: container2
|
||||
status:
|
||||
phase: podPhase
|
||||
conditions:
|
||||
- type: PodScheduled
|
||||
status: "False"
|
||||
reason: SchedulingGated
|
||||
`)
|
||||
info := &ResourceInfo{}
|
||||
populateNodeInfo(pod, info, []string{})
|
||||
assert.Equal(t, []v1alpha1.InfoItem{
|
||||
{Name: "Status Reason", Value: "SchedulingGated"},
|
||||
{Name: "Node", Value: "minikube"},
|
||||
{Name: "Containers", Value: "0/2"},
|
||||
}, info.Info)
|
||||
}
|
||||
|
||||
func TestGetNodeInfo(t *testing.T) {
|
||||
node := strToUnstructured(`
|
||||
apiVersion: v1
|
||||
|
||||
@@ -98,6 +98,18 @@ func (ctrl *ApplicationController) executePostDeleteHooks(app *v1alpha1.Applicat
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
if hookHealth == nil {
|
||||
logCtx.WithFields(log.Fields{
|
||||
"group": obj.GroupVersionKind().Group,
|
||||
"version": obj.GroupVersionKind().Version,
|
||||
"kind": obj.GetKind(),
|
||||
"name": obj.GetName(),
|
||||
"namespace": obj.GetNamespace(),
|
||||
}).Info("No health check defined for resource, considering it healthy")
|
||||
hookHealth = &health.HealthStatus{
|
||||
Status: health.HealthStatusHealthy,
|
||||
}
|
||||
}
|
||||
if hookHealth.Status == health.HealthStatusProgressing {
|
||||
progressingHooksCnt++
|
||||
}
|
||||
@@ -128,6 +140,11 @@ func (ctrl *ApplicationController) cleanupPostDeleteHooks(liveObjs map[kube.Reso
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
if hookHealth == nil {
|
||||
hookHealth = &health.HealthStatus{
|
||||
Status: health.HealthStatusHealthy,
|
||||
}
|
||||
}
|
||||
if health.IsWorse(aggregatedHealth, hookHealth.Status) {
|
||||
aggregatedHealth = hookHealth.Status
|
||||
}
|
||||
|
||||
@@ -160,6 +160,11 @@ func (m *appStateManager) GetRepoObjs(app *v1alpha1.Application, sources []v1alp
|
||||
return nil, nil, fmt.Errorf("failed to get Helm settings: %w", err)
|
||||
}
|
||||
|
||||
installationID, err := m.settingsMgr.GetInstallationID()
|
||||
if err != nil {
|
||||
return nil, nil, fmt.Errorf("failed to get installation ID: %w", err)
|
||||
}
|
||||
|
||||
ts.AddCheckpoint("build_options_ms")
|
||||
serverVersion, apiResources, err := m.liveStateCache.GetVersionsInfo(app.Spec.Destination.Server)
|
||||
if err != nil {
|
||||
@@ -222,6 +227,7 @@ func (m *appStateManager) GetRepoObjs(app *v1alpha1.Application, sources []v1alp
|
||||
TrackingMethod: string(argo.GetTrackingMethod(m.settingsMgr)),
|
||||
RefSources: refSources,
|
||||
HasMultipleSources: app.Spec.HasMultipleSources(),
|
||||
InstallationID: installationID,
|
||||
})
|
||||
if err != nil {
|
||||
return nil, nil, fmt.Errorf("failed to compare revisions for source %d of %d: %w", i+1, len(sources), err)
|
||||
@@ -252,6 +258,7 @@ func (m *appStateManager) GetRepoObjs(app *v1alpha1.Application, sources []v1alp
|
||||
RefSources: refSources,
|
||||
ProjectName: proj.Name,
|
||||
ProjectSourceRepos: proj.Spec.SourceRepos,
|
||||
InstallationID: installationID,
|
||||
})
|
||||
if err != nil {
|
||||
return nil, nil, fmt.Errorf("failed to generate manifest for source %d of %d: %w", i+1, len(sources), err)
|
||||
@@ -329,20 +336,24 @@ func DeduplicateTargetObjects(
|
||||
|
||||
// getComparisonSettings will return the system level settings related to the
|
||||
// diff/normalization process.
|
||||
func (m *appStateManager) getComparisonSettings() (string, map[string]v1alpha1.ResourceOverride, *settings.ResourcesFilter, error) {
|
||||
func (m *appStateManager) getComparisonSettings() (string, map[string]v1alpha1.ResourceOverride, *settings.ResourcesFilter, string, error) {
|
||||
resourceOverrides, err := m.settingsMgr.GetResourceOverrides()
|
||||
if err != nil {
|
||||
return "", nil, nil, err
|
||||
return "", nil, nil, "", err
|
||||
}
|
||||
appLabelKey, err := m.settingsMgr.GetAppInstanceLabelKey()
|
||||
if err != nil {
|
||||
return "", nil, nil, err
|
||||
return "", nil, nil, "", err
|
||||
}
|
||||
resFilter, err := m.settingsMgr.GetResourcesFilter()
|
||||
if err != nil {
|
||||
return "", nil, nil, err
|
||||
return "", nil, nil, "", err
|
||||
}
|
||||
return appLabelKey, resourceOverrides, resFilter, nil
|
||||
installationID, err := m.settingsMgr.GetInstallationID()
|
||||
if err != nil {
|
||||
return "", nil, nil, "", err
|
||||
}
|
||||
return appLabelKey, resourceOverrides, resFilter, installationID, nil
|
||||
}
|
||||
|
||||
// verifyGnuPGSignature verifies the result of a GnuPG operation for a given git
|
||||
@@ -393,7 +404,7 @@ func isManagedNamespace(ns *unstructured.Unstructured, app *v1alpha1.Application
|
||||
// revision and overrides in the app spec.
|
||||
func (m *appStateManager) CompareAppState(app *v1alpha1.Application, project *v1alpha1.AppProject, revisions []string, sources []v1alpha1.ApplicationSource, noCache bool, noRevisionCache bool, localManifests []string, hasMultipleSources bool, rollback bool) (*comparisonResult, error) {
|
||||
ts := stats.NewTimingStats()
|
||||
appLabelKey, resourceOverrides, resFilter, err := m.getComparisonSettings()
|
||||
appLabelKey, resourceOverrides, resFilter, installationID, err := m.getComparisonSettings()
|
||||
|
||||
ts.AddCheckpoint("settings_ms")
|
||||
|
||||
@@ -559,7 +570,7 @@ func (m *appStateManager) CompareAppState(app *v1alpha1.Application, project *v1
|
||||
|
||||
for _, liveObj := range liveObjByKey {
|
||||
if liveObj != nil {
|
||||
appInstanceName := m.resourceTracking.GetAppName(liveObj, appLabelKey, trackingMethod)
|
||||
appInstanceName := m.resourceTracking.GetAppName(liveObj, appLabelKey, trackingMethod, installationID)
|
||||
if appInstanceName != "" && appInstanceName != app.InstanceName(m.namespace) {
|
||||
fqInstanceName := strings.ReplaceAll(appInstanceName, "_", "/")
|
||||
conditions = append(conditions, v1alpha1.ApplicationCondition{
|
||||
@@ -698,7 +709,7 @@ func (m *appStateManager) CompareAppState(app *v1alpha1.Application, project *v1
|
||||
}
|
||||
gvk := obj.GroupVersionKind()
|
||||
|
||||
isSelfReferencedObj := m.isSelfReferencedObj(liveObj, targetObj, app.GetName(), appLabelKey, trackingMethod)
|
||||
isSelfReferencedObj := m.isSelfReferencedObj(liveObj, targetObj, app.GetName(), appLabelKey, trackingMethod, installationID)
|
||||
|
||||
resState := v1alpha1.ResourceStatus{
|
||||
Namespace: obj.GetNamespace(),
|
||||
@@ -897,9 +908,7 @@ func useDiffCache(noCache bool, manifestInfos []*apiclient.ManifestResponse, sou
|
||||
return false
|
||||
}
|
||||
|
||||
currentSpec := app.BuildComparedToStatus()
|
||||
specChanged := !reflect.DeepEqual(app.Status.Sync.ComparedTo, currentSpec)
|
||||
if specChanged {
|
||||
if !specEqualsCompareTo(app.Spec, app.Status.Sync.ComparedTo) {
|
||||
log.WithField("useDiffCache", "false").Debug("specChanged")
|
||||
return false
|
||||
}
|
||||
@@ -908,6 +917,29 @@ func useDiffCache(noCache bool, manifestInfos []*apiclient.ManifestResponse, sou
|
||||
return true
|
||||
}
|
||||
|
||||
// specEqualsCompareTo compares the application spec to the comparedTo status. It normalizes the destination to match
|
||||
// the comparedTo destination before comparing. It does not mutate the original spec or comparedTo.
|
||||
func specEqualsCompareTo(spec v1alpha1.ApplicationSpec, comparedTo v1alpha1.ComparedTo) bool {
|
||||
// Make a copy to be sure we don't mutate the original.
|
||||
specCopy := spec.DeepCopy()
|
||||
currentSpec := specCopy.BuildComparedToStatus()
|
||||
|
||||
// The spec might have been augmented to include both server and name, so change it to match the comparedTo before
|
||||
// comparing.
|
||||
if comparedTo.Destination.Server == "" {
|
||||
currentSpec.Destination.Server = ""
|
||||
}
|
||||
if comparedTo.Destination.Name == "" {
|
||||
currentSpec.Destination.Name = ""
|
||||
}
|
||||
|
||||
// Set IsServerInferred to false on both, because that field is not important for comparison.
|
||||
comparedTo.Destination.SetIsServerInferred(false)
|
||||
currentSpec.Destination.SetIsServerInferred(false)
|
||||
|
||||
return reflect.DeepEqual(comparedTo, currentSpec)
|
||||
}
|
||||
|
||||
func (m *appStateManager) persistRevisionHistory(
|
||||
app *v1alpha1.Application,
|
||||
revision string,
|
||||
@@ -1002,7 +1034,7 @@ func NewAppStateManager(
|
||||
// group and kind) match the properties of the live object, or if the tracking method
|
||||
// used does not provide the required properties for matching.
|
||||
// Reference: https://github.com/argoproj/argo-cd/issues/8683
|
||||
func (m *appStateManager) isSelfReferencedObj(live, config *unstructured.Unstructured, appName, appLabelKey string, trackingMethod v1alpha1.TrackingMethod) bool {
|
||||
func (m *appStateManager) isSelfReferencedObj(live, config *unstructured.Unstructured, appName, appLabelKey string, trackingMethod v1alpha1.TrackingMethod, installationID string) bool {
|
||||
if live == nil {
|
||||
return true
|
||||
}
|
||||
@@ -1035,7 +1067,7 @@ func (m *appStateManager) isSelfReferencedObj(live, config *unstructured.Unstruc
|
||||
// to match the properties from the live object. Cluster scoped objects
|
||||
// carry the app's destination namespace in the tracking annotation,
|
||||
// but are unique in GVK + name combination.
|
||||
appInstance := m.resourceTracking.GetAppInstance(live, appLabelKey, trackingMethod)
|
||||
appInstance := m.resourceTracking.GetAppInstance(live, appLabelKey, trackingMethod, installationID)
|
||||
if appInstance != nil {
|
||||
return isSelfReferencedObj(live, *appInstance)
|
||||
}
|
||||
|
||||
@@ -1372,8 +1372,8 @@ func TestIsLiveResourceManaged(t *testing.T) {
|
||||
configObj := managedObj.DeepCopy()
|
||||
|
||||
// then
|
||||
assert.True(t, manager.isSelfReferencedObj(managedObj, configObj, appName, common.AnnotationKeyAppInstance, argo.TrackingMethodLabel))
|
||||
assert.True(t, manager.isSelfReferencedObj(managedObj, configObj, appName, common.AnnotationKeyAppInstance, argo.TrackingMethodAnnotation))
|
||||
assert.True(t, manager.isSelfReferencedObj(managedObj, configObj, appName, common.AnnotationKeyAppInstance, argo.TrackingMethodLabel, ""))
|
||||
assert.True(t, manager.isSelfReferencedObj(managedObj, configObj, appName, common.AnnotationKeyAppInstance, argo.TrackingMethodAnnotation, ""))
|
||||
})
|
||||
t.Run("will return true if tracked with label", func(t *testing.T) {
|
||||
// given
|
||||
@@ -1381,43 +1381,43 @@ func TestIsLiveResourceManaged(t *testing.T) {
|
||||
configObj := managedObjWithLabel.DeepCopy()
|
||||
|
||||
// then
|
||||
assert.True(t, manager.isSelfReferencedObj(managedObjWithLabel, configObj, appName, common.AnnotationKeyAppInstance, argo.TrackingMethodLabel))
|
||||
assert.True(t, manager.isSelfReferencedObj(managedObjWithLabel, configObj, appName, common.AnnotationKeyAppInstance, argo.TrackingMethodLabel, ""))
|
||||
})
|
||||
t.Run("will handle if trackingId has wrong resource name and config is nil", func(t *testing.T) {
|
||||
// given
|
||||
t.Parallel()
|
||||
|
||||
// then
|
||||
assert.True(t, manager.isSelfReferencedObj(unmanagedObjWrongName, nil, appName, common.AnnotationKeyAppInstance, argo.TrackingMethodLabel))
|
||||
assert.False(t, manager.isSelfReferencedObj(unmanagedObjWrongName, nil, appName, common.AnnotationKeyAppInstance, argo.TrackingMethodAnnotation))
|
||||
assert.True(t, manager.isSelfReferencedObj(unmanagedObjWrongName, nil, appName, common.AnnotationKeyAppInstance, argo.TrackingMethodLabel, ""))
|
||||
assert.False(t, manager.isSelfReferencedObj(unmanagedObjWrongName, nil, appName, common.AnnotationKeyAppInstance, argo.TrackingMethodAnnotation, ""))
|
||||
})
|
||||
t.Run("will handle if trackingId has wrong resource group and config is nil", func(t *testing.T) {
|
||||
// given
|
||||
t.Parallel()
|
||||
|
||||
// then
|
||||
assert.True(t, manager.isSelfReferencedObj(unmanagedObjWrongGroup, nil, appName, common.AnnotationKeyAppInstance, argo.TrackingMethodLabel))
|
||||
assert.False(t, manager.isSelfReferencedObj(unmanagedObjWrongGroup, nil, appName, common.AnnotationKeyAppInstance, argo.TrackingMethodAnnotation))
|
||||
assert.True(t, manager.isSelfReferencedObj(unmanagedObjWrongGroup, nil, appName, common.AnnotationKeyAppInstance, argo.TrackingMethodLabel, ""))
|
||||
assert.False(t, manager.isSelfReferencedObj(unmanagedObjWrongGroup, nil, appName, common.AnnotationKeyAppInstance, argo.TrackingMethodAnnotation, ""))
|
||||
})
|
||||
t.Run("will handle if trackingId has wrong kind and config is nil", func(t *testing.T) {
|
||||
// given
|
||||
t.Parallel()
|
||||
|
||||
// then
|
||||
assert.True(t, manager.isSelfReferencedObj(unmanagedObjWrongKind, nil, appName, common.AnnotationKeyAppInstance, argo.TrackingMethodLabel))
|
||||
assert.False(t, manager.isSelfReferencedObj(unmanagedObjWrongKind, nil, appName, common.AnnotationKeyAppInstance, argo.TrackingMethodAnnotation))
|
||||
assert.True(t, manager.isSelfReferencedObj(unmanagedObjWrongKind, nil, appName, common.AnnotationKeyAppInstance, argo.TrackingMethodLabel, ""))
|
||||
assert.False(t, manager.isSelfReferencedObj(unmanagedObjWrongKind, nil, appName, common.AnnotationKeyAppInstance, argo.TrackingMethodAnnotation, ""))
|
||||
})
|
||||
t.Run("will handle if trackingId has wrong namespace and config is nil", func(t *testing.T) {
|
||||
// given
|
||||
t.Parallel()
|
||||
|
||||
// then
|
||||
assert.True(t, manager.isSelfReferencedObj(unmanagedObjWrongNamespace, nil, appName, common.AnnotationKeyAppInstance, argo.TrackingMethodLabel))
|
||||
assert.False(t, manager.isSelfReferencedObj(unmanagedObjWrongNamespace, nil, appName, common.AnnotationKeyAppInstance, argo.TrackingMethodAnnotationAndLabel))
|
||||
assert.True(t, manager.isSelfReferencedObj(unmanagedObjWrongNamespace, nil, appName, common.AnnotationKeyAppInstance, argo.TrackingMethodLabel, ""))
|
||||
assert.False(t, manager.isSelfReferencedObj(unmanagedObjWrongNamespace, nil, appName, common.AnnotationKeyAppInstance, argo.TrackingMethodAnnotationAndLabel, ""))
|
||||
})
|
||||
t.Run("will return true if live is nil", func(t *testing.T) {
|
||||
t.Parallel()
|
||||
assert.True(t, manager.isSelfReferencedObj(nil, nil, appName, common.AnnotationKeyAppInstance, argo.TrackingMethodAnnotation))
|
||||
assert.True(t, manager.isSelfReferencedObj(nil, nil, appName, common.AnnotationKeyAppInstance, argo.TrackingMethodAnnotation, ""))
|
||||
})
|
||||
|
||||
t.Run("will handle upgrade in desired state APIGroup", func(t *testing.T) {
|
||||
@@ -1427,11 +1427,13 @@ func TestIsLiveResourceManaged(t *testing.T) {
|
||||
delete(config.GetAnnotations(), common.AnnotationKeyAppInstance)
|
||||
|
||||
// then
|
||||
assert.True(t, manager.isSelfReferencedObj(managedWrongAPIGroup, config, appName, common.AnnotationKeyAppInstance, argo.TrackingMethodAnnotation))
|
||||
assert.True(t, manager.isSelfReferencedObj(managedWrongAPIGroup, config, appName, common.AnnotationKeyAppInstance, argo.TrackingMethodAnnotation, ""))
|
||||
})
|
||||
}
|
||||
|
||||
func TestUseDiffCache(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
type fixture struct {
|
||||
testName string
|
||||
noCache bool
|
||||
@@ -1527,6 +1529,10 @@ func TestUseDiffCache(t *testing.T) {
|
||||
t.Fatalf("error merging app: %s", err)
|
||||
}
|
||||
}
|
||||
if app.Spec.Destination.Name != "" && app.Spec.Destination.Server != "" {
|
||||
// Simulate the controller's process for populating both of these fields.
|
||||
app.Spec.Destination.SetInferredServer(app.Spec.Destination.Server)
|
||||
}
|
||||
return app
|
||||
}
|
||||
|
||||
@@ -1692,6 +1698,44 @@ func TestUseDiffCache(t *testing.T) {
|
||||
expectedUseCache: false,
|
||||
serverSideDiff: false,
|
||||
},
|
||||
{
|
||||
// There are code paths that modify the ApplicationSpec and augment the destination field with both the
|
||||
// destination server and name. Since both fields are populated in the app spec but not in the comparedTo,
|
||||
// we need to make sure we correctly compare the fields and don't miss the cache.
|
||||
testName: "will return true if the app spec destination contains both server and name, but otherwise matches comparedTo",
|
||||
noCache: false,
|
||||
manifestInfos: manifestInfos("rev1"),
|
||||
sources: sources(),
|
||||
app: app("httpbin", "rev1", false, &argoappv1.Application{
|
||||
Spec: argoappv1.ApplicationSpec{
|
||||
Destination: argoappv1.ApplicationDestination{
|
||||
Server: "https://kubernetes.default.svc",
|
||||
Name: "httpbin",
|
||||
Namespace: "httpbin",
|
||||
},
|
||||
},
|
||||
Status: argoappv1.ApplicationStatus{
|
||||
Resources: []argoappv1.ResourceStatus{},
|
||||
Sync: argoappv1.SyncStatus{
|
||||
Status: argoappv1.SyncStatusCodeSynced,
|
||||
ComparedTo: argoappv1.ComparedTo{
|
||||
Destination: argoappv1.ApplicationDestination{
|
||||
Server: "https://kubernetes.default.svc",
|
||||
Namespace: "httpbin",
|
||||
},
|
||||
},
|
||||
Revision: "rev1",
|
||||
},
|
||||
ReconciledAt: &metav1.Time{
|
||||
Time: time.Now().Add(-time.Hour),
|
||||
},
|
||||
},
|
||||
}),
|
||||
manifestRevisions: []string{"rev1"},
|
||||
statusRefreshTimeout: time.Hour * 24,
|
||||
expectedUseCache: true,
|
||||
serverSideDiff: true,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tc := range cases {
|
||||
|
||||
@@ -167,12 +167,18 @@ func (m *appStateManager) SyncAppState(app *v1alpha1.Application, state *v1alpha
|
||||
state.Phase = common.OperationError
|
||||
state.Message = fmt.Sprintf("Failed to load application project: %v", err)
|
||||
return
|
||||
} else if syncWindowPreventsSync(app, proj) {
|
||||
// If the operation is currently running, simply let the user know the sync is blocked by a current sync window
|
||||
if state.Phase == common.OperationRunning {
|
||||
state.Message = "Sync operation blocked by sync window"
|
||||
} else {
|
||||
isBlocked, err := syncWindowPreventsSync(app, proj)
|
||||
if isBlocked {
|
||||
// If the operation is currently running, simply let the user know the sync is blocked by a current sync window
|
||||
if state.Phase == common.OperationRunning {
|
||||
state.Message = "Sync operation blocked by sync window"
|
||||
if err != nil {
|
||||
state.Message = fmt.Sprintf("%s: %v", state.Message, err)
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
if !isMultiSourceRevision {
|
||||
@@ -282,6 +288,11 @@ func (m *appStateManager) SyncAppState(app *v1alpha1.Application, state *v1alpha
|
||||
log.Errorf("Could not get appInstanceLabelKey: %v", err)
|
||||
return
|
||||
}
|
||||
installationID, err := m.settingsMgr.GetInstallationID()
|
||||
if err != nil {
|
||||
log.Errorf("Could not get installation ID: %v", err)
|
||||
return
|
||||
}
|
||||
trackingMethod := argo.GetTrackingMethod(m.settingsMgr)
|
||||
|
||||
opts := []sync.SyncOpt{
|
||||
@@ -311,7 +322,7 @@ func (m *appStateManager) SyncAppState(app *v1alpha1.Application, state *v1alpha
|
||||
return (len(syncOp.Resources) == 0 ||
|
||||
isPostDeleteHook(target) ||
|
||||
argo.ContainsSyncResource(key.Name, key.Namespace, schema.GroupVersionKind{Kind: key.Kind, Group: key.Group}, syncOp.Resources)) &&
|
||||
m.isSelfReferencedObj(live, target, app.GetName(), appLabelKey, trackingMethod)
|
||||
m.isSelfReferencedObj(live, target, app.GetName(), appLabelKey, trackingMethod, installationID)
|
||||
}),
|
||||
sync.WithManifestValidation(!syncOp.SyncOptions.HasOption(common.SyncOptionsDisableValidation)),
|
||||
sync.WithSyncWaveHook(delayBetweenSyncWaves),
|
||||
@@ -528,11 +539,16 @@ func delayBetweenSyncWaves(phase common.SyncPhase, wave int, finalWave bool) err
|
||||
return nil
|
||||
}
|
||||
|
||||
func syncWindowPreventsSync(app *v1alpha1.Application, proj *v1alpha1.AppProject) bool {
|
||||
func syncWindowPreventsSync(app *v1alpha1.Application, proj *v1alpha1.AppProject) (bool, error) {
|
||||
window := proj.Spec.SyncWindows.Matches(app)
|
||||
isManual := false
|
||||
if app.Status.OperationState != nil {
|
||||
isManual = !app.Status.OperationState.Operation.InitiatedBy.Automated
|
||||
}
|
||||
return !window.CanSync(isManual)
|
||||
canSync, err := window.CanSync(isManual)
|
||||
if err != nil {
|
||||
// prevents sync because sync window has an error
|
||||
return true, err
|
||||
}
|
||||
return !canSync, nil
|
||||
}
|
||||
|
||||
@@ -19,41 +19,119 @@ const observerCallback = function(mutationsList, observer) {
|
||||
const observer = new MutationObserver(observerCallback);
|
||||
observer.observe(targetNode, observerOptions);
|
||||
|
||||
function getCurrentVersion() {
|
||||
const currentVersion = window.location.href.match(/\/en\/(release-(?:v\d+|[\d\.]+|\w+)|latest|stable)\//);
|
||||
if (currentVersion && currentVersion.length > 1) {
|
||||
return currentVersion[1];
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
function initializeVersionDropdown() {
|
||||
const callbackName = 'callback_' + new Date().getTime();
|
||||
window[callbackName] = function(response) {
|
||||
const div = document.createElement('div');
|
||||
div.innerHTML = response.html;
|
||||
document.querySelector(".md-header__inner > .md-header__title").appendChild(div);
|
||||
const headerTitle = document.querySelector(".md-header__inner > .md-header__title");
|
||||
if (headerTitle) {
|
||||
headerTitle.appendChild(div);
|
||||
}
|
||||
|
||||
const container = div.querySelector('.rst-versions');
|
||||
if (!container) return; // Exit if container not found
|
||||
|
||||
// Add caret icon
|
||||
var caret = document.createElement('div');
|
||||
caret.innerHTML = "<i class='fa fa-caret-down dropdown-caret'></i>";
|
||||
caret.classList.add('dropdown-caret');
|
||||
div.querySelector('.rst-current-version').appendChild(caret);
|
||||
const currentVersionElem = div.querySelector('.rst-current-version');
|
||||
if (currentVersionElem) {
|
||||
currentVersionElem.appendChild(caret);
|
||||
}
|
||||
|
||||
div.querySelector('.rst-current-version').addEventListener('click', function() {
|
||||
container.classList.toggle('shift-up');
|
||||
});
|
||||
// Add click listener to toggle dropdown
|
||||
if (currentVersionElem && container) {
|
||||
currentVersionElem.addEventListener('click', function() {
|
||||
container.classList.toggle('shift-up');
|
||||
});
|
||||
}
|
||||
|
||||
// Sorting Logic
|
||||
sortVersionLinks(container);
|
||||
};
|
||||
|
||||
// Load CSS
|
||||
var CSSLink = document.createElement('link');
|
||||
CSSLink.rel = 'stylesheet';
|
||||
CSSLink.href = '/assets/versions.css';
|
||||
document.getElementsByTagName('head')[0].appendChild(CSSLink);
|
||||
|
||||
// Load JSONP Script
|
||||
var script = document.createElement('script');
|
||||
const currentVersion = getCurrentVersion();
|
||||
script.src = 'https://argo-cd.readthedocs.io/_/api/v2/footer_html/?' +
|
||||
'callback=' + callbackName + '&project=argo-cd&page=&theme=mkdocs&format=jsonp&docroot=docs&source_suffix=.md&version=' + (window['READTHEDOCS_DATA'] || { version: 'latest' }).version;
|
||||
'callback=' + callbackName + '&project=argo-cd&page=&theme=mkdocs&format=jsonp&docroot=docs&source_suffix=.md&version=' + (currentVersion || 'latest');
|
||||
document.getElementsByTagName('head')[0].appendChild(script);
|
||||
}
|
||||
|
||||
// Function to sort version links
|
||||
function sortVersionLinks(container) {
|
||||
// Find all <dl> elements within the container
|
||||
const dlElements = container.querySelectorAll('dl');
|
||||
|
||||
dlElements.forEach(dl => {
|
||||
const dt = dl.querySelector('dt');
|
||||
if (dt && dt.textContent.trim().toLowerCase() === 'versions') {
|
||||
// Found the Versions <dl>
|
||||
const ddElements = Array.from(dl.querySelectorAll('dd'));
|
||||
|
||||
// Define sorting criteria
|
||||
ddElements.sort((a, b) => {
|
||||
const aText = a.textContent.trim().toLowerCase();
|
||||
const bText = b.textContent.trim().toLowerCase();
|
||||
|
||||
// Prioritize 'latest' and 'stable'
|
||||
if (aText === 'latest') return -1;
|
||||
if (bText === 'latest') return 1;
|
||||
if (aText === 'stable') return -1;
|
||||
if (bText === 'stable') return 1;
|
||||
|
||||
// Extract version numbers (e.g., release-2.9)
|
||||
const aVersionMatch = aText.match(/release-(\d+(\.\d+)*)/);
|
||||
const bVersionMatch = bText.match(/release-(\d+(\.\d+)*)/);
|
||||
|
||||
if (aVersionMatch && bVersionMatch) {
|
||||
const aVersion = aVersionMatch[1].split('.').map(Number);
|
||||
const bVersion = bVersionMatch[1].split('.').map(Number);
|
||||
|
||||
for (let i = 0; i < Math.max(aVersion.length, bVersion.length); i++) {
|
||||
const aNum = aVersion[i] || 0;
|
||||
const bNum = bVersion[i] || 0;
|
||||
if (aNum > bNum) return -1;
|
||||
if (aNum < bNum) return 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Fallback to alphabetical order
|
||||
return aText.localeCompare(bText);
|
||||
});
|
||||
|
||||
// Remove existing <dd> elements
|
||||
ddElements.forEach(dd => dl.removeChild(dd));
|
||||
|
||||
// Append sorted <dd> elements
|
||||
ddElements.forEach(dd => dl.appendChild(dd));
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// VERSION WARNINGS
|
||||
window.addEventListener("DOMContentLoaded", function() {
|
||||
var currentVersion = window.location.href.match(/\/en\/(release-(?:v\d+|\w+)|latest|stable)\//);
|
||||
var margin = 30;
|
||||
var headerHeight = document.getElementsByClassName("md-header")[0].offsetHeight;
|
||||
if (currentVersion && currentVersion.length > 1) {
|
||||
currentVersion = currentVersion[1];
|
||||
const currentVersion = getCurrentVersion();
|
||||
if (currentVersion) {
|
||||
if (currentVersion === "latest") {
|
||||
document.querySelector("div[data-md-component=announce]").innerHTML = "<div id='announce-msg'>You are viewing the docs for an unreleased version of Argo CD, <a href='https://argo-cd.readthedocs.io/en/stable/'>click here to go to the latest stable version.</a></div>";
|
||||
var bannerHeight = document.getElementById('announce-msg').offsetHeight + margin;
|
||||
@@ -72,4 +150,4 @@ window.addEventListener("DOMContentLoaded", function() {
|
||||
"@media screen and (min-width: 60em){ .md-sidebar--secondary { height: 0; top:" + (bannerHeight + headerHeight) + "px !important; }}";
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
@@ -42,8 +42,11 @@ In order for an application to be managed and reconciled outside the Argo CD's c
|
||||
|
||||
In order to enable this feature, the Argo CD administrator must reconfigure the `argocd-server` and `argocd-application-controller` workloads to add the `--application-namespaces` parameter to the container's startup command.
|
||||
|
||||
The `--application-namespaces` parameter takes a comma-separated list of namespaces where `Applications` are to be allowed in. Each entry of the list supports shell-style wildcards such as `*`, so for example the entry `app-team-*` would match `app-team-one` and `app-team-two`. To enable all namespaces on the cluster where Argo CD is running on, you can just specify `*`, i.e. `--application-namespaces=*`.
|
||||
The `--application-namespaces` parameter takes a comma-separated list of namespaces where `Applications` are to be allowed in. Each entry of the list supports:
|
||||
|
||||
- shell-style wildcards such as `*`, so for example the entry `app-team-*` would match `app-team-one` and `app-team-two`. To enable all namespaces on the cluster where Argo CD is running on, you can just specify `*`, i.e. `--application-namespaces=*`.
|
||||
- regex, requires wrapping the string in ```/```, example to allow all namespaces except a particular one: ```/^((?!not-allowed).)*$/```.
|
||||
|
||||
The startup parameters for both, the `argocd-server` and the `argocd-application-controller` can also be conveniently set up and kept in sync by specifying the `application.namespaces` settings in the `argocd-cmd-params-cm` ConfigMap _instead_ of changing the manifests for the respective workloads. For example:
|
||||
|
||||
```yaml
|
||||
|
||||
@@ -7,6 +7,8 @@ The Git generator contains two subtypes: the Git directory generator, and Git fi
|
||||
If the `project` field in your ApplicationSet is templated, developers may be able to create Applications under Projects with excessive permissions.
|
||||
For ApplicationSets with a templated `project` field, [the source of truth _must_ be controlled by admins](./Security.md#templated-project-field)
|
||||
- in the case of git generators, PRs must require admin approval.
|
||||
- Git generator does not support Signature Verification For ApplicationSets with a templated `project` field.
|
||||
|
||||
|
||||
## Git Generator: Directories
|
||||
|
||||
@@ -326,7 +328,7 @@ As with other generators, clusters *must* already be defined within Argo CD, in
|
||||
In addition to the flattened key/value pairs from the configuration file, the following generator parameters are provided:
|
||||
|
||||
- `{{.path.path}}`: The path to the directory containing matching configuration file within the Git repository. Example: `/clusters/clusterA`, if the config file was `/clusters/clusterA/config.json`
|
||||
- `{{index .path n}}`: The path to the matching configuration file within the Git repository, split into array elements (`n` - array index). Example: `index .path 0: clusters`, `index .path 1: clusterA`
|
||||
- `{{index .path.segments n}}`: The path to the matching configuration file within the Git repository, split into array elements (`n` - array index). Example: `index .path.segments 0: clusters`, `index .path.segments 1: clusterA`
|
||||
- `{{.path.basename}}`: Basename of the path to the directory containing the configuration file (e.g. `clusterA`, with the above example.)
|
||||
- `{{.path.basenameNormalized}}`: This field is the same as `.path.basename` with unsupported characters replaced with `-` (e.g. a `path` of `/directory/directory_2`, and `.path.basename` of `directory_2` would produce `directory-2` here).
|
||||
- `{{.path.filename}}`: The matched filename. e.g., `config.json` in the above example.
|
||||
@@ -360,7 +362,7 @@ spec:
|
||||
files:
|
||||
- path: "applicationset/examples/git-generator-files-discovery/cluster-config/**/config.json"
|
||||
values:
|
||||
base_dir: "{{index .path 0}}/{{index .path 1}}/{{index .path 2}}"
|
||||
base_dir: "{{index .path.segments 0}}/{{index .path.segments 1}}/{{index .path.segments 2}}"
|
||||
template:
|
||||
metadata:
|
||||
name: '{{.cluster.name}}-guestbook'
|
||||
|
||||
@@ -100,6 +100,17 @@ possible with Go text templates:
|
||||
- name: throw-away
|
||||
value: "{{end}}"
|
||||
|
||||
- Signature verification is not supported for the templated `project` field when using the Git generator.
|
||||
|
||||
::yaml
|
||||
apiVersion: argoproj.io/v1alpha1
|
||||
kind: ApplicationSet
|
||||
spec:
|
||||
goTemplate: true
|
||||
template:
|
||||
spec:
|
||||
project: {{.project}}
|
||||
|
||||
|
||||
## Migration guide
|
||||
|
||||
|
||||
@@ -40,6 +40,7 @@ Note:
|
||||
|
||||
- Referenced clusters must already be defined in Argo CD, for the ApplicationSet controller to use them
|
||||
- Only **one** of `name` or `server` may be specified: if both are specified, an error is returned.
|
||||
- Signature Verification does not work with the templated `project` field when using git generator.
|
||||
|
||||
The `metadata` field of template may also be used to set an Application `name`, or to add labels or annotations to the Application.
|
||||
|
||||
|
||||
@@ -279,6 +279,9 @@ data:
|
||||
# - annotation+label : Also uses an annotation for tracking, but additionally labels the resource with the application name
|
||||
application.resourceTrackingMethod: annotation
|
||||
|
||||
# Optional installation id. Allows to have multiple installations of Argo CD in the same cluster.
|
||||
installationID: "my-unique-id"
|
||||
|
||||
# disables admin user. Admin is enabled by default
|
||||
admin.enabled: "false"
|
||||
# add an additional local user with apiKey and login capabilities
|
||||
@@ -420,3 +423,5 @@ data:
|
||||
cluster:
|
||||
name: some-cluster
|
||||
server: https://some-cluster
|
||||
# The maximum size of the payload that can be sent to the webhook server.
|
||||
webhook.maxPayloadSizeMB: 1024
|
||||
@@ -47,8 +47,11 @@ data:
|
||||
controller.log.level: "info"
|
||||
# Prometheus metrics cache expiration (disabled by default. e.g. 24h0m0s)
|
||||
controller.metrics.cache.expiration: "24h0m0s"
|
||||
# Specifies timeout between application self heal attempts (default 5)
|
||||
controller.self.heal.timeout.seconds: "5"
|
||||
# Specifies exponential backoff timeout parameters between application self heal attempts
|
||||
controller.self.heal.timeout.seconds: "2"
|
||||
controller.self.heal.backoff.factor: "3"
|
||||
controller.self.heal.backoff.cap.seconds: "300"
|
||||
|
||||
# Cache expiration for app state (default 1h0m0s)
|
||||
controller.app.state.cache.expiration: "1h0m0s"
|
||||
# Specifies if resource health should be persisted in app CRD (default true)
|
||||
|
||||
@@ -122,9 +122,19 @@ To do so, when the action if performed on an application's resource, the `<actio
|
||||
For instance, to grant access to `example-user` to only delete Pods in the `prod-app` Application, the policy could be:
|
||||
|
||||
```csv
|
||||
p, example-user, applications, delete/*/Pod/*, default/prod-app, allow
|
||||
p, example-user, applications, delete/*/Pod/*/*, default/prod-app, allow
|
||||
```
|
||||
|
||||
!!!warning "Understand glob pattern behavior"
|
||||
|
||||
Argo CD RBAC does not use `/` as a separator when evaluating glob patterns. So the pattern `delete/*/kind/*`
|
||||
will match `delete/<group>/kind/<namespace>/<name>` but also `delete/<group>/<kind>/kind/<name>`.
|
||||
|
||||
The fact that both of these match will generally not be a problem, because resource kinds generally contain capital
|
||||
letters, and namespaces cannot contain capital letters. However, it is possible for a resource kind to be lowercase.
|
||||
So it is better to just always include all the parts of the resource in the pattern (in other words, always use four
|
||||
slashes).
|
||||
|
||||
If we want to grant access to the user to update all resources of an application, but not the application itself:
|
||||
|
||||
```csv
|
||||
@@ -135,7 +145,7 @@ If we want to explicitly deny delete of the application, but allow the user to d
|
||||
|
||||
```csv
|
||||
p, example-user, applications, delete, default/prod-app, deny
|
||||
p, example-user, applications, delete/*/Pod/*, default/prod-app, allow
|
||||
p, example-user, applications, delete/*/Pod/*/*, default/prod-app, allow
|
||||
```
|
||||
|
||||
!!! note
|
||||
@@ -145,7 +155,7 @@ p, example-user, applications, delete/*/Pod/*, default/prod-app, allow
|
||||
|
||||
```csv
|
||||
p, example-user, applications, delete, default/prod-app, allow
|
||||
p, example-user, applications, delete/*/Pod/*, default/prod-app, deny
|
||||
p, example-user, applications, delete/*/Pod/*/*, default/prod-app, deny
|
||||
```
|
||||
|
||||
#### The `action` action
|
||||
|
||||
@@ -65,7 +65,10 @@ argocd-application-controller [flags]
|
||||
--repo-server-strict-tls Whether to use strict validation of the TLS cert presented by the repo server
|
||||
--repo-server-timeout-seconds int Repo server RPC call timeout seconds. (default 60)
|
||||
--request-timeout string The length of time to wait before giving up on a single server request. Non-zero values should contain a corresponding time unit (e.g. 1s, 2m, 3h). A value of zero means don't timeout requests. (default "0")
|
||||
--self-heal-timeout-seconds int Specifies timeout between application self heal attempts (default 5)
|
||||
--self-heal-backoff-cap-seconds int Specifies max timeout of exponential backoff between application self heal attempts (default 300)
|
||||
--self-heal-backoff-factor int Specifies factor of exponential timeout between application self heal attempts (default 3)
|
||||
--self-heal-backoff-timeout-seconds int Specifies initial timeout of exponential backoff between self heal attempts (default 2)
|
||||
--self-heal-timeout-seconds int Specifies timeout between application self heal attempts
|
||||
--sentinel stringArray Redis sentinel hostname and port (e.g. argocd-redis-ha-announce-0:6379).
|
||||
--sentinelmaster string Redis sentinel master group name. (default "master")
|
||||
--server string The address and port of the Kubernetes API server
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
| Argo CD version | Kubernetes versions |
|
||||
|-----------------|---------------------|
|
||||
| 2.7 | v1.26, v1.25, v1.24, v1.23 |
|
||||
| 2.6 | v1.24, v1.23, v1.22 |
|
||||
| 2.5 | v1.24, v1.23, v1.22 |
|
||||
|
||||
| 2.12 | v1.29, v1.28, v1.27, v1.26 |
|
||||
| 2.11 | v1.29, v1.28, v1.27, v1.26, v1.25 |
|
||||
| 2.10 | v1.28, v1.27, v1.26, v1.25 |
|
||||
|
||||
@@ -1,5 +1,18 @@
|
||||
# v2.11 to 2.12
|
||||
|
||||
## Cluster secret scoping changes
|
||||
|
||||
From Argo CD 2.12, there have been some changes to the use of cluster secrets where a `project` is a non-empty value.
|
||||
Previously, an `Application` or `ApplicationSet` would use any cluster secret matching the URL of the `repoUrl` field.
|
||||
From 2.12, we now check to see whether the project field of an application _also_ matches the project field of the cluster
|
||||
secret. What this means is that if you have a cluster secret scoped to `project-a`, an application scoped to `project-b`
|
||||
can no longer make use of the secret. If you have a cluster secret that's intended to be used by applications in multiple
|
||||
projects, you need to **unset** the `project` field.
|
||||
|
||||
This also applies when using the Git generator in applicationsets; since an applicationset is not scoped to a particular
|
||||
project any cluster secrets it makes use of also needs to be globally scoped (i.e. any secret needs to have an unset
|
||||
`project`).
|
||||
|
||||
## Upgraded Helm Version
|
||||
|
||||
Note that bundled Helm version has been upgraded from 3.14.4 to 3.15.2.
|
||||
|
||||
@@ -19,6 +19,8 @@ URL configured in the Git provider should use the `/api/webhook` endpoint of you
|
||||
(e.g. `https://argocd.example.com/api/webhook`). If you wish to use a shared secret, input an
|
||||
arbitrary value in the secret. This value will be used when configuring the webhook in the next step.
|
||||
|
||||
To prevent DDoS attacks with unauthenticated webhook events (the `/api/webhook` endpoint currently lacks rate limiting protection), it is recommended to limit the payload size. You can achieve this by configuring the `argocd-cm` ConfigMap with the `webhook.maxPayloadSizeMB` attribute. The default value is 1GB.
|
||||
|
||||
## Github
|
||||
|
||||

|
||||
|
||||
@@ -29,6 +29,11 @@ not possible using Helm repositories.
|
||||
trust models, and it is not necessary (nor possible) to sign the public keys
|
||||
you are going to import into ArgoCD.
|
||||
|
||||
|
||||
!!!note Limitations
|
||||
Signature verification is not supported for the templated `project` field when
|
||||
using the Git generator.
|
||||
|
||||
## Signature verification targets
|
||||
|
||||
If signature verification is enforced, ArgoCD will verify the signature using
|
||||
|
||||
@@ -319,6 +319,11 @@ stringData:
|
||||
password: ****
|
||||
```
|
||||
|
||||
!!! warning
|
||||
Please keep in mind when using a project-scoped repository, only applications from the same project can make use of
|
||||
it. When using applicationsets with the Git generator, only non-scoped repositories can be used (i.e. repositories that
|
||||
do _not_ have a `project` set).
|
||||
|
||||
All the examples above talk about Git repositories, but the same principles apply to clusters as well.
|
||||
|
||||
```yaml
|
||||
|
||||
@@ -65,6 +65,14 @@ metadata:
|
||||
The advantages of using the tracking id annotation is that there are no clashes any
|
||||
more with other Kubernetes tools and Argo CD is never confused about the owner of a resource. The `annotation+label` can also be used if you want other tools to understand resources managed by Argo CD.
|
||||
|
||||
### Installation ID
|
||||
|
||||
If you are managing one cluster using multiple Argo CD instances, you will need to set `installationID` in the Argo CD ConfigMap. This will prevent conflicts between
|
||||
the different Argo CD instances:
|
||||
|
||||
* Each managed resource will have the annotation `argocd.argoproj.io/tracking-id: <installation-id>`
|
||||
* It is possible to have applications with the same name in Argo CD instances without causing conflicts.
|
||||
|
||||
### Non self-referencing annotations
|
||||
When using the tracking method `annotation` or `annotation+label`, Argo CD will consider the resource properties in the annotation (name, namespace, group and kind) to determine whether the resource should be compared against the desired state. If the tracking annotation does not reference the resource it is applied to, the resource will neither affect the application's sync status nor be marked for pruning.
|
||||
|
||||
|
||||
39
go.mod
39
go.mod
@@ -11,7 +11,7 @@ require (
|
||||
github.com/TomOnTime/utfutil v0.0.0-20180511104225-09c41003ee1d
|
||||
github.com/alicebob/miniredis/v2 v2.30.4
|
||||
github.com/antonmedv/expr v1.15.2
|
||||
github.com/argoproj/gitops-engine v0.7.1-0.20240615185936-83ce6ca8cedc
|
||||
github.com/argoproj/gitops-engine v0.7.1-0.20250129155113-faf5a4e5c37d
|
||||
github.com/argoproj/notifications-engine v0.4.1-0.20240606074338-0802cd427621
|
||||
github.com/argoproj/pkg v0.13.7-0.20230626144333-d56162821bd1
|
||||
github.com/aws/aws-sdk-go v1.50.8
|
||||
@@ -22,13 +22,13 @@ require (
|
||||
github.com/cespare/xxhash/v2 v2.2.0
|
||||
github.com/chainguard-dev/git-urls v1.0.2
|
||||
github.com/coreos/go-oidc/v3 v3.6.0
|
||||
github.com/cyphar/filepath-securejoin v0.2.4
|
||||
github.com/cyphar/filepath-securejoin v0.3.6
|
||||
github.com/dustin/go-humanize v1.0.1
|
||||
github.com/evanphx/json-patch v5.9.0+incompatible
|
||||
github.com/felixge/httpsnoop v1.0.3
|
||||
github.com/fsnotify/fsnotify v1.7.0
|
||||
github.com/gfleury/go-bitbucket-v1 v0.0.0-20220301131131-8e7ed04b843e
|
||||
github.com/go-git/go-git/v5 v5.12.0
|
||||
github.com/go-git/go-git/v5 v5.13.1
|
||||
github.com/go-jose/go-jose/v3 v3.0.3
|
||||
github.com/go-logr/logr v1.4.1
|
||||
github.com/go-openapi/loads v0.21.2
|
||||
@@ -52,14 +52,14 @@ require (
|
||||
github.com/grpc-ecosystem/go-grpc-middleware v1.4.0
|
||||
github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0
|
||||
github.com/grpc-ecosystem/grpc-gateway v1.16.0
|
||||
github.com/hashicorp/go-retryablehttp v0.7.4
|
||||
github.com/hashicorp/go-retryablehttp v0.7.7
|
||||
github.com/imdario/mergo v0.3.16
|
||||
github.com/improbable-eng/grpc-web v0.15.0
|
||||
github.com/itchyny/gojq v0.12.13
|
||||
github.com/jeremywohl/flatten v1.0.1
|
||||
github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51
|
||||
github.com/ktrysmt/go-bitbucket v0.9.67
|
||||
github.com/mattn/go-isatty v0.0.19
|
||||
github.com/mattn/go-isatty v0.0.20
|
||||
github.com/mattn/go-zglob v0.0.4
|
||||
github.com/microsoft/azure-devops-go-api/azuredevops v1.0.0-b5
|
||||
github.com/minio/blake2b-simd v0.0.0-20160723061019-3f5f724cb5b1
|
||||
@@ -74,7 +74,7 @@ require (
|
||||
github.com/soheilhy/cmux v0.1.5
|
||||
github.com/spf13/cobra v1.7.0
|
||||
github.com/spf13/pflag v1.0.5
|
||||
github.com/stretchr/testify v1.9.0
|
||||
github.com/stretchr/testify v1.10.0
|
||||
github.com/valyala/fasttemplate v1.2.2
|
||||
github.com/xanzy/go-gitlab v0.91.1
|
||||
github.com/yuin/gopher-lua v1.1.0
|
||||
@@ -82,12 +82,12 @@ require (
|
||||
go.opentelemetry.io/otel v1.21.0
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.21.0
|
||||
go.opentelemetry.io/otel/sdk v1.21.0
|
||||
golang.org/x/crypto v0.23.0
|
||||
golang.org/x/exp v0.0.0-20230522175609-2e198f4a06a1
|
||||
golang.org/x/net v0.25.0
|
||||
golang.org/x/crypto v0.31.0
|
||||
golang.org/x/exp v0.0.0-20240719175910-8a7402abbf56
|
||||
golang.org/x/net v0.33.0
|
||||
golang.org/x/oauth2 v0.12.0
|
||||
golang.org/x/sync v0.5.0
|
||||
golang.org/x/term v0.20.0
|
||||
golang.org/x/sync v0.10.0
|
||||
golang.org/x/term v0.27.0
|
||||
golang.org/x/time v0.5.0
|
||||
google.golang.org/genproto/googleapis/api v0.0.0-20230822172742-b8732ec3820d
|
||||
google.golang.org/grpc v1.59.0
|
||||
@@ -148,10 +148,10 @@ require (
|
||||
github.com/tidwall/pretty v1.2.0 // indirect
|
||||
go.opencensus.io v0.24.0 // indirect
|
||||
go.starlark.net v0.0.0-20230525235612-a134d8f9ddca // indirect
|
||||
golang.org/x/mod v0.14.0 // indirect
|
||||
golang.org/x/sys v0.20.0 // indirect
|
||||
golang.org/x/text v0.15.0 // indirect
|
||||
golang.org/x/tools v0.16.1 // indirect
|
||||
golang.org/x/mod v0.19.0 // indirect
|
||||
golang.org/x/sys v0.28.0 // indirect
|
||||
golang.org/x/text v0.21.0 // indirect
|
||||
golang.org/x/tools v0.23.0 // indirect
|
||||
google.golang.org/api v0.132.0 // indirect
|
||||
google.golang.org/genproto v0.0.0-20230822172742-b8732ec3820d // indirect
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20230822172742-b8732ec3820d // indirect
|
||||
@@ -174,7 +174,7 @@ require (
|
||||
github.com/Masterminds/goutils v1.1.1 // indirect
|
||||
github.com/Microsoft/go-winio v0.6.1 // indirect
|
||||
github.com/PagerDuty/go-pagerduty v1.7.0 // indirect
|
||||
github.com/ProtonMail/go-crypto v1.0.0 // indirect
|
||||
github.com/ProtonMail/go-crypto v1.1.3 // indirect
|
||||
github.com/RocketChat/Rocket.Chat.Go.SDK v0.0.0-20210112200207-10ab4d695d60 // indirect
|
||||
github.com/alicebob/gopher-json v0.0.0-20200520072559-a9ecdc9d1d3a // indirect
|
||||
github.com/asaskevich/govalidator v0.0.0-20230301143203-a9d515a09cc2 // indirect
|
||||
@@ -187,6 +187,7 @@ require (
|
||||
github.com/davecgh/go-spew v1.1.1 // indirect
|
||||
github.com/desertbit/timer v0.0.0-20180107155436-c41aec40b27f // indirect
|
||||
github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f // indirect
|
||||
github.com/dlclark/regexp2 v1.11.2
|
||||
github.com/emicklei/go-restful/v3 v3.11.0 // indirect
|
||||
github.com/emirpasic/gods v1.18.1 // indirect
|
||||
github.com/evanphx/json-patch/v5 v5.8.0 // indirect
|
||||
@@ -196,7 +197,7 @@ require (
|
||||
github.com/ghodss/yaml v1.0.0 // indirect
|
||||
github.com/go-errors/errors v1.4.2 // indirect
|
||||
github.com/go-git/gcfg v1.5.1-0.20230307220236-3a3c6141e376 // indirect
|
||||
github.com/go-git/go-billy/v5 v5.5.0 // indirect
|
||||
github.com/go-git/go-billy/v5 v5.6.1 // indirect
|
||||
github.com/go-logr/stdr v1.2.2 // indirect
|
||||
github.com/go-openapi/analysis v0.21.4 // indirect
|
||||
github.com/go-openapi/errors v0.20.3 // indirect
|
||||
@@ -249,7 +250,7 @@ require (
|
||||
github.com/opsgenie/opsgenie-go-sdk-v2 v1.0.5 // indirect
|
||||
github.com/peterbourgon/diskv v2.0.1+incompatible // indirect
|
||||
github.com/pjbgf/sha1cd v0.3.0 // indirect
|
||||
github.com/pkg/errors v0.9.1 // indirect
|
||||
github.com/pkg/errors v0.9.1
|
||||
github.com/pmezard/go-difflib v1.0.0 // indirect
|
||||
github.com/prometheus/client_model v0.5.0
|
||||
github.com/prometheus/common v0.45.0 // indirect
|
||||
@@ -259,7 +260,7 @@ require (
|
||||
github.com/russross/blackfriday/v2 v2.1.0 // indirect
|
||||
github.com/sergi/go-diff v1.3.2-0.20230802210424-5b0b94c5c0d3 // indirect
|
||||
github.com/shopspring/decimal v1.2.0 // indirect
|
||||
github.com/skeema/knownhosts v1.2.2 // indirect
|
||||
github.com/skeema/knownhosts v1.3.0 // indirect
|
||||
github.com/slack-go/slack v0.12.2 // indirect
|
||||
github.com/spf13/cast v1.6.0 // indirect
|
||||
github.com/stretchr/objx v0.5.2 // indirect
|
||||
|
||||
95
go.sum
95
go.sum
@@ -658,8 +658,8 @@ github.com/OvyFlash/telegram-bot-api/v5 v5.0.0-20240108230938-63e5c59035bf/go.mo
|
||||
github.com/PagerDuty/go-pagerduty v1.7.0 h1:S1NcMKECxT5hJwV4VT+QzeSsSiv4oWl1s2821dUqG/8=
|
||||
github.com/PagerDuty/go-pagerduty v1.7.0/go.mod h1:PuFyJKRz1liIAH4h5KVXVD18Obpp1ZXRdxHvmGXooro=
|
||||
github.com/ProtonMail/go-crypto v0.0.0-20230217124315-7d5c6f04bbb8/go.mod h1:I0gYDMZ6Z5GRU7l58bNFSkPTFN6Yl12dsUlAZ8xy98g=
|
||||
github.com/ProtonMail/go-crypto v1.0.0 h1:LRuvITjQWX+WIfr930YHG2HNfjR1uOfyf5vE0kC2U78=
|
||||
github.com/ProtonMail/go-crypto v1.0.0/go.mod h1:EjAoLdwvbIOoOQr3ihjnSoLZRtE8azugULFRteWMNc0=
|
||||
github.com/ProtonMail/go-crypto v1.1.3 h1:nRBOetoydLeUb4nHajyO2bKqMLfWQ/ZPwkXqXxPxCFk=
|
||||
github.com/ProtonMail/go-crypto v1.1.3/go.mod h1:rA3QumHc/FZ8pAHreoekgiAbzpNsfQAosU5td4SnOrE=
|
||||
github.com/PuerkitoBio/purell v1.1.1/go.mod h1:c11w/QuzBsJSee3cPx9rAFu61PvFxuPbtSwDGJws/X0=
|
||||
github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578/go.mod h1:uGdkoq3SwY9Y+13GIhn11/XLaGBb4BfwItxLd5jeuXE=
|
||||
github.com/RocketChat/Rocket.Chat.Go.SDK v0.0.0-20210112200207-10ab4d695d60 h1:prBTRx78AQnXzivNT9Crhu564W/zPPr3ibSlpT9xKcE=
|
||||
@@ -695,8 +695,8 @@ github.com/apache/thrift v0.12.0/go.mod h1:cp2SuWMxlEZw2r+iP2GNCdIi4C1qmUzdZFSVb
|
||||
github.com/apache/thrift v0.13.0/go.mod h1:cp2SuWMxlEZw2r+iP2GNCdIi4C1qmUzdZFSVb+bacwQ=
|
||||
github.com/apache/thrift v0.16.0/go.mod h1:PHK3hniurgQaNMZYaCLEqXKsYK8upmhPbmdP2FXSqgU=
|
||||
github.com/appscode/go v0.0.0-20191119085241-0887d8ec2ecc/go.mod h1:OawnOmAL4ZX3YaPdN+8HTNwBveT1jMsqP74moa9XUbE=
|
||||
github.com/argoproj/gitops-engine v0.7.1-0.20240615185936-83ce6ca8cedc h1:J7LJp2Gh9A9/eQN7Lg74JW+YOVO5NEjq5/cudGAiOwk=
|
||||
github.com/argoproj/gitops-engine v0.7.1-0.20240615185936-83ce6ca8cedc/go.mod h1:ByLmH5B1Gs361tgI5x5f8oSFuBEXDYENYpG3zFDWtHU=
|
||||
github.com/argoproj/gitops-engine v0.7.1-0.20250129155113-faf5a4e5c37d h1:LrHPuKm4rFfaVzNOqXhuoLNqe7DnhZ3d5pZA+k431Bo=
|
||||
github.com/argoproj/gitops-engine v0.7.1-0.20250129155113-faf5a4e5c37d/go.mod h1:xMIbuLg9Qj2e0egTy+8NcukbhRaVmWwK9vm3aAQZoi4=
|
||||
github.com/argoproj/notifications-engine v0.4.1-0.20240606074338-0802cd427621 h1:Yg1nt+D2uDK1SL2jSlfukA4yc7db184TTN7iWy3voRE=
|
||||
github.com/argoproj/notifications-engine v0.4.1-0.20240606074338-0802cd427621/go.mod h1:N0A4sEws2soZjEpY4hgZpQS8mRIEw6otzwfkgc3g9uQ=
|
||||
github.com/argoproj/pkg v0.13.7-0.20230626144333-d56162821bd1 h1:qsHwwOJ21K2Ao0xPju1sNuqphyMnMYkyB3ZLoLtxWpo=
|
||||
@@ -828,8 +828,8 @@ 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.18 h1:n56/Zwd5o6whRC5PMGretI4IdRLlmBXYNjScPaBgsbY=
|
||||
github.com/creack/pty v1.1.18/go.mod h1:MOBLtS5ELjhRRrroQr9kyvTxUAFNvYEK993ew/Vr4O4=
|
||||
github.com/cyphar/filepath-securejoin v0.2.4 h1:Ugdm7cg7i6ZK6x3xDF1oEu1nfkyfH53EtKeQYTC3kyg=
|
||||
github.com/cyphar/filepath-securejoin v0.2.4/go.mod h1:aPGpWjXOXUn2NCNjFvBE6aRxGGx79pTxQpKOJNYHHl4=
|
||||
github.com/cyphar/filepath-securejoin v0.3.6 h1:4d9N5ykBnSp5Xn2JkhocYDkOpURL/18CYMpo6xB9uWM=
|
||||
github.com/cyphar/filepath-securejoin v0.3.6/go.mod h1:Sdj7gXlvMcPZsbhwhQ33GguGLDGQL7h7bg04C/+u9jI=
|
||||
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=
|
||||
@@ -843,6 +843,8 @@ github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f h1:lO4WD4F/r
|
||||
github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f/go.mod h1:cuUVRXasLTGF7a8hSLbxyZXjz+1KgoB3wDUb6vlszIc=
|
||||
github.com/distribution/reference v0.5.0 h1:/FUIFXtfc/x2gpa5/VGfiGLuOIdYa1t65IKK2OFGvA0=
|
||||
github.com/distribution/reference v0.5.0/go.mod h1:BbU0aIcezP1/5jX/8MP0YiH4SdvB5Y4f/wlDRiLyi3E=
|
||||
github.com/dlclark/regexp2 v1.11.2 h1:/u628IuisSTwri5/UKloiIsH8+qF2Pu7xEQX+yIKg68=
|
||||
github.com/dlclark/regexp2 v1.11.2/go.mod h1:DHkYz0B9wPfa6wondMfaivmHpzrQ3v9q8cnmRbL6yW8=
|
||||
github.com/dnaeon/go-vcr v1.1.0 h1:ReYa/UBrRyQdant9B4fNHGoCNKw6qh6P0fsdGmZpR7c=
|
||||
github.com/dnaeon/go-vcr v1.1.0/go.mod h1:M7tiix8f0r6mKKJ3Yq/kqU1OYf3MnfmBWVbPx/yU9ko=
|
||||
github.com/docopt/docopt-go v0.0.0-20180111231733-ee0de3bc6815/go.mod h1:WwZ+bS3ebgob9U8Nd0kOddGdZWjyMGR8Wziv+TBNwSE=
|
||||
@@ -854,8 +856,8 @@ github.com/eapache/go-resiliency v1.1.0/go.mod h1:kFI+JgMyC7bLPUVY133qvEBtVayf5m
|
||||
github.com/eapache/go-xerial-snappy v0.0.0-20180814174437-776d5712da21/go.mod h1:+020luEh2TKB4/GOp8oxxtq0Daoen/Cii55CzbTV6DU=
|
||||
github.com/eapache/queue v1.1.0/go.mod h1:6eCeP0CKFpHLu8blIFXhExK/dRa7WDZfr6jVFPTqq+I=
|
||||
github.com/edsrzf/mmap-go v1.0.0/go.mod h1:YO35OhQPt3KJa3ryjFM5Bs14WD66h8eGKpfaBNrHW5M=
|
||||
github.com/elazarl/goproxy v0.0.0-20230808193330-2592e75ae04a h1:mATvB/9r/3gvcejNsXKSkQ6lcIaNec2nyfOdlTBR2lU=
|
||||
github.com/elazarl/goproxy v0.0.0-20230808193330-2592e75ae04a/go.mod h1:Ro8st/ElPeALwNFlcTpWmkr6IoMFfkjXAvTHpevnDsM=
|
||||
github.com/elazarl/goproxy v1.2.3 h1:xwIyKHbaP5yfT6O9KIeYJR5549MXRQkoQMRXGztz8YQ=
|
||||
github.com/elazarl/goproxy v1.2.3/go.mod h1:YfEbZtqP4AetfO6d40vWchF3znWX7C7Vd6ZMfdL8z64=
|
||||
github.com/emicklei/go-restful/v3 v3.8.0/go.mod h1:6n3XBCmQQb25CM2LCACGz8ukIrRry+4bhvbpWn3mrbc=
|
||||
github.com/emicklei/go-restful/v3 v3.11.0 h1:rAQeMHw1c7zTmncogyy8VvRZwtkmkZ4FxERmMY4rD+g=
|
||||
github.com/emicklei/go-restful/v3 v3.11.0/go.mod h1:6n3XBCmQQb25CM2LCACGz8ukIrRry+4bhvbpWn3mrbc=
|
||||
@@ -892,6 +894,8 @@ github.com/facebookgo/subset v0.0.0-20150612182917-8dac2c3c4870/go.mod h1:5tD+ne
|
||||
github.com/fatih/camelcase v1.0.0 h1:hxNvNX/xYBp0ovncs8WyWZrOrpBNub/JfaMvbURyft8=
|
||||
github.com/fatih/camelcase v1.0.0/go.mod h1:yN2Sb0lFhZJUdVvtELVWefmrXpuZESvPmqwoZc+/fpc=
|
||||
github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4=
|
||||
github.com/fatih/color v1.16.0 h1:zmkK9Ngbjj+K0yRhTVONQh1p/HknKYSlNT+vZCzyokM=
|
||||
github.com/fatih/color v1.16.0/go.mod h1:fL2Sau1YI5c0pdGEVCbKQbLXB6edEj1ZgiY4NijnWvE=
|
||||
github.com/felixge/httpsnoop v1.0.1/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U=
|
||||
github.com/felixge/httpsnoop v1.0.3 h1:s/nj+GCswXYzN5v2DpNMuMQYe+0DDwt5WVCU6CWBdXk=
|
||||
github.com/felixge/httpsnoop v1.0.3/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U=
|
||||
@@ -916,8 +920,8 @@ github.com/gin-contrib/sse v0.1.0 h1:Y/yl/+YNO8GZSjAhjMsSuLt29uWRFHdHYUb5lYOV9qE
|
||||
github.com/gin-contrib/sse v0.1.0/go.mod h1:RHrZQHXnP2xjPF+u1gW/2HnVO7nvIa9PG3Gm+fLHvGI=
|
||||
github.com/gin-gonic/gin v1.6.3 h1:ahKqKTFpO5KTPHxWZjEdPScmYaGtLo8Y4DMHoEsnp14=
|
||||
github.com/gin-gonic/gin v1.6.3/go.mod h1:75u5sXoLsGZoRN5Sgbi1eraJ4GU3++wFwWzhwvtwp4M=
|
||||
github.com/gliderlabs/ssh v0.3.7 h1:iV3Bqi942d9huXnzEF2Mt+CY9gLu8DNM4Obd+8bODRE=
|
||||
github.com/gliderlabs/ssh v0.3.7/go.mod h1:zpHEXBstFnQYtGnB8k8kQLol82umzn/2/snG7alWVD8=
|
||||
github.com/gliderlabs/ssh v0.3.8 h1:a4YXD1V7xMF9g5nTkdfnja3Sxy1PVDCj1Zg4Wb8vY6c=
|
||||
github.com/gliderlabs/ssh v0.3.8/go.mod h1:xYoytBv1sV0aL3CavoDuJIQNURXkkfPA/wxQ1pL1fAU=
|
||||
github.com/go-errors/errors v1.4.2 h1:J6MZopCL4uSllY1OfXM374weqZFFItUbrImctkmUxIA=
|
||||
github.com/go-errors/errors v1.4.2/go.mod h1:sIVyrIiJhuEF+Pj9Ebtd6P/rEYROXFi3BopGUQ5a5Og=
|
||||
github.com/go-fed/httpsig v1.1.0 h1:9M+hb0jkEICD8/cAiNqEB66R87tTINszBRTjwjQzWcI=
|
||||
@@ -929,12 +933,12 @@ github.com/go-fonts/liberation v0.2.0/go.mod h1:K6qoJYypsmfVjWg8KOVDQhLc8UDgIK2H
|
||||
github.com/go-fonts/stix v0.1.0/go.mod h1:w/c1f0ldAUlJmLBvlbkvVXLAD+tAMqobIIQpmnUIzUY=
|
||||
github.com/go-git/gcfg v1.5.1-0.20230307220236-3a3c6141e376 h1:+zs/tPmkDkHx3U66DAb0lQFJrpS6731Oaa12ikc+DiI=
|
||||
github.com/go-git/gcfg v1.5.1-0.20230307220236-3a3c6141e376/go.mod h1:an3vInlBmSxCcxctByoQdvwPiA7DTK7jaaFDBTtu0ic=
|
||||
github.com/go-git/go-billy/v5 v5.5.0 h1:yEY4yhzCDuMGSv83oGxiBotRzhwhNr8VZyphhiu+mTU=
|
||||
github.com/go-git/go-billy/v5 v5.5.0/go.mod h1:hmexnoNsr2SJU1Ju67OaNz5ASJY3+sHgFRpCtpDCKow=
|
||||
github.com/go-git/go-billy/v5 v5.6.1 h1:u+dcrgaguSSkbjzHwelEjc0Yj300NUevrrPphk/SoRA=
|
||||
github.com/go-git/go-billy/v5 v5.6.1/go.mod h1:0AsLr1z2+Uksi4NlElmMblP5rPcDZNRCD8ujZCRR2BE=
|
||||
github.com/go-git/go-git-fixtures/v4 v4.3.2-0.20231010084843-55a94097c399 h1:eMje31YglSBqCdIqdhKBW8lokaMrL3uTkpGYlE2OOT4=
|
||||
github.com/go-git/go-git-fixtures/v4 v4.3.2-0.20231010084843-55a94097c399/go.mod h1:1OCfN199q1Jm3HZlxleg+Dw/mwps2Wbk9frAWm+4FII=
|
||||
github.com/go-git/go-git/v5 v5.12.0 h1:7Md+ndsjrzZxbddRDZjF14qK+NN56sy6wkqaVrjZtys=
|
||||
github.com/go-git/go-git/v5 v5.12.0/go.mod h1:FTM9VKtnI2m65hNI/TenDDDnUf2Q9FHnXYjuz9i5OEY=
|
||||
github.com/go-git/go-git/v5 v5.13.1 h1:DAQ9APonnlvSWpvolXWIuV6Q6zXy2wHbN4cVlNR5Q+M=
|
||||
github.com/go-git/go-git/v5 v5.13.1/go.mod h1:qryJB4cSBoq3FRoBRf5A77joojuBcmPJ0qu3XXXVixc=
|
||||
github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU=
|
||||
github.com/go-gl/glfw/v3.3/glfw v0.0.0-20191125211704-12ad95a8df72/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8=
|
||||
github.com/go-gl/glfw/v3.3/glfw v0.0.0-20200222043503-6f7a984d4dc4/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8=
|
||||
@@ -1242,14 +1246,14 @@ github.com/hashicorp/go-cleanhttp v0.5.0/go.mod h1:JpRdi6/HCYpAwUzNwuwqhbovhLtng
|
||||
github.com/hashicorp/go-cleanhttp v0.5.1/go.mod h1:JpRdi6/HCYpAwUzNwuwqhbovhLtngrth3wmdIIUrZ80=
|
||||
github.com/hashicorp/go-cleanhttp v0.5.2 h1:035FKYIWjmULyFRBKPs8TBQoi0x6d9G4xc9neXJWAZQ=
|
||||
github.com/hashicorp/go-cleanhttp v0.5.2/go.mod h1:kO/YDlP8L1346E6Sodw+PrpBSV4/SoxCXGY6BqNFT48=
|
||||
github.com/hashicorp/go-hclog v0.9.2 h1:CG6TE5H9/JXsFWJCfoIVpKFIkFe6ysEuHirp4DxCsHI=
|
||||
github.com/hashicorp/go-hclog v0.9.2/go.mod h1:5CU+agLiy3J7N7QjHK5d05KxGsuXiQLrjA0H7acj2lQ=
|
||||
github.com/hashicorp/go-hclog v1.6.3 h1:Qr2kF+eVWjTiYmU7Y31tYlP1h0q/X3Nl3tPGdaB11/k=
|
||||
github.com/hashicorp/go-hclog v1.6.3/go.mod h1:W4Qnvbt70Wk/zYJryRzDRU/4r0kIg0PVHBcfoyhpF5M=
|
||||
github.com/hashicorp/go-immutable-radix v1.0.0/go.mod h1:0y9vanUI8NX6FsYoO3zeMjhV/C5i9g4Q3DwcSNZ4P60=
|
||||
github.com/hashicorp/go-msgpack v0.5.3/go.mod h1:ahLV/dePpqEmjfWmKiqvPkv/twdG7iPBM1vqhUKIvfM=
|
||||
github.com/hashicorp/go-multierror v1.0.0/go.mod h1:dHtQlpGsu+cZNNAkkCN/P3hoUDHhCYQXV3UM06sGGrk=
|
||||
github.com/hashicorp/go-retryablehttp v0.5.1/go.mod h1:9B5zBasrRhHXnJnui7y6sL7es7NDiJgTc6Er0maI1Xs=
|
||||
github.com/hashicorp/go-retryablehttp v0.7.4 h1:ZQgVdpTdAL7WpMIwLzCfbalOcSUdkDZnpUv3/+BxzFA=
|
||||
github.com/hashicorp/go-retryablehttp v0.7.4/go.mod h1:Jy/gPYAdjqffZ/yFGCFV2doI5wjtH1ewM9u8iYVjtX8=
|
||||
github.com/hashicorp/go-retryablehttp v0.7.7 h1:C8hUCYzor8PIfXHa4UrZkU4VvK8o9ISHxT2Q8+VepXU=
|
||||
github.com/hashicorp/go-retryablehttp v0.7.7/go.mod h1:pkQpWZeYWskR+D1tR2O5OcBFOxfA7DoAO6xtkuQnHTk=
|
||||
github.com/hashicorp/go-rootcerts v1.0.0/go.mod h1:K6zTfqpRlCUIjkwsN4Z+hiSfzSTQa6eBIzfwKfwNnHU=
|
||||
github.com/hashicorp/go-sockaddr v1.0.0/go.mod h1:7Xibr9yA9JjQq1JpNB2Vw7kxv8xerXegt+ozgdvDeDU=
|
||||
github.com/hashicorp/go-syslog v1.0.0/go.mod h1:qPfqrKkXGihmCqbJM2mZgkZGvKG1dFdvsLplgctolz4=
|
||||
@@ -1383,13 +1387,15 @@ github.com/markbates/oncer v0.0.0-20181203154359-bf2de49a0be2/go.mod h1:Ld9puTsI
|
||||
github.com/markbates/safe v1.0.1/go.mod h1:nAqgmRi7cY2nqMc92/bSEeQA+R4OheNU2T1kNSCBdG0=
|
||||
github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU=
|
||||
github.com/mattn/go-colorable v0.1.2/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE=
|
||||
github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA=
|
||||
github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg=
|
||||
github.com/mattn/go-isatty v0.0.3/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4=
|
||||
github.com/mattn/go-isatty v0.0.4/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4=
|
||||
github.com/mattn/go-isatty v0.0.8/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s=
|
||||
github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU=
|
||||
github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM=
|
||||
github.com/mattn/go-isatty v0.0.19 h1:JITubQf0MOLdlGRuRq+jtsDlekdYPia9ZFsB8h/APPA=
|
||||
github.com/mattn/go-isatty v0.0.19/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
|
||||
github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY=
|
||||
github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
|
||||
github.com/mattn/go-runewidth v0.0.2/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU=
|
||||
github.com/mattn/go-runewidth v0.0.4/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU=
|
||||
github.com/mattn/go-runewidth v0.0.9/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI=
|
||||
@@ -1521,8 +1527,8 @@ github.com/onsi/gomega v1.27.7/go.mod h1:1p8OOlwo2iUUDsHnOrjE5UKYJ+e3W8eQ3qSlRah
|
||||
github.com/onsi/gomega v1.27.8/go.mod h1:2J8vzI/s+2shY9XHRApDkdgPo1TKT7P2u6fXeJKFnNQ=
|
||||
github.com/onsi/gomega v1.27.10/go.mod h1:RsS8tutOdbdgzbPtzzATp12yT7kM5I5aElG3evPbQ0M=
|
||||
github.com/onsi/gomega v1.29.0/go.mod h1:9sxs+SwGrKI0+PWe4Fxa9tFQQBG5xSsSbMXOI8PPpoQ=
|
||||
github.com/onsi/gomega v1.30.0 h1:hvMK7xYz4D3HapigLTeGdId/NcfQx1VHMJc60ew99+8=
|
||||
github.com/onsi/gomega v1.30.0/go.mod h1:9sxs+SwGrKI0+PWe4Fxa9tFQQBG5xSsSbMXOI8PPpoQ=
|
||||
github.com/onsi/gomega v1.34.1 h1:EUMJIKUjM8sKjYbtxQI9A4z2o+rruxnzNvpknOXie6k=
|
||||
github.com/onsi/gomega v1.34.1/go.mod h1:kU1QgUvBDLXBJq618Xvm2LUX6rSAfRaFRTcdOeDLwwY=
|
||||
github.com/op/go-logging v0.0.0-20160315200505-970db520ece7/go.mod h1:HzydrMdWErDVzsI23lYNej1Htcns9BCg93Dk0bBINWk=
|
||||
github.com/opencontainers/go-digest v1.0.0 h1:apOUWs51W5PlhuyGyz9FCeeBIOUDA/6nW8Oi/yOhh5U=
|
||||
github.com/opencontainers/go-digest v1.0.0/go.mod h1:0JzlMkj0TRzQZfJkVvzbP0HBR3IKzErnv2BNG4W4MAM=
|
||||
@@ -1621,8 +1627,8 @@ github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFR
|
||||
github.com/rogpeppe/go-internal v1.6.1/go.mod h1:xXDCJY+GAPziupqXw64V24skbSoqbTEfhy4qGm1nDQc=
|
||||
github.com/rogpeppe/go-internal v1.9.0/go.mod h1:WtVeX8xhTBvf0smdhujwtBcq4Qrzq/fJaraNFVN+nFs=
|
||||
github.com/rogpeppe/go-internal v1.10.0/go.mod h1:UQnix2H7Ngw/k4C5ijL5+65zddjncjaFoBhdsK/akog=
|
||||
github.com/rogpeppe/go-internal v1.11.0 h1:cWPaGQEPrBb5/AsnsZesgZZ9yb1OQ+GOISoDNXVBh4M=
|
||||
github.com/rogpeppe/go-internal v1.11.0/go.mod h1:ddIwULY96R17DhadqLgMfk9H9tvdUzkipdSkR5nkCZA=
|
||||
github.com/rogpeppe/go-internal v1.12.0 h1:exVL4IDcn6na9z1rAb56Vxr+CgyK3nn3O+epU5NdKM8=
|
||||
github.com/rogpeppe/go-internal v1.12.0/go.mod h1:E+RYuTGaKKdloAfM02xzb0FW3Paa99yedzYV+kq4uf4=
|
||||
github.com/rs/cors v1.7.0/go.mod h1:gFx+x8UowdsKA9AchylcLynDq+nNFfI8FkUZdN/jGCU=
|
||||
github.com/rs/cors v1.9.0 h1:l9HGsTsHJcvW14Nk7J9KFz8bzeAWXn3CG6bgt7LsrAE=
|
||||
github.com/rs/cors v1.9.0/go.mod h1:XyqrcTp5zjWr1wsJ8PIRZssZ8b/WMcMf71DJnit4EMU=
|
||||
@@ -1650,8 +1656,8 @@ github.com/sirupsen/logrus v1.8.1/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic
|
||||
github.com/sirupsen/logrus v1.9.2/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ=
|
||||
github.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ=
|
||||
github.com/sirupsen/logrus v1.9.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ=
|
||||
github.com/skeema/knownhosts v1.2.2 h1:Iug2P4fLmDw9f41PB6thxUkNUkJzB5i+1/exaj40L3A=
|
||||
github.com/skeema/knownhosts v1.2.2/go.mod h1:xYbVRSPxqBZFrdmDyMmsOs+uX1UZC3nTN3ThzgDxUwo=
|
||||
github.com/skeema/knownhosts v1.3.0 h1:AM+y0rI04VksttfwjkSTNQorvGqmwATnvnAHpSgc0LY=
|
||||
github.com/skeema/knownhosts v1.3.0/go.mod h1:sPINvnADmT/qYH1kfv+ePMmOBTH6Tbl7b5LvTDjFK7M=
|
||||
github.com/skratchdot/open-golang v0.0.0-20160302144031-75fb7ed4208c h1:fyKiXKO1/I/B6Y2U8T7WdQGWzwehOuGIrljPtt7YTTI=
|
||||
github.com/skratchdot/open-golang v0.0.0-20160302144031-75fb7ed4208c/go.mod h1:sUM3LWHvSMaG192sy56D9F7CNvL7jUJVXoqM1QKLnog=
|
||||
github.com/slack-go/slack v0.12.2 h1:x3OppyMyGIbbiyFhsBmpf9pwkUzMhthJMRNmNlA4LaQ=
|
||||
@@ -1701,8 +1707,8 @@ github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO
|
||||
github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
|
||||
github.com/stretchr/testify v1.8.2/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
|
||||
github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
|
||||
github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg=
|
||||
github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
|
||||
github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA=
|
||||
github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
|
||||
github.com/tidwall/gjson v1.14.4 h1:uo0p8EbA09J7RQaflQ1aBRffTR7xedD2bcIVSYxLnkM=
|
||||
github.com/tidwall/gjson v1.14.4/go.mod h1:/wbyibRr2FHMks5tjHJ5F8dMZh3AcwJEMf5vlfC0lxk=
|
||||
github.com/tidwall/match v1.1.1 h1:+Ho715JplO36QYgwN9PGYNhgZvoUSc9X2c80KVTi+GA=
|
||||
@@ -1843,8 +1849,8 @@ golang.org/x/crypto v0.14.0/go.mod h1:MVFd36DqK4CsrnJYDkBA3VC4m2GkXAM0PvzMCn4JQf
|
||||
golang.org/x/crypto v0.16.0/go.mod h1:gCAAfMLgwOJRpTjQ2zCCt2OcSfYMTeZVSRtQlPC7Nq4=
|
||||
golang.org/x/crypto v0.19.0/go.mod h1:Iy9bg/ha4yyC70EfRS8jz+B6ybOBKMaSxLj6P6oBDfU=
|
||||
golang.org/x/crypto v0.21.0/go.mod h1:0BP7YvVV9gBbVKyeTG0Gyn+gZm94bibOW5BjDEYAOMs=
|
||||
golang.org/x/crypto v0.23.0 h1:dIJU/v2J8Mdglj/8rJ6UUOM3Zc9zLZxVZwwxMooUSAI=
|
||||
golang.org/x/crypto v0.23.0/go.mod h1:CKFgDieR+mRhux2Lsu27y0fO304Db0wZe70UKqHu0v8=
|
||||
golang.org/x/crypto v0.31.0 h1:ihbySMvVjLAeSH1IbfcRTkD/iNscyz8rGzjF/E5hV6U=
|
||||
golang.org/x/crypto v0.31.0/go.mod h1:kDsLvtWBEx7MV9tJOj9bnXsPbxwJQ6csT/x4KIN4Ssk=
|
||||
golang.org/x/exp v0.0.0-20180321215751-8460e604b9de/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
|
||||
golang.org/x/exp v0.0.0-20180807140117-3d87b88a115f/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
|
||||
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
|
||||
@@ -1861,8 +1867,8 @@ golang.org/x/exp v0.0.0-20200207192155-f17229e696bd/go.mod h1:J/WKrq2StrnmMY6+EH
|
||||
golang.org/x/exp v0.0.0-20200224162631-6cc2880d07d6/go.mod h1:3jZMyOhIsHpP37uCMkUooju7aAi5cS1Q23tOzKc+0MU=
|
||||
golang.org/x/exp v0.0.0-20200331195152-e8c3332aa8e5/go.mod h1:4M0jN8W1tt0AVLNr8HDosyJCDCDuyL9N9+3m7wDWgKw=
|
||||
golang.org/x/exp v0.0.0-20220827204233-334a2380cb91/go.mod h1:cyybsKvd6eL0RnXn6p/Grxp8F5bW7iYuBgsNCOHpMYE=
|
||||
golang.org/x/exp v0.0.0-20230522175609-2e198f4a06a1 h1:k/i9J1pBpvlfR+9QsetwPyERsqu1GIbi967PQMq3Ivc=
|
||||
golang.org/x/exp v0.0.0-20230522175609-2e198f4a06a1/go.mod h1:V1LtkGg67GoY2N1AnLN78QLrzxkLyJw7RJb1gzOOz9w=
|
||||
golang.org/x/exp v0.0.0-20240719175910-8a7402abbf56 h1:2dVuKD2vS7b0QIHQbpyTISPd0LeHDbnYEryqj5Q1ug8=
|
||||
golang.org/x/exp v0.0.0-20240719175910-8a7402abbf56/go.mod h1:M4RDyNAINzryxdtnbRXRL/OHtkFuWGRjvuhBJpk2IlY=
|
||||
golang.org/x/image v0.0.0-20180708004352-c73c2afc3b81/go.mod h1:ux5Hcp/YLpHSI86hEcLt0YII63i6oz57MZXIpbrjZUs=
|
||||
golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js=
|
||||
golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0=
|
||||
@@ -1909,8 +1915,9 @@ golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
|
||||
golang.org/x/mod v0.9.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
|
||||
golang.org/x/mod v0.10.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
|
||||
golang.org/x/mod v0.12.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
|
||||
golang.org/x/mod v0.14.0 h1:dGoOF9QVLYng8IHTm7BAyWqCqSheQ5pYWGhzW00YJr0=
|
||||
golang.org/x/mod v0.14.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c=
|
||||
golang.org/x/mod v0.19.0 h1:fEdghXQSo20giMthA7cd28ZC+jts4amQ3YMXiP5oMQ8=
|
||||
golang.org/x/mod v0.19.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c=
|
||||
golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
@@ -1995,8 +2002,8 @@ golang.org/x/net v0.17.0/go.mod h1:NxSsAGuq816PNPmqtQdLE42eU2Fs7NoRIZrHJAlaCOE=
|
||||
golang.org/x/net v0.19.0/go.mod h1:CfAk/cbD4CthTvqiEl8NpboMuiuOYsAr/7NOjZJtv1U=
|
||||
golang.org/x/net v0.21.0/go.mod h1:bIjVDfnllIU7BJ2DNgfnXvpSvtn8VRwhlsaeUTyUS44=
|
||||
golang.org/x/net v0.23.0/go.mod h1:JKghWKKOSdJwpW2GEx0Ja7fmaKnMsbu+MWVZTokSYmg=
|
||||
golang.org/x/net v0.25.0 h1:d/OCCoBEUq33pjydKrGQhw7IlUPI2Oylr+8qLx49kac=
|
||||
golang.org/x/net v0.25.0/go.mod h1:JkAGAh7GEvH74S6FOH42FLoXpXbE/aqXSrIQjXgsiwM=
|
||||
golang.org/x/net v0.33.0 h1:74SYHlV8BIgHIFC/LrYkOGIwL19eTYXQ5wc6TBuO36I=
|
||||
golang.org/x/net v0.33.0/go.mod h1:HXLR5J+9DxmrqMwG9qjGCxZ+zKXxBru04zlTvWlWuN4=
|
||||
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
|
||||
golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
|
||||
golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
|
||||
@@ -2050,8 +2057,9 @@ golang.org/x/sync v0.0.0-20220929204114-8fcdb60fdcc0/go.mod h1:RxMgew5VJxzue5/jJ
|
||||
golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.2.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.3.0/go.mod h1:FU7BRWz2tNW+3quACPkgCx/L+uEAv1htQ0V83Z9Rj+Y=
|
||||
golang.org/x/sync v0.5.0 h1:60k92dhOjHxJkrqnwsfl8KuaHbn/5dl0lUPUklKo3qE=
|
||||
golang.org/x/sync v0.5.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
|
||||
golang.org/x/sync v0.10.0 h1:3NQrjDixjgGwUOCaF8w2+VYHv0Ve/vGYSbdkTa98gmQ=
|
||||
golang.org/x/sync v0.10.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
|
||||
golang.org/x/sys v0.0.0-20180823144017-11551d06cbcc/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
@@ -2174,8 +2182,8 @@ golang.org/x/sys v0.13.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.15.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||
golang.org/x/sys v0.17.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||
golang.org/x/sys v0.18.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||
golang.org/x/sys v0.20.0 h1:Od9JTbYCk261bKm4M/mw7AklTlFYIa0bIp9BgSm1S8Y=
|
||||
golang.org/x/sys v0.20.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||
golang.org/x/sys v0.28.0 h1:Fksou7UEQUWlKvIdsqzJmUmCX3cZuD2+P3XyyzwMhlA=
|
||||
golang.org/x/sys v0.28.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
||||
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
|
||||
golang.org/x/term v0.0.0-20220526004731-065cf7ba2467/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
|
||||
@@ -2195,8 +2203,8 @@ golang.org/x/term v0.13.0/go.mod h1:LTmsnFJwVN6bCy1rVCoS+qHT1HhALEFxKncY3WNNh4U=
|
||||
golang.org/x/term v0.15.0/go.mod h1:BDl952bC7+uMoWR75FIrCDx79TPU9oHkTZ9yRbYOrX0=
|
||||
golang.org/x/term v0.17.0/go.mod h1:lLRBjIVuehSbZlaOtGMbcMncT+aqLLLmKrsjNrUguwk=
|
||||
golang.org/x/term v0.18.0/go.mod h1:ILwASektA3OnRv7amZ1xhE/KTR+u50pbXfZ03+6Nx58=
|
||||
golang.org/x/term v0.20.0 h1:VnkxpohqXaOBYJtBmEppKUG6mXpi+4O6purfc2+sMhw=
|
||||
golang.org/x/term v0.20.0/go.mod h1:8UkIAJTvZgivsXaD6/pH6U9ecQzZ45awqEOzuCvwpFY=
|
||||
golang.org/x/term v0.27.0 h1:WP60Sv1nlK1T6SupCHbXzSaN0b9wUmsPoRS9b61A23Q=
|
||||
golang.org/x/term v0.27.0/go.mod h1:iMsnZpn0cago0GOrHO2+Y7u7JPn5AylBrcoWkElMTSM=
|
||||
golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
@@ -2218,8 +2226,8 @@ golang.org/x/text v0.11.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE=
|
||||
golang.org/x/text v0.12.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE=
|
||||
golang.org/x/text v0.13.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE=
|
||||
golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
|
||||
golang.org/x/text v0.15.0 h1:h1V/4gjBv8v9cjcR6+AR5+/cIYK5N/WAgiv4xlsEtAk=
|
||||
golang.org/x/text v0.15.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
|
||||
golang.org/x/text v0.21.0 h1:zyQAAkrwaneQ066sspRyJaG9VNi/YJ1NfzcGB3hZ/qo=
|
||||
golang.org/x/text v0.21.0/go.mod h1:4IBbMaMmOPCJ8SecivzSH54+73PCFmPWxNTLm+vZkEQ=
|
||||
golang.org/x/time v0.0.0-20180412165947-fbb02b2291d2/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
||||
golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
||||
golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
||||
@@ -2312,8 +2320,9 @@ golang.org/x/tools v0.9.1/go.mod h1:owI94Op576fPu3cIGQeHs3joujW/2Oc6MtlxbF5dfNc=
|
||||
golang.org/x/tools v0.9.3/go.mod h1:owI94Op576fPu3cIGQeHs3joujW/2Oc6MtlxbF5dfNc=
|
||||
golang.org/x/tools v0.12.0/go.mod h1:Sc0INKfu04TlqNoRA1hgpFZbhYXHPr4V5DzpSBTPqQM=
|
||||
golang.org/x/tools v0.13.0/go.mod h1:HvlwmtVNQAhOuCjW7xxvovg8wbNq7LwfXh/k7wXUl58=
|
||||
golang.org/x/tools v0.16.1 h1:TLyB3WofjdOEepBHAU20JdNC1Zbg87elYofWYAY5oZA=
|
||||
golang.org/x/tools v0.16.1/go.mod h1:kYVVN6I1mBNoB1OX+noeBjbRk4IUEPa7JJ+TJMEooJ0=
|
||||
golang.org/x/tools v0.23.0 h1:SGsXPZ+2l4JsgaCKkx+FQ9YZ5XEtA1GZYuoDjenLjvg=
|
||||
golang.org/x/tools v0.23.0/go.mod h1:pnu6ufv6vQkll6szChhK3C3L/ruaIv5eBeztNG8wtsI=
|
||||
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
|
||||
6
hack/update-supported-versions.sh
Normal file → Executable file
6
hack/update-supported-versions.sh
Normal file → Executable file
@@ -11,7 +11,11 @@ for n in 0 1 2; do
|
||||
minor_version_num=$((argocd_minor_version_num - n))
|
||||
minor_version="${argocd_major_version_num}.${minor_version_num}"
|
||||
git checkout "release-$minor_version" > /dev/null || exit 1
|
||||
line=$(yq '.jobs["test-e2e"].strategy.matrix["k3s-version"][]' .github/workflows/ci-build.yaml | \
|
||||
|
||||
line=$(yq '.jobs["test-e2e"].strategy.matrix |
|
||||
# k3s-version was an array prior to 2.12. This checks for the old format first and then falls back to the new format.
|
||||
(.["k3s-version"] // (.k3s | map(.version))) |
|
||||
.[]' .github/workflows/ci-build.yaml | \
|
||||
jq --arg minor_version "$minor_version" --raw-input --slurp --raw-output \
|
||||
'split("\n")[:-1] | map(sub("\\.[0-9]+$"; "")) | join(", ") | "| \($minor_version) | \(.) |"')
|
||||
out+="$line\n"
|
||||
|
||||
@@ -97,6 +97,24 @@ spec:
|
||||
name: argocd-cmd-params-cm
|
||||
key: controller.self.heal.timeout.seconds
|
||||
optional: true
|
||||
- name: ARGOCD_APPLICATION_CONTROLLER_SELF_HEAL_BACKOFF_TIMEOUT_SECONDS
|
||||
valueFrom:
|
||||
configMapKeyRef:
|
||||
name: argocd-cmd-params-cm
|
||||
key: controller.self.heal.backoff.timeout.seconds
|
||||
optional: true
|
||||
- name: ARGOCD_APPLICATION_CONTROLLER_SELF_HEAL_BACKOFF_FACTOR
|
||||
valueFrom:
|
||||
configMapKeyRef:
|
||||
name: argocd-cmd-params-cm
|
||||
key: controller.self.heal.backoff.factor
|
||||
optional: true
|
||||
- name: ARGOCD_APPLICATION_CONTROLLER_SELF_HEAL_BACKOFF_CAP_SECONDS
|
||||
valueFrom:
|
||||
configMapKeyRef:
|
||||
name: argocd-cmd-params-cm
|
||||
key: controller.self.heal.backoff.cap.seconds
|
||||
optional: true
|
||||
- name: ARGOCD_APPLICATION_CONTROLLER_REPO_SERVER_PLAINTEXT
|
||||
valueFrom:
|
||||
configMapKeyRef:
|
||||
|
||||
@@ -100,6 +100,24 @@ spec:
|
||||
name: argocd-cmd-params-cm
|
||||
key: controller.self.heal.timeout.seconds
|
||||
optional: true
|
||||
- name: ARGOCD_APPLICATION_CONTROLLER_SELF_HEAL_BACKOFF_TIMEOUT_SECONDS
|
||||
valueFrom:
|
||||
configMapKeyRef:
|
||||
name: argocd-cmd-params-cm
|
||||
key: controller.self.heal.backoff.timeout.seconds
|
||||
optional: true
|
||||
- name: ARGOCD_APPLICATION_CONTROLLER_SELF_HEAL_BACKOFF_FACTOR
|
||||
valueFrom:
|
||||
configMapKeyRef:
|
||||
name: argocd-cmd-params-cm
|
||||
key: controller.self.heal.backoff.factor
|
||||
optional: true
|
||||
- name: ARGOCD_APPLICATION_CONTROLLER_SELF_HEAL_BACKOFF_CAP_SECONDS
|
||||
valueFrom:
|
||||
configMapKeyRef:
|
||||
name: argocd-cmd-params-cm
|
||||
key: controller.self.heal.backoff.cap.seconds
|
||||
optional: true
|
||||
- name: ARGOCD_APPLICATION_CONTROLLER_REPO_SERVER_PLAINTEXT
|
||||
valueFrom:
|
||||
configMapKeyRef:
|
||||
|
||||
@@ -27,6 +27,8 @@ rules:
|
||||
- appprojects
|
||||
verbs:
|
||||
- get
|
||||
- list
|
||||
- watch
|
||||
- apiGroups:
|
||||
- argoproj.io
|
||||
resources:
|
||||
@@ -62,4 +64,4 @@ rules:
|
||||
verbs:
|
||||
- get
|
||||
- list
|
||||
- watch
|
||||
- watch
|
||||
|
||||
@@ -5,7 +5,7 @@ kind: Kustomization
|
||||
images:
|
||||
- name: quay.io/argoproj/argocd
|
||||
newName: quay.io/argoproj/argocd
|
||||
newTag: latest
|
||||
newTag: v2.12.10
|
||||
resources:
|
||||
- ./application-controller
|
||||
- ./dex
|
||||
|
||||
@@ -35,6 +35,8 @@ rules:
|
||||
- appprojects
|
||||
verbs:
|
||||
- get
|
||||
- list
|
||||
- watch
|
||||
- apiGroups:
|
||||
- ""
|
||||
resources:
|
||||
|
||||
@@ -115,6 +115,11 @@ spec:
|
||||
sync:
|
||||
description: Sync contains parameters for the operation
|
||||
properties:
|
||||
autoHealAttemptsCount:
|
||||
description: SelfHealAttemptsCount contains the number of auto-heal
|
||||
attempts
|
||||
format: int64
|
||||
type: integer
|
||||
dryRun:
|
||||
description: DryRun specifies to perform a `kubectl apply --dry-run`
|
||||
without actually performing the sync
|
||||
@@ -2537,6 +2542,11 @@ spec:
|
||||
sync:
|
||||
description: Sync contains parameters for the operation
|
||||
properties:
|
||||
autoHealAttemptsCount:
|
||||
description: SelfHealAttemptsCount contains the number
|
||||
of auto-heal attempts
|
||||
format: int64
|
||||
type: integer
|
||||
dryRun:
|
||||
description: DryRun specifies to perform a `kubectl apply
|
||||
--dry-run` without actually performing the sync
|
||||
@@ -20822,6 +20832,8 @@ rules:
|
||||
- appprojects
|
||||
verbs:
|
||||
- get
|
||||
- list
|
||||
- watch
|
||||
- apiGroups:
|
||||
- argoproj.io
|
||||
resources:
|
||||
@@ -21268,7 +21280,7 @@ spec:
|
||||
key: applicationsetcontroller.enable.scm.providers
|
||||
name: argocd-cmd-params-cm
|
||||
optional: true
|
||||
image: quay.io/argoproj/argocd:latest
|
||||
image: quay.io/argoproj/argocd:v2.12.10
|
||||
imagePullPolicy: Always
|
||||
name: argocd-applicationset-controller
|
||||
ports:
|
||||
@@ -21386,7 +21398,7 @@ spec:
|
||||
- argocd
|
||||
- admin
|
||||
- redis-initial-password
|
||||
image: quay.io/argoproj/argocd:latest
|
||||
image: quay.io/argoproj/argocd:v2.12.10
|
||||
imagePullPolicy: IfNotPresent
|
||||
name: secret-init
|
||||
securityContext:
|
||||
@@ -21639,7 +21651,7 @@ spec:
|
||||
value: /helm-working-dir
|
||||
- name: HELM_DATA_HOME
|
||||
value: /helm-working-dir
|
||||
image: quay.io/argoproj/argocd:latest
|
||||
image: quay.io/argoproj/argocd:v2.12.10
|
||||
imagePullPolicy: Always
|
||||
livenessProbe:
|
||||
failureThreshold: 3
|
||||
@@ -21691,7 +21703,7 @@ spec:
|
||||
- -n
|
||||
- /usr/local/bin/argocd
|
||||
- /var/run/argocd/argocd-cmp-server
|
||||
image: quay.io/argoproj/argocd:latest
|
||||
image: quay.io/argoproj/argocd:v2.12.10
|
||||
name: copyutil
|
||||
securityContext:
|
||||
allowPrivilegeEscalation: false
|
||||
@@ -21855,6 +21867,24 @@ spec:
|
||||
key: controller.self.heal.timeout.seconds
|
||||
name: argocd-cmd-params-cm
|
||||
optional: true
|
||||
- name: ARGOCD_APPLICATION_CONTROLLER_SELF_HEAL_BACKOFF_TIMEOUT_SECONDS
|
||||
valueFrom:
|
||||
configMapKeyRef:
|
||||
key: controller.self.heal.backoff.timeout.seconds
|
||||
name: argocd-cmd-params-cm
|
||||
optional: true
|
||||
- name: ARGOCD_APPLICATION_CONTROLLER_SELF_HEAL_BACKOFF_FACTOR
|
||||
valueFrom:
|
||||
configMapKeyRef:
|
||||
key: controller.self.heal.backoff.factor
|
||||
name: argocd-cmd-params-cm
|
||||
optional: true
|
||||
- name: ARGOCD_APPLICATION_CONTROLLER_SELF_HEAL_BACKOFF_CAP_SECONDS
|
||||
valueFrom:
|
||||
configMapKeyRef:
|
||||
key: controller.self.heal.backoff.cap.seconds
|
||||
name: argocd-cmd-params-cm
|
||||
optional: true
|
||||
- name: ARGOCD_APPLICATION_CONTROLLER_REPO_SERVER_PLAINTEXT
|
||||
valueFrom:
|
||||
configMapKeyRef:
|
||||
@@ -21963,7 +21993,7 @@ spec:
|
||||
key: controller.ignore.normalizer.jq.timeout
|
||||
name: argocd-cmd-params-cm
|
||||
optional: true
|
||||
image: quay.io/argoproj/argocd:latest
|
||||
image: quay.io/argoproj/argocd:v2.12.10
|
||||
imagePullPolicy: Always
|
||||
name: argocd-application-controller
|
||||
ports:
|
||||
|
||||
@@ -12,4 +12,4 @@ resources:
|
||||
images:
|
||||
- name: quay.io/argoproj/argocd
|
||||
newName: quay.io/argoproj/argocd
|
||||
newTag: latest
|
||||
newTag: v2.12.10
|
||||
|
||||
@@ -114,6 +114,11 @@ spec:
|
||||
sync:
|
||||
description: Sync contains parameters for the operation
|
||||
properties:
|
||||
autoHealAttemptsCount:
|
||||
description: SelfHealAttemptsCount contains the number of auto-heal
|
||||
attempts
|
||||
format: int64
|
||||
type: integer
|
||||
dryRun:
|
||||
description: DryRun specifies to perform a `kubectl apply --dry-run`
|
||||
without actually performing the sync
|
||||
@@ -2536,6 +2541,11 @@ spec:
|
||||
sync:
|
||||
description: Sync contains parameters for the operation
|
||||
properties:
|
||||
autoHealAttemptsCount:
|
||||
description: SelfHealAttemptsCount contains the number
|
||||
of auto-heal attempts
|
||||
format: int64
|
||||
type: integer
|
||||
dryRun:
|
||||
description: DryRun specifies to perform a `kubectl apply
|
||||
--dry-run` without actually performing the sync
|
||||
|
||||
@@ -12,7 +12,7 @@ patches:
|
||||
images:
|
||||
- name: quay.io/argoproj/argocd
|
||||
newName: quay.io/argoproj/argocd
|
||||
newTag: latest
|
||||
newTag: v2.12.10
|
||||
resources:
|
||||
- ../../base/application-controller
|
||||
- ../../base/applicationset-controller
|
||||
|
||||
@@ -115,6 +115,11 @@ spec:
|
||||
sync:
|
||||
description: Sync contains parameters for the operation
|
||||
properties:
|
||||
autoHealAttemptsCount:
|
||||
description: SelfHealAttemptsCount contains the number of auto-heal
|
||||
attempts
|
||||
format: int64
|
||||
type: integer
|
||||
dryRun:
|
||||
description: DryRun specifies to perform a `kubectl apply --dry-run`
|
||||
without actually performing the sync
|
||||
@@ -2537,6 +2542,11 @@ spec:
|
||||
sync:
|
||||
description: Sync contains parameters for the operation
|
||||
properties:
|
||||
autoHealAttemptsCount:
|
||||
description: SelfHealAttemptsCount contains the number
|
||||
of auto-heal attempts
|
||||
format: int64
|
||||
type: integer
|
||||
dryRun:
|
||||
description: DryRun specifies to perform a `kubectl apply
|
||||
--dry-run` without actually performing the sync
|
||||
@@ -20860,6 +20870,8 @@ rules:
|
||||
- appprojects
|
||||
verbs:
|
||||
- get
|
||||
- list
|
||||
- watch
|
||||
- apiGroups:
|
||||
- argoproj.io
|
||||
resources:
|
||||
@@ -21108,6 +21120,8 @@ rules:
|
||||
- appprojects
|
||||
verbs:
|
||||
- get
|
||||
- list
|
||||
- watch
|
||||
- apiGroups:
|
||||
- ""
|
||||
resources:
|
||||
@@ -22609,7 +22623,7 @@ spec:
|
||||
key: applicationsetcontroller.enable.scm.providers
|
||||
name: argocd-cmd-params-cm
|
||||
optional: true
|
||||
image: quay.io/argoproj/argocd:latest
|
||||
image: quay.io/argoproj/argocd:v2.12.10
|
||||
imagePullPolicy: Always
|
||||
name: argocd-applicationset-controller
|
||||
ports:
|
||||
@@ -22732,7 +22746,7 @@ spec:
|
||||
- -n
|
||||
- /usr/local/bin/argocd
|
||||
- /shared/argocd-dex
|
||||
image: quay.io/argoproj/argocd:latest
|
||||
image: quay.io/argoproj/argocd:v2.12.10
|
||||
imagePullPolicy: Always
|
||||
name: copyutil
|
||||
securityContext:
|
||||
@@ -22814,7 +22828,7 @@ spec:
|
||||
key: notificationscontroller.selfservice.enabled
|
||||
name: argocd-cmd-params-cm
|
||||
optional: true
|
||||
image: quay.io/argoproj/argocd:latest
|
||||
image: quay.io/argoproj/argocd:v2.12.10
|
||||
imagePullPolicy: Always
|
||||
livenessProbe:
|
||||
tcpSocket:
|
||||
@@ -22933,7 +22947,7 @@ spec:
|
||||
- argocd
|
||||
- admin
|
||||
- redis-initial-password
|
||||
image: quay.io/argoproj/argocd:latest
|
||||
image: quay.io/argoproj/argocd:v2.12.10
|
||||
imagePullPolicy: IfNotPresent
|
||||
name: secret-init
|
||||
securityContext:
|
||||
@@ -23214,7 +23228,7 @@ spec:
|
||||
value: /helm-working-dir
|
||||
- name: HELM_DATA_HOME
|
||||
value: /helm-working-dir
|
||||
image: quay.io/argoproj/argocd:latest
|
||||
image: quay.io/argoproj/argocd:v2.12.10
|
||||
imagePullPolicy: Always
|
||||
livenessProbe:
|
||||
failureThreshold: 3
|
||||
@@ -23266,7 +23280,7 @@ spec:
|
||||
- -n
|
||||
- /usr/local/bin/argocd
|
||||
- /var/run/argocd/argocd-cmp-server
|
||||
image: quay.io/argoproj/argocd:latest
|
||||
image: quay.io/argoproj/argocd:v2.12.10
|
||||
name: copyutil
|
||||
securityContext:
|
||||
allowPrivilegeEscalation: false
|
||||
@@ -23590,7 +23604,7 @@ spec:
|
||||
key: server.api.content.types
|
||||
name: argocd-cmd-params-cm
|
||||
optional: true
|
||||
image: quay.io/argoproj/argocd:latest
|
||||
image: quay.io/argoproj/argocd:v2.12.10
|
||||
imagePullPolicy: Always
|
||||
livenessProbe:
|
||||
httpGet:
|
||||
@@ -23781,6 +23795,24 @@ spec:
|
||||
key: controller.self.heal.timeout.seconds
|
||||
name: argocd-cmd-params-cm
|
||||
optional: true
|
||||
- name: ARGOCD_APPLICATION_CONTROLLER_SELF_HEAL_BACKOFF_TIMEOUT_SECONDS
|
||||
valueFrom:
|
||||
configMapKeyRef:
|
||||
key: controller.self.heal.backoff.timeout.seconds
|
||||
name: argocd-cmd-params-cm
|
||||
optional: true
|
||||
- name: ARGOCD_APPLICATION_CONTROLLER_SELF_HEAL_BACKOFF_FACTOR
|
||||
valueFrom:
|
||||
configMapKeyRef:
|
||||
key: controller.self.heal.backoff.factor
|
||||
name: argocd-cmd-params-cm
|
||||
optional: true
|
||||
- name: ARGOCD_APPLICATION_CONTROLLER_SELF_HEAL_BACKOFF_CAP_SECONDS
|
||||
valueFrom:
|
||||
configMapKeyRef:
|
||||
key: controller.self.heal.backoff.cap.seconds
|
||||
name: argocd-cmd-params-cm
|
||||
optional: true
|
||||
- name: ARGOCD_APPLICATION_CONTROLLER_REPO_SERVER_PLAINTEXT
|
||||
valueFrom:
|
||||
configMapKeyRef:
|
||||
@@ -23889,7 +23921,7 @@ spec:
|
||||
key: controller.ignore.normalizer.jq.timeout
|
||||
name: argocd-cmd-params-cm
|
||||
optional: true
|
||||
image: quay.io/argoproj/argocd:latest
|
||||
image: quay.io/argoproj/argocd:v2.12.10
|
||||
imagePullPolicy: Always
|
||||
name: argocd-application-controller
|
||||
ports:
|
||||
|
||||
@@ -149,6 +149,8 @@ rules:
|
||||
- appprojects
|
||||
verbs:
|
||||
- get
|
||||
- list
|
||||
- watch
|
||||
- apiGroups:
|
||||
- argoproj.io
|
||||
resources:
|
||||
@@ -1686,7 +1688,7 @@ spec:
|
||||
key: applicationsetcontroller.enable.scm.providers
|
||||
name: argocd-cmd-params-cm
|
||||
optional: true
|
||||
image: quay.io/argoproj/argocd:latest
|
||||
image: quay.io/argoproj/argocd:v2.12.10
|
||||
imagePullPolicy: Always
|
||||
name: argocd-applicationset-controller
|
||||
ports:
|
||||
@@ -1809,7 +1811,7 @@ spec:
|
||||
- -n
|
||||
- /usr/local/bin/argocd
|
||||
- /shared/argocd-dex
|
||||
image: quay.io/argoproj/argocd:latest
|
||||
image: quay.io/argoproj/argocd:v2.12.10
|
||||
imagePullPolicy: Always
|
||||
name: copyutil
|
||||
securityContext:
|
||||
@@ -1891,7 +1893,7 @@ spec:
|
||||
key: notificationscontroller.selfservice.enabled
|
||||
name: argocd-cmd-params-cm
|
||||
optional: true
|
||||
image: quay.io/argoproj/argocd:latest
|
||||
image: quay.io/argoproj/argocd:v2.12.10
|
||||
imagePullPolicy: Always
|
||||
livenessProbe:
|
||||
tcpSocket:
|
||||
@@ -2010,7 +2012,7 @@ spec:
|
||||
- argocd
|
||||
- admin
|
||||
- redis-initial-password
|
||||
image: quay.io/argoproj/argocd:latest
|
||||
image: quay.io/argoproj/argocd:v2.12.10
|
||||
imagePullPolicy: IfNotPresent
|
||||
name: secret-init
|
||||
securityContext:
|
||||
@@ -2291,7 +2293,7 @@ spec:
|
||||
value: /helm-working-dir
|
||||
- name: HELM_DATA_HOME
|
||||
value: /helm-working-dir
|
||||
image: quay.io/argoproj/argocd:latest
|
||||
image: quay.io/argoproj/argocd:v2.12.10
|
||||
imagePullPolicy: Always
|
||||
livenessProbe:
|
||||
failureThreshold: 3
|
||||
@@ -2343,7 +2345,7 @@ spec:
|
||||
- -n
|
||||
- /usr/local/bin/argocd
|
||||
- /var/run/argocd/argocd-cmp-server
|
||||
image: quay.io/argoproj/argocd:latest
|
||||
image: quay.io/argoproj/argocd:v2.12.10
|
||||
name: copyutil
|
||||
securityContext:
|
||||
allowPrivilegeEscalation: false
|
||||
@@ -2667,7 +2669,7 @@ spec:
|
||||
key: server.api.content.types
|
||||
name: argocd-cmd-params-cm
|
||||
optional: true
|
||||
image: quay.io/argoproj/argocd:latest
|
||||
image: quay.io/argoproj/argocd:v2.12.10
|
||||
imagePullPolicy: Always
|
||||
livenessProbe:
|
||||
httpGet:
|
||||
@@ -2858,6 +2860,24 @@ spec:
|
||||
key: controller.self.heal.timeout.seconds
|
||||
name: argocd-cmd-params-cm
|
||||
optional: true
|
||||
- name: ARGOCD_APPLICATION_CONTROLLER_SELF_HEAL_BACKOFF_TIMEOUT_SECONDS
|
||||
valueFrom:
|
||||
configMapKeyRef:
|
||||
key: controller.self.heal.backoff.timeout.seconds
|
||||
name: argocd-cmd-params-cm
|
||||
optional: true
|
||||
- name: ARGOCD_APPLICATION_CONTROLLER_SELF_HEAL_BACKOFF_FACTOR
|
||||
valueFrom:
|
||||
configMapKeyRef:
|
||||
key: controller.self.heal.backoff.factor
|
||||
name: argocd-cmd-params-cm
|
||||
optional: true
|
||||
- name: ARGOCD_APPLICATION_CONTROLLER_SELF_HEAL_BACKOFF_CAP_SECONDS
|
||||
valueFrom:
|
||||
configMapKeyRef:
|
||||
key: controller.self.heal.backoff.cap.seconds
|
||||
name: argocd-cmd-params-cm
|
||||
optional: true
|
||||
- name: ARGOCD_APPLICATION_CONTROLLER_REPO_SERVER_PLAINTEXT
|
||||
valueFrom:
|
||||
configMapKeyRef:
|
||||
@@ -2966,7 +2986,7 @@ spec:
|
||||
key: controller.ignore.normalizer.jq.timeout
|
||||
name: argocd-cmd-params-cm
|
||||
optional: true
|
||||
image: quay.io/argoproj/argocd:latest
|
||||
image: quay.io/argoproj/argocd:v2.12.10
|
||||
imagePullPolicy: Always
|
||||
name: argocd-application-controller
|
||||
ports:
|
||||
|
||||
@@ -115,6 +115,11 @@ spec:
|
||||
sync:
|
||||
description: Sync contains parameters for the operation
|
||||
properties:
|
||||
autoHealAttemptsCount:
|
||||
description: SelfHealAttemptsCount contains the number of auto-heal
|
||||
attempts
|
||||
format: int64
|
||||
type: integer
|
||||
dryRun:
|
||||
description: DryRun specifies to perform a `kubectl apply --dry-run`
|
||||
without actually performing the sync
|
||||
@@ -2537,6 +2542,11 @@ spec:
|
||||
sync:
|
||||
description: Sync contains parameters for the operation
|
||||
properties:
|
||||
autoHealAttemptsCount:
|
||||
description: SelfHealAttemptsCount contains the number
|
||||
of auto-heal attempts
|
||||
format: int64
|
||||
type: integer
|
||||
dryRun:
|
||||
description: DryRun specifies to perform a `kubectl apply
|
||||
--dry-run` without actually performing the sync
|
||||
@@ -20849,6 +20859,8 @@ rules:
|
||||
- appprojects
|
||||
verbs:
|
||||
- get
|
||||
- list
|
||||
- watch
|
||||
- apiGroups:
|
||||
- argoproj.io
|
||||
resources:
|
||||
@@ -21075,6 +21087,8 @@ rules:
|
||||
- appprojects
|
||||
verbs:
|
||||
- get
|
||||
- list
|
||||
- watch
|
||||
- apiGroups:
|
||||
- ""
|
||||
resources:
|
||||
@@ -21726,7 +21740,7 @@ spec:
|
||||
key: applicationsetcontroller.enable.scm.providers
|
||||
name: argocd-cmd-params-cm
|
||||
optional: true
|
||||
image: quay.io/argoproj/argocd:latest
|
||||
image: quay.io/argoproj/argocd:v2.12.10
|
||||
imagePullPolicy: Always
|
||||
name: argocd-applicationset-controller
|
||||
ports:
|
||||
@@ -21849,7 +21863,7 @@ spec:
|
||||
- -n
|
||||
- /usr/local/bin/argocd
|
||||
- /shared/argocd-dex
|
||||
image: quay.io/argoproj/argocd:latest
|
||||
image: quay.io/argoproj/argocd:v2.12.10
|
||||
imagePullPolicy: Always
|
||||
name: copyutil
|
||||
securityContext:
|
||||
@@ -21931,7 +21945,7 @@ spec:
|
||||
key: notificationscontroller.selfservice.enabled
|
||||
name: argocd-cmd-params-cm
|
||||
optional: true
|
||||
image: quay.io/argoproj/argocd:latest
|
||||
image: quay.io/argoproj/argocd:v2.12.10
|
||||
imagePullPolicy: Always
|
||||
livenessProbe:
|
||||
tcpSocket:
|
||||
@@ -22031,7 +22045,7 @@ spec:
|
||||
- argocd
|
||||
- admin
|
||||
- redis-initial-password
|
||||
image: quay.io/argoproj/argocd:latest
|
||||
image: quay.io/argoproj/argocd:v2.12.10
|
||||
imagePullPolicy: IfNotPresent
|
||||
name: secret-init
|
||||
securityContext:
|
||||
@@ -22284,7 +22298,7 @@ spec:
|
||||
value: /helm-working-dir
|
||||
- name: HELM_DATA_HOME
|
||||
value: /helm-working-dir
|
||||
image: quay.io/argoproj/argocd:latest
|
||||
image: quay.io/argoproj/argocd:v2.12.10
|
||||
imagePullPolicy: Always
|
||||
livenessProbe:
|
||||
failureThreshold: 3
|
||||
@@ -22336,7 +22350,7 @@ spec:
|
||||
- -n
|
||||
- /usr/local/bin/argocd
|
||||
- /var/run/argocd/argocd-cmp-server
|
||||
image: quay.io/argoproj/argocd:latest
|
||||
image: quay.io/argoproj/argocd:v2.12.10
|
||||
name: copyutil
|
||||
securityContext:
|
||||
allowPrivilegeEscalation: false
|
||||
@@ -22658,7 +22672,7 @@ spec:
|
||||
key: server.api.content.types
|
||||
name: argocd-cmd-params-cm
|
||||
optional: true
|
||||
image: quay.io/argoproj/argocd:latest
|
||||
image: quay.io/argoproj/argocd:v2.12.10
|
||||
imagePullPolicy: Always
|
||||
livenessProbe:
|
||||
httpGet:
|
||||
@@ -22849,6 +22863,24 @@ spec:
|
||||
key: controller.self.heal.timeout.seconds
|
||||
name: argocd-cmd-params-cm
|
||||
optional: true
|
||||
- name: ARGOCD_APPLICATION_CONTROLLER_SELF_HEAL_BACKOFF_TIMEOUT_SECONDS
|
||||
valueFrom:
|
||||
configMapKeyRef:
|
||||
key: controller.self.heal.backoff.timeout.seconds
|
||||
name: argocd-cmd-params-cm
|
||||
optional: true
|
||||
- name: ARGOCD_APPLICATION_CONTROLLER_SELF_HEAL_BACKOFF_FACTOR
|
||||
valueFrom:
|
||||
configMapKeyRef:
|
||||
key: controller.self.heal.backoff.factor
|
||||
name: argocd-cmd-params-cm
|
||||
optional: true
|
||||
- name: ARGOCD_APPLICATION_CONTROLLER_SELF_HEAL_BACKOFF_CAP_SECONDS
|
||||
valueFrom:
|
||||
configMapKeyRef:
|
||||
key: controller.self.heal.backoff.cap.seconds
|
||||
name: argocd-cmd-params-cm
|
||||
optional: true
|
||||
- name: ARGOCD_APPLICATION_CONTROLLER_REPO_SERVER_PLAINTEXT
|
||||
valueFrom:
|
||||
configMapKeyRef:
|
||||
@@ -22957,7 +22989,7 @@ spec:
|
||||
key: controller.ignore.normalizer.jq.timeout
|
||||
name: argocd-cmd-params-cm
|
||||
optional: true
|
||||
image: quay.io/argoproj/argocd:latest
|
||||
image: quay.io/argoproj/argocd:v2.12.10
|
||||
imagePullPolicy: Always
|
||||
name: argocd-application-controller
|
||||
ports:
|
||||
|
||||
@@ -138,6 +138,8 @@ rules:
|
||||
- appprojects
|
||||
verbs:
|
||||
- get
|
||||
- list
|
||||
- watch
|
||||
- apiGroups:
|
||||
- argoproj.io
|
||||
resources:
|
||||
@@ -803,7 +805,7 @@ spec:
|
||||
key: applicationsetcontroller.enable.scm.providers
|
||||
name: argocd-cmd-params-cm
|
||||
optional: true
|
||||
image: quay.io/argoproj/argocd:latest
|
||||
image: quay.io/argoproj/argocd:v2.12.10
|
||||
imagePullPolicy: Always
|
||||
name: argocd-applicationset-controller
|
||||
ports:
|
||||
@@ -926,7 +928,7 @@ spec:
|
||||
- -n
|
||||
- /usr/local/bin/argocd
|
||||
- /shared/argocd-dex
|
||||
image: quay.io/argoproj/argocd:latest
|
||||
image: quay.io/argoproj/argocd:v2.12.10
|
||||
imagePullPolicy: Always
|
||||
name: copyutil
|
||||
securityContext:
|
||||
@@ -1008,7 +1010,7 @@ spec:
|
||||
key: notificationscontroller.selfservice.enabled
|
||||
name: argocd-cmd-params-cm
|
||||
optional: true
|
||||
image: quay.io/argoproj/argocd:latest
|
||||
image: quay.io/argoproj/argocd:v2.12.10
|
||||
imagePullPolicy: Always
|
||||
livenessProbe:
|
||||
tcpSocket:
|
||||
@@ -1108,7 +1110,7 @@ spec:
|
||||
- argocd
|
||||
- admin
|
||||
- redis-initial-password
|
||||
image: quay.io/argoproj/argocd:latest
|
||||
image: quay.io/argoproj/argocd:v2.12.10
|
||||
imagePullPolicy: IfNotPresent
|
||||
name: secret-init
|
||||
securityContext:
|
||||
@@ -1361,7 +1363,7 @@ spec:
|
||||
value: /helm-working-dir
|
||||
- name: HELM_DATA_HOME
|
||||
value: /helm-working-dir
|
||||
image: quay.io/argoproj/argocd:latest
|
||||
image: quay.io/argoproj/argocd:v2.12.10
|
||||
imagePullPolicy: Always
|
||||
livenessProbe:
|
||||
failureThreshold: 3
|
||||
@@ -1413,7 +1415,7 @@ spec:
|
||||
- -n
|
||||
- /usr/local/bin/argocd
|
||||
- /var/run/argocd/argocd-cmp-server
|
||||
image: quay.io/argoproj/argocd:latest
|
||||
image: quay.io/argoproj/argocd:v2.12.10
|
||||
name: copyutil
|
||||
securityContext:
|
||||
allowPrivilegeEscalation: false
|
||||
@@ -1735,7 +1737,7 @@ spec:
|
||||
key: server.api.content.types
|
||||
name: argocd-cmd-params-cm
|
||||
optional: true
|
||||
image: quay.io/argoproj/argocd:latest
|
||||
image: quay.io/argoproj/argocd:v2.12.10
|
||||
imagePullPolicy: Always
|
||||
livenessProbe:
|
||||
httpGet:
|
||||
@@ -1926,6 +1928,24 @@ spec:
|
||||
key: controller.self.heal.timeout.seconds
|
||||
name: argocd-cmd-params-cm
|
||||
optional: true
|
||||
- name: ARGOCD_APPLICATION_CONTROLLER_SELF_HEAL_BACKOFF_TIMEOUT_SECONDS
|
||||
valueFrom:
|
||||
configMapKeyRef:
|
||||
key: controller.self.heal.backoff.timeout.seconds
|
||||
name: argocd-cmd-params-cm
|
||||
optional: true
|
||||
- name: ARGOCD_APPLICATION_CONTROLLER_SELF_HEAL_BACKOFF_FACTOR
|
||||
valueFrom:
|
||||
configMapKeyRef:
|
||||
key: controller.self.heal.backoff.factor
|
||||
name: argocd-cmd-params-cm
|
||||
optional: true
|
||||
- name: ARGOCD_APPLICATION_CONTROLLER_SELF_HEAL_BACKOFF_CAP_SECONDS
|
||||
valueFrom:
|
||||
configMapKeyRef:
|
||||
key: controller.self.heal.backoff.cap.seconds
|
||||
name: argocd-cmd-params-cm
|
||||
optional: true
|
||||
- name: ARGOCD_APPLICATION_CONTROLLER_REPO_SERVER_PLAINTEXT
|
||||
valueFrom:
|
||||
configMapKeyRef:
|
||||
@@ -2034,7 +2054,7 @@ spec:
|
||||
key: controller.ignore.normalizer.jq.timeout
|
||||
name: argocd-cmd-params-cm
|
||||
optional: true
|
||||
image: quay.io/argoproj/argocd:latest
|
||||
image: quay.io/argoproj/argocd:v2.12.10
|
||||
imagePullPolicy: Always
|
||||
name: argocd-application-controller
|
||||
ports:
|
||||
|
||||
@@ -128,6 +128,7 @@ nav:
|
||||
- operator-manual/server-commands/additional-configuration-method.md
|
||||
- Upgrading:
|
||||
- operator-manual/upgrading/overview.md
|
||||
- operator-manual/upgrading/2.11-2.12.md
|
||||
- operator-manual/upgrading/2.10-2.11.md
|
||||
- operator-manual/upgrading/2.9-2.10.md
|
||||
- operator-manual/upgrading/2.8-2.9.md
|
||||
|
||||
@@ -122,7 +122,7 @@ func NewController(
|
||||
|
||||
// Check if app is not in the namespace where the controller is in, and also app is not in one of the applicationNamespaces
|
||||
func checkAppNotInAdditionalNamespaces(app *unstructured.Unstructured, namespace string, applicationNamespaces []string) bool {
|
||||
return namespace != app.GetNamespace() && !glob.MatchStringInList(applicationNamespaces, app.GetNamespace(), false)
|
||||
return namespace != app.GetNamespace() && !glob.MatchStringInList(applicationNamespaces, app.GetNamespace(), glob.REGEXP)
|
||||
}
|
||||
|
||||
func (c *notificationController) alterDestinations(obj v1.Object, destinations services.Destinations, cfg api.Config) services.Destinations {
|
||||
@@ -151,7 +151,7 @@ func newInformer(resClient dynamic.ResourceInterface, controllerNamespace string
|
||||
}
|
||||
newItems := []unstructured.Unstructured{}
|
||||
for _, res := range appList.Items {
|
||||
if controllerNamespace == res.GetNamespace() || glob.MatchStringInList(applicationNamespaces, res.GetNamespace(), false) {
|
||||
if controllerNamespace == res.GetNamespace() || glob.MatchStringInList(applicationNamespaces, res.GetNamespace(), glob.REGEXP) {
|
||||
newItems = append(newItems, res)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -101,6 +101,7 @@ type Settings struct {
|
||||
ExecEnabled bool `protobuf:"varint,22,opt,name=execEnabled,proto3" json:"execEnabled,omitempty"`
|
||||
ControllerNamespace string `protobuf:"bytes,23,opt,name=controllerNamespace,proto3" json:"controllerNamespace,omitempty"`
|
||||
AppsInAnyNamespaceEnabled bool `protobuf:"varint,24,opt,name=appsInAnyNamespaceEnabled,proto3" json:"appsInAnyNamespaceEnabled,omitempty"`
|
||||
InstallationID string `protobuf:"bytes,26,opt,name=installationID,proto3" json:"installationID,omitempty"`
|
||||
XXX_NoUnkeyedLiteral struct{} `json:"-"`
|
||||
XXX_unrecognized []byte `json:"-"`
|
||||
XXX_sizecache int32 `json:"-"`
|
||||
@@ -307,6 +308,13 @@ func (m *Settings) GetAppsInAnyNamespaceEnabled() bool {
|
||||
return false
|
||||
}
|
||||
|
||||
func (m *Settings) GetInstallationID() string {
|
||||
if m != nil {
|
||||
return m.InstallationID
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
type GoogleAnalyticsConfig struct {
|
||||
TrackingID string `protobuf:"bytes,1,opt,name=trackingID,proto3" json:"trackingID,omitempty"`
|
||||
AnonymizeUsers bool `protobuf:"varint,2,opt,name=anonymizeUsers,proto3" json:"anonymizeUsers,omitempty"`
|
||||
@@ -740,83 +748,84 @@ func init() {
|
||||
func init() { proto.RegisterFile("server/settings/settings.proto", fileDescriptor_a480d494da040caa) }
|
||||
|
||||
var fileDescriptor_a480d494da040caa = []byte{
|
||||
// 1215 bytes of a gzipped FileDescriptorProto
|
||||
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xa4, 0x56, 0x4f, 0x6f, 0x1b, 0x45,
|
||||
0x14, 0xd7, 0xd6, 0x69, 0x62, 0x3f, 0x37, 0x75, 0x32, 0x6d, 0xd3, 0xad, 0x55, 0x12, 0xe3, 0x43,
|
||||
0x65, 0x10, 0xac, 0x9b, 0x54, 0x08, 0x54, 0x51, 0x41, 0x6d, 0x57, 0xad, 0x69, 0xda, 0x86, 0x69,
|
||||
0xd3, 0x03, 0x97, 0x6a, 0xb2, 0x7e, 0xac, 0x97, 0xac, 0x67, 0x56, 0x33, 0xb3, 0xa6, 0xee, 0x91,
|
||||
0x0f, 0xc0, 0x05, 0x3e, 0x0b, 0x07, 0xee, 0x08, 0x8e, 0x48, 0xdc, 0x23, 0x64, 0xf1, 0x41, 0xd0,
|
||||
0xce, 0xfe, 0xc9, 0x66, 0xed, 0x14, 0xa4, 0xde, 0x66, 0x7e, 0xbf, 0xf7, 0x6f, 0xde, 0xbc, 0x37,
|
||||
0xf3, 0x60, 0x5b, 0xa1, 0x9c, 0xa2, 0xec, 0x2a, 0xd4, 0xda, 0xe7, 0x9e, 0xca, 0x17, 0x4e, 0x28,
|
||||
0x85, 0x16, 0x64, 0xcd, 0x0d, 0x22, 0xa5, 0x51, 0x36, 0xaf, 0x7a, 0xc2, 0x13, 0x06, 0xeb, 0xc6,
|
||||
0xab, 0x84, 0x6e, 0xde, 0xf4, 0x84, 0xf0, 0x02, 0xec, 0xb2, 0xd0, 0xef, 0x32, 0xce, 0x85, 0x66,
|
||||
0xda, 0x17, 0x3c, 0x55, 0x6e, 0xee, 0x7b, 0xbe, 0x1e, 0x47, 0x47, 0x8e, 0x2b, 0x26, 0x5d, 0x26,
|
||||
0x8d, 0xfa, 0x77, 0x66, 0xf1, 0xb1, 0x3b, 0xea, 0x4e, 0xf7, 0xba, 0xe1, 0xb1, 0x17, 0x6b, 0xaa,
|
||||
0x2e, 0x0b, 0xc3, 0xc0, 0x77, 0x8d, 0x6e, 0x77, 0xba, 0xcb, 0x82, 0x70, 0xcc, 0x76, 0xbb, 0x1e,
|
||||
0x72, 0x94, 0x4c, 0xe3, 0x28, 0xb5, 0xf6, 0xe5, 0x7f, 0x58, 0x2b, 0x9f, 0x44, 0xf8, 0x23, 0xb7,
|
||||
0xeb, 0x06, 0xcc, 0x9f, 0xa4, 0xf1, 0xb4, 0x1b, 0xb0, 0xfe, 0x3c, 0x65, 0xbf, 0x8e, 0x50, 0xce,
|
||||
0xda, 0xbf, 0xd4, 0xa1, 0x9a, 0x21, 0xe4, 0x06, 0x54, 0x22, 0x19, 0xd8, 0x56, 0xcb, 0xea, 0xd4,
|
||||
0x7a, 0x6b, 0xf3, 0x93, 0x9d, 0xca, 0x21, 0xdd, 0xa7, 0x31, 0x46, 0x6e, 0x43, 0x6d, 0x84, 0xaf,
|
||||
0xfb, 0x82, 0x7f, 0xeb, 0x7b, 0xf6, 0x85, 0x96, 0xd5, 0xa9, 0xef, 0x11, 0x27, 0xcd, 0x8c, 0x33,
|
||||
0xc8, 0x18, 0x7a, 0x2a, 0x44, 0xfa, 0x00, 0xb1, 0xff, 0x54, 0xa5, 0x62, 0x54, 0xae, 0xe4, 0x2a,
|
||||
0xcf, 0x86, 0x83, 0x7e, 0x42, 0xf5, 0x2e, 0xcf, 0x4f, 0x76, 0xe0, 0x74, 0x4f, 0x0b, 0x6a, 0xa4,
|
||||
0x05, 0x75, 0x16, 0x86, 0xfb, 0xec, 0x08, 0x83, 0xc7, 0x38, 0xb3, 0x57, 0xe2, 0xc8, 0x68, 0x11,
|
||||
0x22, 0x2f, 0x61, 0x53, 0xa2, 0x12, 0x91, 0x74, 0xf1, 0xd9, 0x14, 0xa5, 0xf4, 0x47, 0xa8, 0xec,
|
||||
0x8b, 0xad, 0x4a, 0xa7, 0xbe, 0xd7, 0xc9, 0xbd, 0x65, 0x27, 0x74, 0x68, 0x59, 0xf4, 0x01, 0xd7,
|
||||
0x72, 0x46, 0x17, 0x4d, 0x10, 0x07, 0x88, 0xd2, 0x4c, 0x47, 0xaa, 0xc7, 0x46, 0x1e, 0x3e, 0xe0,
|
||||
0xec, 0x28, 0xc0, 0x91, 0xbd, 0xda, 0xb2, 0x3a, 0x55, 0xba, 0x84, 0x21, 0x8f, 0xa0, 0x91, 0x54,
|
||||
0xc2, 0x7d, 0xce, 0x82, 0x99, 0xf6, 0x5d, 0x65, 0xaf, 0x99, 0x33, 0x6f, 0xe7, 0x51, 0x3c, 0x3c,
|
||||
0xcb, 0xa7, 0xc7, 0x2d, 0xab, 0x91, 0x37, 0xb0, 0x71, 0x1c, 0x29, 0x2d, 0x26, 0xfe, 0x1b, 0x7c,
|
||||
0x16, 0x9a, 0x6a, 0xb2, 0xab, 0xc6, 0xd4, 0x53, 0xe7, 0xb4, 0x00, 0x9c, 0xac, 0x00, 0xcc, 0xe2,
|
||||
0x95, 0x3b, 0x72, 0xa6, 0x7b, 0x4e, 0x78, 0xec, 0x39, 0x71, 0x39, 0x39, 0x85, 0x72, 0x72, 0xb2,
|
||||
0x72, 0x72, 0x1e, 0x97, 0xac, 0xd2, 0x05, 0x3f, 0xe4, 0x7d, 0x58, 0x19, 0x63, 0x10, 0xda, 0x35,
|
||||
0xe3, 0x6f, 0x3d, 0x0f, 0xfd, 0x11, 0x06, 0x21, 0x35, 0x14, 0xf9, 0x00, 0xd6, 0xc2, 0x20, 0xf2,
|
||||
0x7c, 0xae, 0x6c, 0x30, 0x69, 0x6e, 0xe4, 0x52, 0x07, 0x06, 0xa7, 0x19, 0x1f, 0xe7, 0x30, 0x52,
|
||||
0x28, 0xf7, 0x45, 0xbc, 0x1b, 0xf8, 0x2a, 0xc9, 0x61, 0x3d, 0xc9, 0xe1, 0x22, 0x43, 0x7e, 0xb4,
|
||||
0xe0, 0xba, 0x6b, 0xb2, 0xf2, 0x84, 0x71, 0xe6, 0xe1, 0x04, 0xb9, 0x3e, 0x48, 0x7d, 0x5d, 0x32,
|
||||
0xbe, 0x5e, 0xbc, 0x5b, 0x06, 0xfa, 0x4b, 0x8d, 0xd3, 0xf3, 0x9c, 0x92, 0x8f, 0x60, 0x33, 0x4f,
|
||||
0xd1, 0x4b, 0x94, 0xca, 0xdc, 0xc5, 0x7a, 0xab, 0xd2, 0xa9, 0xd1, 0x45, 0x82, 0x34, 0xa1, 0x1a,
|
||||
0xf9, 0x7d, 0xa5, 0x0e, 0xe9, 0xbe, 0x7d, 0xd9, 0x54, 0x6a, 0xbe, 0x27, 0x1d, 0x68, 0x44, 0x7e,
|
||||
0x8f, 0x71, 0x8e, 0xb2, 0x2f, 0xb8, 0x46, 0xae, 0xed, 0x86, 0x11, 0x29, 0xc3, 0x71, 0xc9, 0x67,
|
||||
0x50, 0x6c, 0x68, 0x23, 0x29, 0xf9, 0x02, 0x14, 0xdb, 0x0a, 0x99, 0x52, 0xdf, 0x0b, 0x39, 0x3a,
|
||||
0x60, 0x5a, 0xa3, 0xe4, 0xf6, 0x66, 0x62, 0xab, 0x04, 0x93, 0x5b, 0x70, 0x59, 0x4b, 0xe6, 0x1e,
|
||||
0xfb, 0xdc, 0x7b, 0x82, 0x7a, 0x2c, 0x46, 0x36, 0x31, 0x82, 0x25, 0x34, 0x3e, 0x67, 0xe6, 0xe0,
|
||||
0x00, 0xe5, 0x84, 0xf1, 0x38, 0xbe, 0x2b, 0xe6, 0x9e, 0x16, 0x09, 0xf2, 0x21, 0x6c, 0xe4, 0xa0,
|
||||
0x50, 0x7e, 0x9c, 0x62, 0xfb, 0xaa, 0xb1, 0xbb, 0x80, 0x97, 0xda, 0x88, 0x0a, 0xa1, 0x0f, 0x65,
|
||||
0x60, 0x5f, 0x33, 0xd2, 0x4b, 0x98, 0xf8, 0xf4, 0xf8, 0x1a, 0xdd, 0xac, 0xdf, 0xb6, 0x4c, 0x0c,
|
||||
0x45, 0x88, 0xdc, 0x86, 0x2b, 0xae, 0xe0, 0x5a, 0x8a, 0x20, 0x40, 0xf9, 0x94, 0x4d, 0x50, 0x85,
|
||||
0xcc, 0x45, 0xfb, 0xba, 0x31, 0xb9, 0x8c, 0x22, 0x9f, 0xc3, 0x0d, 0x16, 0x86, 0x6a, 0xc8, 0xef,
|
||||
0xf3, 0x59, 0x8e, 0x66, 0x1e, 0x6c, 0xe3, 0xe1, 0x7c, 0x81, 0xe6, 0xcf, 0x16, 0x6c, 0x2d, 0x7f,
|
||||
0x36, 0xc8, 0x06, 0x54, 0x8e, 0x71, 0x96, 0xbc, 0x97, 0x34, 0x5e, 0x92, 0x11, 0x5c, 0x9c, 0xb2,
|
||||
0x20, 0xc2, 0xf4, 0x89, 0x7c, 0xc7, 0x86, 0x2d, 0xbb, 0xa5, 0x89, 0xf1, 0xbb, 0x17, 0x3e, 0xb3,
|
||||
0xda, 0xaf, 0xe0, 0xda, 0xd2, 0xf7, 0x84, 0x6c, 0x03, 0x64, 0xb7, 0x3b, 0x1c, 0xa4, 0xb1, 0x15,
|
||||
0x90, 0xb8, 0x26, 0x18, 0x17, 0x7c, 0x16, 0x97, 0xee, 0xa1, 0x42, 0xa9, 0x4c, 0xac, 0x55, 0x5a,
|
||||
0x42, 0xdb, 0x03, 0xb8, 0x9e, 0x3d, 0x9b, 0x69, 0x3b, 0x50, 0x54, 0xa1, 0xe0, 0x0a, 0x8b, 0x4f,
|
||||
0x80, 0xf5, 0xf6, 0x27, 0xa0, 0xfd, 0xab, 0x05, 0x2b, 0xf1, 0xe3, 0x41, 0x6c, 0x58, 0x73, 0xc7,
|
||||
0xcc, 0xdc, 0x7e, 0x12, 0x53, 0xb6, 0x8d, 0xdb, 0x26, 0x5e, 0xbe, 0xc0, 0xd7, 0xda, 0x84, 0x52,
|
||||
0xa3, 0xf9, 0x9e, 0xdc, 0x03, 0x38, 0xf2, 0x39, 0x93, 0xb3, 0x43, 0x19, 0x28, 0xbb, 0x62, 0x9c,
|
||||
0xbd, 0x77, 0xe6, 0x55, 0x72, 0x7a, 0x39, 0x9f, 0xbc, 0xe5, 0x05, 0x85, 0xe6, 0x3d, 0x68, 0x94,
|
||||
0xe8, 0x25, 0x77, 0x76, 0xb5, 0x78, 0x67, 0xb5, 0x62, 0x8e, 0x6f, 0xc2, 0x6a, 0x72, 0x1e, 0x42,
|
||||
0x60, 0x85, 0xb3, 0x09, 0xa6, 0x6a, 0x66, 0xdd, 0xfe, 0x02, 0x6a, 0xf9, 0xc7, 0x47, 0xf6, 0x00,
|
||||
0x5c, 0xc1, 0x39, 0xba, 0x5a, 0xc8, 0x2c, 0x2b, 0xa7, 0x1f, 0x64, 0x3f, 0xa3, 0x68, 0x41, 0xaa,
|
||||
0x7d, 0x07, 0x6a, 0x39, 0xb1, 0xcc, 0x43, 0x8c, 0xe9, 0x59, 0x98, 0x05, 0x66, 0xd6, 0xed, 0xdf,
|
||||
0x2a, 0x50, 0xf8, 0x2c, 0x97, 0xaa, 0x6d, 0xc1, 0xaa, 0xaf, 0x54, 0x84, 0x32, 0x55, 0x4c, 0x77,
|
||||
0xa4, 0x03, 0x55, 0x37, 0xf0, 0x91, 0xeb, 0xe1, 0xc0, 0xfc, 0xc7, 0xb5, 0xde, 0xa5, 0xf9, 0xc9,
|
||||
0x4e, 0xb5, 0x9f, 0x62, 0x34, 0x67, 0xc9, 0x2e, 0xd4, 0xdd, 0xc0, 0xcf, 0x88, 0xe4, 0xdb, 0xed,
|
||||
0x35, 0xe6, 0x27, 0x3b, 0xf5, 0xfe, 0xfe, 0x30, 0x97, 0x2f, 0xca, 0xc4, 0x4e, 0x95, 0x2b, 0xc2,
|
||||
0xf4, 0xf3, 0xad, 0xd1, 0x74, 0x47, 0x5e, 0xc1, 0xba, 0x3f, 0x7a, 0x21, 0x8e, 0x91, 0xf7, 0xcd,
|
||||
0x20, 0x62, 0xaf, 0x9a, 0xdc, 0xdc, 0x5a, 0x32, 0x09, 0x38, 0xc3, 0xa2, 0xa0, 0xb9, 0xae, 0xde,
|
||||
0xe6, 0xfc, 0x64, 0x67, 0x7d, 0x38, 0x28, 0xe0, 0xf4, 0xac, 0x3d, 0x72, 0x17, 0x6c, 0x34, 0xad,
|
||||
0x7a, 0xf0, 0xb8, 0xff, 0xe0, 0x7e, 0xa4, 0xc7, 0xc8, 0x75, 0xda, 0x49, 0xe6, 0x07, 0xae, 0xd2,
|
||||
0x73, 0xf9, 0xe6, 0x0c, 0xc8, 0xa2, 0xcf, 0x25, 0x25, 0xf2, 0xe4, 0x6c, 0x5b, 0x7f, 0xfa, 0xd6,
|
||||
0xb6, 0x4e, 0xa6, 0x30, 0x27, 0x1f, 0x23, 0xe3, 0x71, 0xc6, 0x31, 0xf6, 0x0b, 0xb5, 0xb5, 0xf7,
|
||||
0xbb, 0x05, 0x8d, 0xac, 0xbf, 0x9e, 0xa3, 0x9c, 0xfa, 0x2e, 0x92, 0xaf, 0xa0, 0xf2, 0x10, 0x35,
|
||||
0xd9, 0x5a, 0x98, 0x5b, 0xcc, 0xac, 0xd6, 0xdc, 0x5c, 0xc0, 0xdb, 0xf6, 0x0f, 0x7f, 0xfd, 0xf3,
|
||||
0xd3, 0x05, 0x42, 0x36, 0xcc, 0xfc, 0x39, 0xdd, 0xcd, 0x67, 0x3f, 0x32, 0x06, 0x78, 0x88, 0xf9,
|
||||
0x47, 0x76, 0x9e, 0xc9, 0xd6, 0x02, 0x5e, 0xea, 0xf5, 0x76, 0xcb, 0x78, 0x68, 0x12, 0xbb, 0xec,
|
||||
0xa1, 0x9b, 0xb6, 0x78, 0xaf, 0xff, 0xc7, 0x7c, 0xdb, 0xfa, 0x73, 0xbe, 0x6d, 0xfd, 0x3d, 0xdf,
|
||||
0xb6, 0xbe, 0xf9, 0xe4, 0xff, 0x4d, 0xbc, 0x49, 0xa9, 0xe5, 0xc6, 0x8e, 0x56, 0xcd, 0x7c, 0x7a,
|
||||
0xe7, 0xdf, 0x00, 0x00, 0x00, 0xff, 0xff, 0xf1, 0x4f, 0xb0, 0x2d, 0x8e, 0x0b, 0x00, 0x00,
|
||||
// 1231 bytes of a gzipped FileDescriptorProto
|
||||
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xa4, 0x56, 0xcf, 0x6f, 0x1b, 0xc5,
|
||||
0x17, 0xd7, 0xd6, 0x69, 0x62, 0x3f, 0x37, 0x75, 0x32, 0x6d, 0xd3, 0xad, 0xd5, 0x6f, 0xe2, 0xaf,
|
||||
0x0f, 0x95, 0x41, 0xb0, 0x6e, 0x52, 0x21, 0x50, 0x45, 0x05, 0xb5, 0x5d, 0xb5, 0xa6, 0x69, 0x1b,
|
||||
0xb6, 0x4d, 0x0f, 0x5c, 0xaa, 0xc9, 0xfa, 0xb1, 0x5e, 0xb2, 0x9e, 0x59, 0xcd, 0xcc, 0x9a, 0xb8,
|
||||
0x47, 0xfe, 0x00, 0x2e, 0xf0, 0xd7, 0x70, 0x47, 0x70, 0x44, 0xe2, 0x1e, 0x21, 0x8b, 0x3f, 0x04,
|
||||
0xcd, 0xec, 0x8f, 0x6c, 0xd6, 0x4e, 0x41, 0xea, 0x6d, 0xe6, 0xf3, 0x79, 0xbf, 0xe6, 0xcd, 0x7b,
|
||||
0x33, 0x0f, 0xb6, 0x25, 0x8a, 0x29, 0x8a, 0xae, 0x44, 0xa5, 0x02, 0xe6, 0xcb, 0x7c, 0xe1, 0x44,
|
||||
0x82, 0x2b, 0x4e, 0xd6, 0xbc, 0x30, 0x96, 0x0a, 0x45, 0xf3, 0xba, 0xcf, 0x7d, 0x6e, 0xb0, 0xae,
|
||||
0x5e, 0x25, 0x74, 0xf3, 0xb6, 0xcf, 0xb9, 0x1f, 0x62, 0x97, 0x46, 0x41, 0x97, 0x32, 0xc6, 0x15,
|
||||
0x55, 0x01, 0x67, 0xa9, 0x72, 0x73, 0xdf, 0x0f, 0xd4, 0x38, 0x3e, 0x72, 0x3c, 0x3e, 0xe9, 0x52,
|
||||
0x61, 0xd4, 0xbf, 0x33, 0x8b, 0x8f, 0xbd, 0x51, 0x77, 0xba, 0xd7, 0x8d, 0x8e, 0x7d, 0xad, 0x29,
|
||||
0xbb, 0x34, 0x8a, 0xc2, 0xc0, 0x33, 0xba, 0xdd, 0xe9, 0x2e, 0x0d, 0xa3, 0x31, 0xdd, 0xed, 0xfa,
|
||||
0xc8, 0x50, 0x50, 0x85, 0xa3, 0xd4, 0xda, 0x97, 0xff, 0x62, 0xad, 0x7c, 0x12, 0x1e, 0x8c, 0xbc,
|
||||
0xae, 0x17, 0xd2, 0x60, 0x92, 0xc6, 0xd3, 0x6e, 0xc0, 0xfa, 0xcb, 0x94, 0xfd, 0x3a, 0x46, 0x31,
|
||||
0x6b, 0x9f, 0xd6, 0xa1, 0x9a, 0x21, 0xe4, 0x16, 0x54, 0x62, 0x11, 0xda, 0x56, 0xcb, 0xea, 0xd4,
|
||||
0x7a, 0x6b, 0xf3, 0xd3, 0x9d, 0xca, 0xa1, 0xbb, 0xef, 0x6a, 0x8c, 0xdc, 0x85, 0xda, 0x08, 0x4f,
|
||||
0xfa, 0x9c, 0x7d, 0x1b, 0xf8, 0xf6, 0xa5, 0x96, 0xd5, 0xa9, 0xef, 0x11, 0x27, 0xcd, 0x8c, 0x33,
|
||||
0xc8, 0x18, 0xf7, 0x4c, 0x88, 0xf4, 0x01, 0xb4, 0xff, 0x54, 0xa5, 0x62, 0x54, 0xae, 0xe5, 0x2a,
|
||||
0x2f, 0x86, 0x83, 0x7e, 0x42, 0xf5, 0xae, 0xce, 0x4f, 0x77, 0xe0, 0x6c, 0xef, 0x16, 0xd4, 0x48,
|
||||
0x0b, 0xea, 0x34, 0x8a, 0xf6, 0xe9, 0x11, 0x86, 0x4f, 0x71, 0x66, 0xaf, 0xe8, 0xc8, 0xdc, 0x22,
|
||||
0x44, 0x5e, 0xc3, 0xa6, 0x40, 0xc9, 0x63, 0xe1, 0xe1, 0x8b, 0x29, 0x0a, 0x11, 0x8c, 0x50, 0xda,
|
||||
0x97, 0x5b, 0x95, 0x4e, 0x7d, 0xaf, 0x93, 0x7b, 0xcb, 0x4e, 0xe8, 0xb8, 0x65, 0xd1, 0x47, 0x4c,
|
||||
0x89, 0x99, 0xbb, 0x68, 0x82, 0x38, 0x40, 0xa4, 0xa2, 0x2a, 0x96, 0x3d, 0x3a, 0xf2, 0xf1, 0x11,
|
||||
0xa3, 0x47, 0x21, 0x8e, 0xec, 0xd5, 0x96, 0xd5, 0xa9, 0xba, 0x4b, 0x18, 0xf2, 0x04, 0x1a, 0x49,
|
||||
0x25, 0x3c, 0x64, 0x34, 0x9c, 0xa9, 0xc0, 0x93, 0xf6, 0x9a, 0x39, 0xf3, 0x76, 0x1e, 0xc5, 0xe3,
|
||||
0xf3, 0x7c, 0x7a, 0xdc, 0xb2, 0x1a, 0x79, 0x0b, 0x1b, 0xc7, 0xb1, 0x54, 0x7c, 0x12, 0xbc, 0xc5,
|
||||
0x17, 0x91, 0xa9, 0x26, 0xbb, 0x6a, 0x4c, 0x3d, 0x77, 0xce, 0x0a, 0xc0, 0xc9, 0x0a, 0xc0, 0x2c,
|
||||
0xde, 0x78, 0x23, 0x67, 0xba, 0xe7, 0x44, 0xc7, 0xbe, 0xa3, 0xcb, 0xc9, 0x29, 0x94, 0x93, 0x93,
|
||||
0x95, 0x93, 0xf3, 0xb4, 0x64, 0xd5, 0x5d, 0xf0, 0x43, 0xfe, 0x0f, 0x2b, 0x63, 0x0c, 0x23, 0xbb,
|
||||
0x66, 0xfc, 0xad, 0xe7, 0xa1, 0x3f, 0xc1, 0x30, 0x72, 0x0d, 0x45, 0x3e, 0x80, 0xb5, 0x28, 0x8c,
|
||||
0xfd, 0x80, 0x49, 0x1b, 0x4c, 0x9a, 0x1b, 0xb9, 0xd4, 0x81, 0xc1, 0xdd, 0x8c, 0xd7, 0x39, 0x8c,
|
||||
0x25, 0x8a, 0x7d, 0xae, 0x77, 0x83, 0x40, 0x26, 0x39, 0xac, 0x27, 0x39, 0x5c, 0x64, 0xc8, 0x8f,
|
||||
0x16, 0xdc, 0xf4, 0x4c, 0x56, 0x9e, 0x51, 0x46, 0x7d, 0x9c, 0x20, 0x53, 0x07, 0xa9, 0xaf, 0x2b,
|
||||
0xc6, 0xd7, 0xab, 0xf7, 0xcb, 0x40, 0x7f, 0xa9, 0x71, 0xf7, 0x22, 0xa7, 0xe4, 0x23, 0xd8, 0xcc,
|
||||
0x53, 0xf4, 0x1a, 0x85, 0x34, 0x77, 0xb1, 0xde, 0xaa, 0x74, 0x6a, 0xee, 0x22, 0x41, 0x9a, 0x50,
|
||||
0x8d, 0x83, 0xbe, 0x94, 0x87, 0xee, 0xbe, 0x7d, 0xd5, 0x54, 0x6a, 0xbe, 0x27, 0x1d, 0x68, 0xc4,
|
||||
0x41, 0x8f, 0x32, 0x86, 0xa2, 0xcf, 0x99, 0x42, 0xa6, 0xec, 0x86, 0x11, 0x29, 0xc3, 0xba, 0xe4,
|
||||
0x33, 0x48, 0x1b, 0xda, 0x48, 0x4a, 0xbe, 0x00, 0x69, 0x5b, 0x11, 0x95, 0xf2, 0x7b, 0x2e, 0x46,
|
||||
0x07, 0x54, 0x29, 0x14, 0xcc, 0xde, 0x4c, 0x6c, 0x95, 0x60, 0x72, 0x07, 0xae, 0x2a, 0x41, 0xbd,
|
||||
0xe3, 0x80, 0xf9, 0xcf, 0x50, 0x8d, 0xf9, 0xc8, 0x26, 0x46, 0xb0, 0x84, 0xea, 0x73, 0x66, 0x0e,
|
||||
0x0e, 0x50, 0x4c, 0x28, 0xd3, 0xf1, 0x5d, 0x33, 0xf7, 0xb4, 0x48, 0x90, 0x0f, 0x61, 0x23, 0x07,
|
||||
0xb9, 0x0c, 0x74, 0x8a, 0xed, 0xeb, 0xc6, 0xee, 0x02, 0x5e, 0x6a, 0x23, 0x97, 0x73, 0x75, 0x28,
|
||||
0x42, 0xfb, 0x86, 0x91, 0x5e, 0xc2, 0xe8, 0xd3, 0xe3, 0x09, 0x7a, 0x59, 0xbf, 0x6d, 0x99, 0x18,
|
||||
0x8a, 0x10, 0xb9, 0x0b, 0xd7, 0x3c, 0xce, 0x94, 0xe0, 0x61, 0x88, 0xe2, 0x39, 0x9d, 0xa0, 0x8c,
|
||||
0xa8, 0x87, 0xf6, 0x4d, 0x63, 0x72, 0x19, 0x45, 0x3e, 0x87, 0x5b, 0x34, 0x8a, 0xe4, 0x90, 0x3d,
|
||||
0x64, 0xb3, 0x1c, 0xcd, 0x3c, 0xd8, 0xc6, 0xc3, 0xc5, 0x02, 0x3a, 0x87, 0x01, 0x93, 0x8a, 0x86,
|
||||
0xa1, 0x29, 0xa6, 0xe1, 0xc0, 0x6e, 0x26, 0x39, 0x3c, 0x8f, 0x36, 0x7f, 0xb6, 0x60, 0x6b, 0xf9,
|
||||
0xf3, 0x42, 0x36, 0xa0, 0x72, 0x8c, 0xb3, 0xe4, 0x5d, 0x75, 0xf5, 0x92, 0x8c, 0xe0, 0xf2, 0x94,
|
||||
0x86, 0x31, 0xa6, 0x4f, 0xe9, 0x7b, 0x36, 0x76, 0xd9, 0xad, 0x9b, 0x18, 0xbf, 0x7f, 0xe9, 0x33,
|
||||
0xab, 0xfd, 0x06, 0x6e, 0x2c, 0x7d, 0x77, 0xc8, 0x36, 0x40, 0x56, 0x05, 0xc3, 0x41, 0x1a, 0x5b,
|
||||
0x01, 0xd1, 0xe7, 0xa6, 0x8c, 0xb3, 0x99, 0x2e, 0xf1, 0x43, 0x89, 0x42, 0x9a, 0x58, 0xab, 0x6e,
|
||||
0x09, 0x6d, 0x0f, 0xe0, 0x66, 0xf6, 0xbc, 0xa6, 0x6d, 0xe3, 0xa2, 0x8c, 0x38, 0x93, 0x58, 0x7c,
|
||||
0x2a, 0xac, 0x77, 0x3f, 0x15, 0xed, 0x5f, 0x2c, 0x58, 0xd1, 0x8f, 0x0c, 0xb1, 0x61, 0xcd, 0x1b,
|
||||
0x53, 0x53, 0x25, 0x49, 0x4c, 0xd9, 0x56, 0xb7, 0x97, 0x5e, 0xbe, 0xc2, 0x13, 0x65, 0x42, 0xa9,
|
||||
0xb9, 0xf9, 0x9e, 0x3c, 0x00, 0x38, 0x0a, 0x18, 0x15, 0xb3, 0x43, 0x11, 0x4a, 0xbb, 0x62, 0x9c,
|
||||
0xfd, 0xef, 0xdc, 0xeb, 0xe5, 0xf4, 0x72, 0x3e, 0x79, 0xf3, 0x0b, 0x0a, 0xcd, 0x07, 0xd0, 0x28,
|
||||
0xd1, 0x4b, 0xee, 0xec, 0x7a, 0xf1, 0xce, 0x6a, 0xc5, 0x1c, 0xdf, 0x86, 0xd5, 0xe4, 0x3c, 0x84,
|
||||
0xc0, 0x0a, 0xa3, 0x13, 0x4c, 0xd5, 0xcc, 0xba, 0xfd, 0x05, 0xd4, 0xf2, 0x0f, 0x92, 0xec, 0x01,
|
||||
0x78, 0x9c, 0x31, 0xf4, 0x14, 0x17, 0x59, 0x56, 0xce, 0x3e, 0xd2, 0x7e, 0x46, 0xb9, 0x05, 0xa9,
|
||||
0xf6, 0x3d, 0xa8, 0xe5, 0xc4, 0x32, 0x0f, 0x1a, 0x53, 0xb3, 0x28, 0x0b, 0xcc, 0xac, 0xdb, 0xbf,
|
||||
0x56, 0xa0, 0xf0, 0xa9, 0x2e, 0x55, 0xdb, 0x82, 0xd5, 0x40, 0xca, 0x18, 0x45, 0xaa, 0x98, 0xee,
|
||||
0x48, 0x07, 0xaa, 0x5e, 0x18, 0x20, 0x53, 0xc3, 0x81, 0xf9, 0xb7, 0x6b, 0xbd, 0x2b, 0xf3, 0xd3,
|
||||
0x9d, 0x6a, 0x3f, 0xc5, 0xdc, 0x9c, 0x25, 0xbb, 0x50, 0xf7, 0xc2, 0x20, 0x23, 0x92, 0xef, 0xb9,
|
||||
0xd7, 0x98, 0x9f, 0xee, 0xd4, 0xfb, 0xfb, 0xc3, 0x5c, 0xbe, 0x28, 0xa3, 0x9d, 0x4a, 0x8f, 0x47,
|
||||
0xe9, 0x27, 0x5d, 0x73, 0xd3, 0x1d, 0x79, 0x03, 0xeb, 0xc1, 0xe8, 0x15, 0x3f, 0x46, 0xd6, 0x37,
|
||||
0x03, 0x8b, 0xbd, 0x6a, 0x72, 0x73, 0x67, 0xc9, 0xc4, 0xe0, 0x0c, 0x8b, 0x82, 0xe6, 0xba, 0x7a,
|
||||
0x9b, 0xf3, 0xd3, 0x9d, 0xf5, 0xe1, 0xa0, 0x80, 0xbb, 0xe7, 0xed, 0x91, 0xfb, 0x60, 0xa3, 0x69,
|
||||
0xe9, 0x83, 0xa7, 0xfd, 0x47, 0x0f, 0x63, 0x35, 0x46, 0xa6, 0xd2, 0x4e, 0x32, 0x3f, 0x75, 0xd5,
|
||||
0xbd, 0x90, 0x6f, 0xce, 0x80, 0x2c, 0xfa, 0x5c, 0x52, 0x22, 0xcf, 0xce, 0xb7, 0xf5, 0xa7, 0xef,
|
||||
0x6c, 0xeb, 0x64, 0x5a, 0x73, 0xf2, 0x71, 0x53, 0x8f, 0x3d, 0x8e, 0xb1, 0x5f, 0xa8, 0xad, 0xbd,
|
||||
0xdf, 0x2c, 0x68, 0x64, 0xfd, 0xf5, 0x12, 0xc5, 0x34, 0xf0, 0x90, 0x7c, 0x05, 0x95, 0xc7, 0xa8,
|
||||
0xc8, 0xd6, 0xc2, 0x7c, 0x63, 0x66, 0xba, 0xe6, 0xe6, 0x02, 0xde, 0xb6, 0x7f, 0xf8, 0xf3, 0xef,
|
||||
0x9f, 0x2e, 0x11, 0xb2, 0x61, 0xe6, 0xd4, 0xe9, 0x6e, 0x3e, 0x23, 0x92, 0x31, 0xc0, 0x63, 0xcc,
|
||||
0x3f, 0xbc, 0x8b, 0x4c, 0xb6, 0x16, 0xf0, 0x52, 0xaf, 0xb7, 0x5b, 0xc6, 0x43, 0x93, 0xd8, 0x65,
|
||||
0x0f, 0xdd, 0xb4, 0xc5, 0x7b, 0xfd, 0xdf, 0xe7, 0xdb, 0xd6, 0x1f, 0xf3, 0x6d, 0xeb, 0xaf, 0xf9,
|
||||
0xb6, 0xf5, 0xcd, 0x27, 0xff, 0x6d, 0x32, 0x4e, 0x4a, 0x2d, 0x37, 0x76, 0xb4, 0x6a, 0xe6, 0xd8,
|
||||
0x7b, 0xff, 0x04, 0x00, 0x00, 0xff, 0xff, 0x1d, 0x4a, 0xd6, 0x7b, 0xb6, 0x0b, 0x00, 0x00,
|
||||
}
|
||||
|
||||
// Reference imports to suppress errors if they are not otherwise used.
|
||||
@@ -990,6 +999,15 @@ func (m *Settings) MarshalToSizedBuffer(dAtA []byte) (int, error) {
|
||||
i -= len(m.XXX_unrecognized)
|
||||
copy(dAtA[i:], m.XXX_unrecognized)
|
||||
}
|
||||
if len(m.InstallationID) > 0 {
|
||||
i -= len(m.InstallationID)
|
||||
copy(dAtA[i:], m.InstallationID)
|
||||
i = encodeVarintSettings(dAtA, i, uint64(len(m.InstallationID)))
|
||||
i--
|
||||
dAtA[i] = 0x1
|
||||
i--
|
||||
dAtA[i] = 0xd2
|
||||
}
|
||||
if m.AppsInAnyNamespaceEnabled {
|
||||
i--
|
||||
if m.AppsInAnyNamespaceEnabled {
|
||||
@@ -1750,6 +1768,10 @@ func (m *Settings) Size() (n int) {
|
||||
if m.AppsInAnyNamespaceEnabled {
|
||||
n += 3
|
||||
}
|
||||
l = len(m.InstallationID)
|
||||
if l > 0 {
|
||||
n += 2 + l + sovSettings(uint64(l))
|
||||
}
|
||||
if m.XXX_unrecognized != nil {
|
||||
n += len(m.XXX_unrecognized)
|
||||
}
|
||||
@@ -2840,6 +2862,38 @@ func (m *Settings) Unmarshal(dAtA []byte) error {
|
||||
}
|
||||
}
|
||||
m.AppsInAnyNamespaceEnabled = bool(v != 0)
|
||||
case 26:
|
||||
if wireType != 2 {
|
||||
return fmt.Errorf("proto: wrong wireType = %d for field InstallationID", wireType)
|
||||
}
|
||||
var stringLen uint64
|
||||
for shift := uint(0); ; shift += 7 {
|
||||
if shift >= 64 {
|
||||
return ErrIntOverflowSettings
|
||||
}
|
||||
if iNdEx >= l {
|
||||
return io.ErrUnexpectedEOF
|
||||
}
|
||||
b := dAtA[iNdEx]
|
||||
iNdEx++
|
||||
stringLen |= uint64(b&0x7F) << shift
|
||||
if b < 0x80 {
|
||||
break
|
||||
}
|
||||
}
|
||||
intStringLen := int(stringLen)
|
||||
if intStringLen < 0 {
|
||||
return ErrInvalidLengthSettings
|
||||
}
|
||||
postIndex := iNdEx + intStringLen
|
||||
if postIndex < 0 {
|
||||
return ErrInvalidLengthSettings
|
||||
}
|
||||
if postIndex > l {
|
||||
return io.ErrUnexpectedEOF
|
||||
}
|
||||
m.InstallationID = string(dAtA[iNdEx:postIndex])
|
||||
iNdEx = postIndex
|
||||
default:
|
||||
iNdEx = preIndex
|
||||
skippy, err := skipSettings(dAtA[iNdEx:])
|
||||
|
||||
@@ -562,5 +562,5 @@ func (p AppProject) IsAppNamespacePermitted(app *Application, controllerNs strin
|
||||
return true
|
||||
}
|
||||
|
||||
return glob.MatchStringInList(p.Spec.SourceNamespaces, app.Namespace, false)
|
||||
return glob.MatchStringInList(p.Spec.SourceNamespaces, app.Namespace, glob.REGEXP)
|
||||
}
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -2174,6 +2174,9 @@ message SyncOperation {
|
||||
// Revisions is the list of revision (Git) or chart version (Helm) which to sync each source in sources field for the application to
|
||||
// If omitted, will use the revision specified in app spec.
|
||||
repeated string revisions = 11;
|
||||
|
||||
// SelfHealAttemptsCount contains the number of auto-heal attempts
|
||||
optional int64 autoHealAttemptsCount = 12;
|
||||
}
|
||||
|
||||
// SyncOperationResource contains resources to sync.
|
||||
@@ -2241,7 +2244,6 @@ message SyncStatus {
|
||||
optional string status = 1;
|
||||
|
||||
// ComparedTo contains information about what has been compared
|
||||
// +patchStrategy=replace
|
||||
optional ComparedTo comparedTo = 2;
|
||||
|
||||
// Revision contains information about the revision the comparison has been performed to
|
||||
|
||||
@@ -7758,11 +7758,6 @@ func schema_pkg_apis_application_v1alpha1_SyncStatus(ref common.ReferenceCallbac
|
||||
},
|
||||
},
|
||||
"comparedTo": {
|
||||
VendorExtensible: spec.VendorExtensible{
|
||||
Extensions: spec.Extensions{
|
||||
"x-kubernetes-patch-strategy": "replace",
|
||||
},
|
||||
},
|
||||
SchemaProps: spec.SchemaProps{
|
||||
Description: "ComparedTo contains information about what has been compared",
|
||||
Default: map[string]interface{}{},
|
||||
|
||||
@@ -3,6 +3,7 @@ package v1alpha1
|
||||
import (
|
||||
"fmt"
|
||||
"net/url"
|
||||
"strings"
|
||||
|
||||
"github.com/argoproj/argo-cd/v2/util/cert"
|
||||
"github.com/argoproj/argo-cd/v2/util/git"
|
||||
@@ -227,21 +228,22 @@ func getCAPath(repoURL string) string {
|
||||
}
|
||||
|
||||
hostname := ""
|
||||
// url.Parse() will happily parse most things thrown at it. When the URL
|
||||
// is either https or oci, we use the parsed hostname to retrieve the cert,
|
||||
// otherwise we'll use the parsed path (OCI repos are often specified as
|
||||
// hostname, without protocol).
|
||||
parsedURL, err := url.Parse(repoURL)
|
||||
var parsedURL *url.URL
|
||||
var err error
|
||||
// Without schema in url, url.Parse() treats the url as differently
|
||||
// and may incorrectly parses the hostname if url contains a path or port.
|
||||
// To ensure proper parsing, prepend a dummy schema.
|
||||
if !strings.Contains(repoURL, "://") {
|
||||
parsedURL, err = url.Parse("protocol://" + repoURL)
|
||||
} else {
|
||||
parsedURL, err = url.Parse(repoURL)
|
||||
}
|
||||
if err != nil {
|
||||
log.Warnf("Could not parse repo URL '%s': %v", repoURL, err)
|
||||
return ""
|
||||
}
|
||||
if parsedURL.Scheme == "https" || parsedURL.Scheme == "oci" {
|
||||
hostname = parsedURL.Host
|
||||
} else if parsedURL.Scheme == "" {
|
||||
hostname = parsedURL.Path
|
||||
}
|
||||
|
||||
hostname = parsedURL.Hostname()
|
||||
if hostname == "" {
|
||||
log.Warnf("Could not get hostname for repository '%s'", repoURL)
|
||||
return ""
|
||||
|
||||
@@ -938,6 +938,12 @@ type ApplicationDestination struct {
|
||||
isServerInferred bool `json:"-"`
|
||||
}
|
||||
|
||||
// SetIsServerInferred sets the isServerInferred flag. This is used to allow comparison between two destinations where
|
||||
// one server is inferred and the other is not.
|
||||
func (d *ApplicationDestination) SetIsServerInferred(inferred bool) {
|
||||
d.isServerInferred = inferred
|
||||
}
|
||||
|
||||
type ResourceHealthLocation string
|
||||
|
||||
var (
|
||||
@@ -992,15 +998,15 @@ func (a *ApplicationStatus) GetRevisions() []string {
|
||||
|
||||
// BuildComparedToStatus will build a ComparedTo object based on the current
|
||||
// Application state.
|
||||
func (app *Application) BuildComparedToStatus() ComparedTo {
|
||||
func (spec *ApplicationSpec) BuildComparedToStatus() ComparedTo {
|
||||
ct := ComparedTo{
|
||||
Destination: app.Spec.Destination,
|
||||
IgnoreDifferences: app.Spec.IgnoreDifferences,
|
||||
Destination: spec.Destination,
|
||||
IgnoreDifferences: spec.IgnoreDifferences,
|
||||
}
|
||||
if app.Spec.HasMultipleSources() {
|
||||
ct.Sources = app.Spec.Sources
|
||||
if spec.HasMultipleSources() {
|
||||
ct.Sources = spec.Sources
|
||||
} else {
|
||||
ct.Source = app.Spec.GetSource()
|
||||
ct.Source = spec.GetSource()
|
||||
}
|
||||
return ct
|
||||
}
|
||||
@@ -1110,6 +1116,8 @@ type SyncOperation struct {
|
||||
// Revisions is the list of revision (Git) or chart version (Helm) which to sync each source in sources field for the application to
|
||||
// If omitted, will use the revision specified in app spec.
|
||||
Revisions []string `json:"revisions,omitempty" protobuf:"bytes,11,opt,name=revisions"`
|
||||
// SelfHealAttemptsCount contains the number of auto-heal attempts
|
||||
SelfHealAttemptsCount int64 `json:"autoHealAttemptsCount,omitempty" protobuf:"bytes,12,opt,name=autoHealAttemptsCount"`
|
||||
}
|
||||
|
||||
// IsApplyStrategy returns true if the sync strategy is "apply"
|
||||
@@ -1519,8 +1527,7 @@ type SyncStatus struct {
|
||||
// Status is the sync state of the comparison
|
||||
Status SyncStatusCode `json:"status" protobuf:"bytes,1,opt,name=status,casttype=SyncStatusCode"`
|
||||
// ComparedTo contains information about what has been compared
|
||||
// +patchStrategy=replace
|
||||
ComparedTo ComparedTo `json:"comparedTo,omitempty" protobuf:"bytes,2,opt,name=comparedTo" patchStrategy:"replace"`
|
||||
ComparedTo ComparedTo `json:"comparedTo,omitempty" protobuf:"bytes,2,opt,name=comparedTo"`
|
||||
// Revision contains information about the revision the comparison has been performed to
|
||||
Revision string `json:"revision,omitempty" protobuf:"bytes,3,opt,name=revision"`
|
||||
// Revisions contains information about the revisions of multiple sources the comparison has been performed to
|
||||
@@ -2073,6 +2080,8 @@ var validActions = map[string]bool{
|
||||
|
||||
var validActionPatterns = []*regexp.Regexp{
|
||||
regexp.MustCompile("action/.*"),
|
||||
regexp.MustCompile("update/.*"),
|
||||
regexp.MustCompile("delete/.*"),
|
||||
}
|
||||
|
||||
func isValidAction(action string) bool {
|
||||
@@ -2260,11 +2269,11 @@ func (s *SyncWindows) HasWindows() bool {
|
||||
}
|
||||
|
||||
// Active returns a list of sync windows that are currently active
|
||||
func (s *SyncWindows) Active() *SyncWindows {
|
||||
func (s *SyncWindows) Active() (*SyncWindows, error) {
|
||||
return s.active(time.Now())
|
||||
}
|
||||
|
||||
func (s *SyncWindows) active(currentTime time.Time) *SyncWindows {
|
||||
func (s *SyncWindows) active(currentTime time.Time) (*SyncWindows, error) {
|
||||
// If SyncWindows.Active() is called outside of a UTC locale, it should be
|
||||
// first converted to UTC before we scan through the SyncWindows.
|
||||
currentTime = currentTime.In(time.UTC)
|
||||
@@ -2273,8 +2282,14 @@ func (s *SyncWindows) active(currentTime time.Time) *SyncWindows {
|
||||
var active SyncWindows
|
||||
specParser := cron.NewParser(cron.Minute | cron.Hour | cron.Dom | cron.Month | cron.Dow)
|
||||
for _, w := range *s {
|
||||
schedule, _ := specParser.Parse(w.Schedule)
|
||||
duration, _ := time.ParseDuration(w.Duration)
|
||||
schedule, sErr := specParser.Parse(w.Schedule)
|
||||
if sErr != nil {
|
||||
return nil, fmt.Errorf("cannot parse schedule '%s': %w", w.Schedule, sErr)
|
||||
}
|
||||
duration, dErr := time.ParseDuration(w.Duration)
|
||||
if dErr != nil {
|
||||
return nil, fmt.Errorf("cannot parse duration '%s': %w", w.Duration, dErr)
|
||||
}
|
||||
|
||||
// Offset the nextWindow time to consider the timeZone of the sync window
|
||||
timeZoneOffsetDuration := w.scheduleOffsetByTimeZone()
|
||||
@@ -2284,20 +2299,20 @@ func (s *SyncWindows) active(currentTime time.Time) *SyncWindows {
|
||||
}
|
||||
}
|
||||
if len(active) > 0 {
|
||||
return &active
|
||||
return &active, nil
|
||||
}
|
||||
}
|
||||
return nil
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
// InactiveAllows will iterate over the SyncWindows and return all inactive allow windows
|
||||
// for the current time. If the current time is in an inactive allow window, syncs will
|
||||
// be denied.
|
||||
func (s *SyncWindows) InactiveAllows() *SyncWindows {
|
||||
func (s *SyncWindows) InactiveAllows() (*SyncWindows, error) {
|
||||
return s.inactiveAllows(time.Now())
|
||||
}
|
||||
|
||||
func (s *SyncWindows) inactiveAllows(currentTime time.Time) *SyncWindows {
|
||||
func (s *SyncWindows) inactiveAllows(currentTime time.Time) (*SyncWindows, error) {
|
||||
// If SyncWindows.InactiveAllows() is called outside of a UTC locale, it should be
|
||||
// first converted to UTC before we scan through the SyncWindows.
|
||||
currentTime = currentTime.In(time.UTC)
|
||||
@@ -2308,21 +2323,27 @@ func (s *SyncWindows) inactiveAllows(currentTime time.Time) *SyncWindows {
|
||||
for _, w := range *s {
|
||||
if w.Kind == "allow" {
|
||||
schedule, sErr := specParser.Parse(w.Schedule)
|
||||
if sErr != nil {
|
||||
return nil, fmt.Errorf("cannot parse schedule '%s': %w", w.Schedule, sErr)
|
||||
}
|
||||
duration, dErr := time.ParseDuration(w.Duration)
|
||||
if dErr != nil {
|
||||
return nil, fmt.Errorf("cannot parse duration '%s': %w", w.Duration, dErr)
|
||||
}
|
||||
// Offset the nextWindow time to consider the timeZone of the sync window
|
||||
timeZoneOffsetDuration := w.scheduleOffsetByTimeZone()
|
||||
nextWindow := schedule.Next(currentTime.Add(timeZoneOffsetDuration - duration))
|
||||
|
||||
if !nextWindow.Before(currentTime.Add(timeZoneOffsetDuration)) && sErr == nil && dErr == nil {
|
||||
if !nextWindow.Before(currentTime.Add(timeZoneOffsetDuration)) {
|
||||
inactive = append(inactive, w)
|
||||
}
|
||||
}
|
||||
}
|
||||
if len(inactive) > 0 {
|
||||
return &inactive
|
||||
return &inactive, nil
|
||||
}
|
||||
}
|
||||
return nil
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
func (w *SyncWindow) scheduleOffsetByTimeZone() time.Duration {
|
||||
@@ -2426,36 +2447,42 @@ func (w *SyncWindows) Matches(app *Application) *SyncWindows {
|
||||
}
|
||||
|
||||
// CanSync returns true if a sync window currently allows a sync. isManual indicates whether the sync has been triggered manually.
|
||||
func (w *SyncWindows) CanSync(isManual bool) bool {
|
||||
func (w *SyncWindows) CanSync(isManual bool) (bool, error) {
|
||||
if !w.HasWindows() {
|
||||
return true
|
||||
return true, nil
|
||||
}
|
||||
|
||||
active := w.Active()
|
||||
active, err := w.Active()
|
||||
if err != nil {
|
||||
return false, fmt.Errorf("invalid sync windows: %w", err)
|
||||
}
|
||||
hasActiveDeny, manualEnabled := active.hasDeny()
|
||||
|
||||
if hasActiveDeny {
|
||||
if isManual && manualEnabled {
|
||||
return true
|
||||
return true, nil
|
||||
} else {
|
||||
return false
|
||||
return false, nil
|
||||
}
|
||||
}
|
||||
|
||||
if active.hasAllow() {
|
||||
return true
|
||||
return true, nil
|
||||
}
|
||||
|
||||
inactiveAllows := w.InactiveAllows()
|
||||
inactiveAllows, err := w.InactiveAllows()
|
||||
if err != nil {
|
||||
return false, fmt.Errorf("invalid sync windows: %w", err)
|
||||
}
|
||||
if inactiveAllows.HasWindows() {
|
||||
if isManual && inactiveAllows.manualEnabled() {
|
||||
return true
|
||||
return true, nil
|
||||
} else {
|
||||
return false
|
||||
return false, nil
|
||||
}
|
||||
}
|
||||
|
||||
return true
|
||||
return true, nil
|
||||
}
|
||||
|
||||
// hasDeny will iterate over the SyncWindows and return if a deny window is found and if
|
||||
@@ -2510,24 +2537,30 @@ func (w *SyncWindows) manualEnabled() bool {
|
||||
}
|
||||
|
||||
// Active returns true if the sync window is currently active
|
||||
func (w SyncWindow) Active() bool {
|
||||
func (w SyncWindow) Active() (bool, error) {
|
||||
return w.active(time.Now())
|
||||
}
|
||||
|
||||
func (w SyncWindow) active(currentTime time.Time) bool {
|
||||
func (w SyncWindow) active(currentTime time.Time) (bool, error) {
|
||||
// If SyncWindow.Active() is called outside of a UTC locale, it should be
|
||||
// first converted to UTC before search
|
||||
currentTime = currentTime.UTC()
|
||||
|
||||
specParser := cron.NewParser(cron.Minute | cron.Hour | cron.Dom | cron.Month | cron.Dow)
|
||||
schedule, _ := specParser.Parse(w.Schedule)
|
||||
duration, _ := time.ParseDuration(w.Duration)
|
||||
schedule, sErr := specParser.Parse(w.Schedule)
|
||||
if sErr != nil {
|
||||
return false, fmt.Errorf("cannot parse schedule '%s': %w", w.Schedule, sErr)
|
||||
}
|
||||
duration, dErr := time.ParseDuration(w.Duration)
|
||||
if dErr != nil {
|
||||
return false, fmt.Errorf("cannot parse duration '%s': %w", w.Duration, dErr)
|
||||
}
|
||||
|
||||
// Offset the nextWindow time to consider the timeZone of the sync window
|
||||
timeZoneOffsetDuration := w.scheduleOffsetByTimeZone()
|
||||
nextWindow := schedule.Next(currentTime.Add(timeZoneOffsetDuration - duration))
|
||||
|
||||
return nextWindow.Before(currentTime.Add(timeZoneOffsetDuration))
|
||||
return nextWindow.Before(currentTime.Add(timeZoneOffsetDuration)), nil
|
||||
}
|
||||
|
||||
// Update updates a sync window's settings with the given parameter
|
||||
|
||||
@@ -11,10 +11,7 @@ import (
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/argoproj/gitops-engine/pkg/diff"
|
||||
"github.com/stretchr/testify/require"
|
||||
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
|
||||
"k8s.io/apimachinery/pkg/runtime"
|
||||
"k8s.io/utils/ptr"
|
||||
|
||||
argocdcommon "github.com/argoproj/argo-cd/v2/common"
|
||||
@@ -827,8 +824,12 @@ func TestAppProject_ValidPolicyRules(t *testing.T) {
|
||||
"p, proj:my-proj:my-role, applications, *, my-proj/foo, allow",
|
||||
"p, proj:my-proj:my-role, applications, create, my-proj/foo, allow",
|
||||
"p, proj:my-proj:my-role, applications, update, my-proj/foo, allow",
|
||||
"p, proj:my-proj:my-role, applications, update/*, my-proj/foo, allow",
|
||||
"p, proj:my-proj:my-role, applications, update/*/Pod/*, my-proj/foo, allow",
|
||||
"p, proj:my-proj:my-role, applications, sync, my-proj/foo, allow",
|
||||
"p, proj:my-proj:my-role, applications, delete, my-proj/foo, allow",
|
||||
"p, proj:my-proj:my-role, applications, delete/*, my-proj/foo, allow",
|
||||
"p, proj:my-proj:my-role, applications, delete/*/Pod/*, my-proj/foo, allow",
|
||||
"p, proj:my-proj:my-role, applications, action/*, my-proj/foo, allow",
|
||||
"p, proj:my-proj:my-role, applications, action/apps/Deployment/restart, my-proj/foo, allow",
|
||||
}
|
||||
@@ -1630,7 +1631,9 @@ func TestSyncWindows_HasWindows(t *testing.T) {
|
||||
func TestSyncWindows_Active(t *testing.T) {
|
||||
t.Run("WithTestProject", func(t *testing.T) {
|
||||
proj := newTestProjectWithSyncWindows()
|
||||
assert.Len(t, *proj.Spec.SyncWindows.Active(), 1)
|
||||
activeWindows, err := proj.Spec.SyncWindows.Active()
|
||||
require.NoError(t, err)
|
||||
assert.Len(t, *activeWindows, 1)
|
||||
})
|
||||
|
||||
syncWindow := func(kind string, schedule string, duration string, timeZone string) *SyncWindow {
|
||||
@@ -1657,6 +1660,7 @@ func TestSyncWindows_Active(t *testing.T) {
|
||||
currentTime time.Time
|
||||
matchingIndex int
|
||||
expectedLength int
|
||||
isErr bool
|
||||
}{
|
||||
{
|
||||
name: "MatchFirst",
|
||||
@@ -1764,11 +1768,36 @@ func TestSyncWindows_Active(t *testing.T) {
|
||||
matchingIndex: 0,
|
||||
expectedLength: 1,
|
||||
},
|
||||
{
|
||||
name: "MatchNone-InvalidSchedule",
|
||||
syncWindow: SyncWindows{
|
||||
syncWindow("allow", "* 10 * * 7", "3h", ""),
|
||||
syncWindow("allow", "* 11 * * 7", "3h", ""),
|
||||
},
|
||||
currentTime: timeWithHour(12, time.UTC),
|
||||
expectedLength: 0,
|
||||
isErr: true,
|
||||
},
|
||||
{
|
||||
name: "MatchNone-InvalidDuration",
|
||||
syncWindow: SyncWindows{
|
||||
syncWindow("allow", "* 10 * * *", "3a", ""),
|
||||
syncWindow("allow", "* 11 * * *", "3a", ""),
|
||||
},
|
||||
currentTime: timeWithHour(12, time.UTC),
|
||||
expectedLength: 0,
|
||||
isErr: true,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
result := tt.syncWindow.active(tt.currentTime)
|
||||
result, err := tt.syncWindow.active(tt.currentTime)
|
||||
if tt.isErr {
|
||||
require.Error(t, err)
|
||||
} else {
|
||||
require.NoError(t, err)
|
||||
}
|
||||
if result == nil {
|
||||
result = &SyncWindows{}
|
||||
}
|
||||
@@ -1785,7 +1814,9 @@ func TestSyncWindows_InactiveAllows(t *testing.T) {
|
||||
t.Run("WithTestProject", func(t *testing.T) {
|
||||
proj := newTestProjectWithSyncWindows()
|
||||
proj.Spec.SyncWindows[0].Schedule = "0 0 1 1 1"
|
||||
assert.Len(t, *proj.Spec.SyncWindows.InactiveAllows(), 1)
|
||||
inactiveAllowWindows, err := proj.Spec.SyncWindows.InactiveAllows()
|
||||
require.NoError(t, err)
|
||||
assert.Len(t, *inactiveAllowWindows, 1)
|
||||
})
|
||||
|
||||
syncWindow := func(kind string, schedule string, duration string, timeZone string) *SyncWindow {
|
||||
@@ -1812,6 +1843,7 @@ func TestSyncWindows_InactiveAllows(t *testing.T) {
|
||||
currentTime time.Time
|
||||
matchingIndex int
|
||||
expectedLength int
|
||||
isErr bool
|
||||
}{
|
||||
{
|
||||
name: "MatchFirst",
|
||||
@@ -1937,11 +1969,34 @@ func TestSyncWindows_InactiveAllows(t *testing.T) {
|
||||
matchingIndex: 0,
|
||||
expectedLength: 1,
|
||||
},
|
||||
{
|
||||
name: "MatchNone-InvalidSchedule",
|
||||
syncWindow: SyncWindows{
|
||||
syncWindow("allow", "* 10 * * 7", "2h", ""),
|
||||
},
|
||||
currentTime: timeWithHour(17, time.UTC),
|
||||
expectedLength: 0,
|
||||
isErr: true,
|
||||
},
|
||||
{
|
||||
name: "MatchNone-InvalidDuration",
|
||||
syncWindow: SyncWindows{
|
||||
syncWindow("allow", "* 10 * * *", "2a", ""),
|
||||
},
|
||||
currentTime: timeWithHour(17, time.UTC),
|
||||
expectedLength: 0,
|
||||
isErr: true,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
result := tt.syncWindow.inactiveAllows(tt.currentTime)
|
||||
result, err := tt.syncWindow.inactiveAllows(tt.currentTime)
|
||||
if tt.isErr {
|
||||
require.Error(t, err)
|
||||
} else {
|
||||
require.NoError(t, err)
|
||||
}
|
||||
if result == nil {
|
||||
result = &SyncWindows{}
|
||||
}
|
||||
@@ -2052,9 +2107,10 @@ func TestSyncWindows_CanSync(t *testing.T) {
|
||||
proj := newProjectBuilder().withInactiveDenyWindow(true).build()
|
||||
|
||||
// when
|
||||
canSync := proj.Spec.SyncWindows.CanSync(true)
|
||||
canSync, err := proj.Spec.SyncWindows.CanSync(true)
|
||||
|
||||
// then
|
||||
require.NoError(t, err)
|
||||
assert.True(t, canSync)
|
||||
})
|
||||
t.Run("will allow manual sync if inactive-deny-window set with manual false", func(t *testing.T) {
|
||||
@@ -2063,9 +2119,10 @@ func TestSyncWindows_CanSync(t *testing.T) {
|
||||
proj := newProjectBuilder().withInactiveDenyWindow(false).build()
|
||||
|
||||
// when
|
||||
canSync := proj.Spec.SyncWindows.CanSync(true)
|
||||
canSync, err := proj.Spec.SyncWindows.CanSync(true)
|
||||
|
||||
// then
|
||||
require.NoError(t, err)
|
||||
assert.True(t, canSync)
|
||||
})
|
||||
t.Run("will deny manual sync if one inactive-allow-windows set with manual false", func(t *testing.T) {
|
||||
@@ -2077,9 +2134,10 @@ func TestSyncWindows_CanSync(t *testing.T) {
|
||||
build()
|
||||
|
||||
// when
|
||||
canSync := proj.Spec.SyncWindows.CanSync(true)
|
||||
canSync, err := proj.Spec.SyncWindows.CanSync(true)
|
||||
|
||||
// then
|
||||
require.NoError(t, err)
|
||||
assert.False(t, canSync)
|
||||
})
|
||||
t.Run("will allow manual sync if on active-allow-window set with manual true", func(t *testing.T) {
|
||||
@@ -2090,9 +2148,10 @@ func TestSyncWindows_CanSync(t *testing.T) {
|
||||
build()
|
||||
|
||||
// when
|
||||
canSync := proj.Spec.SyncWindows.CanSync(true)
|
||||
canSync, err := proj.Spec.SyncWindows.CanSync(true)
|
||||
|
||||
// then
|
||||
require.NoError(t, err)
|
||||
assert.True(t, canSync)
|
||||
})
|
||||
t.Run("will allow manual sync if on active-allow-window set with manual false", func(t *testing.T) {
|
||||
@@ -2103,9 +2162,10 @@ func TestSyncWindows_CanSync(t *testing.T) {
|
||||
build()
|
||||
|
||||
// when
|
||||
canSync := proj.Spec.SyncWindows.CanSync(true)
|
||||
canSync, err := proj.Spec.SyncWindows.CanSync(true)
|
||||
|
||||
// then
|
||||
require.NoError(t, err)
|
||||
assert.True(t, canSync)
|
||||
})
|
||||
t.Run("will allow auto sync if on active-allow-window", func(t *testing.T) {
|
||||
@@ -2116,9 +2176,10 @@ func TestSyncWindows_CanSync(t *testing.T) {
|
||||
build()
|
||||
|
||||
// when
|
||||
canSync := proj.Spec.SyncWindows.CanSync(false)
|
||||
canSync, err := proj.Spec.SyncWindows.CanSync(false)
|
||||
|
||||
// then
|
||||
require.NoError(t, err)
|
||||
assert.True(t, canSync)
|
||||
})
|
||||
t.Run("will allow manual sync active-allow and inactive-deny", func(t *testing.T) {
|
||||
@@ -2130,9 +2191,10 @@ func TestSyncWindows_CanSync(t *testing.T) {
|
||||
build()
|
||||
|
||||
// when
|
||||
canSync := proj.Spec.SyncWindows.CanSync(true)
|
||||
canSync, err := proj.Spec.SyncWindows.CanSync(true)
|
||||
|
||||
// then
|
||||
require.NoError(t, err)
|
||||
assert.True(t, canSync)
|
||||
})
|
||||
t.Run("will allow auto sync active-allow and inactive-deny", func(t *testing.T) {
|
||||
@@ -2144,9 +2206,10 @@ func TestSyncWindows_CanSync(t *testing.T) {
|
||||
build()
|
||||
|
||||
// when
|
||||
canSync := proj.Spec.SyncWindows.CanSync(false)
|
||||
canSync, err := proj.Spec.SyncWindows.CanSync(false)
|
||||
|
||||
// then
|
||||
require.NoError(t, err)
|
||||
assert.True(t, canSync)
|
||||
})
|
||||
t.Run("will deny manual sync inactive-allow", func(t *testing.T) {
|
||||
@@ -2157,9 +2220,10 @@ func TestSyncWindows_CanSync(t *testing.T) {
|
||||
build()
|
||||
|
||||
// when
|
||||
canSync := proj.Spec.SyncWindows.CanSync(true)
|
||||
canSync, err := proj.Spec.SyncWindows.CanSync(true)
|
||||
|
||||
// then
|
||||
require.NoError(t, err)
|
||||
assert.False(t, canSync)
|
||||
})
|
||||
t.Run("will deny auto sync inactive-allow", func(t *testing.T) {
|
||||
@@ -2170,9 +2234,10 @@ func TestSyncWindows_CanSync(t *testing.T) {
|
||||
build()
|
||||
|
||||
// when
|
||||
canSync := proj.Spec.SyncWindows.CanSync(false)
|
||||
canSync, err := proj.Spec.SyncWindows.CanSync(false)
|
||||
|
||||
// then
|
||||
require.NoError(t, err)
|
||||
assert.False(t, canSync)
|
||||
})
|
||||
t.Run("will allow manual sync inactive-allow with ManualSync enabled", func(t *testing.T) {
|
||||
@@ -2183,9 +2248,10 @@ func TestSyncWindows_CanSync(t *testing.T) {
|
||||
build()
|
||||
|
||||
// when
|
||||
canSync := proj.Spec.SyncWindows.CanSync(true)
|
||||
canSync, err := proj.Spec.SyncWindows.CanSync(true)
|
||||
|
||||
// then
|
||||
require.NoError(t, err)
|
||||
assert.True(t, canSync)
|
||||
})
|
||||
t.Run("will deny auto sync inactive-allow with ManualSync enabled", func(t *testing.T) {
|
||||
@@ -2196,9 +2262,10 @@ func TestSyncWindows_CanSync(t *testing.T) {
|
||||
build()
|
||||
|
||||
// when
|
||||
canSync := proj.Spec.SyncWindows.CanSync(false)
|
||||
canSync, err := proj.Spec.SyncWindows.CanSync(false)
|
||||
|
||||
// then
|
||||
require.NoError(t, err)
|
||||
assert.False(t, canSync)
|
||||
})
|
||||
t.Run("will deny manual sync with inactive-allow and inactive-deny", func(t *testing.T) {
|
||||
@@ -2210,9 +2277,10 @@ func TestSyncWindows_CanSync(t *testing.T) {
|
||||
build()
|
||||
|
||||
// when
|
||||
canSync := proj.Spec.SyncWindows.CanSync(true)
|
||||
canSync, err := proj.Spec.SyncWindows.CanSync(true)
|
||||
|
||||
// then
|
||||
require.NoError(t, err)
|
||||
assert.False(t, canSync)
|
||||
})
|
||||
t.Run("will deny auto sync with inactive-allow and inactive-deny", func(t *testing.T) {
|
||||
@@ -2224,9 +2292,10 @@ func TestSyncWindows_CanSync(t *testing.T) {
|
||||
build()
|
||||
|
||||
// when
|
||||
canSync := proj.Spec.SyncWindows.CanSync(false)
|
||||
canSync, err := proj.Spec.SyncWindows.CanSync(false)
|
||||
|
||||
// then
|
||||
require.NoError(t, err)
|
||||
assert.False(t, canSync)
|
||||
})
|
||||
t.Run("will allow auto sync with active-allow and inactive-allow", func(t *testing.T) {
|
||||
@@ -2238,9 +2307,10 @@ func TestSyncWindows_CanSync(t *testing.T) {
|
||||
build()
|
||||
|
||||
// when
|
||||
canSync := proj.Spec.SyncWindows.CanSync(false)
|
||||
canSync, err := proj.Spec.SyncWindows.CanSync(false)
|
||||
|
||||
// then
|
||||
require.NoError(t, err)
|
||||
assert.True(t, canSync)
|
||||
})
|
||||
t.Run("will deny manual sync with active-deny", func(t *testing.T) {
|
||||
@@ -2251,9 +2321,10 @@ func TestSyncWindows_CanSync(t *testing.T) {
|
||||
build()
|
||||
|
||||
// when
|
||||
canSync := proj.Spec.SyncWindows.CanSync(true)
|
||||
canSync, err := proj.Spec.SyncWindows.CanSync(true)
|
||||
|
||||
// then
|
||||
require.NoError(t, err)
|
||||
assert.False(t, canSync)
|
||||
})
|
||||
t.Run("will deny auto sync with active-deny", func(t *testing.T) {
|
||||
@@ -2264,9 +2335,10 @@ func TestSyncWindows_CanSync(t *testing.T) {
|
||||
build()
|
||||
|
||||
// when
|
||||
canSync := proj.Spec.SyncWindows.CanSync(false)
|
||||
canSync, err := proj.Spec.SyncWindows.CanSync(false)
|
||||
|
||||
// then
|
||||
require.NoError(t, err)
|
||||
assert.False(t, canSync)
|
||||
})
|
||||
t.Run("will allow manual sync with active-deny with ManualSync enabled", func(t *testing.T) {
|
||||
@@ -2277,9 +2349,10 @@ func TestSyncWindows_CanSync(t *testing.T) {
|
||||
build()
|
||||
|
||||
// when
|
||||
canSync := proj.Spec.SyncWindows.CanSync(true)
|
||||
canSync, err := proj.Spec.SyncWindows.CanSync(true)
|
||||
|
||||
// then
|
||||
require.NoError(t, err)
|
||||
assert.True(t, canSync)
|
||||
})
|
||||
t.Run("will deny auto sync with active-deny with ManualSync enabled", func(t *testing.T) {
|
||||
@@ -2290,9 +2363,10 @@ func TestSyncWindows_CanSync(t *testing.T) {
|
||||
build()
|
||||
|
||||
// when
|
||||
canSync := proj.Spec.SyncWindows.CanSync(false)
|
||||
canSync, err := proj.Spec.SyncWindows.CanSync(false)
|
||||
|
||||
// then
|
||||
require.NoError(t, err)
|
||||
assert.False(t, canSync)
|
||||
})
|
||||
t.Run("will deny manual sync with many active-deny having one with ManualSync disabled", func(t *testing.T) {
|
||||
@@ -2306,9 +2380,10 @@ func TestSyncWindows_CanSync(t *testing.T) {
|
||||
build()
|
||||
|
||||
// when
|
||||
canSync := proj.Spec.SyncWindows.CanSync(true)
|
||||
canSync, err := proj.Spec.SyncWindows.CanSync(true)
|
||||
|
||||
// then
|
||||
require.NoError(t, err)
|
||||
assert.False(t, canSync)
|
||||
})
|
||||
t.Run("will deny auto sync with many active-deny having one with ManualSync disabled", func(t *testing.T) {
|
||||
@@ -2322,9 +2397,10 @@ func TestSyncWindows_CanSync(t *testing.T) {
|
||||
build()
|
||||
|
||||
// when
|
||||
canSync := proj.Spec.SyncWindows.CanSync(false)
|
||||
canSync, err := proj.Spec.SyncWindows.CanSync(false)
|
||||
|
||||
// then
|
||||
require.NoError(t, err)
|
||||
assert.False(t, canSync)
|
||||
})
|
||||
t.Run("will deny manual sync with active-deny and active-allow windows with ManualSync disabled", func(t *testing.T) {
|
||||
@@ -2336,9 +2412,10 @@ func TestSyncWindows_CanSync(t *testing.T) {
|
||||
build()
|
||||
|
||||
// when
|
||||
canSync := proj.Spec.SyncWindows.CanSync(true)
|
||||
canSync, err := proj.Spec.SyncWindows.CanSync(true)
|
||||
|
||||
// then
|
||||
require.NoError(t, err)
|
||||
assert.False(t, canSync)
|
||||
})
|
||||
t.Run("will allow manual sync with active-deny and active-allow windows with ManualSync enabled", func(t *testing.T) {
|
||||
@@ -2350,9 +2427,10 @@ func TestSyncWindows_CanSync(t *testing.T) {
|
||||
build()
|
||||
|
||||
// when
|
||||
canSync := proj.Spec.SyncWindows.CanSync(true)
|
||||
canSync, err := proj.Spec.SyncWindows.CanSync(true)
|
||||
|
||||
// then
|
||||
require.NoError(t, err)
|
||||
assert.True(t, canSync)
|
||||
})
|
||||
t.Run("will deny auto sync with active-deny and active-allow windows with ManualSync enabled", func(t *testing.T) {
|
||||
@@ -2364,9 +2442,24 @@ func TestSyncWindows_CanSync(t *testing.T) {
|
||||
build()
|
||||
|
||||
// when
|
||||
canSync := proj.Spec.SyncWindows.CanSync(false)
|
||||
canSync, err := proj.Spec.SyncWindows.CanSync(false)
|
||||
|
||||
// then
|
||||
require.NoError(t, err)
|
||||
assert.False(t, canSync)
|
||||
})
|
||||
t.Run("will deny and return error with invalid windows", func(t *testing.T) {
|
||||
// given
|
||||
t.Parallel()
|
||||
proj := newProjectBuilder().
|
||||
withInvalidWindows().
|
||||
build()
|
||||
|
||||
// when
|
||||
canSync, err := proj.Spec.SyncWindows.CanSync(false)
|
||||
|
||||
// then
|
||||
require.Error(t, err)
|
||||
assert.False(t, canSync)
|
||||
})
|
||||
}
|
||||
@@ -2416,8 +2509,9 @@ func TestSyncWindows_hasAllow(t *testing.T) {
|
||||
func TestSyncWindow_Active(t *testing.T) {
|
||||
window := &SyncWindow{Schedule: "* * * * *", Duration: "1h"}
|
||||
t.Run("ActiveWindow", func(t *testing.T) {
|
||||
window.Active()
|
||||
assert.True(t, window.Active())
|
||||
isActive, err := window.Active()
|
||||
require.NoError(t, err)
|
||||
assert.True(t, isActive)
|
||||
})
|
||||
|
||||
syncWindow := func(kind string, schedule string, duration string) SyncWindow {
|
||||
@@ -2442,6 +2536,7 @@ func TestSyncWindow_Active(t *testing.T) {
|
||||
syncWindow SyncWindow
|
||||
currentTime time.Time
|
||||
expectedResult bool
|
||||
isErr bool
|
||||
}{
|
||||
{
|
||||
name: "Allow-active",
|
||||
@@ -2491,11 +2586,44 @@ func TestSyncWindow_Active(t *testing.T) {
|
||||
currentTime: timeWithHour(13-4, utcM4Zone),
|
||||
expectedResult: false,
|
||||
},
|
||||
{
|
||||
name: "Allow-inactive-InvalidSchedule",
|
||||
syncWindow: syncWindow("allow", "* 10 * * 7", "2h"),
|
||||
currentTime: timeWithHour(11, time.UTC),
|
||||
expectedResult: false,
|
||||
isErr: true,
|
||||
},
|
||||
{
|
||||
name: "Deny-inactive-InvalidSchedule",
|
||||
syncWindow: syncWindow("deny", "* 10 * * 7", "2h"),
|
||||
currentTime: timeWithHour(11, time.UTC),
|
||||
expectedResult: false,
|
||||
isErr: true,
|
||||
},
|
||||
{
|
||||
name: "Allow-inactive-InvalidDuration",
|
||||
syncWindow: syncWindow("allow", "* 10 * * *", "2a"),
|
||||
currentTime: timeWithHour(11, time.UTC),
|
||||
expectedResult: false,
|
||||
isErr: true,
|
||||
},
|
||||
{
|
||||
name: "Deny-inactive-InvalidDuration",
|
||||
syncWindow: syncWindow("deny", "* 10 * * *", "2a"),
|
||||
currentTime: timeWithHour(11, time.UTC),
|
||||
expectedResult: false,
|
||||
isErr: true,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
result := tt.syncWindow.active(tt.currentTime)
|
||||
result, err := tt.syncWindow.active(tt.currentTime)
|
||||
if tt.isErr {
|
||||
require.Error(t, err)
|
||||
} else {
|
||||
require.NoError(t, err)
|
||||
}
|
||||
assert.Equal(t, tt.expectedResult, result)
|
||||
})
|
||||
}
|
||||
@@ -2607,6 +2735,16 @@ func (b *projectBuilder) withInactiveDenyWindow(allowManual bool) *projectBuilde
|
||||
return b
|
||||
}
|
||||
|
||||
func (b *projectBuilder) withInvalidWindows() *projectBuilder {
|
||||
b.proj.Spec.SyncWindows = append(b.proj.Spec.SyncWindows,
|
||||
newSyncWindow("allow", "* 10 * * 7", false),
|
||||
newSyncWindow("deny", "* 10 * * 7", false),
|
||||
newSyncWindow("allow", "* 10 * * 7", true),
|
||||
newSyncWindow("deny", "* 10 * * 7", true),
|
||||
)
|
||||
return b
|
||||
}
|
||||
|
||||
func inactiveCronSchedule() string {
|
||||
hourPlus10, _, _ := time.Now().Add(10 * time.Hour).Clock()
|
||||
return fmt.Sprintf("0 %d * * *", hourPlus10)
|
||||
@@ -3089,7 +3227,7 @@ func TestOrphanedResourcesMonitorSettings_IsWarn(t *testing.T) {
|
||||
assert.True(t, settings.IsWarn())
|
||||
}
|
||||
|
||||
func Test_isValidPolicy(t *testing.T) {
|
||||
func Test_isValidPolicyObject(t *testing.T) {
|
||||
policyTests := []struct {
|
||||
name string
|
||||
policy string
|
||||
@@ -3239,18 +3377,25 @@ func TestGetCAPath(t *testing.T) {
|
||||
"https://foo.example.com",
|
||||
"oci://foo.example.com",
|
||||
"foo.example.com",
|
||||
"foo.example.com/charts",
|
||||
"https://foo.example.com:5000",
|
||||
"foo.example.com:5000",
|
||||
"foo.example.com:5000/charts",
|
||||
"ssh://foo.example.com",
|
||||
}
|
||||
invalidpath := []string{
|
||||
"https://bar.example.com",
|
||||
"oci://bar.example.com",
|
||||
"bar.example.com",
|
||||
"ssh://foo.example.com",
|
||||
"git@example.com:organization/reponame.git",
|
||||
"ssh://bar.example.com",
|
||||
"git@foo.example.com:organization/reponame.git",
|
||||
"ssh://git@foo.example.com:organization/reponame.git",
|
||||
"/some/invalid/thing",
|
||||
"../another/invalid/thing",
|
||||
"./also/invalid",
|
||||
"$invalid/as/well",
|
||||
"..",
|
||||
"://invalid",
|
||||
}
|
||||
|
||||
for _, str := range validcert {
|
||||
@@ -3734,35 +3879,3 @@ func TestApplicationSpec_GetSourcePtrByIndex(t *testing.T) {
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestHelmValuesObjectHasReplaceStrategy(t *testing.T) {
|
||||
app := Application{
|
||||
Status: ApplicationStatus{Sync: SyncStatus{ComparedTo: ComparedTo{
|
||||
Source: ApplicationSource{
|
||||
Helm: &ApplicationSourceHelm{
|
||||
ValuesObject: &runtime.RawExtension{
|
||||
Object: &unstructured.Unstructured{Object: map[string]interface{}{"key": []string{"value"}}},
|
||||
},
|
||||
},
|
||||
},
|
||||
}}},
|
||||
}
|
||||
|
||||
appModified := Application{
|
||||
Status: ApplicationStatus{Sync: SyncStatus{ComparedTo: ComparedTo{
|
||||
Source: ApplicationSource{
|
||||
Helm: &ApplicationSourceHelm{
|
||||
ValuesObject: &runtime.RawExtension{
|
||||
Object: &unstructured.Unstructured{Object: map[string]interface{}{"key": []string{"value-modified1"}}},
|
||||
},
|
||||
},
|
||||
},
|
||||
}}},
|
||||
}
|
||||
|
||||
patch, _, err := diff.CreateTwoWayMergePatch(
|
||||
app,
|
||||
appModified, Application{})
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, `{"status":{"sync":{"comparedTo":{"destination":{},"source":{"helm":{"valuesObject":{"key":["value-modified1"]}},"repoURL":""}}}}}`, string(patch))
|
||||
}
|
||||
|
||||
@@ -57,7 +57,9 @@ type ManifestRequest struct {
|
||||
// This is used to surface "source not permitted" errors for Helm repositories
|
||||
ProjectSourceRepos []string `protobuf:"bytes,24,rep,name=projectSourceRepos,proto3" json:"projectSourceRepos,omitempty"`
|
||||
// This is used to surface "source not permitted" errors for Helm repositories
|
||||
ProjectName string `protobuf:"bytes,25,opt,name=projectName,proto3" json:"projectName,omitempty"`
|
||||
ProjectName string `protobuf:"bytes,25,opt,name=projectName,proto3" json:"projectName,omitempty"`
|
||||
// Holds instance installation id
|
||||
InstallationID string `protobuf:"bytes,27,opt,name=installationID,proto3" json:"installationID,omitempty"`
|
||||
XXX_NoUnkeyedLiteral struct{} `json:"-"`
|
||||
XXX_unrecognized []byte `json:"-"`
|
||||
XXX_sizecache int32 `json:"-"`
|
||||
@@ -250,6 +252,13 @@ func (m *ManifestRequest) GetProjectName() string {
|
||||
return ""
|
||||
}
|
||||
|
||||
func (m *ManifestRequest) GetInstallationID() string {
|
||||
if m != nil {
|
||||
return m.InstallationID
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
type ManifestRequestWithFiles struct {
|
||||
// Types that are valid to be assigned to Part:
|
||||
// *ManifestRequestWithFiles_Request
|
||||
@@ -2196,6 +2205,7 @@ type UpdateRevisionForPathsRequest struct {
|
||||
SyncedRevision string `protobuf:"bytes,11,opt,name=syncedRevision,proto3" json:"syncedRevision,omitempty"`
|
||||
Revision string `protobuf:"bytes,12,opt,name=revision,proto3" json:"revision,omitempty"`
|
||||
Paths []string `protobuf:"bytes,13,rep,name=paths,proto3" json:"paths,omitempty"`
|
||||
InstallationID string `protobuf:"bytes,15,opt,name=installationID,proto3" json:"installationID,omitempty"`
|
||||
XXX_NoUnkeyedLiteral struct{} `json:"-"`
|
||||
XXX_unrecognized []byte `json:"-"`
|
||||
XXX_sizecache int32 `json:"-"`
|
||||
@@ -2325,6 +2335,13 @@ func (m *UpdateRevisionForPathsRequest) GetPaths() []string {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (m *UpdateRevisionForPathsRequest) GetInstallationID() string {
|
||||
if m != nil {
|
||||
return m.InstallationID
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
type UpdateRevisionForPathsResponse struct {
|
||||
XXX_NoUnkeyedLiteral struct{} `json:"-"`
|
||||
XXX_unrecognized []byte `json:"-"`
|
||||
@@ -2414,151 +2431,152 @@ func init() {
|
||||
}
|
||||
|
||||
var fileDescriptor_dd8723cfcc820480 = []byte{
|
||||
// 2298 bytes of a gzipped FileDescriptorProto
|
||||
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xdc, 0x5a, 0xcd, 0x73, 0x1c, 0x47,
|
||||
0x15, 0xd7, 0x7e, 0x6a, 0xf7, 0x49, 0xd6, 0x47, 0xdb, 0x96, 0xc7, 0x1b, 0x5b, 0xa5, 0x0c, 0xd8,
|
||||
0xe5, 0xd8, 0xc9, 0x6e, 0x59, 0xae, 0xc4, 0xe0, 0x84, 0x50, 0x8a, 0x62, 0x4b, 0x8e, 0x2d, 0x5b,
|
||||
0x8c, 0x1d, 0x28, 0x83, 0x81, 0xea, 0x9d, 0xed, 0xdd, 0x9d, 0xec, 0x7c, 0xb4, 0x67, 0x7a, 0x14,
|
||||
0xd6, 0x55, 0x9c, 0xa0, 0xb8, 0x70, 0xe7, 0xc0, 0x95, 0x7f, 0x80, 0x0b, 0xc5, 0x91, 0x03, 0xc5,
|
||||
0xc7, 0x91, 0xe2, 0xc2, 0x11, 0xca, 0x47, 0xfe, 0x0a, 0xaa, 0x3f, 0xe6, 0x73, 0x67, 0xd7, 0x0a,
|
||||
0x6b, 0x2b, 0x90, 0x8b, 0x34, 0xfd, 0xba, 0xfb, 0xbd, 0xd7, 0xaf, 0xdf, 0x7b, 0xfd, 0x7b, 0xdd,
|
||||
0x0b, 0x97, 0x7d, 0x42, 0xbd, 0x80, 0xf8, 0x47, 0xc4, 0xef, 0x88, 0x4f, 0x8b, 0x79, 0xfe, 0x38,
|
||||
0xf5, 0xd9, 0xa6, 0xbe, 0xc7, 0x3c, 0x04, 0x09, 0xa5, 0x75, 0x7f, 0x60, 0xb1, 0x61, 0xd8, 0x6d,
|
||||
0x9b, 0x9e, 0xd3, 0xc1, 0xfe, 0xc0, 0xa3, 0xbe, 0xf7, 0x99, 0xf8, 0x78, 0xc7, 0xec, 0x75, 0x8e,
|
||||
0xb6, 0x3b, 0x74, 0x34, 0xe8, 0x60, 0x6a, 0x05, 0x1d, 0x4c, 0xa9, 0x6d, 0x99, 0x98, 0x59, 0x9e,
|
||||
0xdb, 0x39, 0xba, 0x8e, 0x6d, 0x3a, 0xc4, 0xd7, 0x3b, 0x03, 0xe2, 0x12, 0x1f, 0x33, 0xd2, 0x93,
|
||||
0x9c, 0x5b, 0x6f, 0x0c, 0x3c, 0x6f, 0x60, 0x93, 0x8e, 0x68, 0x75, 0xc3, 0x7e, 0x87, 0x38, 0x94,
|
||||
0x29, 0xb1, 0xfa, 0xbf, 0x97, 0x61, 0xf5, 0x00, 0xbb, 0x56, 0x9f, 0x04, 0xcc, 0x20, 0xcf, 0x42,
|
||||
0x12, 0x30, 0xf4, 0x14, 0xaa, 0x5c, 0x19, 0xad, 0xb4, 0x55, 0xba, 0xb2, 0xb4, 0xbd, 0xdf, 0x4e,
|
||||
0xb4, 0x69, 0x47, 0xda, 0x88, 0x8f, 0x1f, 0x9b, 0xbd, 0xf6, 0xd1, 0x76, 0x9b, 0x8e, 0x06, 0x6d,
|
||||
0xae, 0x4d, 0x3b, 0xa5, 0x4d, 0x3b, 0xd2, 0xa6, 0x6d, 0xc4, 0xcb, 0x32, 0x04, 0x57, 0xd4, 0x82,
|
||||
0x86, 0x4f, 0x8e, 0xac, 0xc0, 0xf2, 0x5c, 0xad, 0xbc, 0x55, 0xba, 0xd2, 0x34, 0xe2, 0x36, 0xd2,
|
||||
0x60, 0xd1, 0xf5, 0x76, 0xb1, 0x39, 0x24, 0x5a, 0x65, 0xab, 0x74, 0xa5, 0x61, 0x44, 0x4d, 0xb4,
|
||||
0x05, 0x4b, 0x98, 0xd2, 0xfb, 0xb8, 0x4b, 0xec, 0x7b, 0x64, 0xac, 0x55, 0xc5, 0xc4, 0x34, 0x89,
|
||||
0xcf, 0xc5, 0x94, 0x3e, 0xc0, 0x0e, 0xd1, 0x6a, 0xa2, 0x37, 0x6a, 0xa2, 0x0b, 0xd0, 0x74, 0xb1,
|
||||
0x43, 0x02, 0x8a, 0x4d, 0xa2, 0x35, 0x44, 0x5f, 0x42, 0x40, 0x3f, 0x85, 0xf5, 0x94, 0xe2, 0x8f,
|
||||
0xbc, 0xd0, 0x37, 0x89, 0x06, 0x62, 0xe9, 0x0f, 0xe7, 0x5b, 0xfa, 0x4e, 0x9e, 0xad, 0x31, 0x29,
|
||||
0x09, 0xfd, 0x08, 0x6a, 0x62, 0xe7, 0xb5, 0xa5, 0xad, 0xca, 0x2b, 0xb5, 0xb6, 0x64, 0x8b, 0x5c,
|
||||
0x58, 0xa4, 0x76, 0x38, 0xb0, 0xdc, 0x40, 0x5b, 0x16, 0x12, 0x1e, 0xcf, 0x27, 0x61, 0xd7, 0x73,
|
||||
0xfb, 0xd6, 0xe0, 0x00, 0xbb, 0x78, 0x40, 0x1c, 0xe2, 0xb2, 0x43, 0xc1, 0xdc, 0x88, 0x84, 0xa0,
|
||||
0xe7, 0xb0, 0x36, 0x0a, 0x03, 0xe6, 0x39, 0xd6, 0x73, 0xf2, 0x90, 0xf2, 0xb9, 0x81, 0x76, 0x4a,
|
||||
0x58, 0xf3, 0xc1, 0x7c, 0x82, 0xef, 0xe5, 0xb8, 0x1a, 0x13, 0x72, 0xb8, 0x93, 0x8c, 0xc2, 0x2e,
|
||||
0xf9, 0x2e, 0xf1, 0x85, 0x77, 0xad, 0x48, 0x27, 0x49, 0x91, 0xa4, 0x1b, 0x59, 0xaa, 0x15, 0x68,
|
||||
0xab, 0x5b, 0x15, 0xe9, 0x46, 0x31, 0x09, 0x5d, 0x81, 0xd5, 0x23, 0xe2, 0x5b, 0xfd, 0xf1, 0x23,
|
||||
0x6b, 0xe0, 0x62, 0x16, 0xfa, 0x44, 0x5b, 0x13, 0xae, 0x98, 0x27, 0x23, 0x07, 0x4e, 0x0d, 0x89,
|
||||
0xed, 0x70, 0x93, 0xef, 0xfa, 0xa4, 0x17, 0x68, 0xeb, 0xc2, 0xbe, 0x7b, 0xf3, 0xef, 0xa0, 0x60,
|
||||
0x67, 0x64, 0xb9, 0x73, 0xc5, 0x5c, 0xcf, 0x50, 0x91, 0x22, 0x63, 0x04, 0x49, 0xc5, 0x72, 0x64,
|
||||
0x74, 0x19, 0x56, 0x98, 0x8f, 0xcd, 0x91, 0xe5, 0x0e, 0x0e, 0x08, 0x1b, 0x7a, 0x3d, 0xed, 0xb4,
|
||||
0xb0, 0x44, 0x8e, 0x8a, 0x4c, 0x40, 0xc4, 0xc5, 0x5d, 0x9b, 0xf4, 0xa4, 0x2f, 0x3e, 0x1e, 0x53,
|
||||
0x12, 0x68, 0x67, 0xc4, 0x2a, 0x6e, 0xb4, 0x53, 0x19, 0x2a, 0x97, 0x20, 0xda, 0xb7, 0x27, 0x66,
|
||||
0xdd, 0x76, 0x99, 0x3f, 0x36, 0x0a, 0xd8, 0xa1, 0x11, 0x2c, 0xf1, 0x75, 0x44, 0xae, 0x70, 0x56,
|
||||
0xb8, 0xc2, 0xdd, 0xf9, 0x6c, 0xb4, 0x9f, 0x30, 0x34, 0xd2, 0xdc, 0x51, 0x1b, 0xd0, 0x10, 0x07,
|
||||
0x07, 0xa1, 0xcd, 0x2c, 0x6a, 0x13, 0xa9, 0x46, 0xa0, 0x6d, 0x08, 0x33, 0x15, 0xf4, 0xa0, 0x7b,
|
||||
0x00, 0x3e, 0xe9, 0x47, 0xe3, 0xce, 0x89, 0x95, 0x5f, 0x9b, 0xb5, 0x72, 0x23, 0x1e, 0x2d, 0x57,
|
||||
0x9c, 0x9a, 0xce, 0x85, 0xf3, 0x65, 0x10, 0x93, 0xa9, 0x68, 0x17, 0x61, 0xad, 0x09, 0x17, 0x2b,
|
||||
0xe8, 0xe1, 0xbe, 0xa8, 0xa8, 0x22, 0x69, 0x9d, 0x97, 0xde, 0x9a, 0x22, 0xb5, 0x6e, 0xc3, 0xb9,
|
||||
0x29, 0xa6, 0x46, 0x6b, 0x50, 0x19, 0x91, 0xb1, 0x48, 0xd1, 0x4d, 0x83, 0x7f, 0xa2, 0x33, 0x50,
|
||||
0x3b, 0xc2, 0x76, 0x48, 0x44, 0x52, 0x6d, 0x18, 0xb2, 0x71, 0xab, 0xfc, 0x8d, 0x52, 0xeb, 0x17,
|
||||
0x25, 0x58, 0xcd, 0x29, 0x5e, 0x30, 0xff, 0x87, 0xe9, 0xf9, 0xaf, 0xc0, 0x8d, 0xfb, 0x8f, 0xb1,
|
||||
0x3f, 0x20, 0x2c, 0xa5, 0x88, 0xfe, 0xf7, 0x12, 0x68, 0x39, 0x8b, 0x7e, 0xcf, 0x62, 0xc3, 0x3b,
|
||||
0x96, 0x4d, 0x02, 0x74, 0x13, 0x16, 0x7d, 0x49, 0x53, 0x07, 0xcf, 0x1b, 0x33, 0x36, 0x62, 0x7f,
|
||||
0xc1, 0x88, 0x46, 0xa3, 0x0f, 0xa1, 0xe1, 0x10, 0x86, 0x7b, 0x98, 0x61, 0xa5, 0xfb, 0x56, 0xd1,
|
||||
0x4c, 0x2e, 0xe5, 0x40, 0x8d, 0xdb, 0x5f, 0x30, 0xe2, 0x39, 0xe8, 0x5d, 0xa8, 0x99, 0xc3, 0xd0,
|
||||
0x1d, 0x89, 0x23, 0x67, 0x69, 0xfb, 0xe2, 0xb4, 0xc9, 0xbb, 0x7c, 0xd0, 0xfe, 0x82, 0x21, 0x47,
|
||||
0x7f, 0x54, 0x87, 0x2a, 0xc5, 0x3e, 0xd3, 0xef, 0xc0, 0x99, 0x22, 0x11, 0xfc, 0x9c, 0x33, 0x87,
|
||||
0xc4, 0x1c, 0x05, 0xa1, 0xa3, 0xcc, 0x1c, 0xb7, 0x11, 0x82, 0x6a, 0x60, 0x3d, 0x97, 0xa6, 0xae,
|
||||
0x18, 0xe2, 0x5b, 0x7f, 0x0b, 0xd6, 0x27, 0xa4, 0xf1, 0x4d, 0x95, 0xba, 0x71, 0x0e, 0xcb, 0x4a,
|
||||
0xb4, 0x1e, 0xc2, 0xd9, 0xc7, 0xc2, 0x16, 0x71, 0xb2, 0x3f, 0x89, 0x93, 0x5b, 0xdf, 0x87, 0x8d,
|
||||
0xbc, 0xd8, 0x80, 0x7a, 0x6e, 0x40, 0xb8, 0xeb, 0x8b, 0xec, 0x68, 0x91, 0x5e, 0xd2, 0x2b, 0xb4,
|
||||
0x68, 0x18, 0x05, 0x3d, 0xfa, 0x6f, 0xca, 0xb0, 0x61, 0x90, 0xc0, 0xb3, 0x8f, 0x48, 0x94, 0xba,
|
||||
0x4e, 0x06, 0x7c, 0xfc, 0x00, 0x2a, 0x98, 0x52, 0xe5, 0x26, 0x77, 0x5f, 0xd9, 0xf1, 0x6e, 0x70,
|
||||
0xae, 0xe8, 0x6d, 0x58, 0xc7, 0x4e, 0xd7, 0x1a, 0x84, 0x5e, 0x18, 0x44, 0xcb, 0x12, 0x4e, 0xd5,
|
||||
0x34, 0x26, 0x3b, 0x78, 0xf8, 0x07, 0x22, 0x22, 0xef, 0xba, 0x3d, 0xf2, 0x13, 0x81, 0x68, 0x2a,
|
||||
0x46, 0x9a, 0xa4, 0x9b, 0x70, 0x6e, 0xc2, 0x48, 0xca, 0xe0, 0x69, 0x10, 0x55, 0xca, 0x81, 0xa8,
|
||||
0x42, 0x35, 0xca, 0x53, 0xd4, 0xd0, 0xff, 0x5c, 0x82, 0xb5, 0x24, 0xb8, 0x14, 0xfb, 0x0b, 0xd0,
|
||||
0x74, 0x14, 0x2d, 0xd0, 0x4a, 0x22, 0x83, 0x25, 0x84, 0x2c, 0x9e, 0x2a, 0xe7, 0xf1, 0xd4, 0x06,
|
||||
0xd4, 0x25, 0xdc, 0x55, 0x4b, 0x57, 0xad, 0x8c, 0xca, 0xd5, 0x9c, 0xca, 0x9b, 0x00, 0x41, 0x9c,
|
||||
0xe1, 0xb4, 0xba, 0xe8, 0x4d, 0x51, 0x90, 0x0e, 0xcb, 0xf2, 0xf4, 0x35, 0x48, 0x10, 0xda, 0x4c,
|
||||
0x5b, 0x14, 0x23, 0x32, 0x34, 0xdd, 0x83, 0xd5, 0xfb, 0x16, 0x5f, 0x43, 0x3f, 0x38, 0x99, 0x70,
|
||||
0x78, 0x0f, 0xaa, 0x5c, 0x18, 0x5f, 0x58, 0xd7, 0xc7, 0xae, 0x39, 0x24, 0x91, 0xad, 0xe2, 0x36,
|
||||
0x0f, 0x74, 0x86, 0x07, 0x81, 0x56, 0x16, 0x74, 0xf1, 0xad, 0xff, 0xbe, 0x2c, 0x35, 0xdd, 0xa1,
|
||||
0x34, 0xf8, 0xf2, 0x21, 0x77, 0x31, 0x08, 0xa8, 0x4c, 0x82, 0x80, 0x9c, 0xca, 0x5f, 0x04, 0x04,
|
||||
0xbc, 0xa2, 0x83, 0x4c, 0x0f, 0x61, 0x71, 0x87, 0x52, 0xae, 0x08, 0xba, 0x0e, 0x55, 0x4c, 0xa9,
|
||||
0x34, 0x78, 0x2e, 0x67, 0xab, 0x21, 0xfc, 0xbf, 0x52, 0x49, 0x0c, 0x6d, 0xdd, 0x84, 0x66, 0x4c,
|
||||
0x7a, 0x99, 0xd8, 0x66, 0x5a, 0xec, 0x16, 0x80, 0x44, 0xb9, 0x77, 0xdd, 0xbe, 0xc7, 0xb7, 0x94,
|
||||
0x3b, 0xbb, 0x9a, 0x2a, 0xbe, 0xf5, 0x5b, 0xd1, 0x08, 0xa1, 0xdb, 0xdb, 0x50, 0xb3, 0x18, 0x71,
|
||||
0x22, 0xe5, 0x36, 0xd2, 0xca, 0x25, 0x8c, 0x0c, 0x39, 0x48, 0xff, 0x4b, 0x03, 0xce, 0xf3, 0x1d,
|
||||
0x7b, 0x24, 0xc2, 0x64, 0x87, 0xd2, 0x8f, 0x09, 0xc3, 0x96, 0x1d, 0x7c, 0x27, 0x24, 0xfe, 0xf8,
|
||||
0x35, 0x3b, 0xc6, 0x00, 0xea, 0x32, 0xca, 0x54, 0x46, 0x7c, 0xe5, 0x05, 0x8f, 0x62, 0x9f, 0x54,
|
||||
0x39, 0x95, 0xd7, 0x53, 0xe5, 0x14, 0x55, 0x1d, 0xd5, 0x13, 0xaa, 0x3a, 0xa6, 0x17, 0x9e, 0xa9,
|
||||
0x72, 0xb6, 0x9e, 0x2d, 0x67, 0x0b, 0xc0, 0xfc, 0xe2, 0x71, 0xc1, 0x7c, 0xa3, 0x10, 0xcc, 0x3b,
|
||||
0x85, 0x71, 0xdc, 0x14, 0xe6, 0xfe, 0x56, 0xda, 0x03, 0xa7, 0xfa, 0xda, 0x3c, 0xb0, 0x1e, 0x5e,
|
||||
0x2b, 0xac, 0xff, 0x34, 0x03, 0xd3, 0x65, 0xa1, 0xfc, 0xee, 0xf1, 0xd6, 0x34, 0x03, 0xb0, 0x7f,
|
||||
0xe5, 0xe0, 0xf5, 0xcf, 0x05, 0xaa, 0xa2, 0x5e, 0x62, 0x83, 0xf8, 0x40, 0xe7, 0xe7, 0x10, 0x3f,
|
||||
0x5a, 0x55, 0xd2, 0xe2, 0xdf, 0xe8, 0x1a, 0x54, 0xb9, 0x91, 0x15, 0xec, 0x3d, 0x97, 0xb6, 0x27,
|
||||
0xdf, 0x89, 0x1d, 0x4a, 0x1f, 0x51, 0x62, 0x1a, 0x62, 0x10, 0xba, 0x05, 0xcd, 0xd8, 0xf1, 0x55,
|
||||
0x64, 0x5d, 0x48, 0xcf, 0x88, 0xe3, 0x24, 0x9a, 0x96, 0x0c, 0xe7, 0x73, 0x7b, 0x96, 0x4f, 0x4c,
|
||||
0x01, 0x0a, 0x6b, 0x93, 0x73, 0x3f, 0x8e, 0x3a, 0xe3, 0xb9, 0xf1, 0x70, 0x74, 0x1d, 0xea, 0xf2,
|
||||
0x66, 0x41, 0x44, 0xd0, 0xd2, 0xf6, 0xf9, 0xc9, 0x64, 0x1a, 0xcd, 0x52, 0x03, 0xf5, 0x3f, 0x95,
|
||||
0xe0, 0xcd, 0xc4, 0x21, 0xa2, 0x68, 0x8a, 0x70, 0xf9, 0x97, 0x7f, 0xe2, 0x5e, 0x86, 0x15, 0x51,
|
||||
0x08, 0x24, 0x17, 0x0c, 0xf2, 0xae, 0x2b, 0x47, 0xd5, 0x7f, 0x57, 0x82, 0x4b, 0x93, 0xeb, 0xd8,
|
||||
0x1d, 0x62, 0x9f, 0xc5, 0xdb, 0x7b, 0x12, 0x6b, 0x89, 0x0e, 0xbc, 0x72, 0x72, 0xe0, 0x65, 0xd6,
|
||||
0x57, 0xc9, 0xae, 0x4f, 0xff, 0x43, 0x19, 0x96, 0x52, 0x0e, 0x54, 0x74, 0x60, 0x72, 0xc0, 0x27,
|
||||
0xfc, 0x56, 0x94, 0x7e, 0xe2, 0x50, 0x68, 0x1a, 0x29, 0x0a, 0x1a, 0x01, 0x50, 0xec, 0x63, 0x87,
|
||||
0x30, 0xe2, 0xf3, 0x4c, 0xce, 0x23, 0xfe, 0xde, 0xfc, 0xd9, 0xe5, 0x30, 0xe2, 0x69, 0xa4, 0xd8,
|
||||
0x73, 0xc4, 0x2a, 0x44, 0x07, 0x2a, 0x7f, 0xab, 0x16, 0xfa, 0x1c, 0x56, 0xfa, 0x96, 0x4d, 0x0e,
|
||||
0x13, 0x45, 0xea, 0x42, 0x91, 0x87, 0xf3, 0x2b, 0x72, 0x27, 0xcd, 0xd7, 0xc8, 0x89, 0xd1, 0xaf,
|
||||
0xc2, 0x5a, 0x3e, 0x9e, 0xb8, 0x92, 0x96, 0x83, 0x07, 0xb1, 0xb5, 0x54, 0x4b, 0x47, 0xb0, 0x96,
|
||||
0x8f, 0x1f, 0xfd, 0x9f, 0x65, 0x38, 0x1b, 0xb3, 0xdb, 0x71, 0x5d, 0x2f, 0x74, 0x4d, 0x71, 0x59,
|
||||
0x57, 0xb8, 0x17, 0x67, 0xa0, 0xc6, 0x2c, 0x66, 0xc7, 0xc0, 0x47, 0x34, 0xf8, 0xd9, 0xc5, 0x3c,
|
||||
0xcf, 0x66, 0x16, 0x55, 0x1b, 0x1c, 0x35, 0xe5, 0xde, 0x3f, 0x0b, 0x2d, 0x9f, 0xf4, 0x44, 0x26,
|
||||
0x68, 0x18, 0x71, 0x9b, 0xf7, 0x71, 0x54, 0x23, 0x60, 0xbc, 0x34, 0x66, 0xdc, 0x16, 0x7e, 0xef,
|
||||
0xd9, 0x36, 0x31, 0xb9, 0x39, 0x52, 0x40, 0x3f, 0x47, 0x15, 0x05, 0x04, 0xf3, 0x2d, 0x77, 0xa0,
|
||||
0x60, 0xbe, 0x6a, 0x71, 0x3d, 0xb1, 0xef, 0xe3, 0xb1, 0xd6, 0x10, 0x06, 0x90, 0x0d, 0xf4, 0x01,
|
||||
0x54, 0x1c, 0x4c, 0xd5, 0x41, 0x77, 0x35, 0x93, 0x1d, 0x8a, 0x2c, 0xd0, 0x3e, 0xc0, 0x54, 0x9e,
|
||||
0x04, 0x7c, 0x5a, 0xeb, 0x3d, 0x68, 0x44, 0x84, 0x2f, 0x04, 0x09, 0x3f, 0x83, 0x53, 0x99, 0xe4,
|
||||
0x83, 0x9e, 0xc0, 0x46, 0xe2, 0x51, 0x69, 0x81, 0x0a, 0x04, 0xbe, 0xf9, 0x52, 0xcd, 0x8c, 0x29,
|
||||
0x0c, 0xf4, 0x67, 0xb0, 0xce, 0x5d, 0x46, 0x04, 0xfe, 0x09, 0x95, 0x36, 0xef, 0x43, 0x33, 0x16,
|
||||
0x59, 0xe8, 0x33, 0x2d, 0x68, 0x1c, 0x45, 0x97, 0xa8, 0xb2, 0xb6, 0x89, 0xdb, 0xfa, 0x0e, 0xa0,
|
||||
0xb4, 0xbe, 0xea, 0x04, 0xba, 0x96, 0x05, 0xc5, 0x67, 0xf3, 0xc7, 0x8d, 0x18, 0x1e, 0x61, 0xe2,
|
||||
0x7f, 0x94, 0x61, 0x75, 0xcf, 0x12, 0xf7, 0x20, 0x27, 0x94, 0xe4, 0xae, 0xc2, 0x5a, 0x10, 0x76,
|
||||
0x1d, 0xaf, 0x17, 0xda, 0x44, 0x81, 0x02, 0x75, 0xd2, 0x4f, 0xd0, 0x67, 0x25, 0x3f, 0x6e, 0x2c,
|
||||
0x8a, 0xd9, 0x50, 0x55, 0xb8, 0xe2, 0x1b, 0x7d, 0x00, 0xe7, 0x1f, 0x90, 0xcf, 0xd5, 0x7a, 0xf6,
|
||||
0x6c, 0xaf, 0xdb, 0xb5, 0xdc, 0x41, 0x24, 0xa4, 0x26, 0x84, 0x4c, 0x1f, 0x50, 0x04, 0x15, 0xeb,
|
||||
0xc5, 0x50, 0x31, 0xae, 0x92, 0x77, 0x3d, 0xc7, 0xb1, 0x98, 0x42, 0x94, 0x19, 0x9a, 0xfe, 0xb3,
|
||||
0x12, 0xac, 0x25, 0x96, 0x55, 0x7b, 0x73, 0x53, 0xc6, 0x90, 0xdc, 0x99, 0x4b, 0xe9, 0x9d, 0xc9,
|
||||
0x0f, 0xfd, 0xef, 0xc3, 0x67, 0x39, 0x1d, 0x3e, 0xbf, 0x2c, 0xc3, 0xd9, 0x3d, 0x8b, 0x45, 0x89,
|
||||
0xcb, 0xfa, 0x7f, 0xdb, 0xe5, 0x82, 0x3d, 0xa9, 0x1e, 0x6f, 0x4f, 0x6a, 0x05, 0x7b, 0xd2, 0x86,
|
||||
0x8d, 0xbc, 0x31, 0xd4, 0xc6, 0x9c, 0x81, 0x1a, 0xf7, 0xa0, 0xe8, 0x5e, 0x41, 0x36, 0xf4, 0xdf,
|
||||
0xd6, 0xe1, 0xe2, 0xa7, 0xb4, 0x87, 0x59, 0x7c, 0x2f, 0x74, 0xc7, 0xf3, 0x0f, 0x79, 0xd7, 0xc9,
|
||||
0x58, 0x31, 0xf7, 0x16, 0x57, 0x9e, 0xf9, 0x16, 0x57, 0x99, 0xf1, 0x16, 0x57, 0x3d, 0xd6, 0x5b,
|
||||
0x5c, 0xed, 0xc4, 0xde, 0xe2, 0x26, 0x6b, 0xad, 0x7a, 0x61, 0xad, 0xf5, 0x24, 0x53, 0x8f, 0x2c,
|
||||
0x8a, 0xb0, 0xf9, 0x66, 0x3a, 0x6c, 0x66, 0xee, 0xce, 0xcc, 0x47, 0x84, 0xdc, 0x13, 0x56, 0xe3,
|
||||
0xa5, 0x4f, 0x58, 0xcd, 0xc9, 0x27, 0xac, 0xe2, 0x57, 0x10, 0x98, 0xfa, 0x0a, 0x72, 0x19, 0x56,
|
||||
0x82, 0xb1, 0x6b, 0x92, 0x5e, 0x7c, 0x5b, 0xb8, 0x24, 0x97, 0x9d, 0xa5, 0x66, 0x22, 0x62, 0x39,
|
||||
0x17, 0x11, 0xb1, 0xa7, 0x9e, 0x4a, 0x79, 0xea, 0xff, 0x4e, 0x69, 0xb4, 0x05, 0x9b, 0xd3, 0xf6,
|
||||
0x44, 0x86, 0xda, 0xf6, 0x1f, 0x01, 0xd6, 0x13, 0xb4, 0xcd, 0xff, 0x5a, 0x26, 0x41, 0x0f, 0x61,
|
||||
0x6d, 0x4f, 0x3d, 0xa7, 0x47, 0x97, 0xa4, 0x68, 0xd6, 0xbb, 0x44, 0xeb, 0x42, 0x71, 0xa7, 0x14,
|
||||
0xa2, 0x2f, 0x20, 0x13, 0xce, 0xe7, 0x19, 0x26, 0x4f, 0x20, 0x5f, 0x9f, 0xc1, 0x39, 0x1e, 0xf5,
|
||||
0x32, 0x11, 0x57, 0x4a, 0xe8, 0x09, 0xac, 0x64, 0x2f, 0xea, 0x51, 0x06, 0x7e, 0x14, 0xbe, 0x1d,
|
||||
0xb4, 0xf4, 0x59, 0x43, 0x62, 0xfd, 0x9f, 0xf2, 0x0d, 0xcd, 0xdc, 0x49, 0x23, 0x3d, 0x5b, 0x89,
|
||||
0x17, 0xdd, 0xea, 0xb7, 0xbe, 0x36, 0x73, 0x4c, 0xcc, 0xfd, 0x7d, 0x68, 0x44, 0x77, 0xb8, 0x59,
|
||||
0x33, 0xe7, 0x6e, 0x76, 0x5b, 0x6b, 0x59, 0x7e, 0xfd, 0x40, 0x5f, 0x40, 0x1f, 0xca, 0xc9, 0x3b,
|
||||
0x94, 0x16, 0x4c, 0x4e, 0xdd, 0x5c, 0xb6, 0x4e, 0x17, 0xdc, 0x16, 0xea, 0x0b, 0xe8, 0xdb, 0xb0,
|
||||
0xc4, 0xbf, 0x0e, 0xd5, 0x43, 0xf6, 0x46, 0x5b, 0xfe, 0x6e, 0xa2, 0x1d, 0xfd, 0x6e, 0xa2, 0x7d,
|
||||
0xdb, 0xa1, 0x6c, 0xdc, 0x2a, 0xb8, 0xce, 0x53, 0x0c, 0x9e, 0xc2, 0xa9, 0x3d, 0xc2, 0x92, 0xea,
|
||||
0x1b, 0x5d, 0x3a, 0xd6, 0x1d, 0x45, 0x4b, 0xcf, 0x0f, 0x9b, 0x2c, 0xe0, 0xf5, 0x05, 0xf4, 0xab,
|
||||
0x12, 0x9c, 0xde, 0x23, 0x2c, 0x5f, 0xcf, 0xa2, 0x77, 0x8a, 0x85, 0x4c, 0xa9, 0x7b, 0x5b, 0x0f,
|
||||
0xe6, 0x8d, 0xae, 0x2c, 0x5b, 0x7d, 0x01, 0xfd, 0xba, 0x04, 0xe7, 0x52, 0x8a, 0xa5, 0x0b, 0x54,
|
||||
0x74, 0x7d, 0xb6, 0x72, 0x05, 0xc5, 0x6c, 0xeb, 0x93, 0x39, 0x7f, 0x9f, 0x90, 0x62, 0xa9, 0x2f,
|
||||
0xa0, 0x43, 0xb1, 0x27, 0x09, 0x1e, 0x45, 0x17, 0x0b, 0x81, 0x67, 0x2c, 0x7d, 0x73, 0x5a, 0x77,
|
||||
0xbc, 0x0f, 0x9f, 0xc0, 0xd2, 0x1e, 0x61, 0x11, 0x30, 0xca, 0x7a, 0x5a, 0x0e, 0xb3, 0x66, 0x43,
|
||||
0x35, 0x8f, 0xa5, 0x84, 0xc7, 0xac, 0x4b, 0x5e, 0xa9, 0xc3, 0x3f, 0x1b, 0xab, 0x85, 0x28, 0x29,
|
||||
0xeb, 0x31, 0xc5, 0xd8, 0x41, 0x5f, 0x40, 0xcf, 0x60, 0xa3, 0x38, 0xe9, 0xa1, 0xb7, 0x8e, 0x7d,
|
||||
0x58, 0xb5, 0xae, 0x1e, 0x67, 0x68, 0x24, 0xf2, 0xa3, 0x9d, 0xbf, 0xbe, 0xd8, 0x2c, 0xfd, 0xed,
|
||||
0xc5, 0x66, 0xe9, 0x5f, 0x2f, 0x36, 0x4b, 0xdf, 0xbf, 0xf1, 0x92, 0xdf, 0x31, 0xa5, 0x7e, 0x1a,
|
||||
0x85, 0xa9, 0x65, 0xda, 0x16, 0x71, 0x59, 0xb7, 0x2e, 0xe2, 0xed, 0xc6, 0x7f, 0x02, 0x00, 0x00,
|
||||
0xff, 0xff, 0xb7, 0x8d, 0xc3, 0x0e, 0x39, 0x25, 0x00, 0x00,
|
||||
// 2319 bytes of a gzipped FileDescriptorProto
|
||||
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xdc, 0x5a, 0xcb, 0x73, 0x1c, 0x47,
|
||||
0x19, 0xd7, 0x3e, 0xb5, 0xfb, 0xc9, 0x7a, 0xb5, 0x6d, 0x79, 0xbc, 0xb6, 0x55, 0xca, 0x80, 0x5d,
|
||||
0x8e, 0x9d, 0xac, 0xca, 0x72, 0x25, 0x06, 0x27, 0x84, 0x52, 0x64, 0x5b, 0x72, 0x6c, 0xd9, 0x62,
|
||||
0xec, 0x40, 0x19, 0x0c, 0x54, 0xef, 0x6c, 0x6b, 0xb7, 0xa3, 0x79, 0xb4, 0x67, 0x7a, 0x14, 0xe4,
|
||||
0x2a, 0x4e, 0x50, 0x5c, 0xb8, 0x71, 0xe0, 0xc0, 0x95, 0xbf, 0x81, 0xe2, 0xc8, 0x81, 0xe2, 0x71,
|
||||
0xa4, 0xb8, 0xc0, 0x0d, 0xca, 0x7f, 0x09, 0xd5, 0x8f, 0x79, 0xee, 0xec, 0x5a, 0x41, 0xb2, 0x02,
|
||||
0xb9, 0x48, 0xd3, 0x5f, 0x77, 0x7f, 0xdf, 0xd7, 0xdf, 0xa3, 0xfb, 0xf7, 0x75, 0x2f, 0x5c, 0x09,
|
||||
0x08, 0xf3, 0x43, 0x12, 0xec, 0x93, 0x60, 0x55, 0x7e, 0x52, 0xee, 0x07, 0x07, 0x99, 0xcf, 0x2e,
|
||||
0x0b, 0x7c, 0xee, 0x23, 0x48, 0x29, 0x9d, 0x87, 0x03, 0xca, 0x87, 0x51, 0xaf, 0x6b, 0xfb, 0xee,
|
||||
0x2a, 0x0e, 0x06, 0x3e, 0x0b, 0xfc, 0xcf, 0xe4, 0xc7, 0xbb, 0x76, 0x7f, 0x75, 0x7f, 0x6d, 0x95,
|
||||
0xed, 0x0d, 0x56, 0x31, 0xa3, 0xe1, 0x2a, 0x66, 0xcc, 0xa1, 0x36, 0xe6, 0xd4, 0xf7, 0x56, 0xf7,
|
||||
0x6f, 0x60, 0x87, 0x0d, 0xf1, 0x8d, 0xd5, 0x01, 0xf1, 0x48, 0x80, 0x39, 0xe9, 0x2b, 0xce, 0x9d,
|
||||
0x0b, 0x03, 0xdf, 0x1f, 0x38, 0x64, 0x55, 0xb6, 0x7a, 0xd1, 0xee, 0x2a, 0x71, 0x19, 0xd7, 0x62,
|
||||
0xcd, 0x5f, 0xcd, 0xc2, 0xfc, 0x36, 0xf6, 0xe8, 0x2e, 0x09, 0xb9, 0x45, 0x5e, 0x44, 0x24, 0xe4,
|
||||
0xe8, 0x39, 0xd4, 0x85, 0x32, 0x46, 0x65, 0xa5, 0x72, 0x75, 0x66, 0x6d, 0xab, 0x9b, 0x6a, 0xd3,
|
||||
0x8d, 0xb5, 0x91, 0x1f, 0x3f, 0xb6, 0xfb, 0xdd, 0xfd, 0xb5, 0x2e, 0xdb, 0x1b, 0x74, 0x85, 0x36,
|
||||
0xdd, 0x8c, 0x36, 0xdd, 0x58, 0x9b, 0xae, 0x95, 0x2c, 0xcb, 0x92, 0x5c, 0x51, 0x07, 0x5a, 0x01,
|
||||
0xd9, 0xa7, 0x21, 0xf5, 0x3d, 0xa3, 0xba, 0x52, 0xb9, 0xda, 0xb6, 0x92, 0x36, 0x32, 0x60, 0xda,
|
||||
0xf3, 0x37, 0xb0, 0x3d, 0x24, 0x46, 0x6d, 0xa5, 0x72, 0xb5, 0x65, 0xc5, 0x4d, 0xb4, 0x02, 0x33,
|
||||
0x98, 0xb1, 0x87, 0xb8, 0x47, 0x9c, 0x07, 0xe4, 0xc0, 0xa8, 0xcb, 0x89, 0x59, 0x92, 0x98, 0x8b,
|
||||
0x19, 0x7b, 0x84, 0x5d, 0x62, 0x34, 0x64, 0x6f, 0xdc, 0x44, 0x17, 0xa1, 0xed, 0x61, 0x97, 0x84,
|
||||
0x0c, 0xdb, 0xc4, 0x68, 0xc9, 0xbe, 0x94, 0x80, 0x7e, 0x0a, 0x8b, 0x19, 0xc5, 0x9f, 0xf8, 0x51,
|
||||
0x60, 0x13, 0x03, 0xe4, 0xd2, 0x1f, 0x1f, 0x6d, 0xe9, 0xeb, 0x45, 0xb6, 0xd6, 0xa8, 0x24, 0xf4,
|
||||
0x23, 0x68, 0x48, 0xcf, 0x1b, 0x33, 0x2b, 0xb5, 0x63, 0xb5, 0xb6, 0x62, 0x8b, 0x3c, 0x98, 0x66,
|
||||
0x4e, 0x34, 0xa0, 0x5e, 0x68, 0x9c, 0x92, 0x12, 0x9e, 0x1e, 0x4d, 0xc2, 0x86, 0xef, 0xed, 0xd2,
|
||||
0xc1, 0x36, 0xf6, 0xf0, 0x80, 0xb8, 0xc4, 0xe3, 0x3b, 0x92, 0xb9, 0x15, 0x0b, 0x41, 0x2f, 0x61,
|
||||
0x61, 0x2f, 0x0a, 0xb9, 0xef, 0xd2, 0x97, 0xe4, 0x31, 0x13, 0x73, 0x43, 0x63, 0x56, 0x5a, 0xf3,
|
||||
0xd1, 0xd1, 0x04, 0x3f, 0x28, 0x70, 0xb5, 0x46, 0xe4, 0x88, 0x20, 0xd9, 0x8b, 0x7a, 0xe4, 0xbb,
|
||||
0x24, 0x90, 0xd1, 0x35, 0xa7, 0x82, 0x24, 0x43, 0x52, 0x61, 0x44, 0x75, 0x2b, 0x34, 0xe6, 0x57,
|
||||
0x6a, 0x2a, 0x8c, 0x12, 0x12, 0xba, 0x0a, 0xf3, 0xfb, 0x24, 0xa0, 0xbb, 0x07, 0x4f, 0xe8, 0xc0,
|
||||
0xc3, 0x3c, 0x0a, 0x88, 0xb1, 0x20, 0x43, 0xb1, 0x48, 0x46, 0x2e, 0xcc, 0x0e, 0x89, 0xe3, 0x0a,
|
||||
0x93, 0x6f, 0x04, 0xa4, 0x1f, 0x1a, 0x8b, 0xd2, 0xbe, 0x9b, 0x47, 0xf7, 0xa0, 0x64, 0x67, 0xe5,
|
||||
0xb9, 0x0b, 0xc5, 0x3c, 0xdf, 0xd2, 0x99, 0xa2, 0x72, 0x04, 0x29, 0xc5, 0x0a, 0x64, 0x74, 0x05,
|
||||
0xe6, 0x78, 0x80, 0xed, 0x3d, 0xea, 0x0d, 0xb6, 0x09, 0x1f, 0xfa, 0x7d, 0xe3, 0xb4, 0xb4, 0x44,
|
||||
0x81, 0x8a, 0x6c, 0x40, 0xc4, 0xc3, 0x3d, 0x87, 0xf4, 0x55, 0x2c, 0x3e, 0x3d, 0x60, 0x24, 0x34,
|
||||
0xce, 0xc8, 0x55, 0xdc, 0xec, 0x66, 0x76, 0xa8, 0xc2, 0x06, 0xd1, 0xbd, 0x3b, 0x32, 0xeb, 0xae,
|
||||
0xc7, 0x83, 0x03, 0xab, 0x84, 0x1d, 0xda, 0x83, 0x19, 0xb1, 0x8e, 0x38, 0x14, 0xce, 0xca, 0x50,
|
||||
0xb8, 0x7f, 0x34, 0x1b, 0x6d, 0xa5, 0x0c, 0xad, 0x2c, 0x77, 0xd4, 0x05, 0x34, 0xc4, 0xe1, 0x76,
|
||||
0xe4, 0x70, 0xca, 0x1c, 0xa2, 0xd4, 0x08, 0x8d, 0x25, 0x69, 0xa6, 0x92, 0x1e, 0xf4, 0x00, 0x20,
|
||||
0x20, 0xbb, 0xf1, 0xb8, 0x73, 0x72, 0xe5, 0xd7, 0x27, 0xad, 0xdc, 0x4a, 0x46, 0xab, 0x15, 0x67,
|
||||
0xa6, 0x0b, 0xe1, 0x62, 0x19, 0xc4, 0xe6, 0x3a, 0xdb, 0x65, 0x5a, 0x1b, 0x32, 0xc4, 0x4a, 0x7a,
|
||||
0x44, 0x2c, 0x6a, 0xaa, 0xdc, 0xb4, 0xce, 0xab, 0x68, 0xcd, 0x90, 0x84, 0x23, 0xa9, 0x17, 0x72,
|
||||
0xec, 0x38, 0xd2, 0x00, 0xf7, 0xef, 0x18, 0x17, 0x94, 0x23, 0xf3, 0xd4, 0xce, 0x5d, 0x38, 0x37,
|
||||
0xc6, 0x25, 0x68, 0x01, 0x6a, 0x7b, 0xe4, 0x40, 0x6e, 0xe5, 0x6d, 0x4b, 0x7c, 0xa2, 0x33, 0xd0,
|
||||
0xd8, 0xc7, 0x4e, 0x44, 0xe4, 0xe6, 0xdb, 0xb2, 0x54, 0xe3, 0x76, 0xf5, 0x1b, 0x95, 0xce, 0x2f,
|
||||
0x2a, 0x30, 0x5f, 0x58, 0x60, 0xc9, 0xfc, 0x1f, 0x66, 0xe7, 0x1f, 0x43, 0xb8, 0xef, 0x3e, 0xc5,
|
||||
0xc1, 0x80, 0xf0, 0x8c, 0x22, 0xe6, 0xdf, 0x2b, 0x60, 0x14, 0x2c, 0xff, 0x3d, 0xca, 0x87, 0xf7,
|
||||
0xa8, 0x43, 0x42, 0x74, 0x0b, 0xa6, 0x03, 0x45, 0xd3, 0x07, 0xd4, 0x85, 0x09, 0x0e, 0xdb, 0x9a,
|
||||
0xb2, 0xe2, 0xd1, 0xe8, 0x23, 0x68, 0xb9, 0x84, 0xe3, 0x3e, 0xe6, 0x58, 0xeb, 0xbe, 0x52, 0x36,
|
||||
0x53, 0x48, 0xd9, 0xd6, 0xe3, 0xb6, 0xa6, 0xac, 0x64, 0x0e, 0x7a, 0x0f, 0x1a, 0xf6, 0x30, 0xf2,
|
||||
0xf6, 0xe4, 0xd1, 0x34, 0xb3, 0x76, 0x69, 0xdc, 0xe4, 0x0d, 0x31, 0x68, 0x6b, 0xca, 0x52, 0xa3,
|
||||
0x3f, 0x6e, 0x42, 0x9d, 0xe1, 0x80, 0x9b, 0xf7, 0xe0, 0x4c, 0x99, 0x08, 0x71, 0x1e, 0xda, 0x43,
|
||||
0x62, 0xef, 0x85, 0x91, 0xab, 0xcd, 0x9c, 0xb4, 0x11, 0x82, 0x7a, 0x48, 0x5f, 0x2a, 0x53, 0xd7,
|
||||
0x2c, 0xf9, 0x6d, 0xbe, 0x0d, 0x8b, 0x23, 0xd2, 0x84, 0x53, 0x95, 0x6e, 0x82, 0xc3, 0x29, 0x2d,
|
||||
0xda, 0x8c, 0xe0, 0xec, 0x53, 0x69, 0x8b, 0xe4, 0x50, 0x38, 0x89, 0x13, 0xde, 0xdc, 0x82, 0xa5,
|
||||
0xa2, 0xd8, 0x90, 0xf9, 0x5e, 0x48, 0x44, 0x8a, 0xc8, 0x5d, 0x94, 0x92, 0x7e, 0xda, 0x2b, 0xb5,
|
||||
0x68, 0x59, 0x25, 0x3d, 0xe6, 0x6f, 0xab, 0xb0, 0x64, 0x91, 0xd0, 0x77, 0xf6, 0x49, 0xbc, 0xc5,
|
||||
0x9d, 0x0c, 0x48, 0xf9, 0x01, 0xd4, 0x30, 0x63, 0x3a, 0x4c, 0xee, 0x1f, 0x1b, 0x0c, 0xb0, 0x04,
|
||||
0x57, 0xf4, 0x0e, 0x2c, 0x62, 0xb7, 0x47, 0x07, 0x91, 0x1f, 0x85, 0xf1, 0xb2, 0x64, 0x50, 0xb5,
|
||||
0xad, 0xd1, 0x0e, 0xb1, 0x4d, 0x84, 0x32, 0x23, 0xef, 0x7b, 0x7d, 0xf2, 0x13, 0x89, 0x7c, 0x6a,
|
||||
0x56, 0x96, 0x64, 0xda, 0x70, 0x6e, 0xc4, 0x48, 0xda, 0xe0, 0x59, 0xb0, 0x55, 0x29, 0x80, 0xad,
|
||||
0x52, 0x35, 0xaa, 0x63, 0xd4, 0x30, 0xff, 0x5c, 0x81, 0x85, 0x34, 0xb9, 0x34, 0xfb, 0x8b, 0xd0,
|
||||
0x76, 0x35, 0x2d, 0x34, 0x2a, 0x72, 0xa7, 0x4b, 0x09, 0x79, 0xdc, 0x55, 0x2d, 0xe2, 0xae, 0x25,
|
||||
0x68, 0x2a, 0x58, 0xac, 0x97, 0xae, 0x5b, 0x39, 0x95, 0xeb, 0x05, 0x95, 0x97, 0x01, 0xc2, 0x64,
|
||||
0x87, 0x33, 0x9a, 0xb2, 0x37, 0x43, 0x41, 0x26, 0x9c, 0x52, 0xa7, 0xb4, 0x45, 0xc2, 0xc8, 0xe1,
|
||||
0xc6, 0xb4, 0x1c, 0x91, 0xa3, 0x99, 0x3e, 0xcc, 0x3f, 0xa4, 0x62, 0x0d, 0xbb, 0xe1, 0xc9, 0xa4,
|
||||
0xc3, 0xfb, 0x50, 0x17, 0xc2, 0xc4, 0xc2, 0x7a, 0x01, 0xf6, 0xec, 0x21, 0x89, 0x6d, 0x95, 0xb4,
|
||||
0x45, 0xa2, 0x73, 0x3c, 0x08, 0x8d, 0xaa, 0xa4, 0xcb, 0x6f, 0xf3, 0xf7, 0x55, 0xa5, 0xe9, 0x3a,
|
||||
0x63, 0xe1, 0x97, 0x0f, 0xcd, 0xcb, 0xc1, 0x42, 0x6d, 0x14, 0x2c, 0x14, 0x54, 0xfe, 0x22, 0x60,
|
||||
0xe1, 0x98, 0x0e, 0x32, 0x33, 0x82, 0xe9, 0x75, 0xc6, 0x84, 0x22, 0xe8, 0x06, 0xd4, 0x31, 0x63,
|
||||
0xca, 0xe0, 0x85, 0x3d, 0x5b, 0x0f, 0x11, 0xff, 0xb5, 0x4a, 0x72, 0x68, 0xe7, 0x16, 0xb4, 0x13,
|
||||
0xd2, 0xeb, 0xc4, 0xb6, 0xb3, 0x62, 0x57, 0x00, 0x14, 0x1a, 0xbe, 0xef, 0xed, 0xfa, 0xc2, 0xa5,
|
||||
0x22, 0xd8, 0xf5, 0x54, 0xf9, 0x6d, 0xde, 0x8e, 0x47, 0x48, 0xdd, 0xde, 0x81, 0x06, 0xe5, 0xc4,
|
||||
0x8d, 0x95, 0x5b, 0xca, 0x2a, 0x97, 0x32, 0xb2, 0xd4, 0x20, 0xf3, 0x2f, 0x2d, 0x38, 0x2f, 0x3c,
|
||||
0xf6, 0x44, 0xa6, 0xc9, 0x3a, 0x63, 0x77, 0x08, 0xc7, 0xd4, 0x09, 0xbf, 0x13, 0x91, 0xe0, 0xe0,
|
||||
0x0d, 0x07, 0xc6, 0x00, 0x9a, 0x2a, 0xcb, 0xf4, 0x8e, 0x78, 0xec, 0x85, 0x91, 0x66, 0x9f, 0x56,
|
||||
0x43, 0xb5, 0x37, 0x53, 0x0d, 0x95, 0x55, 0x27, 0xf5, 0x13, 0xaa, 0x4e, 0xc6, 0x17, 0xa8, 0x99,
|
||||
0xb2, 0xb7, 0x99, 0x2f, 0x7b, 0x4b, 0x40, 0xff, 0xf4, 0x61, 0x41, 0x7f, 0xab, 0x14, 0xf4, 0xbb,
|
||||
0xa5, 0x79, 0xdc, 0x96, 0xe6, 0xfe, 0x56, 0x36, 0x02, 0xc7, 0xc6, 0xda, 0x51, 0xe0, 0x3f, 0xbc,
|
||||
0x51, 0xf8, 0xff, 0x69, 0x0e, 0xce, 0xab, 0x82, 0xfa, 0xbd, 0xc3, 0xad, 0x69, 0x02, 0xb0, 0xff,
|
||||
0xca, 0xc1, 0xeb, 0x9f, 0x4b, 0x54, 0xc5, 0xfc, 0xd4, 0x06, 0xc9, 0x81, 0x2e, 0xce, 0x21, 0x71,
|
||||
0xb4, 0xea, 0x4d, 0x4b, 0x7c, 0xa3, 0xeb, 0x50, 0x17, 0x46, 0xd6, 0xb0, 0xf7, 0x5c, 0xd6, 0x9e,
|
||||
0xc2, 0x13, 0xeb, 0x8c, 0x3d, 0x61, 0xc4, 0xb6, 0xe4, 0x20, 0x74, 0x1b, 0xda, 0x49, 0xe0, 0xeb,
|
||||
0xcc, 0xba, 0x98, 0x9d, 0x91, 0xe4, 0x49, 0x3c, 0x2d, 0x1d, 0x2e, 0xe6, 0xf6, 0x69, 0x40, 0x6c,
|
||||
0x09, 0x0a, 0x1b, 0xa3, 0x73, 0xef, 0xc4, 0x9d, 0xc9, 0xdc, 0x64, 0x38, 0xba, 0x01, 0x4d, 0x75,
|
||||
0x03, 0x21, 0x33, 0x68, 0x66, 0xed, 0xfc, 0xe8, 0x66, 0x1a, 0xcf, 0xd2, 0x03, 0xcd, 0x3f, 0x55,
|
||||
0xe0, 0xad, 0x34, 0x20, 0xe2, 0x6c, 0x8a, 0x71, 0xf9, 0x97, 0x7f, 0xe2, 0x5e, 0x81, 0x39, 0x59,
|
||||
0x08, 0xa4, 0x17, 0x11, 0xea, 0x4e, 0xac, 0x40, 0x35, 0x7f, 0x57, 0x81, 0xcb, 0xa3, 0xeb, 0xd8,
|
||||
0x18, 0xe2, 0x80, 0x27, 0xee, 0x3d, 0x89, 0xb5, 0xc4, 0x07, 0x5e, 0x35, 0x3d, 0xf0, 0x72, 0xeb,
|
||||
0xab, 0xe5, 0xd7, 0x67, 0xfe, 0xa1, 0x0a, 0x33, 0x99, 0x00, 0x2a, 0x3b, 0x30, 0x05, 0xe0, 0x93,
|
||||
0x71, 0x2b, 0x4b, 0x3f, 0x79, 0x28, 0xb4, 0xad, 0x0c, 0x05, 0xed, 0x01, 0x30, 0x1c, 0x60, 0x97,
|
||||
0x70, 0x12, 0x88, 0x9d, 0x5c, 0x64, 0xfc, 0x83, 0xa3, 0xef, 0x2e, 0x3b, 0x31, 0x4f, 0x2b, 0xc3,
|
||||
0x5e, 0x20, 0x56, 0x29, 0x3a, 0xd4, 0xfb, 0xb7, 0x6e, 0xa1, 0xcf, 0x61, 0x6e, 0x97, 0x3a, 0x64,
|
||||
0x27, 0x55, 0xa4, 0x29, 0x15, 0x79, 0x7c, 0x74, 0x45, 0xee, 0x65, 0xf9, 0x5a, 0x05, 0x31, 0xe6,
|
||||
0x35, 0x58, 0x28, 0xe6, 0x93, 0x50, 0x92, 0xba, 0x78, 0x90, 0x58, 0x4b, 0xb7, 0x4c, 0x04, 0x0b,
|
||||
0xc5, 0xfc, 0x31, 0xff, 0x55, 0x85, 0xb3, 0x09, 0xbb, 0x75, 0xcf, 0xf3, 0x23, 0xcf, 0x96, 0x97,
|
||||
0x7a, 0xa5, 0xbe, 0x38, 0x03, 0x0d, 0x4e, 0xb9, 0x93, 0x00, 0x1f, 0xd9, 0x10, 0x67, 0x17, 0xf7,
|
||||
0x7d, 0x87, 0x53, 0xa6, 0x1d, 0x1c, 0x37, 0x95, 0xef, 0x5f, 0x44, 0x34, 0x20, 0x7d, 0xb9, 0x13,
|
||||
0xb4, 0xac, 0xa4, 0x2d, 0xfa, 0x04, 0xaa, 0x91, 0x30, 0x5e, 0x19, 0x33, 0x69, 0xcb, 0xb8, 0xf7,
|
||||
0x1d, 0x87, 0xd8, 0xc2, 0x1c, 0x19, 0xa0, 0x5f, 0xa0, 0xca, 0x02, 0x82, 0x07, 0xd4, 0x1b, 0x68,
|
||||
0x98, 0xaf, 0x5b, 0x42, 0x4f, 0x1c, 0x04, 0xf8, 0xc0, 0x68, 0x49, 0x03, 0xa8, 0x06, 0xfa, 0x10,
|
||||
0x6a, 0x2e, 0x66, 0xfa, 0xa0, 0xbb, 0x96, 0xdb, 0x1d, 0xca, 0x2c, 0xd0, 0xdd, 0xc6, 0x4c, 0x9d,
|
||||
0x04, 0x62, 0x5a, 0xe7, 0x7d, 0x68, 0xc5, 0x84, 0x2f, 0x04, 0x09, 0x3f, 0x83, 0xd9, 0xdc, 0xe6,
|
||||
0x83, 0x9e, 0xc1, 0x52, 0x1a, 0x51, 0x59, 0x81, 0x1a, 0x04, 0xbe, 0xf5, 0x5a, 0xcd, 0xac, 0x31,
|
||||
0x0c, 0xcc, 0x17, 0xb0, 0x28, 0x42, 0x46, 0x26, 0xfe, 0x09, 0x95, 0x36, 0x1f, 0x40, 0x3b, 0x11,
|
||||
0x59, 0x1a, 0x33, 0x1d, 0x68, 0xed, 0xc7, 0x97, 0xad, 0xaa, 0xb6, 0x49, 0xda, 0xe6, 0x3a, 0xa0,
|
||||
0xac, 0xbe, 0xfa, 0x04, 0xba, 0x9e, 0x07, 0xc5, 0x67, 0x8b, 0xc7, 0x8d, 0x1c, 0x1e, 0x63, 0xe2,
|
||||
0x7f, 0x54, 0x61, 0x7e, 0x93, 0xca, 0x7b, 0x90, 0x13, 0xda, 0xe4, 0xae, 0xc1, 0x42, 0x18, 0xf5,
|
||||
0x5c, 0xbf, 0x1f, 0x39, 0x44, 0x83, 0x02, 0x7d, 0xd2, 0x8f, 0xd0, 0x27, 0x6d, 0x7e, 0xc2, 0x58,
|
||||
0x0c, 0xf3, 0xa1, 0xae, 0x70, 0xe5, 0x37, 0xfa, 0x10, 0xce, 0x3f, 0x22, 0x9f, 0xeb, 0xf5, 0x6c,
|
||||
0x3a, 0x7e, 0xaf, 0x47, 0xbd, 0x41, 0x2c, 0xa4, 0x21, 0x85, 0x8c, 0x1f, 0x50, 0x06, 0x15, 0x9b,
|
||||
0xe5, 0x50, 0x31, 0xa9, 0x92, 0x37, 0x7c, 0xd7, 0xa5, 0x5c, 0x23, 0xca, 0x1c, 0xcd, 0xfc, 0x59,
|
||||
0x05, 0x16, 0x52, 0xcb, 0x6a, 0xdf, 0xdc, 0x52, 0x39, 0xa4, 0x3c, 0x73, 0x39, 0xeb, 0x99, 0xe2,
|
||||
0xd0, 0xff, 0x3e, 0x7d, 0x4e, 0x65, 0xd3, 0xe7, 0x97, 0x55, 0x38, 0xbb, 0x49, 0x79, 0xbc, 0x71,
|
||||
0xd1, 0xff, 0x37, 0x2f, 0x97, 0xf8, 0xa4, 0x7e, 0x38, 0x9f, 0x34, 0x4a, 0x7c, 0xd2, 0x85, 0xa5,
|
||||
0xa2, 0x31, 0xb4, 0x63, 0xce, 0x40, 0x43, 0x44, 0x50, 0x7c, 0xaf, 0xa0, 0x1a, 0xe6, 0x3f, 0x9b,
|
||||
0x70, 0xe9, 0x53, 0xd6, 0xc7, 0x3c, 0xb9, 0x17, 0xba, 0xe7, 0x07, 0x3b, 0xa2, 0xeb, 0x64, 0xac,
|
||||
0x58, 0x78, 0xb3, 0xab, 0x4e, 0x7c, 0xb3, 0xab, 0x4d, 0x78, 0xb3, 0xab, 0x1f, 0xea, 0xcd, 0xae,
|
||||
0x71, 0x62, 0x6f, 0x76, 0xa3, 0xb5, 0x56, 0xb3, 0xb4, 0xd6, 0x7a, 0x96, 0xab, 0x47, 0xa6, 0x65,
|
||||
0xda, 0x7c, 0x33, 0x9b, 0x36, 0x13, 0xbd, 0x33, 0xf1, 0xb1, 0xa1, 0xf0, 0xd4, 0xd5, 0x7a, 0xed,
|
||||
0x53, 0x57, 0x7b, 0xf4, 0xa9, 0xab, 0xfc, 0xb5, 0x04, 0xc6, 0xbe, 0x96, 0x5c, 0x81, 0xb9, 0xf0,
|
||||
0xc0, 0xb3, 0x49, 0x3f, 0xb9, 0x2d, 0x9c, 0x51, 0xcb, 0xce, 0x53, 0x73, 0x19, 0x71, 0xaa, 0x90,
|
||||
0x11, 0x49, 0xa4, 0xce, 0x66, 0x22, 0xb5, 0xe4, 0xa1, 0x63, 0xbe, 0xf4, 0xa1, 0xe3, 0x7f, 0xa6,
|
||||
0x84, 0x5a, 0x81, 0xe5, 0x71, 0xbe, 0x53, 0x29, 0xb9, 0xf6, 0x47, 0x80, 0xc5, 0x14, 0x95, 0x8b,
|
||||
0xbf, 0xd4, 0x26, 0xe8, 0x31, 0x2c, 0x6c, 0xea, 0xe7, 0xf9, 0xf8, 0x32, 0x15, 0x4d, 0x7a, 0xbf,
|
||||
0xe8, 0x5c, 0x2c, 0xef, 0x54, 0x42, 0xcc, 0x29, 0x64, 0xc3, 0xf9, 0x22, 0xc3, 0xf4, 0xa9, 0xe4,
|
||||
0xeb, 0x13, 0x38, 0x27, 0xa3, 0x5e, 0x27, 0xe2, 0x6a, 0x05, 0x3d, 0x83, 0xb9, 0xfc, 0x85, 0x3e,
|
||||
0xca, 0xc1, 0x94, 0xd2, 0x37, 0x86, 0x8e, 0x39, 0x69, 0x48, 0xa2, 0xff, 0x73, 0xe1, 0xd0, 0xdc,
|
||||
0xdd, 0x35, 0x32, 0xf3, 0x15, 0x7b, 0xd9, 0xed, 0x7f, 0xe7, 0x6b, 0x13, 0xc7, 0x24, 0xdc, 0x3f,
|
||||
0x80, 0x56, 0x7c, 0xd7, 0x9b, 0x37, 0x73, 0xe1, 0x06, 0xb8, 0xb3, 0x90, 0xe7, 0xb7, 0x1b, 0x9a,
|
||||
0x53, 0xe8, 0x23, 0x35, 0x79, 0x9d, 0xb1, 0x92, 0xc9, 0x99, 0x1b, 0xce, 0xce, 0xe9, 0x92, 0x5b,
|
||||
0x45, 0x73, 0x0a, 0x7d, 0x1b, 0x66, 0xc4, 0xd7, 0x8e, 0x7e, 0x18, 0x5f, 0xea, 0xaa, 0xdf, 0x61,
|
||||
0x74, 0xe3, 0xdf, 0x61, 0x74, 0xef, 0xba, 0x8c, 0x1f, 0x74, 0x4a, 0xae, 0xfd, 0x34, 0x83, 0xe7,
|
||||
0x30, 0xbb, 0x49, 0x78, 0x5a, 0xa5, 0xa3, 0xcb, 0x87, 0xba, 0xcb, 0xe8, 0x98, 0xc5, 0x61, 0xa3,
|
||||
0x85, 0xbe, 0x39, 0x85, 0x7e, 0x5d, 0x81, 0xd3, 0x9b, 0x84, 0x17, 0xeb, 0x5e, 0xf4, 0x6e, 0xb9,
|
||||
0x90, 0x31, 0xf5, 0x71, 0xe7, 0xd1, 0x51, 0xb3, 0x2b, 0xcf, 0xd6, 0x9c, 0x42, 0xbf, 0xa9, 0xc0,
|
||||
0xb9, 0x8c, 0x62, 0xd9, 0x42, 0x16, 0xdd, 0x98, 0xac, 0x5c, 0x49, 0xd1, 0xdb, 0xf9, 0xe4, 0x88,
|
||||
0xbf, 0x77, 0xc8, 0xb0, 0x34, 0xa7, 0xd0, 0x8e, 0xf4, 0x49, 0x8a, 0x5b, 0xd1, 0xa5, 0x52, 0x80,
|
||||
0x9a, 0x48, 0x5f, 0x1e, 0xd7, 0x9d, 0xf8, 0xe1, 0x13, 0x98, 0xd9, 0x24, 0x3c, 0x06, 0x50, 0xf9,
|
||||
0x48, 0x2b, 0x60, 0xdb, 0x7c, 0xaa, 0x16, 0x31, 0x97, 0x8c, 0x98, 0x45, 0xc5, 0x2b, 0x03, 0x12,
|
||||
0xf2, 0xb9, 0x5a, 0x8a, 0xa6, 0xf2, 0x11, 0x53, 0x8e, 0x31, 0xcc, 0x29, 0xf4, 0x02, 0x96, 0xca,
|
||||
0x37, 0x3d, 0xf4, 0xf6, 0xa1, 0x0f, 0xb5, 0xce, 0xb5, 0xc3, 0x0c, 0x8d, 0x45, 0x7e, 0xbc, 0xfe,
|
||||
0xd7, 0x57, 0xcb, 0x95, 0xbf, 0xbd, 0x5a, 0xae, 0xfc, 0xfb, 0xd5, 0x72, 0xe5, 0xfb, 0x37, 0x5f,
|
||||
0xf3, 0xbb, 0xa8, 0xcc, 0x4f, 0xad, 0x30, 0xa3, 0xb6, 0x43, 0x89, 0xc7, 0x7b, 0x4d, 0x99, 0x6f,
|
||||
0x37, 0xff, 0x13, 0x00, 0x00, 0xff, 0xff, 0xf0, 0xf8, 0x9b, 0x3a, 0x89, 0x25, 0x00, 0x00,
|
||||
}
|
||||
|
||||
// Reference imports to suppress errors if they are not otherwise used.
|
||||
@@ -3196,6 +3214,15 @@ func (m *ManifestRequest) MarshalToSizedBuffer(dAtA []byte) (int, error) {
|
||||
i -= len(m.XXX_unrecognized)
|
||||
copy(dAtA[i:], m.XXX_unrecognized)
|
||||
}
|
||||
if len(m.InstallationID) > 0 {
|
||||
i -= len(m.InstallationID)
|
||||
copy(dAtA[i:], m.InstallationID)
|
||||
i = encodeVarintRepository(dAtA, i, uint64(len(m.InstallationID)))
|
||||
i--
|
||||
dAtA[i] = 0x1
|
||||
i--
|
||||
dAtA[i] = 0xda
|
||||
}
|
||||
if len(m.ProjectName) > 0 {
|
||||
i -= len(m.ProjectName)
|
||||
copy(dAtA[i:], m.ProjectName)
|
||||
@@ -5211,6 +5238,13 @@ func (m *UpdateRevisionForPathsRequest) MarshalToSizedBuffer(dAtA []byte) (int,
|
||||
i -= len(m.XXX_unrecognized)
|
||||
copy(dAtA[i:], m.XXX_unrecognized)
|
||||
}
|
||||
if len(m.InstallationID) > 0 {
|
||||
i -= len(m.InstallationID)
|
||||
copy(dAtA[i:], m.InstallationID)
|
||||
i = encodeVarintRepository(dAtA, i, uint64(len(m.InstallationID)))
|
||||
i--
|
||||
dAtA[i] = 0x7a
|
||||
}
|
||||
if len(m.Paths) > 0 {
|
||||
for iNdEx := len(m.Paths) - 1; iNdEx >= 0; iNdEx-- {
|
||||
i -= len(m.Paths[iNdEx])
|
||||
@@ -5492,6 +5526,10 @@ func (m *ManifestRequest) Size() (n int) {
|
||||
if l > 0 {
|
||||
n += 2 + l + sovRepository(uint64(l))
|
||||
}
|
||||
l = len(m.InstallationID)
|
||||
if l > 0 {
|
||||
n += 2 + l + sovRepository(uint64(l))
|
||||
}
|
||||
if m.XXX_unrecognized != nil {
|
||||
n += len(m.XXX_unrecognized)
|
||||
}
|
||||
@@ -6342,6 +6380,10 @@ func (m *UpdateRevisionForPathsRequest) Size() (n int) {
|
||||
n += 1 + l + sovRepository(uint64(l))
|
||||
}
|
||||
}
|
||||
l = len(m.InstallationID)
|
||||
if l > 0 {
|
||||
n += 1 + l + sovRepository(uint64(l))
|
||||
}
|
||||
if m.XXX_unrecognized != nil {
|
||||
n += len(m.XXX_unrecognized)
|
||||
}
|
||||
@@ -7253,6 +7295,38 @@ func (m *ManifestRequest) Unmarshal(dAtA []byte) error {
|
||||
}
|
||||
m.ProjectName = string(dAtA[iNdEx:postIndex])
|
||||
iNdEx = postIndex
|
||||
case 27:
|
||||
if wireType != 2 {
|
||||
return fmt.Errorf("proto: wrong wireType = %d for field InstallationID", wireType)
|
||||
}
|
||||
var stringLen uint64
|
||||
for shift := uint(0); ; shift += 7 {
|
||||
if shift >= 64 {
|
||||
return ErrIntOverflowRepository
|
||||
}
|
||||
if iNdEx >= l {
|
||||
return io.ErrUnexpectedEOF
|
||||
}
|
||||
b := dAtA[iNdEx]
|
||||
iNdEx++
|
||||
stringLen |= uint64(b&0x7F) << shift
|
||||
if b < 0x80 {
|
||||
break
|
||||
}
|
||||
}
|
||||
intStringLen := int(stringLen)
|
||||
if intStringLen < 0 {
|
||||
return ErrInvalidLengthRepository
|
||||
}
|
||||
postIndex := iNdEx + intStringLen
|
||||
if postIndex < 0 {
|
||||
return ErrInvalidLengthRepository
|
||||
}
|
||||
if postIndex > l {
|
||||
return io.ErrUnexpectedEOF
|
||||
}
|
||||
m.InstallationID = string(dAtA[iNdEx:postIndex])
|
||||
iNdEx = postIndex
|
||||
default:
|
||||
iNdEx = preIndex
|
||||
skippy, err := skipRepository(dAtA[iNdEx:])
|
||||
@@ -12537,6 +12611,38 @@ func (m *UpdateRevisionForPathsRequest) Unmarshal(dAtA []byte) error {
|
||||
}
|
||||
m.Paths = append(m.Paths, string(dAtA[iNdEx:postIndex]))
|
||||
iNdEx = postIndex
|
||||
case 15:
|
||||
if wireType != 2 {
|
||||
return fmt.Errorf("proto: wrong wireType = %d for field InstallationID", wireType)
|
||||
}
|
||||
var stringLen uint64
|
||||
for shift := uint(0); ; shift += 7 {
|
||||
if shift >= 64 {
|
||||
return ErrIntOverflowRepository
|
||||
}
|
||||
if iNdEx >= l {
|
||||
return io.ErrUnexpectedEOF
|
||||
}
|
||||
b := dAtA[iNdEx]
|
||||
iNdEx++
|
||||
stringLen |= uint64(b&0x7F) << shift
|
||||
if b < 0x80 {
|
||||
break
|
||||
}
|
||||
}
|
||||
intStringLen := int(stringLen)
|
||||
if intStringLen < 0 {
|
||||
return ErrInvalidLengthRepository
|
||||
}
|
||||
postIndex := iNdEx + intStringLen
|
||||
if postIndex < 0 {
|
||||
return ErrInvalidLengthRepository
|
||||
}
|
||||
if postIndex > l {
|
||||
return io.ErrUnexpectedEOF
|
||||
}
|
||||
m.InstallationID = string(dAtA[iNdEx:postIndex])
|
||||
iNdEx = postIndex
|
||||
default:
|
||||
iNdEx = preIndex
|
||||
skippy, err := skipRepository(dAtA[iNdEx:])
|
||||
|
||||
28
reposerver/cache/cache.go
vendored
28
reposerver/cache/cache.go
vendored
@@ -290,13 +290,17 @@ func (c *Cache) UnlockGitReferences(repo string, lockId string) error {
|
||||
|
||||
// refSourceCommitSHAs is a list of resolved revisions for each ref source. This allows us to invalidate the cache
|
||||
// when someone pushes a commit to a source which is referenced from the main source (the one referred to by `revision`).
|
||||
func manifestCacheKey(revision string, appSrc *appv1.ApplicationSource, srcRefs appv1.RefTargetRevisionMapping, namespace string, trackingMethod string, appLabelKey string, appName string, info ClusterRuntimeInfo, refSourceCommitSHAs ResolvedRevisions) string {
|
||||
func manifestCacheKey(revision string, appSrc *appv1.ApplicationSource, srcRefs appv1.RefTargetRevisionMapping, namespace string, trackingMethod string, appLabelKey string, appName string, info ClusterRuntimeInfo, refSourceCommitSHAs ResolvedRevisions, installationID string) string {
|
||||
// TODO: this function is getting unwieldy. We should probably consolidate some of this stuff into a struct. For
|
||||
// example, revision could be part of ResolvedRevisions. And srcRefs is probably redundant now that
|
||||
// refSourceCommitSHAs has been added. We don't need to know the _target_ revisions of the referenced sources
|
||||
// when the _resolved_ revisions are already part of the key.
|
||||
trackingKey := trackingKey(appLabelKey, trackingMethod)
|
||||
return fmt.Sprintf("mfst|%s|%s|%s|%s|%d", trackingKey, appName, revision, namespace, appSourceKey(appSrc, srcRefs, refSourceCommitSHAs)+clusterRuntimeInfoKey(info))
|
||||
key := fmt.Sprintf("mfst|%s|%s|%s|%s|%d", trackingKey, appName, revision, namespace, appSourceKey(appSrc, srcRefs, refSourceCommitSHAs)+clusterRuntimeInfoKey(info))
|
||||
if installationID != "" {
|
||||
key = fmt.Sprintf("%s|%s", key, installationID)
|
||||
}
|
||||
return key
|
||||
}
|
||||
|
||||
func trackingKey(appLabelKey string, trackingMethod string) string {
|
||||
@@ -323,14 +327,14 @@ func LogDebugManifestCacheKeyFields(message string, reason string, revision stri
|
||||
}
|
||||
}
|
||||
|
||||
func (c *Cache) SetNewRevisionManifests(newRevision string, revision string, appSrc *appv1.ApplicationSource, srcRefs appv1.RefTargetRevisionMapping, clusterInfo ClusterRuntimeInfo, namespace string, trackingMethod string, appLabelKey string, appName string, refSourceCommitSHAs ResolvedRevisions) error {
|
||||
oldKey := manifestCacheKey(revision, appSrc, srcRefs, namespace, trackingMethod, appLabelKey, appName, clusterInfo, refSourceCommitSHAs)
|
||||
newKey := manifestCacheKey(newRevision, appSrc, srcRefs, namespace, trackingMethod, appLabelKey, appName, clusterInfo, refSourceCommitSHAs)
|
||||
func (c *Cache) SetNewRevisionManifests(newRevision string, revision string, appSrc *appv1.ApplicationSource, srcRefs appv1.RefTargetRevisionMapping, clusterInfo ClusterRuntimeInfo, namespace string, trackingMethod string, appLabelKey string, appName string, refSourceCommitSHAs ResolvedRevisions, installationID string) error {
|
||||
oldKey := manifestCacheKey(revision, appSrc, srcRefs, namespace, trackingMethod, appLabelKey, appName, clusterInfo, refSourceCommitSHAs, installationID)
|
||||
newKey := manifestCacheKey(newRevision, appSrc, srcRefs, namespace, trackingMethod, appLabelKey, appName, clusterInfo, refSourceCommitSHAs, installationID)
|
||||
return c.cache.RenameItem(oldKey, newKey, c.repoCacheExpiration)
|
||||
}
|
||||
|
||||
func (c *Cache) GetManifests(revision string, appSrc *appv1.ApplicationSource, srcRefs appv1.RefTargetRevisionMapping, clusterInfo ClusterRuntimeInfo, namespace string, trackingMethod string, appLabelKey string, appName string, res *CachedManifestResponse, refSourceCommitSHAs ResolvedRevisions) error {
|
||||
err := c.cache.GetItem(manifestCacheKey(revision, appSrc, srcRefs, namespace, trackingMethod, appLabelKey, appName, clusterInfo, refSourceCommitSHAs), res)
|
||||
func (c *Cache) GetManifests(revision string, appSrc *appv1.ApplicationSource, srcRefs appv1.RefTargetRevisionMapping, clusterInfo ClusterRuntimeInfo, namespace string, trackingMethod string, appLabelKey string, appName string, res *CachedManifestResponse, refSourceCommitSHAs ResolvedRevisions, installationID string) error {
|
||||
err := c.cache.GetItem(manifestCacheKey(revision, appSrc, srcRefs, namespace, trackingMethod, appLabelKey, appName, clusterInfo, refSourceCommitSHAs, installationID), res)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -346,7 +350,7 @@ func (c *Cache) GetManifests(revision string, appSrc *appv1.ApplicationSource, s
|
||||
|
||||
LogDebugManifestCacheKeyFields("deleting manifests cache", "manifest hash did not match or cached response is empty", revision, appSrc, srcRefs, clusterInfo, namespace, trackingMethod, appLabelKey, appName, refSourceCommitSHAs)
|
||||
|
||||
err = c.DeleteManifests(revision, appSrc, srcRefs, clusterInfo, namespace, trackingMethod, appLabelKey, appName, refSourceCommitSHAs)
|
||||
err = c.DeleteManifests(revision, appSrc, srcRefs, clusterInfo, namespace, trackingMethod, appLabelKey, appName, refSourceCommitSHAs, installationID)
|
||||
if err != nil {
|
||||
return fmt.Errorf("Unable to delete manifest after hash mismatch, %w", err)
|
||||
}
|
||||
@@ -366,7 +370,7 @@ func (c *Cache) GetManifests(revision string, appSrc *appv1.ApplicationSource, s
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *Cache) SetManifests(revision string, appSrc *appv1.ApplicationSource, srcRefs appv1.RefTargetRevisionMapping, clusterInfo ClusterRuntimeInfo, namespace string, trackingMethod string, appLabelKey string, appName string, res *CachedManifestResponse, refSourceCommitSHAs ResolvedRevisions) error {
|
||||
func (c *Cache) SetManifests(revision string, appSrc *appv1.ApplicationSource, srcRefs appv1.RefTargetRevisionMapping, clusterInfo ClusterRuntimeInfo, namespace string, trackingMethod string, appLabelKey string, appName string, res *CachedManifestResponse, refSourceCommitSHAs ResolvedRevisions, installationID string) error {
|
||||
// Generate and apply the cache entry hash, before writing
|
||||
if res != nil {
|
||||
res = res.shallowCopy()
|
||||
@@ -378,7 +382,7 @@ func (c *Cache) SetManifests(revision string, appSrc *appv1.ApplicationSource, s
|
||||
}
|
||||
|
||||
return c.cache.SetItem(
|
||||
manifestCacheKey(revision, appSrc, srcRefs, namespace, trackingMethod, appLabelKey, appName, clusterInfo, refSourceCommitSHAs),
|
||||
manifestCacheKey(revision, appSrc, srcRefs, namespace, trackingMethod, appLabelKey, appName, clusterInfo, refSourceCommitSHAs, installationID),
|
||||
res,
|
||||
&cacheutil.CacheActionOpts{
|
||||
Expiration: c.repoCacheExpiration,
|
||||
@@ -386,9 +390,9 @@ func (c *Cache) SetManifests(revision string, appSrc *appv1.ApplicationSource, s
|
||||
})
|
||||
}
|
||||
|
||||
func (c *Cache) DeleteManifests(revision string, appSrc *appv1.ApplicationSource, srcRefs appv1.RefTargetRevisionMapping, clusterInfo ClusterRuntimeInfo, namespace, trackingMethod, appLabelKey, appName string, refSourceCommitSHAs ResolvedRevisions) error {
|
||||
func (c *Cache) DeleteManifests(revision string, appSrc *appv1.ApplicationSource, srcRefs appv1.RefTargetRevisionMapping, clusterInfo ClusterRuntimeInfo, namespace, trackingMethod, appLabelKey, appName string, refSourceCommitSHAs ResolvedRevisions, installationID string) error {
|
||||
return c.cache.SetItem(
|
||||
manifestCacheKey(revision, appSrc, srcRefs, namespace, trackingMethod, appLabelKey, appName, clusterInfo, refSourceCommitSHAs),
|
||||
manifestCacheKey(revision, appSrc, srcRefs, namespace, trackingMethod, appLabelKey, appName, clusterInfo, refSourceCommitSHAs, installationID),
|
||||
"",
|
||||
&cacheutil.CacheActionOpts{Delete: true})
|
||||
}
|
||||
|
||||
26
reposerver/cache/cache_test.go
vendored
26
reposerver/cache/cache_test.go
vendored
@@ -95,43 +95,43 @@ func TestCache_GetManifests(t *testing.T) {
|
||||
// cache miss
|
||||
q := &apiclient.ManifestRequest{}
|
||||
value := &CachedManifestResponse{}
|
||||
err := cache.GetManifests("my-revision", &ApplicationSource{}, q.RefSources, q, "my-namespace", "", "my-app-label-key", "my-app-label-value", value, nil)
|
||||
err := cache.GetManifests("my-revision", &ApplicationSource{}, q.RefSources, q, "my-namespace", "", "my-app-label-key", "my-app-label-value", value, nil, "")
|
||||
assert.Equal(t, ErrCacheMiss, err)
|
||||
// populate cache
|
||||
res := &CachedManifestResponse{ManifestResponse: &apiclient.ManifestResponse{SourceType: "my-source-type"}}
|
||||
err = cache.SetManifests("my-revision", &ApplicationSource{}, q.RefSources, q, "my-namespace", "", "my-app-label-key", "my-app-label-value", res, nil)
|
||||
err = cache.SetManifests("my-revision", &ApplicationSource{}, q.RefSources, q, "my-namespace", "", "my-app-label-key", "my-app-label-value", res, nil, "")
|
||||
require.NoError(t, err)
|
||||
t.Run("expect cache miss because of changed revision", func(t *testing.T) {
|
||||
err = cache.GetManifests("other-revision", &ApplicationSource{}, q.RefSources, q, "my-namespace", "", "my-app-label-key", "my-app-label-value", value, nil)
|
||||
err = cache.GetManifests("other-revision", &ApplicationSource{}, q.RefSources, q, "my-namespace", "", "my-app-label-key", "my-app-label-value", value, nil, "")
|
||||
assert.Equal(t, ErrCacheMiss, err)
|
||||
})
|
||||
t.Run("expect cache miss because of changed path", func(t *testing.T) {
|
||||
err = cache.GetManifests("my-revision", &ApplicationSource{Path: "other-path"}, q.RefSources, q, "my-namespace", "", "my-app-label-key", "my-app-label-value", value, nil)
|
||||
err = cache.GetManifests("my-revision", &ApplicationSource{Path: "other-path"}, q.RefSources, q, "my-namespace", "", "my-app-label-key", "my-app-label-value", value, nil, "")
|
||||
assert.Equal(t, ErrCacheMiss, err)
|
||||
})
|
||||
t.Run("expect cache miss because of changed namespace", func(t *testing.T) {
|
||||
err = cache.GetManifests("my-revision", &ApplicationSource{}, q.RefSources, q, "other-namespace", "", "my-app-label-key", "my-app-label-value", value, nil)
|
||||
err = cache.GetManifests("my-revision", &ApplicationSource{}, q.RefSources, q, "other-namespace", "", "my-app-label-key", "my-app-label-value", value, nil, "")
|
||||
assert.Equal(t, ErrCacheMiss, err)
|
||||
})
|
||||
t.Run("expect cache miss because of changed app label key", func(t *testing.T) {
|
||||
err = cache.GetManifests("my-revision", &ApplicationSource{}, q.RefSources, q, "my-namespace", "", "other-app-label-key", "my-app-label-value", value, nil)
|
||||
err = cache.GetManifests("my-revision", &ApplicationSource{}, q.RefSources, q, "my-namespace", "", "other-app-label-key", "my-app-label-value", value, nil, "")
|
||||
assert.Equal(t, ErrCacheMiss, err)
|
||||
})
|
||||
t.Run("expect cache miss because of changed app label value", func(t *testing.T) {
|
||||
err = cache.GetManifests("my-revision", &ApplicationSource{}, q.RefSources, q, "my-namespace", "", "my-app-label-key", "other-app-label-value", value, nil)
|
||||
err = cache.GetManifests("my-revision", &ApplicationSource{}, q.RefSources, q, "my-namespace", "", "my-app-label-key", "other-app-label-value", value, nil, "")
|
||||
assert.Equal(t, ErrCacheMiss, err)
|
||||
})
|
||||
t.Run("expect cache miss because of changed referenced source", func(t *testing.T) {
|
||||
err = cache.GetManifests("my-revision", &ApplicationSource{}, q.RefSources, q, "my-namespace", "", "my-app-label-key", "other-app-label-value", value, map[string]string{"my-referenced-source": "my-referenced-revision"})
|
||||
err = cache.GetManifests("my-revision", &ApplicationSource{}, q.RefSources, q, "my-namespace", "", "my-app-label-key", "other-app-label-value", value, map[string]string{"my-referenced-source": "my-referenced-revision"}, "")
|
||||
assert.Equal(t, ErrCacheMiss, err)
|
||||
})
|
||||
t.Run("expect cache hit", func(t *testing.T) {
|
||||
err = cache.SetManifests(
|
||||
"my-revision1", &ApplicationSource{}, q.RefSources, q, "my-namespace", "", "my-app-label-key", "my-app-label-value",
|
||||
&CachedManifestResponse{ManifestResponse: &apiclient.ManifestResponse{SourceType: "my-source-type", Revision: "my-revision2"}}, nil)
|
||||
&CachedManifestResponse{ManifestResponse: &apiclient.ManifestResponse{SourceType: "my-source-type", Revision: "my-revision2"}}, nil, "")
|
||||
require.NoError(t, err)
|
||||
|
||||
err = cache.GetManifests("my-revision1", &ApplicationSource{}, q.RefSources, q, "my-namespace", "", "my-app-label-key", "my-app-label-value", value, nil)
|
||||
err = cache.GetManifests("my-revision1", &ApplicationSource{}, q.RefSources, q, "my-namespace", "", "my-app-label-key", "my-app-label-value", value, nil, "")
|
||||
require.NoError(t, err)
|
||||
|
||||
assert.Equal(t, "my-source-type", value.ManifestResponse.SourceType)
|
||||
@@ -200,7 +200,7 @@ func TestCachedManifestResponse_HashBehavior(t *testing.T) {
|
||||
NumberOfConsecutiveFailures: 0,
|
||||
}
|
||||
q := &apiclient.ManifestRequest{}
|
||||
err := repoCache.SetManifests(response.Revision, appSrc, q.RefSources, q, response.Namespace, "", appKey, appValue, store, nil)
|
||||
err := repoCache.SetManifests(response.Revision, appSrc, q.RefSources, q, response.Namespace, "", appKey, appValue, store, nil, "")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
@@ -230,7 +230,7 @@ func TestCachedManifestResponse_HashBehavior(t *testing.T) {
|
||||
|
||||
// Retrieve the value using 'GetManifests' and confirm it works
|
||||
retrievedVal := &CachedManifestResponse{}
|
||||
err = repoCache.GetManifests(response.Revision, appSrc, q.RefSources, q, response.Namespace, "", appKey, appValue, retrievedVal, nil)
|
||||
err = repoCache.GetManifests(response.Revision, appSrc, q.RefSources, q, response.Namespace, "", appKey, appValue, retrievedVal, nil, "")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
@@ -252,7 +252,7 @@ func TestCachedManifestResponse_HashBehavior(t *testing.T) {
|
||||
|
||||
// Retrieve the value using GetManifests and confirm it returns a cache miss
|
||||
retrievedVal = &CachedManifestResponse{}
|
||||
err = repoCache.GetManifests(response.Revision, appSrc, q.RefSources, q, response.Namespace, "", appKey, appValue, retrievedVal, nil)
|
||||
err = repoCache.GetManifests(response.Revision, appSrc, q.RefSources, q, response.Namespace, "", appKey, appValue, retrievedVal, nil, "")
|
||||
|
||||
assert.Equal(t, err, cacheutil.ErrCacheMiss)
|
||||
|
||||
|
||||
@@ -814,7 +814,7 @@ func (s *Service) runManifestGenAsync(ctx context.Context, repoRoot, commitSHA,
|
||||
// Retrieve a new copy (if available) of the cached response: this ensures we are updating the latest copy of the cache,
|
||||
// rather than a copy of the cache that occurred before (a potentially lengthy) manifest generation.
|
||||
innerRes := &cache.CachedManifestResponse{}
|
||||
cacheErr := s.cache.GetManifests(cacheKey, appSourceCopy, q.RefSources, q, q.Namespace, q.TrackingMethod, q.AppLabelKey, q.AppName, innerRes, refSourceCommitSHAs)
|
||||
cacheErr := s.cache.GetManifests(cacheKey, appSourceCopy, q.RefSources, q, q.Namespace, q.TrackingMethod, q.AppLabelKey, q.AppName, innerRes, refSourceCommitSHAs, q.InstallationID)
|
||||
if cacheErr != nil && !errors.Is(cacheErr, cache.ErrCacheMiss) {
|
||||
logCtx.Warnf("manifest cache get error %s: %v", appSourceCopy.String(), cacheErr)
|
||||
ch.errCh <- cacheErr
|
||||
@@ -832,7 +832,7 @@ func (s *Service) runManifestGenAsync(ctx context.Context, repoRoot, commitSHA,
|
||||
// Update the cache to include failure information
|
||||
innerRes.NumberOfConsecutiveFailures++
|
||||
innerRes.MostRecentError = err.Error()
|
||||
cacheErr = s.cache.SetManifests(cacheKey, appSourceCopy, q.RefSources, q, q.Namespace, q.TrackingMethod, q.AppLabelKey, q.AppName, innerRes, refSourceCommitSHAs)
|
||||
cacheErr = s.cache.SetManifests(cacheKey, appSourceCopy, q.RefSources, q, q.Namespace, q.TrackingMethod, q.AppLabelKey, q.AppName, innerRes, refSourceCommitSHAs, q.InstallationID)
|
||||
|
||||
if cacheErr != nil {
|
||||
logCtx.Warnf("manifest cache set error %s: %v", appSourceCopy.String(), cacheErr)
|
||||
@@ -856,7 +856,7 @@ func (s *Service) runManifestGenAsync(ctx context.Context, repoRoot, commitSHA,
|
||||
}
|
||||
manifestGenResult.Revision = commitSHA
|
||||
manifestGenResult.VerifyResult = opContext.verificationResult
|
||||
err = s.cache.SetManifests(cacheKey, appSourceCopy, q.RefSources, q, q.Namespace, q.TrackingMethod, q.AppLabelKey, q.AppName, &manifestGenCacheEntry, refSourceCommitSHAs)
|
||||
err = s.cache.SetManifests(cacheKey, appSourceCopy, q.RefSources, q, q.Namespace, q.TrackingMethod, q.AppLabelKey, q.AppName, &manifestGenCacheEntry, refSourceCommitSHAs, q.InstallationID)
|
||||
if err != nil {
|
||||
log.Warnf("manifest cache set error %s/%s: %v", appSourceCopy.String(), cacheKey, err)
|
||||
}
|
||||
@@ -873,7 +873,7 @@ func (s *Service) getManifestCacheEntry(cacheKey string, q *apiclient.ManifestRe
|
||||
cache.LogDebugManifestCacheKeyFields("getting manifests cache", "GenerateManifest API call", cacheKey, q.ApplicationSource, q.RefSources, q, q.Namespace, q.TrackingMethod, q.AppLabelKey, q.AppName, refSourceCommitSHAs)
|
||||
|
||||
res := cache.CachedManifestResponse{}
|
||||
err := s.cache.GetManifests(cacheKey, q.ApplicationSource, q.RefSources, q, q.Namespace, q.TrackingMethod, q.AppLabelKey, q.AppName, &res, refSourceCommitSHAs)
|
||||
err := s.cache.GetManifests(cacheKey, q.ApplicationSource, q.RefSources, q, q.Namespace, q.TrackingMethod, q.AppLabelKey, q.AppName, &res, refSourceCommitSHAs, q.InstallationID)
|
||||
if err == nil {
|
||||
// The cache contains an existing value
|
||||
|
||||
@@ -890,7 +890,7 @@ func (s *Service) getManifestCacheEntry(cacheKey string, q *apiclient.ManifestRe
|
||||
cache.LogDebugManifestCacheKeyFields("deleting manifests cache", "manifest hash did not match or cached response is empty", cacheKey, q.ApplicationSource, q.RefSources, q, q.Namespace, q.TrackingMethod, q.AppLabelKey, q.AppName, refSourceCommitSHAs)
|
||||
|
||||
// We can now try again, so reset the cache state and run the operation below
|
||||
err = s.cache.DeleteManifests(cacheKey, q.ApplicationSource, q.RefSources, q, q.Namespace, q.TrackingMethod, q.AppLabelKey, q.AppName, refSourceCommitSHAs)
|
||||
err = s.cache.DeleteManifests(cacheKey, q.ApplicationSource, q.RefSources, q, q.Namespace, q.TrackingMethod, q.AppLabelKey, q.AppName, refSourceCommitSHAs, q.InstallationID)
|
||||
if err != nil {
|
||||
log.Warnf("manifest cache set error %s/%s: %v", q.ApplicationSource.String(), cacheKey, err)
|
||||
}
|
||||
@@ -905,7 +905,7 @@ func (s *Service) getManifestCacheEntry(cacheKey string, q *apiclient.ManifestRe
|
||||
cache.LogDebugManifestCacheKeyFields("deleting manifests cache", "reset after paused generation count", cacheKey, q.ApplicationSource, q.RefSources, q, q.Namespace, q.TrackingMethod, q.AppLabelKey, q.AppName, refSourceCommitSHAs)
|
||||
|
||||
// We can now try again, so reset the error cache state and run the operation below
|
||||
err = s.cache.DeleteManifests(cacheKey, q.ApplicationSource, q.RefSources, q, q.Namespace, q.TrackingMethod, q.AppLabelKey, q.AppName, refSourceCommitSHAs)
|
||||
err = s.cache.DeleteManifests(cacheKey, q.ApplicationSource, q.RefSources, q, q.Namespace, q.TrackingMethod, q.AppLabelKey, q.AppName, refSourceCommitSHAs, q.InstallationID)
|
||||
if err != nil {
|
||||
log.Warnf("manifest cache set error %s/%s: %v", q.ApplicationSource.String(), cacheKey, err)
|
||||
}
|
||||
@@ -925,7 +925,7 @@ func (s *Service) getManifestCacheEntry(cacheKey string, q *apiclient.ManifestRe
|
||||
// Increment the number of returned cached responses and push that new value to the cache
|
||||
// (if we have not already done so previously in this function)
|
||||
res.NumberOfCachedResponsesReturned++
|
||||
err = s.cache.SetManifests(cacheKey, q.ApplicationSource, q.RefSources, q, q.Namespace, q.TrackingMethod, q.AppLabelKey, q.AppName, &res, refSourceCommitSHAs)
|
||||
err = s.cache.SetManifests(cacheKey, q.ApplicationSource, q.RefSources, q, q.Namespace, q.TrackingMethod, q.AppLabelKey, q.AppName, &res, refSourceCommitSHAs, q.InstallationID)
|
||||
if err != nil {
|
||||
log.Warnf("manifest cache set error %s/%s: %v", q.ApplicationSource.String(), cacheKey, err)
|
||||
}
|
||||
@@ -1444,7 +1444,7 @@ func GenerateManifests(ctx context.Context, appPath, repoRoot, revision string,
|
||||
|
||||
for _, target := range targets {
|
||||
if q.AppLabelKey != "" && q.AppName != "" && !kube.IsCRD(target) {
|
||||
err = resourceTracking.SetAppInstance(target, q.AppLabelKey, q.AppName, q.Namespace, v1alpha1.TrackingMethod(q.TrackingMethod))
|
||||
err = resourceTracking.SetAppInstance(target, q.AppLabelKey, q.AppName, q.Namespace, v1alpha1.TrackingMethod(q.TrackingMethod), q.InstallationID)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to set app instance tracking info on manifest: %w", err)
|
||||
}
|
||||
@@ -2769,7 +2769,10 @@ func (s *Service) UpdateRevisionForPaths(_ context.Context, request *apiclient.U
|
||||
return nil, status.Errorf(codes.Internal, "unable to get changed files for repo %s with revision %s: %v", repo.Repo, revision, err)
|
||||
}
|
||||
|
||||
changed := apppathutil.AppFilesHaveChanged(refreshPaths, files)
|
||||
changed := false
|
||||
if len(files) != 0 {
|
||||
changed = apppathutil.AppFilesHaveChanged(refreshPaths, files)
|
||||
}
|
||||
|
||||
if !changed {
|
||||
logCtx.Debugf("no changes found for application %s in repo %s from revision %s to revision %s", request.AppName, repo.Repo, syncedRevision, revision)
|
||||
@@ -2803,7 +2806,7 @@ func (s *Service) updateCachedRevision(logCtx *log.Entry, oldRev string, newRev
|
||||
}
|
||||
}
|
||||
|
||||
err := s.cache.SetNewRevisionManifests(newRev, oldRev, request.ApplicationSource, request.RefSources, request, request.Namespace, request.TrackingMethod, request.AppLabelKey, request.AppName, repoRefs)
|
||||
err := s.cache.SetNewRevisionManifests(newRev, oldRev, request.ApplicationSource, request.RefSources, request, request.Namespace, request.TrackingMethod, request.AppLabelKey, request.AppName, repoRefs, request.InstallationID)
|
||||
if err != nil {
|
||||
if errors.Is(err, cache.ErrCacheMiss) {
|
||||
logCtx.Debugf("manifest cache miss during comparison for application %s in repo %s from revision %s", request.AppName, request.GetRepo().Repo, oldRev)
|
||||
|
||||
@@ -36,6 +36,8 @@ message ManifestRequest {
|
||||
repeated string projectSourceRepos = 24;
|
||||
// This is used to surface "source not permitted" errors for Helm repositories
|
||||
string projectName = 25;
|
||||
// Holds instance installation id
|
||||
string installationID = 27;
|
||||
}
|
||||
|
||||
message ManifestRequestWithFiles {
|
||||
@@ -275,6 +277,7 @@ message UpdateRevisionForPathsRequest {
|
||||
string syncedRevision = 11;
|
||||
string revision = 12;
|
||||
repeated string paths = 13;
|
||||
string installationID = 15;
|
||||
}
|
||||
|
||||
message UpdateRevisionForPathsResponse {
|
||||
|
||||
@@ -310,7 +310,7 @@ func TestGenerateManifests_K8SAPIResetCache(t *testing.T) {
|
||||
|
||||
cachedFakeResponse := &apiclient.ManifestResponse{Manifests: []string{"Fake"}, Revision: mock.Anything}
|
||||
|
||||
err := service.cache.SetManifests(mock.Anything, &src, q.RefSources, &q, "", "", "", "", &cache.CachedManifestResponse{ManifestResponse: cachedFakeResponse}, nil)
|
||||
err := service.cache.SetManifests(mock.Anything, &src, q.RefSources, &q, "", "", "", "", &cache.CachedManifestResponse{ManifestResponse: cachedFakeResponse}, nil, "")
|
||||
require.NoError(t, err)
|
||||
|
||||
res, err := service.GenerateManifest(context.Background(), &q)
|
||||
@@ -335,7 +335,7 @@ func TestGenerateManifests_EmptyCache(t *testing.T) {
|
||||
ProjectSourceRepos: []string{"*"},
|
||||
}
|
||||
|
||||
err := service.cache.SetManifests(mock.Anything, &src, q.RefSources, &q, "", "", "", "", &cache.CachedManifestResponse{ManifestResponse: nil}, nil)
|
||||
err := service.cache.SetManifests(mock.Anything, &src, q.RefSources, &q, "", "", "", "", &cache.CachedManifestResponse{ManifestResponse: nil}, nil, "")
|
||||
require.NoError(t, err)
|
||||
|
||||
res, err := service.GenerateManifest(context.Background(), &q)
|
||||
@@ -768,7 +768,7 @@ func TestManifestGenErrorCacheByNumRequests(t *testing.T) {
|
||||
assert.NotNil(t, manifestRequest)
|
||||
|
||||
cachedManifestResponse := &cache.CachedManifestResponse{}
|
||||
err := service.cache.GetManifests(mock.Anything, manifestRequest.ApplicationSource, manifestRequest.RefSources, manifestRequest, manifestRequest.Namespace, "", manifestRequest.AppLabelKey, manifestRequest.AppName, cachedManifestResponse, nil)
|
||||
err := service.cache.GetManifests(mock.Anything, manifestRequest.ApplicationSource, manifestRequest.RefSources, manifestRequest, manifestRequest.Namespace, "", manifestRequest.AppLabelKey, manifestRequest.AppName, cachedManifestResponse, nil, "")
|
||||
require.NoError(t, err)
|
||||
return cachedManifestResponse
|
||||
}
|
||||
@@ -2104,7 +2104,7 @@ func TestGenerateManifestsWithAppParameterFile(t *testing.T) {
|
||||
// Try to pull from the cache with a `source` that does not include any overrides. Overrides should not be
|
||||
// part of the cache key, because you can't get the overrides without a repo operation. And avoiding repo
|
||||
// operations is the point of the cache.
|
||||
err = service.cache.GetManifests(mock.Anything, source, argoappv1.RefTargetRevisionMapping{}, &argoappv1.ClusterInfo{}, "", "", "", "test", res, nil)
|
||||
err = service.cache.GetManifests(mock.Anything, source, argoappv1.RefTargetRevisionMapping{}, &argoappv1.ClusterInfo{}, "", "", "", "test", res, nil, "")
|
||||
require.NoError(t, err)
|
||||
})
|
||||
})
|
||||
|
||||
@@ -0,0 +1,47 @@
|
||||
local health_status = {
|
||||
status = "Progressing",
|
||||
message = "Provisioning..."
|
||||
}
|
||||
|
||||
-- If .status is nil or doesn't have conditions, then the control plane is not ready
|
||||
if obj.status == nil or obj.status.conditions == nil then
|
||||
return health_status
|
||||
end
|
||||
|
||||
-- Accumulator for the error messages (could be multiple conditions in error state)
|
||||
err_msg = ""
|
||||
|
||||
-- Iterate over the conditions to determine the health status
|
||||
for i, condition in ipairs(obj.status.conditions) do
|
||||
-- Check if the Ready condition is True, then the control plane is ready
|
||||
if condition.type == "Ready" and condition.status == "True" then
|
||||
health_status.status = "Healthy"
|
||||
health_status.message = "Control plane is ready"
|
||||
return health_status
|
||||
end
|
||||
|
||||
-- If we have a condition that is False and has an Error severity, then the control plane is in a degraded state
|
||||
if condition.status == "False" and condition.severity == "Error" then
|
||||
health_status.status = "Degraded"
|
||||
err_msg = err_msg .. condition.message .. " "
|
||||
end
|
||||
end
|
||||
|
||||
-- If we have any error conditions, then the control plane is in a degraded state
|
||||
if health_status.status == "Degraded" then
|
||||
health_status.message = err_msg
|
||||
return health_status
|
||||
end
|
||||
|
||||
-- If .status.ready is False, then the control plane is not ready
|
||||
if obj.status.ready == false then
|
||||
health_status.status = "Progressing"
|
||||
health_status.message = "Control plane is not ready (.status.ready is false)"
|
||||
return health_status
|
||||
end
|
||||
|
||||
-- If we reach this point, then the control plane is not ready and we don't have any error conditions
|
||||
health_status.status = "Progressing"
|
||||
health_status.message = "Control plane is not ready"
|
||||
|
||||
return health_status
|
||||
@@ -0,0 +1,17 @@
|
||||
tests:
|
||||
- healthStatus:
|
||||
status: Healthy
|
||||
message: 'Control plane is ready'
|
||||
inputPath: testdata/healthy.yaml
|
||||
- healthStatus:
|
||||
status: Progressing
|
||||
message: 'Control plane is not ready (.status.ready is false)'
|
||||
inputPath: testdata/progressing_ready_false.yaml
|
||||
- healthStatus:
|
||||
status: Progressing
|
||||
message: 'Control plane is not ready'
|
||||
inputPath: testdata/progressing_ready_true.yaml
|
||||
- healthStatus:
|
||||
status: Degraded
|
||||
message: '7 of 10 completed failed reconciling OIDC provider for cluster: failed to create OIDC provider: error creating provider: LimitExceeded: Cannot exceed quota for OpenIdConnectProvidersPerAccount: 100 '
|
||||
inputPath: testdata/degraded.yaml
|
||||
@@ -0,0 +1,50 @@
|
||||
apiVersion: controlplane.cluster.x-k8s.io/v1beta2
|
||||
kind: AWSManagedControlPlane
|
||||
metadata:
|
||||
name: test
|
||||
namespace: ns-test
|
||||
ownerReferences:
|
||||
- apiVersion: cluster.x-k8s.io/v1beta1
|
||||
blockOwnerDeletion: true
|
||||
controller: true
|
||||
kind: Cluster
|
||||
name: test
|
||||
status:
|
||||
conditions:
|
||||
- lastTransitionTime: "2024-07-30T11:10:03Z"
|
||||
status: "False"
|
||||
severity: Error
|
||||
message: "7 of 10 completed"
|
||||
type: Ready
|
||||
- lastTransitionTime: "2024-07-26T14:35:48Z"
|
||||
status: "True"
|
||||
type: ClusterSecurityGroupsReady
|
||||
- lastTransitionTime: "2024-07-30T02:20:06Z"
|
||||
status: "True"
|
||||
type: EKSAddonsConfigured
|
||||
- lastTransitionTime: "2024-07-26T14:43:57Z"
|
||||
reason: created
|
||||
severity: Info
|
||||
status: "False"
|
||||
type: EKSControlPlaneCreating
|
||||
- lastTransitionTime: "2024-07-25T09:22:46Z"
|
||||
message: "failed reconciling OIDC provider for cluster: failed to create OIDC provider: error creating provider: LimitExceeded: Cannot exceed quota for OpenIdConnectProvidersPerAccount: 100"
|
||||
reason: EKSControlPlaneReconciliationFailed
|
||||
severity: Error
|
||||
status: "False"
|
||||
- lastTransitionTime: "2024-07-30T11:10:03Z"
|
||||
status: "True"
|
||||
type: EKSControlPlaneReady
|
||||
- lastTransitionTime: "2024-07-26T15:28:01Z"
|
||||
status: "True"
|
||||
type: IAMAuthenticatorConfigured
|
||||
- lastTransitionTime: "2024-07-26T15:27:58Z"
|
||||
status: "True"
|
||||
type: IAMControlPlaneRolesReady
|
||||
- lastTransitionTime: "2024-07-26T14:35:48Z"
|
||||
status: "True"
|
||||
type: SubnetsReady
|
||||
- lastTransitionTime: "2024-07-26T14:35:46Z"
|
||||
status: "True"
|
||||
type: VpcReady
|
||||
ready: true
|
||||
@@ -0,0 +1,46 @@
|
||||
apiVersion: controlplane.cluster.x-k8s.io/v1beta2
|
||||
kind: AWSManagedControlPlane
|
||||
metadata:
|
||||
name: test
|
||||
namespace: ns-test
|
||||
ownerReferences:
|
||||
- apiVersion: cluster.x-k8s.io/v1beta1
|
||||
blockOwnerDeletion: true
|
||||
controller: true
|
||||
kind: Cluster
|
||||
name: test
|
||||
status:
|
||||
conditions:
|
||||
- lastTransitionTime: "2024-07-30T11:10:03Z"
|
||||
status: "True"
|
||||
type: Ready
|
||||
- lastTransitionTime: "2024-07-26T14:35:48Z"
|
||||
status: "True"
|
||||
type: ClusterSecurityGroupsReady
|
||||
- lastTransitionTime: "2024-07-30T02:20:06Z"
|
||||
status: "True"
|
||||
type: EKSAddonsConfigured
|
||||
- lastTransitionTime: "2024-07-26T14:43:57Z"
|
||||
reason: created
|
||||
severity: Info
|
||||
status: "False"
|
||||
type: EKSControlPlaneCreating
|
||||
- lastTransitionTime: "2024-07-30T11:10:03Z"
|
||||
status: "True"
|
||||
type: EKSControlPlaneReady
|
||||
- lastTransitionTime: "2024-07-30T13:05:45Z"
|
||||
status: "True"
|
||||
type: EKSIdentityProviderConfigured
|
||||
- lastTransitionTime: "2024-07-26T15:28:01Z"
|
||||
status: "True"
|
||||
type: IAMAuthenticatorConfigured
|
||||
- lastTransitionTime: "2024-07-26T15:27:58Z"
|
||||
status: "True"
|
||||
type: IAMControlPlaneRolesReady
|
||||
- lastTransitionTime: "2024-07-26T14:35:48Z"
|
||||
status: "True"
|
||||
type: SubnetsReady
|
||||
- lastTransitionTime: "2024-07-26T14:35:46Z"
|
||||
status: "True"
|
||||
type: VpcReady
|
||||
ready: true
|
||||
@@ -0,0 +1,46 @@
|
||||
apiVersion: controlplane.cluster.x-k8s.io/v1beta2
|
||||
kind: AWSManagedControlPlane
|
||||
metadata:
|
||||
name: test
|
||||
namespace: ns-test
|
||||
ownerReferences:
|
||||
- apiVersion: cluster.x-k8s.io/v1beta1
|
||||
blockOwnerDeletion: true
|
||||
controller: true
|
||||
kind: Cluster
|
||||
name: test
|
||||
status:
|
||||
conditions:
|
||||
- lastTransitionTime: "2024-07-30T11:10:03Z"
|
||||
status: "False"
|
||||
type: Ready
|
||||
- lastTransitionTime: "2024-07-26T14:35:48Z"
|
||||
status: "True"
|
||||
type: ClusterSecurityGroupsReady
|
||||
- lastTransitionTime: "2024-07-30T02:20:06Z"
|
||||
status: "True"
|
||||
type: EKSAddonsConfigured
|
||||
- lastTransitionTime: "2024-07-26T14:43:57Z"
|
||||
reason: created
|
||||
severity: Info
|
||||
status: "False"
|
||||
type: EKSControlPlaneCreating
|
||||
- lastTransitionTime: "2024-07-30T11:10:03Z"
|
||||
status: "True"
|
||||
type: EKSControlPlaneReady
|
||||
- lastTransitionTime: "2024-07-30T13:05:45Z"
|
||||
status: "True"
|
||||
type: EKSIdentityProviderConfigured
|
||||
- lastTransitionTime: "2024-07-26T15:28:01Z"
|
||||
status: "True"
|
||||
type: IAMAuthenticatorConfigured
|
||||
- lastTransitionTime: "2024-07-26T15:27:58Z"
|
||||
status: "True"
|
||||
type: IAMControlPlaneRolesReady
|
||||
- lastTransitionTime: "2024-07-26T14:35:48Z"
|
||||
status: "True"
|
||||
type: SubnetsReady
|
||||
- lastTransitionTime: "2024-07-26T14:35:46Z"
|
||||
status: "True"
|
||||
type: VpcReady
|
||||
ready: false
|
||||
@@ -0,0 +1,46 @@
|
||||
apiVersion: controlplane.cluster.x-k8s.io/v1beta2
|
||||
kind: AWSManagedControlPlane
|
||||
metadata:
|
||||
name: test
|
||||
namespace: ns-test
|
||||
ownerReferences:
|
||||
- apiVersion: cluster.x-k8s.io/v1beta1
|
||||
blockOwnerDeletion: true
|
||||
controller: true
|
||||
kind: Cluster
|
||||
name: test
|
||||
status:
|
||||
conditions:
|
||||
- lastTransitionTime: "2024-07-30T11:10:03Z"
|
||||
status: "False"
|
||||
type: Ready
|
||||
- lastTransitionTime: "2024-07-26T14:35:48Z"
|
||||
status: "True"
|
||||
type: ClusterSecurityGroupsReady
|
||||
- lastTransitionTime: "2024-07-30T02:20:06Z"
|
||||
status: "True"
|
||||
type: EKSAddonsConfigured
|
||||
- lastTransitionTime: "2024-07-26T14:43:57Z"
|
||||
reason: created
|
||||
severity: Info
|
||||
status: "False"
|
||||
type: EKSControlPlaneCreating
|
||||
- lastTransitionTime: "2024-07-30T11:10:03Z"
|
||||
status: "True"
|
||||
type: EKSControlPlaneReady
|
||||
- lastTransitionTime: "2024-07-30T13:05:45Z"
|
||||
status: "True"
|
||||
type: EKSIdentityProviderConfigured
|
||||
- lastTransitionTime: "2024-07-26T15:28:01Z"
|
||||
status: "True"
|
||||
type: IAMAuthenticatorConfigured
|
||||
- lastTransitionTime: "2024-07-26T15:27:58Z"
|
||||
status: "True"
|
||||
type: IAMControlPlaneRolesReady
|
||||
- lastTransitionTime: "2024-07-26T14:35:48Z"
|
||||
status: "True"
|
||||
type: SubnetsReady
|
||||
- lastTransitionTime: "2024-07-26T14:35:46Z"
|
||||
status: "True"
|
||||
type: VpcReady
|
||||
ready: true
|
||||
@@ -509,6 +509,10 @@ func (s *Server) GetManifests(ctx context.Context, q *application.ApplicationMan
|
||||
if err != nil {
|
||||
return fmt.Errorf("error getting kustomize settings options: %w", err)
|
||||
}
|
||||
installationID, err := s.settingsMgr.GetInstallationID()
|
||||
if err != nil {
|
||||
return fmt.Errorf("error getting installation ID: %w", err)
|
||||
}
|
||||
|
||||
manifestInfo, err := client.GenerateManifest(ctx, &apiclient.ManifestRequest{
|
||||
Repo: repo,
|
||||
@@ -529,6 +533,7 @@ func (s *Server) GetManifests(ctx context.Context, q *application.ApplicationMan
|
||||
ProjectSourceRepos: proj.Spec.SourceRepos,
|
||||
HasMultipleSources: a.Spec.HasMultipleSources(),
|
||||
RefSources: refSources,
|
||||
InstallationID: installationID,
|
||||
})
|
||||
if err != nil {
|
||||
return fmt.Errorf("error generating manifests: %w", err)
|
||||
@@ -1495,71 +1500,9 @@ func (s *Server) RevisionMetadata(ctx context.Context, q *application.RevisionMe
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var versionId int64 = 0
|
||||
if q.VersionId != nil {
|
||||
versionId = int64(*q.VersionId)
|
||||
}
|
||||
|
||||
var source *v1alpha1.ApplicationSource
|
||||
|
||||
// To support changes between single source and multi source revisions
|
||||
// we have to calculate if the operation has to be done as multisource or not.
|
||||
// There are 2 different scenarios, checking current revision and historic revision
|
||||
// - Current revision (VersionId is nil or 0):
|
||||
// - The application is multi source and required version too -> multi source
|
||||
// - The application is single source and the required version too -> single source
|
||||
// - The application is multi source and the required version is single source -> single source
|
||||
// - The application is single source and the required version is multi source -> multi source
|
||||
// - Historic revision:
|
||||
// - The application is multi source and the previous one too -> multi source
|
||||
// - The application is single source and the previous one too -> single source
|
||||
// - The application is multi source and the previous one is single source -> multi source
|
||||
// - The application is single source and the previous one is multi source -> single source
|
||||
isRevisionMultiSource := a.Spec.HasMultipleSources()
|
||||
emptyHistory := len(a.Status.History) == 0
|
||||
if !emptyHistory {
|
||||
for _, h := range a.Status.History {
|
||||
if h.ID == versionId {
|
||||
isRevisionMultiSource = len(h.Revisions) > 0
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// If the historical data is empty (because the app hasn't been synced yet)
|
||||
// we can use the source, if not (the app has been synced at least once)
|
||||
// we have to use the history because sources can be added/removed
|
||||
if emptyHistory {
|
||||
if isRevisionMultiSource {
|
||||
source = &a.Spec.Sources[*q.SourceIndex]
|
||||
} else {
|
||||
s := a.Spec.GetSource()
|
||||
source = &s
|
||||
}
|
||||
} else {
|
||||
// the source count can change during the time, we cannot just trust in .status.sync
|
||||
// because if a source has been added/removed, the revisions there won't match
|
||||
// as this is only used for the UI and not internally, we can use the historical data
|
||||
// using the specific revisionId
|
||||
for _, h := range a.Status.History {
|
||||
if h.ID == versionId {
|
||||
// The iteration values are assigned to the respective iteration variables as in an assignment statement.
|
||||
// The iteration variables may be declared by the “range” clause using a form of short variable declaration (:=).
|
||||
// In this case their types are set to the types of the respective iteration values and their scope is the block of the "for" statement;
|
||||
// they are re-used in each iteration. If the iteration variables are declared outside the "for" statement,
|
||||
// after execution their values will be those of the last iteration.
|
||||
// https://golang.org/ref/spec#For_statements
|
||||
h := h
|
||||
if isRevisionMultiSource {
|
||||
source = &h.Sources[*q.SourceIndex]
|
||||
} else {
|
||||
source = &h.Source
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if source == nil {
|
||||
return nil, fmt.Errorf("revision not found: %w", err)
|
||||
source, err := getAppSourceBySourceIndexAndVersionId(a, q.SourceIndex, q.VersionId)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("error getting app source by source index and version ID: %w", err)
|
||||
}
|
||||
|
||||
repo, err := s.db.GetRepository(ctx, source.RepoURL, proj.Name)
|
||||
@@ -1585,22 +1528,9 @@ func (s *Server) RevisionChartDetails(ctx context.Context, q *application.Revisi
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var source *v1alpha1.ApplicationSource
|
||||
if a.Spec.HasMultipleSources() {
|
||||
// the source count can change during the time, we cannot just trust in .status.sync
|
||||
// because if a source has been added/removed, the revisions there won't match
|
||||
// as this is only used for the UI and not internally, we can use the historical data
|
||||
// using the specific revisionId
|
||||
for _, h := range a.Status.History {
|
||||
if h.ID == int64(*q.VersionId) {
|
||||
source = &h.Sources[*q.SourceIndex]
|
||||
}
|
||||
}
|
||||
if source == nil {
|
||||
return nil, fmt.Errorf("revision not found: %w", err)
|
||||
}
|
||||
} else {
|
||||
source = a.Spec.Source
|
||||
source, err := getAppSourceBySourceIndexAndVersionId(a, q.SourceIndex, q.VersionId)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("error getting app source by source index and version ID: %w", err)
|
||||
}
|
||||
|
||||
if source.Chart == "" {
|
||||
@@ -1622,6 +1552,76 @@ func (s *Server) RevisionChartDetails(ctx context.Context, q *application.Revisi
|
||||
})
|
||||
}
|
||||
|
||||
// getAppSourceBySourceIndexAndVersionId returns the source for a specific source index and version ID. Source index and
|
||||
// version ID are optional. If the source index is not specified, it defaults to 0. If the version ID is not specified,
|
||||
// we use the source(s) currently configured for the app. If the version ID is specified, we find the source for that
|
||||
// version ID. If the version ID is not found, we return an error. If the source index is out of bounds for whichever
|
||||
// source we choose (configured sources or sources for a specific version), we return an error.
|
||||
func getAppSourceBySourceIndexAndVersionId(a *appv1.Application, sourceIndexMaybe *int32, versionIdMaybe *int32) (appv1.ApplicationSource, error) {
|
||||
// Start with all the app's configured sources.
|
||||
sources := a.Spec.GetSources()
|
||||
|
||||
// If the user specified a version, get the sources for that version. If the version is not found, return an error.
|
||||
if versionIdMaybe != nil {
|
||||
versionId := int64(*versionIdMaybe)
|
||||
var err error
|
||||
sources, err = getSourcesByVersionId(a, versionId)
|
||||
if err != nil {
|
||||
return appv1.ApplicationSource{}, fmt.Errorf("error getting source by version ID: %w", err)
|
||||
}
|
||||
}
|
||||
|
||||
// Start by assuming we want the first source.
|
||||
sourceIndex := 0
|
||||
|
||||
// If the user specified a source index, use that instead.
|
||||
if sourceIndexMaybe != nil {
|
||||
sourceIndex = int(*sourceIndexMaybe)
|
||||
if sourceIndex >= len(sources) {
|
||||
if len(sources) == 1 {
|
||||
return appv1.ApplicationSource{}, fmt.Errorf("source index %d not found because there is only 1 source", sourceIndex)
|
||||
}
|
||||
return appv1.ApplicationSource{}, fmt.Errorf("source index %d not found because there are only %d sources", sourceIndex, len(sources))
|
||||
}
|
||||
}
|
||||
|
||||
source := sources[sourceIndex]
|
||||
|
||||
return source, nil
|
||||
}
|
||||
|
||||
// getRevisionHistoryByVersionId returns the revision history for a specific version ID.
|
||||
// If the version ID is not found, it returns an empty revision history and false.
|
||||
func getRevisionHistoryByVersionId(histories v1alpha1.RevisionHistories, versionId int64) (appv1.RevisionHistory, bool) {
|
||||
for _, h := range histories {
|
||||
if h.ID == versionId {
|
||||
return h, true
|
||||
}
|
||||
}
|
||||
return appv1.RevisionHistory{}, false
|
||||
}
|
||||
|
||||
// getSourcesByVersionId returns the sources for a specific version ID. If there is no history, it returns an error.
|
||||
// If the version ID is not found, it returns an error. If the version ID is found, and there are multiple sources,
|
||||
// it returns the sources for that version ID. If the version ID is found, and there is only one source, it returns
|
||||
// a slice with just the single source.
|
||||
func getSourcesByVersionId(a *appv1.Application, versionId int64) ([]appv1.ApplicationSource, error) {
|
||||
if len(a.Status.History) == 0 {
|
||||
return nil, fmt.Errorf("version ID %d not found because the app has no history", versionId)
|
||||
}
|
||||
|
||||
h, ok := getRevisionHistoryByVersionId(a.Status.History, versionId)
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("revision history not found for version ID %d", versionId)
|
||||
}
|
||||
|
||||
if len(h.Sources) > 0 {
|
||||
return h.Sources, nil
|
||||
}
|
||||
|
||||
return []v1alpha1.ApplicationSource{h.Source}, nil
|
||||
}
|
||||
|
||||
func isMatchingResource(q *application.ResourcesQuery, key kube.ResourceKey) bool {
|
||||
return (q.GetName() == "" || q.GetName() == key.Name) &&
|
||||
(q.GetNamespace() == "" || q.GetNamespace() == key.Namespace) &&
|
||||
@@ -1891,7 +1891,11 @@ func (s *Server) Sync(ctx context.Context, syncReq *application.ApplicationSyncR
|
||||
|
||||
s.inferResourcesStatusHealth(a)
|
||||
|
||||
if !proj.Spec.SyncWindows.Matches(a).CanSync(true) {
|
||||
canSync, err := proj.Spec.SyncWindows.Matches(a).CanSync(true)
|
||||
if err != nil {
|
||||
return a, status.Errorf(codes.PermissionDenied, "cannot sync: invalid sync window: %v", err)
|
||||
}
|
||||
if !canSync {
|
||||
return a, status.Errorf(codes.PermissionDenied, "cannot sync: blocked by sync window")
|
||||
}
|
||||
|
||||
@@ -2206,7 +2210,7 @@ func getAmbiguousRevision(app *appv1.Application, syncReq *application.Applicati
|
||||
ambiguousRevision := ""
|
||||
if app.Spec.HasMultipleSources() {
|
||||
for i, pos := range syncReq.SourcePositions {
|
||||
if pos == int64(sourceIndex) {
|
||||
if pos == int64(sourceIndex+1) {
|
||||
ambiguousRevision = syncReq.Revisions[i]
|
||||
}
|
||||
}
|
||||
@@ -2608,10 +2612,17 @@ func (s *Server) GetApplicationSyncWindows(ctx context.Context, q *application.A
|
||||
}
|
||||
|
||||
windows := proj.Spec.SyncWindows.Matches(a)
|
||||
sync := windows.CanSync(true)
|
||||
sync, err := windows.CanSync(true)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("invalid sync windows: %w", err)
|
||||
}
|
||||
|
||||
activeWindows, err := windows.Active()
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("invalid sync windows: %w", err)
|
||||
}
|
||||
res := &application.ApplicationSyncWindowsResponse{
|
||||
ActiveWindows: convertSyncWindows(windows.Active()),
|
||||
ActiveWindows: convertSyncWindows(activeWindows),
|
||||
AssignedWindows: convertSyncWindows(windows),
|
||||
CanSync: &sync,
|
||||
}
|
||||
|
||||
@@ -10,6 +10,8 @@ import (
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"k8s.io/utils/pointer"
|
||||
|
||||
"k8s.io/apimachinery/pkg/labels"
|
||||
|
||||
"github.com/argoproj/gitops-engine/pkg/health"
|
||||
@@ -2917,7 +2919,7 @@ func TestGetAmbiguousRevision_MultiSource(t *testing.T) {
|
||||
},
|
||||
}
|
||||
syncReq := &application.ApplicationSyncRequest{
|
||||
SourcePositions: []int64{0, 1},
|
||||
SourcePositions: []int64{1, 2},
|
||||
Revisions: []string{"rev1", "rev2"},
|
||||
}
|
||||
|
||||
@@ -3025,3 +3027,265 @@ func TestServer_ResolveSourceRevisions_SingleSource(t *testing.T) {
|
||||
assert.Equal(t, ([]string)(nil), sourceRevisions)
|
||||
assert.Equal(t, ([]string)(nil), displayRevisions)
|
||||
}
|
||||
|
||||
func Test_RevisionMetadata(t *testing.T) {
|
||||
singleSourceApp := newTestApp()
|
||||
singleSourceApp.Name = "single-source-app"
|
||||
singleSourceApp.Spec = appv1.ApplicationSpec{
|
||||
Source: &appv1.ApplicationSource{
|
||||
RepoURL: "https://github.com/argoproj/argocd-example-apps.git",
|
||||
Path: "helm-guestbook",
|
||||
TargetRevision: "HEAD",
|
||||
},
|
||||
}
|
||||
|
||||
multiSourceApp := newTestApp()
|
||||
multiSourceApp.Name = "multi-source-app"
|
||||
multiSourceApp.Spec = appv1.ApplicationSpec{
|
||||
Sources: []appv1.ApplicationSource{
|
||||
{
|
||||
RepoURL: "https://github.com/argoproj/argocd-example-apps.git",
|
||||
Path: "helm-guestbook",
|
||||
TargetRevision: "HEAD",
|
||||
},
|
||||
{
|
||||
RepoURL: "https://github.com/argoproj/argocd-example-apps.git",
|
||||
Path: "kustomize-guestbook",
|
||||
TargetRevision: "HEAD",
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
singleSourceHistory := []appv1.RevisionHistory{
|
||||
{
|
||||
ID: 1,
|
||||
Source: singleSourceApp.Spec.GetSource(),
|
||||
Revision: "a",
|
||||
},
|
||||
}
|
||||
multiSourceHistory := []appv1.RevisionHistory{
|
||||
{
|
||||
ID: 1,
|
||||
Sources: multiSourceApp.Spec.GetSources(),
|
||||
Revisions: []string{"a", "b"},
|
||||
},
|
||||
}
|
||||
|
||||
testCases := []struct {
|
||||
name string
|
||||
multiSource bool
|
||||
history *struct {
|
||||
matchesSourceType bool
|
||||
}
|
||||
sourceIndex *int32
|
||||
versionId *int32
|
||||
expectErrorContains *string
|
||||
}{
|
||||
{
|
||||
name: "single-source app without history, no source index, no version ID",
|
||||
multiSource: false,
|
||||
},
|
||||
{
|
||||
name: "single-source app without history, no source index, missing version ID",
|
||||
multiSource: false,
|
||||
versionId: pointer.Int32(999),
|
||||
expectErrorContains: pointer.String("the app has no history"),
|
||||
},
|
||||
{
|
||||
name: "single source app without history, present source index, no version ID",
|
||||
multiSource: false,
|
||||
sourceIndex: pointer.Int32(0),
|
||||
},
|
||||
{
|
||||
name: "single source app without history, invalid source index, no version ID",
|
||||
multiSource: false,
|
||||
sourceIndex: pointer.Int32(999),
|
||||
expectErrorContains: pointer.String("source index 999 not found"),
|
||||
},
|
||||
{
|
||||
name: "single source app with matching history, no source index, no version ID",
|
||||
multiSource: false,
|
||||
history: &struct{ matchesSourceType bool }{true},
|
||||
},
|
||||
{
|
||||
name: "single source app with matching history, no source index, missing version ID",
|
||||
multiSource: false,
|
||||
history: &struct{ matchesSourceType bool }{true},
|
||||
versionId: pointer.Int32(999),
|
||||
expectErrorContains: pointer.String("history not found for version ID 999"),
|
||||
},
|
||||
{
|
||||
name: "single source app with matching history, no source index, present version ID",
|
||||
multiSource: false,
|
||||
history: &struct{ matchesSourceType bool }{true},
|
||||
versionId: pointer.Int32(1),
|
||||
},
|
||||
{
|
||||
name: "single source app with multi-source history, no source index, no version ID",
|
||||
multiSource: false,
|
||||
history: &struct{ matchesSourceType bool }{false},
|
||||
},
|
||||
{
|
||||
name: "single source app with multi-source history, no source index, missing version ID",
|
||||
multiSource: false,
|
||||
history: &struct{ matchesSourceType bool }{false},
|
||||
versionId: pointer.Int32(999),
|
||||
expectErrorContains: pointer.String("history not found for version ID 999"),
|
||||
},
|
||||
{
|
||||
name: "single source app with multi-source history, no source index, present version ID",
|
||||
multiSource: false,
|
||||
history: &struct{ matchesSourceType bool }{false},
|
||||
versionId: pointer.Int32(1),
|
||||
},
|
||||
{
|
||||
name: "single-source app with multi-source history, source index 1, no version ID",
|
||||
multiSource: false,
|
||||
sourceIndex: pointer.Int32(1),
|
||||
history: &struct{ matchesSourceType bool }{false},
|
||||
// Since the user requested source index 1, but no version ID, we'll get an error when looking at the live
|
||||
// source, because the live source is single-source.
|
||||
expectErrorContains: pointer.String("there is only 1 source"),
|
||||
},
|
||||
{
|
||||
name: "single-source app with multi-source history, invalid source index, no version ID",
|
||||
multiSource: false,
|
||||
sourceIndex: pointer.Int32(999),
|
||||
history: &struct{ matchesSourceType bool }{false},
|
||||
expectErrorContains: pointer.String("source index 999 not found"),
|
||||
},
|
||||
{
|
||||
name: "single-source app with multi-source history, valid source index, present version ID",
|
||||
multiSource: false,
|
||||
sourceIndex: pointer.Int32(1),
|
||||
history: &struct{ matchesSourceType bool }{false},
|
||||
versionId: pointer.Int32(1),
|
||||
},
|
||||
{
|
||||
name: "multi-source app without history, no source index, no version ID",
|
||||
multiSource: true,
|
||||
},
|
||||
{
|
||||
name: "multi-source app without history, no source index, missing version ID",
|
||||
multiSource: true,
|
||||
versionId: pointer.Int32(999),
|
||||
expectErrorContains: pointer.String("the app has no history"),
|
||||
},
|
||||
{
|
||||
name: "multi-source app without history, present source index, no version ID",
|
||||
multiSource: true,
|
||||
sourceIndex: pointer.Int32(1),
|
||||
},
|
||||
{
|
||||
name: "multi-source app without history, invalid source index, no version ID",
|
||||
multiSource: true,
|
||||
sourceIndex: pointer.Int32(999),
|
||||
expectErrorContains: pointer.String("source index 999 not found"),
|
||||
},
|
||||
{
|
||||
name: "multi-source app with matching history, no source index, no version ID",
|
||||
multiSource: true,
|
||||
history: &struct{ matchesSourceType bool }{true},
|
||||
},
|
||||
{
|
||||
name: "multi-source app with matching history, no source index, missing version ID",
|
||||
multiSource: true,
|
||||
history: &struct{ matchesSourceType bool }{true},
|
||||
versionId: pointer.Int32(999),
|
||||
expectErrorContains: pointer.String("history not found for version ID 999"),
|
||||
},
|
||||
{
|
||||
name: "multi-source app with matching history, no source index, present version ID",
|
||||
multiSource: true,
|
||||
history: &struct{ matchesSourceType bool }{true},
|
||||
versionId: pointer.Int32(1),
|
||||
},
|
||||
{
|
||||
name: "multi-source app with single-source history, no source index, no version ID",
|
||||
multiSource: true,
|
||||
history: &struct{ matchesSourceType bool }{false},
|
||||
},
|
||||
{
|
||||
name: "multi-source app with single-source history, no source index, missing version ID",
|
||||
multiSource: true,
|
||||
history: &struct{ matchesSourceType bool }{false},
|
||||
versionId: pointer.Int32(999),
|
||||
expectErrorContains: pointer.String("history not found for version ID 999"),
|
||||
},
|
||||
{
|
||||
name: "multi-source app with single-source history, no source index, present version ID",
|
||||
multiSource: true,
|
||||
history: &struct{ matchesSourceType bool }{false},
|
||||
versionId: pointer.Int32(1),
|
||||
},
|
||||
{
|
||||
name: "multi-source app with single-source history, source index 1, no version ID",
|
||||
multiSource: true,
|
||||
sourceIndex: pointer.Int32(1),
|
||||
history: &struct{ matchesSourceType bool }{false},
|
||||
},
|
||||
{
|
||||
name: "multi-source app with single-source history, invalid source index, no version ID",
|
||||
multiSource: true,
|
||||
sourceIndex: pointer.Int32(999),
|
||||
history: &struct{ matchesSourceType bool }{false},
|
||||
expectErrorContains: pointer.String("source index 999 not found"),
|
||||
},
|
||||
{
|
||||
name: "multi-source app with single-source history, valid source index, present version ID",
|
||||
multiSource: true,
|
||||
sourceIndex: pointer.Int32(0),
|
||||
history: &struct{ matchesSourceType bool }{false},
|
||||
versionId: pointer.Int32(1),
|
||||
},
|
||||
{
|
||||
name: "multi-source app with single-source history, source index 1, present version ID",
|
||||
multiSource: true,
|
||||
sourceIndex: pointer.Int32(1),
|
||||
history: &struct{ matchesSourceType bool }{false},
|
||||
versionId: pointer.Int32(1),
|
||||
expectErrorContains: pointer.String("source index 1 not found"),
|
||||
},
|
||||
}
|
||||
|
||||
for _, tc := range testCases {
|
||||
tcc := tc
|
||||
t.Run(tcc.name, func(t *testing.T) {
|
||||
app := singleSourceApp
|
||||
if tcc.multiSource {
|
||||
app = multiSourceApp
|
||||
}
|
||||
if tcc.history != nil {
|
||||
if tcc.history.matchesSourceType {
|
||||
if tcc.multiSource {
|
||||
app.Status.History = multiSourceHistory
|
||||
} else {
|
||||
app.Status.History = singleSourceHistory
|
||||
}
|
||||
} else {
|
||||
if tcc.multiSource {
|
||||
app.Status.History = singleSourceHistory
|
||||
} else {
|
||||
app.Status.History = multiSourceHistory
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
s := newTestAppServer(t, app)
|
||||
|
||||
request := &application.RevisionMetadataQuery{
|
||||
Name: pointer.String(app.Name),
|
||||
Revision: pointer.String("HEAD"),
|
||||
SourceIndex: tcc.sourceIndex,
|
||||
VersionId: tcc.versionId,
|
||||
}
|
||||
|
||||
_, err := s.RevisionMetadata(context.Background(), request)
|
||||
if tcc.expectErrorContains != nil {
|
||||
require.ErrorContains(t, err, *tcc.expectErrorContains)
|
||||
} else {
|
||||
require.NoError(t, err)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@@ -120,16 +120,22 @@ func mergeLogStreams(streams []chan logEntry, bufferingDuration time.Duration) c
|
||||
var sentAt time.Time
|
||||
|
||||
ticker := time.NewTicker(bufferingDuration)
|
||||
done := make(chan struct{})
|
||||
go func() {
|
||||
for range ticker.C {
|
||||
sentAtLock.Lock()
|
||||
// waited long enough for logs from each streams, send everything accumulated
|
||||
if sentAt.Add(bufferingDuration).Before(time.Now()) {
|
||||
_ = send(true)
|
||||
sentAt = time.Now()
|
||||
}
|
||||
for {
|
||||
select {
|
||||
case <-done:
|
||||
return
|
||||
case <-ticker.C:
|
||||
sentAtLock.Lock()
|
||||
// waited long enough for logs from each streams, send everything accumulated
|
||||
if sentAt.Add(bufferingDuration).Before(time.Now()) {
|
||||
_ = send(true)
|
||||
sentAt = time.Now()
|
||||
}
|
||||
|
||||
sentAtLock.Unlock()
|
||||
sentAtLock.Unlock()
|
||||
}
|
||||
}
|
||||
}()
|
||||
|
||||
@@ -144,8 +150,13 @@ func mergeLogStreams(streams []chan logEntry, bufferingDuration time.Duration) c
|
||||
|
||||
_ = send(true)
|
||||
|
||||
close(merged)
|
||||
ticker.Stop()
|
||||
// ticker.Stop() does not close the channel, and it does not wait for the channel to be drained. So we need to
|
||||
// explicitly prevent the gorountine from leaking by closing the channel. We also need to prevent the goroutine
|
||||
// from calling `send` again, because `send` pushes to the `merged` channel which we're about to close.
|
||||
// This describes the approach nicely: https://stackoverflow.com/questions/17797754/ticker-stop-behaviour-in-golang
|
||||
done <- struct{}{}
|
||||
close(merged)
|
||||
}()
|
||||
return merged
|
||||
}
|
||||
|
||||
@@ -75,3 +75,33 @@ func TestMergeLogStreams(t *testing.T) {
|
||||
|
||||
assert.Equal(t, []string{"1", "2", "3", "4"}, lines)
|
||||
}
|
||||
|
||||
func TestMergeLogStreams_RaceCondition(t *testing.T) {
|
||||
// Test for regression of this issue: https://github.com/argoproj/argo-cd/issues/7006
|
||||
for i := 0; i < 5000; i++ {
|
||||
first := make(chan logEntry)
|
||||
second := make(chan logEntry)
|
||||
|
||||
go func() {
|
||||
parseLogsStream("first", io.NopCloser(strings.NewReader(`2021-02-09T00:00:01Z 1`)), first)
|
||||
time.Sleep(time.Duration(i%3) * time.Millisecond)
|
||||
close(first)
|
||||
}()
|
||||
|
||||
go func() {
|
||||
parseLogsStream("second", io.NopCloser(strings.NewReader(`2021-02-09T00:00:02Z 2`)), second)
|
||||
time.Sleep(time.Duration((i+1)%3) * time.Millisecond)
|
||||
close(second)
|
||||
}()
|
||||
|
||||
merged := mergeLogStreams([]chan logEntry{first, second}, 1*time.Millisecond)
|
||||
|
||||
// Drain the channel
|
||||
for range merged {
|
||||
}
|
||||
|
||||
// This test intentionally doesn't test the order of the output. Under these intense conditions, the test would
|
||||
// fail often due to out of order entries. This test is only meant to reproduce a race between a channel writer
|
||||
// and channel closer.
|
||||
}
|
||||
}
|
||||
|
||||
@@ -229,7 +229,7 @@ func (s *terminalHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
|
||||
|
||||
fieldLog.Info("terminal session starting")
|
||||
|
||||
session, err := newTerminalSession(w, r, nil, s.sessionManager)
|
||||
session, err := newTerminalSession(ctx, w, r, nil, s.sessionManager, appRBACName, s.enf)
|
||||
if err != nil {
|
||||
http.Error(w, "Failed to start terminal session", http.StatusBadRequest)
|
||||
return
|
||||
|
||||
@@ -1,12 +1,16 @@
|
||||
package application
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/argoproj/argo-cd/v2/server/rbacpolicy"
|
||||
"github.com/argoproj/argo-cd/v2/util/rbac"
|
||||
|
||||
"github.com/argoproj/argo-cd/v2/common"
|
||||
httputil "github.com/argoproj/argo-cd/v2/util/http"
|
||||
util_session "github.com/argoproj/argo-cd/v2/util/session"
|
||||
@@ -32,6 +36,7 @@ var upgrader = func() websocket.Upgrader {
|
||||
|
||||
// terminalSession implements PtyHandler
|
||||
type terminalSession struct {
|
||||
ctx context.Context
|
||||
wsConn *websocket.Conn
|
||||
sizeChan chan remotecommand.TerminalSize
|
||||
doneChan chan struct{}
|
||||
@@ -40,6 +45,8 @@ type terminalSession struct {
|
||||
writeLock sync.Mutex
|
||||
sessionManager *util_session.SessionManager
|
||||
token *string
|
||||
appRBACName string
|
||||
enf *rbac.Enforcer
|
||||
}
|
||||
|
||||
// getToken get auth token from web socket request
|
||||
@@ -49,7 +56,7 @@ func getToken(r *http.Request) (string, error) {
|
||||
}
|
||||
|
||||
// newTerminalSession create terminalSession
|
||||
func newTerminalSession(w http.ResponseWriter, r *http.Request, responseHeader http.Header, sessionManager *util_session.SessionManager) (*terminalSession, error) {
|
||||
func newTerminalSession(ctx context.Context, w http.ResponseWriter, r *http.Request, responseHeader http.Header, sessionManager *util_session.SessionManager, appRBACName string, enf *rbac.Enforcer) (*terminalSession, error) {
|
||||
token, err := getToken(r)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
@@ -60,12 +67,15 @@ func newTerminalSession(w http.ResponseWriter, r *http.Request, responseHeader h
|
||||
return nil, err
|
||||
}
|
||||
session := &terminalSession{
|
||||
ctx: ctx,
|
||||
wsConn: conn,
|
||||
tty: true,
|
||||
sizeChan: make(chan remotecommand.TerminalSize),
|
||||
doneChan: make(chan struct{}),
|
||||
sessionManager: sessionManager,
|
||||
token: &token,
|
||||
appRBACName: appRBACName,
|
||||
enf: enf,
|
||||
}
|
||||
return session, nil
|
||||
}
|
||||
@@ -126,6 +136,29 @@ func (t *terminalSession) reconnect() (int, error) {
|
||||
return 0, nil
|
||||
}
|
||||
|
||||
func (t *terminalSession) validatePermissions(p []byte) (int, error) {
|
||||
permissionDeniedMessage, _ := json.Marshal(TerminalMessage{
|
||||
Operation: "stdout",
|
||||
Data: "Permission denied",
|
||||
})
|
||||
if err := t.enf.EnforceErr(t.ctx.Value("claims"), rbacpolicy.ResourceApplications, rbacpolicy.ActionGet, t.appRBACName); err != nil {
|
||||
err = t.wsConn.WriteMessage(websocket.TextMessage, permissionDeniedMessage)
|
||||
if err != nil {
|
||||
log.Errorf("permission denied message err: %v", err)
|
||||
}
|
||||
return copy(p, EndOfTransmission), permissionDeniedErr
|
||||
}
|
||||
|
||||
if err := t.enf.EnforceErr(t.ctx.Value("claims"), rbacpolicy.ResourceExec, rbacpolicy.ActionCreate, t.appRBACName); err != nil {
|
||||
err = t.wsConn.WriteMessage(websocket.TextMessage, permissionDeniedMessage)
|
||||
if err != nil {
|
||||
log.Errorf("permission denied message err: %v", err)
|
||||
}
|
||||
return copy(p, EndOfTransmission), permissionDeniedErr
|
||||
}
|
||||
return 0, nil
|
||||
}
|
||||
|
||||
// Read called in a loop from remotecommand as long as the process is running
|
||||
func (t *terminalSession) Read(p []byte) (int, error) {
|
||||
// check if token still valid
|
||||
@@ -136,6 +169,12 @@ func (t *terminalSession) Read(p []byte) (int, error) {
|
||||
return t.reconnect()
|
||||
}
|
||||
|
||||
// validate permissions
|
||||
code, err := t.validatePermissions(p)
|
||||
if err != nil {
|
||||
return code, err
|
||||
}
|
||||
|
||||
t.readLock.Lock()
|
||||
_, message, err := t.wsConn.ReadMessage()
|
||||
t.readLock.Unlock()
|
||||
|
||||
@@ -1,25 +1,65 @@
|
||||
package application
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
v1 "k8s.io/api/core/v1"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/client-go/kubernetes/fake"
|
||||
|
||||
"github.com/argoproj/argo-cd/v2/common"
|
||||
"github.com/argoproj/argo-cd/v2/util/assets"
|
||||
"github.com/argoproj/argo-cd/v2/util/rbac"
|
||||
|
||||
"github.com/golang-jwt/jwt/v4"
|
||||
"github.com/gorilla/websocket"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func reconnect(w http.ResponseWriter, r *http.Request) {
|
||||
func newTestTerminalSession(w http.ResponseWriter, r *http.Request) terminalSession {
|
||||
upgrader := websocket.Upgrader{}
|
||||
c, err := upgrader.Upgrade(w, r, nil)
|
||||
if err != nil {
|
||||
return
|
||||
return terminalSession{}
|
||||
}
|
||||
|
||||
ts := terminalSession{wsConn: c}
|
||||
return terminalSession{wsConn: c}
|
||||
}
|
||||
|
||||
func newEnforcer() *rbac.Enforcer {
|
||||
additionalConfig := make(map[string]string, 0)
|
||||
kubeclientset := fake.NewSimpleClientset(&v1.ConfigMap{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Namespace: testNamespace,
|
||||
Name: "argocd-cm",
|
||||
Labels: map[string]string{
|
||||
"app.kubernetes.io/part-of": "argocd",
|
||||
},
|
||||
},
|
||||
Data: additionalConfig,
|
||||
}, &v1.Secret{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "argocd-secret",
|
||||
Namespace: testNamespace,
|
||||
},
|
||||
Data: map[string][]byte{
|
||||
"admin.password": []byte("test"),
|
||||
"server.secretkey": []byte("test"),
|
||||
},
|
||||
})
|
||||
|
||||
enforcer := rbac.NewEnforcer(kubeclientset, testNamespace, common.ArgoCDRBACConfigMapName, nil)
|
||||
return enforcer
|
||||
}
|
||||
|
||||
func reconnect(w http.ResponseWriter, r *http.Request) {
|
||||
ts := newTestTerminalSession(w, r)
|
||||
_, _ = ts.reconnect()
|
||||
}
|
||||
|
||||
@@ -44,3 +84,71 @@ func TestReconnect(t *testing.T) {
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, ReconnectMessage, message.Data)
|
||||
}
|
||||
|
||||
func TestValidateWithAdminPermissions(t *testing.T) {
|
||||
validate := func(w http.ResponseWriter, r *http.Request) {
|
||||
enf := newEnforcer()
|
||||
_ = enf.SetBuiltinPolicy(assets.BuiltinPolicyCSV)
|
||||
enf.SetDefaultRole("role:admin")
|
||||
enf.SetClaimsEnforcerFunc(func(claims jwt.Claims, rvals ...interface{}) bool {
|
||||
return true
|
||||
})
|
||||
ts := newTestTerminalSession(w, r)
|
||||
ts.enf = enf
|
||||
ts.appRBACName = "test"
|
||||
// nolint:staticcheck
|
||||
ts.ctx = context.WithValue(context.Background(), "claims", &jwt.MapClaims{"groups": []string{"admin"}})
|
||||
_, err := ts.validatePermissions([]byte{})
|
||||
require.NoError(t, err)
|
||||
}
|
||||
|
||||
s := httptest.NewServer(http.HandlerFunc(validate))
|
||||
defer s.Close()
|
||||
|
||||
u := "ws" + strings.TrimPrefix(s.URL, "http")
|
||||
|
||||
// Connect to the server
|
||||
ws, _, err := websocket.DefaultDialer.Dial(u, nil)
|
||||
require.NoError(t, err)
|
||||
|
||||
defer ws.Close()
|
||||
}
|
||||
|
||||
func TestValidateWithoutPermissions(t *testing.T) {
|
||||
validate := func(w http.ResponseWriter, r *http.Request) {
|
||||
enf := newEnforcer()
|
||||
_ = enf.SetBuiltinPolicy(assets.BuiltinPolicyCSV)
|
||||
enf.SetDefaultRole("role:test")
|
||||
enf.SetClaimsEnforcerFunc(func(claims jwt.Claims, rvals ...interface{}) bool {
|
||||
return false
|
||||
})
|
||||
ts := newTestTerminalSession(w, r)
|
||||
ts.enf = enf
|
||||
ts.appRBACName = "test"
|
||||
// nolint:staticcheck
|
||||
ts.ctx = context.WithValue(context.Background(), "claims", &jwt.MapClaims{"groups": []string{"test"}})
|
||||
_, err := ts.validatePermissions([]byte{})
|
||||
require.Error(t, err)
|
||||
assert.Equal(t, permissionDeniedErr.Error(), err.Error())
|
||||
}
|
||||
|
||||
s := httptest.NewServer(http.HandlerFunc(validate))
|
||||
defer s.Close()
|
||||
|
||||
u := "ws" + strings.TrimPrefix(s.URL, "http")
|
||||
|
||||
// Connect to the server
|
||||
ws, _, err := websocket.DefaultDialer.Dial(u, nil)
|
||||
require.NoError(t, err)
|
||||
|
||||
defer ws.Close()
|
||||
|
||||
_, p, _ := ws.ReadMessage()
|
||||
|
||||
var message TerminalMessage
|
||||
|
||||
err = json.Unmarshal(p, &message)
|
||||
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, "Permission denied", message.Data)
|
||||
}
|
||||
|
||||
@@ -525,7 +525,10 @@ func (s *Server) GetSyncWindowsState(ctx context.Context, q *project.SyncWindows
|
||||
|
||||
res := &project.SyncWindowsResponse{}
|
||||
|
||||
windows := proj.Spec.SyncWindows.Active()
|
||||
windows, err := proj.Spec.SyncWindows.Active()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if windows.HasWindows() {
|
||||
res.Windows = *windows
|
||||
} else {
|
||||
|
||||
@@ -188,17 +188,20 @@ func (s *Server) ListRepositories(ctx context.Context, q *repositorypkg.RepoQuer
|
||||
}
|
||||
// remove secrets
|
||||
items = append(items, &appsv1.Repository{
|
||||
Repo: repo.Repo,
|
||||
Type: rType,
|
||||
Name: repo.Name,
|
||||
Username: repo.Username,
|
||||
Insecure: repo.IsInsecure(),
|
||||
EnableLFS: repo.EnableLFS,
|
||||
EnableOCI: repo.EnableOCI,
|
||||
Proxy: repo.Proxy,
|
||||
Project: repo.Project,
|
||||
ForceHttpBasicAuth: repo.ForceHttpBasicAuth,
|
||||
InheritedCreds: repo.InheritedCreds,
|
||||
Repo: repo.Repo,
|
||||
Type: rType,
|
||||
Name: repo.Name,
|
||||
Username: repo.Username,
|
||||
Insecure: repo.IsInsecure(),
|
||||
EnableLFS: repo.EnableLFS,
|
||||
EnableOCI: repo.EnableOCI,
|
||||
Proxy: repo.Proxy,
|
||||
Project: repo.Project,
|
||||
ForceHttpBasicAuth: repo.ForceHttpBasicAuth,
|
||||
InheritedCreds: repo.InheritedCreds,
|
||||
GithubAppId: repo.GithubAppId,
|
||||
GithubAppInstallationId: repo.GithubAppInstallationId,
|
||||
GitHubAppEnterpriseBaseURL: repo.GitHubAppEnterpriseBaseURL,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@@ -21,6 +21,7 @@ import (
|
||||
|
||||
"github.com/argoproj/argo-cd/v2/common"
|
||||
"github.com/argoproj/argo-cd/v2/pkg/apiclient/repository"
|
||||
repositorypkg "github.com/argoproj/argo-cd/v2/pkg/apiclient/repository"
|
||||
appsv1 "github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1"
|
||||
fakeapps "github.com/argoproj/argo-cd/v2/pkg/client/clientset/versioned/fake"
|
||||
appinformer "github.com/argoproj/argo-cd/v2/pkg/client/informers/externalversions"
|
||||
@@ -1057,3 +1058,35 @@ func TestGetRepository(t *testing.T) {
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestDeleteRepository(t *testing.T) {
|
||||
repositories := map[string]string{
|
||||
"valid": "https://bitbucket.org/workspace/repo.git",
|
||||
// Check a wrongly formatter repo as well, see https://github.com/argoproj/argo-cd/issues/20921
|
||||
"invalid": "git clone https://bitbucket.org/workspace/repo.git",
|
||||
}
|
||||
|
||||
kubeclientset := fake.NewSimpleClientset(&argocdCM, &argocdSecret)
|
||||
settingsMgr := settings.NewSettingsManager(context.Background(), kubeclientset, testNamespace)
|
||||
|
||||
for name, repo := range repositories {
|
||||
t.Run(name, func(t *testing.T) {
|
||||
repoServerClient := mocks.RepoServerServiceClient{}
|
||||
repoServerClient.On("TestRepository", mock.Anything, mock.Anything).Return(&apiclient.TestRepositoryResponse{}, nil)
|
||||
|
||||
repoServerClientset := mocks.Clientset{RepoServerServiceClient: &repoServerClient}
|
||||
enforcer := newEnforcer(kubeclientset)
|
||||
|
||||
db := &dbmocks.ArgoDB{}
|
||||
db.On("DeleteRepository", context.TODO(), repo, "default").Return(nil)
|
||||
db.On("ListRepositories", context.TODO()).Return([]*appsv1.Repository{{Repo: repo, Project: "default"}}, nil)
|
||||
db.On("GetRepository", context.TODO(), repo, "default").Return(&appsv1.Repository{Repo: repo, Project: "default"}, nil)
|
||||
appLister, projLister := newAppAndProjLister(defaultProj)
|
||||
|
||||
s := NewServer(&repoServerClientset, db, enforcer, newFixtures().Cache, appLister, projLister, testNamespace, settingsMgr)
|
||||
resp, err := s.DeleteRepository(context.TODO(), &repository.RepoQuery{Repo: repo, AppProject: "default"})
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, repositorypkg.RepoResponse{}, *resp)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1049,7 +1049,7 @@ func (a *ArgoCDServer) newHTTPServer(ctx context.Context, port int, grpcWebHandl
|
||||
|
||||
// Webhook handler for git events (Note: cache timeouts are hardcoded because API server does not write to cache and not really using them)
|
||||
argoDB := db.NewDB(a.Namespace, a.settingsMgr, a.KubeClientset)
|
||||
acdWebhookHandler := webhook.NewHandler(a.Namespace, a.ArgoCDServerOpts.ApplicationNamespaces, a.AppClientset, a.settings, a.settingsMgr, a.RepoServerCache, a.Cache, argoDB)
|
||||
acdWebhookHandler := webhook.NewHandler(a.Namespace, a.ArgoCDServerOpts.ApplicationNamespaces, a.AppClientset, a.settings, a.settingsMgr, a.RepoServerCache, a.Cache, argoDB, a.settingsMgr.GetMaxWebhookPayloadSize())
|
||||
|
||||
mux.HandleFunc("/api/webhook", acdWebhookHandler.Handler)
|
||||
|
||||
|
||||
@@ -42,6 +42,7 @@ message Settings {
|
||||
bool execEnabled = 22;
|
||||
string controllerNamespace = 23;
|
||||
bool appsInAnyNamespaceEnabled = 24;
|
||||
string installationID = 26;
|
||||
}
|
||||
|
||||
message GoogleAnalyticsConfig {
|
||||
|
||||
@@ -2880,3 +2880,49 @@ func TestAnnotationTrackingExtraResources(t *testing.T) {
|
||||
Expect(SyncStatusIs(SyncStatusCodeSynced)).
|
||||
Expect(HealthIs(health.HealthStatusHealthy))
|
||||
}
|
||||
|
||||
func TestInstallationID(t *testing.T) {
|
||||
ctx := Given(t)
|
||||
ctx.
|
||||
SetTrackingMethod(string(argo.TrackingMethodAnnotation)).
|
||||
And(func() {
|
||||
_, err := fixture.KubeClientset.CoreV1().ConfigMaps(DeploymentNamespace()).Create(
|
||||
context.Background(), &v1.ConfigMap{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "test-configmap",
|
||||
Annotations: map[string]string{
|
||||
common.AnnotationKeyAppInstance: fmt.Sprintf("%s:/ConfigMap:%s/test-configmap", ctx.AppName(), DeploymentNamespace()),
|
||||
},
|
||||
},
|
||||
}, metav1.CreateOptions{})
|
||||
require.NoError(t, err)
|
||||
}).
|
||||
Path(guestbookPath).
|
||||
Prune(false).
|
||||
When().IgnoreErrors().CreateApp().Sync().
|
||||
Then().Expect(OperationPhaseIs(OperationSucceeded)).Expect(SyncStatusIs(SyncStatusCodeOutOfSync)).
|
||||
And(func(app *Application) {
|
||||
var cm *ResourceStatus
|
||||
for i := range app.Status.Resources {
|
||||
if app.Status.Resources[i].Kind == "ConfigMap" && app.Status.Resources[i].Name == "test-configmap" {
|
||||
cm = &app.Status.Resources[i]
|
||||
break
|
||||
}
|
||||
}
|
||||
require.NotNil(t, cm)
|
||||
assert.Equal(t, SyncStatusCodeOutOfSync, cm.Status)
|
||||
}).
|
||||
When().SetInstallationID("test").Sync().
|
||||
Then().
|
||||
Expect(SyncStatusIs(SyncStatusCodeSynced)).
|
||||
And(func(app *Application) {
|
||||
require.Len(t, app.Status.Resources, 2)
|
||||
svc, err := fixture.KubeClientset.CoreV1().Services(DeploymentNamespace()).Get(context.Background(), "guestbook-ui", metav1.GetOptions{})
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, "test", svc.Annotations[common.AnnotationInstallationID])
|
||||
|
||||
deploy, err := fixture.KubeClientset.AppsV1().Deployments(DeploymentNamespace()).Get(context.Background(), "guestbook-ui", metav1.GetOptions{})
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, "test", deploy.Annotations[common.AnnotationInstallationID])
|
||||
})
|
||||
}
|
||||
|
||||
@@ -522,101 +522,6 @@ func TestSimpleListGeneratorGoTemplate(t *testing.T) {
|
||||
Delete().Then().Expect(ApplicationsDoNotExist([]argov1alpha1.Application{*expectedAppNewMetadata}))
|
||||
}
|
||||
|
||||
func TestCreateApplicationDespiteParamsError(t *testing.T) {
|
||||
expectedErrorMessage := `failed to execute go template {{.cluster}}-guestbook: template: :1:2: executing "" at <.cluster>: map has no entry for key "cluster"`
|
||||
expectedConditionsParamsError := []v1alpha1.ApplicationSetCondition{
|
||||
{
|
||||
Type: v1alpha1.ApplicationSetConditionErrorOccurred,
|
||||
Status: v1alpha1.ApplicationSetConditionStatusTrue,
|
||||
Message: expectedErrorMessage,
|
||||
Reason: v1alpha1.ApplicationSetReasonRenderTemplateParamsError,
|
||||
},
|
||||
{
|
||||
Type: v1alpha1.ApplicationSetConditionParametersGenerated,
|
||||
Status: v1alpha1.ApplicationSetConditionStatusFalse,
|
||||
Message: expectedErrorMessage,
|
||||
Reason: v1alpha1.ApplicationSetReasonErrorOccurred,
|
||||
},
|
||||
{
|
||||
Type: v1alpha1.ApplicationSetConditionResourcesUpToDate,
|
||||
Status: v1alpha1.ApplicationSetConditionStatusFalse,
|
||||
Message: expectedErrorMessage,
|
||||
Reason: v1alpha1.ApplicationSetReasonRenderTemplateParamsError,
|
||||
},
|
||||
}
|
||||
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: "guestbook",
|
||||
},
|
||||
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: "simple-list-generator",
|
||||
},
|
||||
Spec: v1alpha1.ApplicationSetSpec{
|
||||
GoTemplate: true,
|
||||
GoTemplateOptions: []string{"missingkey=error"},
|
||||
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: "guestbook",
|
||||
},
|
||||
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"}`),
|
||||
},
|
||||
{
|
||||
Raw: []byte(`{"invalidCluster": "invalid-cluster","url": "https://kubernetes.default.svc"}`),
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}).Then().Expect(ApplicationsExist([]argov1alpha1.Application{expectedApp})).
|
||||
|
||||
// verify the ApplicationSet status conditions were set correctly
|
||||
Expect(ApplicationSetHasConditions("simple-list-generator", expectedConditionsParamsError)).
|
||||
|
||||
// Delete the ApplicationSet, and verify it deletes the Applications
|
||||
When().
|
||||
Delete().Then().Expect(ApplicationsDoNotExist([]argov1alpha1.Application{expectedApp}))
|
||||
}
|
||||
|
||||
func TestRenderHelmValuesObject(t *testing.T) {
|
||||
expectedApp := argov1alpha1.Application{
|
||||
TypeMeta: metav1.TypeMeta{
|
||||
@@ -1241,7 +1146,6 @@ func TestSimpleGitDirectoryGenerator(t *testing.T) {
|
||||
expectedApps := []argov1alpha1.Application{
|
||||
generateExpectedApp("kustomize-guestbook"),
|
||||
generateExpectedApp("helm-guestbook"),
|
||||
generateExpectedApp("ksonnet-guestbook"),
|
||||
}
|
||||
|
||||
var expectedAppsNewNamespace []argov1alpha1.Application
|
||||
@@ -1351,7 +1255,6 @@ func TestSimpleGitDirectoryGeneratorGoTemplate(t *testing.T) {
|
||||
expectedApps := []argov1alpha1.Application{
|
||||
generateExpectedApp("kustomize-guestbook"),
|
||||
generateExpectedApp("helm-guestbook"),
|
||||
generateExpectedApp("ksonnet-guestbook"),
|
||||
}
|
||||
|
||||
var expectedAppsNewNamespace []argov1alpha1.Application
|
||||
|
||||
@@ -469,6 +469,11 @@ func (a *Actions) SetTrackingMethod(trackingMethod string) *Actions {
|
||||
return a
|
||||
}
|
||||
|
||||
func (a *Actions) SetInstallationID(installationID string) *Actions {
|
||||
fixture.SetInstallationID(installationID)
|
||||
return a
|
||||
}
|
||||
|
||||
func (a *Actions) SetTrackingLabel(trackingLabel string) *Actions {
|
||||
fixture.SetTrackingLabel(trackingLabel)
|
||||
return a
|
||||
|
||||
@@ -362,6 +362,11 @@ func (c *Context) SetTrackingMethod(trackingMethod string) *Context {
|
||||
return c
|
||||
}
|
||||
|
||||
func (c *Context) SetInstallationID(installationID string) *Context {
|
||||
fixture.SetTrackingMethod(installationID)
|
||||
return c
|
||||
}
|
||||
|
||||
func (c *Context) GetTrackingMethod() v1alpha1.TrackingMethod {
|
||||
return c.trackingMethod
|
||||
}
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user