From 8cca50bc682a53da5170325a81d99bb2160c7f6e Mon Sep 17 00:00:00 2001 From: Luis San Martin Date: Fri, 20 May 2022 12:57:36 +0200 Subject: [PATCH 1/3] add support for ecs clusters in ocean --- internal/cmd/ocean/get_cluster.go | 1 + internal/cmd/ocean/get_cluster_ecs.go | 119 ++++++++++++++++++++++++++ 2 files changed, 120 insertions(+) create mode 100644 internal/cmd/ocean/get_cluster_ecs.go diff --git a/internal/cmd/ocean/get_cluster.go b/internal/cmd/ocean/get_cluster.go index 12a5f5c6..0ccfe6fe 100644 --- a/internal/cmd/ocean/get_cluster.go +++ b/internal/cmd/ocean/get_cluster.go @@ -39,6 +39,7 @@ func newCmdGetCluster(opts *CmdGetOptions) *CmdGetCluster { func (x *CmdGetCluster) initSubCommands() { commands := []func(*CmdGetClusterOptions) *cobra.Command{ NewCmdGetClusterKubernetes, + NewCmdGetClusterEcs, } for _, cmd := range commands { diff --git a/internal/cmd/ocean/get_cluster_ecs.go b/internal/cmd/ocean/get_cluster_ecs.go new file mode 100644 index 00000000..b4c47c23 --- /dev/null +++ b/internal/cmd/ocean/get_cluster_ecs.go @@ -0,0 +1,119 @@ +package ocean + +import ( + "context" + "sort" + + "github.com/spf13/cobra" + "github.com/spf13/pflag" + "github.com/spotinst/spotctl/internal/flags" + "github.com/spotinst/spotctl/internal/spot" + "github.com/spotinst/spotctl/internal/writer" +) + +type ( + CmdGetClusterEcs struct { + cmd *cobra.Command + opts CmdGetClusterEcsOptions + } + + CmdGetClusterEcsOptions struct { + *CmdGetClusterOptions + } +) + +func NewCmdGetClusterEcs(opts *CmdGetClusterOptions) *cobra.Command { + return newCmdGetClusterEcs(opts).cmd +} + +func newCmdGetClusterEcs(opts *CmdGetClusterOptions) *CmdGetClusterEcs { + var cmd CmdGetClusterEcs + + cmd.cmd = &cobra.Command{ + Use: "ecs", + Short: "Display one or many ecs clusters", + SilenceErrors: true, + SilenceUsage: true, + Aliases: []string{"ecs"}, + RunE: func(*cobra.Command, []string) error { + return cmd.Run(context.Background()) + }, + } + + cmd.opts.Init(cmd.cmd.Flags(), opts) + + return &cmd +} + +func (x *CmdGetClusterEcs) Run(ctx context.Context) error { + steps := []func(context.Context) error{ + x.survey, + x.log, + x.validate, + x.run, + } + + for _, step := range steps { + if err := step(ctx); err != nil { + return err + } + } + + return nil +} + +func (x *CmdGetClusterEcs) survey(ctx context.Context) error { + if x.opts.Noninteractive { + return nil + } + + return nil +} + +func (x *CmdGetClusterEcs) log(ctx context.Context) error { + flags.Log(x.cmd) + return nil +} + +func (x *CmdGetClusterEcs) validate(ctx context.Context) error { + return x.opts.Validate() +} + +func (x *CmdGetClusterEcs) run(ctx context.Context) error { + spotClientOpts := []spot.ClientOption{ + spot.WithCredentialsProfile(x.opts.Profile), + spot.WithDryRun(x.opts.DryRun), + } + + spotClient, err := x.opts.Clientset.NewSpotClient(spotClientOpts...) + if err != nil { + return err + } + + oceanClient, err := spotClient.Services().Ocean(x.opts.CloudProvider, spot.OrchestratorECS) + if err != nil { + return err + } + + clusters, err := oceanClient.ListClusters(ctx) + if err != nil { + return err + } + + w, err := x.opts.Clientset.NewWriter(writer.Format(x.opts.Output)) + if err != nil { + return err + } + + sort.Sort(&spot.OceanClustersSorter{Clusters: clusters}) + + return w.Write(clusters) +} + +func (x *CmdGetClusterEcsOptions) Init(fs *pflag.FlagSet, opts *CmdGetClusterOptions) { + x.CmdGetClusterOptions = opts +} + +func (x *CmdGetClusterEcsOptions) Validate() error { + return x.CmdGetClusterOptions.Validate() +} From b2982073a9d7a5cc84c7755e60c5585623b3a66b Mon Sep 17 00:00:00 2001 From: Luis San Martin Date: Mon, 15 Aug 2022 16:37:44 +0200 Subject: [PATCH 2/3] add logs output --- Makefile | 1 + internal/cmd/ocean/get_cluster.go | 1 + internal/cmd/ocean/get_cluster_logs.go | 130 +++++++++++++++++++++++++ 3 files changed, 132 insertions(+) create mode 100644 internal/cmd/ocean/get_cluster_logs.go diff --git a/Makefile b/Makefile index e8e9a978..26d7b845 100644 --- a/Makefile +++ b/Makefile @@ -44,6 +44,7 @@ todo: ## Show to-do items per file .PHONY: build build: fmt ## Build all commands $(Q) $(GO) build -trimpath -race -o dist/spotctl cmd/spotctl/main.go + cp dist/spotctl /home/pathcl/go/bin .PHONY: test test: fmt ## Run all tests diff --git a/internal/cmd/ocean/get_cluster.go b/internal/cmd/ocean/get_cluster.go index 0ccfe6fe..b728a2b9 100644 --- a/internal/cmd/ocean/get_cluster.go +++ b/internal/cmd/ocean/get_cluster.go @@ -40,6 +40,7 @@ func (x *CmdGetCluster) initSubCommands() { commands := []func(*CmdGetClusterOptions) *cobra.Command{ NewCmdGetClusterKubernetes, NewCmdGetClusterEcs, + NewCmdGetClusterLogs, } for _, cmd := range commands { diff --git a/internal/cmd/ocean/get_cluster_logs.go b/internal/cmd/ocean/get_cluster_logs.go new file mode 100644 index 00000000..bea61b9f --- /dev/null +++ b/internal/cmd/ocean/get_cluster_logs.go @@ -0,0 +1,130 @@ +package ocean + +import ( + "context" + "fmt" + "log" + "os" + "strconv" + "time" + + "github.com/spf13/cobra" + "github.com/spf13/pflag" + "github.com/spotinst/spotctl/internal/flags" + "github.com/spotinst/spotinst-sdk-go/service/ocean" + "github.com/spotinst/spotinst-sdk-go/service/ocean/providers/aws" + "github.com/spotinst/spotinst-sdk-go/spotinst" + "github.com/spotinst/spotinst-sdk-go/spotinst/session" +) + +type ( + CmdGetClusterLogs struct { + cmd *cobra.Command + opts CmdGetClusterLogsOptions + } + + CmdGetClusterLogsOptions struct { + *CmdGetClusterOptions + } +) + +func NewCmdGetClusterLogs(opts *CmdGetClusterOptions) *cobra.Command { + return newCmdGetClusterLogs(opts).cmd +} + +func newCmdGetClusterLogs(opts *CmdGetClusterOptions) *CmdGetClusterLogs { + var cmd CmdGetClusterLogs + + cmd.cmd = &cobra.Command{ + Use: "logs", + Short: "Get Logs from ocean ecs", + SilenceErrors: true, + SilenceUsage: true, + Aliases: []string{"ecs"}, + RunE: func(*cobra.Command, []string) error { + return cmd.Run(context.Background()) + }, + } + + cmd.opts.Init(cmd.cmd.Flags(), opts) + + return &cmd +} + +func (x *CmdGetClusterLogs) Run(ctx context.Context) error { + steps := []func(context.Context) error{ + x.survey, + x.log, + x.validate, + x.run, + } + + for _, step := range steps { + if err := step(ctx); err != nil { + return err + } + } + + return nil +} + +func (x *CmdGetClusterLogs) survey(ctx context.Context) error { + if x.opts.Noninteractive { + return nil + } + + return nil +} + +func (x *CmdGetClusterLogs) log(ctx context.Context) error { + flags.Log(x.cmd) + return nil +} + +func (x *CmdGetClusterLogs) validate(ctx context.Context) error { + return x.opts.Validate() +} + +// get cluster logs + +func (x *CmdGetClusterLogs) run(ctx context.Context) error { + sess := session.New() + svc := ocean.New(sess) + + today := strconv.FormatInt(time.Now().Sub(time.Unix(0, 0)).Milliseconds(), 10) + lastWeek := strconv.FormatInt(time.Now().Sub(time.Unix(0, 0)).Milliseconds()-604800000, 10) + + fmt.Println(lastWeek, today) + + // Get log events. + + cluster := os.Getenv("OCEAN_CLUSTER") + out, err := svc.CloudProviderAWS().GetLogEvents(ctx, &aws.GetLogEventsInput{ + ClusterID: spotinst.String(cluster), + FromDate: spotinst.String(lastWeek), + ToDate: spotinst.String(today), + }) + if err != nil { + log.Fatalf("spotinst: failed to get log events: %v", err) + } + + // Output log events, if any. + if len(out.Events) > 0 { + for _, event := range out.Events { + fmt.Printf("%s [%s] %s\n", + spotinst.TimeValue(event.CreatedAt).Format(time.RFC3339), + spotinst.StringValue(event.Severity), + spotinst.StringValue(event.Message)) + } + } + + return err +} + +func (x *CmdGetClusterLogsOptions) Init(fs *pflag.FlagSet, opts *CmdGetClusterOptions) { + x.CmdGetClusterOptions = opts +} + +func (x *CmdGetClusterLogsOptions) Validate() error { + return x.CmdGetClusterOptions.Validate() +} From acf51ddc3c5229bdf1f5ca39f6c851f29eb19bb8 Mon Sep 17 00:00:00 2001 From: Luis San Martin Date: Mon, 15 Aug 2022 22:45:27 +0200 Subject: [PATCH 3/3] add edit for ecs clusters --- Dockerfile | 14 +++ internal/cmd/ocean/edit_cluster.go | 1 + internal/cmd/ocean/edit_cluster_ecs.go | 166 +++++++++++++++++++++++++ internal/cmd/ocean/get_cluster_logs.go | 4 +- 4 files changed, 184 insertions(+), 1 deletion(-) create mode 100644 Dockerfile create mode 100644 internal/cmd/ocean/edit_cluster_ecs.go diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 00000000..8bdfd3dd --- /dev/null +++ b/Dockerfile @@ -0,0 +1,14 @@ +# Start by building the gflication. +FROM golang:1.17-bullseye as build + +WORKDIR /go/src/spotctl +ADD . /go/src/spotctl + +RUN go get -d -v ./... + +RUN go build -o /go/bin/spotctl + +# Now copy it into our base image. +FROM gcr.io/distroless/base-debian11 +COPY --from=build /go/bin/spotctl / +ENTRYPOINT ["/spotctl"] diff --git a/internal/cmd/ocean/edit_cluster.go b/internal/cmd/ocean/edit_cluster.go index 6261a76f..244ffbc3 100644 --- a/internal/cmd/ocean/edit_cluster.go +++ b/internal/cmd/ocean/edit_cluster.go @@ -39,6 +39,7 @@ func newCmdEditCluster(opts *CmdEditOptions) *CmdEditCluster { func (x *CmdEditCluster) initSubCommands() { commands := []func(*CmdEditClusterOptions) *cobra.Command{ NewCmdEditClusterKubernetes, + NewCmdEditClusterECS, } for _, cmd := range commands { diff --git a/internal/cmd/ocean/edit_cluster_ecs.go b/internal/cmd/ocean/edit_cluster_ecs.go new file mode 100644 index 00000000..e1f4155d --- /dev/null +++ b/internal/cmd/ocean/edit_cluster_ecs.go @@ -0,0 +1,166 @@ +package ocean + +import ( + "bytes" + "context" + "encoding/json" + "fmt" + "os" + + "github.com/spf13/cobra" + "github.com/spf13/pflag" + "github.com/spotinst/spotctl/internal/errors" + "github.com/spotinst/spotctl/internal/flags" + "github.com/spotinst/spotctl/internal/spot" +) + +type ( + CmdEditClusterECS struct { + cmd *cobra.Command + opts CmdEditClusterECSOptions + } + + CmdEditClusterECSOptions struct { + *CmdEditClusterOptions + + ClusterID string + } +) + +func NewCmdEditClusterECS(opts *CmdEditClusterOptions) *cobra.Command { + return newCmdEditClusterECS(opts).cmd +} + +func newCmdEditClusterECS(opts *CmdEditClusterOptions) *CmdEditClusterECS { + var cmd CmdEditClusterECS + + cmd.cmd = &cobra.Command{ + Use: "ecs", + Short: "Edit a ECS cluster", + SilenceErrors: true, + SilenceUsage: true, + Aliases: []string{"e"}, + RunE: func(*cobra.Command, []string) error { + return cmd.Run(context.Background()) + }, + } + + cmd.opts.Init(cmd.cmd.Flags(), opts) + + return &cmd +} + +func (x *CmdEditClusterECS) Run(ctx context.Context) error { + steps := []func(context.Context) error{ + x.survey, + x.log, + x.validate, + x.run, + } + + for _, step := range steps { + if err := step(ctx); err != nil { + return err + } + } + + return nil +} + +func (x *CmdEditClusterECS) survey(ctx context.Context) error { + if x.opts.Noninteractive { + return nil + } + + return nil +} + +func (x *CmdEditClusterECS) log(ctx context.Context) error { + flags.Log(x.cmd) + return nil +} + +func (x *CmdEditClusterECS) validate(ctx context.Context) error { + return x.opts.Validate() +} + +func (x *CmdEditClusterECS) run(ctx context.Context) error { + spotClientOpts := []spot.ClientOption{ + spot.WithCredentialsProfile(x.opts.Profile), + spot.WithDryRun(x.opts.DryRun), + } + + spotClient, err := x.opts.Clientset.NewSpotClient(spotClientOpts...) + if err != nil { + return err + } + + oceanClient, err := spotClient.Services().Ocean(x.opts.CloudProvider, spot.OrchestratorECS) + if err != nil { + return err + } + + cluster, err := oceanClient.GetCluster(ctx, x.opts.ClusterID) + if err != nil { + return err + } + + rawJSON, err := json.MarshalIndent(cluster.Obj, "", " ") + if err != nil { + return err + } + + editor, err := x.opts.Clientset.NewEditor() + if err != nil { + return err + } + + editedJSON, path, err := editor.OpenTempFile(ctx, "spotinst", ".json", bytes.NewBuffer(rawJSON)) + if err != nil { + return err + } + + if bytes.Equal(rawJSON, editedJSON) { + os.Remove(path) + fmt.Fprintln(x.opts.Out, "Edit cancelled, no changes made.") + return nil + } + + if err := json.Unmarshal(editedJSON, cluster.Obj); err != nil { + return err + } + + _, err = oceanClient.UpdateCluster(ctx, cluster) + return err +} + +func (x *CmdEditClusterECSOptions) Init(fs *pflag.FlagSet, opts *CmdEditClusterOptions) { + x.initDefaults(opts) + x.initFlags(fs) +} + +func (x *CmdEditClusterECSOptions) initDefaults(opts *CmdEditClusterOptions) { + x.CmdEditClusterOptions = opts +} + +func (x *CmdEditClusterECSOptions) initFlags(fs *pflag.FlagSet) { + fs.StringVar(&x.ClusterID, flags.FlagOceanClusterID, x.ClusterID, "id of the cluster") +} + +func (x *CmdEditClusterECSOptions) Validate() error { + errg := errors.NewErrorGroup() + + if err := x.CmdEditClusterOptions.Validate(); err != nil { + errg.Add(err) + } + + if x.ClusterID == "" { + errg.Add(errors.Required("ClusterID")) + } + + if errg.Len() > 0 { + return errg + } + + return nil +} diff --git a/internal/cmd/ocean/get_cluster_logs.go b/internal/cmd/ocean/get_cluster_logs.go index bea61b9f..845094d5 100644 --- a/internal/cmd/ocean/get_cluster_logs.go +++ b/internal/cmd/ocean/get_cluster_logs.go @@ -94,7 +94,9 @@ func (x *CmdGetClusterLogs) run(ctx context.Context) error { today := strconv.FormatInt(time.Now().Sub(time.Unix(0, 0)).Milliseconds(), 10) lastWeek := strconv.FormatInt(time.Now().Sub(time.Unix(0, 0)).Milliseconds()-604800000, 10) - fmt.Println(lastWeek, today) + t := time.Now().Unix() + timeT := time.Unix(t, 0) + fmt.Printf("From 1 week ago until: %s\n", timeT) // Get log events.