diff --git a/README.md b/README.md index 2997db5444cad1243e6b12b83f2fdcd94702df23..fe44731ac3b7793cd5d4e594f7faa5f546f38262 100644 --- a/README.md +++ b/README.md @@ -77,10 +77,22 @@ $ docker-machine create \ some-machine ``` +### Using a snapshot + +Assuming your snapshot ID is `424242`: +```bash +$ docker-machine create \ + --driver hetzner \ + --hetzner-api-token=QJhoRT38JfAUO037PWJ5Zt9iAABIxdxdh4gPqNkUGKIrUMd6I3cPIsfKozI513sy \ + --hetzner-image-id=424242 \ + some-machine +``` + ## Options - `--hetzner-api-token`: **required**. Your project-specific access token for the Hetzner Cloud API. -- `--hetzner-image`: The name of the Hetzner Cloud image to use, see [Images API](https://docs.hetzner.cloud/#resources-images-get) for how to get a list (defaults to `ubuntu-16.04`). +- `--hetzner-image`: The name of the Hetzner Cloud image to use, see [Images API](https://docs.hetzner.cloud/#resources-images-get) for how to get a list (defaults to `ubuntu-16.04`). +- `--hetzner-image-id`: The id of the Hetzner cloud image (or snapshot) to use, see [Images API](https://docs.hetzner.cloud/#resources-images-get) for how to get a list (mutually excludes `--hetzner-image`). - `--hetzner-server-type`: The type of the Hetzner Cloud server, see [Server Types API](https://docs.hetzner.cloud/#resources-server-types-get) for how to get a list (defaults to `cx11`). - `--hetzner-server-location`: The location to create the server in, see [Locations API](https://docs.hetzner.cloud/#resources-locations-get) for how to get a list. **NOTICE: Beware that Hetzner does not reject invalid location names at the time of writing this; instead, a seemingly random location is chosen. Double check both the option value's @@ -110,7 +122,8 @@ was used during creation. | CLI option | Environment variable | Default | | ----------------------------------- | --------------------------------- | -------------------------- | | **`--hetzner-api-token`** | `HETZNER_API_TOKEN` | - | -| `--hetzner-image ` | `HETZNER_IMAGE_IMAGE` | `ubuntu-16.04` | +| `--hetzner-image` | `HETZNER_IMAGE_IMAGE` | `ubuntu-16.04` | +| `--hetzner-image-id` | `HETZNER_IMAGE_IMAGE_ID` | - | | `--hetzner-server-type` | `HETZNER_TYPE` | `cx11` | | `--hetzner-server-location` | `HETZNER_LOCATION` | - *(let Hetzner choose)* | | `--hetzner-existing-key-path` | `HETZNER_EXISTING_KEY_PATH` | - *(generate new keypair)* | diff --git a/driver.go b/driver.go index 5f09b68cada5e1c6a71f3e2753ff0e029e03d7bc..939fe04b096733680a6b65714b98b4d2c9fa2691 100644 --- a/driver.go +++ b/driver.go @@ -24,6 +24,7 @@ type Driver struct { AccessToken string Image string + ImageID int cachedImage *hcloud.Image Type string cachedType *hcloud.ServerType @@ -45,6 +46,7 @@ const ( flagAPIToken = "hetzner-api-token" flagImage = "hetzner-image" + flagImageID = "hetzner-image-id" flagType = "hetzner-server-type" flagLocation = "hetzner-server-location" flagExKeyID = "hetzner-existing-key-id" @@ -83,6 +85,11 @@ func (d *Driver) GetCreateFlags() []mcnflag.Flag { Usage: "Image to use for server creation", Value: defaultImage, }, + mcnflag.IntFlag{ + EnvVar: "HETZNER_IMAGE_ID", + Name: flagImageID, + Usage: "Image to use for server creation", + }, mcnflag.StringFlag{ EnvVar: "HETZNER_TYPE", Name: flagType, @@ -119,6 +126,7 @@ func (d *Driver) GetCreateFlags() []mcnflag.Flag { func (d *Driver) SetConfigFromFlags(opts drivers.DriverOptions) error { d.AccessToken = opts.String(flagAPIToken) d.Image = opts.String(flagImage) + d.ImageID = opts.Int(flagImageID) d.Location = opts.String(flagLocation) d.Type = opts.String(flagType) d.KeyID = opts.Int(flagExKeyID) @@ -131,6 +139,11 @@ func (d *Driver) SetConfigFromFlags(opts drivers.DriverOptions) error { if d.AccessToken == "" { return errors.Errorf("hetzner requires --%v to be set", flagAPIToken) } + + if d.ImageID != 0 && d.Image != defaultImage { + return errors.Errorf("--%v and --%v are mutually exclusive", flagImage, flagImageID) + } + return nil } @@ -466,10 +479,21 @@ func (d *Driver) getImage() (*hcloud.Image, error) { return d.cachedImage, nil } - image, _, err := d.getClient().Image.GetByName(context.Background(), d.Image) - if err != nil { - return image, errors.Wrap(err, "could not get image by name") + var image *hcloud.Image + var err error + + if d.ImageID != 0 { + image, _, err = d.getClient().Image.GetByID(context.Background(), d.ImageID) + if err != nil { + return image, errors.Wrap(err, fmt.Sprintf("could not get image by id %v", d.ImageID)) + } + } else { + image, _, err = d.getClient().Image.GetByName(context.Background(), d.Image) + if err != nil { + return image, errors.Wrap(err, fmt.Sprintf("could not get image by name %v", d.Image)) + } } + d.cachedImage = image return image, nil }