From 6aa97041ac42fd58e8d1103dc2b420b04497f531 Mon Sep 17 00:00:00 2001 From: Weslei Paulo Pereira Date: Sat, 10 Oct 2026 20:07:38 -0300 Subject: [PATCH] feat(api): add tags to Instance, VPC, Network and SecurityGroup specs Add an optional spec.tags string list to the Instance, VPC, Network and SecurityGroup specs so the control plane can persist user-defined resource tags on the CR and read them back through the REST API and UI. Without the field, strict field validation rejects spec.tags writes, which blocks core#256 (VM tags) and the upcoming network/VPC tagging. --- CHANGELOG.md | 4 ++++ api/v1alpha1/instance_types.go | 5 +++++ api/v1alpha1/network_types.go | 5 +++++ api/v1alpha1/securitygroup_types.go | 5 +++++ api/v1alpha1/vpc_types.go | 5 +++++ api/v1alpha1/zz_generated.deepcopy.go | 20 +++++++++++++++++++ .../crd/bases/virtfoundry.io_instances.yaml | 7 +++++++ config/crd/bases/virtfoundry.io_networks.yaml | 7 +++++++ .../bases/virtfoundry.io_securitygroups.yaml | 7 +++++++ config/crd/bases/virtfoundry.io_vpcs.yaml | 7 +++++++ 10 files changed, 72 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 3afce31..e4afc64 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,10 @@ Format based on [Keep a Changelog](https://keepachangelog.com/). Versioning alig ## [Unreleased] +### Added + +- `spec.tags` on `Instance`, `VPC`, `Network` and `SecurityGroup`. A free-form list of user labels persisted on the CR so the REST API and UI can round-trip resource tags. Previously the field did not exist in the schema, so control-plane writes of `spec.tags` were rejected by strict field validation (core#256). + ### Changed - The Helm chart is no longer kept in this repository. The only copy is `charts/virtfoundry-operator` in [virtfoundry/helm-charts](https://github.com/virtfoundry/helm-charts). `make verify-chart-rbac` checks that chart (sibling checkout or a clone of helm-charts main) against the ClusterRole generated from the kubebuilder markers. To add a permission, add the rule to the chart first, then the marker here. diff --git a/api/v1alpha1/instance_types.go b/api/v1alpha1/instance_types.go index 210ed39..620d9d6 100644 --- a/api/v1alpha1/instance_types.go +++ b/api/v1alpha1/instance_types.go @@ -75,6 +75,11 @@ type InstanceSpec struct { // +optional PowerState string `json:"powerState,omitempty"` + // Tags are free-form user labels persisted on the Instance and returned + // unchanged by the REST API and UI. + // +optional + Tags []string `json:"tags,omitempty"` + // Import optional CloudStack/other import identity. // +optional Import *ImportMeta `json:"import,omitempty"` diff --git a/api/v1alpha1/network_types.go b/api/v1alpha1/network_types.go index 7ea5800..9c175e7 100644 --- a/api/v1alpha1/network_types.go +++ b/api/v1alpha1/network_types.go @@ -40,6 +40,11 @@ type NetworkSpec struct { // +optional VPCRef *LocalObjectRef `json:"vpcRef,omitempty"` + // Tags are free-form user labels persisted on the Network and returned + // unchanged by the REST API and UI. + // +optional + Tags []string `json:"tags,omitempty"` + // Import optional CloudStack/other import identity. // +optional Import *ImportMeta `json:"import,omitempty"` diff --git a/api/v1alpha1/securitygroup_types.go b/api/v1alpha1/securitygroup_types.go index 67ba23a..5c4a3da 100644 --- a/api/v1alpha1/securitygroup_types.go +++ b/api/v1alpha1/securitygroup_types.go @@ -53,6 +53,11 @@ type SecurityGroupSpec struct { // +optional VPCRef *LocalObjectRef `json:"vpcRef,omitempty"` + // Tags are free-form user labels persisted on the SecurityGroup and + // returned unchanged by the REST API and UI. + // +optional + Tags []string `json:"tags,omitempty"` + // Rules firewall rules applied to attached instances. // +optional Rules []SecurityGroupRuleSpec `json:"rules,omitempty"` diff --git a/api/v1alpha1/vpc_types.go b/api/v1alpha1/vpc_types.go index 73e7495..5497893 100644 --- a/api/v1alpha1/vpc_types.go +++ b/api/v1alpha1/vpc_types.go @@ -28,6 +28,11 @@ type VPCSpec struct { // CIDR IPv4 network range for the VPC. CIDR string `json:"cidr"` + // Tags are free-form user labels persisted on the VPC and returned + // unchanged by the REST API and UI. + // +optional + Tags []string `json:"tags,omitempty"` + // Import optional CloudStack/other import identity. // +optional Import *ImportMeta `json:"import,omitempty"` diff --git a/api/v1alpha1/zz_generated.deepcopy.go b/api/v1alpha1/zz_generated.deepcopy.go index 77b85e3..9907d99 100644 --- a/api/v1alpha1/zz_generated.deepcopy.go +++ b/api/v1alpha1/zz_generated.deepcopy.go @@ -650,6 +650,11 @@ func (in *InstanceSpec) DeepCopyInto(out *InstanceSpec) { *out = new(SecretKeyRef) **out = **in } + if in.Tags != nil { + in, out := &in.Tags, &out.Tags + *out = make([]string, len(*in)) + copy(*out, *in) + } if in.Import != nil { in, out := &in.Import, &out.Import *out = new(ImportMeta) @@ -786,6 +791,11 @@ func (in *NetworkSpec) DeepCopyInto(out *NetworkSpec) { *out = new(LocalObjectRef) **out = **in } + if in.Tags != nil { + in, out := &in.Tags, &out.Tags + *out = make([]string, len(*in)) + copy(*out, *in) + } if in.Import != nil { in, out := &in.Import, &out.Import *out = new(ImportMeta) @@ -1220,6 +1230,11 @@ func (in *SecurityGroupSpec) DeepCopyInto(out *SecurityGroupSpec) { *out = new(LocalObjectRef) **out = **in } + if in.Tags != nil { + in, out := &in.Tags, &out.Tags + *out = make([]string, len(*in)) + copy(*out, *in) + } if in.Rules != nil { in, out := &in.Rules, &out.Rules *out = make([]SecurityGroupRuleSpec, len(*in)) @@ -1631,6 +1646,11 @@ func (in *VPCList) DeepCopyObject() runtime.Object { // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *VPCSpec) DeepCopyInto(out *VPCSpec) { *out = *in + if in.Tags != nil { + in, out := &in.Tags, &out.Tags + *out = make([]string, len(*in)) + copy(*out, *in) + } if in.Import != nil { in, out := &in.Import, &out.Import *out = new(ImportMeta) diff --git a/config/crd/bases/virtfoundry.io_instances.yaml b/config/crd/bases/virtfoundry.io_instances.yaml index d7f936d..d506dcf 100644 --- a/config/crd/bases/virtfoundry.io_instances.yaml +++ b/config/crd/bases/virtfoundry.io_instances.yaml @@ -139,6 +139,13 @@ spec: - name type: object type: array + tags: + description: |- + Tags are free-form user labels persisted on the Instance and returned + unchanged by the REST API and UI. + items: + type: string + type: array templateRef: description: TemplateRef names the Template CR. properties: diff --git a/config/crd/bases/virtfoundry.io_networks.yaml b/config/crd/bases/virtfoundry.io_networks.yaml index 11a347c..351140e 100644 --- a/config/crd/bases/virtfoundry.io_networks.yaml +++ b/config/crd/bases/virtfoundry.io_networks.yaml @@ -64,6 +64,13 @@ spec: - isolated - shared type: string + tags: + description: |- + Tags are free-form user labels persisted on the Network and returned + unchanged by the REST API and UI. + items: + type: string + type: array vpcRef: description: VPCRef names the parent VPC CR. properties: diff --git a/config/crd/bases/virtfoundry.io_securitygroups.yaml b/config/crd/bases/virtfoundry.io_securitygroups.yaml index 75ecc38..40eb9fb 100644 --- a/config/crd/bases/virtfoundry.io_securitygroups.yaml +++ b/config/crd/bases/virtfoundry.io_securitygroups.yaml @@ -74,6 +74,13 @@ spec: - protocol type: object type: array + tags: + description: |- + Tags are free-form user labels persisted on the SecurityGroup and + returned unchanged by the REST API and UI. + items: + type: string + type: array vpcRef: description: VPCRef names the parent VPC CR. properties: diff --git a/config/crd/bases/virtfoundry.io_vpcs.yaml b/config/crd/bases/virtfoundry.io_vpcs.yaml index b190443..b8abf90 100644 --- a/config/crd/bases/virtfoundry.io_vpcs.yaml +++ b/config/crd/bases/virtfoundry.io_vpcs.yaml @@ -65,6 +65,13 @@ spec: name: description: Name display label shown in UI/REST. type: string + tags: + description: |- + Tags are free-form user labels persisted on the VPC and returned + unchanged by the REST API and UI. + items: + type: string + type: array required: - cidr - name