Skip to content

Commit d4e8cce

Browse files
RoddieKieleyCursor claude-4-sonnet
andcommitted
Refactor deploymentForMCPServer for platform detection. (#1063) (#1285)
Signed-off-by: Roddie Kieley <[email protected]> Co-authored-by: Cursor claude-4-sonnet <[email protected]>
1 parent b3ef9e8 commit d4e8cce

File tree

7 files changed

+779
-105
lines changed

7 files changed

+779
-105
lines changed

cmd/thv-operator/controllers/mcpserver_controller.go

Lines changed: 48 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ import (
1111
"reflect"
1212
"slices"
1313
"strings"
14+
"sync"
1415
"time"
1516

1617
appsv1 "k8s.io/api/apps/v1"
@@ -22,20 +23,24 @@ import (
2223
"k8s.io/apimachinery/pkg/runtime"
2324
"k8s.io/apimachinery/pkg/types"
2425
"k8s.io/apimachinery/pkg/util/intstr"
25-
"k8s.io/utils/ptr"
26+
"k8s.io/client-go/rest"
2627
ctrl "sigs.k8s.io/controller-runtime"
2728
"sigs.k8s.io/controller-runtime/pkg/client"
2829
"sigs.k8s.io/controller-runtime/pkg/controller/controllerutil"
2930
"sigs.k8s.io/controller-runtime/pkg/log"
3031

3132
mcpv1alpha1 "github.com/stacklok/toolhive/cmd/thv-operator/api/v1alpha1"
33+
"github.com/stacklok/toolhive/pkg/container/kubernetes"
3234
"github.com/stacklok/toolhive/pkg/logger"
3335
)
3436

3537
// MCPServerReconciler reconciles a MCPServer object
3638
type MCPServerReconciler struct {
3739
client.Client
38-
Scheme *runtime.Scheme
40+
Scheme *runtime.Scheme
41+
platformDetector kubernetes.PlatformDetector
42+
detectedPlatform kubernetes.Platform
43+
platformOnce sync.Once
3944
}
4045

4146
// defaultRBACRules are the default RBAC rules that the
@@ -82,6 +87,35 @@ const (
8287
authzLabelValueInline = "inline"
8388
)
8489

90+
// detectPlatform detects the Kubernetes platform type (Kubernetes vs OpenShift)
91+
// It uses sync.Once to ensure the detection is only performed once and cached
92+
func (r *MCPServerReconciler) detectPlatform(ctx context.Context) (kubernetes.Platform, error) {
93+
var err error
94+
r.platformOnce.Do(func() {
95+
// Initialize platform detector if not already done
96+
if r.platformDetector == nil {
97+
r.platformDetector = kubernetes.NewDefaultPlatformDetector()
98+
}
99+
100+
cfg, configErr := rest.InClusterConfig()
101+
if configErr != nil {
102+
err = fmt.Errorf("failed to get in-cluster config for platform detection: %w", configErr)
103+
return
104+
}
105+
106+
r.detectedPlatform, err = r.platformDetector.DetectPlatform(cfg)
107+
if err != nil {
108+
err = fmt.Errorf("failed to detect platform: %w", err)
109+
return
110+
}
111+
112+
ctxLogger := log.FromContext(ctx)
113+
ctxLogger.Info("Platform detected for MCPServer controller", "platform", r.detectedPlatform.String())
114+
})
115+
116+
return r.detectedPlatform, err
117+
}
118+
85119
// Reconcile is part of the main kubernetes reconciliation loop which aims to
86120
// move the current state of the cluster closer to the desired state.
87121
//
@@ -156,7 +190,7 @@ func (r *MCPServerReconciler) Reconcile(ctx context.Context, req ctrl.Request) (
156190
err = r.Get(ctx, types.NamespacedName{Name: mcpServer.Name, Namespace: mcpServer.Namespace}, deployment)
157191
if err != nil && errors.IsNotFound(err) {
158192
// Define a new deployment
159-
dep := r.deploymentForMCPServer(mcpServer)
193+
dep := r.deploymentForMCPServer(ctx, mcpServer)
160194
if dep == nil {
161195
ctxLogger.Error(nil, "Failed to create Deployment object")
162196
return ctrl.Result{}, fmt.Errorf("failed to create Deployment object")
@@ -225,7 +259,7 @@ func (r *MCPServerReconciler) Reconcile(ctx context.Context, req ctrl.Request) (
225259
// Check if the deployment spec changed
226260
if deploymentNeedsUpdate(deployment, mcpServer) {
227261
// Update the deployment
228-
newDeployment := r.deploymentForMCPServer(mcpServer)
262+
newDeployment := r.deploymentForMCPServer(ctx, mcpServer)
229263
deployment.Spec = newDeployment.Spec
230264
err = r.Update(ctx, deployment)
231265
if err != nil {
@@ -401,7 +435,7 @@ func (r *MCPServerReconciler) ensureRBACResources(ctx context.Context, mcpServer
401435
// deploymentForMCPServer returns a MCPServer Deployment object
402436
//
403437
//nolint:gocyclo
404-
func (r *MCPServerReconciler) deploymentForMCPServer(m *mcpv1alpha1.MCPServer) *appsv1.Deployment {
438+
func (r *MCPServerReconciler) deploymentForMCPServer(ctx context.Context, m *mcpv1alpha1.MCPServer) *appsv1.Deployment {
405439
ls := labelsForMCPServer(m.Name)
406440
replicas := int32(1)
407441

@@ -581,22 +615,17 @@ func (r *MCPServerReconciler) deploymentForMCPServer(m *mcpv1alpha1.MCPServer) *
581615
}
582616
}
583617

584-
// Prepare ProxyRunner's pod and container security context
585-
proxyRunnerPodSecurityContext := &corev1.PodSecurityContext{
586-
RunAsNonRoot: ptr.To(true),
587-
RunAsUser: ptr.To(int64(1000)),
588-
RunAsGroup: ptr.To(int64(1000)),
589-
FSGroup: ptr.To(int64(1000)),
618+
// Detect platform and prepare ProxyRunner's pod and container security context
619+
_, err := r.detectPlatform(ctx)
620+
if err != nil {
621+
ctxLogger := log.FromContext(ctx)
622+
ctxLogger.Error(err, "Failed to detect platform, defaulting to Kubernetes", "mcpserver", m.Name)
590623
}
591624

592-
proxyRunnerContainerSecurityContext := &corev1.SecurityContext{
593-
Privileged: ptr.To(false),
594-
RunAsNonRoot: ptr.To(true),
595-
RunAsUser: ptr.To(int64(1000)),
596-
RunAsGroup: ptr.To(int64(1000)),
597-
AllowPrivilegeEscalation: ptr.To(false),
598-
ReadOnlyRootFilesystem: ptr.To(true),
599-
}
625+
// Use SecurityContextBuilder for platform-aware security context
626+
securityBuilder := kubernetes.NewSecurityContextBuilder(r.detectedPlatform)
627+
proxyRunnerPodSecurityContext := securityBuilder.BuildPodSecurityContext()
628+
proxyRunnerContainerSecurityContext := securityBuilder.BuildContainerSecurityContext()
600629

601630
env = ensureRequiredEnvVars(env)
602631

0 commit comments

Comments
 (0)