diff --git a/api/types/constants.go b/api/types/constants.go index bdf420986d45a..342905ead4b26 100644 --- a/api/types/constants.go +++ b/api/types/constants.go @@ -516,6 +516,32 @@ const ( // discovered databases. DatabaseAdminLabel = TeleportNamespace + "/db-admin" + // cloudKubeClusterNameOverrideLabel is a cloud agnostic label key for + // overriding kubernetes cluster name in discovered cloud kube clusters. + // It's used for AWS, GCP, and Azure, but not exported to decouple the + // cloud-specific labels from eachother. + cloudKubeClusterNameOverrideLabel = "TeleportKubernetesName" + + // cloudDatabaseNameOverrideLabel is a cloud agnostic label key for + // overriding the database name in discovered cloud databases. + // It's used for AWS, GCP, and Azure, but not exported to decouple the + // cloud-specific labels from eachother. + cloudDatabaseNameOverrideLabel = "TeleportDatabaseName" + + // AzureDatabaseNameOverrideLabel is the label key containing the database + // name override for discovered Azure databases. + // Azure tags cannot contain these characters: "<>%&\?/", so it doesn't + // start with the namespace prefix. + AzureDatabaseNameOverrideLabel = cloudDatabaseNameOverrideLabel + + // AzureKubeClusterNameOverrideLabel is the label key containing the + // kubernetes cluster name override for discovered Azure kube clusters. + AzureKubeClusterNameOverrideLabel = cloudKubeClusterNameOverrideLabel + + // GCPKubeClusterNameOverrideLabel is the label key containing the + // kubernetes cluster name override for discovered GCP kube clusters. + GCPKubeClusterNameOverrideLabel = cloudKubeClusterNameOverrideLabel + // ReqAnnotationSchedulesLabel is the request annotation key at which schedules are stored for access plugins. ReqAnnotationSchedulesLabel = "/schedules" @@ -530,6 +556,97 @@ const ( TeleportAzureMSIEndpoint = "azure-msi." + TeleportNamespace ) +var ( + // AWSKubeClusterNameOverrideLabels are the label keys that Teleport + // supports to override the kubernetes cluster name of discovered AWS kube + // clusters. + // Originally Teleport supported just the namespaced label + // "teleport.dev/kubernetes-name", but this was an invalid label key in + // other clouds. + // For consistency and backwards compatibility, Teleport now supports both + // the generic cloud kube cluster name override label and the original + // namespaced label. + AWSKubeClusterNameOverrideLabels = []string{ + cloudKubeClusterNameOverrideLabel, + // This is a legacy label that should continue to be supported, but + // don't reference it in documentation or error messages anymore. + // The generic label takes precedence. + TeleportNamespace + "/kubernetes-name", + } + // AWSDatabaseNameOverrideLabels are the label keys that Teleport + // supports to override the database name of discovered AWS databases. + // Originally Teleport supported just the namespaced label + // "teleport.dev/database_name", but this was an invalid label key in + // other clouds. + // For consistency and backwards compatibility, Teleport now supports both + // the generic cloud database name override label and the original + // namespaced label. + AWSDatabaseNameOverrideLabels = []string{ + cloudDatabaseNameOverrideLabel, + // This is a legacy label that should continue to be supported, but + // don't reference it in documentation or error messages anymore. + // The generic label takes precedence. + TeleportNamespace + "/database_name", + } +) + +// Labels added by the discovery service to discovered databases, +// Kubernetes clusters, and Windows desktops. +const ( + // DiscoveryLabelRegion identifies a discovered cloud resource's region. + DiscoveryLabelRegion = "region" + // DiscoveryLabelAccountID is the label key containing AWS account ID. + DiscoveryLabelAccountID = "account-id" + // DiscoveryLabelEngine is the label key containing database engine name. + DiscoveryLabelEngine = "engine" + // DiscoveryLabelEngineVersion is the label key containing database engine version. + DiscoveryLabelEngineVersion = "engine-version" + // DiscoveryLabelEndpointType is the label key containing the endpoint type. + DiscoveryLabelEndpointType = "endpoint-type" + // DiscoveryLabelVPCID is the label key containing the VPC ID. + DiscoveryLabelVPCID = "vpc-id" + // DiscoveryLabelNamespace is the label key for namespace name. + DiscoveryLabelNamespace = "namespace" + // DiscoveryLabelWorkgroup is the label key for workgroup name. + DiscoveryLabelWorkgroup = "workgroup" + // DiscoveryLabelStatus is the label key containing the database status, e.g. "available" + DiscoveryLabelStatus = "status" + + // DiscoveryLabelAzureSubscriptionID is the label key for Azure subscription ID. + DiscoveryLabelAzureSubscriptionID = "subscription-id" + // DiscoveryLabelAzureResourceGroup is the label key for the Azure resource group name. + DiscoveryLabelAzureResourceGroup = "resource-group" + // DiscoveryLabelAzureReplicationRole is the replication role of an Azure DB Flexible server, e.g. "Source" or "Replica". + DiscoveryLabelAzureReplicationRole = "replication-role" + // DiscoveryLabelAzureSourceServer is the source server for replica Azure DB Flexible servers. + // This is the source (primary) database resource name. + DiscoveryLabelAzureSourceServer = "source-server" + + // DiscoveryLabelGCPProjectID is the label key for GCP project ID. + DiscoveryLabelGCPProjectID = "project-id" + // DiscoveryLabelGCPLocation is the label key for GCP location. + DiscoveryLabelGCPLocation = "location" + + // DiscoveryLabelWindowsDNSHostName is the DNS hostname of an LDAP object. + DiscoveryLabelWindowsDNSHostName = TeleportNamespace + "/dns_host_name" + //DiscoveryLabelWindowsComputerName is the name of an LDAP object. + DiscoveryLabelWindowsComputerName = TeleportNamespace + "/computer_name" + //DiscoveryLabelWindowsOS is the operating system of an LDAP object. + DiscoveryLabelWindowsOS = TeleportNamespace + "/os" + //DiscoveryLabelWindowsOSVersion operating system version of an LDAP object. + DiscoveryLabelWindowsOSVersion = TeleportNamespace + "/os_version" + //DiscoveryLabelWindowsOU is an LDAP objects's OU. + DiscoveryLabelWindowsOU = TeleportNamespace + "/ou" + //DiscoveryLabelWindowsIsDomainController is whether an LDAP object is a + // domain controller. + DiscoveryLabelWindowsIsDomainController = TeleportNamespace + "/is_domain_controller" + //DiscoveryLabelWindowsDomain is an Active Directory domain name. + DiscoveryLabelWindowsDomain = TeleportNamespace + "/windows_domain" + // DiscoveryLabelLDAPPrefix is the prefix used when applying any custom + // labels per the discovery LDAP attribute labels configuration. + DiscoveryLabelLDAPPrefix = "ldap/" +) + const ( // TeleportInternalLabelPrefix is the prefix used by all Teleport internal labels. Those labels // are automatically populated by Teleport and are expected to be used by Teleport internal diff --git a/docs/pages/database-access/guides/postgres-redshift.mdx b/docs/pages/database-access/guides/postgres-redshift.mdx index c6b05534b1b0a..b9e5ad7f0c4eb 100644 --- a/docs/pages/database-access/guides/postgres-redshift.mdx +++ b/docs/pages/database-access/guides/postgres-redshift.mdx @@ -128,7 +128,7 @@ $ tsh db ls - You can override the database name by applying the `teleport.dev/database_name` AWS tag to the resource. The value of the tag will be used as the database name. + You can override the database name by applying the `TeleportDatabaseName` AWS tag to the resource. The value of the tag will be used as the database name. To retrieve credentials for a database and connect to it: diff --git a/docs/pages/database-access/guides/rds.mdx b/docs/pages/database-access/guides/rds.mdx index f98d1ded9bce7..f01e893096ef3 100644 --- a/docs/pages/database-access/guides/rds.mdx +++ b/docs/pages/database-access/guides/rds.mdx @@ -179,7 +179,7 @@ $ tsh db ls ``, `-reader`, and `-custom-` respectively. - You can override the `` part of the name with `teleport.dev/database_name` AWS tag. + You can override the `` part of the name with `TeleportDatabaseName` AWS tag. To retrieve credentials for a database and connect to it: diff --git a/docs/pages/database-access/guides/redis-aws.mdx b/docs/pages/database-access/guides/redis-aws.mdx index 20d3bdd87ed24..59ee67039a3d9 100644 --- a/docs/pages/database-access/guides/redis-aws.mdx +++ b/docs/pages/database-access/guides/redis-aws.mdx @@ -235,7 +235,7 @@ $ tsh db ls - You can override the database name by applying the `teleport.dev/database_name` AWS tag to the resource. The value of the tag will be used as the database name. + You can override the database name by applying the `TeleportDatabaseName` AWS tag to the resource. The value of the tag will be used as the database name. To retrieve credentials for a database and connect to it: diff --git a/docs/pages/database-access/guides/redshift-serverless.mdx b/docs/pages/database-access/guides/redshift-serverless.mdx index 1d9fca3c337a6..7be008f9cde7b 100644 --- a/docs/pages/database-access/guides/redshift-serverless.mdx +++ b/docs/pages/database-access/guides/redshift-serverless.mdx @@ -300,7 +300,7 @@ my-redshift Redshift cluster in us-east-1 ... -You can override the database name by applying the `teleport.dev/database_name` +You can override the database name by applying the `TeleportDatabaseName` AWS tag to the resource. The value of the tag will be used as the database name. diff --git a/docs/pages/kubernetes-access/discovery.mdx b/docs/pages/kubernetes-access/discovery.mdx index fd69db551a963..b80be501c287f 100644 --- a/docs/pages/kubernetes-access/discovery.mdx +++ b/docs/pages/kubernetes-access/discovery.mdx @@ -38,8 +38,8 @@ cloud provider such as: You can import the cluster under a different name into Teleport's registry. -To achieve this, you must attach the following tag to the resources — EKS and AKS — in your cloud provider: - - ***key***: `teleport.dev/kubernetes-name` +To achieve this, you must attach the following tag to the resources — EKS, AKS, GKE — in your cloud provider: + - ***key***: `TeleportKubernetesName` - ***value***: desired name The Discovery Service will check if the cluster includes the tag and use its value diff --git a/lib/backend/kubernetes/kubernetes.go b/lib/backend/kubernetes/kubernetes.go index 7b6074c3a2ce9..711594ac71ee0 100644 --- a/lib/backend/kubernetes/kubernetes.go +++ b/lib/backend/kubernetes/kubernetes.go @@ -159,7 +159,7 @@ func NewShared() (*Backend, error) { return NewSharedWithClient(restClient) } -// NewSharedWithClient returns a new instance of the shared kuberenetes secret store with the provided client (equivalent +// NewSharedWithClient returns a new instance of the shared kubernetes secret store with the provided client (equivalent // to NewWithClient() except that this backend can be written to by any teleport agent within the helm release. used for propagating // relevant state to controllers). func NewSharedWithClient(restClient kubernetes.Interface) (*Backend, error) { diff --git a/lib/services/database.go b/lib/services/database.go index cef7b755ac772..6f9dcfd1319ca 100644 --- a/lib/services/database.go +++ b/lib/services/database.go @@ -277,30 +277,16 @@ func isDNSError(err error) bool { return errors.As(err, &dnsErr) } -// setDBNameByLabel modifies the types.Metadata argument in place, setting the database name. -// The name is calculated based on nameParts arguments which are joined by hyphens "-". -// If the DB name override label is present, it will replace the *first* name part. -func setDBNameByLabel(overrideLabel string, meta types.Metadata, firstNamePart string, extraNameParts ...string) types.Metadata { - nameParts := append([]string{firstNamePart}, extraNameParts...) - - // apply override - if override, found := meta.Labels[overrideLabel]; found && override != "" { - nameParts[0] = override - } - - meta.Name = strings.Join(nameParts, "-") - - return meta -} - -// setDBName sets database name if override label labelTeleportDBName is present. -func setDBName(meta types.Metadata, firstNamePart string, extraNameParts ...string) types.Metadata { - return setDBNameByLabel(labelTeleportDBName, meta, firstNamePart, extraNameParts...) +// setAWSDBName sets database name, overriding the first part if the database +// override label for AWS is present. +func setAWSDBName(meta types.Metadata, firstNamePart string, extraNameParts ...string) types.Metadata { + return setResourceName(types.AWSDatabaseNameOverrideLabels, meta, firstNamePart, extraNameParts...) } -// setDBName sets database name if override label labelTeleportDBNameAzure is present. +// setDBName sets database name, overriding the first part if the Azure database +// override label for Azure is present. func setAzureDBName(meta types.Metadata, firstNamePart string, extraNameParts ...string) types.Metadata { - return setDBNameByLabel(labelTeleportDBNameAzure, meta, firstNamePart, extraNameParts...) + return setResourceName([]string{types.AzureDatabaseNameOverrideLabel}, meta, firstNamePart, extraNameParts...) } // NewDatabaseFromAzureServer creates a database resource from an AzureDB server. @@ -474,7 +460,7 @@ func NewDatabaseFromAzureMySQLFlexServer(server *armmysqlflexibleservers.Server) } var description string - if replicaRole, ok := labels[labelReplicationRole]; ok { + if replicaRole, ok := labels[types.DiscoveryLabelAzureReplicationRole]; ok { description = fmt.Sprintf("Azure MySQL Flexible server in %v (%v endpoint)", azure.StringVal(server.Location), strings.ToLower(replicaRole)) } else { @@ -544,7 +530,7 @@ func NewDatabaseFromRDSInstance(instance *rds.DBInstance) (types.Database, error } return types.NewDatabaseV3( - setDBName(types.Metadata{ + setAWSDBName(types.Metadata{ Description: fmt.Sprintf("RDS instance in %v", metadata.Region), Labels: labelsFromRDSInstance(instance, metadata), }, aws.StringValue(instance.DBInstanceIdentifier)), @@ -577,7 +563,7 @@ func NewDatabaseFromRDSV2Instance(instance *rdsTypesV2.DBInstance) (types.Databa } return types.NewDatabaseV3( - setDBName(types.Metadata{ + setAWSDBName(types.Metadata{ Description: fmt.Sprintf("RDS instance in %v", metadata.Region), Labels: labelsFromRDSV2Instance(instance, metadata), }, aws.StringValue(instance.DBInstanceIdentifier)), @@ -624,10 +610,10 @@ func MetadataFromRDSV2Instance(rdsInstance *rdsTypesV2.DBInstance) (*types.AWS, // It uses aws sdk v2. func labelsFromRDSV2Instance(rdsInstance *rdsTypesV2.DBInstance, meta *types.AWS) map[string]string { labels := labelsFromAWSMetadata(meta) - labels[labelEngine] = aws.StringValue(rdsInstance.Engine) - labels[labelEngineVersion] = aws.StringValue(rdsInstance.EngineVersion) - labels[labelEndpointType] = string(RDSEndpointTypeInstance) - labels[labelStatus] = aws.StringValue(rdsInstance.DBInstanceStatus) + labels[types.DiscoveryLabelEngine] = aws.StringValue(rdsInstance.Engine) + labels[types.DiscoveryLabelEngineVersion] = aws.StringValue(rdsInstance.EngineVersion) + labels[types.DiscoveryLabelEndpointType] = string(RDSEndpointTypeInstance) + labels[types.DiscoveryLabelStatus] = aws.StringValue(rdsInstance.DBInstanceStatus) return addLabels(labels, libcloudaws.TagsToLabels(rdsInstance.TagList)) } @@ -648,7 +634,7 @@ func NewDatabaseFromRDSV2Cluster(cluster *rdsTypesV2.DBCluster) (types.Database, uri = fmt.Sprintf("%v:%v", aws.StringValue(cluster.Endpoint), *cluster.Port) } return types.NewDatabaseV3( - setDBName(types.Metadata{ + setAWSDBName(types.Metadata{ Description: fmt.Sprintf("Aurora cluster in %v", metadata.Region), Labels: labelsFromRDSV2Cluster(cluster, metadata, RDSEndpointTypePrimary), }, aws.StringValue(cluster.DBClusterIdentifier)), @@ -681,10 +667,10 @@ func MetadataFromRDSV2Cluster(rdsCluster *rdsTypesV2.DBCluster) (*types.AWS, err // It uses aws sdk v2. func labelsFromRDSV2Cluster(rdsCluster *rdsTypesV2.DBCluster, meta *types.AWS, endpointType RDSEndpointType) map[string]string { labels := labelsFromAWSMetadata(meta) - labels[labelEngine] = aws.StringValue(rdsCluster.Engine) - labels[labelEngineVersion] = aws.StringValue(rdsCluster.EngineVersion) - labels[labelEndpointType] = string(endpointType) - labels[labelStatus] = aws.StringValue(rdsCluster.Status) + labels[types.DiscoveryLabelEngine] = aws.StringValue(rdsCluster.Engine) + labels[types.DiscoveryLabelEngineVersion] = aws.StringValue(rdsCluster.EngineVersion) + labels[types.DiscoveryLabelEndpointType] = string(endpointType) + labels[types.DiscoveryLabelStatus] = aws.StringValue(rdsCluster.Status) return addLabels(labels, libcloudaws.TagsToLabels(rdsCluster.TagList)) } @@ -699,7 +685,7 @@ func NewDatabaseFromRDSCluster(cluster *rds.DBCluster) (types.Database, error) { return nil, trace.Wrap(err) } return types.NewDatabaseV3( - setDBName(types.Metadata{ + setAWSDBName(types.Metadata{ Description: fmt.Sprintf("Aurora cluster in %v", metadata.Region), Labels: labelsFromRDSCluster(cluster, metadata, RDSEndpointTypePrimary), }, aws.StringValue(cluster.DBClusterIdentifier)), @@ -721,7 +707,7 @@ func NewDatabaseFromRDSClusterReaderEndpoint(cluster *rds.DBCluster) (types.Data return nil, trace.Wrap(err) } return types.NewDatabaseV3( - setDBName(types.Metadata{ + setAWSDBName(types.Metadata{ Description: fmt.Sprintf("Aurora cluster in %v (%v endpoint)", metadata.Region, string(RDSEndpointTypeReader)), Labels: labelsFromRDSCluster(cluster, metadata, RDSEndpointTypeReader), }, aws.StringValue(cluster.DBClusterIdentifier), string(RDSEndpointTypeReader)), @@ -759,7 +745,7 @@ func NewDatabasesFromRDSClusterCustomEndpoints(cluster *rds.DBCluster) (types.Da } database, err := types.NewDatabaseV3( - setDBName(types.Metadata{ + setAWSDBName(types.Metadata{ Description: fmt.Sprintf("Aurora cluster in %v (%v endpoint)", metadata.Region, string(RDSEndpointTypeCustom)), Labels: labelsFromRDSCluster(cluster, metadata, RDSEndpointTypeCustom), }, aws.StringValue(cluster.DBClusterIdentifier), string(RDSEndpointTypeCustom), endpointDetails.ClusterCustomEndpointName), @@ -796,7 +782,7 @@ func NewDatabaseFromRDSProxy(dbProxy *rds.DBProxy, port int64, tags []*rds.Tag) return nil, trace.Wrap(err) } return types.NewDatabaseV3( - setDBName(types.Metadata{ + setAWSDBName(types.Metadata{ Description: fmt.Sprintf("RDS Proxy in %v", metadata.Region), Labels: labelsFromRDSProxy(dbProxy, metadata, tags), }, aws.StringValue(dbProxy.DBProxyName)), @@ -819,7 +805,7 @@ func NewDatabaseFromRDSProxyCustomEndpoint(dbProxy *rds.DBProxy, customEndpoint return nil, trace.Wrap(err) } return types.NewDatabaseV3( - setDBName(types.Metadata{ + setAWSDBName(types.Metadata{ Description: fmt.Sprintf("RDS Proxy endpoint in %v", metadata.Region), Labels: labelsFromRDSProxyCustomEndpoint(dbProxy, customEndpoint, metadata, tags), }, aws.StringValue(dbProxy.DBProxyName), aws.StringValue(customEndpoint.DBProxyEndpointName)), @@ -857,7 +843,7 @@ func NewDatabaseFromRedshiftCluster(cluster *redshift.Cluster) (types.Database, } return types.NewDatabaseV3( - setDBName(types.Metadata{ + setAWSDBName(types.Metadata{ Description: fmt.Sprintf("Redshift cluster in %v", metadata.Region), Labels: labelsFromRedshiftCluster(cluster, metadata), }, aws.StringValue(cluster.ClusterIdentifier)), @@ -914,7 +900,7 @@ func newElastiCacheDatabase(cluster *elasticache.ReplicationGroup, endpoint *ela suffix = []string{endpointType} } - return types.NewDatabaseV3(setDBName(types.Metadata{ + return types.NewDatabaseV3(setAWSDBName(types.Metadata{ Description: fmt.Sprintf("ElastiCache cluster in %v (%v endpoint)", metadata.Region, endpointType), Labels: labelsFromMetaAndEndpointType(metadata, endpointType, extraLabels), }, aws.StringValue(cluster.ReplicationGroupId), suffix...), types.DatabaseSpecV3{ @@ -939,7 +925,7 @@ func NewDatabaseFromOpenSearchDomain(domain *opensearchservice.DomainStatus, tag Labels: labelsFromOpenSearchDomain(domain, metadata, apiawsutils.OpenSearchDefaultEndpoint, tags), } - meta = setDBName(meta, aws.StringValue(domain.DomainName)) + meta = setAWSDBName(meta, aws.StringValue(domain.DomainName)) spec := types.DatabaseSpecV3{ Protocol: defaults.ProtocolOpenSearch, URI: fmt.Sprintf("%v:443", aws.StringValue(domain.Endpoint)), @@ -965,7 +951,7 @@ func NewDatabaseFromOpenSearchDomain(domain *opensearchservice.DomainStatus, tag Labels: labelsFromOpenSearchDomain(domain, metadata, apiawsutils.OpenSearchCustomEndpoint, tags), } - meta = setDBName(meta, aws.StringValue(domain.DomainName), "custom") + meta = setAWSDBName(meta, aws.StringValue(domain.DomainName), "custom") spec := types.DatabaseSpecV3{ Protocol: defaults.ProtocolOpenSearch, URI: fmt.Sprintf("%v:443", aws.StringValue(domain.DomainEndpointOptions.CustomEndpoint)), @@ -992,10 +978,10 @@ func NewDatabaseFromOpenSearchDomain(domain *opensearchservice.DomainStatus, tag } if domain.VPCOptions != nil { - meta.Labels[labelVPCID] = aws.StringValue(domain.VPCOptions.VPCId) + meta.Labels[types.DiscoveryLabelVPCID] = aws.StringValue(domain.VPCOptions.VPCId) } - meta = setDBName(meta, aws.StringValue(domain.DomainName), name) + meta = setAWSDBName(meta, aws.StringValue(domain.DomainName), name) spec := types.DatabaseSpecV3{ Protocol: defaults.ProtocolOpenSearch, URI: fmt.Sprintf("%v:443", aws.StringValue(url)), @@ -1024,7 +1010,7 @@ func NewDatabaseFromMemoryDBCluster(cluster *memorydb.Cluster, extraLabels map[s } return types.NewDatabaseV3( - setDBName(types.Metadata{ + setAWSDBName(types.Metadata{ Description: fmt.Sprintf("MemoryDB cluster in %v", metadata.Region), Labels: labelsFromMetaAndEndpointType(metadata, endpointType, extraLabels), }, aws.StringValue(cluster.Name)), @@ -1048,7 +1034,7 @@ func NewDatabaseFromRedshiftServerlessWorkgroup(workgroup *redshiftserverless.Wo } return types.NewDatabaseV3( - setDBName(types.Metadata{ + setAWSDBName(types.Metadata{ Description: fmt.Sprintf("Redshift Serverless workgroup in %v", metadata.Region), Labels: labelsFromRedshiftServerlessWorkgroup(workgroup, metadata, tags), }, metadata.RedshiftServerless.WorkgroupName), @@ -1072,7 +1058,7 @@ func NewDatabaseFromRedshiftServerlessVPCEndpoint(endpoint *redshiftserverless.E } return types.NewDatabaseV3( - setDBName(types.Metadata{ + setAWSDBName(types.Metadata{ Description: fmt.Sprintf("Redshift Serverless endpoint in %v", metadata.Region), Labels: labelsFromRedshiftServerlessVPCEndpoint(endpoint, workgroup, metadata, tags), }, metadata.RedshiftServerless.WorkgroupName, metadata.RedshiftServerless.EndpointName), @@ -1296,7 +1282,7 @@ func ExtraElastiCacheLabels(cluster *elasticache.ReplicationGroup, tags []*elast for _, node := range allNodes { if aws.StringValue(node.ReplicationGroupId) == replicationGroupID { subnetGroupName = aws.StringValue(node.CacheSubnetGroupName) - labels[labelEngineVersion] = aws.StringValue(node.EngineVersion) + labels[types.DiscoveryLabelEngineVersion] = aws.StringValue(node.EngineVersion) break } } @@ -1308,7 +1294,7 @@ func ExtraElastiCacheLabels(cluster *elasticache.ReplicationGroup, tags []*elast // for filtering. for _, subnetGroup := range allSubnetGroups { if aws.StringValue(subnetGroup.CacheSubnetGroupName) == subnetGroupName { - labels[labelVPCID] = aws.StringValue(subnetGroup.VpcId) + labels[types.DiscoveryLabelVPCID] = aws.StringValue(subnetGroup.VpcId) break } } @@ -1323,12 +1309,12 @@ func ExtraMemoryDBLabels(cluster *memorydb.Cluster, tags []*memorydb.Tag, allSub labels := make(map[string]string) // Engine version. - labels[labelEngineVersion] = aws.StringValue(cluster.EngineVersion) + labels[types.DiscoveryLabelEngineVersion] = aws.StringValue(cluster.EngineVersion) // VPC ID. for _, subnetGroup := range allSubnetGroups { if aws.StringValue(subnetGroup.Name) == aws.StringValue(cluster.SubnetGroupName) { - labels[labelVPCID] = aws.StringValue(subnetGroup.VpcId) + labels[types.DiscoveryLabelVPCID] = aws.StringValue(subnetGroup.VpcId) break } } @@ -1364,8 +1350,8 @@ func rdsEngineFamilyToProtocol(engineFamily string) (string, error) { // labelsFromAzureServer creates database labels for the provided Azure DB server. func labelsFromAzureServer(server *azure.DBServer) (map[string]string, error) { labels := azureTagsToLabels(server.Tags) - labels[labelRegion] = server.Location - labels[labelEngineVersion] = server.Properties.Version + labels[types.DiscoveryLabelRegion] = azureutils.NormalizeLocation(server.Location) + labels[types.DiscoveryLabelEngineVersion] = server.Properties.Version return withLabelsFromAzureResourceID(labels, server.ID) } @@ -1375,26 +1361,26 @@ func withLabelsFromAzureResourceID(labels map[string]string, resourceID string) if err != nil { return nil, trace.Wrap(err) } - labels[labelEngine] = rid.ResourceType.String() - labels[labelResourceGroup] = rid.ResourceGroupName - labels[labelSubscriptionID] = rid.SubscriptionID + labels[types.DiscoveryLabelEngine] = rid.ResourceType.String() + labels[types.DiscoveryLabelAzureResourceGroup] = rid.ResourceGroupName + labels[types.DiscoveryLabelAzureSubscriptionID] = rid.SubscriptionID return labels, nil } // labelsFromAzureRedis creates database labels from the provided Azure Redis instance. func labelsFromAzureRedis(server *armredis.ResourceInfo) (map[string]string, error) { labels := azureTagsToLabels(azure.ConvertTags(server.Tags)) - labels[labelRegion] = azure.StringVal(server.Location) - labels[labelEngineVersion] = azure.StringVal(server.Properties.RedisVersion) + labels[types.DiscoveryLabelRegion] = azureutils.NormalizeLocation(azure.StringVal(server.Location)) + labels[types.DiscoveryLabelEngineVersion] = azure.StringVal(server.Properties.RedisVersion) return withLabelsFromAzureResourceID(labels, azure.StringVal(server.ID)) } // labelsFromAzureRedisEnterprise creates database labels from the provided Azure Redis Enterprise server. func labelsFromAzureRedisEnterprise(cluster *armredisenterprise.Cluster, database *armredisenterprise.Database) (map[string]string, error) { labels := azureTagsToLabels(azure.ConvertTags(cluster.Tags)) - labels[labelRegion] = azure.StringVal(cluster.Location) - labels[labelEngineVersion] = azure.StringVal(cluster.Properties.RedisVersion) - labels[labelEndpointType] = azure.StringVal(database.Properties.ClusteringPolicy) + labels[types.DiscoveryLabelRegion] = azureutils.NormalizeLocation(azure.StringVal(cluster.Location)) + labels[types.DiscoveryLabelEngineVersion] = azure.StringVal(cluster.Properties.RedisVersion) + labels[types.DiscoveryLabelEndpointType] = azure.StringVal(database.Properties.ClusteringPolicy) return withLabelsFromAzureResourceID(labels, azure.StringVal(cluster.ID)) } @@ -1402,8 +1388,8 @@ func labelsFromAzureRedisEnterprise(cluster *armredisenterprise.Cluster, databas // server. func labelsFromAzureSQLServer(server *armsql.Server) (map[string]string, error) { labels := azureTagsToLabels(azure.ConvertTags(server.Tags)) - labels[labelRegion] = azure.StringVal(server.Location) - labels[labelEngineVersion] = azure.StringVal(server.Properties.Version) + labels[types.DiscoveryLabelRegion] = azureutils.NormalizeLocation(azure.StringVal(server.Location)) + labels[types.DiscoveryLabelEngineVersion] = azure.StringVal(server.Properties.Version) return withLabelsFromAzureResourceID(labels, azure.StringVal(server.ID)) } @@ -1411,29 +1397,29 @@ func labelsFromAzureSQLServer(server *armsql.Server) (map[string]string, error) // Azure Managed SQL server. func labelsFromAzureManagedSQLServer(server *armsql.ManagedInstance) (map[string]string, error) { labels := azureTagsToLabels(azure.ConvertTags(server.Tags)) - labels[labelRegion] = azure.StringVal(server.Location) + labels[types.DiscoveryLabelRegion] = azureutils.NormalizeLocation(azure.StringVal(server.Location)) return withLabelsFromAzureResourceID(labels, azure.StringVal(server.ID)) } // labelsFromAzureMySQLFlexServer creates database labels for the provided Azure MySQL flex server. func labelsFromAzureMySQLFlexServer(server *armmysqlflexibleservers.Server) (map[string]string, error) { labels := azureTagsToLabels(azure.ConvertTags(server.Tags)) - labels[labelRegion] = azure.StringVal(server.Location) - labels[labelEngineVersion] = azure.StringVal(server.Properties.Version) + labels[types.DiscoveryLabelRegion] = azureutils.NormalizeLocation(azure.StringVal(server.Location)) + labels[types.DiscoveryLabelEngineVersion] = azure.StringVal(server.Properties.Version) role := azure.StringVal(server.Properties.ReplicationRole) switch armmysqlflexibleservers.ReplicationRole(role) { case armmysqlflexibleservers.ReplicationRoleNone: // don't add a label if this server has 'None' replication. case armmysqlflexibleservers.ReplicationRoleSource: - labels[labelReplicationRole] = role + labels[types.DiscoveryLabelAzureReplicationRole] = role case armmysqlflexibleservers.ReplicationRoleReplica: - labels[labelReplicationRole] = role + labels[types.DiscoveryLabelAzureReplicationRole] = role ssrid, err := arm.ParseResourceID(azure.StringVal(server.Properties.SourceServerResourceID)) if err != nil { - log.WithError(err).Debugf("Skipping malformed %q label for Azure MySQL Flexible server replica.", labelSourceServer) + log.WithError(err).Debugf("Skipping malformed %q label for Azure MySQL Flexible server replica.", types.DiscoveryLabelAzureSourceServer) } else { - labels[labelSourceServer] = ssrid.Name + labels[types.DiscoveryLabelAzureSourceServer] = ssrid.Name } } return withLabelsFromAzureResourceID(labels, azure.StringVal(server.ID)) @@ -1442,26 +1428,26 @@ func labelsFromAzureMySQLFlexServer(server *armmysqlflexibleservers.Server) (map // labelsFromAzurePostgresFlexServer creates database labels for the provided Azure postgres flex server. func labelsFromAzurePostgresFlexServer(server *armpostgresqlflexibleservers.Server) (map[string]string, error) { labels := azureTagsToLabels(azure.ConvertTags(server.Tags)) - labels[labelRegion] = azure.StringVal(server.Location) - labels[labelEngineVersion] = azure.StringVal(server.Properties.Version) + labels[types.DiscoveryLabelRegion] = azureutils.NormalizeLocation(azure.StringVal(server.Location)) + labels[types.DiscoveryLabelEngineVersion] = azure.StringVal(server.Properties.Version) return withLabelsFromAzureResourceID(labels, azure.StringVal(server.ID)) } // labelsFromRDSInstance creates database labels for the provided RDS instance. func labelsFromRDSInstance(rdsInstance *rds.DBInstance, meta *types.AWS) map[string]string { labels := labelsFromAWSMetadata(meta) - labels[labelEngine] = aws.StringValue(rdsInstance.Engine) - labels[labelEngineVersion] = aws.StringValue(rdsInstance.EngineVersion) - labels[labelEndpointType] = string(RDSEndpointTypeInstance) + labels[types.DiscoveryLabelEngine] = aws.StringValue(rdsInstance.Engine) + labels[types.DiscoveryLabelEngineVersion] = aws.StringValue(rdsInstance.EngineVersion) + labels[types.DiscoveryLabelEndpointType] = string(RDSEndpointTypeInstance) return addLabels(labels, libcloudaws.TagsToLabels(rdsInstance.TagList)) } // labelsFromRDSCluster creates database labels for the provided RDS cluster. func labelsFromRDSCluster(rdsCluster *rds.DBCluster, meta *types.AWS, endpointType RDSEndpointType) map[string]string { labels := labelsFromAWSMetadata(meta) - labels[labelEngine] = aws.StringValue(rdsCluster.Engine) - labels[labelEngineVersion] = aws.StringValue(rdsCluster.EngineVersion) - labels[labelEndpointType] = string(endpointType) + labels[types.DiscoveryLabelEngine] = aws.StringValue(rdsCluster.Engine) + labels[types.DiscoveryLabelEngineVersion] = aws.StringValue(rdsCluster.EngineVersion) + labels[types.DiscoveryLabelEndpointType] = string(endpointType) return addLabels(labels, libcloudaws.TagsToLabels(rdsCluster.TagList)) } @@ -1469,8 +1455,8 @@ func labelsFromRDSCluster(rdsCluster *rds.DBCluster, meta *types.AWS, endpointTy func labelsFromRDSProxy(rdsProxy *rds.DBProxy, meta *types.AWS, tags []*rds.Tag) map[string]string { // rds.DBProxy has no TagList. labels := labelsFromAWSMetadata(meta) - labels[labelVPCID] = aws.StringValue(rdsProxy.VpcId) - labels[labelEngine] = aws.StringValue(rdsProxy.EngineFamily) + labels[types.DiscoveryLabelVPCID] = aws.StringValue(rdsProxy.VpcId) + labels[types.DiscoveryLabelEngine] = aws.StringValue(rdsProxy.EngineFamily) return addLabels(labels, libcloudaws.TagsToLabels(tags)) } @@ -1478,7 +1464,7 @@ func labelsFromRDSProxy(rdsProxy *rds.DBProxy, meta *types.AWS, tags []*rds.Tag) // RDS Proxy custom endpoint. func labelsFromRDSProxyCustomEndpoint(rdsProxy *rds.DBProxy, customEndpoint *rds.DBProxyEndpoint, meta *types.AWS, tags []*rds.Tag) map[string]string { labels := labelsFromRDSProxy(rdsProxy, meta, tags) - labels[labelEndpointType] = aws.StringValue(customEndpoint.TargetRole) + labels[types.DiscoveryLabelEndpointType] = aws.StringValue(customEndpoint.TargetRole) return labels } @@ -1490,21 +1476,21 @@ func labelsFromRedshiftCluster(cluster *redshift.Cluster, meta *types.AWS) map[s func labelsFromRedshiftServerlessWorkgroup(workgroup *redshiftserverless.Workgroup, meta *types.AWS, tags []*redshiftserverless.Tag) map[string]string { labels := labelsFromAWSMetadata(meta) - labels[labelEndpointType] = RedshiftServerlessWorkgroupEndpoint - labels[labelNamespace] = aws.StringValue(workgroup.NamespaceName) + labels[types.DiscoveryLabelEndpointType] = RedshiftServerlessWorkgroupEndpoint + labels[types.DiscoveryLabelNamespace] = aws.StringValue(workgroup.NamespaceName) if workgroup.Endpoint != nil && len(workgroup.Endpoint.VpcEndpoints) > 0 { - labels[labelVPCID] = aws.StringValue(workgroup.Endpoint.VpcEndpoints[0].VpcId) + labels[types.DiscoveryLabelVPCID] = aws.StringValue(workgroup.Endpoint.VpcEndpoints[0].VpcId) } return addLabels(labels, libcloudaws.TagsToLabels(tags)) } func labelsFromRedshiftServerlessVPCEndpoint(endpoint *redshiftserverless.EndpointAccess, workgroup *redshiftserverless.Workgroup, meta *types.AWS, tags []*redshiftserverless.Tag) map[string]string { labels := labelsFromAWSMetadata(meta) - labels[labelEndpointType] = RedshiftServerlessVPCEndpoint - labels[labelWorkgroup] = aws.StringValue(endpoint.WorkgroupName) - labels[labelNamespace] = aws.StringValue(workgroup.NamespaceName) + labels[types.DiscoveryLabelEndpointType] = RedshiftServerlessVPCEndpoint + labels[types.DiscoveryLabelWorkgroup] = aws.StringValue(endpoint.WorkgroupName) + labels[types.DiscoveryLabelNamespace] = aws.StringValue(workgroup.NamespaceName) if endpoint.VpcEndpoint != nil { - labels[labelVPCID] = aws.StringValue(endpoint.VpcEndpoint.VpcId) + labels[types.DiscoveryLabelVPCID] = aws.StringValue(endpoint.VpcEndpoint.VpcId) } return addLabels(labels, libcloudaws.TagsToLabels(tags)) } @@ -1513,28 +1499,32 @@ func labelsFromRedshiftServerlessVPCEndpoint(endpoint *redshiftserverless.Endpoi func labelsFromAWSMetadata(meta *types.AWS) map[string]string { labels := make(map[string]string) if meta != nil { - labels[labelAccountID] = meta.AccountID - labels[labelRegion] = meta.Region + labels[types.DiscoveryLabelAccountID] = meta.AccountID + labels[types.DiscoveryLabelRegion] = meta.Region } + labels[types.OriginLabel] = types.OriginCloud + labels[types.CloudLabel] = types.CloudAWS return labels } func labelsFromOpenSearchDomain(domain *opensearchservice.DomainStatus, meta *types.AWS, endpointType string, tags []*opensearchservice.Tag) map[string]string { labels := labelsFromMetaAndEndpointType(meta, endpointType, libcloudaws.TagsToLabels(tags)) - labels[labelEngineVersion] = aws.StringValue(domain.EngineVersion) + labels[types.DiscoveryLabelEngineVersion] = aws.StringValue(domain.EngineVersion) return labels } // labelsFromMetaAndEndpointType creates database labels from provided AWS meta and endpoint type. func labelsFromMetaAndEndpointType(meta *types.AWS, endpointType string, extraLabels map[string]string) map[string]string { labels := labelsFromAWSMetadata(meta) - labels[labelEndpointType] = endpointType + labels[types.DiscoveryLabelEndpointType] = endpointType return addLabels(labels, extraLabels) } // azureTagsToLabels converts Azure tags to a labels map. func azureTagsToLabels(tags map[string]string) map[string]string { labels := make(map[string]string) + labels[types.OriginLabel] = types.OriginCloud + labels[types.CloudLabel] = types.CloudAzure return addLabels(labels, tags) } @@ -1778,7 +1768,7 @@ func auroraMySQLVersion(cluster *rds.DBCluster) string { // GetMySQLEngineVersion returns MySQL engine version from provided metadata labels. // An empty string is returned if label doesn't exist. func GetMySQLEngineVersion(labels map[string]string) string { - engine, ok := labels[labelEngine] + engine, ok := labels[types.DiscoveryLabelEngine] if !ok { return "" } @@ -1789,7 +1779,7 @@ func GetMySQLEngineVersion(labels map[string]string) string { return "" } - version, ok := labels[labelEngineVersion] + version, ok := labels[types.DiscoveryLabelEngineVersion] if !ok { return "" } @@ -1802,7 +1792,7 @@ func IsAzureFlexServer(db types.Database) bool { if db.GetAzure().IsFlexiServer { return true } - engine, ok := db.GetMetadata().Labels[labelEngine] + engine, ok := db.GetMetadata().Labels[types.DiscoveryLabelEngine] return ok && (engine == AzureEngineMySQLFlex || engine == AzureEnginePostgresFlex) } @@ -1818,38 +1808,6 @@ func MakeAzureDatabaseLoginUsername(db types.Database, user string) string { return fmt.Sprintf("%v@%v", user, db.GetAzure().Name) } -const ( - // labelAccountID is the label key containing AWS account ID. - labelAccountID = "account-id" - // labelRegion is the label key containing the cloud region. - labelRegion = "region" - // labelEngine is the label key containing database engine name. - labelEngine = "engine" - // labelEngineVersion is the label key containing database engine version. - labelEngineVersion = "engine-version" - // labelEndpointType is the label key containing the endpoint type. - labelEndpointType = "endpoint-type" - // labelVPCID is the label key containing the VPC ID. - labelVPCID = "vpc-id" - // labelNamespace is the label key for namespace name. - labelNamespace = "namespace" - // labelWorkgroup is the label key for workgroup name. - labelWorkgroup = "workgroup" - // labelTeleportDBName is the label key containing the database name override. - labelTeleportDBName = types.TeleportNamespace + "/database_name" - // labelTeleportDBNameAzure is the label key containing the database name - // override for Azure databases. Azure tags connot contain these - // characters: "<>%&\?/". - labelTeleportDBNameAzure = "TeleportDatabaseName" - // labelReplicationRole is the replication role of an Azure DB Flexible server, e.g. "Source" or "Replica". - labelReplicationRole = "replication-role" - // labelSourceServer is the source server for replica Azure DB Flexible servers. - // This is the source (primary) database resource name. - labelSourceServer = "source-server" - // labelStatus is the label key containing the database status, e.g. "available" - labelStatus = "status" -) - const ( // RDSEngineMySQL is RDS engine name for MySQL instances. RDSEngineMySQL = "mysql" @@ -1911,13 +1869,6 @@ const ( RedshiftServerlessVPCEndpoint = "vpc-endpoint" ) -const ( - // labelSubscriptionID is the label key for Azure subscription ID. - labelSubscriptionID = "subscription-id" - // labelResourceGroup is the label key for the Azure resource group name. - labelResourceGroup = "resource-group" -) - const ( // azureSQLServerDefaultPort is the default port for Azure SQL Server. azureSQLServerDefaultPort = 1433 diff --git a/lib/services/database_test.go b/lib/services/database_test.go index 2aa9a8ad21983..f0a8fd85d5278 100644 --- a/lib/services/database_test.go +++ b/lib/services/database_test.go @@ -350,12 +350,14 @@ func TestDatabaseFromAzureDBServer(t *testing.T) { Name: "testdb", Description: "Azure MySQL server in eastus", Labels: map[string]string{ - labelRegion: "eastus", - labelEngine: "Microsoft.DBforMySQL/servers", - labelEngineVersion: "5.7", - labelResourceGroup: "defaultRG", - labelSubscriptionID: "sub1", - "foo": "bar", + types.DiscoveryLabelRegion: "eastus", + types.DiscoveryLabelEngine: "Microsoft.DBforMySQL/servers", + types.DiscoveryLabelEngineVersion: "5.7", + types.DiscoveryLabelAzureResourceGroup: "defaultRG", + types.OriginLabel: types.OriginCloud, + types.CloudLabel: types.CloudAzure, + types.DiscoveryLabelAzureSubscriptionID: "sub1", + "foo": "bar", }, }, types.DatabaseSpecV3{ Protocol: defaults.ProtocolMySQL, @@ -397,12 +399,14 @@ func TestDatabaseFromAzureRedis(t *testing.T) { Name: name, Description: "Azure Redis server in eastus", Labels: map[string]string{ - labelRegion: region, - labelEngine: "Microsoft.Cache/Redis", - labelEngineVersion: "6.0", - labelResourceGroup: group, - labelSubscriptionID: subscription, - "foo": "bar", + types.DiscoveryLabelRegion: region, + types.DiscoveryLabelEngine: "Microsoft.Cache/Redis", + types.DiscoveryLabelEngineVersion: "6.0", + types.DiscoveryLabelAzureResourceGroup: group, + types.OriginLabel: types.OriginCloud, + types.CloudLabel: types.CloudAzure, + types.DiscoveryLabelAzureSubscriptionID: subscription, + "foo": "bar", }, }, types.DatabaseSpecV3{ Protocol: defaults.ProtocolRedis, @@ -454,13 +458,15 @@ func TestDatabaseFromAzureRedisEnterprise(t *testing.T) { Name: name, Description: "Azure Redis Enterprise server in eastus", Labels: map[string]string{ - labelRegion: region, - labelEngine: "Microsoft.Cache/redisEnterprise", - labelEngineVersion: "6.0", - labelResourceGroup: group, - labelSubscriptionID: subscription, - labelEndpointType: "OSSCluster", - "foo": "bar", + types.DiscoveryLabelRegion: region, + types.DiscoveryLabelEngine: "Microsoft.Cache/redisEnterprise", + types.DiscoveryLabelEngineVersion: "6.0", + types.DiscoveryLabelAzureResourceGroup: group, + types.OriginLabel: types.OriginCloud, + types.CloudLabel: types.CloudAzure, + types.DiscoveryLabelAzureSubscriptionID: subscription, + types.DiscoveryLabelEndpointType: "OSSCluster", + "foo": "bar", }, }, types.DatabaseSpecV3{ Protocol: defaults.ProtocolRedis, @@ -503,12 +509,14 @@ func TestDatabaseFromRDSInstance(t *testing.T) { Name: "instance-1", Description: "RDS instance in us-west-1", Labels: map[string]string{ - labelAccountID: "123456789012", - labelRegion: "us-west-1", - labelEngine: RDSEnginePostgres, - labelEngineVersion: "13.0", - labelEndpointType: "instance", - "key": "val", + types.DiscoveryLabelAccountID: "123456789012", + types.OriginLabel: types.OriginCloud, + types.CloudLabel: types.CloudAWS, + types.DiscoveryLabelRegion: "us-west-1", + types.DiscoveryLabelEngine: RDSEnginePostgres, + types.DiscoveryLabelEngineVersion: "13.0", + types.DiscoveryLabelEndpointType: "instance", + "key": "val", }, }, types.DatabaseSpecV3{ Protocol: defaults.ProtocolPostgres, @@ -562,13 +570,15 @@ func TestDatabaseFromRDSV2Instance(t *testing.T) { Name: "instance-1", Description: "RDS instance in us-west-1", Labels: map[string]string{ - labelAccountID: "123456789012", - labelRegion: "us-west-1", - labelEngine: RDSEnginePostgres, - labelEngineVersion: "13.0", - labelEndpointType: "instance", - labelStatus: "available", - "key": "val", + types.DiscoveryLabelAccountID: "123456789012", + types.OriginLabel: types.OriginCloud, + types.CloudLabel: types.CloudAWS, + types.DiscoveryLabelRegion: "us-west-1", + types.DiscoveryLabelEngine: RDSEnginePostgres, + types.DiscoveryLabelEngineVersion: "13.0", + types.DiscoveryLabelEndpointType: "instance", + types.DiscoveryLabelStatus: "available", + "key": "val", }, }, types.DatabaseSpecV3{ Protocol: defaults.ProtocolPostgres, @@ -594,72 +604,80 @@ func TestDatabaseFromRDSV2Instance(t *testing.T) { require.NoError(t, err) require.Empty(t, cmp.Diff(expected, actual)) - t.Run("with name override", func(t *testing.T) { - newName := "override-1" - - instance.TagList = append(instance.TagList, - rdsTypesV2.Tag{ - Key: aws.String(labelTeleportDBName), - Value: aws.String(newName), - }, - ) - expected.Metadata.Name = newName + for _, overrideLabel := range types.AWSDatabaseNameOverrideLabels { + t.Run("with name override via "+overrideLabel, func(t *testing.T) { + newName := "override-1" + instance := instance + instance.TagList = append(instance.TagList, + rdsTypesV2.Tag{ + Key: aws.String(overrideLabel), + Value: aws.String(newName), + }, + ) + expected.Metadata.Name = newName - actual, err := NewDatabaseFromRDSV2Instance(instance) - require.NoError(t, err) - require.Equal(t, actual.GetName(), newName) - }) + actual, err := NewDatabaseFromRDSV2Instance(instance) + require.NoError(t, err) + require.Equal(t, actual.GetName(), newName) + }) + } } // 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{ - 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, + for _, overrideLabel := range types.AWSDatabaseNameOverrideLabels { + 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), }, - }, - }) - require.NoError(t, err) - actual, err := NewDatabaseFromRDSInstance(instance) - require.NoError(t, err) - require.Empty(t, cmp.Diff(expected, actual)) + TagList: []*rds.Tag{ + {Key: aws.String("key"), Value: aws.String("val")}, + {Key: aws.String(overrideLabel), Value: aws.String("override-1")}, + }, + } + t.Run("via "+overrideLabel, func(t *testing.T) { + expected, err := types.NewDatabaseV3(types.Metadata{ + Name: "override-1", + Description: "RDS instance in us-west-1", + Labels: map[string]string{ + types.DiscoveryLabelAccountID: "123456789012", + types.OriginLabel: types.OriginCloud, + types.CloudLabel: types.CloudAWS, + types.DiscoveryLabelRegion: "us-west-1", + types.DiscoveryLabelEngine: RDSEnginePostgres, + types.DiscoveryLabelEngineVersion: "13.0", + types.DiscoveryLabelEndpointType: "instance", + overrideLabel: "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.Empty(t, cmp.Diff(expected, actual)) + }) + } } // TestDatabaseFromRDSCluster tests converting an RDS cluster to a database resource. @@ -699,12 +717,14 @@ func TestDatabaseFromRDSCluster(t *testing.T) { Name: "cluster-1", Description: "Aurora cluster in us-east-1", Labels: map[string]string{ - labelAccountID: "123456789012", - labelRegion: "us-east-1", - labelEngine: RDSEngineAuroraMySQL, - labelEngineVersion: "8.0.0", - labelEndpointType: "primary", - "key": "val", + types.DiscoveryLabelAccountID: "123456789012", + types.OriginLabel: types.OriginCloud, + types.CloudLabel: types.CloudAWS, + types.DiscoveryLabelRegion: "us-east-1", + types.DiscoveryLabelEngine: RDSEngineAuroraMySQL, + types.DiscoveryLabelEngineVersion: "8.0.0", + types.DiscoveryLabelEndpointType: "primary", + "key": "val", }, }, types.DatabaseSpecV3{ Protocol: defaults.ProtocolMySQL, @@ -722,12 +742,14 @@ func TestDatabaseFromRDSCluster(t *testing.T) { Name: "cluster-1-reader", Description: "Aurora cluster in us-east-1 (reader endpoint)", Labels: map[string]string{ - labelAccountID: "123456789012", - labelRegion: "us-east-1", - labelEngine: RDSEngineAuroraMySQL, - labelEngineVersion: "8.0.0", - labelEndpointType: "reader", - "key": "val", + types.DiscoveryLabelAccountID: "123456789012", + types.OriginLabel: types.OriginCloud, + types.CloudLabel: types.CloudAWS, + types.DiscoveryLabelRegion: "us-east-1", + types.DiscoveryLabelEngine: RDSEngineAuroraMySQL, + types.DiscoveryLabelEngineVersion: "8.0.0", + types.DiscoveryLabelEndpointType: "reader", + "key": "val", }, }, types.DatabaseSpecV3{ Protocol: defaults.ProtocolMySQL, @@ -742,12 +764,14 @@ func TestDatabaseFromRDSCluster(t *testing.T) { t.Run("custom endpoints", func(t *testing.T) { expectedLabels := map[string]string{ - labelAccountID: "123456789012", - labelRegion: "us-east-1", - labelEngine: RDSEngineAuroraMySQL, - labelEngineVersion: "8.0.0", - labelEndpointType: "custom", - "key": "val", + types.DiscoveryLabelAccountID: "123456789012", + types.OriginLabel: types.OriginCloud, + types.CloudLabel: types.CloudAWS, + types.DiscoveryLabelRegion: "us-east-1", + types.DiscoveryLabelEngine: RDSEngineAuroraMySQL, + types.DiscoveryLabelEngineVersion: "8.0.0", + types.DiscoveryLabelEndpointType: "custom", + "key": "val", } expectedMyEndpoint1, err := types.NewDatabaseV3(types.Metadata{ @@ -833,13 +857,15 @@ func TestDatabaseFromRDSV2Cluster(t *testing.T) { Name: "cluster-1", Description: "Aurora cluster in us-east-1", Labels: map[string]string{ - labelAccountID: "123456789012", - labelRegion: "us-east-1", - labelEngine: RDSEngineAuroraMySQL, - labelEngineVersion: "8.0.0", - labelEndpointType: "primary", - labelStatus: "available", - "key": "val", + types.DiscoveryLabelAccountID: "123456789012", + types.OriginLabel: types.OriginCloud, + types.CloudLabel: types.CloudAWS, + types.DiscoveryLabelRegion: "us-east-1", + types.DiscoveryLabelEngine: RDSEngineAuroraMySQL, + types.DiscoveryLabelEngineVersion: "8.0.0", + types.DiscoveryLabelEndpointType: "primary", + types.DiscoveryLabelStatus: "available", + "key": "val", }, }, types.DatabaseSpecV3{ Protocol: defaults.ProtocolMySQL, @@ -851,157 +877,167 @@ func TestDatabaseFromRDSV2Cluster(t *testing.T) { require.NoError(t, err) require.Empty(t, cmp.Diff(expected, actual)) - t.Run("with name override", func(t *testing.T) { - newName := "override-1" + for _, overrideLabel := range types.AWSDatabaseNameOverrideLabels { + t.Run("with name override via "+overrideLabel, func(t *testing.T) { + newName := "override-1" - cluster.TagList = append(cluster.TagList, - rdsTypesV2.Tag{ - Key: aws.String(labelTeleportDBName), - Value: aws.String(newName), - }, - ) - expected.Metadata.Name = newName + cluster.TagList = append(cluster.TagList, + rdsTypesV2.Tag{ + Key: aws.String(overrideLabel), + Value: aws.String(newName), + }, + ) + expected.Metadata.Name = newName - actual, err := NewDatabaseFromRDSV2Cluster(cluster) - require.NoError(t, err) - require.Equal(t, actual.GetName(), newName) - }) + actual, err := NewDatabaseFromRDSV2Cluster(cluster) + require.NoError(t, err) + require.Equal(t, actual.GetName(), newName) + }) + } }) } // 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{ - labelAccountID: "123456789012", - labelRegion: "us-east-1", - labelEngine: RDSEngineAuroraMySQL, - labelEngineVersion: "8.0.0", - labelEndpointType: "primary", - labelTeleportDBName: "mycluster-2", - "key": "val", + for _, overrideLabel := range types.AWSDatabaseNameOverrideLabels { + 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(overrideLabel), Value: aws.String("mycluster-2")}, }, - }, types.DatabaseSpecV3{ - Protocol: defaults.ProtocolMySQL, - URI: "localhost:3306", - AWS: expectedAWS, - }) - require.NoError(t, err) - actual, err := NewDatabaseFromRDSCluster(cluster) - require.NoError(t, err) - require.Empty(t, cmp.Diff(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{ - labelAccountID: "123456789012", - labelRegion: "us-east-1", - labelEngine: RDSEngineAuroraMySQL, - labelEngineVersion: "8.0.0", - labelEndpointType: "reader", - labelTeleportDBName: "mycluster-2", - "key": "val", + expectedAWS := types.AWS{ + AccountID: "123456789012", + Region: "us-east-1", + RDS: types.RDS{ + ClusterID: "cluster-1", + ResourceID: "resource-1", + IAMAuth: true, }, - }, types.DatabaseSpecV3{ - Protocol: defaults.ProtocolMySQL, - URI: "reader.host:3306", - AWS: expectedAWS, - }) - require.NoError(t, err) - actual, err := NewDatabaseFromRDSClusterReaderEndpoint(cluster) - require.NoError(t, err) - require.Empty(t, cmp.Diff(expected, actual)) - }) - - t.Run("custom endpoints", func(t *testing.T) { - expectedLabels := map[string]string{ - 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", - }, + 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.DiscoveryLabelAccountID: "123456789012", + types.OriginLabel: types.OriginCloud, + types.CloudLabel: types.CloudAWS, + types.DiscoveryLabelRegion: "us-east-1", + types.DiscoveryLabelEngine: RDSEngineAuroraMySQL, + types.DiscoveryLabelEngineVersion: "8.0.0", + types.DiscoveryLabelEndpointType: "primary", + overrideLabel: "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.Empty(t, cmp.Diff(expected, actual)) }) - 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", - }, + 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.DiscoveryLabelAccountID: "123456789012", + types.OriginLabel: types.OriginCloud, + types.CloudLabel: types.CloudAWS, + types.DiscoveryLabelRegion: "us-east-1", + types.DiscoveryLabelEngine: RDSEngineAuroraMySQL, + types.DiscoveryLabelEngineVersion: "8.0.0", + types.DiscoveryLabelEndpointType: "reader", + overrideLabel: "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.Empty(t, cmp.Diff(expected, actual)) }) - require.NoError(t, err) - databases, err := NewDatabasesFromRDSClusterCustomEndpoints(cluster) - require.NoError(t, err) - require.Equal(t, types.Databases{expectedMyEndpoint1, expectedMyEndpoint2}, databases) - }) + t.Run("custom endpoints", func(t *testing.T) { + expectedLabels := map[string]string{ + types.DiscoveryLabelAccountID: "123456789012", + types.OriginLabel: types.OriginCloud, + types.CloudLabel: types.CloudAWS, + types.DiscoveryLabelRegion: "us-east-1", + types.DiscoveryLabelEngine: RDSEngineAuroraMySQL, + types.DiscoveryLabelEngineVersion: "8.0.0", + types.DiscoveryLabelEndpointType: "custom", + overrideLabel: "mycluster-2", + "key": "val", + } - 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) - }) + 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) { @@ -1032,11 +1068,13 @@ func TestDatabaseFromRDSProxy(t *testing.T) { Name: "testproxy", Description: "RDS Proxy in ca-central-1", Labels: map[string]string{ - "key": "val", - labelAccountID: "123456789012", - labelRegion: "ca-central-1", - labelEngine: "MYSQL", - labelVPCID: "test-vpc-id", + "key": "val", + types.DiscoveryLabelAccountID: "123456789012", + types.OriginLabel: types.OriginCloud, + types.CloudLabel: types.CloudAWS, + types.DiscoveryLabelRegion: "ca-central-1", + types.DiscoveryLabelEngine: "MYSQL", + types.DiscoveryLabelVPCID: "test-vpc-id", }, }, types.DatabaseSpecV3{ Protocol: defaults.ProtocolMySQL, @@ -1062,12 +1100,14 @@ func TestDatabaseFromRDSProxy(t *testing.T) { Name: "testproxy-custom", Description: "RDS Proxy endpoint in ca-central-1", Labels: map[string]string{ - "key": "val", - labelAccountID: "123456789012", - labelRegion: "ca-central-1", - labelEngine: "MYSQL", - labelVPCID: "test-vpc-id", - labelEndpointType: "READ_ONLY", + "key": "val", + types.DiscoveryLabelAccountID: "123456789012", + types.OriginLabel: types.OriginCloud, + types.CloudLabel: types.CloudAWS, + types.DiscoveryLabelRegion: "ca-central-1", + types.DiscoveryLabelEngine: "MYSQL", + types.DiscoveryLabelVPCID: "test-vpc-id", + types.DiscoveryLabelEndpointType: "READ_ONLY", }, }, types.DatabaseSpecV3{ Protocol: defaults.ProtocolMySQL, @@ -1224,8 +1264,14 @@ func TestAzureTagsToLabels(t *testing.T) { "Name": "test", } labels := azureTagsToLabels(azureTags) - require.Equal(t, map[string]string{"Name": "test", "Env": "dev", - "foo:bar": "some-id"}, labels) + wantLabels := map[string]string{ + "Name": "test", + "Env": "dev", + "foo:bar": "some-id", + types.OriginLabel: types.OriginCloud, + types.CloudLabel: types.CloudAzure, + } + require.Equal(t, wantLabels, labels) } // TestDatabaseFromRedshiftCluster tests converting an Redshift cluster to a database resource. @@ -1253,8 +1299,10 @@ func TestDatabaseFromRedshiftCluster(t *testing.T) { Name: "mycluster", Description: "Redshift cluster in us-east-1", Labels: map[string]string{ - labelAccountID: "123456789012", - labelRegion: "us-east-1", + types.DiscoveryLabelAccountID: "123456789012", + types.OriginLabel: types.OriginCloud, + types.CloudLabel: types.CloudAWS, + types.DiscoveryLabelRegion: "us-east-1", "key": "val", "elasticbeanstalk:environment-id": "id", }, @@ -1277,65 +1325,69 @@ func TestDatabaseFromRedshiftCluster(t *testing.T) { require.Empty(t, cmp.Diff(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"), + for _, overrideLabel := range types.AWSDatabaseNameOverrideLabels { + t.Run("success with name override via"+overrideLabel, 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), }, - { - Key: aws.String("elasticbeanstalk:environment-id"), - Value: aws.String("id"), + Tags: []*redshift.Tag{ + { + Key: aws.String("key"), + Value: aws.String("val"), + }, + { + Key: aws.String("elasticbeanstalk:environment-id"), + Value: aws.String("id"), + }, + { + Key: aws.String(overrideLabel), + Value: aws.String("mycluster-override-2"), + }, }, - { - 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.DiscoveryLabelAccountID: "123456789012", + types.OriginLabel: types.OriginCloud, + types.CloudLabel: types.CloudAWS, + types.DiscoveryLabelRegion: "us-east-1", + overrideLabel: "mycluster-override-2", + "key": "val", + "elasticbeanstalk:environment-id": "id", }, - }, - } - expected, err := types.NewDatabaseV3(types.Metadata{ - Name: "mycluster-override-2", - Description: "Redshift cluster in us-east-1", - Labels: map[string]string{ - 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", + }, 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) + require.NoError(t, err) - actual, err := NewDatabaseFromRedshiftCluster(cluster) - require.NoError(t, err) - require.Empty(t, cmp.Diff(expected, actual)) - }) + actual, err := NewDatabaseFromRedshiftCluster(cluster) + require.NoError(t, err) + require.Empty(t, cmp.Diff(expected, actual)) + }) - t.Run("missing endpoint", func(t *testing.T) { - _, err := NewDatabaseFromRedshiftCluster(&redshift.Cluster{ - ClusterIdentifier: aws.String("still-creating"), + 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) }) - require.Error(t, err) - require.True(t, trace.IsBadParameter(err), "Expected trace.BadParameter, got %v", err) - }) + } } func TestDatabaseFromElastiCacheConfigurationEndpoint(t *testing.T) { @@ -1381,10 +1433,12 @@ func TestDatabaseFromElastiCacheConfigurationEndpoint(t *testing.T) { Name: "my-cluster", Description: "ElastiCache cluster in us-east-1 (configuration endpoint)", Labels: map[string]string{ - labelAccountID: "123456789012", - labelRegion: "us-east-1", - labelEndpointType: "configuration", - "key": "value", + types.DiscoveryLabelAccountID: "123456789012", + types.OriginLabel: types.OriginCloud, + types.CloudLabel: types.CloudAWS, + types.DiscoveryLabelRegion: "us-east-1", + types.DiscoveryLabelEndpointType: "configuration", + "key": "value", }, }, types.DatabaseSpecV3{ Protocol: defaults.ProtocolRedis, @@ -1408,76 +1462,82 @@ func TestDatabaseFromElastiCacheConfigurationEndpoint(t *testing.T) { } 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"), - }, + for _, overrideLabel := range types.AWSDatabaseNameOverrideLabels { + t.Run("via "+overrideLabel, func(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), }, - }, - { - NodeGroupId: aws.String("0002"), - NodeGroupMembers: []*elasticache.NodeGroupMember{ + UserGroupIds: []*string{aws.String("my-user-group")}, + NodeGroups: []*elasticache.NodeGroup{ { - CacheClusterId: aws.String("my-cluster-0002-001"), + NodeGroupId: aws.String("0001"), + NodeGroupMembers: []*elasticache.NodeGroupMember{ + { + CacheClusterId: aws.String("my-cluster-0001-001"), + }, + { + CacheClusterId: aws.String("my-cluster-0001-002"), + }, + }, }, { - CacheClusterId: aws.String("my-cluster-0002-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", - } + } + extraLabels := map[string]string{ + overrideLabel: "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{ - 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) + 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.DiscoveryLabelAccountID: "123456789012", + types.OriginLabel: types.OriginCloud, + types.CloudLabel: types.CloudAWS, + types.DiscoveryLabelRegion: "us-east-1", + types.DiscoveryLabelEndpointType: "configuration", + overrideLabel: "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.Empty(t, cmp.Diff(expected, actual)) + actual, err := NewDatabaseFromElastiCacheConfigurationEndpoint(cluster, extraLabels) + require.NoError(t, err) + require.Empty(t, cmp.Diff(expected, actual)) + }) + } } func TestDatabaseFromElastiCacheNodeGroups(t *testing.T) { @@ -1508,10 +1568,12 @@ func TestDatabaseFromElastiCacheNodeGroups(t *testing.T) { Name: "my-cluster", Description: "ElastiCache cluster in us-east-1 (primary endpoint)", Labels: map[string]string{ - labelAccountID: "123456789012", - labelRegion: "us-east-1", - labelEndpointType: "primary", - "key": "value", + types.DiscoveryLabelAccountID: "123456789012", + types.OriginLabel: types.OriginCloud, + types.CloudLabel: types.CloudAWS, + types.DiscoveryLabelRegion: "us-east-1", + types.DiscoveryLabelEndpointType: "primary", + "key": "value", }, }, types.DatabaseSpecV3{ Protocol: defaults.ProtocolRedis, @@ -1533,10 +1595,12 @@ func TestDatabaseFromElastiCacheNodeGroups(t *testing.T) { Name: "my-cluster-reader", Description: "ElastiCache cluster in us-east-1 (reader endpoint)", Labels: map[string]string{ - labelAccountID: "123456789012", - labelRegion: "us-east-1", - labelEndpointType: "reader", - "key": "value", + types.DiscoveryLabelAccountID: "123456789012", + types.OriginLabel: types.OriginCloud, + types.CloudLabel: types.CloudAWS, + types.DiscoveryLabelRegion: "us-east-1", + types.DiscoveryLabelEndpointType: "reader", + "key": "value", }, }, types.DatabaseSpecV3{ Protocol: defaults.ProtocolRedis, @@ -1560,87 +1624,95 @@ func TestDatabaseFromElastiCacheNodeGroups(t *testing.T) { } 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), + for _, overrideLabel := range types.AWSDatabaseNameOverrideLabels { + t.Run("via "+overrideLabel, func(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", - } + } + extraLabels := map[string]string{ + overrideLabel: "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{ - 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) + 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.DiscoveryLabelAccountID: "123456789012", + types.OriginLabel: types.OriginCloud, + types.CloudLabel: types.CloudAWS, + types.DiscoveryLabelRegion: "us-east-1", + types.DiscoveryLabelEndpointType: "primary", + overrideLabel: "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{ - 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) + 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.DiscoveryLabelAccountID: "123456789012", + types.OriginLabel: types.OriginCloud, + types.CloudLabel: types.CloudAWS, + types.DiscoveryLabelRegion: "us-east-1", + types.DiscoveryLabelEndpointType: "reader", + overrideLabel: "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) + actual, err := NewDatabasesFromElastiCacheNodeGroups(cluster, extraLabels) + require.NoError(t, err) + require.Equal(t, types.Databases{expectedPrimary, expectedReader}, actual) + }) + } } func TestDatabaseFromMemoryDBCluster(t *testing.T) { @@ -1661,10 +1733,12 @@ func TestDatabaseFromMemoryDBCluster(t *testing.T) { Name: "my-cluster", Description: "MemoryDB cluster in us-east-1", Labels: map[string]string{ - labelAccountID: "123456789012", - labelRegion: "us-east-1", - labelEndpointType: "cluster", - "key": "value", + types.DiscoveryLabelAccountID: "123456789012", + types.OriginLabel: types.OriginCloud, + types.CloudLabel: types.CloudAWS, + types.DiscoveryLabelRegion: "us-east-1", + types.DiscoveryLabelEndpointType: "cluster", + "key": "value", }, }, types.DatabaseSpecV3{ Protocol: defaults.ProtocolRedis, @@ -1694,12 +1768,14 @@ func TestDatabaseFromRedshiftServerlessWorkgroup(t *testing.T) { Name: "my-workgroup", Description: "Redshift Serverless workgroup in eu-west-2", Labels: map[string]string{ - labelAccountID: "123456789012", - labelRegion: "eu-west-2", - labelEndpointType: "workgroup", - labelNamespace: "my-namespace", - labelVPCID: "vpc-id", - "env": "prod", + types.DiscoveryLabelAccountID: "123456789012", + types.OriginLabel: types.OriginCloud, + types.CloudLabel: types.CloudAWS, + types.DiscoveryLabelRegion: "eu-west-2", + types.DiscoveryLabelEndpointType: "workgroup", + types.DiscoveryLabelNamespace: "my-namespace", + types.DiscoveryLabelVPCID: "vpc-id", + "env": "prod", }, }, types.DatabaseSpecV3{ Protocol: defaults.ProtocolPostgres, @@ -1728,13 +1804,15 @@ func TestDatabaseFromRedshiftServerlessVPCEndpoint(t *testing.T) { Name: "my-workgroup-my-endpoint", Description: "Redshift Serverless endpoint in eu-west-2", Labels: map[string]string{ - labelAccountID: "123456789012", - labelRegion: "eu-west-2", - labelEndpointType: "vpc-endpoint", - labelWorkgroup: "my-workgroup", - labelNamespace: "my-namespace", - labelVPCID: "vpc-id", - "env": "prod", + types.DiscoveryLabelAccountID: "123456789012", + types.OriginLabel: types.OriginCloud, + types.CloudLabel: types.CloudAWS, + types.DiscoveryLabelRegion: "eu-west-2", + types.DiscoveryLabelEndpointType: "vpc-endpoint", + types.DiscoveryLabelWorkgroup: "my-workgroup", + types.DiscoveryLabelNamespace: "my-namespace", + types.DiscoveryLabelVPCID: "vpc-id", + "env": "prod", }, }, types.DatabaseSpecV3{ Protocol: defaults.ProtocolPostgres, @@ -1760,51 +1838,57 @@ func TestDatabaseFromRedshiftServerlessVPCEndpoint(t *testing.T) { } 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", - } + for _, overrideLabel := range types.AWSDatabaseNameOverrideLabels { + t.Run("via "+overrideLabel, func(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{ + overrideLabel: "override-1", + "key": "value", + } - expected, err := types.NewDatabaseV3(types.Metadata{ - Name: "override-1", - Description: "MemoryDB cluster in us-east-1", - Labels: map[string]string{ - 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) + expected, err := types.NewDatabaseV3(types.Metadata{ + Name: "override-1", + Description: "MemoryDB cluster in us-east-1", + Labels: map[string]string{ + types.DiscoveryLabelAccountID: "123456789012", + types.OriginLabel: types.OriginCloud, + types.CloudLabel: types.CloudAWS, + types.DiscoveryLabelRegion: "us-east-1", + types.DiscoveryLabelEndpointType: "cluster", + overrideLabel: "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.Empty(t, cmp.Diff(expected, actual)) + actual, err := NewDatabaseFromMemoryDBCluster(cluster, extraLabels) + require.NoError(t, err) + require.Empty(t, cmp.Diff(expected, actual)) + }) + } } func TestExtraElastiCacheLabels(t *testing.T) { @@ -1947,16 +2031,16 @@ func TestGetLabelEngineVersion(t *testing.T) { { name: "mysql-8.0.0", labels: map[string]string{ - labelEngine: RDSEngineMySQL, - labelEngineVersion: "8.0.0", + types.DiscoveryLabelEngine: RDSEngineMySQL, + types.DiscoveryLabelEngineVersion: "8.0.0", }, want: "8.0.0", }, { name: "mariadb returns nothing", labels: map[string]string{ - labelEngine: RDSEngineMariaDB, - labelEngineVersion: "10.6.7", + types.DiscoveryLabelEngine: RDSEngineMariaDB, + types.DiscoveryLabelEngineVersion: "10.6.7", }, want: "", }, @@ -1968,16 +2052,16 @@ func TestGetLabelEngineVersion(t *testing.T) { { name: "azure-mysql-8.0.0", labels: map[string]string{ - labelEngine: AzureEngineMySQL, - labelEngineVersion: "8.0.0", + types.DiscoveryLabelEngine: AzureEngineMySQL, + types.DiscoveryLabelEngineVersion: "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), + types.DiscoveryLabelEngine: AzureEngineMySQLFlex, + types.DiscoveryLabelEngineVersion: string(armmysqlflexibleservers.ServerVersionEight021), }, want: "8.0.21", }, @@ -1995,51 +2079,6 @@ func TestGetLabelEngineVersion(t *testing.T) { } } -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 @@ -2071,8 +2110,8 @@ func TestNewDatabaseFromAzureSQLServer(t *testing.T) { // Assert labels labels := db.GetMetadata().Labels - require.Equal(t, "westus", labels[labelRegion]) - require.Equal(t, "12.0", labels[labelEngineVersion]) + require.Equal(t, "westus", labels[types.DiscoveryLabelRegion]) + require.Equal(t, "12.0", labels[types.DiscoveryLabelEngineVersion]) }, }, { @@ -2126,7 +2165,7 @@ func TestNewDatabaseFromAzureManagedSQLServer(t *testing.T) { // Assert labels labels := db.GetMetadata().Labels - require.Equal(t, "westus", labels[labelRegion]) + require.Equal(t, "westus", labels[types.DiscoveryLabelRegion]) }, }, { @@ -2212,18 +2251,20 @@ func TestDatabaseFromAzureMySQLFlexServer(t *testing.T) { } wantLabels := map[string]string{ - labelRegion: region, - labelEngine: provider, - labelEngineVersion: "8.0.21", - labelResourceGroup: group, - labelSubscriptionID: subID, - "foo": "bar", + types.DiscoveryLabelRegion: region, + types.DiscoveryLabelEngine: provider, + types.DiscoveryLabelEngineVersion: "8.0.21", + types.DiscoveryLabelAzureResourceGroup: group, + types.OriginLabel: types.OriginCloud, + types.CloudLabel: types.CloudAzure, + types.DiscoveryLabelAzureSubscriptionID: subID, + "foo": "bar", } if tt.wantReplicationRoleLabel != "" { - wantLabels[labelReplicationRole] = tt.wantReplicationRoleLabel + wantLabels[types.DiscoveryLabelAzureReplicationRole] = tt.wantReplicationRoleLabel } if tt.wantSourceServerLabel != "" { - wantLabels[labelSourceServer] = tt.wantSourceServerLabel + wantLabels[types.DiscoveryLabelAzureSourceServer] = tt.wantSourceServerLabel } wantDB, err := types.NewDatabaseV3(types.Metadata{ Name: tt.serverName, @@ -2242,7 +2283,7 @@ func TestDatabaseFromAzureMySQLFlexServer(t *testing.T) { actual, err := NewDatabaseFromAzureMySQLFlexServer(&server) require.NoError(t, err) - require.Equal(t, wantDB, actual) + require.Empty(t, cmp.Diff(wantDB, actual)) }) } } @@ -2287,12 +2328,14 @@ func TestDatabaseFromAzurePostgresFlexServer(t *testing.T) { } wantLabels := map[string]string{ - labelRegion: region, - labelEngine: provider, - labelEngineVersion: "14", - labelResourceGroup: group, - labelSubscriptionID: subID, - "foo": "bar", + types.DiscoveryLabelRegion: region, + types.DiscoveryLabelEngine: provider, + types.DiscoveryLabelEngineVersion: "14", + types.DiscoveryLabelAzureResourceGroup: group, + types.OriginLabel: types.OriginCloud, + types.CloudLabel: types.CloudAzure, + types.DiscoveryLabelAzureSubscriptionID: subID, + "foo": "bar", } wantDB, err := types.NewDatabaseV3(types.Metadata{ Name: tt.serverName, @@ -2311,7 +2354,7 @@ func TestDatabaseFromAzurePostgresFlexServer(t *testing.T) { actual, err := NewDatabaseFromAzurePostgresFlexServer(&server) require.NoError(t, err) - require.Equal(t, wantDB, actual) + require.Empty(t, cmp.Diff(wantDB, actual)) }) } } @@ -2383,12 +2426,14 @@ func TestMakeAzureDatabaseLoginUsername(t *testing.T) { Name: serverName, Description: "test azure db server", Labels: map[string]string{ - labelRegion: "eastus", - labelEngine: tt.engine, - labelEngineVersion: "1.2.3", - labelResourceGroup: group, - labelSubscriptionID: subID, - "foo": "bar", + types.DiscoveryLabelRegion: "eastus", + types.DiscoveryLabelEngine: tt.engine, + types.DiscoveryLabelEngineVersion: "1.2.3", + types.DiscoveryLabelAzureResourceGroup: group, + types.OriginLabel: types.OriginCloud, + types.CloudLabel: types.CloudAzure, + types.DiscoveryLabelAzureSubscriptionID: subID, + "foo": "bar", }, }, types.DatabaseSpecV3{ Protocol: tt.protocol, diff --git a/lib/services/kubernetes.go b/lib/services/kubernetes.go index cd719f6d12d6f..5b553c281da43 100644 --- a/lib/services/kubernetes.go +++ b/lib/services/kubernetes.go @@ -19,7 +19,6 @@ package services import ( "context" "fmt" - "strings" "github.com/aws/aws-sdk-go-v2/aws/arn" "github.com/aws/aws-sdk-go/aws" @@ -170,32 +169,32 @@ func UnmarshalKubeCluster(data []byte, opts ...MarshalOption) (types.KubeCluster return nil, trace.BadParameter("unsupported kube cluster resource version %q", h.Version) } -const ( - // labelTeleportKubeClusterName is the label key containing the kubernetes cluster name override. - labelTeleportKubeClusterName = types.TeleportNamespace + "/kubernetes-name" -) - -// setKubeName modifies the types.Metadata argument in place, setting the kubernetes cluster name. -// The name is calculated based on nameParts arguments which are joined by hyphens "-". -// If the kube_cluster name override label is present (setKubeName), it will replace the *first* name part. -func setKubeName(meta types.Metadata, firstNamePart string, extraNameParts ...string) types.Metadata { - nameParts := append([]string{firstNamePart}, extraNameParts...) - - // apply override - if override, found := meta.Labels[labelTeleportKubeClusterName]; found && override != "" { - nameParts[0] = override - } +// setAWSKubeName modifies the types.Metadata in place, overriding the first +// part if the kube cluster override label for AWS is present, and setting the +// kube cluster name. +func setAWSKubeName(meta types.Metadata, firstNamePart string, extraNameParts ...string) types.Metadata { + return setResourceName(types.AWSKubeClusterNameOverrideLabels, meta, firstNamePart, extraNameParts...) +} - meta.Name = strings.Join(nameParts, "-") +// setAzureKubeName modifies the types.Metadata in place, overriding the first +// part if the AKS kube cluster override label is present, and setting the kube +// cluster name. +func setAzureKubeName(meta types.Metadata, firstNamePart string, extraNameParts ...string) types.Metadata { + return setResourceName([]string{types.AzureKubeClusterNameOverrideLabel}, meta, firstNamePart, extraNameParts...) +} - return meta +// setGCPKubeName modifies the types.Metadata in place, overriding the first +// part if the GKE kube cluster override label is present, and setting the kube +// cluster name. +func setGCPKubeName(meta types.Metadata, firstNamePart string, extraNameParts ...string) types.Metadata { + return setResourceName([]string{types.GCPKubeClusterNameOverrideLabel}, meta, firstNamePart, extraNameParts...) } // NewKubeClusterFromAzureAKS creates a kube_cluster resource from an AKSCluster. func NewKubeClusterFromAzureAKS(cluster *azure.AKSCluster) (types.KubeCluster, error) { labels := labelsFromAzureKubeCluster(cluster) return types.NewKubernetesClusterV3( - setKubeName(types.Metadata{ + setAzureKubeName(types.Metadata{ Description: fmt.Sprintf("Azure AKS cluster %q in %v", cluster.Name, cluster.Location), @@ -216,17 +215,17 @@ func labelsFromAzureKubeCluster(cluster *azure.AKSCluster) map[string]string { labels := azureTagsToLabels(cluster.Tags) labels[types.OriginLabel] = types.OriginCloud labels[types.CloudLabel] = types.CloudAzure - labels[labelRegion] = cluster.Location + labels[types.DiscoveryLabelRegion] = cluster.Location - labels[labelResourceGroup] = cluster.GroupName - labels[labelSubscriptionID] = cluster.SubscriptionID + labels[types.DiscoveryLabelAzureResourceGroup] = cluster.GroupName + labels[types.DiscoveryLabelAzureSubscriptionID] = cluster.SubscriptionID return labels } // NewKubeClusterFromGCPGKE creates a kube_cluster resource from an GKE cluster. func NewKubeClusterFromGCPGKE(cluster gcp.GKECluster) (types.KubeCluster, error) { return types.NewKubernetesClusterV3( - setKubeName(types.Metadata{ + setGCPKubeName(types.Metadata{ Description: getOrSetDefaultGCPDescription(cluster), Labels: labelsFromGCPKubeCluster(cluster), }, cluster.Name), @@ -255,9 +254,9 @@ func labelsFromGCPKubeCluster(cluster gcp.GKECluster) map[string]string { labels := maps.Clone(cluster.Labels) labels[types.OriginLabel] = types.OriginCloud labels[types.CloudLabel] = types.CloudGCP - labels[labelLocation] = cluster.Location + labels[types.DiscoveryLabelGCPLocation] = cluster.Location - labels[labelProjectID] = cluster.ProjectID + labels[types.DiscoveryLabelGCPProjectID] = cluster.ProjectID return labels } @@ -270,7 +269,7 @@ func NewKubeClusterFromAWSEKS(cluster *eks.Cluster) (types.KubeCluster, error) { labels := labelsFromAWSKubeCluster(cluster, parsedARN) return types.NewKubernetesClusterV3( - setKubeName(types.Metadata{ + setAWSKubeName(types.Metadata{ Description: fmt.Sprintf("AWS EKS cluster %q in %s", aws.StringValue(cluster.Name), parsedARN.Region), @@ -290,9 +289,9 @@ func labelsFromAWSKubeCluster(cluster *eks.Cluster, parsedARN arn.ARN) map[strin labels := awsEKSTagsToLabels(cluster.Tags) labels[types.OriginLabel] = types.OriginCloud labels[types.CloudLabel] = types.CloudAWS - labels[labelRegion] = parsedARN.Region + labels[types.DiscoveryLabelRegion] = parsedARN.Region - labels[labelAccountID] = parsedARN.AccountID + labels[types.DiscoveryLabelAccountID] = parsedARN.AccountID return labels } @@ -308,10 +307,3 @@ func awsEKSTagsToLabels(tags map[string]*string) map[string]string { } return labels } - -const ( - // labelProjectID is the label key for GCP project ID. - labelProjectID = "project-id" - // labelLocation is the label key for GCP location. - labelLocation = "location" -) diff --git a/lib/services/kubernetes_test.go b/lib/services/kubernetes_test.go index 5926a1f410c46..eaed30dc3b36c 100644 --- a/lib/services/kubernetes_test.go +++ b/lib/services/kubernetes_test.go @@ -19,9 +19,15 @@ package services import ( "testing" + "cloud.google.com/go/container/apiv1/containerpb" + "github.com/aws/aws-sdk-go/aws" + "github.com/aws/aws-sdk-go/service/eks" + "github.com/google/go-cmp/cmp" "github.com/stretchr/testify/require" "github.com/gravitational/teleport/api/types" + "github.com/gravitational/teleport/lib/cloud/azure" + "github.com/gravitational/teleport/lib/cloud/gcp" "github.com/gravitational/teleport/lib/utils" ) @@ -77,6 +83,136 @@ func TestKubernetesServerMarshal(t *testing.T) { require.Equal(t, expected, actual) } +func TestNewKubeClusterFromAWSEKS(t *testing.T) { + for _, overrideLabel := range types.AWSKubeClusterNameOverrideLabels { + t.Run("with name override via "+overrideLabel, func(t *testing.T) { + expected, err := types.NewKubernetesClusterV3(types.Metadata{ + Name: "override-1", + Description: `AWS EKS cluster "cluster1" in eu-west-1`, + Labels: map[string]string{ + types.DiscoveryLabelAccountID: "123456789012", + types.DiscoveryLabelRegion: "eu-west-1", + types.OriginLabel: types.OriginCloud, + types.CloudLabel: types.CloudAWS, + overrideLabel: "override-1", + "env": "prod", + }, + }, types.KubernetesClusterSpecV3{ + AWS: types.KubeAWS{ + Name: "cluster1", + Region: "eu-west-1", + AccountID: "123456789012", + }, + }) + require.NoError(t, err) + + cluster := &eks.Cluster{ + Name: aws.String("cluster1"), + Arn: aws.String("arn:aws:eks:eu-west-1:123456789012:cluster/cluster1"), + Status: aws.String(eks.ClusterStatusActive), + Tags: map[string]*string{ + overrideLabel: aws.String("override-1"), + "env": aws.String("prod"), + }, + } + actual, err := NewKubeClusterFromAWSEKS(cluster) + require.NoError(t, err) + require.Empty(t, cmp.Diff(expected, actual)) + require.NoError(t, err) + require.True(t, actual.IsAWS()) + require.False(t, actual.IsAzure()) + require.False(t, actual.IsGCP()) + }) + } +} + +func TestNewKubeClusterFromAzureAKS(t *testing.T) { + overrideLabel := types.AzureKubeClusterNameOverrideLabel + expected, err := types.NewKubernetesClusterV3(types.Metadata{ + Name: "override-1", + Description: `Azure AKS cluster "aks-cluster1" in uswest1`, + Labels: map[string]string{ + types.DiscoveryLabelRegion: "uswest1", + types.DiscoveryLabelAzureResourceGroup: "group1", + types.DiscoveryLabelAzureSubscriptionID: "subID", + types.OriginLabel: types.OriginCloud, + types.CloudLabel: types.CloudAzure, + overrideLabel: "override-1", + "env": "prod", + }, + }, types.KubernetesClusterSpecV3{ + Azure: types.KubeAzure{ + ResourceName: "aks-cluster1", + ResourceGroup: "group1", + TenantID: "tenantID", + SubscriptionID: "subID", + }, + }) + require.NoError(t, err) + + cluster := &azure.AKSCluster{ + Name: "aks-cluster1", + GroupName: "group1", + TenantID: "tenantID", + Location: "uswest1", + SubscriptionID: "subID", + Tags: map[string]string{ + "env": "prod", + overrideLabel: "override-1", + }, + Properties: azure.AKSClusterProperties{}, + } + actual, err := NewKubeClusterFromAzureAKS(cluster) + require.NoError(t, err) + require.Empty(t, cmp.Diff(expected, actual)) + require.NoError(t, err) + require.True(t, actual.IsAzure()) + require.False(t, actual.IsGCP()) + require.False(t, actual.IsAWS()) +} + +func TestNewKubeClusterFromGCPGKE(t *testing.T) { + overrideLabel := types.GCPKubeClusterNameOverrideLabel + expected, err := types.NewKubernetesClusterV3(types.Metadata{ + Name: "override-1", + Description: "desc1", + Labels: map[string]string{ + types.DiscoveryLabelGCPLocation: "central-1", + types.DiscoveryLabelGCPProjectID: "p1", + types.OriginLabel: types.OriginCloud, + types.CloudLabel: types.CloudGCP, + overrideLabel: "override-1", + "env": "prod", + }, + }, types.KubernetesClusterSpecV3{ + GCP: types.KubeGCP{ + Name: "cluster1", + ProjectID: "p1", + Location: "central-1", + }, + }) + require.NoError(t, err) + + cluster := gcp.GKECluster{ + Name: "cluster1", + Status: containerpb.Cluster_RUNNING, + Labels: map[string]string{ + overrideLabel: "override-1", + "env": "prod", + }, + ProjectID: "p1", + Location: "central-1", + Description: "desc1", + } + actual, err := NewKubeClusterFromGCPGKE(cluster) + require.NoError(t, err) + require.Empty(t, cmp.Diff(expected, actual)) + require.NoError(t, err) + require.True(t, actual.IsGCP()) + require.False(t, actual.IsAzure()) + require.False(t, actual.IsAWS()) +} + var kubeServerYAML = `--- kind: kube_server version: v3 diff --git a/lib/services/resource.go b/lib/services/resource.go index 016c9be72f3df..b249fcee7c852 100644 --- a/lib/services/resource.go +++ b/lib/services/resource.go @@ -638,3 +638,22 @@ func (u *UnknownResource) UnmarshalJSON(raw []byte) error { copy(u.Raw, raw) return nil } + +// setResourceName modifies the types.Metadata argument in place, setting the resource name. +// The name is calculated based on nameParts arguments which are joined by hyphens "-". +// If a name override label is present, it will replace the *first* name part. +func setResourceName(overrideLabels []string, meta types.Metadata, firstNamePart string, extraNameParts ...string) types.Metadata { + nameParts := append([]string{firstNamePart}, extraNameParts...) + + // apply override + for _, overrideLabel := range overrideLabels { + if override, found := meta.Labels[overrideLabel]; found && override != "" { + nameParts[0] = override + break + } + } + + meta.Name = strings.Join(nameParts, "-") + + return meta +} diff --git a/lib/services/resource_test.go b/lib/services/resource_test.go index 1714ccc76e99d..87f1cf7edcb54 100644 --- a/lib/services/resource_test.go +++ b/lib/services/resource_test.go @@ -176,3 +176,59 @@ func TestParseShortcut(t *testing.T) { }) } } + +func Test_setResourceName(t *testing.T) { + tests := []struct { + name string + meta types.Metadata + overrideLabels []string + 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 by generic cloud label, one part name", + meta: types.Metadata{Labels: map[string]string{types.AWSDatabaseNameOverrideLabels[0]: "gizmo"}}, + overrideLabels: types.AWSDatabaseNameOverrideLabels, + firstNamePart: "foo", + extraNameParts: nil, + want: types.Metadata{Name: "gizmo", Labels: map[string]string{types.AWSDatabaseNameOverrideLabels[0]: "gizmo"}}, + }, + { + name: "override by original AWS label, one part name", + meta: types.Metadata{Labels: map[string]string{types.AWSDatabaseNameOverrideLabels[1]: "gizmo"}}, + overrideLabels: types.AWSDatabaseNameOverrideLabels, + firstNamePart: "foo", + extraNameParts: nil, + want: types.Metadata{Name: "gizmo", Labels: map[string]string{types.AWSDatabaseNameOverrideLabels[1]: "gizmo"}}, + }, + { + name: "override, multi part name", + meta: types.Metadata{Labels: map[string]string{types.AzureDatabaseNameOverrideLabel: "gizmo"}}, + overrideLabels: []string{types.AzureDatabaseNameOverrideLabel}, + firstNamePart: "foo", + extraNameParts: []string{"bar", "baz"}, + want: types.Metadata{Name: "gizmo-bar-baz", Labels: map[string]string{types.AzureDatabaseNameOverrideLabel: "gizmo"}}, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + result := setResourceName(tt.overrideLabels, tt.meta, tt.firstNamePart, tt.extraNameParts...) + require.Equal(t, tt.want, result) + }) + } +} diff --git a/lib/srv/desktop/discovery.go b/lib/srv/desktop/discovery.go index c2bf630524b78..2feb6ec4c9e65 100644 --- a/lib/srv/desktop/discovery.go +++ b/lib/srv/desktop/discovery.go @@ -154,29 +154,29 @@ func (s *WindowsService) deleteDesktop(ctx context.Context, r types.ResourceWith func (s *WindowsService) applyLabelsFromLDAP(entry *ldap.Entry, labels map[string]string) { // apply common LDAP labels by default labels[types.OriginLabel] = types.OriginDynamic - labels[types.TeleportNamespace+"/dns_host_name"] = entry.GetAttributeValue(windows.AttrDNSHostName) - labels[types.TeleportNamespace+"/computer_name"] = entry.GetAttributeValue(windows.AttrName) - labels[types.TeleportNamespace+"/os"] = entry.GetAttributeValue(windows.AttrOS) - labels[types.TeleportNamespace+"/os_version"] = entry.GetAttributeValue(windows.AttrOSVersion) + labels[types.DiscoveryLabelWindowsDNSHostName] = entry.GetAttributeValue(windows.AttrDNSHostName) + labels[types.DiscoveryLabelWindowsComputerName] = entry.GetAttributeValue(windows.AttrName) + labels[types.DiscoveryLabelWindowsOS] = entry.GetAttributeValue(windows.AttrOS) + labels[types.DiscoveryLabelWindowsOSVersion] = entry.GetAttributeValue(windows.AttrOSVersion) // attempt to compute the desktop's OU from its DN dn := entry.GetAttributeValue(windows.AttrDistinguishedName) cn := entry.GetAttributeValue(windows.AttrCommonName) if len(dn) > 0 && len(cn) > 0 { ou := strings.TrimPrefix(dn, "CN="+cn+",") - labels[types.TeleportNamespace+"/ou"] = ou + labels[types.DiscoveryLabelWindowsOU] = ou } // label domain controllers switch entry.GetAttributeValue(windows.AttrPrimaryGroupID) { case windows.WritableDomainControllerGroupID, windows.ReadOnlyDomainControllerGroupID: - labels[types.TeleportNamespace+"/is_domain_controller"] = "true" + labels[types.DiscoveryLabelWindowsIsDomainController] = "true" } // apply any custom labels per the discovery configuration for _, attr := range s.cfg.DiscoveryLDAPAttributeLabels { if v := entry.GetAttributeValue(attr); v != "" { - labels["ldap/"+attr] = v + labels[types.DiscoveryLabelLDAPPrefix+attr] = v } } } @@ -208,7 +208,7 @@ func (s *WindowsService) ldapEntryToWindowsDesktop(ctx context.Context, entry *l return nil, trace.BadParameter("LDAP entry missing hostname, has attributes: %v", entry.Attributes) } labels := getHostLabels(hostname) - labels[types.TeleportNamespace+"/windows_domain"] = s.cfg.Domain + labels[types.DiscoveryLabelWindowsDomain] = s.cfg.Domain s.applyLabelsFromLDAP(entry, labels) addrs, err := s.lookupDesktop(ctx, hostname) diff --git a/lib/srv/desktop/discovery_test.go b/lib/srv/desktop/discovery_test.go index 483cfd5ac6f55..dda1b6c4d97a9 100644 --- a/lib/srv/desktop/discovery_test.go +++ b/lib/srv/desktop/discovery_test.go @@ -84,13 +84,13 @@ func TestAppliesLDAPLabels(t *testing.T) { // check default labels require.Equal(t, l[types.OriginLabel], types.OriginDynamic) - require.Equal(t, l[types.TeleportNamespace+"/dns_host_name"], "foo.example.com") - require.Equal(t, l[types.TeleportNamespace+"/computer_name"], "foo") - require.Equal(t, l[types.TeleportNamespace+"/os"], "Windows Server") - require.Equal(t, l[types.TeleportNamespace+"/os_version"], "6.1") + require.Equal(t, l[types.DiscoveryLabelWindowsDNSHostName], "foo.example.com") + require.Equal(t, l[types.DiscoveryLabelWindowsComputerName], "foo") + require.Equal(t, l[types.DiscoveryLabelWindowsOS], "Windows Server") + require.Equal(t, l[types.DiscoveryLabelWindowsOSVersion], "6.1") // check OU label - require.Equal(t, l[types.TeleportNamespace+"/ou"], "OU=IT,DC=goteleport,DC=com") + require.Equal(t, l[types.DiscoveryLabelWindowsOU], "OU=IT,DC=goteleport,DC=com") // check custom labels require.Equal(t, l["ldap/bar"], "baz") @@ -130,7 +130,7 @@ func TestLabelsDomainControllers(t *testing.T) { l := make(map[string]string) s.applyLabelsFromLDAP(test.entry, l) - b, _ := strconv.ParseBool(l[types.TeleportNamespace+"/is_domain_controller"]) + b, _ := strconv.ParseBool(l[types.DiscoveryLabelWindowsIsDomainController]) test.assert(t, b) }) } diff --git a/web/packages/teleport/src/Discover/Database/CreateDatabase/useCreateDatabase.ts b/web/packages/teleport/src/Discover/Database/CreateDatabase/useCreateDatabase.ts index dd31f215e3732..dec9612e9f7f9 100644 --- a/web/packages/teleport/src/Discover/Database/CreateDatabase/useCreateDatabase.ts +++ b/web/packages/teleport/src/Discover/Database/CreateDatabase/useCreateDatabase.ts @@ -221,7 +221,7 @@ export function useCreateDatabase() { const preErrMsg = 'failed to register database: '; const nonAwsMsg = `use a different name and try again`; const awsMsg = `change (or define) the value of the \ - tag "teleport.dev/database_name" on the RDS instance and try again`; + tag "TeleportDatabaseName" on the RDS instance and try again`; try { await ctx.databaseService.fetchDatabase(clusterId, dbName);