Skip to content

Commit

Permalink
Merge pull request #10 from magodo/planned_change
Browse files Browse the repository at this point in the history
Display information in the "Plan" stage
  • Loading branch information
magodo authored Jan 22, 2025
2 parents 855b1fd + 7071a6a commit d3054e4
Show file tree
Hide file tree
Showing 5 changed files with 144 additions and 65 deletions.
4 changes: 2 additions & 2 deletions internal/csv/csv.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,8 @@ import (
)

type Input struct {
RefreshInfos state.ResourceInfos
ApplyInfos state.ResourceInfos
RefreshInfos state.ResourceOperationInfos
ApplyInfos state.ResourceOperationInfos
}

func ToCsv(input Input) []byte {
Expand Down
36 changes: 18 additions & 18 deletions internal/plainui/ui.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,8 +20,8 @@ type UIModel struct {
reader reader.Reader
writer io.Writer

refreshInfos state.ResourceInfos
applyInfos state.ResourceInfos
refreshInfos state.ResourceOperationInfos
applyInfos state.ResourceOperationInfos

totalCnt int
doneCnt int
Expand Down Expand Up @@ -117,28 +117,28 @@ func (m *UIModel) Run() error {
case views.HookMsg:
switch hook := msg.Hook.(type) {
case json.RefreshStart:
res := &state.ResourceInfo{
res := &state.ResourceOperationInfo{
Idx: len(m.refreshInfos) + 1,
RawResourceAddr: hook.Resource,
Loc: state.ResourceInfoLocator{
Loc: state.ResourceOperationInfoLocator{
Module: hook.Resource.Module,
ResourceAddr: hook.Resource.Addr,
Action: "refresh",
},
Status: state.ResourceStatusStart,
Status: state.ResourceOperationStatusStart,
StartTime: msg.TimeStamp,
}
m.refreshInfos = append(m.refreshInfos, res)
msgstr = msg.Message

case json.RefreshComplete:
loc := state.ResourceInfoLocator{
loc := state.ResourceOperationInfoLocator{
Module: hook.Resource.Module,
ResourceAddr: hook.Resource.Addr,
Action: "refresh",
}
status := state.ResourceStatusComplete
update := state.ResourceInfoUpdate{
status := state.ResourceOperationStatusComplete
update := state.ResourceOperationInfoUpdate{
Status: &status,
Endtime: &msg.TimeStamp,
}
Expand All @@ -149,15 +149,15 @@ func (m *UIModel) Run() error {
msgstr = msg.Message

case json.OperationStart:
info := &state.ResourceInfo{
info := &state.ResourceOperationInfo{
Idx: len(m.applyInfos) + 1,
RawResourceAddr: hook.Resource,
Loc: state.ResourceInfoLocator{
Loc: state.ResourceOperationInfoLocator{
Module: hook.Resource.Module,
ResourceAddr: hook.Resource.Addr,
Action: string(hook.Action),
},
Status: state.ResourceStatusStart,
Status: state.ResourceOperationStatusStart,
StartTime: msg.TimeStamp,
}
m.applyInfos = append(m.applyInfos, info)
Expand All @@ -166,7 +166,7 @@ func (m *UIModel) Run() error {
msgstr = fmt.Sprintf("[%*d/%*d] %s", w, info.Idx, w, m.totalCnt, msg.Message)

case json.OperationProgress:
loc := state.ResourceInfoLocator{
loc := state.ResourceOperationInfoLocator{
Module: hook.Resource.Module,
ResourceAddr: hook.Resource.Addr,
Action: string(hook.Action),
Expand All @@ -181,13 +181,13 @@ func (m *UIModel) Run() error {
msgstr = fmt.Sprintf("[%*d/%*d] %s", w, info.Idx, w, m.totalCnt, msg.Message)

case json.OperationComplete:
loc := state.ResourceInfoLocator{
loc := state.ResourceOperationInfoLocator{
Module: hook.Resource.Module,
ResourceAddr: hook.Resource.Addr,
Action: string(hook.Action),
}
status := state.ResourceStatusComplete
update := state.ResourceInfoUpdate{
status := state.ResourceOperationStatusComplete
update := state.ResourceOperationInfoUpdate{
Status: &status,
Endtime: &msg.TimeStamp,
}
Expand All @@ -201,13 +201,13 @@ func (m *UIModel) Run() error {
msgstr = fmt.Sprintf("[%*d/%*d] %s", w, info.Idx, w, m.totalCnt, msg.Message)

case json.OperationErrored:
loc := state.ResourceInfoLocator{
loc := state.ResourceOperationInfoLocator{
Module: hook.Resource.Module,
ResourceAddr: hook.Resource.Addr,
Action: string(hook.Action),
}
status := state.ResourceStatusErrored
update := state.ResourceInfoUpdate{
status := state.ResourceOperationStatusErrored
update := state.ResourceOperationInfoUpdate{
Status: &status,
Endtime: &msg.TimeStamp,
}
Expand Down
67 changes: 67 additions & 0 deletions internal/state/plan_info.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
package state

import (
"fmt"
"strconv"

"github.com/charmbracelet/bubbles/table"
"github.com/magodo/pipeform/internal/terraform/views/json"
)

type PlanInfo struct {
Resource json.ResourceAddr
Action json.ChangeAction

PrevResource *json.ResourceAddr
Reason json.ChangeReason
}

type PlanInfos []*PlanInfo

func (infos PlanInfos) ToRows() []table.Row {
var rows []table.Row
for i, info := range infos {
var comment string
switch info.Action {
case json.ActionDelete, json.ActionReplace:
comment = string(info.Reason)
case json.ActionMove:
if info.PrevResource != nil {
source := info.PrevResource.Addr
if info.PrevResource.Module != "" {
source = fmt.Sprintf("%s (%s)", source, info.PrevResource.Module)
}
comment = fmt.Sprintf("Moved from %s", source)
}
}
row := []string{
strconv.Itoa(i + 1),
info.Resource.Module,
info.Resource.Addr,
string(info.Action),
comment,
}
rows = append(rows, row)
}
return rows
}

func (infos PlanInfos) ToColumns(width int) []table.Column {
const indexWidth = 6
const actionWidth = 8

dynamicWidth := width - indexWidth - actionWidth

commentWidth := dynamicWidth / 3
moduleWidth := dynamicWidth / 3
resourceWidth := dynamicWidth / 3

return []table.Column{
{Title: "Index", Width: indexWidth},
{Title: "Module", Width: moduleWidth},
{Title: "Resource", Width: resourceWidth},
{Title: "Action", Width: actionWidth},
// Comment is a combination of "reason" (for delete/replace) and a modified version of "previous_resource" (for move)
{Title: "Comment", Width: commentWidth},
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -10,57 +10,57 @@ import (
"github.com/magodo/pipeform/internal/terraform/views/json"
)

type ResourceStatus string
type ResourceOperationStatus string

const (
// Once received one OperationStart hook message
ResourceStatusStart ResourceStatus = "start"
ResourceOperationStatusStart ResourceOperationStatus = "start"
// Once received one OperationComplete hook message
ResourceStatusComplete ResourceStatus = "complete"
ResourceOperationStatusComplete ResourceOperationStatus = "complete"
// Once received one OperationErrored hook message
ResourceStatusErrored ResourceStatus = "error"
ResourceOperationStatusErrored ResourceOperationStatus = "error"

// TODO: Support refresh? (refresh is an independent lifecycle than the resource apply lifecycle)
// TODO: Support provision? (provision is a intermidiate stage in the resource apply lifecycle)
)

func resourceStatusEmoji(status ResourceStatus) string {
func resourceOperationStatusEmoji(status ResourceOperationStatus) string {
switch status {
case ResourceStatusStart:
case ResourceOperationStatusStart:
return "🕛"
case ResourceStatusComplete:
case ResourceOperationStatusComplete:
return "✅"
case ResourceStatusErrored:
case ResourceOperationStatusErrored:
return "❌"
default:
return "❓"
}
}

type ResourceInfoLocator struct {
type ResourceOperationInfoLocator struct {
Module string
ResourceAddr string
Action string
}

type ResourceInfo struct {
type ResourceOperationInfo struct {
Idx int
RawResourceAddr json.ResourceAddr
Loc ResourceInfoLocator
Status ResourceStatus
Loc ResourceOperationInfoLocator
Status ResourceOperationStatus
StartTime time.Time
EndTime time.Time
}

type ResourceInfoUpdate struct {
Status *ResourceStatus
type ResourceOperationInfoUpdate struct {
Status *ResourceOperationStatus
Endtime *time.Time
}

// ResourceInfos records the operation information for each resource's action.
type ResourceInfos []*ResourceInfo
// ResourceOperationInfos records the operation information for each resource's action.
type ResourceOperationInfos []*ResourceOperationInfo

func (infos ResourceInfos) Find(loc ResourceInfoLocator) *ResourceInfo {
func (infos ResourceOperationInfos) Find(loc ResourceOperationInfoLocator) *ResourceOperationInfo {
for _, info := range infos {
if info.Loc == loc {
return info
Expand All @@ -69,7 +69,7 @@ func (infos ResourceInfos) Find(loc ResourceInfoLocator) *ResourceInfo {
return nil
}

func (infos ResourceInfos) Update(loc ResourceInfoLocator, update ResourceInfoUpdate) *ResourceInfo {
func (infos ResourceOperationInfos) Update(loc ResourceOperationInfoLocator, update ResourceOperationInfoUpdate) *ResourceOperationInfo {
info := infos.Find(loc)
if info == nil {
return nil
Expand All @@ -85,7 +85,7 @@ func (infos ResourceInfos) Update(loc ResourceInfoLocator, update ResourceInfoUp

// ToRows turns the ResourceInfos into table rows.
// The total is used to decorate the index as a fraction, if total > 0.
func (infos ResourceInfos) ToRows(total int) []table.Row {
func (infos ResourceOperationInfos) ToRows(total int) []table.Row {
now := time.Now()
var rows []table.Row
for _, info := range infos {
Expand All @@ -103,7 +103,7 @@ func (infos ResourceInfos) ToRows(total int) []table.Row {

row := []string{
idx,
resourceStatusEmoji(info.Status),
resourceOperationStatusEmoji(info.Status),
string(info.Loc.Action),
module,
info.Loc.ResourceAddr,
Expand All @@ -114,7 +114,7 @@ func (infos ResourceInfos) ToRows(total int) []table.Row {
return rows
}

func (infos ResourceInfos) ToColumns(width int) []table.Column {
func (infos ResourceOperationInfos) ToColumns(width int) []table.Column {
const statusWidth = 6
const actionWidth = 8
const timeWidth = 24
Expand All @@ -135,7 +135,7 @@ func (infos ResourceInfos) ToColumns(width int) []table.Column {
}
}

func (infos ResourceInfos) ToCsv(stage string) []string {
func (infos ResourceOperationInfos) ToCsv(stage string) []string {
var out []string
now := time.Now()
for _, info := range infos {
Expand All @@ -157,7 +157,7 @@ func (infos ResourceInfos) ToCsv(stage string) []string {
return out
}

func (info ResourceInfo) Duration(now time.Time) time.Duration {
func (info ResourceOperationInfo) Duration(now time.Time) time.Duration {
var dur time.Duration
if info.EndTime.Equal(time.Time{}) {
dur = now.Sub(info.StartTime).Truncate(time.Second)
Expand Down
Loading

0 comments on commit d3054e4

Please sign in to comment.