Select the types of activity you want to include in your feed.
update hsmsecrets to have a parentref that point back to the operator that manages them. this allows hsmsecrets to be deployed outside of the operator namespace
···2424// EDIT THIS FILE! THIS IS SCAFFOLDING FOR YOU TO OWN!
2525// NOTE: json tags are required. Any new fields you add must have json tags for the fields to be serialized.
26262727+// ParentReference identifies an API object (typically an HSM operator instance)
2828+// to which the HSMSecret should be associated.
2929+type ParentReference struct {
3030+ // Name is the name of the parent resource.
3131+ Name string `json:"name"`
3232+3333+ // Namespace is the namespace of the parent resource.
3434+ // This must be provided when the parent is in a different namespace
3535+ // from the HSMSecret.
3636+ // +optional
3737+ Namespace *string `json:"namespace,omitempty"`
3838+3939+ // Group is the API group of the parent resource.
4040+ // Defaults to "apps" (for Deployment resources).
4141+ // +kubebuilder:default="apps"
4242+ // +optional
4343+ Group *string `json:"group,omitempty"`
4444+4545+ // Kind is the kind of the parent resource.
4646+ // Defaults to "Deployment".
4747+ // +kubebuilder:default="Deployment"
4848+ // +optional
4949+ Kind *string `json:"kind,omitempty"`
5050+}
5151+2752// HSMSecretSpec defines the desired state of HSMSecret.
2853type HSMSecretSpec struct {
5454+ // ParentRef identifies the HSM operator instance that should handle this HSMSecret.
5555+ // HSMSecrets without a ParentRef are ignored by all operators.
5656+ // +optional
5757+ ParentRef *ParentReference `json:"parentRef,omitempty"`
5858+2959 // SecretName is the name of the Kubernetes Secret object to create/update
3060 // Defaults to the HSMSecret name if not specified
3161 // +optional
+36-1
api/v1alpha1/zz_generated.deepcopy.go
···346346 *out = *in
347347 out.TypeMeta = in.TypeMeta
348348 in.ObjectMeta.DeepCopyInto(&out.ObjectMeta)
349349- out.Spec = in.Spec
349349+ in.Spec.DeepCopyInto(&out.Spec)
350350 in.Status.DeepCopyInto(&out.Status)
351351}
352352···403403// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
404404func (in *HSMSecretSpec) DeepCopyInto(out *HSMSecretSpec) {
405405 *out = *in
406406+ if in.ParentRef != nil {
407407+ in, out := &in.ParentRef, &out.ParentRef
408408+ *out = new(ParentReference)
409409+ (*in).DeepCopyInto(*out)
410410+ }
406411}
407412408413// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new HSMSecretSpec.
···518523 return nil
519524 }
520525 out := new(PKCS11Config)
526526+ in.DeepCopyInto(out)
527527+ return out
528528+}
529529+530530+// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
531531+func (in *ParentReference) DeepCopyInto(out *ParentReference) {
532532+ *out = *in
533533+ if in.Namespace != nil {
534534+ in, out := &in.Namespace, &out.Namespace
535535+ *out = new(string)
536536+ **out = **in
537537+ }
538538+ if in.Group != nil {
539539+ in, out := &in.Group, &out.Group
540540+ *out = new(string)
541541+ **out = **in
542542+ }
543543+ if in.Kind != nil {
544544+ in, out := &in.Kind, &out.Kind
545545+ *out = new(string)
546546+ **out = **in
547547+ }
548548+}
549549+550550+// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ParentReference.
551551+func (in *ParentReference) DeepCopy() *ParentReference {
552552+ if in == nil {
553553+ return nil
554554+ }
555555+ out := new(ParentReference)
521556 in.DeepCopyInto(out)
522557 return out
523558}
+29
config/crd/bases/hsm.j5t.io_hsmsecrets.yaml
···5959 description: AutoSync enables bidirectional synchronization between
6060 HSM and Kubernetes Secret
6161 type: boolean
6262+ parentRef:
6363+ description: |-
6464+ ParentRef identifies the HSM operator instance that should handle this HSMSecret.
6565+ HSMSecrets without a ParentRef are ignored by all operators.
6666+ properties:
6767+ group:
6868+ default: apps
6969+ description: |-
7070+ Group is the API group of the parent resource.
7171+ Defaults to "apps" (for Deployment resources).
7272+ type: string
7373+ kind:
7474+ default: Deployment
7575+ description: |-
7676+ Kind is the kind of the parent resource.
7777+ Defaults to "Deployment".
7878+ type: string
7979+ name:
8080+ description: Name is the name of the parent resource.
8181+ type: string
8282+ namespace:
8383+ description: |-
8484+ Namespace is the namespace of the parent resource.
8585+ This must be provided when the parent is in a different namespace
8686+ from the HSMSecret.
8787+ type: string
8888+ required:
8989+ - name
9090+ type: object
6291 secretName:
6392 description: |-
6493 SecretName is the name of the Kubernetes Secret object to create/update
+75
config/samples/README.md
···11+# HSMSecret Cross-Namespace Support
22+33+This directory contains sample HSMSecret manifests demonstrating cross-namespace functionality.
44+55+## ParentRef-Based Operator Association
66+77+When multiple HSM operator instances are deployed in a cluster, HSMSecrets use `parentRef` to specify which operator should handle them:
88+99+```yaml
1010+apiVersion: hsm.j5t.io/v1alpha1
1111+kind: HSMSecret
1212+metadata:
1313+ name: my-secret
1414+ namespace: production
1515+spec:
1616+ parentRef:
1717+ name: controller-manager
1818+ namespace: hsm-operator-system
1919+ # ... rest of spec
2020+```
2121+2222+## Behavior
2323+2424+- **With parentRef**: Only the operator with matching name and namespace will handle the HSMSecret
2525+- **Without parentRef**: HSMSecret is ignored by all operators (explicit association required)
2626+2727+## Architecture
2828+2929+- **HSMSecrets**: Can be created in any namespace
3030+- **Kubernetes Secrets**: Created in the same namespace as their HSMSecret
3131+- **Operator Infrastructure**: HSMDevices, HSMPools, agents remain in the operator's namespace
3232+- **RBAC**: ClusterRole provides cluster-wide permissions
3333+3434+## Helm Integration
3535+3636+When deploying via Helm, the `parentRef` is automatically added to HSMSecrets:
3737+3838+```yaml
3939+# In Helm values.yaml
4040+hsmsecret:
4141+ enabled: true
4242+ secrets:
4343+ - name: "database-credentials"
4444+ namespace: "production"
4545+ secretName: "db-secrets"
4646+ syncInterval: 300
4747+ autoSync: true
4848+ - name: "api-keys"
4949+ namespace: "development"
5050+ secretName: "third-party-keys"
5151+ syncInterval: 60
5252+```
5353+5454+This creates HSMSecrets with automatically generated `parentRef`:
5555+5656+```yaml
5757+apiVersion: hsm.j5t.io/v1alpha1
5858+kind: HSMSecret
5959+metadata:
6060+ name: database-credentials
6161+ namespace: production
6262+spec:
6363+ parentRef:
6464+ name: my-release-hsm-secrets-operator-controller-manager
6565+ namespace: my-operator-namespace
6666+ secretName: db-secrets
6767+ syncInterval: 300
6868+ autoSync: true
6969+```
7070+7171+**Benefits**:
7272+- No manual parentRef configuration needed
7373+- Automatic association with the deploying Helm release
7474+- Multi-tenant support for multiple operator deployments
7575+- Cross-namespace secret management with explicit operator ownership
+6
config/samples/hsm_v1alpha1_hsmsecret.yaml
···99spec:
1010 # HSM path is automatically set to the metadata.name (hsmsecret-sample)
11111212+ # ParentRef identifies which operator instance should handle this HSMSecret
1313+ # Comment out to ignore this HSMSecret (no operator will process it)
1414+ parentRef:
1515+ name: controller-manager
1616+ namespace: hsm-secrets-operator-system
1717+1218 # Name of the Kubernetes Secret to create (defaults to HSMSecret name if not specified)
1319 secretName: "database-credentials"
1420
···11+apiVersion: hsm.j5t.io/v1alpha1
22+kind: HSMSecret
33+metadata:
44+ labels:
55+ app.kubernetes.io/name: hsm-secrets-operator
66+ app.kubernetes.io/managed-by: kustomize
77+ name: app-database-secret
88+ namespace: production # This HSMSecret is in the 'production' namespace
99+spec:
1010+ # HSM path is automatically set to the metadata.name (app-database-secret)
1111+1212+ # ParentRef identifies which operator instance should handle this HSMSecret
1313+ parentRef:
1414+ name: controller-manager
1515+ namespace: hsm-secrets-operator-system
1616+1717+ # Name of the Kubernetes Secret to create (defaults to HSMSecret name if not specified)
1818+ secretName: "database-credentials"
1919+2020+ # Enable automatic synchronization between HSM and Kubernetes Secret
2121+ autoSync: true
2222+2323+ # Synchronization interval in seconds (default: 300)
2424+ syncInterval: 60
2525+2626+ # Type of Kubernetes Secret to create (default: Opaque)
2727+ secretType: Opaque
2828+2929+---
3030+# Another HSMSecret in a different namespace to demonstrate cross-namespace functionality
3131+apiVersion: hsm.j5t.io/v1alpha1
3232+kind: HSMSecret
3333+metadata:
3434+ labels:
3535+ app.kubernetes.io/name: hsm-secrets-operator
3636+ app.kubernetes.io/managed-by: kustomize
3737+ name: api-keys
3838+ namespace: development # This HSMSecret is in the 'development' namespace
3939+spec:
4040+ # HSM path is automatically set to the metadata.name (api-keys)
4141+4242+ # ParentRef identifies which operator instance should handle this HSMSecret
4343+ parentRef:
4444+ name: controller-manager
4545+ namespace: hsm-secrets-operator-system
4646+4747+ # Name of the Kubernetes Secret to create (defaults to HSMSecret name if not specified)
4848+ secretName: "third-party-api-keys"
4949+5050+ # Enable automatic synchronization between HSM and Kubernetes Secret
5151+ autoSync: true
5252+5353+ # Synchronization interval in seconds
5454+ syncInterval: 30
5555+5656+ # Type of Kubernetes Secret to create
5757+ secretType: Opaque
+2-2
helm/hsm-secrets-operator/Chart.yaml
···22name: hsm-secrets-operator
33description: A Kubernetes operator that bridges Pico HSM binary data storage with Kubernetes Secrets
44type: application
55-version: 0.5.9
66-appVersion: v0.5.9
55+version: 0.5.10
66+appVersion: v0.5.10
77icon: https://raw.githubusercontent.com/cncf/artwork/master/projects/kubernetes/icon/color/kubernetes-icon-color.svg
88home: https://github.com/evanjarrett/hsm-secrets-operator
99sources:
···5959 description: AutoSync enables bidirectional synchronization between
6060 HSM and Kubernetes Secret
6161 type: boolean
6262+ parentRef:
6363+ description: |-
6464+ ParentRef identifies the HSM operator instance that should handle this HSMSecret.
6565+ HSMSecrets without a ParentRef are ignored by all operators.
6666+ properties:
6767+ group:
6868+ default: apps
6969+ description: |-
7070+ Group is the API group of the parent resource.
7171+ Defaults to "apps" (for Deployment resources).
7272+ type: string
7373+ kind:
7474+ default: Deployment
7575+ description: |-
7676+ Kind is the kind of the parent resource.
7777+ Defaults to "Deployment".
7878+ type: string
7979+ name:
8080+ description: Name is the name of the parent resource.
8181+ type: string
8282+ namespace:
8383+ description: |-
8484+ Namespace is the namespace of the parent resource.
8585+ This must be provided when the parent is in a different namespace
8686+ from the HSMSecret.
8787+ type: string
8888+ required:
8989+ - name
9090+ type: object
6291 secretName:
6392 description: |-
6493 SecretName is the name of the Kubernetes Secret object to create/update
···7099 create
71100 type: string
72101 syncInterval:
7373- default: 300
102102+ default: 30
74103 description: |-
75104 SyncInterval defines how often to check for HSM changes (in seconds)
76105 Only applies when AutoSync is true
···138167 - type
139168 type: object
140169 type: array
170170+ deviceSyncStatus:
171171+ description: DeviceSyncStatus tracks sync status for each HSM device
172172+ in mirrored setups
173173+ items:
174174+ description: HSMDeviceSync tracks synchronization state for a specific
175175+ HSM device
176176+ properties:
177177+ checksum:
178178+ description: Checksum is the SHA256 checksum of the data on
179179+ this device
180180+ type: string
181181+ deviceName:
182182+ description: DeviceName is the name of the HSM device
183183+ type: string
184184+ lastError:
185185+ description: LastError contains the last error when syncing
186186+ with this device
187187+ type: string
188188+ lastSyncTime:
189189+ description: LastSyncTime is the timestamp of the last successful
190190+ sync with this device
191191+ format: date-time
192192+ type: string
193193+ online:
194194+ description: Online indicates if this device is currently available
195195+ type: boolean
196196+ status:
197197+ description: Status indicates the sync status for this specific
198198+ device
199199+ type: string
200200+ version:
201201+ description: |-
202202+ Version is a monotonically increasing counter for conflict resolution
203203+ Updated each time the secret changes on this device
204204+ format: int64
205205+ type: integer
206206+ required:
207207+ - deviceName
208208+ type: object
209209+ type: array
141210 hsmChecksum:
142142- description: HSMChecksum is the SHA256 checksum of the HSM data
211211+ description: HSMChecksum is the SHA256 checksum of the HSM data (deprecated
212212+ - use DeviceSyncStatus)
143213 type: string
144214 lastError:
145215 description: LastError contains the last error message if SyncStatus
···149219 description: LastSyncTime is the timestamp of the last successful
150220 synchronization
151221 format: date-time
222222+ type: string
223223+ primaryDevice:
224224+ description: |-
225225+ PrimaryDevice indicates which device is currently considered the primary source of truth
226226+ Used for conflict resolution in multi-device scenarios
152227 type: string
153228 secretChecksum:
154229 description: SecretChecksum is the SHA256 checksum of the Kubernetes
···197272 type: string
198273 type: object
199274 x-kubernetes-map-type: atomic
275275+ syncConflict:
276276+ description: SyncConflict indicates if there are conflicting versions
277277+ across devices
278278+ type: boolean
200279 syncStatus:
201280 description: SyncStatus indicates the current synchronization status
202281 type: string
···1010 {{- include "hsm-secrets-operator.labels" $ | nindent 4 }}
1111 app.kubernetes.io/component: hsmsecret
1212spec:
1313+ # ParentRef automatically points to this Helm release's operator instance
1414+ parentRef:
1515+ name: {{ include "hsm-secrets-operator.controllerManagerName" $ }}
1616+ namespace: {{ $.Release.Namespace }}
1317 {{- with .secretName }}
1418 secretName: {{ . }}
1519 {{- end }}
+17-2
helm/hsm-secrets-operator/values.yaml
···248248 # Enable HSMSecret resource creation
249249 enabled: false
250250 # List of HSM secrets to manage
251251+ # Note: parentRef is automatically added by the Helm chart to associate with this operator instance
251252 secrets:
252252- - name: "example-secret"
253253- namespace: "default"
253253+ - name: "database-credentials"
254254+ namespace: "production"
255255+ secretName: "db-secrets"
254256 syncInterval: 300
257257+ autoSync: true
258258+ - name: "api-keys"
259259+ namespace: "development"
260260+ secretName: "third-party-keys"
261261+ syncInterval: 60
262262+ autoSync: true
263263+ # Example of additional secrets:
264264+ # - name: "tls-certs"
265265+ # namespace: "ingress-system"
266266+ # secretName: "wildcard-tls"
267267+ # secretType: "kubernetes.io/tls"
268268+ # syncInterval: 3600 # Sync hourly for certificates
269269+ # autoSync: false # Manual sync only
255270256271# Additional configuration
257272config:
+42-6
internal/controller/hsmsecret_controller.go
···4949// HSMSecretReconciler reconciles a HSMSecret object
5050type HSMSecretReconciler struct {
5151 client.Client
5252- Scheme *runtime.Scheme
5353- AgentManager *agent.Manager
5252+ Scheme *runtime.Scheme
5353+ AgentManager *agent.Manager
5454+ OperatorNamespace string
5555+ OperatorName string
5456}
55575658// +kubebuilder:rbac:groups=hsm.j5t.io,resources=hsmsecrets,verbs=get;list;watch;create;update;patch;delete
···7577 }
7678 logger.Error(err, "Failed to get HSMSecret")
7779 return ctrl.Result{}, err
8080+ }
8181+8282+ // Check if this HSMSecret should be handled by this operator instance
8383+ if !r.shouldHandleSecret(&hsmSecret) {
8484+ logger.V(1).Info("HSMSecret not assigned to this operator instance, skipping",
8585+ "secret", hsmSecret.Name,
8686+ "namespace", hsmSecret.Namespace,
8787+ "operatorName", r.OperatorName,
8888+ "operatorNamespace", r.OperatorNamespace)
8989+ return ctrl.Result{}, nil
7890 }
79918092 // Find target HSM device and ensure agent is running
···120132 logger := log.FromContext(ctx)
121133122134 // Find the appropriate HSM device
123123- hsmDevice, err := r.findHSMDeviceForSecret(ctx, hsmSecret)
135135+ hsmDevice, err := r.findHSMDeviceForSecret(ctx)
124136 if err != nil {
125137 return nil, nil, fmt.Errorf("failed to find HSM device for secret: %w", err)
126138 }
···503515}
504516505517// findHSMDeviceForSecret finds the HSMDevice that should contain the secret
506506-func (r *HSMSecretReconciler) findHSMDeviceForSecret(ctx context.Context, hsmSecret *hsmv1alpha1.HSMSecret) (*hsmv1alpha1.HSMDevice, error) {
507507- // List all HSMDevices in the same namespace
518518+// Note: HSMDevices are managed in the operator namespace, not the HSMSecret's namespace
519519+func (r *HSMSecretReconciler) findHSMDeviceForSecret(ctx context.Context) (*hsmv1alpha1.HSMDevice, error) {
520520+ // List HSMDevices in this operator's namespace (where operator infrastructure is contained)
508521 var hsmDeviceList hsmv1alpha1.HSMDeviceList
509509- if err := r.List(ctx, &hsmDeviceList, client.InNamespace(hsmSecret.Namespace)); err != nil {
522522+ if err := r.List(ctx, &hsmDeviceList, client.InNamespace(r.OperatorNamespace)); err != nil {
510523 return nil, fmt.Errorf("failed to list HSM devices: %w", err)
511524 }
512525···530543 }
531544532545 return nil, fmt.Errorf("no suitable HSM device found in ready state")
546546+}
547547+548548+// shouldHandleSecret determines if this operator instance should handle the given HSMSecret
549549+func (r *HSMSecretReconciler) shouldHandleSecret(hsmSecret *hsmv1alpha1.HSMSecret) bool {
550550+ // If no parentRef is present, ignore the secret (explicit association required)
551551+ if hsmSecret.Spec.ParentRef == nil {
552552+ return false
553553+ }
554554+555555+ parentRef := hsmSecret.Spec.ParentRef
556556+557557+ // Check if the parent name matches this operator
558558+ if parentRef.Name != r.OperatorName {
559559+ return false
560560+ }
561561+562562+ // Check namespace match - if parentRef.Namespace is nil, assume same namespace as HSMSecret
563563+ expectedNamespace := r.OperatorNamespace
564564+ if parentRef.Namespace != nil {
565565+ expectedNamespace = *parentRef.Namespace
566566+ }
567567+568568+ return expectedNamespace == r.OperatorNamespace
533569}
534570535571// SetupWithManager sets up the controller with the Manager.
+129
internal/controller/hsmsecret_parentref_test.go
···11+/*
22+Copyright 2025.
33+44+Licensed under the Apache License, Version 2.0 (the "License");
55+you may not use this file except in compliance with the License.
66+You may obtain a copy of the License at
77+88+ http://www.apache.org/licenses/LICENSE-2.0
99+1010+Unless required by applicable law or agreed to in writing, software
1111+distributed under the License is distributed on an "AS IS" BASIS,
1212+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
1313+See the License for the specific language governing permissions and
1414+limitations under the License.
1515+*/
1616+1717+package controller
1818+1919+import (
2020+ "testing"
2121+2222+ metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
2323+2424+ hsmv1alpha1 "github.com/evanjarrett/hsm-secrets-operator/api/v1alpha1"
2525+)
2626+2727+func stringPtr(s string) *string {
2828+ return &s
2929+}
3030+3131+func TestShouldHandleSecret(t *testing.T) {
3232+ reconciler := &HSMSecretReconciler{
3333+ OperatorNamespace: "hsm-operator-system",
3434+ OperatorName: "controller-manager",
3535+ }
3636+3737+ tests := []struct {
3838+ name string
3939+ secret *hsmv1alpha1.HSMSecret
4040+ expected bool
4141+ }{
4242+ {
4343+ name: "no parentRef - should not handle (explicit association required)",
4444+ secret: &hsmv1alpha1.HSMSecret{
4545+ ObjectMeta: metav1.ObjectMeta{
4646+ Name: "test-secret",
4747+ Namespace: "default",
4848+ },
4949+ Spec: hsmv1alpha1.HSMSecretSpec{
5050+ // No parentRef
5151+ },
5252+ },
5353+ expected: false,
5454+ },
5555+ {
5656+ name: "matching parentRef - should handle",
5757+ secret: &hsmv1alpha1.HSMSecret{
5858+ ObjectMeta: metav1.ObjectMeta{
5959+ Name: "test-secret",
6060+ Namespace: "production",
6161+ },
6262+ Spec: hsmv1alpha1.HSMSecretSpec{
6363+ ParentRef: &hsmv1alpha1.ParentReference{
6464+ Name: "controller-manager",
6565+ Namespace: stringPtr("hsm-operator-system"),
6666+ },
6767+ },
6868+ },
6969+ expected: true,
7070+ },
7171+ {
7272+ name: "different parent name - should not handle",
7373+ secret: &hsmv1alpha1.HSMSecret{
7474+ ObjectMeta: metav1.ObjectMeta{
7575+ Name: "test-secret",
7676+ Namespace: "production",
7777+ },
7878+ Spec: hsmv1alpha1.HSMSecretSpec{
7979+ ParentRef: &hsmv1alpha1.ParentReference{
8080+ Name: "other-operator",
8181+ Namespace: stringPtr("hsm-operator-system"),
8282+ },
8383+ },
8484+ },
8585+ expected: false,
8686+ },
8787+ {
8888+ name: "different parent namespace - should not handle",
8989+ secret: &hsmv1alpha1.HSMSecret{
9090+ ObjectMeta: metav1.ObjectMeta{
9191+ Name: "test-secret",
9292+ Namespace: "production",
9393+ },
9494+ Spec: hsmv1alpha1.HSMSecretSpec{
9595+ ParentRef: &hsmv1alpha1.ParentReference{
9696+ Name: "controller-manager",
9797+ Namespace: stringPtr("other-operator-system"),
9898+ },
9999+ },
100100+ },
101101+ expected: false,
102102+ },
103103+ {
104104+ name: "parentRef without namespace (should use operator namespace) - should handle",
105105+ secret: &hsmv1alpha1.HSMSecret{
106106+ ObjectMeta: metav1.ObjectMeta{
107107+ Name: "test-secret",
108108+ Namespace: "production",
109109+ },
110110+ Spec: hsmv1alpha1.HSMSecretSpec{
111111+ ParentRef: &hsmv1alpha1.ParentReference{
112112+ Name: "controller-manager",
113113+ // Namespace is nil, should default to operator namespace
114114+ },
115115+ },
116116+ },
117117+ expected: true,
118118+ },
119119+ }
120120+121121+ for _, tt := range tests {
122122+ t.Run(tt.name, func(t *testing.T) {
123123+ result := reconciler.shouldHandleSecret(tt.secret)
124124+ if result != tt.expected {
125125+ t.Errorf("shouldHandleSecret() = %v, expected %v", result, tt.expected)
126126+ }
127127+ })
128128+ }
129129+}
+49-3
internal/modes/manager/manager.go
···1919import (
2020 "crypto/tls"
2121 "flag"
2222+ "os"
2223 "path/filepath"
2424+ "strings"
2325 "time"
24262527 // Import all Kubernetes client auth plugins (e.g. Azure, GCP, OIDC, etc.)
···5052func init() {
5153 utilruntime.Must(clientgoscheme.AddToScheme(scheme))
5254 utilruntime.Must(hsmv1alpha1.AddToScheme(scheme))
5555+}
5656+5757+// getCurrentNamespace returns the namespace the operator is running in
5858+func getCurrentNamespace() string {
5959+ // Try to read namespace from service account mount
6060+ if ns, err := os.ReadFile("/var/run/secrets/kubernetes.io/serviceaccount/namespace"); err == nil {
6161+ return strings.TrimSpace(string(ns))
6262+ }
6363+6464+ // Fallback to default namespace if we can't determine it
6565+ setupLog.Info("Could not determine current namespace, using 'default'")
6666+ return "default"
6767+}
6868+6969+// getOperatorName returns the operator deployment name
7070+// This can be overridden via environment variable or falls back to default
7171+func getOperatorName() string {
7272+ // Check if operator name is provided via environment variable
7373+ if name := os.Getenv("OPERATOR_NAME"); name != "" {
7474+ return name
7575+ }
7676+7777+ // Check if deployment name is provided via downward API
7878+ if hostname := os.Getenv("HOSTNAME"); hostname != "" {
7979+ // Kubernetes deployment pods have hostname like: deployment-name-replicaset-hash-pod-hash
8080+ // Extract just the deployment name part
8181+ parts := strings.Split(hostname, "-")
8282+ if len(parts) >= 2 {
8383+ // Return the first two parts as deployment name (e.g., "controller-manager")
8484+ return strings.Join(parts[:2], "-")
8585+ }
8686+ return hostname
8787+ }
8888+8989+ // Fallback to default deployment name
9090+ setupLog.Info("Could not determine operator name, using 'controller-manager'")
9191+ return "controller-manager"
5392}
54935594// Run starts the manager mode
···212251 // HSM mirroring is now handled by the sync package and HSMSyncReconciler
213252 // Device discovery is handled by separate discovery daemon
214253254254+ // Get current operator namespace and name
255255+ operatorNamespace := getCurrentNamespace()
256256+ operatorName := getOperatorName()
257257+ setupLog.Info("Detected operator details", "namespace", operatorNamespace, "name", operatorName)
258258+215259 // Agent manager will detect the current namespace automatically
216260 imageResolver := controller.NewImageResolver(mgr.GetClient())
217261 agentManager := agent.NewManager(mgr.GetClient(), "", imageResolver)
···237281 }
238282239283 if err := (&controller.HSMSecretReconciler{
240240- Client: mgr.GetClient(),
241241- Scheme: mgr.GetScheme(),
242242- AgentManager: agentManager,
284284+ Client: mgr.GetClient(),
285285+ Scheme: mgr.GetScheme(),
286286+ AgentManager: agentManager,
287287+ OperatorNamespace: operatorNamespace,
288288+ OperatorName: operatorName,
243289 }).SetupWithManager(mgr); err != nil {
244290 setupLog.Error(err, "unable to create controller", "controller", "HSMSecret")
245291 return err