mirror of
https://github.com/argoproj/argo-cd.git
synced 2026-03-15 12:58:48 +01:00
Compare commits
46 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
3f1e7d401e | ||
|
|
85838126fc | ||
|
|
822788f0f9 | ||
|
|
8bc460fe28 | ||
|
|
814b2367c8 | ||
|
|
92ced542b8 | ||
|
|
0d579a0ec1 | ||
|
|
9c685cf021 | ||
|
|
37e24408a8 | ||
|
|
fdcbfade20 | ||
|
|
cf02b10d01 | ||
|
|
46dc70b6c6 | ||
|
|
d7e4ada9af | ||
|
|
292e69f97f | ||
|
|
618bd14c09 | ||
|
|
a047d72688 | ||
|
|
472d4eb16d | ||
|
|
674cf8d6e2 | ||
|
|
c1dba1c764 | ||
|
|
d383379b0c | ||
|
|
adbb1f50c8 | ||
|
|
419165a296 | ||
|
|
19f5d43235 | ||
|
|
2a433f168a | ||
|
|
494e9eeb51 | ||
|
|
cc75f42a10 | ||
|
|
876ff3035e | ||
|
|
4d63d279d6 | ||
|
|
62ae79ab5e | ||
|
|
6f5eaff91f | ||
|
|
c8b4707c55 | ||
|
|
09143f26a4 | ||
|
|
f08375665b | ||
|
|
0901195cd9 | ||
|
|
463e62ff80 | ||
|
|
71e523e776 | ||
|
|
61d0ef7614 | ||
|
|
d54d931f3e | ||
|
|
0c31efd158 | ||
|
|
a325c35320 | ||
|
|
9d56d4fa26 | ||
|
|
707342d707 | ||
|
|
de600c0222 | ||
|
|
937e88a164 | ||
|
|
4f3e5080a5 | ||
|
|
cf74364052 |
6
.github/workflows/ci-build.yaml
vendored
6
.github/workflows/ci-build.yaml
vendored
@@ -13,7 +13,7 @@ on:
|
||||
|
||||
env:
|
||||
# Golang version to use across CI steps
|
||||
GOLANG_VERSION: '1.18'
|
||||
GOLANG_VERSION: '1.19.7'
|
||||
|
||||
concurrency:
|
||||
group: ${{ github.workflow }}-${{ github.ref }}
|
||||
@@ -425,9 +425,9 @@ jobs:
|
||||
git config --global user.email "john.doe@example.com"
|
||||
- name: Pull Docker image required for tests
|
||||
run: |
|
||||
docker pull ghcr.io/dexidp/dex:v2.35.3
|
||||
docker pull ghcr.io/dexidp/dex:v2.36.0
|
||||
docker pull argoproj/argo-cd-ci-builder:v1.0.0
|
||||
docker pull redis:7.0.8-alpine
|
||||
docker pull redis:7.0.11-alpine
|
||||
- name: Create target directory for binaries in the build-process
|
||||
run: |
|
||||
mkdir -p dist
|
||||
|
||||
1
.github/workflows/codeql.yml
vendored
1
.github/workflows/codeql.yml
vendored
@@ -5,6 +5,7 @@ on:
|
||||
# Secrets aren't available for dependabot on push. https://docs.github.com/en/enterprise-cloud@latest/code-security/code-scanning/automatically-scanning-your-code-for-vulnerabilities-and-errors/troubleshooting-the-codeql-workflow#error-403-resource-not-accessible-by-integration-when-using-dependabot
|
||||
branches-ignore:
|
||||
- 'dependabot/**'
|
||||
- 'cherry-pick-*'
|
||||
pull_request:
|
||||
schedule:
|
||||
- cron: '0 19 * * 0'
|
||||
|
||||
2
.github/workflows/image.yaml
vendored
2
.github/workflows/image.yaml
vendored
@@ -10,7 +10,7 @@ on:
|
||||
types: [ labeled, unlabeled, opened, synchronize, reopened ]
|
||||
|
||||
env:
|
||||
GOLANG_VERSION: '1.18'
|
||||
GOLANG_VERSION: '1.19.7'
|
||||
|
||||
concurrency:
|
||||
group: ${{ github.workflow }}-${{ github.ref }}
|
||||
|
||||
2
.github/workflows/release.yaml
vendored
2
.github/workflows/release.yaml
vendored
@@ -12,7 +12,7 @@ on:
|
||||
- "!release-v0*"
|
||||
|
||||
env:
|
||||
GOLANG_VERSION: '1.18'
|
||||
GOLANG_VERSION: '1.19.7'
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
|
||||
@@ -4,7 +4,7 @@ ARG BASE_IMAGE=docker.io/library/ubuntu:22.04
|
||||
# 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.18 AS builder
|
||||
FROM docker.io/library/golang:1.19.7 AS builder
|
||||
|
||||
RUN echo 'deb http://deb.debian.org/debian buster-backports main' >> /etc/apt/sources.list
|
||||
|
||||
@@ -99,7 +99,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.18 AS argocd-build
|
||||
FROM --platform=$BUILDPLATFORM docker.io/library/golang:1.19.7 AS argocd-build
|
||||
|
||||
WORKDIR /go/src/github.com/argoproj/argo-cd
|
||||
|
||||
|
||||
2
USERS.md
2
USERS.md
@@ -202,6 +202,7 @@ Currently, the following organizations are **officially** using Argo CD:
|
||||
1. [SI Analytics](https://si-analytics.ai)
|
||||
1. [Skit](https://skit.ai/)
|
||||
1. [Skyscanner](https://www.skyscanner.net/)
|
||||
1. [Smart Pension](https://www.smartpension.co.uk/)
|
||||
1. [Smilee.io](https://smilee.io)
|
||||
1. [Snapp](https://snapp.ir/)
|
||||
1. [Snyk](https://snyk.io/)
|
||||
@@ -239,6 +240,7 @@ Currently, the following organizations are **officially** using Argo CD:
|
||||
1. [ungleich.ch](https://ungleich.ch/)
|
||||
1. [Unifonic Inc](https://www.unifonic.com/)
|
||||
1. [Universidad Mesoamericana](https://www.umes.edu.gt/)
|
||||
1. [Urbantz](https://urbantz.com/)
|
||||
1. [Viaduct](https://www.viaduct.ai/)
|
||||
1. [Vinted](https://vinted.com/)
|
||||
1. [Virtuo](https://www.govirtuo.com/)
|
||||
|
||||
@@ -2,6 +2,7 @@ package controllers
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
|
||||
log "github.com/sirupsen/logrus"
|
||||
|
||||
@@ -46,7 +47,6 @@ type addRateLimitingInterface interface {
|
||||
}
|
||||
|
||||
func (h *clusterSecretEventHandler) queueRelatedAppGenerators(q addRateLimitingInterface, object client.Object) {
|
||||
|
||||
// Check for label, lookup all ApplicationSets that might match the cluster, queue them all
|
||||
if object.GetLabels()[generators.ArgoCDSecretTypeLabel] != generators.ArgoCDSecretTypeCluster {
|
||||
return
|
||||
@@ -73,6 +73,40 @@ func (h *clusterSecretEventHandler) queueRelatedAppGenerators(q addRateLimitingI
|
||||
foundClusterGenerator = true
|
||||
break
|
||||
}
|
||||
|
||||
if generator.Matrix != nil {
|
||||
ok, err := nestedGeneratorsHaveClusterGenerator(generator.Matrix.Generators)
|
||||
if err != nil {
|
||||
h.Log.
|
||||
WithFields(log.Fields{
|
||||
"namespace": appSet.GetNamespace(),
|
||||
"name": appSet.GetName(),
|
||||
}).
|
||||
WithError(err).
|
||||
Error("Unable to check if ApplicationSet matrix generators have cluster generator")
|
||||
}
|
||||
if ok {
|
||||
foundClusterGenerator = true
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
if generator.Merge != nil {
|
||||
ok, err := nestedGeneratorsHaveClusterGenerator(generator.Merge.Generators)
|
||||
if err != nil {
|
||||
h.Log.
|
||||
WithFields(log.Fields{
|
||||
"namespace": appSet.GetNamespace(),
|
||||
"name": appSet.GetName(),
|
||||
}).
|
||||
WithError(err).
|
||||
Error("Unable to check if ApplicationSet merge generators have cluster generator")
|
||||
}
|
||||
if ok {
|
||||
foundClusterGenerator = true
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
if foundClusterGenerator {
|
||||
|
||||
@@ -82,3 +116,42 @@ func (h *clusterSecretEventHandler) queueRelatedAppGenerators(q addRateLimitingI
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// nestedGeneratorsHaveClusterGenerator iterate over provided nested generators to check if they have a cluster generator.
|
||||
func nestedGeneratorsHaveClusterGenerator(generators []argoprojiov1alpha1.ApplicationSetNestedGenerator) (bool, error) {
|
||||
for _, generator := range generators {
|
||||
if ok, err := nestedGeneratorHasClusterGenerator(generator); ok || err != nil {
|
||||
return ok, err
|
||||
}
|
||||
}
|
||||
return false, nil
|
||||
}
|
||||
|
||||
// nestedGeneratorHasClusterGenerator checks if the provided generator has a cluster generator.
|
||||
func nestedGeneratorHasClusterGenerator(nested argoprojiov1alpha1.ApplicationSetNestedGenerator) (bool, error) {
|
||||
if nested.Clusters != nil {
|
||||
return true, nil
|
||||
}
|
||||
|
||||
if nested.Matrix != nil {
|
||||
nestedMatrix, err := argoprojiov1alpha1.ToNestedMatrixGenerator(nested.Matrix)
|
||||
if err != nil {
|
||||
return false, fmt.Errorf("unable to get nested matrix generator: %w", err)
|
||||
}
|
||||
if nestedMatrix != nil {
|
||||
return nestedGeneratorsHaveClusterGenerator(nestedMatrix.ToMatrixGenerator().Generators)
|
||||
}
|
||||
}
|
||||
|
||||
if nested.Merge != nil {
|
||||
nestedMerge, err := argoprojiov1alpha1.ToNestedMergeGenerator(nested.Merge)
|
||||
if err != nil {
|
||||
return false, fmt.Errorf("unable to get nested merge generator: %w", err)
|
||||
}
|
||||
if nestedMerge != nil {
|
||||
return nestedGeneratorsHaveClusterGenerator(nestedMerge.ToMergeGenerator().Generators)
|
||||
}
|
||||
}
|
||||
|
||||
return false, nil
|
||||
}
|
||||
|
||||
@@ -6,6 +6,7 @@ import (
|
||||
log "github.com/sirupsen/logrus"
|
||||
"github.com/stretchr/testify/assert"
|
||||
corev1 "k8s.io/api/core/v1"
|
||||
apiextensionsv1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1"
|
||||
v1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/apimachinery/pkg/runtime"
|
||||
"k8s.io/apimachinery/pkg/types"
|
||||
@@ -163,7 +164,6 @@ func TestClusterEventHandler(t *testing.T) {
|
||||
{NamespacedName: types.NamespacedName{Namespace: "another-namespace", Name: "my-app-set"}},
|
||||
},
|
||||
},
|
||||
|
||||
{
|
||||
name: "non-argo cd secret should not match",
|
||||
items: []argov1alpha1.ApplicationSet{
|
||||
@@ -189,6 +189,348 @@ func TestClusterEventHandler(t *testing.T) {
|
||||
},
|
||||
expectedRequests: []reconcile.Request{},
|
||||
},
|
||||
{
|
||||
name: "a matrix generator with a cluster generator should produce a request",
|
||||
items: []argov1alpha1.ApplicationSet{
|
||||
{
|
||||
ObjectMeta: v1.ObjectMeta{
|
||||
Name: "my-app-set",
|
||||
Namespace: "argocd",
|
||||
},
|
||||
Spec: argov1alpha1.ApplicationSetSpec{
|
||||
Generators: []argov1alpha1.ApplicationSetGenerator{
|
||||
{
|
||||
Matrix: &argov1alpha1.MatrixGenerator{
|
||||
Generators: []argov1alpha1.ApplicationSetNestedGenerator{
|
||||
{
|
||||
Clusters: &argov1alpha1.ClusterGenerator{},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
secret: corev1.Secret{
|
||||
ObjectMeta: v1.ObjectMeta{
|
||||
Namespace: "argocd",
|
||||
Name: "my-secret",
|
||||
Labels: map[string]string{
|
||||
generators.ArgoCDSecretTypeLabel: generators.ArgoCDSecretTypeCluster,
|
||||
},
|
||||
},
|
||||
},
|
||||
expectedRequests: []reconcile.Request{{
|
||||
NamespacedName: types.NamespacedName{Namespace: "argocd", Name: "my-app-set"},
|
||||
}},
|
||||
},
|
||||
{
|
||||
name: "a matrix generator with non cluster generator should not match",
|
||||
items: []argov1alpha1.ApplicationSet{
|
||||
{
|
||||
ObjectMeta: v1.ObjectMeta{
|
||||
Name: "my-app-set",
|
||||
Namespace: "argocd",
|
||||
},
|
||||
Spec: argov1alpha1.ApplicationSetSpec{
|
||||
Generators: []argov1alpha1.ApplicationSetGenerator{
|
||||
{
|
||||
Matrix: &argov1alpha1.MatrixGenerator{
|
||||
Generators: []argov1alpha1.ApplicationSetNestedGenerator{
|
||||
{
|
||||
List: &argov1alpha1.ListGenerator{},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
secret: corev1.Secret{
|
||||
ObjectMeta: v1.ObjectMeta{
|
||||
Namespace: "argocd",
|
||||
Name: "my-secret",
|
||||
Labels: map[string]string{
|
||||
generators.ArgoCDSecretTypeLabel: generators.ArgoCDSecretTypeCluster,
|
||||
},
|
||||
},
|
||||
},
|
||||
expectedRequests: []reconcile.Request{},
|
||||
},
|
||||
{
|
||||
name: "a matrix generator with a nested matrix generator containing a cluster generator should produce a request",
|
||||
items: []argov1alpha1.ApplicationSet{
|
||||
{
|
||||
ObjectMeta: v1.ObjectMeta{
|
||||
Name: "my-app-set",
|
||||
Namespace: "argocd",
|
||||
},
|
||||
Spec: argov1alpha1.ApplicationSetSpec{
|
||||
Generators: []argov1alpha1.ApplicationSetGenerator{
|
||||
{
|
||||
Matrix: &argov1alpha1.MatrixGenerator{
|
||||
Generators: []argov1alpha1.ApplicationSetNestedGenerator{
|
||||
{
|
||||
Matrix: &apiextensionsv1.JSON{
|
||||
Raw: []byte(
|
||||
`{
|
||||
"generators": [
|
||||
{
|
||||
"clusters": {
|
||||
"selector": {
|
||||
"matchLabels": {
|
||||
"argocd.argoproj.io/secret-type": "cluster"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
}`,
|
||||
),
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
secret: corev1.Secret{
|
||||
ObjectMeta: v1.ObjectMeta{
|
||||
Namespace: "argocd",
|
||||
Name: "my-secret",
|
||||
Labels: map[string]string{
|
||||
generators.ArgoCDSecretTypeLabel: generators.ArgoCDSecretTypeCluster,
|
||||
},
|
||||
},
|
||||
},
|
||||
expectedRequests: []reconcile.Request{{
|
||||
NamespacedName: types.NamespacedName{Namespace: "argocd", Name: "my-app-set"},
|
||||
}},
|
||||
},
|
||||
{
|
||||
name: "a matrix generator with a nested matrix generator containing non cluster generator should not match",
|
||||
items: []argov1alpha1.ApplicationSet{
|
||||
{
|
||||
ObjectMeta: v1.ObjectMeta{
|
||||
Name: "my-app-set",
|
||||
Namespace: "argocd",
|
||||
},
|
||||
Spec: argov1alpha1.ApplicationSetSpec{
|
||||
Generators: []argov1alpha1.ApplicationSetGenerator{
|
||||
{
|
||||
Matrix: &argov1alpha1.MatrixGenerator{
|
||||
Generators: []argov1alpha1.ApplicationSetNestedGenerator{
|
||||
{
|
||||
Matrix: &apiextensionsv1.JSON{
|
||||
Raw: []byte(
|
||||
`{
|
||||
"generators": [
|
||||
{
|
||||
"list": {
|
||||
"elements": [
|
||||
"a",
|
||||
"b"
|
||||
]
|
||||
}
|
||||
}
|
||||
]
|
||||
}`,
|
||||
),
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
secret: corev1.Secret{
|
||||
ObjectMeta: v1.ObjectMeta{
|
||||
Namespace: "argocd",
|
||||
Name: "my-secret",
|
||||
Labels: map[string]string{
|
||||
generators.ArgoCDSecretTypeLabel: generators.ArgoCDSecretTypeCluster,
|
||||
},
|
||||
},
|
||||
},
|
||||
expectedRequests: []reconcile.Request{},
|
||||
},
|
||||
{
|
||||
name: "a merge generator with a cluster generator should produce a request",
|
||||
items: []argov1alpha1.ApplicationSet{
|
||||
{
|
||||
ObjectMeta: v1.ObjectMeta{
|
||||
Name: "my-app-set",
|
||||
Namespace: "argocd",
|
||||
},
|
||||
Spec: argov1alpha1.ApplicationSetSpec{
|
||||
Generators: []argov1alpha1.ApplicationSetGenerator{
|
||||
{
|
||||
Merge: &argov1alpha1.MergeGenerator{
|
||||
Generators: []argov1alpha1.ApplicationSetNestedGenerator{
|
||||
{
|
||||
Clusters: &argov1alpha1.ClusterGenerator{},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
secret: corev1.Secret{
|
||||
ObjectMeta: v1.ObjectMeta{
|
||||
Namespace: "argocd",
|
||||
Name: "my-secret",
|
||||
Labels: map[string]string{
|
||||
generators.ArgoCDSecretTypeLabel: generators.ArgoCDSecretTypeCluster,
|
||||
},
|
||||
},
|
||||
},
|
||||
expectedRequests: []reconcile.Request{{
|
||||
NamespacedName: types.NamespacedName{Namespace: "argocd", Name: "my-app-set"},
|
||||
}},
|
||||
},
|
||||
{
|
||||
name: "a matrix generator with non cluster generator should not match",
|
||||
items: []argov1alpha1.ApplicationSet{
|
||||
{
|
||||
ObjectMeta: v1.ObjectMeta{
|
||||
Name: "my-app-set",
|
||||
Namespace: "argocd",
|
||||
},
|
||||
Spec: argov1alpha1.ApplicationSetSpec{
|
||||
Generators: []argov1alpha1.ApplicationSetGenerator{
|
||||
{
|
||||
Merge: &argov1alpha1.MergeGenerator{
|
||||
Generators: []argov1alpha1.ApplicationSetNestedGenerator{
|
||||
{
|
||||
List: &argov1alpha1.ListGenerator{},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
secret: corev1.Secret{
|
||||
ObjectMeta: v1.ObjectMeta{
|
||||
Namespace: "argocd",
|
||||
Name: "my-secret",
|
||||
Labels: map[string]string{
|
||||
generators.ArgoCDSecretTypeLabel: generators.ArgoCDSecretTypeCluster,
|
||||
},
|
||||
},
|
||||
},
|
||||
expectedRequests: []reconcile.Request{},
|
||||
},
|
||||
{
|
||||
name: "a merge generator with a nested merge generator containing a cluster generator should produce a request",
|
||||
items: []argov1alpha1.ApplicationSet{
|
||||
{
|
||||
ObjectMeta: v1.ObjectMeta{
|
||||
Name: "my-app-set",
|
||||
Namespace: "argocd",
|
||||
},
|
||||
Spec: argov1alpha1.ApplicationSetSpec{
|
||||
Generators: []argov1alpha1.ApplicationSetGenerator{
|
||||
{
|
||||
Merge: &argov1alpha1.MergeGenerator{
|
||||
Generators: []argov1alpha1.ApplicationSetNestedGenerator{
|
||||
{
|
||||
Merge: &apiextensionsv1.JSON{
|
||||
Raw: []byte(
|
||||
`{
|
||||
"generators": [
|
||||
{
|
||||
"clusters": {
|
||||
"selector": {
|
||||
"matchLabels": {
|
||||
"argocd.argoproj.io/secret-type": "cluster"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
}`,
|
||||
),
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
secret: corev1.Secret{
|
||||
ObjectMeta: v1.ObjectMeta{
|
||||
Namespace: "argocd",
|
||||
Name: "my-secret",
|
||||
Labels: map[string]string{
|
||||
generators.ArgoCDSecretTypeLabel: generators.ArgoCDSecretTypeCluster,
|
||||
},
|
||||
},
|
||||
},
|
||||
expectedRequests: []reconcile.Request{{
|
||||
NamespacedName: types.NamespacedName{Namespace: "argocd", Name: "my-app-set"},
|
||||
}},
|
||||
},
|
||||
{
|
||||
name: "a merge generator with a nested merge generator containing non cluster generator should not match",
|
||||
items: []argov1alpha1.ApplicationSet{
|
||||
{
|
||||
ObjectMeta: v1.ObjectMeta{
|
||||
Name: "my-app-set",
|
||||
Namespace: "argocd",
|
||||
},
|
||||
Spec: argov1alpha1.ApplicationSetSpec{
|
||||
Generators: []argov1alpha1.ApplicationSetGenerator{
|
||||
{
|
||||
Merge: &argov1alpha1.MergeGenerator{
|
||||
Generators: []argov1alpha1.ApplicationSetNestedGenerator{
|
||||
{
|
||||
Merge: &apiextensionsv1.JSON{
|
||||
Raw: []byte(
|
||||
`{
|
||||
"generators": [
|
||||
{
|
||||
"list": {
|
||||
"elements": [
|
||||
"a",
|
||||
"b"
|
||||
]
|
||||
}
|
||||
}
|
||||
]
|
||||
}`,
|
||||
),
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
secret: corev1.Secret{
|
||||
ObjectMeta: v1.ObjectMeta{
|
||||
Namespace: "argocd",
|
||||
Name: "my-secret",
|
||||
Labels: map[string]string{
|
||||
generators.ArgoCDSecretTypeLabel: generators.ArgoCDSecretTypeCluster,
|
||||
},
|
||||
},
|
||||
},
|
||||
expectedRequests: []reconcile.Request{},
|
||||
},
|
||||
}
|
||||
|
||||
for _, test := range tests {
|
||||
|
||||
@@ -5,8 +5,8 @@ import (
|
||||
"reflect"
|
||||
|
||||
"github.com/argoproj/argo-cd/v2/applicationset/utils"
|
||||
"github.com/jeremywohl/flatten"
|
||||
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/apimachinery/pkg/labels"
|
||||
|
||||
argoprojiov1alpha1 "github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1"
|
||||
@@ -26,7 +26,10 @@ type TransformResult struct {
|
||||
|
||||
// Transform a spec generator to list of paramSets and a template
|
||||
func Transform(requestedGenerator argoprojiov1alpha1.ApplicationSetGenerator, allGenerators map[string]Generator, baseTemplate argoprojiov1alpha1.ApplicationSetTemplate, appSet *argoprojiov1alpha1.ApplicationSet, genParams map[string]interface{}) ([]TransformResult, error) {
|
||||
selector, err := metav1.LabelSelectorAsSelector(requestedGenerator.Selector)
|
||||
// This is a custom version of the `LabelSelectorAsSelector` that is in k8s.io/apimachinery. This has been copied
|
||||
// verbatim from that package, with the difference that we do not have any restrictions on label values. This is done
|
||||
// so that, among other things, we can match on cluster urls.
|
||||
selector, err := utils.LabelSelectorAsSelector(requestedGenerator.Selector)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("error parsing label selector: %w", err)
|
||||
}
|
||||
@@ -71,8 +74,17 @@ func Transform(requestedGenerator argoprojiov1alpha1.ApplicationSetGenerator, al
|
||||
}
|
||||
var filterParams []map[string]interface{}
|
||||
for _, param := range params {
|
||||
flatParam, err := flattenParameters(param)
|
||||
if err != nil {
|
||||
log.WithError(err).WithField("generator", g).
|
||||
Error("error flattening params")
|
||||
if firstError == nil {
|
||||
firstError = err
|
||||
}
|
||||
continue
|
||||
}
|
||||
|
||||
if requestedGenerator.Selector != nil && !selector.Matches(labels.Set(keepOnlyStringValues(param))) {
|
||||
if requestedGenerator.Selector != nil && !selector.Matches(labels.Set(flatParam)) {
|
||||
continue
|
||||
}
|
||||
filterParams = append(filterParams, param)
|
||||
@@ -87,18 +99,6 @@ func Transform(requestedGenerator argoprojiov1alpha1.ApplicationSetGenerator, al
|
||||
return res, firstError
|
||||
}
|
||||
|
||||
func keepOnlyStringValues(in map[string]interface{}) map[string]string {
|
||||
var out map[string]string = map[string]string{}
|
||||
|
||||
for key, value := range in {
|
||||
if _, ok := value.(string); ok {
|
||||
out[key] = value.(string)
|
||||
}
|
||||
}
|
||||
|
||||
return out
|
||||
}
|
||||
|
||||
func GetRelevantGenerators(requestedGenerator *argoprojiov1alpha1.ApplicationSetGenerator, generators map[string]Generator) []Generator {
|
||||
var res []Generator
|
||||
|
||||
@@ -121,6 +121,20 @@ func GetRelevantGenerators(requestedGenerator *argoprojiov1alpha1.ApplicationSet
|
||||
return res
|
||||
}
|
||||
|
||||
func flattenParameters(in map[string]interface{}) (map[string]string, error) {
|
||||
flat, err := flatten.Flatten(in, "", flatten.DotStyle)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
out := make(map[string]string, len(flat))
|
||||
for k, v := range flat {
|
||||
out[k] = fmt.Sprintf("%v", v)
|
||||
}
|
||||
|
||||
return out, nil
|
||||
}
|
||||
|
||||
func mergeGeneratorTemplate(g Generator, requestedGenerator *argoprojiov1alpha1.ApplicationSetGenerator, applicationSetTemplate argoprojiov1alpha1.ApplicationSetTemplate) (argoprojiov1alpha1.ApplicationSetTemplate, error) {
|
||||
// Make a copy of the value from `GetTemplate()` before merge, rather than copying directly into
|
||||
// the provided parameter (which will touch the original resource object returned by client-go)
|
||||
|
||||
@@ -94,8 +94,160 @@ func TestMatchValues(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func emptyTemplate() argoprojiov1alpha1.ApplicationSetTemplate {
|
||||
return argoprojiov1alpha1.ApplicationSetTemplate{
|
||||
func TestMatchValuesGoTemplate(t *testing.T) {
|
||||
testCases := []struct {
|
||||
name string
|
||||
elements []apiextensionsv1.JSON
|
||||
selector *metav1.LabelSelector
|
||||
expected []map[string]interface{}
|
||||
}{
|
||||
{
|
||||
name: "no filter",
|
||||
elements: []apiextensionsv1.JSON{{Raw: []byte(`{"cluster": "cluster","url": "url"}`)}},
|
||||
selector: &metav1.LabelSelector{},
|
||||
expected: []map[string]interface{}{{"cluster": "cluster", "url": "url"}},
|
||||
},
|
||||
{
|
||||
name: "nil",
|
||||
elements: []apiextensionsv1.JSON{{Raw: []byte(`{"cluster": "cluster","url": "url"}`)}},
|
||||
selector: nil,
|
||||
expected: []map[string]interface{}{{"cluster": "cluster", "url": "url"}},
|
||||
},
|
||||
{
|
||||
name: "values.foo should be foo but is ignore element",
|
||||
elements: []apiextensionsv1.JSON{{Raw: []byte(`{"cluster": "cluster","url": "url","values":{"foo":"bar"}}`)}},
|
||||
selector: &metav1.LabelSelector{
|
||||
MatchLabels: map[string]string{
|
||||
"values.foo": "foo",
|
||||
},
|
||||
},
|
||||
expected: []map[string]interface{}{},
|
||||
},
|
||||
{
|
||||
name: "values.foo should be bar",
|
||||
elements: []apiextensionsv1.JSON{{Raw: []byte(`{"cluster": "cluster","url": "url","values":{"foo":"bar"}}`)}},
|
||||
selector: &metav1.LabelSelector{
|
||||
MatchLabels: map[string]string{
|
||||
"values.foo": "bar",
|
||||
},
|
||||
},
|
||||
expected: []map[string]interface{}{{"cluster": "cluster", "url": "url", "values": map[string]interface{}{"foo": "bar"}}},
|
||||
},
|
||||
{
|
||||
name: "values.0 should be bar",
|
||||
elements: []apiextensionsv1.JSON{{Raw: []byte(`{"cluster": "cluster","url": "url","values":["bar"]}`)}},
|
||||
selector: &metav1.LabelSelector{
|
||||
MatchLabels: map[string]string{
|
||||
"values.0": "bar",
|
||||
},
|
||||
},
|
||||
expected: []map[string]interface{}{{"cluster": "cluster", "url": "url", "values": []interface{}{"bar"}}},
|
||||
},
|
||||
}
|
||||
|
||||
for _, testCase := range testCases {
|
||||
t.Run(testCase.name, func(t *testing.T) {
|
||||
var listGenerator = NewListGenerator()
|
||||
var data = map[string]Generator{
|
||||
"List": listGenerator,
|
||||
}
|
||||
|
||||
applicationSetInfo := argov1alpha1.ApplicationSet{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "set",
|
||||
},
|
||||
Spec: argov1alpha1.ApplicationSetSpec{
|
||||
GoTemplate: true,
|
||||
},
|
||||
}
|
||||
|
||||
results, err := Transform(argov1alpha1.ApplicationSetGenerator{
|
||||
Selector: testCase.selector,
|
||||
List: &argov1alpha1.ListGenerator{
|
||||
Elements: testCase.elements,
|
||||
Template: emptyTemplate(),
|
||||
}},
|
||||
data,
|
||||
emptyTemplate(),
|
||||
&applicationSetInfo, nil)
|
||||
|
||||
assert.NoError(t, err)
|
||||
assert.ElementsMatch(t, testCase.expected, results[0].Params)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestTransForm(t *testing.T) {
|
||||
testCases := []struct {
|
||||
name string
|
||||
selector *metav1.LabelSelector
|
||||
expected []map[string]interface{}
|
||||
}{
|
||||
{
|
||||
name: "server filter",
|
||||
selector: &metav1.LabelSelector{
|
||||
MatchLabels: map[string]string{"server": "https://production-01.example.com"},
|
||||
},
|
||||
expected: []map[string]interface{}{{
|
||||
"metadata.annotations.foo.argoproj.io": "production",
|
||||
"metadata.labels.argocd.argoproj.io/secret-type": "cluster",
|
||||
"metadata.labels.environment": "production",
|
||||
"metadata.labels.org": "bar",
|
||||
"name": "production_01/west",
|
||||
"nameNormalized": "production-01-west",
|
||||
"server": "https://production-01.example.com",
|
||||
}},
|
||||
},
|
||||
{
|
||||
name: "server filter with long url",
|
||||
selector: &metav1.LabelSelector{
|
||||
MatchLabels: map[string]string{"server": "https://some-really-long-url-that-will-exceed-63-characters.com"},
|
||||
},
|
||||
expected: []map[string]interface{}{{
|
||||
"metadata.annotations.foo.argoproj.io": "production",
|
||||
"metadata.labels.argocd.argoproj.io/secret-type": "cluster",
|
||||
"metadata.labels.environment": "production",
|
||||
"metadata.labels.org": "bar",
|
||||
"name": "some-really-long-server-url",
|
||||
"nameNormalized": "some-really-long-server-url",
|
||||
"server": "https://some-really-long-url-that-will-exceed-63-characters.com",
|
||||
}},
|
||||
},
|
||||
}
|
||||
|
||||
for _, testCase := range testCases {
|
||||
t.Run(testCase.name, func(t *testing.T) {
|
||||
testGenerators := map[string]Generator{
|
||||
"Clusters": getMockClusterGenerator(),
|
||||
}
|
||||
|
||||
applicationSetInfo := argov1alpha1.ApplicationSet{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "set",
|
||||
},
|
||||
Spec: argov1alpha1.ApplicationSetSpec{},
|
||||
}
|
||||
|
||||
results, err := Transform(
|
||||
argov1alpha1.ApplicationSetGenerator{
|
||||
Selector: testCase.selector,
|
||||
Clusters: &argov1alpha1.ClusterGenerator{
|
||||
Selector: metav1.LabelSelector{},
|
||||
Template: argov1alpha1.ApplicationSetTemplate{},
|
||||
Values: nil,
|
||||
}},
|
||||
testGenerators,
|
||||
emptyTemplate(),
|
||||
&applicationSetInfo, nil)
|
||||
|
||||
assert.NoError(t, err)
|
||||
assert.ElementsMatch(t, testCase.expected, results[0].Params)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func emptyTemplate() argov1alpha1.ApplicationSetTemplate {
|
||||
return argov1alpha1.ApplicationSetTemplate{
|
||||
Spec: argov1alpha1.ApplicationSpec{
|
||||
Project: "project",
|
||||
},
|
||||
@@ -152,8 +304,35 @@ func getMockClusterGenerator() Generator {
|
||||
},
|
||||
Type: corev1.SecretType("Opaque"),
|
||||
},
|
||||
&corev1.Secret{
|
||||
TypeMeta: metav1.TypeMeta{
|
||||
Kind: "Secret",
|
||||
APIVersion: "v1",
|
||||
},
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "some-really-long-server-url",
|
||||
Namespace: "namespace",
|
||||
Labels: map[string]string{
|
||||
"argocd.argoproj.io/secret-type": "cluster",
|
||||
"environment": "production",
|
||||
"org": "bar",
|
||||
},
|
||||
Annotations: map[string]string{
|
||||
"foo.argoproj.io": "production",
|
||||
},
|
||||
},
|
||||
Data: map[string][]byte{
|
||||
"config": []byte("{}"),
|
||||
"name": []byte("some-really-long-server-url"),
|
||||
"server": []byte("https://some-really-long-url-that-will-exceed-63-characters.com"),
|
||||
},
|
||||
Type: corev1.SecretType("Opaque"),
|
||||
},
|
||||
}
|
||||
runtimeClusters := []runtime.Object{}
|
||||
for _, clientCluster := range clusters {
|
||||
runtimeClusters = append(runtimeClusters, clientCluster)
|
||||
}
|
||||
appClientset := kubefake.NewSimpleClientset(runtimeClusters...)
|
||||
|
||||
fakeClient := fake.NewClientBuilder().WithObjects(clusters...).Build()
|
||||
|
||||
@@ -30,7 +30,7 @@ func TestMatrixGenerate(t *testing.T) {
|
||||
}
|
||||
|
||||
listGenerator := &argoprojiov1alpha1.ListGenerator{
|
||||
Elements: []apiextensionsv1.JSON{{Raw: []byte(`{"cluster": "Cluster","url": "Url"}`)}},
|
||||
Elements: []apiextensionsv1.JSON{{Raw: []byte(`{"cluster": "Cluster","url": "Url", "templated": "test-{{path.basenameNormalized}}"}`)}},
|
||||
}
|
||||
|
||||
testCases := []struct {
|
||||
@@ -50,8 +50,8 @@ func TestMatrixGenerate(t *testing.T) {
|
||||
},
|
||||
},
|
||||
expected: []map[string]interface{}{
|
||||
{"path": "app1", "path.basename": "app1", "path.basenameNormalized": "app1", "cluster": "Cluster", "url": "Url"},
|
||||
{"path": "app2", "path.basename": "app2", "path.basenameNormalized": "app2", "cluster": "Cluster", "url": "Url"},
|
||||
{"path": "app1", "path.basename": "app1", "path.basenameNormalized": "app1", "cluster": "Cluster", "url": "Url", "templated": "test-app1"},
|
||||
{"path": "app2", "path.basename": "app2", "path.basenameNormalized": "app2", "cluster": "Cluster", "url": "Url", "templated": "test-app2"},
|
||||
},
|
||||
},
|
||||
{
|
||||
|
||||
261
applicationset/utils/selector.go
Normal file
261
applicationset/utils/selector.go
Normal file
@@ -0,0 +1,261 @@
|
||||
package utils
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
v1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/apimachinery/pkg/labels"
|
||||
"k8s.io/apimachinery/pkg/selection"
|
||||
"k8s.io/apimachinery/pkg/util/validation"
|
||||
"k8s.io/apimachinery/pkg/util/validation/field"
|
||||
"k8s.io/klog/v2"
|
||||
"sort"
|
||||
"strconv"
|
||||
"strings"
|
||||
)
|
||||
|
||||
var (
|
||||
unaryOperators = []string{
|
||||
string(selection.Exists), string(selection.DoesNotExist),
|
||||
}
|
||||
binaryOperators = []string{
|
||||
string(selection.In), string(selection.NotIn),
|
||||
string(selection.Equals), string(selection.DoubleEquals), string(selection.NotEquals),
|
||||
string(selection.GreaterThan), string(selection.LessThan),
|
||||
}
|
||||
validRequirementOperators = append(binaryOperators, unaryOperators...)
|
||||
)
|
||||
|
||||
// Selector represents a label selector.
|
||||
type Selector interface {
|
||||
// Matches returns true if this selector matches the given set of labels.
|
||||
Matches(labels.Labels) bool
|
||||
|
||||
// Add adds requirements to the Selector
|
||||
Add(r ...Requirement) Selector
|
||||
}
|
||||
|
||||
type internalSelector []Requirement
|
||||
|
||||
// ByKey sorts requirements by key to obtain deterministic parser
|
||||
type ByKey []Requirement
|
||||
|
||||
func (a ByKey) Len() int { return len(a) }
|
||||
|
||||
func (a ByKey) Swap(i, j int) { a[i], a[j] = a[j], a[i] }
|
||||
|
||||
func (a ByKey) Less(i, j int) bool { return a[i].key < a[j].key }
|
||||
|
||||
// Matches for a internalSelector returns true if all
|
||||
// its Requirements match the input Labels. If any
|
||||
// Requirement does not match, false is returned.
|
||||
func (s internalSelector) Matches(l labels.Labels) bool {
|
||||
for ix := range s {
|
||||
if matches := s[ix].Matches(l); !matches {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
// Add adds requirements to the selector. It copies the current selector returning a new one
|
||||
func (s internalSelector) Add(reqs ...Requirement) Selector {
|
||||
ret := make(internalSelector, 0, len(s)+len(reqs))
|
||||
ret = append(ret, s...)
|
||||
ret = append(ret, reqs...)
|
||||
sort.Sort(ByKey(ret))
|
||||
return ret
|
||||
}
|
||||
|
||||
type nothingSelector struct{}
|
||||
|
||||
func (n nothingSelector) Matches(l labels.Labels) bool {
|
||||
return false
|
||||
}
|
||||
|
||||
func (n nothingSelector) Add(r ...Requirement) Selector {
|
||||
return n
|
||||
}
|
||||
|
||||
// Nothing returns a selector that matches no labels
|
||||
func nothing() Selector {
|
||||
return nothingSelector{}
|
||||
}
|
||||
|
||||
// Everything returns a selector that matches all labels.
|
||||
func everything() Selector {
|
||||
return internalSelector{}
|
||||
}
|
||||
|
||||
// LabelSelectorAsSelector converts the LabelSelector api type into a struct that implements
|
||||
// labels.Selector
|
||||
// Note: This function should be kept in sync with the selector methods in pkg/labels/selector.go
|
||||
func LabelSelectorAsSelector(ps *v1.LabelSelector) (Selector, error) {
|
||||
if ps == nil {
|
||||
return nothing(), nil
|
||||
}
|
||||
if len(ps.MatchLabels)+len(ps.MatchExpressions) == 0 {
|
||||
return everything(), nil
|
||||
}
|
||||
requirements := make([]Requirement, 0, len(ps.MatchLabels)+len(ps.MatchExpressions))
|
||||
for k, v := range ps.MatchLabels {
|
||||
r, err := newRequirement(k, selection.Equals, []string{v})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
requirements = append(requirements, *r)
|
||||
}
|
||||
for _, expr := range ps.MatchExpressions {
|
||||
var op selection.Operator
|
||||
switch expr.Operator {
|
||||
case v1.LabelSelectorOpIn:
|
||||
op = selection.In
|
||||
case v1.LabelSelectorOpNotIn:
|
||||
op = selection.NotIn
|
||||
case v1.LabelSelectorOpExists:
|
||||
op = selection.Exists
|
||||
case v1.LabelSelectorOpDoesNotExist:
|
||||
op = selection.DoesNotExist
|
||||
default:
|
||||
return nil, fmt.Errorf("%q is not a valid pod selector operator", expr.Operator)
|
||||
}
|
||||
r, err := newRequirement(expr.Key, op, append([]string(nil), expr.Values...))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
requirements = append(requirements, *r)
|
||||
}
|
||||
selector := newSelector()
|
||||
selector = selector.Add(requirements...)
|
||||
return selector, nil
|
||||
}
|
||||
|
||||
// NewSelector returns a nil selector
|
||||
func newSelector() Selector {
|
||||
return internalSelector(nil)
|
||||
}
|
||||
|
||||
func validateLabelKey(k string, path *field.Path) *field.Error {
|
||||
if errs := validation.IsQualifiedName(k); len(errs) != 0 {
|
||||
return field.Invalid(path, k, strings.Join(errs, "; "))
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// NewRequirement is the constructor for a Requirement.
|
||||
// If any of these rules is violated, an error is returned:
|
||||
// (1) The operator can only be In, NotIn, Equals, DoubleEquals, Gt, Lt, NotEquals, Exists, or DoesNotExist.
|
||||
// (2) If the operator is In or NotIn, the values set must be non-empty.
|
||||
// (3) If the operator is Equals, DoubleEquals, or NotEquals, the values set must contain one value.
|
||||
// (4) If the operator is Exists or DoesNotExist, the value set must be empty.
|
||||
// (5) If the operator is Gt or Lt, the values set must contain only one value, which will be interpreted as an integer.
|
||||
// (6) The key is invalid due to its length, or sequence
|
||||
//
|
||||
// of characters. See validateLabelKey for more details.
|
||||
//
|
||||
// The empty string is a valid value in the input values set.
|
||||
// Returned error, if not nil, is guaranteed to be an aggregated field.ErrorList
|
||||
func newRequirement(key string, op selection.Operator, vals []string, opts ...field.PathOption) (*Requirement, error) {
|
||||
var allErrs field.ErrorList
|
||||
path := field.ToPath(opts...)
|
||||
if err := validateLabelKey(key, path.Child("key")); err != nil {
|
||||
allErrs = append(allErrs, err)
|
||||
}
|
||||
|
||||
valuePath := path.Child("values")
|
||||
switch op {
|
||||
case selection.In, selection.NotIn:
|
||||
if len(vals) == 0 {
|
||||
allErrs = append(allErrs, field.Invalid(valuePath, vals, "for 'in', 'notin' operators, values set can't be empty"))
|
||||
}
|
||||
case selection.Equals, selection.DoubleEquals, selection.NotEquals:
|
||||
if len(vals) != 1 {
|
||||
allErrs = append(allErrs, field.Invalid(valuePath, vals, "exact-match compatibility requires one single value"))
|
||||
}
|
||||
case selection.Exists, selection.DoesNotExist:
|
||||
if len(vals) != 0 {
|
||||
allErrs = append(allErrs, field.Invalid(valuePath, vals, "values set must be empty for exists and does not exist"))
|
||||
}
|
||||
case selection.GreaterThan, selection.LessThan:
|
||||
if len(vals) != 1 {
|
||||
allErrs = append(allErrs, field.Invalid(valuePath, vals, "for 'Gt', 'Lt' operators, exactly one value is required"))
|
||||
}
|
||||
for i := range vals {
|
||||
if _, err := strconv.ParseInt(vals[i], 10, 64); err != nil {
|
||||
allErrs = append(allErrs, field.Invalid(valuePath.Index(i), vals[i], "for 'Gt', 'Lt' operators, the value must be an integer"))
|
||||
}
|
||||
}
|
||||
default:
|
||||
allErrs = append(allErrs, field.NotSupported(path.Child("operator"), op, validRequirementOperators))
|
||||
}
|
||||
|
||||
return &Requirement{key: key, operator: op, strValues: vals}, allErrs.ToAggregate()
|
||||
}
|
||||
|
||||
// Requirement contains values, a key, and an operator that relates the key and values.
|
||||
// The zero value of Requirement is invalid.
|
||||
// Requirement implements both set based match and exact match
|
||||
// Requirement should be initialized via NewRequirement constructor for creating a valid Requirement.
|
||||
// +k8s:deepcopy-gen=true
|
||||
type Requirement struct {
|
||||
key string
|
||||
operator selection.Operator
|
||||
// In the majority of cases we have at most one value here.
|
||||
// It is generally faster to operate on a single-element slice
|
||||
// than on a single-element map, so we have a slice here.
|
||||
strValues []string
|
||||
}
|
||||
|
||||
func (r *Requirement) hasValue(value string) bool {
|
||||
for i := range r.strValues {
|
||||
if r.strValues[i] == value {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func (r *Requirement) Matches(ls labels.Labels) bool {
|
||||
switch r.operator {
|
||||
case selection.In, selection.Equals, selection.DoubleEquals:
|
||||
if !ls.Has(r.key) {
|
||||
return false
|
||||
}
|
||||
return r.hasValue(ls.Get(r.key))
|
||||
case selection.NotIn, selection.NotEquals:
|
||||
if !ls.Has(r.key) {
|
||||
return true
|
||||
}
|
||||
return !r.hasValue(ls.Get(r.key))
|
||||
case selection.Exists:
|
||||
return ls.Has(r.key)
|
||||
case selection.DoesNotExist:
|
||||
return !ls.Has(r.key)
|
||||
case selection.GreaterThan, selection.LessThan:
|
||||
if !ls.Has(r.key) {
|
||||
return false
|
||||
}
|
||||
lsValue, err := strconv.ParseInt(ls.Get(r.key), 10, 64)
|
||||
if err != nil {
|
||||
klog.V(10).Infof("ParseInt failed for value %+v in label %+v, %+v", ls.Get(r.key), ls, err)
|
||||
return false
|
||||
}
|
||||
|
||||
// There should be only one strValue in r.strValues, and can be converted to an integer.
|
||||
if len(r.strValues) != 1 {
|
||||
klog.V(10).Infof("Invalid values count %+v of requirement %#v, for 'Gt', 'Lt' operators, exactly one value is required", len(r.strValues), r)
|
||||
return false
|
||||
}
|
||||
|
||||
var rValue int64
|
||||
for i := range r.strValues {
|
||||
rValue, err = strconv.ParseInt(r.strValues[i], 10, 64)
|
||||
if err != nil {
|
||||
klog.V(10).Infof("ParseInt failed for value %+v in requirement %#v, for 'Gt', 'Lt' operators, the value must be an integer", r.strValues[i], r)
|
||||
return false
|
||||
}
|
||||
}
|
||||
return (r.operator == selection.GreaterThan && lsValue > rValue) || (r.operator == selection.LessThan && lsValue < rValue)
|
||||
default:
|
||||
return false
|
||||
}
|
||||
}
|
||||
@@ -96,6 +96,25 @@ func (r *Render) deeplyReplace(copy, original reflect.Value, replaceMap map[stri
|
||||
// specific case time
|
||||
if currentType == "time.Time" {
|
||||
copy.Field(i).Set(original.Field(i))
|
||||
} else if currentType == "Raw.k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1" {
|
||||
var unmarshaled interface{}
|
||||
originalBytes := original.Field(i).Bytes()
|
||||
err := json.Unmarshal(originalBytes, &unmarshaled)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to unmarshal JSON field: %w", err)
|
||||
}
|
||||
jsonOriginal := reflect.ValueOf(&unmarshaled)
|
||||
jsonCopy := reflect.New(jsonOriginal.Type()).Elem()
|
||||
err = r.deeplyReplace(jsonCopy, jsonOriginal, replaceMap, useGoTemplate)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to deeply replace JSON field contents: %w", err)
|
||||
}
|
||||
jsonCopyInterface := jsonCopy.Interface().(*interface{})
|
||||
data, err := json.Marshal(jsonCopyInterface)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to marshal templated JSON field: %w", err)
|
||||
}
|
||||
copy.Field(i).Set(reflect.ValueOf(data))
|
||||
} else if err := r.deeplyReplace(copy.Field(i), original.Field(i), replaceMap, useGoTemplate); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
@@ -9,13 +9,16 @@ import (
|
||||
"github.com/argoproj/argo-cd/v2/cmd/argocd/commands/initialize"
|
||||
"github.com/argoproj/argo-cd/v2/common"
|
||||
argocdclient "github.com/argoproj/argo-cd/v2/pkg/apiclient"
|
||||
"github.com/argoproj/argo-cd/v2/util/cache"
|
||||
"github.com/argoproj/argo-cd/v2/util/env"
|
||||
"github.com/argoproj/argo-cd/v2/util/errors"
|
||||
)
|
||||
|
||||
func NewDashboardCommand() *cobra.Command {
|
||||
var (
|
||||
port int
|
||||
address string
|
||||
port int
|
||||
address string
|
||||
compressionStr string
|
||||
)
|
||||
cmd := &cobra.Command{
|
||||
Use: "dashboard",
|
||||
@@ -23,7 +26,9 @@ func NewDashboardCommand() *cobra.Command {
|
||||
Run: func(cmd *cobra.Command, args []string) {
|
||||
ctx := cmd.Context()
|
||||
|
||||
errors.CheckError(headless.StartLocalServer(ctx, &argocdclient.ClientOptions{Core: true}, initialize.RetrieveContextIfChanged(cmd.Flag("context")), &port, &address))
|
||||
compression, err := cache.CompressionTypeFromString(compressionStr)
|
||||
errors.CheckError(err)
|
||||
errors.CheckError(headless.StartLocalServer(ctx, &argocdclient.ClientOptions{Core: true}, initialize.RetrieveContextIfChanged(cmd.Flag("context")), &port, &address, compression))
|
||||
println(fmt.Sprintf("Argo CD UI is available at http://%s:%d", address, port))
|
||||
<-ctx.Done()
|
||||
},
|
||||
@@ -31,5 +36,6 @@ func NewDashboardCommand() *cobra.Command {
|
||||
initialize.InitCommand(cmd)
|
||||
cmd.Flags().IntVar(&port, "port", common.DefaultPortAPIServer, "Listen on given port")
|
||||
cmd.Flags().StringVar(&address, "address", common.DefaultAddressAPIServer, "Listen on given address")
|
||||
cmd.Flags().StringVar(&compressionStr, "redis-compress", env.StringFromEnv("REDIS_COMPRESSION", string(cache.RedisCompressionNone)), "Enable this if the application controller is configured with redis compression enabled. (possible values: none, gzip)")
|
||||
return cmd
|
||||
}
|
||||
|
||||
@@ -1446,7 +1446,7 @@ func NewApplicationWaitCommand(clientOpts *argocdclient.ClientOptions) *cobra.Co
|
||||
}
|
||||
}
|
||||
for _, appName := range appNames {
|
||||
_, err := waitOnApplicationStatus(ctx, acdClient, appName, timeout, watch, selectedResources)
|
||||
_, _, err := waitOnApplicationStatus(ctx, acdClient, appName, timeout, watch, selectedResources)
|
||||
errors.CheckError(err)
|
||||
}
|
||||
},
|
||||
@@ -1712,15 +1712,15 @@ func NewApplicationSyncCommand(clientOpts *argocdclient.ClientOptions) *cobra.Co
|
||||
errors.CheckError(err)
|
||||
|
||||
if !async {
|
||||
app, err := waitOnApplicationStatus(ctx, acdClient, appQualifiedName, timeout, watchOpts{operation: true}, selectedResources)
|
||||
app, opState, err := waitOnApplicationStatus(ctx, acdClient, appQualifiedName, timeout, watchOpts{operation: true}, selectedResources)
|
||||
errors.CheckError(err)
|
||||
|
||||
if !dryRun {
|
||||
if !app.Status.OperationState.Phase.Successful() {
|
||||
log.Fatalf("Operation has completed with phase: %s", app.Status.OperationState.Phase)
|
||||
if !opState.Phase.Successful() {
|
||||
log.Fatalf("Operation has completed with phase: %s", opState.Phase)
|
||||
} else if len(selectedResources) == 0 && app.Status.Sync.Status != argoappv1.SyncStatusCodeSynced {
|
||||
// Only get resources to be pruned if sync was application-wide and final status is not synced
|
||||
pruningRequired := app.Status.OperationState.SyncResult.Resources.PruningRequired()
|
||||
pruningRequired := opState.SyncResult.Resources.PruningRequired()
|
||||
if pruningRequired > 0 {
|
||||
log.Fatalf("%d resources require pruning", pruningRequired)
|
||||
}
|
||||
@@ -1920,7 +1920,10 @@ func checkResourceStatus(watch watchOpts, healthStatus string, syncStatus string
|
||||
|
||||
const waitFormatString = "%s\t%5s\t%10s\t%10s\t%20s\t%8s\t%7s\t%10s\t%s\n"
|
||||
|
||||
func waitOnApplicationStatus(ctx context.Context, acdClient argocdclient.Client, appName string, timeout uint, watch watchOpts, selectedResources []*argoappv1.SyncOperationResource) (*argoappv1.Application, error) {
|
||||
// waitOnApplicationStatus watches an application and blocks until either the desired watch conditions
|
||||
// are fulfiled or we reach the timeout. Returns the app once desired conditions have been filled.
|
||||
// Additionally return the operationState at time of fulfilment (which may be different than returned app).
|
||||
func waitOnApplicationStatus(ctx context.Context, acdClient argocdclient.Client, appName string, timeout uint, watch watchOpts, selectedResources []*argoappv1.SyncOperationResource) (*argoappv1.Application, *argoappv1.OperationState, error) {
|
||||
ctx, cancel := context.WithCancel(ctx)
|
||||
defer cancel()
|
||||
|
||||
@@ -1978,10 +1981,20 @@ func waitOnApplicationStatus(ctx context.Context, acdClient argocdclient.Client,
|
||||
AppNamespace: &appNs,
|
||||
})
|
||||
errors.CheckError(err)
|
||||
|
||||
// printFinalStatus() will refresh and update the app object, potentially causing the app's
|
||||
// status.operationState to be different than the version when we break out of the event loop.
|
||||
// This means the app.status is unreliable for determining the final state of the operation.
|
||||
// finalOperationState captures the operationState as it was seen when we met the conditions of
|
||||
// the wait, so the caller can rely on it to determine the outcome of the operation.
|
||||
// See: https://github.com/argoproj/argo-cd/issues/5592
|
||||
finalOperationState := app.Status.OperationState
|
||||
|
||||
appEventCh := acdClient.WatchApplicationWithRetry(ctx, appName, app.ResourceVersion)
|
||||
for appEvent := range appEventCh {
|
||||
app = &appEvent.Application
|
||||
|
||||
finalOperationState = app.Status.OperationState
|
||||
operationInProgress := false
|
||||
// consider the operation is in progress
|
||||
if app.Operation != nil {
|
||||
@@ -2019,7 +2032,7 @@ func waitOnApplicationStatus(ctx context.Context, acdClient argocdclient.Client,
|
||||
|
||||
if selectedResourcesAreReady && (!operationInProgress || !watch.operation) {
|
||||
app = printFinalStatus(app)
|
||||
return app, nil
|
||||
return app, finalOperationState, nil
|
||||
}
|
||||
|
||||
newStates := groupResourceStates(app, selectedResources)
|
||||
@@ -2029,7 +2042,7 @@ func waitOnApplicationStatus(ctx context.Context, acdClient argocdclient.Client,
|
||||
if prevState, found := prevStates[stateKey]; found {
|
||||
if watch.health && prevState.Health != string(health.HealthStatusUnknown) && prevState.Health != string(health.HealthStatusDegraded) && newState.Health == string(health.HealthStatusDegraded) {
|
||||
_ = printFinalStatus(app)
|
||||
return nil, fmt.Errorf("application '%s' health state has transitioned from %s to %s", appName, prevState.Health, newState.Health)
|
||||
return nil, finalOperationState, fmt.Errorf("application '%s' health state has transitioned from %s to %s", appName, prevState.Health, newState.Health)
|
||||
}
|
||||
doPrint = prevState.Merge(newState)
|
||||
} else {
|
||||
@@ -2043,7 +2056,7 @@ func waitOnApplicationStatus(ctx context.Context, acdClient argocdclient.Client,
|
||||
_ = w.Flush()
|
||||
}
|
||||
_ = printFinalStatus(app)
|
||||
return nil, fmt.Errorf("timed out (%ds) waiting for app %q match desired state", timeout, appName)
|
||||
return nil, finalOperationState, fmt.Errorf("timed out (%ds) waiting for app %q match desired state", timeout, appName)
|
||||
}
|
||||
|
||||
// setParameterOverrides updates an existing or appends a new parameter override in the application
|
||||
@@ -2199,7 +2212,7 @@ func NewApplicationRollbackCommand(clientOpts *argocdclient.ClientOptions) *cobr
|
||||
})
|
||||
errors.CheckError(err)
|
||||
|
||||
_, err = waitOnApplicationStatus(ctx, acdClient, app.QualifiedName(), timeout, watchOpts{
|
||||
_, _, err = waitOnApplicationStatus(ctx, acdClient, app.QualifiedName(), timeout, watchOpts{
|
||||
operation: true,
|
||||
}, nil)
|
||||
errors.CheckError(err)
|
||||
|
||||
@@ -38,11 +38,12 @@ import (
|
||||
)
|
||||
|
||||
type forwardCacheClient struct {
|
||||
namespace string
|
||||
context string
|
||||
init sync.Once
|
||||
client cache.CacheClient
|
||||
err error
|
||||
namespace string
|
||||
context string
|
||||
init sync.Once
|
||||
client cache.CacheClient
|
||||
compression cache.RedisCompressionType
|
||||
err error
|
||||
}
|
||||
|
||||
func (c *forwardCacheClient) doLazy(action func(client cache.CacheClient) error) error {
|
||||
@@ -58,7 +59,7 @@ func (c *forwardCacheClient) doLazy(action func(client cache.CacheClient) error)
|
||||
}
|
||||
|
||||
redisClient := redis.NewClient(&redis.Options{Addr: fmt.Sprintf("localhost:%d", redisPort)})
|
||||
c.client = cache.NewRedisCache(redisClient, time.Hour, cache.RedisCompressionNone)
|
||||
c.client = cache.NewRedisCache(redisClient, time.Hour, c.compression)
|
||||
})
|
||||
if c.err != nil {
|
||||
return c.err
|
||||
@@ -139,7 +140,7 @@ func testAPI(ctx context.Context, clientOpts *apiclient.ClientOptions) error {
|
||||
|
||||
// StartLocalServer allows executing command in a headless mode: on the fly starts Argo CD API server and
|
||||
// changes provided client options to use started API server port
|
||||
func StartLocalServer(ctx context.Context, clientOpts *apiclient.ClientOptions, ctxStr string, port *int, address *string) error {
|
||||
func StartLocalServer(ctx context.Context, clientOpts *apiclient.ClientOptions, ctxStr string, port *int, address *string, compression cache.RedisCompressionType) error {
|
||||
flags := pflag.NewFlagSet("tmp", pflag.ContinueOnError)
|
||||
clientConfig := cli.AddKubectlFlagsToSet(flags)
|
||||
startInProcessAPI := clientOpts.Core
|
||||
@@ -200,7 +201,7 @@ func StartLocalServer(ctx context.Context, clientOpts *apiclient.ClientOptions,
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
appstateCache := appstatecache.NewCache(cache.NewCache(&forwardCacheClient{namespace: namespace, context: ctxStr}), time.Hour)
|
||||
appstateCache := appstatecache.NewCache(cache.NewCache(&forwardCacheClient{namespace: namespace, context: ctxStr, compression: compression}), time.Hour)
|
||||
srv := server.NewServer(ctx, server.ArgoCDServerOpts{
|
||||
EnableGZip: false,
|
||||
Namespace: namespace,
|
||||
@@ -243,7 +244,7 @@ func NewClientOrDie(opts *apiclient.ClientOptions, c *cobra.Command) apiclient.C
|
||||
ctx := c.Context()
|
||||
|
||||
ctxStr := initialize.RetrieveContextIfChanged(c.Flag("context"))
|
||||
err := StartLocalServer(ctx, opts, ctxStr, nil, nil)
|
||||
err := StartLocalServer(ctx, opts, ctxStr, nil, nil, cache.RedisCompressionNone)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
@@ -3,6 +3,7 @@ package commands
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"strconv"
|
||||
"text/tabwriter"
|
||||
|
||||
log "github.com/sirupsen/logrus"
|
||||
@@ -250,15 +251,12 @@ func printRepoTable(repos appsv1.Repositories) {
|
||||
_, _ = fmt.Fprintf(w, "TYPE\tNAME\tREPO\tINSECURE\tOCI\tLFS\tCREDS\tSTATUS\tMESSAGE\tPROJECT\n")
|
||||
for _, r := range repos {
|
||||
var hasCreds string
|
||||
if !r.HasCredentials() {
|
||||
hasCreds = "false"
|
||||
if r.InheritedCreds {
|
||||
hasCreds = "inherited"
|
||||
} else {
|
||||
if r.InheritedCreds {
|
||||
hasCreds = "inherited"
|
||||
} else {
|
||||
hasCreds = "true"
|
||||
}
|
||||
hasCreds = strconv.FormatBool(r.HasCredentials())
|
||||
}
|
||||
|
||||
_, _ = fmt.Fprintf(w, "%s\t%s\t%s\t%v\t%v\t%v\t%s\t%s\t%s\t%s\n", r.Type, r.Name, r.Repo, r.IsInsecure(), r.EnableOCI, r.EnableLFS, hasCreds, r.ConnectionState.Status, r.ConnectionState.Message, r.Project)
|
||||
}
|
||||
_ = w.Flush()
|
||||
|
||||
@@ -138,7 +138,10 @@ func readProjFromURI(fileURL string, proj *v1alpha1.AppProject) error {
|
||||
} else {
|
||||
err = config.UnmarshalRemoteFile(fileURL, &proj)
|
||||
}
|
||||
return fmt.Errorf("error reading proj from uri: %w", err)
|
||||
if err != nil {
|
||||
return fmt.Errorf("error reading proj from uri: %w", err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func SetProjSpecOptions(flags *pflag.FlagSet, spec *v1alpha1.AppProjectSpec, projOpts *ProjectOpts) int {
|
||||
|
||||
@@ -378,7 +378,7 @@ func (s *Service) GetParametersAnnouncement(stream apiclient.ConfigManagementPlu
|
||||
return fmt.Errorf("illegal appPath: out of workDir bound")
|
||||
}
|
||||
|
||||
repoResponse, err := getParametersAnnouncement(bufferedCtx, appPath, s.initConstants.PluginConfig.Spec.Parameters.Static, s.initConstants.PluginConfig.Spec.Parameters.Dynamic)
|
||||
repoResponse, err := getParametersAnnouncement(bufferedCtx, appPath, s.initConstants.PluginConfig.Spec.Parameters.Static, s.initConstants.PluginConfig.Spec.Parameters.Dynamic, metadata.GetEnv())
|
||||
if err != nil {
|
||||
return fmt.Errorf("get parameters announcement error: %w", err)
|
||||
}
|
||||
@@ -390,11 +390,12 @@ func (s *Service) GetParametersAnnouncement(stream apiclient.ConfigManagementPlu
|
||||
return nil
|
||||
}
|
||||
|
||||
func getParametersAnnouncement(ctx context.Context, appDir string, announcements []*repoclient.ParameterAnnouncement, command Command) (*apiclient.ParametersAnnouncementResponse, error) {
|
||||
func getParametersAnnouncement(ctx context.Context, appDir string, announcements []*repoclient.ParameterAnnouncement, command Command, envEntries []*apiclient.EnvEntry) (*apiclient.ParametersAnnouncementResponse, error) {
|
||||
augmentedAnnouncements := announcements
|
||||
|
||||
if len(command.Command) > 0 {
|
||||
stdout, err := runCommand(ctx, command, appDir, os.Environ())
|
||||
env := append(os.Environ(), environ(envEntries)...)
|
||||
stdout, err := runCommand(ctx, command, appDir, env)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("error executing dynamic parameter output command: %w", err)
|
||||
}
|
||||
|
||||
@@ -381,7 +381,7 @@ func Test_getParametersAnnouncement_empty_command(t *testing.T) {
|
||||
Command: []string{"echo"},
|
||||
Args: []string{`[]`},
|
||||
}
|
||||
res, err := getParametersAnnouncement(context.Background(), "", *static, command)
|
||||
res, err := getParametersAnnouncement(context.Background(), "", *static, command, []*apiclient.EnvEntry{})
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, []*repoclient.ParameterAnnouncement{{Name: "static-a"}, {Name: "static-b"}}, res.ParameterAnnouncements)
|
||||
}
|
||||
@@ -395,7 +395,7 @@ func Test_getParametersAnnouncement_no_command(t *testing.T) {
|
||||
err := yaml.Unmarshal([]byte(staticYAML), static)
|
||||
require.NoError(t, err)
|
||||
command := Command{}
|
||||
res, err := getParametersAnnouncement(context.Background(), "", *static, command)
|
||||
res, err := getParametersAnnouncement(context.Background(), "", *static, command, []*apiclient.EnvEntry{})
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, []*repoclient.ParameterAnnouncement{{Name: "static-a"}, {Name: "static-b"}}, res.ParameterAnnouncements)
|
||||
}
|
||||
@@ -412,7 +412,7 @@ func Test_getParametersAnnouncement_static_and_dynamic(t *testing.T) {
|
||||
Command: []string{"echo"},
|
||||
Args: []string{`[{"name": "dynamic-a"}, {"name": "dynamic-b"}]`},
|
||||
}
|
||||
res, err := getParametersAnnouncement(context.Background(), "", *static, command)
|
||||
res, err := getParametersAnnouncement(context.Background(), "", *static, command, []*apiclient.EnvEntry{})
|
||||
require.NoError(t, err)
|
||||
expected := []*repoclient.ParameterAnnouncement{
|
||||
{Name: "dynamic-a"},
|
||||
@@ -428,7 +428,7 @@ func Test_getParametersAnnouncement_invalid_json(t *testing.T) {
|
||||
Command: []string{"echo"},
|
||||
Args: []string{`[`},
|
||||
}
|
||||
_, err := getParametersAnnouncement(context.Background(), "", []*repoclient.ParameterAnnouncement{}, command)
|
||||
_, err := getParametersAnnouncement(context.Background(), "", []*repoclient.ParameterAnnouncement{}, command, []*apiclient.EnvEntry{})
|
||||
assert.Error(t, err)
|
||||
assert.Contains(t, err.Error(), "unexpected end of JSON input")
|
||||
}
|
||||
@@ -438,7 +438,7 @@ func Test_getParametersAnnouncement_bad_command(t *testing.T) {
|
||||
Command: []string{"exit"},
|
||||
Args: []string{"1"},
|
||||
}
|
||||
_, err := getParametersAnnouncement(context.Background(), "", []*repoclient.ParameterAnnouncement{}, command)
|
||||
_, err := getParametersAnnouncement(context.Background(), "", []*repoclient.ParameterAnnouncement{}, command, []*apiclient.EnvEntry{})
|
||||
assert.Error(t, err)
|
||||
assert.Contains(t, err.Error(), "error executing dynamic parameter output command")
|
||||
}
|
||||
@@ -730,16 +730,17 @@ func TestService_GetParametersAnnouncement(t *testing.T) {
|
||||
require.NoError(t, err)
|
||||
|
||||
t.Run("successful response", func(t *testing.T) {
|
||||
s, err := NewMockParametersAnnouncementStream("./testdata/kustomize", "./testdata/kustomize", nil)
|
||||
s, err := NewMockParametersAnnouncementStream("./testdata/kustomize", "./testdata/kustomize", []string{"MUST_BE_SET=yep"})
|
||||
require.NoError(t, err)
|
||||
err = service.GetParametersAnnouncement(s)
|
||||
require.NoError(t, err)
|
||||
require.NotNil(t, s.response)
|
||||
require.Len(t, s.response.ParameterAnnouncements, 1)
|
||||
assert.Equal(t, repoclient.ParameterAnnouncement{Name: "test-param", String_: "test-value"}, *s.response.ParameterAnnouncements[0])
|
||||
require.Len(t, s.response.ParameterAnnouncements, 2)
|
||||
assert.Equal(t, repoclient.ParameterAnnouncement{Name: "dynamic-test-param", String_: "yep"}, *s.response.ParameterAnnouncements[0])
|
||||
assert.Equal(t, repoclient.ParameterAnnouncement{Name: "test-param", String_: "test-value"}, *s.response.ParameterAnnouncements[1])
|
||||
})
|
||||
t.Run("out of bounds app", func(t *testing.T) {
|
||||
s, err := NewMockParametersAnnouncementStream("./testdata/kustomize", "./testdata/kustomize", nil)
|
||||
s, err := NewMockParametersAnnouncementStream("./testdata/kustomize", "./testdata/kustomize", []string{"MUST_BE_SET=yep"})
|
||||
require.NoError(t, err)
|
||||
// set a malicious app path on the metadata
|
||||
s.metadataRequest.Request.(*apiclient.AppStreamRequest_Metadata).Metadata.AppRelPath = "../out-of-bounds"
|
||||
@@ -747,6 +748,13 @@ func TestService_GetParametersAnnouncement(t *testing.T) {
|
||||
require.ErrorContains(t, err, "illegal appPath")
|
||||
require.Nil(t, s.response)
|
||||
})
|
||||
t.Run("fails when script fails", func(t *testing.T) {
|
||||
s, err := NewMockParametersAnnouncementStream("./testdata/kustomize", "./testdata/kustomize", []string{"WRONG_ENV_VAR=oops"})
|
||||
require.NoError(t, err)
|
||||
err = service.GetParametersAnnouncement(s)
|
||||
require.ErrorContains(t, err, "error executing dynamic parameter output command")
|
||||
require.Nil(t, s.response)
|
||||
})
|
||||
}
|
||||
|
||||
func Test_getCommandArgsToLog(t *testing.T) {
|
||||
|
||||
@@ -5,9 +5,15 @@ metadata:
|
||||
spec:
|
||||
version: v1.0
|
||||
init:
|
||||
command: [kustomize, version]
|
||||
command: [sh, -c]
|
||||
args:
|
||||
- |
|
||||
kustomize version
|
||||
generate:
|
||||
command: [sh, -c, "kustomize build"]
|
||||
command: [sh, -c]
|
||||
args:
|
||||
- |
|
||||
kustomize build
|
||||
discover:
|
||||
find:
|
||||
command: [sh, -c, find . -name kustomization.yaml]
|
||||
@@ -16,3 +22,12 @@ spec:
|
||||
static:
|
||||
- name: test-param
|
||||
string: test-value
|
||||
dynamic:
|
||||
command: [sh, -c]
|
||||
args:
|
||||
- |
|
||||
# Make sure env vars are making it to the plugin.
|
||||
if [ -z "$MUST_BE_SET" ]; then
|
||||
exit 1
|
||||
fi
|
||||
echo "[{\"name\": \"dynamic-test-param\", \"string\": \"$MUST_BE_SET\"}]"
|
||||
|
||||
7
controller/cache/cache.go
vendored
7
controller/cache/cache.go
vendored
@@ -488,10 +488,11 @@ func (c *liveStateCache) getSyncedCluster(server string) (clustercache.ClusterCa
|
||||
func (c *liveStateCache) invalidate(cacheSettings cacheSettings) {
|
||||
log.Info("invalidating live state cache")
|
||||
c.lock.Lock()
|
||||
defer c.lock.Unlock()
|
||||
|
||||
c.cacheSettings = cacheSettings
|
||||
for _, clust := range c.clusters {
|
||||
clusters := c.clusters
|
||||
c.lock.Unlock()
|
||||
|
||||
for _, clust := range clusters {
|
||||
clust.Invalidate(clustercache.SetSettings(cacheSettings.clusterSettings))
|
||||
}
|
||||
log.Info("live state cache invalidated")
|
||||
|
||||
BIN
docs/assets/extra_info-1.png
Normal file
BIN
docs/assets/extra_info-1.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 32 KiB |
BIN
docs/assets/extra_info-2.png
Normal file
BIN
docs/assets/extra_info-2.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 8.3 KiB |
BIN
docs/assets/extra_info.png
Normal file
BIN
docs/assets/extra_info.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 132 KiB |
@@ -9,16 +9,6 @@ setTimeout(function() {
|
||||
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() {
|
||||
const classes = container.className.split(' ');
|
||||
const index = classes.indexOf('shift-up');
|
||||
if (index === -1) {
|
||||
classes.push('shift-up');
|
||||
} else {
|
||||
classes.splice(index, 1);
|
||||
}
|
||||
container.className = classes.join(' ');
|
||||
});
|
||||
}
|
||||
|
||||
var CSSLink = document.createElement('link');
|
||||
|
||||
@@ -9,12 +9,7 @@ To test:
|
||||
```bash
|
||||
make serve-docs
|
||||
```
|
||||
|
||||
Check for broken external links:
|
||||
|
||||
```bash
|
||||
make lint-docs
|
||||
```
|
||||
Once running, you can view your locally built documentation at [http://0.0.0.0:8000/](http://0.0.0.0:8000/).
|
||||
|
||||
## Deploying
|
||||
|
||||
|
||||
@@ -146,7 +146,12 @@ spec:
|
||||
# name: in-cluster
|
||||
# The namespace will only be set for namespace-scoped resources that have not set a value for .metadata.namespace
|
||||
namespace: guestbook
|
||||
|
||||
|
||||
# Extra information to show in the Argo CD Application details tab
|
||||
info:
|
||||
- name: 'Example:'
|
||||
value: 'https://example.com'
|
||||
|
||||
# Sync policy
|
||||
syncPolicy:
|
||||
automated: # automated sync by default retries failed attempts 5 times with following delays between attempts ( 5s, 10s, 20s, 40s, 80s ); retry controlled using `retry` field.
|
||||
|
||||
@@ -15,13 +15,13 @@ export VERSION=v1.0.1
|
||||
Export to a backup:
|
||||
|
||||
```bash
|
||||
docker run -v ~/.kube:/home/argocd/.kube --rm argoproj/argocd:$VERSION argocd admin export > backup.yaml
|
||||
docker run -v ~/.kube:/home/argocd/.kube --rm quay.io/argoproj/argocd:$VERSION argocd admin export > backup.yaml
|
||||
```
|
||||
|
||||
Import from a backup:
|
||||
|
||||
```bash
|
||||
docker run -i -v ~/.kube:/home/argocd/.kube --rm argoproj/argocd:$VERSION argocd admin import - < backup.yaml
|
||||
docker run -i -v ~/.kube:/home/argocd/.kube --rm quay.io/argoproj/argocd:$VERSION argocd admin import - < backup.yaml
|
||||
```
|
||||
|
||||
!!! note
|
||||
|
||||
@@ -538,15 +538,15 @@ spec:
|
||||
- secretName: secret-yourdomain-com
|
||||
rules:
|
||||
- host: argocd.yourdomain.com
|
||||
http:
|
||||
paths:
|
||||
- pathType: ImplementationSpecific
|
||||
path: "/*" # "*" is needed. Without this, the UI Javascript and CSS will not load properly
|
||||
backend:
|
||||
service:
|
||||
name: argocd-server
|
||||
port:
|
||||
number: 80
|
||||
http:
|
||||
paths:
|
||||
- pathType: ImplementationSpecific
|
||||
path: "/*" # "*" is needed. Without this, the UI Javascript and CSS will not load properly
|
||||
backend:
|
||||
service:
|
||||
name: argocd-server
|
||||
port:
|
||||
number: 80
|
||||
```
|
||||
|
||||
If you use the version `1.21.3-gke.1600` or later, you should use the following Ingress resource:
|
||||
@@ -563,15 +563,15 @@ spec:
|
||||
- secretName: secret-yourdomain-com
|
||||
rules:
|
||||
- host: argocd.yourdomain.com
|
||||
http:
|
||||
paths:
|
||||
- pathType: Prefix
|
||||
path: "/"
|
||||
backend:
|
||||
service:
|
||||
name: argocd-server
|
||||
port:
|
||||
number: 80
|
||||
http:
|
||||
paths:
|
||||
- pathType: Prefix
|
||||
path: "/"
|
||||
backend:
|
||||
service:
|
||||
name: argocd-server
|
||||
port:
|
||||
number: 80
|
||||
```
|
||||
|
||||
As you may know already, it can take some minutes to deploy the load balancer and become ready to accept connections. Once it's ready, get the public IP address for your Load Balancer, go to your DNS server (Google or third party) and point your domain or subdomain (i.e. argocd.yourdomain.com) to that IP address.
|
||||
|
||||
@@ -74,7 +74,7 @@ kind: Kustomization
|
||||
|
||||
namespace: argocd
|
||||
resources:
|
||||
- github.com/argoproj/argo-cd/manifests/ha?ref=v2.6.2
|
||||
- github.com/argoproj/argo-cd/manifests/ha/base?ref=v2.6.2
|
||||
```
|
||||
|
||||
## Helm
|
||||
|
||||
@@ -64,7 +64,7 @@ See [Web-based Terminal](web_based_terminal.md) for more info.
|
||||
|
||||
#### The `applicationsets` resource
|
||||
|
||||
[ApplicationSets](applicationset) provide a declarative way to automatically create/update/delete Applications.
|
||||
[ApplicationSets](applicationset/index.md) provide a declarative way to automatically create/update/delete Applications.
|
||||
|
||||
Granting `applicationsets, create` effectively grants the ability to create Applications. While it doesn't allow the
|
||||
user to create Applications directly, they can create Applications via an ApplicationSet.
|
||||
|
||||
@@ -19,7 +19,7 @@ metadata:
|
||||
spec:
|
||||
accessTokenSkewMillis: 120000
|
||||
accessTokenTimeToLive: 1200000
|
||||
authChainName: LoginService
|
||||
authChainName: login-service
|
||||
clientId: argocd
|
||||
codeLastMileKeyName: lastmile-oidc
|
||||
codeTokenSkewMilis: 60000
|
||||
|
||||
@@ -22,7 +22,7 @@ or
|
||||
argocd app delete APPNAME
|
||||
```
|
||||
|
||||
# Deletion Using `kubectl`
|
||||
## Deletion Using `kubectl`
|
||||
|
||||
To perform a non-cascade delete, make sure the finalizer is unset and then delete the app:
|
||||
|
||||
@@ -38,7 +38,7 @@ kubectl patch app APPNAME -p '{"metadata": {"finalizers": ["resources-finalizer
|
||||
kubectl delete app APPNAME
|
||||
```
|
||||
|
||||
# About The Deletion Finalizer
|
||||
## About The Deletion Finalizer
|
||||
|
||||
```yaml
|
||||
metadata:
|
||||
|
||||
@@ -25,6 +25,7 @@ argocd admin dashboard [flags]
|
||||
--password string Password for basic authentication to the API server
|
||||
--port int Listen on given port (default 8080)
|
||||
--proxy-url string If provided, this URL will be used to connect via proxy
|
||||
--redis-compress string Enable this if the application controller is configured with redis compression enabled. (possible values: none, gzip) (default "none")
|
||||
--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")
|
||||
--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
|
||||
|
||||
28
docs/user-guide/extra_info.md
Normal file
28
docs/user-guide/extra_info.md
Normal file
@@ -0,0 +1,28 @@
|
||||
# Add extra Application info
|
||||
|
||||
You can add additional information to an Application on your ArgoCD dashboard.
|
||||
If you wish to add clickable links, see [Add external URL](https://argo-cd.readthedocs.io/en/stable/user-guide/external-url/).
|
||||
|
||||
This is done by providing the 'info' field a key-value in your Application manifest.
|
||||
|
||||
Example:
|
||||
```yaml
|
||||
project: argo-demo
|
||||
source:
|
||||
repoURL: 'https://demo'
|
||||
path: argo-demo
|
||||
destination:
|
||||
server: https://demo
|
||||
namespace: argo-demo
|
||||
info:
|
||||
- name: Example:
|
||||
value: >-
|
||||
https://example.com
|
||||
```
|
||||

|
||||
|
||||
The additional information will be visible on the ArgoCD Application details page.
|
||||
|
||||

|
||||
|
||||

|
||||
@@ -48,6 +48,29 @@ source:
|
||||
- values-production.yaml
|
||||
```
|
||||
|
||||
## Values
|
||||
|
||||
Argo CD supports the equivalent of a values file directly in the Application manifest using the `source.helm.values` key.
|
||||
|
||||
```
|
||||
source:
|
||||
helm:
|
||||
values: |
|
||||
ingress:
|
||||
enabled: true
|
||||
path: /
|
||||
hosts:
|
||||
- mydomain.example.com
|
||||
annotations:
|
||||
kubernetes.io/ingress.class: nginx
|
||||
kubernetes.io/tls-acme: "true"
|
||||
labels: {}
|
||||
tls:
|
||||
- secretName: mydomain-tls
|
||||
hosts:
|
||||
- mydomain.example.com
|
||||
```
|
||||
|
||||
## Helm Parameters
|
||||
|
||||
Helm has the ability to set parameter values, which override any values in
|
||||
@@ -115,7 +138,7 @@ Argo CD supports many (most?) Helm hooks by mapping the Helm annotations onto Ar
|
||||
| `helm.sh/hook: test-success` | Not supported. No equivalent in Argo CD. |
|
||||
| `helm.sh/hook: test-failure` | Not supported. No equivalent in Argo CD. |
|
||||
| `helm.sh/hook-delete-policy` | Supported. See also `argocd.argoproj.io/hook-delete-policy`). |
|
||||
| `helm.sh/hook-delete-timeout` | No supported. Never used in Helm stable |
|
||||
| `helm.sh/hook-delete-timeout` | Not supported. Never used in Helm stable |
|
||||
| `helm.sh/hook-weight` | Supported as equivalent to `argocd.argoproj.io/sync-wave`. |
|
||||
|
||||
Unsupported hooks are ignored. In Argo CD, hooks are created by using `kubectl apply`, rather than `kubectl create`. This means that if the hook is named and already exists, it will not change unless you have annotated it with `before-hook-creation`.
|
||||
|
||||
@@ -321,7 +321,7 @@ stringData:
|
||||
|
||||
All the examples above talk about Git repositories, but the same principles apply to clusters as well.
|
||||
|
||||
With cluster-scoped clusters we can also restrict projects to only allow applications whose destinations belong to the
|
||||
With project-scoped clusters we can also restrict projects to only allow applications whose destinations belong to the
|
||||
same project. The default behavior allows for applications to be installed onto clusters which are not a part of the same
|
||||
project, as the example below demonstrates:
|
||||
|
||||
|
||||
16
go.mod
16
go.mod
@@ -8,8 +8,9 @@ require (
|
||||
github.com/Masterminds/semver/v3 v3.2.0
|
||||
github.com/TomOnTime/utfutil v0.0.0-20180511104225-09c41003ee1d
|
||||
github.com/alicebob/miniredis/v2 v2.23.1
|
||||
github.com/argoproj/gitops-engine v0.7.1-0.20221208230615-917f5a0f16d5
|
||||
github.com/argoproj/notifications-engine v0.3.1-0.20221203221941-490d98afd1d6
|
||||
github.com/antonmedv/expr v1.9.0
|
||||
github.com/argoproj/gitops-engine v0.7.1-0.20230512020822-b4dd8b8c3976
|
||||
github.com/argoproj/notifications-engine v0.4.1-0.20230228182525-f754726f03da
|
||||
github.com/argoproj/pkg v0.13.7-0.20221221191914-44694015343d
|
||||
github.com/aws/aws-sdk-go v1.44.164
|
||||
github.com/bombsimon/logrusr/v2 v2.0.1
|
||||
@@ -73,7 +74,7 @@ require (
|
||||
github.com/whilp/git-urls v0.0.0-20191001220047-6db9661140c0
|
||||
github.com/xanzy/go-gitlab v0.60.0
|
||||
github.com/yuin/gopher-lua v0.0.0-20220504180219-658193537a64
|
||||
golang.org/x/crypto v0.0.0-20220722155217-630584e8d5aa
|
||||
golang.org/x/crypto v0.3.0
|
||||
golang.org/x/net v0.7.0 // indirect
|
||||
golang.org/x/oauth2 v0.0.0-20220608161450-d0670ef3b1eb
|
||||
golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4
|
||||
@@ -106,8 +107,7 @@ require (
|
||||
)
|
||||
|
||||
require (
|
||||
github.com/Masterminds/sprig/v3 v3.2.2
|
||||
github.com/antonmedv/expr v1.9.0
|
||||
github.com/Masterminds/sprig/v3 v3.2.3
|
||||
github.com/gosimple/slug v1.13.1
|
||||
github.com/microsoft/azure-devops-go-api/azuredevops v1.0.0-b5
|
||||
github.com/robfig/cron/v3 v3.0.1
|
||||
@@ -136,8 +136,6 @@ require (
|
||||
github.com/Azure/go-autorest/tracing v0.6.0 // indirect
|
||||
github.com/MakeNowJust/heredoc v0.0.0-20170808103936-bb23615498cd // indirect
|
||||
github.com/Masterminds/goutils v1.1.1 // indirect
|
||||
github.com/Masterminds/semver v1.5.0 // indirect
|
||||
github.com/Masterminds/sprig v2.22.0+incompatible // indirect
|
||||
github.com/Microsoft/go-winio v0.4.17 // indirect
|
||||
github.com/PagerDuty/go-pagerduty v1.6.0 // indirect
|
||||
github.com/ProtonMail/go-crypto v0.0.0-20210428141323-04723f9f07d7 // indirect
|
||||
@@ -181,7 +179,7 @@ require (
|
||||
github.com/grpc-ecosystem/grpc-gateway/v2 v2.7.0 // indirect
|
||||
github.com/hashicorp/go-cleanhttp v0.5.2 // indirect
|
||||
github.com/hashicorp/go-version v1.2.1 // indirect
|
||||
github.com/huandu/xstrings v1.3.1 // indirect
|
||||
github.com/huandu/xstrings v1.3.3 // indirect
|
||||
github.com/inconshreveable/mousetrap v1.0.1 // indirect
|
||||
github.com/itchyny/timefmt-go v0.1.4 // indirect
|
||||
github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99 // indirect
|
||||
@@ -217,7 +215,7 @@ require (
|
||||
github.com/russross/blackfriday v1.5.2 // indirect
|
||||
github.com/russross/blackfriday/v2 v2.1.0 // indirect
|
||||
github.com/sergi/go-diff v1.1.0 // indirect
|
||||
github.com/slack-go/slack v0.10.1 // indirect
|
||||
github.com/slack-go/slack v0.12.1 // indirect
|
||||
github.com/valyala/bytebufferpool v1.0.0 // indirect
|
||||
github.com/vmihailenco/go-tinylfu v0.2.1 // indirect
|
||||
github.com/vmihailenco/msgpack/v5 v5.3.4 // indirect
|
||||
|
||||
32
go.sum
32
go.sum
@@ -84,15 +84,10 @@ github.com/MakeNowJust/heredoc v0.0.0-20170808103936-bb23615498cd h1:sjQovDkwrZp
|
||||
github.com/MakeNowJust/heredoc v0.0.0-20170808103936-bb23615498cd/go.mod h1:64YHyfSL2R96J44Nlwm39UHepQbyR5q10x7iYa1ks2E=
|
||||
github.com/Masterminds/goutils v1.1.1 h1:5nUrii3FMTL5diU80unEVvNevw1nH4+ZV4DSLVJLSYI=
|
||||
github.com/Masterminds/goutils v1.1.1/go.mod h1:8cTjp+g8YejhMuvIA5y2vz3BpJxksy863GQaJW2MFNU=
|
||||
github.com/Masterminds/semver v1.5.0 h1:H65muMkzWKEuNDnfl9d70GUjFniHKHRbFPGBuZ3QEww=
|
||||
github.com/Masterminds/semver v1.5.0/go.mod h1:MB6lktGJrhw8PrUyiEoblNEGEQ+RzHPF078ddwwvV3Y=
|
||||
github.com/Masterminds/semver/v3 v3.1.1/go.mod h1:VPu/7SZ7ePZ3QOrcuXROw5FAcLl4a0cBrbBpGY/8hQs=
|
||||
github.com/Masterminds/semver/v3 v3.2.0 h1:3MEsd0SM6jqZojhjLWWeBY+Kcjy9i6MQAeY7YgDP83g=
|
||||
github.com/Masterminds/semver/v3 v3.2.0/go.mod h1:qvl/7zhW3nngYb5+80sSMF+FG2BjYrf8m9wsX0PNOMQ=
|
||||
github.com/Masterminds/sprig v2.22.0+incompatible h1:z4yfnGrZ7netVz+0EDJ0Wi+5VZCSYp4Z0m2dk6cEM60=
|
||||
github.com/Masterminds/sprig v2.22.0+incompatible/go.mod h1:y6hNFY5UBTIWBxnzTeuNhlNS5hqE0NB0E6fgfo2Br3o=
|
||||
github.com/Masterminds/sprig/v3 v3.2.2 h1:17jRggJu518dr3QaafizSXOjKYp94wKfABxUmyxvxX8=
|
||||
github.com/Masterminds/sprig/v3 v3.2.2/go.mod h1:UoaO7Yp8KlPnJIYWTFkMaqPUYKTfGFPhxNuwnnxkKlk=
|
||||
github.com/Masterminds/sprig/v3 v3.2.3 h1:eL2fZNezLomi0uOLqjQoN6BfsDD+fyLtgbJMAj9n6YA=
|
||||
github.com/Masterminds/sprig/v3 v3.2.3/go.mod h1:rXcFaZ2zZbLRJv/xSysmlgIM1u11eBaRMhvYXJNkGuM=
|
||||
github.com/Microsoft/go-winio v0.4.14/go.mod h1:qXqCSQ3Xa7+6tgxaGTIe4Kpcdsi+P8jBhyzoq1bpyYA=
|
||||
github.com/Microsoft/go-winio v0.4.15/go.mod h1:tTuCMEN+UleMWgg9dVx4Hu52b1bJo+59jBh3ajtinzw=
|
||||
github.com/Microsoft/go-winio v0.4.16/go.mod h1:XB6nPKklQyQ7GC9LdcBEcBl8PF76WugXOPRXwdLnMv0=
|
||||
@@ -137,10 +132,10 @@ github.com/antonmedv/expr v1.9.0/go.mod h1:5qsM3oLGDND7sDmQGDXHkYfkjYMUX14qsgqmH
|
||||
github.com/apache/thrift v0.12.0/go.mod h1:cp2SuWMxlEZw2r+iP2GNCdIi4C1qmUzdZFSVb+bacwQ=
|
||||
github.com/apache/thrift v0.13.0/go.mod h1:cp2SuWMxlEZw2r+iP2GNCdIi4C1qmUzdZFSVb+bacwQ=
|
||||
github.com/appscode/go v0.0.0-20191119085241-0887d8ec2ecc/go.mod h1:OawnOmAL4ZX3YaPdN+8HTNwBveT1jMsqP74moa9XUbE=
|
||||
github.com/argoproj/gitops-engine v0.7.1-0.20221208230615-917f5a0f16d5 h1:iRpHi7X3q9G55KTaMjxKicgNnS2blFHaEfOOgsmP8lE=
|
||||
github.com/argoproj/gitops-engine v0.7.1-0.20221208230615-917f5a0f16d5/go.mod h1:WpA/B7tgwfz+sdNE3LqrTrb7ArEY1FOPI2pAGI0hfPc=
|
||||
github.com/argoproj/notifications-engine v0.3.1-0.20221203221941-490d98afd1d6 h1:b92Xft7MQv/SP56FW08zt5CMTE1rySH8UPDKOAgSzOM=
|
||||
github.com/argoproj/notifications-engine v0.3.1-0.20221203221941-490d98afd1d6/go.mod h1:pgPU59KCsBOMhyw9amRWPoSuBmUWvx3Xsc5r0mUriLg=
|
||||
github.com/argoproj/gitops-engine v0.7.1-0.20230512020822-b4dd8b8c3976 h1:8i12dOcimhwrJxUznzZR/NW4JpIL5DXZjkI3Bl3yh38=
|
||||
github.com/argoproj/gitops-engine v0.7.1-0.20230512020822-b4dd8b8c3976/go.mod h1:WpA/B7tgwfz+sdNE3LqrTrb7ArEY1FOPI2pAGI0hfPc=
|
||||
github.com/argoproj/notifications-engine v0.4.1-0.20230228182525-f754726f03da h1:Vf9xvHcXn4TP/nLIfWn+TaC521V9fpz/DwRP6uEeVR8=
|
||||
github.com/argoproj/notifications-engine v0.4.1-0.20230228182525-f754726f03da/go.mod h1:05koR0gE/O0i5YDbidg1dpr76XitK4DJveh+dIAq6e8=
|
||||
github.com/argoproj/pkg v0.13.7-0.20221221191914-44694015343d h1:7fXEKF3OQ9i1PrgieA6FLrXOL3UAKyiotomn0RHevds=
|
||||
github.com/argoproj/pkg v0.13.7-0.20221221191914-44694015343d/go.mod h1:RKjj5FJ6KxtktOY49GJSG49qO6Z4lH7RnrVCaS3tf18=
|
||||
github.com/armon/circbuf v0.0.0-20150827004946-bbbad097214e/go.mod h1:3U/XgcO3hCbHZ8TKRvWD2dDTCfh9M9ya+I9JpbB7O8o=
|
||||
@@ -642,8 +637,8 @@ github.com/heketi/heketi v10.3.0+incompatible/go.mod h1:bB9ly3RchcQqsQ9CpyaQwvva
|
||||
github.com/heketi/tests v0.0.0-20151005000721-f3775cbcefd6/go.mod h1:xGMAM8JLi7UkZt1i4FQeQy0R2T8GLUwQhOP5M1gBhy4=
|
||||
github.com/howeyc/gopass v0.0.0-20170109162249-bf9dde6d0d2c/go.mod h1:lADxMC39cJJqL93Duh1xhAs4I2Zs8mKS89XWXFGp9cs=
|
||||
github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU=
|
||||
github.com/huandu/xstrings v1.3.1 h1:4jgBlKK6tLKFvO8u5pmYjG91cqytmDCDvGh7ECVFfFs=
|
||||
github.com/huandu/xstrings v1.3.1/go.mod h1:y5/lhBue+AyNmUVz9RLU9xbLR0o4KIIExikq4ovT0aE=
|
||||
github.com/huandu/xstrings v1.3.3 h1:/Gcsuc1x8JVbJ9/rlye4xZnVAbEkGauT8lbebqcQws4=
|
||||
github.com/huandu/xstrings v1.3.3/go.mod h1:y5/lhBue+AyNmUVz9RLU9xbLR0o4KIIExikq4ovT0aE=
|
||||
github.com/hudl/fargo v1.3.0/go.mod h1:y3CKSmjA+wD2gak7sUSXTAoopbhU08POFhmITJgmKTg=
|
||||
github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc=
|
||||
github.com/ianlancetaylor/demangle v0.0.0-20200824232613-28f6c0f3b639/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc=
|
||||
@@ -1011,8 +1006,8 @@ github.com/sirupsen/logrus v1.9.0 h1:trlNQbNUG3OdDrDil03MCb1H2o9nJ1x4/5LYw7byDE0
|
||||
github.com/sirupsen/logrus v1.9.0/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ=
|
||||
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.10.1 h1:BGbxa0kMsGEvLOEoZmYs8T1wWfoZXwmQFBb6FgYCXUA=
|
||||
github.com/slack-go/slack v0.10.1/go.mod h1:wWL//kk0ho+FcQXcBTmEafUI5dz4qz5f4mMk8oIkioQ=
|
||||
github.com/slack-go/slack v0.12.1 h1:X97b9g2hnITDtNsNe5GkGx6O2/Sz/uC20ejRZN6QxOw=
|
||||
github.com/slack-go/slack v0.12.1/go.mod h1:hlGi5oXA+Gt+yWTPP0plCdRKmjsDxecdHxYQdlMQKOw=
|
||||
github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc=
|
||||
github.com/smartystreets/assertions v1.1.0/go.mod h1:tcbTF8ujkAEcZ8TElKY+i30BzYlVhC/LOxJk7iOWnoo=
|
||||
github.com/smartystreets/goconvey v0.0.0-20190731233626-505e41936337/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA=
|
||||
@@ -1217,7 +1212,6 @@ golang.org/x/crypto v0.0.0-20190701094942-4def268fd1a4/go.mod h1:yigFU9vqHzYiE8U
|
||||
golang.org/x/crypto v0.0.0-20190820162420-60c769a6c586/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-20200414173820-0848c9571904/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
||||
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
||||
golang.org/x/crypto v0.0.0-20201002170205-7f63de1d35b0/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
||||
golang.org/x/crypto v0.0.0-20210322153248-0c34fe9e7dc2/go.mod h1:T9bdIzuCu7OtxOm1hfPfRQxPLYneinmdGuTeoZ9dtd4=
|
||||
@@ -1226,8 +1220,9 @@ golang.org/x/crypto v0.0.0-20210817164053-32db794688a5/go.mod h1:GvvjBRRGRdwPK5y
|
||||
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
|
||||
golang.org/x/crypto v0.0.0-20220214200702-86341886e292/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
|
||||
golang.org/x/crypto v0.0.0-20220622213112-05595931fe9d/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
|
||||
golang.org/x/crypto v0.0.0-20220722155217-630584e8d5aa h1:zuSxTR4o9y82ebqCUJYNGJbGPo6sKVl54f/TVDObg1c=
|
||||
golang.org/x/crypto v0.0.0-20220722155217-630584e8d5aa/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
|
||||
golang.org/x/crypto v0.3.0 h1:a06MkbcxBrEFc0w0QIZWXrH/9cCX6KJyWbBOIwAn+7A=
|
||||
golang.org/x/crypto v0.3.0/go.mod h1:hebNnKkNXi2UzZN1eVRvBB7co0a+JxK6XbPiWVs/3J4=
|
||||
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=
|
||||
@@ -1340,6 +1335,7 @@ golang.org/x/net v0.0.0-20220127200216-cd36cc0744dd/go.mod h1:CfG3xpIq0wQ8r1q4Su
|
||||
golang.org/x/net v0.0.0-20220225172249-27dd8689420f/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk=
|
||||
golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
|
||||
golang.org/x/net v0.1.0/go.mod h1:Cx3nUiGt4eDBEyega/BKRp+/AlGL8hYe7U9odMt2Cco=
|
||||
golang.org/x/net v0.2.0/go.mod h1:KqCZLdyyvdV855qA2rE3GC2aiw5xGR5TEjj8smXukLY=
|
||||
golang.org/x/net v0.7.0 h1:rJrUqqhjsgNp7KqAIc25s9pZnjU7TUcSY7HcVZjdn1g=
|
||||
golang.org/x/net v0.7.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs=
|
||||
golang.org/x/oauth2 v0.0.0-20180227000427-d7d64896b5ff/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
|
||||
@@ -1487,11 +1483,13 @@ golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBc
|
||||
golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.2.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.5.0 h1:MUK/U/4lj1t1oPg0HfuXDN/Z1wv31ZJ/YcPiGccS4DU=
|
||||
golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
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=
|
||||
golang.org/x/term v0.2.0/go.mod h1:TVmDHMZPmdnySmBfhjOoOdhjzdE1h4u1VwSiw2l1Nuc=
|
||||
golang.org/x/term v0.5.0 h1:n2a8QNdAb0sZNpU9R1ALUXBbY+w51fCQDN+7EdxNBsY=
|
||||
golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k=
|
||||
golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
|
||||
@@ -37,7 +37,7 @@ spec:
|
||||
type: RuntimeDefault
|
||||
containers:
|
||||
- name: dex
|
||||
image: ghcr.io/dexidp/dex:v2.35.3
|
||||
image: ghcr.io/dexidp/dex:v2.36.0
|
||||
imagePullPolicy: Always
|
||||
command: [/shared/argocd-dex, rundex]
|
||||
env:
|
||||
|
||||
@@ -5,7 +5,7 @@ kind: Kustomization
|
||||
images:
|
||||
- name: quay.io/argoproj/argocd
|
||||
newName: quay.io/argoproj/argocd
|
||||
newTag: v2.6.7
|
||||
newTag: v2.6.9
|
||||
resources:
|
||||
- ./application-controller
|
||||
- ./dex
|
||||
|
||||
@@ -23,7 +23,7 @@ spec:
|
||||
serviceAccountName: argocd-redis
|
||||
containers:
|
||||
- name: redis
|
||||
image: redis:7.0.8-alpine
|
||||
image: redis:7.0.11-alpine
|
||||
imagePullPolicy: Always
|
||||
args:
|
||||
- "--save"
|
||||
|
||||
@@ -15557,7 +15557,7 @@ spec:
|
||||
key: applicationsetcontroller.enable.progressive.syncs
|
||||
name: argocd-cmd-params-cm
|
||||
optional: true
|
||||
image: quay.io/argoproj/argocd:v2.6.7
|
||||
image: quay.io/argoproj/argocd:v2.6.9
|
||||
imagePullPolicy: Always
|
||||
name: argocd-applicationset-controller
|
||||
ports:
|
||||
@@ -15639,7 +15639,7 @@ spec:
|
||||
- ""
|
||||
- --appendonly
|
||||
- "no"
|
||||
image: redis:7.0.8-alpine
|
||||
image: redis:7.0.11-alpine
|
||||
imagePullPolicy: Always
|
||||
name: redis
|
||||
ports:
|
||||
@@ -15821,7 +15821,7 @@ spec:
|
||||
value: /helm-working-dir
|
||||
- name: HELM_DATA_HOME
|
||||
value: /helm-working-dir
|
||||
image: quay.io/argoproj/argocd:v2.6.7
|
||||
image: quay.io/argoproj/argocd:v2.6.9
|
||||
imagePullPolicy: Always
|
||||
livenessProbe:
|
||||
failureThreshold: 3
|
||||
@@ -15873,7 +15873,7 @@ spec:
|
||||
- -n
|
||||
- /usr/local/bin/argocd
|
||||
- /var/run/argocd/argocd-cmp-server
|
||||
image: quay.io/argoproj/argocd:v2.6.7
|
||||
image: quay.io/argoproj/argocd:v2.6.9
|
||||
name: copyutil
|
||||
securityContext:
|
||||
allowPrivilegeEscalation: false
|
||||
@@ -16080,7 +16080,7 @@ spec:
|
||||
key: application.namespaces
|
||||
name: argocd-cmd-params-cm
|
||||
optional: true
|
||||
image: quay.io/argoproj/argocd:v2.6.7
|
||||
image: quay.io/argoproj/argocd:v2.6.9
|
||||
imagePullPolicy: Always
|
||||
name: argocd-application-controller
|
||||
ports:
|
||||
|
||||
@@ -12,4 +12,4 @@ resources:
|
||||
images:
|
||||
- name: quay.io/argoproj/argocd
|
||||
newName: quay.io/argoproj/argocd
|
||||
newTag: v2.6.7
|
||||
newTag: v2.6.9
|
||||
|
||||
@@ -11,7 +11,7 @@ patchesStrategicMerge:
|
||||
images:
|
||||
- name: quay.io/argoproj/argocd
|
||||
newName: quay.io/argoproj/argocd
|
||||
newTag: v2.6.7
|
||||
newTag: v2.6.9
|
||||
resources:
|
||||
- ../../base/application-controller
|
||||
- ../../base/applicationset-controller
|
||||
|
||||
@@ -1071,7 +1071,7 @@ spec:
|
||||
topologyKey: kubernetes.io/hostname
|
||||
initContainers:
|
||||
- name: config-init
|
||||
image: haproxy:2.6.9-alpine
|
||||
image: haproxy:2.6.12-alpine
|
||||
imagePullPolicy: IfNotPresent
|
||||
resources:
|
||||
{}
|
||||
@@ -1089,7 +1089,7 @@ spec:
|
||||
mountPath: /data
|
||||
containers:
|
||||
- name: haproxy
|
||||
image: haproxy:2.6.9-alpine
|
||||
image: haproxy:2.6.12-alpine
|
||||
imagePullPolicy: IfNotPresent
|
||||
securityContext:
|
||||
null
|
||||
@@ -1179,7 +1179,7 @@ spec:
|
||||
automountServiceAccountToken: false
|
||||
initContainers:
|
||||
- name: config-init
|
||||
image: redis:7.0.8-alpine
|
||||
image: redis:7.0.11-alpine
|
||||
imagePullPolicy: IfNotPresent
|
||||
resources:
|
||||
{}
|
||||
@@ -1206,7 +1206,7 @@ spec:
|
||||
|
||||
containers:
|
||||
- name: redis
|
||||
image: redis:7.0.8-alpine
|
||||
image: redis:7.0.11-alpine
|
||||
imagePullPolicy: IfNotPresent
|
||||
command:
|
||||
- redis-server
|
||||
@@ -1256,7 +1256,7 @@ spec:
|
||||
- /bin/sh
|
||||
- /readonly-config/trigger-failover-if-master.sh
|
||||
- name: sentinel
|
||||
image: redis:7.0.8-alpine
|
||||
image: redis:7.0.11-alpine
|
||||
imagePullPolicy: IfNotPresent
|
||||
command:
|
||||
- redis-sentinel
|
||||
@@ -1300,7 +1300,7 @@ spec:
|
||||
{}
|
||||
|
||||
- name: split-brain-fix
|
||||
image: redis:7.0.8-alpine
|
||||
image: redis:7.0.11-alpine
|
||||
imagePullPolicy: IfNotPresent
|
||||
command:
|
||||
- sh
|
||||
|
||||
@@ -11,14 +11,14 @@ redis-ha:
|
||||
IPv6:
|
||||
enabled: false
|
||||
image:
|
||||
tag: 2.6.9-alpine
|
||||
tag: 2.6.12-alpine
|
||||
containerSecurityContext: null
|
||||
timeout:
|
||||
server: 6m
|
||||
client: 6m
|
||||
checkInterval: 3s
|
||||
image:
|
||||
tag: 7.0.8-alpine
|
||||
tag: 7.0.11-alpine
|
||||
containerSecurityContext: null
|
||||
sentinel:
|
||||
bind: "0.0.0.0"
|
||||
|
||||
@@ -16758,7 +16758,7 @@ spec:
|
||||
key: applicationsetcontroller.enable.progressive.syncs
|
||||
name: argocd-cmd-params-cm
|
||||
optional: true
|
||||
image: quay.io/argoproj/argocd:v2.6.7
|
||||
image: quay.io/argoproj/argocd:v2.6.9
|
||||
imagePullPolicy: Always
|
||||
name: argocd-applicationset-controller
|
||||
ports:
|
||||
@@ -16839,7 +16839,7 @@ spec:
|
||||
key: dexserver.disable.tls
|
||||
name: argocd-cmd-params-cm
|
||||
optional: true
|
||||
image: ghcr.io/dexidp/dex:v2.35.3
|
||||
image: ghcr.io/dexidp/dex:v2.36.0
|
||||
imagePullPolicy: Always
|
||||
name: dex
|
||||
ports:
|
||||
@@ -16868,7 +16868,7 @@ spec:
|
||||
- -n
|
||||
- /usr/local/bin/argocd
|
||||
- /shared/argocd-dex
|
||||
image: quay.io/argoproj/argocd:v2.6.7
|
||||
image: quay.io/argoproj/argocd:v2.6.9
|
||||
imagePullPolicy: Always
|
||||
name: copyutil
|
||||
securityContext:
|
||||
@@ -16921,7 +16921,7 @@ spec:
|
||||
containers:
|
||||
- command:
|
||||
- argocd-notifications
|
||||
image: quay.io/argoproj/argocd:v2.6.7
|
||||
image: quay.io/argoproj/argocd:v2.6.9
|
||||
imagePullPolicy: Always
|
||||
livenessProbe:
|
||||
tcpSocket:
|
||||
@@ -16992,7 +16992,7 @@ spec:
|
||||
app.kubernetes.io/name: argocd-redis-ha-haproxy
|
||||
topologyKey: kubernetes.io/hostname
|
||||
containers:
|
||||
- image: haproxy:2.6.9-alpine
|
||||
- image: haproxy:2.6.12-alpine
|
||||
imagePullPolicy: IfNotPresent
|
||||
lifecycle: {}
|
||||
livenessProbe:
|
||||
@@ -17028,7 +17028,7 @@ spec:
|
||||
- /readonly/haproxy_init.sh
|
||||
command:
|
||||
- sh
|
||||
image: haproxy:2.6.9-alpine
|
||||
image: haproxy:2.6.12-alpine
|
||||
imagePullPolicy: IfNotPresent
|
||||
name: config-init
|
||||
securityContext:
|
||||
@@ -17224,7 +17224,7 @@ spec:
|
||||
value: /helm-working-dir
|
||||
- name: HELM_DATA_HOME
|
||||
value: /helm-working-dir
|
||||
image: quay.io/argoproj/argocd:v2.6.7
|
||||
image: quay.io/argoproj/argocd:v2.6.9
|
||||
imagePullPolicy: Always
|
||||
livenessProbe:
|
||||
failureThreshold: 3
|
||||
@@ -17276,7 +17276,7 @@ spec:
|
||||
- -n
|
||||
- /usr/local/bin/argocd
|
||||
- /var/run/argocd/argocd-cmp-server
|
||||
image: quay.io/argoproj/argocd:v2.6.7
|
||||
image: quay.io/argoproj/argocd:v2.6.9
|
||||
name: copyutil
|
||||
securityContext:
|
||||
allowPrivilegeEscalation: false
|
||||
@@ -17555,7 +17555,7 @@ spec:
|
||||
key: server.enable.proxy.extension
|
||||
name: argocd-cmd-params-cm
|
||||
optional: true
|
||||
image: quay.io/argoproj/argocd:v2.6.7
|
||||
image: quay.io/argoproj/argocd:v2.6.9
|
||||
imagePullPolicy: Always
|
||||
livenessProbe:
|
||||
httpGet:
|
||||
@@ -17791,7 +17791,7 @@ spec:
|
||||
key: application.namespaces
|
||||
name: argocd-cmd-params-cm
|
||||
optional: true
|
||||
image: quay.io/argoproj/argocd:v2.6.7
|
||||
image: quay.io/argoproj/argocd:v2.6.9
|
||||
imagePullPolicy: Always
|
||||
name: argocd-application-controller
|
||||
ports:
|
||||
@@ -17868,7 +17868,7 @@ spec:
|
||||
- /data/conf/redis.conf
|
||||
command:
|
||||
- redis-server
|
||||
image: redis:7.0.8-alpine
|
||||
image: redis:7.0.11-alpine
|
||||
imagePullPolicy: IfNotPresent
|
||||
lifecycle:
|
||||
preStop:
|
||||
@@ -17921,7 +17921,7 @@ spec:
|
||||
- /data/conf/sentinel.conf
|
||||
command:
|
||||
- redis-sentinel
|
||||
image: redis:7.0.8-alpine
|
||||
image: redis:7.0.11-alpine
|
||||
imagePullPolicy: IfNotPresent
|
||||
lifecycle: {}
|
||||
livenessProbe:
|
||||
@@ -17973,7 +17973,7 @@ spec:
|
||||
value: 40000915ab58c3fa8fd888fb8b24711944e6cbb4
|
||||
- name: SENTINEL_ID_2
|
||||
value: 2bbec7894d954a8af3bb54d13eaec53cb024e2ca
|
||||
image: redis:7.0.8-alpine
|
||||
image: redis:7.0.11-alpine
|
||||
imagePullPolicy: IfNotPresent
|
||||
name: split-brain-fix
|
||||
resources: {}
|
||||
@@ -18002,7 +18002,7 @@ spec:
|
||||
value: 40000915ab58c3fa8fd888fb8b24711944e6cbb4
|
||||
- name: SENTINEL_ID_2
|
||||
value: 2bbec7894d954a8af3bb54d13eaec53cb024e2ca
|
||||
image: redis:7.0.8-alpine
|
||||
image: redis:7.0.11-alpine
|
||||
imagePullPolicy: IfNotPresent
|
||||
name: config-init
|
||||
securityContext:
|
||||
|
||||
@@ -1562,7 +1562,7 @@ spec:
|
||||
key: applicationsetcontroller.enable.progressive.syncs
|
||||
name: argocd-cmd-params-cm
|
||||
optional: true
|
||||
image: quay.io/argoproj/argocd:v2.6.7
|
||||
image: quay.io/argoproj/argocd:v2.6.9
|
||||
imagePullPolicy: Always
|
||||
name: argocd-applicationset-controller
|
||||
ports:
|
||||
@@ -1643,7 +1643,7 @@ spec:
|
||||
key: dexserver.disable.tls
|
||||
name: argocd-cmd-params-cm
|
||||
optional: true
|
||||
image: ghcr.io/dexidp/dex:v2.35.3
|
||||
image: ghcr.io/dexidp/dex:v2.36.0
|
||||
imagePullPolicy: Always
|
||||
name: dex
|
||||
ports:
|
||||
@@ -1672,7 +1672,7 @@ spec:
|
||||
- -n
|
||||
- /usr/local/bin/argocd
|
||||
- /shared/argocd-dex
|
||||
image: quay.io/argoproj/argocd:v2.6.7
|
||||
image: quay.io/argoproj/argocd:v2.6.9
|
||||
imagePullPolicy: Always
|
||||
name: copyutil
|
||||
securityContext:
|
||||
@@ -1725,7 +1725,7 @@ spec:
|
||||
containers:
|
||||
- command:
|
||||
- argocd-notifications
|
||||
image: quay.io/argoproj/argocd:v2.6.7
|
||||
image: quay.io/argoproj/argocd:v2.6.9
|
||||
imagePullPolicy: Always
|
||||
livenessProbe:
|
||||
tcpSocket:
|
||||
@@ -1796,7 +1796,7 @@ spec:
|
||||
app.kubernetes.io/name: argocd-redis-ha-haproxy
|
||||
topologyKey: kubernetes.io/hostname
|
||||
containers:
|
||||
- image: haproxy:2.6.9-alpine
|
||||
- image: haproxy:2.6.12-alpine
|
||||
imagePullPolicy: IfNotPresent
|
||||
lifecycle: {}
|
||||
livenessProbe:
|
||||
@@ -1832,7 +1832,7 @@ spec:
|
||||
- /readonly/haproxy_init.sh
|
||||
command:
|
||||
- sh
|
||||
image: haproxy:2.6.9-alpine
|
||||
image: haproxy:2.6.12-alpine
|
||||
imagePullPolicy: IfNotPresent
|
||||
name: config-init
|
||||
securityContext:
|
||||
@@ -2028,7 +2028,7 @@ spec:
|
||||
value: /helm-working-dir
|
||||
- name: HELM_DATA_HOME
|
||||
value: /helm-working-dir
|
||||
image: quay.io/argoproj/argocd:v2.6.7
|
||||
image: quay.io/argoproj/argocd:v2.6.9
|
||||
imagePullPolicy: Always
|
||||
livenessProbe:
|
||||
failureThreshold: 3
|
||||
@@ -2080,7 +2080,7 @@ spec:
|
||||
- -n
|
||||
- /usr/local/bin/argocd
|
||||
- /var/run/argocd/argocd-cmp-server
|
||||
image: quay.io/argoproj/argocd:v2.6.7
|
||||
image: quay.io/argoproj/argocd:v2.6.9
|
||||
name: copyutil
|
||||
securityContext:
|
||||
allowPrivilegeEscalation: false
|
||||
@@ -2359,7 +2359,7 @@ spec:
|
||||
key: server.enable.proxy.extension
|
||||
name: argocd-cmd-params-cm
|
||||
optional: true
|
||||
image: quay.io/argoproj/argocd:v2.6.7
|
||||
image: quay.io/argoproj/argocd:v2.6.9
|
||||
imagePullPolicy: Always
|
||||
livenessProbe:
|
||||
httpGet:
|
||||
@@ -2595,7 +2595,7 @@ spec:
|
||||
key: application.namespaces
|
||||
name: argocd-cmd-params-cm
|
||||
optional: true
|
||||
image: quay.io/argoproj/argocd:v2.6.7
|
||||
image: quay.io/argoproj/argocd:v2.6.9
|
||||
imagePullPolicy: Always
|
||||
name: argocd-application-controller
|
||||
ports:
|
||||
@@ -2672,7 +2672,7 @@ spec:
|
||||
- /data/conf/redis.conf
|
||||
command:
|
||||
- redis-server
|
||||
image: redis:7.0.8-alpine
|
||||
image: redis:7.0.11-alpine
|
||||
imagePullPolicy: IfNotPresent
|
||||
lifecycle:
|
||||
preStop:
|
||||
@@ -2725,7 +2725,7 @@ spec:
|
||||
- /data/conf/sentinel.conf
|
||||
command:
|
||||
- redis-sentinel
|
||||
image: redis:7.0.8-alpine
|
||||
image: redis:7.0.11-alpine
|
||||
imagePullPolicy: IfNotPresent
|
||||
lifecycle: {}
|
||||
livenessProbe:
|
||||
@@ -2777,7 +2777,7 @@ spec:
|
||||
value: 40000915ab58c3fa8fd888fb8b24711944e6cbb4
|
||||
- name: SENTINEL_ID_2
|
||||
value: 2bbec7894d954a8af3bb54d13eaec53cb024e2ca
|
||||
image: redis:7.0.8-alpine
|
||||
image: redis:7.0.11-alpine
|
||||
imagePullPolicy: IfNotPresent
|
||||
name: split-brain-fix
|
||||
resources: {}
|
||||
@@ -2806,7 +2806,7 @@ spec:
|
||||
value: 40000915ab58c3fa8fd888fb8b24711944e6cbb4
|
||||
- name: SENTINEL_ID_2
|
||||
value: 2bbec7894d954a8af3bb54d13eaec53cb024e2ca
|
||||
image: redis:7.0.8-alpine
|
||||
image: redis:7.0.11-alpine
|
||||
imagePullPolicy: IfNotPresent
|
||||
name: config-init
|
||||
securityContext:
|
||||
|
||||
@@ -15877,7 +15877,7 @@ spec:
|
||||
key: applicationsetcontroller.enable.progressive.syncs
|
||||
name: argocd-cmd-params-cm
|
||||
optional: true
|
||||
image: quay.io/argoproj/argocd:v2.6.7
|
||||
image: quay.io/argoproj/argocd:v2.6.9
|
||||
imagePullPolicy: Always
|
||||
name: argocd-applicationset-controller
|
||||
ports:
|
||||
@@ -15958,7 +15958,7 @@ spec:
|
||||
key: dexserver.disable.tls
|
||||
name: argocd-cmd-params-cm
|
||||
optional: true
|
||||
image: ghcr.io/dexidp/dex:v2.35.3
|
||||
image: ghcr.io/dexidp/dex:v2.36.0
|
||||
imagePullPolicy: Always
|
||||
name: dex
|
||||
ports:
|
||||
@@ -15987,7 +15987,7 @@ spec:
|
||||
- -n
|
||||
- /usr/local/bin/argocd
|
||||
- /shared/argocd-dex
|
||||
image: quay.io/argoproj/argocd:v2.6.7
|
||||
image: quay.io/argoproj/argocd:v2.6.9
|
||||
imagePullPolicy: Always
|
||||
name: copyutil
|
||||
securityContext:
|
||||
@@ -16040,7 +16040,7 @@ spec:
|
||||
containers:
|
||||
- command:
|
||||
- argocd-notifications
|
||||
image: quay.io/argoproj/argocd:v2.6.7
|
||||
image: quay.io/argoproj/argocd:v2.6.9
|
||||
imagePullPolicy: Always
|
||||
livenessProbe:
|
||||
tcpSocket:
|
||||
@@ -16117,7 +16117,7 @@ spec:
|
||||
- ""
|
||||
- --appendonly
|
||||
- "no"
|
||||
image: redis:7.0.8-alpine
|
||||
image: redis:7.0.11-alpine
|
||||
imagePullPolicy: Always
|
||||
name: redis
|
||||
ports:
|
||||
@@ -16299,7 +16299,7 @@ spec:
|
||||
value: /helm-working-dir
|
||||
- name: HELM_DATA_HOME
|
||||
value: /helm-working-dir
|
||||
image: quay.io/argoproj/argocd:v2.6.7
|
||||
image: quay.io/argoproj/argocd:v2.6.9
|
||||
imagePullPolicy: Always
|
||||
livenessProbe:
|
||||
failureThreshold: 3
|
||||
@@ -16351,7 +16351,7 @@ spec:
|
||||
- -n
|
||||
- /usr/local/bin/argocd
|
||||
- /var/run/argocd/argocd-cmp-server
|
||||
image: quay.io/argoproj/argocd:v2.6.7
|
||||
image: quay.io/argoproj/argocd:v2.6.9
|
||||
name: copyutil
|
||||
securityContext:
|
||||
allowPrivilegeEscalation: false
|
||||
@@ -16626,7 +16626,7 @@ spec:
|
||||
key: server.enable.proxy.extension
|
||||
name: argocd-cmd-params-cm
|
||||
optional: true
|
||||
image: quay.io/argoproj/argocd:v2.6.7
|
||||
image: quay.io/argoproj/argocd:v2.6.9
|
||||
imagePullPolicy: Always
|
||||
livenessProbe:
|
||||
httpGet:
|
||||
@@ -16860,7 +16860,7 @@ spec:
|
||||
key: application.namespaces
|
||||
name: argocd-cmd-params-cm
|
||||
optional: true
|
||||
image: quay.io/argoproj/argocd:v2.6.7
|
||||
image: quay.io/argoproj/argocd:v2.6.9
|
||||
imagePullPolicy: Always
|
||||
name: argocd-application-controller
|
||||
ports:
|
||||
|
||||
@@ -681,7 +681,7 @@ spec:
|
||||
key: applicationsetcontroller.enable.progressive.syncs
|
||||
name: argocd-cmd-params-cm
|
||||
optional: true
|
||||
image: quay.io/argoproj/argocd:v2.6.7
|
||||
image: quay.io/argoproj/argocd:v2.6.9
|
||||
imagePullPolicy: Always
|
||||
name: argocd-applicationset-controller
|
||||
ports:
|
||||
@@ -762,7 +762,7 @@ spec:
|
||||
key: dexserver.disable.tls
|
||||
name: argocd-cmd-params-cm
|
||||
optional: true
|
||||
image: ghcr.io/dexidp/dex:v2.35.3
|
||||
image: ghcr.io/dexidp/dex:v2.36.0
|
||||
imagePullPolicy: Always
|
||||
name: dex
|
||||
ports:
|
||||
@@ -791,7 +791,7 @@ spec:
|
||||
- -n
|
||||
- /usr/local/bin/argocd
|
||||
- /shared/argocd-dex
|
||||
image: quay.io/argoproj/argocd:v2.6.7
|
||||
image: quay.io/argoproj/argocd:v2.6.9
|
||||
imagePullPolicy: Always
|
||||
name: copyutil
|
||||
securityContext:
|
||||
@@ -844,7 +844,7 @@ spec:
|
||||
containers:
|
||||
- command:
|
||||
- argocd-notifications
|
||||
image: quay.io/argoproj/argocd:v2.6.7
|
||||
image: quay.io/argoproj/argocd:v2.6.9
|
||||
imagePullPolicy: Always
|
||||
livenessProbe:
|
||||
tcpSocket:
|
||||
@@ -921,7 +921,7 @@ spec:
|
||||
- ""
|
||||
- --appendonly
|
||||
- "no"
|
||||
image: redis:7.0.8-alpine
|
||||
image: redis:7.0.11-alpine
|
||||
imagePullPolicy: Always
|
||||
name: redis
|
||||
ports:
|
||||
@@ -1103,7 +1103,7 @@ spec:
|
||||
value: /helm-working-dir
|
||||
- name: HELM_DATA_HOME
|
||||
value: /helm-working-dir
|
||||
image: quay.io/argoproj/argocd:v2.6.7
|
||||
image: quay.io/argoproj/argocd:v2.6.9
|
||||
imagePullPolicy: Always
|
||||
livenessProbe:
|
||||
failureThreshold: 3
|
||||
@@ -1155,7 +1155,7 @@ spec:
|
||||
- -n
|
||||
- /usr/local/bin/argocd
|
||||
- /var/run/argocd/argocd-cmp-server
|
||||
image: quay.io/argoproj/argocd:v2.6.7
|
||||
image: quay.io/argoproj/argocd:v2.6.9
|
||||
name: copyutil
|
||||
securityContext:
|
||||
allowPrivilegeEscalation: false
|
||||
@@ -1430,7 +1430,7 @@ spec:
|
||||
key: server.enable.proxy.extension
|
||||
name: argocd-cmd-params-cm
|
||||
optional: true
|
||||
image: quay.io/argoproj/argocd:v2.6.7
|
||||
image: quay.io/argoproj/argocd:v2.6.9
|
||||
imagePullPolicy: Always
|
||||
livenessProbe:
|
||||
httpGet:
|
||||
@@ -1664,7 +1664,7 @@ spec:
|
||||
key: application.namespaces
|
||||
name: argocd-cmd-params-cm
|
||||
optional: true
|
||||
image: quay.io/argoproj/argocd:v2.6.7
|
||||
image: quay.io/argoproj/argocd:v2.6.9
|
||||
imagePullPolicy: Always
|
||||
name: argocd-application-controller
|
||||
ports:
|
||||
|
||||
@@ -161,6 +161,7 @@ nav:
|
||||
- user-guide/best_practices.md
|
||||
- user-guide/status-badge.md
|
||||
- user-guide/external-url.md
|
||||
- user-guide/extra_info.md
|
||||
- Notification subscriptions: user-guide/subscriptions.md
|
||||
- Command Reference: user-guide/commands/argocd.md
|
||||
- Developer Guide:
|
||||
|
||||
@@ -450,6 +450,7 @@ type ApplicationSourceKustomize struct {
|
||||
func (k *ApplicationSourceKustomize) AllowsConcurrentProcessing() bool {
|
||||
return len(k.Images) == 0 &&
|
||||
len(k.CommonLabels) == 0 &&
|
||||
len(k.CommonAnnotations) == 0 &&
|
||||
k.NamePrefix == "" &&
|
||||
k.NameSuffix == ""
|
||||
}
|
||||
|
||||
@@ -2880,11 +2880,21 @@ func TestRetryStrategy_NextRetryAtCustomBackoff(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestSourceAllowsConcurrentProcessing_KustomizeParams(t *testing.T) {
|
||||
src := ApplicationSource{Path: ".", Kustomize: &ApplicationSourceKustomize{
|
||||
NameSuffix: "test",
|
||||
}}
|
||||
t.Run("Has NameSuffix", func(t *testing.T) {
|
||||
src := ApplicationSource{Path: ".", Kustomize: &ApplicationSourceKustomize{
|
||||
NameSuffix: "test",
|
||||
}}
|
||||
|
||||
assert.False(t, src.AllowsConcurrentProcessing())
|
||||
assert.False(t, src.AllowsConcurrentProcessing())
|
||||
})
|
||||
|
||||
t.Run("Has CommonAnnotations", func(t *testing.T) {
|
||||
src := ApplicationSource{Path: ".", Kustomize: &ApplicationSourceKustomize{
|
||||
CommonAnnotations: map[string]string{"foo": "bar"},
|
||||
}}
|
||||
|
||||
assert.False(t, src.AllowsConcurrentProcessing())
|
||||
})
|
||||
}
|
||||
|
||||
func TestUnSetCascadedDeletion(t *testing.T) {
|
||||
|
||||
@@ -221,7 +221,7 @@ func (s *Service) ListApps(ctx context.Context, q *apiclient.ListAppsRequest) (*
|
||||
}
|
||||
|
||||
defer io.Close(closer)
|
||||
apps, err := discovery.Discover(ctx, gitClient.Root(), q.EnabledSourceTypes, s.initConstants.CMPTarExcludedGlobs)
|
||||
apps, err := discovery.Discover(ctx, gitClient.Root(), gitClient.Root(), q.EnabledSourceTypes, s.initConstants.CMPTarExcludedGlobs)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -302,6 +302,7 @@ func (s *Service) runRepoOperation(
|
||||
var helmClient helm.Client
|
||||
var err error
|
||||
revision = textutils.FirstNonEmpty(revision, source.TargetRevision)
|
||||
unresolvedRevision := revision
|
||||
if source.IsHelm() {
|
||||
helmClient, revision, err = s.newHelmClientResolveRevision(repo, revision, source.Chart, settings.noCache || settings.noRevisionCache)
|
||||
if err != nil {
|
||||
@@ -426,7 +427,7 @@ func (s *Service) runRepoOperation(
|
||||
return operation(gitClient.Root(), commitSHA, revision, func() (*operationContext, error) {
|
||||
var signature string
|
||||
if verifyCommit {
|
||||
signature, err = gitClient.VerifyCommitSignature(revision)
|
||||
signature, err = gitClient.VerifyCommitSignature(unresolvedRevision)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -474,6 +475,7 @@ func resolveReferencedSources(hasMultipleSources bool, source *v1alpha1.Applicat
|
||||
if !ok {
|
||||
_, referencedCommitSHA, err := newClientResolveRevision(&refSourceMapping.Repo, refSourceMapping.TargetRevision)
|
||||
if err != nil {
|
||||
log.Errorf("Failed to get git client for repo %s: %v", refSourceMapping.Repo.Repo, err)
|
||||
return nil, fmt.Errorf("failed to get git client for repo %s", refSourceMapping.Repo.Repo)
|
||||
}
|
||||
|
||||
@@ -706,6 +708,7 @@ func (s *Service) runManifestGenAsync(ctx context.Context, repoRoot, commitSHA,
|
||||
} else {
|
||||
gitClient, referencedCommitSHA, err := s.newClientResolveRevision(&refSourceMapping.Repo, refSourceMapping.TargetRevision)
|
||||
if err != nil {
|
||||
log.Errorf("Failed to get git client for repo %s: %v", refSourceMapping.Repo.Repo, err)
|
||||
ch.errCh <- fmt.Errorf("failed to get git client for repo %s", refSourceMapping.Repo.Repo)
|
||||
return
|
||||
}
|
||||
@@ -1292,7 +1295,7 @@ func GenerateManifests(ctx context.Context, appPath, repoRoot, revision string,
|
||||
|
||||
resourceTracking := argo.NewResourceTracking()
|
||||
|
||||
appSourceType, err := GetAppSourceType(ctx, q.ApplicationSource, appPath, q.AppName, q.EnabledSourceTypes, opt.cmpTarExcludedGlobs)
|
||||
appSourceType, err := GetAppSourceType(ctx, q.ApplicationSource, appPath, repoRoot, q.AppName, q.EnabledSourceTypes, opt.cmpTarExcludedGlobs)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -1469,8 +1472,8 @@ func mergeSourceParameters(source *v1alpha1.ApplicationSource, path, appName str
|
||||
}
|
||||
|
||||
// GetAppSourceType returns explicit application source type or examines a directory and determines its application source type
|
||||
func GetAppSourceType(ctx context.Context, source *v1alpha1.ApplicationSource, path, appName string, enableGenerateManifests map[string]bool, tarExcludedGlobs []string) (v1alpha1.ApplicationSourceType, error) {
|
||||
err := mergeSourceParameters(source, path, appName)
|
||||
func GetAppSourceType(ctx context.Context, source *v1alpha1.ApplicationSource, appPath, repoPath, appName string, enableGenerateManifests map[string]bool, tarExcludedGlobs []string) (v1alpha1.ApplicationSourceType, error) {
|
||||
err := mergeSourceParameters(source, appPath, appName)
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("error while parsing source parameters: %v", err)
|
||||
}
|
||||
@@ -1486,7 +1489,7 @@ func GetAppSourceType(ctx context.Context, source *v1alpha1.ApplicationSource, p
|
||||
}
|
||||
return *appSourceType, nil
|
||||
}
|
||||
appType, err := discovery.AppType(ctx, path, enableGenerateManifests, tarExcludedGlobs)
|
||||
appType, err := discovery.AppType(ctx, appPath, repoPath, enableGenerateManifests, tarExcludedGlobs)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
@@ -1906,7 +1909,7 @@ func runConfigManagementPluginSidecars(ctx context.Context, appPath, repoPath, p
|
||||
}
|
||||
|
||||
// detect config management plugin server (sidecar)
|
||||
conn, cmpClient, err := discovery.DetectConfigManagementPlugin(ctx, appPath, pluginName, env, tarExcludedGlobs)
|
||||
conn, cmpClient, err := discovery.DetectConfigManagementPlugin(ctx, appPath, repoPath, pluginName, env, tarExcludedGlobs)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -1963,7 +1966,7 @@ func (s *Service) GetAppDetails(ctx context.Context, q *apiclient.RepoServerAppD
|
||||
return err
|
||||
}
|
||||
|
||||
appSourceType, err := GetAppSourceType(ctx, q.Source, opContext.appPath, q.AppName, q.EnabledSourceTypes, s.initConstants.CMPTarExcludedGlobs)
|
||||
appSourceType, err := GetAppSourceType(ctx, q.Source, opContext.appPath, repoRoot, q.AppName, q.EnabledSourceTypes, s.initConstants.CMPTarExcludedGlobs)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -2157,7 +2160,7 @@ func populatePluginAppDetails(ctx context.Context, res *apiclient.RepoAppDetails
|
||||
pluginName = q.Source.Plugin.Name
|
||||
}
|
||||
// detect config management plugin server (sidecar)
|
||||
conn, cmpClient, err := discovery.DetectConfigManagementPlugin(ctx, appPath, pluginName, env, tarExcludedGlobs)
|
||||
conn, cmpClient, err := discovery.DetectConfigManagementPlugin(ctx, appPath, repoPath, pluginName, env, tarExcludedGlobs)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to detect CMP for app: %w", err)
|
||||
}
|
||||
|
||||
@@ -1188,15 +1188,15 @@ func TestGenerateNullList(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestIdentifyAppSourceTypeByAppDirWithKustomizations(t *testing.T) {
|
||||
sourceType, err := GetAppSourceType(context.Background(), &argoappv1.ApplicationSource{}, "./testdata/kustomization_yaml", "testapp", map[string]bool{}, []string{})
|
||||
sourceType, err := GetAppSourceType(context.Background(), &argoappv1.ApplicationSource{}, "./testdata/kustomization_yaml", "./testdata", "testapp", map[string]bool{}, []string{})
|
||||
assert.Nil(t, err)
|
||||
assert.Equal(t, argoappv1.ApplicationSourceTypeKustomize, sourceType)
|
||||
|
||||
sourceType, err = GetAppSourceType(context.Background(), &argoappv1.ApplicationSource{}, "./testdata/kustomization_yml", "testapp", map[string]bool{}, []string{})
|
||||
sourceType, err = GetAppSourceType(context.Background(), &argoappv1.ApplicationSource{}, "./testdata/kustomization_yml", "./testdata", "testapp", map[string]bool{}, []string{})
|
||||
assert.Nil(t, err)
|
||||
assert.Equal(t, argoappv1.ApplicationSourceTypeKustomize, sourceType)
|
||||
|
||||
sourceType, err = GetAppSourceType(context.Background(), &argoappv1.ApplicationSource{}, "./testdata/Kustomization", "testapp", map[string]bool{}, []string{})
|
||||
sourceType, err = GetAppSourceType(context.Background(), &argoappv1.ApplicationSource{}, "./testdata/Kustomization", "./testdata", "testapp", map[string]bool{}, []string{})
|
||||
assert.Nil(t, err)
|
||||
assert.Equal(t, argoappv1.ApplicationSourceTypeKustomize, sourceType)
|
||||
}
|
||||
|
||||
@@ -396,9 +396,7 @@ func (t *TestServerStream) SendHeader(metadata.MD) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (t *TestServerStream) SetTrailer(metadata.MD) {
|
||||
return
|
||||
}
|
||||
func (t *TestServerStream) SetTrailer(metadata.MD) {}
|
||||
|
||||
func (t *TestServerStream) Context() context.Context {
|
||||
return t.ctx
|
||||
@@ -449,9 +447,7 @@ func (t *TestResourceTreeServer) SendHeader(metadata.MD) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (t *TestResourceTreeServer) SetTrailer(metadata.MD) {
|
||||
return
|
||||
}
|
||||
func (t *TestResourceTreeServer) SetTrailer(metadata.MD) {}
|
||||
|
||||
func (t *TestResourceTreeServer) Context() context.Context {
|
||||
return t.ctx
|
||||
@@ -481,9 +477,7 @@ func (t *TestPodLogsServer) SendHeader(metadata.MD) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (t *TestPodLogsServer) SetTrailer(metadata.MD) {
|
||||
return
|
||||
}
|
||||
func (t *TestPodLogsServer) SetTrailer(metadata.MD) {}
|
||||
|
||||
func (t *TestPodLogsServer) Context() context.Context {
|
||||
return t.ctx
|
||||
@@ -550,6 +544,7 @@ func TestNoAppEnumeration(t *testing.T) {
|
||||
appServer := newTestAppServerWithEnforcerConfigure(f, t, testApp, testDeployment)
|
||||
|
||||
noRoleCtx := context.Background()
|
||||
// nolint:staticcheck
|
||||
adminCtx := context.WithValue(noRoleCtx, "claims", &jwt.MapClaims{"groups": []string{"admin"}})
|
||||
|
||||
t.Run("Get", func(t *testing.T) {
|
||||
|
||||
@@ -160,6 +160,7 @@ func (s *Server) Get(ctx context.Context, q *repositorypkg.RepoQuery) (*appsv1.R
|
||||
GitHubAppEnterpriseBaseURL: repo.GitHubAppEnterpriseBaseURL,
|
||||
Proxy: repo.Proxy,
|
||||
Project: repo.Project,
|
||||
InheritedCreds: repo.InheritedCreds,
|
||||
}
|
||||
|
||||
item.ConnectionState = s.getConnectionState(ctx, item.Repo, q.ForceRefresh)
|
||||
@@ -193,6 +194,7 @@ func (s *Server) ListRepositories(ctx context.Context, q *repositorypkg.RepoQuer
|
||||
Proxy: repo.Proxy,
|
||||
Project: repo.Project,
|
||||
ForceHttpBasicAuth: repo.ForceHttpBasicAuth,
|
||||
InheritedCreds: repo.InheritedCreds,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@@ -86,7 +86,18 @@ var (
|
||||
Destinations: []appsv1.ApplicationDestination{{Server: "*", Namespace: "*"}},
|
||||
},
|
||||
}
|
||||
|
||||
fakeRepo = appsv1.Repository{
|
||||
Repo: "https://test",
|
||||
Type: "test",
|
||||
Name: "test",
|
||||
Username: "argo",
|
||||
Insecure: false,
|
||||
EnableLFS: false,
|
||||
EnableOCI: false,
|
||||
Proxy: "test",
|
||||
Project: "argocd",
|
||||
InheritedCreds: true,
|
||||
}
|
||||
guestbookApp = &appsv1.Application{
|
||||
TypeMeta: metav1.TypeMeta{
|
||||
Kind: "Application",
|
||||
@@ -196,6 +207,33 @@ func TestRepositoryServer(t *testing.T) {
|
||||
assert.Equal(t, repo.Repo, url)
|
||||
})
|
||||
|
||||
t.Run("Test_GetInherited", func(t *testing.T) {
|
||||
repoServerClient := mocks.RepoServerServiceClient{}
|
||||
repoServerClient.On("TestRepository", mock.Anything, mock.Anything).Return(&apiclient.TestRepositoryResponse{}, nil)
|
||||
repoServerClientset := mocks.Clientset{RepoServerServiceClient: &repoServerClient}
|
||||
|
||||
url := "https://test"
|
||||
db := &dbmocks.ArgoDB{}
|
||||
testRepo := &appsv1.Repository{
|
||||
Repo: url,
|
||||
Type: "git",
|
||||
Username: "foo",
|
||||
InheritedCreds: true,
|
||||
}
|
||||
db.On("GetRepository", context.TODO(), url).Return(testRepo, nil)
|
||||
db.On("RepositoryExists", context.TODO(), url).Return(true, nil)
|
||||
|
||||
s := NewServer(&repoServerClientset, db, enforcer, newFixtures().Cache, appLister, projLister, settingsMgr)
|
||||
repo, err := s.Get(context.TODO(), &repository.RepoQuery{
|
||||
Repo: url,
|
||||
})
|
||||
assert.Nil(t, err)
|
||||
|
||||
testRepo.ConnectionState = repo.ConnectionState // overwrite connection state on our test object to simplify comparison below
|
||||
|
||||
assert.Equal(t, testRepo, repo)
|
||||
})
|
||||
|
||||
t.Run("Test_GetWithErrorShouldReturn403", func(t *testing.T) {
|
||||
repoServerClient := mocks.RepoServerServiceClient{}
|
||||
repoServerClientset := mocks.Clientset{RepoServerServiceClient: &repoServerClient}
|
||||
@@ -279,6 +317,23 @@ func TestRepositoryServer(t *testing.T) {
|
||||
assert.Equal(t, repo.Repo, "test")
|
||||
})
|
||||
|
||||
t.Run("Test_ListRepositories", func(t *testing.T) {
|
||||
repoServerClient := mocks.RepoServerServiceClient{}
|
||||
repoServerClient.On("TestRepository", mock.Anything, mock.Anything).Return(&apiclient.TestRepositoryResponse{}, nil)
|
||||
repoServerClientset := mocks.Clientset{RepoServerServiceClient: &repoServerClient}
|
||||
enforcer := newEnforcer(kubeclientset)
|
||||
|
||||
url := "https://test"
|
||||
db := &dbmocks.ArgoDB{}
|
||||
db.On("GetRepository", context.TODO(), url).Return(nil, nil)
|
||||
db.On("ListHelmRepositories", context.TODO(), mock.Anything).Return(nil, nil)
|
||||
db.On("ListRepositories", context.TODO()).Return([]*appsv1.Repository{&fakeRepo, &fakeRepo}, nil)
|
||||
|
||||
s := NewServer(&repoServerClientset, db, enforcer, newFixtures().Cache, appLister, projLister, settingsMgr)
|
||||
resp, err := s.ListRepositories(context.TODO(), &repository.RepoQuery{})
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, 2, len(resp.Items))
|
||||
})
|
||||
}
|
||||
|
||||
func TestRepositoryServerListApps(t *testing.T) {
|
||||
|
||||
@@ -947,7 +947,8 @@ func (a *ArgoCDServer) newHTTPServer(ctx context.Context, port int, grpcWebHandl
|
||||
|
||||
// Webhook handler for git events (Note: cache timeouts are hardcoded because API server does not write to cache and not really using them)
|
||||
argoDB := db.NewDB(a.Namespace, a.settingsMgr, a.KubeClientset)
|
||||
acdWebhookHandler := webhook.NewHandler(a.Namespace, a.AppClientset, a.settings, a.settingsMgr, repocache.NewCache(a.Cache.GetCache(), 24*time.Hour, 3*time.Minute), a.Cache, argoDB)
|
||||
acdWebhookHandler := webhook.NewHandler(a.Namespace, a.ArgoCDServerOpts.ApplicationNamespaces, a.AppClientset, a.settings, a.settingsMgr, repocache.NewCache(a.Cache.GetCache(), 24*time.Hour, 3*time.Minute), a.Cache, argoDB)
|
||||
|
||||
mux.HandleFunc("/api/webhook", acdWebhookHandler.Handler)
|
||||
|
||||
// Serve cli binaries directly from API server
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
FROM docker.io/library/redis:7.0.8-alpine as redis
|
||||
FROM docker.io/library/redis:7.0.11-alpine as redis
|
||||
|
||||
# There are libraries we will want to copy from here in the final stage of the
|
||||
# build, but the COPY directive does not have a way to determine system
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
controller: [ "$BIN_MODE" == 'true' ] && COMMAND=./dist/argocd || COMMAND='go run ./cmd/main.go' && sh -c "FORCE_LOG_COLORS=1 ARGOCD_FAKE_IN_CLUSTER=true ARGOCD_TLS_DATA_PATH=${ARGOCD_TLS_DATA_PATH:-/tmp/argocd-local/tls} ARGOCD_SSH_DATA_PATH=${ARGOCD_SSH_DATA_PATH:-/tmp/argocd-local/ssh} ARGOCD_BINARY_NAME=argocd-application-controller $COMMAND --loglevel debug --redis localhost:${ARGOCD_E2E_REDIS_PORT:-6379} --repo-server localhost:${ARGOCD_E2E_REPOSERVER_PORT:-8081} --application-namespaces=${ARGOCD_APPLICATION_NAMESPACES:-''}"
|
||||
api-server: [ "$BIN_MODE" == 'true' ] && COMMAND=./dist/argocd || COMMAND='go run ./cmd/main.go' && sh -c "FORCE_LOG_COLORS=1 ARGOCD_FAKE_IN_CLUSTER=true ARGOCD_BINARY_NAME=argocd-server $COMMAND --loglevel debug --redis localhost:${ARGOCD_E2E_REDIS_PORT:-6379} --disable-auth=${ARGOCD_E2E_DISABLE_AUTH:-'true'} --insecure --dex-server http://localhost:${ARGOCD_E2E_DEX_PORT:-5556} --repo-server localhost:${ARGOCD_E2E_REPOSERVER_PORT:-8081} --port ${ARGOCD_E2E_APISERVER_PORT:-8080} --application-namespaces=${ARGOCD_APPLICATION_NAMESPACES:-''} "
|
||||
dex: sh -c "test $ARGOCD_IN_CI = true && exit 0; ARGOCD_BINARY_NAME=argocd-dex go run github.com/argoproj/argo-cd/cmd gendexcfg -o `pwd`/dist/dex.yaml && docker run --rm -p ${ARGOCD_E2E_DEX_PORT:-5556}:${ARGOCD_E2E_DEX_PORT:-5556} -v `pwd`/dist/dex.yaml:/dex.yaml ghcr.io/dexidp/dex:v2.35.3 serve /dex.yaml"
|
||||
controller: [ "$BIN_MODE" = 'true' ] && COMMAND=./dist/argocd || COMMAND='go run ./cmd/main.go' && sh -c "FORCE_LOG_COLORS=1 ARGOCD_FAKE_IN_CLUSTER=true ARGOCD_TLS_DATA_PATH=${ARGOCD_TLS_DATA_PATH:-/tmp/argocd-local/tls} ARGOCD_SSH_DATA_PATH=${ARGOCD_SSH_DATA_PATH:-/tmp/argocd-local/ssh} ARGOCD_BINARY_NAME=argocd-application-controller $COMMAND --loglevel debug --redis localhost:${ARGOCD_E2E_REDIS_PORT:-6379} --repo-server localhost:${ARGOCD_E2E_REPOSERVER_PORT:-8081} --application-namespaces=${ARGOCD_APPLICATION_NAMESPACES:-''}"
|
||||
api-server: [ "$BIN_MODE" = 'true' ] && COMMAND=./dist/argocd || COMMAND='go run ./cmd/main.go' && sh -c "FORCE_LOG_COLORS=1 ARGOCD_FAKE_IN_CLUSTER=true ARGOCD_BINARY_NAME=argocd-server $COMMAND --loglevel debug --redis localhost:${ARGOCD_E2E_REDIS_PORT:-6379} --disable-auth=${ARGOCD_E2E_DISABLE_AUTH:-'true'} --insecure --dex-server http://localhost:${ARGOCD_E2E_DEX_PORT:-5556} --repo-server localhost:${ARGOCD_E2E_REPOSERVER_PORT:-8081} --port ${ARGOCD_E2E_APISERVER_PORT:-8080} --application-namespaces=${ARGOCD_APPLICATION_NAMESPACES:-''} "
|
||||
dex: sh -c "test $ARGOCD_IN_CI = true && exit 0; ARGOCD_BINARY_NAME=argocd-dex go run github.com/argoproj/argo-cd/cmd gendexcfg -o `pwd`/dist/dex.yaml && docker run --rm -p ${ARGOCD_E2E_DEX_PORT:-5556}:${ARGOCD_E2E_DEX_PORT:-5556} -v `pwd`/dist/dex.yaml:/dex.yaml ghcr.io/dexidp/dex:v2.36.0 serve /dex.yaml"
|
||||
redis: sh -c "/usr/local/bin/redis-server --save "" --appendonly no --port ${ARGOCD_E2E_REDIS_PORT:-6379}"
|
||||
repo-server: [ "$BIN_MODE" == 'true' ] && COMMAND=./dist/argocd || COMMAND='go run ./cmd/main.go' && sh -c "FORCE_LOG_COLORS=1 ARGOCD_FAKE_IN_CLUSTER=true ARGOCD_GNUPGHOME=${ARGOCD_GNUPGHOME:-/tmp/argocd-local/gpg/keys} ARGOCD_PLUGINSOCKFILEPATH=${ARGOCD_PLUGINSOCKFILEPATH:-./test/cmp} ARGOCD_GPG_DATA_PATH=${ARGOCD_GPG_DATA_PATH:-/tmp/argocd-local/gpg/source} ARGOCD_BINARY_NAME=argocd-repo-server $COMMAND --loglevel debug --port ${ARGOCD_E2E_REPOSERVER_PORT:-8081} --redis localhost:${ARGOCD_E2E_REDIS_PORT:-6379}"
|
||||
ui: sh -c "test $ARGOCD_IN_CI = true && exit 0; cd ui && ARGOCD_E2E_YARN_HOST=0.0.0.0 ${ARGOCD_E2E_YARN_CMD:-yarn} start"
|
||||
|
||||
@@ -249,7 +249,7 @@ func TestSyncToSignedCommitWithoutKnownKey(t *testing.T) {
|
||||
Expect(HealthIs(health.HealthStatusMissing))
|
||||
}
|
||||
|
||||
func TestSyncToSignedCommitKeyWithKnownKey(t *testing.T) {
|
||||
func TestSyncToSignedCommitWithKnownKey(t *testing.T) {
|
||||
SkipOnEnv(t, "GPG")
|
||||
Given(t).
|
||||
Project("gpg").
|
||||
@@ -267,6 +267,62 @@ func TestSyncToSignedCommitKeyWithKnownKey(t *testing.T) {
|
||||
Expect(HealthIs(health.HealthStatusHealthy))
|
||||
}
|
||||
|
||||
func TestSyncToSignedTagWithKnownKey(t *testing.T) {
|
||||
SkipOnEnv(t, "GPG")
|
||||
Given(t).
|
||||
Project("gpg").
|
||||
Revision("signed-tag").
|
||||
Path(guestbookPath).
|
||||
GPGPublicKeyAdded().
|
||||
Sleep(2).
|
||||
When().
|
||||
AddSignedTag("signed-tag").
|
||||
IgnoreErrors().
|
||||
CreateApp().
|
||||
Sync().
|
||||
Then().
|
||||
Expect(OperationPhaseIs(OperationSucceeded)).
|
||||
Expect(SyncStatusIs(SyncStatusCodeSynced)).
|
||||
Expect(HealthIs(health.HealthStatusHealthy))
|
||||
}
|
||||
|
||||
func TestSyncToSignedTagWithUnknownKey(t *testing.T) {
|
||||
SkipOnEnv(t, "GPG")
|
||||
Given(t).
|
||||
Project("gpg").
|
||||
Revision("signed-tag").
|
||||
Path(guestbookPath).
|
||||
Sleep(2).
|
||||
When().
|
||||
AddSignedTag("signed-tag").
|
||||
IgnoreErrors().
|
||||
CreateApp().
|
||||
Sync().
|
||||
Then().
|
||||
Expect(OperationPhaseIs(OperationError)).
|
||||
Expect(SyncStatusIs(SyncStatusCodeOutOfSync)).
|
||||
Expect(HealthIs(health.HealthStatusMissing))
|
||||
}
|
||||
|
||||
func TestSyncToUnsignedTag(t *testing.T) {
|
||||
SkipOnEnv(t, "GPG")
|
||||
Given(t).
|
||||
Project("gpg").
|
||||
Revision("unsigned-tag").
|
||||
Path(guestbookPath).
|
||||
GPGPublicKeyAdded().
|
||||
Sleep(2).
|
||||
When().
|
||||
AddTag("unsigned-tag").
|
||||
IgnoreErrors().
|
||||
CreateApp().
|
||||
Sync().
|
||||
Then().
|
||||
Expect(OperationPhaseIs(OperationError)).
|
||||
Expect(SyncStatusIs(SyncStatusCodeOutOfSync)).
|
||||
Expect(HealthIs(health.HealthStatusMissing))
|
||||
}
|
||||
|
||||
func TestAppCreation(t *testing.T) {
|
||||
ctx := Given(t)
|
||||
ctx.
|
||||
|
||||
@@ -156,7 +156,7 @@ func TestCustomToolWithEnv(t *testing.T) {
|
||||
})
|
||||
}
|
||||
|
||||
//make sure we can sync and diff with --local
|
||||
// make sure we can sync and diff with --local
|
||||
func TestCustomToolSyncAndDiffLocal(t *testing.T) {
|
||||
ctx := Given(t)
|
||||
ctx.
|
||||
@@ -203,7 +203,7 @@ func startCMPServer(configFile string) {
|
||||
FailOnErr(RunWithStdin("", "", "../../dist/argocd", "--config-dir-path", configFile))
|
||||
}
|
||||
|
||||
//Discover by fileName
|
||||
// Discover by fileName
|
||||
func TestCMPDiscoverWithFileName(t *testing.T) {
|
||||
pluginName := "cmp-fileName"
|
||||
Given(t).
|
||||
@@ -212,7 +212,7 @@ func TestCMPDiscoverWithFileName(t *testing.T) {
|
||||
time.Sleep(1 * time.Second)
|
||||
os.Setenv("ARGOCD_BINARY_NAME", "argocd")
|
||||
}).
|
||||
Path(pluginName).
|
||||
Path(pluginName + "/subdir").
|
||||
When().
|
||||
CreateApp().
|
||||
Sync().
|
||||
@@ -222,7 +222,7 @@ func TestCMPDiscoverWithFileName(t *testing.T) {
|
||||
Expect(HealthIs(health.HealthStatusHealthy))
|
||||
}
|
||||
|
||||
//Discover by Find glob
|
||||
// Discover by Find glob
|
||||
func TestCMPDiscoverWithFindGlob(t *testing.T) {
|
||||
Given(t).
|
||||
And(func() {
|
||||
@@ -240,7 +240,7 @@ func TestCMPDiscoverWithFindGlob(t *testing.T) {
|
||||
Expect(HealthIs(health.HealthStatusHealthy))
|
||||
}
|
||||
|
||||
//Discover by Plugin Name
|
||||
// Discover by Plugin Name
|
||||
func TestCMPDiscoverWithPluginName(t *testing.T) {
|
||||
Given(t).
|
||||
And(func() {
|
||||
@@ -261,7 +261,7 @@ func TestCMPDiscoverWithPluginName(t *testing.T) {
|
||||
Expect(HealthIs(health.HealthStatusHealthy))
|
||||
}
|
||||
|
||||
//Discover by Find command
|
||||
// Discover by Find command
|
||||
func TestCMPDiscoverWithFindCommandWithEnv(t *testing.T) {
|
||||
pluginName := "cmp-find-command"
|
||||
ctx := Given(t)
|
||||
@@ -329,3 +329,54 @@ func TestPruneResourceFromCMP(t *testing.T) {
|
||||
assert.Error(t, err)
|
||||
})
|
||||
}
|
||||
|
||||
func TestCMPWithSymlinkPartialFiles(t *testing.T) {
|
||||
Given(t, WithTestData("testdata2")).
|
||||
And(func() {
|
||||
go startCMPServer("./testdata2/cmp-symlink")
|
||||
time.Sleep(1 * time.Second)
|
||||
os.Setenv("ARGOCD_BINARY_NAME", "argocd")
|
||||
}).
|
||||
Path("guestbook-partial-symlink-files").
|
||||
When().
|
||||
CreateApp().
|
||||
Sync().
|
||||
Then().
|
||||
Expect(OperationPhaseIs(OperationSucceeded)).
|
||||
Expect(SyncStatusIs(SyncStatusCodeSynced)).
|
||||
Expect(HealthIs(health.HealthStatusHealthy))
|
||||
}
|
||||
|
||||
func TestCMPWithSymlinkFiles(t *testing.T) {
|
||||
Given(t, WithTestData("testdata2")).
|
||||
And(func() {
|
||||
go startCMPServer("./testdata2/cmp-symlink")
|
||||
time.Sleep(1 * time.Second)
|
||||
os.Setenv("ARGOCD_BINARY_NAME", "argocd")
|
||||
}).
|
||||
Path("guestbook-symlink-files").
|
||||
When().
|
||||
CreateApp().
|
||||
Sync().
|
||||
Then().
|
||||
Expect(OperationPhaseIs(OperationSucceeded)).
|
||||
Expect(SyncStatusIs(SyncStatusCodeSynced)).
|
||||
Expect(HealthIs(health.HealthStatusHealthy))
|
||||
}
|
||||
|
||||
func TestCMPWithSymlinkFolder(t *testing.T) {
|
||||
Given(t, WithTestData("testdata2")).
|
||||
And(func() {
|
||||
go startCMPServer("./testdata2/cmp-symlink")
|
||||
time.Sleep(1 * time.Second)
|
||||
os.Setenv("ARGOCD_BINARY_NAME", "argocd")
|
||||
}).
|
||||
Path("guestbook-symlink-folder").
|
||||
When().
|
||||
CreateApp().
|
||||
Sync().
|
||||
Then().
|
||||
Expect(OperationPhaseIs(OperationSucceeded)).
|
||||
Expect(SyncStatusIs(SyncStatusCodeSynced)).
|
||||
Expect(HealthIs(health.HealthStatusHealthy))
|
||||
}
|
||||
|
||||
@@ -64,6 +64,18 @@ func (a *Actions) AddSignedFile(fileName, fileContents string) *Actions {
|
||||
return a
|
||||
}
|
||||
|
||||
func (a *Actions) AddSignedTag(name string) *Actions {
|
||||
a.context.t.Helper()
|
||||
fixture.AddSignedTag(name)
|
||||
return a
|
||||
}
|
||||
|
||||
func (a *Actions) AddTag(name string) *Actions {
|
||||
a.context.t.Helper()
|
||||
fixture.AddTag(name)
|
||||
return a
|
||||
}
|
||||
|
||||
func (a *Actions) CreateFromPartialFile(data string, flags ...string) *Actions {
|
||||
a.context.t.Helper()
|
||||
tmpFile, err := os.CreateTemp("", "")
|
||||
|
||||
@@ -50,8 +50,8 @@ type ContextArgs struct {
|
||||
AppNamespace string
|
||||
}
|
||||
|
||||
func Given(t *testing.T) *Context {
|
||||
fixture.EnsureCleanState(t)
|
||||
func Given(t *testing.T, opts ...fixture.TestOption) *Context {
|
||||
fixture.EnsureCleanState(t, opts...)
|
||||
return GivenWithSameState(t)
|
||||
}
|
||||
|
||||
|
||||
@@ -92,6 +92,7 @@ type ACL struct {
|
||||
const (
|
||||
RepoURLTypeFile = "file"
|
||||
RepoURLTypeHTTPS = "https"
|
||||
RepoURLTypeHTTPSOrg = "https-org"
|
||||
RepoURLTypeHTTPSClientCert = "https-cc"
|
||||
RepoURLTypeHTTPSSubmodule = "https-sub"
|
||||
RepoURLTypeHTTPSSubmoduleParent = "https-par"
|
||||
@@ -103,6 +104,8 @@ const (
|
||||
RepoURLTypeHelmOCI = "helm-oci"
|
||||
GitUsername = "admin"
|
||||
GitPassword = "password"
|
||||
GithubAppID = "2978632978"
|
||||
GithubAppInstallationID = "7893789433789"
|
||||
GpgGoodKeyID = "D56C4FCA57A46444"
|
||||
HelmOCIRegistryURL = "localhost:5000/myrepo"
|
||||
)
|
||||
@@ -251,6 +254,7 @@ const (
|
||||
EnvRepoURLTypeSSHSubmodule = "ARGOCD_E2E_REPO_SSH_SUBMODULE"
|
||||
EnvRepoURLTypeSSHSubmoduleParent = "ARGOCD_E2E_REPO_SSH_SUBMODULE_PARENT"
|
||||
EnvRepoURLTypeHTTPS = "ARGOCD_E2E_REPO_HTTPS"
|
||||
EnvRepoURLTypeHTTPSOrg = "ARGOCD_E2E_REPO_HTTPS_ORG"
|
||||
EnvRepoURLTypeHTTPSClientCert = "ARGOCD_E2E_REPO_HTTPS_CLIENT_CERT"
|
||||
EnvRepoURLTypeHTTPSSubmodule = "ARGOCD_E2E_REPO_HTTPS_SUBMODULE"
|
||||
EnvRepoURLTypeHTTPSSubmoduleParent = "ARGOCD_E2E_REPO_HTTPS_SUBMODULE_PARENT"
|
||||
@@ -272,6 +276,9 @@ func RepoURL(urlType RepoURLType) string {
|
||||
// Git server via HTTPS
|
||||
case RepoURLTypeHTTPS:
|
||||
return GetEnvWithDefault(EnvRepoURLTypeHTTPS, "https://localhost:9443/argo-e2e/testdata.git")
|
||||
// Git "organisation" via HTTPS
|
||||
case RepoURLTypeHTTPSOrg:
|
||||
return GetEnvWithDefault(EnvRepoURLTypeHTTPSOrg, "https://localhost:9443/argo-e2e")
|
||||
// Git server via HTTPS - Client Cert protected
|
||||
case RepoURLTypeHTTPSClientCert:
|
||||
return GetEnvWithDefault(EnvRepoURLTypeHTTPSClientCert, "https://localhost:9444/argo-e2e/testdata.git")
|
||||
@@ -514,7 +521,30 @@ func SetParamInNotificationsConfigMap(key, value string) {
|
||||
})
|
||||
}
|
||||
|
||||
func EnsureCleanState(t *testing.T) {
|
||||
type TestOption func(option *testOption)
|
||||
|
||||
type testOption struct {
|
||||
testdata string
|
||||
}
|
||||
|
||||
func newTestOption(opts ...TestOption) *testOption {
|
||||
to := &testOption{
|
||||
testdata: "testdata",
|
||||
}
|
||||
for _, opt := range opts {
|
||||
opt(to)
|
||||
}
|
||||
return to
|
||||
}
|
||||
|
||||
func WithTestData(testdata string) TestOption {
|
||||
return func(option *testOption) {
|
||||
option.testdata = testdata
|
||||
}
|
||||
}
|
||||
|
||||
func EnsureCleanState(t *testing.T, opts ...TestOption) {
|
||||
opt := newTestOption(opts...)
|
||||
// In large scenarios, we can skip tests that already run
|
||||
SkipIfAlreadyRun(t)
|
||||
// Register this test after it has been run & was successfull
|
||||
@@ -632,7 +662,7 @@ func EnsureCleanState(t *testing.T) {
|
||||
}
|
||||
|
||||
// set-up tmp repo, must have unique name
|
||||
FailOnErr(Run("", "cp", "-Rf", "testdata", repoDirectory()))
|
||||
FailOnErr(Run("", "cp", "-Rf", opt.testdata, repoDirectory()))
|
||||
FailOnErr(Run(repoDirectory(), "chmod", "777", "."))
|
||||
FailOnErr(Run(repoDirectory(), "git", "init"))
|
||||
FailOnErr(Run(repoDirectory(), "git", "add", "."))
|
||||
@@ -747,6 +777,26 @@ func AddSignedFile(path, contents string) {
|
||||
}
|
||||
}
|
||||
|
||||
func AddSignedTag(name string) {
|
||||
prevGnuPGHome := os.Getenv("GNUPGHOME")
|
||||
os.Setenv("GNUPGHOME", TmpDir+"/gpg")
|
||||
defer os.Setenv("GNUPGHOME", prevGnuPGHome)
|
||||
FailOnErr(Run(repoDirectory(), "git", "-c", fmt.Sprintf("user.signingkey=%s", GpgGoodKeyID), "tag", "-sm", "add signed tag", name))
|
||||
if IsRemote() {
|
||||
FailOnErr(Run(repoDirectory(), "git", "push", "--tags", "-f", "origin", "master"))
|
||||
}
|
||||
}
|
||||
|
||||
func AddTag(name string) {
|
||||
prevGnuPGHome := os.Getenv("GNUPGHOME")
|
||||
os.Setenv("GNUPGHOME", TmpDir+"/gpg")
|
||||
defer os.Setenv("GNUPGHOME", prevGnuPGHome)
|
||||
FailOnErr(Run(repoDirectory(), "git", "tag", name))
|
||||
if IsRemote() {
|
||||
FailOnErr(Run(repoDirectory(), "git", "push", "--tags", "-f", "origin", "master"))
|
||||
}
|
||||
}
|
||||
|
||||
// create the resource by creating using "kubectl apply", with bonus templating
|
||||
func Declarative(filename string, values interface{}) (string, error) {
|
||||
|
||||
|
||||
@@ -7,9 +7,11 @@ import (
|
||||
"github.com/stretchr/testify/assert"
|
||||
|
||||
repositorypkg "github.com/argoproj/argo-cd/v2/pkg/apiclient/repository"
|
||||
"github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1"
|
||||
"github.com/argoproj/argo-cd/v2/test/e2e/fixture"
|
||||
"github.com/argoproj/argo-cd/v2/test/e2e/fixture/app"
|
||||
"github.com/argoproj/argo-cd/v2/test/e2e/fixture/repos"
|
||||
. "github.com/argoproj/argo-cd/v2/util/errors"
|
||||
argoio "github.com/argoproj/argo-cd/v2/util/io"
|
||||
"github.com/argoproj/argo-cd/v2/util/settings"
|
||||
)
|
||||
@@ -52,6 +54,38 @@ func TestAddRemovePublicRepo(t *testing.T) {
|
||||
})
|
||||
}
|
||||
|
||||
func TestGetRepoWithInheritedCreds(t *testing.T) {
|
||||
app.Given(t).And(func() {
|
||||
// create repo credentials
|
||||
FailOnErr(fixture.RunCli("repocreds", "add", fixture.RepoURL(fixture.RepoURLTypeHTTPSOrg), "--github-app-id", fixture.GithubAppID, "--github-app-installation-id", fixture.GithubAppInstallationID, "--github-app-private-key-path", repos.CertKeyPath))
|
||||
|
||||
repoUrl := fixture.RepoURL(fixture.RepoURLTypeHTTPS)
|
||||
|
||||
// Hack: First we need to create repo with valid credentials
|
||||
FailOnErr(fixture.RunCli("repo", "add", repoUrl, "--username", fixture.GitUsername, "--password", fixture.GitPassword, "--insecure-skip-server-verification"))
|
||||
|
||||
// Then, we remove username/password so that the repo inherits the credentials from our repocreds
|
||||
conn, repoClient, err := fixture.ArgoCDClientset.NewRepoClient()
|
||||
assert.NoError(t, err)
|
||||
defer argoio.Close(conn)
|
||||
|
||||
_, err = repoClient.UpdateRepository(context.Background(), &repositorypkg.RepoUpdateRequest{
|
||||
Repo: &v1alpha1.Repository{
|
||||
Repo: repoUrl,
|
||||
},
|
||||
})
|
||||
assert.NoError(t, err)
|
||||
|
||||
// CLI output should indicate that repo has inherited credentials
|
||||
out, err := fixture.RunCli("repo", "get", repoUrl)
|
||||
assert.NoError(t, err)
|
||||
assert.Contains(t, out, "inherited")
|
||||
|
||||
_, err = fixture.RunCli("repo", "rm", repoUrl)
|
||||
assert.NoError(t, err)
|
||||
})
|
||||
}
|
||||
|
||||
func TestUpsertExistingRepo(t *testing.T) {
|
||||
app.Given(t).And(func() {
|
||||
fixture.SetRepos(settings.RepositoryCredentials{URL: fixture.RepoURL(fixture.RepoURLTypeFile)})
|
||||
|
||||
2
test/e2e/testdata/cmp-fileName/plugin.yaml
vendored
2
test/e2e/testdata/cmp-fileName/plugin.yaml
vendored
@@ -7,4 +7,4 @@ spec:
|
||||
generate:
|
||||
command: [sh, -c, 'echo "{\"kind\": \"ConfigMap\", \"apiVersion\": \"v1\", \"metadata\": { \"name\": \"$ARGOCD_APP_NAME\", \"namespace\": \"$ARGOCD_APP_NAMESPACE\", \"annotations\": {\"Foo\": \"$FOO\", \"KubeVersion\": \"$KUBE_VERSION\", \"KubeApiVersion\": \"$KUBE_API_VERSIONS\",\"Bar\": \"baz\"}}}"']
|
||||
discover:
|
||||
fileName: "subdir/s*.yaml"
|
||||
fileName: "cmp-fileName/subdir/s*.yaml"
|
||||
|
||||
13
test/e2e/testdata2/cmp-symlink/plugin.yaml
Normal file
13
test/e2e/testdata2/cmp-symlink/plugin.yaml
Normal file
@@ -0,0 +1,13 @@
|
||||
apiVersion: argoproj.io/v1alpha1
|
||||
kind: ConfigManagementPlugin
|
||||
metadata:
|
||||
name: cmp-symlink
|
||||
spec:
|
||||
version: v1.0
|
||||
init:
|
||||
command: [kustomize, version]
|
||||
generate:
|
||||
command: [sh, -c, 'kustomize edit set image test=quay.io/argoprojlabs/argocd-e2e-container:0.2 && kustomize build --load-restrictor LoadRestrictionsNone']
|
||||
discover:
|
||||
find:
|
||||
glob: "**/kustomization.yaml"
|
||||
@@ -0,0 +1 @@
|
||||
../guestbook/guestbook-ui-deployment.yaml
|
||||
@@ -0,0 +1,10 @@
|
||||
apiVersion: v1
|
||||
kind: Service
|
||||
metadata:
|
||||
name: guestbook-ui
|
||||
spec:
|
||||
ports:
|
||||
- port: 80
|
||||
targetPort: 80
|
||||
selector:
|
||||
app: guestbook-ui
|
||||
@@ -0,0 +1,6 @@
|
||||
apiVersion: kustomize.config.k8s.io/v1beta1
|
||||
kind: Kustomization
|
||||
|
||||
resources:
|
||||
- ./guestbook-ui-deployment.yaml
|
||||
- ./guestbook-ui-svc.yaml
|
||||
@@ -0,0 +1 @@
|
||||
../guestbook/guestbook-ui-deployment.yaml
|
||||
1
test/e2e/testdata2/guestbook-symlink-files/guestbook-ui-svc.yaml
Symbolic link
1
test/e2e/testdata2/guestbook-symlink-files/guestbook-ui-svc.yaml
Symbolic link
@@ -0,0 +1 @@
|
||||
../guestbook/guestbook-ui-svc.yaml
|
||||
1
test/e2e/testdata2/guestbook-symlink-files/kustomization.yaml
Symbolic link
1
test/e2e/testdata2/guestbook-symlink-files/kustomization.yaml
Symbolic link
@@ -0,0 +1 @@
|
||||
../guestbook/kustomization.yaml
|
||||
1
test/e2e/testdata2/guestbook-symlink-folder
Symbolic link
1
test/e2e/testdata2/guestbook-symlink-folder
Symbolic link
@@ -0,0 +1 @@
|
||||
guestbook
|
||||
23
test/e2e/testdata2/guestbook/guestbook-ui-deployment.yaml
Normal file
23
test/e2e/testdata2/guestbook/guestbook-ui-deployment.yaml
Normal file
@@ -0,0 +1,23 @@
|
||||
apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
metadata:
|
||||
name: guestbook-ui
|
||||
labels:
|
||||
test: "true"
|
||||
spec:
|
||||
replicas: 0
|
||||
revisionHistoryLimit: 3
|
||||
selector:
|
||||
matchLabels:
|
||||
app: guestbook-ui
|
||||
template:
|
||||
metadata:
|
||||
labels:
|
||||
app: guestbook-ui
|
||||
spec:
|
||||
containers:
|
||||
- image: test
|
||||
imagePullPolicy: IfNotPresent
|
||||
name: guestbook-ui
|
||||
ports:
|
||||
- containerPort: 80
|
||||
10
test/e2e/testdata2/guestbook/guestbook-ui-svc.yaml
Normal file
10
test/e2e/testdata2/guestbook/guestbook-ui-svc.yaml
Normal file
@@ -0,0 +1,10 @@
|
||||
apiVersion: v1
|
||||
kind: Service
|
||||
metadata:
|
||||
name: guestbook-ui
|
||||
spec:
|
||||
ports:
|
||||
- port: 80
|
||||
targetPort: 80
|
||||
selector:
|
||||
app: guestbook-ui
|
||||
6
test/e2e/testdata2/guestbook/kustomization.yaml
Normal file
6
test/e2e/testdata2/guestbook/kustomization.yaml
Normal file
@@ -0,0 +1,6 @@
|
||||
apiVersion: kustomize.config.k8s.io/v1beta1
|
||||
kind: Kustomization
|
||||
|
||||
resources:
|
||||
- ./guestbook-ui-deployment.yaml
|
||||
- ./guestbook-ui-svc.yaml
|
||||
@@ -35,7 +35,7 @@ test('gitlab.com', () => {
|
||||
'git@gitlab.com:alex_collins/private-repo.git',
|
||||
'b1fe9426ead684d7af16958920968342ee295c1f',
|
||||
'https://gitlab.com/alex_collins/private-repo',
|
||||
'https://gitlab.com/alex_collins/private-repo/commit/b1fe9426ead684d7af16958920968342ee295c1f');
|
||||
'https://gitlab.com/alex_collins/private-repo/-/commit/b1fe9426ead684d7af16958920968342ee295c1f');
|
||||
});
|
||||
|
||||
test('bitbucket.org', () => {
|
||||
|
||||
@@ -39,6 +39,12 @@ export function revisionUrl(url: string, revision: string, forPath: boolean): st
|
||||
urlSubPath = isSHA(revision) && !forPath ? 'commits' : 'src';
|
||||
}
|
||||
|
||||
// Gitlab changed the way urls to commit look like
|
||||
// Ref: https://docs.gitlab.com/ee/update/deprecations.html#legacy-urls-replaced-or-removed
|
||||
if (parsed.source === 'gitlab.com') {
|
||||
urlSubPath = '-/' + urlSubPath;
|
||||
}
|
||||
|
||||
if (!supportedSource(parsed)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@@ -322,11 +322,12 @@ export class ApplicationsService {
|
||||
.then(res => (res.body.actions as models.ResourceAction[]) || []);
|
||||
}
|
||||
|
||||
public patchResource(name: string, appNamspace: string, resource: models.ResourceNode, patch: string, patchType: string): Promise<models.State> {
|
||||
public patchResource(name: string, appNamespace: string, resource: models.ResourceNode, patch: string, patchType: string): Promise<models.State> {
|
||||
return requests
|
||||
.post(`/applications/${name}/resource`)
|
||||
.query({
|
||||
name: resource.name,
|
||||
appNamespace,
|
||||
namespace: resource.namespace,
|
||||
resourceName: resource.name,
|
||||
version: resource.version,
|
||||
|
||||
@@ -31,11 +31,11 @@ func IsManifestGenerationEnabled(sourceType v1alpha1.ApplicationSourceType, enab
|
||||
return enabled
|
||||
}
|
||||
|
||||
func Discover(ctx context.Context, repoPath string, enableGenerateManifests map[string]bool, tarExcludedGlobs []string) (map[string]string, error) {
|
||||
func Discover(ctx context.Context, appPath, repoPath string, enableGenerateManifests map[string]bool, tarExcludedGlobs []string) (map[string]string, error) {
|
||||
apps := make(map[string]string)
|
||||
|
||||
// Check if it is CMP
|
||||
conn, _, err := DetectConfigManagementPlugin(ctx, repoPath, "", []string{}, tarExcludedGlobs)
|
||||
conn, _, err := DetectConfigManagementPlugin(ctx, appPath, repoPath, "", []string{}, tarExcludedGlobs)
|
||||
if err == nil {
|
||||
// Found CMP
|
||||
io.Close(conn)
|
||||
@@ -44,14 +44,14 @@ func Discover(ctx context.Context, repoPath string, enableGenerateManifests map[
|
||||
return apps, nil
|
||||
}
|
||||
|
||||
err = filepath.Walk(repoPath, func(path string, info os.FileInfo, err error) error {
|
||||
err = filepath.Walk(appPath, func(path string, info os.FileInfo, err error) error {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if info.IsDir() {
|
||||
return nil
|
||||
}
|
||||
dir, err := filepath.Rel(repoPath, filepath.Dir(path))
|
||||
dir, err := filepath.Rel(appPath, filepath.Dir(path))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -67,8 +67,8 @@ func Discover(ctx context.Context, repoPath string, enableGenerateManifests map[
|
||||
return apps, err
|
||||
}
|
||||
|
||||
func AppType(ctx context.Context, path string, enableGenerateManifests map[string]bool, tarExcludedGlobs []string) (string, error) {
|
||||
apps, err := Discover(ctx, path, enableGenerateManifests, tarExcludedGlobs)
|
||||
func AppType(ctx context.Context, appPath, repoPath string, enableGenerateManifests map[string]bool, tarExcludedGlobs []string) (string, error) {
|
||||
apps, err := Discover(ctx, appPath, repoPath, enableGenerateManifests, tarExcludedGlobs)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
@@ -85,7 +85,7 @@ func AppType(ctx context.Context, path string, enableGenerateManifests map[strin
|
||||
// check cmpSupports()
|
||||
// if supported return conn for the cmp-server
|
||||
|
||||
func DetectConfigManagementPlugin(ctx context.Context, repoPath, pluginName string, env []string, tarExcludedGlobs []string) (io.Closer, pluginclient.ConfigManagementPluginServiceClient, error) {
|
||||
func DetectConfigManagementPlugin(ctx context.Context, appPath, repoPath, pluginName string, env []string, tarExcludedGlobs []string) (io.Closer, pluginclient.ConfigManagementPluginServiceClient, error) {
|
||||
var conn io.Closer
|
||||
var cmpClient pluginclient.ConfigManagementPluginServiceClient
|
||||
var connFound bool
|
||||
@@ -98,7 +98,7 @@ func DetectConfigManagementPlugin(ctx context.Context, repoPath, pluginName stri
|
||||
|
||||
if pluginName != "" {
|
||||
// check if the given plugin supports the repo
|
||||
conn, cmpClient, connFound = cmpSupports(ctx, pluginSockFilePath, repoPath, fmt.Sprintf("%v.sock", pluginName), env, tarExcludedGlobs, true)
|
||||
conn, cmpClient, connFound = cmpSupports(ctx, pluginSockFilePath, appPath, repoPath, fmt.Sprintf("%v.sock", pluginName), env, tarExcludedGlobs, true)
|
||||
if !connFound {
|
||||
return nil, nil, fmt.Errorf("couldn't find cmp-server plugin with name %v supporting the given repository", pluginName)
|
||||
}
|
||||
@@ -109,7 +109,7 @@ func DetectConfigManagementPlugin(ctx context.Context, repoPath, pluginName stri
|
||||
}
|
||||
for _, file := range fileList {
|
||||
if file.Type() == os.ModeSocket {
|
||||
conn, cmpClient, connFound = cmpSupports(ctx, pluginSockFilePath, repoPath, file.Name(), env, tarExcludedGlobs, false)
|
||||
conn, cmpClient, connFound = cmpSupports(ctx, pluginSockFilePath, appPath, repoPath, file.Name(), env, tarExcludedGlobs, false)
|
||||
if connFound {
|
||||
break
|
||||
}
|
||||
@@ -125,13 +125,13 @@ func DetectConfigManagementPlugin(ctx context.Context, repoPath, pluginName stri
|
||||
// matchRepositoryCMP will send the repoPath to the cmp-server. The cmp-server will
|
||||
// inspect the files and return true if the repo is supported for manifest generation.
|
||||
// Will return false otherwise.
|
||||
func matchRepositoryCMP(ctx context.Context, repoPath string, client pluginclient.ConfigManagementPluginServiceClient, env []string, tarExcludedGlobs []string) (bool, bool, error) {
|
||||
func matchRepositoryCMP(ctx context.Context, appPath, repoPath string, client pluginclient.ConfigManagementPluginServiceClient, env []string, tarExcludedGlobs []string) (bool, bool, error) {
|
||||
matchRepoStream, err := client.MatchRepository(ctx, grpc_retry.Disable())
|
||||
if err != nil {
|
||||
return false, false, fmt.Errorf("error getting stream client: %s", err)
|
||||
}
|
||||
|
||||
err = cmp.SendRepoStream(ctx, repoPath, repoPath, matchRepoStream, env, tarExcludedGlobs)
|
||||
err = cmp.SendRepoStream(ctx, appPath, repoPath, matchRepoStream, env, tarExcludedGlobs)
|
||||
if err != nil {
|
||||
return false, false, fmt.Errorf("error sending stream: %s", err)
|
||||
}
|
||||
@@ -142,7 +142,7 @@ func matchRepositoryCMP(ctx context.Context, repoPath string, client pluginclien
|
||||
return resp.GetIsSupported(), resp.GetIsDiscoveryEnabled(), nil
|
||||
}
|
||||
|
||||
func cmpSupports(ctx context.Context, pluginSockFilePath, repoPath, fileName string, env []string, tarExcludedGlobs []string, namedPlugin bool) (io.Closer, pluginclient.ConfigManagementPluginServiceClient, bool) {
|
||||
func cmpSupports(ctx context.Context, pluginSockFilePath, appPath, repoPath, fileName string, env []string, tarExcludedGlobs []string, namedPlugin bool) (io.Closer, pluginclient.ConfigManagementPluginServiceClient, bool) {
|
||||
address := filepath.Join(pluginSockFilePath, fileName)
|
||||
if !files.Inbound(address, pluginSockFilePath) {
|
||||
log.Errorf("invalid socket file path, %v is outside plugin socket dir %v", fileName, pluginSockFilePath)
|
||||
@@ -160,7 +160,7 @@ func cmpSupports(ctx context.Context, pluginSockFilePath, repoPath, fileName str
|
||||
return nil, nil, false
|
||||
}
|
||||
|
||||
isSupported, isDiscoveryEnabled, err := matchRepositoryCMP(ctx, repoPath, cmpClient, env, tarExcludedGlobs)
|
||||
isSupported, isDiscoveryEnabled, err := matchRepositoryCMP(ctx, appPath, repoPath, cmpClient, env, tarExcludedGlobs)
|
||||
if err != nil {
|
||||
log.WithFields(log.Fields{
|
||||
common.SecurityField: common.SecurityMedium,
|
||||
|
||||
@@ -10,7 +10,7 @@ import (
|
||||
)
|
||||
|
||||
func TestDiscover(t *testing.T) {
|
||||
apps, err := Discover(context.Background(), "./testdata", map[string]bool{}, []string{})
|
||||
apps, err := Discover(context.Background(), "./testdata", "./testdata", map[string]bool{}, []string{})
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, map[string]string{
|
||||
"foo": "Kustomize",
|
||||
@@ -19,15 +19,15 @@ func TestDiscover(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestAppType(t *testing.T) {
|
||||
appType, err := AppType(context.Background(), "./testdata/foo", map[string]bool{}, []string{})
|
||||
appType, err := AppType(context.Background(), "./testdata/foo", "./testdata", map[string]bool{}, []string{})
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, "Kustomize", appType)
|
||||
|
||||
appType, err = AppType(context.Background(), "./testdata/baz", map[string]bool{}, []string{})
|
||||
appType, err = AppType(context.Background(), "./testdata/baz", "./testdata", map[string]bool{}, []string{})
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, "Helm", appType)
|
||||
|
||||
appType, err = AppType(context.Background(), "./testdata", map[string]bool{}, []string{})
|
||||
appType, err = AppType(context.Background(), "./testdata", "./testdata", map[string]bool{}, []string{})
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, "Directory", appType)
|
||||
}
|
||||
@@ -37,15 +37,15 @@ func TestAppType_Disabled(t *testing.T) {
|
||||
string(v1alpha1.ApplicationSourceTypeKustomize): false,
|
||||
string(v1alpha1.ApplicationSourceTypeHelm): false,
|
||||
}
|
||||
appType, err := AppType(context.Background(), "./testdata/foo", enableManifestGeneration, []string{})
|
||||
appType, err := AppType(context.Background(), "./testdata/foo", "./testdata", enableManifestGeneration, []string{})
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, "Directory", appType)
|
||||
|
||||
appType, err = AppType(context.Background(), "./testdata/baz", enableManifestGeneration, []string{})
|
||||
appType, err = AppType(context.Background(), "./testdata/baz", "./testdata", enableManifestGeneration, []string{})
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, "Directory", appType)
|
||||
|
||||
appType, err = AppType(context.Background(), "./testdata", enableManifestGeneration, []string{})
|
||||
appType, err = AppType(context.Background(), "./testdata", "./testdata", enableManifestGeneration, []string{})
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, "Directory", appType)
|
||||
}
|
||||
|
||||
@@ -106,14 +106,14 @@ func SendRepoStream(ctx context.Context, appPath, repoPath string, sender Stream
|
||||
}
|
||||
|
||||
func GetCompressedRepoAndMetadata(repoPath string, appPath string, env []string, excludedGlobs []string, opt *senderOption) (*os.File, *pluginclient.AppStreamRequest, error) {
|
||||
// compress all files in appPath in tgz
|
||||
// compress all files in repoPath in tgz
|
||||
tgz, filesWritten, checksum, err := tgzstream.CompressFiles(repoPath, nil, excludedGlobs)
|
||||
if filesWritten == 0 {
|
||||
return nil, nil, fmt.Errorf("no files to send")
|
||||
}
|
||||
if err != nil {
|
||||
return nil, nil, fmt.Errorf("error compressing repo files: %w", err)
|
||||
}
|
||||
if filesWritten == 0 {
|
||||
return nil, nil, fmt.Errorf("no files to send")
|
||||
}
|
||||
if opt != nil && opt.tarDoneChan != nil {
|
||||
opt.tarDoneChan <- true
|
||||
close(opt.tarDoneChan)
|
||||
|
||||
@@ -39,12 +39,12 @@ type RepoStreamReceiver interface {
|
||||
// SendApplicationManifestQueryWithFiles compresses a folder and sends it over the stream
|
||||
func SendApplicationManifestQueryWithFiles(ctx context.Context, stream ApplicationStreamSender, appName string, appNs string, dir string, inclusions []string) error {
|
||||
f, filesWritten, checksum, err := tgzstream.CompressFiles(dir, inclusions, nil)
|
||||
if filesWritten == 0 {
|
||||
return fmt.Errorf("no files to send")
|
||||
}
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to compress files: %w", err)
|
||||
}
|
||||
if filesWritten == 0 {
|
||||
return fmt.Errorf("no files to send")
|
||||
}
|
||||
|
||||
err = stream.Send(&applicationpkg.ApplicationManifestQueryWithFilesWrapper{
|
||||
Part: &applicationpkg.ApplicationManifestQueryWithFilesWrapper_Query{
|
||||
|
||||
@@ -4,6 +4,7 @@ import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"github.com/argoproj/argo-cd/v2/util/glob"
|
||||
"html"
|
||||
"net/http"
|
||||
"net/url"
|
||||
@@ -47,6 +48,7 @@ type ArgoCDWebhookHandler struct {
|
||||
serverCache *servercache.Cache
|
||||
db db.ArgoDB
|
||||
ns string
|
||||
appNs []string
|
||||
appClientset appclientset.Interface
|
||||
github *github.Webhook
|
||||
gitlab *gitlab.Webhook
|
||||
@@ -56,7 +58,7 @@ type ArgoCDWebhookHandler struct {
|
||||
settingsSrc settingsSource
|
||||
}
|
||||
|
||||
func NewHandler(namespace string, appClientset appclientset.Interface, set *settings.ArgoCDSettings, settingsSrc settingsSource, repoCache *cache.Cache, serverCache *servercache.Cache, argoDB db.ArgoDB) *ArgoCDWebhookHandler {
|
||||
func NewHandler(namespace string, applicationNamespaces []string, appClientset appclientset.Interface, set *settings.ArgoCDSettings, settingsSrc settingsSource, repoCache *cache.Cache, serverCache *servercache.Cache, argoDB db.ArgoDB) *ArgoCDWebhookHandler {
|
||||
githubWebhook, err := github.New(github.Options.Secret(set.WebhookGitHubSecret))
|
||||
if err != nil {
|
||||
log.Warnf("Unable to init the GitHub webhook")
|
||||
@@ -80,6 +82,7 @@ func NewHandler(namespace string, appClientset appclientset.Interface, set *sett
|
||||
|
||||
acdWebhook := ArgoCDWebhookHandler{
|
||||
ns: namespace,
|
||||
appNs: applicationNamespaces,
|
||||
appClientset: appClientset,
|
||||
github: githubWebhook,
|
||||
gitlab: gitlabWebhook,
|
||||
@@ -216,7 +219,14 @@ func (a *ArgoCDWebhookHandler) HandleEvent(payload interface{}) {
|
||||
for _, webURL := range webURLs {
|
||||
log.Infof("Received push event repo: %s, revision: %s, touchedHead: %v", webURL, revision, touchedHead)
|
||||
}
|
||||
appIf := a.appClientset.ArgoprojV1alpha1().Applications(a.ns)
|
||||
|
||||
nsFilter := a.ns
|
||||
if len(a.appNs) > 0 {
|
||||
// Retrieve app from all namespaces
|
||||
nsFilter = ""
|
||||
}
|
||||
|
||||
appIf := a.appClientset.ArgoprojV1alpha1().Applications(nsFilter)
|
||||
apps, err := appIf.List(context.Background(), metav1.ListOptions{})
|
||||
if err != nil {
|
||||
log.Warnf("Failed to list applications: %v", err)
|
||||
@@ -234,13 +244,23 @@ func (a *ArgoCDWebhookHandler) HandleEvent(payload interface{}) {
|
||||
return
|
||||
}
|
||||
|
||||
// Skip any application that is neither in the control plane's namespace
|
||||
// nor in the list of enabled namespaces.
|
||||
var filteredApps []v1alpha1.Application
|
||||
for _, app := range apps.Items {
|
||||
if app.Namespace == a.ns || glob.MatchStringInList(a.appNs, app.Namespace, false) {
|
||||
filteredApps = append(filteredApps, app)
|
||||
}
|
||||
}
|
||||
|
||||
for _, webURL := range webURLs {
|
||||
repoRegexp, err := getWebUrlRegex(webURL)
|
||||
if err != nil {
|
||||
log.Warnf("Failed to get repoRegexp: %s", err)
|
||||
continue
|
||||
}
|
||||
for _, app := range apps.Items {
|
||||
for _, app := range filteredApps {
|
||||
|
||||
for _, source := range app.Spec.GetSources() {
|
||||
if sourceRevisionHasChanged(source, revision, touchedHead) && sourceUsesURL(source, webURL, repoRegexp) {
|
||||
if appFilesHaveChanged(&app, changedFiles) {
|
||||
|
||||
@@ -53,7 +53,7 @@ type reactorDef struct {
|
||||
reaction kubetesting.ReactionFunc
|
||||
}
|
||||
|
||||
func NewMockHandler(reactor *reactorDef, objects ...runtime.Object) *ArgoCDWebhookHandler {
|
||||
func NewMockHandler(reactor *reactorDef, applicationNamespaces []string, objects ...runtime.Object) *ArgoCDWebhookHandler {
|
||||
appClientset := appclientset.NewSimpleClientset(objects...)
|
||||
if reactor != nil {
|
||||
defaultReactor := appClientset.ReactionChain[0]
|
||||
@@ -64,7 +64,8 @@ func NewMockHandler(reactor *reactorDef, objects ...runtime.Object) *ArgoCDWebho
|
||||
appClientset.AddReactor(reactor.verb, reactor.resource, reactor.reaction)
|
||||
}
|
||||
cacheClient := cacheutil.NewCache(cacheutil.NewInMemoryCache(1 * time.Hour))
|
||||
return NewHandler("", appClientset, &settings.ArgoCDSettings{}, &fakeSettingsSrc{}, cache.NewCache(
|
||||
|
||||
return NewHandler("argocd", applicationNamespaces, appClientset, &settings.ArgoCDSettings{}, &fakeSettingsSrc{}, cache.NewCache(
|
||||
cacheClient,
|
||||
1*time.Minute,
|
||||
1*time.Minute,
|
||||
@@ -73,8 +74,8 @@ func NewMockHandler(reactor *reactorDef, objects ...runtime.Object) *ArgoCDWebho
|
||||
|
||||
func TestGitHubCommitEvent(t *testing.T) {
|
||||
hook := test.NewGlobal()
|
||||
h := NewMockHandler(nil)
|
||||
req := httptest.NewRequest("POST", "/api/webhook", nil)
|
||||
h := NewMockHandler(nil, []string{})
|
||||
req := httptest.NewRequest(http.MethodPost, "/api/webhook", nil)
|
||||
req.Header.Set("X-GitHub-Event", "push")
|
||||
eventJSON, err := os.ReadFile("testdata/github-commit-event.json")
|
||||
assert.NoError(t, err)
|
||||
@@ -98,9 +99,10 @@ func TestGitHubCommitEvent_MultiSource_Refresh(t *testing.T) {
|
||||
patched = true
|
||||
return true, nil, nil
|
||||
}
|
||||
h := NewMockHandler(&reactorDef{"patch", "applications", reaction}, &v1alpha1.Application{
|
||||
h := NewMockHandler(&reactorDef{"patch", "applications", reaction}, []string{}, &v1alpha1.Application{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "app-to-refresh",
|
||||
Name: "app-to-refresh",
|
||||
Namespace: "argocd",
|
||||
},
|
||||
Spec: v1alpha1.ApplicationSpec{
|
||||
Sources: v1alpha1.ApplicationSources{
|
||||
@@ -126,8 +128,9 @@ func TestGitHubCommitEvent_MultiSource_Refresh(t *testing.T) {
|
||||
},
|
||||
},
|
||||
},
|
||||
})
|
||||
req := httptest.NewRequest("POST", "/api/webhook", nil)
|
||||
},
|
||||
)
|
||||
req := httptest.NewRequest(http.MethodPost, "/api/webhook", nil)
|
||||
req.Header.Set("X-GitHub-Event", "push")
|
||||
eventJSON, err := os.ReadFile("testdata/github-commit-event.json")
|
||||
assert.NoError(t, err)
|
||||
@@ -141,10 +144,106 @@ func TestGitHubCommitEvent_MultiSource_Refresh(t *testing.T) {
|
||||
hook.Reset()
|
||||
}
|
||||
|
||||
// TestGitHubCommitEvent_AppsInOtherNamespaces makes sure that webhooks properly find apps in the configured set of
|
||||
// allowed namespaces when Apps are allowed in any namespace
|
||||
func TestGitHubCommitEvent_AppsInOtherNamespaces(t *testing.T) {
|
||||
hook := test.NewGlobal()
|
||||
|
||||
patchedApps := make([]string, 0, 3)
|
||||
reaction := func(action kubetesting.Action) (handled bool, ret runtime.Object, err error) {
|
||||
patchAction := action.(kubetesting.PatchAction)
|
||||
patchedApps = append(patchedApps, patchAction.GetName())
|
||||
return true, nil, nil
|
||||
}
|
||||
|
||||
h := NewMockHandler(&reactorDef{"patch", "applications", reaction}, []string{"end-to-end-tests", "app-team-*"},
|
||||
&v1alpha1.Application{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "app-to-refresh-in-default-namespace",
|
||||
Namespace: "argocd",
|
||||
},
|
||||
Spec: v1alpha1.ApplicationSpec{
|
||||
Sources: v1alpha1.ApplicationSources{
|
||||
{
|
||||
RepoURL: "https://github.com/jessesuen/test-repo",
|
||||
Path: ".",
|
||||
},
|
||||
},
|
||||
},
|
||||
}, &v1alpha1.Application{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "app-to-ignore",
|
||||
Namespace: "kube-system",
|
||||
},
|
||||
Spec: v1alpha1.ApplicationSpec{
|
||||
Sources: v1alpha1.ApplicationSources{
|
||||
{
|
||||
RepoURL: "https://github.com/jessesuen/test-repo",
|
||||
Path: ".",
|
||||
},
|
||||
},
|
||||
},
|
||||
}, &v1alpha1.Application{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "app-to-refresh-in-exact-match-namespace",
|
||||
Namespace: "end-to-end-tests",
|
||||
},
|
||||
Spec: v1alpha1.ApplicationSpec{
|
||||
Sources: v1alpha1.ApplicationSources{
|
||||
{
|
||||
RepoURL: "https://github.com/jessesuen/test-repo",
|
||||
Path: ".",
|
||||
},
|
||||
},
|
||||
},
|
||||
}, &v1alpha1.Application{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "app-to-refresh-in-globbed-namespace",
|
||||
Namespace: "app-team-two",
|
||||
},
|
||||
Spec: v1alpha1.ApplicationSpec{
|
||||
Sources: v1alpha1.ApplicationSources{
|
||||
{
|
||||
RepoURL: "https://github.com/jessesuen/test-repo",
|
||||
Path: ".",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
)
|
||||
req := httptest.NewRequest("POST", "/api/webhook", nil)
|
||||
req.Header.Set("X-GitHub-Event", "push")
|
||||
eventJSON, err := os.ReadFile("testdata/github-commit-event.json")
|
||||
assert.NoError(t, err)
|
||||
req.Body = io.NopCloser(bytes.NewReader(eventJSON))
|
||||
w := httptest.NewRecorder()
|
||||
h.Handler(w, req)
|
||||
assert.Equal(t, w.Code, http.StatusOK)
|
||||
|
||||
logMessages := make([]string, 0, len(hook.Entries))
|
||||
|
||||
for _, entry := range hook.Entries {
|
||||
logMessages = append(logMessages, entry.Message)
|
||||
}
|
||||
|
||||
assert.Contains(t, logMessages, "Requested app 'app-to-refresh-in-default-namespace' refresh")
|
||||
assert.Contains(t, logMessages, "Requested app 'app-to-refresh-in-exact-match-namespace' refresh")
|
||||
assert.Contains(t, logMessages, "Requested app 'app-to-refresh-in-globbed-namespace' refresh")
|
||||
assert.NotContains(t, logMessages, "Requested app 'app-to-ignore' refresh")
|
||||
|
||||
assert.Contains(t, patchedApps, "app-to-refresh-in-default-namespace")
|
||||
assert.Contains(t, patchedApps, "app-to-refresh-in-exact-match-namespace")
|
||||
assert.Contains(t, patchedApps, "app-to-refresh-in-globbed-namespace")
|
||||
assert.NotContains(t, patchedApps, "app-to-ignore")
|
||||
assert.Len(t, patchedApps, 3)
|
||||
|
||||
hook.Reset()
|
||||
}
|
||||
|
||||
func TestGitHubTagEvent(t *testing.T) {
|
||||
hook := test.NewGlobal()
|
||||
h := NewMockHandler(nil)
|
||||
req := httptest.NewRequest("POST", "/api/webhook", nil)
|
||||
h := NewMockHandler(nil, []string{})
|
||||
req := httptest.NewRequest(http.MethodPost, "/api/webhook", nil)
|
||||
req.Header.Set("X-GitHub-Event", "push")
|
||||
eventJSON, err := os.ReadFile("testdata/github-tag-event.json")
|
||||
assert.NoError(t, err)
|
||||
@@ -159,8 +258,8 @@ func TestGitHubTagEvent(t *testing.T) {
|
||||
|
||||
func TestGitHubPingEvent(t *testing.T) {
|
||||
hook := test.NewGlobal()
|
||||
h := NewMockHandler(nil)
|
||||
req := httptest.NewRequest("POST", "/api/webhook", nil)
|
||||
h := NewMockHandler(nil, []string{})
|
||||
req := httptest.NewRequest(http.MethodPost, "/api/webhook", nil)
|
||||
req.Header.Set("X-GitHub-Event", "ping")
|
||||
eventJSON, err := os.ReadFile("testdata/github-ping-event.json")
|
||||
assert.NoError(t, err)
|
||||
@@ -175,8 +274,8 @@ func TestGitHubPingEvent(t *testing.T) {
|
||||
|
||||
func TestBitbucketServerRepositoryReferenceChangedEvent(t *testing.T) {
|
||||
hook := test.NewGlobal()
|
||||
h := NewMockHandler(nil)
|
||||
req := httptest.NewRequest("POST", "/api/webhook", nil)
|
||||
h := NewMockHandler(nil, []string{})
|
||||
req := httptest.NewRequest(http.MethodPost, "/api/webhook", nil)
|
||||
req.Header.Set("X-Event-Key", "repo:refs_changed")
|
||||
eventJSON, err := os.ReadFile("testdata/bitbucket-server-event.json")
|
||||
assert.NoError(t, err)
|
||||
@@ -193,7 +292,7 @@ func TestBitbucketServerRepositoryReferenceChangedEvent(t *testing.T) {
|
||||
|
||||
func TestBitbucketServerRepositoryDiagnosticPingEvent(t *testing.T) {
|
||||
hook := test.NewGlobal()
|
||||
h := NewMockHandler(nil)
|
||||
h := NewMockHandler(nil, []string{})
|
||||
eventJSON := "{\"test\": true}"
|
||||
req := httptest.NewRequest("POST", "/api/webhook", bytes.NewBufferString(eventJSON))
|
||||
req.Header.Set("X-Event-Key", "diagnostics:ping")
|
||||
@@ -207,8 +306,8 @@ func TestBitbucketServerRepositoryDiagnosticPingEvent(t *testing.T) {
|
||||
|
||||
func TestGogsPushEvent(t *testing.T) {
|
||||
hook := test.NewGlobal()
|
||||
h := NewMockHandler(nil)
|
||||
req := httptest.NewRequest("POST", "/api/webhook", nil)
|
||||
h := NewMockHandler(nil, []string{})
|
||||
req := httptest.NewRequest(http.MethodPost, "/api/webhook", nil)
|
||||
req.Header.Set("X-Gogs-Event", "push")
|
||||
eventJSON, err := os.ReadFile("testdata/gogs-event.json")
|
||||
assert.NoError(t, err)
|
||||
@@ -223,8 +322,8 @@ func TestGogsPushEvent(t *testing.T) {
|
||||
|
||||
func TestGitLabPushEvent(t *testing.T) {
|
||||
hook := test.NewGlobal()
|
||||
h := NewMockHandler(nil)
|
||||
req := httptest.NewRequest("POST", "/api/webhook", nil)
|
||||
h := NewMockHandler(nil, []string{})
|
||||
req := httptest.NewRequest(http.MethodPost, "/api/webhook", nil)
|
||||
req.Header.Set("X-Gitlab-Event", "Push Hook")
|
||||
eventJSON, err := os.ReadFile("testdata/gitlab-event.json")
|
||||
assert.NoError(t, err)
|
||||
@@ -239,8 +338,8 @@ func TestGitLabPushEvent(t *testing.T) {
|
||||
|
||||
func TestInvalidMethod(t *testing.T) {
|
||||
hook := test.NewGlobal()
|
||||
h := NewMockHandler(nil)
|
||||
req := httptest.NewRequest("GET", "/api/webhook", nil)
|
||||
h := NewMockHandler(nil, []string{})
|
||||
req := httptest.NewRequest(http.MethodGet, "/api/webhook", nil)
|
||||
req.Header.Set("X-GitHub-Event", "push")
|
||||
w := httptest.NewRecorder()
|
||||
h.Handler(w, req)
|
||||
@@ -253,8 +352,8 @@ func TestInvalidMethod(t *testing.T) {
|
||||
|
||||
func TestInvalidEvent(t *testing.T) {
|
||||
hook := test.NewGlobal()
|
||||
h := NewMockHandler(nil)
|
||||
req := httptest.NewRequest("POST", "/api/webhook", nil)
|
||||
h := NewMockHandler(nil, []string{})
|
||||
req := httptest.NewRequest(http.MethodPost, "/api/webhook", nil)
|
||||
req.Header.Set("X-GitHub-Event", "push")
|
||||
w := httptest.NewRecorder()
|
||||
h.Handler(w, req)
|
||||
@@ -267,8 +366,8 @@ func TestInvalidEvent(t *testing.T) {
|
||||
|
||||
func TestUnknownEvent(t *testing.T) {
|
||||
hook := test.NewGlobal()
|
||||
h := NewMockHandler(nil)
|
||||
req := httptest.NewRequest("POST", "/api/webhook", nil)
|
||||
h := NewMockHandler(nil, []string{})
|
||||
req := httptest.NewRequest(http.MethodPost, "/api/webhook", nil)
|
||||
req.Header.Set("X-Unknown-Event", "push")
|
||||
w := httptest.NewRecorder()
|
||||
h.Handler(w, req)
|
||||
|
||||
Reference in New Issue
Block a user