Skip to content

Commit a04dfc8

Browse files
author
Dmitrii Shelomentsev
committed
GCLOUD2-13152: Profiles
1 parent f870dbf commit a04dfc8

17 files changed

Lines changed: 967 additions & 58 deletions

File tree

cmd/gcore-cli/main.go

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,10 @@
11
package main
22

33
import (
4+
"github.com/G-core/gcore-cli/internal/commands"
45
"github.com/G-core/gcore-cli/internal/core"
56
)
67

78
func main() {
8-
core.Execute()
9+
core.Execute(commands.Commands())
910
}

go.mod

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,6 @@ module github.com/G-core/gcore-cli
22

33
go 1.21.5
44

5-
65
require (
76
github.com/G-Core/FastEdge-client-sdk-go v0.0.0-20240304075046-db0c8c3d17e7
87
github.com/alecthomas/assert v1.0.0
@@ -18,6 +17,7 @@ require (
1817
)
1918

2019
require (
20+
github.com/AlekSi/pointer v1.2.0 // indirect
2121
github.com/alecthomas/colour v0.1.0 // indirect
2222
github.com/alecthomas/repr v0.0.0-20210801044451-80ca428c5142 // indirect
2323
github.com/apapsch/go-jsonmerge/v2 v2.0.0 // indirect
@@ -27,6 +27,7 @@ require (
2727
github.com/go-openapi/swag v0.22.9 // indirect
2828
github.com/google/uuid v1.6.0 // indirect
2929
github.com/hashicorp/hcl v1.0.0 // indirect
30+
github.com/iancoleman/strcase v0.3.0 // indirect
3031
github.com/inconshreveable/mousetrap v1.1.0 // indirect
3132
github.com/invopop/yaml v0.2.0 // indirect
3233
github.com/josharian/intern v1.0.0 // indirect

go.sum

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
github.com/AlekSi/pointer v1.2.0 h1:glcy/gc4h8HnG2Z3ZECSzZ1IX1x2JxRVuDzaJwQE0+w=
2+
github.com/AlekSi/pointer v1.2.0/go.mod h1:gZGfd3dpW4vEc/UlyfKKi1roIqcCgwOIvb0tSNSBle0=
13
github.com/G-Core/FastEdge-client-sdk-go v0.0.0-20240214130448-d87df1e38764 h1:7CATrk7BJpU1t4wr2avczZaX1KrwsjhArBKBaiSQN2M=
24
github.com/G-Core/FastEdge-client-sdk-go v0.0.0-20240214130448-d87df1e38764/go.mod h1:ggyUVhy8/OCMBY4nbm7n9qDoPioROCk4vHhDJq9w7qE=
35
github.com/G-Core/FastEdge-client-sdk-go v0.0.0-20240304075046-db0c8c3d17e7 h1:99yyAfaF6OV2ghz75yE72LwdzxP6gUYa3hL7XkObb5s=
@@ -43,6 +45,8 @@ github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
4345
github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
4446
github.com/hashicorp/hcl v1.0.0 h1:0Anlzjpi4vEasTeNFn2mLJgTSwt0+6sfsiTG8qcWGx4=
4547
github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ=
48+
github.com/iancoleman/strcase v0.3.0 h1:nTXanmYxhfFAMjZL34Ov6gkzEsSJZ5DbhxWjvSASxEI=
49+
github.com/iancoleman/strcase v0.3.0/go.mod h1:iwCmte+B7n89clKwxIoIXy/HfoL7AsD47ZCWhYzw7ho=
4650
github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8=
4751
github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw=
4852
github.com/invopop/yaml v0.2.0 h1:7zky/qH+O0DwAyoobXUqvVBwgBFRxKoQ/3FjcVpjTMY=

internal/commands/commands.go

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
package commands
2+
3+
import (
4+
"github.com/spf13/cobra"
5+
6+
"github.com/G-core/gcore-cli/internal/commands/config"
7+
"github.com/G-core/gcore-cli/internal/commands/fastedge"
8+
initCmd "github.com/G-core/gcore-cli/internal/commands/init"
9+
)
10+
11+
func Commands() []*cobra.Command {
12+
return []*cobra.Command{
13+
fastedge.Commands(),
14+
initCmd.Commands(),
15+
config.Commands(),
16+
}
17+
}

internal/commands/config/config.go

Lines changed: 149 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,149 @@
1+
package config
2+
3+
import (
4+
"fmt"
5+
"slices"
6+
7+
"github.com/spf13/cobra"
8+
9+
"github.com/G-core/gcore-cli/internal/config"
10+
"github.com/G-core/gcore-cli/internal/core"
11+
"github.com/G-core/gcore-cli/internal/output"
12+
)
13+
14+
func Commands() *cobra.Command {
15+
var cmd = &cobra.Command{
16+
Use: "config",
17+
Short: "Config file management",
18+
GroupID: "configuration",
19+
Run: func(cmd *cobra.Command, args []string) {
20+
cmd.Help()
21+
},
22+
}
23+
24+
cmd.AddCommand(info(), get(), set(), unset(), dump(), profileCmd())
25+
return cmd
26+
}
27+
28+
func profileCmd() *cobra.Command {
29+
var cmd = &cobra.Command{
30+
Use: "profile",
31+
Short: "Commands to manage profiles from the config",
32+
Run: func(cmd *cobra.Command, args []string) {
33+
cmd.Help()
34+
},
35+
}
36+
37+
cmd.AddCommand(listProfiles(), switchProfileCmd(), deleteProfileCmd())
38+
return cmd
39+
}
40+
41+
func deleteProfileCmd() *cobra.Command {
42+
cmd := &cobra.Command{
43+
Use: "delete <profile>",
44+
Aliases: []string{"d"},
45+
ValidArgsFunction: core.ProfileCompletion,
46+
Short: "Delete profile from the config",
47+
RunE: func(cmd *cobra.Command, args []string) error {
48+
if len(args) == 0 {
49+
cmd.Help()
50+
51+
return nil
52+
}
53+
54+
profileName := args[0]
55+
ctx := cmd.Context()
56+
cfg := core.ExtractConfig(ctx)
57+
active := core.ExtractProfile(ctx)
58+
59+
_, exist := cfg.Profiles[profileName]
60+
if exist {
61+
delete(cfg.Profiles, profileName)
62+
} else {
63+
return fmt.Errorf("profile '%s' doesn't exist", profileName)
64+
}
65+
66+
if active == profileName {
67+
cfg.ActiveProfile = config.DefaultProfile
68+
}
69+
70+
path, err := core.ExtractConfigPath(ctx)
71+
if err != nil {
72+
return err
73+
}
74+
75+
return cfg.Save(path)
76+
},
77+
}
78+
79+
return cmd
80+
}
81+
82+
func listProfiles() *cobra.Command {
83+
cmd := &cobra.Command{
84+
Use: "list",
85+
Aliases: []string{"ls"},
86+
Short: "Display list of available profiles in the config",
87+
RunE: func(cmd *cobra.Command, args []string) error {
88+
ctx := cmd.Context()
89+
cfg := core.ExtractConfig(ctx)
90+
91+
profiles := append([]profileView{}, toProfileView(config.DefaultProfile, &cfg.Profile))
92+
93+
var names []string
94+
for name, _ := range cfg.Profiles {
95+
names = append(names, name)
96+
}
97+
slices.Sort(names)
98+
99+
for _, name := range names {
100+
pv := toProfileView(name, cfg.Profiles[name])
101+
102+
profiles = append(profiles, pv)
103+
}
104+
105+
output.Print(profiles)
106+
107+
return nil
108+
},
109+
}
110+
111+
return cmd
112+
}
113+
114+
func switchProfileCmd() *cobra.Command {
115+
cmd := &cobra.Command{
116+
Use: "switch <profile>",
117+
ValidArgsFunction: core.ProfileCompletion,
118+
Short: "Make selected profile active",
119+
RunE: func(cmd *cobra.Command, args []string) error {
120+
if len(args) == 0 {
121+
cmd.Help()
122+
123+
return nil
124+
}
125+
126+
profileName := args[0]
127+
ctx := cmd.Context()
128+
cfg := core.ExtractConfig(ctx)
129+
130+
_, exist := cfg.Profiles[profileName]
131+
if exist {
132+
cfg.ActiveProfile = profileName
133+
} else if profileName != config.DefaultProfile {
134+
return fmt.Errorf("profile '%s' doesn't exist", profileName)
135+
} else {
136+
cfg.ActiveProfile = config.DefaultProfile
137+
}
138+
139+
path, err := core.ExtractConfigPath(ctx)
140+
if err != nil {
141+
return err
142+
}
143+
144+
return cfg.Save(path)
145+
},
146+
}
147+
148+
return cmd
149+
}

internal/commands/config/dump.go

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
package config
2+
3+
import (
4+
"github.com/AlekSi/pointer"
5+
"github.com/spf13/cobra"
6+
7+
"github.com/G-core/gcore-cli/internal/core"
8+
"github.com/G-core/gcore-cli/internal/output"
9+
)
10+
11+
func dump() *cobra.Command {
12+
var cmd = &cobra.Command{
13+
Use: "dump",
14+
Short: "Dumps the config file",
15+
Args: cobra.NoArgs,
16+
RunE: func(cmd *cobra.Command, args []string) (err error) {
17+
ctx := cmd.Context()
18+
cfg := core.ExtractConfig(ctx)
19+
20+
// Secure keys
21+
cfg.Profile.ApiKey = pointer.To(secureKey(cfg.Profile.ApiKey))
22+
for _, profile := range cfg.Profiles {
23+
profile.ApiKey = pointer.To(secureKey(profile.ApiKey))
24+
}
25+
26+
output.Print(cfg)
27+
28+
return nil
29+
},
30+
}
31+
32+
return cmd
33+
}

internal/commands/config/get.go

Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,74 @@
1+
package config
2+
3+
import (
4+
"fmt"
5+
"reflect"
6+
7+
"github.com/iancoleman/strcase"
8+
"github.com/spf13/cobra"
9+
10+
"github.com/G-core/gcore-cli/internal/config"
11+
"github.com/G-core/gcore-cli/internal/core"
12+
"github.com/G-core/gcore-cli/internal/output"
13+
)
14+
15+
func getProfileField(profile *config.Profile, key string) (reflect.Value, error) {
16+
field := reflect.ValueOf(profile).Elem().FieldByName(strcase.ToCamel(key))
17+
reflect.ValueOf(profile).Elem().FieldByNameFunc(func(s string) bool {
18+
return key == strcase.ToKebab(s)
19+
})
20+
21+
if !field.IsValid() {
22+
return reflect.ValueOf(nil), fmt.Errorf("invalid key: %s", key)
23+
}
24+
25+
return field, nil
26+
}
27+
28+
func getProfileValue(profile *config.Profile, fieldName string) (interface{}, error) {
29+
field, err := getProfileField(profile, fieldName)
30+
if err != nil {
31+
return nil, err
32+
}
33+
return field.Interface(), nil
34+
}
35+
36+
func get() *cobra.Command {
37+
var cmd = &cobra.Command{
38+
Use: "get <property>",
39+
Short: "Get property value from the config file",
40+
ValidArgs: []string{"api-url", "api-key"},
41+
Args: func(cmd *cobra.Command, args []string) error {
42+
if len(args) == 0 {
43+
cmd.Help()
44+
}
45+
46+
return nil
47+
},
48+
RunE: func(cmd *cobra.Command, args []string) error {
49+
if len(args) == 0 {
50+
return nil
51+
}
52+
53+
ctx := cmd.Context()
54+
profileName := core.ExtractProfile(ctx)
55+
cfg := core.ExtractConfig(ctx)
56+
57+
profile, err := cfg.GetProfile(profileName)
58+
if err != nil {
59+
return err
60+
}
61+
62+
value, err := getProfileValue(profile, args[0])
63+
if err != nil {
64+
return err
65+
}
66+
67+
output.Print(value)
68+
69+
return nil
70+
},
71+
}
72+
73+
return cmd
74+
}

internal/commands/config/info.go

Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,69 @@
1+
package config
2+
3+
import (
4+
"strings"
5+
6+
"github.com/AlekSi/pointer"
7+
"github.com/spf13/cobra"
8+
9+
"github.com/G-core/gcore-cli/internal/config"
10+
"github.com/G-core/gcore-cli/internal/core"
11+
"github.com/G-core/gcore-cli/internal/output"
12+
)
13+
14+
type profileView struct {
15+
Name string
16+
ApiUrl *string
17+
ApiKey *string
18+
}
19+
20+
func toProfileView(name string, profile *config.Profile) profileView {
21+
var pv = profileView{
22+
Name: name,
23+
}
24+
25+
if profile.ApiUrl != nil {
26+
pv.ApiUrl = profile.ApiUrl
27+
}
28+
29+
if profile.ApiKey != nil {
30+
pv.ApiKey = pointer.To(secureKey(profile.ApiKey))
31+
}
32+
33+
return pv
34+
}
35+
36+
func info() *cobra.Command {
37+
var cmd = &cobra.Command{
38+
Use: "info",
39+
Short: "Get information about config profile",
40+
Args: cobra.NoArgs,
41+
RunE: func(cmd *cobra.Command, args []string) error {
42+
ctx := cmd.Context()
43+
profile, err := core.GetClientProfile(ctx)
44+
if err != nil {
45+
return err
46+
}
47+
48+
output.Print(toProfileView(core.ExtractProfile(ctx), profile))
49+
50+
return nil
51+
},
52+
}
53+
54+
return cmd
55+
}
56+
57+
func secureKey(key *string) string {
58+
if key == nil || *key == "" {
59+
return ""
60+
}
61+
62+
var p1 = 0 + 5
63+
var p2 = len(*key) - 1 - 5
64+
if p1 > p2 {
65+
return "XXXXXX"
66+
}
67+
68+
return strings.Join([]string{(*key)[0:p1], "XXXXXX", (*key)[p2 : len((*key))-1]}, "")
69+
}

0 commit comments

Comments
 (0)