diff --git a/charts/compute/crds/compute.unikorn-cloud.org_computeclusters.yaml b/charts/compute/crds/compute.unikorn-cloud.org_computeclusters.yaml index cc3c8ca..fc58bea 100644 --- a/charts/compute/crds/compute.unikorn-cloud.org_computeclusters.yaml +++ b/charts/compute/crds/compute.unikorn-cloud.org_computeclusters.yaml @@ -165,6 +165,23 @@ spec: description: Image is the region service image to deploy with. type: string + imageSelector: + description: ImageSelector is the image selector to use + for the pool. + properties: + distro: + description: Distro A distribution name. + type: string + variant: + description: Variant A free form variant e.g. desktop/server. + type: string + version: + description: Version Version of the operating system + e.g. "24.04". + type: string + required: + - distro + type: object name: description: Name is the name of the pool. type: string diff --git a/pkg/apis/unikorn/v1alpha1/types.go b/pkg/apis/unikorn/v1alpha1/types.go index 2935e82..98aabd5 100644 --- a/pkg/apis/unikorn/v1alpha1/types.go +++ b/pkg/apis/unikorn/v1alpha1/types.go @@ -34,6 +34,8 @@ type ComputeWorkloadPoolSpec struct { Firewall []FirewallRule `json:"firewall,omitempty"` // UserData contains configuration information or scripts to use upon launch. UserData []byte `json:"userData,omitempty"` + // ImageSelector is the image selector to use for the pool. + ImageSelector *ComputeWorkloadPoolImageSelector `json:"imageSelector,omitempty"` } type PublicIPAllocationSpec struct { @@ -41,6 +43,22 @@ type PublicIPAllocationSpec struct { Enabled bool `json:"enabled,omitempty"` } +type ComputeWorkloadPoolImageSelector struct { + // Distro A distribution name. + Distro OsDistro `json:"distro"` + // Variant A free form variant e.g. desktop/server. + Variant *string `json:"variant,omitempty"` + // Version of the operating system e.g. "24.04". + Version *string `json:"version,omitempty"` +} + +type OsDistro string + +const ( + Rocky OsDistro = "rocky" + Ubuntu OsDistro = "ubuntu" +) + // +kubebuilder:validation:Enum=ingress;egress type FirewallRuleDirection string diff --git a/pkg/apis/unikorn/v1alpha1/zz_generated.deepcopy.go b/pkg/apis/unikorn/v1alpha1/zz_generated.deepcopy.go index dba8818..beaedbe 100644 --- a/pkg/apis/unikorn/v1alpha1/zz_generated.deepcopy.go +++ b/pkg/apis/unikorn/v1alpha1/zz_generated.deepcopy.go @@ -193,6 +193,32 @@ func (in *ComputeClusterWorkloadPoolsSpec) DeepCopy() *ComputeClusterWorkloadPoo return out } +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *ComputeWorkloadPoolImageSelector) DeepCopyInto(out *ComputeWorkloadPoolImageSelector) { + *out = *in + if in.Variant != nil { + in, out := &in.Variant, &out.Variant + *out = new(string) + **out = **in + } + if in.Version != nil { + in, out := &in.Version, &out.Version + *out = new(string) + **out = **in + } + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ComputeWorkloadPoolImageSelector. +func (in *ComputeWorkloadPoolImageSelector) DeepCopy() *ComputeWorkloadPoolImageSelector { + if in == nil { + return nil + } + out := new(ComputeWorkloadPoolImageSelector) + in.DeepCopyInto(out) + return out +} + // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *ComputeWorkloadPoolSpec) DeepCopyInto(out *ComputeWorkloadPoolSpec) { *out = *in @@ -214,6 +240,11 @@ func (in *ComputeWorkloadPoolSpec) DeepCopyInto(out *ComputeWorkloadPoolSpec) { *out = make([]byte, len(*in)) copy(*out, *in) } + if in.ImageSelector != nil { + in, out := &in.ImageSelector, &out.ImageSelector + *out = new(ComputeWorkloadPoolImageSelector) + (*in).DeepCopyInto(*out) + } return } diff --git a/pkg/openapi/schema.go b/pkg/openapi/schema.go index bd3fe81..1a004c3 100644 --- a/pkg/openapi/schema.go +++ b/pkg/openapi/schema.go @@ -20,97 +20,97 @@ 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 f07aefc..c3de72c 100644 --- a/pkg/openapi/server.spec.yaml +++ b/pkg/openapi/server.spec.yaml @@ -226,7 +226,7 @@ components: publicIPAllocation: $ref: '#/components/schemas/publicIPAllocation' image: - $ref: '#/components/schemas/imageSelector' + $ref: '#/components/schemas/computeImage' userData: description: UserData contains base64-encoded configuration information or scripts to use upon launch. type: string @@ -277,6 +277,18 @@ components: enabled: description: Enable public IP allocation. type: boolean + computeImage: + description: The image to use for a server. + type: object + properties: + id: + description: The image ID. + type: string + selector: + $ref: '#/components/schemas/imageSelector' + oneOf: + - required: [id] + - required: [selector] imageSelector: description: A server image selector. type: object @@ -420,8 +432,9 @@ components: machine: flavorId: c7568e2d-f9ab-453d-9a3a-51375f78426b image: - distro: ubuntu - version: '24.04' + selector: + distro: ubuntu + version: '24.04' replicas: 3 firewall: - direction: ingress @@ -438,9 +451,6 @@ components: - 172.16.0.0/12 publicIPAllocation: enabled: true - image: - os: ubuntu - version: 20.04 responses: computeClusterResponse: description: A Compute cluster. @@ -463,8 +473,9 @@ components: machine: flavorId: c7568e2d-f9ab-453d-9a3a-51375f78426b image: - distro: ubuntu - version: '24.04' + selector: + distro: ubuntu + version: '24.04' replicas: 3 firewall: - direction: ingress @@ -481,11 +492,6 @@ components: - 172.16.0.0/12 publicIPAllocation: enabled: true - image: - kernel: linux - family: debian - distro: ubuntu - version: '20.04' status: workloadPools: - name: default @@ -516,8 +522,9 @@ components: machine: flavorId: c7568e2d-f9ab-453d-9a3a-51375f78426b image: - distro: ubuntu - version: '24.04' + selector: + distro: ubuntu + version: '24.04' replicas: 3 firewall: - direction: ingress @@ -534,11 +541,6 @@ components: - 172.16.0.0/12 publicIPAllocation: enabled: true - image: - kernel: linux - family: debian - dsitro: ubuntu - version: 20.04 status: workloadPools: - name: default diff --git a/pkg/openapi/types.go b/pkg/openapi/types.go index ffa88fb..f0a3028 100644 --- a/pkg/openapi/types.go +++ b/pkg/openapi/types.go @@ -4,6 +4,10 @@ package openapi import ( + "encoding/json" + "fmt" + + "github.com/oapi-codegen/runtime" externalRef0 "github.com/unikorn-cloud/core/pkg/openapi" externalRef1 "github.com/unikorn-cloud/region/pkg/openapi" ) @@ -110,6 +114,22 @@ type ComputeClusterWrite struct { // ComputeClusters A list of Compute clusters. type ComputeClusters = []ComputeClusterRead +// ComputeImage The image to use for a server. +type ComputeImage struct { + // Id The image ID. + Id *string `json:"id,omitempty"` + + // Selector A server image selector. + Selector *ImageSelector `json:"selector,omitempty"` + union json.RawMessage +} + +// ComputeImage0 defines model for . +type ComputeImage0 = interface{} + +// ComputeImage1 defines model for . +type ComputeImage1 = interface{} + // FirewallRule A firewall rule applied to a workload pool. type FirewallRule struct { // Direction The direction of network traffic to apply the rule to. @@ -163,8 +183,8 @@ type MachinePool struct { // FlavorId Flavor ID. FlavorId string `json:"flavorId"` - // Image A server image selector. - Image ImageSelector `json:"image"` + // Image The image to use for a server. + Image ComputeImage `json:"image"` // PublicIPAllocation A public IP allocation settings. PublicIPAllocation *PublicIPAllocation `json:"publicIPAllocation,omitempty"` @@ -214,3 +234,113 @@ type PostApiV1OrganizationsOrganizationIDProjectsProjectIDClustersJSONRequestBod // PutApiV1OrganizationsOrganizationIDProjectsProjectIDClustersClusterIDJSONRequestBody defines body for PutApiV1OrganizationsOrganizationIDProjectsProjectIDClustersClusterID for application/json ContentType. type PutApiV1OrganizationsOrganizationIDProjectsProjectIDClustersClusterIDJSONRequestBody = ComputeClusterWrite + +// AsComputeImage0 returns the union data inside the ComputeImage as a ComputeImage0 +func (t ComputeImage) AsComputeImage0() (ComputeImage0, error) { + var body ComputeImage0 + err := json.Unmarshal(t.union, &body) + return body, err +} + +// FromComputeImage0 overwrites any union data inside the ComputeImage as the provided ComputeImage0 +func (t *ComputeImage) FromComputeImage0(v ComputeImage0) error { + b, err := json.Marshal(v) + t.union = b + return err +} + +// MergeComputeImage0 performs a merge with any union data inside the ComputeImage, using the provided ComputeImage0 +func (t *ComputeImage) MergeComputeImage0(v ComputeImage0) error { + b, err := json.Marshal(v) + if err != nil { + return err + } + + merged, err := runtime.JSONMerge(t.union, b) + t.union = merged + return err +} + +// AsComputeImage1 returns the union data inside the ComputeImage as a ComputeImage1 +func (t ComputeImage) AsComputeImage1() (ComputeImage1, error) { + var body ComputeImage1 + err := json.Unmarshal(t.union, &body) + return body, err +} + +// FromComputeImage1 overwrites any union data inside the ComputeImage as the provided ComputeImage1 +func (t *ComputeImage) FromComputeImage1(v ComputeImage1) error { + b, err := json.Marshal(v) + t.union = b + return err +} + +// MergeComputeImage1 performs a merge with any union data inside the ComputeImage, using the provided ComputeImage1 +func (t *ComputeImage) MergeComputeImage1(v ComputeImage1) error { + b, err := json.Marshal(v) + if err != nil { + return err + } + + merged, err := runtime.JSONMerge(t.union, b) + t.union = merged + return err +} + +func (t ComputeImage) MarshalJSON() ([]byte, error) { + b, err := t.union.MarshalJSON() + if err != nil { + return nil, err + } + object := make(map[string]json.RawMessage) + if t.union != nil { + err = json.Unmarshal(b, &object) + if err != nil { + return nil, err + } + } + + if t.Id != nil { + object["id"], err = json.Marshal(t.Id) + if err != nil { + return nil, fmt.Errorf("error marshaling 'id': %w", err) + } + } + + if t.Selector != nil { + object["selector"], err = json.Marshal(t.Selector) + if err != nil { + return nil, fmt.Errorf("error marshaling 'selector': %w", err) + } + } + b, err = json.Marshal(object) + return b, err +} + +func (t *ComputeImage) UnmarshalJSON(b []byte) error { + err := t.union.UnmarshalJSON(b) + if err != nil { + return err + } + object := make(map[string]json.RawMessage) + err = json.Unmarshal(b, &object) + if err != nil { + return err + } + + if raw, found := object["id"]; found { + err = json.Unmarshal(raw, &t.Id) + if err != nil { + return fmt.Errorf("error reading 'id': %w", err) + } + } + + if raw, found := object["selector"]; found { + err = json.Unmarshal(raw, &t.Selector) + if err != nil { + return fmt.Errorf("error reading 'selector': %w", err) + } + } + + return err +} diff --git a/pkg/server/handler/cluster/client.go b/pkg/server/handler/cluster/client.go index dfa0faf..d9d0fee 100644 --- a/pkg/server/handler/cluster/client.go +++ b/pkg/server/handler/cluster/client.go @@ -106,7 +106,7 @@ func (c *Client) List(ctx context.Context, organizationID string) (openapi.Compu return strings.Compare(a.Name, b.Name) }) - return newGenerator(c.client, c.options, c.region, "", organizationID, "", nil).convertList(ctx, result) + return newGenerator(c.client, c.options, c.region, "", organizationID, "", nil).convertList(result), nil } // get returns the cluster. @@ -237,7 +237,7 @@ func (c *Client) Create(ctx context.Context, organizationID, projectID string, r return nil, errors.OAuth2ServerError("failed to create cluster").WithError(err) } - return newGenerator(c.client, c.options, c.region, "", organizationID, "", nil).convert(ctx, cluster) + return newGenerator(c.client, c.options, c.region, "", organizationID, "", nil).convert(cluster), nil } // Delete deletes the implicit cluster indentified by the JTW claims. diff --git a/pkg/server/handler/cluster/conversion.go b/pkg/server/handler/cluster/conversion.go index dd6485d..3a9caa3 100644 --- a/pkg/server/handler/cluster/conversion.go +++ b/pkg/server/handler/cluster/conversion.go @@ -74,48 +74,33 @@ func newGenerator(client client.Client, options *Options, region regionapi.Clien } } -func (g *generator) getImage(ctx context.Context, regionID, imageID string) (*regionapi.Image, error) { - resp, err := g.region.GetApiV1OrganizationsOrganizationIDRegionsRegionIDImagesWithResponse(ctx, g.organizationID, regionID) - if err != nil { - return nil, err - } - - if resp.StatusCode() != http.StatusOK { - return nil, errors.OAuth2ServerError("failed to list images") - } - - images := *resp.JSON200 - - for i := range images { - if images[i].Metadata.Id == imageID { - return &images[i], nil - } - } - - return nil, errors.OAuth2ServerError("failed to lookup image id") -} - // convertMachine converts from a custom resource into the API definition. -func (g *generator) convertMachine(ctx context.Context, in *unikornv1.ComputeWorkloadPoolSpec, cluster *unikornv1.ComputeCluster) (*openapi.MachinePool, error) { - image, err := g.getImage(ctx, cluster.Spec.RegionID, *in.ImageID) - if err != nil { - return nil, err - } - - machine := &openapi.MachinePool{ +func (g *generator) convertMachine(in *unikornv1.ComputeWorkloadPoolSpec) *openapi.MachinePool { + return &openapi.MachinePool{ Replicas: *in.Replicas, FlavorId: *in.FlavorID, PublicIPAllocation: convertPublicIPAllocation(in.PublicIPAllocation), Firewall: convertFirewallRules(in.Firewall), - Image: openapi.ImageSelector{ - Distro: image.Spec.Os.Distro, - Variant: image.Spec.Os.Variant, - Version: ptr.To(image.Spec.Os.Version), - }, - UserData: convertUserData(in.UserData), + Image: convertImage(in), + UserData: convertUserData(in.UserData), } +} - return machine, nil +// convertImage converts from a custom resource into the API definition. +func convertImage(in *unikornv1.ComputeWorkloadPoolSpec) openapi.ComputeImage { + if in.ImageSelector == nil { + return openapi.ComputeImage{ + Id: in.ImageID, + } + } + + return openapi.ComputeImage{ + Selector: &openapi.ImageSelector{ + Distro: regionapi.OsDistro(in.ImageSelector.Distro), + Variant: in.ImageSelector.Variant, + Version: in.ImageSelector.Version, + }, + } } // convertUserData converts from a custom resource into the API definition. @@ -197,34 +182,22 @@ func convertPublicIPAllocation(in *unikornv1.PublicIPAllocationSpec) *openapi.Pu } // convertWorkloadPool converts from a custom resource into the API definition. -func (g *generator) convertWorkloadPool(ctx context.Context, in *unikornv1.ComputeClusterWorkloadPoolsPoolSpec, cluster *unikornv1.ComputeCluster) (*openapi.ComputeClusterWorkloadPool, error) { - machine, err := g.convertMachine(ctx, &in.ComputeWorkloadPoolSpec, cluster) - if err != nil { - return nil, err - } - - workloadPool := &openapi.ComputeClusterWorkloadPool{ +func (g *generator) convertWorkloadPool(in *unikornv1.ComputeClusterWorkloadPoolsPoolSpec) *openapi.ComputeClusterWorkloadPool { + return &openapi.ComputeClusterWorkloadPool{ Name: in.Name, - Machine: *machine, + Machine: *g.convertMachine(&in.ComputeWorkloadPoolSpec), } - - return workloadPool, nil } // convertWorkloadPools converts from a custom resource into the API definition. -func (g *generator) convertWorkloadPools(ctx context.Context, in *unikornv1.ComputeCluster) ([]openapi.ComputeClusterWorkloadPool, error) { +func (g *generator) convertWorkloadPools(in *unikornv1.ComputeCluster) []openapi.ComputeClusterWorkloadPool { workloadPools := make([]openapi.ComputeClusterWorkloadPool, len(in.Spec.WorkloadPools.Pools)) for i := range in.Spec.WorkloadPools.Pools { - pool, err := g.convertWorkloadPool(ctx, &in.Spec.WorkloadPools.Pools[i], in) - if err != nil { - return nil, err - } - - workloadPools[i] = *pool + workloadPools[i] = *g.convertWorkloadPool(&in.Spec.WorkloadPools.Pools[i]) } - return workloadPools, nil + return workloadPools } func convertCondition(in unikornv1core.ConditionReason) coreapi.ResourceProvisioningStatus { @@ -304,44 +277,34 @@ func convertClusterStatus(in *unikornv1.ComputeClusterStatus) *openapi.ComputeCl } // convert converts from a custom resource into the API definition. -func (g *generator) convert(ctx context.Context, in *unikornv1.ComputeCluster) (*openapi.ComputeClusterRead, error) { +func (g *generator) convert(in *unikornv1.ComputeCluster) *openapi.ComputeClusterRead { provisioningStatus := coreapi.ResourceProvisioningStatusUnknown if condition, err := in.StatusConditionRead(unikornv1core.ConditionAvailable); err == nil { provisioningStatus = conversion.ConvertStatusCondition(condition) } - pools, err := g.convertWorkloadPools(ctx, in) - if err != nil { - return nil, err - } - out := &openapi.ComputeClusterRead{ Metadata: conversion.ProjectScopedResourceReadMetadata(in, in.Spec.Tags, provisioningStatus), Spec: openapi.ComputeClusterSpec{ RegionId: in.Spec.RegionID, - WorkloadPools: pools, + WorkloadPools: g.convertWorkloadPools(in), }, Status: convertClusterStatus(&in.Status), } - return out, nil + return out } // uconvertList converts from a custom resource list into the API definition. -func (g *generator) convertList(ctx context.Context, in *unikornv1.ComputeClusterList) (openapi.ComputeClusters, error) { +func (g *generator) convertList(in *unikornv1.ComputeClusterList) openapi.ComputeClusters { out := make(openapi.ComputeClusters, len(in.Items)) for i := range in.Items { - cluster, err := g.convert(ctx, &in.Items[i]) - if err != nil { - return nil, err - } - - out[i] = *cluster + out[i] = *g.convert(&in.Items[i]) } - return out, nil + return out } // chooseImages returns an image for the requested machine and flavor. @@ -359,30 +322,7 @@ func (g *generator) chooseImage(ctx context.Context, request *openapi.ComputeClu // TODO: is the image compatible with the flavor virtualization type??? images = slices.DeleteFunc(images, func(image regionapi.Image) bool { - // Is it the right distro? - if image.Spec.Os.Distro != m.Image.Distro { - return true - } - - // Is it the right variant? - if m.Image.Variant != nil { - if image.Spec.Os.Variant == nil { - return true - } - - if *m.Image.Variant != *image.Spec.Os.Variant { - return true - } - } - - // Is it the right version? - if m.Image.Version != nil { - if *m.Image.Version != image.Spec.Os.Version { - return true - } - } - - return false + return g.filterImage(image, m) }) if len(images) == 0 { @@ -393,6 +333,38 @@ func (g *generator) chooseImage(ctx context.Context, request *openapi.ComputeClu return &images[0], nil } +func (g *generator) filterImage(image regionapi.Image, m *openapi.MachinePool) bool { + // If the image ID is set, we use it to get the image. + if m.Image.Id != nil { + return *m.Image.Id != image.Metadata.Id + } + + // Is it the right distro? + if image.Spec.Os.Distro != m.Image.Selector.Distro { + return true + } + + // Is it the right variant? + if m.Image.Selector.Variant != nil { + if image.Spec.Os.Variant == nil { + return true + } + + if *m.Image.Selector.Variant != *image.Spec.Os.Variant { + return true + } + } + + // Is it the right version? + if m.Image.Selector.Version != nil { + if *m.Image.Selector.Version != image.Spec.Os.Version { + return true + } + } + + return false +} + // generateNetwork generates the network part of a cluster. func (g *generator) generateNetwork() *unikornv1core.NetworkGeneric { // Grab some defaults (as these are in the right format already) @@ -453,6 +425,7 @@ func (g *generator) generateWorkloadPools(ctx context.Context, request *openapi. PublicIPAllocation: g.generatePublicIPAllocation(pool), Firewall: firewall, UserData: g.generateUserData(pool.Machine.UserData), + ImageSelector: g.generateImageSelector(pool.Machine.Image), }, } @@ -462,6 +435,19 @@ func (g *generator) generateWorkloadPools(ctx context.Context, request *openapi. return workloadPools, nil } +// generateImageSelector generates the image selector part of a workload pool. +func (g *generator) generateImageSelector(in openapi.ComputeImage) *unikornv1.ComputeWorkloadPoolImageSelector { + if in.Id != nil { + return nil + } + + return &unikornv1.ComputeWorkloadPoolImageSelector{ + Distro: unikornv1.OsDistro(in.Selector.Distro), + Variant: in.Selector.Variant, + Version: in.Selector.Version, + } +} + // generatePublicIPAllocation generates the public IP allocation part of a workload pool. func (g *generator) generatePublicIPAllocation(request *openapi.ComputeClusterWorkloadPool) *unikornv1.PublicIPAllocationSpec { if request.Machine.PublicIPAllocation == nil {