Skip to content

Commit

Permalink
Merge pull request #2 from st1t/rds
Browse files Browse the repository at this point in the history
Add rds and sg subcommand
  • Loading branch information
st1t committed Feb 12, 2022
2 parents c762ff5 + eb35ca4 commit ff45e16
Show file tree
Hide file tree
Showing 10 changed files with 912 additions and 7 deletions.
62 changes: 61 additions & 1 deletion README.md
Expand Up @@ -4,6 +4,11 @@ The vaws command was created to simplify the display of AWS resources.
This repository is a Go version of the command that was created in the following repository.
https://github.com/st1t/vaws

## Install

Download the appropriate one for your CPU architecture from the following site.
https://github.com/st1t/vaws/releases

## Usage

```bash
Expand All @@ -17,18 +22,73 @@ Available Commands:
completion Generate the autocompletion script for the specified shell
ec2 Show EC2 instances.
help Help about any command
rds Show RDS instances.
sg Show Security Group

Flags:
-p, --aws-profile string -p my-aws
-h, --help help for vaws
-s, --sort-position int -s 1 (default 1)
-t, --toggle Help message for toggle
-v, --version version for vaws

Use "vaws [command] --help" for more information about a command.
$
```

### EC2

```shell
$ vaws ec2 -p my-aws
+-------+---------------------+----------+---------------+---------------+---------+---------------------------------------+
| NAME | ID | TYPE | PRIVATE IP | PUBLIC IP | STATE | SECURITY GROUP |
+-------+---------------------+----------+---------------+---------------+---------+---------------------------------------+
| app01 | i-06d4c29e4e5ccadc4 | t2.micro | 172.31.35.175 | 54.238.30.226 | running | launch-wizard-2(sg-0f0b4c4642ffb5ef2) |
| app02 | i-06723a6629e542c50 | t3.small | 172.31.21.33 | 18.179.33.182 | running | launch-wizard-3(sg-08d35fef29987e75e) |
| web01 | i-0abee92626b0a28a7 | t3.nano | 172.31.18.8 | 35.73.127.100 | running | launch-wizard-1(sg-0d642190887707fd0) |
+-------+---------------------+----------+---------------+---------------+---------+---------------------------------------+
```

If you want to sort by a specific column, use the S option.
The following command sorts by SecurityGroup column.
```shell
$ vaws ec2 -p my-aws -s 7
+-------+---------------------+----------+---------------+---------------+---------+---------------------------------------+
| NAME | ID | TYPE | PRIVATE IP | PUBLIC IP | STATE | SECURITY GROUP |
+-------+---------------------+----------+---------------+---------------+---------+---------------------------------------+
| web01 | i-0abee92626b0a28a7 | t3.nano | 172.31.18.8 | 35.73.127.100 | running | launch-wizard-1(sg-0d642190887707fd0) |
| app01 | i-06d4c29e4e5ccadc4 | t2.micro | 172.31.35.175 | 54.238.30.226 | running | launch-wizard-2(sg-0f0b4c4642ffb5ef2) |
| app02 | i-06723a6629e542c50 | t3.small | 172.31.21.33 | 18.179.33.182 | running | launch-wizard-3(sg-08d35fef29987e75e) |
+-------+---------------------+----------+---------------+---------------+---------+---------------------------------------+
```

## RDS

```shell
$ vaws rds -p my-aws
+-----------------+-----------+--------------------------+-----------------------------------------------------------------------+--------------------------------------------------------------------------+
| CLUSTER | STATUS | INSTANCES | WRITE-ENDPOINT | READ-ENDPOINT |
+-----------------+-----------+--------------------------+-----------------------------------------------------------------------+--------------------------------------------------------------------------+
| test-cluster-01 | available | test-cluster-instance-01 | test-cluster-01.cluster-cb8aaaaaaaaa.ap-northeast-1.rds.amazonaws.com | test-cluster-01.cluster-ro-cb8aaaaaaaaa.ap-northeast-1.rds.amazonaws.com |
| test-cluster-01 | available | test-cluster-instance-02 | test-cluster-01.cluster-cb8aaaaaaaaa.ap-northeast-1.rds.amazonaws.com | test-cluster-01.cluster-ro-cb8aaaaaaaaa.ap-northeast-1.rds.amazonaws.com |
+-----------------+-----------+--------------------------+-----------------------------------------------------------------------+--------------------------------------------------------------------------+
```

## SecurityGroup

```shell
$ vaws sg -p my-aws
+-----------------+---------+----------------------+------+----------------------+-----------------------+
| NAME | TYPE | ID | PORT | SOURCE | VPC |
+-----------------+---------+----------------------+------+----------------------+-----------------------+
| default | inbound | sg-0d642190887707fd0 | -1 | 0.0.0.0/0 | vpc-0f9999c7db8c44b21 |
| launch-wizard-1 | inbound | sg-0d642190887707fd0 | 22 | 0.0.0.0/0 | vpc-0f9999c7db8c44b21 |
| launch-wizard-2 | inbound | sg-08d35fef29987e75e | 22 | 8.8.8.8/32 | vpc-0f9999c7db8c44b21 |
| launch-wizard-2 | inbound | sg-08d35fef29987e75e | 22 | 0.0.0.0/0 | vpc-0f9999c7db8c44b21 |
| launch-wizard-2 | inbound | sg-08d35fef29987e75e | 22 | sg-0d642190887707fd0 | vpc-0f9999c7db8c44b21 |
| launch-wizard-2 | inbound | sg-08d35fef29987e75e | 53 | pl-61a12345 | vpc-0f9999c7db8c44b21 |
+-----------------+---------+----------------------+------+----------------------+-----------------------+
```

## License

The gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT).
4 changes: 2 additions & 2 deletions cmd/vaws/ec2.go
Expand Up @@ -42,7 +42,7 @@ var ec2Cmd = &cobra.Command{
fmt.Println(err)
os.Exit(1)
}
err = printEc2Instances(outputs, tablewriter.NewWriter(os.Stdout), sortPosition)
err = showEc2Instances(outputs, tablewriter.NewWriter(os.Stdout), sortPosition)
if err != nil {
fmt.Println(err)
os.Exit(1)
Expand Down Expand Up @@ -74,7 +74,7 @@ func getEc2Instances(cfg aws.Config) ([]*ec2.DescribeInstancesOutput, error) {
return outputs, nil
}

func printEc2Instances(outputs []*ec2.DescribeInstancesOutput, table *tablewriter.Table, sortPosition int) error {
func showEc2Instances(outputs []*ec2.DescribeInstancesOutput, table *tablewriter.Table, sortPosition int) error {
header := []string{"NAME", "ID", "TYPE", "PRIVATE_IP", "PUBLIC_IP", "STATE", "SECURITY_GROUP"}
if sortPosition > len(header) || 1 > sortPosition {
return fmt.Errorf("out of sort range number when using --sort option")
Expand Down
2 changes: 1 addition & 1 deletion cmd/vaws/ec2_test.go
Expand Up @@ -347,7 +347,7 @@ func Test_printEc2Instances(t *testing.T) {
for _, tt := range tests {
var buf bytes.Buffer
t.Run(tt.name, func(t *testing.T) {
printEc2Instances(tt.args.outputs, tablewriter.NewWriter(&buf), tt.args.sortPosition)
showEc2Instances(tt.args.outputs, tablewriter.NewWriter(&buf), tt.args.sortPosition)
if buf.String() != tt.want {
t.Errorf("failed to test: %s\n", tt.name)
t.Errorf("\nwant:\n%s\n", tt.want)
Expand Down
154 changes: 154 additions & 0 deletions cmd/vaws/rds.go
@@ -0,0 +1,154 @@
package vaws

import (
"context"
"fmt"
"github.com/aws/aws-sdk-go-v2/aws"
"github.com/aws/aws-sdk-go-v2/service/rds"
"github.com/olekukonko/tablewriter"
"os"
"sort"

"github.com/spf13/cobra"
)

// rdsCmd represents the rds command
var rdsCmd = &cobra.Command{
Use: "rds",
Short: "Show RDS instances.",
Long: `Show RDS instances.`,
Run: func(cmd *cobra.Command, args []string) {
profile, err := cmd.Flags().GetString("aws-profile")
if err != nil {
fmt.Println(err)
os.Exit(1)
}
if profile != "" {
err := os.Setenv("AWS_PROFILE", profile)
if err != nil {
fmt.Println(err)
os.Exit(1)
}
}
sortPosition, err := cmd.Flags().GetInt("sort-position")
if err != nil {
fmt.Println(err)
os.Exit(1)
}
instanceFlg, err := cmd.Flags().GetBool("instance")
if err != nil {
fmt.Println(err)
os.Exit(1)
}
if instanceFlg {
output, err := getRdsInstances(newAwsConfig())
if err != nil {
fmt.Println(err)
os.Exit(1)
}
err = showRdsInstances(output, tablewriter.NewWriter(os.Stdout), sortPosition)
if err != nil {
fmt.Println(err)
os.Exit(1)
}
} else {
output, err := getRdsClusters(newAwsConfig())
if err != nil {
fmt.Println(err)
os.Exit(1)
}
err = showRdsClusters(output, tablewriter.NewWriter(os.Stdout), sortPosition)
if err != nil {
fmt.Println(err)
os.Exit(1)
}

}
},
}

func init() {
rootCmd.AddCommand(rdsCmd)
rdsCmd.Flags().BoolP("instance", "i", false, "Show instances")
}

func getRdsClusters(cfg aws.Config) (*rds.DescribeDBClustersOutput, error) {
client := rds.NewFromConfig(cfg)
output, err := client.DescribeDBClusters(context.TODO(), &rds.DescribeDBClustersInput{
DBClusterIdentifier: nil,
Filters: nil,
IncludeShared: false,
Marker: nil,
MaxRecords: nil,
})
if err != nil {
return nil, err
}
return output, nil
}

func getRdsInstances(cfg aws.Config) (*rds.DescribeDBInstancesOutput, error) {
client := rds.NewFromConfig(cfg)
output, err := client.DescribeDBInstances(context.TODO(), &rds.DescribeDBInstancesInput{
DBInstanceIdentifier: nil,
Filters: nil,
Marker: nil,
MaxRecords: nil,
})
if err != nil {
return nil, err
}
return output, nil
}

func showRdsClusters(instances *rds.DescribeDBClustersOutput, table *tablewriter.Table, sortPosition int) error {
header := []string{"CLUSTER", "STATUS", "INSTANCES", "WRITE-ENDPOINT", "READ-ENDPOINT"}
if sortPosition > len(header) || 1 > sortPosition {
return fmt.Errorf("out of sort range number when using --sort option")
}
recordIndex := sortPosition - 1
table.SetHeader(header)
var records [][]string
for _, object := range instances.DBClusters {
cluster := *object.DBClusterIdentifier
status := *object.Status
wEndpoint := *object.Endpoint
rEndpoint := *object.ReaderEndpoint
instanceId := ""
for i, v := range object.DBClusterMembers {
if i == 0 {
instanceId += *v.DBInstanceIdentifier
} else {
instanceId += fmt.Sprintf(", %s", *v.DBInstanceIdentifier)
}
}
records = append(records, []string{cluster, status, instanceId, wEndpoint, rEndpoint})
}
sort.Slice(records, func(i, j int) bool { return records[i][recordIndex] < records[j][recordIndex] })
table.AppendBulk(records)
table.Render()
return nil
}

func showRdsInstances(instances *rds.DescribeDBInstancesOutput, table *tablewriter.Table, sortPosition int) error {
header := []string{"CLUSTER", "INSTANCE", "TYPE", "ENGINE", "STATUS", "ENDPOINT(INSTANCE)"}
if sortPosition > len(header) || 1 > sortPosition {
return fmt.Errorf("out of sort range number when using --sort option")
}
recordIndex := sortPosition - 1
table.SetHeader(header)
var records [][]string
for _, object := range instances.DBInstances {
cluster := *object.DBClusterIdentifier
instanceName := *object.DBInstanceIdentifier
class := *object.DBInstanceClass
engine := *object.EngineVersion
status := *object.DBInstanceStatus
endpoint := *object.Endpoint.Address
records = append(records, []string{cluster, instanceName, class, engine, status, endpoint})
}
sort.Slice(records, func(i, j int) bool { return records[i][recordIndex] < records[j][recordIndex] })
table.AppendBulk(records)
table.Render()
return nil
}
113 changes: 113 additions & 0 deletions cmd/vaws/rds_test.go
@@ -0,0 +1,113 @@
package vaws

import (
"bytes"
"github.com/aws/aws-sdk-go-v2/aws"
"github.com/aws/aws-sdk-go-v2/service/rds"
"github.com/aws/aws-sdk-go-v2/service/rds/types"
"github.com/olekukonko/tablewriter"
"testing"
)

func Test_showRdsClusters(t *testing.T) {
type args struct {
instances *rds.DescribeDBClustersOutput
table *tablewriter.Table
sortPosition int
}
tests := []struct {
name string
args args
want string
}{
{
name: "default",
args: args{
instances: &rds.DescribeDBClustersOutput{
DBClusters: []types.DBCluster{
{
DBClusterIdentifier: aws.String("test-cluster-01"),
DBClusterMembers: []types.DBClusterMember{
{
DBInstanceIdentifier: aws.String("test-cluster-instance-01"),
},
},
Endpoint: aws.String("test-cluster-01.cluster-cb8aaaaaaaaa.ap-northeast-1.rds.amazonaws.com"),
ReaderEndpoint: aws.String("test-cluster-01.cluster-ro-cb8aaaaaaaaa.ap-northeast-1.rds.amazonaws.com"),
Status: aws.String("available"),
},
{
DBClusterIdentifier: aws.String("test-cluster-01"),
DBClusterMembers: []types.DBClusterMember{
{
DBInstanceIdentifier: aws.String("test-cluster-instance-02"),
},
},
Endpoint: aws.String("test-cluster-01.cluster-cb8aaaaaaaaa.ap-northeast-1.rds.amazonaws.com"),
ReaderEndpoint: aws.String("test-cluster-01.cluster-ro-cb8aaaaaaaaa.ap-northeast-1.rds.amazonaws.com"),
Status: aws.String("available"),
},
},
},
sortPosition: 1,
},
want: `+-----------------+-----------+--------------------------+-----------------------------------------------------------------------+--------------------------------------------------------------------------+
| CLUSTER | STATUS | INSTANCES | WRITE-ENDPOINT | READ-ENDPOINT |
+-----------------+-----------+--------------------------+-----------------------------------------------------------------------+--------------------------------------------------------------------------+
| test-cluster-01 | available | test-cluster-instance-01 | test-cluster-01.cluster-cb8aaaaaaaaa.ap-northeast-1.rds.amazonaws.com | test-cluster-01.cluster-ro-cb8aaaaaaaaa.ap-northeast-1.rds.amazonaws.com |
| test-cluster-01 | available | test-cluster-instance-02 | test-cluster-01.cluster-cb8aaaaaaaaa.ap-northeast-1.rds.amazonaws.com | test-cluster-01.cluster-ro-cb8aaaaaaaaa.ap-northeast-1.rds.amazonaws.com |
+-----------------+-----------+--------------------------+-----------------------------------------------------------------------+--------------------------------------------------------------------------+
`,
},
{
name: "sort request",
args: args{
instances: &rds.DescribeDBClustersOutput{
DBClusters: []types.DBCluster{
{
DBClusterIdentifier: aws.String("test-cluster-01"),
DBClusterMembers: []types.DBClusterMember{
{
DBInstanceIdentifier: aws.String("test-cluster-instance-02"),
},
},
Endpoint: aws.String("test-cluster-01.cluster-cb8aaaaaaaaa.ap-northeast-1.rds.amazonaws.com"),
ReaderEndpoint: aws.String("test-cluster-01.cluster-ro-cb8aaaaaaaaa.ap-northeast-1.rds.amazonaws.com"),
Status: aws.String("available"),
},
{
DBClusterIdentifier: aws.String("test-cluster-01"),
DBClusterMembers: []types.DBClusterMember{
{
DBInstanceIdentifier: aws.String("test-cluster-instance-01"),
},
},
Endpoint: aws.String("test-cluster-01.cluster-cb8aaaaaaaaa.ap-northeast-1.rds.amazonaws.com"),
ReaderEndpoint: aws.String("test-cluster-01.cluster-ro-cb8aaaaaaaaa.ap-northeast-1.rds.amazonaws.com"),
Status: aws.String("available"),
},
},
},
sortPosition: 3,
},
want: `+-----------------+-----------+--------------------------+-----------------------------------------------------------------------+--------------------------------------------------------------------------+
| CLUSTER | STATUS | INSTANCES | WRITE-ENDPOINT | READ-ENDPOINT |
+-----------------+-----------+--------------------------+-----------------------------------------------------------------------+--------------------------------------------------------------------------+
| test-cluster-01 | available | test-cluster-instance-01 | test-cluster-01.cluster-cb8aaaaaaaaa.ap-northeast-1.rds.amazonaws.com | test-cluster-01.cluster-ro-cb8aaaaaaaaa.ap-northeast-1.rds.amazonaws.com |
| test-cluster-01 | available | test-cluster-instance-02 | test-cluster-01.cluster-cb8aaaaaaaaa.ap-northeast-1.rds.amazonaws.com | test-cluster-01.cluster-ro-cb8aaaaaaaaa.ap-northeast-1.rds.amazonaws.com |
+-----------------+-----------+--------------------------+-----------------------------------------------------------------------+--------------------------------------------------------------------------+
`,
},
}
for _, tt := range tests {
var buf bytes.Buffer
t.Run(tt.name, func(t *testing.T) {
showRdsClusters(tt.args.instances, tablewriter.NewWriter(&buf), tt.args.sortPosition)
if buf.String() != tt.want {
t.Errorf("failed to test: %s\n", tt.name)
t.Errorf("\nwant:\n%s\n", tt.want)
t.Errorf("\ninput:\n%s\n", buf.String())
}
})
}
}
3 changes: 1 addition & 2 deletions cmd/vaws/root.go
Expand Up @@ -11,7 +11,7 @@ var rootCmd = &cobra.Command{
Use: "vaws",
Short: "The vaws command was created to simplify the display of AWS resources.",
Long: `The vaws command was created to simplify the display of AWS resources.`,
Version: "0.1.0",
Version: "0.2.0",
}

func Execute() {
Expand All @@ -22,7 +22,6 @@ func Execute() {
}

func init() {
rootCmd.Flags().BoolP("toggle", "t", false, "Help message for toggle")
rootCmd.PersistentFlags().StringP("aws-profile", "p", "", "-p my-aws")
rootCmd.PersistentFlags().IntP("sort-position", "s", 1, "-s 1")
}

0 comments on commit ff45e16

Please sign in to comment.