A Kubernetes operator that bridges Hardware Security Module (HSM) data storage with Kubernetes Secrets, providing true secret portability th
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

fix up deploy multiple agents

+233 -223
+2 -2
helm/hsm-secrets-operator/Chart.yaml
··· 2 2 name: hsm-secrets-operator 3 3 description: A Kubernetes operator that bridges Pico HSM binary data storage with Kubernetes Secrets 4 4 type: application 5 - version: 0.5.14 6 - appVersion: v0.5.14 5 + version: 0.5.15 6 + appVersion: v0.5.15 7 7 icon: https://raw.githubusercontent.com/cncf/artwork/master/projects/kubernetes/icon/color/kubernetes-icon-color.svg 8 8 home: https://github.com/evanjarrett/hsm-secrets-operator 9 9 sources:
+224 -196
internal/agent/deployment.go
··· 19 19 import ( 20 20 "context" 21 21 "fmt" 22 + "slices" 23 + "strings" 22 24 "sync" 23 25 "time" 24 26 ··· 40 42 // ManagerInterface defines the interface for HSM agent management 41 43 // This allows for easier testing with mocks 42 44 type ManagerInterface interface { 43 - EnsureAgent(ctx context.Context, hsmDevice *hsmv1alpha1.HSMDevice, hsmSecret *hsmv1alpha1.HSMSecret) (string, error) 45 + EnsureAgent(ctx context.Context, hsmDevice *hsmv1alpha1.HSMDevice, hsmSecret *hsmv1alpha1.HSMSecret) error 44 46 CleanupAgent(ctx context.Context, hsmDevice *hsmv1alpha1.HSMDevice) error 45 47 } 46 48 ··· 131 133 return m 132 134 } 133 135 134 - // EnsureAgent ensures an HSM agent pod exists for the given HSM device 135 - func (m *Manager) EnsureAgent(ctx context.Context, hsmDevice *hsmv1alpha1.HSMDevice, hsmSecret *hsmv1alpha1.HSMSecret) (string, error) { 136 + // EnsureAgent ensures HSM agents are deployed for all available devices in the pool 137 + func (m *Manager) EnsureAgent(ctx context.Context, hsmDevice *hsmv1alpha1.HSMDevice, hsmSecret *hsmv1alpha1.HSMSecret) error { 138 + // Get the HSMPool for this device to find all aggregated devices 139 + poolName := hsmDevice.Name + "-pool" 140 + var hsmPool hsmv1alpha1.HSMPool 141 + if err := m.Get(ctx, types.NamespacedName{ 142 + Name: poolName, 143 + Namespace: hsmDevice.Namespace, 144 + }, &hsmPool); err != nil { 145 + return fmt.Errorf("failed to get HSMPool %s: %w", poolName, err) 146 + } 136 147 m.mu.Lock() 137 148 defer m.mu.Unlock() 138 149 139 - deviceName := hsmDevice.Name 140 - agentName := m.generateAgentName(hsmDevice) 141 - agentNamespace := hsmDevice.Namespace 150 + // Ensure agents for each available aggregated device in the pool 151 + for i, aggregatedDevice := range hsmPool.Status.AggregatedDevices { 152 + if !aggregatedDevice.Available { 153 + continue 154 + } 155 + 156 + // Create unique agent name for each physical device 157 + agentName := fmt.Sprintf("%s-%s-%d", AgentNamePrefix, hsmDevice.Name, i) 158 + agentKey := fmt.Sprintf("%s-%d", hsmDevice.Name, i) // Unique key for tracking 142 159 143 - // Check if we already have this agent tracked 144 - if agentInfo, exists := m.activeAgents[deviceName]; exists { 145 - // Agent exists in tracking, verify it's still healthy 146 - if m.isAgentHealthy(ctx, agentInfo) { 147 - // Return the first pod IP as endpoint for backward compatibility 148 - if len(agentInfo.PodIPs) > 0 { 149 - return fmt.Sprintf("http://%s:%d", agentInfo.PodIPs[0], AgentPort), nil 160 + // Check if we already have this specific agent tracked 161 + if agentInfo, exists := m.activeAgents[agentKey]; exists { 162 + // Agent exists in tracking, verify it's still healthy 163 + if m.isAgentHealthy(ctx, agentInfo) { 164 + continue // Agent is healthy, skip 150 165 } 166 + // Agent unhealthy, remove from tracking and recreate 167 + m.removeAgentFromTracking(agentKey) 151 168 } 152 - // Agent unhealthy, remove from tracking and recreate 153 - m.removeAgentFromTracking(deviceName) 154 - } 155 169 156 - // Check if deployment exists in Kubernetes 157 - var deployment appsv1.Deployment 158 - err := m.Get(ctx, types.NamespacedName{ 159 - Name: agentName, 160 - Namespace: agentNamespace, 161 - }, &deployment) 170 + // Check if deployment exists in Kubernetes 171 + var deployment appsv1.Deployment 172 + err := m.Get(ctx, types.NamespacedName{ 173 + Name: agentName, 174 + Namespace: hsmDevice.Namespace, 175 + }, &deployment) 162 176 163 - if err == nil { 164 - // Agent exists, but check if volume mounts need updating due to device path changes 165 - needsUpdate, err := m.agentNeedsUpdate(ctx, &deployment, hsmDevice) 166 - if err != nil { 167 - return "", fmt.Errorf("failed to check if agent needs update: %w", err) 168 - } 177 + if err == nil { 178 + // Agent exists, but check if it's still targeting the right device/node 179 + needsUpdate := m.deploymentNeedsUpdateForDevice(&deployment, &aggregatedDevice) 169 180 170 - if needsUpdate { 171 - // Delete existing deployment to trigger recreation with new volume mounts 172 - if err := m.Delete(ctx, &deployment); err != nil { 173 - return "", fmt.Errorf("failed to delete outdated agent deployment: %w", err) 174 - } 175 - // Continue to create new deployment below 176 - } else { 177 - // Agent exists in K8s but not tracked - wait for it and track it 178 - podIPs, err := m.waitForAgentReady(ctx, agentName, agentNamespace) 179 - if err != nil { 180 - return "", fmt.Errorf("failed waiting for existing agent pods: %w", err) 181 - } 181 + if needsUpdate { 182 + // Delete existing deployment to trigger recreation 183 + if err := m.Delete(ctx, &deployment); err != nil { 184 + return fmt.Errorf("failed to delete outdated agent deployment %s: %w", agentName, err) 185 + } 186 + } else { 187 + // Agent exists and is correct - wait for it and track it 188 + podIPs, err := m.waitForAgentReady(ctx, agentName, hsmDevice.Namespace) 189 + if err != nil { 190 + return fmt.Errorf("failed waiting for existing agent pods %s: %w", agentName, err) 191 + } 182 192 183 - // Track the existing agent 184 - agentInfo := &AgentInfo{ 185 - DeviceName: deviceName, 186 - PodIPs: podIPs, 187 - CreatedAt: time.Now(), 188 - LastHealthCheck: time.Now(), 189 - Status: AgentStatusReady, 190 - AgentName: agentName, 191 - Namespace: agentNamespace, 192 - } 193 + // Track the existing agent 194 + agentInfo := &AgentInfo{ 195 + DeviceName: agentKey, 196 + PodIPs: podIPs, 197 + CreatedAt: time.Now(), 198 + LastHealthCheck: time.Now(), 199 + Status: AgentStatusReady, 200 + AgentName: agentName, 201 + Namespace: hsmDevice.Namespace, 202 + } 193 203 194 - m.activeAgents[deviceName] = agentInfo 195 - return fmt.Sprintf("http://%s:%d", podIPs[0], AgentPort), nil 204 + m.activeAgents[agentKey] = agentInfo 205 + continue 206 + } 207 + } else if !errors.IsNotFound(err) { 208 + return fmt.Errorf("failed to check agent deployment %s: %w", agentName, err) 196 209 } 197 - } 198 210 199 - if !errors.IsNotFound(err) { 200 - return "", fmt.Errorf("failed to check agent deployment: %w", err) 201 - } 211 + // Create agent deployment for this specific device 212 + if err := m.createAgentDeployment(ctx, hsmDevice, nil, hsmDevice.Namespace, &aggregatedDevice, agentName); err != nil { 213 + return fmt.Errorf("failed to create agent deployment %s: %w", agentName, err) 214 + } 202 215 203 - // Create agent deployment 204 - if err := m.createAgentDeployment(ctx, hsmDevice, hsmSecret, agentNamespace); err != nil { 205 - return "", fmt.Errorf("failed to create agent deployment: %w", err) 206 - } 216 + // Wait for agent pods to be ready and get their IPs 217 + podIPs, err := m.waitForAgentReady(ctx, agentName, hsmDevice.Namespace) 218 + if err != nil { 219 + return fmt.Errorf("failed waiting for agent pods %s: %w", agentName, err) 220 + } 207 221 208 - // Wait for agent pods to be ready and get their IPs 209 - podIPs, err := m.waitForAgentReady(ctx, agentName, agentNamespace) 210 - if err != nil { 211 - return "", fmt.Errorf("failed waiting for agent pods: %w", err) 212 - } 222 + // Track the new agent 223 + agentInfo := &AgentInfo{ 224 + DeviceName: agentKey, 225 + PodIPs: podIPs, 226 + CreatedAt: time.Now(), 227 + LastHealthCheck: time.Now(), 228 + Status: AgentStatusReady, 229 + AgentName: agentName, 230 + Namespace: hsmDevice.Namespace, 231 + } 213 232 214 - // Track the new agent 215 - agentInfo := &AgentInfo{ 216 - DeviceName: deviceName, 217 - PodIPs: podIPs, 218 - CreatedAt: time.Now(), 219 - LastHealthCheck: time.Now(), 220 - Status: AgentStatusReady, 221 - AgentName: agentName, 222 - Namespace: agentNamespace, 233 + m.activeAgents[agentKey] = agentInfo 223 234 } 224 235 225 - m.activeAgents[deviceName] = agentInfo 226 - 227 - // For backward compatibility, still return HTTP endpoint (will change to gRPC later) 228 - return fmt.Sprintf("http://%s:%d", podIPs[0], AgentPort), nil 236 + return nil 229 237 } 230 238 231 - // CleanupAgent removes the HSM agent for the given device when no longer needed 239 + // CleanupAgent removes all HSM agents for the given device when no longer needed 232 240 func (m *Manager) CleanupAgent(ctx context.Context, hsmDevice *hsmv1alpha1.HSMDevice) error { 233 241 m.mu.Lock() 234 242 defer m.mu.Unlock() 235 - 236 - agentName := m.generateAgentName(hsmDevice) 237 243 238 244 // Check if any HSMSecrets still reference this device 239 245 var hsmSecretList hsmv1alpha1.HSMSecretList ··· 254 260 return nil 255 261 } 256 262 257 - // Remove from internal tracking 258 - m.removeAgentFromTracking(hsmDevice.Name) 263 + // Get the HSMPool to find all agent deployments to clean up 264 + poolName := hsmDevice.Name + "-pool" 265 + var hsmPool hsmv1alpha1.HSMPool 266 + if err := m.Get(ctx, types.NamespacedName{ 267 + Name: poolName, 268 + Namespace: hsmDevice.Namespace, 269 + }, &hsmPool); err != nil { 270 + // If pool doesn't exist, try to clean up any remaining tracked agents 271 + return m.cleanupTrackedAgents(ctx, hsmDevice) 272 + } 259 273 260 - // Delete deployment 261 - deployment := &appsv1.Deployment{ 262 - ObjectMeta: metav1.ObjectMeta{ 263 - Name: agentName, 264 - Namespace: hsmDevice.Namespace, 265 - }, 274 + // Clean up all agent deployments (one per aggregated device) 275 + for i := range hsmPool.Status.AggregatedDevices { 276 + agentName := fmt.Sprintf("%s-%s-%d", AgentNamePrefix, hsmDevice.Name, i) 277 + agentKey := fmt.Sprintf("%s-%d", hsmDevice.Name, i) 278 + 279 + // Remove from internal tracking 280 + m.removeAgentFromTracking(agentKey) 281 + 282 + // Delete deployment 283 + deployment := &appsv1.Deployment{ 284 + ObjectMeta: metav1.ObjectMeta{ 285 + Name: agentName, 286 + Namespace: hsmDevice.Namespace, 287 + }, 288 + } 289 + if err := m.Delete(ctx, deployment); err != nil && !errors.IsNotFound(err) { 290 + return fmt.Errorf("failed to delete agent deployment %s: %w", agentName, err) 291 + } 266 292 } 267 - if err := m.Delete(ctx, deployment); err != nil && !errors.IsNotFound(err) { 268 - return fmt.Errorf("failed to delete agent deployment: %w", err) 293 + 294 + return nil 295 + } 296 + 297 + // cleanupTrackedAgents cleans up any remaining tracked agents when HSMPool is not available 298 + func (m *Manager) cleanupTrackedAgents(ctx context.Context, hsmDevice *hsmv1alpha1.HSMDevice) error { 299 + // Find all tracked agents for this device 300 + var agentsToCleanup []string 301 + devicePrefix := hsmDevice.Name + "-" 302 + 303 + for agentKey := range m.activeAgents { 304 + if strings.HasPrefix(agentKey, devicePrefix) { 305 + agentsToCleanup = append(agentsToCleanup, agentKey) 306 + } 307 + } 308 + 309 + // Clean up each tracked agent 310 + for _, agentKey := range agentsToCleanup { 311 + agentInfo := m.activeAgents[agentKey] 312 + 313 + // Remove from tracking 314 + m.removeAgentFromTracking(agentKey) 315 + 316 + // Delete deployment 317 + deployment := &appsv1.Deployment{ 318 + ObjectMeta: metav1.ObjectMeta{ 319 + Name: agentInfo.AgentName, 320 + Namespace: agentInfo.Namespace, 321 + }, 322 + } 323 + if err := m.Delete(ctx, deployment); err != nil && !errors.IsNotFound(err) { 324 + return fmt.Errorf("failed to delete agent deployment %s: %w", agentInfo.AgentName, err) 325 + } 269 326 } 270 327 271 328 return nil ··· 276 333 return fmt.Sprintf("%s-%s", AgentNamePrefix, hsmDevice.Name) 277 334 } 278 335 279 - // createAgentDeployment creates the HSM agent deployment 280 - func (m *Manager) createAgentDeployment(ctx context.Context, hsmDevice *hsmv1alpha1.HSMDevice, hsmSecret *hsmv1alpha1.HSMSecret, namespace string) error { 281 - agentName := m.generateAgentName(hsmDevice) 336 + // createAgentDeployment creates the HSM agent deployment for a specific device 337 + func (m *Manager) createAgentDeployment(ctx context.Context, hsmDevice *hsmv1alpha1.HSMDevice, hsmSecret *hsmv1alpha1.HSMSecret, namespace string, specificDevice *hsmv1alpha1.DiscoveredDevice, customAgentName string) error { 338 + if specificDevice == nil { 339 + return fmt.Errorf("specificDevice is required") 340 + } 282 341 283 - // Find the node where the HSM device is located 284 - targetNode := m.findTargetNode(hsmDevice) 285 - if targetNode == "" { 286 - return fmt.Errorf("no target node found for HSM device %s", hsmDevice.Name) 342 + var agentName string 343 + if customAgentName != "" { 344 + agentName = customAgentName 345 + } else { 346 + agentName = m.generateAgentName(hsmDevice) 287 347 } 348 + 349 + targetNode := specificDevice.NodeName 350 + devicePath := specificDevice.DevicePath 288 351 289 352 // Get discovery image from environment, manager image, or use default 290 353 agentImage := m.ImageResolver.GetImage(ctx, "AGENT_IMAGE") ··· 420 483 RunAsNonRoot: boolPtr(false), 421 484 RunAsUser: int64Ptr(0), 422 485 }, 423 - VolumeMounts: m.buildAgentVolumeMounts(hsmDevice), 486 + VolumeMounts: m.buildAgentVolumeMounts(), 424 487 }, 425 488 }, 426 - Volumes: m.buildAgentVolumes(hsmDevice), 489 + Volumes: m.buildAgentVolumes(devicePath), 427 490 }, 428 491 }, 429 492 }, ··· 439 502 return m.Create(ctx, deployment) 440 503 } 441 504 442 - // findTargetNode finds the node where the HSM device is located by checking the HSMPool 443 - func (m *Manager) findTargetNode(hsmDevice *hsmv1alpha1.HSMDevice) string { 444 - // Find the HSMPool for this device 445 - poolName := hsmDevice.Name + "-pool" 446 - pool := &hsmv1alpha1.HSMPool{} 447 - 448 - ctx := context.Background() 449 - err := m.Get(ctx, types.NamespacedName{ 450 - Name: poolName, 451 - Namespace: hsmDevice.Namespace, 452 - }, pool) 453 - 454 - if err != nil { 455 - // Fallback: if no pool found, use node selector if present 456 - if hsmDevice.Spec.NodeSelector != nil { 457 - // This would need more sophisticated logic to map selectors to actual nodes 458 - // For now, return empty to indicate no target found 459 - return "" 460 - } 461 - return "" 462 - } 463 - 464 - // Look for discovered devices in the pool status 465 - for _, device := range pool.Status.AggregatedDevices { 466 - if device.Available && device.NodeName != "" { 467 - return device.NodeName 468 - } 469 - } 470 - 471 - // Fallback: if no specific node found, use node selector if present 472 - if hsmDevice.Spec.NodeSelector != nil { 473 - // This would need more sophisticated logic to map selectors to actual nodes 474 - // For now, return empty to indicate no target found 475 - return "" 476 - } 477 - 478 - return "" 479 - } 480 - 481 505 // secretReferencesDevice checks if an HSMSecret references the given device 482 506 func (m *Manager) secretReferencesDevice(hsmSecret *hsmv1alpha1.HSMSecret, hsmDevice *hsmv1alpha1.HSMDevice) bool { 483 507 // This is a simplified check - in practice, you might want more sophisticated logic ··· 544 568 } 545 569 546 570 // buildAgentVolumeMounts builds volume mounts for the HSM agent 547 - func (m *Manager) buildAgentVolumeMounts(hsmDevice *hsmv1alpha1.HSMDevice) []corev1.VolumeMount { 548 - mounts := []corev1.VolumeMount{ 571 + func (m *Manager) buildAgentVolumeMounts() []corev1.VolumeMount { 572 + return []corev1.VolumeMount{ 549 573 { 550 574 Name: "tmp", 551 575 MountPath: "/tmp", 552 576 }, 553 - } 554 - 555 - // Add device mounts if needed - get from HSMPool 556 - poolName := hsmDevice.Name + "-pool" 557 - pool := &hsmv1alpha1.HSMPool{} 558 - 559 - ctx := context.Background() 560 - err := m.Get(ctx, types.NamespacedName{ 561 - Name: poolName, 562 - Namespace: hsmDevice.Namespace, 563 - }, pool) 564 - 565 - if err == nil { 566 - for _, device := range pool.Status.AggregatedDevices { 567 - if device.DevicePath != "" { 568 - mounts = append(mounts, corev1.VolumeMount{ 569 - Name: "hsm-device", 570 - MountPath: "/dev/hsm", 571 - }) 572 - break // Only need one mount point 573 - } 574 - } 577 + { 578 + Name: "hsm-device", 579 + MountPath: "/dev/hsm", 580 + }, 575 581 } 576 - 577 - return mounts 578 582 } 579 583 580 584 // buildAgentVolumes builds volumes for the HSM agent 581 - func (m *Manager) buildAgentVolumes(hsmDevice *hsmv1alpha1.HSMDevice) []corev1.Volume { 582 - volumes := []corev1.Volume{ 585 + func (m *Manager) buildAgentVolumes(devicePath string) []corev1.Volume { 586 + return []corev1.Volume{ 583 587 { 584 588 Name: "tmp", 585 589 VolumeSource: corev1.VolumeSource{ 586 590 EmptyDir: &corev1.EmptyDirVolumeSource{}, 587 591 }, 588 592 }, 589 - } 590 - 591 - // Add device volumes if needed - get from HSMPool 592 - poolName := hsmDevice.Name + "-pool" 593 - pool := &hsmv1alpha1.HSMPool{} 594 - 595 - ctx := context.Background() 596 - err := m.Get(ctx, types.NamespacedName{ 597 - Name: poolName, 598 - Namespace: hsmDevice.Namespace, 599 - }, pool) 600 - 601 - if err == nil { 602 - for _, device := range pool.Status.AggregatedDevices { 603 - if device.DevicePath != "" { 604 - volumes = append(volumes, corev1.Volume{ 605 - Name: "hsm-device", 606 - VolumeSource: corev1.VolumeSource{ 607 - HostPath: &corev1.HostPathVolumeSource{ 608 - Path: device.DevicePath, 609 - Type: hostPathTypePtr(corev1.HostPathCharDev), 610 - }, 611 - }, 612 - }) 613 - break // Only need one volume 614 - } 615 - } 593 + { 594 + Name: "hsm-device", 595 + VolumeSource: corev1.VolumeSource{ 596 + HostPath: &corev1.HostPathVolumeSource{ 597 + Path: devicePath, 598 + Type: hostPathTypePtr(corev1.HostPathCharDev), 599 + }, 600 + }, 601 + }, 616 602 } 617 - 618 - return volumes 619 603 } 620 604 621 605 // agentNeedsUpdate checks if the agent deployment needs to be updated due to device path or image changes ··· 701 685 } 702 686 703 687 return false, nil 688 + } 689 + 690 + // deploymentNeedsUpdateForDevice checks if a deployment needs to be updated for a specific device 691 + // This is a simplified check that only validates device-specific configuration 692 + func (m *Manager) deploymentNeedsUpdateForDevice(deployment *appsv1.Deployment, aggregatedDevice *hsmv1alpha1.DiscoveredDevice) bool { 693 + // Check node affinity - ensure agent is pinned to the correct node 694 + if deployment.Spec.Template.Spec.Affinity == nil || 695 + deployment.Spec.Template.Spec.Affinity.NodeAffinity == nil || 696 + deployment.Spec.Template.Spec.Affinity.NodeAffinity.RequiredDuringSchedulingIgnoredDuringExecution == nil { 697 + return true // Missing required node affinity 698 + } 699 + 700 + // Check if the node name matches the aggregated device's node 701 + nodeSelector := deployment.Spec.Template.Spec.Affinity.NodeAffinity.RequiredDuringSchedulingIgnoredDuringExecution 702 + if len(nodeSelector.NodeSelectorTerms) == 0 { 703 + return true 704 + } 705 + 706 + // Check if hostname requirement matches the device's node 707 + nodeMatches := false 708 + for _, term := range nodeSelector.NodeSelectorTerms { 709 + for _, expr := range term.MatchExpressions { 710 + if expr.Key == "kubernetes.io/hostname" && expr.Operator == corev1.NodeSelectorOpIn { 711 + if slices.Contains(expr.Values, aggregatedDevice.NodeName) { 712 + nodeMatches = true 713 + } 714 + } 715 + } 716 + } 717 + 718 + if !nodeMatches { 719 + return true // Node doesn't match 720 + } 721 + 722 + // Check device path in volume mounts 723 + for _, vol := range deployment.Spec.Template.Spec.Volumes { 724 + if vol.Name == "hsm-device" && vol.HostPath != nil { 725 + if vol.HostPath.Path != aggregatedDevice.DevicePath { 726 + return true // Device path changed 727 + } 728 + } 729 + } 730 + 731 + return false 704 732 } 705 733 706 734 // Helper functions
+3 -21
internal/controller/hsmpool_agent_controller.go
··· 64 64 65 65 // Only deploy agents for ready pools with discovered hardware 66 66 if hsmPool.Status.Phase == hsmv1alpha1.HSMPoolPhaseReady && len(hsmPool.Status.AggregatedDevices) > 0 { 67 - // For each HSMDevice referenced by this pool, ensure an agent exists 67 + // For each HSMDevice referenced by this pool, ensure agents exist for all aggregated devices 68 68 for _, deviceRef := range hsmPool.Spec.HSMDeviceRefs { 69 69 // Get the HSMDevice to pass to agent manager 70 70 var hsmDevice hsmv1alpha1.HSMDevice ··· 76 76 continue 77 77 } 78 78 79 - if err := r.ensureHSMAgent(ctx, &hsmDevice); err != nil { 80 - logger.Error(err, "Failed to ensure HSM agent", "device", deviceRef) 79 + if err := r.AgentManager.EnsureAgent(ctx, &hsmDevice, nil); err != nil { 80 + logger.Error(err, "Failed to ensure HSM agents for pool", "device", deviceRef) 81 81 } 82 82 } 83 83 } else { ··· 93 93 } 94 94 95 95 return ctrl.Result{}, nil 96 - } 97 - 98 - // ensureHSMAgent ensures an HSM agent pod is running for the given device 99 - func (r *HSMPoolAgentReconciler) ensureHSMAgent(ctx context.Context, hsmDevice *hsmv1alpha1.HSMDevice) error { 100 - logger := log.FromContext(ctx) 101 - 102 - if r.AgentManager == nil { 103 - return fmt.Errorf("agent manager not configured") 104 - } 105 - 106 - // Ensure agent pod is running for this device 107 - agentEndpoint, err := r.AgentManager.EnsureAgent(ctx, hsmDevice, nil) 108 - if err != nil { 109 - return fmt.Errorf("failed to ensure HSM agent: %w", err) 110 - } 111 - 112 - logger.Info("HSM agent ensured", "device", hsmDevice.Name, "endpoint", agentEndpoint) 113 - return nil 114 96 } 115 97 116 98 // cleanupStaleAgents removes agent deployments for devices that have been unavailable for too long
+2 -2
internal/controller/hsmpool_agent_controller_test.go
··· 456 456 CleanupCalls []string // Track which devices were cleaned up 457 457 } 458 458 459 - func (m *MockAgentManager) EnsureAgent(ctx context.Context, hsmDevice *hsmv1alpha1.HSMDevice, hsmSecret *hsmv1alpha1.HSMSecret) (string, error) { 460 - return "mock-endpoint", nil 459 + func (m *MockAgentManager) EnsureAgent(ctx context.Context, hsmDevice *hsmv1alpha1.HSMDevice, hsmSecret *hsmv1alpha1.HSMSecret) error { 460 + return nil 461 461 } 462 462 463 463 func (m *MockAgentManager) CleanupAgent(ctx context.Context, hsmDevice *hsmv1alpha1.HSMDevice) error {
+2 -2
internal/controller/hsmsecret_controller.go
··· 192 192 193 193 // Ensure agent pods are running for all devices and create clients 194 194 for _, hsmDevice := range hsmDevices { 195 - // EnsureAgent now returns HTTP endpoint for backward compatibility, but we'll use gRPC 196 - _, err = r.AgentManager.EnsureAgent(ctx, hsmDevice, hsmSecret) 195 + // EnsureAgent ensures agents for all devices in the pool 196 + err = r.AgentManager.EnsureAgent(ctx, hsmDevice, hsmSecret) 197 197 if err != nil { 198 198 // Clean up any successful connections before returning error 199 199 if err := deviceClients.Close(); err != nil {