diff --git a/cluster-autoscaler/cloudprovider/hetzner/README.md b/cluster-autoscaler/cloudprovider/hetzner/README.md index b3ba63c5ea8c..29af7e634c61 100644 --- a/cluster-autoscaler/cloudprovider/hetzner/README.md +++ b/cluster-autoscaler/cloudprovider/hetzner/README.md @@ -5,11 +5,17 @@ The cluster autoscaler for Hetzner Cloud scales worker nodes. # Configuration `HCLOUD_TOKEN` Required Hetzner Cloud token. + `HCLOUD_CLOUD_INIT` Base64 encoded Cloud Init yaml with commands to join the cluster, Sample [examples/cloud-init.txt for (Kubernetes 1.20.1)](examples/cloud-init.txt) -`HCLOUD_IMAGE` Defaults to `ubuntu-20.04`, @see https://docs.hetzner.cloud/#images + +`HCLOUD_IMAGE` Defaults to `ubuntu-20.04`, @see https://docs.hetzner.cloud/#images. You can also use an image ID here (e.g. `15512617`), or a label selector associated with a custom snapshot (e.g. `customized_ubuntu=true`). + `HCLOUD_NETWORK` Default empty , The name of the network that is used in the cluster , @see https://docs.hetzner.cloud/#networks + `HCLOUD_SSH_KEY` Default empty , This SSH Key will have access to the fresh created server, @see https://docs.hetzner.cloud/#ssh-keys + Node groups must be defined with the `--nodes=::::` flag. + Multiple flags will create multiple node pools. For example: ``` --nodes=1:10:CPX51:FSN1:pool1 diff --git a/cluster-autoscaler/cloudprovider/hetzner/hetzner_manager.go b/cluster-autoscaler/cloudprovider/hetzner/hetzner_manager.go index e76de37b6356..b4e8fc511394 100644 --- a/cluster-autoscaler/cloudprovider/hetzner/hetzner_manager.go +++ b/cluster-autoscaler/cloudprovider/hetzner/hetzner_manager.go @@ -39,7 +39,7 @@ type hetznerManager struct { nodeGroups map[string]*hetznerNodeGroup apiCallContext context.Context cloudInit string - image string + image *hcloud.Image sshKey *hcloud.SSHKey network *hcloud.Network } @@ -55,11 +55,6 @@ func newManager() (*hetznerManager, error) { return nil, errors.New("`HCLOUD_CLOUD_INIT` is not specified") } - image := os.Getenv("HCLOUD_IMAGE") - if image == "" { - image = "ubuntu-20.04" - } - client := hcloud.NewClient(hcloud.WithToken(token)) ctx := context.Background() cloudInit, err := base64.StdEncoding.DecodeString(cloudInitBase64) @@ -67,6 +62,38 @@ func newManager() (*hetznerManager, error) { return nil, fmt.Errorf("failed to parse cloud init error: %s", err) } + imageName := os.Getenv("HCLOUD_IMAGE") + if imageName == "" { + imageName = "ubuntu-20.04" + } + + // 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 || image == nil { + labelSelector := strings.Split(imageName, "=") + if len(labelSelector) != 2 { + return nil, fmt.Errorf("unable to find image %s: invalid label selector", imageName) + } + + 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: fmt.Sprintf("%s=%s", labelSelector[0], labelSelector[1]), + }, + }) + + if err != nil || len(images) == 0 { + return nil, fmt.Errorf("unable to find image %s: %v", imageName, err) + } + + image = images[0] + } + var network *hcloud.Network networkName := os.Getenv("HCLOUD_NETWORK") diff --git a/cluster-autoscaler/cloudprovider/hetzner/hetzner_node_group.go b/cluster-autoscaler/cloudprovider/hetzner/hetzner_node_group.go index c232554a1227..7d06be8c24e2 100644 --- a/cluster-autoscaler/cloudprovider/hetzner/hetzner_node_group.go +++ b/cluster-autoscaler/cloudprovider/hetzner/hetzner_node_group.go @@ -353,7 +353,7 @@ func createServer(n *hetznerNodeGroup) error { UserData: n.manager.cloudInit, Location: &hcloud.Location{Name: n.region}, ServerType: &hcloud.ServerType{Name: n.instanceType}, - Image: &hcloud.Image{Name: n.manager.image}, + Image: n.manager.image, StartAfterCreate: &StartAfterCreate, Labels: map[string]string{ nodeGroupLabel: n.id,