feat: control systemd Docker daemon target
This commit is contained in:
+42
-11
@@ -26,14 +26,15 @@ type formState struct {
|
||||
}
|
||||
|
||||
type Model struct {
|
||||
application app.Module
|
||||
snapshot app.Snapshot
|
||||
page page
|
||||
selected int
|
||||
form formState
|
||||
confirm bool
|
||||
status string
|
||||
applying bool
|
||||
application app.Module
|
||||
snapshot app.Snapshot
|
||||
page page
|
||||
selected int
|
||||
form formState
|
||||
confirm bool
|
||||
targetConfirm *app.Intent
|
||||
status string
|
||||
applying bool
|
||||
}
|
||||
|
||||
type targetOperationDone struct {
|
||||
@@ -50,9 +51,18 @@ func (m Model) Update(message tea.Msg) (tea.Model, tea.Cmd) {
|
||||
if completed, ok := message.(targetOperationDone); ok {
|
||||
m.applying = false
|
||||
m.snapshot = completed.result.Snapshot
|
||||
if completed.result.Err != nil {
|
||||
if completed.result.Confirmation != "" {
|
||||
intent := app.Intent{Kind: app.SetTargetDesired}
|
||||
if m.targetConfirm != nil {
|
||||
intent = *m.targetConfirm
|
||||
}
|
||||
m.targetConfirm = &intent
|
||||
m.status = completed.result.Confirmation
|
||||
} else if completed.result.Err != nil {
|
||||
m.targetConfirm = nil
|
||||
m.status = "\x1b[31mError: " + completed.result.Err.Error() + "\x1b[0m"
|
||||
} else {
|
||||
m.targetConfirm = nil
|
||||
m.status = completed.result.Notice
|
||||
}
|
||||
return m, nil
|
||||
@@ -74,6 +84,21 @@ func (m Model) Update(message tea.Msg) (tea.Model, tea.Cmd) {
|
||||
}
|
||||
|
||||
func (m Model) updateMain(key tea.KeyMsg) (tea.Model, tea.Cmd) {
|
||||
if m.targetConfirm != nil && !m.applying {
|
||||
switch key.String() {
|
||||
case "y":
|
||||
intent := *m.targetConfirm
|
||||
intent.Confirm = true
|
||||
m.targetConfirm = nil
|
||||
m.applying = true
|
||||
m.status = ""
|
||||
return m, func() tea.Msg { return targetOperationDone{result: m.application.Dispatch(intent)} }
|
||||
case "n", "esc", "q":
|
||||
m.targetConfirm = nil
|
||||
m.status = ""
|
||||
}
|
||||
return m, nil
|
||||
}
|
||||
targets := m.availableTargets()
|
||||
switch key.String() {
|
||||
case "p":
|
||||
@@ -93,8 +118,10 @@ func (m Model) updateMain(key tea.KeyMsg) (tea.Model, tea.Cmd) {
|
||||
desired := selected.State != target.Enabled
|
||||
m.applying = true
|
||||
m.status = ""
|
||||
intent := app.Intent{Kind: app.SetTargetDesired, TargetID: selected.ID, Enabled: desired}
|
||||
m.targetConfirm = &intent
|
||||
return m, func() tea.Msg {
|
||||
return targetOperationDone{result: m.application.Dispatch(app.Intent{Kind: app.SetTargetDesired, TargetID: selected.ID, Enabled: desired})}
|
||||
return targetOperationDone{result: m.application.Dispatch(intent)}
|
||||
}
|
||||
}
|
||||
case "q", "esc", "ctrl+c":
|
||||
@@ -288,7 +315,11 @@ func (m Model) mainView() string {
|
||||
fmt.Fprintf(&b, "\nUnavailable %s: %s\n", item.Description.Name, item.AvailabilityEvidence)
|
||||
}
|
||||
}
|
||||
b.WriteString("\nSpace: toggle ↑/k ↓/j: select p: manage Proxy Profiles q/Esc: quit\n")
|
||||
if m.targetConfirm != nil && !m.applying {
|
||||
b.WriteString("\ny: confirm n/Esc: cancel\n")
|
||||
} else {
|
||||
b.WriteString("\nSpace: toggle ↑/k ↓/j: select p: manage Proxy Profiles q/Esc: quit\n")
|
||||
}
|
||||
if m.status != "" {
|
||||
fmt.Fprintf(&b, "\n%s\n", m.status)
|
||||
}
|
||||
|
||||
@@ -49,6 +49,42 @@ func TestTargetPanelShowsOnlyAvailableTargetsAndAppliesAsynchronously(t *testing
|
||||
}
|
||||
}
|
||||
|
||||
func TestTargetRestartConfirmationCancelsWithoutConfirmedMutation(t *testing.T) {
|
||||
initial := app.Snapshot{ActiveProfile: "office", Targets: []app.TargetSnapshot{{ID: "docker-daemon", Description: target.Description{Name: "Docker daemon"}, Available: true, State: target.Disabled}}}
|
||||
module := &targetModule{snapshot: initial, result: app.Result{Snapshot: initial, Confirmation: "Docker will restart immediately and may disrupt running work."}}
|
||||
model := NewModel(module)
|
||||
updated, command := model.Update(tea.KeyMsg{Type: tea.KeySpace})
|
||||
model = updated.(Model)
|
||||
updated, _ = model.Update(command())
|
||||
model = updated.(Model)
|
||||
if !strings.Contains(model.View(), "restart immediately") || len(module.intents) != 1 || module.intents[0].Confirm {
|
||||
t.Fatalf("warning = intents %#v\n%s", module.intents, model.View())
|
||||
}
|
||||
updated, command = model.Update(tea.KeyMsg{Type: tea.KeyRunes, Runes: []rune{'n'}})
|
||||
model = updated.(Model)
|
||||
if command != nil || len(module.intents) != 1 || strings.Contains(model.View(), "restart immediately") {
|
||||
t.Fatalf("cancel mutated = intents %#v\n%s", module.intents, model.View())
|
||||
}
|
||||
|
||||
updated, command = model.Update(tea.KeyMsg{Type: tea.KeySpace})
|
||||
model = updated.(Model)
|
||||
updated, _ = model.Update(command())
|
||||
model = updated.(Model)
|
||||
module.result = app.Result{
|
||||
Snapshot: app.Snapshot{ActiveProfile: "office", Targets: []app.TargetSnapshot{{ID: "docker-daemon", Description: target.Description{Name: "Docker daemon"}, Available: true, State: target.Enabled}}},
|
||||
Notice: "Docker daemon Target is now Enabled",
|
||||
}
|
||||
updated, command = model.Update(tea.KeyMsg{Type: tea.KeyRunes, Runes: []rune{'y'}})
|
||||
model = updated.(Model)
|
||||
if command == nil {
|
||||
t.Fatal("confirmation did not start operation")
|
||||
}
|
||||
_ = command()
|
||||
if len(module.intents) != 3 || !module.intents[2].Confirm {
|
||||
t.Fatalf("confirmed intents = %#v", module.intents)
|
||||
}
|
||||
}
|
||||
|
||||
func TestUnavailableShellIsOutsideMainListWithDetectionExplanation(t *testing.T) {
|
||||
module := &targetModule{snapshot: app.Snapshot{Targets: []app.TargetSnapshot{
|
||||
{ID: "shell", Description: target.Description{Name: "Shell"}, Available: false, AvailabilityEvidence: "Shell Target requires Bash or Zsh; neither was detected"},
|
||||
|
||||
Reference in New Issue
Block a user