Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Support new release scheme #168

Merged
merged 2 commits into from
Aug 16, 2017
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
6 changes: 3 additions & 3 deletions Gopkg.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion Gopkg.toml
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@

[[constraint]]
name = "github.com/Masterminds/semver"
version = "1.2.2"
branch = "2.x"

[[constraint]]
name = "github.com/codegangsta/cli"
Expand Down
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"
104 changes: 76 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,65 @@ 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},

// latest edge/experimental
Parse("experimental"): {fmt.Sprintf("https://download.docker.com/%s/static/edge/%s/docker-17.06.0-ce.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)
}
})
}
}
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
Loading