Skip to content
This repository has been archived by the owner on Jan 8, 2024. It is now read-only.

Feature: waypoint job CLI #3067

Merged
merged 10 commits into from
Mar 4, 2022
4 changes: 4 additions & 0 deletions .changelog/3067.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
```release-note:feature
**cli:** Introduce a new CLI command for job management and inspection
`waypoint job`.
```
84 changes: 84 additions & 0 deletions internal/cli/job_cancel.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
package cli

import (
"google.golang.org/grpc/codes"
"google.golang.org/grpc/status"

"github.com/hashicorp/waypoint-plugin-sdk/terminal"
"github.com/hashicorp/waypoint/internal/clierrors"
"github.com/hashicorp/waypoint/internal/pkg/flag"
pb "github.com/hashicorp/waypoint/pkg/server/gen"
)

type JobCancelCommand struct {
*baseCommand

flagJson bool
}

func (c *JobCancelCommand) Run(args []string) int {
// Initialize. If we fail, we just exit since Init handles the UI.
if err := c.Init(
WithArgs(args),
WithFlags(c.Flags()),
); err != nil {
return 1
}

ctx := c.Ctx

var jobId string
if len(c.args) == 0 {
c.ui.Output("Job ID required.\n\n%s", c.Help(), terminal.WithErrorStyle())
return 1
} else {
jobId = c.args[0]
}

sg := c.ui.StepGroup()
defer sg.Wait()

s := sg.Add("Cancelling job %q", jobId)
defer func() { s.Abort() }()

_, err := c.project.Client().CancelJob(ctx, &pb.CancelJobRequest{
JobId: jobId,
})
if err != nil {
s.Update("Failed to marked job %q for cancellation", jobId)
s.Status(terminal.StatusError)
s.Done()

if status.Code(err) == codes.NotFound {
c.ui.Output("Job id not found: %s", clierrors.Humanize(err),
terminal.WithErrorStyle())
return 1
}

c.ui.Output(clierrors.Humanize(err), terminal.WithErrorStyle())
return 1
}

s.Update("Marked job %q for cancellation", jobId)
s.Done()

return 0
}

func (c *JobCancelCommand) Flags() *flag.Sets {
return c.flagSet(flagSetOperation, func(set *flag.Sets) {
})
}

func (c *JobCancelCommand) Synopsis() string {
return "Cancel a running a job by id"
}

func (c *JobCancelCommand) Help() string {
return formatHelp(`
Usage: waypoint job cancel [options] <job-id>

Cancel a running job by id from Waypoint server.

` + c.Flags().Help())
}
74 changes: 74 additions & 0 deletions internal/cli/job_get_stream.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
package cli

import (
"google.golang.org/grpc/codes"
"google.golang.org/grpc/status"

"github.com/hashicorp/waypoint-plugin-sdk/terminal"
"github.com/hashicorp/waypoint/internal/clierrors"
"github.com/hashicorp/waypoint/internal/pkg/flag"
pb "github.com/hashicorp/waypoint/pkg/server/gen"
)

type JobGetStreamCommand struct {
*baseCommand

flagJson bool
}

func (c *JobGetStreamCommand) Run(args []string) int {
// Initialize. If we fail, we just exit since Init handles the UI.
if err := c.Init(
WithArgs(args),
WithFlags(c.Flags()),
); err != nil {
return 1
}
ctx := c.Ctx

var jobId string
if len(c.args) == 0 {
c.ui.Output("Job ID required.\n\n%s", c.Help(), terminal.WithErrorStyle())
return 1
} else {
jobId = c.args[0]
}

_, err := c.project.Client().GetJobStream(ctx, &pb.GetJobStreamRequest{
JobId: jobId,
})
if err != nil {
if status.Code(err) == codes.NotFound {
c.ui.Output("Job id not found: %s", clierrors.Humanize(err),
terminal.WithErrorStyle())
return 1
}

c.ui.Output(clierrors.Humanize(err), terminal.WithErrorStyle())
return 1
}

// TODO(briancain): process and print terminal events like `internal/client/job.go`
c.ui.Output("Job stream is not implemented yet!", terminal.WithWarningStyle())
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'll open a new issue once this PR is wrapped up to finish implementing this.


return 0
}

func (c *JobGetStreamCommand) Flags() *flag.Sets {
return c.flagSet(flagSetOperation, func(set *flag.Sets) {
//f := set.NewSet("Command Options")
})
}

func (c *JobGetStreamCommand) Synopsis() string {
return "Attach a local CLI to a job stream by id"
}

func (c *JobGetStreamCommand) Help() string {
return formatHelp(`
Usage: waypoint job get-stream [options] <job-id>

Connects the local CLI to an active job stream.

` + c.Flags().Help())
}
191 changes: 191 additions & 0 deletions internal/cli/job_inspect.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,191 @@
package cli

import (
"fmt"

"github.com/golang/protobuf/jsonpb"
"google.golang.org/grpc/codes"
"google.golang.org/grpc/status"

"github.com/hashicorp/waypoint-plugin-sdk/terminal"
"github.com/hashicorp/waypoint/internal/clierrors"
"github.com/hashicorp/waypoint/internal/pkg/flag"
pb "github.com/hashicorp/waypoint/pkg/server/gen"
)

type JobInspectCommand struct {
*baseCommand

flagJson bool
}

func (c *JobInspectCommand) Run(args []string) int {
// Initialize. If we fail, we just exit since Init handles the UI.
if err := c.Init(
WithArgs(args),
WithFlags(c.Flags()),
); err != nil {
return 1
}
ctx := c.Ctx

var jobId string
if len(c.args) == 0 {
c.ui.Output("Job ID required.\n\n%s", c.Help(), terminal.WithErrorStyle())
return 1
} else {
jobId = c.args[0]
}

resp, err := c.project.Client().GetJob(ctx, &pb.GetJobRequest{
JobId: jobId,
})
if err != nil {
if status.Code(err) == codes.NotFound {
c.ui.Output("Job id not found: %s", clierrors.Humanize(err),
terminal.WithErrorStyle())
return 1
}

c.ui.Output(clierrors.Humanize(err), terminal.WithErrorStyle())
return 1
}
if resp == nil {
c.ui.Output("The requested job id %q was empty", jobId, terminal.WithWarningStyle())
return 0
}

if c.flagJson {
var m jsonpb.Marshaler
m.Indent = "\t"
str, err := m.MarshalToString(resp)
if err != nil {
c.ui.Output(clierrors.Humanize(err), terminal.WithErrorStyle())
return 1
}

fmt.Println(str)
return 0
}

var op string
// Job_Noop seems to be missing the isJob_operation method
switch resp.Operation.(type) {
case *pb.Job_Build:
op = "Build"
case *pb.Job_Push:
op = "Push"
case *pb.Job_Deploy:
op = "Deploy"
case *pb.Job_Destroy:
op = "Destroy"
case *pb.Job_Release:
op = "Release"
case *pb.Job_Validate:
op = "Validate"
case *pb.Job_Auth:
op = "Auth"
case *pb.Job_Docs:
op = "Docs"
case *pb.Job_ConfigSync:
op = "ConfigSync"
case *pb.Job_Exec:
op = "Exec"
case *pb.Job_Up:
op = "Up"
case *pb.Job_Logs:
op = "Logs"
case *pb.Job_QueueProject:
op = "QueueProject"
case *pb.Job_Poll:
op = "Poll"
case *pb.Job_StatusReport:
op = "StatusReport"
case *pb.Job_StartTask:
op = "StartTask"
case *pb.Job_StopTask:
op = "StopTask"
case *pb.Job_Init:
op = "Init"
default:
op = "Unknown"
}

var jobState string
switch resp.State {
case pb.Job_UNKNOWN:
jobState = "Unknown"
case pb.Job_QUEUED:
jobState = "Queued"
case pb.Job_WAITING:
jobState = "Waiting"
case pb.Job_RUNNING:
jobState = "Running"
case pb.Job_ERROR:
jobState = "Error"
case pb.Job_SUCCESS:
jobState = "Sucecss"
default:
jobState = "Unknown"
}

var targetRunner string
switch target := resp.TargetRunner.Target.(type) {
case *pb.Ref_Runner_Any:
targetRunner = "*"
case *pb.Ref_Runner_Id:
targetRunner = target.Id.Id
}

c.ui.Output("Job Configuration", terminal.WithHeaderStyle())
c.ui.NamedValues([]terminal.NamedValue{
{
Name: "ID", Value: resp.Id,
},
{
Name: "Operation", Value: op,
},
{
Name: "State", Value: jobState,
},
{
briancain marked this conversation as resolved.
Show resolved Hide resolved
Name: "Target Runner", Value: targetRunner,
},
{
Name: "Workspace", Value: resp.Workspace.Workspace,
},
{
Name: "Project", Value: resp.Application.Project,
},
{
Name: "Application", Value: resp.Application.Application,
},
}, terminal.WithInfoStyle())

return 0
}

func (c *JobInspectCommand) Flags() *flag.Sets {
return c.flagSet(flagSetOperation, func(set *flag.Sets) {
f := set.NewSet("Command Options")
f.BoolVar(&flag.BoolVar{
Name: "json",
Target: &c.flagJson,
Default: false,
Usage: "Output the list of jobs as json.",
})
})
}

func (c *JobInspectCommand) Synopsis() string {
return "Inspect the details of a job by id in Waypoint"
}

func (c *JobInspectCommand) Help() string {
return formatHelp(`
Usage: waypoint job list [options]

Inspect the details of a job by id in Waypoint server.

` + c.Flags().Help())
}
Loading