From 320bf76c4a27bb77d74f2c7d43da3e42ac03f0a3 Mon Sep 17 00:00:00 2001 From: codebfu Date: Wed, 4 Mar 2020 09:26:02 +0100 Subject: [PATCH] feat(cdsctl): action usage command (#5025) --- cli/cdsctl/action.go | 42 +++++++++++++++++ engine/api/action.go | 8 ++-- engine/api/action/usage.go | 45 +++---------------- sdk/action.go | 33 ++++++++++++++ sdk/cdsclient/client_action.go | 11 +++++ sdk/cdsclient/interface.go | 1 + .../mock_cdsclient/interface_mock.go | 40 +++++++++++++++++ 7 files changed, 137 insertions(+), 43 deletions(-) diff --git a/cli/cdsctl/action.go b/cli/cdsctl/action.go index 413d00aebf..20e97d10ed 100644 --- a/cli/cdsctl/action.go +++ b/cli/cdsctl/action.go @@ -5,6 +5,7 @@ import ( "io/ioutil" "path" "time" + "strings" "github.com/spf13/cobra" @@ -28,6 +29,7 @@ var actionBuiltinCmd = cli.Command{ func action() *cobra.Command { return cli.NewCommand(actionCmd, nil, []*cobra.Command{ cli.NewListCommand(actionListCmd, actionListRun, nil), + cli.NewListCommand(actionUsageCmd, actionUsageRun, nil), cli.NewGetCommand(actionShowCmd, actionShowRun, nil), cli.NewCommand(actionDeleteCmd, actionDeleteRun, nil), cli.NewCommand(actionDocCmd, actionDocRun, nil), @@ -87,6 +89,46 @@ func actionListRun(v cli.Values) (cli.ListResult, error) { return cli.AsListResult(ads), nil } +var actionUsageCmd = cli.Command{ + Name: "usage", + Short: "CDS action usage", + Args: []cli.Arg{ + {Name: "action-path"}, + }, +} + +func actionUsageRun(v cli.Values) (cli.ListResult, error) { + groupName, actionName, err := cli.ParsePath(v.GetString("action-path")) + if err != nil { + return nil, err + } + + usages, err := client.ActionUsage(groupName, actionName) + if err != nil { + return nil, err + } + + type ActionUsageDisplay struct { + Type string `cli:"Type"` + Path string `cli:"Path"` + } + + au := []ActionUsageDisplay{} + for _, v := range usages.Pipelines { + au = append(au, ActionUsageDisplay{ + Type: "pipeline", + Path: strings.Replace(fmt.Sprintf("%s - %s - %s", v.ProjectName, v.PipelineName, v.ActionName)," "," ",-1), + }) + } + for _, v := range usages.Actions { + au = append(au, ActionUsageDisplay{ + Type: "action", + Path: fmt.Sprintf("%s/%s", v.GroupName, v.ParentActionName), + }) + } + return cli.AsListResult(au), nil +} + var actionShowCmd = cli.Command{ Name: "show", Short: "Show a CDS action", diff --git a/engine/api/action.go b/engine/api/action.go index 5eb07766ef..9c1a8242bb 100644 --- a/engine/api/action.go +++ b/engine/api/action.go @@ -823,8 +823,8 @@ func (api *API) getActionBuiltinUsageHandler() service.Handler { } } -func getActionUsage(ctx context.Context, db gorp.SqlExecutor, store cache.Store, a *sdk.Action) (action.Usage, error) { - var usage action.Usage +func getActionUsage(ctx context.Context, db gorp.SqlExecutor, store cache.Store, a *sdk.Action) (sdk.ActionUsages, error) { + var usage sdk.ActionUsages var err error usage.Pipelines, err = action.GetPipelineUsages(db, group.SharedInfraGroup.ID, a.ID) if err != nil { @@ -848,7 +848,7 @@ func getActionUsage(ctx context.Context, db gorp.SqlExecutor, store cache.Store, mProjectIDs[ps[i].ID] = struct{}{} } - filteredPipelines := make([]action.UsagePipeline, 0, len(usage.Pipelines)) + filteredPipelines := make([]sdk.UsagePipeline, 0, len(usage.Pipelines)) for i := range usage.Pipelines { if _, ok := mProjectIDs[usage.Pipelines[i].ProjectID]; ok { filteredPipelines = append(filteredPipelines, usage.Pipelines[i]) @@ -863,7 +863,7 @@ func getActionUsage(ctx context.Context, db gorp.SqlExecutor, store cache.Store, mGroupIDs[groupIDs[i]] = struct{}{} } - filteredActions := make([]action.UsageAction, 0, len(usage.Actions)) + filteredActions := make([]sdk.UsageAction, 0, len(usage.Actions)) for i := range usage.Actions { if _, ok := mGroupIDs[usage.Actions[i].GroupID]; ok { filteredActions = append(filteredActions, usage.Actions[i]) diff --git a/engine/api/action/usage.go b/engine/api/action/usage.go index f34c8818f3..1d3b6d78e9 100644 --- a/engine/api/action/usage.go +++ b/engine/api/action/usage.go @@ -6,30 +6,8 @@ import ( "github.com/ovh/cds/sdk" ) -// Usage for action. -type Usage struct { - Pipelines []UsagePipeline `json:"pipelines"` - Actions []UsageAction `json:"actions"` -} - -// UsagePipeline represent a pipeline using an action. -type UsagePipeline struct { - ProjectID int64 `json:"project_id"` - ProjectKey string `json:"project_key"` - ProjectName string `json:"project_name"` - PipelineID int64 `json:"pipeline_id"` - PipelineName string `json:"pipeline_name"` - StageID int64 `json:"stage_id"` - StageName string `json:"stage_name"` - JobID int64 `json:"job_id"` - JobName string `json:"job_name"` - ActionID int64 `json:"action_id"` - ActionName string `json:"action_name"` - Warning bool `json:"warning"` -} - // GetPipelineUsages returns the list of pipelines using an action -func GetPipelineUsages(db gorp.SqlExecutor, sharedInfraGroupID, actionID int64) ([]UsagePipeline, error) { +func GetPipelineUsages(db gorp.SqlExecutor, sharedInfraGroupID, actionID int64) ([]sdk.UsagePipeline, error) { rows, err := db.Query(` SELECT DISTINCT project.id, project.projectKey, project.name, @@ -54,9 +32,9 @@ func GetPipelineUsages(db gorp.SqlExecutor, sharedInfraGroupID, actionID int64) } defer rows.Close() - us := []UsagePipeline{} + us := []sdk.UsagePipeline{} for rows.Next() { - var u UsagePipeline + var u sdk.UsagePipeline if err := rows.Scan( &u.ProjectID, &u.ProjectKey, &u.ProjectName, &u.PipelineID, &u.PipelineName, @@ -73,19 +51,8 @@ func GetPipelineUsages(db gorp.SqlExecutor, sharedInfraGroupID, actionID int64) return us, nil } -// UsageAction represent a action using an action. -type UsageAction struct { - GroupID int64 `json:"group_id"` - GroupName string `json:"group_name"` - ParentActionID int64 `json:"parent_action_id"` - ParentActionName string `json:"parent_action_name"` - ActionID int64 `json:"action_id"` - ActionName string `json:"action_name"` - Warning bool `json:"warning"` -} - // GetActionUsages returns the list of actions using an action -func GetActionUsages(db gorp.SqlExecutor, sharedInfraGroupID, actionID int64) ([]UsageAction, error) { +func GetActionUsages(db gorp.SqlExecutor, sharedInfraGroupID, actionID int64) ([]sdk.UsageAction, error) { rows, err := db.Query(` SELECT DISTINCT "group".id, "group".name, @@ -104,9 +71,9 @@ func GetActionUsages(db gorp.SqlExecutor, sharedInfraGroupID, actionID int64) ([ } defer rows.Close() - us := []UsageAction{} + us := []sdk.UsageAction{} for rows.Next() { - var u UsageAction + var u sdk.UsageAction if err := rows.Scan( &u.GroupID, &u.GroupName, &u.ParentActionID, &u.ParentActionName, diff --git a/sdk/action.go b/sdk/action.go index 3197862636..f5a503c3e0 100644 --- a/sdk/action.go +++ b/sdk/action.go @@ -60,6 +60,39 @@ type Action struct { Editable bool `json:"editable,omitempty" db:"-"` } +// UsageAction represent a action using an action. +type UsageAction struct { + GroupID int64 `json:"group_id"` + GroupName string `json:"group_name"` + ParentActionID int64 `json:"parent_action_id"` + ParentActionName string `json:"parent_action_name"` + ActionID int64 `json:"action_id"` + ActionName string `json:"action_name"` + Warning bool `json:"warning"` +} + +// ActionUsages for action. +type ActionUsages struct { + Pipelines []UsagePipeline `json:"pipelines"` + Actions []UsageAction `json:"actions"` +} + +// UsagePipeline represent a pipeline using an action. +type UsagePipeline struct { + ProjectID int64 `json:"project_id"` + ProjectKey string `json:"project_key"` + ProjectName string `json:"project_name"` + PipelineID int64 `json:"pipeline_id"` + PipelineName string `json:"pipeline_name"` + StageID int64 `json:"stage_id"` + StageName string `json:"stage_name"` + JobID int64 `json:"job_id"` + JobName string `json:"job_name"` + ActionID int64 `json:"action_id"` + ActionName string `json:"action_name"` + Warning bool `json:"warning"` +} + // Value returns driver.Value from action. func (a Action) Value() (driver.Value, error) { j, err := json.Marshal(a) diff --git a/sdk/cdsclient/client_action.go b/sdk/cdsclient/client_action.go index 4996cfaf43..013da496be 100644 --- a/sdk/cdsclient/client_action.go +++ b/sdk/cdsclient/client_action.go @@ -35,6 +35,17 @@ func (c *client) ActionGet(groupName, name string, mods ...RequestModifier) (*sd return &a, nil } +func (c *client) ActionUsage(groupName, name string, mods ...RequestModifier) (*sdk.ActionUsages, error) { + var a sdk.ActionUsages + + path := fmt.Sprintf("/action/%s/%s/usage", groupName, name) + if _, err := c.GetJSON(context.Background(), path, &a, mods...); err != nil { + return nil, err + } + + return &a, nil +} + func (c *client) ActionList() ([]sdk.Action, error) { actions := []sdk.Action{} if _, err := c.GetJSON(context.Background(), "/action", &actions); err != nil { diff --git a/sdk/cdsclient/interface.go b/sdk/cdsclient/interface.go index 71257199e6..57f539ff87 100644 --- a/sdk/cdsclient/interface.go +++ b/sdk/cdsclient/interface.go @@ -156,6 +156,7 @@ type DownloadClient interface { type ActionClient interface { ActionDelete(groupName, name string) error ActionGet(groupName, name string, mods ...RequestModifier) (*sdk.Action, error) + ActionUsage(groupName, name string, mods ...RequestModifier) (*sdk.ActionUsages, error) ActionList() ([]sdk.Action, error) ActionImport(content io.Reader, format string) error ActionExport(groupName, name string, format string) ([]byte, error) diff --git a/sdk/cdsclient/mock_cdsclient/interface_mock.go b/sdk/cdsclient/mock_cdsclient/interface_mock.go index e08c536995..a81197616c 100644 --- a/sdk/cdsclient/mock_cdsclient/interface_mock.go +++ b/sdk/cdsclient/mock_cdsclient/interface_mock.go @@ -1799,6 +1799,26 @@ func (mr *MockActionClientMockRecorder) ActionGet(groupName, name interface{}, m return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ActionGet", reflect.TypeOf((*MockActionClient)(nil).ActionGet), varargs...) } +// ActionUsage mocks base method +func (m *MockActionClient) ActionUsage(groupName, name string, mods ...cdsclient.RequestModifier) (*sdk.ActionUsages, error) { + m.ctrl.T.Helper() + varargs := []interface{}{groupName, name} + for _, a := range mods { + varargs = append(varargs, a) + } + ret := m.ctrl.Call(m, "ActionUsage", varargs...) + ret0, _ := ret[0].(*sdk.ActionUsages) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// ActionUsage indicates an expected call of ActionUsage +func (mr *MockActionClientMockRecorder) ActionUsage(groupName, name interface{}, mods ...interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + varargs := append([]interface{}{groupName, name}, mods...) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ActionUsage", reflect.TypeOf((*MockActionClient)(nil).ActionUsage), varargs...) +} + // ActionList mocks base method func (m *MockActionClient) ActionList() ([]sdk.Action, error) { m.ctrl.T.Helper() @@ -4498,6 +4518,26 @@ func (mr *MockInterfaceMockRecorder) ActionGet(groupName, name interface{}, mods return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ActionGet", reflect.TypeOf((*MockInterface)(nil).ActionGet), varargs...) } +// ActionUsage mocks base method +func (m *MockInterface) ActionUsage(groupName, name string, mods ...cdsclient.RequestModifier) (*sdk.ActionUsages, error) { + m.ctrl.T.Helper() + varargs := []interface{}{groupName, name} + for _, a := range mods { + varargs = append(varargs, a) + } + ret := m.ctrl.Call(m, "ActionUsage", varargs...) + ret0, _ := ret[0].(*sdk.ActionUsages) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// ActionUsage indicates an expected call of ActionUsage +func (mr *MockInterfaceMockRecorder) ActionUsage(groupName, name interface{}, mods ...interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + varargs := append([]interface{}{groupName, name}, mods...) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ActionUsage", reflect.TypeOf((*MockInterface)(nil).ActionUsage), varargs...) +} + // ActionList mocks base method func (m *MockInterface) ActionList() ([]sdk.Action, error) { m.ctrl.T.Helper()