55package vmwarefusion
66
77import (
8+ "archive/tar"
89 "fmt"
910 "io"
1011 "io/ioutil"
@@ -14,6 +15,7 @@ import (
1415 "path"
1516 "path/filepath"
1617 "regexp"
18+ "runtime"
1719 "strings"
1820 "text/template"
1921 "time"
@@ -24,14 +26,13 @@ import (
2426 "github.com/docker/machine/ssh"
2527 "github.com/docker/machine/state"
2628 "github.com/docker/machine/utils"
27- cssh "golang.org/x/crypto/ssh"
2829)
2930
3031const (
3132 B2D_USER = "docker"
3233 B2D_PASS = "tcuser"
3334 dockerConfigDir = "/var/lib/boot2docker"
34- isoFilename = "boot2docker-vmw .iso"
35+ isoFilename = "boot2docker-1.5.0-GH747 .iso"
3536)
3637
3738// Driver for VMware Fusion
@@ -40,6 +41,7 @@ type Driver struct {
4041 IPAddress string
4142 Memory int
4243 DiskSize int
44+ CPUs int
4345 ISO string
4446 Boot2DockerURL string
4547 CaCertPath string
@@ -105,6 +107,13 @@ func (d *Driver) SetConfigFromFlags(flags drivers.DriverOptions) error {
105107 d .SwarmHost = flags .String ("swarm-host" )
106108 d .SwarmDiscovery = flags .String ("swarm-discovery" )
107109
110+ // We support a maximum of 16 cpu to be consistent with Virtual Hardware 10
111+ // specs.
112+ d .CPUs = int (runtime .NumCPU ())
113+ if d .CPUs > 16 {
114+ d .CPUs = 16
115+ }
116+
108117 return nil
109118}
110119
@@ -168,15 +177,16 @@ func (d *Driver) Create() error {
168177
169178 } else {
170179 // TODO: until vmw tools are merged into b2d master
171- // we will use the iso from the vmware team
180+ // we will use the iso from the vmware team.
172181 //// todo: check latest release URL, download if it's new
173182 //// until then always use "latest"
174183 //isoURL, err = b2dutils.GetLatestBoot2DockerReleaseURL()
175184 //if err != nil {
176185 // log.Warnf("Unable to check for the latest release: %s", err)
177186 //}
178187
179- isoURL := "https://github.com/cloudnativeapps/boot2docker/releases/download/v1.5.0-vmw/boot2docker-1.5.0-vmw.iso"
188+ // see https://github.com/boot2docker/boot2docker/pull/747
189+ isoURL := "https://github.com/cloudnativeapps/boot2docker/releases/download/1.5.0-GH747/boot2docker-1.5.0-GH747.iso"
180190
181191 if _ , err := os .Stat (commonIsoPath ); os .IsNotExist (err ) {
182192 log .Infof ("Downloading boot2docker.iso to %s..." , commonIsoPath )
@@ -255,34 +265,22 @@ func (d *Driver) Create() error {
255265 return fmt .Errorf ("Machine didn't return an IP after 120 seconds, aborting" )
256266 }
257267
268+ // we got an IP, let's copy ssh keys over
258269 d .IPAddress = ip
259270
260- key , err := ioutil . ReadFile ( d . publicSSHKeyPath ())
261- if err != nil {
271+ // Generate a tar keys bundle
272+ if err := d . generateKeyBundle (); err != nil {
262273 return err
263274 }
264275
265- // so, vmrun above will not work without vmtools in b2d. since getting stuff into TCL
266- // is much more painful, we simply use the b2d password to get the initial public key
267- // onto the machine. from then on we use the pub key. meh.
268- sshConfig := & cssh.ClientConfig {
269- User : B2D_USER ,
270- Auth : []cssh.AuthMethod {
271- cssh .Password (B2D_PASS ),
272- },
273- }
274- sshClient , err := cssh .Dial ("tcp" , fmt .Sprintf ("%s:22" , ip ), sshConfig )
275- if err != nil {
276- return err
277- }
278- session , err := sshClient .NewSession ()
279- if err != nil {
280- return err
281- }
282- if err := session .Run (fmt .Sprintf ("mkdir /home/docker/.ssh && echo \" %s\" > /home/docker/.ssh/authorized_keys" , string (key ))); err != nil {
283- return err
284- }
285- session .Close ()
276+ // Test if /var/lib/boot2docker exists
277+ vmrun ("-gu" , B2D_USER , "-gp" , B2D_PASS , "directoryExistsInGuest" , d .vmxPath (), "/var/lib/boot2docker" )
278+
279+ // Copy SSH keys bundle
280+ vmrun ("-gu" , B2D_USER , "-gp" , B2D_PASS , "CopyFileFromHostToGuest" , d .vmxPath (), path .Join (d .storePath , "userdata.tar" ), "/home/docker/userdata.tar" )
281+
282+ // Expand tar file.
283+ vmrun ("-gu" , B2D_USER , "-gp" , B2D_PASS , "runScriptInGuest" , d .vmxPath (), "/bin/sh" , "sudo /bin/mv /home/docker/userdata.tar /var/lib/boot2docker/userdata.tar && sudo tar xf /var/lib/boot2docker/userdata.tar -C /home/docker/ > /var/log/userdata.log 2>&1 && sudo chown -R docker:staff /home/docker" )
286284
287285 log .Debugf ("Setting hostname: %s" , d .MachineName )
288286 cmd , err := d .GetSSHCommand (fmt .Sprintf (
@@ -302,11 +300,13 @@ func (d *Driver) Create() error {
302300}
303301
304302func (d * Driver ) Start () error {
303+ log .Infof ("Starting %s..." , d .MachineName )
305304 vmrun ("start" , d .vmxPath (), "nogui" )
306305 return nil
307306}
308307
309308func (d * Driver ) Stop () error {
309+ log .Infof ("Gracefully shutting down %s..." , d .MachineName )
310310 vmrun ("stop" , d .vmxPath (), "nogui" )
311311 return nil
312312}
@@ -319,18 +319,20 @@ func (d *Driver) Remove() error {
319319 return fmt .Errorf ("Error stopping VM before deletion" )
320320 }
321321 }
322-
322+ log . Infof ( "Deleting %s..." , d . MachineName )
323323 vmrun ("deleteVM" , d .vmxPath (), "nogui" )
324324 return nil
325325}
326326
327327func (d * Driver ) Restart () error {
328+ log .Infof ("Gracefully restarting %s..." , d .MachineName )
328329 vmrun ("reset" , d .vmxPath (), "nogui" )
329330 return nil
330331}
331332
332333func (d * Driver ) Kill () error {
333- vmrun ("stop" , d .vmxPath (), "nogui" )
334+ log .Infof ("Forcibly halting %s..." , d .MachineName )
335+ vmrun ("stop" , d .vmxPath (), "hard nogui" )
334336 return nil
335337}
336338
@@ -351,7 +353,8 @@ func (d *Driver) StartDocker() error {
351353func (d * Driver ) StopDocker () error {
352354 log .Debug ("Stopping Docker..." )
353355
354- cmd , err := d .GetSSHCommand ("if [ -e /var/run/docker.pid ]; then sudo /etc/init.d/docker stop ; fi" )
356+ // Check if pidfile exists, then if process exists, depending on that stop docker or remove pidfile
357+ cmd , err := d .GetSSHCommand ("if [ -e /var/run/docker.pid ]; then if [ -f /proc/$(cat /var/run/docker.pid)/status ]; then sudo /etc/init.d/docker stop; else rm -f /var/run/docker.pid; fi fi" )
355358 if err != nil {
356359 return err
357360 }
@@ -367,7 +370,7 @@ func (d *Driver) GetDockerConfigDir() string {
367370}
368371
369372func (d * Driver ) Upgrade () error {
370- return nil
373+ return fmt . Errorf ( "VMware Fusion does not currently support the upgrade operation." )
371374}
372375
373376func (d * Driver ) GetSSHCommand (args ... string ) (* exec.Cmd , error ) {
@@ -502,3 +505,58 @@ func (d *Driver) sshKeyPath() string {
502505func (d * Driver ) publicSSHKeyPath () string {
503506 return d .sshKeyPath () + ".pub"
504507}
508+
509+ // Make a boot2docker userdata.tar key bundle
510+ func (d * Driver ) generateKeyBundle () error {
511+ log .Debugf ("Creating Tar key bundle..." )
512+
513+ magicString := "boot2docker, this is vmware speaking"
514+
515+ tf , err := os .Create (path .Join (d .storePath , "userdata.tar" ))
516+ if err != nil {
517+ return err
518+ }
519+ defer tf .Close ()
520+ var fileWriter io.WriteCloser = tf
521+
522+ tw := tar .NewWriter (fileWriter )
523+ defer tw .Close ()
524+
525+ // magicString first so we can figure out who originally wrote the tar.
526+ file := & tar.Header {Name : magicString , Size : int64 (len (magicString ))}
527+ if err := tw .WriteHeader (file ); err != nil {
528+ return err
529+ }
530+ if _ , err := tw .Write ([]byte (magicString )); err != nil {
531+ return err
532+ }
533+ // .ssh/key.pub => authorized_keys
534+ file = & tar.Header {Name : ".ssh" , Typeflag : tar .TypeDir , Mode : 0700 }
535+ if err := tw .WriteHeader (file ); err != nil {
536+ return err
537+ }
538+ pubKey , err := ioutil .ReadFile (d .publicSSHKeyPath ())
539+ if err != nil {
540+ return err
541+ }
542+ file = & tar.Header {Name : ".ssh/authorized_keys" , Size : int64 (len (pubKey )), Mode : 0644 }
543+ if err := tw .WriteHeader (file ); err != nil {
544+ return err
545+ }
546+ if _ , err := tw .Write ([]byte (pubKey )); err != nil {
547+ return err
548+ }
549+ file = & tar.Header {Name : ".ssh/authorized_keys2" , Size : int64 (len (pubKey )), Mode : 0644 }
550+ if err := tw .WriteHeader (file ); err != nil {
551+ return err
552+ }
553+ if _ , err := tw .Write ([]byte (pubKey )); err != nil {
554+ return err
555+ }
556+ if err := tw .Close (); err != nil {
557+ return err
558+ }
559+
560+ return nil
561+
562+ }
0 commit comments