From 37e5b2d516d6d329e03df67c6332cde571a5aa8e Mon Sep 17 00:00:00 2001 From: Bharath Nallapeta Date: Tue, 22 Jul 2025 11:50:45 +0530 Subject: [PATCH 1/3] proposal: add new CRD OpenStackClusterIdentity Signed-off-by: Bharath Nallapeta --- .../20250722-openstackclusteridentity.md | 213 ++++++++++++++++++ 1 file changed, 213 insertions(+) create mode 100644 docs/proposals/20250722-openstackclusteridentity.md diff --git a/docs/proposals/20250722-openstackclusteridentity.md b/docs/proposals/20250722-openstackclusteridentity.md new file mode 100644 index 0000000000..8bbd33ae50 --- /dev/null +++ b/docs/proposals/20250722-openstackclusteridentity.md @@ -0,0 +1,213 @@ +# OpenStackClusterIdentity for Centralized Credential Management + +## Metadata + +- **Authors**: @bnallapeta +- **Reviewers**: CAPO maintainers +- **Status**: Proposed +- **Creation Date**: 2025-07-22 +- **Last Updated**: 2025-07-22 + +## Summary + +This proposal introduces `OpenStackClusterIdentity`, a cluster-scoped resource for centralized OpenStack credential management in CAPO. This enables multi-tenant environments to share credentials across namespaces while maintaining proper access controls, following patterns from AWS and Azure Cluster API providers. + +## Motivation + +### Goals +- Enable centralized storage of OpenStack credentials in cluster-scoped resources +- Provide fine-grained namespace access controls for credential usage +- Maintain 100% backward compatibility with existing OpenStackCluster resources +- Support manual, gradual migration without breaking existing deployments +- Follow established patterns from other Cluster API providers (AWS, Azure) + +### Non-Goals +- Automatic migration of existing deployments +- Integration with external secret management systems +- Breaking changes to existing API or functionality + +### User Stories + +#### Story 1: Platform Administrator +As a platform administrator managing multiple tenant namespaces, I want to store OpenStack credentials centrally in a secure namespace (e.g., `capo-system`), control which tenant namespaces can use specific credentials, and rotate credentials in one place without updating every namespace. + +#### Story 2: Tenant User +As a tenant user in namespace `team-a`, I want to create OpenStack clusters using centrally managed credentials without managing OpenStack secrets in my namespace, with clear error messages if I don't have permission to use specific credentials. + +#### Story 3: Multi-Region Setup +As an administrator managing clusters across multiple OpenStack regions, I want to create region-specific cluster identities with appropriate credentials and allow tenants to use different regional credentials based on their needs. + +### API Design + +#### New OpenStackClusterIdentity Resource (Cluster-scoped) + +```go +type OpenStackClusterIdentity struct { + metav1.TypeMeta `json:",inline"` + metav1.ObjectMeta `json:"metadata,omitempty"` + Spec OpenStackClusterIdentitySpec `json:"spec,omitempty"` + Status OpenStackClusterIdentityStatus `json:"status,omitempty"` +} + +type OpenStackClusterIdentitySpec struct { + // SecretRef references the secret containing OpenStack credentials + SecretRef OpenStackCredentialSecretReference `json:"secretRef"` + + // AllowedNamespaces defines which namespaces can use this identity + // +optional + AllowedNamespaces []string `json:"allowedNamespaces,omitempty"` + + // NamespaceSelector selects allowed namespaces via labels + // +optional + NamespaceSelector *metav1.LabelSelector `json:"namespaceSelector,omitempty"` +} + +type OpenStackCredentialSecretReference struct { + Name string `json:"name"` + Namespace string `json:"namespace"` +} + +type OpenStackClusterIdentityStatus struct { + Ready bool `json:"ready"` + Conditions clusterv1.Conditions `json:"conditions,omitempty"` +} + +type OpenStackClusterIdentityReference struct { + Name string `json:"name"` +} +``` + +**Enhanced OpenStackCluster:** +```go +type OpenStackClusterSpec struct { + // ... existing fields unchanged ... + + // ClusterIdentityRef references a cluster-scoped identity (NEW) + // Takes precedence over IdentityRef if specified + // +optional + ClusterIdentityRef *OpenStackClusterIdentityReference `json:"clusterIdentityRef,omitempty"` + + // IdentityRef references namespace-local secret (EXISTING - unchanged) + // +kubebuilder:validation:Required + IdentityRef OpenStackIdentityReference `json:"identityRef"` +} +``` + +### Implementation Details + +#### Credential Resolution Logic +The scope factory will implement dual-path credential resolution: + +```go +func (f *providerScopeFactory) resolveCredentials(obj infrav1.IdentityRefProvider) { + // Priority 1: Check for cluster identity reference + if clusterRef := obj.GetClusterIdentityRef(); clusterRef != nil { + return f.newScopeFromClusterIdentity(clusterRef) + } + + // Priority 2: Fall back to existing namespace identity behavior + return f.newScopeFromNamespaceIdentity(obj.GetIdentityRef()) +} +``` + +**Key Edge Cases:** +- Both references specified: Use `clusterIdentityRef`, log warning +- Identity deletion: Clusters show degraded status, don't fail +- Permission changes: Dynamic re-validation during reconciliation +- Invalid access: Clear error messages with namespace authorization checks + +**RBAC Requirements:** +```yaml +rules: +- apiGroups: [""] + resources: ["secrets", "namespaces"] + verbs: ["get"] +- apiGroups: ["infrastructure.cluster.x-k8s.io"] + resources: ["openstackclusteridentities"] + verbs: ["get", "list", "watch"] +``` + +### Backward Compatibility + +- Existing `identityRef` field remains required and fully functional +- Clusters without `clusterIdentityRef` work exactly as before +- `clusterIdentityRef` takes precedence when both are specified + +#### Migration Strategy +- **No forced migration**: Existing deployments continue working indefinitely +- **Manual opt-in**: Users choose when to adopt cluster identities +- **Gradual adoption**: Mix of old and new approaches supported +- **Clear documentation**: Step-by-step migration guides and examples + +### Testing Strategy + +**Unit Tests**: API validation, dual-path credential resolution, RBAC permission checking, edge cases +**Integration Tests**: End-to-end credential resolution, cross-namespace access validation, permission enforcement +**E2E Tests**: Full cluster lifecycle with cluster identity, mixed deployments, runtime permission changes +**Security Tests**: Unauthorized access attempts, RBAC boundary enforcement, audit trail verification + +## Risks and Mitigations + +| Risk | Mitigation | +|------|------------| +| Cross-namespace RBAC complexity | Extensive testing, clear documentation, validation webhooks | +| Security boundary violations | Strict validation, audit logging, security review | +| Migration complexity | Clear documentation, examples, optional migration | +| Performance impact | Caching strategy, minimal additional overhead | + +## Alternatives + +**External Secret Operator**: More complex, adds external dependency +**OpenStack Application Credentials**: Not universally supported across deployments +**Namespace-scoped Identity**: Doesn't solve centralized management +**ConfigMap-based References**: No validation, security concerns + +## Example Usage + +```yaml +apiVersion: infrastructure.cluster.x-k8s.io/v1beta1 +kind: OpenStackClusterIdentity +metadata: + name: production-openstack +spec: + secretRef: + name: openstack-credentials + namespace: capo-system + allowedNamespaces: [team-a, team-b] + +--- +apiVersion: infrastructure.cluster.x-k8s.io/v1beta1 +kind: OpenStackClusterIdentity +metadata: + name: development-openstack +spec: + secretRef: + name: dev-openstack-credentials + namespace: capo-system + namespaceSelector: + matchLabels: + environment: "development" +``` + +### Use in OpenStackCluster +```yaml +apiVersion: infrastructure.cluster.x-k8s.io/v1beta1 +kind: OpenStackCluster +metadata: + name: my-cluster + namespace: team-a +spec: + clusterIdentityRef: + name: production-openstack + identityRef: # Fallback for backward compatibility + name: fallback-secret + cloudName: openstack +``` + +## Implementation Notes + +**Controller Changes**: Modify `pkg/scope/provider.go` for dual-path resolution, update cluster controller for identity validation +**API Generation**: Update CRD generation, generate deepcopy/clients/informers, update webhooks +**Documentation**: API reference, migration guides, security best practices + +This proposal provides centralized credential management while maintaining full backward compatibility and following established Kubernetes patterns. \ No newline at end of file From bc2097fa10e1514196e5d7522272809f3615a93a Mon Sep 17 00:00:00 2001 From: Bharath Nallapeta Date: Tue, 29 Jul 2025 19:03:03 +0530 Subject: [PATCH 2/3] worked on feedback Signed-off-by: Bharath Nallapeta --- .../20250722-openstackclusteridentity.md | 307 ++++++++++++++---- 1 file changed, 239 insertions(+), 68 deletions(-) diff --git a/docs/proposals/20250722-openstackclusteridentity.md b/docs/proposals/20250722-openstackclusteridentity.md index 8bbd33ae50..fa63bacaca 100644 --- a/docs/proposals/20250722-openstackclusteridentity.md +++ b/docs/proposals/20250722-openstackclusteridentity.md @@ -3,10 +3,10 @@ ## Metadata - **Authors**: @bnallapeta -- **Reviewers**: CAPO maintainers +- **Reviewers**: CAPO maintainers (@mdbooth) - **Status**: Proposed - **Creation Date**: 2025-07-22 -- **Last Updated**: 2025-07-22 +- **Last Updated**: 2025-07-29 ## Summary @@ -53,11 +53,8 @@ type OpenStackClusterIdentitySpec struct { // SecretRef references the secret containing OpenStack credentials SecretRef OpenStackCredentialSecretReference `json:"secretRef"` - // AllowedNamespaces defines which namespaces can use this identity - // +optional - AllowedNamespaces []string `json:"allowedNamespaces,omitempty"` - // NamespaceSelector selects allowed namespaces via labels + // All namespaces have a kubernetes.io/metadata.name label containing their name // +optional NamespaceSelector *metav1.LabelSelector `json:"namespaceSelector,omitempty"` } @@ -68,102 +65,181 @@ type OpenStackCredentialSecretReference struct { } type OpenStackClusterIdentityStatus struct { - Ready bool `json:"ready"` Conditions clusterv1.Conditions `json:"conditions,omitempty"` } - -type OpenStackClusterIdentityReference struct { - Name string `json:"name"` -} ``` -**Enhanced OpenStackCluster:** -```go -type OpenStackClusterSpec struct { - // ... existing fields unchanged ... +#### Enhanced OpenStackIdentityReference (Discriminated Union) + +We extend the existing `identityRef` to support multiple types using a discriminated union pattern: - // ClusterIdentityRef references a cluster-scoped identity (NEW) - // Takes precedence over IdentityRef if specified +```go +type OpenStackIdentityReference struct { + // Type specifies the identity reference type + // +kubebuilder:validation:Enum=Secret;ClusterIdentity + // +kubebuilder:default=Secret + // +kubebuilder:validation:XValidation:rule="self == 'Secret' ? has(self.cloudName) : !has(self.cloudName)",message="cloudName required for Secret type, forbidden for ClusterIdentity type" + // +kubebuilder:validation:XValidation:rule="has(self.name)",message="name is required" // +optional - ClusterIdentityRef *OpenStackClusterIdentityReference `json:"clusterIdentityRef,omitempty"` + Type string `json:"type,omitempty"` - // IdentityRef references namespace-local secret (EXISTING - unchanged) - // +kubebuilder:validation:Required - IdentityRef OpenStackIdentityReference `json:"identityRef"` + // Name of the secret (type=Secret) or cluster identity (type=ClusterIdentity) + // +optional + Name string `json:"name,omitempty"` + + // CloudName required for Secret type, ignored for ClusterIdentity type + // +optional + CloudName string `json:"cloudName,omitempty"` + + // Region applies to both types + // +optional + Region string `json:"region,omitempty"` } ``` ### Implementation Details #### Credential Resolution Logic -The scope factory will implement dual-path credential resolution: +The scope factory will implement type-based credential resolution: ```go -func (f *providerScopeFactory) resolveCredentials(obj infrav1.IdentityRefProvider) { - // Priority 1: Check for cluster identity reference - if clusterRef := obj.GetClusterIdentityRef(); clusterRef != nil { - return f.newScopeFromClusterIdentity(clusterRef) +func (f *providerScopeFactory) resolveCredentials(identityRef *infrav1.OpenStackIdentityReference) { + switch identityRef.Type { + case "ClusterIdentity": + return f.newScopeFromClusterIdentity(identityRef.Name) + case "Secret", "": // Default to Secret for backward compatibility + return f.newScopeFromSecretIdentity(identityRef) + default: + return fmt.Errorf("unsupported identity type: %s", identityRef.Type) } - - // Priority 2: Fall back to existing namespace identity behavior - return f.newScopeFromNamespaceIdentity(obj.GetIdentityRef()) } ``` -**Key Edge Cases:** -- Both references specified: Use `clusterIdentityRef`, log warning -- Identity deletion: Clusters show degraded status, don't fail -- Permission changes: Dynamic re-validation during reconciliation -- Invalid access: Clear error messages with namespace authorization checks +#### Permission and Access Control + +This feature involves two distinct types of permissions: + +**1. Controller RBAC (Kubernetes-level permissions)** +The CAPO controller already has cluster-wide secret access. We only need to add: -**RBAC Requirements:** ```yaml -rules: +# ADD to existing config/rbac/role.yaml - apiGroups: [""] - resources: ["secrets", "namespaces"] - verbs: ["get"] + resources: ["namespaces"] + verbs: ["get"] # Read namespace metadata for validation - apiGroups: ["infrastructure.cluster.x-k8s.io"] resources: ["openstackclusteridentities"] - verbs: ["get", "list", "watch"] + verbs: ["get", "list", "watch"] # Manage cluster identities +``` + +**2. Namespace Access Control (Application-level permissions)** +The cluster identity defines which namespaces can use it via namespace selectors: + +```go +func (r *OpenStackClusterReconciler) validateNamespaceAccess(identity *OpenStackClusterIdentity, namespace string) error { + // If no selector specified, allow all namespaces + if identity.Spec.NamespaceSelector == nil { + return nil + } + + // Get the namespace object + ns := &corev1.Namespace{} + err := r.Client.Get(ctx, types.NamespacedName{Name: namespace}, ns) + if err != nil { + return fmt.Errorf("failed to get namespace %s: %w", namespace, err) + } + + // Check if namespace matches the selector + selector, err := metav1.LabelSelectorAsSelector(identity.Spec.NamespaceSelector) + if err != nil { + return fmt.Errorf("invalid namespace selector: %w", err) + } + + if !selector.Matches(labels.Set(ns.Labels)) { + return fmt.Errorf("namespace %s not allowed to use cluster identity %s", + namespace, identity.Name) + } + + return nil +} +``` + +**Key Edge Cases:** +- Missing type field: Defaults to `Secret` behavior (100% backward compatible) +- Invalid type: Clear error message +- Invalid field combinations: CEL validation prevents misconfigurations +- Namespace access denied: Clear error message with identity name and namespace + +#### CEL Validation + +We use CEL (Common Expression Language) for API validation, following existing CAPO patterns: + +```go +type OpenStackIdentityReference struct { + // Type specifies the identity reference type + // +kubebuilder:validation:Enum=Secret;ClusterIdentity + // +kubebuilder:default=Secret + // +kubebuilder:validation:XValidation:rule="self == 'Secret' ? has(self.cloudName) : !has(self.cloudName)",message="cloudName required for Secret type, forbidden for ClusterIdentity type" + // +kubebuilder:validation:XValidation:rule="has(self.name)",message="name is required" + // +optional + Type string `json:"type,omitempty"` + + // Name of the secret (type=Secret) or cluster identity (type=ClusterIdentity) + // +optional + Name string `json:"name,omitempty"` + + // CloudName required for Secret type, ignored for ClusterIdentity type + // +optional + CloudName string `json:"cloudName,omitempty"` + + // Region applies to both types + // +optional + Region string `json:"region,omitempty"` +} ``` +**CEL Validation Rules:** +1. **Name Required**: `name` field is always required for both types +2. **CloudName Logic**: Required for Secret type, forbidden for ClusterIdentity type +3. **Type Safety**: Enum validation ensures only valid types are accepted + ### Backward Compatibility -- Existing `identityRef` field remains required and fully functional -- Clusters without `clusterIdentityRef` work exactly as before -- `clusterIdentityRef` takes precedence when both are specified +- **Perfect backward compatibility**: Existing `identityRef` configurations work unchanged +- **Default behavior**: Missing `type` field defaults to `Secret` behavior +- **No migration required**: Existing clusters continue working +- **Gradual adoption**: Users can adopt `type: ClusterIdentity` when required #### Migration Strategy - **No forced migration**: Existing deployments continue working indefinitely -- **Manual opt-in**: Users choose when to adopt cluster identities -- **Gradual adoption**: Mix of old and new approaches supported -- **Clear documentation**: Step-by-step migration guides and examples +- **Manual opt-in**: Users add `type: ClusterIdentity` when ready +- **Clear validation**: Type-specific field validation prevents misconfigurations ### Testing Strategy -**Unit Tests**: API validation, dual-path credential resolution, RBAC permission checking, edge cases -**Integration Tests**: End-to-end credential resolution, cross-namespace access validation, permission enforcement -**E2E Tests**: Full cluster lifecycle with cluster identity, mixed deployments, runtime permission changes -**Security Tests**: Unauthorized access attempts, RBAC boundary enforcement, audit trail verification +**Unit Tests**: API validation for both identity types, credential resolution logic, namespace access validation +**Integration Tests**: End-to-end credential resolution for both types, cross-namespace access validation +**E2E Tests**: Full cluster lifecycle with both identity types, mixed deployments +**Security Tests**: Unauthorized access attempts, namespace boundary enforcement ## Risks and Mitigations | Risk | Mitigation | |------|------------| -| Cross-namespace RBAC complexity | Extensive testing, clear documentation, validation webhooks | -| Security boundary violations | Strict validation, audit logging, security review | -| Migration complexity | Clear documentation, examples, optional migration | -| Performance impact | Caching strategy, minimal additional overhead | +| Complex validation logic | Type-specific validation, comprehensive testing | +| Field confusion | Clear documentation, validation webhooks | +| Migration issues | Extensive backward compatibility testing | +| Cross-namespace security | Strict namespace selector validation, audit logging | ## Alternatives +**Separate clusterIdentityRef field**: Requires dual fields, harder to maintain long-term **External Secret Operator**: More complex, adds external dependency -**OpenStack Application Credentials**: Not universally supported across deployments -**Namespace-scoped Identity**: Doesn't solve centralized management **ConfigMap-based References**: No validation, security concerns ## Example Usage +### Create Cluster Identity ```yaml apiVersion: infrastructure.cluster.x-k8s.io/v1beta1 kind: OpenStackClusterIdentity @@ -173,8 +249,11 @@ spec: secretRef: name: openstack-credentials namespace: capo-system - allowedNamespaces: [team-a, team-b] - + namespaceSelector: + matchExpressions: + - key: kubernetes.io/metadata.name + operator: In + values: [team-a, team-b] --- apiVersion: infrastructure.cluster.x-k8s.io/v1beta1 kind: OpenStackClusterIdentity @@ -186,28 +265,120 @@ spec: namespace: capo-system namespaceSelector: matchLabels: - environment: "development" + environment: development ``` -### Use in OpenStackCluster +### Current Secret-based Identity (unchanged) ```yaml apiVersion: infrastructure.cluster.x-k8s.io/v1beta1 kind: OpenStackCluster metadata: - name: my-cluster + name: cluster-a namespace: team-a spec: - clusterIdentityRef: - name: production-openstack - identityRef: # Fallback for backward compatibility - name: fallback-secret + identityRef: + # No type field = defaults to Secret behavior + name: my-secret + cloudName: openstack +``` + +### New ClusterIdentity-based (new usage) +```yaml +apiVersion: infrastructure.cluster.x-k8s.io/v1beta1 +kind: OpenStackCluster +metadata: + name: cluster-a + namespace: team-a +spec: + identityRef: + type: ClusterIdentity + name: prod-openstack +``` + +### Explicit Secret Type (optional) +```yaml +apiVersion: infrastructure.cluster.x-k8s.io/v1beta1 +kind: OpenStackCluster +metadata: + name: cluster-a + namespace: team-a +spec: + identityRef: + type: Secret + name: my-secret cloudName: openstack ``` ## Implementation Notes -**Controller Changes**: Modify `pkg/scope/provider.go` for dual-path resolution, update cluster controller for identity validation -**API Generation**: Update CRD generation, generate deepcopy/clients/informers, update webhooks -**Documentation**: API reference, migration guides, security best practices +**API Changes**: +- Extend `OpenStackIdentityReference` with `type` field with `Secret` and `ClusterIdentity` as the only supported values +- Add CEL validation rules for type-specific field combinations +- Update CRD generation for new field + +**RBAC Changes**: +- Add namespace `get` permission to existing `config/rbac/role.yaml` +- Add `openstackclusteridentities` resource permissions + +**Controller Changes**: +- Modify `pkg/scope/provider.go` for type-based credential resolution +- Add namespace access validation logic in controllers +- Add cluster identity lookup and permission checking +- Implement comprehensive error handling with clear messages + +**Backward Compatibility**: +- Default `type` to `Secret` when not specified +- CEL validation maintains existing field requirements for secret-based identities +- Ensure all existing configurations continue working + +**Security Considerations**: +- Validate namespace access on every cluster reconciliation +- Log access attempts for audit purposes +- Clear error messages for permission denials +- Fail securely when cluster identity is not accessible + +**Broader Impact**: +This change automatically enables cluster identity support for: +- `OpenStackCluster` resources +- `OpenStackMachine` resources +- `OpenStackServer` resources + +## Conditions + +Following the existing CAPO pattern for simple resources (like OpenStackFloatingIPPool), the `OpenStackClusterIdentityStatus` uses a single condition: + +- **Ready**: Indicates whether the identity can be used for authentication. This includes secret accessibility, structure validation, and credential validation. Different reason codes provide specific failure details. + +### Example Condition States + +**Secret not found:** +```yaml +status: + conditions: + - type: Ready + status: "False" + reason: SecretNotFound + message: "Secret 'openstack-creds' not found in namespace 'capo-system'" +``` + +**Authentication failure:** +```yaml +status: + conditions: + - type: Ready + status: "False" + reason: AuthenticationFailed + message: "OpenStack authentication failed: invalid credentials for cloud 'openstack'" +``` + +**Ready state:** +```yaml +status: + conditions: + - type: Ready + status: "True" + reason: CredentialsValid + message: "OpenStack credentials validated successfully" +``` -This proposal provides centralized credential management while maintaining full backward compatibility and following established Kubernetes patterns. \ No newline at end of file +This proposal provides centralized credential management while maintaining full backward compatibility and following established Kubernetes patterns (discriminated union). From 4af2ff444cdb3d740cb97d5319a0e2294268e408 Mon Sep 17 00:00:00 2001 From: Bharath Nallapeta Date: Fri, 1 Aug 2025 14:08:41 +0530 Subject: [PATCH 3/3] removed conditions field Signed-off-by: Bharath Nallapeta --- .../20250722-openstackclusteridentity.md | 51 ++----------------- 1 file changed, 4 insertions(+), 47 deletions(-) diff --git a/docs/proposals/20250722-openstackclusteridentity.md b/docs/proposals/20250722-openstackclusteridentity.md index fa63bacaca..73a9e78fda 100644 --- a/docs/proposals/20250722-openstackclusteridentity.md +++ b/docs/proposals/20250722-openstackclusteridentity.md @@ -43,10 +43,9 @@ As an administrator managing clusters across multiple OpenStack regions, I want ```go type OpenStackClusterIdentity struct { - metav1.TypeMeta `json:",inline"` + metav1.TypeMeta `json:",inline"` metav1.ObjectMeta `json:"metadata,omitempty"` - Spec OpenStackClusterIdentitySpec `json:"spec,omitempty"` - Status OpenStackClusterIdentityStatus `json:"status,omitempty"` + Spec OpenStackClusterIdentitySpec `json:"spec,omitempty"` } type OpenStackClusterIdentitySpec struct { @@ -63,10 +62,6 @@ type OpenStackCredentialSecretReference struct { Name string `json:"name"` Namespace string `json:"namespace"` } - -type OpenStackClusterIdentityStatus struct { - Conditions clusterv1.Conditions `json:"conditions,omitempty"` -} ``` #### Enhanced OpenStackIdentityReference (Discriminated Union) @@ -87,7 +82,7 @@ type OpenStackIdentityReference struct { // +optional Name string `json:"name,omitempty"` - // CloudName required for Secret type, ignored for ClusterIdentity type + // CloudName required for Secret type, forbidden for ClusterIdentity type // +optional CloudName string `json:"cloudName,omitempty"` @@ -188,7 +183,7 @@ type OpenStackIdentityReference struct { // +optional Name string `json:"name,omitempty"` - // CloudName required for Secret type, ignored for ClusterIdentity type + // CloudName required for Secret type, forbidden for ClusterIdentity type // +optional CloudName string `json:"cloudName,omitempty"` @@ -343,42 +338,4 @@ This change automatically enables cluster identity support for: - `OpenStackMachine` resources - `OpenStackServer` resources -## Conditions - -Following the existing CAPO pattern for simple resources (like OpenStackFloatingIPPool), the `OpenStackClusterIdentityStatus` uses a single condition: - -- **Ready**: Indicates whether the identity can be used for authentication. This includes secret accessibility, structure validation, and credential validation. Different reason codes provide specific failure details. - -### Example Condition States - -**Secret not found:** -```yaml -status: - conditions: - - type: Ready - status: "False" - reason: SecretNotFound - message: "Secret 'openstack-creds' not found in namespace 'capo-system'" -``` - -**Authentication failure:** -```yaml -status: - conditions: - - type: Ready - status: "False" - reason: AuthenticationFailed - message: "OpenStack authentication failed: invalid credentials for cloud 'openstack'" -``` - -**Ready state:** -```yaml -status: - conditions: - - type: Ready - status: "True" - reason: CredentialsValid - message: "OpenStack credentials validated successfully" -``` - This proposal provides centralized credential management while maintaining full backward compatibility and following established Kubernetes patterns (discriminated union).