From 65949d74aad9280af24fac37fa61620231943747 Mon Sep 17 00:00:00 2001 From: Simon Murray Date: Wed, 27 Nov 2024 13:58:14 +0000 Subject: [PATCH] Add Resource Tags (#80) --- charts/region/Chart.yaml | 4 +- .../crds/region.unikorn-cloud.org_quotas.yaml | 16 ++ .../region.unikorn-cloud.org_regions.yaml | 16 ++ ....unikorn-cloud.org_securitygrouprules.yaml | 16 ++ ...gion.unikorn-cloud.org_securitygroups.yaml | 1 + go.mod | 6 +- go.sum | 14 +- pkg/apis/unikorn/v1alpha1/types.go | 26 +- .../unikorn/v1alpha1/zz_generated.deepcopy.go | 59 ++-- pkg/handler/handler.go | 88 +----- pkg/handler/region/region.go | 2 +- pkg/handler/server/conversion.go | 54 +--- pkg/openapi/schema.go | 264 +++++++++--------- pkg/openapi/server.spec.yaml | 60 +--- pkg/openapi/types.go | 38 +-- 15 files changed, 251 insertions(+), 413 deletions(-) diff --git a/charts/region/Chart.yaml b/charts/region/Chart.yaml index 2346d9b..eed22d5 100644 --- a/charts/region/Chart.yaml +++ b/charts/region/Chart.yaml @@ -4,8 +4,8 @@ description: A Helm chart for deploying Unikorn's Region Controller type: application -version: v0.1.45 -appVersion: v0.1.45 +version: v0.1.46 +appVersion: v0.1.46 icon: https://raw.githubusercontent.com/unikorn-cloud/assets/main/images/logos/dark-on-light/icon.png diff --git a/charts/region/crds/region.unikorn-cloud.org_quotas.yaml b/charts/region/crds/region.unikorn-cloud.org_quotas.yaml index 45905a9..397a4b3 100644 --- a/charts/region/crds/region.unikorn-cloud.org_quotas.yaml +++ b/charts/region/crds/region.unikorn-cloud.org_quotas.yaml @@ -74,6 +74,22 @@ spec: x-kubernetes-list-map-keys: - id x-kubernetes-list-type: map + tags: + description: Tags are aribrary user data. + items: + description: Tag is an arbirary key/value. + properties: + name: + description: Name of the tag. + type: string + value: + description: Value of the tag. + type: string + required: + - name + - value + type: object + type: array type: object status: type: object diff --git a/charts/region/crds/region.unikorn-cloud.org_regions.yaml b/charts/region/crds/region.unikorn-cloud.org_regions.yaml index aa9233f..2ae20b4 100644 --- a/charts/region/crds/region.unikorn-cloud.org_regions.yaml +++ b/charts/region/crds/region.unikorn-cloud.org_regions.yaml @@ -312,6 +312,22 @@ spec: enum: - openstack type: string + tags: + description: Tags are aribrary user data. + items: + description: Tag is an arbirary key/value. + properties: + name: + description: Name of the tag. + type: string + value: + description: Value of the tag. + type: string + required: + - name + - value + type: object + type: array required: - provider type: object diff --git a/charts/region/crds/region.unikorn-cloud.org_securitygrouprules.yaml b/charts/region/crds/region.unikorn-cloud.org_securitygrouprules.yaml index c849903..76980ca 100644 --- a/charts/region/crds/region.unikorn-cloud.org_securitygrouprules.yaml +++ b/charts/region/crds/region.unikorn-cloud.org_securitygrouprules.yaml @@ -92,6 +92,22 @@ spec: - tcp - udp type: string + tags: + description: Tags are aribrary user data. + items: + description: Tag is an arbirary key/value. + properties: + name: + description: Name of the tag. + type: string + value: + description: Value of the tag. + type: string + required: + - name + - value + type: object + type: array required: - cidr - direction diff --git a/charts/region/crds/region.unikorn-cloud.org_securitygroups.yaml b/charts/region/crds/region.unikorn-cloud.org_securitygroups.yaml index 492c743..f1a92a9 100644 --- a/charts/region/crds/region.unikorn-cloud.org_securitygroups.yaml +++ b/charts/region/crds/region.unikorn-cloud.org_securitygroups.yaml @@ -59,6 +59,7 @@ spec: description: |- Tags are an abitrary list of key/value pairs that a client may populate to store metadata for the resource. + Tags are aribrary user data. items: description: Tag is an arbirary key/value. properties: diff --git a/go.mod b/go.mod index 05576f1..d2ed698 100644 --- a/go.mod +++ b/go.mod @@ -9,8 +9,8 @@ require ( github.com/gophercloud/utils v0.0.0-20231010081019-80377eca5d56 github.com/oapi-codegen/runtime v1.1.1 github.com/spf13/pflag v1.0.5 - github.com/unikorn-cloud/core v0.1.76 - github.com/unikorn-cloud/identity v0.2.44 + github.com/unikorn-cloud/core v0.1.85 + github.com/unikorn-cloud/identity v0.2.45 go.opentelemetry.io/otel v1.31.0 go.opentelemetry.io/otel/sdk v1.31.0 go.opentelemetry.io/otel/trace v1.31.0 @@ -23,6 +23,7 @@ require ( ) require ( + github.com/Masterminds/semver/v3 v3.3.0 // indirect github.com/apapsch/go-jsonmerge/v2 v2.0.0 // indirect github.com/beorn7/perks v1.0.1 // indirect github.com/cenkalti/backoff/v4 v4.3.0 // indirect @@ -57,7 +58,6 @@ require ( github.com/json-iterator/go v1.1.12 // indirect github.com/klauspost/compress v1.17.11 // indirect github.com/mailru/easyjson v0.7.7 // indirect - github.com/masterminds/semver v1.5.0 // indirect github.com/mitchellh/go-homedir v1.1.0 // indirect github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect github.com/modern-go/reflect2 v1.0.2 // indirect diff --git a/go.sum b/go.sum index 4b407e4..32f2f03 100644 --- a/go.sum +++ b/go.sum @@ -1,5 +1,5 @@ -github.com/Masterminds/semver v1.5.0 h1:H65muMkzWKEuNDnfl9d70GUjFniHKHRbFPGBuZ3QEww= -github.com/Masterminds/semver v1.5.0/go.mod h1:MB6lktGJrhw8PrUyiEoblNEGEQ+RzHPF078ddwwvV3Y= +github.com/Masterminds/semver/v3 v3.3.0 h1:B8LGeaivUe71a5qox1ICM/JLl0NqZSW5CHyL+hmvYS0= +github.com/Masterminds/semver/v3 v3.3.0/go.mod h1:4V+yj/TJE1HU9XfppCwVMZq3I84lprf4nC11bSS5beM= github.com/RaveNoX/go-jsoncommentstrip v1.0.0/go.mod h1:78ihd09MekBnJnxpICcwzCMzGrKSKYe4AqU6PDYYpjk= github.com/apapsch/go-jsonmerge/v2 v2.0.0 h1:axGnT1gRIfimI7gJifB699GoE/oq+F2MU7Dml6nw9rQ= github.com/apapsch/go-jsonmerge/v2 v2.0.0/go.mod h1:lvDnEdqiQrp0O42VQGgmlKpxL1AP2+08jFMw88y4klk= @@ -101,8 +101,6 @@ github.com/kylelemons/godebug v1.1.0 h1:RPNrshWIDI6G2gRW9EHilWtl7Z6Sb1BR0xunSBf0 github.com/kylelemons/godebug v1.1.0/go.mod h1:9/0rRGxNHcop5bhtWyNeEfOS8JIWk580+fNqagV/RAw= github.com/mailru/easyjson v0.7.7 h1:UGYAvKxe3sBsEDzO8ZeWOSlIQfWFlxbzLZe7hwFURr0= github.com/mailru/easyjson v0.7.7/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc= -github.com/masterminds/semver v1.5.0 h1:hTxJTTY7tjvnWMrl08O6u3G6BLlKVwxSz01lVac9P8U= -github.com/masterminds/semver v1.5.0/go.mod h1:s7KNT9fnd7edGzwwP7RBX4H0v/CYd5qdOLfkL1V75yg= github.com/mitchellh/go-homedir v1.1.0 h1:lukF9ziXFxDFPkA1vsr5zpc1XuPDn/wFntq5mG+4E0Y= github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= @@ -146,10 +144,10 @@ github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsT github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= github.com/ugorji/go/codec v1.2.12 h1:9LC83zGrHhuUA9l16C9AHXAqEV/2wBQ4nkvumAE65EE= github.com/ugorji/go/codec v1.2.12/go.mod h1:UNopzCgEMSXjBc6AOMqYvWC1ktqTAfzJZUZgYf6w6lg= -github.com/unikorn-cloud/core v0.1.76 h1:h9TsNTYimmu7N23RB3J7PLKp+ekJF7vM9AF1yMuVgIo= -github.com/unikorn-cloud/core v0.1.76/go.mod h1:S9AF4PwTQljImb9w0P2jKjzRe8fLM+rx+ZbxrAHw/yE= -github.com/unikorn-cloud/identity v0.2.44 h1:tXV/qsJ77Dkx8ba8gnBFXHWUgBNsJ2oo/5TjnyhkH7U= -github.com/unikorn-cloud/identity v0.2.44/go.mod h1:JMbS6iTYzt0OVt5AkqZys3WVnpLabGvUl8kGWcxzFZI= +github.com/unikorn-cloud/core v0.1.85 h1:S4B0nr0jhxF8SCsKyCRVwcx8+kJsI8fQVONLJDf9aic= +github.com/unikorn-cloud/core v0.1.85/go.mod h1:wEKzCwAnIyTbo27l++Wl+gK95TAxMsFS3y3jbFB03aw= +github.com/unikorn-cloud/identity v0.2.45 h1:YFmw3uunwdZMuYiimv4lfU6s7iTtYoc22xJqLTrlOVg= +github.com/unikorn-cloud/identity v0.2.45/go.mod h1:WzNNv05ReDMLfWsOtq53uzhX2GU2nXDw76Bdsf3BPnM= github.com/x448/float16 v0.8.4 h1:qLwI1I70+NjRFUR3zs1JPUCgaCXSh3SW62uAKT1mSBM= github.com/x448/float16 v0.8.4/go.mod h1:14CWIYCyZA/cWjXOioeEpHeN/83MdbZDRQHoFcYsOfg= github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= diff --git a/pkg/apis/unikorn/v1alpha1/types.go b/pkg/apis/unikorn/v1alpha1/types.go index 1a6adac..d7e7306 100644 --- a/pkg/apis/unikorn/v1alpha1/types.go +++ b/pkg/apis/unikorn/v1alpha1/types.go @@ -60,6 +60,8 @@ type Region struct { // RegionSpec defines metadata about the region. type RegionSpec struct { + // Tags are aribrary user data. + Tags unikornv1core.TagList `json:"tags,omitempty"` // Type defines the provider type. Provider Provider `json:"provider"` // Openstack is provider specific configuration for the region. @@ -255,17 +257,6 @@ type RegionStatus struct { Conditions []unikornv1core.Condition `json:"conditions,omitempty"` } -// Tag is an arbirary key/value. -type Tag struct { - // Name of the tag. - Name string `json:"name"` - // Value of the tag. - Value string `json:"value"` -} - -// TagList is an ordered list of tags. -type TagList []Tag - // IdentityList is a typed list of identities. // +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object type IdentityList struct { @@ -300,7 +291,7 @@ type IdentitySpec struct { Pause bool `json:"pause,omitempty"` // Tags are an abitrary list of key/value pairs that a client // may populate to store metadata for the resource. - Tags TagList `json:"tags,omitempty"` + Tags unikornv1core.TagList `json:"tags,omitempty"` // Provider defines the provider type. Provider Provider `json:"provider"` } @@ -383,7 +374,7 @@ type NetworkSpec struct { Pause bool `json:"pause,omitempty"` // Tags are an abitrary list of key/value pairs that a client // may populate to store metadata for the resource. - Tags TagList `json:"tags,omitempty"` + Tags unikornv1core.TagList `json:"tags,omitempty"` // Provider defines the provider type. Provider Provider `json:"provider"` // Prefix is the IPv4 address prefix. @@ -499,6 +490,8 @@ type Quota struct { } type QuotaSpec struct { + // Tags are aribrary user data. + Tags unikornv1core.TagList `json:"tags,omitempty"` // Flavors is a list of flavors and their count. // +listType=map // +listMapKey=id @@ -545,7 +538,8 @@ type SecurityGroupSpec struct { Pause bool `json:"pause,omitempty"` // Tags are an abitrary list of key/value pairs that a client // may populate to store metadata for the resource. - Tags TagList `json:"tags,omitempty"` + // Tags are aribrary user data. + Tags unikornv1core.TagList `json:"tags,omitempty"` // Provider defines the provider type. Provider Provider `json:"provider"` } @@ -608,6 +602,8 @@ type SecurityGroupRule struct { type SecurityGroupRuleSpec struct { // Pause, if true, will inhibit reconciliation. Pause bool `json:"pause,omitempty"` + // Tags are aribrary user data. + Tags unikornv1core.TagList `json:"tags,omitempty"` // Direction is the direction of the rule. Direction *SecurityGroupRuleDirection `json:"direction"` // Protocol is the protocol of the rule. @@ -710,7 +706,7 @@ type ServerSpec struct { Pause bool `json:"pause,omitempty"` // Tags are an abitrary list of key/value pairs that a client // may populate to store metadata for the resource. - Tags TagList `json:"tags,omitempty"` + Tags unikornv1core.TagList `json:"tags,omitempty"` // Provider defines the provider type. Provider Provider `json:"provider"` // FlavorID is the flavor ID. diff --git a/pkg/apis/unikorn/v1alpha1/zz_generated.deepcopy.go b/pkg/apis/unikorn/v1alpha1/zz_generated.deepcopy.go index 19f3d85..dbff267 100644 --- a/pkg/apis/unikorn/v1alpha1/zz_generated.deepcopy.go +++ b/pkg/apis/unikorn/v1alpha1/zz_generated.deepcopy.go @@ -228,7 +228,7 @@ func (in *IdentitySpec) DeepCopyInto(out *IdentitySpec) { *out = *in if in.Tags != nil { in, out := &in.Tags, &out.Tags - *out = make(TagList, len(*in)) + *out = make(unikornv1alpha1.TagList, len(*in)) copy(*out, *in) } return @@ -401,7 +401,7 @@ func (in *NetworkSpec) DeepCopyInto(out *NetworkSpec) { *out = *in if in.Tags != nil { in, out := &in.Tags, &out.Tags - *out = make(TagList, len(*in)) + *out = make(unikornv1alpha1.TagList, len(*in)) copy(*out, *in) } if in.Prefix != nil { @@ -1124,6 +1124,11 @@ func (in *QuotaList) DeepCopyObject() runtime.Object { // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *QuotaSpec) DeepCopyInto(out *QuotaSpec) { *out = *in + if in.Tags != nil { + in, out := &in.Tags, &out.Tags + *out = make(unikornv1alpha1.TagList, len(*in)) + copy(*out, *in) + } if in.Flavors != nil { in, out := &in.Flavors, &out.Flavors *out = make([]FlavorQuota, len(*in)) @@ -1357,6 +1362,11 @@ func (in *RegionOpenstackSpec) DeepCopy() *RegionOpenstackSpec { // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *RegionSpec) DeepCopyInto(out *RegionSpec) { *out = *in + if in.Tags != nil { + in, out := &in.Tags, &out.Tags + *out = make(unikornv1alpha1.TagList, len(*in)) + copy(*out, *in) + } if in.Openstack != nil { in, out := &in.Openstack, &out.Openstack *out = new(RegionOpenstackSpec) @@ -1565,6 +1575,11 @@ func (in *SecurityGroupRulePortRange) DeepCopy() *SecurityGroupRulePortRange { // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *SecurityGroupRuleSpec) DeepCopyInto(out *SecurityGroupRuleSpec) { *out = *in + if in.Tags != nil { + in, out := &in.Tags, &out.Tags + *out = make(unikornv1alpha1.TagList, len(*in)) + copy(*out, *in) + } if in.Direction != nil { in, out := &in.Direction, &out.Direction *out = new(SecurityGroupRuleDirection) @@ -1625,7 +1640,7 @@ func (in *SecurityGroupSpec) DeepCopyInto(out *SecurityGroupSpec) { *out = *in if in.Tags != nil { in, out := &in.Tags, &out.Tags - *out = make(TagList, len(*in)) + *out = make(unikornv1alpha1.TagList, len(*in)) copy(*out, *in) } return @@ -1820,7 +1835,7 @@ func (in *ServerSpec) DeepCopyInto(out *ServerSpec) { *out = *in if in.Tags != nil { in, out := &in.Tags, &out.Tags - *out = make(TagList, len(*in)) + *out = make(unikornv1alpha1.TagList, len(*in)) copy(*out, *in) } if in.Image != nil { @@ -1889,42 +1904,6 @@ func (in *ServerStatus) DeepCopy() *ServerStatus { return out } -// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *Tag) DeepCopyInto(out *Tag) { - *out = *in - return -} - -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Tag. -func (in *Tag) DeepCopy() *Tag { - if in == nil { - return nil - } - out := new(Tag) - in.DeepCopyInto(out) - return out -} - -// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in TagList) DeepCopyInto(out *TagList) { - { - in := &in - *out = make(TagList, len(*in)) - copy(*out, *in) - return - } -} - -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new TagList. -func (in TagList) DeepCopy() TagList { - if in == nil { - return nil - } - out := new(TagList) - in.DeepCopyInto(out) - return *out -} - // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *VLANAllocation) DeepCopyInto(out *VLANAllocation) { *out = *in diff --git a/pkg/handler/handler.go b/pkg/handler/handler.go index 0279ccd..f25e25c 100644 --- a/pkg/handler/handler.go +++ b/pkg/handler/handler.go @@ -301,29 +301,6 @@ func (h *Handler) GetApiV1OrganizationsOrganizationIDRegionsRegionIDImages(w htt util.WriteJSONResponse(w, r, http.StatusOK, out) } -func convertTag(in unikornv1.Tag) openapi.Tag { - out := openapi.Tag{ - Name: in.Name, - Value: in.Value, - } - - return out -} - -func convertTags(in unikornv1.TagList) openapi.TagList { - if in == nil { - return nil - } - - out := make(openapi.TagList, len(in)) - - for i := range in { - out[i] = convertTag(in[i]) - } - - return out -} - func (h *Handler) convertIdentity(ctx context.Context, in *unikornv1.Identity) *openapi.IdentityRead { provisioningStatus := coreapi.ResourceProvisioningStatusUnknown @@ -332,16 +309,12 @@ func (h *Handler) convertIdentity(ctx context.Context, in *unikornv1.Identity) * } out := &openapi.IdentityRead{ - Metadata: conversion.ProjectScopedResourceReadMetadata(in, provisioningStatus), + Metadata: conversion.ProjectScopedResourceReadMetadata(in, in.Spec.Tags, provisioningStatus), Spec: openapi.IdentitySpec{ RegionId: in.Labels[constants.RegionLabel], }, } - if tags := convertTags(in.Spec.Tags); tags != nil { - out.Spec.Tags = &tags - } - switch in.Spec.Provider { case unikornv1.ProviderOpenstack: out.Spec.Type = openapi.Openstack @@ -403,29 +376,6 @@ func (h *Handler) GetApiV1OrganizationsOrganizationIDIdentities(w http.ResponseW util.WriteJSONResponse(w, r, http.StatusOK, h.convertIdentityList(r.Context(), result)) } -func generateTag(in openapi.Tag) unikornv1.Tag { - out := unikornv1.Tag{ - Name: in.Name, - Value: in.Value, - } - - return out -} - -func generateTagList(in *openapi.TagList) unikornv1.TagList { - if in == nil { - return nil - } - - out := make(unikornv1.TagList, len(*in)) - - for i := range *in { - out[i] = generateTag((*in)[i]) - } - - return out -} - func (h *Handler) PostApiV1OrganizationsOrganizationIDProjectsProjectIDIdentities(w http.ResponseWriter, r *http.Request, organizationID openapi.OrganizationIDParameter, projectID openapi.ProjectIDParameter) { if err := rbac.AllowProjectScope(r.Context(), "region:identities", identityapi.Create, organizationID, projectID); err != nil { errors.HandleError(w, r, err) @@ -460,7 +410,7 @@ func (h *Handler) PostApiV1OrganizationsOrganizationIDProjectsProjectIDIdentitie identity := &unikornv1.Identity{ ObjectMeta: conversion.NewObjectMetadata(&request.Metadata, h.namespace, userinfo.Sub).WithOrganization(organizationID).WithProject(projectID).WithLabel(constants.RegionLabel, request.Spec.RegionId).Get(), Spec: unikornv1.IdentitySpec{ - Tags: generateTagList(request.Spec.Tags), + Tags: conversion.GenerateTagList(request.Metadata.Tags), Provider: region.Spec.Provider, }, } @@ -531,7 +481,7 @@ func (h *Handler) convertNetwork(ctx context.Context, in *unikornv1.Network) *op } out := &openapi.NetworkRead{ - Metadata: conversion.ProjectScopedResourceReadMetadata(in, provisioningStatus), + Metadata: conversion.ProjectScopedResourceReadMetadata(in, in.Spec.Tags, provisioningStatus), Spec: openapi.NetworkReadSpec{ RegionId: in.Labels[constants.RegionLabel], Prefix: in.Spec.Prefix.String(), @@ -539,10 +489,6 @@ func (h *Handler) convertNetwork(ctx context.Context, in *unikornv1.Network) *op }, } - if tags := convertTags(in.Spec.Tags); tags != nil { - out.Spec.Tags = &tags - } - switch in.Spec.Provider { case unikornv1.ProviderOpenstack: out.Spec.Type = openapi.Openstack @@ -572,7 +518,7 @@ func (h *Handler) convertNetworkList(ctx context.Context, in unikornv1.NetworkLi } func (h *Handler) GetApiV1OrganizationsOrganizationIDNetworks(w http.ResponseWriter, r *http.Request, organizationID openapi.OrganizationIDParameter) { - if err := rbac.AllowOrganizationScope(r.Context(), "region:physicalnetworks", identityapi.Read, organizationID); err != nil { + if err := rbac.AllowOrganizationScope(r.Context(), "region:networks", identityapi.Read, organizationID); err != nil { errors.HandleError(w, r, err) return } @@ -598,7 +544,7 @@ func (h *Handler) GetApiV1OrganizationsOrganizationIDNetworks(w http.ResponseWri } func (h *Handler) PostApiV1OrganizationsOrganizationIDProjectsProjectIDIdentitiesIdentityIDNetworks(w http.ResponseWriter, r *http.Request, organizationID openapi.OrganizationIDParameter, projectID openapi.ProjectIDParameter, identityID openapi.IdentityIDParameter) { - if err := rbac.AllowProjectScope(r.Context(), "region:physicalnetworks", identityapi.Create, organizationID, projectID); err != nil { + if err := rbac.AllowProjectScope(r.Context(), "region:networks", identityapi.Create, organizationID, projectID); err != nil { errors.HandleError(w, r, err) return } @@ -645,6 +591,7 @@ func (h *Handler) PostApiV1OrganizationsOrganizationIDProjectsProjectIDIdentitie network := &unikornv1.Network{ ObjectMeta: conversion.NewObjectMetadata(&request.Metadata, h.namespace, userinfo.Sub).WithOrganization(organizationID).WithProject(projectID).WithLabel(constants.RegionLabel, identity.Labels[constants.RegionLabel]).WithLabel(constants.IdentityLabel, identityID).Get(), Spec: unikornv1.NetworkSpec{ + Tags: conversion.GenerateTagList(request.Metadata.Tags), Provider: identity.Spec.Provider, Prefix: &unikornv1core.IPv4Prefix{ IPNet: *prefix, @@ -653,10 +600,6 @@ func (h *Handler) PostApiV1OrganizationsOrganizationIDProjectsProjectIDIdentitie }, } - if request.Spec != nil { - network.Spec.Tags = generateTagList(request.Spec.Tags) - } - // The resource belongs to its identity, for cascading deletion. if err := controllerutil.SetOwnerReference(identity, network, h.client.Scheme(), controllerutil.WithBlockOwnerDeletion(true)); err != nil { errors.HandleError(w, r, errors.OAuth2ServerError("unable to set resource owner").WithError(err)) @@ -672,7 +615,7 @@ func (h *Handler) PostApiV1OrganizationsOrganizationIDProjectsProjectIDIdentitie } func (h *Handler) GetApiV1OrganizationsOrganizationIDProjectsProjectIDIdentitiesIdentityIDNetworksNetworkID(w http.ResponseWriter, r *http.Request, organizationID openapi.OrganizationIDParameter, projectID openapi.ProjectIDParameter, identityID openapi.IdentityIDParameter, physicalNetworkID openapi.NetworkIDParameter) { - if err := rbac.AllowProjectScope(r.Context(), "region:physicalnetworks", identityapi.Read, organizationID, projectID); err != nil { + if err := rbac.AllowProjectScope(r.Context(), "region:networks", identityapi.Read, organizationID, projectID); err != nil { errors.HandleError(w, r, err) return } @@ -687,7 +630,7 @@ func (h *Handler) GetApiV1OrganizationsOrganizationIDProjectsProjectIDIdentities } func (h *Handler) DeleteApiV1OrganizationsOrganizationIDProjectsProjectIDIdentitiesIdentityIDNetworksNetworkID(w http.ResponseWriter, r *http.Request, organizationID openapi.OrganizationIDParameter, projectID openapi.ProjectIDParameter, identityID openapi.IdentityIDParameter, physicalNetworkID openapi.NetworkIDParameter) { - if err := rbac.AllowProjectScope(r.Context(), "region:physicalnetworks", identityapi.Delete, organizationID, projectID); err != nil { + if err := rbac.AllowProjectScope(r.Context(), "region:networks", identityapi.Delete, organizationID, projectID); err != nil { errors.HandleError(w, r, err) return } @@ -826,6 +769,7 @@ func (h *Handler) generateQuota(ctx context.Context, organizationID, projectID s resource := &unikornv1.Quota{ ObjectMeta: conversion.NewObjectMetadata(metadata, h.namespace, userinfo.Sub).WithOrganization(organizationID).WithProject(projectID).WithLabel(constants.RegionLabel, identity.Labels[constants.RegionLabel]).WithLabel(constants.IdentityLabel, identity.Name).Get(), Spec: unikornv1.QuotaSpec{ + // TODO: tags?? Flavors: generateFlavorQuotas(in.Flavors), }, } @@ -942,16 +886,12 @@ func (h *Handler) convertSecurityGroup(in *unikornv1.SecurityGroup) *openapi.Sec } out := &openapi.SecurityGroupRead{ - Metadata: conversion.ProjectScopedResourceReadMetadata(in, provisioningStatus), + Metadata: conversion.ProjectScopedResourceReadMetadata(in, in.Spec.Tags, provisioningStatus), Spec: openapi.SecurityGroupReadSpec{ RegionId: in.Labels[constants.RegionLabel], }, } - if tags := convertTags(in.Spec.Tags); tags != nil { - out.Spec.Tags = &tags - } - return out } @@ -1009,14 +949,11 @@ func (h *Handler) generateSecurityGroup(ctx context.Context, organizationID, pro ObjectMeta: conversion.NewObjectMetadata(&in.Metadata, h.namespace, userinfo.Sub).WithOrganization(organizationID).WithProject(projectID).WithLabel(constants.RegionLabel, identity.Labels[constants.RegionLabel]). WithLabel(constants.IdentityLabel, identity.Name).Get(), Spec: unikornv1.SecurityGroupSpec{ + Tags: conversion.GenerateTagList(in.Metadata.Tags), Provider: identity.Spec.Provider, }, } - if in.Spec != nil { - resource.Spec.Tags = generateTagList(in.Spec.Tags) - } - // Ensure the security is owned by the identity so it is automatically cleaned // up on identity deletion. if err := controllerutil.SetOwnerReference(identity, resource, h.client.Scheme(), controllerutil.WithBlockOwnerDeletion(true)); err != nil { @@ -1244,7 +1181,7 @@ func (h *Handler) convertSecurityGroupRule(in *unikornv1.SecurityGroupRule) *ope } out := &openapi.SecurityGroupRuleRead{ - Metadata: conversion.ProjectScopedResourceReadMetadata(in, provisioningStatus), + Metadata: conversion.ProjectScopedResourceReadMetadata(in, in.Spec.Tags, provisioningStatus), Spec: openapi.SecurityGroupRuleReadSpec{ Direction: convertSecurityGroupRuleDirection(*in.Spec.Direction), Protocol: openapi.SecurityGroupRuleReadSpecProtocol(*in.Spec.Protocol), @@ -1315,6 +1252,7 @@ func (h *Handler) generateSecurityGroupRule(ctx context.Context, organizationID, ObjectMeta: conversion.NewObjectMetadata(&in.Metadata, h.namespace, userinfo.Sub).WithOrganization(organizationID).WithProject(projectID).WithLabel(constants.RegionLabel, identity.Labels[constants.RegionLabel]). WithLabel(constants.IdentityLabel, identity.Name).WithLabel(constants.SecurityGroupLabel, securityGroup.Name).Get(), Spec: unikornv1.SecurityGroupRuleSpec{ + Tags: conversion.GenerateTagList(in.Metadata.Tags), Direction: generateSecurityGroupRuleDirection(in.Spec.Direction), Protocol: generateSecurityGroupRuleProtocol(in.Spec.Protocol), Port: generateSecurityGroupRulePort(in.Spec.Port), diff --git a/pkg/handler/region/region.go b/pkg/handler/region/region.go index 6489c6c..85f555d 100644 --- a/pkg/handler/region/region.go +++ b/pkg/handler/region/region.go @@ -122,7 +122,7 @@ func convertRegionType(in unikornv1.Provider) openapi.RegionType { func convert(in *unikornv1.Region) *openapi.RegionRead { out := &openapi.RegionRead{ - Metadata: conversion.ResourceReadMetadata(in, coreopenapi.ResourceProvisioningStatusProvisioned), + Metadata: conversion.ResourceReadMetadata(in, in.Spec.Tags, coreopenapi.ResourceProvisioningStatusProvisioned), Spec: openapi.RegionSpec{ Type: convertRegionType(in.Spec.Provider), }, diff --git a/pkg/handler/server/conversion.go b/pkg/handler/server/conversion.go index 9b8f2c2..e695e93 100644 --- a/pkg/handler/server/conversion.go +++ b/pkg/handler/server/conversion.go @@ -52,7 +52,7 @@ func convert(in *unikornv1.Server) *openapi.ServerRead { } out := &openapi.ServerRead{ - Metadata: conversion.ProjectScopedResourceReadMetadata(in, provisioningStatus), + Metadata: conversion.ProjectScopedResourceReadMetadata(in, in.Spec.Tags, provisioningStatus), Spec: openapi.ServerReadSpec{ FlavorId: in.Spec.FlavorID, Image: convertServerImage(in.Spec.Image), @@ -66,10 +66,6 @@ func convert(in *unikornv1.Server) *openapi.ServerRead { }, } - if tags := convertTags(in.Spec.Tags); tags != nil { - out.Spec.Tags = &tags - } - return out } @@ -125,52 +121,6 @@ func convertServerSecurityGroup(in *unikornv1.ServerSecurityGroupSpec) openapi.S } } -func convertTag(in unikornv1.Tag) openapi.Tag { - out := openapi.Tag{ - Name: in.Name, - Value: in.Value, - } - - return out -} - -func convertTags(in unikornv1.TagList) openapi.TagList { - if in == nil { - return nil - } - - out := make(openapi.TagList, len(in)) - - for i := range in { - out[i] = convertTag(in[i]) - } - - return out -} - -func generateTag(in openapi.Tag) unikornv1.Tag { - out := unikornv1.Tag{ - Name: in.Name, - Value: in.Value, - } - - return out -} - -func generateTagList(in *openapi.TagList) unikornv1.TagList { - if in == nil { - return nil - } - - out := make(unikornv1.TagList, len(*in)) - - for i := range *in { - out[i] = generateTag((*in)[i]) - } - - return out -} - type generator struct { // client allows Kubernetes API access. client client.Client @@ -207,7 +157,7 @@ func (g *generator) generate(ctx context.Context, in *openapi.ServerWrite) (*uni ObjectMeta: conversion.NewObjectMetadata(&in.Metadata, g.namespace, userinfo.Sub).WithOrganization(g.organizationID).WithProject(g.projectID).WithLabel(constants.RegionLabel, g.identity.Labels[constants.RegionLabel]). WithLabel(constants.IdentityLabel, g.identity.Name).Get(), Spec: unikornv1.ServerSpec{ - Tags: generateTagList(in.Spec.Tags), + Tags: conversion.GenerateTagList(in.Metadata.Tags), Provider: g.identity.Spec.Provider, FlavorID: in.Spec.FlavorId, Image: g.generateImage(&in.Spec.Image), diff --git a/pkg/openapi/schema.go b/pkg/openapi/schema.go index e6864b1..4125f37 100644 --- a/pkg/openapi/schema.go +++ b/pkg/openapi/schema.go @@ -19,138 +19,138 @@ import ( // Base64 encoded, gzipped, json marshaled Swagger object var swaggerSpec = []string{ - "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", } // GetSwagger returns the content of the embedded swagger specification file diff --git a/pkg/openapi/server.spec.yaml b/pkg/openapi/server.spec.yaml index 4b3b8e6..6871ae0 100644 --- a/pkg/openapi/server.spec.yaml +++ b/pkg/openapi/server.spec.yaml @@ -826,32 +826,12 @@ components: type: array items: $ref: '#/components/schemas/flavor' - tag: - description: An arbitrary tag name and value. - type: object - required: - - name - - value - properties: - name: - description: A unique tag name. - type: string - value: - description: The value of the tag. - type: string - tagList: - description: A list of tags. - type: array - items: - $ref: '#/components/schemas/tag' identityWriteSpec: description: Request parameters for creating an identity. type: object required: - regionId properties: - tags: - $ref: '#/components/schemas/tagList' regionId: description: The region an identity is provisioned in. type: string @@ -898,8 +878,6 @@ components: - type - regionId properties: - tags: - $ref: '#/components/schemas/tagList' type: $ref: '#/components/schemas/regionType' regionId: @@ -940,8 +918,6 @@ components: - dnsNameservers - type properties: - tags: - $ref: '#/components/schemas/tagList' prefix: description: An IPv4 prefix for the network. type: string @@ -976,8 +952,6 @@ components: - prefix - dnsNameservers properties: - tags: - $ref: '#/components/schemas/tagList' prefix: description: An IPv4 prefix for the network. type: string @@ -1152,17 +1126,12 @@ components: securityGroupWriteSpec: description: A security group's specification. type: object - properties: - tags: - $ref: '#/components/schemas/tagList' securityGroupReadSpec: description: A security group's specification. type: object required: - regionId properties: - tags: - $ref: '#/components/schemas/tagList' regionId: description: The region an identity is provisioned in. type: string @@ -1200,8 +1169,6 @@ components: - image - networks properties: - tags: - $ref: '#/components/schemas/tagList' flavorId: description: The flavor of the server. type: string @@ -1293,8 +1260,6 @@ components: - image - networks properties: - tags: - $ref: '#/components/schemas/tagList' flavorId: description: The flavor of the server. type: string @@ -1348,11 +1313,11 @@ components: metadata: name: identity-name description: A verbose description - spec: - regionId: c7568e2d-f9ab-453d-9a3a-51375f78426b tags: - name: cluster-id value: 9361402c-f998-49cc-ab21-9bb99afcfde8 + spec: + regionId: c7568e2d-f9ab-453d-9a3a-51375f78426b networkRequest: description: A request for a network. content: @@ -1363,10 +1328,10 @@ components: metadata: name: network-name description: A verbose description - spec: tags: - name: cluster-id value: 9361402c-f998-49cc-ab21-9bb99afcfde8 + spec: prefix: 192.168.0.0/24 dnsNameservers: - 8.8.8.8 @@ -1390,7 +1355,6 @@ components: metadata: name: security-group-name description: A verbose description - spec: tags: - name: cluster-id value: 9361402c-f998-49cc-ab21-9bb99afcfde8 @@ -1420,6 +1384,9 @@ components: metadata: name: server-name description: A verbose description + tags: + - name: tag-name + value: tag-value spec: flavorId: 9a8c6370-4065-4d4a-9da0-7678df40cd9d image: @@ -1431,9 +1398,6 @@ components: enabled: true networks: - id: 9a8c6370-4065-4d4a-9da0-7678df40cd9d - tags: - - name: tag-name - value: tag-value responses: regionsResponse: description: A list of regions. @@ -1720,6 +1684,9 @@ components: creationTime: 2024-05-31T14:11:00Z createdBy: john.doe@acme.com provisioningStatus: provisioned + tags: + - name: tag-name + value: tag-value spec: flavorId: 9a8c6370-4065-4d4a-9da0-7678df40cd9d image: @@ -1731,9 +1698,6 @@ components: enabled: true networks: - id: 9a8c6370-4065-4d4a-9da0-7678df40cd9d - tags: - - name: tag-name - value: tag-value status: privateIP: 192.168.1.50 publicIP: 142.250.66.196 @@ -1752,6 +1716,9 @@ components: creationTime: 2024-05-31T14:11:00Z createdBy: john.doe@acme.com provisioningStatus: provisioned + tags: + - name: tag-name + value: tag-value spec: flavorId: 9a8c6370-4065-4d4a-9da0-7678df40cd9d image: @@ -1763,9 +1730,6 @@ components: enabled: true networks: - id: 9a8c6370-4065-4d4a-9da0-7678df40cd9d - tags: - - name: tag-name - value: tag-value status: privateIP: 192.168.1.50 publicIP: 142.250.66.196 diff --git a/pkg/openapi/types.go b/pkg/openapi/types.go index d8e6d8e..66590b2 100644 --- a/pkg/openapi/types.go +++ b/pkg/openapi/types.go @@ -162,9 +162,6 @@ type IdentitySpec struct { // RegionId The region an identity is provisioned in. RegionId string `json:"regionId"` - // Tags A list of tags. - Tags *TagList `json:"tags,omitempty"` - // Type The region's provider type. Type RegionType `json:"type"` } @@ -203,9 +200,6 @@ type IdentityWrite struct { type IdentityWriteSpec struct { // RegionId The region an identity is provisioned in. RegionId string `json:"regionId"` - - // Tags A list of tags. - Tags *TagList `json:"tags,omitempty"` } // Image An image. @@ -280,9 +274,6 @@ type NetworkReadSpec struct { // RegionId The region an identity is provisioned in. RegionId string `json:"regionId"` - // Tags A list of tags. - Tags *TagList `json:"tags,omitempty"` - // Type The region's provider type. Type RegionType `json:"type"` } @@ -318,9 +309,6 @@ type NetworkWriteSpec struct { // Prefix An IPv4 prefix for the network. Prefix string `json:"prefix"` - - // Tags A list of tags. - Tags *TagList `json:"tags,omitempty"` } // NetworksRead A list of networks. @@ -377,9 +365,6 @@ type SecurityGroupRead struct { type SecurityGroupReadSpec struct { // RegionId The region an identity is provisioned in. RegionId string `json:"regionId"` - - // Tags A list of tags. - Tags *TagList `json:"tags,omitempty"` } // SecurityGroupRulePort The port definition to allow traffic. @@ -472,10 +457,7 @@ type SecurityGroupWrite struct { } // SecurityGroupWriteSpec A security group's specification. -type SecurityGroupWriteSpec struct { - // Tags A list of tags. - Tags *TagList `json:"tags,omitempty"` -} +type SecurityGroupWriteSpec = map[string]interface{} // SecurityGroupsRead A list of security groups. type SecurityGroupsRead = []SecurityGroupRead @@ -540,9 +522,6 @@ type ServerReadSpec struct { // SecurityGroups A list of security groups. SecurityGroups *ServerSecurityGroupList `json:"securityGroups,omitempty"` - - // Tags A list of tags. - Tags *TagList `json:"tags,omitempty"` } // ServerReadStatus A server's status. @@ -588,9 +567,6 @@ type ServerWriteSpec struct { // SecurityGroups A list of security groups. SecurityGroups *ServerSecurityGroupList `json:"securityGroups,omitempty"` - - // Tags A list of tags. - Tags *TagList `json:"tags,omitempty"` } // ServersRead A list of servers. @@ -602,18 +578,6 @@ type SoftwareVersions struct { Kubernetes *externalRef0.Semver `json:"kubernetes,omitempty"` } -// Tag An arbitrary tag name and value. -type Tag struct { - // Name A unique tag name. - Name string `json:"name"` - - // Value The value of the tag. - Value string `json:"value"` -} - -// TagList A list of tags. -type TagList = []Tag - // IdentityIDParameter A Kubernetes name. Must be a valid DNS containing only lower case characters, numbers or hyphens, start and end with a character or number, and be at most 63 characters in length. type IdentityIDParameter = KubernetesNameParameter