-
Notifications
You must be signed in to change notification settings - Fork 22
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
10 changed files
with
370 additions
and
11 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,149 @@ | ||
package playground | ||
|
||
import ( | ||
"context" | ||
"fmt" | ||
"io" | ||
"strings" | ||
"text/tabwriter" | ||
"time" | ||
|
||
"github.com/dustin/go-humanize" | ||
"github.com/spf13/cobra" | ||
|
||
"github.com/iximiuz/labctl/internal/api" | ||
"github.com/iximiuz/labctl/internal/labcli" | ||
) | ||
|
||
type listOptions struct { | ||
all bool | ||
quiet bool | ||
} | ||
|
||
func newListCommand(cli labcli.CLI) *cobra.Command { | ||
var opts listOptions | ||
|
||
cmd := &cobra.Command{ | ||
Use: "list [flags]", | ||
Aliases: []string{"ls"}, | ||
Short: `List recent playgrounds (up to 50)`, | ||
RunE: func(cmd *cobra.Command, args []string) error { | ||
return labcli.WrapStatusError(runListPlaygrounds(cmd.Context(), cli, &opts)) | ||
}, | ||
} | ||
|
||
flags := cmd.Flags() | ||
|
||
flags.BoolVarP( | ||
&opts.all, | ||
"all", | ||
"a", | ||
false, | ||
`List all playgrounds (including terminated)`, | ||
) | ||
flags.BoolVarP( | ||
&opts.quiet, | ||
"quiet", | ||
"q", | ||
false, | ||
`Only print playground IDs`, | ||
) | ||
|
||
return cmd | ||
} | ||
|
||
func runListPlaygrounds(ctx context.Context, cli labcli.CLI, opts *listOptions) error { | ||
printer := newListPrinter(cli.OutputStream(), opts.quiet) | ||
defer printer.flush() | ||
|
||
printer.printHeader() | ||
|
||
plays, err := cli.Client().ListPlays(ctx) | ||
if err != nil { | ||
return fmt.Errorf("couldn't list playgrounds: %w", err) | ||
} | ||
|
||
for _, play := range plays { | ||
if opts.all || play.Active { | ||
printer.printOne(play) | ||
} | ||
} | ||
|
||
return nil | ||
} | ||
|
||
type listPrinter struct { | ||
quiet bool | ||
header []string | ||
writer *tabwriter.Writer | ||
} | ||
|
||
func newListPrinter(outStream io.Writer, quiet bool) *listPrinter { | ||
header := []string{ | ||
"PLAYGROUND ID", | ||
"NAME", | ||
"CREATED", | ||
"STATUS", | ||
"LINK", | ||
} | ||
|
||
return &listPrinter{ | ||
quiet: quiet, | ||
header: header, | ||
writer: tabwriter.NewWriter(outStream, 0, 4, 2, ' ', 0), | ||
} | ||
} | ||
|
||
func (p *listPrinter) printHeader() { | ||
if !p.quiet { | ||
fmt.Fprintln(p.writer, strings.Join(p.header, "\t")) | ||
} | ||
} | ||
|
||
func (p *listPrinter) printOne(play *api.Play) { | ||
if p.quiet { | ||
fmt.Fprintln(p.writer, play.ID) | ||
return | ||
} | ||
|
||
var link string | ||
if play.Active { | ||
link = play.PageURL | ||
} | ||
|
||
fields := []string{ | ||
play.ID, | ||
play.Playground.Name, | ||
humanize.Time(safeParseTime(play.CreatedAt)), | ||
playStatus(play), | ||
link, | ||
} | ||
|
||
fmt.Fprintln(p.writer, strings.Join(fields, "\t")) | ||
} | ||
|
||
func (p *listPrinter) flush() { | ||
p.writer.Flush() | ||
} | ||
|
||
func playStatus(play *api.Play) string { | ||
if play.Running { | ||
return fmt.Sprintf("running (expires in %s)", | ||
humanize.Time(time.Now().Add(time.Duration(play.ExpiresIn)*time.Millisecond))) | ||
} | ||
if play.Destroyed { | ||
return fmt.Sprintf("terminated %s", | ||
humanize.Time(safeParseTime(play.LastStateAt))) | ||
} | ||
if play.Failed { | ||
return fmt.Sprintf("failed %s", | ||
humanize.Time(safeParseTime(play.LastStateAt))) | ||
} | ||
|
||
return "unknown" | ||
} | ||
|
||
func safeParseTime(s string) time.Time { | ||
t, _ := time.Parse(time.RFC3339, s) | ||
return t | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,23 @@ | ||
package playground | ||
|
||
import ( | ||
"github.com/spf13/cobra" | ||
|
||
"github.com/iximiuz/labctl/internal/labcli" | ||
) | ||
|
||
func NewCommand(cli labcli.CLI) *cobra.Command { | ||
cmd := &cobra.Command{ | ||
Use: "playground <list|start|stop> [playground-name]", | ||
Aliases: []string{"p", "playgrounds"}, | ||
Short: "List, start and stop playgrounds", | ||
} | ||
|
||
cmd.AddCommand( | ||
newListCommand(cli), | ||
newStartCommand(cli), | ||
newStopCommand(cli), | ||
) | ||
|
||
return cmd | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,73 @@ | ||
package playground | ||
|
||
import ( | ||
"context" | ||
"fmt" | ||
|
||
"github.com/skratchdot/open-golang/open" | ||
"github.com/spf13/cobra" | ||
|
||
"github.com/iximiuz/labctl/internal/api" | ||
"github.com/iximiuz/labctl/internal/labcli" | ||
) | ||
|
||
type startOptions struct { | ||
playground string | ||
|
||
open bool | ||
quiet bool | ||
} | ||
|
||
func newStartCommand(cli labcli.CLI) *cobra.Command { | ||
var opts startOptions | ||
|
||
cmd := &cobra.Command{ | ||
Use: "start [flags] <playground-name>", | ||
Short: `Start a new playground, possibly open it in a browser`, | ||
Args: cobra.ExactArgs(1), | ||
RunE: func(cmd *cobra.Command, args []string) error { | ||
cli.SetQuiet(opts.quiet) | ||
|
||
opts.playground = args[0] | ||
|
||
return labcli.WrapStatusError(runStartPlayground(cmd.Context(), cli, &opts)) | ||
}, | ||
} | ||
|
||
flags := cmd.Flags() | ||
|
||
flags.BoolVar( | ||
&opts.open, | ||
"open", | ||
false, | ||
`Open the playground page in a browser`, | ||
) | ||
flags.BoolVarP( | ||
&opts.quiet, | ||
"quiet", | ||
"q", | ||
false, | ||
`Only print playground's ID`, | ||
) | ||
|
||
return cmd | ||
} | ||
|
||
func runStartPlayground(ctx context.Context, cli labcli.CLI, opts *startOptions) error { | ||
play, err := cli.Client().CreatePlay(ctx, api.CreatePlayRequest{ | ||
Playground: opts.playground, | ||
}) | ||
if err != nil { | ||
return fmt.Errorf("couldn't create a new playground: %w", err) | ||
} | ||
|
||
cli.PrintAux("Opening %s in your browser...\n", play.PageURL) | ||
|
||
if err := open.Run(play.PageURL); err != nil { | ||
cli.PrintAux("Couldn't open the browser. Copy the above URL into a browser manually to access the playground.\n") | ||
} | ||
|
||
cli.PrintOut("%s\n", play.ID) | ||
|
||
return nil | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,80 @@ | ||
package playground | ||
|
||
import ( | ||
"context" | ||
"fmt" | ||
"time" | ||
|
||
"github.com/briandowns/spinner" | ||
"github.com/spf13/cobra" | ||
|
||
"github.com/iximiuz/labctl/internal/labcli" | ||
) | ||
|
||
const stopCommandTimeout = 5 * time.Minute | ||
|
||
type stopOptions struct { | ||
playID string | ||
|
||
quiet bool | ||
} | ||
|
||
func newStopCommand(cli labcli.CLI) *cobra.Command { | ||
var opts stopOptions | ||
|
||
cmd := &cobra.Command{ | ||
Use: "stop [flags] <playground-id>", | ||
Short: `Start a new playground, possibly open it in a browser`, | ||
Args: cobra.ExactArgs(1), | ||
RunE: func(cmd *cobra.Command, args []string) error { | ||
cli.SetQuiet(opts.quiet) | ||
|
||
opts.playID = args[0] | ||
|
||
return labcli.WrapStatusError(runStopPlayground(cmd.Context(), cli, &opts)) | ||
}, | ||
} | ||
|
||
flags := cmd.Flags() | ||
|
||
flags.BoolVarP( | ||
&opts.quiet, | ||
"quiet", | ||
"q", | ||
false, | ||
`Do not print any diagnostic messages`, | ||
) | ||
|
||
return cmd | ||
} | ||
|
||
func runStopPlayground(ctx context.Context, cli labcli.CLI, opts *stopOptions) error { | ||
cli.PrintAux("Stopping playground %s...\n", opts.playID) | ||
|
||
if err := cli.Client().DeletePlay(ctx, opts.playID); err != nil { | ||
return fmt.Errorf("couldn't delete the playground: %w", err) | ||
} | ||
|
||
s := spinner.New(spinner.CharSets[38], 300*time.Millisecond) | ||
s.Writer = cli.AuxStream() | ||
s.Prefix = "Waiting for playground to terminate... " | ||
s.Start() | ||
|
||
ctx, cancel := context.WithTimeout(ctx, stopCommandTimeout) | ||
defer cancel() | ||
|
||
for ctx.Err() == nil { | ||
if play, err := cli.Client().GetPlay(ctx, opts.playID); err == nil && !play.Active { | ||
s.FinalMSG = "Waiting for playground to terminate... Done.\n" | ||
s.Stop() | ||
|
||
return nil | ||
} | ||
|
||
time.Sleep(2 * time.Second) | ||
} | ||
|
||
cli.PrintAux("Playground has been stopped.\n") | ||
|
||
return nil | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.