mirror of
https://github.com/argoproj/argo-cd.git
synced 2026-03-06 00:18:48 +01:00
Compare commits
95 Commits
v2.10.0
...
release-2.
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
5498456fa8 | ||
|
|
7bd0c3669f | ||
|
|
c9a7c0cd47 | ||
|
|
a80f192b4f | ||
|
|
3d900c7084 | ||
|
|
a3e235907a | ||
|
|
6e33cba80e | ||
|
|
1200b6c42d | ||
|
|
f52dcf6f3c | ||
|
|
6b0060587d | ||
|
|
9865a8a340 | ||
|
|
b3c2bc114b | ||
|
|
286568e73a | ||
|
|
5b041c2af2 | ||
|
|
a1d375836e | ||
|
|
c02a3d775c | ||
|
|
3bf801f2df | ||
|
|
ef535230d8 | ||
|
|
14963d7fac | ||
|
|
46c0c0b64d | ||
|
|
794b2e050d | ||
|
|
d8ddce87be | ||
|
|
7e99a1340e | ||
|
|
63a30293fe | ||
|
|
2fbb69b892 | ||
|
|
9c711337e7 | ||
|
|
2c2064be7b | ||
|
|
9d8148bc61 | ||
|
|
9ba6943111 | ||
|
|
c79714d660 | ||
|
|
cb6f5ac8b8 | ||
|
|
ec35043a64 | ||
|
|
531123b70c | ||
|
|
27e49f8b78 | ||
|
|
82ae349929 | ||
|
|
f61f47264f | ||
|
|
0a179fb98e | ||
|
|
a960c6be07 | ||
|
|
0895ebc135 | ||
|
|
2de0ceade2 | ||
|
|
bdd889d439 | ||
|
|
d58c96b456 | ||
|
|
5425568bd1 | ||
|
|
320ced67b8 | ||
|
|
f2d31330ff | ||
|
|
fa7f330ab3 | ||
|
|
1466755aeb | ||
|
|
8d267c0136 | ||
|
|
c071af8081 | ||
|
|
04785a4861 | ||
|
|
37b1cf5306 | ||
|
|
15865b9a04 | ||
|
|
47a35c1a11 | ||
|
|
9e5cc5a26f | ||
|
|
19addbd9bb | ||
|
|
744df40552 | ||
|
|
b060053b09 | ||
|
|
696ca0a57f | ||
|
|
c514105af7 | ||
|
|
d504d2b1d9 | ||
|
|
5814864d6c | ||
|
|
da65596511 | ||
|
|
73be9c4c2c | ||
|
|
d124f1603e | ||
|
|
335875d13e | ||
|
|
4192e3f3ac | ||
|
|
3e5a878f6e | ||
|
|
47d586169f | ||
|
|
f5d63a5c77 | ||
|
|
ce04dc5c6f | ||
|
|
cebb6538f7 | ||
|
|
ab7e45da13 | ||
|
|
a8ae929d55 | ||
|
|
f3fdaa7eab | ||
|
|
0b4659c046 | ||
|
|
0fd6344537 | ||
|
|
0977f61554 | ||
|
|
3dd069b049 | ||
|
|
37da5e2ae5 | ||
|
|
12886657ac | ||
|
|
fcf5d8c238 | ||
|
|
1ee3c80bc8 | ||
|
|
a79fcad0e9 | ||
|
|
67e57a47a2 | ||
|
|
d99ee9d28b | ||
|
|
28a9225e7b | ||
|
|
f5d6b2972b | ||
|
|
06e2e0da9a | ||
|
|
a79e0eaca4 | ||
|
|
65461a1b61 | ||
|
|
2268f08819 | ||
|
|
a1a5c58a7d | ||
|
|
9c379af169 | ||
|
|
4e01115a48 | ||
|
|
eddf0a5f30 |
14
.github/workflows/ci-build.yaml
vendored
14
.github/workflows/ci-build.yaml
vendored
@@ -52,7 +52,7 @@ jobs:
|
||||
with:
|
||||
go-version: ${{ env.GOLANG_VERSION }}
|
||||
- name: Restore go build cache
|
||||
uses: actions/cache@704facf57e6136b1bc63b828d79edcd491f0ee84 # v3.3.2
|
||||
uses: actions/cache@0400d5f644dc74513175e3cd8d07132dd4860809 # v4.2.4
|
||||
with:
|
||||
path: ~/.cache/go-build
|
||||
key: ${{ runner.os }}-go-build-v1-${{ github.run_id }}
|
||||
@@ -117,7 +117,7 @@ jobs:
|
||||
run: |
|
||||
echo "/usr/local/bin" >> $GITHUB_PATH
|
||||
- name: Restore go build cache
|
||||
uses: actions/cache@704facf57e6136b1bc63b828d79edcd491f0ee84 # v3.3.2
|
||||
uses: actions/cache@0400d5f644dc74513175e3cd8d07132dd4860809 # v4.2.4
|
||||
with:
|
||||
path: ~/.cache/go-build
|
||||
key: ${{ runner.os }}-go-build-v1-${{ github.run_id }}
|
||||
@@ -184,7 +184,7 @@ jobs:
|
||||
run: |
|
||||
echo "/usr/local/bin" >> $GITHUB_PATH
|
||||
- name: Restore go build cache
|
||||
uses: actions/cache@704facf57e6136b1bc63b828d79edcd491f0ee84 # v3.3.2
|
||||
uses: actions/cache@0400d5f644dc74513175e3cd8d07132dd4860809 # v4.2.4
|
||||
with:
|
||||
path: ~/.cache/go-build
|
||||
key: ${{ runner.os }}-go-build-v1-${{ github.run_id }}
|
||||
@@ -270,7 +270,7 @@ jobs:
|
||||
node-version: '20.7.0'
|
||||
- name: Restore node dependency cache
|
||||
id: cache-dependencies
|
||||
uses: actions/cache@704facf57e6136b1bc63b828d79edcd491f0ee84 # v3.3.2
|
||||
uses: actions/cache@0400d5f644dc74513175e3cd8d07132dd4860809 # v4.2.4
|
||||
with:
|
||||
path: ui/node_modules
|
||||
key: ${{ runner.os }}-node-dep-v2-${{ hashFiles('**/yarn.lock') }}
|
||||
@@ -305,7 +305,7 @@ jobs:
|
||||
fetch-depth: 0
|
||||
- name: Restore node dependency cache
|
||||
id: cache-dependencies
|
||||
uses: actions/cache@704facf57e6136b1bc63b828d79edcd491f0ee84 # v3.3.2
|
||||
uses: actions/cache@0400d5f644dc74513175e3cd8d07132dd4860809 # v4.2.4
|
||||
with:
|
||||
path: ui/node_modules
|
||||
key: ${{ runner.os }}-node-dep-v2-${{ hashFiles('**/yarn.lock') }}
|
||||
@@ -401,7 +401,7 @@ jobs:
|
||||
sudo chmod go-r $HOME/.kube/config
|
||||
kubectl version
|
||||
- name: Restore go build cache
|
||||
uses: actions/cache@704facf57e6136b1bc63b828d79edcd491f0ee84 # v3.3.2
|
||||
uses: actions/cache@0400d5f644dc74513175e3cd8d07132dd4860809 # v4.2.4
|
||||
with:
|
||||
path: ~/.cache/go-build
|
||||
key: ${{ runner.os }}-go-build-v1-${{ github.run_id }}
|
||||
@@ -429,7 +429,7 @@ jobs:
|
||||
run: |
|
||||
docker pull ghcr.io/dexidp/dex:v2.37.0
|
||||
docker pull argoproj/argo-cd-ci-builder:v1.0.0
|
||||
docker pull redis:7.0.14-alpine
|
||||
docker pull redis:7.0.15-alpine
|
||||
- name: Create target directory for binaries in the build-process
|
||||
run: |
|
||||
mkdir -p dist
|
||||
|
||||
4
.github/workflows/image-reuse.yaml
vendored
4
.github/workflows/image-reuse.yaml
vendored
@@ -74,9 +74,7 @@ jobs:
|
||||
go-version: ${{ inputs.go-version }}
|
||||
|
||||
- name: Install cosign
|
||||
uses: sigstore/cosign-installer@1fc5bd396d372bee37d608f955b336615edf79c8 # v3.2.0
|
||||
with:
|
||||
cosign-release: 'v2.2.1'
|
||||
uses: sigstore/cosign-installer@e1523de7571e31dbe865fd2e80c5c7c23ae71eb4 # v3.4.0
|
||||
|
||||
- uses: docker/setup-qemu-action@2b82ce82d56a2a04d2637cd93a637ae1b359c0a7 # v2.2.0
|
||||
- uses: docker/setup-buildx-action@f95db51fddba0c2d1ec667646a06c2ce06100226 # v3.0.0
|
||||
|
||||
2
.github/workflows/image.yaml
vendored
2
.github/workflows/image.yaml
vendored
@@ -86,7 +86,7 @@ jobs:
|
||||
packages: write # for uploading attestations. (https://github.com/slsa-framework/slsa-github-generator/blob/main/internal/builders/container/README.md#known-issues)
|
||||
if: ${{ github.repository == 'argoproj/argo-cd' && github.event_name == 'push' }}
|
||||
# Must be refernced by a tag. https://github.com/slsa-framework/slsa-github-generator/blob/main/internal/builders/container/README.md#referencing-the-slsa-generator
|
||||
uses: slsa-framework/slsa-github-generator/.github/workflows/generator_container_slsa3.yml@v1.7.0
|
||||
uses: slsa-framework/slsa-github-generator/.github/workflows/generator_container_slsa3.yml@v2.1.0
|
||||
with:
|
||||
image: ghcr.io/argoproj/argo-cd/argocd
|
||||
digest: ${{ needs.build-and-publish.outputs.image-digest }}
|
||||
|
||||
42
.github/workflows/release.yaml
vendored
42
.github/workflows/release.yaml
vendored
@@ -31,20 +31,20 @@ jobs:
|
||||
quay_password: ${{ secrets.RELEASE_QUAY_TOKEN }}
|
||||
|
||||
argocd-image-provenance:
|
||||
needs: [argocd-image]
|
||||
permissions:
|
||||
actions: read # for detecting the Github Actions environment.
|
||||
id-token: write # for creating OIDC tokens for signing.
|
||||
packages: write # for uploading attestations. (https://github.com/slsa-framework/slsa-github-generator/blob/main/internal/builders/container/README.md#known-issues)
|
||||
# Must be refernced by a tag. https://github.com/slsa-framework/slsa-github-generator/blob/main/internal/builders/container/README.md#referencing-the-slsa-generator
|
||||
if: github.repository == 'argoproj/argo-cd'
|
||||
uses: slsa-framework/slsa-github-generator/.github/workflows/generator_container_slsa3.yml@v1.9.0
|
||||
with:
|
||||
image: quay.io/argoproj/argocd
|
||||
digest: ${{ needs.argocd-image.outputs.image-digest }}
|
||||
secrets:
|
||||
registry-username: ${{ secrets.RELEASE_QUAY_USERNAME }}
|
||||
registry-password: ${{ secrets.RELEASE_QUAY_TOKEN }}
|
||||
needs: [argocd-image]
|
||||
permissions:
|
||||
actions: read # for detecting the Github Actions environment.
|
||||
id-token: write # for creating OIDC tokens for signing.
|
||||
packages: write # for uploading attestations. (https://github.com/slsa-framework/slsa-github-generator/blob/main/internal/builders/container/README.md#known-issues)
|
||||
# Must be refernced by a tag. https://github.com/slsa-framework/slsa-github-generator/blob/main/internal/builders/container/README.md#referencing-the-slsa-generator
|
||||
if: github.repository == 'argoproj/argo-cd'
|
||||
uses: slsa-framework/slsa-github-generator/.github/workflows/generator_container_slsa3.yml@v2.1.0
|
||||
with:
|
||||
image: quay.io/argoproj/argocd
|
||||
digest: ${{ needs.argocd-image.outputs.image-digest }}
|
||||
secrets:
|
||||
registry-username: ${{ secrets.RELEASE_QUAY_USERNAME }}
|
||||
registry-password: ${{ secrets.RELEASE_QUAY_TOKEN }}
|
||||
|
||||
goreleaser:
|
||||
needs:
|
||||
@@ -87,6 +87,14 @@ jobs:
|
||||
echo "KUBECTL_VERSION=$(go list -m k8s.io/client-go | head -n 1 | rev | cut -d' ' -f1 | rev)" >> $GITHUB_ENV
|
||||
echo "GIT_TREE_STATE=$(if [ -z "`git status --porcelain`" ]; then echo "clean" ; else echo "dirty"; fi)" >> $GITHUB_ENV
|
||||
|
||||
- name: Free Disk Space (Ubuntu)
|
||||
uses: jlumbroso/free-disk-space@4d9e71b726748f254fe64fa44d273194bd18ec91
|
||||
with:
|
||||
large-packages: false
|
||||
docker-images: false
|
||||
swap-storage: false
|
||||
tool-cache: false
|
||||
|
||||
- name: Run GoReleaser
|
||||
uses: goreleaser/goreleaser-action@7ec5c2b0c6cdda6e8bbb49444bc797dd33d74dd8 # v5.0.0
|
||||
id: run-goreleaser
|
||||
@@ -120,7 +128,7 @@ jobs:
|
||||
contents: write # Needed for release uploads
|
||||
if: github.repository == 'argoproj/argo-cd'
|
||||
# Must be refernced by a tag. https://github.com/slsa-framework/slsa-github-generator/blob/main/internal/builders/container/README.md#referencing-the-slsa-generator
|
||||
uses: slsa-framework/slsa-github-generator/.github/workflows/generator_generic_slsa3.yml@v1.9.0
|
||||
uses: slsa-framework/slsa-github-generator/.github/workflows/generator_generic_slsa3.yml@v2.1.0
|
||||
with:
|
||||
base64-subjects: "${{ needs.goreleaser.outputs.hashes }}"
|
||||
provenance-name: "argocd-cli.intoto.jsonl"
|
||||
@@ -203,8 +211,8 @@ jobs:
|
||||
id-token: write # Needed for provenance signing and ID
|
||||
contents: write # Needed for release uploads
|
||||
if: github.repository == 'argoproj/argo-cd'
|
||||
# Must be refernced by a tag. https://github.com/slsa-framework/slsa-github-generator/blob/main/internal/builders/container/README.md#referencing-the-slsa-generator
|
||||
uses: slsa-framework/slsa-github-generator/.github/workflows/generator_generic_slsa3.yml@v1.9.0
|
||||
# Must be referenced by a tag. https://github.com/slsa-framework/slsa-github-generator/blob/main/internal/builders/container/README.md#referencing-the-slsa-generator
|
||||
uses: slsa-framework/slsa-github-generator/.github/workflows/generator_generic_slsa3.yml@v2.1.0
|
||||
with:
|
||||
base64-subjects: "${{ needs.generate-sbom.outputs.hashes }}"
|
||||
provenance-name: "argocd-sbom.intoto.jsonl"
|
||||
|
||||
@@ -114,7 +114,7 @@ changelog:
|
||||
exclude:
|
||||
- '^test:'
|
||||
- '^.*?Bump(\([[:word:]]+\))?.+$'
|
||||
- '^.*?[Bot](\([[:word:]]+\))?.+$'
|
||||
- '^.*?\[Bot\](\([[:word:]]+\))?.+$'
|
||||
|
||||
|
||||
# yaml-language-server: $schema=https://goreleaser.com/static/schema.json
|
||||
|
||||
@@ -4,9 +4,9 @@ ARG BASE_IMAGE=docker.io/library/ubuntu:22.04@sha256:0bced47fffa3361afa981854fca
|
||||
# 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.21.3@sha256:02d7116222536a5cf0fcf631f90b507758b669648e0f20186d2dc94a9b419a9b AS builder
|
||||
FROM docker.io/library/golang:1.21.10@sha256:16438a8e66c0c984f732e815ee5b7d715b8e33e81bac6d6a3750b1067744e7ca AS builder
|
||||
|
||||
RUN echo 'deb http://deb.debian.org/debian buster-backports main' >> /etc/apt/sources.list
|
||||
RUN echo 'deb http://archive.debian.org/debian buster-backports main' >> /etc/apt/sources.list
|
||||
|
||||
RUN apt-get update && apt-get install --no-install-recommends -y \
|
||||
openssh-server \
|
||||
@@ -101,7 +101,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.21.3@sha256:02d7116222536a5cf0fcf631f90b507758b669648e0f20186d2dc94a9b419a9b AS argocd-build
|
||||
FROM --platform=$BUILDPLATFORM docker.io/library/golang:1.21.10@sha256:16438a8e66c0c984f732e815ee5b7d715b8e33e81bac6d6a3750b1067744e7ca AS argocd-build
|
||||
|
||||
WORKDIR /go/src/github.com/argoproj/argo-cd
|
||||
|
||||
|
||||
2
USERS.md
2
USERS.md
@@ -40,6 +40,7 @@ Currently, the following organizations are **officially** using Argo CD:
|
||||
1. [Boozt](https://www.booztgroup.com/)
|
||||
1. [Boticario](https://www.boticario.com.br/)
|
||||
1. [Bulder Bank](https://bulderbank.no)
|
||||
1. [CAM](https://cam-inc.co.jp)
|
||||
1. [Camptocamp](https://camptocamp.com)
|
||||
1. [Candis](https://www.candis.io)
|
||||
1. [Capital One](https://www.capitalone.com)
|
||||
@@ -233,6 +234,7 @@ Currently, the following organizations are **officially** using Argo CD:
|
||||
1. [QuintoAndar](https://quintoandar.com.br)
|
||||
1. [Quipper](https://www.quipper.com/)
|
||||
1. [RapidAPI](https://www.rapidapi.com/)
|
||||
1. [rebuy](https://www.rebuy.de/)
|
||||
1. [Recreation.gov](https://www.recreation.gov/)
|
||||
1. [Red Hat](https://www.redhat.com/)
|
||||
1. [Redpill Linpro](https://www.redpill-linpro.com/)
|
||||
|
||||
@@ -50,6 +50,7 @@ import (
|
||||
argov1alpha1 "github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1"
|
||||
appclientset "github.com/argoproj/argo-cd/v2/pkg/client/clientset/versioned"
|
||||
argoutil "github.com/argoproj/argo-cd/v2/util/argo"
|
||||
"github.com/argoproj/argo-cd/v2/util/argo/normalizers"
|
||||
|
||||
"github.com/argoproj/argo-cd/v2/pkg/apis/application"
|
||||
)
|
||||
@@ -666,7 +667,7 @@ func (r *ApplicationSetReconciler) createOrUpdateInCluster(ctx context.Context,
|
||||
},
|
||||
}
|
||||
|
||||
action, err := utils.CreateOrUpdate(ctx, appLog, r.Client, applicationSet.Spec.IgnoreApplicationDifferences, found, func() error {
|
||||
action, err := utils.CreateOrUpdate(ctx, appLog, r.Client, applicationSet.Spec.IgnoreApplicationDifferences, normalizers.IgnoreNormalizerOpts{}, found, func() error {
|
||||
// Copy only the Application/ObjectMeta fields that are significant, from the generatedApp
|
||||
found.Spec = generatedApp.Spec
|
||||
|
||||
|
||||
@@ -20,6 +20,7 @@ import (
|
||||
argov1alpha1 "github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1"
|
||||
"github.com/argoproj/argo-cd/v2/util/argo"
|
||||
argodiff "github.com/argoproj/argo-cd/v2/util/argo/diff"
|
||||
"github.com/argoproj/argo-cd/v2/util/argo/normalizers"
|
||||
)
|
||||
|
||||
// CreateOrUpdate overrides "sigs.k8s.io/controller-runtime" function
|
||||
@@ -35,7 +36,7 @@ import (
|
||||
// The MutateFn is called regardless of creating or updating an object.
|
||||
//
|
||||
// It returns the executed operation and an error.
|
||||
func CreateOrUpdate(ctx context.Context, logCtx *log.Entry, c client.Client, ignoreAppDifferences argov1alpha1.ApplicationSetIgnoreDifferences, obj *argov1alpha1.Application, f controllerutil.MutateFn) (controllerutil.OperationResult, error) {
|
||||
func CreateOrUpdate(ctx context.Context, logCtx *log.Entry, c client.Client, ignoreAppDifferences argov1alpha1.ApplicationSetIgnoreDifferences, ignoreNormalizerOpts normalizers.IgnoreNormalizerOpts, obj *argov1alpha1.Application, f controllerutil.MutateFn) (controllerutil.OperationResult, error) {
|
||||
|
||||
key := client.ObjectKeyFromObject(obj)
|
||||
if err := c.Get(ctx, key, obj); err != nil {
|
||||
@@ -60,7 +61,7 @@ func CreateOrUpdate(ctx context.Context, logCtx *log.Entry, c client.Client, ign
|
||||
|
||||
// Apply ignoreApplicationDifferences rules to remove ignored fields from both the live and the desired state. This
|
||||
// prevents those differences from appearing in the diff and therefore in the patch.
|
||||
err := applyIgnoreDifferences(ignoreAppDifferences, normalizedLive, obj)
|
||||
err := applyIgnoreDifferences(ignoreAppDifferences, normalizedLive, obj, ignoreNormalizerOpts)
|
||||
if err != nil {
|
||||
return controllerutil.OperationResultNone, fmt.Errorf("failed to apply ignore differences: %w", err)
|
||||
}
|
||||
@@ -134,14 +135,14 @@ func mutate(f controllerutil.MutateFn, key client.ObjectKey, obj client.Object)
|
||||
}
|
||||
|
||||
// applyIgnoreDifferences applies the ignore differences rules to the found application. It modifies the applications in place.
|
||||
func applyIgnoreDifferences(applicationSetIgnoreDifferences argov1alpha1.ApplicationSetIgnoreDifferences, found *argov1alpha1.Application, generatedApp *argov1alpha1.Application) error {
|
||||
func applyIgnoreDifferences(applicationSetIgnoreDifferences argov1alpha1.ApplicationSetIgnoreDifferences, found *argov1alpha1.Application, generatedApp *argov1alpha1.Application, ignoreNormalizerOpts normalizers.IgnoreNormalizerOpts) error {
|
||||
if len(applicationSetIgnoreDifferences) == 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
generatedAppCopy := generatedApp.DeepCopy()
|
||||
diffConfig, err := argodiff.NewDiffConfigBuilder().
|
||||
WithDiffSettings(applicationSetIgnoreDifferences.ToApplicationIgnoreDifferences(), nil, false).
|
||||
WithDiffSettings(applicationSetIgnoreDifferences.ToApplicationIgnoreDifferences(), nil, false, ignoreNormalizerOpts).
|
||||
WithNoCache().
|
||||
Build()
|
||||
if err != nil {
|
||||
|
||||
@@ -9,6 +9,7 @@ import (
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
|
||||
"github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1"
|
||||
"github.com/argoproj/argo-cd/v2/util/argo/normalizers"
|
||||
)
|
||||
|
||||
func Test_applyIgnoreDifferences(t *testing.T) {
|
||||
@@ -222,7 +223,7 @@ spec:
|
||||
generatedApp := v1alpha1.Application{TypeMeta: appMeta}
|
||||
err = yaml.Unmarshal([]byte(tc.generatedApp), &generatedApp)
|
||||
require.NoError(t, err, tc.generatedApp)
|
||||
err = applyIgnoreDifferences(tc.ignoreDifferences, &foundApp, &generatedApp)
|
||||
err = applyIgnoreDifferences(tc.ignoreDifferences, &foundApp, &generatedApp, normalizers.IgnoreNormalizerOpts{})
|
||||
require.NoError(t, err)
|
||||
yamlFound, err := yaml.Marshal(tc.foundApp)
|
||||
require.NoError(t, err)
|
||||
|
||||
@@ -7405,6 +7405,7 @@
|
||||
"properties": {
|
||||
"elements": {
|
||||
"type": "array",
|
||||
"title": "+kubebuilder:validation:Optional",
|
||||
"items": {
|
||||
"$ref": "#/definitions/v1JSON"
|
||||
}
|
||||
|
||||
@@ -6,7 +6,6 @@ import (
|
||||
"math"
|
||||
"time"
|
||||
|
||||
"github.com/argoproj/argo-cd/v2/pkg/ratelimiter"
|
||||
"github.com/argoproj/pkg/stats"
|
||||
"github.com/redis/go-redis/v9"
|
||||
log "github.com/sirupsen/logrus"
|
||||
@@ -20,19 +19,18 @@ import (
|
||||
"github.com/argoproj/argo-cd/v2/controller/sharding"
|
||||
"github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1"
|
||||
appclientset "github.com/argoproj/argo-cd/v2/pkg/client/clientset/versioned"
|
||||
"github.com/argoproj/argo-cd/v2/pkg/ratelimiter"
|
||||
"github.com/argoproj/argo-cd/v2/reposerver/apiclient"
|
||||
"github.com/argoproj/argo-cd/v2/util/argo/normalizers"
|
||||
cacheutil "github.com/argoproj/argo-cd/v2/util/cache"
|
||||
appstatecache "github.com/argoproj/argo-cd/v2/util/cache/appstate"
|
||||
"github.com/argoproj/argo-cd/v2/util/cli"
|
||||
"github.com/argoproj/argo-cd/v2/util/db"
|
||||
"github.com/argoproj/argo-cd/v2/util/env"
|
||||
"github.com/argoproj/argo-cd/v2/util/errors"
|
||||
kubeutil "github.com/argoproj/argo-cd/v2/util/kube"
|
||||
"github.com/argoproj/argo-cd/v2/util/settings"
|
||||
"github.com/argoproj/argo-cd/v2/util/tls"
|
||||
"github.com/argoproj/argo-cd/v2/util/trace"
|
||||
kubeerrors "k8s.io/apimachinery/pkg/api/errors"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
)
|
||||
|
||||
const (
|
||||
@@ -75,6 +73,7 @@ func NewCommand() *cobra.Command {
|
||||
shardingAlgorithm string
|
||||
enableDynamicClusterDistribution bool
|
||||
serverSideDiff bool
|
||||
ignoreNormalizerOpts normalizers.IgnoreNormalizerOpts
|
||||
)
|
||||
var command = cobra.Command{
|
||||
Use: cliName,
|
||||
@@ -147,7 +146,8 @@ func NewCommand() *cobra.Command {
|
||||
appController.InvalidateProjectsCache()
|
||||
}))
|
||||
kubectl := kubeutil.NewKubectl()
|
||||
clusterSharding := getClusterSharding(kubeClient, settingsMgr, shardingAlgorithm, enableDynamicClusterDistribution)
|
||||
clusterSharding, err := sharding.GetClusterSharding(kubeClient, settingsMgr, shardingAlgorithm, enableDynamicClusterDistribution)
|
||||
errors.CheckError(err)
|
||||
appController, err = controller.NewApplicationController(
|
||||
namespace,
|
||||
settingsMgr,
|
||||
@@ -170,6 +170,8 @@ func NewCommand() *cobra.Command {
|
||||
applicationNamespaces,
|
||||
&workqueueRateLimit,
|
||||
serverSideDiff,
|
||||
enableDynamicClusterDistribution,
|
||||
ignoreNormalizerOpts,
|
||||
)
|
||||
errors.CheckError(err)
|
||||
cacheutil.CollectMetrics(redisClient, appController.GetMetricsServer())
|
||||
@@ -221,7 +223,7 @@ func NewCommand() *cobra.Command {
|
||||
command.Flags().StringVar(&shardingAlgorithm, "sharding-method", env.StringFromEnv(common.EnvControllerShardingAlgorithm, common.DefaultShardingAlgorithm), "Enables choice of sharding method. Supported sharding methods are : [legacy, round-robin] ")
|
||||
// global queue rate limit config
|
||||
command.Flags().Int64Var(&workqueueRateLimit.BucketSize, "wq-bucket-size", env.ParseInt64FromEnv("WORKQUEUE_BUCKET_SIZE", 500, 1, math.MaxInt64), "Set Workqueue Rate Limiter Bucket Size, default 500")
|
||||
command.Flags().Int64Var(&workqueueRateLimit.BucketQPS, "wq-bucket-qps", env.ParseInt64FromEnv("WORKQUEUE_BUCKET_QPS", 50, 1, math.MaxInt64), "Set Workqueue Rate Limiter Bucket QPS, default 50")
|
||||
command.Flags().Float64Var(&workqueueRateLimit.BucketQPS, "wq-bucket-qps", env.ParseFloat64FromEnv("WORKQUEUE_BUCKET_QPS", math.MaxFloat64, 1, math.MaxFloat64), "Set Workqueue Rate Limiter Bucket QPS, default set to MaxFloat64 which disables the bucket limiter")
|
||||
// individual item rate limit config
|
||||
// when WORKQUEUE_FAILURE_COOLDOWN is 0 per item rate limiting is disabled(default)
|
||||
command.Flags().DurationVar(&workqueueRateLimit.FailureCoolDown, "wq-cooldown-ns", time.Duration(env.ParseInt64FromEnv("WORKQUEUE_FAILURE_COOLDOWN_NS", 0, 0, (24*time.Hour).Nanoseconds())), "Set Workqueue Per Item Rate Limiter Cooldown duration in ns, default 0(per item rate limiter disabled)")
|
||||
@@ -230,61 +232,9 @@ func NewCommand() *cobra.Command {
|
||||
command.Flags().Float64Var(&workqueueRateLimit.BackoffFactor, "wq-backoff-factor", env.ParseFloat64FromEnv("WORKQUEUE_BACKOFF_FACTOR", 1.5, 0, math.MaxFloat64), "Set Workqueue Per Item Rate Limiter Backoff Factor, default is 1.5")
|
||||
command.Flags().BoolVar(&enableDynamicClusterDistribution, "dynamic-cluster-distribution-enabled", env.ParseBoolFromEnv(common.EnvEnableDynamicClusterDistribution, false), "Enables dynamic cluster distribution.")
|
||||
command.Flags().BoolVar(&serverSideDiff, "server-side-diff-enabled", env.ParseBoolFromEnv(common.EnvServerSideDiff, false), "Feature flag to enable ServerSide diff. Default (\"false\")")
|
||||
command.Flags().DurationVar(&ignoreNormalizerOpts.JQExecutionTimeout, "ignore-normalizer-jq-execution-timeout-seconds", env.ParseDurationFromEnv("ARGOCD_IGNORE_NORMALIZER_JQ_TIMEOUT", 0*time.Second, 0, math.MaxInt64), "Set ignore normalizer JQ execution timeout")
|
||||
cacheSource = appstatecache.AddCacheFlagsToCmd(&command, func(client *redis.Client) {
|
||||
redisClient = client
|
||||
})
|
||||
return &command
|
||||
}
|
||||
|
||||
func getClusterSharding(kubeClient *kubernetes.Clientset, settingsMgr *settings.SettingsManager, shardingAlgorithm string, enableDynamicClusterDistribution bool) sharding.ClusterShardingCache {
|
||||
var replicasCount int
|
||||
// StatefulSet mode and Deployment mode uses different default values for shard number.
|
||||
defaultShardNumberValue := 0
|
||||
applicationControllerName := env.StringFromEnv(common.EnvAppControllerName, common.DefaultApplicationControllerName)
|
||||
appControllerDeployment, err := kubeClient.AppsV1().Deployments(settingsMgr.GetNamespace()).Get(context.Background(), applicationControllerName, metav1.GetOptions{})
|
||||
|
||||
// if the application controller deployment was not found, the Get() call returns an empty Deployment object. So, set the variable to nil explicitly
|
||||
if err != nil && kubeerrors.IsNotFound(err) {
|
||||
appControllerDeployment = nil
|
||||
}
|
||||
|
||||
if enableDynamicClusterDistribution && appControllerDeployment != nil && appControllerDeployment.Spec.Replicas != nil {
|
||||
replicasCount = int(*appControllerDeployment.Spec.Replicas)
|
||||
defaultShardNumberValue = -1
|
||||
} else {
|
||||
replicasCount = env.ParseNumFromEnv(common.EnvControllerReplicas, 0, 0, math.MaxInt32)
|
||||
}
|
||||
shardNumber := env.ParseNumFromEnv(common.EnvControllerShard, defaultShardNumberValue, -math.MaxInt32, math.MaxInt32)
|
||||
if replicasCount > 1 {
|
||||
// check for shard mapping using configmap if application-controller is a deployment
|
||||
// else use existing logic to infer shard from pod name if application-controller is a statefulset
|
||||
if enableDynamicClusterDistribution && appControllerDeployment != nil {
|
||||
var err error
|
||||
// retry 3 times if we find a conflict while updating shard mapping configMap.
|
||||
// If we still see conflicts after the retries, wait for next iteration of heartbeat process.
|
||||
for i := 0; i <= common.AppControllerHeartbeatUpdateRetryCount; i++ {
|
||||
shardNumber, err = sharding.GetOrUpdateShardFromConfigMap(kubeClient, settingsMgr, replicasCount, shardNumber)
|
||||
if err != nil && !kubeerrors.IsConflict(err) {
|
||||
err = fmt.Errorf("unable to get shard due to error updating the sharding config map: %s", err)
|
||||
break
|
||||
}
|
||||
log.Warnf("conflict when getting shard from shard mapping configMap. Retrying (%d/3)", i)
|
||||
}
|
||||
errors.CheckError(err)
|
||||
} else {
|
||||
if shardNumber < 0 {
|
||||
var err error
|
||||
shardNumber, err = sharding.InferShard()
|
||||
errors.CheckError(err)
|
||||
}
|
||||
if shardNumber > replicasCount {
|
||||
log.Warnf("Calculated shard number %d is greated than the number of replicas count. Defaulting to 0", shardNumber)
|
||||
shardNumber = 0
|
||||
}
|
||||
}
|
||||
} else {
|
||||
log.Info("Processing all cluster shards")
|
||||
}
|
||||
db := db.NewDB(settingsMgr.GetNamespace(), settingsMgr, kubeClient)
|
||||
return sharding.NewClusterSharding(db, shardNumber, replicasCount, shardingAlgorithm)
|
||||
}
|
||||
|
||||
@@ -68,6 +68,7 @@ func NewCommand() *cobra.Command {
|
||||
streamedManifestMaxTarSize string
|
||||
streamedManifestMaxExtractedSize string
|
||||
helmManifestMaxExtractedSize string
|
||||
helmRegistryMaxIndexSize string
|
||||
disableManifestMaxExtractedSize bool
|
||||
)
|
||||
var command = cobra.Command{
|
||||
@@ -110,6 +111,9 @@ func NewCommand() *cobra.Command {
|
||||
helmManifestMaxExtractedSizeQuantity, err := resource.ParseQuantity(helmManifestMaxExtractedSize)
|
||||
errors.CheckError(err)
|
||||
|
||||
helmRegistryMaxIndexSizeQuantity, err := resource.ParseQuantity(helmRegistryMaxIndexSize)
|
||||
errors.CheckError(err)
|
||||
|
||||
askPassServer := askpass.NewServer()
|
||||
metricsServer := metrics.NewMetricsServer()
|
||||
cacheutil.CollectMetrics(redisClient, metricsServer)
|
||||
@@ -125,6 +129,7 @@ func NewCommand() *cobra.Command {
|
||||
StreamedManifestMaxExtractedSize: streamedManifestMaxExtractedSizeQuantity.ToDec().Value(),
|
||||
StreamedManifestMaxTarSize: streamedManifestMaxTarSizeQuantity.ToDec().Value(),
|
||||
HelmManifestMaxExtractedSize: helmManifestMaxExtractedSizeQuantity.ToDec().Value(),
|
||||
HelmRegistryMaxIndexSize: helmRegistryMaxIndexSizeQuantity.ToDec().Value(),
|
||||
}, askPassServer)
|
||||
errors.CheckError(err)
|
||||
|
||||
@@ -208,6 +213,7 @@ func NewCommand() *cobra.Command {
|
||||
command.Flags().StringVar(&streamedManifestMaxTarSize, "streamed-manifest-max-tar-size", env.StringFromEnv("ARGOCD_REPO_SERVER_STREAMED_MANIFEST_MAX_TAR_SIZE", "100M"), "Maximum size of streamed manifest archives")
|
||||
command.Flags().StringVar(&streamedManifestMaxExtractedSize, "streamed-manifest-max-extracted-size", env.StringFromEnv("ARGOCD_REPO_SERVER_STREAMED_MANIFEST_MAX_EXTRACTED_SIZE", "1G"), "Maximum size of streamed manifest archives when extracted")
|
||||
command.Flags().StringVar(&helmManifestMaxExtractedSize, "helm-manifest-max-extracted-size", env.StringFromEnv("ARGOCD_REPO_SERVER_HELM_MANIFEST_MAX_EXTRACTED_SIZE", "1G"), "Maximum size of helm manifest archives when extracted")
|
||||
command.Flags().StringVar(&helmRegistryMaxIndexSize, "helm-registry-max-index-size", env.StringFromEnv("ARGOCD_REPO_SERVER_HELM_MANIFEST_MAX_INDEX_SIZE", "1G"), "Maximum size of registry index file")
|
||||
command.Flags().BoolVar(&disableManifestMaxExtractedSize, "disable-helm-manifest-max-extracted-size", env.ParseBoolFromEnv("ARGOCD_REPO_SERVER_DISABLE_HELM_MANIFEST_MAX_EXTRACTED_SIZE", false), "Disable maximum size of helm manifest archives when extracted")
|
||||
tlsConfigCustomizerSrc = tls.AddTLSFlagsToCmd(&command)
|
||||
cacheSrc = reposervercache.AddCacheFlagsToCmd(&command, func(client *redis.Client) {
|
||||
|
||||
@@ -242,7 +242,7 @@ func NewCommand() *cobra.Command {
|
||||
command.Flags().StringVar(&repoServerAddress, "repo-server", env.StringFromEnv("ARGOCD_SERVER_REPO_SERVER", common.DefaultRepoServerAddr), "Repo server address")
|
||||
command.Flags().StringVar(&dexServerAddress, "dex-server", env.StringFromEnv("ARGOCD_SERVER_DEX_SERVER", common.DefaultDexServerAddr), "Dex server address")
|
||||
command.Flags().BoolVar(&disableAuth, "disable-auth", env.ParseBoolFromEnv("ARGOCD_SERVER_DISABLE_AUTH", false), "Disable client authentication")
|
||||
command.Flags().StringVar(&contentTypes, "api-content-types", env.StringFromEnv("ARGOCD_API_CONTENT_TYPES", "application/json"), "Semicolon separated list of allowed content types for non GET api requests. Any content type is allowed if empty.")
|
||||
command.Flags().StringVar(&contentTypes, "api-content-types", env.StringFromEnv("ARGOCD_API_CONTENT_TYPES", "application/json", env.StringFromEnvOpts{AllowEmpty: true}), "Semicolon separated list of allowed content types for non GET api requests. Any content type is allowed if empty.")
|
||||
command.Flags().BoolVar(&enableGZip, "enable-gzip", env.ParseBoolFromEnv("ARGOCD_SERVER_ENABLE_GZIP", true), "Enable GZIP compression")
|
||||
command.AddCommand(cli.NewVersionCmd(cliName))
|
||||
command.Flags().StringVar(&listenHost, "address", env.StringFromEnv("ARGOCD_SERVER_LISTEN_ADDRESS", common.DefaultAddressAPIServer), "Listen on given address")
|
||||
|
||||
@@ -141,6 +141,7 @@ $ argocd admin initial-password reset
|
||||
command.AddCommand(NewDashboardCommand(clientOpts))
|
||||
command.AddCommand(NewNotificationsCommand())
|
||||
command.AddCommand(NewInitialPasswordCommand())
|
||||
command.AddCommand(NewRedisInitialPasswordCommand())
|
||||
|
||||
command.Flags().StringVar(&cmdutil.LogFormat, "logformat", "text", "Set the logging format. One of: text|json")
|
||||
command.Flags().StringVar(&cmdutil.LogLevel, "loglevel", "info", "Set the logging level. One of: debug|info|warn|error")
|
||||
|
||||
@@ -30,6 +30,7 @@ import (
|
||||
appinformers "github.com/argoproj/argo-cd/v2/pkg/client/informers/externalversions"
|
||||
reposerverclient "github.com/argoproj/argo-cd/v2/reposerver/apiclient"
|
||||
"github.com/argoproj/argo-cd/v2/util/argo"
|
||||
"github.com/argoproj/argo-cd/v2/util/argo/normalizers"
|
||||
cacheutil "github.com/argoproj/argo-cd/v2/util/cache"
|
||||
appstatecache "github.com/argoproj/argo-cd/v2/util/cache/appstate"
|
||||
"github.com/argoproj/argo-cd/v2/util/cli"
|
||||
@@ -238,12 +239,13 @@ func diffReconcileResults(res1 reconcileResults, res2 reconcileResults) error {
|
||||
|
||||
func NewReconcileCommand(clientOpts *argocdclient.ClientOptions) *cobra.Command {
|
||||
var (
|
||||
clientConfig clientcmd.ClientConfig
|
||||
selector string
|
||||
repoServerAddress string
|
||||
outputFormat string
|
||||
refresh bool
|
||||
serverSideDiff bool
|
||||
clientConfig clientcmd.ClientConfig
|
||||
selector string
|
||||
repoServerAddress string
|
||||
outputFormat string
|
||||
refresh bool
|
||||
serverSideDiff bool
|
||||
ignoreNormalizerOpts normalizers.IgnoreNormalizerOpts
|
||||
)
|
||||
|
||||
var command = &cobra.Command{
|
||||
@@ -281,7 +283,7 @@ func NewReconcileCommand(clientOpts *argocdclient.ClientOptions) *cobra.Command
|
||||
|
||||
appClientset := appclientset.NewForConfigOrDie(cfg)
|
||||
kubeClientset := kubernetes.NewForConfigOrDie(cfg)
|
||||
result, err = reconcileApplications(ctx, kubeClientset, appClientset, namespace, repoServerClient, selector, newLiveStateCache, serverSideDiff)
|
||||
result, err = reconcileApplications(ctx, kubeClientset, appClientset, namespace, repoServerClient, selector, newLiveStateCache, serverSideDiff, ignoreNormalizerOpts)
|
||||
errors.CheckError(err)
|
||||
} else {
|
||||
appClientset := appclientset.NewForConfigOrDie(cfg)
|
||||
@@ -297,7 +299,7 @@ func NewReconcileCommand(clientOpts *argocdclient.ClientOptions) *cobra.Command
|
||||
command.Flags().StringVar(&outputFormat, "o", "yaml", "Output format (yaml|json)")
|
||||
command.Flags().BoolVar(&refresh, "refresh", false, "If set to true then recalculates apps reconciliation")
|
||||
command.Flags().BoolVar(&serverSideDiff, "server-side-diff", false, "If set to \"true\" will use server-side diff while comparing resources. Default (\"false\")")
|
||||
|
||||
command.Flags().DurationVar(&ignoreNormalizerOpts.JQExecutionTimeout, "ignore-normalizer-jq-execution-timeout", normalizers.DefaultJQExecutionTimeout, "Set ignore normalizer JQ execution timeout")
|
||||
return command
|
||||
}
|
||||
|
||||
@@ -347,6 +349,7 @@ func reconcileApplications(
|
||||
selector string,
|
||||
createLiveStateCache func(argoDB db.ArgoDB, appInformer kubecache.SharedIndexInformer, settingsMgr *settings.SettingsManager, server *metrics.MetricsServer) cache.LiveStateCache,
|
||||
serverSideDiff bool,
|
||||
ignoreNormalizerOpts normalizers.IgnoreNormalizerOpts,
|
||||
) ([]appReconcileResult, error) {
|
||||
settingsMgr := settings.NewSettingsManager(ctx, kubeClientset, namespace)
|
||||
argoDB := db.NewDB(namespace, settingsMgr, kubeClientset)
|
||||
@@ -387,7 +390,7 @@ func reconcileApplications(
|
||||
)
|
||||
|
||||
appStateManager := controller.NewAppStateManager(
|
||||
argoDB, appClientset, repoServerClient, namespace, kubeutil.NewKubectl(), settingsMgr, stateCache, projInformer, server, cache, time.Second, argo.NewResourceTracking(), false, 0, serverSideDiff)
|
||||
argoDB, appClientset, repoServerClient, namespace, kubeutil.NewKubectl(), settingsMgr, stateCache, projInformer, server, cache, time.Second, argo.NewResourceTracking(), false, 0, serverSideDiff, ignoreNormalizerOpts)
|
||||
|
||||
appsList, err := appClientset.ArgoprojV1alpha1().Applications(namespace).List(ctx, v1.ListOptions{LabelSelector: selector})
|
||||
if err != nil {
|
||||
|
||||
@@ -23,6 +23,7 @@ import (
|
||||
argocdclient "github.com/argoproj/argo-cd/v2/reposerver/apiclient"
|
||||
"github.com/argoproj/argo-cd/v2/reposerver/apiclient/mocks"
|
||||
"github.com/argoproj/argo-cd/v2/test"
|
||||
"github.com/argoproj/argo-cd/v2/util/argo/normalizers"
|
||||
"github.com/argoproj/argo-cd/v2/util/db"
|
||||
"github.com/argoproj/argo-cd/v2/util/settings"
|
||||
)
|
||||
@@ -114,6 +115,7 @@ func TestGetReconcileResults_Refresh(t *testing.T) {
|
||||
return &liveStateCache
|
||||
},
|
||||
false,
|
||||
normalizers.IgnoreNormalizerOpts{},
|
||||
)
|
||||
|
||||
if !assert.NoError(t, err) {
|
||||
|
||||
@@ -101,7 +101,17 @@ func loadClusters(ctx context.Context, kubeClient *kubernetes.Clientset, appClie
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
client := redis.NewClient(&redis.Options{Addr: fmt.Sprintf("localhost:%d", port)})
|
||||
|
||||
redisOptions := &redis.Options{Addr: fmt.Sprintf("localhost:%d", port)}
|
||||
|
||||
secret, err := kubeClient.CoreV1().Secrets(namespace).Get(context.Background(), defaulRedisInitialPasswordSecretName, v1.GetOptions{})
|
||||
if err == nil {
|
||||
if _, ok := secret.Data[defaultResisInitialPasswordKey]; ok {
|
||||
redisOptions.Password = string(secret.Data[defaultResisInitialPasswordKey])
|
||||
}
|
||||
}
|
||||
|
||||
client := redis.NewClient(redisOptions)
|
||||
compressionType, err := cacheutil.CompressionTypeFromString(redisCompressionStr)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
|
||||
98
cmd/argocd/commands/admin/redis_initial_password.go
Normal file
98
cmd/argocd/commands/admin/redis_initial_password.go
Normal file
@@ -0,0 +1,98 @@
|
||||
package admin
|
||||
|
||||
import (
|
||||
"context"
|
||||
"crypto/rand"
|
||||
"fmt"
|
||||
"math/big"
|
||||
|
||||
"github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1"
|
||||
"github.com/argoproj/argo-cd/v2/util/cli"
|
||||
apierr "k8s.io/apimachinery/pkg/api/errors"
|
||||
|
||||
"github.com/argoproj/argo-cd/v2/util/errors"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
v1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/client-go/kubernetes"
|
||||
"k8s.io/client-go/tools/clientcmd"
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
corev1 "k8s.io/api/core/v1"
|
||||
)
|
||||
|
||||
const defaulRedisInitialPasswordSecretName = "argocd-redis"
|
||||
const defaultResisInitialPasswordKey = "auth"
|
||||
|
||||
func generateRandomPassword() (string, error) {
|
||||
const initialPasswordLength = 16
|
||||
const letters = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz-"
|
||||
randBytes := make([]byte, initialPasswordLength)
|
||||
for i := 0; i < initialPasswordLength; i++ {
|
||||
num, err := rand.Int(rand.Reader, big.NewInt(int64(len(letters))))
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
randBytes[i] = letters[num.Int64()]
|
||||
}
|
||||
initialPassword := string(randBytes)
|
||||
return initialPassword, nil
|
||||
}
|
||||
|
||||
// NewRedisInitialPasswordCommand defines a new command to ensure Argo CD Redis password secret exists.
|
||||
func NewRedisInitialPasswordCommand() *cobra.Command {
|
||||
var (
|
||||
clientConfig clientcmd.ClientConfig
|
||||
)
|
||||
var command = cobra.Command{
|
||||
Use: "redis-initial-password",
|
||||
Short: "Ensure the Redis password exists, creating a new one if necessary.",
|
||||
Run: func(c *cobra.Command, args []string) {
|
||||
namespace, _, err := clientConfig.Namespace()
|
||||
errors.CheckError(err)
|
||||
|
||||
redisInitialPasswordSecretName := defaulRedisInitialPasswordSecretName
|
||||
redisInitialPasswordKey := defaultResisInitialPasswordKey
|
||||
fmt.Printf("Checking for initial Redis password in secret %s/%s at key %s. \n", namespace, redisInitialPasswordSecretName, redisInitialPasswordKey)
|
||||
|
||||
config, err := clientConfig.ClientConfig()
|
||||
errors.CheckError(err)
|
||||
errors.CheckError(v1alpha1.SetK8SConfigDefaults(config))
|
||||
|
||||
kubeClientset := kubernetes.NewForConfigOrDie(config)
|
||||
|
||||
randomPassword, err := generateRandomPassword()
|
||||
errors.CheckError(err)
|
||||
|
||||
data := map[string][]byte{
|
||||
redisInitialPasswordKey: []byte(randomPassword),
|
||||
}
|
||||
secret := &corev1.Secret{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: redisInitialPasswordSecretName,
|
||||
Namespace: namespace,
|
||||
},
|
||||
Data: data,
|
||||
Type: corev1.SecretTypeOpaque,
|
||||
}
|
||||
_, err = kubeClientset.CoreV1().Secrets(namespace).Create(context.Background(), secret, metav1.CreateOptions{})
|
||||
if err != nil && !apierr.IsAlreadyExists(err) {
|
||||
errors.CheckError(err)
|
||||
}
|
||||
|
||||
fmt.Println("Argo CD Redis secret state confirmed: secret name argocd-redis.")
|
||||
secret, err = kubeClientset.CoreV1().Secrets(namespace).Get(context.Background(), redisInitialPasswordSecretName, v1.GetOptions{})
|
||||
errors.CheckError(err)
|
||||
|
||||
if _, ok := secret.Data[redisInitialPasswordKey]; ok {
|
||||
fmt.Println("Password secret is configured properly.")
|
||||
} else {
|
||||
err := fmt.Errorf("key %s doesn't exist in secret %s. \n", redisInitialPasswordKey, redisInitialPasswordSecretName)
|
||||
errors.CheckError(err)
|
||||
}
|
||||
},
|
||||
}
|
||||
|
||||
clientConfig = cli.AddKubectlFlagsToCmd(&command)
|
||||
|
||||
return &command
|
||||
}
|
||||
@@ -428,7 +428,7 @@ argocd admin settings resource-overrides ignore-differences ./deploy.yaml --argo
|
||||
// configurations. This requires access to live resources which is not the
|
||||
// purpose of this command. This will just apply jsonPointers and
|
||||
// jqPathExpressions configurations.
|
||||
normalizer, err := normalizers.NewIgnoreNormalizer(nil, overrides)
|
||||
normalizer, err := normalizers.NewIgnoreNormalizer(nil, overrides, normalizers.IgnoreNormalizerOpts{})
|
||||
errors.CheckError(err)
|
||||
|
||||
normalizedRes := res.DeepCopy()
|
||||
@@ -453,6 +453,9 @@ argocd admin settings resource-overrides ignore-differences ./deploy.yaml --argo
|
||||
}
|
||||
|
||||
func NewResourceIgnoreResourceUpdatesCommand(cmdCtx commandContext) *cobra.Command {
|
||||
var (
|
||||
ignoreNormalizerOpts normalizers.IgnoreNormalizerOpts
|
||||
)
|
||||
var command = &cobra.Command{
|
||||
Use: "ignore-resource-updates RESOURCE_YAML_PATH",
|
||||
Short: "Renders fields excluded from resource updates",
|
||||
@@ -474,7 +477,7 @@ argocd admin settings resource-overrides ignore-resource-updates ./deploy.yaml -
|
||||
return
|
||||
}
|
||||
|
||||
normalizer, err := normalizers.NewIgnoreNormalizer(nil, overrides)
|
||||
normalizer, err := normalizers.NewIgnoreNormalizer(nil, overrides, ignoreNormalizerOpts)
|
||||
errors.CheckError(err)
|
||||
|
||||
normalizedRes := res.DeepCopy()
|
||||
@@ -495,6 +498,7 @@ argocd admin settings resource-overrides ignore-resource-updates ./deploy.yaml -
|
||||
})
|
||||
},
|
||||
}
|
||||
command.Flags().DurationVar(&ignoreNormalizerOpts.JQExecutionTimeout, "ignore-normalizer-jq-execution-timeout", normalizers.DefaultJQExecutionTimeout, "Set ignore normalizer JQ execution timeout")
|
||||
return command
|
||||
}
|
||||
|
||||
|
||||
@@ -44,6 +44,7 @@ import (
|
||||
"github.com/argoproj/argo-cd/v2/reposerver/repository"
|
||||
"github.com/argoproj/argo-cd/v2/util/argo"
|
||||
argodiff "github.com/argoproj/argo-cd/v2/util/argo/diff"
|
||||
"github.com/argoproj/argo-cd/v2/util/argo/normalizers"
|
||||
"github.com/argoproj/argo-cd/v2/util/cli"
|
||||
"github.com/argoproj/argo-cd/v2/util/errors"
|
||||
"github.com/argoproj/argo-cd/v2/util/git"
|
||||
@@ -1049,14 +1050,15 @@ type objKeyLiveTarget struct {
|
||||
// NewApplicationDiffCommand returns a new instance of an `argocd app diff` command
|
||||
func NewApplicationDiffCommand(clientOpts *argocdclient.ClientOptions) *cobra.Command {
|
||||
var (
|
||||
refresh bool
|
||||
hardRefresh bool
|
||||
exitCode bool
|
||||
local string
|
||||
revision string
|
||||
localRepoRoot string
|
||||
serverSideGenerate bool
|
||||
localIncludes []string
|
||||
refresh bool
|
||||
hardRefresh bool
|
||||
exitCode bool
|
||||
local string
|
||||
revision string
|
||||
localRepoRoot string
|
||||
serverSideGenerate bool
|
||||
localIncludes []string
|
||||
ignoreNormalizerOpts normalizers.IgnoreNormalizerOpts
|
||||
)
|
||||
shortDesc := "Perform a diff against the target and live state."
|
||||
var command = &cobra.Command{
|
||||
@@ -1116,13 +1118,14 @@ func NewApplicationDiffCommand(clientOpts *argocdclient.ClientOptions) *cobra.Co
|
||||
defer argoio.Close(conn)
|
||||
cluster, err := clusterIf.Get(ctx, &clusterpkg.ClusterQuery{Name: app.Spec.Destination.Name, Server: app.Spec.Destination.Server})
|
||||
errors.CheckError(err)
|
||||
|
||||
diffOption.local = local
|
||||
diffOption.localRepoRoot = localRepoRoot
|
||||
diffOption.cluster = cluster
|
||||
}
|
||||
}
|
||||
proj := getProject(c, clientOpts, ctx, app.Spec.Project)
|
||||
foundDiffs := findandPrintDiff(ctx, app, proj.Project, resources, argoSettings, diffOption)
|
||||
foundDiffs := findandPrintDiff(ctx, app, proj.Project, resources, argoSettings, diffOption, ignoreNormalizerOpts)
|
||||
if foundDiffs && exitCode {
|
||||
os.Exit(1)
|
||||
}
|
||||
@@ -1136,6 +1139,7 @@ func NewApplicationDiffCommand(clientOpts *argocdclient.ClientOptions) *cobra.Co
|
||||
command.Flags().StringVar(&localRepoRoot, "local-repo-root", "/", "Path to the repository root. Used together with --local allows setting the repository root")
|
||||
command.Flags().BoolVar(&serverSideGenerate, "server-side-generate", false, "Used with --local, this will send your manifests to the server for diffing")
|
||||
command.Flags().StringArrayVar(&localIncludes, "local-include", []string{"*.yaml", "*.yml", "*.json"}, "Used with --server-side-generate, specify patterns of filenames to send. Matching is based on filename and not path.")
|
||||
command.Flags().DurationVar(&ignoreNormalizerOpts.JQExecutionTimeout, "ignore-normalizer-jq-execution-timeout", normalizers.DefaultJQExecutionTimeout, "Set ignore normalizer JQ execution timeout")
|
||||
return command
|
||||
}
|
||||
|
||||
@@ -1150,7 +1154,7 @@ type DifferenceOption struct {
|
||||
}
|
||||
|
||||
// findandPrintDiff ... Prints difference between application current state and state stored in git or locally, returns boolean as true if difference is found else returns false
|
||||
func findandPrintDiff(ctx context.Context, app *argoappv1.Application, proj *argoappv1.AppProject, resources *application.ManagedResourcesResponse, argoSettings *settings.Settings, diffOptions *DifferenceOption) bool {
|
||||
func findandPrintDiff(ctx context.Context, app *argoappv1.Application, proj *argoappv1.AppProject, resources *application.ManagedResourcesResponse, argoSettings *settings.Settings, diffOptions *DifferenceOption, ignoreNormalizerOpts normalizers.IgnoreNormalizerOpts) bool {
|
||||
var foundDiffs bool
|
||||
liveObjs, err := cmdutil.LiveObjects(resources.Items)
|
||||
errors.CheckError(err)
|
||||
@@ -1205,7 +1209,7 @@ func findandPrintDiff(ctx context.Context, app *argoappv1.Application, proj *arg
|
||||
// compareOptions in the protobuf
|
||||
ignoreAggregatedRoles := false
|
||||
diffConfig, err := argodiff.NewDiffConfigBuilder().
|
||||
WithDiffSettings(app.Spec.IgnoreDifferences, overrides, ignoreAggregatedRoles).
|
||||
WithDiffSettings(app.Spec.IgnoreDifferences, overrides, ignoreAggregatedRoles, ignoreNormalizerOpts).
|
||||
WithTracking(argoSettings.AppLabelKey, argoSettings.TrackingMethod).
|
||||
WithNoCache().
|
||||
Build()
|
||||
@@ -1698,6 +1702,7 @@ func NewApplicationSyncCommand(clientOpts *argocdclient.ClientOptions) *cobra.Co
|
||||
diffChangesConfirm bool
|
||||
projects []string
|
||||
output string
|
||||
ignoreNormalizerOpts normalizers.IgnoreNormalizerOpts
|
||||
)
|
||||
var command = &cobra.Command{
|
||||
Use: "sync [APPNAME... | -l selector | --project project-name]",
|
||||
@@ -1922,7 +1927,7 @@ func NewApplicationSyncCommand(clientOpts *argocdclient.ClientOptions) *cobra.Co
|
||||
fmt.Printf("====== Previewing differences between live and desired state of application %s ======\n", appQualifiedName)
|
||||
|
||||
proj := getProject(c, clientOpts, ctx, app.Spec.Project)
|
||||
foundDiffs = findandPrintDiff(ctx, app, proj.Project, resources, argoSettings, diffOption)
|
||||
foundDiffs = findandPrintDiff(ctx, app, proj.Project, resources, argoSettings, diffOption, ignoreNormalizerOpts)
|
||||
if foundDiffs {
|
||||
if !diffChangesConfirm {
|
||||
yesno := cli.AskToProceed(fmt.Sprintf("Please review changes to application %s shown above. Do you want to continue the sync process? (y/n): ", appQualifiedName))
|
||||
@@ -1980,6 +1985,7 @@ func NewApplicationSyncCommand(clientOpts *argocdclient.ClientOptions) *cobra.Co
|
||||
command.Flags().BoolVar(&diffChanges, "preview-changes", false, "Preview difference against the target and live state before syncing app and wait for user confirmation")
|
||||
command.Flags().StringArrayVar(&projects, "project", []string{}, "Sync apps that belong to the specified projects. This option may be specified repeatedly.")
|
||||
command.Flags().StringVarP(&output, "output", "o", "wide", "Output format. One of: json|yaml|wide|tree|tree=detailed")
|
||||
command.Flags().DurationVar(&ignoreNormalizerOpts.JQExecutionTimeout, "ignore-normalizer-jq-execution-timeout", normalizers.DefaultJQExecutionTimeout, "Set ignore normalizer JQ execution timeout")
|
||||
return command
|
||||
}
|
||||
|
||||
|
||||
@@ -46,13 +46,13 @@ func NewServer(initConstants plugin.CMPServerInitConstants) (*ArgoCDCMPServer, e
|
||||
|
||||
serverLog := log.NewEntry(log.StandardLogger())
|
||||
streamInterceptors := []grpc.StreamServerInterceptor{
|
||||
otelgrpc.StreamServerInterceptor(),
|
||||
otelgrpc.StreamServerInterceptor(), //nolint:staticcheck // TODO: ignore SA1019 for depreciation: see https://github.com/argoproj/argo-cd/issues/18258
|
||||
grpc_logrus.StreamServerInterceptor(serverLog),
|
||||
grpc_prometheus.StreamServerInterceptor,
|
||||
grpc_util.PanicLoggerStreamServerInterceptor(serverLog),
|
||||
}
|
||||
unaryInterceptors := []grpc.UnaryServerInterceptor{
|
||||
otelgrpc.UnaryServerInterceptor(),
|
||||
otelgrpc.UnaryServerInterceptor(), //nolint:staticcheck // TODO: ignore SA1019 for depreciation: see https://github.com/argoproj/argo-cd/issues/18258
|
||||
grpc_logrus.UnaryServerInterceptor(serverLog),
|
||||
grpc_prometheus.UnaryServerInterceptor,
|
||||
grpc_util.PanicLoggerUnaryServerInterceptor(serverLog),
|
||||
|
||||
@@ -55,6 +55,7 @@ import (
|
||||
"github.com/argoproj/argo-cd/v2/reposerver/apiclient"
|
||||
"github.com/argoproj/argo-cd/v2/util/argo"
|
||||
argodiff "github.com/argoproj/argo-cd/v2/util/argo/diff"
|
||||
"github.com/argoproj/argo-cd/v2/util/argo/normalizers"
|
||||
"github.com/argoproj/argo-cd/v2/util/env"
|
||||
|
||||
kubeerrors "k8s.io/apimachinery/pkg/api/errors"
|
||||
@@ -114,7 +115,6 @@ type ApplicationController struct {
|
||||
appInformer cache.SharedIndexInformer
|
||||
appLister applisters.ApplicationLister
|
||||
projInformer cache.SharedIndexInformer
|
||||
deploymentInformer informerv1.DeploymentInformer
|
||||
appStateManager AppStateManager
|
||||
stateCache statecache.LiveStateCache
|
||||
statusRefreshTimeout time.Duration
|
||||
@@ -131,6 +131,11 @@ type ApplicationController struct {
|
||||
clusterSharding sharding.ClusterShardingCache
|
||||
projByNameCache sync.Map
|
||||
applicationNamespaces []string
|
||||
ignoreNormalizerOpts normalizers.IgnoreNormalizerOpts
|
||||
|
||||
// dynamicClusterDistributionEnabled if disabled deploymentInformer is never initialized
|
||||
dynamicClusterDistributionEnabled bool
|
||||
deploymentInformer informerv1.DeploymentInformer
|
||||
}
|
||||
|
||||
// NewApplicationController creates new instance of ApplicationController.
|
||||
@@ -156,6 +161,8 @@ func NewApplicationController(
|
||||
applicationNamespaces []string,
|
||||
rateLimiterConfig *ratelimiter.AppControllerRateLimiterConfig,
|
||||
serverSideDiff bool,
|
||||
dynamicClusterDistributionEnabled bool,
|
||||
ignoreNormalizerOpts normalizers.IgnoreNormalizerOpts,
|
||||
) (*ApplicationController, error) {
|
||||
log.Infof("appResyncPeriod=%v, appHardResyncPeriod=%v, appResyncJitter=%v", appResyncPeriod, appHardResyncPeriod, appResyncJitter)
|
||||
db := db.NewDB(namespace, settingsMgr, kubeClientset)
|
||||
@@ -164,28 +171,30 @@ func NewApplicationController(
|
||||
log.Info("Using default workqueue rate limiter config")
|
||||
}
|
||||
ctrl := ApplicationController{
|
||||
cache: argoCache,
|
||||
namespace: namespace,
|
||||
kubeClientset: kubeClientset,
|
||||
kubectl: kubectl,
|
||||
applicationClientset: applicationClientset,
|
||||
repoClientset: repoClientset,
|
||||
appRefreshQueue: workqueue.NewNamedRateLimitingQueue(ratelimiter.NewCustomAppControllerRateLimiter(rateLimiterConfig), "app_reconciliation_queue"),
|
||||
appOperationQueue: workqueue.NewNamedRateLimitingQueue(ratelimiter.NewCustomAppControllerRateLimiter(rateLimiterConfig), "app_operation_processing_queue"),
|
||||
projectRefreshQueue: workqueue.NewNamedRateLimitingQueue(ratelimiter.NewCustomAppControllerRateLimiter(rateLimiterConfig), "project_reconciliation_queue"),
|
||||
appComparisonTypeRefreshQueue: workqueue.NewRateLimitingQueue(ratelimiter.NewCustomAppControllerRateLimiter(rateLimiterConfig)),
|
||||
db: db,
|
||||
statusRefreshTimeout: appResyncPeriod,
|
||||
statusHardRefreshTimeout: appHardResyncPeriod,
|
||||
statusRefreshJitter: appResyncJitter,
|
||||
refreshRequestedApps: make(map[string]CompareWith),
|
||||
refreshRequestedAppsMutex: &sync.Mutex{},
|
||||
auditLogger: argo.NewAuditLogger(namespace, kubeClientset, common.ApplicationController),
|
||||
settingsMgr: settingsMgr,
|
||||
selfHealTimeout: selfHealTimeout,
|
||||
clusterSharding: clusterSharding,
|
||||
projByNameCache: sync.Map{},
|
||||
applicationNamespaces: applicationNamespaces,
|
||||
cache: argoCache,
|
||||
namespace: namespace,
|
||||
kubeClientset: kubeClientset,
|
||||
kubectl: kubectl,
|
||||
applicationClientset: applicationClientset,
|
||||
repoClientset: repoClientset,
|
||||
appRefreshQueue: workqueue.NewNamedRateLimitingQueue(ratelimiter.NewCustomAppControllerRateLimiter(rateLimiterConfig), "app_reconciliation_queue"),
|
||||
appOperationQueue: workqueue.NewNamedRateLimitingQueue(ratelimiter.NewCustomAppControllerRateLimiter(rateLimiterConfig), "app_operation_processing_queue"),
|
||||
projectRefreshQueue: workqueue.NewNamedRateLimitingQueue(ratelimiter.NewCustomAppControllerRateLimiter(rateLimiterConfig), "project_reconciliation_queue"),
|
||||
appComparisonTypeRefreshQueue: workqueue.NewRateLimitingQueue(ratelimiter.NewCustomAppControllerRateLimiter(rateLimiterConfig)),
|
||||
db: db,
|
||||
statusRefreshTimeout: appResyncPeriod,
|
||||
statusHardRefreshTimeout: appHardResyncPeriod,
|
||||
statusRefreshJitter: appResyncJitter,
|
||||
refreshRequestedApps: make(map[string]CompareWith),
|
||||
refreshRequestedAppsMutex: &sync.Mutex{},
|
||||
auditLogger: argo.NewAuditLogger(namespace, kubeClientset, common.ApplicationController),
|
||||
settingsMgr: settingsMgr,
|
||||
selfHealTimeout: selfHealTimeout,
|
||||
clusterSharding: clusterSharding,
|
||||
projByNameCache: sync.Map{},
|
||||
applicationNamespaces: applicationNamespaces,
|
||||
dynamicClusterDistributionEnabled: dynamicClusterDistributionEnabled,
|
||||
ignoreNormalizerOpts: ignoreNormalizerOpts,
|
||||
}
|
||||
if kubectlParallelismLimit > 0 {
|
||||
ctrl.kubectlSemaphore = semaphore.NewWeighted(kubectlParallelismLimit)
|
||||
@@ -228,25 +237,33 @@ func NewApplicationController(
|
||||
}
|
||||
|
||||
factory := informers.NewSharedInformerFactoryWithOptions(ctrl.kubeClientset, defaultDeploymentInformerResyncDuration, informers.WithNamespace(settingsMgr.GetNamespace()))
|
||||
deploymentInformer := factory.Apps().V1().Deployments()
|
||||
|
||||
var deploymentInformer informerv1.DeploymentInformer
|
||||
|
||||
// only initialize deployment informer if dynamic distribution is enabled
|
||||
if dynamicClusterDistributionEnabled {
|
||||
deploymentInformer = factory.Apps().V1().Deployments()
|
||||
}
|
||||
|
||||
readinessHealthCheck := func(r *http.Request) error {
|
||||
applicationControllerName := env.StringFromEnv(common.EnvAppControllerName, common.DefaultApplicationControllerName)
|
||||
appControllerDeployment, err := deploymentInformer.Lister().Deployments(settingsMgr.GetNamespace()).Get(applicationControllerName)
|
||||
if err != nil {
|
||||
if kubeerrors.IsNotFound(err) {
|
||||
appControllerDeployment = nil
|
||||
} else {
|
||||
return fmt.Errorf("error retrieving Application Controller Deployment: %s", err)
|
||||
if dynamicClusterDistributionEnabled {
|
||||
applicationControllerName := env.StringFromEnv(common.EnvAppControllerName, common.DefaultApplicationControllerName)
|
||||
appControllerDeployment, err := deploymentInformer.Lister().Deployments(settingsMgr.GetNamespace()).Get(applicationControllerName)
|
||||
if err != nil {
|
||||
if kubeerrors.IsNotFound(err) {
|
||||
appControllerDeployment = nil
|
||||
} else {
|
||||
return fmt.Errorf("error retrieving Application Controller Deployment: %s", err)
|
||||
}
|
||||
}
|
||||
}
|
||||
if appControllerDeployment != nil {
|
||||
if appControllerDeployment.Spec.Replicas != nil && int(*appControllerDeployment.Spec.Replicas) <= 0 {
|
||||
return fmt.Errorf("application controller deployment replicas is not set or is less than 0, replicas: %d", appControllerDeployment.Spec.Replicas)
|
||||
}
|
||||
shard := env.ParseNumFromEnv(common.EnvControllerShard, -1, -math.MaxInt32, math.MaxInt32)
|
||||
if _, err := sharding.GetOrUpdateShardFromConfigMap(kubeClientset.(*kubernetes.Clientset), settingsMgr, int(*appControllerDeployment.Spec.Replicas), shard); err != nil {
|
||||
return fmt.Errorf("error while updating the heartbeat for to the Shard Mapping ConfigMap: %s", err)
|
||||
if appControllerDeployment != nil {
|
||||
if appControllerDeployment.Spec.Replicas != nil && int(*appControllerDeployment.Spec.Replicas) <= 0 {
|
||||
return fmt.Errorf("application controller deployment replicas is not set or is less than 0, replicas: %d", appControllerDeployment.Spec.Replicas)
|
||||
}
|
||||
shard := env.ParseNumFromEnv(common.EnvControllerShard, -1, -math.MaxInt32, math.MaxInt32)
|
||||
if _, err := sharding.GetOrUpdateShardFromConfigMap(kubeClientset.(*kubernetes.Clientset), settingsMgr, int(*appControllerDeployment.Spec.Replicas), shard); err != nil {
|
||||
return fmt.Errorf("error while updating the heartbeat for to the Shard Mapping ConfigMap: %s", err)
|
||||
}
|
||||
}
|
||||
}
|
||||
return nil
|
||||
@@ -265,7 +282,7 @@ func NewApplicationController(
|
||||
}
|
||||
}
|
||||
stateCache := statecache.NewLiveStateCache(db, appInformer, ctrl.settingsMgr, kubectl, ctrl.metricsServer, ctrl.handleObjectUpdated, clusterSharding, argo.NewResourceTracking())
|
||||
appStateManager := NewAppStateManager(db, applicationClientset, repoClientset, namespace, kubectl, ctrl.settingsMgr, stateCache, projInformer, ctrl.metricsServer, argoCache, ctrl.statusRefreshTimeout, argo.NewResourceTracking(), persistResourceHealth, repoErrorGracePeriod, serverSideDiff)
|
||||
appStateManager := NewAppStateManager(db, applicationClientset, repoClientset, namespace, kubectl, ctrl.settingsMgr, stateCache, projInformer, ctrl.metricsServer, argoCache, ctrl.statusRefreshTimeout, argo.NewResourceTracking(), persistResourceHealth, repoErrorGracePeriod, serverSideDiff, ignoreNormalizerOpts)
|
||||
ctrl.appInformer = appInformer
|
||||
ctrl.appLister = appLister
|
||||
ctrl.projInformer = projInformer
|
||||
@@ -716,7 +733,7 @@ func (ctrl *ApplicationController) hideSecretData(app *appv1.Application, compar
|
||||
return nil, fmt.Errorf("error getting cluster cache: %s", err)
|
||||
}
|
||||
diffConfig, err := argodiff.NewDiffConfigBuilder().
|
||||
WithDiffSettings(app.Spec.IgnoreDifferences, resourceOverrides, compareOptions.IgnoreAggregatedRoles).
|
||||
WithDiffSettings(app.Spec.IgnoreDifferences, resourceOverrides, compareOptions.IgnoreAggregatedRoles, ctrl.ignoreNormalizerOpts).
|
||||
WithTracking(appLabelKey, trackingMethod).
|
||||
WithNoCache().
|
||||
WithLogger(logutils.NewLogrusLogger(logutils.NewWithCurrentConfig())).
|
||||
@@ -774,7 +791,11 @@ func (ctrl *ApplicationController) Run(ctx context.Context, statusProcessors int
|
||||
|
||||
go ctrl.appInformer.Run(ctx.Done())
|
||||
go ctrl.projInformer.Run(ctx.Done())
|
||||
go ctrl.deploymentInformer.Informer().Run(ctx.Done())
|
||||
|
||||
if ctrl.dynamicClusterDistributionEnabled {
|
||||
// only start deployment informer if dynamic distribution is enabled
|
||||
go ctrl.deploymentInformer.Informer().Run(ctx.Done())
|
||||
}
|
||||
|
||||
clusters, err := ctrl.db.ListClusters(ctx)
|
||||
if err != nil {
|
||||
@@ -1737,6 +1758,22 @@ func (ctrl *ApplicationController) normalizeApplication(orig, app *appv1.Applica
|
||||
}
|
||||
}
|
||||
|
||||
func createMergePatch(orig, new interface{}) ([]byte, bool, error) {
|
||||
origBytes, err := json.Marshal(orig)
|
||||
if err != nil {
|
||||
return nil, false, err
|
||||
}
|
||||
newBytes, err := json.Marshal(new)
|
||||
if err != nil {
|
||||
return nil, false, err
|
||||
}
|
||||
patch, err := jsonpatch.CreateMergePatch(origBytes, newBytes)
|
||||
if err != nil {
|
||||
return nil, false, err
|
||||
}
|
||||
return patch, string(patch) != "{}", nil
|
||||
}
|
||||
|
||||
// persistAppStatus persists updates to application status. If no changes were made, it is a no-op
|
||||
func (ctrl *ApplicationController) persistAppStatus(orig *appv1.Application, newStatus *appv1.ApplicationStatus) (patchMs time.Duration) {
|
||||
logCtx := log.WithFields(log.Fields{"application": orig.QualifiedName()})
|
||||
@@ -1756,9 +1793,9 @@ func (ctrl *ApplicationController) persistAppStatus(orig *appv1.Application, new
|
||||
}
|
||||
delete(newAnnotations, appv1.AnnotationKeyRefresh)
|
||||
}
|
||||
patch, modified, err := diff.CreateTwoWayMergePatch(
|
||||
patch, modified, err := createMergePatch(
|
||||
&appv1.Application{ObjectMeta: metav1.ObjectMeta{Annotations: orig.GetAnnotations()}, Status: orig.Status},
|
||||
&appv1.Application{ObjectMeta: metav1.ObjectMeta{Annotations: newAnnotations}, Status: *newStatus}, appv1.Application{})
|
||||
&appv1.Application{ObjectMeta: metav1.ObjectMeta{Annotations: newAnnotations}, Status: *newStatus})
|
||||
if err != nil {
|
||||
logCtx.Errorf("Error constructing app status patch: %v", err)
|
||||
return
|
||||
|
||||
@@ -42,6 +42,7 @@ import (
|
||||
"github.com/argoproj/argo-cd/v2/reposerver/apiclient"
|
||||
mockrepoclient "github.com/argoproj/argo-cd/v2/reposerver/apiclient/mocks"
|
||||
"github.com/argoproj/argo-cd/v2/test"
|
||||
"github.com/argoproj/argo-cd/v2/util/argo/normalizers"
|
||||
cacheutil "github.com/argoproj/argo-cd/v2/util/cache"
|
||||
appstatecache "github.com/argoproj/argo-cd/v2/util/cache/appstate"
|
||||
"github.com/argoproj/argo-cd/v2/util/settings"
|
||||
@@ -155,8 +156,9 @@ func newFakeController(data *fakeData, repoErr error) *ApplicationController {
|
||||
nil,
|
||||
data.applicationNamespaces,
|
||||
nil,
|
||||
|
||||
false,
|
||||
false,
|
||||
normalizers.IgnoreNormalizerOpts{},
|
||||
)
|
||||
db := &dbmocks.ArgoDB{}
|
||||
db.On("GetApplicationControllerReplicas").Return(1)
|
||||
@@ -1006,7 +1008,7 @@ func TestNormalizeApplication(t *testing.T) {
|
||||
normalized := false
|
||||
fakeAppCs.AddReactor("patch", "*", func(action kubetesting.Action) (handled bool, ret runtime.Object, err error) {
|
||||
if patchAction, ok := action.(kubetesting.PatchAction); ok {
|
||||
if string(patchAction.GetPatch()) == `{"spec":{"project":"default"}}` {
|
||||
if string(patchAction.GetPatch()) == `{"spec":{"project":"default"},"status":{"sync":{"comparedTo":{"destination":{},"source":{"repoURL":""}}}}}` {
|
||||
normalized = true
|
||||
}
|
||||
}
|
||||
@@ -1909,3 +1911,65 @@ func TestAddControllerNamespace(t *testing.T) {
|
||||
assert.Equal(t, test.FakeArgoCDNamespace, updatedApp.Status.ControllerNamespace)
|
||||
})
|
||||
}
|
||||
|
||||
func TestHelmValuesObjectHasReplaceStrategy(t *testing.T) {
|
||||
app := v1alpha1.Application{
|
||||
Status: v1alpha1.ApplicationStatus{Sync: v1alpha1.SyncStatus{ComparedTo: v1alpha1.ComparedTo{
|
||||
Source: v1alpha1.ApplicationSource{
|
||||
Helm: &v1alpha1.ApplicationSourceHelm{
|
||||
ValuesObject: &runtime.RawExtension{
|
||||
Object: &unstructured.Unstructured{Object: map[string]interface{}{"key": []string{"value"}}},
|
||||
},
|
||||
},
|
||||
},
|
||||
}}},
|
||||
}
|
||||
|
||||
appModified := v1alpha1.Application{
|
||||
Status: v1alpha1.ApplicationStatus{Sync: v1alpha1.SyncStatus{ComparedTo: v1alpha1.ComparedTo{
|
||||
Source: v1alpha1.ApplicationSource{
|
||||
Helm: &v1alpha1.ApplicationSourceHelm{
|
||||
ValuesObject: &runtime.RawExtension{
|
||||
Object: &unstructured.Unstructured{Object: map[string]interface{}{"key": []string{"value-modified1"}}},
|
||||
},
|
||||
},
|
||||
},
|
||||
}}},
|
||||
}
|
||||
|
||||
patch, _, err := createMergePatch(
|
||||
app,
|
||||
appModified)
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, `{"status":{"sync":{"comparedTo":{"source":{"helm":{"valuesObject":{"key":["value-modified1"]}}}}}}}`, string(patch))
|
||||
}
|
||||
|
||||
func TestAppStatusIsReplaced(t *testing.T) {
|
||||
original := &v1alpha1.ApplicationStatus{Sync: v1alpha1.SyncStatus{
|
||||
ComparedTo: v1alpha1.ComparedTo{
|
||||
Destination: v1alpha1.ApplicationDestination{
|
||||
Server: "https://mycluster",
|
||||
},
|
||||
},
|
||||
}}
|
||||
|
||||
updated := &v1alpha1.ApplicationStatus{Sync: v1alpha1.SyncStatus{
|
||||
ComparedTo: v1alpha1.ComparedTo{
|
||||
Destination: v1alpha1.ApplicationDestination{
|
||||
Name: "mycluster",
|
||||
},
|
||||
},
|
||||
}}
|
||||
|
||||
patchData, ok, err := createMergePatch(original, updated)
|
||||
|
||||
require.NoError(t, err)
|
||||
require.True(t, ok)
|
||||
patchObj := map[string]interface{}{}
|
||||
require.NoError(t, json.Unmarshal(patchData, &patchObj))
|
||||
|
||||
val, has, err := unstructured.NestedFieldNoCopy(patchObj, "sync", "comparedTo", "destination", "server")
|
||||
require.NoError(t, err)
|
||||
require.True(t, has)
|
||||
require.Nil(t, val)
|
||||
}
|
||||
|
||||
43
controller/cache/cache.go
vendored
43
controller/cache/cache.go
vendored
@@ -33,6 +33,7 @@ import (
|
||||
"github.com/argoproj/argo-cd/v2/pkg/apis/application"
|
||||
appv1 "github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1"
|
||||
"github.com/argoproj/argo-cd/v2/util/argo"
|
||||
"github.com/argoproj/argo-cd/v2/util/argo/normalizers"
|
||||
"github.com/argoproj/argo-cd/v2/util/db"
|
||||
"github.com/argoproj/argo-cd/v2/util/env"
|
||||
logutils "github.com/argoproj/argo-cd/v2/util/log"
|
||||
@@ -197,14 +198,15 @@ type cacheSettings struct {
|
||||
}
|
||||
|
||||
type liveStateCache struct {
|
||||
db db.ArgoDB
|
||||
appInformer cache.SharedIndexInformer
|
||||
onObjectUpdated ObjectUpdatedHandler
|
||||
kubectl kube.Kubectl
|
||||
settingsMgr *settings.SettingsManager
|
||||
metricsServer *metrics.MetricsServer
|
||||
clusterSharding sharding.ClusterShardingCache
|
||||
resourceTracking argo.ResourceTracking
|
||||
db db.ArgoDB
|
||||
appInformer cache.SharedIndexInformer
|
||||
onObjectUpdated ObjectUpdatedHandler
|
||||
kubectl kube.Kubectl
|
||||
settingsMgr *settings.SettingsManager
|
||||
metricsServer *metrics.MetricsServer
|
||||
clusterSharding sharding.ClusterShardingCache
|
||||
resourceTracking argo.ResourceTracking
|
||||
ignoreNormalizerOpts normalizers.IgnoreNormalizerOpts
|
||||
|
||||
clusters map[string]clustercache.ClusterCache
|
||||
cacheSettings cacheSettings
|
||||
@@ -288,7 +290,8 @@ func isRootAppNode(r *clustercache.Resource) bool {
|
||||
}
|
||||
|
||||
func getApp(r *clustercache.Resource, ns map[kube.ResourceKey]*clustercache.Resource) string {
|
||||
return getAppRecursive(r, ns, map[kube.ResourceKey]bool{})
|
||||
name, _ := getAppRecursive(r, ns, map[kube.ResourceKey]bool{})
|
||||
return name
|
||||
}
|
||||
|
||||
func ownerRefGV(ownerRef metav1.OwnerReference) schema.GroupVersion {
|
||||
@@ -299,27 +302,31 @@ func ownerRefGV(ownerRef metav1.OwnerReference) schema.GroupVersion {
|
||||
return gv
|
||||
}
|
||||
|
||||
func getAppRecursive(r *clustercache.Resource, ns map[kube.ResourceKey]*clustercache.Resource, visited map[kube.ResourceKey]bool) string {
|
||||
func getAppRecursive(r *clustercache.Resource, ns map[kube.ResourceKey]*clustercache.Resource, visited map[kube.ResourceKey]bool) (string, bool) {
|
||||
if !visited[r.ResourceKey()] {
|
||||
visited[r.ResourceKey()] = true
|
||||
} else {
|
||||
log.Warnf("Circular dependency detected: %v.", visited)
|
||||
return resInfo(r).AppName
|
||||
return resInfo(r).AppName, false
|
||||
}
|
||||
|
||||
if resInfo(r).AppName != "" {
|
||||
return resInfo(r).AppName
|
||||
return resInfo(r).AppName, true
|
||||
}
|
||||
for _, ownerRef := range r.OwnerRefs {
|
||||
gv := ownerRefGV(ownerRef)
|
||||
if parent, ok := ns[kube.NewResourceKey(gv.Group, ownerRef.Kind, r.Ref.Namespace, ownerRef.Name)]; ok {
|
||||
app := getAppRecursive(parent, ns, visited)
|
||||
if app != "" {
|
||||
return app
|
||||
visited_branch := make(map[kube.ResourceKey]bool, len(visited))
|
||||
for k, v := range visited {
|
||||
visited_branch[k] = v
|
||||
}
|
||||
app, ok := getAppRecursive(parent, ns, visited_branch)
|
||||
if app != "" || !ok {
|
||||
return app, ok
|
||||
}
|
||||
}
|
||||
}
|
||||
return ""
|
||||
return "", true
|
||||
}
|
||||
|
||||
var (
|
||||
@@ -487,7 +494,7 @@ func (c *liveStateCache) getCluster(server string) (clustercache.ClusterCache, e
|
||||
gvk := un.GroupVersionKind()
|
||||
|
||||
if cacheSettings.ignoreResourceUpdatesEnabled && shouldHashManifest(appName, gvk) {
|
||||
hash, err := generateManifestHash(un, nil, cacheSettings.resourceOverrides)
|
||||
hash, err := generateManifestHash(un, nil, cacheSettings.resourceOverrides, c.ignoreNormalizerOpts)
|
||||
if err != nil {
|
||||
log.Errorf("Failed to generate manifest hash: %v", err)
|
||||
} else {
|
||||
@@ -751,7 +758,7 @@ func (c *liveStateCache) handleAddEvent(cluster *appv1.Cluster) {
|
||||
}
|
||||
|
||||
func (c *liveStateCache) handleModEvent(oldCluster *appv1.Cluster, newCluster *appv1.Cluster) {
|
||||
c.clusterSharding.Update(newCluster)
|
||||
c.clusterSharding.Update(oldCluster, newCluster)
|
||||
c.lock.Lock()
|
||||
cluster, ok := c.clusters[newCluster.Server]
|
||||
c.lock.Unlock()
|
||||
|
||||
211
controller/cache/cache_test.go
vendored
211
controller/cache/cache_test.go
vendored
@@ -18,6 +18,7 @@ import (
|
||||
"github.com/argoproj/gitops-engine/pkg/cache"
|
||||
"github.com/argoproj/gitops-engine/pkg/cache/mocks"
|
||||
"github.com/argoproj/gitops-engine/pkg/health"
|
||||
"github.com/argoproj/gitops-engine/pkg/utils/kube"
|
||||
"github.com/stretchr/testify/mock"
|
||||
"k8s.io/client-go/kubernetes/fake"
|
||||
|
||||
@@ -319,6 +320,216 @@ func Test_asResourceNode_owner_refs(t *testing.T) {
|
||||
assert.Equal(t, expected, resNode)
|
||||
}
|
||||
|
||||
func Test_getAppRecursive(t *testing.T) {
|
||||
for _, tt := range []struct {
|
||||
name string
|
||||
r *cache.Resource
|
||||
ns map[kube.ResourceKey]*cache.Resource
|
||||
wantName string
|
||||
wantOK assert.BoolAssertionFunc
|
||||
}{
|
||||
{
|
||||
name: "ok: cm1->app1",
|
||||
r: &cache.Resource{
|
||||
Ref: v1.ObjectReference{
|
||||
Name: "cm1",
|
||||
},
|
||||
OwnerRefs: []metav1.OwnerReference{
|
||||
{Name: "app1"},
|
||||
},
|
||||
},
|
||||
ns: map[kube.ResourceKey]*cache.Resource{
|
||||
kube.NewResourceKey("", "", "", "app1"): {
|
||||
Info: &ResourceInfo{
|
||||
AppName: "app1",
|
||||
},
|
||||
},
|
||||
},
|
||||
wantName: "app1",
|
||||
wantOK: assert.True,
|
||||
},
|
||||
{
|
||||
name: "ok: cm1->cm2->app1",
|
||||
r: &cache.Resource{
|
||||
Ref: v1.ObjectReference{
|
||||
Name: "cm1",
|
||||
},
|
||||
OwnerRefs: []metav1.OwnerReference{
|
||||
{Name: "cm2"},
|
||||
},
|
||||
},
|
||||
ns: map[kube.ResourceKey]*cache.Resource{
|
||||
kube.NewResourceKey("", "", "", "cm2"): {
|
||||
Ref: v1.ObjectReference{
|
||||
Name: "cm2",
|
||||
},
|
||||
OwnerRefs: []metav1.OwnerReference{
|
||||
{Name: "app1"},
|
||||
},
|
||||
},
|
||||
kube.NewResourceKey("", "", "", "app1"): {
|
||||
Info: &ResourceInfo{
|
||||
AppName: "app1",
|
||||
},
|
||||
},
|
||||
},
|
||||
wantName: "app1",
|
||||
wantOK: assert.True,
|
||||
},
|
||||
{
|
||||
name: "cm1->cm2->app1 & cm1->cm3->app1",
|
||||
r: &cache.Resource{
|
||||
Ref: v1.ObjectReference{
|
||||
Name: "cm1",
|
||||
},
|
||||
OwnerRefs: []metav1.OwnerReference{
|
||||
{Name: "cm2"},
|
||||
{Name: "cm3"},
|
||||
},
|
||||
},
|
||||
ns: map[kube.ResourceKey]*cache.Resource{
|
||||
kube.NewResourceKey("", "", "", "cm2"): {
|
||||
Ref: v1.ObjectReference{
|
||||
Name: "cm2",
|
||||
},
|
||||
OwnerRefs: []metav1.OwnerReference{
|
||||
{Name: "app1"},
|
||||
},
|
||||
},
|
||||
kube.NewResourceKey("", "", "", "cm3"): {
|
||||
Ref: v1.ObjectReference{
|
||||
Name: "cm3",
|
||||
},
|
||||
OwnerRefs: []metav1.OwnerReference{
|
||||
{Name: "app1"},
|
||||
},
|
||||
},
|
||||
kube.NewResourceKey("", "", "", "app1"): {
|
||||
Info: &ResourceInfo{
|
||||
AppName: "app1",
|
||||
},
|
||||
},
|
||||
},
|
||||
wantName: "app1",
|
||||
wantOK: assert.True,
|
||||
},
|
||||
{
|
||||
// Nothing cycle.
|
||||
// Issue #11699, fixed #12667.
|
||||
name: "ok: cm1->cm2 & cm1->cm3->cm2 & cm1->cm3->app1",
|
||||
r: &cache.Resource{
|
||||
Ref: v1.ObjectReference{
|
||||
Name: "cm1",
|
||||
},
|
||||
OwnerRefs: []metav1.OwnerReference{
|
||||
{Name: "cm2"},
|
||||
{Name: "cm3"},
|
||||
},
|
||||
},
|
||||
ns: map[kube.ResourceKey]*cache.Resource{
|
||||
kube.NewResourceKey("", "", "", "cm2"): {
|
||||
Ref: v1.ObjectReference{
|
||||
Name: "cm2",
|
||||
},
|
||||
},
|
||||
kube.NewResourceKey("", "", "", "cm3"): {
|
||||
Ref: v1.ObjectReference{
|
||||
Name: "cm3",
|
||||
},
|
||||
OwnerRefs: []metav1.OwnerReference{
|
||||
{Name: "cm2"},
|
||||
{Name: "app1"},
|
||||
},
|
||||
},
|
||||
kube.NewResourceKey("", "", "", "app1"): {
|
||||
Info: &ResourceInfo{
|
||||
AppName: "app1",
|
||||
},
|
||||
},
|
||||
},
|
||||
wantName: "app1",
|
||||
wantOK: assert.True,
|
||||
},
|
||||
{
|
||||
name: "cycle: cm1<->cm2",
|
||||
r: &cache.Resource{
|
||||
Ref: v1.ObjectReference{
|
||||
Name: "cm1",
|
||||
},
|
||||
OwnerRefs: []metav1.OwnerReference{
|
||||
{Name: "cm2"},
|
||||
},
|
||||
},
|
||||
ns: map[kube.ResourceKey]*cache.Resource{
|
||||
kube.NewResourceKey("", "", "", "cm1"): {
|
||||
Ref: v1.ObjectReference{
|
||||
Name: "cm1",
|
||||
},
|
||||
OwnerRefs: []metav1.OwnerReference{
|
||||
{Name: "cm2"},
|
||||
},
|
||||
},
|
||||
kube.NewResourceKey("", "", "", "cm2"): {
|
||||
Ref: v1.ObjectReference{
|
||||
Name: "cm2",
|
||||
},
|
||||
OwnerRefs: []metav1.OwnerReference{
|
||||
{Name: "cm1"},
|
||||
},
|
||||
},
|
||||
},
|
||||
wantName: "",
|
||||
wantOK: assert.False,
|
||||
},
|
||||
{
|
||||
name: "cycle: cm1->cm2->cm3->cm1",
|
||||
r: &cache.Resource{
|
||||
Ref: v1.ObjectReference{
|
||||
Name: "cm1",
|
||||
},
|
||||
OwnerRefs: []metav1.OwnerReference{
|
||||
{Name: "cm2"},
|
||||
},
|
||||
},
|
||||
ns: map[kube.ResourceKey]*cache.Resource{
|
||||
kube.NewResourceKey("", "", "", "cm1"): {
|
||||
Ref: v1.ObjectReference{
|
||||
Name: "cm1",
|
||||
},
|
||||
OwnerRefs: []metav1.OwnerReference{
|
||||
{Name: "cm2"},
|
||||
},
|
||||
},
|
||||
kube.NewResourceKey("", "", "", "cm2"): {
|
||||
Ref: v1.ObjectReference{
|
||||
Name: "cm2",
|
||||
},
|
||||
OwnerRefs: []metav1.OwnerReference{
|
||||
{Name: "cm3"},
|
||||
},
|
||||
},
|
||||
kube.NewResourceKey("", "", "", "cm3"): {
|
||||
Ref: v1.ObjectReference{
|
||||
Name: "cm3",
|
||||
},
|
||||
OwnerRefs: []metav1.OwnerReference{
|
||||
{Name: "cm1"},
|
||||
},
|
||||
},
|
||||
},
|
||||
wantName: "",
|
||||
wantOK: assert.False,
|
||||
},
|
||||
} {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
visited := map[kube.ResourceKey]bool{}
|
||||
got, ok := getAppRecursive(tt.r, tt.ns, visited)
|
||||
assert.Equal(t, tt.wantName, got)
|
||||
tt.wantOK(t, ok)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestSkipResourceUpdate(t *testing.T) {
|
||||
var (
|
||||
hash1_x string = "x"
|
||||
|
||||
4
controller/cache/info.go
vendored
4
controller/cache/info.go
vendored
@@ -408,8 +408,8 @@ func populateHostNodeInfo(un *unstructured.Unstructured, res *ResourceInfo) {
|
||||
}
|
||||
}
|
||||
|
||||
func generateManifestHash(un *unstructured.Unstructured, ignores []v1alpha1.ResourceIgnoreDifferences, overrides map[string]v1alpha1.ResourceOverride) (string, error) {
|
||||
normalizer, err := normalizers.NewIgnoreNormalizer(ignores, overrides)
|
||||
func generateManifestHash(un *unstructured.Unstructured, ignores []v1alpha1.ResourceIgnoreDifferences, overrides map[string]v1alpha1.ResourceOverride, opts normalizers.IgnoreNormalizerOpts) (string, error) {
|
||||
normalizer, err := normalizers.NewIgnoreNormalizer(ignores, overrides, opts)
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("error creating normalizer: %w", err)
|
||||
}
|
||||
|
||||
3
controller/cache/info_test.go
vendored
3
controller/cache/info_test.go
vendored
@@ -16,6 +16,7 @@ import (
|
||||
"sigs.k8s.io/yaml"
|
||||
|
||||
"github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1"
|
||||
"github.com/argoproj/argo-cd/v2/util/argo/normalizers"
|
||||
)
|
||||
|
||||
func strToUnstructured(jsonStr string) *unstructured.Unstructured {
|
||||
@@ -749,7 +750,7 @@ func TestManifestHash(t *testing.T) {
|
||||
|
||||
expected := hash(data)
|
||||
|
||||
hash, err := generateManifestHash(manifest, ignores, nil)
|
||||
hash, err := generateManifestHash(manifest, ignores, nil, normalizers.IgnoreNormalizerOpts{})
|
||||
assert.Equal(t, expected, hash)
|
||||
assert.Nil(t, err)
|
||||
}
|
||||
|
||||
@@ -23,6 +23,8 @@ import (
|
||||
"github.com/argoproj/argo-cd/v2/util/git"
|
||||
"github.com/argoproj/argo-cd/v2/util/healthz"
|
||||
"github.com/argoproj/argo-cd/v2/util/profile"
|
||||
|
||||
ctrl_metrics "sigs.k8s.io/controller-runtime/pkg/metrics"
|
||||
)
|
||||
|
||||
type MetricsServer struct {
|
||||
@@ -160,12 +162,12 @@ func NewMetricsServer(addr string, appLister applister.ApplicationLister, appFil
|
||||
|
||||
mux := http.NewServeMux()
|
||||
registry := NewAppRegistry(appLister, appFilter, appLabels)
|
||||
registry.MustRegister(depth, adds, latency, workDuration, unfinished, longestRunningProcessor, retries)
|
||||
|
||||
mux.Handle(MetricsPath, promhttp.HandlerFor(prometheus.Gatherers{
|
||||
// contains app controller specific metrics
|
||||
registry,
|
||||
// contains process, golang and controller workqueues metrics
|
||||
prometheus.DefaultGatherer,
|
||||
// contains workqueue metrics, process and golang metrics
|
||||
ctrl_metrics.Registry,
|
||||
}, promhttp.HandlerOpts{}))
|
||||
profile.RegisterProfiler(mux)
|
||||
healthz.ServeHealthCheck(mux, healthCheck)
|
||||
|
||||
@@ -2,6 +2,7 @@ package metrics
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"log"
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
@@ -15,12 +16,15 @@ import (
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/apimachinery/pkg/runtime"
|
||||
"k8s.io/client-go/tools/cache"
|
||||
"k8s.io/client-go/util/workqueue"
|
||||
"sigs.k8s.io/yaml"
|
||||
|
||||
argoappv1 "github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1"
|
||||
appclientset "github.com/argoproj/argo-cd/v2/pkg/client/clientset/versioned/fake"
|
||||
appinformer "github.com/argoproj/argo-cd/v2/pkg/client/informers/externalversions"
|
||||
applister "github.com/argoproj/argo-cd/v2/pkg/client/listers/application/v1alpha1"
|
||||
|
||||
"sigs.k8s.io/controller-runtime/pkg/controller"
|
||||
)
|
||||
|
||||
const fakeApp = `
|
||||
@@ -140,6 +144,12 @@ var appFilter = func(obj interface{}) bool {
|
||||
return true
|
||||
}
|
||||
|
||||
func init() {
|
||||
// Create a fake controller so we initialize the internal controller metrics.
|
||||
// https://github.com/kubernetes-sigs/controller-runtime/blob/4000e996a202917ad7d40f02ed8a2079a9ce25e9/pkg/internal/controller/metrics/metrics.go
|
||||
_, _ = controller.New("test-controller", nil, controller.Options{})
|
||||
}
|
||||
|
||||
func newFakeApp(fakeAppYAML string) *argoappv1.Application {
|
||||
var app argoappv1.Application
|
||||
err := yaml.Unmarshal([]byte(fakeAppYAML), &app)
|
||||
@@ -360,7 +370,7 @@ func assertMetricsPrinted(t *testing.T, expectedLines, body string) {
|
||||
if line == "" {
|
||||
continue
|
||||
}
|
||||
assert.Contains(t, body, line, "expected metrics mismatch")
|
||||
assert.Contains(t, body, line, fmt.Sprintf("expected metrics mismatch for line: %s", line))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -443,3 +453,70 @@ argocd_app_sync_total{dest_server="https://localhost:6443",name="my-app",namespa
|
||||
err = metricsServ.SetExpiration(time.Second)
|
||||
assert.Error(t, err)
|
||||
}
|
||||
|
||||
func TestWorkqueueMetrics(t *testing.T) {
|
||||
cancel, appLister := newFakeLister()
|
||||
defer cancel()
|
||||
metricsServ, err := NewMetricsServer("localhost:8082", appLister, appFilter, noOpHealthCheck, []string{})
|
||||
assert.NoError(t, err)
|
||||
|
||||
expectedMetrics := `
|
||||
# TYPE workqueue_adds_total counter
|
||||
workqueue_adds_total{name="test"}
|
||||
|
||||
# TYPE workqueue_depth gauge
|
||||
workqueue_depth{name="test"}
|
||||
|
||||
# TYPE workqueue_longest_running_processor_seconds gauge
|
||||
workqueue_longest_running_processor_seconds{name="test"}
|
||||
|
||||
# TYPE workqueue_queue_duration_seconds histogram
|
||||
|
||||
# TYPE workqueue_unfinished_work_seconds gauge
|
||||
workqueue_unfinished_work_seconds{name="test"}
|
||||
|
||||
# TYPE workqueue_work_duration_seconds histogram
|
||||
`
|
||||
workqueue.NewNamed("test")
|
||||
|
||||
req, err := http.NewRequest(http.MethodGet, "/metrics", nil)
|
||||
assert.NoError(t, err)
|
||||
rr := httptest.NewRecorder()
|
||||
metricsServ.Handler.ServeHTTP(rr, req)
|
||||
assert.Equal(t, rr.Code, http.StatusOK)
|
||||
body := rr.Body.String()
|
||||
log.Println(body)
|
||||
assertMetricsPrinted(t, expectedMetrics, body)
|
||||
}
|
||||
|
||||
func TestGoMetrics(t *testing.T) {
|
||||
cancel, appLister := newFakeLister()
|
||||
defer cancel()
|
||||
metricsServ, err := NewMetricsServer("localhost:8082", appLister, appFilter, noOpHealthCheck, []string{})
|
||||
assert.NoError(t, err)
|
||||
|
||||
expectedMetrics := `
|
||||
# TYPE go_gc_duration_seconds summary
|
||||
go_gc_duration_seconds_sum
|
||||
go_gc_duration_seconds_count
|
||||
# TYPE go_goroutines gauge
|
||||
go_goroutines
|
||||
# TYPE go_info gauge
|
||||
go_info
|
||||
# TYPE go_memstats_alloc_bytes gauge
|
||||
go_memstats_alloc_bytes
|
||||
# TYPE go_memstats_sys_bytes gauge
|
||||
go_memstats_sys_bytes
|
||||
# TYPE go_threads gauge
|
||||
go_threads
|
||||
`
|
||||
|
||||
req, err := http.NewRequest(http.MethodGet, "/metrics", nil)
|
||||
assert.NoError(t, err)
|
||||
rr := httptest.NewRecorder()
|
||||
metricsServ.Handler.ServeHTTP(rr, req)
|
||||
assert.Equal(t, rr.Code, http.StatusOK)
|
||||
body := rr.Body.String()
|
||||
log.Println(body)
|
||||
assertMetricsPrinted(t, expectedMetrics, body)
|
||||
}
|
||||
|
||||
@@ -1,101 +0,0 @@
|
||||
package metrics
|
||||
|
||||
import (
|
||||
"github.com/prometheus/client_golang/prometheus"
|
||||
"k8s.io/client-go/util/workqueue"
|
||||
)
|
||||
|
||||
const (
|
||||
WorkQueueSubsystem = "workqueue"
|
||||
DepthKey = "depth"
|
||||
AddsKey = "adds_total"
|
||||
QueueLatencyKey = "queue_duration_seconds"
|
||||
WorkDurationKey = "work_duration_seconds"
|
||||
UnfinishedWorkKey = "unfinished_work_seconds"
|
||||
LongestRunningProcessorKey = "longest_running_processor_seconds"
|
||||
RetriesKey = "retries_total"
|
||||
)
|
||||
|
||||
var (
|
||||
depth = prometheus.NewGaugeVec(prometheus.GaugeOpts{
|
||||
Subsystem: WorkQueueSubsystem,
|
||||
Name: DepthKey,
|
||||
Help: "Current depth of workqueue",
|
||||
}, []string{"name"})
|
||||
|
||||
adds = prometheus.NewCounterVec(prometheus.CounterOpts{
|
||||
Subsystem: WorkQueueSubsystem,
|
||||
Name: AddsKey,
|
||||
Help: "Total number of adds handled by workqueue",
|
||||
}, []string{"name"})
|
||||
|
||||
latency = prometheus.NewHistogramVec(prometheus.HistogramOpts{
|
||||
Subsystem: WorkQueueSubsystem,
|
||||
Name: QueueLatencyKey,
|
||||
Help: "How long in seconds an item stays in workqueue before being requested",
|
||||
Buckets: []float64{1e-6, 1e-5, 1e-4, 1e-3, 1e-2, 1e-1, 1, 5, 10, 15, 30, 60, 120, 180},
|
||||
}, []string{"name"})
|
||||
|
||||
workDuration = prometheus.NewHistogramVec(prometheus.HistogramOpts{
|
||||
Subsystem: WorkQueueSubsystem,
|
||||
Name: WorkDurationKey,
|
||||
Help: "How long in seconds processing an item from workqueue takes.",
|
||||
Buckets: []float64{1e-6, 1e-5, 1e-4, 1e-3, 1e-2, 1e-1, 1, 5, 10, 15, 30, 60, 120, 180},
|
||||
}, []string{"name"})
|
||||
|
||||
unfinished = prometheus.NewGaugeVec(prometheus.GaugeOpts{
|
||||
Subsystem: WorkQueueSubsystem,
|
||||
Name: UnfinishedWorkKey,
|
||||
Help: "How many seconds of work has been done that " +
|
||||
"is in progress and hasn't been observed by work_duration. Large " +
|
||||
"values indicate stuck threads. One can deduce the number of stuck " +
|
||||
"threads by observing the rate at which this increases.",
|
||||
}, []string{"name"})
|
||||
|
||||
longestRunningProcessor = prometheus.NewGaugeVec(prometheus.GaugeOpts{
|
||||
Subsystem: WorkQueueSubsystem,
|
||||
Name: LongestRunningProcessorKey,
|
||||
Help: "How many seconds has the longest running " +
|
||||
"processor for workqueue been running.",
|
||||
}, []string{"name"})
|
||||
|
||||
retries = prometheus.NewCounterVec(prometheus.CounterOpts{
|
||||
Subsystem: WorkQueueSubsystem,
|
||||
Name: RetriesKey,
|
||||
Help: "Total number of retries handled by workqueue",
|
||||
}, []string{"name"})
|
||||
)
|
||||
|
||||
func init() {
|
||||
workqueue.SetProvider(workqueueMetricsProvider{})
|
||||
}
|
||||
|
||||
type workqueueMetricsProvider struct{}
|
||||
|
||||
func (workqueueMetricsProvider) NewDepthMetric(name string) workqueue.GaugeMetric {
|
||||
return depth.WithLabelValues(name)
|
||||
}
|
||||
|
||||
func (workqueueMetricsProvider) NewAddsMetric(name string) workqueue.CounterMetric {
|
||||
return adds.WithLabelValues(name)
|
||||
}
|
||||
|
||||
func (workqueueMetricsProvider) NewLatencyMetric(name string) workqueue.HistogramMetric {
|
||||
return latency.WithLabelValues(name)
|
||||
}
|
||||
|
||||
func (workqueueMetricsProvider) NewWorkDurationMetric(name string) workqueue.HistogramMetric {
|
||||
return workDuration.WithLabelValues(name)
|
||||
}
|
||||
|
||||
func (workqueueMetricsProvider) NewUnfinishedWorkSecondsMetric(name string) workqueue.SettableGaugeMetric {
|
||||
return unfinished.WithLabelValues(name)
|
||||
}
|
||||
|
||||
func (workqueueMetricsProvider) NewLongestRunningProcessorSecondsMetric(name string) workqueue.SettableGaugeMetric {
|
||||
return longestRunningProcessor.WithLabelValues(name)
|
||||
}
|
||||
|
||||
func (workqueueMetricsProvider) NewRetriesMetric(name string) workqueue.CounterMetric {
|
||||
return retries.WithLabelValues(name)
|
||||
}
|
||||
@@ -12,7 +12,7 @@ type ClusterShardingCache interface {
|
||||
Init(clusters *v1alpha1.ClusterList)
|
||||
Add(c *v1alpha1.Cluster)
|
||||
Delete(clusterServer string)
|
||||
Update(c *v1alpha1.Cluster)
|
||||
Update(oldCluster *v1alpha1.Cluster, newCluster *v1alpha1.Cluster)
|
||||
IsManagedCluster(c *v1alpha1.Cluster) bool
|
||||
GetDistribution() map[string]int
|
||||
}
|
||||
@@ -26,7 +26,7 @@ type ClusterSharding struct {
|
||||
getClusterShard DistributionFunction
|
||||
}
|
||||
|
||||
func NewClusterSharding(db db.ArgoDB, shard, replicas int, shardingAlgorithm string) ClusterShardingCache {
|
||||
func NewClusterSharding(_ db.ArgoDB, shard, replicas int, shardingAlgorithm string) ClusterShardingCache {
|
||||
log.Debugf("Processing clusters from shard %d: Using filter function: %s", shard, shardingAlgorithm)
|
||||
clusterSharding := &ClusterSharding{
|
||||
Shard: shard,
|
||||
@@ -67,7 +67,8 @@ func (sharding *ClusterSharding) Init(clusters *v1alpha1.ClusterList) {
|
||||
defer sharding.lock.Unlock()
|
||||
newClusters := make(map[string]*v1alpha1.Cluster, len(clusters.Items))
|
||||
for _, c := range clusters.Items {
|
||||
newClusters[c.Server] = &c
|
||||
cluster := c
|
||||
newClusters[c.Server] = &cluster
|
||||
}
|
||||
sharding.Clusters = newClusters
|
||||
sharding.updateDistribution()
|
||||
@@ -96,13 +97,16 @@ func (sharding *ClusterSharding) Delete(clusterServer string) {
|
||||
}
|
||||
}
|
||||
|
||||
func (sharding *ClusterSharding) Update(c *v1alpha1.Cluster) {
|
||||
func (sharding *ClusterSharding) Update(oldCluster *v1alpha1.Cluster, newCluster *v1alpha1.Cluster) {
|
||||
sharding.lock.Lock()
|
||||
defer sharding.lock.Unlock()
|
||||
|
||||
old, ok := sharding.Clusters[c.Server]
|
||||
sharding.Clusters[c.Server] = c
|
||||
if !ok || hasShardingUpdates(old, c) {
|
||||
if _, ok := sharding.Clusters[oldCluster.Server]; ok && oldCluster.Server != newCluster.Server {
|
||||
delete(sharding.Clusters, oldCluster.Server)
|
||||
delete(sharding.Shards, oldCluster.Server)
|
||||
}
|
||||
sharding.Clusters[newCluster.Server] = newCluster
|
||||
if hasShardingUpdates(oldCluster, newCluster) {
|
||||
sharding.updateDistribution()
|
||||
} else {
|
||||
log.Debugf("Skipping sharding distribution update. No relevant changes")
|
||||
@@ -111,8 +115,8 @@ func (sharding *ClusterSharding) Update(c *v1alpha1.Cluster) {
|
||||
|
||||
func (sharding *ClusterSharding) GetDistribution() map[string]int {
|
||||
sharding.lock.RLock()
|
||||
defer sharding.lock.RUnlock()
|
||||
shards := sharding.Shards
|
||||
sharding.lock.RUnlock()
|
||||
|
||||
distribution := make(map[string]int, len(shards))
|
||||
for k, v := range shards {
|
||||
@@ -122,9 +126,7 @@ func (sharding *ClusterSharding) GetDistribution() map[string]int {
|
||||
}
|
||||
|
||||
func (sharding *ClusterSharding) updateDistribution() {
|
||||
log.Info("Updating cluster shards")
|
||||
|
||||
for _, c := range sharding.Clusters {
|
||||
for k, c := range sharding.Clusters {
|
||||
shard := 0
|
||||
if c.Shard != nil {
|
||||
requestedShard := int(*c.Shard)
|
||||
@@ -136,24 +138,44 @@ func (sharding *ClusterSharding) updateDistribution() {
|
||||
} else {
|
||||
shard = sharding.getClusterShard(c)
|
||||
}
|
||||
var shard64 int64 = int64(shard)
|
||||
c.Shard = &shard64
|
||||
sharding.Shards[c.Server] = shard
|
||||
|
||||
existingShard, ok := sharding.Shards[k]
|
||||
if ok && existingShard != shard {
|
||||
log.Infof("Cluster %s has changed shard from %d to %d", k, existingShard, shard)
|
||||
} else if !ok {
|
||||
log.Infof("Cluster %s has been assigned to shard %d", k, shard)
|
||||
} else {
|
||||
log.Debugf("Cluster %s has not changed shard", k)
|
||||
}
|
||||
sharding.Shards[k] = shard
|
||||
}
|
||||
}
|
||||
|
||||
// hasShardingUpdates returns true if the sharding distribution has been updated.
|
||||
// nil checking is done for the corner case of the in-cluster cluster which may
|
||||
// have a nil shard assigned
|
||||
// hasShardingUpdates returns true if the sharding distribution has explicitly changed
|
||||
func hasShardingUpdates(old, new *v1alpha1.Cluster) bool {
|
||||
if old == nil || new == nil || (old.Shard == nil && new.Shard == nil) {
|
||||
if old == nil || new == nil {
|
||||
return false
|
||||
}
|
||||
return old.Shard != new.Shard
|
||||
|
||||
// returns true if the cluster id has changed because some sharding algorithms depend on it.
|
||||
if old.ID != new.ID {
|
||||
return true
|
||||
}
|
||||
|
||||
if old.Server != new.Server {
|
||||
return true
|
||||
}
|
||||
|
||||
// return false if the shard field has not been modified
|
||||
if old.Shard == nil && new.Shard == nil {
|
||||
return false
|
||||
}
|
||||
return old.Shard == nil || new.Shard == nil || int64(*old.Shard) != int64(*new.Shard)
|
||||
}
|
||||
|
||||
func (d *ClusterSharding) GetClusterAccessor() clusterAccessor {
|
||||
return func() []*v1alpha1.Cluster {
|
||||
// no need to lock, as this is only called from the updateDistribution function
|
||||
clusters := make([]*v1alpha1.Cluster, 0, len(d.Clusters))
|
||||
for _, c := range d.Clusters {
|
||||
clusters = append(clusters, c)
|
||||
|
||||
475
controller/sharding/cache_test.go
Normal file
475
controller/sharding/cache_test.go
Normal file
@@ -0,0 +1,475 @@
|
||||
package sharding
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1"
|
||||
dbmocks "github.com/argoproj/argo-cd/v2/util/db/mocks"
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func setupTestSharding(shard int, replicas int) *ClusterSharding {
|
||||
shardingAlgorithm := "legacy" // we are using the legacy algorithm as it is deterministic based on the cluster id which is easier to test
|
||||
db := &dbmocks.ArgoDB{}
|
||||
return NewClusterSharding(db, shard, replicas, shardingAlgorithm).(*ClusterSharding)
|
||||
}
|
||||
|
||||
func TestNewClusterSharding(t *testing.T) {
|
||||
shard := 1
|
||||
replicas := 2
|
||||
sharding := setupTestSharding(shard, replicas)
|
||||
|
||||
assert.NotNil(t, sharding)
|
||||
assert.Equal(t, shard, sharding.Shard)
|
||||
assert.Equal(t, replicas, sharding.Replicas)
|
||||
assert.NotNil(t, sharding.Shards)
|
||||
assert.NotNil(t, sharding.Clusters)
|
||||
}
|
||||
|
||||
func TestClusterSharding_Add(t *testing.T) {
|
||||
shard := 1
|
||||
replicas := 2
|
||||
sharding := setupTestSharding(shard, replicas)
|
||||
|
||||
clusterA := &v1alpha1.Cluster{
|
||||
ID: "2",
|
||||
Server: "https://127.0.0.1:6443",
|
||||
}
|
||||
|
||||
sharding.Add(clusterA)
|
||||
|
||||
clusterB := v1alpha1.Cluster{
|
||||
ID: "1",
|
||||
Server: "https://kubernetes.default.svc",
|
||||
}
|
||||
|
||||
sharding.Add(&clusterB)
|
||||
|
||||
distribution := sharding.GetDistribution()
|
||||
|
||||
assert.Contains(t, sharding.Clusters, clusterA.Server)
|
||||
assert.Contains(t, sharding.Clusters, clusterB.Server)
|
||||
|
||||
clusterDistribution, ok := distribution[clusterA.Server]
|
||||
assert.True(t, ok)
|
||||
assert.Equal(t, 1, clusterDistribution)
|
||||
|
||||
myClusterDistribution, ok := distribution[clusterB.Server]
|
||||
assert.True(t, ok)
|
||||
assert.Equal(t, 0, myClusterDistribution)
|
||||
|
||||
assert.Equal(t, 2, len(distribution))
|
||||
}
|
||||
|
||||
func TestClusterSharding_AddRoundRobin_Redistributes(t *testing.T) {
|
||||
shard := 1
|
||||
replicas := 2
|
||||
|
||||
db := &dbmocks.ArgoDB{}
|
||||
|
||||
sharding := NewClusterSharding(db, shard, replicas, "round-robin").(*ClusterSharding)
|
||||
|
||||
clusterA := &v1alpha1.Cluster{
|
||||
ID: "1",
|
||||
Server: "https://127.0.0.1:6443",
|
||||
}
|
||||
sharding.Add(clusterA)
|
||||
|
||||
clusterB := v1alpha1.Cluster{
|
||||
ID: "3",
|
||||
Server: "https://kubernetes.default.svc",
|
||||
}
|
||||
sharding.Add(&clusterB)
|
||||
|
||||
distributionBefore := sharding.GetDistribution()
|
||||
|
||||
assert.Contains(t, sharding.Clusters, clusterA.Server)
|
||||
assert.Contains(t, sharding.Clusters, clusterB.Server)
|
||||
|
||||
clusterDistributionA, ok := distributionBefore[clusterA.Server]
|
||||
assert.True(t, ok)
|
||||
assert.Equal(t, 0, clusterDistributionA)
|
||||
|
||||
clusterDistributionB, ok := distributionBefore[clusterB.Server]
|
||||
assert.True(t, ok)
|
||||
assert.Equal(t, 1, clusterDistributionB)
|
||||
|
||||
assert.Equal(t, 2, len(distributionBefore))
|
||||
|
||||
clusterC := v1alpha1.Cluster{
|
||||
ID: "2",
|
||||
Server: "https://1.1.1.1",
|
||||
}
|
||||
sharding.Add(&clusterC)
|
||||
|
||||
distributionAfter := sharding.GetDistribution()
|
||||
|
||||
assert.Contains(t, sharding.Clusters, clusterA.Server)
|
||||
assert.Contains(t, sharding.Clusters, clusterB.Server)
|
||||
assert.Contains(t, sharding.Clusters, clusterC.Server)
|
||||
|
||||
clusterDistributionA, ok = distributionAfter[clusterA.Server]
|
||||
assert.True(t, ok)
|
||||
assert.Equal(t, 0, clusterDistributionA)
|
||||
|
||||
clusterDistributionC, ok := distributionAfter[clusterC.Server]
|
||||
assert.True(t, ok)
|
||||
assert.Equal(t, 1, clusterDistributionC) // will be assigned to shard 1 because the .ID is smaller then the "B" cluster
|
||||
|
||||
clusterDistributionB, ok = distributionAfter[clusterB.Server]
|
||||
assert.True(t, ok)
|
||||
assert.Equal(t, 0, clusterDistributionB) // will be reassigned to shard 0 because the .ID is bigger then the "C" cluster
|
||||
}
|
||||
|
||||
func TestClusterSharding_Delete(t *testing.T) {
|
||||
shard := 1
|
||||
replicas := 2
|
||||
sharding := setupTestSharding(shard, replicas)
|
||||
|
||||
sharding.Init(
|
||||
&v1alpha1.ClusterList{
|
||||
Items: []v1alpha1.Cluster{
|
||||
{
|
||||
ID: "2",
|
||||
Server: "https://127.0.0.1:6443",
|
||||
},
|
||||
{
|
||||
ID: "1",
|
||||
Server: "https://kubernetes.default.svc",
|
||||
},
|
||||
},
|
||||
},
|
||||
)
|
||||
|
||||
sharding.Delete("https://kubernetes.default.svc")
|
||||
distribution := sharding.GetDistribution()
|
||||
assert.Equal(t, 1, len(distribution))
|
||||
}
|
||||
|
||||
func TestClusterSharding_Update(t *testing.T) {
|
||||
shard := 1
|
||||
replicas := 2
|
||||
sharding := setupTestSharding(shard, replicas)
|
||||
|
||||
sharding.Init(
|
||||
&v1alpha1.ClusterList{
|
||||
Items: []v1alpha1.Cluster{
|
||||
{
|
||||
ID: "2",
|
||||
Server: "https://127.0.0.1:6443",
|
||||
},
|
||||
{
|
||||
ID: "1",
|
||||
Server: "https://kubernetes.default.svc",
|
||||
},
|
||||
},
|
||||
},
|
||||
)
|
||||
|
||||
distributionBefore := sharding.GetDistribution()
|
||||
assert.Equal(t, 2, len(distributionBefore))
|
||||
|
||||
distributionA, ok := distributionBefore["https://kubernetes.default.svc"]
|
||||
assert.True(t, ok)
|
||||
assert.Equal(t, 0, distributionA)
|
||||
|
||||
sharding.Update(&v1alpha1.Cluster{
|
||||
ID: "1",
|
||||
Server: "https://kubernetes.default.svc",
|
||||
}, &v1alpha1.Cluster{
|
||||
ID: "4",
|
||||
Server: "https://kubernetes.default.svc",
|
||||
})
|
||||
|
||||
distributionAfter := sharding.GetDistribution()
|
||||
assert.Equal(t, 2, len(distributionAfter))
|
||||
|
||||
distributionA, ok = distributionAfter["https://kubernetes.default.svc"]
|
||||
assert.True(t, ok)
|
||||
assert.Equal(t, 1, distributionA)
|
||||
}
|
||||
|
||||
func TestClusterSharding_UpdateServerName(t *testing.T) {
|
||||
shard := 1
|
||||
replicas := 2
|
||||
sharding := setupTestSharding(shard, replicas)
|
||||
|
||||
sharding.Init(
|
||||
&v1alpha1.ClusterList{
|
||||
Items: []v1alpha1.Cluster{
|
||||
{
|
||||
ID: "2",
|
||||
Server: "https://127.0.0.1:6443",
|
||||
},
|
||||
{
|
||||
ID: "1",
|
||||
Server: "https://kubernetes.default.svc",
|
||||
},
|
||||
},
|
||||
},
|
||||
)
|
||||
|
||||
distributionBefore := sharding.GetDistribution()
|
||||
assert.Equal(t, 2, len(distributionBefore))
|
||||
|
||||
distributionA, ok := distributionBefore["https://kubernetes.default.svc"]
|
||||
assert.True(t, ok)
|
||||
assert.Equal(t, 0, distributionA)
|
||||
|
||||
sharding.Update(&v1alpha1.Cluster{
|
||||
ID: "1",
|
||||
Server: "https://kubernetes.default.svc",
|
||||
}, &v1alpha1.Cluster{
|
||||
ID: "1",
|
||||
Server: "https://server2",
|
||||
})
|
||||
|
||||
distributionAfter := sharding.GetDistribution()
|
||||
assert.Equal(t, 2, len(distributionAfter))
|
||||
|
||||
_, ok = distributionAfter["https://kubernetes.default.svc"]
|
||||
assert.False(t, ok) // the old server name should not be present anymore
|
||||
|
||||
_, ok = distributionAfter["https://server2"]
|
||||
assert.True(t, ok) // the new server name should be present
|
||||
}
|
||||
|
||||
func TestClusterSharding_IsManagedCluster(t *testing.T) {
|
||||
replicas := 2
|
||||
sharding0 := setupTestSharding(0, replicas)
|
||||
|
||||
sharding0.Init(
|
||||
&v1alpha1.ClusterList{
|
||||
Items: []v1alpha1.Cluster{
|
||||
{
|
||||
ID: "1",
|
||||
Server: "https://kubernetes.default.svc",
|
||||
},
|
||||
{
|
||||
ID: "2",
|
||||
Server: "https://127.0.0.1:6443",
|
||||
},
|
||||
},
|
||||
},
|
||||
)
|
||||
|
||||
assert.True(t, sharding0.IsManagedCluster(&v1alpha1.Cluster{
|
||||
ID: "1",
|
||||
Server: "https://kubernetes.default.svc",
|
||||
}))
|
||||
|
||||
assert.False(t, sharding0.IsManagedCluster(&v1alpha1.Cluster{
|
||||
ID: "2",
|
||||
Server: "https://127.0.0.1:6443",
|
||||
}))
|
||||
|
||||
sharding1 := setupTestSharding(1, replicas)
|
||||
|
||||
sharding1.Init(
|
||||
&v1alpha1.ClusterList{
|
||||
Items: []v1alpha1.Cluster{
|
||||
{
|
||||
ID: "2",
|
||||
Server: "https://127.0.0.1:6443",
|
||||
},
|
||||
{
|
||||
ID: "1",
|
||||
Server: "https://kubernetes.default.svc",
|
||||
},
|
||||
},
|
||||
},
|
||||
)
|
||||
|
||||
assert.False(t, sharding1.IsManagedCluster(&v1alpha1.Cluster{
|
||||
ID: "1",
|
||||
Server: "https://kubernetes.default.svc",
|
||||
}))
|
||||
|
||||
assert.True(t, sharding1.IsManagedCluster(&v1alpha1.Cluster{
|
||||
ID: "2",
|
||||
Server: "https://127.0.0.1:6443",
|
||||
}))
|
||||
|
||||
}
|
||||
|
||||
func TestClusterSharding_ClusterShardOfResourceShouldNotBeChanged(t *testing.T) {
|
||||
shard := 1
|
||||
replicas := 2
|
||||
sharding := setupTestSharding(shard, replicas)
|
||||
|
||||
Int64Ptr := func(i int64) *int64 {
|
||||
return &i
|
||||
}
|
||||
|
||||
clusterWithNil := &v1alpha1.Cluster{
|
||||
ID: "2",
|
||||
Server: "https://127.0.0.1:6443",
|
||||
Shard: nil,
|
||||
}
|
||||
|
||||
clusterWithValue := &v1alpha1.Cluster{
|
||||
ID: "1",
|
||||
Server: "https://kubernetes.default.svc",
|
||||
Shard: Int64Ptr(1),
|
||||
}
|
||||
|
||||
clusterWithToBigValue := &v1alpha1.Cluster{
|
||||
ID: "3",
|
||||
Server: "https://1.1.1.1",
|
||||
Shard: Int64Ptr(999), // shard value is explicitly bigger than the number of replicas
|
||||
}
|
||||
|
||||
sharding.Init(
|
||||
&v1alpha1.ClusterList{
|
||||
Items: []v1alpha1.Cluster{
|
||||
*clusterWithNil,
|
||||
*clusterWithValue,
|
||||
*clusterWithToBigValue,
|
||||
},
|
||||
},
|
||||
)
|
||||
distribution := sharding.GetDistribution()
|
||||
assert.Equal(t, 3, len(distribution))
|
||||
|
||||
assert.Nil(t, sharding.Clusters[clusterWithNil.Server].Shard)
|
||||
|
||||
assert.NotNil(t, sharding.Clusters[clusterWithValue.Server].Shard)
|
||||
assert.Equal(t, int64(1), *sharding.Clusters[clusterWithValue.Server].Shard)
|
||||
assert.Equal(t, 1, distribution[clusterWithValue.Server])
|
||||
|
||||
assert.NotNil(t, sharding.Clusters[clusterWithToBigValue.Server].Shard)
|
||||
assert.Equal(t, int64(999), *sharding.Clusters[clusterWithToBigValue.Server].Shard)
|
||||
assert.Equal(t, 0, distribution[clusterWithToBigValue.Server]) // will be assigned to shard 0 because the value is bigger than the number of replicas
|
||||
}
|
||||
|
||||
func TestHasShardingUpdates(t *testing.T) {
|
||||
Int64Ptr := func(i int64) *int64 {
|
||||
return &i
|
||||
}
|
||||
|
||||
testCases := []struct {
|
||||
name string
|
||||
old *v1alpha1.Cluster
|
||||
new *v1alpha1.Cluster
|
||||
expected bool
|
||||
}{
|
||||
{
|
||||
name: "No updates",
|
||||
old: &v1alpha1.Cluster{
|
||||
Server: "https://kubernetes.default.svc",
|
||||
Shard: Int64Ptr(1),
|
||||
},
|
||||
new: &v1alpha1.Cluster{
|
||||
Server: "https://kubernetes.default.svc",
|
||||
Shard: Int64Ptr(1),
|
||||
},
|
||||
expected: false,
|
||||
},
|
||||
{
|
||||
name: "Updates",
|
||||
old: &v1alpha1.Cluster{
|
||||
Server: "https://kubernetes.default.svc",
|
||||
Shard: Int64Ptr(1),
|
||||
},
|
||||
new: &v1alpha1.Cluster{
|
||||
Server: "https://kubernetes.default.svc",
|
||||
Shard: Int64Ptr(2),
|
||||
},
|
||||
expected: true,
|
||||
},
|
||||
{
|
||||
name: "Old is nil",
|
||||
old: nil,
|
||||
new: &v1alpha1.Cluster{
|
||||
Server: "https://kubernetes.default.svc",
|
||||
Shard: Int64Ptr(2),
|
||||
},
|
||||
expected: false,
|
||||
},
|
||||
{
|
||||
name: "New is nil",
|
||||
old: &v1alpha1.Cluster{
|
||||
Server: "https://kubernetes.default.svc",
|
||||
Shard: Int64Ptr(2),
|
||||
},
|
||||
new: nil,
|
||||
expected: false,
|
||||
},
|
||||
{
|
||||
name: "Both are nil",
|
||||
old: nil,
|
||||
new: nil,
|
||||
expected: false,
|
||||
},
|
||||
{
|
||||
name: "Both shards are nil",
|
||||
old: &v1alpha1.Cluster{
|
||||
Server: "https://kubernetes.default.svc",
|
||||
Shard: nil,
|
||||
},
|
||||
new: &v1alpha1.Cluster{
|
||||
Server: "https://kubernetes.default.svc",
|
||||
Shard: nil,
|
||||
},
|
||||
expected: false,
|
||||
},
|
||||
{
|
||||
name: "Old shard is nil",
|
||||
old: &v1alpha1.Cluster{
|
||||
Server: "https://kubernetes.default.svc",
|
||||
Shard: nil,
|
||||
},
|
||||
new: &v1alpha1.Cluster{
|
||||
Server: "https://kubernetes.default.svc",
|
||||
Shard: Int64Ptr(2),
|
||||
},
|
||||
expected: true,
|
||||
},
|
||||
{
|
||||
name: "New shard is nil",
|
||||
old: &v1alpha1.Cluster{
|
||||
Server: "https://kubernetes.default.svc",
|
||||
Shard: Int64Ptr(2),
|
||||
},
|
||||
new: &v1alpha1.Cluster{
|
||||
Server: "https://kubernetes.default.svc",
|
||||
Shard: nil,
|
||||
},
|
||||
expected: true,
|
||||
},
|
||||
{
|
||||
name: "Cluster ID has changed",
|
||||
old: &v1alpha1.Cluster{
|
||||
ID: "1",
|
||||
Server: "https://kubernetes.default.svc",
|
||||
Shard: Int64Ptr(2),
|
||||
},
|
||||
new: &v1alpha1.Cluster{
|
||||
ID: "2",
|
||||
Server: "https://kubernetes.default.svc",
|
||||
Shard: Int64Ptr(2),
|
||||
},
|
||||
expected: true,
|
||||
},
|
||||
{
|
||||
name: "Server has changed",
|
||||
old: &v1alpha1.Cluster{
|
||||
ID: "1",
|
||||
Server: "https://server1",
|
||||
Shard: Int64Ptr(2),
|
||||
},
|
||||
new: &v1alpha1.Cluster{
|
||||
ID: "1",
|
||||
Server: "https://server2",
|
||||
Shard: Int64Ptr(2),
|
||||
},
|
||||
expected: true,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tc := range testCases {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
assert.Equal(t, tc.expected, hasShardingUpdates(tc.old, tc.new))
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -4,6 +4,7 @@ import (
|
||||
"context"
|
||||
"fmt"
|
||||
"hash/fnv"
|
||||
"math"
|
||||
"os"
|
||||
"sort"
|
||||
"strconv"
|
||||
@@ -20,6 +21,7 @@ import (
|
||||
|
||||
"github.com/argoproj/argo-cd/v2/util/db"
|
||||
"github.com/argoproj/argo-cd/v2/util/env"
|
||||
"github.com/argoproj/argo-cd/v2/util/errors"
|
||||
"github.com/argoproj/argo-cd/v2/util/settings"
|
||||
log "github.com/sirupsen/logrus"
|
||||
kubeerrors "k8s.io/apimachinery/pkg/api/errors"
|
||||
@@ -206,7 +208,7 @@ func createClusterIndexByClusterIdMap(getCluster clusterAccessor) map[string]int
|
||||
// The function takes the shard number from the environment variable (default value -1, if not set) and passes it to this function.
|
||||
// If the shard value passed to this function is -1, that is, the shard was not set as an environment variable,
|
||||
// we default the shard number to 0 for computing the default config map.
|
||||
func GetOrUpdateShardFromConfigMap(kubeClient *kubernetes.Clientset, settingsMgr *settings.SettingsManager, replicas, shard int) (int, error) {
|
||||
func GetOrUpdateShardFromConfigMap(kubeClient kubernetes.Interface, settingsMgr *settings.SettingsManager, replicas, shard int) (int, error) {
|
||||
hostname, err := osHostnameFunction()
|
||||
if err != nil {
|
||||
return -1, err
|
||||
@@ -363,3 +365,59 @@ func getDefaultShardMappingData(replicas int) []shardApplicationControllerMappin
|
||||
}
|
||||
return shardMappingData
|
||||
}
|
||||
|
||||
func GetClusterSharding(kubeClient kubernetes.Interface, settingsMgr *settings.SettingsManager, shardingAlgorithm string, enableDynamicClusterDistribution bool) (ClusterShardingCache, error) {
|
||||
var replicasCount int
|
||||
if enableDynamicClusterDistribution {
|
||||
applicationControllerName := env.StringFromEnv(common.EnvAppControllerName, common.DefaultApplicationControllerName)
|
||||
appControllerDeployment, err := kubeClient.AppsV1().Deployments(settingsMgr.GetNamespace()).Get(context.Background(), applicationControllerName, metav1.GetOptions{})
|
||||
|
||||
// if app controller deployment is not found when dynamic cluster distribution is enabled error out
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("(dymanic cluster distribution) failed to get app controller deployment: %v", err)
|
||||
}
|
||||
|
||||
if appControllerDeployment != nil && appControllerDeployment.Spec.Replicas != nil {
|
||||
replicasCount = int(*appControllerDeployment.Spec.Replicas)
|
||||
} else {
|
||||
return nil, fmt.Errorf("(dymanic cluster distribution) failed to get app controller deployment replica count")
|
||||
}
|
||||
|
||||
} else {
|
||||
replicasCount = env.ParseNumFromEnv(common.EnvControllerReplicas, 0, 0, math.MaxInt32)
|
||||
}
|
||||
shardNumber := env.ParseNumFromEnv(common.EnvControllerShard, -1, -math.MaxInt32, math.MaxInt32)
|
||||
if replicasCount > 1 {
|
||||
// check for shard mapping using configmap if application-controller is a deployment
|
||||
// else use existing logic to infer shard from pod name if application-controller is a statefulset
|
||||
if enableDynamicClusterDistribution {
|
||||
var err error
|
||||
// retry 3 times if we find a conflict while updating shard mapping configMap.
|
||||
// If we still see conflicts after the retries, wait for next iteration of heartbeat process.
|
||||
for i := 0; i <= common.AppControllerHeartbeatUpdateRetryCount; i++ {
|
||||
shardNumber, err = GetOrUpdateShardFromConfigMap(kubeClient, settingsMgr, replicasCount, shardNumber)
|
||||
if err != nil && !kubeerrors.IsConflict(err) {
|
||||
err = fmt.Errorf("unable to get shard due to error updating the sharding config map: %s", err)
|
||||
break
|
||||
}
|
||||
log.Warnf("conflict when getting shard from shard mapping configMap. Retrying (%d/3)", i)
|
||||
}
|
||||
errors.CheckError(err)
|
||||
} else {
|
||||
if shardNumber < 0 {
|
||||
var err error
|
||||
shardNumber, err = InferShard()
|
||||
errors.CheckError(err)
|
||||
}
|
||||
if shardNumber > replicasCount {
|
||||
log.Warnf("Calculated shard number %d is greated than the number of replicas count. Defaulting to 0", shardNumber)
|
||||
shardNumber = 0
|
||||
}
|
||||
}
|
||||
} else {
|
||||
log.Info("Processing all cluster shards")
|
||||
shardNumber = 0
|
||||
}
|
||||
db := db.NewDB(settingsMgr.GetNamespace(), settingsMgr, kubeClient)
|
||||
return NewClusterSharding(db, shardNumber, replicasCount, shardingAlgorithm), nil
|
||||
}
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
package sharding
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
@@ -12,10 +13,14 @@ import (
|
||||
"github.com/argoproj/argo-cd/v2/common"
|
||||
"github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1"
|
||||
dbmocks "github.com/argoproj/argo-cd/v2/util/db/mocks"
|
||||
"github.com/argoproj/argo-cd/v2/util/settings"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/mock"
|
||||
appsv1 "k8s.io/api/apps/v1"
|
||||
v1 "k8s.io/api/core/v1"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/apimachinery/pkg/runtime"
|
||||
kubefake "k8s.io/client-go/kubernetes/fake"
|
||||
)
|
||||
|
||||
func TestGetShardByID_NotEmptyID(t *testing.T) {
|
||||
@@ -681,3 +686,187 @@ func Test_getOrUpdateShardNumberForController(t *testing.T) {
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestGetClusterSharding(t *testing.T) {
|
||||
IntPtr := func(i int32) *int32 {
|
||||
return &i
|
||||
}
|
||||
|
||||
deployment := &appsv1.Deployment{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: common.DefaultApplicationControllerName,
|
||||
Namespace: "argocd",
|
||||
},
|
||||
Spec: appsv1.DeploymentSpec{
|
||||
Replicas: IntPtr(1),
|
||||
},
|
||||
}
|
||||
|
||||
deploymentMultiReplicas := &appsv1.Deployment{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "argocd-application-controller-multi-replicas",
|
||||
Namespace: "argocd",
|
||||
},
|
||||
Spec: appsv1.DeploymentSpec{
|
||||
Replicas: IntPtr(3),
|
||||
},
|
||||
}
|
||||
|
||||
objects := append([]runtime.Object{}, deployment, deploymentMultiReplicas)
|
||||
kubeclientset := kubefake.NewSimpleClientset(objects...)
|
||||
|
||||
settingsMgr := settings.NewSettingsManager(context.TODO(), kubeclientset, "argocd", settings.WithRepoOrClusterChangedHandler(func() {
|
||||
}))
|
||||
|
||||
testCases := []struct {
|
||||
name string
|
||||
useDynamicSharding bool
|
||||
envsSetter func(t *testing.T)
|
||||
cleanup func()
|
||||
expectedShard int
|
||||
expectedReplicas int
|
||||
expectedErr error
|
||||
}{
|
||||
{
|
||||
name: "Default sharding with statefulset",
|
||||
envsSetter: func(t *testing.T) {
|
||||
t.Setenv(common.EnvControllerReplicas, "1")
|
||||
},
|
||||
cleanup: func() {},
|
||||
useDynamicSharding: false,
|
||||
expectedShard: 0,
|
||||
expectedReplicas: 1,
|
||||
expectedErr: nil,
|
||||
},
|
||||
{
|
||||
name: "Default sharding with deployment",
|
||||
envsSetter: func(t *testing.T) {
|
||||
t.Setenv(common.EnvAppControllerName, common.DefaultApplicationControllerName)
|
||||
},
|
||||
cleanup: func() {},
|
||||
useDynamicSharding: true,
|
||||
expectedShard: 0,
|
||||
expectedReplicas: 1,
|
||||
expectedErr: nil,
|
||||
},
|
||||
{
|
||||
name: "Default sharding with deployment and multiple replicas",
|
||||
envsSetter: func(t *testing.T) {
|
||||
t.Setenv(common.EnvAppControllerName, "argocd-application-controller-multi-replicas")
|
||||
},
|
||||
cleanup: func() {},
|
||||
useDynamicSharding: true,
|
||||
expectedShard: 0,
|
||||
expectedReplicas: 3,
|
||||
expectedErr: nil,
|
||||
},
|
||||
{
|
||||
name: "Statefulset multiple replicas",
|
||||
envsSetter: func(t *testing.T) {
|
||||
t.Setenv(common.EnvControllerReplicas, "3")
|
||||
osHostnameFunction = func() (string, error) { return "example-shard-3", nil }
|
||||
},
|
||||
cleanup: func() {
|
||||
osHostnameFunction = os.Hostname
|
||||
},
|
||||
useDynamicSharding: false,
|
||||
expectedShard: 3,
|
||||
expectedReplicas: 3,
|
||||
expectedErr: nil,
|
||||
},
|
||||
{
|
||||
name: "Explicit shard with statefulset and 1 replica",
|
||||
envsSetter: func(t *testing.T) {
|
||||
t.Setenv(common.EnvControllerReplicas, "1")
|
||||
t.Setenv(common.EnvControllerShard, "3")
|
||||
},
|
||||
cleanup: func() {},
|
||||
useDynamicSharding: false,
|
||||
expectedShard: 0,
|
||||
expectedReplicas: 1,
|
||||
expectedErr: nil,
|
||||
},
|
||||
{
|
||||
name: "Explicit shard with statefulset and 2 replica - and to high shard",
|
||||
envsSetter: func(t *testing.T) {
|
||||
t.Setenv(common.EnvControllerReplicas, "2")
|
||||
t.Setenv(common.EnvControllerShard, "3")
|
||||
},
|
||||
cleanup: func() {},
|
||||
useDynamicSharding: false,
|
||||
expectedShard: 0,
|
||||
expectedReplicas: 2,
|
||||
expectedErr: nil,
|
||||
},
|
||||
{
|
||||
name: "Explicit shard with statefulset and 2 replica",
|
||||
envsSetter: func(t *testing.T) {
|
||||
t.Setenv(common.EnvControllerReplicas, "2")
|
||||
t.Setenv(common.EnvControllerShard, "1")
|
||||
},
|
||||
cleanup: func() {},
|
||||
useDynamicSharding: false,
|
||||
expectedShard: 1,
|
||||
expectedReplicas: 2,
|
||||
expectedErr: nil,
|
||||
},
|
||||
{
|
||||
name: "Explicit shard with deployment",
|
||||
envsSetter: func(t *testing.T) {
|
||||
t.Setenv(common.EnvControllerShard, "3")
|
||||
},
|
||||
cleanup: func() {},
|
||||
useDynamicSharding: true,
|
||||
expectedShard: 0,
|
||||
expectedReplicas: 1,
|
||||
expectedErr: nil,
|
||||
},
|
||||
{
|
||||
name: "Explicit shard with deployment and multiple replicas will read from configmap",
|
||||
envsSetter: func(t *testing.T) {
|
||||
t.Setenv(common.EnvAppControllerName, "argocd-application-controller-multi-replicas")
|
||||
t.Setenv(common.EnvControllerShard, "3")
|
||||
},
|
||||
cleanup: func() {},
|
||||
useDynamicSharding: true,
|
||||
expectedShard: 0,
|
||||
expectedReplicas: 3,
|
||||
expectedErr: nil,
|
||||
},
|
||||
{
|
||||
name: "Dynamic sharding but missing deployment",
|
||||
envsSetter: func(t *testing.T) {
|
||||
t.Setenv(common.EnvAppControllerName, "missing-deployment")
|
||||
},
|
||||
cleanup: func() {},
|
||||
useDynamicSharding: true,
|
||||
expectedShard: 0,
|
||||
expectedReplicas: 1,
|
||||
expectedErr: fmt.Errorf("(dymanic cluster distribution) failed to get app controller deployment: deployments.apps \"missing-deployment\" not found"),
|
||||
},
|
||||
}
|
||||
|
||||
for _, tc := range testCases {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
tc.envsSetter(t)
|
||||
defer tc.cleanup()
|
||||
shardingCache, err := GetClusterSharding(kubeclientset, settingsMgr, "round-robin", tc.useDynamicSharding)
|
||||
|
||||
if shardingCache != nil {
|
||||
clusterSharding := shardingCache.(*ClusterSharding)
|
||||
assert.Equal(t, tc.expectedShard, clusterSharding.Shard)
|
||||
assert.Equal(t, tc.expectedReplicas, clusterSharding.Replicas)
|
||||
}
|
||||
|
||||
if tc.expectedErr != nil {
|
||||
if err != nil {
|
||||
assert.Equal(t, tc.expectedErr.Error(), err.Error())
|
||||
} else {
|
||||
t.Errorf("Expected error %v but got nil", tc.expectedErr)
|
||||
}
|
||||
} else {
|
||||
assert.Nil(t, err)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@@ -35,6 +35,7 @@ import (
|
||||
"github.com/argoproj/argo-cd/v2/reposerver/apiclient"
|
||||
"github.com/argoproj/argo-cd/v2/util/argo"
|
||||
argodiff "github.com/argoproj/argo-cd/v2/util/argo/diff"
|
||||
"github.com/argoproj/argo-cd/v2/util/argo/normalizers"
|
||||
appstatecache "github.com/argoproj/argo-cd/v2/util/cache/appstate"
|
||||
"github.com/argoproj/argo-cd/v2/util/db"
|
||||
"github.com/argoproj/argo-cd/v2/util/gpg"
|
||||
@@ -117,6 +118,7 @@ type appStateManager struct {
|
||||
repoErrorCache goSync.Map
|
||||
repoErrorGracePeriod time.Duration
|
||||
serverSideDiff bool
|
||||
ignoreNormalizerOpts normalizers.IgnoreNormalizerOpts
|
||||
}
|
||||
|
||||
// GetRepoObjs will generate the manifests for the given application delegating the
|
||||
@@ -605,7 +607,7 @@ func (m *appStateManager) CompareAppState(app *v1alpha1.Application, project *v1
|
||||
useDiffCache := useDiffCache(noCache, manifestInfos, sources, app, manifestRevisions, m.statusRefreshTimeout, serverSideDiff, logCtx)
|
||||
|
||||
diffConfigBuilder := argodiff.NewDiffConfigBuilder().
|
||||
WithDiffSettings(app.Spec.IgnoreDifferences, resourceOverrides, compareOptions.IgnoreAggregatedRoles).
|
||||
WithDiffSettings(app.Spec.IgnoreDifferences, resourceOverrides, compareOptions.IgnoreAggregatedRoles, m.ignoreNormalizerOpts).
|
||||
WithTracking(appLabelKey, string(trackingMethod))
|
||||
|
||||
if useDiffCache {
|
||||
@@ -935,6 +937,7 @@ func NewAppStateManager(
|
||||
persistResourceHealth bool,
|
||||
repoErrorGracePeriod time.Duration,
|
||||
serverSideDiff bool,
|
||||
ignoreNormalizerOpts normalizers.IgnoreNormalizerOpts,
|
||||
) AppStateManager {
|
||||
return &appStateManager{
|
||||
liveStateCache: liveStateCache,
|
||||
@@ -952,6 +955,7 @@ func NewAppStateManager(
|
||||
persistResourceHealth: persistResourceHealth,
|
||||
repoErrorGracePeriod: repoErrorGracePeriod,
|
||||
serverSideDiff: serverSideDiff,
|
||||
ignoreNormalizerOpts: ignoreNormalizerOpts,
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -2,7 +2,6 @@ package controller
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
goerrors "errors"
|
||||
"fmt"
|
||||
"os"
|
||||
@@ -11,6 +10,7 @@ import (
|
||||
"time"
|
||||
|
||||
cdcommon "github.com/argoproj/argo-cd/v2/common"
|
||||
"k8s.io/apimachinery/pkg/util/strategicpatch"
|
||||
|
||||
"github.com/argoproj/gitops-engine/pkg/sync"
|
||||
"github.com/argoproj/gitops-engine/pkg/sync/common"
|
||||
@@ -21,6 +21,7 @@ import (
|
||||
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
|
||||
"k8s.io/apimachinery/pkg/runtime/schema"
|
||||
"k8s.io/apimachinery/pkg/util/managedfields"
|
||||
"k8s.io/client-go/kubernetes/scheme"
|
||||
"k8s.io/kubectl/pkg/util/openapi"
|
||||
|
||||
"github.com/argoproj/argo-cd/v2/controller/metrics"
|
||||
@@ -399,11 +400,10 @@ func (m *appStateManager) SyncAppState(app *v1alpha1.Application, state *v1alpha
|
||||
}
|
||||
}
|
||||
|
||||
// normalizeTargetResources will apply the diff normalization in all live and target resources.
|
||||
// Then it calculates the merge patch between the normalized live and the current live resources.
|
||||
// Finally it applies the merge patch in the normalized target resources. This is done to ensure
|
||||
// that target resources have the same ignored diff fields values from live ones to avoid them to
|
||||
// be applied in the cluster. Returns the list of normalized target resources.
|
||||
// normalizeTargetResources modifies target resources to ensure ignored fields are not touched during synchronization:
|
||||
// - 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(cr *comparisonResult) ([]*unstructured.Unstructured, error) {
|
||||
// normalize live and target resources
|
||||
normalized, err := diff.Normalize(cr.reconciliationResult.Live, cr.reconciliationResult.Target, cr.diffConfig)
|
||||
@@ -422,94 +422,35 @@ func normalizeTargetResources(cr *comparisonResult) ([]*unstructured.Unstructure
|
||||
patchedTargets = append(patchedTargets, originalTarget)
|
||||
continue
|
||||
}
|
||||
// calculate targetPatch between normalized and target resource
|
||||
targetPatch, err := getMergePatch(normalizedTarget, originalTarget)
|
||||
|
||||
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
|
||||
}
|
||||
lookupPatchMeta = &meta
|
||||
}
|
||||
|
||||
livePatch, err := getMergePatch(normalized.Lives[idx], live, lookupPatchMeta)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// check if there is a patch to apply. An empty patch is identified by a '{}' string.
|
||||
if len(targetPatch) > 2 {
|
||||
livePatch, err := getMergePatch(normalized.Lives[idx], live)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
// generate a minimal patch that uses the fields from targetPatch (template)
|
||||
// with livePatch values
|
||||
patch, err := compilePatch(targetPatch, livePatch)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
normalizedTarget, err = applyMergePatch(normalizedTarget, patch)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
} else {
|
||||
// if there is no patch just use the original target
|
||||
normalizedTarget = originalTarget
|
||||
normalizedTarget, err = applyMergePatch(normalizedTarget, livePatch, versionedObject)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
patchedTargets = append(patchedTargets, normalizedTarget)
|
||||
}
|
||||
return patchedTargets, nil
|
||||
}
|
||||
|
||||
// compilePatch will generate a patch using the fields from templatePatch with
|
||||
// the values from valuePatch.
|
||||
func compilePatch(templatePatch, valuePatch []byte) ([]byte, error) {
|
||||
templateMap := make(map[string]interface{})
|
||||
err := json.Unmarshal(templatePatch, &templateMap)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
valueMap := make(map[string]interface{})
|
||||
err = json.Unmarshal(valuePatch, &valueMap)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
resultMap := intersectMap(templateMap, valueMap)
|
||||
return json.Marshal(resultMap)
|
||||
}
|
||||
|
||||
// intersectMap will return map with the fields intersection from the 2 provided
|
||||
// maps populated with the valueMap values.
|
||||
func intersectMap(templateMap, valueMap map[string]interface{}) map[string]interface{} {
|
||||
result := make(map[string]interface{})
|
||||
for k, v := range templateMap {
|
||||
if innerTMap, ok := v.(map[string]interface{}); ok {
|
||||
if innerVMap, ok := valueMap[k].(map[string]interface{}); ok {
|
||||
result[k] = intersectMap(innerTMap, innerVMap)
|
||||
}
|
||||
} else if innerTSlice, ok := v.([]interface{}); ok {
|
||||
if innerVSlice, ok := valueMap[k].([]interface{}); ok {
|
||||
items := []interface{}{}
|
||||
for idx, innerTSliceValue := range innerTSlice {
|
||||
if idx < len(innerVSlice) {
|
||||
if tSliceValueMap, ok := innerTSliceValue.(map[string]interface{}); ok {
|
||||
if vSliceValueMap, ok := innerVSlice[idx].(map[string]interface{}); ok {
|
||||
item := intersectMap(tSliceValueMap, vSliceValueMap)
|
||||
items = append(items, item)
|
||||
}
|
||||
} else {
|
||||
items = append(items, innerVSlice[idx])
|
||||
}
|
||||
}
|
||||
}
|
||||
if len(items) > 0 {
|
||||
result[k] = items
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if _, ok := valueMap[k]; ok {
|
||||
result[k] = valueMap[k]
|
||||
}
|
||||
}
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
// getMergePatch calculates and returns the patch between the original and the
|
||||
// modified unstructures.
|
||||
func getMergePatch(original, modified *unstructured.Unstructured) ([]byte, error) {
|
||||
func getMergePatch(original, modified *unstructured.Unstructured, lookupPatchMeta *strategicpatch.PatchMetaFromStruct) ([]byte, error) {
|
||||
originalJSON, err := original.MarshalJSON()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
@@ -518,20 +459,30 @@ func getMergePatch(original, modified *unstructured.Unstructured) ([]byte, error
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if lookupPatchMeta != nil {
|
||||
return strategicpatch.CreateThreeWayMergePatch(modifiedJSON, modifiedJSON, originalJSON, lookupPatchMeta, true)
|
||||
}
|
||||
|
||||
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) (*unstructured.Unstructured, error) {
|
||||
func applyMergePatch(obj *unstructured.Unstructured, patch []byte, versionedObject interface{}) (*unstructured.Unstructured, error) {
|
||||
originalJSON, err := obj.MarshalJSON()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
patchedJSON, err := jsonpatch.MergePatch(originalJSON, patch)
|
||||
var patchedJSON []byte
|
||||
if versionedObject == nil {
|
||||
patchedJSON, err = jsonpatch.MergePatch(originalJSON, patch)
|
||||
} else {
|
||||
patchedJSON, err = strategicpatch.StrategicMergePatch(originalJSON, patch, versionedObject)
|
||||
}
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
patchedObj := &unstructured.Unstructured{}
|
||||
_, _, err = unstructured.UnstructuredJSONScheme.Decode(patchedJSON, nil, patchedObj)
|
||||
if err != nil {
|
||||
|
||||
@@ -18,6 +18,7 @@ import (
|
||||
"github.com/argoproj/argo-cd/v2/reposerver/apiclient"
|
||||
"github.com/argoproj/argo-cd/v2/test"
|
||||
"github.com/argoproj/argo-cd/v2/util/argo/diff"
|
||||
"github.com/argoproj/argo-cd/v2/util/argo/normalizers"
|
||||
)
|
||||
|
||||
func TestPersistRevisionHistory(t *testing.T) {
|
||||
@@ -261,7 +262,7 @@ func TestNormalizeTargetResources(t *testing.T) {
|
||||
setup := func(t *testing.T, ignores []v1alpha1.ResourceIgnoreDifferences) *fixture {
|
||||
t.Helper()
|
||||
dc, err := diff.NewDiffConfigBuilder().
|
||||
WithDiffSettings(ignores, nil, true).
|
||||
WithDiffSettings(ignores, nil, true, normalizers.IgnoreNormalizerOpts{}).
|
||||
WithNoCache().
|
||||
Build()
|
||||
require.NoError(t, err)
|
||||
@@ -386,3 +387,207 @@ func TestNormalizeTargetResources(t *testing.T) {
|
||||
assert.Equal(t, 2, len(containers))
|
||||
})
|
||||
}
|
||||
|
||||
func TestNormalizeTargetResourcesWithList(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.LiveHTTPProxy)
|
||||
target := test.YamlToUnstructured(testdata.TargetHTTPProxy)
|
||||
return &fixture{
|
||||
&comparisonResult{
|
||||
reconciliationResult: sync.ReconciliationResult{
|
||||
Live: []*unstructured.Unstructured{live},
|
||||
Target: []*unstructured.Unstructured{target},
|
||||
},
|
||||
diffConfig: dc,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
t.Run("will properly ignore nested fields within arrays", func(t *testing.T) {
|
||||
// given
|
||||
ignores := []v1alpha1.ResourceIgnoreDifferences{
|
||||
{
|
||||
Group: "projectcontour.io",
|
||||
Kind: "HTTPProxy",
|
||||
JQPathExpressions: []string{".spec.routes[]"},
|
||||
//JSONPointers: []string{"/spec/routes"},
|
||||
},
|
||||
}
|
||||
f := setupHttpProxy(t, ignores)
|
||||
target := test.YamlToUnstructured(testdata.TargetHTTPProxy)
|
||||
f.comparisonResult.reconciliationResult.Target = []*unstructured.Unstructured{target}
|
||||
|
||||
// when
|
||||
patchedTargets, err := normalizeTargetResources(f.comparisonResult)
|
||||
|
||||
// then
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, 1, len(f.comparisonResult.reconciliationResult.Live))
|
||||
require.Equal(t, 1, len(f.comparisonResult.reconciliationResult.Target))
|
||||
require.Equal(t, 1, len(patchedTargets))
|
||||
|
||||
// live should have 1 entry
|
||||
require.Equal(t, 1, len(dig[[]any](f.comparisonResult.reconciliationResult.Live[0].Object, []interface{}{"spec", "routes", 0, "rateLimitPolicy", "global", "descriptors"})))
|
||||
// assert some arbitrary field to show `entries[0]` is not an empty object
|
||||
require.Equal(t, "sample-header", dig[string](f.comparisonResult.reconciliationResult.Live[0].Object, []interface{}{"spec", "routes", 0, "rateLimitPolicy", "global", "descriptors", 0, "entries", 0, "requestHeader", "headerName"}))
|
||||
|
||||
// target has 2 entries
|
||||
require.Equal(t, 2, len(dig[[]any](f.comparisonResult.reconciliationResult.Target[0].Object, []interface{}{"spec", "routes", 0, "rateLimitPolicy", "global", "descriptors", 0, "entries"})))
|
||||
// assert some arbitrary field to show `entries[0]` is not an empty object
|
||||
require.Equal(t, "sample-header", dig[string](f.comparisonResult.reconciliationResult.Target[0].Object, []interface{}{"spec", "routes", 0, "rateLimitPolicy", "global", "descriptors", 0, "entries", 0, "requestHeaderValueMatch", "headers", 0, "name"}))
|
||||
|
||||
// It should be *1* entries in the array
|
||||
require.Equal(t, 1, len(dig[[]any](patchedTargets[0].Object, []interface{}{"spec", "routes", 0, "rateLimitPolicy", "global", "descriptors"})))
|
||||
// and it should NOT equal an empty object
|
||||
require.Len(t, dig[any](patchedTargets[0].Object, []interface{}{"spec", "routes", 0, "rateLimitPolicy", "global", "descriptors", 0, "entries", 0}), 1)
|
||||
|
||||
})
|
||||
t.Run("will correctly set array entries if new entries have been added", func(t *testing.T) {
|
||||
// given
|
||||
ignores := []v1alpha1.ResourceIgnoreDifferences{
|
||||
{
|
||||
Group: "apps",
|
||||
Kind: "Deployment",
|
||||
JQPathExpressions: []string{".spec.template.spec.containers[].env[] | select(.name == \"SOME_ENV_VAR\")"},
|
||||
},
|
||||
}
|
||||
f := setupHttpProxy(t, ignores)
|
||||
live := test.YamlToUnstructured(testdata.LiveDeploymentEnvVarsYaml)
|
||||
target := test.YamlToUnstructured(testdata.TargetDeploymentEnvVarsYaml)
|
||||
f.comparisonResult.reconciliationResult.Live = []*unstructured.Unstructured{live}
|
||||
f.comparisonResult.reconciliationResult.Target = []*unstructured.Unstructured{target}
|
||||
|
||||
// when
|
||||
targets, err := normalizeTargetResources(f.comparisonResult)
|
||||
|
||||
// then
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, 1, len(targets))
|
||||
containers, ok, err := unstructured.NestedSlice(targets[0].Object, "spec", "template", "spec", "containers")
|
||||
require.NoError(t, err)
|
||||
require.True(t, ok)
|
||||
assert.Equal(t, 1, len(containers))
|
||||
|
||||
ports := containers[0].(map[string]interface{})["ports"].([]interface{})
|
||||
assert.Equal(t, 1, len(ports))
|
||||
|
||||
env := containers[0].(map[string]interface{})["env"].([]interface{})
|
||||
assert.Equal(t, 3, len(env))
|
||||
|
||||
first := env[0]
|
||||
second := env[1]
|
||||
third := env[2]
|
||||
|
||||
// Currently the defined order at this time is the insertion order of the target manifest.
|
||||
assert.Equal(t, "SOME_ENV_VAR", first.(map[string]interface{})["name"])
|
||||
assert.Equal(t, "some_value", first.(map[string]interface{})["value"])
|
||||
|
||||
assert.Equal(t, "SOME_OTHER_ENV_VAR", second.(map[string]interface{})["name"])
|
||||
assert.Equal(t, "some_other_value", second.(map[string]interface{})["value"])
|
||||
|
||||
assert.Equal(t, "YET_ANOTHER_ENV_VAR", third.(map[string]interface{})["name"])
|
||||
assert.Equal(t, "yet_another_value", third.(map[string]interface{})["value"])
|
||||
})
|
||||
|
||||
t.Run("ignore-deployment-image-replicas-changes-additive", func(t *testing.T) {
|
||||
// given
|
||||
|
||||
ignores := []v1alpha1.ResourceIgnoreDifferences{
|
||||
{
|
||||
Group: "apps",
|
||||
Kind: "Deployment",
|
||||
JSONPointers: []string{"/spec/replicas"},
|
||||
}, {
|
||||
Group: "apps",
|
||||
Kind: "Deployment",
|
||||
JQPathExpressions: []string{".spec.template.spec.containers[].image"},
|
||||
},
|
||||
}
|
||||
f := setupHttpProxy(t, ignores)
|
||||
live := test.YamlToUnstructured(testdata.MinimalImageReplicaDeploymentYaml)
|
||||
target := test.YamlToUnstructured(testdata.AdditionalImageReplicaDeploymentYaml)
|
||||
f.comparisonResult.reconciliationResult.Live = []*unstructured.Unstructured{live}
|
||||
f.comparisonResult.reconciliationResult.Target = []*unstructured.Unstructured{target}
|
||||
|
||||
// when
|
||||
targets, err := normalizeTargetResources(f.comparisonResult)
|
||||
|
||||
// then
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, 1, len(targets))
|
||||
metadata, ok, err := unstructured.NestedMap(targets[0].Object, "metadata")
|
||||
require.NoError(t, err)
|
||||
require.True(t, ok)
|
||||
labels, ok := metadata["labels"].(map[string]interface{})
|
||||
require.True(t, ok)
|
||||
assert.Equal(t, 2, len(labels))
|
||||
assert.Equal(t, "web", labels["appProcess"])
|
||||
|
||||
spec, ok, err := unstructured.NestedMap(targets[0].Object, "spec")
|
||||
require.NoError(t, err)
|
||||
require.True(t, ok)
|
||||
|
||||
assert.Equal(t, int64(1), spec["replicas"])
|
||||
|
||||
template, ok := spec["template"].(map[string]interface{})
|
||||
require.True(t, ok)
|
||||
|
||||
tMetadata, ok := template["metadata"].(map[string]interface{})
|
||||
require.True(t, ok)
|
||||
tLabels, ok := tMetadata["labels"].(map[string]interface{})
|
||||
require.True(t, ok)
|
||||
assert.Equal(t, 2, len(tLabels))
|
||||
assert.Equal(t, "web", tLabels["appProcess"])
|
||||
|
||||
tSpec, ok := template["spec"].(map[string]interface{})
|
||||
require.True(t, ok)
|
||||
containers, ok, err := unstructured.NestedSlice(tSpec, "containers")
|
||||
require.NoError(t, err)
|
||||
require.True(t, ok)
|
||||
assert.Equal(t, 1, len(containers))
|
||||
|
||||
first := containers[0].(map[string]interface{})
|
||||
assert.Equal(t, "alpine:3", first["image"])
|
||||
|
||||
resources, ok := first["resources"].(map[string]interface{})
|
||||
require.True(t, ok)
|
||||
requests, ok := resources["requests"].(map[string]interface{})
|
||||
require.True(t, ok)
|
||||
assert.Equal(t, "400m", requests["cpu"])
|
||||
|
||||
env, ok, err := unstructured.NestedSlice(first, "env")
|
||||
require.NoError(t, err)
|
||||
require.True(t, ok)
|
||||
assert.Equal(t, 1, len(env))
|
||||
|
||||
env0 := env[0].(map[string]interface{})
|
||||
assert.Equal(t, "EV", env0["name"])
|
||||
assert.Equal(t, "here", env0["value"])
|
||||
})
|
||||
}
|
||||
|
||||
func dig[T any](obj interface{}, path []interface{}) T {
|
||||
i := obj
|
||||
|
||||
for _, segment := range path {
|
||||
switch segment.(type) {
|
||||
case int:
|
||||
i = i.([]interface{})[segment.(int)]
|
||||
case string:
|
||||
i = i.(map[string]interface{})[segment.(string)]
|
||||
default:
|
||||
panic("invalid path for object")
|
||||
}
|
||||
}
|
||||
|
||||
return i.(T)
|
||||
}
|
||||
|
||||
28
controller/testdata/additional-image-replicas-deployment.yaml
vendored
Normal file
28
controller/testdata/additional-image-replicas-deployment.yaml
vendored
Normal file
@@ -0,0 +1,28 @@
|
||||
apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
metadata:
|
||||
labels:
|
||||
app: client
|
||||
appProcess: web
|
||||
name: client
|
||||
spec:
|
||||
replicas: 2
|
||||
selector:
|
||||
matchLabels:
|
||||
app: client
|
||||
strategy: {}
|
||||
template:
|
||||
metadata:
|
||||
labels:
|
||||
app: client
|
||||
appProcess: web
|
||||
spec:
|
||||
containers:
|
||||
- image: alpine:2
|
||||
name: alpine
|
||||
resources:
|
||||
requests:
|
||||
cpu: 400m
|
||||
env:
|
||||
- name: EV
|
||||
value: here
|
||||
18
controller/testdata/data.go
vendored
18
controller/testdata/data.go
vendored
@@ -11,4 +11,22 @@ var (
|
||||
|
||||
//go:embed target-deployment-new-entries.yaml
|
||||
TargetDeploymentNewEntries string
|
||||
|
||||
//go:embed live-httpproxy.yaml
|
||||
LiveHTTPProxy string
|
||||
|
||||
//go:embed target-httpproxy.yaml
|
||||
TargetHTTPProxy string
|
||||
|
||||
//go:embed live-deployment-env-vars.yaml
|
||||
LiveDeploymentEnvVarsYaml string
|
||||
|
||||
//go:embed target-deployment-env-vars.yaml
|
||||
TargetDeploymentEnvVarsYaml string
|
||||
|
||||
//go:embed minimal-image-replicas-deployment.yaml
|
||||
MinimalImageReplicaDeploymentYaml string
|
||||
|
||||
//go:embed additional-image-replicas-deployment.yaml
|
||||
AdditionalImageReplicaDeploymentYaml string
|
||||
)
|
||||
|
||||
177
controller/testdata/live-deployment-env-vars.yaml
vendored
Normal file
177
controller/testdata/live-deployment-env-vars.yaml
vendored
Normal file
@@ -0,0 +1,177 @@
|
||||
apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
metadata:
|
||||
annotations:
|
||||
argocd.argoproj.io/tracking-id: 'guestbook:apps/Deployment:default/kustomize-guestbook-ui'
|
||||
deployment.kubernetes.io/revision: '9'
|
||||
iksm-version: '2.0'
|
||||
kubectl.kubernetes.io/last-applied-configuration: >
|
||||
{"apiVersion":"apps/v1","kind":"Deployment","metadata":{"annotations":{"argocd.argoproj.io/tracking-id":"guestbook:apps/Deployment:default/kustomize-guestbook-ui","iksm-version":"2.0"},"name":"kustomize-guestbook-ui","namespace":"default"},"spec":{"replicas":4,"revisionHistoryLimit":3,"selector":{"matchLabels":{"app":"guestbook-ui"}},"template":{"metadata":{"labels":{"app":"guestbook-ui"}},"spec":{"containers":[{"env":[{"name":"SOME_ENV_VAR","value":"some_value"}],"image":"gcr.io/heptio-images/ks-guestbook-demo:0.1","name":"guestbook-ui","ports":[{"containerPort":80}],"resources":{"requests":{"cpu":"50m","memory":"100Mi"}}}]}}}}
|
||||
creationTimestamp: '2022-01-05T15:45:21Z'
|
||||
generation: 119
|
||||
managedFields:
|
||||
- apiVersion: apps/v1
|
||||
fieldsType: FieldsV1
|
||||
fieldsV1:
|
||||
'f:metadata':
|
||||
'f:annotations':
|
||||
'f:iksm-version': {}
|
||||
manager: janitor
|
||||
operation: Apply
|
||||
time: '2022-01-06T18:21:04Z'
|
||||
- apiVersion: apps/v1
|
||||
fieldsType: FieldsV1
|
||||
fieldsV1:
|
||||
'f:metadata':
|
||||
'f:annotations':
|
||||
.: {}
|
||||
'f:argocd.argoproj.io/tracking-id': {}
|
||||
'f:kubectl.kubernetes.io/last-applied-configuration': {}
|
||||
'f:spec':
|
||||
'f:progressDeadlineSeconds': {}
|
||||
'f:replicas': {}
|
||||
'f:revisionHistoryLimit': {}
|
||||
'f:selector': {}
|
||||
'f:strategy':
|
||||
'f:rollingUpdate':
|
||||
.: {}
|
||||
'f:maxSurge': {}
|
||||
'f:maxUnavailable': {}
|
||||
'f:type': {}
|
||||
'f:template':
|
||||
'f:metadata':
|
||||
'f:labels':
|
||||
.: {}
|
||||
'f:app': {}
|
||||
'f:spec':
|
||||
'f:containers':
|
||||
'k:{"name":"guestbook-ui"}':
|
||||
.: {}
|
||||
'f:env':
|
||||
.: {}
|
||||
'k:{"name":"SOME_ENV_VAR"}':
|
||||
.: {}
|
||||
'f:name': {}
|
||||
'f:value': {}
|
||||
'f:image': {}
|
||||
'f:imagePullPolicy': {}
|
||||
'f:name': {}
|
||||
'f:ports':
|
||||
.: {}
|
||||
'k:{"containerPort":80,"protocol":"TCP"}':
|
||||
.: {}
|
||||
'f:containerPort': {}
|
||||
'f:protocol': {}
|
||||
'f:resources':
|
||||
.: {}
|
||||
'f:requests':
|
||||
.: {}
|
||||
'f:cpu': {}
|
||||
'f:memory': {}
|
||||
'f:terminationMessagePath': {}
|
||||
'f:terminationMessagePolicy': {}
|
||||
'f:dnsPolicy': {}
|
||||
'f:restartPolicy': {}
|
||||
'f:schedulerName': {}
|
||||
'f:securityContext': {}
|
||||
'f:terminationGracePeriodSeconds': {}
|
||||
manager: argocd
|
||||
operation: Update
|
||||
time: '2022-01-06T15:04:15Z'
|
||||
- apiVersion: apps/v1
|
||||
fieldsType: FieldsV1
|
||||
fieldsV1:
|
||||
'f:metadata':
|
||||
'f:annotations':
|
||||
'f:deployment.kubernetes.io/revision': {}
|
||||
'f:status':
|
||||
'f:availableReplicas': {}
|
||||
'f:conditions':
|
||||
.: {}
|
||||
'k:{"type":"Available"}':
|
||||
.: {}
|
||||
'f:lastTransitionTime': {}
|
||||
'f:lastUpdateTime': {}
|
||||
'f:message': {}
|
||||
'f:reason': {}
|
||||
'f:status': {}
|
||||
'f:type': {}
|
||||
'k:{"type":"Progressing"}':
|
||||
.: {}
|
||||
'f:lastTransitionTime': {}
|
||||
'f:lastUpdateTime': {}
|
||||
'f:message': {}
|
||||
'f:reason': {}
|
||||
'f:status': {}
|
||||
'f:type': {}
|
||||
'f:observedGeneration': {}
|
||||
'f:readyReplicas': {}
|
||||
'f:replicas': {}
|
||||
'f:updatedReplicas': {}
|
||||
manager: kube-controller-manager
|
||||
operation: Update
|
||||
time: '2022-01-06T18:15:14Z'
|
||||
name: kustomize-guestbook-ui
|
||||
namespace: default
|
||||
resourceVersion: '8289211'
|
||||
uid: ef253575-ce44-4c5e-84ad-16e81d0df6eb
|
||||
spec:
|
||||
progressDeadlineSeconds: 600
|
||||
replicas: 4
|
||||
revisionHistoryLimit: 3
|
||||
selector:
|
||||
matchLabels:
|
||||
app: guestbook-ui
|
||||
strategy:
|
||||
rollingUpdate:
|
||||
maxSurge: 25%
|
||||
maxUnavailable: 25%
|
||||
type: RollingUpdate
|
||||
template:
|
||||
metadata:
|
||||
creationTimestamp: null
|
||||
labels:
|
||||
app: guestbook-ui
|
||||
spec:
|
||||
containers:
|
||||
- env:
|
||||
- name: SOME_ENV_VAR
|
||||
value: some_value
|
||||
image: 'gcr.io/heptio-images/ks-guestbook-demo:0.1'
|
||||
imagePullPolicy: IfNotPresent
|
||||
name: guestbook-ui
|
||||
ports:
|
||||
- containerPort: 80
|
||||
protocol: TCP
|
||||
resources:
|
||||
requests:
|
||||
cpu: 50m
|
||||
memory: 100Mi
|
||||
terminationMessagePath: /dev/termination-log
|
||||
terminationMessagePolicy: File
|
||||
dnsPolicy: ClusterFirst
|
||||
restartPolicy: Always
|
||||
schedulerName: default-scheduler
|
||||
securityContext: {}
|
||||
terminationGracePeriodSeconds: 30
|
||||
status:
|
||||
availableReplicas: 4
|
||||
conditions:
|
||||
- lastTransitionTime: '2022-01-05T22:20:37Z'
|
||||
lastUpdateTime: '2022-01-05T22:43:47Z'
|
||||
message: >-
|
||||
ReplicaSet "kustomize-guestbook-ui-6549d54677" has successfully
|
||||
progressed.
|
||||
reason: NewReplicaSetAvailable
|
||||
status: 'True'
|
||||
type: Progressing
|
||||
- lastTransitionTime: '2022-01-06T18:15:14Z'
|
||||
lastUpdateTime: '2022-01-06T18:15:14Z'
|
||||
message: Deployment has minimum availability.
|
||||
reason: MinimumReplicasAvailable
|
||||
status: 'True'
|
||||
type: Available
|
||||
observedGeneration: 119
|
||||
readyReplicas: 4
|
||||
replicas: 4
|
||||
updatedReplicas: 4
|
||||
14
controller/testdata/live-httpproxy.yaml
vendored
Normal file
14
controller/testdata/live-httpproxy.yaml
vendored
Normal file
@@ -0,0 +1,14 @@
|
||||
apiVersion: projectcontour.io/v1
|
||||
kind: HTTPProxy
|
||||
metadata:
|
||||
name: my-http-proxy
|
||||
namespace: default
|
||||
spec:
|
||||
routes:
|
||||
- rateLimitPolicy:
|
||||
global:
|
||||
descriptors:
|
||||
- entries:
|
||||
- requestHeader:
|
||||
descriptorKey: sample-key
|
||||
headerName: sample-header
|
||||
21
controller/testdata/minimal-image-replicas-deployment.yaml
vendored
Normal file
21
controller/testdata/minimal-image-replicas-deployment.yaml
vendored
Normal file
@@ -0,0 +1,21 @@
|
||||
apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
metadata:
|
||||
labels:
|
||||
app: client
|
||||
name: client
|
||||
spec:
|
||||
replicas: 1
|
||||
selector:
|
||||
matchLabels:
|
||||
app: client
|
||||
strategy: {}
|
||||
template:
|
||||
metadata:
|
||||
labels:
|
||||
app: client
|
||||
spec:
|
||||
containers:
|
||||
- image: alpine:3
|
||||
name: alpine
|
||||
resources: {}
|
||||
35
controller/testdata/target-deployment-env-vars.yaml
vendored
Normal file
35
controller/testdata/target-deployment-env-vars.yaml
vendored
Normal file
@@ -0,0 +1,35 @@
|
||||
apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
metadata:
|
||||
annotations:
|
||||
argocd.argoproj.io/tracking-id: 'guestbook:apps/Deployment:default/kustomize-guestbook-ui'
|
||||
iksm-version: '1.0'
|
||||
name: kustomize-guestbook-ui
|
||||
namespace: default
|
||||
spec:
|
||||
replicas: 1
|
||||
revisionHistoryLimit: 3
|
||||
selector:
|
||||
matchLabels:
|
||||
app: guestbook-ui
|
||||
template:
|
||||
metadata:
|
||||
labels:
|
||||
app: guestbook-ui
|
||||
spec:
|
||||
containers:
|
||||
- env:
|
||||
- name: SOME_OTHER_ENV_VAR
|
||||
value: some_other_value
|
||||
- name: YET_ANOTHER_ENV_VAR
|
||||
value: yet_another_value
|
||||
- name: SOME_ENV_VAR
|
||||
value: different_value!
|
||||
image: 'gcr.io/heptio-images/ks-guestbook-demo:0.1'
|
||||
name: guestbook-ui
|
||||
ports:
|
||||
- containerPort: 80
|
||||
resources:
|
||||
requests:
|
||||
cpu: 50m
|
||||
memory: 100Mi
|
||||
23
controller/testdata/target-httpproxy.yaml
vendored
Normal file
23
controller/testdata/target-httpproxy.yaml
vendored
Normal file
@@ -0,0 +1,23 @@
|
||||
apiVersion: projectcontour.io/v1
|
||||
kind: HTTPProxy
|
||||
metadata:
|
||||
name: my-http-proxy
|
||||
namespace: default
|
||||
spec:
|
||||
routes:
|
||||
- rateLimitPolicy:
|
||||
global:
|
||||
descriptors:
|
||||
- entries:
|
||||
- requestHeaderValueMatch:
|
||||
headers:
|
||||
- contains: sample-key
|
||||
name: sample-header
|
||||
value: third
|
||||
- requestHeader:
|
||||
descriptorKey: sample-key
|
||||
headerName: sample-header
|
||||
- entries:
|
||||
- requestHeader:
|
||||
descriptorKey: sample-key
|
||||
headerName: sample-header
|
||||
@@ -1,48 +1,83 @@
|
||||
setTimeout(function() {
|
||||
const callbackName = 'callback_' + new Date().getTime();
|
||||
window[callbackName] = function (response) {
|
||||
const div = document.createElement('div');
|
||||
div.innerHTML = response.html;
|
||||
document.querySelector(".md-header__inner > .md-header__title").appendChild(div);
|
||||
const container = div.querySelector('.rst-versions');
|
||||
var caret = document.createElement('div');
|
||||
caret.innerHTML = "<i class='fa fa-caret-down dropdown-caret'></i>"
|
||||
caret.classList.add('dropdown-caret')
|
||||
div.querySelector('.rst-current-version').appendChild(caret);
|
||||
const targetNode = document.querySelector('.md-header__inner');
|
||||
const observerOptions = {
|
||||
childList: true,
|
||||
subtree: true
|
||||
};
|
||||
|
||||
const observerCallback = function(mutationsList, observer) {
|
||||
for (let mutation of mutationsList) {
|
||||
if (mutation.type === 'childList') {
|
||||
const titleElement = document.querySelector('.md-header__inner > .md-header__title');
|
||||
if (titleElement) {
|
||||
initializeVersionDropdown();
|
||||
observer.disconnect();
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
const observer = new MutationObserver(observerCallback);
|
||||
observer.observe(targetNode, observerOptions);
|
||||
|
||||
function getCurrentVersion() {
|
||||
const currentVersion = window.location.href.match(/\/en\/(release-(?:v\d+|[\d\.]+|\w+)|latest|stable)\//);
|
||||
if (currentVersion && currentVersion.length > 1) {
|
||||
return currentVersion[1];
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
function initializeVersionDropdown() {
|
||||
const callbackName = 'callback_' + new Date().getTime();
|
||||
window[callbackName] = function(response) {
|
||||
const div = document.createElement('div');
|
||||
div.innerHTML = response.html;
|
||||
document.querySelector(".md-header__inner > .md-header__title").appendChild(div);
|
||||
const container = div.querySelector('.rst-versions');
|
||||
var caret = document.createElement('div');
|
||||
caret.innerHTML = "<i class='fa fa-caret-down dropdown-caret'></i>";
|
||||
caret.classList.add('dropdown-caret');
|
||||
div.querySelector('.rst-current-version').appendChild(caret);
|
||||
|
||||
div.querySelector('.rst-current-version').addEventListener('click', function() {
|
||||
container.classList.toggle('shift-up');
|
||||
});
|
||||
};
|
||||
|
||||
var CSSLink = document.createElement('link');
|
||||
CSSLink.rel='stylesheet';
|
||||
CSSLink.rel = 'stylesheet';
|
||||
CSSLink.href = '/assets/versions.css';
|
||||
document.getElementsByTagName('head')[0].appendChild(CSSLink);
|
||||
|
||||
var script = document.createElement('script');
|
||||
script.src = 'https://argo-cd.readthedocs.io/_/api/v2/footer_html/?'+
|
||||
'callback=' + callbackName + '&project=argo-cd&page=&theme=mkdocs&format=jsonp&docroot=docs&source_suffix=.md&version=' + (window['READTHEDOCS_DATA'] || { version: 'latest' }).version;
|
||||
const currentVersion = getCurrentVersion();
|
||||
script.src = 'https://argo-cd.readthedocs.io/_/api/v2/footer_html/?' +
|
||||
'callback=' + callbackName + '&project=argo-cd&page=&theme=mkdocs&format=jsonp&docroot=docs&source_suffix=.md&version=' + (currentVersion || 'latest');
|
||||
document.getElementsByTagName('head')[0].appendChild(script);
|
||||
}, 0);
|
||||
}
|
||||
|
||||
// VERSION WARNINGS
|
||||
window.addEventListener("DOMContentLoaded", function() {
|
||||
var rtdData = window['READTHEDOCS_DATA'] || { version: 'latest' };
|
||||
var margin = 30;
|
||||
var headerHeight = document.getElementsByClassName("md-header")[0].offsetHeight;
|
||||
if (rtdData.version === "latest") {
|
||||
document.querySelector("div[data-md-component=announce]").innerHTML = "<div id='announce-msg'>You are viewing the docs for an unreleased version of Argo CD, <a href='https://argo-cd.readthedocs.io/en/stable/'>click here to go to the latest stable version.</a></div>"
|
||||
var bannerHeight = document.getElementById('announce-msg').offsetHeight + margin
|
||||
document.querySelector("header.md-header").style.top = bannerHeight +"px";
|
||||
document.querySelector('style').textContent +=
|
||||
"@media screen and (min-width: 76.25em){ .md-sidebar { height: 0; top:"+ (bannerHeight+headerHeight)+"px !important; }}"
|
||||
document.querySelector('style').textContent +=
|
||||
"@media screen and (min-width: 60em){ .md-sidebar--secondary { height: 0; top:"+ (bannerHeight+headerHeight)+"px !important; }}"
|
||||
}
|
||||
else if (rtdData.version !== "stable") {
|
||||
document.querySelector("div[data-md-component=announce]").innerHTML = "<div id='announce-msg'>You are viewing the docs for a previous version of Argo CD, <a href='https://argo-cd.readthedocs.io/en/stable/'>click here to go to the latest stable version.</a></div>"
|
||||
var bannerHeight = document.getElementById('announce-msg').offsetHeight + margin
|
||||
document.querySelector("header.md-header").style.top = bannerHeight +"px";
|
||||
document.querySelector('style').textContent +=
|
||||
"@media screen and (min-width: 76.25em){ .md-sidebar { height: 0; top:"+ (bannerHeight+headerHeight)+"px !important; }}"
|
||||
document.querySelector('style').textContent +=
|
||||
"@media screen and (min-width: 60em){ .md-sidebar--secondary { height: 0; top:"+ (bannerHeight+headerHeight)+"px !important; }}"
|
||||
var headerHeight = document.getElementsByClassName("md-header")[0].offsetHeight;
|
||||
const currentVersion = getCurrentVersion();
|
||||
if (currentVersion) {
|
||||
if (currentVersion === "latest") {
|
||||
document.querySelector("div[data-md-component=announce]").innerHTML = "<div id='announce-msg'>You are viewing the docs for an unreleased version of Argo CD, <a href='https://argo-cd.readthedocs.io/en/stable/'>click here to go to the latest stable version.</a></div>";
|
||||
var bannerHeight = document.getElementById('announce-msg').offsetHeight + margin;
|
||||
document.querySelector("header.md-header").style.top = bannerHeight + "px";
|
||||
document.querySelector('style').textContent +=
|
||||
"@media screen and (min-width: 76.25em){ .md-sidebar { height: 0; top:" + (bannerHeight + headerHeight) + "px !important; }}";
|
||||
document.querySelector('style').textContent +=
|
||||
"@media screen and (min-width: 60em){ .md-sidebar--secondary { height: 0; top:" + (bannerHeight + headerHeight) + "px !important; }}";
|
||||
} else if (currentVersion !== "stable") {
|
||||
document.querySelector("div[data-md-component=announce]").innerHTML = "<div id='announce-msg'>You are viewing the docs for a previous version of Argo CD, <a href='https://argo-cd.readthedocs.io/en/stable/'>click here to go to the latest stable version.</a></div>";
|
||||
var bannerHeight = document.getElementById('announce-msg').offsetHeight + margin;
|
||||
document.querySelector("header.md-header").style.top = bannerHeight + "px";
|
||||
document.querySelector('style').textContent +=
|
||||
"@media screen and (min-width: 76.25em){ .md-sidebar { height: 0; top:" + (bannerHeight + headerHeight) + "px !important; }}";
|
||||
document.querySelector('style').textContent +=
|
||||
"@media screen and (min-width: 60em){ .md-sidebar--secondary { height: 0; top:" + (bannerHeight + headerHeight) + "px !important; }}";
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
42
docs/faq.md
42
docs/faq.md
@@ -268,3 +268,45 @@ The most common instance of this error is with `env:` fields for `containers`.
|
||||
|
||||
!!! note "Dynamic applications"
|
||||
It's possible that your application is being generated by a tool in which case the duplication might not be evident within the scope of a single file. If you have trouble debugging this problem, consider filing a ticket to the owner of the generator tool asking them to improve its validation and error reporting.
|
||||
|
||||
## How to rotate Redis secret?
|
||||
* Delete `argocd-redis` secret in the namespace where Argo CD is installed.
|
||||
```bash
|
||||
kubectl delete secret argocd-redis -n <argocd namesapce>
|
||||
```
|
||||
* If you are running Redis in HA mode, restart Redis in HA.
|
||||
```bash
|
||||
kubectl rollout restart deployment argocd-redis-ha-haproxy
|
||||
kubectl rollout restart statefulset argocd-redis-ha-server
|
||||
```
|
||||
* If you are running Redis in non-HA mode, restart Redis.
|
||||
```bash
|
||||
kubectl rollout restart deployment argocd-redis
|
||||
```
|
||||
* Restart other components.
|
||||
```bash
|
||||
kubectl rollout restart deployment argocd-server argocd-repo-server
|
||||
kubectl rollout restart statefulset argocd-application-controller
|
||||
```
|
||||
|
||||
## How to turn off Redis auth if users really want to?
|
||||
|
||||
Argo CD default installation is now configured automatically enable Redis authentication.
|
||||
If for some reason authenticated Redis does not work for you and you want to use non-authenticated Redis, here are the steps:
|
||||
|
||||
* You need to have your own Redis installation.
|
||||
* Configure Argo CD to use your own Redis instance. See this [doc](https://argo-cd.readthedocs.io/en/stable/operator-manual/argocd-cmd-params-cm-yaml/) for the Argo CD configuration.
|
||||
* If you already installed Redis shipped with Argo CD, you also need to clean up the existing components:
|
||||
* When HA Redis is used:
|
||||
* kubectl delete deployment argocd-redis-ha-haproxy
|
||||
* kubectl delete statefulset argocd-redis-ha-server
|
||||
* When non-HA Redis is used:
|
||||
* kubectl delete deployment argocd-redis
|
||||
* Remove environment variable `REDIS_PASSWORD` from the following manifests
|
||||
* Deployment: argocd-repo-server:
|
||||
* Deployment: argocd-server
|
||||
* StatefulSet: argocd-application-controller
|
||||
|
||||
## How do I provide my own Redis credentials?
|
||||
The Redis password is stored in Kubernetes secret `argocd-redis` with key `auth` in the namespace where Argo CD is installed.
|
||||
You can config your secret provider to generate Kubernetes secret accordingly.
|
||||
@@ -38,6 +38,9 @@ Do one of:
|
||||
|
||||
Use `argocd login --core` to [configure](./user-guide/commands/argocd_login.md) CLI access and skip steps 3-5.
|
||||
|
||||
!!! note
|
||||
This default installation for Redis is using password authentication. The Redis password is stored in Kubernetes secret `argocd-redis` with key `auth` in the namespace where Argo CD is installed.
|
||||
|
||||
## 2. Download Argo CD CLI
|
||||
|
||||
Download the latest Argo CD version from [https://github.com/argoproj/argo-cd/releases/latest](https://github.com/argoproj/argo-cd/releases/latest). More detailed installation instructions can be found via the [CLI installation documentation](cli_installation.md).
|
||||
|
||||
@@ -326,7 +326,7 @@ As with other generators, clusters *must* already be defined within Argo CD, in
|
||||
In addition to the flattened key/value pairs from the configuration file, the following generator parameters are provided:
|
||||
|
||||
- `{{.path.path}}`: The path to the directory containing matching configuration file within the Git repository. Example: `/clusters/clusterA`, if the config file was `/clusters/clusterA/config.json`
|
||||
- `{{index .path n}}`: The path to the matching configuration file within the Git repository, split into array elements (`n` - array index). Example: `index .path 0: clusters`, `index .path 1: clusterA`
|
||||
- `{{index .path.segments n}}`: The path to the matching configuration file within the Git repository, split into array elements (`n` - array index). Example: `index .path.segments 0: clusters`, `index .path.segments 1: clusterA`
|
||||
- `{{.path.basename}}`: Basename of the path to the directory containing the configuration file (e.g. `clusterA`, with the above example.)
|
||||
- `{{.path.basenameNormalized}}`: This field is the same as `.path.basename` with unsupported characters replaced with `-` (e.g. a `path` of `/directory/directory_2`, and `.path.basename` of `directory_2` would produce `directory-2` here).
|
||||
- `{{.path.filename}}`: The matched filename. e.g., `config.json` in the above example.
|
||||
@@ -360,7 +360,7 @@ spec:
|
||||
files:
|
||||
- path: "applicationset/examples/git-generator-files-discovery/cluster-config/**/config.json"
|
||||
values:
|
||||
base_dir: "{{index .path 0}}/{{index .path 1}}/{{index .path 2}}"
|
||||
base_dir: "{{index .path.segments 0}}/{{index .path.segments 1}}/{{index .path.segments 2}}"
|
||||
template:
|
||||
metadata:
|
||||
name: '{{.cluster.name}}-guestbook'
|
||||
|
||||
@@ -412,3 +412,5 @@ data:
|
||||
cluster:
|
||||
name: some-cluster
|
||||
server: https://some-cluster
|
||||
# The maximum size of the payload that can be sent to the webhook server.
|
||||
webhook.maxPayloadSizeMB: 1024
|
||||
@@ -267,13 +267,13 @@ The final rate limiter uses a combination of both and calculates the final backo
|
||||
|
||||
### Global rate limits
|
||||
|
||||
This is enabled by default, it is a simple bucket based rate limiter that limits the number of items that can be queued per second.
|
||||
This is disabled by default, it is a simple bucket based rate limiter that limits the number of items that can be queued per second.
|
||||
This is useful to prevent a large number of apps from being queued at the same time.
|
||||
|
||||
To configure the bucket limiter you can set the following environment variables:
|
||||
|
||||
* `WORKQUEUE_BUCKET_SIZE` - The number of items that can be queued in a single burst. Defaults to 500.
|
||||
* `WORKQUEUE_BUCKET_QPS` - The number of items that can be queued per second. Defaults to 50.
|
||||
* `WORKQUEUE_BUCKET_QPS` - The number of items that can be queued per second. Defaults to MaxFloat64, which disables the limiter.
|
||||
|
||||
### Per item rate limits
|
||||
|
||||
|
||||
@@ -12,8 +12,8 @@ To be able to send notifications with argocd-notifications you have to create an
|
||||
8. Give your integration a name, copy the "API key" and safe it somewhere for later
|
||||
9. Make sure the checkboxes for "Create and Update Access" and "enable" are selected, disable the other checkboxes to remove unnecessary permissions
|
||||
10. Click "Safe Integration" at the bottom
|
||||
11. Check your browser for the correct server apiURL. If it is "app.opsgenie.com" then use the us/international api url `api.opsgenie.com` in the next step, otherwise use `api.eu.opsgenie.com` (european api).
|
||||
12. You are finished with configuring opsgenie. Now you need to configure argocd-notifications. Use the apiUrl, the team name and the apiKey to configure the opsgenie integration in the `argocd-notifications-secret` secret.
|
||||
11. Check your browser for the correct server apiURL. If it is "app.opsgenie.com" then use the US/international api url `api.opsgenie.com` in the next step, otherwise use `api.eu.opsgenie.com` (European API).
|
||||
12. You are finished with configuring opsgenie. Now you need to configure argocd-notifications. Use the apiUrl, the team name and the apiKey to configure the Opsgenie integration in the `argocd-notifications-secret` secret.
|
||||
|
||||
```yaml
|
||||
apiVersion: v1
|
||||
|
||||
@@ -1,17 +1,17 @@
|
||||
# Pagerduty
|
||||
# PagerDuty
|
||||
|
||||
## Parameters
|
||||
|
||||
The Pagerduty notification service is used to create pagerduty incidents and requires specifying the following settings:
|
||||
The PagerDuty notification service is used to create PagerDuty incidents and requires specifying the following settings:
|
||||
|
||||
* `pagerdutyToken` - the pagerduty auth token
|
||||
* `pagerdutyToken` - the PagerDuty auth token
|
||||
* `from` - email address of a valid user associated with the account making the request.
|
||||
* `serviceID` - The ID of the resource.
|
||||
|
||||
|
||||
## Example
|
||||
|
||||
The following snippet contains sample Pagerduty service configuration:
|
||||
The following snippet contains sample PagerDuty service configuration:
|
||||
|
||||
```yaml
|
||||
apiVersion: v1
|
||||
@@ -35,7 +35,7 @@ data:
|
||||
|
||||
## Template
|
||||
|
||||
[Notification templates](../templates.md) support specifying subject for pagerduty notifications:
|
||||
[Notification templates](../templates.md) support specifying subject for PagerDuty notifications:
|
||||
|
||||
```yaml
|
||||
apiVersion: v1
|
||||
@@ -62,5 +62,5 @@ apiVersion: argoproj.io/v1alpha1
|
||||
kind: Rollout
|
||||
metadata:
|
||||
annotations:
|
||||
notifications.argoproj.io/subscribe.on-rollout-aborted.pagerduty: "<serviceID for Pagerduty>"
|
||||
notifications.argoproj.io/subscribe.on-rollout-aborted.pagerduty: "<serviceID for PagerDuty>"
|
||||
```
|
||||
|
||||
@@ -74,5 +74,5 @@ apiVersion: argoproj.io/v1alpha1
|
||||
kind: Rollout
|
||||
metadata:
|
||||
annotations:
|
||||
notifications.argoproj.io/subscribe.on-rollout-aborted.pagerdutyv2: "<serviceID for Pagerduty>"
|
||||
notifications.argoproj.io/subscribe.on-rollout-aborted.pagerdutyv2: "<serviceID for PagerDuty>"
|
||||
```
|
||||
|
||||
@@ -15,71 +15,72 @@ argocd-application-controller [flags]
|
||||
### Options
|
||||
|
||||
```
|
||||
--app-hard-resync int Time period in seconds for application hard resync.
|
||||
--app-resync int Time period in seconds for application resync. (default 180)
|
||||
--app-resync-jitter int Maximum time period in seconds to add as a delay jitter for application resync.
|
||||
--app-state-cache-expiration duration Cache expiration for app state (default 1h0m0s)
|
||||
--application-namespaces strings List of additional namespaces that applications are allowed to be reconciled from
|
||||
--as string Username to impersonate for the operation
|
||||
--as-group stringArray Group to impersonate for the operation, this flag can be repeated to specify multiple groups.
|
||||
--as-uid string UID to impersonate for the operation
|
||||
--certificate-authority string Path to a cert file for the certificate authority
|
||||
--client-certificate string Path to a client certificate file for TLS
|
||||
--client-key string Path to a client key file for TLS
|
||||
--cluster string The name of the kubeconfig cluster to use
|
||||
--context string The name of the kubeconfig context to use
|
||||
--default-cache-expiration duration Cache expiration default (default 24h0m0s)
|
||||
--disable-compression If true, opt-out of response compression for all requests to the server
|
||||
--dynamic-cluster-distribution-enabled Enables dynamic cluster distribution.
|
||||
--gloglevel int Set the glog logging level
|
||||
-h, --help help for argocd-application-controller
|
||||
--insecure-skip-tls-verify If true, the server's certificate will not be checked for validity. This will make your HTTPS connections insecure
|
||||
--kubeconfig string Path to a kube config. Only required if out-of-cluster
|
||||
--kubectl-parallelism-limit int Number of allowed concurrent kubectl fork/execs. Any value less than 1 means no limit. (default 20)
|
||||
--logformat string Set the logging format. One of: text|json (default "text")
|
||||
--loglevel string Set the logging level. One of: debug|info|warn|error (default "info")
|
||||
--metrics-application-labels strings List of Application labels that will be added to the argocd_application_labels metric
|
||||
--metrics-cache-expiration duration Prometheus metrics cache expiration (disabled by default. e.g. 24h0m0s)
|
||||
--metrics-port int Start metrics server on given port (default 8082)
|
||||
-n, --namespace string If present, the namespace scope for this CLI request
|
||||
--operation-processors int Number of application operation processors (default 10)
|
||||
--otlp-address string OpenTelemetry collector address to send traces to
|
||||
--otlp-attrs strings List of OpenTelemetry collector extra attrs when send traces, each attribute is separated by a colon(e.g. key:value)
|
||||
--otlp-headers stringToString List of OpenTelemetry collector extra headers sent with traces, headers are comma-separated key-value pairs(e.g. key1=value1,key2=value2) (default [])
|
||||
--otlp-insecure OpenTelemetry collector insecure mode (default true)
|
||||
--password string Password for basic authentication to the API server
|
||||
--persist-resource-health Enables storing the managed resources health in the Application CRD (default true)
|
||||
--proxy-url string If provided, this URL will be used to connect via proxy
|
||||
--redis string Redis server hostname and port (e.g. argocd-redis:6379).
|
||||
--redis-ca-certificate string Path to Redis server CA certificate (e.g. /etc/certs/redis/ca.crt). If not specified, system trusted CAs will be used for server certificate validation.
|
||||
--redis-client-certificate string Path to Redis client certificate (e.g. /etc/certs/redis/client.crt).
|
||||
--redis-client-key string Path to Redis client key (e.g. /etc/certs/redis/client.crt).
|
||||
--redis-compress string Enable compression for data sent to Redis with the required compression algorithm. (possible values: gzip, none) (default "gzip")
|
||||
--redis-insecure-skip-tls-verify Skip Redis server certificate validation.
|
||||
--redis-use-tls Use TLS when connecting to Redis.
|
||||
--redisdb int Redis database.
|
||||
--repo-error-grace-period-seconds int Grace period in seconds for ignoring consecutive errors while communicating with repo server. (default 180)
|
||||
--repo-server string Repo server address. (default "argocd-repo-server:8081")
|
||||
--repo-server-plaintext Disable TLS on connections to repo server
|
||||
--repo-server-strict-tls Whether to use strict validation of the TLS cert presented by the repo server
|
||||
--repo-server-timeout-seconds int Repo server RPC call timeout seconds. (default 60)
|
||||
--request-timeout string The length of time to wait before giving up on a single server request. Non-zero values should contain a corresponding time unit (e.g. 1s, 2m, 3h). A value of zero means don't timeout requests. (default "0")
|
||||
--self-heal-timeout-seconds int Specifies timeout between application self heal attempts (default 5)
|
||||
--sentinel stringArray Redis sentinel hostname and port (e.g. argocd-redis-ha-announce-0:6379).
|
||||
--sentinelmaster string Redis sentinel master group name. (default "master")
|
||||
--server string The address and port of the Kubernetes API server
|
||||
--server-side-diff-enabled Feature flag to enable ServerSide diff. Default ("false")
|
||||
--sharding-method string Enables choice of sharding method. Supported sharding methods are : [legacy, round-robin] (default "legacy")
|
||||
--status-processors int Number of application status processors (default 20)
|
||||
--tls-server-name string If provided, this name will be used to validate server certificate. If this is not provided, hostname used to contact the server is used.
|
||||
--token string Bearer token for authentication to the API server
|
||||
--user string The name of the kubeconfig user to use
|
||||
--username string Username for basic authentication to the API server
|
||||
--wq-backoff-factor float Set Workqueue Per Item Rate Limiter Backoff Factor, default is 1.5 (default 1.5)
|
||||
--wq-basedelay-ns duration Set Workqueue Per Item Rate Limiter Base Delay duration in nanoseconds, default 1000000 (1ms) (default 1ms)
|
||||
--wq-bucket-qps int Set Workqueue Rate Limiter Bucket QPS, default 50 (default 50)
|
||||
--wq-bucket-size int Set Workqueue Rate Limiter Bucket Size, default 500 (default 500)
|
||||
--wq-cooldown-ns duration Set Workqueue Per Item Rate Limiter Cooldown duration in ns, default 0(per item rate limiter disabled)
|
||||
--wq-maxdelay-ns duration Set Workqueue Per Item Rate Limiter Max Delay duration in nanoseconds, default 1000000000 (1s) (default 1s)
|
||||
--app-hard-resync int Time period in seconds for application hard resync.
|
||||
--app-resync int Time period in seconds for application resync. (default 180)
|
||||
--app-resync-jitter int Maximum time period in seconds to add as a delay jitter for application resync.
|
||||
--app-state-cache-expiration duration Cache expiration for app state (default 1h0m0s)
|
||||
--application-namespaces strings List of additional namespaces that applications are allowed to be reconciled from
|
||||
--as string Username to impersonate for the operation
|
||||
--as-group stringArray Group to impersonate for the operation, this flag can be repeated to specify multiple groups.
|
||||
--as-uid string UID to impersonate for the operation
|
||||
--certificate-authority string Path to a cert file for the certificate authority
|
||||
--client-certificate string Path to a client certificate file for TLS
|
||||
--client-key string Path to a client key file for TLS
|
||||
--cluster string The name of the kubeconfig cluster to use
|
||||
--context string The name of the kubeconfig context to use
|
||||
--default-cache-expiration duration Cache expiration default (default 24h0m0s)
|
||||
--disable-compression If true, opt-out of response compression for all requests to the server
|
||||
--dynamic-cluster-distribution-enabled Enables dynamic cluster distribution.
|
||||
--gloglevel int Set the glog logging level
|
||||
-h, --help help for argocd-application-controller
|
||||
--ignore-normalizer-jq-execution-timeout-seconds duration Set ignore normalizer JQ execution timeout
|
||||
--insecure-skip-tls-verify If true, the server's certificate will not be checked for validity. This will make your HTTPS connections insecure
|
||||
--kubeconfig string Path to a kube config. Only required if out-of-cluster
|
||||
--kubectl-parallelism-limit int Number of allowed concurrent kubectl fork/execs. Any value less than 1 means no limit. (default 20)
|
||||
--logformat string Set the logging format. One of: text|json (default "text")
|
||||
--loglevel string Set the logging level. One of: debug|info|warn|error (default "info")
|
||||
--metrics-application-labels strings List of Application labels that will be added to the argocd_application_labels metric
|
||||
--metrics-cache-expiration duration Prometheus metrics cache expiration (disabled by default. e.g. 24h0m0s)
|
||||
--metrics-port int Start metrics server on given port (default 8082)
|
||||
-n, --namespace string If present, the namespace scope for this CLI request
|
||||
--operation-processors int Number of application operation processors (default 10)
|
||||
--otlp-address string OpenTelemetry collector address to send traces to
|
||||
--otlp-attrs strings List of OpenTelemetry collector extra attrs when send traces, each attribute is separated by a colon(e.g. key:value)
|
||||
--otlp-headers stringToString List of OpenTelemetry collector extra headers sent with traces, headers are comma-separated key-value pairs(e.g. key1=value1,key2=value2) (default [])
|
||||
--otlp-insecure OpenTelemetry collector insecure mode (default true)
|
||||
--password string Password for basic authentication to the API server
|
||||
--persist-resource-health Enables storing the managed resources health in the Application CRD (default true)
|
||||
--proxy-url string If provided, this URL will be used to connect via proxy
|
||||
--redis string Redis server hostname and port (e.g. argocd-redis:6379).
|
||||
--redis-ca-certificate string Path to Redis server CA certificate (e.g. /etc/certs/redis/ca.crt). If not specified, system trusted CAs will be used for server certificate validation.
|
||||
--redis-client-certificate string Path to Redis client certificate (e.g. /etc/certs/redis/client.crt).
|
||||
--redis-client-key string Path to Redis client key (e.g. /etc/certs/redis/client.crt).
|
||||
--redis-compress string Enable compression for data sent to Redis with the required compression algorithm. (possible values: gzip, none) (default "gzip")
|
||||
--redis-insecure-skip-tls-verify Skip Redis server certificate validation.
|
||||
--redis-use-tls Use TLS when connecting to Redis.
|
||||
--redisdb int Redis database.
|
||||
--repo-error-grace-period-seconds int Grace period in seconds for ignoring consecutive errors while communicating with repo server. (default 180)
|
||||
--repo-server string Repo server address. (default "argocd-repo-server:8081")
|
||||
--repo-server-plaintext Disable TLS on connections to repo server
|
||||
--repo-server-strict-tls Whether to use strict validation of the TLS cert presented by the repo server
|
||||
--repo-server-timeout-seconds int Repo server RPC call timeout seconds. (default 60)
|
||||
--request-timeout string The length of time to wait before giving up on a single server request. Non-zero values should contain a corresponding time unit (e.g. 1s, 2m, 3h). A value of zero means don't timeout requests. (default "0")
|
||||
--self-heal-timeout-seconds int Specifies timeout between application self heal attempts (default 5)
|
||||
--sentinel stringArray Redis sentinel hostname and port (e.g. argocd-redis-ha-announce-0:6379).
|
||||
--sentinelmaster string Redis sentinel master group name. (default "master")
|
||||
--server string The address and port of the Kubernetes API server
|
||||
--server-side-diff-enabled Feature flag to enable ServerSide diff. Default ("false")
|
||||
--sharding-method string Enables choice of sharding method. Supported sharding methods are : [legacy, round-robin] (default "legacy")
|
||||
--status-processors int Number of application status processors (default 20)
|
||||
--tls-server-name string If provided, this name will be used to validate server certificate. If this is not provided, hostname used to contact the server is used.
|
||||
--token string Bearer token for authentication to the API server
|
||||
--user string The name of the kubeconfig user to use
|
||||
--username string Username for basic authentication to the API server
|
||||
--wq-backoff-factor float Set Workqueue Per Item Rate Limiter Backoff Factor, default is 1.5 (default 1.5)
|
||||
--wq-basedelay-ns duration Set Workqueue Per Item Rate Limiter Base Delay duration in nanoseconds, default 1000000 (1ms) (default 1ms)
|
||||
--wq-bucket-qps float Set Workqueue Rate Limiter Bucket QPS, default set to MaxFloat64 which disables the bucket limiter (default 1.7976931348623157e+308)
|
||||
--wq-bucket-size int Set Workqueue Rate Limiter Bucket Size, default 500 (default 500)
|
||||
--wq-cooldown-ns duration Set Workqueue Per Item Rate Limiter Cooldown duration in ns, default 0(per item rate limiter disabled)
|
||||
--wq-maxdelay-ns duration Set Workqueue Per Item Rate Limiter Max Delay duration in nanoseconds, default 1000000000 (1s) (default 1s)
|
||||
```
|
||||
|
||||
|
||||
@@ -21,6 +21,7 @@ argocd-repo-server [flags]
|
||||
--disable-helm-manifest-max-extracted-size Disable maximum size of helm manifest archives when extracted
|
||||
--disable-tls Disable TLS on the gRPC endpoint
|
||||
--helm-manifest-max-extracted-size string Maximum size of helm manifest archives when extracted (default "1G")
|
||||
--helm-registry-max-index-size string Maximum size of registry index file (default "1G")
|
||||
-h, --help help for argocd-repo-server
|
||||
--logformat string Set the logging format. One of: text|json (default "text")
|
||||
--loglevel string Set the logging level. One of: debug|info|warn|error (default "info")
|
||||
|
||||
@@ -3,3 +3,56 @@
|
||||
## Upgraded Kustomize Version
|
||||
|
||||
Note that bundled Kustomize version has been upgraded from 5.1.0 to 5.2.1.
|
||||
|
||||
## Egress NetworkPolicy for `argocd-redis` and `argocd-redis-ha-haproxy`
|
||||
|
||||
Starting with Argo CD 2.9.16, the NetworkPolicy for the `argocd-redis` and `argocd-redis-ha-haproxy` dropped Egress restrictions. This change was made
|
||||
to allow access to the Kubernetes API to create a secret to secure Redis access.
|
||||
|
||||
To retain similar networking restrictions as before 2.9.16, you can add an Egress rule to allow access only to the
|
||||
Kubernetes API and access needed by Redis itself. The Egress rule for Kubernetes access will depend entirely on your
|
||||
Kubernetes setup. The access for Redis itself can be allowed by adding the following to the
|
||||
`argocd-redis-network-policy` NetworkPolicy:
|
||||
|
||||
```diff
|
||||
kind: NetworkPolicy
|
||||
apiVersion: networking.k8s.io/v1
|
||||
metadata:
|
||||
name: argocd-redis-network-policy
|
||||
spec:
|
||||
policyTypes:
|
||||
- Ingress
|
||||
+ - Egress
|
||||
+ egress:
|
||||
+ - ports:
|
||||
+ - port: 53
|
||||
+ protocol: UDP
|
||||
+ - port: 53
|
||||
+ protocol: TCP
|
||||
```
|
||||
|
||||
```diff
|
||||
kind: NetworkPolicy
|
||||
apiVersion: networking.k8s.io/v1
|
||||
metadata:
|
||||
name: argocd-redis-ha-haproxy
|
||||
spec:
|
||||
policyTypes:
|
||||
- Ingress
|
||||
+ - Egress
|
||||
+ egress:
|
||||
+ - ports:
|
||||
+ - port: 6379
|
||||
+ protocol: TCP
|
||||
+ - port: 26379
|
||||
+ protocol: TCP
|
||||
+ to:
|
||||
+ - podSelector:
|
||||
+ matchLabels:
|
||||
+ app.kubernetes.io/name: argocd-redis-ha
|
||||
+ - ports:
|
||||
+ - port: 53
|
||||
+ protocol: UDP
|
||||
+ - port: 53
|
||||
+ protocol: TCP
|
||||
```
|
||||
@@ -13,4 +13,66 @@ before enabling `managedNamespaceMetadata` on an existing namespace.
|
||||
|
||||
## Upgraded Helm Version
|
||||
|
||||
Note that bundled Helm version has been upgraded from 3.13.2 to 3.14.0.
|
||||
Note that bundled Helm version has been upgraded from 3.13.2 to 3.14.3.
|
||||
|
||||
## Egress NetworkPolicy for `argocd-redis` and `argocd-redis-ha-haproxy`
|
||||
|
||||
Starting with Argo CD 2.10.11, the NetworkPolicy for the `argocd-redis` and `argocd-redis-ha-haproxy` dropped Egress restrictions. This change was made
|
||||
to allow access to the Kubernetes API to create a secret to secure Redis access.
|
||||
|
||||
To retain similar networking restrictions as before 2.10.11, you can add an Egress rule to allow access only to the
|
||||
Kubernetes API and access needed by Redis itself. The Egress rule for Kubernetes access will depend entirely on your
|
||||
Kubernetes setup. The access for Redis itself can be allowed by adding the following to the
|
||||
`argocd-redis-network-policy` NetworkPolicy:
|
||||
|
||||
```diff
|
||||
kind: NetworkPolicy
|
||||
apiVersion: networking.k8s.io/v1
|
||||
metadata:
|
||||
name: argocd-redis-network-policy
|
||||
spec:
|
||||
policyTypes:
|
||||
- Ingress
|
||||
+ - Egress
|
||||
+ egress:
|
||||
+ - ports:
|
||||
+ - port: 53
|
||||
+ protocol: UDP
|
||||
+ - port: 53
|
||||
+ protocol: TCP
|
||||
```
|
||||
|
||||
```diff
|
||||
kind: NetworkPolicy
|
||||
apiVersion: networking.k8s.io/v1
|
||||
metadata:
|
||||
name: argocd-redis-ha-haproxy
|
||||
spec:
|
||||
policyTypes:
|
||||
- Ingress
|
||||
+ - Egress
|
||||
+ egress:
|
||||
+ - ports:
|
||||
+ - port: 6379
|
||||
+ protocol: TCP
|
||||
+ - port: 26379
|
||||
+ protocol: TCP
|
||||
+ to:
|
||||
+ - podSelector:
|
||||
+ matchLabels:
|
||||
+ app.kubernetes.io/name: argocd-redis-ha
|
||||
+ - ports:
|
||||
+ - port: 53
|
||||
+ protocol: UDP
|
||||
+ - port: 53
|
||||
+ protocol: TCP
|
||||
```
|
||||
|
||||
## Sanitized project API response
|
||||
|
||||
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.
|
||||
|
||||
> **Note:** The 2.10 series has been EOL for some time and has not received security updates. 2.10.18 was patched for critical
|
||||
> CVE-2025-55190 but was not patched for other vulnerabilities. It is important to upgrade to a supported version as quickly as possible.
|
||||
|
||||
@@ -19,6 +19,8 @@ URL configured in the Git provider should use the `/api/webhook` endpoint of you
|
||||
(e.g. `https://argocd.example.com/api/webhook`). If you wish to use a shared secret, input an
|
||||
arbitrary value in the secret. This value will be used when configuring the webhook in the next step.
|
||||
|
||||
To prevent DDoS attacks with unauthenticated webhook events (the `/api/webhook` endpoint currently lacks rate limiting protection), it is recommended to limit the payload size. You can achieve this by configuring the `argocd-cm` ConfigMap with the `webhook.maxPayloadSizeMB` attribute. The default value is 1GB.
|
||||
|
||||
## Github
|
||||
|
||||

|
||||
|
||||
@@ -139,6 +139,7 @@ $ argocd admin initial-password reset
|
||||
* [argocd admin initial-password](argocd_admin_initial-password.md) - Prints initial password to log in to Argo CD for the first time
|
||||
* [argocd admin notifications](argocd_admin_notifications.md) - Set of CLI commands that helps manage notifications settings
|
||||
* [argocd admin proj](argocd_admin_proj.md) - Manage projects configuration
|
||||
* [argocd admin redis-initial-password](argocd_admin_redis-initial-password.md) - Ensure the Redis password exists, creating a new one if necessary.
|
||||
* [argocd admin repo](argocd_admin_repo.md) - Manage repositories configuration
|
||||
* [argocd admin settings](argocd_admin_settings.md) - Provides set of commands for settings validation and troubleshooting
|
||||
|
||||
|
||||
@@ -11,32 +11,33 @@ argocd admin app get-reconcile-results PATH [flags]
|
||||
### Options
|
||||
|
||||
```
|
||||
--as string Username to impersonate for the operation
|
||||
--as-group stringArray Group to impersonate for the operation, this flag can be repeated to specify multiple groups.
|
||||
--as-uid string UID to impersonate for the operation
|
||||
--certificate-authority string Path to a cert file for the certificate authority
|
||||
--client-certificate string Path to a client certificate file for TLS
|
||||
--client-key string Path to a client key file for TLS
|
||||
--cluster string The name of the kubeconfig cluster to use
|
||||
--context string The name of the kubeconfig context to use
|
||||
--disable-compression If true, opt-out of response compression for all requests to the server
|
||||
-h, --help help for get-reconcile-results
|
||||
--insecure-skip-tls-verify If true, the server's certificate will not be checked for validity. This will make your HTTPS connections insecure
|
||||
--kubeconfig string Path to a kube config. Only required if out-of-cluster
|
||||
--l string Label selector
|
||||
-n, --namespace string If present, the namespace scope for this CLI request
|
||||
--o string Output format (yaml|json) (default "yaml")
|
||||
--password string Password for basic authentication to the API server
|
||||
--proxy-url string If provided, this URL will be used to connect via proxy
|
||||
--refresh If set to true then recalculates apps reconciliation
|
||||
--repo-server string Repo server address.
|
||||
--request-timeout string The length of time to wait before giving up on a single server request. Non-zero values should contain a corresponding time unit (e.g. 1s, 2m, 3h). A value of zero means don't timeout requests. (default "0")
|
||||
--server string The address and port of the Kubernetes API server
|
||||
--server-side-diff If set to "true" will use server-side diff while comparing resources. Default ("false")
|
||||
--tls-server-name string If provided, this name will be used to validate server certificate. If this is not provided, hostname used to contact the server is used.
|
||||
--token string Bearer token for authentication to the API server
|
||||
--user string The name of the kubeconfig user to use
|
||||
--username string Username for basic authentication to the API server
|
||||
--as string Username to impersonate for the operation
|
||||
--as-group stringArray Group to impersonate for the operation, this flag can be repeated to specify multiple groups.
|
||||
--as-uid string UID to impersonate for the operation
|
||||
--certificate-authority string Path to a cert file for the certificate authority
|
||||
--client-certificate string Path to a client certificate file for TLS
|
||||
--client-key string Path to a client key file for TLS
|
||||
--cluster string The name of the kubeconfig cluster to use
|
||||
--context string The name of the kubeconfig context to use
|
||||
--disable-compression If true, opt-out of response compression for all requests to the server
|
||||
-h, --help help for get-reconcile-results
|
||||
--ignore-normalizer-jq-execution-timeout duration Set ignore normalizer JQ execution timeout (default 1s)
|
||||
--insecure-skip-tls-verify If true, the server's certificate will not be checked for validity. This will make your HTTPS connections insecure
|
||||
--kubeconfig string Path to a kube config. Only required if out-of-cluster
|
||||
--l string Label selector
|
||||
-n, --namespace string If present, the namespace scope for this CLI request
|
||||
--o string Output format (yaml|json) (default "yaml")
|
||||
--password string Password for basic authentication to the API server
|
||||
--proxy-url string If provided, this URL will be used to connect via proxy
|
||||
--refresh If set to true then recalculates apps reconciliation
|
||||
--repo-server string Repo server address.
|
||||
--request-timeout string The length of time to wait before giving up on a single server request. Non-zero values should contain a corresponding time unit (e.g. 1s, 2m, 3h). A value of zero means don't timeout requests. (default "0")
|
||||
--server string The address and port of the Kubernetes API server
|
||||
--server-side-diff If set to "true" will use server-side diff while comparing resources. Default ("false")
|
||||
--tls-server-name string If provided, this name will be used to validate server certificate. If this is not provided, hostname used to contact the server is used.
|
||||
--token string Bearer token for authentication to the API server
|
||||
--user string The name of the kubeconfig user to use
|
||||
--username string Username for basic authentication to the API server
|
||||
```
|
||||
|
||||
### Options inherited from parent commands
|
||||
|
||||
@@ -0,0 +1,67 @@
|
||||
# `argocd admin redis-initial-password` Command Reference
|
||||
|
||||
## argocd admin redis-initial-password
|
||||
|
||||
Ensure the Redis password exists, creating a new one if necessary.
|
||||
|
||||
```
|
||||
argocd admin redis-initial-password [flags]
|
||||
```
|
||||
|
||||
### Options
|
||||
|
||||
```
|
||||
--as string Username to impersonate for the operation
|
||||
--as-group stringArray Group to impersonate for the operation, this flag can be repeated to specify multiple groups.
|
||||
--as-uid string UID to impersonate for the operation
|
||||
--certificate-authority string Path to a cert file for the certificate authority
|
||||
--client-certificate string Path to a client certificate file for TLS
|
||||
--client-key string Path to a client key file for TLS
|
||||
--cluster string The name of the kubeconfig cluster to use
|
||||
--context string The name of the kubeconfig context to use
|
||||
--disable-compression If true, opt-out of response compression for all requests to the server
|
||||
-h, --help help for redis-initial-password
|
||||
--insecure-skip-tls-verify If true, the server's certificate will not be checked for validity. This will make your HTTPS connections insecure
|
||||
--kubeconfig string Path to a kube config. Only required if out-of-cluster
|
||||
-n, --namespace string If present, the namespace scope for this CLI request
|
||||
--password string Password for basic authentication to the API server
|
||||
--proxy-url string If provided, this URL will be used to connect via proxy
|
||||
--request-timeout string The length of time to wait before giving up on a single server request. Non-zero values should contain a corresponding time unit (e.g. 1s, 2m, 3h). A value of zero means don't timeout requests. (default "0")
|
||||
--server string The address and port of the Kubernetes API server
|
||||
--tls-server-name string If provided, this name will be used to validate server certificate. If this is not provided, hostname used to contact the server is used.
|
||||
--token string Bearer token for authentication to the API server
|
||||
--user string The name of the kubeconfig user to use
|
||||
--username string Username for basic authentication to the API server
|
||||
```
|
||||
|
||||
### Options inherited from parent commands
|
||||
|
||||
```
|
||||
--auth-token string Authentication token
|
||||
--client-crt string Client certificate file
|
||||
--client-crt-key string Client certificate key file
|
||||
--config string Path to Argo CD config (default "/home/user/.config/argocd/config")
|
||||
--controller-name string Name of the Argo CD Application controller; set this or the ARGOCD_APPLICATION_CONTROLLER_NAME environment variable when the controller's name label differs from the default, for example when installing via the Helm chart (default "argocd-application-controller")
|
||||
--core If set to true then CLI talks directly to Kubernetes instead of talking to Argo CD API server
|
||||
--grpc-web Enables gRPC-web protocol. Useful if Argo CD server is behind proxy which does not support HTTP2.
|
||||
--grpc-web-root-path string Enables gRPC-web protocol. Useful if Argo CD server is behind proxy which does not support HTTP2. Set web root.
|
||||
-H, --header strings Sets additional header to all requests made by Argo CD CLI. (Can be repeated multiple times to add multiple headers, also supports comma separated headers)
|
||||
--http-retry-max int Maximum number of retries to establish http connection to Argo CD server
|
||||
--insecure Skip server certificate and domain verification
|
||||
--kube-context string Directs the command to the given kube-context
|
||||
--logformat string Set the logging format. One of: text|json (default "text")
|
||||
--loglevel string Set the logging level. One of: debug|info|warn|error (default "info")
|
||||
--plaintext Disable TLS
|
||||
--port-forward Connect to a random argocd-server port using port forwarding
|
||||
--port-forward-namespace string Namespace name which should be used for port forwarding
|
||||
--redis-haproxy-name string Name of the Redis HA Proxy; set this or the ARGOCD_REDIS_HAPROXY_NAME environment variable when the HA Proxy's name label differs from the default, for example when installing via the Helm chart (default "argocd-redis-ha-haproxy")
|
||||
--redis-name string Name of the Redis deployment; set this or the ARGOCD_REDIS_NAME environment variable when the Redis's name label differs from the default, for example when installing via the Helm chart (default "argocd-redis")
|
||||
--repo-server-name string Name of the Argo CD Repo server; set this or the ARGOCD_REPO_SERVER_NAME environment variable when the server's name label differs from the default, for example when installing via the Helm chart (default "argocd-repo-server")
|
||||
--server-crt string Server certificate file
|
||||
--server-name string Name of the Argo CD API server; set this or the ARGOCD_SERVER_NAME environment variable when the server's name label differs from the default, for example when installing via the Helm chart (default "argocd-server")
|
||||
```
|
||||
|
||||
### SEE ALSO
|
||||
|
||||
* [argocd admin](argocd_admin.md) - Contains a set of commands useful for Argo CD administrators and requires direct Kubernetes access
|
||||
|
||||
@@ -22,7 +22,8 @@ argocd admin settings resource-overrides ignore-resource-updates ./deploy.yaml -
|
||||
### Options
|
||||
|
||||
```
|
||||
-h, --help help for ignore-resource-updates
|
||||
-h, --help help for ignore-resource-updates
|
||||
--ignore-normalizer-jq-execution-timeout duration Set ignore normalizer JQ execution timeout (default 1s)
|
||||
```
|
||||
|
||||
### Options inherited from parent commands
|
||||
|
||||
@@ -17,15 +17,16 @@ argocd app diff APPNAME [flags]
|
||||
### Options
|
||||
|
||||
```
|
||||
--exit-code Return non-zero exit code when there is a diff (default true)
|
||||
--hard-refresh Refresh application data as well as target manifests cache
|
||||
-h, --help help for diff
|
||||
--local string Compare live app to a local manifests
|
||||
--local-include stringArray Used with --server-side-generate, specify patterns of filenames to send. Matching is based on filename and not path. (default [*.yaml,*.yml,*.json])
|
||||
--local-repo-root string Path to the repository root. Used together with --local allows setting the repository root (default "/")
|
||||
--refresh Refresh application data when retrieving
|
||||
--revision string Compare live app to a particular revision
|
||||
--server-side-generate Used with --local, this will send your manifests to the server for diffing
|
||||
--exit-code Return non-zero exit code when there is a diff (default true)
|
||||
--hard-refresh Refresh application data as well as target manifests cache
|
||||
-h, --help help for diff
|
||||
--ignore-normalizer-jq-execution-timeout duration Set ignore normalizer JQ execution timeout (default 1s)
|
||||
--local string Compare live app to a local manifests
|
||||
--local-include stringArray Used with --server-side-generate, specify patterns of filenames to send. Matching is based on filename and not path. (default [*.yaml,*.yml,*.json])
|
||||
--local-repo-root string Path to the repository root. Used together with --local allows setting the repository root (default "/")
|
||||
--refresh Refresh application data when retrieving
|
||||
--revision string Compare live app to a particular revision
|
||||
--server-side-generate Used with --local, this will send your manifests to the server for diffing
|
||||
```
|
||||
|
||||
### Options inherited from parent commands
|
||||
|
||||
@@ -38,31 +38,32 @@ argocd app sync [APPNAME... | -l selector | --project project-name] [flags]
|
||||
### Options
|
||||
|
||||
```
|
||||
--apply-out-of-sync-only Sync only out-of-sync resources
|
||||
--assumeYes Assume yes as answer for all user queries or prompts
|
||||
--async Do not wait for application to sync before continuing
|
||||
--dry-run Preview apply without affecting cluster
|
||||
--force Use a force apply
|
||||
-h, --help help for sync
|
||||
--info stringArray A list of key-value pairs during sync process. These infos will be persisted in app.
|
||||
--label stringArray Sync only specific resources with a label. This option may be specified repeatedly.
|
||||
--local string Path to a local directory. When this flag is present no git queries will be made
|
||||
--local-repo-root string Path to the repository root. Used together with --local allows setting the repository root (default "/")
|
||||
-o, --output string Output format. One of: json|yaml|wide|tree|tree=detailed (default "wide")
|
||||
--preview-changes Preview difference against the target and live state before syncing app and wait for user confirmation
|
||||
--project stringArray Sync apps that belong to the specified projects. This option may be specified repeatedly.
|
||||
--prune Allow deleting unexpected resources
|
||||
--replace Use a kubectl create/replace instead apply
|
||||
--resource stringArray Sync only specific resources as GROUP:KIND:NAME or !GROUP:KIND:NAME. Fields may be blank and '*' can be used. This option may be specified repeatedly
|
||||
--retry-backoff-duration duration Retry backoff base duration. Input needs to be a duration (e.g. 2m, 1h) (default 5s)
|
||||
--retry-backoff-factor int Factor multiplies the base duration after each failed retry (default 2)
|
||||
--retry-backoff-max-duration duration Max retry backoff duration. Input needs to be a duration (e.g. 2m, 1h) (default 3m0s)
|
||||
--retry-limit int Max number of allowed sync retries
|
||||
--revision string Sync to a specific revision. Preserves parameter overrides
|
||||
-l, --selector string Sync apps that match this label. Supports '=', '==', '!=', in, notin, exists & not exists. Matching apps must satisfy all of the specified label constraints.
|
||||
--server-side Use server-side apply while syncing the application
|
||||
--strategy string Sync strategy (one of: apply|hook)
|
||||
--timeout uint Time out after this many seconds
|
||||
--apply-out-of-sync-only Sync only out-of-sync resources
|
||||
--assumeYes Assume yes as answer for all user queries or prompts
|
||||
--async Do not wait for application to sync before continuing
|
||||
--dry-run Preview apply without affecting cluster
|
||||
--force Use a force apply
|
||||
-h, --help help for sync
|
||||
--ignore-normalizer-jq-execution-timeout duration Set ignore normalizer JQ execution timeout (default 1s)
|
||||
--info stringArray A list of key-value pairs during sync process. These infos will be persisted in app.
|
||||
--label stringArray Sync only specific resources with a label. This option may be specified repeatedly.
|
||||
--local string Path to a local directory. When this flag is present no git queries will be made
|
||||
--local-repo-root string Path to the repository root. Used together with --local allows setting the repository root (default "/")
|
||||
-o, --output string Output format. One of: json|yaml|wide|tree|tree=detailed (default "wide")
|
||||
--preview-changes Preview difference against the target and live state before syncing app and wait for user confirmation
|
||||
--project stringArray Sync apps that belong to the specified projects. This option may be specified repeatedly.
|
||||
--prune Allow deleting unexpected resources
|
||||
--replace Use a kubectl create/replace instead apply
|
||||
--resource stringArray Sync only specific resources as GROUP:KIND:NAME or !GROUP:KIND:NAME. Fields may be blank and '*' can be used. This option may be specified repeatedly
|
||||
--retry-backoff-duration duration Retry backoff base duration. Input needs to be a duration (e.g. 2m, 1h) (default 5s)
|
||||
--retry-backoff-factor int Factor multiplies the base duration after each failed retry (default 2)
|
||||
--retry-backoff-max-duration duration Max retry backoff duration. Input needs to be a duration (e.g. 2m, 1h) (default 3m0s)
|
||||
--retry-limit int Max number of allowed sync retries
|
||||
--revision string Sync to a specific revision. Preserves parameter overrides
|
||||
-l, --selector string Sync apps that match this label. Supports '=', '==', '!=', in, notin, exists & not exists. Matching apps must satisfy all of the specified label constraints.
|
||||
--server-side Use server-side apply while syncing the application
|
||||
--strategy string Sync strategy (one of: apply|hook)
|
||||
--timeout uint Time out after this many seconds
|
||||
```
|
||||
|
||||
### Options inherited from parent commands
|
||||
|
||||
@@ -185,3 +185,16 @@ The list of supported Kubernetes types is available in [diffing_known_types.txt]
|
||||
|
||||
* `core/Quantity`
|
||||
* `meta/v1/duration`
|
||||
|
||||
|
||||
### JQ Path expression timeout
|
||||
|
||||
By default, the evaluation of a JQPathExpression is limited to one second. If you encounter a "JQ patch execution timed out" error message due to a complex JQPathExpression that requires more time to evaluate, you can extend the timeout period by configuring the `ignore.normalizer.jq.timeout` setting within the `argocd-cmd-params-cm` ConfigMap.
|
||||
|
||||
```yaml
|
||||
apiVersion: v1
|
||||
kind: ConfigMap
|
||||
metadata:
|
||||
name: argocd-cmd-params-cm
|
||||
data:
|
||||
ignore.normalizer.jq.timeout: "5s"
|
||||
|
||||
@@ -165,6 +165,21 @@ metadata:
|
||||
argocd.argoproj.io/sync-options: Replace=true
|
||||
```
|
||||
|
||||
## Force Sync
|
||||
|
||||
For certain resources you might want to delete and recreate. e.g. job resources that should run every time when syncing.
|
||||
|
||||
!!! warning
|
||||
During the sync process, the resources will be synchronized using the 'kubectl delete/create' command.
|
||||
This sync option has a destructive action, which could cause an outage for your application.
|
||||
|
||||
In such cases you might use `Force=true` sync option in target resources annotation:
|
||||
```yaml
|
||||
metadata:
|
||||
annotations:
|
||||
argocd.argoproj.io/sync-options: Force=true,Replace=true
|
||||
```
|
||||
|
||||
## Server-Side Apply
|
||||
|
||||
This option enables Kubernetes
|
||||
|
||||
62
go.mod
62
go.mod
@@ -13,10 +13,10 @@ require (
|
||||
github.com/TomOnTime/utfutil v0.0.0-20180511104225-09c41003ee1d
|
||||
github.com/alicebob/miniredis/v2 v2.30.4
|
||||
github.com/antonmedv/expr v1.15.2
|
||||
github.com/argoproj/gitops-engine v0.7.1-0.20240122213038-792124280fcc
|
||||
github.com/argoproj/notifications-engine v0.4.1-0.20231027194313-a8d185ecc0a9
|
||||
github.com/argoproj/gitops-engine v0.7.1-0.20240715141017-b6ec82aedce5
|
||||
github.com/argoproj/notifications-engine v0.4.1-0.20240126143042-84b9f7913604
|
||||
github.com/argoproj/pkg v0.13.7-0.20230626144333-d56162821bd1
|
||||
github.com/aws/aws-sdk-go v1.44.317
|
||||
github.com/aws/aws-sdk-go v1.50.8
|
||||
github.com/bmatcuk/doublestar/v4 v4.6.0
|
||||
github.com/bombsimon/logrusr/v2 v2.0.1
|
||||
github.com/bradleyfalzon/ghinstallation/v2 v2.6.0
|
||||
@@ -29,7 +29,7 @@ require (
|
||||
github.com/fsnotify/fsnotify v1.6.0
|
||||
github.com/gfleury/go-bitbucket-v1 v0.0.0-20220301131131-8e7ed04b843e
|
||||
github.com/go-git/go-git/v5 v5.11.0
|
||||
github.com/go-jose/go-jose/v3 v3.0.1
|
||||
github.com/go-jose/go-jose/v3 v3.0.3
|
||||
github.com/go-logr/logr v1.3.0
|
||||
github.com/go-openapi/loads v0.21.2
|
||||
github.com/go-openapi/runtime v0.26.0
|
||||
@@ -39,7 +39,7 @@ require (
|
||||
github.com/gogits/go-gogs-client v0.0.0-20200905025246-8bb8a50cb355
|
||||
github.com/gogo/protobuf v1.3.2
|
||||
github.com/golang-jwt/jwt/v4 v4.5.0
|
||||
github.com/golang/protobuf v1.5.3
|
||||
github.com/golang/protobuf v1.5.4
|
||||
github.com/google/go-cmp v0.6.0
|
||||
github.com/google/go-github/v35 v35.3.0
|
||||
github.com/google/go-jsonnet v0.20.0
|
||||
@@ -51,14 +51,14 @@ require (
|
||||
github.com/grpc-ecosystem/go-grpc-middleware v1.4.0
|
||||
github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0
|
||||
github.com/grpc-ecosystem/grpc-gateway v1.16.0
|
||||
github.com/hashicorp/go-retryablehttp v0.7.4
|
||||
github.com/hashicorp/go-retryablehttp v0.7.7
|
||||
github.com/imdario/mergo v0.3.16
|
||||
github.com/improbable-eng/grpc-web v0.15.0
|
||||
github.com/itchyny/gojq v0.12.13
|
||||
github.com/jeremywohl/flatten v1.0.1
|
||||
github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51
|
||||
github.com/ktrysmt/go-bitbucket v0.9.67
|
||||
github.com/mattn/go-isatty v0.0.19
|
||||
github.com/mattn/go-isatty v0.0.20
|
||||
github.com/mattn/go-zglob v0.0.4
|
||||
github.com/microsoft/azure-devops-go-api/azuredevops v1.0.0-b5
|
||||
github.com/olekukonko/tablewriter v0.0.5
|
||||
@@ -77,22 +77,22 @@ require (
|
||||
github.com/whilp/git-urls v1.0.0
|
||||
github.com/xanzy/go-gitlab v0.91.1
|
||||
github.com/yuin/gopher-lua v1.1.0
|
||||
go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.42.0
|
||||
go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.46.1
|
||||
go.opentelemetry.io/otel v1.21.0
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.21.0
|
||||
go.opentelemetry.io/otel/sdk v1.21.0
|
||||
golang.org/x/crypto v0.16.0
|
||||
golang.org/x/crypto v0.19.0
|
||||
golang.org/x/exp v0.0.0-20230522175609-2e198f4a06a1
|
||||
golang.org/x/oauth2 v0.11.0
|
||||
golang.org/x/sync v0.3.0
|
||||
golang.org/x/term v0.15.0
|
||||
golang.org/x/term v0.17.0
|
||||
google.golang.org/genproto/googleapis/api v0.0.0-20230822172742-b8732ec3820d
|
||||
google.golang.org/grpc v1.59.0
|
||||
google.golang.org/protobuf v1.31.0
|
||||
google.golang.org/protobuf v1.33.0
|
||||
gopkg.in/yaml.v2 v2.4.0
|
||||
gopkg.in/yaml.v3 v3.0.1
|
||||
k8s.io/api v0.26.11
|
||||
k8s.io/apiextensions-apiserver v0.26.4
|
||||
k8s.io/apiextensions-apiserver v0.26.10
|
||||
k8s.io/apimachinery v0.26.11
|
||||
k8s.io/apiserver v0.26.11
|
||||
k8s.io/client-go v0.26.11
|
||||
@@ -103,7 +103,7 @@ require (
|
||||
k8s.io/utils v0.0.0-20230220204549-a5ecb0141aa5
|
||||
layeh.com/gopher-json v0.0.0-20190114024228-97fed8db8427
|
||||
oras.land/oras-go/v2 v2.3.0
|
||||
sigs.k8s.io/controller-runtime v0.14.6
|
||||
sigs.k8s.io/controller-runtime v0.14.7
|
||||
sigs.k8s.io/structured-merge-diff/v4 v4.4.1
|
||||
sigs.k8s.io/yaml v1.3.0
|
||||
)
|
||||
@@ -114,19 +114,20 @@ require (
|
||||
github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.1.0 // indirect
|
||||
github.com/Azure/azure-sdk-for-go/sdk/internal v1.0.0 // indirect
|
||||
github.com/AzureAD/microsoft-authentication-library-for-go v0.5.2 // indirect
|
||||
github.com/aws/aws-sdk-go-v2 v1.17.3 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/config v1.18.8 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/credentials v1.13.8 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.12.21 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/internal/configsources v1.1.27 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.4.21 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/internal/ini v1.3.28 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.9.21 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/service/sqs v1.20.0 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/service/sso v1.12.0 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/service/ssooidc v1.14.0 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/service/sts v1.18.0 // indirect
|
||||
github.com/aws/smithy-go v1.13.5 // indirect
|
||||
github.com/aws/aws-sdk-go-v2 v1.24.1 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/config v1.25.12 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/credentials v1.16.16 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.14.11 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/internal/configsources v1.2.10 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.5.10 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/internal/ini v1.7.1 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.10.4 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.10.10 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/service/sqs v1.29.7 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/service/sso v1.18.7 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/service/ssooidc v1.21.7 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/service/sts v1.26.7 // indirect
|
||||
github.com/aws/smithy-go v1.19.0 // indirect
|
||||
github.com/golang-jwt/jwt v3.2.2+incompatible // indirect
|
||||
github.com/google/s2a-go v0.1.4 // indirect
|
||||
github.com/googleapis/enterprise-certificate-proxy v0.2.5 // indirect
|
||||
@@ -249,7 +250,7 @@ require (
|
||||
github.com/russross/blackfriday/v2 v2.1.0 // indirect
|
||||
github.com/sergi/go-diff v1.1.0 // indirect
|
||||
github.com/shopspring/decimal v1.2.0 // indirect
|
||||
github.com/skeema/knownhosts v1.2.1 // indirect
|
||||
github.com/skeema/knownhosts v1.2.2 // indirect
|
||||
github.com/slack-go/slack v0.12.2 // indirect
|
||||
github.com/spf13/cast v1.5.1 // indirect
|
||||
github.com/stretchr/objx v0.5.0 // indirect
|
||||
@@ -267,7 +268,7 @@ require (
|
||||
go.starlark.net v0.0.0-20220328144851-d1966c6b9fcd // indirect
|
||||
golang.org/x/mod v0.12.0 // indirect
|
||||
golang.org/x/net v0.19.0
|
||||
golang.org/x/sys v0.15.0 // indirect
|
||||
golang.org/x/sys v0.20.0 // indirect
|
||||
golang.org/x/text v0.14.0 // indirect
|
||||
golang.org/x/time v0.3.0
|
||||
golang.org/x/tools v0.13.0 // indirect
|
||||
@@ -294,9 +295,12 @@ replace (
|
||||
// https://github.com/golang/go/issues/33546#issuecomment-519656923
|
||||
github.com/go-check/check => github.com/go-check/check v0.0.0-20180628173108-788fd7840127
|
||||
|
||||
github.com/golang/protobuf => github.com/golang/protobuf v1.4.2
|
||||
github.com/golang/protobuf => github.com/golang/protobuf v1.5.4
|
||||
github.com/grpc-ecosystem/grpc-gateway => github.com/grpc-ecosystem/grpc-gateway v1.16.0
|
||||
|
||||
// Avoid CVE-2023-46402
|
||||
github.com/whilp/git-urls => github.com/chainguard-dev/git-urls v1.0.2
|
||||
|
||||
// Avoid CVE-2022-3064
|
||||
gopkg.in/yaml.v2 => gopkg.in/yaml.v2 v2.4.0
|
||||
|
||||
|
||||
123
go.sum
123
go.sum
@@ -694,10 +694,10 @@ github.com/apache/thrift v0.12.0/go.mod h1:cp2SuWMxlEZw2r+iP2GNCdIi4C1qmUzdZFSVb
|
||||
github.com/apache/thrift v0.13.0/go.mod h1:cp2SuWMxlEZw2r+iP2GNCdIi4C1qmUzdZFSVb+bacwQ=
|
||||
github.com/apache/thrift v0.16.0/go.mod h1:PHK3hniurgQaNMZYaCLEqXKsYK8upmhPbmdP2FXSqgU=
|
||||
github.com/appscode/go v0.0.0-20191119085241-0887d8ec2ecc/go.mod h1:OawnOmAL4ZX3YaPdN+8HTNwBveT1jMsqP74moa9XUbE=
|
||||
github.com/argoproj/gitops-engine v0.7.1-0.20240122213038-792124280fcc h1:Fv94Mi2WvtvPkEH5WoWC3iy/VoQRLeSsE0hyg0n2UkY=
|
||||
github.com/argoproj/gitops-engine v0.7.1-0.20240122213038-792124280fcc/go.mod h1:gWE8uROi7hIkWGNAVM+8FWkMfo0vZ03SLx/aFw/DBzg=
|
||||
github.com/argoproj/notifications-engine v0.4.1-0.20231027194313-a8d185ecc0a9 h1:1lt0VXzmLK7Vv0kaeal3S6/JIfzPyBORkUWXhiqF3l0=
|
||||
github.com/argoproj/notifications-engine v0.4.1-0.20231027194313-a8d185ecc0a9/go.mod h1:E/vv4+by868m0mmflaRfGBmKBtAupoF+mmyfekP8QCk=
|
||||
github.com/argoproj/gitops-engine v0.7.1-0.20240715141017-b6ec82aedce5 h1:YF0xxjIYPeZfsKfZtTd7rxEWQ7EeiTBJHO3PmQ2kV3c=
|
||||
github.com/argoproj/gitops-engine v0.7.1-0.20240715141017-b6ec82aedce5/go.mod h1:d4eLldeEFyZIcVySAMhXhnh1tTa4qfvPYfut9B8UClw=
|
||||
github.com/argoproj/notifications-engine v0.4.1-0.20240126143042-84b9f7913604 h1:pMfBao6Vm1Ax0xGIp9BWEia2nKkccHwV0dTEdrsFOpo=
|
||||
github.com/argoproj/notifications-engine v0.4.1-0.20240126143042-84b9f7913604/go.mod h1:TsyusmXQWIL0ST7YMRG/ered7WlWDmbmnPpXnS2LJmM=
|
||||
github.com/argoproj/pkg v0.13.7-0.20230626144333-d56162821bd1 h1:qsHwwOJ21K2Ao0xPju1sNuqphyMnMYkyB3ZLoLtxWpo=
|
||||
github.com/argoproj/pkg v0.13.7-0.20230626144333-d56162821bd1/go.mod h1:CZHlkyAD1/+FbEn6cB2DQTj48IoLGvEYsWEvtzP3238=
|
||||
github.com/armon/circbuf v0.0.0-20150827004946-bbbad097214e/go.mod h1:3U/XgcO3hCbHZ8TKRvWD2dDTCfh9M9ya+I9JpbB7O8o=
|
||||
@@ -713,35 +713,37 @@ github.com/asaskevich/govalidator v0.0.0-20230301143203-a9d515a09cc2/go.mod h1:W
|
||||
github.com/aws/aws-lambda-go v1.13.3/go.mod h1:4UKl9IzQMoD+QF79YdCuzCwp8VbmG4VAQwij/eHl5CU=
|
||||
github.com/aws/aws-sdk-go v1.27.0/go.mod h1:KmX6BPdI08NWTb3/sm4ZGu5ShLoqVDhKgpiN924inxo=
|
||||
github.com/aws/aws-sdk-go v1.44.289/go.mod h1:aVsgQcEevwlmQ7qHE9I3h+dtQgpqhFB+i8Phjh7fkwI=
|
||||
github.com/aws/aws-sdk-go v1.44.317 h1:+8XWrLmGMwPPXSRSLPzhgcGnzJ2mYkgkrcB9C/GnSOU=
|
||||
github.com/aws/aws-sdk-go v1.44.317/go.mod h1:aVsgQcEevwlmQ7qHE9I3h+dtQgpqhFB+i8Phjh7fkwI=
|
||||
github.com/aws/aws-sdk-go v1.50.8 h1:gY0WoOW+/Wz6XmYSgDH9ge3wnAevYDSQWPxxJvqAkP4=
|
||||
github.com/aws/aws-sdk-go v1.50.8/go.mod h1:LF8svs817+Nz+DmiMQKTO3ubZ/6IaTpq3TjupRn3Eqk=
|
||||
github.com/aws/aws-sdk-go-v2 v0.18.0/go.mod h1:JWVYvqSMppoMJC0x5wdwiImzgXTI9FuZwxzkQq9wy+g=
|
||||
github.com/aws/aws-sdk-go-v2 v1.17.3 h1:shN7NlnVzvDUgPQ+1rLMSxY8OWRNDRYtiqe0p/PgrhY=
|
||||
github.com/aws/aws-sdk-go-v2 v1.17.3/go.mod h1:uzbQtefpm44goOPmdKyAlXSNcwlRgF3ePWVW6EtJvvw=
|
||||
github.com/aws/aws-sdk-go-v2/config v1.18.8 h1:lDpy0WM8AHsywOnVrOHaSMfpaiV2igOw8D7svkFkXVA=
|
||||
github.com/aws/aws-sdk-go-v2/config v1.18.8/go.mod h1:5XCmmyutmzzgkpk/6NYTjeWb6lgo9N170m1j6pQkIBs=
|
||||
github.com/aws/aws-sdk-go-v2/credentials v1.13.8 h1:vTrwTvv5qAwjWIGhZDSBH/oQHuIQjGmD232k01FUh6A=
|
||||
github.com/aws/aws-sdk-go-v2/credentials v1.13.8/go.mod h1:lVa4OHbvgjVot4gmh1uouF1ubgexSCN92P6CJQpT0t8=
|
||||
github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.12.21 h1:j9wi1kQ8b+e0FBVHxCqCGo4kxDU175hoDHcWAi0sauU=
|
||||
github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.12.21/go.mod h1:ugwW57Z5Z48bpvUyZuaPy4Kv+vEfJWnIrky7RmkBvJg=
|
||||
github.com/aws/aws-sdk-go-v2/internal/configsources v1.1.27 h1:I3cakv2Uy1vNmmhRQmFptYDxOvBnwCdNwyw63N0RaRU=
|
||||
github.com/aws/aws-sdk-go-v2/internal/configsources v1.1.27/go.mod h1:a1/UpzeyBBerajpnP5nGZa9mGzsBn5cOKxm6NWQsvoI=
|
||||
github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.4.21 h1:5NbbMrIzmUn/TXFqAle6mgrH5m9cOvMLRGL7pnG8tRE=
|
||||
github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.4.21/go.mod h1:+Gxn8jYn5k9ebfHEqlhrMirFjSW0v0C9fI+KN5vk2kE=
|
||||
github.com/aws/aws-sdk-go-v2/internal/ini v1.3.28 h1:KeTxcGdNnQudb46oOl4d90f2I33DF/c6q3RnZAmvQdQ=
|
||||
github.com/aws/aws-sdk-go-v2/internal/ini v1.3.28/go.mod h1:yRZVr/iT0AqyHeep00SZ4YfBAKojXz08w3XMBscdi0c=
|
||||
github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.9.21 h1:5C6XgTViSb0bunmU57b3CT+MhxULqHH2721FVA+/kDM=
|
||||
github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.9.21/go.mod h1:lRToEJsn+DRA9lW4O9L9+/3hjTkUzlzyzHqn8MTds5k=
|
||||
github.com/aws/aws-sdk-go-v2/service/sqs v1.20.0 h1:tQoMg8i4nFAB70cJ4wiAYEiZRYo2P6uDmU2D6ys/igo=
|
||||
github.com/aws/aws-sdk-go-v2/service/sqs v1.20.0/go.mod h1:jQhN5f4p3PALMNlUtfb/0wGIFlV7vGtJlPDVfxfNfPY=
|
||||
github.com/aws/aws-sdk-go-v2/service/sso v1.12.0 h1:/2gzjhQowRLarkkBOGPXSRnb8sQ2RVsjdG1C/UliK/c=
|
||||
github.com/aws/aws-sdk-go-v2/service/sso v1.12.0/go.mod h1:wo/B7uUm/7zw/dWhBJ4FXuw1sySU5lyIhVg1Bu2yL9A=
|
||||
github.com/aws/aws-sdk-go-v2/service/ssooidc v1.14.0 h1:Jfly6mRxk2ZOSlbCvZfKNS7TukSx1mIzhSsqZ/IGSZI=
|
||||
github.com/aws/aws-sdk-go-v2/service/ssooidc v1.14.0/go.mod h1:TZSH7xLO7+phDtViY/KUp9WGCJMQkLJ/VpgkTFd5gh8=
|
||||
github.com/aws/aws-sdk-go-v2/service/sts v1.18.0 h1:kOO++CYo50RcTFISESluhWEi5Prhg+gaSs4whWabiZU=
|
||||
github.com/aws/aws-sdk-go-v2/service/sts v1.18.0/go.mod h1:+lGbb3+1ugwKrNTWcf2RT05Xmp543B06zDFTwiTLp7I=
|
||||
github.com/aws/smithy-go v1.13.5 h1:hgz0X/DX0dGqTYpGALqXJoRKRj5oQ7150i5FdTePzO8=
|
||||
github.com/aws/smithy-go v1.13.5/go.mod h1:Tg+OJXh4MB2R/uN61Ko2f6hTZwB/ZYGOtib8J3gBHzA=
|
||||
github.com/aws/aws-sdk-go-v2 v1.24.1 h1:xAojnj+ktS95YZlDf0zxWBkbFtymPeDP+rvUQIH3uAU=
|
||||
github.com/aws/aws-sdk-go-v2 v1.24.1/go.mod h1:LNh45Br1YAkEKaAqvmE1m8FUx6a5b/V0oAKV7of29b4=
|
||||
github.com/aws/aws-sdk-go-v2/config v1.25.12 h1:mF4cMuNh/2G+d19nWnm1vJ/ak0qK6SbqF0KtSX9pxu0=
|
||||
github.com/aws/aws-sdk-go-v2/config v1.25.12/go.mod h1:lOvvqtZP9p29GIjOTuA/76HiVk0c/s8qRcFRq2+E2uc=
|
||||
github.com/aws/aws-sdk-go-v2/credentials v1.16.16 h1:8q6Rliyv0aUFAVtzaldUEcS+T5gbadPbWdV1WcAddK8=
|
||||
github.com/aws/aws-sdk-go-v2/credentials v1.16.16/go.mod h1:UHVZrdUsv63hPXFo1H7c5fEneoVo9UXiz36QG1GEPi0=
|
||||
github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.14.11 h1:c5I5iH+DZcH3xOIMlz3/tCKJDaHFwYEmxvlh2fAcFo8=
|
||||
github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.14.11/go.mod h1:cRrYDYAMUohBJUtUnOhydaMHtiK/1NZ0Otc9lIb6O0Y=
|
||||
github.com/aws/aws-sdk-go-v2/internal/configsources v1.2.10 h1:vF+Zgd9s+H4vOXd5BMaPWykta2a6Ih0AKLq/X6NYKn4=
|
||||
github.com/aws/aws-sdk-go-v2/internal/configsources v1.2.10/go.mod h1:6BkRjejp/GR4411UGqkX8+wFMbFbqsUIimfK4XjOKR4=
|
||||
github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.5.10 h1:nYPe006ktcqUji8S2mqXf9c/7NdiKriOwMvWQHgYztw=
|
||||
github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.5.10/go.mod h1:6UV4SZkVvmODfXKql4LCbaZUpF7HO2BX38FgBf9ZOLw=
|
||||
github.com/aws/aws-sdk-go-v2/internal/ini v1.7.1 h1:uR9lXYjdPX0xY+NhvaJ4dD8rpSRz5VY81ccIIoNG+lw=
|
||||
github.com/aws/aws-sdk-go-v2/internal/ini v1.7.1/go.mod h1:6fQQgfuGmw8Al/3M2IgIllycxV7ZW7WCdVSqfBeUiCY=
|
||||
github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.10.4 h1:/b31bi3YVNlkzkBrm9LfpaKoaYZUxIAj4sHfOTmLfqw=
|
||||
github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.10.4/go.mod h1:2aGXHFmbInwgP9ZfpmdIfOELL79zhdNYNmReK8qDfdQ=
|
||||
github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.10.10 h1:DBYTXwIGQSGs9w4jKm60F5dmCQ3EEruxdc0MFh+3EY4=
|
||||
github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.10.10/go.mod h1:wohMUQiFdzo0NtxbBg0mSRGZ4vL3n0dKjLTINdcIino=
|
||||
github.com/aws/aws-sdk-go-v2/service/sqs v1.29.7 h1:tRNrFDGRm81e6nTX5Q4CFblea99eAfm0dxXazGpLceU=
|
||||
github.com/aws/aws-sdk-go-v2/service/sqs v1.29.7/go.mod h1:8GWUDux5Z2h6z2efAtr54RdHXtLm8sq7Rg85ZNY/CZM=
|
||||
github.com/aws/aws-sdk-go-v2/service/sso v1.18.7 h1:eajuO3nykDPdYicLlP3AGgOyVN3MOlFmZv7WGTuJPow=
|
||||
github.com/aws/aws-sdk-go-v2/service/sso v1.18.7/go.mod h1:+mJNDdF+qiUlNKNC3fxn74WWNN+sOiGOEImje+3ScPM=
|
||||
github.com/aws/aws-sdk-go-v2/service/ssooidc v1.21.7 h1:QPMJf+Jw8E1l7zqhZmMlFw6w1NmfkfiSK8mS4zOx3BA=
|
||||
github.com/aws/aws-sdk-go-v2/service/ssooidc v1.21.7/go.mod h1:ykf3COxYI0UJmxcfcxcVuz7b6uADi1FkiUz6Eb7AgM8=
|
||||
github.com/aws/aws-sdk-go-v2/service/sts v1.26.7 h1:NzO4Vrau795RkUdSHKEwiR01FaGzGOH1EETJ+5QHnm0=
|
||||
github.com/aws/aws-sdk-go-v2/service/sts v1.26.7/go.mod h1:6h2YuIoxaMSCFf5fi1EgZAwdfkGMgDY+DVfa61uLe4U=
|
||||
github.com/aws/smithy-go v1.19.0 h1:KWFKQV80DpP3vJrrA9sVAHQ5gc2z8i4EzrLhLlWXcBM=
|
||||
github.com/aws/smithy-go v1.19.0/go.mod h1:NukqUGpCZIILqqiV0NIjeFh24kd/FAa4beRb6nbIUPE=
|
||||
github.com/beevik/ntp v0.2.0/go.mod h1:hIHWr+l3+/clUnF44zdK+CWW7fO8dR5cIylAQ76NRpg=
|
||||
github.com/benbjohnson/clock v1.1.0/go.mod h1:J11/hYXuz8f4ySSvYwY0FKfm+ezbsZBKZxNJlLklBHA=
|
||||
github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q=
|
||||
@@ -784,6 +786,8 @@ github.com/cespare/xxhash/v2 v2.2.0 h1:DC2CZ1Ep5Y4k3ZQ899DldepgrayRUGE6BBZ/cd9Cj
|
||||
github.com/cespare/xxhash/v2 v2.2.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
|
||||
github.com/chai2010/gettext-go v1.0.2 h1:1Lwwip6Q2QGsAdl/ZKPCwTe9fe0CjlUbqj5bFNSjIRk=
|
||||
github.com/chai2010/gettext-go v1.0.2/go.mod h1:y+wnP2cHYaVj19NZhYKAwEMH2CI1gNHeQQ+5AjwawxA=
|
||||
github.com/chainguard-dev/git-urls v1.0.2 h1:pSpT7ifrpc5X55n4aTTm7FFUE+ZQHKiqpiwNkJrVcKQ=
|
||||
github.com/chainguard-dev/git-urls v1.0.2/go.mod h1:rbGgj10OS7UgZlbzdUQIQpT0k/D4+An04HJY7Ol+Y/o=
|
||||
github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI=
|
||||
github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI=
|
||||
github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU=
|
||||
@@ -887,6 +891,8 @@ github.com/facebookgo/subset v0.0.0-20150612182917-8dac2c3c4870/go.mod h1:5tD+ne
|
||||
github.com/fatih/camelcase v1.0.0 h1:hxNvNX/xYBp0ovncs8WyWZrOrpBNub/JfaMvbURyft8=
|
||||
github.com/fatih/camelcase v1.0.0/go.mod h1:yN2Sb0lFhZJUdVvtELVWefmrXpuZESvPmqwoZc+/fpc=
|
||||
github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4=
|
||||
github.com/fatih/color v1.16.0 h1:zmkK9Ngbjj+K0yRhTVONQh1p/HknKYSlNT+vZCzyokM=
|
||||
github.com/fatih/color v1.16.0/go.mod h1:fL2Sau1YI5c0pdGEVCbKQbLXB6edEj1ZgiY4NijnWvE=
|
||||
github.com/felixge/httpsnoop v1.0.1/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U=
|
||||
github.com/felixge/httpsnoop v1.0.3 h1:s/nj+GCswXYzN5v2DpNMuMQYe+0DDwt5WVCU6CWBdXk=
|
||||
github.com/felixge/httpsnoop v1.0.3/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U=
|
||||
@@ -932,8 +938,8 @@ github.com/go-git/go-git/v5 v5.11.0/go.mod h1:6GFcX2P3NM7FPBfpePbpLd21XxsgdAt+lK
|
||||
github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU=
|
||||
github.com/go-gl/glfw/v3.3/glfw v0.0.0-20191125211704-12ad95a8df72/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8=
|
||||
github.com/go-gl/glfw/v3.3/glfw v0.0.0-20200222043503-6f7a984d4dc4/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8=
|
||||
github.com/go-jose/go-jose/v3 v3.0.1 h1:pWmKFVtt+Jl0vBZTIpz/eAKwsm6LkIxDVVbFHKkchhA=
|
||||
github.com/go-jose/go-jose/v3 v3.0.1/go.mod h1:RNkWWRld676jZEYoV3+XK8L2ZnNSvIsxFMht0mSX+u8=
|
||||
github.com/go-jose/go-jose/v3 v3.0.3 h1:fFKWeig/irsp7XD2zBxvnmA/XaRWp5V3CBsZXJF7G7k=
|
||||
github.com/go-jose/go-jose/v3 v3.0.3/go.mod h1:5b+7YgP7ZICgJDBdfjZaIt+H/9L9T/YQrVfLAMboGkQ=
|
||||
github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as=
|
||||
github.com/go-kit/kit v0.9.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as=
|
||||
github.com/go-kit/kit v0.10.0/go.mod h1:xUsJbQ/Fp4kEt7AFgCuvyX4a71u8h9jB8tj/ORgOZ7o=
|
||||
@@ -1086,8 +1092,8 @@ github.com/golang/mock v1.4.4/go.mod h1:l3mdAwkq5BuhzHwde/uurv3sEJeZMXNpwsxVWU71
|
||||
github.com/golang/mock v1.5.0/go.mod h1:CWnOUgYIOo4TcNZ0wHX3YZCqsaM1I1Jvs6v3mP3KVu8=
|
||||
github.com/golang/mock v1.6.0 h1:ErTB+efbowRARo13NNdxyJji2egdxLGQhRaY+DUumQc=
|
||||
github.com/golang/mock v1.6.0/go.mod h1:p6yTPP+5HYm5mzsMV8JkE6ZKdX+/wYM6Hr+LicevLPs=
|
||||
github.com/golang/protobuf v1.4.2 h1:+Z5KGCizgyZCbGh1KZqA0fcLLkwbsjIzS4aV2v7wJX0=
|
||||
github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI=
|
||||
github.com/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek=
|
||||
github.com/golang/protobuf v1.5.4/go.mod h1:lnTiLA8Wa4RWRcIUkrtSVa5nRhsEGBg48fD6rSs7xps=
|
||||
github.com/golang/snappy v0.0.0-20180518054509-2e65f85255db/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
|
||||
github.com/golang/snappy v0.0.1/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
|
||||
github.com/golang/snappy v0.0.3/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
|
||||
@@ -1235,14 +1241,14 @@ github.com/hashicorp/go-cleanhttp v0.5.0/go.mod h1:JpRdi6/HCYpAwUzNwuwqhbovhLtng
|
||||
github.com/hashicorp/go-cleanhttp v0.5.1/go.mod h1:JpRdi6/HCYpAwUzNwuwqhbovhLtngrth3wmdIIUrZ80=
|
||||
github.com/hashicorp/go-cleanhttp v0.5.2 h1:035FKYIWjmULyFRBKPs8TBQoi0x6d9G4xc9neXJWAZQ=
|
||||
github.com/hashicorp/go-cleanhttp v0.5.2/go.mod h1:kO/YDlP8L1346E6Sodw+PrpBSV4/SoxCXGY6BqNFT48=
|
||||
github.com/hashicorp/go-hclog v0.9.2 h1:CG6TE5H9/JXsFWJCfoIVpKFIkFe6ysEuHirp4DxCsHI=
|
||||
github.com/hashicorp/go-hclog v0.9.2/go.mod h1:5CU+agLiy3J7N7QjHK5d05KxGsuXiQLrjA0H7acj2lQ=
|
||||
github.com/hashicorp/go-hclog v1.6.3 h1:Qr2kF+eVWjTiYmU7Y31tYlP1h0q/X3Nl3tPGdaB11/k=
|
||||
github.com/hashicorp/go-hclog v1.6.3/go.mod h1:W4Qnvbt70Wk/zYJryRzDRU/4r0kIg0PVHBcfoyhpF5M=
|
||||
github.com/hashicorp/go-immutable-radix v1.0.0/go.mod h1:0y9vanUI8NX6FsYoO3zeMjhV/C5i9g4Q3DwcSNZ4P60=
|
||||
github.com/hashicorp/go-msgpack v0.5.3/go.mod h1:ahLV/dePpqEmjfWmKiqvPkv/twdG7iPBM1vqhUKIvfM=
|
||||
github.com/hashicorp/go-multierror v1.0.0/go.mod h1:dHtQlpGsu+cZNNAkkCN/P3hoUDHhCYQXV3UM06sGGrk=
|
||||
github.com/hashicorp/go-retryablehttp v0.5.1/go.mod h1:9B5zBasrRhHXnJnui7y6sL7es7NDiJgTc6Er0maI1Xs=
|
||||
github.com/hashicorp/go-retryablehttp v0.7.4 h1:ZQgVdpTdAL7WpMIwLzCfbalOcSUdkDZnpUv3/+BxzFA=
|
||||
github.com/hashicorp/go-retryablehttp v0.7.4/go.mod h1:Jy/gPYAdjqffZ/yFGCFV2doI5wjtH1ewM9u8iYVjtX8=
|
||||
github.com/hashicorp/go-retryablehttp v0.7.7 h1:C8hUCYzor8PIfXHa4UrZkU4VvK8o9ISHxT2Q8+VepXU=
|
||||
github.com/hashicorp/go-retryablehttp v0.7.7/go.mod h1:pkQpWZeYWskR+D1tR2O5OcBFOxfA7DoAO6xtkuQnHTk=
|
||||
github.com/hashicorp/go-rootcerts v1.0.0/go.mod h1:K6zTfqpRlCUIjkwsN4Z+hiSfzSTQa6eBIzfwKfwNnHU=
|
||||
github.com/hashicorp/go-sockaddr v1.0.0/go.mod h1:7Xibr9yA9JjQq1JpNB2Vw7kxv8xerXegt+ozgdvDeDU=
|
||||
github.com/hashicorp/go-syslog v1.0.0/go.mod h1:qPfqrKkXGihmCqbJM2mZgkZGvKG1dFdvsLplgctolz4=
|
||||
@@ -1377,13 +1383,15 @@ github.com/markbates/oncer v0.0.0-20181203154359-bf2de49a0be2/go.mod h1:Ld9puTsI
|
||||
github.com/markbates/safe v1.0.1/go.mod h1:nAqgmRi7cY2nqMc92/bSEeQA+R4OheNU2T1kNSCBdG0=
|
||||
github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU=
|
||||
github.com/mattn/go-colorable v0.1.2/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE=
|
||||
github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA=
|
||||
github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg=
|
||||
github.com/mattn/go-isatty v0.0.3/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4=
|
||||
github.com/mattn/go-isatty v0.0.4/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4=
|
||||
github.com/mattn/go-isatty v0.0.8/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s=
|
||||
github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU=
|
||||
github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM=
|
||||
github.com/mattn/go-isatty v0.0.19 h1:JITubQf0MOLdlGRuRq+jtsDlekdYPia9ZFsB8h/APPA=
|
||||
github.com/mattn/go-isatty v0.0.19/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
|
||||
github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY=
|
||||
github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
|
||||
github.com/mattn/go-runewidth v0.0.2/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU=
|
||||
github.com/mattn/go-runewidth v0.0.4/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU=
|
||||
github.com/mattn/go-runewidth v0.0.9/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI=
|
||||
@@ -1622,8 +1630,8 @@ github.com/sirupsen/logrus v1.8.1/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic
|
||||
github.com/sirupsen/logrus v1.9.2/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ=
|
||||
github.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ=
|
||||
github.com/sirupsen/logrus v1.9.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ=
|
||||
github.com/skeema/knownhosts v1.2.1 h1:SHWdIUa82uGZz+F+47k8SY4QhhI291cXCpopT1lK2AQ=
|
||||
github.com/skeema/knownhosts v1.2.1/go.mod h1:xYbVRSPxqBZFrdmDyMmsOs+uX1UZC3nTN3ThzgDxUwo=
|
||||
github.com/skeema/knownhosts v1.2.2 h1:Iug2P4fLmDw9f41PB6thxUkNUkJzB5i+1/exaj40L3A=
|
||||
github.com/skeema/knownhosts v1.2.2/go.mod h1:xYbVRSPxqBZFrdmDyMmsOs+uX1UZC3nTN3ThzgDxUwo=
|
||||
github.com/skratchdot/open-golang v0.0.0-20160302144031-75fb7ed4208c h1:fyKiXKO1/I/B6Y2U8T7WdQGWzwehOuGIrljPtt7YTTI=
|
||||
github.com/skratchdot/open-golang v0.0.0-20160302144031-75fb7ed4208c/go.mod h1:sUM3LWHvSMaG192sy56D9F7CNvL7jUJVXoqM1QKLnog=
|
||||
github.com/slack-go/slack v0.12.2 h1:x3OppyMyGIbbiyFhsBmpf9pwkUzMhthJMRNmNlA4LaQ=
|
||||
@@ -1697,8 +1705,6 @@ github.com/vmihailenco/msgpack/v5 v5.3.4 h1:qMKAwOV+meBw2Y8k9cVwAy7qErtYCwBzZ2el
|
||||
github.com/vmihailenco/msgpack/v5 v5.3.4/go.mod h1:7xyJ9e+0+9SaZT0Wt1RGleJXzli6Q/V5KbhBonMG9jc=
|
||||
github.com/vmihailenco/tagparser/v2 v2.0.0 h1:y09buUbR+b5aycVFQs/g70pqKVZNBmxwAhO7/IwNM9g=
|
||||
github.com/vmihailenco/tagparser/v2 v2.0.0/go.mod h1:Wri+At7QHww0WTrCBeu4J6bNtoV6mEfg5OIWRZA9qds=
|
||||
github.com/whilp/git-urls v1.0.0 h1:95f6UMWN5FKW71ECsXRUd3FVYiXdrE7aX4NZKcPmIjU=
|
||||
github.com/whilp/git-urls v1.0.0/go.mod h1:J16SAmobsqc3Qcy98brfl5f5+e0clUvg1krgwk/qCfE=
|
||||
github.com/xanzy/go-gitlab v0.91.1 h1:gnV57IPGYywWer32oXKBcdmc8dVxeKl3AauV8Bu17rw=
|
||||
github.com/xanzy/go-gitlab v0.91.1/go.mod h1:5ryv+MnpZStBH8I/77HuQBsMbBGANtVpLWC15qOjWAw=
|
||||
github.com/xanzy/ssh-agent v0.3.3 h1:+/15pJfg/RsTxqYcX6fHqOXZwwMP+2VyYWJeWM2qQFM=
|
||||
@@ -1744,8 +1750,8 @@ go.opencensus.io v0.22.5/go.mod h1:5pWMHQbX5EPX2/62yrJeAkowc+lfs/XD7Uxpq3pI6kk=
|
||||
go.opencensus.io v0.23.0/go.mod h1:XItmlyltB5F7CS4xOC1DcqMoFqwtC6OG2xF7mCv7P7E=
|
||||
go.opencensus.io v0.24.0 h1:y73uSU6J157QMP2kn2r30vwW1A2W2WFwSCGnAVxeaD0=
|
||||
go.opencensus.io v0.24.0/go.mod h1:vNK8G9p7aAivkbmorf4v+7Hgx+Zs0yY+0fOtgBfjQKo=
|
||||
go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.42.0 h1:ZOLJc06r4CB42laIXg/7udr0pbZyuAihN10A/XuiQRY=
|
||||
go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.42.0/go.mod h1:5z+/ZWJQKXa9YT34fQNx5K8Hd1EoIhvtUygUQPqEOgQ=
|
||||
go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.46.1 h1:SpGay3w+nEwMpfVnbqOLH5gY52/foP8RE8UzTZ1pdSE=
|
||||
go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.46.1/go.mod h1:4UoMYEZOC0yN/sPGH76KPkkU7zgiEWYWL9vwmbnTJPE=
|
||||
go.opentelemetry.io/otel v1.21.0 h1:hzLeKBZEL7Okw2mGzZ0cc4k/A7Fta0uoPgaJCr8fsFc=
|
||||
go.opentelemetry.io/otel v1.21.0/go.mod h1:QZzNPQPm1zLX4gZK4cMi+71eaorMSGT3A4znnUvNNEo=
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.21.0 h1:cl5P5/GIfFh4t6xyruOgJP5QiA1pw4fYYdv6nc6CBWw=
|
||||
@@ -1793,7 +1799,6 @@ golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8U
|
||||
golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||
golang.org/x/crypto v0.0.0-20190701094942-4def268fd1a4/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||
golang.org/x/crypto v0.0.0-20190820162420-60c769a6c586/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||
golang.org/x/crypto v0.0.0-20190911031432-227b76d455e7/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||
golang.org/x/crypto v0.0.0-20200302210943-78000ba7a073/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
||||
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
||||
@@ -1813,8 +1818,8 @@ golang.org/x/crypto v0.10.0/go.mod h1:o4eNf7Ede1fv+hwOwZsTHl9EsPFO6q6ZvYR8vYfY45
|
||||
golang.org/x/crypto v0.12.0/go.mod h1:NF0Gs7EO5K4qLn+Ylc+fih8BSTeIjAP05siRnAh98yw=
|
||||
golang.org/x/crypto v0.13.0/go.mod h1:y6Z2r+Rw4iayiXXAIxJIDAJ1zMW4yaTpebo8fPOliYc=
|
||||
golang.org/x/crypto v0.14.0/go.mod h1:MVFd36DqK4CsrnJYDkBA3VC4m2GkXAM0PvzMCn4JQf4=
|
||||
golang.org/x/crypto v0.16.0 h1:mMMrFzRSCF0GvB7Ne27XVtVAaXLrPmgPC7/v0tkwHaY=
|
||||
golang.org/x/crypto v0.16.0/go.mod h1:gCAAfMLgwOJRpTjQ2zCCt2OcSfYMTeZVSRtQlPC7Nq4=
|
||||
golang.org/x/crypto v0.19.0 h1:ENy+Az/9Y1vSrlrvBSyna3PITt4tiZLf7sgCjZBX7Wo=
|
||||
golang.org/x/crypto v0.19.0/go.mod h1:Iy9bg/ha4yyC70EfRS8jz+B6ybOBKMaSxLj6P6oBDfU=
|
||||
golang.org/x/exp v0.0.0-20180321215751-8460e604b9de/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
|
||||
golang.org/x/exp v0.0.0-20180807140117-3d87b88a115f/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
|
||||
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
|
||||
@@ -2133,8 +2138,9 @@ golang.org/x/sys v0.9.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.11.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.12.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.13.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.15.0 h1:h48lPFYpsTvQJZF4EKyI4aLHaev3CxivZmv7yZig9pc=
|
||||
golang.org/x/sys v0.15.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||
golang.org/x/sys v0.17.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||
golang.org/x/sys v0.20.0 h1:Od9JTbYCk261bKm4M/mw7AklTlFYIa0bIp9BgSm1S8Y=
|
||||
golang.org/x/sys v0.20.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
||||
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
|
||||
golang.org/x/term v0.1.0/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
|
||||
@@ -2149,8 +2155,8 @@ golang.org/x/term v0.9.0/go.mod h1:M6DEAAIenWoTxdKrOltXcmDY3rSplQUkrvaDU5FcQyo=
|
||||
golang.org/x/term v0.11.0/go.mod h1:zC9APTIj3jG3FdV/Ons+XE1riIZXG4aZ4GTHiPZJPIU=
|
||||
golang.org/x/term v0.12.0/go.mod h1:owVbMEjm3cBLCHdkQu9b1opXd4ETQWc3BhuQGKgXgvU=
|
||||
golang.org/x/term v0.13.0/go.mod h1:LTmsnFJwVN6bCy1rVCoS+qHT1HhALEFxKncY3WNNh4U=
|
||||
golang.org/x/term v0.15.0 h1:y/Oo/a/q3IXu26lQgl04j/gjuBDOBlx7X6Om1j2CPW4=
|
||||
golang.org/x/term v0.15.0/go.mod h1:BDl952bC7+uMoWR75FIrCDx79TPU9oHkTZ9yRbYOrX0=
|
||||
golang.org/x/term v0.17.0 h1:mkTF7LCd6WGJNL3K1Ad7kwxNfYAW6a8a8QqtMblp/4U=
|
||||
golang.org/x/term v0.17.0/go.mod h1:lLRBjIVuehSbZlaOtGMbcMncT+aqLLLmKrsjNrUguwk=
|
||||
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=
|
||||
@@ -2575,8 +2581,9 @@ google.golang.org/protobuf v1.28.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqw
|
||||
google.golang.org/protobuf v1.28.1/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I=
|
||||
google.golang.org/protobuf v1.29.1/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I=
|
||||
google.golang.org/protobuf v1.30.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I=
|
||||
google.golang.org/protobuf v1.31.0 h1:g0LDEJHgrBl9N9r17Ru3sqWhkIx2NB67okBHPwC7hs8=
|
||||
google.golang.org/protobuf v1.31.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I=
|
||||
google.golang.org/protobuf v1.33.0 h1:uNO2rsAINq/JlFpSdYEKIZ0uKD/R9cpdv0T+yoGwGmI=
|
||||
google.golang.org/protobuf v1.33.0/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos=
|
||||
gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw=
|
||||
gopkg.in/alexcesaro/quotedprintable.v3 v3.0.0-20150716171945-2caba252f4dc h1:2gGKlE2+asNV9m7xrywl36YYNnBG5ZQ0r/BOOxqPpmk=
|
||||
gopkg.in/alexcesaro/quotedprintable.v3 v3.0.0-20150716171945-2caba252f4dc/go.mod h1:m7x9LTH6d71AHyAX77c9yqWCCa3UKHcVEj9y7hAtKDk=
|
||||
@@ -2704,8 +2711,8 @@ rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8
|
||||
rsc.io/pdf v0.1.1/go.mod h1:n8OzWcQ6Sp37PL01nO98y4iUCRdTGarVfzxY20ICaU4=
|
||||
rsc.io/quote/v3 v3.1.0/go.mod h1:yEA65RcK8LyAZtP9Kv3t0HmxON59tX3rD+tICJqUlj0=
|
||||
rsc.io/sampler v1.3.0/go.mod h1:T1hPZKmBbMNahiBKFy5HrXp6adAjACjK9JXDnKaTXpA=
|
||||
sigs.k8s.io/controller-runtime v0.14.6 h1:oxstGVvXGNnMvY7TAESYk+lzr6S3V5VFxQ6d92KcwQA=
|
||||
sigs.k8s.io/controller-runtime v0.14.6/go.mod h1:WqIdsAY6JBsjfc/CqO0CORmNtoCtE4S6qbPc9s68h+0=
|
||||
sigs.k8s.io/controller-runtime v0.14.7 h1:Vrnm2vk9ZFlRkXATHz0W0wXcqNl7kPat8q2JyxVy0Q8=
|
||||
sigs.k8s.io/controller-runtime v0.14.7/go.mod h1:ErTs3SJCOujNUnTz4AS+uh8hp6DHMo1gj6fFndJT1X8=
|
||||
sigs.k8s.io/json v0.0.0-20220713155537-f223a00ba0e2/go.mod h1:B8JuhiUyNFVKdsE8h686QcCxMaH6HrOAZj4vswFpcB0=
|
||||
sigs.k8s.io/json v0.0.0-20221116044647-bc3834ca7abd h1:EDPBXCAspyGV4jQlpZSudPeMmr1bNJefnuqLsRAsHZo=
|
||||
sigs.k8s.io/json v0.0.0-20221116044647-bc3834ca7abd/go.mod h1:B8JuhiUyNFVKdsE8h686QcCxMaH6HrOAZj4vswFpcB0=
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"log"
|
||||
"os"
|
||||
@@ -64,6 +65,11 @@ func updateMkDocsNav(parent string, child string, subchild string, files []strin
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// The marshaller drops custom tags, so re-add this one. Turns out this is much less invasive than trying to handle
|
||||
// it at the YAML parser level.
|
||||
newmkdocs = bytes.Replace(newmkdocs, []byte("site_url: READTHEDOCS_CANONICAL_URL"), []byte("site_url: !ENV READTHEDOCS_CANONICAL_URL"), 1)
|
||||
|
||||
return os.WriteFile("mkdocs.yml", newmkdocs, 0644)
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
75496ea824f92305ff7d28af37f4af57536bf5138399c824dff997b9d239dd42 helm-v3.14.1-linux-amd64.tar.gz
|
||||
@@ -0,0 +1 @@
|
||||
f865b8ad4228fd0990bbc5b50615eb6cb9eb31c9a9ca7238401ed897bbbe9033 helm-v3.14.1-linux-arm64.tar.gz
|
||||
@@ -0,0 +1 @@
|
||||
4d853ab8fe3462287c7272fbadd5f73531ecdd6fa0db37d31630e41ae1ae21de helm-v3.14.1-linux-ppc64le.tar.gz
|
||||
@@ -0,0 +1 @@
|
||||
19bf07999c7244bfeb0fd27152919b9faa1148cf43910edbb98efa9150058a98 helm-v3.14.1-linux-s390x.tar.gz
|
||||
@@ -0,0 +1 @@
|
||||
0885a501d586c1e949e9b113bf3fb3290b0bbf74db9444a1d8c2723a143006a5 helm-v3.14.2-linux-amd64.tar.gz
|
||||
@@ -0,0 +1 @@
|
||||
c65d6a9557bb359abc2c0d26670de850b52327dc3976ad6f9e14c298ea3e1b61 helm-v3.14.2-linux-arm64.tar.gz
|
||||
@@ -0,0 +1 @@
|
||||
f3bc8582ff151e619cd285d9cdf9fef1c5733ee5522d8bed2ef680ef07f87223 helm-v3.14.2-linux-ppc64le.tar.gz
|
||||
@@ -0,0 +1 @@
|
||||
7bda34aa26638e5116b31385f3b781172572175bf4c1ae00c87d8b154458ed94 helm-v3.14.2-linux-s390x.tar.gz
|
||||
@@ -0,0 +1 @@
|
||||
4d5d01a94c7d6b07e71690dc1988bf3229680284c87f4242d28c6f1cc99653be helm-v3.14.3-darwin-amd64.tar.gz
|
||||
@@ -0,0 +1 @@
|
||||
dff794152b62b7c1a9ff615d510f8657bcd7a3727c668e0d9d4955f70d5f7573 helm-v3.14.3-darwin-arm64.tar.gz
|
||||
@@ -0,0 +1 @@
|
||||
3c90f24e180f8c207b8a18e5ec82cb0fa49858a7a0a86e4ed52a98398681e00b helm-v3.14.3-linux-amd64.tar.gz
|
||||
@@ -0,0 +1 @@
|
||||
85e1573e76fa60af14ba7e9ec75db2129b6884203be866893fa0b3f7e41ccd5e helm-v3.14.3-linux-arm64.tar.gz
|
||||
@@ -0,0 +1 @@
|
||||
aab121ca470e2a502cda849a9b3e92eeb9a32e213b0f0a79a95a04e375d26ce7 helm-v3.14.3-linux-ppc64le.tar.gz
|
||||
@@ -0,0 +1 @@
|
||||
d64fa8aced3244b549377741dc4e2db8109e5270c0723c11b547a9da5f99ad43 helm-v3.14.3-linux-s390x.tar.gz
|
||||
@@ -0,0 +1 @@
|
||||
73434aeac36ad068ce2e5582b8851a286dc628eae16494a26e2ad0b24a7199f9 helm-v3.14.4-darwin-amd64.tar.gz
|
||||
@@ -0,0 +1 @@
|
||||
61e9c5455f06b2ad0a1280975bf65892e707adc19d766b0cf4e9006e3b7b4b6c helm-v3.14.4-darwin-arm64.tar.gz
|
||||
@@ -0,0 +1 @@
|
||||
a5844ef2c38ef6ddf3b5a8f7d91e7e0e8ebc39a38bb3fc8013d629c1ef29c259 helm-v3.14.4-linux-amd64.tar.gz
|
||||
@@ -0,0 +1 @@
|
||||
113ccc53b7c57c2aba0cd0aa560b5500841b18b5210d78641acfddc53dac8ab2 helm-v3.14.4-linux-arm64.tar.gz
|
||||
@@ -0,0 +1 @@
|
||||
d0d625b43f6650ad376428520b2238baa2400bfedb43b2e0f24ad7247f0f59b5 helm-v3.14.4-linux-ppc64le.tar.gz
|
||||
@@ -0,0 +1 @@
|
||||
a5750d0cb1ba34ce84ab3be6382a14617130661d15dd2aa1b36630b293437936 helm-v3.14.4-linux-s390x.tar.gz
|
||||
@@ -11,7 +11,7 @@
|
||||
# Use ./hack/installers/checksums/add-helm-checksums.sh and
|
||||
# add-kustomize-checksums.sh to help download checksums.
|
||||
###############################################################################
|
||||
helm3_version=3.14.0
|
||||
helm3_version=3.14.4
|
||||
kubectl_version=1.17.8
|
||||
kubectx_version=0.6.3
|
||||
kustomize5_version=5.2.1
|
||||
|
||||
@@ -20,6 +20,11 @@ spec:
|
||||
- args:
|
||||
- /usr/local/bin/argocd-application-controller
|
||||
env:
|
||||
- name: REDIS_PASSWORD
|
||||
valueFrom:
|
||||
secretKeyRef:
|
||||
key: auth
|
||||
name: argocd-redis
|
||||
- name: ARGOCD_RECONCILIATION_TIMEOUT
|
||||
valueFrom:
|
||||
configMapKeyRef:
|
||||
|
||||
@@ -21,6 +21,11 @@ spec:
|
||||
- args:
|
||||
- /usr/local/bin/argocd-application-controller
|
||||
env:
|
||||
- name: REDIS_PASSWORD
|
||||
valueFrom:
|
||||
secretKeyRef:
|
||||
key: auth
|
||||
name: argocd-redis
|
||||
- name: ARGOCD_CONTROLLER_REPLICAS
|
||||
value: "1"
|
||||
- name: ARGOCD_RECONCILIATION_TIMEOUT
|
||||
@@ -197,6 +202,12 @@ spec:
|
||||
name: argocd-cmd-params-cm
|
||||
key: controller.diff.server.side
|
||||
optional: true
|
||||
- name: ARGOCD_IGNORE_NORMALIZER_JQ_TIMEOUT
|
||||
valueFrom:
|
||||
configMapKeyRef:
|
||||
name: argocd-cmd-params-cm
|
||||
key: controller.ignore.normalizer.jq.timeout
|
||||
optional: true
|
||||
image: quay.io/argoproj/argocd:latest
|
||||
imagePullPolicy: Always
|
||||
name: argocd-application-controller
|
||||
|
||||
@@ -5,7 +5,7 @@ kind: Kustomization
|
||||
images:
|
||||
- name: quay.io/argoproj/argocd
|
||||
newName: quay.io/argoproj/argocd
|
||||
newTag: v2.10.0
|
||||
newTag: v2.10.20
|
||||
resources:
|
||||
- ./application-controller
|
||||
- ./dex
|
||||
|
||||
@@ -15,6 +15,23 @@ spec:
|
||||
labels:
|
||||
app.kubernetes.io/name: argocd-redis
|
||||
spec:
|
||||
initContainers:
|
||||
- command:
|
||||
- argocd
|
||||
- admin
|
||||
- redis-initial-password
|
||||
image: quay.io/argoproj/argocd:latest
|
||||
imagePullPolicy: IfNotPresent
|
||||
name: secret-init
|
||||
securityContext:
|
||||
allowPrivilegeEscalation: false
|
||||
capabilities:
|
||||
drop:
|
||||
- ALL
|
||||
readOnlyRootFilesystem: true
|
||||
runAsNonRoot: true
|
||||
seccompProfile:
|
||||
type: RuntimeDefault
|
||||
securityContext:
|
||||
runAsNonRoot: true
|
||||
runAsUser: 999
|
||||
@@ -23,13 +40,20 @@ spec:
|
||||
serviceAccountName: argocd-redis
|
||||
containers:
|
||||
- name: redis
|
||||
image: redis:7.0.14-alpine
|
||||
image: redis:7.0.15-alpine
|
||||
imagePullPolicy: Always
|
||||
args:
|
||||
- "--save"
|
||||
- ""
|
||||
- "--appendonly"
|
||||
- "no"
|
||||
- --requirepass $(REDIS_PASSWORD)
|
||||
env:
|
||||
- name: REDIS_PASSWORD
|
||||
valueFrom:
|
||||
secretKeyRef:
|
||||
key: auth
|
||||
name: argocd-redis
|
||||
ports:
|
||||
- containerPort: 6379
|
||||
securityContext:
|
||||
|
||||
@@ -8,7 +8,6 @@ spec:
|
||||
app.kubernetes.io/name: argocd-redis
|
||||
policyTypes:
|
||||
- Ingress
|
||||
- Egress
|
||||
ingress:
|
||||
- from:
|
||||
- podSelector:
|
||||
@@ -23,9 +22,3 @@ spec:
|
||||
ports:
|
||||
- protocol: TCP
|
||||
port: 6379
|
||||
egress:
|
||||
- ports:
|
||||
- port: 53
|
||||
protocol: UDP
|
||||
- port: 53
|
||||
protocol: TCP
|
||||
|
||||
23
manifests/base/redis/argocd-redis-role.yaml
Normal file
23
manifests/base/redis/argocd-redis-role.yaml
Normal file
@@ -0,0 +1,23 @@
|
||||
apiVersion: rbac.authorization.k8s.io/v1
|
||||
kind: Role
|
||||
metadata:
|
||||
labels:
|
||||
app.kubernetes.io/component: redis
|
||||
app.kubernetes.io/name: argocd-redis
|
||||
app.kubernetes.io/part-of: argocd
|
||||
name: argocd-redis
|
||||
rules:
|
||||
- apiGroups:
|
||||
- ""
|
||||
resources:
|
||||
- secrets
|
||||
resourceNames:
|
||||
- argocd-redis
|
||||
verbs:
|
||||
- get
|
||||
- apiGroups:
|
||||
- ""
|
||||
resources:
|
||||
- secrets
|
||||
verbs:
|
||||
- create
|
||||
15
manifests/base/redis/argocd-redis-rolebinding.yaml
Normal file
15
manifests/base/redis/argocd-redis-rolebinding.yaml
Normal file
@@ -0,0 +1,15 @@
|
||||
apiVersion: rbac.authorization.k8s.io/v1
|
||||
kind: RoleBinding
|
||||
metadata:
|
||||
labels:
|
||||
app.kubernetes.io/component: redis
|
||||
app.kubernetes.io/name: argocd-redis
|
||||
app.kubernetes.io/part-of: argocd
|
||||
name: argocd-redis
|
||||
roleRef:
|
||||
apiGroup: rbac.authorization.k8s.io
|
||||
kind: Role
|
||||
name: argocd-redis
|
||||
subjects:
|
||||
- kind: ServiceAccount
|
||||
name: argocd-redis
|
||||
@@ -6,3 +6,5 @@ resources:
|
||||
- argocd-redis-sa.yaml
|
||||
- argocd-redis-service.yaml
|
||||
- argocd-redis-network-policy.yaml
|
||||
- argocd-redis-role.yaml
|
||||
- argocd-redis-rolebinding.yaml
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user