Skip to content

Commit 4af4b8d

Browse files
authored
Replace global logger with context-aware logging in operator (#2007)
Resolves #1830 This change replaces all instances of global logger usage in the ToolHive operator with context-aware logging to preserve trace context for better observability. Changes: - Updated validateRunConfig() to accept context and use log.FromContext() - Updated helper functions in mcpserver_controller.go to use context-aware logging: - ensureRequiredEnvVars() - serviceForMCPServer() - deploymentNeedsUpdate() - generateKubernetesOIDCArgs() - generateConfigMapOIDCArgs() - Removed unused global logger imports - Updated function signatures to accept context.Context parameters - Fixed all calling sites to pass context - Updated test files to accommodate new function signatures - Fixed linting issues (line length violations) All tests pass and no regressions introduced.
1 parent 975da43 commit 4af4b8d

File tree

5 files changed

+45
-34
lines changed

5 files changed

+45
-34
lines changed

cmd/thv-operator/controllers/mcpserver_controller.go

Lines changed: 35 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,6 @@ import (
3131

3232
mcpv1alpha1 "github.com/stacklok/toolhive/cmd/thv-operator/api/v1alpha1"
3333
"github.com/stacklok/toolhive/pkg/container/kubernetes"
34-
"github.com/stacklok/toolhive/pkg/logger"
3534
)
3635

3736
// MCPServerReconciler reconciles a MCPServer object
@@ -290,7 +289,7 @@ func (r *MCPServerReconciler) Reconcile(ctx context.Context, req ctrl.Request) (
290289
err = r.Get(ctx, types.NamespacedName{Name: serviceName, Namespace: mcpServer.Namespace}, service)
291290
if err != nil && errors.IsNotFound(err) {
292291
// Define a new service
293-
svc := r.serviceForMCPServer(mcpServer)
292+
svc := r.serviceForMCPServer(ctx, mcpServer)
294293
if svc == nil {
295294
ctxLogger.Error(nil, "Failed to create Service object")
296295
return ctrl.Result{}, fmt.Errorf("failed to create Service object")
@@ -319,7 +318,7 @@ func (r *MCPServerReconciler) Reconcile(ctx context.Context, req ctrl.Request) (
319318
}
320319

321320
// Check if the deployment spec changed
322-
if r.deploymentNeedsUpdate(deployment, mcpServer) {
321+
if r.deploymentNeedsUpdate(ctx, deployment, mcpServer) {
323322
// Update the deployment
324323
newDeployment := r.deploymentForMCPServer(ctx, mcpServer)
325324
deployment.Spec = newDeployment.Spec
@@ -337,7 +336,7 @@ func (r *MCPServerReconciler) Reconcile(ctx context.Context, req ctrl.Request) (
337336
// Check if the service spec changed
338337
if serviceNeedsUpdate(service, mcpServer) {
339338
// Update the service
340-
newService := r.serviceForMCPServer(mcpServer)
339+
newService := r.serviceForMCPServer(ctx, mcpServer)
341340
service.Spec.Ports = newService.Spec.Ports
342341
err = r.Update(ctx, service)
343342
if err != nil {
@@ -523,7 +522,7 @@ func (r *MCPServerReconciler) createRBACResource(
523522
ctxLogger := log.FromContext(ctx)
524523
desired := createResource()
525524
if err := controllerutil.SetControllerReference(mcpServer, desired, r.Scheme); err != nil {
526-
logger.Errorf("Failed to set controller reference for %s: %v", resourceType, err)
525+
ctxLogger.Error(err, "Failed to set controller reference", "resourceType", resourceType)
527526
return nil
528527
}
529528

@@ -550,7 +549,7 @@ func (r *MCPServerReconciler) updateRBACResourceIfNeeded(
550549
ctxLogger := log.FromContext(ctx)
551550
desired := createResource()
552551
if err := controllerutil.SetControllerReference(mcpServer, desired, r.Scheme); err != nil {
553-
logger.Errorf("Failed to set controller reference for %s: %v", resourceType, err)
552+
ctxLogger.Error(err, "Failed to set controller reference", "resourceType", resourceType)
554553
return nil
555554
}
556555

@@ -708,7 +707,8 @@ func (r *MCPServerReconciler) deploymentForMCPServer(ctx context.Context, m *mcp
708707
if finalPodTemplateSpec != nil {
709708
podTemplatePatch, err := json.Marshal(finalPodTemplateSpec)
710709
if err != nil {
711-
logger.Errorf("Failed to marshal pod template spec: %v", err)
710+
ctxLogger := log.FromContext(ctx)
711+
ctxLogger.Error(err, "Failed to marshal pod template spec")
712712
} else {
713713
args = append(args, fmt.Sprintf("--k8s-pod-patch=%s", string(podTemplatePatch)))
714714
}
@@ -746,7 +746,8 @@ func (r *MCPServerReconciler) deploymentForMCPServer(ctx context.Context, m *mcp
746746
if finalPodTemplateSpec != nil {
747747
podTemplatePatch, err := json.Marshal(finalPodTemplateSpec)
748748
if err != nil {
749-
logger.Errorf("Failed to marshal pod template spec: %v", err)
749+
ctxLogger := log.FromContext(ctx)
750+
ctxLogger.Error(err, "Failed to marshal pod template spec")
750751
} else {
751752
args = append(args, fmt.Sprintf("--k8s-pod-patch=%s", string(podTemplatePatch)))
752753
}
@@ -966,7 +967,7 @@ func (r *MCPServerReconciler) deploymentForMCPServer(ctx context.Context, m *mcp
966967
proxyRunnerPodSecurityContext := securityBuilder.BuildPodSecurityContext()
967968
proxyRunnerContainerSecurityContext := securityBuilder.BuildContainerSecurityContext()
968969

969-
env = ensureRequiredEnvVars(env)
970+
env = ensureRequiredEnvVars(ctx, env)
970971

971972
dep := &appsv1.Deployment{
972973
ObjectMeta: metav1.ObjectMeta{
@@ -1034,15 +1035,17 @@ func (r *MCPServerReconciler) deploymentForMCPServer(ctx context.Context, m *mcp
10341035

10351036
// Set MCPServer instance as the owner and controller
10361037
if err := controllerutil.SetControllerReference(m, dep, r.Scheme); err != nil {
1037-
logger.Errorf("Failed to set controller reference for Deployment: %v", err)
1038+
ctxLogger := log.FromContext(ctx)
1039+
ctxLogger.Error(err, "Failed to set controller reference for Deployment")
10381040
return nil
10391041
}
10401042
return dep
10411043
}
10421044

1043-
func ensureRequiredEnvVars(env []corev1.EnvVar) []corev1.EnvVar {
1045+
func ensureRequiredEnvVars(ctx context.Context, env []corev1.EnvVar) []corev1.EnvVar {
10441046
// Check for the existence of the XDG_CONFIG_HOME, HOME, TOOLHIVE_RUNTIME, and UNSTRUCTURED_LOGS environment variables
10451047
// and set them to defaults if they don't exist
1048+
ctxLogger := log.FromContext(ctx)
10461049
xdgConfigHomeFound := false
10471050
homeFound := false
10481051
toolhiveRuntimeFound := false
@@ -1062,29 +1065,29 @@ func ensureRequiredEnvVars(env []corev1.EnvVar) []corev1.EnvVar {
10621065
}
10631066
}
10641067
if !xdgConfigHomeFound {
1065-
logger.Debugf("XDG_CONFIG_HOME not found, setting to /tmp")
1068+
ctxLogger.V(1).Info("XDG_CONFIG_HOME not found, setting to /tmp")
10661069
env = append(env, corev1.EnvVar{
10671070
Name: "XDG_CONFIG_HOME",
10681071
Value: "/tmp",
10691072
})
10701073
}
10711074
if !homeFound {
1072-
logger.Debugf("HOME not found, setting to /tmp")
1075+
ctxLogger.V(1).Info("HOME not found, setting to /tmp")
10731076
env = append(env, corev1.EnvVar{
10741077
Name: "HOME",
10751078
Value: "/tmp",
10761079
})
10771080
}
10781081
if !toolhiveRuntimeFound {
1079-
logger.Debugf("TOOLHIVE_RUNTIME not found, setting to kubernetes")
1082+
ctxLogger.V(1).Info("TOOLHIVE_RUNTIME not found, setting to kubernetes")
10801083
env = append(env, corev1.EnvVar{
10811084
Name: "TOOLHIVE_RUNTIME",
10821085
Value: "kubernetes",
10831086
})
10841087
}
10851088
// Always use structured JSON logs in Kubernetes (not configurable)
10861089
if !unstructuredLogsFound {
1087-
logger.Debugf("UNSTRUCTURED_LOGS not found, setting to false for structured JSON logging")
1090+
ctxLogger.V(1).Info("UNSTRUCTURED_LOGS not found, setting to false for structured JSON logging")
10881091
env = append(env, corev1.EnvVar{
10891092
Name: "UNSTRUCTURED_LOGS",
10901093
Value: "false",
@@ -1104,7 +1107,7 @@ func createServiceURL(mcpServerName, namespace string, port int32) string {
11041107
}
11051108

11061109
// serviceForMCPServer returns a MCPServer Service object
1107-
func (r *MCPServerReconciler) serviceForMCPServer(m *mcpv1alpha1.MCPServer) *corev1.Service {
1110+
func (r *MCPServerReconciler) serviceForMCPServer(ctx context.Context, m *mcpv1alpha1.MCPServer) *corev1.Service {
11081111
ls := labelsForMCPServer(m.Name)
11091112

11101113
// we want to generate a service name that is unique for the proxy service
@@ -1144,7 +1147,8 @@ func (r *MCPServerReconciler) serviceForMCPServer(m *mcpv1alpha1.MCPServer) *cor
11441147

11451148
// Set MCPServer instance as the owner and controller
11461149
if err := controllerutil.SetControllerReference(m, svc, r.Scheme); err != nil {
1147-
logger.Errorf("Failed to set controller reference for Service: %v", err)
1150+
ctxLogger := log.FromContext(ctx)
1151+
ctxLogger.Error(err, "Failed to set controller reference for Service")
11481152
return nil
11491153
}
11501154
return svc
@@ -1256,7 +1260,11 @@ func (r *MCPServerReconciler) finalizeMCPServer(ctx context.Context, m *mcpv1alp
12561260
// deploymentNeedsUpdate checks if the deployment needs to be updated
12571261
//
12581262
//nolint:gocyclo
1259-
func (r *MCPServerReconciler) deploymentNeedsUpdate(deployment *appsv1.Deployment, mcpServer *mcpv1alpha1.MCPServer) bool {
1263+
func (r *MCPServerReconciler) deploymentNeedsUpdate(
1264+
ctx context.Context,
1265+
deployment *appsv1.Deployment,
1266+
mcpServer *mcpv1alpha1.MCPServer,
1267+
) bool {
12601268
// Check if the container args have changed
12611269
if len(deployment.Spec.Template.Spec.Containers) > 0 {
12621270
container := deployment.Spec.Template.Spec.Containers[0]
@@ -1371,7 +1379,7 @@ func (r *MCPServerReconciler) deploymentNeedsUpdate(deployment *appsv1.Deploymen
13711379
}
13721380
}
13731381
// Add default environment variables that are always injected
1374-
expectedProxyEnv = ensureRequiredEnvVars(expectedProxyEnv)
1382+
expectedProxyEnv = ensureRequiredEnvVars(ctx, expectedProxyEnv)
13751383
if !reflect.DeepEqual(container.Env, expectedProxyEnv) {
13761384
return true
13771385
}
@@ -1401,7 +1409,8 @@ func (r *MCPServerReconciler) deploymentNeedsUpdate(deployment *appsv1.Deploymen
14011409
if expectedPodTemplateSpec != nil {
14021410
expectedPatch, err := json.Marshal(expectedPodTemplateSpec)
14031411
if err != nil {
1404-
logger.Errorf("Failed to marshal expected pod template spec: %v", err)
1412+
ctxLogger := log.FromContext(ctx)
1413+
ctxLogger.Error(err, "Failed to marshal expected pod template spec")
14051414
return true // Assume change if we can't marshal
14061415
}
14071416
expectedPatchString := string(expectedPatch)
@@ -1705,7 +1714,7 @@ func (r *MCPServerReconciler) generateOIDCArgs(ctx context.Context, m *mcpv1alph
17051714

17061715
switch m.Spec.OIDCConfig.Type {
17071716
case mcpv1alpha1.OIDCConfigTypeKubernetes:
1708-
args = append(args, r.generateKubernetesOIDCArgs(m)...)
1717+
args = append(args, r.generateKubernetesOIDCArgs(ctx, m)...)
17091718
case mcpv1alpha1.OIDCConfigTypeConfigMap:
17101719
args = append(args, r.generateConfigMapOIDCArgs(ctx, m)...)
17111720
case mcpv1alpha1.OIDCConfigTypeInline:
@@ -1716,13 +1725,14 @@ func (r *MCPServerReconciler) generateOIDCArgs(ctx context.Context, m *mcpv1alph
17161725
}
17171726

17181727
// generateKubernetesOIDCArgs generates OIDC args for Kubernetes service account token validation
1719-
func (*MCPServerReconciler) generateKubernetesOIDCArgs(m *mcpv1alpha1.MCPServer) []string {
1728+
func (*MCPServerReconciler) generateKubernetesOIDCArgs(ctx context.Context, m *mcpv1alpha1.MCPServer) []string {
17201729
var args []string
17211730
config := m.Spec.OIDCConfig.Kubernetes
17221731

17231732
// Set defaults if config is nil
17241733
if config == nil {
1725-
logger.Infof("Kubernetes OIDCConfig is nil for MCPServer %s, using default configuration", m.Name)
1734+
ctxLogger := log.FromContext(ctx)
1735+
ctxLogger.Info("Kubernetes OIDCConfig is nil, using default configuration", "mcpServer", m.Name)
17261736
defaultUseClusterAuth := true
17271737
config = &mcpv1alpha1.KubernetesOIDCConfig{
17281738
UseClusterAuth: &defaultUseClusterAuth, // Default to true
@@ -1804,7 +1814,8 @@ func (r *MCPServerReconciler) generateConfigMapOIDCArgs( // nolint:gocyclo
18041814
Namespace: m.Namespace,
18051815
}, configMap)
18061816
if err != nil {
1807-
logger.Errorf("Failed to get ConfigMap %s: %v", config.Name, err)
1817+
ctxLogger := log.FromContext(ctx)
1818+
ctxLogger.Error(err, "Failed to get ConfigMap", "configMapName", config.Name)
18081819
return args
18091820
}
18101821

cmd/thv-operator/controllers/mcpserver_oidc_test.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -359,7 +359,7 @@ func TestGenerateKubernetesOIDCArgs(t *testing.T) {
359359
t.Run(tt.name, func(t *testing.T) {
360360
t.Parallel()
361361

362-
args := reconciler.generateKubernetesOIDCArgs(tt.mcpServer)
362+
args := reconciler.generateKubernetesOIDCArgs(context.Background(), tt.mcpServer)
363363
assert.Equal(t, tt.expectedArgs, args)
364364
})
365365
}

cmd/thv-operator/controllers/mcpserver_resource_overrides_test.go

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -314,7 +314,7 @@ func TestResourceOverrides(t *testing.T) {
314314
assert.Equal(t, tt.expectedDeploymentAnns, deployment.Annotations)
315315

316316
// Test service creation
317-
service := r.serviceForMCPServer(tt.mcpServer)
317+
service := r.serviceForMCPServer(context.Background(), tt.mcpServer)
318318
require.NotNil(t, service)
319319

320320
assert.Equal(t, tt.expectedServiceLabels, service.Labels)
@@ -457,7 +457,7 @@ func TestDeploymentNeedsUpdateServiceAccount(t *testing.T) {
457457
require.NotNil(t, deployment)
458458

459459
// Test with the current deployment - this should NOT need update
460-
needsUpdate := r.deploymentNeedsUpdate(deployment, mcpServer)
460+
needsUpdate := r.deploymentNeedsUpdate(context.Background(), deployment, mcpServer)
461461

462462
// With the service account bug fixed, this should now return false
463463
assert.False(t, needsUpdate, "deploymentNeedsUpdate should return false when deployment matches MCPServer spec")
@@ -637,7 +637,7 @@ func TestDeploymentNeedsUpdateProxyEnv(t *testing.T) {
637637
deployment.Spec.Template.Spec.Containers[0].Image = getToolhiveRunnerImage()
638638

639639
// Test if deployment needs update - should correlate with env change expectation
640-
needsUpdate := r.deploymentNeedsUpdate(deployment, tt.mcpServer)
640+
needsUpdate := r.deploymentNeedsUpdate(context.Background(), deployment, tt.mcpServer)
641641

642642
if tt.expectEnvChange {
643643
assert.True(t, needsUpdate, "Expected deployment update due to proxy env change")
@@ -718,7 +718,7 @@ func TestDeploymentNeedsUpdateToolsFilter(t *testing.T) {
718718

719719
mcpServer.Spec.ToolsFilter = tt.newToolsFilter
720720

721-
needsUpdate := r.deploymentNeedsUpdate(deployment, mcpServer)
721+
needsUpdate := r.deploymentNeedsUpdate(context.Background(), deployment, mcpServer)
722722
assert.Equal(t, tt.expectEnvChange, needsUpdate)
723723
})
724724
}

cmd/thv-operator/controllers/mcpserver_runconfig.go

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,6 @@ import (
2222

2323
mcpv1alpha1 "github.com/stacklok/toolhive/cmd/thv-operator/api/v1alpha1"
2424
"github.com/stacklok/toolhive/pkg/authz"
25-
"github.com/stacklok/toolhive/pkg/logger"
2625
"github.com/stacklok/toolhive/pkg/operator/accessors"
2726
"github.com/stacklok/toolhive/pkg/runner"
2827
transporttypes "github.com/stacklok/toolhive/pkg/transport/types"
@@ -92,7 +91,7 @@ func (r *MCPServerReconciler) ensureRunConfigConfigMap(ctx context.Context, m *m
9291
}
9392

9493
// Validate the RunConfig before creating the ConfigMap
95-
if err := r.validateRunConfig(runConfig); err != nil {
94+
if err := r.validateRunConfig(ctx, runConfig); err != nil {
9695
return fmt.Errorf("invalid RunConfig: %w", err)
9796
}
9897

@@ -354,7 +353,7 @@ func labelsForRunConfig(mcpServerName string) map[string]string {
354353
}
355354

356355
// validateRunConfig validates a RunConfig for operator-managed deployments
357-
func (r *MCPServerReconciler) validateRunConfig(config *runner.RunConfig) error {
356+
func (r *MCPServerReconciler) validateRunConfig(ctx context.Context, config *runner.RunConfig) error {
358357
if config == nil {
359358
return fmt.Errorf("RunConfig cannot be nil")
360359
}
@@ -387,7 +386,8 @@ func (r *MCPServerReconciler) validateRunConfig(config *runner.RunConfig) error
387386
return err
388387
}
389388

390-
logger.Debugf("RunConfig validation passed for %s", config.Name)
389+
ctxLogger := log.FromContext(ctx)
390+
ctxLogger.V(1).Info("RunConfig validation passed", "name", config.Name)
391391
return nil
392392
}
393393

cmd/thv-operator/controllers/mcpserver_runconfig_test.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1441,7 +1441,7 @@ func TestValidateRunConfig(t *testing.T) {
14411441
t.Run(tt.name, func(t *testing.T) {
14421442
t.Parallel()
14431443
r := &MCPServerReconciler{}
1444-
err := r.validateRunConfig(tt.config)
1444+
err := r.validateRunConfig(t.Context(), tt.config)
14451445

14461446
if tt.expectErr {
14471447
assert.Error(t, err)

0 commit comments

Comments
 (0)