Skip to content

Commit 89352d0

Browse files
hodaaaaaaaaaagemini-code-assist[bot]glasnt
authored
Add code samples for Connect Gateway API (#13311)
* Add code samples for Connect Gateway API * Update connectgateway/main.py Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com> * Update connectgateway/main.py Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com> * Update connectgateway/main.py Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com> * Update connectgateway/main.py Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com> * Update connectgateway/main.py Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com> * Use env variables for membership fields * Add tests to the Connect Gateway sample * Add tests to the Connect Gateway sample * Add tests to the Connect Gateway sample * Add tests to the Connect Gateway sample * Add tests to the Connect Gateway sample * Add tests to the Connect Gateway sample * Add tests to the Connect Gateway sample * Add tests to the Connect Gateway sample * Add tests to the Connect Gateway sample * Add tests to the Connect Gateway sample * address PR comments * address PR comments * address PR comments * address PR comments * address PR comments * address PR comments * use ADC instead of service account * use ADC instead of service account * use ADC instead of service account * use ADC instead of service account * use ADC instead of service account * Update connectgateway/requirements.txt * add back base noxfile_config, deleted accidentally * add back base noxfile_config, deleted accidentally * address lint errors * address lint errors * Update connectgateway/requirements-test.txt * Update connectgateway/requirements-test.txt * Cleanup excess comments --------- Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com> Co-authored-by: Katie McLaughlin <katie@glasnt.com>
1 parent b2af41b commit 89352d0

File tree

8 files changed

+229
-0
lines changed

8 files changed

+229
-0
lines changed

.github/CODEOWNERS

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -83,6 +83,7 @@
8383
/bigquery-datatransfer/**/* @GoogleCloudPlatform/api-bigquery @GoogleCloudPlatform/python-samples-reviewers @GoogleCloudPlatform/cloud-samples-reviewers
8484
/bigquery-migration/**/* @GoogleCloudPlatform/api-bigquery @GoogleCloudPlatform/python-samples-reviewers @GoogleCloudPlatform/cloud-samples-reviewers
8585
/bigquery-reservation/**/* @GoogleCloudPlatform/api-bigquery @GoogleCloudPlatform/python-samples-reviewers @GoogleCloudPlatform/cloud-samples-reviewers
86+
/connectgateway/**/* @GoogleCloudPlatform/connectgateway @GoogleCloudPlatform/python-samples-reviewers @GoogleCloudPlatform/cloud-samples-reviewers
8687
/dlp/**/* @GoogleCloudPlatform/googleapis-dlp @GoogleCloudPlatform/python-samples-reviewers @GoogleCloudPlatform/cloud-samples-reviewers
8788
/functions/spanner/* @GoogleCloudPlatform/api-spanner-python @GoogleCloudPlatform/functions-framework-google @GoogleCloudPlatform/python-samples-reviewers @GoogleCloudPlatform/cloud-samples-reviewers
8889
/healthcare/**/* @GoogleCloudPlatform/healthcare-life-sciences @GoogleCloudPlatform/python-samples-reviewers @GoogleCloudPlatform/cloud-samples-reviewers

.github/blunderbuss.yml

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -262,6 +262,10 @@ assign_prs_by:
262262
- "api: dataplex"
263263
to:
264264
- GoogleCloudPlatform/googleapi-dataplex
265+
- labels:
266+
- "api: connectgateway"
267+
to:
268+
- GoogleCloudPlatform/connectgateway
265269
# Self-service individuals
266270
- labels:
267271
- "api: auth"

connectgateway/README.md

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
# Sample Snippets for Connect Gateway API
2+
3+
## Quick Start
4+
5+
In order to run these samples, you first need to go through the following steps:
6+
7+
1. [Select or create a Cloud Platform project.](https://console.cloud.google.com/project)
8+
2. [Enable billing for your project.](https://cloud.google.com/billing/docs/how-to/modify-project#enable_billing_for_a_project)
9+
3. [Setup Authentication.](https://googleapis.dev/python/google-api-core/latest/auth.html)
10+
4. [Setup Connect Gateway.](https://cloud.google.com/kubernetes-engine/enterprise/multicluster-management/gateway/setup)

connectgateway/get_namespace.py

Lines changed: 97 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,97 @@
1+
# Copyright 2025 Google LLC
2+
#
3+
# Licensed under the Apache License, Version 2.0 (the "License");
4+
# you may not use this file except in compliance with the License.
5+
# You may obtain a copy of the License at
6+
#
7+
# http://www.apache.org/licenses/LICENSE-2.0
8+
#
9+
# Unless required by applicable law or agreed to in writing, software
10+
# distributed under the License is distributed on an "AS IS" BASIS,
11+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
# See the License for the specific language governing permissions and
13+
# limitations under the License.
14+
15+
# [START connectgateway_get_namespace]
16+
import os
17+
import sys
18+
19+
from google.api_core import exceptions
20+
import google.auth
21+
from google.auth.transport import requests
22+
from google.cloud.gkeconnect import gateway_v1
23+
from kubernetes import client
24+
25+
26+
SCOPES = ['https://www.googleapis.com/auth/cloud-platform']
27+
28+
29+
def get_gateway_url(membership_name: str, location: str) -> str:
30+
"""Fetches the GKE Connect Gateway URL for the specified membership."""
31+
try:
32+
client_options = {}
33+
if location != "global":
34+
# If the location is not global, the endpoint needs to be set to the regional endpoint.
35+
regional_endpoint = f"{location}-connectgateway.googleapis.com"
36+
client_options = {"api_endpoint": regional_endpoint}
37+
gateway_client = gateway_v1.GatewayControlClient(client_options=client_options)
38+
request = gateway_v1.GenerateCredentialsRequest()
39+
request.name = membership_name
40+
response = gateway_client.generate_credentials(request=request)
41+
print(f'GKE Connect Gateway Endpoint: {response.endpoint}')
42+
if not response.endpoint:
43+
print("Error: GKE Connect Gateway Endpoint is empty.")
44+
sys.exit(1)
45+
return response.endpoint
46+
except exceptions.NotFound as e:
47+
print(f'Membership not found: {e}')
48+
sys.exit(1)
49+
except Exception as e:
50+
print(f'Error fetching GKE Connect Gateway URL: {e}')
51+
sys.exit(1)
52+
53+
54+
def configure_kubernetes_client(gateway_url: str) -> client.CoreV1Api:
55+
"""Configures the Kubernetes client with the GKE Connect Gateway URL and credentials."""
56+
57+
configuration = client.Configuration()
58+
59+
# Configure the API client with the custom host.
60+
configuration.host = gateway_url
61+
62+
# Configure API key using default auth.
63+
credentials, _ = google.auth.default(scopes=SCOPES)
64+
auth_req = requests.Request()
65+
credentials.refresh(auth_req)
66+
configuration.api_key = {'authorization': f'Bearer {credentials.token}'}
67+
68+
api_client = client.ApiClient(configuration=configuration)
69+
return client.CoreV1Api(api_client)
70+
71+
72+
def get_default_namespace(api_client: client.CoreV1Api) -> None:
73+
"""Get default namespace in the Kubernetes cluster."""
74+
try:
75+
namespace = api_client.read_namespace(name="default")
76+
return namespace
77+
except client.ApiException as e:
78+
print(f"Error getting default namespace: {e}\nStatus: {e.status}\nReason: {e.reason}")
79+
sys.exit(1)
80+
81+
82+
def get_namespace(membership_name: str, location: str) -> None:
83+
"""Main function to connect to the cluster and get the default namespace."""
84+
gateway_url = get_gateway_url(membership_name, location)
85+
core_v1_api = configure_kubernetes_client(gateway_url)
86+
namespace = get_default_namespace(core_v1_api)
87+
print(f"\nDefault Namespace:\n{namespace}")
88+
89+
# [END connectgateway_get_namespace]
90+
91+
return namespace
92+
93+
94+
if __name__ == "__main__":
95+
MEMBERSHIP_NAME = os.environ.get('MEMBERSHIP_NAME')
96+
MEMBERSHIP_LOCATION = os.environ.get("MEMBERSHIP_LOCATION")
97+
namespace = get_namespace(MEMBERSHIP_NAME, MEMBERSHIP_LOCATION)

connectgateway/get_namespace_test.py

Lines changed: 89 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,89 @@
1+
# Copyright 2025 Google LLC
2+
#
3+
# Licensed under the Apache License, Version 2.0 (the "License");
4+
# you may not use this file except in compliance with the License.
5+
# You may obtain a copy of the License at
6+
#
7+
# http://www.apache.org/licenses/LICENSE-2.0
8+
#
9+
# Unless required by applicable law or agreed to in writing, software
10+
# distributed under the License is distributed on an "AS IS" BASIS,
11+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
# See the License for the specific language governing permissions and
13+
# limitations under the License.
14+
15+
import os
16+
from time import sleep
17+
import uuid
18+
19+
20+
from google.cloud import container_v1 as gke
21+
22+
import pytest
23+
24+
import get_namespace
25+
26+
PROJECT_ID = os.environ["GOOGLE_CLOUD_PROJECT"]
27+
ZONE = "us-central1-a"
28+
REGION = "us-central1"
29+
CLUSTER_NAME = f"cluster-{uuid.uuid4().hex[:10]}"
30+
31+
32+
@pytest.fixture(autouse=True)
33+
def setup_and_tear_down() -> None:
34+
create_cluster(PROJECT_ID, ZONE, CLUSTER_NAME)
35+
36+
yield
37+
38+
delete_cluster(PROJECT_ID, ZONE, CLUSTER_NAME)
39+
40+
41+
def poll_operation(client: gke.ClusterManagerClient, op_id: str) -> None:
42+
43+
while True:
44+
# Make GetOperation request
45+
operation = client.get_operation({"name": op_id})
46+
# Print the Operation Information
47+
print(operation)
48+
49+
# Stop polling when Operation is done.
50+
if operation.status == gke.Operation.Status.DONE:
51+
break
52+
53+
# Wait 30 seconds before polling again
54+
sleep(30)
55+
56+
57+
def create_cluster(project_id: str, location: str, cluster_name: str) -> None:
58+
"""Create a new GKE cluster in the given GCP Project and Zone/Region."""
59+
# Initialize the Cluster management client.
60+
client = gke.ClusterManagerClient()
61+
cluster_location = client.common_location_path(project_id, location)
62+
cluster_def = {
63+
"name": str(cluster_name),
64+
"initial_node_count": 1,
65+
"fleet": {"project": str(project_id)},
66+
}
67+
68+
# Create the request object with the location identifier.
69+
request = {"parent": cluster_location, "cluster": cluster_def}
70+
create_response = client.create_cluster(request)
71+
op_identifier = f"{cluster_location}/operations/{create_response.name}"
72+
# poll for the operation status and schedule a retry until the cluster is created
73+
poll_operation(client, op_identifier)
74+
75+
76+
def delete_cluster(project_id: str, location: str, cluster_name: str) -> None:
77+
"""Delete the created GKE cluster."""
78+
client = gke.ClusterManagerClient()
79+
cluster_location = client.common_location_path(project_id, location)
80+
cluster_name = f"{cluster_location}/clusters/{cluster_name}"
81+
client.delete_cluster({"name": cluster_name})
82+
83+
84+
def test_get_namespace() -> None:
85+
membership_name = f"projects/{PROJECT_ID}/locations/{REGION}/memberships/{CLUSTER_NAME}"
86+
results = get_namespace.get_namespace(membership_name, REGION)
87+
88+
assert results is not None
89+
assert results.metadata.name == "default"

connectgateway/noxfile_config.py

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
# Copyright 2025 Google LLC
2+
#
3+
# Licensed under the Apache License, Version 2.0 (the "License");
4+
# you may not use this file except in compliance with the License.
5+
# You may obtain a copy of the License at
6+
#
7+
# http://www.apache.org/licenses/LICENSE-2.0
8+
#
9+
# Unless required by applicable law or agreed to in writing, software
10+
# distributed under the License is distributed on an "AS IS" BASIS,
11+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
# See the License for the specific language governing permissions and
13+
# limitations under the License.
14+
15+
TEST_CONFIG_OVERRIDE = {
16+
# You can opt out from the test for specific Python versions.
17+
"ignored_versions": ["2.7", "3.7", "3.8", "3.9", "3.10", "3.11", "3.12"],
18+
"enforce_type_hints": True,
19+
"gcloud_project_env": "GOOGLE_CLOUD_PROJECT",
20+
"pip_version_override": None,
21+
"envs": {},
22+
}

connectgateway/requirements-test.txt

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
google-cloud-container==2.56.1
2+
pytest==8.3.5

connectgateway/requirements.txt

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
google-cloud-gke-connect-gateway==0.10.3
2+
google-auth==2.38.0
3+
kubernetes==32.0.1
4+
google-api-core==2.24.2

0 commit comments

Comments
 (0)