Skip to content
This repository was archived by the owner on Feb 27, 2018. It is now read-only.

Commit 5d67082

Browse files
Nathan LeClairenathanleclaire
authored andcommitted
Add support for upgrading binaries
`boot2docker upgrade` will support the Docker client binary, boot2docker-cli binary, and boot2docker ISO now. Signed-off-by: Nathan LeClaire <nathan.leclaire@gmail.com>
1 parent 583f714 commit 5d67082

4 files changed

Lines changed: 191 additions & 11 deletions

File tree

cmds.go

Lines changed: 143 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,9 @@ package main
33
import (
44
"encoding/json"
55
"fmt"
6+
"net/http"
7+
8+
"io/ioutil"
69
"os"
710
"os/exec"
811
"path/filepath"
@@ -277,6 +280,19 @@ func cmdPoweroff() error {
277280

278281
// Upgrade the boot2docker ISO - preserving server state
279282
func cmdUpgrade() error {
283+
if runtime.GOOS == "darwin" || runtime.GOOS == "linux" {
284+
if B2D.Clobber {
285+
err := upgradeDockerClientBinary()
286+
if err != nil {
287+
return err
288+
}
289+
} else {
290+
fmt.Println("Skipping client binary download, use --clobber=true to enable...")
291+
}
292+
}
293+
if err := upgradeBoot2DockerBinary(); err != nil {
294+
return fmt.Errorf("Error upgrading boot2docker binary: %s", err)
295+
}
280296
m, err := driver.GetMachine(&B2D)
281297
if err == nil {
282298
if m.GetState() == driver.Running || m.GetState() == driver.Saved || m.GetState() == driver.Paused {
@@ -292,6 +308,133 @@ func cmdUpgrade() error {
292308
return cmdDownload()
293309
}
294310

311+
func upgradeBoot2DockerBinary() error {
312+
var (
313+
goos, arch, ext string
314+
)
315+
latestVersion, err := getLatestReleaseName("https://api.github.com/repos/boot2docker/boot2docker-cli/releases")
316+
if err != nil {
317+
return fmt.Errorf("Error attempting to get the latest boot2docker-cli release: %s", err)
318+
}
319+
baseUrl := "https://github.com/boot2docker/boot2docker-cli/releases/download"
320+
321+
ext = ""
322+
323+
switch runtime.GOARCH {
324+
case "amd64":
325+
arch = "amd64"
326+
default:
327+
return fmt.Errorf("Architecture not supported")
328+
}
329+
330+
switch runtime.GOOS {
331+
case "darwin", "linux":
332+
goos = runtime.GOOS
333+
case "windows":
334+
goos = "windows"
335+
arch = "windows"
336+
ext = ".exe"
337+
default:
338+
return fmt.Errorf("Operating system not supported")
339+
}
340+
binaryUrl := fmt.Sprintf("%s/%s/boot2docker-%s-%s-%s%s", baseUrl, latestVersion, latestVersion, goos, arch, ext)
341+
currentBoot2DockerVersion := Version
342+
if err := attemptUpgrade(binaryUrl, "boot2docker", latestVersion, currentBoot2DockerVersion); err != nil {
343+
return fmt.Errorf("Error attempting upgrade: %s", err)
344+
}
345+
return nil
346+
}
347+
348+
func upgradeDockerClientBinary() error {
349+
var (
350+
clientOs, clientArch string
351+
)
352+
resp, err := http.Get("https://get.docker.com/latest")
353+
if err != nil {
354+
return fmt.Errorf("Error checking the latest version of Docker: %s", err)
355+
}
356+
defer resp.Body.Close()
357+
latestVersionBytes, err := ioutil.ReadAll(resp.Body)
358+
if err != nil {
359+
return fmt.Errorf("Error reading response body on latest version of Docker call: %s", err)
360+
}
361+
latestVersion := strings.TrimSpace(string(latestVersionBytes))
362+
localClientVersion, err := getLocalClientVersion()
363+
if err != nil {
364+
return fmt.Errorf("Error getting local Docker client version: %s", err)
365+
}
366+
switch runtime.GOARCH {
367+
case "amd64":
368+
clientArch = "x86_64"
369+
default:
370+
return fmt.Errorf("Architecture not supported")
371+
}
372+
373+
switch runtime.GOOS {
374+
case "darwin":
375+
clientOs = "Darwin"
376+
case "linux":
377+
clientOs = "Linux"
378+
default:
379+
return fmt.Errorf("Operating system not supported")
380+
}
381+
binaryUrl := fmt.Sprintf("https://get.docker.com/builds/%s/%s/docker-latest", clientOs, clientArch)
382+
if err := attemptUpgrade(binaryUrl, "docker", latestVersion, localClientVersion); err != nil {
383+
return fmt.Errorf("Error attempting upgrade: %s", err)
384+
}
385+
return nil
386+
}
387+
388+
func attemptUpgrade(binaryUrl, binaryName, latestVersion, localVersion string) error {
389+
if (latestVersion != localVersion && !strings.Contains(latestVersion, "rc")) || B2D.ForceUpgradeDownload {
390+
if err := backupAndDownload(binaryUrl, binaryName, localVersion); err != nil {
391+
return fmt.Errorf("Error attempting backup and download of Docker client binary: %s", err)
392+
}
393+
} else {
394+
fmt.Printf("%s is up to date (%s), skipping upgrade...\n", binaryName, localVersion)
395+
}
396+
return nil
397+
}
398+
399+
func backupAndDownload(binaryUrl, binaryName, localVersion string) error {
400+
binaryPath, err := exec.LookPath(binaryName)
401+
if err != nil {
402+
return fmt.Errorf("Error attempting to locate local binary: %s", err)
403+
}
404+
path := strings.TrimSpace(string(binaryPath))
405+
406+
fmt.Println("Backing up existing", binaryName, "binary...")
407+
if err := backupBinary(binaryName, localVersion, path); err != nil {
408+
return fmt.Errorf("Error backing up docker client: %s", err)
409+
}
410+
411+
fmt.Println("Downloading new", binaryName, "client binary...")
412+
if err := download(path, binaryUrl); err != nil {
413+
return fmt.Errorf("Error attempting to download new client binary: %s", err)
414+
}
415+
if err := os.Chmod(path, 0755); err != nil {
416+
return err
417+
}
418+
fmt.Printf("Success: downloaded %s\n\tto %s\n\tThe old version is backed up to ~/.boot2docker.\n", binaryUrl, path)
419+
return nil
420+
}
421+
422+
func backupBinary(binaryName, localVersion, path string) error {
423+
dir, err := cfgDir(".boot2docker")
424+
if err != nil {
425+
return fmt.Errorf("Error getting boot2docker config dir", err)
426+
}
427+
buf, err := ioutil.ReadFile(path)
428+
if err != nil {
429+
return fmt.Errorf("Error opening binary for reading at %s: %s", path, err)
430+
}
431+
backupName := fmt.Sprintf("%s-%s", binaryName, localVersion)
432+
if err := ioutil.WriteFile(filepath.Join(dir, backupName), buf, 0755); err != nil {
433+
return fmt.Errorf("Error creating backup file", err)
434+
}
435+
return nil
436+
}
437+
295438
// Gracefully stop and then start the VM.
296439
func cmdRestart() error {
297440
m, err := driver.GetMachine(&B2D)

config.go

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -93,6 +93,14 @@ func config() (*flag.FlagSet, error) {
9393
B2D.Dir = dir
9494
flags.StringVar(&B2D.ISOURL, "iso-url", "https://api.github.com/repos/boot2docker/boot2docker/releases", "source URL to provision the boot2docker ISO image.")
9595
flags.StringVar(&B2D.ISO, "iso", filepath.Join(dir, "boot2docker.iso"), "path to boot2docker ISO image.")
96+
if runtime.GOOS == "darwin" {
97+
// clobber by default
98+
flags.BoolVar(&B2D.Clobber, "clobber", true, "overwrite Docker client binary on boot2docker upgrade")
99+
} else {
100+
flags.BoolVar(&B2D.Clobber, "clobber", false, "overwrite Docker client binary on boot2docker upgrade")
101+
}
102+
103+
flags.BoolVar(&B2D.ForceUpgradeDownload, "force-upgrade-download", false, "always download on boot2docker upgrade, never skip")
96104

97105
// Sven disabled this, as it is broken - if I user with a fresh computer downloads
98106
// just the boot2docker-cli, and then runs `boot2docker --init ip`, we create a vm

driver/config.go

Lines changed: 13 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,9 @@ package driver
22

33
import (
44
"fmt"
5-
flag "github.com/ogier/pflag"
65
"net"
6+
7+
flag "github.com/ogier/pflag"
78
)
89

910
// Machine config.
@@ -14,15 +15,17 @@ type MachineConfig struct {
1415
Driver string
1516

1617
// basic config
17-
SSH string // SSH client executable
18-
SSHGen string // SSH keygen executable
19-
SSHKey string // SSH key to send to the vm
20-
VM string // virtual machine name
21-
Dir string // boot2docker directory
22-
ISOURL string // Source URL to retrieve the ISO from
23-
ISO string // boot2docker ISO image path
24-
DiskSize uint // VM disk image size (MB)
25-
Memory uint // VM memory size (MB)
18+
Clobber bool
19+
ForceUpgradeDownload bool
20+
SSH string // SSH client executable
21+
SSHGen string // SSH keygen executable
22+
SSHKey string // SSH key to send to the vm
23+
VM string // virtual machine name
24+
Dir string // boot2docker directory
25+
ISOURL string // Source URL to retrieve the ISO from
26+
ISO string // boot2docker ISO image path
27+
DiskSize uint // VM disk image size (MB)
28+
Memory uint // VM memory size (MB)
2629

2730
// NAT network: port forwarding
2831
SSHPort uint16 // host SSH port (forward to port 22 in VM)

util.go

Lines changed: 27 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,11 @@ import (
2121
"github.com/boot2docker/boot2docker-cli/driver"
2222
)
2323

24+
var (
25+
// We're looking to get e.g. "1.2.0" from "Docker version 1.2.0, build fa7b24f"
26+
versionRe = regexp.MustCompile(`(\d+\.?){3}`)
27+
)
28+
2429
// Try if addr tcp://addr is readable for n times at wait interval.
2530
func read(addr string, n int, wait time.Duration) error {
2631
var lastErr error
@@ -105,6 +110,7 @@ func getLatestReleaseName(url string) (string, error) {
105110
defer rsp.Body.Close()
106111

107112
var t []struct {
113+
Name string `json:"name"`
108114
TagName string `json:"tag_name"`
109115
}
110116
body, err := ioutil.ReadAll(rsp.Body)
@@ -125,7 +131,27 @@ func getLatestReleaseName(url string) (string, error) {
125131
if len(t) == 0 {
126132
return "", fmt.Errorf("no releases found")
127133
}
128-
return t[0].TagName, nil
134+
135+
// Looking up by tag instead of release.
136+
// Github API call for docker releases yields nothing,
137+
// so we use tags API call in this case.
138+
name := ""
139+
if strings.Contains(url, "tags") {
140+
name = t[0].Name
141+
} else {
142+
name = t[0].TagName
143+
}
144+
return name, nil
145+
}
146+
147+
func getLocalClientVersion() (string, error) {
148+
versionOutput, err := exec.Command("docker", "-v").Output()
149+
if err != nil {
150+
return "", err
151+
}
152+
versionNumber := versionRe.FindString(string(versionOutput))
153+
154+
return versionNumber, nil
129155
}
130156

131157
func cmdInteractive(m driver.Machine, args ...string) error {

0 commit comments

Comments
 (0)