From f2f643287aad082caff31646ac03152fa5844212 Mon Sep 17 00:00:00 2001 From: Steven Miller Date: Mon, 9 Mar 2026 15:06:10 -0400 Subject: [PATCH 1/2] rename tag fields from metadata to tags --- cmd/api/api/builds.go | 20 +- cmd/api/api/devices.go | 8 +- cmd/api/api/images.go | 12 +- cmd/api/api/ingress.go | 10 +- cmd/api/api/instances.go | 20 +- cmd/api/api/metadata.go | 26 +- cmd/api/api/snapshots.go | 14 +- cmd/api/api/volumes.go | 24 +- lib/builds/manager.go | 6 +- lib/builds/storage.go | 4 +- lib/builds/storage_test.go | 10 +- lib/builds/types.go | 6 +- lib/devices/manager.go | 4 +- lib/devices/manager_test.go | 8 +- lib/devices/types.go | 28 +- lib/images/manager.go | 10 +- lib/images/storage.go | 6 +- lib/images/storage_test.go | 10 +- lib/images/types.go | 6 +- lib/ingress/manager.go | 6 +- lib/ingress/manager_test.go | 12 +- lib/ingress/storage.go | 2 +- lib/ingress/types.go | 12 +- lib/instances/create.go | 8 +- lib/instances/filter_test.go | 74 ++--- lib/instances/fork.go | 8 +- lib/instances/fork_test.go | 6 +- lib/instances/snapshot.go | 6 +- lib/instances/types.go | 26 +- lib/oapi/oapi.go | 598 +++++++++++++++++------------------ lib/snapshot/store_test.go | 12 +- lib/snapshot/types.go | 16 +- lib/tags/clone.go | 10 +- lib/tags/constraints.go | 2 +- lib/tags/errors.go | 2 +- lib/tags/match.go | 4 +- lib/tags/types.go | 4 +- lib/tags/validate.go | 24 +- lib/tags/validate_test.go | 12 +- lib/volumes/manager.go | 10 +- lib/volumes/manager_test.go | 10 +- lib/volumes/storage.go | 2 +- lib/volumes/types.go | 18 +- openapi.yaml | 110 +++---- 44 files changed, 613 insertions(+), 613 deletions(-) diff --git a/cmd/api/api/builds.go b/cmd/api/api/builds.go index b74ce279..1f6d4fc2 100644 --- a/cmd/api/api/builds.go +++ b/cmd/api/api/builds.go @@ -31,7 +31,7 @@ func (s *ApiService) ListBuilds(ctx context.Context, request oapi.ListBuildsRequ oapiBuilds := make([]oapi.Build, 0, len(domainBuilds)) for _, b := range domainBuilds { - if b == nil || !matchesMetadataFilter(b.Metadata, request.Params.Metadata) { + if b == nil || !matchesTagsFilter(b.Tags, request.Params.Tags) { continue } oapiBuilds = append(oapiBuilds, buildToOAPI(b)) @@ -50,7 +50,7 @@ func (s *ApiService) CreateBuild(ctx context.Context, request oapi.CreateBuildRe var timeoutSeconds, memoryMB, cpus int var isAdminBuild bool var secrets []builds.SecretRef - var metadata map[string]string + var resourceTags map[string]string for { part, err := request.Body.NextPart() @@ -174,22 +174,22 @@ func (s *ApiService) CreateBuild(ctx context.Context, request oapi.CreateBuildRe }, nil } imageName = string(data) - case "metadata": + case "tags": data, err := io.ReadAll(part) if err != nil { return oapi.CreateBuild400JSONResponse{ Code: "invalid_request", - Message: "failed to read metadata field", + Message: "failed to read tags field", }, nil } - parsed, err := parseMetadataJSON(string(data)) + parsed, err := parseTagsJSON(string(data)) if err != nil { return oapi.CreateBuild400JSONResponse{ Code: "invalid_request", - Message: "metadata must be a JSON object with string key-value pairs", + Message: "tags must be a JSON object with string key-value pairs", }, nil } - metadata = parsed + resourceTags = parsed } part.Close() } @@ -224,7 +224,7 @@ func (s *ApiService) CreateBuild(ctx context.Context, request oapi.CreateBuildRe IsAdminBuild: isAdminBuild, GlobalCacheKey: globalCacheKey, ImageName: imageName, - Metadata: metadata, + Tags: resourceTags, } // Apply build policy if any field was provided @@ -245,7 +245,7 @@ func (s *ApiService) CreateBuild(ctx context.Context, request oapi.CreateBuildRe build, err := s.BuildManager.CreateBuild(ctx, domainReq, sourceData) if err != nil { switch { - case errors.Is(err, tags.ErrInvalidMetadata): + case errors.Is(err, tags.ErrInvalidTags): return oapi.CreateBuild400JSONResponse{ Code: "invalid_request", Message: err.Error(), @@ -386,7 +386,7 @@ func buildToOAPI(b *builds.Build) oapi.Build { oapiBuild := oapi.Build{ Id: b.ID, Status: oapi.BuildStatus(b.Status), - Metadata: toOAPIMetadata(b.Metadata), + Tags: toOAPITags(b.Tags), QueuePosition: b.QueuePosition, ImageDigest: b.ImageDigest, ImageRef: b.ImageRef, diff --git a/cmd/api/api/devices.go b/cmd/api/api/devices.go index 1d744c9f..153625c1 100644 --- a/cmd/api/api/devices.go +++ b/cmd/api/api/devices.go @@ -21,7 +21,7 @@ func (s *ApiService) ListDevices(ctx context.Context, request oapi.ListDevicesRe result := make([]oapi.Device, 0, len(deviceList)) for _, d := range deviceList { - if !matchesMetadataFilter(d.Metadata, request.Params.Metadata) { + if !matchesTagsFilter(d.Tags, request.Params.Tags) { continue } result = append(result, deviceToOAPI(d)) @@ -57,13 +57,13 @@ func (s *ApiService) CreateDevice(ctx context.Context, request oapi.CreateDevice req := devices.CreateDeviceRequest{ Name: name, PCIAddress: request.Body.PciAddress, - Metadata: toMapMetadata(request.Body.Metadata), + Tags: toMapTags(request.Body.Tags), } device, err := s.DeviceManager.CreateDevice(ctx, req) if err != nil { switch { - case errors.Is(err, tags.ErrInvalidMetadata): + case errors.Is(err, tags.ErrInvalidTags): return oapi.CreateDevice400JSONResponse{ Code: "invalid_request", Message: err.Error(), @@ -152,7 +152,7 @@ func deviceToOAPI(d devices.Device) oapi.Device { Id: d.Id, Name: &d.Name, Type: deviceType, - Metadata: toOAPIMetadata(d.Metadata), + Tags: toOAPITags(d.Tags), PciAddress: d.PCIAddress, VendorId: d.VendorID, DeviceId: d.DeviceID, diff --git a/cmd/api/api/images.go b/cmd/api/api/images.go index 37844c36..9fdfa608 100644 --- a/cmd/api/api/images.go +++ b/cmd/api/api/images.go @@ -25,7 +25,7 @@ func (s *ApiService) ListImages(ctx context.Context, request oapi.ListImagesRequ oapiImages := make([]oapi.Image, 0, len(domainImages)) for _, img := range domainImages { - if !matchesMetadataFilter(img.Metadata, request.Params.Metadata) { + if !matchesTagsFilter(img.Tags, request.Params.Tags) { continue } oapiImages = append(oapiImages, imageToOAPI(img)) @@ -37,14 +37,14 @@ func (s *ApiService) CreateImage(ctx context.Context, request oapi.CreateImageRe log := logger.FromContext(ctx) domainReq := images.CreateImageRequest{ - Name: request.Body.Name, - Metadata: toMapMetadata(request.Body.Metadata), + Name: request.Body.Name, + Tags: toMapTags(request.Body.Tags), } img, err := s.ImageManager.CreateImage(ctx, domainReq) if err != nil { switch { - case errors.Is(err, tags.ErrInvalidMetadata): + case errors.Is(err, tags.ErrInvalidTags): return oapi.CreateImage400JSONResponse{ Code: "invalid_request", Message: err.Error(), @@ -126,8 +126,8 @@ func imageToOAPI(img images.Image) oapi.Image { if len(img.Env) > 0 { oapiImg.Env = &img.Env } - if len(img.Metadata) > 0 { - oapiImg.Metadata = toOAPIMetadata(img.Metadata) + if len(img.Tags) > 0 { + oapiImg.Tags = toOAPITags(img.Tags) } if img.WorkingDir != "" { oapiImg.WorkingDir = &img.WorkingDir diff --git a/cmd/api/api/ingress.go b/cmd/api/api/ingress.go index f0f67522..3eb86706 100644 --- a/cmd/api/api/ingress.go +++ b/cmd/api/api/ingress.go @@ -25,7 +25,7 @@ func (s *ApiService) ListIngresses(ctx context.Context, request oapi.ListIngress oapiIngresses := make([]oapi.Ingress, 0, len(ingresses)) for _, ing := range ingresses { - if !matchesMetadataFilter(ing.Metadata, request.Params.Metadata) { + if !matchesTagsFilter(ing.Tags, request.Params.Tags) { continue } oapiIngresses = append(oapiIngresses, ingressToOAPI(ing)) @@ -40,9 +40,9 @@ func (s *ApiService) CreateIngress(ctx context.Context, request oapi.CreateIngre // Convert OAPI request to domain request domainReq := ingress.CreateIngressRequest{ - Name: request.Body.Name, - Metadata: toMapMetadata(request.Body.Metadata), - Rules: make([]ingress.IngressRule, len(request.Body.Rules)), + Name: request.Body.Name, + Tags: toMapTags(request.Body.Tags), + Rules: make([]ingress.IngressRule, len(request.Body.Rules)), } for i, rule := range request.Body.Rules { @@ -184,7 +184,7 @@ func ingressToOAPI(ing ingress.Ingress) oapi.Ingress { return oapi.Ingress{ Id: ing.ID, Name: ing.Name, - Metadata: toOAPIMetadata(ing.Metadata), + Tags: toOAPITags(ing.Tags), Rules: rules, CreatedAt: ing.CreatedAt, } diff --git a/cmd/api/api/instances.go b/cmd/api/api/instances.go index aa3b4524..b771addf 100644 --- a/cmd/api/api/instances.go +++ b/cmd/api/api/instances.go @@ -21,20 +21,20 @@ import ( "github.com/samber/lo" ) -// ListInstances lists instances, optionally filtered by state and/or metadata. +// ListInstances lists instances, optionally filtered by state and/or tags. func (s *ApiService) ListInstances(ctx context.Context, request oapi.ListInstancesRequestObject) (oapi.ListInstancesResponseObject, error) { log := logger.FromContext(ctx) // Convert OAPI params to domain filter var filter *instances.ListInstancesFilter - if request.Params.State != nil || request.Params.Metadata != nil { + if request.Params.State != nil || request.Params.Tags != nil { filter = &instances.ListInstancesFilter{} if request.Params.State != nil { state := instances.State(*request.Params.State) filter.State = &state } - if request.Params.Metadata != nil { - filter.Metadata = toMapMetadata(request.Params.Metadata) + if request.Params.Tags != nil { + filter.Tags = toMapTags(request.Params.Tags) } } @@ -125,9 +125,9 @@ func (s *ApiService) CreateInstance(ctx context.Context, request oapi.CreateInst env = *request.Body.Env } - metadata := make(map[string]string) - if request.Body.Metadata != nil { - metadata = toMapMetadata(request.Body.Metadata) + resourceTags := make(map[string]string) + if request.Body.Tags != nil { + resourceTags = toMapTags(request.Body.Tags) } // Parse network enabled (default: true) @@ -253,7 +253,7 @@ func (s *ApiService) CreateInstance(ctx context.Context, request oapi.CreateInst NetworkBandwidthDownload: networkBandwidthDownload, NetworkBandwidthUpload: networkBandwidthUpload, Env: env, - Metadata: metadata, + Tags: resourceTags, NetworkEnabled: networkEnabled, Devices: deviceRefs, Volumes: volumes, @@ -855,8 +855,8 @@ func instanceToOAPI(inst instances.Instance) oapi.Instance { oapiInst.Env = &inst.Env } - if len(inst.Metadata) > 0 { - oapiInst.Metadata = toOAPIMetadata(inst.Metadata) + if len(inst.Tags) > 0 { + oapiInst.Tags = toOAPITags(inst.Tags) } // Convert volume attachments diff --git a/cmd/api/api/metadata.go b/cmd/api/api/metadata.go index 8539a542..ed7e48a2 100644 --- a/cmd/api/api/metadata.go +++ b/cmd/api/api/metadata.go @@ -8,35 +8,35 @@ import ( "github.com/kernel/hypeman/lib/tags" ) -func toMapMetadata(metadata *oapi.MetadataTags) map[string]string { - if metadata == nil { +func toMapTags(resourceTags *oapi.Tags) map[string]string { + if resourceTags == nil { return nil } - return tags.Clone(map[string]string(*metadata)) + return tags.Clone(map[string]string(*resourceTags)) } -func toOAPIMetadata(metadata map[string]string) *oapi.MetadataTags { - if len(metadata) == 0 { +func toOAPITags(resourceTags map[string]string) *oapi.Tags { + if len(resourceTags) == 0 { return nil } - cloned := oapi.MetadataTags(tags.Clone(metadata)) + cloned := oapi.Tags(tags.Clone(resourceTags)) return &cloned } -func matchesMetadataFilter(metadata map[string]string, filter *oapi.MetadataTags) bool { +func matchesTagsFilter(resourceTags map[string]string, filter *oapi.Tags) bool { if filter == nil { return true } - return tags.Matches(metadata, map[string]string(*filter)) + return tags.Matches(resourceTags, map[string]string(*filter)) } -func parseMetadataJSON(raw string) (map[string]string, error) { +func parseTagsJSON(raw string) (map[string]string, error) { if raw == "" { return nil, nil } - var metadata map[string]string - if err := json.Unmarshal([]byte(raw), &metadata); err != nil { - return nil, fmt.Errorf("parse metadata JSON: %w", err) + var resourceTags map[string]string + if err := json.Unmarshal([]byte(raw), &resourceTags); err != nil { + return nil, fmt.Errorf("parse tags JSON: %w", err) } - return metadata, nil + return resourceTags, nil } diff --git a/cmd/api/api/snapshots.go b/cmd/api/api/snapshots.go index b5a70d0c..2609c8be 100644 --- a/cmd/api/api/snapshots.go +++ b/cmd/api/api/snapshots.go @@ -29,9 +29,9 @@ func (s *ApiService) CreateInstanceSnapshot(ctx context.Context, request oapi.Cr } result, err := s.InstanceManager.CreateSnapshot(ctx, inst.Id, instances.CreateSnapshotRequest{ - Kind: instances.SnapshotKind(request.Body.Kind), - Name: name, - Metadata: toMapMetadata(request.Body.Metadata), + Kind: instances.SnapshotKind(request.Body.Kind), + Name: name, + Tags: toMapTags(request.Body.Tags), }) if err != nil { log := logger.FromContext(ctx) @@ -105,10 +105,10 @@ func (s *ApiService) ListSnapshots(ctx context.Context, request oapi.ListSnapsho if request.Params.Name != nil { filter.Name = request.Params.Name } - if request.Params.Metadata != nil { - filter.Metadata = toMapMetadata(request.Params.Metadata) + if request.Params.Tags != nil { + filter.Tags = toMapTags(request.Params.Tags) } - if filter.SourceInstanceID == nil && filter.Kind == nil && filter.Name == nil && len(filter.Metadata) == 0 { + if filter.SourceInstanceID == nil && filter.Kind == nil && filter.Name == nil && len(filter.Tags) == 0 { filter = nil } @@ -199,7 +199,7 @@ func snapshotToOAPI(snapshot instances.Snapshot) oapi.Snapshot { out := oapi.Snapshot{ Id: snapshot.Id, Kind: kind, - Metadata: toOAPIMetadata(snapshot.Metadata), + Tags: toOAPITags(snapshot.Tags), SourceInstanceId: snapshot.SourceInstanceID, SourceInstanceName: snapshot.SourceName, SourceHypervisor: sourceHypervisor, diff --git a/cmd/api/api/volumes.go b/cmd/api/api/volumes.go index 7efddd9b..0e450da6 100644 --- a/cmd/api/api/volumes.go +++ b/cmd/api/api/volumes.go @@ -26,7 +26,7 @@ func (s *ApiService) ListVolumes(ctx context.Context, request oapi.ListVolumesRe oapiVols := make([]oapi.Volume, 0, len(domainVols)) for _, vol := range domainVols { - if !matchesMetadataFilter(vol.Metadata, request.Params.Metadata) { + if !matchesTagsFilter(vol.Tags, request.Params.Tags) { continue } oapiVols = append(oapiVols, volumeToOAPI(vol)) @@ -47,10 +47,10 @@ func (s *ApiService) CreateVolume(ctx context.Context, request oapi.CreateVolume } domainReq := volumes.CreateVolumeRequest{ - Name: request.Body.Name, - SizeGb: request.Body.SizeGb, - Id: request.Body.Id, - Metadata: toMapMetadata(request.Body.Metadata), + Name: request.Body.Name, + SizeGb: request.Body.SizeGb, + Id: request.Body.Id, + Tags: toMapTags(request.Body.Tags), } vol, err := s.VolumeManager.CreateVolume(ctx, domainReq) @@ -61,7 +61,7 @@ func (s *ApiService) CreateVolume(ctx context.Context, request oapi.CreateVolume Message: "volume with this ID already exists", }, nil } - if errors.Is(err, tags.ErrInvalidMetadata) { + if errors.Is(err, tags.ErrInvalidTags) { return oapi.CreateVolume400JSONResponse{ Code: "invalid_request", Message: err.Error(), @@ -99,10 +99,10 @@ func (s *ApiService) CreateVolumeFromArchive(ctx context.Context, request oapi.C // Create the volume from archive - stream directly without buffering domainReq := volumes.CreateVolumeFromArchiveRequest{ - Name: request.Params.Name, - SizeGb: request.Params.SizeGb, - Id: request.Params.Id, - Metadata: toMapMetadata(request.Params.Metadata), + Name: request.Params.Name, + SizeGb: request.Params.SizeGb, + Id: request.Params.Id, + Tags: toMapTags(request.Params.Tags), } vol, err := s.VolumeManager.CreateVolumeFromArchive(ctx, domainReq, request.Body) @@ -119,7 +119,7 @@ func (s *ApiService) CreateVolumeFromArchive(ctx context.Context, request oapi.C Message: "volume with this ID already exists", }, nil } - if errors.Is(err, tags.ErrInvalidMetadata) { + if errors.Is(err, tags.ErrInvalidTags) { return oapi.CreateVolumeFromArchive400JSONResponse{ Code: "invalid_request", Message: err.Error(), @@ -186,7 +186,7 @@ func volumeToOAPI(vol volumes.Volume) oapi.Volume { Id: vol.Id, Name: vol.Name, SizeGb: vol.SizeGb, - Metadata: toOAPIMetadata(vol.Metadata), + Tags: toOAPITags(vol.Tags), CreatedAt: vol.CreatedAt, } diff --git a/lib/builds/manager.go b/lib/builds/manager.go index 41632e82..39097efc 100644 --- a/lib/builds/manager.go +++ b/lib/builds/manager.go @@ -371,10 +371,10 @@ func (m *manager) waitForBuilderImageReady(ctx context.Context, imageRef string) // CreateBuild starts a new build job func (m *manager) CreateBuild(ctx context.Context, req CreateBuildRequest, sourceData []byte) (*Build, error) { m.logger.Info("creating build") - if err := tags.Validate(req.Metadata); err != nil { + if err := tags.Validate(req.Tags); err != nil { return nil, err } - req.Metadata = tags.Clone(req.Metadata) + req.Tags = tags.Clone(req.Tags) // Apply defaults to build policy policy := req.BuildPolicy @@ -395,7 +395,7 @@ func (m *manager) CreateBuild(ctx context.Context, req CreateBuildRequest, sourc meta := &buildMetadata{ ID: id, Status: StatusQueued, - Metadata: tags.Clone(req.Metadata), + Tags: tags.Clone(req.Tags), Request: &req, CreatedAt: time.Now(), } diff --git a/lib/builds/storage.go b/lib/builds/storage.go index 59ff4fed..ef03b483 100644 --- a/lib/builds/storage.go +++ b/lib/builds/storage.go @@ -15,7 +15,7 @@ import ( type buildMetadata struct { ID string `json:"id"` Status string `json:"status"` - Metadata tags.Metadata `json:"metadata,omitempty"` + Tags tags.Tags `json:"tags,omitempty"` Request *CreateBuildRequest `json:"request,omitempty"` ImageDigest *string `json:"image_digest,omitempty"` ImageRef *string `json:"image_ref,omitempty"` @@ -33,7 +33,7 @@ func (m *buildMetadata) toBuild() *Build { return &Build{ ID: m.ID, Status: m.Status, - Metadata: tags.Clone(m.Metadata), + Tags: tags.Clone(m.Tags), ImageDigest: m.ImageDigest, ImageRef: m.ImageRef, Error: m.Error, diff --git a/lib/builds/storage_test.go b/lib/builds/storage_test.go index 45b6db9a..e1e82182 100644 --- a/lib/builds/storage_test.go +++ b/lib/builds/storage_test.go @@ -20,7 +20,7 @@ func TestBuildMetadataReadWrite_MetadataRoundTrip(t *testing.T) { meta := &buildMetadata{ ID: id, Status: StatusQueued, - Metadata: map[string]string{"team": "backend", "env": "staging"}, + Tags: map[string]string{"team": "backend", "env": "staging"}, CreatedAt: time.Now().UTC().Truncate(time.Second), } @@ -28,11 +28,11 @@ func TestBuildMetadataReadWrite_MetadataRoundTrip(t *testing.T) { loaded, err := readMetadata(p, id) require.NoError(t, err) - require.Equal(t, map[string]string{"team": "backend", "env": "staging"}, loaded.Metadata) + require.Equal(t, map[string]string{"team": "backend", "env": "staging"}, loaded.Tags) build := loaded.toBuild() - require.Equal(t, map[string]string{"team": "backend", "env": "staging"}, build.Metadata) + require.Equal(t, map[string]string{"team": "backend", "env": "staging"}, build.Tags) - loaded.Metadata["team"] = "mutated" - require.Equal(t, "backend", build.Metadata["team"]) + loaded.Tags["team"] = "mutated" + require.Equal(t, "backend", build.Tags["team"]) } diff --git a/lib/builds/types.go b/lib/builds/types.go index e312d9d6..0b309e82 100644 --- a/lib/builds/types.go +++ b/lib/builds/types.go @@ -23,7 +23,7 @@ const ( type Build struct { ID string `json:"id"` Status string `json:"status"` - Metadata tags.Metadata `json:"metadata,omitempty"` + Tags tags.Tags `json:"tags,omitempty"` QueuePosition *int `json:"queue_position,omitempty"` ImageDigest *string `json:"image_digest,omitempty"` ImageRef *string `json:"image_ref,omitempty"` @@ -73,8 +73,8 @@ type CreateBuildRequest struct { // When set, the image is pushed to {registry}/{image_name} instead of {registry}/builds/{id}. ImageName string `json:"image_name,omitempty"` - // Metadata is optional user-defined key-value metadata for the build resource. - Metadata tags.Metadata `json:"metadata,omitempty"` + // Tags are optional user-defined key-value tags for the build resource. + Tags tags.Tags `json:"tags,omitempty"` } // BuildPolicy defines resource limits and network policy for a build diff --git a/lib/devices/manager.go b/lib/devices/manager.go index a152daa7..91e47ae7 100644 --- a/lib/devices/manager.go +++ b/lib/devices/manager.go @@ -141,7 +141,7 @@ func (m *manager) ListAvailableDevices(ctx context.Context) ([]AvailableDevice, func (m *manager) CreateDevice(ctx context.Context, req CreateDeviceRequest) (*Device, error) { log := logger.FromContext(ctx) - if err := tags.Validate(req.Metadata); err != nil { + if err := tags.Validate(req.Tags); err != nil { return nil, err } @@ -189,7 +189,7 @@ func (m *manager) CreateDevice(ctx context.Context, req CreateDeviceRequest) (*D Id: id, Name: name, Type: DetermineDeviceType(deviceInfo), - Metadata: tags.Clone(req.Metadata), + Tags: tags.Clone(req.Tags), PCIAddress: req.PCIAddress, VendorID: deviceInfo.VendorID, DeviceID: deviceInfo.DeviceID, diff --git a/lib/devices/manager_test.go b/lib/devices/manager_test.go index 5a6f856b..2dd5c815 100644 --- a/lib/devices/manager_test.go +++ b/lib/devices/manager_test.go @@ -176,7 +176,7 @@ func TestSaveLoadDevice_MetadataRoundTrip(t *testing.T) { Id: "dev-meta-1", Name: "meta-device", Type: DeviceTypeGeneric, - Metadata: map[string]string{"team": "platform", "env": "prod"}, + Tags: map[string]string{"team": "platform", "env": "prod"}, PCIAddress: "0000:00:00.0", VendorID: "1234", DeviceID: "5678", @@ -188,8 +188,8 @@ func TestSaveLoadDevice_MetadataRoundTrip(t *testing.T) { loaded, err := mgr.loadDevice(device.Id) require.NoError(t, err) - require.Equal(t, map[string]string{"team": "platform", "env": "prod"}, loaded.Metadata) + require.Equal(t, map[string]string{"team": "platform", "env": "prod"}, loaded.Tags) - device.Metadata["team"] = "mutated" - require.Equal(t, "platform", loaded.Metadata["team"]) + device.Tags["team"] = "mutated" + require.Equal(t, "platform", loaded.Tags["team"]) } diff --git a/lib/devices/types.go b/lib/devices/types.go index 3dd5a95f..57d87059 100644 --- a/lib/devices/types.go +++ b/lib/devices/types.go @@ -17,24 +17,24 @@ const ( // Device represents a registered PCI device for passthrough type Device struct { - Id string `json:"id"` // cuid2 identifier - Name string `json:"name"` // user-provided globally unique name - Type DeviceType `json:"type"` // gpu or pci - Metadata tags.Metadata `json:"metadata,omitempty"` // user-defined key-value metadata - PCIAddress string `json:"pci_address"` // e.g., "0000:a2:00.0" - VendorID string `json:"vendor_id"` // e.g., "10de" - DeviceID string `json:"device_id"` // e.g., "27b8" - IOMMUGroup int `json:"iommu_group"` // IOMMU group number - BoundToVFIO bool `json:"bound_to_vfio"` // whether device is bound to vfio-pci - AttachedTo *string `json:"attached_to"` // instance ID if attached, nil otherwise - CreatedAt time.Time `json:"created_at"` + Id string `json:"id"` // cuid2 identifier + Name string `json:"name"` // user-provided globally unique name + Type DeviceType `json:"type"` // gpu or pci + Tags tags.Tags `json:"tags,omitempty"` // user-defined key-value tags + PCIAddress string `json:"pci_address"` // e.g., "0000:a2:00.0" + VendorID string `json:"vendor_id"` // e.g., "10de" + DeviceID string `json:"device_id"` // e.g., "27b8" + IOMMUGroup int `json:"iommu_group"` // IOMMU group number + BoundToVFIO bool `json:"bound_to_vfio"` // whether device is bound to vfio-pci + AttachedTo *string `json:"attached_to"` // instance ID if attached, nil otherwise + CreatedAt time.Time `json:"created_at"` } // CreateDeviceRequest is the request to register a new device type CreateDeviceRequest struct { - Name string `json:"name,omitempty"` // optional: globally unique name (auto-generated if not provided) - PCIAddress string `json:"pci_address"` // required: PCI address (e.g., "0000:a2:00.0") - Metadata tags.Metadata `json:"metadata,omitempty"` + Name string `json:"name,omitempty"` // optional: globally unique name (auto-generated if not provided) + PCIAddress string `json:"pci_address"` // required: PCI address (e.g., "0000:a2:00.0") + Tags tags.Tags `json:"tags,omitempty"` } // AvailableDevice represents a PCI device discovered on the host diff --git a/lib/images/manager.go b/lib/images/manager.go index e74a60de..44959d39 100644 --- a/lib/images/manager.go +++ b/lib/images/manager.go @@ -105,7 +105,7 @@ func (m *manager) ListImages(ctx context.Context) ([]Image, error) { } func (m *manager) CreateImage(ctx context.Context, req CreateImageRequest) (*Image, error) { - if err := tags.Validate(req.Metadata); err != nil { + if err := tags.Validate(req.Tags); err != nil { return nil, err } @@ -209,10 +209,10 @@ func (m *manager) createAndQueueImage(ref *ResolvedRef, req CreateImageRequest) Digest: ref.Digest(), Status: StatusPending, Request: &CreateImageRequest{ - Name: ref.String(), - Metadata: tags.Clone(req.Metadata), + Name: ref.String(), + Tags: tags.Clone(req.Tags), }, - Metadata: tags.Clone(req.Metadata), + Tags: tags.Clone(req.Tags), CreatedAt: time.Now(), } @@ -222,7 +222,7 @@ func (m *manager) createAndQueueImage(ref *ResolvedRef, req CreateImageRequest) } // Enqueue the build using digest as the queue key for deduplication - queuePos := m.queue.Enqueue(ref.Digest(), CreateImageRequest{Name: ref.String(), Metadata: tags.Clone(req.Metadata)}, func() { + queuePos := m.queue.Enqueue(ref.Digest(), CreateImageRequest{Name: ref.String(), Tags: tags.Clone(req.Tags)}, func() { m.buildImage(context.Background(), ref) }) diff --git a/lib/images/storage.go b/lib/images/storage.go index 364e6c15..01b2a2ea 100644 --- a/lib/images/storage.go +++ b/lib/images/storage.go @@ -22,7 +22,7 @@ type imageMetadata struct { Entrypoint []string `json:"entrypoint,omitempty"` Cmd []string `json:"cmd,omitempty"` Env map[string]string `json:"env,omitempty"` - Metadata tags.Metadata `json:"metadata,omitempty"` + Tags tags.Tags `json:"tags,omitempty"` WorkingDir string `json:"working_dir,omitempty"` CreatedAt time.Time `json:"created_at"` } @@ -50,8 +50,8 @@ func (m *imageMetadata) toImage() *Image { if len(m.Env) > 0 { img.Env = m.Env } - if len(m.Metadata) > 0 { - img.Metadata = tags.Clone(m.Metadata) + if len(m.Tags) > 0 { + img.Tags = tags.Clone(m.Tags) } if m.WorkingDir != "" { img.WorkingDir = m.WorkingDir diff --git a/lib/images/storage_test.go b/lib/images/storage_test.go index 0473b827..f8ec44dc 100644 --- a/lib/images/storage_test.go +++ b/lib/images/storage_test.go @@ -13,7 +13,7 @@ func TestImageMetadataToImage_ClonesMetadata(t *testing.T) { Name: "docker.io/library/alpine:latest", Digest: "sha256:abc", Status: StatusReady, - Metadata: map[string]string{"team": "backend", "env": "staging"}, + Tags: map[string]string{"team": "backend", "env": "staging"}, SizeBytes: 123, CreatedAt: createdAt, } @@ -21,12 +21,12 @@ func TestImageMetadataToImage_ClonesMetadata(t *testing.T) { img := source.toImage() require.Equal(t, source.Name, img.Name) require.Equal(t, source.Digest, img.Digest) - require.Equal(t, map[string]string{"team": "backend", "env": "staging"}, img.Metadata) + require.Equal(t, map[string]string{"team": "backend", "env": "staging"}, img.Tags) require.NotNil(t, img.SizeBytes) require.Equal(t, int64(123), *img.SizeBytes) - source.Metadata["team"] = "mutated" - require.Equal(t, "backend", img.Metadata["team"]) + source.Tags["team"] = "mutated" + require.Equal(t, "backend", img.Tags["team"]) } func TestImageMetadataToImage_EmptyMetadataOmitted(t *testing.T) { @@ -37,5 +37,5 @@ func TestImageMetadataToImage_EmptyMetadataOmitted(t *testing.T) { CreatedAt: time.Now().UTC(), }).toImage() - require.Nil(t, img.Metadata) + require.Nil(t, img.Tags) } diff --git a/lib/images/types.go b/lib/images/types.go index b16ff943..5d02c7ce 100644 --- a/lib/images/types.go +++ b/lib/images/types.go @@ -17,13 +17,13 @@ type Image struct { Entrypoint []string Cmd []string Env map[string]string - Metadata tags.Metadata + Tags tags.Tags WorkingDir string CreatedAt time.Time } // CreateImageRequest represents a request to create an image type CreateImageRequest struct { - Name string - Metadata tags.Metadata + Name string + Tags tags.Tags } diff --git a/lib/ingress/manager.go b/lib/ingress/manager.go index fedc453e..239d7f9a 100644 --- a/lib/ingress/manager.go +++ b/lib/ingress/manager.go @@ -351,7 +351,7 @@ func (m *manager) Create(ctx context.Context, req CreateIngressRequest) (*Ingres ingress := Ingress{ ID: id, Name: req.Name, - Metadata: tags.Clone(req.Metadata), + Tags: tags.Clone(req.Tags), Rules: req.Rules, CreatedAt: time.Now().UTC(), } @@ -377,7 +377,7 @@ func (m *manager) Create(ctx context.Context, req CreateIngressRequest) (*Ingres stored := &storedIngress{ ID: ingress.ID, Name: ingress.Name, - Metadata: tags.Clone(ingress.Metadata), + Tags: tags.Clone(ingress.Tags), Rules: ingress.Rules, CreatedAt: ingress.CreatedAt.Format(time.RFC3339), } @@ -625,7 +625,7 @@ func storedToIngress(stored *storedIngress) *Ingress { return &Ingress{ ID: stored.ID, Name: stored.Name, - Metadata: tags.Clone(stored.Metadata), + Tags: tags.Clone(stored.Tags), Rules: stored.Rules, CreatedAt: createdAt, } diff --git a/lib/ingress/manager_test.go b/lib/ingress/manager_test.go index 9059d367..2dd151dd 100644 --- a/lib/ingress/manager_test.go +++ b/lib/ingress/manager_test.go @@ -194,8 +194,8 @@ func TestCreateIngress_MetadataRoundTrip(t *testing.T) { reqMetadata := map[string]string{"team": "api", "env": "staging"} req := CreateIngressRequest{ - Name: "metadata-ingress", - Metadata: reqMetadata, + Name: "metadata-ingress", + Tags: reqMetadata, Rules: []IngressRule{ { Match: IngressMatch{Hostname: "metadata.example.com"}, @@ -206,19 +206,19 @@ func TestCreateIngress_MetadataRoundTrip(t *testing.T) { ing, err := manager.Create(ctx, req) require.NoError(t, err) - require.Equal(t, map[string]string{"team": "api", "env": "staging"}, ing.Metadata) + require.Equal(t, map[string]string{"team": "api", "env": "staging"}, ing.Tags) reqMetadata["team"] = "mutated" - require.Equal(t, "api", ing.Metadata["team"]) + require.Equal(t, "api", ing.Tags["team"]) got, err := manager.Get(ctx, ing.ID) require.NoError(t, err) - require.Equal(t, map[string]string{"team": "api", "env": "staging"}, got.Metadata) + require.Equal(t, map[string]string{"team": "api", "env": "staging"}, got.Tags) listed, err := manager.List(ctx) require.NoError(t, err) require.Len(t, listed, 1) - require.Equal(t, map[string]string{"team": "api", "env": "staging"}, listed[0].Metadata) + require.Equal(t, map[string]string{"team": "api", "env": "staging"}, listed[0].Tags) } func TestCreateIngress_InvalidName(t *testing.T) { diff --git a/lib/ingress/storage.go b/lib/ingress/storage.go index ca6a78bc..f65bae20 100644 --- a/lib/ingress/storage.go +++ b/lib/ingress/storage.go @@ -17,7 +17,7 @@ import ( type storedIngress struct { ID string `json:"id"` Name string `json:"name"` - Metadata tags.Metadata `json:"metadata,omitempty"` + Tags tags.Tags `json:"tags,omitempty"` Rules []IngressRule `json:"rules"` CreatedAt string `json:"created_at"` // RFC3339 format } diff --git a/lib/ingress/types.go b/lib/ingress/types.go index f1f6cee6..4e9f1058 100644 --- a/lib/ingress/types.go +++ b/lib/ingress/types.go @@ -19,8 +19,8 @@ type Ingress struct { // Name is a human-readable name for the ingress. Name string `json:"name"` - // Metadata is optional user-defined key-value metadata. - Metadata tags.Metadata `json:"metadata,omitempty"` + // Tags are optional user-defined key-value tags. + Tags tags.Tags `json:"tags,omitempty"` // Rules define the routing rules for this ingress. Rules []IngressRule `json:"rules"` @@ -177,8 +177,8 @@ type CreateIngressRequest struct { // Name is a human-readable name for the ingress. Name string `json:"name"` - // Metadata is optional user-defined key-value metadata. - Metadata tags.Metadata `json:"metadata,omitempty"` + // Tags are optional user-defined key-value tags. + Tags tags.Tags `json:"tags,omitempty"` // Rules define the routing rules for this ingress. Rules []IngressRule `json:"rules"` @@ -193,8 +193,8 @@ func (r *CreateIngressRequest) Validate() error { if len(r.Rules) == 0 { return &ValidationError{Field: "rules", Message: "at least one rule is required"} } - if err := tags.Validate(r.Metadata); err != nil { - return &ValidationError{Field: "metadata", Message: err.Error()} + if err := tags.Validate(r.Tags); err != nil { + return &ValidationError{Field: "tags", Message: err.Error()} } for i, rule := range r.Rules { diff --git a/lib/instances/create.go b/lib/instances/create.go index 1f762ff3..c9dfe244 100644 --- a/lib/instances/create.go +++ b/lib/instances/create.go @@ -162,8 +162,8 @@ func (m *manager) createInstance( if req.Env == nil { req.Env = make(map[string]string) } - if req.Metadata == nil { - req.Metadata = make(map[string]string) + if req.Tags == nil { + req.Tags = make(map[string]string) } // 7. Determine network based on NetworkEnabled flag @@ -294,7 +294,7 @@ func (m *manager) createInstance( NetworkBandwidthUpload: req.NetworkBandwidthUpload, // Will be set by caller if using resource manager DiskIOBps: req.DiskIOBps, // Will be set by caller if using resource manager Env: req.Env, - Metadata: tags.Clone(req.Metadata), + Tags: tags.Clone(req.Tags), NetworkEnabled: req.NetworkEnabled, CreatedAt: time.Now(), StartedAt: nil, @@ -467,7 +467,7 @@ func validateCreateRequest(req CreateInstanceRequest) error { if req.Vcpus < 0 { return fmt.Errorf("vcpus cannot be negative") } - if err := tags.Validate(req.Metadata); err != nil { + if err := tags.Validate(req.Tags); err != nil { return fmt.Errorf("%w: %v", ErrInvalidRequest, err) } diff --git a/lib/instances/filter_test.go b/lib/instances/filter_test.go index 905d15db..ade7a6c7 100644 --- a/lib/instances/filter_test.go +++ b/lib/instances/filter_test.go @@ -24,7 +24,7 @@ func TestListInstancesFilter_Matches(t *testing.T) { Id: "inst-1", Name: "web-server", Image: "nginx:latest", - Metadata: map[string]string{ + Tags: map[string]string{ "team": "backend", "env": "staging", }, @@ -58,30 +58,30 @@ func TestListInstancesFilter_Matches(t *testing.T) { want: false, }, { - name: "single metadata key matches", + name: "single tag key matches", filter: &ListInstancesFilter{ - Metadata: map[string]string{"team": "backend"}, + Tags: map[string]string{"team": "backend"}, }, want: true, }, { - name: "single metadata key wrong value", + name: "single tag key wrong value", filter: &ListInstancesFilter{ - Metadata: map[string]string{"team": "frontend"}, + Tags: map[string]string{"team": "frontend"}, }, want: false, }, { - name: "metadata key does not exist", + name: "tag key does not exist", filter: &ListInstancesFilter{ - Metadata: map[string]string{"project": "alpha"}, + Tags: map[string]string{"project": "alpha"}, }, want: false, }, { - name: "multiple metadata keys all match", + name: "multiple tag keys all match", filter: &ListInstancesFilter{ - Metadata: map[string]string{ + Tags: map[string]string{ "team": "backend", "env": "staging", }, @@ -89,9 +89,9 @@ func TestListInstancesFilter_Matches(t *testing.T) { want: true, }, { - name: "multiple metadata keys partial match", + name: "multiple tag keys partial match", filter: &ListInstancesFilter{ - Metadata: map[string]string{ + Tags: map[string]string{ "team": "backend", "env": "production", }, @@ -99,26 +99,26 @@ func TestListInstancesFilter_Matches(t *testing.T) { want: false, }, { - name: "state and metadata combined match", + name: "state and tags combined match", filter: &ListInstancesFilter{ - State: &running, - Metadata: map[string]string{"team": "backend"}, + State: &running, + Tags: map[string]string{"team": "backend"}, }, want: true, }, { - name: "state matches but metadata does not", + name: "state matches but tags do not", filter: &ListInstancesFilter{ - State: &running, - Metadata: map[string]string{"team": "frontend"}, + State: &running, + Tags: map[string]string{"team": "frontend"}, }, want: false, }, { - name: "metadata matches but state does not", + name: "tags match but state does not", filter: &ListInstancesFilter{ - State: &stopped, - Metadata: map[string]string{"team": "backend"}, + State: &stopped, + Tags: map[string]string{"team": "backend"}, }, want: false, }, @@ -136,16 +136,16 @@ func TestListInstancesFilter_Matches_NilMetadata(t *testing.T) { t.Parallel() inst := &Instance{ StoredMetadata: StoredMetadata{ - Id: "inst-2", - Metadata: nil, + Id: "inst-2", + Tags: nil, }, State: StateRunning, } filter := &ListInstancesFilter{ - Metadata: map[string]string{"team": "backend"}, + Tags: map[string]string{"team": "backend"}, } - assert.False(t, filter.Matches(inst), "should not match when instance has no metadata") + assert.False(t, filter.Matches(inst), "should not match when instance has no tags") } // TestListInstances_WithFilter exercises the full ListInstances path using @@ -157,13 +157,13 @@ func TestListInstances_WithFilter(t *testing.T) { mgr := &manager{paths: p} - // Create three instances with different metadata on disk + // Create three instances with different tags on disk instances := []StoredMetadata{ { Id: "inst-a", Name: "web", Image: "nginx:latest", - Metadata: map[string]string{"team": "backend", "env": "prod"}, + Tags: map[string]string{"team": "backend", "env": "prod"}, CreatedAt: time.Now(), HypervisorType: hypervisor.TypeCloudHypervisor, SocketPath: "/nonexistent/a.sock", @@ -173,7 +173,7 @@ func TestListInstances_WithFilter(t *testing.T) { Id: "inst-b", Name: "worker", Image: "python:3", - Metadata: map[string]string{"team": "backend", "env": "staging"}, + Tags: map[string]string{"team": "backend", "env": "staging"}, CreatedAt: time.Now(), HypervisorType: hypervisor.TypeCloudHypervisor, SocketPath: "/nonexistent/b.sock", @@ -183,7 +183,7 @@ func TestListInstances_WithFilter(t *testing.T) { Id: "inst-c", Name: "frontend", Image: "node:20", - Metadata: map[string]string{"team": "frontend", "env": "prod"}, + Tags: map[string]string{"team": "frontend", "env": "prod"}, CreatedAt: time.Now(), HypervisorType: hypervisor.TypeCloudHypervisor, SocketPath: "/nonexistent/c.sock", @@ -206,9 +206,9 @@ func TestListInstances_WithFilter(t *testing.T) { assert.Len(t, result, 3) }) - t.Run("filter by single metadata key", func(t *testing.T) { + t.Run("filter by single tag key", func(t *testing.T) { result, err := mgr.ListInstances(ctx, &ListInstancesFilter{ - Metadata: map[string]string{"team": "backend"}, + Tags: map[string]string{"team": "backend"}, }) require.NoError(t, err) assert.Len(t, result, 2) @@ -216,18 +216,18 @@ func TestListInstances_WithFilter(t *testing.T) { assert.ElementsMatch(t, []string{"web", "worker"}, names) }) - t.Run("filter by two metadata keys", func(t *testing.T) { + t.Run("filter by two tag keys", func(t *testing.T) { result, err := mgr.ListInstances(ctx, &ListInstancesFilter{ - Metadata: map[string]string{"team": "backend", "env": "prod"}, + Tags: map[string]string{"team": "backend", "env": "prod"}, }) require.NoError(t, err) require.Len(t, result, 1) assert.Equal(t, "web", result[0].Name) }) - t.Run("filter by metadata with no matches", func(t *testing.T) { + t.Run("filter by tags with no matches", func(t *testing.T) { result, err := mgr.ListInstances(ctx, &ListInstancesFilter{ - Metadata: map[string]string{"team": "devops"}, + Tags: map[string]string{"team": "devops"}, }) require.NoError(t, err) assert.Empty(t, result) @@ -250,11 +250,11 @@ func TestListInstances_WithFilter(t *testing.T) { assert.Empty(t, result) }) - t.Run("filter by state and metadata combined", func(t *testing.T) { + t.Run("filter by state and tags combined", func(t *testing.T) { stopped := StateStopped result, err := mgr.ListInstances(ctx, &ListInstancesFilter{ - State: &stopped, - Metadata: map[string]string{"env": "prod"}, + State: &stopped, + Tags: map[string]string{"env": "prod"}, }) require.NoError(t, err) assert.Len(t, result, 2) diff --git a/lib/instances/fork.go b/lib/instances/fork.go index 60d3a5cf..5ce36778 100644 --- a/lib/instances/fork.go +++ b/lib/instances/fork.go @@ -467,10 +467,10 @@ func cloneStoredMetadataForFork(src StoredMetadata) StoredMetadata { dst.Env[k] = v } } - if src.Metadata != nil { - dst.Metadata = make(map[string]string, len(src.Metadata)) - for k, v := range src.Metadata { - dst.Metadata[k] = v + if src.Tags != nil { + dst.Tags = make(map[string]string, len(src.Tags)) + for k, v := range src.Tags { + dst.Tags[k] = v } } if src.Volumes != nil { diff --git a/lib/instances/fork_test.go b/lib/instances/fork_test.go index 78ae1822..484cae2c 100644 --- a/lib/instances/fork_test.go +++ b/lib/instances/fork_test.go @@ -224,7 +224,7 @@ func TestCloneStoredMetadataForFork_DeepCopiesReferenceFields(t *testing.T) { src := StoredMetadata{ Env: map[string]string{"A": "1"}, - Metadata: map[string]string{"m": "x"}, + Tags: map[string]string{"m": "x"}, Volumes: []VolumeAttachment{{VolumeID: "vol-1", MountPath: "/data"}}, Devices: []string{"0000:01:00.0"}, Entrypoint: []string{"/bin/sh", "-c"}, @@ -239,7 +239,7 @@ func TestCloneStoredMetadataForFork_DeepCopiesReferenceFields(t *testing.T) { require.Equal(t, src, cloned) cloned.Env["A"] = "2" - cloned.Metadata["m"] = "y" + cloned.Tags["m"] = "y" cloned.Volumes[0].MountPath = "/mnt" cloned.Devices[0] = "0000:02:00.0" cloned.Entrypoint[0] = "/usr/bin/env" @@ -251,7 +251,7 @@ func TestCloneStoredMetadataForFork_DeepCopiesReferenceFields(t *testing.T) { *cloned.StoppedAt = now require.Equal(t, "1", src.Env["A"]) - require.Equal(t, "x", src.Metadata["m"]) + require.Equal(t, "x", src.Tags["m"]) require.Equal(t, "/data", src.Volumes[0].MountPath) require.Equal(t, "0000:01:00.0", src.Devices[0]) require.Equal(t, "/bin/sh", src.Entrypoint[0]) diff --git a/lib/instances/snapshot.go b/lib/instances/snapshot.go index 4df46078..fcf65e79 100644 --- a/lib/instances/snapshot.go +++ b/lib/instances/snapshot.go @@ -125,7 +125,7 @@ func (m *manager) createSnapshot(ctx context.Context, id string, req CreateSnaps Id: snapshotID, Name: req.Name, Kind: req.Kind, - Metadata: tags.Clone(req.Metadata), + Tags: tags.Clone(req.Tags), SourceInstanceID: stored.Id, SourceName: stored.Name, SourceHypervisor: stored.HypervisorType, @@ -157,7 +157,7 @@ func (m *manager) createSnapshot(ctx context.Context, id string, req CreateSnaps Id: snapshotID, Name: req.Name, Kind: req.Kind, - Metadata: tags.Clone(req.Metadata), + Tags: tags.Clone(req.Tags), SourceInstanceID: stored.Id, SourceName: stored.Name, SourceHypervisor: stored.HypervisorType, @@ -461,7 +461,7 @@ func validateCreateSnapshotRequest(req CreateSnapshotRequest) error { if req.Kind != SnapshotKindStandby && req.Kind != SnapshotKindStopped { return fmt.Errorf("%w: kind must be one of %s, %s", ErrInvalidRequest, SnapshotKindStandby, SnapshotKindStopped) } - if err := tags.Validate(req.Metadata); err != nil { + if err := tags.Validate(req.Tags); err != nil { return fmt.Errorf("%w: %v", ErrInvalidRequest, err) } if req.Name != "" { diff --git a/lib/instances/types.go b/lib/instances/types.go index bb548d6a..edd85b07 100644 --- a/lib/instances/types.go +++ b/lib/instances/types.go @@ -48,10 +48,10 @@ type StoredMetadata struct { // Configuration Env map[string]string - Metadata tags.Metadata // User-defined key-value metadata - NetworkEnabled bool // Whether instance has networking enabled (uses default network) - IP string // Assigned IP address (empty if NetworkEnabled=false) - MAC string // Assigned MAC address (empty if NetworkEnabled=false) + Tags tags.Tags // User-defined key-value tags + NetworkEnabled bool // Whether instance has networking enabled (uses default network) + IP string // Assigned IP address (empty if NetworkEnabled=false) + MAC string // Assigned MAC address (empty if NetworkEnabled=false) // Attached volumes Volumes []VolumeAttachment // Volumes attached to this instance @@ -119,8 +119,8 @@ func (i *Instance) GetHypervisorType() string { // ListInstancesFilter contains optional filters for listing instances. // All fields are ANDed together: an instance must match every specified filter. type ListInstancesFilter struct { - State *State // Filter by instance state - Metadata tags.Metadata // Filter by metadata key-value pairs (all must match) + State *State // Filter by instance state + Tags tags.Tags // Filter by tag key-value pairs (all must match) } // Matches returns true if the given instance satisfies all filter criteria. @@ -131,11 +131,11 @@ func (f *ListInstancesFilter) Matches(inst *Instance) bool { if f.State != nil && inst.State != *f.State { return false } - for k, v := range f.Metadata { - if inst.Metadata == nil { + for k, v := range f.Tags { + if inst.Tags == nil { return false } - if actual, ok := inst.Metadata[k]; !ok || actual != v { + if actual, ok := inst.Tags[k]; !ok || actual != v { return false } } @@ -159,7 +159,7 @@ type CreateInstanceRequest struct { NetworkBandwidthUpload int64 // Upload rate limit bytes/sec (0 = auto, proportional to CPU) DiskIOBps int64 // Disk I/O rate limit bytes/sec (0 = auto, proportional to CPU) Env map[string]string // Optional environment variables - Metadata tags.Metadata // Optional user-defined key-value metadata + Tags tags.Tags // Optional user-defined key-value tags NetworkEnabled bool // Whether to enable networking (uses default network) Devices []string // Device IDs or names to attach (GPU passthrough) Volumes []VolumeAttachment // Volumes to attach at creation time @@ -202,9 +202,9 @@ type ListSnapshotsFilter = snapshot.ListSnapshotsFilter // CreateSnapshotRequest is the domain request for creating a snapshot. type CreateSnapshotRequest struct { - Kind SnapshotKind // Required: Standby or Stopped - Name string // Optional: unique per source instance - Metadata tags.Metadata // Optional user-defined key-value metadata + Kind SnapshotKind // Required: Standby or Stopped + Name string // Optional: unique per source instance + Tags tags.Tags // Optional user-defined key-value tags } // RestoreSnapshotRequest is the domain request for restoring a snapshot in-place. diff --git a/lib/oapi/oapi.go b/lib/oapi/oapi.go index 674b6e6d..34c0ac65 100644 --- a/lib/oapi/oapi.go +++ b/lib/oapi/oapi.go @@ -207,10 +207,7 @@ type Build struct { ImageDigest *string `json:"image_digest"` // ImageRef Full image reference (only when status is ready) - ImageRef *string `json:"image_ref"` - - // Metadata User-defined key-value metadata tags. - Metadata *MetadataTags `json:"metadata,omitempty"` + ImageRef *string `json:"image_ref"` Provenance *BuildProvenance `json:"provenance,omitempty"` // QueuePosition Position in build queue (only when status is queued) @@ -221,6 +218,9 @@ type Build struct { // Status Build job status Status BuildStatus `json:"status"` + + // Tags User-defined key-value tags. + Tags *Tags `json:"tags,omitempty"` } // BuildEvent defines model for BuildEvent. @@ -264,35 +264,35 @@ type BuildStatus string // CreateDeviceRequest defines model for CreateDeviceRequest. type CreateDeviceRequest struct { - // Metadata User-defined key-value metadata tags. - Metadata *MetadataTags `json:"metadata,omitempty"` - // Name Optional globally unique device name. If not provided, a name is auto-generated from the PCI address (e.g., "pci-0000-a2-00-0") Name *string `json:"name,omitempty"` // PciAddress PCI address of the device (required, e.g., "0000:a2:00.0") PciAddress string `json:"pci_address"` + + // Tags User-defined key-value tags. + Tags *Tags `json:"tags,omitempty"` } // CreateImageRequest defines model for CreateImageRequest. type CreateImageRequest struct { - // Metadata User-defined key-value metadata tags. - Metadata *MetadataTags `json:"metadata,omitempty"` - // Name OCI image reference (e.g., docker.io/library/nginx:latest) Name string `json:"name"` + + // Tags User-defined key-value tags. + Tags *Tags `json:"tags,omitempty"` } // CreateIngressRequest defines model for CreateIngressRequest. type CreateIngressRequest struct { - // Metadata User-defined key-value metadata tags. - Metadata *MetadataTags `json:"metadata,omitempty"` - // Name Human-readable name (lowercase letters, digits, and dashes only; cannot start or end with a dash) Name string `json:"name"` // Rules Routing rules for this ingress Rules []IngressRule `json:"rules"` + + // Tags User-defined key-value tags. + Tags *Tags `json:"tags,omitempty"` } // CreateInstanceRequest defines model for CreateInstanceRequest. @@ -324,9 +324,6 @@ type CreateInstanceRequest struct { // Image OCI image reference Image string `json:"image"` - // Metadata User-defined key-value metadata tags. - Metadata *MetadataTags `json:"metadata,omitempty"` - // Name Human-readable name (lowercase letters, digits, and dashes only; cannot start or end with a dash) Name string `json:"name"` @@ -359,6 +356,9 @@ type CreateInstanceRequest struct { // Recommended for workloads that don't need kernel module compilation. SkipKernelHeaders *bool `json:"skip_kernel_headers,omitempty"` + // Tags User-defined key-value tags. + Tags *Tags `json:"tags,omitempty"` + // Vcpus Number of virtual CPUs Vcpus *int `json:"vcpus,omitempty"` @@ -374,11 +374,11 @@ type CreateSnapshotRequest struct { // Kind Snapshot capture kind Kind SnapshotKind `json:"kind"` - // Metadata User-defined key-value metadata tags. - Metadata *MetadataTags `json:"metadata,omitempty"` - // Name Optional snapshot name (lowercase letters, digits, and dashes only; cannot start or end with a dash) Name *string `json:"name,omitempty"` + + // Tags User-defined key-value tags. + Tags *Tags `json:"tags,omitempty"` } // CreateVolumeRequest defines model for CreateVolumeRequest. @@ -386,14 +386,14 @@ type CreateVolumeRequest struct { // Id Optional custom identifier (auto-generated if not provided) Id *string `json:"id,omitempty"` - // Metadata User-defined key-value metadata tags. - Metadata *MetadataTags `json:"metadata,omitempty"` - // Name Volume name Name string `json:"name"` // SizeGb Size in gigabytes SizeGb int `json:"size_gb"` + + // Tags User-defined key-value tags. + Tags *Tags `json:"tags,omitempty"` } // Device defines model for Device. @@ -418,15 +418,15 @@ type Device struct { // IommuGroup IOMMU group number IommuGroup int `json:"iommu_group"` - // Metadata User-defined key-value metadata tags. - Metadata *MetadataTags `json:"metadata,omitempty"` - // Name Device name (user-provided or auto-generated from PCI address) Name *string `json:"name,omitempty"` // PciAddress PCI address PciAddress string `json:"pci_address"` + // Tags User-defined key-value tags. + Tags *Tags `json:"tags,omitempty"` + // Type Type of PCI device Type DeviceType `json:"type"` @@ -574,9 +574,6 @@ type Image struct { // Error Error message if status is failed Error *string `json:"error"` - // Metadata User-defined key-value metadata tags. - Metadata *MetadataTags `json:"metadata,omitempty"` - // Name Normalized OCI image reference (tag or digest) Name string `json:"name"` @@ -589,6 +586,9 @@ type Image struct { // Status Build status Status ImageStatus `json:"status"` + // Tags User-defined key-value tags. + Tags *Tags `json:"tags,omitempty"` + // WorkingDir Working directory from container metadata WorkingDir *string `json:"working_dir"` } @@ -604,14 +604,14 @@ type Ingress struct { // Id Auto-generated unique identifier Id string `json:"id"` - // Metadata User-defined key-value metadata tags. - Metadata *MetadataTags `json:"metadata,omitempty"` - // Name Human-readable name Name string `json:"name"` // Rules Routing rules for this ingress Rules []IngressRule `json:"rules"` + + // Tags User-defined key-value tags. + Tags *Tags `json:"tags,omitempty"` } // IngressMatch defines model for IngressMatch. @@ -689,9 +689,6 @@ type Instance struct { // Image OCI image reference Image string `json:"image"` - // Metadata User-defined key-value metadata tags. - Metadata *MetadataTags `json:"metadata,omitempty"` - // Name Human-readable name Name string `json:"name"` @@ -741,6 +738,9 @@ type Instance struct { // StoppedAt Stop timestamp (RFC3339) StoppedAt *time.Time `json:"stopped_at"` + // Tags User-defined key-value tags. + Tags *Tags `json:"tags,omitempty"` + // Vcpus Number of virtual CPUs Vcpus *int `json:"vcpus,omitempty"` @@ -803,9 +803,6 @@ type InstanceStats struct { NetworkTxBytes int64 `json:"network_tx_bytes"` } -// MetadataTags User-defined key-value metadata tags. -type MetadataTags map[string]string - // PassthroughDevice Physical GPU available for passthrough type PassthroughDevice struct { // Available Whether this GPU is available (not attached to an instance) @@ -932,9 +929,6 @@ type Snapshot struct { // Kind Snapshot capture kind Kind SnapshotKind `json:"kind"` - // Metadata User-defined key-value metadata tags. - Metadata *MetadataTags `json:"metadata,omitempty"` - // Name Optional human-readable snapshot name (unique per source instance) Name *string `json:"name"` @@ -949,6 +943,9 @@ type Snapshot struct { // SourceInstanceName Source instance name at snapshot creation time SourceInstanceName string `json:"source_instance_name"` + + // Tags User-defined key-value tags. + Tags *Tags `json:"tags,omitempty"` } // SnapshotSourceHypervisor Source instance hypervisor at snapshot creation time @@ -960,6 +957,9 @@ type SnapshotKind string // SnapshotTargetState Target state when restoring or forking from a snapshot type SnapshotTargetState string +// Tags User-defined key-value tags. +type Tags map[string]string + // Volume defines model for Volume. type Volume struct { // Attachments List of current attachments (empty if not attached) @@ -971,14 +971,14 @@ type Volume struct { // Id Unique identifier Id string `json:"id"` - // Metadata User-defined key-value metadata tags. - Metadata *MetadataTags `json:"metadata,omitempty"` - // Name Volume name Name string `json:"name"` // SizeGb Size in gigabytes SizeGb int `json:"size_gb"` + + // Tags User-defined key-value tags. + Tags *Tags `json:"tags,omitempty"` } // VolumeAttachment defines model for VolumeAttachment. @@ -1013,8 +1013,8 @@ type VolumeMount struct { // ListBuildsParams defines parameters for ListBuilds. type ListBuildsParams struct { - // Metadata Filter builds by metadata key-value pairs. - Metadata *MetadataTags `json:"metadata,omitempty"` + // Tags Filter builds by tag key-value pairs. + Tags *Tags `json:"tags,omitempty"` } // CreateBuildMultipartBody defines parameters for CreateBuild. @@ -1047,10 +1047,6 @@ type CreateBuildMultipartBody struct { // MemoryMb Memory limit for builder VM in MB (default 2048) MemoryMb *int `json:"memory_mb,omitempty"` - // Metadata JSON object of metadata tags. - // Example: {"team":"backend","env":"staging"} - Metadata *string `json:"metadata,omitempty"` - // Secrets JSON array of secret references to inject during build. // Each object has "id" (required) for use with --mount=type=secret,id=... // Example: [{"id": "npm_token"}, {"id": "github_token"}] @@ -1059,6 +1055,10 @@ type CreateBuildMultipartBody struct { // Source Source tarball (tar.gz) containing application code and optionally a Dockerfile Source openapi_types.File `json:"source"` + // Tags JSON object of tags. + // Example: {"team":"backend","env":"staging"} + Tags *string `json:"tags,omitempty"` + // TimeoutSeconds Build timeout (default 600) TimeoutSeconds *int `json:"timeout_seconds,omitempty"` } @@ -1071,20 +1071,20 @@ type GetBuildEventsParams struct { // ListDevicesParams defines parameters for ListDevices. type ListDevicesParams struct { - // Metadata Filter devices by metadata key-value pairs. - Metadata *MetadataTags `json:"metadata,omitempty"` + // Tags Filter devices by tag key-value pairs. + Tags *Tags `json:"tags,omitempty"` } // ListImagesParams defines parameters for ListImages. type ListImagesParams struct { - // Metadata Filter images by metadata key-value pairs. - Metadata *MetadataTags `json:"metadata,omitempty"` + // Tags Filter images by tag key-value pairs. + Tags *Tags `json:"tags,omitempty"` } // ListIngressesParams defines parameters for ListIngresses. type ListIngressesParams struct { - // Metadata Filter ingresses by metadata key-value pairs. - Metadata *MetadataTags `json:"metadata,omitempty"` + // Tags Filter ingresses by tag key-value pairs. + Tags *Tags `json:"tags,omitempty"` } // ListInstancesParams defines parameters for ListInstances. @@ -1092,10 +1092,10 @@ type ListInstancesParams struct { // State Filter instances by state (e.g., Running, Stopped) State *InstanceState `form:"state,omitempty" json:"state,omitempty"` - // Metadata Filter instances by metadata key-value pairs. Uses deepObject style: - // ?metadata[team]=backend&metadata[env]=staging + // Tags Filter instances by tag key-value pairs. Uses deepObject style: + // ?tags[team]=backend&tags[env]=staging // Multiple entries are ANDed together. All specified key-value pairs must match. - Metadata *MetadataTags `json:"metadata,omitempty"` + Tags *Tags `json:"tags,omitempty"` } // GetInstanceLogsParams defines parameters for GetInstanceLogs. @@ -1145,14 +1145,14 @@ type ListSnapshotsParams struct { // Name Filter snapshots by snapshot name Name *string `form:"name,omitempty" json:"name,omitempty"` - // Metadata Filter snapshots by metadata key-value pairs. - Metadata *MetadataTags `json:"metadata,omitempty"` + // Tags Filter snapshots by tag key-value pairs. + Tags *Tags `json:"tags,omitempty"` } // ListVolumesParams defines parameters for ListVolumes. type ListVolumesParams struct { - // Metadata Filter volumes by metadata key-value pairs. - Metadata *MetadataTags `json:"metadata,omitempty"` + // Tags Filter volumes by tag key-value pairs. + Tags *Tags `json:"tags,omitempty"` } // CreateVolumeFromArchiveParams defines parameters for CreateVolumeFromArchive. @@ -1166,8 +1166,8 @@ type CreateVolumeFromArchiveParams struct { // Id Optional custom volume ID (auto-generated if not provided) Id *string `form:"id,omitempty" json:"id,omitempty"` - // Metadata Metadata tags for the created volume. - Metadata *MetadataTags `json:"metadata,omitempty"` + // Tags Tags for the created volume. + Tags *Tags `json:"tags,omitempty"` } // CreateBuildMultipartRequestBody defines body for CreateBuild for multipart/form-data ContentType. @@ -2131,9 +2131,9 @@ func NewListBuildsRequest(server string, params *ListBuildsParams) (*http.Reques if params != nil { queryValues := queryURL.Query() - if params.Metadata != nil { + if params.Tags != nil { - if queryFrag, err := runtime.StyleParamWithLocation("deepObject", true, "metadata", runtime.ParamLocationQuery, *params.Metadata); err != nil { + if queryFrag, err := runtime.StyleParamWithLocation("deepObject", true, "tags", runtime.ParamLocationQuery, *params.Tags); err != nil { return nil, err } else if parsed, err := url.ParseQuery(queryFrag); err != nil { return nil, err @@ -2333,9 +2333,9 @@ func NewListDevicesRequest(server string, params *ListDevicesParams) (*http.Requ if params != nil { queryValues := queryURL.Query() - if params.Metadata != nil { + if params.Tags != nil { - if queryFrag, err := runtime.StyleParamWithLocation("deepObject", true, "metadata", runtime.ParamLocationQuery, *params.Metadata); err != nil { + if queryFrag, err := runtime.StyleParamWithLocation("deepObject", true, "tags", runtime.ParamLocationQuery, *params.Tags); err != nil { return nil, err } else if parsed, err := url.ParseQuery(queryFrag); err != nil { return nil, err @@ -2544,9 +2544,9 @@ func NewListImagesRequest(server string, params *ListImagesParams) (*http.Reques if params != nil { queryValues := queryURL.Query() - if params.Metadata != nil { + if params.Tags != nil { - if queryFrag, err := runtime.StyleParamWithLocation("deepObject", true, "metadata", runtime.ParamLocationQuery, *params.Metadata); err != nil { + if queryFrag, err := runtime.StyleParamWithLocation("deepObject", true, "tags", runtime.ParamLocationQuery, *params.Tags); err != nil { return nil, err } else if parsed, err := url.ParseQuery(queryFrag); err != nil { return nil, err @@ -2701,9 +2701,9 @@ func NewListIngressesRequest(server string, params *ListIngressesParams) (*http. if params != nil { queryValues := queryURL.Query() - if params.Metadata != nil { + if params.Tags != nil { - if queryFrag, err := runtime.StyleParamWithLocation("deepObject", true, "metadata", runtime.ParamLocationQuery, *params.Metadata); err != nil { + if queryFrag, err := runtime.StyleParamWithLocation("deepObject", true, "tags", runtime.ParamLocationQuery, *params.Tags); err != nil { return nil, err } else if parsed, err := url.ParseQuery(queryFrag); err != nil { return nil, err @@ -2874,9 +2874,9 @@ func NewListInstancesRequest(server string, params *ListInstancesParams) (*http. } - if params.Metadata != nil { + if params.Tags != nil { - if queryFrag, err := runtime.StyleParamWithLocation("deepObject", true, "metadata", runtime.ParamLocationQuery, *params.Metadata); err != nil { + if queryFrag, err := runtime.StyleParamWithLocation("deepObject", true, "tags", runtime.ParamLocationQuery, *params.Tags); err != nil { return nil, err } else if parsed, err := url.ParseQuery(queryFrag); err != nil { return nil, err @@ -3688,9 +3688,9 @@ func NewListSnapshotsRequest(server string, params *ListSnapshotsParams) (*http. } - if params.Metadata != nil { + if params.Tags != nil { - if queryFrag, err := runtime.StyleParamWithLocation("deepObject", true, "metadata", runtime.ParamLocationQuery, *params.Metadata); err != nil { + if queryFrag, err := runtime.StyleParamWithLocation("deepObject", true, "tags", runtime.ParamLocationQuery, *params.Tags); err != nil { return nil, err } else if parsed, err := url.ParseQuery(queryFrag); err != nil { return nil, err @@ -3852,9 +3852,9 @@ func NewListVolumesRequest(server string, params *ListVolumesParams) (*http.Requ if params != nil { queryValues := queryURL.Query() - if params.Metadata != nil { + if params.Tags != nil { - if queryFrag, err := runtime.StyleParamWithLocation("deepObject", true, "metadata", runtime.ParamLocationQuery, *params.Metadata); err != nil { + if queryFrag, err := runtime.StyleParamWithLocation("deepObject", true, "tags", runtime.ParamLocationQuery, *params.Tags); err != nil { return nil, err } else if parsed, err := url.ParseQuery(queryFrag); err != nil { return nil, err @@ -3981,9 +3981,9 @@ func NewCreateVolumeFromArchiveRequestWithBody(server string, params *CreateVolu } - if params.Metadata != nil { + if params.Tags != nil { - if queryFrag, err := runtime.StyleParamWithLocation("deepObject", true, "metadata", runtime.ParamLocationQuery, *params.Metadata); err != nil { + if queryFrag, err := runtime.StyleParamWithLocation("deepObject", true, "tags", runtime.ParamLocationQuery, *params.Tags); err != nil { return nil, err } else if parsed, err := url.ParseQuery(queryFrag); err != nil { return nil, err @@ -8280,11 +8280,11 @@ func (siw *ServerInterfaceWrapper) ListBuilds(w http.ResponseWriter, r *http.Req // Parameter object where we will unmarshal all parameters from the context var params ListBuildsParams - // ------------- Optional query parameter "metadata" ------------- + // ------------- Optional query parameter "tags" ------------- - err = runtime.BindQueryParameter("deepObject", true, false, "metadata", r.URL.Query(), ¶ms.Metadata) + err = runtime.BindQueryParameter("deepObject", true, false, "tags", r.URL.Query(), ¶ms.Tags) if err != nil { - siw.ErrorHandlerFunc(w, r, &InvalidParamFormatError{ParamName: "metadata", Err: err}) + siw.ErrorHandlerFunc(w, r, &InvalidParamFormatError{ParamName: "tags", Err: err}) return } @@ -8437,11 +8437,11 @@ func (siw *ServerInterfaceWrapper) ListDevices(w http.ResponseWriter, r *http.Re // Parameter object where we will unmarshal all parameters from the context var params ListDevicesParams - // ------------- Optional query parameter "metadata" ------------- + // ------------- Optional query parameter "tags" ------------- - err = runtime.BindQueryParameter("deepObject", true, false, "metadata", r.URL.Query(), ¶ms.Metadata) + err = runtime.BindQueryParameter("deepObject", true, false, "tags", r.URL.Query(), ¶ms.Tags) if err != nil { - siw.ErrorHandlerFunc(w, r, &InvalidParamFormatError{ParamName: "metadata", Err: err}) + siw.ErrorHandlerFunc(w, r, &InvalidParamFormatError{ParamName: "tags", Err: err}) return } @@ -8586,11 +8586,11 @@ func (siw *ServerInterfaceWrapper) ListImages(w http.ResponseWriter, r *http.Req // Parameter object where we will unmarshal all parameters from the context var params ListImagesParams - // ------------- Optional query parameter "metadata" ------------- + // ------------- Optional query parameter "tags" ------------- - err = runtime.BindQueryParameter("deepObject", true, false, "metadata", r.URL.Query(), ¶ms.Metadata) + err = runtime.BindQueryParameter("deepObject", true, false, "tags", r.URL.Query(), ¶ms.Tags) if err != nil { - siw.ErrorHandlerFunc(w, r, &InvalidParamFormatError{ParamName: "metadata", Err: err}) + siw.ErrorHandlerFunc(w, r, &InvalidParamFormatError{ParamName: "tags", Err: err}) return } @@ -8701,11 +8701,11 @@ func (siw *ServerInterfaceWrapper) ListIngresses(w http.ResponseWriter, r *http. // Parameter object where we will unmarshal all parameters from the context var params ListIngressesParams - // ------------- Optional query parameter "metadata" ------------- + // ------------- Optional query parameter "tags" ------------- - err = runtime.BindQueryParameter("deepObject", true, false, "metadata", r.URL.Query(), ¶ms.Metadata) + err = runtime.BindQueryParameter("deepObject", true, false, "tags", r.URL.Query(), ¶ms.Tags) if err != nil { - siw.ErrorHandlerFunc(w, r, &InvalidParamFormatError{ParamName: "metadata", Err: err}) + siw.ErrorHandlerFunc(w, r, &InvalidParamFormatError{ParamName: "tags", Err: err}) return } @@ -8824,11 +8824,11 @@ func (siw *ServerInterfaceWrapper) ListInstances(w http.ResponseWriter, r *http. return } - // ------------- Optional query parameter "metadata" ------------- + // ------------- Optional query parameter "tags" ------------- - err = runtime.BindQueryParameter("deepObject", true, false, "metadata", r.URL.Query(), ¶ms.Metadata) + err = runtime.BindQueryParameter("deepObject", true, false, "tags", r.URL.Query(), ¶ms.Tags) if err != nil { - siw.ErrorHandlerFunc(w, r, &InvalidParamFormatError{ParamName: "metadata", Err: err}) + siw.ErrorHandlerFunc(w, r, &InvalidParamFormatError{ParamName: "tags", Err: err}) return } @@ -9435,11 +9435,11 @@ func (siw *ServerInterfaceWrapper) ListSnapshots(w http.ResponseWriter, r *http. return } - // ------------- Optional query parameter "metadata" ------------- + // ------------- Optional query parameter "tags" ------------- - err = runtime.BindQueryParameter("deepObject", true, false, "metadata", r.URL.Query(), ¶ms.Metadata) + err = runtime.BindQueryParameter("deepObject", true, false, "tags", r.URL.Query(), ¶ms.Tags) if err != nil { - siw.ErrorHandlerFunc(w, r, &InvalidParamFormatError{ParamName: "metadata", Err: err}) + siw.ErrorHandlerFunc(w, r, &InvalidParamFormatError{ParamName: "tags", Err: err}) return } @@ -9561,11 +9561,11 @@ func (siw *ServerInterfaceWrapper) ListVolumes(w http.ResponseWriter, r *http.Re // Parameter object where we will unmarshal all parameters from the context var params ListVolumesParams - // ------------- Optional query parameter "metadata" ------------- + // ------------- Optional query parameter "tags" ------------- - err = runtime.BindQueryParameter("deepObject", true, false, "metadata", r.URL.Query(), ¶ms.Metadata) + err = runtime.BindQueryParameter("deepObject", true, false, "tags", r.URL.Query(), ¶ms.Tags) if err != nil { - siw.ErrorHandlerFunc(w, r, &InvalidParamFormatError{ParamName: "metadata", Err: err}) + siw.ErrorHandlerFunc(w, r, &InvalidParamFormatError{ParamName: "tags", Err: err}) return } @@ -9652,11 +9652,11 @@ func (siw *ServerInterfaceWrapper) CreateVolumeFromArchive(w http.ResponseWriter return } - // ------------- Optional query parameter "metadata" ------------- + // ------------- Optional query parameter "tags" ------------- - err = runtime.BindQueryParameter("deepObject", true, false, "metadata", r.URL.Query(), ¶ms.Metadata) + err = runtime.BindQueryParameter("deepObject", true, false, "tags", r.URL.Query(), ¶ms.Tags) if err != nil { - siw.ErrorHandlerFunc(w, r, &InvalidParamFormatError{ParamName: "metadata", Err: err}) + siw.ErrorHandlerFunc(w, r, &InvalidParamFormatError{ParamName: "tags", Err: err}) return } @@ -13295,201 +13295,201 @@ func (sh *strictHandler) GetVolume(w http.ResponseWriter, r *http.Request, id st // Base64 encoded, gzipped, json marshaled Swagger object var swaggerSpec = []string{ - "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", - "qQaH/cN+68v7L/8/AAD//9ibNZqCHgEA", + "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", + "//9dM5gHNB0BAA==", } // GetSwagger returns the content of the embedded swagger specification file diff --git a/lib/snapshot/store_test.go b/lib/snapshot/store_test.go index 10e485cb..57fc50ba 100644 --- a/lib/snapshot/store_test.go +++ b/lib/snapshot/store_test.go @@ -24,7 +24,7 @@ func TestStoreSaveLoadListDelete(t *testing.T) { Id: "snap1", Name: "baseline", Kind: SnapshotKindStandby, - Metadata: map[string]string{"team": "backend", "env": "staging"}, + Tags: map[string]string{"team": "backend", "env": "staging"}, SourceInstanceID: "inst1", SourceName: "vm1", SourceHypervisor: hypervisor.TypeQEMU, @@ -39,7 +39,7 @@ func TestStoreSaveLoadListDelete(t *testing.T) { got, err := store.LoadRecord(record.Snapshot.Id) require.NoError(t, err) require.Equal(t, record.Snapshot.Id, got.Snapshot.Id) - require.Equal(t, record.Snapshot.Metadata, got.Snapshot.Metadata) + require.Equal(t, record.Snapshot.Tags, got.Snapshot.Tags) require.JSONEq(t, string(record.StoredMetadata), string(got.StoredMetadata)) listed, err := store.List(nil) @@ -87,26 +87,26 @@ func TestListSnapshotsFilterMatches(t *testing.T) { SourceInstanceID: &sourceID, Kind: &kind, Name: &name, - Metadata: map[string]string{"team": "backend", "env": "staging"}, + Tags: map[string]string{"team": "backend", "env": "staging"}, } require.True(t, filter.Matches(&Snapshot{ SourceInstanceID: "inst1", Kind: SnapshotKindStandby, Name: "snap", - Metadata: map[string]string{"team": "backend", "env": "staging"}, + Tags: map[string]string{"team": "backend", "env": "staging"}, })) require.False(t, filter.Matches(&Snapshot{ SourceInstanceID: "inst2", Kind: SnapshotKindStandby, Name: "snap", - Metadata: map[string]string{"team": "backend", "env": "staging"}, + Tags: map[string]string{"team": "backend", "env": "staging"}, })) require.False(t, filter.Matches(&Snapshot{ SourceInstanceID: "inst1", Kind: SnapshotKindStandby, Name: "snap", - Metadata: map[string]string{"team": "backend", "env": "prod"}, + Tags: map[string]string{"team": "backend", "env": "prod"}, })) } diff --git a/lib/snapshot/types.go b/lib/snapshot/types.go index ef2bd8a8..0c19596d 100644 --- a/lib/snapshot/types.go +++ b/lib/snapshot/types.go @@ -19,12 +19,12 @@ const ( // Snapshot is a centrally stored immutable snapshot resource. type Snapshot struct { - Id string `json:"id"` - Name string `json:"name"` - Kind SnapshotKind `json:"kind"` - Metadata tags.Metadata `json:"metadata,omitempty"` - SourceInstanceID string `json:"source_instance_id"` - SourceName string `json:"source_instance_name"` + Id string `json:"id"` + Name string `json:"name"` + Kind SnapshotKind `json:"kind"` + Tags tags.Tags `json:"tags,omitempty"` + SourceInstanceID string `json:"source_instance_id"` + SourceName string `json:"source_instance_name"` SourceHypervisor hypervisor.Type CreatedAt time.Time `json:"created_at"` SizeBytes int64 `json:"size_bytes"` @@ -35,7 +35,7 @@ type ListSnapshotsFilter struct { SourceInstanceID *string Kind *SnapshotKind Name *string - Metadata tags.Metadata + Tags tags.Tags } // Matches returns true if the given snapshot satisfies all filter criteria. @@ -52,7 +52,7 @@ func (f *ListSnapshotsFilter) Matches(snapshot *Snapshot) bool { if f.Name != nil && snapshot.Name != *f.Name { return false } - if !tags.Matches(snapshot.Metadata, f.Metadata) { + if !tags.Matches(snapshot.Tags, f.Tags) { return false } return true diff --git a/lib/tags/clone.go b/lib/tags/clone.go index fbe9b709..c569ab90 100644 --- a/lib/tags/clone.go +++ b/lib/tags/clone.go @@ -1,12 +1,12 @@ package tags -// Clone returns a deep copy of metadata map and normalizes empty maps to nil. -func Clone(metadata Metadata) Metadata { - if len(metadata) == 0 { +// Clone returns a deep copy of a tag map and normalizes empty maps to nil. +func Clone(resourceTags Tags) Tags { + if len(resourceTags) == 0 { return nil } - out := make(Metadata, len(metadata)) - for k, v := range metadata { + out := make(Tags, len(resourceTags)) + for k, v := range resourceTags { out[k] = v } return out diff --git a/lib/tags/constraints.go b/lib/tags/constraints.go index 9b8daed2..0274d485 100644 --- a/lib/tags/constraints.go +++ b/lib/tags/constraints.go @@ -10,5 +10,5 @@ const ( MaxValueLength = 256 ) -// Allowed characters are aligned with the AWS-like strict metadata contract. +// Allowed characters are aligned with the AWS-like strict tag contract. var allowedPattern = regexp.MustCompile(`^[A-Za-z0-9 _.:/=+@-]+$`) diff --git a/lib/tags/errors.go b/lib/tags/errors.go index b7db599c..7e41423a 100644 --- a/lib/tags/errors.go +++ b/lib/tags/errors.go @@ -2,4 +2,4 @@ package tags import "errors" -var ErrInvalidMetadata = errors.New("invalid metadata") +var ErrInvalidTags = errors.New("invalid tags") diff --git a/lib/tags/match.go b/lib/tags/match.go index e2b617f5..df257b60 100644 --- a/lib/tags/match.go +++ b/lib/tags/match.go @@ -1,7 +1,7 @@ package tags -// Matches returns true when resource metadata satisfies all filter pairs. -func Matches(resource Metadata, filter Metadata) bool { +// Matches returns true when resource tags satisfy all filter pairs. +func Matches(resource Tags, filter Tags) bool { if len(filter) == 0 { return true } diff --git a/lib/tags/types.go b/lib/tags/types.go index 032b47c1..ff7d9cae 100644 --- a/lib/tags/types.go +++ b/lib/tags/types.go @@ -1,4 +1,4 @@ package tags -// Metadata is the shared domain type for resource metadata tags. -type Metadata = map[string]string +// Tags is the shared domain type for resource tags. +type Tags = map[string]string diff --git a/lib/tags/validate.go b/lib/tags/validate.go index 4888b685..bc8621d9 100644 --- a/lib/tags/validate.go +++ b/lib/tags/validate.go @@ -6,39 +6,39 @@ import ( "unicode/utf8" ) -// Validate enforces metadata constraints for all mutable resources. -func Validate(metadata Metadata) error { - if len(metadata) == 0 { +// Validate enforces tag constraints for all mutable resources. +func Validate(resourceTags Tags) error { + if len(resourceTags) == 0 { return nil } - if len(metadata) > MaxEntries { - return fmt.Errorf("%w: too many entries: %d (max %d)", ErrInvalidMetadata, len(metadata), MaxEntries) + if len(resourceTags) > MaxEntries { + return fmt.Errorf("%w: too many entries: %d (max %d)", ErrInvalidTags, len(resourceTags), MaxEntries) } - keys := make([]string, 0, len(metadata)) - for key := range metadata { + keys := make([]string, 0, len(resourceTags)) + for key := range resourceTags { keys = append(keys, key) } sort.Strings(keys) for _, key := range keys { - value := metadata[key] + value := resourceTags[key] keyLen := utf8.RuneCountInString(key) if keyLen < MinKeyLength || keyLen > MaxKeyLength { - return fmt.Errorf("%w: key %q length %d out of range [%d,%d]", ErrInvalidMetadata, key, keyLen, MinKeyLength, MaxKeyLength) + return fmt.Errorf("%w: key %q length %d out of range [%d,%d]", ErrInvalidTags, key, keyLen, MinKeyLength, MaxKeyLength) } if !allowedPattern.MatchString(key) { - return fmt.Errorf("%w: key %q contains unsupported characters", ErrInvalidMetadata, key) + return fmt.Errorf("%w: key %q contains unsupported characters", ErrInvalidTags, key) } valueLen := utf8.RuneCountInString(value) if valueLen < MinValueLength || valueLen > MaxValueLength { - return fmt.Errorf("%w: value for key %q length %d out of range [%d,%d]", ErrInvalidMetadata, key, valueLen, MinValueLength, MaxValueLength) + return fmt.Errorf("%w: value for key %q length %d out of range [%d,%d]", ErrInvalidTags, key, valueLen, MinValueLength, MaxValueLength) } if valueLen > 0 && !allowedPattern.MatchString(value) { - return fmt.Errorf("%w: value for key %q contains unsupported characters", ErrInvalidMetadata, key) + return fmt.Errorf("%w: value for key %q contains unsupported characters", ErrInvalidTags, key) } } diff --git a/lib/tags/validate_test.go b/lib/tags/validate_test.go index 45307ba3..ea32207a 100644 --- a/lib/tags/validate_test.go +++ b/lib/tags/validate_test.go @@ -18,15 +18,15 @@ func TestValidate(t *testing.T) { err := Validate(map[string]string{"": "x"}) require.Error(t, err) - require.ErrorIs(t, err, ErrInvalidMetadata) + require.ErrorIs(t, err, ErrInvalidTags) err = Validate(map[string]string{"tēam": "backend"}) require.Error(t, err) - require.ErrorIs(t, err, ErrInvalidMetadata) + require.ErrorIs(t, err, ErrInvalidTags) err = Validate(map[string]string{"team": "支付"}) require.Error(t, err) - require.ErrorIs(t, err, ErrInvalidMetadata) + require.ErrorIs(t, err, ErrInvalidTags) tooMany := make(map[string]string, MaxEntries+1) for i := 0; i < MaxEntries+1; i++ { @@ -34,15 +34,15 @@ func TestValidate(t *testing.T) { } err = Validate(tooMany) require.Error(t, err) - require.ErrorIs(t, err, ErrInvalidMetadata) + require.ErrorIs(t, err, ErrInvalidTags) longKey := map[string]string{strings.Repeat("a", MaxKeyLength+1): "v"} err = Validate(longKey) require.Error(t, err) - require.ErrorIs(t, err, ErrInvalidMetadata) + require.ErrorIs(t, err, ErrInvalidTags) longValue := map[string]string{"key": strings.Repeat("a", MaxValueLength+1)} err = Validate(longValue) require.Error(t, err) - require.ErrorIs(t, err, ErrInvalidMetadata) + require.ErrorIs(t, err, ErrInvalidTags) } diff --git a/lib/volumes/manager.go b/lib/volumes/manager.go index f644960e..705759c6 100644 --- a/lib/volumes/manager.go +++ b/lib/volumes/manager.go @@ -111,7 +111,7 @@ func (m *manager) calculateTotalVolumeStorage(ctx context.Context) (int64, error // CreateVolume creates a new volume func (m *manager) CreateVolume(ctx context.Context, req CreateVolumeRequest) (*Volume, error) { start := time.Now() - if err := tags.Validate(req.Metadata); err != nil { + if err := tags.Validate(req.Tags); err != nil { return nil, err } @@ -158,7 +158,7 @@ func (m *manager) CreateVolume(ctx context.Context, req CreateVolumeRequest) (*V Id: id, Name: req.Name, SizeGb: req.SizeGb, - Metadata: tags.Clone(req.Metadata), + Tags: tags.Clone(req.Tags), CreatedAt: now.Format(time.RFC3339), } @@ -177,7 +177,7 @@ func (m *manager) CreateVolume(ctx context.Context, req CreateVolumeRequest) (*V // The archive is safely extracted with size limits to prevent tar bombs. func (m *manager) CreateVolumeFromArchive(ctx context.Context, req CreateVolumeFromArchiveRequest, archive io.Reader) (*Volume, error) { start := time.Now() - if err := tags.Validate(req.Metadata); err != nil { + if err := tags.Validate(req.Tags); err != nil { return nil, err } @@ -244,7 +244,7 @@ func (m *manager) CreateVolumeFromArchive(ctx context.Context, req CreateVolumeF Id: id, Name: req.Name, SizeGb: actualSizeGb, - Metadata: tags.Clone(req.Metadata), + Tags: tags.Clone(req.Tags), CreatedAt: now.Format(time.RFC3339), } @@ -429,7 +429,7 @@ func (m *manager) metadataToVolume(meta *storedMetadata) *Volume { Id: meta.Id, Name: meta.Name, SizeGb: meta.SizeGb, - Metadata: tags.Clone(meta.Metadata), + Tags: tags.Clone(meta.Tags), CreatedAt: createdAt, Attachments: attachments, } diff --git a/lib/volumes/manager_test.go b/lib/volumes/manager_test.go index 69c4e7c5..f7b05aaf 100644 --- a/lib/volumes/manager_test.go +++ b/lib/volumes/manager_test.go @@ -401,19 +401,19 @@ func TestCreateVolume_MetadataRoundTrip(t *testing.T) { Name: "tagged-vol", SizeGb: 10, Attachments: []storedAttachment{}, - Metadata: map[string]string{"team": "backend", "env": "staging"}, + Tags: map[string]string{"team": "backend", "env": "staging"}, } require.NoError(t, os.MkdirAll(p.VolumeDir(meta.Id), 0755)) require.NoError(t, saveMetadata(p, meta)) loaded, err := loadMetadata(p, meta.Id) require.NoError(t, err) - require.Equal(t, map[string]string{"team": "backend", "env": "staging"}, loaded.Metadata) + require.Equal(t, map[string]string{"team": "backend", "env": "staging"}, loaded.Tags) vol := (&manager{}).metadataToVolume(loaded) - require.Equal(t, map[string]string{"team": "backend", "env": "staging"}, vol.Metadata) + require.Equal(t, map[string]string{"team": "backend", "env": "staging"}, vol.Tags) // Verify deep-copy behavior from persisted metadata. - loaded.Metadata["team"] = "mutated" - require.Equal(t, "backend", vol.Metadata["team"]) + loaded.Tags["team"] = "mutated" + require.Equal(t, "backend", vol.Tags["team"]) } diff --git a/lib/volumes/storage.go b/lib/volumes/storage.go index 7709e659..cd600ddb 100644 --- a/lib/volumes/storage.go +++ b/lib/volumes/storage.go @@ -28,7 +28,7 @@ type storedMetadata struct { Id string `json:"id"` Name string `json:"name"` SizeGb int `json:"size_gb"` - Metadata tags.Metadata `json:"metadata,omitempty"` + Tags tags.Tags `json:"tags,omitempty"` CreatedAt string `json:"created_at"` // RFC3339 format Attachments []storedAttachment `json:"attachments,omitempty"` } diff --git a/lib/volumes/types.go b/lib/volumes/types.go index 2ddac750..c6648bfb 100644 --- a/lib/volumes/types.go +++ b/lib/volumes/types.go @@ -18,17 +18,17 @@ type Volume struct { Id string Name string SizeGb int - Metadata tags.Metadata + Tags tags.Tags CreatedAt time.Time Attachments []Attachment // List of current attachments (empty if not attached) } // CreateVolumeRequest is the domain request for creating a volume type CreateVolumeRequest struct { - Name string - SizeGb int - Id *string // Optional custom ID - Metadata tags.Metadata + Name string + SizeGb int + Id *string // Optional custom ID + Tags tags.Tags } // AttachVolumeRequest is the domain request for attaching a volume to an instance @@ -41,8 +41,8 @@ type AttachVolumeRequest struct { // CreateVolumeFromArchiveRequest is the domain request for creating a volume // pre-populated with content from a tar.gz archive type CreateVolumeFromArchiveRequest struct { - Name string - SizeGb int // Maximum size in GB (extraction fails if content exceeds this) - Id *string // Optional custom ID - Metadata tags.Metadata + Name string + SizeGb int // Maximum size in GB (extraction fails if content exceeds this) + Id *string // Optional custom ID + Tags tags.Tags } diff --git a/openapi.yaml b/openapi.yaml index ea6a3190..4fceba4a 100644 --- a/openapi.yaml +++ b/openapi.yaml @@ -98,10 +98,10 @@ components: enum: [tcp, udp] default: tcp - MetadataTags: + Tags: type: object maxProperties: 50 - description: User-defined key-value metadata tags. + description: User-defined key-value tags. propertyNames: type: string minLength: 1 @@ -161,8 +161,8 @@ components: example: PORT: "3000" NODE_ENV: production - metadata: - $ref: "#/components/schemas/MetadataTags" + tags: + $ref: "#/components/schemas/Tags" network: type: object description: Network configuration for the instance @@ -286,8 +286,8 @@ components: example: baseline-standby kind: $ref: "#/components/schemas/SnapshotKind" - metadata: - $ref: "#/components/schemas/MetadataTags" + tags: + $ref: "#/components/schemas/Tags" source_instance_id: type: string description: Source instance ID at snapshot creation time @@ -324,8 +324,8 @@ components: pattern: ^[a-z0-9]([a-z0-9-]*[a-z0-9])?$ maxLength: 63 example: pre-upgrade - metadata: - $ref: "#/components/schemas/MetadataTags" + tags: + $ref: "#/components/schemas/Tags" RestoreSnapshotRequest: type: object @@ -418,8 +418,8 @@ components: additionalProperties: type: string description: Environment variables - metadata: - $ref: "#/components/schemas/MetadataTags" + tags: + $ref: "#/components/schemas/Tags" network: type: object description: Network configuration of the instance @@ -595,8 +595,8 @@ components: type: string description: OCI image reference (e.g., docker.io/library/nginx:latest) example: docker.io/library/nginx:latest - metadata: - $ref: "#/components/schemas/MetadataTags" + tags: + $ref: "#/components/schemas/Tags" Image: type: object @@ -652,8 +652,8 @@ components: description: Environment variables from container metadata example: PATH: /usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin - metadata: - $ref: "#/components/schemas/MetadataTags" + tags: + $ref: "#/components/schemas/Tags" working_dir: type: string description: Working directory from container metadata @@ -681,8 +681,8 @@ components: type: integer description: Size in gigabytes example: 10 - metadata: - $ref: "#/components/schemas/MetadataTags" + tags: + $ref: "#/components/schemas/Tags" VolumeAttachment: type: object @@ -717,8 +717,8 @@ components: type: integer description: Size in gigabytes example: 10 - metadata: - $ref: "#/components/schemas/MetadataTags" + tags: + $ref: "#/components/schemas/Tags" attachments: type: array description: List of current attachments (empty if not attached) @@ -824,8 +824,8 @@ components: items: $ref: "#/components/schemas/IngressRule" minItems: 1 - metadata: - $ref: "#/components/schemas/MetadataTags" + tags: + $ref: "#/components/schemas/Tags" Ingress: type: object @@ -839,8 +839,8 @@ components: type: string description: Human-readable name example: my-api-ingress - metadata: - $ref: "#/components/schemas/MetadataTags" + tags: + $ref: "#/components/schemas/Tags" rules: type: array description: Routing rules for this ingress @@ -954,8 +954,8 @@ components: type: string description: PCI address of the device (required, e.g., "0000:a2:00.0") example: "0000:a2:00.0" - metadata: - $ref: "#/components/schemas/MetadataTags" + tags: + $ref: "#/components/schemas/Tags" Device: type: object @@ -971,8 +971,8 @@ components: example: l4-gpu type: $ref: "#/components/schemas/DeviceType" - metadata: - $ref: "#/components/schemas/MetadataTags" + tags: + $ref: "#/components/schemas/Tags" pci_address: type: string description: PCI address @@ -1118,8 +1118,8 @@ components: example: "build-abc123" status: $ref: "#/components/schemas/BuildStatus" - metadata: - $ref: "#/components/schemas/MetadataTags" + tags: + $ref: "#/components/schemas/Tags" queue_position: type: integer description: Position in build queue (only when status is queued) @@ -1332,14 +1332,14 @@ paths: security: - bearerAuth: [] parameters: - - name: metadata + - name: tags in: query required: false style: deepObject explode: true schema: - $ref: "#/components/schemas/MetadataTags" - description: Filter images by metadata key-value pairs. + $ref: "#/components/schemas/Tags" + description: Filter images by tag key-value pairs. responses: 200: description: List of images @@ -1477,16 +1477,16 @@ paths: schema: $ref: "#/components/schemas/InstanceState" description: Filter instances by state (e.g., Running, Stopped) - - name: metadata + - name: tags in: query required: false style: deepObject explode: true schema: - $ref: "#/components/schemas/MetadataTags" + $ref: "#/components/schemas/Tags" description: | - Filter instances by metadata key-value pairs. Uses deepObject style: - ?metadata[team]=backend&metadata[env]=staging + Filter instances by tag key-value pairs. Uses deepObject style: + ?tags[team]=backend&tags[env]=staging Multiple entries are ANDed together. All specified key-value pairs must match. responses: 200: @@ -1892,14 +1892,14 @@ paths: schema: type: string description: Filter snapshots by snapshot name - - name: metadata + - name: tags in: query required: false style: deepObject explode: true schema: - $ref: "#/components/schemas/MetadataTags" - description: Filter snapshots by metadata key-value pairs. + $ref: "#/components/schemas/Tags" + description: Filter snapshots by tag key-value pairs. responses: 200: description: List of snapshots @@ -2385,14 +2385,14 @@ paths: security: - bearerAuth: [] parameters: - - name: metadata + - name: tags in: query required: false style: deepObject explode: true schema: - $ref: "#/components/schemas/MetadataTags" - description: Filter volumes by metadata key-value pairs. + $ref: "#/components/schemas/Tags" + description: Filter volumes by tag key-value pairs. responses: 200: description: List of volumes @@ -2489,14 +2489,14 @@ paths: type: string description: Optional custom volume ID (auto-generated if not provided) example: vol-data-1 - - name: metadata + - name: tags in: query required: false style: deepObject explode: true schema: - $ref: "#/components/schemas/MetadataTags" - description: Metadata tags for the created volume. + $ref: "#/components/schemas/Tags" + description: Tags for the created volume. requestBody: required: true description: tar.gz archive file containing the volume content @@ -2610,14 +2610,14 @@ paths: security: - bearerAuth: [] parameters: - - name: metadata + - name: tags in: query required: false style: deepObject explode: true schema: - $ref: "#/components/schemas/MetadataTags" - description: Filter devices by metadata key-value pairs. + $ref: "#/components/schemas/Tags" + description: Filter devices by tag key-value pairs. responses: 200: description: List of registered devices @@ -2789,14 +2789,14 @@ paths: security: - bearerAuth: [] parameters: - - name: metadata + - name: tags in: query required: false style: deepObject explode: true schema: - $ref: "#/components/schemas/MetadataTags" - description: Filter ingresses by metadata key-value pairs. + $ref: "#/components/schemas/Tags" + description: Filter ingresses by tag key-value pairs. responses: 200: description: List of ingresses @@ -2940,14 +2940,14 @@ paths: security: - bearerAuth: [] parameters: - - name: metadata + - name: tags in: query required: false style: deepObject explode: true schema: - $ref: "#/components/schemas/MetadataTags" - description: Filter builds by metadata key-value pairs. + $ref: "#/components/schemas/Tags" + description: Filter builds by tag key-value pairs. responses: 200: description: List of builds @@ -3030,10 +3030,10 @@ paths: Global cache identifier (e.g., "node", "python", "ubuntu", "browser"). When specified, the build will import from cache/global/{key}. Admin builds will also export to this location. - metadata: + tags: type: string description: | - JSON object of metadata tags. + JSON object of tags. Example: {"team":"backend","env":"staging"} responses: 202: From 2b022263c618246846d5ab8ca0a6eed05ca8b453 Mon Sep 17 00:00:00 2001 From: Steven Miller Date: Mon, 9 Mar 2026 15:09:45 -0400 Subject: [PATCH 2/2] update tags README terminology --- lib/tags/README.md | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/lib/tags/README.md b/lib/tags/README.md index f02e1639..0c3a5a84 100644 --- a/lib/tags/README.md +++ b/lib/tags/README.md @@ -1,12 +1,12 @@ -# Metadata Tags +# Tags -This package defines the product contract for user-provided metadata tags used across mutable Hypeman resources. +This package defines the product contract for user-provided tags used across mutable Hypeman resources. ## What Tags Are -Metadata tags are optional string key/value pairs that let users label resources for ownership, environment, automation, and filtering. +Tags are optional string key/value pairs that let users label resources for ownership, environment, automation, and filtering. -Domain code uses the shared `tags.Metadata` type alias so resource packages reference one metadata type consistently. +Domain code uses the shared `tags.Tags` type alias so resource packages reference one tags type consistently. Examples: - `team=backend` @@ -30,6 +30,6 @@ Examples: ## API Expectations -- Create endpoints accept optional `metadata`. -- Get/List responses include `metadata` when present. -- List endpoints support filtering by `metadata[...]` where enabled. +- Create endpoints accept optional `tags`. +- Get/List responses include `tags` when present. +- List endpoints support filtering by `tags[...]` where enabled.