Skip to content

Commit fdcdde8

Browse files
authored
feat(bigtable): add an e2e flow for direct access with instructions (#12939)
This function attempts to establish a connection to the Bigtable instance using settings that force the use of DirectAccess. It then checks if the underlying gRPC connection is indeed using a DirectPath IP address.
1 parent 0464d0b commit fdcdde8

File tree

3 files changed

+174
-2
lines changed

3 files changed

+174
-2
lines changed

bigtable/direct_access_check.go

Lines changed: 129 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,129 @@
1+
/*
2+
Copyright 2025 Google LLC
3+
4+
Licensed under the Apache License, Version 2.0 (the "License");
5+
you may not use this file except in compliance with the License.
6+
You may obtain a copy of the License at
7+
8+
http://www.apache.org/licenses/LICENSE-2.0
9+
10+
Unless required by applicable law or agreed to in writing, software
11+
distributed under the License is distributed on an "AS IS" BASIS,
12+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
See the License for the specific language governing permissions and
14+
limitations under the License.
15+
*/
16+
17+
package bigtable
18+
19+
import (
20+
"context"
21+
"errors"
22+
"fmt"
23+
"os"
24+
"strconv"
25+
"strings"
26+
27+
"google.golang.org/api/option"
28+
"google.golang.org/grpc"
29+
"google.golang.org/grpc/peer"
30+
)
31+
32+
const (
33+
directPathIPV6Prefix = "[2001:4860:8040"
34+
directPathIPV4Prefix = "34.126"
35+
)
36+
37+
// This function attempts to establish a connection to the Bigtable instance using
38+
// Direct Access. It then checks if the underlying
39+
// gRPC connection is indeed using a DirectPath IP address.
40+
//
41+
// Prerequisites for successful Direct Access connectivity:
42+
// 1. The environment variable `CBT_ENABLE_DIRECTPATH` must be set to "true".
43+
// 2. The code must be running in a Google Cloud environment (e.g., GCE VM, GKE)
44+
// that is properly configured for Direct Access. This includes ensuring
45+
// that your routes and firewall rules allow egress traffic to the
46+
// Direct Access IP ranges: 34.126.0.0/18 and 2001:4860:8040::/42.
47+
// 3. The service account must have the necessary IAM permissions.
48+
//
49+
// Parameters:
50+
// - ctx: The context for the operation.
51+
// - project: The Google Cloud project ID.
52+
// - instance: The Cloud Bigtable instance ID.
53+
// - appProfile: The application profile ID to use for the connection. Defaults to "default" if empty.
54+
// - opts: Additional option.ClientOption to configure the Bigtable client. These are
55+
// appended to the options used to force DirectPath.
56+
//
57+
// Returns:
58+
// - bool: True if DirectPath is successfully used for the connection, False otherwise.
59+
// - error: An error if the check could not be completed, or if DirectPath is not
60+
// enabled/configured. Specific error causes include:
61+
// - "CBT_ENABLE_DIRECTPATH=true is not set in env var": The required environment variable is missing.
62+
// - Failure to create the Bigtable client (e.g., invalid project/instance).
63+
// - Failure during the PingAndWarm call (e.g., network issue, permissions).
64+
//
65+
66+
// CheckDirectAccessSupported verifies if Direct Access connectivity is enabled, configured,
67+
// and actively being used for the given Cloud Bigtable instance.
68+
func CheckDirectAccessSupported(ctx context.Context, project, instance, appProfile string, opts ...option.ClientOption) (bool, error) {
69+
// Check if env variable is set to true
70+
// Inside the function
71+
envVal := os.Getenv("CBT_ENABLE_DIRECTPATH")
72+
if envVal == "" {
73+
return false, errors.New("CBT_ENABLE_DIRECTPATH environment variable is not set")
74+
}
75+
isEnvEnabled, err := strconv.ParseBool(envVal)
76+
if err != nil {
77+
return false, fmt.Errorf("invalid value for CBT_ENABLE_DIRECTPATH: %s, must be true or false: %w", envVal, err)
78+
}
79+
if !isEnvEnabled {
80+
return false, errors.New("CBT_ENABLE_DIRECTPATH is not set to true")
81+
}
82+
isDirectPathUsed := false
83+
// Define the unary client interceptor
84+
interceptor := func(ctx context.Context, method string, req, reply interface{}, cc *grpc.ClientConn, invoker grpc.UnaryInvoker, callOpts ...grpc.CallOption) error {
85+
// Create a new context with a peer to be captured by FromContext
86+
peerInfo := &peer.Peer{}
87+
allCallOpts := append(callOpts, grpc.Peer(peerInfo))
88+
89+
// Invoke the original RPC call
90+
err := invoker(ctx, method, req, reply, cc, allCallOpts...)
91+
if err != nil {
92+
return err
93+
}
94+
95+
// After the call, store the captured peer address
96+
if peerInfo.Addr != nil {
97+
remoteIP := peerInfo.Addr.String()
98+
if strings.HasPrefix(remoteIP, directPathIPV4Prefix) || strings.HasPrefix(remoteIP, directPathIPV6Prefix) {
99+
isDirectPathUsed = true
100+
}
101+
}
102+
103+
return nil
104+
}
105+
106+
// register the interceptor
107+
allOpts := append([]option.ClientOption{
108+
option.WithGRPCDialOption(grpc.WithUnaryInterceptor(interceptor)),
109+
}, opts...)
110+
111+
config := ClientConfig{
112+
AppProfile: appProfile,
113+
MetricsProvider: NoopMetricsProvider{},
114+
}
115+
116+
client, err := NewClientWithConfig(ctx, project, instance, config, allOpts...)
117+
if err != nil {
118+
return false, fmt.Errorf("CheckDirectConnectivitySupported: failed to create Bigtable client for checking DirectAccess %w", err)
119+
}
120+
defer client.Close()
121+
122+
// Call the PingAndWarm method
123+
err = client.PingAndWarm(ctx)
124+
if err != nil {
125+
return false, fmt.Errorf("CheckDirectConnectivitySupported: PingAndWarm failed: %w", err)
126+
}
127+
128+
return isDirectPathUsed, nil
129+
}
Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
/*
2+
Copyright 2025 Google LLC
3+
4+
Licensed under the Apache License, Version 2.0 (the "License");
5+
you may not use this file except in compliance with the License.
6+
You may obtain a copy of the License at
7+
8+
http://www.apache.org/licenses/LICENSE-2.0
9+
10+
Unless required by applicable law or agreed to in writing, software
11+
distributed under the License is distributed on an "AS IS" BASIS,
12+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
See the License for the specific language governing permissions and
14+
limitations under the License.
15+
*/
16+
17+
package bigtable
18+
19+
import (
20+
"context"
21+
"log"
22+
"os"
23+
)
24+
25+
func ExampleCheckDirectAccessSupported() {
26+
// Example Usage:
27+
ctx := context.Background()
28+
projectID := "my-project"
29+
instanceID := "my-instance"
30+
appProfileID := "default"
31+
32+
// Set the environment variable if not already set
33+
os.Setenv("CBT_ENABLE_DIRECTPATH", "true")
34+
35+
isDirectPath, err := CheckDirectAccessSupported(ctx, projectID, instanceID, appProfileID)
36+
if err != nil {
37+
log.Fatalf("DirectPath check failed: %v", err)
38+
}
39+
40+
if isDirectPath {
41+
log.Printf("DirectPath connectivity is active for %s/%s", projectID, instanceID)
42+
} else {
43+
log.Printf("DirectPath connectivity is NOT active for %s/%s", projectID, instanceID)
44+
}
45+
}

bigtable/integration_test.go

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -58,8 +58,6 @@ import (
5858
)
5959

6060
const (
61-
directPathIPV6Prefix = "[2001:4860:8040"
62-
directPathIPV4Prefix = "34.126"
6361
timeUntilResourceCleanup = time.Hour * 12 // 12 hours
6462
prefixOfInstanceResources = "bt-it-"
6563
prefixOfClusterResources = "bt-c-"

0 commit comments

Comments
 (0)