From c604da28cd9d1f70ce91bf6c81f651d6132715b1 Mon Sep 17 00:00:00 2001 From: marwan-at-work Date: Thu, 26 Jul 2018 16:32:04 -0400 Subject: [PATCH 1/6] download: add list from go cli --- cmd/proxy/actions/app_proxy.go | 4 +- pkg/config/env/go.go | 6 ++ pkg/download/download.go | 17 ++++ pkg/download/goget/goget.go | 173 +++++++++++++++++++++++++++++++++ pkg/download/list.go | 20 ++-- 5 files changed, 210 insertions(+), 10 deletions(-) create mode 100644 pkg/download/download.go create mode 100644 pkg/download/goget/goget.go diff --git a/cmd/proxy/actions/app_proxy.go b/cmd/proxy/actions/app_proxy.go index 0443cc589..6d483e67c 100644 --- a/cmd/proxy/actions/app_proxy.go +++ b/cmd/proxy/actions/app_proxy.go @@ -3,6 +3,7 @@ package actions import ( "github.com/gobuffalo/buffalo" "github.com/gomods/athens/pkg/download" + "github.com/gomods/athens/pkg/download/goget" "github.com/gomods/athens/pkg/log" "github.com/gomods/athens/pkg/module" "github.com/gomods/athens/pkg/storage" @@ -16,8 +17,9 @@ func addProxyRoutes( ) error { app.GET("/", proxyHomeHandler) + dp := goget.New() // Download Protocol - app.GET(download.PathList, download.ListHandler(storage, proxy)) + app.GET(download.PathList, download.ListHandler(dp, lggr, proxy)) app.GET(download.PathVersionInfo, cacheMissHandler(download.VersionInfoHandler(storage, proxy), app.Worker, mf, lggr)) app.GET(download.PathVersionModule, cacheMissHandler(download.VersionModuleHandler(storage, proxy), app.Worker, mf, lggr)) app.GET(download.PathVersionZip, cacheMissHandler(download.VersionZipHandler(storage, proxy, lggr), app.Worker, mf, lggr)) diff --git a/pkg/config/env/go.go b/pkg/config/env/go.go index f5a9c7299..0567196f5 100644 --- a/pkg/config/env/go.go +++ b/pkg/config/env/go.go @@ -21,3 +21,9 @@ func GoPath() (string, error) { return env, nil } + +// GoBinPath returns the path to Go's executable binary +// this binary must have Go Modules enabled. +func GoBinPath() string { + return envy.Get("GO_BIN_PATH", "vgo") +} diff --git a/pkg/download/download.go b/pkg/download/download.go new file mode 100644 index 000000000..d5a7eb320 --- /dev/null +++ b/pkg/download/download.go @@ -0,0 +1,17 @@ +package download + +import ( + "context" + "io" + + "github.com/gomods/athens/pkg/storage" +) + +// Protocol is the download protocol defined by cmd/go +type Protocol interface { + List(ctx context.Context, module string) ([]string, error) + Info(ctx context.Context, module, version string) (*storage.RevInfo, error) + Latest(ctx context.Context, module string) (*storage.RevInfo, error) + GoMod(ctx context.Context, module, version string) ([]byte, error) + Zip(ctx context.Context, module, version string) (io.ReadCloser, error) +} diff --git a/pkg/download/goget/goget.go b/pkg/download/goget/goget.go new file mode 100644 index 000000000..1dd202a53 --- /dev/null +++ b/pkg/download/goget/goget.go @@ -0,0 +1,173 @@ +package goget + +import ( + "context" + "encoding/json" + "fmt" + "io" + "os/exec" + "path/filepath" + "strings" + "time" + + "github.com/gomods/athens/pkg/module" + + "github.com/gomods/athens/pkg/config" + "github.com/gomods/athens/pkg/config/env" + "github.com/gomods/athens/pkg/download" + "github.com/gomods/athens/pkg/errors" + "github.com/gomods/athens/pkg/storage" + "github.com/spf13/afero" +) + +// New returns a download protocol by using +// go get. You must have a modules supported +// go binary for this to work. +func New() download.Protocol { + return &goget{ + goBinPath: env.GoBinPath(), + fs: afero.NewOsFs(), + } +} + +type goget struct { + goBinPath string + fs afero.Fs +} + +func (gg *goget) List(ctx context.Context, mod string) ([]string, error) { + const op errors.Op = "goget.List" + lr, err := gg.list(op, mod) + if err != nil { + return nil, err + } + + return lr.Versions, nil +} + +type listResp struct { + Path string + Version string + Versions []string + Time time.Time +} + +// dummyMod creates a random go module so that listing +// a depdencny's upstream versions can run from the go cli. +func dummyMod(fs afero.Fs, repoRoot string) error { + const op errors.Op = "goget.dummyMod" + gomodPath := filepath.Join(repoRoot, "go.mod") + gomodContent := []byte("module mod") + if err := afero.WriteFile(fs, gomodPath, gomodContent, 0666); err != nil { + return errors.E(op, err) + } + sourcePath := filepath.Join(repoRoot, "mod.go") + sourceContent := []byte(`package mod`) + if err := afero.WriteFile(fs, sourcePath, sourceContent, 0666); err != nil { + return errors.E(op, err) + } + return nil +} + +func (gg *goget) Info(ctx context.Context, mod string, ver string) (*storage.RevInfo, error) { + const op errors.Op = "goget.Info" + fetcher, _ := module.NewGoGetFetcher(gg.goBinPath, gg.fs) // TODO: remove err from func call + ref, err := fetcher.Fetch(mod, ver) + if err != nil { + return nil, errors.E(op, err) + } + defer ref.Clear() + v, err := ref.Read() + if err != nil { + return nil, errors.E(op, err) + } + defer v.Zip.Close() + + var ri storage.RevInfo + err = json.Unmarshal(v.Info, &ri) + if err != nil { + return nil, errors.E(op, err) + } + + return &ri, nil +} + +func (gg *goget) Latest(ctx context.Context, mod string) (*storage.RevInfo, error) { + const op errors.Op = "goget.Latest" + lr, err := gg.list(op, mod) + if err != nil { + return nil, err + } + + pseudoInfo := strings.Split(lr.Version, "-") + if len(pseudoInfo) < 3 { + return nil, errors.E(op, fmt.Errorf("malformed pseudoInfo %v", lr.Version)) + } + return &storage.RevInfo{ + Name: pseudoInfo[2], + Short: pseudoInfo[2], + Time: lr.Time, + Version: lr.Version, + }, nil +} + +func (gg *goget) list(op errors.Op, mod string) (*listResp, error) { + hackyPath, err := afero.TempDir(gg.fs, "", "hackymod") + if err != nil { + return nil, errors.E(op, err) + } + defer gg.fs.RemoveAll(hackyPath) + err = dummyMod(gg.fs, hackyPath) + cmd := exec.Command( + gg.goBinPath, + "list", "-m", "-versions", "-json", + config.FmtModVer(mod, "latest"), + ) + cmd.Dir = hackyPath + + bts, err := cmd.CombinedOutput() + if err != nil { + errFmt := fmt.Errorf("%v: %s", err, bts) + return nil, errors.E(op, errFmt) + } + + var lr listResp + err = json.Unmarshal(bts, &lr) + if err != nil { + return nil, errors.E(op, err) + } + + return &lr, nil +} + +func (gg *goget) GoMod(ctx context.Context, mod string, ver string) ([]byte, error) { + const op errors.Op = "goget.Info" + fetcher, _ := module.NewGoGetFetcher(gg.goBinPath, gg.fs) // TODO: remove err from func call + ref, err := fetcher.Fetch(mod, ver) + if err != nil { + return nil, errors.E(op, err) + } + defer ref.Clear() + v, err := ref.Read() + if err != nil { + return nil, errors.E(op, err) + } + defer v.Zip.Close() + + return v.Mod, nil +} + +func (gg *goget) Zip(ctx context.Context, mod string, ver string) (io.ReadCloser, error) { + const op errors.Op = "goget.Info" + fetcher, _ := module.NewGoGetFetcher(gg.goBinPath, gg.fs) // TODO: remove err from func call + ref, err := fetcher.Fetch(mod, ver) + if err != nil { + return nil, errors.E(op, err) + } + v, err := ref.Read() + if err != nil { + return nil, errors.E(op, err) + } + + return v.Zip, nil +} diff --git a/pkg/download/list.go b/pkg/download/list.go index 355e6b427..328fc9bbe 100644 --- a/pkg/download/list.go +++ b/pkg/download/list.go @@ -7,29 +7,31 @@ import ( "github.com/bketelsen/buffet" "github.com/gobuffalo/buffalo" "github.com/gobuffalo/buffalo/render" + "github.com/gomods/athens/pkg/errors" + "github.com/gomods/athens/pkg/log" "github.com/gomods/athens/pkg/paths" - "github.com/gomods/athens/pkg/storage" - errs "github.com/pkg/errors" ) // PathList URL. const PathList = "/{module:.+}/@v/list" // ListHandler implements GET baseURL/module/@v/list -func ListHandler(lister storage.Lister, eng *render.Engine) func(c buffalo.Context) error { +func ListHandler(dp Protocol, lggr *log.Logger, eng *render.Engine) func(c buffalo.Context) error { return func(c buffalo.Context) error { sp := buffet.SpanFromContext(c) sp.SetOperationName("listHandler") mod, err := paths.GetModule(c) if err != nil { - return err + lggr.SystemErr(err) + return c.Render(500, nil) } - versions, err := lister.List(c, mod) - if storage.IsNotFoundError(err) { - return c.Render(http.StatusNotFound, eng.JSON(err.Error())) - } else if err != nil { - return errs.WithStack(err) + + versions, err := dp.List(c, mod) + if err != nil { + lggr.SystemErr(err) + return c.Render(errors.Kind(err), eng.JSON(errors.KindText(err))) } + return c.Render(http.StatusOK, eng.String(strings.Join(versions, "\n"))) } } From bbb569ba3cd4015e7b7953d53d85bd380cc99623 Mon Sep 17 00:00:00 2001 From: marwan-at-work Date: Thu, 26 Jul 2018 18:15:33 -0400 Subject: [PATCH 2/6] download: include goget tests + hacky hack --- pkg/download/goget/goget.go | 12 ++++++++--- pkg/download/goget/goget_test.go | 37 ++++++++++++++++++++++++++++++++ 2 files changed, 46 insertions(+), 3 deletions(-) create mode 100644 pkg/download/goget/goget_test.go diff --git a/pkg/download/goget/goget.go b/pkg/download/goget/goget.go index 1dd202a53..fc6a76710 100644 --- a/pkg/download/goget/goget.go +++ b/pkg/download/goget/goget.go @@ -1,6 +1,7 @@ package goget import ( + "bytes" "context" "encoding/json" "fmt" @@ -10,12 +11,11 @@ import ( "strings" "time" - "github.com/gomods/athens/pkg/module" - "github.com/gomods/athens/pkg/config" "github.com/gomods/athens/pkg/config/env" "github.com/gomods/athens/pkg/download" "github.com/gomods/athens/pkg/errors" + "github.com/gomods/athens/pkg/module" "github.com/gomods/athens/pkg/storage" "github.com/spf13/afero" ) @@ -48,7 +48,7 @@ func (gg *goget) List(ctx context.Context, mod string) ([]string, error) { type listResp struct { Path string Version string - Versions []string + Versions []string `json:"omitempty"` Time time.Time } @@ -131,6 +131,12 @@ func (gg *goget) list(op errors.Op, mod string) (*listResp, error) { return nil, errors.E(op, errFmt) } + // ugly hack until go cli implements -quiet flag. + // https://github.com/golang/go/issues/26628 + if bytes.HasPrefix(bts, []byte("go: finding")) { + bts = bts[bytes.Index(bts, []byte{'\n'}):] + } + var lr listResp err = json.Unmarshal(bts, &lr) if err != nil { diff --git a/pkg/download/goget/goget_test.go b/pkg/download/goget/goget_test.go new file mode 100644 index 000000000..6b61b84ea --- /dev/null +++ b/pkg/download/goget/goget_test.go @@ -0,0 +1,37 @@ +package goget + +import ( + "context" + "testing" +) + +type testCase struct { + name string + mod string + version string +} + +// TODO(marwan): we should create Test Repos under github.com/gomods +// so we can get reproducible results from live VCS repos. +// For now, I cannot test that github.com/pkg/errors returns v0.8.0 +// from goget.Latest, because they could very well introduce a new tag +// in the near future. +var tt = []testCase{ + {"basic list", "github.com/pkg/errors", "latest"}, + {"list non tagged", "github.com/marwan-at-work/gowatch", "latest"}, + {"list vanity", "golang.org/x/tools", "latest"}, +} + +func TestList(t *testing.T) { + dp := New() + ctx := context.Background() + + for _, tc := range tt { + t.Run(tc.name, func(t *testing.T) { + _, err := dp.List(ctx, tc.mod) // TODO ensure list is correct per TODO above. + if err != nil { + t.Fatal(err) + } + }) + } +} From a6296448d9131e869218d076cd74f85bdb447875 Mon Sep 17 00:00:00 2001 From: marwan-at-work Date: Fri, 27 Jul 2018 01:55:25 -0400 Subject: [PATCH 3/6] download: move dummyMod to pkg/module --- pkg/download/download.go | 3 ++- pkg/download/goget/goget.go | 20 +------------------- pkg/module/go_get_fetcher.go | 15 +++++++++------ 3 files changed, 12 insertions(+), 26 deletions(-) diff --git a/pkg/download/download.go b/pkg/download/download.go index d5a7eb320..1efbc7923 100644 --- a/pkg/download/download.go +++ b/pkg/download/download.go @@ -7,7 +7,8 @@ import ( "github.com/gomods/athens/pkg/storage" ) -// Protocol is the download protocol defined by cmd/go +// Protocol is the download protocol which mirrors +// the http requests that cmd/go makes to the proxy. type Protocol interface { List(ctx context.Context, module string) ([]string, error) Info(ctx context.Context, module, version string) (*storage.RevInfo, error) diff --git a/pkg/download/goget/goget.go b/pkg/download/goget/goget.go index fc6a76710..5c5042cde 100644 --- a/pkg/download/goget/goget.go +++ b/pkg/download/goget/goget.go @@ -7,7 +7,6 @@ import ( "fmt" "io" "os/exec" - "path/filepath" "strings" "time" @@ -52,23 +51,6 @@ type listResp struct { Time time.Time } -// dummyMod creates a random go module so that listing -// a depdencny's upstream versions can run from the go cli. -func dummyMod(fs afero.Fs, repoRoot string) error { - const op errors.Op = "goget.dummyMod" - gomodPath := filepath.Join(repoRoot, "go.mod") - gomodContent := []byte("module mod") - if err := afero.WriteFile(fs, gomodPath, gomodContent, 0666); err != nil { - return errors.E(op, err) - } - sourcePath := filepath.Join(repoRoot, "mod.go") - sourceContent := []byte(`package mod`) - if err := afero.WriteFile(fs, sourcePath, sourceContent, 0666); err != nil { - return errors.E(op, err) - } - return nil -} - func (gg *goget) Info(ctx context.Context, mod string, ver string) (*storage.RevInfo, error) { const op errors.Op = "goget.Info" fetcher, _ := module.NewGoGetFetcher(gg.goBinPath, gg.fs) // TODO: remove err from func call @@ -117,7 +99,7 @@ func (gg *goget) list(op errors.Op, mod string) (*listResp, error) { return nil, errors.E(op, err) } defer gg.fs.RemoveAll(hackyPath) - err = dummyMod(gg.fs, hackyPath) + err = module.Dummy(gg.fs, hackyPath) cmd := exec.Command( gg.goBinPath, "list", "-m", "-versions", "-json", diff --git a/pkg/module/go_get_fetcher.go b/pkg/module/go_get_fetcher.go index 5c41294b8..c96ade358 100644 --- a/pkg/module/go_get_fetcher.go +++ b/pkg/module/go_get_fetcher.go @@ -49,7 +49,7 @@ func (g *goGetFetcher) Fetch(mod, ver string) (Ref, error) { } // setup the module with barebones stuff - if err := prepareStructure(g.fs, modPath); err != nil { + if err := Dummy(g.fs, modPath); err != nil { // TODO: return a ref for cleaning up the goPathRoot // https://github.com/gomods/athens/issues/329 ref.Clear() @@ -68,18 +68,21 @@ func (g *goGetFetcher) Fetch(mod, ver string) (Ref, error) { return newDiskRef(g.fs, cachePath, ver), err } -// Hacky thing makes vgo not to complain -func prepareStructure(fs afero.Fs, repoRoot string) error { +// Dummy Hacky thing makes vgo not to complain +func Dummy(fs afero.Fs, repoRoot string) error { + const op errors.Op = "module.Dummy" // vgo expects go.mod file present with module statement or .go file with import comment gomodPath := filepath.Join(repoRoot, "go.mod") gomodContent := []byte("module mod") if err := afero.WriteFile(fs, gomodPath, gomodContent, 0666); err != nil { - return err + return errors.E(op, err) } - sourcePath := filepath.Join(repoRoot, "mod.go") sourceContent := []byte("package mod") - return afero.WriteFile(fs, sourcePath, sourceContent, 0666) + if err := afero.WriteFile(fs, sourcePath, sourceContent, 0666); err != nil { + return errors.E(op, err) + } + return nil } // given a filesystem, gopath, repository root, module and version, runs 'vgo get' From 82f60c9c5652a4d93c205df2419484347c277962 Mon Sep 17 00:00:00 2001 From: marwan-at-work Date: Fri, 27 Jul 2018 10:03:02 -0400 Subject: [PATCH 4/6] Olympus: pass dp and lggr to /list --- cmd/olympus/actions/app.go | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/cmd/olympus/actions/app.go b/cmd/olympus/actions/app.go index 988cf8a80..c50fdb549 100644 --- a/cmd/olympus/actions/app.go +++ b/cmd/olympus/actions/app.go @@ -13,6 +13,7 @@ import ( "github.com/gomods/athens/pkg/cdn/metadata/azurecdn" "github.com/gomods/athens/pkg/config/env" "github.com/gomods/athens/pkg/download" + "github.com/gomods/athens/pkg/download/goget" "github.com/gomods/athens/pkg/eventlog" "github.com/gomods/athens/pkg/log" "github.com/gomods/athens/pkg/storage" @@ -114,8 +115,9 @@ func App(config *AppConfig) (*buffalo.App, error) { app.POST("/cachemiss", cachemissHandler(w)) app.POST("/push", pushNotificationHandler(w)) + dp := goget.New() // Download Protocol - app.GET(download.PathList, download.ListHandler(config.Storage, renderEng)) + app.GET(download.PathList, download.ListHandler(dp, lggr, renderEng)) app.GET(download.PathVersionInfo, download.VersionInfoHandler(config.Storage, renderEng)) app.GET(download.PathVersionModule, download.VersionModuleHandler(config.Storage, renderEng)) app.GET(download.PathVersionZip, download.VersionZipHandler(config.Storage, renderEng, lggr)) From 79a5d67eb0b654994ed86fceb46154e81e9315fa Mon Sep 17 00:00:00 2001 From: marwan-at-work Date: Fri, 27 Jul 2018 14:13:57 -0400 Subject: [PATCH 5/6] download: add Version to interface --- cmd/proxy/actions/app_proxy.go | 2 +- pkg/download/download.go | 89 ++++++++++++++++++++++++++++++++-- pkg/download/goget/goget.go | 48 ++++++++---------- pkg/errors/kinds.go | 6 +++ 4 files changed, 111 insertions(+), 34 deletions(-) create mode 100644 pkg/errors/kinds.go diff --git a/cmd/proxy/actions/app_proxy.go b/cmd/proxy/actions/app_proxy.go index 6d483e67c..a0deb7f71 100644 --- a/cmd/proxy/actions/app_proxy.go +++ b/cmd/proxy/actions/app_proxy.go @@ -17,7 +17,7 @@ func addProxyRoutes( ) error { app.GET("/", proxyHomeHandler) - dp := goget.New() + dp := download.New(goget.New(), storage) // Download Protocol app.GET(download.PathList, download.ListHandler(dp, lggr, proxy)) app.GET(download.PathVersionInfo, cacheMissHandler(download.VersionInfoHandler(storage, proxy), app.Worker, mf, lggr)) diff --git a/pkg/download/download.go b/pkg/download/download.go index 1efbc7923..97790838b 100644 --- a/pkg/download/download.go +++ b/pkg/download/download.go @@ -4,15 +4,94 @@ import ( "context" "io" + "github.com/gomods/athens/pkg/errors" "github.com/gomods/athens/pkg/storage" ) // Protocol is the download protocol which mirrors // the http requests that cmd/go makes to the proxy. type Protocol interface { - List(ctx context.Context, module string) ([]string, error) - Info(ctx context.Context, module, version string) (*storage.RevInfo, error) - Latest(ctx context.Context, module string) (*storage.RevInfo, error) - GoMod(ctx context.Context, module, version string) ([]byte, error) - Zip(ctx context.Context, module, version string) (io.ReadCloser, error) + List(ctx context.Context, mod string) ([]string, error) + Info(ctx context.Context, mod, ver string) ([]byte, error) + Latest(ctx context.Context, mod string) (*storage.RevInfo, error) + GoMod(ctx context.Context, mod, ver string) ([]byte, error) + Zip(ctx context.Context, mod, ver string) (io.ReadCloser, error) + Version(ctx context.Context, mod, ver string) (*storage.Version, error) +} + +type protocol struct { + s storage.Backend + dp Protocol +} + +// New takes an upstream Protocol and storage +// it always prefers storage, otherwise it goes to upstream +// and fills the storage with the results. +func New(dp Protocol, s storage.Backend) Protocol { + return &protocol{dp: dp, s: s} +} + +func (p *protocol) List(ctx context.Context, mod string) ([]string, error) { + return p.dp.List(ctx, mod) +} + +func (p *protocol) Info(ctx context.Context, mod, ver string) ([]byte, error) { + const op errors.Op = "protocol.Info" + v, err := p.s.Get(mod, ver) + if errors.ErrNotFound(err) { + v, err = p.fillCache(ctx, mod, ver) + } + if err != nil { + return nil, errors.E(op, err) + } + + return v.Info, nil +} + +func (p *protocol) fillCache(ctx context.Context, mod, ver string) (*storage.Version, error) { + const op errors.Op = "protocol.fillCache" + v, err := p.dp.Version(ctx, mod, ver) + if err != nil { + return nil, errors.E(op, err) + } + err = p.s.Save(ctx, mod, ver, v.Mod, v.Zip, v.Info) + if err != nil { + return nil, errors.E(op, err) + } + + return v, nil +} + +func (p *protocol) Latest(ctx context.Context, mod string) (*storage.RevInfo, error) { + return p.dp.Latest(ctx, mod) +} + +func (p *protocol) GoMod(ctx context.Context, mod, ver string) ([]byte, error) { + const op errors.Op = "protocol.GoMod" + v, err := p.s.Get(mod, ver) + if errors.ErrNotFound(err) { + v, err = p.fillCache(ctx, mod, ver) + } + if err != nil { + return nil, errors.E(op, err) + } + + return v.Mod, nil +} + +func (p *protocol) Zip(ctx context.Context, mod, ver string) (io.ReadCloser, error) { + const op errors.Op = "protocol.Zip" + v, err := p.s.Get(mod, ver) + if errors.ErrNotFound(err) { + v, err = p.fillCache(ctx, mod, ver) + } + if err != nil { + return nil, errors.E(op, err) + } + + return v.Zip, nil +} + +func (p *protocol) Version(ctx context.Context, mod, ver string) (*storage.Version, error) { + return p.dp.Version(ctx, mod, ver) } diff --git a/pkg/download/goget/goget.go b/pkg/download/goget/goget.go index 5c5042cde..c5285e378 100644 --- a/pkg/download/goget/goget.go +++ b/pkg/download/goget/goget.go @@ -51,27 +51,15 @@ type listResp struct { Time time.Time } -func (gg *goget) Info(ctx context.Context, mod string, ver string) (*storage.RevInfo, error) { +func (gg *goget) Info(ctx context.Context, mod string, ver string) ([]byte, error) { const op errors.Op = "goget.Info" - fetcher, _ := module.NewGoGetFetcher(gg.goBinPath, gg.fs) // TODO: remove err from func call - ref, err := fetcher.Fetch(mod, ver) + v, err := gg.Version(ctx, mod, ver) if err != nil { - return nil, errors.E(op, err) - } - defer ref.Clear() - v, err := ref.Read() - if err != nil { - return nil, errors.E(op, err) + return nil, errors.E(op) } - defer v.Zip.Close() + v.Zip.Close() - var ri storage.RevInfo - err = json.Unmarshal(v.Info, &ri) - if err != nil { - return nil, errors.E(op, err) - } - - return &ri, nil + return v.Info, nil } func (gg *goget) Latest(ctx context.Context, mod string) (*storage.RevInfo, error) { @@ -130,23 +118,27 @@ func (gg *goget) list(op errors.Op, mod string) (*listResp, error) { func (gg *goget) GoMod(ctx context.Context, mod string, ver string) ([]byte, error) { const op errors.Op = "goget.Info" - fetcher, _ := module.NewGoGetFetcher(gg.goBinPath, gg.fs) // TODO: remove err from func call - ref, err := fetcher.Fetch(mod, ver) + v, err := gg.Version(ctx, mod, ver) if err != nil { - return nil, errors.E(op, err) - } - defer ref.Clear() - v, err := ref.Read() - if err != nil { - return nil, errors.E(op, err) + return nil, errors.E(op) } - defer v.Zip.Close() + v.Zip.Close() return v.Mod, nil } -func (gg *goget) Zip(ctx context.Context, mod string, ver string) (io.ReadCloser, error) { +func (gg *goget) Zip(ctx context.Context, mod, ver string) (io.ReadCloser, error) { const op errors.Op = "goget.Info" + v, err := gg.Version(ctx, mod, ver) + if err != nil { + return nil, errors.E(op) + } + + return v.Zip, nil +} + +func (gg *goget) Version(ctx context.Context, mod, ver string) (*storage.Version, error) { + const op errors.Op = "goget.Version" fetcher, _ := module.NewGoGetFetcher(gg.goBinPath, gg.fs) // TODO: remove err from func call ref, err := fetcher.Fetch(mod, ver) if err != nil { @@ -157,5 +149,5 @@ func (gg *goget) Zip(ctx context.Context, mod string, ver string) (io.ReadCloser return nil, errors.E(op, err) } - return v.Zip, nil + return v, nil } diff --git a/pkg/errors/kinds.go b/pkg/errors/kinds.go new file mode 100644 index 000000000..83bdee1db --- /dev/null +++ b/pkg/errors/kinds.go @@ -0,0 +1,6 @@ +package errors + +// ErrNotFound helper function for KindNotFound +func ErrNotFound(err error) bool { + return Kind(err) == KindNotFound +} From 4379bcd17c646701fcac3b0d92760fbd6deae3a4 Mon Sep 17 00:00:00 2001 From: marwan-at-work Date: Fri, 27 Jul 2018 14:17:23 -0400 Subject: [PATCH 6/6] download: document Protocol --- pkg/download/download.go | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/pkg/download/download.go b/pkg/download/download.go index 97790838b..c17629c8a 100644 --- a/pkg/download/download.go +++ b/pkg/download/download.go @@ -11,11 +11,22 @@ import ( // Protocol is the download protocol which mirrors // the http requests that cmd/go makes to the proxy. type Protocol interface { + // List implements GET /{module}/@v/list List(ctx context.Context, mod string) ([]string, error) + + // Info implements GET /{module}/@v/{version}.info Info(ctx context.Context, mod, ver string) ([]byte, error) + + // Latest implements GET /{module}/@latest Latest(ctx context.Context, mod string) (*storage.RevInfo, error) + + // GoMod implements GET /{module}/@v/{version}.mod GoMod(ctx context.Context, mod, ver string) ([]byte, error) + + // Zip implements GET /{module}/@v/{version}.zip Zip(ctx context.Context, mod, ver string) (io.ReadCloser, error) + + // Version is a helper method to get Info, GoMod, and Zip together. Version(ctx context.Context, mod, ver string) (*storage.Version, error) }