@@ -2,10 +2,14 @@ package dockerversion
22
33import (
44 "fmt"
5+ "log"
6+ "net/http"
7+ "path/filepath"
58 "sort"
69 "strings"
710
811 "github.com/Masterminds/semver"
12+ "github.com/howtowhale/dvm/dvm-helper/internal/downloader"
913 "github.com/pkg/errors"
1014)
1115
@@ -37,12 +41,9 @@ func Parse(value string) Version {
3741 return v
3842}
3943
40- func (version Version ) BuildDownloadURL (mirror string ) (url string , archived bool , checksumed bool , err error ) {
44+ func (version Version ) buildDownloadURL (mirror string , forcePrerelease bool ) (url string , archived bool , checksumed bool , err error ) {
4145 var releaseSlug , versionSlug , extSlug string
4246
43- archivedReleaseCutoff , _ := semver .NewVersion ("1.11.0-rc1" )
44- dockerStoreCutoff , _ := semver .NewVersion ("17.06.0-ce" )
45-
4647 var edgeVersion Version
4748 if version .IsEdge () {
4849 edgeVersion , err = findLatestEdgeVersion (mirror )
@@ -52,7 +53,7 @@ func (version Version) BuildDownloadURL(mirror string) (url string, archived boo
5253 }
5354
5455 // Docker Store Download
55- if version .IsEdge () || ! version . semver . LessThan ( dockerStoreCutoff ) {
56+ if version .shouldBeInDockerStore ( ) {
5657 archived = true
5758 checksumed = false
5859 extSlug = archiveFileExt
@@ -62,7 +63,7 @@ func (version Version) BuildDownloadURL(mirror string) (url string, archived boo
6263 if version .IsEdge () {
6364 releaseSlug = "edge"
6465 versionSlug = edgeVersion .String ()
65- } else if version .IsPrerelease () {
66+ } else if version .IsPrerelease () || forcePrerelease {
6667 releaseSlug = "test"
6768 versionSlug = version .String ()
6869 } else {
@@ -74,7 +75,7 @@ func (version Version) BuildDownloadURL(mirror string) (url string, archived boo
7475 mirror , mobyOS , releaseSlug , dockerArch , versionSlug , extSlug )
7576 return
7677 } else { // Original Download
77- archived = ! version .semver . LessThan ( archivedReleaseCutoff )
78+ archived = version .shouldBeArchived ( )
7879 checksumed = true
7980 versionSlug = version .String ()
8081 if archived {
@@ -97,6 +98,70 @@ func (version Version) BuildDownloadURL(mirror string) (url string, archived boo
9798 }
9899}
99100
101+ // Download a Docker release.
102+ // version - the desired version.
103+ // mirrorURL - optional alternate download location.
104+ // binaryPath - full path to where the Docker client binary should be saved.
105+ func (version Version ) Download (mirrorURL string , binaryPath string , l * log.Logger ) error {
106+ err := version .download (false , mirrorURL , binaryPath , l )
107+ if err != nil && ! version .IsPrerelease () && version .shouldBeInDockerStore () {
108+ // Docker initially publishes non-rc version versions to the test location
109+ // and then later republishes to the stable location
110+ // Retry stable versions against test to find "unstable" stable versions. :-)
111+ l .Printf ("Could not find a stable release for %s, checking for a test release\n " , version )
112+ retryErr := version .download (true , mirrorURL , binaryPath , l )
113+ return errors .Wrapf (retryErr , "Attempted to fallback to downloading from the prerelease location after downloading from the stable location failed: %s" , err .Error ())
114+ }
115+ return err
116+ }
117+
118+ func (version Version ) download (forcePrerelease bool , mirrorURL string , binaryPath string , l * log.Logger ) error {
119+ url , archived , checksumed , err := version .buildDownloadURL (mirrorURL , forcePrerelease )
120+ if err != nil {
121+ return errors .Wrapf (err , "Unable to determine the download URL for %s" , version )
122+ }
123+
124+ l .Printf ("Checking if %s can be found at %s" , version , url )
125+ head , err := http .Head (url )
126+ if err != nil {
127+ return errors .Wrapf (err , "Unable to determine if %s is a valid version" , version )
128+ }
129+ if head .StatusCode >= 400 {
130+ return errors .Errorf ("Version %s not found (%v) - try `dvm ls-remote` to browse available versions" , version , head .StatusCode )
131+ }
132+
133+ d := downloader .New (l )
134+ binaryName := filepath .Base (binaryPath )
135+
136+ if archived {
137+ archivedFile := filepath .Join ("docker" , binaryName )
138+ if checksumed {
139+ return d .DownloadArchivedFileWithChecksum (url , archivedFile , binaryPath )
140+ }
141+ return d .DownloadArchivedFile (url , archivedFile , binaryPath )
142+ }
143+
144+ if checksumed {
145+ return d .DownloadFileWithChecksum (url , binaryPath )
146+ }
147+ return d .DownloadFile (url , binaryPath )
148+ }
149+
150+ func (version Version ) shouldBeInDockerStore () bool {
151+ if version .IsEdge () {
152+ return true
153+ }
154+
155+ dockerStoreCutoff , _ := semver .NewVersion ("17.06.0-ce" )
156+
157+ return version .semver != nil && ! version .semver .LessThan (dockerStoreCutoff )
158+ }
159+
160+ func (version Version ) shouldBeArchived () bool {
161+ archivedReleaseCutoff , _ := semver .NewVersion ("1.11.0-rc1" )
162+ return version .semver != nil && ! version .semver .LessThan (archivedReleaseCutoff )
163+ }
164+
100165func (version Version ) IsPrerelease () bool {
101166 if version .semver == nil {
102167 return false
0 commit comments