mirror of
https://github.com/gravitational/teleport.git
synced 2026-10-31 20:39:37 +08:00
* Move GCP cloud clients into a separate interface * Add azure mysql/postgresql flex server support * Add teleport db configure create support for azure postgres/mysql flex servers * discover both single and flexi server with 'mysql' and 'postgres' db types. * go doc gcp interface * Add is_flexi_server to database Azure config
2241 lines
68 KiB
Go
2241 lines
68 KiB
Go
/*
|
|
Copyright 2021 Gravitational, Inc.
|
|
|
|
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
|
|
|
|
Unless required by applicable law or agreed to in writing, software
|
|
distributed under the License is distributed on an "AS IS" BASIS,
|
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
See the License for the specific language governing permissions and
|
|
limitations under the License.
|
|
*/
|
|
|
|
package services
|
|
|
|
import (
|
|
"bufio"
|
|
"fmt"
|
|
"strings"
|
|
"testing"
|
|
|
|
"github.com/Azure/azure-sdk-for-go/sdk/azcore/to"
|
|
"github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/mysql/armmysql"
|
|
"github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/mysql/armmysqlflexibleservers"
|
|
"github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/postgresql/armpostgresqlflexibleservers"
|
|
"github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/redis/armredis/v2"
|
|
"github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/redisenterprise/armredisenterprise"
|
|
"github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/sql/armsql"
|
|
"github.com/aws/aws-sdk-go/aws"
|
|
"github.com/aws/aws-sdk-go/service/elasticache"
|
|
"github.com/aws/aws-sdk-go/service/memorydb"
|
|
"github.com/aws/aws-sdk-go/service/rds"
|
|
"github.com/aws/aws-sdk-go/service/redshift"
|
|
"github.com/aws/aws-sdk-go/service/redshiftserverless"
|
|
"github.com/google/uuid"
|
|
"github.com/gravitational/trace"
|
|
"github.com/stretchr/testify/require"
|
|
|
|
"github.com/gravitational/teleport/api/types"
|
|
awsutils "github.com/gravitational/teleport/api/utils/aws"
|
|
azureutils "github.com/gravitational/teleport/api/utils/azure"
|
|
libcloudaws "github.com/gravitational/teleport/lib/cloud/aws"
|
|
"github.com/gravitational/teleport/lib/cloud/azure"
|
|
"github.com/gravitational/teleport/lib/cloud/mocks"
|
|
"github.com/gravitational/teleport/lib/defaults"
|
|
"github.com/gravitational/teleport/lib/fixtures"
|
|
"github.com/gravitational/teleport/lib/utils"
|
|
)
|
|
|
|
// TestDatabaseUnmarshal verifies a database resource can be unmarshaled.
|
|
func TestDatabaseUnmarshal(t *testing.T) {
|
|
expected, err := types.NewDatabaseV3(types.Metadata{
|
|
Name: "test-database",
|
|
Description: "Test description",
|
|
Labels: map[string]string{"env": "dev"},
|
|
}, types.DatabaseSpecV3{
|
|
Protocol: defaults.ProtocolPostgres,
|
|
URI: "localhost:5432",
|
|
CACert: fixtures.TLSCACertPEM,
|
|
})
|
|
require.NoError(t, err)
|
|
data, err := utils.ToJSON([]byte(fmt.Sprintf(databaseYAML, indent(fixtures.TLSCACertPEM, 4))))
|
|
require.NoError(t, err)
|
|
actual, err := UnmarshalDatabase(data)
|
|
require.NoError(t, err)
|
|
require.Equal(t, expected, actual)
|
|
}
|
|
|
|
// TestDatabaseMarshal verifies a marshaled database resource can be unmarshaled back.
|
|
func TestDatabaseMarshal(t *testing.T) {
|
|
expected, err := types.NewDatabaseV3(types.Metadata{
|
|
Name: "test-database",
|
|
Description: "Test description",
|
|
Labels: map[string]string{"env": "dev"},
|
|
}, types.DatabaseSpecV3{
|
|
Protocol: defaults.ProtocolPostgres,
|
|
URI: "localhost:5432",
|
|
CACert: fixtures.TLSCACertPEM,
|
|
})
|
|
require.NoError(t, err)
|
|
data, err := MarshalDatabase(expected)
|
|
require.NoError(t, err)
|
|
actual, err := UnmarshalDatabase(data)
|
|
require.NoError(t, err)
|
|
require.Equal(t, expected, actual)
|
|
}
|
|
|
|
func TestValidateDatabase(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
tests := []struct {
|
|
inputName string
|
|
inputSpec types.DatabaseSpecV3
|
|
expectError bool
|
|
}{
|
|
{
|
|
// Captured error:
|
|
// a DNS-1035 label must consist of lower case alphanumeric
|
|
// characters or '-', start with an alphabetic character, and end
|
|
// with an alphanumeric character (e.g. 'my-name', or 'abc-123',
|
|
// regex used for validation is '[a-z]([-a-z0-9]*[a-z0-9])?')
|
|
inputName: "invalid-database-name-",
|
|
inputSpec: types.DatabaseSpecV3{
|
|
Protocol: defaults.ProtocolPostgres,
|
|
URI: "localhost:5432",
|
|
},
|
|
expectError: true,
|
|
},
|
|
{
|
|
inputName: "invalid-database-protocol",
|
|
inputSpec: types.DatabaseSpecV3{
|
|
Protocol: "unknown",
|
|
URI: "localhost:5432",
|
|
},
|
|
expectError: true,
|
|
},
|
|
{
|
|
inputName: "invalid-database-uri",
|
|
inputSpec: types.DatabaseSpecV3{
|
|
Protocol: defaults.ProtocolPostgres,
|
|
URI: "missing-port",
|
|
},
|
|
expectError: true,
|
|
},
|
|
{
|
|
inputName: "invalid-database-CA-cert",
|
|
inputSpec: types.DatabaseSpecV3{
|
|
Protocol: defaults.ProtocolPostgres,
|
|
URI: "localhost:5432",
|
|
TLS: types.DatabaseTLS{
|
|
CACert: "bad-cert",
|
|
},
|
|
},
|
|
expectError: true,
|
|
},
|
|
{
|
|
inputName: "valid-mongodb",
|
|
inputSpec: types.DatabaseSpecV3{
|
|
Protocol: defaults.ProtocolMongoDB,
|
|
URI: "mongodb://mongo-1:27017,mongo-2:27018/?replicaSet=rs0",
|
|
},
|
|
expectError: false,
|
|
},
|
|
{
|
|
inputName: "valid-mongodb-srv",
|
|
inputSpec: types.DatabaseSpecV3{
|
|
Protocol: defaults.ProtocolMongoDB,
|
|
URI: "mongodb+srv://valid.but.cannot.be.resolved.com",
|
|
},
|
|
expectError: false,
|
|
},
|
|
{
|
|
inputName: "invalid-mongodb-srv",
|
|
inputSpec: types.DatabaseSpecV3{
|
|
Protocol: defaults.ProtocolMongoDB,
|
|
URI: "mongodb+srv://valid.but.cannot.be.resolved.com/?readpreference=unknown",
|
|
},
|
|
expectError: true,
|
|
},
|
|
{
|
|
inputName: "invalid-mongodb-missing-username",
|
|
inputSpec: types.DatabaseSpecV3{
|
|
Protocol: defaults.ProtocolMongoDB,
|
|
URI: "mongodb://mongo-1:27017/?authmechanism=plain",
|
|
},
|
|
expectError: true,
|
|
},
|
|
{
|
|
inputName: "valid-redis",
|
|
inputSpec: types.DatabaseSpecV3{
|
|
Protocol: defaults.ProtocolRedis,
|
|
URI: "rediss://redis.example.com:6379",
|
|
},
|
|
expectError: false,
|
|
},
|
|
{
|
|
inputName: "invalid-redis-incorrect-mode",
|
|
inputSpec: types.DatabaseSpecV3{
|
|
Protocol: defaults.ProtocolRedis,
|
|
URI: "rediss://redis.example.com:6379?mode=unknown",
|
|
},
|
|
expectError: true,
|
|
},
|
|
{
|
|
inputName: "valid-snowflake",
|
|
inputSpec: types.DatabaseSpecV3{
|
|
Protocol: defaults.ProtocolSnowflake,
|
|
URI: "test.snowflakecomputing.com",
|
|
},
|
|
expectError: false,
|
|
},
|
|
{
|
|
inputName: "invalid-snowflake",
|
|
inputSpec: types.DatabaseSpecV3{
|
|
Protocol: defaults.ProtocolSnowflake,
|
|
URI: "not.snow.flake.com",
|
|
},
|
|
expectError: true,
|
|
},
|
|
{
|
|
inputName: "valid-cassandra-without-uri",
|
|
inputSpec: types.DatabaseSpecV3{
|
|
Protocol: defaults.ProtocolCassandra,
|
|
AWS: types.AWS{
|
|
Region: "us-east-1",
|
|
AccountID: "123456789012",
|
|
},
|
|
},
|
|
expectError: false,
|
|
},
|
|
{
|
|
inputName: "valid-dynamodb-without-uri",
|
|
inputSpec: types.DatabaseSpecV3{
|
|
Protocol: defaults.ProtocolDynamoDB,
|
|
AWS: types.AWS{
|
|
Region: "us-east-1",
|
|
AccountID: "1234567890",
|
|
},
|
|
},
|
|
expectError: false,
|
|
},
|
|
}
|
|
|
|
for _, test := range tests {
|
|
t.Run(test.inputName, func(t *testing.T) {
|
|
database, err := types.NewDatabaseV3(types.Metadata{
|
|
Name: test.inputName,
|
|
}, test.inputSpec)
|
|
require.NoError(t, err)
|
|
|
|
err = ValidateDatabase(database)
|
|
if test.expectError {
|
|
require.Error(t, err)
|
|
} else {
|
|
require.NoError(t, err)
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
// indent returns the string where each line is indented by the specified
|
|
// number of spaces.
|
|
func indent(s string, spaces int) string {
|
|
var lines []string
|
|
scanner := bufio.NewScanner(strings.NewReader(s))
|
|
for scanner.Scan() {
|
|
lines = append(lines, fmt.Sprintf("%v%v", strings.Repeat(" ", spaces), scanner.Text()))
|
|
}
|
|
return strings.Join(lines, "\n")
|
|
}
|
|
|
|
var databaseYAML = `kind: db
|
|
version: v3
|
|
metadata:
|
|
name: test-database
|
|
description: "Test description"
|
|
labels:
|
|
env: dev
|
|
spec:
|
|
protocol: "postgres"
|
|
uri: "localhost:5432"
|
|
ca_cert: |
|
|
%v`
|
|
|
|
// TestDatabaseFromAzureDBServer tests converting an Azure DB Server to a database resource.
|
|
func TestDatabaseFromAzureDBServer(t *testing.T) {
|
|
subscription := "sub1"
|
|
resourceGroup := "defaultRG"
|
|
resourceType := "Microsoft.DBforMySQL/servers"
|
|
name := "testdb"
|
|
id := fmt.Sprintf("/subscriptions/%v/resourceGroups/%v/providers/%v/%v",
|
|
subscription,
|
|
resourceGroup,
|
|
resourceType,
|
|
name,
|
|
)
|
|
|
|
server := azure.DBServer{
|
|
ID: id,
|
|
Location: "eastus",
|
|
Name: name,
|
|
Port: "3306",
|
|
Properties: azure.ServerProperties{
|
|
FullyQualifiedDomainName: name + ".mysql" + azureutils.DatabaseEndpointSuffix,
|
|
UserVisibleState: string(armmysql.ServerStateReady),
|
|
Version: string(armmysql.ServerVersionFive7),
|
|
},
|
|
Protocol: defaults.ProtocolMySQL,
|
|
Tags: map[string]string{
|
|
"foo": "bar",
|
|
},
|
|
}
|
|
|
|
expected, err := types.NewDatabaseV3(types.Metadata{
|
|
Name: "testdb",
|
|
Description: "Azure MySQL server in eastus",
|
|
Labels: map[string]string{
|
|
types.OriginLabel: types.OriginCloud,
|
|
labelRegion: "eastus",
|
|
labelEngine: "Microsoft.DBforMySQL/servers",
|
|
labelEngineVersion: "5.7",
|
|
labelResourceGroup: "defaultRG",
|
|
labelSubscriptionID: "sub1",
|
|
"foo": "bar",
|
|
},
|
|
}, types.DatabaseSpecV3{
|
|
Protocol: defaults.ProtocolMySQL,
|
|
URI: "testdb.mysql.database.azure.com:3306",
|
|
Azure: types.Azure{
|
|
Name: "testdb",
|
|
ResourceID: id,
|
|
},
|
|
})
|
|
require.NoError(t, err)
|
|
|
|
actual, err := NewDatabaseFromAzureServer(&server)
|
|
require.NoError(t, err)
|
|
require.Equal(t, expected, actual)
|
|
}
|
|
|
|
func TestDatabaseFromAzureRedis(t *testing.T) {
|
|
subscription := "test-sub"
|
|
name := "test-azure-redis"
|
|
group := "test-group"
|
|
region := "eastus"
|
|
id := fmt.Sprintf("/subscriptions/%v/resourceGroups/%v/providers/Microsoft.Cache/Redis/%v", subscription, group, name)
|
|
resourceInfo := &armredis.ResourceInfo{
|
|
Name: to.Ptr(name),
|
|
ID: to.Ptr(id),
|
|
Location: to.Ptr(region),
|
|
Tags: map[string]*string{
|
|
"foo": to.Ptr("bar"),
|
|
},
|
|
Properties: &armredis.Properties{
|
|
HostName: to.Ptr(fmt.Sprintf("%v.redis.cache.windows.net", name)),
|
|
SSLPort: to.Ptr(int32(6380)),
|
|
ProvisioningState: to.Ptr(armredis.ProvisioningStateSucceeded),
|
|
RedisVersion: to.Ptr("6.0"),
|
|
},
|
|
}
|
|
|
|
expected, err := types.NewDatabaseV3(types.Metadata{
|
|
Name: name,
|
|
Description: "Azure Redis server in eastus",
|
|
Labels: map[string]string{
|
|
types.OriginLabel: types.OriginCloud,
|
|
labelRegion: region,
|
|
labelEngine: "Microsoft.Cache/Redis",
|
|
labelEngineVersion: "6.0",
|
|
labelResourceGroup: group,
|
|
labelSubscriptionID: subscription,
|
|
"foo": "bar",
|
|
},
|
|
}, types.DatabaseSpecV3{
|
|
Protocol: defaults.ProtocolRedis,
|
|
URI: "test-azure-redis.redis.cache.windows.net:6380",
|
|
Azure: types.Azure{
|
|
Name: "test-azure-redis",
|
|
ResourceID: id,
|
|
},
|
|
})
|
|
require.NoError(t, err)
|
|
|
|
actual, err := NewDatabaseFromAzureRedis(resourceInfo)
|
|
require.NoError(t, err)
|
|
require.Equal(t, expected, actual)
|
|
}
|
|
|
|
func TestDatabaseFromAzureRedisEnterprise(t *testing.T) {
|
|
subscription := "test-sub"
|
|
name := "test-azure-redis"
|
|
group := "test-group"
|
|
region := "eastus"
|
|
clusterID := fmt.Sprintf("/subscriptions/%v/resourceGroups/%v/providers/Microsoft.Cache/redisEnterprise/%v", subscription, group, name)
|
|
databaseID := fmt.Sprintf("%v/databases/default", clusterID)
|
|
|
|
armCluster := &armredisenterprise.Cluster{
|
|
Name: to.Ptr(name),
|
|
ID: to.Ptr(clusterID),
|
|
Location: to.Ptr(region),
|
|
Tags: map[string]*string{
|
|
"foo": to.Ptr("bar"),
|
|
},
|
|
Properties: &armredisenterprise.ClusterProperties{
|
|
HostName: to.Ptr(fmt.Sprintf("%v.%v.redisenterprise.cache.azure.net", name, region)),
|
|
RedisVersion: to.Ptr("6.0"),
|
|
},
|
|
}
|
|
armDatabase := &armredisenterprise.Database{
|
|
Name: to.Ptr("default"),
|
|
ID: to.Ptr(databaseID),
|
|
Properties: &armredisenterprise.DatabaseProperties{
|
|
ProvisioningState: to.Ptr(armredisenterprise.ProvisioningStateSucceeded),
|
|
Port: to.Ptr(int32(10000)),
|
|
ClusteringPolicy: to.Ptr(armredisenterprise.ClusteringPolicyOSSCluster),
|
|
ClientProtocol: to.Ptr(armredisenterprise.ProtocolEncrypted),
|
|
},
|
|
}
|
|
|
|
expected, err := types.NewDatabaseV3(types.Metadata{
|
|
Name: name,
|
|
Description: "Azure Redis Enterprise server in eastus",
|
|
Labels: map[string]string{
|
|
types.OriginLabel: types.OriginCloud,
|
|
labelRegion: region,
|
|
labelEngine: "Microsoft.Cache/redisEnterprise",
|
|
labelEngineVersion: "6.0",
|
|
labelResourceGroup: group,
|
|
labelSubscriptionID: subscription,
|
|
labelEndpointType: "OSSCluster",
|
|
"foo": "bar",
|
|
},
|
|
}, types.DatabaseSpecV3{
|
|
Protocol: defaults.ProtocolRedis,
|
|
URI: "test-azure-redis.eastus.redisenterprise.cache.azure.net:10000",
|
|
Azure: types.Azure{
|
|
Name: "test-azure-redis",
|
|
ResourceID: databaseID,
|
|
Redis: types.AzureRedis{
|
|
ClusteringPolicy: "OSSCluster",
|
|
},
|
|
},
|
|
})
|
|
require.NoError(t, err)
|
|
|
|
actual, err := NewDatabaseFromAzureRedisEnterprise(armCluster, armDatabase)
|
|
require.NoError(t, err)
|
|
require.Equal(t, expected, actual)
|
|
}
|
|
|
|
// TestDatabaseFromRDSInstance tests converting an RDS instance to a database resource.
|
|
func TestDatabaseFromRDSInstance(t *testing.T) {
|
|
instance := &rds.DBInstance{
|
|
DBInstanceArn: aws.String("arn:aws:rds:us-west-1:123456789012:db:instance-1"),
|
|
DBInstanceIdentifier: aws.String("instance-1"),
|
|
DBClusterIdentifier: aws.String("cluster-1"),
|
|
DbiResourceId: aws.String("resource-1"),
|
|
IAMDatabaseAuthenticationEnabled: aws.Bool(true),
|
|
Engine: aws.String(RDSEnginePostgres),
|
|
EngineVersion: aws.String("13.0"),
|
|
Endpoint: &rds.Endpoint{
|
|
Address: aws.String("localhost"),
|
|
Port: aws.Int64(5432),
|
|
},
|
|
TagList: []*rds.Tag{{
|
|
Key: aws.String("key"),
|
|
Value: aws.String("val"),
|
|
}},
|
|
}
|
|
expected, err := types.NewDatabaseV3(types.Metadata{
|
|
Name: "instance-1",
|
|
Description: "RDS instance in us-west-1",
|
|
Labels: map[string]string{
|
|
types.OriginLabel: types.OriginCloud,
|
|
labelAccountID: "123456789012",
|
|
labelRegion: "us-west-1",
|
|
labelEngine: RDSEnginePostgres,
|
|
labelEngineVersion: "13.0",
|
|
labelEndpointType: "instance",
|
|
"key": "val",
|
|
},
|
|
}, types.DatabaseSpecV3{
|
|
Protocol: defaults.ProtocolPostgres,
|
|
URI: "localhost:5432",
|
|
AWS: types.AWS{
|
|
AccountID: "123456789012",
|
|
Region: "us-west-1",
|
|
RDS: types.RDS{
|
|
InstanceID: "instance-1",
|
|
ClusterID: "cluster-1",
|
|
ResourceID: "resource-1",
|
|
IAMAuth: true,
|
|
},
|
|
},
|
|
})
|
|
require.NoError(t, err)
|
|
actual, err := NewDatabaseFromRDSInstance(instance)
|
|
require.NoError(t, err)
|
|
require.Equal(t, expected, actual)
|
|
}
|
|
|
|
// TestDatabaseFromRDSInstance tests converting an RDS instance to a database resource.
|
|
func TestDatabaseFromRDSInstanceNameOverride(t *testing.T) {
|
|
instance := &rds.DBInstance{
|
|
DBInstanceArn: aws.String("arn:aws:rds:us-west-1:123456789012:db:instance-1"),
|
|
DBInstanceIdentifier: aws.String("instance-1"),
|
|
DBClusterIdentifier: aws.String("cluster-1"),
|
|
DbiResourceId: aws.String("resource-1"),
|
|
IAMDatabaseAuthenticationEnabled: aws.Bool(true),
|
|
Engine: aws.String(RDSEnginePostgres),
|
|
EngineVersion: aws.String("13.0"),
|
|
Endpoint: &rds.Endpoint{
|
|
Address: aws.String("localhost"),
|
|
Port: aws.Int64(5432),
|
|
},
|
|
TagList: []*rds.Tag{
|
|
{Key: aws.String("key"), Value: aws.String("val")},
|
|
{Key: aws.String(labelTeleportDBName), Value: aws.String("override-1")},
|
|
},
|
|
}
|
|
expected, err := types.NewDatabaseV3(types.Metadata{
|
|
Name: "override-1",
|
|
Description: "RDS instance in us-west-1",
|
|
Labels: map[string]string{
|
|
types.OriginLabel: types.OriginCloud,
|
|
labelAccountID: "123456789012",
|
|
labelRegion: "us-west-1",
|
|
labelEngine: RDSEnginePostgres,
|
|
labelEngineVersion: "13.0",
|
|
labelEndpointType: "instance",
|
|
labelTeleportDBName: "override-1",
|
|
"key": "val",
|
|
},
|
|
}, types.DatabaseSpecV3{
|
|
Protocol: defaults.ProtocolPostgres,
|
|
URI: "localhost:5432",
|
|
AWS: types.AWS{
|
|
AccountID: "123456789012",
|
|
Region: "us-west-1",
|
|
RDS: types.RDS{
|
|
InstanceID: "instance-1",
|
|
ClusterID: "cluster-1",
|
|
ResourceID: "resource-1",
|
|
IAMAuth: true,
|
|
},
|
|
},
|
|
})
|
|
require.NoError(t, err)
|
|
actual, err := NewDatabaseFromRDSInstance(instance)
|
|
require.NoError(t, err)
|
|
require.Equal(t, expected, actual)
|
|
}
|
|
|
|
// TestDatabaseFromRDSCluster tests converting an RDS cluster to a database resource.
|
|
func TestDatabaseFromRDSCluster(t *testing.T) {
|
|
cluster := &rds.DBCluster{
|
|
DBClusterArn: aws.String("arn:aws:rds:us-east-1:123456789012:cluster:cluster-1"),
|
|
DBClusterIdentifier: aws.String("cluster-1"),
|
|
DbClusterResourceId: aws.String("resource-1"),
|
|
IAMDatabaseAuthenticationEnabled: aws.Bool(true),
|
|
Engine: aws.String(RDSEngineAuroraMySQL),
|
|
EngineVersion: aws.String("8.0.0"),
|
|
Endpoint: aws.String("localhost"),
|
|
ReaderEndpoint: aws.String("reader.host"),
|
|
Port: aws.Int64(3306),
|
|
CustomEndpoints: []*string{
|
|
aws.String("myendpoint1.cluster-custom-example.us-east-1.rds.amazonaws.com"),
|
|
aws.String("myendpoint2.cluster-custom-example.us-east-1.rds.amazonaws.com"),
|
|
},
|
|
TagList: []*rds.Tag{{
|
|
Key: aws.String("key"),
|
|
Value: aws.String("val"),
|
|
}},
|
|
}
|
|
|
|
expectedAWS := types.AWS{
|
|
AccountID: "123456789012",
|
|
Region: "us-east-1",
|
|
RDS: types.RDS{
|
|
ClusterID: "cluster-1",
|
|
ResourceID: "resource-1",
|
|
IAMAuth: true,
|
|
},
|
|
}
|
|
|
|
t.Run("primary", func(t *testing.T) {
|
|
expected, err := types.NewDatabaseV3(types.Metadata{
|
|
Name: "cluster-1",
|
|
Description: "Aurora cluster in us-east-1",
|
|
Labels: map[string]string{
|
|
types.OriginLabel: types.OriginCloud,
|
|
labelAccountID: "123456789012",
|
|
labelRegion: "us-east-1",
|
|
labelEngine: RDSEngineAuroraMySQL,
|
|
labelEngineVersion: "8.0.0",
|
|
labelEndpointType: "primary",
|
|
"key": "val",
|
|
},
|
|
}, types.DatabaseSpecV3{
|
|
Protocol: defaults.ProtocolMySQL,
|
|
URI: "localhost:3306",
|
|
AWS: expectedAWS,
|
|
})
|
|
require.NoError(t, err)
|
|
actual, err := NewDatabaseFromRDSCluster(cluster)
|
|
require.NoError(t, err)
|
|
require.Equal(t, expected, actual)
|
|
})
|
|
|
|
t.Run("reader", func(t *testing.T) {
|
|
expected, err := types.NewDatabaseV3(types.Metadata{
|
|
Name: "cluster-1-reader",
|
|
Description: "Aurora cluster in us-east-1 (reader endpoint)",
|
|
Labels: map[string]string{
|
|
types.OriginLabel: types.OriginCloud,
|
|
labelAccountID: "123456789012",
|
|
labelRegion: "us-east-1",
|
|
labelEngine: RDSEngineAuroraMySQL,
|
|
labelEngineVersion: "8.0.0",
|
|
labelEndpointType: "reader",
|
|
"key": "val",
|
|
},
|
|
}, types.DatabaseSpecV3{
|
|
Protocol: defaults.ProtocolMySQL,
|
|
URI: "reader.host:3306",
|
|
AWS: expectedAWS,
|
|
})
|
|
require.NoError(t, err)
|
|
actual, err := NewDatabaseFromRDSClusterReaderEndpoint(cluster)
|
|
require.NoError(t, err)
|
|
require.Equal(t, expected, actual)
|
|
})
|
|
|
|
t.Run("custom endpoints", func(t *testing.T) {
|
|
expectedLabels := map[string]string{
|
|
types.OriginLabel: types.OriginCloud,
|
|
labelAccountID: "123456789012",
|
|
labelRegion: "us-east-1",
|
|
labelEngine: RDSEngineAuroraMySQL,
|
|
labelEngineVersion: "8.0.0",
|
|
labelEndpointType: "custom",
|
|
"key": "val",
|
|
}
|
|
|
|
expectedMyEndpoint1, err := types.NewDatabaseV3(types.Metadata{
|
|
Name: "cluster-1-custom-myendpoint1",
|
|
Description: "Aurora cluster in us-east-1 (custom endpoint)",
|
|
Labels: expectedLabels,
|
|
}, types.DatabaseSpecV3{
|
|
Protocol: defaults.ProtocolMySQL,
|
|
URI: "myendpoint1.cluster-custom-example.us-east-1.rds.amazonaws.com:3306",
|
|
AWS: expectedAWS,
|
|
TLS: types.DatabaseTLS{
|
|
ServerName: "localhost",
|
|
},
|
|
})
|
|
require.NoError(t, err)
|
|
|
|
expectedMyEndpoint2, err := types.NewDatabaseV3(types.Metadata{
|
|
Name: "cluster-1-custom-myendpoint2",
|
|
Description: "Aurora cluster in us-east-1 (custom endpoint)",
|
|
Labels: expectedLabels,
|
|
}, types.DatabaseSpecV3{
|
|
Protocol: defaults.ProtocolMySQL,
|
|
URI: "myendpoint2.cluster-custom-example.us-east-1.rds.amazonaws.com:3306",
|
|
AWS: expectedAWS,
|
|
TLS: types.DatabaseTLS{
|
|
ServerName: "localhost",
|
|
},
|
|
})
|
|
require.NoError(t, err)
|
|
|
|
databases, err := NewDatabasesFromRDSClusterCustomEndpoints(cluster)
|
|
require.NoError(t, err)
|
|
require.Equal(t, types.Databases{expectedMyEndpoint1, expectedMyEndpoint2}, databases)
|
|
})
|
|
|
|
t.Run("bad custom endpoints ", func(t *testing.T) {
|
|
badCluster := *cluster
|
|
badCluster.CustomEndpoints = []*string{
|
|
aws.String("badendpoint1"),
|
|
aws.String("badendpoint2"),
|
|
}
|
|
_, err := NewDatabasesFromRDSClusterCustomEndpoints(&badCluster)
|
|
require.Error(t, err)
|
|
})
|
|
}
|
|
|
|
// TestDatabaseFromRDSClusterNameOverride tests converting an RDS cluster to a database resource with overridden name.
|
|
func TestDatabaseFromRDSClusterNameOverride(t *testing.T) {
|
|
cluster := &rds.DBCluster{
|
|
DBClusterArn: aws.String("arn:aws:rds:us-east-1:123456789012:cluster:cluster-1"),
|
|
DBClusterIdentifier: aws.String("cluster-1"),
|
|
DbClusterResourceId: aws.String("resource-1"),
|
|
IAMDatabaseAuthenticationEnabled: aws.Bool(true),
|
|
Engine: aws.String(RDSEngineAuroraMySQL),
|
|
EngineVersion: aws.String("8.0.0"),
|
|
Endpoint: aws.String("localhost"),
|
|
ReaderEndpoint: aws.String("reader.host"),
|
|
Port: aws.Int64(3306),
|
|
CustomEndpoints: []*string{
|
|
aws.String("myendpoint1.cluster-custom-example.us-east-1.rds.amazonaws.com"),
|
|
aws.String("myendpoint2.cluster-custom-example.us-east-1.rds.amazonaws.com"),
|
|
},
|
|
TagList: []*rds.Tag{
|
|
{Key: aws.String("key"), Value: aws.String("val")},
|
|
{Key: aws.String(labelTeleportDBName), Value: aws.String("mycluster-2")},
|
|
},
|
|
}
|
|
|
|
expectedAWS := types.AWS{
|
|
AccountID: "123456789012",
|
|
Region: "us-east-1",
|
|
RDS: types.RDS{
|
|
ClusterID: "cluster-1",
|
|
ResourceID: "resource-1",
|
|
IAMAuth: true,
|
|
},
|
|
}
|
|
|
|
t.Run("primary", func(t *testing.T) {
|
|
expected, err := types.NewDatabaseV3(types.Metadata{
|
|
Name: "mycluster-2",
|
|
Description: "Aurora cluster in us-east-1",
|
|
Labels: map[string]string{
|
|
types.OriginLabel: types.OriginCloud,
|
|
labelAccountID: "123456789012",
|
|
labelRegion: "us-east-1",
|
|
labelEngine: RDSEngineAuroraMySQL,
|
|
labelEngineVersion: "8.0.0",
|
|
labelEndpointType: "primary",
|
|
labelTeleportDBName: "mycluster-2",
|
|
"key": "val",
|
|
},
|
|
}, types.DatabaseSpecV3{
|
|
Protocol: defaults.ProtocolMySQL,
|
|
URI: "localhost:3306",
|
|
AWS: expectedAWS,
|
|
})
|
|
require.NoError(t, err)
|
|
actual, err := NewDatabaseFromRDSCluster(cluster)
|
|
require.NoError(t, err)
|
|
require.Equal(t, expected, actual)
|
|
})
|
|
|
|
t.Run("reader", func(t *testing.T) {
|
|
expected, err := types.NewDatabaseV3(types.Metadata{
|
|
Name: "mycluster-2-reader",
|
|
Description: "Aurora cluster in us-east-1 (reader endpoint)",
|
|
Labels: map[string]string{
|
|
types.OriginLabel: types.OriginCloud,
|
|
labelAccountID: "123456789012",
|
|
labelRegion: "us-east-1",
|
|
labelEngine: RDSEngineAuroraMySQL,
|
|
labelEngineVersion: "8.0.0",
|
|
labelEndpointType: "reader",
|
|
labelTeleportDBName: "mycluster-2",
|
|
"key": "val",
|
|
},
|
|
}, types.DatabaseSpecV3{
|
|
Protocol: defaults.ProtocolMySQL,
|
|
URI: "reader.host:3306",
|
|
AWS: expectedAWS,
|
|
})
|
|
require.NoError(t, err)
|
|
actual, err := NewDatabaseFromRDSClusterReaderEndpoint(cluster)
|
|
require.NoError(t, err)
|
|
require.Equal(t, expected, actual)
|
|
})
|
|
|
|
t.Run("custom endpoints", func(t *testing.T) {
|
|
expectedLabels := map[string]string{
|
|
types.OriginLabel: types.OriginCloud,
|
|
labelAccountID: "123456789012",
|
|
labelRegion: "us-east-1",
|
|
labelEngine: RDSEngineAuroraMySQL,
|
|
labelEngineVersion: "8.0.0",
|
|
labelEndpointType: "custom",
|
|
labelTeleportDBName: "mycluster-2",
|
|
"key": "val",
|
|
}
|
|
|
|
expectedMyEndpoint1, err := types.NewDatabaseV3(types.Metadata{
|
|
Name: "mycluster-2-custom-myendpoint1",
|
|
Description: "Aurora cluster in us-east-1 (custom endpoint)",
|
|
Labels: expectedLabels,
|
|
}, types.DatabaseSpecV3{
|
|
Protocol: defaults.ProtocolMySQL,
|
|
URI: "myendpoint1.cluster-custom-example.us-east-1.rds.amazonaws.com:3306",
|
|
AWS: expectedAWS,
|
|
TLS: types.DatabaseTLS{
|
|
ServerName: "localhost",
|
|
},
|
|
})
|
|
require.NoError(t, err)
|
|
|
|
expectedMyEndpoint2, err := types.NewDatabaseV3(types.Metadata{
|
|
Name: "mycluster-2-custom-myendpoint2",
|
|
Description: "Aurora cluster in us-east-1 (custom endpoint)",
|
|
Labels: expectedLabels,
|
|
}, types.DatabaseSpecV3{
|
|
Protocol: defaults.ProtocolMySQL,
|
|
URI: "myendpoint2.cluster-custom-example.us-east-1.rds.amazonaws.com:3306",
|
|
AWS: expectedAWS,
|
|
TLS: types.DatabaseTLS{
|
|
ServerName: "localhost",
|
|
},
|
|
})
|
|
require.NoError(t, err)
|
|
|
|
databases, err := NewDatabasesFromRDSClusterCustomEndpoints(cluster)
|
|
require.NoError(t, err)
|
|
require.Equal(t, types.Databases{expectedMyEndpoint1, expectedMyEndpoint2}, databases)
|
|
})
|
|
|
|
t.Run("bad custom endpoints ", func(t *testing.T) {
|
|
badCluster := *cluster
|
|
badCluster.CustomEndpoints = []*string{
|
|
aws.String("badendpoint1"),
|
|
aws.String("badendpoint2"),
|
|
}
|
|
_, err := NewDatabasesFromRDSClusterCustomEndpoints(&badCluster)
|
|
require.Error(t, err)
|
|
})
|
|
}
|
|
|
|
func TestDatabaseFromRDSProxy(t *testing.T) {
|
|
var port int64 = 9999
|
|
dbProxy := &rds.DBProxy{
|
|
DBProxyArn: aws.String("arn:aws:rds:ca-central-1:123456789012:db-proxy:prx-abcdef"),
|
|
DBProxyName: aws.String("testproxy"),
|
|
EngineFamily: aws.String(rds.EngineFamilyMysql),
|
|
Endpoint: aws.String("proxy.rds.test"),
|
|
VpcId: aws.String("test-vpc-id"),
|
|
}
|
|
|
|
dbProxyEndpoint := &rds.DBProxyEndpoint{
|
|
Endpoint: aws.String("custom.proxy.rds.test"),
|
|
DBProxyEndpointName: aws.String("custom"),
|
|
DBProxyName: aws.String("testproxy"),
|
|
DBProxyEndpointArn: aws.String("arn:aws:rds:ca-central-1:123456789012:db-proxy-endpoint:prx-endpoint-abcdef"),
|
|
TargetRole: aws.String(rds.DBProxyEndpointTargetRoleReadOnly),
|
|
}
|
|
|
|
tags := []*rds.Tag{{
|
|
Key: aws.String("key"),
|
|
Value: aws.String("val"),
|
|
}}
|
|
|
|
t.Run("default endpoint", func(t *testing.T) {
|
|
expected, err := types.NewDatabaseV3(types.Metadata{
|
|
Name: "testproxy",
|
|
Description: "RDS Proxy in ca-central-1",
|
|
Labels: map[string]string{
|
|
"key": "val",
|
|
types.OriginLabel: types.OriginCloud,
|
|
labelAccountID: "123456789012",
|
|
labelRegion: "ca-central-1",
|
|
labelEngine: "MYSQL",
|
|
labelVPCID: "test-vpc-id",
|
|
},
|
|
}, types.DatabaseSpecV3{
|
|
Protocol: defaults.ProtocolMySQL,
|
|
URI: "proxy.rds.test:9999",
|
|
AWS: types.AWS{
|
|
Region: "ca-central-1",
|
|
AccountID: "123456789012",
|
|
RDSProxy: types.RDSProxy{
|
|
ResourceID: "prx-abcdef",
|
|
Name: "testproxy",
|
|
},
|
|
},
|
|
})
|
|
require.NoError(t, err)
|
|
|
|
actual, err := NewDatabaseFromRDSProxy(dbProxy, port, tags)
|
|
require.NoError(t, err)
|
|
require.Equal(t, expected, actual)
|
|
})
|
|
|
|
t.Run("custom endpoint", func(t *testing.T) {
|
|
expected, err := types.NewDatabaseV3(types.Metadata{
|
|
Name: "testproxy-custom",
|
|
Description: "RDS Proxy endpoint in ca-central-1",
|
|
Labels: map[string]string{
|
|
"key": "val",
|
|
types.OriginLabel: types.OriginCloud,
|
|
labelAccountID: "123456789012",
|
|
labelRegion: "ca-central-1",
|
|
labelEngine: "MYSQL",
|
|
labelVPCID: "test-vpc-id",
|
|
labelEndpointType: "READ_ONLY",
|
|
},
|
|
}, types.DatabaseSpecV3{
|
|
Protocol: defaults.ProtocolMySQL,
|
|
URI: "custom.proxy.rds.test:9999",
|
|
AWS: types.AWS{
|
|
Region: "ca-central-1",
|
|
AccountID: "123456789012",
|
|
RDSProxy: types.RDSProxy{
|
|
ResourceID: "prx-abcdef",
|
|
Name: "testproxy",
|
|
CustomEndpointName: "custom",
|
|
},
|
|
},
|
|
TLS: types.DatabaseTLS{
|
|
ServerName: "proxy.rds.test",
|
|
},
|
|
})
|
|
require.NoError(t, err)
|
|
|
|
actual, err := NewDatabaseFromRDSProxyCustomEndpoint(dbProxy, dbProxyEndpoint, port, tags)
|
|
require.NoError(t, err)
|
|
require.Equal(t, expected, actual)
|
|
})
|
|
}
|
|
|
|
func TestAuroraMySQLVersion(t *testing.T) {
|
|
tests := []struct {
|
|
engineVersion string
|
|
expectedMySQLVersion string
|
|
}{
|
|
{
|
|
engineVersion: "5.6.10a",
|
|
expectedMySQLVersion: "5.6.10a",
|
|
},
|
|
{
|
|
engineVersion: "5.6.mysql_aurora.1.22.1",
|
|
expectedMySQLVersion: "1.22.1",
|
|
},
|
|
{
|
|
engineVersion: "5.6.mysql_aurora.1.22.1.3",
|
|
expectedMySQLVersion: "1.22.1.3",
|
|
},
|
|
}
|
|
for _, test := range tests {
|
|
t.Run(test.engineVersion, func(t *testing.T) {
|
|
require.Equal(t, test.expectedMySQLVersion, auroraMySQLVersion(&rds.DBCluster{EngineVersion: aws.String(test.engineVersion)}))
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestIsRDSClusterSupported(t *testing.T) {
|
|
tests := []struct {
|
|
name string
|
|
engineMode string
|
|
engineVersion string
|
|
isSupported bool
|
|
}{
|
|
{
|
|
name: "provisioned",
|
|
engineMode: RDSEngineModeProvisioned,
|
|
engineVersion: "5.6.mysql_aurora.1.22.0",
|
|
isSupported: true,
|
|
},
|
|
{
|
|
name: "serverless",
|
|
engineMode: RDSEngineModeServerless,
|
|
engineVersion: "5.6.mysql_aurora.1.22.0",
|
|
isSupported: false,
|
|
},
|
|
{
|
|
name: "parallel query supported",
|
|
engineMode: RDSEngineModeParallelQuery,
|
|
engineVersion: "5.6.mysql_aurora.1.22.0",
|
|
isSupported: true,
|
|
},
|
|
{
|
|
name: "parallel query unsupported",
|
|
engineMode: RDSEngineModeParallelQuery,
|
|
engineVersion: "5.6.mysql_aurora.1.19.6",
|
|
isSupported: false,
|
|
},
|
|
}
|
|
|
|
for _, test := range tests {
|
|
t.Run(test.name, func(t *testing.T) {
|
|
cluster := &rds.DBCluster{
|
|
DBClusterArn: aws.String("arn:aws:rds:us-east-1:123456789012:cluster:test"),
|
|
DBClusterIdentifier: aws.String(test.name),
|
|
DbClusterResourceId: aws.String(uuid.New().String()),
|
|
Engine: aws.String(RDSEngineAuroraMySQL),
|
|
EngineMode: aws.String(test.engineMode),
|
|
EngineVersion: aws.String(test.engineVersion),
|
|
}
|
|
|
|
got, want := IsRDSClusterSupported(cluster), test.isSupported
|
|
require.Equal(t, want, got, "IsRDSClusterSupported = %v, want = %v", got, want)
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestIsRDSInstanceSupported(t *testing.T) {
|
|
tests := []struct {
|
|
name string
|
|
engine string
|
|
engineVersion string
|
|
isSupported bool
|
|
}{
|
|
{
|
|
name: "non-MariaDB engine",
|
|
engine: RDSEnginePostgres,
|
|
engineVersion: "13.3",
|
|
isSupported: true,
|
|
},
|
|
{
|
|
name: "unsupported MariaDB",
|
|
engine: RDSEngineMariaDB,
|
|
engineVersion: "10.3.28",
|
|
isSupported: false,
|
|
},
|
|
{
|
|
name: "min supported version",
|
|
engine: RDSEngineMariaDB,
|
|
engineVersion: "10.6.2",
|
|
isSupported: true,
|
|
},
|
|
{
|
|
name: "supported version",
|
|
engine: RDSEngineMariaDB,
|
|
engineVersion: "10.8.0",
|
|
isSupported: true,
|
|
},
|
|
}
|
|
|
|
for _, test := range tests {
|
|
t.Run(test.name, func(t *testing.T) {
|
|
cluster := &rds.DBInstance{
|
|
DBInstanceArn: aws.String("arn:aws:rds:us-east-1:123456789012:instance:test"),
|
|
DBClusterIdentifier: aws.String(test.name),
|
|
DbiResourceId: aws.String(uuid.New().String()),
|
|
Engine: aws.String(test.engine),
|
|
EngineVersion: aws.String(test.engineVersion),
|
|
}
|
|
|
|
got, want := IsRDSInstanceSupported(cluster), test.isSupported
|
|
require.Equal(t, want, got, "IsRDSInstanceSupported = %v, want = %v", got, want)
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestAzureTagsToLabels(t *testing.T) {
|
|
azureTags := map[string]string{
|
|
"Env": "dev",
|
|
"foo:bar": "some-id",
|
|
"Name": "test",
|
|
}
|
|
labels := azureTagsToLabels(azureTags)
|
|
require.Equal(t, map[string]string{"Name": "test", "Env": "dev",
|
|
"foo:bar": "some-id"}, labels)
|
|
}
|
|
|
|
// TestDatabaseFromRedshiftCluster tests converting an Redshift cluster to a database resource.
|
|
func TestDatabaseFromRedshiftCluster(t *testing.T) {
|
|
t.Run("success", func(t *testing.T) {
|
|
cluster := &redshift.Cluster{
|
|
ClusterIdentifier: aws.String("mycluster"),
|
|
ClusterNamespaceArn: aws.String("arn:aws:redshift:us-east-1:123456789012:namespace:u-u-i-d"),
|
|
Endpoint: &redshift.Endpoint{
|
|
Address: aws.String("localhost"),
|
|
Port: aws.Int64(5439),
|
|
},
|
|
Tags: []*redshift.Tag{
|
|
{
|
|
Key: aws.String("key"),
|
|
Value: aws.String("val"),
|
|
},
|
|
{
|
|
Key: aws.String("elasticbeanstalk:environment-id"),
|
|
Value: aws.String("id"),
|
|
},
|
|
},
|
|
}
|
|
expected, err := types.NewDatabaseV3(types.Metadata{
|
|
Name: "mycluster",
|
|
Description: "Redshift cluster in us-east-1",
|
|
Labels: map[string]string{
|
|
types.OriginLabel: types.OriginCloud,
|
|
labelAccountID: "123456789012",
|
|
labelRegion: "us-east-1",
|
|
"key": "val",
|
|
"elasticbeanstalk:environment-id": "id",
|
|
},
|
|
}, types.DatabaseSpecV3{
|
|
Protocol: defaults.ProtocolPostgres,
|
|
URI: "localhost:5439",
|
|
AWS: types.AWS{
|
|
AccountID: "123456789012",
|
|
Region: "us-east-1",
|
|
Redshift: types.Redshift{
|
|
ClusterID: "mycluster",
|
|
},
|
|
},
|
|
})
|
|
|
|
require.NoError(t, err)
|
|
|
|
actual, err := NewDatabaseFromRedshiftCluster(cluster)
|
|
require.NoError(t, err)
|
|
require.Equal(t, expected, actual)
|
|
})
|
|
|
|
t.Run("success with name override", func(t *testing.T) {
|
|
cluster := &redshift.Cluster{
|
|
ClusterIdentifier: aws.String("mycluster"),
|
|
ClusterNamespaceArn: aws.String("arn:aws:redshift:us-east-1:123456789012:namespace:u-u-i-d"),
|
|
Endpoint: &redshift.Endpoint{
|
|
Address: aws.String("localhost"),
|
|
Port: aws.Int64(5439),
|
|
},
|
|
Tags: []*redshift.Tag{
|
|
{
|
|
Key: aws.String("key"),
|
|
Value: aws.String("val"),
|
|
},
|
|
{
|
|
Key: aws.String("elasticbeanstalk:environment-id"),
|
|
Value: aws.String("id"),
|
|
},
|
|
{
|
|
Key: aws.String(labelTeleportDBName),
|
|
Value: aws.String("mycluster-override-2"),
|
|
},
|
|
},
|
|
}
|
|
expected, err := types.NewDatabaseV3(types.Metadata{
|
|
Name: "mycluster-override-2",
|
|
Description: "Redshift cluster in us-east-1",
|
|
Labels: map[string]string{
|
|
types.OriginLabel: types.OriginCloud,
|
|
labelAccountID: "123456789012",
|
|
labelRegion: "us-east-1",
|
|
labelTeleportDBName: "mycluster-override-2",
|
|
"key": "val",
|
|
"elasticbeanstalk:environment-id": "id",
|
|
},
|
|
}, types.DatabaseSpecV3{
|
|
Protocol: defaults.ProtocolPostgres,
|
|
URI: "localhost:5439",
|
|
AWS: types.AWS{
|
|
AccountID: "123456789012",
|
|
Region: "us-east-1",
|
|
Redshift: types.Redshift{
|
|
ClusterID: "mycluster",
|
|
},
|
|
},
|
|
})
|
|
|
|
require.NoError(t, err)
|
|
|
|
actual, err := NewDatabaseFromRedshiftCluster(cluster)
|
|
require.NoError(t, err)
|
|
require.Equal(t, expected, actual)
|
|
})
|
|
|
|
t.Run("missing endpoint", func(t *testing.T) {
|
|
_, err := NewDatabaseFromRedshiftCluster(&redshift.Cluster{
|
|
ClusterIdentifier: aws.String("still-creating"),
|
|
})
|
|
require.Error(t, err)
|
|
require.True(t, trace.IsBadParameter(err), "Expected trace.BadParameter, got %v", err)
|
|
})
|
|
}
|
|
|
|
func TestDatabaseFromElastiCacheConfigurationEndpoint(t *testing.T) {
|
|
cluster := &elasticache.ReplicationGroup{
|
|
ARN: aws.String("arn:aws:elasticache:us-east-1:123456789012:replicationgroup:my-cluster"),
|
|
ReplicationGroupId: aws.String("my-cluster"),
|
|
Status: aws.String("available"),
|
|
TransitEncryptionEnabled: aws.Bool(true),
|
|
ClusterEnabled: aws.Bool(true),
|
|
ConfigurationEndpoint: &elasticache.Endpoint{
|
|
Address: aws.String("configuration.localhost"),
|
|
Port: aws.Int64(6379),
|
|
},
|
|
UserGroupIds: []*string{aws.String("my-user-group")},
|
|
NodeGroups: []*elasticache.NodeGroup{
|
|
{
|
|
NodeGroupId: aws.String("0001"),
|
|
NodeGroupMembers: []*elasticache.NodeGroupMember{
|
|
{
|
|
CacheClusterId: aws.String("my-cluster-0001-001"),
|
|
},
|
|
{
|
|
CacheClusterId: aws.String("my-cluster-0001-002"),
|
|
},
|
|
},
|
|
},
|
|
{
|
|
NodeGroupId: aws.String("0002"),
|
|
NodeGroupMembers: []*elasticache.NodeGroupMember{
|
|
{
|
|
CacheClusterId: aws.String("my-cluster-0002-001"),
|
|
},
|
|
{
|
|
CacheClusterId: aws.String("my-cluster-0002-002"),
|
|
},
|
|
},
|
|
},
|
|
},
|
|
}
|
|
extraLabels := map[string]string{"key": "value"}
|
|
|
|
expected, err := types.NewDatabaseV3(types.Metadata{
|
|
Name: "my-cluster",
|
|
Description: "ElastiCache cluster in us-east-1 (configuration endpoint)",
|
|
Labels: map[string]string{
|
|
types.OriginLabel: types.OriginCloud,
|
|
labelAccountID: "123456789012",
|
|
labelRegion: "us-east-1",
|
|
labelEndpointType: "configuration",
|
|
"key": "value",
|
|
},
|
|
}, types.DatabaseSpecV3{
|
|
Protocol: defaults.ProtocolRedis,
|
|
URI: "configuration.localhost:6379",
|
|
AWS: types.AWS{
|
|
AccountID: "123456789012",
|
|
Region: "us-east-1",
|
|
ElastiCache: types.ElastiCache{
|
|
ReplicationGroupID: "my-cluster",
|
|
UserGroupIDs: []string{"my-user-group"},
|
|
TransitEncryptionEnabled: true,
|
|
EndpointType: awsutils.ElastiCacheConfigurationEndpoint,
|
|
},
|
|
},
|
|
})
|
|
require.NoError(t, err)
|
|
|
|
actual, err := NewDatabaseFromElastiCacheConfigurationEndpoint(cluster, extraLabels)
|
|
require.NoError(t, err)
|
|
require.Equal(t, expected, actual)
|
|
}
|
|
|
|
func TestDatabaseFromElastiCacheConfigurationEndpointNameOverride(t *testing.T) {
|
|
cluster := &elasticache.ReplicationGroup{
|
|
ARN: aws.String("arn:aws:elasticache:us-east-1:123456789012:replicationgroup:my-cluster"),
|
|
ReplicationGroupId: aws.String("my-cluster"),
|
|
Status: aws.String("available"),
|
|
TransitEncryptionEnabled: aws.Bool(true),
|
|
ClusterEnabled: aws.Bool(true),
|
|
ConfigurationEndpoint: &elasticache.Endpoint{
|
|
Address: aws.String("configuration.localhost"),
|
|
Port: aws.Int64(6379),
|
|
},
|
|
UserGroupIds: []*string{aws.String("my-user-group")},
|
|
NodeGroups: []*elasticache.NodeGroup{
|
|
{
|
|
NodeGroupId: aws.String("0001"),
|
|
NodeGroupMembers: []*elasticache.NodeGroupMember{
|
|
{
|
|
CacheClusterId: aws.String("my-cluster-0001-001"),
|
|
},
|
|
{
|
|
CacheClusterId: aws.String("my-cluster-0001-002"),
|
|
},
|
|
},
|
|
},
|
|
{
|
|
NodeGroupId: aws.String("0002"),
|
|
NodeGroupMembers: []*elasticache.NodeGroupMember{
|
|
{
|
|
CacheClusterId: aws.String("my-cluster-0002-001"),
|
|
},
|
|
{
|
|
CacheClusterId: aws.String("my-cluster-0002-002"),
|
|
},
|
|
},
|
|
},
|
|
},
|
|
}
|
|
extraLabels := map[string]string{
|
|
labelTeleportDBName: "my-override-cluster-2",
|
|
"key": "value",
|
|
}
|
|
|
|
expected, err := types.NewDatabaseV3(types.Metadata{
|
|
Name: "my-override-cluster-2",
|
|
Description: "ElastiCache cluster in us-east-1 (configuration endpoint)",
|
|
Labels: map[string]string{
|
|
types.OriginLabel: types.OriginCloud,
|
|
labelAccountID: "123456789012",
|
|
labelRegion: "us-east-1",
|
|
labelEndpointType: "configuration",
|
|
labelTeleportDBName: "my-override-cluster-2",
|
|
"key": "value",
|
|
},
|
|
}, types.DatabaseSpecV3{
|
|
Protocol: defaults.ProtocolRedis,
|
|
URI: "configuration.localhost:6379",
|
|
AWS: types.AWS{
|
|
AccountID: "123456789012",
|
|
Region: "us-east-1",
|
|
ElastiCache: types.ElastiCache{
|
|
ReplicationGroupID: "my-cluster",
|
|
UserGroupIDs: []string{"my-user-group"},
|
|
TransitEncryptionEnabled: true,
|
|
EndpointType: awsutils.ElastiCacheConfigurationEndpoint,
|
|
},
|
|
},
|
|
})
|
|
require.NoError(t, err)
|
|
|
|
actual, err := NewDatabaseFromElastiCacheConfigurationEndpoint(cluster, extraLabels)
|
|
require.NoError(t, err)
|
|
require.Equal(t, expected, actual)
|
|
}
|
|
|
|
func TestDatabaseFromElastiCacheNodeGroups(t *testing.T) {
|
|
cluster := &elasticache.ReplicationGroup{
|
|
ARN: aws.String("arn:aws:elasticache:us-east-1:123456789012:replicationgroup:my-cluster"),
|
|
ReplicationGroupId: aws.String("my-cluster"),
|
|
Status: aws.String("available"),
|
|
TransitEncryptionEnabled: aws.Bool(true),
|
|
ClusterEnabled: aws.Bool(false),
|
|
UserGroupIds: []*string{aws.String("my-user-group")},
|
|
NodeGroups: []*elasticache.NodeGroup{
|
|
{
|
|
NodeGroupId: aws.String("0001"),
|
|
PrimaryEndpoint: &elasticache.Endpoint{
|
|
Address: aws.String("primary.localhost"),
|
|
Port: aws.Int64(6379),
|
|
},
|
|
ReaderEndpoint: &elasticache.Endpoint{
|
|
Address: aws.String("reader.localhost"),
|
|
Port: aws.Int64(6379),
|
|
},
|
|
},
|
|
},
|
|
}
|
|
extraLabels := map[string]string{"key": "value"}
|
|
|
|
expectedPrimary, err := types.NewDatabaseV3(types.Metadata{
|
|
Name: "my-cluster",
|
|
Description: "ElastiCache cluster in us-east-1 (primary endpoint)",
|
|
Labels: map[string]string{
|
|
types.OriginLabel: types.OriginCloud,
|
|
labelAccountID: "123456789012",
|
|
labelRegion: "us-east-1",
|
|
labelEndpointType: "primary",
|
|
"key": "value",
|
|
},
|
|
}, types.DatabaseSpecV3{
|
|
Protocol: defaults.ProtocolRedis,
|
|
URI: "primary.localhost:6379",
|
|
AWS: types.AWS{
|
|
AccountID: "123456789012",
|
|
Region: "us-east-1",
|
|
ElastiCache: types.ElastiCache{
|
|
ReplicationGroupID: "my-cluster",
|
|
UserGroupIDs: []string{"my-user-group"},
|
|
TransitEncryptionEnabled: true,
|
|
EndpointType: awsutils.ElastiCachePrimaryEndpoint,
|
|
},
|
|
},
|
|
})
|
|
require.NoError(t, err)
|
|
|
|
expectedReader, err := types.NewDatabaseV3(types.Metadata{
|
|
Name: "my-cluster-reader",
|
|
Description: "ElastiCache cluster in us-east-1 (reader endpoint)",
|
|
Labels: map[string]string{
|
|
types.OriginLabel: types.OriginCloud,
|
|
labelAccountID: "123456789012",
|
|
labelRegion: "us-east-1",
|
|
labelEndpointType: "reader",
|
|
"key": "value",
|
|
},
|
|
}, types.DatabaseSpecV3{
|
|
Protocol: defaults.ProtocolRedis,
|
|
URI: "reader.localhost:6379",
|
|
AWS: types.AWS{
|
|
AccountID: "123456789012",
|
|
Region: "us-east-1",
|
|
ElastiCache: types.ElastiCache{
|
|
ReplicationGroupID: "my-cluster",
|
|
UserGroupIDs: []string{"my-user-group"},
|
|
TransitEncryptionEnabled: true,
|
|
EndpointType: awsutils.ElastiCacheReaderEndpoint,
|
|
},
|
|
},
|
|
})
|
|
require.NoError(t, err)
|
|
|
|
actual, err := NewDatabasesFromElastiCacheNodeGroups(cluster, extraLabels)
|
|
require.NoError(t, err)
|
|
require.Equal(t, types.Databases{expectedPrimary, expectedReader}, actual)
|
|
}
|
|
|
|
func TestDatabaseFromElastiCacheNodeGroupsNameOverride(t *testing.T) {
|
|
cluster := &elasticache.ReplicationGroup{
|
|
ARN: aws.String("arn:aws:elasticache:us-east-1:123456789012:replicationgroup:my-cluster"),
|
|
ReplicationGroupId: aws.String("my-cluster"),
|
|
Status: aws.String("available"),
|
|
TransitEncryptionEnabled: aws.Bool(true),
|
|
ClusterEnabled: aws.Bool(false),
|
|
UserGroupIds: []*string{aws.String("my-user-group")},
|
|
NodeGroups: []*elasticache.NodeGroup{
|
|
{
|
|
NodeGroupId: aws.String("0001"),
|
|
PrimaryEndpoint: &elasticache.Endpoint{
|
|
Address: aws.String("primary.localhost"),
|
|
Port: aws.Int64(6379),
|
|
},
|
|
ReaderEndpoint: &elasticache.Endpoint{
|
|
Address: aws.String("reader.localhost"),
|
|
Port: aws.Int64(6379),
|
|
},
|
|
},
|
|
},
|
|
}
|
|
extraLabels := map[string]string{
|
|
labelTeleportDBName: "my-override-cluster-2",
|
|
"key": "value",
|
|
}
|
|
|
|
expectedPrimary, err := types.NewDatabaseV3(types.Metadata{
|
|
Name: "my-override-cluster-2",
|
|
Description: "ElastiCache cluster in us-east-1 (primary endpoint)",
|
|
Labels: map[string]string{
|
|
types.OriginLabel: types.OriginCloud,
|
|
labelAccountID: "123456789012",
|
|
labelRegion: "us-east-1",
|
|
labelEndpointType: "primary",
|
|
labelTeleportDBName: "my-override-cluster-2",
|
|
"key": "value",
|
|
},
|
|
}, types.DatabaseSpecV3{
|
|
Protocol: defaults.ProtocolRedis,
|
|
URI: "primary.localhost:6379",
|
|
AWS: types.AWS{
|
|
AccountID: "123456789012",
|
|
Region: "us-east-1",
|
|
ElastiCache: types.ElastiCache{
|
|
ReplicationGroupID: "my-cluster",
|
|
UserGroupIDs: []string{"my-user-group"},
|
|
TransitEncryptionEnabled: true,
|
|
EndpointType: awsutils.ElastiCachePrimaryEndpoint,
|
|
},
|
|
},
|
|
})
|
|
require.NoError(t, err)
|
|
|
|
expectedReader, err := types.NewDatabaseV3(types.Metadata{
|
|
Name: "my-override-cluster-2-reader",
|
|
Description: "ElastiCache cluster in us-east-1 (reader endpoint)",
|
|
Labels: map[string]string{
|
|
types.OriginLabel: types.OriginCloud,
|
|
labelAccountID: "123456789012",
|
|
labelRegion: "us-east-1",
|
|
labelEndpointType: "reader",
|
|
labelTeleportDBName: "my-override-cluster-2",
|
|
"key": "value",
|
|
},
|
|
}, types.DatabaseSpecV3{
|
|
Protocol: defaults.ProtocolRedis,
|
|
URI: "reader.localhost:6379",
|
|
AWS: types.AWS{
|
|
AccountID: "123456789012",
|
|
Region: "us-east-1",
|
|
ElastiCache: types.ElastiCache{
|
|
ReplicationGroupID: "my-cluster",
|
|
UserGroupIDs: []string{"my-user-group"},
|
|
TransitEncryptionEnabled: true,
|
|
EndpointType: awsutils.ElastiCacheReaderEndpoint,
|
|
},
|
|
},
|
|
})
|
|
require.NoError(t, err)
|
|
|
|
actual, err := NewDatabasesFromElastiCacheNodeGroups(cluster, extraLabels)
|
|
require.NoError(t, err)
|
|
require.Equal(t, types.Databases{expectedPrimary, expectedReader}, actual)
|
|
}
|
|
|
|
func TestDatabaseFromMemoryDBCluster(t *testing.T) {
|
|
cluster := &memorydb.Cluster{
|
|
ARN: aws.String("arn:aws:memorydb:us-east-1:123456789012:cluster:my-cluster"),
|
|
Name: aws.String("my-cluster"),
|
|
Status: aws.String("available"),
|
|
TLSEnabled: aws.Bool(true),
|
|
ACLName: aws.String("my-user-group"),
|
|
ClusterEndpoint: &memorydb.Endpoint{
|
|
Address: aws.String("memorydb.localhost"),
|
|
Port: aws.Int64(6379),
|
|
},
|
|
}
|
|
extraLabels := map[string]string{"key": "value"}
|
|
|
|
expected, err := types.NewDatabaseV3(types.Metadata{
|
|
Name: "my-cluster",
|
|
Description: "MemoryDB cluster in us-east-1",
|
|
Labels: map[string]string{
|
|
types.OriginLabel: types.OriginCloud,
|
|
labelAccountID: "123456789012",
|
|
labelRegion: "us-east-1",
|
|
labelEndpointType: "cluster",
|
|
"key": "value",
|
|
},
|
|
}, types.DatabaseSpecV3{
|
|
Protocol: defaults.ProtocolRedis,
|
|
URI: "memorydb.localhost:6379",
|
|
AWS: types.AWS{
|
|
AccountID: "123456789012",
|
|
Region: "us-east-1",
|
|
MemoryDB: types.MemoryDB{
|
|
ClusterName: "my-cluster",
|
|
ACLName: "my-user-group",
|
|
TLSEnabled: true,
|
|
EndpointType: awsutils.MemoryDBClusterEndpoint,
|
|
},
|
|
},
|
|
})
|
|
require.NoError(t, err)
|
|
|
|
actual, err := NewDatabaseFromMemoryDBCluster(cluster, extraLabels)
|
|
require.NoError(t, err)
|
|
require.Equal(t, expected, actual)
|
|
}
|
|
|
|
func TestDatabaseFromRedshiftServerlessWorkgroup(t *testing.T) {
|
|
workgroup := mocks.RedshiftServerlessWorkgroup("my-workgroup", "eu-west-2")
|
|
tags := libcloudaws.LabelsToTags[redshiftserverless.Tag](map[string]string{"env": "prod"})
|
|
expected, err := types.NewDatabaseV3(types.Metadata{
|
|
Name: "my-workgroup",
|
|
Description: "Redshift Serverless workgroup in eu-west-2",
|
|
Labels: map[string]string{
|
|
types.OriginLabel: types.OriginCloud,
|
|
labelAccountID: "123456789012",
|
|
labelRegion: "eu-west-2",
|
|
labelEndpointType: "workgroup",
|
|
labelNamespace: "my-namespace",
|
|
labelVPCID: "vpc-id",
|
|
"env": "prod",
|
|
},
|
|
}, types.DatabaseSpecV3{
|
|
Protocol: defaults.ProtocolPostgres,
|
|
URI: "my-workgroup.123456789012.eu-west-2.redshift-serverless.amazonaws.com:5439",
|
|
AWS: types.AWS{
|
|
AccountID: "123456789012",
|
|
Region: "eu-west-2",
|
|
RedshiftServerless: types.RedshiftServerless{
|
|
WorkgroupName: "my-workgroup",
|
|
WorkgroupID: "some-uuid-for-my-workgroup",
|
|
},
|
|
},
|
|
})
|
|
require.NoError(t, err)
|
|
|
|
actual, err := NewDatabaseFromRedshiftServerlessWorkgroup(workgroup, tags)
|
|
require.NoError(t, err)
|
|
require.Equal(t, expected, actual)
|
|
}
|
|
|
|
func TestDatabaseFromRedshiftServerlessVPCEndpoint(t *testing.T) {
|
|
workgroup := mocks.RedshiftServerlessWorkgroup("my-workgroup", "eu-west-2")
|
|
endpoint := mocks.RedshiftServerlessEndpointAccess(workgroup, "my-endpoint", "eu-west-2")
|
|
tags := libcloudaws.LabelsToTags[redshiftserverless.Tag](map[string]string{"env": "prod"})
|
|
expected, err := types.NewDatabaseV3(types.Metadata{
|
|
Name: "my-workgroup-my-endpoint",
|
|
Description: "Redshift Serverless endpoint in eu-west-2",
|
|
Labels: map[string]string{
|
|
types.OriginLabel: types.OriginCloud,
|
|
labelAccountID: "123456789012",
|
|
labelRegion: "eu-west-2",
|
|
labelEndpointType: "vpc-endpoint",
|
|
labelWorkgroup: "my-workgroup",
|
|
labelNamespace: "my-namespace",
|
|
labelVPCID: "vpc-id",
|
|
"env": "prod",
|
|
},
|
|
}, types.DatabaseSpecV3{
|
|
Protocol: defaults.ProtocolPostgres,
|
|
URI: "my-endpoint-endpoint-xxxyyyzzz.123456789012.eu-west-2.redshift-serverless.amazonaws.com:5439",
|
|
AWS: types.AWS{
|
|
AccountID: "123456789012",
|
|
Region: "eu-west-2",
|
|
RedshiftServerless: types.RedshiftServerless{
|
|
WorkgroupName: "my-workgroup",
|
|
EndpointName: "my-endpoint",
|
|
WorkgroupID: "some-uuid-for-my-workgroup",
|
|
},
|
|
},
|
|
TLS: types.DatabaseTLS{
|
|
ServerName: "my-workgroup.123456789012.eu-west-2.redshift-serverless.amazonaws.com",
|
|
},
|
|
})
|
|
require.NoError(t, err)
|
|
|
|
actual, err := NewDatabaseFromRedshiftServerlessVPCEndpoint(endpoint, workgroup, tags)
|
|
require.NoError(t, err)
|
|
require.Equal(t, expected, actual)
|
|
}
|
|
|
|
func TestDatabaseFromMemoryDBClusterNameOverride(t *testing.T) {
|
|
cluster := &memorydb.Cluster{
|
|
ARN: aws.String("arn:aws:memorydb:us-east-1:123456789012:cluster:my-cluster"),
|
|
Name: aws.String("my-cluster"),
|
|
Status: aws.String("available"),
|
|
TLSEnabled: aws.Bool(true),
|
|
ACLName: aws.String("my-user-group"),
|
|
ClusterEndpoint: &memorydb.Endpoint{
|
|
Address: aws.String("memorydb.localhost"),
|
|
Port: aws.Int64(6379),
|
|
},
|
|
}
|
|
extraLabels := map[string]string{
|
|
labelTeleportDBName: "override-1",
|
|
"key": "value",
|
|
}
|
|
|
|
expected, err := types.NewDatabaseV3(types.Metadata{
|
|
Name: "override-1",
|
|
Description: "MemoryDB cluster in us-east-1",
|
|
Labels: map[string]string{
|
|
types.OriginLabel: types.OriginCloud,
|
|
labelAccountID: "123456789012",
|
|
labelRegion: "us-east-1",
|
|
labelEndpointType: "cluster",
|
|
labelTeleportDBName: "override-1",
|
|
"key": "value",
|
|
},
|
|
}, types.DatabaseSpecV3{
|
|
Protocol: defaults.ProtocolRedis,
|
|
URI: "memorydb.localhost:6379",
|
|
AWS: types.AWS{
|
|
AccountID: "123456789012",
|
|
Region: "us-east-1",
|
|
MemoryDB: types.MemoryDB{
|
|
ClusterName: "my-cluster",
|
|
ACLName: "my-user-group",
|
|
TLSEnabled: true,
|
|
EndpointType: awsutils.MemoryDBClusterEndpoint,
|
|
},
|
|
},
|
|
})
|
|
require.NoError(t, err)
|
|
|
|
actual, err := NewDatabaseFromMemoryDBCluster(cluster, extraLabels)
|
|
require.NoError(t, err)
|
|
require.Equal(t, expected, actual)
|
|
}
|
|
|
|
func TestExtraElastiCacheLabels(t *testing.T) {
|
|
cluster := &elasticache.ReplicationGroup{
|
|
ReplicationGroupId: aws.String("my-redis"),
|
|
}
|
|
tags := []*elasticache.Tag{
|
|
{Key: aws.String("key1"), Value: aws.String("value1")},
|
|
{Key: aws.String("key2"), Value: aws.String("value2")},
|
|
}
|
|
|
|
nodes := []*elasticache.CacheCluster{
|
|
{
|
|
ReplicationGroupId: aws.String("some-other-redis"),
|
|
EngineVersion: aws.String("8.8.8"),
|
|
CacheSubnetGroupName: aws.String("some-other-subnet-group"),
|
|
},
|
|
{
|
|
ReplicationGroupId: aws.String("my-redis"),
|
|
EngineVersion: aws.String("6.6.6"),
|
|
CacheSubnetGroupName: aws.String("my-subnet-group"),
|
|
},
|
|
}
|
|
|
|
subnetGroups := []*elasticache.CacheSubnetGroup{
|
|
{
|
|
CacheSubnetGroupName: aws.String("some-other-subnet-group"),
|
|
VpcId: aws.String("some-other-vpc"),
|
|
},
|
|
{
|
|
CacheSubnetGroupName: aws.String("my-subnet-group"),
|
|
VpcId: aws.String("my-vpc"),
|
|
},
|
|
}
|
|
|
|
tests := []struct {
|
|
name string
|
|
inputTags []*elasticache.Tag
|
|
inputNodes []*elasticache.CacheCluster
|
|
inputSubnetGroups []*elasticache.CacheSubnetGroup
|
|
expectLabels map[string]string
|
|
}{
|
|
{
|
|
name: "all tags",
|
|
inputTags: tags,
|
|
inputNodes: nodes,
|
|
inputSubnetGroups: subnetGroups,
|
|
expectLabels: map[string]string{
|
|
"key1": "value1",
|
|
"key2": "value2",
|
|
"engine-version": "6.6.6",
|
|
"vpc-id": "my-vpc",
|
|
},
|
|
},
|
|
{
|
|
name: "no resource tags",
|
|
inputTags: nil,
|
|
inputNodes: nodes,
|
|
inputSubnetGroups: subnetGroups,
|
|
expectLabels: map[string]string{
|
|
"engine-version": "6.6.6",
|
|
"vpc-id": "my-vpc",
|
|
},
|
|
},
|
|
{
|
|
name: "no nodes",
|
|
inputTags: tags,
|
|
inputNodes: nil,
|
|
inputSubnetGroups: subnetGroups,
|
|
|
|
// Without subnet group name from nodes, VPC ID cannot be found.
|
|
expectLabels: map[string]string{
|
|
"key1": "value1",
|
|
"key2": "value2",
|
|
},
|
|
},
|
|
{
|
|
name: "no subnet groups",
|
|
inputTags: tags,
|
|
inputNodes: nodes,
|
|
inputSubnetGroups: nil,
|
|
expectLabels: map[string]string{
|
|
"key1": "value1",
|
|
"key2": "value2",
|
|
"engine-version": "6.6.6",
|
|
},
|
|
},
|
|
}
|
|
|
|
for _, test := range tests {
|
|
t.Run(test.name, func(t *testing.T) {
|
|
actualLabels := ExtraElastiCacheLabels(cluster, test.inputTags, test.inputNodes, test.inputSubnetGroups)
|
|
require.Equal(t, test.expectLabels, actualLabels)
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestExtraMemoryDBLabels(t *testing.T) {
|
|
cluster := &memorydb.Cluster{
|
|
Name: aws.String("my-cluster"),
|
|
SubnetGroupName: aws.String("my-subnet-group"),
|
|
EngineVersion: aws.String("6.6.6"),
|
|
}
|
|
|
|
allSubnetGroups := []*memorydb.SubnetGroup{
|
|
{
|
|
Name: aws.String("other-subnet-group"),
|
|
VpcId: aws.String("other-vpc-id"),
|
|
},
|
|
{
|
|
Name: aws.String("my-subnet-group"),
|
|
VpcId: aws.String("my-vpc-id"),
|
|
},
|
|
}
|
|
|
|
resourceTags := []*memorydb.Tag{
|
|
{Key: aws.String("key1"), Value: aws.String("value1")},
|
|
{Key: aws.String("key2"), Value: aws.String("value2")},
|
|
}
|
|
|
|
expected := map[string]string{
|
|
"key1": "value1",
|
|
"key2": "value2",
|
|
"engine-version": "6.6.6",
|
|
"vpc-id": "my-vpc-id",
|
|
}
|
|
|
|
actual := ExtraMemoryDBLabels(cluster, resourceTags, allSubnetGroups)
|
|
require.Equal(t, expected, actual)
|
|
}
|
|
|
|
func TestGetLabelEngineVersion(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
tests := []struct {
|
|
name string
|
|
labels map[string]string
|
|
want string
|
|
}{
|
|
{
|
|
name: "mysql-8.0.0",
|
|
labels: map[string]string{
|
|
labelEngine: RDSEngineMySQL,
|
|
labelEngineVersion: "8.0.0",
|
|
},
|
|
want: "8.0.0",
|
|
},
|
|
{
|
|
name: "mariadb returns nothing",
|
|
labels: map[string]string{
|
|
labelEngine: RDSEngineMariaDB,
|
|
labelEngineVersion: "10.6.7",
|
|
},
|
|
want: "",
|
|
},
|
|
{
|
|
name: "missing labels",
|
|
labels: map[string]string{},
|
|
want: "",
|
|
},
|
|
{
|
|
name: "azure-mysql-8.0.0",
|
|
labels: map[string]string{
|
|
labelEngine: AzureEngineMySQL,
|
|
labelEngineVersion: "8.0.0",
|
|
},
|
|
want: "8.0.0",
|
|
},
|
|
{
|
|
name: "azure-mysql-8.0.0 flex server",
|
|
labels: map[string]string{
|
|
labelEngine: AzureEngineMySQLFlex,
|
|
labelEngineVersion: string(armmysqlflexibleservers.ServerVersionEight021),
|
|
},
|
|
want: "8.0.21",
|
|
},
|
|
}
|
|
|
|
for _, tt := range tests {
|
|
tt := tt
|
|
t.Run(tt.name, func(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
if got := GetMySQLEngineVersion(tt.labels); got != tt.want {
|
|
t.Errorf("GetMySQLEngineVersion() = %v, want %v", got, tt.want)
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func Test_setDBName(t *testing.T) {
|
|
tests := []struct {
|
|
name string
|
|
meta types.Metadata
|
|
firstNamePart string
|
|
extraNameParts []string
|
|
want types.Metadata
|
|
}{
|
|
{
|
|
name: "no override, one part name",
|
|
meta: types.Metadata{},
|
|
firstNamePart: "foo",
|
|
extraNameParts: nil,
|
|
want: types.Metadata{Name: "foo"},
|
|
},
|
|
{
|
|
name: "no override, multi part name",
|
|
meta: types.Metadata{},
|
|
firstNamePart: "foo",
|
|
extraNameParts: []string{"bar", "baz"},
|
|
want: types.Metadata{Name: "foo-bar-baz"},
|
|
},
|
|
{
|
|
name: "override, one part name",
|
|
meta: types.Metadata{Labels: map[string]string{labelTeleportDBName: "gizmo"}},
|
|
firstNamePart: "foo",
|
|
extraNameParts: nil,
|
|
want: types.Metadata{Name: "gizmo", Labels: map[string]string{labelTeleportDBName: "gizmo"}},
|
|
},
|
|
{
|
|
name: "override, multi part name",
|
|
meta: types.Metadata{Labels: map[string]string{labelTeleportDBName: "gizmo"}},
|
|
firstNamePart: "foo",
|
|
extraNameParts: []string{"bar", "baz"},
|
|
want: types.Metadata{Name: "gizmo-bar-baz", Labels: map[string]string{labelTeleportDBName: "gizmo"}},
|
|
},
|
|
}
|
|
for _, tt := range tests {
|
|
t.Run(tt.name, func(t *testing.T) {
|
|
result := setDBName(tt.meta, tt.firstNamePart, tt.extraNameParts...)
|
|
require.Equal(t, tt.want, result)
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestNewDatabaseFromAzureSQLServer(t *testing.T) {
|
|
for _, tc := range []struct {
|
|
desc string
|
|
server *armsql.Server
|
|
expectedErr require.ErrorAssertionFunc
|
|
expectedDB require.ValueAssertionFunc
|
|
}{
|
|
{
|
|
desc: "complete server",
|
|
server: &armsql.Server{
|
|
ID: to.Ptr("/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/my-resource-groupd/providers/Microsoft.Sql/servers/sqlserver"),
|
|
Name: to.Ptr("sqlserver"),
|
|
Location: to.Ptr("westus"),
|
|
Properties: &armsql.ServerProperties{
|
|
FullyQualifiedDomainName: to.Ptr("sqlserver.database.windows.net"),
|
|
Version: to.Ptr("12.0"),
|
|
},
|
|
},
|
|
expectedErr: require.NoError,
|
|
expectedDB: func(t require.TestingT, i interface{}, _ ...interface{}) {
|
|
db, ok := i.(types.Database)
|
|
require.True(t, ok, "expected types.Database, got %T", i)
|
|
|
|
require.Equal(t, db.GetProtocol(), defaults.ProtocolSQLServer)
|
|
require.Equal(t, "sqlserver", db.GetName())
|
|
require.Equal(t, "sqlserver.database.windows.net:1433", db.GetURI())
|
|
require.Equal(t, "sqlserver", db.GetAzure().Name)
|
|
require.Equal(t, "/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/my-resource-groupd/providers/Microsoft.Sql/servers/sqlserver", db.GetAzure().ResourceID)
|
|
|
|
// Assert labels
|
|
labels := db.GetMetadata().Labels
|
|
require.Equal(t, types.OriginCloud, labels[types.OriginLabel])
|
|
require.Equal(t, "westus", labels[labelRegion])
|
|
require.Equal(t, "12.0", labels[labelEngineVersion])
|
|
},
|
|
},
|
|
{
|
|
desc: "empty properties",
|
|
server: &armsql.Server{Properties: nil},
|
|
expectedErr: require.Error,
|
|
expectedDB: require.Nil,
|
|
},
|
|
{
|
|
desc: "empty FQDN",
|
|
server: &armsql.Server{Properties: &armsql.ServerProperties{FullyQualifiedDomainName: nil}},
|
|
expectedErr: require.Error,
|
|
expectedDB: require.Nil,
|
|
},
|
|
} {
|
|
t.Run(tc.desc, func(t *testing.T) {
|
|
database, err := NewDatabaseFromAzureSQLServer(tc.server)
|
|
tc.expectedErr(t, err)
|
|
tc.expectedDB(t, database)
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestNewDatabaseFromAzureManagedSQLServer(t *testing.T) {
|
|
for _, tc := range []struct {
|
|
desc string
|
|
server *armsql.ManagedInstance
|
|
expectedErr require.ErrorAssertionFunc
|
|
expectedDB require.ValueAssertionFunc
|
|
}{
|
|
{
|
|
desc: "complete server",
|
|
server: &armsql.ManagedInstance{
|
|
ID: to.Ptr("/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/my-resource-groupd/providers/Microsoft.Sql/servers/sqlserver"),
|
|
Name: to.Ptr("sqlserver"),
|
|
Location: to.Ptr("westus"),
|
|
Properties: &armsql.ManagedInstanceProperties{
|
|
FullyQualifiedDomainName: to.Ptr("sqlserver.database.windows.net"),
|
|
},
|
|
},
|
|
expectedErr: require.NoError,
|
|
expectedDB: func(t require.TestingT, i interface{}, _ ...interface{}) {
|
|
db, ok := i.(types.Database)
|
|
require.True(t, ok, "expected types.Database, got %T", i)
|
|
|
|
require.Equal(t, db.GetProtocol(), defaults.ProtocolSQLServer)
|
|
require.Equal(t, "sqlserver", db.GetName())
|
|
require.Equal(t, "sqlserver.database.windows.net:1433", db.GetURI())
|
|
require.Equal(t, "sqlserver", db.GetAzure().Name)
|
|
require.Equal(t, "/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/my-resource-groupd/providers/Microsoft.Sql/servers/sqlserver", db.GetAzure().ResourceID)
|
|
|
|
// Assert labels
|
|
labels := db.GetMetadata().Labels
|
|
require.Equal(t, types.OriginCloud, labels[types.OriginLabel])
|
|
require.Equal(t, "westus", labels[labelRegion])
|
|
},
|
|
},
|
|
{
|
|
desc: "empty properties",
|
|
server: &armsql.ManagedInstance{Properties: nil},
|
|
expectedErr: require.Error,
|
|
expectedDB: require.Nil,
|
|
},
|
|
{
|
|
desc: "empty FQDN",
|
|
server: &armsql.ManagedInstance{Properties: &armsql.ManagedInstanceProperties{FullyQualifiedDomainName: nil}},
|
|
expectedErr: require.Error,
|
|
expectedDB: require.Nil,
|
|
},
|
|
} {
|
|
t.Run(tc.desc, func(t *testing.T) {
|
|
database, err := NewDatabaseFromAzureManagedSQLServer(tc.server)
|
|
tc.expectedErr(t, err)
|
|
tc.expectedDB(t, database)
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestDatabaseFromAzureMySQLFlexServer(t *testing.T) {
|
|
t.Parallel()
|
|
subID := "sub123"
|
|
group := "test-group"
|
|
region := "eastus"
|
|
provider := AzureEngineMySQLFlex
|
|
|
|
tests := []struct {
|
|
desc string
|
|
serverName string
|
|
replicationRole armmysqlflexibleservers.ReplicationRole
|
|
sourceServerID *string
|
|
wantReplicationRoleLabel string
|
|
wantSourceServerLabel string
|
|
wantDBDesc string
|
|
}{
|
|
{
|
|
desc: "server without replication",
|
|
serverName: "azure-mysql-flex",
|
|
replicationRole: armmysqlflexibleservers.ReplicationRoleNone,
|
|
wantDBDesc: "Azure MySQL Flexible server in eastus",
|
|
},
|
|
{
|
|
desc: "source server",
|
|
serverName: "azure-mysql-flex-source",
|
|
replicationRole: armmysqlflexibleservers.ReplicationRoleSource,
|
|
wantReplicationRoleLabel: "Source",
|
|
wantDBDesc: "Azure MySQL Flexible server in eastus (source endpoint)",
|
|
},
|
|
{
|
|
desc: "replica server",
|
|
serverName: "azure-mysql-flex-replica",
|
|
replicationRole: armmysqlflexibleservers.ReplicationRoleReplica,
|
|
sourceServerID: to.Ptr(makeAzureResourceID(subID, group, provider, "azure-mysql-flex-source")),
|
|
wantReplicationRoleLabel: "Replica",
|
|
wantSourceServerLabel: "azure-mysql-flex-source",
|
|
wantDBDesc: "Azure MySQL Flexible server in eastus (replica endpoint)",
|
|
},
|
|
}
|
|
|
|
for _, tt := range tests {
|
|
tt := tt
|
|
t.Run(tt.desc, func(t *testing.T) {
|
|
t.Parallel()
|
|
rid := makeAzureResourceID(subID, group, provider, tt.serverName)
|
|
server := armmysqlflexibleservers.Server{
|
|
Name: to.Ptr(tt.serverName),
|
|
ID: to.Ptr(rid),
|
|
Location: to.Ptr(region),
|
|
Tags: map[string]*string{
|
|
"foo": to.Ptr("bar"),
|
|
},
|
|
Properties: &armmysqlflexibleservers.ServerProperties{
|
|
FullyQualifiedDomainName: to.Ptr(tt.serverName + ".mysql" + azureutils.DatabaseEndpointSuffix),
|
|
State: to.Ptr(armmysqlflexibleservers.ServerStateReady),
|
|
Version: to.Ptr(armmysqlflexibleservers.ServerVersionEight021),
|
|
ReplicationRole: &tt.replicationRole,
|
|
SourceServerResourceID: tt.sourceServerID,
|
|
},
|
|
}
|
|
|
|
wantLabels := map[string]string{
|
|
types.OriginLabel: types.OriginCloud,
|
|
labelRegion: region,
|
|
labelEngine: provider,
|
|
labelEngineVersion: "8.0.21",
|
|
labelResourceGroup: group,
|
|
labelSubscriptionID: subID,
|
|
"foo": "bar",
|
|
}
|
|
if tt.wantReplicationRoleLabel != "" {
|
|
wantLabels[labelReplicationRole] = tt.wantReplicationRoleLabel
|
|
}
|
|
if tt.wantSourceServerLabel != "" {
|
|
wantLabels[labelSourceServer] = tt.wantSourceServerLabel
|
|
}
|
|
wantDB, err := types.NewDatabaseV3(types.Metadata{
|
|
Name: tt.serverName,
|
|
Description: tt.wantDBDesc,
|
|
Labels: wantLabels,
|
|
}, types.DatabaseSpecV3{
|
|
Protocol: defaults.ProtocolMySQL,
|
|
URI: tt.serverName + ".mysql.database.azure.com:3306",
|
|
Azure: types.Azure{
|
|
Name: tt.serverName,
|
|
ResourceID: rid,
|
|
IsFlexiServer: true,
|
|
},
|
|
})
|
|
require.NoError(t, err)
|
|
|
|
actual, err := NewDatabaseFromAzureMySQLFlexServer(&server)
|
|
require.NoError(t, err)
|
|
require.Equal(t, wantDB, actual)
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestDatabaseFromAzurePostgresFlexServer(t *testing.T) {
|
|
t.Parallel()
|
|
subID := "sub123"
|
|
group := "test-group"
|
|
region := "eastus"
|
|
provider := AzureEnginePostgresFlex
|
|
|
|
tests := []struct {
|
|
desc string
|
|
serverName string
|
|
wantDBDesc string
|
|
}{
|
|
{
|
|
desc: "server without replication",
|
|
serverName: "azure-postgres-flex",
|
|
wantDBDesc: "Azure PostgreSQL Flexible server in eastus",
|
|
},
|
|
// TODO(gavin): add more tests if replication is done somehow in postgres. it's different than the azure mysql flex setup for some reason.
|
|
}
|
|
|
|
for _, tt := range tests {
|
|
tt := tt
|
|
t.Run(tt.desc, func(t *testing.T) {
|
|
t.Parallel()
|
|
rid := makeAzureResourceID(subID, group, provider, tt.serverName)
|
|
server := armpostgresqlflexibleservers.Server{
|
|
Name: to.Ptr(tt.serverName),
|
|
ID: to.Ptr(rid),
|
|
Location: to.Ptr(region),
|
|
Tags: map[string]*string{
|
|
"foo": to.Ptr("bar"),
|
|
},
|
|
Properties: &armpostgresqlflexibleservers.ServerProperties{
|
|
FullyQualifiedDomainName: to.Ptr(tt.serverName + ".postgres" + azureutils.DatabaseEndpointSuffix),
|
|
State: to.Ptr(armpostgresqlflexibleservers.ServerStateReady),
|
|
Version: to.Ptr(armpostgresqlflexibleservers.ServerVersionFourteen),
|
|
},
|
|
}
|
|
|
|
wantLabels := map[string]string{
|
|
types.OriginLabel: types.OriginCloud,
|
|
labelRegion: region,
|
|
labelEngine: provider,
|
|
labelEngineVersion: "14",
|
|
labelResourceGroup: group,
|
|
labelSubscriptionID: subID,
|
|
"foo": "bar",
|
|
}
|
|
wantDB, err := types.NewDatabaseV3(types.Metadata{
|
|
Name: tt.serverName,
|
|
Description: tt.wantDBDesc,
|
|
Labels: wantLabels,
|
|
}, types.DatabaseSpecV3{
|
|
Protocol: defaults.ProtocolPostgres,
|
|
URI: tt.serverName + ".postgres.database.azure.com:5432",
|
|
Azure: types.Azure{
|
|
Name: tt.serverName,
|
|
ResourceID: rid,
|
|
IsFlexiServer: true,
|
|
},
|
|
})
|
|
require.NoError(t, err)
|
|
|
|
actual, err := NewDatabaseFromAzurePostgresFlexServer(&server)
|
|
require.NoError(t, err)
|
|
require.Equal(t, wantDB, actual)
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestMakeAzureDatabaseLoginUsername(t *testing.T) {
|
|
t.Parallel()
|
|
subID := "sub123"
|
|
group := "group"
|
|
serverName := "test-server"
|
|
tests := []struct {
|
|
desc string
|
|
protocol string
|
|
engine string
|
|
staticIsFlex bool
|
|
wantIsFlex bool
|
|
}{
|
|
{
|
|
desc: "mysql flex",
|
|
protocol: defaults.ProtocolMySQL,
|
|
engine: AzureEngineMySQLFlex,
|
|
wantIsFlex: true,
|
|
},
|
|
{
|
|
desc: "postgres flex",
|
|
protocol: defaults.ProtocolPostgres,
|
|
engine: AzureEnginePostgresFlex,
|
|
wantIsFlex: true,
|
|
},
|
|
{
|
|
desc: "static config mysql flex",
|
|
protocol: defaults.ProtocolMySQL,
|
|
staticIsFlex: true,
|
|
wantIsFlex: true,
|
|
},
|
|
{
|
|
desc: "static config postgres flex",
|
|
protocol: defaults.ProtocolPostgres,
|
|
staticIsFlex: true,
|
|
wantIsFlex: true,
|
|
},
|
|
{
|
|
desc: "mysql single server",
|
|
protocol: defaults.ProtocolMySQL,
|
|
engine: AzureEngineMySQL,
|
|
wantIsFlex: false,
|
|
},
|
|
{
|
|
desc: "postgres single server",
|
|
protocol: defaults.ProtocolPostgres,
|
|
engine: AzureEnginePostgres,
|
|
wantIsFlex: false,
|
|
},
|
|
{
|
|
desc: "mysql non-azure",
|
|
protocol: defaults.ProtocolMySQL,
|
|
wantIsFlex: false,
|
|
},
|
|
{
|
|
desc: "postgres non-azure",
|
|
protocol: defaults.ProtocolPostgres,
|
|
wantIsFlex: false,
|
|
},
|
|
}
|
|
for _, tt := range tests {
|
|
tt := tt
|
|
t.Run(tt.desc, func(t *testing.T) {
|
|
t.Parallel()
|
|
db, err := types.NewDatabaseV3(types.Metadata{
|
|
Name: serverName,
|
|
Description: "test azure db server",
|
|
Labels: map[string]string{
|
|
types.OriginLabel: types.OriginCloud,
|
|
labelRegion: "eastus",
|
|
labelEngine: tt.engine,
|
|
labelEngineVersion: "1.2.3",
|
|
labelResourceGroup: group,
|
|
labelSubscriptionID: subID,
|
|
"foo": "bar",
|
|
},
|
|
}, types.DatabaseSpecV3{
|
|
Protocol: tt.protocol,
|
|
URI: "example.com:1234",
|
|
Azure: types.Azure{
|
|
Name: serverName,
|
|
ResourceID: makeAzureResourceID(subID, group, tt.engine, serverName),
|
|
IsFlexiServer: tt.staticIsFlex,
|
|
},
|
|
})
|
|
require.NoError(t, err)
|
|
require.Equal(t, tt.wantIsFlex, IsAzureFlexServer(db))
|
|
|
|
user := MakeAzureDatabaseLoginUsername(db, "alice")
|
|
if tt.wantIsFlex {
|
|
require.Equal(t, "alice", user)
|
|
} else {
|
|
require.Equal(t, "alice@test-server", user)
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func makeAzureResourceID(subID, group, provider, resourceName string) string {
|
|
return fmt.Sprintf("/subscriptions/%v/resourceGroups/%v/providers/%v/%v",
|
|
subID, group, provider, resourceName)
|
|
}
|