From 8aade1859bbfd6d0f61bffdf12a3f9846187dd1e Mon Sep 17 00:00:00 2001 From: Carlo Mandelli Date: Tue, 26 Nov 2019 11:07:10 +0100 Subject: [PATCH] cmd: add log get and delete subcommands --- cmd/agola/cmd/log.go | 28 ++++++ cmd/agola/cmd/logdelete.go | 115 ++++++++++++++++++++++++ cmd/agola/cmd/logget.go | 142 ++++++++++++++++++++++++++++++ services/gateway/client/client.go | 6 +- tests/setup_test.go | 4 +- 5 files changed, 291 insertions(+), 4 deletions(-) create mode 100644 cmd/agola/cmd/log.go create mode 100644 cmd/agola/cmd/logdelete.go create mode 100644 cmd/agola/cmd/logget.go diff --git a/cmd/agola/cmd/log.go b/cmd/agola/cmd/log.go new file mode 100644 index 0000000..007ca7c --- /dev/null +++ b/cmd/agola/cmd/log.go @@ -0,0 +1,28 @@ +// Copyright 2019 Sorint.lab +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied +// See the License for the specific language governing permissions and +// limitations under the License. + +package cmd + +import ( + "github.com/spf13/cobra" +) + +var cmdLog = &cobra.Command{ + Use: "log", + Short: "log", +} + +func init() { + cmdAgola.AddCommand(cmdLog) +} diff --git a/cmd/agola/cmd/logdelete.go b/cmd/agola/cmd/logdelete.go new file mode 100644 index 0000000..e4d6a9c --- /dev/null +++ b/cmd/agola/cmd/logdelete.go @@ -0,0 +1,115 @@ +// Copyright 2019 Sorint.lab +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied +// See the License for the specific language governing permissions and +// limitations under the License. + +package cmd + +import ( + "context" + + gwapitypes "agola.io/agola/services/gateway/api/types" + gwclient "agola.io/agola/services/gateway/client" + + "github.com/spf13/cobra" + errors "golang.org/x/xerrors" +) + +var cmdLogDelete = &cobra.Command{ + Use: "delete", + Short: "delete a setup/step log", + Run: func(cmd *cobra.Command, args []string) { + if err := logDelete(cmd, args); err != nil { + log.Fatalf("err: %v", err) + } + }, +} + +type logDeleteOptions struct { + runid string + taskname string + taskid string + step int + setup bool +} + +var logDeleteOpts logDeleteOptions + +func init() { + flags := cmdLogDelete.Flags() + + flags.StringVar(&logDeleteOpts.runid, "runid", "", "Run Id") + flags.StringVar(&logDeleteOpts.taskname, "taskname", "", "Task name") + flags.StringVar(&logDeleteOpts.taskid, "taskid", "", "Task Id") + flags.IntVar(&logDeleteOpts.step, "step", 0, "Step number") + flags.BoolVar(&logDeleteOpts.setup, "setup", false, "Setup step") + + if err := cmdLogDelete.MarkFlagRequired("runid"); err != nil { + log.Fatal(err) + } + + cmdLog.AddCommand(cmdLogDelete) +} + +func logDelete(cmd *cobra.Command, args []string) error { + + var taskid string + flags := cmd.Flags() + + if flags.Changed("taskname") && flags.Changed("taskid") { + return errors.Errorf(`only one of "--taskname" or "--taskid" can be provided`) + } + if !flags.Changed("taskname") && !flags.Changed("taskid") { + return errors.Errorf(`one of "--taskname" or "--taskid" must be provided`) + } + if flags.Changed("step") && flags.Changed("setup") { + return errors.Errorf(`only one of "--step" or "--setup" can be provided`) + } + if !flags.Changed("step") && !flags.Changed("setup") { + return errors.Errorf(`one of "--step" or "--setup" must be provided`) + } + if flags.Changed("step") && logDeleteOpts.step < 0 { + return errors.Errorf("%d is an invalid step number, it must be equal or greater than zero", logDeleteOpts.step) + } + + gwclient := gwclient.NewClient(gatewayURL, token) + + if flags.Changed("taskid") { + taskid = logDeleteOpts.taskid + } + if flags.Changed("taskname") { + var task *gwapitypes.RunResponseTask + var taskfound bool + + run, _, err := gwclient.GetRun(context.TODO(), logDeleteOpts.runid) + if err != nil { + return err + } + for _, t := range run.Tasks { + if t.Name == logDeleteOpts.taskname { + task = t + taskfound = true + break + } + } + if !taskfound { + return errors.Errorf("task %q not found in run %q", logDeleteOpts.taskname, logDeleteOpts.runid) + } + taskid = task.ID + } + log.Infof("deleting log") + if _, err := gwclient.DeleteLogs(context.TODO(), logDeleteOpts.runid, taskid, logDeleteOpts.setup, logDeleteOpts.step); err != nil { + return errors.Errorf("failed to delete log: %v", err) + } + + return nil +} diff --git a/cmd/agola/cmd/logget.go b/cmd/agola/cmd/logget.go new file mode 100644 index 0000000..f320c98 --- /dev/null +++ b/cmd/agola/cmd/logget.go @@ -0,0 +1,142 @@ +// Copyright 2019 Sorint.lab +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied +// See the License for the specific language governing permissions and +// limitations under the License. + +package cmd + +import ( + "context" + "io" + "os" + + gwapitypes "agola.io/agola/services/gateway/api/types" + gwclient "agola.io/agola/services/gateway/client" + + "github.com/spf13/cobra" + errors "golang.org/x/xerrors" +) + +var cmdLogGet = &cobra.Command{ + Use: "get", + Short: "get a setup/step log", + Run: func(cmd *cobra.Command, args []string) { + if err := logGet(cmd, args); err != nil { + log.Fatalf("err: %v", err) + } + }, +} + +type logGetOptions struct { + runid string + taskname string + taskid string + step int + setup bool + follow bool + output string +} + +var logGetOpts logGetOptions + +func init() { + flags := cmdLogGet.Flags() + + flags.StringVar(&logGetOpts.runid, "runid", "", "Run Id") + flags.StringVar(&logGetOpts.taskname, "taskname", "", "Task name") + flags.StringVar(&logGetOpts.taskid, "taskid", "", "Task Id") + flags.IntVar(&logGetOpts.step, "step", 0, "Step number") + flags.BoolVar(&logGetOpts.setup, "setup", false, "Setup step") + flags.BoolVar(&logGetOpts.follow, "follow", false, "Follow log stream") + flags.StringVar(&logGetOpts.output, "output", "", "Write output to file") + + if err := cmdLogGet.MarkFlagRequired("runid"); err != nil { + log.Fatal(err) + } + + cmdLog.AddCommand(cmdLogGet) +} + +func logGet(cmd *cobra.Command, args []string) error { + + var taskid string + flags := cmd.Flags() + + if flags.Changed("taskname") && flags.Changed("taskid") { + return errors.Errorf(`only one of "--taskname" or "--taskid" can be provided`) + } + if !flags.Changed("taskname") && !flags.Changed("taskid") { + return errors.Errorf(`one of "--taskname" or "--taskid" must be provided`) + } + if flags.Changed("step") && flags.Changed("setup") { + return errors.Errorf(`only one of "--step" or "--setup" can be provided`) + } + if !flags.Changed("step") && !flags.Changed("setup") { + return errors.Errorf(`one of "--step" or "--setup" must be provided`) + } + if flags.Changed("step") && logGetOpts.step < 0 { + return errors.Errorf("step number %d is invalid, it must be equal or greater than zero", logGetOpts.step) + } + if flags.Changed("follow") && flags.Changed("output") { + return errors.Errorf(`only one of "--follow" or "--output" can be provided`) + } + + gwclient := gwclient.NewClient(gatewayURL, token) + + if flags.Changed("taskid") { + taskid = logGetOpts.taskid + } + if flags.Changed("taskname") { + var task *gwapitypes.RunResponseTask + var taskfound bool + + run, _, err := gwclient.GetRun(context.TODO(), logGetOpts.runid) + if err != nil { + return err + } + for _, t := range run.Tasks { + if t.Name == logGetOpts.taskname { + task = t + taskfound = true + break + } + } + if !taskfound { + return errors.Errorf("task %q not found in run %q", logGetOpts.taskname, logGetOpts.runid) + } + taskid = task.ID + } + + log.Infof("getting log") + resp, err := gwclient.GetLogs(context.TODO(), logGetOpts.runid, taskid, logGetOpts.setup, logGetOpts.step, logGetOpts.follow) + if err != nil { + return errors.Errorf("failed to get log: %v", err) + } + defer resp.Body.Close() + + if flags.Changed("output") { + f, err := os.Create(logGetOpts.output) + if err != nil { + return err + } + defer f.Close() + if _, err := io.Copy(f, resp.Body); err != nil { + return errors.Errorf("failed to write log: %v", err) + } + } else { + if _, err := io.Copy(os.Stdout, resp.Body); err != nil { + return errors.Errorf("unexpected err: %v", err) + } + } + + return nil +} diff --git a/services/gateway/client/client.go b/services/gateway/client/client.go index ed64689..f8b4df8 100644 --- a/services/gateway/client/client.go +++ b/services/gateway/client/client.go @@ -490,7 +490,7 @@ func (c *Client) GetRuns(ctx context.Context, phaseFilter, resultFilter, groups, return getRunsResponse, resp, err } -func (c *Client) GetLogs(ctx context.Context, runID, taskID string, setup bool, step int) (*http.Response, error) { +func (c *Client) GetLogs(ctx context.Context, runID, taskID string, setup bool, step int, follow bool) (*http.Response, error) { q := url.Values{} q.Add("runID", runID) q.Add("taskID", taskID) @@ -499,7 +499,9 @@ func (c *Client) GetLogs(ctx context.Context, runID, taskID string, setup bool, } else { q.Add("step", strconv.Itoa(step)) } - + if follow { + q.Add("follow", "") + } return c.getResponse(ctx, "GET", "/logs", q, nil, nil) } diff --git a/tests/setup_test.go b/tests/setup_test.go index 0e0b3a4..e67ce8e 100644 --- a/tests/setup_test.go +++ b/tests/setup_test.go @@ -1078,7 +1078,7 @@ func TestDirectRunVariables(t *testing.T) { } } - resp, err := gwClient.GetLogs(ctx, run.ID, task.ID, false, 1) + resp, err := gwClient.GetLogs(ctx, run.ID, task.ID, false, 1, false) if err != nil { t.Fatalf("unexpected err: %v", err) } @@ -1266,7 +1266,7 @@ func TestDirectRunLogs(t *testing.T) { if tt.delete { _, err = gwClient.DeleteLogs(ctx, run.ID, task.ID, tt.setup, tt.step) } else { - _, err = gwClient.GetLogs(ctx, run.ID, task.ID, tt.setup, tt.step) + _, err = gwClient.GetLogs(ctx, run.ID, task.ID, tt.setup, tt.step, false) } if err != nil {