diff --git a/interfaces/registry.go b/interfaces/registry.go index 3f38e40..1f64777 100644 --- a/interfaces/registry.go +++ b/interfaces/registry.go @@ -10,7 +10,7 @@ import ( ) type IRegistry interface { - Catalog(ctx context.Context, pagination common.PaginationOption, options common.CatalogOption, authenticator authn.Authenticator) (repositories []string, nextPage *common.PaginationOption, statusCode int, err error) + Catalog(ctx context.Context, pagination common.PaginationOption, options common.CatalogOption, authenticator authn.Authenticator) (repositories []string, nextPage *common.PaginationOption, err error) List(repoName string, pagination common.PaginationOption, options ...remote.Option) (tags []string, nextPagination *common.PaginationOption, err error) GetLatestTags(repoName string, depth int, options ...remote.Option) (tags []string, err error) GetAuth() *authn.AuthConfig diff --git a/registries/defaultregistry/default.go b/registries/defaultregistry/default.go index fb51482..91682a3 100644 --- a/registries/defaultregistry/default.go +++ b/registries/defaultregistry/default.go @@ -17,7 +17,6 @@ import ( "github.com/google/go-containerregistry/pkg/authn" "github.com/google/go-containerregistry/pkg/name" "github.com/google/go-containerregistry/pkg/v1/remote" - "github.com/google/go-containerregistry/pkg/v1/remote/transport" "github.com/google/go-containerregistry/pkg/v1/types" version "github.com/hashicorp/go-version" "k8s.io/utils/strings/slices" @@ -77,27 +76,22 @@ func (reg *DefaultRegistry) List(repoName string, pagination common.PaginationOp } // this is the default catalog implementation uses remote(for now) -func (reg *DefaultRegistry) Catalog(ctx context.Context, pagination common.PaginationOption, options common.CatalogOption, authenticator authn.Authenticator) ([]string, *common.PaginationOption, int, error) { - var statusCode int +func (reg *DefaultRegistry) Catalog(ctx context.Context, pagination common.PaginationOption, options common.CatalogOption, authenticator authn.Authenticator) ([]string, *common.PaginationOption, error) { if err := common.ValidateAuth(reg.GetAuth()); err == nil { if authenticator == nil { authenticator = authn.FromConfig(*reg.GetAuth()) } - res, _, statusCode, err := reg.CatalogPage(ctx, pagination, options, authenticator) - return res, nil, statusCode, err + res, _, err := reg.CatalogPage(ctx, pagination, options, authenticator) + return res, nil, err } repos, err := remote.CatalogPage(*reg.GetRegistry(), pagination.Cursor, pagination.Size, remote.WithAuth(authn.Anonymous)) - if err != nil { - if transportError, ok := err.(*transport.Error); ok { - statusCode = transportError.StatusCode - } - } - return repos, common.CalcNextV2Pagination(repos, pagination.Size), statusCode, err + + return repos, common.CalcNextV2Pagination(repos, pagination.Size), err } // Build http req and append password / token as bearer token // See https://cloud.google.com/container-registry/docs/advanced-authentication#token -func (reg *DefaultRegistry) gcrCatalogPage(pagination common.PaginationOption, options common.CatalogOption) ([]string, *common.PaginationOption, int, error) { +func (reg *DefaultRegistry) gcrCatalogPage(pagination common.PaginationOption, options common.CatalogOption) ([]string, *common.PaginationOption, error) { uri := reg.GetURL("_catalog") q := uri.Query() @@ -110,7 +104,7 @@ func (reg *DefaultRegistry) gcrCatalogPage(pagination common.PaginationOption, o uri.RawQuery = q.Encode() req, err := http.NewRequest(http.MethodGet, uri.String(), nil) if err != nil { - return nil, nil, 0, err + return nil, nil, err } if pagination.Cursor != "" { url := uri.String() @@ -125,51 +119,50 @@ func (reg *DefaultRegistry) gcrCatalogPage(pagination common.PaginationOption, o req.Header.Add("Authorization", fmt.Sprintf("Bearer %s", reg.GetAuth().Password)) resp, err := reg.HTTPClient.Do(req) if err != nil { - return nil, nil, 0, err + return nil, nil, err + } + if resp.StatusCode == http.StatusUnauthorized || resp.StatusCode == http.StatusForbidden { + return nil, nil, fmt.Errorf("authentication error: got %v status code", resp.StatusCode) + } + if resp.StatusCode > 399 { + return nil, nil, fmt.Errorf("got %v status code", resp.StatusCode) } defer resp.Body.Close() repos := &CatalogV2Response{} if err := json.NewDecoder(resp.Body).Decode(repos); err != nil { - return nil, nil, resp.StatusCode, err + return nil, nil, err } pgn := &common.PaginationOption{Size: pagination.Size} if len(repos.Repositories) > 0 && pagination.Size > 0 { pgn.Cursor = repos.Repositories[len(repos.Repositories)-1] } - return repos.Repositories, pgn, resp.StatusCode, nil + return repos.Repositories, pgn, nil } -func (reg *DefaultRegistry) CatalogPage(ctx context.Context, pagination common.PaginationOption, options common.CatalogOption, authenticator authn.Authenticator) ([]string, *common.PaginationOption, int, error) { +func (reg *DefaultRegistry) CatalogPage(ctx context.Context, pagination common.PaginationOption, options common.CatalogOption, authenticator authn.Authenticator) ([]string, *common.PaginationOption, error) { var repos []string var err error - var statusCode int var pgn *common.PaginationOption switch provider := getRegistryProvider(reg.Registry.RegistryStr()); provider { case "gcr": - repos, pgn, statusCode, err = reg.gcrCatalogPage(pagination, options) + repos, pgn, err = reg.gcrCatalogPage(pagination, options) default: repos, err = remote.CatalogPage(*reg.GetRegistry(), pagination.Cursor, pagination.Size, remote.WithAuth(authenticator)) - // for google's container registry error implementation - if err != nil { - if transportError, ok := err.(*transport.Error); ok { - statusCode = transportError.StatusCode - } - } pgn = common.CalcNextV2Pagination(repos, pagination.Size) } - return repos, pgn, statusCode, err + return repos, pgn, err } -func (reg *DefaultRegistry) GetV2Token(client *http.Client, url string) (*common.V2TokenResponse, int, error) { +func (reg *DefaultRegistry) GetV2Token(client *http.Client, url string) (*common.V2TokenResponse, error) { if reg.GetAuth() == nil { - return nil, 0, fmt.Errorf("no authorization found") + return nil, fmt.Errorf("no authorization found") } req, err := http.NewRequest(http.MethodGet, url, nil) if err != nil { - return nil, 0, err + return nil, err } if reg.GetAuth().Username != "" && reg.GetAuth().Password != "" { @@ -180,7 +173,7 @@ func (reg *DefaultRegistry) GetV2Token(client *http.Client, url string) (*common resp, err := client.Do(req) if err != nil { - return nil, 0, err + return nil, err } defer resp.Body.Close() @@ -188,13 +181,13 @@ func (reg *DefaultRegistry) GetV2Token(client *http.Client, url string) (*common token := &common.V2TokenResponse{} if err := json.NewDecoder(resp.Body).Decode(token); err != nil { - return nil, resp.StatusCode, err + return nil, err } if token.Token == "" { - return nil, resp.StatusCode, fmt.Errorf("recieved an empty token") + return nil, fmt.Errorf("recieved an empty token") } - return token, resp.StatusCode, nil + return token, nil } // GetLatestTags returns the latest tags for a given repository in descending order by the image creation time diff --git a/registries/factory_test.go b/registries/factory_test.go index 4a4a7d5..6cd49e3 100644 --- a/registries/factory_test.go +++ b/registries/factory_test.go @@ -62,7 +62,7 @@ func TestHarborAdminProject(t *testing.T) { */ func testHarbor(reg interfaces.IRegistry, t *testing.T) { ctx := context.Background() - for repos, repoNextPage, _, err := reg.Catalog(ctx, common.MakePagination(1), common.CatalogOption{}, nil); err == nil; repos, repoNextPage, _, err = reg.Catalog(ctx, *repoNextPage, common.CatalogOption{}, nil) { + for repos, repoNextPage, err := reg.Catalog(ctx, common.MakePagination(1), common.CatalogOption{}, nil); err == nil; repos, repoNextPage, err = reg.Catalog(ctx, *repoNextPage, common.CatalogOption{}, nil) { if err != nil { t.Errorf("%s", err.Error()) } diff --git a/registries/harbor/harbor.go b/registries/harbor/harbor.go index feab89b..058ea1d 100644 --- a/registries/harbor/harbor.go +++ b/registries/harbor/harbor.go @@ -39,7 +39,7 @@ func (*HarborRegistry) GetMaxPageSize() int { return 100 } -func (h *HarborRegistry) Catalog(ctx context.Context, pagination common.PaginationOption, options common.CatalogOption, authenticator authn.Authenticator) ([]string, *common.PaginationOption, int, error) { +func (h *HarborRegistry) Catalog(ctx context.Context, pagination common.PaginationOption, options common.CatalogOption, authenticator authn.Authenticator) ([]string, *common.PaginationOption, error) { //if first pagination request set the page number (cursor) to 1 if len(pagination.Cursor) == 0 { pagination.Cursor = "1" @@ -47,41 +47,36 @@ func (h *HarborRegistry) Catalog(ctx context.Context, pagination common.Paginati //ensure pagination Cursor is a number _, err := strconv.Atoi(pagination.Cursor) if err != nil { - return nil, nil, 0, fmt.Errorf("invalid pagination, cursor must be an integer") + return nil, nil, fmt.Errorf("invalid pagination, cursor must be an integer") } } //create list repos request req, err := h.repositoriesRequest(strconv.Itoa(pagination.Size), pagination.Cursor) if err != nil { - return nil, nil, 0, err + return nil, nil, err } //create client according to registry configuration res, err := h.getClient().Do(req) if err != nil { - return nil, nil, 0, err + return nil, nil, err } if err := transport.CheckError(res, http.StatusOK); err != nil { - // for google's container registry error implementation - var statusCode int - if transportError, ok := err.(*transport.Error); ok { - statusCode = transportError.StatusCode - } - return nil, nil, statusCode, err + return nil, nil, err } defer res.Body.Close() body, err := ioutil.ReadAll(res.Body) if err != nil { - return nil, nil, res.StatusCode, err + return nil, nil, err } //decode the repositories names from the response if repos, err := decodeObjectsNames(body); err != nil { - return nil, nil, res.StatusCode, err + return nil, nil, err } else { //get next pagination (can be nill if this is the last one) nextPagination, err := getNextPageOption(res) - return repos, nextPagination, res.StatusCode, err + return repos, nextPagination, err } } diff --git a/registries/harbor/harbor_test.go b/registries/harbor/harbor_test.go index 1c8266a..ac3da74 100644 --- a/registries/harbor/harbor_test.go +++ b/registries/harbor/harbor_test.go @@ -41,9 +41,8 @@ func TestCatalogAndList(t *testing.T) { assert.Nil(t, err) ctx := context.Background() //test catalog - repos, nextPage, statusCode, err := harbor.Catalog(ctx, common.NoPaginationOption(), common.CatalogOption{}, nil) + repos, nextPage, err := harbor.Catalog(ctx, common.NoPaginationOption(), common.CatalogOption{}, nil) assert.Nil(t, nextPage) - assert.Equal(t, 200, statusCode) assert.Nil(t, err) assert.Equal(t, []string{"my-project/ca-ws", "user2private/kibana", "user-project/kibana", "my-project/kibana", "my-project/postgres"}, repos) testServer.Close() @@ -93,9 +92,9 @@ func TestCatalogAndListWithProjectAndPagination(t *testing.T) { t.Error(err) } //test catalog - repos, nextReposPage, statusCode, err := harbor.Catalog(ctx, common.MakePagination(2), common.CatalogOption{}, nil) + repos, nextReposPage, err := harbor.Catalog(ctx, common.MakePagination(2), common.CatalogOption{}, nil) assert.Nil(t, err) - assert.Equal(t, 200, statusCode) + assert.Equal(t, []string{"my-project/ca-ws", "my-project/kibana"}, repos) assert.Equal(t, &common.PaginationOption{Cursor: "2", Size: 2}, nextReposPage) testServer.Close() @@ -111,9 +110,9 @@ func TestCatalogAndListWithProjectAndPagination(t *testing.T) { t.Error(err) } - _, nextReposPage, statusCode, err = harbor.Catalog(ctx, common.PaginationOption{Cursor: "2", Size: 1}, common.CatalogOption{}, nil) + _, nextReposPage, err = harbor.Catalog(ctx, common.PaginationOption{Cursor: "2", Size: 1}, common.CatalogOption{}, nil) assert.Nil(t, err) - assert.Equal(t, 200, statusCode) + assert.Equal(t, &common.PaginationOption{Cursor: "3", Size: 1}, nextReposPage) testServer.Close() @@ -127,7 +126,7 @@ func TestCatalogAndListWithProjectAndPagination(t *testing.T) { if err != nil { t.Error(err) } - _, nextReposPage, _, err = harbor.Catalog(ctx, common.PaginationOption{Cursor: "2", Size: 2}, common.CatalogOption{}, nil) + _, nextReposPage, err = harbor.Catalog(ctx, common.PaginationOption{Cursor: "2", Size: 2}, common.CatalogOption{}, nil) assert.Nil(t, err) assert.Nil(t, nextReposPage) testServer.Close() diff --git a/registries/quay/catalog.go b/registries/quay/catalog.go index 9e92568..83b3ce6 100644 --- a/registries/quay/catalog.go +++ b/registries/quay/catalog.go @@ -36,7 +36,7 @@ func catalogOptionsToQuery(uri *url.URL, pagination common.PaginationOption, opt return uri } -func (reg *QuayioRegistry) Catalog(ctx context.Context, pagination common.PaginationOption, options common.CatalogOption, authenticator authn.Authenticator) ([]string, *common.PaginationOption, int, error) { +func (reg *QuayioRegistry) Catalog(ctx context.Context, pagination common.PaginationOption, options common.CatalogOption, authenticator authn.Authenticator) ([]string, *common.PaginationOption, error) { //if quay is invalid we use it as public!!!! if err := common.ValidateAuth(reg.GetAuth()); err == nil { if authenticator == nil { @@ -53,12 +53,12 @@ func (reg *QuayioRegistry) Catalog(ctx context.Context, pagination common.Pagina return reg.catalogQuayProprietery(pagination, options) } -func (reg *QuayioRegistry) catalogQuayV2Auth(pagination common.PaginationOption, options common.CatalogOption) ([]string, *common.PaginationOption, int, error) { +func (reg *QuayioRegistry) catalogQuayV2Auth(pagination common.PaginationOption, options common.CatalogOption) ([]string, *common.PaginationOption, error) { //Token Request - token, statusCode, err := reg.GetV2Token(reg.HTTPClient, AUTH_URL) + token, err := reg.GetV2Token(reg.HTTPClient, AUTH_URL) if err != nil { - return nil, nil, statusCode, err + return nil, nil, err } reg.GetAuth().RegistryToken = token.Token uri := reg.DefaultRegistry.GetURL("_catalog") @@ -73,7 +73,7 @@ func (reg *QuayioRegistry) catalogQuayV2Auth(pagination common.PaginationOption, uri.RawQuery = q.Encode() req, err := http.NewRequest(http.MethodGet, uri.String(), nil) if err != nil { - return nil, nil, 0, err + return nil, nil, err } if pagination.Cursor != "" { @@ -89,26 +89,32 @@ func (reg *QuayioRegistry) catalogQuayV2Auth(pagination common.PaginationOption, req.Header.Add("Authorization", fmt.Sprintf("Bearer %s", token.Token)) resp, err := reg.HTTPClient.Do(req) if err != nil { - return nil, nil, 0, err + return nil, nil, err + } + if resp.StatusCode == http.StatusUnauthorized || resp.StatusCode == http.StatusForbidden { + return nil, nil, fmt.Errorf("authentication error: got %v status code", resp.StatusCode) + } + if resp.StatusCode > 399 { + return nil, nil, fmt.Errorf("error: got %v status code", resp.StatusCode) } defer resp.Body.Close() repos := &defaultregistry.CatalogV2Response{} if err := json.NewDecoder(resp.Body).Decode(repos); err != nil { - return nil, nil, resp.StatusCode, err + return nil, nil, err } pgn := &common.PaginationOption{Size: pagination.Size} if len(repos.Repositories) > 0 && pagination.Size > 0 { pgn.Cursor = repos.Repositories[len(repos.Repositories)-1] } - return repos.Repositories, pgn, resp.StatusCode, nil + return repos.Repositories, pgn, nil } -func (reg *QuayioRegistry) catalogQuayProprietery(pagination common.PaginationOption, options common.CatalogOption) ([]string, *common.PaginationOption, int, error) { - data, statusCode, err := reg.CatalogAux(pagination, options) +func (reg *QuayioRegistry) catalogQuayProprietery(pagination common.PaginationOption, options common.CatalogOption) ([]string, *common.PaginationOption, error) { + data, err := reg.CatalogAux(pagination, options) if err != nil { - return nil, nil, statusCode, err + return nil, nil, err } repositories := data.Transform(pagination.Size) var pgn *common.PaginationOption = nil @@ -118,31 +124,37 @@ func (reg *QuayioRegistry) catalogQuayProprietery(pagination common.PaginationOp repositories = append(repositories, data.Transform(0)...) } - return repositories, pgn, statusCode, nil + return repositories, pgn, nil } -func (reg *QuayioRegistry) CatalogAux(pagination common.PaginationOption, options common.CatalogOption) (*QuayCatalogResponse, int, error) { +func (reg *QuayioRegistry) CatalogAux(pagination common.PaginationOption, options common.CatalogOption) (*QuayCatalogResponse, error) { uri := reg.getURL("repository") uri = catalogOptionsToQuery(uri, pagination, options) client := http.Client{} req, err := http.NewRequest("GET", uri.String(), nil) if err != nil { - return nil, 0, err + return nil, err } resp, err := client.Do(req) if err != nil { - return nil, 0, err + return nil, err + } + if resp.StatusCode == http.StatusUnauthorized || resp.StatusCode == http.StatusForbidden { + return nil, fmt.Errorf("authentication error: got %v status code", resp.StatusCode) + } + if resp.StatusCode > 399 { + return nil, fmt.Errorf("error: got %v status code", resp.StatusCode) } defer resp.Body.Close() body, err := ioutil.ReadAll(resp.Body) if err != nil { - return nil, resp.StatusCode, err + return nil, err } data := &QuayCatalogResponse{} if err := json.Unmarshal(body, data); err != nil { - return nil, resp.StatusCode, err + return nil, err } body = nil - return data, resp.StatusCode, nil + return data, nil } diff --git a/registries/quay/quayio_test.go b/registries/quay/quayio_test.go index 4aeb191..628ea8b 100644 --- a/registries/quay/quayio_test.go +++ b/registries/quay/quayio_test.go @@ -19,10 +19,9 @@ func TestSimpleNoAuth(t *testing.T) { quayio, err := NewQuayIORegistry(nil, ®istry, &common.RegistryOptions{}) ctx := context.Background() - repos, _, statusCode, err := quayio.Catalog(ctx, common.NoPaginationOption(), common.CatalogOption{IsPublic: true, Namespaces: "quay"}, nil) + repos, _, err := quayio.Catalog(ctx, common.NoPaginationOption(), common.CatalogOption{IsPublic: true, Namespaces: "quay"}, nil) assert.Nil(t, err, "failed to catalog") assert.NotEmpty(t, repos, "expected some returned images") - assert.Equal(t, 200, statusCode, "expected status code 200") repo := repos[0] fullRepoName := quayio.GetRegistry().Name() + "/" + repo