diff --git a/cmd/machine.go b/cmd/machine.go index fe443a16a0..a80cc334d6 100644 --- a/cmd/machine.go +++ b/cmd/machine.go @@ -113,7 +113,7 @@ func main() { }, cli.StringFlag{ EnvVar: "MACHINE_STORAGE_PATH", - Name: "s, storage-path", + Name: "storage-path, s", Value: mcndirs.GetBaseDir(), Usage: "Configures storage path", }, diff --git a/docs/AVAILABLE_DRIVER_PLUGINS.md b/docs/AVAILABLE_DRIVER_PLUGINS.md index e5bc81acec..755b442ee0 100644 --- a/docs/AVAILABLE_DRIVER_PLUGINS.md +++ b/docs/AVAILABLE_DRIVER_PLUGINS.md @@ -21,16 +21,19 @@ with Docker Inc. Use 3rd party plugins at your own risk. | Name | Repository | Maintainer GitHub Handle | Maintainer Email | | ---------------------- | ------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------- | ------------------------------------------------------------ | +| 1&1 Cloud Server | | [StackPointCloud, Inc.](https://github.com/stackpointcloud) | sdk@1and1.com | | Aliyun ECS | | [denverdino](https://github.com/denverdino)
[menglingwei](https://github.com/menglingwei) | denverdino@gmail.com
v.con@qq.com | | Amazon Cloud Formation | | [Jeff Ellin](https://github.com/jeffellin) | acf@ellin.com | | BrightBox | | [NeilW](https://github.com/NeilW) | neil@aldur.co.uk | | CenturyLink Cloud | | [ack](https://github.com/ack) | albert.choi@ctl.io | +| Citrix XenServer | | [robertbreker](https://github.com/robertbreker)
[phusl](https://github.com/phusl) | robert.breker@citrix.com
phus.lu@citrix.com | | Docker-In-Docker | | [nathanleclaire](https://github.com/nathanleclaire) | nathan.leclaire@gmail.com | | HPE OneView | | [wenlock](https://github.com/wenlock)
[miqui](https://github.com/miqui) | wenlock@hpe.com
miqui@hpe.com | | KVM | | [dhiltgen](https://github.com/dhiltgen) | daniel.hiltgen@docker.com | | OpenNebula | | [jmelis](https://github.com/jmelis) | jmelis@opennebula.org | | OVH Cloud | | [yadutaf](https://github.com/yadutaf) | jt@yadutaf.fr | | Packet | | [betawaffle](https://github.com/betawaffle) | andy@packet.net | +| ProfitBricks | | [StackPointCloud, Inc.](https://github.com/stackpointcloud) | legal90@gmail.com | | Parallels for OSX | | [legal90](https://github.com/legal90) | legal90@gmail.com | | RackHD | | [kacole2](https://github.com/kacole2) | kendrick.coleman@emc.com | | SAKURA CLOUD | | [yamamoto-febc](https://github.com/yamamoto-febc) | yamamoto.febc@gmail.com | diff --git a/drivers/openstack/client.go b/drivers/openstack/client.go index 791a66f45e..83a57390aa 100644 --- a/drivers/openstack/client.go +++ b/drivers/openstack/client.go @@ -63,6 +63,7 @@ func (c *GenericClient) CreateInstance(d *Driver) (string, error) { Name: d.MachineName, FlavorRef: d.FlavorId, ImageRef: d.ImageId, + UserData: d.UserData, SecurityGroups: d.SecurityGroups, AvailabilityZone: d.AvailabilityZone, } diff --git a/drivers/openstack/openstack.go b/drivers/openstack/openstack.go index 3b31e8a3f7..89ce20cf47 100644 --- a/drivers/openstack/openstack.go +++ b/drivers/openstack/openstack.go @@ -37,6 +37,7 @@ type Driver struct { KeyPairName string NetworkName string NetworkId string + UserData []byte PrivateKeyFile string SecurityGroups []string FloatingIpPool string @@ -161,6 +162,12 @@ func (d *Driver) GetCreateFlags() []mcnflag.Flag { Usage: "Private keyfile to use for SSH (absolute path)", Value: "", }, + mcnflag.StringFlag{ + EnvVar: "OS_USER_DATA_FILE", + Name: "openstack-user-data-file", + Usage: "File containing an openstack userdata script", + Value: "", + }, mcnflag.StringFlag{ EnvVar: "OS_NETWORK_NAME", Name: "openstack-net-name", @@ -270,6 +277,16 @@ func (d *Driver) SetConfigFromFlags(flags drivers.DriverOptions) error { d.SSHPort = flags.Int("openstack-ssh-port") d.KeyPairName = flags.String("openstack-keypair-name") d.PrivateKeyFile = flags.String("openstack-private-key-file") + + if flags.String("openstack-user-data-file") != "" { + userData, err := ioutil.ReadFile(flags.String("openstack-user-data-file")) + if err == nil { + d.UserData = userData + } else { + return err + } + } + d.SetSwarmConfigFromFlags(flags) return d.checkConfig() diff --git a/libmachine/provision/configure_swarm.go b/libmachine/provision/configure_swarm.go index 8870ddd513..5d7545caec 100644 --- a/libmachine/provision/configure_swarm.go +++ b/libmachine/provision/configure_swarm.go @@ -86,7 +86,7 @@ func configureSwarm(p Provisioner, swarmOptions swarm.Options, authOptions auth. }, Binds: []string{hostBind}, PortBindings: map[string][]dockerclient.PortBinding{ - "3376/tcp": { + fmt.Sprintf("%s/tcp", port): { { HostIp: "0.0.0.0", HostPort: port, @@ -99,8 +99,8 @@ func configureSwarm(p Provisioner, swarmOptions swarm.Options, authOptions auth. Image: swarmOptions.Image, Env: swarmOptions.Env, ExposedPorts: map[string]struct{}{ - "2375/tcp": {}, - "3376/tcp": {}, + "2375/tcp": {}, + fmt.Sprintf("%s/tcp", port): {}, }, Cmd: cmdMaster, HostConfig: masterHostConfig, diff --git a/libmachine/ssh/client.go b/libmachine/ssh/client.go index b356247890..499df4af32 100644 --- a/libmachine/ssh/client.go +++ b/libmachine/ssh/client.go @@ -332,9 +332,12 @@ func NewExternalClient(sshBinaryPath, user, host string, port int, auth *Auth) ( mode := fi.Mode() log.Debugf("Using SSH private key: %s (%s)", privateKeyPath, mode) // Private key file should have strict permissions - if mode != 0600 { - // Abort with correct message - return nil, fmt.Errorf("Permissions %#o for '%s' are too open.", mode, privateKeyPath) + perm := mode.Perm() + if perm&0400 == 0 { + return nil, fmt.Errorf("'%s' is not readable", privateKeyPath) + } + if perm&0077 != 0 { + return nil, fmt.Errorf("permissions %#o for '%s' are too open", perm, privateKeyPath) } } args = append(args, "-i", privateKeyPath) diff --git a/libmachine/ssh/client_test.go b/libmachine/ssh/client_test.go index 2ca3d41d3c..5610c1c0e6 100644 --- a/libmachine/ssh/client_test.go +++ b/libmachine/ssh/client_test.go @@ -1,6 +1,9 @@ package ssh import ( + "fmt" + "io/ioutil" + "os" "runtime" "testing" @@ -46,39 +49,58 @@ func TestGetSSHCmdArgs(t *testing.T) { } func TestNewExternalClient(t *testing.T) { + keyFile, err := ioutil.TempFile("", "docker-machine-tests-dummy-private-key") + if err != nil { + t.Fatal(err) + } + defer keyFile.Close() + + keyFilename := keyFile.Name() + defer os.Remove(keyFilename) + cases := []struct { sshBinaryPath string user string host string port int auth *Auth + perm os.FileMode expectedError string skipOS string }{ { - sshBinaryPath: "/usr/local/bin/ssh", - user: "docker", - host: "localhost", - port: 22, auth: &Auth{Keys: []string{"/tmp/private-key-not-exist"}}, expectedError: "stat /tmp/private-key-not-exist: no such file or directory", skipOS: "none", }, { - sshBinaryPath: "/usr/local/bin/ssh", - user: "docker", - host: "localhost", - port: 22, - auth: &Auth{Keys: []string{"/dev/null"}}, - expectedError: "Permissions 0410000666 for '/dev/null' are too open.", + auth: &Auth{Keys: []string{keyFilename}}, + perm: 0400, + skipOS: "windows", + }, + { + auth: &Auth{Keys: []string{keyFilename}}, + perm: 0100, + expectedError: fmt.Sprintf("'%s' is not readable", keyFilename), + skipOS: "windows", + }, + { + auth: &Auth{Keys: []string{keyFilename}}, + perm: 0644, + expectedError: fmt.Sprintf("permissions 0644 for '%s' are too open", keyFilename), skipOS: "windows", }, } for _, c := range cases { if runtime.GOOS != c.skipOS { + keyFile.Chmod(c.perm) _, err := NewExternalClient(c.sshBinaryPath, c.user, c.host, c.port, c.auth) - assert.EqualError(t, err, c.expectedError) + if c.expectedError != "" { + assert.EqualError(t, err, c.expectedError) + } else { + assert.Equal(t, err, nil) + } } } }