package repos import ( "encoding/json" "fmt" "strconv" "strings" ) const ( v2Url = "api/v2.0" v1Url = "api" projectUrl = "/projects" repositoryUrl = "repositories" artifactUrl = "/artifacts" tagUrl = "tags" ) func NewHarborClient(endpoint, username, password, version string) *harborClient { return &harborClient{ EndPoint: endpoint, HttpClient: &HttpClient{ Username: username, Password: password, Host: endpoint, }, Version: version, } } type harborClient struct { EndPoint string HttpClient *HttpClient Version string } type artifacts struct { Type string Tags []NameResult } type harborBody struct { Name string Version string Format string ProjectID int `json:"project_id"` ArtifactCount int `json:"artifact_count"` TagsCount int `json:"tags_count"` } func (c *harborClient) ListRepos(request ProjectRequest) (names []string, err error) { projectUrl := fmt.Sprintf("%s?page=%d&page_size=%d", getProjectUrl(c.Version), request.Page, request.Limit) if request.Name != "" { if c.Version == "v2" { projectUrl = projectUrl + "&q=name=~" + request.Name } else { projectUrl = projectUrl + "&name=" + request.Name } } body, _, err := c.HttpClient.Get(projectUrl) if err != nil { return } var projects []harborBody if err = json.Unmarshal(body, &projects); err != nil { return } for _, r := range projects { names = append(names, r.Name) } return } const LIMIT = 100 func (c *harborClient) ListImages(request RepoRequest) (response RepoResponse, err error) { project := request.Repo startCount := (request.Page - 1) * request.Limit start := true p := 1 artifactCount := 0 repoArCount := 0 artifactStart := 0 var repos []harborBody for start { var repoUrl string if c.Version == "v2" { repoUrl = fmt.Sprintf("%s/%s/%s?page=%d&&page_size=%d", getProjectUrl(c.Version), project, repositoryUrl, p, LIMIT) } else { result, err1 := c.HttpClient.GetNameResult(getProjectUrl(c.Version)) if err1 != nil { err = err1 return } for _, value := range result { if value.Name == project { repoUrl = fmt.Sprintf("%s/%s%s", v1Url, repositoryUrl, "?project_id="+strconv.Itoa(value.ProjectID)) break } } } body, _, err1 := c.HttpClient.Get(repoUrl) if err1 != nil { err = err1 return } var items []harborBody if err1 = json.Unmarshal(body, &items); err1 != nil { err = err1 return } if len(items) < LIMIT { start = false } for _, v := range items { count := v.ArtifactCount if c.Version == "v1" { count = v.TagsCount } if count == 0 { continue } artifactCount = artifactCount + count if artifactCount > startCount { repos = append(repos, v) repoArCount = repoArCount + count if repoArCount >= request.Limit { start = false break } continue } if artifactCount >= startCount { artifactStart = artifactCount - startCount } } p++ } var repoUrl string if c.Version == "v2" { repoUrl = fmt.Sprintf("%s/%s/%s", getProjectUrl(c.Version), project, repositoryUrl) } var items []string for _, r := range repos { var tagsUrl string if c.Version == "v2" { repoName := strings.Replace(r.Name, project+"/", "", -1) tagsUrl = fmt.Sprintf("%s/%s/%s?page=%d&&page_size=%d", repoUrl, repoName, artifactUrl, 1, r.ArtifactCount) } else { tagsUrl = fmt.Sprintf("%s/%s/%s/%s", v1Url, repositoryUrl, r.Name, tagUrl) } body, res, err2 := c.HttpClient.Get(tagsUrl) if res != nil && res.StatusCode == 404 { continue } if err2 != nil { if strings.Contains(err2.Error(), "404") { continue } err = err2 return } if c.Version == "v2" { var artifacts []artifacts if err = json.Unmarshal(body, &artifacts); err != nil { return } for _, art := range artifacts { for _, tag := range art.Tags { items = append(items, r.Name+":"+tag.Name) } } } else { var tags []NameResult if err = json.Unmarshal(body, &tags); err != nil { return } for _, tag := range tags { items = append(items, r.Name+":"+tag.Name) } } } artifactEnd := artifactStart + request.Limit if artifactStart+request.Limit > len(items) { artifactEnd = len(items) } if artifactStart > artifactEnd { response.Items = []string{} return } response.Items = items[artifactStart:artifactEnd] if len(response.Items) == request.Limit { response.ContinueToken = "continue" } return } func (c *harborClient) ListImagesWithoutPage(project string) (images []string, err error) { if c.Version == "v2" { repoUrl := fmt.Sprintf("%s/%s/%s?page=1&&page_size=100", getProjectUrl(c.Version), project, repositoryUrl) result, _, err1 := c.HttpClient.Get(repoUrl) if err1 != nil { err = err1 return } var items []harborBody if err1 = json.Unmarshal(result, &items); err1 != nil { err = err1 return } repoUrl = fmt.Sprintf("%s/%s/%s", getProjectUrl(c.Version), project, repositoryUrl) for _, r := range items { repoName := strings.Replace(r.Name, project+"/", "", -1) body, _, err2 := c.HttpClient.Get(fmt.Sprintf("%s/%s/%s?page=1&&page_size=%d", repoUrl, repoName, artifactUrl, r.TagsCount)) if err2 != nil { if strings.Contains(err2.Error(), "404") { continue } err = err2 return } var artifacts []artifacts if err = json.Unmarshal(body, &artifacts); err != nil { return } for _, art := range artifacts { for _, tag := range art.Tags { images = append(images, r.Name+":"+tag.Name) } } } } else { result, err1 := c.HttpClient.GetNameResult(getProjectUrl(c.Version)) if err1 != nil { err = err1 return } var repoUrl string for _, value := range result { if value.Name == project { repoUrl = fmt.Sprintf("%s/%s%s", v1Url, repositoryUrl, "?project_id="+strconv.Itoa(value.ProjectID)) break } } repos, err1 := c.HttpClient.GetNameResult(repoUrl) if err1 != nil { err = err1 return } for _, repo := range repos { tagUrl := fmt.Sprintf("%s/%s/%s/%s", v1Url, repositoryUrl, repo.Name, tagUrl) tags, err1 := c.HttpClient.GetNameResult(tagUrl) if err1 != nil { err = err1 return } for _, tag := range tags { images = append(images, repo.Name+":"+tag.Name) } } } return } func getProjectUrl(version string) string { if version == "v1" { return v1Url + projectUrl } else { return v2Url + projectUrl } }