Skip to content

Commit

Permalink
azure: check image availability in region for spot calculation
Browse files Browse the repository at this point in the history
this adds a check to the spot calculation to only consider regions
that supports the requested community gallary image
  • Loading branch information
anjannath committed Jan 20, 2025
1 parent b62c1ff commit 214a335
Show file tree
Hide file tree
Showing 6 changed files with 99 additions and 24 deletions.
6 changes: 3 additions & 3 deletions pkg/provider/azure/action/aks/aks.go
Original file line number Diff line number Diff line change
Expand Up @@ -209,9 +209,9 @@ func (r *AKSRequest) valuesCheckingSpot() (*string, *float64, error) {
if r.Spot {
bsc, err :=
spotAzure.GetBestSpotChoice(spotAzure.BestSpotChoiceRequest{
VMTypes: []string{r.VMSize},
OSType: "linux",
EvictioRateTolerance: r.SpotTolerance,
VMTypes: []string{r.VMSize},
OSType: "linux",
EvictionRateTolerance: r.SpotTolerance,
})
logging.Debugf("Best spot price option found: %v", bsc)
if err != nil {
Expand Down
11 changes: 8 additions & 3 deletions pkg/provider/azure/action/linux/linux.go
Original file line number Diff line number Diff line change
Expand Up @@ -175,11 +175,16 @@ func (r *LinuxRequest) deployer(ctx *pulumi.Context) error {

func (r *LinuxRequest) valuesCheckingSpot() (*string, string, *float64, error) {
if r.Spot {
ir, err := data.GetImageRef(r.OSType, r.Arch, r.Version)
if err != nil {
return nil, "", nil, err
}
bsc, err :=
spotAzure.GetBestSpotChoice(spotAzure.BestSpotChoiceRequest{
VMTypes: util.If(len(r.VMSizes) > 0, r.VMSizes, []string{defaultVMSize}),
OSType: "linux",
EvictioRateTolerance: r.SpotTolerance,
VMTypes: util.If(len(r.VMSizes) > 0, r.VMSizes, []string{defaultVMSize}),
OSType: "linux",
EvictionRateTolerance: r.SpotTolerance,
ImageRef: *ir,
})
logging.Debugf("Best spot price option found: %v", bsc)
if err != nil {
Expand Down
6 changes: 3 additions & 3 deletions pkg/provider/azure/action/windows/windows.go
Original file line number Diff line number Diff line change
Expand Up @@ -172,9 +172,9 @@ func (r *WindowsRequest) valuesCheckingSpot() (*string, string, *float64, error)
if r.Spot {
bsc, err :=
spotAzure.GetBestSpotChoice(spotAzure.BestSpotChoiceRequest{
VMTypes: util.If(len(r.VMSizes) > 0, r.VMSizes, []string{defaultVMSize}),
OSType: "windows",
EvictioRateTolerance: r.SpotTolerance,
VMTypes: util.If(len(r.VMSizes) > 0, r.VMSizes, []string{defaultVMSize}),
OSType: "windows",
EvictionRateTolerance: r.SpotTolerance,
})
logging.Debugf("Best spot price option found: %v", bsc)
if err != nil {
Expand Down
59 changes: 59 additions & 0 deletions pkg/provider/azure/data/images.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
package data

import (
"context"
"fmt"
"os"
"strings"

"github.com/Azure/azure-sdk-for-go/sdk/azidentity"
"github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/compute/armcompute/v6"
maptAzIdentity "github.com/redhat-developer/mapt/pkg/provider/azure/module/identity"
"github.com/redhat-developer/mapt/pkg/util/logging"
)

type ImageRequest struct {
Region string
ImageReference
}

func GetImage(req ImageRequest) (*armcompute.CommunityGalleryImagesClientGetResponse, error) {
maptAzIdentity.SetAZIdentityEnvs()

cred, err := azidentity.NewDefaultAzureCredential(nil)
if err != nil {
return nil, err
}
ctx := context.Background()
subscriptionId := os.Getenv("AZURE_SUBSCRIPTION_ID")

clientFactory, err := armcompute.NewClientFactory(subscriptionId, cred, nil)
if err != nil {
return nil, err
}
// for community gallary images
if len(req.ID) > 0 {
// extract gallary ID and image name from ID url which looks like:
// /CommunityGalleries/Fedora-5e266ba4-2250-406d-adad-5d73860d958f/Images/Fedora-Cloud-41-x64
parts := strings.Split(req.ID, "/")
if len(parts) != 4 {
return nil, fmt.Errorf("invalida community gallary image ID: %s", req.ID)
}
res, err := clientFactory.NewCommunityGalleryImagesClient().Get(ctx, req.Region, parts[1], parts[3], nil)
if err != nil {
return nil, err
}
return &res, nil
}
// for azure offered VM images: https://learn.microsoft.com/en-us/rest/api/compute/virtual-machine-images/get
// there's a different API to check but currently we only check availability of community images
return nil, nil
}

func IsImageOffered(req ImageRequest) bool {
if _, err := GetImage(req); err != nil {
logging.Debugf("error while checking if image available at location: %v", err)
return false
}
return true
}
35 changes: 23 additions & 12 deletions pkg/spot/azure/spot.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import (
"github.com/Azure/azure-sdk-for-go/sdk/azcore/to"
"github.com/Azure/azure-sdk-for-go/sdk/azidentity"
"github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/resourcegraph/armresourcegraph"
"github.com/redhat-developer/mapt/pkg/provider/azure/data"
maptAzIdentity "github.com/redhat-developer/mapt/pkg/provider/azure/module/identity"
"github.com/redhat-developer/mapt/pkg/util"
"github.com/redhat-developer/mapt/pkg/util/logging"
Expand Down Expand Up @@ -42,9 +43,10 @@ const (
type EvictionRate int

type BestSpotChoiceRequest struct {
VMTypes []string
OSType string
EvictioRateTolerance EvictionRate
VMTypes []string
OSType string
EvictionRateTolerance EvictionRate
ImageRef data.ImageReference
}

type BestSpotChoiceResponse struct {
Expand Down Expand Up @@ -103,7 +105,7 @@ func GetBestSpotChoice(r BestSpotChoiceRequest) (*BestSpotChoiceResponse, error)
return nil, fmt.Errorf("error eviction rates are returning empty")
}
// Compare prices and evictions
return getBestSpotChoice(phr, evrr, Lowest, r.EvictioRateTolerance)
return getBestSpotChoice(phr, evrr, Lowest, r.EvictionRateTolerance, r.ImageRef.ID)
}

func getGraphClient() (*armresourcegraph.Client, error) {
Expand Down Expand Up @@ -204,7 +206,7 @@ func getEvictionRateInfoByVMTypes(ctx context.Context, client *armresourcegraph.
return results, nil
}

func getBestSpotChoice(s []priceHistory, e []evictionRate, currentERT EvictionRate, maxERT EvictionRate) (*BestSpotChoiceResponse, error) {
func getBestSpotChoice(s []priceHistory, e []evictionRate, currentERT EvictionRate, maxERT EvictionRate, imageID string) (*BestSpotChoiceResponse, error) {
var evm map[string]string = make(map[string]string)
for _, ev := range e {
evm[fmt.Sprintf("%s%s", ev.Location, ev.VMType)] = ev.EvictionRate
Expand All @@ -216,12 +218,21 @@ func getBestSpotChoice(s []priceHistory, e []evictionRate, currentERT EvictionRa
// and pick one randomly to improve distribution of instances
// across locations
if ok && er == getEvictionRateValue(currentERT) {
spotChoices = append(spotChoices,
&BestSpotChoiceResponse{
VMType: sv.VMType,
Location: sv.Location,
Price: sv.Price,
})
ir := data.ImageRequest{
Region: sv.Location,
ImageReference: data.ImageReference{
ID: imageID,
},
}
if data.IsImageOffered(ir) {
spotChoices = append(spotChoices,
&BestSpotChoiceResponse{
VMType: sv.VMType,
Location: sv.Location,
Price: sv.Price,
})

}
}
}
if len(spotChoices) > 0 {
Expand All @@ -237,7 +248,7 @@ func getBestSpotChoice(s []priceHistory, e []evictionRate, currentERT EvictionRa
if !ok {
return nil, fmt.Errorf("could not find any spot")
}
return getBestSpotChoice(s, e, *higherERT, maxERT)
return getBestSpotChoice(s, e, *higherERT, maxERT, imageID)
}

// Get previous higher evicition rate for a giving eviction rate
Expand Down
6 changes: 3 additions & 3 deletions pkg/spot/spot.go
Original file line number Diff line number Diff line change
Expand Up @@ -126,9 +126,9 @@ func (sr *SpotRequest) GetAzureLowestPrice() (SpotPrice, error) {
return SpotPrice{}, nil
}
spr := azure.BestSpotChoiceRequest{
VMTypes: vms,
OSType: sr.getAzureOsType(),
EvictioRateTolerance: azure.EvictionRate(sr.EvictionRateTolerance),
VMTypes: vms,
OSType: sr.getAzureOsType(),
EvictionRateTolerance: azure.EvictionRate(sr.EvictionRateTolerance),
}

prices, err := azure.GetBestSpotChoice(spr)
Expand Down

0 comments on commit 214a335

Please sign in to comment.