From 4e69517b52f8a4803b3f98b26a391e1f0740f3df Mon Sep 17 00:00:00 2001 From: Nathan ter Bogt Date: Tue, 29 Sep 2026 15:54:13 +1300 Subject: [PATCH 1/2] Initial command for enviroment suspend and resume --- cmd/skpr/main.go | 4 ++++ cmd/skpr/resume/command.go | 37 +++++++++++++++++++++++++++++ cmd/skpr/suspend/command.go | 37 +++++++++++++++++++++++++++++ go.mod | 2 +- go.sum | 4 ++-- internal/command/list/command.go | 2 ++ internal/command/resume/command.go | 35 +++++++++++++++++++++++++++ internal/command/suspend/command.go | 35 +++++++++++++++++++++++++++ 8 files changed, 153 insertions(+), 3 deletions(-) create mode 100644 cmd/skpr/resume/command.go create mode 100644 cmd/skpr/suspend/command.go create mode 100644 internal/command/resume/command.go create mode 100644 internal/command/suspend/command.go diff --git a/cmd/skpr/main.go b/cmd/skpr/main.go index 30e7b9e..45cf9db 100644 --- a/cmd/skpr/main.go +++ b/cmd/skpr/main.go @@ -35,8 +35,10 @@ import ( "github.com/skpr/cli/cmd/skpr/purge" "github.com/skpr/cli/cmd/skpr/release" "github.com/skpr/cli/cmd/skpr/restore" + "github.com/skpr/cli/cmd/skpr/resume" "github.com/skpr/cli/cmd/skpr/rsync" "github.com/skpr/cli/cmd/skpr/shell" + "github.com/skpr/cli/cmd/skpr/suspend" "github.com/skpr/cli/cmd/skpr/trace" "github.com/skpr/cli/cmd/skpr/validate" "github.com/skpr/cli/cmd/skpr/version" @@ -116,8 +118,10 @@ func main() { cmd.AddCommand(purge.NewCommand()) cmd.AddCommand(release.NewCommand(featureFlags.DockerClient)) cmd.AddCommand(restore.NewCommand()) + cmd.AddCommand(resume.NewCommand()) cmd.AddCommand(rsync.NewCommand()) cmd.AddCommand(shell.NewCommand()) + cmd.AddCommand(suspend.NewCommand()) cmd.AddCommand(validate.NewCommand()) cmd.AddCommand(version.NewCommand()) diff --git a/cmd/skpr/resume/command.go b/cmd/skpr/resume/command.go new file mode 100644 index 0000000..127ad91 --- /dev/null +++ b/cmd/skpr/resume/command.go @@ -0,0 +1,37 @@ +package resume + +import ( + "github.com/spf13/cobra" + + skprcommand "github.com/skpr/cli/internal/command" + v1resume "github.com/skpr/cli/internal/command/resume" +) + +var ( + cmdLong = `Resume an environment.` + + cmdExample = ` + # Resume the dev environment + skpr resume dev` +) + +// NewCommand creates a new cobra.Command for 'resume' sub command +func NewCommand() *cobra.Command { + command := v1resume.Command{} + + cmd := &cobra.Command{ + Use: "resume ", + Args: cobra.MatchAll(cobra.ExactArgs(1), cobra.OnlyValidArgs), + DisableFlagsInUseLine: true, + Short: "Resume an environment.", + Long: cmdLong, + Example: cmdExample, + GroupID: skprcommand.GroupLifecycle, + RunE: func(cmd *cobra.Command, args []string) error { + command.Environment = args[0] + return command.Run(cmd.Context()) + }, + } + + return cmd +} diff --git a/cmd/skpr/suspend/command.go b/cmd/skpr/suspend/command.go new file mode 100644 index 0000000..9c40fef --- /dev/null +++ b/cmd/skpr/suspend/command.go @@ -0,0 +1,37 @@ +package suspend + +import ( + "github.com/spf13/cobra" + + skprcommand "github.com/skpr/cli/internal/command" + v1suspend "github.com/skpr/cli/internal/command/suspend" +) + +var ( + cmdLong = `Suspend an environment.` + + cmdExample = ` + # Suspend the dev environment + skpr suspend dev` +) + +// NewCommand creates a new cobra.Command for 'suspend' sub command +func NewCommand() *cobra.Command { + command := v1suspend.Command{} + + cmd := &cobra.Command{ + Use: "suspend ", + Args: cobra.MatchAll(cobra.ExactArgs(1), cobra.OnlyValidArgs), + DisableFlagsInUseLine: true, + Short: "Suspend an environment.", + Long: cmdLong, + Example: cmdExample, + GroupID: skprcommand.GroupLifecycle, + RunE: func(cmd *cobra.Command, args []string) error { + command.Environment = args[0] + return command.Run(cmd.Context()) + }, + } + + return cmd +} diff --git a/go.mod b/go.mod index bafdfbd..248c6d1 100644 --- a/go.mod +++ b/go.mod @@ -30,7 +30,7 @@ require ( github.com/moby/patternmatcher v0.6.1 github.com/olekukonko/ts v0.0.0-20171002115256-78ecb04241c0 github.com/pkg/errors v0.9.1 - github.com/skpr/api v1.8.5 + github.com/skpr/api v1.9.1 github.com/skpr/compass v1.13.3 github.com/skratchdot/open-golang v0.0.0-20200116055534-eef842397966 github.com/spf13/cobra v1.10.2 diff --git a/go.sum b/go.sum index b730611..a39ecbd 100644 --- a/go.sum +++ b/go.sum @@ -210,8 +210,8 @@ github.com/rogpeppe/go-internal v1.14.1/go.mod h1:MaRKkUm5W0goXpeCfT7UZI6fk/L7L7 github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= github.com/sirupsen/logrus v1.9.4 h1:TsZE7l11zFCLZnZ+teH4Umoq5BhEIfIzfRDZ1Uzql2w= github.com/sirupsen/logrus v1.9.4/go.mod h1:ftWc9WdOfJ0a92nsE2jF5u5ZwH8Bv2zdeOC42RjbV2g= -github.com/skpr/api v1.8.5 h1:7PRQ9UphXUJDzMD3HaTWF1H3SQZ0zRFyv8/x7WRMttc= -github.com/skpr/api v1.8.5/go.mod h1:3ujFy8YadnZ/eEOCoeTnmtIFeVoBu6sQjTLLUKkFVFY= +github.com/skpr/api v1.9.1 h1:QnWQGoMHoADxPJurGJo/FSqbDkPRxh6jBf/7zKsrRzQ= +github.com/skpr/api v1.9.1/go.mod h1:UEv2ZbhO49IPAS9pbLFuVSmNvXrb6aKQZXHfwutxlgA= github.com/skpr/compass v1.13.3 h1:Ma1zjhgflSBKW5Nw3g/VEFQEfJndscQExSou6sOLuhg= github.com/skpr/compass v1.13.3/go.mod h1:SM6S6PAku3q2r6L7QGKxu+E5vLA6TEQFZmTYoWapcGQ= github.com/skratchdot/open-golang v0.0.0-20200116055534-eef842397966 h1:JIAuq3EEf9cgbU6AtGPK4CTG3Zf6CKMNqf0MHTggAUA= diff --git a/internal/command/list/command.go b/internal/command/list/command.go index f04bf97..aed5a14 100644 --- a/internal/command/list/command.go +++ b/internal/command/list/command.go @@ -49,6 +49,7 @@ func Print(w io.Writer, list []*pb.Environment) error { "Size", "Routes", "Phase", + "Suspended", } var rows [][]string @@ -60,6 +61,7 @@ func Print(w io.Writer, list []*pb.Environment) error { item.Size, strings.Join(append(item.Ingress.Routes, item.Ingress.Domain), "\n"), color.ApplyColorToString(item.Phase), + fmt.Sprintf("%v", item.Suspended), }) } diff --git a/internal/command/resume/command.go b/internal/command/resume/command.go new file mode 100644 index 0000000..ec9c7d8 --- /dev/null +++ b/internal/command/resume/command.go @@ -0,0 +1,35 @@ +package resume + +import ( + "context" + "fmt" + "os" + + "github.com/skpr/api/pb" + + "github.com/skpr/cli/internal/client" +) + +// Command for resuming cron jobs. +type Command struct { + Environment string +} + +// Run the command. +func (cmd *Command) Run(ctx context.Context) error { + ctx, client, err := client.New(ctx) + if err != nil { + return err + } + + _, err = client.Environment().Resume(ctx, &pb.EnvironmentResumeRequest{ + Name: cmd.Environment, + }) + if err != nil { + return err + } + + fmt.Fprintln(os.Stderr, "The environment has been resumed.") + + return nil +} diff --git a/internal/command/suspend/command.go b/internal/command/suspend/command.go new file mode 100644 index 0000000..388848f --- /dev/null +++ b/internal/command/suspend/command.go @@ -0,0 +1,35 @@ +package suspend + +import ( + "context" + "fmt" + "os" + + "github.com/skpr/api/pb" + + "github.com/skpr/cli/internal/client" +) + +// Command to suspend cron jobs. +type Command struct { + Environment string +} + +// Run the command. +func (cmd *Command) Run(ctx context.Context) error { + ctx, client, err := client.New(ctx) + if err != nil { + return err + } + + _, err = client.Environment().Suspend(ctx, &pb.EnvironmentSuspendRequest{ + Name: cmd.Environment, + }) + if err != nil { + return err + } + + fmt.Fprintln(os.Stderr, "The environment has been suspended.") + + return nil +} From f9ed66f13f0fbb5d22a2e7ef5f49781a64c4636f Mon Sep 17 00:00:00 2001 From: Nathan ter Bogt Date: Fri, 2 Oct 2026 09:13:04 +1300 Subject: [PATCH 2/2] Make sure we confirm suspension for a production environment --- cmd/skpr/suspend/command.go | 3 +++ internal/command/suspend/command.go | 15 +++++++++++++++ 2 files changed, 18 insertions(+) diff --git a/cmd/skpr/suspend/command.go b/cmd/skpr/suspend/command.go index 9c40fef..1a54086 100644 --- a/cmd/skpr/suspend/command.go +++ b/cmd/skpr/suspend/command.go @@ -33,5 +33,8 @@ func NewCommand() *cobra.Command { }, } + cmd.Flags().BoolVarP(&command.Force, "force", "f", command.Force, "Skpr will not request a confirmation for suspending production environments") + _ = cmd.Flags().MarkHidden("force") + return cmd } diff --git a/internal/command/suspend/command.go b/internal/command/suspend/command.go index 388848f..d67c23c 100644 --- a/internal/command/suspend/command.go +++ b/internal/command/suspend/command.go @@ -8,11 +8,13 @@ import ( "github.com/skpr/api/pb" "github.com/skpr/cli/internal/client" + "github.com/skpr/cli/internal/confirmation" ) // Command to suspend cron jobs. type Command struct { Environment string + Force bool } // Run the command. @@ -22,6 +24,19 @@ func (cmd *Command) Run(ctx context.Context) error { return err } + env, err := client.Environment().Get(ctx, &pb.EnvironmentGetRequest{ + Name: cmd.Environment, + }) + if err != nil { + return err + } + + if env.Environment.Production { + if ok := confirmation.Confirm(cmd.Force, "Are you sure you want to suspend a production environment? [yes/no]"); !ok { + return nil + } + } + _, err = client.Environment().Suspend(ctx, &pb.EnvironmentSuspendRequest{ Name: cmd.Environment, })