diff --git a/api/deployments.go b/api/deployments.go index 665d38834..6785b8f6d 100644 --- a/api/deployments.go +++ b/api/deployments.go @@ -18,7 +18,7 @@ func (c *Client) Deployments() *Deployments { return &Deployments{client: c} } -// List is used to dump all of the deployments. +// List is used to dump all the deployments. func (d *Deployments) List(q *QueryOptions) ([]*Deployment, *QueryMeta, error) { var resp []*Deployment qm, err := d.client.query("/v1/deployments", &resp, q) diff --git a/api/deployments_test.go b/api/deployments_test.go new file mode 100644 index 000000000..c3bf046e1 --- /dev/null +++ b/api/deployments_test.go @@ -0,0 +1,394 @@ +package api + +import ( + "fmt" + "testing" + "time" + + "github.com/hashicorp/nomad/api/internal/testutil" + "github.com/shoenig/test/must" + "github.com/shoenig/test/wait" +) + +func TestDeployments_List(t *testing.T) { + testutil.Parallel(t) + + c, s := makeClient(t, nil, nil) + defer s.Stop() + deployments := c.Deployments() + jobs := c.Jobs() + + // Create a job of type service + job := testServiceJob() + + // Initially there should be no deployment + resp0, _, err := deployments.List(nil) + must.NoError(t, err) + must.Len(t, 0, resp0) + + // Register + resp1, wm, err := jobs.Register(job, nil) + must.NoError(t, err) + must.NotNil(t, resp1) + must.UUIDv4(t, resp1.EvalID) + assertWriteMeta(t, wm) + + // Query the jobs back out again + resp2, qm, err := jobs.List(nil) + assertQueryMeta(t, qm) + must.NoError(t, err) + must.Len(t, 1, resp2) + + f := func() error { + // List the deployment + resp3, _, err := deployments.List(nil) + if err != nil { + return err + } + if len(resp3) != 1 { + return fmt.Errorf(fmt.Sprintf("expected 1 deployment, found %v", len(resp3))) + } + return nil + } + must.Wait(t, wait.InitialSuccess( + wait.ErrorFunc(f), + wait.Timeout(3*time.Second), + wait.Gap(100*time.Millisecond), + )) +} + +func TestDeployments_PrefixList(t *testing.T) { + testutil.Parallel(t) + + c, s := makeClient(t, nil, nil) + defer s.Stop() + deployments := c.Deployments() + jobs := c.Jobs() + + // Initially there should be no deployment + resp, _, err := deployments.PrefixList("b1") + must.NoError(t, err) + must.Len(t, 0, resp) + + // Create a job of type service + job := testServiceJob() + + // Register the job + resp2, wm, err := jobs.Register(job, nil) + must.NoError(t, err) + must.NotNil(t, resp2) + must.UUIDv4(t, resp2.EvalID) + assertWriteMeta(t, wm) + + // Query the jobs back out again + resp3, qm, err := jobs.List(nil) + assertQueryMeta(t, qm) + must.NoError(t, err) + must.Len(t, 1, resp3) + + f := func() error { + // List the deployment + resp4, _, err := jobs.Deployments(resp3[0].ID, true, nil) + if len(resp4) != 1 { + return fmt.Errorf("expected 1 deployment, found %v", len(resp4)) + } + + // Prefix List + resp5, _, err := deployments.PrefixList(resp4[0].ID) + if err != nil { + return err + } + if len(resp5) != 1 { + return fmt.Errorf(fmt.Sprintf("expected 1 deployment, found %v", len(resp5))) + } + return nil + } + must.Wait(t, wait.InitialSuccess( + wait.ErrorFunc(f), + wait.Timeout(3*time.Second), + wait.Gap(100*time.Millisecond), + )) +} + +func TestDeployments_Info(t *testing.T) { + testutil.Parallel(t) + + c, s := makeClient(t, nil, nil) + defer s.Stop() + deployments := c.Deployments() + jobs := c.Jobs() + + // Create a job of type service + job := testServiceJob() + + // Register + resp, wm, err := jobs.Register(job, nil) + must.NoError(t, err) + must.NotNil(t, resp) + must.UUIDv4(t, resp.EvalID) + assertWriteMeta(t, wm) + + // Query the jobs + resp2, qm, err := jobs.List(nil) + assertQueryMeta(t, qm) + must.NoError(t, err) + must.Len(t, 1, resp2) + + f := func() error { + // Get the deploymentID for Info + resp3, _, err := jobs.Deployments(resp2[0].ID, true, nil) + if len(resp3) != 1 { + return fmt.Errorf("expected 1 deployment, found %v", len(resp3)) + } + + // Get Info using deployment ID + resp4, _, err := deployments.Info(resp3[0].ID, nil) + if err != nil { + return err + } + if resp4.JobID != resp2[0].ID { + return fmt.Errorf(fmt.Sprintf("expected job id: %v, found %v", resp4.JobID, resp2[0].ID)) + } + if resp4.Namespace != resp2[0].Namespace { + return fmt.Errorf(fmt.Sprintf("expected deployment namespace: %v, found %v", resp4.Namespace, resp2[0].Namespace)) + } + return nil + } + must.Wait(t, wait.InitialSuccess( + wait.ErrorFunc(f), + wait.Timeout(3*time.Second), + wait.Gap(100*time.Millisecond), + )) +} + +func TestDeployments_Allocations(t *testing.T) { + testutil.Parallel(t) + + c, s := makeClient(t, nil, nil) + defer s.Stop() + deployments := c.Deployments() + jobs := c.Jobs() + + // Create a job of type service + job := testServiceJob() + + // Register + resp, wm, err := jobs.Register(job, nil) + must.NoError(t, err) + must.NotNil(t, resp) + must.UUIDv4(t, resp.EvalID) + assertWriteMeta(t, wm) + + // Query the jobs + resp2, qm, err := jobs.List(nil) + assertQueryMeta(t, qm) + must.NoError(t, err) + must.Len(t, 1, resp2) + + f := func() error { + // Get the deploymentID for Allocations + resp3, _, err := jobs.Deployments(resp2[0].ID, true, nil) + if len(resp3) != 1 { + return fmt.Errorf("expected 1 deployment, found %v", len(resp3)) + } + + // Query deployment list + resp4, _, err := deployments.List(nil) + if err != nil { + return err + } + if len(resp4) != 1 { + return fmt.Errorf("expected 1 deployment, found %v", len(resp4)) + } + + // Get Allocations + resp5, _, err := deployments.Allocations(resp3[0].ID, nil) + if err != nil { + return err + } + if len(resp5) != 0 { + return fmt.Errorf("expected 0 Allocations, found %v", len(resp5)) + } + return nil + } + must.Wait(t, wait.InitialSuccess( + wait.ErrorFunc(f), + wait.Timeout(3*time.Second), + wait.Gap(100*time.Millisecond), + )) +} + +func TestDeployments_Fail(t *testing.T) { + testutil.Parallel(t) + + c, s := makeClient(t, nil, nil) + defer s.Stop() + deployments := c.Deployments() + jobs := c.Jobs() + + // Create a job of type service + job := testServiceJob() + + // Register + resp, wm, err := jobs.Register(job, nil) + must.NoError(t, err) + must.NotNil(t, resp) + must.UUIDv4(t, resp.EvalID) + assertWriteMeta(t, wm) + + // Query the jobs + resp2, qm, err := jobs.List(nil) + assertQueryMeta(t, qm) + must.NoError(t, err) + must.Len(t, 1, resp2) + + f := func() error { + // Get the deploymentID for Failing + resp3, _, err := jobs.Deployments(resp2[0].ID, true, nil) + if len(resp3) != 1 { + return fmt.Errorf("expected 1 deployment, found %v", len(resp3)) + } + + // Fail Deployment + _, _, err = deployments.Fail(resp3[0].ID, nil) + if err != nil { + return err + } + + // Query Info to check the status + resp4, _, err := deployments.Info(resp3[0].ID, nil) + if err != nil { + return err + } + if resp4.Status != "failed" { + return fmt.Errorf("expected failed status, got %v", resp4.Status) + } + return nil + } + must.Wait(t, wait.InitialSuccess( + wait.ErrorFunc(f), + wait.Timeout(3*time.Second), + wait.Gap(100*time.Millisecond), + )) +} + +func TestDeployments_Pause(t *testing.T) { + testutil.Parallel(t) + + c, s := makeClient(t, nil, nil) + defer s.Stop() + deployments := c.Deployments() + jobs := c.Jobs() + + // Create a job of type service + job := testServiceJob() + + // Register + resp, wm, err := jobs.Register(job, nil) + must.NoError(t, err) + must.NotNil(t, resp) + must.UUIDv4(t, resp.EvalID) + assertWriteMeta(t, wm) + + // Query the jobs + resp2, qm, err := jobs.List(nil) + assertQueryMeta(t, qm) + must.NoError(t, err) + must.Len(t, 1, resp2) + + f := func() error { + // Get the deploymentID for pausing + resp3, _, err := jobs.Deployments(resp2[0].ID, true, nil) + if len(resp3) != 1 { + return fmt.Errorf("expected 1 deployment, found %v", len(resp3)) + } + + // Pause Deployment + _, _, err = deployments.Pause(resp3[0].ID, true, nil) + if err != nil { + return err + } + + // Query Info to check the status + resp4, _, err := deployments.Info(resp3[0].ID, nil) + if err != nil { + return err + } + if resp4.Status != "paused" { + return fmt.Errorf("expected paused status, got %v", resp4.Status) + } + return nil + } + must.Wait(t, wait.InitialSuccess( + wait.ErrorFunc(f), + wait.Timeout(3*time.Second), + wait.Gap(100*time.Millisecond), + )) +} + +func TestDeployments_Unpause(t *testing.T) { + testutil.Parallel(t) + + c, s := makeClient(t, nil, nil) + defer s.Stop() + deployments := c.Deployments() + jobs := c.Jobs() + + // Create a job of type service + job := testServiceJob() + + // Register + resp, wm, err := jobs.Register(job, nil) + must.NoError(t, err) + must.NotNil(t, resp) + must.UUIDv4(t, resp.EvalID) + assertWriteMeta(t, wm) + + // Query the jobs + resp2, qm, err := jobs.List(nil) + assertQueryMeta(t, qm) + must.NoError(t, err) + must.Len(t, 1, resp2) + + f := func() error { + // Get the deploymentID for un-pausing + resp3, _, err := jobs.Deployments(resp2[0].ID, true, nil) + if len(resp3) != 1 { + return fmt.Errorf("expected 1 deployment, found %v", len(resp3)) + } + + // Pause Deployment + _, _, err = deployments.Pause(resp3[0].ID, true, nil) + if err != nil { + return err + } + + // Query Info to check the status + resp4, _, err := deployments.Info(resp3[0].ID, nil) + if err != nil { + return err + } + if resp4.Status != "paused" { + return fmt.Errorf("expected paused status, got %v", resp4.Status) + } + + // UnPause the deployment + _, _, err = deployments.Pause(resp3[0].ID, false, nil) + must.NoError(t, err) + + // Query Info again to check the status + resp5, _, err := deployments.Info(resp3[0].ID, nil) + if err != nil { + return err + } + if resp5.Status != "running" { + return fmt.Errorf("expected running status, got %v", resp5.Status) + } + return nil + } + must.Wait(t, wait.InitialSuccess( + wait.ErrorFunc(f), + wait.Timeout(3*time.Second), + wait.Gap(100*time.Millisecond), + )) +} diff --git a/api/util_test.go b/api/util_test.go index 5af2625b2..c5932d4c0 100644 --- a/api/util_test.go +++ b/api/util_test.go @@ -48,6 +48,14 @@ func testJob() *Job { return job } +func testServiceJob() *Job { + // Create a job of type service + task := NewTask("dummy-task", "exec").SetConfig("command", "/bin/sleep") + group1 := NewTaskGroup("dummy-group", 1).AddTask(task) + job := NewServiceJob("dummy-service", "dummy-service", "global", 5).AddTaskGroup(group1) + return job +} + func testJobWithScalingPolicy() *Job { job := testJob() job.TaskGroups[0].Scaling = &ScalingPolicy{