Skip to content

Commit

Permalink
Improve Region Cloud APIs (#4)
Browse files Browse the repository at this point in the history
Make everything use the generic metadata methodology.  Make everything
more flexible and provide better support for heterogenous GPU compute by
exposing GPU on both images and flavors to allow better matching.
  • Loading branch information
spjmurray authored Jun 7, 2024
1 parent a6af832 commit d4995a3
Show file tree
Hide file tree
Showing 10 changed files with 310 additions and 192 deletions.
2 changes: 1 addition & 1 deletion go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ 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.43
github.com/unikorn-cloud/core v0.1.45
go.opentelemetry.io/otel v1.27.0
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.27.0
go.opentelemetry.io/otel/sdk v1.27.0
Expand Down
4 changes: 2 additions & 2 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -136,8 +136,8 @@ 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.43 h1:QszxVqWaZXIzSlf0qkHa5m8cRnjKGvHjM8iUk0Y3U9A=
github.com/unikorn-cloud/core v0.1.43/go.mod h1:cP39UQN7aSmsfjQuSMsworI4oBIwx4oA4u20CbPpfZw=
github.com/unikorn-cloud/core v0.1.45 h1:EmnY4p3LWrCSPQVc/jgPN/i3Oau7TFS1PQyMPKwwXSM=
github.com/unikorn-cloud/core v0.1.45/go.mod h1:cP39UQN7aSmsfjQuSMsworI4oBIwx4oA4u20CbPpfZw=
github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
Expand Down
12 changes: 6 additions & 6 deletions pkg/apis/unikorn/v1alpha1/types.go
Original file line number Diff line number Diff line change
Expand Up @@ -134,9 +134,9 @@ type RegionStatus struct {
// IdentityList is a typed list of identities.
// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object
type IdentityList struct {
metav1.TypeMeta `json:",inline"`
metav1.ListMeta `json:"metadata,omitempty"`
Items []Identity `json:"items"`
metav1.TypeMeta `json:",inline"`
metav1.ListMeta `json:"metadata,omitempty"`
Items []Identity `json:"items"`
}

// Identity defines an on-demand cloud identity. The region controller must
Expand All @@ -152,9 +152,9 @@ type IdentityList struct {
// +kubebuilder:printcolumn:name="status",type="string",JSONPath=".status.conditions[?(@.type==\"Available\")].reason"
// +kubebuilder:printcolumn:name="age",type="date",JSONPath=".metadata.creationTimestamp"
type Identity struct {
metav1.TypeMeta `json:",inline"`
metav1.ObjectMeta `json:"metadata,omitempty"`
Spec IdentitySpec `json:"spec"`
metav1.TypeMeta `json:",inline"`
metav1.ObjectMeta `json:"metadata,omitempty"`
Spec IdentitySpec `json:"spec"`
Status IdentityStatus `json:"status"`
}

Expand Down
88 changes: 63 additions & 25 deletions pkg/handler/handler.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,11 +24,13 @@ import (
"sort"
"time"

coreopenapi "github.com/unikorn-cloud/core/pkg/openapi"
"github.com/unikorn-cloud/core/pkg/server/errors"
"github.com/unikorn-cloud/core/pkg/server/middleware/openapi/oidc"
coreutil "github.com/unikorn-cloud/core/pkg/util"
"github.com/unikorn-cloud/region/pkg/handler/region"
"github.com/unikorn-cloud/region/pkg/openapi"
"github.com/unikorn-cloud/region/pkg/providers"
"github.com/unikorn-cloud/region/pkg/server/util"

"sigs.k8s.io/controller-runtime/pkg/client"
Expand Down Expand Up @@ -78,6 +80,41 @@ func (h *Handler) GetApiV1Regions(w http.ResponseWriter, r *http.Request) {
util.WriteJSONResponse(w, r, http.StatusOK, result)
}

func convertGpuVendor(in providers.GPUVendor) openapi.GpuVendor {
switch in {
case providers.Nvidia:
return openapi.Nvidia
case providers.AMD:
return openapi.Amd
}

return ""
}

func convertFlavor(in providers.Flavor) openapi.Flavor {
out := openapi.Flavor{
Metadata: coreopenapi.StaticResourceMetadata{
Id: in.ID,
Name: in.Name,
},
Spec: openapi.FlavorSpec{
Cpus: in.CPUs,
Memory: int(in.Memory.Value()) >> 30,
Disk: int(in.Disk.Value()) / 1000000000,
},
}

if in.GPUs != 0 {
out.Spec.Gpu = &openapi.GpuSpec{
Vendor: convertGpuVendor(in.GPUVendor),
Model: "H100",
Count: in.GPUs,
}
}

return out
}

func (h *Handler) GetApiV1RegionsRegionIDFlavors(w http.ResponseWriter, r *http.Request, regionID openapi.RegionIDParameter) {
provider, err := region.NewClient(h.client, h.namespace).Provider(r.Context(), regionID)
if err != nil {
Expand All @@ -94,27 +131,35 @@ func (h *Handler) GetApiV1RegionsRegionIDFlavors(w http.ResponseWriter, r *http.
// Apply ordering guarantees.
sort.Stable(result)

out := make(openapi.Flavors, 0, len(result))
out := make(openapi.Flavors, len(result))

for _, r := range result {
t := openapi.Flavor{
Name: r.Name,
Cpus: r.CPUs,
Memory: int(r.Memory.Value()) >> 30,
Disk: int(r.Disk.Value()) / 1000000000,
}

if r.GPUs != 0 {
t.Gpus = coreutil.ToPointer(r.GPUs)
}

out = append(out, t)
for i := range result {
out[i] = convertFlavor(result[i])
}

h.setCacheable(w)
util.WriteJSONResponse(w, r, http.StatusOK, out)
}

func convertImage(in providers.Image) openapi.Image {
out := openapi.Image{
Metadata: coreopenapi.StaticResourceMetadata{
Id: in.ID,
Name: in.Name,
CreationTime: in.Created,
},
Spec: openapi.ImageSpec{
SoftwareVersions: &openapi.SoftwareVersions{},
},
}

if in.KubernetesVersion != "" {
out.Spec.SoftwareVersions.Kubernetes = coreutil.ToPointer(in.KubernetesVersion)
}

return out
}

func (h *Handler) GetApiV1RegionsRegionIDImages(w http.ResponseWriter, r *http.Request, regionID openapi.RegionIDParameter) {
provider, err := region.NewClient(h.client, h.namespace).Provider(r.Context(), regionID)
if err != nil {
Expand All @@ -131,17 +176,10 @@ func (h *Handler) GetApiV1RegionsRegionIDImages(w http.ResponseWriter, r *http.R
// Apply ordering guarantees.
sort.Stable(result)

out := make(openapi.Images, 0, len(result))

for _, r := range result {
out = append(out, openapi.Image{
Name: r.Name,
Created: r.Created,
Modified: r.Modified,
Versions: openapi.ImageVersions{
Kubernetes: r.KubernetesVersion,
},
})
out := make(openapi.Images, len(result))

for i := range result {
out[i] = convertImage(result[i])
}

h.setCacheable(w)
Expand Down
8 changes: 4 additions & 4 deletions pkg/handler/region/region.go
Original file line number Diff line number Diff line change
Expand Up @@ -63,9 +63,9 @@ func (c *Client) list(ctx context.Context) (*unikornv1.RegionList, error) {
return &regions, nil
}

func findRegion(regions *unikornv1.RegionList, region string) (*unikornv1.Region, error) {
func findRegion(regions *unikornv1.RegionList, regionID string) (*unikornv1.Region, error) {
for i := range regions.Items {
if regions.Items[i].Name == region {
if regions.Items[i].Name == regionID {
return &regions.Items[i], nil
}
}
Expand All @@ -86,13 +86,13 @@ func (c Client) newProvider(region *unikornv1.Region) (providers.Provider, error
return nil, ErrRegionProviderUnimplmented
}

func (c *Client) Provider(ctx context.Context, regionName string) (providers.Provider, error) {
func (c *Client) Provider(ctx context.Context, regionID string) (providers.Provider, error) {
regions, err := c.list(ctx)
if err != nil {
return nil, err
}

region, err := findRegion(regions, regionName)
region, err := findRegion(regions, regionID)
if err != nil {
return nil, err
}
Expand Down
128 changes: 65 additions & 63 deletions pkg/openapi/schema.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Loading

0 comments on commit d4995a3

Please sign in to comment.