Skip to content

Commit 43731d7

Browse files
implemented deploy logic and basic ui
1 parent 531c1b3 commit 43731d7

21 files changed

Lines changed: 295 additions & 159 deletions

File tree

client/client.go

Lines changed: 7 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -75,11 +75,9 @@ type Client interface {
7575

7676
DecommisionAccount(ctx context.Context, id AccountId) (chan DecommisionAccountProgress, error)
7777

78-
DeployPublicKeys(ctx context.Context, publicKeyId ...PublicKeyId) (chan DeployProgress, error)
78+
DeployAccount(ctx context.Context, accountId AccountId) (chan DeployProgressAccount, error)
7979

80-
DeployAccounts(ctx context.Context, accountId ...AccountId) (chan DeployProgress, error)
81-
82-
DeployAll(ctx context.Context) (chan DeployProgress, error)
80+
DeployAccounts(ctx context.Context, accountIds ...AccountId) (chan DeployProgressAccounts, error)
8381
}
8482

8583
// id is a local identifier type used by the client API.
@@ -122,21 +120,20 @@ type Link struct {
122120
// ...
123121
}
124122

125-
type DeployAccountProgress struct {
123+
type DeployProgressAccount struct {
126124
Progress float64
127125
Status string
128126
Err error
129127
}
130128

131-
// DeployProgress reports incremental progress for a deployment operation.
132-
type DeployProgress struct {
133-
Accounts map[AccountId]*DeployAccountProgress
129+
type DeployProgressAccounts struct {
130+
Accounts map[AccountId]*DeployProgressAccount
134131
}
135132

136-
func (dp DeployProgress) TotalProgress() float64 {
133+
func (dp DeployProgressAccounts) Progress() float64 {
137134
return slicest.Reduce(
138135
slicest.MapValues(dp.Accounts),
139-
func(dap *DeployAccountProgress, total float64) float64 { return total + dap.Progress },
136+
func(dap *DeployProgressAccount, total float64) float64 { return total + dap.Progress },
140137
) / float64(len(dp.Accounts))
141138
}
142139

client/mock/client.go

Lines changed: 12 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -55,9 +55,8 @@ type ClientOverwrites struct {
5555
ListExistingTags func(ctx context.Context) tags.Tags
5656
OnboardHost func(ctx context.Context, host string, port int /* , gateway string, plugin string */, accountUsername string, deploymentKey string) (chan client.OnboardHostProgress, error)
5757
DecommisionAccount func(ctx context.Context, id client.AccountId) (chan client.DecommisionAccountProgress, error)
58-
DeployPublicKeys func(ctx context.Context, publicKeyId ...client.PublicKeyId) (chan client.DeployProgress, error)
59-
DeployAccounts func(ctx context.Context, accountId ...client.AccountId) (chan client.DeployProgress, error)
60-
DeployAll func(ctx context.Context) (chan client.DeployProgress, error)
58+
DeployAccount func(ctx context.Context, accountId client.AccountId) (chan client.DeployProgressAccount, error)
59+
DeployAccounts func(ctx context.Context, accountIds ...client.AccountId) (chan client.DeployProgressAccounts, error)
6160
}
6261

6362
// *[Client] implements [client.Client]
@@ -452,38 +451,26 @@ func (m *Client) DecommisionAccount(ctx context.Context, id client.AccountId) (c
452451
panic("Client.DecommisionAccount not implemented")
453452
}
454453

455-
func (m *Client) DeployPublicKeys(ctx context.Context, publicKeyId ...client.PublicKeyId) (chan client.DeployProgress, error) {
454+
func (m *Client) DeployAccount(ctx context.Context, accountId client.AccountId) (chan client.DeployProgressAccount, error) {
456455
if m.Pre != nil {
457-
m.Pre("DeployPublicKeys", map[string]any{"ctx": ctx, "publicKeyId": publicKeyId})
456+
m.Pre("DeployAccount", map[string]any{"ctx": ctx, "accountId": accountId})
458457
}
459-
if m.Overwrites.DeployPublicKeys != nil {
460-
return m.Overwrites.DeployPublicKeys(ctx, publicKeyId...)
458+
if m.Overwrites.DeployAccount != nil {
459+
return m.Overwrites.DeployAccount(ctx, accountId)
461460
} else if m.BaseClient != nil {
462-
return m.BaseClient.DeployPublicKeys(ctx, publicKeyId...)
461+
return m.BaseClient.DeployAccount(ctx, accountId)
463462
}
464-
panic("Client.DeployPublicKeys not implemented")
463+
panic("Client.DeployAccount not implemented")
465464
}
466465

467-
func (m *Client) DeployAccounts(ctx context.Context, accountId ...client.AccountId) (chan client.DeployProgress, error) {
466+
func (m *Client) DeployAccounts(ctx context.Context, accountIds ...client.AccountId) (chan client.DeployProgressAccounts, error) {
468467
if m.Pre != nil {
469-
m.Pre("DeployAccounts", map[string]any{"ctx": ctx, "accountId": accountId})
468+
m.Pre("DeployAccounts", map[string]any{"ctx": ctx, "accountIds": accountIds})
470469
}
471470
if m.Overwrites.DeployAccounts != nil {
472-
return m.Overwrites.DeployAccounts(ctx, accountId...)
471+
return m.Overwrites.DeployAccounts(ctx, accountIds...)
473472
} else if m.BaseClient != nil {
474-
return m.BaseClient.DeployAccounts(ctx, accountId...)
473+
return m.BaseClient.DeployAccounts(ctx, accountIds...)
475474
}
476475
panic("Client.DeployAccounts not implemented")
477476
}
478-
479-
func (m *Client) DeployAll(ctx context.Context) (chan client.DeployProgress, error) {
480-
if m.Pre != nil {
481-
m.Pre("DeployAll", map[string]any{"ctx": ctx})
482-
}
483-
if m.Overwrites.DeployAll != nil {
484-
return m.Overwrites.DeployAll(ctx)
485-
} else if m.BaseClient != nil {
486-
return m.BaseClient.DeployAll(ctx)
487-
}
488-
panic("Client.DeployAll not implemented")
489-
}

client/testui/client.go

Lines changed: 88 additions & 64 deletions
Original file line numberDiff line numberDiff line change
@@ -49,59 +49,6 @@ func (c *Client) accountDeployCache(account client.Account, deployCache string)
4949
return fmt.Sprintf("%s %s@%s:%d\n%s", account.DeployMethod, account.Username, account.Host, account.Port, deployCache)
5050
}
5151

52-
func (c *Client) deployAccounts(ctx context.Context, accounts ...client.Account) (chan client.DeployProgress, error) {
53-
deployDatas, err := slicest.MapX(accounts, func(a client.Account) (string, error) {
54-
return c.accountDeployData(ctx, a)
55-
})
56-
if err != nil {
57-
return nil, err
58-
}
59-
60-
deployProgressChan := make(chan client.DeployProgress)
61-
deployProgress := client.DeployProgress{
62-
Accounts: slicest.ToMap(accounts, func(account client.Account) (client.AccountId, *client.DeployAccountProgress) {
63-
return account.Id, &client.DeployAccountProgress{0, "not started", nil}
64-
}),
65-
}
66-
67-
go func() {
68-
for i, account := range accounts {
69-
deployProgress.Accounts[account.Id].Status = "deploying"
70-
deployProgressChan <- deployProgress
71-
72-
// simulate deplay
73-
for _i := range 5 {
74-
time.Sleep(time.Millisecond * 100)
75-
deployProgress.Accounts[account.Id].Progress = float64(_i+1) / 10
76-
deployProgressChan <- deployProgress
77-
}
78-
79-
_i, ok := slices.BinarySearchFunc(c.accounts, account.Id, func(a client.Account, id client.AccountId) int { return int(a.Id - id) })
80-
if !ok {
81-
deployProgress.Accounts[account.Id].Status = "error"
82-
deployProgress.Accounts[account.Id].Progress = 1
83-
deployProgress.Accounts[account.Id].Err = fmt.Errorf("account with id %v not found", account.Id)
84-
deployProgressChan <- deployProgress
85-
continue
86-
}
87-
88-
// simulate deplay
89-
for _i := range 5 {
90-
time.Sleep(time.Millisecond * 100)
91-
deployProgress.Accounts[account.Id].Progress = float64(_i+6) / 10
92-
deployProgressChan <- deployProgress
93-
}
94-
95-
c.accounts[_i].DeployCache = c.accountDeployCache(account, deployDatas[i])
96-
deployProgress.Accounts[account.Id].Status = "finished"
97-
deployProgress.Accounts[account.Id].Progress = 1
98-
deployProgressChan <- deployProgress
99-
}
100-
}()
101-
102-
return deployProgressChan, nil
103-
}
104-
10552
// --- Lifecycle & Initialization ---
10653

10754
func NewClient() *Client {
@@ -237,9 +184,29 @@ func (c *Client) GetAccount(ctx context.Context, id client.AccountId) (client.Ac
237184
}
238185

239186
func (c *Client) GetAccounts(ctx context.Context, ids ...client.AccountId) ([]client.Account, error) {
240-
return slicest.Filter(c.accounts, func(account client.Account) bool {
187+
accounts := slicest.Filter(c.accounts, func(account client.Account) bool {
241188
return slices.Contains(ids, account.Id)
242-
}), nil
189+
})
190+
191+
if len(accounts) != len(ids) {
192+
return nil, fmt.Errorf(
193+
"accounts with the following ids could not be found: %s",
194+
strings.Join(
195+
slicest.Map(
196+
slicest.Filter(ids, func(id client.AccountId) bool {
197+
_, ok := slices.BinarySearchFunc(accounts, id, func(account client.Account, id client.AccountId) int {
198+
return int(account.Id - id)
199+
})
200+
return !ok
201+
}),
202+
func(id client.AccountId) string { return fmt.Sprint(id) },
203+
),
204+
", ",
205+
),
206+
)
207+
}
208+
209+
return accounts, nil
243210
}
244211

245212
func (c *Client) ListAccounts(ctx context.Context) ([]client.Account, error) {
@@ -359,24 +326,81 @@ func (c *Client) DecommisionAccount(ctx context.Context, id client.AccountId) (c
359326
return nil, errors.New("client.DecommisionAccount not implemented")
360327
}
361328

362-
func (c *Client) DeployPublicKeys(ctx context.Context, publicKeyId ...client.PublicKeyId) (chan client.DeployProgress, error) {
363-
return nil, errors.New("client.DeployPublicKeys not implemented")
329+
func (c *Client) DeployAccount(ctx context.Context, accountId client.AccountId) (chan client.DeployProgressAccount, error) {
330+
dpc, err := c.DeployAccounts(ctx, accountId)
331+
if err != nil {
332+
return nil, err
333+
}
334+
335+
// convert channel to only report the single accounts progress
336+
dbac := make(chan client.DeployProgressAccount)
337+
go func() {
338+
defer close(dbac)
339+
340+
for dp := range dpc {
341+
dbac <- *dp.Accounts[accountId]
342+
}
343+
}()
344+
345+
return dbac, nil
364346
}
365347

366-
func (c *Client) DeployAccounts(ctx context.Context, accountIds ...client.AccountId) (chan client.DeployProgress, error) {
348+
func (c *Client) DeployAccounts(ctx context.Context, accountIds ...client.AccountId) (chan client.DeployProgressAccounts, error) {
367349
accounts, err := c.GetAccounts(ctx, accountIds...)
368350
if err != nil {
369351
return nil, err
370352
}
371353

372-
return c.deployAccounts(ctx, accounts...)
373-
}
374-
375-
func (c *Client) DeployAll(ctx context.Context) (chan client.DeployProgress, error) {
376-
accounts, err := c.ListAccounts(ctx)
354+
deployDatas, err := slicest.MapX(accounts, func(a client.Account) (string, error) {
355+
return c.accountDeployData(ctx, a)
356+
})
377357
if err != nil {
378358
return nil, err
379359
}
380360

381-
return c.deployAccounts(ctx, accounts...)
361+
deployProgressChan := make(chan client.DeployProgressAccounts)
362+
deployProgress := client.DeployProgressAccounts{
363+
Accounts: slicest.ToMap(accounts, func(account client.Account) (client.AccountId, *client.DeployProgressAccount) {
364+
return account.Id, &client.DeployProgressAccount{0, "not started", nil}
365+
}),
366+
}
367+
368+
go func() {
369+
defer close(deployProgressChan)
370+
371+
for i, account := range accounts {
372+
deployProgress.Accounts[account.Id].Status = "deploying"
373+
deployProgressChan <- deployProgress
374+
375+
// simulate deplay
376+
for _i := range 5 {
377+
time.Sleep(time.Millisecond * 100)
378+
deployProgress.Accounts[account.Id].Progress = float64(_i+1) / 10
379+
deployProgressChan <- deployProgress
380+
}
381+
382+
_i, ok := slices.BinarySearchFunc(c.accounts, account.Id, func(a client.Account, id client.AccountId) int { return int(a.Id - id) })
383+
if !ok {
384+
deployProgress.Accounts[account.Id].Status = "error"
385+
deployProgress.Accounts[account.Id].Progress = 1
386+
deployProgress.Accounts[account.Id].Err = fmt.Errorf("account with id %v not found", account.Id)
387+
deployProgressChan <- deployProgress
388+
continue
389+
}
390+
391+
// simulate deplay
392+
for _i := range 5 {
393+
time.Sleep(time.Millisecond * 100)
394+
deployProgress.Accounts[account.Id].Progress = float64(_i+6) / 10
395+
deployProgressChan <- deployProgress
396+
}
397+
398+
c.accounts[_i].DeployCache = c.accountDeployCache(account, deployDatas[i])
399+
deployProgress.Accounts[account.Id].Status = "finished"
400+
deployProgress.Accounts[account.Id].Progress = 1
401+
deployProgressChan <- deployProgress
402+
}
403+
}()
404+
405+
return deployProgressChan, nil
382406
}

ui/tui/models/components/menu/navigation.go

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,10 @@
33
// This source code is licensed under the MIT license found in the LICENSE file.
44
package menu
55

6-
import tea "github.com/charmbracelet/bubbletea"
6+
import (
7+
tea "github.com/charmbracelet/bubbletea"
8+
"github.com/toeirei/keymaster/ui/tui/util"
9+
)
710

811
func (m *Model) up() {
912
// get pointer to current active stack index
@@ -39,7 +42,7 @@ func (m *Model) right() tea.Cmd {
3942
} else if activeItem.Cmd != nil {
4043
return activeItem.Cmd
4144
} else {
42-
return func() tea.Msg { return ItemSelected{Id: activeItem.Id} }
45+
return util.TeaMsgToCmd(ItemSelected{Id: activeItem.Id})
4346
}
4447
}
4548

ui/tui/models/components/router/controll.go

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -13,11 +13,11 @@ type Controll struct {
1313
}
1414

1515
func (c *Controll) Push(model *util.Model) tea.Cmd {
16-
return func() tea.Msg { return PushMsg{rid: c.rid, Model: model} }
16+
return util.TeaMsgToCmd(PushMsg{rid: c.rid, Model: model})
1717
}
1818
func (c *Controll) Pop(count int) tea.Cmd {
19-
return func() tea.Msg { return PopMsg{rid: c.rid, Count: count} }
19+
return util.TeaMsgToCmd(PopMsg{rid: c.rid, Count: count})
2020
}
2121
func (c *Controll) Change(model *util.Model) tea.Cmd {
22-
return func() tea.Msg { return ChangeMsg{rid: c.rid, Model: model} }
22+
return util.TeaMsgToCmd(ChangeMsg{rid: c.rid, Model: model})
2323
}

ui/tui/models/helpers/crud/create_model.go

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -72,9 +72,9 @@ func (m *CreateModel[TRecord, TRecordCreate, TRecordUpdate, TRecordId, TFilter])
7272
return popupviews.OpenProgress(
7373
popupviews.ProgressSpinner,
7474
"Creating "+m.crud.Texts.EntityNameSingular,
75-
func(_ popupviews.ProgressChan) tea.Msg {
75+
func(_ popupviews.ProgressChan) tea.Cmd {
7676
record, err := m.crud.createRecord(result)
77-
return createMsgCreateResult[TRecord]{record, err}
77+
return util.TeaMsgToCmd(createMsgCreateResult[TRecord]{record, err})
7878
},
7979
), true
8080
}),
@@ -112,7 +112,7 @@ func (m *CreateModel[TRecord, TRecordCreate, TRecordUpdate, TRecordId, TFilter])
112112
if msg.err != nil {
113113
return popupviews.OpenMessage(popupviews.MessageError, "Error creating "+m.crud.Texts.EntityNameSingular+":\n"+msg.err.Error(), nil)
114114
}
115-
return tea.Sequence(m.crud.routerControll.Pop(1), func() tea.Msg { return CreateMsgCreated[TRecord]{msg.record} })
115+
return tea.Sequence(m.crud.routerControll.Pop(1), util.TeaMsgToCmd(CreateMsgCreated[TRecord]{msg.record}))
116116
}
117117

118118
if !m.focussed {

ui/tui/models/helpers/crud/list_model.go

Lines changed: 8 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -153,10 +153,12 @@ func (m *ListModel[TRecord, TRecordCreate, TRecordUpdate, TRecordId, TFilter]) U
153153
{Name: "Delete", Cmd: popupviews.OpenProgress(
154154
popupviews.ProgressSpinner,
155155
"Deleting "+m.crud.Texts.EntityNameSingular,
156-
func(_ popupviews.ProgressChan) tea.Msg {
157-
return listMsgDeleteResult[TRecord]{
158-
record: *selectedRecord,
159-
err: m.crud.deleteRecord(m.crud.getRecordId(*selectedRecord)),
156+
func(_ popupviews.ProgressChan) tea.Cmd {
157+
return func() tea.Msg {
158+
return listMsgDeleteResult[TRecord]{
159+
record: *selectedRecord,
160+
err: m.crud.deleteRecord(m.crud.getRecordId(*selectedRecord)),
161+
}
160162
}
161163
},
162164
)},
@@ -217,9 +219,9 @@ var _ util.Model = (*ListModel[any, any, any, any, any])(nil)
217219
func (m *ListModel[TRecord, TRecordCreate, TRecordUpdate, TRecordId, TFilter]) reload() tea.Cmd {
218220
return popupviews.OpenProgress(
219221
popupviews.ProgressSpinner,
220-
"Loading "+m.crud.Texts.EntityNameMultiple, func(pc popupviews.ProgressChan) tea.Msg {
222+
"Loading "+m.crud.Texts.EntityNameMultiple, func(pc popupviews.ProgressChan) tea.Cmd {
221223
records, err := m.crud.getRecords(util.NewZero[TFilter]())
222-
return listMsgReloaded[TRecord]{records, err}
224+
return util.TeaMsgToCmd(listMsgReloaded[TRecord]{records, err})
223225
},
224226
)
225227
}

ui/tui/models/helpers/crud/update_model.go

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -69,9 +69,9 @@ func (m *UpdateModel[TRecord, TRecordCreate, TRecordUpdate, TRecordId, TFilter])
6969
return popupviews.OpenProgress(
7070
popupviews.ProgressSpinner,
7171
"Updating "+m.crud.Texts.EntityNameSingular,
72-
func(_ popupviews.ProgressChan) tea.Msg {
72+
func(_ popupviews.ProgressChan) tea.Cmd {
7373
record, err := m.crud.updateRecord(m.crud.getRecordId(m.record), result)
74-
return updateMsgUpdateResult[TRecord]{record, err}
74+
return util.TeaMsgToCmd(updateMsgUpdateResult[TRecord]{record, err})
7575
},
7676
), true
7777
}),
@@ -116,7 +116,7 @@ func (m *UpdateModel[TRecord, TRecordCreate, TRecordUpdate, TRecordId, TFilter])
116116
}
117117
return nil
118118
}
119-
return tea.Sequence(m.crud.routerControll.Pop(1), func() tea.Msg { return UpdateMsgUpdated[TRecord]{msg.record} })
119+
return tea.Sequence(m.crud.routerControll.Pop(1), util.TeaMsgToCmd(UpdateMsgUpdated[TRecord]{msg.record}))
120120
}
121121

122122
if !m.focussed {

0 commit comments

Comments
 (0)