mirror of
https://github.com/argoproj/argo-cd.git
synced 2026-02-22 18:48:46 +01:00
Compare commits
50 Commits
v3.2.0
...
release-3.
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
48549a2035 | ||
|
|
10c3fd02f4 | ||
|
|
ca08f90e96 | ||
|
|
1f03b27fd5 | ||
|
|
9c128e2d4c | ||
|
|
75eddbd910 | ||
|
|
65b029342d | ||
|
|
2ff406ae33 | ||
|
|
76fc92f655 | ||
|
|
ad117b88a6 | ||
|
|
508da9c791 | ||
|
|
20866f4557 | ||
|
|
e3b108b738 | ||
|
|
c56f4400f2 | ||
|
|
e9d03a633e | ||
|
|
030b4f982b | ||
|
|
fafbd44489 | ||
|
|
d7d9674e33 | ||
|
|
e6f54030f0 | ||
|
|
b4146969ed | ||
|
|
51c6375130 | ||
|
|
b67eb40a45 | ||
|
|
8a0633b74a | ||
|
|
0d4f505954 | ||
|
|
2b6251dfed | ||
|
|
8f903c3a11 | ||
|
|
8d0dde1388 | ||
|
|
784f62ca6d | ||
|
|
33b5043405 | ||
|
|
88fe638aff | ||
|
|
a29703877e | ||
|
|
95e7cdb16f | ||
|
|
122f4db3db | ||
|
|
2d65b26420 | ||
|
|
0ace9bb9a3 | ||
|
|
6398ec3dcb | ||
|
|
732b16fb2a | ||
|
|
024c7e6020 | ||
|
|
26b7fb2c61 | ||
|
|
8c4ab63a9c | ||
|
|
29f869c82f | ||
|
|
c11e67d4bf | ||
|
|
a0a18438ab | ||
|
|
dabdf39772 | ||
|
|
cd8df1721c | ||
|
|
27c5065308 | ||
|
|
1545390cd8 | ||
|
|
7bd02d7f02 | ||
|
|
86c9994394 | ||
|
|
6dd5e7a6d2 |
8
.github/workflows/ci-build.yaml
vendored
8
.github/workflows/ci-build.yaml
vendored
@@ -14,7 +14,7 @@ on:
|
||||
env:
|
||||
# Golang version to use across CI steps
|
||||
# renovate: datasource=golang-version packageName=golang
|
||||
GOLANG_VERSION: '1.25.0'
|
||||
GOLANG_VERSION: '1.25.6'
|
||||
|
||||
concurrency:
|
||||
group: ${{ github.workflow }}-${{ github.ref }}
|
||||
@@ -414,14 +414,14 @@ jobs:
|
||||
# latest: true means that this version mush upload the coverage report to codecov.io
|
||||
# We designate the latest version because we only collect code coverage for that version.
|
||||
k3s:
|
||||
- version: v1.33.1
|
||||
- version: v1.34.2
|
||||
latest: true
|
||||
- version: v1.33.1
|
||||
latest: false
|
||||
- version: v1.32.1
|
||||
latest: false
|
||||
- version: v1.31.0
|
||||
latest: false
|
||||
- version: v1.30.4
|
||||
latest: false
|
||||
needs:
|
||||
- build-go
|
||||
- changes
|
||||
|
||||
4
.github/workflows/image.yaml
vendored
4
.github/workflows/image.yaml
vendored
@@ -53,7 +53,7 @@ jobs:
|
||||
with:
|
||||
# Note: cannot use env variables to set go-version (https://docs.github.com/en/actions/using-workflows/reusing-workflows#limitations)
|
||||
# renovate: datasource=golang-version packageName=golang
|
||||
go-version: 1.25.0
|
||||
go-version: 1.25.6
|
||||
platforms: ${{ needs.set-vars.outputs.platforms }}
|
||||
push: false
|
||||
|
||||
@@ -70,7 +70,7 @@ jobs:
|
||||
ghcr_image_name: ghcr.io/argoproj/argo-cd/argocd:${{ needs.set-vars.outputs.image-tag }}
|
||||
# Note: cannot use env variables to set go-version (https://docs.github.com/en/actions/using-workflows/reusing-workflows#limitations)
|
||||
# renovate: datasource=golang-version packageName=golang
|
||||
go-version: 1.25.0
|
||||
go-version: 1.25.6
|
||||
platforms: ${{ needs.set-vars.outputs.platforms }}
|
||||
push: true
|
||||
secrets:
|
||||
|
||||
4
.github/workflows/release.yaml
vendored
4
.github/workflows/release.yaml
vendored
@@ -11,7 +11,7 @@ permissions: {}
|
||||
|
||||
env:
|
||||
# renovate: datasource=golang-version packageName=golang
|
||||
GOLANG_VERSION: '1.25.0' # Note: go-version must also be set in job argocd-image.with.go-version
|
||||
GOLANG_VERSION: '1.25.6' # Note: go-version must also be set in job argocd-image.with.go-version
|
||||
|
||||
jobs:
|
||||
argocd-image:
|
||||
@@ -25,7 +25,7 @@ jobs:
|
||||
quay_image_name: quay.io/argoproj/argocd:${{ github.ref_name }}
|
||||
# Note: cannot use env variables to set go-version (https://docs.github.com/en/actions/using-workflows/reusing-workflows#limitations)
|
||||
# renovate: datasource=golang-version packageName=golang
|
||||
go-version: 1.25.0
|
||||
go-version: 1.25.6
|
||||
platforms: linux/amd64,linux/arm64,linux/s390x,linux/ppc64le
|
||||
push: true
|
||||
secrets:
|
||||
|
||||
9
.github/workflows/renovate.yaml
vendored
9
.github/workflows/renovate.yaml
vendored
@@ -19,7 +19,14 @@ jobs:
|
||||
private-key: ${{ secrets.RENOVATE_APP_PRIVATE_KEY }}
|
||||
|
||||
- name: Checkout
|
||||
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # 4.2.2
|
||||
uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # 6.0.1
|
||||
|
||||
# Some codegen commands require Go to be setup
|
||||
- name: Setup Golang
|
||||
uses: actions/setup-go@7a3fe6cf4cb3a834922a1244abfce67bcef6a0c5 # v6.2.0
|
||||
with:
|
||||
# renovate: datasource=golang-version packageName=golang
|
||||
go-version: 1.25.6
|
||||
|
||||
- name: Self-hosted Renovate
|
||||
uses: renovatebot/github-action@f8af9272cd94a4637c29f60dea8731afd3134473 #43.0.12
|
||||
|
||||
1
.gitignore
vendored
1
.gitignore
vendored
@@ -20,6 +20,7 @@ node_modules/
|
||||
.kube/
|
||||
./test/cmp/*.sock
|
||||
.envrc.remote
|
||||
.mirrord/
|
||||
.*.swp
|
||||
rerunreport.txt
|
||||
|
||||
|
||||
@@ -4,7 +4,7 @@ ARG BASE_IMAGE=docker.io/library/ubuntu:25.04@sha256:10bb10bb062de665d4dc3e0ea36
|
||||
# Initial stage which pulls prepares build dependencies and CLI tooling we need for our final image
|
||||
# Also used as the image in CI jobs so needs all dependencies
|
||||
####################################################################################################
|
||||
FROM docker.io/library/golang:1.25.0@sha256:9e56f0d0f043a68bb8c47c819e47dc29f6e8f5129b8885bed9d43f058f7f3ed6 AS builder
|
||||
FROM docker.io/library/golang:1.25.6@sha256:fc24d3881a021e7b968a4610fc024fba749f98fe5c07d4f28e6cfa14dc65a84c AS builder
|
||||
|
||||
WORKDIR /tmp
|
||||
|
||||
@@ -103,7 +103,7 @@ RUN HOST_ARCH=$TARGETARCH NODE_ENV='production' NODE_ONLINE_ENV='online' NODE_OP
|
||||
####################################################################################################
|
||||
# Argo CD Build stage which performs the actual build of Argo CD binaries
|
||||
####################################################################################################
|
||||
FROM --platform=$BUILDPLATFORM docker.io/library/golang:1.25.0@sha256:9e56f0d0f043a68bb8c47c819e47dc29f6e8f5129b8885bed9d43f058f7f3ed6 AS argocd-build
|
||||
FROM --platform=$BUILDPLATFORM docker.io/library/golang:1.25.6@sha256:fc24d3881a021e7b968a4610fc024fba749f98fe5c07d4f28e6cfa14dc65a84c AS argocd-build
|
||||
|
||||
WORKDIR /go/src/github.com/argoproj/argo-cd
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
FROM docker.io/library/golang:1.25.0@sha256:9e56f0d0f043a68bb8c47c819e47dc29f6e8f5129b8885bed9d43f058f7f3ed6
|
||||
FROM docker.io/library/golang:1.25.6@sha256:fc24d3881a021e7b968a4610fc024fba749f98fe5c07d4f28e6cfa14dc65a84c
|
||||
|
||||
ENV DEBIAN_FRONTEND=noninteractive
|
||||
|
||||
|
||||
@@ -75,6 +75,7 @@ const (
|
||||
var defaultPreservedAnnotations = []string{
|
||||
NotifiedAnnotationKey,
|
||||
argov1alpha1.AnnotationKeyRefresh,
|
||||
argov1alpha1.AnnotationKeyHydrate,
|
||||
}
|
||||
|
||||
type deleteInOrder struct {
|
||||
@@ -651,8 +652,9 @@ func (r *ApplicationSetReconciler) SetupWithManager(mgr ctrl.Manager, enableProg
|
||||
Watches(
|
||||
&corev1.Secret{},
|
||||
&clusterSecretEventHandler{
|
||||
Client: mgr.GetClient(),
|
||||
Log: log.WithField("type", "createSecretEventHandler"),
|
||||
Client: mgr.GetClient(),
|
||||
Log: log.WithField("type", "createSecretEventHandler"),
|
||||
ApplicationSetNamespaces: r.ApplicationSetNamespaces,
|
||||
}).
|
||||
Complete(r)
|
||||
}
|
||||
|
||||
@@ -589,6 +589,72 @@ func TestCreateOrUpdateInCluster(t *testing.T) {
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "Ensure that hydrate annotation is preserved from an existing app",
|
||||
appSet: v1alpha1.ApplicationSet{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "name",
|
||||
Namespace: "namespace",
|
||||
},
|
||||
Spec: v1alpha1.ApplicationSetSpec{
|
||||
Template: v1alpha1.ApplicationSetTemplate{
|
||||
Spec: v1alpha1.ApplicationSpec{
|
||||
Project: "project",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
existingApps: []v1alpha1.Application{
|
||||
{
|
||||
TypeMeta: metav1.TypeMeta{
|
||||
Kind: application.ApplicationKind,
|
||||
APIVersion: "argoproj.io/v1alpha1",
|
||||
},
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "app1",
|
||||
Namespace: "namespace",
|
||||
ResourceVersion: "2",
|
||||
Annotations: map[string]string{
|
||||
"annot-key": "annot-value",
|
||||
v1alpha1.AnnotationKeyHydrate: string(v1alpha1.RefreshTypeNormal),
|
||||
},
|
||||
},
|
||||
Spec: v1alpha1.ApplicationSpec{
|
||||
Project: "project",
|
||||
},
|
||||
},
|
||||
},
|
||||
desiredApps: []v1alpha1.Application{
|
||||
{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "app1",
|
||||
Namespace: "namespace",
|
||||
},
|
||||
Spec: v1alpha1.ApplicationSpec{
|
||||
Project: "project",
|
||||
},
|
||||
},
|
||||
},
|
||||
expected: []v1alpha1.Application{
|
||||
{
|
||||
TypeMeta: metav1.TypeMeta{
|
||||
Kind: application.ApplicationKind,
|
||||
APIVersion: "argoproj.io/v1alpha1",
|
||||
},
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "app1",
|
||||
Namespace: "namespace",
|
||||
ResourceVersion: "3",
|
||||
Annotations: map[string]string{
|
||||
v1alpha1.AnnotationKeyHydrate: string(v1alpha1.RefreshTypeNormal),
|
||||
},
|
||||
},
|
||||
Spec: v1alpha1.ApplicationSpec{
|
||||
Project: "project",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "Ensure that configured preserved annotations are preserved from an existing app",
|
||||
appSet: v1alpha1.ApplicationSet{
|
||||
|
||||
@@ -14,6 +14,7 @@ import (
|
||||
"sigs.k8s.io/controller-runtime/pkg/client"
|
||||
"sigs.k8s.io/controller-runtime/pkg/event"
|
||||
|
||||
"github.com/argoproj/argo-cd/v3/applicationset/utils"
|
||||
"github.com/argoproj/argo-cd/v3/common"
|
||||
argoprojiov1alpha1 "github.com/argoproj/argo-cd/v3/pkg/apis/application/v1alpha1"
|
||||
)
|
||||
@@ -22,8 +23,9 @@ import (
|
||||
// requeue any related ApplicationSets.
|
||||
type clusterSecretEventHandler struct {
|
||||
// handler.EnqueueRequestForOwner
|
||||
Log log.FieldLogger
|
||||
Client client.Client
|
||||
Log log.FieldLogger
|
||||
Client client.Client
|
||||
ApplicationSetNamespaces []string
|
||||
}
|
||||
|
||||
func (h *clusterSecretEventHandler) Create(ctx context.Context, e event.CreateEvent, q workqueue.TypedRateLimitingInterface[reconcile.Request]) {
|
||||
@@ -68,6 +70,10 @@ func (h *clusterSecretEventHandler) queueRelatedAppGenerators(ctx context.Contex
|
||||
|
||||
h.Log.WithField("count", len(appSetList.Items)).Info("listed ApplicationSets")
|
||||
for _, appSet := range appSetList.Items {
|
||||
if !utils.IsNamespaceAllowed(h.ApplicationSetNamespaces, appSet.GetNamespace()) {
|
||||
// Ignore it as not part of the allowed list of namespaces in which to watch Appsets
|
||||
continue
|
||||
}
|
||||
foundClusterGenerator := false
|
||||
for _, generator := range appSet.Spec.Generators {
|
||||
if generator.Clusters != nil {
|
||||
|
||||
@@ -137,7 +137,7 @@ func TestClusterEventHandler(t *testing.T) {
|
||||
{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "my-app-set",
|
||||
Namespace: "another-namespace",
|
||||
Namespace: "argocd",
|
||||
},
|
||||
Spec: argov1alpha1.ApplicationSetSpec{
|
||||
Generators: []argov1alpha1.ApplicationSetGenerator{
|
||||
@@ -171,9 +171,37 @@ func TestClusterEventHandler(t *testing.T) {
|
||||
},
|
||||
},
|
||||
expectedRequests: []reconcile.Request{
|
||||
{NamespacedName: types.NamespacedName{Namespace: "another-namespace", Name: "my-app-set"}},
|
||||
{NamespacedName: types.NamespacedName{Namespace: "argocd", Name: "my-app-set"}},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "cluster generators in other namespaces should not match",
|
||||
items: []argov1alpha1.ApplicationSet{
|
||||
{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "my-app-set",
|
||||
Namespace: "my-namespace-not-allowed",
|
||||
},
|
||||
Spec: argov1alpha1.ApplicationSetSpec{
|
||||
Generators: []argov1alpha1.ApplicationSetGenerator{
|
||||
{
|
||||
Clusters: &argov1alpha1.ClusterGenerator{},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
secret: corev1.Secret{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Namespace: "argocd",
|
||||
Name: "my-secret",
|
||||
Labels: map[string]string{
|
||||
argocommon.LabelKeySecretType: argocommon.LabelValueSecretTypeCluster,
|
||||
},
|
||||
},
|
||||
},
|
||||
expectedRequests: []reconcile.Request{},
|
||||
},
|
||||
{
|
||||
name: "non-argo cd secret should not match",
|
||||
items: []argov1alpha1.ApplicationSet{
|
||||
@@ -552,8 +580,9 @@ func TestClusterEventHandler(t *testing.T) {
|
||||
fakeClient := fake.NewClientBuilder().WithScheme(scheme).WithLists(&appSetList).Build()
|
||||
|
||||
handler := &clusterSecretEventHandler{
|
||||
Client: fakeClient,
|
||||
Log: log.WithField("type", "createSecretEventHandler"),
|
||||
Client: fakeClient,
|
||||
Log: log.WithField("type", "createSecretEventHandler"),
|
||||
ApplicationSetNamespaces: []string{"argocd"},
|
||||
}
|
||||
|
||||
mockAddRateLimitingInterface := mockAddRateLimitingInterface{}
|
||||
|
||||
@@ -80,6 +80,7 @@ func NewCommand() *cobra.Command {
|
||||
includeHiddenDirectories bool
|
||||
cmpUseManifestGeneratePaths bool
|
||||
ociMediaTypes []string
|
||||
enableBuiltinGitConfig bool
|
||||
)
|
||||
command := cobra.Command{
|
||||
Use: cliName,
|
||||
@@ -155,6 +156,7 @@ func NewCommand() *cobra.Command {
|
||||
IncludeHiddenDirectories: includeHiddenDirectories,
|
||||
CMPUseManifestGeneratePaths: cmpUseManifestGeneratePaths,
|
||||
OCIMediaTypes: ociMediaTypes,
|
||||
EnableBuiltinGitConfig: enableBuiltinGitConfig,
|
||||
}, askPassServer)
|
||||
errors.CheckError(err)
|
||||
|
||||
@@ -264,6 +266,7 @@ func NewCommand() *cobra.Command {
|
||||
command.Flags().BoolVar(&includeHiddenDirectories, "include-hidden-directories", env.ParseBoolFromEnv("ARGOCD_REPO_SERVER_INCLUDE_HIDDEN_DIRECTORIES", false), "Include hidden directories from Git")
|
||||
command.Flags().BoolVar(&cmpUseManifestGeneratePaths, "plugin-use-manifest-generate-paths", env.ParseBoolFromEnv("ARGOCD_REPO_SERVER_PLUGIN_USE_MANIFEST_GENERATE_PATHS", false), "Pass the resources described in argocd.argoproj.io/manifest-generate-paths value to the cmpserver to generate the application manifests.")
|
||||
command.Flags().StringSliceVar(&ociMediaTypes, "oci-layer-media-types", env.StringsFromEnv("ARGOCD_REPO_SERVER_OCI_LAYER_MEDIA_TYPES", []string{"application/vnd.oci.image.layer.v1.tar", "application/vnd.oci.image.layer.v1.tar+gzip", "application/vnd.cncf.helm.chart.content.v1.tar+gzip"}, ","), "Comma separated list of allowed media types for OCI media types. This only accounts for media types within layers.")
|
||||
command.Flags().BoolVar(&enableBuiltinGitConfig, "enable-builtin-git-config", env.ParseBoolFromEnv("ARGOCD_REPO_SERVER_ENABLE_BUILTIN_GIT_CONFIG", true), "Enable builtin git configuration options that are required for correct argocd-repo-server operation.")
|
||||
tlsConfigCustomizerSrc = tls.AddTLSFlagsToCmd(&command)
|
||||
cacheSrc = reposervercache.AddCacheFlagsToCmd(&command, cacheutil.Options{
|
||||
OnClientCreated: func(client *redis.Client) {
|
||||
|
||||
@@ -21,8 +21,8 @@ import (
|
||||
|
||||
var sprigFuncMap = sprig.GenericFuncMap() // a singleton for better performance
|
||||
|
||||
const gitAttributesContents = `*/README.md linguist-generated=true
|
||||
*/hydrator.metadata linguist-generated=true`
|
||||
const gitAttributesContents = `**/README.md linguist-generated=true
|
||||
**/hydrator.metadata linguist-generated=true`
|
||||
|
||||
func init() {
|
||||
// Avoid allowing the user to learn things about the environment.
|
||||
|
||||
@@ -7,8 +7,10 @@ import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"os"
|
||||
"os/exec"
|
||||
"path"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
@@ -234,6 +236,74 @@ func TestWriteGitAttributes(t *testing.T) {
|
||||
gitAttributesPath := filepath.Join(root.Name(), ".gitattributes")
|
||||
gitAttributesBytes, err := os.ReadFile(gitAttributesPath)
|
||||
require.NoError(t, err)
|
||||
assert.Contains(t, string(gitAttributesBytes), "*/README.md linguist-generated=true")
|
||||
assert.Contains(t, string(gitAttributesBytes), "*/hydrator.metadata linguist-generated=true")
|
||||
assert.Contains(t, string(gitAttributesBytes), "README.md linguist-generated=true")
|
||||
assert.Contains(t, string(gitAttributesBytes), "hydrator.metadata linguist-generated=true")
|
||||
}
|
||||
|
||||
func TestWriteGitAttributes_MatchesAllDepths(t *testing.T) {
|
||||
root := tempRoot(t)
|
||||
|
||||
err := writeGitAttributes(root)
|
||||
require.NoError(t, err)
|
||||
|
||||
// The gitattributes pattern needs to match files at all depths:
|
||||
// - hydrator.metadata (root level)
|
||||
// - path1/hydrator.metadata (one level deep)
|
||||
// - path1/nested/deep/hydrator.metadata (multiple levels deep)
|
||||
// Same for README.md files
|
||||
//
|
||||
// The pattern "**/hydrator.metadata" matches at any depth including root
|
||||
// The pattern "*/hydrator.metadata" only matches exactly one directory level deep
|
||||
|
||||
// Test actual Git behavior using git check-attr
|
||||
// Initialize a git repo
|
||||
ctx := t.Context()
|
||||
repoPath := root.Name()
|
||||
cmd := exec.CommandContext(ctx, "git", "init")
|
||||
cmd.Dir = repoPath
|
||||
output, err := cmd.CombinedOutput()
|
||||
require.NoError(t, err, "Failed to init git repo: %s", string(output))
|
||||
|
||||
// Test files at different depths
|
||||
testCases := []struct {
|
||||
path string
|
||||
shouldMatch bool
|
||||
description string
|
||||
}{
|
||||
{"hydrator.metadata", true, "root level hydrator.metadata"},
|
||||
{"README.md", true, "root level README.md"},
|
||||
{"path1/hydrator.metadata", true, "one level deep hydrator.metadata"},
|
||||
{"path1/README.md", true, "one level deep README.md"},
|
||||
{"path1/nested/hydrator.metadata", true, "two levels deep hydrator.metadata"},
|
||||
{"path1/nested/README.md", true, "two levels deep README.md"},
|
||||
{"path1/nested/deep/hydrator.metadata", true, "three levels deep hydrator.metadata"},
|
||||
{"path1/nested/deep/README.md", true, "three levels deep README.md"},
|
||||
{"manifest.yaml", false, "manifest.yaml should not match"},
|
||||
{"path1/manifest.yaml", false, "nested manifest.yaml should not match"},
|
||||
}
|
||||
|
||||
for _, tc := range testCases {
|
||||
t.Run(tc.description, func(t *testing.T) {
|
||||
// Use git check-attr to verify if linguist-generated attribute is set
|
||||
cmd := exec.CommandContext(ctx, "git", "check-attr", "linguist-generated", tc.path)
|
||||
cmd.Dir = repoPath
|
||||
output, err := cmd.CombinedOutput()
|
||||
require.NoError(t, err, "Failed to run git check-attr: %s", string(output))
|
||||
|
||||
// Output format: <path>: <attribute>: <value>
|
||||
// Example: "hydrator.metadata: linguist-generated: true"
|
||||
outputStr := strings.TrimSpace(string(output))
|
||||
|
||||
if tc.shouldMatch {
|
||||
expectedOutput := tc.path + ": linguist-generated: true"
|
||||
assert.Equal(t, expectedOutput, outputStr,
|
||||
"File %s should have linguist-generated=true attribute", tc.path)
|
||||
} else {
|
||||
// Attribute should be unspecified
|
||||
expectedOutput := tc.path + ": linguist-generated: unspecified"
|
||||
assert.Equal(t, expectedOutput, outputStr,
|
||||
"File %s should not have linguist-generated=true attribute", tc.path)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1508,8 +1508,18 @@ func (ctrl *ApplicationController) processRequestedAppOperation(app *appv1.Appli
|
||||
// if we just completed an operation, force a refresh so that UI will report up-to-date
|
||||
// sync/health information
|
||||
if _, err := cache.MetaNamespaceKeyFunc(app); err == nil {
|
||||
// force app refresh with using CompareWithLatest comparison type and trigger app reconciliation loop
|
||||
ctrl.requestAppRefresh(app.QualifiedName(), CompareWithLatestForceResolve.Pointer(), nil)
|
||||
var compareWith CompareWith
|
||||
if state.Operation.InitiatedBy.Automated {
|
||||
// Do not force revision resolution on automated operations because
|
||||
// this would cause excessive Ls-Remote requests on monorepo commits
|
||||
compareWith = CompareWithLatest
|
||||
} else {
|
||||
// Force app refresh with using most recent resolved revision after sync,
|
||||
// so UI won't show a just synced application being out of sync if it was
|
||||
// synced after commit but before app. refresh (see #18153)
|
||||
compareWith = CompareWithLatestForceResolve
|
||||
}
|
||||
ctrl.requestAppRefresh(app.QualifiedName(), compareWith.Pointer(), nil)
|
||||
} else {
|
||||
logCtx.Warnf("Fails to requeue application: %v", err)
|
||||
}
|
||||
|
||||
@@ -2321,6 +2321,41 @@ func TestProcessRequestedAppOperation_Successful(t *testing.T) {
|
||||
assert.Equal(t, CompareWithLatestForceResolve, level)
|
||||
}
|
||||
|
||||
func TestProcessRequestedAppAutomatedOperation_Successful(t *testing.T) {
|
||||
app := newFakeApp()
|
||||
app.Spec.Project = "default"
|
||||
app.Operation = &v1alpha1.Operation{
|
||||
Sync: &v1alpha1.SyncOperation{},
|
||||
InitiatedBy: v1alpha1.OperationInitiator{
|
||||
Automated: true,
|
||||
},
|
||||
}
|
||||
ctrl := newFakeController(&fakeData{
|
||||
apps: []runtime.Object{app, &defaultProj},
|
||||
manifestResponses: []*apiclient.ManifestResponse{{
|
||||
Manifests: []string{},
|
||||
}},
|
||||
}, nil)
|
||||
fakeAppCs := ctrl.applicationClientset.(*appclientset.Clientset)
|
||||
receivedPatch := map[string]any{}
|
||||
fakeAppCs.PrependReactor("patch", "*", func(action kubetesting.Action) (handled bool, ret runtime.Object, err error) {
|
||||
if patchAction, ok := action.(kubetesting.PatchAction); ok {
|
||||
require.NoError(t, json.Unmarshal(patchAction.GetPatch(), &receivedPatch))
|
||||
}
|
||||
return true, &v1alpha1.Application{}, nil
|
||||
})
|
||||
|
||||
ctrl.processRequestedAppOperation(app)
|
||||
|
||||
phase, _, _ := unstructured.NestedString(receivedPatch, "status", "operationState", "phase")
|
||||
message, _, _ := unstructured.NestedString(receivedPatch, "status", "operationState", "message")
|
||||
assert.Equal(t, string(synccommon.OperationSucceeded), phase)
|
||||
assert.Equal(t, "successfully synced (no more tasks)", message)
|
||||
ok, level := ctrl.isRefreshRequested(ctrl.toAppKey(app.Name))
|
||||
assert.True(t, ok)
|
||||
assert.Equal(t, CompareWithLatest, level)
|
||||
}
|
||||
|
||||
func TestProcessRequestedAppOperation_SyncTimeout(t *testing.T) {
|
||||
testCases := []struct {
|
||||
name string
|
||||
|
||||
@@ -260,7 +260,7 @@ func (m *appStateManager) GetRepoObjs(ctx context.Context, app *v1alpha1.Applica
|
||||
Revision: revision,
|
||||
SyncedRevision: syncedRevision,
|
||||
NoRevisionCache: noRevisionCache,
|
||||
Paths: path.GetAppRefreshPaths(app),
|
||||
Paths: path.GetSourceRefreshPaths(app, source),
|
||||
AppLabelKey: appLabelKey,
|
||||
AppName: app.InstanceName(m.namespace),
|
||||
Namespace: appNamespace,
|
||||
|
||||
@@ -2,7 +2,6 @@ package controller
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
stderrors "errors"
|
||||
"fmt"
|
||||
"os"
|
||||
@@ -263,7 +262,7 @@ func (m *appStateManager) SyncAppState(app *v1alpha1.Application, project *v1alp
|
||||
// resources which in this case applies the live values in the configured
|
||||
// ignore differences fields.
|
||||
if syncOp.SyncOptions.HasOption("RespectIgnoreDifferences=true") {
|
||||
patchedTargets, err := normalizeTargetResources(openAPISchema, compareResult)
|
||||
patchedTargets, err := normalizeTargetResources(compareResult)
|
||||
if err != nil {
|
||||
state.Phase = common.OperationError
|
||||
state.Message = fmt.Sprintf("Failed to normalize target resources: %s", err)
|
||||
@@ -435,65 +434,53 @@ func (m *appStateManager) SyncAppState(app *v1alpha1.Application, project *v1alp
|
||||
// - applies normalization to the target resources based on the live resources
|
||||
// - copies ignored fields from the matching live resources: apply normalizer to the live resource,
|
||||
// calculates the patch performed by normalizer and applies the patch to the target resource
|
||||
func normalizeTargetResources(openAPISchema openapi.Resources, cr *comparisonResult) ([]*unstructured.Unstructured, error) {
|
||||
// Normalize live and target resources (cleaning or aligning them)
|
||||
func normalizeTargetResources(cr *comparisonResult) ([]*unstructured.Unstructured, error) {
|
||||
// normalize live and target resources
|
||||
normalized, err := diff.Normalize(cr.reconciliationResult.Live, cr.reconciliationResult.Target, cr.diffConfig)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
patchedTargets := []*unstructured.Unstructured{}
|
||||
|
||||
for idx, live := range cr.reconciliationResult.Live {
|
||||
normalizedTarget := normalized.Targets[idx]
|
||||
if normalizedTarget == nil {
|
||||
patchedTargets = append(patchedTargets, nil)
|
||||
continue
|
||||
}
|
||||
gvk := normalizedTarget.GroupVersionKind()
|
||||
|
||||
originalTarget := cr.reconciliationResult.Target[idx]
|
||||
if live == nil {
|
||||
// No live resource, just use target
|
||||
patchedTargets = append(patchedTargets, originalTarget)
|
||||
continue
|
||||
}
|
||||
|
||||
var (
|
||||
lookupPatchMeta strategicpatch.LookupPatchMeta
|
||||
versionedObject any
|
||||
)
|
||||
|
||||
// Load patch meta struct or OpenAPI schema for CRDs
|
||||
if versionedObject, err = scheme.Scheme.New(gvk); err == nil {
|
||||
if lookupPatchMeta, err = strategicpatch.NewPatchMetaFromStruct(versionedObject); err != nil {
|
||||
var lookupPatchMeta *strategicpatch.PatchMetaFromStruct
|
||||
versionedObject, err := scheme.Scheme.New(normalizedTarget.GroupVersionKind())
|
||||
if err == nil {
|
||||
meta, err := strategicpatch.NewPatchMetaFromStruct(versionedObject)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
} else if crdSchema := openAPISchema.LookupResource(gvk); crdSchema != nil {
|
||||
lookupPatchMeta = strategicpatch.NewPatchMetaFromOpenAPI(crdSchema)
|
||||
lookupPatchMeta = &meta
|
||||
}
|
||||
|
||||
// Calculate live patch
|
||||
livePatch, err := getMergePatch(normalized.Lives[idx], live, lookupPatchMeta)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Apply the patch to the normalized target
|
||||
// This ensures ignored fields in live are restored into the target before syncing
|
||||
normalizedTarget, err = applyMergePatch(normalizedTarget, livePatch, versionedObject, lookupPatchMeta)
|
||||
normalizedTarget, err = applyMergePatch(normalizedTarget, livePatch, versionedObject)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
patchedTargets = append(patchedTargets, normalizedTarget)
|
||||
}
|
||||
|
||||
return patchedTargets, nil
|
||||
}
|
||||
|
||||
// getMergePatch calculates and returns the patch between the original and the
|
||||
// modified unstructures.
|
||||
func getMergePatch(original, modified *unstructured.Unstructured, lookupPatchMeta strategicpatch.LookupPatchMeta) ([]byte, error) {
|
||||
func getMergePatch(original, modified *unstructured.Unstructured, lookupPatchMeta *strategicpatch.PatchMetaFromStruct) ([]byte, error) {
|
||||
originalJSON, err := original.MarshalJSON()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
@@ -509,35 +496,18 @@ func getMergePatch(original, modified *unstructured.Unstructured, lookupPatchMet
|
||||
return jsonpatch.CreateMergePatch(originalJSON, modifiedJSON)
|
||||
}
|
||||
|
||||
// applyMergePatch will apply the given patch in the obj and return the patched unstructure.
|
||||
func applyMergePatch(obj *unstructured.Unstructured, patch []byte, versionedObject any, meta strategicpatch.LookupPatchMeta) (*unstructured.Unstructured, error) {
|
||||
// applyMergePatch will apply the given patch in the obj and return the patched
|
||||
// unstructure.
|
||||
func applyMergePatch(obj *unstructured.Unstructured, patch []byte, versionedObject any) (*unstructured.Unstructured, error) {
|
||||
originalJSON, err := obj.MarshalJSON()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
var patchedJSON []byte
|
||||
switch {
|
||||
case versionedObject != nil:
|
||||
patchedJSON, err = strategicpatch.StrategicMergePatch(originalJSON, patch, versionedObject)
|
||||
case meta != nil:
|
||||
var originalMap, patchMap map[string]any
|
||||
if err := json.Unmarshal(originalJSON, &originalMap); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if err := json.Unmarshal(patch, &patchMap); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
patchedMap, err := strategicpatch.StrategicMergeMapPatchUsingLookupPatchMeta(originalMap, patchMap, meta)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
patchedJSON, err = json.Marshal(patchedMap)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
default:
|
||||
if versionedObject == nil {
|
||||
patchedJSON, err = jsonpatch.MergePatch(originalJSON, patch)
|
||||
} else {
|
||||
patchedJSON, err = strategicpatch.StrategicMergePatch(originalJSON, patch, versionedObject)
|
||||
}
|
||||
if err != nil {
|
||||
return nil, err
|
||||
|
||||
@@ -1,17 +1,9 @@
|
||||
package controller
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"strconv"
|
||||
"testing"
|
||||
|
||||
openapi_v2 "github.com/google/gnostic-models/openapiv2"
|
||||
"k8s.io/apimachinery/pkg/runtime/schema"
|
||||
"k8s.io/kubectl/pkg/util/openapi"
|
||||
|
||||
"sigs.k8s.io/yaml"
|
||||
|
||||
"github.com/argoproj/gitops-engine/pkg/sync"
|
||||
synccommon "github.com/argoproj/gitops-engine/pkg/sync/common"
|
||||
"github.com/argoproj/gitops-engine/pkg/utils/kube"
|
||||
@@ -31,29 +23,6 @@ import (
|
||||
"github.com/argoproj/argo-cd/v3/util/argo/normalizers"
|
||||
)
|
||||
|
||||
type fakeDiscovery struct {
|
||||
schema *openapi_v2.Document
|
||||
}
|
||||
|
||||
func (f *fakeDiscovery) OpenAPISchema() (*openapi_v2.Document, error) {
|
||||
return f.schema, nil
|
||||
}
|
||||
|
||||
func loadCRDSchema(t *testing.T, path string) *openapi_v2.Document {
|
||||
t.Helper()
|
||||
|
||||
data, err := os.ReadFile(path)
|
||||
require.NoError(t, err)
|
||||
|
||||
jsonData, err := yaml.YAMLToJSON(data)
|
||||
require.NoError(t, err)
|
||||
|
||||
doc, err := openapi_v2.ParseDocument(jsonData)
|
||||
require.NoError(t, err)
|
||||
|
||||
return doc
|
||||
}
|
||||
|
||||
func TestPersistRevisionHistory(t *testing.T) {
|
||||
app := newFakeApp()
|
||||
app.Status.OperationState = nil
|
||||
@@ -416,7 +385,7 @@ func TestNormalizeTargetResources(t *testing.T) {
|
||||
f := setup(t, ignores)
|
||||
|
||||
// when
|
||||
targets, err := normalizeTargetResources(nil, f.comparisonResult)
|
||||
targets, err := normalizeTargetResources(f.comparisonResult)
|
||||
|
||||
// then
|
||||
require.NoError(t, err)
|
||||
@@ -429,7 +398,7 @@ func TestNormalizeTargetResources(t *testing.T) {
|
||||
f := setup(t, []v1alpha1.ResourceIgnoreDifferences{})
|
||||
|
||||
// when
|
||||
targets, err := normalizeTargetResources(nil, f.comparisonResult)
|
||||
targets, err := normalizeTargetResources(f.comparisonResult)
|
||||
|
||||
// then
|
||||
require.NoError(t, err)
|
||||
@@ -449,7 +418,7 @@ func TestNormalizeTargetResources(t *testing.T) {
|
||||
unstructured.RemoveNestedField(live.Object, "metadata", "annotations", "iksm-version")
|
||||
|
||||
// when
|
||||
targets, err := normalizeTargetResources(nil, f.comparisonResult)
|
||||
targets, err := normalizeTargetResources(f.comparisonResult)
|
||||
|
||||
// then
|
||||
require.NoError(t, err)
|
||||
@@ -474,7 +443,7 @@ func TestNormalizeTargetResources(t *testing.T) {
|
||||
f := setup(t, ignores)
|
||||
|
||||
// when
|
||||
targets, err := normalizeTargetResources(nil, f.comparisonResult)
|
||||
targets, err := normalizeTargetResources(f.comparisonResult)
|
||||
|
||||
// then
|
||||
require.NoError(t, err)
|
||||
@@ -489,6 +458,7 @@ func TestNormalizeTargetResources(t *testing.T) {
|
||||
assert.Equal(t, int64(4), replicas)
|
||||
})
|
||||
t.Run("will keep new array entries not found in live state if not ignored", func(t *testing.T) {
|
||||
t.Skip("limitation in the current implementation")
|
||||
// given
|
||||
ignores := []v1alpha1.ResourceIgnoreDifferences{
|
||||
{
|
||||
@@ -502,7 +472,7 @@ func TestNormalizeTargetResources(t *testing.T) {
|
||||
f.comparisonResult.reconciliationResult.Target = []*unstructured.Unstructured{target}
|
||||
|
||||
// when
|
||||
targets, err := normalizeTargetResources(nil, f.comparisonResult)
|
||||
targets, err := normalizeTargetResources(f.comparisonResult)
|
||||
|
||||
// then
|
||||
require.NoError(t, err)
|
||||
@@ -539,11 +509,6 @@ func TestNormalizeTargetResourcesWithList(t *testing.T) {
|
||||
}
|
||||
|
||||
t.Run("will properly ignore nested fields within arrays", func(t *testing.T) {
|
||||
doc := loadCRDSchema(t, "testdata/schemas/httpproxy_openapi_v2.yaml")
|
||||
disco := &fakeDiscovery{schema: doc}
|
||||
oapiGetter := openapi.NewOpenAPIGetter(disco)
|
||||
oapiResources, err := openapi.NewOpenAPIParser(oapiGetter).Parse()
|
||||
require.NoError(t, err)
|
||||
// given
|
||||
ignores := []v1alpha1.ResourceIgnoreDifferences{
|
||||
{
|
||||
@@ -557,11 +522,8 @@ func TestNormalizeTargetResourcesWithList(t *testing.T) {
|
||||
target := test.YamlToUnstructured(testdata.TargetHTTPProxy)
|
||||
f.comparisonResult.reconciliationResult.Target = []*unstructured.Unstructured{target}
|
||||
|
||||
gvk := schema.GroupVersionKind{Group: "projectcontour.io", Version: "v1", Kind: "HTTPProxy"}
|
||||
fmt.Printf("LookupResource result: %+v\n", oapiResources.LookupResource(gvk))
|
||||
|
||||
// when
|
||||
patchedTargets, err := normalizeTargetResources(oapiResources, f.comparisonResult)
|
||||
patchedTargets, err := normalizeTargetResources(f.comparisonResult)
|
||||
|
||||
// then
|
||||
require.NoError(t, err)
|
||||
@@ -600,7 +562,7 @@ func TestNormalizeTargetResourcesWithList(t *testing.T) {
|
||||
f.comparisonResult.reconciliationResult.Target = []*unstructured.Unstructured{target}
|
||||
|
||||
// when
|
||||
targets, err := normalizeTargetResources(nil, f.comparisonResult)
|
||||
targets, err := normalizeTargetResources(f.comparisonResult)
|
||||
|
||||
// then
|
||||
require.NoError(t, err)
|
||||
@@ -652,7 +614,7 @@ func TestNormalizeTargetResourcesWithList(t *testing.T) {
|
||||
f.comparisonResult.reconciliationResult.Target = []*unstructured.Unstructured{target}
|
||||
|
||||
// when
|
||||
targets, err := normalizeTargetResources(nil, f.comparisonResult)
|
||||
targets, err := normalizeTargetResources(f.comparisonResult)
|
||||
|
||||
// then
|
||||
require.NoError(t, err)
|
||||
@@ -706,175 +668,6 @@ func TestNormalizeTargetResourcesWithList(t *testing.T) {
|
||||
assert.Equal(t, "EV", env0["name"])
|
||||
assert.Equal(t, "here", env0["value"])
|
||||
})
|
||||
|
||||
t.Run("patches ignored differences in individual array elements of HTTPProxy CRD", func(t *testing.T) {
|
||||
doc := loadCRDSchema(t, "testdata/schemas/httpproxy_openapi_v2.yaml")
|
||||
disco := &fakeDiscovery{schema: doc}
|
||||
oapiGetter := openapi.NewOpenAPIGetter(disco)
|
||||
oapiResources, err := openapi.NewOpenAPIParser(oapiGetter).Parse()
|
||||
require.NoError(t, err)
|
||||
|
||||
ignores := []v1alpha1.ResourceIgnoreDifferences{
|
||||
{
|
||||
Group: "projectcontour.io",
|
||||
Kind: "HTTPProxy",
|
||||
JQPathExpressions: []string{".spec.routes[].rateLimitPolicy.global.descriptors[].entries[]"},
|
||||
},
|
||||
}
|
||||
|
||||
f := setupHTTPProxy(t, ignores)
|
||||
|
||||
target := test.YamlToUnstructured(testdata.TargetHTTPProxy)
|
||||
f.comparisonResult.reconciliationResult.Target = []*unstructured.Unstructured{target}
|
||||
|
||||
live := test.YamlToUnstructured(testdata.LiveHTTPProxy)
|
||||
f.comparisonResult.reconciliationResult.Live = []*unstructured.Unstructured{live}
|
||||
|
||||
patchedTargets, err := normalizeTargetResources(oapiResources, f.comparisonResult)
|
||||
require.NoError(t, err)
|
||||
require.Len(t, patchedTargets, 1)
|
||||
patched := patchedTargets[0]
|
||||
|
||||
// verify descriptors array in patched target
|
||||
descriptors := dig(patched.Object, "spec", "routes", 0, "rateLimitPolicy", "global", "descriptors").([]any)
|
||||
require.Len(t, descriptors, 1) // Only the descriptors with ignored entries should remain
|
||||
|
||||
// verify individual entries array inside the descriptor
|
||||
entriesArr := dig(patched.Object, "spec", "routes", 0, "rateLimitPolicy", "global", "descriptors", 0, "entries").([]any)
|
||||
require.Len(t, entriesArr, 1) // Only the ignored entry should be patched
|
||||
|
||||
// verify the content of the entry is preserved correctly
|
||||
entry := entriesArr[0].(map[string]any)
|
||||
requestHeader := entry["requestHeader"].(map[string]any)
|
||||
assert.Equal(t, "sample-header", requestHeader["headerName"])
|
||||
assert.Equal(t, "sample-key", requestHeader["descriptorKey"])
|
||||
})
|
||||
}
|
||||
|
||||
func TestNormalizeTargetResourcesCRDs(t *testing.T) {
|
||||
type fixture struct {
|
||||
comparisonResult *comparisonResult
|
||||
}
|
||||
setupHTTPProxy := func(t *testing.T, ignores []v1alpha1.ResourceIgnoreDifferences) *fixture {
|
||||
t.Helper()
|
||||
dc, err := diff.NewDiffConfigBuilder().
|
||||
WithDiffSettings(ignores, nil, true, normalizers.IgnoreNormalizerOpts{}).
|
||||
WithNoCache().
|
||||
Build()
|
||||
require.NoError(t, err)
|
||||
live := test.YamlToUnstructured(testdata.SimpleAppLiveYaml)
|
||||
target := test.YamlToUnstructured(testdata.SimpleAppTargetYaml)
|
||||
return &fixture{
|
||||
&comparisonResult{
|
||||
reconciliationResult: sync.ReconciliationResult{
|
||||
Live: []*unstructured.Unstructured{live},
|
||||
Target: []*unstructured.Unstructured{target},
|
||||
},
|
||||
diffConfig: dc,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
t.Run("sample-app", func(t *testing.T) {
|
||||
doc := loadCRDSchema(t, "testdata/schemas/simple-app.yaml")
|
||||
disco := &fakeDiscovery{schema: doc}
|
||||
oapiGetter := openapi.NewOpenAPIGetter(disco)
|
||||
oapiResources, err := openapi.NewOpenAPIParser(oapiGetter).Parse()
|
||||
require.NoError(t, err)
|
||||
|
||||
ignores := []v1alpha1.ResourceIgnoreDifferences{
|
||||
{
|
||||
Group: "example.com",
|
||||
Kind: "SimpleApp",
|
||||
JQPathExpressions: []string{".spec.servers[1].enabled", ".spec.servers[0].port"},
|
||||
},
|
||||
}
|
||||
|
||||
f := setupHTTPProxy(t, ignores)
|
||||
|
||||
target := test.YamlToUnstructured(testdata.SimpleAppTargetYaml)
|
||||
f.comparisonResult.reconciliationResult.Target = []*unstructured.Unstructured{target}
|
||||
|
||||
live := test.YamlToUnstructured(testdata.SimpleAppLiveYaml)
|
||||
f.comparisonResult.reconciliationResult.Live = []*unstructured.Unstructured{live}
|
||||
|
||||
patchedTargets, err := normalizeTargetResources(oapiResources, f.comparisonResult)
|
||||
require.NoError(t, err)
|
||||
require.Len(t, patchedTargets, 1)
|
||||
|
||||
patched := patchedTargets[0]
|
||||
require.NotNil(t, patched)
|
||||
|
||||
// 'spec.servers' array has length 2
|
||||
servers := dig(patched.Object, "spec", "servers").([]any)
|
||||
require.Len(t, servers, 2)
|
||||
|
||||
// first server's 'name' is 'server1'
|
||||
name1 := dig(patched.Object, "spec", "servers", 0, "name").(string)
|
||||
assert.Equal(t, "server1", name1)
|
||||
|
||||
assert.Equal(t, int64(8081), dig(patched.Object, "spec", "servers", 0, "port").(int64))
|
||||
assert.Equal(t, int64(9090), dig(patched.Object, "spec", "servers", 1, "port").(int64))
|
||||
|
||||
// first server's 'enabled' should be true
|
||||
enabled1 := dig(patched.Object, "spec", "servers", 0, "enabled").(bool)
|
||||
assert.True(t, enabled1)
|
||||
|
||||
// second server's 'name' should be 'server2'
|
||||
name2 := dig(patched.Object, "spec", "servers", 1, "name").(string)
|
||||
assert.Equal(t, "server2", name2)
|
||||
|
||||
// second server's 'enabled' should be true (respected from live due to ignoreDifferences)
|
||||
enabled2 := dig(patched.Object, "spec", "servers", 1, "enabled").(bool)
|
||||
assert.True(t, enabled2)
|
||||
})
|
||||
t.Run("rollout-obj", func(t *testing.T) {
|
||||
// Load Rollout CRD schema like SimpleApp
|
||||
doc := loadCRDSchema(t, "testdata/schemas/rollout-schema.yaml")
|
||||
disco := &fakeDiscovery{schema: doc}
|
||||
oapiGetter := openapi.NewOpenAPIGetter(disco)
|
||||
oapiResources, err := openapi.NewOpenAPIParser(oapiGetter).Parse()
|
||||
require.NoError(t, err)
|
||||
|
||||
ignores := []v1alpha1.ResourceIgnoreDifferences{
|
||||
{
|
||||
Group: "argoproj.io",
|
||||
Kind: "Rollout",
|
||||
JQPathExpressions: []string{`.spec.template.spec.containers[] | select(.name == "init") | .image`},
|
||||
},
|
||||
}
|
||||
|
||||
f := setupHTTPProxy(t, ignores)
|
||||
|
||||
live := test.YamlToUnstructured(testdata.LiveRolloutYaml)
|
||||
target := test.YamlToUnstructured(testdata.TargetRolloutYaml)
|
||||
f.comparisonResult.reconciliationResult.Live = []*unstructured.Unstructured{live}
|
||||
f.comparisonResult.reconciliationResult.Target = []*unstructured.Unstructured{target}
|
||||
|
||||
targets, err := normalizeTargetResources(oapiResources, f.comparisonResult)
|
||||
require.NoError(t, err)
|
||||
require.Len(t, targets, 1)
|
||||
|
||||
patched := targets[0]
|
||||
require.NotNil(t, patched)
|
||||
|
||||
containers := dig(patched.Object, "spec", "template", "spec", "containers").([]any)
|
||||
require.Len(t, containers, 2)
|
||||
|
||||
initContainer := containers[0].(map[string]any)
|
||||
mainContainer := containers[1].(map[string]any)
|
||||
|
||||
// Assert init container image is preserved (ignoreDifferences works)
|
||||
initImage := dig(initContainer, "image").(string)
|
||||
assert.Equal(t, "init-container:v1", initImage)
|
||||
|
||||
// Assert main container fields as expected
|
||||
mainName := dig(mainContainer, "name").(string)
|
||||
assert.Equal(t, "main", mainName)
|
||||
|
||||
mainImage := dig(mainContainer, "image").(string)
|
||||
assert.Equal(t, "main-container:v1", mainImage)
|
||||
})
|
||||
}
|
||||
|
||||
func TestDeriveServiceAccountMatchingNamespaces(t *testing.T) {
|
||||
|
||||
12
controller/testdata/data.go
vendored
12
controller/testdata/data.go
vendored
@@ -32,16 +32,4 @@ var (
|
||||
|
||||
//go:embed additional-image-replicas-deployment.yaml
|
||||
AdditionalImageReplicaDeploymentYaml string
|
||||
|
||||
//go:embed simple-app-live.yaml
|
||||
SimpleAppLiveYaml string
|
||||
|
||||
//go:embed simple-app-target.yaml
|
||||
SimpleAppTargetYaml string
|
||||
|
||||
//go:embed target-rollout.yaml
|
||||
TargetRolloutYaml string
|
||||
|
||||
//go:embed live-rollout.yaml
|
||||
LiveRolloutYaml string
|
||||
)
|
||||
|
||||
25
controller/testdata/live-rollout.yaml
vendored
25
controller/testdata/live-rollout.yaml
vendored
@@ -1,25 +0,0 @@
|
||||
apiVersion: argoproj.io/v1alpha1
|
||||
kind: Rollout
|
||||
metadata:
|
||||
name: rollout-sample
|
||||
spec:
|
||||
replicas: 2
|
||||
strategy:
|
||||
canary:
|
||||
steps:
|
||||
- setWeight: 20
|
||||
selector:
|
||||
matchLabels:
|
||||
app: rollout-sample
|
||||
template:
|
||||
metadata:
|
||||
labels:
|
||||
app: rollout-sample
|
||||
spec:
|
||||
containers:
|
||||
- name: init
|
||||
image: init-container:v1
|
||||
livenessProbe:
|
||||
initialDelaySeconds: 10
|
||||
- name: main
|
||||
image: main-container:v1
|
||||
@@ -1,62 +0,0 @@
|
||||
swagger: "2.0"
|
||||
info:
|
||||
title: HTTPProxy
|
||||
version: "v1"
|
||||
paths: {}
|
||||
definitions:
|
||||
io.projectcontour.v1.HTTPProxy:
|
||||
type: object
|
||||
x-kubernetes-group-version-kind:
|
||||
- group: projectcontour.io
|
||||
version: v1
|
||||
kind: HTTPProxy
|
||||
properties:
|
||||
spec:
|
||||
type: object
|
||||
properties:
|
||||
routes:
|
||||
type: array
|
||||
items:
|
||||
type: object
|
||||
properties:
|
||||
rateLimitPolicy:
|
||||
type: object
|
||||
properties:
|
||||
global:
|
||||
type: object
|
||||
properties:
|
||||
descriptors:
|
||||
type: array
|
||||
x-kubernetes-list-map-keys:
|
||||
- entries
|
||||
items:
|
||||
type: object
|
||||
properties:
|
||||
entries:
|
||||
type: array
|
||||
x-kubernetes-list-map-keys:
|
||||
- headerName
|
||||
items:
|
||||
type: object
|
||||
properties:
|
||||
requestHeader:
|
||||
type: object
|
||||
properties:
|
||||
descriptorKey:
|
||||
type: string
|
||||
headerName:
|
||||
type: string
|
||||
requestHeaderValueMatch:
|
||||
type: object
|
||||
properties:
|
||||
headers:
|
||||
type: array
|
||||
items:
|
||||
type: object
|
||||
properties:
|
||||
name:
|
||||
type: string
|
||||
contains:
|
||||
type: string
|
||||
value:
|
||||
type: string
|
||||
67
controller/testdata/schemas/rollout-schema.yaml
vendored
67
controller/testdata/schemas/rollout-schema.yaml
vendored
@@ -1,67 +0,0 @@
|
||||
swagger: "2.0"
|
||||
info:
|
||||
title: Rollout
|
||||
version: "v1alpha1"
|
||||
paths: {}
|
||||
definitions:
|
||||
argoproj.io.v1alpha1.Rollout:
|
||||
type: object
|
||||
x-kubernetes-group-version-kind:
|
||||
- group: argoproj.io
|
||||
version: v1alpha1
|
||||
kind: Rollout
|
||||
properties:
|
||||
spec:
|
||||
type: object
|
||||
properties:
|
||||
replicas:
|
||||
type: integer
|
||||
strategy:
|
||||
type: object
|
||||
properties:
|
||||
canary:
|
||||
type: object
|
||||
properties:
|
||||
steps:
|
||||
type: array
|
||||
items:
|
||||
type: object
|
||||
properties:
|
||||
setWeight:
|
||||
type: integer
|
||||
selector:
|
||||
type: object
|
||||
properties:
|
||||
matchLabels:
|
||||
type: object
|
||||
additionalProperties:
|
||||
type: string
|
||||
template:
|
||||
type: object
|
||||
properties:
|
||||
metadata:
|
||||
type: object
|
||||
properties:
|
||||
labels:
|
||||
type: object
|
||||
additionalProperties:
|
||||
type: string
|
||||
spec:
|
||||
type: object
|
||||
properties:
|
||||
containers:
|
||||
type: array
|
||||
x-kubernetes-list-map-keys:
|
||||
- name
|
||||
items:
|
||||
type: object
|
||||
properties:
|
||||
name:
|
||||
type: string
|
||||
image:
|
||||
type: string
|
||||
livenessProbe:
|
||||
type: object
|
||||
properties:
|
||||
initialDelaySeconds:
|
||||
type: integer
|
||||
29
controller/testdata/schemas/simple-app.yaml
vendored
29
controller/testdata/schemas/simple-app.yaml
vendored
@@ -1,29 +0,0 @@
|
||||
swagger: "2.0"
|
||||
info:
|
||||
title: SimpleApp
|
||||
version: "v1"
|
||||
paths: {}
|
||||
definitions:
|
||||
example.com.v1.SimpleApp:
|
||||
type: object
|
||||
x-kubernetes-group-version-kind:
|
||||
- group: example.com
|
||||
version: v1
|
||||
kind: SimpleApp
|
||||
properties:
|
||||
spec:
|
||||
type: object
|
||||
properties:
|
||||
servers:
|
||||
type: array
|
||||
x-kubernetes-list-map-keys:
|
||||
- name
|
||||
items:
|
||||
type: object
|
||||
properties:
|
||||
name:
|
||||
type: string
|
||||
port:
|
||||
type: integer
|
||||
enabled:
|
||||
type: boolean
|
||||
12
controller/testdata/simple-app-live.yaml
vendored
12
controller/testdata/simple-app-live.yaml
vendored
@@ -1,12 +0,0 @@
|
||||
apiVersion: example.com/v1
|
||||
kind: SimpleApp
|
||||
metadata:
|
||||
name: simpleapp-sample
|
||||
spec:
|
||||
servers:
|
||||
- name: server1
|
||||
port: 8081 # port changed in live from 8080
|
||||
enabled: true
|
||||
- name: server2
|
||||
port: 9090
|
||||
enabled: true # enabled changed in live from false
|
||||
12
controller/testdata/simple-app-target.yaml
vendored
12
controller/testdata/simple-app-target.yaml
vendored
@@ -1,12 +0,0 @@
|
||||
apiVersion: example.com/v1
|
||||
kind: SimpleApp
|
||||
metadata:
|
||||
name: simpleapp-sample
|
||||
spec:
|
||||
servers:
|
||||
- name: server1
|
||||
port: 8080
|
||||
enabled: true
|
||||
- name: server2
|
||||
port: 9090
|
||||
enabled: false
|
||||
25
controller/testdata/target-rollout.yaml
vendored
25
controller/testdata/target-rollout.yaml
vendored
@@ -1,25 +0,0 @@
|
||||
apiVersion: argoproj.io/v1alpha1
|
||||
kind: Rollout
|
||||
metadata:
|
||||
name: rollout-sample
|
||||
spec:
|
||||
replicas: 2
|
||||
strategy:
|
||||
canary:
|
||||
steps:
|
||||
- setWeight: 20
|
||||
selector:
|
||||
matchLabels:
|
||||
app: rollout-sample
|
||||
template:
|
||||
metadata:
|
||||
labels:
|
||||
app: rollout-sample
|
||||
spec:
|
||||
containers:
|
||||
- name: init
|
||||
image: init-container:v1
|
||||
livenessProbe:
|
||||
initialDelaySeconds: 15
|
||||
- name: main
|
||||
image: main-container:v1
|
||||
@@ -18,9 +18,11 @@ These are the upcoming releases dates:
|
||||
| v2.13 | Monday, Sep. 16, 2024 | Monday, Nov. 4, 2024 | [Regina Voloshin](https://github.com/reggie-k) | [Pavel Kostohrys](https://github.com/pasha-codefresh) | [checklist](https://github.com/argoproj/argo-cd/issues/19513) |
|
||||
| v2.14 | Monday, Dec. 16, 2024 | Monday, Feb. 3, 2025 | [Ryan Umstead](https://github.com/rumstead) | [Pavel Kostohrys](https://github.com/pasha-codefresh) | [checklist](https://github.com/argoproj/argo-cd/issues/20869) |
|
||||
| v3.0 | Monday, Mar. 17, 2025 | Tuesday, May 6, 2025 | [Regina Voloshin](https://github.com/reggie-k) | | [checklist](https://github.com/argoproj/argo-cd/issues/21735) |
|
||||
| v3.1 | Monday, Jun. 16, 2025 | Monday, Aug. 4, 2025 | [Christian Hernandez](https://github.com/christianh814) | [Alexandre Gaudreault](https://github.com/agaudreault) | [checklist](#) |
|
||||
| v3.2 | Monday, Sep. 15, 2025 | Monday, Nov. 3, 2025 | [Nitish Kumar](https://github.com/nitishfy) | | [checklist](#) |
|
||||
| v3.3 | Monday, Dec. 15, 2025 | Monday, Feb. 2, 2026 | | |
|
||||
| v3.1 | Monday, Jun. 16, 2025 | Monday, Aug. 4, 2025 | [Christian Hernandez](https://github.com/christianh814) | [Alexandre Gaudreault](https://github.com/agaudreault) | [checklist](https://github.com/argoproj/argo-cd/issues/23347) |
|
||||
| v3.2 | Monday, Sep. 15, 2025 | Monday, Nov. 3, 2025 | [Nitish Kumar](https://github.com/nitishfy) | [Michael Crenshaw](https://github.com/crenshaw-dev) | [checklist](https://github.com/argoproj/argo-cd/issues/24539) |
|
||||
| v3.3 | Monday, Dec. 15, 2025 | Monday, Feb. 2, 2026 | [Peter Jiang](https://github.com/pjiang-dev) | [Regina Voloshin](https://github.com/reggie-k) | [checklist](https://github.com/argoproj/argo-cd/issues/25211) |
|
||||
| v3.4 | Monday, Mar. 16, 2026 | Monday, May. 4, 2026 | | |
|
||||
| v3.5 | Monday, Jun. 15, 2026 | Monday, Aug. 3, 2026 | | |
|
||||
|
||||
Actual release dates might differ from the plan by a few days.
|
||||
|
||||
|
||||
@@ -219,6 +219,8 @@ data:
|
||||
reposerver.git.lsremote.parallelism.limit: "0"
|
||||
# Git requests timeout.
|
||||
reposerver.git.request.timeout: "15s"
|
||||
# Enable builtin git configuration options that are required for correct argocd-repo-server operation (default "true")
|
||||
reposerver.enable.builtin.git.config: "true"
|
||||
# Include hidden directories from Git
|
||||
reposerver.include.hidden.directories: "false"
|
||||
|
||||
|
||||
@@ -121,6 +121,18 @@ spec:
|
||||
...
|
||||
```
|
||||
|
||||
### Deleting child applications
|
||||
|
||||
When working with the App of Apps pattern, you may need to delete individual child applications. Starting in 3.2, Argo CD provides consistent deletion behaviour whether you delete from the Applications List or from the parent application's Resource Tree.
|
||||
|
||||
For detailed information about deletion options and behaviour, including:
|
||||
- Consistent deletion across UI views
|
||||
- Non-cascading (orphan) deletion to preserve managed resources
|
||||
- Child application detection and improved dialog messages
|
||||
- Best practices and example scenarios
|
||||
|
||||
See [Deleting Applications in the UI](../user-guide/app_deletion.md#deleting-applications-in-the-ui).
|
||||
|
||||
### Ignoring differences in child applications
|
||||
|
||||
To allow changes in child apps without triggering an out-of-sync status, or modification for debugging etc, the app of apps pattern works with [diff customization](../user-guide/diffing/). The example below shows how to ignore changes to syncPolicy and other common values.
|
||||
|
||||
43
docs/operator-manual/git_configuration.md
Normal file
43
docs/operator-manual/git_configuration.md
Normal file
@@ -0,0 +1,43 @@
|
||||
|
||||
# Git Configuration
|
||||
|
||||
## System Configuration
|
||||
|
||||
Argo CD uses the Git installation from its base image (Ubuntu), which
|
||||
includes a standard system configuration file located at
|
||||
`/etc/gitconfig`. This file is minimal, just defining filters
|
||||
necessary for Git LFS functionality.
|
||||
|
||||
You can customize Git's system configuration by mounting a file from a
|
||||
ConfigMap or by creating a custom Argo CD image.
|
||||
|
||||
## Global Configuration
|
||||
|
||||
Argo CD runs Git with the `HOME` environment variable set to
|
||||
`/dev/null`. As a result, global Git configuration is not supported.
|
||||
|
||||
## Built-in Configuration
|
||||
|
||||
The `argocd-repo-server` adds specific configuration parameters to the
|
||||
Git environment to ensure proper Argo CD operation. These built-in
|
||||
settings override any conflicting values from the system Git
|
||||
configuration.
|
||||
|
||||
Currently, the following built-in configuration options are set:
|
||||
|
||||
- `maintenance.autoDetach=false`
|
||||
- `gc.autoDetach=false`
|
||||
|
||||
These settings force Git's repository maintenance tasks to run in the
|
||||
foreground. This prevents Git from running detached background
|
||||
processes that could modify the repository and interfere with
|
||||
subsequent Git invocations from `argocd-repo-server`.
|
||||
|
||||
You can disable these built-in settings by setting the
|
||||
`argocd-cmd-params-cm` value `reposerver.enable.builtin.git.config` to
|
||||
`"false"`. This allows you to experiment with background processing or
|
||||
if you are certain that concurrency issues will not occur in your
|
||||
environment.
|
||||
|
||||
> [!NOTE]
|
||||
> Disabling this is not recommended and is not supported!
|
||||
@@ -11,6 +11,10 @@ The notification service is used to push events to [Alertmanager](https://github
|
||||
* `basicAuth` - optional, server auth
|
||||
* `bearerToken` - optional, server auth
|
||||
* `timeout` - optional, the timeout in seconds used when sending alerts, default is "3 seconds"
|
||||
* `maxIdleConns` - optional, maximum number of idle (keep-alive) connections across all hosts.
|
||||
* `maxIdleConnsPerHost` - optional, maximum number of idle (keep-alive) connections per host.
|
||||
* `maxConnsPerHost` - optional, maximum total connections per host.
|
||||
* `idleConnTimeout` - optional, maximum amount of time an idle (keep-alive) connection will remain open before closing.
|
||||
|
||||
`basicAuth` or `bearerToken` is used for authentication, you can choose one. If the two are set at the same time, `basicAuth` takes precedence over `bearerToken`.
|
||||
|
||||
|
||||
@@ -12,6 +12,19 @@ The Email notification service sends email notifications using SMTP protocol and
|
||||
* `html` - optional bool, true or false
|
||||
* `insecure_skip_verify` - optional bool, true or false
|
||||
|
||||
### Using Gmail
|
||||
|
||||
When configuring Gmail as the SMTP service:
|
||||
|
||||
* `username` - Must be your Gmail address.
|
||||
* `password` - Use an App Password, not your regular Gmail password.
|
||||
|
||||
To Generate an app password, follow this link https://myaccount.google.com/apppasswords
|
||||
|
||||
!!! note
|
||||
This applies to personal Gmail accounts (non-Google Workspace). For Google Workspace users, SMTP settings
|
||||
and authentication methods may differ.
|
||||
|
||||
## Example
|
||||
|
||||
The following snippet contains sample Gmail service configuration:
|
||||
@@ -23,11 +36,11 @@ metadata:
|
||||
name: argocd-notifications-cm
|
||||
data:
|
||||
service.email.gmail: |
|
||||
username: $email-username
|
||||
password: $email-password
|
||||
username: $username
|
||||
password: $password
|
||||
host: smtp.gmail.com
|
||||
port: 465
|
||||
from: $email-username
|
||||
from: $email-address
|
||||
```
|
||||
|
||||
Without authentication:
|
||||
@@ -41,7 +54,7 @@ data:
|
||||
service.email.example: |
|
||||
host: smtp.example.com
|
||||
port: 587
|
||||
from: $email-username
|
||||
from: $email-address
|
||||
```
|
||||
|
||||
## Template
|
||||
|
||||
@@ -8,6 +8,10 @@ The GitHub notification service changes commit status using [GitHub Apps](https:
|
||||
- `installationID` - the app installation id
|
||||
- `privateKey` - the app private key
|
||||
- `enterpriseBaseURL` - optional URL, e.g. https://git.example.com/api/v3
|
||||
- `maxIdleConns` - optional, maximum number of idle (keep-alive) connections across all hosts.
|
||||
- `maxIdleConnsPerHost` - optional, maximum number of idle (keep-alive) connections per host.
|
||||
- `maxConnsPerHost` - optional, maximum total connections per host.
|
||||
- `idleConnTimeout` - optional, maximum amount of time an idle (keep-alive) connection will remain open before closing.
|
||||
|
||||
> ⚠️ _NOTE:_ Specifying `/api/v3` in the `enterpriseBaseURL` is required until [argoproj/notifications-engine#205](https://github.com/argoproj/notifications-engine/issues/205) is resolved.
|
||||
|
||||
|
||||
@@ -9,6 +9,10 @@ Available parameters :
|
||||
* `apiURL` - the server url, e.g. https://grafana.example.com
|
||||
* `apiKey` - the API key for the serviceaccount
|
||||
* `insecureSkipVerify` - optional bool, true or false
|
||||
* `maxIdleConns` - optional, maximum number of idle (keep-alive) connections across all hosts.
|
||||
* `maxIdleConnsPerHost` - optional, maximum number of idle (keep-alive) connections per host.
|
||||
* `maxConnsPerHost` - optional, maximum total connections per host.
|
||||
* `idleConnTimeout` - optional, maximum amount of time an idle (keep-alive) connection will remain open before closing.
|
||||
|
||||
1. Login to your Grafana instance as `admin`
|
||||
2. On the left menu, go to Configuration / API Keys
|
||||
|
||||
@@ -5,6 +5,10 @@
|
||||
* `apiURL` - the server url, e.g. https://mattermost.example.com
|
||||
* `token` - the bot token
|
||||
* `insecureSkipVerify` - optional bool, true or false
|
||||
* `maxIdleConns` - optional, maximum number of idle (keep-alive) connections across all hosts.
|
||||
* `maxIdleConnsPerHost` - optional, maximum number of idle (keep-alive) connections per host.
|
||||
* `maxConnsPerHost` - optional, maximum total connections per host.
|
||||
* `idleConnTimeout` - optional, maximum amount of time an idle (keep-alive) connection will remain open before closing, e.g. '90s'.
|
||||
|
||||
## Configuration
|
||||
|
||||
|
||||
@@ -4,6 +4,10 @@
|
||||
|
||||
* `apiURL` - the api server url, e.g. https://api.newrelic.com
|
||||
* `apiKey` - a [NewRelic ApiKey](https://docs.newrelic.com/docs/apis/rest-api-v2/get-started/introduction-new-relic-rest-api-v2/#api_key)
|
||||
* `maxIdleConns` - optional, maximum number of idle (keep-alive) connections across all hosts.
|
||||
* `maxIdleConnsPerHost` - optional, maximum number of idle (keep-alive) connections per host.
|
||||
* `maxConnsPerHost` - optional, maximum total connections per host.
|
||||
* `idleConnTimeout` - optional, maximum amount of time an idle (keep-alive) connection will remain open before closing, e.g. '90s'.
|
||||
|
||||
## Configuration
|
||||
|
||||
|
||||
@@ -47,7 +47,8 @@ metadata:
|
||||
* [Grafana](./grafana.md)
|
||||
* [Webhook](./webhook.md)
|
||||
* [Telegram](./telegram.md)
|
||||
* [Teams](./teams.md)
|
||||
* [Teams (Office 365 Connectors)](./teams.md) - Legacy service (deprecated, retires March 31, 2026)
|
||||
* [Teams Workflows](./teams-workflows.md) - Recommended replacement for Office 365 Connectors
|
||||
* [Google Chat](./googlechat.md)
|
||||
* [Rocket.Chat](./rocketchat.md)
|
||||
* [Pushover](./pushover.md)
|
||||
|
||||
@@ -62,6 +62,8 @@ The parameters for the PagerDuty configuration in the template generally match w
|
||||
* `group` - Logical grouping of components of a service.
|
||||
* `class` - The class/type of the event.
|
||||
* `url` - The URL that should be used for the link "View in ArgoCD" in PagerDuty.
|
||||
* `dedupKey` - A string used by PagerDuty to deduplicate and correlate events. Events with the same `dedupKey` will be grouped into the same incident. If omitted, PagerDuty will create a new incident for each event.
|
||||
|
||||
|
||||
The `timestamp` and `custom_details` parameters are not currently supported.
|
||||
|
||||
|
||||
@@ -16,6 +16,11 @@ The Slack notification service configuration includes following settings:
|
||||
| `token` | **True** | `string` | The app's OAuth access token. | `xoxb-1234567890-1234567890123-5n38u5ed63fgzqlvuyxvxcx6` |
|
||||
| `username` | False | `string` | The app username. | `argocd` |
|
||||
| `disableUnfurl` | False | `bool` | Disable slack unfurling links in messages | `true` |
|
||||
| `maxIdleConns` | False | `int` | Maximum number of idle (keep-alive) connections across all hosts. | — |
|
||||
| `maxIdleConnsPerHost` | False | `int` | Maximum number of idle (keep-alive) connections per host. | — |
|
||||
| `maxConnsPerHost` | False | `int` | Maximum total connections per host. | — |
|
||||
| `idleConnTimeout` | False | `string` | Maximum amount of time an idle (keep-alive) connection will remain open before closing (e.g., `90s`). | — |
|
||||
|
||||
|
||||
## Configuration
|
||||
|
||||
|
||||
370
docs/operator-manual/notifications/services/teams-workflows.md
Executable file
370
docs/operator-manual/notifications/services/teams-workflows.md
Executable file
@@ -0,0 +1,370 @@
|
||||
# Teams Workflows
|
||||
|
||||
## Overview
|
||||
|
||||
The Teams Workflows notification service sends message notifications using Microsoft Teams Workflows (Power Automate). This is the recommended replacement for the legacy Office 365 Connectors service, which will be retired on March 31, 2026.
|
||||
|
||||
## Parameters
|
||||
|
||||
The Teams Workflows notification service requires specifying the following settings:
|
||||
|
||||
* `recipientUrls` - the webhook url map, e.g. `channelName: https://api.powerautomate.com/webhook/...`
|
||||
|
||||
## Supported Webhook URL Formats
|
||||
|
||||
The service supports the following Microsoft Teams Workflows webhook URL patterns:
|
||||
|
||||
- `https://api.powerautomate.com/...`
|
||||
- `https://api.powerplatform.com/...`
|
||||
- `https://flow.microsoft.com/...`
|
||||
- URLs containing `/powerautomate/` in the path
|
||||
|
||||
## Configuration
|
||||
|
||||
1. Open `Teams` and go to the channel you wish to set notifications for
|
||||
2. Click on the 3 dots next to the channel name
|
||||
3. Select`Workflows`
|
||||
4. Click on `Manage`
|
||||
5. Click `New flow`
|
||||
6. Write `Send webhook alerts to a channel` in the search bar or select it from the template list
|
||||
7. Choose your team and channel
|
||||
8. Configure the webhook name and settings
|
||||
9. Copy the webhook URL (it will be from `api.powerautomate.com`, `api.powerplatform.com`, or `flow.microsoft.com`)
|
||||
10. Store it in `argocd-notifications-secret` and define it in `argocd-notifications-cm`
|
||||
|
||||
```yaml
|
||||
apiVersion: v1
|
||||
kind: ConfigMap
|
||||
metadata:
|
||||
name: argocd-notifications-cm
|
||||
data:
|
||||
service.teams-workflows: |
|
||||
recipientUrls:
|
||||
channelName: $channel-workflows-url
|
||||
```
|
||||
|
||||
```yaml
|
||||
apiVersion: v1
|
||||
kind: Secret
|
||||
metadata:
|
||||
name: <secret-name>
|
||||
stringData:
|
||||
channel-workflows-url: https://api.powerautomate.com/webhook/your-webhook-id
|
||||
```
|
||||
|
||||
11. Create subscription for your Teams Workflows integration:
|
||||
|
||||
```yaml
|
||||
apiVersion: argoproj.io/v1alpha1
|
||||
kind: Application
|
||||
metadata:
|
||||
annotations:
|
||||
notifications.argoproj.io/subscribe.on-sync-succeeded.teams-workflows: channelName
|
||||
```
|
||||
|
||||
## Channel Support
|
||||
|
||||
- ✅ Standard Teams channels
|
||||
- ✅ Shared channels (as of December 2025)
|
||||
- ✅ Private channels (as of December 2025)
|
||||
|
||||
Teams Workflows provides enhanced channel support compared to Office 365 Connectors, allowing you to post to shared and private channels in addition to standard channels.
|
||||
|
||||
## Adaptive Card Format
|
||||
|
||||
The Teams Workflows service uses **Adaptive Cards** exclusively, which is the modern, flexible card format for Microsoft Teams. All notifications are automatically converted to Adaptive Card format and wrapped in the required message envelope.
|
||||
|
||||
### Option 1: Using Template Fields (Recommended)
|
||||
|
||||
The service automatically converts template fields to Adaptive Card format. This is the simplest and most maintainable approach:
|
||||
|
||||
```yaml
|
||||
template.app-sync-succeeded: |
|
||||
teams-workflows:
|
||||
# ThemeColor supports Adaptive Card semantic colors: "Good", "Warning", "Attention", "Accent"
|
||||
# or hex colors like "#000080"
|
||||
themeColor: "Good"
|
||||
title: Application {{.app.metadata.name}} has been successfully synced
|
||||
text: Application {{.app.metadata.name}} has been successfully synced at {{.app.status.operationState.finishedAt}}.
|
||||
summary: "{{.app.metadata.name}} sync succeeded"
|
||||
facts: |
|
||||
[{
|
||||
"name": "Sync Status",
|
||||
"value": "{{.app.status.sync.status}}"
|
||||
}, {
|
||||
"name": "Repository",
|
||||
"value": "{{.app.spec.source.repoURL}}"
|
||||
}]
|
||||
sections: |
|
||||
[{
|
||||
"facts": [
|
||||
{
|
||||
"name": "Namespace",
|
||||
"value": "{{.app.metadata.namespace}}"
|
||||
},
|
||||
{
|
||||
"name": "Cluster",
|
||||
"value": "{{.app.spec.destination.server}}"
|
||||
}
|
||||
]
|
||||
}]
|
||||
potentialAction: |-
|
||||
[{
|
||||
"@type": "OpenUri",
|
||||
"name": "View in Argo CD",
|
||||
"targets": [{
|
||||
"os": "default",
|
||||
"uri": "{{.context.argocdUrl}}/applications/{{.app.metadata.name}}"
|
||||
}]
|
||||
}]
|
||||
```
|
||||
|
||||
**How it works:**
|
||||
- `title` → Converted to a large, bold TextBlock
|
||||
- `text` → Converted to a regular TextBlock
|
||||
- `facts` → Converted to a FactSet element
|
||||
- `sections` → Facts within sections are extracted and converted to FactSet elements
|
||||
- `potentialAction` → OpenUri actions are converted to Action.OpenUrl
|
||||
- `themeColor` → Applied to the title TextBlock (supports semantic colors like "Good", "Warning", "Attention", "Accent" or hex colors)
|
||||
|
||||
### Option 2: Custom Adaptive Card JSON
|
||||
|
||||
For full control and advanced features, you can provide a complete Adaptive Card JSON template:
|
||||
|
||||
```yaml
|
||||
template.app-sync-succeeded: |
|
||||
teams-workflows:
|
||||
adaptiveCard: |
|
||||
{
|
||||
"type": "AdaptiveCard",
|
||||
"version": "1.4",
|
||||
"body": [
|
||||
{
|
||||
"type": "TextBlock",
|
||||
"text": "Application {{.app.metadata.name}} synced successfully",
|
||||
"size": "Large",
|
||||
"weight": "Bolder",
|
||||
"color": "Good"
|
||||
},
|
||||
{
|
||||
"type": "TextBlock",
|
||||
"text": "Application {{.app.metadata.name}} has been successfully synced at {{.app.status.operationState.finishedAt}}.",
|
||||
"wrap": true
|
||||
},
|
||||
{
|
||||
"type": "FactSet",
|
||||
"facts": [
|
||||
{
|
||||
"title": "Sync Status",
|
||||
"value": "{{.app.status.sync.status}}"
|
||||
},
|
||||
{
|
||||
"title": "Repository",
|
||||
"value": "{{.app.spec.source.repoURL}}"
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
"actions": [
|
||||
{
|
||||
"type": "Action.OpenUrl",
|
||||
"title": "View in Argo CD",
|
||||
"url": "{{.context.argocdUrl}}/applications/{{.app.metadata.name}}"
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
**Note:** When using `adaptiveCard`, you only need to provide the AdaptiveCard JSON structure (not the full message envelope). The service automatically wraps it in the required `message` + `attachments` format for Teams Workflows.
|
||||
|
||||
**Important:** If you provide `adaptiveCard`, it takes precedence over all other template fields (`title`, `text`, `facts`, etc.).
|
||||
|
||||
## Template Fields
|
||||
|
||||
The Teams Workflows service supports the following template fields, which are automatically converted to Adaptive Card format:
|
||||
|
||||
### Standard Fields
|
||||
|
||||
- `title` - Message title (converted to large, bold TextBlock)
|
||||
- `text` - Message text content (converted to TextBlock)
|
||||
- `summary` - Summary text (currently not used in Adaptive Cards, but preserved for compatibility)
|
||||
- `themeColor` - Color for the title. Supports:
|
||||
- Semantic colors: `"Good"` (green), `"Warning"` (yellow), `"Attention"` (red), `"Accent"` (blue)
|
||||
- Hex colors: `"#000080"`, `"#FF0000"`, etc.
|
||||
- `facts` - JSON array of fact key-value pairs (converted to FactSet)
|
||||
```yaml
|
||||
facts: |
|
||||
[{
|
||||
"name": "Status",
|
||||
"value": "{{.app.status.sync.status}}"
|
||||
}]
|
||||
```
|
||||
- `sections` - JSON array of sections containing facts (facts are extracted and converted to FactSet)
|
||||
```yaml
|
||||
sections: |
|
||||
[{
|
||||
"facts": [{
|
||||
"name": "Namespace",
|
||||
"value": "{{.app.metadata.namespace}}"
|
||||
}]
|
||||
}]
|
||||
```
|
||||
- `potentialAction` - JSON array of action buttons (OpenUri actions converted to Action.OpenUrl)
|
||||
```yaml
|
||||
potentialAction: |-
|
||||
[{
|
||||
"@type": "OpenUri",
|
||||
"name": "View Details",
|
||||
"targets": [{
|
||||
"os": "default",
|
||||
"uri": "{{.context.argocdUrl}}/applications/{{.app.metadata.name}}"
|
||||
}]
|
||||
}]
|
||||
```
|
||||
|
||||
### Advanced Fields
|
||||
|
||||
- `adaptiveCard` - Complete Adaptive Card JSON template (takes precedence over all other fields)
|
||||
- Only provide the AdaptiveCard structure, not the message envelope
|
||||
- Supports full Adaptive Card 1.4 specification
|
||||
- Allows access to all Adaptive Card features (containers, columns, images, etc.)
|
||||
|
||||
- `template` - Raw JSON template (legacy, use `adaptiveCard` instead)
|
||||
|
||||
### Field Conversion Details
|
||||
|
||||
| Template Field | Adaptive Card Element | Notes |
|
||||
|---------------|----------------------|-------|
|
||||
| `title` | `TextBlock` with `size: "Large"`, `weight: "Bolder"` | ThemeColor applied to this element |
|
||||
| `text` | `TextBlock` with `wrap: true` | Uses `n.Message` if `text` is empty |
|
||||
| `facts` | `FactSet` | Each fact becomes a `title`/`value` pair |
|
||||
| `sections[].facts` | `FactSet` | Facts extracted from sections |
|
||||
| `potentialAction[OpenUri]` | `Action.OpenUrl` | Only OpenUri actions are converted |
|
||||
| `themeColor` | Applied to title `TextBlock.color` | Supports semantic and hex colors |
|
||||
|
||||
## Migration from Office 365 Connectors
|
||||
|
||||
If you're currently using the `teams` service with Office 365 Connectors, follow these steps to migrate:
|
||||
|
||||
1. **Create a new Workflows webhook** using the configuration steps above
|
||||
|
||||
2. **Update your service configuration:**
|
||||
- Change from `service.teams` to `service.teams-workflows`
|
||||
- Update the webhook URL to your new Workflows webhook URL
|
||||
|
||||
3. **Update your templates:**
|
||||
- Change `teams:` to `teams-workflows:` in your templates
|
||||
- Your existing template fields (`title`, `text`, `facts`, `sections`, `potentialAction`) will automatically be converted to Adaptive Card format
|
||||
- No changes needed to your template structure - the conversion is automatic
|
||||
|
||||
4. **Update your subscriptions:**
|
||||
```yaml
|
||||
# Old
|
||||
notifications.argoproj.io/subscribe.on-sync-succeeded.teams: channelName
|
||||
|
||||
# New
|
||||
notifications.argoproj.io/subscribe.on-sync-succeeded.teams-workflows: channelName
|
||||
```
|
||||
|
||||
5. **Test and verify:**
|
||||
- Send a test notification to verify it works correctly
|
||||
- Once verified, you can remove the old Office 365 Connector configuration
|
||||
|
||||
**Note:** Your existing templates will work without modification. The service automatically converts your template fields to Adaptive Card format, so you get the benefits of modern cards without changing your templates.
|
||||
|
||||
## Differences from Office 365 Connectors
|
||||
|
||||
| Feature | Office 365 Connectors | Teams Workflows |
|
||||
|---------|----------------------|-----------------|
|
||||
| Service Name | `teams` | `teams-workflows` |
|
||||
| Standard Channels | ✅ | ✅ |
|
||||
| Shared Channels | ❌ | ✅ (Dec 2025+) |
|
||||
| Private Channels | ❌ | ✅ (Dec 2025+) |
|
||||
| Card Format | messageCard (legacy) | Adaptive Cards (modern) |
|
||||
| Template Conversion | N/A | Automatic conversion from template fields |
|
||||
| Retirement Date | March 31, 2026 | Active |
|
||||
|
||||
## Adaptive Card Features
|
||||
|
||||
The Teams Workflows service leverages Adaptive Cards, which provide:
|
||||
|
||||
- **Rich Content**: Support for text, images, fact sets, and more
|
||||
- **Flexible Layout**: Containers, columns, and adaptive layouts
|
||||
- **Interactive Elements**: Action buttons, input fields, and more
|
||||
- **Semantic Colors**: Built-in color schemes (Good, Warning, Attention, Accent)
|
||||
- **Cross-Platform**: Works across Teams, Outlook, and other Microsoft 365 apps
|
||||
|
||||
### Example: Advanced Adaptive Card Template
|
||||
|
||||
For complex notifications, you can use the full Adaptive Card specification:
|
||||
|
||||
```yaml
|
||||
template.app-sync-succeeded-advanced: |
|
||||
teams-workflows:
|
||||
adaptiveCard: |
|
||||
{
|
||||
"type": "AdaptiveCard",
|
||||
"version": "1.4",
|
||||
"body": [
|
||||
{
|
||||
"type": "Container",
|
||||
"items": [
|
||||
{
|
||||
"type": "ColumnSet",
|
||||
"columns": [
|
||||
{
|
||||
"type": "Column",
|
||||
"width": "auto",
|
||||
"items": [
|
||||
{
|
||||
"type": "Image",
|
||||
"url": "https://example.com/success-icon.png",
|
||||
"size": "Small"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"type": "Column",
|
||||
"width": "stretch",
|
||||
"items": [
|
||||
{
|
||||
"type": "TextBlock",
|
||||
"text": "Application {{.app.metadata.name}}",
|
||||
"weight": "Bolder",
|
||||
"size": "Large"
|
||||
},
|
||||
{
|
||||
"type": "TextBlock",
|
||||
"text": "Successfully synced",
|
||||
"spacing": "None",
|
||||
"isSubtle": true
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"type": "FactSet",
|
||||
"facts": [
|
||||
{
|
||||
"title": "Status",
|
||||
"value": "{{.app.status.sync.status}}"
|
||||
},
|
||||
{
|
||||
"title": "Repository",
|
||||
"value": "{{.app.spec.source.repoURL}}"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
"actions": [
|
||||
{
|
||||
"type": "Action.OpenUrl",
|
||||
"title": "View in Argo CD",
|
||||
"url": "{{.context.argocdUrl}}/applications/{{.app.metadata.name}}"
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
@@ -1,18 +1,46 @@
|
||||
# Teams
|
||||
# Teams (Office 365 Connectors)
|
||||
|
||||
## ⚠️ Deprecation Notice
|
||||
|
||||
**Office 365 Connectors are being retired by Microsoft.**
|
||||
|
||||
Microsoft is retiring the Office 365 Connectors service in Teams. The service will be fully retired by **March 31, 2026** (extended from the original timeline of December 2025).
|
||||
|
||||
### What this means:
|
||||
- **Old Office 365 Connectors** (webhook URLs from `webhook.office.com`) will stop working after the retirement date
|
||||
- **New Power Automate Workflows** (webhook URLs from `api.powerautomate.com`, `api.powerplatform.com`, or `flow.microsoft.com`) are the recommended replacement
|
||||
|
||||
### Migration Required:
|
||||
If you are currently using Office 365 Connectors (Incoming Webhook), you should migrate to Power Automate Workflows before the retirement date. The notifications-engine automatically detects the webhook type and handles both formats, but you should plan your migration.
|
||||
|
||||
**Migration Resources:**
|
||||
- [Microsoft Deprecation Notice](https://devblogs.microsoft.com/microsoft365dev/retirement-of-office-365-connectors-within-microsoft-teams/)
|
||||
- [Create incoming webhooks with Workflows for Microsoft Teams](https://support.microsoft.com/en-us/office/create-incoming-webhooks-with-workflows-for-microsoft-teams-4b3b0b0e-0b5a-4b5a-9b5a-0b5a-4b5a-9b5a)
|
||||
|
||||
---
|
||||
|
||||
## Parameters
|
||||
|
||||
The Teams notification service send message notifications using Teams bot and requires specifying the following settings:
|
||||
The Teams notification service sends message notifications using Office 365 Connectors and requires specifying the following settings:
|
||||
|
||||
* `recipientUrls` - the webhook url map, e.g. `channelName: https://example.com`
|
||||
* `recipientUrls` - the webhook url map, e.g. `channelName: https://outlook.office.com/webhook/...`
|
||||
|
||||
> **⚠️ Deprecation Notice:** Office 365 Connectors will be retired by Microsoft on **March 31, 2026**. We recommend migrating to the [Teams Workflows service](./teams-workflows.md) for continued support and enhanced features.
|
||||
|
||||
## Configuration
|
||||
|
||||
> **💡 For Power Automate Workflows (Recommended):** See the [Teams Workflows documentation](./teams-workflows.md) for detailed configuration instructions.
|
||||
|
||||
### Office 365 Connectors (Deprecated - Retiring March 31, 2026)
|
||||
|
||||
> **⚠️ Warning:** This method is deprecated and will stop working after March 31, 2026. Please migrate to Power Automate Workflows.
|
||||
|
||||
1. Open `Teams` and goto `Apps`
|
||||
2. Find `Incoming Webhook` microsoft app and click on it
|
||||
3. Press `Add to a team` -> select team and channel -> press `Set up a connector`
|
||||
4. Enter webhook name and upload image (optional)
|
||||
5. Press `Create` then copy webhook url and store it in `argocd-notifications-secret` and define it in `argocd-notifications-cm`
|
||||
5. Press `Create` then copy webhook url (it will be from `webhook.office.com`)
|
||||
6. Store it in `argocd-notifications-secret` and define it in `argocd-notifications-cm`
|
||||
|
||||
```yaml
|
||||
apiVersion: v1
|
||||
@@ -31,10 +59,20 @@ kind: Secret
|
||||
metadata:
|
||||
name: <secret-name>
|
||||
stringData:
|
||||
channel-teams-url: https://example.com
|
||||
channel-teams-url: https://webhook.office.com/webhook/your-webhook-id # Office 365 Connector (deprecated)
|
||||
```
|
||||
|
||||
6. Create subscription for your Teams integration:
|
||||
> **Note:** For Power Automate Workflows webhooks, use the [Teams Workflows service](./teams-workflows.md) instead.
|
||||
|
||||
### Webhook Type Detection
|
||||
|
||||
The `teams` service supports Office 365 Connectors (deprecated):
|
||||
|
||||
- **Office 365 Connectors**: URLs from `webhook.office.com` (deprecated)
|
||||
- Requires response body to be exactly `"1"` for success
|
||||
- Will stop working after March 31, 2026
|
||||
|
||||
7. Create subscription for your Teams integration:
|
||||
|
||||
```yaml
|
||||
apiVersion: argoproj.io/v1alpha1
|
||||
@@ -44,12 +82,20 @@ metadata:
|
||||
notifications.argoproj.io/subscribe.on-sync-succeeded.teams: channelName
|
||||
```
|
||||
|
||||
## Channel Support
|
||||
|
||||
- ✅ Standard Teams channels only
|
||||
|
||||
> **Note:** Office 365 Connectors only support standard Teams channels. For shared channels or private channels, use the [Teams Workflows service](./teams-workflows.md).
|
||||
|
||||
## Templates
|
||||
|
||||

|
||||
|
||||
[Notification templates](../templates.md) can be customized to leverage teams message sections, facts, themeColor, summary and potentialAction [feature](https://docs.microsoft.com/en-us/microsoftteams/platform/webhooks-and-connectors/how-to/connectors-using).
|
||||
|
||||
The Teams service uses the **messageCard** format (MessageCard schema) which is compatible with Office 365 Connectors.
|
||||
|
||||
```yaml
|
||||
template.app-sync-succeeded: |
|
||||
teams:
|
||||
@@ -124,3 +170,7 @@ template.app-sync-succeeded: |
|
||||
teams:
|
||||
summary: "Sync Succeeded"
|
||||
```
|
||||
|
||||
## Migration to Teams Workflows
|
||||
|
||||
If you're currently using Office 365 Connectors, see the [Teams Workflows documentation](./teams-workflows.md) for migration instructions and enhanced features.
|
||||
|
||||
@@ -14,6 +14,10 @@ The Webhook notification service configuration includes following settings:
|
||||
- `retryWaitMin` - Optional, the minimum wait time between retries. Default value: 1s.
|
||||
- `retryWaitMax` - Optional, the maximum wait time between retries. Default value: 5s.
|
||||
- `retryMax` - Optional, the maximum number of retries. Default value: 3.
|
||||
- `maxIdleConns` - optional, maximum number of idle (keep-alive) connections across all hosts.
|
||||
- `maxIdleConnsPerHost` - optional, maximum number of idle (keep-alive) connections per host.
|
||||
- `maxConnsPerHost` - optional, maximum total connections per host.
|
||||
- `idleConnTimeout` - optional, maximum amount of time an idle (keep-alive) connection will remain open before closing, e.g. '90s'.
|
||||
|
||||
## Retry Behavior
|
||||
|
||||
@@ -74,6 +78,29 @@ metadata:
|
||||
notifications.argoproj.io/subscribe.<trigger-name>.<webhook-name>: ""
|
||||
```
|
||||
|
||||
4. TLS configuration (optional)
|
||||
|
||||
If your webhook server uses a custom TLS certificate, you can configure the notification service to trust it by adding the certificate to the `argocd-tls-certs-cm` ConfigMap as shown below:
|
||||
|
||||
```yaml
|
||||
apiVersion: v1
|
||||
kind: ConfigMap
|
||||
metadata:
|
||||
name: argocd-tls-certs-cm
|
||||
data:
|
||||
<hostname>: |
|
||||
-----BEGIN CERTIFICATE-----
|
||||
<TLS DATA>
|
||||
-----END CERTIFICATE-----
|
||||
```
|
||||
|
||||
*NOTE:*
|
||||
*If the custom certificate is not trusted, you may encounter errors such as:*
|
||||
```
|
||||
Put \"https://...\": x509: certificate signed by unknown authority
|
||||
```
|
||||
*Adding the server's certificate to `argocd-tls-certs-cm` resolves this issue.*
|
||||
|
||||
## Examples
|
||||
|
||||
### Set GitHub commit status
|
||||
|
||||
@@ -35,14 +35,26 @@ metadata:
|
||||
name: argocd-notifications-cm
|
||||
data:
|
||||
trigger.sync-operation-change: |
|
||||
- when: app.status.operationState.phase in ['Succeeded']
|
||||
- when: app.status?.operationState.phase in ['Succeeded']
|
||||
send: [github-commit-status]
|
||||
- when: app.status.operationState.phase in ['Running']
|
||||
- when: app.status?.operationState.phase in ['Running']
|
||||
send: [github-commit-status]
|
||||
- when: app.status.operationState.phase in ['Error', 'Failed']
|
||||
- when: app.status?.operationState.phase in ['Error', 'Failed']
|
||||
send: [app-sync-failed, github-commit-status]
|
||||
```
|
||||
|
||||
|
||||
## Accessing Optional Manifest Sections and Fields
|
||||
|
||||
Note that in the trigger example above, the `?.` (optional chaining) operator is used to access the Application's
|
||||
`status.operationState` section. This section is optional; it is not present when an operation has been initiated but has not yet
|
||||
started by the Application Controller.
|
||||
|
||||
If the `?.` operator were not used, `status.operationState` would resolve to `nil` and the evaluation of the
|
||||
`app.status.operationState.phase` expression would fail. The `app.status?.operationState.phase` expression is equivalent to
|
||||
`app.status.operationState != nil ? app.status.operationState.phase : nil`.
|
||||
|
||||
|
||||
## Avoid Sending Same Notification Too Often
|
||||
|
||||
In some cases, the trigger condition might be "flapping". The example below illustrates the problem.
|
||||
@@ -60,14 +72,14 @@ data:
|
||||
# Optional 'oncePer' property ensure that notification is sent only once per specified field value
|
||||
# E.g. following is triggered once per sync revision
|
||||
trigger.on-deployed: |
|
||||
when: app.status.operationState.phase in ['Succeeded'] and app.status.health.status == 'Healthy'
|
||||
when: app.status?.operationState.phase in ['Succeeded'] and app.status.health.status == 'Healthy'
|
||||
oncePer: app.status.sync.revision
|
||||
send: [app-sync-succeeded]
|
||||
```
|
||||
|
||||
**Mono Repo Usage**
|
||||
|
||||
When one repo is used to sync multiple applications, the `oncePer: app.status.sync.revision` field will trigger a notification for each commit. For mono repos, the better approach will be using `oncePer: app.status.operationState.syncResult.revision` statement. This way a notification will be sent only for a particular Application's revision.
|
||||
When one repo is used to sync multiple applications, the `oncePer: app.status.sync.revision` field will trigger a notification for each commit. For mono repos, the better approach will be using `oncePer: app.status?.operationState.syncResult.revision` statement. This way a notification will be sent only for a particular Application's revision.
|
||||
|
||||
### oncePer
|
||||
|
||||
@@ -122,7 +134,7 @@ Triggers have access to the set of built-in functions.
|
||||
Example:
|
||||
|
||||
```yaml
|
||||
when: time.Now().Sub(time.Parse(app.status.operationState.startedAt)).Minutes() >= 5
|
||||
when: time.Now().Sub(time.Parse(app.status?.operationState.startedAt)).Minutes() >= 5
|
||||
```
|
||||
|
||||
{!docs/operator-manual/notifications/functions.md!}
|
||||
|
||||
@@ -21,6 +21,7 @@ argocd-repo-server [flags]
|
||||
--disable-helm-manifest-max-extracted-size Disable maximum size of helm manifest archives when extracted
|
||||
--disable-oci-manifest-max-extracted-size Disable maximum size of oci manifest archives when extracted
|
||||
--disable-tls Disable TLS on the gRPC endpoint
|
||||
--enable-builtin-git-config Enable builtin git configuration options that are required for correct argocd-repo-server operation. (default true)
|
||||
--helm-manifest-max-extracted-size string Maximum size of helm manifest archives when extracted (default "1G")
|
||||
--helm-registry-max-index-size string Maximum size of registry index file (default "1G")
|
||||
-h, --help help for argocd-repo-server
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
| Argo CD version | Kubernetes versions |
|
||||
|-----------------|---------------------|
|
||||
| 3.2 | v1.33, v1.32, v1.31, v1.30 |
|
||||
| 3.1 | v1.33, v1.32, v1.31, v1.30 |
|
||||
| 3.2 | v1.34, v1.33, v1.32, v1.31 |
|
||||
| 3.1 | v1.34, v1.33, v1.32, v1.31 |
|
||||
| 3.0 | v1.32, v1.31, v1.30, v1.29 |
|
||||
|
||||
@@ -55,4 +55,16 @@ spec:
|
||||
+ protocol: UDP
|
||||
+ - port: 53
|
||||
+ protocol: TCP
|
||||
```
|
||||
```
|
||||
|
||||
## Added Healthchecks
|
||||
|
||||
* [route53.aws.crossplane.io/ResourceRecordSet](https://github.com/argoproj/argo-cd/commit/666499f6108124ef7bfa0c6cc616770c6dc4f42c)
|
||||
* [cloudfront.aws.crossplane.io/Distribution](https://github.com/argoproj/argo-cd/commit/21c384f42354ada2b94c18773104527eb27f86bc)
|
||||
* [beat.k8s.elastic.co/Beat](https://github.com/argoproj/argo-cd/commit/5100726fd61617a0001a27233cfe8ac4354bdbed)
|
||||
* [apps.kruise.io/AdvancedCronjob](https://github.com/argoproj/argo-cd/commit/d6da9f2a15fba708d70531c5b3f2797663fb3c08)
|
||||
* [apps.kruise.io/BroadcastJob](https://github.com/argoproj/argo-cd/commit/d6da9f2a15fba708d70531c5b3f2797663fb3c08)
|
||||
* [apps.kruise.io/CloneSet](https://github.com/argoproj/argo-cd/commit/d6da9f2a15fba708d70531c5b3f2797663fb3c08)
|
||||
* [apps.kruise.io/DaemonSet](https://github.com/argoproj/argo-cd/commit/d6da9f2a15fba708d70531c5b3f2797663fb3c08)
|
||||
* [apps.kruise.io/StatefulSet](https://github.com/argoproj/argo-cd/commit/d6da9f2a15fba708d70531c5b3f2797663fb3c08)
|
||||
* [rollouts.kruise.io/Rollout](https://github.com/argoproj/argo-cd/commit/d6da9f2a15fba708d70531c5b3f2797663fb3c08)
|
||||
@@ -57,3 +57,24 @@ The affected ApplicationSet fields are the following (jq selector syntax):
|
||||
* `.spec.generators[].clusterDecisionResource.labelSelector`
|
||||
* `.spec.generators[].matrix.generators[].selector`
|
||||
* `.spec.generators[].merge.generators[].selector`
|
||||
|
||||
## Added Healthchecks
|
||||
|
||||
* [core.humio.com/HumioAction](https://github.com/argoproj/argo-cd/commit/1cd6fcac4f38edf3cd3b5409fa1b6d4aa4ad2694)
|
||||
* [core.humio.com/HumioAlert](https://github.com/argoproj/argo-cd/commit/1cd6fcac4f38edf3cd3b5409fa1b6d4aa4ad2694)
|
||||
* [core.humio.com/HumioCluster](https://github.com/argoproj/argo-cd/commit/1cd6fcac4f38edf3cd3b5409fa1b6d4aa4ad2694)
|
||||
* [core.humio.com/HumioIngestToken](https://github.com/argoproj/argo-cd/commit/1cd6fcac4f38edf3cd3b5409fa1b6d4aa4ad2694)
|
||||
* [core.humio.com/HumioParser](https://github.com/argoproj/argo-cd/commit/1cd6fcac4f38edf3cd3b5409fa1b6d4aa4ad2694)
|
||||
* [core.humio.com/HumioRepository](https://github.com/argoproj/argo-cd/commit/1cd6fcac4f38edf3cd3b5409fa1b6d4aa4ad2694)
|
||||
* [core.humio.com/HumioView](https://github.com/argoproj/argo-cd/commit/1cd6fcac4f38edf3cd3b5409fa1b6d4aa4ad2694)
|
||||
* [k8s.mariadb.com/Backup](https://github.com/argoproj/argo-cd/commit/440fbac12b7469fd3ed4a6e1f6ace5cf7eacaf39)
|
||||
* [k8s.mariadb.com/Database](https://github.com/argoproj/argo-cd/commit/440fbac12b7469fd3ed4a6e1f6ace5cf7eacaf39)
|
||||
* [k8s.mariadb.com/Grant](https://github.com/argoproj/argo-cd/commit/440fbac12b7469fd3ed4a6e1f6ace5cf7eacaf39)
|
||||
* [k8s.mariadb.com/MariaDB](https://github.com/argoproj/argo-cd/commit/440fbac12b7469fd3ed4a6e1f6ace5cf7eacaf39)
|
||||
* [k8s.mariadb.com/SqlJob](https://github.com/argoproj/argo-cd/commit/440fbac12b7469fd3ed4a6e1f6ace5cf7eacaf39)
|
||||
* [k8s.mariadb.com/User](https://github.com/argoproj/argo-cd/commit/440fbac12b7469fd3ed4a6e1f6ace5cf7eacaf39)
|
||||
* [kafka.strimzi.io/KafkaBridge](https://github.com/argoproj/argo-cd/commit/f13861740c17be1ab261f986532706cdda638b24)
|
||||
* [kafka.strimzi.io/KafkaConnector](https://github.com/argoproj/argo-cd/commit/f13861740c17be1ab261f986532706cdda638b24)
|
||||
* [keda.sh/ScaledObject](https://github.com/argoproj/argo-cd/commit/9bc9ff9c7a3573742a767c38679cbefb4f07c1c0)
|
||||
* [openfaas.com/Function](https://github.com/argoproj/argo-cd/commit/2a05ae02ab90ae06fefa97ed6b9310590d317783)
|
||||
* [camel.apache.org/Integration](https://github.com/argoproj/argo-cd/commit/1e2f5987d25307581cd56b8fe9d329633e0f704f)
|
||||
@@ -68,6 +68,41 @@ The default extension for log files generated by Argo CD when using the "Downloa
|
||||
- Consistency with standard log file conventions.
|
||||
|
||||
If you have any custom scripts or tools that depend on the `.txt` extension, please update them accordingly.
|
||||
|
||||
## Added proxy to kustomize
|
||||
|
||||
Proxy config set on repository credentials / repository templates is now passed down to the `kustomize build` command.
|
||||
|
||||
## Added Healthchecks
|
||||
|
||||
* [controlplane.cluster.x-k8s.io/AWSManagedControlPlane](https://github.com/argoproj/argo-cd/commit/f1105705126153674c79f69b5d9c9647360d16f5)
|
||||
* [policy.open-cluster-management.io/CertificatePolicy](https://github.com/argoproj/argo-cd/commit/d2231577c7f667d86bd0aa9505f871ecf1fde2bb)
|
||||
* [policy.open-cluster-management.io/ConfigurationPolicy](https://github.com/argoproj/argo-cd/commit/d2231577c7f667d86bd0aa9505f871ecf1fde2bb)
|
||||
* [policy.open-cluster-management.io/OperatorPolicy](https://github.com/argoproj/argo-cd/commit/d2231577c7f667d86bd0aa9505f871ecf1fde2bb)
|
||||
* [policy.open-cluster-management.io/Policy](https://github.com/argoproj/argo-cd/commit/d2231577c7f667d86bd0aa9505f871ecf1fde2bb)
|
||||
* [PodDisruptionBudget](https://github.com/argoproj/argo-cd/commit/e86258d8a5049260b841abc0ef1fd7f7a4b7cd45)
|
||||
* [cluster.x-k8s.io/MachinePool](https://github.com/argoproj/argo-cd/commit/59e00911304288b4f96889bf669b6ed2aecdf31b)
|
||||
* [lifecycle.keptn.sh/KeptnWorkloadVersion](https://github.com/argoproj/argo-cd/commit/ddc0b0fd3fa7e0b53170582846b20be23c301185)
|
||||
* [numaplane.numaproj.io/ISBServiceRollout](https://github.com/argoproj/argo-cd/commit/d6bc02b1956a375f853e9d5c37d97ee6963154df)
|
||||
* [numaplane.numaproj.io/NumaflowControllerRollout](https://github.com/argoproj/argo-cd/commit/d6bc02b1956a375f853e9d5c37d97ee6963154df)
|
||||
* [numaplane.numaproj.io/PipelineRollout](https://github.com/argoproj/argo-cd/commit/d6bc02b1956a375f853e9d5c37d97ee6963154df)
|
||||
* [rds.aws.crossplane.io/DBCluster](https://github.com/argoproj/argo-cd/commit/f26b76a7aa81637474cfb7992629ea1007124606)
|
||||
* [rds.aws.crossplane.io/DBInstance](https://github.com/argoproj/argo-cd/commit/f26b76a7aa81637474cfb7992629ea1007124606)
|
||||
* [iam.aws.crossplane.io/Policy](https://github.com/argoproj/argo-cd/commit/7f338e910f11929d172b39f5c2b395948529f7e8)
|
||||
* [iam.aws.crossplane.io/RolePolicyAttachment](https://github.com/argoproj/argo-cd/commit/7f338e910f11929d172b39f5c2b395948529f7e8)
|
||||
* [iam.aws.crossplane.io/Role](https://github.com/argoproj/argo-cd/commit/7f338e910f11929d172b39f5c2b395948529f7e8)
|
||||
* [s3.aws.crossplane.io/Bucket](https://github.com/argoproj/argo-cd/commit/7f338e910f11929d172b39f5c2b395948529f7e8)
|
||||
* [metrics.keptn.sh/KeptnMetric](https://github.com/argoproj/argo-cd/commit/326cc4a06b2cb5ac99797d3f04c2d4c48b8692e2)
|
||||
* [metrics.keptn.sh/Analysis](https://github.com/argoproj/argo-cd/commit/e26c105e527ed262cc5dc838a793841017ba316a)
|
||||
* [numaplane.numaproj.io/MonoVertexRollout](https://github.com/argoproj/argo-cd/commit/32ee00f1f494f69cc84d1881dda70ce514e1f737)
|
||||
* [helm.toolkit.fluxcd.io/HelmRelease](https://github.com/argoproj/argo-cd/commit/824d0dced73196bf3c148f92a164145cc115c5ea)
|
||||
* [image.toolkit.fluxcd.io/ImagePolicy](https://github.com/argoproj/argo-cd/commit/824d0dced73196bf3c148f92a164145cc115c5ea)
|
||||
* [image.toolkit.fluxcd.io/ImageRepository](https://github.com/argoproj/argo-cd/commit/824d0dced73196bf3c148f92a164145cc115c5ea)
|
||||
* [image.toolkit.fluxcd.io/ImageUpdateAutomation](https://github.com/argoproj/argo-cd/commit/824d0dced73196bf3c148f92a164145cc115c5ea)
|
||||
* [kustomize.toolkit.fluxcd.io/Kustomization](https://github.com/argoproj/argo-cd/commit/824d0dced73196bf3c148f92a164145cc115c5ea)
|
||||
* [notification.toolkit.fluxcd.io/Receiver](https://github.com/argoproj/argo-cd/commit/824d0dced73196bf3c148f92a164145cc115c5ea)
|
||||
* [source.toolkit.fluxcd.io/Bucket](https://github.com/argoproj/argo-cd/commit/824d0dced73196bf3c148f92a164145cc115c5ea)
|
||||
* [source.toolkit.fluxcd.io/GitRepository](https://github.com/argoproj/argo-cd/commit/824d0dced73196bf3c148f92a164145cc115c5ea)
|
||||
* [source.toolkit.fluxcd.io/HelmChart](https://github.com/argoproj/argo-cd/commit/824d0dced73196bf3c148f92a164145cc115c5ea)
|
||||
* [source.toolkit.fluxcd.io/HelmRepository](https://github.com/argoproj/argo-cd/commit/824d0dced73196bf3c148f92a164145cc115c5ea)
|
||||
* [source.toolkit.fluxcd.io/OCIRepository](https://github.com/argoproj/argo-cd/commit/824d0dced73196bf3c148f92a164145cc115c5ea)
|
||||
|
||||
@@ -20,3 +20,27 @@ the [CLI and Application CR](https://argo-cd.readthedocs.io/en/latest/user-guide
|
||||
Due to security reasons ([GHSA-786q-9hcg-v9ff](https://github.com/argoproj/argo-cd/security/advisories/GHSA-786q-9hcg-v9ff)),
|
||||
the project API response was sanitized to remove sensitive information. This includes
|
||||
credentials of project-scoped repositories and clusters.
|
||||
|
||||
## Added Healthchecks
|
||||
|
||||
* [platform.confluent.io/Connector](https://github.com/argoproj/argo-cd/commit/99efafb55a553a9ab962d56c20dab54ba65b7ae0)
|
||||
* [addons.cluster.x-k8s.io/ClusterResourceSet](https://github.com/argoproj/argo-cd/commit/fdf539dc6a027ef975fde23bf734f880570ccdc3)
|
||||
* [numaflow.numaproj.io/InterStepBufferService](https://github.com/argoproj/argo-cd/commit/82484ce758aa80334ecf66bfda28b9d5c41a8c30)
|
||||
* [numaflow.numaproj.io/MonoVertex](https://github.com/argoproj/argo-cd/commit/82484ce758aa80334ecf66bfda28b9d5c41a8c30)
|
||||
* [numaflow.numaproj.io/Pipeline](https://github.com/argoproj/argo-cd/commit/82484ce758aa80334ecf66bfda28b9d5c41a8c30)
|
||||
* [numaflow.numaproj.io/Vertex](https://github.com/argoproj/argo-cd/commit/82484ce758aa80334ecf66bfda28b9d5c41a8c30)
|
||||
* [acid.zalan.do/Postgresql](https://github.com/argoproj/argo-cd/commit/19d85aa9fbb40dca452f0a0f2f9ab462e02c851d)
|
||||
* [grafana.integreatly.org/Grafana](https://github.com/argoproj/argo-cd/commit/19d85aa9fbb40dca452f0a0f2f9ab462e02c851d)
|
||||
* [grafana.integreatly.org/GrafanaDatasource](https://github.com/argoproj/argo-cd/commit/19d85aa9fbb40dca452f0a0f2f9ab462e02c851d)
|
||||
* [k8s.keycloak.org/Keycloak](https://github.com/argoproj/argo-cd/commit/19d85aa9fbb40dca452f0a0f2f9ab462e02c851d)
|
||||
* [solr.apache.org/SolrCloud](https://github.com/argoproj/argo-cd/commit/19d85aa9fbb40dca452f0a0f2f9ab462e02c851d)
|
||||
* [gateway.solo.io/Gateway](https://github.com/argoproj/argo-cd/commit/2a199bc7ae70ce8b933cda81f8558916621750d5)
|
||||
* [gateway.solo.io/MatchableHttpGateway](https://github.com/argoproj/argo-cd/commit/2a199bc7ae70ce8b933cda81f8558916621750d5)
|
||||
* [gateway.solo.io/RouteOption](https://github.com/argoproj/argo-cd/commit/2a199bc7ae70ce8b933cda81f8558916621750d5)
|
||||
* [gateway.solo.io/RouteTable](https://github.com/argoproj/argo-cd/commit/2a199bc7ae70ce8b933cda81f8558916621750d5)
|
||||
* [gateway.solo.io/VirtualHostOption](https://github.com/argoproj/argo-cd/commit/2a199bc7ae70ce8b933cda81f8558916621750d5)
|
||||
* [gateway.solo.io/VirtualService](https://github.com/argoproj/argo-cd/commit/2a199bc7ae70ce8b933cda81f8558916621750d5)
|
||||
* [gloo.solo.io/Proxy](https://github.com/argoproj/argo-cd/commit/2a199bc7ae70ce8b933cda81f8558916621750d5)
|
||||
* [gloo.solo.io/Settings](https://github.com/argoproj/argo-cd/commit/2a199bc7ae70ce8b933cda81f8558916621750d5)
|
||||
* [gloo.solo.io/Upstream](https://github.com/argoproj/argo-cd/commit/2a199bc7ae70ce8b933cda81f8558916621750d5)
|
||||
* [gloo.solo.io/UpstreamGroup](https://github.com/argoproj/argo-cd/commit/2a199bc7ae70ce8b933cda81f8558916621750d5)
|
||||
@@ -288,6 +288,9 @@ resources.
|
||||
delete it. To avoid this edge case, it is recommended to perform a sync operation on your Applications, even if
|
||||
they are not out of sync, so that orphan resource detection will work as expected on the next sync.
|
||||
|
||||
After upgrading to version 3.0, the Argo CD tracking annotation will only appear on an Application’s resources when
|
||||
either a new Git commit is made or the Application is explicitly synced.
|
||||
|
||||
##### Users who rely on label-based for resources that are not managed by Argo CD
|
||||
Some users rely on label-based tracking to track resources that are not managed by Argo CD. They may set annotations
|
||||
to have Argo CD ignore the resource as extraneous or to disable pruning. If you are using label-based tracking to track
|
||||
@@ -497,4 +500,8 @@ More details for ignored resource updates in the [Diffing customization](../../u
|
||||
|
||||
Due to security reasons ([GHSA-786q-9hcg-v9ff](https://github.com/argoproj/argo-cd/security/advisories/GHSA-786q-9hcg-v9ff)),
|
||||
the project API response was sanitized to remove sensitive information. This includes
|
||||
credentials of project-scoped repositories and clusters.
|
||||
credentials of project-scoped repositories and clusters.
|
||||
|
||||
## Added Healthchecks
|
||||
|
||||
* No new added health checks
|
||||
@@ -63,3 +63,25 @@ to the [release notes](https://github.com/kubernetes-sigs/kustomize/releases/tag
|
||||
Due to security reasons ([GHSA-786q-9hcg-v9ff](https://github.com/argoproj/argo-cd/security/advisories/GHSA-786q-9hcg-v9ff)),
|
||||
the project API response was sanitized to remove sensitive information. This includes
|
||||
credentials of project-scoped repositories and clusters.
|
||||
|
||||
## Added Healthchecks
|
||||
|
||||
* [core.spinkube.dev/SpinApp](https://github.com/argoproj/argo-cd/commit/7d6604404fd3b7d77124f9623a2d7a12cc24a0bb)
|
||||
* [opentelemetry.io/OpenTelemetryCollector](https://github.com/argoproj/argo-cd/commit/65464d8b77941c65499028bb14172fc40e62e38b)
|
||||
* [logstash.k8s.elastic.co/Logstash](https://github.com/argoproj/argo-cd/commit/8f1f5c7234e694a4830744f92e1b0f8d1e3cd43d)
|
||||
* [kyverno.io/Policy](https://github.com/argoproj/argo-cd/commit/e578b85410f748c6c7b4e10ff1a5fdbca09b3328)
|
||||
* [projectcontour.io/HTTPProxy](https://github.com/argoproj/argo-cd/commit/ce4b7a28cc77959fab5b6fedd14b1f9e9a4af4f7)
|
||||
* [grafana.integreatly.org/GrafanaDashboard](https://github.com/argoproj/argo-cd/commit/5a3a10479380eb39f1c145babdf94ed1a72d054c)
|
||||
* [grafana.integreatly.org/GrafanaFolder](https://github.com/argoproj/argo-cd/commit/5a3a10479380eb39f1c145babdf94ed1a72d054c)
|
||||
* [postgresql.cnpg.io/Cluster](https://github.com/argoproj/argo-cd/commit/f4edcf7717940e44a141dadb5ca8c5fc11951cb2)
|
||||
* [gateway.networking.k8s.io/GRPCRoute](https://github.com/argoproj/argo-cd/commit/a2152a1216cdbeaa7bd02d0b2fb225390f96c77a)
|
||||
* [gateway.networking.k8s.io/Gateway](https://github.com/argoproj/argo-cd/commit/a2152a1216cdbeaa7bd02d0b2fb225390f96c77a)
|
||||
* [gateway.networking.k8s.io/HTTPRoute](https://github.com/argoproj/argo-cd/commit/a2152a1216cdbeaa7bd02d0b2fb225390f96c77a)
|
||||
* [rabbitmq.com/Binding](https://github.com/argoproj/argo-cd/commit/96039be4e075e5b22781703023bfbbe5586bd081)
|
||||
* [rabbitmq.com/Exchange](https://github.com/argoproj/argo-cd/commit/96039be4e075e5b22781703023bfbbe5586bd081)
|
||||
* [rabbitmq.com/Permission](https://github.com/argoproj/argo-cd/commit/96039be4e075e5b22781703023bfbbe5586bd081)
|
||||
* [rabbitmq.com/Policy](https://github.com/argoproj/argo-cd/commit/96039be4e075e5b22781703023bfbbe5586bd081)
|
||||
* [rabbitmq.com/Queue](https://github.com/argoproj/argo-cd/commit/96039be4e075e5b22781703023bfbbe5586bd081)
|
||||
* [rabbitmq.com/Shovel](https://github.com/argoproj/argo-cd/commit/96039be4e075e5b22781703023bfbbe5586bd081)
|
||||
* [rabbitmq.com/User](https://github.com/argoproj/argo-cd/commit/96039be4e075e5b22781703023bfbbe5586bd081)
|
||||
* [rabbitmq.com/Vhost](https://github.com/argoproj/argo-cd/commit/96039be4e075e5b22781703023bfbbe5586bd081)
|
||||
@@ -4,7 +4,7 @@
|
||||
|
||||
### Hydration paths must now be non-root
|
||||
|
||||
Source hydration now requires that every application specify a non-root path.
|
||||
Source hydration (with [Source Hydrator](../../../user-guide/source-hydrator/)) now requires that every application specify a non-root path.
|
||||
Using the repository root (for example, "" or ".") is no longer supported. This change ensures
|
||||
that hydration outputs are isolated to a dedicated subdirectory and prevents accidental overwrites
|
||||
or deletions of important files stored at the root, such as CI pipelines, documentation, or configuration files.
|
||||
@@ -92,3 +92,30 @@ credentials of project-scoped repositories and clusters.
|
||||
The `resources` field of the `status` resource of an ApplicationSet is now limited to 5000 elements by default. This is
|
||||
to prevent status bloat and exceeding etcd limits. The limit can be configured by setting the `applicationsetcontroller.status.max.resources.count`
|
||||
field in the `argocd-cmd-params-cm` ConfigMap.
|
||||
|
||||
## Added Healthchecks
|
||||
|
||||
* [datadoghq.com/DatadogMetric](https://github.com/argoproj/argo-cd/commit/5c9a5ef9a65f8e04e729fbae54a9310c0a42f6c2)
|
||||
* [CronJob](https://github.com/argoproj/argo-cd/commit/d3de4435ce86f3f85a4cc58978b2544af2ac4248)
|
||||
* [promoter.argoproj.io/ArgoCDCommitStatus](https://github.com/argoproj/argo-cd/commit/36f1a59c09ad4ef384689fa0699ff7eba60f4a20)
|
||||
* [promoter.argoproj.io/ChangeTransferPolicy](https://github.com/argoproj/argo-cd/commit/36f1a59c09ad4ef384689fa0699ff7eba60f4a20)
|
||||
* [promoter.argoproj.io/CommitStatus](https://github.com/argoproj/argo-cd/commit/36f1a59c09ad4ef384689fa0699ff7eba60f4a20)
|
||||
* [promoter.argoproj.io/PromotionStrategy](https://github.com/argoproj/argo-cd/commit/36f1a59c09ad4ef384689fa0699ff7eba60f4a20)
|
||||
* [promoter.argoproj.io/PullRequest](https://github.com/argoproj/argo-cd/commit/36f1a59c09ad4ef384689fa0699ff7eba60f4a20)
|
||||
* [coralogix.com/Alert](https://github.com/argoproj/argo-cd/commit/dcf1965c529790855647f036e4e7ea0323fbf812)
|
||||
* [coralogix.com/RecordingRuleGroupSet](https://github.com/argoproj/argo-cd/commit/dcf1965c529790855647f036e4e7ea0323fbf812)
|
||||
* [projectcontour.io/ExtensionService](https://github.com/argoproj/argo-cd/commit/4e63bc756394d93c684b6b8e8b3856e0e6b3f199)
|
||||
* [clickhouse-keeper.altinity.com/ClickHouseKeeperInstallation](https://github.com/argoproj/argo-cd/commit/c447628913da1c0134bbb1d21a9ae366804b4a8e)
|
||||
* [clickhouse.altinity.com/ClickHouseInstallation](https://github.com/argoproj/argo-cd/commit/c447628913da1c0134bbb1d21a9ae366804b4a8e)
|
||||
* [apps.3scale.net/APIManager](https://github.com/argoproj/argo-cd/commit/d954789d47276354371556ff97e6a7c3840440e0)
|
||||
* [capabilities.3scale.net/ActiveDoc](https://github.com/argoproj/argo-cd/commit/d954789d47276354371556ff97e6a7c3840440e0)
|
||||
* [capabilities.3scale.net/ApplicationAuth](https://github.com/argoproj/argo-cd/commit/d954789d47276354371556ff97e6a7c3840440e0)
|
||||
* [capabilities.3scale.net/Application](https://github.com/argoproj/argo-cd/commit/d954789d47276354371556ff97e6a7c3840440e0)
|
||||
* [capabilities.3scale.net/Backend](https://github.com/argoproj/argo-cd/commit/d954789d47276354371556ff97e6a7c3840440e0)
|
||||
* [capabilities.3scale.net/CustomPolicyDefinition](https://github.com/argoproj/argo-cd/commit/d954789d47276354371556ff97e6a7c3840440e0)
|
||||
* [capabilities.3scale.net/DeveloperAccount](https://github.com/argoproj/argo-cd/commit/d954789d47276354371556ff97e6a7c3840440e0)
|
||||
* [capabilities.3scale.net/DeveloperUser](https://github.com/argoproj/argo-cd/commit/d954789d47276354371556ff97e6a7c3840440e0)
|
||||
* [capabilities.3scale.net/OpenAPI](https://github.com/argoproj/argo-cd/commit/d954789d47276354371556ff97e6a7c3840440e0)
|
||||
* [capabilities.3scale.net/Product](https://github.com/argoproj/argo-cd/commit/d954789d47276354371556ff97e6a7c3840440e0)
|
||||
* [capabilities.3scale.net/ProxyConfigPromote](https://github.com/argoproj/argo-cd/commit/d954789d47276354371556ff97e6a7c3840440e0)
|
||||
* [capabilities.3scale.net/Tenant](https://github.com/argoproj/argo-cd/commit/d954789d47276354371556ff97e6a7c3840440e0)
|
||||
@@ -58,3 +58,120 @@ Argo CD performs [background cascading deletion](https://kubernetes.io/docs/conc
|
||||
|
||||
When you invoke `argocd app delete` with `--cascade`, the finalizer is added automatically.
|
||||
You can set the propagation policy with `--propagation-policy <foreground|background>`.
|
||||
|
||||
## Deleting Applications in the UI
|
||||
|
||||
Argo CD provides a consistent deletion experience across different views in the UI. When deleting applications, you can access the delete functionality from:
|
||||
|
||||
- **Applications List View**: The main applications page showing all applications
|
||||
- **Application Details View - Resource Tree**: When viewing an application's resource tree that contains child applications
|
||||
|
||||
### Consistent Deletion Behaviour
|
||||
|
||||
Starting in Argo CD 3.2, deletion behavior is now **consistent** across all UI views. Whether you delete an application from the Applications List or from the Resource Tree view, the same deletion mechanism and options are used.
|
||||
|
||||
Previously, deleting an application from the Resource Tree treated it as a generic Kubernetes resource, which could lead to unexpected behaviour with non-cascading deletes. Now, Argo CD properly detects Application resources and uses the standard Application deletion API in all contexts.
|
||||
|
||||
### Deleting Child Applications in App of Apps Pattern
|
||||
|
||||
When using the [App of Apps pattern](../operator-manual/cluster-bootstrapping.md), parent applications can contain child applications as resources. Argo CD automatically detects child applications and provides improved dialog messages to help you understand what you're deleting.
|
||||
|
||||
#### Child Application Detection
|
||||
|
||||
Argo CD identifies a child application by checking for the `app.kubernetes.io/part-of` label. If this label is present and has a non-empty value, the application is considered a child application.
|
||||
|
||||
#### Delete Dialog Differences
|
||||
|
||||
**When deleting a child application:**
|
||||
|
||||
- Dialog title: "Delete child application"
|
||||
- Confirmation prompt references "child application" to make it clear you're deleting a managed application
|
||||
- Additional warning note appears when deleting from the Resource Tree
|
||||
|
||||
**When deleting a regular application:**
|
||||
|
||||
- Dialog title: "Delete application"
|
||||
- Standard confirmation prompt
|
||||
|
||||
**When deleting from the Resource Tree:**
|
||||
|
||||
An additional informational note appears:
|
||||
|
||||
> ⚠️ **Note:** You are about to delete an Application from the resource tree. This uses the same deletion behaviour as the Applications list page.
|
||||
|
||||
This note clarifies that the deletion will use the proper Application deletion API, not generic Kubernetes resource deletion.
|
||||
|
||||
### Deletion Options (Propagation Policies)
|
||||
|
||||
When deleting an application through the UI, you can choose from three propagation policies:
|
||||
|
||||
#### 1. Foreground (Default)
|
||||
|
||||
- Deletes the application and all its managed resources
|
||||
- Waits for all managed resources to be deleted before the Application is removed
|
||||
- **Use case**: When you want to ensure all resources are cleaned up before the Application disappears
|
||||
|
||||
#### 2. Background
|
||||
|
||||
- Deletes the application and all its managed resources
|
||||
- The Application is removed immediately, and resources are deleted in the background
|
||||
- **Use case**: When you want faster Application deletion and don't need to wait for resource cleanup
|
||||
|
||||
#### 3. Non-Cascading (Orphan)
|
||||
|
||||
- Deletes **only** the Application resource
|
||||
- All managed resources (Deployments, Services, ConfigMaps, etc.) are **preserved** in the cluster
|
||||
- The finalizer is removed automatically before deletion
|
||||
- **Use case**: When you want to stop managing resources through Argo CD but keep them running
|
||||
|
||||
> [!WARNING]
|
||||
> **Important for Non-Cascading Deletes**
|
||||
>
|
||||
> When you select **Non-Cascading**, Argo CD will:
|
||||
> - Remove the `resources-finalizer.argocd.argoproj.io` finalizer from the Application
|
||||
> - Delete only the Application resource
|
||||
> - Leave all managed resources (Pods, Services, Deployments, etc.) running in the cluster
|
||||
>
|
||||
> This behaviour is now **consistent** whether you delete from the Applications List or from the Resource Tree view.
|
||||
|
||||
### Best Practices for App of Apps Pattern
|
||||
|
||||
When working with the App of Apps pattern:
|
||||
|
||||
1. **Understand the impact**: Deleting a child application with Foreground or Background propagation will delete all of its managed resources
|
||||
2. **Review before deleting**: Always verify what resources are managed by the application before performing a cascading delete
|
||||
3. **Use Non-Cascading cautiously**: If you only want to remove the Application resource but keep the deployed workloads, use Non-Cascading delete
|
||||
4. **Consider finalizers**: Ensure child applications have appropriate finalizers set based on your deletion strategy (see [Cascading Deletion](../operator-manual/cluster-bootstrapping.md#cascading-deletion))
|
||||
|
||||
### Example Scenarios
|
||||
|
||||
#### Scenario 1: Deleting a child application and all its resources
|
||||
|
||||
1. Navigate to the parent application's Resource Tree
|
||||
2. Click the kebab menu (button with the three vertical dots) on a child Application resource
|
||||
3. Select "Delete"
|
||||
4. Choose **Foreground** or **Background** propagation policy
|
||||
5. Confirm the deletion
|
||||
|
||||
**Result**: The child Application and all its managed resources (Deployments, Services, etc.) are deleted.
|
||||
|
||||
#### Scenario 2: Removing Argo CD management but keeping resources
|
||||
|
||||
1. Navigate to the Applications List or the parent application's Resource Tree
|
||||
2. Click the kebab menu (button with the three vertical dots) on a child Application resource
|
||||
3. Select "Delete"
|
||||
4. Choose **Non-Cascading** propagation policy
|
||||
5. Confirm the deletion
|
||||
|
||||
**Result**: Only the Application resource is deleted. All managed resources continue running in the cluster.
|
||||
|
||||
#### Scenario 3: Deleting from Resource Tree with context awareness
|
||||
|
||||
When you delete a child application from the Resource Tree view:
|
||||
|
||||
- Argo CD recognizes it as an Application resource (not just a generic Kubernetes resource)
|
||||
- Shows "Delete child application" dialog if it detects the `app.kubernetes.io/part-of` label
|
||||
- Displays an informational note explaining you're using the same behaviour as the Applications List
|
||||
- Provides the same three propagation policy options
|
||||
|
||||
This ensures predictable and consistent deletion behaviour regardless of where you initiate the deletion.
|
||||
|
||||
@@ -17,11 +17,12 @@ Adding the argocd.argoproj.io/hook annotation to a resource will assign it to a
|
||||
|
||||
## How phases work?
|
||||
|
||||
Argo CD will respect resources assigned to different phases, during a sync operation Argo CD will do the following.
|
||||
Argo CD will respect resources assigned to different phases, during a sync operation Argo CD will do the following:
|
||||
|
||||
1. Apply all the resources marked as PreSync hooks. If any of them fails the whole sync process will stop and will be marked as failed
|
||||
2. Apply all the resources marked as Sync hooks. If any of them fails the whole sync process will be marked as failed. Hooks marked with SyncFail will also run
|
||||
3. Apply all the resources marked as PostSync hooks. If any of them fails the whole sync process will be marked as failed.
|
||||
|
||||
Apply all the resources marked as PreSync hooks. If any of them fails the whole sync process will stop and will be marked as failed
|
||||
Apply all the resources marked as Sync hooks. If any of them fails the whole sync process will be marked as failed. Hooks marked with SyncFail will also run
|
||||
Apply all the resources marked as PostSync hooks. If any of them fails the whole sync process will be marked as failed.
|
||||
Hooks marked with Skip will not be applied.
|
||||
|
||||
Here is a graphical overview of the sync process:
|
||||
@@ -54,8 +55,9 @@ Argo CD also offers an alternative method of changing the sync order of resource
|
||||
Hooks and resources are assigned to wave 0 by default. The wave can be negative, so you can create a wave that runs before all other resources.
|
||||
|
||||
When a sync operation takes place, Argo CD will:
|
||||
Order all resources according to their wave (lowest to highest)
|
||||
Apply the resources according to the resulting sequence
|
||||
|
||||
1. Order all resources according to their wave (lowest to highest)
|
||||
2. Apply the resources according to the resulting sequence
|
||||
|
||||
There is currently a delay between each sync wave in order to give other controllers a chance to react to the spec change that was just applied. This also prevents Argo CD from assessing resource health too quickly (against the stale object), causing hooks to fire prematurely. The current delay between each sync wave is 2 seconds and can be configured via the environment variable ARGOCD_SYNC_WAVE_DELAY.
|
||||
|
||||
@@ -67,16 +69,16 @@ While you can use sync waves on their own, for maximum flexibility you can combi
|
||||
|
||||
When Argo CD starts a sync, it orders the resources in the following precedence:
|
||||
|
||||
The phase
|
||||
The wave they are in (lower values first)
|
||||
By kind (e.g. namespaces first and then other Kubernetes resources, followed by custom resources)
|
||||
By name
|
||||
1. The phase
|
||||
2. The wave they are in (lower values first)
|
||||
3. By kind (e.g. namespaces first and then other Kubernetes resources, followed by custom resources)
|
||||
4. By name
|
||||
|
||||
Once the order is defined:
|
||||
|
||||
First Argo CD determines the number of the first wave to apply. This is the first number where any resource is out-of-sync or unhealthy.
|
||||
It applies resources in that wave.
|
||||
It repeats this process until all phases and waves are in-sync and healthy.
|
||||
1. First Argo CD determines the number of the first wave to apply. This is the first number where any resource is out-of-sync or unhealthy.
|
||||
2. It applies resources in that wave.
|
||||
3. It repeats this process until all phases and waves are in-sync and healthy.
|
||||
|
||||
Because an application can have resources that are unhealthy in the first wave, it may be that the app can never get to healthy.
|
||||
|
||||
|
||||
28
go.mod
28
go.mod
@@ -1,6 +1,6 @@
|
||||
module github.com/argoproj/argo-cd/v3
|
||||
|
||||
go 1.25.0
|
||||
go 1.25.5
|
||||
|
||||
require (
|
||||
code.gitea.io/sdk/gitea v0.22.0
|
||||
@@ -12,8 +12,8 @@ require (
|
||||
github.com/Masterminds/sprig/v3 v3.3.0
|
||||
github.com/TomOnTime/utfutil v1.0.0
|
||||
github.com/alicebob/miniredis/v2 v2.35.0
|
||||
github.com/argoproj/gitops-engine v0.7.1-0.20251006172252-b89b0871b414
|
||||
github.com/argoproj/notifications-engine v0.4.1-0.20250908182349-da04400446ff
|
||||
github.com/argoproj/gitops-engine v0.7.1-0.20251217140045-5baed5604d2d
|
||||
github.com/argoproj/notifications-engine v0.5.1-0.20260119155007-a23b5827d630
|
||||
github.com/argoproj/pkg v0.13.6
|
||||
github.com/argoproj/pkg/v2 v2.0.1
|
||||
github.com/aws/aws-sdk-go v1.55.7
|
||||
@@ -29,7 +29,7 @@ require (
|
||||
github.com/dlclark/regexp2 v1.11.5
|
||||
github.com/dustin/go-humanize v1.0.1
|
||||
github.com/evanphx/json-patch v5.9.11+incompatible
|
||||
github.com/expr-lang/expr v1.17.6
|
||||
github.com/expr-lang/expr v1.17.7
|
||||
github.com/felixge/httpsnoop v1.0.4
|
||||
github.com/fsnotify/fsnotify v1.9.0
|
||||
github.com/gfleury/go-bitbucket-v1 v0.0.0-20240917142304-df385efaac68
|
||||
@@ -47,7 +47,7 @@ require (
|
||||
github.com/golang-jwt/jwt/v5 v5.3.0
|
||||
github.com/golang/protobuf v1.5.4
|
||||
github.com/google/btree v1.1.3
|
||||
github.com/google/gnostic-models v0.7.0
|
||||
github.com/google/gnostic-models v0.7.0 // indirect
|
||||
github.com/google/go-cmp v0.7.0
|
||||
github.com/google/go-github/v69 v69.2.0
|
||||
github.com/google/go-jsonnet v0.21.0
|
||||
@@ -92,11 +92,11 @@ require (
|
||||
go.opentelemetry.io/otel v1.38.0
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.38.0
|
||||
go.opentelemetry.io/otel/sdk v1.38.0
|
||||
golang.org/x/crypto v0.42.0
|
||||
golang.org/x/net v0.44.0
|
||||
golang.org/x/crypto v0.46.0
|
||||
golang.org/x/net v0.47.0
|
||||
golang.org/x/oauth2 v0.31.0
|
||||
golang.org/x/sync v0.17.0
|
||||
golang.org/x/term v0.35.0
|
||||
golang.org/x/sync v0.19.0
|
||||
golang.org/x/term v0.38.0
|
||||
golang.org/x/time v0.13.0
|
||||
google.golang.org/genproto/googleapis/api v0.0.0-20250825161204-c5933d9347a5
|
||||
google.golang.org/grpc v1.75.1
|
||||
@@ -267,10 +267,10 @@ require (
|
||||
go.opentelemetry.io/proto/otlp v1.7.1 // indirect
|
||||
go.yaml.in/yaml/v2 v2.4.2 // indirect
|
||||
go.yaml.in/yaml/v3 v3.0.4 // indirect
|
||||
golang.org/x/mod v0.27.0 // indirect
|
||||
golang.org/x/sys v0.36.0 // indirect
|
||||
golang.org/x/text v0.29.0 // indirect
|
||||
golang.org/x/tools v0.36.0 // indirect
|
||||
golang.org/x/mod v0.30.0 // indirect
|
||||
golang.org/x/sys v0.39.0 // indirect
|
||||
golang.org/x/text v0.32.0 // indirect
|
||||
golang.org/x/tools v0.39.0 // indirect
|
||||
golang.org/x/tools/go/packages/packagestest v0.1.1-deprecated // indirect
|
||||
gomodules.xyz/envconfig v1.3.1-0.20190308184047-426f31af0d45 // indirect
|
||||
gomodules.xyz/jsonpatch/v2 v2.4.0 // indirect
|
||||
@@ -290,7 +290,7 @@ require (
|
||||
k8s.io/controller-manager v0.34.0 // indirect
|
||||
k8s.io/gengo/v2 v2.0.0-20250604051438-85fd79dbfd9f // indirect
|
||||
k8s.io/kube-aggregator v0.34.0 // indirect
|
||||
k8s.io/kubernetes v1.34.0 // indirect
|
||||
k8s.io/kubernetes v1.34.2 // indirect
|
||||
nhooyr.io/websocket v1.8.7 // indirect
|
||||
sigs.k8s.io/json v0.0.0-20241014173422-cfa47c3a1cc8 // indirect
|
||||
sigs.k8s.io/kustomize/api v0.20.1 // indirect
|
||||
|
||||
44
go.sum
44
go.sum
@@ -113,10 +113,10 @@ github.com/anmitsu/go-shlex v0.0.0-20200514113438-38f4b401e2be h1:9AeTilPcZAjCFI
|
||||
github.com/anmitsu/go-shlex v0.0.0-20200514113438-38f4b401e2be/go.mod h1:ySMOLuWl6zY27l47sB3qLNK6tF2fkHG55UZxx8oIVo4=
|
||||
github.com/antihax/optional v1.0.0/go.mod h1:uupD/76wgC+ih3iEmQUL+0Ugr19nfwCT1kdvxnR2qWY=
|
||||
github.com/appscode/go v0.0.0-20191119085241-0887d8ec2ecc/go.mod h1:OawnOmAL4ZX3YaPdN+8HTNwBveT1jMsqP74moa9XUbE=
|
||||
github.com/argoproj/gitops-engine v0.7.1-0.20251006172252-b89b0871b414 h1:2w1vd2VZja7Mlf/rblJkp6/Eq8fNDuM7p6pI4PTAJhg=
|
||||
github.com/argoproj/gitops-engine v0.7.1-0.20251006172252-b89b0871b414/go.mod h1:2nqYZBhj8CfVZb3ATakZpi1KNb/yc7mpadIHslicTFI=
|
||||
github.com/argoproj/notifications-engine v0.4.1-0.20250908182349-da04400446ff h1:pGGAeHIktPuYCRl1Z540XdxPFnedqyUhJK4VgpyJZfY=
|
||||
github.com/argoproj/notifications-engine v0.4.1-0.20250908182349-da04400446ff/go.mod h1:d1RazGXWvKRFv9//rg4MRRR7rbvbE7XLgTSMT5fITTE=
|
||||
github.com/argoproj/gitops-engine v0.7.1-0.20251217140045-5baed5604d2d h1:iUJYrbSvpV9n8vyl1sBt1GceM60HhHfnHxuzcm5apDg=
|
||||
github.com/argoproj/gitops-engine v0.7.1-0.20251217140045-5baed5604d2d/go.mod h1:PauXVUVcfiTgC+34lDdWzPS101g4NpsUtDAjFBnWf94=
|
||||
github.com/argoproj/notifications-engine v0.5.1-0.20260119155007-a23b5827d630 h1:naE5KNRTOALjF5nVIGUHrHU5xjlB8QJJiCu+aISIlSs=
|
||||
github.com/argoproj/notifications-engine v0.5.1-0.20260119155007-a23b5827d630/go.mod h1:d1RazGXWvKRFv9//rg4MRRR7rbvbE7XLgTSMT5fITTE=
|
||||
github.com/argoproj/pkg v0.13.6 h1:36WPD9MNYECHcO1/R1pj6teYspiK7uMQLCgLGft2abM=
|
||||
github.com/argoproj/pkg v0.13.6/go.mod h1:I698DoJBKuvNFaixh4vFl2C88cNIT1WS7KCbz5ewyF8=
|
||||
github.com/argoproj/pkg/v2 v2.0.1 h1:O/gCETzB/3+/hyFL/7d/VM/6pSOIRWIiBOTb2xqAHvc=
|
||||
@@ -257,8 +257,8 @@ github.com/evanphx/json-patch/v5 v5.9.11 h1:/8HVnzMq13/3x9TPvjG08wUGqBTmZBsCWzjT
|
||||
github.com/evanphx/json-patch/v5 v5.9.11/go.mod h1:3j+LviiESTElxA4p3EMKAB9HXj3/XEtnUf6OZxqIQTM=
|
||||
github.com/exponent-io/jsonpath v0.0.0-20210407135951-1de76d718b3f h1:Wl78ApPPB2Wvf/TIe2xdyJxTlb6obmF18d8QdkxNDu4=
|
||||
github.com/exponent-io/jsonpath v0.0.0-20210407135951-1de76d718b3f/go.mod h1:OSYXu++VVOHnXeitef/D8n/6y4QV8uLHSFXX4NeXMGc=
|
||||
github.com/expr-lang/expr v1.17.6 h1:1h6i8ONk9cexhDmowO/A64VPxHScu7qfSl2k8OlINec=
|
||||
github.com/expr-lang/expr v1.17.6/go.mod h1:8/vRC7+7HBzESEqt5kKpYXxrxkr31SaO8r40VO/1IT4=
|
||||
github.com/expr-lang/expr v1.17.7 h1:Q0xY/e/2aCIp8g9s/LGvMDCC5PxYlvHgDZRQ4y16JX8=
|
||||
github.com/expr-lang/expr v1.17.7/go.mod h1:8/vRC7+7HBzESEqt5kKpYXxrxkr31SaO8r40VO/1IT4=
|
||||
github.com/facebookgo/ensure v0.0.0-20160127193407-b4ab57deab51/go.mod h1:Yg+htXGokKKdzcwhuNDwVvN+uBxDGXJ7G/VN1d8fa64=
|
||||
github.com/facebookgo/stack v0.0.0-20160209184415-751773369052/go.mod h1:UbMTZqLaRiH3MsBH8va0n7s1pQYcu3uTb8G4tygF4Zg=
|
||||
github.com/facebookgo/subset v0.0.0-20150612182917-8dac2c3c4870/go.mod h1:5tD+neXqOorC30/tWg0LCSkrqj/AR6gu8yY8/fpw1q0=
|
||||
@@ -974,8 +974,8 @@ golang.org/x/crypto v0.28.0/go.mod h1:rmgy+3RHxRZMyY0jjAJShp2zgEdOqj2AO7U0pYmeQ7
|
||||
golang.org/x/crypto v0.36.0/go.mod h1:Y4J0ReaxCR1IMaabaSMugxJES1EpwhBHhv2bDHklZvc=
|
||||
golang.org/x/crypto v0.39.0/go.mod h1:L+Xg3Wf6HoL4Bn4238Z6ft6KfEpN0tJGo53AAPC632U=
|
||||
golang.org/x/crypto v0.40.0/go.mod h1:Qr1vMER5WyS2dfPHAlsOj01wgLbsyWtFn/aY+5+ZdxY=
|
||||
golang.org/x/crypto v0.42.0 h1:chiH31gIWm57EkTXpwnqf8qeuMUi0yekh6mT2AvFlqI=
|
||||
golang.org/x/crypto v0.42.0/go.mod h1:4+rDnOTJhQCx2q7/j6rAN5XDw8kPjeaXEUR2eL94ix8=
|
||||
golang.org/x/crypto v0.46.0 h1:cKRW/pmt1pKAfetfu+RCEvjvZkA9RimPbh7bhFjGVBU=
|
||||
golang.org/x/crypto v0.46.0/go.mod h1:Evb/oLKmMraqjZ2iQTwDwvCtJkczlDuTmdJXoZVzqU0=
|
||||
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
|
||||
golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
|
||||
golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8=
|
||||
@@ -1018,8 +1018,8 @@ golang.org/x/mod v0.19.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c=
|
||||
golang.org/x/mod v0.20.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c=
|
||||
golang.org/x/mod v0.25.0/go.mod h1:IXM97Txy2VM4PJ3gI61r1YEk/gAj6zAHN3AdZt6S9Ww=
|
||||
golang.org/x/mod v0.26.0/go.mod h1:/j6NAhSk8iQ723BGAUyoAcn7SlD7s15Dp9Nd/SfeaFQ=
|
||||
golang.org/x/mod v0.27.0 h1:kb+q2PyFnEADO2IEF935ehFUXlWiNjJWtRNgBLSfbxQ=
|
||||
golang.org/x/mod v0.27.0/go.mod h1:rWI627Fq0DEoudcK+MBkNkCe0EetEaDSwJJkCcjpazc=
|
||||
golang.org/x/mod v0.30.0 h1:fDEXFVZ/fmCKProc/yAXXUijritrDzahmwwefnjoPFk=
|
||||
golang.org/x/mod v0.30.0/go.mod h1:lAsf5O2EvJeSFMiBxXDki7sCgAxEUcZHXoXMKT4GJKc=
|
||||
golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
@@ -1085,8 +1085,8 @@ golang.org/x/net v0.30.0/go.mod h1:2wGyMJ5iFasEhkwi13ChkO/t1ECNC4X4eBKkVFyYFlU=
|
||||
golang.org/x/net v0.38.0/go.mod h1:ivrbrMbzFq5J41QOQh0siUuly180yBYtLp+CKbEaFx8=
|
||||
golang.org/x/net v0.41.0/go.mod h1:B/K4NNqkfmg07DQYrbwvSluqCJOOXwUjeb/5lOisjbA=
|
||||
golang.org/x/net v0.42.0/go.mod h1:FF1RA5d3u7nAYA4z2TkclSCKh68eSXtiFwcWQpPXdt8=
|
||||
golang.org/x/net v0.44.0 h1:evd8IRDyfNBMBTTY5XRF1vaZlD+EmWx6x8PkhR04H/I=
|
||||
golang.org/x/net v0.44.0/go.mod h1:ECOoLqd5U3Lhyeyo/QDCEVQ4sNgYsqvCZ722XogGieY=
|
||||
golang.org/x/net v0.47.0 h1:Mx+4dIFzqraBXUugkia1OOvlD6LemFo1ALMHjrXDOhY=
|
||||
golang.org/x/net v0.47.0/go.mod h1:/jNxtkgq5yWUGYkaZGqo27cfGZ1c5Nen03aYrrKpVRU=
|
||||
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
|
||||
golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
|
||||
golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
|
||||
@@ -1111,8 +1111,8 @@ golang.org/x/sync v0.8.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
|
||||
golang.org/x/sync v0.12.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA=
|
||||
golang.org/x/sync v0.15.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA=
|
||||
golang.org/x/sync v0.16.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA=
|
||||
golang.org/x/sync v0.17.0 h1:l60nONMj9l5drqw6jlhIELNv9I0A4OFgRsG9k2oT9Ug=
|
||||
golang.org/x/sync v0.17.0/go.mod h1:9KTHXmSnoGruLpwFjVSX0lNNA75CykiMECbovNTZqGI=
|
||||
golang.org/x/sync v0.19.0 h1:vV+1eWNmZ5geRlYjzm2adRgW2/mcpevXNg50YZtPCE4=
|
||||
golang.org/x/sync v0.19.0/go.mod h1:9KTHXmSnoGruLpwFjVSX0lNNA75CykiMECbovNTZqGI=
|
||||
golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
@@ -1195,8 +1195,8 @@ golang.org/x/sys v0.26.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||
golang.org/x/sys v0.31.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k=
|
||||
golang.org/x/sys v0.33.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k=
|
||||
golang.org/x/sys v0.34.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k=
|
||||
golang.org/x/sys v0.36.0 h1:KVRy2GtZBrk1cBYA7MKu5bEZFxQk4NIDV6RLVcC8o0k=
|
||||
golang.org/x/sys v0.36.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks=
|
||||
golang.org/x/sys v0.39.0 h1:CvCKL8MeisomCi6qNZ+wbb0DN9E5AATixKsvNtMoMFk=
|
||||
golang.org/x/sys v0.39.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks=
|
||||
golang.org/x/telemetry v0.0.0-20250710130107-8d8967aff50b/go.mod h1:4ZwOYna0/zsOKwuR5X/m0QFOJpSZvAxFfkQT+Erd9D4=
|
||||
golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw=
|
||||
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
||||
@@ -1223,8 +1223,8 @@ golang.org/x/term v0.25.0/go.mod h1:RPyXicDX+6vLxogjjRxjgD2TKtmAO6NZBsBRfrOLu7M=
|
||||
golang.org/x/term v0.30.0/go.mod h1:NYYFdzHoI5wRh/h5tDMdMqCqPJZEuNqVR5xJLd/n67g=
|
||||
golang.org/x/term v0.32.0/go.mod h1:uZG1FhGx848Sqfsq4/DlJr3xGGsYMu/L5GW4abiaEPQ=
|
||||
golang.org/x/term v0.33.0/go.mod h1:s18+ql9tYWp1IfpV9DmCtQDDSRBUjKaw9M1eAv5UeF0=
|
||||
golang.org/x/term v0.35.0 h1:bZBVKBudEyhRcajGcNc3jIfWPqV4y/Kt2XcoigOWtDQ=
|
||||
golang.org/x/term v0.35.0/go.mod h1:TPGtkTLesOwf2DE8CgVYiZinHAOuy5AYUYT1lENIZnA=
|
||||
golang.org/x/term v0.38.0 h1:PQ5pkm/rLO6HnxFR7N2lJHOZX6Kez5Y1gDSJla6jo7Q=
|
||||
golang.org/x/term v0.38.0/go.mod h1:bSEAKrOT1W+VSu9TSCMtoGEOUcKxOKgl3LE5QEF/xVg=
|
||||
golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
@@ -1249,8 +1249,8 @@ golang.org/x/text v0.19.0/go.mod h1:BuEKDfySbSR4drPmRPG/7iBdf8hvFMuRexcpahXilzY=
|
||||
golang.org/x/text v0.23.0/go.mod h1:/BLNzu4aZCJ1+kcD0DNRotWKage4q2rGVAg4o22unh4=
|
||||
golang.org/x/text v0.26.0/go.mod h1:QK15LZJUUQVJxhz7wXgxSy/CJaTFjd0G+YLonydOVQA=
|
||||
golang.org/x/text v0.27.0/go.mod h1:1D28KMCvyooCX9hBiosv5Tz/+YLxj0j7XhWjpSUF7CU=
|
||||
golang.org/x/text v0.29.0 h1:1neNs90w9YzJ9BocxfsQNHKuAT4pkghyXc4nhZ6sJvk=
|
||||
golang.org/x/text v0.29.0/go.mod h1:7MhJOA9CD2qZyOKYazxdYMF85OwPdEr9jTtBpO7ydH4=
|
||||
golang.org/x/text v0.32.0 h1:ZD01bjUt1FQ9WJ0ClOL5vxgxOI/sVCNgX1YtKwcY0mU=
|
||||
golang.org/x/text v0.32.0/go.mod h1:o/rUWzghvpD5TXrTIBuJU77MTaN0ljMWE47kxGJQ7jY=
|
||||
golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
||||
golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
||||
golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
||||
@@ -1434,8 +1434,8 @@ k8s.io/kube-openapi v0.0.0-20250710124328-f3f2b991d03b h1:MloQ9/bdJyIu9lb1PzujOP
|
||||
k8s.io/kube-openapi v0.0.0-20250710124328-f3f2b991d03b/go.mod h1:UZ2yyWbFTpuhSbFhv24aGNOdoRdJZgsIObGBUaYVsts=
|
||||
k8s.io/kubectl v0.34.0 h1:NcXz4TPTaUwhiX4LU+6r6udrlm0NsVnSkP3R9t0dmxs=
|
||||
k8s.io/kubectl v0.34.0/go.mod h1:bmd0W5i+HuG7/p5sqicr0Li0rR2iIhXL0oUyLF3OjR4=
|
||||
k8s.io/kubernetes v1.34.0 h1:NvUrwPAVB4W3mSOpJ/RtNGHWWYyUP/xPaX5rUSpzA0w=
|
||||
k8s.io/kubernetes v1.34.0/go.mod h1:iu+FhII+Oc/1gGWLJcer6wpyih441aNFHl7Pvm8yPto=
|
||||
k8s.io/kubernetes v1.34.2 h1:WQdDvYJazkmkwSncgNwGvVtaCt4TYXIU3wSMRgvp3MI=
|
||||
k8s.io/kubernetes v1.34.2/go.mod h1:m6pZk6a179pRo2wsTiCPORJ86iOEQmfIzUvtyEF8BwA=
|
||||
k8s.io/utils v0.0.0-20240711033017-18e509b52bc8/go.mod h1:OLgZIPagt7ERELqWJFomSt595RzquPNLL48iOWgYOg0=
|
||||
k8s.io/utils v0.0.0-20250604170112-4c0f3b243397 h1:hwvWFiBzdWw1FhfY1FooPn3kzWuJ8tmbZBHi4zVsl1Y=
|
||||
k8s.io/utils v0.0.0-20250604170112-4c0f3b243397/go.mod h1:OLgZIPagt7ERELqWJFomSt595RzquPNLL48iOWgYOg0=
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
module github.com/argoproj/argo-cd/get-previous-release
|
||||
|
||||
go 1.23.5
|
||||
go 1.25.5
|
||||
|
||||
require (
|
||||
github.com/stretchr/testify v1.9.0
|
||||
|
||||
@@ -12,4 +12,4 @@ resources:
|
||||
images:
|
||||
- name: quay.io/argoproj/argocd
|
||||
newName: quay.io/argoproj/argocd
|
||||
newTag: v3.2.0
|
||||
newTag: v3.2.7
|
||||
|
||||
@@ -5,7 +5,7 @@ kind: Kustomization
|
||||
images:
|
||||
- name: quay.io/argoproj/argocd
|
||||
newName: quay.io/argoproj/argocd
|
||||
newTag: v3.2.0
|
||||
newTag: v3.2.7
|
||||
resources:
|
||||
- ./application-controller
|
||||
- ./dex
|
||||
|
||||
@@ -239,6 +239,12 @@ spec:
|
||||
key: reposerver.git.request.timeout
|
||||
name: argocd-cmd-params-cm
|
||||
optional: true
|
||||
- name: ARGOCD_REPO_SERVER_ENABLE_BUILTIN_GIT_CONFIG
|
||||
valueFrom:
|
||||
configMapKeyRef:
|
||||
name: argocd-cmd-params-cm
|
||||
key: reposerver.enable.builtin.git.config
|
||||
optional: true
|
||||
- name: ARGOCD_GRPC_MAX_SIZE_MB
|
||||
valueFrom:
|
||||
configMapKeyRef:
|
||||
|
||||
18
manifests/core-install-with-hydrator.yaml
generated
18
manifests/core-install-with-hydrator.yaml
generated
@@ -24850,7 +24850,7 @@ spec:
|
||||
key: applicationsetcontroller.status.max.resources.count
|
||||
name: argocd-cmd-params-cm
|
||||
optional: true
|
||||
image: quay.io/argoproj/argocd:v3.2.0
|
||||
image: quay.io/argoproj/argocd:v3.2.7
|
||||
imagePullPolicy: Always
|
||||
name: argocd-applicationset-controller
|
||||
ports:
|
||||
@@ -24985,7 +24985,7 @@ spec:
|
||||
key: log.format.timestamp
|
||||
name: argocd-cmd-params-cm
|
||||
optional: true
|
||||
image: quay.io/argoproj/argocd:v3.2.0
|
||||
image: quay.io/argoproj/argocd:v3.2.7
|
||||
imagePullPolicy: Always
|
||||
livenessProbe:
|
||||
failureThreshold: 3
|
||||
@@ -25113,7 +25113,7 @@ spec:
|
||||
- argocd
|
||||
- admin
|
||||
- redis-initial-password
|
||||
image: quay.io/argoproj/argocd:v3.2.0
|
||||
image: quay.io/argoproj/argocd:v3.2.7
|
||||
imagePullPolicy: IfNotPresent
|
||||
name: secret-init
|
||||
securityContext:
|
||||
@@ -25386,6 +25386,12 @@ spec:
|
||||
key: reposerver.git.request.timeout
|
||||
name: argocd-cmd-params-cm
|
||||
optional: true
|
||||
- name: ARGOCD_REPO_SERVER_ENABLE_BUILTIN_GIT_CONFIG
|
||||
valueFrom:
|
||||
configMapKeyRef:
|
||||
key: reposerver.enable.builtin.git.config
|
||||
name: argocd-cmd-params-cm
|
||||
optional: true
|
||||
- name: ARGOCD_GRPC_MAX_SIZE_MB
|
||||
valueFrom:
|
||||
configMapKeyRef:
|
||||
@@ -25404,7 +25410,7 @@ spec:
|
||||
value: /helm-working-dir
|
||||
- name: HELM_DATA_HOME
|
||||
value: /helm-working-dir
|
||||
image: quay.io/argoproj/argocd:v3.2.0
|
||||
image: quay.io/argoproj/argocd:v3.2.7
|
||||
imagePullPolicy: Always
|
||||
livenessProbe:
|
||||
failureThreshold: 3
|
||||
@@ -25456,7 +25462,7 @@ spec:
|
||||
- -n
|
||||
- /usr/local/bin/argocd
|
||||
- /var/run/argocd/argocd-cmp-server
|
||||
image: quay.io/argoproj/argocd:v3.2.0
|
||||
image: quay.io/argoproj/argocd:v3.2.7
|
||||
name: copyutil
|
||||
securityContext:
|
||||
allowPrivilegeEscalation: false
|
||||
@@ -25804,7 +25810,7 @@ spec:
|
||||
optional: true
|
||||
- name: KUBECACHEDIR
|
||||
value: /tmp/kubecache
|
||||
image: quay.io/argoproj/argocd:v3.2.0
|
||||
image: quay.io/argoproj/argocd:v3.2.7
|
||||
imagePullPolicy: Always
|
||||
name: argocd-application-controller
|
||||
ports:
|
||||
|
||||
16
manifests/core-install.yaml
generated
16
manifests/core-install.yaml
generated
@@ -24818,7 +24818,7 @@ spec:
|
||||
key: applicationsetcontroller.status.max.resources.count
|
||||
name: argocd-cmd-params-cm
|
||||
optional: true
|
||||
image: quay.io/argoproj/argocd:v3.2.0
|
||||
image: quay.io/argoproj/argocd:v3.2.7
|
||||
imagePullPolicy: Always
|
||||
name: argocd-applicationset-controller
|
||||
ports:
|
||||
@@ -24947,7 +24947,7 @@ spec:
|
||||
- argocd
|
||||
- admin
|
||||
- redis-initial-password
|
||||
image: quay.io/argoproj/argocd:v3.2.0
|
||||
image: quay.io/argoproj/argocd:v3.2.7
|
||||
imagePullPolicy: IfNotPresent
|
||||
name: secret-init
|
||||
securityContext:
|
||||
@@ -25220,6 +25220,12 @@ spec:
|
||||
key: reposerver.git.request.timeout
|
||||
name: argocd-cmd-params-cm
|
||||
optional: true
|
||||
- name: ARGOCD_REPO_SERVER_ENABLE_BUILTIN_GIT_CONFIG
|
||||
valueFrom:
|
||||
configMapKeyRef:
|
||||
key: reposerver.enable.builtin.git.config
|
||||
name: argocd-cmd-params-cm
|
||||
optional: true
|
||||
- name: ARGOCD_GRPC_MAX_SIZE_MB
|
||||
valueFrom:
|
||||
configMapKeyRef:
|
||||
@@ -25238,7 +25244,7 @@ spec:
|
||||
value: /helm-working-dir
|
||||
- name: HELM_DATA_HOME
|
||||
value: /helm-working-dir
|
||||
image: quay.io/argoproj/argocd:v3.2.0
|
||||
image: quay.io/argoproj/argocd:v3.2.7
|
||||
imagePullPolicy: Always
|
||||
livenessProbe:
|
||||
failureThreshold: 3
|
||||
@@ -25290,7 +25296,7 @@ spec:
|
||||
- -n
|
||||
- /usr/local/bin/argocd
|
||||
- /var/run/argocd/argocd-cmp-server
|
||||
image: quay.io/argoproj/argocd:v3.2.0
|
||||
image: quay.io/argoproj/argocd:v3.2.7
|
||||
name: copyutil
|
||||
securityContext:
|
||||
allowPrivilegeEscalation: false
|
||||
@@ -25638,7 +25644,7 @@ spec:
|
||||
optional: true
|
||||
- name: KUBECACHEDIR
|
||||
value: /tmp/kubecache
|
||||
image: quay.io/argoproj/argocd:v3.2.0
|
||||
image: quay.io/argoproj/argocd:v3.2.7
|
||||
imagePullPolicy: Always
|
||||
name: argocd-application-controller
|
||||
ports:
|
||||
|
||||
@@ -12,4 +12,4 @@ resources:
|
||||
images:
|
||||
- name: quay.io/argoproj/argocd
|
||||
newName: quay.io/argoproj/argocd
|
||||
newTag: v3.2.0
|
||||
newTag: v3.2.7
|
||||
|
||||
@@ -12,7 +12,7 @@ patches:
|
||||
images:
|
||||
- name: quay.io/argoproj/argocd
|
||||
newName: quay.io/argoproj/argocd
|
||||
newTag: v3.2.0
|
||||
newTag: v3.2.7
|
||||
resources:
|
||||
- ../../base/application-controller
|
||||
- ../../base/applicationset-controller
|
||||
|
||||
24
manifests/ha/install-with-hydrator.yaml
generated
24
manifests/ha/install-with-hydrator.yaml
generated
@@ -26216,7 +26216,7 @@ spec:
|
||||
key: applicationsetcontroller.status.max.resources.count
|
||||
name: argocd-cmd-params-cm
|
||||
optional: true
|
||||
image: quay.io/argoproj/argocd:v3.2.0
|
||||
image: quay.io/argoproj/argocd:v3.2.7
|
||||
imagePullPolicy: Always
|
||||
name: argocd-applicationset-controller
|
||||
ports:
|
||||
@@ -26351,7 +26351,7 @@ spec:
|
||||
key: log.format.timestamp
|
||||
name: argocd-cmd-params-cm
|
||||
optional: true
|
||||
image: quay.io/argoproj/argocd:v3.2.0
|
||||
image: quay.io/argoproj/argocd:v3.2.7
|
||||
imagePullPolicy: Always
|
||||
livenessProbe:
|
||||
failureThreshold: 3
|
||||
@@ -26502,7 +26502,7 @@ spec:
|
||||
- -n
|
||||
- /usr/local/bin/argocd
|
||||
- /shared/argocd-dex
|
||||
image: quay.io/argoproj/argocd:v3.2.0
|
||||
image: quay.io/argoproj/argocd:v3.2.7
|
||||
imagePullPolicy: Always
|
||||
name: copyutil
|
||||
securityContext:
|
||||
@@ -26598,7 +26598,7 @@ spec:
|
||||
key: notificationscontroller.repo.server.plaintext
|
||||
name: argocd-cmd-params-cm
|
||||
optional: true
|
||||
image: quay.io/argoproj/argocd:v3.2.0
|
||||
image: quay.io/argoproj/argocd:v3.2.7
|
||||
imagePullPolicy: Always
|
||||
livenessProbe:
|
||||
tcpSocket:
|
||||
@@ -26722,7 +26722,7 @@ spec:
|
||||
- argocd
|
||||
- admin
|
||||
- redis-initial-password
|
||||
image: quay.io/argoproj/argocd:v3.2.0
|
||||
image: quay.io/argoproj/argocd:v3.2.7
|
||||
imagePullPolicy: IfNotPresent
|
||||
name: secret-init
|
||||
securityContext:
|
||||
@@ -27021,6 +27021,12 @@ spec:
|
||||
key: reposerver.git.request.timeout
|
||||
name: argocd-cmd-params-cm
|
||||
optional: true
|
||||
- name: ARGOCD_REPO_SERVER_ENABLE_BUILTIN_GIT_CONFIG
|
||||
valueFrom:
|
||||
configMapKeyRef:
|
||||
key: reposerver.enable.builtin.git.config
|
||||
name: argocd-cmd-params-cm
|
||||
optional: true
|
||||
- name: ARGOCD_GRPC_MAX_SIZE_MB
|
||||
valueFrom:
|
||||
configMapKeyRef:
|
||||
@@ -27039,7 +27045,7 @@ spec:
|
||||
value: /helm-working-dir
|
||||
- name: HELM_DATA_HOME
|
||||
value: /helm-working-dir
|
||||
image: quay.io/argoproj/argocd:v3.2.0
|
||||
image: quay.io/argoproj/argocd:v3.2.7
|
||||
imagePullPolicy: Always
|
||||
livenessProbe:
|
||||
failureThreshold: 3
|
||||
@@ -27091,7 +27097,7 @@ spec:
|
||||
- -n
|
||||
- /usr/local/bin/argocd
|
||||
- /var/run/argocd/argocd-cmp-server
|
||||
image: quay.io/argoproj/argocd:v3.2.0
|
||||
image: quay.io/argoproj/argocd:v3.2.7
|
||||
name: copyutil
|
||||
securityContext:
|
||||
allowPrivilegeEscalation: false
|
||||
@@ -27465,7 +27471,7 @@ spec:
|
||||
key: server.sync.replace.allowed
|
||||
name: argocd-cmd-params-cm
|
||||
optional: true
|
||||
image: quay.io/argoproj/argocd:v3.2.0
|
||||
image: quay.io/argoproj/argocd:v3.2.7
|
||||
imagePullPolicy: Always
|
||||
livenessProbe:
|
||||
httpGet:
|
||||
@@ -27849,7 +27855,7 @@ spec:
|
||||
optional: true
|
||||
- name: KUBECACHEDIR
|
||||
value: /tmp/kubecache
|
||||
image: quay.io/argoproj/argocd:v3.2.0
|
||||
image: quay.io/argoproj/argocd:v3.2.7
|
||||
imagePullPolicy: Always
|
||||
name: argocd-application-controller
|
||||
ports:
|
||||
|
||||
22
manifests/ha/install.yaml
generated
22
manifests/ha/install.yaml
generated
@@ -26186,7 +26186,7 @@ spec:
|
||||
key: applicationsetcontroller.status.max.resources.count
|
||||
name: argocd-cmd-params-cm
|
||||
optional: true
|
||||
image: quay.io/argoproj/argocd:v3.2.0
|
||||
image: quay.io/argoproj/argocd:v3.2.7
|
||||
imagePullPolicy: Always
|
||||
name: argocd-applicationset-controller
|
||||
ports:
|
||||
@@ -26338,7 +26338,7 @@ spec:
|
||||
- -n
|
||||
- /usr/local/bin/argocd
|
||||
- /shared/argocd-dex
|
||||
image: quay.io/argoproj/argocd:v3.2.0
|
||||
image: quay.io/argoproj/argocd:v3.2.7
|
||||
imagePullPolicy: Always
|
||||
name: copyutil
|
||||
securityContext:
|
||||
@@ -26434,7 +26434,7 @@ spec:
|
||||
key: notificationscontroller.repo.server.plaintext
|
||||
name: argocd-cmd-params-cm
|
||||
optional: true
|
||||
image: quay.io/argoproj/argocd:v3.2.0
|
||||
image: quay.io/argoproj/argocd:v3.2.7
|
||||
imagePullPolicy: Always
|
||||
livenessProbe:
|
||||
tcpSocket:
|
||||
@@ -26558,7 +26558,7 @@ spec:
|
||||
- argocd
|
||||
- admin
|
||||
- redis-initial-password
|
||||
image: quay.io/argoproj/argocd:v3.2.0
|
||||
image: quay.io/argoproj/argocd:v3.2.7
|
||||
imagePullPolicy: IfNotPresent
|
||||
name: secret-init
|
||||
securityContext:
|
||||
@@ -26857,6 +26857,12 @@ spec:
|
||||
key: reposerver.git.request.timeout
|
||||
name: argocd-cmd-params-cm
|
||||
optional: true
|
||||
- name: ARGOCD_REPO_SERVER_ENABLE_BUILTIN_GIT_CONFIG
|
||||
valueFrom:
|
||||
configMapKeyRef:
|
||||
key: reposerver.enable.builtin.git.config
|
||||
name: argocd-cmd-params-cm
|
||||
optional: true
|
||||
- name: ARGOCD_GRPC_MAX_SIZE_MB
|
||||
valueFrom:
|
||||
configMapKeyRef:
|
||||
@@ -26875,7 +26881,7 @@ spec:
|
||||
value: /helm-working-dir
|
||||
- name: HELM_DATA_HOME
|
||||
value: /helm-working-dir
|
||||
image: quay.io/argoproj/argocd:v3.2.0
|
||||
image: quay.io/argoproj/argocd:v3.2.7
|
||||
imagePullPolicy: Always
|
||||
livenessProbe:
|
||||
failureThreshold: 3
|
||||
@@ -26927,7 +26933,7 @@ spec:
|
||||
- -n
|
||||
- /usr/local/bin/argocd
|
||||
- /var/run/argocd/argocd-cmp-server
|
||||
image: quay.io/argoproj/argocd:v3.2.0
|
||||
image: quay.io/argoproj/argocd:v3.2.7
|
||||
name: copyutil
|
||||
securityContext:
|
||||
allowPrivilegeEscalation: false
|
||||
@@ -27301,7 +27307,7 @@ spec:
|
||||
key: server.sync.replace.allowed
|
||||
name: argocd-cmd-params-cm
|
||||
optional: true
|
||||
image: quay.io/argoproj/argocd:v3.2.0
|
||||
image: quay.io/argoproj/argocd:v3.2.7
|
||||
imagePullPolicy: Always
|
||||
livenessProbe:
|
||||
httpGet:
|
||||
@@ -27685,7 +27691,7 @@ spec:
|
||||
optional: true
|
||||
- name: KUBECACHEDIR
|
||||
value: /tmp/kubecache
|
||||
image: quay.io/argoproj/argocd:v3.2.0
|
||||
image: quay.io/argoproj/argocd:v3.2.7
|
||||
imagePullPolicy: Always
|
||||
name: argocd-application-controller
|
||||
ports:
|
||||
|
||||
24
manifests/ha/namespace-install-with-hydrator.yaml
generated
24
manifests/ha/namespace-install-with-hydrator.yaml
generated
@@ -1897,7 +1897,7 @@ spec:
|
||||
key: applicationsetcontroller.status.max.resources.count
|
||||
name: argocd-cmd-params-cm
|
||||
optional: true
|
||||
image: quay.io/argoproj/argocd:v3.2.0
|
||||
image: quay.io/argoproj/argocd:v3.2.7
|
||||
imagePullPolicy: Always
|
||||
name: argocd-applicationset-controller
|
||||
ports:
|
||||
@@ -2032,7 +2032,7 @@ spec:
|
||||
key: log.format.timestamp
|
||||
name: argocd-cmd-params-cm
|
||||
optional: true
|
||||
image: quay.io/argoproj/argocd:v3.2.0
|
||||
image: quay.io/argoproj/argocd:v3.2.7
|
||||
imagePullPolicy: Always
|
||||
livenessProbe:
|
||||
failureThreshold: 3
|
||||
@@ -2183,7 +2183,7 @@ spec:
|
||||
- -n
|
||||
- /usr/local/bin/argocd
|
||||
- /shared/argocd-dex
|
||||
image: quay.io/argoproj/argocd:v3.2.0
|
||||
image: quay.io/argoproj/argocd:v3.2.7
|
||||
imagePullPolicy: Always
|
||||
name: copyutil
|
||||
securityContext:
|
||||
@@ -2279,7 +2279,7 @@ spec:
|
||||
key: notificationscontroller.repo.server.plaintext
|
||||
name: argocd-cmd-params-cm
|
||||
optional: true
|
||||
image: quay.io/argoproj/argocd:v3.2.0
|
||||
image: quay.io/argoproj/argocd:v3.2.7
|
||||
imagePullPolicy: Always
|
||||
livenessProbe:
|
||||
tcpSocket:
|
||||
@@ -2403,7 +2403,7 @@ spec:
|
||||
- argocd
|
||||
- admin
|
||||
- redis-initial-password
|
||||
image: quay.io/argoproj/argocd:v3.2.0
|
||||
image: quay.io/argoproj/argocd:v3.2.7
|
||||
imagePullPolicy: IfNotPresent
|
||||
name: secret-init
|
||||
securityContext:
|
||||
@@ -2702,6 +2702,12 @@ spec:
|
||||
key: reposerver.git.request.timeout
|
||||
name: argocd-cmd-params-cm
|
||||
optional: true
|
||||
- name: ARGOCD_REPO_SERVER_ENABLE_BUILTIN_GIT_CONFIG
|
||||
valueFrom:
|
||||
configMapKeyRef:
|
||||
key: reposerver.enable.builtin.git.config
|
||||
name: argocd-cmd-params-cm
|
||||
optional: true
|
||||
- name: ARGOCD_GRPC_MAX_SIZE_MB
|
||||
valueFrom:
|
||||
configMapKeyRef:
|
||||
@@ -2720,7 +2726,7 @@ spec:
|
||||
value: /helm-working-dir
|
||||
- name: HELM_DATA_HOME
|
||||
value: /helm-working-dir
|
||||
image: quay.io/argoproj/argocd:v3.2.0
|
||||
image: quay.io/argoproj/argocd:v3.2.7
|
||||
imagePullPolicy: Always
|
||||
livenessProbe:
|
||||
failureThreshold: 3
|
||||
@@ -2772,7 +2778,7 @@ spec:
|
||||
- -n
|
||||
- /usr/local/bin/argocd
|
||||
- /var/run/argocd/argocd-cmp-server
|
||||
image: quay.io/argoproj/argocd:v3.2.0
|
||||
image: quay.io/argoproj/argocd:v3.2.7
|
||||
name: copyutil
|
||||
securityContext:
|
||||
allowPrivilegeEscalation: false
|
||||
@@ -3146,7 +3152,7 @@ spec:
|
||||
key: server.sync.replace.allowed
|
||||
name: argocd-cmd-params-cm
|
||||
optional: true
|
||||
image: quay.io/argoproj/argocd:v3.2.0
|
||||
image: quay.io/argoproj/argocd:v3.2.7
|
||||
imagePullPolicy: Always
|
||||
livenessProbe:
|
||||
httpGet:
|
||||
@@ -3530,7 +3536,7 @@ spec:
|
||||
optional: true
|
||||
- name: KUBECACHEDIR
|
||||
value: /tmp/kubecache
|
||||
image: quay.io/argoproj/argocd:v3.2.0
|
||||
image: quay.io/argoproj/argocd:v3.2.7
|
||||
imagePullPolicy: Always
|
||||
name: argocd-application-controller
|
||||
ports:
|
||||
|
||||
22
manifests/ha/namespace-install.yaml
generated
22
manifests/ha/namespace-install.yaml
generated
@@ -1867,7 +1867,7 @@ spec:
|
||||
key: applicationsetcontroller.status.max.resources.count
|
||||
name: argocd-cmd-params-cm
|
||||
optional: true
|
||||
image: quay.io/argoproj/argocd:v3.2.0
|
||||
image: quay.io/argoproj/argocd:v3.2.7
|
||||
imagePullPolicy: Always
|
||||
name: argocd-applicationset-controller
|
||||
ports:
|
||||
@@ -2019,7 +2019,7 @@ spec:
|
||||
- -n
|
||||
- /usr/local/bin/argocd
|
||||
- /shared/argocd-dex
|
||||
image: quay.io/argoproj/argocd:v3.2.0
|
||||
image: quay.io/argoproj/argocd:v3.2.7
|
||||
imagePullPolicy: Always
|
||||
name: copyutil
|
||||
securityContext:
|
||||
@@ -2115,7 +2115,7 @@ spec:
|
||||
key: notificationscontroller.repo.server.plaintext
|
||||
name: argocd-cmd-params-cm
|
||||
optional: true
|
||||
image: quay.io/argoproj/argocd:v3.2.0
|
||||
image: quay.io/argoproj/argocd:v3.2.7
|
||||
imagePullPolicy: Always
|
||||
livenessProbe:
|
||||
tcpSocket:
|
||||
@@ -2239,7 +2239,7 @@ spec:
|
||||
- argocd
|
||||
- admin
|
||||
- redis-initial-password
|
||||
image: quay.io/argoproj/argocd:v3.2.0
|
||||
image: quay.io/argoproj/argocd:v3.2.7
|
||||
imagePullPolicy: IfNotPresent
|
||||
name: secret-init
|
||||
securityContext:
|
||||
@@ -2538,6 +2538,12 @@ spec:
|
||||
key: reposerver.git.request.timeout
|
||||
name: argocd-cmd-params-cm
|
||||
optional: true
|
||||
- name: ARGOCD_REPO_SERVER_ENABLE_BUILTIN_GIT_CONFIG
|
||||
valueFrom:
|
||||
configMapKeyRef:
|
||||
key: reposerver.enable.builtin.git.config
|
||||
name: argocd-cmd-params-cm
|
||||
optional: true
|
||||
- name: ARGOCD_GRPC_MAX_SIZE_MB
|
||||
valueFrom:
|
||||
configMapKeyRef:
|
||||
@@ -2556,7 +2562,7 @@ spec:
|
||||
value: /helm-working-dir
|
||||
- name: HELM_DATA_HOME
|
||||
value: /helm-working-dir
|
||||
image: quay.io/argoproj/argocd:v3.2.0
|
||||
image: quay.io/argoproj/argocd:v3.2.7
|
||||
imagePullPolicy: Always
|
||||
livenessProbe:
|
||||
failureThreshold: 3
|
||||
@@ -2608,7 +2614,7 @@ spec:
|
||||
- -n
|
||||
- /usr/local/bin/argocd
|
||||
- /var/run/argocd/argocd-cmp-server
|
||||
image: quay.io/argoproj/argocd:v3.2.0
|
||||
image: quay.io/argoproj/argocd:v3.2.7
|
||||
name: copyutil
|
||||
securityContext:
|
||||
allowPrivilegeEscalation: false
|
||||
@@ -2982,7 +2988,7 @@ spec:
|
||||
key: server.sync.replace.allowed
|
||||
name: argocd-cmd-params-cm
|
||||
optional: true
|
||||
image: quay.io/argoproj/argocd:v3.2.0
|
||||
image: quay.io/argoproj/argocd:v3.2.7
|
||||
imagePullPolicy: Always
|
||||
livenessProbe:
|
||||
httpGet:
|
||||
@@ -3366,7 +3372,7 @@ spec:
|
||||
optional: true
|
||||
- name: KUBECACHEDIR
|
||||
value: /tmp/kubecache
|
||||
image: quay.io/argoproj/argocd:v3.2.0
|
||||
image: quay.io/argoproj/argocd:v3.2.7
|
||||
imagePullPolicy: Always
|
||||
name: argocd-application-controller
|
||||
ports:
|
||||
|
||||
24
manifests/install-with-hydrator.yaml
generated
24
manifests/install-with-hydrator.yaml
generated
@@ -25294,7 +25294,7 @@ spec:
|
||||
key: applicationsetcontroller.status.max.resources.count
|
||||
name: argocd-cmd-params-cm
|
||||
optional: true
|
||||
image: quay.io/argoproj/argocd:v3.2.0
|
||||
image: quay.io/argoproj/argocd:v3.2.7
|
||||
imagePullPolicy: Always
|
||||
name: argocd-applicationset-controller
|
||||
ports:
|
||||
@@ -25429,7 +25429,7 @@ spec:
|
||||
key: log.format.timestamp
|
||||
name: argocd-cmd-params-cm
|
||||
optional: true
|
||||
image: quay.io/argoproj/argocd:v3.2.0
|
||||
image: quay.io/argoproj/argocd:v3.2.7
|
||||
imagePullPolicy: Always
|
||||
livenessProbe:
|
||||
failureThreshold: 3
|
||||
@@ -25580,7 +25580,7 @@ spec:
|
||||
- -n
|
||||
- /usr/local/bin/argocd
|
||||
- /shared/argocd-dex
|
||||
image: quay.io/argoproj/argocd:v3.2.0
|
||||
image: quay.io/argoproj/argocd:v3.2.7
|
||||
imagePullPolicy: Always
|
||||
name: copyutil
|
||||
securityContext:
|
||||
@@ -25676,7 +25676,7 @@ spec:
|
||||
key: notificationscontroller.repo.server.plaintext
|
||||
name: argocd-cmd-params-cm
|
||||
optional: true
|
||||
image: quay.io/argoproj/argocd:v3.2.0
|
||||
image: quay.io/argoproj/argocd:v3.2.7
|
||||
imagePullPolicy: Always
|
||||
livenessProbe:
|
||||
tcpSocket:
|
||||
@@ -25778,7 +25778,7 @@ spec:
|
||||
- argocd
|
||||
- admin
|
||||
- redis-initial-password
|
||||
image: quay.io/argoproj/argocd:v3.2.0
|
||||
image: quay.io/argoproj/argocd:v3.2.7
|
||||
imagePullPolicy: IfNotPresent
|
||||
name: secret-init
|
||||
securityContext:
|
||||
@@ -26051,6 +26051,12 @@ spec:
|
||||
key: reposerver.git.request.timeout
|
||||
name: argocd-cmd-params-cm
|
||||
optional: true
|
||||
- name: ARGOCD_REPO_SERVER_ENABLE_BUILTIN_GIT_CONFIG
|
||||
valueFrom:
|
||||
configMapKeyRef:
|
||||
key: reposerver.enable.builtin.git.config
|
||||
name: argocd-cmd-params-cm
|
||||
optional: true
|
||||
- name: ARGOCD_GRPC_MAX_SIZE_MB
|
||||
valueFrom:
|
||||
configMapKeyRef:
|
||||
@@ -26069,7 +26075,7 @@ spec:
|
||||
value: /helm-working-dir
|
||||
- name: HELM_DATA_HOME
|
||||
value: /helm-working-dir
|
||||
image: quay.io/argoproj/argocd:v3.2.0
|
||||
image: quay.io/argoproj/argocd:v3.2.7
|
||||
imagePullPolicy: Always
|
||||
livenessProbe:
|
||||
failureThreshold: 3
|
||||
@@ -26121,7 +26127,7 @@ spec:
|
||||
- -n
|
||||
- /usr/local/bin/argocd
|
||||
- /var/run/argocd/argocd-cmp-server
|
||||
image: quay.io/argoproj/argocd:v3.2.0
|
||||
image: quay.io/argoproj/argocd:v3.2.7
|
||||
name: copyutil
|
||||
securityContext:
|
||||
allowPrivilegeEscalation: false
|
||||
@@ -26493,7 +26499,7 @@ spec:
|
||||
key: server.sync.replace.allowed
|
||||
name: argocd-cmd-params-cm
|
||||
optional: true
|
||||
image: quay.io/argoproj/argocd:v3.2.0
|
||||
image: quay.io/argoproj/argocd:v3.2.7
|
||||
imagePullPolicy: Always
|
||||
livenessProbe:
|
||||
httpGet:
|
||||
@@ -26877,7 +26883,7 @@ spec:
|
||||
optional: true
|
||||
- name: KUBECACHEDIR
|
||||
value: /tmp/kubecache
|
||||
image: quay.io/argoproj/argocd:v3.2.0
|
||||
image: quay.io/argoproj/argocd:v3.2.7
|
||||
imagePullPolicy: Always
|
||||
name: argocd-application-controller
|
||||
ports:
|
||||
|
||||
22
manifests/install.yaml
generated
22
manifests/install.yaml
generated
@@ -25262,7 +25262,7 @@ spec:
|
||||
key: applicationsetcontroller.status.max.resources.count
|
||||
name: argocd-cmd-params-cm
|
||||
optional: true
|
||||
image: quay.io/argoproj/argocd:v3.2.0
|
||||
image: quay.io/argoproj/argocd:v3.2.7
|
||||
imagePullPolicy: Always
|
||||
name: argocd-applicationset-controller
|
||||
ports:
|
||||
@@ -25414,7 +25414,7 @@ spec:
|
||||
- -n
|
||||
- /usr/local/bin/argocd
|
||||
- /shared/argocd-dex
|
||||
image: quay.io/argoproj/argocd:v3.2.0
|
||||
image: quay.io/argoproj/argocd:v3.2.7
|
||||
imagePullPolicy: Always
|
||||
name: copyutil
|
||||
securityContext:
|
||||
@@ -25510,7 +25510,7 @@ spec:
|
||||
key: notificationscontroller.repo.server.plaintext
|
||||
name: argocd-cmd-params-cm
|
||||
optional: true
|
||||
image: quay.io/argoproj/argocd:v3.2.0
|
||||
image: quay.io/argoproj/argocd:v3.2.7
|
||||
imagePullPolicy: Always
|
||||
livenessProbe:
|
||||
tcpSocket:
|
||||
@@ -25612,7 +25612,7 @@ spec:
|
||||
- argocd
|
||||
- admin
|
||||
- redis-initial-password
|
||||
image: quay.io/argoproj/argocd:v3.2.0
|
||||
image: quay.io/argoproj/argocd:v3.2.7
|
||||
imagePullPolicy: IfNotPresent
|
||||
name: secret-init
|
||||
securityContext:
|
||||
@@ -25885,6 +25885,12 @@ spec:
|
||||
key: reposerver.git.request.timeout
|
||||
name: argocd-cmd-params-cm
|
||||
optional: true
|
||||
- name: ARGOCD_REPO_SERVER_ENABLE_BUILTIN_GIT_CONFIG
|
||||
valueFrom:
|
||||
configMapKeyRef:
|
||||
key: reposerver.enable.builtin.git.config
|
||||
name: argocd-cmd-params-cm
|
||||
optional: true
|
||||
- name: ARGOCD_GRPC_MAX_SIZE_MB
|
||||
valueFrom:
|
||||
configMapKeyRef:
|
||||
@@ -25903,7 +25909,7 @@ spec:
|
||||
value: /helm-working-dir
|
||||
- name: HELM_DATA_HOME
|
||||
value: /helm-working-dir
|
||||
image: quay.io/argoproj/argocd:v3.2.0
|
||||
image: quay.io/argoproj/argocd:v3.2.7
|
||||
imagePullPolicy: Always
|
||||
livenessProbe:
|
||||
failureThreshold: 3
|
||||
@@ -25955,7 +25961,7 @@ spec:
|
||||
- -n
|
||||
- /usr/local/bin/argocd
|
||||
- /var/run/argocd/argocd-cmp-server
|
||||
image: quay.io/argoproj/argocd:v3.2.0
|
||||
image: quay.io/argoproj/argocd:v3.2.7
|
||||
name: copyutil
|
||||
securityContext:
|
||||
allowPrivilegeEscalation: false
|
||||
@@ -26327,7 +26333,7 @@ spec:
|
||||
key: server.sync.replace.allowed
|
||||
name: argocd-cmd-params-cm
|
||||
optional: true
|
||||
image: quay.io/argoproj/argocd:v3.2.0
|
||||
image: quay.io/argoproj/argocd:v3.2.7
|
||||
imagePullPolicy: Always
|
||||
livenessProbe:
|
||||
httpGet:
|
||||
@@ -26711,7 +26717,7 @@ spec:
|
||||
optional: true
|
||||
- name: KUBECACHEDIR
|
||||
value: /tmp/kubecache
|
||||
image: quay.io/argoproj/argocd:v3.2.0
|
||||
image: quay.io/argoproj/argocd:v3.2.7
|
||||
imagePullPolicy: Always
|
||||
name: argocd-application-controller
|
||||
ports:
|
||||
|
||||
24
manifests/namespace-install-with-hydrator.yaml
generated
24
manifests/namespace-install-with-hydrator.yaml
generated
@@ -975,7 +975,7 @@ spec:
|
||||
key: applicationsetcontroller.status.max.resources.count
|
||||
name: argocd-cmd-params-cm
|
||||
optional: true
|
||||
image: quay.io/argoproj/argocd:v3.2.0
|
||||
image: quay.io/argoproj/argocd:v3.2.7
|
||||
imagePullPolicy: Always
|
||||
name: argocd-applicationset-controller
|
||||
ports:
|
||||
@@ -1110,7 +1110,7 @@ spec:
|
||||
key: log.format.timestamp
|
||||
name: argocd-cmd-params-cm
|
||||
optional: true
|
||||
image: quay.io/argoproj/argocd:v3.2.0
|
||||
image: quay.io/argoproj/argocd:v3.2.7
|
||||
imagePullPolicy: Always
|
||||
livenessProbe:
|
||||
failureThreshold: 3
|
||||
@@ -1261,7 +1261,7 @@ spec:
|
||||
- -n
|
||||
- /usr/local/bin/argocd
|
||||
- /shared/argocd-dex
|
||||
image: quay.io/argoproj/argocd:v3.2.0
|
||||
image: quay.io/argoproj/argocd:v3.2.7
|
||||
imagePullPolicy: Always
|
||||
name: copyutil
|
||||
securityContext:
|
||||
@@ -1357,7 +1357,7 @@ spec:
|
||||
key: notificationscontroller.repo.server.plaintext
|
||||
name: argocd-cmd-params-cm
|
||||
optional: true
|
||||
image: quay.io/argoproj/argocd:v3.2.0
|
||||
image: quay.io/argoproj/argocd:v3.2.7
|
||||
imagePullPolicy: Always
|
||||
livenessProbe:
|
||||
tcpSocket:
|
||||
@@ -1459,7 +1459,7 @@ spec:
|
||||
- argocd
|
||||
- admin
|
||||
- redis-initial-password
|
||||
image: quay.io/argoproj/argocd:v3.2.0
|
||||
image: quay.io/argoproj/argocd:v3.2.7
|
||||
imagePullPolicy: IfNotPresent
|
||||
name: secret-init
|
||||
securityContext:
|
||||
@@ -1732,6 +1732,12 @@ spec:
|
||||
key: reposerver.git.request.timeout
|
||||
name: argocd-cmd-params-cm
|
||||
optional: true
|
||||
- name: ARGOCD_REPO_SERVER_ENABLE_BUILTIN_GIT_CONFIG
|
||||
valueFrom:
|
||||
configMapKeyRef:
|
||||
key: reposerver.enable.builtin.git.config
|
||||
name: argocd-cmd-params-cm
|
||||
optional: true
|
||||
- name: ARGOCD_GRPC_MAX_SIZE_MB
|
||||
valueFrom:
|
||||
configMapKeyRef:
|
||||
@@ -1750,7 +1756,7 @@ spec:
|
||||
value: /helm-working-dir
|
||||
- name: HELM_DATA_HOME
|
||||
value: /helm-working-dir
|
||||
image: quay.io/argoproj/argocd:v3.2.0
|
||||
image: quay.io/argoproj/argocd:v3.2.7
|
||||
imagePullPolicy: Always
|
||||
livenessProbe:
|
||||
failureThreshold: 3
|
||||
@@ -1802,7 +1808,7 @@ spec:
|
||||
- -n
|
||||
- /usr/local/bin/argocd
|
||||
- /var/run/argocd/argocd-cmp-server
|
||||
image: quay.io/argoproj/argocd:v3.2.0
|
||||
image: quay.io/argoproj/argocd:v3.2.7
|
||||
name: copyutil
|
||||
securityContext:
|
||||
allowPrivilegeEscalation: false
|
||||
@@ -2174,7 +2180,7 @@ spec:
|
||||
key: server.sync.replace.allowed
|
||||
name: argocd-cmd-params-cm
|
||||
optional: true
|
||||
image: quay.io/argoproj/argocd:v3.2.0
|
||||
image: quay.io/argoproj/argocd:v3.2.7
|
||||
imagePullPolicy: Always
|
||||
livenessProbe:
|
||||
httpGet:
|
||||
@@ -2558,7 +2564,7 @@ spec:
|
||||
optional: true
|
||||
- name: KUBECACHEDIR
|
||||
value: /tmp/kubecache
|
||||
image: quay.io/argoproj/argocd:v3.2.0
|
||||
image: quay.io/argoproj/argocd:v3.2.7
|
||||
imagePullPolicy: Always
|
||||
name: argocd-application-controller
|
||||
ports:
|
||||
|
||||
22
manifests/namespace-install.yaml
generated
22
manifests/namespace-install.yaml
generated
@@ -943,7 +943,7 @@ spec:
|
||||
key: applicationsetcontroller.status.max.resources.count
|
||||
name: argocd-cmd-params-cm
|
||||
optional: true
|
||||
image: quay.io/argoproj/argocd:v3.2.0
|
||||
image: quay.io/argoproj/argocd:v3.2.7
|
||||
imagePullPolicy: Always
|
||||
name: argocd-applicationset-controller
|
||||
ports:
|
||||
@@ -1095,7 +1095,7 @@ spec:
|
||||
- -n
|
||||
- /usr/local/bin/argocd
|
||||
- /shared/argocd-dex
|
||||
image: quay.io/argoproj/argocd:v3.2.0
|
||||
image: quay.io/argoproj/argocd:v3.2.7
|
||||
imagePullPolicy: Always
|
||||
name: copyutil
|
||||
securityContext:
|
||||
@@ -1191,7 +1191,7 @@ spec:
|
||||
key: notificationscontroller.repo.server.plaintext
|
||||
name: argocd-cmd-params-cm
|
||||
optional: true
|
||||
image: quay.io/argoproj/argocd:v3.2.0
|
||||
image: quay.io/argoproj/argocd:v3.2.7
|
||||
imagePullPolicy: Always
|
||||
livenessProbe:
|
||||
tcpSocket:
|
||||
@@ -1293,7 +1293,7 @@ spec:
|
||||
- argocd
|
||||
- admin
|
||||
- redis-initial-password
|
||||
image: quay.io/argoproj/argocd:v3.2.0
|
||||
image: quay.io/argoproj/argocd:v3.2.7
|
||||
imagePullPolicy: IfNotPresent
|
||||
name: secret-init
|
||||
securityContext:
|
||||
@@ -1566,6 +1566,12 @@ spec:
|
||||
key: reposerver.git.request.timeout
|
||||
name: argocd-cmd-params-cm
|
||||
optional: true
|
||||
- name: ARGOCD_REPO_SERVER_ENABLE_BUILTIN_GIT_CONFIG
|
||||
valueFrom:
|
||||
configMapKeyRef:
|
||||
key: reposerver.enable.builtin.git.config
|
||||
name: argocd-cmd-params-cm
|
||||
optional: true
|
||||
- name: ARGOCD_GRPC_MAX_SIZE_MB
|
||||
valueFrom:
|
||||
configMapKeyRef:
|
||||
@@ -1584,7 +1590,7 @@ spec:
|
||||
value: /helm-working-dir
|
||||
- name: HELM_DATA_HOME
|
||||
value: /helm-working-dir
|
||||
image: quay.io/argoproj/argocd:v3.2.0
|
||||
image: quay.io/argoproj/argocd:v3.2.7
|
||||
imagePullPolicy: Always
|
||||
livenessProbe:
|
||||
failureThreshold: 3
|
||||
@@ -1636,7 +1642,7 @@ spec:
|
||||
- -n
|
||||
- /usr/local/bin/argocd
|
||||
- /var/run/argocd/argocd-cmp-server
|
||||
image: quay.io/argoproj/argocd:v3.2.0
|
||||
image: quay.io/argoproj/argocd:v3.2.7
|
||||
name: copyutil
|
||||
securityContext:
|
||||
allowPrivilegeEscalation: false
|
||||
@@ -2008,7 +2014,7 @@ spec:
|
||||
key: server.sync.replace.allowed
|
||||
name: argocd-cmd-params-cm
|
||||
optional: true
|
||||
image: quay.io/argoproj/argocd:v3.2.0
|
||||
image: quay.io/argoproj/argocd:v3.2.7
|
||||
imagePullPolicy: Always
|
||||
livenessProbe:
|
||||
httpGet:
|
||||
@@ -2392,7 +2398,7 @@ spec:
|
||||
optional: true
|
||||
- name: KUBECACHEDIR
|
||||
value: /tmp/kubecache
|
||||
image: quay.io/argoproj/argocd:v3.2.0
|
||||
image: quay.io/argoproj/argocd:v3.2.7
|
||||
imagePullPolicy: Always
|
||||
name: argocd-application-controller
|
||||
ports:
|
||||
|
||||
@@ -63,6 +63,7 @@ nav:
|
||||
- operator-manual/web_based_terminal.md
|
||||
- operator-manual/config-management-plugins.md
|
||||
- operator-manual/deep_links.md
|
||||
- operator-manual/git_configuration.md
|
||||
- Notifications:
|
||||
- Overview: operator-manual/notifications/index.md
|
||||
- operator-manual/notifications/triggers.md
|
||||
@@ -91,6 +92,7 @@ nav:
|
||||
- operator-manual/notifications/services/pushover.md
|
||||
- operator-manual/notifications/services/rocketchat.md
|
||||
- operator-manual/notifications/services/slack.md
|
||||
- operator-manual/notifications/services/teams-workflows.md
|
||||
- operator-manual/notifications/services/teams.md
|
||||
- operator-manual/notifications/services/telegram.md
|
||||
- operator-manual/notifications/services/webex.md
|
||||
|
||||
@@ -1,10 +1,20 @@
|
||||
package apiclient
|
||||
|
||||
import (
|
||||
"context"
|
||||
"io"
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"strconv"
|
||||
"sync"
|
||||
"sync/atomic"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
"google.golang.org/grpc/codes"
|
||||
"google.golang.org/grpc/metadata"
|
||||
)
|
||||
|
||||
func Test_parseHeaders(t *testing.T) {
|
||||
@@ -39,3 +49,234 @@ func Test_parseGRPCHeaders(t *testing.T) {
|
||||
assert.ErrorContains(t, err, "additional headers must be colon(:)-separated: foo")
|
||||
})
|
||||
}
|
||||
|
||||
func TestExecuteRequest_ClosesBodyOnHTTPError(t *testing.T) {
|
||||
bodyClosed := &atomic.Bool{}
|
||||
|
||||
// Create a test server that returns HTTP 500 error
|
||||
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, _ *http.Request) {
|
||||
w.WriteHeader(http.StatusInternalServerError)
|
||||
}))
|
||||
defer server.Close()
|
||||
|
||||
// Create client with custom httpClient that tracks body closure
|
||||
originalTransport := http.DefaultTransport
|
||||
customTransport := &testTransport{
|
||||
base: originalTransport,
|
||||
bodyClosed: bodyClosed,
|
||||
}
|
||||
|
||||
c := &client{
|
||||
ServerAddr: server.URL[7:], // Remove "http://"
|
||||
PlainText: true,
|
||||
httpClient: &http.Client{
|
||||
Transport: customTransport,
|
||||
},
|
||||
GRPCWebRootPath: "",
|
||||
}
|
||||
|
||||
// Execute request that should fail with HTTP 500
|
||||
ctx := context.Background()
|
||||
md := metadata.New(map[string]string{})
|
||||
_, err := c.executeRequest(ctx, "/test.Service/Method", []byte("test"), md)
|
||||
|
||||
// Verify error was returned
|
||||
require.Error(t, err)
|
||||
assert.Contains(t, err.Error(), "failed with status code 500")
|
||||
|
||||
// Give a small delay to ensure Close() was called
|
||||
time.Sleep(10 * time.Millisecond)
|
||||
|
||||
// Verify body was closed to prevent connection leak
|
||||
assert.True(t, bodyClosed.Load(), "response body should be closed on HTTP error to prevent connection leak")
|
||||
}
|
||||
|
||||
func TestExecuteRequest_ClosesBodyOnGRPCError(t *testing.T) {
|
||||
bodyClosed := &atomic.Bool{}
|
||||
|
||||
// Create a test server that returns HTTP 200 but with gRPC error status
|
||||
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, _ *http.Request) {
|
||||
w.Header().Set("Grpc-Status", "3") // codes.InvalidArgument
|
||||
w.Header().Set("Grpc-Message", "invalid argument")
|
||||
w.WriteHeader(http.StatusOK)
|
||||
}))
|
||||
defer server.Close()
|
||||
|
||||
// Create client with custom httpClient that tracks body closure
|
||||
originalTransport := http.DefaultTransport
|
||||
customTransport := &testTransport{
|
||||
base: originalTransport,
|
||||
bodyClosed: bodyClosed,
|
||||
}
|
||||
|
||||
c := &client{
|
||||
ServerAddr: server.URL[7:], // Remove "http://"
|
||||
PlainText: true,
|
||||
httpClient: &http.Client{
|
||||
Transport: customTransport,
|
||||
},
|
||||
GRPCWebRootPath: "",
|
||||
}
|
||||
|
||||
// Execute request that should fail with gRPC error
|
||||
ctx := context.Background()
|
||||
md := metadata.New(map[string]string{})
|
||||
_, err := c.executeRequest(ctx, "/test.Service/Method", []byte("test"), md)
|
||||
|
||||
// Verify gRPC error was returned
|
||||
require.Error(t, err)
|
||||
assert.Contains(t, err.Error(), "invalid argument")
|
||||
|
||||
// Give a small delay to ensure Close() was called
|
||||
time.Sleep(10 * time.Millisecond)
|
||||
|
||||
// Verify body was closed to prevent connection leak
|
||||
assert.True(t, bodyClosed.Load(), "response body should be closed on gRPC error to prevent connection leak")
|
||||
}
|
||||
|
||||
func TestExecuteRequest_ConcurrentErrorRequests_NoConnectionLeak(t *testing.T) {
|
||||
// This test simulates the scenario from the test script:
|
||||
// Multiple concurrent requests that fail should all close their response bodies
|
||||
|
||||
var totalRequests atomic.Int32
|
||||
var closedBodies atomic.Int32
|
||||
|
||||
// Create a test server that always returns errors
|
||||
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, _ *http.Request) {
|
||||
totalRequests.Add(1)
|
||||
// Alternate between HTTP errors and gRPC errors
|
||||
if totalRequests.Load()%2 == 0 {
|
||||
w.WriteHeader(http.StatusBadRequest)
|
||||
} else {
|
||||
w.Header().Set("Grpc-Status", strconv.Itoa(int(codes.PermissionDenied)))
|
||||
w.Header().Set("Grpc-Message", "permission denied")
|
||||
w.WriteHeader(http.StatusOK)
|
||||
}
|
||||
}))
|
||||
defer server.Close()
|
||||
|
||||
// Create client with custom transport that tracks closures
|
||||
customTransport := &testTransport{
|
||||
base: http.DefaultTransport,
|
||||
bodyClosed: &atomic.Bool{},
|
||||
onClose: func() {
|
||||
closedBodies.Add(1)
|
||||
},
|
||||
}
|
||||
|
||||
c := &client{
|
||||
ServerAddr: server.URL[7:],
|
||||
PlainText: true,
|
||||
httpClient: &http.Client{
|
||||
Transport: customTransport,
|
||||
},
|
||||
GRPCWebRootPath: "",
|
||||
}
|
||||
|
||||
// Simulate concurrent requests like in the test script
|
||||
concurrency := 10
|
||||
iterations := 5
|
||||
|
||||
var wg sync.WaitGroup
|
||||
for iter := 0; iter < iterations; iter++ {
|
||||
for i := 0; i < concurrency; i++ {
|
||||
wg.Add(1)
|
||||
go func() {
|
||||
defer wg.Done()
|
||||
ctx := context.Background()
|
||||
md := metadata.New(map[string]string{})
|
||||
_, err := c.executeRequest(ctx, "/application.ApplicationService/ManagedResources", []byte("test"), md)
|
||||
// We expect errors
|
||||
assert.Error(t, err)
|
||||
}()
|
||||
}
|
||||
wg.Wait()
|
||||
}
|
||||
|
||||
// Give time for all Close() calls to complete
|
||||
time.Sleep(100 * time.Millisecond)
|
||||
|
||||
// Verify all response bodies were closed
|
||||
expectedTotal := int32(concurrency * iterations)
|
||||
assert.Equal(t, expectedTotal, totalRequests.Load(), "all requests should have been made")
|
||||
assert.Equal(t, expectedTotal, closedBodies.Load(), "all response bodies should be closed to prevent connection leaks")
|
||||
}
|
||||
|
||||
func TestExecuteRequest_SuccessDoesNotCloseBodyPrematurely(t *testing.T) {
|
||||
// Verify that successful requests do NOT close the body in executeRequest
|
||||
// (caller is responsible for closing in success case)
|
||||
|
||||
bodyClosed := &atomic.Bool{}
|
||||
|
||||
// Create a test server that returns success
|
||||
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, _ *http.Request) {
|
||||
w.Header().Set("Grpc-Status", "0") // codes.OK
|
||||
w.WriteHeader(http.StatusOK)
|
||||
}))
|
||||
defer server.Close()
|
||||
|
||||
customTransport := &testTransport{
|
||||
base: http.DefaultTransport,
|
||||
bodyClosed: bodyClosed,
|
||||
}
|
||||
|
||||
c := &client{
|
||||
ServerAddr: server.URL[7:],
|
||||
PlainText: true,
|
||||
httpClient: &http.Client{
|
||||
Transport: customTransport,
|
||||
},
|
||||
GRPCWebRootPath: "",
|
||||
}
|
||||
|
||||
// Execute successful request
|
||||
ctx := context.Background()
|
||||
md := metadata.New(map[string]string{})
|
||||
resp, err := c.executeRequest(ctx, "/test.Service/Method", []byte("test"), md)
|
||||
|
||||
// Verify success
|
||||
require.NoError(t, err)
|
||||
require.NotNil(t, resp)
|
||||
defer resp.Body.Close()
|
||||
|
||||
// Verify body was NOT closed by executeRequest (caller's responsibility)
|
||||
time.Sleep(10 * time.Millisecond)
|
||||
assert.False(t, bodyClosed.Load(), "response body should NOT be closed by executeRequest on success - caller is responsible")
|
||||
}
|
||||
|
||||
// testTransport wraps http.RoundTripper to track body closures
|
||||
type testTransport struct {
|
||||
base http.RoundTripper
|
||||
bodyClosed *atomic.Bool
|
||||
onClose func() // Optional callback for each close
|
||||
}
|
||||
|
||||
func (t *testTransport) RoundTrip(req *http.Request) (*http.Response, error) {
|
||||
resp, err := t.base.RoundTrip(req)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Wrap the response body to track Close() calls
|
||||
resp.Body = &closeTracker{
|
||||
ReadCloser: resp.Body,
|
||||
closed: t.bodyClosed,
|
||||
onClose: t.onClose,
|
||||
}
|
||||
|
||||
return resp, nil
|
||||
}
|
||||
|
||||
type closeTracker struct {
|
||||
io.ReadCloser
|
||||
closed *atomic.Bool
|
||||
onClose func()
|
||||
}
|
||||
|
||||
func (c *closeTracker) Close() error {
|
||||
c.closed.Store(true)
|
||||
if c.onClose != nil {
|
||||
c.onClose()
|
||||
}
|
||||
return c.ReadCloser.Close()
|
||||
}
|
||||
|
||||
@@ -86,6 +86,9 @@ func (c *client) executeRequest(ctx context.Context, fullMethodName string, msg
|
||||
return nil, err
|
||||
}
|
||||
if resp.StatusCode != http.StatusOK {
|
||||
if resp.Body != nil {
|
||||
utilio.Close(resp.Body)
|
||||
}
|
||||
return nil, fmt.Errorf("%s %s failed with status code %d", req.Method, req.URL, resp.StatusCode)
|
||||
}
|
||||
var code codes.Code
|
||||
@@ -97,6 +100,9 @@ func (c *client) executeRequest(ctx context.Context, fullMethodName string, msg
|
||||
code = codes.Code(statusInt)
|
||||
}
|
||||
if code != codes.OK {
|
||||
if resp.Body != nil {
|
||||
utilio.Close(resp.Body)
|
||||
}
|
||||
return nil, status.Error(code, resp.Header.Get("Grpc-Message"))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -117,6 +117,7 @@ type RepoServerInitConstants struct {
|
||||
DisableHelmManifestMaxExtractedSize bool
|
||||
IncludeHiddenDirectories bool
|
||||
CMPUseManifestGeneratePaths bool
|
||||
EnableBuiltinGitConfig bool
|
||||
}
|
||||
|
||||
var manifestGenerateLock = sync.NewKeyLock()
|
||||
@@ -2566,7 +2567,9 @@ func (s *Service) newClient(repo *v1alpha1.Repository, opts ...git.ClientOpts) (
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
opts = append(opts, git.WithEventHandlers(metrics.NewGitClientEventHandlers(s.metricsServer)))
|
||||
opts = append(opts,
|
||||
git.WithEventHandlers(metrics.NewGitClientEventHandlers(s.metricsServer)),
|
||||
git.WithBuiltinGitConfig(s.initConstants.EnableBuiltinGitConfig))
|
||||
return s.newGitClient(repo.Repo, repoPath, repo.GetGitCreds(s.gitCredsStore), repo.IsInsecure(), repo.EnableLFS, repo.Proxy, repo.NoProxy, opts...)
|
||||
}
|
||||
|
||||
|
||||
@@ -24,14 +24,23 @@ if obj.status ~= nil then
|
||||
|
||||
if obj.status.conditions ~= nil then
|
||||
for i, condition in pairs(obj.status.conditions) do
|
||||
-- Check if the InferenceService is Stopped
|
||||
if condition.type == "Stopped" and condition.status == "True" then
|
||||
health_status.status = "Suspended"
|
||||
health_status.message = "InferenceService is Stopped"
|
||||
return health_status
|
||||
end
|
||||
|
||||
-- Check for unhealthy statuses
|
||||
-- Note: The Stopped condition's healthy status is False
|
||||
if condition.status == "Unknown" then
|
||||
status_unknown = status_unknown + 1
|
||||
elseif condition.status == "False" then
|
||||
elseif condition.status == "False" and condition.type ~= "Stopped" then
|
||||
status_false = status_false + 1
|
||||
end
|
||||
|
||||
if condition.status ~= "True" then
|
||||
-- Add the error messages if the status is unhealthy
|
||||
if condition.status ~= "True" and condition.type ~= "Stopped" then
|
||||
msg = msg .. " | " .. i .. ": " .. condition.type .. " | " .. condition.status
|
||||
if condition.reason ~= nil and condition.reason ~= "" then
|
||||
msg = msg .. " | " .. condition.reason
|
||||
|
||||
@@ -23,6 +23,10 @@ tests:
|
||||
status: Degraded
|
||||
message: "0: transitionStatus | BlockedByFailedLoad"
|
||||
inputPath: testdata/degraded_modelmesh.yaml
|
||||
- healthStatus:
|
||||
status: Suspended
|
||||
message: InferenceService is Stopped
|
||||
inputPath: testdata/stopped.yaml
|
||||
- healthStatus:
|
||||
status: Healthy
|
||||
message: InferenceService is healthy.
|
||||
|
||||
@@ -23,3 +23,7 @@ status:
|
||||
- lastTransitionTime: "2023-06-20T22:44:51Z"
|
||||
status: "True"
|
||||
type: Ready
|
||||
- lastTransitionTime: "2023-06-20T22:44:51Z"
|
||||
severity: Info
|
||||
status: 'False'
|
||||
type: Stopped
|
||||
|
||||
@@ -31,5 +31,9 @@ status:
|
||||
severity: Info
|
||||
status: 'True'
|
||||
type: RoutesReady
|
||||
- lastTransitionTime: '2024-05-30T22:14:31Z'
|
||||
severity: Info
|
||||
status: 'False'
|
||||
type: Stopped
|
||||
modelStatus:
|
||||
transitionStatus: UpToDate
|
||||
|
||||
@@ -17,3 +17,7 @@ status:
|
||||
- lastTransitionTime: '2024-05-16T18:48:56Z'
|
||||
status: 'True'
|
||||
type: Ready
|
||||
- lastTransitionTime: '2024-05-16T18:48:56Z'
|
||||
severity: Info
|
||||
status: 'False'
|
||||
type: Stopped
|
||||
|
||||
23
resource_customizations/serving.kserve.io/InferenceService/testdata/stopped.yaml
vendored
Normal file
23
resource_customizations/serving.kserve.io/InferenceService/testdata/stopped.yaml
vendored
Normal file
@@ -0,0 +1,23 @@
|
||||
apiVersion: serving.kserve.io/v1beta1
|
||||
kind: InferenceService
|
||||
metadata:
|
||||
name: helloworld
|
||||
namespace: default
|
||||
annotations:
|
||||
serving.kserve.io/deploymentMode: RawDeployment
|
||||
serving.kserve.io/stop: 'true'
|
||||
spec: {}
|
||||
status:
|
||||
conditions:
|
||||
- lastTransitionTime: '2024-05-16T18:48:56Z'
|
||||
reason: Stopped
|
||||
status: 'False'
|
||||
type: PredictorReady
|
||||
- lastTransitionTime: '2024-05-16T18:48:56Z'
|
||||
reason: Stopped
|
||||
status: 'False'
|
||||
type: Ready
|
||||
- lastTransitionTime: '2024-05-16T18:48:56Z'
|
||||
severity: Info
|
||||
status: 'True'
|
||||
type: Stopped
|
||||
@@ -2407,7 +2407,7 @@ func (s *Server) TerminateOperation(ctx context.Context, termOpReq *application.
|
||||
}
|
||||
log.Warnf("failed to set operation for app %q due to update conflict. retrying again...", *termOpReq.Name)
|
||||
time.Sleep(100 * time.Millisecond)
|
||||
_, err = s.appclientset.ArgoprojV1alpha1().Applications(appNs).Get(ctx, appName, metav1.GetOptions{})
|
||||
a, err = s.appclientset.ArgoprojV1alpha1().Applications(appNs).Get(ctx, appName, metav1.GetOptions{})
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("error getting application by name: %w", err)
|
||||
}
|
||||
|
||||
@@ -29,6 +29,7 @@ import (
|
||||
appsv1 "k8s.io/api/apps/v1"
|
||||
k8sbatchv1 "k8s.io/api/batch/v1"
|
||||
corev1 "k8s.io/api/core/v1"
|
||||
apierrors "k8s.io/apimachinery/pkg/api/errors"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
|
||||
"k8s.io/apimachinery/pkg/runtime"
|
||||
@@ -4030,3 +4031,75 @@ func TestServerSideDiff(t *testing.T) {
|
||||
assert.Contains(t, err.Error(), "application")
|
||||
})
|
||||
}
|
||||
|
||||
// TestTerminateOperationWithConflicts tests that TerminateOperation properly handles
|
||||
// concurrent update conflicts by retrying with the fresh application object.
|
||||
//
|
||||
// This test reproduces a bug where the retry loop discards the fresh app object
|
||||
// fetched from Get(), causing all retries to fail with stale resource versions.
|
||||
func TestTerminateOperationWithConflicts(t *testing.T) {
|
||||
testApp := newTestApp()
|
||||
testApp.ResourceVersion = "1"
|
||||
testApp.Operation = &v1alpha1.Operation{
|
||||
Sync: &v1alpha1.SyncOperation{},
|
||||
}
|
||||
testApp.Status.OperationState = &v1alpha1.OperationState{
|
||||
Operation: *testApp.Operation,
|
||||
Phase: synccommon.OperationRunning,
|
||||
}
|
||||
|
||||
appServer := newTestAppServer(t, testApp)
|
||||
ctx := context.Background()
|
||||
|
||||
// Get the fake clientset from the deepCopy wrapper
|
||||
fakeAppCs := appServer.appclientset.(*deepCopyAppClientset).GetUnderlyingClientSet().(*apps.Clientset)
|
||||
|
||||
getCallCount := 0
|
||||
updateCallCount := 0
|
||||
|
||||
// Remove default reactors and add our custom ones
|
||||
fakeAppCs.ReactionChain = nil
|
||||
|
||||
// Mock Get to return original version first, then fresh version
|
||||
fakeAppCs.AddReactor("get", "applications", func(_ kubetesting.Action) (handled bool, ret runtime.Object, err error) {
|
||||
getCallCount++
|
||||
freshApp := testApp.DeepCopy()
|
||||
if getCallCount == 1 {
|
||||
// First Get (for initialization) returns original version
|
||||
freshApp.ResourceVersion = "1"
|
||||
} else {
|
||||
// Subsequent Gets (during retry) return fresh version
|
||||
freshApp.ResourceVersion = "2"
|
||||
}
|
||||
return true, freshApp, nil
|
||||
})
|
||||
|
||||
// Mock Update to return conflict on first call, success on second
|
||||
fakeAppCs.AddReactor("update", "applications", func(action kubetesting.Action) (handled bool, ret runtime.Object, err error) {
|
||||
updateCallCount++
|
||||
updateAction := action.(kubetesting.UpdateAction)
|
||||
app := updateAction.GetObject().(*v1alpha1.Application)
|
||||
|
||||
// First call (with original resource version): return conflict
|
||||
if app.ResourceVersion == "1" {
|
||||
return true, nil, apierrors.NewConflict(
|
||||
schema.GroupResource{Group: "argoproj.io", Resource: "applications"},
|
||||
app.Name,
|
||||
stderrors.New("the object has been modified"),
|
||||
)
|
||||
}
|
||||
|
||||
// Second call (with refreshed resource version from Get): return success
|
||||
updatedApp := app.DeepCopy()
|
||||
return true, updatedApp, nil
|
||||
})
|
||||
|
||||
// Attempt to terminate the operation
|
||||
_, err := appServer.TerminateOperation(ctx, &application.OperationTerminateRequest{
|
||||
Name: ptr.To(testApp.Name),
|
||||
})
|
||||
|
||||
// Should succeed after retrying with the fresh app
|
||||
require.NoError(t, err)
|
||||
assert.GreaterOrEqual(t, updateCallCount, 2, "Update should be called at least twice (once with conflict, once with success)")
|
||||
}
|
||||
|
||||
@@ -1220,9 +1220,12 @@ func (server *ArgoCDServer) newHTTPServer(ctx context.Context, port int, grpcWeb
|
||||
|
||||
terminalOpts := application.TerminalOptions{DisableAuth: server.DisableAuth, Enf: server.enf}
|
||||
|
||||
// SSO ClientApp
|
||||
server.ssoClientApp, _ = oidc.NewClientApp(server.settings, server.DexServerAddr, server.DexTLSConfig, server.BaseHRef, cacheutil.NewRedisCache(server.RedisClient, server.settings.UserInfoCacheExpiration(), cacheutil.RedisCompressionNone))
|
||||
|
||||
terminal := application.NewHandler(server.appLister, server.Namespace, server.ApplicationNamespaces, server.db, appResourceTreeFn, server.settings.ExecShells, server.sessionMgr, &terminalOpts).
|
||||
WithFeatureFlagMiddleware(server.settingsMgr.GetSettings)
|
||||
th := util_session.WithAuthMiddleware(server.DisableAuth, server.sessionMgr, terminal)
|
||||
th := util_session.WithAuthMiddleware(server.DisableAuth, server.settings.IsSSOConfigured(), server.ssoClientApp, server.sessionMgr, terminal)
|
||||
mux.Handle("/terminal", th)
|
||||
|
||||
// Proxy extension is currently an alpha feature and is disabled
|
||||
@@ -1252,7 +1255,7 @@ func (server *ArgoCDServer) newHTTPServer(ctx context.Context, port int, grpcWeb
|
||||
swagger.ServeSwaggerUI(mux, assets.SwaggerJSON, "/swagger-ui", server.RootPath)
|
||||
healthz.ServeHealthCheck(mux, server.healthCheck)
|
||||
|
||||
// Dex reverse proxy and client app and OAuth2 login/callback
|
||||
// Dex reverse proxy and OAuth2 login/callback
|
||||
server.registerDexHandlers(mux)
|
||||
|
||||
// Webhook handler for git events (Note: cache timeouts are hardcoded because API server does not write to cache and not really using them)
|
||||
@@ -1304,7 +1307,7 @@ func enforceContentTypes(handler http.Handler, types []string) http.Handler {
|
||||
func registerExtensions(mux *http.ServeMux, a *ArgoCDServer, metricsReg HTTPMetricsRegistry) {
|
||||
a.log.Info("Registering extensions...")
|
||||
extHandler := http.HandlerFunc(a.extensionManager.CallExtension())
|
||||
authMiddleware := a.sessionMgr.AuthMiddlewareFunc(a.DisableAuth)
|
||||
authMiddleware := a.sessionMgr.AuthMiddlewareFunc(a.DisableAuth, a.settings.IsSSOConfigured(), a.ssoClientApp)
|
||||
// auth middleware ensures that requests to all extensions are authenticated first
|
||||
mux.Handle(extension.URLPrefix+"/", authMiddleware(extHandler))
|
||||
|
||||
@@ -1358,7 +1361,7 @@ func (server *ArgoCDServer) serveExtensions(extensionsSharedPath string, w http.
|
||||
}
|
||||
}
|
||||
|
||||
// registerDexHandlers will register dex HTTP handlers, creating the OAuth client app
|
||||
// registerDexHandlers will register dex HTTP handlers
|
||||
func (server *ArgoCDServer) registerDexHandlers(mux *http.ServeMux) {
|
||||
if !server.settings.IsSSOConfigured() {
|
||||
return
|
||||
@@ -1366,7 +1369,6 @@ func (server *ArgoCDServer) registerDexHandlers(mux *http.ServeMux) {
|
||||
// Run dex OpenID Connect Identity Provider behind a reverse proxy (served at /api/dex)
|
||||
var err error
|
||||
mux.HandleFunc(common.DexAPIEndpoint+"/", dexutil.NewDexHTTPReverseProxy(server.DexServerAddr, server.BaseHRef, server.DexTLSConfig))
|
||||
server.ssoClientApp, err = oidc.NewClientApp(server.settings, server.DexServerAddr, server.DexTLSConfig, server.BaseHRef, cacheutil.NewRedisCache(server.RedisClient, server.settings.UserInfoCacheExpiration(), cacheutil.RedisCompressionNone))
|
||||
errorsutil.CheckError(err)
|
||||
mux.HandleFunc(common.LoginEndpoint, server.ssoClientApp.HandleLogin)
|
||||
mux.HandleFunc(common.CallbackEndpoint, server.ssoClientApp.HandleCallback)
|
||||
@@ -1577,34 +1579,15 @@ func (server *ArgoCDServer) getClaims(ctx context.Context) (jwt.Claims, string,
|
||||
return claims, "", status.Errorf(codes.Unauthenticated, "invalid session: %v", err)
|
||||
}
|
||||
|
||||
// Some SSO implementations (Okta) require a call to
|
||||
// the OIDC user info path to get attributes like groups
|
||||
// we assume that everywhere in argocd jwt.MapClaims is used as type for interface jwt.Claims
|
||||
// otherwise this would cause a panic
|
||||
var groupClaims jwt.MapClaims
|
||||
if groupClaims, ok = claims.(jwt.MapClaims); !ok {
|
||||
if tmpClaims, ok := claims.(*jwt.MapClaims); ok {
|
||||
groupClaims = *tmpClaims
|
||||
}
|
||||
}
|
||||
iss := jwtutil.StringField(groupClaims, "iss")
|
||||
if iss != util_session.SessionManagerClaimsIssuer && server.settings.UserInfoGroupsEnabled() && server.settings.UserInfoPath() != "" {
|
||||
userInfo, unauthorized, err := server.ssoClientApp.GetUserInfo(groupClaims, server.settings.IssuerURL(), server.settings.UserInfoPath())
|
||||
if unauthorized {
|
||||
log.Errorf("error while quering userinfo endpoint: %v", err)
|
||||
return claims, "", status.Errorf(codes.Unauthenticated, "invalid session")
|
||||
}
|
||||
finalClaims := claims
|
||||
if server.settings.IsSSOConfigured() {
|
||||
finalClaims, err = server.ssoClientApp.SetGroupsFromUserInfo(claims, util_session.SessionManagerClaimsIssuer)
|
||||
if err != nil {
|
||||
log.Errorf("error fetching user info endpoint: %v", err)
|
||||
return claims, "", status.Errorf(codes.Internal, "invalid userinfo response")
|
||||
return claims, "", status.Errorf(codes.Unauthenticated, "invalid session: %v", err)
|
||||
}
|
||||
if groupClaims["sub"] != userInfo["sub"] {
|
||||
return claims, "", status.Error(codes.Unknown, "subject of claims from user info endpoint didn't match subject of idToken, see https://openid.net/specs/openid-connect-core-1_0.html#UserInfo")
|
||||
}
|
||||
groupClaims["groups"] = userInfo["groups"]
|
||||
}
|
||||
|
||||
return groupClaims, newToken, nil
|
||||
return finalClaims, newToken, nil
|
||||
}
|
||||
|
||||
// getToken extracts the token from gRPC metadata or cookie headers
|
||||
|
||||
@@ -8,7 +8,8 @@ sonar.projectVersion=1.0
|
||||
# Path is relative to the sonar-project.properties file. Replace "\" by "/" on Windows.
|
||||
sonar.sources=.
|
||||
|
||||
sonar.exclusions=docs/**
|
||||
# Exclude docs and testdata with kustomization files (Sonar IaC parser fails on empty/edge-case YAML)
|
||||
sonar.exclusions=docs/**,**/testdata/**
|
||||
|
||||
# Encoding of the source code. Default is default system encoding
|
||||
sonar.sourceEncoding=UTF-8
|
||||
@@ -24,5 +25,5 @@ sonar.go.exclusions=**/vendor/**,**/*.pb.go,**/*_test.go,**/*.pb.gw.go,**/mocks/
|
||||
# Exclude following set of patterns from duplication detection
|
||||
sonar.cpd.exclusions=**/*.pb.go,**/*.g.cs,**/*.gw.go,**/mocks/*,docs/**
|
||||
|
||||
# Exclude test manifests from analysis
|
||||
sonar.kubernetes.exclusions=controller/testdata/**,test/**,util/kustomize/testdata/**
|
||||
# Exclude test manifests from analysis (avoids Sonar IaC parser errors on empty/edge-case kustomization files)
|
||||
sonar.kubernetes.exclusions=controller/testdata/**,test/**,util/kustomize/testdata/**,util/app/discovery/testdata/**,reposerver/repository/testdata/**
|
||||
|
||||
@@ -3078,6 +3078,12 @@ func TestDeletionConfirmation(t *testing.T) {
|
||||
Then().Expect(OperationPhaseIs(OperationRunning)).
|
||||
When().ConfirmDeletion().
|
||||
Then().Expect(OperationPhaseIs(OperationSucceeded)).
|
||||
Expect(SyncStatusIs(SyncStatusCodeSynced)).
|
||||
Expect(HealthIs(health.HealthStatusHealthy)).
|
||||
// Wait for controller caches to fully settle before deletion
|
||||
// This ensures both the informer and cluster watcher have the latest state
|
||||
When().Refresh(RefreshTypeNormal).
|
||||
Then().
|
||||
When().Delete(true).
|
||||
Then().
|
||||
And(func(app *Application) {
|
||||
|
||||
@@ -41,10 +41,10 @@ func TestSyncWithNoDestinationServiceAccountsInProject(t *testing.T) {
|
||||
Given(t).
|
||||
Path("guestbook").
|
||||
When().
|
||||
WithImpersonationEnabled("", nil).
|
||||
CreateFromFile(func(app *v1alpha1.Application) {
|
||||
app.Spec.SyncPolicy = &v1alpha1.SyncPolicy{Automated: &v1alpha1.SyncPolicyAutomated{}}
|
||||
}).
|
||||
WithImpersonationEnabled("", nil).
|
||||
Then().
|
||||
// With the impersonation feature enabled, Application sync must fail
|
||||
// when there are no destination service accounts configured in AppProject
|
||||
|
||||
@@ -37,15 +37,17 @@ export const ApplicationHydrateOperationState: React.FunctionComponent<Props> =
|
||||
if (hydrateOperationState.finishedAt && hydrateOperationState.phase !== 'Hydrating') {
|
||||
operationAttributes.push({title: 'FINISHED AT', value: <Timestamp date={hydrateOperationState.finishedAt} />});
|
||||
}
|
||||
operationAttributes.push({
|
||||
title: 'DRY REVISION',
|
||||
value: (
|
||||
<div>
|
||||
<Revision repoUrl={hydrateOperationState.sourceHydrator.drySource.repoURL} revision={hydrateOperationState.drySHA} />
|
||||
</div>
|
||||
)
|
||||
});
|
||||
if (hydrateOperationState.finishedAt) {
|
||||
if (hydrateOperationState.drySHA) {
|
||||
operationAttributes.push({
|
||||
title: 'DRY REVISION',
|
||||
value: (
|
||||
<div>
|
||||
<Revision repoUrl={hydrateOperationState.sourceHydrator.drySource.repoURL} revision={hydrateOperationState.drySHA} />
|
||||
</div>
|
||||
)
|
||||
});
|
||||
}
|
||||
if (hydrateOperationState.finishedAt && hydrateOperationState.hydratedSHA) {
|
||||
operationAttributes.push({
|
||||
title: 'HYDRATED REVISION',
|
||||
value: (
|
||||
|
||||
@@ -272,13 +272,15 @@ export const ApplicationNodeInfo = (props: {
|
||||
Resource not found in cluster:{' '}
|
||||
{`${props?.controlled?.state?.targetState?.apiVersion}/${props?.controlled?.state?.targetState?.kind}:${props.node.name}`}
|
||||
<br />
|
||||
{props?.controlled?.state?.normalizedLiveState?.apiVersion && (
|
||||
<span>
|
||||
Please update your resource specification to use the latest Kubernetes API resources supported by the target cluster. The
|
||||
recommended syntax is{' '}
|
||||
{`${props.controlled.state.normalizedLiveState.apiVersion}/${props?.controlled.state.normalizedLiveState?.kind}:${props.node.name}`}
|
||||
</span>
|
||||
)}
|
||||
{props?.controlled?.state?.normalizedLiveState?.apiVersion &&
|
||||
`${props?.controlled?.state?.targetState?.apiVersion}/${props?.controlled?.state?.targetState?.kind}:${props.node.name}` !==
|
||||
`${props.controlled.state.normalizedLiveState.apiVersion}/${props?.controlled.state.normalizedLiveState?.kind}:${props.node.name}` && (
|
||||
<span>
|
||||
Please update your resource specification to use the latest Kubernetes API resources supported by the target cluster. The
|
||||
recommended syntax is{' '}
|
||||
{`${props.controlled.state.normalizedLiveState.apiVersion}/${props?.controlled.state.normalizedLiveState?.kind}:${props.node.name}`}
|
||||
</span>
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
</React.Fragment>
|
||||
|
||||
@@ -1,8 +1,7 @@
|
||||
import * as React from 'react';
|
||||
import Moment from 'react-moment';
|
||||
import {Pod, ResourceName} from '../../../shared/models';
|
||||
import {isYoungerThanXMinutes} from '../utils';
|
||||
import {formatMetric} from './pod-view';
|
||||
import {isYoungerThanXMinutes, formatResourceInfo} from '../utils';
|
||||
|
||||
export const PodTooltip = (props: {pod: Pod}) => {
|
||||
const pod = props.pod;
|
||||
@@ -23,10 +22,20 @@ export const PodTooltip = (props: {pod: Pod}) => {
|
||||
})
|
||||
.map(i => {
|
||||
const isPodRequests = i.name === ResourceName.ResourceCPU || i.name === ResourceName.ResourceMemory;
|
||||
const formattedValue = isPodRequests ? formatMetric(i.name as ResourceName, parseInt(i.value, 10)) : i.value;
|
||||
let formattedValue = i.value;
|
||||
let label = `${i.name}:`;
|
||||
|
||||
//this is just to show cpu and mem info with "Requests" as prefix
|
||||
const label = i.name === ResourceName.ResourceCPU ? 'Requests CPU:' : i.name === ResourceName.ResourceMemory ? 'Requests MEM:' : `${i.name}:`;
|
||||
if (isPodRequests) {
|
||||
if (i.name === ResourceName.ResourceCPU) {
|
||||
const {displayValue} = formatResourceInfo('cpu', i.value);
|
||||
formattedValue = displayValue;
|
||||
label = 'Requests CPU:';
|
||||
} else if (i.name === ResourceName.ResourceMemory) {
|
||||
const {displayValue} = formatResourceInfo('memory', i.value);
|
||||
formattedValue = displayValue;
|
||||
label = 'Requests MEM:';
|
||||
}
|
||||
}
|
||||
|
||||
return (
|
||||
<div className='row' key={i.name}>
|
||||
|
||||
@@ -20,7 +20,7 @@
|
||||
position: absolute;
|
||||
color: #A3A3A3;
|
||||
font-size: 10px;
|
||||
top: -10px;
|
||||
top: -9px;
|
||||
transform: rotate(180deg);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -22,7 +22,8 @@ import {
|
||||
NodeId,
|
||||
nodeKey,
|
||||
PodHealthIcon,
|
||||
getUsrMsgKeyToDisplay
|
||||
getUsrMsgKeyToDisplay,
|
||||
formatResourceInfo
|
||||
} from '../utils';
|
||||
import {NodeUpdateAnimation} from './node-update-animation';
|
||||
import {PodGroup} from '../application-pod-view/pod-view';
|
||||
@@ -530,11 +531,19 @@ function renderPodGroup(props: ApplicationResourceTreeProps, id: string, node: R
|
||||
<Tooltip
|
||||
content={
|
||||
<>
|
||||
{(node.info || []).map(i => (
|
||||
<div key={i.name}>
|
||||
{i.name}: {i.value}
|
||||
</div>
|
||||
))}
|
||||
{(node.info || []).map(i => {
|
||||
// Use common formatting function for CPU and Memory
|
||||
if (i.name === 'cpu' || i.name === 'memory') {
|
||||
const {tooltipValue} = formatResourceInfo(i.name, `${i.value}`);
|
||||
return <div key={i.name}>{tooltipValue}</div>;
|
||||
} else {
|
||||
return (
|
||||
<div key={i.name}>
|
||||
{i.name}: {i.value}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
})}
|
||||
</>
|
||||
}
|
||||
key={node.uid}>
|
||||
@@ -662,9 +671,9 @@ function expandCollapse(node: ResourceTreeNode, props: ApplicationResourceTreePr
|
||||
|
||||
function NodeInfoDetails({tag: tag, kind: kind}: {tag: models.InfoItem; kind: string}) {
|
||||
if (kind === 'Pod') {
|
||||
const val = `${tag.name}`;
|
||||
const val = tag.name;
|
||||
if (val === 'Status Reason') {
|
||||
if (`${tag.value}` !== 'ImagePullBackOff')
|
||||
if (String(tag.value) !== 'ImagePullBackOff')
|
||||
return (
|
||||
<span className='application-resource-tree__node-label' title={`Status: ${tag.value}`}>
|
||||
{tag.value}
|
||||
@@ -680,10 +689,10 @@ function NodeInfoDetails({tag: tag, kind: kind}: {tag: models.InfoItem; kind: st
|
||||
);
|
||||
}
|
||||
} else if (val === 'Containers') {
|
||||
const arr = `${tag.value}`.split('/');
|
||||
const arr = String(tag.value).split('/');
|
||||
const title = `Number of containers in total: ${arr[1]} \nNumber of ready containers: ${arr[0]}`;
|
||||
return (
|
||||
<span className='application-resource-tree__node-label' title={`${title}`}>
|
||||
<span className='application-resource-tree__node-label' title={title}>
|
||||
{tag.value}
|
||||
</span>
|
||||
);
|
||||
@@ -699,6 +708,14 @@ function NodeInfoDetails({tag: tag, kind: kind}: {tag: models.InfoItem; kind: st
|
||||
{tag.value}
|
||||
</span>
|
||||
);
|
||||
} else if (val === 'cpu' || val === 'memory') {
|
||||
// Use common formatting function for CPU and Memory
|
||||
const {displayValue, tooltipValue} = formatResourceInfo(val, String(tag.value));
|
||||
return (
|
||||
<span className='application-resource-tree__node-label' title={tooltipValue}>
|
||||
{displayValue}
|
||||
</span>
|
||||
);
|
||||
} else {
|
||||
return (
|
||||
<span className='application-resource-tree__node-label' title={`${tag.name}: ${tag.value}`}>
|
||||
@@ -801,19 +818,27 @@ function renderResourceNode(props: ApplicationResourceTreeProps, id: string, nod
|
||||
) : null}
|
||||
{(node.info || [])
|
||||
.filter(tag => !tag.name.includes('Node'))
|
||||
.slice(0, 4)
|
||||
.slice(0, 2)
|
||||
.map((tag, i) => {
|
||||
return <NodeInfoDetails tag={tag} kind={node.kind} key={i} />;
|
||||
})}
|
||||
{(node.info || []).length > 4 && (
|
||||
{(node.info || []).length > 3 && (
|
||||
<Tooltip
|
||||
content={
|
||||
<>
|
||||
{(node.info || []).map(i => (
|
||||
<div key={i.name}>
|
||||
{i.name}: {i.value}
|
||||
</div>
|
||||
))}
|
||||
{(node.info || []).map(i => {
|
||||
// Use common formatting function for CPU and Memory
|
||||
if (i.name === 'cpu' || i.name === 'memory') {
|
||||
const {tooltipValue} = formatResourceInfo(i.name, `${i.value}`);
|
||||
return <div key={i.name}>{tooltipValue}</div>;
|
||||
} else {
|
||||
return (
|
||||
<div key={i.name}>
|
||||
{i.name}: {i.value}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
})}
|
||||
</>
|
||||
}
|
||||
key={node.uid}>
|
||||
|
||||
@@ -338,7 +338,7 @@ export const ApplicationStatusPanel = ({application, showDiff, showOperation, sh
|
||||
}}>
|
||||
{(data: models.ApplicationSyncWindowState) => (
|
||||
<React.Fragment>
|
||||
{data.assignedWindows && (
|
||||
{data?.assignedWindows && (
|
||||
<div className='application-status-panel__item' style={{position: 'relative'}}>
|
||||
{sectionLabel({
|
||||
title: 'SYNC WINDOWS',
|
||||
|
||||
@@ -94,7 +94,7 @@ export const ApplicationsRefreshPanel = ({show, apps, hide}: {show: boolean; app
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
<ApplicationSelector apps={apps} formApi={formApi} />
|
||||
{show && <ApplicationSelector apps={apps} formApi={formApi} />}
|
||||
</div>
|
||||
)}
|
||||
</Form>
|
||||
|
||||
@@ -146,7 +146,7 @@ export const ApplicationsSyncPanel = ({show, apps, hide}: {show: boolean; apps:
|
||||
|
||||
<ApplicationRetryOptions id='applications-sync-panel' formApi={formApi} />
|
||||
|
||||
<ApplicationSelector apps={apps} formApi={formApi} />
|
||||
{show && <ApplicationSelector apps={apps} formApi={formApi} />}
|
||||
</div>
|
||||
)}
|
||||
</Form>
|
||||
|
||||
@@ -1553,13 +1553,13 @@ export const SyncWindowStatusIcon = ({state, window}: {state: appModels.SyncWind
|
||||
);
|
||||
};
|
||||
|
||||
export const ApplicationSyncWindowStatusIcon = ({project, state}: {project: string; state: appModels.ApplicationSyncWindowState}) => {
|
||||
export const ApplicationSyncWindowStatusIcon = ({project, state}: {project: string; state?: appModels.ApplicationSyncWindowState}) => {
|
||||
let className = '';
|
||||
let color = '';
|
||||
let deny = false;
|
||||
let allow = false;
|
||||
let inactiveAllow = false;
|
||||
if (state.assignedWindows !== undefined && state.assignedWindows.length > 0) {
|
||||
if (state?.assignedWindows !== undefined && state?.assignedWindows.length > 0) {
|
||||
if (state.activeWindows !== undefined && state.activeWindows.length > 0) {
|
||||
for (const w of state.activeWindows) {
|
||||
if (w.kind === 'deny') {
|
||||
@@ -1815,3 +1815,43 @@ export const podRequests = {
|
||||
CPU: 'Requests (CPU)',
|
||||
MEMORY: 'Requests (MEM)'
|
||||
} as const;
|
||||
|
||||
export function formatResourceInfo(name: string, value: string): {displayValue: string; tooltipValue: string} {
|
||||
const numValue = parseInt(value, 10);
|
||||
|
||||
const formatCPUValue = (milliCpu: number): string => {
|
||||
return milliCpu >= 1000 ? `${(milliCpu / 1000).toFixed(1)}` : `${milliCpu}m`;
|
||||
};
|
||||
|
||||
const formatMemoryValue = (milliBytes: number): string => {
|
||||
const mib = Math.round(milliBytes / (1024 * 1024 * 1000));
|
||||
return `${mib}Mi`;
|
||||
};
|
||||
|
||||
const formatCPUTooltip = (milliCpu: number): string => {
|
||||
const displayValue = milliCpu >= 1000 ? `${(milliCpu / 1000).toFixed(1)} cores` : `${milliCpu}m`;
|
||||
return `CPU Request: ${displayValue}`;
|
||||
};
|
||||
|
||||
const formatMemoryTooltip = (milliBytes: number): string => {
|
||||
const mib = Math.round(milliBytes / (1024 * 1024 * 1000));
|
||||
return `Memory Request: ${mib}Mi`;
|
||||
};
|
||||
|
||||
if (name === 'cpu') {
|
||||
return {
|
||||
displayValue: formatCPUValue(numValue),
|
||||
tooltipValue: formatCPUTooltip(numValue)
|
||||
};
|
||||
} else if (name === 'memory') {
|
||||
return {
|
||||
displayValue: formatMemoryValue(numValue),
|
||||
tooltipValue: formatMemoryTooltip(numValue)
|
||||
};
|
||||
}
|
||||
|
||||
return {
|
||||
displayValue: value,
|
||||
tooltipValue: `${name}: ${value}`
|
||||
};
|
||||
}
|
||||
|
||||
@@ -511,7 +511,9 @@ export interface HydrateOperation {
|
||||
finishedAt?: models.Time;
|
||||
phase: HydrateOperationPhase;
|
||||
message: string;
|
||||
// drySHA is the sha of the DRY commit being hydrated. This will be empty if the operation is not successful.
|
||||
drySHA: string;
|
||||
// hydratedSHA is the sha of the hydrated commit. This will be empty if the operation is not successful.
|
||||
hydratedSHA: string;
|
||||
sourceHydrator: SourceHydrator;
|
||||
}
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user