github.com/hernad/nomad@v1.6.112/command/testing_test.go (about) 1 // Copyright (c) HashiCorp, Inc. 2 // SPDX-License-Identifier: MPL-2.0 3 4 package command 5 6 import ( 7 "fmt" 8 "os" 9 "regexp" 10 "testing" 11 "time" 12 13 "github.com/hernad/nomad/api" 14 "github.com/hernad/nomad/command/agent" 15 "github.com/hernad/nomad/helper/pointer" 16 "github.com/hernad/nomad/nomad/structs" 17 "github.com/hernad/nomad/testutil" 18 "github.com/shoenig/test/must" 19 ) 20 21 var nonAlphaNum = regexp.MustCompile(`[^a-zA-Z0-9]+`) 22 23 func testServer(t *testing.T, runClient bool, cb func(*agent.Config)) (*agent.TestAgent, *api.Client, string) { 24 // Make a new test server 25 a := agent.NewTestAgent(t, t.Name(), func(config *agent.Config) { 26 config.Client.Enabled = runClient 27 28 if cb != nil { 29 cb(config) 30 } 31 }) 32 t.Cleanup(a.Shutdown) 33 34 c := a.Client() 35 return a, c, a.HTTPAddr() 36 } 37 38 // testClient starts a new test client, blocks until it joins, and performs 39 // cleanup after the test is complete. 40 func testClient(t *testing.T, name string, cb func(*agent.Config)) (*agent.TestAgent, *api.Client, string) { 41 t.Logf("Starting client agent %s", name) 42 a := agent.NewTestAgent(t, name, func(config *agent.Config) { 43 if cb != nil { 44 cb(config) 45 } 46 }) 47 t.Cleanup(a.Shutdown) 48 49 c := a.Client() 50 t.Logf("Waiting for client %s to join server(s) %s", name, a.GetConfig().Client.Servers) 51 testutil.WaitForClient(t, a.Agent.RPC, a.Agent.Client().NodeID(), a.Agent.Client().Region()) 52 53 return a, c, a.HTTPAddr() 54 } 55 56 func testJob(jobID string) *api.Job { 57 task := api.NewTask("task1", "mock_driver"). 58 SetConfig("kill_after", "1s"). 59 SetConfig("run_for", "5s"). 60 SetConfig("exit_code", 0). 61 Require(&api.Resources{ 62 MemoryMB: pointer.Of(256), 63 CPU: pointer.Of(100), 64 }). 65 SetLogConfig(&api.LogConfig{ 66 MaxFiles: pointer.Of(1), 67 MaxFileSizeMB: pointer.Of(2), 68 }) 69 70 group := api.NewTaskGroup("group1", 1). 71 AddTask(task). 72 RequireDisk(&api.EphemeralDisk{ 73 SizeMB: pointer.Of(20), 74 }) 75 76 job := api.NewBatchJob(jobID, jobID, "global", 1). 77 AddDatacenter("dc1"). 78 AddTaskGroup(group) 79 80 return job 81 } 82 83 func testNomadServiceJob(jobID string) *api.Job { 84 j := testJob(jobID) 85 j.TaskGroups[0].Services = []*api.Service{{ 86 Name: "service1", 87 PortLabel: "1000", 88 AddressMode: "", 89 Address: "127.0.0.1", 90 Checks: []api.ServiceCheck{{ 91 Name: "check1", 92 Type: "http", 93 Path: "/", 94 Interval: 1 * time.Second, 95 Timeout: 1 * time.Second, 96 }}, 97 Provider: "nomad", 98 }} 99 return j 100 } 101 102 func testMultiRegionJob(jobID, region, datacenter string) *api.Job { 103 task := api.NewTask("task1", "mock_driver"). 104 SetConfig("kill_after", "10s"). 105 SetConfig("run_for", "15s"). 106 SetConfig("exit_code", 0). 107 Require(&api.Resources{ 108 MemoryMB: pointer.Of(256), 109 CPU: pointer.Of(100), 110 }). 111 SetLogConfig(&api.LogConfig{ 112 MaxFiles: pointer.Of(1), 113 MaxFileSizeMB: pointer.Of(2), 114 }) 115 116 group := api.NewTaskGroup("group1", 1). 117 AddTask(task). 118 RequireDisk(&api.EphemeralDisk{ 119 SizeMB: pointer.Of(20), 120 }) 121 122 job := api.NewServiceJob(jobID, jobID, region, 1).AddDatacenter(datacenter).AddTaskGroup(group) 123 job.Region = nil 124 job.Multiregion = &api.Multiregion{ 125 Regions: []*api.MultiregionRegion{ 126 { 127 Name: "east", 128 Datacenters: []string{"east-1"}, 129 }, 130 { 131 Name: "west", 132 Datacenters: []string{"west-1"}, 133 }, 134 }, 135 } 136 137 return job 138 } 139 140 func waitForNodes(t *testing.T, client *api.Client) { 141 testutil.WaitForResult(func() (bool, error) { 142 nodes, _, err := client.Nodes().List(nil) 143 if err != nil { 144 return false, err 145 } 146 for _, node := range nodes { 147 if _, ok := node.Drivers["mock_driver"]; ok && 148 node.Status == structs.NodeStatusReady { 149 return true, nil 150 } 151 } 152 return false, fmt.Errorf("no ready nodes") 153 }, func(err error) { 154 must.NoError(t, err) 155 }) 156 } 157 158 func waitForJobAllocsStatus(t *testing.T, client *api.Client, jobID string, status string, token string) { 159 testutil.WaitForResult(func() (bool, error) { 160 q := &api.QueryOptions{AuthToken: token} 161 162 allocs, _, err := client.Jobs().Allocations(jobID, true, q) 163 if err != nil { 164 return false, fmt.Errorf("failed to query job allocs: %v", err) 165 } 166 if len(allocs) == 0 { 167 return false, fmt.Errorf("no allocs") 168 } 169 170 for _, alloc := range allocs { 171 if alloc.ClientStatus != status { 172 return false, fmt.Errorf("alloc status is %q not %q", alloc.ClientStatus, status) 173 } 174 } 175 return true, nil 176 }, func(err error) { 177 must.NoError(t, err) 178 }) 179 } 180 181 func waitForAllocStatus(t *testing.T, client *api.Client, allocID string, status string) { 182 testutil.WaitForResult(func() (bool, error) { 183 alloc, _, err := client.Allocations().Info(allocID, nil) 184 if err != nil { 185 return false, err 186 } 187 if alloc.ClientStatus == status { 188 return true, nil 189 } 190 return false, fmt.Errorf("alloc status is %q not %q", alloc.ClientStatus, status) 191 }, func(err error) { 192 must.NoError(t, err) 193 }) 194 } 195 196 func waitForAllocRunning(t *testing.T, client *api.Client, allocID string) { 197 waitForAllocStatus(t, client, allocID, api.AllocClientStatusRunning) 198 } 199 200 func waitForCheckStatus(t *testing.T, client *api.Client, allocID, status string) { 201 testutil.WaitForResult(func() (bool, error) { 202 results, err := client.Allocations().Checks(allocID, nil) 203 if err != nil { 204 return false, err 205 } 206 207 // pick a check, any check will do 208 for _, check := range results { 209 if check.Status == status { 210 return true, nil 211 } 212 } 213 214 return false, fmt.Errorf("no check with status: %s", status) 215 }, func(err error) { 216 t.Fatalf("timed out waiting for alloc to be running: %v", err) 217 }) 218 } 219 220 func getAllocFromJob(t *testing.T, client *api.Client, jobID string) string { 221 var allocID string 222 if allocations, _, err := client.Jobs().Allocations(jobID, false, nil); err == nil { 223 if len(allocations) > 0 { 224 allocID = allocations[0].ID 225 } 226 } 227 must.NotEq(t, "", allocID, must.Sprint("expected to find an evaluation after running job", jobID)) 228 return allocID 229 } 230 231 func getTempFile(t *testing.T, name string) (string, func()) { 232 f, err := os.CreateTemp("", name) 233 must.NoError(t, err) 234 must.NoError(t, f.Close()) 235 return f.Name(), func() { 236 _ = os.Remove(f.Name()) 237 } 238 }