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  }