Skip to content
Merged

Tags #127

Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
34 changes: 31 additions & 3 deletions cmd/api/api/builds.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import (
"github.com/kernel/hypeman/lib/images"
"github.com/kernel/hypeman/lib/logger"
"github.com/kernel/hypeman/lib/oapi"
"github.com/kernel/hypeman/lib/tags"
)

// ListBuilds returns all builds
Expand All @@ -28,9 +29,12 @@ func (s *ApiService) ListBuilds(ctx context.Context, request oapi.ListBuildsRequ
}, nil
}

oapiBuilds := make([]oapi.Build, len(domainBuilds))
for i, b := range domainBuilds {
oapiBuilds[i] = buildToOAPI(b)
oapiBuilds := make([]oapi.Build, 0, len(domainBuilds))
for _, b := range domainBuilds {
if b == nil || !matchesMetadataFilter(b.Metadata, request.Params.Metadata) {
continue
}
oapiBuilds = append(oapiBuilds, buildToOAPI(b))
}

return oapi.ListBuilds200JSONResponse(oapiBuilds), nil
Expand All @@ -46,6 +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

for {
part, err := request.Body.NextPart()
Expand Down Expand Up @@ -169,6 +174,22 @@ func (s *ApiService) CreateBuild(ctx context.Context, request oapi.CreateBuildRe
}, nil
}
imageName = string(data)
case "metadata":
data, err := io.ReadAll(part)
if err != nil {
return oapi.CreateBuild400JSONResponse{
Code: "invalid_request",
Message: "failed to read metadata field",
}, nil
}
parsed, err := parseMetadataJSON(string(data))
if err != nil {
return oapi.CreateBuild400JSONResponse{
Code: "invalid_request",
Message: "metadata must be a JSON object with string key-value pairs",
}, nil
}
metadata = parsed
}
part.Close()
}
Expand Down Expand Up @@ -203,6 +224,7 @@ func (s *ApiService) CreateBuild(ctx context.Context, request oapi.CreateBuildRe
IsAdminBuild: isAdminBuild,
GlobalCacheKey: globalCacheKey,
ImageName: imageName,
Metadata: metadata,
}

// Apply build policy if any field was provided
Expand All @@ -223,6 +245,11 @@ 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):
return oapi.CreateBuild400JSONResponse{
Code: "invalid_request",
Message: err.Error(),
}, nil
case errors.Is(err, builds.ErrDockerfileRequired):
return oapi.CreateBuild400JSONResponse{
Code: "dockerfile_required",
Expand Down Expand Up @@ -359,6 +386,7 @@ func buildToOAPI(b *builds.Build) oapi.Build {
oapiBuild := oapi.Build{
Id: b.ID,
Status: oapi.BuildStatus(b.Status),
Metadata: toOAPIMetadata(b.Metadata),
QueuePosition: b.QueuePosition,
ImageDigest: b.ImageDigest,
ImageRef: b.ImageRef,
Expand Down
17 changes: 14 additions & 3 deletions cmd/api/api/devices.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import (

"github.com/kernel/hypeman/lib/devices"
"github.com/kernel/hypeman/lib/oapi"
"github.com/kernel/hypeman/lib/tags"
)

// ListDevices returns all registered devices
Expand All @@ -18,9 +19,12 @@ func (s *ApiService) ListDevices(ctx context.Context, request oapi.ListDevicesRe
}, nil
}

result := make([]oapi.Device, len(deviceList))
for i, d := range deviceList {
result[i] = deviceToOAPI(d)
result := make([]oapi.Device, 0, len(deviceList))
for _, d := range deviceList {
if !matchesMetadataFilter(d.Metadata, request.Params.Metadata) {
continue
}
result = append(result, deviceToOAPI(d))
}

return oapi.ListDevices200JSONResponse(result), nil
Expand Down Expand Up @@ -53,11 +57,17 @@ func (s *ApiService) CreateDevice(ctx context.Context, request oapi.CreateDevice
req := devices.CreateDeviceRequest{
Name: name,
PCIAddress: request.Body.PciAddress,
Metadata: toMapMetadata(request.Body.Metadata),
}

device, err := s.DeviceManager.CreateDevice(ctx, req)
if err != nil {
switch {
case errors.Is(err, tags.ErrInvalidMetadata):
return oapi.CreateDevice400JSONResponse{
Code: "invalid_request",
Message: err.Error(),
}, nil
case errors.Is(err, devices.ErrInvalidName):
return oapi.CreateDevice400JSONResponse{
Code: "invalid_name",
Expand Down Expand Up @@ -142,6 +152,7 @@ func deviceToOAPI(d devices.Device) oapi.Device {
Id: d.Id,
Name: &d.Name,
Type: deviceType,
Metadata: toOAPIMetadata(d.Metadata),
PciAddress: d.PCIAddress,
VendorId: d.VendorID,
DeviceId: d.DeviceID,
Expand Down
22 changes: 17 additions & 5 deletions cmd/api/api/images.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import (
"github.com/kernel/hypeman/lib/logger"
mw "github.com/kernel/hypeman/lib/middleware"
"github.com/kernel/hypeman/lib/oapi"
"github.com/kernel/hypeman/lib/tags"
)

func (s *ApiService) ListImages(ctx context.Context, request oapi.ListImagesRequestObject) (oapi.ListImagesResponseObject, error) {
Expand All @@ -22,24 +23,32 @@ func (s *ApiService) ListImages(ctx context.Context, request oapi.ListImagesRequ
}, nil
}

oapiImages := make([]oapi.Image, len(domainImages))
for i, img := range domainImages {
oapiImages[i] = imageToOAPI(img)
oapiImages := make([]oapi.Image, 0, len(domainImages))
for _, img := range domainImages {
if !matchesMetadataFilter(img.Metadata, request.Params.Metadata) {
continue
}
oapiImages = append(oapiImages, imageToOAPI(img))
}

return oapi.ListImages200JSONResponse(oapiImages), nil
}

func (s *ApiService) CreateImage(ctx context.Context, request oapi.CreateImageRequestObject) (oapi.CreateImageResponseObject, error) {
log := logger.FromContext(ctx)

domainReq := images.CreateImageRequest{
Name: request.Body.Name,
Name: request.Body.Name,
Metadata: toMapMetadata(request.Body.Metadata),
}

img, err := s.ImageManager.CreateImage(ctx, domainReq)
if err != nil {
switch {
case errors.Is(err, tags.ErrInvalidMetadata):
return oapi.CreateImage400JSONResponse{
Code: "invalid_request",
Message: err.Error(),
}, nil
case errors.Is(err, images.ErrInvalidName):
return oapi.CreateImage400JSONResponse{
Code: "invalid_name",
Expand Down Expand Up @@ -117,6 +126,9 @@ 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 img.WorkingDir != "" {
oapiImg.WorkingDir = &img.WorkingDir
}
Expand Down
15 changes: 10 additions & 5 deletions cmd/api/api/ingress.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,9 +23,12 @@ func (s *ApiService) ListIngresses(ctx context.Context, request oapi.ListIngress
}, nil
}

oapiIngresses := make([]oapi.Ingress, len(ingresses))
for i, ing := range ingresses {
oapiIngresses[i] = ingressToOAPI(ing)
oapiIngresses := make([]oapi.Ingress, 0, len(ingresses))
for _, ing := range ingresses {
if !matchesMetadataFilter(ing.Metadata, request.Params.Metadata) {
continue
}
oapiIngresses = append(oapiIngresses, ingressToOAPI(ing))
}

return oapi.ListIngresses200JSONResponse(oapiIngresses), nil
Expand All @@ -37,8 +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,
Rules: make([]ingress.IngressRule, len(request.Body.Rules)),
Name: request.Body.Name,
Metadata: toMapMetadata(request.Body.Metadata),
Rules: make([]ingress.IngressRule, len(request.Body.Rules)),
}

for i, rule := range request.Body.Rules {
Expand Down Expand Up @@ -180,6 +184,7 @@ func ingressToOAPI(ing ingress.Ingress) oapi.Ingress {
return oapi.Ingress{
Id: ing.ID,
Name: ing.Name,
Metadata: toOAPIMetadata(ing.Metadata),
Rules: rules,
CreatedAt: ing.CreatedAt,
}
Expand Down
11 changes: 8 additions & 3 deletions cmd/api/api/instances.go
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ func (s *ApiService) ListInstances(ctx context.Context, request oapi.ListInstanc
filter.State = &state
}
if request.Params.Metadata != nil {
filter.Metadata = *request.Params.Metadata
filter.Metadata = toMapMetadata(request.Params.Metadata)
}
}

Expand Down Expand Up @@ -127,7 +127,7 @@ func (s *ApiService) CreateInstance(ctx context.Context, request oapi.CreateInst

metadata := make(map[string]string)
if request.Body.Metadata != nil {
metadata = *request.Body.Metadata
metadata = toMapMetadata(request.Body.Metadata)
}

// Parse network enabled (default: true)
Expand Down Expand Up @@ -288,6 +288,11 @@ func (s *ApiService) CreateInstance(ctx context.Context, request oapi.CreateInst
Code: "insufficient_resources",
Message: err.Error(),
}, nil
case errors.Is(err, instances.ErrInvalidRequest):
return oapi.CreateInstance400JSONResponse{
Code: "invalid_request",
Message: err.Error(),
}, nil
default:
log.ErrorContext(ctx, "failed to create instance", "error", err, "image", request.Body.Image)
return oapi.CreateInstance500JSONResponse{
Expand Down Expand Up @@ -849,7 +854,7 @@ func instanceToOAPI(inst instances.Instance) oapi.Instance {
}

if len(inst.Metadata) > 0 {
oapiInst.Metadata = &inst.Metadata
oapiInst.Metadata = toOAPIMetadata(inst.Metadata)
}

// Convert volume attachments
Expand Down
42 changes: 42 additions & 0 deletions cmd/api/api/metadata.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
package api

import (
"encoding/json"
"fmt"

"github.com/kernel/hypeman/lib/oapi"
"github.com/kernel/hypeman/lib/tags"
)

func toMapMetadata(metadata *oapi.MetadataTags) map[string]string {
if metadata == nil {
return nil
}
return tags.Clone(map[string]string(*metadata))
}

func toOAPIMetadata(metadata map[string]string) *oapi.MetadataTags {
if len(metadata) == 0 {
return nil
}
cloned := oapi.MetadataTags(tags.Clone(metadata))
return &cloned
}

func matchesMetadataFilter(metadata map[string]string, filter *oapi.MetadataTags) bool {
if filter == nil {
return true
}
return tags.Matches(metadata, map[string]string(*filter))
}

func parseMetadataJSON(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)
}
return metadata, nil
}
11 changes: 8 additions & 3 deletions cmd/api/api/snapshots.go
Original file line number Diff line number Diff line change
Expand Up @@ -29,8 +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,
Kind: instances.SnapshotKind(request.Body.Kind),
Name: name,
Metadata: toMapMetadata(request.Body.Metadata),
})
if err != nil {
log := logger.FromContext(ctx)
Expand Down Expand Up @@ -104,7 +105,10 @@ func (s *ApiService) ListSnapshots(ctx context.Context, request oapi.ListSnapsho
if request.Params.Name != nil {
filter.Name = request.Params.Name
}
if filter.SourceInstanceID == nil && filter.Kind == nil && filter.Name == nil {
if request.Params.Metadata != nil {
filter.Metadata = toMapMetadata(request.Params.Metadata)
}
if filter.SourceInstanceID == nil && filter.Kind == nil && filter.Name == nil && len(filter.Metadata) == 0 {
filter = nil
}

Expand Down Expand Up @@ -195,6 +199,7 @@ func snapshotToOAPI(snapshot instances.Snapshot) oapi.Snapshot {
out := oapi.Snapshot{
Id: snapshot.Id,
Kind: kind,
Metadata: toOAPIMetadata(snapshot.Metadata),
SourceInstanceId: snapshot.SourceInstanceID,
SourceInstanceName: snapshot.SourceName,
SourceHypervisor: sourceHypervisor,
Expand Down
Loading