99 appsv1 "k8s.io/api/apps/v1"
1010 corev1 "k8s.io/api/core/v1"
1111 "k8s.io/apimachinery/pkg/api/errors"
12- "k8s.io/apimachinery/pkg/api/resource"
1312 metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
14- "k8s.io/apimachinery/pkg/util/intstr"
1513 "sigs.k8s.io/controller-runtime/pkg/client"
1614 "sigs.k8s.io/controller-runtime/pkg/controller/controllerutil"
1715 "sigs.k8s.io/controller-runtime/pkg/log"
@@ -79,11 +77,7 @@ func (m *manager) ensureDeployment(
7977 ctxLogger := log .FromContext (ctx ).WithValues ("mcpregistry" , mcpRegistry .Name )
8078
8179 // Build the desired deployment configuration
82- deployment , err := m .buildRegistryAPIDeployment (mcpRegistry , configManager )
83- if err != nil {
84- ctxLogger .Error (err , "Failed to build deployment configuration" )
85- return nil , fmt .Errorf ("failed to build deployment configuration: %w" , err )
86- }
80+ deployment := m .buildRegistryAPIDeployment (mcpRegistry , configManager )
8781 deploymentName := deployment .Name
8882
8983 // Set owner reference for automatic garbage collection
@@ -94,7 +88,7 @@ func (m *manager) ensureDeployment(
9488
9589 // Check if deployment already exists
9690 existing := & appsv1.Deployment {}
97- err = m .client .Get (ctx , client.ObjectKey {
91+ err : = m .client .Get (ctx , client.ObjectKey {
9892 Name : deploymentName ,
9993 Namespace : mcpRegistry .Namespace ,
10094 }, existing )
@@ -124,16 +118,31 @@ func (m *manager) ensureDeployment(
124118// buildRegistryAPIDeployment creates and configures a Deployment object for the registry API.
125119// This function handles all deployment configuration including labels, container specs, probes,
126120// and storage manager integration. It returns a fully configured deployment ready for Kubernetes API operations.
127- func (m * manager ) buildRegistryAPIDeployment (
121+ func (* manager ) buildRegistryAPIDeployment (
128122 mcpRegistry * mcpv1alpha1.MCPRegistry ,
129123 configManager config.ConfigManager ,
130- ) ( * appsv1.Deployment , error ) {
124+ ) * appsv1.Deployment {
131125 // Generate deployment name using the established pattern
132126 deploymentName := mcpRegistry .GetAPIResourceName ()
133127
134128 // Define labels using common function
135129 labels := labelsForRegistryAPI (mcpRegistry , deploymentName )
136130
131+ // Build the PodTemplateSpec using the functional options pattern
132+ // This includes all mount configurations via the builder pattern
133+ builder := NewPodTemplateSpecBuilder ()
134+ podTemplateSpec := builder .Apply (
135+ WithLabels (labels ),
136+ WithAnnotations (map [string ]string {
137+ "toolhive.stacklok.dev/config-hash" : "hash-dummy-value" ,
138+ }),
139+ WithServiceAccountName (DefaultServiceAccountName ),
140+ WithContainer (BuildRegistryAPIContainer (getRegistryAPIImage ())),
141+ WithRegistryServerConfigMount (registryAPIContainerName , configManager .GetRegistryServerConfigMapName ()),
142+ WithRegistrySourceMounts (registryAPIContainerName , mcpRegistry .Spec .Registries ),
143+ WithRegistryStorageMount (registryAPIContainerName ),
144+ ).Build ()
145+
137146 // Create basic deployment specification with named container
138147 deployment := & appsv1.Deployment {
139148 ObjectMeta : metav1.ObjectMeta {
@@ -149,82 +158,11 @@ func (m *manager) buildRegistryAPIDeployment(
149158 "app.kubernetes.io/component" : "registry-api" ,
150159 },
151160 },
152- Template : corev1.PodTemplateSpec {
153- ObjectMeta : metav1.ObjectMeta {
154- Labels : labels ,
155- Annotations : map [string ]string {
156- "toolhive.stacklok.dev/config-hash" : "hash-dummy-value" ,
157- },
158- },
159- Spec : corev1.PodSpec {
160- ServiceAccountName : DefaultServiceAccountName ,
161- Containers : []corev1.Container {
162- {
163- Name : registryAPIContainerName ,
164- Image : getRegistryAPIImage (),
165- Args : []string {
166- ServeCommand ,
167- },
168- Ports : []corev1.ContainerPort {
169- {
170- ContainerPort : RegistryAPIPort ,
171- Name : RegistryAPIPortName ,
172- Protocol : corev1 .ProtocolTCP ,
173- },
174- },
175- // Add resource limits and requests for production readiness
176- Resources : corev1.ResourceRequirements {
177- Requests : corev1.ResourceList {
178- corev1 .ResourceCPU : resource .MustParse (DefaultCPURequest ),
179- corev1 .ResourceMemory : resource .MustParse (DefaultMemoryRequest ),
180- },
181- Limits : corev1.ResourceList {
182- corev1 .ResourceCPU : resource .MustParse (DefaultCPULimit ),
183- corev1 .ResourceMemory : resource .MustParse (DefaultMemoryLimit ),
184- },
185- },
186- // Add liveness and readiness probes
187- LivenessProbe : & corev1.Probe {
188- ProbeHandler : corev1.ProbeHandler {
189- HTTPGet : & corev1.HTTPGetAction {
190- Path : HealthCheckPath ,
191- Port : intstr .FromInt32 (RegistryAPIPort ),
192- },
193- },
194- InitialDelaySeconds : LivenessInitialDelay ,
195- PeriodSeconds : LivenessPeriod ,
196- },
197- ReadinessProbe : & corev1.Probe {
198- ProbeHandler : corev1.ProbeHandler {
199- HTTPGet : & corev1.HTTPGetAction {
200- Path : ReadinessCheckPath ,
201- Port : intstr .FromInt32 (RegistryAPIPort ),
202- },
203- },
204- InitialDelaySeconds : ReadinessInitialDelay ,
205- PeriodSeconds : ReadinessPeriod ,
206- },
207- },
208- },
209- },
210- },
161+ Template : podTemplateSpec ,
211162 },
212163 }
213164
214- // Configure storage-specific aspects using the new inverted dependency approach
215- if err := m .configureRegistryServerConfigMounts (deployment , registryAPIContainerName , configManager ); err != nil {
216- return nil , fmt .Errorf ("failed to configure registry server config mounts: %w" , err )
217- }
218-
219- if err := m .configureRegistrySourceMounts (deployment , mcpRegistry , registryAPIContainerName ); err != nil {
220- return nil , fmt .Errorf ("failed to configure registry source mounts: %w" , err )
221- }
222-
223- if err := m .configureRegistryStorageMounts (deployment , registryAPIContainerName ); err != nil {
224- return nil , fmt .Errorf ("failed to configure registry storage mounts: %w" , err )
225- }
226-
227- return deployment , nil
165+ return deployment
228166}
229167
230168// getRegistryAPIImage returns the registry API container image to use
@@ -240,33 +178,3 @@ func getRegistryAPIImageWithEnvGetter(envGetter func(string) string) string {
240178 }
241179 return "ghcr.io/stacklok/thv-registry-api:latest"
242180}
243-
244- // findContainerByName finds a container by name in a slice of containers
245- func findContainerByName (containers []corev1.Container , name string ) * corev1.Container {
246- for i := range containers {
247- if containers [i ].Name == name {
248- return & containers [i ]
249- }
250- }
251- return nil
252- }
253-
254- // hasVolume checks if a volume with the given name exists in the volumes slice
255- func hasVolume (volumes []corev1.Volume , name string ) bool {
256- for _ , volume := range volumes {
257- if volume .Name == name {
258- return true
259- }
260- }
261- return false
262- }
263-
264- // hasVolumeMount checks if a volume mount with the given name exists in the volume mounts slice
265- func hasVolumeMount (volumeMounts []corev1.VolumeMount , name string ) bool {
266- for _ , mount := range volumeMounts {
267- if mount .Name == name {
268- return true
269- }
270- }
271- return false
272- }
0 commit comments