diff --git a/src/common/api/base.go b/src/common/api/base.go index 52696b615..255bd0ff8 100644 --- a/src/common/api/base.go +++ b/src/common/api/base.go @@ -23,6 +23,7 @@ import ( "github.com/astaxie/beego/validation" "github.com/vmware/harbor/src/common/dao" "github.com/vmware/harbor/src/common/models" + http_error "github.com/vmware/harbor/src/common/utils/error" "github.com/vmware/harbor/src/common/utils/log" "github.com/vmware/harbor/src/ui/auth" @@ -80,6 +81,21 @@ func (b *BaseAPI) HandleInternalServerError(text string) { b.RenderError(http.StatusInternalServerError, "") } +// ParseAndHandleError : if the err is an instance of utils/error.Error, +// return the status code and the detail message contained in err, otherwise +// return 500 +func (b *BaseAPI) ParseAndHandleError(text string, err error) { + if err == nil { + return + } + log.Errorf("%s: %v", text, err) + if e, ok := err.(*http_error.HTTPError); ok { + b.RenderError(e.StatusCode, e.Detail) + return + } + b.RenderError(http.StatusInternalServerError, "") +} + // Render returns nil as it won't render template func (b *BaseAPI) Render() error { return nil diff --git a/src/common/security/admiral/authcontext/authcontext.go b/src/common/security/admiral/authcontext/authcontext.go index 2ca9ed5f9..a980f801b 100644 --- a/src/common/security/admiral/authcontext/authcontext.go +++ b/src/common/security/admiral/authcontext/authcontext.go @@ -26,6 +26,7 @@ import ( "github.com/vmware/harbor/src/common" "github.com/vmware/harbor/src/common/models" "github.com/vmware/harbor/src/common/utils" + http_error "github.com/vmware/harbor/src/common/utils/error" "github.com/vmware/harbor/src/common/utils/log" ) @@ -202,7 +203,10 @@ func send(client *http.Client, req *http.Request) (*AuthContext, error) { } if resp.StatusCode != http.StatusOK { - return nil, fmt.Errorf("unexpected status code: %d %s", resp.StatusCode, string(data)) + return nil, &http_error.HTTPError{ + StatusCode: resp.StatusCode, + Detail: string(data), + } } ctx := &AuthContext{} diff --git a/src/common/utils/error/error.go b/src/common/utils/error/error.go index a95773fc7..2f7ec33cf 100644 --- a/src/common/utils/error/error.go +++ b/src/common/utils/error/error.go @@ -18,13 +18,13 @@ import ( "fmt" ) -// Error : if response is returned but the status code is not 200, an Error instance will be returned -type Error struct { +// HTTPError : if response is returned but the status code is not 200, an Error instance will be returned +type HTTPError struct { StatusCode int Detail string } // Error returns the details as string -func (e *Error) Error() string { +func (e *HTTPError) Error() string { return fmt.Sprintf("%d %s", e.StatusCode, e.Detail) } diff --git a/src/common/utils/error/error_test.go b/src/common/utils/error/error_test.go index 8493021a0..edc4aa226 100644 --- a/src/common/utils/error/error_test.go +++ b/src/common/utils/error/error_test.go @@ -18,7 +18,7 @@ import ( ) func TestError(t *testing.T) { - err := &Error{ + err := &HTTPError{ StatusCode: 404, Detail: "not found", } diff --git a/src/common/utils/registry/auth/util.go b/src/common/utils/registry/auth/util.go index 719418294..373c95890 100644 --- a/src/common/utils/registry/auth/util.go +++ b/src/common/utils/registry/auth/util.go @@ -78,7 +78,7 @@ func getToken(client *http.Client, credential Credential, realm, service string, return nil, err } if resp.StatusCode != http.StatusOK { - return nil, ®istry_error.Error{ + return nil, ®istry_error.HTTPError{ StatusCode: resp.StatusCode, Detail: string(data), } diff --git a/src/common/utils/registry/registry.go b/src/common/utils/registry/registry.go index 19e5638e5..d560e4346 100644 --- a/src/common/utils/registry/registry.go +++ b/src/common/utils/registry/registry.go @@ -126,7 +126,7 @@ func (r *Registry) Catalog() ([]string, error) { suffix = "" } } else { - return repos, ®istry_error.Error{ + return repos, ®istry_error.HTTPError{ StatusCode: resp.StatusCode, Detail: string(b), } @@ -157,7 +157,7 @@ func (r *Registry) Ping() error { return err } - return ®istry_error.Error{ + return ®istry_error.HTTPError{ StatusCode: resp.StatusCode, Detail: string(b), } diff --git a/src/common/utils/registry/repository.go b/src/common/utils/registry/repository.go index b2cebc54c..e08b3ed3f 100644 --- a/src/common/utils/registry/repository.go +++ b/src/common/utils/registry/repository.go @@ -72,7 +72,7 @@ func NewRepositoryWithModifiers(name, endpoint string, insecure bool, modifiers func parseError(err error) error { if urlErr, ok := err.(*url.Error); ok { - if regErr, ok := urlErr.Err.(*registry_error.Error); ok { + if regErr, ok := urlErr.Err.(*registry_error.HTTPError); ok { return regErr } } @@ -120,7 +120,7 @@ func (r *Repository) ListTag() ([]string, error) { return tags, nil } - return tags, ®istry_error.Error{ + return tags, ®istry_error.HTTPError{ StatusCode: resp.StatusCode, Detail: string(b), } @@ -160,7 +160,7 @@ func (r *Repository) ManifestExist(reference string) (digest string, exist bool, return } - err = ®istry_error.Error{ + err = ®istry_error.HTTPError{ StatusCode: resp.StatusCode, Detail: string(b), } @@ -197,7 +197,7 @@ func (r *Repository) PullManifest(reference string, acceptMediaTypes []string) ( return } - err = ®istry_error.Error{ + err = ®istry_error.HTTPError{ StatusCode: resp.StatusCode, Detail: string(b), } @@ -232,7 +232,7 @@ func (r *Repository) PushManifest(reference, mediaType string, payload []byte) ( return } - err = ®istry_error.Error{ + err = ®istry_error.HTTPError{ StatusCode: resp.StatusCode, Detail: string(b), } @@ -263,7 +263,7 @@ func (r *Repository) DeleteManifest(digest string) error { return err } - return ®istry_error.Error{ + return ®istry_error.HTTPError{ StatusCode: resp.StatusCode, Detail: string(b), } @@ -277,7 +277,7 @@ func (r *Repository) DeleteTag(tag string) error { } if !exist { - return ®istry_error.Error{ + return ®istry_error.HTTPError{ StatusCode: http.StatusNotFound, } } @@ -312,7 +312,7 @@ func (r *Repository) BlobExist(digest string) (bool, error) { return false, err } - return false, ®istry_error.Error{ + return false, ®istry_error.HTTPError{ StatusCode: resp.StatusCode, Detail: string(b), } @@ -348,7 +348,7 @@ func (r *Repository) PullBlob(digest string) (size int64, data io.ReadCloser, er return } - err = ®istry_error.Error{ + err = ®istry_error.HTTPError{ StatusCode: resp.StatusCode, Detail: string(b), } @@ -379,7 +379,7 @@ func (r *Repository) initiateBlobUpload(name string) (location, uploadUUID strin return } - err = ®istry_error.Error{ + err = ®istry_error.HTTPError{ StatusCode: resp.StatusCode, Detail: string(b), } @@ -409,7 +409,7 @@ func (r *Repository) monolithicBlobUpload(location, digest string, size int64, d return err } - return ®istry_error.Error{ + return ®istry_error.HTTPError{ StatusCode: resp.StatusCode, Detail: string(b), } @@ -447,7 +447,7 @@ func (r *Repository) DeleteBlob(digest string) error { return err } - return ®istry_error.Error{ + return ®istry_error.HTTPError{ StatusCode: resp.StatusCode, Detail: string(b), } diff --git a/src/common/utils/registry/repository_test.go b/src/common/utils/registry/repository_test.go index 22c7fd62d..63d43ed64 100644 --- a/src/common/utils/registry/repository_test.go +++ b/src/common/utils/registry/repository_test.go @@ -396,10 +396,10 @@ func TestListTag(t *testing.T) { func TestParseError(t *testing.T) { err := &url.Error{ - Err: ®istry_error.Error{}, + Err: ®istry_error.HTTPError{}, } e := parseError(err) - if _, ok := e.(*registry_error.Error); !ok { + if _, ok := e.(*registry_error.HTTPError); !ok { t.Errorf("error type does not match registry error") } } diff --git a/src/ui/api/member.go b/src/ui/api/member.go index 3f3638fc3..d4f7b6841 100644 --- a/src/ui/api/member.go +++ b/src/ui/api/member.go @@ -67,8 +67,7 @@ func (pma *ProjectMemberAPI) Prepare() { } project, err := pma.ProjectMgr.Get(pid) if err != nil { - pma.HandleInternalServerError( - fmt.Sprintf("failed to get project %d: %v", pid, err)) + pma.ParseAndHandleError(fmt.Sprintf("failed to get project %d", pid), err) return } if project == nil { diff --git a/src/ui/api/project.go b/src/ui/api/project.go index 55c4298f5..4e580ed62 100644 --- a/src/ui/api/project.go +++ b/src/ui/api/project.go @@ -59,8 +59,7 @@ func (p *ProjectAPI) Prepare() { project, err := p.ProjectMgr.Get(id) if err != nil { - p.HandleInternalServerError(fmt.Sprintf("failed to get project %d: %v", - id, err)) + p.ParseAndHandleError(fmt.Sprintf("failed to get project %d", id), err) return } @@ -107,8 +106,8 @@ func (p *ProjectAPI) Post() { exist, err := p.ProjectMgr.Exist(pro.Name) if err != nil { - p.HandleInternalServerError(fmt.Sprintf("failed to check the existence of project %s: %v", - pro.Name, err)) + p.ParseAndHandleError(fmt.Sprintf("failed to check the existence of project %s", + pro.Name), err) return } if exist { @@ -126,12 +125,12 @@ func (p *ProjectAPI) Post() { AutomaticallyScanImagesOnPush: pro.AutomaticallyScanImagesOnPush, }) if err != nil { - log.Errorf("Failed to add project, error: %v", err) dup, _ := regexp.MatchString(dupProjectPattern, err.Error()) if dup { + log.Debugf("conflict %s", pro.Name) p.RenderError(http.StatusConflict, "") } else { - p.RenderError(http.StatusInternalServerError, "Failed to add project") + p.ParseAndHandleError("failed to add project", err) } return } @@ -163,8 +162,7 @@ func (p *ProjectAPI) Head() { project, err := p.ProjectMgr.Get(name) if err != nil { - p.HandleInternalServerError(fmt.Sprintf("failed to get project %s: %v", - name, err)) + p.ParseAndHandleError(fmt.Sprintf("failed to get project %s", name), err) return } @@ -223,8 +221,7 @@ func (p *ProjectAPI) Delete() { } if err = p.ProjectMgr.Delete(p.project.ProjectID); err != nil { - p.HandleInternalServerError( - fmt.Sprintf("failed to delete project %d: %v", p.project.ProjectID, err)) + p.ParseAndHandleError(fmt.Sprintf("failed to delete project %d", p.project.ProjectID), err) return } @@ -299,13 +296,13 @@ func (p *ProjectAPI) List() { total, err := p.ProjectMgr.GetTotal(query, base) if err != nil { - p.HandleInternalServerError(fmt.Sprintf("failed to get total of projects: %v", err)) + p.ParseAndHandleError("failed to get total of projects", err) return } projects, err := p.ProjectMgr.GetAll(query, base) if err != nil { - p.HandleInternalServerError(fmt.Sprintf("failed to get projects: %v", err)) + p.ParseAndHandleError("failed to get projects", err) return } @@ -359,8 +356,8 @@ func (p *ProjectAPI) ToggleProjectPublic() { &models.Project{ Public: req.Public, }); err != nil { - p.HandleInternalServerError(fmt.Sprintf("failed to update project %d: %v", - p.project.ProjectID, err)) + p.ParseAndHandleError(fmt.Sprintf("failed to update project %d", + p.project.ProjectID), err) return } } diff --git a/src/ui/api/replication_policy.go b/src/ui/api/replication_policy.go index 6445134fc..ebde92340 100644 --- a/src/ui/api/replication_policy.go +++ b/src/ui/api/replication_policy.go @@ -86,8 +86,8 @@ func (pa *RepPolicyAPI) List() { for _, policy := range policies { project, err := pa.ProjectMgr.Get(policy.ProjectID) if err != nil { - pa.HandleInternalServerError(fmt.Sprintf( - "failed to get project %d: %v", policy.ProjectID, err)) + pa.ParseAndHandleError(fmt.Sprintf( + "failed to get project %d", policy.ProjectID), err) return } if project != nil { @@ -118,8 +118,8 @@ func (pa *RepPolicyAPI) Post() { project, err := pa.ProjectMgr.Get(policy.ProjectID) if err != nil { - log.Errorf("failed to get project %d: %v", policy.ProjectID, err) - pa.CustomAbort(http.StatusInternalServerError, http.StatusText(http.StatusInternalServerError)) + pa.ParseAndHandleError(fmt.Sprintf("failed to get project %d", policy.ProjectID), err) + return } if project == nil { diff --git a/src/ui/api/repository.go b/src/ui/api/repository.go index fc6b2d925..789e9a757 100644 --- a/src/ui/api/repository.go +++ b/src/ui/api/repository.go @@ -84,8 +84,8 @@ func (ra *RepositoryAPI) Get() { exist, err := ra.ProjectMgr.Exist(projectID) if err != nil { - ra.HandleInternalServerError(fmt.Sprintf("failed to check the existence of project %d: %v", - projectID, err)) + ra.ParseAndHandleError(fmt.Sprintf("failed to check the existence of project %d", + projectID), err) return } @@ -169,8 +169,8 @@ func (ra *RepositoryAPI) Delete() { projectName, _ := utils.ParseRepository(repoName) project, err := ra.ProjectMgr.Get(projectName) if err != nil { - ra.HandleInternalServerError(fmt.Sprintf("failed to get the project %s: %v", - projectName, err)) + ra.ParseAndHandleError(fmt.Sprintf("failed to get the project %s", + projectName), err) return } @@ -200,7 +200,7 @@ func (ra *RepositoryAPI) Delete() { if len(tag) == 0 { tagList, err := rc.ListTag() if err != nil { - if regErr, ok := err.(*registry_error.Error); ok { + if regErr, ok := err.(*registry_error.HTTPError); ok { ra.CustomAbort(regErr.StatusCode, regErr.Detail) } @@ -242,7 +242,7 @@ func (ra *RepositoryAPI) Delete() { for _, t := range tags { if err = rc.DeleteTag(t); err != nil { - if regErr, ok := err.(*registry_error.Error); ok { + if regErr, ok := err.(*registry_error.HTTPError); ok { if regErr.StatusCode == http.StatusNotFound { continue } @@ -335,8 +335,8 @@ func (ra *RepositoryAPI) GetTags() { projectName, _ := utils.ParseRepository(repoName) exist, err := ra.ProjectMgr.Exist(projectName) if err != nil { - ra.HandleInternalServerError(fmt.Sprintf("failed to check the existence of project %s: %v", - projectName, err)) + ra.ParseAndHandleError(fmt.Sprintf("failed to check the existence of project %s", + projectName), err) return } @@ -475,8 +475,8 @@ func (ra *RepositoryAPI) GetManifests() { projectName, _ := utils.ParseRepository(repoName) exist, err := ra.ProjectMgr.Exist(projectName) if err != nil { - ra.HandleInternalServerError(fmt.Sprintf("failed to check the existence of project %s: %v", - projectName, err)) + ra.ParseAndHandleError(fmt.Sprintf("failed to check the existence of project %s", + projectName), err) return } @@ -503,7 +503,7 @@ func (ra *RepositoryAPI) GetManifests() { manifest, err := getManifest(rc, tag, version) if err != nil { - if regErr, ok := err.(*registry_error.Error); ok { + if regErr, ok := err.(*registry_error.HTTPError); ok { ra.CustomAbort(regErr.StatusCode, regErr.Detail) } @@ -577,7 +577,7 @@ func (ra *RepositoryAPI) GetTopRepos() { projectIDs := []int64{} projects, err := ra.ProjectMgr.GetPublic() if err != nil { - ra.HandleInternalServerError(fmt.Sprintf("failed to get public projects: %v", err)) + ra.ParseAndHandleError("failed to get public projects", err) return } if ra.SecurityCtx.IsAuthenticated() { @@ -617,8 +617,8 @@ func (ra *RepositoryAPI) GetSignatures() { projectName, _ := utils.ParseRepository(repoName) exist, err := ra.ProjectMgr.Exist(projectName) if err != nil { - ra.HandleInternalServerError(fmt.Sprintf("failed to check the existence of project %s: %v", - projectName, err)) + ra.ParseAndHandleError(fmt.Sprintf("failed to check the existence of project %s", + projectName), err) return } @@ -658,8 +658,8 @@ func (ra *RepositoryAPI) ScanImage() { projectName, _ := utils.ParseRepository(repoName) exist, err := ra.ProjectMgr.Exist(projectName) if err != nil { - ra.HandleInternalServerError(fmt.Sprintf("failed to check the existence of project %s: %v", - projectName, err)) + ra.ParseAndHandleError(fmt.Sprintf("failed to check the existence of project %s", + projectName), err) return } if !exist { @@ -776,7 +776,7 @@ func (ra *RepositoryAPI) checkExistence(repository, tag string) (bool, string, e project, _ := utils.ParseRepository(repository) exist, err := ra.ProjectMgr.Exist(project) if err != nil { - return false, "", fmt.Errorf("failed to check the existence of project %s: %v", project, err) + return false, "", err } if !exist { log.Errorf("project %s not found", project) diff --git a/src/ui/api/search.go b/src/ui/api/search.go index 5f90c0823..a17be9a31 100644 --- a/src/ui/api/search.go +++ b/src/ui/api/search.go @@ -51,15 +51,13 @@ func (s *SearchAPI) Get() { if isSysAdmin { projects, err = s.ProjectMgr.GetAll(nil) if err != nil { - s.HandleInternalServerError(fmt.Sprintf( - "failed to get projects: %v", err)) + s.ParseAndHandleError("failed to get projects", err) return } } else { projects, err = s.ProjectMgr.GetPublic() if err != nil { - s.HandleInternalServerError(fmt.Sprintf( - "failed to get projects: %v", err)) + s.ParseAndHandleError("failed to get projects", err) return } if isAuthenticated { diff --git a/src/ui/api/statistic.go b/src/ui/api/statistic.go index c9eccd9c0..817860714 100644 --- a/src/ui/api/statistic.go +++ b/src/ui/api/statistic.go @@ -59,8 +59,7 @@ func (s *StatisticAPI) Get() { statistic := map[string]int64{} pubProjs, err := s.ProjectMgr.GetPublic() if err != nil { - s.HandleInternalServerError(fmt.Sprintf( - "failed to get public projects: %v", err)) + s.ParseAndHandleError("failed to get public projects", err) return } @@ -102,8 +101,8 @@ func (s *StatisticAPI) Get() { }, }) if err != nil { - s.HandleInternalServerError(fmt.Sprintf( - "failed to get projects of user %s: %v", s.username, err)) + s.ParseAndHandleError(fmt.Sprintf( + "failed to get projects of user %s", s.username), err) return } diff --git a/src/ui/api/target.go b/src/ui/api/target.go index 216453158..eaa0c3f98 100644 --- a/src/ui/api/target.go +++ b/src/ui/api/target.go @@ -81,7 +81,7 @@ func (t *TargetAPI) ping(endpoint, username, password string) { } if err = registry.Ping(); err != nil { - if regErr, ok := err.(*registry_error.Error); ok { + if regErr, ok := err.(*registry_error.HTTPError); ok { t.CustomAbort(regErr.StatusCode, regErr.Detail) } diff --git a/src/ui/api/utils.go b/src/ui/api/utils.go index a9ad1c364..2fff9de26 100644 --- a/src/ui/api/utils.go +++ b/src/ui/api/utils.go @@ -440,7 +440,7 @@ func getReposByProject(name string, keyword ...string) ([]string, error) { func repositoryExist(name string, client *registry.Repository) (bool, error) { tags, err := client.ListTag() if err != nil { - if regErr, ok := err.(*registry_error.Error); ok && regErr.StatusCode == http.StatusNotFound { + if regErr, ok := err.(*registry_error.HTTPError); ok && regErr.StatusCode == http.StatusNotFound { return false, nil } return false, err diff --git a/src/ui/projectmanager/pms/pm.go b/src/ui/projectmanager/pms/pm.go index 571f81789..f18eb7d2b 100644 --- a/src/ui/projectmanager/pms/pm.go +++ b/src/ui/projectmanager/pms/pm.go @@ -412,7 +412,7 @@ func (p *ProjectManager) send(method, path string, body io.Reader) ([]byte, erro } if resp.StatusCode != http.StatusOK { - return nil, &er.Error{ + return nil, &er.HTTPError{ StatusCode: resp.StatusCode, Detail: string(b), }