Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 10 additions & 7 deletions adapter/client.go
Original file line number Diff line number Diff line change
Expand Up @@ -129,6 +129,9 @@ func newAdapterClient(
ctx context.Context,
opts Options,
) (*AdapterClient, error) {
if opts.ExperimentalHost {
opts.InstanceType = Omni
}
// Create a client.
cl := &AdapterClient{
opts: opts,
Expand Down Expand Up @@ -167,11 +170,11 @@ func generatedGRPCClientOptions() []option.ClientOption {
}
}

// createExperimentalHostCredentials is only supported for connecting to experimental
// hosts. It reads the provided CA certificate file and optionally the
// client certificate and key files to set up TLS or mutual TLS credentials, and
// creates gRPC dial options to connect to an experimental host endpoint.
func createExperimentalHostCredentials(caCertFile, clientCertificateFile, clientCertificateKey string) (option.ClientOption, error) {
// createSpannerOmniCredentials is only supported for connecting to Spanner
// Omni. It reads the provided CA certificate file and optionally the client
// certificate and key files to set up TLS or mutual TLS credentials, and
// creates gRPC dial options to connect to an Omni endpoint.
func createSpannerOmniCredentials(caCertFile, clientCertificateFile, clientCertificateKey string) (option.ClientOption, error) {
if caCertFile == "" {
return nil, nil
}
Expand Down Expand Up @@ -234,9 +237,9 @@ func getAllClientOpts(
internaloption.EnableDirectPathXds(),
)
}
if opts.ExperimentalHost {
if opts.InstanceType == Omni {
clientDefaultOpts = append(clientDefaultOpts, option.WithoutAuthentication())
credOpts, err := createExperimentalHostCredentials(opts.CaCertificate, opts.ClientCertificate, opts.ClientKey)
credOpts, err := createSpannerOmniCredentials(opts.CaCertificate, opts.ClientCertificate, opts.ClientKey)
if err != nil {
return nil, err
}
Expand Down
22 changes: 16 additions & 6 deletions adapter/client_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -101,15 +101,25 @@ func TestGetAllClientOpts(t *testing.T) {
assert.NotEmpty(t, clientOpts)

opts.UsePlainText = false
opts.ExperimentalHost = true
opts.InstanceType = Omni
clientOpts, err = getAllClientOpts(opts)
assert.NoError(t, err)
assert.NotEmpty(t, clientOpts)
}

func TestCreateExperimentalHostNoCredentials(t *testing.T) {
func TestGetAllClientOpts_ExperimentalHostDeprecatedButWorks(t *testing.T) {
t.Parallel()
creds, err := createExperimentalHostCredentials("", "", "")
opts := Options{
ExperimentalHost: true,
}
clientOpts, err := getAllClientOpts(opts)
assert.NoError(t, err)
assert.NotEmpty(t, clientOpts)
}

func TestCreateSpannerOmniNoCredentials(t *testing.T) {
t.Parallel()
creds, err := createSpannerOmniCredentials("", "", "")
assert.NoError(t, err)
assert.Nil(t, creds)
}
Expand All @@ -128,7 +138,7 @@ func createDummyCerts(t *testing.T) (caFile, certFile, keyFile string) {
return
}

func TestCreateExperimentalHostCredentials(t *testing.T) {
func TestCreateSpannerOmniCredentials(t *testing.T) {
caFile, certFile, keyFile := createDummyCerts(t)

tests := []struct {
Expand Down Expand Up @@ -173,9 +183,9 @@ func TestCreateExperimentalHostCredentials(t *testing.T) {

for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
opt, err := createExperimentalHostCredentials(tt.caCert, tt.clientCert, tt.clientKey)
opt, err := createSpannerOmniCredentials(tt.caCert, tt.clientCert, tt.clientKey)
if (err != nil) != tt.wantErr {
t.Errorf("createExperimentalHostCredentials() error = %v, wantErr %v", err, tt.wantErr)
t.Errorf("createSpannerOmniCredentials() error = %v, wantErr %v", err, tt.wantErr)
return
}
if !tt.wantErr && tt.caCert != "" {
Expand Down
11 changes: 11 additions & 0 deletions adapter/options.go
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,11 @@ type Options struct {
// Defaults to false.
UsePlainText bool
// Optional boolean indicate whether endpoint is experimental host instance
// Deprecated: Use InstanceType=OMNI with SpannerEndpoint instead.
ExperimentalHost bool
// Specifies the type of Spanner instance to connect to (CLOUD or OMNI).
// Defaults to CLOUD.
InstanceType InstanceType
// Optional string CA certificate file path for establishing tls connection
CaCertificate string
// Optional string client certificate file path for establishing mTLS connection
Expand All @@ -53,3 +57,10 @@ type Options struct {
// Optional string server key file path for proxy TLS connection
ProxyTLSKeyFile string
}

type InstanceType string

const (
Cloud InstanceType = "CLOUD"
Omni InstanceType = "OMNI"
)
7 changes: 7 additions & 0 deletions adapter/tcp.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ import (
"errors"
"fmt"
"net"
"strings"

"github.com/datastax/go-cassandra-native-protocol/frame"
"github.com/googleapis/go-spanner-cassandra/logger"
Expand All @@ -47,6 +48,12 @@ func NewTCPProxy(opts Options) (*TCPProxy, error) {
if opts.NumGrpcChannels <= 0 {
opts.NumGrpcChannels = 4
}
if opts.ExperimentalHost {
opts.InstanceType = Omni
}
if opts.InstanceType == Omni && !strings.Contains(opts.DatabaseUri, "/") {
opts.DatabaseUri = "projects/default/instances/default/databases/" + opts.DatabaseUri
}

// Create spanner adapter client.
cl, err := newAdapterClient(ctx, opts)
Expand Down
34 changes: 21 additions & 13 deletions cassandra/gocql/integration_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ import (
database "cloud.google.com/go/spanner/admin/database/apiv1"
adminpb "cloud.google.com/go/spanner/admin/database/apiv1/databasepb"
"github.com/gocql/gocql"
"github.com/googleapis/go-spanner-cassandra/adapter"
"github.com/stretchr/testify/require"
"github.com/testcontainers/testcontainers-go"
"github.com/testcontainers/testcontainers-go/wait"
Expand Down Expand Up @@ -148,23 +149,32 @@ func assertDeepEqual(
func setupAndRunSpanner(m *testing.M, spannerEndpoint string) int {
ctx, cancel := context.WithTimeout(context.Background(), 20*time.Minute)
defer cancel()
experimentalHost := os.Getenv("EXPERIMENTAL_HOST")

instanceTypeStr := os.Getenv("SPANNER_INSTANCE_TYPE")

var instanceType adapter.InstanceType
if strings.ToUpper(instanceTypeStr) == "OMNI" {
instanceType = adapter.Omni
} else {
instanceType = adapter.Cloud
}

instanceURI := os.Getenv("INTEGRATION_TEST_INSTANCE")
if instanceURI == "" && experimentalHost == "" {
if instanceURI == "" && instanceType == adapter.Omni {
instanceURI = "projects/default/instances/default"
}

if instanceURI == "" {
log.Fatalf(
"at least one of the environment variables INTEGRATION_TEST_INSTANCE or EXPERIMENTAL_HOST must be set and non-empty for the integration test",
"environment variable INTEGRATION_TEST_INSTANCE must be set and non-empty for the integration test",
)
}
if experimentalHost != "" {
instanceURI = "projects/default/instances/default"
spannerEndpoint = experimentalHost
}
databaseUri = fmt.Sprintf("%s/databases/%s", instanceURI, keyspace)
var err error
clientOpts := []option.ClientOption{
option.WithEndpoint(spannerEndpoint),
}
if experimentalHost != "" {
if instanceType == adapter.Omni {
clientOpts = append(clientOpts, option.WithoutAuthentication())
clientOpts = append(clientOpts, option.WithGRPCDialOption(grpc.WithTransportCredentials(insecure.NewCredentials())))
}
Expand Down Expand Up @@ -205,14 +215,12 @@ func setupAndRunSpanner(m *testing.M, spannerEndpoint string) int {
SpannerEndpoint: spannerEndpoint,
LogLevel: "warn",
MaxCommitDelay: randomMaxCommitDelay,
}

if experimentalHost != "" {
opts.ExperimentalHost = true
opts.UsePlainText = true
InstanceType: instanceType,
UsePlainText: instanceType == adapter.Omni,
}

cluster = NewCluster(opts)

if cluster == nil {
log.Fatalf("Failed to create cluster")
}
Expand Down
18 changes: 17 additions & 1 deletion cassandra/gocql/spanner.go
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,11 @@ type Options struct {
// Defaults to false.
UsePlainText bool
// Optional boolean indicate whether spanner endpoint is a Experimental Host instance
// Deprecated: Use InstanceType=OMNI with SpannerEndpoint instead.
ExperimentalHost bool
// Specifies the type of Spanner instance to connect to (CLOUD or OMNI).
// Defaults to CLOUD.
InstanceType adapter.InstanceType
// Optional string CA certificate file path for establishing tls connection
CaCertificate string
// Optional string client certificate file path for establishing mTLS connection
Expand All @@ -70,6 +74,13 @@ type Options struct {
ProxyTLSKeyFile string
}

type InstanceType = adapter.InstanceType

const (
Cloud InstanceType = adapter.Cloud
Omni InstanceType = adapter.Omni
)

type ProxyAddressTranslator struct {
proxyIP net.IP
proxyPort int
Expand All @@ -91,7 +102,10 @@ func NewCluster(
err,
)
}
if opts.ExperimentalHost && !strings.Contains(opts.DatabaseUri, "/") {
if opts.ExperimentalHost {
opts.InstanceType = adapter.Omni
}
if opts.InstanceType == adapter.Omni && !strings.Contains(opts.DatabaseUri, "/") {
opts.DatabaseUri = "projects/default/instances/default/databases/" + opts.DatabaseUri
}
// Create a new local Cassandra proxy.
Expand All @@ -107,13 +121,15 @@ func NewCluster(
GoogleApiOpts: opts.GoogleApiOpts,
UsePlainText: opts.UsePlainText,
ExperimentalHost: opts.ExperimentalHost,
InstanceType: opts.InstanceType,
CaCertificate: opts.CaCertificate,
ClientCertificate: opts.ClientCertificate,
ClientKey: opts.ClientKey,
ProxyTLSCertFile: opts.ProxyTLSCertFile,
ProxyTLSKeyFile: opts.ProxyTLSKeyFile,
},
)

if err != nil {
panic(
err,
Expand Down
48 changes: 47 additions & 1 deletion cassandra/gocql/spanner_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -203,7 +203,7 @@ func TestBasicBatch(t *testing.T) {
}
}

func TestNewCluster_ExperimentalHost(t *testing.T) {
func TestNewCluster_ExperimentalHostDeprecatedButWorks(t *testing.T) {
t.Cleanup(adapter.ResetGrpcFuncs())
adapter.MockCreateSessionGrpc()
adapter.MockAdaptMessageGrpc(false)
Expand Down Expand Up @@ -249,6 +249,52 @@ func TestNewCluster_ExperimentalHost(t *testing.T) {
}
}

func TestNewCluster_InstanceType(t *testing.T) {
t.Cleanup(adapter.ResetGrpcFuncs())
adapter.MockCreateSessionGrpc()
adapter.MockAdaptMessageGrpc(false)

testCases := []struct {
name string
initialDatabase string
expectedDatabase string
instanceType adapter.InstanceType
}{
{
name: "InstanceType OMNI with simple db name",
initialDatabase: "test-db",
expectedDatabase: "projects/default/instances/default/databases/test-db",
instanceType: adapter.Omni,
},
{
name: "InstanceType OMNI with full db name",
initialDatabase: "projects/p/instances/i/databases/d",
expectedDatabase: "projects/p/instances/i/databases/d",
instanceType: adapter.Omni,
},
{
name: "InstanceType CLOUD",
initialDatabase: "test-db",
expectedDatabase: "test-db",
instanceType: adapter.Cloud,
},
}

for _, tc := range testCases {
t.Run(tc.name, func(t *testing.T) {
opts := &Options{
DatabaseUri: tc.initialDatabase,
GoogleApiOpts: adapter.SkipAuthOpts,
InstanceType: tc.instanceType,
}
cluster := NewCluster(opts)
require.NotNil(t, cluster)
assert.Equal(t, tc.expectedDatabase, opts.DatabaseUri)
teardownCluster(t, cluster)
})
}
}

func TestNewCluster_TLSValidation(t *testing.T) {
t.Cleanup(adapter.ResetGrpcFuncs())
adapter.MockCreateSessionGrpc()
Expand Down
14 changes: 13 additions & 1 deletion cassandra_launcher.go
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ import (
"fmt"
"os"
"os/signal"
"strings"
"syscall"

spanner "github.com/googleapis/go-spanner-cassandra/cassandra/gocql"
Expand Down Expand Up @@ -81,7 +82,13 @@ func main() {
experimentalHost := flag.Bool(
"experimentalHost",
false,
"Spanner Endpoint is an experimental host instance (optional). Default to false.",
"Spanner Endpoint is an experimental host instance (optional). Default to false. Deprecated: Use --instanceType=OMNI with --endpoint instead.",
)

instanceType := flag.String(
"instanceType",
"CLOUD",
"Specifies the type of Spanner instance to connect to (CLOUD or OMNI). Defaults to CLOUD.",
)

caCertificate := flag.String(
Expand Down Expand Up @@ -122,6 +129,10 @@ func main() {
os.Exit(1)
}

if *experimentalHost {
logger.Warn("Flag --experimentalHost is deprecated, use --instanceType=OMNI with --endpoint instead.")
}

opts := &spanner.Options{
DatabaseUri: *databaseURI,
TCPEndpoint: *tcpEndpoint,
Expand All @@ -131,6 +142,7 @@ func main() {
SpannerEndpoint: *spannerEndpoint,
UsePlainText: *usePlainText,
ExperimentalHost: *experimentalHost,
InstanceType: spanner.InstanceType(strings.ToUpper(*instanceType)),
CaCertificate: *caCertificate,
ClientCertificate: *clientCertificate,
ClientKey: *clientKey,
Expand Down
4 changes: 4 additions & 0 deletions logger/logger.go
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,10 @@ func Error(message string, fields ...zap.Field) {
zapLog.Error(message, fields...)
}

func Warn(message string, fields ...zap.Field) {
zapLog.Warn(message, fields...)
}

func Fatal(message string, fields ...zap.Field) {
zapLog.Fatal(message, fields...)
}
Expand Down
Loading