From b4be0099d84778127d13d7efb9ecebf120dd3069 Mon Sep 17 00:00:00 2001 From: Alexandre Gaudreault Date: Thu, 26 Sep 2024 19:13:13 +0000 Subject: [PATCH] fix(extension): add header to support apps-in-any-namespace (#20123) Signed-off-by: Alexandre Gaudreault --- server/extension/extension.go | 16 +++++++++++++--- server/extension/extension_test.go | 6 ++++-- server/server.go | 2 +- 3 files changed, 18 insertions(+), 6 deletions(-) diff --git a/server/extension/extension.go b/server/extension/extension.go index 706dfbbb31abd..57e493b221052 100644 --- a/server/extension/extension.go +++ b/server/extension/extension.go @@ -33,6 +33,12 @@ const ( DefaultIdleConnectionTimeout = 60 * time.Second DefaultMaxIdleConnections = 30 + // HeaderArgoCDNamespace defines the namespace of the + // argo control plane to be passed to the extension handler. + // Example: + // Argocd-Namespace: "namespace" + HeaderArgoCDNamespace = "Argocd-Namespace" + // HeaderArgoCDApplicationName defines the name of the // expected application header to be passed to the extension // handler. The header value must follow the format: @@ -333,6 +339,7 @@ type RbacEnforcer interface { // and handling proxy extensions. type Manager struct { log *log.Entry + namespace string settings SettingsGetter application ApplicationGetter project ProjectGetter @@ -355,9 +362,10 @@ type ExtensionMetricsRegistry interface { } // NewManager will initialize a new manager. -func NewManager(log *log.Entry, sg SettingsGetter, ag ApplicationGetter, pg ProjectGetter, rbac RbacEnforcer, ug UserGetter) *Manager { +func NewManager(log *log.Entry, namespace string, sg SettingsGetter, ag ApplicationGetter, pg ProjectGetter, rbac RbacEnforcer, ug UserGetter) *Manager { return &Manager{ log: log, + namespace: namespace, settings: sg, application: ag, project: pg, @@ -740,7 +748,7 @@ func (m *Manager) CallExtension() func(http.ResponseWriter, *http.Request) { user := m.userGetter.GetUser(r.Context()) groups := m.userGetter.GetGroups(r.Context()) - prepareRequest(r, extName, app, user, groups) + prepareRequest(r, m.namespace, extName, app, user, groups) m.log.Debugf("proxing request for extension %q", extName) // httpsnoop package is used to properly wrap the responseWriter // and avoid optional intefaces issue: @@ -763,11 +771,13 @@ func registerMetrics(extName string, metrics httpsnoop.Metrics, extensionMetrics // the Argo CD extension API section from it. It provides additional information to // the backend service appending them in the outgoing request headers. The appended // headers are: +// - Control plane namespace // - Cluster destination name // - Cluster destination server // - Argo CD authenticated username -func prepareRequest(r *http.Request, extName string, app *v1alpha1.Application, username string, groups []string) { +func prepareRequest(r *http.Request, namespace string, extName string, app *v1alpha1.Application, username string, groups []string) { r.URL.Path = strings.TrimPrefix(r.URL.Path, fmt.Sprintf("%s/%s", URLPrefix, extName)) + r.Header.Set(HeaderArgoCDNamespace, namespace) if app.Spec.Destination.Name != "" { r.Header.Set(HeaderArgoCDTargetClusterName, app.Spec.Destination.Name) } diff --git a/server/extension/extension_test.go b/server/extension/extension_test.go index 300e1e89a490d..e412537eea0cd 100644 --- a/server/extension/extension_test.go +++ b/server/extension/extension_test.go @@ -150,7 +150,7 @@ func TestRegisterExtensions(t *testing.T) { logger, _ := test.NewNullLogger() logEntry := logger.WithContext(context.Background()) - m := extension.NewManager(logEntry, settMock, nil, nil, nil, nil) + m := extension.NewManager(logEntry, "", settMock, nil, nil, nil, nil) return &fixture{ settingsGetterMock: settMock, @@ -248,6 +248,7 @@ func TestCallExtension(t *testing.T) { userMock *mocks.UserGetter manager *extension.Manager } + defaultServerNamespace := "control-plane-ns" defaultProjectName := "project-name" setup := func() *fixture { @@ -260,7 +261,7 @@ func TestCallExtension(t *testing.T) { logger, _ := test.NewNullLogger() logEntry := logger.WithContext(context.Background()) - m := extension.NewManager(logEntry, settMock, appMock, projMock, rbacMock, userMock) + m := extension.NewManager(logEntry, defaultServerNamespace, settMock, appMock, projMock, rbacMock, userMock) m.AddMetricsRegistry(metricsMock) mux := http.NewServeMux() @@ -444,6 +445,7 @@ func TestCallExtension(t *testing.T) { require.NoError(t, err) actual := strings.TrimSuffix(string(body), "\n") assert.Equal(t, backendResponse, actual) + assert.Equal(t, defaultServerNamespace, resp.Header.Get(extension.HeaderArgoCDNamespace)) assert.Equal(t, clusterURL, resp.Header.Get(extension.HeaderArgoCDTargetClusterURL)) assert.Equal(t, "Bearer some-bearer-token", resp.Header.Get("Authorization")) assert.Equal(t, "some-user", resp.Header.Get(extension.HeaderArgoCDUsername)) diff --git a/server/server.go b/server/server.go index 8ad9e451a898d..252d4a9ccf362 100644 --- a/server/server.go +++ b/server/server.go @@ -327,7 +327,7 @@ func NewServer(ctx context.Context, opts ArgoCDServerOpts, appsetOpts Applicatio ag := extension.NewDefaultApplicationGetter(appLister) pg := extension.NewDefaultProjectGetter(projLister, dbInstance) ug := extension.NewDefaultUserGetter(policyEnf) - em := extension.NewManager(logger, sg, ag, pg, enf, ug) + em := extension.NewManager(logger, opts.Namespace, sg, ag, pg, enf, ug) a := &ArgoCDServer{ ArgoCDServerOpts: opts,