mirror of
https://github.com/gomods/athens
synced 2026-02-03 12:10:32 +00:00
Fix list (#607)
* merge list from storage with the one from go list * fix and rename * move stuff to semver pkg * fix gofmt * move union out of semver pkg * add tests * fix err msg * fix tests * error handling * error handling, go list refactoring * fix list * cleanup * fix gofmt
This commit is contained in:
committed by
Aaron Schlesinger
parent
d745dfbae4
commit
6af44242fb
+49
-77
@@ -1,21 +1,13 @@
|
||||
package download
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io"
|
||||
"os/exec"
|
||||
"time"
|
||||
|
||||
"github.com/gomods/athens/pkg/config"
|
||||
"github.com/gomods/athens/pkg/errors"
|
||||
"github.com/gomods/athens/pkg/module"
|
||||
"github.com/gomods/athens/pkg/observ"
|
||||
"github.com/gomods/athens/pkg/stash"
|
||||
"github.com/gomods/athens/pkg/storage"
|
||||
"github.com/spf13/afero"
|
||||
)
|
||||
|
||||
// Protocol is the download protocol which mirrors
|
||||
@@ -42,10 +34,9 @@ type Wrapper func(Protocol) Protocol
|
||||
|
||||
// Opts specifies download protocol options to avoid long func signature.
|
||||
type Opts struct {
|
||||
Storage storage.Backend
|
||||
Stasher stash.Stasher
|
||||
GoBinPath string
|
||||
Fs afero.Fs
|
||||
Storage storage.Backend
|
||||
Stasher stash.Stasher
|
||||
Lister UpstreamLister
|
||||
}
|
||||
|
||||
// New returns a full implementation of the download.Protocol
|
||||
@@ -54,7 +45,7 @@ type Opts struct {
|
||||
// The wrappers are applied in order, meaning the last wrapper
|
||||
// passed is the Protocol that gets hit first.
|
||||
func New(opts *Opts, wrappers ...Wrapper) Protocol {
|
||||
var p Protocol = &protocol{opts.Storage, opts.Stasher, opts.GoBinPath, opts.Fs}
|
||||
var p Protocol = &protocol{opts.Storage, opts.Stasher, opts.Lister}
|
||||
for _, w := range wrappers {
|
||||
p = w(p)
|
||||
}
|
||||
@@ -63,88 +54,49 @@ func New(opts *Opts, wrappers ...Wrapper) Protocol {
|
||||
}
|
||||
|
||||
type protocol struct {
|
||||
s storage.Backend
|
||||
stasher stash.Stasher
|
||||
goBinPath string
|
||||
fs afero.Fs
|
||||
s storage.Backend
|
||||
stasher stash.Stasher
|
||||
lister UpstreamLister
|
||||
}
|
||||
|
||||
func (p *protocol) List(ctx context.Context, mod string) ([]string, error) {
|
||||
const op errors.Op = "protocol.List"
|
||||
ctx, span := observ.StartSpan(ctx, op.String())
|
||||
defer span.End()
|
||||
lr, err := p.list(op, mod)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
|
||||
strList, sErr := p.s.List(ctx, mod)
|
||||
// if we got an unexpected storage err then we can not guarantee that the end result contains all versions
|
||||
// a tag or repo could have been deleted
|
||||
if sErr != nil {
|
||||
return nil, errors.E(op, sErr)
|
||||
}
|
||||
_, goList, goErr := p.lister.List(mod)
|
||||
isUnexpGoErr := goErr != nil && !errors.IsRepoNotFoundErr(goErr)
|
||||
// if i.e. github is unavailable we should fail as well so that the behavior of the proxy is stable.
|
||||
// otherwise we will get different results the next time because i.e. GH is up again
|
||||
if isUnexpGoErr {
|
||||
return nil, errors.E(op, goErr)
|
||||
}
|
||||
|
||||
return lr.Versions, nil
|
||||
isRepoNotFoundErr := goErr != nil && errors.IsRepoNotFoundErr(goErr)
|
||||
storageEmpty := len(strList) == 0
|
||||
if isRepoNotFoundErr && storageEmpty {
|
||||
return nil, errors.E(op, errors.M(mod), errors.KindNotFound, goErr)
|
||||
}
|
||||
|
||||
return union(goList, strList), nil
|
||||
}
|
||||
|
||||
func (p *protocol) Latest(ctx context.Context, mod string) (*storage.RevInfo, error) {
|
||||
const op errors.Op = "protocol.Latest"
|
||||
ctx, span := observ.StartSpan(ctx, op.String())
|
||||
defer span.End()
|
||||
lr, err := p.list(op, mod)
|
||||
lr, _, err := p.lister.List(mod)
|
||||
if err != nil {
|
||||
return nil, errors.E(op, err)
|
||||
}
|
||||
|
||||
return &storage.RevInfo{
|
||||
Time: lr.Time,
|
||||
Version: lr.Version,
|
||||
}, nil
|
||||
}
|
||||
|
||||
type listResp struct {
|
||||
Path string
|
||||
Version string
|
||||
Versions []string `json:",omitempty"`
|
||||
Time time.Time
|
||||
}
|
||||
|
||||
func (p *protocol) list(op errors.Op, mod string) (*listResp, error) {
|
||||
hackyPath, err := afero.TempDir(p.fs, "", "hackymod")
|
||||
if err != nil {
|
||||
return nil, errors.E(op, err)
|
||||
}
|
||||
defer p.fs.RemoveAll(hackyPath)
|
||||
err = module.Dummy(p.fs, hackyPath)
|
||||
if err != nil {
|
||||
return nil, errors.E(op, err)
|
||||
}
|
||||
|
||||
cmd := exec.Command(
|
||||
p.goBinPath,
|
||||
"list", "-m", "-versions", "-json",
|
||||
config.FmtModVer(mod, "latest"),
|
||||
)
|
||||
cmd.Dir = hackyPath
|
||||
stdout := &bytes.Buffer{}
|
||||
stderr := &bytes.Buffer{}
|
||||
cmd.Stdout = stdout
|
||||
cmd.Stderr = stderr
|
||||
|
||||
gopath, err := afero.TempDir(p.fs, "", "athens")
|
||||
if err != nil {
|
||||
return nil, errors.E(op, err)
|
||||
}
|
||||
defer module.ClearFiles(p.fs, gopath)
|
||||
cmd.Env = module.PrepareEnv(gopath)
|
||||
|
||||
err = cmd.Run()
|
||||
if err != nil {
|
||||
err = fmt.Errorf("%v: %s", err, stderr)
|
||||
return nil, errors.E(op, err)
|
||||
}
|
||||
|
||||
var lr listResp
|
||||
err = json.NewDecoder(stdout).Decode(&lr)
|
||||
if err != nil {
|
||||
return nil, errors.E(op, err)
|
||||
}
|
||||
|
||||
return &lr, nil
|
||||
return lr, nil
|
||||
}
|
||||
|
||||
func (p *protocol) Info(ctx context.Context, mod, ver string) ([]byte, error) {
|
||||
@@ -203,3 +155,23 @@ func (p *protocol) Zip(ctx context.Context, mod, ver string) (io.ReadCloser, err
|
||||
|
||||
return zip, nil
|
||||
}
|
||||
|
||||
// union concatenates two version lists and removes duplicates
|
||||
func union(list1, list2 []string) []string {
|
||||
if list1 == nil {
|
||||
list1 = []string{}
|
||||
}
|
||||
if list2 == nil {
|
||||
list2 = []string{}
|
||||
}
|
||||
list := append(list1, list2...)
|
||||
unique := []string{}
|
||||
m := make(map[string]struct{})
|
||||
for _, v := range list {
|
||||
if _, ok := m[v]; !ok {
|
||||
unique = append(unique, v)
|
||||
m[v] = struct{}{}
|
||||
}
|
||||
}
|
||||
return unique
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user