diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 3593c3b0..766492f0 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -3,6 +3,18 @@ on: - push - pull_request jobs: + lint: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v3 + - name: Set up Go + uses: actions/setup-go@v3 + with: + go-version-file: 'go.mod' + cache: true + - name: Lint code + run: make lint-docker + build: env: ATHENS_MONGO_STORAGE_URL: mongodb://localhost:27017 @@ -53,7 +65,7 @@ jobs: with: go-version-file: 'go.mod' cache: true - - name: Run linter + - name: Verify changes run: make verify - name: Unit tests run: go test -v -race ./... diff --git a/.golangci.yml b/.golangci.yml new file mode 100644 index 00000000..d757d9db --- /dev/null +++ b/.golangci.yml @@ -0,0 +1,80 @@ +run: + tests: false + timeout: 5m + +linters-settings: + cyclop: + max-complexity: 12 + skip-tests: true + gofumpt: + extra-rules: true + +linters: + enable-all: true + disable: + - interfacer # deprecated + - scopelint # deprecated + - maligned # deprecated + - golint # deprecated + - structcheck # deprecated + - deadcode # deprecated + - varcheck # deprecated + - nosnakecase # deprecated + - ifshort # deprecated + - errchkjson + - exhaustive + - exhaustivestruct + - exhaustruct + - forcetypeassert + - funlen + - gochecknoglobals + - gochecknoinits + - goconst + - godox + - goerr113 + - gomnd + - ireturn + - lll + - musttag + - nilnil + - nlreturn + - nonamedreturns + - tagliatelle + - varnamelen + - wrapcheck + - wsl + - cyclop # TODO: turn this back on later + - gocognit # TODO: turn this back on later + - forbidigo # TODO: turn this back on later + +issues: + exclude-use-default: false + exclude: + - 'package-comments: should have a package comment' + - 'ST1000: at least one file in a package should have a package comment' + - 'G204: Subprocess launched with a potential tainted input or cmd arguments' + - 'G204: Subprocess launched with variable' + - 'G402: TLS MinVersion too low.' + - 'const `op` is unused' + exclude-rules: + - path: cmd/proxy/main.go + text: 'G108: Profiling endpoint is automatically exposed on /debug/pprof' + - path: pkg/stash/stasher.go + linters: + - contextcheck + - path: pkg/stash/with_azureblob.go # False positive + linters: + - bodyclose + - path: pkg/storage/azureblob/azureblob.go # False positive + linters: + - bodyclose + - path: pkg/storage/compliance/* + linters: + - thelper + - gosec + - errcheck + - path: pkg/index/compliance/* + linters: + - thelper + - gosec + - errcheck diff --git a/DEVELOPMENT.md b/DEVELOPMENT.md index 88ea1e2e..5585724e 100644 --- a/DEVELOPMENT.md +++ b/DEVELOPMENT.md @@ -234,10 +234,10 @@ Then open [http://localhost:1313](http://localhost:1313/). # Linting -In our CI/CD pass, we use govet, so feel free to run it locally beforehand: +In our CI/CD pass, we use golangci-lint, so feel free to run it locally beforehand: ``` -go vet ./... +make lint ``` # For People Doing a Release diff --git a/Makefile b/Makefile index 95f726cf..f99c2162 100644 --- a/Makefile +++ b/Makefile @@ -1,6 +1,8 @@ VERSION = "unset" DATE=$(shell date -u +%Y-%m-%d-%H:%M:%S-%Z) +GOLANGCI_LINT_VERSION=v1.51.2 + ifndef GOLANG_VERSION override GOLANG_VERSION = 1.19 endif @@ -43,10 +45,16 @@ docs: ## build the docs docker image setup-dev-env: $(MAKE) dev +.PHONY: lint +lint: + @golangci-lint run ./... + +.PHONY: lint-docker +lint-docker: + @docker run -t --rm -v $(CURDIR):/app -w /app golangci/golangci-lint:$(GOLANGCI_LINT_VERSION) golangci-lint run ./... + .PHONY: verify verify: ## verify athens codebase - ./scripts/check_gofmt.sh - ./scripts/check_govet.sh ./scripts/check_deps.sh ./scripts/check_conflicts.sh diff --git a/cmd/proxy/actions/app.go b/cmd/proxy/actions/app.go index 4b3d7c3f..e3702246 100644 --- a/cmd/proxy/actions/app.go +++ b/cmd/proxy/actions/app.go @@ -16,7 +16,7 @@ import ( "go.opencensus.io/plugin/ochttp" ) -// Service is the name of the service that we want to tag our processes with +// Service is the name of the service that we want to tag our processes with. const Service = "proxy" // App is where all routes and middleware for the proxy @@ -126,7 +126,7 @@ func App(conf *config.Config) (http.Handler, error) { store, err := GetStorage(conf.StorageType, conf.Storage, conf.TimeoutDuration(), client) if err != nil { - err = fmt.Errorf("error getting storage configuration (%s)", err) + err = fmt.Errorf("error getting storage configuration: %w", err) return nil, err } @@ -140,7 +140,7 @@ func App(conf *config.Config) (http.Handler, error) { lggr, conf, ); err != nil { - err = fmt.Errorf("error adding proxy routes (%s)", err) + err = fmt.Errorf("error adding proxy routes: %w", err) return nil, err } diff --git a/cmd/proxy/actions/app_proxy.go b/cmd/proxy/actions/app_proxy.go index e0f1f6bc..27187f68 100644 --- a/cmd/proxy/actions/app_proxy.go +++ b/cmd/proxy/actions/app_proxy.go @@ -2,6 +2,7 @@ package actions import ( "context" + "errors" "fmt" "net/http" "net/url" @@ -54,7 +55,7 @@ func addProxyRoutes( } supportPath := path.Join("/sumdb", sumdbURL.Host, "/supported") r.HandleFunc(supportPath, func(w http.ResponseWriter, r *http.Request) { - w.WriteHeader(200) + w.WriteHeader(http.StatusOK) }) sumHandler := sumdbProxy(sumdbURL, c.NoSumPatterns) pathPrefix := "/sumdb/" + sumdbURL.Host @@ -63,7 +64,7 @@ func addProxyRoutes( ) } - // Download Protocol + // Download Protocol: // the download.Protocol and the stash.Stasher interfaces are composable // in a middleware fashion. Therefore you can separate concerns // by the functionality: a download.Protocol that just takes care @@ -142,13 +143,13 @@ func getSingleFlight(l *log.Logger, c *config.Config, checker storage.Checker) ( return stash.WithSingleflight, nil case "etcd": if c.SingleFlight == nil || c.SingleFlight.Etcd == nil { - return nil, fmt.Errorf("Etcd config must be present") + return nil, errors.New("etcd config must be present") } endpoints := strings.Split(c.SingleFlight.Etcd.Endpoints, ",") return stash.WithEtcd(endpoints, checker) case "redis": if c.SingleFlight == nil || c.SingleFlight.Redis == nil { - return nil, fmt.Errorf("Redis config must be present") + return nil, errors.New("redis config must be present") } return stash.WithRedisLock( &athensLoggerForRedis{logger: l}, @@ -158,7 +159,7 @@ func getSingleFlight(l *log.Logger, c *config.Config, checker storage.Checker) ( c.SingleFlight.Redis.LockConfig) case "redis-sentinel": if c.SingleFlight == nil || c.SingleFlight.RedisSentinel == nil { - return nil, fmt.Errorf("Redis config must be present") + return nil, errors.New("redis config must be present") } return stash.WithRedisSentinelLock( &athensLoggerForRedis{logger: l}, diff --git a/cmd/proxy/actions/auth.go b/cmd/proxy/actions/auth.go index e654da87..c4daedc6 100644 --- a/cmd/proxy/actions/auth.go +++ b/cmd/proxy/actions/auth.go @@ -19,7 +19,7 @@ func initializeAuthFile(path string) { return } - fileBts, err := os.ReadFile(path) + fileBts, err := os.ReadFile(filepath.Clean(path)) if err != nil { log.Fatalf("could not read %s: %v", path, err) } @@ -31,7 +31,7 @@ func initializeAuthFile(path string) { fileName := transformAuthFileName(filepath.Base(path)) rcp := filepath.Join(hdir, fileName) - if err := os.WriteFile(rcp, fileBts, 0600); err != nil { + if err := os.WriteFile(rcp, fileBts, 0o600); err != nil { log.Fatalf("could not write to file: %v", err) } } @@ -45,7 +45,7 @@ func netrcFromToken(tok string) { log.Fatalf("netrcFromToken: could not get homedir: %v", err) } rcp := filepath.Join(hdir, getNETRCFilename()) - if err := os.WriteFile(rcp, []byte(fileContent), 0600); err != nil { + if err := os.WriteFile(rcp, []byte(fileContent), 0o600); err != nil { log.Fatalf("netrcFromToken: could not write to file: %v", err) } } diff --git a/cmd/proxy/actions/basicauth.go b/cmd/proxy/actions/basicauth.go index ea0938e0..a9a754b0 100644 --- a/cmd/proxy/actions/basicauth.go +++ b/cmd/proxy/actions/basicauth.go @@ -8,10 +8,8 @@ import ( "github.com/gorilla/mux" ) -var ( - // basicAuthExcludedPaths is a regular expression that matches paths that should not be protected by HTTP basic authentication. - basicAuthExcludedPaths = regexp.MustCompile("^/(health|ready)z$") -) +// basicAuthExcludedPaths is a regular expression that matches paths that should not be protected by HTTP basic authentication. +var basicAuthExcludedPaths = regexp.MustCompile("^/(health|ready)z$") func basicAuth(user, pass string) mux.MiddlewareFunc { return func(h http.Handler) http.Handler { @@ -40,9 +38,5 @@ func checkAuth(r *http.Request, user, pass string) bool { } isPass := subtle.ConstantTimeCompare([]byte(pass), []byte(givenPass)) - if isPass != 1 { - return false - } - - return true + return isPass == 1 } diff --git a/cmd/proxy/actions/catalog.go b/cmd/proxy/actions/catalog.go index eb98052b..769208f1 100644 --- a/cmd/proxy/actions/catalog.go +++ b/cmd/proxy/actions/catalog.go @@ -18,7 +18,7 @@ type catalogRes struct { NextPageToken string `json:"next,omitempty"` } -// catalogHandler implements GET baseURL/catalog +// catalogHandler implements GET baseURL/catalog. func catalogHandler(s storage.Backend) http.HandlerFunc { const op errors.Op = "actions.CatalogHandler" cs, isCataloger := s.(storage.Cataloger) @@ -54,7 +54,7 @@ func catalogHandler(s storage.Backend) http.HandlerFunc { } // getLimitFromParam converts a URL query parameter into an int -// otherwise converts defaultPageSize constant +// otherwise converts defaultPageSize constant. func getLimitFromParam(param string) (int, error) { if param == "" { return defaultPageSize, nil diff --git a/cmd/proxy/actions/home.go b/cmd/proxy/actions/home.go index f0f62f74..94fb545f 100644 --- a/cmd/proxy/actions/home.go +++ b/cmd/proxy/actions/home.go @@ -5,5 +5,5 @@ import ( ) func proxyHomeHandler(w http.ResponseWriter, r *http.Request) { - w.Write([]byte(`"Welcome to The Athens Proxy"`)) + _, _ = w.Write([]byte(`"Welcome to The Athens Proxy"`)) } diff --git a/cmd/proxy/actions/index.go b/cmd/proxy/actions/index.go index 19fce99e..e747a7ab 100644 --- a/cmd/proxy/actions/index.go +++ b/cmd/proxy/actions/index.go @@ -13,7 +13,7 @@ import ( "github.com/sirupsen/logrus" ) -// indexHandler implements GET baseURL/index +// indexHandler implements GET baseURL/index. func indexHandler(index index.Indexer) http.HandlerFunc { return func(w http.ResponseWriter, r *http.Request) { ctx := r.Context() diff --git a/cmd/proxy/actions/robots.go b/cmd/proxy/actions/robots.go index ce2387da..b7edb135 100644 --- a/cmd/proxy/actions/robots.go +++ b/cmd/proxy/actions/robots.go @@ -6,7 +6,7 @@ import ( "github.com/gomods/athens/pkg/config" ) -// robotsHandler implements GET baseURL/robots.txt +// robotsHandler implements GET baseURL/robots.txt. func robotsHandler(c *config.Config) http.HandlerFunc { return func(w http.ResponseWriter, r *http.Request) { http.ServeFile(w, r, c.RobotsFile) diff --git a/cmd/proxy/actions/storage.go b/cmd/proxy/actions/storage.go index 04722cc6..3d9faa3e 100644 --- a/cmd/proxy/actions/storage.go +++ b/cmd/proxy/actions/storage.go @@ -21,7 +21,7 @@ import ( "github.com/spf13/afero" ) -// GetStorage returns storage backend based on env configuration +// GetStorage returns storage backend based on env configuration. func GetStorage(storageType string, storageConfig *config.Storage, timeout time.Duration, client *http.Client) (storage.Backend, error) { const op errors.Op = "actions.GetStorage" switch storageType { diff --git a/cmd/proxy/actions/sumdb.go b/cmd/proxy/actions/sumdb.go index 24018b1c..6a9dbdbd 100644 --- a/cmd/proxy/actions/sumdb.go +++ b/cmd/proxy/actions/sumdb.go @@ -17,12 +17,12 @@ func sumdbProxy(url *url.URL, nosumPatterns []string) http.Handler { req.URL.Host = url.Host } if len(nosumPatterns) > 0 { - return noSumWrapper(rp, url.Host, nosumPatterns) + return noSumWrapper(rp, nosumPatterns) } return rp } -func noSumWrapper(h http.Handler, host string, patterns []string) http.Handler { +func noSumWrapper(h http.Handler, patterns []string) http.Handler { return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { if strings.HasPrefix(r.URL.Path, "/lookup/") { for _, p := range patterns { diff --git a/cmd/proxy/actions/sumdb_test.go b/cmd/proxy/actions/sumdb_test.go index 195b9176..ff55604e 100644 --- a/cmd/proxy/actions/sumdb_test.go +++ b/cmd/proxy/actions/sumdb_test.go @@ -97,7 +97,7 @@ func TestNoSumPatterns(t *testing.T) { for _, tc := range noSumTestCases { t.Run(tc.name, func(t *testing.T) { w := httptest.NewRecorder() - skipHandler := noSumWrapper(http.HandlerFunc(emptyHandler), "sum.golang.org", tc.patterns) + skipHandler := noSumWrapper(http.HandlerFunc(emptyHandler), tc.patterns) req := httptest.NewRequest("GET", "/lookup/"+tc.given, nil) skipHandler.ServeHTTP(w, req) if tc.status != w.Code { diff --git a/cmd/proxy/actions/version.go b/cmd/proxy/actions/version.go index ba23a468..a0cba012 100644 --- a/cmd/proxy/actions/version.go +++ b/cmd/proxy/actions/version.go @@ -8,5 +8,5 @@ import ( ) func versionHandler(w http.ResponseWriter, r *http.Request) { - json.NewEncoder(w).Encode(build.Data()) + _ = json.NewEncoder(w).Encode(build.Data()) } diff --git a/cmd/proxy/main.go b/cmd/proxy/main.go index dafd68e2..13e981d7 100644 --- a/cmd/proxy/main.go +++ b/cmd/proxy/main.go @@ -2,16 +2,16 @@ package main import ( "context" + "errors" "flag" "fmt" "log" "net/http" + _ "net/http/pprof" "os" "os/signal" "time" - _ "net/http/pprof" - "github.com/gomods/athens/cmd/proxy/actions" "github.com/gomods/athens/internal/shutdown" "github.com/gomods/athens/pkg/build" @@ -45,8 +45,9 @@ func main() { } srv := &http.Server{ - Addr: conf.Port, - Handler: handler, + Addr: conf.Port, + Handler: handler, + ReadHeaderTimeout: 2 * time.Second, } idleConnsClosed := make(chan struct{}) @@ -54,7 +55,7 @@ func main() { sigint := make(chan os.Signal, 1) signal.Notify(sigint, shutdown.GetSignals()...) s := <-sigint - log.Printf("Recived signal (%s): Shutting down server", s) + log.Printf("Received signal (%s): Shutting down server", s) // We received an interrupt signal, shut down. ctx, cancel := context.WithTimeout(context.Background(), time.Second*time.Duration(conf.ShutdownTimeout)) @@ -70,7 +71,7 @@ func main() { // pprof to be exposed on a different port than the application for security matters, not to expose profiling data and avoid DoS attacks (profiling slows down the service) // https://www.farsightsecurity.com/txt-record/2016/10/28/cmikk-go-remote-profiling/ log.Printf("Starting `pprof` at port %v", conf.PprofPort) - log.Fatal(http.ListenAndServe(conf.PprofPort, nil)) + log.Fatal(http.ListenAndServe(conf.PprofPort, nil)) //nolint:gosec // This should not be exposed to the world. }() } @@ -81,7 +82,7 @@ func main() { err = srv.ListenAndServe() } - if err != http.ErrServerClosed { + if !errors.Is(err, http.ErrServerClosed) { log.Fatal(err) } diff --git a/init.ps1 b/init.ps1 index 6ff89a31..36ee0919 100644 --- a/init.ps1 +++ b/init.ps1 @@ -79,8 +79,6 @@ if ($docs.IsPresent) { } if ($verify.IsPresent) { - execScript "check_gofmt.ps1" - execScript "check_govet.ps1" execScript "check_deps.ps1" } diff --git a/pkg/build/build.go b/pkg/build/build.go index bfb74b25..d88f1987 100644 --- a/pkg/build/build.go +++ b/pkg/build/build.go @@ -9,7 +9,7 @@ import ( "fmt" ) -// Details represents known data for a given build +// Details represents known data for a given build. type Details struct { Version string `json:"version,omitempty"` Date string `json:"date,omitempty"` @@ -29,7 +29,7 @@ func String() string { return fmt.Sprintf("Build Details:\n\tVersion:\t%s\n\tDate:\t\t%s", version, buildDate) } -// Data returns build details as a struct +// Data returns build details as a struct. func Data() Details { return Details{ Version: version, diff --git a/pkg/config/azureblob.go b/pkg/config/azureblob.go index fb532fc4..bbff569f 100644 --- a/pkg/config/azureblob.go +++ b/pkg/config/azureblob.go @@ -1,6 +1,6 @@ package config -// AzureBlobConfig specifies the properties required to use Azure as the storage backend +// AzureBlobConfig specifies the properties required to use Azure as the storage backend. type AzureBlobConfig struct { AccountName string `validate:"required" envconfig:"ATHENS_AZURE_ACCOUNT_NAME"` AccountKey string `validate:"required" envconfig:"ATHENS_AZURE_ACCOUNT_KEY"` diff --git a/pkg/config/config.go b/pkg/config/config.go index ca2a1b0e..d480f8a7 100644 --- a/pkg/config/config.go +++ b/pkg/config/config.go @@ -18,7 +18,7 @@ import ( const defaultConfigFile = "athens.toml" -// Config provides configuration values for all components +// Config provides configuration values for all components. type Config struct { TimeoutConf GoEnv string `validate:"required" envconfig:"GO_ENV"` @@ -63,12 +63,12 @@ type Config struct { } // EnvList is a list of key-value environment -// variables that are passed to the Go command +// variables that are passed to the Go command. type EnvList []string // HasKey returns whether a key-value entry // is present by only checking the left of -// key=value +// key=value. func (el EnvList) HasKey(key string) bool { for _, env := range el { if strings.HasPrefix(env, key+"=") { @@ -79,7 +79,7 @@ func (el EnvList) HasKey(key string) bool { } // Add adds a key=value entry to the environment -// list +// list. func (el *EnvList) Add(key, value string) { *el = append(*el, key+"="+value) } @@ -110,7 +110,7 @@ func (el *EnvList) Decode(value string) error { } // Validate validates that all strings inside the -// list are of the key=value format +// list are of the key=value format. func (el EnvList) Validate() error { const op errors.Op = "EnvList.Validate" for _, env := range el { @@ -123,7 +123,7 @@ func (el EnvList) Validate() error { } // Load loads the config from a file. -// If file is not present returns default config +// If file is not present returns default config. func Load(configFile string) (*Config, error) { // User explicitly specified a config file if configFile != "" { @@ -206,7 +206,7 @@ func defaultConfig() *Config { } // BasicAuth returns BasicAuthUser and BasicAuthPassword -// and ok if neither of them are empty +// and ok if neither of them are empty. func (c *Config) BasicAuth() (user, pass string, ok bool) { user = c.BasicAuthUser pass = c.BasicAuthPass @@ -215,7 +215,7 @@ func (c *Config) BasicAuth() (user, pass string, ok bool) { } // TLSCertFiles returns certificate and key files and an error if -// both files doesn't exist and have approperiate file permissions +// both files doesn't exist and have approperiate file permissions. func (c *Config) TLSCertFiles() (cert, key string, err error) { if c.TLSCertFile == "" && c.TLSKeyFile == "" { return "", "", nil @@ -223,29 +223,28 @@ func (c *Config) TLSCertFiles() (cert, key string, err error) { certFile, err := os.Stat(c.TLSCertFile) if err != nil { - return "", "", fmt.Errorf("Could not access TLSCertFile: %v", err) + return "", "", fmt.Errorf("could not access TLSCertFile: %w", err) } keyFile, err := os.Stat(c.TLSKeyFile) if err != nil { - return "", "", fmt.Errorf("Could not access TLSKeyFile: %v", err) + return "", "", fmt.Errorf("could not access TLSKeyFile: %w", err) } - if keyFile.Mode()&077 != 0 && runtime.GOOS != "windows" { + if keyFile.Mode()&0o077 != 0 && runtime.GOOS != "windows" { return "", "", fmt.Errorf("TLSKeyFile should not be accessible by others") } return certFile.Name(), keyFile.Name(), nil } -// FilterOff returns true if the FilterFile is empty +// FilterOff returns true if the FilterFile is empty. func (c *Config) FilterOff() bool { return c.FilterFile == "" } -// ParseConfigFile parses the given file into an athens config struct +// ParseConfigFile parses the given file into an athens config struct. func ParseConfigFile(configFile string) (*Config, error) { - var config Config // attempt to read the given config file if _, err := toml.DecodeFile(configFile, &config); err != nil { @@ -271,7 +270,7 @@ func ParseConfigFile(configFile string) (*Config, error) { return &config, nil } -// envOverride uses Environment variables to override unspecified properties +// envOverride uses Environment variables to override unspecified properties. func envOverride(config *Config) error { const defaultPort = ":3000" err := envconfig.Process("athens", config) @@ -355,16 +354,16 @@ func validateIndex(validate *validator.Validate, indexType string, config *Index func GetConf(path string) (*Config, error) { absPath, err := filepath.Abs(path) if err != nil { - return nil, fmt.Errorf("Unable to construct absolute path to test config file") + return nil, fmt.Errorf("unable to construct absolute path to test config file") } conf, err := ParseConfigFile(absPath) if err != nil { - return nil, fmt.Errorf("Unable to parse test config file: %s", err.Error()) + return nil, fmt.Errorf("unable to parse test config file: %w", err) } return conf, nil } -// checkFilePerms given a list of files +// checkFilePerms given a list of files. func checkFilePerms(files ...string) error { const op = "config.checkFilePerms" @@ -384,10 +383,9 @@ func checkFilePerms(files ...string) error { // Assume unix based system (MacOS and Linux) // the bit mask is calculated using the umask command which tells which permissions // should not be allowed for a particular user, group or world - if fInfo.Mode()&0077 != 0 && runtime.GOOS != "windows" { + if fInfo.Mode()&0o077 != 0 && runtime.GOOS != "windows" { return errors.E(op, f+" should have at most rwx,-, - (bit mask 077) as permission") } - } return nil diff --git a/pkg/config/disk.go b/pkg/config/disk.go index e8d37694..2db42f22 100644 --- a/pkg/config/disk.go +++ b/pkg/config/disk.go @@ -1,6 +1,6 @@ package config -// DiskConfig specifies the properties required to use Disk as the storage backend +// DiskConfig specifies the properties required to use Disk as the storage backend. type DiskConfig struct { RootPath string `validate:"required" envconfig:"ATHENS_DISK_STORAGE_ROOT"` } diff --git a/pkg/config/external.go b/pkg/config/external.go index a3f70b7a..ff5f563f 100644 --- a/pkg/config/external.go +++ b/pkg/config/external.go @@ -1,6 +1,6 @@ package config -// External specifies configuration for an external http storage +// External specifies configuration for an external http storage. type External struct { URL string `validate:"required" envconfig:"ATHENS_EXTERNAL_STORAGE_URL"` } diff --git a/pkg/config/gcp.go b/pkg/config/gcp.go index 36321a12..26f7343a 100644 --- a/pkg/config/gcp.go +++ b/pkg/config/gcp.go @@ -1,6 +1,6 @@ package config -// GCPConfig specifies the properties required to use GCP as the storage backend +// GCPConfig specifies the properties required to use GCP as the storage backend. type GCPConfig struct { ProjectID string `envconfig:"GOOGLE_CLOUD_PROJECT"` Bucket string `validate:"required" envconfig:"ATHENS_STORAGE_GCP_BUCKET"` diff --git a/pkg/config/index.go b/pkg/config/index.go index ed050ea9..c5ddd0af 100644 --- a/pkg/config/index.go +++ b/pkg/config/index.go @@ -1,6 +1,6 @@ package config -// Index is the config for various index storage backends +// Index is the config for various index storage backends. type Index struct { MySQL *MySQL Postgres *Postgres diff --git a/pkg/config/minio.go b/pkg/config/minio.go index fc5885f4..ee624382 100644 --- a/pkg/config/minio.go +++ b/pkg/config/minio.go @@ -1,7 +1,7 @@ package config // MinioConfig specifies the properties required to use Minio or DigitalOcean Spaces -// as the storage backend +// as the storage backend. type MinioConfig struct { Endpoint string `validate:"required" envconfig:"ATHENS_MINIO_ENDPOINT"` Key string `validate:"required" envconfig:"ATHENS_MINIO_ACCESS_KEY_ID"` diff --git a/pkg/config/module.go b/pkg/config/module.go index 48412f84..f76bd1e9 100644 --- a/pkg/config/module.go +++ b/pkg/config/module.go @@ -7,20 +7,20 @@ import ( ) // PackageVersionedName return package full name used in storage. -// E.g athens/@v/v1.0.mod +// E.g athens/@v/v1.0.mod. func PackageVersionedName(module, version, ext string) string { return fmt.Sprintf("%s/@v/%s.%s", module, version, ext) } // FmtModVer is a helper function that can take -// pkg/a/b and v2.3.1 and returns pkg/a/b@v2.3.1 +// pkg/a/b and v2.3.1 and returns pkg/a/b@v2.3.1. func FmtModVer(mod, ver string) string { return fmt.Sprintf("%s@%s", mod, ver) } // ModuleVersionFromPath returns module and version from a -// storage path -// E.g athens/@v/v1.0.info -> athens and v.1.0 +// storage path. +// E.g athens/@v/v1.0.info -> athens and v.1.0. func ModuleVersionFromPath(path string) (string, string) { segments := strings.Split(path, "/@v/") if len(segments) != 2 { diff --git a/pkg/config/mongo.go b/pkg/config/mongo.go index 044e5e49..91f6f654 100644 --- a/pkg/config/mongo.go +++ b/pkg/config/mongo.go @@ -1,6 +1,6 @@ package config -// MongoConfig specifies the properties required to use MongoDB as the storage backend +// MongoConfig specifies the properties required to use MongoDB as the storage backend. type MongoConfig struct { URL string `validate:"required" envconfig:"ATHENS_MONGO_STORAGE_URL"` DefaultDBName string `envconfig:"ATHENS_MONGO_DEFAULT_DATABASE" default:"athens"` diff --git a/pkg/config/mysql.go b/pkg/config/mysql.go index 3234bc10..46e5c959 100644 --- a/pkg/config/mysql.go +++ b/pkg/config/mysql.go @@ -1,6 +1,6 @@ package config -// MySQL config +// MySQL config. type MySQL struct { Protocol string `validate:"required" envconfig:"ATHENS_INDEX_MYSQL_PROTOCOL"` Host string `validate:"required" envconfig:"ATHENS_INDEX_MYSQL_HOST"` diff --git a/pkg/config/postgres.go b/pkg/config/postgres.go index 4a4c07fc..ee2827f1 100644 --- a/pkg/config/postgres.go +++ b/pkg/config/postgres.go @@ -1,6 +1,6 @@ package config -// Postgres config +// Postgres config. type Postgres struct { Host string `validate:"required" envconfig:"ATHENS_INDEX_POSTGRES_HOST"` Port int `validate:"required" envconfig:"ATHENS_INDEX_POSTGRES_PORT"` diff --git a/pkg/config/s3.go b/pkg/config/s3.go index bc644408..f72faefd 100644 --- a/pkg/config/s3.go +++ b/pkg/config/s3.go @@ -1,6 +1,6 @@ package config -// S3Config specifies the properties required to use S3 as the storage backend +// S3Config specifies the properties required to use S3 as the storage backend. type S3Config struct { Region string `validate:"required" envconfig:"AWS_REGION"` Key string `envconfig:"AWS_ACCESS_KEY_ID"` diff --git a/pkg/config/singleflight.go b/pkg/config/singleflight.go index 49cc2568..69049b10 100644 --- a/pkg/config/singleflight.go +++ b/pkg/config/singleflight.go @@ -25,7 +25,7 @@ type Redis struct { } // RedisSentinel is the configuration for using redis with sentinel -// for SingleFlight +// for SingleFlight. type RedisSentinel struct { Endpoints []string `envconfig:"ATHENS_REDIS_SENTINEL_ENDPOINTS"` MasterName string `envconfig:"ATHENS_REDIS_SENTINEL_MASTER_NAME"` @@ -33,12 +33,14 @@ type RedisSentinel struct { LockConfig *RedisLockConfig } +// RedisLockConfig is the configuration for redis locking. type RedisLockConfig struct { Timeout int `envconfig:"ATHENS_REDIS_LOCK_TIMEOUT"` TTL int `envconfig:"ATHENS_REDIS_LOCK_TTL"` MaxRetries int `envconfig:"ATHENS_REDIS_LOCK_MAX_RETRIES"` } +// DefaultRedisLockConfig returns the default redis locking configuration. func DefaultRedisLockConfig() *RedisLockConfig { return &RedisLockConfig{ TTL: 900, diff --git a/pkg/config/storage.go b/pkg/config/storage.go index 9dd8ffa9..49d0c605 100644 --- a/pkg/config/storage.go +++ b/pkg/config/storage.go @@ -1,6 +1,6 @@ package config -// Storage provides configs for various storage backends +// Storage provides configs for various storage backends. type Storage struct { Disk *DiskConfig GCP *GCPConfig diff --git a/pkg/config/timeout.go b/pkg/config/timeout.go index 8e86b84f..507bb308 100644 --- a/pkg/config/timeout.go +++ b/pkg/config/timeout.go @@ -2,17 +2,17 @@ package config import "time" -// TimeoutConf is a common struct for anything with a timeout +// TimeoutConf is a common struct for anything with a timeout. type TimeoutConf struct { Timeout int `validate:"required"` } -// TimeoutDuration returns the timeout as time.duration +// TimeoutDuration returns the timeout as time.duration. func (t *TimeoutConf) TimeoutDuration() time.Duration { return GetTimeoutDuration(t.Timeout) } -// GetTimeoutDuration returns the timeout as time.duration +// GetTimeoutDuration returns the timeout as time.duration. func GetTimeoutDuration(timeout int) time.Duration { return time.Second * time.Duration(timeout) } diff --git a/pkg/download/get_module_params.go b/pkg/download/get_module_params.go index ff8bdd33..1d68a2bf 100644 --- a/pkg/download/get_module_params.go +++ b/pkg/download/get_module_params.go @@ -7,7 +7,7 @@ import ( "github.com/gomods/athens/pkg/paths" ) -func getModuleParams(r *http.Request, op errors.Op) (mod string, ver string, err error) { +func getModuleParams(r *http.Request, op errors.Op) (mod, ver string, err error) { params, err := paths.GetAllParams(r) if err != nil { return "", "", errors.E(op, err, errors.KindBadRequest) diff --git a/pkg/download/handler.go b/pkg/download/handler.go index 13106e62..8045e4d1 100644 --- a/pkg/download/handler.go +++ b/pkg/download/handler.go @@ -15,8 +15,7 @@ import ( // a ready-to-go http handler that serves up cmd/go's download protocol. type ProtocolHandler func(dp Protocol, lggr log.Entry, df *mode.DownloadFile) http.Handler -// HandlerOpts are the generic options -// for a ProtocolHandler +// HandlerOpts are the generic options for a ProtocolHandler. type HandlerOpts struct { Protocol Protocol Logger *log.Logger @@ -26,7 +25,7 @@ type HandlerOpts struct { // LogEntryHandler pulls a log entry from the request context. Thanks to the // LogEntryMiddleware, we should have a log entry stored in the context for each // request with request-specific fields. This will grab the entry and pass it to -// the protocol handlers +// the protocol handlers. func LogEntryHandler(ph ProtocolHandler, opts *HandlerOpts) http.Handler { f := func(w http.ResponseWriter, r *http.Request) { ent := log.EntryFromContext(r.Context()) diff --git a/pkg/download/latest.go b/pkg/download/latest.go index e288141f..6e5aad19 100644 --- a/pkg/download/latest.go +++ b/pkg/download/latest.go @@ -13,7 +13,7 @@ import ( // PathLatest URL. const PathLatest = "/{module:.+}/@latest" -// LatestHandler implements GET baseURL/module/@latest +// LatestHandler implements GET baseURL/module/@latest. func LatestHandler(dp Protocol, lggr log.Entry, df *mode.DownloadFile) http.Handler { const op errors.Op = "download.LatestHandler" f := func(w http.ResponseWriter, r *http.Request) { diff --git a/pkg/download/list.go b/pkg/download/list.go index 3de919a4..405fbca4 100644 --- a/pkg/download/list.go +++ b/pkg/download/list.go @@ -14,7 +14,7 @@ import ( // PathList URL. const PathList = "/{module:.+}/@v/list" -// ListHandler implements GET baseURL/module/@v/list +// ListHandler implements GET baseURL/module/@v/list. func ListHandler(dp Protocol, lggr log.Entry, df *mode.DownloadFile) http.Handler { const op errors.Op = "download.ListHandler" f := func(w http.ResponseWriter, r *http.Request) { diff --git a/pkg/download/mode/mode.go b/pkg/download/mode/mode.go index 0e46132f..20960eda 100644 --- a/pkg/download/mode/mode.go +++ b/pkg/download/mode/mode.go @@ -4,6 +4,7 @@ import ( "encoding/base64" "fmt" "os" + "path/filepath" "strings" "github.com/gomods/athens/pkg/errors" @@ -16,7 +17,7 @@ import ( // when a module is not found in storage. type Mode string -// DownloadMode constants. For more information see config.dev.toml +// DownloadMode constants. For more information see config.dev.toml. const ( Sync Mode = "sync" Async Mode = "async" @@ -59,7 +60,7 @@ func NewFile(m Mode, downloadURL string) (*DownloadFile, error) { if strings.HasPrefix(string(m), "file:") { filePath := string(m[5:]) - bts, err := os.ReadFile(filePath) + bts, err := os.ReadFile(filepath.Clean(filePath)) if err != nil { return nil, err } diff --git a/pkg/download/protocol.go b/pkg/download/protocol.go index 324a0767..b9d5945e 100644 --- a/pkg/download/protocol.go +++ b/pkg/download/protocol.go @@ -48,7 +48,7 @@ type Opts struct { NetworkMode string } -// NetworkMode constants +// NetworkMode constants. const ( Strict = "strict" Offline = "offline" @@ -263,12 +263,12 @@ func (p *protocol) processDownload(ctx context.Context, mod, ver string, f func( } return f(newVer) case mode.Async: - go p.stasher.Stash(ctx, mod, ver) + go func() { _, _ = p.stasher.Stash(ctx, mod, ver) }() return errors.E(op, "async: module not found", errors.KindNotFound) case mode.Redirect: return errors.E(op, "redirect", errors.KindRedirect) case mode.AsyncRedirect: - go p.stasher.Stash(ctx, mod, ver) + go func() { _, _ = p.stasher.Stash(ctx, mod, ver) }() return errors.E(op, "async_redirect: module not found", errors.KindRedirect) case mode.None: return errors.E(op, "none", errors.KindNotFound) @@ -276,7 +276,7 @@ func (p *protocol) processDownload(ctx context.Context, mod, ver string, f func( return nil } -// union concatenates two version lists and removes duplicates +// union concatenates two version lists and removes duplicates. func union(list1, list2 []string) []string { if list1 == nil { list1 = []string{} @@ -284,10 +284,10 @@ func union(list1, list2 []string) []string { if list2 == nil { list2 = []string{} } - list := append(list1, list2...) + list1 = append(list1, list2...) unique := []string{} m := make(map[string]struct{}) - for _, v := range list { + for _, v := range list1 { if _, ok := m[v]; !ok { unique = append(unique, v) m[v] = struct{}{} diff --git a/pkg/download/version_info.go b/pkg/download/version_info.go index b0dd802e..3a6c9603 100644 --- a/pkg/download/version_info.go +++ b/pkg/download/version_info.go @@ -11,7 +11,7 @@ import ( // PathVersionInfo URL. const PathVersionInfo = "/{module:.+}/@v/{version}.info" -// InfoHandler implements GET baseURL/module/@v/version.info +// InfoHandler implements GET baseURL/module/@v/version.info. func InfoHandler(dp Protocol, lggr log.Entry, df *mode.DownloadFile) http.Handler { const op errors.Op = "download.InfoHandler" f := func(w http.ResponseWriter, r *http.Request) { @@ -38,7 +38,7 @@ func InfoHandler(dp Protocol, lggr log.Entry, df *mode.DownloadFile) http.Handle w.WriteHeader(errors.Kind(err)) } - w.Write(info) + _, _ = w.Write(info) } return http.HandlerFunc(f) } diff --git a/pkg/download/version_module.go b/pkg/download/version_module.go index 46806894..1a100230 100644 --- a/pkg/download/version_module.go +++ b/pkg/download/version_module.go @@ -11,7 +11,7 @@ import ( // PathVersionModule URL. const PathVersionModule = "/{module:.+}/@v/{version}.mod" -// ModuleHandler implements GET baseURL/module/@v/version.mod +// ModuleHandler implements GET baseURL/module/@v/version.mod. func ModuleHandler(dp Protocol, lggr log.Entry, df *mode.DownloadFile) http.Handler { const op errors.Op = "download.VersionModuleHandler" f := func(w http.ResponseWriter, r *http.Request) { @@ -42,7 +42,7 @@ func ModuleHandler(dp Protocol, lggr log.Entry, df *mode.DownloadFile) http.Hand return } - w.Write(modBts) + _, _ = w.Write(modBts) } return http.HandlerFunc(f) } diff --git a/pkg/download/version_zip.go b/pkg/download/version_zip.go index d9bdb862..bfc5b773 100644 --- a/pkg/download/version_zip.go +++ b/pkg/download/version_zip.go @@ -13,7 +13,7 @@ import ( // PathVersionZip URL. const PathVersionZip = "/{module:.+}/@v/{version}.zip" -// ZipHandler implements GET baseURL/module/@v/version.zip +// ZipHandler implements GET baseURL/module/@v/version.zip. func ZipHandler(dp Protocol, lggr log.Entry, df *mode.DownloadFile) http.Handler { const op errors.Op = "download.ZipHandler" f := func(w http.ResponseWriter, r *http.Request) { @@ -41,7 +41,7 @@ func ZipHandler(dp Protocol, lggr log.Entry, df *mode.DownloadFile) http.Handler w.WriteHeader(errors.Kind(err)) return } - defer zip.Close() + defer func() { _ = zip.Close() }() w.Header().Set("Content-Type", "application/zip") size := zip.Size() diff --git a/pkg/errors/errors.go b/pkg/errors/errors.go index 9700b936..4f922034 100644 --- a/pkg/errors/errors.go +++ b/pkg/errors/errors.go @@ -9,7 +9,7 @@ import ( "github.com/sirupsen/logrus" ) -// Kind enums +// Kind enums. const ( KindNotFound = http.StatusNotFound KindBadRequest = http.StatusBadRequest @@ -55,6 +55,16 @@ func Is(err error, kind int) bool { return Kind(err) == kind } +// IsErr is a convenience wrapper around the std library errors.Is. +func IsErr(err, target error) bool { + return errors.Is(err, target) +} + +// AsErr is a convenience wrapper around the std library errors.As. +func AsErr(err error, target any) bool { + return errors.As(err, target) +} + // Op describes any independent function or // method in Athens. A series of operations // forms a more readable stack trace. @@ -69,7 +79,7 @@ func (o Op) String() string { // a module from a regular error string or version. type M string -// V represents a module version in an error +// V represents a module version in an error. type V string // E is a helper function to construct an Error type @@ -114,8 +124,8 @@ func E(op Op, args ...interface{}) error { // if none exists, then the level is Error because // it is an unexpected. func Severity(err error) logrus.Level { - e, ok := err.(Error) - if !ok { + var e Error + if !errors.As(err, &e) { return logrus.ErrorLevel } @@ -145,8 +155,8 @@ func Expect(err error, kinds ...int) logrus.Level { // Kind recursively searches for the // first error kind it finds. func Kind(err error) int { - e, ok := err.(Error) - if !ok { + var e Error + if !errors.As(err, &e) { return KindUnexpected } @@ -173,6 +183,7 @@ func KindText(err error) string { func Ops(err Error) []Op { ops := []Op{err.Op} for { + //nolint:errorlint // We iterate the errors anyway. embeddedErr, ok := err.Err.(Error) if !ok { break diff --git a/pkg/errors/kinds.go b/pkg/errors/kinds.go index e62417ea..5b2f46d8 100644 --- a/pkg/errors/kinds.go +++ b/pkg/errors/kinds.go @@ -1,6 +1,6 @@ package errors -// IsNotFoundErr helper function for KindNotFound +// IsNotFoundErr helper function for KindNotFound. func IsNotFoundErr(err error) bool { return Kind(err) == KindNotFound } diff --git a/pkg/index/compliance/compliance.go b/pkg/index/compliance/compliance.go index 16f46499..e27591e7 100644 --- a/pkg/index/compliance/compliance.go +++ b/pkg/index/compliance/compliance.go @@ -21,7 +21,7 @@ func RunTests(t *testing.T, indexer index.Indexer, clearIndex func() error) { t.Fatal(err) } - var tests = []struct { + tests := []struct { name string desc string limit int diff --git a/pkg/index/indexer.go b/pkg/index/indexer.go index 395aa0c5..5b6f8a3d 100644 --- a/pkg/index/indexer.go +++ b/pkg/index/indexer.go @@ -13,7 +13,7 @@ type Line struct { } // Indexer is an interface that can process new module@versions -// and also retrieve 'limit' module@versions that were indexed after 'since' +// and also retrieve 'limit' module@versions that were indexed after 'since'. type Indexer interface { // Index stores the module@version into the index backend. // Implementer must create the Timestamp at the time and set it diff --git a/pkg/index/mem/mem.go b/pkg/index/mem/mem.go index 253f0d43..e6d0bf7e 100644 --- a/pkg/index/mem/mem.go +++ b/pkg/index/mem/mem.go @@ -10,7 +10,7 @@ import ( "github.com/gomods/athens/pkg/index" ) -// New returns a new in-memory indexer +// New returns a new in-memory indexer. func New() index.Indexer { return &indexer{} } diff --git a/pkg/index/mysql/mysql.go b/pkg/index/mysql/mysql.go index 7298cd3f..39f66771 100644 --- a/pkg/index/mysql/mysql.go +++ b/pkg/index/mysql/mysql.go @@ -83,8 +83,8 @@ func (i *indexer) Lines(ctx context.Context, since time.Time, limit int) ([]*ind if err != nil { return nil, errors.E(op, err) } - defer rows.Close() - lines := []*index.Line{} + defer func() { _ = rows.Close() }() + var lines []*index.Line for rows.Next() { var line index.Line err = rows.Scan(&line.Path, &line.Version, &line.Timestamp) @@ -108,13 +108,14 @@ func getMySQLSource(cfg *config.MySQL) string { } func getKind(err error) int { - mysqlErr, ok := err.(*mysql.MySQLError) - if !ok { + mysqlErr := &mysql.MySQLError{} + if !errors.AsErr(err, &mysqlErr) { return errors.KindUnexpected } switch mysqlErr.Number { case 1062: return errors.KindAlreadyExists + default: + return errors.KindUnexpected } - return errors.KindUnexpected } diff --git a/pkg/index/nop/nop.go b/pkg/index/nop/nop.go index 5140d249..91049156 100644 --- a/pkg/index/nop/nop.go +++ b/pkg/index/nop/nop.go @@ -7,7 +7,7 @@ import ( "github.com/gomods/athens/pkg/index" ) -// New returns a no-op Indexer +// New returns a no-op Indexer. func New() index.Indexer { return indexer{} } @@ -17,6 +17,7 @@ type indexer struct{} func (indexer) Index(ctx context.Context, mod, ver string) error { return nil } + func (indexer) Lines(ctx context.Context, since time.Time, limit int) ([]*index.Line, error) { return []*index.Line{}, nil } diff --git a/pkg/index/postgres/postgres.go b/pkg/index/postgres/postgres.go index 6fb14c40..e3d83bcc 100644 --- a/pkg/index/postgres/postgres.go +++ b/pkg/index/postgres/postgres.go @@ -7,17 +7,15 @@ import ( "strings" "time" - // register the driver with database/sql - "github.com/lib/pq" - "github.com/gomods/athens/pkg/config" "github.com/gomods/athens/pkg/errors" "github.com/gomods/athens/pkg/index" + "github.com/lib/pq" ) // New returns a new Indexer with a PostgreSQL implementation. // It attempts to connect to the DB and create the index table -// if it doesn ot already exist. +// if it does not already exist. func New(cfg *config.Postgres) (index.Indexer, error) { dataSource := getPostgresSource(cfg) db, err := sql.Open("postgres", dataSource) @@ -42,7 +40,7 @@ var schema = [...]string{ id SERIAL PRIMARY KEY, path VARCHAR(255) NOT NULL, version VARCHAR(255) NOT NULL, - timestamp timestamp NOT NULL + timestamp TIMESTAMP NOT NULL ) `, ` @@ -82,8 +80,8 @@ func (i *indexer) Lines(ctx context.Context, since time.Time, limit int) ([]*ind if err != nil { return nil, errors.E(op, err) } - defer rows.Close() - lines := []*index.Line{} + defer func() { _ = rows.Close() }() + var lines []*index.Line for rows.Next() { var line index.Line err = rows.Scan(&line.Path, &line.Version, &line.Timestamp) @@ -96,7 +94,7 @@ func (i *indexer) Lines(ctx context.Context, since time.Time, limit int) ([]*ind } func getPostgresSource(cfg *config.Postgres) string { - args := []string{} + args := make([]string, 0, 5+len(cfg.Params)) args = append(args, "host="+cfg.Host) args = append(args, "port=", strconv.Itoa(cfg.Port)) args = append(args, "user=", cfg.User) @@ -109,13 +107,14 @@ func getPostgresSource(cfg *config.Postgres) string { } func getKind(err error) int { - pqerr, ok := err.(*pq.Error) - if !ok { + pqerr := &pq.Error{} + if !errors.AsErr(err, &pqerr) { return errors.KindUnexpected } switch pqerr.Code { case "23505": return errors.KindAlreadyExists + default: + return errors.KindUnexpected } - return errors.KindUnexpected } diff --git a/pkg/log/entry.go b/pkg/log/entry.go index e90d9df4..ef21480e 100644 --- a/pkg/log/entry.go +++ b/pkg/log/entry.go @@ -35,8 +35,8 @@ func (e *entry) WithFields(fields map[string]interface{}) Entry { } func (e *entry) SystemErr(err error) { - athensErr, ok := err.(errors.Error) - if !ok { + var athensErr errors.Error + if !errors.IsErr(err, athensErr) { e.Error(err) return } diff --git a/pkg/log/log.go b/pkg/log/log.go index c0b78337..1c55d3e2 100644 --- a/pkg/log/log.go +++ b/pkg/log/log.go @@ -41,7 +41,7 @@ func (l *Logger) WithFields(fields map[string]interface{}) Entry { return &entry{e} } -// NoOpLogger provides a Logger that does nothing +// NoOpLogger provides a Logger that does nothing. func NoOpLogger() *Logger { return &Logger{ Logger: &logrus.Logger{}, diff --git a/pkg/log/log_context.go b/pkg/log/log_context.go index c4501df0..fd36069d 100644 --- a/pkg/log/log_context.go +++ b/pkg/log/log_context.go @@ -8,14 +8,14 @@ type ctxKey string const logEntryKey ctxKey = "log-entry-context-key" -// SetEntryInContext stores an Entry in the request context +// SetEntryInContext stores an Entry in the request context. func SetEntryInContext(ctx context.Context, e Entry) context.Context { return context.WithValue(ctx, logEntryKey, e) } // EntryFromContext returns an Entry that has been stored in the request context. // If there is no value for the key or the type assertion fails, it returns a new -// entry from the provided logger +// entry from the provided logger. func EntryFromContext(ctx context.Context) Entry { e, ok := ctx.Value(logEntryKey).(Entry) if !ok || e == nil { diff --git a/pkg/middleware/content_type.go b/pkg/middleware/content_type.go index 9078ea9a..76d906f6 100644 --- a/pkg/middleware/content_type.go +++ b/pkg/middleware/content_type.go @@ -4,7 +4,7 @@ import ( "net/http" ) -// ContentType writes writes an application/json +// ContentType writes an application/json // Content-Type header. func ContentType(h http.Handler) http.Handler { f := func(w http.ResponseWriter, r *http.Request) { diff --git a/pkg/middleware/log_entry.go b/pkg/middleware/log_entry.go index 88b084a9..8d6016cf 100644 --- a/pkg/middleware/log_entry.go +++ b/pkg/middleware/log_entry.go @@ -10,7 +10,7 @@ import ( ) // LogEntryMiddleware builds a log.Entry, setting the request fields -// and storing it in the context to be used throughout the stack +// and storing it in the context to be used throughout the stack. func LogEntryMiddleware(lggr *log.Logger) mux.MiddlewareFunc { return func(h http.Handler) http.Handler { f := func(w http.ResponseWriter, r *http.Request) { diff --git a/pkg/middleware/requestid.go b/pkg/middleware/requestid.go index d4850f94..9a528975 100644 --- a/pkg/middleware/requestid.go +++ b/pkg/middleware/requestid.go @@ -9,7 +9,7 @@ import ( // WithRequestID ensures a request id is in the // request context by either the incoming header -// or creating a new one +// or creating a new one. func WithRequestID(h http.Handler) http.Handler { return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { requestID := r.Header.Get(requestid.HeaderKey) diff --git a/pkg/middleware/validation.go b/pkg/middleware/validation.go index cd01a5e5..371b3df8 100644 --- a/pkg/middleware/validation.go +++ b/pkg/middleware/validation.go @@ -14,7 +14,7 @@ import ( ) // NewValidationMiddleware builds a middleware function that performs validation checks by calling -// an external webhook +// an external webhook. func NewValidationMiddleware(client *http.Client, validatorHook string) mux.MiddlewareFunc { const op errors.Op = "actions.NewValidationMiddleware" return func(h http.Handler) http.Handler { @@ -50,7 +50,7 @@ func NewValidationMiddleware(client *http.Client, validatorHook string) mux.Midd } } -func maybeLogValidationReason(context context.Context, message string, mod string, version string) { +func maybeLogValidationReason(context context.Context, message, mod, version string) { if len(message) > 0 { entry := log.EntryFromContext(context) entry.Warnf("error validating %s@%s %s", mod, version, message) @@ -85,6 +85,10 @@ func validate(ctx context.Context, client *http.Client, hook, mod, ver string) ( if err != nil { return validationResponse{Valid: false}, errors.E(op, err) } + defer func() { + _, _ = io.Copy(io.Discard, resp.Body) + _ = resp.Body.Close() + }() switch resp.StatusCode { case http.StatusOK: @@ -97,8 +101,6 @@ func validate(ctx context.Context, client *http.Client, hook, mod, ver string) ( } func validationResponseFromRequest(resp *http.Response) validationResponse { - defer resp.Body.Close() - body, _ := io.ReadAll(resp.Body) return validationResponse{Valid: resp.StatusCode == http.StatusOK, Message: body} } diff --git a/pkg/module/fetcher.go b/pkg/module/fetcher.go index 1f76bdfe..b0969a72 100644 --- a/pkg/module/fetcher.go +++ b/pkg/module/fetcher.go @@ -6,7 +6,7 @@ import ( "github.com/gomods/athens/pkg/storage" ) -// Fetcher fetches module from an upstream source +// Fetcher fetches module from an upstream source. type Fetcher interface { // Fetch downloads the sources from an upstream and returns the corresponding // .info, .mod, and .zip files. diff --git a/pkg/module/filter.go b/pkg/module/filter.go index 80b04eaa..8da2b9ca 100644 --- a/pkg/module/filter.go +++ b/pkg/module/filter.go @@ -3,6 +3,7 @@ package module import ( "bufio" "os" + "path/filepath" "strconv" "strings" @@ -14,15 +15,15 @@ var ( versionSeparator = "." ) -// Filter is a filter of modules +// Filter is a filter of modules. type Filter struct { root ruleNode filePath string } -// NewFilter creates new filter based on rules defined in a configuration file -// WARNING: this is not concurrently safe -// Configuration consists of two operations: + for include and - for exclude +// NewFilter creates new filter based on rules defined in a configuration file. +// WARNING: this is not concurrently safe. +// Configuration consists of two operations: + for include and - for exclude: // e.g. // - github.com/a // - github.com/a/b @@ -33,7 +34,7 @@ type Filter struct { // - // + github.com/a // -// will exclude all items from communication except github.com/a +// will exclude all items from communication except github.com/a. func NewFilter(filterFilePath string) (*Filter, error) { // Do not return an error if the file path is empty // Do not attempt to parse it as well. @@ -42,10 +43,9 @@ func NewFilter(filterFilePath string) (*Filter, error) { } return initFromConfig(filterFilePath) - } -// AddRule adds rule for specified path +// AddRule adds rule for specified path. func (f *Filter) AddRule(path string, qualifiers []string, rule FilterRule) { f.ensurePath(path) @@ -70,7 +70,7 @@ func (f *Filter) AddRule(path string, qualifiers []string, rule FilterRule) { latest.next[last] = rn } -// Rule returns the filter rule to be applied to the given path +// Rule returns the filter rule to be applied to the given path. func (f *Filter) Rule(path, version string) FilterRule { segs := getPathSegments(path) rule := f.getAssociatedRule(version, segs...) @@ -145,7 +145,6 @@ func initFromConfig(filePath string) (*Filter, error) { f.root = rn for idx, line := range lines { - // Ignore newline if len(line) == 0 { continue @@ -160,7 +159,7 @@ func initFromConfig(filePath string) (*Filter, error) { } ruleSign := strings.TrimSpace(split[0]) - rule := Default + var rule FilterRule switch ruleSign { case "+": rule = Include @@ -195,11 +194,11 @@ func initFromConfig(filePath string) (*Filter, error) { // matches checks if the given version matches the given qualifier. // Qualifiers can be: -// - plain versions -// - v1.2.3 enables v1.2.3 -// - ~1.2.3: enables 1.2.x which are at least 1.2.3 -// - ^1.2.3: enables 1.x.x which are at least 1.2.3 -// - <1.2.3: enables everything lower than 1.2.3 includes 1.2.2 and 0.58.9 as well +// - plain versions. +// - v1.2.3 enables v1.2.3. +// - ~1.2.3: enables 1.2.x which are at least 1.2.3. +// - ^1.2.3: enables 1.x.x which are at least 1.2.3. +// - <1.2.3: enables everything lower than 1.2.3 includes 1.2.2 and 0.58.9 as well. func matches(version, qualifier string) bool { if len(qualifier) < 2 || len(version) < 1 { return false @@ -297,7 +296,7 @@ func newRule(r FilterRule) ruleNode { func getConfigLines(filterFile string) ([]string, error) { const op errors.Op = "module.getConfigLines" - f, err := os.Open(filterFile) + f, err := os.Open(filepath.Clean(filterFile)) if err != nil { return nil, errors.E(op, err) } diff --git a/pkg/module/filterRule.go b/pkg/module/filterRule.go index 3a8b88f2..8d24c79f 100644 --- a/pkg/module/filterRule.go +++ b/pkg/module/filterRule.go @@ -1,16 +1,16 @@ package module -// FilterRule defines behavior of module communication +// FilterRule defines behavior of module communication. type FilterRule int const ( - // Default filter rule does not alter default/parent behavior + // Default filter rule does not alter default/parent behavior. Default FilterRule = iota - // Include treats modules the usual way - // Used for reverting Exclude of parent path + // Include treats modules the usual way. + // Used for reverting Exclude of parent path. Include - // Exclude filter rule excludes package and its children from communication + // Exclude filter rule excludes package and its children from communication. Exclude - // Direct filter rule forces the package to be fetched directly from upstream proxy + // Direct filter rule forces the package to be fetched directly from upstream proxy. Direct ) diff --git a/pkg/module/go_get_fetcher.go b/pkg/module/go_get_fetcher.go index 6e4ffb1e..e43d3529 100644 --- a/pkg/module/go_get_fetcher.go +++ b/pkg/module/go_get_fetcher.go @@ -35,7 +35,7 @@ type goModule struct { GoModSum string `json:"goModSum"` // checksum for go.mod (as in go.sum) } -// NewGoGetFetcher creates fetcher which uses go get tool to fetch modules +// NewGoGetFetcher creates fetcher which uses go get tool to fetch modules. func NewGoGetFetcher(goBinaryName, gogetDir string, envVars []string, fs afero.Fs) (Fetcher, error) { const op errors.Op = "module.NewGoGetFetcher" if err := validGoBinary(goBinaryName); err != nil { @@ -64,7 +64,7 @@ func (g *goGetFetcher) Fetch(ctx context.Context, mod, ver string) (*storage.Ver sourcePath := filepath.Join(goPathRoot, "src") modPath := filepath.Join(sourcePath, getRepoDirName(mod, ver)) if err := g.fs.MkdirAll(modPath, os.ModeDir|os.ModePerm); err != nil { - clearFiles(g.fs, goPathRoot) + _ = clearFiles(g.fs, goPathRoot) return nil, errors.E(op, err) } @@ -72,14 +72,13 @@ func (g *goGetFetcher) Fetch(ctx context.Context, mod, ver string) (*storage.Ver ctx, g.goBinaryName, g.envVars, - g.fs, goPathRoot, modPath, mod, ver, ) if err != nil { - clearFiles(g.fs, goPathRoot) + _ = clearFiles(g.fs, goPathRoot) return nil, errors.E(op, err) } @@ -116,7 +115,6 @@ func downloadModule( ctx context.Context, goBinaryName string, envVars []string, - fs afero.Fs, gopath, repoRoot, module, @@ -137,7 +135,7 @@ func downloadModule( err := cmd.Run() if err != nil { - err = fmt.Errorf("%v: %s", err, stderr) + err = fmt.Errorf("%w: %s", err, stderr) var m goModule if jsonErr := json.NewDecoder(stdout).Decode(&m); jsonErr != nil { return goModule{}, errors.E(op, err) @@ -165,17 +163,17 @@ func isLimitHit(o string) bool { } // getRepoDirName takes a raw repository URI and a version and creates a directory name that the -// repository contents can be put into +// repository contents can be put into. func getRepoDirName(repoURI, version string) string { - escapedURI := strings.Replace(repoURI, "/", "-", -1) + escapedURI := strings.ReplaceAll(repoURI, "/", "-") return fmt.Sprintf("%s-%s", escapedURI, version) } func validGoBinary(name string) error { const op errors.Op = "module.validGoBinary" err := exec.Command(name).Run() - _, ok := err.(*exec.ExitError) - if err != nil && !ok { + eErr := &exec.ExitError{} + if err != nil && !errors.AsErr(err, &eErr) { return errors.E(op, err) } return nil diff --git a/pkg/module/go_vcs_lister.go b/pkg/module/go_vcs_lister.go index 7a021d05..a6c1c611 100644 --- a/pkg/module/go_vcs_lister.go +++ b/pkg/module/go_vcs_lister.go @@ -28,7 +28,7 @@ type vcsLister struct { fs afero.Fs } -// NewVCSLister creates an UpstreamLister which uses VCS to fetch a list of available versions +// NewVCSLister creates an UpstreamLister which uses VCS to fetch a list of available versions. func NewVCSLister(goBinPath string, env []string, fs afero.Fs) UpstreamLister { return &vcsLister{ goBinPath: goBinPath, @@ -39,13 +39,13 @@ func NewVCSLister(goBinPath string, env []string, fs afero.Fs) UpstreamLister { func (l *vcsLister) List(ctx context.Context, module string) (*storage.RevInfo, []string, error) { const op errors.Op = "vcsLister.List" - ctx, span := observ.StartSpan(ctx, op.String()) + _, span := observ.StartSpan(ctx, op.String()) defer span.End() tmpDir, err := afero.TempDir(l.fs, "", "go-list") if err != nil { return nil, nil, errors.E(op, err) } - defer l.fs.RemoveAll(tmpDir) + defer func() { _ = l.fs.RemoveAll(tmpDir) }() cmd := exec.Command( l.goBinPath, @@ -62,12 +62,12 @@ func (l *vcsLister) List(ctx context.Context, module string) (*storage.RevInfo, if err != nil { return nil, nil, errors.E(op, err) } - defer clearFiles(l.fs, gopath) + defer func() { _ = clearFiles(l.fs, gopath) }() cmd.Env = prepareEnv(gopath, l.env) err = cmd.Run() if err != nil { - err = fmt.Errorf("%v: %s", err, stderr) + err = fmt.Errorf("%w: %s", err, stderr) // as of now, we can't recognize between a true NotFound // and an unexpected error, so we choose the more // hopeful path of NotFound. This way the Go command diff --git a/pkg/module/prepare_env.go b/pkg/module/prepare_env.go index 49549759..f12b8334 100644 --- a/pkg/module/prepare_env.go +++ b/pkg/module/prepare_env.go @@ -9,7 +9,7 @@ import ( // prepareEnv will return all the appropriate // environment variables for a Go Command to run -// successfully (such as GOPATH, GOCACHE, PATH etc) +// successfully (such as GOPATH, GOCACHE, PATH etc). func prepareEnv(gopath string, envVars []string) []string { gopathEnv := fmt.Sprintf("GOPATH=%s", gopath) cacheEnv := fmt.Sprintf("GOCACHE=%s", filepath.Join(gopath, "cache")) diff --git a/pkg/module/zip_read_closer.go b/pkg/module/zip_read_closer.go index 3f24f933..b6a14f38 100644 --- a/pkg/module/zip_read_closer.go +++ b/pkg/module/zip_read_closer.go @@ -14,10 +14,10 @@ type zipReadCloser struct { goPath string } -// Close closes the zip file handle and clears up disk space used by the underlying disk ref -// It is the caller's responsibility to call this method to free up utilized disk space +// Close closes the zip file handle and clears up disk space used by the underlying disk ref. +// It is the caller's responsibility to call this method to free up utilized disk space. func (rc *zipReadCloser) Close() error { - rc.zip.Close() + _ = rc.zip.Close() return clearFiles(rc.fs, rc.goPath) } @@ -25,8 +25,8 @@ func (rc *zipReadCloser) Read(p []byte) (n int, err error) { return rc.zip.Read(p) } -// clearFiles deletes all data from the given fs at path root -// This function must be called when zip is closed to cleanup the entire GOPATH created by the diskref +// clearFiles deletes all data from the given fs at path root. +// This function must be called when zip is closed to cleanup the entire GOPATH created by the diskref. func clearFiles(fs afero.Fs, root string) error { const op errors.Op = "module.ClearFiles" // This is required because vgo ensures dependencies are read-only @@ -36,7 +36,7 @@ func clearFiles(fs afero.Fs, root string) error { if err != nil { return err } - return fs.Chmod(path, 0770) + return fs.Chmod(path, 0o770) } err := afero.Walk(fs, root, walkFn) if err != nil { diff --git a/pkg/observ/observ.go b/pkg/observ/observ.go index 15181c33..b7f98657 100755 --- a/pkg/observ/observ.go +++ b/pkg/observ/observ.go @@ -13,16 +13,16 @@ import ( // RegisterExporter determines the type of TraceExporter service for exporting traces from opencensus // User can choose from multiple tracing services (datadog, jaegar) -// RegisterExporter returns the 'Flush' function for that particular tracing service -func RegisterExporter(traceExporter, URL, service, ENV string) (func(), error) { +// RegisterExporter returns the 'Flush' function for that particular tracing service. +func RegisterExporter(traceExporter, url, service, env string) (func(), error) { const op errors.Op = "observ.RegisterExporter" switch traceExporter { case "jaeger": - return registerJaegerExporter(URL, service, ENV) + return registerJaegerExporter(url, service, env) case "datadog": - return registerDatadogExporter(URL, service, ENV) + return registerDatadogExporter(url, service, env) case "stackdriver": - return registerStackdriverExporter(URL, ENV) + return registerStackdriverExporter(url, env) case "": return nil, errors.E(op, "Exporter not specified. Traces won't be exported") default: @@ -32,14 +32,14 @@ func RegisterExporter(traceExporter, URL, service, ENV string) (func(), error) { // registerJaegerExporter creates a jaeger exporter for exporting traces to opencensus. // Currently uses the 'TraceExporter' variable in the config file. -// It should in the future have a nice sampling rate defined -func registerJaegerExporter(URL, service, ENV string) (func(), error) { +// It should in the future have a nice sampling rate defined. +func registerJaegerExporter(url, service, env string) (func(), error) { const op errors.Op = "observ.registerJaegarExporter" - if URL == "" { + if url == "" { return nil, errors.E(op, "Exporter URL is empty. Traces won't be exported") } ex, err := jaeger.NewExporter(jaeger.Options{ - Endpoint: URL, + Endpoint: url, Process: jaeger.Process{ ServiceName: service, Tags: []jaeger.Tag{ @@ -53,41 +53,41 @@ func registerJaegerExporter(URL, service, ENV string) (func(), error) { if err != nil { return nil, errors.E(op, err) } - traceRegisterExporter(ex, ENV) + traceRegisterExporter(ex, env) return ex.Flush, nil } -func traceRegisterExporter(exporter trace.Exporter, ENV string) { +func traceRegisterExporter(exporter trace.Exporter, env string) { trace.RegisterExporter(exporter) - if ENV == "development" { + if env == "development" { trace.ApplyConfig(trace.Config{DefaultSampler: trace.AlwaysSample()}) } } // registerDatadogTracerExporter creates a datadog exporter. // Currently uses the 'TraceExporter' variable in the config file. -func registerDatadogExporter(URL, service, ENV string) (func(), error) { +func registerDatadogExporter(url, service, env string) (func(), error) { ex := datadog.NewExporter( datadog.Options{ - TraceAddr: URL, + TraceAddr: url, Service: service, }) - traceRegisterExporter(ex, ENV) + traceRegisterExporter(ex, env) return ex.Stop, nil } -func registerStackdriverExporter(projectID, ENV string) (func(), error) { +func registerStackdriverExporter(projectID, env string) (func(), error) { const op errors.Op = "observ.registerStackdriverExporter" ex, err := stackdriver.NewExporter(stackdriver.Options{ProjectID: projectID}) if err != nil { return nil, errors.E(op, err) } - traceRegisterExporter(ex, ENV) + traceRegisterExporter(ex, env) return ex.Flush, nil } // StartSpan takes in a Context Interface and opName and starts a span. It returns the new attached ObserverContext -// and span +// and span. func StartSpan(ctx context.Context, op string) (context.Context, *trace.Span) { return trace.StartSpan(ctx, op) } diff --git a/pkg/observ/stats.go b/pkg/observ/stats.go index 59560b12..a670ef2b 100755 --- a/pkg/observ/stats.go +++ b/pkg/observ/stats.go @@ -15,10 +15,10 @@ import ( ) // RegisterStatsExporter determines the type of StatsExporter service for exporting stats from Opencensus -// Currently it supports: prometheus +// Currently it supports: prometheus. func RegisterStatsExporter(r *mux.Router, statsExporter, service string) (func(), error) { const op errors.Op = "observ.RegisterStatsExporter" - var stop = func() {} + stop := func() {} var err error switch statsExporter { case "prometheus": @@ -38,7 +38,7 @@ func RegisterStatsExporter(r *mux.Router, statsExporter, service string) (func() default: return nil, errors.E(op, fmt.Sprintf("StatsExporter %s not supported. Please open PR or an issue at github.com/gomods/athens", statsExporter)) } - if err := registerViews(); err != nil { + if err = registerViews(); err != nil { return nil, errors.E(op, err) } @@ -89,7 +89,7 @@ func registerStatsStackDriverExporter(projectID string) (func(), error) { return sd.Flush, nil } -// registerViews register stats which should be collected in Athens +// registerViews register stats which should be collected in Athens. func registerViews() error { const op errors.Op = "observ.registerViews" if err := view.Register( diff --git a/pkg/paths/decode.go b/pkg/paths/decode.go index e22eef98..ab3de74f 100644 --- a/pkg/paths/decode.go +++ b/pkg/paths/decode.go @@ -19,8 +19,9 @@ func DecodePath(encoding string) (path string, err error) { return path, nil } -// Ripped from cmd/go +// Ripped from cmd/go. func decodeString(encoding string) (string, bool) { + //nolint:prealloc var buf []byte bang := false diff --git a/pkg/paths/path.go b/pkg/paths/path.go index d8109384..20995654 100644 --- a/pkg/paths/path.go +++ b/pkg/paths/path.go @@ -9,7 +9,7 @@ import ( "github.com/gorilla/mux" ) -// GetModule gets the module from the path of a ?go-get=1 request +// GetModule gets the module from the path of a ?go-get=1 request. func GetModule(r *http.Request) (string, error) { const op errors.Op = "paths.GetModule" module := mux.Vars(r)["module"] @@ -19,7 +19,7 @@ func GetModule(r *http.Request) (string, error) { return DecodePath(module) } -// GetVersion gets the version from the path of a ?go-get=1 request +// GetVersion gets the version from the path of a ?go-get=1 request. func GetVersion(r *http.Request) (string, error) { const op errors.Op = "paths.GetVersion" @@ -31,13 +31,13 @@ func GetVersion(r *http.Request) (string, error) { } // AllPathParams holds the module and version in the path of a ?go-get=1 -// request +// request. type AllPathParams struct { Module string `json:"module"` Version string `json:"version"` } -// GetAllParams fetches the path params from r and returns them +// GetAllParams fetches the path params from r and returns them. func GetAllParams(r *http.Request) (*AllPathParams, error) { const op errors.Op = "paths.GetAllParams" mod, err := GetModule(r) @@ -54,7 +54,7 @@ func GetAllParams(r *http.Request) (*AllPathParams, error) { } // MatchesPattern reports whether the path prefix of target matches -// pattern (as defined by path.Match) +// pattern (as defined by path.Match). // // This tries to keep the same behavior as GOPRIVATE/GONOPROXY/GONOSUMDB, // and is adopted from: diff --git a/pkg/requestid/requestid.go b/pkg/requestid/requestid.go index fc3a8b92..b92063fc 100644 --- a/pkg/requestid/requestid.go +++ b/pkg/requestid/requestid.go @@ -3,18 +3,18 @@ package requestid import "context" // HeaderKey is the header key that athens uses -// to pass request ids into logs and outbound requests +// to pass request ids into logs and outbound requests. const HeaderKey = "Athens-Request-ID" type key struct{} -// SetInContext sets the given requestID into the context +// SetInContext sets the given requestID into the context. func SetInContext(ctx context.Context, id string) context.Context { return context.WithValue(ctx, key{}, id) } // FromContext returns a requestID from the context or an empty -// string if not found +// string if not found. func FromContext(ctx context.Context) string { id, _ := ctx.Value(key{}).(string) return id diff --git a/pkg/stash/stasher.go b/pkg/stash/stasher.go index d0d79a9a..68365cf5 100644 --- a/pkg/stash/stasher.go +++ b/pkg/stash/stasher.go @@ -19,7 +19,7 @@ import ( // what was requested, this is helpful if what was requested // was a descriptive version such as a branch name or a full commit sha. type Stasher interface { - Stash(ctx context.Context, mod string, ver string) (string, error) + Stash(ctx context.Context, mod, ver string) (string, error) } // Wrapper helps extend the main stasher's functionality with addons. @@ -46,7 +46,7 @@ type stasher struct { func (s *stasher) Stash(ctx context.Context, mod, ver string) (string, error) { const op errors.Op = "stasher.Stash" - _, span := observ.StartSpan(ctx, op.String()) + ctx, span := observ.StartSpan(ctx, op.String()) defer span.End() log.EntryFromContext(ctx).Debugf("saving %s@%s to storage...", mod, ver) @@ -58,7 +58,7 @@ func (s *stasher) Stash(ctx context.Context, mod, ver string) (string, error) { if err != nil { return "", errors.E(op, err) } - defer v.Zip.Close() + defer func() { _ = v.Zip.Close() }() if v.Semver != ver { exists, err := s.checker.Exists(ctx, mod, v.Semver) if err != nil { diff --git a/pkg/stash/with_azureblob.go b/pkg/stash/with_azureblob.go index fd4bd0a8..ccebda68 100644 --- a/pkg/stash/with_azureblob.go +++ b/pkg/stash/with_azureblob.go @@ -132,8 +132,8 @@ func (s *azblobLock) acquireLease(ctx context.Context, blobURL azblob.BlockBlobU _, err := blobURL.Upload(tctx, bytes.NewReader([]byte{1}), azblob.BlobHTTPHeaders{}, nil, azblob.BlobAccessConditions{}) if err != nil { // if the blob is already leased we will get http.StatusPreconditionFailed while writing to that blob - stgErr, ok := err.(azblob.StorageError) - if !ok || stgErr.Response().StatusCode != http.StatusPreconditionFailed { + var stgErr azblob.StorageError + if !errors.AsErr(err, &stgErr) || stgErr.Response().StatusCode != http.StatusPreconditionFailed { return "", errors.E(op, err) } } @@ -147,7 +147,8 @@ func (s *azblobLock) acquireLease(ctx context.Context, blobURL azblob.BlockBlobU res, err := blobURL.AcquireLease(tctx, leaseID.String(), 15, azblob.ModifiedAccessConditions{}) if err != nil { // if the blob is already leased we will get http.StatusConflict - wait and try again - if stgErr, ok := err.(azblob.StorageError); ok && stgErr.Response().StatusCode == http.StatusConflict { + var stgErr azblob.StorageError + if ok := errors.AsErr(err, &stgErr); ok && stgErr.Response().StatusCode == http.StatusConflict { select { case <-time.After(1 * time.Second): continue diff --git a/pkg/stash/with_etcd.go b/pkg/stash/with_etcd.go index 7940e9bf..1cd8d2f9 100644 --- a/pkg/stash/with_etcd.go +++ b/pkg/stash/with_etcd.go @@ -8,7 +8,7 @@ import ( "github.com/gomods/athens/pkg/errors" "github.com/gomods/athens/pkg/observ" "github.com/gomods/athens/pkg/storage" - "go.etcd.io/etcd/client/v3" + clientv3 "go.etcd.io/etcd/client/v3" "go.etcd.io/etcd/client/v3/concurrency" "golang.org/x/sync/errgroup" ) diff --git a/pkg/stash/with_redis.go b/pkg/stash/with_redis.go index 35bf7936..64e8d1de 100644 --- a/pkg/stash/with_redis.go +++ b/pkg/stash/with_redis.go @@ -13,14 +13,14 @@ import ( "github.com/gomods/athens/pkg/storage" ) -// RedisLogger mirrors github.com/go-redis/redis/v8/internal.Logging +// RedisLogger mirrors github.com/go-redis/redis/v8/internal.Logging. type RedisLogger interface { Printf(ctx context.Context, format string, v ...interface{}) } // WithRedisLock returns a distributed singleflight // using a redis cluster. If it cannot connect, it will return an error. -func WithRedisLock(l RedisLogger, endpoint string, password string, checker storage.Checker, lockConfig *config.RedisLockConfig) (Wrapper, error) { +func WithRedisLock(l RedisLogger, endpoint, password string, checker storage.Checker, lockConfig *config.RedisLockConfig) (Wrapper, error) { redis.SetLogger(l) const op errors.Op = "stash.WithRedisLock" diff --git a/pkg/stash/with_redis_sentinel.go b/pkg/stash/with_redis_sentinel.go index 3eb4aa59..bf890b20 100644 --- a/pkg/stash/with_redis_sentinel.go +++ b/pkg/stash/with_redis_sentinel.go @@ -10,7 +10,7 @@ import ( ) // WithRedisSentinelLock returns a distributed singleflight -// with a redis cluster that utilizes sentinel for quorum and failover +// with a redis cluster that utilizes sentinel for quorum and failover. func WithRedisSentinelLock(l RedisLogger, endpoints []string, master, password string, checker storage.Checker, lockConfig *config.RedisLockConfig) (Wrapper, error) { redis.SetLogger(l) diff --git a/pkg/storage/azureblob/azureblob.go b/pkg/storage/azureblob/azureblob.go index 916623e4..53712d5b 100644 --- a/pkg/storage/azureblob/azureblob.go +++ b/pkg/storage/azureblob/azureblob.go @@ -15,14 +15,6 @@ import ( "github.com/gomods/athens/pkg/storage" ) -type client interface { - UploadWithContext(ctx context.Context, path, contentType string, content io.Reader) error - BlobExists(ctx context.Context, path string) (bool, error) - ReadBlob(ctx context.Context, path string) (io.ReadCloser, error) - ListBlobs(ctx context.Context, prefix string) ([]string, error) - DeleteBlob(ctx context.Context, path string) error -} - type azureBlobStoreClient struct { containerURL *azblob.ContainerURL } @@ -45,13 +37,13 @@ func newBlobStoreClient(accountURL *url.URL, accountName, accountKey, containerN } // Storage implements (github.com/gomods/athens/pkg/storage).Saver and -// also provides a function to fetch the location of a module +// also provides a function to fetch the location of a module. type Storage struct { client *azureBlobStoreClient timeout time.Duration } -// New creates a new azure blobs storage +// New creates a new azure blobs storage. func New(conf *config.AzureBlobConfig, timeout time.Duration) (*Storage, error) { const op errors.Op = "azureblob.New" u, err := url.Parse(fmt.Sprintf("https://%s.blob.core.windows.net", conf.AccountName)) @@ -65,7 +57,7 @@ func New(conf *config.AzureBlobConfig, timeout time.Duration) (*Storage, error) return &Storage{client: cl, timeout: timeout}, nil } -// BlobExists checks if a particular blob exists in the container +// BlobExists checks if a particular blob exists in the container. func (c *azureBlobStoreClient) BlobExists(ctx context.Context, path string) (bool, error) { const op errors.Op = "azureblob.BlobExists" // TODO: Any better way of doing this ? @@ -73,10 +65,8 @@ func (c *azureBlobStoreClient) BlobExists(ctx context.Context, path string) (boo _, err := blobURL.GetProperties(ctx, azblob.BlobAccessConditions{}) if err != nil { var serr azblob.StorageError - var ok bool - - if serr, ok = err.(azblob.StorageError); !ok { - return false, errors.E(op, fmt.Errorf("Error in casting to azure error type %v", err)) + if !errors.AsErr(err, &serr) { + return false, errors.E(op, fmt.Errorf("error in casting to azure error type %w", err)) } if serr.Response().StatusCode == http.StatusNotFound { return false, nil @@ -85,10 +75,9 @@ func (c *azureBlobStoreClient) BlobExists(ctx context.Context, path string) (boo return false, errors.E(op, err) } return true, nil - } -// ReadBlob returns a storage.SizeReadCloser for the contents of a blob +// ReadBlob returns a storage.SizeReadCloser for the contents of a blob. func (c *azureBlobStoreClient) ReadBlob(ctx context.Context, path string) (storage.SizeReadCloser, error) { const op errors.Op = "azureblob.ReadBlob" blobURL := c.containerURL.NewBlockBlobURL(path) @@ -101,7 +90,7 @@ func (c *azureBlobStoreClient) ReadBlob(ctx context.Context, path string) (stora return storage.NewSizer(rc, size), nil } -// ListBlobs will list all blobs which has the given prefix +// ListBlobs will list all blobs which has the given prefix. func (c *azureBlobStoreClient) ListBlobs(ctx context.Context, prefix string) ([]string, error) { const op errors.Op = "azureblob.ListBlobs" var blobs []string @@ -122,7 +111,7 @@ func (c *azureBlobStoreClient) ListBlobs(ctx context.Context, prefix string) ([] return blobs, nil } -// DeleteBlob deletes the blob with the given path +// DeleteBlob deletes the blob with the given path. func (c *azureBlobStoreClient) DeleteBlob(ctx context.Context, path string) error { const op errors.Op = "azureblob.DeleteBlob" blobURL := c.containerURL.NewBlockBlobURL(path) @@ -133,7 +122,7 @@ func (c *azureBlobStoreClient) DeleteBlob(ctx context.Context, path string) erro return nil } -// UploadWithContext uploads a blob to the container +// UploadWithContext uploads a blob to the container. func (c *azureBlobStoreClient) UploadWithContext(ctx context.Context, path, contentType string, content io.Reader) error { const op errors.Op = "azureblob.UploadWithContext" ctx, span := observ.StartSpan(ctx, op.String()) diff --git a/pkg/storage/azureblob/cataloger.go b/pkg/storage/azureblob/cataloger.go index d7906936..4bba622d 100644 --- a/pkg/storage/azureblob/cataloger.go +++ b/pkg/storage/azureblob/cataloger.go @@ -12,8 +12,8 @@ import ( "github.com/gomods/athens/pkg/paths" ) -// Catalog implements the (./pkg/storage).Catalog interface -// It returns a list of versions, if any, for a given module +// Catalog implements the (./pkg/storage).Catalog interface. +// It returns a list of versions, if any, for a given module. func (s *Storage) Catalog(ctx context.Context, token string, pageSize int) ([]paths.AllPathParams, string, error) { const op errors.Op = "azblob.Catalog" ctx, span := observ.StartSpan(ctx, op.String()) diff --git a/pkg/storage/azureblob/checker.go b/pkg/storage/azureblob/checker.go index 93388588..99f1056e 100644 --- a/pkg/storage/azureblob/checker.go +++ b/pkg/storage/azureblob/checker.go @@ -9,8 +9,8 @@ import ( ) // Exists implements the (./pkg/storage).Checker interface -// returning true if the module at version exists in storage -func (s *Storage) Exists(ctx context.Context, module string, version string) (bool, error) { +// returning true if the module at version exists in storage. +func (s *Storage) Exists(ctx context.Context, module, version string) (bool, error) { const op errors.Op = "azureblob.Exists" ctx, span := observ.StartSpan(ctx, op.String()) defer span.End() diff --git a/pkg/storage/azureblob/deleter.go b/pkg/storage/azureblob/deleter.go index 0907a1cc..bfdd1c0b 100644 --- a/pkg/storage/azureblob/deleter.go +++ b/pkg/storage/azureblob/deleter.go @@ -11,7 +11,7 @@ import ( // Delete implements the (./pkg/storage).Deleter interface and // removes a version of a module from storage. Returning ErrNotFound // if the version does not exist. -func (s *Storage) Delete(ctx context.Context, module string, version string) error { +func (s *Storage) Delete(ctx context.Context, module, version string) error { const op errors.Op = "azureblob.Delete" ctx, span := observ.StartSpan(ctx, op.String()) defer span.End() diff --git a/pkg/storage/azureblob/getter.go b/pkg/storage/azureblob/getter.go index 1a8675a3..77aeb1a2 100644 --- a/pkg/storage/azureblob/getter.go +++ b/pkg/storage/azureblob/getter.go @@ -11,8 +11,8 @@ import ( "github.com/gomods/athens/pkg/storage" ) -// Info implements the (./pkg/storage).Getter interface -func (s *Storage) Info(ctx context.Context, module string, version string) ([]byte, error) { +// Info implements the (./pkg/storage).Getter interface. +func (s *Storage) Info(ctx context.Context, module, version string) ([]byte, error) { const op errors.Op = "azureblob.Info" ctx, span := observ.StartSpan(ctx, op.String()) defer span.End() @@ -43,8 +43,8 @@ func (s *Storage) Info(ctx context.Context, module string, version string) ([]by return infoBytes, nil } -// GoMod implements the (./pkg/storage).Getter interface -func (s *Storage) GoMod(ctx context.Context, module string, version string) ([]byte, error) { +// GoMod implements the (./pkg/storage).Getter interface. +func (s *Storage) GoMod(ctx context.Context, module, version string) ([]byte, error) { const op errors.Op = "azureblob.GoMod" ctx, span := observ.StartSpan(ctx, op.String()) defer span.End() @@ -64,7 +64,7 @@ func (s *Storage) GoMod(ctx context.Context, module string, version string) ([]b modBytes, err := io.ReadAll(modReader) if err != nil { - return nil, errors.E(op, fmt.Errorf("could not get new reader for mod file: %s", err), errors.M(module), errors.V(version)) + return nil, errors.E(op, fmt.Errorf("could not get new reader for mod file: %w", err), errors.M(module), errors.V(version)) } err = modReader.Close() @@ -75,8 +75,8 @@ func (s *Storage) GoMod(ctx context.Context, module string, version string) ([]b return modBytes, nil } -// Zip implements the (./pkg/storage).Getter interface -func (s *Storage) Zip(ctx context.Context, module string, version string) (storage.SizeReadCloser, error) { +// Zip implements the (./pkg/storage).Getter interface. +func (s *Storage) Zip(ctx context.Context, module, version string) (storage.SizeReadCloser, error) { const op errors.Op = "azureblob.Zip" ctx, span := observ.StartSpan(ctx, op.String()) defer span.End() diff --git a/pkg/storage/azureblob/lister.go b/pkg/storage/azureblob/lister.go index 9e8c5225..62425445 100644 --- a/pkg/storage/azureblob/lister.go +++ b/pkg/storage/azureblob/lister.go @@ -8,8 +8,8 @@ import ( "github.com/gomods/athens/pkg/observ" ) -// List implements the (./pkg/storage).Lister interface -// It returns a list of versions, if any, for a given module +// List implements the (./pkg/storage).Lister interface. +// It returns a list of versions, if any, for a given module. func (s *Storage) List(ctx context.Context, module string) ([]string, error) { const op errors.Op = "azureblob.List" ctx, span := observ.StartSpan(ctx, op.String()) diff --git a/pkg/storage/backend.go b/pkg/storage/backend.go index 7cb5ef4b..97c27a53 100644 --- a/pkg/storage/backend.go +++ b/pkg/storage/backend.go @@ -1,6 +1,6 @@ package storage -// Backend is a complete storage backend (i.e. file system, database) implementation - a lister, reader and saver +// Backend is a complete storage backend (i.e. file system, database) implementation - a lister, reader and saver. type Backend interface { Lister Getter diff --git a/pkg/storage/cataloger.go b/pkg/storage/cataloger.go index b1bbd163..793f5c5c 100644 --- a/pkg/storage/cataloger.go +++ b/pkg/storage/cataloger.go @@ -6,7 +6,7 @@ import ( "github.com/gomods/athens/pkg/paths" ) -// Cataloger is the interface that lists all the modules and version contained in the storage +// Cataloger is the interface that lists all the modules and version contained in the storage. type Cataloger interface { // Catalog gets all the modules / versions. Catalog(ctx context.Context, token string, pageSize int) ([]paths.AllPathParams, string, error) diff --git a/pkg/storage/checker.go b/pkg/storage/checker.go index 6a64c7d5..8f017b19 100644 --- a/pkg/storage/checker.go +++ b/pkg/storage/checker.go @@ -6,14 +6,14 @@ import ( "github.com/gomods/athens/pkg/errors" ) -// Checker is the interface that checks if the version of the module exists +// Checker is the interface that checks if the version of the module exists. type Checker interface { // Exists checks whether or not module in specified version is present - // in the backing storage + // in the backing storage. Exists(ctx context.Context, module, version string) (bool, error) } -// WithChecker wraps the backend with a Checker implementaiton +// WithChecker wraps the backend with a Checker implementation. func WithChecker(strg Backend) Checker { if checker, ok := strg.(Checker); ok { return checker diff --git a/pkg/storage/compliance/tests.go b/pkg/storage/compliance/tests.go index 47fcf521..32f60113 100644 --- a/pkg/storage/compliance/tests.go +++ b/pkg/storage/compliance/tests.go @@ -6,7 +6,6 @@ import ( "fmt" "io" "math/rand" - "sort" "testing" "github.com/gomods/athens/pkg/errors" @@ -26,7 +25,6 @@ func RunTests(t *testing.T, b storage.Backend, clearBackend func() error) { testGet(t, b) testExists(t, b) testShouldNotExist(t, b) - // testCatalog(t, b) } // testNotFound ensures that a storage Backend @@ -209,49 +207,6 @@ func testDelete(t *testing.T, b storage.Backend) { require.Equal(t, false, exists) } -func testCatalog(t *testing.T, b storage.Backend) { - cs, ok := b.(storage.Cataloger) - if !ok { - t.Skip() - } - ctx := context.Background() - - mock := getMockModule() - zipBts, _ := io.ReadAll(mock.Zip) - modname := "github.com/gomods/testCatalogModule" - for i := 0; i < 6; i++ { - ver := fmt.Sprintf("v1.2.%04d", i) - b.Save(ctx, modname, ver, mock.Mod, bytes.NewReader(zipBts), mock.Info) - - defer b.Delete(ctx, modname, ver) - } - - allres, next, err := cs.Catalog(ctx, "", 5) - - require.NoError(t, err) - require.Equal(t, 5, len(allres)) - - res, next, err := cs.Catalog(ctx, next, 50) - allres = append(allres, res...) - require.NoError(t, err) - require.Equal(t, 1, len(res)) - require.Equal(t, "", next) - - sort.Slice(allres, func(i, j int) bool { - if allres[i].Module == allres[j].Module { - return allres[i].Version < allres[j].Version - } - return allres[i].Module < allres[j].Module - }) - require.Equal(t, modname, allres[0].Module) - require.Equal(t, "v1.2.0000", allres[0].Version) - require.Equal(t, "v1.2.0004", allres[4].Version) - - for i := 1; i < len(allres); i++ { - require.NotEqual(t, allres[i].Version, allres[i-1].Version) - } -} - func getMockModule() *storage.Version { return &storage.Version{ Info: []byte("123"), diff --git a/pkg/storage/deleter.go b/pkg/storage/deleter.go index 367c65b7..b093af00 100644 --- a/pkg/storage/deleter.go +++ b/pkg/storage/deleter.go @@ -2,7 +2,7 @@ package storage import "context" -// Deleter deletes module metadata and its source from underlying storage +// Deleter deletes module metadata and its source from underlying storage. type Deleter interface { // Delete must return ErrNotFound if the module/version are not // found. diff --git a/pkg/storage/external/client.go b/pkg/storage/external/client.go index 8a024ab2..b33e5d21 100644 --- a/pkg/storage/external/client.go +++ b/pkg/storage/external/client.go @@ -20,7 +20,7 @@ type service struct { c *http.Client } -// NewClient returns an external storage client +// NewClient returns an external storage client. func NewClient(url string, c *http.Client) storage.Backend { if c == nil { c = &http.Client{} @@ -93,7 +93,7 @@ func (s *service) Save(ctx context.Context, mod, ver string, modFile []byte, zip mw := multipart.NewWriter(pw) go func() { err := upload(mw, modFile, info, zip) - pw.CloseWithError(err) + _ = pw.CloseWithError(err) }() req, err := http.NewRequestWithContext(ctx, http.MethodPost, url, pr) if err != nil { @@ -104,8 +104,8 @@ func (s *service) Save(ctx context.Context, mod, ver string, modFile []byte, zip if err != nil { return errors.E(op, err) } - defer resp.Body.Close() - if resp.StatusCode != 200 { + defer func() { _ = resp.Body.Close() }() + if resp.StatusCode != http.StatusOK { bts, _ := io.ReadAll(resp.Body) return errors.E(op, fmt.Errorf("unexpected status code: %v - body: %s", resp.StatusCode, bts), resp.StatusCode) } @@ -118,35 +118,35 @@ func (s *service) Delete(ctx context.Context, mod, ver string) error { if err != nil { return errors.E(op, err) } - defer body.Close() + defer func() { _ = body.Close() }() return nil } func upload(mw *multipart.Writer, mod, info []byte, zip io.Reader) error { - defer mw.Close() + defer func() { _ = mw.Close() }() infoW, err := mw.CreateFormFile("mod.info", "mod.info") if err != nil { - return fmt.Errorf("error creating info file: %v", err) + return fmt.Errorf("error creating info file: %w", err) } _, err = infoW.Write(info) if err != nil { - return fmt.Errorf("error writing info file: %v", err) + return fmt.Errorf("error writing info file: %w", err) } modW, err := mw.CreateFormFile("mod.mod", "mod.mod") if err != nil { - return fmt.Errorf("error creating mod file: %v", err) + return fmt.Errorf("error creating mod file: %w", err) } _, err = modW.Write(mod) if err != nil { - return fmt.Errorf("error writing mod file: %v", err) + return fmt.Errorf("error writing mod file: %w", err) } zipW, err := mw.CreateFormFile("mod.zip", "mod.zip") if err != nil { - return fmt.Errorf("error creating zip file: %v", err) + return fmt.Errorf("error creating zip file: %w", err) } _, err = io.Copy(zipW, zip) if err != nil { - return fmt.Errorf("error writing zip file: %v", err) + return fmt.Errorf("error writing zip file: %w", err) } return nil } @@ -174,9 +174,9 @@ func (s *service) doRequest(ctx context.Context, method, mod, ver, ext string) ( if err != nil { return nil, 0, errors.E(op, err) } - if resp.StatusCode != 200 { + if resp.StatusCode != http.StatusOK { body, _ := io.ReadAll(resp.Body) - resp.Body.Close() + _ = resp.Body.Close() return nil, 0, errors.E(op, fmt.Errorf("none 200 status code: %v - body: %s", resp.StatusCode, body), resp.StatusCode) } var size int64 diff --git a/pkg/storage/external/server.go b/pkg/storage/external/server.go index 4b9b76b6..b4a4b8ca 100644 --- a/pkg/storage/external/server.go +++ b/pkg/storage/external/server.go @@ -17,7 +17,7 @@ import ( // NewServer takes a storage.Backend implementation of your // choice, and returns a new http.Handler that Athens can -// reach out to for storage operations +// reach out to for storage operations. func NewServer(strg storage.Backend) http.Handler { r := mux.NewRouter() r.HandleFunc(download.PathList, func(w http.ResponseWriter, r *http.Request) { @@ -27,12 +27,12 @@ func NewServer(strg storage.Backend) http.Handler { http.Error(w, err.Error(), errors.Kind(err)) return } - fmt.Fprintf(w, "%s", strings.Join(list, "\n")) + _, _ = fmt.Fprintf(w, "%s", strings.Join(list, "\n")) }).Methods(http.MethodGet) r.HandleFunc(download.PathVersionInfo, func(w http.ResponseWriter, r *http.Request) { params, err := paths.GetAllParams(r) if err != nil { - http.Error(w, err.Error(), 400) + http.Error(w, err.Error(), http.StatusBadRequest) return } info, err := strg.Info(r.Context(), params.Module, params.Version) @@ -40,12 +40,12 @@ func NewServer(strg storage.Backend) http.Handler { http.Error(w, err.Error(), errors.Kind(err)) return } - w.Write(info) + _, _ = w.Write(info) }).Methods(http.MethodGet) r.HandleFunc(download.PathVersionModule, func(w http.ResponseWriter, r *http.Request) { params, err := paths.GetAllParams(r) if err != nil { - http.Error(w, err.Error(), 400) + http.Error(w, err.Error(), http.StatusBadRequest) return } mod, err := strg.GoMod(r.Context(), params.Module, params.Version) @@ -53,12 +53,12 @@ func NewServer(strg storage.Backend) http.Handler { http.Error(w, err.Error(), errors.Kind(err)) return } - w.Write(mod) + _, _ = w.Write(mod) }).Methods(http.MethodGet) r.HandleFunc(download.PathVersionZip, func(w http.ResponseWriter, r *http.Request) { params, err := paths.GetAllParams(r) if err != nil { - http.Error(w, err.Error(), 400) + http.Error(w, err.Error(), http.StatusBadRequest) return } zip, err := strg.Zip(r.Context(), params.Module, params.Version) @@ -66,52 +66,52 @@ func NewServer(strg storage.Backend) http.Handler { http.Error(w, err.Error(), errors.Kind(err)) return } - defer zip.Close() + defer func() { _ = zip.Close() }() w.Header().Set("Content-Length", strconv.FormatInt(zip.Size(), 10)) - io.Copy(w, zip) + _, _ = io.Copy(w, zip) }).Methods(http.MethodGet) r.HandleFunc("/{module:.+}/@v/{version}.save", func(w http.ResponseWriter, r *http.Request) { params, err := paths.GetAllParams(r) if err != nil { - http.Error(w, err.Error(), 400) + http.Error(w, err.Error(), http.StatusBadRequest) return } err = r.ParseMultipartForm(zip.MaxZipFile + zip.MaxGoMod) if err != nil { - http.Error(w, err.Error(), 400) + http.Error(w, err.Error(), http.StatusBadRequest) return } infoFile, _, err := r.FormFile("mod.info") if err != nil { - http.Error(w, err.Error(), 400) + http.Error(w, err.Error(), http.StatusBadRequest) return } - defer infoFile.Close() + defer func() { _ = infoFile.Close() }() info, err := io.ReadAll(infoFile) if err != nil { - http.Error(w, err.Error(), 400) + http.Error(w, err.Error(), http.StatusBadRequest) return } modReader, _, err := r.FormFile("mod.mod") if err != nil { - http.Error(w, err.Error(), 400) + http.Error(w, err.Error(), http.StatusBadRequest) return } - defer modReader.Close() + defer func() { _ = modReader.Close() }() modFile, err := io.ReadAll(modReader) if err != nil { - http.Error(w, err.Error(), 400) + http.Error(w, err.Error(), http.StatusBadRequest) return } modZ, _, err := r.FormFile("mod.zip") if err != nil { - http.Error(w, err.Error(), 400) + http.Error(w, err.Error(), http.StatusBadRequest) return } - defer modZ.Close() + defer func() { _ = modZ.Close() }() err = strg.Save(r.Context(), params.Module, params.Version, modFile, modZ, info) if err != nil { - http.Error(w, err.Error(), 400) + http.Error(w, err.Error(), http.StatusBadRequest) return } }).Methods(http.MethodPost) @@ -119,7 +119,7 @@ func NewServer(strg storage.Backend) http.Handler { r.HandleFunc("/{module:.+}/@v/{version}.delete", func(w http.ResponseWriter, r *http.Request) { params, err := paths.GetAllParams(r) if err != nil { - http.Error(w, err.Error(), 400) + http.Error(w, err.Error(), http.StatusBadRequest) return } err = strg.Delete(r.Context(), params.Module, params.Version) diff --git a/pkg/storage/fs/cataloger.go b/pkg/storage/fs/cataloger.go index 368f73cf..d310c483 100644 --- a/pkg/storage/fs/cataloger.go +++ b/pkg/storage/fs/cataloger.go @@ -15,11 +15,11 @@ import ( const tokenSeparator = "|" -// Catalog implements the (./pkg/storage).Cataloger interface -// It returns a list of modules and versions contained in the storage +// Catalog implements the (./pkg/storage).Cataloger interface. +// It returns a list of modules and versions contained in the storage. func (s *storageImpl) Catalog(ctx context.Context, token string, pageSize int) ([]paths.AllPathParams, string, error) { const op errors.Op = "fs.Catalog" - ctx, span := observ.StartSpan(ctx, op.String()) + _, span := observ.StartSpan(ctx, op.String()) defer span.End() fromModule, fromVersion, err := modVerFromToken(token) @@ -41,7 +41,7 @@ func (s *storageImpl) Catalog(ctx context.Context, token string, pageSize int) ( m, version := filepath.Split(modVer) module := filepath.Clean(m) - module = strings.Replace(module, string(os.PathSeparator), "/", -1) + module = strings.ReplaceAll(module, string(os.PathSeparator), "/") if fromModule != "" && module < fromModule { // it is ok to land on the same module return nil @@ -60,7 +60,7 @@ func (s *storageImpl) Catalog(ctx context.Context, token string, pageSize int) ( } return nil }) - if err != nil && err != io.EOF { + if err != nil && !errors.IsErr(err, io.EOF) { return nil, "", errors.E(op, err, errors.KindUnexpected) } diff --git a/pkg/storage/fs/checker.go b/pkg/storage/fs/checker.go index 9d7a3987..b32dc488 100644 --- a/pkg/storage/fs/checker.go +++ b/pkg/storage/fs/checker.go @@ -9,14 +9,13 @@ import ( "github.com/spf13/afero" ) -func (v *storageImpl) Exists(ctx context.Context, module, version string) (bool, error) { +func (s *storageImpl) Exists(ctx context.Context, module, version string) (bool, error) { const op errors.Op = "fs.Exists" - ctx, span := observ.StartSpan(ctx, op.String()) + _, span := observ.StartSpan(ctx, op.String()) defer span.End() - versionedPath := v.versionLocation(module, version) - - files, err := afero.ReadDir(v.filesystem, versionedPath) + versionedPath := s.versionLocation(module, version) + files, err := afero.ReadDir(s.filesystem, versionedPath) if err != nil { if os.IsNotExist(err) { return false, nil diff --git a/pkg/storage/fs/deleter.go b/pkg/storage/fs/deleter.go index 02128258..420ccc04 100644 --- a/pkg/storage/fs/deleter.go +++ b/pkg/storage/fs/deleter.go @@ -8,17 +8,17 @@ import ( ) // Delete removes a specific version of a module. -func (v *storageImpl) Delete(ctx context.Context, module, version string) error { +func (s *storageImpl) Delete(ctx context.Context, module, version string) error { const op errors.Op = "fs.Delete" ctx, span := observ.StartSpan(ctx, op.String()) defer span.End() - versionedPath := v.versionLocation(module, version) - exists, err := v.Exists(ctx, module, version) + versionedPath := s.versionLocation(module, version) + exists, err := s.Exists(ctx, module, version) if err != nil { return errors.E(op, err, errors.M(module), errors.V(version)) } if !exists { return errors.E(op, errors.M(module), errors.V(version), errors.KindNotFound) } - return v.filesystem.RemoveAll(versionedPath) + return s.filesystem.RemoveAll(versionedPath) } diff --git a/pkg/storage/fs/fs.go b/pkg/storage/fs/fs.go index bfc3bbf8..8e0dd41a 100644 --- a/pkg/storage/fs/fs.go +++ b/pkg/storage/fs/fs.go @@ -21,17 +21,16 @@ func (s *storageImpl) moduleLocation(module string) string { func (s *storageImpl) versionLocation(module, version string) string { return filepath.Join(s.moduleLocation(module), version) - } // NewStorage returns a new ListerSaver implementation that stores -// everything under rootDir -// If the root directory does not exist an error is returned +// everything under rootDir. +// If the root directory does not exist an error is returned. func NewStorage(rootDir string, filesystem afero.Fs) (storage.Backend, error) { const op errors.Op = "fs.NewStorage" exists, err := afero.Exists(filesystem, rootDir) if err != nil { - return nil, errors.E(op, fmt.Errorf("could not check if root directory `%s` exists: %s", rootDir, err)) + return nil, errors.E(op, fmt.Errorf("could not check if root directory `%s` exists: %w", rootDir, err)) } if !exists { return nil, errors.E(op, fmt.Errorf("root directory `%s` does not exist", rootDir)) diff --git a/pkg/storage/fs/getter.go b/pkg/storage/fs/getter.go index 513e2ea8..e6f26e91 100644 --- a/pkg/storage/fs/getter.go +++ b/pkg/storage/fs/getter.go @@ -11,12 +11,12 @@ import ( "github.com/spf13/afero" ) -func (v *storageImpl) Info(ctx context.Context, module, version string) ([]byte, error) { +func (s *storageImpl) Info(ctx context.Context, module, version string) ([]byte, error) { const op errors.Op = "fs.Info" - ctx, span := observ.StartSpan(ctx, op.String()) + _, span := observ.StartSpan(ctx, op.String()) defer span.End() - versionedPath := v.versionLocation(module, version) - info, err := afero.ReadFile(v.filesystem, filepath.Join(versionedPath, version+".info")) + versionedPath := s.versionLocation(module, version) + info, err := afero.ReadFile(s.filesystem, filepath.Join(versionedPath, version+".info")) if err != nil { return nil, errors.E(op, errors.M(module), errors.V(version), errors.KindNotFound) } @@ -24,12 +24,12 @@ func (v *storageImpl) Info(ctx context.Context, module, version string) ([]byte, return info, nil } -func (v *storageImpl) GoMod(ctx context.Context, module, version string) ([]byte, error) { +func (s *storageImpl) GoMod(ctx context.Context, module, version string) ([]byte, error) { const op errors.Op = "fs.GoMod" - ctx, span := observ.StartSpan(ctx, op.String()) + _, span := observ.StartSpan(ctx, op.String()) defer span.End() - versionedPath := v.versionLocation(module, version) - mod, err := afero.ReadFile(v.filesystem, filepath.Join(versionedPath, "go.mod")) + versionedPath := s.versionLocation(module, version) + mod, err := afero.ReadFile(s.filesystem, filepath.Join(versionedPath, "go.mod")) if err != nil { return nil, errors.E(op, errors.M(module), errors.V(version), errors.KindNotFound) } @@ -37,13 +37,13 @@ func (v *storageImpl) GoMod(ctx context.Context, module, version string) ([]byte return mod, nil } -func (v *storageImpl) Zip(ctx context.Context, module, version string) (storage.SizeReadCloser, error) { +func (s *storageImpl) Zip(ctx context.Context, module, version string) (storage.SizeReadCloser, error) { const op errors.Op = "fs.Zip" - ctx, span := observ.StartSpan(ctx, op.String()) + _, span := observ.StartSpan(ctx, op.String()) defer span.End() - versionedPath := v.versionLocation(module, version) + versionedPath := s.versionLocation(module, version) - src, err := v.filesystem.OpenFile(filepath.Join(versionedPath, "source.zip"), os.O_RDONLY, 0666) + src, err := s.filesystem.OpenFile(filepath.Join(versionedPath, "source.zip"), os.O_RDONLY, 0o666) if err != nil { return nil, errors.E(op, errors.M(module), errors.V(version), errors.KindNotFound) } @@ -53,15 +53,3 @@ func (v *storageImpl) Zip(ctx context.Context, module, version string) (storage. } return storage.NewSizer(src, fi.Size()), nil } - -func (v *storageImpl) ZipSize(ctx context.Context, module, version string) (int64, error) { - const op errors.Op = "fs.ZipFileSize" - ctx, span := observ.StartSpan(ctx, op.String()) - defer span.End() - versionedPath := v.versionLocation(module, version) - fi, err := v.filesystem.Stat(filepath.Join(versionedPath)) - if err != nil { - return 0, errors.E(op, err, errors.M(module), errors.V(version), errors.KindNotFound) - } - return fi.Size(), nil -} diff --git a/pkg/storage/fs/lister.go b/pkg/storage/fs/lister.go index 04458035..d4982a7f 100644 --- a/pkg/storage/fs/lister.go +++ b/pkg/storage/fs/lister.go @@ -11,12 +11,12 @@ import ( "golang.org/x/mod/semver" ) -func (l *storageImpl) List(ctx context.Context, module string) ([]string, error) { +func (s *storageImpl) List(ctx context.Context, module string) ([]string, error) { const op errors.Op = "fs.List" - ctx, span := observ.StartSpan(ctx, op.String()) + _, span := observ.StartSpan(ctx, op.String()) defer span.End() - loc := l.moduleLocation(module) - fileInfos, err := afero.ReadDir(l.filesystem, loc) + loc := s.moduleLocation(module) + fileInfos, err := afero.ReadDir(s.filesystem, loc) if err != nil { if os.IsNotExist(err) { return []string{}, nil diff --git a/pkg/storage/fs/saver.go b/pkg/storage/fs/saver.go index e146d807..bf826eee 100644 --- a/pkg/storage/fs/saver.go +++ b/pkg/storage/fs/saver.go @@ -13,37 +13,37 @@ import ( func (s *storageImpl) Save(ctx context.Context, module, version string, mod []byte, zip io.Reader, info []byte) error { const op errors.Op = "fs.Save" - ctx, span := observ.StartSpan(ctx, op.String()) + _, span := observ.StartSpan(ctx, op.String()) defer span.End() dir := s.versionLocation(module, version) - // NB: the process's umask is subtracted from the permissions below, - // so a umask of for example 0077 allows directories and files to be - // created with mode 0700 / 0600, i.e. not world- or group-readable + // NB: The process's umask is subtracted from the permissions below, + // so an umask of for example 0077 allows directories and files to be + // created with mode 0700 / 0600, i.e. not world- or group-readable. - // make the versioned directory to hold the go.mod and the zipfile - if err := s.filesystem.MkdirAll(dir, 0777); err != nil { + // Make the versioned directory to hold the go.mod and the zipfile. + if err := s.filesystem.MkdirAll(dir, 0o777); err != nil { return errors.E(op, err, errors.M(module), errors.V(version)) } - // write the go.mod file - if err := afero.WriteFile(s.filesystem, filepath.Join(dir, "go.mod"), mod, 0666); err != nil { + // Write the go.mod file. + if err := afero.WriteFile(s.filesystem, filepath.Join(dir, "go.mod"), mod, 0o666); err != nil { return errors.E(op, err, errors.M(module), errors.V(version)) } - // write the zipfile - f, err := s.filesystem.OpenFile(filepath.Join(dir, "source.zip"), os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0666) + // Write the zipfile. + f, err := s.filesystem.OpenFile(filepath.Join(dir, "source.zip"), os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0o666) if err != nil { return errors.E(op, err, errors.M(module), errors.V(version)) } - defer f.Close() + defer func() { _ = f.Close() }() _, err = io.Copy(f, zip) if err != nil { return errors.E(op, err, errors.M(module), errors.V(version)) } // write the info file - err = afero.WriteFile(s.filesystem, filepath.Join(dir, version+".info"), info, 0666) + err = afero.WriteFile(s.filesystem, filepath.Join(dir, version+".info"), info, 0o666) if err != nil { return errors.E(op, err) } diff --git a/pkg/storage/gcp/cataloger.go b/pkg/storage/gcp/cataloger.go index 7de52596..32868bd8 100644 --- a/pkg/storage/gcp/cataloger.go +++ b/pkg/storage/gcp/cataloger.go @@ -14,7 +14,7 @@ import ( ) // Catalog implements the (./pkg/storage).Catalog interface -// It returns a list of versions, if any, for a given module +// It returns a list of versions, if any, for a given module. func (s *Storage) Catalog(ctx context.Context, token string, pageSize int) ([]paths.AllPathParams, string, error) { const op errors.Op = "gcp.Catalog" ctx, span := observ.StartSpan(ctx, op.String()) diff --git a/pkg/storage/gcp/checker.go b/pkg/storage/gcp/checker.go index 2e8bf476..259a85ba 100644 --- a/pkg/storage/gcp/checker.go +++ b/pkg/storage/gcp/checker.go @@ -11,7 +11,7 @@ import ( ) // Exists implements the (./pkg/storage).Checker interface -// returning true if the module at version exists in storage +// returning true if the module at version exists in storage. func (s *Storage) Exists(ctx context.Context, module, version string) (bool, error) { const op errors.Op = "gcp.Exists" ctx, span := observ.StartSpan(ctx, op.String()) @@ -21,7 +21,7 @@ func (s *Storage) Exists(ctx context.Context, module, version string) (bool, err var count int for { attrs, err := it.Next() - if err == iterator.Done { + if errors.IsErr(err, iterator.Done) { break } if err != nil { diff --git a/pkg/storage/gcp/gcp.go b/pkg/storage/gcp/gcp.go index e7429845..a15d25d1 100644 --- a/pkg/storage/gcp/gcp.go +++ b/pkg/storage/gcp/gcp.go @@ -13,7 +13,7 @@ import ( "google.golang.org/api/option" ) -// Storage implements the (./pkg/storage).Backend interface +// Storage implements the (./pkg/storage).Backend interface. type Storage struct { bucket *storage.BucketHandle timeout time.Duration @@ -34,8 +34,8 @@ func New(ctx context.Context, gcpConf *config.GCPConfig, timeout time.Duration) return nil, errors.E(op, err) } - if _, err := s.bucket.Attrs(ctx); err != nil { - if err == storage.ErrBucketNotExist { + if _, err = s.bucket.Attrs(ctx); err != nil { + if errors.IsErr(err, storage.ErrBucketNotExist) { return nil, errors.E(op, "You must manually create a storage bucket for Athens, see https://cloud.google.com/storage/docs/creating-buckets#storage-create-bucket-console") } return nil, errors.E(op, err) @@ -48,21 +48,21 @@ func New(ctx context.Context, gcpConf *config.GCPConfig, timeout time.Duration) // this is so that the unit tests can use this to create their own short-lived buckets. func newClient(ctx context.Context, gcpConf *config.GCPConfig, timeout time.Duration) (*Storage, error) { const op errors.Op = "gcp.newClient" - opts := []option.ClientOption{} + var opts []option.ClientOption if gcpConf.JSONKey != "" { key, err := base64.StdEncoding.DecodeString(gcpConf.JSONKey) if err != nil { - return nil, errors.E(op, fmt.Errorf("could not decode base64 json key: %v", err)) + return nil, errors.E(op, fmt.Errorf("could not decode base64 json key: %w", err)) } creds, err := google.CredentialsFromJSON(ctx, key, storage.ScopeReadWrite) if err != nil { - return nil, errors.E(op, fmt.Errorf("could not get GCS credentials: %v", err)) + return nil, errors.E(op, fmt.Errorf("could not get GCS credentials: %w", err)) } opts = append(opts, option.WithCredentials(creds)) } s, err := storage.NewClient(ctx, opts...) if err != nil { - return nil, errors.E(op, fmt.Errorf("could not create new storage client: %s", err)) + return nil, errors.E(op, fmt.Errorf("could not create new storage client: %w", err)) } return &Storage{ diff --git a/pkg/storage/gcp/getter.go b/pkg/storage/gcp/getter.go index b9abd7ef..3edea951 100644 --- a/pkg/storage/gcp/getter.go +++ b/pkg/storage/gcp/getter.go @@ -12,7 +12,7 @@ import ( pkgstorage "github.com/gomods/athens/pkg/storage" ) -// Info implements Getter +// Info implements Getter. func (s *Storage) Info(ctx context.Context, module, version string) ([]byte, error) { const op errors.Op = "gcp.Info" ctx, span := observ.StartSpan(ctx, op.String()) @@ -22,14 +22,14 @@ func (s *Storage) Info(ctx context.Context, module, version string) ([]byte, err return nil, errors.E(op, err, getErrorKind(err), errors.M(module), errors.V(version)) } infoBytes, err := io.ReadAll(infoReader) - infoReader.Close() + _ = infoReader.Close() if err != nil { return nil, errors.E(op, err, errors.M(module), errors.V(version)) } return infoBytes, nil } -// GoMod implements Getter +// GoMod implements Getter. func (s *Storage) GoMod(ctx context.Context, module, version string) ([]byte, error) { const op errors.Op = "gcp.GoMod" ctx, span := observ.StartSpan(ctx, op.String()) @@ -39,15 +39,15 @@ func (s *Storage) GoMod(ctx context.Context, module, version string) ([]byte, er return nil, errors.E(op, err, getErrorKind(err), errors.M(module), errors.V(version)) } modBytes, err := io.ReadAll(modReader) - modReader.Close() + _ = modReader.Close() if err != nil { - return nil, errors.E(op, fmt.Errorf("could not get new reader for mod file: %s", err), errors.M(module), errors.V(version)) + return nil, errors.E(op, fmt.Errorf("could not get new reader for mod file: %w", err), errors.M(module), errors.V(version)) } return modBytes, nil } -// Zip implements Getter +// Zip implements Getter. func (s *Storage) Zip(ctx context.Context, module, version string) (pkgstorage.SizeReadCloser, error) { const op errors.Op = "gcp.Zip" ctx, span := observ.StartSpan(ctx, op.String()) @@ -56,11 +56,11 @@ func (s *Storage) Zip(ctx context.Context, module, version string) (pkgstorage.S if err != nil { return nil, errors.E(op, err, getErrorKind(err), errors.M(module), errors.V(version)) } - return pkgstorage.NewSizer(zipReader, zipReader.Size()), nil + return pkgstorage.NewSizer(zipReader, zipReader.Attrs.Size), nil } func getErrorKind(err error) int { - if err == storage.ErrObjectNotExist { + if errors.IsErr(err, storage.ErrObjectNotExist) { return errors.KindNotFound } return errors.KindUnexpected diff --git a/pkg/storage/gcp/lister.go b/pkg/storage/gcp/lister.go index fb681037..97f2437b 100644 --- a/pkg/storage/gcp/lister.go +++ b/pkg/storage/gcp/lister.go @@ -10,8 +10,8 @@ import ( "google.golang.org/api/iterator" ) -// List implements the (./pkg/storage).Lister interface -// It returns a list of versions, if any, for a given module +// List implements the (./pkg/storage).Lister interface. +// It returns a list of versions, if any, for a given module. func (s *Storage) List(ctx context.Context, module string) ([]string, error) { const op errors.Op = "gcp.List" ctx, span := observ.StartSpan(ctx, op.String()) @@ -22,7 +22,7 @@ func (s *Storage) List(ctx context.Context, module string) ([]string, error) { paths := []string{} for { attrs, err := it.Next() - if err == iterator.Done { + if errors.IsErr(err, iterator.Done) { break } if err != nil { diff --git a/pkg/storage/gcp/saver.go b/pkg/storage/gcp/saver.go index d66a559a..4298aaa1 100644 --- a/pkg/storage/gcp/saver.go +++ b/pkg/storage/gcp/saver.go @@ -53,15 +53,15 @@ func (s *Storage) upload(ctx context.Context, path string, stream io.Reader) err // Once we support private storage buckets this may need refactoring // unless there is a way to set the default perms in the project. if _, err := io.Copy(wc, stream); err != nil { - wc.Close() + _ = wc.Close() return err } err := wc.Close() if err != nil { kind := errors.KindBadRequest - apiErr, ok := err.(*googleapi.Error) - if ok && apiErr.Code == 412 { + apiErr := &googleapi.Error{} + if errors.AsErr(err, &apiErr) && apiErr.Code == 412 { kind = errors.KindAlreadyExists } return errors.E(op, err, kind) diff --git a/pkg/storage/getter.go b/pkg/storage/getter.go index d26eb38b..e04531b8 100644 --- a/pkg/storage/getter.go +++ b/pkg/storage/getter.go @@ -5,7 +5,7 @@ import ( "io" ) -// Getter gets module metadata and its source from underlying storage +// Getter gets module metadata and its source from underlying storage. type Getter interface { Info(ctx context.Context, module, vsn string) ([]byte, error) GoMod(ctx context.Context, module, vsn string) ([]byte, error) @@ -14,14 +14,14 @@ type Getter interface { // SizeReadCloser extends io.ReadCloser // with a Size() method that tells you the -// length of the io.ReadCloser if read in full +// length of the io.ReadCloser if read in full. type SizeReadCloser interface { io.ReadCloser Size() int64 } // NewSizer is a helper wrapper to return an implementation -// of ReadCloserSizer +// of ReadCloserSizer. func NewSizer(rc io.ReadCloser, size int64) SizeReadCloser { return &sizeReadCloser{rc, size} } diff --git a/pkg/storage/lister.go b/pkg/storage/lister.go index 30135b5b..175d59f3 100644 --- a/pkg/storage/lister.go +++ b/pkg/storage/lister.go @@ -2,7 +2,7 @@ package storage import "context" -// Lister is the interface that lists versions of a specific baseURL & module +// Lister is the interface that lists versions of a specific baseURL & module. type Lister interface { // List gets all the versions for the given baseURL & module. // It returns ErrNotFound if the module isn't found diff --git a/pkg/storage/mem/mem.go b/pkg/storage/mem/mem.go index 8644edd1..13458c69 100644 --- a/pkg/storage/mem/mem.go +++ b/pkg/storage/mem/mem.go @@ -9,19 +9,19 @@ import ( "github.com/spf13/afero" ) -// NewStorage creates new in-memory storage using the afero.NewMemMapFs() in memory file system +// NewStorage creates new in-memory storage using the afero.NewMemMapFs() in memory file system. func NewStorage() (storage.Backend, error) { const op errors.Op = "mem.NewStorage" memFs := afero.NewMemMapFs() tmpDir, err := afero.TempDir(memFs, "", "") if err != nil { - return nil, errors.E(op, fmt.Errorf("could not create temp dir for 'In Memory' storage (%s)", err)) + return nil, errors.E(op, fmt.Errorf("could not create temp dir for 'In Memory' storage: %w", err)) } memStorage, err := fs.NewStorage(tmpDir, memFs) if err != nil { - return nil, errors.E(op, fmt.Errorf("could not create storage from memory fs (%s)", err)) + return nil, errors.E(op, fmt.Errorf("could not create storage from memory fs: %w", err)) } return memStorage, nil } diff --git a/pkg/storage/minio/cataloger.go b/pkg/storage/minio/cataloger.go index 56ccb48b..4cfbd226 100644 --- a/pkg/storage/minio/cataloger.go +++ b/pkg/storage/minio/cataloger.go @@ -11,11 +11,11 @@ import ( "github.com/minio/minio-go/v6" ) -// Catalog implements the (./pkg/storage).Cataloger interface -// It returns a list of modules and versions contained in the storage +// Catalog implements the (./pkg/storage).Cataloger interface. +// It returns a list of modules and versions contained in the storage. func (s *storageImpl) Catalog(ctx context.Context, token string, pageSize int) ([]paths.AllPathParams, string, error) { const op errors.Op = "minio.Catalog" - ctx, span := observ.StartSpan(ctx, op.String()) + _, span := observ.StartSpan(ctx, op.String()) defer span.End() res := make([]paths.AllPathParams, 0) count := pageSize @@ -50,7 +50,7 @@ func fetchModsAndVersions(objects []minio.ObjectInfo, elementsNum int) ([]paths. continue } - p, err := parseMinioKey(&o) + p, err := parseMinioKey(o) if err != nil { continue } @@ -66,7 +66,7 @@ func fetchModsAndVersions(objects []minio.ObjectInfo, elementsNum int) ([]paths. return res, lastKey } -func parseMinioKey(o *minio.ObjectInfo) (paths.AllPathParams, error) { +func parseMinioKey(o minio.ObjectInfo) (paths.AllPathParams, error) { const op errors.Op = "minio.parseMinioKey" _, m, v := extractKey(o.Key) diff --git a/pkg/storage/minio/checker.go b/pkg/storage/minio/checker.go index c555a248..6fc6d6ea 100644 --- a/pkg/storage/minio/checker.go +++ b/pkg/storage/minio/checker.go @@ -8,21 +8,17 @@ import ( "github.com/gomods/athens/pkg/observ" ) -const ( - minioErrorCodeNoSuchKey = "NoSuchKey" -) - -func (v *storageImpl) Exists(ctx context.Context, module, version string) (bool, error) { +func (s *storageImpl) Exists(ctx context.Context, module, version string) (bool, error) { const op errors.Op = "minio.Exists" - ctx, span := observ.StartSpan(ctx, op.String()) + _, span := observ.StartSpan(ctx, op.String()) defer span.End() - versionedPath := v.versionLocation(module, version) + versionedPath := s.versionLocation(module, version) modPath := fmt.Sprintf("%s/go.mod", versionedPath) infoPath := fmt.Sprintf("%s/%s.info", versionedPath, version) zipPath := fmt.Sprintf("%s/source.zip", versionedPath) var count int - objectCh, err := v.minioCore.ListObjectsV2(v.bucketName, versionedPath, "", false, "", 0, "") + objectCh, err := s.minioCore.ListObjectsV2(s.bucketName, versionedPath, "", false, "", 0, "") if err != nil { return false, errors.E(op, err, errors.M(module), errors.V(version)) } diff --git a/pkg/storage/minio/deleter.go b/pkg/storage/minio/deleter.go index 2275d25e..d261bdd3 100644 --- a/pkg/storage/minio/deleter.go +++ b/pkg/storage/minio/deleter.go @@ -8,11 +8,11 @@ import ( "github.com/gomods/athens/pkg/observ" ) -func (v *storageImpl) Delete(ctx context.Context, module, version string) error { +func (s *storageImpl) Delete(ctx context.Context, module, version string) error { const op errors.Op = "minio.Delete" ctx, span := observ.StartSpan(ctx, op.String()) defer span.End() - exists, err := v.Exists(ctx, module, version) + exists, err := s.Exists(ctx, module, version) if err != nil { return errors.E(op, err, errors.M(module), errors.V(version)) } @@ -21,20 +21,20 @@ func (v *storageImpl) Delete(ctx context.Context, module, version string) error return errors.E(op, errors.M(module), errors.V(version), errors.KindNotFound) } - versionedPath := v.versionLocation(module, version) + versionedPath := s.versionLocation(module, version) modPath := fmt.Sprintf("%s/go.mod", versionedPath) - if err := v.minioClient.RemoveObject(v.bucketName, modPath); err != nil { + if err := s.minioClient.RemoveObject(s.bucketName, modPath); err != nil { return errors.E(op, err, errors.M(module), errors.V(version)) } zipPath := fmt.Sprintf("%s/source.zip", versionedPath) - if err := v.minioClient.RemoveObject(v.bucketName, zipPath); err != nil { + if err := s.minioClient.RemoveObject(s.bucketName, zipPath); err != nil { return errors.E(op, err, errors.M(module), errors.V(version)) } infoPath := fmt.Sprintf("%s/%s.info", versionedPath, version) - err = v.minioClient.RemoveObject(v.bucketName, infoPath) + err = s.minioClient.RemoveObject(s.bucketName, infoPath) if err != nil { return errors.E(op, err, errors.M(module), errors.V(version)) } diff --git a/pkg/storage/minio/getter.go b/pkg/storage/minio/getter.go index dd18891e..ebdf8bd5 100644 --- a/pkg/storage/minio/getter.go +++ b/pkg/storage/minio/getter.go @@ -12,16 +12,16 @@ import ( minio "github.com/minio/minio-go/v6" ) -func (v *storageImpl) Info(ctx context.Context, module, vsn string) ([]byte, error) { +func (s *storageImpl) Info(ctx context.Context, module, vsn string) ([]byte, error) { const op errors.Op = "minio.Info" - ctx, span := observ.StartSpan(ctx, op.String()) + _, span := observ.StartSpan(ctx, op.String()) defer span.End() - infoPath := fmt.Sprintf("%s/%s.info", v.versionLocation(module, vsn), vsn) - infoReader, err := v.minioClient.GetObject(v.bucketName, infoPath, minio.GetObjectOptions{}) + infoPath := fmt.Sprintf("%s/%s.info", s.versionLocation(module, vsn), vsn) + infoReader, err := s.minioClient.GetObject(s.bucketName, infoPath, minio.GetObjectOptions{}) if err != nil { return nil, errors.E(op, err) } - defer infoReader.Close() + defer func() { _ = infoReader.Close() }() info, err := io.ReadAll(infoReader) if err != nil { return nil, transformNotFoundErr(op, module, vsn, err) @@ -30,16 +30,16 @@ func (v *storageImpl) Info(ctx context.Context, module, vsn string) ([]byte, err return info, nil } -func (v *storageImpl) GoMod(ctx context.Context, module, vsn string) ([]byte, error) { +func (s *storageImpl) GoMod(ctx context.Context, module, vsn string) ([]byte, error) { const op errors.Op = "minio.GoMod" - ctx, span := observ.StartSpan(ctx, op.String()) + _, span := observ.StartSpan(ctx, op.String()) defer span.End() - modPath := fmt.Sprintf("%s/go.mod", v.versionLocation(module, vsn)) - modReader, err := v.minioClient.GetObject(v.bucketName, modPath, minio.GetObjectOptions{}) + modPath := fmt.Sprintf("%s/go.mod", s.versionLocation(module, vsn)) + modReader, err := s.minioClient.GetObject(s.bucketName, modPath, minio.GetObjectOptions{}) if err != nil { return nil, errors.E(op, err) } - defer modReader.Close() + defer func() { _ = modReader.Close() }() mod, err := io.ReadAll(modReader) if err != nil { return nil, transformNotFoundErr(op, module, vsn, err) @@ -48,31 +48,32 @@ func (v *storageImpl) GoMod(ctx context.Context, module, vsn string) ([]byte, er return mod, nil } -func (v *storageImpl) Zip(ctx context.Context, module, vsn string) (storage.SizeReadCloser, error) { +func (s *storageImpl) Zip(ctx context.Context, module, vsn string) (storage.SizeReadCloser, error) { const op errors.Op = "minio.Zip" - ctx, span := observ.StartSpan(ctx, op.String()) + _, span := observ.StartSpan(ctx, op.String()) defer span.End() - zipPath := fmt.Sprintf("%s/source.zip", v.versionLocation(module, vsn)) - _, err := v.minioClient.StatObject(v.bucketName, zipPath, minio.StatObjectOptions{}) + zipPath := fmt.Sprintf("%s/source.zip", s.versionLocation(module, vsn)) + _, err := s.minioClient.StatObject(s.bucketName, zipPath, minio.StatObjectOptions{}) if err != nil { return nil, errors.E(op, err, errors.KindNotFound, errors.M(module), errors.V(vsn)) } - zipReader, err := v.minioClient.GetObject(v.bucketName, zipPath, minio.GetObjectOptions{}) + zipReader, err := s.minioClient.GetObject(s.bucketName, zipPath, minio.GetObjectOptions{}) if err != nil { return nil, errors.E(op, err) } oi, err := zipReader.Stat() if err != nil { - zipReader.Close() + _ = zipReader.Close() return nil, errors.E(op, err) } return storage.NewSizer(zipReader, oi.Size), nil } func transformNotFoundErr(op errors.Op, module, version string, err error) error { - if eresp, ok := err.(minio.ErrorResponse); ok { + var eresp minio.ErrorResponse + if errors.AsErr(err, &eresp) { if eresp.StatusCode == http.StatusNotFound { return errors.E(op, errors.M(module), errors.V(version), errors.KindNotFound) } diff --git a/pkg/storage/minio/key.go b/pkg/storage/minio/key.go index 2af230ab..45327980 100644 --- a/pkg/storage/minio/key.go +++ b/pkg/storage/minio/key.go @@ -5,7 +5,7 @@ import ( "strings" ) -func extractKey(objectKey string) (key string, module string, version string) { +func extractKey(objectKey string) (key, module, version string) { var err error key, err = url.PathUnescape(objectKey) if err != nil { @@ -14,8 +14,8 @@ func extractKey(objectKey string) (key string, module string, version string) { parts := strings.Split(key, "/") version = parts[len(parts)-2] - module = strings.Replace(key, version, "", -2) - module = strings.Replace(module, "//.info", "", -1) + module = strings.ReplaceAll(key, version, "") + module = strings.ReplaceAll(module, "//.info", "") return key, module, version } diff --git a/pkg/storage/minio/lister.go b/pkg/storage/minio/lister.go index 6cb034aa..c656e6c3 100644 --- a/pkg/storage/minio/lister.go +++ b/pkg/storage/minio/lister.go @@ -9,26 +9,25 @@ import ( "github.com/gomods/athens/pkg/observ" ) -func (l *storageImpl) List(ctx context.Context, module string) ([]string, error) { +func (s *storageImpl) List(ctx context.Context, module string) ([]string, error) { const op errors.Op = "minio.List" - ctx, span := observ.StartSpan(ctx, op.String()) + _, span := observ.StartSpan(ctx, op.String()) defer span.End() doneCh := make(chan struct{}) defer close(doneCh) searchPrefix := module + "/" - objectCh, err := l.minioCore.ListObjectsV2(l.bucketName, searchPrefix, "", false, "", 0, "") - + objectCh, err := s.minioCore.ListObjectsV2(s.bucketName, searchPrefix, "", false, "", 0, "") if err != nil { return nil, errors.E(op, err, errors.M(module)) } - ret := []string{} + var ret []string for _, object := range objectCh.Contents { if object.Err != nil { return nil, errors.E(op, object.Err, errors.M(module)) } key, _, ver := extractKey(object.Key) - goModKey := fmt.Sprintf("%s/go.mod", l.versionLocation(module, ver)) + goModKey := fmt.Sprintf("%s/go.mod", s.versionLocation(module, ver)) if goModKey == key { ret = append(ret, ver) } diff --git a/pkg/storage/minio/minio.go b/pkg/storage/minio/minio.go index 13d2baeb..b105aea1 100644 --- a/pkg/storage/minio/minio.go +++ b/pkg/storage/minio/minio.go @@ -21,7 +21,7 @@ func (s *storageImpl) versionLocation(module, version string) string { } // NewStorage returns a connected Minio or DigitalOcean Spaces storage -// that implements storage.Backend +// that implements storage.Backend. func NewStorage(conf *config.MinioConfig, timeout time.Duration) (storage.Backend, error) { const op errors.Op = "minio.NewStorage" endpoint := conf.Endpoint diff --git a/pkg/storage/minio/saver.go b/pkg/storage/minio/saver.go index 5fa1e7df..864b4c18 100644 --- a/pkg/storage/minio/saver.go +++ b/pkg/storage/minio/saver.go @@ -15,7 +15,7 @@ import ( func (s *storageImpl) Save(ctx context.Context, module, vsn string, mod []byte, zip io.Reader, info []byte) error { const op errors.Op = "storage.minio.Save" - ctx, span := observ.StartSpan(ctx, op.String()) + _, span := observ.StartSpan(ctx, op.String()) defer span.End() dir := s.versionLocation(module, vsn) modFileName := dir + "/" + "go.mod" diff --git a/pkg/storage/module/delete.go b/pkg/storage/module/delete.go index 126eebcf..6bf54d66 100644 --- a/pkg/storage/module/delete.go +++ b/pkg/storage/module/delete.go @@ -7,26 +7,26 @@ import ( "github.com/gomods/athens/pkg/config" "github.com/gomods/athens/pkg/errors" - multierror "github.com/hashicorp/go-multierror" + "github.com/hashicorp/go-multierror" ) -// Deleter takes a path to a file and deletes it from the blob store +// Deleter takes a path to a file and deletes it from the blob store. type Deleter func(ctx context.Context, path string) error // Delete deletes .info, .mod and .zip files from the blob store in parallel. -// Returns multierror containing errors from all deletes and timeouts -func Delete(ctx context.Context, module, version string, delete Deleter, timeout time.Duration) error { +// Returns multierror containing errors from all deletes and timeouts. +func Delete(ctx context.Context, module, version string, del Deleter, timeout time.Duration) error { const op errors.Op = "module.Delete" tctx, cancel := context.WithTimeout(ctx, timeout) defer cancel() - del := func(ext string) <-chan error { + delFn := func(ext string) <-chan error { ec := make(chan error) go func() { defer close(ec) p := config.PackageVersionedName(module, version, ext) - ec <- delete(tctx, p) + ec <- del(tctx, p) }() return ec } @@ -34,10 +34,10 @@ func Delete(ctx context.Context, module, version string, delete Deleter, timeout errChan := make(chan error, numFiles) delOrAbort := func(ext string) { select { - case err := <-del(ext): + case err := <-delFn(ext): errChan <- err case <-tctx.Done(): - errChan <- fmt.Errorf("deleting %s.%s.%s failed: %s", module, version, ext, tctx.Err()) + errChan <- fmt.Errorf("deleting %s.%s.%s failed: %w", module, version, ext, tctx.Err()) } } diff --git a/pkg/storage/module/upload.go b/pkg/storage/module/upload.go index 5395a007..5bd4a1e9 100644 --- a/pkg/storage/module/upload.go +++ b/pkg/storage/module/upload.go @@ -13,11 +13,11 @@ import ( const numFiles = 3 -// Uploader takes a stream and saves it to the blob store under a given path +// Uploader takes a stream and saves it to the blob store under a given path. type Uploader func(ctx context.Context, path, contentType string, stream io.Reader) error // Upload saves .info, .mod and .zip files to the blob store in parallel. -// Returns multierror containing errors from all uploads and timeouts +// Returns multierror containing errors from all uploads and timeouts. func Upload(ctx context.Context, module, version string, info, mod, zip io.Reader, uploader Uploader, timeout time.Duration) error { const op errors.Op = "module.Upload" tctx, cancel := context.WithTimeout(ctx, timeout) @@ -40,7 +40,7 @@ func Upload(ctx context.Context, module, version string, info, mod, zip io.Reade case err := <-save(ext, contentType, stream): errChan <- err case <-tctx.Done(): - errChan <- fmt.Errorf("uploading %s.%s.%s failed: %s", module, version, ext, tctx.Err()) + errChan <- fmt.Errorf("uploading %s.%s.%s failed: %w", module, version, ext, tctx.Err()) } } go saveOrAbort("info", "application/json", info) diff --git a/pkg/storage/mongo/cataloger.go b/pkg/storage/mongo/cataloger.go index 6430202f..f409fa99 100644 --- a/pkg/storage/mongo/cataloger.go +++ b/pkg/storage/mongo/cataloger.go @@ -2,6 +2,7 @@ package mongo import ( "context" + "github.com/gomods/athens/pkg/errors" "github.com/gomods/athens/pkg/paths" "github.com/gomods/athens/pkg/storage" @@ -11,8 +12,8 @@ import ( "go.mongodb.org/mongo-driver/mongo/options" ) -// Catalog implements the (./pkg/storage).Cataloger interface -// It returns a list of modules and versions contained in the storage +// Catalog implements the (./pkg/storage).Cataloger interface. +// It returns a list of modules and versions contained in the storage. func (s *ModuleStore) Catalog(ctx context.Context, token string, pageSize int) ([]paths.AllPathParams, string, error) { const op errors.Op = "mongo.Catalog" q := bson.M{} @@ -33,7 +34,6 @@ func (s *ModuleStore) Catalog(ctx context.Context, token string, pageSize int) ( modules := make([]storage.Module, 0) findOptions := options.Find().SetProjection(projection).SetSort(sort).SetLimit(int64(pageSize)) cursor, err := c.Find(tctx, q, findOptions) - if err != nil { return nil, "", errors.E(op, err) } @@ -53,13 +53,13 @@ func (s *ModuleStore) Catalog(ctx context.Context, token string, pageSize int) ( return nil, "", nil } - var versions = make([]paths.AllPathParams, len(modules)) + versions := make([]paths.AllPathParams, len(modules)) for i := range modules { versions[i].Module = modules[i].Module versions[i].Version = modules[i].Version } - var next = modules[len(modules)-1].ID.Hex() + next := modules[len(modules)-1].ID.Hex() if len(modules) < pageSize { return versions, "", nil } diff --git a/pkg/storage/mongo/checker.go b/pkg/storage/mongo/checker.go index 27fe28b1..3fd45f2f 100644 --- a/pkg/storage/mongo/checker.go +++ b/pkg/storage/mongo/checker.go @@ -8,7 +8,7 @@ import ( "go.mongodb.org/mongo-driver/bson" ) -// Exists checks for a specific version of a module +// Exists checks for a specific version of a module. func (s *ModuleStore) Exists(ctx context.Context, module, vsn string) (bool, error) { var op errors.Op = "mongo.Exists" ctx, span := observ.StartSpan(ctx, op.String()) diff --git a/pkg/storage/mongo/deleter.go b/pkg/storage/mongo/deleter.go index d7fd7dba..8f25b235 100644 --- a/pkg/storage/mongo/deleter.go +++ b/pkg/storage/mongo/deleter.go @@ -11,7 +11,7 @@ import ( "go.mongodb.org/mongo-driver/x/bsonx" ) -// Delete removes a specific version of a module +// Delete removes a specific version of a module. func (s *ModuleStore) Delete(ctx context.Context, module, version string) error { const op errors.Op = "mongo.Delete" ctx, span := observ.StartSpan(ctx, op.String()) @@ -40,11 +40,11 @@ func (s *ModuleStore) Delete(ctx context.Context, module, version string) error var x bsonx.Doc for cursor.Next(ctx) { - cursor.Decode(&x) + _ = cursor.Decode(&x) } if err = bucket.Delete(x.Lookup("_id").ObjectID()); err != nil { kind := errors.KindUnexpected - if err == gridfs.ErrFileNotFound { + if errors.IsErr(err, gridfs.ErrFileNotFound) { kind = errors.KindNotFound } return errors.E(op, err, kind, errors.M(module), errors.V(version)) diff --git a/pkg/storage/mongo/getter.go b/pkg/storage/mongo/getter.go index 94762945..11821dec 100644 --- a/pkg/storage/mongo/getter.go +++ b/pkg/storage/mongo/getter.go @@ -13,14 +13,13 @@ import ( "go.mongodb.org/mongo-driver/x/bsonx" ) -// Info implements storage.Getter +// Info implements storage.Getter. func (s *ModuleStore) Info(ctx context.Context, module, vsn string) ([]byte, error) { const op errors.Op = "mongo.Info" ctx, span := observ.StartSpan(ctx, op.String()) defer span.End() result, err := query(ctx, s, module, vsn) - if err != nil { return nil, errors.E(op, err) } @@ -28,14 +27,13 @@ func (s *ModuleStore) Info(ctx context.Context, module, vsn string) ([]byte, err return result.Info, nil } -// GoMod implements storage.Getter +// GoMod implements storage.Getter. func (s *ModuleStore) GoMod(ctx context.Context, module, vsn string) ([]byte, error) { const op errors.Op = "mongo.GoMod" ctx, span := observ.StartSpan(ctx, op.String()) defer span.End() result, err := query(ctx, s, module, vsn) - if err != nil { return nil, errors.E(op, err) } @@ -43,7 +41,7 @@ func (s *ModuleStore) GoMod(ctx context.Context, module, vsn string) ([]byte, er return result.Mod, nil } -// Zip implements storage.Getter +// Zip implements storage.Getter. func (s *ModuleStore) Zip(ctx context.Context, module, vsn string) (storage.SizeReadCloser, error) { const op errors.Op = "mongo.Zip" ctx, span := observ.StartSpan(ctx, op.String()) @@ -59,7 +57,7 @@ func (s *ModuleStore) Zip(ctx context.Context, module, vsn string) (storage.Size dStream, err := bucket.OpenDownloadStreamByName(zipName, options.GridFSName()) if err != nil { kind := errors.KindUnexpected - if err == gridfs.ErrFileNotFound { + if errors.IsErr(err, gridfs.ErrFileNotFound) { kind = errors.KindNotFound } return nil, errors.E(op, err, kind, errors.M(module), errors.V(vsn)) @@ -79,7 +77,7 @@ func (s *ModuleStore) Zip(ctx context.Context, module, vsn string) (storage.Size return storage.NewSizer(dStream, size), nil } -// Query connects to and queries storage module +// Query connects to and queries storage module. func query(ctx context.Context, s *ModuleStore, module, vsn string) (*storage.Module, error) { const op errors.Op = "mongo.query" ctx, span := observ.StartSpan(ctx, op.String()) @@ -95,7 +93,7 @@ func query(ctx context.Context, s *ModuleStore, module, vsn string) (*storage.Mo queryResult := c.FindOne(tctx, bson.M{"module": module, "version": vsn}) if queryErr := queryResult.Err(); queryErr != nil { kind := errors.KindUnexpected - if queryErr == mongo.ErrNoDocuments { + if errors.IsErr(queryErr, mongo.ErrNoDocuments) { kind = errors.KindNotFound } return nil, errors.E(op, queryErr, kind, errors.M(module), errors.V(vsn)) @@ -103,7 +101,7 @@ func query(ctx context.Context, s *ModuleStore, module, vsn string) (*storage.Mo if err := queryResult.Decode(result); err != nil { kind := errors.KindUnexpected - if err == mongo.ErrNoDocuments { + if errors.IsErr(err, mongo.ErrNoDocuments) { kind = errors.KindNotFound } return nil, errors.E(op, err, kind, errors.M(module), errors.V(vsn)) diff --git a/pkg/storage/mongo/lister.go b/pkg/storage/mongo/lister.go index 09bfdd39..5f5ba52f 100644 --- a/pkg/storage/mongo/lister.go +++ b/pkg/storage/mongo/lister.go @@ -12,7 +12,7 @@ import ( "go.mongodb.org/mongo-driver/mongo/options" ) -// List lists all versions of a module +// List lists all versions of a module. func (s *ModuleStore) List(ctx context.Context, moduleName string) ([]string, error) { const op errors.Op = "mongo.List" ctx, span := observ.StartSpan(ctx, op.String()) @@ -31,9 +31,9 @@ func (s *ModuleStore) List(ctx context.Context, moduleName string) ([]string, er var errs error for cursor.Next(ctx) { var module storage.Module - if err := cursor.Decode(&module); err != nil { + if err = cursor.Decode(&module); err != nil { kind := errors.KindUnexpected - if err == mongo.ErrNoDocuments { + if errors.IsErr(err, mongo.ErrNoDocuments) { kind = errors.KindNotFound } errs = multierror.Append(errs, errors.E(op, err, kind)) diff --git a/pkg/storage/mongo/mongo.go b/pkg/storage/mongo/mongo.go index 2ba32881..939ba9d7 100644 --- a/pkg/storage/mongo/mongo.go +++ b/pkg/storage/mongo/mongo.go @@ -44,7 +44,7 @@ func NewStorage(conf *config.MongoConfig, timeout time.Duration) (*ModuleStore, return nil, errors.E(op, err) } - _, err = ms.connect(conf) + _, err = ms.connect() if err != nil { return nil, errors.E(op, err) @@ -53,27 +53,27 @@ func NewStorage(conf *config.MongoConfig, timeout time.Duration) (*ModuleStore, return ms, nil } -func (m *ModuleStore) connect(conf *config.MongoConfig) (*mongo.Collection, error) { +func (s *ModuleStore) connect() (*mongo.Collection, error) { const op errors.Op = "mongo.connect" - err := m.client.Connect(context.Background()) + err := s.client.Connect(context.Background()) if err != nil { return nil, errors.E(op, err) } - return m.initDatabase(), nil + return s.initDatabase(), nil } -func (m *ModuleStore) initDatabase() *mongo.Collection { - if m.db == "" { - m.db = "athens" +func (s *ModuleStore) initDatabase() *mongo.Collection { + if s.db == "" { + s.db = "athens" } - if m.coll == "" { - m.coll = "modules" + if s.coll == "" { + s.coll = "modules" } - c := m.client.Database(m.db).Collection(m.coll) + c := s.client.Database(s.db).Collection(s.coll) indexView := c.Indexes() keys := bson.D{ {Key: "base_url", Value: 1}, @@ -81,26 +81,26 @@ func (m *ModuleStore) initDatabase() *mongo.Collection { {Key: "version", Value: 1}, } indexOptions := options.Index().SetSparse(true).SetUnique(true) - indexView.CreateOne(context.Background(), mongo.IndexModel{Keys: keys, Options: indexOptions}, options.CreateIndexes()) + _, _ = indexView.CreateOne(context.Background(), mongo.IndexModel{Keys: keys, Options: indexOptions}, options.CreateIndexes()) return c } -func (m *ModuleStore) newClient() (*mongo.Client, error) { +func (s *ModuleStore) newClient() (*mongo.Client, error) { const op errors.Op = "mongo.newClient" tlsConfig := &tls.Config{} clientOptions := options.Client() - clientOptions = clientOptions.ApplyURI(m.url) + clientOptions = clientOptions.ApplyURI(s.url) err := clientOptions.Validate() if err != nil { return nil, errors.E(op, err) } - if m.certPath != "" { + if s.certPath != "" { // Sets only when the env var is setup in config.dev.toml - tlsConfig.InsecureSkipVerify = m.insecure + tlsConfig.InsecureSkipVerify = s.insecure var roots *x509.CertPool // See if there is a system cert pool roots, err := x509.SystemCertPool() @@ -109,19 +109,19 @@ func (m *ModuleStore) newClient() (*mongo.Client, error) { roots = x509.NewCertPool() } - cert, err := os.ReadFile(m.certPath) + cert, err := os.ReadFile(s.certPath) if err != nil { return nil, errors.E(op, err) } if ok := roots.AppendCertsFromPEM(cert); !ok { - return nil, fmt.Errorf("failed to parse certificate from: %s", m.certPath) + return nil, fmt.Errorf("failed to parse certificate from: %s", s.certPath) } tlsConfig.ClientCAs = roots clientOptions = clientOptions.SetTLSConfig(tlsConfig) } - clientOptions = clientOptions.SetConnectTimeout(m.timeout) + clientOptions = clientOptions.SetConnectTimeout(s.timeout) client, err := mongo.NewClient(clientOptions) if err != nil { return nil, errors.E(op, err) @@ -130,6 +130,6 @@ func (m *ModuleStore) newClient() (*mongo.Client, error) { return client, nil } -func (m *ModuleStore) gridFileName(mod, ver string) string { - return strings.Replace(mod, "/", "_", -1) + "_" + ver + ".zip" +func (s *ModuleStore) gridFileName(mod, ver string) string { + return strings.ReplaceAll(mod, "/", "_") + "_" + ver + ".zip" } diff --git a/pkg/storage/mongo/mongo_test.go b/pkg/storage/mongo/mongo_test.go index 13b2a8da..7e2a31e7 100644 --- a/pkg/storage/mongo/mongo_test.go +++ b/pkg/storage/mongo/mongo_test.go @@ -20,8 +20,8 @@ func TestBackend(t *testing.T) { compliance.RunTests(t, backend, backend.clear) } -func (m *ModuleStore) clear() error { - m.client.Database(m.db).Drop(context.Background()) +func (s *ModuleStore) clear() error { + s.client.Database(s.db).Drop(context.Background()) return nil } diff --git a/pkg/storage/mongo/saver.go b/pkg/storage/mongo/saver.go index 878d25b1..131c9097 100644 --- a/pkg/storage/mongo/saver.go +++ b/pkg/storage/mongo/saver.go @@ -37,10 +37,9 @@ func (s *ModuleStore) Save(ctx context.Context, module, version string, mod []by if err != nil { return errors.E(op, err, errors.M(module), errors.V(version)) } - defer uStream.Close() + defer func() { _ = uStream.Close() }() numBytesWritten, err := io.Copy(uStream, zip) - if err != nil { return errors.E(op, err, errors.M(module), errors.V(version)) } diff --git a/pkg/storage/rev_info.go b/pkg/storage/rev_info.go index 8ed99af7..bb97fe01 100644 --- a/pkg/storage/rev_info.go +++ b/pkg/storage/rev_info.go @@ -2,7 +2,7 @@ package storage import "time" -// From https://pkg.go.dev/cmd/go/internal/modfetch/codehost#Origin +// Origin is taken from https://pkg.go.dev/cmd/go/internal/modfetch/codehost#Origin. type Origin struct { VCS string `json:",omitempty"` // "git" etc URL string `json:",omitempty"` // URL of repository @@ -36,7 +36,7 @@ type Origin struct { // RevInfo is json-encodable into the response body for // GET baseURL/module/@v/version.info -// From https://pkg.go.dev/cmd/go/internal/modfetch/codehost#RevInfo +// from https://pkg.go.dev/cmd/go/internal/modfetch/codehost#RevInfo. type RevInfo struct { Origin *Origin Name string // complete ID in underlying repository diff --git a/pkg/storage/s3/cataloger.go b/pkg/storage/s3/cataloger.go index 3614329f..19308571 100644 --- a/pkg/storage/s3/cataloger.go +++ b/pkg/storage/s3/cataloger.go @@ -5,18 +5,16 @@ import ( "fmt" "strings" - "github.com/gomods/athens/pkg/config" - - "github.com/gomods/athens/pkg/paths" - "github.com/aws/aws-sdk-go/aws" "github.com/aws/aws-sdk-go/service/s3" + "github.com/gomods/athens/pkg/config" "github.com/gomods/athens/pkg/errors" "github.com/gomods/athens/pkg/observ" + "github.com/gomods/athens/pkg/paths" ) -// Catalog implements the (./pkg/storage).Cataloger interface -// It returns a list of modules and versions contained in the storage +// Catalog implements the (./pkg/storage).Cataloger interface. +// It returns a list of modules and versions contained in the storage. func (s *Storage) Catalog(ctx context.Context, token string, pageSize int) ([]paths.AllPathParams, string, error) { const op errors.Op = "s3.Catalog" ctx, span := observ.StartSpan(ctx, op.String()) diff --git a/pkg/storage/s3/checker.go b/pkg/storage/s3/checker.go index 6fee5a03..63c6a91a 100644 --- a/pkg/storage/s3/checker.go +++ b/pkg/storage/s3/checker.go @@ -13,7 +13,7 @@ import ( ) // Exists implements the (./pkg/storage).Checker interface -// returning true if the module at version exists in storage +// returning true if the module at version exists in storage. func (s *Storage) Exists(ctx context.Context, module, version string) (bool, error) { const op errors.Op = "s3.Exists" ctx, span := observ.StartSpan(ctx, op.String()) @@ -38,7 +38,6 @@ func (s *Storage) Exists(ctx context.Context, module, version string) (bool, err } return len(found) < 3 }) - if err != nil { return false, errors.E(op, err, errors.M(module), errors.V(version)) } diff --git a/pkg/storage/s3/getter.go b/pkg/storage/s3/getter.go index d9b3e664..8420864b 100644 --- a/pkg/storage/s3/getter.go +++ b/pkg/storage/s3/getter.go @@ -13,7 +13,7 @@ import ( "github.com/gomods/athens/pkg/storage" ) -// Info implements the (./pkg/storage).Getter interface +// Info implements the (./pkg/storage).Getter interface. func (s *Storage) Info(ctx context.Context, module, version string) ([]byte, error) { const op errors.Op = "s3.Info" ctx, span := observ.StartSpan(ctx, op.String()) @@ -30,7 +30,7 @@ func (s *Storage) Info(ctx context.Context, module, version string) ([]byte, err if err != nil { return nil, errors.E(op, err, errors.M(module), errors.V(version)) } - defer infoReader.Close() + defer func() { _ = infoReader.Close() }() infoBytes, err := io.ReadAll(infoReader) if err != nil { @@ -39,7 +39,7 @@ func (s *Storage) Info(ctx context.Context, module, version string) ([]byte, err return infoBytes, nil } -// GoMod implements the (./pkg/storage).Getter interface +// GoMod implements the (./pkg/storage).Getter interface. func (s *Storage) GoMod(ctx context.Context, module, version string) ([]byte, error) { const op errors.Op = "s3.GoMod" ctx, span := observ.StartSpan(ctx, op.String()) @@ -56,17 +56,17 @@ func (s *Storage) GoMod(ctx context.Context, module, version string) ([]byte, er if err != nil { return nil, errors.E(op, err, errors.M(module), errors.V(version)) } - defer modReader.Close() + defer func() { _ = modReader.Close() }() modBytes, err := io.ReadAll(modReader) if err != nil { - return nil, errors.E(op, fmt.Errorf("could not get new reader for mod file: %s", err), errors.M(module), errors.V(version)) + return nil, errors.E(op, fmt.Errorf("could not get new reader for mod file: %w", err), errors.M(module), errors.V(version)) } return modBytes, nil } -// Zip implements the (./pkg/storage).Getter interface +// Zip implements the (./pkg/storage).Getter interface. func (s *Storage) Zip(ctx context.Context, module, version string) (storage.SizeReadCloser, error) { const op errors.Op = "s3.Zip" ctx, span := observ.StartSpan(ctx, op.String()) diff --git a/pkg/storage/s3/lister.go b/pkg/storage/s3/lister.go index 038cb406..9b51c6d6 100644 --- a/pkg/storage/s3/lister.go +++ b/pkg/storage/s3/lister.go @@ -10,8 +10,8 @@ import ( "github.com/gomods/athens/pkg/observ" ) -// List implements the (./pkg/storage).Lister interface -// It returns a list of versions, if any, for a given module +// List implements the (./pkg/storage).Lister interface. +// It returns a list of versions, if any, for a given module. func (s *Storage) List(ctx context.Context, module string) ([]string, error) { const op errors.Op = "s3.List" ctx, span := observ.StartSpan(ctx, op.String()) diff --git a/pkg/storage/s3/s3.go b/pkg/storage/s3/s3.go index bf29a168..813db57d 100644 --- a/pkg/storage/s3/s3.go +++ b/pkg/storage/s3/s3.go @@ -16,14 +16,14 @@ import ( ) // Storage implements (./pkg/storage).Backend and -// also provides a function to fetch the location of a module -// Storage uses amazon aws go SDK which expects these env variables +// also provides a function to fetch the location of a module. +// Storage uses amazon aws go SDK which expects these env variables. // - AWS_REGION - region for this storage, e.g 'us-west-2' // - AWS_ACCESS_KEY_ID - [optional] // - AWS_SECRET_ACCESS_KEY - [optional] // - AWS_SESSION_TOKEN - [optional] // - AWS_FORCE_PATH_STYLE - [optional] -// For information how to get your keyId and access key turn to official aws docs: https://docs.aws.amazon.com/sdk-for-go/v1/developer-guide/setting-up.html +// For information how to get your keyId and access key turn to official aws docs: https://docs.aws.amazon.com/sdk-for-go/v1/developer-guide/setting-up.html. type Storage struct { bucket string uploader s3manageriface.UploaderAPI @@ -31,12 +31,12 @@ type Storage struct { timeout time.Duration } -// New creates a new AWS S3 CDN saver +// New creates a new AWS S3 CDN saver. func New(s3Conf *config.S3Config, timeout time.Duration, options ...func(*aws.Config)) (*Storage, error) { const op errors.Op = "s3.New" awsConfig := defaults.Config() - // remove anonymous credentials from the default config so that + // Remove anonymous credentials from the default config so that // session.NewSession can auto-resolve credentials from role, profile, env etc. awsConfig.Credentials = nil awsConfig.Region = aws.String(s3Conf.Region) @@ -67,7 +67,7 @@ func New(s3Conf *config.S3Config, timeout time.Duration, options ...func(*aws.Co awsConfig.Endpoint = aws.String(s3Conf.Endpoint) } - // create a session with creds + // Create a session with creds. sess, err := session.NewSession(awsConfig) if err != nil { return nil, errors.E(op, err) @@ -83,6 +83,6 @@ func New(s3Conf *config.S3Config, timeout time.Duration, options ...func(*aws.Co }, nil } -func endpointFrom(credentialsEndpoint string, relativeURI string) string { +func endpointFrom(credentialsEndpoint, relativeURI string) string { return credentialsEndpoint + relativeURI } diff --git a/pkg/storage/saver.go b/pkg/storage/saver.go index 47777c99..45eb73a9 100644 --- a/pkg/storage/saver.go +++ b/pkg/storage/saver.go @@ -5,7 +5,7 @@ import ( "io" ) -// Saver saves module metadata and its source to underlying storage +// Saver saves module metadata and its source to underlying storage. type Saver interface { Save(ctx context.Context, module, version string, mod []byte, zip io.Reader, info []byte) error } diff --git a/pkg/storage/version.go b/pkg/storage/version.go index e21f7737..4947c4bf 100644 --- a/pkg/storage/version.go +++ b/pkg/storage/version.go @@ -2,7 +2,7 @@ package storage import "io" -// Version represents a version of a module and contains .mod file, a .info file and zip file of a specific version +// Version represents a version of a module and contains .mod file, a .info file and zip file of a specific version. type Version struct { Mod []byte Zip io.ReadCloser diff --git a/scripts/check_gofmt.sh b/scripts/check_gofmt.sh deleted file mode 100755 index f79aa9fe..00000000 --- a/scripts/check_gofmt.sh +++ /dev/null @@ -1,10 +0,0 @@ -#!/bin/bash - -# check_gofmt.sh -# Fail if a .go file hasn't been formatted with gofmt -set -euo pipefail - -GO_FILES=$(find . -iname '*.go' -type f -not -path "./vendor/*") # All the .go files -for f in $GO_FILES; do - test -z "$(gofmt -s -w "$f" | tee /dev/stderr)" -done diff --git a/scripts/check_govet.sh b/scripts/check_govet.sh deleted file mode 100755 index e6a8a484..00000000 --- a/scripts/check_govet.sh +++ /dev/null @@ -1,7 +0,0 @@ -#!/bin/bash - -# check_govet.sh -# Run the linter on everything except generated code -set -euo pipefail - -go vet ./... diff --git a/scripts/ps/check_gofmt.ps1 b/scripts/ps/check_gofmt.ps1 deleted file mode 100755 index 972fd4d3..00000000 --- a/scripts/ps/check_gofmt.ps1 +++ /dev/null @@ -1,7 +0,0 @@ -# check_gofmt.ps1 -# Fail if a .go file hasn't been formatted with gofmt -$GO_FILES = Get-ChildItem -Recurse -Filter *.go -Name | Select-String -Pattern $(Join-Path "vendor" -ChildPath ".*") -NotMatch -$out = & gofmt -s -l $GO_FILES -if ($out.length -gt 0) { - Write-Error $out -} diff --git a/scripts/ps/check_govet.ps1 b/scripts/ps/check_govet.ps1 deleted file mode 100755 index 8e61f452..00000000 --- a/scripts/ps/check_govet.ps1 +++ /dev/null @@ -1,7 +0,0 @@ -# check_govet.ps1 -# Run the linter on everything - -$out = & go vet ./... -if ($LastExitCode -ne 0) { - Write-Error $out -}