Skip to content
This repository was archived by the owner on Jul 31, 2023. It is now read-only.

Commit cd8c10d

Browse files
oppermaxbemica
andauthored
Add more env specific colors for switch-roles and introduce ordering of profiles (#64)
* upgrade to go 1.18 * upgrade go version in gh actions * upgrade golanci lint version * update dependencies * update vendor * add more environment specific colors * add ordered flag * add function to order a slice of profiles * order vault profiles * order switch-roles profiles * update tests * vendor * update docs * fix order generation * add tests for ordering * add an ✨oxford comma✨🎓 Co-authored-by: Mica Beneke <[email protected]> Co-authored-by: Mica Beneke <[email protected]>
1 parent a7b414f commit cd8c10d

19 files changed

+1617
-18
lines changed

Diff for: README.md

+3
Original file line numberDiff line numberDiff line change
@@ -89,6 +89,7 @@ OPTIONAL
8989
except the source profile and generated config
9090
--use-role-name-in-profile=false Append the role name to the profile name
9191
--role=STRING If set, then a profile with this role will be generated for every account in the organization, in addition to the roles that the user has permissions to assume
92+
--ordered=true Saves the profiles according to alphabetical order, stage, and uniqueness
9293
```
9394

9495
Note: When using the `--role` flag we do not check to see if the user has permission to assume that role. This is useful
@@ -125,6 +126,8 @@ REQUIRED
125126
OPTIONAL
126127
127128
--color="00ff7f" The hexcode color that should be set for each profile
129+
--dev-color="00d619" The hexcode color that should be set for each profile which name ends in 'dev' or 'poc'
130+
--int-color="ffea00" The hexcode color that should be set for each profile which name ends in 'int' or 'stg'
128131
--prd-color="ff0000" The hexcode color that should be set for each profile which name ends in 'prd' or 'global'
129132
--use-role-name-in-profile=false Append the role name to the profile name
130133
```

Diff for: go.mod

+1
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ require (
66
github.com/alecthomas/kong v0.6.1
77
github.com/aws/aws-sdk-go v1.44.52
88
github.com/rs/zerolog v1.27.0
9+
golang.org/x/exp v0.0.0-20220706164943-b4a6d9510983
910
gopkg.in/ini.v1 v1.66.6
1011
)
1112

Diff for: go.sum

+2
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,8 @@ github.com/rs/zerolog v1.27.0/go.mod h1:7frBqO0oezxmnO7GF86FY++uy8I0Tk/If5ni1G9Q
2626
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
2727
github.com/stretchr/testify v1.7.2 h1:4jaiDzPyXQvSd7D0EjG45355tLlV3VOECpq10pLC+8s=
2828
github.com/stretchr/testify v1.7.2/go.mod h1:R6va5+xMeoiuVRoj+gSkQ7d3FALtqAAGI1FQKckRals=
29+
golang.org/x/exp v0.0.0-20220706164943-b4a6d9510983 h1:sUweFwmLOje8KNfXAVqGGAsmgJ/F8jJ6wBLJDt4BTKY=
30+
golang.org/x/exp v0.0.0-20220706164943-b4a6d9510983/go.mod h1:Kr81I6Kryrl9sr8s2FK3vxD90NdsKWRuOIl2O4CvYbA=
2931
golang.org/x/net v0.0.0-20220127200216-cd36cc0744dd/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk=
3032
golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
3133
golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=

Diff for: pkg/cmd/cli.go

+1
Original file line numberDiff line numberDiff line change
@@ -19,4 +19,5 @@ type CLI struct {
1919
SwitchRoles SwitchRolesCmd `cmd help:"generates a config for aws-extend-switch-roles"`
2020
Debug bool `help:"set the log level to debug" default:"false"`
2121
Role string `help:"If set, then a profile with this role will be generated for every account in the organization, in addition to the roles that the user has permissions to assume"`
22+
Ordered bool `help:"disable ordering based on alphabet, stage and uniqueness" default:"true"`
2223
}

Diff for: pkg/cmd/cmd_test.go

+17-12
Original file line numberDiff line numberDiff line change
@@ -66,7 +66,7 @@ include_profile = default
6666
KeepCustomConfig: false,
6767
UseRoleNameInProfile: false,
6868
Region: "",
69-
})
69+
}, true)
7070
},
7171
},
7272
{
@@ -87,7 +87,7 @@ include_profile = default
8787
KeepCustomConfig: false,
8888
UseRoleNameInProfile: false,
8989
Region: "",
90-
})
90+
}, true)
9191
},
9292
},
9393
{
@@ -108,7 +108,7 @@ include_profile = default
108108
KeepCustomConfig: false,
109109
UseRoleNameInProfile: false,
110110
Region: "",
111-
})
111+
}, true)
112112
},
113113
},
114114
{
@@ -129,7 +129,7 @@ include_profile = my-profile
129129
KeepCustomConfig: false,
130130
UseRoleNameInProfile: false,
131131
Region: "",
132-
})
132+
}, true)
133133
},
134134
},
135135
{
@@ -150,7 +150,7 @@ include_profile = default
150150
KeepCustomConfig: false,
151151
UseRoleNameInProfile: true,
152152
Region: "",
153-
})
153+
}, true)
154154
},
155155
},
156156
{
@@ -185,7 +185,7 @@ include_profile = default
185185
KeepCustomConfig: true,
186186
UseRoleNameInProfile: true,
187187
Region: "",
188-
})
188+
}, true)
189189
},
190190
},
191191
{
@@ -217,7 +217,7 @@ include_profile = default
217217
KeepCustomConfig: false,
218218
UseRoleNameInProfile: true,
219219
Region: "",
220-
})
220+
}, true)
221221
},
222222
},
223223
{
@@ -239,7 +239,7 @@ region = eu-central-1
239239
KeepCustomConfig: false,
240240
UseRoleNameInProfile: true,
241241
Region: "eu-central-1",
242-
})
242+
}, true)
243243
},
244244
},
245245
{
@@ -256,7 +256,7 @@ color = ffffff
256256
OutputFile: filename,
257257
UseRoleNameInProfile: false,
258258
Color: "ffffff",
259-
})
259+
}, true)
260260
},
261261
},
262262
{
@@ -273,7 +273,7 @@ color = ffffff
273273
OutputFile: filename,
274274
UseRoleNameInProfile: true,
275275
Color: "ffffff",
276-
})
276+
}, true)
277277
},
278278
},
279279
{
@@ -290,7 +290,7 @@ color = ffffff
290290
OutputFile: filename,
291291
UseRoleNameInProfile: false,
292292
Color: "ffffff",
293-
})
293+
}, true)
294294
},
295295
},
296296
}
@@ -299,7 +299,12 @@ color = ffffff
299299
t.Run(fmt.Sprintf("%s %s", testCase.describe, testCase.it), func(t *testing.T) {
300300

301301
filename := setup(testCase.originalConfig)
302-
defer os.Remove(filename)
302+
defer func() {
303+
err := os.Remove(filename)
304+
if err != nil {
305+
log.Panic(err)
306+
}
307+
}()
303308

304309
testCase.run(filename)
305310

Diff for: pkg/cmd/switch_roles.go

+19-3
Original file line numberDiff line numberDiff line change
@@ -24,32 +24,48 @@ import (
2424
// nolint:govet // we need the bare `required` tag here
2525
type SwitchRolesCmd struct {
2626
Color string `help:"The hexcode color that should be set for each profile which name doesn't end in 'prd' or 'global'" default:"00ff7f"`
27+
DevColor string `help:"The hexcode color that should be set for each profile which name ends in 'dev' or 'poc'" default:"00d619"`
28+
IntColor string `help:"The hexcode color that should be set for each profile which name ends in 'int' or 'stg'" default:"ffea00"`
2729
PrdColor string `help:"The hexcode color that should be set for each profile which name ends in 'prd' or 'global'" default:"ff0000"`
2830
OutputFile string `help:"Where to save the config." required`
2931
UseRoleNameInProfile bool `help:"Append the role name to the profile name" default:false`
3032
}
3133

3234
func (swc *SwitchRolesCmd) Run(cli *CLI) error {
3335
roleArns, accountMap := util.GetAWSContext().GetRolesAndAccounts(cli.Role)
34-
generateSwitchRolesProfile(accountMap, roleArns, cli.SwitchRoles)
36+
generateSwitchRolesProfile(accountMap, roleArns, cli.SwitchRoles, cli.Ordered)
3537

3638
return nil
3739
}
3840

3941
func envSpecificColor(profileName string, cmdOptions SwitchRolesCmd) string {
4042
lowerKeyProfileName := strings.ToLower(profileName)
4143

44+
if strings.HasSuffix(lowerKeyProfileName, "dev") || strings.HasSuffix(lowerKeyProfileName, "poc") {
45+
return cmdOptions.DevColor
46+
}
47+
48+
if strings.HasSuffix(lowerKeyProfileName, "int") || strings.HasSuffix(lowerKeyProfileName, "stg") {
49+
return cmdOptions.IntColor
50+
}
51+
4252
if strings.HasSuffix(lowerKeyProfileName, "prd") || strings.HasSuffix(lowerKeyProfileName, "global") {
4353
return cmdOptions.PrdColor
4454
}
4555

4656
return cmdOptions.Color
4757
}
4858

49-
func generateSwitchRolesProfile(accountMap map[string]string, roleArns []string, cmdOptions SwitchRolesCmd) {
59+
func generateSwitchRolesProfile(accountMap map[string]string, roleArns []string, cmdOptions SwitchRolesCmd, ordered bool) {
5060
config := ini.Empty()
5161

52-
for _, profile := range util.GetProfiles("", accountMap, roleArns, cmdOptions.UseRoleNameInProfile) {
62+
profiles := util.GetProfiles("", accountMap, roleArns, cmdOptions.UseRoleNameInProfile)
63+
64+
if ordered {
65+
profiles = util.OrderProfiles(profiles)
66+
}
67+
68+
for _, profile := range profiles {
5369
profileSection := config.Section(profile.ProfileName)
5470

5571
setKey := util.GetKeySetter(profileSection)

Diff for: pkg/cmd/vault.go

+8-3
Original file line numberDiff line numberDiff line change
@@ -32,12 +32,12 @@ type VaultCmd struct {
3232

3333
func (vc *VaultCmd) Run(cli *CLI) error {
3434
roleArns, accountMap := util.GetAWSContext().GetRolesAndAccounts(cli.Role)
35-
generateVaultProfile(accountMap, roleArns, cli.Vault)
35+
generateVaultProfile(accountMap, roleArns, cli.Vault, cli.Ordered)
3636

3737
return nil
3838
}
3939

40-
func generateVaultProfile(accountMap map[string]string, roleArns []string, cmdOptions VaultCmd) {
40+
func generateVaultProfile(accountMap map[string]string, roleArns []string, cmdOptions VaultCmd, ordered bool) {
4141
config, err := ini.Load(cmdOptions.VaultConfigPath)
4242
if err != nil {
4343
log.Panic().Err(err).Str("file-path", cmdOptions.VaultConfigPath).Msg("could not load config")
@@ -68,8 +68,13 @@ func generateVaultProfile(accountMap map[string]string, roleArns []string, cmdOp
6868

6969
config = newConfig
7070
}
71+
profiles := util.GetProfiles("profile ", accountMap, roleArns, cmdOptions.UseRoleNameInProfile)
7172

72-
for _, profile := range util.GetProfiles("profile ", accountMap, roleArns, cmdOptions.UseRoleNameInProfile) {
73+
if ordered {
74+
profiles = util.OrderProfiles(profiles)
75+
}
76+
77+
for _, profile := range profiles {
7378
profileSection := config.Section(profile.ProfileName)
7479

7580
setKey := util.GetKeySetter(profileSection)

Diff for: pkg/util/order.go

+52
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
package util
2+
3+
/*
4+
Copyright 2021 MOIA GmbH
5+
Licensed under the Apache License, Version 2.0 (the "License");
6+
you may not use this file except in compliance with the License.
7+
You may obtain a copy of the License at
8+
http://www.apache.org/licenses/LICENSE-2.0
9+
Unless required by applicable law or agreed to in writing, software
10+
distributed under the License is distributed on an "AS IS" BASIS,
11+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
See the License for the specific language governing permissions and
13+
limitations under the License.
14+
*/
15+
16+
import (
17+
"golang.org/x/exp/slices"
18+
"strings"
19+
)
20+
21+
var stages = []string{"poc", "stg", "dev", "int", "prd"}
22+
23+
func profileLess(x, y Profile) bool {
24+
return x.ProfileName < y.ProfileName
25+
}
26+
27+
func OrderProfiles(unorderedProfiles []Profile) []Profile {
28+
var singleProfiles, stagedProfiles []Profile
29+
30+
for _, profile := range unorderedProfiles {
31+
if isStageProfile(profile) {
32+
stagedProfiles = append(stagedProfiles, profile)
33+
} else {
34+
singleProfiles = append(singleProfiles, profile)
35+
}
36+
37+
}
38+
39+
slices.SortFunc(singleProfiles, profileLess)
40+
slices.SortFunc(stagedProfiles, profileLess)
41+
42+
return append(singleProfiles, stagedProfiles...)
43+
}
44+
45+
func isStageProfile(profile Profile) bool {
46+
for _, stage := range stages {
47+
if strings.HasSuffix(profile.ProfileName, stage) {
48+
return true
49+
}
50+
}
51+
return false
52+
}

Diff for: pkg/util/order_test.go

+100
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,100 @@
1+
package util
2+
3+
import (
4+
"reflect"
5+
"testing"
6+
)
7+
8+
func TestOrderProfiles(t *testing.T) {
9+
10+
tests := []struct {
11+
name string
12+
unorderedProfiles []Profile
13+
want []Profile
14+
}{
15+
{name: "mixed profile list",
16+
unorderedProfiles: []Profile{
17+
{ProfileName: "bar.int"},
18+
{ProfileName: "tools"},
19+
{ProfileName: "foo.dev"},
20+
{ProfileName: "cookies"},
21+
{ProfileName: "bar.prd"},
22+
{ProfileName: "gears"},
23+
},
24+
want: []Profile{
25+
{ProfileName: "cookies"},
26+
{ProfileName: "gears"},
27+
{ProfileName: "tools"},
28+
{ProfileName: "bar.int"},
29+
{ProfileName: "bar.prd"},
30+
{ProfileName: "foo.dev"},
31+
}},
32+
{name: "only single profiles",
33+
unorderedProfiles: []Profile{
34+
{ProfileName: "bar"},
35+
{ProfileName: "tools"},
36+
{ProfileName: "foo"},
37+
{ProfileName: "cookies"},
38+
{ProfileName: "bikes"},
39+
{ProfileName: "gears"},
40+
},
41+
want: []Profile{
42+
{ProfileName: "bar"},
43+
{ProfileName: "bikes"},
44+
{ProfileName: "cookies"},
45+
{ProfileName: "foo"},
46+
{ProfileName: "gears"},
47+
{ProfileName: "tools"},
48+
}},
49+
{name: "only staged profiles",
50+
unorderedProfiles: []Profile{
51+
{ProfileName: "bar.int"},
52+
{ProfileName: "tools.prd"},
53+
{ProfileName: "foo.dev"},
54+
{ProfileName: "cookies.dev"},
55+
{ProfileName: "bar.prd"},
56+
{ProfileName: "gears.poc"},
57+
},
58+
want: []Profile{
59+
{ProfileName: "bar.int"},
60+
{ProfileName: "bar.prd"},
61+
{ProfileName: "cookies.dev"},
62+
{ProfileName: "foo.dev"},
63+
{ProfileName: "gears.poc"},
64+
{ProfileName: "tools.prd"},
65+
}},
66+
}
67+
for _, tt := range tests {
68+
t.Run(tt.name, func(t *testing.T) {
69+
if got := OrderProfiles(tt.unorderedProfiles); !reflect.DeepEqual(got, tt.want) {
70+
t.Errorf("OrderProfiles() = %v, want %v", got, tt.want)
71+
}
72+
})
73+
}
74+
}
75+
76+
func Test_isStageProfile(t *testing.T) {
77+
78+
tests := []struct {
79+
name string
80+
profile Profile
81+
want bool
82+
}{
83+
{name: "unique account",
84+
profile: Profile{ProfileName: "cookies"},
85+
want: false},
86+
{name: "dev account",
87+
profile: Profile{ProfileName: "cookies.dev"},
88+
want: true},
89+
{name: "global account",
90+
profile: Profile{ProfileName: "tools.global"},
91+
want: false},
92+
}
93+
for _, tt := range tests {
94+
t.Run(tt.name, func(t *testing.T) {
95+
if got := isStageProfile(tt.profile); got != tt.want {
96+
t.Errorf("isStageProfile() = %v, want %v", got, tt.want)
97+
}
98+
})
99+
}
100+
}

Diff for: vendor/golang.org/x/exp/AUTHORS

+3
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Diff for: vendor/golang.org/x/exp/CONTRIBUTORS

+3
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)