Skip to content
Commits on Source (2)
......@@ -91,14 +91,14 @@ $ docker-machine create \
## Options
- `--hetzner-api-token`: **required**. Your project-specific access token for the Hetzner Cloud API.
- `--hetzner-image`: The name (or ID) of the Hetzner Cloud image to use, see [Images API](https://docs.hetzner.cloud/#resources-images-get) for how to get a list (currently defaults to `ubuntu-20.04`). *Explicitly specifying an image is **strongly** recommended and will be **required from v6 onwards***.
- `--hetzner-image`: The name (or ID) of the Hetzner Cloud image to use, see [Images API](https://docs.hetzner.cloud/#images-get-all-images) for how to get a list (currently defaults to `ubuntu-20.04`). *Explicitly specifying an image is **strongly** recommended and will be **required from v6 onwards***.
- `--hetzner-image-arch`: The architecture to use during image lookup, inferred from the server type if not explicitly given.
- `--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.
- `--hetzner-image-id`: The id of the Hetzner cloud image (or snapshot) to use, see [Images API](https://docs.hetzner.cloud/#images-get-all-images) for how to get a list (mutually excludes `--hetzner-image`).
- `--hetzner-server-type`: The type of the Hetzner Cloud server, see [Server Types API](hhttps://docs.hetzner.cloud/#server-types-get-all-server-types) 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/#locations-get-all-locations) for how to get a list.
- `--hetzner-existing-key-path`: Use an existing (local) SSH key instead of generating a new keypair. If a remote key with a matching fingerprint exists, it will be used as if specified using `--hetzner-existing-key-id`, rather than uploading a new key.
- `--hetzner-existing-key-id`: **requires `--hetzner-existing-key-path`**. Use an existing (remote) SSH key instead of uploading the imported key pair,
see [SSH Keys API](https://docs.hetzner.cloud/#resources-ssh-keys-get) for how to get a list
see [SSH Keys API](https://docs.hetzner.cloud/#ssh-keys-get-all-ssh-keys) for how to get a list
- `--hetzner-additional-key`: Upload an additional public key associated with the server, or associate an existing one with the same fingerprint. Can be specified multiple times.
- `--hetzner-user-data`: Cloud-init based data, passed inline as-is.
- `--hetzner-user-data-file`: Cloud-init based data, read from passed file.
......@@ -118,6 +118,11 @@ $ docker-machine create \
- `--hetzner-wait-on-polling`: Amount of seconds to wait between requests when waiting for some state to change. (Default: 1 second)
- `--hetzner-wait-for-running-timeout`: Max amount of seconds to wait until a machine is running. (Default: 0/no timeout)
Please beware, that for options referring to entities by name, such as server locations and types, the names used by the API may differ from the ones
shown in the server creation UI. If server creation fails due to a failure to resolve such issues, try another variant of the name (e.g. lowercase,
kebab-case). As of writing, server types use lowercase (i.e. `cx21` instead of `CX21`) and locations use a three-letter abbreviation suffixed by 1
(i.e. `fsn1` instead of `Falkenstein`).
#### Image selection
When `--hetzner-image-id` is passed, it will be used for lookup by ID as-is. No additional validation is performed, and it is mutually exclusive with
......
......@@ -42,7 +42,7 @@ func (d *Driver) destroyServer() error {
return nil
}
srv, err := d.getServerHandle()
srv, err := d.getServerHandleNullable()
if err != nil {
return errors.Wrap(err, "could not get server handle")
}
......
......@@ -55,8 +55,8 @@ type Driver struct {
AdditionalKeyIDs []int
cachedAdditionalKeys []*hcloud.SSHKey
WaitOnError int
WaitOnPolling int
WaitOnError int
WaitOnPolling int
WaitForRunningTimeout int
// internal housekeeping
......@@ -99,12 +99,12 @@ const (
defaultSSHPort = 22
defaultSSHUser = "root"
flagWaitOnError = "hetzner-wait-on-error"
defaultWaitOnError = 0
flagWaitOnPolling = "hetzner-wait-on-polling"
defaultWaitOnPolling = 1
flagWaitForRunningTimeout = "hetzner-wait-for-running-timeout"
defaultWaitForRunningTimeout = 0
flagWaitOnError = "hetzner-wait-on-error"
defaultWaitOnError = 0
flagWaitOnPolling = "hetzner-wait-on-polling"
defaultWaitOnPolling = 1
flagWaitForRunningTimeout = "hetzner-wait-for-running-timeout"
defaultWaitForRunningTimeout = 0
legacyFlagUserDataFromFile = "hetzner-user-data-from-file"
legacyFlagDisablePublic4 = "hetzner-disable-public-4"
......@@ -608,9 +608,6 @@ func (d *Driver) Start() error {
if err != nil {
return errors.Wrap(err, "could not get server handle")
}
if srv == nil {
return errors.New("server not found")
}
act, _, err := d.getClient().Server.Poweron(context.Background(), srv)
if err != nil {
......@@ -628,9 +625,6 @@ func (d *Driver) Stop() error {
if err != nil {
return errors.Wrap(err, "could not get server handle")
}
if srv == nil {
return errors.New("server not found")
}
act, _, err := d.getClient().Server.Shutdown(context.Background(), srv)
if err != nil {
......@@ -648,9 +642,6 @@ func (d *Driver) Kill() error {
if err != nil {
return errors.Wrap(err, "could not get server handle")
}
if srv == nil {
return errors.New("server not found")
}
act, _, err := d.getClient().Server.Poweroff(context.Background(), srv)
if err != nil {
......
......@@ -21,7 +21,10 @@ func (d *Driver) getLocation() (*hcloud.Location, error) {
location, _, err := d.getClient().Location.GetByName(context.Background(), d.Location)
if err != nil {
return location, errors.Wrap(err, "could not get location by name")
return nil, errors.Wrap(err, "could not get location by name")
}
if location == nil {
return nil, fmt.Errorf("unknown location: %v", d.Location)
}
d.cachedLocation = location
return location, nil
......@@ -34,7 +37,10 @@ func (d *Driver) getType() (*hcloud.ServerType, error) {
stype, _, err := d.getClient().ServerType.GetByName(context.Background(), d.Type)
if err != nil {
return stype, errors.Wrap(err, "could not get type by name")
return nil, errors.Wrap(err, "could not get type by name")
}
if stype == nil {
return nil, fmt.Errorf("unknown server type: %v", d.Type)
}
d.cachedType = stype
return instrumented(stype), nil
......@@ -51,7 +57,10 @@ func (d *Driver) getImage() (*hcloud.Image, 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))
return nil, errors.Wrap(err, fmt.Sprintf("could not get image by id %v", d.ImageID))
}
if image == nil {
return nil, fmt.Errorf("image id not found: %v", d.ImageID)
}
} else {
arch, err := d.getImageArchitectureForLookup()
......@@ -61,7 +70,10 @@ func (d *Driver) getImage() (*hcloud.Image, error) {
image, _, err = d.getClient().Image.GetByNameAndArchitecture(context.Background(), d.Image, arch)
if err != nil {
return image, errors.Wrap(err, fmt.Sprintf("could not get image by name %v", d.Image))
return nil, errors.Wrap(err, fmt.Sprintf("could not get image by name %v", d.Image))
}
if image == nil {
return nil, fmt.Errorf("image not found: %v[%v]", d.Image, arch)
}
}
......@@ -87,12 +99,15 @@ func (d *Driver) getKey() (*hcloud.SSHKey, error) {
return d.cachedKey, nil
}
stype, _, err := d.getClient().SSHKey.GetByID(context.Background(), d.KeyID)
key, _, err := d.getClient().SSHKey.GetByID(context.Background(), d.KeyID)
if err != nil {
return stype, errors.Wrap(err, "could not get sshkey by ID")
return nil, errors.Wrap(err, "could not get sshkey by ID")
}
d.cachedKey = stype
return instrumented(stype), nil
if key == nil {
return nil, fmt.Errorf("key not found: %v", d.KeyID)
}
d.cachedKey = key
return instrumented(key), nil
}
func (d *Driver) getRemoteKeyWithSameFingerprint(publicKeyBytes []byte) (*hcloud.SSHKey, error) {
......@@ -107,10 +122,24 @@ func (d *Driver) getRemoteKeyWithSameFingerprint(publicKeyBytes []byte) (*hcloud
if err != nil {
return remoteKey, errors.Wrap(err, "could not get sshkey by fingerprint")
}
if remoteKey == nil {
return nil, fmt.Errorf("key not found by fingerprint: %v", fp)
}
return instrumented(remoteKey), nil
}
func (d *Driver) getServerHandle() (*hcloud.Server, error) {
srv, err := d.getServerHandleNullable()
if err != nil {
return nil, err
}
if srv == nil {
return nil, fmt.Errorf("server does not exist: %v", d.ServerID)
}
return srv, nil
}
func (d *Driver) getServerHandleNullable() (*hcloud.Server, error) {
if d.cachedServer != nil {
return d.cachedServer, nil
}
......@@ -134,6 +163,9 @@ func (d *Driver) waitForAction(a *hcloud.Action) error {
if err != nil {
return errors.Wrap(err, "could not get client by ID")
}
if act == nil {
return fmt.Errorf("action not found: %v", a.ID)
}
if act.Status == hcloud.ActionStatusSuccess {
log.Debugf(" -> finished %s[%d]", act.Command, act.ID)
......