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
16 changes: 16 additions & 0 deletions internal/nomad/client.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,22 @@ const AllNamespaces = "*"
// DefaultNamespace is the namespace every cluster has, and keeps.
const DefaultNamespace = "default"

// EveryNamespace says the namespace stands for all of them: none named, or
// AllNamespaces.
func EveryNamespace(namespace string) bool {
return namespace == "" || namespace == AllNamespaces
}

// TargetNamespace is where something new goes from a session in namespace:
// that namespace, or DefaultNamespace when the session looks at all of them.
func TargetNamespace(namespace string) string {
if EveryNamespace(namespace) {
return DefaultNamespace
}

return namespace
}

// Config says which cluster to talk to. An empty address or region falls
// back to the environment, the same variables the nomad command reads,
// which is also where the token comes from.
Expand Down
12 changes: 12 additions & 0 deletions internal/nomad/client_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -297,3 +297,15 @@ func TestNew_TheCertificatesOfANamedCluster(t *testing.T) {
})
require.ErrorContains(t, err, "missing-ca.pem")
}

func TestEveryNamespace(t *testing.T) {
for namespace, want := range map[string]bool{"": true, "*": true, "default": false, "web": false} {
require.Equal(t, want, nomad.EveryNamespace(namespace), "namespace %q", namespace)
}
}

func TestTargetNamespace(t *testing.T) {
for namespace, want := range map[string]string{"": "default", "*": "default", "default": "default", "web": "web"} {
require.Equal(t, want, nomad.TargetNamespace(namespace), "namespace %q", namespace)
}
}
9 changes: 1 addition & 8 deletions internal/nomad/deployments.go
Original file line number Diff line number Diff line change
Expand Up @@ -81,14 +81,7 @@ func (c *Client) JobDeployments(ctx context.Context, namespace, jobID string) ([
}

func newDeploymentDetail(d *api.Deployment) DeploymentDetail {
detail := DeploymentDetail{Deployment: Deployment{
ID: d.ID,
JobID: d.JobID,
Namespace: d.Namespace,
JobVersion: d.JobVersion,
Status: d.Status,
StatusDescription: d.StatusDescription,
}}
detail := DeploymentDetail{Deployment: newDeployment(d)}

for name, state := range d.TaskGroups {
if state == nil {
Expand Down
6 changes: 2 additions & 4 deletions internal/nomad/edit.go
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ func (c *Client) jobOf(ctx context.Context, namespace, source string, vars JobVa
return nil, nil, nil, err
}

if namespace != "" && namespace != AllNamespaces {
if !EveryNamespace(namespace) {
job.Namespace = &namespace
}

Expand Down Expand Up @@ -339,9 +339,7 @@ type JobTarget struct {
// file names, or else the one of the session, and default when the session
// looks at all of them.
func (c *Client) JobTarget(ctx context.Context, namespace, source string) (JobTarget, error) {
if namespace == "" || namespace == AllNamespaces {
namespace = DefaultNamespace
}
namespace = TargetNamespace(namespace)

job, _, err := c.parseJob(ctx, namespace, source, JobVariables{}, false)
if err != nil {
Expand Down
22 changes: 9 additions & 13 deletions internal/nomad/plugin.go
Original file line number Diff line number Diff line change
Expand Up @@ -50,21 +50,17 @@ type PluginInstance struct {
// Plugins lists the CSI plugins of the cluster.
func (c *Client) Plugins(ctx context.Context) ([]Plugin, error) {
list, _, err := c.api.CSIPlugins().List(c.query(ctx, ""))
if err != nil {
return nil, err
}

out := make([]Plugin, 0, len(list))
for _, p := range list {
out = append(out, Plugin{
ID: p.ID,
Provider: p.Provider,
Controllers: Health{Healthy: p.ControllersHealthy, Expected: p.ControllersExpected},
Nodes: Health{Healthy: p.NodesHealthy, Expected: p.NodesExpected},
})
}
return listed(list, err, newPlugin)
}

return out, nil
func newPlugin(p *api.CSIPluginListStub) Plugin {
return Plugin{
ID: p.ID,
Provider: p.Provider,
Controllers: Health{Healthy: p.ControllersHealthy, Expected: p.ControllersExpected},
Nodes: Health{Healthy: p.NodesHealthy, Expected: p.NodesExpected},
}
}

// Plugin reads one CSI plugin.
Expand Down
76 changes: 24 additions & 52 deletions internal/nomad/resources.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,23 +20,19 @@ type Deployment struct {
// Deployments lists the rollouts of a namespace.
func (c *Client) Deployments(ctx context.Context, namespace string) ([]Deployment, error) {
list, _, err := c.api.Deployments().List(c.query(ctx, namespace))
if err != nil {
return nil, err
}

out := make([]Deployment, 0, len(list))
for _, d := range list {
out = append(out, Deployment{
ID: d.ID,
JobID: d.JobID,
Namespace: d.Namespace,
JobVersion: d.JobVersion,
Status: d.Status,
StatusDescription: d.StatusDescription,
})
}
return listed(list, err, newDeployment)
}

return out, nil
func newDeployment(d *api.Deployment) Deployment {
return Deployment{
ID: d.ID,
JobID: d.JobID,
Namespace: d.Namespace,
JobVersion: d.JobVersion,
Status: d.Status,
StatusDescription: d.StatusDescription,
}
}

// Namespace is one namespace of the cluster.
Expand All @@ -49,16 +45,12 @@ type Namespace struct {
// Namespaces lists the namespaces of the cluster.
func (c *Client) Namespaces(ctx context.Context) ([]Namespace, error) {
list, _, err := c.api.Namespaces().List(c.query(ctx, ""))
if err != nil {
return nil, err
}

out := make([]Namespace, 0, len(list))
for _, n := range list {
out = append(out, Namespace{Name: n.Name, Description: n.Description, Quota: n.Quota})
}
return listed(list, err, newNamespace)
}

return out, nil
func newNamespace(n *api.Namespace) Namespace {
return Namespace{Name: n.Name, Description: n.Description, Quota: n.Quota}
}

// Service is one name in the service catalog.
Expand Down Expand Up @@ -104,16 +96,8 @@ type Evaluation struct {
// Evaluations lists the scheduling decisions of a namespace.
func (c *Client) Evaluations(ctx context.Context, namespace string) ([]Evaluation, error) {
list, _, err := c.api.Evaluations().List(c.query(ctx, namespace))
if err != nil {
return nil, err
}

out := make([]Evaluation, 0, len(list))
for _, e := range list {
out = append(out, newEvaluation(e))
}

return out, nil
return listed(list, err, newEvaluation)
}

func newEvaluation(e *api.Evaluation) Evaluation {
Expand Down Expand Up @@ -208,16 +192,8 @@ type VariableDetail struct {
// Variables lists what the variable store of a namespace holds.
func (c *Client) Variables(ctx context.Context, namespace string) ([]Variable, error) {
list, _, err := c.api.Variables().List(c.query(ctx, namespace))
if err != nil {
return nil, err
}

out := make([]Variable, 0, len(list))
for _, v := range list {
out = append(out, variableOf(v))
}

return out, nil
return listed(list, err, variableOf)
}

// Variable reads a variable with its values.
Expand Down Expand Up @@ -264,22 +240,18 @@ type NodePool struct {
// NodePools lists the pools nodes are grouped into.
func (c *Client) NodePools(ctx context.Context) ([]NodePool, error) {
list, _, err := c.api.NodePools().List(c.query(ctx, ""))
if err != nil {
return nil, err
}

out := make([]NodePool, 0, len(list))
for _, p := range list {
pool := NodePool{Name: p.Name, Description: p.Description}
return listed(list, err, newNodePool)
}

if p.SchedulerConfiguration != nil {
pool.Scheduler = string(p.SchedulerConfiguration.SchedulerAlgorithm)
}
func newNodePool(p *api.NodePool) NodePool {
pool := NodePool{Name: p.Name, Description: p.Description}

out = append(out, pool)
if p.SchedulerConfiguration != nil {
pool.Scheduler = string(p.SchedulerConfiguration.SchedulerAlgorithm)
}

return out, nil
return pool
}

// PoolNodes lists the clients of a node pool, as the cluster counts them:
Expand Down
27 changes: 20 additions & 7 deletions internal/nomad/resources_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -76,10 +76,10 @@ func TestDeployments_Read(t *testing.T) {
r.NoError(err)
r.Len(deployments, 1)

r.Equal("dep-1", deployments[0].ID)
r.Equal("web", deployments[0].JobID)
r.Equal("running", deployments[0].Status)
r.Equal(uint64(3), deployments[0].JobVersion)
r.Equal(nomad.Deployment{
ID: "dep-1", JobID: "web", Namespace: "production", JobVersion: 3,
Status: "running", StatusDescription: "Deployment is running",
}, deployments[0])
}

func TestNamespaces_Read(t *testing.T) {
Expand Down Expand Up @@ -121,9 +121,11 @@ func TestEvaluations_Read(t *testing.T) {
r.NoError(err)
r.Len(evals, 1)

r.Equal("eval-1", evals[0].ID)
r.Equal("job-register", evals[0].TriggeredBy)
r.Equal("complete", evals[0].Status)
r.Equal(nomad.Evaluation{
ID: "eval-1", JobID: "web", Namespace: "production", Type: "service",
TriggeredBy: "job-register", Status: "complete",
Created: evals[0].Created,
}, evals[0])
r.False(evals[0].Created.IsZero())
}

Expand Down Expand Up @@ -175,6 +177,7 @@ func TestVariables_Read(t *testing.T) {
r.Len(variables, 1)

r.Equal("nomad/jobs/web", variables[0].Path)
r.Equal("production", variables[0].Namespace)

// The index a delete gives, so that a variable changed since is kept.
r.Equal(uint64(769), variables[0].Index)
Expand Down Expand Up @@ -226,6 +229,16 @@ func TestNodePools_Read(t *testing.T) {
r.Equal("binpack", pools[0].Scheduler)
}

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

client, _ := recorder(t, `[{"Name": "gpu", "Description": "gpus"}]`)

pools, err := client.NodePools(context.Background())
r.NoError(err)
r.Equal([]nomad.NodePool{{Name: "gpu", Description: "gpus"}}, pools)
}

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

Expand Down
28 changes: 19 additions & 9 deletions internal/ui/allocpanel.go
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,23 @@ func openFollowUp(p tasksPage, e env) (tasksPage, outcome) {
// field is a label and its value on a line of a panel.
type field struct{ label, value string }

// panelRow starts a row of a panel where the columns of the table do.
func panelRow(row string) string { return strings.Repeat(" ", tableIndent) + row }

// fieldLineRows are the rows of a panel for its lines of fields, cut to the
// width left after the margin. A line where no field has a value is left out.
func fieldLineRows(lines [][]field, width int) []string {
rows := []string{}

for _, line := range lines {
if row := fieldLine(line, width-tableIndent); row != "" {
rows = append(rows, panelRow(row))
}
}

return rows
}

// allocPanel is what the tasks of an allocation show above them: its status,
// client, job version and deployment health, its ports, the allocations
// before and after it, and its checks. A line with no values is left out,
Expand Down Expand Up @@ -112,14 +129,7 @@ func allocPanel(alloc nomad.Alloc, checks []nomad.Check, width, room int) []stri
},
}

rows := []string{}

for _, line := range lines {
if row := fieldLine(line, width-1); row != "" {
// The panel starts where the columns of the table do.
rows = append(rows, " "+row)
}
}
rows := fieldLineRows(lines, width)

return fitPanel(rows, checkBlock(checks, width), room)
}
Expand Down Expand Up @@ -224,7 +234,7 @@ func checkBlock(checks []nomad.Check, width int) panelBlock {
}

return panelBlock{
title: []string{" " + styleLabel.Render("Checks")},
title: []string{panelRow(styleLabel.Render("Checks"))},
entries: entries,
more: func(count int) string { return fmt.Sprintf(" + %d more checks", count) },
}
Expand Down
23 changes: 23 additions & 0 deletions internal/ui/allocpanel_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import (
"testing"

tea "charm.land/bubbletea/v2"
"charm.land/lipgloss/v2"
"github.com/stretchr/testify/require"

"github.com/ingvarch/urga/internal/nomad"
Expand Down Expand Up @@ -239,3 +240,25 @@ func TestFitPanel_LeavesTheHeadItWasGiven(t *testing.T) {
r.Equal([]string{"status", "kept"}, backing)
r.Equal("status", rows[0])
}

func TestPanelRow_StartsWhereTheTableDoes(t *testing.T) {
require.Equal(t, strings.Repeat(" ", tableIndent)+"row", panelRow("row"))
}

func TestFieldLineRows(t *testing.T) {
lines := [][]field{
{{"Status", "running"}},
{{"Ports", ""}},
{{"Client", "node-1"}},
}

rows := fieldLineRows(lines, 40)

require.Len(t, rows, 2, "a line with no value is left out")
require.Equal(t, panelRow(fieldLine(lines[0], 40-tableIndent)), rows[0])
require.Equal(t, panelRow(fieldLine(lines[2], 40-tableIndent)), rows[1])

for _, row := range fieldLineRows(lines, 8) {
require.LessOrEqual(t, lipgloss.Width(row), 8)
}
}
Loading
Loading