github.com/iqoqo/nomad@v0.11.3-0.20200911112621-d7021c74d101/e2e/e2eutil/utils.go (about)

     1  package e2eutil
     2  
     3  import (
     4  	"fmt"
     5  	"testing"
     6  	"time"
     7  
     8  	"github.com/hashicorp/nomad/api"
     9  	"github.com/hashicorp/nomad/helper"
    10  	"github.com/hashicorp/nomad/jobspec"
    11  	"github.com/hashicorp/nomad/nomad/structs"
    12  	"github.com/hashicorp/nomad/testutil"
    13  	"github.com/kr/pretty"
    14  	"github.com/stretchr/testify/assert"
    15  	"github.com/stretchr/testify/require"
    16  )
    17  
    18  // retries is used to control how many times to retry checking if the cluster has a leader yet
    19  const retries = 500
    20  
    21  func WaitForLeader(t *testing.T, nomadClient *api.Client) {
    22  	statusAPI := nomadClient.Status()
    23  
    24  	testutil.WaitForResultRetries(retries, func() (bool, error) {
    25  		leader, err := statusAPI.Leader()
    26  		return leader != "", err
    27  	}, func(err error) {
    28  		t.Fatalf("failed to find leader: %v", err)
    29  	})
    30  }
    31  
    32  // WaitForNodesReady waits until at least `nodes` number of nodes are ready or
    33  // fails the test.
    34  func WaitForNodesReady(t *testing.T, nomadClient *api.Client, nodes int) {
    35  	nodesAPI := nomadClient.Nodes()
    36  
    37  	testutil.WaitForResultRetries(retries, func() (bool, error) {
    38  		defer time.Sleep(time.Millisecond * 100)
    39  		nodesList, _, err := nodesAPI.List(nil)
    40  		if err != nil {
    41  			return false, fmt.Errorf("error listing nodes: %v", err)
    42  		}
    43  
    44  		eligibleNodes := 0
    45  		for _, node := range nodesList {
    46  			if node.Status == "ready" {
    47  				eligibleNodes++
    48  			}
    49  		}
    50  
    51  		return eligibleNodes >= nodes, fmt.Errorf("only %d nodes ready (wanted at least %d)", eligibleNodes, nodes)
    52  	}, func(err error) {
    53  		t.Fatalf("failed to get enough ready nodes: %v", err)
    54  	})
    55  }
    56  
    57  func stringToPtrOrNil(s string) *string {
    58  	if s == "" {
    59  		return nil
    60  	}
    61  	return helper.StringToPtr(s)
    62  }
    63  
    64  func RegisterAllocs(t *testing.T, nomadClient *api.Client, jobFile, jobID, cToken string) []*api.AllocationListStub {
    65  
    66  	// Parse job
    67  	job, err := jobspec.ParseFile(jobFile)
    68  	require.NoError(t, err)
    69  
    70  	// Set custom job ID (distinguish among tests)
    71  	job.ID = helper.StringToPtr(jobID)
    72  
    73  	// Set a Consul "operator" token for the job, if provided.
    74  	job.ConsulToken = stringToPtrOrNil(cToken)
    75  
    76  	// Register job
    77  	var idx uint64
    78  	jobs := nomadClient.Jobs()
    79  	testutil.WaitForResult(func() (bool, error) {
    80  		resp, meta, err := jobs.Register(job, nil)
    81  		if err != nil {
    82  			return false, err
    83  		}
    84  		idx = meta.LastIndex
    85  		return resp.EvalID != "", fmt.Errorf("expected EvalID:%s", pretty.Sprint(resp))
    86  	}, func(err error) {
    87  		require.NoError(t, err)
    88  	})
    89  
    90  	allocs, _, err := jobs.Allocations(jobID, false, &api.QueryOptions{WaitIndex: idx})
    91  	require.NoError(t, err)
    92  	return allocs
    93  }
    94  
    95  func RegisterAndWaitForAllocs(t *testing.T, nomadClient *api.Client, jobFile, jobID, cToken string) []*api.AllocationListStub {
    96  	jobs := nomadClient.Jobs()
    97  
    98  	// Start allocations
    99  	RegisterAllocs(t, nomadClient, jobFile, jobID, cToken)
   100  
   101  	var err error
   102  	allocs := []*api.AllocationListStub{}
   103  	evals := []*api.Evaluation{}
   104  
   105  	// Wrap in retry to wait until placement
   106  	ok := assert.Eventually(t, func() bool {
   107  		allocs, _, err = jobs.Allocations(jobID, false, nil)
   108  		if len(allocs) < 1 {
   109  			evals, _, err = nomadClient.Jobs().Evaluations(jobID, nil)
   110  		}
   111  		return len(allocs) > 0
   112  	}, 30*time.Second, time.Second)
   113  
   114  	msg := fmt.Sprintf("allocations not placed for %s", jobID)
   115  	if !ok && len(evals) > 0 {
   116  		for _, eval := range evals {
   117  			msg += fmt.Sprintf("\n  %s - %s", eval.Status, eval.StatusDescription)
   118  		}
   119  	}
   120  	require.Truef(t, ok, msg)
   121  	require.NoError(t, err) // we only care about the last error
   122  	return allocs
   123  }
   124  
   125  func WaitForAllocRunning(t *testing.T, nomadClient *api.Client, allocID string) {
   126  	testutil.WaitForResultRetries(retries, func() (bool, error) {
   127  		time.Sleep(time.Millisecond * 100)
   128  		alloc, _, err := nomadClient.Allocations().Info(allocID, nil)
   129  		if err != nil {
   130  			return false, err
   131  		}
   132  
   133  		return alloc.ClientStatus == structs.AllocClientStatusRunning, fmt.Errorf("expected status running, but was: %s", alloc.ClientStatus)
   134  	}, func(err error) {
   135  		t.Fatalf("failed to wait on alloc: %v", err)
   136  	})
   137  }
   138  
   139  func WaitForAllocsRunning(t *testing.T, nomadClient *api.Client, allocIDs []string) {
   140  	for _, allocID := range allocIDs {
   141  		WaitForAllocRunning(t, nomadClient, allocID)
   142  	}
   143  }
   144  
   145  func WaitForAllocsNotPending(t *testing.T, nomadClient *api.Client, allocIDs []string) {
   146  	for _, allocID := range allocIDs {
   147  		WaitForAllocNotPending(t, nomadClient, allocID)
   148  	}
   149  }
   150  
   151  func WaitForAllocNotPending(t *testing.T, nomadClient *api.Client, allocID string) {
   152  	testutil.WaitForResultRetries(retries, func() (bool, error) {
   153  		time.Sleep(time.Millisecond * 100)
   154  		alloc, _, err := nomadClient.Allocations().Info(allocID, nil)
   155  		if err != nil {
   156  			return false, err
   157  		}
   158  
   159  		return alloc.ClientStatus != structs.AllocClientStatusPending, fmt.Errorf("expected status not pending, but was: %s", alloc.ClientStatus)
   160  	}, func(err error) {
   161  		t.Fatalf("failed to wait on alloc: %v", err)
   162  	})
   163  }
   164  
   165  func WaitForAllocStopped(t *testing.T, nomadClient *api.Client, allocID string) {
   166  	testutil.WaitForResultRetries(retries, func() (bool, error) {
   167  		time.Sleep(time.Millisecond * 100)
   168  		alloc, _, err := nomadClient.Allocations().Info(allocID, nil)
   169  		if err != nil {
   170  			return false, err
   171  		}
   172  		switch alloc.ClientStatus {
   173  		case structs.AllocClientStatusComplete:
   174  			return true, nil
   175  		case structs.AllocClientStatusFailed:
   176  			return true, nil
   177  		case structs.AllocClientStatusLost:
   178  			return true, nil
   179  		default:
   180  			return false, fmt.Errorf("expected stopped alloc, but was: %s",
   181  				alloc.ClientStatus)
   182  		}
   183  	}, func(err error) {
   184  		t.Fatalf("failed to wait on alloc: %v", err)
   185  	})
   186  }
   187  
   188  func AllocIDsFromAllocationListStubs(allocs []*api.AllocationListStub) []string {
   189  	allocIDs := make([]string, 0, len(allocs))
   190  	for _, alloc := range allocs {
   191  		allocIDs = append(allocIDs, alloc.ID)
   192  	}
   193  	return allocIDs
   194  }
   195  
   196  func DeploymentsForJob(t *testing.T, nomadClient *api.Client, jobID string) []*api.Deployment {
   197  	ds, _, err := nomadClient.Deployments().List(nil)
   198  	require.NoError(t, err)
   199  
   200  	out := []*api.Deployment{}
   201  	for _, d := range ds {
   202  		if d.JobID == jobID {
   203  			out = append(out, d)
   204  		}
   205  	}
   206  
   207  	return out
   208  }
   209  
   210  func WaitForDeployment(t *testing.T, nomadClient *api.Client, deployID string, status string, statusDesc string) {
   211  	testutil.WaitForResultRetries(retries, func() (bool, error) {
   212  		time.Sleep(time.Millisecond * 100)
   213  		deploy, _, err := nomadClient.Deployments().Info(deployID, nil)
   214  		if err != nil {
   215  			return false, err
   216  		}
   217  
   218  		if deploy.Status == status && deploy.StatusDescription == statusDesc {
   219  			return true, nil
   220  		}
   221  		return false, fmt.Errorf("expected status %s \"%s\", but got: %s \"%s\"",
   222  			status,
   223  			statusDesc,
   224  			deploy.Status,
   225  			deploy.StatusDescription,
   226  		)
   227  
   228  	}, func(err error) {
   229  		t.Fatalf("failed to wait on deployment: %v", err)
   230  	})
   231  }