diff --git a/lib/cloud/gcp/kubernetes.go b/lib/cloud/gcp/kubernetes.go index 166d50723335d..d09033b1846ab 100644 --- a/lib/cloud/gcp/kubernetes.go +++ b/lib/cloud/gcp/kubernetes.go @@ -186,7 +186,7 @@ func (g *gkeClient) ListClusters(ctx context.Context, projectID string, location }, ) if err != nil { - return nil, trace.Wrap(err) + return nil, trace.Wrap(convertAPIError(err)) } var clusters []GKECluster for _, cluster := range res.Clusters { @@ -226,7 +226,7 @@ func (g *gkeClient) GetClusterRestConfig(ctx context.Context, cfg ClusterDetails }, ) if err != nil { - return nil, time.Time{}, trace.Wrap(err) + return nil, time.Time{}, trace.Wrap(convertAPIError(err)) } // Generate a SA Authentication Token. diff --git a/lib/srv/discovery/fetchers/gke.go b/lib/srv/discovery/fetchers/gke.go index 7ebb2ab37e384..eb8e5904804dc 100644 --- a/lib/srv/discovery/fetchers/gke.go +++ b/lib/srv/discovery/fetchers/gke.go @@ -112,6 +112,15 @@ func (a *gkeFetcher) getGKEClusters(ctx context.Context, projectID string) (type var clusters types.KubeClusters gkeClusters, err := a.GKEClient.ListClusters(ctx, projectID, a.Location) + if trace.IsAccessDenied(err) { + a.Log.WithFields(logrus.Fields{ + "project_id": projectID, + "location": a.Location, + }).Warn("Access denied to list GKE clusters") + return nil, nil + } else if err != nil { + return nil, trace.Wrap(err) + } for _, gkeCluster := range gkeClusters { cluster, err := a.getMatchingKubeCluster(gkeCluster) // trace.CompareFailed is returned if the cluster did not match the matcher filtering labels diff --git a/lib/srv/discovery/fetchers/gke_test.go b/lib/srv/discovery/fetchers/gke_test.go index 53374682b179d..75e1b2f389f7e 100644 --- a/lib/srv/discovery/fetchers/gke_test.go +++ b/lib/srv/discovery/fetchers/gke_test.go @@ -23,6 +23,7 @@ import ( "testing" containerpb "cloud.google.com/go/container/apiv1/containerpb" + "github.com/gravitational/trace" "github.com/sirupsen/logrus" "github.com/stretchr/testify/require" @@ -36,6 +37,7 @@ func TestGKEFetcher(t *testing.T) { location string filterLabels types.Labels projectID string + errs map[string]error } tests := []struct { name string @@ -110,11 +112,26 @@ func TestGKEFetcher(t *testing.T) { }, want: gkeClustersToResources(t, gkeMockClusters...), }, + { + name: "list everything but one project misses permissions", + args: args{ + location: "uswest2", + filterLabels: types.Labels{ + "env": []string{"prod", "stg"}, + }, + projectID: "*", + errs: map[string]error{ + "p2": trace.AccessDenied("no access"), + }, + }, + want: gkeClustersToResources(t, gkeMockClusters[:4]...), + }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { + client := newPopulatedGCPMock(tt.args.errs) cfg := GKEFetcherConfig{ - GKEClient: newPopulatedGCPMock(), + GKEClient: client, ProjectClient: newPopulatedGCPProjectsMock(), FilterLabels: tt.args.filterLabels, Location: tt.args.location, @@ -134,9 +151,13 @@ func TestGKEFetcher(t *testing.T) { type mockGKEAPI struct { gcp.GKEClient clusters []gcp.GKECluster + errs map[string]error } func (m *mockGKEAPI) ListClusters(ctx context.Context, projectID string, location string) ([]gcp.GKECluster, error) { + if err, ok := m.errs[projectID]; ok { + return nil, err + } var clusters []gcp.GKECluster for _, cluster := range m.clusters { if cluster.ProjectID != projectID { @@ -148,9 +169,10 @@ func (m *mockGKEAPI) ListClusters(ctx context.Context, projectID string, locatio return clusters, nil } -func newPopulatedGCPMock() *mockGKEAPI { +func newPopulatedGCPMock(errs map[string]error) *mockGKEAPI { return &mockGKEAPI{ clusters: gkeMockClusters, + errs: errs, } }