From 3186e11d7027645b6326d8b27e4fd012f946a8f0 Mon Sep 17 00:00:00 2001 From: Haihan Jiang Date: Sat, 30 May 2026 04:27:32 -0700 Subject: [PATCH] Add GKE client factory support --- src/gcp_scanner/client/client_factory.py | 2 ++ src/gcp_scanner/client/container_client.py | 38 ++++++++++++++++++++++ src/gcp_scanner/scanner.py | 4 +-- src/gcp_scanner/test_unit.py | 22 +++++++++++++ 4 files changed, 63 insertions(+), 3 deletions(-) create mode 100644 src/gcp_scanner/client/container_client.py diff --git a/src/gcp_scanner/client/client_factory.py b/src/gcp_scanner/client/client_factory.py index b0066f13..397d0534 100644 --- a/src/gcp_scanner/client/client_factory.py +++ b/src/gcp_scanner/client/client_factory.py @@ -21,6 +21,7 @@ from gcp_scanner.client.cloud_functions_client import CloudFunctionsClient from gcp_scanner.client.cloud_resource_manager_client import CloudResourceManagerClient from gcp_scanner.client.compute_client import ComputeClient +from gcp_scanner.client.container_client import ContainerClient from gcp_scanner.client.datastore_client import DatastoreClient from gcp_scanner.client.dns_client import DNSClient from gcp_scanner.client.domains_client import DomainsClient @@ -49,6 +50,7 @@ class ClientFactory: "cloudkms": CloudKMSClient, "cloudresourcemanager": CloudResourceManagerClient, "compute": ComputeClient, + "container": ContainerClient, "datastore": DatastoreClient, "domains": DomainsClient, "dns": DNSClient, diff --git a/src/gcp_scanner/client/container_client.py b/src/gcp_scanner/client/container_client.py new file mode 100644 index 00000000..5d9cf1bd --- /dev/null +++ b/src/gcp_scanner/client/container_client.py @@ -0,0 +1,38 @@ +# Copyright 2026 Google LLC +# +# Licensed 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 +# +# https://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. + +from google.cloud import container_v1 +from httplib2 import Credentials + +from .interface_client import IClient + + +class ContainerClient(IClient): + """ContainerClient class.""" + + def get_service( + self, + credentials: Credentials, + ) -> container_v1.services.cluster_manager.client.ClusterManagerClient: + """Get service for GKE container resources. + + Args: + credentials: An google.oauth2.credentials.Credentials object. + + Returns: + A ClusterManagerClient. + """ + return container_v1.services.cluster_manager.ClusterManagerClient( + credentials=credentials, + ) diff --git a/src/gcp_scanner/scanner.py b/src/gcp_scanner/scanner.py index e0b76daf..1f0c9cbf 100644 --- a/src/gcp_scanner/scanner.py +++ b/src/gcp_scanner/scanner.py @@ -370,9 +370,7 @@ def iam_client_for_credentials( def gke_client_for_credentials( credentials: Credentials, ) -> container_v1.services.cluster_manager.client.ClusterManagerClient: - return container_v1.services.cluster_manager.ClusterManagerClient( - credentials=credentials - ) + return ClientFactory.get_client("container").get_service(credentials) def get_sa_details_from_key_files(key_path): diff --git a/src/gcp_scanner/test_unit.py b/src/gcp_scanner/test_unit.py index eb707a67..f5e51d7b 100644 --- a/src/gcp_scanner/test_unit.py +++ b/src/gcp_scanner/test_unit.py @@ -41,6 +41,7 @@ from .client.cloud_functions_client import CloudFunctionsClient from .client.cloud_resource_manager_client import CloudResourceManagerClient from .client.compute_client import ComputeClient +from .client.container_client import ContainerClient from .client.datastore_client import DatastoreClient from .client.dns_client import DNSClient from .client.domains_client import DomainsClient @@ -887,6 +888,27 @@ def test_get_client_compute(self): client = ClientFactory.get_client("compute") self.assertIsInstance(client, ComputeClient) + def test_get_client_container(self): + """Test get_client method with 'container' name.""" + client = ClientFactory.get_client("container") + self.assertIsInstance(client, ContainerClient) + + @patch( + "gcp_scanner.client.container_client.container_v1.services." + "cluster_manager.ClusterManagerClient" + ) + def test_container_client_get_service(self, mock_cluster_manager_client): + """Test ContainerClient creates the GKE cluster manager client.""" + credentials_obj = Mock() + client = ContainerClient() + + service = client.get_service(credentials_obj) + + self.assertEqual(service, mock_cluster_manager_client.return_value) + mock_cluster_manager_client.assert_called_once_with( + credentials=credentials_obj, + ) + def test_get_client_appengine(self): """Test get_client method with 'appengine' name.""" client = ClientFactory.get_client("appengine")