Skip to content

Commit 5eefb14

Browse files
committed
attr
1 parent 7490021 commit 5eefb14

12 files changed

Lines changed: 382 additions & 79 deletions

eos/client_test.go

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -602,6 +602,29 @@ func TestNamespaceStatsParseWithPreamble(t *testing.T) {
602602
}
603603
}
604604

605+
func TestParseNamespaceAttrs(t *testing.T) {
606+
input := `
607+
* attr listing
608+
sys.forced.layout="replica"
609+
user.comment = "hello world"
610+
sys.mask=755
611+
`
612+
613+
attrs := parseNamespaceAttrs([]byte(input))
614+
if len(attrs) != 3 {
615+
t.Fatalf("expected 3 attrs, got %d", len(attrs))
616+
}
617+
if attrs[0].Key != "sys.forced.layout" || attrs[0].Value != "replica" {
618+
t.Fatalf("unexpected first attr: %+v", attrs[0])
619+
}
620+
if attrs[1].Key != "sys.mask" || attrs[1].Value != "755" {
621+
t.Fatalf("unexpected second attr: %+v", attrs[1])
622+
}
623+
if attrs[2].Key != "user.comment" || attrs[2].Value != "hello world" {
624+
t.Fatalf("unexpected third attr: %+v", attrs[2])
625+
}
626+
}
627+
605628
func TestParseEOSServerVersion(t *testing.T) {
606629
tests := []struct {
607630
name string

eos/fetch_namespace.go

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -97,6 +97,17 @@ func (c *Client) StatPath(ctx context.Context, rawPath string) (Entry, error) {
9797
return c.statPathViaCLI(rawPath)
9898
}
9999

100+
func (c *Client) ListAttrs(ctx context.Context, rawPath string) ([]NamespaceAttr, error) {
101+
_ = ctx
102+
103+
output, err := c.runCommand("eos", "attr", "ls", rawPath)
104+
if err != nil {
105+
return nil, fmt.Errorf("eos attr ls: %w", err)
106+
}
107+
108+
return parseNamespaceAttrs(output), nil
109+
}
110+
100111
func (c *Client) statPathViaCLI(rawPath string) (Entry, error) {
101112
info, err := c.fetchCLIFileInfo(rawPath)
102113
if err != nil {
@@ -191,3 +202,37 @@ func entryFromCLI(info cliFileInfo) Entry {
191202

192203
return entry
193204
}
205+
206+
func parseNamespaceAttrs(output []byte) []NamespaceAttr {
207+
lines := strings.Split(strings.ReplaceAll(string(output), "\r\n", "\n"), "\n")
208+
attrs := make([]NamespaceAttr, 0, len(lines))
209+
for _, line := range lines {
210+
line = strings.TrimSpace(line)
211+
if line == "" || strings.HasPrefix(line, "*") {
212+
continue
213+
}
214+
215+
key, value, found := strings.Cut(line, "=")
216+
if !found {
217+
continue
218+
}
219+
220+
key = strings.TrimSpace(key)
221+
value = strings.TrimSpace(value)
222+
value = strings.Trim(value, "\"")
223+
if key == "" {
224+
continue
225+
}
226+
227+
attrs = append(attrs, NamespaceAttr{
228+
Key: key,
229+
Value: value,
230+
})
231+
}
232+
233+
sort.Slice(attrs, func(i, j int) bool {
234+
return strings.ToLower(attrs[i].Key) < strings.ToLower(attrs[j].Key)
235+
})
236+
237+
return attrs
238+
}

eos/types.go

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,11 @@ type Directory struct {
5959
Entries []Entry
6060
}
6161

62+
type NamespaceAttr struct {
63+
Key string
64+
Value string
65+
}
66+
6267
type NodeStats struct {
6368
State string
6469
FileCount uint64

ui/commands.go

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -94,6 +94,13 @@ func loadDirectoryCmd(client *eos.Client, dirPath string) tea.Cmd {
9494
}
9595
}
9696

97+
func loadNamespaceAttrsCmd(client *eos.Client, path string) tea.Cmd {
98+
return func() tea.Msg {
99+
attrs, err := client.ListAttrs(context.Background(), path)
100+
return namespaceAttrsLoadedMsg{path: path, attrs: attrs, err: err}
101+
}
102+
}
103+
97104
func loadSpaceStatusCmd(client *eos.Client) tea.Cmd {
98105
return func() tea.Msg {
99106
records, err := client.SpaceStatus(context.Background(), "default")

ui/keys.go

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -147,24 +147,27 @@ func (m model) updateFileSystemKeys(msg tea.KeyMsg) (tea.Model, tea.Cmd) {
147147

148148
func (m model) updateNamespaceKeys(msg tea.KeyMsg) (tea.Model, tea.Cmd) {
149149
half := max(1, m.height/6)
150+
selectionChanged := false
150151
switch msg.String() {
151152
case "up", "k":
152153
if m.nsSelected > 0 {
153154
m.nsSelected--
155+
selectionChanged = true
154156
}
155157
case "down", "j":
156158
if m.nsSelected < len(m.directory.Entries)-1 {
157159
m.nsSelected++
160+
selectionChanged = true
158161
}
159162
case "ctrl+u":
160163
m.nsSelected = max(0, m.nsSelected-half)
161-
return m, nil
164+
selectionChanged = true
162165
case "ctrl+d":
163166
m.nsSelected = min(len(m.directory.Entries)-1, m.nsSelected+half)
164-
return m, nil
167+
selectionChanged = true
165168
case "G":
166169
m.nsSelected = max(0, len(m.directory.Entries)-1)
167-
return m, nil
170+
selectionChanged = true
168171
case "g":
169172
m.nsSelected = 0
170173
m.nsLoading = true
@@ -188,6 +191,10 @@ func (m model) updateNamespaceKeys(msg tea.KeyMsg) (tea.Model, tea.Cmd) {
188191
}
189192
}
190193

194+
if selectionChanged {
195+
return m.startNamespaceAttrLoad(false)
196+
}
197+
191198
return m, nil
192199
}
193200

ui/model.go

Lines changed: 51 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -27,8 +27,10 @@ func NewModel(client *eos.Client, endpoint, rootPath string) tea.Model {
2727
)
2828

2929
state := persistedUIState{}
30+
activeView := defaultActiveView()
3031
if rootPath == "" {
3132
state = loadPersistedUIState()
33+
activeView = state.ActiveView
3234
}
3335
initialPath := rootPath
3436
if initialPath == "" {
@@ -37,7 +39,6 @@ func NewModel(client *eos.Client, endpoint, rootPath string) tea.Model {
3739
if initialPath == "" {
3840
initialPath = "/eos"
3941
}
40-
activeView := state.ActiveView
4142
commandLogVisible := state.CommandLogVisible
4243

4344
return model{
@@ -162,40 +163,13 @@ func (m model) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
162163
}
163164
return m, nil
164165
case "tab":
165-
m.activeView = (m.activeView + 1) % viewCount
166+
m.activeView = nextOrderedView(m.activeView, 1)
166167
return m.onViewChanged()
167168
case "shift+tab":
168-
m.activeView = (m.activeView + viewCount - 1) % viewCount
169+
m.activeView = nextOrderedView(m.activeView, -1)
169170
return m.onViewChanged()
170-
case "1":
171-
m.activeView = viewMGM
172-
return m.onViewChanged()
173-
case "2":
174-
m.activeView = viewQDB
175-
return m.onViewChanged()
176-
case "3":
177-
m.activeView = viewFST
178-
return m.onViewChanged()
179-
case "4":
180-
m.activeView = viewFileSystems
181-
return m.onViewChanged()
182-
case "5":
183-
m.activeView = viewNamespace
184-
return m.onViewChanged()
185-
case "6":
186-
m.activeView = viewSpaces
187-
return m.onViewChanged()
188-
case "7":
189-
m.activeView = viewNamespaceStats
190-
return m.onViewChanged()
191-
case "8":
192-
m.activeView = viewSpaceStatus
193-
return m.onViewChanged()
194-
case "9":
195-
m.activeView = viewIOShaping
196-
return m.onViewChanged()
197-
case "0":
198-
m.activeView = viewGroups
171+
case "1", "2", "3", "4", "5", "6", "7", "8", "9", "0":
172+
m.activeView, _ = viewForHotkey(msg.String())
199173
return m.onViewChanged()
200174
case "r":
201175
return m.refreshActiveView()
@@ -360,6 +334,17 @@ func (m model) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
360334
}
361335
m.status = fmt.Sprintf("Browsing namespace %s", m.directory.Path)
362336
m.persistUIState()
337+
return m.startNamespaceAttrLoad(true)
338+
}
339+
case namespaceAttrsLoadedMsg:
340+
if msg.path != m.nsAttrsTargetPath {
341+
return m, nil
342+
}
343+
m.nsAttrsLoading = false
344+
m.nsAttrsLoaded = true
345+
m.nsAttrsErr = msg.err
346+
if msg.err == nil {
347+
m.nsAttrs = msg.attrs
363348
}
364349
case spaceStatusLoadedMsg:
365350
m.spaceStatusLoading = false
@@ -636,16 +621,49 @@ func (m model) refreshActiveView() (tea.Model, tea.Cmd) {
636621
}
637622

638623
func (m model) maybeLoadNamespace() (tea.Model, tea.Cmd) {
639-
if m.nsLoaded || m.nsLoading {
624+
if m.nsLoading {
640625
return m, nil
641626
}
627+
if m.nsLoaded {
628+
return m.startNamespaceAttrLoad(false)
629+
}
642630

643631
m.nsLoading = true
644632
m.nsErr = nil
645633
m.status = fmt.Sprintf("Loading namespace %s...", m.directory.Path)
646634
return m, loadDirectoryCmd(m.client, m.directory.Path)
647635
}
648636

637+
func (m model) currentNamespaceAttrTargetPath() string {
638+
if selected, ok := m.selectedNamespaceEntry(); ok && selected.Path != "" {
639+
return selected.Path
640+
}
641+
if m.directory.Self.Path != "" {
642+
return m.directory.Self.Path
643+
}
644+
if m.directory.Path != "" {
645+
return m.directory.Path
646+
}
647+
return "/"
648+
}
649+
650+
func (m model) startNamespaceAttrLoad(force bool) (tea.Model, tea.Cmd) {
651+
path := m.currentNamespaceAttrTargetPath()
652+
if path == "" || m.client == nil {
653+
return m, nil
654+
}
655+
if !force && m.nsAttrsTargetPath == path && (m.nsAttrsLoading || m.nsAttrsLoaded) {
656+
return m, nil
657+
}
658+
659+
m.nsAttrsTargetPath = path
660+
m.nsAttrsLoading = true
661+
m.nsAttrsLoaded = false
662+
m.nsAttrsErr = nil
663+
m.nsAttrs = nil
664+
return m, loadNamespaceAttrsCmd(m.client, path)
665+
}
666+
649667
func (m model) maybeLoadSpaceStatus() (tea.Model, tea.Cmd) {
650668
if !m.spaceStatusLoading && len(m.spaceStatus) > 0 {
651669
return m, nil

0 commit comments

Comments
 (0)