Skip to content
This repository was archived by the owner on Feb 27, 2018. It is now read-only.
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
94 changes: 70 additions & 24 deletions cmds.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,6 @@ import (
"io/ioutil"
"net"
"os"
//"os/exec"
"path/filepath"
"runtime"
"strings"
Expand Down Expand Up @@ -80,6 +79,7 @@ func cmdInit() int {
m.OSType = "Linux26_64"
m.CPUs = uint(runtime.NumCPU())
m.Memory = B2D.Memory
m.SerialFile = B2D.SerialFile

m.Flag |= vbx.F_pae
m.Flag |= vbx.F_longmode // important: use x86-64 processor
Expand Down Expand Up @@ -232,27 +232,50 @@ func cmdUp() int {
return 1
}

logf("Waiting for SSH server to start...")
addr := fmt.Sprintf("localhost:%d", m.SSHPort)
const n = 10
// Try to connect to the SSH 10 times at 3 sec interval before giving up.
if err := read(addr, n, 3*time.Second); err != nil {
logf("Failed to connect to SSH port at %s after %d attempts. Last error: %v", addr, n, err)
if err := m.Refresh(); err != nil {
logf("Failed to start machine %q: %s", B2D.VM, err)
return 1
}
if m.State != vbx.Running {
logf("Failed to start machine %q (run again with -v for details)", B2D.VM)
return 1
}

logf("Waiting for VM to be started...")
//give the VM a little time to start, so we don't kill the Serial Pipe/Socket
time.Sleep(2)
natSSH := fmt.Sprintf("localhost:%d", B2D.SSHPort)
IP := ""
for i := 1; i < 30; i++ {
if B2D.Serial && runtime.GOOS != "windows" {
if IP = RequestIPFromSerialPort(m.SerialFile); IP != "" {
break
}
}
if err := read(natSSH, 1, 1*time.Second); err == nil {
IP = RequestIPFromSSH(m)
break
}

print(".")
}
print("\n")

logf("Started.")

if IP == "" {
logf("Auto detection of the VM's IP address.")
}
switch runtime.GOOS {
case "windows":
logf("Docker client does not run on Windows for now. Please use")
logf(" %s ssh", os.Args[0])
logf("to SSH into the VM instead.")
default:
// Check if $DOCKER_HOST ENV var is properly configured.
if os.Getenv("DOCKER_HOST") != fmt.Sprintf("tcp://localhost:%d", m.DockerPort) {
if os.Getenv("DOCKER_HOST") != fmt.Sprintf("tcp://%s:%d", IP, m.DockerPort) {
logf("To connect the Docker client to the Docker daemon, please set:")
logf(" export DOCKER_HOST=tcp://localhost:%d", m.DockerPort)
logf(" export DOCKER_HOST=tcp://%s:%d", IP, m.DockerPort)
}
}
return 0
Expand Down Expand Up @@ -433,6 +456,33 @@ func cmdIP() int {
return 1
}

IP := ""
if B2D.Serial {
for i := 1; i < 20; i++ {
if runtime.GOOS != "windows" {
if IP = RequestIPFromSerialPort(m.SerialFile); IP != "" {
break
}
}
}
}

if IP == "" {
IP = RequestIPFromSSH(m)
}
if IP != "" {
errf("\nThe VM's Host only interface IP address is: ")
fmt.Printf("%s", IP)
errf("\n\n")
} else {
errf("\nFailed to get VM Host only IP address.\n")
errf("\tWas the VM initilized using boot2docker?\n")
}
return 0
}

func RequestIPFromSSH(m *vbx.Machine) string {
// fall back to using the NAT port forwarded ssh
out, err := cmd(B2D.SSH,
//"-vvv", //TODO: add if its boot2docker -v
"-o", "StrictHostKeyChecking=no",
Expand All @@ -442,25 +492,21 @@ func cmdIP() int {
"docker@localhost",
"ip addr show dev eth1",
)
IP := ""
if err != nil {
logf("%s", err)
return 1
}
// parse to find: inet 192.168.59.103/24 brd 192.168.59.255 scope global eth1
lines := strings.Split(out, "\n")
for _, line := range lines {
vals := strings.Split(strings.TrimSpace(line), " ")
if vals[0] == "inet" {
ip := vals[1][:strings.Index(vals[1], "/")]
errf("\nThe VM's Host only interface IP address is: ")
fmt.Printf("%s", ip)
errf("\n\n")
return 0
} else {
// parse to find: inet 192.168.59.103/24 brd 192.168.59.255 scope global eth1
lines := strings.Split(out, "\n")
for _, line := range lines {
vals := strings.Split(strings.TrimSpace(line), " ")
if len(vals) >= 2 && vals[0] == "inet" {
IP = vals[1][:strings.Index(vals[1], "/")]
break
}
}
}
errf("\nFailed to get VM Host only IP address.\n")
errf("\tWas the VM initilized using boot2docker?\n")
return 0
return IP
}

// Download the boot2docker ISO image.
Expand Down
22 changes: 22 additions & 0 deletions config.go
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,10 @@ var B2D struct {
LowerIP net.IP
UpperIP net.IP
DHCPEnabled bool

// Serial console pipe/socket
Serial bool
SerialFile string
}

var (
Expand Down Expand Up @@ -138,6 +142,14 @@ func config() (*flag.FlagSet, error) {
flags.IPVar(&B2D.LowerIP, "lowerip", net.ParseIP("192.168.59.103"), "VirtualBox host-only network DHCP lower bound.")
flags.IPVar(&B2D.UpperIP, "upperip", net.ParseIP("192.168.59.254"), "VirtualBox host-only network DHCP upper bound.")

if runtime.GOOS != "windows" {
//SerialFile ~~ filepath.Join(dir, B2D.vm+".sock")
flags.StringVar(&B2D.SerialFile, "serialfile", "", "path to the serial socket/pipe.")
flags.BoolVar(&B2D.Serial, "serial", false, "try serial console to get IP address (experimental)")
} else {
B2D.Serial = false
}

// Set the defaults
if err := flags.Parse([]string{}); err != nil {
return nil, err
Expand All @@ -164,6 +176,16 @@ func config() (*flag.FlagSet, error) {

vbx.Verbose = B2D.Verbose
vbx.VBM = B2D.VBM

if B2D.SerialFile == "" {
if runtime.GOOS == "windows" {
//SerialFile ~~ filepath.Join(dir, B2D.vm+".sock")
B2D.SerialFile = `\\.\pipe\` + B2D.VM
} else {
B2D.SerialFile = filepath.Join(dir, B2D.VM+".sock")
}
}

return flags, nil
}

Expand Down
70 changes: 70 additions & 0 deletions util.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import (
"os"
"os/exec"
"path/filepath"
"regexp"
"strings"
"time"
)
Expand Down Expand Up @@ -167,3 +168,72 @@ func CopyFile(src, dst string) (int64, error) {
defer df.Close()
return io.Copy(df, sf)
}

func reader(r io.Reader) {
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It seems like there must be an easier way to soak up and ignore all data in a io.Reader than this... but I don't know off-hand what it is. Maybe someone else does.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yes please - I'm doing something wrong that kills the socket after a little while.

buf := make([]byte, 1024)
for {
_, err := io.ReadAtLeast(r, buf[:], 20)
if err != nil {
return
}
}
}

// use the serial port socket to ask what the VM's host only IP is
func RequestIPFromSerialPort(socket string) string {
c, err := net.Dial("unix", socket)

if err != nil {
return ""
}
defer c.Close()
c.SetDeadline(time.Now().Add(time.Second))

line := ""
_, err = c.Write([]byte("\r"))
_, err = c.Write([]byte("docker\r"))

IP := ""
fullLog := ""

for IP == "" {
_, err := c.Write([]byte("ip addr show dev eth1\r"))
if err != nil {
println(err)
break
}
time.Sleep(1 * time.Second)
buf := make([]byte, 1024)
for {
n, err := c.Read(buf[:])
if err != nil {
return IP
}
line = line + string(buf[0:n])
fullLog += string(buf[0:n])
if strings.Contains(line, "\n") {
//go looking for the string we want, and chomp line to after the \n
if i := strings.IndexAny(line, "\n"); i != -1 {
// inet 10.180.1.3/16 brd 10.180.255.255 scope global wlan0
inet := regexp.MustCompile(`^[\t ]*inet ([0-9.]*).*$`)
if ip := inet.FindStringSubmatch(line[:i]); ip != nil {
IP = ip[1]
// clean up
break
} else {
line = line[i+1:]
}
}
}
}

}
go reader(c)
//give us time reader clean up
time.Sleep(1)
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Relying on Sleep for another goroutine to complete may not be very portable.
Instead, maybe:

ch := make(chan bool)
go reader(c, ch)
<- ch

and the reader will close the channel when it is done. Or you could use a select for a timeout... or you could just leave it as is. :-)

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

mmm, ok, I need to learn more - I started down this path, and things fell apart fast. - Might look post 0.12.0 release

if IP == "" && B2D.Verbose {
logf(fullLog)
}

return IP
}
19 changes: 19 additions & 0 deletions virtualbox/machine.go
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,7 @@ type Machine struct {
BootOrder []string // max 4 slots, each in {none|floppy|dvd|disk|net}
DockerPort uint
SSHPort uint
SerialFile string
}

// Refresh reloads the machine information.
Expand All @@ -92,6 +93,11 @@ func (m *Machine) Start() error {
case Poweroff, Saved, Aborted:
return vbm("startvm", m.Name, "--type", "headless")
}
if err := m.Refresh(); err == nil {
if m.State != Running {
return fmt.Errorf("Failed to start", m.Name)
}
}
return nil
}

Expand Down Expand Up @@ -251,6 +257,12 @@ func GetMachine(id string) (*Machine, error) {
return nil, err
}
m.SSHPort = uint(n)
case "uartmode1":
// uartmode1="server,/home/sven/.boot2docker/boot2docker-vm.sock"
vals := strings.Split(val, ",")
if len(vals) >= 2 {
m.SerialFile = vals[1]
}
}
}
if err := s.Err(); err != nil {
Expand Down Expand Up @@ -345,6 +357,13 @@ func (m *Machine) Modify() error {
"--accelerate3d", m.Flag.Get(F_accelerate3d),
}

//if runtime.GOOS != "windows" {
args = append(args,
"--uart1", "0x3F8", "4",
"--uartmode1", "server", m.SerialFile,
)
//}

for i, dev := range m.BootOrder {
if i > 3 {
break // Only four slots `--boot{1,2,3,4}`. Ignore the rest.
Expand Down