package command import ( "fmt" "testing" "github.com/hashicorp/nomad/api" "github.com/hashicorp/nomad/ci" "github.com/hashicorp/nomad/helper" "github.com/hashicorp/nomad/nomad/mock" "github.com/hashicorp/nomad/nomad/structs" "github.com/hashicorp/nomad/testutil" "github.com/mitchellh/cli" "github.com/posener/complete" "github.com/stretchr/testify/require" ) func TestJobPeriodicForceCommand_Implements(t *testing.T) { ci.Parallel(t) var _ cli.Command = &JobPeriodicForceCommand{} } func TestJobPeriodicForceCommand_Fails(t *testing.T) { ci.Parallel(t) ui := cli.NewMockUi() cmd := &JobPeriodicForceCommand{Meta: Meta{Ui: ui}} // Fails on misuse code := cmd.Run([]string{"some", "bad", "args"}) require.Equal(t, code, 1, "expected error") out := ui.ErrorWriter.String() require.Contains(t, out, commandErrorText(cmd), "expected help output") ui.ErrorWriter.Reset() code = cmd.Run([]string{"-address=nope", "12"}) require.Equal(t, code, 1, "expected error") out = ui.ErrorWriter.String() require.Contains(t, out, "Error forcing periodic job", "expected force error") } func TestJobPeriodicForceCommand_AutocompleteArgs(t *testing.T) { ci.Parallel(t) srv, _, url := testServer(t, true, nil) defer srv.Shutdown() ui := cli.NewMockUi() cmd := &JobPeriodicForceCommand{Meta: Meta{Ui: ui, flagAddress: url}} // Create a fake job, not periodic state := srv.Agent.Server().State() j := mock.Job() require.NoError(t, state.UpsertJob(structs.MsgTypeTestSetup, 1000, j)) predictor := cmd.AutocompleteArgs() res := predictor.Predict(complete.Args{Last: j.ID[:len(j.ID)-5]}) require.Empty(t, res) // Create another fake job, periodic state = srv.Agent.Server().State() j2 := mock.Job() j2.Periodic = &structs.PeriodicConfig{ Enabled: true, Spec: "spec", SpecType: "cron", ProhibitOverlap: true, TimeZone: "test zone", } require.NoError(t, state.UpsertJob(structs.MsgTypeTestSetup, 1000, j2)) res = predictor.Predict(complete.Args{Last: j2.ID[:len(j.ID)-5]}) require.Equal(t, []string{j2.ID}, res) res = predictor.Predict(complete.Args{}) require.Equal(t, []string{j2.ID}, res) } func TestJobPeriodicForceCommand_NonPeriodicJob(t *testing.T) { ci.Parallel(t) srv, client, url := testServer(t, true, nil) defer srv.Shutdown() testutil.WaitForResult(func() (bool, error) { nodes, _, err := client.Nodes().List(nil) if err != nil { return false, err } if len(nodes) == 0 { return false, fmt.Errorf("missing node") } if _, ok := nodes[0].Drivers["mock_driver"]; !ok { return false, fmt.Errorf("mock_driver not ready") } return true, nil }, func(err error) { require.NoError(t, err) }) // Register a job j := testJob("job_not_periodic") ui := cli.NewMockUi() cmd := &JobPeriodicForceCommand{Meta: Meta{Ui: ui, flagAddress: url}} resp, _, err := client.Jobs().Register(j, nil) require.NoError(t, err) code := waitForSuccess(ui, client, fullId, t, resp.EvalID) require.Equal(t, 0, code) code = cmd.Run([]string{"-address=" + url, "job_not_periodic"}) require.Equal(t, 1, code, "expected exit code") out := ui.ErrorWriter.String() require.Contains(t, out, "No periodic job(s)", "non-periodic error message") } func TestJobPeriodicForceCommand_SuccessfulPeriodicForceDetach(t *testing.T) { ci.Parallel(t) srv, client, url := testServer(t, true, nil) defer srv.Shutdown() testutil.WaitForResult(func() (bool, error) { nodes, _, err := client.Nodes().List(nil) if err != nil { return false, err } if len(nodes) == 0 { return false, fmt.Errorf("missing node") } if _, ok := nodes[0].Drivers["mock_driver"]; !ok { return false, fmt.Errorf("mock_driver not ready") } return true, nil }, func(err error) { require.NoError(t, err) }) // Register a job j := testJob("job1_is_periodic") j.Periodic = &api.PeriodicConfig{ SpecType: helper.StringToPtr(api.PeriodicSpecCron), Spec: helper.StringToPtr("*/15 * * * * *"), ProhibitOverlap: helper.BoolToPtr(true), TimeZone: helper.StringToPtr("Europe/Minsk"), } ui := cli.NewMockUi() cmd := &JobPeriodicForceCommand{Meta: Meta{Ui: ui, flagAddress: url}} _, _, err := client.Jobs().Register(j, nil) require.NoError(t, err) code := cmd.Run([]string{"-address=" + url, "-detach", "job1_is_periodic"}) require.Equal(t, 0, code, "expected no error code") out := ui.OutputWriter.String() require.Contains(t, out, "Force periodic successful") require.Contains(t, out, "Evaluation ID:") } func TestJobPeriodicForceCommand_SuccessfulPeriodicForce(t *testing.T) { ci.Parallel(t) srv, client, url := testServer(t, true, nil) defer srv.Shutdown() testutil.WaitForResult(func() (bool, error) { nodes, _, err := client.Nodes().List(nil) if err != nil { return false, err } if len(nodes) == 0 { return false, fmt.Errorf("missing node") } if _, ok := nodes[0].Drivers["mock_driver"]; !ok { return false, fmt.Errorf("mock_driver not ready") } return true, nil }, func(err error) { require.NoError(t, err) }) // Register a job j := testJob("job2_is_periodic") j.Periodic = &api.PeriodicConfig{ SpecType: helper.StringToPtr(api.PeriodicSpecCron), Spec: helper.StringToPtr("*/15 * * * * *"), ProhibitOverlap: helper.BoolToPtr(true), TimeZone: helper.StringToPtr("Europe/Minsk"), } ui := cli.NewMockUi() cmd := &JobPeriodicForceCommand{Meta: Meta{Ui: ui, flagAddress: url}} _, _, err := client.Jobs().Register(j, nil) require.NoError(t, err) code := cmd.Run([]string{"-address=" + url, "job2_is_periodic"}) require.Equal(t, 0, code, "expected no error code") out := ui.OutputWriter.String() require.Contains(t, out, "Monitoring evaluation") require.Contains(t, out, "finished with status \"complete\"") }