Skip to content

Commit

Permalink
Merge pull request #176 from carolynvs/experimental
Browse files Browse the repository at this point in the history
Support "edge" releases
  • Loading branch information
carolynvs authored Oct 19, 2017
2 parents 9d9a4c1 + ba9d98d commit ca8c06c
Show file tree
Hide file tree
Showing 11 changed files with 265 additions and 89 deletions.
42 changes: 23 additions & 19 deletions dvm-helper/dockerversion/dockerversion.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ import (
)

const SystemAlias = "system"
const ExperimentalAlias = "experimental"
const EdgeAlias = "edge"

type Version struct {
// Since Docker versions aren't valid versions, (it has leading zeroes)
Expand All @@ -37,26 +37,30 @@ func Parse(value string) Version {
return v
}

func (version Version) BuildDownloadURL(mirrorURL string) (url string, archived bool) {
func (version Version) BuildDownloadURL(mirror string) (url string, archived bool, checksumed bool, err error) {
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() {
if version.IsEdge() {
// TODO: Figure out the latest edge version
edgeVersion = Parse("17.06.0-ce")
edgeVersion, err = findLatestEdgeVersion(mirror)
if err != nil {
return
}
}

// Docker Store Download
if version.IsExperimental() || !version.semver.LessThan(dockerStoreCutoff) {
if version.IsEdge() || !version.semver.LessThan(dockerStoreCutoff) {
archived = true
checksumed = !version.IsEdge()
extSlug = archiveFileExt
if mirrorURL == "" {
mirrorURL = "download.docker.com"
if mirror == "" {
mirror = "download.docker.com"
}
if version.IsExperimental() {
if version.IsEdge() {
releaseSlug = "edge"
versionSlug = edgeVersion.String()
} else if version.IsPrerelease() {
Expand All @@ -68,18 +72,19 @@ func (version Version) BuildDownloadURL(mirrorURL string) (url string, archived
}

url = fmt.Sprintf("https://%s/%s/static/%s/%s/docker-%s%s",
mirrorURL, mobyOS, releaseSlug, dockerArch, versionSlug, extSlug)
mirror, mobyOS, releaseSlug, dockerArch, versionSlug, extSlug)
return
} else { // Original Download
archived = !version.semver.LessThan(archivedReleaseCutoff)
checksumed = true
versionSlug = version.String()
if archived {
extSlug = archiveFileExt
} else {
extSlug = binaryFileExt
}
if mirrorURL == "" {
mirrorURL = "docker.com"
if mirror == "" {
mirror = "docker.com"
}
if version.IsPrerelease() {
releaseSlug = "test"
Expand All @@ -88,7 +93,7 @@ func (version Version) BuildDownloadURL(mirrorURL string) (url string, archived
}

url = fmt.Sprintf("https://%s.%s/builds/%s/%s/docker-%s%s",
releaseSlug, mirrorURL, dockerOS, dockerArch, versionSlug, extSlug)
releaseSlug, mirror, dockerOS, dockerArch, versionSlug, extSlug)
return
}
}
Expand Down Expand Up @@ -126,12 +131,12 @@ func (version *Version) SetAsSystem() {
version.alias = SystemAlias
}

func (version Version) IsExperimental() bool {
return version.alias == ExperimentalAlias
func (version Version) IsEdge() bool {
return version.alias == EdgeAlias
}

func (version *Version) SetAsExperimental() {
version.alias = ExperimentalAlias
func (version *Version) SetAsEdge() {
version.alias = EdgeAlias
}

func (version Version) String() string {
Expand All @@ -153,7 +158,7 @@ func (version Version) Slug() string {
if version.IsSystem() {
return ""
}
if version.IsExperimental() {
if version.IsEdge() {
return version.alias
}
return version.formatRaw()
Expand All @@ -168,8 +173,7 @@ func (version Version) Name() string {

func (version Version) formatRaw() string {
value := version.raw
prefix := value[0:1]
if strings.ToLower(prefix) == "v" {
if strings.HasPrefix(strings.ToLower(value), "v") {
value = value[1:]
}
return value
Expand Down
69 changes: 56 additions & 13 deletions dvm-helper/dockerversion/dockerversion_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -53,18 +53,31 @@ func TestSystemAlias(t *testing.T) {
"The value for an empty aliased version should be empty")
}

func TestExperimentalAlias(t *testing.T) {
v := Parse(ExperimentalAlias)
assert.Equal(t, ExperimentalAlias, v.Slug(),
"The slug for the experimental version should be 'experimental'")
assert.Equal(t, ExperimentalAlias, v.String(),
func TestEdgeAlias(t *testing.T) {
v := Parse(EdgeAlias)
assert.Equal(t, EdgeAlias, v.Slug(),
"The slug for the edge version should be 'edge'")
assert.Equal(t, EdgeAlias, v.String(),
"An empty alias should only print the alias")
assert.Equal(t, ExperimentalAlias, v.Name(),
assert.Equal(t, EdgeAlias, 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 TestEdgeAliasWithVersion(t *testing.T) {
v := Parse("17.06.0-ce+02c1d87")
v.SetAsEdge()
assert.Equal(t, EdgeAlias, v.Slug(),
"The slug for the edge version should be 'edge'")
assert.Equal(t, "edge (17.06.0-ce+02c1d87)", v.String(),
"The string representation should include the alias and version")
assert.Equal(t, EdgeAlias, v.Name(),
"The name for an aliased version should be its alias")
assert.Equal(t, "17.06.0-ce+02c1d87", v.Value(),
"The value for a populated alias should be the version")
}

func TestAlias(t *testing.T) {
v := NewAlias("prod", "1.2.3")
assert.Equal(t, "1.2.3", v.Slug(),
Expand All @@ -89,10 +102,10 @@ func TestSemanticVersion(t *testing.T) {
"The value for a semantic version should be its semver value")
}

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

func TestSetAsSystem(t *testing.T) {
Expand Down Expand Up @@ -126,20 +139,24 @@ func TestVersion_BuildDownloadURL(t *testing.T) {

// 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("")
gotURL, gotArchived, gotChecksumed, err := version.BuildDownloadURL("")
if err != nil {
t.Fatal(err)
}

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)
}
if !gotChecksumed {
t.Fatalf("Expected %s to provide a checksum", version)
}

response, err := http.DefaultClient.Head(gotURL)
if err != nil {
Expand All @@ -152,3 +169,29 @@ func TestVersion_BuildDownloadURL(t *testing.T) {
})
}
}

func TestVersion_DownloadEdgeRelease(t *testing.T) {
version := Parse("edge")

url, archived, checksumed, err := version.BuildDownloadURL("")
if err != nil {
t.Fatalf("%#v", err)
}

if !archived {
t.Fatal("Expected the edge release to be archived.")
}

if checksumed {
t.Fatal("Expected the edge release to NOT be checksumed.")
}

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

if response.StatusCode != 200 {
t.Fatalf("Unexpected status code (%d) when downloading %s", response.StatusCode, url)
}
}
57 changes: 57 additions & 0 deletions dvm-helper/dockerversion/edge_version.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
package dockerversion

import (
"fmt"
"net/http"
"net/url"

"bytes"
"regexp"

"github.com/pkg/errors"
)

var hrefRegex = regexp.MustCompile("href=\"docker-(.*).tgz\"")

func findLatestEdgeVersion(mirrorURL string) (Version, error) {
if mirrorURL == "" {
mirrorURL = "https://download.docker.com"
}

mirror, err := url.Parse(mirrorURL)
if err != nil {
return Version{}, errors.Wrapf(err, "Unable to parse the mirror URL: %s", mirrorURL)
}

edgeReleasesUrl := fmt.Sprintf("%s://%s/%s/static/edge/%s", mirror.Scheme, mirror.Host, mobyOS, dockerArch)
response, err := http.Get(edgeReleasesUrl)
if err != nil {
return Version{}, errors.Wrapf(err, "Unable to list edge releases at %s", edgeReleasesUrl)
}
defer response.Body.Close()

b := bytes.Buffer{}
_, err = b.ReadFrom(response.Body)
if err != nil {
}
errors.Wrapf(err, "Unable to read the listing of edge releases at %s", edgeReleasesUrl)

matches := hrefRegex.FindAllStringSubmatch(b.String(), -1)
var results []Version
for _, match := range matches {
version := Parse(match[1])
if version.semver == nil {
continue
}
results = append(results, version)
}

Sort(results)

if len(results) == 0 {
return Version{}, errors.Errorf("No valid edge versions were found at %s", edgeReleasesUrl)
}

last := len(results) - 1
return results[last], nil
}
28 changes: 28 additions & 0 deletions dvm-helper/dockerversion/edge_version_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
package dockerversion

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

"github.com/howtowhale/dvm/dvm-helper/internal/test"
)

func TestVersion_findLatestEdgeVersion(t *testing.T) {
releaseListing := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Content-Type", "text/html")
fmt.Fprintln(w, test.LoadTestData("edge_releases.html"))
}))

v, err := findLatestEdgeVersion(releaseListing.URL)
if err != nil {
t.Fatalf("%#v", err)
}

wantV := "17.06.0-ce"
gotV := v.String()
if wantV != gotV {
t.Fatalf("Expected '%s', got '%s'", wantV, gotV)
}
}
15 changes: 15 additions & 0 deletions dvm-helper/dockerversion/testdata/edge_releases.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Index of /linux/static/edge/x86_64/</title>
</head>
<body>
<h1>Index of /linux/static/edge/x86_64/</h1>
<hr>
<pre><a href="../">../</a>
<a href="docker-17.03.0-ce.tgz">docker-17.03.0-ce.tgz</a> 2017-03-01 11:46 27M
<a href="docker-17.04.0-ce.tgz">docker-17.04.0-ce.tgz</a> 2017-04-06 01:07 26M
<a href="docker-17.05.0-ce.tgz">docker-17.05.0-ce.tgz</a> 2017-05-05 04:11 28M
<a href="docker-17.06.0-ce.tgz">docker-17.06.0-ce.tgz</a> 2017-06-28 04:51 29M
</pre><hr></body></html>
Loading

0 comments on commit ca8c06c

Please sign in to comment.