diff --git a/.bazel/golangcilint.yaml b/.bazel/golangcilint.yaml index 5fb65e21..e4161c20 100644 --- a/.bazel/golangcilint.yaml +++ b/.bazel/golangcilint.yaml @@ -14,3 +14,6 @@ linters-settings: dupl: # tokens count to trigger issue, 150 by default threshold: 200 + nestif: + # their are various nested if else, therefore specifying complexity as 26 + min-complexity: 26 \ No newline at end of file diff --git a/.travis.yml b/.travis.yml index fe557c32..e3983f1c 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,7 +1,7 @@ language: go go: - - 1.12.x + - 1.14.x env: - GO111MODULE=on @@ -19,7 +19,14 @@ cache: - $HOME/.cache/bazel install: - - if [[ "$TRAVIS_OS_NAME" == "linux" ]]; then wget -N https://github.com/bazelbuild/bazel/releases/download/0.26.1/bazel-0.26.1-installer-linux-x86_64.sh && chmod +x bazel-0.26.1-installer-linux-x86_64.sh && ./bazel-0.26.1-installer-linux-x86_64.sh --user; go get -u github.com/swaggo/swag/cmd/swag; go mod download; fi + - if [[ "$TRAVIS_OS_NAME" == "linux" ]]; then wget -N https://github.com/bazelbuild/bazel/releases/download/3.0.0/bazel-3.0.0-installer-linux-x86_64.sh && chmod +x bazel-3.0.0-installer-linux-x86_64.sh && ./bazel-3.0.0-installer-linux-x86_64.sh --user; go get -u github.com/swaggo/swag/cmd/swag; go mod download; sudo apt-get update; sudo apt-get install rpm; fi + +script: + - if [[ "$TRAVIS_OS_NAME" == "linux" ]]; then make && make -f Makefile.bazel build; fi + +after_success: + - bash <(curl -s https://codecov.io/bash) + script: - if [[ "$TRAVIS_OS_NAME" == "linux" ]]; then make && make -f Makefile.bazel build; fi diff --git a/Makefile b/Makefile index 59888cc5..98d5c71c 100644 --- a/Makefile +++ b/Makefile @@ -27,7 +27,7 @@ covhtml: .PHONY: check check: .bazel/golangcilint.yaml - golangci-lint --version || curl -sfL https://install.goreleaser.com/github.com/golangci/golangci-lint.sh | sh -s v1.21.0 + golangci-lint --version || curl -sfL https://install.goreleaser.com/github.com/golangci/golangci-lint.sh | sh -s v1.26.0 golangci-lint --config .bazel/golangcilint.yaml run --enable-all ./cmd/... ./pkg/... docs/docs.go: diff --git a/WORKSPACE b/WORKSPACE index e2e4d6be..43e73b95 100644 --- a/WORKSPACE +++ b/WORKSPACE @@ -1,6 +1,6 @@ workspace(name = "com_github_anuvu_zot") -go_version = "1.12.9" +go_version = "1.14.4" go_os = "linux" @@ -9,22 +9,22 @@ go_arch = "amd64" load("@bazel_tools//tools/build_defs/repo:http.bzl", "http_archive") load("@bazel_tools//tools/build_defs/repo:git.bzl", "git_repository") -go_rules_version = "0.19.3" +go_rules_version = "v0.23.3" http_archive( name = "io_bazel_rules_go", + sha256 = "a8d6b1b354d371a646d2f7927319974e0f9e52f73a2452d2b3877118169eb6bb", urls = [ - "https://storage.googleapis.com/bazel-mirror/github.com/bazelbuild/rules_go/releases/download/{}/rules_go-{}.tar.gz".format(go_rules_version, go_rules_version), + "https://mirror.bazel.build/github.com/bazelbuild/rules_go/releases/download/{}/rules_go-{}.tar.gz".format(go_rules_version, go_rules_version), "https://github.com/bazelbuild/rules_go/releases/download/{}/rules_go-{}.tar.gz".format(go_rules_version, go_rules_version), ], - sha256 = "313f2c7a23fecc33023563f082f381a32b9b7254f727a7dd2d6380ccc6dfe09b", ) -gazelle_version = "0.18.1" +gazelle_version = "v0.21.0" http_archive( name = "bazel_gazelle", - sha256 = "be9296bfd64882e3c08e3283c58fcb461fa6dd3c171764fcc4cf322f60615a9b", + sha256 = "bfd86b3cbe855d6c16c6fce60d76bd51f5c8dbc9cfcaef7a2bb5c1aafd0710e8", urls = ["https://github.com/bazelbuild/bazel-gazelle/releases/download/{}/bazel-gazelle-{}.tar.gz".format(gazelle_version, gazelle_version)], ) @@ -43,13 +43,15 @@ git_repository( remote = "https://github.com/atlassian/bazel-tools.git", ) -skylib_version = "0.8.0" +skylib_version = "1.0.2" http_archive( name = "bazel_skylib", - sha256 = "2ea8a5ed2b448baf4a6855d3ce049c4c452a6470b1efd1504fdb7c1c134d220a", - strip_prefix = "bazel-skylib-{}".format(skylib_version), - urls = ["https://github.com/bazelbuild/bazel-skylib/archive/{}.tar.gz".format(skylib_version)], + sha256 = "97e70364e9249702246c0e9444bccdc4b847bed1eb03c5a3ece4f83dfe6abc44", + urls = [ + "https://mirror.bazel.build/github.com/bazelbuild/bazel-skylib/releases/download/{}/bazel-skylib-{}.tar.gz".format(skylib_version, skylib_version), + "https://github.com/bazelbuild/bazel-skylib/releases/download/{}/bazel-skylib-{}.tar.gz".format(skylib_version, skylib_version), + ], ) load("@bazel_skylib//lib:versions.bzl", "versions") diff --git a/errors/errors.go b/errors/errors.go index 3291073a..b0a300f9 100644 --- a/errors/errors.go +++ b/errors/errors.go @@ -24,4 +24,6 @@ var ( ErrCacheRootBucket = errors.New("cache: unable to create/update root bucket") ErrCacheNoBucket = errors.New("cache: unable to find bucket") ErrCacheMiss = errors.New("cache: miss") + ErrRequireCred = errors.New("ldap: bind credentials required") + ErrInvalidCred = errors.New("ldap: invalid credentials") ) diff --git a/go.mod b/go.mod index eed7f8a4..1bf1511a 100644 --- a/go.mod +++ b/go.mod @@ -1,6 +1,6 @@ module github.com/anuvu/zot -go 1.13 +go 1.14 require ( github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751 diff --git a/pkg/api/BUILD.bazel b/pkg/api/BUILD.bazel index b9820d8e..815edba3 100644 --- a/pkg/api/BUILD.bazel +++ b/pkg/api/BUILD.bazel @@ -41,6 +41,7 @@ go_test( embed = [":go_default_library"], race = "on", deps = [ + "//errors:go_default_library", "@com_github_chartmuseum_auth//:go_default_library", "@com_github_mitchellh_mapstructure//:go_default_library", "@com_github_nmcclain_ldap//:go_default_library", diff --git a/pkg/api/auth.go b/pkg/api/auth.go index 402cbe86..fb488e46 100644 --- a/pkg/api/auth.go +++ b/pkg/api/auth.go @@ -71,12 +71,13 @@ func bearerAuthHandler(c *Controller) mux.MiddlewareFunc { } } -// nolint (gocyclo) - we use closure making this a complex subroutine +// nolint:gocyclo // we use closure making this a complex subroutine func basicAuthHandler(c *Controller) mux.MiddlewareFunc { realm := c.Config.HTTP.Realm if realm == "" { realm = "Authorization Required" } + realm = "Basic realm=" + strconv.Quote(realm) // no password based authN, if neither LDAP nor HTTP BASIC is enabled @@ -97,7 +98,9 @@ func basicAuthHandler(c *Controller) mux.MiddlewareFunc { } credMap := make(map[string]string) + delay := c.Config.HTTP.Auth.FailDelay + var ldapClient *LDAPClient if c.Config.HTTP.Auth != nil { @@ -117,27 +120,36 @@ func basicAuthHandler(c *Controller) mux.MiddlewareFunc { Log: c.Log, SubtreeSearch: l.SubtreeSearch, } + if c.Config.HTTP.Auth.LDAP.CACert != "" { caCert, err := ioutil.ReadFile(c.Config.HTTP.Auth.LDAP.CACert) + if err != nil { panic(err) } + caCertPool := x509.NewCertPool() + if !caCertPool.AppendCertsFromPEM(caCert) { panic(errors.ErrBadCACert) } + ldapClient.ClientCAs = caCertPool } else { // default to system cert pool caCertPool, err := x509.SystemCertPool() + if err != nil { panic(errors.ErrBadCACert) } + ldapClient.ClientCAs = caCertPool } } + if c.Config.HTTP.Auth.HTPasswd.Path != "" { f, err := os.Open(c.Config.HTTP.Auth.HTPasswd.Path) + if err != nil { panic(err) } @@ -170,6 +182,7 @@ func basicAuthHandler(c *Controller) mux.MiddlewareFunc { } s := strings.SplitN(basicAuth, " ", 2) + if len(s) != 2 || strings.ToLower(s[0]) != "basic" { authFail(w, realm, delay) return @@ -182,6 +195,7 @@ func basicAuthHandler(c *Controller) mux.MiddlewareFunc { } pair := strings.SplitN(string(b), ":", 2) + // nolint:gomnd if len(pair) != 2 { authFail(w, realm, delay) return @@ -211,7 +225,6 @@ func basicAuthHandler(c *Controller) mux.MiddlewareFunc { } authFail(w, realm, delay) - return }) } } diff --git a/pkg/api/config.go b/pkg/api/config.go index 29549de3..b07d6fed 100644 --- a/pkg/api/config.go +++ b/pkg/api/config.go @@ -7,8 +7,8 @@ import ( dspec "github.com/opencontainers/distribution-spec" ) -//nolint (gochecknoglobals) -var Commit string +// Commit ... +var Commit string //nolint: gochecknoglobals type StorageConfig struct { RootDirectory string @@ -85,7 +85,7 @@ func NewConfig() *Config { } } -// Sanitize makes a sanitized copy of the config removing any secrets +// Sanitize makes a sanitized copy of the config removing any secrets. func (c *Config) Sanitize() *Config { if c.HTTP.Auth != nil && c.HTTP.Auth.LDAP != nil && c.HTTP.Auth.LDAP.BindPassword != "" { s := &Config{} diff --git a/pkg/api/controller.go b/pkg/api/controller.go index e881799d..0497d210 100644 --- a/pkg/api/controller.go +++ b/pkg/api/controller.go @@ -87,7 +87,7 @@ func (c *Controller) Run() error { PreferServerCipherSuites: true, MinVersion: tls.VersionTLS12, } - server.TLSConfig.BuildNameToCertificate() + server.TLSConfig.BuildNameToCertificate() // nolint: staticcheck } return server.ServeTLS(l, c.Config.HTTP.TLS.Cert, c.Config.HTTP.TLS.Key) diff --git a/pkg/api/controller_test.go b/pkg/api/controller_test.go index 95643a15..7cc6b544 100644 --- a/pkg/api/controller_test.go +++ b/pkg/api/controller_test.go @@ -5,7 +5,6 @@ import ( "crypto/tls" "crypto/x509" "encoding/json" - "errors" "fmt" "io/ioutil" "net" @@ -20,6 +19,7 @@ import ( "golang.org/x/crypto/bcrypt" + "github.com/anuvu/zot/errors" "github.com/anuvu/zot/pkg/api" "github.com/chartmuseum/auth" "github.com/mitchellh/mapstructure" @@ -66,7 +66,7 @@ func makeHtpasswdFile() string { // bcrypt(username="test", passwd="test") content := []byte("test:$2y$05$hlbSXDp6hzDLu6VwACS39ORvVRpr3OMR4RlJ31jtlaOEGnPjKZI1m\n") - if err := ioutil.WriteFile(f.Name(), content, 0644); err != nil { + if err := ioutil.WriteFile(f.Name(), content, 0600); err != nil { panic(err) } @@ -81,7 +81,7 @@ func makeHtpasswdFileFromString(fileContent string) string { // bcrypt(username="test", passwd="test") content := []byte(fileContent) - if err := ioutil.WriteFile(f.Name(), content, 0644); err != nil { + if err := ioutil.WriteFile(f.Name(), content, 0600); err != nil { panic(err) } @@ -935,7 +935,7 @@ func (l *testLDAPServer) Stop() { func (l *testLDAPServer) Bind(bindDN, bindSimplePw string, conn net.Conn) (vldap.LDAPResultCode, error) { if bindDN == "" || bindSimplePw == "" { - return vldap.LDAPResultInappropriateAuthentication, errors.New("ldap: bind creds required") + return vldap.LDAPResultInappropriateAuthentication, errors.ErrRequireCred } if (bindDN == LDAPBindDN && bindSimplePw == LDAPBindPassword) || @@ -943,7 +943,7 @@ func (l *testLDAPServer) Bind(bindDN, bindSimplePw string, conn net.Conn) (vldap return vldap.LDAPResultSuccess, nil } - return vldap.LDAPResultInvalidCredentials, errors.New("ldap: invalid credentials") + return vldap.LDAPResultInvalidCredentials, errors.ErrInvalidCred } func (l *testLDAPServer) Search(boundDN string, req vldap.SearchRequest, diff --git a/pkg/api/errors.go b/pkg/api/errors.go index b97bf11d..ece2dd58 100644 --- a/pkg/api/errors.go +++ b/pkg/api/errors.go @@ -17,7 +17,7 @@ type ErrorList struct { type ErrorCode int -// nolint (golint) +// nolint: golint, stylecheck const ( BLOB_UNKNOWN ErrorCode = iota BLOB_UPLOAD_INVALID @@ -58,7 +58,7 @@ func (e ErrorCode) String() string { return m[e] } -func NewError(code ErrorCode, detail ...interface{}) Error { //nolint (interfacer) +func NewError(code ErrorCode, detail ...interface{}) Error { //nolint: interfacer var errMap = map[ErrorCode]Error{ BLOB_UNKNOWN: { Message: "blob unknown to registry", diff --git a/pkg/api/ldap.go b/pkg/api/ldap.go index b1babfd0..68b2db5e 100644 --- a/pkg/api/ldap.go +++ b/pkg/api/ldap.go @@ -55,12 +55,12 @@ func (lc *LDAPClient) Connect() error { // Reconnect with TLS if !lc.SkipTLS { config := &tls.Config{ - InsecureSkipVerify: lc.InsecureSkipVerify, // nolint (gosec): InsecureSkipVerify is not true by default + InsecureSkipVerify: lc.InsecureSkipVerify, // nolint: gosec // InsecureSkipVerify is not true by default RootCAs: lc.ClientCAs, } if lc.ClientCertificates != nil && len(lc.ClientCertificates) > 0 { config.Certificates = lc.ClientCertificates - config.BuildNameToCertificate() + config.BuildNameToCertificate() // nolint: staticcheck } err = l.StartTLS(config) @@ -72,13 +72,13 @@ func (lc *LDAPClient) Connect() error { } } else { config := &tls.Config{ - InsecureSkipVerify: lc.InsecureSkipVerify, // nolint (gosec): InsecureSkipVerify is not true by default + InsecureSkipVerify: lc.InsecureSkipVerify, // nolint: gosec // InsecureSkipVerify is not true by default ServerName: lc.ServerName, RootCAs: lc.ClientCAs, } if lc.ClientCertificates != nil && len(lc.ClientCertificates) > 0 { config.Certificates = lc.ClientCertificates - config.BuildNameToCertificate() + config.BuildNameToCertificate() // nolint: staticcheck } l, err = goldap.DialTLS("tcp", address, config) if err != nil { diff --git a/pkg/api/regexp.go b/pkg/api/regexp.go index e2df041e..0fafea2c 100644 --- a/pkg/api/regexp.go +++ b/pkg/api/regexp.go @@ -2,7 +2,7 @@ package api import "regexp" -// nolint (gochecknoglobals) +// nolint: gochecknoglobals var ( // alphaNumericRegexp defines the alpha numeric atom, typically a // component of names. This only allows lower case characters and digits. diff --git a/pkg/api/routes.go b/pkg/api/routes.go index 8e960dfc..c96d1f36 100644 --- a/pkg/api/routes.go +++ b/pkg/api/routes.go @@ -21,7 +21,7 @@ import ( "strconv" "strings" - _ "github.com/anuvu/zot/docs" // nolint (golint) - as required by swaggo + _ "github.com/anuvu/zot/docs" // as required by swaggo "github.com/anuvu/zot/errors" "github.com/anuvu/zot/pkg/log" "github.com/gorilla/mux" @@ -50,7 +50,7 @@ func NewRouteHandler(c *Controller) *RouteHandler { return rh } -// blobRLockWrapper calls the real handler with read-lock held +// blobRLockWrapper calls the real handler with read-lock held. func (rh *RouteHandler) blobRLockWrapper(f func(w http.ResponseWriter, r *http.Request)) func(w http.ResponseWriter, r *http.Request) { return func(w http.ResponseWriter, r *http.Request) { @@ -60,7 +60,7 @@ func (rh *RouteHandler) blobRLockWrapper(f func(w http.ResponseWriter, } } -// blobLockWrapper calls the real handler with write-lock held +// blobLockWrapper calls the real handler with write-lock held. func (rh *RouteHandler) blobLockWrapper(f func(w http.ResponseWriter, r *http.Request)) func(w http.ResponseWriter, r *http.Request) { return func(w http.ResponseWriter, r *http.Request) { @@ -117,7 +117,7 @@ func (rh *RouteHandler) SetupRoutes() { // @Router /v2/ [get] // @Accept json // @Produce json -// @Success 200 {string} string "ok" +// @Success 200 {string} string "ok". func (rh *RouteHandler) CheckVersionSupport(w http.ResponseWriter, r *http.Request) { w.Header().Set(DistAPIVersion, "registry/2.0") // NOTE: compatibility workaround - return this header in "allowed-read" mode to allow for clients to @@ -151,7 +151,7 @@ type ImageTags struct { // @Param last query string true "last tag value for pagination" // @Success 200 {object} api.ImageTags // @Failure 404 {string} string "not found" -// @Failure 400 {string} string "bad request" +// @Failure 400 {string} string "bad request". func (rh *RouteHandler) ListTags(w http.ResponseWriter, r *http.Request) { vars := mux.Vars(r) name, ok := vars["name"] @@ -260,7 +260,7 @@ func (rh *RouteHandler) ListTags(w http.ResponseWriter, r *http.Request) { // @Success 200 {string} string "ok" // @Header 200 {object} api.DistContentDigestKey // @Failure 404 {string} string "not found" -// @Failure 500 {string} string "internal server error" +// @Failure 500 {string} string "internal server error". func (rh *RouteHandler) CheckManifest(w http.ResponseWriter, r *http.Request) { vars := mux.Vars(r) name, ok := vars["name"] @@ -299,7 +299,7 @@ func (rh *RouteHandler) CheckManifest(w http.ResponseWriter, r *http.Request) { w.WriteHeader(http.StatusOK) } -// NOTE: https://github.com/swaggo/swag/issues/387 +// NOTE: https://github.com/swaggo/swag/issues/387. type ImageManifest struct { ispec.Manifest } @@ -315,7 +315,7 @@ type ImageManifest struct { // @Header 200 {object} api.DistContentDigestKey // @Failure 404 {string} string "not found" // @Failure 500 {string} string "internal server error" -// @Router /v2/{name}/manifests/{reference} [get] +// @Router /v2/{name}/manifests/{reference} [get]. func (rh *RouteHandler) GetManifest(w http.ResponseWriter, r *http.Request) { vars := mux.Vars(r) name, ok := vars["name"] @@ -367,7 +367,7 @@ func (rh *RouteHandler) GetManifest(w http.ResponseWriter, r *http.Request) { // @Failure 400 {string} string "bad request" // @Failure 404 {string} string "not found" // @Failure 500 {string} string "internal server error" -// @Router /v2/{name}/manifests/{reference} [put] +// @Router /v2/{name}/manifests/{reference} [put]. func (rh *RouteHandler) UpdateManifest(w http.ResponseWriter, r *http.Request) { vars := mux.Vars(r) name, ok := vars["name"] @@ -433,7 +433,7 @@ func (rh *RouteHandler) UpdateManifest(w http.ResponseWriter, r *http.Request) { // @Param name path string true "repository name" // @Param reference path string true "image reference or digest" // @Success 200 {string} string "ok" -// @Router /v2/{name}/manifests/{reference} [delete] +// @Router /v2/{name}/manifests/{reference} [delete]. func (rh *RouteHandler) DeleteManifest(w http.ResponseWriter, r *http.Request) { vars := mux.Vars(r) name, ok := vars["name"] @@ -481,7 +481,7 @@ func (rh *RouteHandler) DeleteManifest(w http.ResponseWriter, r *http.Request) { // @Param digest path string true "blob/layer digest" // @Success 200 {object} api.ImageManifest // @Header 200 {object} api.DistContentDigestKey -// @Router /v2/{name}/blobs/{digest} [head] +// @Router /v2/{name}/blobs/{digest} [head]. func (rh *RouteHandler) CheckBlob(w http.ResponseWriter, r *http.Request) { vars := mux.Vars(r) name, ok := vars["name"] @@ -535,7 +535,7 @@ func (rh *RouteHandler) CheckBlob(w http.ResponseWriter, r *http.Request) { // @Param digest path string true "blob/layer digest" // @Header 200 {object} api.DistContentDigestKey // @Success 200 {object} api.ImageManifest -// @Router /v2/{name}/blobs/{digest} [get] +// @Router /v2/{name}/blobs/{digest} [get]. func (rh *RouteHandler) GetBlob(w http.ResponseWriter, r *http.Request) { vars := mux.Vars(r) name, ok := vars["name"] @@ -584,7 +584,7 @@ func (rh *RouteHandler) GetBlob(w http.ResponseWriter, r *http.Request) { // @Param name path string true "repository name" // @Param digest path string true "blob/layer digest" // @Success 202 {string} string "accepted" -// @Router /v2/{name}/blobs/{digest} [delete] +// @Router /v2/{name}/blobs/{digest} [delete]. func (rh *RouteHandler) DeleteBlob(w http.ResponseWriter, r *http.Request) { vars := mux.Vars(r) name, ok := vars["name"] @@ -631,7 +631,7 @@ func (rh *RouteHandler) DeleteBlob(w http.ResponseWriter, r *http.Request) { // @Header 202 {string} Range "bytes=0-0" // @Failure 404 {string} string "not found" // @Failure 500 {string} string "internal server error" -// @Router /v2/{name}/blobs/uploads [post] +// @Router /v2/{name}/blobs/uploads [post]. func (rh *RouteHandler) CreateBlobUpload(w http.ResponseWriter, r *http.Request) { vars := mux.Vars(r) name, ok := vars["name"] @@ -736,7 +736,7 @@ func (rh *RouteHandler) CreateBlobUpload(w http.ResponseWriter, r *http.Request) // @Header 202 {string} Range "bytes=0-128" // @Failure 404 {string} string "not found" // @Failure 500 {string} string "internal server error" -// @Router /v2/{name}/blobs/uploads/{session_id} [get] +// @Router /v2/{name}/blobs/uploads/{session_id} [get]. func (rh *RouteHandler) GetBlobUpload(w http.ResponseWriter, r *http.Request) { vars := mux.Vars(r) name, ok := vars["name"] @@ -795,7 +795,7 @@ func (rh *RouteHandler) GetBlobUpload(w http.ResponseWriter, r *http.Request) { // @Failure 404 {string} string "not found" // @Failure 416 {string} string "range not satisfiable" // @Failure 500 {string} string "internal server error" -// @Router /v2/{name}/blobs/uploads/{session_id} [patch] +// @Router /v2/{name}/blobs/uploads/{session_id} [patch]. func (rh *RouteHandler) PatchBlobUpload(w http.ResponseWriter, r *http.Request) { vars := mux.Vars(r) name, ok := vars["name"] @@ -893,7 +893,7 @@ func (rh *RouteHandler) PatchBlobUpload(w http.ResponseWriter, r *http.Request) // @Header 200 {object} api.DistContentDigestKey // @Failure 404 {string} string "not found" // @Failure 500 {string} string "internal server error" -// @Router /v2/{name}/blobs/uploads/{session_id} [put] +// @Router /v2/{name}/blobs/uploads/{session_id} [put]. func (rh *RouteHandler) UpdateBlobUpload(w http.ResponseWriter, r *http.Request) { rh.c.Log.Info().Interface("headers", r.Header).Msg("HEADERS") vars := mux.Vars(r) @@ -1018,7 +1018,7 @@ finish: // @Success 200 {string} string "ok" // @Failure 404 {string} string "not found" // @Failure 500 {string} string "internal server error" -// @Router /v2/{name}/blobs/uploads/{session_id} [delete] +// @Router /v2/{name}/blobs/uploads/{session_id} [delete]. func (rh *RouteHandler) DeleteBlobUpload(w http.ResponseWriter, r *http.Request) { vars := mux.Vars(r) name, ok := vars["name"] @@ -1064,7 +1064,7 @@ type RepositoryList struct { // @Produce json // @Success 200 {object} api.RepositoryList // @Failure 500 {string} string "internal server error" -// @Router /v2/_catalog [get] +// @Router /v2/_catalog [get]. func (rh *RouteHandler) ListRepositories(w http.ResponseWriter, r *http.Request) { repos, err := rh.c.ImageStore.GetRepositories() if err != nil { diff --git a/pkg/cli/root.go b/pkg/cli/root.go index 740bac9b..81e0c4d8 100644 --- a/pkg/cli/root.go +++ b/pkg/cli/root.go @@ -12,7 +12,7 @@ import ( ) // metadataConfig reports metadata after parsing, which we use to track -// errors +// errors. func metadataConfig(md *mapstructure.Metadata) viper.DecoderConfigOption { return func(c *mapstructure.DecoderConfig) { c.Metadata = md diff --git a/pkg/compliance/v1_0_0/check.go b/pkg/compliance/v1_0_0/check.go index 7d4e4c0c..c3b16c38 100644 --- a/pkg/compliance/v1_0_0/check.go +++ b/pkg/compliance/v1_0_0/check.go @@ -1,5 +1,5 @@ -//nolint (dupl) -package v1_0_0 +// nolint: dupl +package v1_0_0 // nolint:stylecheck,golint import ( "bytes" @@ -14,7 +14,7 @@ import ( "github.com/anuvu/zot/pkg/compliance" godigest "github.com/opencontainers/go-digest" ispec "github.com/opencontainers/image-spec/specs-go/v1" - . "github.com/smartystreets/goconvey/convey" + . "github.com/smartystreets/goconvey/convey" // nolint:golint,stylecheck "github.com/smartystreets/goconvey/convey/reporting" "gopkg.in/resty.v1" ) @@ -29,8 +29,8 @@ func Location(baseURL string, resp *resty.Response) string { if loc[0] == '/' { return baseURL + loc } - return loc + return loc } func CheckWorkflows(t *testing.T, config *compliance.Config) { @@ -40,6 +40,7 @@ func CheckWorkflows(t *testing.T, config *compliance.Config) { if config.OutputJSON { outputJSONEnter() + defer outputJSONExit() } @@ -688,6 +689,7 @@ func CheckWorkflows(t *testing.T, config *compliance.Config) { }) } +// nolint: gochecknoglobals var ( old *os.File r *os.File @@ -709,7 +711,12 @@ func outputJSONEnter() { // copy the output in a separate goroutine so printing can't block indefinitely go func() { var buf bytes.Buffer - io.Copy(&buf, r) + + _, err := io.Copy(&buf, r) + if err != nil { + panic(err) + } + outC <- buf.String() }() } @@ -717,7 +724,9 @@ func outputJSONEnter() { func outputJSONExit() { // back to normal state w.Close() + os.Stdout = old // restoring the real stdout + out := <-outC // The output of JSON is combined with regular output, so we look for the @@ -734,13 +743,16 @@ func outputJSONExit() { func validateMinifyRawJSON(rawJSON string) string { var j interface{} + err := json.Unmarshal([]byte(rawJSON), &j) if err != nil { panic(err) } + rawJSONBytesMinified, err := json.Marshal(j) if err != nil { panic(err) } + return string(rawJSONBytesMinified) } diff --git a/pkg/compliance/v1_0_0/check_test.go b/pkg/compliance/v1_0_0/check_test.go index 4a0ab592..efcbca15 100644 --- a/pkg/compliance/v1_0_0/check_test.go +++ b/pkg/compliance/v1_0_0/check_test.go @@ -1,4 +1,3 @@ -//nolint (dupl) package v1_0_0_test import ( @@ -16,6 +15,7 @@ import ( "gopkg.in/resty.v1" ) +// nolint: gochecknoglobals var ( listenAddress = "127.0.0.1" ) @@ -39,12 +39,13 @@ func TestWorkflowsOutputJSON(t *testing.T) { }) } -// start local server on random open port +// start local server on random open port. func startServer() (*api.Controller, string) { portInt, err := freeport.GetFreePort() if err != nil { panic(err) } + randomPort := fmt.Sprintf("%d", portInt) fmt.Println(randomPort) @@ -52,12 +53,14 @@ func startServer() (*api.Controller, string) { config.HTTP.Address = listenAddress config.HTTP.Port = randomPort ctrl := api.NewController(config) + dir, err := ioutil.TempDir("", "oci-repo-test") if err != nil { panic(err) } ctrl.Config.Storage.RootDirectory = dir + go func() { // this blocks if err := ctrl.Run(); err != nil { @@ -66,12 +69,14 @@ func startServer() (*api.Controller, string) { }() baseURL := fmt.Sprintf("http://%s:%s", listenAddress, randomPort) + for { // poll until ready resp, _ := resty.R().Get(baseURL) if resp.StatusCode() == 404 { break } + time.Sleep(100 * time.Millisecond) } @@ -79,6 +84,13 @@ func startServer() (*api.Controller, string) { } func stopServer(ctrl *api.Controller) { - ctrl.Server.Shutdown(context.Background()) - os.RemoveAll(ctrl.Config.Storage.RootDirectory) + err := ctrl.Server.Shutdown(context.Background()) + if err != nil { + panic(err) + } + + err = os.RemoveAll(ctrl.Config.Storage.RootDirectory) + if err != nil { + panic(err) + } } diff --git a/pkg/log/log.go b/pkg/log/log.go index 2231b23d..ea5eafa1 100644 --- a/pkg/log/log.go +++ b/pkg/log/log.go @@ -9,7 +9,7 @@ import ( "github.com/rs/zerolog" ) -// Logger extends zerolog's Logger +// Logger extends zerolog's Logger. type Logger struct { zerolog.Logger } diff --git a/pkg/storage/cache.go b/pkg/storage/cache.go index bd863293..6eaaf1e7 100644 --- a/pkg/storage/cache.go +++ b/pkg/storage/cache.go @@ -20,7 +20,7 @@ type Cache struct { log zlog.Logger } -// Blob is a blob record +// Blob is a blob record. type Blob struct { Path string } diff --git a/pkg/storage/storage.go b/pkg/storage/storage.go index 40241847..afc8d892 100644 --- a/pkg/storage/storage.go +++ b/pkg/storage/storage.go @@ -24,6 +24,7 @@ import ( const ( // BlobUploadDir defines the upload directory for blob uploads. BlobUploadDir = ".uploads" + schemaVersion = 2 ) // BlobUpload models and upload request. @@ -68,22 +69,22 @@ func NewImageStore(rootDir string, gc bool, dedupe bool, log zlog.Logger) *Image return is } -// RLock read-lock +// RLock read-lock. func (is *ImageStore) RLock() { is.lock.RLock() } -// RUnlock read-unlock +// RUnlock read-unlock. func (is *ImageStore) RUnlock() { is.lock.RUnlock() } -// Lock write-lock +// Lock write-lock. func (is *ImageStore) Lock() { is.lock.Lock() } -// Unlock write-unlock +// Unlock write-unlock. func (is *ImageStore) Unlock() { is.lock.Unlock() } @@ -111,7 +112,7 @@ func (is *ImageStore) InitRepo(name string) error { is.log.Panic().Err(err).Msg("unable to marshal JSON") } - if err := ioutil.WriteFile(ilPath, buf, 0644); err != nil { + if err := ioutil.WriteFile(ilPath, buf, 0644); err != nil { //nolint: gosec is.log.Error().Err(err).Str("file", ilPath).Msg("unable to write file") return err } @@ -128,7 +129,7 @@ func (is *ImageStore) InitRepo(name string) error { is.log.Panic().Err(err).Msg("unable to marshal JSON") } - if err := ioutil.WriteFile(indexPath, buf, 0644); err != nil { + if err := ioutil.WriteFile(indexPath, buf, 0644); err != nil { //nolint: gosec is.log.Error().Err(err).Str("file", indexPath).Msg("unable to write file") return err } @@ -152,7 +153,7 @@ func (is *ImageStore) ValidateRepo(name string) (bool, error) { is.log.Error().Err(err).Str("dir", dir).Msg("unable to read directory") return false, errors.ErrRepoNotFound } - + // nolint:gomnd if len(files) < 3 { return false, errors.ErrRepoBadVersion } @@ -365,7 +366,7 @@ func (is *ImageStore) PutImageManifest(repo string, reference string, mediaType return "", errors.ErrBadManifest } - if m.SchemaVersion != 2 { + if m.SchemaVersion != schemaVersion { is.log.Error().Int("SchemaVersion", m.SchemaVersion).Msg("invalid manifest") return "", errors.ErrBadManifest } @@ -463,7 +464,7 @@ func (is *ImageStore) PutImageManifest(repo string, reference string, mediaType ensureDir(dir, is.log) file := path.Join(dir, mDigest.Encoded()) - if err := ioutil.WriteFile(file, body, 0644); err != nil { + if err := ioutil.WriteFile(file, body, 0600); err != nil { is.log.Error().Err(err).Str("file", file).Msg("unable to write") return "", err } @@ -479,7 +480,7 @@ func (is *ImageStore) PutImageManifest(repo string, reference string, mediaType return "", err } - if err := ioutil.WriteFile(file, buf, 0644); err != nil { + if err := ioutil.WriteFile(file, buf, 0644); err != nil { //nolint: gosec is.log.Error().Err(err).Str("file", file).Msg("unable to write") return "", err } @@ -556,7 +557,7 @@ func (is *ImageStore) DeleteImageManifest(repo string, reference string) error { return err } - if err := ioutil.WriteFile(file, buf, 0644); err != nil { + if err := ioutil.WriteFile(file, buf, 0644); err != nil { //nolint: gosec return err } @@ -771,7 +772,7 @@ func (is *ImageStore) FinishBlobUpload(repo string, uuid string, body io.Reader, return nil } -// FullBlobUpload handles a full blob upload, and no partial session is created +// FullBlobUpload handles a full blob upload, and no partial session is created. func (is *ImageStore) FullBlobUpload(repo string, body io.Reader, digest string) (string, int64, error) { if err := is.InitRepo(repo); err != nil { return "", -1, err @@ -836,11 +837,14 @@ func (is *ImageStore) FullBlobUpload(repo string, body io.Reader, digest string) return uuid, n, nil } -// nolint (interfacer) +// nolint:interfacer func (is *ImageStore) DedupeBlob(src string, dstDigest godigest.Digest, dst string) error { retry: is.log.Debug().Str("src", src).Str("dstDigest", dstDigest.String()).Str("dst", dst).Msg("dedupe: ENTER") + dstRecord, err := is.cache.GetBlob(dstDigest.String()) + + // nolint:goerr113 if err != nil && err != errors.ErrCacheMiss { is.log.Error().Err(err).Str("blobPath", dst).Msg("dedupe: unable to lookup blob record") return err @@ -849,14 +853,17 @@ retry: if dstRecord == "" { if err := is.cache.PutBlob(dstDigest.String(), dst); err != nil { is.log.Error().Err(err).Str("blobPath", dst).Msg("dedupe: unable to insert blob record") + return err } // move the blob from uploads to final dest if err := os.Rename(src, dst); err != nil { is.log.Error().Err(err).Str("src", src).Str("dst", dst).Msg("dedupe: unable to rename blob") + return err } + is.log.Debug().Str("src", src).Str("dst", dst).Msg("dedupe: rename") } else { dstRecord = path.Join(is.rootDir, dstRecord) @@ -866,7 +873,9 @@ retry: is.log.Error().Err(err).Str("blobPath", dstRecord).Msg("dedupe: unable to stat") // the actual blob on disk may have been removed by GC, so sync the cache if err := is.cache.DeleteBlob(dstDigest.String(), dst); err != nil { + // nolint:lll is.log.Error().Err(err).Str("dstDigest", dstDigest.String()).Str("dst", dst).Msg("dedupe: unable to delete blob record") + return err } goto retry @@ -874,11 +883,13 @@ retry: dstFi, err := os.Stat(dst) if err != nil && !os.IsNotExist(err) { is.log.Error().Err(err).Str("blobPath", dstRecord).Msg("dedupe: unable to stat") + return err } if !os.SameFile(dstFi, dstRecordFi) { if err := os.Link(dstRecord, dst); err != nil { is.log.Error().Err(err).Str("blobPath", dst).Str("link", dstRecord).Msg("dedupe: unable to hard link") + return err } } @@ -930,7 +941,7 @@ func (is *ImageStore) CheckBlob(repo string, digest string, // GetBlob returns a stream to read the blob. // FIXME: we should probably parse the manifest and use (digest, mediaType) as a -// blob selector instead of directly downloading the blob +// blob selector instead of directly downloading the blob. func (is *ImageStore) GetBlob(repo string, digest string, mediaType string) (io.Reader, int64, error) { d, err := godigest.Parse(digest) if err != nil { @@ -989,7 +1000,7 @@ func (is *ImageStore) DeleteBlob(repo string, digest string) error { // garbage collection // Scrub will clean up all unreferenced blobs. -// TODO +// TODO. func Scrub(dir string, fix bool) error { return nil } diff --git a/pkg/storage/storage_test.go b/pkg/storage/storage_test.go index 2d52631f..5254e955 100644 --- a/pkg/storage/storage_test.go +++ b/pkg/storage/storage_test.go @@ -563,7 +563,7 @@ func TestNegativeCases(t *testing.T) { So(err, ShouldNotBeNil) So(os.RemoveAll(path.Join(dir, "test")), ShouldBeNil) So(il.InitRepo("test"), ShouldBeNil) - So(ioutil.WriteFile(path.Join(dir, "test", "index.json"), []byte{}, 0755), ShouldBeNil) + So(ioutil.WriteFile(path.Join(dir, "test", "index.json"), []byte{}, 0600), ShouldBeNil) _, err = il.GetImageTags("test") So(err, ShouldNotBeNil) }) @@ -586,7 +586,7 @@ func TestNegativeCases(t *testing.T) { So(err, ShouldNotBeNil) So(os.RemoveAll(path.Join(dir, "test")), ShouldBeNil) So(il.InitRepo("test"), ShouldBeNil) - So(ioutil.WriteFile(path.Join(dir, "test", "index.json"), []byte{}, 0755), ShouldBeNil) + So(ioutil.WriteFile(path.Join(dir, "test", "index.json"), []byte{}, 0600), ShouldBeNil) _, _, _, err = il.GetImageManifest("test", "") So(err, ShouldNotBeNil) })