diff --git a/apis/v1alpha1/ack-generate-metadata.yaml b/apis/v1alpha1/ack-generate-metadata.yaml index f872374..2568e0b 100755 --- a/apis/v1alpha1/ack-generate-metadata.yaml +++ b/apis/v1alpha1/ack-generate-metadata.yaml @@ -1,13 +1,13 @@ ack_generate_info: - build_date: "2026-08-13T00:59:18Z" + build_date: "2026-08-13T21:57:46Z" build_hash: 65d45b2e6c9efd6aca20e0d36826d1e18e4ba2b7 - go_version: go1.26.5 + go_version: go1.26.0 version: v0.62.1 -api_directory_checksum: 8f60eb5c2efd66afac68b6306bc19ac59b55bf39 +api_directory_checksum: 6af865b9ff13dc6a618c6c5d02101eeb9267caf3 api_version: v1alpha1 aws_sdk_go_version: v1.32.6 generator_config_info: - file_checksum: 5bcee3b30b5b81b3f66396527ee0f5696e413cfb + file_checksum: 4acee79f10d3c8f7ecb876ab1894bf99fcef78bf original_file_name: generator.yaml last_modification: reason: API generation diff --git a/apis/v1alpha1/generator.yaml b/apis/v1alpha1/generator.yaml index 3dcd172..2889e9e 100644 --- a/apis/v1alpha1/generator.yaml +++ b/apis/v1alpha1/generator.yaml @@ -25,6 +25,10 @@ resources: Status: type: string is_read_only: true + KeyspaceName: + references: + resource: Keyspace + path: Spec.KeyspaceName EncryptionSpecification.KMSKeyIdentifier: references: resource: Key diff --git a/apis/v1alpha1/table.go b/apis/v1alpha1/table.go index 4b0004e..61c6d07 100644 --- a/apis/v1alpha1/table.go +++ b/apis/v1alpha1/table.go @@ -68,8 +68,8 @@ type TableSpec struct { // The name of the keyspace that the table is going to be created in. // // Regex Pattern: `^[a-zA-Z0-9][a-zA-Z0-9_]{0,47}$` - // +kubebuilder:validation:Required - KeyspaceName *string `json:"keyspaceName"` + KeyspaceName *string `json:"keyspaceName,omitempty"` + KeyspaceRef *ackv1alpha1.AWSResourceReferenceWrapper `json:"keyspaceRef,omitempty"` // Specifies if pointInTimeRecovery is enabled or disabled for the table. The // options are: // diff --git a/apis/v1alpha1/zz_generated.deepcopy.go b/apis/v1alpha1/zz_generated.deepcopy.go index 0f554da..5db2dd3 100644 --- a/apis/v1alpha1/zz_generated.deepcopy.go +++ b/apis/v1alpha1/zz_generated.deepcopy.go @@ -866,6 +866,11 @@ func (in *TableSpec) DeepCopyInto(out *TableSpec) { *out = new(string) **out = **in } + if in.KeyspaceRef != nil { + in, out := &in.KeyspaceRef, &out.KeyspaceRef + *out = new(corev1alpha1.AWSResourceReferenceWrapper) + (*in).DeepCopyInto(*out) + } if in.PointInTimeRecovery != nil { in, out := &in.PointInTimeRecovery, &out.PointInTimeRecovery *out = new(PointInTimeRecovery) diff --git a/config/crd/bases/keyspaces.services.k8s.aws_tables.yaml b/config/crd/bases/keyspaces.services.k8s.aws_tables.yaml index f9bc0cf..8b1c83e 100644 --- a/config/crd/bases/keyspaces.services.k8s.aws_tables.yaml +++ b/config/crd/bases/keyspaces.services.k8s.aws_tables.yaml @@ -131,6 +131,23 @@ spec: Regex Pattern: `^[a-zA-Z0-9][a-zA-Z0-9_]{0,47}$` type: string + keyspaceRef: + description: "AWSResourceReferenceWrapper provides a wrapper around + *AWSResourceReference\ntype to provide more user friendly syntax + for references using 'from' field\nEx:\nAPIIDRef:\n\n\tfrom:\n\t + \ name: my-api" + properties: + from: + description: |- + AWSResourceReference provides all the values necessary to reference another + k8s resource for finding the identifier(Id/ARN/Name) + properties: + name: + type: string + namespace: + type: string + type: object + type: object pointInTimeRecovery: description: |- Specifies if pointInTimeRecovery is enabled or disabled for the table. The @@ -279,7 +296,6 @@ spec: type: string type: object required: - - keyspaceName - schemaDefinition - tableName type: object diff --git a/generator.yaml b/generator.yaml index 3dcd172..2889e9e 100644 --- a/generator.yaml +++ b/generator.yaml @@ -25,6 +25,10 @@ resources: Status: type: string is_read_only: true + KeyspaceName: + references: + resource: Keyspace + path: Spec.KeyspaceName EncryptionSpecification.KMSKeyIdentifier: references: resource: Key diff --git a/helm/crds/keyspaces.services.k8s.aws_tables.yaml b/helm/crds/keyspaces.services.k8s.aws_tables.yaml index c57d00f..58ec407 100644 --- a/helm/crds/keyspaces.services.k8s.aws_tables.yaml +++ b/helm/crds/keyspaces.services.k8s.aws_tables.yaml @@ -131,6 +131,23 @@ spec: Regex Pattern: `^[a-zA-Z0-9][a-zA-Z0-9_]{0,47}$` type: string + keyspaceRef: + description: "AWSResourceReferenceWrapper provides a wrapper around + *AWSResourceReference\ntype to provide more user friendly syntax + for references using 'from' field\nEx:\nAPIIDRef:\n\n\tfrom:\n\t + \ name: my-api" + properties: + from: + description: |- + AWSResourceReference provides all the values necessary to reference another + k8s resource for finding the identifier(Id/ARN/Name) + properties: + name: + type: string + namespace: + type: string + type: object + type: object pointInTimeRecovery: description: |- Specifies if pointInTimeRecovery is enabled or disabled for the table. The @@ -279,7 +296,6 @@ spec: type: string type: object required: - - keyspaceName - schemaDefinition - tableName type: object diff --git a/pkg/resource/table/delta.go b/pkg/resource/table/delta.go index 8103706..9c3b5d1 100644 --- a/pkg/resource/table/delta.go +++ b/pkg/resource/table/delta.go @@ -121,6 +121,9 @@ func newResourceDelta( delta.Add("Spec.KeyspaceName", a.ko.Spec.KeyspaceName, b.ko.Spec.KeyspaceName) } } + if !equality.Semantic.Equalities.DeepEqual(a.ko.Spec.KeyspaceRef, b.ko.Spec.KeyspaceRef) { + delta.Add("Spec.KeyspaceRef", a.ko.Spec.KeyspaceRef, b.ko.Spec.KeyspaceRef) + } if ackcompare.HasNilDifference(a.ko.Spec.PointInTimeRecovery, b.ko.Spec.PointInTimeRecovery) { delta.Add("Spec.PointInTimeRecovery", a.ko.Spec.PointInTimeRecovery, b.ko.Spec.PointInTimeRecovery) } else if a.ko.Spec.PointInTimeRecovery != nil && b.ko.Spec.PointInTimeRecovery != nil { diff --git a/pkg/resource/table/references.go b/pkg/resource/table/references.go index 150684e..4cda0b8 100644 --- a/pkg/resource/table/references.go +++ b/pkg/resource/table/references.go @@ -48,6 +48,10 @@ func (rm *resourceManager) ClearResolvedReferences(res acktypes.AWSResource) ack } } + if ko.Spec.KeyspaceRef != nil { + ko.Spec.KeyspaceName = nil + } + return &resource{ko} } @@ -73,6 +77,12 @@ func (rm *resourceManager) ResolveReferences( resourceHasReferences = resourceHasReferences || fieldHasReferences } + if fieldHasReferences, err := rm.resolveReferenceForKeyspaceName(ctx, apiReader, ko); err != nil { + return &resource{ko}, (resourceHasReferences || fieldHasReferences), err + } else { + resourceHasReferences = resourceHasReferences || fieldHasReferences + } + return &resource{ko}, resourceHasReferences, err } @@ -85,6 +95,13 @@ func validateReferenceFields(ko *svcapitypes.Table) error { return ackerr.ResourceReferenceAndIDNotSupportedFor("EncryptionSpecification.KMSKeyIdentifier", "EncryptionSpecification.KMSKeyIdentifierRef") } } + + if ko.Spec.KeyspaceRef != nil && ko.Spec.KeyspaceName != nil { + return ackerr.ResourceReferenceAndIDNotSupportedFor("KeyspaceName", "KeyspaceRef") + } + if ko.Spec.KeyspaceRef == nil && ko.Spec.KeyspaceName == nil { + return ackerr.ResourceReferenceOrIDRequiredFor("KeyspaceName", "KeyspaceRef") + } return nil } @@ -180,3 +197,94 @@ func getReferencedResourceState_Key( } return nil } + +// resolveReferenceForKeyspaceName reads the resource referenced +// from KeyspaceRef field and sets the KeyspaceName +// from referenced resource. Returns a boolean indicating whether a reference +// contains references, or an error +func (rm *resourceManager) resolveReferenceForKeyspaceName( + ctx context.Context, + apiReader client.Reader, + ko *svcapitypes.Table, +) (hasReferences bool, err error) { + if ko.Spec.KeyspaceRef != nil && ko.Spec.KeyspaceRef.From != nil { + hasReferences = true + arr := ko.Spec.KeyspaceRef.From + if arr.Name == nil || *arr.Name == "" { + return hasReferences, fmt.Errorf("provided resource reference is nil or empty: KeyspaceRef") + } + namespace, err := ackrt.ResolveCrossNamespaceReference( + ctx, + rm.cfg.EnableCrossNamespace, + &ko.Status.Conditions, + ackrt.CrossNamespaceRefKindResource, + ko.ObjectMeta.GetNamespace(), + arr.Namespace, + *arr.Name, + ) + if err != nil { + return hasReferences, err + } + obj := &svcapitypes.Keyspace{} + if err := getReferencedResourceState_Keyspace(ctx, apiReader, obj, *arr.Name, namespace); err != nil { + return hasReferences, err + } + ko.Spec.KeyspaceName = (*string)(obj.Spec.KeyspaceName) + } + + return hasReferences, nil +} + +// getReferencedResourceState_Keyspace looks up whether a referenced resource +// exists and is in a ACK.ResourceSynced=True state. If the referenced resource does exist and is +// in a Synced state, returns nil, otherwise returns `ackerr.ResourceReferenceTerminalFor` or +// `ResourceReferenceNotSyncedFor` depending on if the resource is in a Terminal state. +func getReferencedResourceState_Keyspace( + ctx context.Context, + apiReader client.Reader, + obj *svcapitypes.Keyspace, + name string, // the Kubernetes name of the referenced resource + namespace string, // the Kubernetes namespace of the referenced resource +) error { + namespacedName := types.NamespacedName{ + Namespace: namespace, + Name: name, + } + err := apiReader.Get(ctx, namespacedName, obj) + if err != nil { + return err + } + var refResourceTerminal bool + for _, cond := range obj.Status.Conditions { + if cond.Type == ackv1alpha1.ConditionTypeTerminal && + cond.Status == corev1.ConditionTrue { + return ackerr.ResourceReferenceTerminalFor( + "Keyspace", + namespace, name) + } + } + if refResourceTerminal { + return ackerr.ResourceReferenceTerminalFor( + "Keyspace", + namespace, name) + } + var refResourceSynced bool + for _, cond := range obj.Status.Conditions { + if cond.Type == ackv1alpha1.ConditionTypeResourceSynced && + cond.Status == corev1.ConditionTrue { + refResourceSynced = true + } + } + if !refResourceSynced { + return ackerr.ResourceReferenceNotSyncedFor( + "Keyspace", + namespace, name) + } + if obj.Spec.KeyspaceName == nil { + return ackerr.ResourceReferenceMissingTargetFieldFor( + "Keyspace", + namespace, name, + "Spec.KeyspaceName") + } + return nil +} diff --git a/pkg/version/version.go b/pkg/version/version.go index 2dc560c..f0d3c41 100644 --- a/pkg/version/version.go +++ b/pkg/version/version.go @@ -28,5 +28,5 @@ var ( // time and are immutable for the life of the generated code. const ( ACKGenerateVersion = "v0.62.1" - ACKGenerateBuildDate = "2026-08-13T00:59:18Z" + ACKGenerateBuildDate = "2026-08-13T21:57:46Z" )