Skip to content

Commit

Permalink
fix(rdb): connect to public or private endpoint (#3094)
Browse files Browse the repository at this point in the history
  • Loading branch information
Monitob committed Apr 27, 2023
1 parent 3252886 commit 6ecde0b
Show file tree
Hide file tree
Showing 9 changed files with 929 additions and 594 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -6,11 +6,12 @@ USAGE:
scw rdb instance connect <instance-id ...> [arg=value ...]

ARGS:
instance-id UUID of the instance
username Name of the user to connect with to the database
[database=rdb] Name of the database
[cli-db] Command line tool to use, default to psql/mysql
[region=fr-par] Region to target. If none is passed will use default region from the config (fr-par | nl-ams)
[private-network=false] Connect by the private network endpoint attached.
instance-id UUID of the instance
username Name of the user to connect with to the database
[database=rdb] Name of the database
[cli-db] Command line tool to use, default to psql/mysql
[region=fr-par] Region to target. If none is passed will use default region from the config (fr-par | nl-ams)

FLAGS:
-h, --help help for connect
Expand Down
1 change: 1 addition & 0 deletions docs/commands/rdb.md
Original file line number Diff line number Diff line change
Expand Up @@ -659,6 +659,7 @@ scw rdb instance connect <instance-id ...> [arg=value ...]

| Name | | Description |
|------|---|-------------|
| private-network | Default: `false` | Connect by the private network endpoint attached. |
| instance-id | Required | UUID of the instance |
| username | Required | Name of the user to connect with to the database |
| database | Default: `rdb` | Name of the database |
Expand Down
74 changes: 61 additions & 13 deletions internal/namespaces/rdb/v1/custom_instance.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,10 @@ import (
)

const (
instanceActionTimeout = 20 * time.Minute
instanceActionTimeout = 20 * time.Minute
errorMessagePublicEndpointNotFound = "public endpoint not found"
errorMessagePrivateEndpointNotFound = "private endpoint not found"
errorMessageEndpointNotFound = "any endpoint is associated on your instance"
)

var (
Expand Down Expand Up @@ -396,11 +399,12 @@ func instanceWaitCommand() *core.Command {
}

type instanceConnectArgs struct {
Region scw.Region
InstanceID string
Username string
Database *string
CliDB *string
Region scw.Region
PrivateNetwork bool
InstanceID string
Username string
Database *string
CliDB *string
}

type engineFamily string
Expand Down Expand Up @@ -463,7 +467,27 @@ func detectEngineFamily(instance *rdb.Instance) (engineFamily, error) {
return Unknown, fmt.Errorf("unknown engine: %s", instance.Engine)
}

func createConnectCommandLineArgs(instance *rdb.Instance, family engineFamily, args *instanceConnectArgs) ([]string, error) {
func getPublicEndpoint(endpoints []*rdb.Endpoint) (*rdb.Endpoint, error) {
for _, e := range endpoints {
if e.LoadBalancer != nil {
return e, nil
}
}

return nil, fmt.Errorf(errorMessagePublicEndpointNotFound)
}

func getPrivateEndpoint(endpoints []*rdb.Endpoint) (*rdb.Endpoint, error) {
for _, e := range endpoints {
if e.PrivateNetwork != nil {
return e, nil
}
}

return nil, fmt.Errorf(errorMessagePrivateEndpointNotFound)
}

func createConnectCommandLineArgs(endpoint *rdb.Endpoint, family engineFamily, args *instanceConnectArgs) ([]string, error) {
database := "rdb"
if args.Database != nil {
database = *args.Database
Expand All @@ -479,8 +503,8 @@ func createConnectCommandLineArgs(instance *rdb.Instance, family engineFamily, a
// psql -h 51.159.25.206 --port 13917 -d rdb -U username
return []string{
clidb,
"--host", instance.Endpoints[0].IP.String(),
"--port", fmt.Sprintf("%d", instance.Endpoints[0].Port),
"--host", endpoint.IP.String(),
"--port", fmt.Sprintf("%d", endpoint.Port),
"--username", args.Username,
"--dbname", database,
}, nil
Expand All @@ -493,14 +517,14 @@ func createConnectCommandLineArgs(instance *rdb.Instance, family engineFamily, a
// mysql -h 195.154.69.163 --port 12210 -p -u username
return []string{
clidb,
"--host", instance.Endpoints[0].IP.String(),
"--port", fmt.Sprintf("%d", instance.Endpoints[0].Port),
"--host", endpoint.IP.String(),
"--port", fmt.Sprintf("%d", endpoint.Port),
"--database", database,
"--user", args.Username,
}, nil
}

return nil, fmt.Errorf("unrecognize database engine: %s", instance.Engine)
return nil, fmt.Errorf("unrecognize database engine: %s", family)
}

func instanceConnectCommand() *core.Command {
Expand All @@ -512,6 +536,12 @@ func instanceConnectCommand() *core.Command {
Long: "Connect to an instance using locally installed CLI such as psql or mysql.",
ArgsType: reflect.TypeOf(instanceConnectArgs{}),
ArgSpecs: core.ArgSpecs{
{
Name: "private-network",
Short: `Connect by the private network endpoint attached.`,
Required: false,
Default: core.DefaultValueSetter("false"),
},
{
Name: "instance-id",
Short: `UUID of the instance`,
Expand Down Expand Up @@ -552,7 +582,25 @@ func instanceConnectCommand() *core.Command {
return nil, err
}

cmdArgs, err := createConnectCommandLineArgs(instance, engineFamily, args)
if len(instance.Endpoints) == 0 {
return nil, fmt.Errorf(errorMessageEndpointNotFound)
}

var endpoint *rdb.Endpoint
switch {
case args.PrivateNetwork:
endpoint, err = getPrivateEndpoint(instance.Endpoints)
if err != nil {
return nil, err
}
default:
endpoint, err = getPublicEndpoint(instance.Endpoints)
if err != nil {
return nil, err
}
}

cmdArgs, err := createConnectCommandLineArgs(endpoint, engineFamily, args)
if err != nil {
return nil, err
}
Expand Down
15 changes: 15 additions & 0 deletions internal/namespaces/rdb/v1/custom_instance_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -180,4 +180,19 @@ func Test_Connect(t *testing.T) {
OverrideExec: core.OverrideExecSimple("psql --host {{ .Instance.Endpoint.IP }} --port {{ .Instance.Endpoint.Port }} --username {{ .username }} --dbname rdb", 0),
AfterFunc: deleteInstance(),
}))
t.Run("psql", core.Test(&core.TestConfig{
Commands: GetCommands(),
BeforeFunc: core.BeforeFuncCombine(
core.BeforeFuncStoreInMeta("username", user),
createPN(),
createInstanceWithPrivateNetwork("PostgreSQL-14"),
),
Cmd: "scw rdb instance connect {{ .Instance.ID }} username={{ .username }}",
Check: core.TestCheckCombine(
core.TestCheckGolden(),
core.TestCheckExitCode(0),
),
OverrideExec: core.OverrideExecSimple("psql --host {{ .Instance.Endpoint.IP }} --port {{ .Instance.Endpoint.Port }} --username {{ .username }} --dbname rdb", 0),
AfterFunc: deleteInstance(),
}))
}
36 changes: 36 additions & 0 deletions internal/namespaces/rdb/v1/helper_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@ import (
"fmt"

"github.com/scaleway/scaleway-cli/v2/internal/core"
"github.com/scaleway/scaleway-sdk-go/api/vpc/v1"
"github.com/scaleway/scaleway-sdk-go/scw"
)

const (
Expand All @@ -20,6 +22,40 @@ func createInstance(engine string) core.BeforeFunc {
)
}

func createInstanceWithPrivateNetwork(engine string) core.BeforeFunc {
return core.ExecStoreBeforeCmd(
"Instance",
fmt.Sprintf("scw rdb instance create node-type=DB-DEV-S is-ha-cluster=false name=%s engine=%s user-name=%s password=%s init-endpoints.0.private-network.private-network-id={{ .PN.ID }} init-endpoints.0.private-network.service-ip={{ .IPNet }} --wait", name, engine, user, password),
)
}

func createPN() core.BeforeFunc {
return func(ctx *core.BeforeFuncCtx) error {
var err error
api := vpc.NewAPI(ctx.Client)
pn, _ := api.CreatePrivateNetwork(&vpc.CreatePrivateNetworkRequest{})
ctx.Meta["PN"] = pn
ctx.Meta["IPNet"], err = getIPSubnet(pn.Subnets[0])
if err != nil {
return err
}
return nil
}
}

func getIPSubnet(ipNet scw.IPNet) (*string, error) {
addr := ipNet.IP.To4()
if addr == nil {
return nil, fmt.Errorf("could get ip 4 bytes")
}
addr = addr.Mask(addr.DefaultMask())
addr[3] = +3

sz, _ := ipNet.Mask.Size()
ipNetStr := fmt.Sprintf("%s/%d", addr.String(), sz)
return &ipNetStr, nil
}

func deleteInstance() core.AfterFunc {
return core.ExecAfterCmd("scw rdb instance delete {{ .Instance.ID }}")
}

0 comments on commit 6ecde0b

Please sign in to comment.