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 }