Skip to content

Commit 8924022

Browse files
committed
feat(target): Add ability to extend the target options
1 parent b05f8c8 commit 8924022

File tree

4 files changed

+163
-48
lines changed

4 files changed

+163
-48
lines changed

pkg/output/format.go

Lines changed: 25 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -81,12 +81,22 @@ func printTable(object interface{}) {
8181
}
8282
}
8383

84-
func nameFromField(f reflect.StructField) string {
85-
if f.Tag != "" && f.Tag.Get("yaml") != "" {
86-
return strings.Split(f.Tag.Get("yaml"), ",")[0]
84+
func tags(f reflect.StructField) []string {
85+
if f.Tag != "" {
86+
for _, tName := range []string{"yaml", "json"} {
87+
if f.Tag.Get(tName) != "" {
88+
return strings.Split(f.Tag.Get(tName), ",")
89+
}
90+
}
8791
}
88-
if f.Tag != "" && f.Tag.Get("json") != "" {
89-
return strings.Split(f.Tag.Get("json"), ",")[0]
92+
93+
return nil
94+
}
95+
96+
func nameFromField(f reflect.StructField) string {
97+
t := tags(f)
98+
if len(t) > 0 {
99+
return t[0]
90100
}
91101
return ""
92102
}
@@ -136,7 +146,9 @@ func printList(object interface{}, out io.Writer) {
136146
case reflect.Struct:
137147
row := table.Row{}
138148
for fi := 0; fi < v.Index(i).NumField(); fi++ {
139-
row = append(row, v.Index(i).Field(fi))
149+
if len(row) < len(names) {
150+
row = append(row, v.Index(i).Field(fi))
151+
}
140152
}
141153
rows = append(rows, row)
142154
case reflect.Slice, reflect.Array, reflect.Func, reflect.Chan, reflect.Interface, reflect.Map:
@@ -183,7 +195,9 @@ func printMap(object interface{}, out io.Writer) {
183195
case reflect.Struct:
184196
row := table.Row{k}
185197
for fi := 0; fi < v.NumField(); fi++ {
186-
row = append(row, v.Field(fi))
198+
if len(row) <= len(names) {
199+
row = append(row, v.Field(fi))
200+
}
187201
}
188202
rows = append(rows, row)
189203
case reflect.Slice, reflect.Array, reflect.Func, reflect.Chan, reflect.Interface, reflect.Map:
@@ -212,7 +226,10 @@ func printStruct(object interface{}, out io.Writer) {
212226
v := reflect.ValueOf(object)
213227
t := reflect.TypeOf(object)
214228
for fi := 0; fi < v.NumField(); fi++ {
215-
rows = append(rows, table.Row{strings.ToUpper(nameFromField(t.Field(fi))), v.Field(fi)})
229+
name := nameFromField(t.Field(fi))
230+
if name != "" {
231+
rows = append(rows, table.Row{strings.ToUpper(name), v.Field(fi)})
232+
}
216233
}
217234

218235
tab.AppendRows(rows)

pkg/target/options.go

Lines changed: 70 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,11 @@
1717
package target
1818

1919
import (
20+
"fmt"
21+
"strings"
22+
2023
"github.com/mitchellh/mapstructure"
24+
"github.com/spf13/cast"
2125
"github.com/spf13/cobra"
2226
"github.com/spf13/viper"
2327

@@ -34,31 +38,81 @@ const (
3438
)
3539

3640
var (
37-
target string
38-
provider string
39-
region string
40-
Providers = []string{Aws, Azure, Gcp, Digitalocean}
41+
target string
42+
provider string
43+
region string
44+
extraConfig []string
45+
Providers = []string{Aws, Azure, Gcp, Digitalocean}
4146
)
4247

48+
func toStringMapStringMapStringE(i interface{}) (map[string]map[string]interface{}, error) {
49+
switch v := i.(type) {
50+
case map[string]map[string]interface{}:
51+
return v, nil
52+
case map[string]interface{}:
53+
var err error
54+
m := make(map[string]map[string]interface{})
55+
56+
for k, val := range v {
57+
m[k], err = cast.ToStringMapE(val)
58+
if err != nil {
59+
return nil, err
60+
}
61+
}
62+
return m, nil
63+
default:
64+
return nil, fmt.Errorf("unable to cast %#v of type %T to map[string]map[string]interface{}", i, i)
65+
}
66+
}
67+
4368
func FromOptions() (*Target, error) {
4469
t := Target{}
70+
4571
if target == "" {
4672
target = DefaultTarget
4773
t.Provider = DefaultProvider
48-
if provider != "" {
49-
t.Provider = provider
50-
}
51-
if region != "" {
52-
t.Region = region
53-
}
54-
} else {
55-
targets := map[string]Target{}
56-
err := mapstructure.Decode(viper.GetStringMap("targets"), &targets)
74+
}
75+
targets, err := toStringMapStringMapStringE(viper.Get("targets"))
76+
if err != nil {
77+
return nil, err
78+
}
79+
tMap := targets[target]
80+
if tMap != nil {
81+
err := mapstructure.Decode(tMap, &t)
5782
if err != nil {
5883
return nil, err
5984
}
60-
t = targets[target]
85+
86+
if len(tMap) > 3 {
87+
// Decode the "extra" map for provider specific values
88+
delete(tMap, "provider")
89+
delete(tMap, "region")
90+
delete(tMap, "name")
91+
err := mapstructure.Decode(tMap, &t.Extra)
92+
if err != nil {
93+
return nil, err
94+
}
95+
}
96+
}
97+
98+
if provider != "" {
99+
t.Provider = provider
100+
}
101+
102+
if region != "" {
103+
t.Region = region
61104
}
105+
106+
if len(extraConfig) > 0 && t.Extra == nil {
107+
t.Extra = map[string]interface{}{}
108+
}
109+
for _, c := range extraConfig {
110+
sc := strings.Split(c, "=")
111+
if len(sc) == 2 {
112+
t.Extra[sc[0]] = sc[1]
113+
}
114+
}
115+
62116
return &t, nil
63117
}
64118

@@ -77,7 +131,7 @@ func AddOptions(cmd *cobra.Command, providerOnly bool) error {
77131
return err
78132
}
79133

80-
cmd.Flags().VarP(pflagext.NewStringEnumVar(&provider, Providers, Aws), "provider", "p", "the provider to deploy to")
134+
cmd.Flags().VarP(pflagext.NewStringEnumVar(&provider, Providers, ""), "provider", "p", "the provider to deploy to")
81135
err = cmd.RegisterFlagCompletionFunc("provider", func(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) {
82136
return Providers, cobra.ShellCompDirectiveDefault
83137
})
@@ -87,6 +141,7 @@ func AddOptions(cmd *cobra.Command, providerOnly bool) error {
87141

88142
if !providerOnly {
89143
cmd.Flags().StringVarP(&region, "region", "r", "", "the region to deploy to")
144+
cmd.Flags().StringSliceVarP(&extraConfig, "extra", "e", nil, "provider specific extra config")
90145
}
91146
return nil
92147
}

pkg/target/options_test.go

Lines changed: 65 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -20,50 +20,92 @@ import (
2020
"reflect"
2121
"testing"
2222

23-
"github.com/mitchellh/mapstructure"
2423
"github.com/spf13/viper"
2524
)
2625

2726
func TestFromOptions(t *testing.T) {
2827
tests := []struct {
29-
name string
30-
target string
31-
provider string
32-
region string
33-
config map[string]Target
34-
want *Target
28+
name string
29+
target string
30+
provider string
31+
region string
32+
extraConfig []string
33+
config map[string]map[string]interface{}
34+
want *Target
3535
}{
3636
{
3737
name: "default",
38+
config: map[string]map[string]interface{}{
39+
"test": {
40+
"provider": "aws",
41+
"region": "westus",
42+
},
43+
},
3844
want: &Target{Provider: Aws, Region: ""},
3945
},
4046
{
4147
name: "from config",
42-
target: "aws",
43-
config: map[string]Target{"aws": {Provider: Aws, Region: "westus"}},
44-
want: &Target{Provider: Aws, Region: "westus"},
48+
target: "az",
49+
config: map[string]map[string]interface{}{
50+
"az": {
51+
"provider": "azure",
52+
"region": "jioindiawest",
53+
},
54+
"aws": {
55+
"provider": "aws",
56+
"region": "us-west-2",
57+
},
58+
},
59+
want: &Target{Provider: Azure, Region: "jioindiawest"},
4560
},
4661
{
47-
name: "from args",
48-
provider: "azure",
49-
region: "eastus",
50-
want: &Target{Provider: Azure, Region: "eastus"},
62+
name: "from config with extra",
63+
target: "azure",
64+
config: map[string]map[string]interface{}{
65+
"azure": {
66+
"provider": "azure",
67+
"region": "westus",
68+
"org": "nitric.io",
69+
"adminemail": "[email protected]",
70+
},
71+
},
72+
want: &Target{
73+
Provider: Azure,
74+
Region: "westus",
75+
Extra: map[string]interface{}{
76+
"org": "nitric.io",
77+
"adminemail": "[email protected]",
78+
},
79+
},
80+
},
81+
{
82+
name: "from args",
83+
provider: "azure",
84+
region: "eastus",
85+
extraConfig: []string{"org=nitric.io", "[email protected]"},
86+
config: map[string]map[string]interface{}{
87+
"azure": {
88+
"provider": "azure",
89+
"region": "westus",
90+
},
91+
},
92+
want: &Target{
93+
Provider: Azure,
94+
Region: "eastus",
95+
Extra: map[string]interface{}{
96+
"org": "nitric.io",
97+
"adminemail": "[email protected]",
98+
},
99+
},
51100
},
52101
}
53102
for _, tt := range tests {
54103
t.Run(tt.name, func(t *testing.T) {
55104
target = tt.target
56105
provider = tt.provider
57106
region = tt.region
58-
if tt.target != "" {
59-
current := viper.GetStringMap("targets")
60-
err := mapstructure.Decode(tt.config, &current)
61-
if err != nil {
62-
t.Error(err)
63-
}
64-
65-
viper.Set("targets", current)
66-
}
107+
extraConfig = tt.extraConfig
108+
viper.Set("targets", tt.config)
67109
got, err := FromOptions()
68110
if err != nil {
69111
t.Errorf("FromOptions() error = %v", err)

pkg/target/types.go

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717
package target
1818

1919
type Target struct {
20-
Provider string `json:"provider,omitempty"`
21-
Region string `json:"region,omitempty"`
20+
Provider string `json:"provider,omitempty"`
21+
Region string `json:"region,omitempty"`
22+
Extra map[string]interface{} `json:",inline,omitempty"`
2223
}

0 commit comments

Comments
 (0)