diff --git a/api/project.go b/api/project.go index 2d2aa1e473..9c1a4f6780 100644 --- a/api/project.go +++ b/api/project.go @@ -31,7 +31,6 @@ import ( type ProjectAPI struct { BaseAPI userID int - username string projectID int64 } @@ -185,7 +184,6 @@ func (p *ProjectAPI) FilterAccessLog() { } p.Data["json"] = accessLogList - log.Errorf("--- accessLog first record: %v ---", accessLogList[0]) p.ServeJSON() } diff --git a/api/repository.go b/api/repository.go index 252cb2fbc3..e259067a41 100644 --- a/api/repository.go +++ b/api/repository.go @@ -18,6 +18,7 @@ package api import ( "encoding/json" "net/http" + "os" "strconv" "strings" "time" @@ -26,6 +27,9 @@ import ( "github.com/vmware/harbor/models" svc_utils "github.com/vmware/harbor/service/utils" "github.com/vmware/harbor/utils/log" + "github.com/vmware/harbor/utils/registry" + "github.com/vmware/harbor/utils/registry/auth" + "github.com/vmware/harbor/utils/registry/errors" ) // RepositoryAPI handles request to /api/repositories /api/repositories/tags /api/repositories/manifests, the parm has to be put @@ -36,6 +40,7 @@ type RepositoryAPI struct { BaseAPI userID int username string + registry *registry.Registry } // Prepare will set a non existent user ID in case the request tries to view repositories under a project he doesn't has permission. @@ -53,6 +58,43 @@ func (ra *RepositoryAPI) Prepare() { } else { ra.username = username } + + var client *http.Client + + //no session, initialize a standard auth handler + if ra.userID == dao.NonExistUserID && len(ra.username) == 0 { + username, password, _ := ra.Ctx.Request.BasicAuth() + + credential := auth.NewBasicAuthCredential(username, password) + client = registry.NewClientStandardAuthHandlerEmbeded(credential) + log.Debug("initializing standard auth handler") + + } else { + // session works, initialize a username auth handler + username := ra.username + if len(username) == 0 { + user, err := dao.GetUser(models.User{ + UserID: ra.userID, + }) + if err != nil { + log.Errorf("error occurred whiling geting user for initializing a username auth handler: %v", err) + return + } + + username = user.Username + } + + client = registry.NewClientUsernameAuthHandlerEmbeded(username) + log.Debug("initializing username auth handler: %s", username) + } + + endpoint := os.Getenv("REGISTRY_URL") + r, err := registry.New(endpoint, client) + if err != nil { + log.Fatalf("error occurred while initializing auth handler for repository API: %v", err) + } + + ra.registry = r } // Get ... @@ -77,11 +119,13 @@ func (ra *RepositoryAPI) Get() { ra.RenderError(http.StatusForbidden, "") return } + repoList, err := svc_utils.GetRepoFromCache() if err != nil { log.Errorf("Failed to get repo from cache, error: %v", err) ra.RenderError(http.StatusInternalServerError, "internal sever error") } + projectName := p.Name q := ra.GetString("q") var resp []string @@ -105,6 +149,56 @@ func (ra *RepositoryAPI) Get() { ra.ServeJSON() } +// Delete ... +func (ra *RepositoryAPI) Delete() { + repoName := ra.GetString("repo_name") + if len(repoName) == 0 { + ra.CustomAbort(http.StatusBadRequest, "repo_name is nil") + } + + tags := []string{} + tag := ra.GetString("tag") + if len(tag) == 0 { + tagList, err := ra.registry.ListTag(repoName) + if err != nil { + e, ok := errors.ParseError(err) + if ok { + log.Info(e) + ra.CustomAbort(e.StatusCode, e.Message) + } else { + log.Error(err) + ra.CustomAbort(http.StatusInternalServerError, "internal error") + } + + } + tags = append(tags, tagList...) + + } else { + tags = append(tags, tag) + } + + for _, t := range tags { + if err := ra.registry.DeleteTag(repoName, t); err != nil { + e, ok := errors.ParseError(err) + if ok { + ra.CustomAbort(e.StatusCode, e.Message) + } else { + log.Error(err) + ra.CustomAbort(http.StatusInternalServerError, "internal error") + } + } + log.Infof("delete tag: %s %s", repoName, t) + } + + go func() { + log.Debug("refreshing catalog cache") + if err := svc_utils.RefreshCatalogCache(); err != nil { + log.Errorf("error occurred while refresh catalog cache: %v", err) + } + }() + +} + type tag struct { Name string `json:"name"` Tags []string `json:"tags"` @@ -116,15 +210,18 @@ func (ra *RepositoryAPI) GetTags() { var tags []string repoName := ra.GetString("repo_name") - result, err := svc_utils.RegistryAPIGet(svc_utils.BuildRegistryURL(repoName, "tags", "list"), ra.username) + tags, err := ra.registry.ListTag(repoName) if err != nil { - log.Errorf("Failed to get repo tags, repo name: %s, error: %v", repoName, err) - ra.RenderError(http.StatusInternalServerError, "Failed to get repo tags") - } else { - t := tag{} - json.Unmarshal(result, &t) - tags = t.Tags + e, ok := errors.ParseError(err) + if ok { + log.Info(e) + ra.CustomAbort(e.StatusCode, e.Message) + } else { + log.Error(err) + ra.CustomAbort(http.StatusInternalServerError, "internal error") + } } + ra.Data["json"] = tags ra.ServeJSON() } @@ -136,14 +233,19 @@ func (ra *RepositoryAPI) GetManifests() { item := models.RepoItem{} - result, err := svc_utils.RegistryAPIGet(svc_utils.BuildRegistryURL(repoName, "manifests", tag), ra.username) + _, _, payload, err := ra.registry.PullManifest(repoName, tag, registry.ManifestVersion1) if err != nil { - log.Errorf("Failed to get manifests for repo, repo name: %s, tag: %s, error: %v", repoName, tag, err) - ra.RenderError(http.StatusInternalServerError, "Internal Server Error") - return + e, ok := errors.ParseError(err) + if ok { + log.Info(e) + ra.CustomAbort(e.StatusCode, e.Message) + } else { + log.Error(err) + ra.CustomAbort(http.StatusInternalServerError, "internal error") + } } mani := models.Manifest{} - err = json.Unmarshal(result, &mani) + err = json.Unmarshal(payload, &mani) if err != nil { log.Errorf("Failed to decode json from response for manifests, repo name: %s, tag: %s, error: %v", repoName, tag, err) ra.RenderError(http.StatusInternalServerError, "Internal Server Error") @@ -157,7 +259,6 @@ func (ra *RepositoryAPI) GetManifests() { ra.RenderError(http.StatusInternalServerError, "Internal Server Error") return } - item.CreatedStr = item.Created.Format("2006-01-02 15:04:05") item.DurationDays = strconv.Itoa(int(time.Since(item.Created).Hours()/24)) + " days" ra.Data["json"] = item diff --git a/models/notification.go b/models/notification.go index 3f30ae7e42..0f608e2c6a 100644 --- a/models/notification.go +++ b/models/notification.go @@ -40,7 +40,6 @@ type Target struct { Digest string Repository string URL string `json:"Url"` - Tag string } // Actor holds information about actor. diff --git a/models/repo.go b/models/repo.go index 44abd9d9b0..c60910b82c 100644 --- a/models/repo.go +++ b/models/repo.go @@ -29,7 +29,6 @@ type RepoItem struct { ID string `json:"Id"` Parent string `json:"Parent"` Created time.Time `json:"Created"` - CreatedStr string `json:"CreatedStr"` DurationDays string `json:"Duration Days"` Author string `json:"Author"` Architecture string `json:"Architecture"` diff --git a/service/notification.go b/service/notification.go index 04611ec21b..ecd19d9b84 100644 --- a/service/notification.go +++ b/service/notification.go @@ -17,6 +17,8 @@ package service import ( "encoding/json" + "net/http" + "os" "regexp" "sort" "strings" @@ -25,6 +27,8 @@ import ( "github.com/vmware/harbor/models" svc_utils "github.com/vmware/harbor/service/utils" "github.com/vmware/harbor/utils/log" + "github.com/vmware/harbor/utils/registry" + "github.com/vmware/harbor/utils/registry/errors" "github.com/astaxie/beego" ) @@ -32,11 +36,7 @@ import ( // NotificationHandler handles request on /service/notifications/, which listens to registry's events. type NotificationHandler struct { beego.Controller -} - -type taglist struct { - Name string `json:"name"` - Tags []string `json:"tags"` + registry *registry.Registry } const manifestPattern = `^application/vnd.docker.distribution.manifest.v\d\+json` @@ -52,8 +52,9 @@ func (n *NotificationHandler) Post() { log.Errorf("error while decoding json: %v", err) return } - var username, action, repo, project, repo_tag, tag_url string + var username, action, repo, project, repo_tag, tag_url, digest string var matched bool + var client *http.Client for _, e := range notification.Events { matched, err = regexp.MatchString(manifestPattern, e.Target.MediaType) if err != nil { @@ -65,15 +66,27 @@ func (n *NotificationHandler) Post() { action = e.Action repo = e.Target.Repository tag_url = e.Target.URL - result, err1 := svc_utils.RegistryAPIGet(tag_url, username) + digest = e.Target.Digest + client = registry.NewClientUsernameAuthHandlerEmbeded(username) + log.Debug("initializing username auth handler: %s", username) + endpoint := os.Getenv("REGISTRY_URL") + r, err1 := registry.New(endpoint, client) if err1 != nil { - log.Errorf("Failed to get manifests for repo, repo name: %s, tag: %s, error: %v", repo, tag_url, err1) + log.Fatalf("error occurred while initializing auth handler for repository API: %v", err1) + + } + n.registry = r + + _, _, payload, err2 := n.registry.PullManifest(repo, digest, registry.ManifestVersion1) + + if err2 != nil { + log.Errorf("Failed to get manifests for repo, repo name: %s, tag: %s, error: %v", repo, tag_url, err2) return } maniDig := models.ManifestDigest{} - err = json.Unmarshal(result, &maniDig) + err = json.Unmarshal(payload, &maniDig) if err != nil { log.Errorf("Failed to decode json from response for manifests, repo name: %s, tag: %s, error: %v", repo, tag_url, err) return @@ -85,36 +98,48 @@ func (n *NotificationHandler) Post() { digestLayers = append(digestLayers, diglayer.Digest) } - result, err = svc_utils.RegistryAPIGet(svc_utils.BuildRegistryURL(repo, "tags", "list"), username) + tags, err := n.registry.ListTag(repo) if err != nil { - log.Errorf("Failed to get repo tags, repo name: %s, error: %v", repo, err) - } else { - t := taglist{} - json.Unmarshal(result, &t) - for _, tag := range t.Tags { - result, err = svc_utils.RegistryAPIGet(svc_utils.BuildRegistryURL(repo, "manifests", tag), username) - if err != nil { - log.Errorf("Failed to get repo tags, repo name: %s, error: %v", repo, err) - continue - } - taginfo := models.Manifest{} - err = json.Unmarshal(result, &taginfo) - if err != nil { - log.Errorf("Failed to decode json from response for manifests, repo name: %s, tag: %s, error: %v", repo, tag, err) - continue - } - for _, fslayer := range taginfo.FsLayers { - tagLayers = append(tagLayers, fslayer.BlobSum) - } - - sort.Strings(digestLayers) - sort.Strings(tagLayers) - eq := compStringArray(digestLayers, tagLayers) - if eq { - repo_tag = tag - break - } + e, ok := errors.ParseError(err) + if ok { + log.Info(e) + } else { + log.Error(err) } + return + } + + log.Infof("tags : %v ", tags) + + for _, tag := range tags { + _, _, payload, err := n.registry.PullManifest(repo, tag, registry.ManifestVersion1) + if err != nil { + e, ok := errors.ParseError(err) + if ok { + log.Info(e) + } else { + log.Error(err) + } + continue + } + taginfo := models.Manifest{} + err = json.Unmarshal(payload, &taginfo) + if err != nil { + log.Errorf("Failed to decode json from response for manifests, repo name: %s, tag: %s, error: %v", repo, tag, err) + continue + } + for _, fslayer := range taginfo.FsLayers { + tagLayers = append(tagLayers, fslayer.BlobSum) + } + + sort.Strings(digestLayers) + sort.Strings(tagLayers) + eq := compStringArray(digestLayers, tagLayers) + if eq { + repo_tag = tag + break + } + } if strings.Contains(repo, "/") {