Compare commits

...

9 Commits

Author SHA1 Message Date
github-actions[bot]
db93798d66 Bump version to 3.0.6 on release-3.0 branch (#23336)
Signed-off-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
Co-authored-by: crenshaw-dev <350466+crenshaw-dev@users.noreply.github.com>
2025-06-09 17:25:10 -04:00
Michael Crenshaw
fc271d4f24 fix(metrics): populate dest_server label (#23246) (#23269) (#23308)
Signed-off-by: Michael Crenshaw <350466+crenshaw-dev@users.noreply.github.com>
2025-06-06 16:53:06 -04:00
Rouke Broersma
d98a0caf5e chore(repo-server): unify semver resolution in new versions subpackage (#20216) (#23310)
Signed-off-by: Paul Larsen <pnvlarsen@gmail.com>
Signed-off-by: Blake Pettersson <blake.pettersson@gmail.com>
Signed-off-by: Alexandre Gaudreault <alexandre_gaudreault@intuit.com>
Co-authored-by: Paul Larsen <pnvlarsen@gmail.com>
Co-authored-by: Blake Pettersson <blake.pettersson@gmail.com>
Co-authored-by: Alexandre Gaudreault <alexandre_gaudreault@intuit.com>
2025-06-06 15:38:45 -04:00
gcp-cherry-pick-bot[bot]
59d4519c61 fix(tls): validate RSA keys before marshaling (cherry-pick #23295) (#23300)
Signed-off-by: Ville Vesilehto <ville@vesilehto.fi>
Co-authored-by: Ville Vesilehto <ville@vesilehto.fi>
2025-06-06 15:14:19 +02:00
Ville Vesilehto
0ef49b6a5a chore: upgrade Go from 1.24.1 to 1.24.4 (release-3.0) (#23293)
Signed-off-by: Ville Vesilehto <ville@vesilehto.fi>
2025-06-06 12:49:12 +02:00
gcp-cherry-pick-bot[bot]
a31d5c915b fix: parse project with applicationset resource (cherry-pick #23252) (#23267)
Signed-off-by: Blake Pettersson <blake.pettersson@gmail.com>
Co-authored-by: Blake Pettersson <blake.pettersson@gmail.com>
2025-06-04 23:12:07 -10:00
Alexandre Gaudreault
f72ac787b6 fix: Change workloadidentity token cache expiry based on token expiry (#23100) (#23264)
Signed-off-by: Jagpreet Singh Tamber <jagpreetstamber@gmail.com>
Signed-off-by: Alexandre Gaudreault <alexandre_gaudreault@intuit.com>
Co-authored-by: Jagpreet Singh Tamber <jagpreetstamber@gmail.com>
2025-06-04 13:55:14 -04:00
Nitish Kumar
001848ee69 chore: replace heptio-images with argocd-e2e-container (cherry-pick #23040) (#23058)
Signed-off-by: nitishfy <justnitish06@gmail.com>
Signed-off-by: Nitish Kumar <justnitish06@gmail.com>
2025-06-03 07:20:51 +03:00
Michael Crenshaw
5c24b6bd7a fix(server): avoid unecessary claims restrictions (#22973) (3.0) (#23207)
Signed-off-by: Michael Crenshaw <350466+crenshaw-dev@users.noreply.github.com>
2025-05-29 15:08:23 -06:00
110 changed files with 937 additions and 555 deletions

View File

@@ -14,7 +14,7 @@ on:
env:
# Golang version to use across CI steps
# renovate: datasource=golang-version packageName=golang
GOLANG_VERSION: '1.24.1'
GOLANG_VERSION: '1.24.4'
concurrency:
group: ${{ github.workflow }}-${{ github.ref }}

View File

@@ -53,7 +53,7 @@ jobs:
with:
# Note: cannot use env variables to set go-version (https://docs.github.com/en/actions/using-workflows/reusing-workflows#limitations)
# renovate: datasource=golang-version packageName=golang
go-version: 1.24.1
go-version: 1.24.4
platforms: ${{ needs.set-vars.outputs.platforms }}
push: false
@@ -70,7 +70,7 @@ jobs:
ghcr_image_name: ghcr.io/argoproj/argo-cd/argocd:${{ needs.set-vars.outputs.image-tag }}
# Note: cannot use env variables to set go-version (https://docs.github.com/en/actions/using-workflows/reusing-workflows#limitations)
# renovate: datasource=golang-version packageName=golang
go-version: 1.24.1
go-version: 1.24.4
platforms: ${{ needs.set-vars.outputs.platforms }}
push: true
secrets:

View File

@@ -11,7 +11,7 @@ permissions: {}
env:
# renovate: datasource=golang-version packageName=golang
GOLANG_VERSION: '1.24.1' # Note: go-version must also be set in job argocd-image.with.go-version
GOLANG_VERSION: '1.24.4' # Note: go-version must also be set in job argocd-image.with.go-version
jobs:
argocd-image:
@@ -25,7 +25,7 @@ jobs:
quay_image_name: quay.io/argoproj/argocd:${{ github.ref_name }}
# Note: cannot use env variables to set go-version (https://docs.github.com/en/actions/using-workflows/reusing-workflows#limitations)
# renovate: datasource=golang-version packageName=golang
go-version: 1.24.1
go-version: 1.24.4
platforms: linux/amd64,linux/arm64,linux/s390x,linux/ppc64le
push: true
secrets:

View File

@@ -4,7 +4,7 @@ ARG BASE_IMAGE=docker.io/library/ubuntu:24.04@sha256:80dd3c3b9c6cecb9f1667e9290b
# 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.24.1@sha256:c5adecdb7b3f8c5ca3c88648a861882849cc8b02fed68ece31e25de88ad13418 AS builder
FROM docker.io/library/golang:1.24.4@sha256:db5d0afbfb4ab648af2393b92e87eaae9ad5e01132803d80caef91b5752d289c AS builder
WORKDIR /tmp
@@ -103,7 +103,7 @@ RUN HOST_ARCH=$TARGETARCH NODE_ENV='production' NODE_ONLINE_ENV='online' NODE_OP
####################################################################################################
# Argo CD Build stage which performs the actual build of Argo CD binaries
####################################################################################################
FROM --platform=$BUILDPLATFORM docker.io/library/golang:1.24.1@sha256:c5adecdb7b3f8c5ca3c88648a861882849cc8b02fed68ece31e25de88ad13418 AS argocd-build
FROM --platform=$BUILDPLATFORM docker.io/library/golang:1.24.4@sha256:db5d0afbfb4ab648af2393b92e87eaae9ad5e01132803d80caef91b5752d289c AS argocd-build
WORKDIR /go/src/github.com/argoproj/argo-cd

View File

@@ -1 +1 @@
3.0.5
3.0.6

View File

@@ -14,7 +14,7 @@ spec:
app: guestbook-ui
spec:
containers:
- image: gcr.io/heptio-images/ks-guestbook-demo:0.2
- image: quay.io/argoprojlabs/argocd-e2e-container:0.2
name: guestbook-ui
ports:
- containerPort: 80

View File

@@ -14,7 +14,7 @@ spec:
app: guestbook-ui
spec:
containers:
- image: gcr.io/heptio-images/ks-guestbook-demo:0.2
- image: quay.io/argoprojlabs/argocd-e2e-container:0.2
name: guestbook-ui
ports:
- containerPort: 80

View File

@@ -5,7 +5,7 @@
replicaCount: 1
image:
repository: gcr.io/heptio-images/ks-guestbook-demo
repository: quay.io/argoprojlabs/argocd-e2e-container
tag: 0.1
pullPolicy: IfNotPresent

View File

@@ -14,7 +14,7 @@ spec:
app: guestbook-ui
spec:
containers:
- image: gcr.io/heptio-images/ks-guestbook-demo:0.2
- image: quay.io/argoprojlabs/argocd-e2e-container:0.2
name: guestbook-ui
ports:
- containerPort: 80

View File

@@ -14,7 +14,7 @@ spec:
app: guestbook-ui
spec:
containers:
- image: gcr.io/heptio-images/ks-guestbook-demo:0.2
- image: quay.io/argoprojlabs/argocd-e2e-container:0.2
name: guestbook-ui
ports:
- containerPort: 80

View File

@@ -14,7 +14,7 @@ spec:
app: guestbook-ui
spec:
containers:
- image: gcr.io/heptio-images/ks-guestbook-demo:0.2
- image: quay.io/argoprojlabs/argocd-e2e-container:0.2
name: guestbook-ui
ports:
- containerPort: 80

View File

@@ -14,7 +14,7 @@ spec:
app: guestbook-ui
spec:
containers:
- image: gcr.io/heptio-images/ks-guestbook-demo:0.2
- image: quay.io/argoprojlabs/argocd-e2e-container:0.2
name: guestbook-ui
ports:
- containerPort: 80

View File

@@ -14,7 +14,7 @@ spec:
app: guestbook-ui
spec:
containers:
- image: gcr.io/heptio-images/ks-guestbook-demo:0.2
- image: quay.io/argoprojlabs/argocd-e2e-container:0.2
name: guestbook-ui
ports:
- containerPort: 80

View File

@@ -99,7 +99,7 @@ func NewGenAppSpecCommand() *cobra.Command {
argocd admin app generate-spec nginx-ingress --repo https://charts.helm.sh/stable --helm-chart nginx-ingress --revision 1.24.3 --dest-namespace default --dest-server https://kubernetes.default.svc
# Generate declarative config for a Kustomize app
argocd admin app generate-spec kustomize-guestbook --repo https://github.com/argoproj/argocd-example-apps.git --path kustomize-guestbook --dest-namespace default --dest-server https://kubernetes.default.svc --kustomize-image gcr.io/heptio-images/ks-guestbook-demo:0.1
argocd admin app generate-spec kustomize-guestbook --repo https://github.com/argoproj/argocd-example-apps.git --path kustomize-guestbook --dest-namespace default --dest-server https://kubernetes.default.svc --kustomize-image quay.io/argoprojlabs/argocd-e2e-container:0.1
# Generate declarative config for a app using a custom tool:
argocd admin app generate-spec kasane --repo https://github.com/argoproj/argocd-example-apps.git --path plugins/kasane --dest-namespace default --dest-server https://kubernetes.default.svc --config-management-plugin kasane
@@ -389,7 +389,7 @@ func reconcileApplications(
return true
}, func(_ *http.Request) error {
return nil
}, []string{}, []string{})
}, []string{}, []string{}, argoDB)
if err != nil {
return nil, fmt.Errorf("error starting new metrics server: %w", err)
}

View File

@@ -143,7 +143,7 @@ func NewApplicationCreateCommand(clientOpts *argocdclient.ClientOptions) *cobra.
argocd app create nginx-ingress --repo https://charts.helm.sh/stable --helm-chart nginx-ingress --revision 1.24.3 --dest-namespace default --dest-server https://kubernetes.default.svc
# Create a Kustomize app
argocd app create kustomize-guestbook --repo https://github.com/argoproj/argocd-example-apps.git --path kustomize-guestbook --dest-namespace default --dest-server https://kubernetes.default.svc --kustomize-image gcr.io/heptio-images/ks-guestbook-demo:0.1
argocd app create kustomize-guestbook --repo https://github.com/argoproj/argocd-example-apps.git --path kustomize-guestbook --dest-namespace default --dest-server https://kubernetes.default.svc --kustomize-image quay.io/argoprojlabs/argocd-e2e-container:0.1
# Create a MultiSource app while yaml file contains an application with multiple sources
argocd app create guestbook --file <path-to-yaml-file>

View File

@@ -325,7 +325,7 @@ func NewApplicationController(
metricsAddr := fmt.Sprintf("0.0.0.0:%d", metricsPort)
ctrl.metricsServer, err = metrics.NewMetricsServer(metricsAddr, appLister, ctrl.canProcessApp, readinessHealthCheck, metricsApplicationLabels, metricsApplicationConditions)
ctrl.metricsServer, err = metrics.NewMetricsServer(metricsAddr, appLister, ctrl.canProcessApp, readinessHealthCheck, metricsApplicationLabels, metricsApplicationConditions, ctrl.db)
if err != nil {
return nil, err
}
@@ -1575,7 +1575,16 @@ func (ctrl *ApplicationController) setOperationState(app *appv1.Application, sta
messages = append(messages, "failed:", state.Message)
}
ctrl.logAppEvent(context.TODO(), app, eventInfo, strings.Join(messages, " "))
ctrl.metricsServer.IncSync(app, state)
destCluster, err := argo.GetDestinationCluster(context.Background(), app.Spec.Destination, ctrl.db)
if err != nil {
logCtx.Warnf("Unable to get destination cluster, setting dest_server label to empty string in sync metric: %v", err)
}
destServer := ""
if destCluster != nil {
destServer = destCluster.Server
}
ctrl.metricsServer.IncSync(app, destServer, state)
}
}
@@ -1648,9 +1657,17 @@ func (ctrl *ApplicationController) processAppRefreshQueueItem() (processNext boo
startTime := time.Now()
ts := stats.NewTimingStats()
var destCluster *appv1.Cluster
defer func() {
reconcileDuration := time.Since(startTime)
ctrl.metricsServer.IncReconcile(origApp, reconcileDuration)
// We may or may not get to the point in the code where destCluster is set. Populate the dest_server label on a
// best-effort basis.
destServer := ""
if destCluster != nil {
destServer = destCluster.Server
}
ctrl.metricsServer.IncReconcile(origApp, destServer, reconcileDuration)
for k, v := range ts.Timings() {
logCtx = logCtx.WithField(k, v.Milliseconds())
}
@@ -1663,7 +1680,7 @@ func (ctrl *ApplicationController) processAppRefreshQueueItem() (processNext boo
if comparisonLevel == ComparisonWithNothing {
// If the destination cluster is invalid, fallback to the normal reconciliation flow
if destCluster, err := argo.GetDestinationCluster(context.Background(), app.Spec.Destination, ctrl.db); err == nil {
if destCluster, err = argo.GetDestinationCluster(context.Background(), app.Spec.Destination, ctrl.db); err == nil {
managedResources := make([]*appv1.ResourceDiff, 0)
if err := ctrl.cache.GetAppManagedResources(app.InstanceName(ctrl.namespace), &managedResources); err == nil {
var tree *appv1.ApplicationTree
@@ -1700,7 +1717,7 @@ func (ctrl *ApplicationController) processAppRefreshQueueItem() (processNext boo
return
}
destCluster, err := argo.GetDestinationCluster(context.Background(), app.Spec.Destination, ctrl.db)
destCluster, err = argo.GetDestinationCluster(context.Background(), app.Spec.Destination, ctrl.db)
if err != nil {
logCtx.Errorf("Failed to get destination cluster: %v", err)
// exit the reconciliation. ctrl.refreshAppConditions should have caught the error

View File

@@ -21,6 +21,7 @@ import (
"github.com/argoproj/argo-cd/v3/common"
argoappv1 "github.com/argoproj/argo-cd/v3/pkg/apis/application/v1alpha1"
applister "github.com/argoproj/argo-cd/v3/pkg/client/listers/application/v1alpha1"
"github.com/argoproj/argo-cd/v3/util/argo"
"github.com/argoproj/argo-cd/v3/util/db"
"github.com/argoproj/argo-cd/v3/util/git"
"github.com/argoproj/argo-cd/v3/util/healthz"
@@ -149,7 +150,7 @@ var (
)
// NewMetricsServer returns a new prometheus server which collects application metrics
func NewMetricsServer(addr string, appLister applister.ApplicationLister, appFilter func(obj any) bool, healthCheck func(r *http.Request) error, appLabels []string, appConditions []string) (*MetricsServer, error) {
func NewMetricsServer(addr string, appLister applister.ApplicationLister, appFilter func(obj any) bool, healthCheck func(r *http.Request) error, appLabels []string, appConditions []string, db db.ArgoDB) (*MetricsServer, error) {
hostname, err := os.Hostname()
if err != nil {
return nil, err
@@ -175,7 +176,7 @@ func NewMetricsServer(addr string, appLister applister.ApplicationLister, appFil
}
mux := http.NewServeMux()
registry := NewAppRegistry(appLister, appFilter, appLabels, appConditions)
registry := NewAppRegistry(appLister, appFilter, appLabels, appConditions, db)
mux.Handle(MetricsPath, promhttp.HandlerFor(prometheus.Gatherers{
// contains app controller specific metrics
@@ -235,11 +236,11 @@ func (m *MetricsServer) RegisterClustersInfoSource(ctx context.Context, source H
}
// IncSync increments the sync counter for an application
func (m *MetricsServer) IncSync(app *argoappv1.Application, state *argoappv1.OperationState) {
func (m *MetricsServer) IncSync(app *argoappv1.Application, destServer string, state *argoappv1.OperationState) {
if !state.Phase.Completed() {
return
}
m.syncCounter.WithLabelValues(app.Namespace, app.Name, app.Spec.GetProject(), app.Spec.Destination.Server, string(state.Phase)).Inc()
m.syncCounter.WithLabelValues(app.Namespace, app.Name, app.Spec.GetProject(), destServer, string(state.Phase)).Inc()
}
func (m *MetricsServer) IncKubectlExec(command string) {
@@ -293,8 +294,8 @@ func (m *MetricsServer) ObserveResourceEventsProcessingDuration(server string, d
}
// IncReconcile increments the reconcile counter for an application
func (m *MetricsServer) IncReconcile(app *argoappv1.Application, duration time.Duration) {
m.reconcileHistogram.WithLabelValues(app.Namespace, app.Spec.Destination.Server).Observe(duration.Seconds())
func (m *MetricsServer) IncReconcile(app *argoappv1.Application, destServer string, duration time.Duration) {
m.reconcileHistogram.WithLabelValues(app.Namespace, destServer).Observe(duration.Seconds())
}
// HasExpiration return true if expiration is set
@@ -336,22 +337,24 @@ type appCollector struct {
appFilter func(obj any) bool
appLabels []string
appConditions []string
db db.ArgoDB
}
// NewAppCollector returns a prometheus collector for application metrics
func NewAppCollector(appLister applister.ApplicationLister, appFilter func(obj any) bool, appLabels []string, appConditions []string) prometheus.Collector {
func NewAppCollector(appLister applister.ApplicationLister, appFilter func(obj any) bool, appLabels []string, appConditions []string, db db.ArgoDB) prometheus.Collector {
return &appCollector{
store: appLister,
appFilter: appFilter,
appLabels: appLabels,
appConditions: appConditions,
db: db,
}
}
// NewAppRegistry creates a new prometheus registry that collects applications
func NewAppRegistry(appLister applister.ApplicationLister, appFilter func(obj any) bool, appLabels []string, appConditions []string) *prometheus.Registry {
func NewAppRegistry(appLister applister.ApplicationLister, appFilter func(obj any) bool, appLabels []string, appConditions []string, db db.ArgoDB) *prometheus.Registry {
registry := prometheus.NewRegistry()
registry.MustRegister(NewAppCollector(appLister, appFilter, appLabels, appConditions))
registry.MustRegister(NewAppCollector(appLister, appFilter, appLabels, appConditions, db))
return registry
}
@@ -375,7 +378,15 @@ func (c *appCollector) Collect(ch chan<- prometheus.Metric) {
}
for _, app := range apps {
if c.appFilter(app) {
c.collectApps(ch, app)
destCluster, err := argo.GetDestinationCluster(context.Background(), app.Spec.Destination, c.db)
if err != nil {
log.Warnf("Failed to get destination cluster for application %s: %v", app.Name, err)
}
destServer := ""
if destCluster != nil {
destServer = destCluster.Server
}
c.collectApps(ch, app, destServer)
}
}
}
@@ -387,7 +398,7 @@ func boolFloat64(b bool) float64 {
return 0
}
func (c *appCollector) collectApps(ch chan<- prometheus.Metric, app *argoappv1.Application) {
func (c *appCollector) collectApps(ch chan<- prometheus.Metric, app *argoappv1.Application, destServer string) {
addConstMetric := func(desc *prometheus.Desc, t prometheus.ValueType, v float64, lv ...string) {
project := app.Spec.GetProject()
lv = append([]string{app.Namespace, app.Name, project}, lv...)
@@ -414,7 +425,7 @@ func (c *appCollector) collectApps(ch chan<- prometheus.Metric, app *argoappv1.A
autoSyncEnabled := app.Spec.SyncPolicy != nil && app.Spec.SyncPolicy.Automated != nil
addGauge(descAppInfo, 1, strconv.FormatBool(autoSyncEnabled), git.NormalizeGitURL(app.Spec.GetSource().RepoURL), app.Spec.Destination.Server, app.Spec.Destination.Namespace, string(syncStatus), string(healthStatus), operation)
addGauge(descAppInfo, 1, strconv.FormatBool(autoSyncEnabled), git.NormalizeGitURL(app.Spec.GetSource().RepoURL), destServer, app.Spec.Destination.Namespace, string(syncStatus), string(healthStatus), operation)
if len(c.appLabels) > 0 {
labelValues := []string{}

View File

@@ -9,6 +9,10 @@ import (
"testing"
"time"
"github.com/stretchr/testify/mock"
"github.com/argoproj/argo-cd/v3/util/db/mocks"
gitopsCache "github.com/argoproj/gitops-engine/pkg/cache"
"github.com/argoproj/gitops-engine/pkg/sync/common"
"github.com/stretchr/testify/assert"
@@ -40,7 +44,7 @@ metadata:
spec:
destination:
namespace: dummy-namespace
server: https://localhost:6443
name: cluster1
project: important-project
source:
path: some/path
@@ -65,7 +69,7 @@ metadata:
spec:
destination:
namespace: dummy-namespace
server: https://localhost:6443
name: cluster1
project: important-project
source:
path: some/path
@@ -100,7 +104,7 @@ metadata:
spec:
destination:
namespace: dummy-namespace
server: https://localhost:6443
name: cluster1
project: important-project
source:
path: some/path
@@ -129,7 +133,7 @@ metadata:
spec:
destination:
namespace: dummy-namespace
server: https://localhost:6443
name: cluster1
project: important-project
source:
path: some/path
@@ -160,7 +164,7 @@ metadata:
spec:
destination:
namespace: dummy-namespace
server: https://localhost:6443
name: cluster1
source:
path: some/path
repoURL: https://github.com/argoproj/argocd-example-apps.git
@@ -252,7 +256,10 @@ func runTest(t *testing.T, cfg TestMetricServerConfig) {
t.Helper()
cancel, appLister := newFakeLister(cfg.FakeAppYAMLs...)
defer cancel()
metricsServ, err := NewMetricsServer("localhost:8082", appLister, appFilter, noOpHealthCheck, cfg.AppLabels, cfg.AppConditions)
mockDB := mocks.NewArgoDB(t)
mockDB.On("GetClusterServersByName", mock.Anything, "cluster1").Return([]string{"https://localhost:6443"}, nil)
mockDB.On("GetCluster", mock.Anything, "https://localhost:6443").Return(&argoappv1.Cluster{Name: "cluster1", Server: "https://localhost:6443"}, nil)
metricsServ, err := NewMetricsServer("localhost:8082", appLister, appFilter, noOpHealthCheck, cfg.AppLabels, cfg.AppConditions, mockDB)
require.NoError(t, err)
if len(cfg.ClustersInfo) > 0 {
@@ -402,7 +409,8 @@ argocd_app_condition{condition="ExcludedResourceWarning",name="my-app-4",namespa
func TestMetricsSyncCounter(t *testing.T) {
cancel, appLister := newFakeLister()
defer cancel()
metricsServ, err := NewMetricsServer("localhost:8082", appLister, appFilter, noOpHealthCheck, []string{}, []string{})
mockDB := mocks.NewArgoDB(t)
metricsServ, err := NewMetricsServer("localhost:8082", appLister, appFilter, noOpHealthCheck, []string{}, []string{}, mockDB)
require.NoError(t, err)
appSyncTotal := `
@@ -414,11 +422,11 @@ argocd_app_sync_total{dest_server="https://localhost:6443",name="my-app",namespa
`
fakeApp := newFakeApp(fakeApp)
metricsServ.IncSync(fakeApp, &argoappv1.OperationState{Phase: common.OperationRunning})
metricsServ.IncSync(fakeApp, &argoappv1.OperationState{Phase: common.OperationFailed})
metricsServ.IncSync(fakeApp, &argoappv1.OperationState{Phase: common.OperationError})
metricsServ.IncSync(fakeApp, &argoappv1.OperationState{Phase: common.OperationSucceeded})
metricsServ.IncSync(fakeApp, &argoappv1.OperationState{Phase: common.OperationSucceeded})
metricsServ.IncSync(fakeApp, "https://localhost:6443", &argoappv1.OperationState{Phase: common.OperationRunning})
metricsServ.IncSync(fakeApp, "https://localhost:6443", &argoappv1.OperationState{Phase: common.OperationFailed})
metricsServ.IncSync(fakeApp, "https://localhost:6443", &argoappv1.OperationState{Phase: common.OperationError})
metricsServ.IncSync(fakeApp, "https://localhost:6443", &argoappv1.OperationState{Phase: common.OperationSucceeded})
metricsServ.IncSync(fakeApp, "https://localhost:6443", &argoappv1.OperationState{Phase: common.OperationSucceeded})
req, err := http.NewRequest(http.MethodGet, "/metrics", nil)
require.NoError(t, err)
@@ -455,7 +463,8 @@ func assertMetricsNotPrinted(t *testing.T, expectedLines, body string) {
func TestReconcileMetrics(t *testing.T) {
cancel, appLister := newFakeLister()
defer cancel()
metricsServ, err := NewMetricsServer("localhost:8082", appLister, appFilter, noOpHealthCheck, []string{}, []string{})
mockDB := mocks.NewArgoDB(t)
metricsServ, err := NewMetricsServer("localhost:8082", appLister, appFilter, noOpHealthCheck, []string{}, []string{}, mockDB)
require.NoError(t, err)
appReconcileMetrics := `
@@ -473,7 +482,7 @@ argocd_app_reconcile_sum{dest_server="https://localhost:6443",namespace="argocd"
argocd_app_reconcile_count{dest_server="https://localhost:6443",namespace="argocd"} 1
`
fakeApp := newFakeApp(fakeApp)
metricsServ.IncReconcile(fakeApp, 5*time.Second)
metricsServ.IncReconcile(fakeApp, "https://localhost:6443", 5*time.Second)
req, err := http.NewRequest(http.MethodGet, "/metrics", nil)
require.NoError(t, err)
@@ -488,7 +497,8 @@ argocd_app_reconcile_count{dest_server="https://localhost:6443",namespace="argoc
func TestOrphanedResourcesMetric(t *testing.T) {
cancel, appLister := newFakeLister()
defer cancel()
metricsServ, err := NewMetricsServer("localhost:8082", appLister, appFilter, noOpHealthCheck, []string{}, []string{})
mockDB := mocks.NewArgoDB(t)
metricsServ, err := NewMetricsServer("localhost:8082", appLister, appFilter, noOpHealthCheck, []string{}, []string{}, mockDB)
require.NoError(t, err)
expectedMetrics := `
@@ -513,7 +523,8 @@ argocd_app_orphaned_resources_count{name="my-app-4",namespace="argocd",project="
func TestMetricsReset(t *testing.T) {
cancel, appLister := newFakeLister()
defer cancel()
metricsServ, err := NewMetricsServer("localhost:8082", appLister, appFilter, noOpHealthCheck, []string{}, []string{})
mockDB := mocks.NewArgoDB(t)
metricsServ, err := NewMetricsServer("localhost:8082", appLister, appFilter, noOpHealthCheck, []string{}, []string{}, mockDB)
require.NoError(t, err)
appSyncTotal := `
@@ -550,7 +561,8 @@ argocd_app_sync_total{dest_server="https://localhost:6443",name="my-app",namespa
func TestWorkqueueMetrics(t *testing.T) {
cancel, appLister := newFakeLister()
defer cancel()
metricsServ, err := NewMetricsServer("localhost:8082", appLister, appFilter, noOpHealthCheck, []string{}, []string{})
mockDB := mocks.NewArgoDB(t)
metricsServ, err := NewMetricsServer("localhost:8082", appLister, appFilter, noOpHealthCheck, []string{}, []string{}, mockDB)
require.NoError(t, err)
expectedMetrics := `
@@ -585,7 +597,8 @@ workqueue_unfinished_work_seconds{controller="test",name="test"}
func TestGoMetrics(t *testing.T) {
cancel, appLister := newFakeLister()
defer cancel()
metricsServ, err := NewMetricsServer("localhost:8082", appLister, appFilter, noOpHealthCheck, []string{}, []string{})
mockDB := mocks.NewArgoDB(t)
metricsServ, err := NewMetricsServer("localhost:8082", appLister, appFilter, noOpHealthCheck, []string{}, []string{}, mockDB)
require.NoError(t, err)
expectedMetrics := `

View File

@@ -6,7 +6,7 @@ metadata:
deployment.kubernetes.io/revision: '9'
iksm-version: '2.0'
kubectl.kubernetes.io/last-applied-configuration: >
{"apiVersion":"apps/v1","kind":"Deployment","metadata":{"annotations":{"argocd.argoproj.io/tracking-id":"guestbook:apps/Deployment:default/kustomize-guestbook-ui","iksm-version":"2.0"},"name":"kustomize-guestbook-ui","namespace":"default"},"spec":{"replicas":4,"revisionHistoryLimit":3,"selector":{"matchLabels":{"app":"guestbook-ui"}},"template":{"metadata":{"labels":{"app":"guestbook-ui"}},"spec":{"containers":[{"env":[{"name":"SOME_ENV_VAR","value":"some_value"}],"image":"gcr.io/heptio-images/ks-guestbook-demo:0.1","name":"guestbook-ui","ports":[{"containerPort":80}],"resources":{"requests":{"cpu":"50m","memory":"100Mi"}}}]}}}}
{"apiVersion":"apps/v1","kind":"Deployment","metadata":{"annotations":{"argocd.argoproj.io/tracking-id":"guestbook:apps/Deployment:default/kustomize-guestbook-ui","iksm-version":"2.0"},"name":"kustomize-guestbook-ui","namespace":"default"},"spec":{"replicas":4,"revisionHistoryLimit":3,"selector":{"matchLabels":{"app":"guestbook-ui"}},"template":{"metadata":{"labels":{"app":"guestbook-ui"}},"spec":{"containers":[{"env":[{"name":"SOME_ENV_VAR","value":"some_value"}],"image":"quay.io/argoprojlabs/argocd-e2e-container:0.1","name":"guestbook-ui","ports":[{"containerPort":80}],"resources":{"requests":{"cpu":"50m","memory":"100Mi"}}}]}}}}
creationTimestamp: '2022-01-05T15:45:21Z'
generation: 119
managedFields:
@@ -137,7 +137,7 @@ spec:
- env:
- name: SOME_ENV_VAR
value: some_value
image: 'gcr.io/heptio-images/ks-guestbook-demo:0.1'
image: 'quay.io/argoprojlabs/argocd-e2e-container:0.1'
imagePullPolicy: IfNotPresent
name: guestbook-ui
ports:

View File

@@ -6,7 +6,7 @@ metadata:
deployment.kubernetes.io/revision: '9'
iksm-version: '2.0'
kubectl.kubernetes.io/last-applied-configuration: >
{"apiVersion":"apps/v1","kind":"Deployment","metadata":{"annotations":{"argocd.argoproj.io/tracking-id":"guestbook:apps/Deployment:default/kustomize-guestbook-ui","iksm-version":"2.0"},"name":"kustomize-guestbook-ui","namespace":"default"},"spec":{"replicas":4,"revisionHistoryLimit":3,"selector":{"matchLabels":{"app":"guestbook-ui"}},"template":{"metadata":{"labels":{"app":"guestbook-ui"}},"spec":{"containers":[{"env":[{"name":"SOME_ENV_VAR","value":"some_value"}],"image":"gcr.io/heptio-images/ks-guestbook-demo:0.1","name":"guestbook-ui","ports":[{"containerPort":80}],"resources":{"requests":{"cpu":"50m","memory":"100Mi"}}}]}}}}
{"apiVersion":"apps/v1","kind":"Deployment","metadata":{"annotations":{"argocd.argoproj.io/tracking-id":"guestbook:apps/Deployment:default/kustomize-guestbook-ui","iksm-version":"2.0"},"name":"kustomize-guestbook-ui","namespace":"default"},"spec":{"replicas":4,"revisionHistoryLimit":3,"selector":{"matchLabels":{"app":"guestbook-ui"}},"template":{"metadata":{"labels":{"app":"guestbook-ui"}},"spec":{"containers":[{"env":[{"name":"SOME_ENV_VAR","value":"some_value"}],"image":"quay.io/argoprojlabs/argocd-e2e-container:0.1","name":"guestbook-ui","ports":[{"containerPort":80}],"resources":{"requests":{"cpu":"50m","memory":"100Mi"}}}]}}}}
creationTimestamp: '2022-01-05T15:45:21Z'
generation: 119
managedFields:
@@ -137,7 +137,7 @@ spec:
- env:
- name: SOME_ENV_VAR
value: some_value
image: 'gcr.io/heptio-images/ks-guestbook-demo:0.1'
image: 'quay.io/argoprojlabs/argocd-e2e-container:0.1'
imagePullPolicy: IfNotPresent
name: guestbook-ui
ports:

View File

@@ -25,7 +25,7 @@ spec:
value: yet_another_value
- name: SOME_ENV_VAR
value: different_value!
image: 'gcr.io/heptio-images/ks-guestbook-demo:0.1'
image: 'quay.io/argoprojlabs/argocd-e2e-container:0.1'
name: guestbook-ui
ports:
- containerPort: 80

View File

@@ -19,7 +19,7 @@ spec:
spec:
containers:
- name: guestbook-ui
image: 'gcr.io/heptio-images/ks-guestbook-demo:0.1'
image: 'quay.io/argoprojlabs/argocd-e2e-container:0.1'
env:
- name: SOME_ENV_VAR
value: some_value

View File

@@ -21,7 +21,7 @@ spec:
- env:
- name: SOME_ENV_VAR
value: some_value
image: 'gcr.io/heptio-images/ks-guestbook-demo:0.1'
image: 'quay.io/argoprojlabs/argocd-e2e-container:0.1'
name: guestbook-ui
ports:
- containerPort: 80

View File

@@ -127,7 +127,7 @@ spec:
forceCommonLabels: false
forceCommonAnnotations: false
images:
- gcr.io/heptio-images/ks-guestbook-demo:0.2
- quay.io/argoprojlabs/argocd-e2e-container:0.2
- my-app=gcr.io/my-repo/my-app:0.1
namespace: custom-namespace
replicas:

View File

@@ -25,7 +25,7 @@ argocd admin app generate-spec APPNAME [flags]
argocd admin app generate-spec nginx-ingress --repo https://charts.helm.sh/stable --helm-chart nginx-ingress --revision 1.24.3 --dest-namespace default --dest-server https://kubernetes.default.svc
# Generate declarative config for a Kustomize app
argocd admin app generate-spec kustomize-guestbook --repo https://github.com/argoproj/argocd-example-apps.git --path kustomize-guestbook --dest-namespace default --dest-server https://kubernetes.default.svc --kustomize-image gcr.io/heptio-images/ks-guestbook-demo:0.1
argocd admin app generate-spec kustomize-guestbook --repo https://github.com/argoproj/argocd-example-apps.git --path kustomize-guestbook --dest-namespace default --dest-server https://kubernetes.default.svc --kustomize-image quay.io/argoprojlabs/argocd-e2e-container:0.1
# Generate declarative config for a app using a custom tool:
argocd admin app generate-spec kasane --repo https://github.com/argoproj/argocd-example-apps.git --path plugins/kasane --dest-namespace default --dest-server https://kubernetes.default.svc --config-management-plugin kasane

View File

@@ -24,7 +24,7 @@ argocd app create APPNAME [flags]
argocd app create nginx-ingress --repo https://charts.helm.sh/stable --helm-chart nginx-ingress --revision 1.24.3 --dest-namespace default --dest-server https://kubernetes.default.svc
# Create a Kustomize app
argocd app create kustomize-guestbook --repo https://github.com/argoproj/argocd-example-apps.git --path kustomize-guestbook --dest-namespace default --dest-server https://kubernetes.default.svc --kustomize-image gcr.io/heptio-images/ks-guestbook-demo:0.1
argocd app create kustomize-guestbook --repo https://github.com/argoproj/argocd-example-apps.git --path kustomize-guestbook --dest-namespace default --dest-server https://kubernetes.default.svc --kustomize-image quay.io/argoprojlabs/argocd-e2e-container:0.1
# Create a MultiSource app while yaml file contains an application with multiple sources
argocd app create guestbook --file <path-to-yaml-file>

View File

@@ -65,7 +65,7 @@ Example:
```yaml
kustomize:
images:
- gcr.io/heptio-images/ks-guestbook-demo:0.2
- quay.io/argoprojlabs/argocd-e2e-container:0.2
```
The `.argocd-source` is trying to solve two following main use cases:

2
go.mod
View File

@@ -1,6 +1,6 @@
module github.com/argoproj/argo-cd/v3
go 1.24.1
go 1.24.4
require (
code.gitea.io/sdk/gitea v0.20.0

View File

@@ -12,4 +12,4 @@ resources:
images:
- name: quay.io/argoproj/argocd
newName: quay.io/argoproj/argocd
newTag: v3.0.5
newTag: v3.0.6

View File

@@ -5,7 +5,7 @@ kind: Kustomization
images:
- name: quay.io/argoproj/argocd
newName: quay.io/argoproj/argocd
newTag: v3.0.5
newTag: v3.0.6
resources:
- ./application-controller
- ./dex

View File

@@ -24609,7 +24609,7 @@ spec:
key: applicationsetcontroller.requeue.after
name: argocd-cmd-params-cm
optional: true
image: quay.io/argoproj/argocd:v3.0.5
image: quay.io/argoproj/argocd:v3.0.6
imagePullPolicy: Always
name: argocd-applicationset-controller
ports:
@@ -24735,7 +24735,7 @@ spec:
key: log.format.timestamp
name: argocd-cmd-params-cm
optional: true
image: quay.io/argoproj/argocd:v3.0.5
image: quay.io/argoproj/argocd:v3.0.6
imagePullPolicy: Always
livenessProbe:
failureThreshold: 3
@@ -24781,7 +24781,7 @@ spec:
- -n
- /usr/local/bin/argocd
- /var/run/argocd/argocd-cmp-server
image: quay.io/argoproj/argocd:v3.0.5
image: quay.io/argoproj/argocd:v3.0.6
name: copyutil
securityContext:
allowPrivilegeEscalation: false
@@ -24885,7 +24885,7 @@ spec:
- argocd
- admin
- redis-initial-password
image: quay.io/argoproj/argocd:v3.0.5
image: quay.io/argoproj/argocd:v3.0.6
imagePullPolicy: IfNotPresent
name: secret-init
securityContext:
@@ -25158,7 +25158,7 @@ spec:
value: /helm-working-dir
- name: HELM_DATA_HOME
value: /helm-working-dir
image: quay.io/argoproj/argocd:v3.0.5
image: quay.io/argoproj/argocd:v3.0.6
imagePullPolicy: Always
livenessProbe:
failureThreshold: 3
@@ -25210,7 +25210,7 @@ spec:
- -n
- /usr/local/bin/argocd
- /var/run/argocd/argocd-cmp-server
image: quay.io/argoproj/argocd:v3.0.5
image: quay.io/argoproj/argocd:v3.0.6
name: copyutil
securityContext:
allowPrivilegeEscalation: false
@@ -25546,7 +25546,7 @@ spec:
optional: true
- name: KUBECACHEDIR
value: /tmp/kubecache
image: quay.io/argoproj/argocd:v3.0.5
image: quay.io/argoproj/argocd:v3.0.6
imagePullPolicy: Always
name: argocd-application-controller
ports:

View File

@@ -24577,7 +24577,7 @@ spec:
key: applicationsetcontroller.requeue.after
name: argocd-cmd-params-cm
optional: true
image: quay.io/argoproj/argocd:v3.0.5
image: quay.io/argoproj/argocd:v3.0.6
imagePullPolicy: Always
name: argocd-applicationset-controller
ports:
@@ -24697,7 +24697,7 @@ spec:
- argocd
- admin
- redis-initial-password
image: quay.io/argoproj/argocd:v3.0.5
image: quay.io/argoproj/argocd:v3.0.6
imagePullPolicy: IfNotPresent
name: secret-init
securityContext:
@@ -24970,7 +24970,7 @@ spec:
value: /helm-working-dir
- name: HELM_DATA_HOME
value: /helm-working-dir
image: quay.io/argoproj/argocd:v3.0.5
image: quay.io/argoproj/argocd:v3.0.6
imagePullPolicy: Always
livenessProbe:
failureThreshold: 3
@@ -25022,7 +25022,7 @@ spec:
- -n
- /usr/local/bin/argocd
- /var/run/argocd/argocd-cmp-server
image: quay.io/argoproj/argocd:v3.0.5
image: quay.io/argoproj/argocd:v3.0.6
name: copyutil
securityContext:
allowPrivilegeEscalation: false
@@ -25358,7 +25358,7 @@ spec:
optional: true
- name: KUBECACHEDIR
value: /tmp/kubecache
image: quay.io/argoproj/argocd:v3.0.5
image: quay.io/argoproj/argocd:v3.0.6
imagePullPolicy: Always
name: argocd-application-controller
ports:

View File

@@ -12,4 +12,4 @@ resources:
images:
- name: quay.io/argoproj/argocd
newName: quay.io/argoproj/argocd
newTag: v3.0.5
newTag: v3.0.6

View File

@@ -12,7 +12,7 @@ patches:
images:
- name: quay.io/argoproj/argocd
newName: quay.io/argoproj/argocd
newTag: v3.0.5
newTag: v3.0.6
resources:
- ../../base/application-controller
- ../../base/applicationset-controller

View File

@@ -25975,7 +25975,7 @@ spec:
key: applicationsetcontroller.requeue.after
name: argocd-cmd-params-cm
optional: true
image: quay.io/argoproj/argocd:v3.0.5
image: quay.io/argoproj/argocd:v3.0.6
imagePullPolicy: Always
name: argocd-applicationset-controller
ports:
@@ -26101,7 +26101,7 @@ spec:
key: log.format.timestamp
name: argocd-cmd-params-cm
optional: true
image: quay.io/argoproj/argocd:v3.0.5
image: quay.io/argoproj/argocd:v3.0.6
imagePullPolicy: Always
livenessProbe:
failureThreshold: 3
@@ -26147,7 +26147,7 @@ spec:
- -n
- /usr/local/bin/argocd
- /var/run/argocd/argocd-cmp-server
image: quay.io/argoproj/argocd:v3.0.5
image: quay.io/argoproj/argocd:v3.0.6
name: copyutil
securityContext:
allowPrivilegeEscalation: false
@@ -26274,7 +26274,7 @@ spec:
- -n
- /usr/local/bin/argocd
- /shared/argocd-dex
image: quay.io/argoproj/argocd:v3.0.5
image: quay.io/argoproj/argocd:v3.0.6
imagePullPolicy: Always
name: copyutil
securityContext:
@@ -26370,7 +26370,7 @@ spec:
key: notificationscontroller.repo.server.plaintext
name: argocd-cmd-params-cm
optional: true
image: quay.io/argoproj/argocd:v3.0.5
image: quay.io/argoproj/argocd:v3.0.6
imagePullPolicy: Always
livenessProbe:
tcpSocket:
@@ -26494,7 +26494,7 @@ spec:
- argocd
- admin
- redis-initial-password
image: quay.io/argoproj/argocd:v3.0.5
image: quay.io/argoproj/argocd:v3.0.6
imagePullPolicy: IfNotPresent
name: secret-init
securityContext:
@@ -26793,7 +26793,7 @@ spec:
value: /helm-working-dir
- name: HELM_DATA_HOME
value: /helm-working-dir
image: quay.io/argoproj/argocd:v3.0.5
image: quay.io/argoproj/argocd:v3.0.6
imagePullPolicy: Always
livenessProbe:
failureThreshold: 3
@@ -26845,7 +26845,7 @@ spec:
- -n
- /usr/local/bin/argocd
- /var/run/argocd/argocd-cmp-server
image: quay.io/argoproj/argocd:v3.0.5
image: quay.io/argoproj/argocd:v3.0.6
name: copyutil
securityContext:
allowPrivilegeEscalation: false
@@ -27219,7 +27219,7 @@ spec:
key: server.sync.replace.allowed
name: argocd-cmd-params-cm
optional: true
image: quay.io/argoproj/argocd:v3.0.5
image: quay.io/argoproj/argocd:v3.0.6
imagePullPolicy: Always
livenessProbe:
httpGet:
@@ -27591,7 +27591,7 @@ spec:
optional: true
- name: KUBECACHEDIR
value: /tmp/kubecache
image: quay.io/argoproj/argocd:v3.0.5
image: quay.io/argoproj/argocd:v3.0.6
imagePullPolicy: Always
name: argocd-application-controller
ports:

View File

@@ -25945,7 +25945,7 @@ spec:
key: applicationsetcontroller.requeue.after
name: argocd-cmd-params-cm
optional: true
image: quay.io/argoproj/argocd:v3.0.5
image: quay.io/argoproj/argocd:v3.0.6
imagePullPolicy: Always
name: argocd-applicationset-controller
ports:
@@ -26088,7 +26088,7 @@ spec:
- -n
- /usr/local/bin/argocd
- /shared/argocd-dex
image: quay.io/argoproj/argocd:v3.0.5
image: quay.io/argoproj/argocd:v3.0.6
imagePullPolicy: Always
name: copyutil
securityContext:
@@ -26184,7 +26184,7 @@ spec:
key: notificationscontroller.repo.server.plaintext
name: argocd-cmd-params-cm
optional: true
image: quay.io/argoproj/argocd:v3.0.5
image: quay.io/argoproj/argocd:v3.0.6
imagePullPolicy: Always
livenessProbe:
tcpSocket:
@@ -26308,7 +26308,7 @@ spec:
- argocd
- admin
- redis-initial-password
image: quay.io/argoproj/argocd:v3.0.5
image: quay.io/argoproj/argocd:v3.0.6
imagePullPolicy: IfNotPresent
name: secret-init
securityContext:
@@ -26607,7 +26607,7 @@ spec:
value: /helm-working-dir
- name: HELM_DATA_HOME
value: /helm-working-dir
image: quay.io/argoproj/argocd:v3.0.5
image: quay.io/argoproj/argocd:v3.0.6
imagePullPolicy: Always
livenessProbe:
failureThreshold: 3
@@ -26659,7 +26659,7 @@ spec:
- -n
- /usr/local/bin/argocd
- /var/run/argocd/argocd-cmp-server
image: quay.io/argoproj/argocd:v3.0.5
image: quay.io/argoproj/argocd:v3.0.6
name: copyutil
securityContext:
allowPrivilegeEscalation: false
@@ -27033,7 +27033,7 @@ spec:
key: server.sync.replace.allowed
name: argocd-cmd-params-cm
optional: true
image: quay.io/argoproj/argocd:v3.0.5
image: quay.io/argoproj/argocd:v3.0.6
imagePullPolicy: Always
livenessProbe:
httpGet:
@@ -27405,7 +27405,7 @@ spec:
optional: true
- name: KUBECACHEDIR
value: /tmp/kubecache
image: quay.io/argoproj/argocd:v3.0.5
image: quay.io/argoproj/argocd:v3.0.6
imagePullPolicy: Always
name: argocd-application-controller
ports:

View File

@@ -1862,7 +1862,7 @@ spec:
key: applicationsetcontroller.requeue.after
name: argocd-cmd-params-cm
optional: true
image: quay.io/argoproj/argocd:v3.0.5
image: quay.io/argoproj/argocd:v3.0.6
imagePullPolicy: Always
name: argocd-applicationset-controller
ports:
@@ -1988,7 +1988,7 @@ spec:
key: log.format.timestamp
name: argocd-cmd-params-cm
optional: true
image: quay.io/argoproj/argocd:v3.0.5
image: quay.io/argoproj/argocd:v3.0.6
imagePullPolicy: Always
livenessProbe:
failureThreshold: 3
@@ -2034,7 +2034,7 @@ spec:
- -n
- /usr/local/bin/argocd
- /var/run/argocd/argocd-cmp-server
image: quay.io/argoproj/argocd:v3.0.5
image: quay.io/argoproj/argocd:v3.0.6
name: copyutil
securityContext:
allowPrivilegeEscalation: false
@@ -2161,7 +2161,7 @@ spec:
- -n
- /usr/local/bin/argocd
- /shared/argocd-dex
image: quay.io/argoproj/argocd:v3.0.5
image: quay.io/argoproj/argocd:v3.0.6
imagePullPolicy: Always
name: copyutil
securityContext:
@@ -2257,7 +2257,7 @@ spec:
key: notificationscontroller.repo.server.plaintext
name: argocd-cmd-params-cm
optional: true
image: quay.io/argoproj/argocd:v3.0.5
image: quay.io/argoproj/argocd:v3.0.6
imagePullPolicy: Always
livenessProbe:
tcpSocket:
@@ -2381,7 +2381,7 @@ spec:
- argocd
- admin
- redis-initial-password
image: quay.io/argoproj/argocd:v3.0.5
image: quay.io/argoproj/argocd:v3.0.6
imagePullPolicy: IfNotPresent
name: secret-init
securityContext:
@@ -2680,7 +2680,7 @@ spec:
value: /helm-working-dir
- name: HELM_DATA_HOME
value: /helm-working-dir
image: quay.io/argoproj/argocd:v3.0.5
image: quay.io/argoproj/argocd:v3.0.6
imagePullPolicy: Always
livenessProbe:
failureThreshold: 3
@@ -2732,7 +2732,7 @@ spec:
- -n
- /usr/local/bin/argocd
- /var/run/argocd/argocd-cmp-server
image: quay.io/argoproj/argocd:v3.0.5
image: quay.io/argoproj/argocd:v3.0.6
name: copyutil
securityContext:
allowPrivilegeEscalation: false
@@ -3106,7 +3106,7 @@ spec:
key: server.sync.replace.allowed
name: argocd-cmd-params-cm
optional: true
image: quay.io/argoproj/argocd:v3.0.5
image: quay.io/argoproj/argocd:v3.0.6
imagePullPolicy: Always
livenessProbe:
httpGet:
@@ -3478,7 +3478,7 @@ spec:
optional: true
- name: KUBECACHEDIR
value: /tmp/kubecache
image: quay.io/argoproj/argocd:v3.0.5
image: quay.io/argoproj/argocd:v3.0.6
imagePullPolicy: Always
name: argocd-application-controller
ports:

View File

@@ -1832,7 +1832,7 @@ spec:
key: applicationsetcontroller.requeue.after
name: argocd-cmd-params-cm
optional: true
image: quay.io/argoproj/argocd:v3.0.5
image: quay.io/argoproj/argocd:v3.0.6
imagePullPolicy: Always
name: argocd-applicationset-controller
ports:
@@ -1975,7 +1975,7 @@ spec:
- -n
- /usr/local/bin/argocd
- /shared/argocd-dex
image: quay.io/argoproj/argocd:v3.0.5
image: quay.io/argoproj/argocd:v3.0.6
imagePullPolicy: Always
name: copyutil
securityContext:
@@ -2071,7 +2071,7 @@ spec:
key: notificationscontroller.repo.server.plaintext
name: argocd-cmd-params-cm
optional: true
image: quay.io/argoproj/argocd:v3.0.5
image: quay.io/argoproj/argocd:v3.0.6
imagePullPolicy: Always
livenessProbe:
tcpSocket:
@@ -2195,7 +2195,7 @@ spec:
- argocd
- admin
- redis-initial-password
image: quay.io/argoproj/argocd:v3.0.5
image: quay.io/argoproj/argocd:v3.0.6
imagePullPolicy: IfNotPresent
name: secret-init
securityContext:
@@ -2494,7 +2494,7 @@ spec:
value: /helm-working-dir
- name: HELM_DATA_HOME
value: /helm-working-dir
image: quay.io/argoproj/argocd:v3.0.5
image: quay.io/argoproj/argocd:v3.0.6
imagePullPolicy: Always
livenessProbe:
failureThreshold: 3
@@ -2546,7 +2546,7 @@ spec:
- -n
- /usr/local/bin/argocd
- /var/run/argocd/argocd-cmp-server
image: quay.io/argoproj/argocd:v3.0.5
image: quay.io/argoproj/argocd:v3.0.6
name: copyutil
securityContext:
allowPrivilegeEscalation: false
@@ -2920,7 +2920,7 @@ spec:
key: server.sync.replace.allowed
name: argocd-cmd-params-cm
optional: true
image: quay.io/argoproj/argocd:v3.0.5
image: quay.io/argoproj/argocd:v3.0.6
imagePullPolicy: Always
livenessProbe:
httpGet:
@@ -3292,7 +3292,7 @@ spec:
optional: true
- name: KUBECACHEDIR
value: /tmp/kubecache
image: quay.io/argoproj/argocd:v3.0.5
image: quay.io/argoproj/argocd:v3.0.6
imagePullPolicy: Always
name: argocd-application-controller
ports:

View File

@@ -25069,7 +25069,7 @@ spec:
key: applicationsetcontroller.requeue.after
name: argocd-cmd-params-cm
optional: true
image: quay.io/argoproj/argocd:v3.0.5
image: quay.io/argoproj/argocd:v3.0.6
imagePullPolicy: Always
name: argocd-applicationset-controller
ports:
@@ -25195,7 +25195,7 @@ spec:
key: log.format.timestamp
name: argocd-cmd-params-cm
optional: true
image: quay.io/argoproj/argocd:v3.0.5
image: quay.io/argoproj/argocd:v3.0.6
imagePullPolicy: Always
livenessProbe:
failureThreshold: 3
@@ -25241,7 +25241,7 @@ spec:
- -n
- /usr/local/bin/argocd
- /var/run/argocd/argocd-cmp-server
image: quay.io/argoproj/argocd:v3.0.5
image: quay.io/argoproj/argocd:v3.0.6
name: copyutil
securityContext:
allowPrivilegeEscalation: false
@@ -25368,7 +25368,7 @@ spec:
- -n
- /usr/local/bin/argocd
- /shared/argocd-dex
image: quay.io/argoproj/argocd:v3.0.5
image: quay.io/argoproj/argocd:v3.0.6
imagePullPolicy: Always
name: copyutil
securityContext:
@@ -25464,7 +25464,7 @@ spec:
key: notificationscontroller.repo.server.plaintext
name: argocd-cmd-params-cm
optional: true
image: quay.io/argoproj/argocd:v3.0.5
image: quay.io/argoproj/argocd:v3.0.6
imagePullPolicy: Always
livenessProbe:
tcpSocket:
@@ -25566,7 +25566,7 @@ spec:
- argocd
- admin
- redis-initial-password
image: quay.io/argoproj/argocd:v3.0.5
image: quay.io/argoproj/argocd:v3.0.6
imagePullPolicy: IfNotPresent
name: secret-init
securityContext:
@@ -25839,7 +25839,7 @@ spec:
value: /helm-working-dir
- name: HELM_DATA_HOME
value: /helm-working-dir
image: quay.io/argoproj/argocd:v3.0.5
image: quay.io/argoproj/argocd:v3.0.6
imagePullPolicy: Always
livenessProbe:
failureThreshold: 3
@@ -25891,7 +25891,7 @@ spec:
- -n
- /usr/local/bin/argocd
- /var/run/argocd/argocd-cmp-server
image: quay.io/argoproj/argocd:v3.0.5
image: quay.io/argoproj/argocd:v3.0.6
name: copyutil
securityContext:
allowPrivilegeEscalation: false
@@ -26263,7 +26263,7 @@ spec:
key: server.sync.replace.allowed
name: argocd-cmd-params-cm
optional: true
image: quay.io/argoproj/argocd:v3.0.5
image: quay.io/argoproj/argocd:v3.0.6
imagePullPolicy: Always
livenessProbe:
httpGet:
@@ -26635,7 +26635,7 @@ spec:
optional: true
- name: KUBECACHEDIR
value: /tmp/kubecache
image: quay.io/argoproj/argocd:v3.0.5
image: quay.io/argoproj/argocd:v3.0.6
imagePullPolicy: Always
name: argocd-application-controller
ports:

16
manifests/install.yaml generated
View File

@@ -25037,7 +25037,7 @@ spec:
key: applicationsetcontroller.requeue.after
name: argocd-cmd-params-cm
optional: true
image: quay.io/argoproj/argocd:v3.0.5
image: quay.io/argoproj/argocd:v3.0.6
imagePullPolicy: Always
name: argocd-applicationset-controller
ports:
@@ -25180,7 +25180,7 @@ spec:
- -n
- /usr/local/bin/argocd
- /shared/argocd-dex
image: quay.io/argoproj/argocd:v3.0.5
image: quay.io/argoproj/argocd:v3.0.6
imagePullPolicy: Always
name: copyutil
securityContext:
@@ -25276,7 +25276,7 @@ spec:
key: notificationscontroller.repo.server.plaintext
name: argocd-cmd-params-cm
optional: true
image: quay.io/argoproj/argocd:v3.0.5
image: quay.io/argoproj/argocd:v3.0.6
imagePullPolicy: Always
livenessProbe:
tcpSocket:
@@ -25378,7 +25378,7 @@ spec:
- argocd
- admin
- redis-initial-password
image: quay.io/argoproj/argocd:v3.0.5
image: quay.io/argoproj/argocd:v3.0.6
imagePullPolicy: IfNotPresent
name: secret-init
securityContext:
@@ -25651,7 +25651,7 @@ spec:
value: /helm-working-dir
- name: HELM_DATA_HOME
value: /helm-working-dir
image: quay.io/argoproj/argocd:v3.0.5
image: quay.io/argoproj/argocd:v3.0.6
imagePullPolicy: Always
livenessProbe:
failureThreshold: 3
@@ -25703,7 +25703,7 @@ spec:
- -n
- /usr/local/bin/argocd
- /var/run/argocd/argocd-cmp-server
image: quay.io/argoproj/argocd:v3.0.5
image: quay.io/argoproj/argocd:v3.0.6
name: copyutil
securityContext:
allowPrivilegeEscalation: false
@@ -26075,7 +26075,7 @@ spec:
key: server.sync.replace.allowed
name: argocd-cmd-params-cm
optional: true
image: quay.io/argoproj/argocd:v3.0.5
image: quay.io/argoproj/argocd:v3.0.6
imagePullPolicy: Always
livenessProbe:
httpGet:
@@ -26447,7 +26447,7 @@ spec:
optional: true
- name: KUBECACHEDIR
value: /tmp/kubecache
image: quay.io/argoproj/argocd:v3.0.5
image: quay.io/argoproj/argocd:v3.0.6
imagePullPolicy: Always
name: argocd-application-controller
ports:

View File

@@ -956,7 +956,7 @@ spec:
key: applicationsetcontroller.requeue.after
name: argocd-cmd-params-cm
optional: true
image: quay.io/argoproj/argocd:v3.0.5
image: quay.io/argoproj/argocd:v3.0.6
imagePullPolicy: Always
name: argocd-applicationset-controller
ports:
@@ -1082,7 +1082,7 @@ spec:
key: log.format.timestamp
name: argocd-cmd-params-cm
optional: true
image: quay.io/argoproj/argocd:v3.0.5
image: quay.io/argoproj/argocd:v3.0.6
imagePullPolicy: Always
livenessProbe:
failureThreshold: 3
@@ -1128,7 +1128,7 @@ spec:
- -n
- /usr/local/bin/argocd
- /var/run/argocd/argocd-cmp-server
image: quay.io/argoproj/argocd:v3.0.5
image: quay.io/argoproj/argocd:v3.0.6
name: copyutil
securityContext:
allowPrivilegeEscalation: false
@@ -1255,7 +1255,7 @@ spec:
- -n
- /usr/local/bin/argocd
- /shared/argocd-dex
image: quay.io/argoproj/argocd:v3.0.5
image: quay.io/argoproj/argocd:v3.0.6
imagePullPolicy: Always
name: copyutil
securityContext:
@@ -1351,7 +1351,7 @@ spec:
key: notificationscontroller.repo.server.plaintext
name: argocd-cmd-params-cm
optional: true
image: quay.io/argoproj/argocd:v3.0.5
image: quay.io/argoproj/argocd:v3.0.6
imagePullPolicy: Always
livenessProbe:
tcpSocket:
@@ -1453,7 +1453,7 @@ spec:
- argocd
- admin
- redis-initial-password
image: quay.io/argoproj/argocd:v3.0.5
image: quay.io/argoproj/argocd:v3.0.6
imagePullPolicy: IfNotPresent
name: secret-init
securityContext:
@@ -1726,7 +1726,7 @@ spec:
value: /helm-working-dir
- name: HELM_DATA_HOME
value: /helm-working-dir
image: quay.io/argoproj/argocd:v3.0.5
image: quay.io/argoproj/argocd:v3.0.6
imagePullPolicy: Always
livenessProbe:
failureThreshold: 3
@@ -1778,7 +1778,7 @@ spec:
- -n
- /usr/local/bin/argocd
- /var/run/argocd/argocd-cmp-server
image: quay.io/argoproj/argocd:v3.0.5
image: quay.io/argoproj/argocd:v3.0.6
name: copyutil
securityContext:
allowPrivilegeEscalation: false
@@ -2150,7 +2150,7 @@ spec:
key: server.sync.replace.allowed
name: argocd-cmd-params-cm
optional: true
image: quay.io/argoproj/argocd:v3.0.5
image: quay.io/argoproj/argocd:v3.0.6
imagePullPolicy: Always
livenessProbe:
httpGet:
@@ -2522,7 +2522,7 @@ spec:
optional: true
- name: KUBECACHEDIR
value: /tmp/kubecache
image: quay.io/argoproj/argocd:v3.0.5
image: quay.io/argoproj/argocd:v3.0.6
imagePullPolicy: Always
name: argocd-application-controller
ports:

View File

@@ -924,7 +924,7 @@ spec:
key: applicationsetcontroller.requeue.after
name: argocd-cmd-params-cm
optional: true
image: quay.io/argoproj/argocd:v3.0.5
image: quay.io/argoproj/argocd:v3.0.6
imagePullPolicy: Always
name: argocd-applicationset-controller
ports:
@@ -1067,7 +1067,7 @@ spec:
- -n
- /usr/local/bin/argocd
- /shared/argocd-dex
image: quay.io/argoproj/argocd:v3.0.5
image: quay.io/argoproj/argocd:v3.0.6
imagePullPolicy: Always
name: copyutil
securityContext:
@@ -1163,7 +1163,7 @@ spec:
key: notificationscontroller.repo.server.plaintext
name: argocd-cmd-params-cm
optional: true
image: quay.io/argoproj/argocd:v3.0.5
image: quay.io/argoproj/argocd:v3.0.6
imagePullPolicy: Always
livenessProbe:
tcpSocket:
@@ -1265,7 +1265,7 @@ spec:
- argocd
- admin
- redis-initial-password
image: quay.io/argoproj/argocd:v3.0.5
image: quay.io/argoproj/argocd:v3.0.6
imagePullPolicy: IfNotPresent
name: secret-init
securityContext:
@@ -1538,7 +1538,7 @@ spec:
value: /helm-working-dir
- name: HELM_DATA_HOME
value: /helm-working-dir
image: quay.io/argoproj/argocd:v3.0.5
image: quay.io/argoproj/argocd:v3.0.6
imagePullPolicy: Always
livenessProbe:
failureThreshold: 3
@@ -1590,7 +1590,7 @@ spec:
- -n
- /usr/local/bin/argocd
- /var/run/argocd/argocd-cmp-server
image: quay.io/argoproj/argocd:v3.0.5
image: quay.io/argoproj/argocd:v3.0.6
name: copyutil
securityContext:
allowPrivilegeEscalation: false
@@ -1962,7 +1962,7 @@ spec:
key: server.sync.replace.allowed
name: argocd-cmd-params-cm
optional: true
image: quay.io/argoproj/argocd:v3.0.5
image: quay.io/argoproj/argocd:v3.0.6
imagePullPolicy: Always
livenessProbe:
httpGet:
@@ -2334,7 +2334,7 @@ spec:
optional: true
- name: KUBECACHEDIR
value: /tmp/kubecache
image: quay.io/argoproj/argocd:v3.0.5
image: quay.io/argoproj/argocd:v3.0.6
imagePullPolicy: Always
name: argocd-application-controller
ports:

View File

@@ -16,7 +16,6 @@ import (
"strings"
"time"
"github.com/Masterminds/semver/v3"
"github.com/TomOnTime/utfutil"
"github.com/argoproj/gitops-engine/pkg/utils/kube"
textutils "github.com/argoproj/gitops-engine/pkg/utils/text"
@@ -60,6 +59,7 @@ import (
"github.com/argoproj/argo-cd/v3/util/kustomize"
"github.com/argoproj/argo-cd/v3/util/manifeststream"
"github.com/argoproj/argo-cd/v3/util/text"
"github.com/argoproj/argo-cd/v3/util/versions"
)
const (
@@ -2403,40 +2403,37 @@ func (s *Service) newClientResolveRevision(repo *v1alpha1.Repository, revision s
func (s *Service) newHelmClientResolveRevision(repo *v1alpha1.Repository, revision string, chart string, noRevisionCache bool) (helm.Client, string, error) {
enableOCI := repo.EnableOCI || helm.IsHelmOciRepo(repo.Repo)
helmClient := s.newHelmClient(repo.Repo, repo.GetHelmCreds(), enableOCI, repo.Proxy, repo.NoProxy, helm.WithIndexCache(s.cache), helm.WithChartPaths(s.chartPaths))
if helm.IsVersion(revision) {
// Note: This check runs the risk of returning a version which is not found in the helm registry.
if versions.IsVersion(revision) {
return helmClient, revision, nil
}
constraints, err := semver.NewConstraint(revision)
if err != nil {
return nil, "", fmt.Errorf("invalid revision '%s': %w", revision, err)
}
var tags []string
if enableOCI {
tags, err := helmClient.GetTags(chart, noRevisionCache)
var err error
tags, err = helmClient.GetTags(chart, noRevisionCache)
if err != nil {
return nil, "", fmt.Errorf("unable to get tags: %w", err)
}
version, err := tags.MaxVersion(constraints)
} else {
index, err := helmClient.GetIndex(noRevisionCache, s.initConstants.HelmRegistryMaxIndexSize)
if err != nil {
return nil, "", fmt.Errorf("no version for constraints: %w", err)
return nil, "", err
}
return helmClient, version.String(), nil
entries, err := index.GetEntries(chart)
if err != nil {
return nil, "", err
}
tags = entries.Tags()
}
index, err := helmClient.GetIndex(noRevisionCache, s.initConstants.HelmRegistryMaxIndexSize)
maxV, err := versions.MaxVersion(revision, tags)
if err != nil {
return nil, "", err
return nil, "", fmt.Errorf("invalid revision: %w", err)
}
entries, err := index.GetEntries(chart)
if err != nil {
return nil, "", err
}
version, err := entries.MaxVersion(constraints)
if err != nil {
return nil, "", err
}
return helmClient, version.String(), nil
return helmClient, maxV, nil
}
// directoryPermissionInitializer ensures the directory has read/write/execute permissions and returns
@@ -2564,13 +2561,10 @@ func (s *Service) GetHelmCharts(_ context.Context, q *apiclient.HelmChartsReques
}
res := apiclient.HelmChartsResponse{}
for chartName, entries := range index.Entries {
chart := apiclient.HelmChart{
Name: chartName,
}
for _, entry := range entries {
chart.Versions = append(chart.Versions, entry.Version)
}
res.Items = append(res.Items, &chart)
res.Items = append(res.Items, &apiclient.HelmChart{
Name: chartName,
Versions: entries.Tags(),
})
}
return &res, nil
}

View File

@@ -126,6 +126,7 @@ func newServiceWithMocks(t *testing.T, root string, signed bool) (*Service, *git
chart: {{Version: "1.0.0"}, {Version: version}},
oobChart: {{Version: "1.0.0"}, {Version: version}},
}}, nil)
helmClient.On("GetTags", mock.Anything, mock.Anything).Return(nil, nil)
helmClient.On("ExtractChart", chart, version, false, int64(0), false).Return("./testdata/my-chart", io.NopCloser, nil)
helmClient.On("ExtractChart", oobChart, version, false, int64(0), false).Return("./testdata2/out-of-bounds-chart", io.NopCloser, nil)
helmClient.On("CleanChartCache", chart, version).Return(nil)
@@ -1595,7 +1596,7 @@ func TestGetAppDetailsHelm(t *testing.T) {
assert.NotNil(t, res.Helm)
assert.Equal(t, "Helm", res.Type)
assert.EqualValues(t, []string{"values-production.yaml", "values.yaml"}, res.Helm.ValueFiles)
assert.Equal(t, []string{"values-production.yaml", "values.yaml"}, res.Helm.ValueFiles)
}
func TestGetAppDetailsHelmUsesCache(t *testing.T) {
@@ -1612,7 +1613,7 @@ func TestGetAppDetailsHelmUsesCache(t *testing.T) {
assert.NotNil(t, res.Helm)
assert.Equal(t, "Helm", res.Type)
assert.EqualValues(t, []string{"values-production.yaml", "values.yaml"}, res.Helm.ValueFiles)
assert.Equal(t, []string{"values-production.yaml", "values.yaml"}, res.Helm.ValueFiles)
}
func TestGetAppDetailsHelm_WithNoValuesFile(t *testing.T) {
@@ -1630,7 +1631,7 @@ func TestGetAppDetailsHelm_WithNoValuesFile(t *testing.T) {
assert.Equal(t, "Helm", res.Type)
assert.Empty(t, res.Helm.ValueFiles)
assert.Equal(t, "", res.Helm.Values)
assert.Empty(t, res.Helm.Values)
}
func TestGetAppDetailsKustomize(t *testing.T) {
@@ -1647,7 +1648,7 @@ func TestGetAppDetailsKustomize(t *testing.T) {
assert.Equal(t, "Kustomize", res.Type)
assert.NotNil(t, res.Kustomize)
assert.EqualValues(t, []string{"nginx:1.15.4", "registry.k8s.io/nginx-slim:0.8"}, res.Kustomize.Images)
assert.Equal(t, []string{"nginx:1.15.4", "registry.k8s.io/nginx-slim:0.8"}, res.Kustomize.Images)
}
func TestGetHelmCharts(t *testing.T) {
@@ -1664,11 +1665,11 @@ func TestGetHelmCharts(t *testing.T) {
item := res.Items[0]
assert.Equal(t, "my-chart", item.Name)
assert.EqualValues(t, []string{"1.0.0", "1.1.0"}, item.Versions)
assert.Equal(t, []string{"1.0.0", "1.1.0"}, item.Versions)
item2 := res.Items[1]
assert.Equal(t, "out-of-bounds-chart", item2.Name)
assert.EqualValues(t, []string{"1.0.0", "1.1.0"}, item2.Versions)
assert.Equal(t, []string{"1.0.0", "1.1.0"}, item2.Versions)
}
func TestGetRevisionMetadata(t *testing.T) {
@@ -1692,7 +1693,7 @@ func TestGetRevisionMetadata(t *testing.T) {
assert.Equal(t, "test", res.Message)
assert.Equal(t, now, res.Date.Time)
assert.Equal(t, "author", res.Author)
assert.EqualValues(t, []string{"tag1", "tag2"}, res.Tags)
assert.Equal(t, []string{"tag1", "tag2"}, res.Tags)
assert.NotEmpty(t, res.SignatureInfo)
// Check for truncated revision value
@@ -1706,7 +1707,7 @@ func TestGetRevisionMetadata(t *testing.T) {
assert.Equal(t, "test", res.Message)
assert.Equal(t, now, res.Date.Time)
assert.Equal(t, "author", res.Author)
assert.EqualValues(t, []string{"tag1", "tag2"}, res.Tags)
assert.Equal(t, []string{"tag1", "tag2"}, res.Tags)
assert.NotEmpty(t, res.SignatureInfo)
// Cache hit - signature info should not be in result
@@ -1826,12 +1827,12 @@ func TestService_newHelmClientResolveRevision(t *testing.T) {
service := newService(t, ".")
t.Run("EmptyRevision", func(t *testing.T) {
_, _, err := service.newHelmClientResolveRevision(&v1alpha1.Repository{}, "", "", true)
assert.EqualError(t, err, "invalid revision '': improper constraint: ")
_, _, err := service.newHelmClientResolveRevision(&v1alpha1.Repository{}, "", "my-chart", true)
assert.EqualError(t, err, "invalid revision: failed to determine semver constraint: improper constraint: ")
})
t.Run("InvalidRevision", func(t *testing.T) {
_, _, err := service.newHelmClientResolveRevision(&v1alpha1.Repository{}, "???", "", true)
assert.EqualError(t, err, "invalid revision '???': improper constraint: ???", true)
_, _, err := service.newHelmClientResolveRevision(&v1alpha1.Repository{}, "???", "my-chart", true)
assert.EqualError(t, err, "invalid revision: failed to determine semver constraint: improper constraint: ???")
})
}
@@ -1847,7 +1848,7 @@ func TestGetAppDetailsWithAppParameterFile(t *testing.T) {
},
})
require.NoError(t, err)
assert.EqualValues(t, []string{"gcr.io/heptio-images/ks-guestbook-demo:0.2"}, details.Kustomize.Images)
assert.Equal(t, []string{"quay.io/argoprojlabs/argocd-e2e-container:0.2"}, details.Kustomize.Images)
})
})
t.Run("No app specific override", func(t *testing.T) {
@@ -1862,7 +1863,7 @@ func TestGetAppDetailsWithAppParameterFile(t *testing.T) {
AppName: "testapp",
})
require.NoError(t, err)
assert.EqualValues(t, []string{"gcr.io/heptio-images/ks-guestbook-demo:0.2"}, details.Kustomize.Images)
assert.Equal(t, []string{"quay.io/argoprojlabs/argocd-e2e-container:0.2"}, details.Kustomize.Images)
})
})
t.Run("Only app specific override", func(t *testing.T) {
@@ -1877,7 +1878,7 @@ func TestGetAppDetailsWithAppParameterFile(t *testing.T) {
AppName: "testapp",
})
require.NoError(t, err)
assert.EqualValues(t, []string{"gcr.io/heptio-images/ks-guestbook-demo:0.3"}, details.Kustomize.Images)
assert.Equal(t, []string{"quay.io/argoprojlabs/argocd-e2e-container:0.3"}, details.Kustomize.Images)
})
})
t.Run("App specific override", func(t *testing.T) {
@@ -1892,7 +1893,7 @@ func TestGetAppDetailsWithAppParameterFile(t *testing.T) {
AppName: "testapp",
})
require.NoError(t, err)
assert.EqualValues(t, []string{"gcr.io/heptio-images/ks-guestbook-demo:0.3"}, details.Kustomize.Images)
assert.Equal(t, []string{"quay.io/argoprojlabs/argocd-e2e-container:0.3"}, details.Kustomize.Images)
})
})
t.Run("App specific overrides containing non-mergeable field", func(t *testing.T) {
@@ -1907,7 +1908,7 @@ func TestGetAppDetailsWithAppParameterFile(t *testing.T) {
AppName: "unmergeable",
})
require.NoError(t, err)
assert.EqualValues(t, []string{"gcr.io/heptio-images/ks-guestbook-demo:0.3"}, details.Kustomize.Images)
assert.Equal(t, []string{"quay.io/argoprojlabs/argocd-e2e-container:0.3"}, details.Kustomize.Images)
})
})
t.Run("Broken app-specific overrides", func(t *testing.T) {
@@ -1979,7 +1980,7 @@ func TestGenerateManifestsWithAppParameterFile(t *testing.T) {
require.True(t, ok)
image, ok, _ := unstructured.NestedString(containers[0].(map[string]any), "image")
require.True(t, ok)
assert.Equal(t, "gcr.io/heptio-images/ks-guestbook-demo:0.2", image)
assert.Equal(t, "quay.io/argoprojlabs/argocd-e2e-container:0.2", image)
})
})
@@ -2009,7 +2010,7 @@ func TestGenerateManifestsWithAppParameterFile(t *testing.T) {
require.True(t, ok)
image, ok, _ := unstructured.NestedString(containers[0].(map[string]any), "image")
require.True(t, ok)
assert.Equal(t, "gcr.io/heptio-images/ks-guestbook-demo:0.2", image)
assert.Equal(t, "quay.io/argoprojlabs/argocd-e2e-container:0.2", image)
})
})
@@ -2040,7 +2041,7 @@ func TestGenerateManifestsWithAppParameterFile(t *testing.T) {
require.True(t, ok)
image, ok, _ := unstructured.NestedString(containers[0].(map[string]any), "image")
require.True(t, ok)
assert.Equal(t, "gcr.io/heptio-images/ks-guestbook-demo:0.3", image)
assert.Equal(t, "quay.io/argoprojlabs/argocd-e2e-container:0.3", image)
})
})
@@ -2093,7 +2094,7 @@ func TestGenerateManifestsWithAppParameterFile(t *testing.T) {
require.True(t, ok)
image, ok, _ := unstructured.NestedString(containers[0].(map[string]any), "image")
require.True(t, ok)
assert.Equal(t, "gcr.io/heptio-images/ks-guestbook-demo:0.1", image)
assert.Equal(t, "quay.io/argoprojlabs/argocd-e2e-container:0.1", image)
})
})
@@ -4101,7 +4102,7 @@ func TestGetRefs_CacheLockTryLockGitRefCacheError(t *testing.T) {
}
func TestGetRevisionChartDetails(t *testing.T) {
t.Run("Test revision semvar", func(t *testing.T) {
t.Run("Test revision semver", func(t *testing.T) {
root := t.TempDir()
service := newService(t, root)
_, err := service.GetRevisionChartDetails(t.Context(), &apiclient.RepoServerRevisionChartDetailsRequest{

View File

@@ -1,4 +1,4 @@
aloi
kustomize:
images:
- gcr.io/heptio-images/ks-guestbook-demo:0.2
- quay.io/argoprojlabs/argocd-e2e-container:0.2

View File

@@ -1,3 +1,3 @@
kustomize:
images:
- gcr.io/heptio-images/ks-guestbook-demo:0.3
- quay.io/argoprojlabs/argocd-e2e-container:0.3

View File

@@ -1,6 +1,6 @@
repo: https://somewhere
kustomize:
images:
- gcr.io/heptio-images/ks-guestbook-demo:0.3
- quay.io/argoprojlabs/argocd-e2e-container:0.3
invalid:
- I don't know

View File

@@ -1,3 +1,3 @@
kustomize:
images:
- gcr.io/heptio-images/ks-guestbook-demo:0.2
- quay.io/argoprojlabs/argocd-e2e-container:0.2

View File

@@ -12,7 +12,7 @@ spec:
app: guestbook-ui
spec:
containers:
- image: gcr.io/heptio-images/ks-guestbook-demo:0.1
- image: quay.io/argoprojlabs/argocd-e2e-container:0.1
name: guestbook-ui
ports:
- containerPort: 81

View File

@@ -4,5 +4,5 @@ kind: Kustomization
resources:
- guestbook.yaml
images:
- name: gcr.io/heptio-images/ks-guestbook-demo
- name: quay.io/argoprojlabs/argocd-e2e-container
newTag: "0.1"

View File

@@ -1,3 +1,3 @@
kustomize:
images:
- gcr.io/heptio-images/ks-guestbook-demo:0.3
- quay.io/argoprojlabs/argocd-e2e-container:0.3

View File

@@ -12,7 +12,7 @@ spec:
app: guestbook-ui
spec:
containers:
- image: gcr.io/heptio-images/ks-guestbook-demo:0.1
- image: quay.io/argoprojlabs/argocd-e2e-container:0.1
name: guestbook-ui
ports:
- containerPort: 81

View File

@@ -4,5 +4,5 @@ kind: Kustomization
resources:
- guestbook.yaml
images:
- name: gcr.io/heptio-images/ks-guestbook-demo
- name: quay.io/argoprojlabs/argocd-e2e-container
newTag: "0.1"

View File

@@ -12,7 +12,7 @@ spec:
app: guestbook-ui
spec:
containers:
- image: gcr.io/heptio-images/ks-guestbook-demo:{{.Values.image.tag}}
- image: quay.io/argoprojlabs/argocd-e2e-container:{{.Values.image.tag}}
name: guestbook-ui
ports:
- containerPort: 81

View File

@@ -1,3 +1,3 @@
kustomize:
images:
- gcr.io/heptio-images/ks-guestbook-demo:0.2
- quay.io/argoprojlabs/argocd-e2e-container:0.2

View File

@@ -12,7 +12,7 @@ spec:
app: guestbook-ui
spec:
containers:
- image: gcr.io/heptio-images/ks-guestbook-demo:0.1
- image: quay.io/argoprojlabs/argocd-e2e-container:0.1
name: guestbook-ui
ports:
- containerPort: 81

View File

@@ -4,5 +4,5 @@ kind: Kustomization
resources:
- guestbook.yaml
images:
- name: gcr.io/heptio-images/ks-guestbook-demo
- name: quay.io/argoprojlabs/argocd-e2e-container
newTag: "0.1"

View File

@@ -1,6 +1,6 @@
{
containerPort: 80,
image: "gcr.io/heptio-images/ks-guestbook-demo:0.2",
image: "quay.io/argoprojlabs/argocd-e2e-container:0.2",
name: "guestbook-ui",
replicas: 1,
servicePort: 80,

View File

@@ -1,6 +1,6 @@
{
containerPort: 80,
image: "gcr.io/heptio-images/ks-guestbook-demo:0.2",
image: "quay.io/argoprojlabs/argocd-e2e-container:0.2",
name: "guestbook-ui",
replicas: 1,
servicePort: 80,

View File

@@ -28,7 +28,7 @@ spec:
app: guestbook-bluegreen
spec:
containers:
- image: gcr.io/heptio-images/ks-guestbook-demo:0.2
- image: quay.io/argoprojlabs/argocd-e2e-container:0.2
name: guestbook-bluegreen
ports:
- containerPort: 80

View File

@@ -28,7 +28,7 @@ spec:
app: guestbook-bluegreen
spec:
containers:
- image: gcr.io/heptio-images/ks-guestbook-demo:0.2
- image: quay.io/argoprojlabs/argocd-e2e-container:0.2
name: guestbook-bluegreen
ports:
- containerPort: 80

View File

@@ -28,7 +28,7 @@ spec:
app: guestbook-bluegreen
spec:
containers:
- image: gcr.io/heptio-images/ks-guestbook-demo:0.2
- image: quay.io/argoprojlabs/argocd-e2e-container:0.2
name: guestbook-bluegreen
ports:
- containerPort: 80

View File

@@ -29,7 +29,7 @@ spec:
app: guestbook-bluegreen
spec:
containers:
- image: gcr.io/heptio-images/ks-guestbook-demo:0.2
- image: quay.io/argoprojlabs/argocd-e2e-container:0.2
name: guestbook-bluegreen
ports:
- containerPort: 80

View File

@@ -28,7 +28,7 @@ spec:
app: guestbook-bluegreen
spec:
containers:
- image: gcr.io/heptio-images/ks-guestbook-demo:0.2
- image: quay.io/argoprojlabs/argocd-e2e-container:0.2
name: guestbook-bluegreen
ports:
- containerPort: 80

View File

@@ -32,7 +32,7 @@ spec:
app: ks-guestbook-ui
spec:
containers:
- image: gcr.io/heptio-images/ks-guestbook-demo:0.2
- image: quay.io/argoprojlabs/argocd-e2e-container:0.2
name: ks-guestbook-ui
ports:
- containerPort: 80

View File

@@ -31,7 +31,7 @@ spec:
app: ks-guestbook-ui
spec:
containers:
- image: gcr.io/heptio-images/ks-guestbook-demo:0.1
- image: quay.io/argoprojlabs/argocd-e2e-container:0.1
name: ks-guestbook-ui
ports:
- containerPort: 83

View File

@@ -32,7 +32,7 @@ spec:
app: ks-guestbook-ui
spec:
containers:
- image: gcr.io/heptio-images/ks-guestbook-demo:0.1
- image: quay.io/argoprojlabs/argocd-e2e-container:0.1
name: ks-guestbook-ui
ports:
- containerPort: 83

View File

@@ -32,7 +32,7 @@ spec:
app: guestbook-canary
spec:
containers:
- image: 'gcr.io/heptio-images/ks-guestbook-demo:0.2'
- image: 'quay.io/argoprojlabs/argocd-e2e-container:0.2'
name: guestbook-canary
ports:
- containerPort: 80

View File

@@ -3,7 +3,7 @@ kind: Rollout
metadata:
annotations:
kubectl.kubernetes.io/last-applied-configuration: >
{"apiVersion":"argoproj.io/v1alpha1","kind":"Rollout","metadata":{"annotations":{},"labels":{"app.kubernetes.io/instance":"guestbook-canary","ksonnet.io/component":"guestbook-ui"},"name":"guestbook-canary","namespace":"default"},"spec":{"minReadySeconds":10,"replicas":5,"selector":{"matchLabels":{"app":"guestbook-canary"}},"strategy":{"canary":{"maxSurge":1,"maxUnavailable":0,"steps":[{"setWeight":20},{"pause":{"duration":30}},{"setWeight":40},{"pause":{}}]}},"template":{"metadata":{"labels":{"app":"guestbook-canary"}},"spec":{"containers":[{"image":"gcr.io/heptio-images/ks-guestbook-demo:0.1","name":"guestbook-canary","ports":[{"containerPort":80}]}]}}}}
{"apiVersion":"argoproj.io/v1alpha1","kind":"Rollout","metadata":{"annotations":{},"labels":{"app.kubernetes.io/instance":"guestbook-canary","ksonnet.io/component":"guestbook-ui"},"name":"guestbook-canary","namespace":"default"},"spec":{"minReadySeconds":10,"replicas":5,"selector":{"matchLabels":{"app":"guestbook-canary"}},"strategy":{"canary":{"maxSurge":1,"maxUnavailable":0,"steps":[{"setWeight":20},{"pause":{"duration":30}},{"setWeight":40},{"pause":{}}]}},"template":{"metadata":{"labels":{"app":"guestbook-canary"}},"spec":{"containers":[{"image":"quay.io/argoprojlabs/argocd-e2e-container:0.1","name":"guestbook-canary","ports":[{"containerPort":80}]}]}}}}
rollout.argoproj.io/revision: '1'
clusterName: ''
creationTimestamp: '2019-05-01T21:55:30Z'
@@ -39,7 +39,7 @@ spec:
app: guestbook-canary
spec:
containers:
- image: 'gcr.io/heptio-images/ks-guestbook-demo:0.1'
- image: 'quay.io/argoprojlabs/argocd-e2e-container:0.1'
name: guestbook-canary
ports:
- containerPort: 80

View File

@@ -3,7 +3,7 @@ kind: Rollout
metadata:
annotations:
kubectl.kubernetes.io/last-applied-configuration: >
{"apiVersion":"argoproj.io/v1alpha1","kind":"Rollout","metadata":{"annotations":{},"labels":{"app.kubernetes.io/instance":"guestbook-canary","ksonnet.io/component":"guestbook-ui"},"name":"guestbook-canary","namespace":"default"},"spec":{"minReadySeconds":10,"replicas":5,"selector":{"matchLabels":{"app":"guestbook-canary"}},"strategy":{"canary":{"maxSurge":1,"maxUnavailable":0,"steps":[{"setWeight":20},{"pause":{"duration":30}},{"setWeight":40},{"pause":{}}]}},"template":{"metadata":{"labels":{"app":"guestbook-canary"}},"spec":{"containers":[{"image":"gcr.io/heptio-images/ks-guestbook-demo:0.1","name":"guestbook-canary","ports":[{"containerPort":80}]}]}}}}
{"apiVersion":"argoproj.io/v1alpha1","kind":"Rollout","metadata":{"annotations":{},"labels":{"app.kubernetes.io/instance":"guestbook-canary","ksonnet.io/component":"guestbook-ui"},"name":"guestbook-canary","namespace":"default"},"spec":{"minReadySeconds":10,"replicas":5,"selector":{"matchLabels":{"app":"guestbook-canary"}},"strategy":{"canary":{"maxSurge":1,"maxUnavailable":0,"steps":[{"setWeight":20},{"pause":{"duration":30}},{"setWeight":40},{"pause":{}}]}},"template":{"metadata":{"labels":{"app":"guestbook-canary"}},"spec":{"containers":[{"image":"quay.io/argoprojlabs/argocd-e2e-container:0.1","name":"guestbook-canary","ports":[{"containerPort":80}]}]}}}}
rollout.argoproj.io/revision: '1'
clusterName: ''
creationTimestamp: '2019-05-01T21:55:30Z'
@@ -39,7 +39,7 @@ spec:
app: guestbook-canary
spec:
containers:
- image: 'gcr.io/heptio-images/ks-guestbook-demo:0.1'
- image: 'quay.io/argoprojlabs/argocd-e2e-container:0.1'
name: guestbook-canary
ports:
- containerPort: 80

View File

@@ -3,7 +3,7 @@ kind: Rollout
metadata:
annotations:
kubectl.kubernetes.io/last-applied-configuration: >
{"apiVersion":"argoproj.io/v1alpha1","kind":"Rollout","metadata":{"annotations":{},"labels":{"app.kubernetes.io/instance":"guestbook-canary","ksonnet.io/component":"guestbook-ui"},"name":"guestbook-canary","namespace":"default"},"spec":{"minReadySeconds":10,"replicas":5,"selector":{"matchLabels":{"app":"guestbook-canary"}},"strategy":{"canary":{"maxSurge":1,"maxUnavailable":0,"steps":[{"setWeight":20},{"pause":{"duration":30}},{"setWeight":40},{"pause":{}}]}},"template":{"metadata":{"labels":{"app":"guestbook-canary"}},"spec":{"containers":[{"image":"gcr.io/heptio-images/ks-guestbook-demo:0.1","name":"guestbook-canary","ports":[{"containerPort":80}]}]}}}}
{"apiVersion":"argoproj.io/v1alpha1","kind":"Rollout","metadata":{"annotations":{},"labels":{"app.kubernetes.io/instance":"guestbook-canary","ksonnet.io/component":"guestbook-ui"},"name":"guestbook-canary","namespace":"default"},"spec":{"minReadySeconds":10,"replicas":5,"selector":{"matchLabels":{"app":"guestbook-canary"}},"strategy":{"canary":{"maxSurge":1,"maxUnavailable":0,"steps":[{"setWeight":20},{"pause":{"duration":30}},{"setWeight":40},{"pause":{}}]}},"template":{"metadata":{"labels":{"app":"guestbook-canary"}},"spec":{"containers":[{"image":"quay.io/argoprojlabs/argocd-e2e-container:0.1","name":"guestbook-canary","ports":[{"containerPort":80}]}]}}}}
rollout.argoproj.io/revision: '2'
clusterName: ''
creationTimestamp: '2019-05-01T21:55:30Z'
@@ -33,7 +33,7 @@ spec:
app: guestbook-canary
spec:
containers:
- image: 'gcr.io/heptio-images/ks-guestbook-demo:0.2'
- image: 'quay.io/argoprojlabs/argocd-e2e-container:0.2'
name: guestbook-canary
ports:
- containerPort: 80

View File

@@ -3,7 +3,7 @@ kind: Rollout
metadata:
annotations:
kubectl.kubernetes.io/last-applied-configuration: |
{"apiVersion":"argoproj.io/v1alpha1","kind":"Rollout","metadata":{"annotations":{},"name":"example-rollout-canary","namespace":"default"},"spec":{"minReadySeconds":30,"replicas":5,"revisionHistoryLimit":3,"selector":{"matchLabels":{"app":"guestbook"}},"strategy":{"canary":{"steps":[{"setWeight":20},{"pause":{"duration":20}},{"setWeight":40},{"pause":{}}]}},"template":{"metadata":{"labels":{"app":"guestbook"}},"spec":{"containers":[{"image":"gcr.io/heptio-images/ks-guestbook-demo:0.1","name":"guestbook","ports":[{"containerPort":80}]}]}}}}
{"apiVersion":"argoproj.io/v1alpha1","kind":"Rollout","metadata":{"annotations":{},"name":"example-rollout-canary","namespace":"default"},"spec":{"minReadySeconds":30,"replicas":5,"revisionHistoryLimit":3,"selector":{"matchLabels":{"app":"guestbook"}},"strategy":{"canary":{"steps":[{"setWeight":20},{"pause":{"duration":20}},{"setWeight":40},{"pause":{}}]}},"template":{"metadata":{"labels":{"app":"guestbook"}},"spec":{"containers":[{"image":"quay.io/argoprojlabs/argocd-e2e-container:0.1","name":"guestbook","ports":[{"containerPort":80}]}]}}}}
rollout.argoproj.io/revision: "3"
creationTimestamp: "2019-10-20T15:42:26Z"
generation: 101
@@ -28,7 +28,7 @@ spec:
app: guestbook
spec:
containers:
- image: gcr.io/heptio-images/ks-guestbook-demo:0.1
- image: quay.io/argoprojlabs/argocd-e2e-container:0.1
name: guestbook
ports:
- containerPort: 80

View File

@@ -31,7 +31,7 @@ spec:
app: guestbook-canary
spec:
containers:
- image: 'gcr.io/heptio-images/ks-guestbook-demo:0.2'
- image: 'quay.io/argoprojlabs/argocd-e2e-container:0.2'
name: guestbook-canary
ports:
- containerPort: 80

View File

@@ -3,7 +3,7 @@ kind: Rollout
metadata:
annotations:
kubectl.kubernetes.io/last-applied-configuration: |
{"apiVersion":"argoproj.io/v1alpha1","kind":"Rollout","metadata":{"annotations":{},"name":"example-rollout-canary","namespace":"default"},"spec":{"minReadySeconds":30,"replicas":5,"revisionHistoryLimit":3,"selector":{"matchLabels":{"app":"guestbook"}},"strategy":{"canary":{"steps":[{"setWeight":20},{"pause":{"duration":20}},{"setWeight":40},{"pause":{}}]}},"template":{"metadata":{"labels":{"app":"guestbook"}},"spec":{"containers":[{"image":"gcr.io/heptio-images/ks-guestbook-demo:0.1","name":"guestbook","ports":[{"containerPort":80}]}]}}}}
{"apiVersion":"argoproj.io/v1alpha1","kind":"Rollout","metadata":{"annotations":{},"name":"example-rollout-canary","namespace":"default"},"spec":{"minReadySeconds":30,"replicas":5,"revisionHistoryLimit":3,"selector":{"matchLabels":{"app":"guestbook"}},"strategy":{"canary":{"steps":[{"setWeight":20},{"pause":{"duration":20}},{"setWeight":40},{"pause":{}}]}},"template":{"metadata":{"labels":{"app":"guestbook"}},"spec":{"containers":[{"image":"quay.io/argoprojlabs/argocd-e2e-container:0.1","name":"guestbook","ports":[{"containerPort":80}]}]}}}}
rollout.argoproj.io/revision: "2"
clusterName: ""
creationTimestamp: 2019-04-26T20:17:43Z
@@ -35,7 +35,7 @@ spec:
app: guestbook
spec:
containers:
- image: gcr.io/heptio-images/ks-guestbook-demo:0.2
- image: quay.io/argoprojlabs/argocd-e2e-container:0.2
name: guestbook
ports:
- containerPort: 80

View File

@@ -38,7 +38,7 @@ spec:
release: guestbook-bluegreen
spec:
containers:
- image: 'gcr.io/heptio-images/ks-guestbook-demo:0.3'
- image: 'quay.io/argoprojlabs/argocd-e2e-container:0.3'
imagePullPolicy: IfNotPresent
livenessProbe:
httpGet:

View File

@@ -20,7 +20,7 @@ spec:
app: guestbook
spec:
containers:
- image: gcr.io/heptio-images/ks-guestbook-demo:0.2
- image: quay.io/argoprojlabs/argocd-e2e-container:0.2
name: guestbook
status:
HPAReplicas: 5

View File

@@ -24,7 +24,7 @@ spec:
app: guestbook-canary
spec:
containers:
- image: 'gcr.io/heptio-images/ks-guestbook-demo:0.1'
- image: 'quay.io/argoprojlabs/argocd-e2e-container:0.1'
name: guestbook-canary
ports:
- containerPort: 80
@@ -49,6 +49,6 @@ status:
selector: app=guestbook-canary
liveVersionDate: "2022-07-14T07:56:27.000Z"
liveVersionImages:
rollouts-demo: gcr.io/heptio-images/ks-guestbook-demo:0.1
rollouts-demo: quay.io/argoprojlabs/argocd-e2e-container:0.1
phase: Healthy
revision: "9"

View File

@@ -137,7 +137,7 @@ func (p *RBACPolicyEnforcer) getProjectFromRequest(rvals ...any) *v1alpha1.AppPr
if res, ok := rvals[1].(string); ok {
if obj, ok := rvals[3].(string); ok {
switch res {
case rbac.ResourceApplications, rbac.ResourceRepositories, rbac.ResourceClusters, rbac.ResourceLogs, rbac.ResourceExec:
case rbac.ResourceApplicationSets, rbac.ResourceApplications, rbac.ResourceRepositories, rbac.ResourceClusters, rbac.ResourceLogs, rbac.ResourceExec:
if objSplit := strings.Split(obj, "/"); len(objSplit) >= 2 {
return getProjectByName(objSplit[0])
}

View File

@@ -6,6 +6,7 @@ import (
"github.com/golang-jwt/jwt/v5"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/client-go/kubernetes/fake"
@@ -187,11 +188,46 @@ func TestGetScopes_CustomScopes(t *testing.T) {
}
func Test_getProjectFromRequest(t *testing.T) {
fp := newFakeProj()
projLister := test.NewFakeProjLister(fp)
tests := []struct {
name string
resource string
action string
arg string
}{
{
name: "valid project/repo string",
resource: "repositories",
action: "create",
arg: newFakeProj().Name + "/https://github.com/argoproj/argocd-example-apps",
},
{
name: "applicationsets with project/repo string",
resource: "applicationsets",
action: "create",
arg: newFakeProj().Name + "/https://github.com/argoproj/argocd-example-apps",
},
{
name: "applicationsets with project/repo string",
resource: "applicationsets",
action: "*",
arg: newFakeProj().Name + "/https://github.com/argoproj/argocd-example-apps",
},
{
name: "applicationsets with project/repo string",
resource: "applicationsets",
action: "get",
arg: newFakeProj().Name + "/https://github.com/argoproj/argocd-example-apps",
},
}
rbacEnforcer := NewRBACPolicyEnforcer(nil, projLister)
project := rbacEnforcer.getProjectFromRequest("", "repositories", "create", fp.Name+"/https://github.com/argoproj/argocd-example-apps")
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
fp := newFakeProj()
projLister := test.NewFakeProjLister(fp)
rbacEnforcer := NewRBACPolicyEnforcer(nil, projLister)
assert.Equal(t, project.Name, fp.Name)
project := rbacEnforcer.getProjectFromRequest("", tt.resource, tt.action, tt.arg)
require.Equal(t, fp.Name, project.Name)
})
}
}

View File

@@ -724,6 +724,22 @@ func TestGetClaims(t *testing.T) {
UserInfoCacheExpiration: "5m",
},
},
{
test: "GetClaimsWithGroupsString",
claims: jwt.MapClaims{
"aud": common.ArgoCDClientAppID,
"exp": defaultExpiry,
"sub": "randomUser",
"groups": "group1",
},
expectedErrorContains: "",
expectedClaims: jwt.MapClaims{
"aud": common.ArgoCDClientAppID,
"exp": defaultExpiryUnix,
"sub": "randomUser",
"groups": "group1",
},
},
}
for _, testData := range tests {

View File

@@ -8,7 +8,7 @@ RUN ln -s /usr/lib/$(uname -m)-linux-gnu /usr/lib/linux-gnu
# Please make sure to also check the contained yarn version and update the references below when upgrading this image's version
FROM docker.io/library/node:22.9.0@sha256:69e667a79aa41ec0db50bc452a60e705ca16f35285eaf037ebe627a65a5cdf52 AS node
FROM docker.io/library/golang:1.24.1@sha256:c5adecdb7b3f8c5ca3c88648a861882849cc8b02fed68ece31e25de88ad13418 AS golang
FROM docker.io/library/golang:1.24.4@sha256:db5d0afbfb4ab648af2393b92e87eaae9ad5e01132803d80caef91b5752d289c AS golang
FROM docker.io/library/registry:2.8@sha256:543dade69668e02e5768d7ea2b0aa4fae6aa7384c9a5a8dbecc2be5136079ddb AS registry

View File

@@ -1,6 +1,6 @@
function (
containerPort=80,
image="gcr.io/heptio-images/ks-guestbook-demo:0.2",
image="quay.io/argoprojlabs/argocd-e2e-container:0.2",
name="jsonnet-guestbook-ui",
replicas=1,
servicePort=80,

View File

@@ -8,7 +8,7 @@
// Each object below should correspond to a component in the components/ directory
"guestbook-ui": {
containerPort: 80,
image: "gcr.io/heptio-images/ks-guestbook-demo:0.2",
image: "quay.io/argoprojlabs/argocd-e2e-container:0.2",
name: "ks-guestbook-ui",
replicas: 0,
servicePort: 80,

View File

@@ -1,6 +1,6 @@
ARG BASE_IMAGE=docker.io/library/ubuntu:24.04@sha256:80dd3c3b9c6cecb9f1667e9290b3bc61b78c2678c02cbdae5f0fea92cc6734ab
FROM docker.io/library/golang:1.24.1@sha256:c5adecdb7b3f8c5ca3c88648a861882849cc8b02fed68ece31e25de88ad13418 AS go
FROM docker.io/library/golang:1.24.4@sha256:db5d0afbfb4ab648af2393b92e87eaae9ad5e01132803d80caef91b5752d289c AS go
RUN go install github.com/mattn/goreman@latest && \
go install github.com/kisielk/godepgraph@latest

View File

@@ -17,7 +17,7 @@ spec:
app: guestbook-ui
spec:
containers:
- image: 'gcr.io/heptio-images/ks-guestbook-demo:0.1'
- image: 'quay.io/argoprojlabs/argocd-e2e-container:0.1'
name: guestbook-ui
ports:
- containerPort: 80

View File

@@ -4,7 +4,7 @@ metadata:
annotations:
deployment.kubernetes.io/revision: "1"
kubectl.kubernetes.io/last-applied-configuration: |
{"apiVersion":"apps/v1","kind":"Deployment","metadata":{"annotations":{},"labels":{"app.kubernetes.io/instance":"guestbook"},"name":"kustomize-guestbook-ui","namespace":"default"},"spec":{"replicas":1,"revisionHistoryLimit":3,"selector":{"matchLabels":{"app":"guestbook-ui"}},"template":{"metadata":{"labels":{"app":"guestbook-ui"}},"spec":{"containers":[{"image":"gcr.io/heptio-images/ks-guestbook-demo:0.1","name":"guestbook-ui","ports":[{"containerPort":80}]}]}}}}
{"apiVersion":"apps/v1","kind":"Deployment","metadata":{"annotations":{},"labels":{"app.kubernetes.io/instance":"guestbook"},"name":"kustomize-guestbook-ui","namespace":"default"},"spec":{"replicas":1,"revisionHistoryLimit":3,"selector":{"matchLabels":{"app":"guestbook-ui"}},"template":{"metadata":{"labels":{"app":"guestbook-ui"}},"spec":{"containers":[{"image":"quay.io/argoprojlabs/argocd-e2e-container:0.1","name":"guestbook-ui","ports":[{"containerPort":80}]}]}}}}
creationTimestamp: "2021-12-01T20:36:10Z"
generation: 4
labels:
@@ -126,7 +126,7 @@ spec:
app: guestbook-ui
spec:
containers:
- image: gcr.io/heptio-images/ks-guestbook-demo:0.1
- image: quay.io/argoprojlabs/argocd-e2e-container:0.1
imagePullPolicy: IfNotPresent
name: guestbook-ui
ports:

View File

@@ -9,10 +9,9 @@ import (
// ArgoClaims defines the claims structure based on Dex's documented claims
type ArgoClaims struct {
jwt.RegisteredClaims
Email string `json:"email,omitempty"`
EmailVerified bool `json:"email_verified,omitempty"`
Name string `json:"name,omitempty"`
Groups []string `json:"groups,omitempty"`
Email string `json:"email,omitempty"`
EmailVerified bool `json:"email_verified,omitempty"`
Name string `json:"name,omitempty"`
// As per Dex docs, federated_claims has a specific structure
FederatedClaims *FederatedClaims `json:"federated_claims,omitempty"`
}

View File

@@ -134,10 +134,6 @@ func TestMapClaimsToArgoClaims(t *testing.T) {
Email: "email@test.com",
EmailVerified: true,
Name: "the-name",
Groups: []string{
"my-org:my-team2",
"my-org:my-team1",
},
FederatedClaims: &FederatedClaims{
ConnectorID: "my-connector",
UserID: "user-id",

View File

@@ -17,8 +17,6 @@ import (
"syscall"
"time"
"github.com/Masterminds/semver/v3"
argoexec "github.com/argoproj/pkg/exec"
"github.com/bmatcuk/doublestar/v4"
"github.com/go-git/go-git/v5"
@@ -39,6 +37,7 @@ import (
"github.com/argoproj/argo-cd/v3/util/env"
executil "github.com/argoproj/argo-cd/v3/util/exec"
"github.com/argoproj/argo-cd/v3/util/proxy"
"github.com/argoproj/argo-cd/v3/util/versions"
)
var ErrInvalidRepoURL = errors.New("repo URL is invalid")
@@ -645,6 +644,16 @@ func (m *nativeGitClient) LsRemote(revision string) (res string, err error) {
return
}
func getGitTags(refs []*plumbing.Reference) []string {
var tags []string
for _, ref := range refs {
if ref.Name().IsTag() {
tags = append(tags, ref.Name().Short())
}
}
return tags
}
func (m *nativeGitClient) lsRemote(revision string) (string, error) {
if IsCommitSHA(revision) {
return revision, nil
@@ -659,9 +668,9 @@ func (m *nativeGitClient) lsRemote(revision string) (string, error) {
revision = "HEAD"
}
semverSha := m.resolveSemverRevision(revision, refs)
if semverSha != "" {
return semverSha, nil
maxV, err := versions.MaxVersion(revision, getGitTags(refs))
if err == nil {
revision = maxV
}
// refToHash keeps a maps of remote refs to their hash
@@ -710,59 +719,6 @@ func (m *nativeGitClient) lsRemote(revision string) (string, error) {
return "", fmt.Errorf("unable to resolve '%s' to a commit SHA", revision)
}
// resolveSemverRevision is a part of the lsRemote method workflow.
// When the user correctly configures the Git repository revision, and that revision is a valid semver constraint, we
// use this logic path rather than the standard lsRemote revision resolution loop.
// Some examples to illustrate the actual behavior - if the revision is:
// * "v0.1.2"/"0.1.2" or "v0.1"/"0.1", then this is not a constraint, it's a pinned version - so we fall back to the standard tag matching in the lsRemote loop.
// * "v0.1.*"/"0.1.*", and there's a tag matching that constraint, then we find the latest matching version and return its commit hash.
// * "v0.1.*"/"0.1.*", and there is *no* tag matching that constraint, then we fall back to the standard tag matching in the lsRemote loop.
// * "custom-tag", only the lsRemote loop will run - because that revision is an invalid semver;
// * "master-branch", only the lsRemote loop will run because that revision is an invalid semver;
func (m *nativeGitClient) resolveSemverRevision(revision string, refs []*plumbing.Reference) string {
if _, err := semver.NewVersion(revision); err == nil {
// If the revision is a valid version, then we know it isn't a constraint; it's just a pin.
// In which case, we should use standard tag resolution mechanisms.
return ""
}
constraint, err := semver.NewConstraint(revision)
if err != nil {
log.Debugf("Revision '%s' is not a valid semver constraint, skipping semver resolution.", revision)
return ""
}
maxVersion := semver.New(0, 0, 0, "", "")
maxVersionHash := plumbing.ZeroHash
for _, ref := range refs {
if !ref.Name().IsTag() {
continue
}
tag := ref.Name().Short()
version, err := semver.NewVersion(tag)
if err != nil {
log.Debugf("Error parsing version for tag: '%s': %v", tag, err)
// Skip this tag and continue to the next one
continue
}
if constraint.Check(version) {
if version.GreaterThan(maxVersion) {
maxVersion = version
maxVersionHash = ref.Hash()
}
}
}
if maxVersionHash.IsZero() {
return ""
}
log.Debugf("Semver constraint '%s' resolved to tag '%s', at reference '%s'", revision, maxVersion.Original(), maxVersionHash.String())
return maxVersionHash.String()
}
// CommitSHA returns current commit sha from `git rev-parse HEAD`
func (m *nativeGitClient) CommitSHA() (string, error) {
out, err := m.runCmd("rev-parse", "HEAD")

View File

@@ -18,6 +18,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"github.com/argoproj/argo-cd/v3/util/workloadidentity"
"github.com/argoproj/argo-cd/v3/util/workloadidentity/mocks"
)
@@ -314,7 +315,7 @@ func Test_SemverTags(t *testing.T) {
// However, if one specifies the minor/patch versions, semver constraints can be used to match non-semver tags.
// 2024-banana is considered as "2024.0.0-banana" in semver-ish, and banana > apple, so it's a match.
// Note: this is more for documentation and future reference than real testing, as it seems like quite odd behaviour.
name: "semver constraints on non-semver tags",
name: "semver constraints on semver tags",
ref: "> 2024.0.0-apple",
expected: mapTagRefs["2024-banana"],
}} {
@@ -847,7 +848,7 @@ func Test_nativeGitClient_CommitAndPush(t *testing.T) {
func Test_newAuth_AzureWorkloadIdentity(t *testing.T) {
tokenprovider := new(mocks.TokenProvider)
tokenprovider.On("GetToken", azureDevopsEntraResourceId).Return("accessToken", nil)
tokenprovider.On("GetToken", azureDevopsEntraResourceId).Return(&workloadidentity.Token{AccessToken: "accessToken"}, nil)
creds := AzureWorkloadIdentityCreds{store: NoopCredsStore{}, tokenProvider: tokenprovider}

View File

@@ -735,7 +735,7 @@ func (creds AzureWorkloadIdentityCreds) getAccessToken(scope string) (string, er
t, found := azureTokenCache.Get(key)
if found {
return t.(string), nil
return t.(*workloadidentity.Token).AccessToken, nil
}
token, err := creds.tokenProvider.GetToken(scope)
@@ -743,8 +743,11 @@ func (creds AzureWorkloadIdentityCreds) getAccessToken(scope string) (string, er
return "", fmt.Errorf("failed to get Azure access token: %w", err)
}
azureTokenCache.Set(key, token, 2*time.Hour)
return token, nil
cacheExpiry := workloadidentity.CalculateCacheExpiryBasedOnTokenExpiry(token.ExpiresOn)
if cacheExpiry > 0 {
azureTokenCache.Set(key, token, cacheExpiry)
}
return token.AccessToken, nil
}
func (creds AzureWorkloadIdentityCreds) GetAzureDevOpsAccessToken() (string, error) {

View File

@@ -8,8 +8,10 @@ import (
"regexp"
"strings"
"testing"
"time"
"github.com/google/uuid"
gocache "github.com/patrickmn/go-cache"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"golang.org/x/oauth2"
@@ -412,9 +414,10 @@ func TestGoogleCloudCreds_Environ_cleanup(t *testing.T) {
}
func TestAzureWorkloadIdentityCreds_Environ(t *testing.T) {
resetAzureTokenCache()
store := &memoryCredsStore{creds: make(map[string]cred)}
workloadIdentityMock := new(mocks.TokenProvider)
workloadIdentityMock.On("GetToken", azureDevopsEntraResourceId).Return("accessToken", nil)
workloadIdentityMock.On("GetToken", azureDevopsEntraResourceId).Return(&workloadidentity.Token{AccessToken: "accessToken", ExpiresOn: time.Now().Add(time.Minute)}, nil)
creds := AzureWorkloadIdentityCreds{store, workloadIdentityMock}
_, _, err := creds.Environ()
require.NoError(t, err)
@@ -427,9 +430,10 @@ func TestAzureWorkloadIdentityCreds_Environ(t *testing.T) {
}
func TestAzureWorkloadIdentityCreds_Environ_cleanup(t *testing.T) {
resetAzureTokenCache()
store := &memoryCredsStore{creds: make(map[string]cred)}
workloadIdentityMock := new(mocks.TokenProvider)
workloadIdentityMock.On("GetToken", azureDevopsEntraResourceId).Return("accessToken", nil)
workloadIdentityMock.On("GetToken", azureDevopsEntraResourceId).Return(&workloadidentity.Token{AccessToken: "accessToken", ExpiresOn: time.Now().Add(time.Minute)}, nil)
creds := AzureWorkloadIdentityCreds{store, workloadIdentityMock}
closer, _, err := creds.Environ()
require.NoError(t, err)
@@ -439,9 +443,10 @@ func TestAzureWorkloadIdentityCreds_Environ_cleanup(t *testing.T) {
}
func TestAzureWorkloadIdentityCreds_GetUserInfo(t *testing.T) {
resetAzureTokenCache()
store := &memoryCredsStore{creds: make(map[string]cred)}
workloadIdentityMock := new(mocks.TokenProvider)
workloadIdentityMock.On("GetToken", azureDevopsEntraResourceId).Return("accessToken", nil)
workloadIdentityMock.On("GetToken", azureDevopsEntraResourceId).Return(&workloadidentity.Token{AccessToken: "accessToken", ExpiresOn: time.Now().Add(time.Minute)}, nil)
creds := AzureWorkloadIdentityCreds{store, workloadIdentityMock}
user, email, err := creds.GetUserInfo(t.Context())
@@ -456,3 +461,45 @@ func TestGetHelmCredsShouldReturnHelmCredsIfAzureWorkloadIdentityNotSpecified(t
_, ok := creds.(AzureWorkloadIdentityCreds)
require.Truef(t, ok, "expected HelmCreds but got %T", creds)
}
func TestAzureWorkloadIdentityCreds_FetchNewTokenIfExistingIsExpired(t *testing.T) {
resetAzureTokenCache()
store := &memoryCredsStore{creds: make(map[string]cred)}
workloadIdentityMock := new(mocks.TokenProvider)
workloadIdentityMock.On("GetToken", azureDevopsEntraResourceId).
Return(&workloadidentity.Token{AccessToken: "firstToken", ExpiresOn: time.Now().Add(time.Minute)}, nil).Once()
workloadIdentityMock.On("GetToken", azureDevopsEntraResourceId).
Return(&workloadidentity.Token{AccessToken: "secondToken"}, nil).Once()
creds := AzureWorkloadIdentityCreds{store, workloadIdentityMock}
token, err := creds.GetAzureDevOpsAccessToken()
require.NoError(t, err)
assert.Equal(t, "firstToken", token)
time.Sleep(5 * time.Second)
token, err = creds.GetAzureDevOpsAccessToken()
require.NoError(t, err)
assert.Equal(t, "secondToken", token)
}
func TestAzureWorkloadIdentityCreds_ReuseTokenIfExistingIsNotExpired(t *testing.T) {
resetAzureTokenCache()
store := &memoryCredsStore{creds: make(map[string]cred)}
workloadIdentityMock := new(mocks.TokenProvider)
firstToken := &workloadidentity.Token{AccessToken: "firstToken", ExpiresOn: time.Now().Add(6 * time.Minute)}
secondToken := &workloadidentity.Token{AccessToken: "secondToken"}
workloadIdentityMock.On("GetToken", azureDevopsEntraResourceId).Return(firstToken, nil).Once()
workloadIdentityMock.On("GetToken", azureDevopsEntraResourceId).Return(secondToken, nil).Once()
creds := AzureWorkloadIdentityCreds{store, workloadIdentityMock}
token, err := creds.GetAzureDevOpsAccessToken()
require.NoError(t, err)
assert.Equal(t, "firstToken", token)
time.Sleep(5 * time.Second)
token, err = creds.GetAzureDevOpsAccessToken()
require.NoError(t, err)
assert.Equal(t, "firstToken", token)
}
func resetAzureTokenCache() {
azureTokenCache = gocache.New(gocache.NoExpiration, 0)
}

View File

@@ -49,7 +49,7 @@ type Client interface {
CleanChartCache(chart string, version string) error
ExtractChart(chart string, version string, passCredentials bool, manifestMaxExtractedSize int64, disableManifestMaxExtractedSize bool) (string, argoio.Closer, error)
GetIndex(noCache bool, maxIndexSize int64) (*Index, error)
GetTags(chart string, noCache bool) (*TagsList, error)
GetTags(chart string, noCache bool) ([]string, error)
TestHelmOCI() (bool, error)
}
@@ -416,7 +416,7 @@ func getIndexURL(rawURL string) (string, error) {
return repoURL.String(), nil
}
func (c *nativeHelmChart) GetTags(chart string, noCache bool) (*TagsList, error) {
func (c *nativeHelmChart) GetTags(chart string, noCache bool) ([]string, error) {
if !c.enableOci {
return nil, OCINotEnabledErr
}
@@ -432,7 +432,11 @@ func (c *nativeHelmChart) GetTags(chart string, noCache bool) (*TagsList, error)
}
}
tags := &TagsList{}
type entriesStruct struct {
Tags []string
}
entries := &entriesStruct{}
if len(data) == 0 {
start := time.Now()
repo, err := remote.NewRepository(tagsURL)
@@ -479,7 +483,7 @@ func (c *nativeHelmChart) GetTags(chart string, noCache bool) (*TagsList, error)
for _, tag := range tagsResult {
// By convention: Change underscore (_) back to plus (+) to get valid SemVer
convertedTag := strings.ReplaceAll(tag, "_", "+")
tags.Tags = append(tags.Tags, convertedTag)
entries.Tags = append(entries.Tags, convertedTag)
}
return nil
@@ -497,11 +501,11 @@ func (c *nativeHelmChart) GetTags(chart string, noCache bool) (*TagsList, error)
}
}
} else {
err := json.Unmarshal(data, tags)
err := json.Unmarshal(data, entries)
if err != nil {
return nil, fmt.Errorf("failed to decode tags: %w", err)
}
}
return tags, nil
return entries.Tags, nil
}

View File

@@ -18,6 +18,7 @@ import (
"gopkg.in/yaml.v2"
"github.com/argoproj/argo-cd/v3/util/io"
"github.com/argoproj/argo-cd/v3/util/workloadidentity"
"github.com/argoproj/argo-cd/v3/util/workloadidentity/mocks"
)
@@ -25,6 +26,10 @@ type fakeIndexCache struct {
data []byte
}
type fakeTagsList struct {
Tags []string `json:"tags"`
}
func (f *fakeIndexCache) SetHelmIndex(_ string, indexData []byte) error {
f.data = indexData
return nil
@@ -170,19 +175,23 @@ func TestGetTagsFromUrl(t *testing.T) {
t.Run("should return tags correctly while following the link header", func(t *testing.T) {
server := httptest.NewTLSServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
t.Logf("called %s", r.URL.Path)
responseTags := TagsList{}
var responseTags fakeTagsList
w.Header().Set("Content-Type", "application/json")
if !strings.Contains(r.URL.String(), "token") {
w.Header().Set("Link", fmt.Sprintf("<https://%s%s?token=next-token>; rel=next", r.Host, r.URL.Path))
responseTags.Tags = []string{"first"}
responseTags = fakeTagsList{
Tags: []string{"first"},
}
} else {
responseTags.Tags = []string{
"second",
"2.8.0",
"2.8.0-prerelease",
"2.8.0_build",
"2.8.0-prerelease_build",
"2.8.0-prerelease.1_build.1234",
responseTags = fakeTagsList{
Tags: []string{
"second",
"2.8.0",
"2.8.0-prerelease",
"2.8.0_build",
"2.8.0-prerelease_build",
"2.8.0-prerelease.1_build.1234",
},
}
}
w.WriteHeader(http.StatusOK)
@@ -193,7 +202,7 @@ func TestGetTagsFromUrl(t *testing.T) {
tags, err := client.GetTags("mychart", true)
require.NoError(t, err)
assert.ElementsMatch(t, tags.Tags, []string{
assert.ElementsMatch(t, tags, []string{
"first",
"second",
"2.8.0",
@@ -229,7 +238,7 @@ func TestGetTagsFromURLPrivateRepoAuthentication(t *testing.T) {
assert.Equal(t, expectedAuthorization, authorization)
responseTags := TagsList{
responseTags := fakeTagsList{
Tags: []string{
"2.8.0",
"2.8.0-prerelease",
@@ -281,7 +290,7 @@ func TestGetTagsFromURLPrivateRepoAuthentication(t *testing.T) {
tags, err := client.GetTags("mychart", true)
require.NoError(t, err)
assert.ElementsMatch(t, tags.Tags, []string{
assert.ElementsMatch(t, tags, []string{
"2.8.0",
"2.8.0-prerelease",
"2.8.0+build",
@@ -300,7 +309,7 @@ func TestGetTagsFromURLPrivateRepoWithAzureWorkloadIdentityAuthentication(t *tes
}
workloadIdentityMock := new(mocks.TokenProvider)
workloadIdentityMock.On("GetToken", "https://management.core.windows.net/.default").Return("accessToken", nil)
workloadIdentityMock.On("GetToken", "https://management.core.windows.net/.default").Return(&workloadidentity.Token{AccessToken: "accessToken"}, nil)
mockServer := httptest.NewTLSServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
t.Logf("called %s", r.URL.Path)
@@ -326,7 +335,7 @@ func TestGetTagsFromURLPrivateRepoWithAzureWorkloadIdentityAuthentication(t *tes
assert.Equal(t, expectedAuthorization, authorization)
responseTags := TagsList{
responseTags := fakeTagsList{
Tags: []string{
"2.8.0",
"2.8.0-prerelease",
@@ -379,7 +388,7 @@ func TestGetTagsFromURLPrivateRepoWithAzureWorkloadIdentityAuthentication(t *tes
tags, err := client.GetTags("mychart", true)
require.NoError(t, err)
assert.ElementsMatch(t, tags.Tags, []string{
assert.ElementsMatch(t, tags, []string{
"2.8.0",
"2.8.0-prerelease",
"2.8.0+build",
@@ -405,7 +414,7 @@ func TestGetTagsFromURLEnvironmentAuthentication(t *testing.T) {
assert.Equal(t, expectedAuthorization, authorization)
responseTags := TagsList{
responseTags := fakeTagsList{
Tags: []string{
"2.8.0",
"2.8.0-prerelease",
@@ -462,7 +471,7 @@ func TestGetTagsFromURLEnvironmentAuthentication(t *testing.T) {
tags, err := client.GetTags("mychart", true)
require.NoError(t, err)
assert.ElementsMatch(t, tags.Tags, []string{
assert.ElementsMatch(t, tags, []string{
"2.8.0",
"2.8.0-prerelease",
"2.8.0+build",

View File

@@ -11,7 +11,9 @@ import (
"strings"
"time"
"github.com/golang-jwt/jwt/v5"
gocache "github.com/patrickmn/go-cache"
log "github.com/sirupsen/logrus"
argoutils "github.com/argoproj/argo-cd/v3/util"
"github.com/argoproj/argo-cd/v3/util/env"
@@ -146,11 +148,33 @@ func (creds AzureWorkloadIdentityCreds) GetAccessToken() (string, error) {
return "", fmt.Errorf("failed to get Azure access token after challenge: %w", err)
}
// Access token has a lifetime of 3 hours
storeAzureToken(key, token, 2*time.Hour)
tokenExpiry, err := getJWTExpiry(token)
if err != nil {
log.Warnf("failed to get token expiry from JWT: %v, using current time as fallback", err)
tokenExpiry = time.Now()
}
cacheExpiry := workloadidentity.CalculateCacheExpiryBasedOnTokenExpiry(tokenExpiry)
if cacheExpiry > 0 {
storeAzureToken(key, token, cacheExpiry)
}
return token, nil
}
func getJWTExpiry(token string) (time.Time, error) {
parser := jwt.NewParser()
claims := jwt.MapClaims{}
_, _, err := parser.ParseUnverified(token, claims)
if err != nil {
return time.Time{}, fmt.Errorf("failed to parse JWT: %w", err)
}
exp, err := claims.GetExpirationTime()
if err != nil {
return time.Time{}, fmt.Errorf("'exp' claim not found or invalid in token: %w", err)
}
return time.UnixMilli(exp.UnixMilli()), nil
}
func (creds AzureWorkloadIdentityCreds) getAccessTokenAfterChallenge(tokenParams map[string]string) (string, error) {
realm := tokenParams["realm"]
service := tokenParams["service"]
@@ -177,7 +201,7 @@ func (creds AzureWorkloadIdentityCreds) getAccessTokenAfterChallenge(tokenParams
formValues := url.Values{}
formValues.Add("grant_type", "access_token")
formValues.Add("service", service)
formValues.Add("access_token", armAccessToken)
formValues.Add("access_token", armAccessToken.AccessToken)
resp, err := client.PostForm(refreshTokenURL, formValues)
if err != nil {

View File

@@ -7,6 +7,8 @@ import (
"testing"
"time"
"github.com/golang-jwt/jwt/v5"
gocache "github.com/patrickmn/go-cache"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
@@ -79,7 +81,7 @@ func TestGetPasswordShouldGenerateTokenIfNotPresentInCache(t *testing.T) {
defer mockServer.Close()
workloadIdentityMock := new(mocks.TokenProvider)
workloadIdentityMock.On("GetToken", "https://management.core.windows.net/.default").Return("accessToken", nil)
workloadIdentityMock.On("GetToken", "https://management.core.windows.net/.default").Return(&workloadidentity.Token{AccessToken: "accessToken"}, nil)
creds := NewAzureWorkloadIdentityCreds(mockServer.URL[8:], "", nil, nil, true, workloadIdentityMock)
// Retrieve the token from the cache
@@ -191,7 +193,7 @@ func TestGetAccessTokenAfterChallenge_Success(t *testing.T) {
defer mockServer.Close()
workloadIdentityMock := new(mocks.TokenProvider)
workloadIdentityMock.On("GetToken", "https://management.core.windows.net/.default").Return("accessToken", nil)
workloadIdentityMock.On("GetToken", "https://management.core.windows.net/.default").Return(&workloadidentity.Token{AccessToken: "accessToken"}, nil)
creds := NewAzureWorkloadIdentityCreds(mockServer.URL[8:], "", nil, nil, true, workloadIdentityMock)
tokenParams := map[string]string{
@@ -216,7 +218,7 @@ func TestGetAccessTokenAfterChallenge_Failure(t *testing.T) {
// Create an instance of AzureWorkloadIdentityCreds with the mock credential wrapper
workloadIdentityMock := new(mocks.TokenProvider)
workloadIdentityMock.On("GetToken", "https://management.core.windows.net/.default").Return("accessToken", nil)
workloadIdentityMock.On("GetToken", "https://management.core.windows.net/.default").Return(&workloadidentity.Token{AccessToken: "accessToken"}, nil)
creds := NewAzureWorkloadIdentityCreds(mockServer.URL[8:], "", nil, nil, true, workloadIdentityMock)
tokenParams := map[string]string{
@@ -241,7 +243,7 @@ func TestGetAccessTokenAfterChallenge_MalformedResponse(t *testing.T) {
// Create an instance of AzureWorkloadIdentityCreds with the mock credential wrapper
workloadIdentityMock := new(mocks.TokenProvider)
workloadIdentityMock.On("GetToken", "https://management.core.windows.net/.default").Return("accessToken", nil)
workloadIdentityMock.On("GetToken", "https://management.core.windows.net/.default").Return(&workloadidentity.Token{AccessToken: "accessToken"}, nil)
creds := NewAzureWorkloadIdentityCreds(mockServer.URL[8:], "", nil, nil, true, workloadIdentityMock)
tokenParams := map[string]string{
@@ -253,3 +255,125 @@ func TestGetAccessTokenAfterChallenge_MalformedResponse(t *testing.T) {
require.ErrorContains(t, err, "failed to unmarshal response body")
assert.Empty(t, refreshToken)
}
// Helper to generate a mock JWT token with a given expiry time
func generateMockJWT(expiry time.Time) (string, error) {
claims := jwt.MapClaims{
"exp": expiry.Unix(),
}
token := jwt.NewWithClaims(jwt.SigningMethodHS256, claims)
// Use a dummy secret for signing
return token.SignedString([]byte("dummy-secret"))
}
func TestGetAccessToken_FetchNewTokenIfExistingIsExpired(t *testing.T) {
resetAzureTokenCache()
accessToken1, _ := generateMockJWT(time.Now().Add(1 * time.Minute))
accessToken2, _ := generateMockJWT(time.Now().Add(1 * time.Minute))
mockServerURL := ""
mockedServerURL := func() string {
return mockServerURL
}
callCount := 0
mockServer := httptest.NewTLSServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
switch r.URL.Path {
case "/v2/":
assert.Equal(t, "/v2/", r.URL.Path)
w.Header().Set("Www-Authenticate", fmt.Sprintf(`Bearer realm="%s",service="%s"`, mockedServerURL(), mockedServerURL()[8:]))
w.WriteHeader(http.StatusUnauthorized)
case "/oauth2/exchange":
assert.Equal(t, "/oauth2/exchange", r.URL.Path)
var response string
switch callCount {
case 0:
response = fmt.Sprintf(`{"refresh_token": "%s"}`, accessToken1)
case 1:
response = fmt.Sprintf(`{"refresh_token": "%s"}`, accessToken2)
default:
response = `{"refresh_token": "defaultToken"}`
}
callCount++
w.WriteHeader(http.StatusOK)
_, err := w.Write([]byte(response))
require.NoError(t, err)
default:
http.NotFound(w, r)
}
}))
defer mockServer.Close()
mockServerURL = mockServer.URL
workloadIdentityMock := new(mocks.TokenProvider)
workloadIdentityMock.On("GetToken", "https://management.core.windows.net/.default").Return(&workloadidentity.Token{AccessToken: "accessToken"}, nil)
creds := NewAzureWorkloadIdentityCreds(mockServer.URL[8:], "", nil, nil, true, workloadIdentityMock)
refreshToken, err := creds.GetAccessToken()
require.NoError(t, err)
assert.Equal(t, accessToken1, refreshToken)
time.Sleep(5 * time.Second) // Wait for the token to expire
refreshToken, err = creds.GetAccessToken()
require.NoError(t, err)
assert.Equal(t, accessToken2, refreshToken)
}
func TestGetAccessToken_ReuseTokenIfExistingIsNotExpired(t *testing.T) {
resetAzureTokenCache()
accessToken1, _ := generateMockJWT(time.Now().Add(6 * time.Minute))
accessToken2, _ := generateMockJWT(time.Now().Add(1 * time.Minute))
mockServerURL := ""
mockedServerURL := func() string {
return mockServerURL
}
callCount := 0
mockServer := httptest.NewTLSServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
switch r.URL.Path {
case "/v2/":
assert.Equal(t, "/v2/", r.URL.Path)
w.Header().Set("Www-Authenticate", fmt.Sprintf(`Bearer realm="%s",service="%s"`, mockedServerURL(), mockedServerURL()[8:]))
w.WriteHeader(http.StatusUnauthorized)
case "/oauth2/exchange":
assert.Equal(t, "/oauth2/exchange", r.URL.Path)
var response string
switch callCount {
case 0:
response = fmt.Sprintf(`{"refresh_token": "%s"}`, accessToken1)
case 1:
response = fmt.Sprintf(`{"refresh_token": "%s"}`, accessToken2)
default:
response = `{"refresh_token": "defaultToken"}`
}
callCount++
w.WriteHeader(http.StatusOK)
_, err := w.Write([]byte(response))
require.NoError(t, err)
default:
http.NotFound(w, r)
}
}))
defer mockServer.Close()
mockServerURL = mockServer.URL
workloadIdentityMock := new(mocks.TokenProvider)
workloadIdentityMock.On("GetToken", "https://management.core.windows.net/.default").Return(&workloadidentity.Token{AccessToken: "accessToken"}, nil)
creds := NewAzureWorkloadIdentityCreds(mockServer.URL[8:], "", nil, nil, true, workloadIdentityMock)
refreshToken, err := creds.GetAccessToken()
require.NoError(t, err)
assert.Equal(t, accessToken1, refreshToken)
time.Sleep(5 * time.Second) // Wait for the token to expire
refreshToken, err = creds.GetAccessToken()
require.NoError(t, err)
assert.Equal(t, accessToken1, refreshToken)
}
func resetAzureTokenCache() {
azureTokenCache = gocache.New(gocache.NoExpiration, 0)
}

View File

@@ -1,13 +1,8 @@
package helm
import (
"errors"
"fmt"
"time"
log "github.com/sirupsen/logrus"
"github.com/Masterminds/semver/v3"
)
type Entry struct {
@@ -15,6 +10,16 @@ type Entry struct {
Created time.Time
}
type Entries []Entry
func (es Entries) Tags() []string {
tags := make([]string, len(es))
for i, e := range es {
tags[i] = e.Version
}
return tags
}
type Index struct {
Entries map[string]Entries
}
@@ -26,34 +31,3 @@ func (i *Index) GetEntries(chart string) (Entries, error) {
}
return entries, nil
}
type Entries []Entry
func (e Entries) MaxVersion(constraints *semver.Constraints) (*semver.Version, error) {
versions := semver.Collection{}
for _, entry := range e {
v, err := semver.NewVersion(entry.Version)
// Invalid semantic version ignored
if errors.Is(err, semver.ErrInvalidSemVer) {
log.Debugf("Invalid sementic version: %s", entry.Version)
continue
}
if err != nil {
return nil, fmt.Errorf("invalid constraint in index: %w", err)
}
if constraints.Check(v) {
versions = append(versions, v)
}
}
if len(versions) == 0 {
return nil, errors.New("constraint not found in index")
}
maxVersion := versions[0]
for _, v := range versions {
if v.GreaterThan(maxVersion) {
maxVersion = v
}
}
return maxVersion, nil
}

View File

@@ -3,7 +3,6 @@ package helm
import (
"testing"
"github.com/Masterminds/semver/v3"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)
@@ -12,10 +11,10 @@ var index = Index{
Entries: map[string]Entries{
"argo-cd": {
{Version: "~0.7.3"},
{Version: "0.7.2"},
{Version: "0.7.1"},
{Version: "0.5.4"},
{Version: "0.5.3"},
{Version: "0.7.2"},
{Version: "0.5.2"},
{Version: "~0.5.2"},
{Version: "0.5.1"},
@@ -30,53 +29,8 @@ func TestIndex_GetEntries(t *testing.T) {
require.EqualError(t, err, "chart 'foo' not found in index")
})
t.Run("Found", func(t *testing.T) {
entries, err := index.GetEntries("argo-cd")
ts, err := index.GetEntries("argo-cd")
require.NoError(t, err)
assert.Len(t, entries, 9)
})
}
func TestEntries_MaxVersion(t *testing.T) {
entries, _ := index.GetEntries("argo-cd")
t.Run("NotFound", func(t *testing.T) {
constraints, _ := semver.NewConstraint("0.8.1")
_, err := entries.MaxVersion(constraints)
require.EqualError(t, err, "constraint not found in index")
})
t.Run("Exact", func(t *testing.T) {
constraints, _ := semver.NewConstraint("0.5.3")
version, err := entries.MaxVersion(constraints)
require.NoError(t, err)
assert.Equal(t, semver.MustParse("0.5.3"), version)
})
t.Run("Constraint", func(t *testing.T) {
constraints, _ := semver.NewConstraint("> 0.5.3")
version, err := entries.MaxVersion(constraints)
require.NoError(t, err)
assert.Equal(t, semver.MustParse("0.7.2"), version)
})
t.Run("Constraint", func(t *testing.T) {
constraints, _ := semver.NewConstraint("> 0.0.0")
version, err := entries.MaxVersion(constraints)
require.NoError(t, err)
assert.Equal(t, semver.MustParse("0.7.2"), version)
})
t.Run("Constraint", func(t *testing.T) {
constraints, _ := semver.NewConstraint(">0.5.0,<0.7.0")
version, err := entries.MaxVersion(constraints)
require.NoError(t, err)
assert.Equal(t, semver.MustParse("0.5.4"), version)
})
t.Run("Constraint", func(t *testing.T) {
constraints, _ := semver.NewConstraint("0.7.*")
version, err := entries.MaxVersion(constraints)
require.NoError(t, err)
assert.Equal(t, semver.MustParse("0.7.2"), version)
})
t.Run("Constraint", func(t *testing.T) {
constraints, _ := semver.NewConstraint("*")
version, err := entries.MaxVersion(constraints)
require.NoError(t, err)
assert.Equal(t, semver.MustParse("0.7.2"), version)
assert.Len(t, ts, len(index.Entries["argo-cd"]))
})
}

View File

@@ -100,23 +100,23 @@ func (_m *Client) GetIndex(noCache bool, maxIndexSize int64) (*helm.Index, error
}
// GetTags provides a mock function with given fields: chart, noCache
func (_m *Client) GetTags(chart string, noCache bool) (*helm.TagsList, error) {
func (_m *Client) GetTags(chart string, noCache bool) ([]string, error) {
ret := _m.Called(chart, noCache)
if len(ret) == 0 {
panic("no return value specified for GetTags")
}
var r0 *helm.TagsList
var r0 []string
var r1 error
if rf, ok := ret.Get(0).(func(string, bool) (*helm.TagsList, error)); ok {
if rf, ok := ret.Get(0).(func(string, bool) ([]string, error)); ok {
return rf(chart, noCache)
}
if rf, ok := ret.Get(0).(func(string, bool) *helm.TagsList); ok {
if rf, ok := ret.Get(0).(func(string, bool) []string); ok {
r0 = rf(chart, noCache)
} else {
if ret.Get(0) != nil {
r0 = ret.Get(0).(*helm.TagsList)
r0 = ret.Get(0).([]string)
}
}

View File

@@ -1,43 +0,0 @@
package helm
import (
"errors"
"fmt"
log "github.com/sirupsen/logrus"
"github.com/Masterminds/semver/v3"
)
type TagsList struct {
Tags []string
}
func (t TagsList) MaxVersion(constraints *semver.Constraints) (*semver.Version, error) {
versions := semver.Collection{}
for _, tag := range t.Tags {
v, err := semver.NewVersion(tag)
// Invalid semantic version ignored
if errors.Is(err, semver.ErrInvalidSemVer) {
log.Debugf("Invalid semantic version: %s", tag)
continue
}
if err != nil {
return nil, fmt.Errorf("invalid constraint in tags: %w", err)
}
if constraints.Check(v) {
versions = append(versions, v)
}
}
if len(versions) == 0 {
return nil, fmt.Errorf("constraint not found in %v tags", len(t.Tags))
}
maxVersion := versions[0]
for _, v := range versions {
if v.GreaterThan(maxVersion) {
maxVersion = v
}
}
return maxVersion, nil
}

Some files were not shown because too many files have changed in this diff Show More