* add job list and update  job status command to mmctl
Этот коммит содержится в:
Ben Cooke
2024-06-17 12:07:05 -04:00
коммит произвёл GitHub
родитель 5894abc36e
Коммит 9187c772b6
38 изменённых файлов: 1423 добавлений и 101 удалений

Просмотреть файл

@@ -270,7 +270,7 @@ func exportDownloadCmdF(c client.Client, command *cobra.Command, args []string)
}
func exportJobListCmdF(c client.Client, command *cobra.Command, args []string) error {
return jobListCmdF(c, command, model.JobTypeExportProcess)
return jobListCmdF(c, command, model.JobTypeExportProcess, "")
}
func exportJobShowCmdF(c client.Client, command *cobra.Command, args []string) error {

Просмотреть файл

@@ -107,7 +107,7 @@ func extractJobShowCmdF(c client.Client, command *cobra.Command, args []string)
}
func extractJobListCmdF(c client.Client, command *cobra.Command, args []string) error {
return jobListCmdF(c, command, model.JobTypeExtractContent)
return jobListCmdF(c, command, model.JobTypeExtractContent, "")
}
func printExtractContentJob(job *model.Job) {

Просмотреть файл

@@ -308,24 +308,6 @@ func importProcessCmdF(c client.Client, command *cobra.Command, args []string) e
return nil
}
func printJob(job *model.Job) {
if job.StartAt > 0 {
printer.PrintT(fmt.Sprintf(` ID: {{.Id}}
Status: {{.Status}}
Created: %s
Started: %s
Data: {{.Data}}
`,
time.Unix(job.CreateAt/1000, 0), time.Unix(job.StartAt/1000, 0)), job)
} else {
printer.PrintT(fmt.Sprintf(` ID: {{.Id}}
Status: {{.Status}}
Created: %s
`,
time.Unix(job.CreateAt/1000, 0)), job)
}
}
func importJobShowCmdF(c client.Client, command *cobra.Command, args []string) error {
job, _, err := c.GetJob(context.TODO(), args[0])
if err != nil {
@@ -337,53 +319,8 @@ func importJobShowCmdF(c client.Client, command *cobra.Command, args []string) e
return nil
}
func jobListCmdF(c client.Client, command *cobra.Command, jobType string) error {
page, err := command.Flags().GetInt("page")
if err != nil {
return err
}
perPage, err := command.Flags().GetInt("per-page")
if err != nil {
return err
}
showAll, err := command.Flags().GetBool("all")
if err != nil {
return err
}
if showAll {
page = 0
}
for {
jobs, _, err := c.GetJobsByType(context.TODO(), jobType, page, perPage)
if err != nil {
return fmt.Errorf("failed to get jobs: %w", err)
}
if len(jobs) == 0 {
if !showAll || page == 0 {
printer.Print("No jobs found")
}
return nil
}
for _, job := range jobs {
printJob(job)
}
if !showAll {
break
}
page++
}
return nil
}
func importJobListCmdF(c client.Client, command *cobra.Command, args []string) error {
return jobListCmdF(c, command, model.JobTypeImportProcess)
return jobListCmdF(c, command, model.JobTypeImportProcess, "")
}
type Statistics struct {

Просмотреть файл

@@ -163,7 +163,7 @@ func (s *MmctlUnitTestSuite) TestImportJobListCmdF() {
s.client.
EXPECT().
GetJobsByType(context.TODO(), model.JobTypeImportProcess, 0, perPage).
GetJobs(context.TODO(), model.JobTypeImportProcess, "", 0, perPage).
Return(mockJobs, &model.Response{}, nil).
Times(1)
@@ -196,7 +196,7 @@ func (s *MmctlUnitTestSuite) TestImportJobListCmdF() {
s.client.
EXPECT().
GetJobsByType(context.TODO(), model.JobTypeImportProcess, 0, perPage).
GetJobs(context.TODO(), model.JobTypeImportProcess, "", 0, perPage).
Return(mockJobs, &model.Response{}, nil).
Times(1)

202
server/cmd/mmctl/commands/job.go Обычный файл
Просмотреть файл

@@ -0,0 +1,202 @@
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
// See LICENSE.txt for license information.
package commands
import (
"context"
"fmt"
"time"
"github.com/hashicorp/go-multierror"
"github.com/mattermost/mattermost/server/public/model"
"github.com/mattermost/mattermost/server/v8/cmd/mmctl/client"
"github.com/mattermost/mattermost/server/v8/cmd/mmctl/printer"
"github.com/spf13/cobra"
)
var JobCmd = &cobra.Command{
Use: "job",
Short: "Management of jobs",
}
var listJobsCmd = &cobra.Command{
Use: "list",
Short: "List the latest jobs",
Example: ` job list
job list --ids jobID1,jobID2
job list --type ldap_sync --status success
job list --type ldap_sync --status success --page 0 --per-page 10`,
Args: cobra.NoArgs,
RunE: withClient(listJobsCmdF),
}
var updateJobCmd = &cobra.Command{
Use: "update [job] [status]",
Short: "Update the status of a job",
Long: `Update the status of a job. The following restrictions are in place:
- in_progress -> pending
- in_progress | pending -> cancel_requested
- cancel_requested -> canceled
Those restriction can be bypassed with --force=true but the only statuses you can go to are: pending, cancel_requested and canceled. This can have unexpected consequences and should be used with caution.`,
Example: ` job update myJobID pending
job update myJobID pending --force true
job update myJobID canceled --force true`,
Args: cobra.MinimumNArgs(2),
RunE: withClient(updateJobCmdF),
}
func init() {
listJobsCmd.Flags().Int("page", 0, "Page number to fetch for the list of import jobs")
listJobsCmd.Flags().Int("per-page", 5, "Number of import jobs to be fetched")
listJobsCmd.Flags().Bool("all", false, "Fetch all import jobs. --page flag will be ignored if provided")
listJobsCmd.Flags().StringSlice("ids", nil, "Comma-separated list of job IDs to which the operation will be applied. All other flags are ignored")
listJobsCmd.Flags().String("status", "", "Filter by job status")
listJobsCmd.Flags().String("type", "", "Filter by job type")
updateJobCmd.Flags().Bool("force", false, "Setting a job status is restricted to certain statuses. You can overwrite these restrictions by using --force. This might cause unexpected behaviour on your Mattermost Server. Use this option with caution.")
JobCmd.AddCommand(
listJobsCmd,
updateJobCmd,
)
RootCmd.AddCommand(JobCmd)
}
func listJobsCmdF(c client.Client, cmd *cobra.Command, args []string) error {
ids, err := cmd.Flags().GetStringSlice("ids")
if err != nil {
return err
}
jobType, err := cmd.Flags().GetString("type")
if err != nil {
return err
}
status, err := cmd.Flags().GetString("status")
if err != nil {
return err
}
if len(ids) > 0 {
jobs := make([]*model.Job, 0, len(ids))
var result *multierror.Error
for _, id := range ids {
isValidId := model.IsValidId(id)
if !isValidId {
result = multierror.Append(result, fmt.Errorf("invalid job ID: %s", id))
continue
}
job, _, err := c.GetJob(context.TODO(), id)
if err != nil {
result = multierror.Append(result, err)
continue
}
jobs = append(jobs, job)
}
for _, job := range jobs {
printJob(job)
}
return result.ErrorOrNil()
}
return jobListCmdF(c, cmd, jobType, status)
}
func updateJobCmdF(c client.Client, cmd *cobra.Command, args []string) error {
force, err := cmd.Flags().GetBool("force")
if err != nil {
return err
}
jobId := args[0]
if !model.IsValidId(jobId) {
return fmt.Errorf("invalid job ID: %s", jobId)
}
status := args[1]
if !model.IsValidJobStatus(status) {
return fmt.Errorf("invalid job status: %s", status)
}
_, err = c.UpdateJobStatus(context.TODO(), jobId, status, force)
if err != nil {
return err
}
return nil
}
func jobListCmdF(c client.Client, command *cobra.Command, jobType string, status string) error {
page, err := command.Flags().GetInt("page")
if err != nil {
return err
}
perPage, err := command.Flags().GetInt("per-page")
if err != nil {
return err
}
showAll, err := command.Flags().GetBool("all")
if err != nil {
return err
}
if showAll {
page = 0
}
if jobType != "" && !model.IsValidJobType(jobType) {
return fmt.Errorf("invalid job type: %s", jobType)
}
if status != "" && !model.IsValidJobStatus(status) {
return fmt.Errorf("invalid job status: %s", status)
}
for {
jobs, _, err := c.GetJobs(context.TODO(), jobType, status, page, perPage)
if err != nil {
return fmt.Errorf("failed to get jobs: %w", err)
}
if len(jobs) == 0 {
if !showAll || page == 0 {
printer.Print("No jobs found")
}
return nil
}
for _, job := range jobs {
printJob(job)
}
if !showAll {
break
}
page++
}
return nil
}
func printJob(job *model.Job) {
if job.StartAt > 0 {
printer.PrintT(fmt.Sprintf(` ID: {{.Id}}
Type: {{.Type}}
Status: {{.Status}}
Created: %s
Started: %s
Data: {{.Data}}
`,
time.Unix(job.CreateAt/1000, 0), time.Unix(job.StartAt/1000, 0)), job)
} else {
printer.PrintT(fmt.Sprintf(` ID: {{.Id}}
Status: {{.Status}}
Created: %s
`,
time.Unix(job.CreateAt/1000, 0)), job)
}
}

204
server/cmd/mmctl/commands/job_test.go Обычный файл
Просмотреть файл

@@ -0,0 +1,204 @@
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
// See LICENSE.txt for license information.
package commands
import (
"context"
"github.com/mattermost/mattermost/server/public/model"
"github.com/mattermost/mattermost/server/v8/cmd/mmctl/printer"
"github.com/spf13/cobra"
)
func (s *MmctlUnitTestSuite) TestListJobsCmdF() {
s.Run("no jobs found", func() {
printer.Clean()
var mockJobs []*model.Job
cmd := &cobra.Command{}
perPage := 10
cmd.Flags().Int("page", 0, "")
cmd.Flags().Int("per-page", perPage, "")
cmd.Flags().Bool("all", false, "")
cmd.Flags().StringSlice("ids", []string{}, "")
cmd.Flags().String("status", "", "")
cmd.Flags().String("type", "", "")
s.client.
EXPECT().
GetJobs(context.TODO(), "", "", 0, perPage).
Return(mockJobs, &model.Response{}, nil).
Times(1)
err := listJobsCmdF(s.client, cmd, nil)
s.Require().Nil(err)
s.Len(printer.GetLines(), 1)
s.Empty(printer.GetErrorLines())
s.Equal("No jobs found", printer.GetLines()[0])
})
s.Run("3 jobs found", func() {
printer.Clean()
mockJobs := []*model.Job{
{
Id: model.NewId(),
},
{
Id: model.NewId(),
},
{
Id: model.NewId(),
},
}
cmd := &cobra.Command{}
perPage := 3
cmd.Flags().Int("page", 0, "")
cmd.Flags().Int("per-page", perPage, "")
cmd.Flags().Bool("all", false, "")
cmd.Flags().StringSlice("ids", []string{}, "")
cmd.Flags().String("status", "", "")
cmd.Flags().String("type", "", "")
s.client.
EXPECT().
GetJobs(context.TODO(), "", "", 0, perPage).
Return(mockJobs, &model.Response{}, nil).
Times(1)
err := listJobsCmdF(s.client, cmd, nil)
s.Require().Nil(err)
s.Len(printer.GetLines(), len(mockJobs))
s.Empty(printer.GetErrorLines())
for i, line := range printer.GetLines() {
s.Equal(mockJobs[i], line.(*model.Job))
}
})
s.Run("return 1 job using ids flag", func() {
printer.Clean()
id := model.NewId()
mockJob := &model.Job{
Id: id,
}
cmd := &cobra.Command{}
perPage := 3
cmd.Flags().Int("page", 0, "")
cmd.Flags().Int("per-page", perPage, "")
cmd.Flags().Bool("all", false, "")
cmd.Flags().StringSlice("ids", []string{id}, "")
cmd.Flags().String("status", "", "")
cmd.Flags().String("type", "", "")
s.client.
EXPECT().
GetJob(context.TODO(), id).
Return(mockJob, &model.Response{}, nil).
Times(1)
err := listJobsCmdF(s.client, cmd, nil)
s.Require().Nil(err)
s.Len(printer.GetLines(), 1)
s.Empty(printer.GetErrorLines())
for _, line := range printer.GetLines() {
s.Equal(mockJob, line.(*model.Job))
}
})
s.Run("return 2 jobs by status", func() {
printer.Clean()
mockJobs := []*model.Job{
{
Id: model.NewId(),
Status: model.JobStatusSuccess,
},
{
Id: model.NewId(),
Status: model.JobStatusSuccess,
},
}
cmd := &cobra.Command{}
perPage := 2
cmd.Flags().Int("page", 0, "")
cmd.Flags().Int("per-page", perPage, "")
cmd.Flags().Bool("all", false, "")
cmd.Flags().String("status", model.JobStatusSuccess, "")
cmd.Flags().StringSlice("ids", []string{}, "")
cmd.Flags().String("type", "", "")
s.client.
EXPECT().
GetJobs(context.TODO(), "", model.JobStatusSuccess, 0, perPage).
Return(mockJobs, &model.Response{}, nil).
Times(1)
err := listJobsCmdF(s.client, cmd, nil)
s.Require().Nil(err)
s.Len(printer.GetLines(), len(mockJobs))
s.Empty(printer.GetErrorLines())
for i, line := range printer.GetLines() {
s.Equal(mockJobs[i], line.(*model.Job))
}
})
s.Run("return 2 jobs by type", func() {
printer.Clean()
mockJobs := []*model.Job{
{
Id: model.NewId(),
Type: model.JobTypeDataRetention,
},
{
Id: model.NewId(),
Type: model.JobTypeDataRetention,
},
}
cmd := &cobra.Command{}
perPage := 2
cmd.Flags().Int("page", 0, "")
cmd.Flags().Int("per-page", perPage, "")
cmd.Flags().Bool("all", false, "")
cmd.Flags().String("type", model.JobTypeDataRetention, "")
cmd.Flags().StringSlice("ids", []string{}, "")
cmd.Flags().String("status", "", "")
s.client.
EXPECT().
GetJobs(context.TODO(), model.JobTypeDataRetention, "", 0, perPage).
Return(mockJobs, &model.Response{}, nil).
Times(1)
err := listJobsCmdF(s.client, cmd, nil)
s.Require().Nil(err)
s.Len(printer.GetLines(), len(mockJobs))
s.Empty(printer.GetErrorLines())
for i, line := range printer.GetLines() {
s.Equal(mockJobs[i], line.(*model.Job))
}
})
}
func (s *MmctlUnitTestSuite) TestUpdateJobCmdF() {
s.Run("update job status", func() {
printer.Clean()
id := model.NewId()
cmd := &cobra.Command{}
cmd.Flags().Bool("force", true, "")
s.client.
EXPECT().
UpdateJobStatus(context.TODO(), id, model.JobStatusPending, true).
Return(&model.Response{}, nil).
Times(1)
err := updateJobCmdF(s.client, cmd, []string{id, model.JobStatusPending})
s.Require().Nil(err)
})
}

Просмотреть файл

@@ -121,7 +121,7 @@ func ldapIDMigrateCmdF(c client.Client, cmd *cobra.Command, args []string) error
}
func ldapJobListCmdF(c client.Client, command *cobra.Command, args []string) error {
return jobListCmdF(c, command, model.JobTypeLdapSync)
return jobListCmdF(c, command, model.JobTypeLdapSync, "")
}
func ldapJobShowCmdF(c client.Client, command *cobra.Command, args []string) error {

Просмотреть файл

@@ -128,7 +128,7 @@ func (s *MmctlUnitTestSuite) TestLdapJobListCmdF() {
s.client.
EXPECT().
GetJobsByType(context.TODO(), model.JobTypeLdapSync, 0, perPage).
GetJobs(context.TODO(), model.JobTypeLdapSync, "", 0, perPage).
Return(mockJobs, &model.Response{}, nil).
Times(1)
@@ -161,7 +161,7 @@ func (s *MmctlUnitTestSuite) TestLdapJobListCmdF() {
s.client.
EXPECT().
GetJobsByType(context.TODO(), model.JobTypeLdapSync, 0, perPage).
GetJobs(context.TODO(), model.JobTypeLdapSync, "", 0, perPage).
Return(mockJobs, &model.Response{}, nil).
Times(1)