mirror of
https://github.com/gravitational/teleport.git
synced 2026-11-01 21:52:02 +08:00
* Add Azure auto-discovery configuration fields * Init databases if azure matchers are in config * Use AzureMatchers in db service * Use all azure subscriptions/resource groups if omitted in matcher * Add azure config tests * Go mod tidy to update dependencies * Add azure response error conversion * Check for azure access denied and give a helpful error message * Add azure subscriptions api * Add azure mysql/postgresql api and wrappers * Test generic db server for azure * Make server properties its own type * Convert server types manually instead of via json * Move server list method selection logic out of api client * Update azure db server tests * Fixup merge * Update comments * Update more comments and remove junk code * Move all azure api into lib/cloud/azure * Update state and version checks * Add mutex to subscription client for caching, just in case * Update lib/cloud/azure/db_server_test.go Co-authored-by: Marek Smoliński <marek@goteleport.com> * Update lib/cloud/azure/subscriptions_test.go Co-authored-by: Marek Smoliński <marek@goteleport.com> * Update lib/cloud/azure/db_server_test.go Co-authored-by: Marek Smoliński <marek@goteleport.com> * Update lib/cloud/azure/db_server_test.go Co-authored-by: Marek Smoliński <marek@goteleport.com> * Update lib/cloud/azure/db_server_test.go Co-authored-by: Marek Smoliński <marek@goteleport.com> * Rename azure subscription client and remove sub ID caching * Add reference links for azure db ports * Move indirect dep into group * Wrap all converted azure response errors * Remove unreachable panic * Godoc DBServer * Remove maxPages arg to azure client funcs * Gofmt * Spacing between copyright and package * import order Co-authored-by: Marek Smoliński <marek@goteleport.com>
319 lines
8.9 KiB
Go
319 lines
8.9 KiB
Go
/*
|
|
Copyright 2022 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 azure
|
|
|
|
import (
|
|
"context"
|
|
"fmt"
|
|
"testing"
|
|
|
|
"github.com/Azure/azure-sdk-for-go/sdk/azcore/arm"
|
|
"github.com/Azure/azure-sdk-for-go/sdk/azcore/to"
|
|
"github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/mysql/armmysql"
|
|
"github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/postgresql/armpostgresql"
|
|
"github.com/google/go-cmp/cmp"
|
|
"github.com/stretchr/testify/require"
|
|
|
|
"github.com/gravitational/teleport/api/types"
|
|
"github.com/gravitational/teleport/lib/defaults"
|
|
)
|
|
|
|
func TestListServers(t *testing.T) {
|
|
t.Parallel()
|
|
myServer1, myDBServer1 := makeMySQLServer("mysql1", "group1")
|
|
myServer2, myDBServer2 := makeMySQLServer("mysql2", "group2")
|
|
pgServer1, pgDBServer1 := makePostgresServer("pgres1", "group1")
|
|
pgServer2, pgDBServer2 := makePostgresServer("pgres2", "group2")
|
|
mySQLClient := NewMySQLServersClient(&ARMMySQLMock{
|
|
DBServers: []*armmysql.Server{myServer1, myServer2},
|
|
})
|
|
pgClient := NewPostgresServerClient(&ARMPostgresMock{
|
|
DBServers: []*armpostgresql.Server{pgServer1, pgServer2},
|
|
})
|
|
tests := []struct {
|
|
name string
|
|
client DBServersClient
|
|
group string
|
|
want []*DBServer
|
|
}{
|
|
{
|
|
name: "list all MySQL servers",
|
|
client: mySQLClient,
|
|
group: types.Wildcard,
|
|
want: []*DBServer{myDBServer1, myDBServer2},
|
|
},
|
|
{
|
|
name: "list MySQL servers in a resource group",
|
|
client: mySQLClient,
|
|
group: "group1",
|
|
want: []*DBServer{myDBServer1},
|
|
},
|
|
{
|
|
name: "list all PostgreSQL servers",
|
|
client: pgClient,
|
|
group: types.Wildcard,
|
|
want: []*DBServer{pgDBServer1, pgDBServer2},
|
|
},
|
|
{
|
|
name: "list PostgreSQL servers in a resource group",
|
|
client: pgClient,
|
|
group: "group2",
|
|
want: []*DBServer{pgDBServer2},
|
|
},
|
|
}
|
|
ctx := context.Background()
|
|
|
|
for _, tt := range tests {
|
|
t.Run(tt.name, func(t *testing.T) {
|
|
var (
|
|
servers []*DBServer
|
|
err error
|
|
)
|
|
if tt.group == types.Wildcard {
|
|
servers, err = tt.client.ListAll(ctx)
|
|
} else {
|
|
servers, err = tt.client.ListWithinGroup(ctx, tt.group)
|
|
}
|
|
require.NoError(t, err)
|
|
require.Empty(t, cmp.Diff(tt.want, servers))
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestGetServer(t *testing.T) {
|
|
t.Parallel()
|
|
myServer1, _ := makeMySQLServer("a", "group1")
|
|
myServer2, myDBServer2 := makeMySQLServer("a", "group2")
|
|
myServer3, _ := makeMySQLServer("b", "group1")
|
|
myServer4, _ := makeMySQLServer("b", "group2")
|
|
pgServer1, pgDBServer1 := makePostgresServer("a", "group1")
|
|
pgServer2, _ := makePostgresServer("a", "group2")
|
|
pgServer3, _ := makePostgresServer("b", "group1")
|
|
pgServer4, _ := makePostgresServer("b", "group2")
|
|
mySQLClient := NewMySQLServersClient(&ARMMySQLMock{
|
|
DBServers: []*armmysql.Server{myServer1, myServer2, myServer3, myServer4},
|
|
})
|
|
pgClient := NewPostgresServerClient(&ARMPostgresMock{
|
|
DBServers: []*armpostgresql.Server{pgServer1, pgServer2, pgServer3, pgServer4},
|
|
})
|
|
|
|
tests := []struct {
|
|
name string
|
|
client DBServersClient
|
|
dbName string
|
|
group string
|
|
want *DBServer
|
|
}{
|
|
{
|
|
name: "get a mysql server",
|
|
client: mySQLClient,
|
|
dbName: "a",
|
|
group: "group2",
|
|
want: myDBServer2,
|
|
},
|
|
{
|
|
name: "get a postgres server",
|
|
client: pgClient,
|
|
dbName: "a",
|
|
group: "group1",
|
|
want: pgDBServer1,
|
|
},
|
|
}
|
|
|
|
ctx := context.Background()
|
|
for _, tt := range tests {
|
|
t.Run(tt.name, func(t *testing.T) {
|
|
s, err := tt.client.Get(ctx, tt.group, tt.dbName)
|
|
require.NoError(t, err)
|
|
require.Empty(t, cmp.Diff(tt.want, s))
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestServerConversion(t *testing.T) {
|
|
t.Parallel()
|
|
tests := []struct {
|
|
name string
|
|
protocol string
|
|
version string
|
|
state string
|
|
wantAvailable bool
|
|
wantSupported bool
|
|
}{
|
|
{
|
|
name: "mysql available and supported",
|
|
protocol: defaults.ProtocolMySQL,
|
|
version: "5.7",
|
|
state: "Ready",
|
|
wantAvailable: true,
|
|
wantSupported: true,
|
|
},
|
|
{
|
|
name: "mysql unavailable and unsupported",
|
|
protocol: defaults.ProtocolMySQL,
|
|
version: "5.6",
|
|
state: "Disabled",
|
|
wantAvailable: false,
|
|
wantSupported: false,
|
|
},
|
|
{
|
|
name: "postgres available and supported",
|
|
protocol: defaults.ProtocolPostgres,
|
|
version: "11",
|
|
state: "Ready",
|
|
wantAvailable: true,
|
|
wantSupported: true,
|
|
},
|
|
{
|
|
name: "postgres unavailable",
|
|
protocol: defaults.ProtocolPostgres,
|
|
version: "11",
|
|
state: "Disabled",
|
|
wantAvailable: false,
|
|
wantSupported: true,
|
|
},
|
|
}
|
|
|
|
const (
|
|
region = "eastus"
|
|
dbName = "dbName"
|
|
group = "resourceGroup"
|
|
)
|
|
|
|
for _, tt := range tests {
|
|
t.Run(tt.name, func(t *testing.T) {
|
|
var (
|
|
dbServer *DBServer
|
|
fqdn string
|
|
id string
|
|
port string
|
|
)
|
|
tags := map[string]string{"foo": "bar", "baz": "qux"}
|
|
switch tt.protocol {
|
|
case defaults.ProtocolMySQL:
|
|
armServer, _ := makeMySQLServer(dbName, group, withMySQLState(tt.state), withMySQLVersion(tt.version))
|
|
dbServer = ServerFromMySQLServer(armServer)
|
|
fqdn = *armServer.Properties.FullyQualifiedDomainName
|
|
id = *armServer.ID
|
|
port = "3306"
|
|
case defaults.ProtocolPostgres:
|
|
armServer, _ := makePostgresServer(dbName, group, withPostgresState(tt.state), withPostgresVersion(tt.version))
|
|
dbServer = ServerFromPostgresServer(armServer)
|
|
fqdn = *armServer.Properties.FullyQualifiedDomainName
|
|
id = *armServer.ID
|
|
port = "5432"
|
|
default:
|
|
require.FailNow(t, "unknown db protocol specified by test")
|
|
}
|
|
|
|
expected := DBServer{
|
|
ID: id,
|
|
Location: region,
|
|
Name: dbName,
|
|
Port: port,
|
|
Properties: ServerProperties{
|
|
FullyQualifiedDomainName: fqdn,
|
|
UserVisibleState: tt.state,
|
|
Version: tt.version,
|
|
},
|
|
Protocol: tt.protocol,
|
|
Tags: tags,
|
|
}
|
|
|
|
_, err := arm.ParseResourceID(dbServer.ID)
|
|
require.NoError(t, err)
|
|
|
|
require.Equal(t, expected, *dbServer)
|
|
require.Equal(t, tt.wantAvailable, dbServer.IsAvailable())
|
|
require.Equal(t, tt.wantSupported, dbServer.IsSupported())
|
|
})
|
|
}
|
|
}
|
|
|
|
type MySQLServerOptFn func(*armmysql.Server)
|
|
type PostgresServerOptFn func(*armpostgresql.Server)
|
|
|
|
func withMySQLState(state string) MySQLServerOptFn {
|
|
return func(s *armmysql.Server) {
|
|
*s.Properties.UserVisibleState = armmysql.ServerState(state)
|
|
}
|
|
}
|
|
|
|
func withMySQLVersion(version string) MySQLServerOptFn {
|
|
return func(s *armmysql.Server) {
|
|
*s.Properties.Version = armmysql.ServerVersion(version)
|
|
}
|
|
}
|
|
|
|
func withPostgresState(state string) PostgresServerOptFn {
|
|
return func(s *armpostgresql.Server) {
|
|
*s.Properties.UserVisibleState = armpostgresql.ServerState(state)
|
|
}
|
|
}
|
|
|
|
func withPostgresVersion(version string) PostgresServerOptFn {
|
|
return func(s *armpostgresql.Server) {
|
|
*s.Properties.Version = armpostgresql.ServerVersion(version)
|
|
}
|
|
}
|
|
|
|
func makeMySQLServer(name, group string, opts ...MySQLServerOptFn) (*armmysql.Server, *DBServer) {
|
|
id := fmt.Sprintf("/subscriptions/subid/resourceGroups/%v/providers/Microsoft.DBforMySQL/servers/%v",
|
|
group, name)
|
|
fqdn := fmt.Sprintf("%v.mysql.database.azure.com", name)
|
|
server := &armmysql.Server{
|
|
Location: to.Ptr("eastus"),
|
|
Properties: &armmysql.ServerProperties{
|
|
FullyQualifiedDomainName: &fqdn,
|
|
UserVisibleState: to.Ptr(armmysql.ServerStateReady),
|
|
Version: to.Ptr(armmysql.ServerVersionFive7),
|
|
},
|
|
Tags: map[string]*string{"foo": to.Ptr("bar"), "baz": to.Ptr("qux")},
|
|
ID: to.Ptr(id),
|
|
Name: &name,
|
|
Type: to.Ptr("Microsoft.DBforMySQL/servers"),
|
|
}
|
|
for _, optFn := range opts {
|
|
optFn(server)
|
|
}
|
|
dbServer := ServerFromMySQLServer(server)
|
|
return server, dbServer
|
|
}
|
|
|
|
func makePostgresServer(name, group string, opts ...PostgresServerOptFn) (*armpostgresql.Server, *DBServer) {
|
|
id := fmt.Sprintf("/subscriptions/subid/resourceGroups/%v/providers/Microsoft.DBforPostgreSQL/servers/%v",
|
|
group, name)
|
|
fqdn := fmt.Sprintf("%v.postgres.database.azure.com", name)
|
|
server := &armpostgresql.Server{
|
|
Location: to.Ptr("eastus"),
|
|
Properties: &armpostgresql.ServerProperties{
|
|
FullyQualifiedDomainName: &fqdn,
|
|
UserVisibleState: to.Ptr(armpostgresql.ServerStateReady),
|
|
Version: to.Ptr(armpostgresql.ServerVersionEleven),
|
|
},
|
|
Tags: map[string]*string{"foo": to.Ptr("bar"), "baz": to.Ptr("qux")},
|
|
ID: to.Ptr(id),
|
|
Name: &name,
|
|
Type: to.Ptr("Microsoft.DBforPostgreSQL/servers"),
|
|
}
|
|
for _, optFn := range opts {
|
|
optFn(server)
|
|
}
|
|
dbServer := ServerFromPostgresServer(server)
|
|
return server, dbServer
|
|
}
|