Revise CommandLine interface to contain libmachine client and store

Signed-off-by: Nathan LeClaire <nathan.leclaire@gmail.com>
This commit is contained in:
Nathan LeClaire 2015-11-13 13:26:31 -08:00
parent 3affe5ec25
commit 19ce7b79bd
30 changed files with 357 additions and 361 deletions

View File

@ -10,8 +10,6 @@ import (
"github.com/docker/machine/commands" "github.com/docker/machine/commands"
"github.com/docker/machine/commands/mcndirs" "github.com/docker/machine/commands/mcndirs"
"github.com/docker/machine/libmachine/log" "github.com/docker/machine/libmachine/log"
"github.com/docker/machine/libmachine/mcnutils"
"github.com/docker/machine/libmachine/ssh"
"github.com/docker/machine/version" "github.com/docker/machine/version"
) )
@ -74,16 +72,6 @@ func main() {
app.Name = path.Base(os.Args[0]) app.Name = path.Base(os.Args[0])
app.Author = "Docker Machine Contributors" app.Author = "Docker Machine Contributors"
app.Email = "https://github.com/docker/machine" app.Email = "https://github.com/docker/machine"
app.Before = func(c *cli.Context) error {
// TODO: Need better handling of config, everything is too
// complected together right now.
if c.GlobalBool("native-ssh") {
ssh.SetDefaultClient(ssh.Native)
}
mcnutils.GithubAPIToken = c.GlobalString("github-api-token")
mcndirs.BaseDir = c.GlobalString("storage-path")
return nil
}
app.Commands = commands.Commands app.Commands = commands.Commands
app.CommandNotFound = cmdNotFound app.CommandNotFound = cmdNotFound

View File

@ -4,46 +4,33 @@ import (
"errors" "errors"
"fmt" "fmt"
"github.com/docker/machine/libmachine/host" "github.com/docker/machine/libmachine"
"github.com/docker/machine/libmachine/persist" "github.com/docker/machine/libmachine/persist"
) )
var ( var (
errTooManyArguments = errors.New("Error: Too many arguments given") errTooManyArguments = errors.New("Error: Too many arguments given")
errNoActiveHost = errors.New("No active host found")
) )
func cmdActive(c CommandLine) error { func cmdActive(c CommandLine, api libmachine.API) error {
if len(c.Args()) > 0 { if len(c.Args()) > 0 {
return errTooManyArguments return errTooManyArguments
} }
store := getStore(c) hosts, err := persist.LoadAllHosts(api)
host, err := getActiveHost(store)
if err != nil { if err != nil {
return fmt.Errorf("Error getting active host: %s", err) return fmt.Errorf("Error getting active host: %s", err)
} }
if host != nil { items := getHostListItems(hosts)
fmt.Println(host.Name)
}
return nil for _, item := range items {
}
func getActiveHost(store persist.Store) (*host.Host, error) {
hosts, err := listHosts(store)
if err != nil {
return nil, err
}
hostListItems := getHostListItems(hosts)
for _, item := range hostListItems {
if item.Active { if item.Active {
return loadHost(store, item.Name) fmt.Println(item.Name)
return nil
} }
} }
return nil, errors.New("Active host not found") return errNoActiveHost
} }

View File

@ -9,14 +9,13 @@ import (
"github.com/codegangsta/cli" "github.com/codegangsta/cli"
"github.com/docker/machine/commands/mcndirs" "github.com/docker/machine/commands/mcndirs"
"github.com/docker/machine/drivers/errdriver" "github.com/docker/machine/libmachine"
"github.com/docker/machine/libmachine/cert" "github.com/docker/machine/libmachine/cert"
"github.com/docker/machine/libmachine/drivers"
"github.com/docker/machine/libmachine/drivers/plugin/localbinary"
"github.com/docker/machine/libmachine/drivers/rpc"
"github.com/docker/machine/libmachine/host" "github.com/docker/machine/libmachine/host"
"github.com/docker/machine/libmachine/log" "github.com/docker/machine/libmachine/log"
"github.com/docker/machine/libmachine/mcnutils"
"github.com/docker/machine/libmachine/persist" "github.com/docker/machine/libmachine/persist"
"github.com/docker/machine/libmachine/ssh"
) )
var ( var (
@ -64,26 +63,49 @@ func (c *contextCommandLine) Application() *cli.App {
return c.App return c.App
} }
func newPluginDriver(driverName string, rawContent []byte) (drivers.Driver, error) { func runAction(actionName string, c CommandLine, api libmachine.API) error {
d, err := rpcdriver.NewRPCClientDriver(rawContent, driverName) hosts, err := persist.LoadHosts(api, c.Args())
if err != nil { if err != nil {
// Not being able to find a driver binary is a "known error" return err
if _, ok := err.(localbinary.ErrPluginBinaryNotFound); ok { }
return errdriver.NewDriver(driverName), nil
if len(hosts) == 0 {
return ErrNoMachineSpecified
}
if errs := runActionForeachMachine(actionName, hosts); len(errs) > 0 {
return consolidateErrs(errs)
}
for _, h := range hosts {
if err := api.Save(h); err != nil {
return fmt.Errorf("Error saving host to store: %s", err)
} }
return nil, err
} }
if driverName == "virtualbox" { return nil
return drivers.NewSerialDriver(d), nil
}
return d, nil
} }
func fatalOnError(command func(commandLine CommandLine) error) func(context *cli.Context) { func fatalOnError(command func(commandLine CommandLine, api libmachine.API) error) func(context *cli.Context) {
return func(context *cli.Context) { return func(context *cli.Context) {
if err := command(&contextCommandLine{context}); err != nil { api := libmachine.NewClient(mcndirs.GetBaseDir())
if context.GlobalBool("native-ssh") {
api.SSHClientType = ssh.Native
}
api.GithubAPIToken = context.GlobalString("github-api-token")
api.Filestore.Path = context.GlobalString("storage-path")
// TODO (nathanleclaire): These should ultimately be accessed
// through the libmachine client by the rest of the code and
// not through their respective modules. For now, however,
// they are also being set the way that they originally were
// set to preserve backwards compatibility.
mcndirs.BaseDir = api.Filestore.Path
mcnutils.GithubAPIToken = api.GithubAPIToken
ssh.SetDefaultClient(api.SSHClientType)
if err := command(&contextCommandLine{context}, api); err != nil {
log.Fatal(err) log.Fatal(err)
} }
} }
@ -102,88 +124,6 @@ func confirmInput(msg string) (bool, error) {
return confirmed, nil return confirmed, nil
} }
func getStore(c CommandLine) persist.Store {
certInfo := getCertPathInfoFromContext(c)
return &persist.Filestore{
Path: c.GlobalString("storage-path"),
CaCertPath: certInfo.CaCertPath,
CaPrivateKeyPath: certInfo.CaPrivateKeyPath,
}
}
func listHosts(store persist.Store) ([]*host.Host, error) {
cliHosts := []*host.Host{}
hosts, err := store.List()
if err != nil {
return nil, fmt.Errorf("Error attempting to list hosts from store: %s", err)
}
for _, h := range hosts {
d, err := newPluginDriver(h.DriverName, h.RawDriver)
if err != nil {
return nil, fmt.Errorf("Error attempting to invoke binary for plugin '%s': %s", h.DriverName, err)
}
h.Driver = d
cliHosts = append(cliHosts, h)
}
return cliHosts, nil
}
func loadHost(store persist.Store, hostName string) (*host.Host, error) {
h, err := store.Load(hostName)
if err != nil {
return nil, fmt.Errorf("Loading host from store failed: %s", err)
}
d, err := newPluginDriver(h.DriverName, h.RawDriver)
if err != nil {
return nil, fmt.Errorf("Error attempting to invoke binary for plugin: %s", err)
}
h.Driver = d
return h, nil
}
func saveHost(store persist.Store, h *host.Host) error {
if err := store.Save(h); err != nil {
return fmt.Errorf("Error attempting to save host to store: %s", err)
}
return nil
}
func getFirstArgHost(c CommandLine) (*host.Host, error) {
store := getStore(c)
hostName := c.Args().First()
h, err := loadHost(store, hostName)
if err != nil {
return nil, fmt.Errorf("Error trying to get host %q: %s", hostName, err)
}
return h, nil
}
func getHostsFromContext(c CommandLine) ([]*host.Host, error) {
store := getStore(c)
hosts := []*host.Host{}
for _, hostName := range c.Args() {
h, err := loadHost(store, hostName)
if err != nil {
return nil, fmt.Errorf("Could not load host %q: %s", hostName, err)
}
hosts = append(hosts, h)
}
return hosts, nil
}
var Commands = []cli.Command{ var Commands = []cli.Command{
{ {
Name: "active", Name: "active",
@ -427,52 +367,27 @@ func consolidateErrs(errs []error) error {
return errors.New(strings.TrimSpace(finalErr)) return errors.New(strings.TrimSpace(finalErr))
} }
func runActionWithContext(actionName string, c CommandLine) error { // Returns the cert paths. codegangsta/cli will not set the cert paths if the
store := getStore(c) // storage-path is set to something different so we cannot use the paths in the
// global options. le sigh.
hosts, err := getHostsFromContext(c) func getCertPathInfoFromCommandLine(c CommandLine) cert.PathInfo {
if err != nil {
return err
}
if len(hosts) == 0 {
return ErrNoMachineSpecified
}
if errs := runActionForeachMachine(actionName, hosts); len(errs) > 0 {
return consolidateErrs(errs)
}
for _, h := range hosts {
if err := saveHost(store, h); err != nil {
return fmt.Errorf("Error saving host to store: %s", err)
}
}
return nil
}
// 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 getCertPathInfoFromContext(c CommandLine) cert.PathInfo {
caCertPath := c.GlobalString("tls-ca-cert") caCertPath := c.GlobalString("tls-ca-cert")
caKeyPath := c.GlobalString("tls-ca-key")
clientCertPath := c.GlobalString("tls-client-cert")
clientKeyPath := c.GlobalString("tls-client-key")
if caCertPath == "" { if caCertPath == "" {
caCertPath = filepath.Join(mcndirs.GetMachineCertDir(), "ca.pem") caCertPath = filepath.Join(mcndirs.GetMachineCertDir(), "ca.pem")
} }
caKeyPath := c.GlobalString("tls-ca-key")
if caKeyPath == "" { if caKeyPath == "" {
caKeyPath = filepath.Join(mcndirs.GetMachineCertDir(), "ca-key.pem") caKeyPath = filepath.Join(mcndirs.GetMachineCertDir(), "ca-key.pem")
} }
clientCertPath := c.GlobalString("tls-client-cert")
if clientCertPath == "" { if clientCertPath == "" {
clientCertPath = filepath.Join(mcndirs.GetMachineCertDir(), "cert.pem") clientCertPath = filepath.Join(mcndirs.GetMachineCertDir(), "cert.pem")
} }
clientKeyPath := c.GlobalString("tls-client-key")
if clientKeyPath == "" { if clientKeyPath == "" {
clientKeyPath = filepath.Join(mcndirs.GetMachineCertDir(), "key.pem") clientKeyPath = filepath.Join(mcndirs.GetMachineCertDir(), "key.pem")
} }

View File

@ -6,6 +6,7 @@ import (
"os" "os"
"strings" "strings"
"github.com/docker/machine/libmachine"
"github.com/docker/machine/libmachine/auth" "github.com/docker/machine/libmachine/auth"
"github.com/docker/machine/libmachine/cert" "github.com/docker/machine/libmachine/cert"
"github.com/docker/machine/libmachine/host" "github.com/docker/machine/libmachine/host"
@ -26,7 +27,7 @@ Be advised that this will trigger a Docker daemon restart which will stop runnin
`, e.hostURL, e.wrappedErr) `, e.hostURL, e.wrappedErr)
} }
func cmdConfig(c CommandLine) error { func cmdConfig(c CommandLine, api libmachine.API) error {
// Ensure that log messages always go to stderr when this command is // Ensure that log messages always go to stderr when this command is
// being run (it is intended to be run in a subshell) // being run (it is intended to be run in a subshell)
log.SetOutWriter(os.Stderr) log.SetOutWriter(os.Stderr)
@ -35,7 +36,7 @@ func cmdConfig(c CommandLine) error {
return ErrExpectedOneMachine return ErrExpectedOneMachine
} }
host, err := getFirstArgHost(c) host, err := api.Load(c.Args().First())
if err != nil { if err != nil {
return err return err
} }

View File

@ -24,7 +24,6 @@ import (
"github.com/docker/machine/libmachine/log" "github.com/docker/machine/libmachine/log"
"github.com/docker/machine/libmachine/mcnerror" "github.com/docker/machine/libmachine/mcnerror"
"github.com/docker/machine/libmachine/mcnflag" "github.com/docker/machine/libmachine/mcnflag"
"github.com/docker/machine/libmachine/persist"
"github.com/docker/machine/libmachine/swarm" "github.com/docker/machine/libmachine/swarm"
) )
@ -123,28 +122,19 @@ var (
} }
) )
func cmdCreateInner(c CommandLine) error { func cmdCreateInner(c CommandLine, api libmachine.API) error {
if len(c.Args()) > 1 { if len(c.Args()) > 1 {
return fmt.Errorf("Invalid command line. Found extra arguments %v", c.Args()[1:]) return fmt.Errorf("Invalid command line. Found extra arguments %v", c.Args()[1:])
} }
name := c.Args().First() name := c.Args().First()
driverName := c.String("driver")
certInfo := getCertPathInfoFromContext(c)
storePath := c.GlobalString("storage-path")
store := &persist.Filestore{
Path: storePath,
CaCertPath: certInfo.CaCertPath,
CaPrivateKeyPath: certInfo.CaPrivateKeyPath,
}
if name == "" { if name == "" {
c.ShowHelp() c.ShowHelp()
return errNoMachineName return errNoMachineName
} }
driverName := c.String("driver")
validName := host.ValidateHostName(name) validName := host.ValidateHostName(name)
if !validName { if !validName {
return fmt.Errorf("Error creating machine: %s", mcnerror.ErrInvalidHostname) return fmt.Errorf("Error creating machine: %s", mcnerror.ErrInvalidHostname)
@ -163,16 +153,18 @@ func cmdCreateInner(c CommandLine) error {
return fmt.Errorf("Error attempting to marshal bare driver data: %s", err) return fmt.Errorf("Error attempting to marshal bare driver data: %s", err)
} }
driver, err := newPluginDriver(driverName, bareDriverData) driver, err := api.NewPluginDriver(driverName, bareDriverData)
if err != nil { if err != nil {
return fmt.Errorf("Error loading driver %q: %s", driverName, err) return fmt.Errorf("Error loading driver %q: %s", driverName, err)
} }
h, err := store.NewHost(driver) h, err := api.NewHost(driver)
if err != nil { if err != nil {
return fmt.Errorf("Error getting new host: %s", err) return fmt.Errorf("Error getting new host: %s", err)
} }
certInfo := getCertPathInfoFromCommandLine(c)
h.HostOptions = &host.Options{ h.HostOptions = &host.Options{
AuthOptions: &auth.Options{ AuthOptions: &auth.Options{
CertDir: mcndirs.GetMachineCertDir(), CertDir: mcndirs.GetMachineCertDir(),
@ -207,7 +199,7 @@ func cmdCreateInner(c CommandLine) error {
}, },
} }
exists, err := store.Exists(h.Name) exists, err := api.Exists(h.Name)
if err != nil { if err != nil {
return fmt.Errorf("Error checking if host exists: %s", err) return fmt.Errorf("Error checking if host exists: %s", err)
} }
@ -227,11 +219,11 @@ func cmdCreateInner(c CommandLine) error {
return fmt.Errorf("Error setting machine configuration from flags provided: %s", err) return fmt.Errorf("Error setting machine configuration from flags provided: %s", err)
} }
if err := libmachine.Create(store, h); err != nil { if err := api.Create(h); err != nil {
return fmt.Errorf("Error creating machine: %s", err) return fmt.Errorf("Error creating machine: %s", err)
} }
if err := saveHost(store, h); err != nil { if err := api.Save(h); err != nil {
return fmt.Errorf("Error attempting to save store: %s", err) return fmt.Errorf("Error attempting to save store: %s", err)
} }
@ -276,7 +268,7 @@ func flagHackLookup(flagName string) string {
return "" return ""
} }
func cmdCreateOuter(c CommandLine) error { func cmdCreateOuter(c CommandLine, api libmachine.API) error {
const ( const (
flagLookupMachineName = "flag-lookup" flagLookupMachineName = "flag-lookup"
) )
@ -296,7 +288,7 @@ func cmdCreateOuter(c CommandLine) error {
return fmt.Errorf("Error attempting to marshal bare driver data: %s", err) return fmt.Errorf("Error attempting to marshal bare driver data: %s", err)
} }
driver, err := newPluginDriver(driverName, bareDriverData) driver, err := api.NewPluginDriver(driverName, bareDriverData)
if err != nil { if err != nil {
return fmt.Errorf("Error loading driver %q: %s", driverName, err) return fmt.Errorf("Error loading driver %q: %s", driverName, err)
} }

View File

@ -10,6 +10,7 @@ import (
"text/template" "text/template"
"github.com/docker/machine/commands/mcndirs" "github.com/docker/machine/commands/mcndirs"
"github.com/docker/machine/libmachine"
"github.com/docker/machine/libmachine/log" "github.com/docker/machine/libmachine/log"
) )
@ -35,23 +36,23 @@ type ShellConfig struct {
NoProxyValue string NoProxyValue string
} }
func cmdEnv(c CommandLine) error { func cmdEnv(c CommandLine, api libmachine.API) error {
// Ensure that log messages always go to stderr when this command is // Ensure that log messages always go to stderr when this command is
// being run (it is intended to be run in a subshell) // being run (it is intended to be run in a subshell)
log.SetOutWriter(os.Stderr) log.SetOutWriter(os.Stderr)
if c.Bool("unset") { if c.Bool("unset") {
return unset(c) return unset(c, api)
} }
return set(c) return set(c, api)
} }
func set(c CommandLine) error { func set(c CommandLine, api libmachine.API) error {
if len(c.Args()) != 1 { if len(c.Args()) != 1 {
return errImproperEnvArgs return errImproperEnvArgs
} }
host, err := getFirstArgHost(c) host, err := api.Load(c.Args().First())
if err != nil { if err != nil {
return err return err
} }
@ -118,7 +119,7 @@ func set(c CommandLine) error {
return executeTemplateStdout(shellCfg) return executeTemplateStdout(shellCfg)
} }
func unset(c CommandLine) error { func unset(c CommandLine, api libmachine.API) error {
if len(c.Args()) != 0 { if len(c.Args()) != 0 {
return errImproperUnsetEnvArgs return errImproperUnsetEnvArgs
} }

View File

@ -5,6 +5,8 @@ import (
"fmt" "fmt"
"os" "os"
"text/template" "text/template"
"github.com/docker/machine/libmachine"
) )
var funcMap = template.FuncMap{ var funcMap = template.FuncMap{
@ -18,13 +20,13 @@ var funcMap = template.FuncMap{
}, },
} }
func cmdInspect(c CommandLine) error { func cmdInspect(c CommandLine, api libmachine.API) error {
if len(c.Args()) == 0 { if len(c.Args()) == 0 {
c.ShowHelp() c.ShowHelp()
return ErrExpectedOneMachine return ErrExpectedOneMachine
} }
host, err := getFirstArgHost(c) host, err := api.Load(c.Args().First())
if err != nil { if err != nil {
return err return err
} }

View File

@ -1,5 +1,7 @@
package commands package commands
func cmdIP(c CommandLine) error { import "github.com/docker/machine/libmachine"
return runActionWithContext("ip", c)
func cmdIP(c CommandLine, api libmachine.API) error {
return runAction("ip", c, api)
} }

View File

@ -1,5 +1,7 @@
package commands package commands
func cmdKill(c CommandLine) error { import "github.com/docker/machine/libmachine"
return runActionWithContext("kill", c)
func cmdKill(c CommandLine, api libmachine.API) error {
return runAction("kill", c, api)
} }

View File

@ -10,9 +10,11 @@ import (
"text/tabwriter" "text/tabwriter"
"time" "time"
"github.com/docker/machine/libmachine"
"github.com/docker/machine/libmachine/drivers" "github.com/docker/machine/libmachine/drivers"
"github.com/docker/machine/libmachine/host" "github.com/docker/machine/libmachine/host"
"github.com/docker/machine/libmachine/log" "github.com/docker/machine/libmachine/log"
"github.com/docker/machine/libmachine/persist"
"github.com/docker/machine/libmachine/state" "github.com/docker/machine/libmachine/state"
"github.com/docker/machine/libmachine/swarm" "github.com/docker/machine/libmachine/swarm"
"github.com/skarademir/naturalsort" "github.com/skarademir/naturalsort"
@ -39,15 +41,14 @@ type HostListItem struct {
SwarmOptions *swarm.Options SwarmOptions *swarm.Options
} }
func cmdLs(c CommandLine) error { func cmdLs(c CommandLine, api libmachine.API) error {
quiet := c.Bool("quiet") quiet := c.Bool("quiet")
filters, err := parseFilters(c.StringSlice("filter")) filters, err := parseFilters(c.StringSlice("filter"))
if err != nil { if err != nil {
return err return err
} }
store := getStore(c) hostList, err := persist.LoadAllHosts(api)
hostList, err := listHosts(store)
if err != nil { if err != nil {
return err return err
} }
@ -261,15 +262,9 @@ func attemptGetHostState(h *host.Host, stateQueryChan chan<- HostListItem) {
close(stateCh) close(stateCh)
close(urlCh) close(urlCh)
active, err := isActive(currentState, url)
if err != nil {
log.Errorf("error determining if host is active for host %s: %s",
h.Name, err)
}
stateQueryChan <- HostListItem{ stateQueryChan <- HostListItem{
Name: h.Name, Name: h.Name,
Active: active, Active: isActive(currentState, url),
DriverName: h.Driver.DriverName(), DriverName: h.Driver.DriverName(),
State: currentState, State: currentState,
URL: url, URL: url,
@ -332,14 +327,14 @@ func sortHostListItemsByName(items []HostListItem) {
// IsActive provides a single function for determining if a host is active // IsActive provides a single function for determining if a host is active
// based on both the url and if the host is stopped. // based on both the url and if the host is stopped.
func isActive(currentState state.State, url string) (bool, error) { func isActive(currentState state.State, url string) bool {
if currentState != state.Running { dockerHost := os.Getenv("DOCKER_HOST")
return false, nil
}
// TODO: hard-coding the swarm port is a travesty... // TODO: hard-coding the swarm port is a travesty...
dockerHost := os.Getenv("DOCKER_HOST")
deSwarmedHost := strings.Replace(dockerHost, ":3376", ":2376", 1) deSwarmedHost := strings.Replace(dockerHost, ":3376", ":2376", 1)
if dockerHost == url || deSwarmedHost == url {
return currentState == state.Running
}
return dockerHost == url || deSwarmedHost == url, nil return false
} }

View File

@ -467,9 +467,8 @@ func TestIsActive(t *testing.T) {
os.Setenv("DOCKER_HOST", c.dockerHost) os.Setenv("DOCKER_HOST", c.dockerHost)
} }
actual, err := isActive(c.state, "tcp://1.2.3.4:2376") actual := isActive(c.state, "tcp://1.2.3.4:2376")
assert.Equal(t, c.expected, actual, "IsActive(%s, \"%s\") should return %v, but didn't", c.state, c.dockerHost, c.expected) assert.Equal(t, c.expected, actual, "IsActive(%s, \"%s\") should return %v, but didn't", c.state, c.dockerHost, c.expected)
assert.NoError(t, err)
} }
} }

View File

@ -1,10 +1,11 @@
package commands package commands
import ( import (
"github.com/docker/machine/libmachine"
"github.com/docker/machine/libmachine/log" "github.com/docker/machine/libmachine/log"
) )
func cmdRegenerateCerts(c CommandLine) error { func cmdRegenerateCerts(c CommandLine, api libmachine.API) error {
if !c.Bool("force") { if !c.Bool("force") {
ok, err := confirmInput("Regenerate TLS machine certs? Warning: this is irreversible.") ok, err := confirmInput("Regenerate TLS machine certs? Warning: this is irreversible.")
if err != nil { if err != nil {
@ -18,5 +19,5 @@ func cmdRegenerateCerts(c CommandLine) error {
log.Infof("Regenerating TLS certificates") log.Infof("Regenerating TLS certificates")
return runActionWithContext("configureAuth", c) return runAction("configureAuth", c, api)
} }

View File

@ -1,11 +1,12 @@
package commands package commands
import ( import (
"github.com/docker/machine/libmachine"
"github.com/docker/machine/libmachine/log" "github.com/docker/machine/libmachine/log"
) )
func cmdRestart(c CommandLine) error { func cmdRestart(c CommandLine, api libmachine.API) error {
if err := runActionWithContext("restart", c); err != nil { if err := runAction("restart", c, api); err != nil {
return err return err
} }

View File

@ -4,20 +4,20 @@ import (
"errors" "errors"
"fmt" "fmt"
"github.com/docker/machine/libmachine"
"github.com/docker/machine/libmachine/log" "github.com/docker/machine/libmachine/log"
) )
func cmdRm(c CommandLine) error { func cmdRm(c CommandLine, api libmachine.API) error {
if len(c.Args()) == 0 { if len(c.Args()) == 0 {
c.ShowHelp() c.ShowHelp()
return errors.New("You must specify a machine name") return errors.New("You must specify a machine name")
} }
force := c.Bool("force") force := c.Bool("force")
store := getStore(c)
for _, hostName := range c.Args() { for _, hostName := range c.Args() {
h, err := loadHost(store, hostName) h, err := api.Load(hostName)
if err != nil { if err != nil {
return fmt.Errorf("Error removing host %q: %s", hostName, err) return fmt.Errorf("Error removing host %q: %s", hostName, err)
} }
@ -29,7 +29,7 @@ func cmdRm(c CommandLine) error {
} }
} }
if err := store.Remove(hostName); err != nil { if err := api.Remove(hostName); err != nil {
log.Errorf("Error removing machine %q from store: %s", hostName, err) log.Errorf("Error removing machine %q from store: %s", hostName, err)
} else { } else {
log.Infof("Successfully removed %s", hostName) log.Infof("Successfully removed %s", hostName)

View File

@ -7,6 +7,7 @@ import (
"os/exec" "os/exec"
"strings" "strings"
"github.com/docker/machine/libmachine"
"github.com/docker/machine/libmachine/log" "github.com/docker/machine/libmachine/log"
"github.com/docker/machine/libmachine/persist" "github.com/docker/machine/libmachine/persist"
) )
@ -44,7 +45,7 @@ type storeHostInfoLoader struct {
} }
func (s *storeHostInfoLoader) load(name string) (HostInfo, error) { func (s *storeHostInfoLoader) load(name string) (HostInfo, error) {
host, err := loadHost(s.store, name) host, err := s.store.Load(name)
if err != nil { if err != nil {
return nil, fmt.Errorf("Error loading host: %s", err) return nil, fmt.Errorf("Error loading host: %s", err)
} }
@ -52,7 +53,7 @@ func (s *storeHostInfoLoader) load(name string) (HostInfo, error) {
return host.Driver, nil return host.Driver, nil
} }
func cmdScp(c CommandLine) error { func cmdScp(c CommandLine, api libmachine.API) error {
args := c.Args() args := c.Args()
if len(args) != 2 { if len(args) != 2 {
c.ShowHelp() c.ShowHelp()
@ -62,8 +63,7 @@ func cmdScp(c CommandLine) error {
src := args[0] src := args[0]
dest := args[1] dest := args[1]
store := getStore(c) hostInfoLoader := &storeHostInfoLoader{api}
hostInfoLoader := &storeHostInfoLoader{store}
cmd, err := getScpCmd(src, dest, c.Bool("recursive"), hostInfoLoader) cmd, err := getScpCmd(src, dest, c.Bool("recursive"), hostInfoLoader)
if err != nil { if err != nil {

View File

@ -3,10 +3,11 @@ package commands
import ( import (
"fmt" "fmt"
"github.com/docker/machine/libmachine"
"github.com/docker/machine/libmachine/state" "github.com/docker/machine/libmachine/state"
) )
func cmdSSH(c CommandLine) error { func cmdSSH(c CommandLine, api libmachine.API) error {
// Check for help flag -- Needed due to SkipFlagParsing // Check for help flag -- Needed due to SkipFlagParsing
for _, arg := range c.Args() { for _, arg := range c.Args() {
if arg == "-help" || arg == "--help" || arg == "-h" { if arg == "-help" || arg == "--help" || arg == "-h" {
@ -20,8 +21,7 @@ func cmdSSH(c CommandLine) error {
return ErrExpectedOneMachine return ErrExpectedOneMachine
} }
store := getStore(c) host, err := api.Load(name)
host, err := loadHost(store, name)
if err != nil { if err != nil {
return err return err
} }

View File

@ -1,11 +1,12 @@
package commands package commands
import ( import (
"github.com/docker/machine/libmachine"
"github.com/docker/machine/libmachine/log" "github.com/docker/machine/libmachine/log"
) )
func cmdStart(c CommandLine) error { func cmdStart(c CommandLine, api libmachine.API) error {
if err := runActionWithContext("start", c); err != nil { if err := runAction("start", c, api); err != nil {
return err return err
} }

View File

@ -1,15 +1,16 @@
package commands package commands
import ( import (
"github.com/docker/machine/libmachine"
"github.com/docker/machine/libmachine/log" "github.com/docker/machine/libmachine/log"
) )
func cmdStatus(c CommandLine) error { func cmdStatus(c CommandLine, api libmachine.API) error {
if len(c.Args()) != 1 { if len(c.Args()) != 1 {
return ErrExpectedOneMachine return ErrExpectedOneMachine
} }
host, err := getFirstArgHost(c) host, err := api.Load(c.Args().First())
if err != nil { if err != nil {
return err return err
} }

View File

@ -1,5 +1,7 @@
package commands package commands
func cmdStop(c CommandLine) error { import "github.com/docker/machine/libmachine"
return runActionWithContext("stop", c)
func cmdStop(c CommandLine, api libmachine.API) error {
return runAction("stop", c, api)
} }

View File

@ -1,5 +1,7 @@
package commands package commands
func cmdUpgrade(c CommandLine) error { import "github.com/docker/machine/libmachine"
return runActionWithContext("upgrade", c)
func cmdUpgrade(c CommandLine, api libmachine.API) error {
return runAction("upgrade", c, api)
} }

View File

@ -2,14 +2,16 @@ package commands
import ( import (
"fmt" "fmt"
"github.com/docker/machine/libmachine"
) )
func cmdURL(c CommandLine) error { func cmdURL(c CommandLine, api libmachine.API) error {
if len(c.Args()) != 1 { if len(c.Args()) != 1 {
return ErrExpectedOneMachine return ErrExpectedOneMachine
} }
host, err := getFirstArgHost(c) host, err := api.Load(c.Args().First())
if err != nil { if err != nil {
return err return err
} }

View File

@ -1,6 +1,8 @@
package commands package commands
func cmdVersion(c CommandLine) error { import "github.com/docker/machine/libmachine"
func cmdVersion(c CommandLine, api libmachine.API) error {
c.ShowVersion() c.ShowVersion()
return nil return nil
} }

View File

@ -8,10 +8,10 @@ import (
"os" "os"
"time" "time"
"github.com/docker/machine/libmachine"
"github.com/docker/machine/libmachine/drivers" "github.com/docker/machine/libmachine/drivers"
"github.com/docker/machine/libmachine/drivers/plugin/localbinary" "github.com/docker/machine/libmachine/drivers/plugin/localbinary"
"github.com/docker/machine/libmachine/drivers/rpc" "github.com/docker/machine/libmachine/drivers/rpc"
"github.com/docker/machine/libmachine/log"
"github.com/docker/machine/libmachine/version" "github.com/docker/machine/libmachine/version"
) )
@ -29,7 +29,7 @@ Please use this plugin through the main 'docker-machine' binary.
os.Exit(1) os.Exit(1)
} }
libmachine.SetDebug(true) log.IsDebug = true
rpcd := rpcdriver.NewRPCServerDriver(d) rpcd := rpcdriver.NewRPCServerDriver(d)
rpc.Register(rpcd) rpc.Register(rpcd)

View File

@ -2,6 +2,7 @@ package main
// Sample Virtualbox create independent of Machine CLI. // Sample Virtualbox create independent of Machine CLI.
import ( import (
"encoding/json"
"fmt" "fmt"
"os" "os"
@ -11,16 +12,11 @@ import (
) )
func main() { func main() {
libmachine.SetDebug(true) log.IsDebug = true
log.SetOutWriter(os.Stdout) log.SetOutWriter(os.Stdout)
log.SetErrWriter(os.Stderr) log.SetErrWriter(os.Stderr)
// returns the familiar store at $HOME/.docker/machine client := libmachine.NewClient("/tmp/automatic")
store := libmachine.GetDefaultStore()
// over-ride this for now (don't want to muck with my default store)
store.Path = "/tmp/automatic"
hostName := "myfunhost" hostName := "myfunhost"
@ -29,14 +25,24 @@ func main() {
driver.CPU = 2 driver.CPU = 2
driver.Memory = 2048 driver.Memory = 2048
h, err := store.NewHost(driver) data, err := json.Marshal(driver)
if err != nil {
log.Fatal(err)
}
pluginDriver, err := client.NewPluginDriver("virtualbox", data)
if err != nil {
log.Fatal(err)
}
h, err := client.NewHost(pluginDriver)
if err != nil { if err != nil {
log.Fatal(err) log.Fatal(err)
} }
h.HostOptions.EngineOptions.StorageDriver = "overlay" h.HostOptions.EngineOptions.StorageDriver = "overlay"
if err := libmachine.Create(store, h); err != nil { if err := client.Create(h); err != nil {
log.Fatal(err) log.Fatal(err)
} }

View File

@ -4,29 +4,81 @@ import (
"fmt" "fmt"
"path/filepath" "path/filepath"
"github.com/docker/machine/libmachine/auth"
"github.com/docker/machine/libmachine/cert" "github.com/docker/machine/libmachine/cert"
"github.com/docker/machine/libmachine/drivers" "github.com/docker/machine/libmachine/drivers"
"github.com/docker/machine/libmachine/engine"
"github.com/docker/machine/libmachine/host" "github.com/docker/machine/libmachine/host"
"github.com/docker/machine/libmachine/log" "github.com/docker/machine/libmachine/log"
"github.com/docker/machine/libmachine/mcnutils" "github.com/docker/machine/libmachine/mcnutils"
"github.com/docker/machine/libmachine/persist" "github.com/docker/machine/libmachine/persist"
"github.com/docker/machine/libmachine/provision" "github.com/docker/machine/libmachine/provision"
"github.com/docker/machine/libmachine/ssh"
"github.com/docker/machine/libmachine/state" "github.com/docker/machine/libmachine/state"
"github.com/docker/machine/libmachine/swarm"
"github.com/docker/machine/libmachine/version"
) )
func GetDefaultStore() *persist.Filestore { type API interface {
homeDir := mcnutils.GetHomeDir() persist.Store
certsDir := filepath.Join(homeDir, ".docker", "machine", "certs") NewPluginDriver(string, []byte) (drivers.Driver, error)
return &persist.Filestore{ NewHost(drivers.Driver) (*host.Host, error)
Path: homeDir, Create(h *host.Host) error
CaCertPath: certsDir, }
CaPrivateKeyPath: certsDir,
type Client struct {
*persist.PluginStore
IsDebug bool
SSHClientType ssh.ClientType
GithubAPIToken string
}
func NewClient(storePath string) *Client {
certsDir := filepath.Join(storePath, ".docker", "machine", "certs")
return &Client{
IsDebug: false,
SSHClientType: ssh.External,
PluginStore: persist.NewPluginStore(storePath, certsDir, certsDir),
} }
} }
func (api *Client) NewHost(driver drivers.Driver) (*host.Host, error) {
certDir := filepath.Join(api.Path, "certs")
hostOptions := &host.Options{
AuthOptions: &auth.Options{
CertDir: certDir,
CaCertPath: filepath.Join(certDir, "ca.pem"),
CaPrivateKeyPath: filepath.Join(certDir, "ca-key.pem"),
ClientCertPath: filepath.Join(certDir, "cert.pem"),
ClientKeyPath: filepath.Join(certDir, "key.pem"),
ServerCertPath: filepath.Join(api.GetMachinesDir(), "server.pem"),
ServerKeyPath: filepath.Join(api.GetMachinesDir(), "server-key.pem"),
},
EngineOptions: &engine.Options{
InstallURL: "https://get.docker.com",
StorageDriver: "aufs",
TLSVerify: true,
},
SwarmOptions: &swarm.Options{
Host: "tcp://0.0.0.0:3376",
Image: "swarm:latest",
Strategy: "spread",
},
}
return &host.Host{
ConfigVersion: version.ConfigVersion,
Name: driver.GetMachineName(),
Driver: driver,
DriverName: driver.DriverName(),
HostOptions: hostOptions,
}, nil
}
// Create is the wrapper method which covers all of the boilerplate around // Create is the wrapper method which covers all of the boilerplate around
// actually creating, provisioning, and persisting an instance in the store. // actually creating, provisioning, and persisting an instance in the store.
func Create(store persist.Store, h *host.Host) error { func (api *Client) Create(h *host.Host) error {
if err := cert.BootstrapCertificates(h.HostOptions.AuthOptions); err != nil { if err := cert.BootstrapCertificates(h.HostOptions.AuthOptions); err != nil {
return fmt.Errorf("Error generating certificates: %s", err) return fmt.Errorf("Error generating certificates: %s", err)
} }
@ -37,7 +89,7 @@ func Create(store persist.Store, h *host.Host) error {
return fmt.Errorf("Error with pre-create check: %s", err) return fmt.Errorf("Error with pre-create check: %s", err)
} }
if err := store.Save(h); err != nil { if err := api.Save(h); err != nil {
return fmt.Errorf("Error saving host to store before attempting creation: %s", err) return fmt.Errorf("Error saving host to store before attempting creation: %s", err)
} }
@ -47,7 +99,7 @@ func Create(store persist.Store, h *host.Host) error {
return fmt.Errorf("Error in driver during machine creation: %s", err) return fmt.Errorf("Error in driver during machine creation: %s", err)
} }
if err := store.Save(h); err != nil { if err := api.Save(h); err != nil {
return fmt.Errorf("Error saving host to store after attempting creation: %s", err) return fmt.Errorf("Error saving host to store after attempting creation: %s", err)
} }
@ -79,7 +131,3 @@ func Create(store persist.Store, h *host.Host) error {
return nil return nil
} }
func SetDebug(val bool) {
log.IsDebug = val
}

View File

@ -8,15 +8,8 @@ import (
"path/filepath" "path/filepath"
"strings" "strings"
"github.com/docker/machine/libmachine/auth"
"github.com/docker/machine/libmachine/drivers"
"github.com/docker/machine/libmachine/drivers/rpc"
"github.com/docker/machine/libmachine/engine"
"github.com/docker/machine/libmachine/host" "github.com/docker/machine/libmachine/host"
"github.com/docker/machine/libmachine/log"
"github.com/docker/machine/libmachine/mcnerror" "github.com/docker/machine/libmachine/mcnerror"
"github.com/docker/machine/libmachine/swarm"
"github.com/docker/machine/libmachine/version"
) )
type Filestore struct { type Filestore struct {
@ -25,7 +18,15 @@ type Filestore struct {
CaPrivateKeyPath string CaPrivateKeyPath string
} }
func (s Filestore) getMachinesDir() string { func NewFilestore(path, caCertPath, caPrivateKeyPath string) *Filestore {
return &Filestore{
Path: path,
CaCertPath: caCertPath,
CaPrivateKeyPath: caPrivateKeyPath,
}
}
func (s Filestore) GetMachinesDir() string {
return filepath.Join(s.Path, "machines") return filepath.Join(s.Path, "machines")
} }
@ -34,31 +35,12 @@ func (s Filestore) saveToFile(data []byte, file string) error {
} }
func (s Filestore) Save(host *host.Host) error { func (s Filestore) Save(host *host.Host) error {
if serialDriver, ok := host.Driver.(*drivers.SerialDriver); ok {
// Unwrap Driver
host.Driver = serialDriver.Driver
// Re-wrap Driver when done
defer func() {
host.Driver = serialDriver
}()
}
// TODO: Does this belong here?
if rpcClientDriver, ok := host.Driver.(*rpcdriver.RPCClientDriver); ok {
data, err := rpcClientDriver.GetConfigRaw()
if err != nil {
return fmt.Errorf("Error getting raw config for driver: %s", err)
}
host.RawDriver = data
}
data, err := json.MarshalIndent(host, "", " ") data, err := json.MarshalIndent(host, "", " ")
if err != nil { if err != nil {
return err return err
} }
hostPath := filepath.Join(s.getMachinesDir(), host.Name) hostPath := filepath.Join(s.GetMachinesDir(), host.Name)
// Ensure that the directory we want to save to exists. // Ensure that the directory we want to save to exists.
if err := os.MkdirAll(hostPath, 0700); err != nil { if err := os.MkdirAll(hostPath, 0700); err != nil {
@ -69,34 +51,29 @@ func (s Filestore) Save(host *host.Host) error {
} }
func (s Filestore) Remove(name string) error { func (s Filestore) Remove(name string) error {
hostPath := filepath.Join(s.getMachinesDir(), name) hostPath := filepath.Join(s.GetMachinesDir(), name)
return os.RemoveAll(hostPath) return os.RemoveAll(hostPath)
} }
func (s Filestore) List() ([]*host.Host, error) { func (s Filestore) List() ([]string, error) {
dir, err := ioutil.ReadDir(s.getMachinesDir()) dir, err := ioutil.ReadDir(s.GetMachinesDir())
if err != nil && !os.IsNotExist(err) { if err != nil && !os.IsNotExist(err) {
return nil, err return nil, err
} }
hosts := []*host.Host{} hostNames := []string{}
for _, file := range dir { for _, file := range dir {
if file.IsDir() && !strings.HasPrefix(file.Name(), ".") { if file.IsDir() && !strings.HasPrefix(file.Name(), ".") {
host, err := s.Load(file.Name()) hostNames = append(hostNames, file.Name())
if err != nil {
log.Errorf("error loading host %q: %s", file.Name(), err)
continue
}
hosts = append(hosts, host)
} }
} }
return hosts, nil return hostNames, nil
} }
func (s Filestore) Exists(name string) (bool, error) { func (s Filestore) Exists(name string) (bool, error) {
_, err := os.Stat(filepath.Join(s.getMachinesDir(), name)) _, err := os.Stat(filepath.Join(s.GetMachinesDir(), name))
if os.IsNotExist(err) { if os.IsNotExist(err) {
return false, nil return false, nil
@ -108,7 +85,7 @@ func (s Filestore) Exists(name string) (bool, error) {
} }
func (s Filestore) loadConfig(h *host.Host) error { func (s Filestore) loadConfig(h *host.Host) error {
data, err := ioutil.ReadFile(filepath.Join(s.getMachinesDir(), h.Name, "config.json")) data, err := ioutil.ReadFile(filepath.Join(s.GetMachinesDir(), h.Name, "config.json"))
if err != nil { if err != nil {
return err return err
} }
@ -128,7 +105,7 @@ func (s Filestore) loadConfig(h *host.Host) error {
// If we end up performing a migration, we should save afterwards so we don't have to do it again on subsequent invocations. // If we end up performing a migration, we should save afterwards so we don't have to do it again on subsequent invocations.
if migrationPerformed { if migrationPerformed {
if err := s.saveToFile(data, filepath.Join(s.getMachinesDir(), h.Name, "config.json.bak")); err != nil { if err := s.saveToFile(data, filepath.Join(s.GetMachinesDir(), h.Name, "config.json.bak")); err != nil {
return fmt.Errorf("Error attempting to save backup after migration: %s", err) return fmt.Errorf("Error attempting to save backup after migration: %s", err)
} }
@ -138,11 +115,10 @@ func (s Filestore) loadConfig(h *host.Host) error {
} }
return nil return nil
} }
func (s Filestore) Load(name string) (*host.Host, error) { func (s Filestore) Load(name string) (*host.Host, error) {
hostPath := filepath.Join(s.getMachinesDir(), name) hostPath := filepath.Join(s.GetMachinesDir(), name)
if _, err := os.Stat(hostPath); os.IsNotExist(err) { if _, err := os.Stat(hostPath); os.IsNotExist(err) {
return nil, mcnerror.ErrHostDoesNotExist{ return nil, mcnerror.ErrHostDoesNotExist{
@ -160,37 +136,3 @@ func (s Filestore) Load(name string) (*host.Host, error) {
return host, nil return host, nil
} }
func (s Filestore) NewHost(driver drivers.Driver) (*host.Host, error) {
certDir := filepath.Join(s.Path, "certs")
hostOptions := &host.Options{
AuthOptions: &auth.Options{
CertDir: certDir,
CaCertPath: filepath.Join(certDir, "ca.pem"),
CaPrivateKeyPath: filepath.Join(certDir, "ca-key.pem"),
ClientCertPath: filepath.Join(certDir, "cert.pem"),
ClientKeyPath: filepath.Join(certDir, "key.pem"),
ServerCertPath: filepath.Join(s.getMachinesDir(), "server.pem"),
ServerKeyPath: filepath.Join(s.getMachinesDir(), "server-key.pem"),
},
EngineOptions: &engine.Options{
InstallURL: "https://get.docker.com",
StorageDriver: "aufs",
TLSVerify: true,
},
SwarmOptions: &swarm.Options{
Host: "tcp://0.0.0.0:3376",
Image: "swarm:latest",
Strategy: "spread",
},
}
return &host.Host{
ConfigVersion: version.ConfigVersion,
Name: driver.GetMachineName(),
Driver: driver,
DriverName: driver.DriverName(),
HostOptions: hostOptions,
}, nil
}

View File

@ -47,7 +47,7 @@ func TestStoreSave(t *testing.T) {
t.Fatal(err) t.Fatal(err)
} }
path := filepath.Join(store.getMachinesDir(), h.Name) path := filepath.Join(store.GetMachinesDir(), h.Name)
if _, err := os.Stat(path); os.IsNotExist(err) { if _, err := os.Stat(path); os.IsNotExist(err) {
t.Fatalf("Host path doesn't exist: %s", path) t.Fatalf("Host path doesn't exist: %s", path)
} }
@ -67,7 +67,7 @@ func TestStoreRemove(t *testing.T) {
t.Fatal(err) t.Fatal(err)
} }
path := filepath.Join(store.getMachinesDir(), h.Name) path := filepath.Join(store.GetMachinesDir(), h.Name)
if _, err := os.Stat(path); os.IsNotExist(err) { if _, err := os.Stat(path); os.IsNotExist(err) {
t.Fatalf("Host path doesn't exist: %s", path) t.Fatalf("Host path doesn't exist: %s", path)
} }
@ -101,8 +101,8 @@ func TestStoreList(t *testing.T) {
t.Fatalf("List returned %d items, expected 1", len(hosts)) t.Fatalf("List returned %d items, expected 1", len(hosts))
} }
if hosts[0].Name != h.Name { if hosts[0] != h.Name {
t.Fatalf("hosts[0] name is incorrect, got: %s", hosts[0].Name) t.Fatalf("hosts[0] name is incorrect, got: %s", hosts[0])
} }
} }

View File

@ -0,0 +1,85 @@
package persist
import (
"fmt"
"github.com/docker/machine/drivers/errdriver"
"github.com/docker/machine/libmachine/drivers"
"github.com/docker/machine/libmachine/drivers/plugin/localbinary"
"github.com/docker/machine/libmachine/drivers/rpc"
"github.com/docker/machine/libmachine/host"
)
type PluginDriverFactory interface {
NewPluginDriver(string, []byte) (drivers.Driver, error)
}
type RPCPluginDriverFactory struct{}
type PluginStore struct {
*Filestore
PluginDriverFactory
}
func (factory RPCPluginDriverFactory) NewPluginDriver(driverName string, rawContent []byte) (drivers.Driver, error) {
d, err := rpcdriver.NewRPCClientDriver(rawContent, driverName)
if err != nil {
// Not being able to find a driver binary is a "known error"
if _, ok := err.(localbinary.ErrPluginBinaryNotFound); ok {
return errdriver.NewDriver(driverName), nil
}
return nil, err
}
if driverName == "virtualbox" {
return drivers.NewSerialDriver(d), nil
}
return d, nil
}
func NewPluginStore(path, caCertPath, caPrivateKeyPath string) *PluginStore {
return &PluginStore{
Filestore: NewFilestore(path, caCertPath, caPrivateKeyPath),
PluginDriverFactory: RPCPluginDriverFactory{},
}
}
func (ps PluginStore) Save(host *host.Host) error {
if serialDriver, ok := host.Driver.(*drivers.SerialDriver); ok {
// Unwrap Driver
host.Driver = serialDriver.Driver
// Re-wrap Driver when done
defer func() {
host.Driver = serialDriver
}()
}
// TODO: Does this belong here?
if rpcClientDriver, ok := host.Driver.(*rpcdriver.RPCClientDriver); ok {
data, err := rpcClientDriver.GetConfigRaw()
if err != nil {
return fmt.Errorf("Error getting raw config for driver: %s", err)
}
host.RawDriver = data
}
return ps.Filestore.Save(host)
}
func (ps PluginStore) Load(name string) (*host.Host, error) {
h, err := ps.Filestore.Load(name)
if err != nil {
return nil, err
}
d, err := ps.NewPluginDriver(h.DriverName, h.RawDriver)
if err != nil {
return nil, err
}
h.Driver = d
return h, nil
}

View File

@ -1,21 +1,15 @@
package persist package persist
import ( import "github.com/docker/machine/libmachine/host"
"github.com/docker/machine/libmachine/drivers"
"github.com/docker/machine/libmachine/host"
)
type Store interface { type Store interface {
// Exists returns whether a machine exists or not // Exists returns whether a machine exists or not
Exists(name string) (bool, error) Exists(name string) (bool, error)
// NewHost will initialize a new host machine
NewHost(driver drivers.Driver) (*host.Host, error)
// List returns a list of all hosts in the store // List returns a list of all hosts in the store
List() ([]*host.Host, error) List() ([]string, error)
// Get loads a host by name // Load loads a host by name
Load(name string) (*host.Host, error) Load(name string) (*host.Host, error)
// Remove removes a machine from the store // Remove removes a machine from the store
@ -24,3 +18,28 @@ type Store interface {
// Save persists a machine in the store // Save persists a machine in the store
Save(host *host.Host) error Save(host *host.Host) error
} }
func LoadHosts(s Store, hostNames []string) ([]*host.Host, error) {
loadedHosts := []*host.Host{}
for _, hostName := range hostNames {
h, err := s.Load(hostName)
if err != nil {
// TODO: (nathanleclaire) Should these be bundled up
// into one error instead of exiting?
return nil, err
}
loadedHosts = append(loadedHosts, h)
}
return loadedHosts, nil
}
func LoadAllHosts(s Store) ([]*host.Host, error) {
hostNames, err := s.List()
if err != nil {
return nil, err
}
return LoadHosts(s, hostNames)
}

View File

@ -73,7 +73,7 @@ load ${BASE_TEST_DIR}/helpers.bash
@test "none: rm non existent machine fails 'machine rm ∞'" { @test "none: rm non existent machine fails 'machine rm ∞'" {
run machine rm ∞ run machine rm ∞
[ "$status" -eq 1 ] [ "$status" -eq 1 ]
[[ ${lines[0]} == "Error removing host \"∞\": Loading host from store failed: Host does not exist: \"∞\"" ]] [[ ${lines[0]} == "Error removing host \"∞\": Host does not exist: \"∞\"" ]]
} }
@test "none: rm is successful 'machine rm 0'" { @test "none: rm is successful 'machine rm 0'" {