Skip to content

Commit 1d41f9b

Browse files
authored
Allow interactive px cli usage to prompt and save preferred cloud in pixie config file (#1964)
Summary: Allow interactive px cli usage to prompt and save preferred cloud in pixie config file This is a continuation of the plan I outlined in #1960 now that cloud addr is required for the `px` cli. Relevant Issues: N/A Type of change: /kind feature Test Plan: Verified the following scenarios. I will make sure to update the PX cli release checklist accordingly if/when approved <details><summary>Default cloud selection testing</summary> - [x] Running `px` command that requires cloud prompts for selection and whether to store it ``` $ ./px auth login Pixie CLI ✔ withpixie.ai:443 ✔ No <------ Cloud selection was opted out of Starting browser... (if browser-based login fails, try running `px auth login --manual` for headless login) Fetching refresh token ... Failed to perform browser based auth. Will try manual auth error=browser failed to open Please Visit: https://work.withpixie.ai:443/login?local_mode=true Copy and paste token here: ^C ``` - [x] Cli commands following storing preferred cloud use the preferred value ``` $ ./px auth login Pixie CLI ✔ withpixie.ai:443 ✔ Yes <------ Cloud selection was opted into saving Starting browser... (if browser-based login fails, try running `px auth login --manual` for headless login) Fetching refresh token ... Failed to perform browser based auth. Will try manual auth error=browser failed to open Please Visit: https://work.withpixie.ai:443/login?local_mode=true Copy and paste token here: ^C $ ./px auth login Pixie CLI Starting browser... (if browser-based login fails, try running `px auth login --manual` for headless login) Fetching refresh token ... Failed to perform browser based auth. Will try manual auth error=browser failed to open Please Visit: https://work.withpixie.ai:443/login?local_mode=true Copy and paste token here: ^C $ cat ~/.pixie/config.json {"uniqueClientID":"XXX","cloudAddr":"withpixie.ai:443"} ``` - [x] Using `--cloud_addr` overrides the value set in config file ``` $ cat ~/.pixie/config.json {"uniqueClientID":"XXX","cloudAddr":"boguscloud.com"} $ ./px --cloud_addr=withpixie.ai auth login Pixie CLI Starting browser... (if browser-based login fails, try running `px auth login --manual` for headless login) Fetching refresh token ... Failed to perform browser based auth. Will try manual auth error=browser failed to open Please Visit: https://work.withpixie.ai:443/login?local_mode=true Copy and paste token here: ``` - [x] Running non-interactively uses cloud stored in config file - [x] Running non-interactively without preferred cloud or `--cloud_addr` results in error </details> <details><summary>`px config` command testing</summary> - [x] `px config list` prints out cloud addr ``` $ ./px config list Pixie CLI CloudAddr: boguscloud.com ``` - [x] `px config set` validates arguments ``` $ ./px config set --key NonExistant --value tesitng Pixie CLI FATA[0000]src/pixie_cli/pkg/cmd/config.go:80 px.dev/pixie/src/pixie_cli/pkg/cmd.glob..func16() Key 'NonExistant' is not settable. Must be one of [CloudAddr] $ ./px config set --key CloudAddr --value withpixie.ai:443 --value testing Pixie CLI FATA[0000]src/pixie_cli/pkg/cmd/config.go:74 px.dev/pixie/src/pixie_cli/pkg/cmd.glob..func16() the number of --key and --value flags must match ``` - [x] `px config set` updates config file ``` $ ./px config set --key CloudAddr --value withpixie.ai:443 Pixie CLI $ ./px config list Pixie CLI CloudAddr: withpixie.ai:443 ``` </details> Changelog Message: Update `px` cli to store preferred cloud in pixie config file Signed-off-by: Dom Del Nano <ddelnano@gmail.com>
1 parent f0dadea commit 1d41f9b

File tree

5 files changed

+169
-2
lines changed

5 files changed

+169
-2
lines changed

go.mod

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,7 @@ require (
4545
github.com/mattn/go-isatty v0.0.17
4646
github.com/mattn/go-runewidth v0.0.9
4747
github.com/mikefarah/yq/v4 v4.30.8
48+
github.com/mitchellh/mapstructure v1.5.0
4849
github.com/nats-io/nats-server/v2 v2.10.4
4950
github.com/nats-io/nats.go v1.31.0
5051
github.com/olekukonko/tablewriter v0.0.5
@@ -214,7 +215,6 @@ require (
214215
github.com/mitchellh/copystructure v1.0.0 // indirect
215216
github.com/mitchellh/go-homedir v1.1.0 // indirect
216217
github.com/mitchellh/go-wordwrap v1.0.0 // indirect
217-
github.com/mitchellh/mapstructure v1.5.0 // indirect
218218
github.com/mitchellh/reflectwalk v1.0.0 // indirect
219219
github.com/moby/spdystream v0.2.0 // indirect
220220
github.com/moby/term v0.0.0-20220808134915-39b0c02b01ae // indirect

src/pixie_cli/pkg/cmd/BUILD.bazel

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ go_library(
2323
"auth.go",
2424
"bindata.gen.go",
2525
"collect_logs.go",
26+
"config.go",
2627
"create_bundle.go",
2728
"create_cloud_certs.go",
2829
"debug.go",
@@ -73,6 +74,7 @@ go_library(
7374
"@com_github_lestrrat_go_jwx//jwt",
7475
"@com_github_manifoldco_promptui//:promptui",
7576
"@com_github_mattn_go_isatty//:go-isatty",
77+
"@com_github_mitchellh_mapstructure//:mapstructure",
7678
"@com_github_segmentio_analytics_go_v3//:analytics-go",
7779
"@com_github_sirupsen_logrus//:logrus",
7880
"@com_github_spf13_cobra//:cobra",

src/pixie_cli/pkg/cmd/config.go

Lines changed: 108 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,108 @@
1+
/*
2+
* Copyright 2018- The Pixie Authors.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*
16+
* SPDX-License-Identifier: Apache-2.0
17+
*/
18+
19+
package cmd
20+
21+
import (
22+
"fmt"
23+
"os"
24+
25+
"github.com/mitchellh/mapstructure"
26+
log "github.com/sirupsen/logrus"
27+
"github.com/spf13/cobra"
28+
"golang.org/x/exp/slices"
29+
30+
"px.dev/pixie/src/pixie_cli/pkg/pxconfig"
31+
)
32+
33+
func init() {
34+
ConfigCmd.AddCommand(ConfigListCmd)
35+
ConfigCmd.AddCommand(ConfigSetCmd)
36+
37+
ConfigSetCmd.Flags().StringArray("key", []string{}, "Key to set")
38+
ConfigSetCmd.Flags().StringArray("value", []string{}, "Value to set")
39+
}
40+
41+
var settableConfigKeys = pxconfig.GetSettableConfigKeys()
42+
43+
var ConfigSetCmd = &cobra.Command{
44+
Use: "set",
45+
Short: "Set configuration options",
46+
Run: func(cmd *cobra.Command, args []string) {
47+
keys, _ := cmd.Flags().GetStringArray("key")
48+
values, _ := cmd.Flags().GetStringArray("value")
49+
50+
input := map[string]interface{}{}
51+
for i := 0; i < len(keys); i++ {
52+
key := keys[i]
53+
value := values[i]
54+
input[key] = value
55+
}
56+
57+
cfg := pxconfig.Cfg()
58+
err := mapstructure.Decode(input, cfg)
59+
60+
if err != nil {
61+
log.Fatalf("Failed to set config: %v", err)
62+
}
63+
64+
err = pxconfig.UpdateConfig(cfg)
65+
if err != nil {
66+
log.Fatalf("Failed to update config: %v", err)
67+
}
68+
},
69+
PreRun: func(cmd *cobra.Command, args []string) {
70+
keys, _ := cmd.Flags().GetStringArray("key")
71+
values, _ := cmd.Flags().GetStringArray("value")
72+
73+
if len(keys) != len(values) {
74+
log.Fatal("the number of --key and --value flags must match")
75+
os.Exit(1)
76+
}
77+
78+
for _, key := range keys {
79+
if !slices.Contains(settableConfigKeys, key) {
80+
log.Fatalf("Key '%s' is not settable. Must be one of %v", key, settableConfigKeys)
81+
}
82+
}
83+
},
84+
}
85+
86+
// ConfigListCmd is the "config list" command.
87+
var ConfigListCmd = &cobra.Command{
88+
Use: "list",
89+
Short: "List configuration options",
90+
Run: func(cmd *cobra.Command, args []string) {
91+
cfg := pxconfig.Cfg().ConfigInfoSettable
92+
93+
var configuration map[string]interface{}
94+
err := mapstructure.Decode(cfg, &configuration)
95+
if err != nil {
96+
log.Fatalf("Failed to decode config: %v", err)
97+
}
98+
for key, value := range configuration {
99+
fmt.Printf("%s: %v\n", key, value)
100+
}
101+
},
102+
}
103+
104+
// ConfigCmd is the "config" command.
105+
var ConfigCmd = &cobra.Command{
106+
Use: "config",
107+
Short: "Get information about the pixie config file",
108+
}

src/pixie_cli/pkg/cmd/root.go

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -72,6 +72,7 @@ func init() {
7272
viper.BindPFlag("direct_vizier_key", RootCmd.PersistentFlags().Lookup("direct_vizier_key"))
7373

7474
RootCmd.AddCommand(VersionCmd)
75+
RootCmd.AddCommand(ConfigCmd)
7576
RootCmd.AddCommand(AuthCmd)
7677
RootCmd.AddCommand(CollectLogsCmd)
7778
RootCmd.AddCommand(CreateCloudCertsCmd)
@@ -201,6 +202,8 @@ var RootCmd = &cobra.Command{
201202
// Name a variable to store a slice of commands that don't require cloudAddr
202203
var cmdsCloudAddrNotReqd = []*cobra.Command{
203204
CollectLogsCmd,
205+
ConfigListCmd,
206+
ConfigSetCmd,
204207
VersionCmd,
205208
}
206209

@@ -212,6 +215,12 @@ func getCloudAddrIfRequired(cmd *cobra.Command) string {
212215
}
213216

214217
cloudAddr := viper.GetString("cloud_addr")
218+
cfg := pxconfig.Cfg()
219+
defaultCloudAddr = cfg.CloudAddr
220+
if cloudAddr == "" && defaultCloudAddr != "" {
221+
cloudAddr = defaultCloudAddr
222+
viper.Set("cloud_addr", cloudAddr)
223+
}
215224
if cloudAddr == "" {
216225
if !isatty.IsTerminal(os.Stdin.Fd()) {
217226
utils.Errorf("No cloud address provided during run within non-interactive shell. Please set the cloud address using the `--cloud_addr` flag or `PX_CLOUD_ADDR` environment variable.")
@@ -229,6 +238,26 @@ func getCloudAddrIfRequired(cmd *cobra.Command) string {
229238

230239
cloudAddr = selectedCloud
231240
viper.Set("cloud_addr", cloudAddr)
241+
242+
defaultCloudPrompt := promptui.Select{
243+
Label: "Set as default cloud address?",
244+
Items: []string{"Yes", "No"},
245+
}
246+
_, storeDefault, err := defaultCloudPrompt.Run()
247+
if err != nil {
248+
utils.WithError(err).Fatal("Failed to select default cloud address")
249+
os.Exit(1)
250+
}
251+
252+
if storeDefault == "Yes" {
253+
cfg.CloudAddr = cloudAddr
254+
err := pxconfig.UpdateConfig(cfg)
255+
256+
if err != nil {
257+
utils.WithError(err).Fatal("Failed to update config file with default cloud address")
258+
os.Exit(1)
259+
}
260+
}
232261
}
233262
}
234263
return cloudAddr

src/pixie_cli/pkg/pxconfig/config.go

Lines changed: 29 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ package pxconfig
2121
import (
2222
"encoding/json"
2323
"os"
24+
"reflect"
2425
"sync"
2526

2627
"github.com/gofrs/uuid"
@@ -31,14 +32,41 @@ import (
3132
// ConfigInfo store the config about the CLI.
3233
type ConfigInfo struct {
3334
// UniqueClientID is the ID assigned to this user on first startup when auth information is not know. This can be later associated with the UserID.
34-
UniqueClientID string `json:"uniqueClientID"`
35+
UniqueClientID string `json:"uniqueClientID"`
36+
ConfigInfoSettable `mapstructure:",squash"`
37+
}
38+
39+
type ConfigInfoSettable struct {
40+
CloudAddr string `json:"cloudAddr,omitempty"`
3541
}
3642

3743
var (
3844
config *ConfigInfo
3945
once sync.Once
4046
)
4147

48+
func GetSettableConfigKeys() []string {
49+
val := reflect.ValueOf(ConfigInfoSettable{})
50+
keys := []string{}
51+
for i := 0; i < val.NumField(); i++ {
52+
keys = append(keys, val.Type().Field(i).Name)
53+
}
54+
return keys
55+
}
56+
57+
func UpdateConfig(cfg *ConfigInfo) error {
58+
configPath, err := utils.EnsureDefaultConfigFilePath()
59+
if err != nil {
60+
utils.WithError(err).Fatal("Failed to load/create config file path")
61+
}
62+
f, err := os.OpenFile(configPath, os.O_RDWR|os.O_TRUNC, 0600)
63+
if err != nil {
64+
return err
65+
}
66+
defer f.Close()
67+
return json.NewEncoder(f).Encode(cfg)
68+
}
69+
4270
func writeDefaultConfig(path string) (*ConfigInfo, error) {
4371
f, err := os.OpenFile(path, os.O_RDWR|os.O_CREATE, 0600)
4472
if err != nil {

0 commit comments

Comments
 (0)