Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Appearance settings

Commit 6a42223

Browse filesBrowse files
committed
feat: simplify work with Database Lab CLI (port forwarding) (platform#91)
1 parent 6bd73b5 commit 6a42223
Copy full SHA for 6a42223

File tree

13 files changed

+508
-22
lines changed
Filter options

13 files changed

+508
-22
lines changed

‎cmd/cli/commands/client.go

Copy file name to clipboardExpand all lines: cmd/cli/commands/client.go
+14-1Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,8 @@
66
package commands
77

88
import (
9+
"net/url"
10+
911
"github.com/urfave/cli/v2"
1012

1113
"gitlab.com/postgres-ai/database-lab/pkg/client/dblabapi"
@@ -17,12 +19,23 @@ const (
1719
URLKey = "url"
1820
TokenKey = "token"
1921
InsecureKey = "insecure"
22+
FwServerURLKey = "forwarding-server-url"
23+
FwLocalPortKey = "forwarding-local-port"
2024
)
2125

2226
// ClientByCLIContext creates a new Database Lab API client.
2327
func ClientByCLIContext(cliCtx *cli.Context) (*dblabapi.Client, error) {
28+
remoteURL, err := url.Parse(cliCtx.String(URLKey))
29+
if err != nil {
30+
return nil, err
31+
}
32+
33+
if cliCtx.String(FwServerURLKey) != "" && cliCtx.String(FwLocalPortKey) != "" {
34+
remoteURL.Host = BuildHostname(remoteURL.Hostname(), cliCtx.String(FwLocalPortKey))
35+
}
36+
2437
options := dblabapi.Options{
25-
Host: cliCtx.String(URLKey),
38+
Host: remoteURL.String(),
2639
VerificationToken: cliCtx.String(TokenKey),
2740
Insecure: cliCtx.Bool(InsecureKey),
2841
}

‎cmd/cli/commands/clone/actions.go

Copy file name to clipboardExpand all lines: cmd/cli/commands/clone/actions.go
+83Lines changed: 83 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,12 +8,15 @@ package clone
88
import (
99
"encoding/json"
1010
"fmt"
11+
"net/url"
1112
"os"
13+
"sync"
1214

1315
"github.com/urfave/cli/v2"
1416

1517
"gitlab.com/postgres-ai/database-lab/cmd/cli/commands"
1618
"gitlab.com/postgres-ai/database-lab/pkg/client/dblabapi/types"
19+
"gitlab.com/postgres-ai/database-lab/pkg/log"
1720
"gitlab.com/postgres-ai/database-lab/pkg/models"
1821
"gitlab.com/postgres-ai/database-lab/pkg/observer"
1922
)
@@ -249,3 +252,83 @@ func observeSummary() func(*cli.Context) error {
249252
return nil
250253
}
251254
}
255+
256+
func forward(cliCtx *cli.Context) error {
257+
remoteURL, err := url.Parse(cliCtx.String(commands.URLKey))
258+
if err != nil {
259+
return err
260+
}
261+
262+
wg := &sync.WaitGroup{}
263+
264+
port, err := retrieveClonePort(cliCtx, wg, remoteURL.Host)
265+
if err != nil {
266+
return err
267+
}
268+
269+
wg.Wait()
270+
271+
log.Dbg(fmt.Sprintf("The clone port has been retrieved: %s", port))
272+
273+
tunnel, err := commands.BuildTunnel(cliCtx, commands.BuildHostname(remoteURL.Hostname(), port))
274+
if err != nil {
275+
return err
276+
}
277+
278+
if err := tunnel.Open(); err != nil {
279+
return err
280+
}
281+
282+
log.Msg(fmt.Sprintf("The clone is available by address: %s", tunnel.Endpoints.Local))
283+
284+
if err := tunnel.Listen(cliCtx.Context); err != nil {
285+
return err
286+
}
287+
288+
return nil
289+
}
290+
291+
func retrieveClonePort(cliCtx *cli.Context, wg *sync.WaitGroup, remoteHost string) (string, error) {
292+
tunnel, err := commands.BuildTunnel(cliCtx, remoteHost)
293+
if err != nil {
294+
return "", err
295+
}
296+
297+
if err := tunnel.Open(); err != nil {
298+
return "", err
299+
}
300+
301+
const goroutineCount = 1
302+
303+
wg.Add(goroutineCount)
304+
305+
go func() {
306+
defer wg.Done()
307+
308+
if err := tunnel.Listen(cliCtx.Context); err != nil {
309+
log.Fatal(err)
310+
}
311+
}()
312+
313+
defer func() {
314+
log.Dbg("Stop tunnel to DBLab")
315+
316+
if err := tunnel.Stop(); err != nil {
317+
log.Err(err)
318+
}
319+
}()
320+
321+
log.Dbg("Retrieving clone port")
322+
323+
dblabClient, err := commands.ClientByCLIContext(cliCtx)
324+
if err != nil {
325+
return "", err
326+
}
327+
328+
clone, err := dblabClient.GetClone(cliCtx.Context, cliCtx.Args().First())
329+
if err != nil {
330+
return "", err
331+
}
332+
333+
return clone.DB.Port, nil
334+
}

‎cmd/cli/commands/clone/command_list.go

Copy file name to clipboardExpand all lines: cmd/cli/commands/clone/command_list.go
+16Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -155,6 +155,22 @@ func CommandList() []*cli.Command {
155155
Usage: "[EXPERIMENTAL] summarize clone monitoring and check results",
156156
Action: observeSummary(),
157157
},
158+
{
159+
Name: "port-forward",
160+
Usage: "start port forwarding to clone",
161+
Before: func(ctxCli *cli.Context) error {
162+
if err := checkCloneIDBefore(ctxCli); err != nil {
163+
return err
164+
}
165+
166+
if err := commands.CheckForwardingServerURL(ctxCli); err != nil {
167+
return err
168+
}
169+
170+
return nil
171+
},
172+
Action: forward,
173+
},
158174
},
159175
}}
160176
}

‎cmd/cli/commands/config/actions.go

Copy file name to clipboardExpand all lines: cmd/cli/commands/config/actions.go
+40-6Lines changed: 40 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,14 @@ import (
1616
"gitlab.com/postgres-ai/database-lab/cmd/cli/commands"
1717
)
1818

19+
// headers of a config list.
20+
const (
21+
envHeader = "ENV "
22+
urlHeader = "URL"
23+
fwServerHeader = "Forwarding server URL"
24+
fwPortHeader = "Forwarding local port"
25+
)
26+
1927
// createEnvironment creates a new CLI environment.
2028
func createEnvironment() func(*cli.Context) error {
2129
return func(cliCtx *cli.Context) (err error) {
@@ -119,32 +127,54 @@ func list() func(*cli.Context) error {
119127
}
120128

121129
environmentNames := make([]string, 0, len(cfg.Environments))
122-
maxNameLength := 0
130+
maxNameLen := 0
131+
maxURLLen := len(urlHeader)
132+
maxFwServerLen := len(fwServerHeader)
123133

124134
for environmentName := range cfg.Environments {
125135
environmentNames = append(environmentNames, environmentName)
126136

127137
nameLength := len(environmentName)
128-
if maxNameLength < nameLength {
129-
maxNameLength = nameLength
138+
if maxNameLen < nameLength {
139+
maxNameLen = nameLength
140+
}
141+
142+
urlLength := len(cfg.Environments[environmentName].URL)
143+
if maxURLLen < urlLength {
144+
maxURLLen = urlLength
145+
}
146+
147+
urlFwLength := len(cfg.Environments[environmentName].Forwarding.ServerURL)
148+
if maxFwServerLen < urlFwLength {
149+
maxFwServerLen = urlFwLength
130150
}
131151
}
132152

133153
sort.Strings(environmentNames)
134154

135-
listOutput := buildListOutput(cfg, environmentNames, maxNameLength)
155+
listOutput := buildListOutput(cfg, environmentNames, maxNameLen, maxURLLen, maxFwServerLen)
136156

137157
_, err = fmt.Fprintf(cliCtx.App.Writer, "Available CLI environments:\n%s", listOutput)
138158

139159
return commands.ToActionError(err)
140160
}
141161
}
142162

143-
func buildListOutput(cfg *CLIConfig, environmentNames []string, maxNameLength int) string {
163+
func buildListOutput(cfg *CLIConfig, environmentNames []string, maxNameLen, maxURLLen, maxFwLen int) string {
164+
// TODO(akartasov): Draw as a table.
144165
const outputAlign = 2
145166

146167
s := strings.Builder{}
147168

169+
s.WriteString(envHeader)
170+
s.WriteString(strings.Repeat(" ", maxNameLen+outputAlign))
171+
s.WriteString(urlHeader)
172+
s.WriteString(strings.Repeat(" ", maxURLLen-len(urlHeader)+outputAlign))
173+
s.WriteString(fwServerHeader)
174+
s.WriteString(strings.Repeat(" ", maxFwLen-len(fwServerHeader)+outputAlign))
175+
s.WriteString(fwPortHeader)
176+
s.WriteString("\n")
177+
148178
for _, environmentName := range environmentNames {
149179
if environmentName == cfg.CurrentEnvironment {
150180
s.WriteString("[*] ")
@@ -153,8 +183,12 @@ func buildListOutput(cfg *CLIConfig, environmentNames []string, maxNameLength in
153183
}
154184

155185
s.WriteString(environmentName)
156-
s.WriteString(strings.Repeat(" ", maxNameLength-len(environmentName)+outputAlign))
186+
s.WriteString(strings.Repeat(" ", maxNameLen-len(environmentName)+outputAlign))
157187
s.WriteString(cfg.Environments[environmentName].URL)
188+
s.WriteString(strings.Repeat(" ", maxURLLen-len(cfg.Environments[environmentName].URL)+outputAlign))
189+
s.WriteString(cfg.Environments[environmentName].Forwarding.ServerURL)
190+
s.WriteString(strings.Repeat(" ", maxFwLen-len(cfg.Environments[environmentName].Forwarding.ServerURL)+outputAlign))
191+
s.WriteString(cfg.Environments[environmentName].Forwarding.LocalPort)
158192
s.WriteString("\n")
159193
}
160194

‎cmd/cli/commands/config/command_list.go

Copy file name to clipboardExpand all lines: cmd/cli/commands/config/command_list.go
+16Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,14 @@ func CommandList() []*cli.Command {
3737
Name: "insecure",
3838
Usage: "allow insecure server connections when using SSL",
3939
},
40+
&cli.StringFlag{
41+
Name: "forwarding-server-url",
42+
Usage: "forwarding server URL of Database Lab instance",
43+
},
44+
&cli.StringFlag{
45+
Name: "forwarding-local-port",
46+
Usage: "local port for forwarding to the Database Lab instance",
47+
},
4048
},
4149
},
4250
{
@@ -57,6 +65,14 @@ func CommandList() []*cli.Command {
5765
Name: "insecure",
5866
Usage: "allow insecure server connections when using SSL",
5967
},
68+
&cli.StringFlag{
69+
Name: "forwarding-server-url",
70+
Usage: "forwarding server URL of Database Lab instance",
71+
},
72+
&cli.StringFlag{
73+
Name: "forwarding-local-port",
74+
Usage: "local port for forwarding to the Database Lab instance",
75+
},
6076
},
6177
},
6278
{

‎cmd/cli/commands/config/environment.go

Copy file name to clipboardExpand all lines: cmd/cli/commands/config/environment.go
+23-5Lines changed: 23 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -13,17 +13,23 @@ import (
1313

1414
// CLIConfig defines a format of CLI configuration.
1515
type CLIConfig struct {
16-
Version string `yaml:"version" json:"version"`
1716
CurrentEnvironment string `yaml:"current_environment" json:"current_environment"`
1817
Environments map[string]Environment `yaml:"environments" json:"environments"`
1918
}
2019

2120
// Environment defines a format of environment configuration.
2221
type Environment struct {
23-
EnvironmentID string `yaml:"-" json:"environment_id"`
24-
URL string `yaml:"url" json:"url"`
25-
Token string `yaml:"token" json:"token"`
26-
Insecure bool `yaml:"insecure" json:"insecure"`
22+
EnvironmentID string `yaml:"-" json:"environment_id"`
23+
URL string `yaml:"url" json:"url"`
24+
Token string `yaml:"token" json:"token"`
25+
Insecure bool `yaml:"insecure" json:"insecure"`
26+
Forwarding Forwarding `yaml:"forwarding" json:"forwarding"`
27+
}
28+
29+
// Forwarding defines configuration for port forwarding.
30+
type Forwarding struct {
31+
ServerURL string `yaml:"server_url" json:"server_url"`
32+
LocalPort string `yaml:"local_port" json:"local_port"`
2733
}
2834

2935
// AddEnvironmentToConfig adds a new environment to CLIConfig.
@@ -40,6 +46,10 @@ func AddEnvironmentToConfig(c *cli.Context, cfg *CLIConfig, environmentID string
4046
URL: c.String(commands.URLKey),
4147
Token: c.String(commands.TokenKey),
4248
Insecure: c.Bool(commands.InsecureKey),
49+
Forwarding: Forwarding{
50+
ServerURL: c.String(commands.FwServerURLKey),
51+
LocalPort: c.String(commands.FwLocalPortKey),
52+
},
4353
}
4454

4555
if cfg.Environments == nil {
@@ -81,6 +91,14 @@ func updateEnvironmentInConfig(c *cli.Context, cfg *CLIConfig, environmentID str
8191
newEnvironment.Insecure = c.Bool(commands.InsecureKey)
8292
}
8393

94+
if c.IsSet(commands.FwServerURLKey) {
95+
newEnvironment.Forwarding.ServerURL = c.String(commands.FwServerURLKey)
96+
}
97+
98+
if c.IsSet(commands.FwLocalPortKey) {
99+
newEnvironment.Forwarding.LocalPort = c.String(commands.FwLocalPortKey)
100+
}
101+
84102
if newEnvironment == environment {
85103
return errors.New("config unchanged. Set different option values to update.") // nolint
86104
}

‎cmd/cli/commands/initialization/actions.go renamed to ‎cmd/cli/commands/global/actions.go

Copy file name to clipboardExpand all lines: cmd/cli/commands/global/actions.go
+34-4Lines changed: 34 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2,18 +2,20 @@
22
2020 © Postgres.ai
33
*/
44

5-
// Package initialization provides commands for a CLI initialization.
6-
package initialization
5+
// Package global provides general commands for CLI usage.
6+
package global
77

88
import (
99
"fmt"
10+
"net/url"
1011
"os"
1112

1213
"github.com/pkg/errors"
1314
"github.com/urfave/cli/v2"
1415

1516
"gitlab.com/postgres-ai/database-lab/cmd/cli/commands"
1617
"gitlab.com/postgres-ai/database-lab/cmd/cli/commands/config"
18+
"gitlab.com/postgres-ai/database-lab/pkg/log"
1719
)
1820

1921
func initCLI(c *cli.Context) error {
@@ -37,8 +39,6 @@ func initCLI(c *cli.Context) error {
3739
}
3840
}
3941

40-
cfg.Version = c.App.Version
41-
4242
environmentID := c.String(commands.EnvironmentIDKey)
4343
if err := config.AddEnvironmentToConfig(c, cfg, environmentID); err != nil {
4444
return err
@@ -53,3 +53,33 @@ func initCLI(c *cli.Context) error {
5353

5454
return err
5555
}
56+
57+
func forward(cliCtx *cli.Context) error {
58+
remoteURL, err := url.Parse(cliCtx.String(commands.URLKey))
59+
if err != nil {
60+
return err
61+
}
62+
63+
tunnel, err := commands.BuildTunnel(cliCtx, remoteURL.Host)
64+
if err != nil {
65+
return err
66+
}
67+
68+
if err := tunnel.Open(); err != nil {
69+
return err
70+
}
71+
72+
defer func() {
73+
if stopErr := tunnel.Stop(); err == nil {
74+
err = stopErr
75+
}
76+
}()
77+
78+
log.Msg(fmt.Sprintf("The connection is available by address: %s", tunnel.Endpoints.Local))
79+
80+
if err := tunnel.Listen(cliCtx.Context); err != nil {
81+
return err
82+
}
83+
84+
return nil
85+
}

0 commit comments

Comments
0 (0)
Morty Proxy This is a proxified and sanitized view of the page, visit original site.