mirror of
https://github.com/gravitational/teleport.git
synced 2026-10-25 10:40:02 +08:00
* feat: added db vnet access to be embedded in tbot * chore: updated based on pr feedback * chore: remove unneded check to validate the nubmer of db servers * chore: moved get resources call to db/vnet package
203 lines
5.6 KiB
Go
203 lines
5.6 KiB
Go
// Teleport
|
|
// Copyright (C) 2026 Gravitational, Inc.
|
|
//
|
|
// This program is free software: you can redistribute it and/or modify
|
|
// it under the terms of the GNU Affero General Public License as published by
|
|
// the Free Software Foundation, either version 3 of the License, or
|
|
// (at your option) any later version.
|
|
//
|
|
// This program is distributed in the hope that it will be useful,
|
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
// GNU Affero General Public License for more details.
|
|
//
|
|
// You should have received a copy of the GNU Affero General Public License
|
|
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
|
|
package db_test
|
|
|
|
import (
|
|
"context"
|
|
"log/slog"
|
|
"testing"
|
|
|
|
"github.com/stretchr/testify/require"
|
|
|
|
"github.com/gravitational/teleport/api/types"
|
|
"github.com/gravitational/teleport/lib/defaults"
|
|
dbvnet "github.com/gravitational/teleport/lib/srv/db/vnet"
|
|
"github.com/gravitational/teleport/lib/vnet/db"
|
|
)
|
|
|
|
func TestParse(t *testing.T) {
|
|
myPostgresHash := dbvnet.DNSName("my-postgres")
|
|
myDBHash := dbvnet.DNSName("my-db")
|
|
|
|
tests := []struct {
|
|
name string
|
|
fqdn string
|
|
zone string
|
|
wantIdentifier string
|
|
wantOK bool
|
|
}{
|
|
{
|
|
name: "vnet_dns_name (hash) prefix",
|
|
fqdn: myPostgresHash + ".db.proxy.example.com.",
|
|
zone: "proxy.example.com",
|
|
wantIdentifier: myPostgresHash,
|
|
wantOK: true,
|
|
},
|
|
{
|
|
name: "different db hash",
|
|
fqdn: myDBHash + ".db.proxy.example.com.",
|
|
zone: "proxy.example.com",
|
|
wantIdentifier: myDBHash,
|
|
wantOK: true,
|
|
},
|
|
{
|
|
name: "literal database name is also accepted",
|
|
fqdn: "my-postgres.db.proxy.example.com.",
|
|
zone: "proxy.example.com",
|
|
wantIdentifier: "my-postgres",
|
|
wantOK: true,
|
|
},
|
|
{
|
|
name: "zone already fully qualified",
|
|
fqdn: myPostgresHash + ".db.proxy.example.com.",
|
|
zone: "proxy.example.com.",
|
|
wantIdentifier: myPostgresHash,
|
|
wantOK: true,
|
|
},
|
|
{
|
|
name: "no db infix",
|
|
fqdn: "app.proxy.example.com.",
|
|
zone: "proxy.example.com",
|
|
wantOK: false,
|
|
},
|
|
{
|
|
name: "wrong zone",
|
|
fqdn: myPostgresHash + ".db.other.example.com.",
|
|
zone: "proxy.example.com",
|
|
wantOK: false,
|
|
},
|
|
{
|
|
name: "empty prefix",
|
|
fqdn: ".db.proxy.example.com.",
|
|
zone: "proxy.example.com",
|
|
wantOK: false,
|
|
},
|
|
{
|
|
name: "only db infix and zone",
|
|
fqdn: "db.proxy.example.com.",
|
|
zone: "proxy.example.com",
|
|
wantOK: false,
|
|
},
|
|
{
|
|
name: "extra dotted prefix is rejected (no embedded user prefix)",
|
|
fqdn: "reader." + myPostgresHash + ".db.proxy.example.com.",
|
|
zone: "proxy.example.com",
|
|
wantOK: false,
|
|
},
|
|
}
|
|
|
|
for _, tt := range tests {
|
|
t.Run(tt.name, func(t *testing.T) {
|
|
got, ok := db.Parse(tt.fqdn, tt.zone)
|
|
require.Equal(t, tt.wantOK, ok)
|
|
if !tt.wantOK {
|
|
return
|
|
}
|
|
require.Equal(t, tt.wantIdentifier, got)
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestPickMatch(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
silent := slog.New(slog.DiscardHandler)
|
|
ctx := context.Background()
|
|
|
|
t.Run("empty servers returns no match", func(t *testing.T) {
|
|
got, ok := db.PickMatch(ctx, silent, "foo", nil)
|
|
require.False(t, ok)
|
|
require.Nil(t, got)
|
|
})
|
|
|
|
t.Run("single server with supported protocol", func(t *testing.T) {
|
|
got, ok := db.PickMatch(ctx, silent, "my-pg",
|
|
[]types.DatabaseServer{newServer(t, "my-pg", "agent-1", defaults.ProtocolPostgres)})
|
|
require.True(t, ok)
|
|
require.Equal(t, "my-pg", got.GetName())
|
|
})
|
|
|
|
t.Run("unsupported protocol returns no match", func(t *testing.T) {
|
|
got, ok := db.PickMatch(ctx, silent, "my-mongo",
|
|
[]types.DatabaseServer{newServer(t, "my-mongo", "agent-1", defaults.ProtocolMongoDB)})
|
|
require.False(t, ok)
|
|
require.Nil(t, got)
|
|
})
|
|
|
|
t.Run("HA db_servers for same database dedupe to one", func(t *testing.T) {
|
|
got, ok := db.PickMatch(ctx, silent, "my-pg", []types.DatabaseServer{
|
|
newServer(t, "my-pg", "agent-1", defaults.ProtocolPostgres),
|
|
newServer(t, "my-pg", "agent-2", defaults.ProtocolPostgres),
|
|
})
|
|
require.True(t, ok)
|
|
require.Equal(t, "my-pg", got.GetName())
|
|
})
|
|
|
|
t.Run("distinct databases with same identifier picks the first", func(t *testing.T) {
|
|
got, ok := db.PickMatch(ctx, silent, "shared-id", []types.DatabaseServer{
|
|
newServer(t, "db-a", "agent-1", defaults.ProtocolPostgres),
|
|
newServer(t, "db-b", "agent-1", defaults.ProtocolPostgres),
|
|
})
|
|
require.True(t, ok)
|
|
require.Equal(t, "db-a", got.GetName())
|
|
})
|
|
}
|
|
|
|
// newServer builds a minimal DatabaseServer suitable for PickMatch tests.
|
|
func newServer(t *testing.T, dbName, agentName, protocol string) types.DatabaseServer {
|
|
t.Helper()
|
|
srv, err := types.NewDatabaseServerV3(types.Metadata{
|
|
Name: dbName,
|
|
}, types.DatabaseServerSpecV3{
|
|
HostID: agentName,
|
|
Hostname: agentName,
|
|
Database: &types.DatabaseV3{
|
|
Metadata: types.Metadata{Name: dbName},
|
|
Spec: types.DatabaseSpecV3{
|
|
Protocol: protocol,
|
|
URI: "localhost:5432",
|
|
},
|
|
},
|
|
})
|
|
require.NoError(t, err)
|
|
return srv
|
|
}
|
|
|
|
func TestIsUserOptional(t *testing.T) {
|
|
supported := []string{
|
|
defaults.ProtocolPostgres,
|
|
defaults.ProtocolCockroachDB,
|
|
defaults.ProtocolMySQL,
|
|
defaults.ProtocolSQLServer,
|
|
}
|
|
for _, p := range supported {
|
|
t.Run(p, func(t *testing.T) {
|
|
require.True(t, db.IsUserOptional(p))
|
|
})
|
|
}
|
|
|
|
unsupported := []string{
|
|
defaults.ProtocolMongoDB,
|
|
defaults.ProtocolClickHouse,
|
|
}
|
|
for _, p := range unsupported {
|
|
t.Run(p+"_user_required", func(t *testing.T) {
|
|
require.False(t, db.IsUserOptional(p))
|
|
})
|
|
}
|
|
}
|