Skip to content

Commit bc50b3a

Browse files
feat: add code samples for Connect Gateway API (#5299)
* Add Connect Gateway Sample * Add Connect Gateway Sample * Add Connect Gateway Sample * change sample to get namespace * Update .github/CODEOWNERS Co-authored-by: Katie McLaughlin <[email protected]> * change sample to get namespace * add tests * add tests * Add tests for the sample * update tests to use gke lib * remove parent dir changes * remove parent dir changes * remove parent dir changes * add connectgateway to go.work * add connectgateway to go.work * update min go version * remove changes to other dirs * update to address pr comments * revert endpoint override changes * use regional endpoint only * change version to 1.23 * change version to 1.23 * change version to 1.23 * change version to 1.23 --------- Co-authored-by: Eric Schmidt <[email protected]>
1 parent 6aeba76 commit bc50b3a

File tree

7 files changed

+561
-1
lines changed

7 files changed

+561
-1
lines changed

.github/CODEOWNERS

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,7 @@
4747
/secretmanager/ @GoogleCloudPlatform/go-samples-reviewers @GoogleCloudPlatform/cloud-samples-reviewers @GoogleCloudPlatform/cloud-secrets-team
4848
/securitycenter/ @GoogleCloudPlatform/go-samples-reviewers @GoogleCloudPlatform/cloud-samples-reviewers @GoogleCloudPlatform/gcp-security-command-center
4949
/translate/ @GoogleCloudPlatform/go-samples-reviewers @GoogleCloudPlatform/cloud-samples-reviewers @GoogleCloudPlatform/cloud-ml-translate-dev
50+
/connectgateway/ @GoogleCloudPlatform/go-samples-reviewers @GoogleCloudPlatform/cloud-samples-reviewers @GoogleCloudPlatform/connectgateway
5051

5152
# Does not have owner
5253
/cdn/ @GoogleCloudPlatform/go-samples-reviewers @GoogleCloudPlatform/cloud-samples-reviewers

.github/blunderbuss.yml

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -103,4 +103,8 @@ assign_prs_by:
103103
- labels:
104104
- "api: securitycenter"
105105
to:
106-
- GoogleCloudPlatform/gcp-security-command-center
106+
- GoogleCloudPlatform/gcp-security-command-center
107+
- labels:
108+
- "api: connectgateway"
109+
to:
110+
- GoogleCloudPlatform/connectgateway

connectgateway/get_namespace.go

Lines changed: 95 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,95 @@
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+
// https://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+
package connectgateway
16+
17+
// [START connectgateway_get_namespace]
18+
19+
import (
20+
"context"
21+
"fmt"
22+
"io"
23+
"net/http"
24+
25+
"golang.org/x/oauth2"
26+
"golang.org/x/oauth2/google"
27+
"google.golang.org/api/option"
28+
29+
gateway "cloud.google.com/go/gkeconnect/gateway/apiv1"
30+
gatewaypb "cloud.google.com/go/gkeconnect/gateway/apiv1/gatewaypb"
31+
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
32+
"k8s.io/client-go/kubernetes"
33+
"k8s.io/client-go/rest"
34+
)
35+
36+
// getNamespace retrieves the Connect Gateway URL associated with the input
37+
// membership. It then creates a kubernetes client using the retrieved Gateway
38+
// URL to make requests to the underlying cluster.
39+
func getNamespace(w io.Writer, membershipName, region string) error {
40+
ctx := context.Background()
41+
// If the membership location is regional, then the regional endpoint needs to be set for the client.
42+
// Global memberships do not require this override as the default endpoint is global.
43+
opts := option.WithEndpoint(fmt.Sprintf("%v-connectgateway.googleapis.com", region))
44+
45+
// Use Gateway Control to retrieve the Connect Gateway URL to be used as the
46+
// host of the kubernetes client.
47+
gatewayClient, err := gateway.NewGatewayControlRESTClient(ctx, opts)
48+
if err != nil {
49+
return fmt.Errorf("failed to create Connect Gateway client: %v", err)
50+
}
51+
defer gatewayClient.Close()
52+
53+
req := &gatewaypb.GenerateCredentialsRequest{
54+
Name: membershipName,
55+
}
56+
resp, err := gatewayClient.GenerateCredentials(ctx, req)
57+
if err != nil {
58+
return fmt.Errorf("failed to fetch Connect Gateway URL for membership %s: %w", membershipName, err)
59+
}
60+
gatewayURL := resp.Endpoint
61+
fmt.Printf("Connect Gateway Endpoint: %s\n", gatewayURL)
62+
63+
// Configure the kubernetes client library using the Connect Gateway URL and
64+
// application default credentials.
65+
scopes := "https://www.googleapis.com/auth/cloud-platform"
66+
tokenSource, err := google.DefaultTokenSource(ctx, scopes)
67+
if err != nil {
68+
return fmt.Errorf("failed to get default credentials: %w", err)
69+
}
70+
wrapTransport := func(rt http.RoundTripper) http.RoundTripper {
71+
return &oauth2.Transport{
72+
Source: tokenSource,
73+
Base: rt,
74+
}
75+
}
76+
config := &rest.Config{
77+
Host: gatewayURL,
78+
WrapTransport: wrapTransport,
79+
}
80+
kubeClient, err := kubernetes.NewForConfig(config)
81+
if err != nil {
82+
return fmt.Errorf("failed to create Kubernetes client: %w", err)
83+
}
84+
85+
// Call GetNamespace using the kubernetes client.
86+
opt := metav1.GetOptions{}
87+
namespace, err := kubeClient.CoreV1().Namespaces().Get(context.Background(), "default", opt)
88+
if err != nil {
89+
return fmt.Errorf("failed to get namespace: %w", err)
90+
}
91+
fmt.Fprintf(w, "\nDefault Namespace:\n%#v", namespace)
92+
return nil
93+
}
94+
95+
// [END connectgateway_get_namespace]
Lines changed: 125 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,125 @@
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+
// https://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+
package connectgateway
16+
17+
import (
18+
"bytes"
19+
"context"
20+
"fmt"
21+
"log"
22+
"strings"
23+
"testing"
24+
"time"
25+
26+
"github.com/GoogleCloudPlatform/golang-samples/internal/testutil"
27+
"github.com/google/uuid"
28+
gke "google.golang.org/api/container/v1"
29+
)
30+
31+
var (
32+
zone = "us-central1-a"
33+
region = "us-central1"
34+
clusterName = fmt.Sprintf("cluster-%s", uuid.New().String()[:10])
35+
)
36+
37+
func TestGetNamespace(t *testing.T) {
38+
ctx := context.Background()
39+
tc := testutil.EndToEndTest(t)
40+
// Setup cluster.
41+
if err := createCluster(ctx, tc.ProjectID, zone, clusterName); err != nil {
42+
t.Fatalf("failed to create cluster: %v", err)
43+
}
44+
defer deleteCluster(ctx, tc.ProjectID, zone, clusterName)
45+
46+
membershipName := fmt.Sprintf("projects/%s/locations/%s/memberships/%s", tc.ProjectID, region, clusterName)
47+
var buf bytes.Buffer
48+
err := getNamespace(&buf, membershipName, region)
49+
if err != nil {
50+
t.Fatalf("getNamespace failed: %v", err)
51+
}
52+
53+
got := buf.String()
54+
fmt.Print(got)
55+
if want := "Name:\"default\""; !strings.Contains(got, want) {
56+
t.Errorf("got %q, want %q", got, want)
57+
}
58+
}
59+
60+
func createCluster(ctx context.Context, projectID, location, clusterName string) error {
61+
svc, err := gke.NewService(ctx)
62+
if err != nil {
63+
log.Fatalf("Could not initialize gke client: %v", err)
64+
}
65+
clusterLocation := fmt.Sprintf("projects/%s/locations/%s", projectID, location)
66+
67+
req := &gke.CreateClusterRequest{
68+
Parent: clusterLocation,
69+
Cluster: &gke.Cluster{
70+
Name: clusterName,
71+
InitialNodeCount: 1,
72+
Fleet: &gke.Fleet{
73+
Project: projectID,
74+
},
75+
},
76+
}
77+
78+
fmt.Printf("Creating cluster %s in %s...\n", clusterName, clusterLocation)
79+
resp, err := svc.Projects.Zones.Clusters.Create(projectID, location, req).Do()
80+
if err != nil {
81+
return fmt.Errorf("failed to create cluster: %v", err.Error())
82+
}
83+
84+
return pollOperation(svc, projectID, resp.Name)
85+
}
86+
87+
func pollOperation(svc *gke.Service, projectId, opID string) error {
88+
fmt.Printf("Polling operation: %s\n", opID)
89+
for {
90+
91+
op, err := svc.Projects.Zones.Operations.Get(projectId, zone, opID).Do()
92+
if err != nil {
93+
return fmt.Errorf("failed to get operation %s: %v", opID, err)
94+
}
95+
fmt.Printf("Operation status: %v\n", op)
96+
97+
if op.Status == "RUNNING" {
98+
fmt.Println("Waiting 30 seconds before polling again...")
99+
time.Sleep(30 * time.Second)
100+
continue
101+
}
102+
103+
if op.Status == "DONE" {
104+
fmt.Println("Operation completed successfully.")
105+
return nil
106+
}
107+
108+
return fmt.Errorf("operation failed with status %v", op.Status)
109+
}
110+
}
111+
112+
func deleteCluster(ctx context.Context, projectID, location, clusterName string) error {
113+
svc, err := gke.NewService(ctx)
114+
if err != nil {
115+
log.Fatalf("Could not initialize gke client: %v", err)
116+
}
117+
clusterFullName := fmt.Sprintf("projects/%s/locations/%s/clusters/%s", projectID, location, clusterName)
118+
119+
fmt.Printf("Deleting cluster %s...\n", clusterFullName)
120+
_, err = svc.Projects.Zones.Clusters.Delete(projectID, zone, clusterName).Do()
121+
if err != nil {
122+
return fmt.Errorf("failed to delete cluster %v: %v", clusterName, err)
123+
}
124+
return nil
125+
}

connectgateway/go.mod

Lines changed: 91 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,91 @@
1+
module github.com/GoogleCloudPlatform/golang-samples/connectgateway
2+
3+
go 1.23.0
4+
5+
require (
6+
cloud.google.com/go/gkeconnect v0.12.4
7+
github.com/GoogleCloudPlatform/golang-samples v0.0.0-20250512171409-d5befca4cf89
8+
github.com/google/uuid v1.6.0
9+
golang.org/x/oauth2 v0.29.0
10+
google.golang.org/api v0.231.0
11+
k8s.io/apimachinery v0.32.4
12+
k8s.io/client-go v0.32.4
13+
)
14+
15+
require (
16+
cel.dev/expr v0.20.0 // indirect
17+
cloud.google.com/go v0.118.3 // indirect
18+
cloud.google.com/go/auth v0.16.1 // indirect
19+
cloud.google.com/go/auth/oauth2adapt v0.2.8 // indirect
20+
cloud.google.com/go/compute/metadata v0.6.0 // indirect
21+
cloud.google.com/go/iam v1.4.0 // indirect
22+
cloud.google.com/go/monitoring v1.24.0 // indirect
23+
cloud.google.com/go/storage v1.50.0 // indirect
24+
github.com/GoogleCloudPlatform/opentelemetry-operations-go/detectors/gcp v1.26.0 // indirect
25+
github.com/GoogleCloudPlatform/opentelemetry-operations-go/exporter/metric v0.49.0 // indirect
26+
github.com/GoogleCloudPlatform/opentelemetry-operations-go/internal/resourcemapping v0.49.0 // indirect
27+
github.com/cespare/xxhash/v2 v2.3.0 // indirect
28+
github.com/cncf/xds/go v0.0.0-20250121191232-2f005788dc42 // indirect
29+
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect
30+
github.com/emicklei/go-restful/v3 v3.12.2 // indirect
31+
github.com/envoyproxy/go-control-plane/envoy v1.32.4 // indirect
32+
github.com/envoyproxy/protoc-gen-validate v1.2.1 // indirect
33+
github.com/felixge/httpsnoop v1.0.4 // indirect
34+
github.com/fxamacker/cbor/v2 v2.8.0 // indirect
35+
github.com/go-jose/go-jose/v4 v4.0.4 // indirect
36+
github.com/go-logr/logr v1.4.2 // indirect
37+
github.com/go-logr/stdr v1.2.2 // indirect
38+
github.com/go-openapi/jsonpointer v0.21.1 // indirect
39+
github.com/go-openapi/jsonreference v0.21.0 // indirect
40+
github.com/go-openapi/swag v0.23.1 // indirect
41+
github.com/gogo/protobuf v1.3.2 // indirect
42+
github.com/golang/protobuf v1.5.4 // indirect
43+
github.com/google/gnostic-models v0.6.9 // indirect
44+
github.com/google/go-cmp v0.7.0 // indirect
45+
github.com/google/gofuzz v1.2.0 // indirect
46+
github.com/google/s2a-go v0.1.9 // indirect
47+
github.com/googleapis/enterprise-certificate-proxy v0.3.6 // indirect
48+
github.com/googleapis/gax-go/v2 v2.14.1 // indirect
49+
github.com/josharian/intern v1.0.0 // indirect
50+
github.com/json-iterator/go v1.1.12 // indirect
51+
github.com/mailru/easyjson v0.9.0 // indirect
52+
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
53+
github.com/modern-go/reflect2 v1.0.2 // indirect
54+
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect
55+
github.com/pkg/errors v0.9.1 // indirect
56+
github.com/planetscale/vtprotobuf v0.6.1-0.20240319094008-0393e58bdf10 // indirect
57+
github.com/spiffe/go-spiffe/v2 v2.5.0 // indirect
58+
github.com/x448/float16 v0.8.4 // indirect
59+
github.com/zeebo/errs v1.4.0 // indirect
60+
go.opentelemetry.io/auto/sdk v1.1.0 // indirect
61+
go.opentelemetry.io/contrib/detectors/gcp v1.34.0 // indirect
62+
go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.60.0 // indirect
63+
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.60.0 // indirect
64+
go.opentelemetry.io/otel v1.35.0 // indirect
65+
go.opentelemetry.io/otel/metric v1.35.0 // indirect
66+
go.opentelemetry.io/otel/sdk v1.35.0 // indirect
67+
go.opentelemetry.io/otel/sdk/metric v1.35.0 // indirect
68+
go.opentelemetry.io/otel/trace v1.35.0 // indirect
69+
golang.org/x/crypto v0.37.0 // indirect
70+
golang.org/x/net v0.39.0 // indirect
71+
golang.org/x/sync v0.13.0 // indirect
72+
golang.org/x/sys v0.32.0 // indirect
73+
golang.org/x/term v0.31.0 // indirect
74+
golang.org/x/text v0.24.0 // indirect
75+
golang.org/x/time v0.11.0 // indirect
76+
google.golang.org/genproto v0.0.0-20250303144028-a0af3efb3deb // indirect
77+
google.golang.org/genproto/googleapis/api v0.0.0-20250428153025-10db94c68c34 // indirect
78+
google.golang.org/genproto/googleapis/rpc v0.0.0-20250428153025-10db94c68c34 // indirect
79+
google.golang.org/grpc v1.72.0 // indirect
80+
google.golang.org/protobuf v1.36.6 // indirect
81+
gopkg.in/evanphx/json-patch.v4 v4.12.0 // indirect
82+
gopkg.in/inf.v0 v0.9.1 // indirect
83+
gopkg.in/yaml.v3 v3.0.1 // indirect
84+
k8s.io/api v0.32.4 // indirect
85+
k8s.io/klog/v2 v2.130.1 // indirect
86+
k8s.io/kube-openapi v0.0.0-20241105132330-32ad38e42d3f // indirect
87+
k8s.io/utils v0.0.0-20241104100929-3ea5e8cea738 // indirect
88+
sigs.k8s.io/json v0.0.0-20241010143419-9aa6b5e7a4b3 // indirect
89+
sigs.k8s.io/structured-merge-diff/v4 v4.4.2 // indirect
90+
sigs.k8s.io/yaml v1.4.0 // indirect
91+
)

0 commit comments

Comments
 (0)