mirror of https://github.com/docker/docs.git
Merge pull request #2168 from dgageot/2167-b2d-upgrade
FIX #2167 b2d download timeout during upgrade
This commit is contained in:
commit
b0f687b1b8
|
@ -11,7 +11,6 @@ import (
|
|||
"os"
|
||||
"path/filepath"
|
||||
"regexp"
|
||||
"time"
|
||||
|
||||
"github.com/docker/machine/libmachine/log"
|
||||
)
|
||||
|
@ -20,12 +19,8 @@ var (
|
|||
GithubAPIToken string
|
||||
)
|
||||
|
||||
const (
|
||||
timeout = time.Second * 5
|
||||
)
|
||||
|
||||
func defaultTimeout(network, addr string) (net.Conn, error) {
|
||||
return net.DialTimeout(network, addr, timeout)
|
||||
return net.Dial(network, addr)
|
||||
}
|
||||
|
||||
func getClient() *http.Client {
|
||||
|
@ -139,12 +134,14 @@ func removeFileIfExists(name string) error {
|
|||
// DownloadISO downloads boot2docker ISO image for the given tag and save it at dest.
|
||||
func (b *B2dUtils) DownloadISO(dir, file, isoURL string) error {
|
||||
u, err := url.Parse(isoURL)
|
||||
|
||||
var src io.ReadCloser
|
||||
if u.Scheme == "file" || u.Scheme == "" {
|
||||
s, err := os.Open(u.Path)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
src = s
|
||||
} else {
|
||||
client := getClient()
|
||||
|
@ -152,7 +149,12 @@ func (b *B2dUtils) DownloadISO(dir, file, isoURL string) error {
|
|||
if err != nil {
|
||||
return err
|
||||
}
|
||||
src = s.Body
|
||||
|
||||
src = &ReaderWithProgress{
|
||||
ReadCloser: s.Body,
|
||||
out: os.Stdout,
|
||||
expectedLength: s.ContentLength,
|
||||
}
|
||||
}
|
||||
|
||||
defer src.Close()
|
||||
|
@ -170,7 +172,6 @@ func (b *B2dUtils) DownloadISO(dir, file, isoURL string) error {
|
|||
}()
|
||||
|
||||
if _, err := io.Copy(f, src); err != nil {
|
||||
// TODO: display download progress?
|
||||
return err
|
||||
}
|
||||
|
||||
|
@ -194,6 +195,39 @@ func (b *B2dUtils) DownloadISO(dir, file, isoURL string) error {
|
|||
return nil
|
||||
}
|
||||
|
||||
type ReaderWithProgress struct {
|
||||
io.ReadCloser
|
||||
out io.Writer
|
||||
bytesTransferred int64
|
||||
expectedLength int64
|
||||
nextPercentToPrint int64
|
||||
}
|
||||
|
||||
func (r *ReaderWithProgress) Read(p []byte) (int, error) {
|
||||
n, err := r.ReadCloser.Read(p)
|
||||
|
||||
if n > 0 {
|
||||
r.bytesTransferred += int64(n)
|
||||
percentage := r.bytesTransferred * 100 / r.expectedLength
|
||||
|
||||
for percentage >= r.nextPercentToPrint {
|
||||
if r.nextPercentToPrint%10 == 0 {
|
||||
fmt.Fprintf(r.out, "%d%%", r.nextPercentToPrint)
|
||||
} else if r.nextPercentToPrint%2 == 0 {
|
||||
fmt.Fprint(r.out, ".")
|
||||
}
|
||||
r.nextPercentToPrint += 2
|
||||
}
|
||||
}
|
||||
|
||||
return n, err
|
||||
}
|
||||
|
||||
func (r *ReaderWithProgress) Close() error {
|
||||
fmt.Fprintln(r.out)
|
||||
return r.ReadCloser.Close()
|
||||
}
|
||||
|
||||
func (b *B2dUtils) DownloadLatestBoot2Docker(apiURL string) error {
|
||||
latestReleaseURL, err := b.GetLatestBoot2DockerReleaseURL(apiURL)
|
||||
if err != nil {
|
||||
|
|
|
@ -7,6 +7,10 @@ import (
|
|||
"net/http/httptest"
|
||||
"path/filepath"
|
||||
"testing"
|
||||
|
||||
"bytes"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func TestGetLatestBoot2DockerReleaseUrl(t *testing.T) {
|
||||
|
@ -18,14 +22,9 @@ func TestGetLatestBoot2DockerReleaseUrl(t *testing.T) {
|
|||
|
||||
b := NewB2dUtils("/tmp/isos")
|
||||
isoURL, err := b.GetLatestBoot2DockerReleaseURL(ts.URL + "/repos/org/repo/releases")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
expectedURL := fmt.Sprintf("%s/org/repo/releases/download/0.1/boot2docker.iso", ts.URL)
|
||||
if isoURL != expectedURL {
|
||||
t.Fatalf("expected url %s; received %s", expectedURL, isoURL)
|
||||
}
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, fmt.Sprintf("%s/org/repo/releases/download/0.1/boot2docker.iso", ts.URL), isoURL)
|
||||
}
|
||||
|
||||
func TestDownloadIso(t *testing.T) {
|
||||
|
@ -38,49 +37,76 @@ func TestDownloadIso(t *testing.T) {
|
|||
filename := "test"
|
||||
|
||||
tmpDir, err := ioutil.TempDir("", "machine-test-")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
assert.NoError(t, err)
|
||||
|
||||
b := NewB2dUtils("/tmp/artifacts")
|
||||
if err := b.DownloadISO(tmpDir, filename, ts.URL); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
err = b.DownloadISO(tmpDir, filename, ts.URL)
|
||||
|
||||
assert.NoError(t, err)
|
||||
|
||||
data, err := ioutil.ReadFile(filepath.Join(tmpDir, filename))
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
if string(data) != testData {
|
||||
t.Fatalf("expected data \"%s\"; received \"%s\"", testData, string(data))
|
||||
}
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, testData, string(data))
|
||||
}
|
||||
|
||||
func TestGetReleasesRequestNoToken(t *testing.T) {
|
||||
GithubAPIToken = ""
|
||||
|
||||
b2d := NewB2dUtils("/tmp/store")
|
||||
req, err := b2d.getReleasesRequest("http://some.github.api")
|
||||
if err != nil {
|
||||
t.Fatal("Expected err to be nil, got ", err)
|
||||
}
|
||||
|
||||
if req.Header.Get("Authorization") != "" {
|
||||
t.Fatal("Expected not to get an 'Authorization' header, but got one: ", req.Header.Get("Authorization"))
|
||||
}
|
||||
assert.NoError(t, err)
|
||||
assert.Empty(t, req.Header.Get("Authorization"))
|
||||
}
|
||||
|
||||
func TestGetReleasesRequest(t *testing.T) {
|
||||
expectedToken := "CATBUG"
|
||||
GithubAPIToken = expectedToken
|
||||
|
||||
b2d := NewB2dUtils("/tmp/store")
|
||||
|
||||
req, err := b2d.getReleasesRequest("http://some.github.api")
|
||||
if err != nil {
|
||||
t.Fatal("Expected err to be nil, got ", err)
|
||||
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, fmt.Sprintf("token %s", expectedToken), req.Header.Get("Authorization"))
|
||||
}
|
||||
|
||||
if req.Header.Get("Authorization") != fmt.Sprintf("token %s", expectedToken) {
|
||||
t.Fatal("Header was not set as expected: ", req.Header.Get("Authorization"))
|
||||
type MockReadCloser struct {
|
||||
blockLengths []int
|
||||
currentBlock int
|
||||
}
|
||||
|
||||
func (r *MockReadCloser) Read(p []byte) (n int, err error) {
|
||||
n = r.blockLengths[r.currentBlock]
|
||||
r.currentBlock++
|
||||
return
|
||||
}
|
||||
|
||||
func (r *MockReadCloser) Close() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func TestReaderWithProgress(t *testing.T) {
|
||||
readCloser := MockReadCloser{blockLengths: []int{5, 45, 50}}
|
||||
output := new(bytes.Buffer)
|
||||
buffer := make([]byte, 100)
|
||||
|
||||
readerWithProgress := ReaderWithProgress{
|
||||
ReadCloser: &readCloser,
|
||||
out: output,
|
||||
expectedLength: 100,
|
||||
}
|
||||
|
||||
readerWithProgress.Read(buffer)
|
||||
assert.Equal(t, "0%..", output.String())
|
||||
|
||||
readerWithProgress.Read(buffer)
|
||||
assert.Equal(t, "0%....10%....20%....30%....40%....50%", output.String())
|
||||
|
||||
readerWithProgress.Read(buffer)
|
||||
assert.Equal(t, "0%....10%....20%....30%....40%....50%....60%....70%....80%....90%....100%", output.String())
|
||||
|
||||
readerWithProgress.Close()
|
||||
assert.Equal(t, "0%....10%....20%....30%....40%....50%....60%....70%....80%....90%....100%\n", output.String())
|
||||
}
|
||||
|
|
|
@ -42,18 +42,36 @@ type Boot2DockerProvisioner struct {
|
|||
}
|
||||
|
||||
func (provisioner *Boot2DockerProvisioner) Service(name string, action serviceaction.ServiceAction) error {
|
||||
var (
|
||||
err error
|
||||
)
|
||||
|
||||
if _, err = provisioner.SSHCommand(fmt.Sprintf("sudo /etc/init.d/%s %s", name, action.String())); err != nil {
|
||||
_, err := provisioner.SSHCommand(fmt.Sprintf("sudo /etc/init.d/%s %s", name, action.String()))
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
func (provisioner *Boot2DockerProvisioner) upgradeIso() error {
|
||||
// TODO: Ideally, we should not read from mcndirs directory at all.
|
||||
// The driver should be able to communicate how and where to place the
|
||||
// relevant files.
|
||||
b2dutils := mcnutils.NewB2dUtils(mcndirs.GetBaseDir())
|
||||
|
||||
// Check if the driver has specified a custom b2d url
|
||||
jsonDriver, err := json.Marshal(provisioner.GetDriver())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
var d struct {
|
||||
Boot2DockerURL string
|
||||
}
|
||||
json.Unmarshal(jsonDriver, &d)
|
||||
|
||||
log.Info("Downloading latest boot2docker iso...")
|
||||
|
||||
// Usually we call this implicitly, but call it here explicitly to get
|
||||
// the latest default boot2docker ISO.
|
||||
if d.Boot2DockerURL == "" {
|
||||
if err := b2dutils.DownloadLatestBoot2Docker(d.Boot2DockerURL); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
func (provisioner *Boot2DockerProvisioner) upgradeIso() error {
|
||||
log.Info("Stopping machine to do the upgrade...")
|
||||
|
||||
if err := provisioner.Driver.Stop(); err != nil {
|
||||
|
@ -66,30 +84,8 @@ func (provisioner *Boot2DockerProvisioner) upgradeIso() error {
|
|||
|
||||
machineName := provisioner.GetDriver().GetMachineName()
|
||||
|
||||
log.Infof("Upgrading machine %s...", machineName)
|
||||
log.Infof("Upgrading machine %q...", machineName)
|
||||
|
||||
// TODO: Ideally, we should not read from mcndirs directory at all.
|
||||
// The driver should be able to communicate how and where to place the
|
||||
// relevant files.
|
||||
b2dutils := mcnutils.NewB2dUtils(mcndirs.GetBaseDir())
|
||||
|
||||
//Check if the driver has specifed a custom b2d url
|
||||
jsonDriver, err := json.Marshal(provisioner.GetDriver())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
var d struct {
|
||||
Boot2DockerURL string
|
||||
}
|
||||
json.Unmarshal(jsonDriver, &d)
|
||||
|
||||
// Usually we call this implicitly, but call it here explicitly to get
|
||||
// the latest default boot2docker ISO.
|
||||
if d.Boot2DockerURL == "" {
|
||||
if err := b2dutils.DownloadLatestBoot2Docker(d.Boot2DockerURL); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
// Either download the latest version of the b2d url that was explicitly
|
||||
// specified when creating the VM or copy the (updated) default ISO
|
||||
if err := b2dutils.CopyIsoToMachineDir(d.Boot2DockerURL, machineName); err != nil {
|
||||
|
|
|
@ -72,6 +72,8 @@ func Register(name string, p *RegisteredProvisioner) {
|
|||
}
|
||||
|
||||
func DetectProvisioner(d drivers.Driver) (Provisioner, error) {
|
||||
log.Info("Detecting the provisioner...")
|
||||
|
||||
osReleaseOut, err := drivers.RunSSHCommandFromDriver(d, "cat /etc/os-release")
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("Error getting SSH command: %s", err)
|
||||
|
|
Loading…
Reference in New Issue