Skip to content

Commit

Permalink
chore: Refactor CLI to avoid global context
Browse files Browse the repository at this point in the history
  • Loading branch information
irvinlim committed Jan 26, 2024
1 parent 9b7c826 commit 45cc76b
Show file tree
Hide file tree
Showing 18 changed files with 135 additions and 120 deletions.
11 changes: 10 additions & 1 deletion cmd/furiko-cli/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
package main

import (
"context"
"fmt"
"os"

Expand All @@ -29,7 +30,7 @@ import (

func main() {
ctx := ctrl.SetupSignalHandler()
err := cmd.NewRootCommand(streams.NewStdStreams()).ExecuteContext(ctx)
err := executeCommand(ctx)
if err != nil {
// Special handling: Do not raise interrupts as errors, but simply return the
// exit code when signaled by SIGINT.
Expand All @@ -41,3 +42,11 @@ func main() {
os.Exit(1)
}
}

func executeCommand(ctx context.Context) error {
root, err := cmd.NewRootCommand(streams.NewStdStreams())
if err != nil {
return err
}
return root.ExecuteContext(ctx)
}
58 changes: 41 additions & 17 deletions pkg/cli/cmd/cmd.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,29 +24,55 @@ import (
"github.com/furiko-io/furiko/pkg/cli/common"
"github.com/furiko-io/furiko/pkg/cli/completion"
"github.com/furiko-io/furiko/pkg/cli/streams"
"github.com/furiko-io/furiko/pkg/runtime/controllercontext"
"github.com/furiko-io/furiko/pkg/utils/logging"
)

type RootCommand struct {
streams *streams.Streams
context controllercontext.Context

verbosity int
}

// NewRootCommand returns a new root command for the command-line utility.
func NewRootCommand(streams *streams.Streams) *cobra.Command {
c := &RootCommand{
streams: streams,
// Initializes the controller context automatically, and returns an error during setup if any,
func NewRootCommand(streams *streams.Streams) (*cobra.Command, error) {
cmd := newCommand()
ctrlContext, err := common.NewContext(cmd)
if err != nil {
return nil, err
}
setupCommand(cmd, ctrlContext, streams)
return cmd, nil
}

cmd := &cobra.Command{
Use: common.CommandName,
Short: "Command-line utility to manage Furiko.",
PersistentPreRunE: c.PersistentPreRunE,
// NewRootCommandWithContext returns a new root command for the command-line utility with the given controller context.
// To automatically set up the controller context, use NewRootCommand.
func NewRootCommandWithContext(ctrlCtx controllercontext.Context, streams *streams.Streams) *cobra.Command {
cmd := newCommand()
setupCommand(cmd, ctrlCtx, streams)
return cmd
}

func newCommand() *cobra.Command {
return &cobra.Command{
Use: common.CommandName,
Short: "Command-line utility to manage Furiko.",

// Don't show help on error.
SilenceUsage: true,
}
}

func setupCommand(cmd *cobra.Command, ctrlCtx controllercontext.Context, streams *streams.Streams) {
c := &RootCommand{
streams: streams,
context: ctrlCtx,
}

// Sets up the log level.
cmd.PersistentPreRunE = c.PersistentPreRunE

// Set IO streams.
streams.SetCmdOutput(cmd)
Expand All @@ -66,23 +92,21 @@ func NewRootCommand(streams *streams.Streams) *cobra.Command {
"Overrides the namespace of the dynamic cluster config.")
flags.IntVarP(&c.verbosity, "v", "v", 0, "Sets the log level verbosity.")

if err := completion.RegisterFlagCompletions(cmd, []completion.FlagCompletion{
if err := completion.RegisterFlagCompletions(ctrlCtx, cmd, []completion.FlagCompletion{
{FlagName: "namespace", Completer: &completion.ListNamespacesCompleter{}},
{FlagName: "cluster", CmdCompletionFunc: completion.ListKubeconfigClusterCompletionFunc},
{FlagName: "context", CmdCompletionFunc: completion.ListKubeconfigContextCompletionFunc},
}); err != nil {
common.Fatal(err, common.DefaultErrorExitCode)
}

cmd.AddCommand(NewDisableCommand(streams))
cmd.AddCommand(NewEnableCommand(streams))
cmd.AddCommand(NewGetCommand(streams))
cmd.AddCommand(NewKillCommand(streams))
cmd.AddCommand(NewListCommand(streams))
cmd.AddCommand(NewLogsCommand(streams))
cmd.AddCommand(NewRunCommand(streams))

return cmd
cmd.AddCommand(NewDisableCommand(ctrlCtx, streams))
cmd.AddCommand(NewEnableCommand(ctrlCtx, streams))
cmd.AddCommand(NewGetCommand(ctrlCtx, streams))
cmd.AddCommand(NewKillCommand(ctrlCtx, streams))
cmd.AddCommand(NewListCommand(ctrlCtx, streams))
cmd.AddCommand(NewLogsCommand(ctrlCtx, streams))
cmd.AddCommand(NewRunCommand(ctrlCtx, streams))
}

func (c *RootCommand) PersistentPreRunE(cmd *cobra.Command, args []string) error {
Expand Down
10 changes: 6 additions & 4 deletions pkg/cli/cmd/cmd_disable.go
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ import (
"github.com/furiko-io/furiko/pkg/cli/common"
"github.com/furiko-io/furiko/pkg/cli/completion"
"github.com/furiko-io/furiko/pkg/cli/streams"
"github.com/furiko-io/furiko/pkg/runtime/controllercontext"
)

var (
Expand All @@ -38,11 +39,13 @@ var (

type DisableCommand struct {
streams *streams.Streams
context controllercontext.Context
}

func NewDisableCommand(streams *streams.Streams) *cobra.Command {
func NewDisableCommand(ctrlCtx controllercontext.Context, streams *streams.Streams) *cobra.Command {
c := &DisableCommand{
streams: streams,
context: ctrlCtx,
}

cmd := &cobra.Command{
Expand All @@ -54,8 +57,7 @@ If the specified JobConfig does not have a schedule, then an error will be throw
If the specified JobConfig is already disabled, then this is a no-op.`,
Example: DisableExample,
Args: cobra.ExactArgs(1),
PreRunE: common.PrerunWithKubeconfig,
ValidArgsFunction: completion.CompleterToCobraCompletionFunc(&completion.ListJobConfigsCompleter{}),
ValidArgsFunction: completion.CompleterToCobraCompletionFunc(ctrlCtx, &completion.ListJobConfigsCompleter{}),
RunE: c.Run,
}

Expand All @@ -64,7 +66,7 @@ If the specified JobConfig is already disabled, then this is a no-op.`,

func (c *DisableCommand) Run(cmd *cobra.Command, args []string) error {
ctx := cmd.Context()
client := common.GetCtrlContext().Clientsets().Furiko().ExecutionV1alpha1()
client := c.context.Clientsets().Furiko().ExecutionV1alpha1()
namespace, err := common.GetNamespace(cmd)
if err != nil {
return err
Expand Down
10 changes: 6 additions & 4 deletions pkg/cli/cmd/cmd_enable.go
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ import (
"github.com/furiko-io/furiko/pkg/cli/common"
"github.com/furiko-io/furiko/pkg/cli/completion"
"github.com/furiko-io/furiko/pkg/cli/streams"
"github.com/furiko-io/furiko/pkg/runtime/controllercontext"
)

var (
Expand All @@ -38,11 +39,13 @@ var (

type EnableCommand struct {
streams *streams.Streams
context controllercontext.Context
}

func NewEnableCommand(streams *streams.Streams) *cobra.Command {
func NewEnableCommand(ctrlCtx controllercontext.Context, streams *streams.Streams) *cobra.Command {
c := &EnableCommand{
streams: streams,
context: ctrlCtx,
}

cmd := &cobra.Command{
Expand All @@ -54,8 +57,7 @@ If the specified JobConfig does not have a schedule, then an error will be throw
If the specified JobConfig is already enabled, then this is a no-op.`,
Example: EnableExample,
Args: cobra.ExactArgs(1),
PreRunE: common.PrerunWithKubeconfig,
ValidArgsFunction: completion.CompleterToCobraCompletionFunc(&completion.ListJobConfigsCompleter{}),
ValidArgsFunction: completion.CompleterToCobraCompletionFunc(ctrlCtx, &completion.ListJobConfigsCompleter{}),
RunE: c.Run,
}

Expand All @@ -64,7 +66,7 @@ If the specified JobConfig is already enabled, then this is a no-op.`,

func (c *EnableCommand) Run(cmd *cobra.Command, args []string) error {
ctx := cmd.Context()
client := common.GetCtrlContext().Clientsets().Furiko().ExecutionV1alpha1()
client := c.context.Clientsets().Furiko().ExecutionV1alpha1()
namespace, err := common.GetNamespace(cmd)
if err != nil {
return err
Expand Down
11 changes: 7 additions & 4 deletions pkg/cli/cmd/cmd_get.go
Original file line number Diff line number Diff line change
Expand Up @@ -26,24 +26,27 @@ import (
"github.com/furiko-io/furiko/pkg/cli/completion"
"github.com/furiko-io/furiko/pkg/cli/printer"
"github.com/furiko-io/furiko/pkg/cli/streams"
"github.com/furiko-io/furiko/pkg/runtime/controllercontext"
)

type GetCommand struct {
streams *streams.Streams
context controllercontext.Context
}

func NewGetCommand(streams *streams.Streams) *cobra.Command {
func NewGetCommand(ctrlCtx controllercontext.Context, streams *streams.Streams) *cobra.Command {
c := &GetCommand{
streams: streams,
context: ctrlCtx,
}

cmd := &cobra.Command{
Use: "get",
Short: "Get one or more resources by name.",
}

c.AddSubcommand(cmd, NewGetJobCommand(streams))
c.AddSubcommand(cmd, NewGetJobConfigCommand(streams))
c.AddSubcommand(cmd, NewGetJobCommand(ctrlCtx, streams))
c.AddSubcommand(cmd, NewGetJobConfigCommand(ctrlCtx, streams))

return cmd
}
Expand All @@ -59,7 +62,7 @@ func (c *GetCommand) RegisterFlags(cmd *cobra.Command) {
cmd.PersistentFlags().StringP("output", "o", string(printer.OutputFormatPretty),
fmt.Sprintf("Output format. One of: %v", strings.Join(printer.GetAllOutputFormatStrings(), "|")))

if err := completion.RegisterFlagCompletions(cmd, []completion.FlagCompletion{
if err := completion.RegisterFlagCompletions(c.context, cmd, []completion.FlagCompletion{
{FlagName: "output", Completer: completion.NewSliceCompleter(printer.AllOutputFormats)},
}); err != nil {
common.Fatal(err, common.DefaultErrorExitCode)
Expand Down
10 changes: 6 additions & 4 deletions pkg/cli/cmd/cmd_get_job.go
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ import (
"github.com/furiko-io/furiko/pkg/cli/printer"
"github.com/furiko-io/furiko/pkg/cli/streams"
"github.com/furiko-io/furiko/pkg/execution/util/parallel"
"github.com/furiko-io/furiko/pkg/runtime/controllercontext"
"github.com/furiko-io/furiko/pkg/utils/ktime"
stringsutils "github.com/furiko-io/furiko/pkg/utils/strings"
)
Expand All @@ -49,23 +50,24 @@ var (

type GetJobCommand struct {
streams *streams.Streams
context controllercontext.Context

output printer.OutputFormat
}

func NewGetJobCommand(streams *streams.Streams) *cobra.Command {
func NewGetJobCommand(ctrlCtx controllercontext.Context, streams *streams.Streams) *cobra.Command {
c := &GetJobCommand{
streams: streams,
context: ctrlCtx,
}

cmd := &cobra.Command{
Use: "job",
Aliases: []string{"jobs"},
Short: "Displays information about one or more Jobs.",
Example: GetJobExample,
PreRunE: common.PrerunWithKubeconfig,
Args: cobra.MinimumNArgs(1),
ValidArgsFunction: completion.CompleterToCobraCompletionFunc(&completion.ListJobsCompleter{}),
ValidArgsFunction: completion.CompleterToCobraCompletionFunc(ctrlCtx, &completion.ListJobsCompleter{}),
RunE: common.RunAllE(
c.Complete,
c.Run,
Expand All @@ -82,7 +84,7 @@ func (c *GetJobCommand) Complete(cmd *cobra.Command, args []string) error {

func (c *GetJobCommand) Run(cmd *cobra.Command, args []string) error {
ctx := cmd.Context()
client := common.GetCtrlContext().Clientsets().Furiko().ExecutionV1alpha1()
client := c.context.Clientsets().Furiko().ExecutionV1alpha1()
namespace, err := common.GetNamespace(cmd)
if err != nil {
return err
Expand Down
12 changes: 7 additions & 5 deletions pkg/cli/cmd/cmd_get_jobconfig.go
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ import (
"github.com/furiko-io/furiko/pkg/cli/printer"
"github.com/furiko-io/furiko/pkg/cli/streams"
"github.com/furiko-io/furiko/pkg/execution/util/cron"
"github.com/furiko-io/furiko/pkg/runtime/controllercontext"
)

var (
Expand All @@ -47,26 +48,27 @@ var (

type GetJobConfigCommand struct {
streams *streams.Streams
context controllercontext.Context

output printer.OutputFormat

// Cached cron parser.
cronParser *cron.Parser
}

func NewGetJobConfigCommand(streams *streams.Streams) *cobra.Command {
func NewGetJobConfigCommand(ctrlCtx controllercontext.Context, streams *streams.Streams) *cobra.Command {
c := &GetJobConfigCommand{
streams: streams,
context: ctrlCtx,
}

cmd := &cobra.Command{
Use: "jobconfig",
Aliases: []string{"jobconfigs"},
Short: "Displays information about one or more JobConfigs.",
Example: GetJobConfigExample,
PreRunE: common.PrerunWithKubeconfig,
Args: cobra.MinimumNArgs(1),
ValidArgsFunction: completion.CompleterToCobraCompletionFunc(&completion.ListJobConfigsCompleter{}),
ValidArgsFunction: completion.CompleterToCobraCompletionFunc(ctrlCtx, &completion.ListJobConfigsCompleter{}),
RunE: common.RunAllE(
c.Complete,
c.Run,
Expand All @@ -80,14 +82,14 @@ func (c *GetJobConfigCommand) Complete(cmd *cobra.Command, args []string) error
c.output = common.GetOutputFormat(cmd)

// Prepare parser.
c.cronParser = cron.NewParserFromConfig(common.GetCronDynamicConfig(cmd))
c.cronParser = cron.NewParserFromConfig(common.GetCronDynamicConfig(c.context, cmd))

return nil
}

func (c *GetJobConfigCommand) Run(cmd *cobra.Command, args []string) error {
ctx := cmd.Context()
client := common.GetCtrlContext().Clientsets().Furiko().ExecutionV1alpha1()
client := c.context.Clientsets().Furiko().ExecutionV1alpha1()
namespace, err := common.GetNamespace(cmd)
if err != nil {
return err
Expand Down
10 changes: 6 additions & 4 deletions pkg/cli/cmd/cmd_kill.go
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ import (
"github.com/furiko-io/furiko/pkg/cli/format"
"github.com/furiko-io/furiko/pkg/cli/streams"
jobutil "github.com/furiko-io/furiko/pkg/execution/util/job"
"github.com/furiko-io/furiko/pkg/runtime/controllercontext"
"github.com/furiko-io/furiko/pkg/utils/ktime"
)

Expand All @@ -48,13 +49,15 @@ var (

type KillCommand struct {
streams *streams.Streams
context controllercontext.Context
override bool
killAt time.Time
}

func NewKillCommand(streams *streams.Streams) *cobra.Command {
func NewKillCommand(ctrlCtx controllercontext.Context, streams *streams.Streams) *cobra.Command {
c := &KillCommand{
streams: streams,
context: ctrlCtx,
}

cmd := &cobra.Command{
Expand All @@ -63,8 +66,7 @@ func NewKillCommand(streams *streams.Streams) *cobra.Command {
Long: `Kills an ongoing Job that is currently running or pending.`,
Example: KillExample,
Args: cobra.ExactArgs(1),
PreRunE: common.PrerunWithKubeconfig,
ValidArgsFunction: completion.CompleterToCobraCompletionFunc(&completion.ListJobsCompleter{Filter: jobutil.IsActive}),
ValidArgsFunction: completion.CompleterToCobraCompletionFunc(ctrlCtx, &completion.ListJobsCompleter{Filter: jobutil.IsActive}),
RunE: common.RunAllE(
c.Complete,
c.Validate,
Expand Down Expand Up @@ -122,7 +124,7 @@ func (c *KillCommand) Validate(cmd *cobra.Command, args []string) error {

func (c *KillCommand) Run(cmd *cobra.Command, args []string) error {
ctx := cmd.Context()
client := common.GetCtrlContext().Clientsets().Furiko().ExecutionV1alpha1()
client := c.context.Clientsets().Furiko().ExecutionV1alpha1()
namespace, err := common.GetNamespace(cmd)
if err != nil {
return err
Expand Down
Loading

0 comments on commit 45cc76b

Please sign in to comment.