Skip to content

Commit

Permalink
feat: Add "Get" command to display mappings as a table (#46)
Browse files Browse the repository at this point in the history
* fix kubeconfig loading ordering

Signed-off-by: Eytan Avisror <eytan_avisror@intuit.com>

* Improvements and fixes

Signed-off-by: Eytan Avisror <eytan_avisror@intuit.com>

* Add unit test

Signed-off-by: Eytan Avisror <eytan_avisror@intuit.com>

* Add unit-tests for retryer

Signed-off-by: Eytan Avisror <eytan_avisror@intuit.com>
  • Loading branch information
Eytan Avisror authored Jun 28, 2022
1 parent caf3194 commit 80ca7b8
Show file tree
Hide file tree
Showing 13 changed files with 317 additions and 34 deletions.
17 changes: 13 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -106,14 +106,14 @@ $ aws-auth remove-by-username --username ops-user
Bootstrap a new node group role

```text
$ aws-auth upsert --maproles --userarn arn:aws:iam::555555555555:role/my-new-node-group-NodeInstanceRole-74RF4UBDUKL6 --username system:node:{{EC2PrivateDNSName}} --groups system:bootstrappers system:nodes
$ aws-auth upsert --maproles --rolearn arn:aws:iam::555555555555:role/my-new-node-group-NodeInstanceRole-74RF4UBDUKL6 --username system:node:{{EC2PrivateDNSName}} --groups system:bootstrappers system:nodes
added arn:aws:iam::555555555555:role/my-new-node-group-NodeInstanceRole-74RF4UBDUKL6 to aws-auth
```

You can also add retries with exponential backoff

```text
$ aws-auth upsert --maproles --userarn arn:aws:iam::555555555555:role/my-new-node-group-NodeInstanceRole-74RF4UBDUKL6 --username system:node:{{EC2PrivateDNSName}} --groups system:bootstrappers system:nodes --retry
$ aws-auth upsert --maproles --rolearn arn:aws:iam::555555555555:role/my-new-node-group-NodeInstanceRole-74RF4UBDUKL6 --username system:node:{{EC2PrivateDNSName}} --groups system:bootstrappers system:nodes --retry
```

Retries are configurable using the following flags
Expand All @@ -128,13 +128,22 @@ Retries are configurable using the following flags
Append groups to mapping instead of overwriting by using --append

```
aws-auth upsert --maproles --rolearn arn:aws:iam::00000000000:role/test --username test --groups test --append
$ aws-auth upsert --maproles --rolearn arn:aws:iam::00000000000:role/test --username test --groups test --append
```

Avoid overwriting username by using --update-username=false

```
aws-auth upsert --maproles --rolearn arn:aws:iam::00000000000:role/test --username test2 --groups test --update-username=false
$ aws-auth upsert --maproles --rolearn arn:aws:iam::00000000000:role/test --username test2 --groups test --update-username=false
```

Use the `get` command to get a detailed view of mappings

```
$ aws-auth get
TYPE ARN USERNAME GROUPS
Role Mapping arn:aws:iam::555555555555:role/my-new-node-group system:node:{{EC2PrivateDNSName}} system:bootstrappers, system:nodes
```

## Usage as a library
Expand Down
83 changes: 83 additions & 0 deletions cmd/cli/get.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
/*
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

package cli

import (
"log"
"os"
"strings"

"github.com/keikoproj/aws-auth/pkg/mapper"
"github.com/olekukonko/tablewriter"
"github.com/spf13/cobra"
)

var getArgs = &mapper.MapperArguments{
OperationType: mapper.OperationGet,
IsGlobal: true,
}

// getCmd represents the base view command when run without subcommands
var getCmd = &cobra.Command{
Use: "get",
Short: "get provides a detailed summary of the configmap",
Long: `get allows a user to output the aws-auth configmap entires in various formats`,
Run: func(cmd *cobra.Command, args []string) {
k, err := getKubernetesClient(getArgs.KubeconfigPath)
if err != nil {
log.Fatal(err)
}

worker := mapper.New(k, true)

d, err := worker.Get(getArgs)
if err != nil {
log.Fatal(err)
}

table := tablewriter.NewWriter(os.Stdout)
table.SetHeader([]string{"Type", "ARN", "Username", "Groups"})
table.SetAutoWrapText(false)
table.SetAutoFormatHeaders(true)
table.SetHeaderAlignment(tablewriter.ALIGN_LEFT)
table.SetAlignment(tablewriter.ALIGN_LEFT)
table.SetCenterSeparator("")
table.SetColumnSeparator("")
table.SetRowSeparator("")
table.SetHeaderLine(false)
table.SetBorder(false)
table.SetTablePadding("\t")
table.SetNoWhiteSpace(true)
data := make([][]string, 0)

for _, row := range d.MapRoles {
data = append(data, []string{"Role Mapping", row.RoleARN, row.Username, strings.Join(row.Groups, ", ")})
}

for _, row := range d.MapUsers {
data = append(data, []string{"User Mapping", row.UserARN, row.Username, strings.Join(row.Groups, ", ")})
}

table.AppendBulk(data)
table.Render()
},
}

func init() {
rootCmd.AddCommand(getCmd)
getCmd.Flags().StringVar(&getArgs.KubeconfigPath, "kubeconfig", "", "Path to kubeconfig")
getCmd.Flags().StringVar(&getArgs.Format, "format", "table", "The format in which to display results (currently only 'table' supported)")
}
42 changes: 24 additions & 18 deletions cmd/cli/root.go
Original file line number Diff line number Diff line change
Expand Up @@ -41,32 +41,38 @@ func Execute() {
}
}

func getKubernetesClient(kubePath string) (kubernetes.Interface, error) {
func getKubernetesConfig() (*rest.Config, error) {
var config *rest.Config
config, err := rest.InClusterConfig()
if err != nil {
return getKubernetesLocalConfig()
}
return config, nil
}

if _, inCluster := os.LookupEnv("KUBERNETES_SERVICE_HOST"); inCluster == true {
config, err := rest.InClusterConfig()
func getKubernetesLocalConfig() (*rest.Config, error) {
loadingRules := clientcmd.NewDefaultClientConfigLoadingRules()
clientCfg := clientcmd.NewNonInteractiveDeferredLoadingClientConfig(loadingRules, &clientcmd.ConfigOverrides{})
return clientCfg.ClientConfig()
}

func getKubernetesClient(kubePath string) (kubernetes.Interface, error) {
var (
config *rest.Config
err error
)

// if kubeconfig path is not provided, try to auto detect
if kubePath == "" {
config, err = getKubernetesConfig()
if err != nil {
return nil, err
}
client, err := kubernetes.NewForConfig(config)
} else {
config, err = clientcmd.BuildConfigFromFlags("", kubePath)
if err != nil {
return nil, err
}
return client, nil
}

if kubePath == "" {
userHome, _ := os.UserHomeDir()
kubePath = fmt.Sprintf("%v/.kube/config", userHome)
if os.Getenv("KUBECONFIG") != "" {
kubePath = os.Getenv("KUBECONFIG")
}
}

config, err := clientcmd.BuildConfigFromFlags("", kubePath)
if err != nil {
return nil, err
}

client, err := kubernetes.NewForConfig(config)
Expand Down
3 changes: 2 additions & 1 deletion cmd/cli/upsert.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,8 @@ import (
)

var upsertArgs = &mapper.MapperArguments{
OperationType: mapper.OperationUpsert,
OperationType: mapper.OperationUpsert,
UpdateUsername: &mapper.UpdateUsernameDefaultValue,
}

// upsertCmd represents the base command when called without any subcommands
Expand Down
2 changes: 2 additions & 0 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ go 1.17

require (
github.com/jpillora/backoff v1.0.0
github.com/olekukonko/tablewriter v0.0.5
github.com/onsi/gomega v1.7.0
github.com/pkg/errors v0.9.1
github.com/spf13/cobra v0.0.5
Expand All @@ -25,6 +26,7 @@ require (
github.com/imdario/mergo v0.3.7 // indirect
github.com/inconshreveable/mousetrap v1.0.0 // indirect
github.com/json-iterator/go v1.1.10 // indirect
github.com/mattn/go-runewidth v0.0.9 // indirect
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
github.com/modern-go/reflect2 v1.0.1 // indirect
github.com/spf13/pflag v1.0.5 // indirect
Expand Down
4 changes: 4 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -151,6 +151,8 @@ github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
github.com/magiconair/properties v1.8.0/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ=
github.com/mailru/easyjson v0.0.0-20190614124828-94de47d64c63/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc=
github.com/mailru/easyjson v0.0.0-20190626092158-b2ccc519800e/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc=
github.com/mattn/go-runewidth v0.0.9 h1:Lm995f3rfxdpd6TSmuVCHVb/QhupuXlYr8sCI/QdE+0=
github.com/mattn/go-runewidth v0.0.9/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI=
github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0=
github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y=
github.com/moby/spdystream v0.2.0/go.mod h1:f7i0iNDQJ059oMTcWxx8MA/zKFIuD/lY+0GqbN2Wy8c=
Expand All @@ -164,6 +166,8 @@ github.com/munnerz/goautoneg v0.0.0-20120707110453-a547fc61f48d/go.mod h1:+n7T8m
github.com/mxk/go-flowrate v0.0.0-20140419014527-cca7078d478f/go.mod h1:ZdcZmHo+o7JKHSa8/e818NopupXU1YMK5fe1lsApnBw=
github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e h1:fD57ERR4JtEqsWbfPhv4DMiApHyliiK5xCTNVSPiaAs=
github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno=
github.com/olekukonko/tablewriter v0.0.5 h1:P2Ga83D34wi1o9J6Wh1mRuqd4mF/x/lgBS7N7AbDhec=
github.com/olekukonko/tablewriter v0.0.5/go.mod h1:hPp6KlRPjbx+hW8ykQs1w3UBbZlj6HuIJcUGPhkA7kY=
github.com/onsi/ginkgo v0.0.0-20170829012221-11459a886d9c/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
github.com/onsi/ginkgo v1.11.0 h1:JAKSXpt1YjtLA7YpPiqO9ss6sNXEsPfSGdwN0UHqzrw=
Expand Down
4 changes: 2 additions & 2 deletions pkg/mapper/configmaps.go
Original file line number Diff line number Diff line change
Expand Up @@ -62,8 +62,8 @@ func ReadAuthMap(k kubernetes.Interface) (AwsAuthData, *v1.ConfigMap, error) {
func CreateAuthMap(k kubernetes.Interface) (*v1.ConfigMap, error) {
configMapObject := &v1.ConfigMap{
ObjectMeta: metav1.ObjectMeta{
Name: "aws-auth",
Namespace: "kube-system",
Name: AwsAuthName,
Namespace: AwsAuthNamespace,
},
}
configMap, err := k.CoreV1().ConfigMaps(AwsAuthNamespace).Create(context.Background(), configMapObject, metav1.CreateOptions{})
Expand Down
15 changes: 15 additions & 0 deletions pkg/mapper/configmaps_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,21 @@ func create_MockConfigMap(client kubernetes.Interface) {
}
_, err := client.CoreV1().ConfigMaps(AwsAuthNamespace).Create(context.Background(), configMap, metav1.CreateOptions{})
gomega.Expect(err).NotTo(gomega.HaveOccurred())
}

func create_MockMalformedConfigMap(client kubernetes.Interface) {
configMap := &v1.ConfigMap{
ObjectMeta: metav1.ObjectMeta{
Name: AwsAuthName,
Namespace: AwsAuthNamespace,
},
Data: map[string]string{
"mapRoles": "''",
"mapUsers": "''",
},
}
_, err := client.CoreV1().ConfigMaps(AwsAuthNamespace).Create(context.Background(), configMap, metav1.CreateOptions{})
gomega.Expect(err).NotTo(gomega.HaveOccurred())

}

Expand Down
45 changes: 45 additions & 0 deletions pkg/mapper/get.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
/*
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

package mapper

// Upsert update or inserts by rolearn
func (b *AuthMapper) Get(args *MapperArguments) (AwsAuthData, error) {
args.IsGlobal = true
args.Validate()

if args.WithRetries {
out, err := WithRetry(func() (interface{}, error) {
return b.getAuth()
}, args)
if err != nil {
return AwsAuthData{}, err
}
return out.(AwsAuthData), nil
}

return b.getAuth()
}

func (b *AuthMapper) getAuth() (AwsAuthData, error) {

// Read the config map and return an AuthMap
authData, _, err := ReadAuthMap(b.KubernetesClient)
if err != nil {
return AwsAuthData{}, err
}

return authData, nil
}
Loading

0 comments on commit 80ca7b8

Please sign in to comment.