From 6e94d1a84a9583f5909aa0e168b4cfbe8b98ccb6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julian=20T=C3=B6lle?= Date: Thu, 23 Mar 2023 15:45:52 +0100 Subject: [PATCH] feat: select correct image by ARCH --- .../cloudprovider/hetzner/hetzner_manager.go | 29 +--------- .../hetzner/hetzner_node_group.go | 54 ++++++++++++++++++- 2 files changed, 54 insertions(+), 29 deletions(-) diff --git a/cluster-autoscaler/cloudprovider/hetzner/hetzner_manager.go b/cluster-autoscaler/cloudprovider/hetzner/hetzner_manager.go index 7c7e479257..7c2f45b3d4 100644 --- a/cluster-autoscaler/cloudprovider/hetzner/hetzner_manager.go +++ b/cluster-autoscaler/cloudprovider/hetzner/hetzner_manager.go @@ -46,7 +46,7 @@ type hetznerManager struct { nodeGroups map[string]*hetznerNodeGroup apiCallContext context.Context cloudInit string - image *hcloud.Image + image string sshKey *hcloud.SSHKey network *hcloud.Network firewall *hcloud.Firewall @@ -103,31 +103,6 @@ func newManager() (*hetznerManager, error) { } } - // Search for an image ID corresponding to the supplied HCLOUD_IMAGE env - // variable. This value can either be an image ID itself (an int), a name - // (e.g. "ubuntu-20.04"), or a label selector associated with an image - // snapshot. In the latter case it will use the most recent snapshot. - image, _, err := client.Image.Get(ctx, imageName) - if err != nil { - return nil, fmt.Errorf("unable to find image %s: %v", imageName, err) - } - if image == nil { - images, err := client.Image.AllWithOpts(ctx, hcloud.ImageListOpts{ - Type: []hcloud.ImageType{hcloud.ImageTypeSnapshot}, - Status: []hcloud.ImageStatus{hcloud.ImageStatusAvailable}, - Sort: []string{"created:desc"}, - ListOpts: hcloud.ListOpts{ - LabelSelector: imageName, - }, - }) - - if err != nil || len(images) == 0 { - return nil, fmt.Errorf("unable to find image %s: %v", imageName, err) - } - - image = images[0] - } - var sshKey *hcloud.SSHKey sshKeyName := os.Getenv("HCLOUD_SSH_KEY") if sshKeyName != "" { @@ -166,7 +141,7 @@ func newManager() (*hetznerManager, error) { client: client, nodeGroups: make(map[string]*hetznerNodeGroup), cloudInit: string(cloudInit), - image: image, + image: imageName, sshKey: sshKey, network: network, firewall: firewall, diff --git a/cluster-autoscaler/cloudprovider/hetzner/hetzner_node_group.go b/cluster-autoscaler/cloudprovider/hetzner/hetzner_node_group.go index bde051c959..05cf317ac0 100644 --- a/cluster-autoscaler/cloudprovider/hetzner/hetzner_node_group.go +++ b/cluster-autoscaler/cloudprovider/hetzner/hetzner_node_group.go @@ -17,8 +17,10 @@ limitations under the License. package hetzner import ( + "context" "fmt" "math/rand" + "strings" "sync" "time" @@ -380,13 +382,23 @@ func instanceTypeArch(manager *hetznerManager, instanceType string) (string, err } func createServer(n *hetznerNodeGroup) error { + serverType, err := n.manager.cachedServerType.getServerType(n.instanceType) + if err != nil { + return err + } + + image, err := findImage(n, serverType) + if err != nil { + return err + } + StartAfterCreate := true opts := hcloud.ServerCreateOpts{ Name: newNodeName(n), UserData: n.manager.cloudInit, Location: &hcloud.Location{Name: n.region}, - ServerType: &hcloud.ServerType{Name: n.instanceType}, - Image: n.manager.image, + ServerType: serverType, + Image: image, StartAfterCreate: &StartAfterCreate, Labels: map[string]string{ nodeGroupLabel: n.id, @@ -423,6 +435,44 @@ func createServer(n *hetznerNodeGroup) error { return nil } +// findImage searches for an image ID corresponding to the supplied +// HCLOUD_IMAGE env variable. This value can either be an image ID itself (an +// int), a name (e.g. "ubuntu-20.04"), or a label selector associated with an +// image snapshot. In the latter case it will use the most recent snapshot. +// It also verifies that the returned image has a compatible architecture with +// server. +func findImage(n *hetznerNodeGroup, serverType *hcloud.ServerType) (*hcloud.Image, error) { + // Select correct image based on server type architecture + image, _, err := n.manager.client.Image.GetForArchitecture(context.TODO(), n.manager.image, serverType.Architecture) + if err != nil { + // Keep looking for label if image was not found by id or name + if !strings.HasPrefix(err.Error(), "image not found") { + return nil, err + } + } + + if image != nil { + return image, nil + } + + // Look for snapshot with label + images, err := n.manager.client.Image.AllWithOpts(context.TODO(), hcloud.ImageListOpts{ + Type: []hcloud.ImageType{hcloud.ImageTypeSnapshot}, + Status: []hcloud.ImageStatus{hcloud.ImageStatusAvailable}, + Sort: []string{"created:desc"}, + Architecture: []hcloud.Architecture{serverType.Architecture}, + ListOpts: hcloud.ListOpts{ + LabelSelector: n.manager.image, + }, + }) + + if err != nil || len(images) == 0 { + return nil, fmt.Errorf("unable to find image %s with architecture %s: %v", n.manager.image, serverType.Architecture, err) + } + + return images[0], nil +} + func waitForServerAction(m *hetznerManager, serverName string, action *hcloud.Action) error { // The implementation of the Hetzner Cloud action client's WatchProgress // method may be a little puzzling. The following comment thus explains how