Skip to content

Commit 5695b55

Browse files
authored
FEATURE-119: Add a new parameter --set-ssh-config-path to preserve ssh_confg path across app restarts (#122)
* Should support config '--set-ssh-config-path' cmd option * mprove ssh_config storage performance * Increase unit test coverage
1 parent 99d06bd commit 5695b55

File tree

18 files changed

+393
-152
lines changed

18 files changed

+393
-152
lines changed

e2e/15_set_ssh_config_path.tape

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
# Test that we can set ssh_config path and it will survive
2+
# application run in normal mode and overriding state file.
3+
4+
Set Shell bash
5+
6+
# Create ssh_config
7+
Type "touch /tmp/delete_me_goto_ssh_config_test_file"
8+
Enter
9+
Sleep 100ms
10+
Type "gg -f temp --set-ssh-config-path /tmp/delete_me_goto_ssh_config_test_file"
11+
Enter
12+
Sleep 100ms
13+
14+
# Run the app
15+
Type "gg -f temp -l debug"
16+
Enter
17+
Sleep 100ms
18+
19+
# Create a random host
20+
Type "n"
21+
Sleep 100ms
22+
Type "network host"
23+
Down
24+
Ctrl+u
25+
Type "127.0.0.1"
26+
Down
27+
Type "network host description"
28+
# Place the host to "manual hosts" group
29+
Down
30+
Type "manual hosts"
31+
Ctrl+S
32+
# Should always give some time when save a host
33+
Sleep 100ms
34+
35+
Type "q"
36+
Sleep 100ms
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
- host:
2+
address: 127.0.0.1
3+
description: network host description
4+
group: manual hosts
5+
title: network host
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
screen_layout: description
2+
ssh_config_path: /tmp/delete_me_goto_ssh_config_test_file
3+
selected: 1
4+
enable_ssh_config: true
5+
theme: default

internal/config/config.go

Lines changed: 18 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -23,14 +23,18 @@ const (
2323

2424
// Configuration structs contains user-definable parameters.
2525
type Configuration struct {
26-
AppMode constant.AppMode
27-
AppName string
28-
DisableFeature FeatureFlag
29-
EnableFeature FeatureFlag
30-
SetTheme string
31-
AppHome string `env:"GG_HOME"`
32-
LogLevel constant.LogLevel `env:"GG_LOG_LEVEL" envDefault:"info"`
33-
SSHConfigFilePath string `env:"GG_SSH_CONFIG_FILE_PATH"`
26+
AppMode constant.AppMode
27+
AppName string
28+
DisableFeature FeatureFlag
29+
EnableFeature FeatureFlag
30+
SetTheme string
31+
AppHome string `env:"GG_HOME"`
32+
LogLevel constant.LogLevel `env:"GG_LOG_LEVEL" envDefault:"info"`
33+
SSHConfigPath string `env:"GG_SSH_CONFIG_FILE_PATH"`
34+
// SetSSHConfigPath is not the same as SSHConfigPath, as when this is set, we must
35+
// write the value to state file and exit. When SSHConfigPath is set, we just use it
36+
// as the path to ssh config within the current application run.
37+
SetSSHConfigPath string
3438
}
3539

3640
func Initialize() (*Configuration, error) {
@@ -81,9 +85,9 @@ func parseCommandLineFlags(envConfig *Configuration, args []string, exitOnError
8185
fs.StringVar(&cmdConfig.AppHome, "f", envConfig.AppHome, "Application home folder")
8286
fs.StringVar(&cmdConfig.LogLevel, "l", envConfig.LogLevel, "Log verbosity level: debug, info")
8387
fs.StringVar(
84-
&cmdConfig.SSHConfigFilePath,
88+
&cmdConfig.SSHConfigPath,
8589
"s",
86-
envConfig.SSHConfigFilePath,
90+
envConfig.SSHConfigPath,
8791
"Specifies an alternative per-user SSH configuration file path",
8892
)
8993
fs.Var(
@@ -97,6 +101,7 @@ func parseCommandLineFlags(envConfig *Configuration, args []string, exitOnError
97101
fmt.Sprintf("Disable feature. Supported values: %s", strings.Join(SupportedFeatures, "|")),
98102
)
99103
fs.StringVar(&cmdConfig.SetTheme, "set-theme", "", "Set application theme")
104+
fs.StringVar(&cmdConfig.SetSSHConfigPath, "set-ssh-config-path", "", "Set SSH configuration file path or URL.")
100105

101106
err := fs.Parse(args[1:]) // args should not include program name, see docs
102107
if err != nil {
@@ -115,6 +120,9 @@ func parseCommandLineFlags(envConfig *Configuration, args []string, exitOnError
115120
case cmdConfig.SetTheme != "":
116121
fmt.Printf("[CONFIG] Set theme to %q\n", cmdConfig.SetTheme)
117122
cmdConfig.AppMode = constant.AppModeType.HandleParam
123+
case cmdConfig.SetSSHConfigPath != "":
124+
fmt.Printf("[CONFIG] Set SSH config file path to %q\n", cmdConfig.SetSSHConfigPath)
125+
cmdConfig.AppMode = constant.AppModeType.HandleParam
118126
}
119127

120128
return &cmdConfig, nil

internal/config/config_test.go

Lines changed: 83 additions & 68 deletions
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,7 @@ func Test_parseEnvironmentConfig(t *testing.T) {
3636
require.Empty(t, envConfig.DisableFeature)
3737
require.Empty(t, envConfig.EnableFeature)
3838
require.Empty(t, envConfig.SetTheme)
39-
require.Empty(t, envConfig.SSHConfigFilePath)
39+
require.Empty(t, envConfig.SSHConfigPath)
4040
require.Equal(t, "info", envConfig.LogLevel)
4141
})
4242

@@ -52,16 +52,16 @@ func Test_parseEnvironmentConfig(t *testing.T) {
5252
require.Empty(t, envConfig.DisableFeature)
5353
require.Empty(t, envConfig.EnableFeature)
5454
require.Empty(t, envConfig.SetTheme)
55-
require.Equal(t, "/tmp/custom_config", envConfig.SSHConfigFilePath)
55+
require.Equal(t, "/tmp/custom_config", envConfig.SSHConfigPath)
5656
require.Equal(t, "debug", envConfig.LogLevel)
5757
})
5858
}
5959

6060
func Test_parseCommandLineFlags(t *testing.T) {
6161
envConfig := &Configuration{
62-
AppHome: "/tmp/home",
63-
LogLevel: "info",
64-
SSHConfigFilePath: "/tmp/custom_config",
62+
AppHome: "/tmp/home",
63+
LogLevel: "info",
64+
SSHConfigPath: "/tmp/custom_config",
6565
}
6666

6767
tests := []struct {
@@ -74,116 +74,130 @@ func Test_parseCommandLineFlags(t *testing.T) {
7474
name: "No command line flags",
7575
args: []string{},
7676
wantConfig: &Configuration{
77-
AppHome: "/tmp/home",
78-
AppMode: "",
79-
DisableFeature: "",
80-
EnableFeature: "",
81-
LogLevel: "info",
82-
SSHConfigFilePath: "/tmp/custom_config",
77+
AppHome: "/tmp/home",
78+
AppMode: "",
79+
DisableFeature: "",
80+
EnableFeature: "",
81+
LogLevel: "info",
82+
SSHConfigPath: "/tmp/custom_config",
8383
},
8484
wantError: false,
8585
}, {
8686
name: "Display help",
8787
args: []string{"-h"},
8888
wantConfig: &Configuration{
89-
AppHome: "/tmp/home",
90-
AppMode: "",
91-
DisableFeature: "",
92-
EnableFeature: "",
93-
LogLevel: "info",
94-
SSHConfigFilePath: "/tmp/custom_config",
95-
SetTheme: "",
89+
AppHome: "/tmp/home",
90+
AppMode: "",
91+
DisableFeature: "",
92+
EnableFeature: "",
93+
LogLevel: "info",
94+
SSHConfigPath: "/tmp/custom_config",
95+
SetTheme: "",
9696
},
9797
wantError: true, // returns flag.ErrHelp, read the os.flag documentation for details
9898
}, {
9999
name: "Display version",
100100
args: []string{"-v"},
101101
wantConfig: &Configuration{
102-
AppHome: "/tmp/home",
103-
AppMode: "DISPLAY_INFO",
104-
DisableFeature: "",
105-
EnableFeature: "",
106-
LogLevel: "info",
107-
SSHConfigFilePath: "/tmp/custom_config",
108-
SetTheme: "",
102+
AppHome: "/tmp/home",
103+
AppMode: "DISPLAY_INFO",
104+
DisableFeature: "",
105+
EnableFeature: "",
106+
LogLevel: "info",
107+
SSHConfigPath: "/tmp/custom_config",
108+
SetTheme: "",
109109
},
110110
wantError: false,
111111
}, {
112112
name: "Set home app folder",
113113
args: []string{"-f", "/tmp/home2"},
114114
wantConfig: &Configuration{
115-
AppHome: "/tmp/home2",
116-
AppMode: "",
117-
DisableFeature: "",
118-
EnableFeature: "",
119-
LogLevel: "info",
120-
SSHConfigFilePath: "/tmp/custom_config",
121-
SetTheme: "",
115+
AppHome: "/tmp/home2",
116+
AppMode: "",
117+
DisableFeature: "",
118+
EnableFeature: "",
119+
LogLevel: "info",
120+
SSHConfigPath: "/tmp/custom_config",
121+
SetTheme: "",
122122
},
123123
wantError: false,
124124
}, {
125125
name: "Set log level",
126126
args: []string{"-l", "debug"},
127127
wantConfig: &Configuration{
128-
AppHome: "/tmp/home",
129-
AppMode: "",
130-
DisableFeature: "",
131-
EnableFeature: "",
132-
LogLevel: "debug",
133-
SSHConfigFilePath: "/tmp/custom_config",
134-
SetTheme: "",
128+
AppHome: "/tmp/home",
129+
AppMode: "",
130+
DisableFeature: "",
131+
EnableFeature: "",
132+
LogLevel: "debug",
133+
SSHConfigPath: "/tmp/custom_config",
134+
SetTheme: "",
135135
},
136136
wantError: false,
137137
}, {
138138
name: "Set SSH config file path",
139139
args: []string{"-s", "/tmp/custom_config2"},
140140
wantConfig: &Configuration{
141-
AppHome: "/tmp/home",
142-
AppMode: "",
143-
DisableFeature: "",
144-
EnableFeature: "",
145-
LogLevel: "info",
146-
SSHConfigFilePath: "/tmp/custom_config2",
147-
SetTheme: "",
141+
AppHome: "/tmp/home",
142+
AppMode: "",
143+
DisableFeature: "",
144+
EnableFeature: "",
145+
LogLevel: "info",
146+
SSHConfigPath: "/tmp/custom_config2",
147+
SetTheme: "",
148148
},
149149
wantError: false,
150150
}, {
151151
name: "Set theme",
152152
args: []string{"--set-theme", "dark"},
153153
wantConfig: &Configuration{
154-
AppHome: "/tmp/home",
155-
AppMode: "HANDLE_PARAM",
156-
DisableFeature: "",
157-
EnableFeature: "",
158-
LogLevel: "info",
159-
SSHConfigFilePath: "/tmp/custom_config",
160-
SetTheme: "dark",
154+
AppHome: "/tmp/home",
155+
AppMode: "HANDLE_PARAM",
156+
DisableFeature: "",
157+
EnableFeature: "",
158+
LogLevel: "info",
159+
SSHConfigPath: "/tmp/custom_config",
160+
SetTheme: "dark",
161161
},
162162
wantError: false,
163163
}, {
164164
name: "Enable feature",
165165
args: []string{"-e", "ssh_config"},
166166
wantConfig: &Configuration{
167-
AppHome: "/tmp/home",
168-
AppMode: "HANDLE_PARAM",
169-
DisableFeature: "",
170-
EnableFeature: "ssh_config",
171-
LogLevel: "info",
172-
SSHConfigFilePath: "/tmp/custom_config",
173-
SetTheme: "",
167+
AppHome: "/tmp/home",
168+
AppMode: "HANDLE_PARAM",
169+
DisableFeature: "",
170+
EnableFeature: "ssh_config",
171+
LogLevel: "info",
172+
SSHConfigPath: "/tmp/custom_config",
173+
SetTheme: "",
174174
},
175175
wantError: false,
176176
}, {
177177
name: "Disable feature",
178178
args: []string{"-d", "ssh_config"},
179179
wantConfig: &Configuration{
180-
AppHome: "/tmp/home",
181-
AppMode: "HANDLE_PARAM",
182-
DisableFeature: "ssh_config",
183-
EnableFeature: "",
184-
LogLevel: "info",
185-
SSHConfigFilePath: "/tmp/custom_config",
186-
SetTheme: "",
180+
AppHome: "/tmp/home",
181+
AppMode: "HANDLE_PARAM",
182+
DisableFeature: "ssh_config",
183+
EnableFeature: "",
184+
LogLevel: "info",
185+
SSHConfigPath: "/tmp/custom_config",
186+
SetTheme: "",
187+
},
188+
wantError: false,
189+
}, {
190+
name: "Set ssh config path",
191+
args: []string{"--set-ssh-config-path", "/tmp/custom_config2"},
192+
wantConfig: &Configuration{
193+
AppHome: "/tmp/home",
194+
AppMode: "HANDLE_PARAM",
195+
DisableFeature: "",
196+
EnableFeature: "",
197+
LogLevel: "info",
198+
SSHConfigPath: "/tmp/custom_config", // this comes from env config, see above
199+
SetSSHConfigPath: "/tmp/custom_config2",
200+
SetTheme: "",
187201
},
188202
wantError: false,
189203
},
@@ -207,7 +221,8 @@ func Test_parseCommandLineFlags(t *testing.T) {
207221
require.Equal(t, tt.wantConfig.DisableFeature, cfg.DisableFeature)
208222
require.Equal(t, tt.wantConfig.EnableFeature, cfg.EnableFeature)
209223
require.Equal(t, tt.wantConfig.LogLevel, cfg.LogLevel)
210-
require.Equal(t, tt.wantConfig.SSHConfigFilePath, cfg.SSHConfigFilePath)
224+
require.Equal(t, tt.wantConfig.SSHConfigPath, cfg.SSHConfigPath)
225+
require.Equal(t, tt.wantConfig.SetSSHConfigPath, cfg.SetSSHConfigPath)
211226
require.Equal(t, tt.wantConfig.SetTheme, cfg.SetTheme)
212227
})
213228
}

internal/model/sshcommand/command.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ func ConnectCommand(options ...Option) string {
1818
}
1919

2020
if sshconfig.IsUserDefinedPath() {
21-
addOption(&sb, OptionConfigFilePath{Value: sshconfig.GetFilePath()})
21+
addOption(&sb, OptionConfigFilePath{Value: sshconfig.Path()})
2222
}
2323

2424
return sb.String()
@@ -34,7 +34,7 @@ func LoadConfigCommand(options ...Option) string {
3434
}
3535

3636
if sshconfig.IsUserDefinedPath() {
37-
addOption(&sb, OptionConfigFilePath{Value: sshconfig.GetFilePath()})
37+
addOption(&sb, OptionConfigFilePath{Value: sshconfig.Path()})
3838
}
3939

4040
return sb.String()

internal/model/sshcommand/option_test.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -126,7 +126,7 @@ func Test_ConnectCommand(t *testing.T) {
126126

127127
// Check that the command uses custom SSH config file path if defined
128128
state.Initialize(context.TODO(),
129-
&config.Configuration{SSHConfigFilePath: "~/.ssh/custom_config"},
129+
&config.Configuration{SSHConfigPath: "~/.ssh/custom_config"},
130130
&mocklogger.Logger{})
131131
actual := ConnectCommand(OptionAddress{Value: "example.com"})
132132
require.Contains(t, actual, `ssh example.com -F "~/.ssh/custom_config"`)
@@ -161,7 +161,7 @@ func Test_LoadConfigCommand(t *testing.T) {
161161

162162
// Repeat the first test with a custom SSH config file path
163163
mockLogger := mocklogger.Logger{}
164-
state.Initialize(context.TODO(), &config.Configuration{SSHConfigFilePath: "~/.ssh/custom_config"}, &mockLogger)
164+
state.Initialize(context.TODO(), &config.Configuration{SSHConfigPath: "~/.ssh/custom_config"}, &mockLogger)
165165
actual := LoadConfigCommand(tests[0].option)
166166
// Should use contains because on Windows version the command starts from 'cmd /c ...'
167167
require.Contains(t, actual, `ssh -G example.com -F "~/.ssh/custom_config"`)

0 commit comments

Comments
 (0)