func filterRepositories(repositories []string, projects []models.Project, keyword string) []map[string]interface{} { i, j := 0, 0 result := []map[string]interface{}{} for i < len(repositories) && j < len(projects) { r := repositories[i] p, _ := utils.ParseRepository(r) d := strings.Compare(p, projects[j].Name) if d < 0 { i++ continue } else if d == 0 { i++ if len(keyword) != 0 && !strings.Contains(r, keyword) { continue } entry := make(map[string]interface{}) entry["repository_name"] = r entry["project_name"] = projects[j].Name entry["project_id"] = projects[j].ProjectID entry["project_public"] = projects[j].Public result = append(result, entry) } else { j++ } } return result }
// Post handles POST request, and records audit log or refreshes cache based on event. func (n *NotificationHandler) Post() { var notification models.Notification err := json.Unmarshal(n.Ctx.Input.CopyBody(1<<32), ¬ification) if err != nil { log.Errorf("failed to decode notification: %v", err) return } events, err := filterEvents(¬ification) if err != nil { log.Errorf("failed to filter events: %v", err) return } for _, event := range events { repository := event.Target.Repository project, _ := utils.ParseRepository(repository) tag := event.Target.Tag action := event.Action user := event.Actor.Name if len(user) == 0 { user = "******" } go func() { if err := dao.AccessLog(user, project, repository, tag, action); err != nil { log.Errorf("failed to add access log: %v", err) } }() if action == "push" { go func() { exist := dao.RepositoryExists(repository) if exist { return } log.Debugf("Add repository %s into DB.", repository) repoRecord := models.RepoRecord{Name: repository, OwnerName: user, ProjectName: project} if err := dao.AddRepository(repoRecord); err != nil { log.Errorf("Error happens when adding repository: %v", err) } if err := cache.RefreshCatalogCache(); err != nil { log.Errorf("failed to refresh cache: %v", err) } }() go api.TriggerReplicationByRepository(repository, []string{tag}, models.RepOpTransfer) } if action == "pull" { go func() { log.Debugf("Increase the repository %s pull count.", repository) if err := dao.IncreasePullCount(repository); err != nil { log.Errorf("Error happens when increasing pull count: %v", repository) } }() } } }
// GetTags handles GET /api/repositories/tags func (ra *RepositoryAPI) GetTags() { repoName := ra.GetString("repo_name") if len(repoName) == 0 { ra.CustomAbort(http.StatusBadRequest, "repo_name is nil") } projectName, _ := utils.ParseRepository(repoName) project, err := dao.GetProjectByName(projectName) if err != nil { log.Errorf("failed to get project %s: %v", projectName, err) ra.CustomAbort(http.StatusInternalServerError, "") } if project == nil { ra.CustomAbort(http.StatusNotFound, fmt.Sprintf("project %s not found", projectName)) } if project.Public == 0 { userID := ra.ValidateUser() if !checkProjectPermission(userID, project.ProjectID) { ra.CustomAbort(http.StatusForbidden, "") } } rc, err := ra.initRepositoryClient(repoName) if err != nil { log.Errorf("error occurred while initializing repository client for %s: %v", repoName, err) ra.CustomAbort(http.StatusInternalServerError, "internal error") } tags := []string{} ts, err := rc.ListTag() if err != nil { regErr, ok := err.(*registry_error.Error) if !ok { log.Errorf("error occurred while listing tags of %s: %v", repoName, err) ra.CustomAbort(http.StatusInternalServerError, "internal error") } // TODO remove the logic if the bug of registry is fixed // It's a workaround for a bug of registry: when listing tags of // a repository which is being pushed, a "NAME_UNKNOWN" error will // been returned, while the catalog API can list this repository. if regErr.StatusCode != http.StatusNotFound { ra.CustomAbort(regErr.StatusCode, regErr.Detail) } } tags = append(tags, ts...) sort.Strings(tags) ra.Data["json"] = tags ra.ServeJSON() }
// GetManifests handles GET /api/repositories/manifests func (ra *RepositoryAPI) GetManifests() { repoName := ra.GetString("repo_name") tag := ra.GetString("tag") if len(repoName) == 0 || len(tag) == 0 { ra.CustomAbort(http.StatusBadRequest, "repo_name or tag is nil") } version := ra.GetString("version") if len(version) == 0 { version = "v2" } if version != "v1" && version != "v2" { ra.CustomAbort(http.StatusBadRequest, "version should be v1 or v2") } projectName, _ := utils.ParseRepository(repoName) project, err := dao.GetProjectByName(projectName) if err != nil { log.Errorf("failed to get project %s: %v", projectName, err) ra.CustomAbort(http.StatusInternalServerError, "") } if project == nil { ra.CustomAbort(http.StatusNotFound, fmt.Sprintf("project %s not found", projectName)) } if project.Public == 0 { userID := ra.ValidateUser() if !checkProjectPermission(userID, project.ProjectID) { ra.CustomAbort(http.StatusForbidden, "") } } rc, err := ra.initRepositoryClient(repoName) if err != nil { log.Errorf("error occurred while initializing repository client for %s: %v", repoName, err) ra.CustomAbort(http.StatusInternalServerError, "internal error") } result := struct { Manifest interface{} `json:"manifest"` Config interface{} `json:"config,omitempty" ` }{} mediaTypes := []string{} switch version { case "v1": mediaTypes = append(mediaTypes, schema1.MediaTypeManifest) case "v2": mediaTypes = append(mediaTypes, schema2.MediaTypeManifest) } _, mediaType, payload, err := rc.PullManifest(tag, mediaTypes) if err != nil { if regErr, ok := err.(*registry_error.Error); ok { ra.CustomAbort(regErr.StatusCode, regErr.Detail) } log.Errorf("error occurred while getting manifest of %s:%s: %v", repoName, tag, err) ra.CustomAbort(http.StatusInternalServerError, "internal error") } manifest, _, err := registry.UnMarshal(mediaType, payload) if err != nil { log.Errorf("an error occurred while parsing manifest of %s:%s: %v", repoName, tag, err) ra.CustomAbort(http.StatusInternalServerError, "") } result.Manifest = manifest deserializedmanifest, ok := manifest.(*schema2.DeserializedManifest) if ok { _, data, err := rc.PullBlob(deserializedmanifest.Target().Digest.String()) if err != nil { log.Errorf("failed to get config of manifest %s:%s: %v", repoName, tag, err) ra.CustomAbort(http.StatusInternalServerError, "") } b, err := ioutil.ReadAll(data) if err != nil { log.Errorf("failed to read config of manifest %s:%s: %v", repoName, tag, err) ra.CustomAbort(http.StatusInternalServerError, "") } result.Config = string(b) } ra.Data["json"] = result ra.ServeJSON() }
// Delete ... func (ra *RepositoryAPI) Delete() { repoName := ra.GetString("repo_name") if len(repoName) == 0 { ra.CustomAbort(http.StatusBadRequest, "repo_name is nil") } projectName, _ := utils.ParseRepository(repoName) project, err := dao.GetProjectByName(projectName) if err != nil { log.Errorf("failed to get project %s: %v", projectName, err) ra.CustomAbort(http.StatusInternalServerError, "") } if project == nil { ra.CustomAbort(http.StatusNotFound, fmt.Sprintf("project %s not found", projectName)) } if project.Public == 0 { userID := ra.ValidateUser() if !hasProjectAdminRole(userID, project.ProjectID) { ra.CustomAbort(http.StatusForbidden, "") } } rc, err := ra.initRepositoryClient(repoName) if err != nil { log.Errorf("error occurred while initializing repository client for %s: %v", repoName, err) ra.CustomAbort(http.StatusInternalServerError, "internal error") } tags := []string{} tag := ra.GetString("tag") if len(tag) == 0 { tagList, err := rc.ListTag() if err != nil { if regErr, ok := err.(*registry_error.Error); ok { ra.CustomAbort(regErr.StatusCode, regErr.Detail) } log.Errorf("error occurred while listing tags of %s: %v", repoName, err) ra.CustomAbort(http.StatusInternalServerError, "internal error") } // TODO remove the logic if the bug of registry is fixed if len(tagList) == 0 { ra.CustomAbort(http.StatusNotFound, http.StatusText(http.StatusNotFound)) } tags = append(tags, tagList...) } else { tags = append(tags, tag) } user, _, ok := ra.Ctx.Request.BasicAuth() if !ok { user, err = ra.getUsername() if err != nil { log.Errorf("failed to get user: %v", err) } } for _, t := range tags { if err := rc.DeleteTag(t); err != nil { if regErr, ok := err.(*registry_error.Error); ok { if regErr.StatusCode != http.StatusNotFound { ra.CustomAbort(regErr.StatusCode, regErr.Detail) } } else { log.Errorf("error occurred while deleting tag %s:%s: %v", repoName, t, err) ra.CustomAbort(http.StatusInternalServerError, "internal error") } } log.Infof("delete tag: %s:%s", repoName, t) go TriggerReplicationByRepository(repoName, []string{t}, models.RepOpDelete) go func(tag string) { if err := dao.AccessLog(user, projectName, repoName, tag, "delete"); err != nil { log.Errorf("failed to add access log: %v", err) } }(t) } exist, err := repositoryExist(repoName, rc) if err != nil { log.Errorf("failed to check the existence of repository %s: %v", repoName, err) ra.CustomAbort(http.StatusInternalServerError, "") } if !exist { if err = dao.DeleteRepository(repoName); err != nil { log.Errorf("failed to delete repository %s: %v", repoName, err) ra.CustomAbort(http.StatusInternalServerError, "") } } go func() { log.Debug("refreshing catalog cache") if err := cache.RefreshCatalogCache(); err != nil { log.Errorf("error occurred while refresh catalog cache: %v", err) } }() }
func projectExists(repository string) (bool, error) { project, _ := utils.ParseRepository(repository) return dao.ProjectExists(project) }
// SyncRegistry syncs the repositories of registry with database. func SyncRegistry() error { log.Debugf("Start syncing repositories from registry to DB... ") reposInRegistry, err := catalog() if err != nil { log.Error(err) return err } var repoRecordsInDB []models.RepoRecord repoRecordsInDB, err = dao.GetAllRepositories() if err != nil { log.Errorf("error occurred while getting all registories. %v", err) return err } var reposInDB []string for _, repoRecordInDB := range repoRecordsInDB { reposInDB = append(reposInDB, repoRecordInDB.Name) } var reposToAdd []string var reposToDel []string reposToAdd, reposToDel, err = diffRepos(reposInRegistry, reposInDB) if err != nil { return err } if len(reposToAdd) > 0 { log.Debugf("Start adding repositories into DB... ") for _, repoToAdd := range reposToAdd { project, _ := utils.ParseRepository(repoToAdd) user, err := dao.GetAccessLogCreator(repoToAdd) if err != nil { log.Errorf("Error happens when getting the repository owner from access log: %v", err) } if len(user) == 0 { user = "******" } pullCount, err := dao.CountPull(repoToAdd) if err != nil { log.Errorf("Error happens when counting pull count from access log: %v", err) } repoRecord := models.RepoRecord{Name: repoToAdd, OwnerName: user, ProjectName: project, PullCount: pullCount} if err := dao.AddRepository(repoRecord); err != nil { log.Errorf("Error happens when adding the missing repository: %v", err) } else { log.Debugf("Add repository: %s success.", repoToAdd) } } } if len(reposToDel) > 0 { log.Debugf("Start deleting repositories from DB... ") for _, repoToDel := range reposToDel { if err := dao.DeleteRepository(repoToDel); err != nil { log.Errorf("Error happens when deleting the repository: %v", err) } else { log.Debugf("Delete repository: %s success.", repoToDel) } } } log.Debugf("Sync repositories from registry to DB is done.") return nil }