Make libmachine usable by outside world

- Clear out some cruft tightly coupling libmachine to filestore

- Comment out drivers other than virtualbox for now

- Change way too many things

- Mostly, break out the code to be more modular.

- Destroy all traces of "provider" in its current form.  It will be
brought back as something more sensible, instead of something which
overlaps in function with both Host and Store.

- Fix mis-managed config passthru

- Remove a few instances of state stored in env vars

- This should be explicitly communicated in Go-land, not through the
shell.

- Rename "store" module to "persist"

- This is done mostly to avoid confusion about the fact that a concrete
instance of a "Store" interface is oftentimes referred to as "store" in
the code.

- Rip out repetitive antipattern for getting store

- This replaces the previous repetive idiom for getting the cert info, and
consequently the store, with a much less repetitive idiom.

- Also, some redundant methods in commands.go for accessing hosts have
either been simplified or removed entirely.

- First steps towards fixing up tests

- Test progress continues

- Replace unit tests with integration tests

- MAKE ALL UNIT TESTS PASS YAY

- Add helper test files

- Don't write to disk in libmachine/host

- Heh.. coverage check strikes again

- Fix remove code

- Move cert code around

- Continued progress: simplify Driver

- Fixups and make creation work with new model

- Move drivers module inside of libmachine

- Move ssh module inside of libmachine

- Move state module to libmachine

- Move utils module to libmachine

- Move version module to libmachine

- Move log module to libmachine

- Modify some constructor methods around

- Change Travis build dep structure

- Boring gofmt fix

- Add version module

- Move NewHost to store

- Update some boring cert path infos to make API easier to use

- Fix up some issues around the new model

- Clean up some cert path stuff

- Don't use shady functions to get store path :D

- Continue artifact work

- Fix silly machines dir bug

- Continue fixing silly path issues

- Change up output of vbm a bit

- Continue work to make example go

- Change output a little more

- Last changes needed to make create finish properly

- Fix config.go to use libmachine

- Cut down code duplication and make both methods work with libmachine

- Add pluggable logging implementation

- Return error when machine already in desired state

- Update example to show log method

- Fix file:// bug

- Fix Swarm defaults

- Remove unused TLS settings from Engine and Swarm options

- Remove spurious error

- Correct bug detecting if migration was performed

- Fix compilation errors from tests

- Fix most of remaining test issues

- Fix final silly bug in tests

- Remove extraneous debug code

- Add -race to test command

- Appease the gofmt

- Appease the generate coverage

- Making executive decision to remove Travis coverage check

In the early days I thought this would be a good idea because it would
encourage people to write tests in case they added a new module.  Well,
in fact it has just turned into a giant nuisance and made refactoring
work like this even more difficult.

- Move Get to Load
- Move HostListItem code to CLI

Signed-off-by: Nathan LeClaire <nathan.leclaire@gmail.com>
This commit is contained in:
Nathan LeClaire 2015-08-18 11:26:42 +09:00
parent f2bb2e0e4e
commit b5927f10c4
197 changed files with 3517 additions and 3882 deletions

View File

@ -4,7 +4,7 @@ import (
"fmt" "fmt"
"github.com/codegangsta/cli" "github.com/codegangsta/cli"
"github.com/docker/machine/log" "github.com/docker/machine/libmachine/log"
) )
func cmdActive(c *cli.Context) { func cmdActive(c *cli.Context) {
@ -12,22 +12,8 @@ func cmdActive(c *cli.Context) {
log.Fatal("Error: Too many arguments given.") log.Fatal("Error: Too many arguments given.")
} }
certInfo := getCertPathInfo(c) store := getStore(c)
defaultStore, err := getDefaultStore( host, err := getActiveHost(store)
c.GlobalString("storage-path"),
certInfo.CaCertPath,
certInfo.CaKeyPath,
)
if err != nil {
log.Fatal(err)
}
provider, err := newProvider(defaultStore)
if err != nil {
log.Fatal(err)
}
host, err := provider.GetActive()
if err != nil { if err != nil {
log.Fatalf("Error getting active host: %s", err) log.Fatalf("Error getting active host: %s", err)
} }

View File

@ -1 +0,0 @@
package commands

View File

@ -6,13 +6,10 @@ import (
"os" "os"
"path/filepath" "path/filepath"
"runtime" "runtime"
"sort"
"strings" "strings"
"github.com/codegangsta/cli" "github.com/codegangsta/cli"
"github.com/skarademir/naturalsort" "github.com/docker/machine/commands/mcndirs"
"github.com/docker/machine/drivers"
_ "github.com/docker/machine/drivers/amazonec2" _ "github.com/docker/machine/drivers/amazonec2"
_ "github.com/docker/machine/drivers/azure" _ "github.com/docker/machine/drivers/azure"
_ "github.com/docker/machine/drivers/digitalocean" _ "github.com/docker/machine/drivers/digitalocean"
@ -28,12 +25,11 @@ import (
_ "github.com/docker/machine/drivers/vmwarefusion" _ "github.com/docker/machine/drivers/vmwarefusion"
_ "github.com/docker/machine/drivers/vmwarevcloudair" _ "github.com/docker/machine/drivers/vmwarevcloudair"
_ "github.com/docker/machine/drivers/vmwarevsphere" _ "github.com/docker/machine/drivers/vmwarevsphere"
"github.com/docker/machine/libmachine/cert"
"github.com/docker/machine/libmachine" "github.com/docker/machine/libmachine/drivers"
"github.com/docker/machine/libmachine/auth" "github.com/docker/machine/libmachine/host"
"github.com/docker/machine/libmachine/swarm" "github.com/docker/machine/libmachine/log"
"github.com/docker/machine/log" "github.com/docker/machine/libmachine/persist"
"github.com/docker/machine/utils"
) )
var ( var (
@ -42,34 +38,6 @@ var (
ErrExpectedOneMachine = errors.New("Error: Expected one machine name as an argument.") ErrExpectedOneMachine = errors.New("Error: Expected one machine name as an argument.")
) )
type machineConfig struct {
machineName string
machineDir string
machineUrl string
clientKeyPath string
serverCertPath string
clientCertPath string
caCertPath string
caKeyPath string
serverKeyPath string
AuthOptions auth.AuthOptions
SwarmOptions swarm.SwarmOptions
}
func sortHostListItemsByName(items []libmachine.HostListItem) {
m := make(map[string]libmachine.HostListItem, len(items))
s := make([]string, len(items))
for i, v := range items {
name := strings.ToLower(v.Name)
m[name] = v
s[i] = name
}
sort.Sort(naturalsort.NaturalSort(s))
for i, v := range s {
items[i] = m[v]
}
}
func confirmInput(msg string) bool { func confirmInput(msg string) bool {
fmt.Printf("%s (y/n): ", msg) fmt.Printf("%s (y/n): ", msg)
var resp string var resp string
@ -88,69 +56,46 @@ func confirmInput(msg string) bool {
return false return false
} }
func newProvider(store libmachine.Store) (*libmachine.Provider, error) { func getMachineDir(rootPath string) string {
return libmachine.New(store) return filepath.Join(rootPath, "machines")
} }
func getDefaultStore(rootPath, caCertPath, privateKeyPath string) (libmachine.Store, error) { func getStore(c *cli.Context) persist.Store {
return libmachine.NewFilestore( certInfo := getCertPathInfoFromContext(c)
rootPath, return &persist.Filestore{
caCertPath, Path: c.GlobalString("storage-path"),
privateKeyPath, CaCertPath: certInfo.CaCertPath,
), nil CaPrivateKeyPath: certInfo.CaPrivateKeyPath,
}
} }
func setupCertificates(caCertPath, caKeyPath, clientCertPath, clientKeyPath string) error { func getFirstArgHost(c *cli.Context) *host.Host {
org := utils.GetUsername() store := getStore(c)
bits := 2048 hostName := c.Args().First()
h, err := store.Load(hostName)
if err != nil {
// I guess I feel OK with bailing here since if we can't get
// the host reliably we're definitely not going to be able to
// do anything else interesting, but also this premature exit
// feels wrong to me. Let's revisit it later.
log.Fatalf("Error trying to get host %q: %s", hostName, err)
}
return h
}
if _, err := os.Stat(utils.GetMachineCertDir()); err != nil { func getHostsFromContext(c *cli.Context) ([]*host.Host, error) {
if os.IsNotExist(err) { store := getStore(c)
if err := os.MkdirAll(utils.GetMachineCertDir(), 0700); err != nil { hosts := []*host.Host{}
log.Fatalf("Error creating machine config dir: %s", err)
} for _, hostName := range c.Args() {
} else { h, err := store.Load(hostName)
log.Fatal(err) if err != nil {
return nil, fmt.Errorf("Could not load host %q: %s", hostName, err)
} }
hosts = append(hosts, h)
} }
if _, err := os.Stat(caCertPath); os.IsNotExist(err) { return hosts, nil
log.Infof("Creating CA: %s", caCertPath)
// check if the key path exists; if so, error
if _, err := os.Stat(caKeyPath); err == nil {
log.Fatalf("The CA key already exists. Please remove it or specify a different key/cert.")
}
if err := utils.GenerateCACertificate(caCertPath, caKeyPath, org, bits); err != nil {
log.Infof("Error generating CA certificate: %s", err)
}
}
if _, err := os.Stat(clientCertPath); os.IsNotExist(err) {
log.Infof("Creating client certificate: %s", clientCertPath)
if _, err := os.Stat(utils.GetMachineCertDir()); err != nil {
if os.IsNotExist(err) {
if err := os.Mkdir(utils.GetMachineCertDir(), 0700); err != nil {
log.Fatalf("Error creating machine client cert dir: %s", err)
}
} else {
log.Fatal(err)
}
}
// check if the key path exists; if so, error
if _, err := os.Stat(clientKeyPath); err == nil {
log.Fatalf("The client key already exists. Please remove it or specify a different key/cert.")
}
if err := utils.GenerateCert([]string{""}, clientCertPath, clientKeyPath, caCertPath, caKeyPath, org, bits); err != nil {
log.Fatalf("Error generating client certificate: %s", err)
}
}
return nil
} }
var sharedCreateFlags = []cli.Flag{ var sharedCreateFlags = []cli.Flag{
@ -407,9 +352,21 @@ var Commands = []cli.Command{
}, },
} }
func printIP(h *host.Host) func() error {
return func() error {
ip, err := h.Driver.GetIP()
if err != nil {
return fmt.Errorf("Error getting IP address: %s", err)
}
fmt.Println(ip)
return nil
}
}
// machineCommand maps the command name to the corresponding machine command. // machineCommand maps the command name to the corresponding machine command.
// We run commands concurrently and communicate back an error if there was one. // We run commands concurrently and communicate back an error if there was one.
func machineCommand(actionName string, host *libmachine.Host, errorChan chan<- error) { func machineCommand(actionName string, host *host.Host, errorChan chan<- error) {
// TODO: These actions should have their own type.
commands := map[string](func() error){ commands := map[string](func() error){
"configureAuth": host.ConfigureAuth, "configureAuth": host.ConfigureAuth,
"start": host.Start, "start": host.Start,
@ -417,7 +374,7 @@ func machineCommand(actionName string, host *libmachine.Host, errorChan chan<- e
"restart": host.Restart, "restart": host.Restart,
"kill": host.Kill, "kill": host.Kill,
"upgrade": host.Upgrade, "upgrade": host.Upgrade,
"ip": host.PrintIP, "ip": printIP(host),
} }
log.Debugf("command=%s machine=%s", actionName, host.Name) log.Debugf("command=%s machine=%s", actionName, host.Name)
@ -431,10 +388,10 @@ func machineCommand(actionName string, host *libmachine.Host, errorChan chan<- e
} }
// runActionForeachMachine will run the command across multiple machines // runActionForeachMachine will run the command across multiple machines
func runActionForeachMachine(actionName string, machines []*libmachine.Host) { func runActionForeachMachine(actionName string, machines []*host.Host) {
var ( var (
numConcurrentActions = 0 numConcurrentActions = 0
serialMachines = []*libmachine.Host{} serialMachines = []*host.Host{}
errorChan = make(chan error) errorChan = make(chan error)
) )
@ -459,7 +416,7 @@ func runActionForeachMachine(actionName string, machines []*libmachine.Host) {
serialChan := make(chan error) serialChan := make(chan error)
go machineCommand(actionName, machine, serialChan) go machineCommand(actionName, machine, serialChan)
if err := <-serialChan; err != nil { if err := <-serialChan; err != nil {
log.Error(err) log.Errorln(err)
} }
close(serialChan) close(serialChan)
} }
@ -469,7 +426,7 @@ func runActionForeachMachine(actionName string, machines []*libmachine.Host) {
// rate limit us. // rate limit us.
for i := 0; i < numConcurrentActions; i++ { for i := 0; i < numConcurrentActions; i++ {
if err := <-errorChan; err != nil { if err := <-errorChan; err != nil {
log.Error(err) log.Errorln(err)
} }
} }
@ -477,183 +434,57 @@ func runActionForeachMachine(actionName string, machines []*libmachine.Host) {
} }
func runActionWithContext(actionName string, c *cli.Context) error { func runActionWithContext(actionName string, c *cli.Context) error {
machines, err := getHosts(c) store := getStore(c)
hosts, err := getHostsFromContext(c)
if err != nil { if err != nil {
return err return err
} }
if len(machines) == 0 { if len(hosts) == 0 {
log.Fatal(ErrNoMachineSpecified) log.Fatal(ErrNoMachineSpecified)
} }
runActionForeachMachine(actionName, machines) runActionForeachMachine(actionName, hosts)
for _, h := range hosts {
if err := store.Save(h); err != nil {
return fmt.Errorf("Error saving host to store: %s", err)
}
}
return nil return nil
} }
func getHosts(c *cli.Context) ([]*libmachine.Host, error) { // Returns the cert paths.
machines := []*libmachine.Host{} // codegangsta/cli will not set the cert paths if the storage-path is set to
for _, n := range c.Args() { // something different so we cannot use the paths in the global options. le
machine, err := loadMachine(n, c) // sigh.
if err != nil { func getCertPathInfoFromContext(c *cli.Context) cert.CertPathInfo {
return nil, err
}
machines = append(machines, machine)
}
return machines, nil
}
func loadMachine(name string, c *cli.Context) (*libmachine.Host, error) {
certInfo := getCertPathInfo(c)
defaultStore, err := getDefaultStore(
c.GlobalString("storage-path"),
certInfo.CaCertPath,
certInfo.CaKeyPath,
)
if err != nil {
log.Fatal(err)
}
provider, err := newProvider(defaultStore)
if err != nil {
log.Fatal(err)
}
host, err := provider.Get(name)
if err != nil {
return nil, err
}
return host, nil
}
func getHost(c *cli.Context) *libmachine.Host {
name := c.Args().First()
defaultStore, err := getDefaultStore(
c.GlobalString("storage-path"),
c.GlobalString("tls-ca-cert"),
c.GlobalString("tls-ca-key"),
)
if err != nil {
log.Fatal(err)
}
provider, err := newProvider(defaultStore)
if err != nil {
log.Fatal(err)
}
host, err := provider.Get(name)
if err != nil {
log.Fatalf("unable to load host: %v", err)
}
return host
}
func getDefaultProvider(c *cli.Context) *libmachine.Provider {
certInfo := getCertPathInfo(c)
defaultStore, err := getDefaultStore(
c.GlobalString("storage-path"),
certInfo.CaCertPath,
certInfo.CaKeyPath,
)
if err != nil {
log.Fatal(err)
}
provider, err := newProvider(defaultStore)
if err != nil {
log.Fatal(err)
}
return provider
}
func getMachineConfig(c *cli.Context) (*machineConfig, error) {
name := c.Args().First()
certInfo := getCertPathInfo(c)
defaultStore, err := getDefaultStore(
c.GlobalString("storage-path"),
certInfo.CaCertPath,
certInfo.CaKeyPath,
)
if err != nil {
log.Fatal(err)
}
provider, err := newProvider(defaultStore)
if err != nil {
log.Fatal(err)
}
m, err := provider.Get(name)
if err != nil {
return nil, err
}
machineDir := filepath.Join(utils.GetMachineDir(), m.Name)
caCert := filepath.Join(machineDir, "ca.pem")
caKey := filepath.Join(utils.GetMachineCertDir(), "ca-key.pem")
clientCert := filepath.Join(machineDir, "cert.pem")
clientKey := filepath.Join(machineDir, "key.pem")
serverCert := filepath.Join(machineDir, "server.pem")
serverKey := filepath.Join(machineDir, "server-key.pem")
machineUrl, err := m.GetURL()
if err != nil {
if err == drivers.ErrHostIsNotRunning {
machineUrl = ""
} else {
return nil, fmt.Errorf("Unexpected error getting machine url: %s", err)
}
}
return &machineConfig{
machineName: name,
machineDir: machineDir,
machineUrl: machineUrl,
clientKeyPath: clientKey,
clientCertPath: clientCert,
serverCertPath: serverCert,
caKeyPath: caKey,
caCertPath: caCert,
serverKeyPath: serverKey,
AuthOptions: *m.HostOptions.AuthOptions,
SwarmOptions: *m.HostOptions.SwarmOptions,
}, nil
}
// getCertPaths returns the cert paths
// codegangsta/cli will not set the cert paths if the storage-path
// is set to something different so we cannot use the paths
// in the global options. le sigh.
func getCertPathInfo(c *cli.Context) libmachine.CertPathInfo {
// setup cert paths
caCertPath := c.GlobalString("tls-ca-cert") caCertPath := c.GlobalString("tls-ca-cert")
caKeyPath := c.GlobalString("tls-ca-key") caKeyPath := c.GlobalString("tls-ca-key")
clientCertPath := c.GlobalString("tls-client-cert") clientCertPath := c.GlobalString("tls-client-cert")
clientKeyPath := c.GlobalString("tls-client-key") clientKeyPath := c.GlobalString("tls-client-key")
if caCertPath == "" { if caCertPath == "" {
caCertPath = filepath.Join(utils.GetMachineCertDir(), "ca.pem") caCertPath = filepath.Join(mcndirs.GetMachineCertDir(), "ca.pem")
} }
if caKeyPath == "" { if caKeyPath == "" {
caKeyPath = filepath.Join(utils.GetMachineCertDir(), "ca-key.pem") caKeyPath = filepath.Join(mcndirs.GetMachineCertDir(), "ca-key.pem")
} }
if clientCertPath == "" { if clientCertPath == "" {
clientCertPath = filepath.Join(utils.GetMachineCertDir(), "cert.pem") clientCertPath = filepath.Join(mcndirs.GetMachineCertDir(), "cert.pem")
} }
if clientKeyPath == "" { if clientKeyPath == "" {
clientKeyPath = filepath.Join(utils.GetMachineCertDir(), "key.pem") clientKeyPath = filepath.Join(mcndirs.GetMachineCertDir(), "key.pem")
} }
return libmachine.CertPathInfo{ return cert.CertPathInfo{
CaCertPath: caCertPath, CaCertPath: caCertPath,
CaKeyPath: caKeyPath, CaPrivateKeyPath: caKeyPath,
ClientCertPath: clientCertPath, ClientCertPath: clientCertPath,
ClientKeyPath: clientKeyPath, ClientKeyPath: clientKeyPath,
} }

View File

@ -1,112 +1,27 @@
package commands package commands
import ( import (
"fmt" "strings"
"io/ioutil"
"os"
"testing" "testing"
"github.com/docker/machine/drivers/fakedriver" "github.com/docker/machine/drivers/fakedriver"
_ "github.com/docker/machine/drivers/none" "github.com/docker/machine/libmachine/host"
"github.com/docker/machine/libmachine" "github.com/docker/machine/libmachine/hosttest"
"github.com/docker/machine/state" "github.com/docker/machine/libmachine/persisttest"
"github.com/docker/machine/libmachine/state"
"github.com/stretchr/testify/assert"
) )
const (
hostTestName = "test-host"
hostTestDriverName = "none"
hostTestCaCert = "test-cert"
hostTestPrivateKey = "test-key"
)
var (
hostTestStorePath string
TestStoreDir string
)
func init() {
tmpDir, err := ioutil.TempDir("", "machine-test-")
if err != nil {
fmt.Println(err)
os.Exit(1)
}
TestStoreDir = tmpDir
}
func clearHosts() error {
return os.RemoveAll(TestStoreDir)
}
func getTestStore() (libmachine.Store, error) {
tmpDir, err := ioutil.TempDir("", "machine-test-")
if err != nil {
fmt.Println(err)
os.Exit(1)
}
hostTestStorePath = tmpDir
os.Setenv("MACHINE_STORAGE_PATH", tmpDir)
return libmachine.NewFilestore(tmpDir, hostTestCaCert, hostTestPrivateKey), nil
}
func cleanup() {
os.RemoveAll(hostTestStorePath)
}
func getTestDriverFlags() *DriverOptionsMock {
name := hostTestName
flags := &DriverOptionsMock{
Data: map[string]interface{}{
"name": name,
"url": "unix:///var/run/docker.sock",
"swarm": false,
"swarm-host": "",
"swarm-master": false,
"swarm-discovery": "",
},
}
return flags
}
type DriverOptionsMock struct {
Data map[string]interface{}
}
func (d DriverOptionsMock) String(key string) string {
return d.Data[key].(string)
}
func (d DriverOptionsMock) StringSlice(key string) []string {
return d.Data[key].([]string)
}
func (d DriverOptionsMock) Int(key string) int {
return d.Data[key].(int)
}
func (d DriverOptionsMock) Bool(key string) bool {
return d.Data[key].(bool)
}
func TestRunActionForeachMachine(t *testing.T) { func TestRunActionForeachMachine(t *testing.T) {
storePath, err := ioutil.TempDir("", ".docker")
if err != nil {
t.Fatal("Error creating tmp dir:", err)
}
// Assume a bunch of machines in randomly started or // Assume a bunch of machines in randomly started or
// stopped states. // stopped states.
machines := []*libmachine.Host{ machines := []*host.Host{
{ {
Name: "foo", Name: "foo",
DriverName: "fakedriver", DriverName: "fakedriver",
Driver: &fakedriver.FakeDriver{ Driver: &fakedriver.FakeDriver{
MockState: state.Running, MockState: state.Running,
}, },
StorePath: storePath,
}, },
{ {
Name: "bar", Name: "bar",
@ -114,7 +29,6 @@ func TestRunActionForeachMachine(t *testing.T) {
Driver: &fakedriver.FakeDriver{ Driver: &fakedriver.FakeDriver{
MockState: state.Stopped, MockState: state.Stopped,
}, },
StorePath: storePath,
}, },
{ {
Name: "baz", Name: "baz",
@ -126,7 +40,6 @@ func TestRunActionForeachMachine(t *testing.T) {
Driver: &fakedriver.FakeDriver{ Driver: &fakedriver.FakeDriver{
MockState: state.Stopped, MockState: state.Stopped,
}, },
StorePath: storePath,
}, },
{ {
Name: "spam", Name: "spam",
@ -134,7 +47,6 @@ func TestRunActionForeachMachine(t *testing.T) {
Driver: &fakedriver.FakeDriver{ Driver: &fakedriver.FakeDriver{
MockState: state.Running, MockState: state.Running,
}, },
StorePath: storePath,
}, },
{ {
Name: "eggs", Name: "eggs",
@ -142,7 +54,6 @@ func TestRunActionForeachMachine(t *testing.T) {
Driver: &fakedriver.FakeDriver{ Driver: &fakedriver.FakeDriver{
MockState: state.Stopped, MockState: state.Stopped,
}, },
StorePath: storePath,
}, },
{ {
Name: "ham", Name: "ham",
@ -150,7 +61,6 @@ func TestRunActionForeachMachine(t *testing.T) {
Driver: &fakedriver.FakeDriver{ Driver: &fakedriver.FakeDriver{
MockState: state.Running, MockState: state.Running,
}, },
StorePath: storePath,
}, },
} }
@ -191,3 +101,29 @@ func TestRunActionForeachMachine(t *testing.T) {
} }
} }
} }
func TestPrintIPEmptyGivenLocalEngine(t *testing.T) {
defer persisttest.Cleanup()
host, _ := hosttest.GetDefaultTestHost()
out, w := captureStdout()
assert.Nil(t, printIP(host)())
w.Close()
assert.Equal(t, "", strings.TrimSpace(<-out))
}
func TestPrintIPPrintsGivenRemoteEngine(t *testing.T) {
defer cleanup()
host, _ := hosttest.GetDefaultTestHost()
host.Driver = &fakedriver.FakeDriver{}
out, w := captureStdout()
assert.Nil(t, printIP(host)())
w.Close()
assert.Equal(t, "1.2.3.4", strings.TrimSpace(<-out))
}

View File

@ -6,74 +6,116 @@ import (
"strings" "strings"
"github.com/codegangsta/cli" "github.com/codegangsta/cli"
"github.com/docker/machine/log" "github.com/docker/machine/libmachine/auth"
"github.com/docker/machine/utils" "github.com/docker/machine/libmachine/cert"
"github.com/docker/machine/libmachine/host"
"github.com/docker/machine/libmachine/log"
"github.com/docker/machine/libmachine/state"
) )
func cmdConfig(c *cli.Context) { func cmdConfig(c *cli.Context) {
if len(c.Args()) != 1 { if len(c.Args()) != 1 {
log.Fatal(ErrExpectedOneMachine) log.Fatal(ErrExpectedOneMachine)
} }
cfg, err := getMachineConfig(c)
h := getFirstArgHost(c)
dockerHost, authOptions, err := runConnectionBoilerplate(h, c)
if err != nil { if err != nil {
log.Fatal(err) log.Fatalf("Error running connection boilerplate: %s", err)
} }
dockerHost, err := getHost(c).Driver.GetURL() log.Debug(dockerHost)
fmt.Printf("--tlsverify --tlscacert=%q --tlscert=%q --tlskey=%q -H=%s",
authOptions.CaCertPath, authOptions.ClientCertPath, authOptions.ClientKeyPath, dockerHost)
}
func runConnectionBoilerplate(h *host.Host, c *cli.Context) (string, *auth.AuthOptions, error) {
hostState, err := h.Driver.GetState()
if err != nil { if err != nil {
log.Fatal(err) // TODO: This is a common operation and should have a commonly
// defined error.
return "", &auth.AuthOptions{}, fmt.Errorf("Error trying to get host state: %s", err)
}
if hostState != state.Running {
return "", &auth.AuthOptions{}, fmt.Errorf("%s is not running. Please start it in order to use the connection settings.", h.Name)
}
dockerHost, err := h.Driver.GetURL()
if err != nil {
return "", &auth.AuthOptions{}, fmt.Errorf("Error getting driver URL: %s", err)
} }
if c.Bool("swarm") { if c.Bool("swarm") {
if !cfg.SwarmOptions.Master { var err error
log.Fatalf("%s is not a swarm master", cfg.machineName) dockerHost, err = parseSwarm(dockerHost, h)
}
u, err := url.Parse(cfg.SwarmOptions.Host)
if err != nil { if err != nil {
log.Fatal(err) return "", &auth.AuthOptions{}, fmt.Errorf("Error parsing swarm: %s", err)
}
}
u, err := url.Parse(dockerHost)
if err != nil {
return "", &auth.AuthOptions{}, fmt.Errorf("Error parsing URL: %s", err)
}
authOptions := h.HostOptions.AuthOptions
if err := checkCert(u.Host, authOptions, c); err != nil {
return "", &auth.AuthOptions{}, fmt.Errorf("Error checking and/or regenerating the certs: %s", err)
}
return dockerHost, authOptions, nil
}
func checkCert(hostUrl string, authOptions *auth.AuthOptions, c *cli.Context) error {
valid, err := cert.ValidateCertificate(
hostUrl,
authOptions.CaCertPath,
authOptions.ServerCertPath,
authOptions.ServerKeyPath,
)
if err != nil {
return fmt.Errorf("Error attempting to validate the certficate: %s", err)
}
if !valid {
log.Errorf("Invalid certs detected; regenerating for %s", hostUrl)
if err := runActionWithContext("configureAuth", c); err != nil {
return fmt.Errorf("Error attempting to regenerate the certs: %s", err)
}
}
return nil
}
// TODO: This could use a unit test.
func parseSwarm(hostUrl string, h *host.Host) (string, error) {
swarmOptions := h.HostOptions.SwarmOptions
if !swarmOptions.Master {
return "", fmt.Errorf("Error: %s is not a swarm master. The --swarm flag is intended for use with swarm masters.", h.Name)
}
u, err := url.Parse(swarmOptions.Host)
if err != nil {
return "", fmt.Errorf("There was an error parsing the url: %s", err)
} }
parts := strings.Split(u.Host, ":") parts := strings.Split(u.Host, ":")
swarmPort := parts[1] swarmPort := parts[1]
// get IP of machine to replace in case swarm host is 0.0.0.0 // get IP of machine to replace in case swarm host is 0.0.0.0
mUrl, err := url.Parse(dockerHost) mUrl, err := url.Parse(hostUrl)
if err != nil { if err != nil {
log.Fatal(err) return "", fmt.Errorf("There was an error parsing the url: %s", err)
} }
mParts := strings.Split(mUrl.Host, ":") mParts := strings.Split(mUrl.Host, ":")
machineIp := mParts[0] machineIp := mParts[0]
dockerHost = fmt.Sprintf("tcp://%s:%s\n", machineIp, swarmPort) hostUrl = fmt.Sprintf("tcp://%s:%s", machineIp, swarmPort)
}
log.Debug(dockerHost) return hostUrl, nil
u, err := url.Parse(cfg.machineUrl)
if err != nil {
log.Fatal(err)
}
if u.Scheme != "unix" {
// validate cert and regenerate if needed
valid, err := utils.ValidateCertificate(
u.Host,
cfg.caCertPath,
cfg.serverCertPath,
cfg.serverKeyPath,
)
if err != nil {
log.Fatal(err)
}
if !valid {
log.Debugf("invalid certs detected; regenerating for %s", u.Host)
if err := runActionWithContext("configureAuth", c); err != nil {
log.Fatal(err)
}
}
}
fmt.Printf("--tlsverify --tlscacert=%q --tlscert=%q --tlskey=%q -H=%s\n",
cfg.caCertPath, cfg.clientCertPath, cfg.clientKeyPath, dockerHost)
} }

View File

@ -1,104 +0,0 @@
package commands
import (
"bytes"
"flag"
"fmt"
"io"
"os"
"path/filepath"
"strings"
"testing"
"github.com/codegangsta/cli"
"github.com/docker/machine/libmachine"
"github.com/docker/machine/libmachine/auth"
"github.com/docker/machine/libmachine/engine"
"github.com/docker/machine/libmachine/swarm"
)
func TestCmdConfig(t *testing.T) {
defer cleanup()
stdout := os.Stdout
r, w, _ := os.Pipe()
os.Stdout = w
defer func() {
os.Stdout = stdout
}()
store, err := getTestStore()
if err != nil {
t.Fatal(err)
}
provider, err := libmachine.New(store)
if err != nil {
t.Fatal(err)
}
flags := getTestDriverFlags()
hostOptions := &libmachine.HostOptions{
EngineOptions: &engine.EngineOptions{},
SwarmOptions: &swarm.SwarmOptions{
Master: false,
Discovery: "",
Address: "",
Host: "",
},
AuthOptions: &auth.AuthOptions{},
}
host, err := provider.Create("test-a", "none", hostOptions, flags)
if err != nil {
t.Fatal(err)
}
outStr := make(chan string)
go func() {
var testOutput bytes.Buffer
io.Copy(&testOutput, r)
outStr <- testOutput.String()
}()
set := flag.NewFlagSet("config", 0)
set.Parse([]string{"test-a"})
globalSet := flag.NewFlagSet("test", 0)
globalSet.String("storage-path", store.GetPath(), "")
c := cli.NewContext(nil, set, globalSet)
cmdConfig(c)
w.Close()
out := <-outStr
if !strings.Contains(out, "--tlsverify") {
t.Fatalf("Expect --tlsverify")
}
testMachineDir := filepath.Join(store.GetPath(), "machines", host.Name)
tlscacert := fmt.Sprintf("--tlscacert=\"%s/ca.pem\"", testMachineDir)
if !strings.Contains(out, tlscacert) {
t.Fatalf("Expected to find %s in %s", tlscacert, out)
}
tlscert := fmt.Sprintf("--tlscert=\"%s/cert.pem\"", testMachineDir)
if !strings.Contains(out, tlscert) {
t.Fatalf("Expected to find %s in %s", tlscert, out)
}
tlskey := fmt.Sprintf("--tlskey=\"%s/key.pem\"", testMachineDir)
if !strings.Contains(out, tlskey) {
t.Fatalf("Expected to find %s in %s", tlskey, out)
}
if !strings.Contains(out, "-H=unix:///var/run/docker.sock") {
t.Fatalf("Expect docker host URL")
}
}

View File

@ -1,31 +1,51 @@
package commands package commands
import ( import (
"errors"
"fmt" "fmt"
"os"
"path/filepath" "path/filepath"
"regexp" "regexp"
"github.com/docker/machine/log"
"github.com/codegangsta/cli" "github.com/codegangsta/cli"
"github.com/docker/machine/drivers" "github.com/docker/machine/commands/mcndirs"
"github.com/docker/machine/drivers/driverfactory"
"github.com/docker/machine/libmachine" "github.com/docker/machine/libmachine"
"github.com/docker/machine/libmachine/auth" "github.com/docker/machine/libmachine/auth"
"github.com/docker/machine/libmachine/drivers"
"github.com/docker/machine/libmachine/engine" "github.com/docker/machine/libmachine/engine"
"github.com/docker/machine/libmachine/host"
"github.com/docker/machine/libmachine/log"
"github.com/docker/machine/libmachine/mcnerror"
"github.com/docker/machine/libmachine/persist"
"github.com/docker/machine/libmachine/swarm" "github.com/docker/machine/libmachine/swarm"
"github.com/docker/machine/utils" )
var (
ErrDriverNotRecognized = errors.New("Driver not recognized.")
) )
func cmdCreate(c *cli.Context) { func cmdCreate(c *cli.Context) {
var ( var (
err error driver drivers.Driver
) )
driver := c.String("driver")
driverName := c.String("driver")
name := c.Args().First() name := c.Args().First()
certInfo := getCertPathInfoFromContext(c)
storePath := c.GlobalString("storage-path")
store := &persist.Filestore{
Path: storePath,
CaCertPath: certInfo.CaCertPath,
CaPrivateKeyPath: certInfo.CaPrivateKeyPath,
}
// TODO: Not really a fan of "none" as the default driver... // TODO: Not really a fan of "none" as the default driver...
if driver != "none" { if driverName != "none" {
c.App.Commands, err = trimDriverFlags(driver, c.App.Commands) var err error
c.App.Commands, err = trimDriverFlags(driverName, c.App.Commands)
if err != nil { if err != nil {
log.Fatal(err) log.Fatal(err)
} }
@ -36,42 +56,25 @@ func cmdCreate(c *cli.Context) {
log.Fatal("You must specify a machine name") log.Fatal("You must specify a machine name")
} }
validName := host.ValidateHostName(name)
if !validName {
log.Fatal("Error creating machine: ", mcnerror.ErrInvalidHostname)
}
if err := validateSwarmDiscovery(c.String("swarm-discovery")); err != nil { if err := validateSwarmDiscovery(c.String("swarm-discovery")); err != nil {
log.Fatalf("Error parsing swarm discovery: %s", err) log.Fatalf("Error parsing swarm discovery: %s", err)
} }
certInfo := getCertPathInfo(c) hostOptions := &host.HostOptions{
if err := setupCertificates(
certInfo.CaCertPath,
certInfo.CaKeyPath,
certInfo.ClientCertPath,
certInfo.ClientKeyPath); err != nil {
log.Fatalf("Error generating certificates: %s", err)
}
defaultStore, err := getDefaultStore(
c.GlobalString("storage-path"),
certInfo.CaCertPath,
certInfo.CaKeyPath,
)
if err != nil {
log.Fatal(err)
}
provider, err := newProvider(defaultStore)
if err != nil {
log.Fatal(err)
}
hostOptions := &libmachine.HostOptions{
AuthOptions: &auth.AuthOptions{ AuthOptions: &auth.AuthOptions{
CertDir: mcndirs.GetMachineCertDir(),
CaCertPath: certInfo.CaCertPath, CaCertPath: certInfo.CaCertPath,
PrivateKeyPath: certInfo.CaKeyPath, CaPrivateKeyPath: certInfo.CaPrivateKeyPath,
ClientCertPath: certInfo.ClientCertPath, ClientCertPath: certInfo.ClientCertPath,
ClientKeyPath: certInfo.ClientKeyPath, ClientKeyPath: certInfo.ClientKeyPath,
ServerCertPath: filepath.Join(utils.GetMachineDir(), name, "server.pem"), ServerCertPath: filepath.Join(mcndirs.GetMachineDir(), name, "server.pem"),
ServerKeyPath: filepath.Join(utils.GetMachineDir(), name, "server-key.pem"), ServerKeyPath: filepath.Join(mcndirs.GetMachineDir(), name, "server-key.pem"),
StorePath: filepath.Join(mcndirs.GetMachineDir(), name),
}, },
EngineOptions: &engine.EngineOptions{ EngineOptions: &engine.EngineOptions{
ArbitraryFlags: c.StringSlice("engine-opt"), ArbitraryFlags: c.StringSlice("engine-opt"),
@ -95,13 +98,39 @@ func cmdCreate(c *cli.Context) {
}, },
} }
_, err = provider.Create(name, driver, hostOptions, c) driver, err := driverfactory.NewDriver(driverName, name, storePath)
if err != nil { if err != nil {
log.Errorf("Error creating machine: %s", err) log.Fatalf("Error trying to get driver: %s", err)
log.Fatal("You will want to check the provider to make sure the machine and associated resources were properly removed.")
} }
info := fmt.Sprintf("%s env %s", c.App.Name, name) h, err := store.NewHost(driver)
if err != nil {
log.Fatalf("Error getting new host: %s", err)
}
h.HostOptions = hostOptions
exists, err := store.Exists(h.Name)
if err != nil {
log.Fatalf("Error checking if host exists: %s", err)
}
if exists {
log.Fatal(mcnerror.ErrHostAlreadyExists{
Name: h.Name,
})
}
// TODO: This should be moved out of the driver and done in the
// commands module.
if err := h.Driver.SetConfigFromFlags(c); err != nil {
log.Fatalf("Error setting machine configuration from flags provided: %s", err)
}
if err := libmachine.Create(store, h); err != nil {
log.Fatalf("Error creating machine: %s", err)
}
info := fmt.Sprintf("%s env %s", os.Args[0], name)
log.Infof("To see how to connect Docker to this machine, run: %s", info) log.Infof("To see how to connect Docker to this machine, run: %s", info)
} }

View File

@ -3,15 +3,11 @@ package commands
import ( import (
"errors" "errors"
"fmt" "fmt"
"net/url"
"os" "os"
"strings"
"text/template" "text/template"
"github.com/docker/machine/log"
"github.com/codegangsta/cli" "github.com/codegangsta/cli"
"github.com/docker/machine/utils" "github.com/docker/machine/libmachine/log"
) )
const ( const (
@ -37,6 +33,14 @@ func cmdEnv(c *cli.Context) {
if len(c.Args()) != 1 && !c.Bool("unset") { if len(c.Args()) != 1 && !c.Bool("unset") {
log.Fatal(improperEnvArgsError) log.Fatal(improperEnvArgsError)
} }
h := getFirstArgHost(c)
dockerHost, authOptions, err := runConnectionBoilerplate(h, c)
if err != nil {
log.Fatalf("Error running connection boilerplate: %s", err)
}
userShell := c.String("shell") userShell := c.String("shell")
if userShell == "" { if userShell == "" {
shell, err := detectShell() shell, err := detectShell()
@ -93,70 +97,12 @@ func cmdEnv(c *cli.Context) {
return return
} }
cfg, err := getMachineConfig(c)
if err != nil {
log.Fatal(err)
}
if cfg.machineUrl == "" {
log.Fatalf("%s is not running. Please start this with %s start %s", cfg.machineName, c.App.Name, cfg.machineName)
}
dockerHost := cfg.machineUrl
if c.Bool("swarm") {
if !cfg.SwarmOptions.Master {
log.Fatalf("%s is not a swarm master", cfg.machineName)
}
u, err := url.Parse(cfg.SwarmOptions.Host)
if err != nil {
log.Fatal(err)
}
parts := strings.Split(u.Host, ":")
swarmPort := parts[1]
// get IP of machine to replace in case swarm host is 0.0.0.0
mUrl, err := url.Parse(cfg.machineUrl)
if err != nil {
log.Fatal(err)
}
mParts := strings.Split(mUrl.Host, ":")
machineIp := mParts[0]
dockerHost = fmt.Sprintf("tcp://%s:%s", machineIp, swarmPort)
}
u, err := url.Parse(cfg.machineUrl)
if err != nil {
log.Fatal(err)
}
if u.Scheme != "unix" {
// validate cert and regenerate if needed
valid, err := utils.ValidateCertificate(
u.Host,
cfg.caCertPath,
cfg.serverCertPath,
cfg.serverKeyPath,
)
if err != nil {
log.Fatal(err)
}
if !valid {
log.Debugf("invalid certs detected; regenerating for %s", u.Host)
if err := runActionWithContext("configureAuth", c); err != nil {
log.Fatal(err)
}
}
}
shellCfg = ShellConfig{ shellCfg = ShellConfig{
DockerCertPath: cfg.machineDir, DockerCertPath: authOptions.CertDir,
DockerHost: dockerHost, DockerHost: dockerHost,
DockerTLSVerify: "1", DockerTLSVerify: "1",
UsageHint: usageHint, UsageHint: usageHint,
MachineName: cfg.machineName, MachineName: h.Name,
} }
switch userShell { switch userShell {

View File

@ -1,316 +0,0 @@
package commands
import (
"bytes"
"flag"
"fmt"
"io"
"os"
"path/filepath"
"strings"
"testing"
"github.com/codegangsta/cli"
"github.com/docker/machine/libmachine"
"github.com/docker/machine/libmachine/auth"
"github.com/docker/machine/libmachine/engine"
"github.com/docker/machine/libmachine/swarm"
)
func TestCmdEnvBash(t *testing.T) {
stdout := os.Stdout
shell := os.Getenv("SHELL")
r, w, _ := os.Pipe()
os.Stdout = w
os.Setenv("MACHINE_STORAGE_PATH", TestStoreDir)
os.Setenv("SHELL", "/bin/bash")
defer func() {
os.Setenv("MACHINE_STORAGE_PATH", "")
os.Setenv("SHELL", shell)
os.Stdout = stdout
}()
if err := clearHosts(); err != nil {
t.Fatal(err)
}
flags := getTestDriverFlags()
store, sErr := getTestStore()
if sErr != nil {
t.Fatal(sErr)
}
provider, err := libmachine.New(store)
if err != nil {
t.Fatal(err)
}
hostOptions := &libmachine.HostOptions{
EngineOptions: &engine.EngineOptions{},
SwarmOptions: &swarm.SwarmOptions{
Master: false,
Discovery: "",
Address: "",
Host: "",
},
AuthOptions: &auth.AuthOptions{},
}
host, err := provider.Create("test-a", "none", hostOptions, flags)
if err != nil {
t.Fatal(err)
}
host, err = provider.Get("test-a")
if err != nil {
t.Fatalf("error loading host: %v", err)
}
outStr := make(chan string)
go func() {
var testOutput bytes.Buffer
io.Copy(&testOutput, r)
outStr <- testOutput.String()
}()
set := flag.NewFlagSet("config", 0)
set.Parse([]string{"test-a"})
c := cli.NewContext(nil, set, set)
c.App = &cli.App{
Name: "docker-machine-test",
}
cmdEnv(c)
w.Close()
out := <-outStr
// parse the output into a map of envvar:value for easier testing below
envvars := make(map[string]string)
for _, e := range strings.Split(strings.TrimSpace(out), "\n") {
if !strings.HasPrefix(e, "export ") {
continue
}
kv := strings.SplitN(e, "=", 2)
key, value := kv[0], kv[1]
envvars[strings.Replace(key, "export ", "", 1)] = value
}
testMachineDir := filepath.Join(store.GetPath(), "machines", host.Name)
expected := map[string]string{
"DOCKER_TLS_VERIFY": "\"1\"",
"DOCKER_CERT_PATH": fmt.Sprintf("\"%s\"", testMachineDir),
"DOCKER_HOST": "\"unix:///var/run/docker.sock\"",
"DOCKER_MACHINE_NAME": `"test-a"`,
}
for k, v := range envvars {
if v != expected[k] {
t.Fatalf("Expected %s == <%s>, but was <%s>", k, expected[k], v)
}
}
}
func TestCmdEnvFish(t *testing.T) {
stdout := os.Stdout
shell := os.Getenv("SHELL")
r, w, _ := os.Pipe()
os.Stdout = w
os.Setenv("MACHINE_STORAGE_PATH", TestStoreDir)
os.Setenv("SHELL", "/bin/fish")
defer func() {
os.Setenv("MACHINE_STORAGE_PATH", "")
os.Setenv("SHELL", shell)
os.Stdout = stdout
}()
if err := clearHosts(); err != nil {
t.Fatal(err)
}
flags := getTestDriverFlags()
store, err := getTestStore()
if err != nil {
t.Fatal(err)
}
provider, err := libmachine.New(store)
if err != nil {
t.Fatal(err)
}
hostOptions := &libmachine.HostOptions{
EngineOptions: &engine.EngineOptions{},
SwarmOptions: &swarm.SwarmOptions{
Master: false,
Discovery: "",
Address: "",
Host: "",
},
AuthOptions: &auth.AuthOptions{},
}
host, err := provider.Create("test-a", "none", hostOptions, flags)
if err != nil {
t.Fatal(err)
}
host, err = provider.Get("test-a")
if err != nil {
t.Fatalf("error loading host: %v", err)
}
outStr := make(chan string)
go func() {
var testOutput bytes.Buffer
io.Copy(&testOutput, r)
outStr <- testOutput.String()
}()
set := flag.NewFlagSet("config", 0)
set.Parse([]string{"test-a"})
c := cli.NewContext(nil, set, set)
c.App = &cli.App{
Name: "docker-machine-test",
}
cmdEnv(c)
w.Close()
out := <-outStr
// parse the output into a map of envvar:value for easier testing below
envvars := make(map[string]string)
for _, e := range strings.Split(strings.TrimSuffix(out, ";\n"), ";\n") {
if !strings.HasPrefix(e, "set -x ") {
continue
}
kv := strings.SplitN(strings.Replace(e, "set -x ", "", 1), " ", 2)
key, value := kv[0], kv[1]
envvars[key] = value
}
testMachineDir := filepath.Join(store.GetPath(), "machines", host.Name)
expected := map[string]string{
"DOCKER_TLS_VERIFY": "\"1\"",
"DOCKER_CERT_PATH": fmt.Sprintf("\"%s\"", testMachineDir),
"DOCKER_HOST": "\"unix:///var/run/docker.sock\"",
"DOCKER_MACHINE_NAME": `"test-a"`,
}
for k, v := range envvars {
if v != expected[k] {
t.Fatalf("Expected %s == <%s>, but was <%s>", k, expected[k], v)
}
}
}
func TestCmdEnvPowerShell(t *testing.T) {
stdout := os.Stdout
shell := os.Getenv("SHELL")
r, w, _ := os.Pipe()
os.Stdout = w
os.Setenv("MACHINE_STORAGE_PATH", TestStoreDir)
os.Setenv("SHELL", "")
defer func() {
os.Setenv("MACHINE_STORAGE_PATH", "")
os.Setenv("SHELL", shell)
os.Stdout = stdout
}()
if err := clearHosts(); err != nil {
t.Fatal(err)
}
flags := getTestDriverFlags()
store, sErr := getTestStore()
if sErr != nil {
t.Fatal(sErr)
}
provider, err := libmachine.New(store)
if err != nil {
t.Fatal(err)
}
hostOptions := &libmachine.HostOptions{
EngineOptions: &engine.EngineOptions{},
SwarmOptions: &swarm.SwarmOptions{
Master: false,
Discovery: "",
Address: "",
Host: "",
},
AuthOptions: &auth.AuthOptions{},
}
host, err := provider.Create("test-a", "none", hostOptions, flags)
if err != nil {
t.Fatal(err)
}
host, err = provider.Get("test-a")
if err != nil {
t.Fatalf("error loading host: %v", err)
}
outStr := make(chan string)
go func() {
var testOutput bytes.Buffer
io.Copy(&testOutput, r)
outStr <- testOutput.String()
}()
set := flag.NewFlagSet("config", 0)
set.Parse([]string{"test-a"})
set.String("shell", "powershell", "")
c := cli.NewContext(nil, set, set)
c.App = &cli.App{
Name: "docker-machine-test",
}
cmdEnv(c)
w.Close()
out := <-outStr
// parse the output into a map of envvar:value for easier testing below
envvars := make(map[string]string)
for _, e := range strings.Split(strings.TrimSpace(out), "\n") {
if !strings.HasPrefix(e, "$Env") {
continue
}
kv := strings.SplitN(e, " = ", 2)
key, value := kv[0], kv[1]
envvars[strings.Replace(key, "$Env:", "", 1)] = value
}
testMachineDir := filepath.Join(store.GetPath(), "machines", host.Name)
expected := map[string]string{
"DOCKER_TLS_VERIFY": "\"1\"",
"DOCKER_CERT_PATH": fmt.Sprintf("\"%s\"", testMachineDir),
"DOCKER_HOST": "\"unix:///var/run/docker.sock\"",
"DOCKER_MACHINE_NAME": `"test-a"`,
}
for k, v := range envvars {
if v != expected[k] {
t.Fatalf("Expected %s == <%s>, but was <%s>", k, expected[k], v)
}
}
}

View File

@ -6,7 +6,7 @@ import (
"os" "os"
"text/template" "text/template"
"github.com/docker/machine/log" "github.com/docker/machine/libmachine/log"
"github.com/codegangsta/cli" "github.com/codegangsta/cli"
) )
@ -31,7 +31,7 @@ func cmdInspect(c *cli.Context) {
log.Fatalf("Template parsing error: %v\n", err) log.Fatalf("Template parsing error: %v\n", err)
} }
jsonHost, err := json.Marshal(getHost(c)) jsonHost, err := json.Marshal(getFirstArgHost(c))
if err != nil { if err != nil {
log.Fatal(err) log.Fatal(err)
} }
@ -45,7 +45,7 @@ func cmdInspect(c *cli.Context) {
} }
os.Stdout.Write([]byte{'\n'}) os.Stdout.Write([]byte{'\n'})
} else { } else {
prettyJSON, err := json.MarshalIndent(getHost(c), "", " ") prettyJSON, err := json.MarshalIndent(getFirstArgHost(c), "", " ")
if err != nil { if err != nil {
log.Fatal(err) log.Fatal(err)
} }

View File

@ -1,117 +0,0 @@
package commands
import (
"bytes"
"encoding/json"
"flag"
"io"
"os"
"strings"
"testing"
"github.com/codegangsta/cli"
"github.com/docker/machine/libmachine"
"github.com/docker/machine/libmachine/auth"
"github.com/docker/machine/libmachine/engine"
"github.com/docker/machine/libmachine/swarm"
"github.com/stretchr/testify/assert"
)
func TestCmdInspectFormat(t *testing.T) {
actual, host := runInspectCommand(t, []string{"test-a"})
expected, _ := json.MarshalIndent(host, "", " ")
assert.Equal(t, string(expected), actual)
actual, _ = runInspectCommand(t, []string{"--format", "{{.DriverName}}", "test-a"})
assert.Equal(t, "none", actual)
actual, _ = runInspectCommand(t, []string{"--format", "{{json .DriverName}}", "test-a"})
assert.Equal(t, "\"none\"", actual)
actual, _ = runInspectCommand(t, []string{"--format", "{{prettyjson .Driver}}", "test-a"})
type ExpectedDriver struct {
CaCertPath string
IPAddress string
MachineName string
PrivateKeyPath string
SSHPort int
SSHUser string
SwarmDiscovery string
SwarmHost string
SwarmMaster bool
URL string
}
expected, err := json.MarshalIndent(&ExpectedDriver{MachineName: "test-a", URL: "unix:///var/run/docker.sock"}, "", " ")
assert.NoError(t, err)
assert.Equal(t, string(expected), actual)
}
func runInspectCommand(t *testing.T, args []string) (string, *libmachine.Host) {
stdout := os.Stdout
stderr := os.Stderr
shell := os.Getenv("SHELL")
r, w, _ := os.Pipe()
os.Stdout = w
os.Stderr = w
os.Setenv("MACHINE_STORAGE_PATH", TestStoreDir)
os.Setenv("SHELL", "/bin/bash")
defer func() {
os.Setenv("MACHINE_STORAGE_PATH", "")
os.Setenv("SHELL", shell)
os.Stdout = stdout
os.Stderr = stderr
}()
if err := clearHosts(); err != nil {
t.Fatal(err)
}
store, sErr := getTestStore()
if sErr != nil {
t.Fatal(sErr)
}
provider, err := libmachine.New(store)
if err != nil {
t.Fatal(err)
}
hostOptions := &libmachine.HostOptions{
EngineOptions: &engine.EngineOptions{},
SwarmOptions: &swarm.SwarmOptions{
Master: false,
Discovery: "",
Address: "",
Host: "",
},
AuthOptions: &auth.AuthOptions{},
}
flags := getTestDriverFlags()
_, err = provider.Create("test-a", "none", hostOptions, flags)
if err != nil {
t.Fatal(err)
}
outStr := make(chan string)
go func() {
var testOutput bytes.Buffer
io.Copy(&testOutput, r)
outStr <- testOutput.String()
}()
set := flag.NewFlagSet("inspect", 0)
set.String("format", "", "")
set.Parse(args)
c := cli.NewContext(nil, set, set)
cmdInspect(c)
w.Close()
out := <-outStr
return strings.TrimSpace(out), getHost(c)
}

View File

@ -2,7 +2,7 @@ package commands
import ( import (
"github.com/codegangsta/cli" "github.com/codegangsta/cli"
"github.com/docker/machine/log" "github.com/docker/machine/libmachine/log"
) )
func cmdIp(c *cli.Context) { func cmdIp(c *cli.Context) {

View File

@ -1 +0,0 @@
package commands

View File

@ -1,7 +1,7 @@
package commands package commands
import ( import (
"github.com/docker/machine/log" "github.com/docker/machine/libmachine/log"
"github.com/codegangsta/cli" "github.com/codegangsta/cli"
) )

View File

@ -1 +0,0 @@
package commands

View File

@ -5,12 +5,23 @@ import (
"fmt" "fmt"
"os" "os"
"regexp" "regexp"
"sort"
"strings" "strings"
"text/tabwriter" "text/tabwriter"
"time"
"github.com/codegangsta/cli" "github.com/codegangsta/cli"
"github.com/docker/machine/libmachine" "github.com/docker/machine/libmachine/drivers"
"github.com/docker/machine/log" "github.com/docker/machine/libmachine/host"
"github.com/docker/machine/libmachine/log"
"github.com/docker/machine/libmachine/persist"
"github.com/docker/machine/libmachine/state"
"github.com/docker/machine/libmachine/swarm"
"github.com/skarademir/naturalsort"
)
var (
stateTimeoutDuration = 10 * time.Second
) )
// FilterOptions - // FilterOptions -
@ -21,6 +32,15 @@ type FilterOptions struct {
Name []string Name []string
} }
type HostListItem struct {
Name string
Active bool
DriverName string
State state.State
URL string
SwarmOptions *swarm.SwarmOptions
}
func cmdLs(c *cli.Context) { func cmdLs(c *cli.Context) {
quiet := c.Bool("quiet") quiet := c.Bool("quiet")
filters, err := parseFilters(c.StringSlice("filter")) filters, err := parseFilters(c.StringSlice("filter"))
@ -28,8 +48,8 @@ func cmdLs(c *cli.Context) {
log.Fatal(err) log.Fatal(err)
} }
provider := getDefaultProvider(c) store := getStore(c)
hostList, err := provider.List() hostList, err := store.List()
if err != nil { if err != nil {
log.Fatal(err) log.Fatal(err)
} }
@ -61,7 +81,7 @@ func cmdLs(c *cli.Context) {
} }
} }
items := libmachine.GetHostListItems(hostList) items := getHostListItems(hostList)
sortHostListItemsByName(items) sortHostListItemsByName(items)
@ -111,7 +131,7 @@ func parseFilters(filters []string) (FilterOptions, error) {
return options, nil return options, nil
} }
func filterHosts(hosts []*libmachine.Host, filters FilterOptions) []*libmachine.Host { func filterHosts(hosts []*host.Host, filters FilterOptions) []*host.Host {
if len(filters.SwarmName) == 0 && if len(filters.SwarmName) == 0 &&
len(filters.DriverName) == 0 && len(filters.DriverName) == 0 &&
len(filters.State) == 0 && len(filters.State) == 0 &&
@ -119,7 +139,7 @@ func filterHosts(hosts []*libmachine.Host, filters FilterOptions) []*libmachine.
return hosts return hosts
} }
filteredHosts := []*libmachine.Host{} filteredHosts := []*host.Host{}
swarmMasters := getSwarmMasters(hosts) swarmMasters := getSwarmMasters(hosts)
for _, h := range hosts { for _, h := range hosts {
@ -130,7 +150,7 @@ func filterHosts(hosts []*libmachine.Host, filters FilterOptions) []*libmachine.
return filteredHosts return filteredHosts
} }
func getSwarmMasters(hosts []*libmachine.Host) map[string]string { func getSwarmMasters(hosts []*host.Host) map[string]string {
swarmMasters := make(map[string]string) swarmMasters := make(map[string]string)
for _, h := range hosts { for _, h := range hosts {
swarmOptions := h.HostOptions.SwarmOptions swarmOptions := h.HostOptions.SwarmOptions
@ -141,7 +161,7 @@ func getSwarmMasters(hosts []*libmachine.Host) map[string]string {
return swarmMasters return swarmMasters
} }
func filterHost(host *libmachine.Host, filters FilterOptions, swarmMasters map[string]string) bool { func filterHost(host *host.Host, filters FilterOptions, swarmMasters map[string]string) bool {
swarmMatches := matchesSwarmName(host, filters.SwarmName, swarmMasters) swarmMatches := matchesSwarmName(host, filters.SwarmName, swarmMasters)
driverMatches := matchesDriverName(host, filters.DriverName) driverMatches := matchesDriverName(host, filters.DriverName)
stateMatches := matchesState(host, filters.State) stateMatches := matchesState(host, filters.State)
@ -150,7 +170,7 @@ func filterHost(host *libmachine.Host, filters FilterOptions, swarmMasters map[s
return swarmMatches && driverMatches && stateMatches && nameMatches return swarmMatches && driverMatches && stateMatches && nameMatches
} }
func matchesSwarmName(host *libmachine.Host, swarmNames []string, swarmMasters map[string]string) bool { func matchesSwarmName(host *host.Host, swarmNames []string, swarmMasters map[string]string) bool {
if len(swarmNames) == 0 { if len(swarmNames) == 0 {
return true return true
} }
@ -164,7 +184,7 @@ func matchesSwarmName(host *libmachine.Host, swarmNames []string, swarmMasters m
return false return false
} }
func matchesDriverName(host *libmachine.Host, driverNames []string) bool { func matchesDriverName(host *host.Host, driverNames []string) bool {
if len(driverNames) == 0 { if len(driverNames) == 0 {
return true return true
} }
@ -176,7 +196,7 @@ func matchesDriverName(host *libmachine.Host, driverNames []string) bool {
return false return false
} }
func matchesState(host *libmachine.Host, states []string) bool { func matchesState(host *host.Host, states []string) bool {
if len(states) == 0 { if len(states) == 0 {
return true return true
} }
@ -192,7 +212,7 @@ func matchesState(host *libmachine.Host, states []string) bool {
return false return false
} }
func matchesName(host *libmachine.Host, names []string) bool { func matchesName(host *host.Host, names []string) bool {
if len(names) == 0 { if len(names) == 0 {
return true return true
} }
@ -207,3 +227,139 @@ func matchesName(host *libmachine.Host, names []string) bool {
} }
return false return false
} }
func getActiveHost(store persist.Store) (*host.Host, error) {
hosts, err := store.List()
if err != nil {
return nil, err
}
hostListItems := getHostListItems(hosts)
for _, item := range hostListItems {
if item.Active {
h, err := store.Load(item.Name)
if err != nil {
return nil, err
}
return h, nil
}
}
return nil, errors.New("Active host not found")
}
func attemptGetHostState(h *host.Host, stateQueryChan chan<- HostListItem) {
currentState, err := h.Driver.GetState()
if err != nil {
log.Errorf("error getting state for host %s: %s", h.Name, err)
}
url, err := h.GetURL()
if err != nil {
if err == drivers.ErrHostIsNotRunning {
url = ""
} else {
log.Errorf("error getting URL for host %s: %s", h.Name, err)
}
}
active, err := isActive(h)
if err != nil {
log.Errorf("error determining if host is active for host %s: %s",
h.Name, err)
}
stateQueryChan <- HostListItem{
Name: h.Name,
Active: active,
DriverName: h.Driver.DriverName(),
State: currentState,
URL: url,
SwarmOptions: h.HostOptions.SwarmOptions,
}
}
func getHostState(h *host.Host, hostListItemsChan chan<- HostListItem) {
// This channel is used to communicate the properties we are querying
// about the host in the case of a successful read.
stateQueryChan := make(chan HostListItem)
go attemptGetHostState(h, stateQueryChan)
select {
// If we get back useful information, great. Forward it straight to
// the original parent channel.
case hli := <-stateQueryChan:
hostListItemsChan <- hli
// Otherwise, give up after a predetermined duration.
case <-time.After(stateTimeoutDuration):
hostListItemsChan <- HostListItem{
Name: h.Name,
DriverName: h.Driver.DriverName(),
State: state.Timeout,
}
}
}
func getHostListItems(hostList []*host.Host) []HostListItem {
hostListItems := []HostListItem{}
hostListItemsChan := make(chan HostListItem)
for _, h := range hostList {
go getHostState(h, hostListItemsChan)
}
for range hostList {
hostListItems = append(hostListItems, <-hostListItemsChan)
}
close(hostListItemsChan)
return hostListItems
}
// IsActive provides a single function for determining if a host is active
// based on both the url and if the host is stopped.
func isActive(h *host.Host) (bool, error) {
currentState, err := h.Driver.GetState()
if err != nil {
log.Errorf("error getting state for host %s: %s", h.Name, err)
return false, err
}
url, err := h.GetURL()
if err != nil {
if err == drivers.ErrHostIsNotRunning {
url = ""
} else {
log.Errorf("error getting URL for host %s: %s", h.Name, err)
return false, err
}
}
dockerHost := os.Getenv("DOCKER_HOST")
notStopped := currentState != state.Stopped
correctURL := url == dockerHost
isActive := notStopped && correctURL
return isActive, nil
}
func sortHostListItemsByName(items []HostListItem) {
m := make(map[string]HostListItem, len(items))
s := make([]string, len(items))
for i, v := range items {
name := strings.ToLower(v.Name)
m[name] = v
s[i] = name
}
sort.Sort(naturalsort.NaturalSort(s))
for i, v := range s {
items[i] = m[v]
}
}

View File

@ -1,15 +1,32 @@
package commands package commands
import ( import (
"bytes"
"io"
"os"
"testing" "testing"
"github.com/docker/machine/drivers/fakedriver" "github.com/docker/machine/drivers/fakedriver"
"github.com/docker/machine/libmachine" "github.com/docker/machine/libmachine/host"
"github.com/docker/machine/libmachine/state"
"github.com/docker/machine/libmachine/swarm" "github.com/docker/machine/libmachine/swarm"
"github.com/docker/machine/state"
"github.com/stretchr/testify/assert" "github.com/stretchr/testify/assert"
) )
var (
hostTestStorePath string
stdout *os.File
)
func init() {
stdout = os.Stdout
}
func cleanup() {
os.Stdout = stdout
os.RemoveAll(hostTestStorePath)
}
func TestParseFiltersErrorsGivenInvalidFilter(t *testing.T) { func TestParseFiltersErrorsGivenInvalidFilter(t *testing.T) {
_, err := parseFilters([]string{"foo=bar"}) _, err := parseFilters([]string{"foo=bar"})
assert.EqualError(t, err, "Unsupported filter key 'foo'") assert.EqualError(t, err, "Unsupported filter key 'foo'")
@ -52,11 +69,11 @@ func TestParseFiltersValueWithEqual(t *testing.T) {
func TestFilterHostsReturnsSameGivenNoFilters(t *testing.T) { func TestFilterHostsReturnsSameGivenNoFilters(t *testing.T) {
opts := FilterOptions{} opts := FilterOptions{}
hosts := []*libmachine.Host{ hosts := []*host.Host{
{ {
Name: "testhost", Name: "testhost",
DriverName: "fakedriver", DriverName: "fakedriver",
HostOptions: &libmachine.HostOptions{}, HostOptions: &host.HostOptions{},
}, },
} }
actual := filterHosts(hosts, opts) actual := filterHosts(hosts, opts)
@ -67,7 +84,7 @@ func TestFilterHostsReturnsEmptyGivenEmptyHosts(t *testing.T) {
opts := FilterOptions{ opts := FilterOptions{
SwarmName: []string{"foo"}, SwarmName: []string{"foo"},
} }
hosts := []*libmachine.Host{} hosts := []*host.Host{}
assert.Empty(t, filterHosts(hosts, opts)) assert.Empty(t, filterHosts(hosts, opts))
} }
@ -75,11 +92,11 @@ func TestFilterHostsReturnsEmptyGivenNonMatchingFilters(t *testing.T) {
opts := FilterOptions{ opts := FilterOptions{
SwarmName: []string{"foo"}, SwarmName: []string{"foo"},
} }
hosts := []*libmachine.Host{ hosts := []*host.Host{
{ {
Name: "testhost", Name: "testhost",
DriverName: "fakedriver", DriverName: "fakedriver",
HostOptions: &libmachine.HostOptions{}, HostOptions: &host.HostOptions{},
}, },
} }
assert.Empty(t, filterHosts(hosts, opts)) assert.Empty(t, filterHosts(hosts, opts))
@ -90,28 +107,28 @@ func TestFilterHostsBySwarmName(t *testing.T) {
SwarmName: []string{"master"}, SwarmName: []string{"master"},
} }
master := master :=
&libmachine.Host{ &host.Host{
Name: "master", Name: "master",
HostOptions: &libmachine.HostOptions{ HostOptions: &host.HostOptions{
SwarmOptions: &swarm.SwarmOptions{Master: true, Discovery: "foo"}, SwarmOptions: &swarm.SwarmOptions{Master: true, Discovery: "foo"},
}, },
} }
node1 := node1 :=
&libmachine.Host{ &host.Host{
Name: "node1", Name: "node1",
HostOptions: &libmachine.HostOptions{ HostOptions: &host.HostOptions{
SwarmOptions: &swarm.SwarmOptions{Master: false, Discovery: "foo"}, SwarmOptions: &swarm.SwarmOptions{Master: false, Discovery: "foo"},
}, },
} }
othermaster := othermaster :=
&libmachine.Host{ &host.Host{
Name: "othermaster", Name: "othermaster",
HostOptions: &libmachine.HostOptions{ HostOptions: &host.HostOptions{
SwarmOptions: &swarm.SwarmOptions{Master: true, Discovery: "bar"}, SwarmOptions: &swarm.SwarmOptions{Master: true, Discovery: "bar"},
}, },
} }
hosts := []*libmachine.Host{master, node1, othermaster} hosts := []*host.Host{master, node1, othermaster}
expected := []*libmachine.Host{master, node1} expected := []*host.Host{master, node1}
assert.EqualValues(t, filterHosts(hosts, opts), expected) assert.EqualValues(t, filterHosts(hosts, opts), expected)
} }
@ -121,25 +138,25 @@ func TestFilterHostsByDriverName(t *testing.T) {
DriverName: []string{"fakedriver"}, DriverName: []string{"fakedriver"},
} }
node1 := node1 :=
&libmachine.Host{ &host.Host{
Name: "node1", Name: "node1",
DriverName: "fakedriver", DriverName: "fakedriver",
HostOptions: &libmachine.HostOptions{}, HostOptions: &host.HostOptions{},
} }
node2 := node2 :=
&libmachine.Host{ &host.Host{
Name: "node2", Name: "node2",
DriverName: "virtualbox", DriverName: "virtualbox",
HostOptions: &libmachine.HostOptions{}, HostOptions: &host.HostOptions{},
} }
node3 := node3 :=
&libmachine.Host{ &host.Host{
Name: "node3", Name: "node3",
DriverName: "fakedriver", DriverName: "fakedriver",
HostOptions: &libmachine.HostOptions{}, HostOptions: &host.HostOptions{},
} }
hosts := []*libmachine.Host{node1, node2, node3} hosts := []*host.Host{node1, node2, node3}
expected := []*libmachine.Host{node1, node3} expected := []*host.Host{node1, node3}
assert.EqualValues(t, filterHosts(hosts, opts), expected) assert.EqualValues(t, filterHosts(hosts, opts), expected)
} }
@ -149,28 +166,28 @@ func TestFilterHostsByState(t *testing.T) {
State: []string{"Paused", "Saved", "Stopped"}, State: []string{"Paused", "Saved", "Stopped"},
} }
node1 := node1 :=
&libmachine.Host{ &host.Host{
Name: "node1", Name: "node1",
DriverName: "fakedriver", DriverName: "fakedriver",
HostOptions: &libmachine.HostOptions{}, HostOptions: &host.HostOptions{},
Driver: &fakedriver.FakeDriver{MockState: state.Paused}, Driver: &fakedriver.FakeDriver{MockState: state.Paused},
} }
node2 := node2 :=
&libmachine.Host{ &host.Host{
Name: "node2", Name: "node2",
DriverName: "virtualbox", DriverName: "virtualbox",
HostOptions: &libmachine.HostOptions{}, HostOptions: &host.HostOptions{},
Driver: &fakedriver.FakeDriver{MockState: state.Stopped}, Driver: &fakedriver.FakeDriver{MockState: state.Stopped},
} }
node3 := node3 :=
&libmachine.Host{ &host.Host{
Name: "node3", Name: "node3",
DriverName: "fakedriver", DriverName: "fakedriver",
HostOptions: &libmachine.HostOptions{}, HostOptions: &host.HostOptions{},
Driver: &fakedriver.FakeDriver{MockState: state.Running}, Driver: &fakedriver.FakeDriver{MockState: state.Running},
} }
hosts := []*libmachine.Host{node1, node2, node3} hosts := []*host.Host{node1, node2, node3}
expected := []*libmachine.Host{node1, node2} expected := []*host.Host{node1, node2}
assert.EqualValues(t, filterHosts(hosts, opts), expected) assert.EqualValues(t, filterHosts(hosts, opts), expected)
} }
@ -180,35 +197,35 @@ func TestFilterHostsByName(t *testing.T) {
Name: []string{"fire", "ice", "earth", "a.?r"}, Name: []string{"fire", "ice", "earth", "a.?r"},
} }
node1 := node1 :=
&libmachine.Host{ &host.Host{
Name: "fire", Name: "fire",
DriverName: "fakedriver", DriverName: "fakedriver",
HostOptions: &libmachine.HostOptions{}, HostOptions: &host.HostOptions{},
Driver: &fakedriver.FakeDriver{MockState: state.Paused, MockName: "fire"}, Driver: &fakedriver.FakeDriver{MockState: state.Paused, MockName: "fire"},
} }
node2 := node2 :=
&libmachine.Host{ &host.Host{
Name: "ice", Name: "ice",
DriverName: "adriver", DriverName: "adriver",
HostOptions: &libmachine.HostOptions{}, HostOptions: &host.HostOptions{},
Driver: &fakedriver.FakeDriver{MockState: state.Paused, MockName: "ice"}, Driver: &fakedriver.FakeDriver{MockState: state.Paused, MockName: "ice"},
} }
node3 := node3 :=
&libmachine.Host{ &host.Host{
Name: "air", Name: "air",
DriverName: "nodriver", DriverName: "nodriver",
HostOptions: &libmachine.HostOptions{}, HostOptions: &host.HostOptions{},
Driver: &fakedriver.FakeDriver{MockState: state.Paused, MockName: "air"}, Driver: &fakedriver.FakeDriver{MockState: state.Paused, MockName: "air"},
} }
node4 := node4 :=
&libmachine.Host{ &host.Host{
Name: "water", Name: "water",
DriverName: "falsedriver", DriverName: "falsedriver",
HostOptions: &libmachine.HostOptions{}, HostOptions: &host.HostOptions{},
Driver: &fakedriver.FakeDriver{MockState: state.Paused, MockName: "water"}, Driver: &fakedriver.FakeDriver{MockState: state.Paused, MockName: "water"},
} }
hosts := []*libmachine.Host{node1, node2, node3, node4} hosts := []*host.Host{node1, node2, node3, node4}
expected := []*libmachine.Host{node1, node2, node3} expected := []*host.Host{node1, node2, node3}
assert.EqualValues(t, filterHosts(hosts, opts), expected) assert.EqualValues(t, filterHosts(hosts, opts), expected)
} }
@ -219,25 +236,25 @@ func TestFilterHostsMultiFlags(t *testing.T) {
DriverName: []string{"fakedriver", "virtualbox"}, DriverName: []string{"fakedriver", "virtualbox"},
} }
node1 := node1 :=
&libmachine.Host{ &host.Host{
Name: "node1", Name: "node1",
DriverName: "fakedriver", DriverName: "fakedriver",
HostOptions: &libmachine.HostOptions{}, HostOptions: &host.HostOptions{},
} }
node2 := node2 :=
&libmachine.Host{ &host.Host{
Name: "node2", Name: "node2",
DriverName: "virtualbox", DriverName: "virtualbox",
HostOptions: &libmachine.HostOptions{}, HostOptions: &host.HostOptions{},
} }
node3 := node3 :=
&libmachine.Host{ &host.Host{
Name: "node3", Name: "node3",
DriverName: "softlayer", DriverName: "softlayer",
HostOptions: &libmachine.HostOptions{}, HostOptions: &host.HostOptions{},
} }
hosts := []*libmachine.Host{node1, node2, node3} hosts := []*host.Host{node1, node2, node3}
expected := []*libmachine.Host{node1, node2} expected := []*host.Host{node1, node2}
assert.EqualValues(t, filterHosts(hosts, opts), expected) assert.EqualValues(t, filterHosts(hosts, opts), expected)
} }
@ -248,28 +265,114 @@ func TestFilterHostsDifferentFlagsProduceAND(t *testing.T) {
State: []string{"Running"}, State: []string{"Running"},
} }
node1 := node1 :=
&libmachine.Host{ &host.Host{
Name: "node1", Name: "node1",
DriverName: "fakedriver", DriverName: "fakedriver",
HostOptions: &libmachine.HostOptions{}, HostOptions: &host.HostOptions{},
Driver: &fakedriver.FakeDriver{MockState: state.Paused}, Driver: &fakedriver.FakeDriver{MockState: state.Paused},
} }
node2 := node2 :=
&libmachine.Host{ &host.Host{
Name: "node2", Name: "node2",
DriverName: "virtualbox", DriverName: "virtualbox",
HostOptions: &libmachine.HostOptions{}, HostOptions: &host.HostOptions{},
Driver: &fakedriver.FakeDriver{MockState: state.Stopped}, Driver: &fakedriver.FakeDriver{MockState: state.Stopped},
} }
node3 := node3 :=
&libmachine.Host{ &host.Host{
Name: "node3", Name: "node3",
DriverName: "fakedriver", DriverName: "fakedriver",
HostOptions: &libmachine.HostOptions{}, HostOptions: &host.HostOptions{},
Driver: &fakedriver.FakeDriver{MockState: state.Running}, Driver: &fakedriver.FakeDriver{MockState: state.Running},
} }
hosts := []*libmachine.Host{node1, node2, node3} hosts := []*host.Host{node1, node2, node3}
expected := []*libmachine.Host{} expected := []*host.Host{}
assert.EqualValues(t, filterHosts(hosts, opts), expected) assert.EqualValues(t, filterHosts(hosts, opts), expected)
} }
func captureStdout() (chan string, *os.File) {
r, w, _ := os.Pipe()
os.Stdout = w
out := make(chan string)
go func() {
var testOutput bytes.Buffer
io.Copy(&testOutput, r)
out <- testOutput.String()
}()
return out, w
}
func TestGetHostListItems(t *testing.T) {
defer cleanup()
hostListItemsChan := make(chan HostListItem)
hosts := []*host.Host{
{
Name: "foo",
DriverName: "fakedriver",
Driver: &fakedriver.FakeDriver{
MockState: state.Running,
},
HostOptions: &host.HostOptions{
SwarmOptions: &swarm.SwarmOptions{
Master: false,
Address: "",
Discovery: "",
},
},
},
{
Name: "bar",
DriverName: "fakedriver",
Driver: &fakedriver.FakeDriver{
MockState: state.Stopped,
},
HostOptions: &host.HostOptions{
SwarmOptions: &swarm.SwarmOptions{
Master: false,
Address: "",
Discovery: "",
},
},
},
{
Name: "baz",
DriverName: "fakedriver",
Driver: &fakedriver.FakeDriver{
MockState: state.Running,
},
HostOptions: &host.HostOptions{
SwarmOptions: &swarm.SwarmOptions{
Master: false,
Address: "",
Discovery: "",
},
},
},
}
expected := map[string]state.State{
"foo": state.Running,
"bar": state.Stopped,
"baz": state.Running,
}
items := []HostListItem{}
for _, host := range hosts {
go getHostState(host, hostListItemsChan)
}
for i := 0; i < len(hosts); i++ {
items = append(items, <-hostListItemsChan)
}
for _, item := range items {
if expected[item.Name] != item.State {
t.Fatal("Expected state did not match for item", item)
}
}
}

32
commands/mcndirs/utils.go Normal file
View File

@ -0,0 +1,32 @@
package mcndirs
import (
"os"
"path/filepath"
"github.com/docker/machine/libmachine/mcnutils"
)
func GetBaseDir() string {
baseDir := os.Getenv("MACHINE_STORAGE_PATH")
if baseDir == "" {
baseDir = filepath.Join(mcnutils.GetHomeDir(), ".docker", "machine")
}
return baseDir
}
func GetDockerDir() string {
return filepath.Join(mcnutils.GetHomeDir(), ".docker")
}
func GetMachineDir() string {
return filepath.Join(GetBaseDir(), "machines")
}
func GetMachineCertDir() string {
return filepath.Join(GetBaseDir(), "certs")
}
func GetMachineCacheDir() string {
return filepath.Join(GetBaseDir(), "cache")
}

View File

@ -1,18 +1,17 @@
package utils package mcndirs
import ( import (
"io/ioutil"
"os" "os"
"path" "path"
"path/filepath"
"runtime"
"strings" "strings"
"testing" "testing"
"github.com/docker/machine/libmachine/mcnutils"
) )
func TestGetBaseDir(t *testing.T) { func TestGetBaseDir(t *testing.T) {
// reset any override env var // reset any override env var
homeDir := GetHomeDir() homeDir := mcnutils.GetHomeDir()
baseDir := GetBaseDir() baseDir := GetBaseDir()
if strings.Index(baseDir, homeDir) != 0 { if strings.Index(baseDir, homeDir) != 0 {
@ -32,7 +31,7 @@ func TestGetCustomBaseDir(t *testing.T) {
} }
func TestGetDockerDir(t *testing.T) { func TestGetDockerDir(t *testing.T) {
homeDir := GetHomeDir() homeDir := mcnutils.GetHomeDir()
baseDir := GetBaseDir() baseDir := GetBaseDir()
if strings.Index(baseDir, homeDir) != 0 { if strings.Index(baseDir, homeDir) != 0 {
@ -77,63 +76,3 @@ func TestGetMachineCertDir(t *testing.T) {
} }
os.Setenv("MACHINE_STORAGE_PATH", "") os.Setenv("MACHINE_STORAGE_PATH", "")
} }
func TestCopyFile(t *testing.T) {
testStr := "test-machine"
srcFile, err := ioutil.TempFile("", "machine-test-")
if err != nil {
t.Fatal(err)
}
srcFi, err := srcFile.Stat()
if err != nil {
t.Fatal(err)
}
srcFile.Write([]byte(testStr))
srcFile.Close()
srcFilePath := filepath.Join(os.TempDir(), srcFi.Name())
destFile, err := ioutil.TempFile("", "machine-copy-test-")
if err != nil {
t.Fatal(err)
}
destFi, err := destFile.Stat()
if err != nil {
t.Fatal(err)
}
destFile.Close()
destFilePath := filepath.Join(os.TempDir(), destFi.Name())
if err := CopyFile(srcFilePath, destFilePath); err != nil {
t.Fatal(err)
}
data, err := ioutil.ReadFile(destFilePath)
if err != nil {
t.Fatal(err)
}
if string(data) != testStr {
t.Fatalf("expected data \"%s\"; received \"%s\"", testStr, string(data))
}
}
func TestGetUsername(t *testing.T) {
currentUser := "unknown"
switch runtime.GOOS {
case "darwin", "linux":
currentUser = os.Getenv("USER")
case "windows":
currentUser = os.Getenv("USERNAME")
}
username := GetUsername()
if username != currentUser {
t.Fatalf("expected username %s; received %s", currentUser, username)
}
}

View File

@ -2,7 +2,7 @@ package commands
import ( import (
"github.com/codegangsta/cli" "github.com/codegangsta/cli"
"github.com/docker/machine/log" "github.com/docker/machine/libmachine/log"
) )
func cmdRegenerateCerts(c *cli.Context) { func cmdRegenerateCerts(c *cli.Context) {

View File

@ -1 +0,0 @@
package commands

View File

@ -1,7 +1,7 @@
package commands package commands
import ( import (
"github.com/docker/machine/log" "github.com/docker/machine/libmachine/log"
"github.com/codegangsta/cli" "github.com/codegangsta/cli"
) )

View File

@ -1 +0,0 @@
package commands

View File

@ -2,7 +2,7 @@ package commands
import ( import (
"github.com/codegangsta/cli" "github.com/codegangsta/cli"
"github.com/docker/machine/log" "github.com/docker/machine/libmachine/log"
) )
func cmdRm(c *cli.Context) { func cmdRm(c *cli.Context) {
@ -15,27 +15,14 @@ func cmdRm(c *cli.Context) {
isError := false isError := false
certInfo := getCertPathInfo(c) store := getStore(c)
defaultStore, err := getDefaultStore(
c.GlobalString("storage-path"),
certInfo.CaCertPath,
certInfo.CaKeyPath,
)
if err != nil {
log.Fatal(err)
}
provider, err := newProvider(defaultStore) for _, hostName := range c.Args() {
if err != nil { if err := store.Remove(hostName, force); err != nil {
log.Fatal(err) log.Errorf("Error removing machine %s: %s", hostName, err)
}
for _, host := range c.Args() {
if err := provider.Remove(host, force); err != nil {
log.Errorf("Error removing machine %s: %s", host, err)
isError = true isError = true
} else { } else {
log.Infof("Successfully removed %s", host) log.Infof("Successfully removed %s", hostName)
} }
} }
if isError { if isError {

View File

@ -1 +0,0 @@
package commands

View File

@ -8,8 +8,9 @@ import (
"strings" "strings"
"github.com/codegangsta/cli" "github.com/codegangsta/cli"
"github.com/docker/machine/libmachine" "github.com/docker/machine/libmachine/host"
"github.com/docker/machine/log" "github.com/docker/machine/libmachine/log"
"github.com/docker/machine/libmachine/persist"
) )
var ( var (
@ -26,7 +27,7 @@ var (
} }
) )
func getInfoForScpArg(hostAndPath string, provider libmachine.Provider) (*libmachine.Host, string, []string, error) { func getInfoForScpArg(hostAndPath string, store persist.Store) (*host.Host, string, []string, error) {
// TODO: What to do about colon in filepath? // TODO: What to do about colon in filepath?
splitInfo := strings.Split(hostAndPath, ":") splitInfo := strings.Split(hostAndPath, ":")
@ -38,7 +39,7 @@ func getInfoForScpArg(hostAndPath string, provider libmachine.Provider) (*libmac
// Remote path. e.g. "machinename:/usr/bin/cmatrix" // Remote path. e.g. "machinename:/usr/bin/cmatrix"
if len(splitInfo) == 2 { if len(splitInfo) == 2 {
path := splitInfo[1] path := splitInfo[1]
host, err := provider.Get(splitInfo[0]) host, err := store.Load(splitInfo[0])
if err != nil { if err != nil {
return nil, "", nil, fmt.Errorf("Error loading host: %s", err) return nil, "", nil, fmt.Errorf("Error loading host: %s", err)
} }
@ -52,7 +53,7 @@ func getInfoForScpArg(hostAndPath string, provider libmachine.Provider) (*libmac
return nil, "", nil, ErrMalformedInput return nil, "", nil, ErrMalformedInput
} }
func generateLocationArg(host *libmachine.Host, path string) (string, error) { func generateLocationArg(host *host.Host, path string) (string, error) {
locationPrefix := "" locationPrefix := ""
if host != nil { if host != nil {
ip, err := host.Driver.GetIP() ip, err := host.Driver.GetIP()
@ -64,18 +65,18 @@ func generateLocationArg(host *libmachine.Host, path string) (string, error) {
return locationPrefix + path, nil return locationPrefix + path, nil
} }
func getScpCmd(src, dest string, sshArgs []string, provider libmachine.Provider) (*exec.Cmd, error) { func getScpCmd(src, dest string, sshArgs []string, store persist.Store) (*exec.Cmd, error) {
cmdPath, err := exec.LookPath("scp") cmdPath, err := exec.LookPath("scp")
if err != nil { if err != nil {
return nil, errors.New("Error: You must have a copy of the scp binary locally to use the scp feature.") return nil, errors.New("Error: You must have a copy of the scp binary locally to use the scp feature.")
} }
srcHost, srcPath, srcOpts, err := getInfoForScpArg(src, provider) srcHost, srcPath, srcOpts, err := getInfoForScpArg(src, store)
if err != nil { if err != nil {
return nil, err return nil, err
} }
destHost, destPath, destOpts, err := getInfoForScpArg(dest, provider) destHost, destPath, destOpts, err := getInfoForScpArg(dest, store)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -129,8 +130,8 @@ func cmdScp(c *cli.Context) {
src := args[0] src := args[0]
dest := args[1] dest := args[1]
provider := getDefaultProvider(c) store := getStore(c)
cmd, err := getScpCmd(src, dest, sshArgs, *provider) cmd, err := getScpCmd(src, dest, sshArgs, store)
if err != nil { if err != nil {
log.Fatal(err) log.Fatal(err)

View File

@ -7,9 +7,9 @@ import (
"reflect" "reflect"
"testing" "testing"
"github.com/docker/machine/drivers" "github.com/docker/machine/libmachine/drivers"
"github.com/docker/machine/libmachine" "github.com/docker/machine/libmachine/host"
"github.com/docker/machine/state" "github.com/docker/machine/libmachine/state"
) )
type ScpFakeDriver struct { type ScpFakeDriver struct {
@ -18,14 +18,6 @@ type ScpFakeDriver struct {
type ScpFakeStore struct{} type ScpFakeStore struct{}
func (d ScpFakeDriver) AuthorizePort(ports []*drivers.Port) error {
return nil
}
func (d ScpFakeDriver) DeauthorizePort(ports []*drivers.Port) error {
return nil
}
func (d ScpFakeDriver) DriverName() string { func (d ScpFakeDriver) DriverName() string {
return "fake" return "fake"
} }
@ -114,33 +106,25 @@ func (d ScpFakeDriver) GetSSHKeyPath() string {
return "/fake/keypath/id_rsa" return "/fake/keypath/id_rsa"
} }
func (d ScpFakeDriver) ResolveStorePath(file string) string {
return "/tmp/store/machines/fake"
}
func (s ScpFakeStore) Exists(name string) (bool, error) { func (s ScpFakeStore) Exists(name string) (bool, error) {
return true, nil return true, nil
} }
func (s ScpFakeStore) GetActive() (*libmachine.Host, error) { func (s ScpFakeStore) GetActive() (*host.Host, error) {
return nil, nil return nil, nil
} }
func (s ScpFakeStore) GetPath() string { func (s ScpFakeStore) List() ([]*host.Host, error) {
return ""
}
func (s ScpFakeStore) GetCACertificatePath() (string, error) {
return "", nil
}
func (s ScpFakeStore) GetPrivateKeyPath() (string, error) {
return "", nil
}
func (s ScpFakeStore) List() ([]*libmachine.Host, error) {
return nil, nil return nil, nil
} }
func (s ScpFakeStore) Get(name string) (*libmachine.Host, error) { func (s ScpFakeStore) Load(name string) (*host.Host, error) {
if name == "myfunhost" { if name == "myfunhost" {
return &libmachine.Host{ return &host.Host{
Name: "myfunhost", Name: "myfunhost",
Driver: ScpFakeDriver{}, Driver: ScpFakeDriver{},
}, nil }, nil
@ -152,15 +136,19 @@ func (s ScpFakeStore) Remove(name string, force bool) error {
return nil return nil
} }
func (s ScpFakeStore) Save(host *libmachine.Host) error { func (s ScpFakeStore) Save(host *host.Host) error {
return nil return nil
} }
func (s ScpFakeStore) NewHost(driver drivers.Driver) (*host.Host, error) {
return nil, nil
}
func TestGetInfoForScpArg(t *testing.T) { func TestGetInfoForScpArg(t *testing.T) {
provider, _ := libmachine.New(ScpFakeStore{}) store := ScpFakeStore{}
expectedPath := "/tmp/foo" expectedPath := "/tmp/foo"
host, path, opts, err := getInfoForScpArg("/tmp/foo", *provider) host, path, opts, err := getInfoForScpArg("/tmp/foo", store)
if err != nil { if err != nil {
t.Fatalf("Unexpected error in local getInfoForScpArg call: %s", err) t.Fatalf("Unexpected error in local getInfoForScpArg call: %s", err)
} }
@ -174,7 +162,7 @@ func TestGetInfoForScpArg(t *testing.T) {
t.Fatal("opts should be nil") t.Fatal("opts should be nil")
} }
host, path, opts, err = getInfoForScpArg("myfunhost:/home/docker/foo", *provider) host, path, opts, err = getInfoForScpArg("myfunhost:/home/docker/foo", store)
if err != nil { if err != nil {
t.Fatalf("Unexpected error in machine-based getInfoForScpArg call: %s", err) t.Fatalf("Unexpected error in machine-based getInfoForScpArg call: %s", err)
} }
@ -194,14 +182,14 @@ func TestGetInfoForScpArg(t *testing.T) {
t.Fatalf("Expected path to be /home/docker/foo, got %s", path) t.Fatalf("Expected path to be /home/docker/foo, got %s", path)
} }
host, path, opts, err = getInfoForScpArg("foo:bar:widget", *provider) host, path, opts, err = getInfoForScpArg("foo:bar:widget", store)
if err != ErrMalformedInput { if err != ErrMalformedInput {
t.Fatalf("Didn't get back an error when we were expecting it for malformed args") t.Fatalf("Didn't get back an error when we were expecting it for malformed args")
} }
} }
func TestGenerateLocationArg(t *testing.T) { func TestGenerateLocationArg(t *testing.T) {
host := libmachine.Host{ host := host.Host{
Driver: ScpFakeDriver{}, Driver: ScpFakeDriver{},
} }
@ -224,8 +212,6 @@ func TestGenerateLocationArg(t *testing.T) {
} }
func TestGetScpCmd(t *testing.T) { func TestGetScpCmd(t *testing.T) {
provider, _ := libmachine.New(ScpFakeStore{})
// TODO: This is a little "integration-ey". Perhaps // TODO: This is a little "integration-ey". Perhaps
// make an ScpDispatcher (name?) interface so that the reliant // make an ScpDispatcher (name?) interface so that the reliant
// methods can be mocked. // methods can be mocked.
@ -238,8 +224,9 @@ func TestGetScpCmd(t *testing.T) {
"root@12.34.56.78:/home/docker/foo", "root@12.34.56.78:/home/docker/foo",
) )
expectedCmd := exec.Command("/usr/bin/scp", expectedArgs...) expectedCmd := exec.Command("/usr/bin/scp", expectedArgs...)
store := ScpFakeStore{}
cmd, err := getScpCmd("/tmp/foo", "myfunhost:/home/docker/foo", append(baseSSHArgs, "-3"), *provider) cmd, err := getScpCmd("/tmp/foo", "myfunhost:/home/docker/foo", append(baseSSHArgs, "-3"), store)
if err != nil { if err != nil {
t.Fatalf("Unexpected err getting scp command: %s", err) t.Fatalf("Unexpected err getting scp command: %s", err)
} }

View File

@ -4,8 +4,8 @@ import (
"fmt" "fmt"
"strings" "strings"
"github.com/docker/machine/log" "github.com/docker/machine/libmachine/log"
"github.com/docker/machine/state" "github.com/docker/machine/libmachine/state"
"github.com/codegangsta/cli" "github.com/codegangsta/cli"
) )
@ -18,22 +18,8 @@ func cmdSsh(c *cli.Context) {
log.Fatal("Error: Please specify a machine name.") log.Fatal("Error: Please specify a machine name.")
} }
certInfo := getCertPathInfo(c) store := getStore(c)
defaultStore, err := getDefaultStore( host, err := store.Load(name)
c.GlobalString("storage-path"),
certInfo.CaCertPath,
certInfo.CaKeyPath,
)
if err != nil {
log.Fatal(err)
}
provider, err := newProvider(defaultStore)
if err != nil {
log.Fatal(err)
}
host, err := provider.Get(name)
if err != nil { if err != nil {
log.Fatal(err) log.Fatal(err)
} }

View File

@ -1 +0,0 @@
package commands

View File

@ -1,7 +1,7 @@
package commands package commands
import ( import (
"github.com/docker/machine/log" "github.com/docker/machine/libmachine/log"
"github.com/codegangsta/cli" "github.com/codegangsta/cli"
) )

View File

@ -1 +0,0 @@
package commands

View File

@ -1,13 +1,13 @@
package commands package commands
import ( import (
"github.com/docker/machine/log" "github.com/docker/machine/libmachine/log"
"github.com/codegangsta/cli" "github.com/codegangsta/cli"
) )
func cmdStatus(c *cli.Context) { func cmdStatus(c *cli.Context) {
host := getHost(c) host := getFirstArgHost(c)
currentState, err := host.Driver.GetState() currentState, err := host.Driver.GetState()
if err != nil { if err != nil {
log.Errorf("error getting state for host %s: %s", host.Name, err) log.Errorf("error getting state for host %s: %s", host.Name, err)

View File

@ -1 +0,0 @@
package commands

View File

@ -1,7 +1,7 @@
package commands package commands
import ( import (
"github.com/docker/machine/log" "github.com/docker/machine/libmachine/log"
"github.com/codegangsta/cli" "github.com/codegangsta/cli"
) )

View File

@ -1 +0,0 @@
package commands

View File

@ -1,7 +1,7 @@
package commands package commands
import ( import (
"github.com/docker/machine/log" "github.com/docker/machine/libmachine/log"
"github.com/codegangsta/cli" "github.com/codegangsta/cli"
) )

View File

@ -1 +0,0 @@
package commands

View File

@ -3,13 +3,13 @@ package commands
import ( import (
"fmt" "fmt"
"github.com/docker/machine/log" "github.com/docker/machine/libmachine/log"
"github.com/codegangsta/cli" "github.com/codegangsta/cli"
) )
func cmdUrl(c *cli.Context) { func cmdUrl(c *cli.Context) {
url, err := getHost(c).GetURL() url, err := getFirstArgHost(c).GetURL()
if err != nil { if err != nil {
log.Fatal(err) log.Fatal(err)
} }

View File

@ -1 +0,0 @@
package commands

View File

@ -12,21 +12,26 @@ import (
"time" "time"
"github.com/codegangsta/cli" "github.com/codegangsta/cli"
"github.com/docker/machine/drivers"
"github.com/docker/machine/drivers/amazonec2/amz" "github.com/docker/machine/drivers/amazonec2/amz"
"github.com/docker/machine/log" "github.com/docker/machine/libmachine/drivers"
"github.com/docker/machine/ssh" "github.com/docker/machine/libmachine/log"
"github.com/docker/machine/state" "github.com/docker/machine/libmachine/mcnutils"
"github.com/docker/machine/utils" "github.com/docker/machine/libmachine/ssh"
"github.com/docker/machine/libmachine/state"
) )
const ( const (
driverName = "amazonec2" driverName = "amazonec2"
ipRange = "0.0.0.0/0"
machineSecurityGroupName = "docker-machine"
defaultAmiId = "ami-615cb725"
defaultRegion = "us-east-1" defaultRegion = "us-east-1"
defaultInstanceType = "t2.micro" defaultInstanceType = "t2.micro"
defaultRootSize = 16 defaultRootSize = 16
ipRange = "0.0.0.0/0" defaultZone = "a"
machineSecurityGroupName = "docker-machine" defaultSecurityGroup = machineSecurityGroupName
defaultSSHUser = "ubuntu"
defaultSpotPrice = "0.50"
) )
var ( var (
@ -65,7 +70,6 @@ type Driver struct {
func init() { func init() {
drivers.Register(driverName, &drivers.RegisteredDriver{ drivers.Register(driverName, &drivers.RegisteredDriver{
New: NewDriver,
GetCreateFlags: GetCreateFlags, GetCreateFlags: GetCreateFlags,
}) })
} }
@ -75,19 +79,16 @@ func GetCreateFlags() []cli.Flag {
cli.StringFlag{ cli.StringFlag{
Name: "amazonec2-access-key", Name: "amazonec2-access-key",
Usage: "AWS Access Key", Usage: "AWS Access Key",
Value: "",
EnvVar: "AWS_ACCESS_KEY_ID", EnvVar: "AWS_ACCESS_KEY_ID",
}, },
cli.StringFlag{ cli.StringFlag{
Name: "amazonec2-secret-key", Name: "amazonec2-secret-key",
Usage: "AWS Secret Key", Usage: "AWS Secret Key",
Value: "",
EnvVar: "AWS_SECRET_ACCESS_KEY", EnvVar: "AWS_SECRET_ACCESS_KEY",
}, },
cli.StringFlag{ cli.StringFlag{
Name: "amazonec2-session-token", Name: "amazonec2-session-token",
Usage: "AWS Session Token", Usage: "AWS Session Token",
Value: "",
EnvVar: "AWS_SESSION_TOKEN", EnvVar: "AWS_SESSION_TOKEN",
}, },
cli.StringFlag{ cli.StringFlag{
@ -104,25 +105,23 @@ func GetCreateFlags() []cli.Flag {
cli.StringFlag{ cli.StringFlag{
Name: "amazonec2-vpc-id", Name: "amazonec2-vpc-id",
Usage: "AWS VPC id", Usage: "AWS VPC id",
Value: "",
EnvVar: "AWS_VPC_ID", EnvVar: "AWS_VPC_ID",
}, },
cli.StringFlag{ cli.StringFlag{
Name: "amazonec2-zone", Name: "amazonec2-zone",
Usage: "AWS zone for instance (i.e. a,b,c,d,e)", Usage: "AWS zone for instance (i.e. a,b,c,d,e)",
Value: "a", Value: defaultZone,
EnvVar: "AWS_ZONE", EnvVar: "AWS_ZONE",
}, },
cli.StringFlag{ cli.StringFlag{
Name: "amazonec2-subnet-id", Name: "amazonec2-subnet-id",
Usage: "AWS VPC subnet id", Usage: "AWS VPC subnet id",
Value: "",
EnvVar: "AWS_SUBNET_ID", EnvVar: "AWS_SUBNET_ID",
}, },
cli.StringFlag{ cli.StringFlag{
Name: "amazonec2-security-group", Name: "amazonec2-security-group",
Usage: "AWS VPC security group", Usage: "AWS VPC security group",
Value: "docker-machine", Value: defaultSecurityGroup,
EnvVar: "AWS_SECURITY_GROUP", EnvVar: "AWS_SECURITY_GROUP",
}, },
cli.StringFlag{ cli.StringFlag{
@ -145,7 +144,7 @@ func GetCreateFlags() []cli.Flag {
cli.StringFlag{ cli.StringFlag{
Name: "amazonec2-ssh-user", Name: "amazonec2-ssh-user",
Usage: "set the name of the ssh user", Usage: "set the name of the ssh user",
Value: "ubuntu", Value: defaultSSHUser,
EnvVar: "AWS_SSH_USER", EnvVar: "AWS_SSH_USER",
}, },
cli.BoolFlag{ cli.BoolFlag{
@ -155,7 +154,7 @@ func GetCreateFlags() []cli.Flag {
cli.StringFlag{ cli.StringFlag{
Name: "amazonec2-spot-price", Name: "amazonec2-spot-price",
Usage: "AWS spot instance bid price (in dollar)", Usage: "AWS spot instance bid price (in dollar)",
Value: "0.50", Value: defaultSpotPrice,
}, },
cli.BoolFlag{ cli.BoolFlag{
Name: "amazonec2-private-address-only", Name: "amazonec2-private-address-only",
@ -172,13 +171,23 @@ func GetCreateFlags() []cli.Flag {
} }
} }
func NewDriver(machineName string, storePath string, caCert string, privateKey string) (drivers.Driver, error) { func NewDriver(hostName, storePath string) drivers.Driver {
id := generateId() id := generateId()
inner := drivers.NewBaseDriver(machineName, storePath, caCert, privateKey)
return &Driver{ return &Driver{
Id: id, Id: id,
BaseDriver: inner, AMI: defaultAmiId,
}, nil Region: defaultRegion,
InstanceType: defaultInstanceType,
RootSize: defaultRootSize,
Zone: defaultZone,
SecurityGroupName: defaultSecurityGroup,
SpotPrice: defaultSpotPrice,
BaseDriver: &drivers.BaseDriver{
SSHUser: defaultSSHUser,
MachineName: hostName,
StorePath: storePath,
},
}
} }
func (d *Driver) SetConfigFromFlags(flags drivers.DriverOptions) error { func (d *Driver) SetConfigFromFlags(flags drivers.DriverOptions) error {
@ -372,7 +381,7 @@ func (d *Driver) Create() error {
d.InstanceId = instance.InstanceId d.InstanceId = instance.InstanceId
log.Debug("waiting for ip address to become available") log.Debug("waiting for ip address to become available")
if err := utils.WaitFor(d.instanceIpAvailable); err != nil { if err := mcnutils.WaitFor(d.instanceIpAvailable); err != nil {
return err return err
} }
@ -536,7 +545,7 @@ func (d *Driver) instanceIsRunning() bool {
} }
func (d *Driver) waitForInstance() error { func (d *Driver) waitForInstance() error {
if err := utils.WaitFor(d.instanceIsRunning); err != nil { if err := mcnutils.WaitFor(d.instanceIsRunning); err != nil {
return err return err
} }
@ -622,7 +631,7 @@ func (d *Driver) configureSecurityGroup(groupName string) error {
securityGroup = group securityGroup = group
// wait until created (dat eventual consistency) // wait until created (dat eventual consistency)
log.Debugf("waiting for group (%s) to become available", group.GroupId) log.Debugf("waiting for group (%s) to become available", group.GroupId)
if err := utils.WaitFor(d.securityGroupAvailableFunc(group.GroupId)); err != nil { if err := mcnutils.WaitFor(d.securityGroupAvailableFunc(group.GroupId)); err != nil {
return err return err
} }
} }

View File

@ -98,10 +98,7 @@ func getTestDriver() (*Driver, error) {
} }
defer cleanup() defer cleanup()
d, err := NewDriver(machineTestName, storePath, machineTestCaCert, machineTestPrivateKey) d := NewDriver(machineTestName, storePath)
if err != nil {
return nil, err
}
d.SetConfigFromFlags(getDefaultTestDriverFlags()) d.SetConfigFromFlags(getDefaultTestDriverFlags())
drv := d.(*Driver) drv := d.(*Driver)
return drv, nil return drv, nil

View File

@ -1 +0,0 @@
package amz

View File

@ -1 +0,0 @@
package amz

View File

@ -1 +0,0 @@
package amz

View File

@ -1 +0,0 @@
package amz

View File

@ -1 +0,0 @@
package amz

View File

@ -1 +0,0 @@
package amz

View File

@ -9,8 +9,8 @@ import (
"net/url" "net/url"
"strconv" "strconv"
"github.com/docker/machine/log" "github.com/docker/machine/libmachine/log"
"github.com/docker/machine/utils" "github.com/docker/machine/libmachine/mcnutils"
awsauth "github.com/smartystreets/go-aws-auth" awsauth "github.com/smartystreets/go-aws-auth"
) )
@ -151,7 +151,7 @@ func NewEC2(auth Auth, region string) *EC2 {
func (e *EC2) awsApiCall(v url.Values) (*http.Response, error) { func (e *EC2) awsApiCall(v url.Values) (*http.Response, error) {
v.Set("Version", "2014-06-15") v.Set("Version", "2014-06-15")
log.Debug("Making AWS API call with values:") log.Debug("Making AWS API call with values:")
utils.DumpVal(v) mcnutils.DumpVal(v)
client := &http.Client{} client := &http.Client{}
finalEndpoint := fmt.Sprintf("%s?%s", e.Endpoint, v.Encode()) finalEndpoint := fmt.Sprintf("%s?%s", e.Endpoint, v.Encode())
req, err := http.NewRequest("GET", finalEndpoint, nil) req, err := http.NewRequest("GET", finalEndpoint, nil)

View File

@ -1 +0,0 @@
package amz

View File

@ -1 +0,0 @@
package amz

View File

@ -1 +0,0 @@
package amz

View File

@ -1 +0,0 @@
package amz

View File

@ -1 +0,0 @@
package amz

View File

@ -1 +0,0 @@
package amz

View File

@ -1 +0,0 @@
package amz

View File

@ -11,11 +11,11 @@ import (
"github.com/MSOpenTech/azure-sdk-for-go/clients/vmClient" "github.com/MSOpenTech/azure-sdk-for-go/clients/vmClient"
"github.com/codegangsta/cli" "github.com/codegangsta/cli"
"github.com/docker/machine/drivers" "github.com/docker/machine/libmachine/drivers"
"github.com/docker/machine/log" "github.com/docker/machine/libmachine/log"
"github.com/docker/machine/ssh" "github.com/docker/machine/libmachine/mcnutils"
"github.com/docker/machine/state" "github.com/docker/machine/libmachine/ssh"
"github.com/docker/machine/utils" "github.com/docker/machine/libmachine/state"
) )
type Driver struct { type Driver struct {
@ -31,9 +31,17 @@ type Driver struct {
DockerSwarmMasterPort int DockerSwarmMasterPort int
} }
const (
defaultDockerPort = 2376
defaultSwarmMasterPort = 3376
defaultLocation = "West US"
defaultSize = "Small"
defaultSSHPort = 22
defaultSSHUsername = "ubuntu"
)
func init() { func init() {
drivers.Register("azure", &drivers.RegisteredDriver{ drivers.Register("azure", &drivers.RegisteredDriver{
New: NewDriver,
GetCreateFlags: GetCreateFlags, GetCreateFlags: GetCreateFlags,
}) })
} }
@ -45,12 +53,12 @@ func GetCreateFlags() []cli.Flag {
cli.IntFlag{ cli.IntFlag{
Name: "azure-docker-port", Name: "azure-docker-port",
Usage: "Azure Docker port", Usage: "Azure Docker port",
Value: 2376, Value: defaultDockerPort,
}, },
cli.IntFlag{ cli.IntFlag{
Name: "azure-docker-swarm-master-port", Name: "azure-docker-swarm-master-port",
Usage: "Azure Docker Swarm master port", Usage: "Azure Docker Swarm master port",
Value: 3376, Value: defaultSwarmMasterPort,
}, },
cli.StringFlag{ cli.StringFlag{
EnvVar: "AZURE_IMAGE", EnvVar: "AZURE_IMAGE",
@ -61,7 +69,7 @@ func GetCreateFlags() []cli.Flag {
EnvVar: "AZURE_LOCATION", EnvVar: "AZURE_LOCATION",
Name: "azure-location", Name: "azure-location",
Usage: "Azure location", Usage: "Azure location",
Value: "West US", Value: defaultLocation,
}, },
cli.StringFlag{ cli.StringFlag{
Name: "azure-password", Name: "azure-password",
@ -76,12 +84,12 @@ func GetCreateFlags() []cli.Flag {
EnvVar: "AZURE_SIZE", EnvVar: "AZURE_SIZE",
Name: "azure-size", Name: "azure-size",
Usage: "Azure size", Usage: "Azure size",
Value: "Small", Value: defaultSize,
}, },
cli.IntFlag{ cli.IntFlag{
Name: "azure-ssh-port", Name: "azure-ssh-port",
Usage: "Azure SSH port", Usage: "Azure SSH port",
Value: 22, Value: defaultSSHPort,
}, },
cli.StringFlag{ cli.StringFlag{
@ -97,15 +105,25 @@ func GetCreateFlags() []cli.Flag {
cli.StringFlag{ cli.StringFlag{
Name: "azure-username", Name: "azure-username",
Usage: "Azure username", Usage: "Azure username",
Value: "ubuntu", Value: defaultSSHUsername,
}, },
} }
} }
func NewDriver(machineName string, storePath string, caCert string, privateKey string) (drivers.Driver, error) { func NewDriver(hostName, storePath string) drivers.Driver {
inner := drivers.NewBaseDriver(machineName, storePath, caCert, privateKey) d := &Driver{
d := &Driver{BaseDriver: inner} DockerPort: defaultDockerPort,
return d, nil DockerSwarmMasterPort: defaultSwarmMasterPort,
Location: defaultLocation,
Size: defaultSize,
BaseDriver: &drivers.BaseDriver{
SSHPort: defaultSSHPort,
SSHUser: defaultSSHUsername,
MachineName: hostName,
StorePath: storePath,
},
}
return d
} }
func (d *Driver) GetSSHHostname() (string, error) { func (d *Driver) GetSSHHostname() (string, error) {
@ -368,7 +386,7 @@ func (d *Driver) Kill() error {
} }
func generateVMName() string { func generateVMName() string {
randomID := utils.TruncateID(utils.GenerateRandomID()) randomID := mcnutils.TruncateID(mcnutils.GenerateRandomID())
return fmt.Sprintf("docker-host-%s", randomID) return fmt.Sprintf("docker-host-%s", randomID)
} }

View File

@ -1 +0,0 @@
package azure

View File

@ -8,10 +8,10 @@ import (
"code.google.com/p/goauth2/oauth" "code.google.com/p/goauth2/oauth"
"github.com/codegangsta/cli" "github.com/codegangsta/cli"
"github.com/digitalocean/godo" "github.com/digitalocean/godo"
"github.com/docker/machine/drivers" "github.com/docker/machine/libmachine/drivers"
"github.com/docker/machine/log" "github.com/docker/machine/libmachine/log"
"github.com/docker/machine/ssh" "github.com/docker/machine/libmachine/ssh"
"github.com/docker/machine/state" "github.com/docker/machine/libmachine/state"
) )
type Driver struct { type Driver struct {
@ -28,9 +28,14 @@ type Driver struct {
PrivateNetworking bool PrivateNetworking bool
} }
const (
defaultImage = "ubuntu-14-04-x64"
defaultRegion = "nyc3"
defaultSize = "512mb"
)
func init() { func init() {
drivers.Register("digitalocean", &drivers.RegisteredDriver{ drivers.Register("digitalocean", &drivers.RegisteredDriver{
New: NewDriver,
GetCreateFlags: GetCreateFlags, GetCreateFlags: GetCreateFlags,
}) })
} }
@ -54,19 +59,19 @@ func GetCreateFlags() []cli.Flag {
EnvVar: "DIGITALOCEAN_IMAGE", EnvVar: "DIGITALOCEAN_IMAGE",
Name: "digitalocean-image", Name: "digitalocean-image",
Usage: "Digital Ocean Image", Usage: "Digital Ocean Image",
Value: "ubuntu-14-04-x64", Value: defaultImage,
}, },
cli.StringFlag{ cli.StringFlag{
EnvVar: "DIGITALOCEAN_REGION", EnvVar: "DIGITALOCEAN_REGION",
Name: "digitalocean-region", Name: "digitalocean-region",
Usage: "Digital Ocean region", Usage: "Digital Ocean region",
Value: "nyc3", Value: defaultRegion,
}, },
cli.StringFlag{ cli.StringFlag{
EnvVar: "DIGITALOCEAN_SIZE", EnvVar: "DIGITALOCEAN_SIZE",
Name: "digitalocean-size", Name: "digitalocean-size",
Usage: "Digital Ocean size", Usage: "Digital Ocean size",
Value: "512mb", Value: defaultSize,
}, },
cli.BoolFlag{ cli.BoolFlag{
EnvVar: "DIGITALOCEAN_IPV6", EnvVar: "DIGITALOCEAN_IPV6",
@ -86,9 +91,16 @@ func GetCreateFlags() []cli.Flag {
} }
} }
func NewDriver(machineName string, storePath string, caCert string, privateKey string) (drivers.Driver, error) { func NewDriver(hostName, storePath string) *Driver {
inner := drivers.NewBaseDriver(machineName, storePath, caCert, privateKey) return &Driver{
return &Driver{BaseDriver: inner}, nil Image: defaultImage,
Size: defaultSize,
Region: defaultRegion,
BaseDriver: &drivers.BaseDriver{
MachineName: hostName,
StorePath: storePath,
},
}
} }
func (d *Driver) GetSSHHostname() (string, error) { func (d *Driver) GetSSHHostname() (string, error) {

View File

@ -1 +0,0 @@
package digitalocean

View File

@ -0,0 +1,65 @@
package driverfactory
import (
"fmt"
"github.com/docker/machine/drivers/amazonec2"
"github.com/docker/machine/drivers/azure"
"github.com/docker/machine/drivers/digitalocean"
"github.com/docker/machine/drivers/exoscale"
"github.com/docker/machine/drivers/generic"
"github.com/docker/machine/drivers/google"
"github.com/docker/machine/drivers/hyperv"
"github.com/docker/machine/drivers/none"
"github.com/docker/machine/drivers/openstack"
"github.com/docker/machine/drivers/rackspace"
"github.com/docker/machine/drivers/softlayer"
"github.com/docker/machine/drivers/virtualbox"
"github.com/docker/machine/drivers/vmwarefusion"
"github.com/docker/machine/drivers/vmwarevcloudair"
"github.com/docker/machine/drivers/vmwarevsphere"
"github.com/docker/machine/libmachine/drivers"
)
func NewDriver(driverName, hostName, storePath string) (drivers.Driver, error) {
var (
driver drivers.Driver
)
switch driverName {
case "virtualbox":
driver = virtualbox.NewDriver(hostName, storePath)
case "digitalocean":
driver = digitalocean.NewDriver(hostName, storePath)
case "amazonec2":
driver = amazonec2.NewDriver(hostName, storePath)
case "azure":
driver = azure.NewDriver(hostName, storePath)
case "exoscale":
driver = exoscale.NewDriver(hostName, storePath)
case "generic":
driver = generic.NewDriver(hostName, storePath)
case "google":
driver = google.NewDriver(hostName, storePath)
case "hyperv":
driver = hyperv.NewDriver(hostName, storePath)
case "openstack":
driver = openstack.NewDriver(hostName, storePath)
case "rackspace":
driver = rackspace.NewDriver(hostName, storePath)
case "softlayer":
driver = softlayer.NewDriver(hostName, storePath)
case "vmwarefusion":
driver = vmwarefusion.NewDriver(hostName, storePath)
case "vmwarevcloudair":
driver = vmwarevcloudair.NewDriver(hostName, storePath)
case "vmwarevsphere":
driver = vmwarevsphere.NewDriver(hostName, storePath)
case "none":
driver = none.NewDriver(hostName, storePath)
default:
return nil, fmt.Errorf("Driver %q not recognized", driverName)
}
return driver, nil
}

View File

@ -9,10 +9,10 @@ import (
"time" "time"
"github.com/codegangsta/cli" "github.com/codegangsta/cli"
"github.com/docker/machine/drivers" "github.com/docker/machine/libmachine/drivers"
"github.com/docker/machine/log" "github.com/docker/machine/libmachine/log"
"github.com/docker/machine/state" "github.com/docker/machine/libmachine/mcnutils"
"github.com/docker/machine/utils" "github.com/docker/machine/libmachine/state"
"github.com/pyr/egoscale/src/egoscale" "github.com/pyr/egoscale/src/egoscale"
) )
@ -31,9 +31,15 @@ type Driver struct {
Id string Id string
} }
const (
defaultInstanceProfile = "small"
defaultDiskSize = 50
defaultImage = "ubuntu-14.04"
defaultAvailabilityZone = "ch-gva-2"
)
func init() { func init() {
drivers.Register("exoscale", &drivers.RegisteredDriver{ drivers.Register("exoscale", &drivers.RegisteredDriver{
New: NewDriver,
GetCreateFlags: GetCreateFlags, GetCreateFlags: GetCreateFlags,
}) })
} }
@ -60,19 +66,19 @@ func GetCreateFlags() []cli.Flag {
cli.StringFlag{ cli.StringFlag{
EnvVar: "EXOSCALE_INSTANCE_PROFILE", EnvVar: "EXOSCALE_INSTANCE_PROFILE",
Name: "exoscale-instance-profile", Name: "exoscale-instance-profile",
Value: "small", Value: defaultInstanceProfile,
Usage: "exoscale instance profile (small, medium, large, ...)", Usage: "exoscale instance profile (small, medium, large, ...)",
}, },
cli.IntFlag{ cli.IntFlag{
EnvVar: "EXOSCALE_DISK_SIZE", EnvVar: "EXOSCALE_DISK_SIZE",
Name: "exoscale-disk-size", Name: "exoscale-disk-size",
Value: 50, Value: defaultDiskSize,
Usage: "exoscale disk size (10, 50, 100, 200, 400)", Usage: "exoscale disk size (10, 50, 100, 200, 400)",
}, },
cli.StringFlag{ cli.StringFlag{
EnvVar: "EXSOCALE_IMAGE", EnvVar: "EXSOCALE_IMAGE",
Name: "exoscale-image", Name: "exoscale-image",
Value: "ubuntu-14.04", Value: defaultImage,
Usage: "exoscale image template", Usage: "exoscale image template",
}, },
cli.StringSliceFlag{ cli.StringSliceFlag{
@ -84,15 +90,23 @@ func GetCreateFlags() []cli.Flag {
cli.StringFlag{ cli.StringFlag{
EnvVar: "EXOSCALE_AVAILABILITY_ZONE", EnvVar: "EXOSCALE_AVAILABILITY_ZONE",
Name: "exoscale-availability-zone", Name: "exoscale-availability-zone",
Value: "ch-gva-2", Value: defaultAvailabilityZone,
Usage: "exoscale availibility zone", Usage: "exoscale availibility zone",
}, },
} }
} }
func NewDriver(machineName string, storePath string, caCert string, privateKey string) (drivers.Driver, error) { func NewDriver(hostName, storePath string) drivers.Driver {
inner := drivers.NewBaseDriver(machineName, storePath, caCert, privateKey) return &Driver{
return &Driver{BaseDriver: inner}, nil InstanceProfile: defaultInstanceProfile,
DiskSize: defaultDiskSize,
Image: defaultImage,
AvailabilityZone: defaultAvailabilityZone,
BaseDriver: &drivers.BaseDriver{
MachineName: hostName,
StorePath: storePath,
},
}
} }
func (d *Driver) GetSSHHostname() (string, error) { func (d *Driver) GetSSHHostname() (string, error) {
@ -426,7 +440,7 @@ func (d *Driver) jobIsDone(client *egoscale.Client, jobid string) (bool, error)
func (d *Driver) waitForJob(client *egoscale.Client, jobid string) error { func (d *Driver) waitForJob(client *egoscale.Client, jobid string) error {
log.Infof("Waiting for job to complete...") log.Infof("Waiting for job to complete...")
return utils.WaitForSpecificOrError(func() (bool, error) { return mcnutils.WaitForSpecificOrError(func() (bool, error) {
return d.jobIsDone(client, jobid) return d.jobIsDone(client, jobid)
}, 60, 2*time.Second) }, 60, 2*time.Second)
} }

View File

@ -1 +0,0 @@
package exoscale

View File

@ -1,8 +1,8 @@
package fakedriver package fakedriver
import ( import (
"github.com/docker/machine/drivers" "github.com/docker/machine/libmachine/drivers"
"github.com/docker/machine/state" "github.com/docker/machine/libmachine/state"
) )
type FakeDriver struct { type FakeDriver struct {

View File

@ -1 +0,0 @@
package fakedriver

View File

@ -8,10 +8,10 @@ import (
"time" "time"
"github.com/codegangsta/cli" "github.com/codegangsta/cli"
"github.com/docker/machine/drivers" "github.com/docker/machine/libmachine/drivers"
"github.com/docker/machine/log" "github.com/docker/machine/libmachine/log"
"github.com/docker/machine/state" "github.com/docker/machine/libmachine/mcnutils"
"github.com/docker/machine/utils" "github.com/docker/machine/libmachine/state"
) )
type Driver struct { type Driver struct {
@ -20,12 +20,17 @@ type Driver struct {
} }
const ( const (
defaultSSHUser = "root"
defaultSSHPort = 22
defaultTimeout = 1 * time.Second defaultTimeout = 1 * time.Second
) )
var (
defaultSSHKey = filepath.Join(mcnutils.GetHomeDir(), ".ssh", "id_rsa")
)
func init() { func init() {
drivers.Register("generic", &drivers.RegisteredDriver{ drivers.Register("generic", &drivers.RegisteredDriver{
New: NewDriver,
GetCreateFlags: GetCreateFlags, GetCreateFlags: GetCreateFlags,
}) })
} }
@ -37,29 +42,35 @@ func GetCreateFlags() []cli.Flag {
cli.StringFlag{ cli.StringFlag{
Name: "generic-ip-address", Name: "generic-ip-address",
Usage: "IP Address of machine", Usage: "IP Address of machine",
Value: "",
}, },
cli.StringFlag{ cli.StringFlag{
Name: "generic-ssh-user", Name: "generic-ssh-user",
Usage: "SSH user", Usage: "SSH user",
Value: "root", Value: defaultSSHUser,
}, },
cli.StringFlag{ cli.StringFlag{
Name: "generic-ssh-key", Name: "generic-ssh-key",
Usage: "SSH private key path", Usage: "SSH private key path",
Value: filepath.Join(utils.GetHomeDir(), ".ssh", "id_rsa"), Value: defaultSSHKey,
}, },
cli.IntFlag{ cli.IntFlag{
Name: "generic-ssh-port", Name: "generic-ssh-port",
Usage: "SSH port", Usage: "SSH port",
Value: 22, Value: defaultSSHPort,
}, },
} }
} }
func NewDriver(machineName string, storePath string, caCert string, privateKey string) (drivers.Driver, error) { func NewDriver(hostName, storePath string) drivers.Driver {
inner := drivers.NewBaseDriver(machineName, storePath, caCert, privateKey) return &Driver{
return &Driver{BaseDriver: inner}, nil SSHKey: defaultSSHKey,
BaseDriver: &drivers.BaseDriver{
SSHUser: defaultSSHUser,
SSHPort: defaultSSHPort,
MachineName: hostName,
StorePath: storePath,
},
}
} }
func (d *Driver) DriverName() string { func (d *Driver) DriverName() string {
@ -98,7 +109,7 @@ func (d *Driver) PreCreateCheck() error {
func (d *Driver) Create() error { func (d *Driver) Create() error {
log.Infof("Importing SSH key...") log.Infof("Importing SSH key...")
if err := utils.CopyFile(d.SSHKey, d.GetSSHKeyPath()); err != nil { if err := mcnutils.CopyFile(d.SSHKey, d.GetSSHKeyPath()); err != nil {
return fmt.Errorf("unable to copy ssh key: %s", err) return fmt.Errorf("unable to copy ssh key: %s", err)
} }

View File

@ -1 +0,0 @@
package generic

View File

@ -12,7 +12,7 @@ import (
"time" "time"
"code.google.com/p/goauth2/oauth" "code.google.com/p/goauth2/oauth"
"github.com/docker/machine/log" "github.com/docker/machine/libmachine/log"
raw "google.golang.org/api/compute/v1" raw "google.golang.org/api/compute/v1"
) )

View File

@ -1 +0,0 @@
package google

View File

@ -8,8 +8,8 @@ import (
"strings" "strings"
"time" "time"
"github.com/docker/machine/log" "github.com/docker/machine/libmachine/log"
"github.com/docker/machine/ssh" "github.com/docker/machine/libmachine/ssh"
raw "google.golang.org/api/compute/v1" raw "google.golang.org/api/compute/v1"
) )

View File

@ -1 +0,0 @@
package google

View File

@ -5,10 +5,10 @@ import (
"strings" "strings"
"github.com/codegangsta/cli" "github.com/codegangsta/cli"
"github.com/docker/machine/drivers" "github.com/docker/machine/libmachine/drivers"
"github.com/docker/machine/log" "github.com/docker/machine/libmachine/log"
"github.com/docker/machine/ssh" "github.com/docker/machine/libmachine/ssh"
"github.com/docker/machine/state" "github.com/docker/machine/libmachine/state"
) )
// Driver is a struct compatible with the docker.hosts.drivers.Driver interface. // Driver is a struct compatible with the docker.hosts.drivers.Driver interface.
@ -26,9 +26,17 @@ type Driver struct {
Tags []string Tags []string
} }
const (
defaultZone = "us-central1-a"
defaultUser = "docker-user"
defaultMachineType = "f1-micro"
defaultScopes = "https://www.googleapis.com/auth/devstorage.read_only,https://www.googleapis.com/auth/logging.write"
defaultDiskType = "pd-standard"
defaultDiskSize = 10
)
func init() { func init() {
drivers.Register("google", &drivers.RegisteredDriver{ drivers.Register("google", &drivers.RegisteredDriver{
New: NewDriver,
GetCreateFlags: GetCreateFlags, GetCreateFlags: GetCreateFlags,
}) })
} }
@ -40,19 +48,19 @@ func GetCreateFlags() []cli.Flag {
cli.StringFlag{ cli.StringFlag{
Name: "google-zone", Name: "google-zone",
Usage: "GCE Zone", Usage: "GCE Zone",
Value: "us-central1-a", Value: defaultZone,
EnvVar: "GOOGLE_ZONE", EnvVar: "GOOGLE_ZONE",
}, },
cli.StringFlag{ cli.StringFlag{
Name: "google-machine-type", Name: "google-machine-type",
Usage: "GCE Machine Type", Usage: "GCE Machine Type",
Value: "f1-micro", Value: defaultMachineType,
EnvVar: "GOOGLE_MACHINE_TYPE", EnvVar: "GOOGLE_MACHINE_TYPE",
}, },
cli.StringFlag{ cli.StringFlag{
Name: "google-username", Name: "google-username",
Usage: "GCE User Name", Usage: "GCE User Name",
Value: "docker-user", Value: defaultUser,
EnvVar: "GOOGLE_USERNAME", EnvVar: "GOOGLE_USERNAME",
}, },
cli.StringFlag{ cli.StringFlag{
@ -68,19 +76,19 @@ func GetCreateFlags() []cli.Flag {
cli.StringFlag{ cli.StringFlag{
Name: "google-scopes", Name: "google-scopes",
Usage: "GCE Scopes (comma-separated if multiple scopes)", Usage: "GCE Scopes (comma-separated if multiple scopes)",
Value: "https://www.googleapis.com/auth/devstorage.read_only,https://www.googleapis.com/auth/logging.write", Value: defaultScopes,
EnvVar: "GOOGLE_SCOPES", EnvVar: "GOOGLE_SCOPES",
}, },
cli.IntFlag{ cli.IntFlag{
Name: "google-disk-size", Name: "google-disk-size",
Usage: "GCE Instance Disk Size (in GB)", Usage: "GCE Instance Disk Size (in GB)",
Value: 10, Value: defaultDiskSize,
EnvVar: "GOOGLE_DISK_SIZE", EnvVar: "GOOGLE_DISK_SIZE",
}, },
cli.StringFlag{ cli.StringFlag{
Name: "google-disk-type", Name: "google-disk-type",
Usage: "GCE Instance Disk type", Usage: "GCE Instance Disk type",
Value: "pd-standard", Value: defaultDiskType,
EnvVar: "GOOGLE_DISK_TYPE", EnvVar: "GOOGLE_DISK_TYPE",
}, },
cli.StringFlag{ cli.StringFlag{
@ -102,9 +110,19 @@ func GetCreateFlags() []cli.Flag {
} }
// NewDriver creates a Driver with the specified storePath. // NewDriver creates a Driver with the specified storePath.
func NewDriver(machineName string, storePath string, caCert string, privateKey string) (drivers.Driver, error) { func NewDriver(machineName string, storePath string) *Driver {
inner := drivers.NewBaseDriver(machineName, storePath, caCert, privateKey) return &Driver{
return &Driver{BaseDriver: inner}, nil Zone: defaultZone,
DiskType: defaultDiskType,
DiskSize: defaultDiskSize,
MachineType: defaultMachineType,
Scopes: defaultScopes,
BaseDriver: &drivers.BaseDriver{
SSHUser: defaultUser,
MachineName: machineName,
StorePath: storePath,
},
}
} }
// GetSSHHostname returns hostname for use with ssh // GetSSHHostname returns hostname for use with ssh

View File

@ -1 +0,0 @@
package google

View File

@ -1,2 +1,472 @@
// this is empty to allow builds on non-windows platforms
package hyperv package hyperv
import (
"archive/tar"
"bytes"
"fmt"
"io/ioutil"
"os"
"time"
"github.com/codegangsta/cli"
"github.com/docker/machine/libmachine/drivers"
"github.com/docker/machine/libmachine/log"
"github.com/docker/machine/libmachine/mcnutils"
"github.com/docker/machine/libmachine/ssh"
"github.com/docker/machine/libmachine/state"
)
type Driver struct {
*drivers.BaseDriver
boot2DockerURL string
boot2DockerLoc string
vSwitch string
diskImage string
DiskSize int
MemSize int
}
const (
defaultDiskSize = 20000
defaultMemory = 1024
)
func init() {
drivers.Register("hyper-v", &drivers.RegisteredDriver{
GetCreateFlags: GetCreateFlags,
})
}
func NewDriver(hostName, storePath string) drivers.Driver {
return &Driver{
DiskSize: defaultDiskSize,
MemSize: defaultMemory,
BaseDriver: &drivers.BaseDriver{
MachineName: hostName,
StorePath: storePath,
},
}
}
// GetCreateFlags registers the flags this driver adds to
// "docker hosts create"
func GetCreateFlags() []cli.Flag {
return []cli.Flag{
cli.StringFlag{
Name: "hyper-v-boot2docker-url",
Usage: "Hyper-V URL of the boot2docker image. Defaults to the latest available version.",
},
cli.StringFlag{
Name: "hyper-v-boot2docker-location",
Usage: "Hyper-V local boot2docker iso. Overrides URL.",
},
cli.StringFlag{
Name: "hyper-v-virtual-switch",
Usage: "Hyper-V virtual switch name. Defaults to first found.",
},
cli.IntFlag{
Name: "hyper-v-disk-size",
Usage: "Hyper-V disk size for host in MB.",
Value: defaultDiskSize,
},
cli.IntFlag{
Name: "hyper-v-memory",
Usage: "Hyper-V memory size for host in MB.",
Value: defaultMemory,
},
}
}
func (d *Driver) SetConfigFromFlags(flags drivers.DriverOptions) error {
d.boot2DockerURL = flags.String("hyper-v-boot2docker-url")
d.boot2DockerLoc = flags.String("hyper-v-boot2docker-location")
d.vSwitch = flags.String("hyper-v-virtual-switch")
d.DiskSize = flags.Int("hyper-v-disk-size")
d.MemSize = flags.Int("hyper-v-memory")
d.SwarmMaster = flags.Bool("swarm-master")
d.SwarmHost = flags.String("swarm-host")
d.SwarmDiscovery = flags.String("swarm-discovery")
d.SSHUser = "docker"
d.SSHPort = 22
return nil
}
func (d *Driver) GetSSHHostname() (string, error) {
return d.GetIP()
}
func (d *Driver) GetSSHUsername() string {
if d.SSHUser == "" {
d.SSHUser = "docker"
}
return d.SSHUser
}
func (d *Driver) DriverName() string {
return "hyper-v"
}
func (d *Driver) PreCreateCheck() error {
return nil
}
func (d *Driver) GetURL() (string, error) {
ip, err := d.GetIP()
if err != nil {
return "", err
}
if ip == "" {
return "", nil
}
return fmt.Sprintf("tcp://%s:2376", ip), nil
}
func (d *Driver) GetState() (state.State, error) {
command := []string{
"(",
"Get-VM",
"-Name", d.MachineName,
").state"}
stdout, err := execute(command)
if err != nil {
return state.None, fmt.Errorf("Failed to find the VM status")
}
resp := parseStdout(stdout)
if len(resp) < 1 {
return state.None, nil
}
switch resp[0] {
case "Running":
return state.Running, nil
case "Off":
return state.Stopped, nil
}
return state.None, nil
}
func (d *Driver) Create() error {
err := hypervAvailable()
if err != nil {
return err
}
d.setMachineNameIfNotSet()
b2dutils := mcnutils.NewB2dUtils("", "", d.StorePath)
if err := b2dutils.CopyIsoToMachineDir(d.boot2DockerURL, d.MachineName); err != nil {
return err
}
log.Infof("Creating SSH key...")
if err := ssh.GenerateSSHKey(d.GetSSHKeyPath()); err != nil {
return err
}
log.Infof("Creating VM...")
virtualSwitch, err := d.chooseVirtualSwitch()
if err != nil {
return err
}
err = d.generateDiskImage()
if err != nil {
return err
}
command := []string{
"New-VM",
"-Name", d.MachineName,
"-Path", fmt.Sprintf("'%s'", d.ResolveStorePath(".")),
"-MemoryStartupBytes", fmt.Sprintf("%dMB", d.MemSize)}
_, err = execute(command)
if err != nil {
return err
}
command = []string{
"Set-VMDvdDrive",
"-VMName", d.MachineName,
"-Path", fmt.Sprintf("'%s'", d.ResolveStorePath("boot2docker.iso"))}
_, err = execute(command)
if err != nil {
return err
}
command = []string{
"Add-VMHardDiskDrive",
"-VMName", d.MachineName,
"-Path", fmt.Sprintf("'%s'", d.diskImage)}
_, err = execute(command)
if err != nil {
return err
}
command = []string{
"Connect-VMNetworkAdapter",
"-VMName", d.MachineName,
"-SwitchName", fmt.Sprintf("'%s'", virtualSwitch)}
_, err = execute(command)
if err != nil {
return err
}
log.Infof("Starting VM...")
if err := d.Start(); err != nil {
return err
}
return nil
}
func (d *Driver) chooseVirtualSwitch() (string, error) {
if d.vSwitch != "" {
return d.vSwitch, nil
}
command := []string{
"@(Get-VMSwitch).Name"}
stdout, err := execute(command)
if err != nil {
return "", err
}
switches := parseStdout(stdout)
if len(switches) > 0 {
log.Infof("Using switch %s", switches[0])
return switches[0], nil
}
return "", fmt.Errorf("no vswitch found")
}
func (d *Driver) wait() error {
log.Infof("Waiting for host to start...")
for {
ip, _ := d.GetIP()
if ip != "" {
break
}
time.Sleep(1 * time.Second)
}
return nil
}
func (d *Driver) Start() error {
command := []string{
"Start-VM",
"-Name", d.MachineName}
_, err := execute(command)
if err != nil {
return err
}
if err := d.wait(); err != nil {
return err
}
d.IPAddress, err = d.GetIP()
return err
}
func (d *Driver) Stop() error {
command := []string{
"Stop-VM",
"-Name", d.MachineName}
_, err := execute(command)
if err != nil {
return err
}
for {
s, err := d.GetState()
if err != nil {
return err
}
if s == state.Running {
time.Sleep(1 * time.Second)
} else {
break
}
}
d.IPAddress = ""
return nil
}
func (d *Driver) Remove() error {
s, err := d.GetState()
if err != nil {
return err
}
if s == state.Running {
if err := d.Kill(); err != nil {
return err
}
}
command := []string{
"Remove-VM",
"-Name", d.MachineName,
"-Force"}
_, err = execute(command)
return err
}
func (d *Driver) Restart() error {
err := d.Stop()
if err != nil {
return err
}
return d.Start()
}
func (d *Driver) Kill() error {
command := []string{
"Stop-VM",
"-Name", d.MachineName,
"-TurnOff"}
_, err := execute(command)
if err != nil {
return err
}
for {
s, err := d.GetState()
if err != nil {
return err
}
if s == state.Running {
time.Sleep(1 * time.Second)
} else {
break
}
}
d.IPAddress = ""
return nil
}
func (d *Driver) setMachineNameIfNotSet() {
if d.MachineName == "" {
d.MachineName = fmt.Sprintf("docker-machine-unknown")
}
}
func (d *Driver) GetIP() (string, error) {
command := []string{
"((",
"Get-VM",
"-Name", d.MachineName,
").networkadapters[0]).ipaddresses[0]"}
stdout, err := execute(command)
if err != nil {
return "", err
}
resp := parseStdout(stdout)
if len(resp) < 1 {
return "", fmt.Errorf("IP not found")
}
return resp[0], nil
}
func (d *Driver) publicSSHKeyPath() string {
return d.GetSSHKeyPath() + ".pub"
}
func (d *Driver) generateDiskImage() error {
// Create a small fixed vhd, put the tar in,
// convert to dynamic, then resize
d.diskImage = d.ResolveStorePath("disk.vhd")
fixed := d.ResolveStorePath("fixed.vhd")
log.Infof("Creating VHD")
command := []string{
"New-VHD",
"-Path", fmt.Sprintf("'%s'", fixed),
"-SizeBytes", "10MB",
"-Fixed"}
_, err := execute(command)
if err != nil {
return err
}
tarBuf, err := d.generateTar()
if err != nil {
return err
}
file, err := os.OpenFile(fixed, os.O_WRONLY, 0644)
if err != nil {
return err
}
defer file.Close()
file.Seek(0, os.SEEK_SET)
_, err = file.Write(tarBuf.Bytes())
if err != nil {
return err
}
file.Close()
command = []string{
"Convert-VHD",
"-Path", fmt.Sprintf("'%s'", fixed),
"-DestinationPath", fmt.Sprintf("'%s'", d.diskImage),
"-VHDType", "Dynamic"}
_, err = execute(command)
if err != nil {
return err
}
command = []string{
"Resize-VHD",
"-Path", fmt.Sprintf("'%s'", d.diskImage),
"-SizeBytes", fmt.Sprintf("%dMB", d.DiskSize)}
_, err = execute(command)
if err != nil {
return err
}
return err
}
// Make a boot2docker VM disk image.
// See https://github.com/boot2docker/boot2docker/blob/master/rootfs/rootfs/etc/rc.d/automount
func (d *Driver) generateTar() (*bytes.Buffer, error) {
magicString := "boot2docker, please format-me"
buf := new(bytes.Buffer)
tw := tar.NewWriter(buf)
// magicString first so the automount script knows to format the disk
file := &tar.Header{Name: magicString, Size: int64(len(magicString))}
if err := tw.WriteHeader(file); err != nil {
return nil, err
}
if _, err := tw.Write([]byte(magicString)); err != nil {
return nil, err
}
// .ssh/key.pub => authorized_keys
file = &tar.Header{Name: ".ssh", Typeflag: tar.TypeDir, Mode: 0700}
if err := tw.WriteHeader(file); err != nil {
return nil, err
}
pubKey, err := ioutil.ReadFile(d.publicSSHKeyPath())
if err != nil {
return nil, err
}
file = &tar.Header{Name: ".ssh/authorized_keys", Size: int64(len(pubKey)), Mode: 0644}
if err := tw.WriteHeader(file); err != nil {
return nil, err
}
if _, err := tw.Write([]byte(pubKey)); err != nil {
return nil, err
}
file = &tar.Header{Name: ".ssh/authorized_keys2", Size: int64(len(pubKey)), Mode: 0644}
if err := tw.WriteHeader(file); err != nil {
return nil, err
}
if _, err := tw.Write([]byte(pubKey)); err != nil {
return nil, err
}
if err := tw.Close(); err != nil {
return nil, err
}
return buf, nil
}

View File

@ -1 +0,0 @@
package hyperv

View File

@ -1,509 +0,0 @@
package hyperv
import (
"archive/tar"
"bytes"
"fmt"
"io/ioutil"
"os"
"path/filepath"
"time"
"github.com/codegangsta/cli"
"github.com/docker/machine/drivers"
"github.com/docker/machine/log"
"github.com/docker/machine/ssh"
"github.com/docker/machine/state"
"github.com/docker/machine/utils"
)
type Driver struct {
*drivers.BaseDriver
boot2DockerURL string
boot2DockerLoc string
vSwitch string
diskImage string
diskSize int
memSize int
}
func init() {
drivers.Register("hyper-v", &drivers.RegisteredDriver{
New: NewDriver,
GetCreateFlags: GetCreateFlags,
})
}
// GetCreateFlags registers the flags this driver adds to
// "docker hosts create"
func GetCreateFlags() []cli.Flag {
return []cli.Flag{
cli.StringFlag{
Name: "hyper-v-boot2docker-url",
Usage: "Hyper-V URL of the boot2docker image. Defaults to the latest available version.",
},
cli.StringFlag{
Name: "hyper-v-boot2docker-location",
Usage: "Hyper-V local boot2docker iso. Overrides URL.",
},
cli.StringFlag{
Name: "hyper-v-virtual-switch",
Usage: "Hyper-V virtual switch name. Defaults to first found.",
},
cli.IntFlag{
Name: "hyper-v-disk-size",
Usage: "Hyper-V disk size for host in MB.",
Value: 20000,
},
cli.IntFlag{
Name: "hyper-v-memory",
Usage: "Hyper-V memory size for host in MB.",
Value: 1024,
},
}
}
func (d *Driver) SetConfigFromFlags(flags drivers.DriverOptions) error {
d.boot2DockerURL = flags.String("hyper-v-boot2docker-url")
d.boot2DockerLoc = flags.String("hyper-v-boot2docker-location")
d.vSwitch = flags.String("hyper-v-virtual-switch")
d.diskSize = flags.Int("hyper-v-disk-size")
d.memSize = flags.Int("hyper-v-memory")
d.SwarmMaster = flags.Bool("swarm-master")
d.SwarmHost = flags.String("swarm-host")
d.SwarmDiscovery = flags.String("swarm-discovery")
d.SSHUser = "docker"
d.SSHPort = 22
return nil
}
func NewDriver(machineName string, storePath string, caCert string, privateKey string) (drivers.Driver, error) {
inner := drivers.NewBaseDriver(machineName, storePath, caCert, privateKey)
return &Driver{BaseDriver: inner}, nil
}
func (d *Driver) GetSSHHostname() (string, error) {
return d.GetIP()
}
func (d *Driver) GetSSHUsername() string {
if d.SSHUser == "" {
d.SSHUser = "docker"
}
return d.SSHUser
}
func (d *Driver) DriverName() string {
return "hyper-v"
}
func (d *Driver) PreCreateCheck() error {
return nil
}
func (d *Driver) GetURL() (string, error) {
ip, err := d.GetIP()
if err != nil {
return "", err
}
if ip == "" {
return "", nil
}
return fmt.Sprintf("tcp://%s:2376", ip), nil
}
func (d *Driver) GetState() (state.State, error) {
command := []string{
"(",
"Get-VM",
"-Name", d.MachineName,
").state"}
stdout, err := execute(command)
if err != nil {
return state.None, fmt.Errorf("Failed to find the VM status")
}
resp := parseStdout(stdout)
if len(resp) < 1 {
return state.None, nil
}
switch resp[0] {
case "Running":
return state.Running, nil
case "Off":
return state.Stopped, nil
}
return state.None, nil
}
func (d *Driver) Create() error {
err := hypervAvailable()
if err != nil {
return err
}
d.setMachineNameIfNotSet()
var isoURL string
b2dutils := utils.NewB2dUtils("", "")
if d.boot2DockerLoc == "" {
if d.boot2DockerURL != "" {
isoURL = d.boot2DockerURL
log.Infof("Downloading boot2docker.iso from %s...", isoURL)
if err := b2dutils.DownloadISO(d.ResolveStorePath("."), "boot2docker.iso", isoURL); err != nil {
return err
}
} else {
// todo: check latest release URL, download if it's new
// until then always use "latest"
isoURL, err = b2dutils.GetLatestBoot2DockerReleaseURL()
if err != nil {
log.Warnf("Unable to check for the latest release: %s", err)
}
// todo: use real constant for .docker
rootPath := filepath.Join(utils.GetDockerDir())
imgPath := filepath.Join(rootPath, "images")
commonIsoPath := filepath.Join(imgPath, "boot2docker.iso")
if _, err := os.Stat(commonIsoPath); os.IsNotExist(err) {
log.Infof("Downloading boot2docker.iso to %s...", commonIsoPath)
// just in case boot2docker.iso has been manually deleted
if _, err := os.Stat(imgPath); os.IsNotExist(err) {
if err := os.Mkdir(imgPath, 0700); err != nil {
return err
}
}
if err := b2dutils.DownloadISO(imgPath, "boot2docker.iso", isoURL); err != nil {
return err
}
}
isoDest := d.ResolveStorePath("boot2docker.iso")
if err := utils.CopyFile(commonIsoPath, isoDest); err != nil {
return err
}
}
} else {
if err := utils.CopyFile(d.boot2DockerLoc, d.ResolveStorePath("boot2docker.iso")); err != nil {
return err
}
}
log.Infof("Creating SSH key...")
if err := ssh.GenerateSSHKey(d.GetSSHKeyPath()); err != nil {
return err
}
log.Infof("Creating VM...")
virtualSwitch, err := d.chooseVirtualSwitch()
if err != nil {
return err
}
err = d.generateDiskImage()
if err != nil {
return err
}
command := []string{
"New-VM",
"-Name", d.MachineName,
"-Path", fmt.Sprintf("'%s'", d.ResolveStorePath(".")),
"-MemoryStartupBytes", fmt.Sprintf("%dMB", d.memSize)}
_, err = execute(command)
if err != nil {
return err
}
command = []string{
"Set-VMDvdDrive",
"-VMName", d.MachineName,
"-Path", fmt.Sprintf("'%s'", d.ResolveStorePath("boot2docker.iso"))}
_, err = execute(command)
if err != nil {
return err
}
command = []string{
"Add-VMHardDiskDrive",
"-VMName", d.MachineName,
"-Path", fmt.Sprintf("'%s'", d.diskImage)}
_, err = execute(command)
if err != nil {
return err
}
command = []string{
"Connect-VMNetworkAdapter",
"-VMName", d.MachineName,
"-SwitchName", fmt.Sprintf("'%s'", virtualSwitch)}
_, err = execute(command)
if err != nil {
return err
}
log.Infof("Starting VM...")
if err := d.Start(); err != nil {
return err
}
return nil
}
func (d *Driver) chooseVirtualSwitch() (string, error) {
if d.vSwitch != "" {
return d.vSwitch, nil
}
command := []string{
"@(Get-VMSwitch).Name"}
stdout, err := execute(command)
if err != nil {
return "", err
}
switches := parseStdout(stdout)
if len(switches) > 0 {
log.Infof("Using switch %s", switches[0])
return switches[0], nil
}
return "", fmt.Errorf("no vswitch found")
}
func (d *Driver) wait() error {
log.Infof("Waiting for host to start...")
for {
ip, _ := d.GetIP()
if ip != "" {
break
}
time.Sleep(1 * time.Second)
}
return nil
}
func (d *Driver) Start() error {
command := []string{
"Start-VM",
"-Name", d.MachineName}
_, err := execute(command)
if err != nil {
return err
}
if err := d.wait(); err != nil {
return err
}
d.IPAddress, err = d.GetIP()
return err
}
func (d *Driver) Stop() error {
command := []string{
"Stop-VM",
"-Name", d.MachineName}
_, err := execute(command)
if err != nil {
return err
}
for {
s, err := d.GetState()
if err != nil {
return err
}
if s == state.Running {
time.Sleep(1 * time.Second)
} else {
break
}
}
d.IPAddress = ""
return nil
}
func (d *Driver) Remove() error {
s, err := d.GetState()
if err != nil {
return err
}
if s == state.Running {
if err := d.Kill(); err != nil {
return err
}
}
command := []string{
"Remove-VM",
"-Name", d.MachineName,
"-Force"}
_, err = execute(command)
return err
}
func (d *Driver) Restart() error {
err := d.Stop()
if err != nil {
return err
}
return d.Start()
}
func (d *Driver) Kill() error {
command := []string{
"Stop-VM",
"-Name", d.MachineName,
"-TurnOff"}
_, err := execute(command)
if err != nil {
return err
}
for {
s, err := d.GetState()
if err != nil {
return err
}
if s == state.Running {
time.Sleep(1 * time.Second)
} else {
break
}
}
d.IPAddress = ""
return nil
}
func (d *Driver) setMachineNameIfNotSet() {
if d.MachineName == "" {
d.MachineName = fmt.Sprintf("docker-machine-unknown")
}
}
func (d *Driver) GetIP() (string, error) {
command := []string{
"((",
"Get-VM",
"-Name", d.MachineName,
").networkadapters[0]).ipaddresses[0]"}
stdout, err := execute(command)
if err != nil {
return "", err
}
resp := parseStdout(stdout)
if len(resp) < 1 {
return "", fmt.Errorf("IP not found")
}
return resp[0], nil
}
func (d *Driver) publicSSHKeyPath() string {
return d.GetSSHKeyPath() + ".pub"
}
func (d *Driver) generateDiskImage() error {
// Create a small fixed vhd, put the tar in,
// convert to dynamic, then resize
d.diskImage = d.ResolveStorePath("disk.vhd")
fixed := d.ResolveStorePath("fixed.vhd")
log.Infof("Creating VHD")
command := []string{
"New-VHD",
"-Path", fmt.Sprintf("'%s'", fixed),
"-SizeBytes", "10MB",
"-Fixed"}
_, err := execute(command)
if err != nil {
return err
}
tarBuf, err := d.generateTar()
if err != nil {
return err
}
file, err := os.OpenFile(fixed, os.O_WRONLY, 0644)
if err != nil {
return err
}
defer file.Close()
file.Seek(0, os.SEEK_SET)
_, err = file.Write(tarBuf.Bytes())
if err != nil {
return err
}
file.Close()
command = []string{
"Convert-VHD",
"-Path", fmt.Sprintf("'%s'", fixed),
"-DestinationPath", fmt.Sprintf("'%s'", d.diskImage),
"-VHDType", "Dynamic"}
_, err = execute(command)
if err != nil {
return err
}
command = []string{
"Resize-VHD",
"-Path", fmt.Sprintf("'%s'", d.diskImage),
"-SizeBytes", fmt.Sprintf("%dMB", d.diskSize)}
_, err = execute(command)
if err != nil {
return err
}
return err
}
// Make a boot2docker VM disk image.
// See https://github.com/boot2docker/boot2docker/blob/master/rootfs/rootfs/etc/rc.d/automount
func (d *Driver) generateTar() (*bytes.Buffer, error) {
magicString := "boot2docker, please format-me"
buf := new(bytes.Buffer)
tw := tar.NewWriter(buf)
// magicString first so the automount script knows to format the disk
file := &tar.Header{Name: magicString, Size: int64(len(magicString))}
if err := tw.WriteHeader(file); err != nil {
return nil, err
}
if _, err := tw.Write([]byte(magicString)); err != nil {
return nil, err
}
// .ssh/key.pub => authorized_keys
file = &tar.Header{Name: ".ssh", Typeflag: tar.TypeDir, Mode: 0700}
if err := tw.WriteHeader(file); err != nil {
return nil, err
}
pubKey, err := ioutil.ReadFile(d.publicSSHKeyPath())
if err != nil {
return nil, err
}
file = &tar.Header{Name: ".ssh/authorized_keys", Size: int64(len(pubKey)), Mode: 0644}
if err := tw.WriteHeader(file); err != nil {
return nil, err
}
if _, err := tw.Write([]byte(pubKey)); err != nil {
return nil, err
}
file = &tar.Header{Name: ".ssh/authorized_keys2", Size: int64(len(pubKey)), Mode: 0644}
if err := tw.WriteHeader(file); err != nil {
return nil, err
}
if _, err := tw.Write([]byte(pubKey)); err != nil {
return nil, err
}
if err := tw.Close(); err != nil {
return nil, err
}
return buf, nil
}

View File

@ -9,7 +9,7 @@ import (
"path/filepath" "path/filepath"
"strings" "strings"
"github.com/docker/machine/log" "github.com/docker/machine/libmachine/log"
) )
var powershell string var powershell string

View File

@ -5,8 +5,8 @@ import (
neturl "net/url" neturl "net/url"
"github.com/codegangsta/cli" "github.com/codegangsta/cli"
"github.com/docker/machine/drivers" "github.com/docker/machine/libmachine/drivers"
"github.com/docker/machine/state" "github.com/docker/machine/libmachine/state"
) )
// Driver is the driver used when no driver is selected. It is used to // Driver is the driver used when no driver is selected. It is used to
@ -19,7 +19,6 @@ type Driver struct {
func init() { func init() {
drivers.Register("none", &drivers.RegisteredDriver{ drivers.Register("none", &drivers.RegisteredDriver{
New: NewDriver,
GetCreateFlags: GetCreateFlags, GetCreateFlags: GetCreateFlags,
}) })
} }
@ -34,9 +33,13 @@ func GetCreateFlags() []cli.Flag {
} }
} }
func NewDriver(machineName string, storePath string, caCert string, privateKey string) (drivers.Driver, error) { func NewDriver(hostName, storePath string) *Driver {
inner := drivers.NewBaseDriver(machineName, storePath, caCert, privateKey) return &Driver{
return &Driver{inner, ""}, nil BaseDriver: &drivers.BaseDriver{
MachineName: hostName,
StorePath: storePath,
},
}
} }
func (d *Driver) Create() error { func (d *Driver) Create() error {

View File

@ -1 +0,0 @@
package none

View File

@ -6,9 +6,9 @@ import (
"net/http" "net/http"
"time" "time"
"github.com/docker/machine/log" "github.com/docker/machine/libmachine/log"
"github.com/docker/machine/utils" "github.com/docker/machine/libmachine/mcnutils"
"github.com/docker/machine/version" "github.com/docker/machine/libmachine/version"
"github.com/rackspace/gophercloud" "github.com/rackspace/gophercloud"
"github.com/rackspace/gophercloud/openstack" "github.com/rackspace/gophercloud/openstack"
"github.com/rackspace/gophercloud/openstack/compute/v2/extensions/keypairs" "github.com/rackspace/gophercloud/openstack/compute/v2/extensions/keypairs"
@ -136,7 +136,7 @@ func (c *GenericClient) DeleteInstance(d *Driver) error {
} }
func (c *GenericClient) WaitForInstanceStatus(d *Driver, status string) error { func (c *GenericClient) WaitForInstanceStatus(d *Driver, status string) error {
return utils.WaitForSpecificOrError(func() (bool, error) { return mcnutils.WaitForSpecificOrError(func() (bool, error) {
current, err := servers.Get(c.Compute, d.MachineId).Extract() current, err := servers.Get(c.Compute, d.MachineId).Extract()
if err != nil { if err != nil {
return true, err return true, err
@ -437,7 +437,7 @@ func (c *GenericClient) Authenticate(d *Driver) error {
return err return err
} }
provider.UserAgent.Prepend(fmt.Sprintf("docker-machine/v%s", version.Version)) provider.UserAgent.Prepend(fmt.Sprintf("docker-machine/v%d", version.ApiVersion))
if d.Insecure { if d.Insecure {
// Configure custom TLS settings. // Configure custom TLS settings.

View File

@ -1 +0,0 @@
package openstack

View File

@ -7,11 +7,11 @@ import (
"time" "time"
"github.com/codegangsta/cli" "github.com/codegangsta/cli"
"github.com/docker/machine/drivers" "github.com/docker/machine/libmachine/drivers"
"github.com/docker/machine/log" "github.com/docker/machine/libmachine/log"
"github.com/docker/machine/ssh" "github.com/docker/machine/libmachine/mcnutils"
"github.com/docker/machine/state" "github.com/docker/machine/libmachine/ssh"
"github.com/docker/machine/utils" "github.com/docker/machine/libmachine/state"
) )
type Driver struct { type Driver struct {
@ -42,9 +42,14 @@ type Driver struct {
client Client client Client
} }
const (
defaultSSHUser = "root"
defaultSSHPort = 22
defaultActiveTimeout = 200
)
func init() { func init() {
drivers.Register("openstack", &drivers.RegisteredDriver{ drivers.Register("openstack", &drivers.RegisteredDriver{
New: NewDriver,
GetCreateFlags: GetCreateFlags, GetCreateFlags: GetCreateFlags,
}) })
} }
@ -158,35 +163,36 @@ func GetCreateFlags() []cli.Flag {
cli.StringFlag{ cli.StringFlag{
Name: "openstack-ssh-user", Name: "openstack-ssh-user",
Usage: "OpenStack SSH user", Usage: "OpenStack SSH user",
Value: "root", Value: defaultSSHUser,
}, },
cli.IntFlag{ cli.IntFlag{
Name: "openstack-ssh-port", Name: "openstack-ssh-port",
Usage: "OpenStack SSH port", Usage: "OpenStack SSH port",
Value: 22, Value: defaultSSHPort,
}, },
cli.IntFlag{ cli.IntFlag{
Name: "openstack-active-timeout", Name: "openstack-active-timeout",
Usage: "OpenStack active timeout", Usage: "OpenStack active timeout",
Value: 200, Value: defaultActiveTimeout,
}, },
} }
} }
func NewDriver(machineName string, storePath string, caCert string, privateKey string) (drivers.Driver, error) { func NewDriver(hostName, storePath string) drivers.Driver {
log.WithFields(log.Fields{ return NewDerivedDriver(hostName, storePath)
"machineName": machineName,
"storePath": storePath,
"caCert": caCert,
"privateKey": privateKey,
}).Debug("Instantiating OpenStack driver...")
return NewDerivedDriver(machineName, storePath, &GenericClient{}, caCert, privateKey)
} }
func NewDerivedDriver(machineName string, storePath string, client Client, caCert string, privateKey string) (*Driver, error) { func NewDerivedDriver(hostName, storePath string) *Driver {
inner := drivers.NewBaseDriver(machineName, storePath, caCert, privateKey) return &Driver{
return &Driver{BaseDriver: inner, client: client}, nil client: &GenericClient{},
ActiveTimeout: defaultActiveTimeout,
BaseDriver: &drivers.BaseDriver{
SSHUser: defaultSSHUser,
SSHPort: defaultSSHPort,
MachineName: hostName,
StorePath: storePath,
},
}
} }
func (d *Driver) GetSSHHostname() (string, error) { func (d *Driver) GetSSHHostname() (string, error) {
@ -310,7 +316,7 @@ func (d *Driver) PreCreateCheck() error {
} }
func (d *Driver) Create() error { func (d *Driver) Create() error {
d.KeyPairName = fmt.Sprintf("%s-%s", d.MachineName, utils.GenerateRandomID()) d.KeyPairName = fmt.Sprintf("%s-%s", d.MachineName, mcnutils.GenerateRandomID())
if err := d.resolveIds(); err != nil { if err := d.resolveIds(); err != nil {
return err return err

View File

@ -1 +0,0 @@
package openstack

View File

@ -4,8 +4,8 @@ import (
"fmt" "fmt"
"github.com/docker/machine/drivers/openstack" "github.com/docker/machine/drivers/openstack"
"github.com/docker/machine/log" "github.com/docker/machine/libmachine/log"
"github.com/docker/machine/version" "github.com/docker/machine/libmachine/version"
"github.com/rackspace/gophercloud" "github.com/rackspace/gophercloud"
"github.com/rackspace/gophercloud/rackspace" "github.com/rackspace/gophercloud/rackspace"
) )
@ -17,7 +17,6 @@ func unsupportedOpErr(operation string) error {
// Client is a Rackspace specialization of the generic OpenStack driver. // Client is a Rackspace specialization of the generic OpenStack driver.
type Client struct { type Client struct {
openstack.GenericClient openstack.GenericClient
driver *Driver driver *Driver
} }
@ -42,7 +41,7 @@ func (c *Client) Authenticate(d *openstack.Driver) error {
return err return err
} }
provider.UserAgent.Prepend(fmt.Sprintf("docker-machine/v%s", version.Version)) provider.UserAgent.Prepend(fmt.Sprintf("docker-machine/v%d", version.ApiVersion))
err = rackspace.Authenticate(provider, opts) err = rackspace.Authenticate(provider, opts)
if err != nil { if err != nil {

View File

@ -1 +0,0 @@
package rackspace

View File

@ -4,9 +4,9 @@ import (
"fmt" "fmt"
"github.com/codegangsta/cli" "github.com/codegangsta/cli"
"github.com/docker/machine/drivers"
"github.com/docker/machine/drivers/openstack" "github.com/docker/machine/drivers/openstack"
"github.com/docker/machine/log" "github.com/docker/machine/libmachine/drivers"
"github.com/docker/machine/libmachine/log"
) )
// Driver is a machine driver for Rackspace. It's a specialization of the generic OpenStack one. // Driver is a machine driver for Rackspace. It's a specialization of the generic OpenStack one.
@ -16,9 +16,16 @@ type Driver struct {
APIKey string APIKey string
} }
const (
defaultEndpointType = "publicURL"
defaultFlavorId = "general1-1"
defaultSSHUser = "root"
defaultSSHPort = 22
defaultDockerInstall = "true"
)
func init() { func init() {
drivers.Register("rackspace", &drivers.RegisteredDriver{ drivers.Register("rackspace", &drivers.RegisteredDriver{
New: NewDriver,
GetCreateFlags: GetCreateFlags, GetCreateFlags: GetCreateFlags,
}) })
} }
@ -49,7 +56,7 @@ func GetCreateFlags() []cli.Flag {
EnvVar: "OS_ENDPOINT_TYPE", EnvVar: "OS_ENDPOINT_TYPE",
Name: "rackspace-endpoint-type", Name: "rackspace-endpoint-type",
Usage: "Rackspace endpoint type (adminURL, internalURL or the default publicURL)", Usage: "Rackspace endpoint type (adminURL, internalURL or the default publicURL)",
Value: "publicURL", Value: defaultEndpointType,
}, },
cli.StringFlag{ cli.StringFlag{
Name: "rackspace-image-id", Name: "rackspace-image-id",
@ -58,45 +65,38 @@ func GetCreateFlags() []cli.Flag {
cli.StringFlag{ cli.StringFlag{
Name: "rackspace-flavor-id", Name: "rackspace-flavor-id",
Usage: "Rackspace flavor ID. Default: General Purpose 1GB", Usage: "Rackspace flavor ID. Default: General Purpose 1GB",
Value: "general1-1", Value: defaultFlavorId,
EnvVar: "OS_FLAVOR_ID", EnvVar: "OS_FLAVOR_ID",
}, },
cli.StringFlag{ cli.StringFlag{
Name: "rackspace-ssh-user", Name: "rackspace-ssh-user",
Usage: "SSH user for the newly booted machine. Set to root by default", Usage: "SSH user for the newly booted machine. Set to root by default",
Value: "root", Value: defaultSSHUser,
}, },
cli.IntFlag{ cli.IntFlag{
Name: "rackspace-ssh-port", Name: "rackspace-ssh-port",
Usage: "SSH port for the newly booted machine. Set to 22 by default", Usage: "SSH port for the newly booted machine. Set to 22 by default",
Value: 22, Value: defaultSSHPort,
}, },
cli.StringFlag{ cli.StringFlag{
Name: "rackspace-docker-install", Name: "rackspace-docker-install",
Usage: "Set if docker have to be installed on the machine", Usage: "Set if docker have to be installed on the machine",
Value: "true", Value: defaultDockerInstall,
}, },
} }
} }
// NewDriver instantiates a Rackspace driver. // NewDriver instantiates a Rackspace driver.
func NewDriver(machineName string, storePath string, caCert string, privateKey string) (drivers.Driver, error) { func NewDriver(machineName, storePath string) drivers.Driver {
log.WithFields(log.Fields{ log.WithFields(log.Fields{
"machineName": machineName, "machineName": machineName,
"storePath": storePath,
"caCert": caCert,
"privateKey": privateKey,
}).Debug("Instantiating Rackspace driver.") }).Debug("Instantiating Rackspace driver.")
client := &Client{} inner := openstack.NewDerivedDriver(machineName, storePath)
inner, err := openstack.NewDerivedDriver(machineName, storePath, client, caCert, privateKey)
if err != nil {
return nil, err
}
driver := &Driver{Driver: inner} return &Driver{
client.driver = driver Driver: inner,
return driver, nil }
} }
// DriverName is the user-visible name of this driver. // DriverName is the user-visible name of this driver.

View File

@ -1 +0,0 @@
package rackspace

View File

@ -8,10 +8,10 @@ import (
"time" "time"
"github.com/codegangsta/cli" "github.com/codegangsta/cli"
"github.com/docker/machine/drivers" "github.com/docker/machine/libmachine/drivers"
"github.com/docker/machine/log" "github.com/docker/machine/libmachine/log"
"github.com/docker/machine/ssh" "github.com/docker/machine/libmachine/ssh"
"github.com/docker/machine/state" "github.com/docker/machine/libmachine/state"
) )
const ( const (
@ -41,16 +41,42 @@ type deviceConfig struct {
PrivateVLAN int PrivateVLAN int
} }
const (
defaultMemory = 1024
defaultDiskSize = 0
defaultRegion = "dal01"
defaultCpus = 1
defaultImage = "UBUNTU_LATEST"
defaultPublicVLANIP = 0
defaultPrivateVLANIP = 0
)
func init() { func init() {
drivers.Register("softlayer", &drivers.RegisteredDriver{ drivers.Register("softlayer", &drivers.RegisteredDriver{
New: NewDriver,
GetCreateFlags: GetCreateFlags, GetCreateFlags: GetCreateFlags,
}) })
} }
func NewDriver(machineName string, storePath string, caCert string, privateKey string) (drivers.Driver, error) { func NewDriver(hostName, storePath string) drivers.Driver {
inner := drivers.NewBaseDriver(machineName, storePath, caCert, privateKey) return &Driver{
return &Driver{BaseDriver: inner}, nil Client: &Client{
Endpoint: ApiEndpoint,
},
deviceConfig: &deviceConfig{
HourlyBilling: true,
DiskSize: defaultDiskSize,
Image: defaultImage,
Memory: defaultMemory,
Cpu: defaultCpus,
Region: defaultRegion,
PrivateVLAN: defaultPrivateVLANIP,
PublicVLAN: defaultPublicVLANIP,
},
BaseDriver: &drivers.BaseDriver{
MachineName: hostName,
StorePath: storePath,
},
}
} }
func (d *Driver) GetSSHHostname() (string, error) { func (d *Driver) GetSSHHostname() (string, error) {
@ -67,49 +93,45 @@ func GetCreateFlags() []cli.Flag {
EnvVar: "SOFTLAYER_MEMORY", EnvVar: "SOFTLAYER_MEMORY",
Name: "softlayer-memory", Name: "softlayer-memory",
Usage: "Memory in MB for machine", Usage: "Memory in MB for machine",
Value: 1024, Value: defaultMemory,
}, },
cli.IntFlag{ cli.IntFlag{
EnvVar: "SOFTLAYER_DISK_SIZE", EnvVar: "SOFTLAYER_DISK_SIZE",
Name: "softlayer-disk-size", Name: "softlayer-disk-size",
Usage: "Disk size for machine, a value of 0 uses the default size on softlayer", Usage: "Disk size for machine, a value of 0 uses the default size on softlayer",
Value: 0, Value: defaultDiskSize,
}, },
cli.StringFlag{ cli.StringFlag{
EnvVar: "SOFTLAYER_USER", EnvVar: "SOFTLAYER_USER",
Name: "softlayer-user", Name: "softlayer-user",
Usage: "softlayer user account name", Usage: "softlayer user account name",
Value: "",
}, },
cli.StringFlag{ cli.StringFlag{
EnvVar: "SOFTLAYER_API_KEY", EnvVar: "SOFTLAYER_API_KEY",
Name: "softlayer-api-key", Name: "softlayer-api-key",
Usage: "softlayer user API key", Usage: "softlayer user API key",
Value: "",
}, },
cli.StringFlag{ cli.StringFlag{
EnvVar: "SOFTLAYER_REGION", EnvVar: "SOFTLAYER_REGION",
Name: "softlayer-region", Name: "softlayer-region",
Usage: "softlayer region for machine", Usage: "softlayer region for machine",
Value: "dal01", Value: defaultRegion,
}, },
cli.IntFlag{ cli.IntFlag{
EnvVar: "SOFTLAYER_CPU", EnvVar: "SOFTLAYER_CPU",
Name: "softlayer-cpu", Name: "softlayer-cpu",
Usage: "number of CPU's for the machine", Usage: "number of CPU's for the machine",
Value: 1, Value: defaultCpus,
}, },
cli.StringFlag{ cli.StringFlag{
EnvVar: "SOFTLAYER_HOSTNAME", EnvVar: "SOFTLAYER_HOSTNAME",
Name: "softlayer-hostname", Name: "softlayer-hostname",
Usage: "hostname for the machine - defaults to machine name", Usage: "hostname for the machine - defaults to machine name",
Value: "",
}, },
cli.StringFlag{ cli.StringFlag{
EnvVar: "SOFTLAYER_DOMAIN", EnvVar: "SOFTLAYER_DOMAIN",
Name: "softlayer-domain", Name: "softlayer-domain",
Usage: "domain name for machine", Usage: "domain name for machine",
Value: "",
}, },
cli.StringFlag{ cli.StringFlag{
EnvVar: "SOFTLAYER_API_ENDPOINT", EnvVar: "SOFTLAYER_API_ENDPOINT",
@ -136,19 +158,17 @@ func GetCreateFlags() []cli.Flag {
EnvVar: "SOFTLAYER_IMAGE", EnvVar: "SOFTLAYER_IMAGE",
Name: "softlayer-image", Name: "softlayer-image",
Usage: "OS image for machine", Usage: "OS image for machine",
Value: "UBUNTU_LATEST", Value: defaultImage,
}, },
cli.IntFlag{ cli.IntFlag{
EnvVar: "SOFTLAYER_PUBLIC_VLAN_ID", EnvVar: "SOFTLAYER_PUBLIC_VLAN_ID",
Name: "softlayer-public-vlan-id", Name: "softlayer-public-vlan-id",
Usage: "", Usage: "",
Value: 0,
}, },
cli.IntFlag{ cli.IntFlag{
EnvVar: "SOFTLAYER_PRIVATE_VLAN_ID", EnvVar: "SOFTLAYER_PRIVATE_VLAN_ID",
Name: "softlayer-private-vlan-id", Name: "softlayer-private-vlan-id",
Usage: "", Usage: "",
Value: 0,
}, },
} }
} }

View File

@ -80,10 +80,7 @@ func getTestDriver() (*Driver, error) {
} }
defer cleanup() defer cleanup()
d, err := NewDriver(machineTestName, storePath, machineTestCaCert, machineTestPrivateKey) d := NewDriver(machineTestName, storePath)
if err != nil {
return nil, err
}
d.SetConfigFromFlags(getDefaultTestDriverFlags()) d.SetConfigFromFlags(getDefaultTestDriverFlags())
drv := d.(*Driver) drv := d.(*Driver)
return drv, nil return drv, nil

View File

@ -1 +0,0 @@
package softlayer

View File

@ -11,7 +11,7 @@ import (
"runtime" "runtime"
"strings" "strings"
"github.com/docker/machine/log" "github.com/docker/machine/libmachine/log"
) )
var ( var (
@ -68,15 +68,17 @@ func vbmOut(args ...string) (string, error) {
func vbmOutErr(args ...string) (string, string, error) { func vbmOutErr(args ...string) (string, string, error) {
cmd := exec.Command(vboxManageCmd, args...) cmd := exec.Command(vboxManageCmd, args...)
log.Debugf("executing: %v %v", vboxManageCmd, strings.Join(args, " ")) log.Debugf("COMMAND: %v %v", vboxManageCmd, strings.Join(args, " "))
var stdout bytes.Buffer var stdout bytes.Buffer
var stderr bytes.Buffer var stderr bytes.Buffer
cmd.Stdout = &stdout cmd.Stdout = &stdout
cmd.Stderr = &stderr cmd.Stderr = &stderr
err := cmd.Run() err := cmd.Run()
stderrStr := stderr.String() stderrStr := stderr.String()
log.Debugf("STDOUT: %v", stdout.String()) if len(args) > 0 {
log.Debugf("STDERR: %v", stderrStr) log.Debugf("STDOUT:\n{\n%v}", stdout.String())
log.Debugf("STDERR:\n{\n%v}", stderrStr)
}
if err != nil { if err != nil {
if ee, ok := err.(*exec.Error); ok && ee == exec.ErrNotFound { if ee, ok := err.(*exec.Error); ok && ee == exec.ErrNotFound {
err = ErrVBMNotFound err = ErrVBMNotFound

View File

@ -1 +0,0 @@
package virtualbox

View File

@ -19,18 +19,24 @@ import (
"time" "time"
"github.com/codegangsta/cli" "github.com/codegangsta/cli"
"github.com/docker/machine/drivers" "github.com/docker/machine/libmachine/drivers"
"github.com/docker/machine/log" "github.com/docker/machine/libmachine/log"
"github.com/docker/machine/ssh" "github.com/docker/machine/libmachine/mcnutils"
"github.com/docker/machine/state" "github.com/docker/machine/libmachine/ssh"
"github.com/docker/machine/utils" "github.com/docker/machine/libmachine/state"
) )
const ( const (
isoFilename = "boot2docker.iso" isoFilename = "boot2docker.iso"
defaultCPU = 1
defaultMemory = 1024
defaultBoot2DockerURL = ""
defaultBoot2DockerImportVM = ""
defaultHostOnlyCIDR = "192.168.99.1/24" defaultHostOnlyCIDR = "192.168.99.1/24"
defaultNictype = "82540EM" defaultHostOnlyNictype = "82540EM"
defaultNicpromisc = "deny" defaultHostOnlyPromiscMode = "deny"
defaultNoShare = false
defaultDiskSize = 20000
) )
var ( var (
@ -52,11 +58,25 @@ type Driver struct {
func init() { func init() {
drivers.Register("virtualbox", &drivers.RegisteredDriver{ drivers.Register("virtualbox", &drivers.RegisteredDriver{
New: NewDriver,
GetCreateFlags: GetCreateFlags, GetCreateFlags: GetCreateFlags,
}) })
} }
func NewDriver(hostName, storePath string) *Driver {
return &Driver{
BaseDriver: &drivers.BaseDriver{
MachineName: hostName,
StorePath: storePath,
},
Memory: defaultMemory,
CPU: defaultCPU,
DiskSize: defaultDiskSize,
HostOnlyCIDR: defaultHostOnlyCIDR,
HostOnlyNicType: defaultHostOnlyNictype,
HostOnlyPromiscMode: defaultHostOnlyPromiscMode,
}
}
// RegisterCreateFlags registers the flags this driver adds to // RegisterCreateFlags registers the flags this driver adds to
// "docker hosts create" // "docker hosts create"
func GetCreateFlags() []cli.Flag { func GetCreateFlags() []cli.Flag {
@ -65,30 +85,30 @@ func GetCreateFlags() []cli.Flag {
EnvVar: "VIRTUALBOX_MEMORY_SIZE", EnvVar: "VIRTUALBOX_MEMORY_SIZE",
Name: "virtualbox-memory", Name: "virtualbox-memory",
Usage: "Size of memory for host in MB", Usage: "Size of memory for host in MB",
Value: 1024, Value: defaultMemory,
}, },
cli.IntFlag{ cli.IntFlag{
EnvVar: "VIRTUALBOX_CPU_COUNT", EnvVar: "VIRTUALBOX_CPU_COUNT",
Name: "virtualbox-cpu-count", Name: "virtualbox-cpu-count",
Usage: "number of CPUs for the machine (-1 to use the number of CPUs available)", Usage: "number of CPUs for the machine (-1 to use the number of CPUs available)",
Value: 1, Value: defaultCPU,
}, },
cli.IntFlag{ cli.IntFlag{
EnvVar: "VIRTUALBOX_DISK_SIZE", EnvVar: "VIRTUALBOX_DISK_SIZE",
Name: "virtualbox-disk-size", Name: "virtualbox-disk-size",
Usage: "Size of disk for host in MB", Usage: "Size of disk for host in MB",
Value: 20000, Value: defaultDiskSize,
}, },
cli.StringFlag{ cli.StringFlag{
EnvVar: "VIRTUALBOX_BOOT2DOCKER_URL", EnvVar: "VIRTUALBOX_BOOT2DOCKER_URL",
Name: "virtualbox-boot2docker-url", Name: "virtualbox-boot2docker-url",
Usage: "The URL of the boot2docker image. Defaults to the latest available version", Usage: "The URL of the boot2docker image. Defaults to the latest available version",
Value: "", Value: defaultBoot2DockerURL,
}, },
cli.StringFlag{ cli.StringFlag{
Name: "virtualbox-import-boot2docker-vm", Name: "virtualbox-import-boot2docker-vm",
Usage: "The name of a Boot2Docker VM to import", Usage: "The name of a Boot2Docker VM to import",
Value: "", Value: defaultBoot2DockerImportVM,
}, },
cli.StringFlag{ cli.StringFlag{
Name: "virtualbox-hostonly-cidr", Name: "virtualbox-hostonly-cidr",
@ -99,13 +119,13 @@ func GetCreateFlags() []cli.Flag {
cli.StringFlag{ cli.StringFlag{
Name: "virtualbox-hostonly-nictype", Name: "virtualbox-hostonly-nictype",
Usage: "Specify the Host Only Network Adapter Type", Usage: "Specify the Host Only Network Adapter Type",
Value: defaultNictype, Value: defaultHostOnlyNictype,
EnvVar: "VIRTUALBOX_HOSTONLY_NIC_TYPE", EnvVar: "VIRTUALBOX_HOSTONLY_NIC_TYPE",
}, },
cli.StringFlag{ cli.StringFlag{
Name: "virtualbox-hostonly-nicpromisc", Name: "virtualbox-hostonly-nicpromisc",
Usage: "Specify the Host Only Network Adapter Promiscuous Mode", Usage: "Specify the Host Only Network Adapter Promiscuous Mode",
Value: defaultNicpromisc, Value: defaultHostOnlyPromiscMode,
EnvVar: "VIRTUALBOX_HOSTONLY_NIC_PROMISC", EnvVar: "VIRTUALBOX_HOSTONLY_NIC_PROMISC",
}, },
cli.BoolFlag{ cli.BoolFlag{
@ -115,11 +135,6 @@ func GetCreateFlags() []cli.Flag {
} }
} }
func NewDriver(machineName string, storePath string, caCert string, privateKey string) (drivers.Driver, error) {
inner := drivers.NewBaseDriver(machineName, storePath, caCert, privateKey)
return &Driver{BaseDriver: inner}, nil
}
func (d *Driver) GetSSHHostname() (string, error) { func (d *Driver) GetSSHHostname() (string, error) {
return "localhost", nil return "localhost", nil
} }
@ -179,7 +194,7 @@ func (d *Driver) Create() error {
return err return err
} }
b2dutils := utils.NewB2dUtils("", "") b2dutils := mcnutils.NewB2dUtils("", "", d.StorePath)
if err := b2dutils.CopyIsoToMachineDir(d.Boot2DockerURL, d.MachineName); err != nil { if err := b2dutils.CopyIsoToMachineDir(d.Boot2DockerURL, d.MachineName); err != nil {
return err return err
} }
@ -216,8 +231,8 @@ func (d *Driver) Create() error {
d.Memory = vmInfo.Memory d.Memory = vmInfo.Memory
log.Debugf("Importing SSH key...") log.Debugf("Importing SSH key...")
keyPath := filepath.Join(utils.GetHomeDir(), ".ssh", "id_boot2docker") keyPath := filepath.Join(mcnutils.GetHomeDir(), ".ssh", "id_boot2docker")
if err := utils.CopyFile(keyPath, d.GetSSHKeyPath()); err != nil { if err := mcnutils.CopyFile(keyPath, d.GetSSHKeyPath()); err != nil {
return err return err
} }
} else { } else {
@ -416,7 +431,7 @@ func (d *Driver) Start() error {
} }
// Bail if we don't get an IP from DHCP after a given number of seconds. // Bail if we don't get an IP from DHCP after a given number of seconds.
if err := utils.WaitForSpecific(d.hostOnlyIpAvailable, 5, 4*time.Second); err != nil { if err := mcnutils.WaitForSpecific(d.hostOnlyIpAvailable, 5, 4*time.Second); err != nil {
return err return err
} }
@ -510,12 +525,6 @@ func (d *Driver) GetState() (state.State, error) {
return state.None, nil return state.None, nil
} }
func (d *Driver) setMachineNameIfNotSet() {
if d.MachineName == "" {
d.MachineName = fmt.Sprintf("docker-machine-unknown")
}
}
func (d *Driver) GetIP() (string, error) { func (d *Driver) GetIP() (string, error) {
// DHCP is used to get the IP, so virtualbox hosts don't have IPs unless // DHCP is used to get the IP, so virtualbox hosts don't have IPs unless
// they are running // they are running

View File

@ -18,11 +18,11 @@ import (
"time" "time"
"github.com/codegangsta/cli" "github.com/codegangsta/cli"
"github.com/docker/machine/drivers" "github.com/docker/machine/libmachine/drivers"
"github.com/docker/machine/log" "github.com/docker/machine/libmachine/log"
"github.com/docker/machine/ssh" "github.com/docker/machine/libmachine/mcnutils"
"github.com/docker/machine/state" "github.com/docker/machine/libmachine/ssh"
"github.com/docker/machine/utils" "github.com/docker/machine/libmachine/state"
cryptossh "golang.org/x/crypto/ssh" cryptossh "golang.org/x/crypto/ssh"
) )
@ -48,9 +48,16 @@ type Driver struct {
ConfigDriveURL string ConfigDriveURL string
} }
const (
defaultSSHUser = B2DUser
defaultSSHPass = B2DPass
defaultDiskSize = 20000
defaultCpus = 1
defaultMemory = 1024
)
func init() { func init() {
drivers.Register("vmwarefusion", &drivers.RegisteredDriver{ drivers.Register("vmwarefusion", &drivers.RegisteredDriver{
New: NewDriver,
GetCreateFlags: GetCreateFlags, GetCreateFlags: GetCreateFlags,
}) })
} }
@ -73,38 +80,47 @@ func GetCreateFlags() []cli.Flag {
EnvVar: "FUSION_CPU_COUNT", EnvVar: "FUSION_CPU_COUNT",
Name: "vmwarefusion-cpu-count", Name: "vmwarefusion-cpu-count",
Usage: "number of CPUs for the machine (-1 to use the number of CPUs available)", Usage: "number of CPUs for the machine (-1 to use the number of CPUs available)",
Value: 1, Value: defaultCpus,
}, },
cli.IntFlag{ cli.IntFlag{
EnvVar: "FUSION_MEMORY_SIZE", EnvVar: "FUSION_MEMORY_SIZE",
Name: "vmwarefusion-memory-size", Name: "vmwarefusion-memory-size",
Usage: "Fusion size of memory for host VM (in MB)", Usage: "Fusion size of memory for host VM (in MB)",
Value: 1024, Value: defaultMemory,
}, },
cli.IntFlag{ cli.IntFlag{
EnvVar: "FUSION_DISK_SIZE", EnvVar: "FUSION_DISK_SIZE",
Name: "vmwarefusion-disk-size", Name: "vmwarefusion-disk-size",
Usage: "Fusion size of disk for host VM (in MB)", Usage: "Fusion size of disk for host VM (in MB)",
Value: 20000, Value: defaultDiskSize,
}, },
cli.StringFlag{ cli.StringFlag{
EnvVar: "FUSION_SSH_USER", EnvVar: "FUSION_SSH_USER",
Name: "vmwarefusion-ssh-user", Name: "vmwarefusion-ssh-user",
Usage: "SSH user", Usage: "SSH user",
Value: B2DUser, Value: defaultSSHUser,
}, },
cli.StringFlag{ cli.StringFlag{
EnvVar: "FUSION_SSH_PASSWORD", EnvVar: "FUSION_SSH_PASSWORD",
Name: "vmwarefusion-ssh-password", Name: "vmwarefusion-ssh-password",
Usage: "SSH password", Usage: "SSH password",
Value: B2DPass, Value: defaultSSHPass,
}, },
} }
} }
func NewDriver(machineName string, storePath string, caCert string, privateKey string) (drivers.Driver, error) { func NewDriver(hostName, storePath string) drivers.Driver {
inner := drivers.NewBaseDriver(machineName, storePath, caCert, privateKey) return &Driver{
return &Driver{BaseDriver: inner}, nil CPUS: defaultCpus,
Memory: defaultMemory,
DiskSize: defaultDiskSize,
SSHPassword: defaultSSHPass,
BaseDriver: &drivers.BaseDriver{
SSHUser: defaultSSHUser,
MachineName: hostName,
StorePath: storePath,
},
}
} }
func (d *Driver) GetSSHHostname() (string, error) { func (d *Driver) GetSSHHostname() (string, error) {
@ -129,7 +145,6 @@ func (d *Driver) SetConfigFromFlags(flags drivers.DriverOptions) error {
d.DiskSize = flags.Int("vmwarefusion-disk-size") d.DiskSize = flags.Int("vmwarefusion-disk-size")
d.Boot2DockerURL = flags.String("vmwarefusion-boot2docker-url") d.Boot2DockerURL = flags.String("vmwarefusion-boot2docker-url")
d.ConfigDriveURL = flags.String("vmwarefusion-configdrive-url") d.ConfigDriveURL = flags.String("vmwarefusion-configdrive-url")
d.ConfigDriveISO = d.ResolveStorePath(isoConfigDrive)
d.ISO = d.ResolveStorePath(isoFilename) d.ISO = d.ResolveStorePath(isoFilename)
d.SwarmMaster = flags.Bool("swarm-master") d.SwarmMaster = flags.Bool("swarm-master")
d.SwarmHost = flags.String("swarm-host") d.SwarmHost = flags.String("swarm-host")
@ -191,8 +206,7 @@ func (d *Driver) PreCreateCheck() error {
} }
func (d *Driver) Create() error { func (d *Driver) Create() error {
b2dutils := mcnutils.NewB2dUtils("", "", d.StorePath)
b2dutils := utils.NewB2dUtils("", "")
if err := b2dutils.CopyIsoToMachineDir(d.Boot2DockerURL, d.MachineName); err != nil { if err := b2dutils.CopyIsoToMachineDir(d.Boot2DockerURL, d.MachineName); err != nil {
return err return err
} }

View File

@ -1 +0,0 @@
package vmwarefusion

View File

@ -8,11 +8,12 @@ import (
"bytes" "bytes"
"errors" "errors"
"fmt" "fmt"
"github.com/docker/machine/log"
"os" "os"
"os/exec" "os/exec"
"path/filepath" "path/filepath"
"strings" "strings"
"github.com/docker/machine/libmachine/log"
) )
var ( var (

View File

@ -1 +0,0 @@
package vmwarefusion

View File

@ -1,2 +0,0 @@
// Package vmwarefusion is empty to allow builds on non-darwin platforms
package vmwarefusion

View File

@ -1 +0,0 @@
package vmwarefusion

View File

@ -12,11 +12,11 @@ import (
"github.com/vmware/govcloudair" "github.com/vmware/govcloudair"
"github.com/codegangsta/cli" "github.com/codegangsta/cli"
"github.com/docker/machine/drivers" "github.com/docker/machine/libmachine/drivers"
"github.com/docker/machine/log" "github.com/docker/machine/libmachine/log"
"github.com/docker/machine/ssh" "github.com/docker/machine/libmachine/mcnutils"
"github.com/docker/machine/state" "github.com/docker/machine/libmachine/ssh"
"github.com/docker/machine/utils" "github.com/docker/machine/libmachine/state"
) )
type Driver struct { type Driver struct {
@ -37,9 +37,17 @@ type Driver struct {
VAppID string VAppID string
} }
const (
defaultCatalog = "Public Catalog"
defaultCatalogItem = "Ubuntu Server 12.04 LTS (amd64 20150127)"
defaultCpus = 1
defaultMemory = 2048
defaultSSHPort = 22
defaultDockerPort = 2376
)
func init() { func init() {
drivers.Register("vmwarevcloudair", &drivers.RegisteredDriver{ drivers.Register("vmwarevcloudair", &drivers.RegisteredDriver{
New: NewDriver,
GetCreateFlags: GetCreateFlags, GetCreateFlags: GetCreateFlags,
}) })
} }
@ -87,13 +95,13 @@ func GetCreateFlags() []cli.Flag {
EnvVar: "VCLOUDAIR_CATALOG", EnvVar: "VCLOUDAIR_CATALOG",
Name: "vmwarevcloudair-catalog", Name: "vmwarevcloudair-catalog",
Usage: "vCloud Air Catalog (default is Public Catalog)", Usage: "vCloud Air Catalog (default is Public Catalog)",
Value: "Public Catalog", Value: defaultCatalog,
}, },
cli.StringFlag{ cli.StringFlag{
EnvVar: "VCLOUDAIR_CATALOGITEM", EnvVar: "VCLOUDAIR_CATALOGITEM",
Name: "vmwarevcloudair-catalogitem", Name: "vmwarevcloudair-catalogitem",
Usage: "vCloud Air Catalog Item (default is Ubuntu Precise)", Usage: "vCloud Air Catalog Item (default is Ubuntu Precise)",
Value: "Ubuntu Server 12.04 LTS (amd64 20150127)", Value: defaultCatalogItem,
}, },
// BoolTFlag is true by default. // BoolTFlag is true by default.
@ -107,32 +115,42 @@ func GetCreateFlags() []cli.Flag {
EnvVar: "VCLOUDAIR_CPU_COUNT", EnvVar: "VCLOUDAIR_CPU_COUNT",
Name: "vmwarevcloudair-cpu-count", Name: "vmwarevcloudair-cpu-count",
Usage: "vCloud Air VM Cpu Count (default 1)", Usage: "vCloud Air VM Cpu Count (default 1)",
Value: 1, Value: defaultCpus,
}, },
cli.IntFlag{ cli.IntFlag{
EnvVar: "VCLOUDAIR_MEMORY_SIZE", EnvVar: "VCLOUDAIR_MEMORY_SIZE",
Name: "vmwarevcloudair-memory-size", Name: "vmwarevcloudair-memory-size",
Usage: "vCloud Air VM Memory Size in MB (default 2048)", Usage: "vCloud Air VM Memory Size in MB (default 2048)",
Value: 2048, Value: defaultMemory,
}, },
cli.IntFlag{ cli.IntFlag{
EnvVar: "VCLOUDAIR_SSH_PORT", EnvVar: "VCLOUDAIR_SSH_PORT",
Name: "vmwarevcloudair-ssh-port", Name: "vmwarevcloudair-ssh-port",
Usage: "vCloud Air SSH port", Usage: "vCloud Air SSH port",
Value: 22, Value: defaultSSHPort,
}, },
cli.IntFlag{ cli.IntFlag{
EnvVar: "VCLOUDAIR_DOCKER_PORT", EnvVar: "VCLOUDAIR_DOCKER_PORT",
Name: "vmwarevcloudair-docker-port", Name: "vmwarevcloudair-docker-port",
Usage: "vCloud Air Docker port", Usage: "vCloud Air Docker port",
Value: 2376, Value: defaultDockerPort,
}, },
} }
} }
func NewDriver(machineName string, storePath string, caCert string, privateKey string) (drivers.Driver, error) { func NewDriver(hostName, storePath string) drivers.Driver {
inner := drivers.NewBaseDriver(machineName, storePath, caCert, privateKey) return &Driver{
return &Driver{BaseDriver: inner}, nil Catalog: defaultCatalog,
CatalogItem: defaultCatalogItem,
CPUCount: defaultCpus,
MemorySize: defaultMemory,
DockerPort: defaultDockerPort,
BaseDriver: &drivers.BaseDriver{
SSHPort: defaultSSHPort,
MachineName: hostName,
StorePath: storePath,
},
}
} }
func (d *Driver) GetSSHHostname() (string, error) { func (d *Driver) GetSSHHostname() (string, error) {
@ -645,7 +663,7 @@ func (d *Driver) Kill() error {
// Helpers // Helpers
func generateVMName() string { func generateVMName() string {
randomID := utils.TruncateID(utils.GenerateRandomID()) randomID := mcnutils.TruncateID(mcnutils.GenerateRandomID())
return fmt.Sprintf("docker-host-%s", randomID) return fmt.Sprintf("docker-host-%s", randomID)
} }

Some files were not shown because too many files have changed in this diff Show More