From 76e8b1719bcfee00b9c787fcced78252e6404e47 Mon Sep 17 00:00:00 2001 From: Sushanta Das Date: Wed, 16 Aug 2023 17:05:44 +0530 Subject: [PATCH] Added function to check if imageRepo is public --- pkg/quay/quay.go | 50 +++++++++++++++++++++++++++++++ pkg/quay/quay_debug_test.go | 15 ++++++++++ pkg/quay/quay_test.go | 59 +++++++++++++++++++++++++++++++++++++ 3 files changed, 124 insertions(+) diff --git a/pkg/quay/quay.go b/pkg/quay/quay.go index 6292d1e..cb9da7a 100644 --- a/pkg/quay/quay.go +++ b/pkg/quay/quay.go @@ -139,6 +139,56 @@ func (c *QuayClient) DoesRepositoryExist(organization, imageRepository string) ( return false, errors.New(data.ErrorMessage) } +// IsRepositoryPublic checks if the specified image repository has visibility public in quay. +func (c *QuayClient) IsRepositoryPublic(organization, imageRepository string) (bool, error) { + url := fmt.Sprintf("%s/repository/%s/%s", c.url, organization, imageRepository) + + req, err := http.NewRequest(http.MethodGet, url, nil) + if err != nil { + return false, fmt.Errorf("failed to create request: %w", err) + } + req.Header.Add("Authorization", fmt.Sprintf("%s %s", "Bearer", c.AuthToken)) + req.Header.Add("Content-Type", "application/json") + + res, err := c.httpClient.Do(req) + if err != nil { + return false, fmt.Errorf("failed to Do request: %w", err) + } + defer res.Body.Close() + + if res.StatusCode == 404 { + return false, fmt.Errorf("repository %s does not exist in %s organization", imageRepository, organization) + } + + if res.StatusCode == 200 { + body, _ := io.ReadAll(res.Body) + repo := &Repository{} + err := json.Unmarshal(body, repo) + if err != nil { + return false, fmt.Errorf("failed to unmarshal response body: %w", err) + } + if repo.IsPublic { + return true, nil + } else { + return false, nil + } + } + + body, err := io.ReadAll(res.Body) + if err != nil { + return false, fmt.Errorf("failed to read response body: %w", err) + } + data := &QuayError{} + err = json.Unmarshal(body, data) + if err != nil { + return false, fmt.Errorf("failed to unmarshal response body: %w", err) + } + if data.Error != "" { + return false, errors.New(data.Error) + } + return false, errors.New(data.ErrorMessage) +} + // DeleteRepository deletes specified image repository. func (c *QuayClient) DeleteRepository(organization, imageRepository string) (bool, error) { url := fmt.Sprintf("%s/repository/%s/%s", c.url, organization, imageRepository) diff --git a/pkg/quay/quay_debug_test.go b/pkg/quay/quay_debug_test.go index e21bdac..565020c 100644 --- a/pkg/quay/quay_debug_test.go +++ b/pkg/quay/quay_debug_test.go @@ -80,6 +80,21 @@ func TestDeleteRepository(t *testing.T) { } } +func TestIsRepositoryPublic(t *testing.T) { + if quayToken == "" { + return + } + quayClient := NewQuayClient(&http.Client{Transport: &http.Transport{}}, quayToken, quayApiUrl) + isPublic, err := quayClient.IsRepositoryPublic(quayOrgName, quayImageRepoName) + if isPublic && err == nil { + t.Log("Repository is public") + } else if isPublic == false && err == nil { + t.Log("Repository is private") + } else { + t.Fatalf("Unexpected error: %s\n", err.Error()) + } +} + func TestChangeRepositoryVisibility(t *testing.T) { if quayToken == "" { return diff --git a/pkg/quay/quay_test.go b/pkg/quay/quay_test.go index 247f58a..27781e2 100644 --- a/pkg/quay/quay_test.go +++ b/pkg/quay/quay_test.go @@ -487,6 +487,65 @@ func TestQuayClient_DoesRepositoryExist(t *testing.T) { } } +func TestQuayClient_IsRepositoryPublic(t *testing.T) { + defer gock.Off() + + client := &http.Client{Transport: &http.Transport{}} + gock.InterceptClient(client) + + quayClient := NewQuayClient(client, "authtoken", "https://quay.io/api/v1") + + testCases := []struct { + name string + isPublic bool + err error + statusCode int + response []byte + }{ + { + name: "Repository is public", + isPublic: true, + err: nil, + statusCode: 200, + response: []byte(`{"namespace": "test_org", "name": "test_repo", "kind": "image", "description": "Test repository", "is_public": true, "is_organization": true, "is_starred": false, "status_token": "", "trust_enabled": false, "tag_expiration_s": 1209600, "is_free_account": false, "state": "NORMAL", "tags": {}, "can_write": true, "can_admin": true}`), + }, + { + name: "Repository is private", + isPublic: false, + err: nil, + statusCode: 200, + response: []byte(`{"namespace": "test_org", "name": "test_repo", "kind": "image", "description": "Test repository", "is_public": false, "is_organization": true, "is_starred": false, "status_token": "", "trust_enabled": false, "tag_expiration_s": 1209600, "is_free_account": false, "state": "NORMAL", "tags": {}, "can_write": true, "can_admin": true}`), + }, + { + name: "Repository does not exist", + isPublic: false, + err: fmt.Errorf("repository %s does not exist in %s organization", repo, org), + statusCode: 404, + response: []byte(`{"detail": "Not Found", "error_message": "Not Found", "error_type": "not_found", "title": "not_found", "type": "https://quay.io/api/v1/error/not_found", "status": 404}`), + }, + } + + for _, tc := range testCases { + t.Run(tc.name, func(t *testing.T) { + gock.New("https://quay.io/api/v1"). + MatchHeader("Content-Type", "application/json"). + MatchHeader("Authorization", "Bearer authtoken"). + Get(fmt.Sprintf("repository/%s/%s", org, repo)). + Reply(tc.statusCode). + JSON(tc.response) + + isPublic, err := quayClient.IsRepositoryPublic(org, repo) + if isPublic != tc.isPublic { + t.Errorf("expected result to be `%t`, got `%t`", tc.isPublic, isPublic) + } + if (tc.err != nil && err == nil) || (tc.err == nil && err != nil) { + t.Errorf("expected error to be `%v`, got `%v`", tc.err, err) + } + + }) + } +} + func TestQuayClient_DeleteRepository(t *testing.T) { defer gock.Off()