mirror of https://github.com/docker/docs.git
Implement upgrade functionality for boot2docker
Signed-off-by: Nathan LeClaire <nathan.leclaire@gmail.com>
This commit is contained in:
parent
a04b908fd3
commit
9b5f395c60
|
@ -579,12 +579,26 @@ dev * virtualbox Stopped
|
|||
|
||||
#### upgrade
|
||||
|
||||
Upgrade a machine to the latest version of Docker.
|
||||
Upgrade a machine to the latest version of Docker. If the machine uses Ubuntu
|
||||
as the underlying operating system, it will upgrade the package `lxc-docker`
|
||||
(our recommended install method). If the machine uses boot2docker, this command
|
||||
will download the latest boot2docker ISO and replace the machine's existing ISO
|
||||
with the latest.
|
||||
|
||||
```
|
||||
$ docker-machine upgrade dev
|
||||
INFO[0000] Stopping machine to do the upgrade...
|
||||
INFO[0005] Upgrading machine dev...
|
||||
INFO[0006] Downloading latest boot2docker release to /tmp/store/cache/boot2docker.iso...
|
||||
INFO[0008] Starting machine back up...
|
||||
INFO[0008] Waiting for VM to start...
|
||||
```
|
||||
|
||||
> **Note**: If you are using a custom boot2docker ISO specified using
|
||||
> `--virtualbox-boot2docker-url` or an equivalent flag, running an upgrade on
|
||||
> that machine will completely replace the specified ISO with the latest
|
||||
> "vanilla" boot2docker ISO available.
|
||||
|
||||
#### url
|
||||
|
||||
Get the URL of a host
|
||||
|
@ -826,7 +840,22 @@ Options:
|
|||
- `--virtualbox-memory`: Size of memory for the host in MB. Default: `1024`
|
||||
- `--virtualbox-cpu-count`: Number of CPUs to use to create the VM. Defaults to number of available CPUs.
|
||||
|
||||
The VirtualBox driver uses the latest boot2docker image.
|
||||
The `--virtualbox-boot2docker-url` flag takes a few different forms. By
|
||||
default, if no value is specified for this flag, Machine will check locally for
|
||||
a boot2docker ISO. If one is found, that will be used as the ISO for the
|
||||
created machine. If one is not found, the latest ISO release available on
|
||||
[boot2docker/boot2docker](https://github.com/boot2docker/boot2docker) will be
|
||||
downloaded and stored locally for future use. Note that this means you must run
|
||||
`docker-machine upgrade` deliberately on a machine if you wish to update the "cached"
|
||||
boot2docker ISO.
|
||||
|
||||
This is the default behavior (when `--virtualbox-boot2docker-url=""`), but the
|
||||
option also supports specifying ISOs by the `http://` and `file://` protocols.
|
||||
`file://` will look at the path specified locally to locate the ISO: for
|
||||
instance, you could specify `--virtualbox-boot2docker-url
|
||||
file://$HOME/Downloads/rc.iso` to test out a release candidate ISO that you have
|
||||
downloaded already. You could also just get an ISO straight from the Internet
|
||||
using the `http://` form.
|
||||
|
||||
Environment variables:
|
||||
|
||||
|
@ -840,6 +869,7 @@ variable and CLI option are provided the CLI option takes the precedence.
|
|||
| `VIRTUALBOX_DISK_SIZE` | `--virtualbox-disk-size` |
|
||||
| `VIRTUALBOX_BOOT2DOCKER_URL` | `--virtualbox-boot2docker-url` |
|
||||
|
||||
|
||||
#### VMware Fusion
|
||||
Creates machines locally on [VMware Fusion](http://www.vmware.com/products/fusion). Requires VMware Fusion to be installed.
|
||||
|
||||
|
|
|
@ -6,6 +6,7 @@ import (
|
|||
"os/exec"
|
||||
"sort"
|
||||
|
||||
log "github.com/Sirupsen/logrus"
|
||||
"github.com/codegangsta/cli"
|
||||
"github.com/docker/machine/provider"
|
||||
"github.com/docker/machine/ssh"
|
||||
|
@ -175,3 +176,16 @@ func GetSSHCommandFromDriver(d Driver, args ...string) (*exec.Cmd, error) {
|
|||
|
||||
return ssh.GetSSHCommand(host, port, user, keyPath, args...), nil
|
||||
}
|
||||
|
||||
func MachineInState(d Driver, desiredState state.State) func() bool {
|
||||
return func() bool {
|
||||
currentState, err := d.GetState()
|
||||
if err != nil {
|
||||
log.Debugf("Error getting machine state: %s", err)
|
||||
}
|
||||
if currentState == desiredState {
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
|
|
@ -158,8 +158,7 @@ func (d *Driver) PreCreateCheck() error {
|
|||
|
||||
func (d *Driver) Create() error {
|
||||
var (
|
||||
err error
|
||||
isoURL string
|
||||
err error
|
||||
)
|
||||
|
||||
// Check that VBoxManage exists and works
|
||||
|
@ -172,46 +171,13 @@ func (d *Driver) Create() error {
|
|||
return err
|
||||
}
|
||||
|
||||
b2dutils := utils.NewB2dUtils("", "")
|
||||
imgPath := utils.GetMachineCacheDir()
|
||||
isoFilename := "boot2docker.iso"
|
||||
commonIsoPath := filepath.Join(imgPath, "boot2docker.iso")
|
||||
// just in case boot2docker.iso has been manually deleted
|
||||
if _, err := os.Stat(imgPath); os.IsNotExist(err) {
|
||||
if err := os.Mkdir(imgPath, 0700); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
if d.Boot2DockerURL != "" {
|
||||
isoURL = d.Boot2DockerURL
|
||||
log.Infof("Downloading %s from %s...", isoFilename, isoURL)
|
||||
if err := b2dutils.DownloadISO(d.storePath, isoFilename, isoURL); err != nil {
|
||||
return err
|
||||
}
|
||||
} else {
|
||||
// todo: check latest release URL, download if it's new
|
||||
// until then always use "latest"
|
||||
isoURL, err = b2dutils.GetLatestBoot2DockerReleaseURL()
|
||||
if err != nil {
|
||||
log.Warnf("Unable to check for the latest release: %s", err)
|
||||
}
|
||||
|
||||
if _, err := os.Stat(commonIsoPath); os.IsNotExist(err) {
|
||||
log.Infof("Downloading %s to %s...", isoFilename, commonIsoPath)
|
||||
if err := b2dutils.DownloadISO(imgPath, isoFilename, isoURL); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
isoDest := filepath.Join(d.storePath, isoFilename)
|
||||
if err := utils.CopyFile(commonIsoPath, isoDest); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
log.Infof("Creating SSH key...")
|
||||
|
||||
b2dutils := utils.NewB2dUtils("", "")
|
||||
if err := b2dutils.CopyIsoToMachineDir(d.Boot2DockerURL, d.MachineName); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := ssh.GenerateSSHKey(d.GetSSHKeyPath()); err != nil {
|
||||
return err
|
||||
}
|
||||
|
|
|
@ -146,19 +146,6 @@ func (h *Host) GetSSHCommand(args ...string) (*exec.Cmd, error) {
|
|||
return cmd, nil
|
||||
}
|
||||
|
||||
func (h *Host) MachineInState(desiredState state.State) func() bool {
|
||||
return func() bool {
|
||||
currentState, err := h.Driver.GetState()
|
||||
if err != nil {
|
||||
log.Debugf("Error getting machine state: %s", err)
|
||||
}
|
||||
if currentState == desiredState {
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
func (h *Host) Start() error {
|
||||
if err := h.Driver.Start(); err != nil {
|
||||
return err
|
||||
|
@ -168,7 +155,7 @@ func (h *Host) Start() error {
|
|||
return err
|
||||
}
|
||||
|
||||
return utils.WaitFor(h.MachineInState(state.Running))
|
||||
return utils.WaitFor(drivers.MachineInState(h.Driver, state.Running))
|
||||
}
|
||||
|
||||
func (h *Host) Stop() error {
|
||||
|
@ -180,7 +167,7 @@ func (h *Host) Stop() error {
|
|||
return err
|
||||
}
|
||||
|
||||
return utils.WaitFor(h.MachineInState(state.Stopped))
|
||||
return utils.WaitFor(drivers.MachineInState(h.Driver, state.Stopped))
|
||||
}
|
||||
|
||||
func (h *Host) Kill() error {
|
||||
|
@ -192,16 +179,16 @@ func (h *Host) Kill() error {
|
|||
return err
|
||||
}
|
||||
|
||||
return utils.WaitFor(h.MachineInState(state.Stopped))
|
||||
return utils.WaitFor(drivers.MachineInState(h.Driver, state.Stopped))
|
||||
}
|
||||
|
||||
func (h *Host) Restart() error {
|
||||
if h.MachineInState(state.Running)() {
|
||||
if drivers.MachineInState(h.Driver, state.Running)() {
|
||||
if err := h.Stop(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := utils.WaitFor(h.MachineInState(state.Stopped)); err != nil {
|
||||
if err := utils.WaitFor(drivers.MachineInState(h.Driver, state.Stopped)); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
@ -210,7 +197,7 @@ func (h *Host) Restart() error {
|
|||
return err
|
||||
}
|
||||
|
||||
if err := utils.WaitFor(h.MachineInState(state.Running)); err != nil {
|
||||
if err := utils.WaitFor(drivers.MachineInState(h.Driver, state.Running)); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
|
|
|
@ -2,14 +2,17 @@ package provision
|
|||
|
||||
import (
|
||||
"bytes"
|
||||
"errors"
|
||||
"fmt"
|
||||
"os/exec"
|
||||
"path"
|
||||
|
||||
log "github.com/Sirupsen/logrus"
|
||||
"github.com/docker/machine/drivers"
|
||||
"github.com/docker/machine/libmachine/auth"
|
||||
"github.com/docker/machine/libmachine/provision/pkgaction"
|
||||
"github.com/docker/machine/libmachine/swarm"
|
||||
"github.com/docker/machine/state"
|
||||
"github.com/docker/machine/utils"
|
||||
)
|
||||
|
||||
|
@ -46,7 +49,54 @@ func (provisioner *Boot2DockerProvisioner) Service(name string, action pkgaction
|
|||
return nil
|
||||
}
|
||||
|
||||
func (provisioner *Boot2DockerProvisioner) upgradeIso() error {
|
||||
log.Infof("Stopping machine to do the upgrade...")
|
||||
|
||||
switch provisioner.Driver.DriverName() {
|
||||
case "vmwarefusion", "vmwarevsphere":
|
||||
return errors.New("Upgrade functionality is currently not supported for these providers, as they use a custom ISO.")
|
||||
}
|
||||
|
||||
if err := provisioner.Driver.Stop(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := utils.WaitFor(drivers.MachineInState(provisioner.Driver, state.Stopped)); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
machineName := provisioner.GetDriver().GetMachineName()
|
||||
|
||||
log.Infof("Upgrading machine %s...", machineName)
|
||||
|
||||
b2dutils := utils.NewB2dUtils("", "")
|
||||
|
||||
// Usually we call this implicitly, but call it here explicitly to get
|
||||
// the latest boot2docker ISO.
|
||||
if err := b2dutils.DownloadLatestBoot2Docker(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Copy the latest version of boot2docker ISO to the machine's directory
|
||||
if err := b2dutils.CopyIsoToMachineDir("", machineName); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
log.Infof("Starting machine back up...")
|
||||
|
||||
if err := provisioner.Driver.Start(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return utils.WaitFor(drivers.MachineInState(provisioner.Driver, state.Running))
|
||||
}
|
||||
|
||||
func (provisioner *Boot2DockerProvisioner) Package(name string, action pkgaction.PackageAction) error {
|
||||
if name == "docker" && action == pkgaction.Upgrade {
|
||||
if err := provisioner.upgradeIso(); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
|
|
|
@ -36,6 +36,12 @@ findCPUCount() {
|
|||
run bash -c "VBoxManage showvminfo --machinereadable $NAME | grep cpus= | cut -d'=' -f2"
|
||||
}
|
||||
|
||||
buildMachineWithOldIsoCheckUpgrade() {
|
||||
run wget https://github.com/boot2docker/boot2docker/releases/download/v1.4.1/boot2docker.iso -O $MACHINE_STORAGE_PATH/cache/boot2docker.iso
|
||||
run machine create -d virtualbox $NAME
|
||||
run machine upgrade $NAME
|
||||
}
|
||||
|
||||
@test "$DRIVER: machine should not exist" {
|
||||
run machine active $NAME
|
||||
[ "$status" -eq 1 ]
|
||||
|
@ -316,6 +322,15 @@ findCPUCount() {
|
|||
[ "$status" -eq 0 ]
|
||||
}
|
||||
|
||||
@test "$DRIVER: upgrade should work" {
|
||||
buildMachineWithOldIsoCheckUpgrade
|
||||
[ "$status" -eq 0 ]
|
||||
}
|
||||
|
||||
@test "$DRIVER: remove machine after upgrade test" {
|
||||
run machine rm -f $NAME
|
||||
}
|
||||
|
||||
# Cleanup of machine store should always be the last 'test'
|
||||
@test "$DRIVER: cleanup" {
|
||||
run rm -rf $MACHINE_STORAGE_PATH
|
||||
|
|
69
utils/b2d.go
69
utils/b2d.go
|
@ -11,6 +11,8 @@ import (
|
|||
"os"
|
||||
"path/filepath"
|
||||
"time"
|
||||
|
||||
log "github.com/Sirupsen/logrus"
|
||||
)
|
||||
|
||||
const (
|
||||
|
@ -36,6 +38,9 @@ func getClient() *http.Client {
|
|||
}
|
||||
|
||||
type B2dUtils struct {
|
||||
isoFilename string
|
||||
commonIsoPath string
|
||||
imgCachePath string
|
||||
githubApiBaseUrl string
|
||||
githubBaseUrl string
|
||||
}
|
||||
|
@ -43,6 +48,8 @@ type B2dUtils struct {
|
|||
func NewB2dUtils(githubApiBaseUrl, githubBaseUrl string) *B2dUtils {
|
||||
defaultBaseApiUrl := "https://api.github.com"
|
||||
defaultBaseUrl := "https://github.com"
|
||||
imgCachePath := GetMachineCacheDir()
|
||||
isoFilename := "boot2docker.iso"
|
||||
|
||||
if githubApiBaseUrl == "" {
|
||||
githubApiBaseUrl = defaultBaseApiUrl
|
||||
|
@ -53,6 +60,9 @@ func NewB2dUtils(githubApiBaseUrl, githubBaseUrl string) *B2dUtils {
|
|||
}
|
||||
|
||||
return &B2dUtils{
|
||||
isoFilename: isoFilename,
|
||||
imgCachePath: GetMachineCacheDir(),
|
||||
commonIsoPath: filepath.Join(imgCachePath, isoFilename),
|
||||
githubApiBaseUrl: githubApiBaseUrl,
|
||||
githubBaseUrl: githubBaseUrl,
|
||||
}
|
||||
|
@ -128,3 +138,62 @@ func (b *B2dUtils) DownloadISO(dir, file, isoUrl string) error {
|
|||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (b *B2dUtils) DownloadLatestBoot2Docker() error {
|
||||
latestReleaseUrl, err := b.GetLatestBoot2DockerReleaseURL()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
log.Infof("Downloading latest boot2docker release to %s...", b.commonIsoPath)
|
||||
if err := b.DownloadISO(b.imgCachePath, b.isoFilename, latestReleaseUrl); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (b *B2dUtils) CopyIsoToMachineDir(isoURL, machineName string) error {
|
||||
machinesDir := GetMachineDir()
|
||||
machineIsoPath := filepath.Join(machinesDir, machineName, b.isoFilename)
|
||||
|
||||
// just in case the cache dir has been manually deleted,
|
||||
// check for it and recreate it if it's gone
|
||||
if _, err := os.Stat(b.imgCachePath); os.IsNotExist(err) {
|
||||
log.Infof("Image cache does not exist, creating it at %s...", b.imgCachePath)
|
||||
if err := os.Mkdir(b.imgCachePath, 0700); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
// By default just copy the existing "cached" iso to
|
||||
// the machine's directory...
|
||||
if isoURL == "" {
|
||||
if err := b.copyDefaultIsoToMachine(machineIsoPath); err != nil {
|
||||
return err
|
||||
}
|
||||
} else {
|
||||
// But if ISO is specified go get it directly
|
||||
log.Infof("Downloading %s from %s...", b.isoFilename, isoURL)
|
||||
if err := b.DownloadISO(filepath.Join(machinesDir, machineName), b.isoFilename, isoURL); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (b *B2dUtils) copyDefaultIsoToMachine(machineIsoPath string) error {
|
||||
if _, err := os.Stat(b.commonIsoPath); os.IsNotExist(err) {
|
||||
log.Info("No default boot2docker iso found locally, downloading the latest release...")
|
||||
if err := b.DownloadLatestBoot2Docker(); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
if err := CopyFile(b.commonIsoPath, machineIsoPath); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
|
|
@ -100,7 +100,7 @@ func WaitForDocker(ip string, daemonPort int) error {
|
|||
return WaitFor(func() bool {
|
||||
conn, err := net.Dial("tcp", fmt.Sprintf("%s:%d", ip, daemonPort))
|
||||
if err != nil {
|
||||
log.Debug("Got an error it was", err)
|
||||
log.Debugf("Got an error it was %s", err)
|
||||
return false
|
||||
}
|
||||
conn.Close()
|
||||
|
|
Loading…
Reference in New Issue