Skip to content

Commit 6db037a

Browse files
committed
passing logger as a receiver field
On-behalf-of: @SAP [email protected] Signed-off-by: Artem Shcherbatiuk <[email protected]>
1 parent fa139a3 commit 6db037a

File tree

4 files changed

+191
-72
lines changed

4 files changed

+191
-72
lines changed

common/auth/metadata_injector.go

Lines changed: 93 additions & 50 deletions
Original file line numberDiff line numberDiff line change
@@ -28,17 +28,31 @@ type MetadataInjectionConfig struct {
2828
HostOverride string // For virtual workspaces
2929
}
3030

31+
// MetadataInjector provides metadata injection services with structured logging
32+
type MetadataInjector struct {
33+
log *logger.Logger
34+
client client.Client
35+
}
36+
37+
// NewMetadataInjector creates a new MetadataInjector service
38+
func NewMetadataInjector(log *logger.Logger, client client.Client) *MetadataInjector {
39+
return &MetadataInjector{
40+
log: log,
41+
client: client,
42+
}
43+
}
44+
3145
// InjectClusterMetadata injects cluster metadata into schema JSON
3246
// This unified function handles both KCP and ClusterAccess use cases
33-
func InjectClusterMetadata(ctx context.Context, schemaJSON []byte, config MetadataInjectionConfig, k8sClient client.Client, log *logger.Logger) ([]byte, error) {
47+
func (m *MetadataInjector) InjectClusterMetadata(ctx context.Context, schemaJSON []byte, config MetadataInjectionConfig) ([]byte, error) {
3448
// Parse the existing schema JSON
3549
var schemaData map[string]interface{}
3650
if err := json.Unmarshal(schemaJSON, &schemaData); err != nil {
3751
return nil, fmt.Errorf("failed to parse schema JSON: %w", err)
3852
}
3953

4054
// Determine the host to use
41-
host := determineHost(config.Host, config.HostOverride, log)
55+
host := m.determineHost(config.Host, config.HostOverride)
4256

4357
// Create cluster metadata
4458
metadata := map[string]interface{}{
@@ -48,36 +62,36 @@ func InjectClusterMetadata(ctx context.Context, schemaJSON []byte, config Metada
4862

4963
// Add auth data if configured
5064
if config.Auth != nil {
51-
authMetadata, err := extractAuthDataForMetadata(ctx, config.Auth, k8sClient)
65+
authMetadata, err := m.extractAuthDataForMetadata(ctx, config.Auth)
5266
if err != nil {
53-
log.Warn().Err(err).Msg("failed to extract auth data for metadata")
67+
m.log.Warn().Err(err).Msg("failed to extract auth data for metadata")
5468
} else if authMetadata != nil {
5569
metadata["auth"] = authMetadata
5670
}
5771
}
5872

5973
// Add CA data - prefer explicit CA config, fallback to kubeconfig CA
6074
if config.CA != nil {
61-
caData, err := ExtractCAData(ctx, config.CA, k8sClient)
75+
caData, err := ExtractCAData(ctx, config.CA, m.client)
6276
if err != nil {
63-
log.Warn().Err(err).Msg("failed to extract CA data for metadata")
77+
m.log.Warn().Err(err).Msg("failed to extract CA data for metadata")
6478
} else if caData != nil {
6579
metadata["ca"] = map[string]interface{}{
6680
"data": base64.StdEncoding.EncodeToString(caData),
6781
}
6882
}
6983
} else if config.Auth != nil {
70-
tryExtractKubeconfigCA(ctx, config.Auth, k8sClient, metadata, log)
84+
m.tryExtractKubeconfigCA(ctx, config.Auth, metadata)
7185
}
7286

73-
return finalizeSchemaInjection(schemaData, metadata, host, config.Path, config.CA != nil || config.Auth != nil, log)
87+
return m.finalizeSchemaInjection(schemaData, metadata, host, config.Path, config.CA != nil || config.Auth != nil)
7488
}
7589

7690
// InjectKCPMetadataFromEnv injects KCP metadata using kubeconfig from environment
7791
// This is a convenience function for KCP use cases
78-
func InjectKCPMetadataFromEnv(schemaJSON []byte, clusterPath string, log *logger.Logger, hostOverride ...string) ([]byte, error) {
92+
func (m *MetadataInjector) InjectKCPMetadataFromEnv(schemaJSON []byte, clusterPath string, hostOverride ...string) ([]byte, error) {
7993
// Get kubeconfig from environment (same sources as ctrl.GetConfig())
80-
kubeconfigData, kubeconfigHost, err := extractKubeconfigFromEnv(log)
94+
kubeconfigData, kubeconfigHost, err := m.extractKubeconfigFromEnv()
8195
if err != nil {
8296
return nil, fmt.Errorf("failed to extract kubeconfig data: %w", err)
8397
}
@@ -95,7 +109,7 @@ func InjectKCPMetadataFromEnv(schemaJSON []byte, clusterPath string, log *logger
95109
}
96110

97111
// Determine which host to use
98-
host := determineKCPHost(kubeconfigHost, override, clusterPath, log)
112+
host := m.determineKCPHost(kubeconfigHost, override, clusterPath)
99113

100114
// Create cluster metadata with environment kubeconfig
101115
metadata := map[string]interface{}{
@@ -108,40 +122,40 @@ func InjectKCPMetadataFromEnv(schemaJSON []byte, clusterPath string, log *logger
108122
}
109123

110124
// Extract CA data from kubeconfig if available
111-
caData := extractCAFromKubeconfigData(kubeconfigData, log)
125+
caData := m.extractCAFromKubeconfigData(kubeconfigData)
112126
if caData != nil {
113127
metadata["ca"] = map[string]interface{}{
114128
"data": base64.StdEncoding.EncodeToString(caData),
115129
}
116130
}
117131

118-
return finalizeSchemaInjection(schemaData, metadata, host, clusterPath, caData != nil, log)
132+
return m.finalizeSchemaInjection(schemaData, metadata, host, clusterPath, caData != nil)
119133
}
120134

121135
// extractAuthDataForMetadata extracts auth data from AuthConfig for metadata injection
122-
func extractAuthDataForMetadata(ctx context.Context, auth *gatewayv1alpha1.AuthConfig, k8sClient client.Client) (map[string]interface{}, error) {
136+
func (m *MetadataInjector) extractAuthDataForMetadata(ctx context.Context, auth *gatewayv1alpha1.AuthConfig) (map[string]interface{}, error) {
123137
if auth == nil {
124138
return nil, nil
125139
}
126140

127141
if auth.SecretRef != nil {
128-
return extractTokenAuth(ctx, auth.SecretRef, k8sClient)
142+
return m.extractTokenAuth(ctx, auth.SecretRef)
129143
}
130144

131145
if auth.KubeconfigSecretRef != nil {
132-
return extractKubeconfigAuth(ctx, auth.KubeconfigSecretRef, k8sClient)
146+
return m.extractKubeconfigAuth(ctx, auth.KubeconfigSecretRef)
133147
}
134148

135149
if auth.ClientCertificateRef != nil {
136-
return extractClientCertAuth(ctx, auth.ClientCertificateRef, k8sClient)
150+
return m.extractClientCertAuth(ctx, auth.ClientCertificateRef)
137151
}
138152

139153
return nil, nil // No auth configured
140154
}
141155

142156
// extractTokenAuth handles token-based authentication from SecretRef
143-
func extractTokenAuth(ctx context.Context, secretRef *gatewayv1alpha1.SecretRef, k8sClient client.Client) (map[string]interface{}, error) {
144-
secret, err := getSecret(ctx, secretRef.Name, secretRef.Namespace, k8sClient)
157+
func (m *MetadataInjector) extractTokenAuth(ctx context.Context, secretRef *gatewayv1alpha1.SecretRef) (map[string]interface{}, error) {
158+
secret, err := m.getSecret(ctx, secretRef.Name, secretRef.Namespace)
145159
if err != nil {
146160
return nil, fmt.Errorf("failed to get auth secret: %w", err)
147161
}
@@ -158,8 +172,8 @@ func extractTokenAuth(ctx context.Context, secretRef *gatewayv1alpha1.SecretRef,
158172
}
159173

160174
// extractKubeconfigAuth handles kubeconfig-based authentication from KubeconfigSecretRef
161-
func extractKubeconfigAuth(ctx context.Context, kubeconfigRef *gatewayv1alpha1.KubeconfigSecretRef, k8sClient client.Client) (map[string]interface{}, error) {
162-
secret, err := getSecret(ctx, kubeconfigRef.Name, kubeconfigRef.Namespace, k8sClient)
175+
func (m *MetadataInjector) extractKubeconfigAuth(ctx context.Context, kubeconfigRef *gatewayv1alpha1.KubeconfigSecretRef) (map[string]interface{}, error) {
176+
secret, err := m.getSecret(ctx, kubeconfigRef.Name, kubeconfigRef.Namespace)
163177
if err != nil {
164178
return nil, fmt.Errorf("failed to get kubeconfig secret: %w", err)
165179
}
@@ -176,8 +190,8 @@ func extractKubeconfigAuth(ctx context.Context, kubeconfigRef *gatewayv1alpha1.K
176190
}
177191

178192
// extractClientCertAuth handles client certificate authentication from ClientCertificateRef
179-
func extractClientCertAuth(ctx context.Context, certRef *gatewayv1alpha1.ClientCertificateRef, k8sClient client.Client) (map[string]interface{}, error) {
180-
secret, err := getSecret(ctx, certRef.Name, certRef.Namespace, k8sClient)
193+
func (m *MetadataInjector) extractClientCertAuth(ctx context.Context, certRef *gatewayv1alpha1.ClientCertificateRef) (map[string]interface{}, error) {
194+
secret, err := m.getSecret(ctx, certRef.Name, certRef.Namespace)
181195
if err != nil {
182196
return nil, fmt.Errorf("failed to get client certificate secret: %w", err)
183197
}
@@ -197,13 +211,13 @@ func extractClientCertAuth(ctx context.Context, certRef *gatewayv1alpha1.ClientC
197211
}
198212

199213
// getSecret is a helper function to retrieve secrets with namespace defaulting
200-
func getSecret(ctx context.Context, name, namespace string, k8sClient client.Client) (*corev1.Secret, error) {
214+
func (m *MetadataInjector) getSecret(ctx context.Context, name, namespace string) (*corev1.Secret, error) {
201215
if namespace == "" {
202216
namespace = "default"
203217
}
204218

205219
secret := &corev1.Secret{}
206-
err := k8sClient.Get(ctx, types.NamespacedName{
220+
err := m.client.Get(ctx, types.NamespacedName{
207221
Name: name,
208222
Namespace: namespace,
209223
}, secret)
@@ -215,11 +229,11 @@ func getSecret(ctx context.Context, name, namespace string, k8sClient client.Cli
215229
}
216230

217231
// extractKubeconfigFromEnv gets kubeconfig data from the same sources as ctrl.GetConfig()
218-
func extractKubeconfigFromEnv(log *logger.Logger) ([]byte, string, error) {
232+
func (m *MetadataInjector) extractKubeconfigFromEnv() ([]byte, string, error) {
219233
// Check KUBECONFIG environment variable first
220234
kubeconfigPath := os.Getenv("KUBECONFIG")
221235
if kubeconfigPath != "" {
222-
log.Debug().Str("source", "KUBECONFIG env var").Str("path", kubeconfigPath).Msg("using kubeconfig from environment variable")
236+
m.log.Debug().Str("source", "KUBECONFIG env var").Str("path", kubeconfigPath).Msg("using kubeconfig from environment variable")
223237
}
224238

225239
// Fall back to default kubeconfig location if not set
@@ -229,7 +243,7 @@ func extractKubeconfigFromEnv(log *logger.Logger) ([]byte, string, error) {
229243
return nil, "", fmt.Errorf("failed to determine kubeconfig location: %w", err)
230244
}
231245
kubeconfigPath = home + "/.kube/config"
232-
log.Debug().Str("source", "default location").Str("path", kubeconfigPath).Msg("using default kubeconfig location")
246+
m.log.Debug().Str("source", "default location").Str("path", kubeconfigPath).Msg("using default kubeconfig location")
233247
}
234248

235249
// Check if file exists
@@ -301,54 +315,54 @@ func stripVirtualWorkspacePath(hostURL string) string {
301315
}
302316

303317
// extractCAFromKubeconfigData extracts CA certificate data from raw kubeconfig bytes
304-
func extractCAFromKubeconfigData(kubeconfigData []byte, log *logger.Logger) []byte {
318+
func (m *MetadataInjector) extractCAFromKubeconfigData(kubeconfigData []byte) []byte {
305319
config, err := clientcmd.Load(kubeconfigData)
306320
if err != nil {
307-
log.Warn().Err(err).Msg("failed to parse kubeconfig for CA extraction")
321+
m.log.Warn().Err(err).Msg("failed to parse kubeconfig for CA extraction")
308322
return nil
309323
}
310324

311325
if config.CurrentContext == "" {
312-
log.Warn().Msg("no current context in kubeconfig for CA extraction")
326+
m.log.Warn().Msg("no current context in kubeconfig for CA extraction")
313327
return nil
314328
}
315329

316330
context, exists := config.Contexts[config.CurrentContext]
317331
if !exists {
318-
log.Warn().Str("context", config.CurrentContext).Msg("current context not found in kubeconfig for CA extraction")
332+
m.log.Warn().Str("context", config.CurrentContext).Msg("current context not found in kubeconfig for CA extraction")
319333
return nil
320334
}
321335

322336
cluster, exists := config.Clusters[context.Cluster]
323337
if !exists {
324-
log.Warn().Str("cluster", context.Cluster).Msg("cluster not found in kubeconfig for CA extraction")
338+
m.log.Warn().Str("cluster", context.Cluster).Msg("cluster not found in kubeconfig for CA extraction")
325339
return nil
326340
}
327341

328342
if len(cluster.CertificateAuthorityData) == 0 {
329-
log.Debug().Msg("no CA data found in kubeconfig")
343+
m.log.Debug().Msg("no CA data found in kubeconfig")
330344
return nil
331345
}
332346

333347
return cluster.CertificateAuthorityData
334348
}
335349

336350
// extractCAFromKubeconfigB64 extracts CA certificate data from base64-encoded kubeconfig
337-
func extractCAFromKubeconfigB64(kubeconfigB64 string, log *logger.Logger) []byte {
351+
func (m *MetadataInjector) extractCAFromKubeconfigB64(kubeconfigB64 string) []byte {
338352
kubeconfigData, err := base64.StdEncoding.DecodeString(kubeconfigB64)
339353
if err != nil {
340-
log.Warn().Err(err).Msg("failed to decode kubeconfig for CA extraction")
354+
m.log.Warn().Err(err).Msg("failed to decode kubeconfig for CA extraction")
341355
return nil
342356
}
343357

344-
return extractCAFromKubeconfigData(kubeconfigData, log)
358+
return m.extractCAFromKubeconfigData(kubeconfigData)
345359
}
346360

347361
// tryExtractKubeconfigCA attempts to extract CA data from kubeconfig auth and adds it to metadata
348-
func tryExtractKubeconfigCA(ctx context.Context, auth *gatewayv1alpha1.AuthConfig, k8sClient client.Client, metadata map[string]interface{}, log *logger.Logger) {
349-
authMetadata, err := extractAuthDataForMetadata(ctx, auth, k8sClient)
362+
func (m *MetadataInjector) tryExtractKubeconfigCA(ctx context.Context, auth *gatewayv1alpha1.AuthConfig, metadata map[string]interface{}) {
363+
authMetadata, err := m.extractAuthDataForMetadata(ctx, auth)
350364
if err != nil {
351-
log.Warn().Err(err).Msg("failed to extract auth data for CA extraction")
365+
m.log.Warn().Err(err).Msg("failed to extract auth data for CA extraction")
352366
return
353367
}
354368

@@ -366,21 +380,21 @@ func tryExtractKubeconfigCA(ctx context.Context, auth *gatewayv1alpha1.AuthConfi
366380
return
367381
}
368382

369-
kubeconfigCAData := extractCAFromKubeconfigB64(kubeconfigB64, log)
383+
kubeconfigCAData := m.extractCAFromKubeconfigB64(kubeconfigB64)
370384
if kubeconfigCAData == nil {
371385
return
372386
}
373387

374388
metadata["ca"] = map[string]interface{}{
375389
"data": base64.StdEncoding.EncodeToString(kubeconfigCAData),
376390
}
377-
log.Info().Msg("extracted CA data from kubeconfig")
391+
m.log.Info().Msg("extracted CA data from kubeconfig")
378392
}
379393

380394
// determineHost determines which host to use based on configuration
381-
func determineHost(originalHost, hostOverride string, log *logger.Logger) string {
395+
func (m *MetadataInjector) determineHost(originalHost, hostOverride string) string {
382396
if hostOverride != "" {
383-
log.Info().
397+
m.log.Info().
384398
Str("originalHost", originalHost).
385399
Str("overrideHost", hostOverride).
386400
Msg("using host override for virtual workspace")
@@ -390,7 +404,7 @@ func determineHost(originalHost, hostOverride string, log *logger.Logger) string
390404
// For normal workspaces, ensure we use a clean host by stripping any virtual workspace paths
391405
cleanedHost := stripVirtualWorkspacePath(originalHost)
392406
if cleanedHost != originalHost {
393-
log.Info().
407+
m.log.Info().
394408
Str("originalHost", originalHost).
395409
Str("cleanedHost", cleanedHost).
396410
Msg("cleaned virtual workspace path from host for normal workspace")
@@ -399,9 +413,9 @@ func determineHost(originalHost, hostOverride string, log *logger.Logger) string
399413
}
400414

401415
// determineKCPHost determines which host to use for KCP metadata injection
402-
func determineKCPHost(kubeconfigHost, override, clusterPath string, log *logger.Logger) string {
416+
func (m *MetadataInjector) determineKCPHost(kubeconfigHost, override, clusterPath string) string {
403417
if override != "" {
404-
log.Info().
418+
m.log.Info().
405419
Str("clusterPath", clusterPath).
406420
Str("originalHost", kubeconfigHost).
407421
Str("overrideHost", override).
@@ -412,7 +426,7 @@ func determineKCPHost(kubeconfigHost, override, clusterPath string, log *logger.
412426
// For normal workspaces, ensure we use a clean KCP host by stripping any virtual workspace paths
413427
host := stripVirtualWorkspacePath(kubeconfigHost)
414428
if host != kubeconfigHost {
415-
log.Info().
429+
m.log.Info().
416430
Str("clusterPath", clusterPath).
417431
Str("originalHost", kubeconfigHost).
418432
Str("cleanedHost", host).
@@ -422,7 +436,7 @@ func determineKCPHost(kubeconfigHost, override, clusterPath string, log *logger.
422436
}
423437

424438
// finalizeSchemaInjection finalizes the schema injection process
425-
func finalizeSchemaInjection(schemaData map[string]interface{}, metadata map[string]interface{}, host, path string, hasCA bool, log *logger.Logger) ([]byte, error) {
439+
func (m *MetadataInjector) finalizeSchemaInjection(schemaData map[string]interface{}, metadata map[string]interface{}, host, path string, hasCA bool) ([]byte, error) {
426440
// Inject the metadata into the schema
427441
schemaData["x-cluster-metadata"] = metadata
428442

@@ -432,11 +446,40 @@ func finalizeSchemaInjection(schemaData map[string]interface{}, metadata map[str
432446
return nil, fmt.Errorf("failed to marshal modified schema: %w", err)
433447
}
434448

435-
log.Info().
449+
m.log.Info().
436450
Str("host", host).
437451
Str("path", path).
438452
Bool("hasCA", hasCA).
439453
Msg("successfully injected cluster metadata into schema")
440454

441455
return modifiedJSON, nil
442456
}
457+
458+
// Legacy function wrappers for backward compatibility
459+
// These can be removed after updating all callers
460+
461+
// InjectClusterMetadata is a legacy wrapper for backward compatibility
462+
func InjectClusterMetadata(ctx context.Context, schemaJSON []byte, config MetadataInjectionConfig, k8sClient client.Client, log *logger.Logger) ([]byte, error) {
463+
injector := NewMetadataInjector(log, k8sClient)
464+
return injector.InjectClusterMetadata(ctx, schemaJSON, config)
465+
}
466+
467+
// InjectKCPMetadataFromEnv is a legacy wrapper for backward compatibility
468+
func InjectKCPMetadataFromEnv(schemaJSON []byte, clusterPath string, log *logger.Logger, hostOverride ...string) ([]byte, error) {
469+
injector := NewMetadataInjector(log, nil)
470+
return injector.InjectKCPMetadataFromEnv(schemaJSON, clusterPath, hostOverride...)
471+
}
472+
473+
// Test exports for internal testing - these expose internal methods for unit tests
474+
475+
// extractKubeconfigFromEnv is exported for testing
476+
func extractKubeconfigFromEnv(log *logger.Logger) ([]byte, string, error) {
477+
injector := NewMetadataInjector(log, nil)
478+
return injector.extractKubeconfigFromEnv()
479+
}
480+
481+
// extractAuthDataForMetadata is exported for testing
482+
func extractAuthDataForMetadata(ctx context.Context, auth *gatewayv1alpha1.AuthConfig, k8sClient client.Client) (map[string]interface{}, error) {
483+
injector := NewMetadataInjector(nil, k8sClient)
484+
return injector.extractAuthDataForMetadata(ctx, auth)
485+
}

0 commit comments

Comments
 (0)