Skip to content
Commits on Source (1)
......@@ -15,8 +15,8 @@ You can find sources and pre-compiled binaries [here](https://github.com/JonasPr
```bash
# Download the binary (this example downloads the binary for linux amd64)
$ wget https://github.com/JonasProgrammer/docker-machine-driver-hetzner/releases/download/3.11.0/docker-machine-driver-hetzner_3.11.0_linux_amd64.tar.gz
$ tar -xvf docker-machine-driver-hetzner_3.11.0_linux_amd64.tar.gz
$ wget https://github.com/JonasProgrammer/docker-machine-driver-hetzner/releases/download/3.12.0/docker-machine-driver-hetzner_3.12.0_linux_amd64.tar.gz
$ tar -xvf docker-machine-driver-hetzner_3.12.0_linux_amd64.tar.gz
# Make it executable and copy the binary in a directory accessible with your $PATH
$ chmod +x docker-machine-driver-hetzner
......@@ -99,8 +99,9 @@ $ docker-machine create \
- `--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
- `--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 User data
- `--hetzner-user-data-from-file`: Use Cloud-init based User data as file, `--hetzner-user-data` as file name
- `--hetzner-user-data`: Cloud-init based data, passed inline as-is.
- `--hetzner-user-data-file`: Cloud-init based data, read from passed file.
- `--hetzner-user-data-from-file`: DEPRECATED, use `--hetzner-user-data-file`. Read `--hetzner-user-data` as file name and use contents as user-data.
- `--hetzner-volumes`: Volume IDs or names which should be attached to the server
- `--hetzner-networks`: Network IDs or names which should be attached to the server private network interface
- `--hetzner-use-private-network`: Use private network
......@@ -142,6 +143,7 @@ was used during creation.
| `--hetzner-existing-key-id` | `HETZNER_EXISTING_KEY_ID` | 0 *(upload new key)* |
| `--hetzner-additional-key` | `HETZNER_ADDITIONAL_KEYS` | |
| `--hetzner-user-data` | `HETZNER_USER_DATA` | |
| `--hetzner-user-data-file` | `HETZNER_USER_DATA_FILE` | |
| `--hetzner-networks` | `HETZNER_NETWORKS` | |
| `--hetzner-firewalls` | `HETZNER_FIREWALLS` | |
| `--hetzner-volumes` | `HETZNER_VOLUMES` | |
......@@ -229,3 +231,13 @@ $ export PATH="$PATH:$GOBIN"
# Make docker-machine output help including hetzner-specific options
$ docker-machine create --driver hetzner
```
## Upcoming breaking changes
### 4.0.0
* `--hetzner-user-data-from-file` will be fully deprecated and its flag description will only read 'DEPRECATED, legacy'; current fallback behaviour will be retained. `--hetzner-flag-user-data-file` should be used instead.
### 5.0.0
* `--hetzner-user-data-from-file` will be removed entirely, including its fallback behavior
......@@ -39,7 +39,7 @@ type Driver struct {
ServerID int
cachedServer *hcloud.Server
userData string
userDataFromFile bool
userDataFile string
Volumes []string
Networks []string
UsePrivateNetwork bool
......@@ -74,7 +74,7 @@ const (
flagExKeyID = "hetzner-existing-key-id"
flagExKeyPath = "hetzner-existing-key-path"
flagUserData = "hetzner-user-data"
flagUserDataFromFile = "hetzner-user-data-from-file"
flagUserDataFile = "hetzner-user-data-file"
flagVolumes = "hetzner-volumes"
flagNetworks = "hetzner-networks"
flagUsePrivateNetwork = "hetzner-use-private-network"
......@@ -104,6 +104,8 @@ const (
flagWaitOnError = "wait-on-error"
defaultWaitOnError = 0
legacyFlagUserDataFromFile = "hetzner-user-data-from-file"
)
// NewDriver initializes a new driver instance; see [drivers.Driver.NewDriver]
......@@ -167,13 +169,19 @@ func (d *Driver) GetCreateFlags() []mcnflag.Flag {
mcnflag.StringFlag{
EnvVar: "HETZNER_USER_DATA",
Name: flagUserData,
Usage: "Cloud-init based User data",
Usage: "Cloud-init based user data (inline).",
Value: "",
},
mcnflag.BoolFlag{
EnvVar: "HETZNER_USER_DATA_FROM_FILE",
Name: flagUserDataFromFile,
Usage: "Cloud-init based User data is file",
Name: legacyFlagUserDataFromFile,
Usage: "DEPRECATED, use --hetzner-user-data-file. Treat --hetzner-user-data argument as filename.",
},
mcnflag.StringFlag{
EnvVar: "HETZNER_USER_DATA_FILE",
Name: flagUserDataFile,
Usage: "Cloud-init based user data (read from file)",
Value: "",
},
mcnflag.StringSliceFlag{
EnvVar: "HETZNER_VOLUMES",
......@@ -290,8 +298,10 @@ func (d *Driver) setConfigFromFlagsImpl(opts drivers.DriverOptions) error {
d.KeyID = opts.Int(flagExKeyID)
d.IsExistingKey = d.KeyID != 0
d.originalKey = opts.String(flagExKeyPath)
d.userData = opts.String(flagUserData)
d.userDataFromFile = opts.Bool(flagUserDataFromFile)
err := d.setUserDataFlags(opts)
if err != nil {
return err
}
d.Volumes = opts.StringSlice(flagVolumes)
d.Networks = opts.StringSlice(flagNetworks)
disablePublic := opts.Bool(flagDisablePublic)
......@@ -316,7 +326,7 @@ func (d *Driver) setConfigFromFlagsImpl(opts drivers.DriverOptions) error {
d.placementGroup = autoSpreadPgName
}
err := d.setLabelsFromFlags(opts)
err = d.setLabelsFromFlags(opts)
if err != nil {
return err
}
......@@ -351,6 +361,30 @@ func (d *Driver) setConfigFromFlagsImpl(opts drivers.DriverOptions) error {
return nil
}
func (d *Driver) setUserDataFlags(opts drivers.DriverOptions) error {
userData := opts.String(flagUserData)
userDataFile := opts.String(flagUserDataFile)
if opts.Bool(legacyFlagUserDataFromFile) {
if userDataFile != "" {
return d.flagFailure("--%v and --%v are mutually exclusive", flagUserDataFile, legacyFlagUserDataFromFile)
}
log.Warnf("--%v is deprecated, pass '--%v \"%v\"'", legacyFlagUserDataFromFile, flagUserDataFile, userData)
d.userDataFile = userData
return nil
}
d.userData = userData
d.userDataFile = userDataFile
if d.userData != "" && d.userDataFile != "" {
return d.flagFailure("--%v and --%v are mutually exclusive", flagUserData, flagUserDataFile)
}
return nil
}
// GetSSHUsername retrieves the SSH username used to connect to the server during provisioning
func (d *Driver) GetSSHUsername() string {
return d.SSHUser
......@@ -601,13 +635,12 @@ func (d *Driver) makeCreateServerOptions() (*hcloud.ServerCreateOpts, error) {
}
func (d *Driver) getUserData() (string, error) {
userData := d.userData
if !d.userDataFromFile {
return userData, nil
file := d.userDataFile
if file == "" {
return d.userData, nil
}
readUserData, err := os.ReadFile(d.userData)
readUserData, err := os.ReadFile(file)
if err != nil {
return "", err
}
......
package main
import (
"github.com/docker/machine/commands/commandstest"
"github.com/docker/machine/libmachine/drivers"
"os"
"strings"
"testing"
)
var defaultFlags = map[string]interface{}{
flagAPIToken: "foo",
}
func makeFlags(args map[string]interface{}) drivers.DriverOptions {
combined := make(map[string]interface{}, len(defaultFlags)+len(args))
for k, v := range defaultFlags {
combined[k] = v
}
for k, v := range args {
combined[k] = v
}
return &commandstest.FakeFlagger{Data: combined}
}
func TestUserData(t *testing.T) {
const fileContents = "User data from file"
const inlineContents = "User data"
file := t.TempDir() + string(os.PathSeparator) + "userData"
err := os.WriteFile(file, []byte(fileContents), 0644)
if err != nil {
t.Fatal(err)
}
// mutual exclusion data <=> data file
d := NewDriver()
err = d.setConfigFromFlagsImpl(makeFlags(map[string]interface{}{
flagUserData: inlineContents,
flagUserDataFile: file,
}))
assertMutualExclusion(t, err, flagUserData, flagUserDataFile)
// mutual exclusion data file <=> legacy flag
d = NewDriver()
err = d.setConfigFromFlagsImpl(&commandstest.FakeFlagger{
Data: map[string]interface{}{
flagAPIToken: "foo",
legacyFlagUserDataFromFile: true,
flagUserDataFile: file,
},
})
assertMutualExclusion(t, err, legacyFlagUserDataFromFile, flagUserDataFile)
// inline user data
d = NewDriver()
err = d.setConfigFromFlagsImpl(makeFlags(map[string]interface{}{
flagAPIToken: "foo",
flagUserData: inlineContents,
}))
if err != nil {
t.Fatalf("unexpected error, %v", err)
}
data, err := d.getUserData()
if err != nil {
t.Fatalf("unexpected error, %v", err)
}
if data != inlineContents {
t.Error("content did not match (inline)")
}
// file user data
d = NewDriver()
err = d.setConfigFromFlagsImpl(makeFlags(map[string]interface{}{
flagAPIToken: "foo",
flagUserDataFile: file,
}))
if err != nil {
t.Fatalf("unexpected error, %v", err)
}
data, err = d.getUserData()
if err != nil {
t.Fatalf("unexpected error, %v", err)
}
if data != fileContents {
t.Error("content did not match (file)")
}
// legacy file user data
d = NewDriver()
err = d.setConfigFromFlagsImpl(makeFlags(map[string]interface{}{
flagAPIToken: "foo",
flagUserData: file,
legacyFlagUserDataFromFile: true,
}))
if err != nil {
t.Fatalf("unexpected error, %v", err)
}
data, err = d.getUserData()
if err != nil {
t.Fatalf("unexpected error, %v", err)
}
if data != fileContents {
t.Error("content did not match (legacy-file)")
}
}
func assertMutualExclusion(t *testing.T, err error, flag1, flag2 string) {
if err == nil {
t.Errorf("expected mutually exclusive flags to fail, but no error was thrown: %v %v", flag1, flag2)
return
}
errstr := err.Error()
if !(strings.Contains(errstr, flag1) && strings.Contains(errstr, flag2) && strings.Contains(errstr, "mutually exclusive")) {
t.Errorf("expected mutually exclusive flags to fail, but message differs: %v %v %v", flag1, flag2, errstr)
}
}
......@@ -9,10 +9,14 @@ require (
golang.org/x/crypto v0.3.0
)
replace github.com/codegangsta/cli v1.22.12 => github.com/urfave/cli v1.22.12
require (
github.com/Azure/go-ansiterm v0.0.0-20210617225240-d185dfc1b5a1 // indirect
github.com/beorn7/perks v1.0.1 // indirect
github.com/cespare/xxhash/v2 v2.1.2 // indirect
github.com/codegangsta/cli v1.22.12 // indirect
github.com/cpuguy83/go-md2man/v2 v2.0.2 // indirect
github.com/docker/docker v20.10.21+incompatible // indirect
github.com/golang/protobuf v1.5.2 // indirect
github.com/matttproud/golang_protobuf_extensions v1.0.4 // indirect
......@@ -21,6 +25,7 @@ require (
github.com/prometheus/client_model v0.3.0 // indirect
github.com/prometheus/common v0.37.0 // indirect
github.com/prometheus/procfs v0.8.0 // indirect
github.com/russross/blackfriday/v2 v2.1.0 // indirect
golang.org/x/net v0.2.0 // indirect
golang.org/x/sys v0.2.0 // indirect
golang.org/x/term v0.2.0 // indirect
......
......@@ -34,6 +34,7 @@ dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7
github.com/Azure/go-ansiterm v0.0.0-20210617225240-d185dfc1b5a1 h1:UQHMgLO+TxOElx5B5HZ4hJQsoJ/PvUvKRhJHDQXO8P8=
github.com/Azure/go-ansiterm v0.0.0-20210617225240-d185dfc1b5a1/go.mod h1:xomTg63KZ2rFqZQzSB4Vz2SUXa1BpHTVz9L5PTmPC4E=
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
github.com/BurntSushi/toml v1.2.1/go.mod h1:CxXYINrC8qIiEnFrOxCa7Jy5BFHlXnUU2pbicEuybxQ=
github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo=
github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc=
github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc=
......@@ -53,6 +54,8 @@ github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5P
github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU=
github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc=
github.com/cpuguy83/go-md2man/v2 v2.0.2 h1:p1EgwI/C7NhT0JmVkwCD2ZBK8j4aeHQX2pMHHBfMQ6w=
github.com/cpuguy83/go-md2man/v2 v2.0.2/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o=
github.com/creack/pty v1.1.11 h1:07n33Z8lZxZ2qwegKbObQohDhXDQxiMMz1NOUGYlesw=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
......@@ -197,15 +200,24 @@ github.com/prometheus/procfs v0.7.3/go.mod h1:cz+aTbrPOrUb4q7XlbU9ygM+/jj0fzG6c1
github.com/prometheus/procfs v0.8.0 h1:ODq8ZFEaYeCaZOJlZZdJA2AbQR98dSHSM1KW/You5mo=
github.com/prometheus/procfs v0.8.0/go.mod h1:z7EfXMXOkbkqb9IINtpCn86r/to3BnA0uaxHdg830/4=
github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4=
github.com/russross/blackfriday/v2 v2.1.0 h1:JIOH55/0cWyOuilr9/qlrm0BSXldqnqwMsf35Ld67mk=
github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo=
github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE=
github.com/sirupsen/logrus v1.6.0/go.mod h1:7uNnSEd1DgxDLC74fIahvMZmmYsHGZGEOFrfsX/uA88=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo=
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY=
github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
github.com/stretchr/testify v1.8.1 h1:w7B6lhMri9wdJUVmEZPGGhZzrYTPvgJArz7wNPgYKsk=
github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
github.com/urfave/cli v1.22.12 h1:igJgVw1JdKH+trcLWLeLwZjU9fEfPesQ+9/e4MQ44S8=
github.com/urfave/cli v1.22.12/go.mod h1:sSBEIC79qR6OvcmsD4U3KABeOTxDqQtdDnaFuUN30b8=
github.com/yuin/goldmark v1.1.25/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
github.com/yuin/goldmark v1.1.32/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
......@@ -490,7 +502,9 @@ gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.2.5/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c h1:dUUwHk2QECo/6vqA44rthZ8ie2QXMNeKRTHCNY2nXvo=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
honnef.co/go/tools v0.0.0-20190418001031-e561f6794a2a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
......