Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions internal/nomad/client.go
Original file line number Diff line number Diff line change
Expand Up @@ -326,8 +326,6 @@ func newJob(stub *api.JobListStub) Job {
return job
}

// unixTime reads a Nomad timestamp. Nomad says "never" with a zero, which is
// not 1970: a job with no submit time would read as twenty thousand days old.
// valueOf is what a field the cluster may leave out holds: the zero value
// when it is left out.
func valueOf[T any](value *T) T {
Expand All @@ -340,6 +338,8 @@ func valueOf[T any](value *T) T {
return *value
}

// unixTime reads a Nomad timestamp. Nomad says "never" with a zero, which is
// not 1970: a job with no submit time would read as twenty thousand days old.
func unixTime(nanos int64) time.Time {
if nanos == 0 {
return time.Time{}
Expand Down
2 changes: 1 addition & 1 deletion internal/ui/acl.go
Original file line number Diff line number Diff line change
Expand Up @@ -84,7 +84,7 @@ func (aclPage) topics() []string { return nil }
func (p aclPage) fetch(e env) tea.Cmd {
client, kind := e.client, p.kind

return fetchList(func(ctx context.Context) ([]nomad.ACLObject, error) {
return request(func(ctx context.Context) ([]nomad.ACLObject, error) {
return client.ACLObjects(ctx, kind)
}, func(items []nomad.ACLObject) tea.Msg { return aclMsg{kind: kind, objects: items} })
}
Expand Down
6 changes: 3 additions & 3 deletions internal/ui/alloc.go
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,7 @@ func (p allocationsPage) where(e env) string {
func (p allocationsPage) fetch(e env) tea.Cmd {
client, namespace, jobID, evalID := e.client, p.where(e), p.jobID, p.evalID

return fetchList(func(ctx context.Context) ([]nomad.Alloc, error) {
return request(func(ctx context.Context) ([]nomad.Alloc, error) {
if evalID != "" {
return client.EvaluationAllocations(ctx, namespace, evalID)
}
Expand All @@ -79,13 +79,13 @@ func (p allocationsPage) fetch(e env) tea.Cmd {
}, func(items []nomad.Alloc) tea.Msg { return allocsMsg(items) })
}

func (p allocationsPage) take(msg tea.Msg, _ env) (page, outcome, bool) {
func (p allocationsPage) take(msg tea.Msg, e env) (page, outcome, bool) {
allocs, ok := msg.(allocsMsg)
if !ok {
return p, outcome{}, false
}

p.allocs, p.read = allocs, time.Now()
p.allocs, p.read = allocs, e.now

return p, outcome{}, true
}
Expand Down
12 changes: 12 additions & 0 deletions internal/ui/allocfaults_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,18 @@ func TestAllocFaults_RestartingWithinTheHour(t *testing.T) {
r.Equal([]string{"recent"}, page.ids(env{}))
}

func TestAllocFaults_JudgedAtTheTimeOfTheModelWhenRead(t *testing.T) {
r := require.New(t)

then := fixedTime()
restarted := nomad.Task{Name: "server", Restarts: 1, LastRestart: then.Add(-10 * time.Minute)}
allocs := allocsMsg{allocFaultsAlloc("recent", statusRunning, restarted)}

page, _, ok := allocationsPage{fault: faultRestarting}.take(allocs, env{now: then})
r.True(ok)
r.Equal([]string{"recent"}, page.(allocationsPage).ids(env{}))
}

func TestAllocFaults_KilledForMemoryWithinTheHour(t *testing.T) {
r := require.New(t)

Expand Down
2 changes: 1 addition & 1 deletion internal/ui/allocrows_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,7 @@ func TestAllocations_ARecentRestartOrOOMNeedsAttention(t *testing.T) {
rows := allocRows(troubledAllocs(), nil, time.Now())

// A restart or an OOM kill of the last hour is a task in trouble now:
// red, as k9s paints a pod that crashes.
// red.
r.Equal(colorDead, rows[0].color)
r.Equal(colorDead, rows[1].color)

Expand Down
2 changes: 2 additions & 0 deletions internal/ui/app_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,7 @@ type fakeClient struct {
queueStatus map[string][]nomad.QueueTenant
queueErr error
queueReads int
queuePools []string
queueStatusErr error
poolsErr error
servers []nomad.Server
Expand Down Expand Up @@ -1342,6 +1343,7 @@ func (f *fakeClient) PoolNodes(_ context.Context, pool string) ([]nomad.Node, er

func (f *fakeClient) BatchQueueJobs(ctx context.Context, pool string) ([]nomad.QueueWorkload, error) {
f.queueReads++
f.queuePools = append(f.queuePools, pool)

err := f.err
if f.queueErr != nil {
Expand Down
2 changes: 1 addition & 1 deletion internal/ui/client.go
Original file line number Diff line number Diff line change
Expand Up @@ -155,7 +155,7 @@ func (p clientPage) fetch(e env) tea.Cmd {
client, nodeID := e.client, p.nodeID

cmds := []tea.Cmd{
fetchList(func(ctx context.Context) ([]nomad.Alloc, error) {
request(func(ctx context.Context) ([]nomad.Alloc, error) {
return client.NodeAllocations(ctx, nodeID)
}, func(items []nomad.Alloc) tea.Msg { return allocsMsg(items) }),
fetchHost(client, nodeID),
Expand Down
2 changes: 1 addition & 1 deletion internal/ui/clientdetail.go
Original file line number Diff line number Diff line change
Expand Up @@ -234,7 +234,7 @@ func (nodeMetaPage) topics() []string { return nil }
func (p nodeMetaPage) fetch(e env) tea.Cmd {
client, nodeID := e.client, p.nodeID

return fetchList(func(ctx context.Context) ([]nomad.MetaEntry, error) {
return request(func(ctx context.Context) ([]nomad.MetaEntry, error) {
return client.NodeMeta(ctx, nodeID)
}, func(meta []nomad.MetaEntry) tea.Msg { return nodeMetaMsg{nodeID: nodeID, meta: meta} })
}
Expand Down
13 changes: 6 additions & 7 deletions internal/ui/clock_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -76,13 +76,12 @@ func TestRender_APageCountsFromTheTimeOfTheModel(t *testing.T) {
// time is part of what changes. Everything else counts from the model.
var clockReaders = map[string]bool{
// update and what it calls when a message comes in
"Model.update": true,
"Model.flashed": true,
"allocationsPage.take": true,
"logState.take": true,
"jobLogsPage.lines": true,
"jobLogsPage.chunk": true,
"saveName": true,
"Model.update": true,
"Model.flashed": true,
"logState.take": true,
"jobLogsPage.lines": true,
"jobLogsPage.chunk": true,
"saveName": true,

// commands
"readPart": true,
Expand Down
2 changes: 1 addition & 1 deletion internal/ui/clusters.go
Original file line number Diff line number Diff line change
Expand Up @@ -157,7 +157,7 @@ func (m Model) askAboutTheCluster() tea.Cmd {
m.onConnection(tea.Batch(
fetchAgent(client),
fetchToken(client),
fetchList(client.Namespaces, func(items []nomad.Namespace) tea.Msg { return namespacesMsg(items) }),
request(client.Namespaces, func(items []nomad.Namespace) tea.Msg { return namespacesMsg(items) }),
fetchRegions(client),
fetchDatacenters(client),
)),
Expand Down
4 changes: 2 additions & 2 deletions internal/ui/deployment.go
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@ func (deploymentsPage) topics() []string { return []string{nomad.TopicDeployment
func (deploymentsPage) fetch(e env) tea.Cmd {
client, namespace := e.client, e.namespace

return fetchList(func(ctx context.Context) ([]nomad.Deployment, error) {
return request(func(ctx context.Context) ([]nomad.Deployment, error) {
return client.Deployments(ctx, namespace)
}, func(items []nomad.Deployment) tea.Msg { return deploymentsMsg(items) })
}
Expand Down Expand Up @@ -287,7 +287,7 @@ func (p deploymentPage) fetch(e env) tea.Cmd {
client, namespace, id := e.client, p.namespace, p.deploymentID

return tea.Batch(
fetchList(func(ctx context.Context) ([]nomad.Alloc, error) {
request(func(ctx context.Context) ([]nomad.Alloc, error) {
return client.DeploymentAllocations(ctx, namespace, id)
}, func(items []nomad.Alloc) tea.Msg { return allocsMsg(items) }),
fetchDeployment(client, namespace, id),
Expand Down
2 changes: 1 addition & 1 deletion internal/ui/evaluation.go
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ func (evaluationsPage) topics() []string { return []string{nomad.TopicEvaluation
func (evaluationsPage) fetch(e env) tea.Cmd {
client, namespace := e.client, e.namespace

return fetchList(func(ctx context.Context) ([]nomad.Evaluation, error) {
return request(func(ctx context.Context) ([]nomad.Evaluation, error) {
return client.Evaluations(ctx, namespace)
}, func(items []nomad.Evaluation) tea.Msg { return evaluationsMsg(items) })
}
Expand Down
2 changes: 1 addition & 1 deletion internal/ui/files.go
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@ func (filesPage) topics() []string { return nil }
func (p filesPage) fetch(e env) tea.Cmd {
client, namespace, allocID, dir := e.client, p.namespace, p.allocID, p.path

return fetchList(func(ctx context.Context) ([]nomad.File, error) {
return request(func(ctx context.Context) ([]nomad.File, error) {
return client.Files(ctx, namespace, allocID, dir)
}, func(files []nomad.File) tea.Msg { return filesMsg(files) })
}
Expand Down
4 changes: 2 additions & 2 deletions internal/ui/instances.go
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@ func (servicesPage) topics() []string { return []string{nomad.TopicService} }
func (servicesPage) fetch(e env) tea.Cmd {
client, namespace := e.client, e.namespace

return fetchList(func(ctx context.Context) ([]nomad.Service, error) {
return request(func(ctx context.Context) ([]nomad.Service, error) {
return client.Services(ctx, namespace)
}, func(items []nomad.Service) tea.Msg { return servicesMsg(items) })
}
Expand Down Expand Up @@ -142,7 +142,7 @@ func (p serviceInstancesPage) where(env) string { return p.namespace }
func (p serviceInstancesPage) fetch(e env) tea.Cmd {
client, namespace, service := e.client, p.namespace, p.service

return fetchList(func(ctx context.Context) ([]nomad.ServiceInstance, error) {
return request(func(ctx context.Context) ([]nomad.ServiceInstance, error) {
return client.ServiceInstances(ctx, namespace, service)
}, func(items []nomad.ServiceInstance) tea.Msg { return instancesMsg(items) })
}
Expand Down
54 changes: 24 additions & 30 deletions internal/ui/jobs.go
Original file line number Diff line number Diff line change
Expand Up @@ -89,7 +89,7 @@ func (jobsPage) topics() []string { return []string{nomad.TopicJob} }
func (p jobsPage) fetch(e env) tea.Cmd {
client, namespace, pool := e.client, p.where(e), p.pool

jobs := fetchList(func(ctx context.Context) ([]nomad.Job, error) {
jobs := request(func(ctx context.Context) ([]nomad.Job, error) {
if pool != "" {
return client.PoolJobs(ctx, pool)
}
Expand Down Expand Up @@ -319,51 +319,45 @@ func fetchBatchQueue(client Client, pool string) tea.Cmd {
}

// batchQueue reads the queue of the pool, or of every pool that has one when
// no pool is given. It stops at the first pool that fails.
// no pool is given. A pool that fails fails the whole read.
func batchQueue(ctx context.Context, client Client, pool string) (map[string]nomad.QueueWorkload, error) {
names := []string{pool}

if pool == "" {
pools, err := client.NodePools(ctx)
if err != nil {
return nil, err
}

names = nil
for _, p := range pools {
if p.Queue != "" {
names = append(names, p.Name)
}
}
items, err := queuedIn(ctx, client, pool)
if err != nil {
return nil, err
}

workloads := map[string]nomad.QueueWorkload{}
for _, w := range items {
markWorkload(workloads, w)
}

for _, name := range names {
list, err := client.BatchQueueJobs(ctx, name)
if err != nil {
return nil, err
}
return workloads, nil
}

for _, w := range list {
markWorkload(workloads, w)
}
func queuedIn(ctx context.Context, client Client, pool string) ([]nomad.QueueWorkload, error) {
if pool != "" {
return client.BatchQueueJobs(ctx, pool)
}

return workloads, nil
q, err := readPoolQueues(ctx, client)
if err != nil {
return nil, err
}

return q.items, q.failed
}

func markWorkload(m map[string]nomad.QueueWorkload, w nomad.QueueWorkload) {
if w.Namespace != "" {
m[w.Namespace+"/"+w.JobID] = w
m[workloadMark(w)] = w
}
m[w.JobID] = w
}

// jobColor shows the state of a job without reading the row, as k9s shows a
// pod: what waits for room is orange, a service not all up yet is red, what
// was stopped and still runs is purple, a dead one is red, a batch job that
// ended gets the spent color, not the dead one, unless a group of it failed.
// jobColor shows the state of a job without reading the row: what waits for
// room is orange, a service not all up yet is red, what was stopped and still
// runs is purple, a dead one is red, a batch job that ended gets the spent
// color, not the dead one, unless a group of it failed.
func jobColor(job nomad.Job) color.Color {
switch job.Status {
case statusRunning:
Expand Down
6 changes: 3 additions & 3 deletions internal/ui/logs.go
Original file line number Diff line number Diff line change
Expand Up @@ -448,9 +448,9 @@ func (m Model) logToggles(width int) string {
press, name string
on bool
}{
{"s", "Autoscroll", m.text.following},
{"t", "Timestamps", m.text.times},
{"w", "Wrap", m.text.wrap},
{followPress, "Autoscroll", m.text.following},
{timesPress, "Timestamps", m.text.times},
{wrapPress, "Wrap", m.text.wrap},
}

cells := make([]string, 0, len(toggles))
Expand Down
10 changes: 10 additions & 0 deletions internal/ui/logs_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -739,3 +739,13 @@ func TestLogs_ASwitchOfTheSessionLeavesTheLogAsItIs(t *testing.T) {
r.True(m.text.wrap)
r.False(client.logsClosed, "the log was closed to be read again")
}

// The toggle line asks for the keys by their letters: a key that took another
// letter would leave its toggle off the line.
func TestLogToggles_AskForTheKeysOfTheToggles(t *testing.T) {
r := require.New(t)

r.Equal(followPress, followKey[logsPage]().press)
r.Equal(timesPress, timesKey[logsPage]().press)
r.Equal(wrapPress, wrapKey[logsPage]().press)
}
4 changes: 4 additions & 0 deletions internal/ui/mark.go
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,10 @@ func allocLabel(allocs []nomad.Alloc) string {
// the thing it was put on is gone.
func jobMark(job nomad.Job) string { return job.Namespace + "/" + job.ID }

func workloadMark(w nomad.QueueWorkload) string {
return jobMark(nomad.Job{Namespace: w.Namespace, ID: w.JobID})
}

func allocMark(alloc nomad.Alloc) string { return alloc.ID }

func nodeMark(node nomad.Node) string { return node.ID }
Expand Down
2 changes: 1 addition & 1 deletion internal/ui/namespaces.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ func (namespacesPage) topics() []string { return nil }
func (p namespacesPage) take(tea.Msg, env) (page, outcome, bool) { return p, outcome{}, false }

func (namespacesPage) fetch(e env) tea.Cmd {
return fetchList(e.client.Namespaces, func(items []nomad.Namespace) tea.Msg { return namespacesMsg(items) })
return request(e.client.Namespaces, func(items []nomad.Namespace) tea.Msg { return namespacesMsg(items) })
}

func (namespacesPage) ids(e env) []string { return names(e.namespaces, namespaceMark) }
Expand Down
2 changes: 1 addition & 1 deletion internal/ui/namespacetasks.go
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ func (namespaceTasksPage) topics() []string { return []string{nomad.TopicAllocat
func (namespaceTasksPage) fetch(e env) tea.Cmd {
client, namespace := e.client, e.namespace

return fetchList(func(ctx context.Context) ([]nomad.Alloc, error) {
return request(func(ctx context.Context) ([]nomad.Alloc, error) {
return client.Allocations(ctx, namespace, "")
}, func(items []nomad.Alloc) tea.Msg { return allocsMsg(items) })
}
Expand Down
2 changes: 1 addition & 1 deletion internal/ui/node.go
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,7 @@ func (nodesPage) fewerTopics() []string { return []string{nomad.TopicNode} }
func (p nodesPage) fetch(e env) tea.Cmd {
client, pool := e.client, p.pool

return fetchList(func(ctx context.Context) ([]nomad.Node, error) {
return request(func(ctx context.Context) ([]nomad.Node, error) {
var (
nodes []nomad.Node
err error
Expand Down
2 changes: 1 addition & 1 deletion internal/ui/nodepools.go
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ func (nodePoolsPage) titles() []string { return nodePoolTitles }
func (nodePoolsPage) topics() []string { return []string{nomad.TopicNodePool} }

func (nodePoolsPage) fetch(e env) tea.Cmd {
return fetchList(e.client.NodePools, func(items []nomad.NodePool) tea.Msg { return nodePoolsMsg(items) })
return request(e.client.NodePools, func(items []nomad.NodePool) tea.Msg { return nodePoolsMsg(items) })
}

func (p nodePoolsPage) take(msg tea.Msg, _ env) (page, outcome, bool) {
Expand Down
15 changes: 12 additions & 3 deletions internal/ui/page.go
Original file line number Diff line number Diff line change
Expand Up @@ -424,7 +424,14 @@ func findWordKeys[P page]() []pageKey[P] {
}
}

func wrapKey[P page]() pageKey[P] { return windowKey[P]("w", "Toggle Wrap", wrapMsg{}) }
// The keys of the toggles of the window, which its toggle line names too.
const (
followPress = "s"
timesPress = "t"
wrapPress = "w"
)

func wrapKey[P page]() pageKey[P] { return windowKey[P](wrapPress, "Toggle Wrap", wrapMsg{}) }

func saveKey[P page]() pageKey[P] { return windowKey[P]("ctrl+s", "Save", saveMsg{}) }

Expand All @@ -433,9 +440,11 @@ func copyTextKey[P page]() pageKey[P] { return windowKey[P]("c", "Copy", copyTex
// followKey follows the end of what a stream writes, or stops following it
// where the view is; timesKey shows or hides the time each line of it
// arrived in front of the line.
func followKey[P page]() pageKey[P] { return windowKey[P]("s", "Toggle Autoscroll", followMsg{}) }
func followKey[P page]() pageKey[P] {
return windowKey[P](followPress, "Toggle Autoscroll", followMsg{})
}

func timesKey[P page]() pageKey[P] { return windowKey[P]("t", "Toggle Timestamps", timesMsg{}) }
func timesKey[P page]() pageKey[P] { return windowKey[P](timesPress, "Toggle Timestamps", timesMsg{}) }

// windowKey is a key of the window over a text, which the root holds.
func windowKey[P page](press, label string, msg tea.Msg) pageKey[P] {
Expand Down
2 changes: 1 addition & 1 deletion internal/ui/plugins.go
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ func (pluginsPage) titles() []string { return pluginsTitles }
func (pluginsPage) topics() []string { return []string{nomad.TopicCSIPlugin} }

func (pluginsPage) fetch(e env) tea.Cmd {
return fetchList(e.client.Plugins, func(items []nomad.Plugin) tea.Msg { return pluginsMsg(items) })
return request(e.client.Plugins, func(items []nomad.Plugin) tea.Msg { return pluginsMsg(items) })
}

func (p pluginsPage) take(msg tea.Msg, _ env) (page, outcome, bool) {
Expand Down
Loading
Loading