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
34 changes: 34 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -187,6 +187,40 @@ You can override the configurations using matching command-line flags. Type
`boot2docker -h` for more information. The configuration file options are
the same as the command-line flags with long names.

## Upgrade

You can use boot2docker-cli to upgrade:

1. The ISO you are using in the VM (and consequently the Docker daemon version)
2. The Docker client binary on your host system
3. The boot2docker-cli binary itself

To do so, run the `boot2docker upgrade` command.

```console
$ boot2docker upgrade
Backing up existing docker binary...
Downloading new docker client binary...
Success: downloaded https://get.docker.com/builds/Darwin/x86_64/docker-latest
to /usr/local/bin/docker
The old version is backed up to ~/.boot2docker.
Backing up existing boot2docker binary...
Downloading new boot2docker client binary...
Success: downloaded https://github.com/boot2docker/boot2docker-cli/releases/download/v1.4.0/boot2docker-v1.4.0-darwin-amd64
to /usr/local/bin/boot2docker
The old version is backed up to ~/.boot2docker.
Latest release for boot2docker/boot2docker is v1.4.0
Downloading boot2docker ISO image...
Success: downloaded https://github.com/boot2docker/boot2docker/releases/download/v1.4.0/boot2docker.iso
to /Users/youruser/.boot2docker/boot2docker.iso
Waiting for VM and Docker daemon to start...
.................ooo
Started.
```

This will back up your current `docker` and `boot2docker` binaries to
`~/.boot2docker` and download the latest ISO, `docker` binary and `boot2docker`
binary in place of the old versions.


## Contribution
Expand Down
145 changes: 143 additions & 2 deletions cmds.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@ package main
import (
"encoding/json"
"fmt"
"io/ioutil"
"net/http"
"os"
"os/exec"
"path/filepath"
Expand All @@ -11,10 +13,9 @@ import (
"strings"
"time"

"github.com/boot2docker/boot2docker-cli/driver"
_ "github.com/boot2docker/boot2docker-cli/dummy"
_ "github.com/boot2docker/boot2docker-cli/virtualbox"

"github.com/boot2docker/boot2docker-cli/driver"
)

// Initialize the boot2docker VM from scratch.
Expand Down Expand Up @@ -277,6 +278,19 @@ func cmdPoweroff() error {

// Upgrade the boot2docker ISO - preserving server state
func cmdUpgrade() error {
if runtime.GOOS == "darwin" || runtime.GOOS == "linux" {
if B2D.Clobber {
err := upgradeDockerClientBinary()
if err != nil {
return err
}
} else {
fmt.Println("Skipping client binary download, use --clobber=true to enable...")
}
}
if err := upgradeBoot2DockerBinary(); err != nil {
return fmt.Errorf("Error upgrading boot2docker binary: %s", err)
}
m, err := driver.GetMachine(&B2D)
if err == nil {
if m.GetState() == driver.Running || m.GetState() == driver.Saved || m.GetState() == driver.Paused {
Expand All @@ -292,6 +306,133 @@ func cmdUpgrade() error {
return cmdDownload()
}

func upgradeBoot2DockerBinary() error {
var (
goos, arch, ext string
)
latestVersion, err := getLatestReleaseName("https://api.github.com/repos/boot2docker/boot2docker-cli/releases")
if err != nil {
return fmt.Errorf("Error attempting to get the latest boot2docker-cli release: %s", err)
}
baseUrl := "https://github.com/boot2docker/boot2docker-cli/releases/download"

ext = ""

switch runtime.GOARCH {
case "amd64":
arch = "amd64"
Copy link
Contributor

Choose a reason for hiding this comment

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

It'd probably be good to add a default: case here to return an error. 👍

default:
return fmt.Errorf("Architecture not supported")
}

switch runtime.GOOS {
case "darwin", "linux":
goos = runtime.GOOS
case "windows":
goos = "windows"
arch = "amd64"
ext = ".exe"
default:
return fmt.Errorf("Operating system not supported")
}
binaryUrl := fmt.Sprintf("%s/%s/boot2docker-%s-%s-%s%s", baseUrl, latestVersion, latestVersion, goos, arch, ext)
currentBoot2DockerVersion := Version
if err := attemptUpgrade(binaryUrl, "boot2docker", latestVersion, currentBoot2DockerVersion); err != nil {
return fmt.Errorf("Error attempting upgrade: %s", err)
}
return nil
}

func upgradeDockerClientBinary() error {
var (
clientOs, clientArch string
)
resp, err := http.Get("https://get.docker.com/latest")
if err != nil {
return fmt.Errorf("Error checking the latest version of Docker: %s", err)
}
defer resp.Body.Close()
latestVersionBytes, err := ioutil.ReadAll(resp.Body)
if err != nil {
return fmt.Errorf("Error reading response body on latest version of Docker call: %s", err)
}
latestVersion := strings.TrimSpace(string(latestVersionBytes))
localClientVersion, err := getLocalClientVersion()
if err != nil {
return fmt.Errorf("Error getting local Docker client version: %s", err)
}
switch runtime.GOARCH {
case "amd64":
clientArch = "x86_64"
default:
return fmt.Errorf("Architecture not supported")
}

switch runtime.GOOS {
case "darwin":
clientOs = "Darwin"
case "linux":
clientOs = "Linux"
default:
return fmt.Errorf("Operating system not supported")
}
binaryUrl := fmt.Sprintf("https://get.docker.com/builds/%s/%s/docker-latest", clientOs, clientArch)
if err := attemptUpgrade(binaryUrl, "docker", latestVersion, localClientVersion); err != nil {
return fmt.Errorf("Error attempting upgrade: %s", err)
}
return nil
}

func attemptUpgrade(binaryUrl, binaryName, latestVersion, localVersion string) error {
if (latestVersion != localVersion && !strings.Contains(latestVersion, "rc")) || B2D.ForceUpgradeDownload {
if err := backupAndDownload(binaryUrl, binaryName, localVersion); err != nil {
return fmt.Errorf("Error attempting backup and download of Docker client binary: %s", err)
}
} else {
fmt.Printf("%s is up to date (%s), skipping upgrade...\n", binaryName, localVersion)
}
return nil
}

func backupAndDownload(binaryUrl, binaryName, localVersion string) error {
binaryPath, err := exec.LookPath(binaryName)
if err != nil {
return fmt.Errorf("Error attempting to locate local binary: %s", err)
}
path := strings.TrimSpace(string(binaryPath))

fmt.Println("Backing up existing", binaryName, "binary...")
if err := backupBinary(binaryName, localVersion, path); err != nil {
return fmt.Errorf("Error backing up docker client: %s", err)
}

fmt.Println("Downloading new", binaryName, "client binary...")
if err := download(path, binaryUrl); err != nil {
return fmt.Errorf("Error attempting to download new client binary: %s", err)
}
if err := os.Chmod(path, 0755); err != nil {
return err
}
fmt.Printf("Success: downloaded %s\n\tto %s\n\tThe old version is backed up to ~/.boot2docker.\n", binaryUrl, path)
return nil
}

func backupBinary(binaryName, localVersion, path string) error {
dir, err := cfgDir(".boot2docker")
if err != nil {
return fmt.Errorf("Error getting boot2docker config dir: %s", err)
}
buf, err := ioutil.ReadFile(path)
if err != nil {
return fmt.Errorf("Error opening binary for reading at %s: %s", path, err)
}
backupName := fmt.Sprintf("%s-%s", binaryName, localVersion)
if err := ioutil.WriteFile(filepath.Join(dir, backupName), buf, 0755); err != nil {
return fmt.Errorf("Error creating backup file: %s", err)
}
return nil
}

// Gracefully stop and then start the VM.
func cmdRestart() error {
m, err := driver.GetMachine(&B2D)
Expand Down
7 changes: 7 additions & 0 deletions config.go
Original file line number Diff line number Diff line change
Expand Up @@ -94,6 +94,13 @@ func config() (*flag.FlagSet, error) {
flags.StringVar(&B2D.ISOURL, "iso-url", "https://api.github.com/repos/boot2docker/boot2docker/releases", "source URL to provision the boot2docker ISO image.")
flags.StringVar(&B2D.ISO, "iso", filepath.Join(dir, "boot2docker.iso"), "path to boot2docker ISO image.")

// clobber (overwrite client binary) by default on OSX. it's more likely that
// users have installed through package manager on Linux, and if so, they should
// upgrade that way.
flags.BoolVar(&B2D.Clobber, "clobber", (runtime.GOOS == "darwin"), "overwrite Docker client binary on boot2docker upgrade")

flags.BoolVar(&B2D.ForceUpgradeDownload, "force-upgrade-download", false, "always download on boot2docker upgrade, never skip")

// Sven disabled this, as it is broken - if I user with a fresh computer downloads
// just the boot2docker-cli, and then runs `boot2docker --init ip`, we create a vm
// which cannot run, because it fails to have have the boot2docker.iso and the ssh keys
Expand Down
23 changes: 13 additions & 10 deletions driver/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,9 @@ package driver

import (
"fmt"
flag "github.com/ogier/pflag"
"net"

flag "github.com/ogier/pflag"
)

// Machine config.
Expand All @@ -14,15 +15,17 @@ type MachineConfig struct {
Driver string

// basic config
SSH string // SSH client executable
SSHGen string // SSH keygen executable
SSHKey string // SSH key to send to the vm
VM string // virtual machine name
Dir string // boot2docker directory
ISOURL string // Source URL to retrieve the ISO from
ISO string // boot2docker ISO image path
DiskSize uint // VM disk image size (MB)
Memory uint // VM memory size (MB)
Clobber bool
ForceUpgradeDownload bool
SSH string // SSH client executable
SSHGen string // SSH keygen executable
SSHKey string // SSH key to send to the vm
VM string // virtual machine name
Dir string // boot2docker directory
ISOURL string // Source URL to retrieve the ISO from
ISO string // boot2docker ISO image path
DiskSize uint // VM disk image size (MB)
Memory uint // VM memory size (MB)

// NAT network: port forwarding
SSHPort uint16 // host SSH port (forward to port 22 in VM)
Expand Down
28 changes: 27 additions & 1 deletion util.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,11 @@ import (
"github.com/boot2docker/boot2docker-cli/driver"
)

var (
// We're looking to get e.g. "1.2.0" from "Docker version 1.2.0, build fa7b24f"
versionRe = regexp.MustCompile(`(\d+\.?){3}`)
)

// Try if addr tcp://addr is readable for n times at wait interval.
func read(addr string, n int, wait time.Duration) error {
var lastErr error
Expand Down Expand Up @@ -105,6 +110,7 @@ func getLatestReleaseName(url string) (string, error) {
defer rsp.Body.Close()

var t []struct {
Name string `json:"name"`
TagName string `json:"tag_name"`
}
body, err := ioutil.ReadAll(rsp.Body)
Expand All @@ -125,7 +131,27 @@ func getLatestReleaseName(url string) (string, error) {
if len(t) == 0 {
return "", fmt.Errorf("no releases found")
}
return t[0].TagName, nil

// Looking up by tag instead of release.
// Github API call for docker releases yields nothing,
// so we use tags API call in this case.
name := ""
if strings.Contains(url, "tags") {
name = t[0].Name
} else {
name = t[0].TagName
}
return name, nil
}

func getLocalClientVersion() (string, error) {
versionOutput, err := exec.Command("docker", "-v").Output()
if err != nil {
return "", err
}
versionNumber := versionRe.FindString(string(versionOutput))

return versionNumber, nil
}

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