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
3 changes: 2 additions & 1 deletion go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,8 @@ require (
github.com/nais/liberator v0.0.0-20250924103433-536eaed90405
github.com/nais/naistrix v0.6.0
github.com/pelletier/go-toml/v2 v2.2.4
github.com/pterm/pterm v0.12.81
github.com/pterm/pterm v0.12.82
github.com/savioxavier/termlink v1.4.3
github.com/sethvargo/go-retry v0.3.0
github.com/stretchr/testify v1.11.1
github.com/suessflorian/gqlfetch v0.7.0
Expand Down
6 changes: 4 additions & 2 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -421,8 +421,8 @@ github.com/pterm/pterm v0.12.31/go.mod h1:32ZAWZVXD7ZfG0s8qqHXePte42kdz8ECtRyEej
github.com/pterm/pterm v0.12.33/go.mod h1:x+h2uL+n7CP/rel9+bImHD5lF3nM9vJj80k9ybiiTTE=
github.com/pterm/pterm v0.12.36/go.mod h1:NjiL09hFhT/vWjQHSj1athJpx6H8cjpHXNAK5bUw8T8=
github.com/pterm/pterm v0.12.40/go.mod h1:ffwPLwlbXxP+rxT0GsgDTzS3y3rmpAO1NMjUkGTYf8s=
github.com/pterm/pterm v0.12.81 h1:ju+j5I2++FO1jBKMmscgh5h5DPFDFMB7epEjSoKehKA=
github.com/pterm/pterm v0.12.81/go.mod h1:TyuyrPjnxfwP+ccJdBTeWHtd/e0ybQHkOS/TakajZCw=
github.com/pterm/pterm v0.12.82 h1:+D9wYhCaeaK0FIQoZtqbNQuNpe2lB2tajKKsTd5paVQ=
github.com/pterm/pterm v0.12.82/go.mod h1:TyuyrPjnxfwP+ccJdBTeWHtd/e0ybQHkOS/TakajZCw=
github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc=
github.com/rivo/uniseg v0.4.7 h1:WUdvkW8uEhrYfLC4ZzdpI2ztxP1I582+49Oc5Mq64VQ=
github.com/rivo/uniseg v0.4.7/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUcx88=
Expand All @@ -434,6 +434,8 @@ github.com/russross/blackfriday/v2 v2.1.0 h1:JIOH55/0cWyOuilr9/qlrm0BSXldqnqwMsf
github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
github.com/sassoftware/go-rpmutils v0.4.0 h1:ojND82NYBxgwrV+mX1CWsd5QJvvEZTKddtCdFLPWhpg=
github.com/sassoftware/go-rpmutils v0.4.0/go.mod h1:3goNWi7PGAT3/dlql2lv3+MSN5jNYPjT5mVcQcIsYzI=
github.com/savioxavier/termlink v1.4.3 h1:Gh6vrG7jSn21cRiYdQqFXYcdXfM+Fg14aG487JTfKpA=
github.com/savioxavier/termlink v1.4.3/go.mod h1:5T5ePUlWbxCHIwyF8/Ez1qufOoGM89RCg9NvG+3G3gc=
github.com/securego/gosec/v2 v2.22.9 h1:njwnorLl1pJMkwaymi1iyWDy8xeaVUByW4oteJzYNHc=
github.com/securego/gosec/v2 v2.22.9/go.mod h1:x3qEF4J5bkDFIm8siAwsYZ40Uu5tD4JWpfVDPx3P3+0=
github.com/segmentio/asm v1.2.0 h1:9BQrFxC+YOHJlTlHGkTrFWf59nbL3XnCoFLTwDCI7ys=
Expand Down
36 changes: 26 additions & 10 deletions internal/naisapi/command/status.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,18 @@ import (
"github.com/nais/cli/internal/naisapi/gql"
"github.com/nais/naistrix"
"github.com/nais/naistrix/output"
"github.com/savioxavier/termlink"
)

type team struct {
Slug string `json:"slug"`
Url string `json:"url"`
}

func (t team) String() string {
return termlink.Link(t.Slug, t.Url)
}

type workload struct {
Kind string `json:"kind"`
Name string `json:"name"`
Expand All @@ -37,9 +47,8 @@ func (f workloadsWithIssues) String() string {
return strings.TrimRight(issues, "\n")
}

type team struct {
// TODO: Once https://github.com/pterm/pterm/issues/697 is resolved, we can use a link to Console instead of just the slug.
Slug string `json:"slug"`
type statusEntry struct {
Team team `json:"team"`
Workloads int `json:"workloads"`
NotNais int `heading:"Not Nais" json:"notNais"`
Issues workloadsWithIssues `heading:"Critical Issues" json:"failing"`
Expand All @@ -52,13 +61,17 @@ func statusCommand(parentFlags *flag.Api) *naistrix.Command {
Title: "Get a quick overview of the status of your teams.",
Flags: flags,
RunFunc: func(ctx context.Context, out naistrix.Output, _ []string) error {
var teams []team
user, err := naisapi.GetAuthenticatedUser(ctx)
if err != nil {
return err
}

ret, err := naisapi.GetStatus(ctx, flags)
if err != nil {
return err
}

var entries []statusEntry
for _, t := range ret {
workloadsWithCriticalIssues := make([]gql.TeamStatusMeUserTeamsTeamMemberConnectionNodesTeamMemberTeamWorkloadsWorkloadConnectionNodesWorkload, 0)
for _, w := range t.Team.Workloads.Nodes {
Expand All @@ -67,8 +80,11 @@ func statusCommand(parentFlags *flag.Api) *naistrix.Command {
}
}

n := team{
Slug: t.Team.Slug,
n := statusEntry{
Team: team{
Slug: t.Team.Slug,
Url: fmt.Sprintf("https://%s/team/%s", user.ConsoleHost(), t.Team.Slug),
},
Workloads: t.Team.Workloads.PageInfo.TotalCount,
NotNais: len(workloadsWithCriticalIssues),
Issues: make(workloadsWithIssues, 0),
Expand All @@ -84,19 +100,19 @@ func statusCommand(parentFlags *flag.Api) *naistrix.Command {
}
n.Issues = append(n.Issues, a)
}
teams = append(teams, n)
entries = append(entries, n)
}

if len(teams) == 0 {
if len(entries) == 0 {
out.Println("No teams found.")
return nil
}

if flags.Output == "json" {
return out.JSON(output.JSONWithPrettyOutput()).Render(teams)
return out.JSON(output.JSONWithPrettyOutput()).Render(entries)
}

return out.Table().Render(teams)
return out.Table().Render(entries)
},
}
}
Expand Down
56 changes: 43 additions & 13 deletions internal/naisapi/command/team.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,16 +2,27 @@ package command

import (
"context"
"fmt"
"strings"

"github.com/nais/cli/internal/naisapi"
"github.com/nais/cli/internal/naisapi/command/flag"
"github.com/nais/cli/internal/naisapi/gql"
"github.com/nais/naistrix"
"github.com/nais/naistrix/output"
"github.com/savioxavier/termlink"
"k8s.io/utils/strings/slices"
)

type teamWorkload struct {
Name string `json:"name"`
Url string `json:"url"`
}

func (tw teamWorkload) String() string {
return termlink.Link(tw.Name, tw.Url)
}

func teamCommand(parentFlags *flag.Api) *naistrix.Command {
flags := &flag.Team{Api: parentFlags}
return &naistrix.Command{
Expand Down Expand Up @@ -282,14 +293,18 @@ func listWorkloads(parentFlags *flag.Team) *naistrix.Command {
},
Flags: flags,
RunFunc: func(ctx context.Context, out naistrix.Output, args []string) error {
// TODO: Once pterm/pterm#697 is resolved, we can use a link to Console instead of just the workload name.
type workload struct {
Name string `json:"name"`
Environment string `json:"environment"`
Type string `json:"type"`
State string `json:"state"`
Vulnerabilities int `json:"vulnerabilities"`
Issues int `heading:"Critical Issues" json:"issues"`
user, err := naisapi.GetAuthenticatedUser(ctx)
if err != nil {
return err
}

type entry struct {
Workload teamWorkload `json:"workload"`
Environment string `json:"environment"`
Type string `json:"type"`
State string `json:"state"`
Vulnerabilities int `json:"vulnerabilities"`
Issues int `heading:"Critical Issues" json:"issues"`
}

teamSlug := args[0]
Expand All @@ -298,7 +313,7 @@ func listWorkloads(parentFlags *flag.Team) *naistrix.Command {
return err
}

workloads := make([]workload, len(ret))
entries := make([]entry, len(ret))
for i, w := range ret {
state := "(unknown)"
switch actual := w.(type) {
Expand All @@ -308,8 +323,23 @@ func listWorkloads(parentFlags *flag.Team) *naistrix.Command {
state = string(actual.GetJobState())
}

workloads[i] = workload{
Name: w.GetName(),
workloadType := "app"
if w.GetTypename() == "Job" {
workloadType = "job"
}

entries[i] = entry{
Workload: teamWorkload{
Name: w.GetName(),
Url: fmt.Sprintf(
"https://%s/team/%s/%s/%s/%s",
user.ConsoleHost(),
teamSlug,
w.GetTeamEnvironment().Environment.Name,
workloadType,
w.GetName(),
),
},
Environment: w.GetTeamEnvironment().Environment.Name,
Type: w.GetTypename(),
State: state,
Expand All @@ -319,15 +349,15 @@ func listWorkloads(parentFlags *flag.Team) *naistrix.Command {
}

if flags.Output == "json" {
return out.JSON(output.JSONWithPrettyOutput()).Render(workloads)
return out.JSON(output.JSONWithPrettyOutput()).Render(entries)
}

if len(ret) == 0 {
out.Println("Team has no workloads.")
return nil
}

return out.Table().Render(workloads)
return out.Table().Render(entries)
},
AutoCompleteFunc: func(ctx context.Context, _ []string, toComplete string) ([]string, string) {
if len(toComplete) < 2 {
Expand Down
33 changes: 22 additions & 11 deletions internal/naisapi/command/teams.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package command

import (
"context"
"fmt"

"github.com/nais/cli/internal/naisapi"
"github.com/nais/cli/internal/naisapi/command/flag"
Expand All @@ -20,13 +21,17 @@ func teamsCommand(parentFlags *flag.Api) *naistrix.Command {
Title: "Get a list of your teams.",
Flags: flags,
RunFunc: func(ctx context.Context, out naistrix.Output, _ []string) error {
// TODO: Once https://github.com/pterm/pterm/issues/697 is resolved, we can use a link to Console instead of just the slug.
type team struct {
Slug string `json:"slug"`
user, err := naisapi.GetAuthenticatedUser(ctx)
if err != nil {
return err
}

type entry struct {
Team team `json:"team"`
Description string `json:"description"`
}

var teams []team
var entries []entry

if flags.All {
ret, err := naisapi.GetAllTeams(ctx)
Expand All @@ -35,8 +40,11 @@ func teamsCommand(parentFlags *flag.Api) *naistrix.Command {
}

for _, t := range ret.Teams.Nodes {
teams = append(teams, team{
Slug: t.Slug,
entries = append(entries, entry{
Team: team{
Slug: t.Slug,
Url: fmt.Sprintf("https://%s/team/%s", user.ConsoleHost(), t.Slug),
},
Description: t.Purpose,
})
}
Expand All @@ -47,23 +55,26 @@ func teamsCommand(parentFlags *flag.Api) *naistrix.Command {
}

for _, t := range userTeams {
teams = append(teams, team{
Slug: t.Team.Slug,
entries = append(entries, entry{
Team: team{
Slug: t.Team.Slug,
Url: fmt.Sprintf("https://%s/team/%s", user.ConsoleHost(), t.Team.Slug),
},
Description: t.Team.Purpose,
})
}
}

if flags.Output == "json" {
return out.JSON(output.JSONWithPrettyOutput()).Render(teams)
return out.JSON(output.JSONWithPrettyOutput()).Render(entries)
}

if len(teams) == 0 {
if len(entries) == 0 {
out.Println("No teams found.")
return nil
}

return out.Table().Render(teams)
return out.Table().Render(entries)
},
}
}