diff --git a/.gitignore b/.gitignore index 53a9e9218f..083adf1928 100644 --- a/.gitignore +++ b/.gitignore @@ -33,4 +33,5 @@ tests/allure/reports/* # vscode .vscode* -*.code-workspace \ No newline at end of file +*.code-workspace +/evaluator/tmpBundles/bundle.tar.gz diff --git a/resources/fetchers/ecr_factory.go b/resources/fetchers/ecr_factory.go index fbb1702b6f..0bbd5af704 100644 --- a/resources/fetchers/ecr_factory.go +++ b/resources/fetchers/ecr_factory.go @@ -66,7 +66,7 @@ func (f *ECRFactory) CreateFrom(log *logp.Logger, cfg ECRFetcherConfig, ch chan return nil, fmt.Errorf("failed to initialize AWS credentials: %w", err) } - ecrPrivateProvider := awslib.NewEcrProvider(awsConfig) + ecrPrivateProvider := awslib.NewEcrProvider() ecrPublicProvider := awslib.NewEcrPublicProvider() kubeClient, err := f.KubernetesProvider.GetClient(cfg.KubeConfig, kubernetes.KubeClientOptions{}) if err != nil { @@ -79,15 +79,19 @@ func (f *ECRFactory) CreateFrom(log *logp.Logger, cfg ECRFetcherConfig, ch chan return nil, fmt.Errorf("could not retrieve user identity for ECR fetcher: %w", err) } - privateRepoRegex := fmt.Sprintf(PrivateRepoRegexTemplate, *identity.Account, awsConfig.Region) + privateRepoRegex := fmt.Sprintf(PrivateRepoRegexTemplate, *identity.Account) privateECRExecutor := PodDescriber{ - FilterRegex: regexp.MustCompile(privateRepoRegex), - Provider: ecrPrivateProvider, + FilterRegex: regexp.MustCompile(privateRepoRegex), + Provider: ecrPrivateProvider, + ExtractRegion: ExtractRegionFromEcrImage, + ImageRegexIndex: EcrImageRegexGroup, } publicECRExecutor := PodDescriber{ - FilterRegex: regexp.MustCompile(PublicRepoRegex), - Provider: ecrPublicProvider, + FilterRegex: regexp.MustCompile(PublicRepoRegex), + Provider: ecrPublicProvider, + ExtractRegion: ExtractRegionFromPublicEcrImage, + ImageRegexIndex: PublicEcrImageRegexIndex, } fe := &ECRFetcher{ @@ -99,6 +103,7 @@ func (f *ECRFactory) CreateFrom(log *logp.Logger, cfg ECRFetcherConfig, ch chan publicECRExecutor, }, resourceCh: ch, + awsConfig: awsConfig, } return fe, nil } diff --git a/resources/fetchers/ecr_factory_test.go b/resources/fetchers/ecr_factory_test.go index b2e00ce38a..df06f03630 100644 --- a/resources/fetchers/ecr_factory_test.go +++ b/resources/fetchers/ecr_factory_test.go @@ -68,7 +68,12 @@ default_region: us1-east "us1-east", "my-account", []string{ - "^my-account\\.dkr\\.ecr\\.us1-east\\.amazonaws\\.com\\/([-\\w\\.\\/]+)[:,@]?", + + // this regex should identify images with an ecr regex template + // .dkr.ecr..amazonaws.com/ + "^my-account\\.dkr\\.ecr\\.([-\\w]+)\\.amazonaws\\.com\\/([-\\w\\.\\/]+)[:,@]?", + // this regex should identify images with a public ecr regex template + // public.ecr.aws// "public\\.ecr\\.aws\\/\\w+\\/([-\\w\\.\\/]+)\\:?", }, }, @@ -113,10 +118,15 @@ default_region: us1-east s.NotNil(fetcher) ecrFetcher, ok := fetcher.(*ECRFetcher) + expectedEcrImageRegexIndex := 2 + expectedEcrPublicImageRegexIndex := 1 + s.True(ok) s.Equal(kubeclient, ecrFetcher.kubeClient) s.Equal(test.expectedRegex[0], ecrFetcher.PodDescribers[0].FilterRegex.String()) + s.Equal(expectedEcrImageRegexIndex, ecrFetcher.PodDescribers[0].ImageRegexIndex) s.Equal(test.expectedRegex[1], ecrFetcher.PodDescribers[1].FilterRegex.String()) + s.Equal(expectedEcrPublicImageRegexIndex, ecrFetcher.PodDescribers[1].ImageRegexIndex) } } diff --git a/resources/fetchers/ecr_fetcher.go b/resources/fetchers/ecr_fetcher.go index 9102573535..96e7743035 100644 --- a/resources/fetchers/ecr_fetcher.go +++ b/resources/fetchers/ecr_fetcher.go @@ -26,6 +26,7 @@ import ( "github.com/elastic/cloudbeat/resources/providers/awslib" v1 "k8s.io/api/core/v1" + "github.com/aws/aws-sdk-go-v2/aws" "github.com/aws/aws-sdk-go-v2/service/ecr" "github.com/elastic/cloudbeat/resources/fetching" "github.com/elastic/elastic-agent-libs/logp" @@ -34,8 +35,15 @@ import ( ) const ( - PrivateRepoRegexTemplate = "^%s\\.dkr\\.ecr\\.%s\\.amazonaws\\.com\\/([-\\w\\.\\/]+)[:,@]?" + // PrivateRepoRegexTemplate should identify images with an ecr regex template + // .dkr.ecr..amazonaws.com/ + PrivateRepoRegexTemplate = "^%s\\.dkr\\.ecr\\.([-\\w]+)\\.amazonaws\\.com\\/([-\\w\\.\\/]+)[:,@]?" + // PublicRepoRegex should identify images with a public ecr regex template + // public.ecr.aws// PublicRepoRegex = "public\\.ecr\\.aws\\/\\w+\\/([-\\w\\.\\/]+)\\:?" + EcrRegionRegexGroup = 1 + PublicEcrImageRegexIndex = 1 + EcrImageRegexGroup = 2 ) type ECRFetcher struct { @@ -44,11 +52,14 @@ type ECRFetcher struct { kubeClient k8s.Interface PodDescribers []PodDescriber resourceCh chan fetching.ResourceInfo + awsConfig aws.Config } type PodDescriber struct { - FilterRegex *regexp.Regexp - Provider awslib.EcrRepositoryDescriber + FilterRegex *regexp.Regexp + Provider awslib.EcrRepositoryDescriber + ExtractRegion func(describer PodDescriber, repo string) (string, error) + ImageRegexIndex int } type ECRFetcherConfig struct { @@ -86,13 +97,27 @@ func (f *ECRFetcher) Fetch(ctx context.Context, cMetadata fetching.CycleMetadata } } } - return nil } func (f *ECRFetcher) describePodImagesRepositories(ctx context.Context, podsList *v1.PodList, describer PodDescriber) ([]ecr.Repository, error) { + regionToReposMap := getAwsRepositories(podsList, describer) + f.log.Debugf("sending pods to ecrProviders: %v", regionToReposMap) + awsRepositories := make([]ecr.Repository, 0) + for region, repositories := range regionToReposMap { + // Add configuration + describedRepo, err := describer.Provider.DescribeRepositories(ctx, f.awsConfig, repositories, region) + if err != nil { + f.log.Errorf("could not retrieve pod's aws repositories for region %s: %w", region, err) + } else { + awsRepositories = append(awsRepositories, describedRepo.Repositories...) + } + } + return awsRepositories, nil +} - repositories := make([]string, 0) +func getAwsRepositories(podsList *v1.PodList, describer PodDescriber) map[string][]string { + reposByRegion := make(map[string][]string, 0) for _, pod := range podsList.Items { for _, container := range pod.Spec.Containers { @@ -101,15 +126,33 @@ func (f *ECRFetcher) describePodImagesRepositories(ctx context.Context, podsList // Takes only aws images regexMatcher := describer.FilterRegex.FindStringSubmatch(image) if regexMatcher != nil { - repository := regexMatcher[1] - repositories = append(repositories, repository) + repository := regexMatcher[describer.ImageRegexIndex] + region, err := describer.ExtractRegion(describer, image) + if err != nil { + logp.Error(err) + continue + } + reposByRegion[region] = append(reposByRegion[region], repository) } } } + return reposByRegion +} + +func ExtractRegionFromEcrImage(describer PodDescriber, image string) (string, error) { + regexMatcher := describer.FilterRegex.FindStringSubmatch(image) + if regexMatcher != nil { + repository := regexMatcher[EcrRegionRegexGroup] + return repository, nil + } - f.log.Debugf("sending pods to ecrProviders: %v", repositories) + return "", fmt.Errorf("could not extract region, image does not match the aws ecr template") +} - return describer.Provider.DescribeRepositories(ctx, repositories) +// ExtractRegionFromPublicEcrImage - Currently the public ecr provider is not functional. +// TODO - This method should be change as part of implementing the public ecr - https://github.com/elastic/security-team/issues/4035 +func ExtractRegionFromPublicEcrImage(_ PodDescriber, _ string) (string, error) { + return "", nil } func (res ECRResource) GetData() interface{} { diff --git a/resources/fetchers/ecr_fetcher_test.go b/resources/fetchers/ecr_fetcher_test.go index 2132c8cdde..a135fca71f 100644 --- a/resources/fetchers/ecr_fetcher_test.go +++ b/resources/fetchers/ecr_fetcher_test.go @@ -20,8 +20,10 @@ package fetchers import ( "context" "fmt" + "github.com/aws/aws-sdk-go-v2/aws" "github.com/elastic/cloudbeat/resources/fetching" "regexp" + "sort" "testing" "github.com/aws/aws-sdk-go-v2/service/ecr" @@ -43,6 +45,11 @@ type ECRFetcherTestSuite struct { resourceCh chan fetching.ResourceInfo } +type describeRepoMockParameters struct { + EcrRepositories []ecr.Repository + ExpectedRepositories []string +} + func TestECRFetcherTestSuite(t *testing.T) { s := new(ECRFetcherTestSuite) s.log = logp.NewLogger("cloudbeat_ecr_fetcher_test_suite") @@ -64,23 +71,20 @@ func (s *ECRFetcherTestSuite) TearDownTest() { func (s *ECRFetcherTestSuite) TestCreateFetcher() { firstRepositoryName := "cloudbeat" secondRepositoryName := "cloudbeat1" - publicRepoName := "build.security/citools" privateRepoWithSlash := "build/cloudbeat" repoArn := "arn:aws:ecr:us-west-2:012345678910:repository/ubuntu" + publicRepoName := "build.security/citools" var tests = []struct { - identityAccount string - region string - namespace string - containers []v1.Container - expectedRepositories []ecr.Repository - expectedPublicRepositories []ecr.Repository - expectedRepositoriesNames []string - expectedPublicRepositoriesNames []string + identityAccount string + namespace string + containers []v1.Container + privateRepositoriesResponseByRegion map[string]describeRepoMockParameters + publicRepositoriesResponseByRegion map[string]describeRepoMockParameters + expectedRepositoriesNames []string }{ { "123456789123", - "us-east-2", "my-namespace", []v1.Container{ { @@ -92,23 +96,29 @@ func (s *ECRFetcherTestSuite) TestCreateFetcher() { Name: "cloudbeat1", }, }, - []ecr.Repository{{ - RepositoryArn: &repoArn, - ImageScanningConfiguration: nil, - RepositoryName: &firstRepositoryName, - RepositoryUri: nil, - }, { - RepositoryArn: &repoArn, - ImageScanningConfiguration: nil, - RepositoryName: &secondRepositoryName, - RepositoryUri: nil, - }}, - []ecr.Repository{}, + map[string]describeRepoMockParameters{ + "us-east-2": { + ExpectedRepositories: []string{"cloudbeat", "cloudbeat1"}, + EcrRepositories: []ecr.Repository{ + { + RepositoryArn: &repoArn, + ImageScanningConfiguration: nil, + RepositoryName: &firstRepositoryName, + RepositoryUri: nil, + }, + { + RepositoryArn: &repoArn, + ImageScanningConfiguration: nil, + RepositoryName: &secondRepositoryName, + RepositoryUri: nil, + }}, + }, + }, + map[string]describeRepoMockParameters{}, []string{firstRepositoryName, secondRepositoryName}, - []string{}, - }, { + }, + { "123456789123", - "us-east-2", "my-namespace", []v1.Container{ { @@ -120,24 +130,27 @@ func (s *ECRFetcherTestSuite) TestCreateFetcher() { Name: "cloudbeat1", }, }, - []ecr.Repository{{ - RepositoryArn: &repoArn, - ImageScanningConfiguration: nil, - RepositoryName: &privateRepoWithSlash, - RepositoryUri: nil, - }, { - RepositoryArn: &repoArn, - ImageScanningConfiguration: nil, - RepositoryName: &secondRepositoryName, - RepositoryUri: nil, - }}, - []ecr.Repository{}, + map[string]describeRepoMockParameters{ + "us-east-2": { + ExpectedRepositories: []string{"build/cloudbeat", "cloudbeat1"}, + EcrRepositories: []ecr.Repository{ + { + RepositoryArn: &repoArn, + ImageScanningConfiguration: nil, + RepositoryName: &privateRepoWithSlash, + RepositoryUri: nil, + }, { + RepositoryArn: &repoArn, + ImageScanningConfiguration: nil, + RepositoryName: &secondRepositoryName, + RepositoryUri: nil, + }}, + }, + }, map[string]describeRepoMockParameters{}, []string{privateRepoWithSlash, secondRepositoryName}, - []string{}, }, { "123456789123", - "us-east-2", "my-namespace", []v1.Container{ { @@ -149,23 +162,34 @@ func (s *ECRFetcherTestSuite) TestCreateFetcher() { Name: "build.security/citools", }, }, - []ecr.Repository{{ - RepositoryArn: &repoArn, - ImageScanningConfiguration: nil, - RepositoryName: &firstRepositoryName, - RepositoryUri: nil, - }}, []ecr.Repository{{ - RepositoryArn: &repoArn, - ImageScanningConfiguration: nil, - RepositoryName: &publicRepoName, - RepositoryUri: nil, - }}, - []string{firstRepositoryName}, - []string{publicRepoName}, + map[string]describeRepoMockParameters{ + "us-east-2": { + ExpectedRepositories: []string{"cloudbeat"}, + EcrRepositories: []ecr.Repository{ + { + RepositoryArn: &repoArn, + ImageScanningConfiguration: nil, + RepositoryName: &firstRepositoryName, + RepositoryUri: nil, + }}, + }, + }, + map[string]describeRepoMockParameters{ + "": { + ExpectedRepositories: []string{"build.security/citools"}, + EcrRepositories: []ecr.Repository{ + { + RepositoryArn: &repoArn, + ImageScanningConfiguration: nil, + RepositoryName: &publicRepoName, + RepositoryUri: nil, + }}, + }, + }, + []string{publicRepoName, firstRepositoryName}, }, { "123456789123", - "us-east-2", "my-namespace", []v1.Container{ { @@ -177,29 +201,47 @@ func (s *ECRFetcherTestSuite) TestCreateFetcher() { Name: "cloudbeat1", }, }, - []ecr.Repository{}, - []ecr.Repository{}, - []string{}, + map[string]describeRepoMockParameters{}, + map[string]describeRepoMockParameters{}, []string{}, }, { "123456789123", - "us-east-1", "my-namespace", []v1.Container{ { - Image: "123456789123.dkr.ecr.wrong-region.amazonaws.com/cloudbeat:latest", + Image: "123456789123.dkr.ecr.us-east-2.amazonaws.com/cloudbeat:latest", Name: "cloudbeat", }, { - Image: "123456789123.dkr.ecr.wrong-region.amazonaws.com/cloudbeat1:latest", + Image: "123456789123.dkr.ecr.us-east-1.amazonaws.com/cloudbeat1:latest", Name: "cloudbeat1", }, }, - []ecr.Repository{}, - []ecr.Repository{}, - []string{}, - []string{}, + map[string]describeRepoMockParameters{ + "us-east-2": { + ExpectedRepositories: []string{"cloudbeat"}, + EcrRepositories: []ecr.Repository{ + { + RepositoryArn: &repoArn, + ImageScanningConfiguration: nil, + RepositoryName: &firstRepositoryName, + RepositoryUri: nil, + }}, + }, + "us-east-1": { + ExpectedRepositories: []string{"cloudbeat1"}, + EcrRepositories: []ecr.Repository{ + { + RepositoryArn: &repoArn, + ImageScanningConfiguration: nil, + RepositoryName: &secondRepositoryName, + RepositoryUri: nil, + }}, + }, + }, + map[string]describeRepoMockParameters{}, + []string{firstRepositoryName, secondRepositoryName}, }, } for _, test := range tests { @@ -226,30 +268,55 @@ func (s *ECRFetcherTestSuite) TestCreateFetcher() { mockedKubernetesClientGetter := &providers.MockedKubernetesClientGetter{} mockedKubernetesClientGetter.EXPECT().GetClient(mock.Anything, mock.Anything).Return(kubeclient, nil) - // Needs to use the same services - ecrProvider := &awslib.MockedEcrRepositoryDescriber{} - ecrProvider.EXPECT().DescribeRepositories(mock.Anything, mock.MatchedBy(func(repo []string) bool { - return s.Equal(test.expectedRepositoriesNames, repo) - })).Return(test.expectedRepositories, nil) + ecrProvider := &awslib.MockEcrRepositoryDescriber{} + // Init private repositories provider - ecrPublicProvider := &awslib.MockedEcrRepositoryDescriber{} - ecrPublicProvider.EXPECT().DescribeRepositories(mock.Anything, mock.MatchedBy(func(repo []string) bool { - return s.Equal(test.expectedPublicRepositoriesNames, repo) - })).Return(test.expectedPublicRepositories, nil) + ecrProvider.EXPECT().DescribeRepositories(mock.Anything, mock.Anything, mock.Anything, mock.Anything).Call. + Return(func(ctx context.Context, cfg aws.Config, repoNames []string, region string) awslib.ECRProviderResponse { + response, ok := test.privateRepositoriesResponseByRegion[region] + s.True(ok) + s.Equal(response.ExpectedRepositories, repoNames) + + return awslib.ECRProviderResponse{ + Repositories: response.EcrRepositories, + } + }, + func(ctx context.Context, cfg aws.Config, repoNames []string, region string) error { + return nil + }) + + // Init public repositories provider + ecrPublicProvider := &awslib.MockEcrRepositoryDescriber{} + ecrPublicProvider.EXPECT().DescribeRepositories(mock.Anything, mock.Anything, mock.Anything, mock.Anything).Call. + Return(func(ctx context.Context, cfg aws.Config, repoNames []string, region string) awslib.ECRProviderResponse { + response, ok := test.publicRepositoriesResponseByRegion[region] + s.True(ok) + s.Equal(response.ExpectedRepositories, repoNames) + + return awslib.ECRProviderResponse{ + Repositories: response.EcrRepositories, + } + }, + func(ctx context.Context, cfg aws.Config, repoNames []string, region string) error { + return nil + }) - privateRepoRegex := fmt.Sprintf(PrivateRepoRegexTemplate, test.identityAccount, test.region) + privateRepoRegex := fmt.Sprintf(PrivateRepoRegexTemplate, test.identityAccount) privateEcrExecutor := PodDescriber{ - FilterRegex: regexp.MustCompile(privateRepoRegex), - Provider: ecrProvider, + FilterRegex: regexp.MustCompile(privateRepoRegex), + Provider: ecrProvider, + ExtractRegion: ExtractRegionFromEcrImage, + ImageRegexIndex: EcrImageRegexGroup, } publicEcrExecutor := PodDescriber{ - FilterRegex: regexp.MustCompile(PublicRepoRegex), - Provider: ecrPublicProvider, + FilterRegex: regexp.MustCompile(PublicRepoRegex), + Provider: ecrPublicProvider, + ExtractRegion: ExtractRegionFromPublicEcrImage, + ImageRegexIndex: PublicEcrImageRegexIndex, } - expectedRepositories := append(test.expectedRepositories, test.expectedPublicRepositories...) ecrFetcher := ECRFetcher{ log: s.log, cfg: ECRFetcherConfig{}, @@ -262,9 +329,16 @@ func (s *ECRFetcherTestSuite) TestCreateFetcher() { err = ecrFetcher.Fetch(ctx, fetching.CycleMetadata{}) results := testhelper.CollectResources(s.resourceCh) - s.Equal(len(expectedRepositories), len(results)) + s.Equal(len(test.expectedRepositoriesNames), len(results)) s.NoError(err) + // DO NOT REMOVE THIS METHOD + // The results provided by the channel can return the results in a different order + // This method order the results before asserting on them + sort.SliceStable(results, func(i, j int) bool { + return *results[i].Resource.(ECRResource).RepositoryName <= *results[j].Resource.(ECRResource).RepositoryName + }) + for i, name := range test.expectedRepositoriesNames { ecrResource := results[i].Resource.(ECRResource) metadata, err := ecrResource.GetMetadata() @@ -322,23 +396,26 @@ func (s *ECRFetcherTestSuite) TestCreateFetcherErrorCases() { mockedKubernetesClientGetter := &providers.MockedKubernetesClientGetter{} mockedKubernetesClientGetter.EXPECT().GetClient(mock.Anything, mock.Anything).Return(kubeclient, nil) - // Needs to use the same services - ecrProvider := &awslib.MockedEcrRepositoryDescriber{} - ecrProvider.EXPECT().DescribeRepositories(mock.Anything, mock.Anything).Return(nil, test.error) + ecrProvider := &awslib.MockEcrRepositoryDescriber{} + ecrProvider.EXPECT().DescribeRepositories(mock.Anything, mock.Anything, mock.Anything, mock.Anything).Return(awslib.ECRProviderResponse{}, test.error) - ecrPublicProvider := &awslib.MockedEcrRepositoryDescriber{} - ecrPublicProvider.EXPECT().DescribeRepositories(mock.Anything, mock.Anything).Return(nil, test.error) + ecrPublicProvider := &awslib.MockEcrRepositoryDescriber{} + ecrPublicProvider.EXPECT().DescribeRepositories(mock.Anything, mock.Anything, mock.Anything, mock.Anything).Return(awslib.ECRProviderResponse{}, test.error) - privateRepoRegex := fmt.Sprintf(PrivateRepoRegexTemplate, test.identityAccount, test.region) + privateRepoRegex := fmt.Sprintf(PrivateRepoRegexTemplate, test.identityAccount) privateEcrExecutor := PodDescriber{ - FilterRegex: regexp.MustCompile(privateRepoRegex), - Provider: ecrProvider, + FilterRegex: regexp.MustCompile(privateRepoRegex), + Provider: ecrProvider, + ExtractRegion: ExtractRegionFromEcrImage, + ImageRegexIndex: EcrImageRegexGroup, } publicEcrExecutor := PodDescriber{ - FilterRegex: regexp.MustCompile(PublicRepoRegex), - Provider: ecrPublicProvider, + FilterRegex: regexp.MustCompile(PublicRepoRegex), + Provider: ecrPublicProvider, + ExtractRegion: ExtractRegionFromPublicEcrImage, + ImageRegexIndex: PublicEcrImageRegexIndex, } ecrFetcher := ECRFetcher{ @@ -351,9 +428,8 @@ func (s *ECRFetcherTestSuite) TestCreateFetcherErrorCases() { ctx := context.Background() err = ecrFetcher.Fetch(ctx, fetching.CycleMetadata{}) - + s.NoError(err) results := testhelper.CollectResources(s.resourceCh) s.Equal(0, len(results)) - s.EqualError(err, "could not retrieve pod's aws repositories: ecr error") } } diff --git a/resources/providers/awslib/ecr_provider.go b/resources/providers/awslib/ecr_provider.go index edf8d69736..a588ab08b8 100644 --- a/resources/providers/awslib/ecr_provider.go +++ b/resources/providers/awslib/ecr_provider.go @@ -26,39 +26,50 @@ import ( ) type ECRProvider struct { - client *ecr.Client +} + +type ECRProviderResponse struct { + Repositories []ecr.Repository + PaginationToken string } type EcrRepositoryDescriber interface { - DescribeRepositories(ctx context.Context, repoNames []string) ([]ecr.Repository, error) + DescribeRepositories(ctx context.Context, cfg aws.Config, repoNames []string, region string) (ECRProviderResponse, error) } -func NewEcrProvider(cfg aws.Config) *ECRProvider { - svc := ecr.New(cfg) - return &ECRProvider{ - client: svc, - } +func NewEcrProvider() *ECRProvider { + return &ECRProvider{} } // DescribeAllECRRepositories This method will return a maximum of 100 repository /// If we will ever wish to change it, DescribeRepositories returns results in paginated manner -func (provider *ECRProvider) DescribeAllECRRepositories(ctx context.Context) ([]ecr.Repository, error) { - /// When repoNames is nil, it will describe all the existing repositories - return provider.DescribeRepositories(ctx, nil) +func (provider *ECRProvider) DescribeAllECRRepositories(ctx context.Context, cfg aws.Config, region string) (ECRProviderResponse, error) { + /// When repoNames is nil, it will describe all the existing Repositories + return provider.DescribeRepositories(ctx, cfg, nil, region) } // DescribeRepositories This method will return a maximum of 100 repository /// If we will ever wish to change it, DescribeRepositories returns results in paginated manner -/// When repoNames is nil, it will describe all the existing repositories -func (provider *ECRProvider) DescribeRepositories(ctx context.Context, repoNames []string) ([]ecr.Repository, error) { +/// When repoNames is nil, it will describe all the existing Repositories +func (provider *ECRProvider) DescribeRepositories(ctx context.Context, cfg aws.Config, repoNames []string, region string) (ECRProviderResponse, error) { + if len(repoNames) == 0 { + return ECRProviderResponse{}, nil + } + + cfg.Region = region + svc := ecr.New(cfg) input := &ecr.DescribeRepositoriesInput{ RepositoryNames: repoNames, } - req := provider.client.DescribeRepositoriesRequest(input) + req := svc.DescribeRepositoriesRequest(input) + response, err := req.Send(ctx) if err != nil { - return nil, fmt.Errorf("failed to fetch repository:%s from ecr, error - %w", repoNames, err) + return ECRProviderResponse{}, fmt.Errorf("failed to fetch repository:%s from ecr, error - %w", repoNames, err) } - return response.Repositories, err + return ECRProviderResponse{ + Repositories: response.Repositories, + PaginationToken: *response.NextToken, + }, err } diff --git a/resources/providers/awslib/ecr_provider_mock.go b/resources/providers/awslib/ecr_provider_mock.go deleted file mode 100644 index 57bd24421f..0000000000 --- a/resources/providers/awslib/ecr_provider_mock.go +++ /dev/null @@ -1,86 +0,0 @@ -// Licensed to Elasticsearch B.V. under one or more contributor -// license agreements. See the NOTICE file distributed with -// this work for additional information regarding copyright -// ownership. Elasticsearch B.V. licenses this file to you under -// the Apache License, Version 2.0 (the "License"); you may -// not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, -// software distributed under the License is distributed on an -// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY -// KIND, either express or implied. See the License for the -// specific language governing permissions and limitations -// under the License. -// Code generated by mockery v2.10.4. DO NOT EDIT. - -package awslib - -import ( - context "context" - - ecr "github.com/aws/aws-sdk-go-v2/service/ecr" - mock "github.com/stretchr/testify/mock" -) - -// MockedEcrRepositoryDescriber is an autogenerated mock type for the EcrRepositoryDescriber type -type MockedEcrRepositoryDescriber struct { - mock.Mock -} - -type MockEcrRepositoryDescriber_Expecter struct { - mock *mock.Mock -} - -func (_m *MockedEcrRepositoryDescriber) EXPECT() *MockEcrRepositoryDescriber_Expecter { - return &MockEcrRepositoryDescriber_Expecter{mock: &_m.Mock} -} - -// DescribeRepositories provides a mock function with given fields: ctx, repoNames -func (_m *MockedEcrRepositoryDescriber) DescribeRepositories(ctx context.Context, repoNames []string) ([]ecr.Repository, error) { - ret := _m.Called(ctx, repoNames) - - var r0 []ecr.Repository - if rf, ok := ret.Get(0).(func(context.Context, []string) []ecr.Repository); ok { - r0 = rf(ctx, repoNames) - } else { - if ret.Get(0) != nil { - r0 = ret.Get(0).([]ecr.Repository) - } - } - - var r1 error - if rf, ok := ret.Get(1).(func(context.Context, []string) error); ok { - r1 = rf(ctx, repoNames) - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// MockEcrRepositoryDescriber_DescribeRepositories_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'DescribeRepositories' -type MockEcrRepositoryDescriber_DescribeRepositories_Call struct { - *mock.Call -} - -// DescribeRepositories is a helper method to define mock.On call -// - ctx context.Context -// - repoNames []string -func (_e *MockEcrRepositoryDescriber_Expecter) DescribeRepositories(ctx interface{}, repoNames interface{}) *MockEcrRepositoryDescriber_DescribeRepositories_Call { - return &MockEcrRepositoryDescriber_DescribeRepositories_Call{Call: _e.mock.On("DescribeRepositories", ctx, repoNames)} -} - -func (_c *MockEcrRepositoryDescriber_DescribeRepositories_Call) Run(run func(ctx context.Context, repoNames []string)) *MockEcrRepositoryDescriber_DescribeRepositories_Call { - _c.Call.Run(func(args mock.Arguments) { - run(args[0].(context.Context), args[1].([]string)) - }) - return _c -} - -func (_c *MockEcrRepositoryDescriber_DescribeRepositories_Call) Return(_a0 []ecr.Repository, _a1 error) *MockEcrRepositoryDescriber_DescribeRepositories_Call { - _c.Call.Return(_a0, _a1) - return _c -} diff --git a/resources/providers/awslib/ecr_public_provider.go b/resources/providers/awslib/ecr_public_provider.go index 5707b87ea6..d1e4ee3133 100644 --- a/resources/providers/awslib/ecr_public_provider.go +++ b/resources/providers/awslib/ecr_public_provider.go @@ -19,6 +19,7 @@ package awslib import ( "context" + "github.com/aws/aws-sdk-go-v2/aws" "github.com/aws/aws-sdk-go-v2/service/ecr" ) @@ -34,16 +35,20 @@ func NewEcrPublicProvider() *ECRPublicProvider { // DescribeAllECRRepositories This method will return a maximum of 100 repository /// If we will ever wish to change it, DescribeRepositories returns results in paginated manner -func (provider *ECRPublicProvider) DescribeAllECRRepositories(ctx context.Context) ([]ecr.Repository, error) { - /// When repoNames is nil, it will describe all the existing repositories +func (provider *ECRPublicProvider) DescribeAllECRRepositories(_ context.Context, _ aws.Config, _ string) (ECRProviderResponse, error) { + /// When repoNames is nil, it will describe all the existing Repositories var emptyArray = make([]ecr.Repository, 0) - return emptyArray, nil + return ECRProviderResponse{ + Repositories: emptyArray, + }, nil } // DescribeRepositories This method will return a maximum of 100 repository /// If we will ever wish to change it, DescribeRepositories returns results in paginated manner -/// When repoNames is nil, it will describe all the existing repositories -func (provider *ECRPublicProvider) DescribeRepositories(ctx context.Context, repoNames []string) ([]ecr.Repository, error) { +/// When repoNames is nil, it will describe all the existing Repositories +func (provider *ECRPublicProvider) DescribeRepositories(_ context.Context, _ aws.Config, _ []string, _ string) (ECRProviderResponse, error) { var emptyArray = make([]ecr.Repository, 0) - return emptyArray, nil + return ECRProviderResponse{ + Repositories: emptyArray, + }, nil } diff --git a/resources/providers/awslib/ecr_repository_describer_mock.go b/resources/providers/awslib/ecr_repository_describer_mock.go new file mode 100644 index 0000000000..7707da9ff6 --- /dev/null +++ b/resources/providers/awslib/ecr_repository_describer_mock.go @@ -0,0 +1,102 @@ +// Licensed to Elasticsearch B.V. under one or more contributor +// license agreements. See the NOTICE file distributed with +// this work for additional information regarding copyright +// ownership. Elasticsearch B.V. licenses this file to you under +// the Apache License, Version 2.0 (the "License"); you may +// not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. +// Code generated by mockery v2.10.4. DO NOT EDIT. + +package awslib + +import ( + context "context" + + aws "github.com/aws/aws-sdk-go-v2/aws" + + mock "github.com/stretchr/testify/mock" +) + +// MockEcrRepositoryDescriber is an autogenerated mock type for the EcrRepositoryDescriber type +type MockEcrRepositoryDescriber struct { + mock.Mock +} + +type MockEcrRepositoryDescriber_Expecter struct { + mock *mock.Mock +} + +func (_m *MockEcrRepositoryDescriber) EXPECT() *MockEcrRepositoryDescriber_Expecter { + return &MockEcrRepositoryDescriber_Expecter{mock: &_m.Mock} +} + +// DescribeRepositories provides a mock function with given fields: ctx, cfg, repoNames, region +func (_m *MockEcrRepositoryDescriber) DescribeRepositories(ctx context.Context, cfg aws.Config, repoNames []string, region string) (ECRProviderResponse, error) { + ret := _m.Called(ctx, cfg, repoNames, region) + + var r0 ECRProviderResponse + if rf, ok := ret.Get(0).(func(context.Context, aws.Config, []string, string) ECRProviderResponse); ok { + r0 = rf(ctx, cfg, repoNames, region) + } else { + r0 = ret.Get(0).(ECRProviderResponse) + } + + var r1 error + if rf, ok := ret.Get(1).(func(context.Context, aws.Config, []string, string) error); ok { + r1 = rf(ctx, cfg, repoNames, region) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +// MockEcrRepositoryDescriber_DescribeRepositories_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'DescribeRepositories' +type MockEcrRepositoryDescriber_DescribeRepositories_Call struct { + *mock.Call +} + +// DescribeRepositories is a helper method to define mock.On call +// - ctx context.Context +// - cfg aws.Config +// - repoNames []string +// - region string +func (_e *MockEcrRepositoryDescriber_Expecter) DescribeRepositories(ctx interface{}, cfg interface{}, repoNames interface{}, region interface{}) *MockEcrRepositoryDescriber_DescribeRepositories_Call { + return &MockEcrRepositoryDescriber_DescribeRepositories_Call{Call: _e.mock.On("DescribeRepositories", ctx, cfg, repoNames, region)} +} + +func (_c *MockEcrRepositoryDescriber_DescribeRepositories_Call) Run(run func(ctx context.Context, cfg aws.Config, repoNames []string, region string)) *MockEcrRepositoryDescriber_DescribeRepositories_Call { + _c.Call.Run(func(args mock.Arguments) { + run(args[0].(context.Context), args[1].(aws.Config), args[2].([]string), args[3].(string)) + }) + return _c +} + +func (_c *MockEcrRepositoryDescriber_DescribeRepositories_Call) Return(_a0 ECRProviderResponse, _a1 error) *MockEcrRepositoryDescriber_DescribeRepositories_Call { + _c.Call.Return(_a0, _a1) + return _c +} + +type mockConstructorTestingTNewMockEcrRepositoryDescriber interface { + mock.TestingT + Cleanup(func()) +} + +// NewMockEcrRepositoryDescriber creates a new instance of MockEcrRepositoryDescriber. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations. +func NewMockEcrRepositoryDescriber(t mockConstructorTestingTNewMockEcrRepositoryDescriber) *MockEcrRepositoryDescriber { + mock := &MockEcrRepositoryDescriber{} + mock.Mock.Test(t) + + t.Cleanup(func() { mock.AssertExpectations(t) }) + + return mock +}