From 56b40c7ff8462d417519798ba1b6b12e54c32e9f Mon Sep 17 00:00:00 2001 From: Philipp Matthes Date: Tue, 24 Mar 2026 16:58:36 +0100 Subject: [PATCH] Add aggregate metadata --- api/v1/hypervisor_types.go | 4 ++++ api/v1/zz_generated.deepcopy.go | 11 ++++++++++- applyconfigurations/api/v1/aggregate.go | 19 +++++++++++++++++-- .../crds/kvm.cloud.sap_hypervisors.yaml | 6 ++++++ internal/openstack/aggregates.go | 5 +++-- internal/openstack/aggregates_test.go | 11 +++++++++-- 6 files changed, 49 insertions(+), 7 deletions(-) diff --git a/api/v1/hypervisor_types.go b/api/v1/hypervisor_types.go index 97796dc..a615547 100644 --- a/api/v1/hypervisor_types.go +++ b/api/v1/hypervisor_types.go @@ -206,6 +206,10 @@ type Aggregate struct { // UUID is the unique identifier of the aggregate. UUID string `json:"uuid"` + + // Metadata is the metadata of the aggregate as key-value pairs. + // +kubebuilder:validation:Optional + Metadata map[string]string `json:"metadata,omitempty"` } type HyperVisorUpdateStatus struct { diff --git a/api/v1/zz_generated.deepcopy.go b/api/v1/zz_generated.deepcopy.go index 3dbdc81..802d5f3 100644 --- a/api/v1/zz_generated.deepcopy.go +++ b/api/v1/zz_generated.deepcopy.go @@ -30,6 +30,13 @@ import ( // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *Aggregate) DeepCopyInto(out *Aggregate) { *out = *in + if in.Metadata != nil { + in, out := &in.Metadata, &out.Metadata + *out = make(map[string]string, len(*in)) + for key, val := range *in { + (*out)[key] = val + } + } } // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Aggregate. @@ -385,7 +392,9 @@ func (in *HypervisorStatus) DeepCopyInto(out *HypervisorStatus) { if in.Aggregates != nil { in, out := &in.Aggregates, &out.Aggregates *out = make([]Aggregate, len(*in)) - copy(*out, *in) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } } if in.Conditions != nil { in, out := &in.Conditions, &out.Conditions diff --git a/applyconfigurations/api/v1/aggregate.go b/applyconfigurations/api/v1/aggregate.go index 7c008a8..5141a5e 100644 --- a/applyconfigurations/api/v1/aggregate.go +++ b/applyconfigurations/api/v1/aggregate.go @@ -5,8 +5,9 @@ package v1 // AggregateApplyConfiguration represents a declarative configuration of the Aggregate type for use // with apply. type AggregateApplyConfiguration struct { - Name *string `json:"name,omitempty"` - UUID *string `json:"uuid,omitempty"` + Name *string `json:"name,omitempty"` + UUID *string `json:"uuid,omitempty"` + Metadata map[string]string `json:"metadata,omitempty"` } // AggregateApplyConfiguration constructs a declarative configuration of the Aggregate type for use with @@ -30,3 +31,17 @@ func (b *AggregateApplyConfiguration) WithUUID(value string) *AggregateApplyConf b.UUID = &value return b } + +// WithMetadata puts the entries into the Metadata field in the declarative configuration +// and returns the receiver, so that objects can be build by chaining "With" function invocations. +// If called multiple times, the entries provided by each call will be put on the Metadata field, +// overwriting an existing map entries in Metadata field with the same key. +func (b *AggregateApplyConfiguration) WithMetadata(entries map[string]string) *AggregateApplyConfiguration { + if b.Metadata == nil && len(entries) > 0 { + b.Metadata = make(map[string]string, len(entries)) + } + for k, v := range entries { + b.Metadata[k] = v + } + return b +} diff --git a/charts/openstack-hypervisor-operator/crds/kvm.cloud.sap_hypervisors.yaml b/charts/openstack-hypervisor-operator/crds/kvm.cloud.sap_hypervisors.yaml index b479fc6..fb1e494 100644 --- a/charts/openstack-hypervisor-operator/crds/kvm.cloud.sap_hypervisors.yaml +++ b/charts/openstack-hypervisor-operator/crds/kvm.cloud.sap_hypervisors.yaml @@ -229,6 +229,12 @@ spec: description: Aggregate represents an OpenStack aggregate with its name and UUID. properties: + metadata: + additionalProperties: + type: string + description: Metadata is the metadata of the aggregate as key-value + pairs. + type: object name: description: Name is the name of the aggregate. type: string diff --git a/internal/openstack/aggregates.go b/internal/openstack/aggregates.go index e26bf37..079a26c 100644 --- a/internal/openstack/aggregates.go +++ b/internal/openstack/aggregates.go @@ -127,8 +127,9 @@ func ApplyAggregates(ctx context.Context, serviceClient *gophercloud.ServiceClie for _, name := range desiredAggregates { agg := aggregateMap[name] // exists as per "Verify all desired aggregates exist" check result = append(result, kvmv1.Aggregate{ - Name: agg.Name, - UUID: agg.UUID, + Name: agg.Name, + UUID: agg.UUID, + Metadata: agg.Metadata, }) } diff --git a/internal/openstack/aggregates_test.go b/internal/openstack/aggregates_test.go index d99fb40..03afb74 100644 --- a/internal/openstack/aggregates_test.go +++ b/internal/openstack/aggregates_test.go @@ -38,7 +38,8 @@ var _ = Describe("ApplyAggregates", func() { "deleted": false, "id": 1, "uuid": "uuid-agg1", - "hosts": ["test-host"] + "hosts": ["test-host"], + "metadata": {"key1": "value1", "key2": "value2"} }, { "name": "agg2", @@ -54,7 +55,8 @@ var _ = Describe("ApplyAggregates", func() { "deleted": false, "id": 3, "uuid": "uuid-agg3", - "hosts": [] + "hosts": [], + "metadata": {} } ] }` @@ -119,12 +121,17 @@ var _ = Describe("ApplyAggregates", func() { // Check we have the right aggregates by name and UUID names := make([]string, len(aggregates)) uuids := make([]string, len(aggregates)) + metadata := make([]map[string]string, len(aggregates)) for i, agg := range aggregates { names[i] = agg.Name uuids[i] = agg.UUID + metadata[i] = agg.Metadata } Expect(names).To(ConsistOf("agg1", "agg2", "agg3")) Expect(uuids).To(ConsistOf("uuid-agg1", "uuid-agg2", "uuid-agg3")) + Expect(metadata[0]).To(Equal(map[string]string{"key1": "value1", "key2": "value2"})) // agg1 metadata should be preserved + Expect(metadata[1]).To(BeNil()) // agg2 has no metadata field, should be nil + Expect(metadata[2]).To(BeEmpty()) // agg3 has empty metadata, should be preserved }) })