mirror of
https://github.com/argoproj/argo-cd.git
synced 2026-03-15 04:48:46 +01:00
Compare commits
23 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
4dab5bd6a6 | ||
|
|
358930be06 | ||
|
|
68f63e7a2b | ||
|
|
85be9a4f22 | ||
|
|
53bb2e4109 | ||
|
|
6e6857ee8b | ||
|
|
5f5fb0d2de | ||
|
|
e2eb54c102 | ||
|
|
3ff57d288d | ||
|
|
a05b042876 | ||
|
|
50271e10c0 | ||
|
|
ee4f09ebd2 | ||
|
|
27d1e641b6 | ||
|
|
b76a09e070 | ||
|
|
ff3ef717e2 | ||
|
|
08fe6f5aea | ||
|
|
1568165166 | ||
|
|
8590550a22 | ||
|
|
d56ef7641c | ||
|
|
02b8336890 | ||
|
|
6b9cd828c6 | ||
|
|
cafd35cea7 | ||
|
|
343dec049a |
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"
|
||||
|
||||
@@ -580,7 +581,7 @@ 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 glob.MatchStringInList(namespaces, e.Object.GetNamespace(), glob.REGEXP)
|
||||
},
|
||||
}
|
||||
}
|
||||
@@ -1365,6 +1366,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,6 +5,7 @@ import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"reflect"
|
||||
"strconv"
|
||||
"strings"
|
||||
"testing"
|
||||
"time"
|
||||
@@ -6351,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)
|
||||
|
||||
@@ -106,14 +106,9 @@ func loadClusters(ctx context.Context, kubeClient *kubernetes.Clientset, appClie
|
||||
}
|
||||
|
||||
redisOptions := &redis.Options{Addr: fmt.Sprintf("localhost:%d", port)}
|
||||
|
||||
secret, err := kubeClient.CoreV1().Secrets(namespace).Get(context.Background(), defaulRedisInitialPasswordSecretName, v1.GetOptions{})
|
||||
if err == nil {
|
||||
if _, ok := secret.Data[defaultResisInitialPasswordKey]; ok {
|
||||
redisOptions.Password = string(secret.Data[defaultResisInitialPasswordKey])
|
||||
}
|
||||
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 {
|
||||
|
||||
@@ -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()
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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
|
||||
@@ -414,3 +419,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)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2011,7 +2011,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 {
|
||||
|
||||
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
|
||||
|
||||
@@ -897,9 +897,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 +906,25 @@ 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 = ""
|
||||
}
|
||||
|
||||
return reflect.DeepEqual(comparedTo, currentSpec)
|
||||
}
|
||||
|
||||
func (m *appStateManager) persistRevisionHistory(
|
||||
app *v1alpha1.Application,
|
||||
revision string,
|
||||
|
||||
@@ -1432,6 +1432,8 @@ func TestIsLiveResourceManaged(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestUseDiffCache(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
type fixture struct {
|
||||
testName string
|
||||
noCache bool
|
||||
@@ -1692,6 +1694,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 {
|
||||
|
||||
@@ -32,23 +32,41 @@ function initializeVersionDropdown() {
|
||||
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/?' +
|
||||
@@ -56,6 +74,58 @@ function initializeVersionDropdown() {
|
||||
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 margin = 30;
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
| Argo CD version | Kubernetes versions |
|
||||
|-----------------|---------------------|
|
||||
| 2.12 | |
|
||||
| 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.
|
||||
|
||||
@@ -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
|
||||
|
||||
3
go.mod
3
go.mod
@@ -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
|
||||
@@ -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
|
||||
|
||||
2
go.sum
2
go.sum
@@ -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=
|
||||
|
||||
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"
|
||||
|
||||
@@ -5,7 +5,7 @@ kind: Kustomization
|
||||
images:
|
||||
- name: quay.io/argoproj/argocd
|
||||
newName: quay.io/argoproj/argocd
|
||||
newTag: v2.12.2
|
||||
newTag: v2.12.6
|
||||
resources:
|
||||
- ./application-controller
|
||||
- ./dex
|
||||
|
||||
@@ -21270,7 +21270,7 @@ spec:
|
||||
key: applicationsetcontroller.enable.scm.providers
|
||||
name: argocd-cmd-params-cm
|
||||
optional: true
|
||||
image: quay.io/argoproj/argocd:v2.12.2
|
||||
image: quay.io/argoproj/argocd:v2.12.6
|
||||
imagePullPolicy: Always
|
||||
name: argocd-applicationset-controller
|
||||
ports:
|
||||
@@ -21388,7 +21388,7 @@ spec:
|
||||
- argocd
|
||||
- admin
|
||||
- redis-initial-password
|
||||
image: quay.io/argoproj/argocd:v2.12.2
|
||||
image: quay.io/argoproj/argocd:v2.12.6
|
||||
imagePullPolicy: IfNotPresent
|
||||
name: secret-init
|
||||
securityContext:
|
||||
@@ -21641,7 +21641,7 @@ spec:
|
||||
value: /helm-working-dir
|
||||
- name: HELM_DATA_HOME
|
||||
value: /helm-working-dir
|
||||
image: quay.io/argoproj/argocd:v2.12.2
|
||||
image: quay.io/argoproj/argocd:v2.12.6
|
||||
imagePullPolicy: Always
|
||||
livenessProbe:
|
||||
failureThreshold: 3
|
||||
@@ -21693,7 +21693,7 @@ spec:
|
||||
- -n
|
||||
- /usr/local/bin/argocd
|
||||
- /var/run/argocd/argocd-cmp-server
|
||||
image: quay.io/argoproj/argocd:v2.12.2
|
||||
image: quay.io/argoproj/argocd:v2.12.6
|
||||
name: copyutil
|
||||
securityContext:
|
||||
allowPrivilegeEscalation: false
|
||||
@@ -21965,7 +21965,7 @@ spec:
|
||||
key: controller.ignore.normalizer.jq.timeout
|
||||
name: argocd-cmd-params-cm
|
||||
optional: true
|
||||
image: quay.io/argoproj/argocd:v2.12.2
|
||||
image: quay.io/argoproj/argocd:v2.12.6
|
||||
imagePullPolicy: Always
|
||||
name: argocd-application-controller
|
||||
ports:
|
||||
|
||||
@@ -12,4 +12,4 @@ resources:
|
||||
images:
|
||||
- name: quay.io/argoproj/argocd
|
||||
newName: quay.io/argoproj/argocd
|
||||
newTag: v2.12.2
|
||||
newTag: v2.12.6
|
||||
|
||||
@@ -12,7 +12,7 @@ patches:
|
||||
images:
|
||||
- name: quay.io/argoproj/argocd
|
||||
newName: quay.io/argoproj/argocd
|
||||
newTag: v2.12.2
|
||||
newTag: v2.12.6
|
||||
resources:
|
||||
- ../../base/application-controller
|
||||
- ../../base/applicationset-controller
|
||||
|
||||
@@ -22613,7 +22613,7 @@ spec:
|
||||
key: applicationsetcontroller.enable.scm.providers
|
||||
name: argocd-cmd-params-cm
|
||||
optional: true
|
||||
image: quay.io/argoproj/argocd:v2.12.2
|
||||
image: quay.io/argoproj/argocd:v2.12.6
|
||||
imagePullPolicy: Always
|
||||
name: argocd-applicationset-controller
|
||||
ports:
|
||||
@@ -22736,7 +22736,7 @@ spec:
|
||||
- -n
|
||||
- /usr/local/bin/argocd
|
||||
- /shared/argocd-dex
|
||||
image: quay.io/argoproj/argocd:v2.12.2
|
||||
image: quay.io/argoproj/argocd:v2.12.6
|
||||
imagePullPolicy: Always
|
||||
name: copyutil
|
||||
securityContext:
|
||||
@@ -22818,7 +22818,7 @@ spec:
|
||||
key: notificationscontroller.selfservice.enabled
|
||||
name: argocd-cmd-params-cm
|
||||
optional: true
|
||||
image: quay.io/argoproj/argocd:v2.12.2
|
||||
image: quay.io/argoproj/argocd:v2.12.6
|
||||
imagePullPolicy: Always
|
||||
livenessProbe:
|
||||
tcpSocket:
|
||||
@@ -22937,7 +22937,7 @@ spec:
|
||||
- argocd
|
||||
- admin
|
||||
- redis-initial-password
|
||||
image: quay.io/argoproj/argocd:v2.12.2
|
||||
image: quay.io/argoproj/argocd:v2.12.6
|
||||
imagePullPolicy: IfNotPresent
|
||||
name: secret-init
|
||||
securityContext:
|
||||
@@ -23218,7 +23218,7 @@ spec:
|
||||
value: /helm-working-dir
|
||||
- name: HELM_DATA_HOME
|
||||
value: /helm-working-dir
|
||||
image: quay.io/argoproj/argocd:v2.12.2
|
||||
image: quay.io/argoproj/argocd:v2.12.6
|
||||
imagePullPolicy: Always
|
||||
livenessProbe:
|
||||
failureThreshold: 3
|
||||
@@ -23270,7 +23270,7 @@ spec:
|
||||
- -n
|
||||
- /usr/local/bin/argocd
|
||||
- /var/run/argocd/argocd-cmp-server
|
||||
image: quay.io/argoproj/argocd:v2.12.2
|
||||
image: quay.io/argoproj/argocd:v2.12.6
|
||||
name: copyutil
|
||||
securityContext:
|
||||
allowPrivilegeEscalation: false
|
||||
@@ -23594,7 +23594,7 @@ spec:
|
||||
key: server.api.content.types
|
||||
name: argocd-cmd-params-cm
|
||||
optional: true
|
||||
image: quay.io/argoproj/argocd:v2.12.2
|
||||
image: quay.io/argoproj/argocd:v2.12.6
|
||||
imagePullPolicy: Always
|
||||
livenessProbe:
|
||||
httpGet:
|
||||
@@ -23893,7 +23893,7 @@ spec:
|
||||
key: controller.ignore.normalizer.jq.timeout
|
||||
name: argocd-cmd-params-cm
|
||||
optional: true
|
||||
image: quay.io/argoproj/argocd:v2.12.2
|
||||
image: quay.io/argoproj/argocd:v2.12.6
|
||||
imagePullPolicy: Always
|
||||
name: argocd-application-controller
|
||||
ports:
|
||||
|
||||
@@ -1688,7 +1688,7 @@ spec:
|
||||
key: applicationsetcontroller.enable.scm.providers
|
||||
name: argocd-cmd-params-cm
|
||||
optional: true
|
||||
image: quay.io/argoproj/argocd:v2.12.2
|
||||
image: quay.io/argoproj/argocd:v2.12.6
|
||||
imagePullPolicy: Always
|
||||
name: argocd-applicationset-controller
|
||||
ports:
|
||||
@@ -1811,7 +1811,7 @@ spec:
|
||||
- -n
|
||||
- /usr/local/bin/argocd
|
||||
- /shared/argocd-dex
|
||||
image: quay.io/argoproj/argocd:v2.12.2
|
||||
image: quay.io/argoproj/argocd:v2.12.6
|
||||
imagePullPolicy: Always
|
||||
name: copyutil
|
||||
securityContext:
|
||||
@@ -1893,7 +1893,7 @@ spec:
|
||||
key: notificationscontroller.selfservice.enabled
|
||||
name: argocd-cmd-params-cm
|
||||
optional: true
|
||||
image: quay.io/argoproj/argocd:v2.12.2
|
||||
image: quay.io/argoproj/argocd:v2.12.6
|
||||
imagePullPolicy: Always
|
||||
livenessProbe:
|
||||
tcpSocket:
|
||||
@@ -2012,7 +2012,7 @@ spec:
|
||||
- argocd
|
||||
- admin
|
||||
- redis-initial-password
|
||||
image: quay.io/argoproj/argocd:v2.12.2
|
||||
image: quay.io/argoproj/argocd:v2.12.6
|
||||
imagePullPolicy: IfNotPresent
|
||||
name: secret-init
|
||||
securityContext:
|
||||
@@ -2293,7 +2293,7 @@ spec:
|
||||
value: /helm-working-dir
|
||||
- name: HELM_DATA_HOME
|
||||
value: /helm-working-dir
|
||||
image: quay.io/argoproj/argocd:v2.12.2
|
||||
image: quay.io/argoproj/argocd:v2.12.6
|
||||
imagePullPolicy: Always
|
||||
livenessProbe:
|
||||
failureThreshold: 3
|
||||
@@ -2345,7 +2345,7 @@ spec:
|
||||
- -n
|
||||
- /usr/local/bin/argocd
|
||||
- /var/run/argocd/argocd-cmp-server
|
||||
image: quay.io/argoproj/argocd:v2.12.2
|
||||
image: quay.io/argoproj/argocd:v2.12.6
|
||||
name: copyutil
|
||||
securityContext:
|
||||
allowPrivilegeEscalation: false
|
||||
@@ -2669,7 +2669,7 @@ spec:
|
||||
key: server.api.content.types
|
||||
name: argocd-cmd-params-cm
|
||||
optional: true
|
||||
image: quay.io/argoproj/argocd:v2.12.2
|
||||
image: quay.io/argoproj/argocd:v2.12.6
|
||||
imagePullPolicy: Always
|
||||
livenessProbe:
|
||||
httpGet:
|
||||
@@ -2968,7 +2968,7 @@ spec:
|
||||
key: controller.ignore.normalizer.jq.timeout
|
||||
name: argocd-cmd-params-cm
|
||||
optional: true
|
||||
image: quay.io/argoproj/argocd:v2.12.2
|
||||
image: quay.io/argoproj/argocd:v2.12.6
|
||||
imagePullPolicy: Always
|
||||
name: argocd-application-controller
|
||||
ports:
|
||||
|
||||
@@ -21730,7 +21730,7 @@ spec:
|
||||
key: applicationsetcontroller.enable.scm.providers
|
||||
name: argocd-cmd-params-cm
|
||||
optional: true
|
||||
image: quay.io/argoproj/argocd:v2.12.2
|
||||
image: quay.io/argoproj/argocd:v2.12.6
|
||||
imagePullPolicy: Always
|
||||
name: argocd-applicationset-controller
|
||||
ports:
|
||||
@@ -21853,7 +21853,7 @@ spec:
|
||||
- -n
|
||||
- /usr/local/bin/argocd
|
||||
- /shared/argocd-dex
|
||||
image: quay.io/argoproj/argocd:v2.12.2
|
||||
image: quay.io/argoproj/argocd:v2.12.6
|
||||
imagePullPolicy: Always
|
||||
name: copyutil
|
||||
securityContext:
|
||||
@@ -21935,7 +21935,7 @@ spec:
|
||||
key: notificationscontroller.selfservice.enabled
|
||||
name: argocd-cmd-params-cm
|
||||
optional: true
|
||||
image: quay.io/argoproj/argocd:v2.12.2
|
||||
image: quay.io/argoproj/argocd:v2.12.6
|
||||
imagePullPolicy: Always
|
||||
livenessProbe:
|
||||
tcpSocket:
|
||||
@@ -22035,7 +22035,7 @@ spec:
|
||||
- argocd
|
||||
- admin
|
||||
- redis-initial-password
|
||||
image: quay.io/argoproj/argocd:v2.12.2
|
||||
image: quay.io/argoproj/argocd:v2.12.6
|
||||
imagePullPolicy: IfNotPresent
|
||||
name: secret-init
|
||||
securityContext:
|
||||
@@ -22288,7 +22288,7 @@ spec:
|
||||
value: /helm-working-dir
|
||||
- name: HELM_DATA_HOME
|
||||
value: /helm-working-dir
|
||||
image: quay.io/argoproj/argocd:v2.12.2
|
||||
image: quay.io/argoproj/argocd:v2.12.6
|
||||
imagePullPolicy: Always
|
||||
livenessProbe:
|
||||
failureThreshold: 3
|
||||
@@ -22340,7 +22340,7 @@ spec:
|
||||
- -n
|
||||
- /usr/local/bin/argocd
|
||||
- /var/run/argocd/argocd-cmp-server
|
||||
image: quay.io/argoproj/argocd:v2.12.2
|
||||
image: quay.io/argoproj/argocd:v2.12.6
|
||||
name: copyutil
|
||||
securityContext:
|
||||
allowPrivilegeEscalation: false
|
||||
@@ -22662,7 +22662,7 @@ spec:
|
||||
key: server.api.content.types
|
||||
name: argocd-cmd-params-cm
|
||||
optional: true
|
||||
image: quay.io/argoproj/argocd:v2.12.2
|
||||
image: quay.io/argoproj/argocd:v2.12.6
|
||||
imagePullPolicy: Always
|
||||
livenessProbe:
|
||||
httpGet:
|
||||
@@ -22961,7 +22961,7 @@ spec:
|
||||
key: controller.ignore.normalizer.jq.timeout
|
||||
name: argocd-cmd-params-cm
|
||||
optional: true
|
||||
image: quay.io/argoproj/argocd:v2.12.2
|
||||
image: quay.io/argoproj/argocd:v2.12.6
|
||||
imagePullPolicy: Always
|
||||
name: argocd-application-controller
|
||||
ports:
|
||||
|
||||
@@ -805,7 +805,7 @@ spec:
|
||||
key: applicationsetcontroller.enable.scm.providers
|
||||
name: argocd-cmd-params-cm
|
||||
optional: true
|
||||
image: quay.io/argoproj/argocd:v2.12.2
|
||||
image: quay.io/argoproj/argocd:v2.12.6
|
||||
imagePullPolicy: Always
|
||||
name: argocd-applicationset-controller
|
||||
ports:
|
||||
@@ -928,7 +928,7 @@ spec:
|
||||
- -n
|
||||
- /usr/local/bin/argocd
|
||||
- /shared/argocd-dex
|
||||
image: quay.io/argoproj/argocd:v2.12.2
|
||||
image: quay.io/argoproj/argocd:v2.12.6
|
||||
imagePullPolicy: Always
|
||||
name: copyutil
|
||||
securityContext:
|
||||
@@ -1010,7 +1010,7 @@ spec:
|
||||
key: notificationscontroller.selfservice.enabled
|
||||
name: argocd-cmd-params-cm
|
||||
optional: true
|
||||
image: quay.io/argoproj/argocd:v2.12.2
|
||||
image: quay.io/argoproj/argocd:v2.12.6
|
||||
imagePullPolicy: Always
|
||||
livenessProbe:
|
||||
tcpSocket:
|
||||
@@ -1110,7 +1110,7 @@ spec:
|
||||
- argocd
|
||||
- admin
|
||||
- redis-initial-password
|
||||
image: quay.io/argoproj/argocd:v2.12.2
|
||||
image: quay.io/argoproj/argocd:v2.12.6
|
||||
imagePullPolicy: IfNotPresent
|
||||
name: secret-init
|
||||
securityContext:
|
||||
@@ -1363,7 +1363,7 @@ spec:
|
||||
value: /helm-working-dir
|
||||
- name: HELM_DATA_HOME
|
||||
value: /helm-working-dir
|
||||
image: quay.io/argoproj/argocd:v2.12.2
|
||||
image: quay.io/argoproj/argocd:v2.12.6
|
||||
imagePullPolicy: Always
|
||||
livenessProbe:
|
||||
failureThreshold: 3
|
||||
@@ -1415,7 +1415,7 @@ spec:
|
||||
- -n
|
||||
- /usr/local/bin/argocd
|
||||
- /var/run/argocd/argocd-cmp-server
|
||||
image: quay.io/argoproj/argocd:v2.12.2
|
||||
image: quay.io/argoproj/argocd:v2.12.6
|
||||
name: copyutil
|
||||
securityContext:
|
||||
allowPrivilegeEscalation: false
|
||||
@@ -1737,7 +1737,7 @@ spec:
|
||||
key: server.api.content.types
|
||||
name: argocd-cmd-params-cm
|
||||
optional: true
|
||||
image: quay.io/argoproj/argocd:v2.12.2
|
||||
image: quay.io/argoproj/argocd:v2.12.6
|
||||
imagePullPolicy: Always
|
||||
livenessProbe:
|
||||
httpGet:
|
||||
@@ -2036,7 +2036,7 @@ spec:
|
||||
key: controller.ignore.normalizer.jq.timeout
|
||||
name: argocd-cmd-params-cm
|
||||
optional: true
|
||||
image: quay.io/argoproj/argocd:v2.12.2
|
||||
image: quay.io/argoproj/argocd:v2.12.6
|
||||
imagePullPolicy: Always
|
||||
name: argocd-application-controller
|
||||
ports:
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
|
||||
@@ -992,15 +992,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
|
||||
}
|
||||
|
||||
@@ -144,8 +144,8 @@ func mergeLogStreams(streams []chan logEntry, bufferingDuration time.Duration) c
|
||||
|
||||
_ = send(true)
|
||||
|
||||
close(merged)
|
||||
ticker.Stop()
|
||||
close(merged)
|
||||
}()
|
||||
return merged
|
||||
}
|
||||
|
||||
@@ -121,7 +121,7 @@
|
||||
"ts-node": "10.9.2",
|
||||
"typescript": "^4.9.5",
|
||||
"typescript-eslint": "^7.8.0",
|
||||
"webpack": "^5.84.1",
|
||||
"webpack": "^5.94.0",
|
||||
"webpack-cli": "^4.9.2",
|
||||
"webpack-dev-server": "^4.7.4",
|
||||
"yarn": "^1.22.21"
|
||||
|
||||
@@ -5,7 +5,7 @@ import {ApplicationSource, RevisionMetadata, ChartDetails} from '../../../shared
|
||||
import {services} from '../../../shared/services';
|
||||
|
||||
export const RevisionMetadataRows = (props: {applicationName: string; applicationNamespace: string; source: ApplicationSource; index: number; versionId: number}) => {
|
||||
if (props.source.chart) {
|
||||
if (props?.source?.chart) {
|
||||
return (
|
||||
<DataLoader
|
||||
input={props}
|
||||
|
||||
@@ -890,18 +890,20 @@ export class ApplicationDetails extends React.Component<RouteComponentProps<{app
|
||||
{
|
||||
iconClassName: 'fa fa-info-circle',
|
||||
title: <ActionMenuItem actionLabel='Details' />,
|
||||
action: () => this.selectNode(fullName)
|
||||
action: () => this.selectNode(fullName),
|
||||
disabled: !app.spec.source && (!app.spec.sources || app.spec.sources.length === 0)
|
||||
},
|
||||
{
|
||||
iconClassName: 'fa fa-file-medical',
|
||||
title: <ActionMenuItem actionLabel='Diff' />,
|
||||
action: () => this.selectNode(fullName, 0, 'diff'),
|
||||
disabled: app.status.sync.status === appModels.SyncStatuses.Synced
|
||||
disabled: app.status.sync.status === appModels.SyncStatuses.Synced || (!app.spec.source && (!app.spec.sources || app.spec.sources.length === 0))
|
||||
},
|
||||
{
|
||||
iconClassName: 'fa fa-sync',
|
||||
title: <ActionMenuItem actionLabel='Sync' />,
|
||||
action: () => AppUtils.showDeploy('all', null, this.appContext.apis)
|
||||
action: () => AppUtils.showDeploy('all', null, this.appContext.apis),
|
||||
disabled: !app.spec.source && (!app.spec.sources || app.spec.sources.length === 0)
|
||||
},
|
||||
{
|
||||
iconClassName: 'fa fa-info-circle',
|
||||
|
||||
@@ -556,23 +556,24 @@ function gatherCoreSourceDetails(i: number, attributes: EditablePanelItem[], sou
|
||||
)
|
||||
});
|
||||
} else {
|
||||
const targetRevision = source ? source.targetRevision || 'HEAD' : 'Unknown';
|
||||
attributes.push({
|
||||
title: 'TARGET REVISION',
|
||||
view: <Revision repoUrl={source.repoURL} revision={source.targetRevision || 'HEAD'} />,
|
||||
edit: (formApi: FormApi) => <RevisionFormField helpIconTop={'0'} hideLabel={true} formApi={formApi} repoURL={source.repoURL} fieldValue={revisionField} />
|
||||
view: <Revision repoUrl={source?.repoURL} revision={targetRevision} />,
|
||||
edit: (formApi: FormApi) => <RevisionFormField helpIconTop={'0'} hideLabel={true} formApi={formApi} repoURL={source?.repoURL} fieldValue={revisionField} />
|
||||
});
|
||||
attributes.push({
|
||||
title: 'PATH',
|
||||
view: (
|
||||
<Revision repoUrl={source.repoURL} revision={source.targetRevision || 'HEAD'} path={source.path} isForPath={true}>
|
||||
{processPath(source.path)}
|
||||
<Revision repoUrl={source?.repoURL} revision={targetRevision} path={source?.path} isForPath={true}>
|
||||
{processPath(source?.path)}
|
||||
</Revision>
|
||||
),
|
||||
edit: (formApi: FormApi) => <FormField formApi={formApi} field={sourcesPathField} component={Text} />
|
||||
});
|
||||
attributes.push({
|
||||
title: 'REF',
|
||||
view: <span>{source.ref}</span>,
|
||||
view: <span>{source?.ref}</span>,
|
||||
edit: (formApi: FormApi) => <FormField formApi={formApi} field={refField} component={Text} />
|
||||
});
|
||||
}
|
||||
|
||||
@@ -112,7 +112,7 @@ export const ApplicationStatusPanel = ({application, showDiff, showOperation, sh
|
||||
application.status.sync &&
|
||||
(hasMultipleSources
|
||||
? application.status.sync.revisions && application.status.sync.revisions[0] && application.spec.sources && !application.spec.sources[0].chart
|
||||
: application.status.sync.revision && !application.spec.source.chart) && (
|
||||
: application.status.sync.revision && !application.spec?.source?.chart) && (
|
||||
<div className='application-status-panel__item-name'>
|
||||
<RevisionMetadataPanel
|
||||
appName={application.metadata.name}
|
||||
@@ -160,7 +160,7 @@ export const ApplicationStatusPanel = ({application, showDiff, showOperation, sh
|
||||
<RevisionMetadataPanel
|
||||
appName={application.metadata.name}
|
||||
appNamespace={application.metadata.namespace}
|
||||
type={source.chart && 'helm'}
|
||||
type={source?.chart && 'helm'}
|
||||
revision={operationStateRevision}
|
||||
versionId={utils.getAppCurrentVersion(application)}
|
||||
/>
|
||||
|
||||
@@ -172,7 +172,7 @@ export const ApplicationSummary = (props: ApplicationSummaryProps) => {
|
||||
},
|
||||
!hasMultipleSources && {
|
||||
title: 'REPO URL',
|
||||
view: <Repo url={source.repoURL} />,
|
||||
view: <Repo url={source?.repoURL} />,
|
||||
edit: (formApi: FormApi) => <FormField formApi={formApi} field='spec.source.repoURL' component={Text} />
|
||||
},
|
||||
...(!hasMultipleSources
|
||||
@@ -180,11 +180,7 @@ export const ApplicationSummary = (props: ApplicationSummaryProps) => {
|
||||
? [
|
||||
{
|
||||
title: 'CHART',
|
||||
view: (
|
||||
<span>
|
||||
{source.chart}:{source.targetRevision}
|
||||
</span>
|
||||
),
|
||||
view: <span>{source && `${source.chart}:${source.targetRevision}`}</span>,
|
||||
edit: (formApi: FormApi) =>
|
||||
hasMultipleSources ? (
|
||||
helpTip('CHART is not editable for applications with multiple sources. You can edit them in the "Manifest" tab.')
|
||||
|
||||
@@ -5,7 +5,7 @@ import {ApplicationSource as ApplicationSourceType} from '../../../shared/models
|
||||
import './applications-source.scss';
|
||||
|
||||
export const ApplicationsSource = ({source}: {source: ApplicationSourceType}) => {
|
||||
const sourceString = `${source.repoURL}/${source.path || source.chart}`;
|
||||
const sourceString = source ? `${source.repoURL}/${source.path || source.chart}` : '';
|
||||
return (
|
||||
<Tooltip content={sourceString}>
|
||||
<div className='application-source'>{sourceString}</div>
|
||||
|
||||
@@ -108,6 +108,7 @@ export const ApplicationTiles = ({applications, syncApplication, refreshApplicat
|
||||
<div className='applications-tiles argo-table-list argo-table-list--clickable' ref={appContainerRef}>
|
||||
{applications.map((app, i) => {
|
||||
const source = getAppDefaultSource(app);
|
||||
const targetRevision = source ? source.targetRevision || 'HEAD' : 'Unknown';
|
||||
return (
|
||||
<div
|
||||
key={AppUtils.appInstanceName(app)}
|
||||
@@ -126,7 +127,7 @@ export const ApplicationTiles = ({applications, syncApplication, refreshApplicat
|
||||
)} applications-tiles__item`}>
|
||||
<div className='row '>
|
||||
<div className={app.status.summary.externalURLs?.length > 0 ? 'columns small-10' : 'columns small-11'}>
|
||||
<i className={'icon argo-icon-' + (source.chart != null ? 'helm' : 'git')} />
|
||||
<i className={'icon argo-icon-' + (source?.chart != null ? 'helm' : 'git')} />
|
||||
<Tooltip content={AppUtils.appInstanceName(app)}>
|
||||
<span className='applications-list__title'>
|
||||
{AppUtils.appQualifiedName(app, useAuthSettingsCtx?.appsInAnyNamespaceEnabled)}
|
||||
@@ -208,8 +209,8 @@ export const ApplicationTiles = ({applications, syncApplication, refreshApplicat
|
||||
Repository:
|
||||
</div>
|
||||
<div className='columns small-9'>
|
||||
<Tooltip content={source.repoURL} zIndex={4}>
|
||||
<span>{source.repoURL}</span>
|
||||
<Tooltip content={source?.repoURL} zIndex={4}>
|
||||
<span>{source?.repoURL}</span>
|
||||
</Tooltip>
|
||||
</div>
|
||||
</div>
|
||||
@@ -217,22 +218,22 @@ export const ApplicationTiles = ({applications, syncApplication, refreshApplicat
|
||||
<div className='columns small-3' title='Target Revision:'>
|
||||
Target Revision:
|
||||
</div>
|
||||
<div className='columns small-9'>{source.targetRevision || 'HEAD'}</div>
|
||||
<div className='columns small-9'>{targetRevision}</div>
|
||||
</div>
|
||||
{source.path && (
|
||||
{source?.path && (
|
||||
<div className='row'>
|
||||
<div className='columns small-3' title='Path:'>
|
||||
Path:
|
||||
</div>
|
||||
<div className='columns small-9'>{source.path}</div>
|
||||
<div className='columns small-9'>{source?.path}</div>
|
||||
</div>
|
||||
)}
|
||||
{source.chart && (
|
||||
{source?.chart && (
|
||||
<div className='row'>
|
||||
<div className='columns small-3' title='Chart:'>
|
||||
Chart:
|
||||
</div>
|
||||
<div className='columns small-9'>{source.chart}</div>
|
||||
<div className='columns small-9'>{source?.chart}</div>
|
||||
</div>
|
||||
)}
|
||||
<div className='row'>
|
||||
|
||||
@@ -706,10 +706,10 @@ export function renderResourceButtons(
|
||||
export function syncStatusMessage(app: appModels.Application) {
|
||||
const source = getAppDefaultSource(app);
|
||||
const revision = getAppDefaultSyncRevision(app);
|
||||
const rev = app.status.sync.revision || source.targetRevision || 'HEAD';
|
||||
let message = source.targetRevision || 'HEAD';
|
||||
const rev = app.status.sync.revision || (source ? source.targetRevision || 'HEAD' : 'Unknown');
|
||||
let message = source ? source?.targetRevision || 'HEAD' : 'Unknown';
|
||||
|
||||
if (revision) {
|
||||
if (revision && source) {
|
||||
if (source.chart) {
|
||||
message += ' (' + revision + ')';
|
||||
} else if (revision.length >= 7 && !revision.startsWith(source.targetRevision)) {
|
||||
@@ -953,23 +953,59 @@ export const OperationState = ({app, quiet}: {app: appModels.Application; quiet?
|
||||
);
|
||||
};
|
||||
|
||||
function isPodInitializedConditionTrue(status: any): boolean {
|
||||
if (!status?.conditions) {
|
||||
return false;
|
||||
}
|
||||
|
||||
for (const condition of status.conditions) {
|
||||
if (condition.type !== 'Initialized') {
|
||||
continue;
|
||||
}
|
||||
return condition.status === 'True';
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
// isPodPhaseTerminal returns true if the pod's phase is terminal.
|
||||
function isPodPhaseTerminal(phase: appModels.PodPhase): boolean {
|
||||
return phase === appModels.PodPhase.PodFailed || phase === appModels.PodPhase.PodSucceeded;
|
||||
}
|
||||
|
||||
export function getPodStateReason(pod: appModels.State): {message: string; reason: string; netContainerStatuses: any[]} {
|
||||
let reason = pod.status.phase;
|
||||
const podPhase = pod.status.phase;
|
||||
let reason = podPhase;
|
||||
let message = '';
|
||||
if (pod.status.reason) {
|
||||
reason = pod.status.reason;
|
||||
}
|
||||
|
||||
let initializing = false;
|
||||
|
||||
let netContainerStatuses = pod.status.initContainerStatuses || [];
|
||||
netContainerStatuses = netContainerStatuses.concat(pod.status.containerStatuses || []);
|
||||
|
||||
for (const condition of pod.status.conditions || []) {
|
||||
if (condition.type === 'PodScheduled' && condition.reason === 'SchedulingGated') {
|
||||
reason = 'SchedulingGated';
|
||||
}
|
||||
}
|
||||
|
||||
const initContainers: Record<string, any> = {};
|
||||
|
||||
for (const container of pod.spec.initContainers ?? []) {
|
||||
initContainers[container.name] = container;
|
||||
}
|
||||
|
||||
let initializing = false;
|
||||
for (const container of (pod.status.initContainerStatuses || []).slice().reverse()) {
|
||||
if (container.state.terminated && container.state.terminated.exitCode === 0) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (container.started && initContainers[container.name].restartPolicy === 'Always') {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (container.state.terminated) {
|
||||
if (container.state.terminated.reason) {
|
||||
reason = `Init:ExitCode:${container.state.terminated.exitCode}`;
|
||||
@@ -987,7 +1023,7 @@ export function getPodStateReason(pod: appModels.State): {message: string; reaso
|
||||
break;
|
||||
}
|
||||
|
||||
if (!initializing) {
|
||||
if (!initializing || isPodInitializedConditionTrue(pod.status)) {
|
||||
let hasRunning = false;
|
||||
for (const container of pod.status.containerStatuses || []) {
|
||||
if (container.state.waiting && container.state.waiting.reason) {
|
||||
@@ -1019,7 +1055,7 @@ export function getPodStateReason(pod: appModels.State): {message: string; reaso
|
||||
if ((pod as any).metadata.deletionTimestamp && pod.status.reason === 'NodeLost') {
|
||||
reason = 'Unknown';
|
||||
message = '';
|
||||
} else if ((pod as any).metadata.deletionTimestamp) {
|
||||
} else if ((pod as any).metadata.deletionTimestamp && !isPodPhaseTerminal(podPhase)) {
|
||||
reason = 'Terminating';
|
||||
message = '';
|
||||
}
|
||||
@@ -1044,7 +1080,7 @@ export const getPodReadinessGatesState = (pod: appModels.State): {nonExistingCon
|
||||
for (const condition of podStatusConditions) {
|
||||
existingConditions.set(condition.type, true);
|
||||
// priority order of conditions
|
||||
// eg. if there are multiple conditions set with same name then the one which comes first is evaluated
|
||||
// e.g. if there are multiple conditions set with same name then the one which comes first is evaluated
|
||||
if (podConditions.has(condition.type)) {
|
||||
continue;
|
||||
}
|
||||
@@ -1091,10 +1127,10 @@ export function isAppNode(node: appModels.ResourceNode) {
|
||||
|
||||
export function getAppOverridesCount(app: appModels.Application) {
|
||||
const source = getAppDefaultSource(app);
|
||||
if (source.kustomize && source.kustomize.images) {
|
||||
if (source?.kustomize?.images) {
|
||||
return source.kustomize.images.length;
|
||||
}
|
||||
if (source.helm && source.helm.parameters) {
|
||||
if (source?.helm?.parameters) {
|
||||
return source.helm.parameters.length;
|
||||
}
|
||||
return 0;
|
||||
|
||||
386
ui/yarn.lock
386
ui/yarn.lock
@@ -1680,7 +1680,7 @@
|
||||
"@jridgewell/sourcemap-codec" "^1.4.10"
|
||||
"@jridgewell/trace-mapping" "^0.3.24"
|
||||
|
||||
"@jridgewell/resolve-uri@3.1.0", "@jridgewell/resolve-uri@^3.0.3":
|
||||
"@jridgewell/resolve-uri@^3.0.3":
|
||||
version "3.1.0"
|
||||
resolved "https://registry.yarnpkg.com/@jridgewell/resolve-uri/-/resolve-uri-3.1.0.tgz#2203b118c157721addfe69d47b70465463066d78"
|
||||
integrity sha512-F2msla3tad+Mfht5cJq7LSXcdudKTWCVYUgw6pLFOOHSTtZlj6SWNYAp+AhuqLmWdBO2X5hPrLcu8cVP8fy28w==
|
||||
@@ -1708,7 +1708,15 @@
|
||||
"@jridgewell/gen-mapping" "^0.3.0"
|
||||
"@jridgewell/trace-mapping" "^0.3.9"
|
||||
|
||||
"@jridgewell/sourcemap-codec@1.4.14", "@jridgewell/sourcemap-codec@^1.4.10":
|
||||
"@jridgewell/source-map@^0.3.3":
|
||||
version "0.3.6"
|
||||
resolved "https://registry.yarnpkg.com/@jridgewell/source-map/-/source-map-0.3.6.tgz#9d71ca886e32502eb9362c9a74a46787c36df81a"
|
||||
integrity sha512-1ZJTZebgqllO79ue2bm3rIGud/bOe0pP5BjSRCRxxYkEZS8STV7zN84UBbiYu7jy+eCKSnVIUgoWWE/tt+shMQ==
|
||||
dependencies:
|
||||
"@jridgewell/gen-mapping" "^0.3.5"
|
||||
"@jridgewell/trace-mapping" "^0.3.25"
|
||||
|
||||
"@jridgewell/sourcemap-codec@^1.4.10":
|
||||
version "1.4.14"
|
||||
resolved "https://registry.yarnpkg.com/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.14.tgz#add4c98d341472a289190b424efbdb096991bb24"
|
||||
integrity sha512-XPSJHWmi394fuUuzDnGz1wiKqWfo1yXecHQMRf2l6hztTO+nPru658AyDngaBe7isIxEkRsPR3FZh+s7iVa4Uw==
|
||||
@@ -1734,7 +1742,7 @@
|
||||
"@jridgewell/resolve-uri" "^3.0.3"
|
||||
"@jridgewell/sourcemap-codec" "^1.4.10"
|
||||
|
||||
"@jridgewell/trace-mapping@^0.3.12", "@jridgewell/trace-mapping@^0.3.18", "@jridgewell/trace-mapping@^0.3.24", "@jridgewell/trace-mapping@^0.3.25":
|
||||
"@jridgewell/trace-mapping@^0.3.12", "@jridgewell/trace-mapping@^0.3.18", "@jridgewell/trace-mapping@^0.3.20", "@jridgewell/trace-mapping@^0.3.24", "@jridgewell/trace-mapping@^0.3.25":
|
||||
version "0.3.25"
|
||||
resolved "https://registry.yarnpkg.com/@jridgewell/trace-mapping/-/trace-mapping-0.3.25.tgz#15f190e98895f3fc23276ee14bc76b675c2e50f0"
|
||||
integrity sha512-vNk6aEwybGtawWmy/PzwnGDOjCkLWSD2wqvjGGAgOAwCGWySYXfYoxt00IJkTF+8Lb57DwOb3Aa0o9CApepiYQ==
|
||||
@@ -1742,14 +1750,6 @@
|
||||
"@jridgewell/resolve-uri" "^3.1.0"
|
||||
"@jridgewell/sourcemap-codec" "^1.4.14"
|
||||
|
||||
"@jridgewell/trace-mapping@^0.3.17":
|
||||
version "0.3.18"
|
||||
resolved "https://registry.yarnpkg.com/@jridgewell/trace-mapping/-/trace-mapping-0.3.18.tgz#25783b2086daf6ff1dcb53c9249ae480e4dd4cd6"
|
||||
integrity sha512-w+niJYzMHdd7USdiH2U6869nqhD2nbfZXND5Yp93qIbEmnDNk7PD48o+YchRVpzMU7M6jVCbenTR7PA1FLQ9pA==
|
||||
dependencies:
|
||||
"@jridgewell/resolve-uri" "3.1.0"
|
||||
"@jridgewell/sourcemap-codec" "1.4.14"
|
||||
|
||||
"@jridgewell/trace-mapping@^0.3.9":
|
||||
version "0.3.14"
|
||||
resolved "https://registry.yarnpkg.com/@jridgewell/trace-mapping/-/trace-mapping-0.3.14.tgz#b231a081d8f66796e475ad588a1ef473112701ed"
|
||||
@@ -2037,26 +2037,10 @@
|
||||
dependencies:
|
||||
deepmerge "*"
|
||||
|
||||
"@types/eslint-scope@^3.7.3":
|
||||
version "3.7.3"
|
||||
resolved "https://registry.yarnpkg.com/@types/eslint-scope/-/eslint-scope-3.7.3.tgz#125b88504b61e3c8bc6f870882003253005c3224"
|
||||
integrity sha512-PB3ldyrcnAicT35TWPs5IcwKD8S333HMaa2VVv4+wdvebJkjWuW/xESoB8IwRcog8HYVYamb1g/R31Qv5Bx03g==
|
||||
dependencies:
|
||||
"@types/eslint" "*"
|
||||
"@types/estree" "*"
|
||||
|
||||
"@types/eslint@*":
|
||||
version "8.4.1"
|
||||
resolved "https://registry.yarnpkg.com/@types/eslint/-/eslint-8.4.1.tgz#c48251553e8759db9e656de3efc846954ac32304"
|
||||
integrity sha512-GE44+DNEyxxh2Kc6ro/VkIj+9ma0pO0bwv9+uHSyBrikYOHr8zYcdPvnBOp1aw8s+CjRvuSx7CyWqRrNFQ59mA==
|
||||
dependencies:
|
||||
"@types/estree" "*"
|
||||
"@types/json-schema" "*"
|
||||
|
||||
"@types/estree@*", "@types/estree@^1.0.0":
|
||||
version "1.0.1"
|
||||
resolved "https://registry.yarnpkg.com/@types/estree/-/estree-1.0.1.tgz#aa22750962f3bf0e79d753d3cc067f010c95f194"
|
||||
integrity sha512-LG4opVs2ANWZ1TJoKc937iMmNstM/d0ae1vNbnBvBhqCSezgVUOzcLCqbI5elV8Vy6WKwKjaqR+zO9VKirBBCA==
|
||||
"@types/estree@^1.0.5":
|
||||
version "1.0.6"
|
||||
resolved "https://registry.yarnpkg.com/@types/estree/-/estree-1.0.6.tgz#628effeeae2064a1b4e79f78e81d87b7e5fc7b50"
|
||||
integrity sha512-AYnb1nQyY49te+VRAVgmzfcgjYS91mY5P0TKUDCLEM+gNnA+3T6rWITXRLYCpahpqSQbN5cE+gHpnPyXjHWxcw==
|
||||
|
||||
"@types/express-serve-static-core@*", "@types/express-serve-static-core@^4.17.18":
|
||||
version "4.17.28"
|
||||
@@ -2160,11 +2144,6 @@
|
||||
"@types/tough-cookie" "*"
|
||||
parse5 "^7.0.0"
|
||||
|
||||
"@types/json-schema@*", "@types/json-schema@^7.0.8", "@types/json-schema@^7.0.9":
|
||||
version "7.0.11"
|
||||
resolved "https://registry.yarnpkg.com/@types/json-schema/-/json-schema-7.0.11.tgz#d421b6c527a3037f7c84433fd2c4229e016863d3"
|
||||
integrity sha512-wOuvG1SN4Us4rez+tylwwwCV1psiNVOkJeM3AUWUNWg/jDQY2+HE/444y5gc+jBmRqASOm2Oeh5c1axHobwRKQ==
|
||||
|
||||
"@types/json-schema@^7.0.15":
|
||||
version "7.0.15"
|
||||
resolved "https://registry.yarnpkg.com/@types/json-schema/-/json-schema-7.0.15.tgz#596a1747233694d50f6ad8a7869fcb6f56cf5841"
|
||||
@@ -2175,6 +2154,11 @@
|
||||
resolved "https://registry.yarnpkg.com/@types/json-schema/-/json-schema-7.0.8.tgz#edf1bf1dbf4e04413ca8e5b17b3b7d7d54b59818"
|
||||
integrity sha512-YSBPTLTVm2e2OoQIDYx8HaeWJ5tTToLH67kXR7zYNGupXMEHa2++G8k+DczX2cFVgalypqtyZIcU19AFcmOpmg==
|
||||
|
||||
"@types/json-schema@^7.0.8", "@types/json-schema@^7.0.9":
|
||||
version "7.0.11"
|
||||
resolved "https://registry.yarnpkg.com/@types/json-schema/-/json-schema-7.0.11.tgz#d421b6c527a3037f7c84433fd2c4229e016863d3"
|
||||
integrity sha512-wOuvG1SN4Us4rez+tylwwwCV1psiNVOkJeM3AUWUNWg/jDQY2+HE/444y5gc+jBmRqASOm2Oeh5c1axHobwRKQ==
|
||||
|
||||
"@types/lodash-es@^4.17.6":
|
||||
version "4.17.6"
|
||||
resolved "https://registry.yarnpkg.com/@types/lodash-es/-/lodash-es-4.17.6.tgz#c2ed4c8320ffa6f11b43eb89e9eaeec65966a0a0"
|
||||
@@ -2483,10 +2467,10 @@
|
||||
"@typescript-eslint/types" "7.8.0"
|
||||
eslint-visitor-keys "^3.4.3"
|
||||
|
||||
"@webassemblyjs/ast@1.11.6", "@webassemblyjs/ast@^1.11.5":
|
||||
version "1.11.6"
|
||||
resolved "https://registry.yarnpkg.com/@webassemblyjs/ast/-/ast-1.11.6.tgz#db046555d3c413f8966ca50a95176a0e2c642e24"
|
||||
integrity sha512-IN1xI7PwOvLPgjcf180gC1bqn3q/QaOCwYUahIOhbYUu8KA/3tw2RT/T0Gidi1l7Hhj5D/INhJxiICObqpMu4Q==
|
||||
"@webassemblyjs/ast@1.12.1", "@webassemblyjs/ast@^1.12.1":
|
||||
version "1.12.1"
|
||||
resolved "https://registry.yarnpkg.com/@webassemblyjs/ast/-/ast-1.12.1.tgz#bb16a0e8b1914f979f45864c23819cc3e3f0d4bb"
|
||||
integrity sha512-EKfMUOPRRUTy5UII4qJDGPpqfwjOmZ5jeGFwid9mnoqIFK+e0vqoi1qH56JpmZSzEL53jKnNzScdmftJyG5xWg==
|
||||
dependencies:
|
||||
"@webassemblyjs/helper-numbers" "1.11.6"
|
||||
"@webassemblyjs/helper-wasm-bytecode" "1.11.6"
|
||||
@@ -2501,10 +2485,10 @@
|
||||
resolved "https://registry.yarnpkg.com/@webassemblyjs/helper-api-error/-/helper-api-error-1.11.6.tgz#6132f68c4acd59dcd141c44b18cbebbd9f2fa768"
|
||||
integrity sha512-o0YkoP4pVu4rN8aTJgAyj9hC2Sv5UlkzCHhxqWj8butaLvnpdc2jOwh4ewE6CX0txSfLn/UYaV/pheS2Txg//Q==
|
||||
|
||||
"@webassemblyjs/helper-buffer@1.11.6":
|
||||
version "1.11.6"
|
||||
resolved "https://registry.yarnpkg.com/@webassemblyjs/helper-buffer/-/helper-buffer-1.11.6.tgz#b66d73c43e296fd5e88006f18524feb0f2c7c093"
|
||||
integrity sha512-z3nFzdcp1mb8nEOFFk8DrYLpHvhKC3grJD2ardfKOzmbmJvEf/tPIqCY+sNcwZIY8ZD7IkB2l7/pqhUhqm7hLA==
|
||||
"@webassemblyjs/helper-buffer@1.12.1":
|
||||
version "1.12.1"
|
||||
resolved "https://registry.yarnpkg.com/@webassemblyjs/helper-buffer/-/helper-buffer-1.12.1.tgz#6df20d272ea5439bf20ab3492b7fb70e9bfcb3f6"
|
||||
integrity sha512-nzJwQw99DNDKr9BVCOZcLuJJUlqkJh+kVzVl6Fmq/tI5ZtEyWT1KZMyOXltXLZJmDtvLCDgwsyrkohEtopTXCw==
|
||||
|
||||
"@webassemblyjs/helper-numbers@1.11.6":
|
||||
version "1.11.6"
|
||||
@@ -2520,15 +2504,15 @@
|
||||
resolved "https://registry.yarnpkg.com/@webassemblyjs/helper-wasm-bytecode/-/helper-wasm-bytecode-1.11.6.tgz#bb2ebdb3b83aa26d9baad4c46d4315283acd51e9"
|
||||
integrity sha512-sFFHKwcmBprO9e7Icf0+gddyWYDViL8bpPjJJl0WHxCdETktXdmtWLGVzoHbqUcY4Be1LkNfwTmXOJUFZYSJdA==
|
||||
|
||||
"@webassemblyjs/helper-wasm-section@1.11.6":
|
||||
version "1.11.6"
|
||||
resolved "https://registry.yarnpkg.com/@webassemblyjs/helper-wasm-section/-/helper-wasm-section-1.11.6.tgz#ff97f3863c55ee7f580fd5c41a381e9def4aa577"
|
||||
integrity sha512-LPpZbSOwTpEC2cgn4hTydySy1Ke+XEu+ETXuoyvuyezHO3Kjdu90KK95Sh9xTbmjrCsUwvWwCOQQNta37VrS9g==
|
||||
"@webassemblyjs/helper-wasm-section@1.12.1":
|
||||
version "1.12.1"
|
||||
resolved "https://registry.yarnpkg.com/@webassemblyjs/helper-wasm-section/-/helper-wasm-section-1.12.1.tgz#3da623233ae1a60409b509a52ade9bc22a37f7bf"
|
||||
integrity sha512-Jif4vfB6FJlUlSbgEMHUyk1j234GTNG9dBJ4XJdOySoj518Xj0oGsNi59cUQF4RRMS9ouBUxDDdyBVfPTypa5g==
|
||||
dependencies:
|
||||
"@webassemblyjs/ast" "1.11.6"
|
||||
"@webassemblyjs/helper-buffer" "1.11.6"
|
||||
"@webassemblyjs/ast" "1.12.1"
|
||||
"@webassemblyjs/helper-buffer" "1.12.1"
|
||||
"@webassemblyjs/helper-wasm-bytecode" "1.11.6"
|
||||
"@webassemblyjs/wasm-gen" "1.11.6"
|
||||
"@webassemblyjs/wasm-gen" "1.12.1"
|
||||
|
||||
"@webassemblyjs/ieee754@1.11.6":
|
||||
version "1.11.6"
|
||||
@@ -2549,59 +2533,59 @@
|
||||
resolved "https://registry.yarnpkg.com/@webassemblyjs/utf8/-/utf8-1.11.6.tgz#90f8bc34c561595fe156603be7253cdbcd0fab5a"
|
||||
integrity sha512-vtXf2wTQ3+up9Zsg8sa2yWiQpzSsMyXj0qViVP6xKGCUT8p8YJ6HqI7l5eCnWx1T/FYdsv07HQs2wTFbbof/RA==
|
||||
|
||||
"@webassemblyjs/wasm-edit@^1.11.5":
|
||||
version "1.11.6"
|
||||
resolved "https://registry.yarnpkg.com/@webassemblyjs/wasm-edit/-/wasm-edit-1.11.6.tgz#c72fa8220524c9b416249f3d94c2958dfe70ceab"
|
||||
integrity sha512-Ybn2I6fnfIGuCR+Faaz7YcvtBKxvoLV3Lebn1tM4o/IAJzmi9AWYIPWpyBfU8cC+JxAO57bk4+zdsTjJR+VTOw==
|
||||
"@webassemblyjs/wasm-edit@^1.12.1":
|
||||
version "1.12.1"
|
||||
resolved "https://registry.yarnpkg.com/@webassemblyjs/wasm-edit/-/wasm-edit-1.12.1.tgz#9f9f3ff52a14c980939be0ef9d5df9ebc678ae3b"
|
||||
integrity sha512-1DuwbVvADvS5mGnXbE+c9NfA8QRcZ6iKquqjjmR10k6o+zzsRVesil54DKexiowcFCPdr/Q0qaMgB01+SQ1u6g==
|
||||
dependencies:
|
||||
"@webassemblyjs/ast" "1.11.6"
|
||||
"@webassemblyjs/helper-buffer" "1.11.6"
|
||||
"@webassemblyjs/ast" "1.12.1"
|
||||
"@webassemblyjs/helper-buffer" "1.12.1"
|
||||
"@webassemblyjs/helper-wasm-bytecode" "1.11.6"
|
||||
"@webassemblyjs/helper-wasm-section" "1.11.6"
|
||||
"@webassemblyjs/wasm-gen" "1.11.6"
|
||||
"@webassemblyjs/wasm-opt" "1.11.6"
|
||||
"@webassemblyjs/wasm-parser" "1.11.6"
|
||||
"@webassemblyjs/wast-printer" "1.11.6"
|
||||
"@webassemblyjs/helper-wasm-section" "1.12.1"
|
||||
"@webassemblyjs/wasm-gen" "1.12.1"
|
||||
"@webassemblyjs/wasm-opt" "1.12.1"
|
||||
"@webassemblyjs/wasm-parser" "1.12.1"
|
||||
"@webassemblyjs/wast-printer" "1.12.1"
|
||||
|
||||
"@webassemblyjs/wasm-gen@1.11.6":
|
||||
version "1.11.6"
|
||||
resolved "https://registry.yarnpkg.com/@webassemblyjs/wasm-gen/-/wasm-gen-1.11.6.tgz#fb5283e0e8b4551cc4e9c3c0d7184a65faf7c268"
|
||||
integrity sha512-3XOqkZP/y6B4F0PBAXvI1/bky7GryoogUtfwExeP/v7Nzwo1QLcq5oQmpKlftZLbT+ERUOAZVQjuNVak6UXjPA==
|
||||
"@webassemblyjs/wasm-gen@1.12.1":
|
||||
version "1.12.1"
|
||||
resolved "https://registry.yarnpkg.com/@webassemblyjs/wasm-gen/-/wasm-gen-1.12.1.tgz#a6520601da1b5700448273666a71ad0a45d78547"
|
||||
integrity sha512-TDq4Ojh9fcohAw6OIMXqiIcTq5KUXTGRkVxbSo1hQnSy6lAM5GSdfwWeSxpAo0YzgsgF182E/U0mDNhuA0tW7w==
|
||||
dependencies:
|
||||
"@webassemblyjs/ast" "1.11.6"
|
||||
"@webassemblyjs/ast" "1.12.1"
|
||||
"@webassemblyjs/helper-wasm-bytecode" "1.11.6"
|
||||
"@webassemblyjs/ieee754" "1.11.6"
|
||||
"@webassemblyjs/leb128" "1.11.6"
|
||||
"@webassemblyjs/utf8" "1.11.6"
|
||||
|
||||
"@webassemblyjs/wasm-opt@1.11.6":
|
||||
version "1.11.6"
|
||||
resolved "https://registry.yarnpkg.com/@webassemblyjs/wasm-opt/-/wasm-opt-1.11.6.tgz#d9a22d651248422ca498b09aa3232a81041487c2"
|
||||
integrity sha512-cOrKuLRE7PCe6AsOVl7WasYf3wbSo4CeOk6PkrjS7g57MFfVUF9u6ysQBBODX0LdgSvQqRiGz3CXvIDKcPNy4g==
|
||||
"@webassemblyjs/wasm-opt@1.12.1":
|
||||
version "1.12.1"
|
||||
resolved "https://registry.yarnpkg.com/@webassemblyjs/wasm-opt/-/wasm-opt-1.12.1.tgz#9e6e81475dfcfb62dab574ac2dda38226c232bc5"
|
||||
integrity sha512-Jg99j/2gG2iaz3hijw857AVYekZe2SAskcqlWIZXjji5WStnOpVoat3gQfT/Q5tb2djnCjBtMocY/Su1GfxPBg==
|
||||
dependencies:
|
||||
"@webassemblyjs/ast" "1.11.6"
|
||||
"@webassemblyjs/helper-buffer" "1.11.6"
|
||||
"@webassemblyjs/wasm-gen" "1.11.6"
|
||||
"@webassemblyjs/wasm-parser" "1.11.6"
|
||||
"@webassemblyjs/ast" "1.12.1"
|
||||
"@webassemblyjs/helper-buffer" "1.12.1"
|
||||
"@webassemblyjs/wasm-gen" "1.12.1"
|
||||
"@webassemblyjs/wasm-parser" "1.12.1"
|
||||
|
||||
"@webassemblyjs/wasm-parser@1.11.6", "@webassemblyjs/wasm-parser@^1.11.5":
|
||||
version "1.11.6"
|
||||
resolved "https://registry.yarnpkg.com/@webassemblyjs/wasm-parser/-/wasm-parser-1.11.6.tgz#bb85378c527df824004812bbdb784eea539174a1"
|
||||
integrity sha512-6ZwPeGzMJM3Dqp3hCsLgESxBGtT/OeCvCZ4TA1JUPYgmhAx38tTPR9JaKy0S5H3evQpO/h2uWs2j6Yc/fjkpTQ==
|
||||
"@webassemblyjs/wasm-parser@1.12.1", "@webassemblyjs/wasm-parser@^1.12.1":
|
||||
version "1.12.1"
|
||||
resolved "https://registry.yarnpkg.com/@webassemblyjs/wasm-parser/-/wasm-parser-1.12.1.tgz#c47acb90e6f083391e3fa61d113650eea1e95937"
|
||||
integrity sha512-xikIi7c2FHXysxXe3COrVUPSheuBtpcfhbpFj4gmu7KRLYOzANztwUU0IbsqvMqzuNK2+glRGWCEqZo1WCLyAQ==
|
||||
dependencies:
|
||||
"@webassemblyjs/ast" "1.11.6"
|
||||
"@webassemblyjs/ast" "1.12.1"
|
||||
"@webassemblyjs/helper-api-error" "1.11.6"
|
||||
"@webassemblyjs/helper-wasm-bytecode" "1.11.6"
|
||||
"@webassemblyjs/ieee754" "1.11.6"
|
||||
"@webassemblyjs/leb128" "1.11.6"
|
||||
"@webassemblyjs/utf8" "1.11.6"
|
||||
|
||||
"@webassemblyjs/wast-printer@1.11.6":
|
||||
version "1.11.6"
|
||||
resolved "https://registry.yarnpkg.com/@webassemblyjs/wast-printer/-/wast-printer-1.11.6.tgz#a7bf8dd7e362aeb1668ff43f35cb849f188eff20"
|
||||
integrity sha512-JM7AhRcE+yW2GWYaKeHL5vt4xqee5N2WcezptmgyhNS+ScggqcT1OtXykhAb13Sn5Yas0j2uv9tHgrjwvzAP4A==
|
||||
"@webassemblyjs/wast-printer@1.12.1":
|
||||
version "1.12.1"
|
||||
resolved "https://registry.yarnpkg.com/@webassemblyjs/wast-printer/-/wast-printer-1.12.1.tgz#bcecf661d7d1abdaf989d8341a4833e33e2b31ac"
|
||||
integrity sha512-+X4WAlOisVWQMikjbcvY2e0rwPsKQ9F688lksZhBcPycBBuii3O7m8FACbDMWDojpAqvjIncrG8J0XHKyQfVeA==
|
||||
dependencies:
|
||||
"@webassemblyjs/ast" "1.11.6"
|
||||
"@webassemblyjs/ast" "1.12.1"
|
||||
"@xtuc/long" "4.2.2"
|
||||
|
||||
"@webpack-cli/configtest@^1.1.1":
|
||||
@@ -2652,10 +2636,10 @@ acorn-globals@^7.0.0:
|
||||
acorn "^8.1.0"
|
||||
acorn-walk "^8.0.2"
|
||||
|
||||
acorn-import-assertions@^1.9.0:
|
||||
version "1.9.0"
|
||||
resolved "https://registry.yarnpkg.com/acorn-import-assertions/-/acorn-import-assertions-1.9.0.tgz#507276249d684797c84e0734ef84860334cfb1ac"
|
||||
integrity sha512-cmMwop9x+8KFhxvKrKfPYmN6/pKTYYHBqLa0DfvVZcKMJWNyWLnaqND7dx/qn66R7ewM1UX5XMaDVP5wlVTaVA==
|
||||
acorn-import-attributes@^1.9.5:
|
||||
version "1.9.5"
|
||||
resolved "https://registry.yarnpkg.com/acorn-import-attributes/-/acorn-import-attributes-1.9.5.tgz#7eb1557b1ba05ef18b5ed0ec67591bfab04688ef"
|
||||
integrity sha512-n02Vykv5uA3eHGM/Z2dQrcD56kL8TyDb2p1+0P83PClMnC/nc+anbQRhIOWnSq4Ke/KvDPrY3C9hDtC/A3eHnQ==
|
||||
|
||||
acorn-jsx@^5.3.2:
|
||||
version "5.3.2"
|
||||
@@ -2672,6 +2656,11 @@ acorn@^8.1.0, acorn@^8.11.3, acorn@^8.4.1, acorn@^8.5.0, acorn@^8.7.1, acorn@^8.
|
||||
resolved "https://registry.yarnpkg.com/acorn/-/acorn-8.11.3.tgz#71e0b14e13a4ec160724b38fb7b0f233b1b81d7a"
|
||||
integrity sha512-Y9rRfJG5jcKOE0CLisYbojUjIrIEE7AGMzA/Sm4BslANhbS+cDMpgBdcPT91oJ7OuJ9hYJBx59RjbhxVnrF8Xg==
|
||||
|
||||
acorn@^8.8.2:
|
||||
version "8.12.1"
|
||||
resolved "https://registry.yarnpkg.com/acorn/-/acorn-8.12.1.tgz#71616bdccbe25e27a54439e0046e89ca76df2248"
|
||||
integrity sha512-tcpGyI9zbizT9JbV6oYE477V6mTlXvvi0T0G3SNIYE2apm/G5huBa1+K89VGeovbg+jycCrfhl3ADxErOuO6Jg==
|
||||
|
||||
add@^2.0.6:
|
||||
version "2.0.6"
|
||||
resolved "https://registry.yarnpkg.com/add/-/add-2.0.6.tgz#248f0a9f6e5a528ef2295dbeec30532130ae2235"
|
||||
@@ -3169,10 +3158,10 @@ binary-extensions@^2.0.0:
|
||||
resolved "https://registry.yarnpkg.com/binary-extensions/-/binary-extensions-2.2.0.tgz#75f502eeaf9ffde42fc98829645be4ea76bd9e2d"
|
||||
integrity sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA==
|
||||
|
||||
body-parser@1.20.2:
|
||||
version "1.20.2"
|
||||
resolved "https://registry.yarnpkg.com/body-parser/-/body-parser-1.20.2.tgz#6feb0e21c4724d06de7ff38da36dad4f57a747fd"
|
||||
integrity sha512-ml9pReCu3M61kGlqoTm2umSXTlRTuGTx0bfYj+uIUKKYycG5NtSbeetV3faSU6R7ajOPw0g/J1PvK4qNy7s5bA==
|
||||
body-parser@1.20.3:
|
||||
version "1.20.3"
|
||||
resolved "https://registry.yarnpkg.com/body-parser/-/body-parser-1.20.3.tgz#1953431221c6fb5cd63c4b36d53fab0928e548c6"
|
||||
integrity sha512-7rAxByjUMqQ3/bHJy7D6OGXvx/MMc4IqBn/X0fcM1QUcAItpZrBEYhWGem+tzXH90c+G01ypMcYJBO9Y30203g==
|
||||
dependencies:
|
||||
bytes "3.1.2"
|
||||
content-type "~1.0.5"
|
||||
@@ -3182,7 +3171,7 @@ body-parser@1.20.2:
|
||||
http-errors "2.0.0"
|
||||
iconv-lite "0.4.24"
|
||||
on-finished "2.4.1"
|
||||
qs "6.11.0"
|
||||
qs "6.13.0"
|
||||
raw-body "2.5.2"
|
||||
type-is "~1.6.18"
|
||||
unpipe "1.0.0"
|
||||
@@ -3226,7 +3215,7 @@ braces@^3.0.1, braces@^3.0.2, braces@~3.0.2:
|
||||
dependencies:
|
||||
fill-range "^7.1.1"
|
||||
|
||||
browserslist@^4.14.5, browserslist@^4.16.6, browserslist@^4.17.5, browserslist@^4.6.0:
|
||||
browserslist@^4.16.6, browserslist@^4.17.5, browserslist@^4.6.0:
|
||||
version "4.20.2"
|
||||
resolved "https://registry.yarnpkg.com/browserslist/-/browserslist-4.20.2.tgz#567b41508757ecd904dab4d1c646c612cd3d4f88"
|
||||
integrity sha512-CQOBCqp/9pDvDbx3xfMi+86pr4KXIf2FDkTTdeuYw8OxS9t898LA1Khq57gtufFILXpfgsSx5woNgsBgvGjpsA==
|
||||
@@ -3237,6 +3226,16 @@ browserslist@^4.14.5, browserslist@^4.16.6, browserslist@^4.17.5, browserslist@^
|
||||
node-releases "^2.0.2"
|
||||
picocolors "^1.0.0"
|
||||
|
||||
browserslist@^4.21.10:
|
||||
version "4.23.3"
|
||||
resolved "https://registry.yarnpkg.com/browserslist/-/browserslist-4.23.3.tgz#debb029d3c93ebc97ffbc8d9cbb03403e227c800"
|
||||
integrity sha512-btwCFJVjI4YWDNfau8RhZ+B1Q/VLoUITrm3RlP6y1tYGWIOa+InuYiRGXUBXo8nA1qKmHMyLB/iVQg5TT4eFoA==
|
||||
dependencies:
|
||||
caniuse-lite "^1.0.30001646"
|
||||
electron-to-chromium "^1.5.4"
|
||||
node-releases "^2.0.18"
|
||||
update-browserslist-db "^1.1.0"
|
||||
|
||||
browserslist@^4.22.2:
|
||||
version "4.23.0"
|
||||
resolved "https://registry.yarnpkg.com/browserslist/-/browserslist-4.23.0.tgz#8f3acc2bbe73af7213399430890f86c63a5674ab"
|
||||
@@ -3369,6 +3368,11 @@ caniuse-lite@^1.0.30001587:
|
||||
resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001620.tgz#78bb6f35b8fe315b96b8590597094145d0b146b4"
|
||||
integrity sha512-WJvYsOjd1/BYUY6SNGUosK9DUidBPDTnOARHp3fSmFO1ekdxaY6nKRttEVrfMmYi80ctS0kz1wiWmm14fVc3ew==
|
||||
|
||||
caniuse-lite@^1.0.30001646:
|
||||
version "1.0.30001662"
|
||||
resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001662.tgz#3574b22dfec54a3f3b6787331da1040fe8e763ec"
|
||||
integrity sha512-sgMUVwLmGseH8ZIrm1d51UbrhqMCH3jvS7gF/M6byuHOnKyLOBL7W8yz5V02OHwgLGA36o/AFhWzzh4uc5aqTA==
|
||||
|
||||
chalk@^1.1.3:
|
||||
version "1.1.3"
|
||||
resolved "https://registry.yarnpkg.com/chalk/-/chalk-1.1.3.tgz#a8115c55e4a702fe4d150abd3872822a7e09fc98"
|
||||
@@ -4181,9 +4185,9 @@ domhandler@^4.0.0, domhandler@^4.2.0:
|
||||
domelementtype "^2.2.0"
|
||||
|
||||
dompurify@^2.2.8:
|
||||
version "2.3.6"
|
||||
resolved "https://registry.yarnpkg.com/dompurify/-/dompurify-2.3.6.tgz#2e019d7d7617aacac07cbbe3d88ae3ad354cf875"
|
||||
integrity sha512-OFP2u/3T1R5CEgWCEONuJ1a5+MFKnOYpkywpUSxv/dj1LeBT1erK+JwM7zK0ROy2BRhqVCf0LRw/kHqKuMkVGg==
|
||||
version "2.5.6"
|
||||
resolved "https://registry.yarnpkg.com/dompurify/-/dompurify-2.5.6.tgz#8402b501611eaa7fb3786072297fcbe2787f8592"
|
||||
integrity sha512-zUTaUBO8pY4+iJMPE1B9XlO2tXVYIcEA4SNGtvDELzTSCQO7RzH+j7S180BmhmJId78lqGU2z19vgVx2Sxs/PQ==
|
||||
|
||||
domutils@^2.5.2, domutils@^2.6.0:
|
||||
version "2.7.0"
|
||||
@@ -4217,6 +4221,11 @@ electron-to-chromium@^1.4.84:
|
||||
resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.4.89.tgz#33c06592812a17a7131873f4596579084ce33ff8"
|
||||
integrity sha512-z1Axg0Fu54fse8wN4fd+GAINdU5mJmLtcl6bqIcYyzNVGONcfHAeeJi88KYMQVKalhXlYuVPzKkFIU5VD0raUw==
|
||||
|
||||
electron-to-chromium@^1.5.4:
|
||||
version "1.5.27"
|
||||
resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.5.27.tgz#5203ce5d6054857d84ba84d3681cbe59132ade78"
|
||||
integrity sha512-o37j1vZqCoEgBuWWXLHQgTN/KDKe7zwpiY5CPeq2RvUqOyJw9xnrULzZAEVQ5p4h+zjMk7hgtOoPdnLxr7m/jw==
|
||||
|
||||
emittery@^0.13.1:
|
||||
version "0.13.1"
|
||||
resolved "https://registry.yarnpkg.com/emittery/-/emittery-0.13.1.tgz#c04b8c3457490e0847ae51fced3af52d338e3dad"
|
||||
@@ -4242,10 +4251,15 @@ encodeurl@~1.0.2:
|
||||
resolved "https://registry.yarnpkg.com/encodeurl/-/encodeurl-1.0.2.tgz#ad3ff4c86ec2d029322f5a02c3a9a606c95b3f59"
|
||||
integrity sha512-TPJXq8JqFaVYm2CWmPvnP2Iyo4ZSM7/QKcSmuMLDObfpH5fi7RUGmd/rTDf+rut/saiDiQEeVTNgAmJEdAOx0w==
|
||||
|
||||
enhanced-resolve@^5.14.1:
|
||||
version "5.14.1"
|
||||
resolved "https://registry.yarnpkg.com/enhanced-resolve/-/enhanced-resolve-5.14.1.tgz#de684b6803724477a4af5d74ccae5de52c25f6b3"
|
||||
integrity sha512-Vklwq2vDKtl0y/vtwjSesgJ5MYS7Etuk5txS8VdKL4AOS1aUlD96zqIfsOSLQsdv3xgMRbtkWM8eG9XDfKUPow==
|
||||
encodeurl@~2.0.0:
|
||||
version "2.0.0"
|
||||
resolved "https://registry.yarnpkg.com/encodeurl/-/encodeurl-2.0.0.tgz#7b8ea898077d7e409d3ac45474ea38eaf0857a58"
|
||||
integrity sha512-Q0n9HRi4m6JuGIV1eFlmvJB7ZEVxu93IrMyiMsGC0lrMJMWzRgx6WGquyfQgZVb31vhGgXnfmPNNXmxnOkRBrg==
|
||||
|
||||
enhanced-resolve@^5.17.1:
|
||||
version "5.17.1"
|
||||
resolved "https://registry.yarnpkg.com/enhanced-resolve/-/enhanced-resolve-5.17.1.tgz#67bfbbcc2f81d511be77d686a90267ef7f898a15"
|
||||
integrity sha512-LMHl3dXhTcfv8gM4kEzIUeTQ+7fpdA0l2tUf34BddXPkz2A5xJ5L/Pchd5BL6rdccM9QGvu0sWZzK1Z1t4wwyg==
|
||||
dependencies:
|
||||
graceful-fs "^4.2.4"
|
||||
tapable "^2.2.0"
|
||||
@@ -4918,36 +4932,36 @@ expect@^29.0.0, expect@^29.7.0:
|
||||
jest-util "^29.7.0"
|
||||
|
||||
express@^4.17.1:
|
||||
version "4.19.2"
|
||||
resolved "https://registry.yarnpkg.com/express/-/express-4.19.2.tgz#e25437827a3aa7f2a827bc8171bbbb664a356465"
|
||||
integrity sha512-5T6nhjsT+EOMzuck8JjBHARTHfMht0POzlA60WV2pMD3gyXw2LZnZ+ueGdNxG+0calOJcWKbpFcuzLZ91YWq9Q==
|
||||
version "4.20.0"
|
||||
resolved "https://registry.yarnpkg.com/express/-/express-4.20.0.tgz#f1d08e591fcec770c07be4767af8eb9bcfd67c48"
|
||||
integrity sha512-pLdae7I6QqShF5PnNTCVn4hI91Dx0Grkn2+IAsMTgMIKuQVte2dN9PeGSSAME2FR8anOhVA62QDIUaWVfEXVLw==
|
||||
dependencies:
|
||||
accepts "~1.3.8"
|
||||
array-flatten "1.1.1"
|
||||
body-parser "1.20.2"
|
||||
body-parser "1.20.3"
|
||||
content-disposition "0.5.4"
|
||||
content-type "~1.0.4"
|
||||
cookie "0.6.0"
|
||||
cookie-signature "1.0.6"
|
||||
debug "2.6.9"
|
||||
depd "2.0.0"
|
||||
encodeurl "~1.0.2"
|
||||
encodeurl "~2.0.0"
|
||||
escape-html "~1.0.3"
|
||||
etag "~1.8.1"
|
||||
finalhandler "1.2.0"
|
||||
fresh "0.5.2"
|
||||
http-errors "2.0.0"
|
||||
merge-descriptors "1.0.1"
|
||||
merge-descriptors "1.0.3"
|
||||
methods "~1.1.2"
|
||||
on-finished "2.4.1"
|
||||
parseurl "~1.3.3"
|
||||
path-to-regexp "0.1.7"
|
||||
path-to-regexp "0.1.10"
|
||||
proxy-addr "~2.0.7"
|
||||
qs "6.11.0"
|
||||
range-parser "~1.2.1"
|
||||
safe-buffer "5.2.1"
|
||||
send "0.18.0"
|
||||
serve-static "1.15.0"
|
||||
send "0.19.0"
|
||||
serve-static "1.16.0"
|
||||
setprototypeof "1.2.0"
|
||||
statuses "2.0.1"
|
||||
type-is "~1.6.18"
|
||||
@@ -5402,6 +5416,11 @@ graceful-fs@^4.1.2, graceful-fs@^4.2.4, graceful-fs@^4.2.6, graceful-fs@^4.2.9:
|
||||
resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.2.9.tgz#041b05df45755e587a24942279b9d113146e1c96"
|
||||
integrity sha512-NtNxqUcXgpW2iMrfqSfR73Glt39K+BLwWsPs94yR63v45T0Wbej7eRmL5cWfwEgqXnmjQp3zaJTshdRW/qC2ZQ==
|
||||
|
||||
graceful-fs@^4.2.11:
|
||||
version "4.2.11"
|
||||
resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.2.11.tgz#4183e4e8bf08bb6e05bbb2f7d2e0c8f712ca40e3"
|
||||
integrity sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==
|
||||
|
||||
graphemer@^1.4.0:
|
||||
version "1.4.0"
|
||||
resolved "https://registry.yarnpkg.com/graphemer/-/graphemer-1.4.0.tgz#fb2f1d55e0e3a1849aeffc90c4fa0dd53a0e66c6"
|
||||
@@ -6121,7 +6140,7 @@ is-wsl@^2.2.0:
|
||||
isarray@0.0.1:
|
||||
version "0.0.1"
|
||||
resolved "https://registry.yarnpkg.com/isarray/-/isarray-0.0.1.tgz#8a18acfca9a8f4177e09abfc6038939b05d1eedf"
|
||||
integrity sha1-ihis/Kmo9Bd+Cav8YDiTmwXR7t8=
|
||||
integrity sha512-D2S+3GLxWH+uhrNEcoh/fnmYeP8E8/zHl644d/jdA0g2uyXvy3sb0qxotE+ne0LtccHknQzWwZEzhak7oJ0COQ==
|
||||
|
||||
isarray@^2.0.5:
|
||||
version "2.0.5"
|
||||
@@ -6984,10 +7003,10 @@ memfs@^3.4.3:
|
||||
dependencies:
|
||||
fs-monkey "^1.0.4"
|
||||
|
||||
merge-descriptors@1.0.1:
|
||||
version "1.0.1"
|
||||
resolved "https://registry.yarnpkg.com/merge-descriptors/-/merge-descriptors-1.0.1.tgz#b00aaa556dd8b44568150ec9d1b953f3f90cbb61"
|
||||
integrity sha512-cCi6g3/Zr1iqQi6ySbseM1Xvooa98N0w31jzUYrXPX2xqObmFGHJ0tQ5u74H3mVh7wLouTseZyYIq39g8cNp1w==
|
||||
merge-descriptors@1.0.3:
|
||||
version "1.0.3"
|
||||
resolved "https://registry.yarnpkg.com/merge-descriptors/-/merge-descriptors-1.0.3.tgz#d80319a65f3c7935351e5cfdac8f9318504dbed5"
|
||||
integrity sha512-gaNvAS7TZ897/rVaZ0nMtAyxNyi/pdbjbAwUpFQpN70GqnVfOiXpeUUMKRBmzXaSQ8DdTX4/0ms62r2K+hE6mQ==
|
||||
|
||||
merge-stream@^2.0.0:
|
||||
version "2.0.0"
|
||||
@@ -7262,6 +7281,11 @@ node-releases@^2.0.14:
|
||||
resolved "https://registry.yarnpkg.com/node-releases/-/node-releases-2.0.14.tgz#2ffb053bceb8b2be8495ece1ab6ce600c4461b0b"
|
||||
integrity sha512-y10wOWt8yZpqXmOgRo77WaHEmhYQYGNA6y421PKsKYWEK8aW+cqAphborZDhqfyKrbZEN92CN1X2KbafY2s7Yw==
|
||||
|
||||
node-releases@^2.0.18:
|
||||
version "2.0.18"
|
||||
resolved "https://registry.yarnpkg.com/node-releases/-/node-releases-2.0.18.tgz#f010e8d35e2fe8d6b2944f03f70213ecedc4ca3f"
|
||||
integrity sha512-d9VeXT4SJ7ZeOqGX6R5EM022wpL+eWPooLI+5UpWn2jCT1aosUQEhQP214x33Wkwx3JQMvIm+tIoVOdodFS40g==
|
||||
|
||||
node-releases@^2.0.2:
|
||||
version "2.0.2"
|
||||
resolved "https://registry.yarnpkg.com/node-releases/-/node-releases-2.0.2.tgz#7139fe71e2f4f11b47d4d2986aaf8c48699e0c01"
|
||||
@@ -7649,15 +7673,15 @@ path-parse@^1.0.6, path-parse@^1.0.7:
|
||||
resolved "https://registry.yarnpkg.com/path-parse/-/path-parse-1.0.7.tgz#fbc114b60ca42b30d9daf5858e4bd68bbedb6735"
|
||||
integrity sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==
|
||||
|
||||
path-to-regexp@0.1.7:
|
||||
version "0.1.7"
|
||||
resolved "https://registry.yarnpkg.com/path-to-regexp/-/path-to-regexp-0.1.7.tgz#df604178005f522f15eb4490e7247a1bfaa67f8c"
|
||||
integrity sha512-5DFkuoqlv1uYQKxy8omFBeJPQcdoE07Kv2sferDCrAq1ohOU+MSDswDIbnx3YAM60qIOnYa53wBhXW0EbMonrQ==
|
||||
path-to-regexp@0.1.10:
|
||||
version "0.1.10"
|
||||
resolved "https://registry.yarnpkg.com/path-to-regexp/-/path-to-regexp-0.1.10.tgz#67e9108c5c0551b9e5326064387de4763c4d5f8b"
|
||||
integrity sha512-7lf7qcQidTku0Gu3YDPc8DJ1q7OOucfa/BSsIwjuh56VU7katFvuM8hULfkwB3Fns/rsVF7PwPKVw1sl5KQS9w==
|
||||
|
||||
path-to-regexp@^1.7.0:
|
||||
version "1.8.0"
|
||||
resolved "https://registry.yarnpkg.com/path-to-regexp/-/path-to-regexp-1.8.0.tgz#887b3ba9d84393e87a0a0b9f4cb756198b53548a"
|
||||
integrity sha512-n43JRhlUKUAlibEJhPeir1ncUID16QnEjNpwzNdO3Lm4ywrBpBZ5oLD0I6br9evr1Y9JTqwRtAh7JLoOzAQdVA==
|
||||
version "1.9.0"
|
||||
resolved "https://registry.yarnpkg.com/path-to-regexp/-/path-to-regexp-1.9.0.tgz#5dc0753acbf8521ca2e0f137b4578b917b10cf24"
|
||||
integrity sha512-xIp7/apCFJuUHdDLWe8O1HIkb0kQrOMb/0u6FXQjemHn/ii5LrIzU6bdECnsiTF/GjZkMEKg1xdiZwNqDYlZ6g==
|
||||
dependencies:
|
||||
isarray "0.0.1"
|
||||
|
||||
@@ -7976,6 +8000,13 @@ qs@6.11.0, qs@^6.11.0:
|
||||
dependencies:
|
||||
side-channel "^1.0.4"
|
||||
|
||||
qs@6.13.0:
|
||||
version "6.13.0"
|
||||
resolved "https://registry.yarnpkg.com/qs/-/qs-6.13.0.tgz#6ca3bd58439f7e245655798997787b0d88a51906"
|
||||
integrity sha512-+38qI9SOr8tfZ4QmJNplMUxqjbe7LKvvZgWdExBOmd+egZTtjLB67Gu0HRX3u/XOq7UU2Nx6nsjvS16Z9uwfpg==
|
||||
dependencies:
|
||||
side-channel "^1.0.6"
|
||||
|
||||
querystring@0.2.0:
|
||||
version "0.2.0"
|
||||
resolved "https://registry.yarnpkg.com/querystring/-/querystring-0.2.0.tgz#b209849203bb25df820da756e747005878521620"
|
||||
@@ -9068,7 +9099,7 @@ schema-utils@^2.7.1:
|
||||
ajv "^6.12.4"
|
||||
ajv-keywords "^3.5.2"
|
||||
|
||||
schema-utils@^3.1.1, schema-utils@^3.1.2:
|
||||
schema-utils@^3.1.1:
|
||||
version "3.1.2"
|
||||
resolved "https://registry.yarnpkg.com/schema-utils/-/schema-utils-3.1.2.tgz#36c10abca6f7577aeae136c804b0c741edeadc99"
|
||||
integrity sha512-pvjEHOgWc9OWA/f/DE3ohBWTD6EleVLf7iFUkoSwAxttdBhB9QUebQgxER2kWueOvRJXPHNnyrvvh9eZINB8Eg==
|
||||
@@ -9077,6 +9108,15 @@ schema-utils@^3.1.1, schema-utils@^3.1.2:
|
||||
ajv "^6.12.5"
|
||||
ajv-keywords "^3.5.2"
|
||||
|
||||
schema-utils@^3.2.0:
|
||||
version "3.3.0"
|
||||
resolved "https://registry.yarnpkg.com/schema-utils/-/schema-utils-3.3.0.tgz#f50a88877c3c01652a15b622ae9e9795df7a60fe"
|
||||
integrity sha512-pN/yOAvcC+5rQ5nERGuwrjLlYvLTbCibnZ1I7B1LaiAz9BRBlE9GMgE/eqV30P7aJQUf7Ddimy/RsbYO/GrVGg==
|
||||
dependencies:
|
||||
"@types/json-schema" "^7.0.8"
|
||||
ajv "^6.12.5"
|
||||
ajv-keywords "^3.5.2"
|
||||
|
||||
schema-utils@^4.0.0:
|
||||
version "4.0.0"
|
||||
resolved "https://registry.yarnpkg.com/schema-utils/-/schema-utils-4.0.0.tgz#60331e9e3ae78ec5d16353c467c34b3a0a1d3df7"
|
||||
@@ -9159,6 +9199,25 @@ send@0.18.0:
|
||||
range-parser "~1.2.1"
|
||||
statuses "2.0.1"
|
||||
|
||||
send@0.19.0:
|
||||
version "0.19.0"
|
||||
resolved "https://registry.yarnpkg.com/send/-/send-0.19.0.tgz#bbc5a388c8ea6c048967049dbeac0e4a3f09d7f8"
|
||||
integrity sha512-dW41u5VfLXu8SJh5bwRmyYUbAoSB3c9uQh6L8h/KtsFREPWpbX1lrljJo186Jc4nmci/sGUZ9a0a0J2zgfq2hw==
|
||||
dependencies:
|
||||
debug "2.6.9"
|
||||
depd "2.0.0"
|
||||
destroy "1.2.0"
|
||||
encodeurl "~1.0.2"
|
||||
escape-html "~1.0.3"
|
||||
etag "~1.8.1"
|
||||
fresh "0.5.2"
|
||||
http-errors "2.0.0"
|
||||
mime "1.6.0"
|
||||
ms "2.1.3"
|
||||
on-finished "2.4.1"
|
||||
range-parser "~1.2.1"
|
||||
statuses "2.0.1"
|
||||
|
||||
serialize-javascript@^5.0.1:
|
||||
version "5.0.1"
|
||||
resolved "https://registry.yarnpkg.com/serialize-javascript/-/serialize-javascript-5.0.1.tgz#7886ec848049a462467a97d3d918ebb2aaf934f4"
|
||||
@@ -9186,10 +9245,10 @@ serve-index@^1.9.1:
|
||||
mime-types "~2.1.17"
|
||||
parseurl "~1.3.2"
|
||||
|
||||
serve-static@1.15.0:
|
||||
version "1.15.0"
|
||||
resolved "https://registry.yarnpkg.com/serve-static/-/serve-static-1.15.0.tgz#faaef08cffe0a1a62f60cad0c4e513cff0ac9540"
|
||||
integrity sha512-XGuRDNjXUijsUL0vl6nSD7cwURuzEgglbOaFuZM9g3kwDXOWVTck0jLzjPzGD+TazWbboZYu52/9/XPdUgne9g==
|
||||
serve-static@1.16.0:
|
||||
version "1.16.0"
|
||||
resolved "https://registry.yarnpkg.com/serve-static/-/serve-static-1.16.0.tgz#2bf4ed49f8af311b519c46f272bf6ac3baf38a92"
|
||||
integrity sha512-pDLK8zwl2eKaYrs8mrPZBJua4hMplRWJ1tIFksVC3FtBEBnl8dxgeHtsaMS8DhS9i4fLObaon6ABoc4/hQGdPA==
|
||||
dependencies:
|
||||
encodeurl "~1.0.2"
|
||||
escape-html "~1.0.3"
|
||||
@@ -9784,16 +9843,16 @@ teeny-request@7.1.1:
|
||||
stream-events "^1.0.5"
|
||||
uuid "^8.0.0"
|
||||
|
||||
terser-webpack-plugin@^5.3.7:
|
||||
version "5.3.9"
|
||||
resolved "https://registry.yarnpkg.com/terser-webpack-plugin/-/terser-webpack-plugin-5.3.9.tgz#832536999c51b46d468067f9e37662a3b96adfe1"
|
||||
integrity sha512-ZuXsqE07EcggTWQjXUj+Aot/OMcD0bMKGgF63f7UxYcu5/AJF53aIpK1YoP5xR9l6s/Hy2b+t1AM0bLNPRuhwA==
|
||||
terser-webpack-plugin@^5.3.10:
|
||||
version "5.3.10"
|
||||
resolved "https://registry.yarnpkg.com/terser-webpack-plugin/-/terser-webpack-plugin-5.3.10.tgz#904f4c9193c6fd2a03f693a2150c62a92f40d199"
|
||||
integrity sha512-BKFPWlPDndPs+NGGCr1U59t0XScL5317Y0UReNrHaw9/FwhPENlq6bfgs+4yPfyP51vqC1bQ4rp1EfXW5ZSH9w==
|
||||
dependencies:
|
||||
"@jridgewell/trace-mapping" "^0.3.17"
|
||||
"@jridgewell/trace-mapping" "^0.3.20"
|
||||
jest-worker "^27.4.5"
|
||||
schema-utils "^3.1.1"
|
||||
serialize-javascript "^6.0.1"
|
||||
terser "^5.16.8"
|
||||
terser "^5.26.0"
|
||||
|
||||
terser@^5.10.0:
|
||||
version "5.14.2"
|
||||
@@ -9805,13 +9864,13 @@ terser@^5.10.0:
|
||||
commander "^2.20.0"
|
||||
source-map-support "~0.5.20"
|
||||
|
||||
terser@^5.16.8:
|
||||
version "5.17.6"
|
||||
resolved "https://registry.yarnpkg.com/terser/-/terser-5.17.6.tgz#d810e75e1bb3350c799cd90ebefe19c9412c12de"
|
||||
integrity sha512-V8QHcs8YuyLkLHsJO5ucyff1ykrLVsR4dNnS//L5Y3NiSXpbK1J+WMVUs67eI0KTxs9JtHhgEQpXQVHlHI92DQ==
|
||||
terser@^5.26.0:
|
||||
version "5.33.0"
|
||||
resolved "https://registry.yarnpkg.com/terser/-/terser-5.33.0.tgz#8f9149538c7468ffcb1246cfec603c16720d2db1"
|
||||
integrity sha512-JuPVaB7s1gdFKPKTelwUyRq5Sid2A3Gko2S0PncwdBq7kN9Ti9HPWDQ06MPsEDGsZeVESjKEnyGy68quBk1w6g==
|
||||
dependencies:
|
||||
"@jridgewell/source-map" "^0.3.2"
|
||||
acorn "^8.5.0"
|
||||
"@jridgewell/source-map" "^0.3.3"
|
||||
acorn "^8.8.2"
|
||||
commander "^2.20.0"
|
||||
source-map-support "~0.5.20"
|
||||
|
||||
@@ -10129,6 +10188,14 @@ update-browserslist-db@^1.0.13:
|
||||
escalade "^3.1.2"
|
||||
picocolors "^1.0.1"
|
||||
|
||||
update-browserslist-db@^1.1.0:
|
||||
version "1.1.0"
|
||||
resolved "https://registry.yarnpkg.com/update-browserslist-db/-/update-browserslist-db-1.1.0.tgz#7ca61c0d8650766090728046e416a8cde682859e"
|
||||
integrity sha512-EdRAaAyk2cUE1wOf2DkEhzxqOQvFOoRJFNS6NeyJ01Gp2beMRpBAINjM2iDXE3KCuKhwnvHIQCJm6ThL2Z+HzQ==
|
||||
dependencies:
|
||||
escalade "^3.1.2"
|
||||
picocolors "^1.0.1"
|
||||
|
||||
uri-js@^4.2.2:
|
||||
version "4.2.2"
|
||||
resolved "https://registry.yarnpkg.com/uri-js/-/uri-js-4.2.2.tgz#94c540e1ff772956e2299507c010aea6c8838eb0"
|
||||
@@ -10241,10 +10308,10 @@ warning@^4.0.1, warning@^4.0.2:
|
||||
dependencies:
|
||||
loose-envify "^1.0.0"
|
||||
|
||||
watchpack@^2.4.0:
|
||||
version "2.4.0"
|
||||
resolved "https://registry.yarnpkg.com/watchpack/-/watchpack-2.4.0.tgz#fa33032374962c78113f93c7f2fb4c54c9862a5d"
|
||||
integrity sha512-Lcvm7MGST/4fup+ifyKi2hjyIAwcdI4HRgtvTpIUxBRhB+RFtUh8XtDOxUfctVCnhVi+QQj49i91OyvzkJl6cg==
|
||||
watchpack@^2.4.1:
|
||||
version "2.4.2"
|
||||
resolved "https://registry.yarnpkg.com/watchpack/-/watchpack-2.4.2.tgz#2feeaed67412e7c33184e5a79ca738fbd38564da"
|
||||
integrity sha512-TnbFSbcOCcDgjZ4piURLCbJ3nJhznVh9kw6F6iokjiFPl8ONxe9A6nMDVXDiNbrSfLILs6vB07F7wLBrwPYzJw==
|
||||
dependencies:
|
||||
glob-to-regexp "^0.4.1"
|
||||
graceful-fs "^4.1.2"
|
||||
@@ -10360,34 +10427,33 @@ webpack-sources@^3.2.3:
|
||||
resolved "https://registry.yarnpkg.com/webpack-sources/-/webpack-sources-3.2.3.tgz#2d4daab8451fd4b240cc27055ff6a0c2ccea0cde"
|
||||
integrity sha512-/DyMEOrDgLKKIG0fmvtz+4dUX/3Ghozwgm6iPp8KRhvn+eQf9+Q7GWxVNMk3+uCPWfdXYC4ExGBckIXdFEfH1w==
|
||||
|
||||
webpack@^5.84.1:
|
||||
version "5.84.1"
|
||||
resolved "https://registry.yarnpkg.com/webpack/-/webpack-5.84.1.tgz#d4493acdeca46b26ffc99d86d784cabfeb925a15"
|
||||
integrity sha512-ZP4qaZ7vVn/K8WN/p990SGATmrL1qg4heP/MrVneczYtpDGJWlrgZv55vxaV2ul885Kz+25MP2kSXkPe3LZfmg==
|
||||
webpack@^5.94.0:
|
||||
version "5.94.0"
|
||||
resolved "https://registry.yarnpkg.com/webpack/-/webpack-5.94.0.tgz#77a6089c716e7ab90c1c67574a28da518a20970f"
|
||||
integrity sha512-KcsGn50VT+06JH/iunZJedYGUJS5FGjow8wb9c0v5n1Om8O1g4L6LjtfxwlXIATopoQu+vOXXa7gYisWxCoPyg==
|
||||
dependencies:
|
||||
"@types/eslint-scope" "^3.7.3"
|
||||
"@types/estree" "^1.0.0"
|
||||
"@webassemblyjs/ast" "^1.11.5"
|
||||
"@webassemblyjs/wasm-edit" "^1.11.5"
|
||||
"@webassemblyjs/wasm-parser" "^1.11.5"
|
||||
"@types/estree" "^1.0.5"
|
||||
"@webassemblyjs/ast" "^1.12.1"
|
||||
"@webassemblyjs/wasm-edit" "^1.12.1"
|
||||
"@webassemblyjs/wasm-parser" "^1.12.1"
|
||||
acorn "^8.7.1"
|
||||
acorn-import-assertions "^1.9.0"
|
||||
browserslist "^4.14.5"
|
||||
acorn-import-attributes "^1.9.5"
|
||||
browserslist "^4.21.10"
|
||||
chrome-trace-event "^1.0.2"
|
||||
enhanced-resolve "^5.14.1"
|
||||
enhanced-resolve "^5.17.1"
|
||||
es-module-lexer "^1.2.1"
|
||||
eslint-scope "5.1.1"
|
||||
events "^3.2.0"
|
||||
glob-to-regexp "^0.4.1"
|
||||
graceful-fs "^4.2.9"
|
||||
graceful-fs "^4.2.11"
|
||||
json-parse-even-better-errors "^2.3.1"
|
||||
loader-runner "^4.2.0"
|
||||
mime-types "^2.1.27"
|
||||
neo-async "^2.6.2"
|
||||
schema-utils "^3.1.2"
|
||||
schema-utils "^3.2.0"
|
||||
tapable "^2.1.1"
|
||||
terser-webpack-plugin "^5.3.7"
|
||||
watchpack "^2.4.0"
|
||||
terser-webpack-plugin "^5.3.10"
|
||||
watchpack "^2.4.1"
|
||||
webpack-sources "^3.2.3"
|
||||
|
||||
websocket-driver@>=0.5.1:
|
||||
|
||||
@@ -1132,7 +1132,7 @@ func GetAppEventLabels(app *argoappv1.Application, projLister applicationsv1.App
|
||||
// Filter out event labels to include
|
||||
inKeys := settingsManager.GetIncludeEventLabelKeys()
|
||||
for k, v := range labels {
|
||||
found := glob.MatchStringInList(inKeys, k, false)
|
||||
found := glob.MatchStringInList(inKeys, k, glob.GLOB)
|
||||
if found {
|
||||
eventLabels[k] = v
|
||||
}
|
||||
@@ -1141,7 +1141,7 @@ func GetAppEventLabels(app *argoappv1.Application, projLister applicationsv1.App
|
||||
// Remove excluded event labels
|
||||
exKeys := settingsManager.GetExcludeEventLabelKeys()
|
||||
for k := range eventLabels {
|
||||
found := glob.MatchStringInList(exKeys, k, false)
|
||||
found := glob.MatchStringInList(exKeys, k, glob.GLOB)
|
||||
if found {
|
||||
delete(eventLabels, k)
|
||||
}
|
||||
|
||||
@@ -4,6 +4,7 @@ import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
|
||||
log "github.com/sirupsen/logrus"
|
||||
v1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
|
||||
"sigs.k8s.io/structured-merge-diff/v4/fieldpath"
|
||||
@@ -28,12 +29,15 @@ func Normalize(live, config *unstructured.Unstructured, trustedManagers []string
|
||||
|
||||
liveCopy := live.DeepCopy()
|
||||
configCopy := config.DeepCopy()
|
||||
normalized := false
|
||||
|
||||
results, err := newTypedResults(liveCopy, configCopy, pt)
|
||||
// error might happen if the resources are not parsable and so cannot be normalized
|
||||
if err != nil {
|
||||
return nil, nil, fmt.Errorf("error building typed results: %w", err)
|
||||
log.Debugf("error building typed results: %v", err)
|
||||
return liveCopy, configCopy, nil
|
||||
}
|
||||
|
||||
normalized := false
|
||||
for _, mf := range live.GetManagedFields() {
|
||||
if trustedManager(mf.Manager, trustedManagers) {
|
||||
err := normalize(mf, results)
|
||||
|
||||
@@ -141,6 +141,16 @@ func TestNormalize(t *testing.T) {
|
||||
assert.Len(t, vwcConfig.Webhooks, 1)
|
||||
assert.Equal(t, "", string(vwcConfig.Webhooks[0].ClientConfig.CABundle))
|
||||
})
|
||||
t.Run("does not fail if object fails validation schema", func(t *testing.T) {
|
||||
desiredState := StrToUnstructured(testdata.DesiredDeploymentYaml)
|
||||
require.NoError(t, unstructured.SetNestedField(desiredState.Object, "spec", "hello", "world"))
|
||||
liveState := StrToUnstructured(testdata.LiveDeploymentWithManagedReplicaYaml)
|
||||
|
||||
pt := parser.Type("io.k8s.api.apps.v1.Deployment")
|
||||
|
||||
_, _, err := managedfields.Normalize(liveState, desiredState, []string{}, &pt)
|
||||
require.NoError(t, err)
|
||||
})
|
||||
}
|
||||
|
||||
func validateNestedFloat64(t *testing.T, expected float64, obj *unstructured.Unstructured, fields ...string) {
|
||||
|
||||
@@ -630,11 +630,7 @@ func (m *nativeGitClient) lsRemote(revision string) (string, error) {
|
||||
revision = "HEAD"
|
||||
}
|
||||
|
||||
semverSha, err := m.resolveSemverRevision(revision, refs)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
semverSha := m.resolveSemverRevision(revision, refs)
|
||||
if semverSha != "" {
|
||||
return semverSha, nil
|
||||
}
|
||||
@@ -682,21 +678,29 @@ func (m *nativeGitClient) lsRemote(revision string) (string, error) {
|
||||
|
||||
// If we get here, revision string had non hexadecimal characters (indicating its a branch, tag,
|
||||
// or symbolic ref) and we were unable to resolve it to a commit SHA.
|
||||
return "", fmt.Errorf("Unable to resolve '%s' to a commit SHA", revision)
|
||||
return "", fmt.Errorf("unable to resolve '%s' to a commit SHA", revision)
|
||||
}
|
||||
|
||||
// resolveSemverRevision is a part of the lsRemote method workflow.
|
||||
// When the user configure correctly the Git repository revision and the revision is a valid semver constraint
|
||||
// only the for loop in this function will run, otherwise the lsRemote loop will try to resolve the revision.
|
||||
// Some examples to illustrate the actual behavior, if:
|
||||
// * The revision is "v0.1.*"/"0.1.*" or "v0.1.2"/"0.1.2" and there's a tag matching that constraint only this function loop will run;
|
||||
// * The revision is "v0.1.*"/"0.1.*" or "0.1.2"/"0.1.2" and there is no tag matching that constraint this function loop and lsRemote loop will run for backward compatibility;
|
||||
// * The revision is "custom-tag" only the lsRemote loop will run because that revision is an invalid semver;
|
||||
// * The revision is "master-branch" only the lsRemote loop will run because that revision is an invalid semver;
|
||||
func (m *nativeGitClient) resolveSemverRevision(revision string, refs []*plumbing.Reference) (string, error) {
|
||||
// When the user correctly configures the Git repository revision, and that revision is a valid semver constraint, we
|
||||
// use this logic path rather than the standard lsRemote revision resolution loop.
|
||||
// Some examples to illustrate the actual behavior - if the revision is:
|
||||
// * "v0.1.2"/"0.1.2" or "v0.1"/"0.1", then this is not a constraint, it's a pinned version - so we fall back to the standard tag matching in the lsRemote loop.
|
||||
// * "v0.1.*"/"0.1.*", and there's a tag matching that constraint, then we find the latest matching version and return its commit hash.
|
||||
// * "v0.1.*"/"0.1.*", and there is *no* tag matching that constraint, then we fall back to the standard tag matching in the lsRemote loop.
|
||||
// * "custom-tag", only the lsRemote loop will run - because that revision is an invalid semver;
|
||||
// * "master-branch", only the lsRemote loop will run because that revision is an invalid semver;
|
||||
func (m *nativeGitClient) resolveSemverRevision(revision string, refs []*plumbing.Reference) string {
|
||||
if _, err := semver.NewVersion(revision); err == nil {
|
||||
// If the revision is a valid version, then we know it isn't a constraint; it's just a pin.
|
||||
// In which case, we should use standard tag resolution mechanisms.
|
||||
return ""
|
||||
}
|
||||
|
||||
constraint, err := semver.NewConstraint(revision)
|
||||
if err != nil {
|
||||
return "", nil
|
||||
log.Debugf("Revision '%s' is not a valid semver constraint, skipping semver resolution.", revision)
|
||||
return ""
|
||||
}
|
||||
|
||||
maxVersion := semver.New(0, 0, 0, "", "")
|
||||
@@ -709,12 +713,9 @@ func (m *nativeGitClient) resolveSemverRevision(revision string, refs []*plumbin
|
||||
tag := ref.Name().Short()
|
||||
version, err := semver.NewVersion(tag)
|
||||
if err != nil {
|
||||
if errors.Is(err, semver.ErrInvalidSemVer) {
|
||||
log.Debugf("Invalid semantic version: %s", tag)
|
||||
continue
|
||||
}
|
||||
|
||||
return "", fmt.Errorf("error parsing version for tag: %w", err)
|
||||
log.Debugf("Error parsing version for tag: '%s': %v", tag, err)
|
||||
// Skip this tag and continue to the next one
|
||||
continue
|
||||
}
|
||||
|
||||
if constraint.Check(version) {
|
||||
@@ -726,10 +727,11 @@ func (m *nativeGitClient) resolveSemverRevision(revision string, refs []*plumbin
|
||||
}
|
||||
|
||||
if maxVersionHash.IsZero() {
|
||||
return "", nil
|
||||
return ""
|
||||
}
|
||||
|
||||
return maxVersionHash.String(), nil
|
||||
log.Debugf("Semver constraint '%s' resolved to tag '%s', at reference '%s'", revision, maxVersion.Original(), maxVersionHash.String())
|
||||
return maxVersionHash.String()
|
||||
}
|
||||
|
||||
// CommitSHA returns current commit sha from `git rev-parse HEAD`
|
||||
|
||||
@@ -173,6 +173,148 @@ func Test_ChangedFiles(t *testing.T) {
|
||||
assert.ElementsMatch(t, []string{"README"}, changedFiles)
|
||||
}
|
||||
|
||||
func Test_SemverTags(t *testing.T) {
|
||||
tempDir := t.TempDir()
|
||||
|
||||
client, err := NewClientExt(fmt.Sprintf("file://%s", tempDir), tempDir, NopCreds{}, true, false, "")
|
||||
require.NoError(t, err)
|
||||
|
||||
err = client.Init()
|
||||
require.NoError(t, err)
|
||||
|
||||
mapTagRefs := map[string]string{}
|
||||
for _, tag := range []string{
|
||||
"v1.0.0-rc1",
|
||||
"v1.0.0-rc2",
|
||||
"v1.0.0",
|
||||
"v1.0",
|
||||
"v1.0.1",
|
||||
"v1.1.0",
|
||||
"2024-apple",
|
||||
"2024-banana",
|
||||
} {
|
||||
err = runCmd(client.Root(), "git", "commit", "-m", tag+" commit", "--allow-empty")
|
||||
require.NoError(t, err)
|
||||
|
||||
// Create an rc semver tag
|
||||
err = runCmd(client.Root(), "git", "tag", tag)
|
||||
require.NoError(t, err)
|
||||
|
||||
sha, err := client.LsRemote("HEAD")
|
||||
require.NoError(t, err)
|
||||
|
||||
mapTagRefs[tag] = sha
|
||||
}
|
||||
|
||||
for _, tc := range []struct {
|
||||
name string
|
||||
ref string
|
||||
expected string
|
||||
error bool
|
||||
}{{
|
||||
name: "pinned rc version",
|
||||
ref: "v1.0.0-rc1",
|
||||
expected: mapTagRefs["v1.0.0-rc1"],
|
||||
}, {
|
||||
name: "lt rc constraint",
|
||||
ref: "< v1.0.0-rc3",
|
||||
expected: mapTagRefs["v1.0.0-rc2"],
|
||||
}, {
|
||||
name: "pinned major version",
|
||||
ref: "v1.0.0",
|
||||
expected: mapTagRefs["v1.0.0"],
|
||||
}, {
|
||||
name: "pinned patch version",
|
||||
ref: "v1.0.1",
|
||||
expected: mapTagRefs["v1.0.1"],
|
||||
}, {
|
||||
name: "pinned minor version",
|
||||
ref: "v1.1.0",
|
||||
expected: mapTagRefs["v1.1.0"],
|
||||
}, {
|
||||
name: "patch wildcard constraint",
|
||||
ref: "v1.0.*",
|
||||
expected: mapTagRefs["v1.0.1"],
|
||||
}, {
|
||||
name: "patch tilde constraint",
|
||||
ref: "~v1.0.0",
|
||||
expected: mapTagRefs["v1.0.1"],
|
||||
}, {
|
||||
name: "minor wildcard constraint",
|
||||
ref: "v1.*",
|
||||
expected: mapTagRefs["v1.1.0"],
|
||||
}, {
|
||||
// The semver library allows for using both * and x as the wildcard modifier.
|
||||
name: "alternative minor wildcard constraint",
|
||||
ref: "v1.x",
|
||||
expected: mapTagRefs["v1.1.0"],
|
||||
}, {
|
||||
name: "minor gte constraint",
|
||||
ref: ">= v1.0.0",
|
||||
expected: mapTagRefs["v1.1.0"],
|
||||
}, {
|
||||
name: "multiple constraints",
|
||||
ref: "> v1.0.0 < v1.1.0",
|
||||
expected: mapTagRefs["v1.0.1"],
|
||||
}, {
|
||||
// We treat non-specific semver versions as regular tags, rather than constraints.
|
||||
name: "non-specific version",
|
||||
ref: "v1.0",
|
||||
expected: mapTagRefs["v1.0"],
|
||||
}, {
|
||||
// Which means a missing tag will raise an error.
|
||||
name: "missing non-specific version",
|
||||
ref: "v1.1",
|
||||
error: true,
|
||||
}, {
|
||||
// This is NOT a semver constraint, so it should always resolve to itself - because specifying a tag should
|
||||
// return the commit for that tag.
|
||||
// semver/v3 has the unfortunate semver-ish behaviour where any tag starting with a number is considered to be
|
||||
// "semver-ish", where that number is the semver major version, and the rest then gets coerced into a beta
|
||||
// version string. This can cause unexpected behaviour with constraints logic.
|
||||
// In this case, if the tag is being incorrectly coerced into semver (for being semver-ish), it will incorrectly
|
||||
// return the commit for the 2024-banana tag; which we want to avoid.
|
||||
name: "apple non-semver tag",
|
||||
ref: "2024-apple",
|
||||
expected: mapTagRefs["2024-apple"],
|
||||
}, {
|
||||
name: "banana non-semver tag",
|
||||
ref: "2024-banana",
|
||||
expected: mapTagRefs["2024-banana"],
|
||||
}, {
|
||||
// A semver version (without constraints) should ONLY match itself.
|
||||
// We do not want "2024-apple" to get "semver-ish'ed" into matching "2024.0.0-apple"; they're different tags.
|
||||
name: "no semver tag coercion",
|
||||
ref: "2024.0.0-apple",
|
||||
error: true,
|
||||
}, {
|
||||
// No minor versions are specified, so we would expect a major version of 2025 or more.
|
||||
// This is because if we specify > 11 in semver, we would not expect 11.1.0 to pass; it should be 12.0.0 or more.
|
||||
// Similarly, if we were to specify > 11.0, we would expect 11.1.0 or more.
|
||||
name: "semver constraints on non-semver tags",
|
||||
ref: "> 2024-apple",
|
||||
error: true,
|
||||
}, {
|
||||
// However, if one specifies the minor/patch versions, semver constraints can be used to match non-semver tags.
|
||||
// 2024-banana is considered as "2024.0.0-banana" in semver-ish, and banana > apple, so it's a match.
|
||||
// Note: this is more for documentation and future reference than real testing, as it seems like quite odd behaviour.
|
||||
name: "semver constraints on non-semver tags",
|
||||
ref: "> 2024.0.0-apple",
|
||||
expected: mapTagRefs["2024-banana"],
|
||||
}} {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
commitSHA, err := client.LsRemote(tc.ref)
|
||||
if tc.error {
|
||||
require.Error(t, err)
|
||||
return
|
||||
}
|
||||
require.NoError(t, err)
|
||||
assert.True(t, IsCommitSHA(commitSHA))
|
||||
assert.Equal(t, tc.expected, commitSHA)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func Test_nativeGitClient_Submodule(t *testing.T) {
|
||||
tempDir, err := os.MkdirTemp("", "")
|
||||
require.NoError(t, err)
|
||||
|
||||
@@ -232,15 +232,10 @@ func TestLsRemote(t *testing.T) {
|
||||
expectedCommit: "ff87d8cb9e669d3738434733ecba3c6dd2c64d70",
|
||||
},
|
||||
{
|
||||
name: "should resolve a pined tag with semantic versioning",
|
||||
name: "should resolve a pinned tag with semantic versioning",
|
||||
revision: "v0.8.0",
|
||||
expectedCommit: "d7c04ae24c16f8ec611b0331596fbc595537abe9",
|
||||
},
|
||||
{
|
||||
name: "should resolve a pined tag with semantic versioning without the 'v' prefix",
|
||||
revision: "0.8.0",
|
||||
expectedCommit: "d7c04ae24c16f8ec611b0331596fbc595537abe9",
|
||||
},
|
||||
{
|
||||
name: "should resolve a range tag with semantic versioning",
|
||||
revision: "v0.8.*", // it should resolve to v0.8.2
|
||||
@@ -298,7 +293,7 @@ func TestLsRemote(t *testing.T) {
|
||||
|
||||
for _, revision := range xfail {
|
||||
_, err := clnt.LsRemote(revision)
|
||||
assert.ErrorContains(t, err, "Unable to resolve")
|
||||
assert.ErrorContains(t, err, "unable to resolve")
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
@@ -31,26 +31,28 @@ func Test_Match(t *testing.T) {
|
||||
|
||||
func Test_MatchList(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
input string
|
||||
list []string
|
||||
exact bool
|
||||
result bool
|
||||
name string
|
||||
input string
|
||||
list []string
|
||||
patternMatch string
|
||||
result bool
|
||||
}{
|
||||
{"Exact name in list", "test", []string{"test"}, true, true},
|
||||
{"Exact name not in list", "test", []string{"other"}, true, false},
|
||||
{"Exact name not in list, multiple elements", "test", []string{"some", "other"}, true, false},
|
||||
{"Exact name not in list, list empty", "test", []string{}, true, false},
|
||||
{"Exact name not in list, empty element", "test", []string{""}, true, false},
|
||||
{"Glob name in list, but exact wanted", "test", []string{"*"}, true, false},
|
||||
{"Glob name in list with simple wildcard", "test", []string{"*"}, false, true},
|
||||
{"Glob name in list without wildcard", "test", []string{"test"}, false, true},
|
||||
{"Glob name in list, multiple elements", "test", []string{"other*", "te*"}, false, true},
|
||||
{"Exact name in list", "test", []string{"test"}, EXACT, true},
|
||||
{"Exact name not in list", "test", []string{"other"}, EXACT, false},
|
||||
{"Exact name not in list, multiple elements", "test", []string{"some", "other"}, EXACT, false},
|
||||
{"Exact name not in list, list empty", "test", []string{}, EXACT, false},
|
||||
{"Exact name not in list, empty element", "test", []string{""}, EXACT, false},
|
||||
{"Glob name in list, but exact wanted", "test", []string{"*"}, EXACT, false},
|
||||
{"Glob name in list with simple wildcard", "test", []string{"*"}, GLOB, true},
|
||||
{"Glob name in list without wildcard", "test", []string{"test"}, GLOB, true},
|
||||
{"Glob name in list, multiple elements", "test", []string{"other*", "te*"}, GLOB, true},
|
||||
{"match everything but specified word: fail", "disallowed", []string{"/^((?!disallowed).)*$/"}, REGEXP, false},
|
||||
{"match everything but specified word: pass", "allowed", []string{"/^((?!disallowed).)*$/"}, REGEXP, true},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
res := MatchStringInList(tt.list, tt.input, tt.exact)
|
||||
res := MatchStringInList(tt.list, tt.input, tt.patternMatch)
|
||||
assert.Equal(t, tt.result, res)
|
||||
})
|
||||
}
|
||||
|
||||
@@ -1,10 +1,30 @@
|
||||
package glob
|
||||
|
||||
// MatchStringInList will return true if item is contained in list. If
|
||||
// exactMatch is set to false, list may contain globs to be matched.
|
||||
func MatchStringInList(list []string, item string, exactMatch bool) bool {
|
||||
import (
|
||||
"strings"
|
||||
|
||||
"github.com/argoproj/argo-cd/v2/util/regex"
|
||||
)
|
||||
|
||||
const (
|
||||
EXACT = "exact"
|
||||
GLOB = "glob"
|
||||
REGEXP = "regexp"
|
||||
)
|
||||
|
||||
// MatchStringInList will return true if item is contained in list.
|
||||
// patternMatch; can be set to exact, glob, regexp.
|
||||
// If patternMatch; is set to exact, the item must be an exact match.
|
||||
// If patternMatch; is set to glob, the item must match a glob pattern.
|
||||
// If patternMatch; is set to regexp, the item must match a regular expression or glob.
|
||||
func MatchStringInList(list []string, item string, patternMatch string) bool {
|
||||
for _, ll := range list {
|
||||
if item == ll || (!exactMatch && Match(ll, item)) {
|
||||
// If string is wrapped in "/", assume it is a regular expression.
|
||||
if patternMatch == REGEXP && strings.HasPrefix(ll, "/") && strings.HasSuffix(ll, "/") && regex.Match(ll[1:len(ll)-1], item) {
|
||||
return true
|
||||
} else if (patternMatch == REGEXP || patternMatch == GLOB) && Match(ll, item) {
|
||||
return true
|
||||
} else if patternMatch == EXACT && item == ll {
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
20
util/regex/regex.go
Normal file
20
util/regex/regex.go
Normal file
@@ -0,0 +1,20 @@
|
||||
package regex
|
||||
|
||||
import (
|
||||
"github.com/dlclark/regexp2"
|
||||
log "github.com/sirupsen/logrus"
|
||||
)
|
||||
|
||||
func Match(pattern, text string) bool {
|
||||
compiledRegex, err := regexp2.Compile(pattern, 0)
|
||||
if err != nil {
|
||||
log.Warnf("failed to compile pattern %s due to error %v", pattern, err)
|
||||
return false
|
||||
}
|
||||
regexMatch, err := compiledRegex.MatchString(text)
|
||||
if err != nil {
|
||||
log.Warnf("failed to match pattern %s due to error %v", pattern, err)
|
||||
return false
|
||||
}
|
||||
return regexMatch
|
||||
}
|
||||
@@ -7,7 +7,7 @@ import (
|
||||
)
|
||||
|
||||
func IsNamespaceEnabled(namespace string, serverNamespace string, enabledNamespaces []string) bool {
|
||||
return namespace == serverNamespace || glob.MatchStringInList(enabledNamespaces, namespace, false)
|
||||
return namespace == serverNamespace || glob.MatchStringInList(enabledNamespaces, namespace, glob.REGEXP)
|
||||
}
|
||||
|
||||
func NamespaceNotPermittedError(namespace string) error {
|
||||
|
||||
@@ -49,6 +49,20 @@ func Test_IsNamespaceEnabled(t *testing.T) {
|
||||
[]string{"allowed"},
|
||||
false,
|
||||
},
|
||||
{
|
||||
"match everything but specified word: fail",
|
||||
"disallowed",
|
||||
"argocd",
|
||||
[]string{"/^((?!disallowed).)*$/"},
|
||||
false,
|
||||
},
|
||||
{
|
||||
"match everything but specified word: pass",
|
||||
"allowed",
|
||||
"argocd",
|
||||
[]string{"/^((?!disallowed).)*$/"},
|
||||
true,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tc := range testCases {
|
||||
|
||||
@@ -278,7 +278,7 @@ func (a *ArgoCDWebhookHandler) HandleEvent(payload interface{}) {
|
||||
// nor in the list of enabled namespaces.
|
||||
var filteredApps []v1alpha1.Application
|
||||
for _, app := range apps.Items {
|
||||
if app.Namespace == a.ns || glob.MatchStringInList(a.appNs, app.Namespace, false) {
|
||||
if app.Namespace == a.ns || glob.MatchStringInList(a.appNs, app.Namespace, glob.REGEXP) {
|
||||
filteredApps = append(filteredApps, app)
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user