From 9eb3e5cee8de08437480d884413896218edd4708 Mon Sep 17 00:00:00 2001 From: Prince Kumar Date: Wed, 10 Jun 2026 03:28:21 +0000 Subject: [PATCH 1/3] test(grpc_validation): e2e test for grpc direct-path fallback --- .../grpc_validation/grpc_validation_test.go | 124 +++++++++++++----- .../grpc_validation/setup_test.go | 13 ++ 2 files changed, 106 insertions(+), 31 deletions(-) diff --git a/tools/integration_tests/grpc_validation/grpc_validation_test.go b/tools/integration_tests/grpc_validation/grpc_validation_test.go index a40ce727c13..81c9f3ab02f 100644 --- a/tools/integration_tests/grpc_validation/grpc_validation_test.go +++ b/tools/integration_tests/grpc_validation/grpc_validation_test.go @@ -102,61 +102,123 @@ func TestGRPCValidationSuite(t *testing.T) { func (g *gRPCValidation) TestGRPCDirectPathConnections() { testCases := []struct { - name string - bucketName string - expectedSuccess bool - expectedLogSubstring string + name string + bucketName string + grpcPathStrategy string + expectedSuccess bool + expectedLogSubstrings []string }{ { - name: "SingleRegion_Success", - bucketName: g.singleRegionBucketForGRPCSuccess, - expectedLogSubstring: fmt.Sprintf("Successfully connected over gRPC DirectPath for %s", g.singleRegionBucketForGRPCSuccess), + name: "SingleRegion_Success_FallbackStrategy", + bucketName: g.singleRegionBucketForGRPCSuccess, + grpcPathStrategy: "direct-path-with-fallback", + expectedSuccess: true, + expectedLogSubstrings: []string{"DirectPath verification succeeded, continuing with DirectPath."}, }, { - name: "SingleRegion_Failure", - bucketName: g.singleRegionBucketForGRPCFailure, - expectedLogSubstring: fmt.Sprintf("Direct path connectivity unavailable for %s, reason:", g.singleRegionBucketForGRPCFailure), + name: "SingleRegion_Failure_FallbackStrategy", + bucketName: g.singleRegionBucketForGRPCFailure, + grpcPathStrategy: "direct-path-with-fallback", + expectedSuccess: true, + expectedLogSubstrings: []string{ + "DirectPath verification failed", + "Grpc dp is not available and falling back to Http.", + }, }, { - name: "MultiRegion_Success", - bucketName: g.multiRegionBucketForGRPCSuccess, - expectedLogSubstring: fmt.Sprintf("Successfully connected over gRPC DirectPath for %s", g.multiRegionBucketForGRPCSuccess), + name: "SingleRegion_Success_DirectPathOnlyStrategy", + bucketName: g.singleRegionBucketForGRPCSuccess, + grpcPathStrategy: "direct-path-only", + expectedSuccess: true, + expectedLogSubstrings: []string{"DirectPath verification succeeded, continuing with DirectPath."}, }, { - name: "MultiRegion_Failure", - bucketName: g.multiRegionBucketForGRPCFailure, - expectedLogSubstring: fmt.Sprintf("Direct path connectivity unavailable for %s, reason: ", g.multiRegionBucketForGRPCFailure), + name: "SingleRegion_Failure_DirectPathOnlyStrategy", + bucketName: g.singleRegionBucketForGRPCFailure, + grpcPathStrategy: "direct-path-only", + expectedSuccess: false, + expectedLogSubstrings: []string{ + "DirectPath verification failed", + "Grpc dp is not available and not falling back to Http as gRPC path strategy is set to DirectPathOnly", + }, + }, + { + name: "MultiRegion_Success_FallbackStrategy", + bucketName: g.multiRegionBucketForGRPCSuccess, + grpcPathStrategy: "direct-path-with-fallback", + expectedSuccess: true, + expectedLogSubstrings: []string{"DirectPath verification succeeded, continuing with DirectPath."}, + }, + { + name: "MultiRegion_Failure_FallbackStrategy", + bucketName: g.multiRegionBucketForGRPCFailure, + grpcPathStrategy: "direct-path-with-fallback", + expectedSuccess: true, + expectedLogSubstrings: []string{ + "DirectPath verification failed", + "Grpc dp is not available and falling back to Http.", + }, + }, + { + name: "MultiRegion_Success_DirectPathOnlyStrategy", + bucketName: g.multiRegionBucketForGRPCSuccess, + grpcPathStrategy: "direct-path-only", + expectedSuccess: true, + expectedLogSubstrings: []string{"DirectPath verification succeeded, continuing with DirectPath."}, + }, + { + name: "MultiRegion_Failure_DirectPathOnlyStrategy", + bucketName: g.multiRegionBucketForGRPCFailure, + grpcPathStrategy: "direct-path-only", + expectedSuccess: false, + expectedLogSubstrings: []string{ + "DirectPath verification failed", + "Grpc dp is not available and not falling back to Http as gRPC path strategy is set to DirectPathOnly", + }, }, } for _, tc := range testCases { g.T().Run(tc.name, func(t *testing.T) { + mountPoint, err := os.MkdirTemp("", "grpc_validation_test") assert.NoError(t, err) logFile := fmt.Sprintf("/tmp/grpc_%s_%d.txt", tc.name, time.Now().UnixNano()) - args := []string{"--client-protocol=grpc", "--log-severity=TRACE", fmt.Sprintf("--log-file=%s", logFile), tc.bucketName, mountPoint} - err = mounting.MountGcsfuse(setup.BinFile(), args) - if err != nil { - if tc.expectedSuccess { - t.Errorf("Unexpected mount failure: %v", err) - } - return - } defer func() { - if err := util.Unmount(mountPoint); err != nil { - t.Logf("Warning: unmount failed: %v", err) - } - os.Remove(mountPoint) + _ = util.Unmount(mountPoint) + _ = os.Remove(mountPoint) // Only remove the log file if the test succeeded if t.Failed() { t.Logf("Test failed, log file '%s' will not be deleted for inspection.", logFile) } else { - os.Remove(logFile) + _ = os.Remove(logFile) } }() - success := operations.CheckLogFileForMessage(g.T(), tc.expectedLogSubstring, logFile) - require.Equal(t, true, success) + + args := []string{ + "--client-protocol=grpc", + "--grpc-path-strategy=" + tc.grpcPathStrategy, + "--log-severity=TRACE", + fmt.Sprintf("--log-file=%s", logFile), + tc.bucketName, + mountPoint, + } + err = mounting.MountGcsfuse(setup.BinFile(), args) + if err != nil { + if tc.expectedSuccess { + t.Errorf("Unexpected mount failure: %v", err) + } + } else { + if !tc.expectedSuccess { + t.Errorf("Expected mount failure but mount succeeded") + } + } + + for _, logSubstring := range tc.expectedLogSubstrings { + success := operations.CheckLogFileForMessage(g.T(), logSubstring, logFile) + require.Equal(t, true, success, "Expected message %q not found in log file %s", logSubstring, logFile) + } }) } } diff --git a/tools/integration_tests/grpc_validation/setup_test.go b/tools/integration_tests/grpc_validation/setup_test.go index 5d1c4f4c2ac..eba1e95b7d5 100644 --- a/tools/integration_tests/grpc_validation/setup_test.go +++ b/tools/integration_tests/grpc_validation/setup_test.go @@ -23,6 +23,7 @@ import ( "testing" "time" + "cloud.google.com/go/compute/metadata" "cloud.google.com/go/storage" client_util "github.com/googlecloudplatform/gcsfuse/v3/tools/integration_tests/util/client" "github.com/googlecloudplatform/gcsfuse/v3/tools/integration_tests/util/setup" @@ -148,6 +149,18 @@ func TestMain(m *testing.M) { // Creating a common storage client for the test ctx = context.Background() + + // Skip tests if not running on GCE or if project is not whitelisted. + projectID, err := metadata.ProjectIDWithContext(ctx) + if err != nil { + log.Println("Skipping tests as we are not running on GCE.") + os.Exit(0) + } + if projectID != "gcs-fuse-test" { + log.Printf("Skipping tests as project %q is not whitelisted for gRPC validation tests.", projectID) + os.Exit(0) + } + if client, err = client_util.CreateStorageClient(ctx); err != nil { log.Fatalf("Creation of storage client failed with error : %v", err) } From e1682e538cc82186ae6e557b9a0d023c09d4ca6f Mon Sep 17 00:00:00 2001 From: Prince Kumar Date: Wed, 10 Jun 2026 03:39:56 +0000 Subject: [PATCH 2/3] fixing lint issue --- .../grpc_validation/grpc_validation_test.go | 32 +++++++++---------- 1 file changed, 16 insertions(+), 16 deletions(-) diff --git a/tools/integration_tests/grpc_validation/grpc_validation_test.go b/tools/integration_tests/grpc_validation/grpc_validation_test.go index 81c9f3ab02f..8be1144a032 100644 --- a/tools/integration_tests/grpc_validation/grpc_validation_test.go +++ b/tools/integration_tests/grpc_validation/grpc_validation_test.go @@ -116,10 +116,10 @@ func (g *gRPCValidation) TestGRPCDirectPathConnections() { expectedLogSubstrings: []string{"DirectPath verification succeeded, continuing with DirectPath."}, }, { - name: "SingleRegion_Failure_FallbackStrategy", - bucketName: g.singleRegionBucketForGRPCFailure, - grpcPathStrategy: "direct-path-with-fallback", - expectedSuccess: true, + name: "SingleRegion_Failure_FallbackStrategy", + bucketName: g.singleRegionBucketForGRPCFailure, + grpcPathStrategy: "direct-path-with-fallback", + expectedSuccess: true, expectedLogSubstrings: []string{ "DirectPath verification failed", "Grpc dp is not available and falling back to Http.", @@ -133,10 +133,10 @@ func (g *gRPCValidation) TestGRPCDirectPathConnections() { expectedLogSubstrings: []string{"DirectPath verification succeeded, continuing with DirectPath."}, }, { - name: "SingleRegion_Failure_DirectPathOnlyStrategy", - bucketName: g.singleRegionBucketForGRPCFailure, - grpcPathStrategy: "direct-path-only", - expectedSuccess: false, + name: "SingleRegion_Failure_DirectPathOnlyStrategy", + bucketName: g.singleRegionBucketForGRPCFailure, + grpcPathStrategy: "direct-path-only", + expectedSuccess: false, expectedLogSubstrings: []string{ "DirectPath verification failed", "Grpc dp is not available and not falling back to Http as gRPC path strategy is set to DirectPathOnly", @@ -150,10 +150,10 @@ func (g *gRPCValidation) TestGRPCDirectPathConnections() { expectedLogSubstrings: []string{"DirectPath verification succeeded, continuing with DirectPath."}, }, { - name: "MultiRegion_Failure_FallbackStrategy", - bucketName: g.multiRegionBucketForGRPCFailure, - grpcPathStrategy: "direct-path-with-fallback", - expectedSuccess: true, + name: "MultiRegion_Failure_FallbackStrategy", + bucketName: g.multiRegionBucketForGRPCFailure, + grpcPathStrategy: "direct-path-with-fallback", + expectedSuccess: true, expectedLogSubstrings: []string{ "DirectPath verification failed", "Grpc dp is not available and falling back to Http.", @@ -167,10 +167,10 @@ func (g *gRPCValidation) TestGRPCDirectPathConnections() { expectedLogSubstrings: []string{"DirectPath verification succeeded, continuing with DirectPath."}, }, { - name: "MultiRegion_Failure_DirectPathOnlyStrategy", - bucketName: g.multiRegionBucketForGRPCFailure, - grpcPathStrategy: "direct-path-only", - expectedSuccess: false, + name: "MultiRegion_Failure_DirectPathOnlyStrategy", + bucketName: g.multiRegionBucketForGRPCFailure, + grpcPathStrategy: "direct-path-only", + expectedSuccess: false, expectedLogSubstrings: []string{ "DirectPath verification failed", "Grpc dp is not available and not falling back to Http as gRPC path strategy is set to DirectPathOnly", From 70ded35f0d3b49fb35ebd481f8daba878d62c165 Mon Sep 17 00:00:00 2001 From: Prince Kumar Date: Fri, 12 Jun 2026 11:35:47 +0000 Subject: [PATCH 3/3] Passing flag to disable the direct-path --- cmd/legacy_main.go | 2 +- .../grpc_validation/grpc_validation_test.go | 14 ++++++++++---- 2 files changed, 11 insertions(+), 5 deletions(-) diff --git a/cmd/legacy_main.go b/cmd/legacy_main.go index 46dc69360bb..22bc8267b52 100644 --- a/cmd/legacy_main.go +++ b/cmd/legacy_main.go @@ -331,7 +331,7 @@ func forwardedEnvVars() []string { // should be ignored. // Forward GCE_METADATA_HOST, GCE_METADATA_ROOT, GCE_METADATA_IP as these are used for mocked metadata services. // Forward GRPC_GO_LOG_VERBOSITY_LEVEL and GRPC_GO_LOG_SEVERITY_LEVEL as these are used to enable grpc debug logs. - for _, envvar := range []string{"GOOGLE_APPLICATION_CREDENTIALS", "no_proxy", "GCE_METADATA_HOST", "GCE_METADATA_ROOT", "GCE_METADATA_IP", "GRPC_GO_LOG_VERBOSITY_LEVEL", "GRPC_GO_LOG_SEVERITY_LEVEL"} { + for _, envvar := range []string{"GOOGLE_APPLICATION_CREDENTIALS", "no_proxy", "GCE_METADATA_HOST", "GCE_METADATA_ROOT", "GCE_METADATA_IP", "GRPC_GO_LOG_VERBOSITY_LEVEL", "GRPC_GO_LOG_SEVERITY_LEVEL", "GOOGLE_CLOUD_DISABLE_DIRECT_PATH"} { if envval, ok := os.LookupEnv(envvar); ok { env = append(env, fmt.Sprintf("%s=%s", envvar, envval)) fmt.Fprintf( diff --git a/tools/integration_tests/grpc_validation/grpc_validation_test.go b/tools/integration_tests/grpc_validation/grpc_validation_test.go index 8be1144a032..f962a067c60 100644 --- a/tools/integration_tests/grpc_validation/grpc_validation_test.go +++ b/tools/integration_tests/grpc_validation/grpc_validation_test.go @@ -17,6 +17,7 @@ package grpc_validation import ( "fmt" "os" + "strings" "testing" "time" @@ -117,7 +118,7 @@ func (g *gRPCValidation) TestGRPCDirectPathConnections() { }, { name: "SingleRegion_Failure_FallbackStrategy", - bucketName: g.singleRegionBucketForGRPCFailure, + bucketName: g.singleRegionBucketForGRPCSuccess, grpcPathStrategy: "direct-path-with-fallback", expectedSuccess: true, expectedLogSubstrings: []string{ @@ -134,7 +135,7 @@ func (g *gRPCValidation) TestGRPCDirectPathConnections() { }, { name: "SingleRegion_Failure_DirectPathOnlyStrategy", - bucketName: g.singleRegionBucketForGRPCFailure, + bucketName: g.singleRegionBucketForGRPCSuccess, grpcPathStrategy: "direct-path-only", expectedSuccess: false, expectedLogSubstrings: []string{ @@ -151,7 +152,7 @@ func (g *gRPCValidation) TestGRPCDirectPathConnections() { }, { name: "MultiRegion_Failure_FallbackStrategy", - bucketName: g.multiRegionBucketForGRPCFailure, + bucketName: g.multiRegionBucketForGRPCSuccess, grpcPathStrategy: "direct-path-with-fallback", expectedSuccess: true, expectedLogSubstrings: []string{ @@ -168,7 +169,7 @@ func (g *gRPCValidation) TestGRPCDirectPathConnections() { }, { name: "MultiRegion_Failure_DirectPathOnlyStrategy", - bucketName: g.multiRegionBucketForGRPCFailure, + bucketName: g.multiRegionBucketForGRPCSuccess, grpcPathStrategy: "direct-path-only", expectedSuccess: false, expectedLogSubstrings: []string{ @@ -180,6 +181,11 @@ func (g *gRPCValidation) TestGRPCDirectPathConnections() { for _, tc := range testCases { g.T().Run(tc.name, func(t *testing.T) { + if strings.Contains(tc.name, "Failure") { + t.Setenv("GOOGLE_CLOUD_DISABLE_DIRECT_PATH", "true") + } else { + t.Setenv("GOOGLE_CLOUD_DISABLE_DIRECT_PATH", "false") + } mountPoint, err := os.MkdirTemp("", "grpc_validation_test") assert.NoError(t, err)