// Copyright (C) MongoDB, Inc. 2017-present. // // Licensed under the Apache License, Version 2.0 (the "License"); you may // not use this file except in compliance with the License. You may obtain // a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 //go:build atlastest // +build atlastest package main import ( "context" "crypto/tls" "encoding/base64" "errors" "fmt" "net/url" "os" "path/filepath" "testing" "time" "go.mongodb.org/mongo-driver/v2/bson" "go.mongodb.org/mongo-driver/v2/internal/assert" "go.mongodb.org/mongo-driver/v2/internal/handshake" "go.mongodb.org/mongo-driver/v2/internal/require" "go.mongodb.org/mongo-driver/v2/mongo" "go.mongodb.org/mongo-driver/v2/mongo/options" ) func TestAtlas(t *testing.T) { cases := []struct { name string envVar string certKeyFile string wantErr string }{ { name: "Atlas with TLS", envVar: "ATLAS_REPL", certKeyFile: "", wantErr: "", }, { name: "Atlas with TLS and shared cluster", envVar: "ATLAS_SHRD", certKeyFile: "", wantErr: "", }, { name: "Atlas with free tier", envVar: "ATLAS_FREE", certKeyFile: "", wantErr: "", }, { name: "Atlas with TLS 1.1", envVar: "ATLAS_TLS11", certKeyFile: "", wantErr: "", }, { name: "Atlas with TLS 1.2", envVar: "ATLAS_TLS12", certKeyFile: "", wantErr: "", }, { name: "Atlas with srv file on replica set", envVar: "ATLAS_SRV_REPL", certKeyFile: "", wantErr: "", }, { name: "Atlas with srv file on shared cluster", envVar: "ATLAS_SRV_SHRD", certKeyFile: "", wantErr: "", }, { name: "Atlas with srv file on free tier", envVar: "ATLAS_SRV_FREE", certKeyFile: "", wantErr: "", }, { name: "Atlas with srv file on TLS 1.1", envVar: "ATLAS_SRV_TLS11", certKeyFile: "", wantErr: "", }, { name: "Atlas with srv file on TLS 1.2", envVar: "ATLAS_SRV_TLS12", certKeyFile: "", wantErr: "", }, { name: "Atlas with X509 Dev", envVar: "ATLAS_X509_DEV", certKeyFile: createAtlasX509DevCertKeyFile(t), wantErr: "", }, { name: "Atlas with X509 Dev no user", envVar: "ATLAS_X509_DEV", certKeyFile: createAtlasX509DevCertKeyFileNoUser(t), wantErr: "UserNotFound", }, } for _, tc := range cases { t.Run(fmt.Sprintf("%s (%s)", tc.name, tc.envVar), func(t *testing.T) { uri := os.Getenv(tc.envVar) require.NotEmpty(t, uri, "Environment variable %s is not set", tc.envVar) if tc.certKeyFile != "" { uri = addTLSCertKeyFile(t, tc.certKeyFile, uri) } // Set a low server selection timeout so we fail fast if there are errors. clientOpts := options.Client(). ApplyURI(uri). SetServerSelectionTimeout(1 * time.Second) // Run basic connectivity test. err := runTest(context.Background(), clientOpts) if tc.wantErr != "" { assert.ErrorContains(t, err, tc.wantErr, "expected error to contain %q", tc.wantErr) return } require.NoError(t, err, "error running test with TLS") orig := clientOpts.TLSConfig if orig == nil { orig = &tls.Config{} } insecure := orig.Clone() insecure.InsecureSkipVerify = true // Run the connectivity test with InsecureSkipVerify to ensure SNI is done // correctly even if verification is disabled. insecureClientOpts := options.Client(). ApplyURI(uri). SetServerSelectionTimeout(1 * time.Second). SetTLSConfig(insecure) err = runTest(context.Background(), insecureClientOpts) require.NoError(t, err, "error running test with tlsInsecure") }) } } func runTest(ctx context.Context, clientOpts *options.ClientOptions) error { client, err := mongo.Connect(clientOpts) if err != nil { return fmt.Errorf("Connect error: %w", err) } defer func() { _ = client.Disconnect(ctx) }() db := client.Database("test") cmd := bson.D{{handshake.LegacyHello, 1}} err = db.RunCommand(ctx, cmd).Err() if err != nil { return fmt.Errorf("legacy hello error: %w", err) } coll := db.Collection("test") if err = coll.FindOne(ctx, bson.D{{"x", 1}}).Err(); err != nil && !errors.Is(err, mongo.ErrNoDocuments) { return fmt.Errorf("FindOne error: %w", err) } return nil } func createAtlasX509DevCertKeyFile(t *testing.T) string { t.Helper() b64 := os.Getenv("ATLAS_X509_DEV_CERT_BASE64") assert.NotEmpty(t, b64, "Environment variable ATLAS_X509_DEV_CERT_BASE64 is not set") certBytes, err := base64.StdEncoding.DecodeString(b64) require.NoError(t, err, "failed to decode ATLAS_X509_DEV_CERT_BASE64") certFilePath := filepath.Join(t.TempDir(), "atlas_x509_dev_cert.pem") err = os.WriteFile(certFilePath, certBytes, 0o600) require.NoError(t, err, "failed to write ATLAS_X509_DEV_CERT_BASE64 to file") return certFilePath } func createAtlasX509DevCertKeyFileNoUser(t *testing.T) string { t.Helper() b64 := os.Getenv("ATLAS_X509_DEV_CERT_NOUSER_BASE64") assert.NotEmpty(t, b64, "Environment variable ATLAS_X509_DEV_CERT_NOUSER_BASE64 is not set") keyBytes, err := base64.StdEncoding.DecodeString(b64) require.NoError(t, err, "failed to decode ATLAS_X509_DEV_CERT_NOUSER_BASE64") keyFilePath := filepath.Join(t.TempDir(), "atlas_x509_dev_cert_no_user.pem") err = os.WriteFile(keyFilePath, keyBytes, 0o600) require.NoError(t, err, "failed to write ATLAS_X509_DEV_CERT_NOUSER_BASE64 to file") return keyFilePath } func addTLSCertKeyFile(t *testing.T, certKeyFile, uri string) string { t.Helper() u, err := url.Parse(uri) require.NoError(t, err, "failed to parse uri") q := u.Query() q.Set("tlsCertificateKeyFile", filepath.ToSlash(certKeyFile)) u.RawQuery = q.Encode() return u.String() }