docs/commands_test.go

592 lines
12 KiB
Go

package main
import (
"bytes"
"flag"
"fmt"
"io"
"io/ioutil"
"os"
"path/filepath"
"strings"
"testing"
"github.com/codegangsta/cli"
"github.com/docker/machine/drivers/fakedriver"
"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/docker/machine/state"
)
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
}
func getDefaultTestHost() (*libmachine.Host, error) {
engineOptions := &engine.EngineOptions{}
swarmOptions := &swarm.SwarmOptions{
Master: false,
Host: "",
Discovery: "",
Address: "",
}
authConfig := &auth.AuthOptions{
CaCertPath: hostTestCaCert,
PrivateKeyPath: hostTestPrivateKey,
}
hostConfig := &libmachine.HostOptions{
EngineConfig: engineOptions,
SwarmConfig: swarmOptions,
AuthConfig: authConfig,
}
host, err := libmachine.NewHost(hostTestName, hostTestDriverName, hostConfig)
if err != nil {
return nil, err
}
flags := getTestDriverFlags()
if err := host.Driver.SetConfigFromFlags(flags); err != nil {
return nil, err
}
return host, nil
}
type DriverOptionsMock struct {
Data map[string]interface{}
}
func (d DriverOptionsMock) String(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 TestGetHostState(t *testing.T) {
defer cleanup()
hostListItems := make(chan hostListItem)
store, err := getTestStore()
if err != nil {
t.Fatal(err)
}
hosts := []libmachine.Host{
{
Name: "foo",
DriverName: "fakedriver",
Driver: &fakedriver.FakeDriver{
MockState: state.Running,
},
StorePath: store.GetPath(),
HostConfig: &libmachine.HostOptions{
SwarmConfig: &swarm.SwarmOptions{
Master: false,
Address: "",
Discovery: "",
},
},
},
{
Name: "bar",
DriverName: "fakedriver",
Driver: &fakedriver.FakeDriver{
MockState: state.Stopped,
},
StorePath: store.GetPath(),
HostConfig: &libmachine.HostOptions{
SwarmConfig: &swarm.SwarmOptions{
Master: false,
Address: "",
Discovery: "",
},
},
},
{
Name: "baz",
DriverName: "fakedriver",
Driver: &fakedriver.FakeDriver{
MockState: state.Running,
},
StorePath: store.GetPath(),
HostConfig: &libmachine.HostOptions{
SwarmConfig: &swarm.SwarmOptions{
Master: false,
Address: "",
Discovery: "",
},
},
},
}
for _, h := range hosts {
if err := store.Save(&h); err != nil {
t.Fatal(err)
}
}
expected := map[string]state.State{
"foo": state.Running,
"bar": state.Stopped,
"baz": state.Running,
}
items := []hostListItem{}
for _, host := range hosts {
go getHostState(host, store, hostListItems)
}
for i := 0; i < len(hosts); i++ {
items = append(items, <-hostListItems)
}
for _, item := range items {
if expected[item.Name] != item.State {
t.Fatal("Expected state did not match for item", item)
}
}
}
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
// stopped states.
machines := []*libmachine.Host{
{
Name: "foo",
DriverName: "fakedriver",
Driver: &fakedriver.FakeDriver{
MockState: state.Running,
},
StorePath: storePath,
},
{
Name: "bar",
DriverName: "fakedriver",
Driver: &fakedriver.FakeDriver{
MockState: state.Stopped,
},
StorePath: storePath,
},
{
Name: "baz",
// Ssh, don't tell anyone but this
// driver only _thinks_ it's named
// virtualbox... (to test serial actions)
// It's actually FakeDriver!
DriverName: "virtualbox",
Driver: &fakedriver.FakeDriver{
MockState: state.Stopped,
},
StorePath: storePath,
},
{
Name: "spam",
DriverName: "virtualbox",
Driver: &fakedriver.FakeDriver{
MockState: state.Running,
},
StorePath: storePath,
},
{
Name: "eggs",
DriverName: "fakedriver",
Driver: &fakedriver.FakeDriver{
MockState: state.Stopped,
},
StorePath: storePath,
},
{
Name: "ham",
DriverName: "fakedriver",
Driver: &fakedriver.FakeDriver{
MockState: state.Running,
},
StorePath: storePath,
},
}
runActionForeachMachine("start", machines)
expected := map[string]state.State{
"foo": state.Running,
"bar": state.Running,
"baz": state.Running,
"spam": state.Running,
"eggs": state.Running,
"ham": state.Running,
}
for _, machine := range machines {
state, _ := machine.Driver.GetState()
if expected[machine.Name] != state {
t.Fatalf("Expected machine %s to have state %s, got state %s", machine.Name, state, expected[machine.Name])
}
}
// OK, now let's stop them all!
expected = map[string]state.State{
"foo": state.Stopped,
"bar": state.Stopped,
"baz": state.Stopped,
"spam": state.Stopped,
"eggs": state.Stopped,
"ham": state.Stopped,
}
runActionForeachMachine("stop", machines)
for _, machine := range machines {
state, _ := machine.Driver.GetState()
if expected[machine.Name] != state {
t.Fatalf("Expected machine %s to have state %s, got state %s", machine.Name, state, expected[machine.Name])
}
}
}
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)
}
mcn, err := libmachine.New(store)
if err != nil {
t.Fatal(err)
}
flags := getTestDriverFlags()
hostOptions := &libmachine.HostOptions{
EngineConfig: &engine.EngineOptions{},
SwarmConfig: &swarm.SwarmOptions{
Master: false,
Discovery: "",
Address: "",
Host: "",
},
AuthConfig: &auth.AuthOptions{},
}
host, err := mcn.Create("test-a", "none", hostOptions, flags)
if err != nil {
t.Fatal(err)
}
if err := store.SetActive(host); err != nil {
t.Fatalf("error setting active 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)
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")
}
}
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)
}
mcn, err := libmachine.New(store)
if err != nil {
t.Fatal(err)
}
hostOptions := &libmachine.HostOptions{
EngineConfig: &engine.EngineOptions{},
SwarmConfig: &swarm.SwarmOptions{
Master: false,
Discovery: "",
Address: "",
Host: "",
},
AuthConfig: &auth.AuthOptions{},
}
host, err := mcn.Create("test-a", "none", hostOptions, flags)
if err != nil {
t.Fatal(err)
}
host, err = mcn.Get("test-a")
if err != nil {
t.Fatalf("error loading host: %v", err)
}
if err := mcn.SetActive(host); err != nil {
t.Fatalf("error setting active 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)
c := cli.NewContext(nil, set, set)
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") {
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",
}
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)
}
mcn, err := libmachine.New(store)
if err != nil {
t.Fatal(err)
}
hostOptions := &libmachine.HostOptions{
EngineConfig: &engine.EngineOptions{},
SwarmConfig: &swarm.SwarmOptions{
Master: false,
Discovery: "",
Address: "",
Host: "",
},
AuthConfig: &auth.AuthOptions{},
}
host, err := mcn.Create("test-a", "none", hostOptions, flags)
if err != nil {
t.Fatal(err)
}
host, err = mcn.Get("test-a")
if err != nil {
t.Fatalf("error loading host: %v", err)
}
if err := mcn.SetActive(host); err != nil {
t.Fatalf("error setting active 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)
c := cli.NewContext(nil, set, set)
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") {
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",
}
for k, v := range envvars {
if v != expected[k] {
t.Fatalf("Expected %s == <%s>, but was <%s>", k, expected[k], v)
}
}
}