|
@ -3,12 +3,16 @@ |
|
|
// credentials must be stored in ~/.aws/credentials before usage.
|
|
|
// credentials must be stored in ~/.aws/credentials before usage.
|
|
|
// After running, press 'h' to view the help menu.
|
|
|
// After running, press 'h' to view the help menu.
|
|
|
//
|
|
|
//
|
|
|
|
|
|
// Dependencies:
|
|
|
|
|
|
// go mod init aws-helpers
|
|
|
|
|
|
// go get .
|
|
|
|
|
|
//
|
|
|
// Use:
|
|
|
// Use:
|
|
|
// go run instances.go --profile default --region us-east-1
|
|
|
// go run instances.go --profile default --region us-east-1
|
|
|
//
|
|
|
//
|
|
|
// Build and Use:
|
|
|
// Build:
|
|
|
// go build .
|
|
|
// go build instances.go
|
|
|
// ./instances --profile default --region us-east-1
|
|
|
// ./instances -p default -r us-east-1
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
package main |
|
|
package main |
|
@ -93,6 +97,7 @@ func initialDisplay(svc *ec2.EC2, list *tview.List, detailsTable *tview.Table) { |
|
|
fetchAndDisplayInstances("running", svc, list, detailsTable) |
|
|
fetchAndDisplayInstances("running", svc, list, detailsTable) |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
// Helper function to display the instance details.
|
|
|
func displayInstanceDetails(table *tview.Table, instance *ec2.Instance) { |
|
|
func displayInstanceDetails(table *tview.Table, instance *ec2.Instance) { |
|
|
table.Clear() |
|
|
table.Clear() |
|
|
|
|
|
|
|
@ -101,6 +106,7 @@ func displayInstanceDetails(table *tview.Table, instance *ec2.Instance) { |
|
|
"Field", "Value", |
|
|
"Field", "Value", |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
// Assign headers to table.
|
|
|
for col, header := range headers { |
|
|
for col, header := range headers { |
|
|
cell := tview.NewTableCell(header). |
|
|
cell := tview.NewTableCell(header). |
|
|
SetTextColor(tview.Styles.SecondaryTextColor). |
|
|
SetTextColor(tview.Styles.SecondaryTextColor). |
|
@ -141,6 +147,7 @@ func displayInstanceDetails(table *tview.Table, instance *ec2.Instance) { |
|
|
"Subnet ID": &instance.SubnetId, |
|
|
"Subnet ID": &instance.SubnetId, |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
// Add instance details to table.
|
|
|
row := 1 |
|
|
row := 1 |
|
|
for _, field := range fields { |
|
|
for _, field := range fields { |
|
|
ptr := fieldMap[field] |
|
|
ptr := fieldMap[field] |
|
@ -153,7 +160,7 @@ func displayInstanceDetails(table *tview.Table, instance *ec2.Instance) { |
|
|
row++ |
|
|
row++ |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
// Special cases
|
|
|
// Special cases that return non-string values.
|
|
|
table.SetCell(row, 0, tview.NewTableCell("Iam Instance Profile Arn")) |
|
|
table.SetCell(row, 0, tview.NewTableCell("Iam Instance Profile Arn")) |
|
|
if instance.IamInstanceProfile != nil && instance.IamInstanceProfile.Arn != nil { |
|
|
if instance.IamInstanceProfile != nil && instance.IamInstanceProfile.Arn != nil { |
|
|
table.SetCell(row, 1, tview.NewTableCell(*instance.IamInstanceProfile.Arn)) |
|
|
table.SetCell(row, 1, tview.NewTableCell(*instance.IamInstanceProfile.Arn)) |
|
@ -176,6 +183,7 @@ func displayInstanceDetails(table *tview.Table, instance *ec2.Instance) { |
|
|
} |
|
|
} |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
// Helper function to start an instance.
|
|
|
func startInstance(svc *ec2.EC2, instance *ec2.Instance) error { |
|
|
func startInstance(svc *ec2.EC2, instance *ec2.Instance) error { |
|
|
input := &ec2.StartInstancesInput{ |
|
|
input := &ec2.StartInstancesInput{ |
|
|
InstanceIds: []*string{ |
|
|
InstanceIds: []*string{ |
|
@ -186,6 +194,7 @@ func startInstance(svc *ec2.EC2, instance *ec2.Instance) error { |
|
|
return err |
|
|
return err |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
// Helper function to stop an instance.
|
|
|
func stopInstance(svc *ec2.EC2, instance *ec2.Instance) error { |
|
|
func stopInstance(svc *ec2.EC2, instance *ec2.Instance) error { |
|
|
input := &ec2.StopInstancesInput{ |
|
|
input := &ec2.StopInstancesInput{ |
|
|
InstanceIds: []*string{ |
|
|
InstanceIds: []*string{ |
|
@ -196,6 +205,7 @@ func stopInstance(svc *ec2.EC2, instance *ec2.Instance) error { |
|
|
return err |
|
|
return err |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
// Helper function to reboot an instance.
|
|
|
func rebootInstance(svc *ec2.EC2, instance *ec2.Instance) error { |
|
|
func rebootInstance(svc *ec2.EC2, instance *ec2.Instance) error { |
|
|
input := &ec2.RebootInstancesInput{ |
|
|
input := &ec2.RebootInstancesInput{ |
|
|
InstanceIds: []*string{ |
|
|
InstanceIds: []*string{ |
|
@ -206,6 +216,7 @@ func rebootInstance(svc *ec2.EC2, instance *ec2.Instance) error { |
|
|
return err |
|
|
return err |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
// Helper function to create a confirm prompt before action is taken.
|
|
|
func confirmModal(title, text string, confirmFunc func()) { |
|
|
func confirmModal(title, text string, confirmFunc func()) { |
|
|
modal := tview.NewModal(). |
|
|
modal := tview.NewModal(). |
|
|
SetText(text). |
|
|
SetText(text). |
|
@ -220,6 +231,7 @@ func confirmModal(title, text string, confirmFunc func()) { |
|
|
app.SetRoot(modal, true) |
|
|
app.SetRoot(modal, true) |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
// Helper function to display any AWS operation errors to the user.
|
|
|
func handleEC2OperationResult(err error, successMsg string, list *tview.List, detailsTable *tview.Table) { |
|
|
func handleEC2OperationResult(err error, successMsg string, list *tview.List, detailsTable *tview.Table) { |
|
|
if err != nil { |
|
|
if err != nil { |
|
|
// Show error modal
|
|
|
// Show error modal
|
|
@ -237,6 +249,7 @@ func handleEC2OperationResult(err error, successMsg string, list *tview.List, de |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
func main() { |
|
|
func main() { |
|
|
|
|
|
// Require profile and region from the user.
|
|
|
flag.StringVar(&profile, "profile", "", "AWS Profile") |
|
|
flag.StringVar(&profile, "profile", "", "AWS Profile") |
|
|
flag.StringVar(&profile, "p", "", "AWS Profile (shorthand)") |
|
|
flag.StringVar(&profile, "p", "", "AWS Profile (shorthand)") |
|
|
flag.StringVar(®ion, "region", "", "AWS Region") |
|
|
flag.StringVar(®ion, "region", "", "AWS Region") |
|
@ -248,6 +261,7 @@ func main() { |
|
|
os.Exit(1) |
|
|
os.Exit(1) |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
// Establish AWS connection.
|
|
|
sess, err := session.NewSessionWithOptions(session.Options{ |
|
|
sess, err := session.NewSessionWithOptions(session.Options{ |
|
|
Profile: profile, |
|
|
Profile: profile, |
|
|
Config: aws.Config{ |
|
|
Config: aws.Config{ |
|
@ -260,6 +274,7 @@ func main() { |
|
|
|
|
|
|
|
|
svc := ec2.New(sess) |
|
|
svc := ec2.New(sess) |
|
|
|
|
|
|
|
|
|
|
|
// Create the application.
|
|
|
app = tview.NewApplication() |
|
|
app = tview.NewApplication() |
|
|
list := tview.NewList().ShowSecondaryText(false) |
|
|
list := tview.NewList().ShowSecondaryText(false) |
|
|
detailsTable := tview.NewTable().SetBorders(true) |
|
|
detailsTable := tview.NewTable().SetBorders(true) |
|
@ -268,16 +283,19 @@ func main() { |
|
|
|
|
|
|
|
|
initialDisplay(svc, list, detailsTable) |
|
|
initialDisplay(svc, list, detailsTable) |
|
|
|
|
|
|
|
|
|
|
|
// Changed function to keep index and currentInstances slice in sync during user navigation.
|
|
|
list.SetChangedFunc(func(index int, mainText string, secondaryText string, shortcut rune) { |
|
|
list.SetChangedFunc(func(index int, mainText string, secondaryText string, shortcut rune) { |
|
|
if index >= 0 && index < len(currentInstances) { |
|
|
if index >= 0 && index < len(currentInstances) { |
|
|
displayInstanceDetails(detailsTable, currentInstances[index]) |
|
|
displayInstanceDetails(detailsTable, currentInstances[index]) |
|
|
} |
|
|
} |
|
|
}) |
|
|
}) |
|
|
|
|
|
|
|
|
|
|
|
// Global view and add instance information.
|
|
|
flex = tview.NewFlex(). |
|
|
flex = tview.NewFlex(). |
|
|
AddItem(list, 0, 1, true). |
|
|
AddItem(list, 0, 1, true). |
|
|
AddItem(detailsTable, 0, 2, false) |
|
|
AddItem(detailsTable, 0, 2, false) |
|
|
|
|
|
|
|
|
|
|
|
// Create the help modal.
|
|
|
helpModal := tview.NewModal(). |
|
|
helpModal := tview.NewModal(). |
|
|
SetText("Keyboard Commands:\n\nh - Show this help menu\nq - Quit the program\nr - Display running instances\na - Display all instances\ns - Display stopped instances\nt - Display terminated instances\n'S'(Shift+S) - Start instance.\n'X'(Shift+X) - Stop instance.\n'R'(Shift+r) - Reboot instance."). |
|
|
SetText("Keyboard Commands:\n\nh - Show this help menu\nq - Quit the program\nr - Display running instances\na - Display all instances\ns - Display stopped instances\nt - Display terminated instances\n'S'(Shift+S) - Start instance.\n'X'(Shift+X) - Stop instance.\n'R'(Shift+r) - Reboot instance."). |
|
|
AddButtons([]string{"Close"}). |
|
|
AddButtons([]string{"Close"}). |
|
@ -287,42 +305,43 @@ func main() { |
|
|
} |
|
|
} |
|
|
}) |
|
|
}) |
|
|
|
|
|
|
|
|
|
|
|
// Assign keybindings.
|
|
|
app.SetRoot(flex, true). |
|
|
app.SetRoot(flex, true). |
|
|
SetInputCapture(func(event *tcell.EventKey) *tcell.EventKey { |
|
|
SetInputCapture(func(event *tcell.EventKey) *tcell.EventKey { |
|
|
switch event.Key() { |
|
|
switch event.Key() { |
|
|
case tcell.KeyRune: |
|
|
case tcell.KeyRune: |
|
|
switch event.Rune() { |
|
|
switch event.Rune() { |
|
|
case 'h': |
|
|
case 'h': // Display help modal.
|
|
|
app.SetRoot(helpModal, true) |
|
|
app.SetRoot(helpModal, true) |
|
|
return nil |
|
|
return nil |
|
|
case 'q': |
|
|
case 'q': // Quit the application.
|
|
|
app.Stop() |
|
|
app.Stop() |
|
|
case 'r': |
|
|
case 'r': // Display running instances
|
|
|
fetchAndDisplayInstances("running", svc, list, detailsTable) |
|
|
fetchAndDisplayInstances("running", svc, list, detailsTable) |
|
|
return nil |
|
|
return nil |
|
|
case 'a': |
|
|
case 'a': // Display any(all) instances.
|
|
|
fetchAndDisplayInstances("any", svc, list, detailsTable) |
|
|
fetchAndDisplayInstances("any", svc, list, detailsTable) |
|
|
return nil |
|
|
return nil |
|
|
case 's': |
|
|
case 's': // Display terminated instances.
|
|
|
fetchAndDisplayInstances("stopped", svc, list, detailsTable) |
|
|
fetchAndDisplayInstances("stopped", svc, list, detailsTable) |
|
|
return nil |
|
|
return nil |
|
|
case 't': |
|
|
case 't': // Display terminated instances.
|
|
|
fetchAndDisplayInstances("terminated", svc, list, detailsTable) |
|
|
fetchAndDisplayInstances("terminated", svc, list, detailsTable) |
|
|
return nil |
|
|
return nil |
|
|
case 'S': // Start Instance
|
|
|
case 'S': // Start Instance.
|
|
|
confirmModal("Start Instance", "Are you sure you want to start the instance?", func() { |
|
|
confirmModal("Start Instance", "Are you sure you want to start the instance?", func() { |
|
|
fmt.Println("Executing start instance command") |
|
|
fmt.Println("Executing start instance command") |
|
|
err := startInstance(svc, currentInstances[list.GetCurrentItem()]) |
|
|
err := startInstance(svc, currentInstances[list.GetCurrentItem()]) |
|
|
handleEC2OperationResult(err, "Successfully started the instance.", list, detailsTable) |
|
|
handleEC2OperationResult(err, "Successfully started the instance.", list, detailsTable) |
|
|
}) |
|
|
}) |
|
|
return nil |
|
|
return nil |
|
|
case 'X': // Stop Instance
|
|
|
case 'X': // Stop Instance.
|
|
|
confirmModal("Stop Instance", "Are you sure you want to stop the instance?", func() { |
|
|
confirmModal("Stop Instance", "Are you sure you want to stop the instance?", func() { |
|
|
err := stopInstance(svc, currentInstances[list.GetCurrentItem()]) |
|
|
err := stopInstance(svc, currentInstances[list.GetCurrentItem()]) |
|
|
handleEC2OperationResult(err, "Successfully stopped the instance.", list, detailsTable) |
|
|
handleEC2OperationResult(err, "Successfully stopped the instance.", list, detailsTable) |
|
|
}) |
|
|
}) |
|
|
return nil |
|
|
return nil |
|
|
case 'R': // Reboot Instance
|
|
|
case 'R': // Reboot Instance.
|
|
|
confirmModal("Reboot Instance", "Are you sure you want to reboot the instance?", func() { |
|
|
confirmModal("Reboot Instance", "Are you sure you want to reboot the instance?", func() { |
|
|
err := rebootInstance(svc, currentInstances[list.GetCurrentItem()]) |
|
|
err := rebootInstance(svc, currentInstances[list.GetCurrentItem()]) |
|
|
handleEC2OperationResult(err, "Successfully rebooted the instance.", list, detailsTable) |
|
|
handleEC2OperationResult(err, "Successfully rebooted the instance.", list, detailsTable) |
|
@ -332,6 +351,7 @@ func main() { |
|
|
return event |
|
|
return event |
|
|
}) |
|
|
}) |
|
|
|
|
|
|
|
|
|
|
|
// Exit the application on panic.
|
|
|
if err := app.Run(); err != nil { |
|
|
if err := app.Run(); err != nil { |
|
|
panic(err) |
|
|
panic(err) |
|
|
} |
|
|
} |
|
|