Skip to content

Commit

Permalink
Support the new release URL scheme
Browse files Browse the repository at this point in the history
* Docker now releases to the docker store only.
* Release candidate URL scheme has changed.
* OS slug has changed.
  • Loading branch information
carolynvs committed Aug 15, 2017
1 parent 70888f9 commit 4ec1b61
Show file tree
Hide file tree
Showing 15 changed files with 165 additions and 71 deletions.
70 changes: 62 additions & 8 deletions dvm-helper/dockerversion/dockerversion.go
Original file line number Diff line number Diff line change
Expand Up @@ -30,13 +30,69 @@ func Parse(value string) Version {
v := Version{raw: value}
semver, err := semver.NewVersion(value)
if err == nil {
v.semver = semver
v.semver = &semver
} else {
v.alias = value
}
return v
}

func (version Version) BuildDownloadURL(mirrorURL string) (url string, archived bool) {
var releaseSlug, versionSlug, extSlug string

archivedReleaseCutoff, _ := semver.NewVersion("1.11.0-rc1")
dockerStoreCutoff, _ := semver.NewVersion("17.06.0-ce")

var edgeVersion Version
if version.IsExperimental() {
// TODO: Figure out the latest edge version
edgeVersion = Parse("17.06.0-ce")
}

// Docker Store Download
if version.IsExperimental() || !version.semver.LessThan(dockerStoreCutoff) {
archived = true
extSlug = archiveFileExt
if mirrorURL == "" {
mirrorURL = "download.docker.com"
}
if version.IsExperimental() {
releaseSlug = "edge"
versionSlug = edgeVersion.String()
} else if version.IsPrerelease() {
releaseSlug = "test"
versionSlug = version.String()
} else {
releaseSlug = "stable"
versionSlug = version.String()
}

url = fmt.Sprintf("https://%s/%s/static/%s/%s/docker-%s%s",
mirrorURL, mobyOS, releaseSlug, dockerArch, versionSlug, extSlug)
return
} else { // Original Download
archived = !version.semver.LessThan(archivedReleaseCutoff)
versionSlug = version.String()
if archived {
extSlug = archiveFileExt
} else {
extSlug = binaryFileExt
}
if mirrorURL == "" {
mirrorURL = "docker.com"
}
if version.IsPrerelease() {
releaseSlug = "test"
} else {
releaseSlug = "get"
}

url = fmt.Sprintf("https://%s.%s/builds/%s/%s/docker-%s%s",
releaseSlug, mirrorURL, dockerOS, dockerArch, versionSlug, extSlug)
return
}
}

func (version Version) IsPrerelease() bool {
if version.semver == nil {
return false
Expand Down Expand Up @@ -78,11 +134,6 @@ func (version *Version) SetAsExperimental() {
version.alias = ExperimentalAlias
}

func (version Version) ShouldUseArchivedRelease() bool {
cutoff, _ := semver.NewConstraint(">= 1.11.0-rc1")
return version.IsExperimental() || cutoff.Check(version.semver)
}

func (version Version) String() string {
if version.alias != "" && version.semver != nil {
return fmt.Sprintf("%s (%s)", version.alias, version.formatRaw())
Expand Down Expand Up @@ -129,7 +180,10 @@ func (version Version) InRange(r string) (bool, error) {
if err != nil {
return false, errors.Wrapf(err, "Unable to parse range constraint: %s", r)
}
return c.Check(version.semver), nil
if version.semver == nil {
return false, nil
}
return c.Matches(*version.semver) == nil, nil
}

// Compare compares Versions v to o:
Expand All @@ -138,7 +192,7 @@ func (version Version) InRange(r string) (bool, error) {
// 1 == v is greater than o
func (v Version) Compare(o Version) int {
if v.semver != nil && o.semver != nil {
return v.semver.Compare(o.semver)
return v.semver.Compare(*o.semver)
}

return strings.Compare(v.alias, o.alias)
Expand Down
6 changes: 6 additions & 0 deletions dvm-helper/dockerversion/dockerversion.nix.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
// +build !windows

package dockerversion

const archiveFileExt string = ".tgz"
const binaryFileExt string = ""
3 changes: 3 additions & 0 deletions dvm-helper/dockerversion/dockerversion_386.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
package dockerversion

const dockerArch string = "i386"
3 changes: 3 additions & 0 deletions dvm-helper/dockerversion/dockerversion_amd64.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
package dockerversion

const dockerArch string = "x86_64"
4 changes: 4 additions & 0 deletions dvm-helper/dockerversion/dockerversion_darwin.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
package dockerversion

const dockerOS string = "Darwin"
const mobyOS string = "mac"
4 changes: 4 additions & 0 deletions dvm-helper/dockerversion/dockerversion_linux.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
package dockerversion

const dockerOS string = "Linux"
const mobyOS string = "linux"
103 changes: 75 additions & 28 deletions dvm-helper/dockerversion/dockerversion_test.go
Original file line number Diff line number Diff line change
@@ -1,76 +1,72 @@
package dockerversion_test
package dockerversion

import (
"fmt"
"net/http"
"testing"

"github.com/howtowhale/dvm/dvm-helper/dockerversion"
"github.com/pkg/errors"
"github.com/stretchr/testify/assert"
)

func TestStripLeadingV(t *testing.T) {
v := dockerversion.Parse("v1.0.0")
v := Parse("v1.0.0")
assert.Equal(t, "1.0.0", v.String(), "Leading v should be stripped from the string representation")
assert.Equal(t, "1.0.0", v.Name(), "Leading v should be stripped from the name")
assert.Equal(t, "1.0.0", v.Value(), "Leading v should be stripped from the version value")
}

func TestIsPrerelease(t *testing.T) {
var v dockerversion.Version
var v Version

v = dockerversion.Parse("17.3.0-ce-rc1")
v = Parse("17.3.0-ce-rc1")
assert.True(t, v.IsPrerelease(), "%s should be a prerelease", v)

v = dockerversion.Parse("1.12.4-rc1")
v = Parse("1.12.4-rc1")
assert.True(t, v.IsPrerelease(), "%s should be a prerelease", v)

v = dockerversion.Parse("1.12.4-beta.1")
v = Parse("1.12.4-beta.1")
assert.True(t, v.IsPrerelease(), "%s should be a prerelease", v)

v = dockerversion.Parse("1.12.4-alpha-2")
v = Parse("1.12.4-alpha-2")
assert.True(t, v.IsPrerelease(), "%s should be a prerelease", v)

v = dockerversion.Parse("17.3.0-ce")
v = Parse("17.3.0-ce")
assert.False(t, v.IsPrerelease(), "%s should NOT be a prerelease", v)
}

func TestPrereleaseUsesArchivedReleases(t *testing.T) {
v := dockerversion.Parse("v1.12.5-rc1")

assert.True(t, v.ShouldUseArchivedRelease())
}

func TestLeadingZeroInVersion(t *testing.T) {
v := dockerversion.Parse("v17.03.0-ce")
v := Parse("v17.03.0-ce")

assert.Equal(t, "17.03.0-ce", v.String(), "Leading zeroes in the version should be preserved")
}

func TestSystemAlias(t *testing.T) {
v := dockerversion.Parse(dockerversion.SystemAlias)
v := Parse(SystemAlias)
assert.Empty(t, v.Slug(),
"The system alias should not have a slug")
assert.Equal(t, dockerversion.SystemAlias, v.String(),
assert.Equal(t, SystemAlias, v.String(),
"An empty alias should only print the alias")
assert.Equal(t, dockerversion.SystemAlias, v.Name(),
assert.Equal(t, SystemAlias, v.Name(),
"The name for an aliased version should be its alias")
assert.Equal(t, "", v.Value(),
"The value for an empty aliased version should be empty")
}

func TestExperimentalAlias(t *testing.T) {
v := dockerversion.Parse(dockerversion.ExperimentalAlias)
assert.Equal(t, dockerversion.ExperimentalAlias, v.Slug(),
v := Parse(ExperimentalAlias)
assert.Equal(t, ExperimentalAlias, v.Slug(),
"The slug for the experimental version should be 'experimental'")
assert.Equal(t, dockerversion.ExperimentalAlias, v.String(),
assert.Equal(t, ExperimentalAlias, v.String(),
"An empty alias should only print the alias")
assert.Equal(t, dockerversion.ExperimentalAlias, v.Name(),
assert.Equal(t, ExperimentalAlias, v.Name(),
"The name for an aliased version should be its alias")
assert.Equal(t, "", v.Value(),
"The value for an empty aliased version should be empty")
}

func TestAlias(t *testing.T) {
v := dockerversion.NewAlias("prod", "1.2.3")
v := NewAlias("prod", "1.2.3")
assert.Equal(t, "1.2.3", v.Slug(),
"The slug for an aliased version should be its semver value")
assert.Equal(t, "prod (1.2.3)", v.String(),
Expand All @@ -82,7 +78,7 @@ func TestAlias(t *testing.T) {
}

func TestSemanticVersion(t *testing.T) {
v := dockerversion.Parse("1.2.3")
v := Parse("1.2.3")
assert.Equal(t, "1.2.3", v.Slug(),
"The slug for a a semantic version should be its semver value")
assert.Equal(t, "1.2.3", v.String(),
Expand All @@ -94,13 +90,64 @@ func TestSemanticVersion(t *testing.T) {
}

func TestSetAsExperimental(t *testing.T) {
v := dockerversion.Parse("1.2.3")
v := Parse("1.2.3")
v.SetAsExperimental()
assert.True(t, v.IsExperimental())
}

func TestSetAsSystem(t *testing.T) {
v := dockerversion.Parse("1.2.3")
v := Parse("1.2.3")
v.SetAsSystem()
assert.True(t, v.IsSystem())
}
}

func TestVersion_BuildDownloadURL(t *testing.T) {
testcases := map[Version]struct {
wantURL string
wantArchived bool
}{
// original download location, without compression
Parse("1.10.3"): {fmt.Sprintf("https://get.docker.com/builds/%s/%s/docker-1.10.3", dockerOS, dockerArch), false},

// original download location, without compression, prerelease
Parse("1.10.0-rc1"): {fmt.Sprintf("https://test.docker.com/builds/%s/%s/docker-1.10.0-rc1", dockerOS, dockerArch), false},

// compressed binaries
Parse("1.11.0-rc1"): {fmt.Sprintf("https://test.docker.com/builds/%s/%s/docker-1.11.0-rc1.tgz", dockerOS, dockerArch), true},

// original version scheme, prerelease binaries
Parse("1.13.0-rc1"): {fmt.Sprintf("https://test.docker.com/builds/%s/%s/docker-1.13.0-rc1.tgz", dockerOS, dockerArch), true},

// yearly notation, original download location, release location
Parse("17.03.0-ce"): {fmt.Sprintf("https://get.docker.com/builds/%s/%s/docker-17.03.0-ce%s", dockerOS, dockerArch, archiveFileExt), true},

// docker store download
Parse("17.06.0-ce"): {fmt.Sprintf("https://download.docker.com/%s/static/stable/%s/docker-17.06.0-ce.tgz", mobyOS, dockerArch), true},

// docker store download, prerelease
Parse("17.07.0-ce-rc1"): {fmt.Sprintf("https://download.docker.com/%s/static/test/%s/docker-17.07.0-ce-rc1.tgz", mobyOS, dockerArch), true},
}

for version, testcase := range testcases {
t.Run(version.String(), func(t *testing.T) {
gotURL, gotArchived := version.BuildDownloadURL("")
if testcase.wantURL != gotURL {
t.Fatalf("Expected %s to be downloaded from '%s', but got '%s'", version, testcase.wantURL, gotURL)
}
if testcase.wantArchived != gotArchived {
t.Fatalf("Expected %s to use an archived download strategy", version)
}

response, err := http.DefaultClient.Head(gotURL)
if err != nil {
t.Fatalf("%#v", errors.Wrapf(err, "Unable to download release from %s", gotURL))
}

if response.StatusCode != 200 {
t.Fatalf("Unexpected status code (%d) when downloading %s", response.StatusCode, gotURL)
}
})
}
}

func TestVersion
6 changes: 6 additions & 0 deletions dvm-helper/dockerversion/dockerversion_windows.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
package dockerversion

const dockerOS string = "Windows"
const mobyOS string = "win"
const archiveFileExt string = ".zip"
const binaryFileExt string = ".exe"
1 change: 0 additions & 1 deletion dvm-helper/dvm-helper.386.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,5 +2,4 @@

package main

const dockerArch string = "i386"
const dvmArch string = "i386"
1 change: 0 additions & 1 deletion dvm-helper/dvm-helper.amd64.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,5 +2,4 @@

package main

const dockerArch string = "x86_64"
const dvmArch string = "x86_64"
1 change: 0 additions & 1 deletion dvm-helper/dvm-helper.darwin.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,5 +2,4 @@

package main

const dockerOS string = "Darwin"
const dvmOS string = "Darwin"
30 changes: 2 additions & 28 deletions dvm-helper/dvm-helper.go
Original file line number Diff line number Diff line change
Expand Up @@ -441,37 +441,11 @@ func install(version dockerversion.Version) {
}
}

func buildDownloadURL(version dockerversion.Version) string {
dockerVersion := version.Value()
if version.IsExperimental() {
dockerVersion = "latest"
}

if mirrorURL == "" {
mirrorURL = "https://get.docker.com/builds"
if version.IsExperimental() {
writeDebug("Downloading from experimental builds mirror")
mirrorURL = "https://experimental.docker.com/builds"
}
if version.IsPrerelease() {
writeDebug("Downloading from prerelease builds mirror")
mirrorURL = "https://test.docker.com/builds"
}
}

// New Docker versions are released in a zip file, vs. the old way of releasing the client binary only
if version.ShouldUseArchivedRelease() {
return fmt.Sprintf("%s/%s/%s/docker-%s%s", mirrorURL, dockerOS, dockerArch, dockerVersion, archiveFileExt)
}

return fmt.Sprintf("%s/%s/%s/docker-%s%s", mirrorURL, dockerOS, dockerArch, dockerVersion, binaryFileExt)
}

func downloadRelease(version dockerversion.Version) {
url := buildDownloadURL(version)
url, archived := version.BuildDownloadURL(mirrorURL)
binaryName := getBinaryName()
binaryPath := filepath.Join(getVersionDir(version), binaryName)
if version.ShouldUseArchivedRelease() {
if archived {
archivedFile := path.Join("docker", binaryName)
downloadArchivedFileWithChecksum(url, archivedFile, binaryPath)
} else {
Expand Down
1 change: 0 additions & 1 deletion dvm-helper/dvm-helper.linux.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,5 +2,4 @@

package main

const dockerOS string = "Linux"
const dvmOS string = "Linux"
1 change: 0 additions & 1 deletion dvm-helper/dvm-helper.nix.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,6 @@ import (
)

const binaryFileExt string = ""
const archiveFileExt string = ".tgz"

func upgradeSelf(version string) {
binaryURL := buildDvmReleaseURL(version, dvmOS, dvmArch, "dvm-helper")
Expand Down
2 changes: 0 additions & 2 deletions dvm-helper/dvm-helper.windows.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,10 +9,8 @@ import (
)
import "strings"

const dockerOS string = "Windows"
const dvmOS string = "Windows"
const binaryFileExt string = ".exe"
const archiveFileExt string = ".zip"

func upgradeSelf(version string) {
binaryURL := buildDvmReleaseURL(version, dvmOS, dvmArch, "dvm-helper.exe")
Expand Down

0 comments on commit 4ec1b61

Please sign in to comment.