Skip to content

Commit d8b370d

Browse files
WIP: Lookup available zones for GCP
1 parent 3556573 commit d8b370d

2 files changed

Lines changed: 74 additions & 2 deletions

File tree

mantle/cmd/kola/options.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -121,7 +121,7 @@ func init() {
121121
// gcp-specific options
122122
sv(&kola.GCPOptions.Image, "gcp-image", "", "GCP image, full api endpoints names are accepted if resource is in a different project")
123123
sv(&kola.GCPOptions.Project, "gcp-project", "fedora-coreos-devel", "GCP project name")
124-
sv(&kola.GCPOptions.Zone, "gcp-zone", "us-central1-a", "GCP zone name")
124+
sv(&kola.GCPOptions.Zone, "gcp-zone", "us-central1-a", "Preferred GCP zone name, if the resources to this zone are depleated, we will fallback to another zone in the same region")
125125
sv(&kola.GCPOptions.MachineType, "gcp-machinetype", "", "GCP machine type")
126126
sv(&kola.GCPOptions.DiskType, "gcp-disktype", "", "GCP disk type (default pd-ssd)")
127127
sv(&kola.GCPOptions.Network, "gcp-network", "default", "GCP network")

mantle/platform/api/gcloud/api.go

Lines changed: 73 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,10 +17,13 @@ package gcloud
1717

1818
import (
1919
"context"
20-
"google.golang.org/api/option"
20+
"fmt"
2121
"net/http"
22+
"regexp"
2223
"time"
2324

25+
"google.golang.org/api/option"
26+
2427
"github.com/coreos/pkg/capnslog"
2528
"google.golang.org/api/compute/v1"
2629

@@ -50,6 +53,68 @@ type API struct {
5053
client *http.Client
5154
compute *compute.Service
5255
options *Options
56+
zones []string
57+
}
58+
59+
// This regex shuld match all standard (non-AI) zones
60+
// See: https://docs.cloud.google.com/compute/docs/regions-zones
61+
var standardZoneRegexp = regexp.MustCompile(`^([a-z]+-[a-z]+\d+)-[a-z]$`)
62+
63+
// zones are in the form "us-central1-a" and the region would be "us-central1"
64+
// See: https://docs.cloud.google.com/compute/docs/regions-zones
65+
func extractRegionFromZone(zone string) (string, error) {
66+
matches := standardZoneRegexp.FindStringSubmatch(zone)
67+
if matches == nil {
68+
return "", fmt.Errorf("zone %q does not match expected format {region}-{letter}", zone)
69+
}
70+
return matches[1], nil
71+
}
72+
73+
func getAvailableZones(computeService *compute.Service, opts *Options) ([]string, error) {
74+
if opts.MachineType == "" {
75+
return []string{opts.Zone}, nil
76+
}
77+
78+
list, err := computeService.MachineTypes.AggregatedList(opts.Project).
79+
Filter("name=" + opts.MachineType).Do()
80+
81+
if err != nil {
82+
return nil, err
83+
}
84+
85+
targetRegion, err := extractRegionFromZone(opts.Zone)
86+
if err != nil {
87+
return nil, fmt.Errorf("could not extract region from zone %q: %w", opts.Zone, err)
88+
}
89+
90+
zones := []string{}
91+
for _, scopedList := range list.Items {
92+
// There should be either 1 or 0 MachineTypes
93+
// 0 if this zone does not have the required machine type
94+
// 1 if this zone does have the required machine type
95+
if len(scopedList.MachineTypes) == 0 {
96+
continue
97+
}
98+
if len(scopedList.MachineTypes) > 1 {
99+
plog.Warningf("Unexpected: got %d machine types for filter name=%s", len(scopedList.MachineTypes), opts.MachineType)
100+
continue
101+
}
102+
zone := scopedList.MachineTypes[0].Zone
103+
if region, err := extractRegionFromZone(zone); err == nil && region == targetRegion {
104+
// If the preferred zone can be used, it should be the first zone that we use,
105+
// so we will make add it to the start of the list, rather than the end.
106+
if zone == opts.Zone {
107+
zones = append([]string{zone}, zones...)
108+
} else {
109+
zones = append(zones, zone)
110+
}
111+
}
112+
}
113+
114+
if len(zones) == 0 {
115+
return zones, fmt.Errorf("no zones in region %s for machine type %s were found", targetRegion, opts.MachineType)
116+
}
117+
return zones, nil
53118
}
54119

55120
func New(opts *Options) (*API, error) {
@@ -83,6 +148,12 @@ func New(opts *Options) (*API, error) {
83148
return nil, err
84149
}
85150

151+
zones, err := getAvailableZones(computeService, opts)
152+
if err != nil {
153+
plog.Warningf("Failed to discover available zones: %v. Falling back to preferred zone (%s) only.", err, opts.Zone)
154+
zones = []string{opts.Zone}
155+
}
156+
86157
if opts.ServiceAcct == "" {
87158
proj, err := computeService.Projects.Get(opts.Project).Do()
88159
if err != nil {
@@ -95,6 +166,7 @@ func New(opts *Options) (*API, error) {
95166
client: client,
96167
compute: computeService,
97168
options: opts,
169+
zones: zones,
98170
}
99171

100172
return api, nil

0 commit comments

Comments
 (0)