mirror of
https://github.com/gravitational/teleport.git
synced 2026-10-12 02:15:01 +00:00
Amazon DocumentDB access part 1 (#43867)
* Amazon DocumentDB access part 1 * fix lint * make derive
This commit is contained in:
@@ -466,6 +466,12 @@ message AWS {
|
||||
IAMPolicyStatus IAMPolicyStatus = 14 [(gogoproto.jsontag) = "iam_policy_status"];
|
||||
// SessionTags is a list of AWS STS session tags.
|
||||
map<string, string> SessionTags = 15 [(gogoproto.jsontag) = "session_tags,omitempty"];
|
||||
|
||||
// DocumentDB contains AWS DocumentDB specific metadata.
|
||||
DocumentDB DocumentDB = 16 [
|
||||
(gogoproto.nullable) = false,
|
||||
(gogoproto.jsontag) = "docdb,omitempty"
|
||||
];
|
||||
}
|
||||
|
||||
// SecretStore contains secret store configurations.
|
||||
@@ -552,6 +558,16 @@ message OpenSearch {
|
||||
string EndpointType = 3 [(gogoproto.jsontag) = "endpoint_type,omitempty"];
|
||||
}
|
||||
|
||||
// DocumentDB contains AWS DocumentDB specific metadata.
|
||||
message DocumentDB {
|
||||
// ClusterID is the cluster identifier.
|
||||
string ClusterID = 1 [(gogoproto.jsontag) = "cluster_id,omitempty"];
|
||||
// InstanceID is the instance identifier.
|
||||
string InstanceID = 2 [(gogoproto.jsontag) = "instance_id,omitempty"];
|
||||
// EndpointType is the type of the endpoint.
|
||||
string EndpointType = 3 [(gogoproto.jsontag) = "endpoint_type,omitempty"];
|
||||
}
|
||||
|
||||
// GCPCloudSQL contains parameters specific to GCP Cloud SQL databases.
|
||||
message GCPCloudSQL {
|
||||
// ProjectID is the GCP project ID the Cloud SQL instance resides in.
|
||||
|
||||
+26
-1
@@ -584,6 +584,9 @@ func (d *DatabaseV3) getAWSType() (string, bool) {
|
||||
if aws.RDSProxy.Name != "" || aws.RDSProxy.CustomEndpointName != "" {
|
||||
return DatabaseTypeRDSProxy, true
|
||||
}
|
||||
if aws.DocumentDB.ClusterID != "" || aws.DocumentDB.InstanceID != "" {
|
||||
return DatabaseTypeDocumentDB, true
|
||||
}
|
||||
if aws.Region != "" || aws.RDS.InstanceID != "" || aws.RDS.ResourceID != "" || aws.RDS.ClusterID != "" {
|
||||
return DatabaseTypeRDS, true
|
||||
}
|
||||
@@ -811,6 +814,23 @@ func (d *DatabaseV3) CheckAndSetDefaults() error {
|
||||
d.Spec.AWS.MemoryDB.TLSEnabled = endpointInfo.TransitEncryptionEnabled
|
||||
d.Spec.AWS.MemoryDB.EndpointType = endpointInfo.EndpointType
|
||||
|
||||
case awsutils.IsDocumentDBEndpoint(d.Spec.URI):
|
||||
endpointInfo, err := awsutils.ParseDocumentDBEndpoint(d.Spec.URI)
|
||||
if err != nil {
|
||||
slog.WarnContext(context.Background(), "Failed to parse DocumentDB endpoint.", "uri", d.Spec.URI, "error", err)
|
||||
break
|
||||
}
|
||||
if d.Spec.AWS.DocumentDB.ClusterID == "" {
|
||||
d.Spec.AWS.DocumentDB.ClusterID = endpointInfo.ClusterID
|
||||
}
|
||||
if d.Spec.AWS.DocumentDB.InstanceID == "" {
|
||||
d.Spec.AWS.DocumentDB.InstanceID = endpointInfo.InstanceID
|
||||
}
|
||||
if d.Spec.AWS.Region == "" {
|
||||
d.Spec.AWS.Region = endpointInfo.Region
|
||||
}
|
||||
d.Spec.AWS.DocumentDB.EndpointType = endpointInfo.EndpointType
|
||||
|
||||
case azureutils.IsDatabaseEndpoint(d.Spec.URI):
|
||||
// For Azure MySQL and PostgresSQL.
|
||||
name, err := azureutils.ParseDatabaseEndpoint(d.Spec.URI)
|
||||
@@ -1046,7 +1066,8 @@ func (d *DatabaseV3) RequireAWSIAMRolesAsUsers() bool {
|
||||
case DatabaseTypeAWSKeyspaces,
|
||||
DatabaseTypeDynamoDB,
|
||||
DatabaseTypeOpenSearch,
|
||||
DatabaseTypeRedshiftServerless:
|
||||
DatabaseTypeRedshiftServerless,
|
||||
DatabaseTypeDocumentDB:
|
||||
return true
|
||||
default:
|
||||
return false
|
||||
@@ -1093,6 +1114,8 @@ func (d *DatabaseV3) GetEndpointType() string {
|
||||
if details, err := awsutils.ParseRDSEndpoint(d.GetURI()); err == nil {
|
||||
return details.EndpointType
|
||||
}
|
||||
case DatabaseTypeDocumentDB:
|
||||
return d.GetAWS().DocumentDB.EndpointType
|
||||
}
|
||||
return ""
|
||||
}
|
||||
@@ -1149,6 +1172,8 @@ const (
|
||||
DatabaseTypeOpenSearch = "opensearch"
|
||||
// DatabaseTypeMongoAtlas
|
||||
DatabaseTypeMongoAtlas = "mongo-atlas"
|
||||
// DatabaseTypeDocumentDB is the database type for AWS-hosted DocumentDB.
|
||||
DatabaseTypeDocumentDB = "docdb"
|
||||
)
|
||||
|
||||
// GetServerName returns the GCP database project and instance as "<project-id>:<instance-id>".
|
||||
|
||||
+109
-99
@@ -34,7 +34,8 @@ func deriveTeleportEqualAWS(this, that *AWS) bool {
|
||||
this.AssumeRoleARN == that.AssumeRoleARN &&
|
||||
deriveTeleportEqual_7(&this.OpenSearch, &that.OpenSearch) &&
|
||||
this.IAMPolicyStatus == that.IAMPolicyStatus &&
|
||||
deriveTeleportEqual_8(this.SessionTags, that.SessionTags)
|
||||
deriveTeleportEqual_8(this.SessionTags, that.SessionTags) &&
|
||||
deriveTeleportEqual_9(&this.DocumentDB, &that.DocumentDB)
|
||||
}
|
||||
|
||||
// deriveTeleportEqualGCPCloudSQL returns whether this and that are equal.
|
||||
@@ -51,7 +52,7 @@ func deriveTeleportEqualAzure(this, that *Azure) bool {
|
||||
this != nil && that != nil &&
|
||||
this.Name == that.Name &&
|
||||
this.ResourceID == that.ResourceID &&
|
||||
deriveTeleportEqual_9(&this.Redis, &that.Redis) &&
|
||||
deriveTeleportEqual_10(&this.Redis, &that.Redis) &&
|
||||
this.IsFlexiServer == that.IsFlexiServer
|
||||
}
|
||||
|
||||
@@ -63,7 +64,7 @@ func deriveTeleportEqualDatabaseV3(this, that *DatabaseV3) bool {
|
||||
this.SubKind == that.SubKind &&
|
||||
this.Version == that.Version &&
|
||||
deriveTeleportEqualMetadata(&this.Metadata, &that.Metadata) &&
|
||||
deriveTeleportEqual_10(&this.Spec, &that.Spec)
|
||||
deriveTeleportEqual_11(&this.Spec, &that.Spec)
|
||||
}
|
||||
|
||||
// deriveTeleportEqualWindowsDesktopV3 returns whether this and that are equal.
|
||||
@@ -71,7 +72,7 @@ func deriveTeleportEqualWindowsDesktopV3(this, that *WindowsDesktopV3) bool {
|
||||
return (this == nil && that == nil) ||
|
||||
this != nil && that != nil &&
|
||||
deriveTeleportEqualResourceHeader(&this.ResourceHeader, &that.ResourceHeader) &&
|
||||
deriveTeleportEqual_11(&this.Spec, &that.Spec)
|
||||
deriveTeleportEqual_12(&this.Spec, &that.Spec)
|
||||
}
|
||||
|
||||
// deriveTeleportEqualKubeAzure returns whether this and that are equal.
|
||||
@@ -110,7 +111,7 @@ func deriveTeleportEqualKubernetesClusterV3(this, that *KubernetesClusterV3) boo
|
||||
this.SubKind == that.SubKind &&
|
||||
this.Version == that.Version &&
|
||||
deriveTeleportEqualMetadata(&this.Metadata, &that.Metadata) &&
|
||||
deriveTeleportEqual_12(&this.Spec, &that.Spec)
|
||||
deriveTeleportEqual_13(&this.Spec, &that.Spec)
|
||||
}
|
||||
|
||||
// deriveTeleportEqualKubernetesServerV3 returns whether this and that are equal.
|
||||
@@ -121,7 +122,7 @@ func deriveTeleportEqualKubernetesServerV3(this, that *KubernetesServerV3) bool
|
||||
this.SubKind == that.SubKind &&
|
||||
this.Version == that.Version &&
|
||||
deriveTeleportEqualMetadata(&this.Metadata, &that.Metadata) &&
|
||||
deriveTeleportEqual_13(&this.Spec, &that.Spec)
|
||||
deriveTeleportEqual_14(&this.Spec, &that.Spec)
|
||||
}
|
||||
|
||||
// deriveTeleportEqualOktaAssignmentV1 returns whether this and that are equal.
|
||||
@@ -129,7 +130,7 @@ func deriveTeleportEqualOktaAssignmentV1(this, that *OktaAssignmentV1) bool {
|
||||
return (this == nil && that == nil) ||
|
||||
this != nil && that != nil &&
|
||||
deriveTeleportEqualResourceHeader(&this.ResourceHeader, &that.ResourceHeader) &&
|
||||
deriveTeleportEqual_14(&this.Spec, &that.Spec)
|
||||
deriveTeleportEqual_15(&this.Spec, &that.Spec)
|
||||
}
|
||||
|
||||
// deriveTeleportEqualResourceHeader returns whether this and that are equal.
|
||||
@@ -158,7 +159,7 @@ func deriveTeleportEqualUserGroupV1(this, that *UserGroupV1) bool {
|
||||
return (this == nil && that == nil) ||
|
||||
this != nil && that != nil &&
|
||||
deriveTeleportEqualResourceHeader(&this.ResourceHeader, &that.ResourceHeader) &&
|
||||
deriveTeleportEqual_15(&this.Spec, &that.Spec)
|
||||
deriveTeleportEqual_16(&this.Spec, &that.Spec)
|
||||
}
|
||||
|
||||
// deriveTeleportEqual returns whether this and that are equal.
|
||||
@@ -167,12 +168,12 @@ func deriveTeleportEqual(this, that *AppSpecV3) bool {
|
||||
this != nil && that != nil &&
|
||||
this.URI == that.URI &&
|
||||
this.PublicAddr == that.PublicAddr &&
|
||||
deriveTeleportEqual_16(this.DynamicLabels, that.DynamicLabels) &&
|
||||
deriveTeleportEqual_17(this.DynamicLabels, that.DynamicLabels) &&
|
||||
this.InsecureSkipVerify == that.InsecureSkipVerify &&
|
||||
deriveTeleportEqual_17(this.Rewrite, that.Rewrite) &&
|
||||
deriveTeleportEqual_18(this.AWS, that.AWS) &&
|
||||
deriveTeleportEqual_18(this.Rewrite, that.Rewrite) &&
|
||||
deriveTeleportEqual_19(this.AWS, that.AWS) &&
|
||||
this.Cloud == that.Cloud &&
|
||||
deriveTeleportEqual_19(this.UserGroups, that.UserGroups) &&
|
||||
deriveTeleportEqual_20(this.UserGroups, that.UserGroups) &&
|
||||
this.Integration == that.Integration
|
||||
}
|
||||
|
||||
@@ -191,7 +192,7 @@ func deriveTeleportEqual_1(this, that *RDS) bool {
|
||||
this.ClusterID == that.ClusterID &&
|
||||
this.ResourceID == that.ResourceID &&
|
||||
this.IAMAuth == that.IAMAuth &&
|
||||
deriveTeleportEqual_19(this.Subnets, that.Subnets) &&
|
||||
deriveTeleportEqual_20(this.Subnets, that.Subnets) &&
|
||||
this.VPCID == that.VPCID
|
||||
}
|
||||
|
||||
@@ -200,7 +201,7 @@ func deriveTeleportEqual_2(this, that *ElastiCache) bool {
|
||||
return (this == nil && that == nil) ||
|
||||
this != nil && that != nil &&
|
||||
this.ReplicationGroupID == that.ReplicationGroupID &&
|
||||
deriveTeleportEqual_19(this.UserGroupIDs, that.UserGroupIDs) &&
|
||||
deriveTeleportEqual_20(this.UserGroupIDs, that.UserGroupIDs) &&
|
||||
this.TransitEncryptionEnabled == that.TransitEncryptionEnabled &&
|
||||
this.EndpointType == that.EndpointType
|
||||
}
|
||||
@@ -271,86 +272,95 @@ func deriveTeleportEqual_8(this, that map[string]string) bool {
|
||||
}
|
||||
|
||||
// deriveTeleportEqual_9 returns whether this and that are equal.
|
||||
func deriveTeleportEqual_9(this, that *AzureRedis) bool {
|
||||
func deriveTeleportEqual_9(this, that *DocumentDB) bool {
|
||||
return (this == nil && that == nil) ||
|
||||
this != nil && that != nil &&
|
||||
this.ClusterID == that.ClusterID &&
|
||||
this.InstanceID == that.InstanceID &&
|
||||
this.EndpointType == that.EndpointType
|
||||
}
|
||||
|
||||
// deriveTeleportEqual_10 returns whether this and that are equal.
|
||||
func deriveTeleportEqual_10(this, that *AzureRedis) bool {
|
||||
return (this == nil && that == nil) ||
|
||||
this != nil && that != nil &&
|
||||
this.ClusteringPolicy == that.ClusteringPolicy
|
||||
}
|
||||
|
||||
// deriveTeleportEqual_10 returns whether this and that are equal.
|
||||
func deriveTeleportEqual_10(this, that *DatabaseSpecV3) bool {
|
||||
// deriveTeleportEqual_11 returns whether this and that are equal.
|
||||
func deriveTeleportEqual_11(this, that *DatabaseSpecV3) bool {
|
||||
return (this == nil && that == nil) ||
|
||||
this != nil && that != nil &&
|
||||
this.Protocol == that.Protocol &&
|
||||
this.URI == that.URI &&
|
||||
this.CACert == that.CACert &&
|
||||
deriveTeleportEqual_16(this.DynamicLabels, that.DynamicLabels) &&
|
||||
deriveTeleportEqual_17(this.DynamicLabels, that.DynamicLabels) &&
|
||||
deriveTeleportEqualAWS(&this.AWS, &that.AWS) &&
|
||||
deriveTeleportEqualGCPCloudSQL(&this.GCP, &that.GCP) &&
|
||||
deriveTeleportEqualAzure(&this.Azure, &that.Azure) &&
|
||||
deriveTeleportEqual_20(&this.TLS, &that.TLS) &&
|
||||
deriveTeleportEqual_21(&this.AD, &that.AD) &&
|
||||
deriveTeleportEqual_22(&this.MySQL, &that.MySQL) &&
|
||||
deriveTeleportEqual_23(this.AdminUser, that.AdminUser) &&
|
||||
deriveTeleportEqual_24(&this.MongoAtlas, &that.MongoAtlas) &&
|
||||
deriveTeleportEqual_25(&this.Oracle, &that.Oracle)
|
||||
deriveTeleportEqual_21(&this.TLS, &that.TLS) &&
|
||||
deriveTeleportEqual_22(&this.AD, &that.AD) &&
|
||||
deriveTeleportEqual_23(&this.MySQL, &that.MySQL) &&
|
||||
deriveTeleportEqual_24(this.AdminUser, that.AdminUser) &&
|
||||
deriveTeleportEqual_25(&this.MongoAtlas, &that.MongoAtlas) &&
|
||||
deriveTeleportEqual_26(&this.Oracle, &that.Oracle)
|
||||
}
|
||||
|
||||
// deriveTeleportEqual_11 returns whether this and that are equal.
|
||||
func deriveTeleportEqual_11(this, that *WindowsDesktopSpecV3) bool {
|
||||
// deriveTeleportEqual_12 returns whether this and that are equal.
|
||||
func deriveTeleportEqual_12(this, that *WindowsDesktopSpecV3) bool {
|
||||
return (this == nil && that == nil) ||
|
||||
this != nil && that != nil &&
|
||||
this.Addr == that.Addr &&
|
||||
this.Domain == that.Domain &&
|
||||
this.HostID == that.HostID &&
|
||||
this.NonAD == that.NonAD &&
|
||||
deriveTeleportEqual_26(this.ScreenSize, that.ScreenSize)
|
||||
deriveTeleportEqual_27(this.ScreenSize, that.ScreenSize)
|
||||
}
|
||||
|
||||
// deriveTeleportEqual_12 returns whether this and that are equal.
|
||||
func deriveTeleportEqual_12(this, that *KubernetesClusterSpecV3) bool {
|
||||
// deriveTeleportEqual_13 returns whether this and that are equal.
|
||||
func deriveTeleportEqual_13(this, that *KubernetesClusterSpecV3) bool {
|
||||
return (this == nil && that == nil) ||
|
||||
this != nil && that != nil &&
|
||||
deriveTeleportEqual_16(this.DynamicLabels, that.DynamicLabels) &&
|
||||
deriveTeleportEqual_17(this.DynamicLabels, that.DynamicLabels) &&
|
||||
bytes.Equal(this.Kubeconfig, that.Kubeconfig) &&
|
||||
deriveTeleportEqualKubeAzure(&this.Azure, &that.Azure) &&
|
||||
deriveTeleportEqualKubeAWS(&this.AWS, &that.AWS) &&
|
||||
deriveTeleportEqualKubeGCP(&this.GCP, &that.GCP)
|
||||
}
|
||||
|
||||
// deriveTeleportEqual_13 returns whether this and that are equal.
|
||||
func deriveTeleportEqual_13(this, that *KubernetesServerSpecV3) bool {
|
||||
// deriveTeleportEqual_14 returns whether this and that are equal.
|
||||
func deriveTeleportEqual_14(this, that *KubernetesServerSpecV3) bool {
|
||||
return (this == nil && that == nil) ||
|
||||
this != nil && that != nil &&
|
||||
this.Version == that.Version &&
|
||||
this.Hostname == that.Hostname &&
|
||||
this.HostID == that.HostID &&
|
||||
deriveTeleportEqual_27(&this.Rotation, &that.Rotation) &&
|
||||
deriveTeleportEqual_28(&this.Rotation, &that.Rotation) &&
|
||||
deriveTeleportEqualKubernetesClusterV3(this.Cluster, that.Cluster) &&
|
||||
deriveTeleportEqual_19(this.ProxyIDs, that.ProxyIDs)
|
||||
deriveTeleportEqual_20(this.ProxyIDs, that.ProxyIDs)
|
||||
}
|
||||
|
||||
// deriveTeleportEqual_14 returns whether this and that are equal.
|
||||
func deriveTeleportEqual_14(this, that *OktaAssignmentSpecV1) bool {
|
||||
// deriveTeleportEqual_15 returns whether this and that are equal.
|
||||
func deriveTeleportEqual_15(this, that *OktaAssignmentSpecV1) bool {
|
||||
return (this == nil && that == nil) ||
|
||||
this != nil && that != nil &&
|
||||
this.User == that.User &&
|
||||
deriveTeleportEqual_28(this.Targets, that.Targets) &&
|
||||
deriveTeleportEqual_29(this.Targets, that.Targets) &&
|
||||
this.CleanupTime.Equal(that.CleanupTime) &&
|
||||
this.Status == that.Status &&
|
||||
this.LastTransition.Equal(that.LastTransition) &&
|
||||
this.Finalized == that.Finalized
|
||||
}
|
||||
|
||||
// deriveTeleportEqual_15 returns whether this and that are equal.
|
||||
func deriveTeleportEqual_15(this, that *UserGroupSpecV1) bool {
|
||||
// deriveTeleportEqual_16 returns whether this and that are equal.
|
||||
func deriveTeleportEqual_16(this, that *UserGroupSpecV1) bool {
|
||||
return (this == nil && that == nil) ||
|
||||
this != nil && that != nil &&
|
||||
deriveTeleportEqual_19(this.Applications, that.Applications)
|
||||
deriveTeleportEqual_20(this.Applications, that.Applications)
|
||||
}
|
||||
|
||||
// deriveTeleportEqual_16 returns whether this and that are equal.
|
||||
func deriveTeleportEqual_16(this, that map[string]CommandLabelV2) bool {
|
||||
// deriveTeleportEqual_17 returns whether this and that are equal.
|
||||
func deriveTeleportEqual_17(this, that map[string]CommandLabelV2) bool {
|
||||
if this == nil || that == nil {
|
||||
return this == nil && that == nil
|
||||
}
|
||||
@@ -362,31 +372,31 @@ func deriveTeleportEqual_16(this, that map[string]CommandLabelV2) bool {
|
||||
if !ok {
|
||||
return false
|
||||
}
|
||||
if !(deriveTeleportEqual_29(&v, &thatv)) {
|
||||
if !(deriveTeleportEqual_30(&v, &thatv)) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
// deriveTeleportEqual_17 returns whether this and that are equal.
|
||||
func deriveTeleportEqual_17(this, that *Rewrite) bool {
|
||||
// deriveTeleportEqual_18 returns whether this and that are equal.
|
||||
func deriveTeleportEqual_18(this, that *Rewrite) bool {
|
||||
return (this == nil && that == nil) ||
|
||||
this != nil && that != nil &&
|
||||
deriveTeleportEqual_19(this.Redirect, that.Redirect) &&
|
||||
deriveTeleportEqual_30(this.Headers, that.Headers) &&
|
||||
deriveTeleportEqual_20(this.Redirect, that.Redirect) &&
|
||||
deriveTeleportEqual_31(this.Headers, that.Headers) &&
|
||||
this.JWTClaims == that.JWTClaims
|
||||
}
|
||||
|
||||
// deriveTeleportEqual_18 returns whether this and that are equal.
|
||||
func deriveTeleportEqual_18(this, that *AppAWS) bool {
|
||||
// deriveTeleportEqual_19 returns whether this and that are equal.
|
||||
func deriveTeleportEqual_19(this, that *AppAWS) bool {
|
||||
return (this == nil && that == nil) ||
|
||||
this != nil && that != nil &&
|
||||
this.ExternalID == that.ExternalID
|
||||
}
|
||||
|
||||
// deriveTeleportEqual_19 returns whether this and that are equal.
|
||||
func deriveTeleportEqual_19(this, that []string) bool {
|
||||
// deriveTeleportEqual_20 returns whether this and that are equal.
|
||||
func deriveTeleportEqual_20(this, that []string) bool {
|
||||
if this == nil || that == nil {
|
||||
return this == nil && that == nil
|
||||
}
|
||||
@@ -401,8 +411,8 @@ func deriveTeleportEqual_19(this, that []string) bool {
|
||||
return true
|
||||
}
|
||||
|
||||
// deriveTeleportEqual_20 returns whether this and that are equal.
|
||||
func deriveTeleportEqual_20(this, that *DatabaseTLS) bool {
|
||||
// deriveTeleportEqual_21 returns whether this and that are equal.
|
||||
func deriveTeleportEqual_21(this, that *DatabaseTLS) bool {
|
||||
return (this == nil && that == nil) ||
|
||||
this != nil && that != nil &&
|
||||
this.Mode == that.Mode &&
|
||||
@@ -411,8 +421,8 @@ func deriveTeleportEqual_20(this, that *DatabaseTLS) bool {
|
||||
this.TrustSystemCertPool == that.TrustSystemCertPool
|
||||
}
|
||||
|
||||
// deriveTeleportEqual_21 returns whether this and that are equal.
|
||||
func deriveTeleportEqual_21(this, that *AD) bool {
|
||||
// deriveTeleportEqual_22 returns whether this and that are equal.
|
||||
func deriveTeleportEqual_22(this, that *AD) bool {
|
||||
return (this == nil && that == nil) ||
|
||||
this != nil && that != nil &&
|
||||
this.KeytabFile == that.KeytabFile &&
|
||||
@@ -423,45 +433,45 @@ func deriveTeleportEqual_21(this, that *AD) bool {
|
||||
this.KDCHostName == that.KDCHostName
|
||||
}
|
||||
|
||||
// deriveTeleportEqual_22 returns whether this and that are equal.
|
||||
func deriveTeleportEqual_22(this, that *MySQLOptions) bool {
|
||||
// deriveTeleportEqual_23 returns whether this and that are equal.
|
||||
func deriveTeleportEqual_23(this, that *MySQLOptions) bool {
|
||||
return (this == nil && that == nil) ||
|
||||
this != nil && that != nil &&
|
||||
this.ServerVersion == that.ServerVersion
|
||||
}
|
||||
|
||||
// deriveTeleportEqual_23 returns whether this and that are equal.
|
||||
func deriveTeleportEqual_23(this, that *DatabaseAdminUser) bool {
|
||||
// deriveTeleportEqual_24 returns whether this and that are equal.
|
||||
func deriveTeleportEqual_24(this, that *DatabaseAdminUser) bool {
|
||||
return (this == nil && that == nil) ||
|
||||
this != nil && that != nil &&
|
||||
this.Name == that.Name &&
|
||||
this.DefaultDatabase == that.DefaultDatabase
|
||||
}
|
||||
|
||||
// deriveTeleportEqual_24 returns whether this and that are equal.
|
||||
func deriveTeleportEqual_24(this, that *MongoAtlas) bool {
|
||||
// deriveTeleportEqual_25 returns whether this and that are equal.
|
||||
func deriveTeleportEqual_25(this, that *MongoAtlas) bool {
|
||||
return (this == nil && that == nil) ||
|
||||
this != nil && that != nil &&
|
||||
this.Name == that.Name
|
||||
}
|
||||
|
||||
// deriveTeleportEqual_25 returns whether this and that are equal.
|
||||
func deriveTeleportEqual_25(this, that *OracleOptions) bool {
|
||||
// deriveTeleportEqual_26 returns whether this and that are equal.
|
||||
func deriveTeleportEqual_26(this, that *OracleOptions) bool {
|
||||
return (this == nil && that == nil) ||
|
||||
this != nil && that != nil &&
|
||||
this.AuditUser == that.AuditUser
|
||||
}
|
||||
|
||||
// deriveTeleportEqual_26 returns whether this and that are equal.
|
||||
func deriveTeleportEqual_26(this, that *Resolution) bool {
|
||||
// deriveTeleportEqual_27 returns whether this and that are equal.
|
||||
func deriveTeleportEqual_27(this, that *Resolution) bool {
|
||||
return (this == nil && that == nil) ||
|
||||
this != nil && that != nil &&
|
||||
this.Width == that.Width &&
|
||||
this.Height == that.Height
|
||||
}
|
||||
|
||||
// deriveTeleportEqual_27 returns whether this and that are equal.
|
||||
func deriveTeleportEqual_27(this, that *Rotation) bool {
|
||||
// deriveTeleportEqual_28 returns whether this and that are equal.
|
||||
func deriveTeleportEqual_28(this, that *Rotation) bool {
|
||||
return (this == nil && that == nil) ||
|
||||
this != nil && that != nil &&
|
||||
this.State == that.State &&
|
||||
@@ -471,36 +481,11 @@ func deriveTeleportEqual_27(this, that *Rotation) bool {
|
||||
this.Started.Equal(that.Started) &&
|
||||
this.GracePeriod == that.GracePeriod &&
|
||||
this.LastRotated.Equal(that.LastRotated) &&
|
||||
deriveTeleportEqual_31(&this.Schedule, &that.Schedule)
|
||||
}
|
||||
|
||||
// deriveTeleportEqual_28 returns whether this and that are equal.
|
||||
func deriveTeleportEqual_28(this, that []*OktaAssignmentTargetV1) bool {
|
||||
if this == nil || that == nil {
|
||||
return this == nil && that == nil
|
||||
}
|
||||
if len(this) != len(that) {
|
||||
return false
|
||||
}
|
||||
for i := 0; i < len(this); i++ {
|
||||
if !(deriveTeleportEqual_32(this[i], that[i])) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
deriveTeleportEqual_32(&this.Schedule, &that.Schedule)
|
||||
}
|
||||
|
||||
// deriveTeleportEqual_29 returns whether this and that are equal.
|
||||
func deriveTeleportEqual_29(this, that *CommandLabelV2) bool {
|
||||
return (this == nil && that == nil) ||
|
||||
this != nil && that != nil &&
|
||||
this.Period == that.Period &&
|
||||
deriveTeleportEqual_19(this.Command, that.Command) &&
|
||||
this.Result == that.Result
|
||||
}
|
||||
|
||||
// deriveTeleportEqual_30 returns whether this and that are equal.
|
||||
func deriveTeleportEqual_30(this, that []*Header) bool {
|
||||
func deriveTeleportEqual_29(this, that []*OktaAssignmentTargetV1) bool {
|
||||
if this == nil || that == nil {
|
||||
return this == nil && that == nil
|
||||
}
|
||||
@@ -515,8 +500,33 @@ func deriveTeleportEqual_30(this, that []*Header) bool {
|
||||
return true
|
||||
}
|
||||
|
||||
// deriveTeleportEqual_30 returns whether this and that are equal.
|
||||
func deriveTeleportEqual_30(this, that *CommandLabelV2) bool {
|
||||
return (this == nil && that == nil) ||
|
||||
this != nil && that != nil &&
|
||||
this.Period == that.Period &&
|
||||
deriveTeleportEqual_20(this.Command, that.Command) &&
|
||||
this.Result == that.Result
|
||||
}
|
||||
|
||||
// deriveTeleportEqual_31 returns whether this and that are equal.
|
||||
func deriveTeleportEqual_31(this, that *RotationSchedule) bool {
|
||||
func deriveTeleportEqual_31(this, that []*Header) bool {
|
||||
if this == nil || that == nil {
|
||||
return this == nil && that == nil
|
||||
}
|
||||
if len(this) != len(that) {
|
||||
return false
|
||||
}
|
||||
for i := 0; i < len(this); i++ {
|
||||
if !(deriveTeleportEqual_34(this[i], that[i])) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
// deriveTeleportEqual_32 returns whether this and that are equal.
|
||||
func deriveTeleportEqual_32(this, that *RotationSchedule) bool {
|
||||
return (this == nil && that == nil) ||
|
||||
this != nil && that != nil &&
|
||||
this.UpdateClients.Equal(that.UpdateClients) &&
|
||||
@@ -524,16 +534,16 @@ func deriveTeleportEqual_31(this, that *RotationSchedule) bool {
|
||||
this.Standby.Equal(that.Standby)
|
||||
}
|
||||
|
||||
// deriveTeleportEqual_32 returns whether this and that are equal.
|
||||
func deriveTeleportEqual_32(this, that *OktaAssignmentTargetV1) bool {
|
||||
// deriveTeleportEqual_33 returns whether this and that are equal.
|
||||
func deriveTeleportEqual_33(this, that *OktaAssignmentTargetV1) bool {
|
||||
return (this == nil && that == nil) ||
|
||||
this != nil && that != nil &&
|
||||
this.Type == that.Type &&
|
||||
this.Id == that.Id
|
||||
}
|
||||
|
||||
// deriveTeleportEqual_33 returns whether this and that are equal.
|
||||
func deriveTeleportEqual_33(this, that *Header) bool {
|
||||
// deriveTeleportEqual_34 returns whether this and that are equal.
|
||||
func deriveTeleportEqual_34(this, that *Header) bool {
|
||||
return (this == nil && that == nil) ||
|
||||
this != nil && that != nil &&
|
||||
this.Name == that.Name &&
|
||||
|
||||
+2535
-2217
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,140 @@
|
||||
/*
|
||||
Copyright 2024 Gravitational, Inc.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package aws
|
||||
|
||||
import (
|
||||
"net/url"
|
||||
"strings"
|
||||
|
||||
"github.com/gravitational/trace"
|
||||
)
|
||||
|
||||
// IsDocumentDBEndpoint returns true if the input URI is a DocumentDB endpoint.
|
||||
//
|
||||
// https://docs.aws.amazon.com/documentdb/latest/developerguide/endpoints.html
|
||||
func IsDocumentDBEndpoint(uri string) bool {
|
||||
return isAWSServiceEndpoint(uri, DocumentDBServiceName)
|
||||
}
|
||||
|
||||
// DocumentDBEndpointDetails contains information about a DocumentDB endpoint.
|
||||
type DocumentDBEndpointDetails struct {
|
||||
// ClusterID is the identifier of a DocumentDB cluster.
|
||||
ClusterID string
|
||||
// InstanceID is the identifier of a DocumentDB instance.
|
||||
InstanceID string
|
||||
// Region is the AWS region for the endpoint.
|
||||
Region string
|
||||
// EndpointType specifies the type of the endpoint.
|
||||
EndpointType string
|
||||
}
|
||||
|
||||
// ParseDocumentDBEndpoint parses and extracts info from the provided
|
||||
// DocumentDB endpoint.
|
||||
func ParseDocumentDBEndpoint(endpoint string) (*DocumentDBEndpointDetails, error) {
|
||||
if !strings.HasPrefix(endpoint, "mongodb+srv://") &&
|
||||
!strings.HasPrefix(endpoint, "mongodb://") {
|
||||
endpoint = "mongodb://" + endpoint
|
||||
}
|
||||
|
||||
docdbURL, err := url.Parse(endpoint)
|
||||
if err != nil {
|
||||
return nil, trace.Wrap(err)
|
||||
}
|
||||
|
||||
endpoint = docdbURL.Hostname()
|
||||
|
||||
if strings.HasSuffix(endpoint, AWSCNEndpointSuffix) {
|
||||
return parseDocumentDBCNEndpoint(endpoint)
|
||||
}
|
||||
if strings.HasSuffix(endpoint, AWSEndpointSuffix) {
|
||||
return parseDocumentDBEndpoint(endpoint)
|
||||
}
|
||||
return nil, trace.BadParameter("failed to parse %v as DocumentDB endpoint", endpoint)
|
||||
}
|
||||
|
||||
func parseDocumentDBCNEndpoint(endpoint string) (*DocumentDBEndpointDetails, error) {
|
||||
// Example:
|
||||
// my-documentdb-cluster-id.cluster-abcdefghijklmnop.docdb.cn-north-1.amazonaws.com.cn
|
||||
parts := strings.Split(strings.TrimSuffix(endpoint, AWSCNEndpointSuffix), ".")
|
||||
if len(parts) != 4 {
|
||||
return nil, trace.BadParameter("failed to parse %v as DocumentDB CN endpoint", endpoint)
|
||||
}
|
||||
if parts[2] != DocumentDBServiceName {
|
||||
return nil, trace.BadParameter("failed to parse %v as DocumentDB CN endpoint", endpoint)
|
||||
}
|
||||
return makeDocumentDBDetails(parts[0], parts[1], parts[3]), nil
|
||||
}
|
||||
|
||||
func parseDocumentDBEndpoint(endpoint string) (*DocumentDBEndpointDetails, error) {
|
||||
// Examples:
|
||||
// my-documentdb-cluster-id.cluster-abcdefghijklmnop.us-east-1.docdb.amazonaws.com
|
||||
// my-documentdb-cluster-id.cluster-ro-abcdefghijklmnop.us-east-1.docdb.amazonaws.com
|
||||
// my-instance-id.abcdefghijklmnop.us-east-1.docdb.amazonaws.com
|
||||
parts := strings.Split(strings.TrimSuffix(endpoint, AWSEndpointSuffix), ".")
|
||||
if len(parts) != 4 {
|
||||
return nil, trace.BadParameter("failed to parse %v as DocumentDB endpoint", endpoint)
|
||||
}
|
||||
if parts[3] != DocumentDBServiceName {
|
||||
return nil, trace.BadParameter("failed to parse %v as DocumentDB endpoint", endpoint)
|
||||
}
|
||||
return makeDocumentDBDetails(parts[0], parts[1], parts[2]), nil
|
||||
}
|
||||
|
||||
func makeDocumentDBDetails(id, endpointTypePart, region string) *DocumentDBEndpointDetails {
|
||||
endpointType := guessDocumentDBEndpointType(endpointTypePart)
|
||||
if endpointType == DocumentDBInstanceEndpoint {
|
||||
return &DocumentDBEndpointDetails{
|
||||
InstanceID: id,
|
||||
Region: region,
|
||||
EndpointType: endpointType,
|
||||
}
|
||||
}
|
||||
|
||||
return &DocumentDBEndpointDetails{
|
||||
ClusterID: id,
|
||||
Region: region,
|
||||
EndpointType: endpointType,
|
||||
}
|
||||
}
|
||||
|
||||
func guessDocumentDBEndpointType(endpointTypePart string) string {
|
||||
switch {
|
||||
case strings.HasPrefix(endpointTypePart, "cluster-ro-"):
|
||||
return DocumentDBClusterReaderEndpoint
|
||||
case strings.HasPrefix(endpointTypePart, "cluster-"):
|
||||
return DocumentDBClusterEndpoint
|
||||
default:
|
||||
return DocumentDBInstanceEndpoint
|
||||
}
|
||||
}
|
||||
|
||||
const (
|
||||
// DocumentDBServiceName is the service name for AWS DocumentDB.
|
||||
//
|
||||
// TODO(greedy52) support DocumentDB Elastic clusters when IAM Auth support
|
||||
// is added. Note that Elastic clusters use "docdb-elastic" as the service
|
||||
// name in the endpoint.
|
||||
DocumentDBServiceName = "docdb"
|
||||
|
||||
// DocumentDBClusterEndpoint specifies a DocumentDB primary/cluster
|
||||
// endpoint.
|
||||
DocumentDBClusterEndpoint = "cluster"
|
||||
// DocumentDBReaderEndpoint specifies a DocumentDB reader endpoint.
|
||||
DocumentDBClusterReaderEndpoint = "reader"
|
||||
// DocumentDBInstanceEndpoint specifies a DocumentDB instance endpoint.
|
||||
DocumentDBInstanceEndpoint = "instance"
|
||||
)
|
||||
@@ -0,0 +1,109 @@
|
||||
/*
|
||||
Copyright 2024 Gravitational, Inc.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package aws
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/gravitational/trace"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func TestParseDocumentDBEndpoint(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
endpoint string
|
||||
expectIsDocumentDBEndpoint bool
|
||||
expectDetails *DocumentDBEndpointDetails
|
||||
expectParseErrorIs func(error) bool
|
||||
}{
|
||||
{
|
||||
name: "DocuementDB cluster",
|
||||
endpoint: "my-documentdb-cluster-id.cluster-abcdefghijklmnop.us-east-1.docdb.amazonaws.com:27017",
|
||||
expectIsDocumentDBEndpoint: true,
|
||||
expectDetails: &DocumentDBEndpointDetails{
|
||||
ClusterID: "my-documentdb-cluster-id",
|
||||
Region: "us-east-1",
|
||||
EndpointType: "cluster",
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "DocuementDB cluster in mongo URL format",
|
||||
endpoint: "mongodb://my-documentdb-cluster-id.cluster-abcdefghijklmnop.us-east-1.docdb.amazonaws.com:27017/?retryWrites=false",
|
||||
expectIsDocumentDBEndpoint: true,
|
||||
expectDetails: &DocumentDBEndpointDetails{
|
||||
ClusterID: "my-documentdb-cluster-id",
|
||||
Region: "us-east-1",
|
||||
EndpointType: "cluster",
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "DocuementDB cluster reader",
|
||||
endpoint: "my-documentdb-cluster-id.cluster-ro-abcdefghijklmnop.us-east-1.docdb.amazonaws.com",
|
||||
expectIsDocumentDBEndpoint: true,
|
||||
expectDetails: &DocumentDBEndpointDetails{
|
||||
ClusterID: "my-documentdb-cluster-id",
|
||||
Region: "us-east-1",
|
||||
EndpointType: "reader",
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "DocuementDB instance",
|
||||
endpoint: "my-instance-id.abcdefghijklmnop.us-east-1.docdb.amazonaws.com",
|
||||
expectIsDocumentDBEndpoint: true,
|
||||
expectDetails: &DocumentDBEndpointDetails{
|
||||
InstanceID: "my-instance-id",
|
||||
Region: "us-east-1",
|
||||
EndpointType: "instance",
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "DocuementDB CN",
|
||||
endpoint: "my-documentdb-cluster-id.cluster-abcdefghijklmnop.docdb.cn-north-1.amazonaws.com.cn",
|
||||
expectIsDocumentDBEndpoint: true,
|
||||
expectDetails: &DocumentDBEndpointDetails{
|
||||
ClusterID: "my-documentdb-cluster-id",
|
||||
Region: "cn-north-1",
|
||||
EndpointType: "cluster",
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "RDS instance fails",
|
||||
endpoint: "aurora-instance-1.abcdefghijklmnop.us-west-1.rds.amazonaws.com:5432",
|
||||
expectIsDocumentDBEndpoint: false,
|
||||
expectParseErrorIs: trace.IsBadParameter,
|
||||
},
|
||||
}
|
||||
|
||||
for _, test := range tests {
|
||||
test := test
|
||||
t.Run(test.name, func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
require.Equal(t, test.expectIsDocumentDBEndpoint, IsDocumentDBEndpoint(test.endpoint))
|
||||
|
||||
actualDetails, err := ParseDocumentDBEndpoint(test.endpoint)
|
||||
if test.expectParseErrorIs != nil {
|
||||
require.Error(t, err)
|
||||
require.True(t, test.expectParseErrorIs(err))
|
||||
} else {
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, test.expectDetails, actualDetails)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -62,7 +62,7 @@ type VnetServiceClient interface {
|
||||
// be fetched (due to e.g., a network error or an expired cert).
|
||||
ListDNSZones(ctx context.Context, in *ListDNSZonesRequest, opts ...grpc.CallOption) (*ListDNSZonesResponse, error)
|
||||
// GetBackgroundItemStatus returns the status of the background item responsible for launching
|
||||
// VNet daemon. macOS only.
|
||||
// VNet daemon. macOS only. tsh must be compiled with the vnetdaemon build tag.
|
||||
GetBackgroundItemStatus(ctx context.Context, in *GetBackgroundItemStatusRequest, opts ...grpc.CallOption) (*GetBackgroundItemStatusResponse, error)
|
||||
}
|
||||
|
||||
@@ -135,7 +135,7 @@ type VnetServiceServer interface {
|
||||
// be fetched (due to e.g., a network error or an expired cert).
|
||||
ListDNSZones(context.Context, *ListDNSZonesRequest) (*ListDNSZonesResponse, error)
|
||||
// GetBackgroundItemStatus returns the status of the background item responsible for launching
|
||||
// VNet daemon. macOS only.
|
||||
// VNet daemon. macOS only. tsh must be compiled with the vnetdaemon build tag.
|
||||
GetBackgroundItemStatus(context.Context, *GetBackgroundItemStatusRequest) (*GetBackgroundItemStatusResponse, error)
|
||||
mustEmbedUnimplementedVnetServiceServer()
|
||||
}
|
||||
|
||||
@@ -563,7 +563,7 @@ func testKubeGatewayCertRenewal(ctx context.Context, t *testing.T, params kubeGa
|
||||
func checkKubeconfigPathInCommandEnv(t *testing.T, daemonService *daemon.Service, gw gateway.Gateway, wantKubeconfigPath string) {
|
||||
t.Helper()
|
||||
|
||||
cmds, err := daemonService.GetGatewayCLICommand(gw)
|
||||
cmds, err := daemonService.GetGatewayCLICommand(context.Background(), gw)
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, []string{"KUBECONFIG=" + wantKubeconfigPath}, cmds.Exec.Env)
|
||||
require.Equal(t, []string{"KUBECONFIG=" + wantKubeconfigPath}, cmds.Preview.Env)
|
||||
|
||||
@@ -19,6 +19,7 @@
|
||||
package dbcmd
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"net/url"
|
||||
"os"
|
||||
@@ -173,7 +174,7 @@ func NewCmdBuilder(tc *client.TeleportClient, profile *client.ProfileStatus,
|
||||
// If CLICommandBuilder's options.tolerateMissingCLIClient is set to true, GetConnectCommand
|
||||
// shouldn't return an error if it cannot locate a client binary. Check WithTolerateMissingCLIClient
|
||||
// docs for more details.
|
||||
func (c *CLICommandBuilder) GetConnectCommand() (*exec.Cmd, error) {
|
||||
func (c *CLICommandBuilder) GetConnectCommand(ctx context.Context) (*exec.Cmd, error) {
|
||||
switch c.db.Protocol {
|
||||
case defaults.ProtocolPostgres:
|
||||
return c.getPostgresCommand(), nil
|
||||
@@ -185,7 +186,7 @@ func (c *CLICommandBuilder) GetConnectCommand() (*exec.Cmd, error) {
|
||||
return c.getMySQLCommand()
|
||||
|
||||
case defaults.ProtocolMongoDB:
|
||||
return c.getMongoCommand(), nil
|
||||
return c.getMongoCommand(ctx)
|
||||
|
||||
case defaults.ProtocolRedis:
|
||||
return c.getRedisCommand(), nil
|
||||
@@ -232,8 +233,7 @@ type CommandAlternative struct {
|
||||
// GetConnectCommandAlternatives returns optional connection commands for protocols that offer multiple options.
|
||||
// Otherwise, it falls back to GetConnectCommand.
|
||||
// The keys in the returned map are command descriptions suitable for display to the end user.
|
||||
func (c *CLICommandBuilder) GetConnectCommandAlternatives() ([]CommandAlternative, error) {
|
||||
|
||||
func (c *CLICommandBuilder) GetConnectCommandAlternatives(ctx context.Context) ([]CommandAlternative, error) {
|
||||
switch c.db.Protocol {
|
||||
case defaults.ProtocolElasticsearch:
|
||||
return c.getElasticsearchAlternativeCommands(), nil
|
||||
@@ -241,7 +241,7 @@ func (c *CLICommandBuilder) GetConnectCommandAlternatives() ([]CommandAlternativ
|
||||
return c.getOpenSearchAlternativeCommands(), nil
|
||||
}
|
||||
|
||||
cmd, err := c.GetConnectCommand()
|
||||
cmd, err := c.GetConnectCommand(ctx)
|
||||
if err != nil {
|
||||
return nil, trace.Wrap(err)
|
||||
}
|
||||
@@ -464,16 +464,25 @@ func (c *CLICommandBuilder) isSqlcmdAvailable() bool {
|
||||
return c.isBinAvailable(sqlcmdBin)
|
||||
}
|
||||
|
||||
func (c *CLICommandBuilder) shouldUseMongoshBin() bool {
|
||||
func (c *CLICommandBuilder) shouldUseMongoshBin(db types.Database) bool {
|
||||
// DocumentDB prefers the legacy "mongo" client.
|
||||
if db.GetType() == types.DatabaseTypeDocumentDB {
|
||||
return c.isMongoshBinAvailable() && !c.isBinAvailable(mongoBin)
|
||||
}
|
||||
// Use "mongosh" if available.
|
||||
// If not, use legacy "mongo" if available.
|
||||
// If both are not available, pick "mongosh" in print out.
|
||||
return c.isMongoshBinAvailable() || !c.isBinAvailable(mongoBin)
|
||||
}
|
||||
|
||||
func (c *CLICommandBuilder) getMongoCommand() *exec.Cmd {
|
||||
func (c *CLICommandBuilder) getMongoCommand(ctx context.Context) (*exec.Cmd, error) {
|
||||
db, err := c.getDatabase(ctx)
|
||||
if err != nil {
|
||||
return nil, trace.Wrap(err)
|
||||
}
|
||||
|
||||
// look for `mongosh`
|
||||
useMongosh := c.shouldUseMongoshBin()
|
||||
useMongosh := c.shouldUseMongoshBin(db)
|
||||
|
||||
var args []string
|
||||
|
||||
@@ -519,11 +528,27 @@ func (c *CLICommandBuilder) getMongoCommand() *exec.Cmd {
|
||||
|
||||
// use `mongosh` if available
|
||||
if useMongosh {
|
||||
return exec.Command(mongoshBin, args...)
|
||||
// DocumentDB does not support retryable writes.
|
||||
// https://docs.aws.amazon.com/documentdb/latest/developerguide/functional-differences.html#functional-differences.retryable-writes
|
||||
if db.GetType() == types.DatabaseTypeDocumentDB {
|
||||
args = append(args, "--retryWrites=false")
|
||||
}
|
||||
|
||||
return exec.Command(mongoshBin, args...), nil
|
||||
}
|
||||
|
||||
// fall back to `mongo` if `mongosh` isn't found
|
||||
return exec.Command(mongoBin, args...)
|
||||
return exec.Command(mongoBin, args...), nil
|
||||
}
|
||||
|
||||
func (c *CLICommandBuilder) getDatabase(ctx context.Context) (types.Database, error) {
|
||||
// Technically, we can just use tc to get the database. But caller may have
|
||||
// extra logic so rely on the callback for now.
|
||||
if c.options.getDatabase == nil {
|
||||
return nil, trace.NotFound("missing GetDatabaseFunc")
|
||||
}
|
||||
db, err := c.options.getDatabase(ctx, c.tc, c.db.ServiceName)
|
||||
return db, trace.Wrap(err)
|
||||
}
|
||||
|
||||
func (c *CLICommandBuilder) getMongoAddress() string {
|
||||
@@ -881,6 +906,7 @@ type connectionCommandOpts struct {
|
||||
exe Execer
|
||||
password string
|
||||
gcp types.GCPCloudSQL
|
||||
getDatabase GetDatabaseFunc
|
||||
}
|
||||
|
||||
// ConnectCommandFunc is a type for functions returned by the "With*" functions in this package.
|
||||
@@ -971,12 +997,23 @@ func WithExecer(exe Execer) ConnectCommandFunc {
|
||||
}
|
||||
|
||||
// WithGCP adds GCP metadata for the database command to access.
|
||||
// TODO(greedy52) use GetDatabaseFunc instead.
|
||||
func WithGCP(gcp types.GCPCloudSQL) ConnectCommandFunc {
|
||||
return func(opts *connectionCommandOpts) {
|
||||
opts.gcp = gcp
|
||||
}
|
||||
}
|
||||
|
||||
// GetDatabaseFunc is a callback to retrieve types.Database.
|
||||
type GetDatabaseFunc func(context.Context, *client.TeleportClient, string) (types.Database, error)
|
||||
|
||||
// WithGetDatabaseFunc provides a callback to retrieve types.Database.
|
||||
func WithGetDatabaseFunc(f GetDatabaseFunc) ConnectCommandFunc {
|
||||
return func(opts *connectionCommandOpts) {
|
||||
opts.getDatabase = f
|
||||
}
|
||||
}
|
||||
|
||||
const (
|
||||
// envVarMongoServerSelectionTimeoutMS is the environment variable that
|
||||
// controls the server selection timeout used for MongoDB clients.
|
||||
|
||||
@@ -19,6 +19,7 @@
|
||||
package dbcmd
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"strings"
|
||||
"testing"
|
||||
@@ -322,6 +323,7 @@ func TestCLICommandBuilderGetConnectCommand(t *testing.T) {
|
||||
name: "mongodb (legacy)",
|
||||
dbProtocol: defaults.ProtocolMongoDB,
|
||||
databaseName: "mydb",
|
||||
opts: []ConnectCommandFunc{withMongoDBAtlasDatabase()},
|
||||
execer: &fakeExec{
|
||||
execOutput: map[string][]byte{
|
||||
"mongo": []byte("legacy"),
|
||||
@@ -338,7 +340,7 @@ func TestCLICommandBuilderGetConnectCommand(t *testing.T) {
|
||||
name: "mongodb no TLS (legacy)",
|
||||
dbProtocol: defaults.ProtocolMongoDB,
|
||||
databaseName: "mydb",
|
||||
opts: []ConnectCommandFunc{WithNoTLS()},
|
||||
opts: []ConnectCommandFunc{WithNoTLS(), withMongoDBAtlasDatabase()},
|
||||
execer: &fakeExec{
|
||||
execOutput: map[string][]byte{
|
||||
"mongo": []byte("legacy"),
|
||||
@@ -353,6 +355,7 @@ func TestCLICommandBuilderGetConnectCommand(t *testing.T) {
|
||||
name: "mongosh no CA",
|
||||
dbProtocol: defaults.ProtocolMongoDB,
|
||||
databaseName: "mydb",
|
||||
opts: []ConnectCommandFunc{withMongoDBAtlasDatabase()},
|
||||
execer: &fakeExec{
|
||||
execOutput: map[string][]byte{
|
||||
"mongosh": []byte("1.1.6"),
|
||||
@@ -370,7 +373,9 @@ func TestCLICommandBuilderGetConnectCommand(t *testing.T) {
|
||||
dbProtocol: defaults.ProtocolMongoDB,
|
||||
databaseName: "mydb",
|
||||
opts: []ConnectCommandFunc{
|
||||
WithLocalProxy("localhost", 12345, "/tmp/keys/example.com/cas/example.com.pem")},
|
||||
WithLocalProxy("localhost", 12345, "/tmp/keys/example.com/cas/example.com.pem"),
|
||||
withMongoDBAtlasDatabase(),
|
||||
},
|
||||
execer: &fakeExec{
|
||||
execOutput: map[string][]byte{
|
||||
"mongosh": []byte("1.1.6"),
|
||||
@@ -387,7 +392,7 @@ func TestCLICommandBuilderGetConnectCommand(t *testing.T) {
|
||||
name: "mongosh no TLS",
|
||||
dbProtocol: defaults.ProtocolMongoDB,
|
||||
databaseName: "mydb",
|
||||
opts: []ConnectCommandFunc{WithNoTLS()},
|
||||
opts: []ConnectCommandFunc{WithNoTLS(), withMongoDBAtlasDatabase()},
|
||||
execer: &fakeExec{
|
||||
execOutput: map[string][]byte{
|
||||
"mongosh": []byte("1.1.6"),
|
||||
@@ -401,7 +406,7 @@ func TestCLICommandBuilderGetConnectCommand(t *testing.T) {
|
||||
name: "mongosh preferred",
|
||||
dbProtocol: defaults.ProtocolMongoDB,
|
||||
databaseName: "mydb",
|
||||
opts: []ConnectCommandFunc{WithNoTLS()},
|
||||
opts: []ConnectCommandFunc{WithNoTLS(), withMongoDBAtlasDatabase()},
|
||||
execer: &fakeExec{
|
||||
execOutput: map[string][]byte{}, // Cannot find either bin.
|
||||
},
|
||||
@@ -409,6 +414,39 @@ func TestCLICommandBuilderGetConnectCommand(t *testing.T) {
|
||||
"mongodb://localhost:12345/mydb?serverSelectionTimeoutMS=5000",
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "DocumentDB",
|
||||
dbProtocol: defaults.ProtocolMongoDB,
|
||||
databaseName: "docdb",
|
||||
opts: []ConnectCommandFunc{WithNoTLS(), withDocumentDBDatabase()},
|
||||
execer: &fakeExec{
|
||||
execOutput: map[string][]byte{
|
||||
// When both are available, legacy mongo is preferred.
|
||||
"mongo": []byte("legacy"),
|
||||
"mongosh": []byte("1.1.6"),
|
||||
},
|
||||
},
|
||||
cmd: []string{"mongo",
|
||||
"mongodb://localhost:12345/docdb?serverSelectionTimeoutMS=5000",
|
||||
},
|
||||
wantErr: false,
|
||||
},
|
||||
{
|
||||
name: "DocumentDB mongosh",
|
||||
dbProtocol: defaults.ProtocolMongoDB,
|
||||
databaseName: "docdb",
|
||||
opts: []ConnectCommandFunc{WithNoTLS(), withDocumentDBDatabase()},
|
||||
execer: &fakeExec{
|
||||
execOutput: map[string][]byte{
|
||||
"mongosh": []byte("1.1.6"),
|
||||
},
|
||||
},
|
||||
cmd: []string{"mongosh",
|
||||
"mongodb://localhost:12345/docdb?serverSelectionTimeoutMS=5000",
|
||||
"--retryWrites=false",
|
||||
},
|
||||
wantErr: false,
|
||||
},
|
||||
{
|
||||
name: "sqlserver",
|
||||
dbProtocol: defaults.ProtocolSQLServer,
|
||||
@@ -749,7 +787,7 @@ func TestCLICommandBuilderGetConnectCommand(t *testing.T) {
|
||||
|
||||
c := NewCmdBuilder(tc, profile, database, "root", opts...)
|
||||
c.uid = utils.NewFakeUID()
|
||||
got, err := c.GetConnectCommand()
|
||||
got, err := c.GetConnectCommand(context.Background())
|
||||
if tt.wantErr {
|
||||
require.Error(t, err)
|
||||
return
|
||||
@@ -913,7 +951,7 @@ func TestCLICommandBuilderGetConnectCommandAlternatives(t *testing.T) {
|
||||
c := NewCmdBuilder(tc, profile, database, "root", opts...)
|
||||
c.uid = utils.NewFakeUID()
|
||||
|
||||
commandOptions, err := c.GetConnectCommandAlternatives()
|
||||
commandOptions, err := c.GetConnectCommandAlternatives(context.Background())
|
||||
if tt.wantErr {
|
||||
require.Error(t, err)
|
||||
return
|
||||
@@ -1001,7 +1039,7 @@ func TestConvertCommandError(t *testing.T) {
|
||||
c := NewCmdBuilder(tc, profile, database, "root", opts...)
|
||||
c.uid = utils.NewFakeUID()
|
||||
|
||||
cmd, err := c.GetConnectCommand()
|
||||
cmd, err := c.GetConnectCommand(context.Background())
|
||||
require.NoError(t, err)
|
||||
|
||||
// make sure the expected test bin is the command bin we got
|
||||
@@ -1016,3 +1054,33 @@ func TestConvertCommandError(t *testing.T) {
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func withMongoDBAtlasDatabase() ConnectCommandFunc {
|
||||
return WithGetDatabaseFunc(func(context.Context, *client.TeleportClient, string) (types.Database, error) {
|
||||
db, err := types.NewDatabaseV3(
|
||||
types.Metadata{
|
||||
Name: "mongodb-atlas",
|
||||
},
|
||||
types.DatabaseSpecV3{
|
||||
Protocol: types.DatabaseProtocolMongoDB,
|
||||
URI: "mongodb+srv://my-cluster.abcdefy.mongodb.net",
|
||||
},
|
||||
)
|
||||
return db, trace.Wrap(err)
|
||||
})
|
||||
}
|
||||
|
||||
func withDocumentDBDatabase() ConnectCommandFunc {
|
||||
return WithGetDatabaseFunc(func(context.Context, *client.TeleportClient, string) (types.Database, error) {
|
||||
db, err := types.NewDatabaseV3(
|
||||
types.Metadata{
|
||||
Name: "docdb",
|
||||
},
|
||||
types.DatabaseSpecV3{
|
||||
Protocol: types.DatabaseProtocolMongoDB,
|
||||
URI: "my-documentdb-cluster-id.cluster-abcdefghijklmnop.us-east-1.docdb.amazonaws.com:27017",
|
||||
},
|
||||
)
|
||||
return db, trace.Wrap(err)
|
||||
})
|
||||
}
|
||||
|
||||
+5
-3
@@ -101,6 +101,7 @@ func (s *Server) shouldInitCACertLocked(database types.Database) bool {
|
||||
// Can only download it for cloud-hosted instances.
|
||||
switch database.GetType() {
|
||||
case types.DatabaseTypeRDS,
|
||||
types.DatabaseTypeDocumentDB,
|
||||
types.DatabaseTypeRedshift,
|
||||
types.DatabaseTypeRedshiftServerless,
|
||||
types.DatabaseTypeElastiCache,
|
||||
@@ -164,7 +165,8 @@ func (s *Server) getCACertPaths(database types.Database) ([]string, error) {
|
||||
switch database.GetType() {
|
||||
// All RDS instances share the same root CA (per AWS region) which can be
|
||||
// downloaded from a well-known URL.
|
||||
case types.DatabaseTypeRDS:
|
||||
// DocumentDB uses same certs as RDS.
|
||||
case types.DatabaseTypeRDS, types.DatabaseTypeDocumentDB:
|
||||
return []string{filepath.Join(s.cfg.DataDir, filepath.Base(rdsCAURLForDatabase(database)))}, nil
|
||||
|
||||
// All Redshift instances share the same root CA which can be downloaded
|
||||
@@ -307,7 +309,7 @@ func NewRealDownloader() CADownloader {
|
||||
// Download downloads CA certificate for the provided cloud database instance.
|
||||
func (d *realDownloader) Download(ctx context.Context, database types.Database, hint string) ([]byte, []byte, error) {
|
||||
switch database.GetType() {
|
||||
case types.DatabaseTypeRDS:
|
||||
case types.DatabaseTypeRDS, types.DatabaseTypeDocumentDB:
|
||||
return d.downloadFromURL(rdsCAURLForDatabase(database))
|
||||
case types.DatabaseTypeRedshift,
|
||||
types.DatabaseTypeRedshiftServerless:
|
||||
@@ -336,7 +338,7 @@ func (d *realDownloader) Download(ctx context.Context, database types.Database,
|
||||
// GetVersion returns the CA version for the provided database.
|
||||
func (d *realDownloader) GetVersion(ctx context.Context, database types.Database, hint string) ([]byte, error) {
|
||||
switch database.GetType() {
|
||||
case types.DatabaseTypeRDS:
|
||||
case types.DatabaseTypeRDS, types.DatabaseTypeDocumentDB:
|
||||
return d.getVersionFromURL(database, rdsCAURLForDatabase(database))
|
||||
case types.DatabaseTypeRedshift,
|
||||
types.DatabaseTypeRedshiftServerless:
|
||||
|
||||
+29
-1
@@ -139,8 +139,31 @@ func TestInitCACert(t *testing.T) {
|
||||
})
|
||||
require.NoError(t, err)
|
||||
|
||||
docdb, err := types.NewDatabaseV3(types.Metadata{
|
||||
Name: "docdb",
|
||||
}, types.DatabaseSpecV3{
|
||||
Protocol: defaults.ProtocolMongoDB,
|
||||
URI: "localhost:27017",
|
||||
AWS: types.AWS{
|
||||
Region: "us-east-1",
|
||||
DocumentDB: types.DocumentDB{
|
||||
ClusterID: "docdb",
|
||||
},
|
||||
},
|
||||
})
|
||||
require.NoError(t, err)
|
||||
|
||||
allDatabases := []types.Database{
|
||||
selfHosted, rds, rdsWithCert, redshift, redshiftServerless, cloudSQL, azureMySQL, memoryDB, mongodbAtlas,
|
||||
selfHosted,
|
||||
rds,
|
||||
rdsWithCert,
|
||||
redshift,
|
||||
redshiftServerless,
|
||||
cloudSQL,
|
||||
azureMySQL,
|
||||
memoryDB,
|
||||
mongodbAtlas,
|
||||
docdb,
|
||||
}
|
||||
|
||||
tests := []struct {
|
||||
@@ -193,6 +216,11 @@ func TestInitCACert(t *testing.T) {
|
||||
database: mongodbAtlas.GetName(),
|
||||
cert: fixtures.TLSCACertPEM,
|
||||
},
|
||||
{
|
||||
desc: "should download DocumentDB CA when it's not set",
|
||||
database: docdb.GetName(),
|
||||
cert: fixtures.TLSCACertPEM,
|
||||
},
|
||||
}
|
||||
|
||||
databaseServer := testCtx.setupDatabaseServer(ctx, t, agentParams{
|
||||
|
||||
@@ -163,7 +163,8 @@ func (e *Engine) getConnectionOptions(ctx context.Context, sessionCtx *common.Se
|
||||
}
|
||||
|
||||
func (e *Engine) getAuthenticator(ctx context.Context, sessionCtx *common.Session) (auth.Authenticator, error) {
|
||||
isAtlasDB := sessionCtx.Database.GetType() == types.DatabaseTypeMongoAtlas
|
||||
dbType := sessionCtx.Database.GetType()
|
||||
isAtlasDB := dbType == types.DatabaseTypeMongoAtlas
|
||||
|
||||
// Currently, the MongoDB Atlas IAM Authentication doesn't work with IAM
|
||||
// users. Here we provide a better error message to the users.
|
||||
@@ -174,6 +175,9 @@ func (e *Engine) getAuthenticator(ctx context.Context, sessionCtx *common.Sessio
|
||||
switch {
|
||||
case isAtlasDB && awsutils.IsRoleARN(sessionCtx.DatabaseUser):
|
||||
return e.getAWSAuthenticator(ctx, sessionCtx)
|
||||
case dbType == types.DatabaseTypeDocumentDB:
|
||||
// DocumentDB uses the same the IAM authenticator as MongoDB Atlas.
|
||||
return e.getAWSAuthenticator(ctx, sessionCtx)
|
||||
default:
|
||||
e.Log.DebugContext(e.Context, "Authenticating to database using certificates.")
|
||||
authenticator, err := auth.CreateAuthenticator(auth.MongoDBX509, &auth.Cred{
|
||||
|
||||
@@ -45,7 +45,7 @@ func (s *Handler) CreateGateway(ctx context.Context, req *api.CreateGatewayReque
|
||||
return nil, trace.Wrap(err)
|
||||
}
|
||||
|
||||
apiGateway, err := s.newAPIGateway(gateway)
|
||||
apiGateway, err := s.newAPIGateway(ctx, gateway)
|
||||
if err != nil {
|
||||
return nil, trace.Wrap(err)
|
||||
}
|
||||
@@ -59,7 +59,7 @@ func (s *Handler) ListGateways(ctx context.Context, req *api.ListGatewaysRequest
|
||||
|
||||
apiGws := make([]*api.Gateway, 0, len(gws))
|
||||
for _, gw := range gws {
|
||||
apiGateway, err := s.newAPIGateway(gw)
|
||||
apiGateway, err := s.newAPIGateway(ctx, gw)
|
||||
if err != nil {
|
||||
return nil, trace.Wrap(err)
|
||||
}
|
||||
@@ -81,8 +81,8 @@ func (s *Handler) RemoveGateway(ctx context.Context, req *api.RemoveGatewayReque
|
||||
return &api.EmptyResponse{}, nil
|
||||
}
|
||||
|
||||
func (s *Handler) newAPIGateway(gateway gateway.Gateway) (*api.Gateway, error) {
|
||||
cmds, err := s.DaemonService.GetGatewayCLICommand(gateway)
|
||||
func (s *Handler) newAPIGateway(ctx context.Context, gateway gateway.Gateway) (*api.Gateway, error) {
|
||||
cmds, err := s.DaemonService.GetGatewayCLICommand(ctx, gateway)
|
||||
if err != nil {
|
||||
return nil, trace.Wrap(err)
|
||||
}
|
||||
@@ -124,7 +124,7 @@ func (s *Handler) SetGatewayTargetSubresourceName(ctx context.Context, req *api.
|
||||
return nil, trace.Wrap(err)
|
||||
}
|
||||
|
||||
apiGateway, err := s.newAPIGateway(gateway)
|
||||
apiGateway, err := s.newAPIGateway(ctx, gateway)
|
||||
if err != nil {
|
||||
return nil, trace.Wrap(err)
|
||||
}
|
||||
@@ -139,7 +139,7 @@ func (s *Handler) SetGatewayLocalPort(ctx context.Context, req *api.SetGatewayLo
|
||||
return nil, trace.Wrap(err)
|
||||
}
|
||||
|
||||
apiGateway, err := s.newAPIGateway(gateway)
|
||||
apiGateway, err := s.newAPIGateway(ctx, gateway)
|
||||
if err != nil {
|
||||
return nil, trace.Wrap(err)
|
||||
}
|
||||
|
||||
@@ -53,7 +53,7 @@ func (c *Cluster) GetDatabase(ctx context.Context, authClient authclient.ClientI
|
||||
dbName := dbURI.GetDbName()
|
||||
err := AddMetadataToRetryableError(ctx, func() error {
|
||||
databases, err := apiclient.GetAllResources[types.DatabaseServer](ctx, authClient, &proto.ListResourcesRequest{
|
||||
Namespace: c.clusterClient.Namespace,
|
||||
Namespace: defaults.Namespace,
|
||||
ResourceType: types.KindDatabaseServer,
|
||||
PredicateExpression: fmt.Sprintf(`name == "%s"`, dbName),
|
||||
})
|
||||
|
||||
+20
-5
@@ -19,10 +19,15 @@
|
||||
package cmd
|
||||
|
||||
import (
|
||||
"context"
|
||||
"os/exec"
|
||||
"sync"
|
||||
|
||||
"github.com/gravitational/trace"
|
||||
|
||||
"github.com/gravitational/teleport/api/types"
|
||||
"github.com/gravitational/teleport/lib/auth/authclient"
|
||||
"github.com/gravitational/teleport/lib/client"
|
||||
"github.com/gravitational/teleport/lib/client/db/dbcmd"
|
||||
"github.com/gravitational/teleport/lib/defaults"
|
||||
"github.com/gravitational/teleport/lib/teleterm/clusters"
|
||||
@@ -43,12 +48,12 @@ type Cmds struct {
|
||||
}
|
||||
|
||||
// NewDBCLICommand creates CLI commands for database gateway.
|
||||
func NewDBCLICommand(cluster *clusters.Cluster, gateway gateway.Gateway) (Cmds, error) {
|
||||
cmds, err := newDBCLICommandWithExecer(cluster, gateway, dbcmd.SystemExecer{})
|
||||
func NewDBCLICommand(ctx context.Context, cluster *clusters.Cluster, gateway gateway.Gateway, authClient authclient.ClientI) (Cmds, error) {
|
||||
cmds, err := newDBCLICommandWithExecer(ctx, cluster, gateway, dbcmd.SystemExecer{}, authClient)
|
||||
return cmds, trace.Wrap(err)
|
||||
}
|
||||
|
||||
func newDBCLICommandWithExecer(cluster *clusters.Cluster, gateway gateway.Gateway, execer dbcmd.Execer) (Cmds, error) {
|
||||
func newDBCLICommandWithExecer(ctx context.Context, cluster *clusters.Cluster, gateway gateway.Gateway, execer dbcmd.Execer, authClient authclient.ClientI) (Cmds, error) {
|
||||
routeToDb := tlsca.RouteToDatabase{
|
||||
ServiceName: gateway.TargetName(),
|
||||
Protocol: gateway.Protocol(),
|
||||
@@ -56,12 +61,22 @@ func newDBCLICommandWithExecer(cluster *clusters.Cluster, gateway gateway.Gatewa
|
||||
Database: gateway.TargetSubresourceName(),
|
||||
}
|
||||
|
||||
var getDatabaseOnce sync.Once
|
||||
var getDatabaseError error
|
||||
var database types.Database
|
||||
|
||||
opts := []dbcmd.ConnectCommandFunc{
|
||||
dbcmd.WithLogger(gateway.Log()),
|
||||
dbcmd.WithLocalProxy(gateway.LocalAddress(), gateway.LocalPortInt(), ""),
|
||||
dbcmd.WithNoTLS(),
|
||||
dbcmd.WithTolerateMissingCLIClient(),
|
||||
dbcmd.WithExecer(execer),
|
||||
dbcmd.WithGetDatabaseFunc(func(ctx context.Context, _ *client.TeleportClient, _ string) (types.Database, error) {
|
||||
getDatabaseOnce.Do(func() {
|
||||
database, getDatabaseError = cluster.GetDatabase(ctx, authClient, gateway.TargetURI())
|
||||
})
|
||||
return database, trace.Wrap(getDatabaseError)
|
||||
}),
|
||||
}
|
||||
|
||||
switch gateway.Protocol() {
|
||||
@@ -75,12 +90,12 @@ func newDBCLICommandWithExecer(cluster *clusters.Cluster, gateway gateway.Gatewa
|
||||
|
||||
previewOpts := append(opts, dbcmd.WithPrintFormat())
|
||||
|
||||
execCmd, err := clusters.NewDBCLICmdBuilder(cluster, routeToDb, opts...).GetConnectCommand()
|
||||
execCmd, err := clusters.NewDBCLICmdBuilder(cluster, routeToDb, opts...).GetConnectCommand(ctx)
|
||||
if err != nil {
|
||||
return Cmds{}, trace.Wrap(err)
|
||||
}
|
||||
|
||||
previewCmd, err := clusters.NewDBCLICmdBuilder(cluster, routeToDb, previewOpts...).GetConnectCommand()
|
||||
previewCmd, err := clusters.NewDBCLICmdBuilder(cluster, routeToDb, previewOpts...).GetConnectCommand(ctx)
|
||||
if err != nil {
|
||||
return Cmds{}, trace.Wrap(err)
|
||||
}
|
||||
|
||||
@@ -19,14 +19,19 @@
|
||||
package cmd
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"os/exec"
|
||||
"path/filepath"
|
||||
"testing"
|
||||
|
||||
"github.com/gravitational/trace"
|
||||
"github.com/sirupsen/logrus"
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
"github.com/gravitational/teleport/api/client/proto"
|
||||
"github.com/gravitational/teleport/api/types"
|
||||
"github.com/gravitational/teleport/lib/auth/authclient"
|
||||
"github.com/gravitational/teleport/lib/defaults"
|
||||
"github.com/gravitational/teleport/lib/teleterm/api/uri"
|
||||
"github.com/gravitational/teleport/lib/teleterm/clusters"
|
||||
@@ -67,6 +72,15 @@ func (m fakeDatabaseGateway) LocalPortInt() int { return 8888 }
|
||||
func (m fakeDatabaseGateway) LocalPort() string { return "8888" }
|
||||
|
||||
func TestNewDBCLICommand(t *testing.T) {
|
||||
// TODO mock other types
|
||||
authClient := &mockAuthClient{
|
||||
database: &types.DatabaseV3{
|
||||
Spec: types.DatabaseSpecV3{
|
||||
Protocol: types.DatabaseProtocolMongoDB,
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
testCases := []struct {
|
||||
name string
|
||||
targetSubresourceName string
|
||||
@@ -112,7 +126,7 @@ func TestNewDBCLICommand(t *testing.T) {
|
||||
protocol: tc.protocol,
|
||||
}
|
||||
|
||||
cmds, err := newDBCLICommandWithExecer(&cluster, mockGateway, fakeExec{})
|
||||
cmds, err := newDBCLICommandWithExecer(context.Background(), &cluster, mockGateway, fakeExec{}, authClient)
|
||||
require.NoError(t, err)
|
||||
|
||||
tc.checkCmds(t, mockGateway, cmds)
|
||||
@@ -120,6 +134,29 @@ func TestNewDBCLICommand(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
type mockAuthClient struct {
|
||||
authclient.ClientI
|
||||
database *types.DatabaseV3
|
||||
}
|
||||
|
||||
func (m *mockAuthClient) GetResources(_ context.Context, _ *proto.ListResourcesRequest) (*proto.ListResourcesResponse, error) {
|
||||
if m.database == nil {
|
||||
return nil, trace.NotFound("not found")
|
||||
}
|
||||
return &proto.ListResourcesResponse{
|
||||
Resources: []*proto.PaginatedResource{{
|
||||
Resource: &proto.PaginatedResource_DatabaseServer{
|
||||
DatabaseServer: &types.DatabaseServerV3{
|
||||
Spec: types.DatabaseServerSpecV3{
|
||||
Database: m.database,
|
||||
},
|
||||
},
|
||||
},
|
||||
}},
|
||||
TotalCount: 1,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func checkMongoCmds(t *testing.T, gw fakeDatabaseGateway, cmds Cmds) {
|
||||
t.Helper()
|
||||
require.Len(t, cmds.Exec.Args, 2)
|
||||
|
||||
@@ -479,7 +479,7 @@ func (s *Service) ListGateways() []gateway.Gateway {
|
||||
}
|
||||
|
||||
// GetGatewayCLICommand creates the CLI command used for the provided gateway.
|
||||
func (s *Service) GetGatewayCLICommand(gateway gateway.Gateway) (cmd.Cmds, error) {
|
||||
func (s *Service) GetGatewayCLICommand(ctx context.Context, gateway gateway.Gateway) (cmd.Cmds, error) {
|
||||
targetURI := gateway.TargetURI()
|
||||
switch {
|
||||
case targetURI.IsDB():
|
||||
@@ -488,7 +488,12 @@ func (s *Service) GetGatewayCLICommand(gateway gateway.Gateway) (cmd.Cmds, error
|
||||
return cmd.Cmds{}, trace.Wrap(err)
|
||||
}
|
||||
|
||||
cmds, err := cmd.NewDBCLICommand(cluster, gateway)
|
||||
clusterClient, err := s.GetCachedClient(ctx, cluster.URI)
|
||||
if err != nil {
|
||||
return cmd.Cmds{}, trace.Wrap(err)
|
||||
}
|
||||
|
||||
cmds, err := cmd.NewDBCLICommand(ctx, cluster, gateway, clusterClient.AuthClient)
|
||||
return cmds, trace.Wrap(err)
|
||||
|
||||
case targetURI.IsKube():
|
||||
|
||||
@@ -24,6 +24,7 @@ import (
|
||||
"net"
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"strings"
|
||||
"sync/atomic"
|
||||
"testing"
|
||||
"time"
|
||||
@@ -675,10 +676,8 @@ func TestGetGatewayCLICommand(t *testing.T) {
|
||||
checkError: require.NoError,
|
||||
checkCmds: func(t *testing.T, cmds cmd.Cmds) {
|
||||
t.Helper()
|
||||
require.Len(t, cmds.Exec.Args, 2)
|
||||
require.Contains(t, cmds.Exec.Args[1], "subresource-name")
|
||||
require.Len(t, cmds.Preview.Args, 2)
|
||||
require.Contains(t, cmds.Preview.Args[1], "subresource-name")
|
||||
require.Contains(t, strings.Join(cmds.Exec.Args, " "), "subresource-name")
|
||||
require.Contains(t, strings.Join(cmds.Preview.Args, " "), "subresource-name")
|
||||
},
|
||||
},
|
||||
{
|
||||
@@ -697,7 +696,7 @@ func TestGetGatewayCLICommand(t *testing.T) {
|
||||
|
||||
for _, test := range tests {
|
||||
t.Run(test.name, func(t *testing.T) {
|
||||
cmds, err := daemon.GetGatewayCLICommand(test.inputGateway)
|
||||
cmds, err := daemon.GetGatewayCLICommand(context.Background(), test.inputGateway)
|
||||
test.checkError(t, err)
|
||||
test.checkCmds(t, cmds)
|
||||
})
|
||||
@@ -714,7 +713,7 @@ func (m fakeGateway) TargetURI() uri.ResourceURI { return m.targetURI }
|
||||
func (m fakeGateway) TargetName() string { return m.targetURI.GetDbName() + m.targetURI.GetKubeName() }
|
||||
func (m fakeGateway) TargetUser() string { return "alice" }
|
||||
func (m fakeGateway) TargetSubresourceName() string { return m.subresourceName }
|
||||
func (m fakeGateway) Protocol() string { return defaults.ProtocolMongoDB }
|
||||
func (m fakeGateway) Protocol() string { return defaults.ProtocolSQLServer }
|
||||
func (m fakeGateway) Log() *logrus.Entry { return nil }
|
||||
func (m fakeGateway) LocalAddress() string { return "localhost" }
|
||||
func (m fakeGateway) LocalPortInt() int { return 8888 }
|
||||
|
||||
+12
-2
@@ -539,7 +539,8 @@ func onDatabaseConfig(cf *CLIConf) error {
|
||||
cmd, err := dbcmd.NewCmdBuilder(tc, profile, *database, rootCluster,
|
||||
dbcmd.WithPrintFormat(),
|
||||
dbcmd.WithLogger(log),
|
||||
).GetConnectCommand()
|
||||
dbcmd.WithGetDatabaseFunc(getDatabase),
|
||||
).GetConnectCommand(cf.Context)
|
||||
if err != nil {
|
||||
return trace.Wrap(err)
|
||||
}
|
||||
@@ -651,6 +652,7 @@ func maybeStartLocalProxy(ctx context.Context, cf *CLIConf,
|
||||
host := "localhost"
|
||||
cmdOpts := []dbcmd.ConnectCommandFunc{
|
||||
dbcmd.WithLocalProxy(host, addr.Port(0), profile.CACertPathForCluster(rootClusterName)),
|
||||
dbcmd.WithGetDatabaseFunc(dbInfo.getDatabaseForDBCmd),
|
||||
}
|
||||
if requires.tunnel {
|
||||
cmdOpts = append(cmdOpts, dbcmd.WithNoTLS())
|
||||
@@ -784,7 +786,7 @@ func onDatabaseConnect(cf *CLIConf) error {
|
||||
}
|
||||
|
||||
bb := dbcmd.NewCmdBuilder(tc, profile, dbInfo.RouteToDatabase, rootClusterName, opts...)
|
||||
cmd, err := bb.GetConnectCommand()
|
||||
cmd, err := bb.GetConnectCommand(cf.Context)
|
||||
if err != nil {
|
||||
return trace.Wrap(err)
|
||||
}
|
||||
@@ -1031,6 +1033,14 @@ func (d *databaseInfo) GetDatabase(ctx context.Context, tc *client.TeleportClien
|
||||
return d.database.Copy(), nil
|
||||
}
|
||||
|
||||
// getDatabaseForDBCmd is a callback for dbcmd connect option.
|
||||
func (d *databaseInfo) getDatabaseForDBCmd(ctx context.Context, tc *client.TeleportClient, serviceName string) (types.Database, error) {
|
||||
if serviceName != d.ServiceName {
|
||||
return nil, trace.BadParameter("expect database service %s but got %s", d.ServiceName, serviceName)
|
||||
}
|
||||
return d.GetDatabase(ctx, tc)
|
||||
}
|
||||
|
||||
// chooseOneDatabase is a helper func that returns either the only database in a
|
||||
// list of databases or returns a database that matches the selector name
|
||||
// or unambiguous discovered name exactly, otherwise an error.
|
||||
|
||||
@@ -230,6 +230,7 @@ func onProxyCommandDB(cf *CLIConf) error {
|
||||
dbcmd.WithLogger(log),
|
||||
dbcmd.WithPrintFormat(),
|
||||
dbcmd.WithTolerateMissingCLIClient(),
|
||||
dbcmd.WithGetDatabaseFunc(dbInfo.getDatabaseForDBCmd),
|
||||
}
|
||||
if opts, err = maybeAddDBUserPassword(cf, tc, dbInfo, opts); err != nil {
|
||||
return trace.Wrap(err)
|
||||
@@ -240,7 +241,7 @@ func onProxyCommandDB(cf *CLIConf) error {
|
||||
|
||||
commands, err := dbcmd.NewCmdBuilder(tc, profile, dbInfo.RouteToDatabase, rootCluster,
|
||||
opts...,
|
||||
).GetConnectCommandAlternatives()
|
||||
).GetConnectCommandAlternatives(cf.Context)
|
||||
if err != nil {
|
||||
return trace.Wrap(err)
|
||||
}
|
||||
|
||||
@@ -3883,7 +3883,8 @@ func TestSerializeDatabases(t *testing.T) {
|
||||
"memorydb": {},
|
||||
"opensearch": {},
|
||||
"rdsproxy": {},
|
||||
"redshift_serverless": {}
|
||||
"redshift_serverless": {},
|
||||
"docdb": {}
|
||||
},
|
||||
"mysql": {},
|
||||
"oracle": {
|
||||
@@ -3915,7 +3916,8 @@ func TestSerializeDatabases(t *testing.T) {
|
||||
"memorydb": {},
|
||||
"opensearch": {},
|
||||
"rdsproxy": {},
|
||||
"redshift_serverless": {}
|
||||
"redshift_serverless": {},
|
||||
"docdb": {}
|
||||
},
|
||||
"azure": {
|
||||
"redis": {}
|
||||
|
||||
Reference in New Issue
Block a user