ssm-browse: new utility to browse SSM parameters
This is more of an exercise to work out how best to use controllers
This commit is contained in:
parent
46be54b5fb
commit
0b745a6dfa
14 changed files with 348 additions and 5 deletions
|
|
@ -1,8 +1,12 @@
|
|||
package events
|
||||
|
||||
import tea "github.com/charmbracelet/bubbletea"
|
||||
import (
|
||||
tea "github.com/charmbracelet/bubbletea"
|
||||
"log"
|
||||
)
|
||||
|
||||
func Error(err error) tea.Msg {
|
||||
log.Println(err)
|
||||
return ErrorMsg(err)
|
||||
}
|
||||
|
||||
|
|
|
|||
18
internal/common/ui/logging/debug.go
Normal file
18
internal/common/ui/logging/debug.go
Normal file
|
|
@ -0,0 +1,18 @@
|
|||
package logging
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
tea "github.com/charmbracelet/bubbletea"
|
||||
"os"
|
||||
)
|
||||
|
||||
func EnableLogging() (closeFn func()) {
|
||||
f, err := tea.LogToFile("debug.log", "debug")
|
||||
if err != nil {
|
||||
fmt.Println("fatal:", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
return func() {
|
||||
f.Close()
|
||||
}
|
||||
}
|
||||
7
internal/ssm-browse/controllers/events.go
Normal file
7
internal/ssm-browse/controllers/events.go
Normal file
|
|
@ -0,0 +1,7 @@
|
|||
package controllers
|
||||
|
||||
import "github.com/lmika/awstools/internal/ssm-browse/models"
|
||||
|
||||
type NewParameterListMsg struct {
|
||||
Parameters *models.SSMParameters
|
||||
}
|
||||
31
internal/ssm-browse/controllers/ssmcontroller.go
Normal file
31
internal/ssm-browse/controllers/ssmcontroller.go
Normal file
|
|
@ -0,0 +1,31 @@
|
|||
package controllers
|
||||
|
||||
import (
|
||||
"context"
|
||||
tea "github.com/charmbracelet/bubbletea"
|
||||
"github.com/lmika/awstools/internal/common/ui/events"
|
||||
"github.com/lmika/awstools/internal/ssm-browse/services/ssmparameters"
|
||||
)
|
||||
|
||||
type SSMController struct {
|
||||
service *ssmparameters.Service
|
||||
}
|
||||
|
||||
func New(service *ssmparameters.Service) *SSMController {
|
||||
return &SSMController{
|
||||
service: service,
|
||||
}
|
||||
}
|
||||
|
||||
func (c *SSMController) Fetch() tea.Cmd {
|
||||
return func() tea.Msg {
|
||||
res, err := c.service.List(context.Background())
|
||||
if err != nil {
|
||||
return events.Error(err)
|
||||
}
|
||||
|
||||
return NewParameterListMsg{
|
||||
Parameters: res,
|
||||
}
|
||||
}
|
||||
}
|
||||
10
internal/ssm-browse/models/models.go
Normal file
10
internal/ssm-browse/models/models.go
Normal file
|
|
@ -0,0 +1,10 @@
|
|||
package models
|
||||
|
||||
type SSMParameters struct {
|
||||
Items []SSMParameter
|
||||
}
|
||||
|
||||
type SSMParameter struct {
|
||||
Name string
|
||||
Value string
|
||||
}
|
||||
42
internal/ssm-browse/providers/awsssm/provider.go
Normal file
42
internal/ssm-browse/providers/awsssm/provider.go
Normal file
|
|
@ -0,0 +1,42 @@
|
|||
package awsssm
|
||||
|
||||
import (
|
||||
"context"
|
||||
"github.com/aws/aws-sdk-go-v2/aws"
|
||||
"github.com/aws/aws-sdk-go-v2/service/ssm"
|
||||
"github.com/lmika/awstools/internal/ssm-browse/models"
|
||||
"github.com/pkg/errors"
|
||||
)
|
||||
|
||||
type Provider struct {
|
||||
client *ssm.Client
|
||||
}
|
||||
|
||||
func NewProvider(client *ssm.Client) *Provider {
|
||||
return &Provider{
|
||||
client: client,
|
||||
}
|
||||
}
|
||||
|
||||
func (p *Provider) List(ctx context.Context) (*models.SSMParameters, error) {
|
||||
pars, err := p.client.GetParametersByPath(ctx, &ssm.GetParametersByPathInput{
|
||||
Path: aws.String("/"),
|
||||
MaxResults: 10,
|
||||
Recursive: true,
|
||||
})
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "cannot get parameters from path")
|
||||
}
|
||||
|
||||
res := &models.SSMParameters{
|
||||
Items: make([]models.SSMParameter, len(pars.Parameters)),
|
||||
}
|
||||
for i, p := range pars.Parameters {
|
||||
res.Items[i] = models.SSMParameter{
|
||||
Name: aws.ToString(p.Name),
|
||||
Value: aws.ToString(p.Value),
|
||||
}
|
||||
}
|
||||
|
||||
return res, nil
|
||||
}
|
||||
10
internal/ssm-browse/services/ssmparameters/iface.go
Normal file
10
internal/ssm-browse/services/ssmparameters/iface.go
Normal file
|
|
@ -0,0 +1,10 @@
|
|||
package ssmparameters
|
||||
|
||||
import (
|
||||
"context"
|
||||
"github.com/lmika/awstools/internal/ssm-browse/models"
|
||||
)
|
||||
|
||||
type SSMProvider interface {
|
||||
List(ctx context.Context) (*models.SSMParameters, error)
|
||||
}
|
||||
20
internal/ssm-browse/services/ssmparameters/service.go
Normal file
20
internal/ssm-browse/services/ssmparameters/service.go
Normal file
|
|
@ -0,0 +1,20 @@
|
|||
package ssmparameters
|
||||
|
||||
import (
|
||||
"context"
|
||||
"github.com/lmika/awstools/internal/ssm-browse/models"
|
||||
)
|
||||
|
||||
type Service struct {
|
||||
provider SSMProvider
|
||||
}
|
||||
|
||||
func NewService(provider SSMProvider) *Service {
|
||||
return &Service{
|
||||
provider: provider,
|
||||
}
|
||||
}
|
||||
|
||||
func (s *Service) List(ctx context.Context) (*models.SSMParameters, error) {
|
||||
return s.provider.List(ctx)
|
||||
}
|
||||
55
internal/ssm-browse/ui/model.go
Normal file
55
internal/ssm-browse/ui/model.go
Normal file
|
|
@ -0,0 +1,55 @@
|
|||
package ui
|
||||
|
||||
import (
|
||||
tea "github.com/charmbracelet/bubbletea"
|
||||
"github.com/lmika/awstools/internal/dynamo-browse/ui/teamodels/layout"
|
||||
"github.com/lmika/awstools/internal/dynamo-browse/ui/teamodels/statusandprompt"
|
||||
"github.com/lmika/awstools/internal/ssm-browse/controllers"
|
||||
"github.com/lmika/awstools/internal/ssm-browse/ui/ssmlist"
|
||||
)
|
||||
|
||||
type Model struct {
|
||||
controller *controllers.SSMController
|
||||
|
||||
root tea.Model
|
||||
ssmList *ssmlist.Model
|
||||
}
|
||||
|
||||
func NewModel(controller *controllers.SSMController) Model {
|
||||
ssmList := ssmlist.New()
|
||||
root := layout.FullScreen(
|
||||
statusandprompt.New(ssmList, "Hello SSM"),
|
||||
)
|
||||
|
||||
return Model{
|
||||
controller: controller,
|
||||
root: root,
|
||||
ssmList: ssmList,
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
func (m Model) Init() tea.Cmd {
|
||||
return m.controller.Fetch()
|
||||
}
|
||||
|
||||
func (m Model) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
|
||||
switch msg := msg.(type) {
|
||||
case controllers.NewParameterListMsg:
|
||||
m.ssmList.SetParameters(msg.Parameters)
|
||||
case tea.KeyMsg:
|
||||
switch msg.String() {
|
||||
case "ctrl+c", "q":
|
||||
return m, tea.Quit
|
||||
}
|
||||
}
|
||||
|
||||
newRoot, cmd := m.root.Update(msg)
|
||||
m.root = newRoot
|
||||
return m, cmd
|
||||
}
|
||||
|
||||
func (m Model) View() string {
|
||||
return m.root.View()
|
||||
}
|
||||
|
||||
69
internal/ssm-browse/ui/ssmlist/ssmlist.go
Normal file
69
internal/ssm-browse/ui/ssmlist/ssmlist.go
Normal file
|
|
@ -0,0 +1,69 @@
|
|||
package ssmlist
|
||||
|
||||
import (
|
||||
table "github.com/calyptia/go-bubble-table"
|
||||
tea "github.com/charmbracelet/bubbletea"
|
||||
"github.com/charmbracelet/lipgloss"
|
||||
"github.com/lmika/awstools/internal/dynamo-browse/ui/teamodels/frame"
|
||||
"github.com/lmika/awstools/internal/dynamo-browse/ui/teamodels/layout"
|
||||
"github.com/lmika/awstools/internal/ssm-browse/models"
|
||||
)
|
||||
|
||||
type Model struct {
|
||||
frameTitle frame.FrameTitle
|
||||
table table.Model
|
||||
|
||||
parameters *models.SSMParameters
|
||||
|
||||
w, h int
|
||||
}
|
||||
|
||||
func New() *Model {
|
||||
frameTitle := frame.NewFrameTitle("SSM", true)
|
||||
table := table.New([]string{"name", "type", "value"}, 0, 0)
|
||||
|
||||
return &Model{
|
||||
frameTitle: frameTitle,
|
||||
table: table,
|
||||
}
|
||||
}
|
||||
|
||||
func (m *Model) SetParameters(parameters *models.SSMParameters) {
|
||||
m.parameters = parameters
|
||||
cols := []string{"name", "type", "value"}
|
||||
|
||||
newTbl := table.New(cols, m.w, m.h-m.frameTitle.HeaderHeight())
|
||||
newRows := make([]table.Row, len(parameters.Items))
|
||||
for i, r := range parameters.Items {
|
||||
newRows[i] = itemTableRow{r}
|
||||
}
|
||||
newTbl.SetRows(newRows)
|
||||
|
||||
m.table = newTbl
|
||||
}
|
||||
|
||||
func (m *Model) Init() tea.Cmd {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (m *Model) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
|
||||
var cmd tea.Cmd
|
||||
switch msg := msg.(type) {
|
||||
case tea.KeyMsg:
|
||||
m.table, cmd = m.table.Update(msg)
|
||||
return m, cmd
|
||||
}
|
||||
return m, nil
|
||||
}
|
||||
|
||||
func (m *Model) View() string {
|
||||
return lipgloss.JoinVertical(lipgloss.Top, m.frameTitle.View(), m.table.View())
|
||||
}
|
||||
|
||||
func (m *Model) Resize(w, h int) layout.ResizingModel {
|
||||
m.w, m.h = w, h
|
||||
m.frameTitle.Resize(w, h)
|
||||
m.table.SetSize(w, h - m.frameTitle.HeaderHeight())
|
||||
return m
|
||||
}
|
||||
|
||||
22
internal/ssm-browse/ui/ssmlist/tblmodel.go
Normal file
22
internal/ssm-browse/ui/ssmlist/tblmodel.go
Normal file
|
|
@ -0,0 +1,22 @@
|
|||
package ssmlist
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
table "github.com/calyptia/go-bubble-table"
|
||||
"github.com/lmika/awstools/internal/ssm-browse/models"
|
||||
"io"
|
||||
)
|
||||
|
||||
type itemTableRow struct {
|
||||
item models.SSMParameter
|
||||
}
|
||||
|
||||
func (mtr itemTableRow) Render(w io.Writer, model table.Model, index int) {
|
||||
line := fmt.Sprintf("%s\t%s\t%s", mtr.item.Name, "String", mtr.item.Value)
|
||||
|
||||
if index == model.Cursor() {
|
||||
fmt.Fprintln(w, model.Styles.SelectedRow.Render(line))
|
||||
} else {
|
||||
fmt.Fprintln(w, line)
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue