github.com/Ilhicas/nomad@v1.0.4-0.20210304152020-e86851182bc3/e2e/e2eutil/e2ejob.go (about) 1 package e2eutil 2 3 import ( 4 "bufio" 5 "context" 6 "fmt" 7 "os" 8 "os/exec" 9 "path/filepath" 10 "strconv" 11 "strings" 12 "testing" 13 "time" 14 15 "github.com/hashicorp/nomad/api" 16 "github.com/hashicorp/nomad/e2e/framework" 17 "github.com/hashicorp/nomad/helper/discover" 18 "github.com/hashicorp/nomad/helper/uuid" 19 "github.com/hashicorp/nomad/nomad/structs" 20 "github.com/hashicorp/nomad/testutil" 21 "github.com/stretchr/testify/assert" 22 "github.com/stretchr/testify/require" 23 ) 24 25 type e2eJob struct { 26 framework.TC 27 jobfile string 28 jobID string 29 } 30 31 func (e *e2eJob) Name() string { 32 return filepath.Base(e.jobfile) 33 } 34 35 // Ensure cluster has leader and at least 1 client node 36 // in a ready state before running tests 37 func (j *e2eJob) BeforeAll(f *framework.F) { 38 WaitForLeader(f.T(), j.Nomad()) 39 WaitForNodesReady(f.T(), j.Nomad(), 1) 40 j.jobID = "e2eutil-" + uuid.Generate()[0:8] 41 } 42 43 func (j *e2eJob) TestJob(f *framework.F) { 44 file, err := os.Open(j.jobfile) 45 t := f.T() 46 require.NoError(t, err) 47 48 scanner := bufio.NewScanner(file) 49 var e2eJobLine string 50 for scanner.Scan() { 51 if strings.HasPrefix(scanner.Text(), "//e2e:") { 52 e2eJobLine = scanner.Text()[6:] 53 } 54 require.NoError(t, scanner.Err()) 55 } 56 57 switch { 58 case strings.HasPrefix(e2eJobLine, "batch"): 59 parseBatchJobLine(t, j, e2eJobLine).Run(f) 60 case strings.HasPrefix(e2eJobLine, "service"): 61 parseServiceJobLine(t, j, e2eJobLine).Run(f) 62 default: 63 require.Fail(t, "could not parse e2e job line: %q", e2eJobLine) 64 } 65 } 66 67 type e2eBatchJob struct { 68 *e2eJob 69 70 shouldFail bool 71 } 72 73 func (j *e2eBatchJob) Run(f *framework.F) { 74 t := f.T() 75 require := require.New(t) 76 nomadClient := j.Nomad() 77 78 allocs := RegisterAndWaitForAllocs(f.T(), nomadClient, j.jobfile, j.jobID, "") 79 require.Equal(1, len(allocs)) 80 allocID := allocs[0].ID 81 82 // wait for the job to stop 83 WaitForAllocStopped(t, nomadClient, allocID) 84 alloc, _, err := nomadClient.Allocations().Info(allocID, nil) 85 require.NoError(err) 86 if j.shouldFail { 87 require.NotEqual(structs.AllocClientStatusComplete, alloc.ClientStatus) 88 } else { 89 require.Equal(structs.AllocClientStatusComplete, alloc.ClientStatus) 90 } 91 } 92 93 type e2eServiceJob struct { 94 *e2eJob 95 96 script string 97 runningDuration time.Duration 98 } 99 100 func (j *e2eServiceJob) Run(f *framework.F) { 101 t := f.T() 102 nomadClient := j.Nomad() 103 104 allocs := RegisterAndWaitForAllocs(f.T(), nomadClient, j.jobfile, j.jobID, "") 105 require.Equal(t, 1, len(allocs)) 106 allocID := allocs[0].ID 107 108 var alloc *api.Allocation 109 WaitForAllocRunning(t, nomadClient, allocID) 110 testutil.AssertUntil(j.runningDuration, func() (bool, error) { 111 var err error 112 alloc, _, err = nomadClient.Allocations().Info(allocID, nil) 113 if err != nil { 114 return false, err 115 } 116 117 return alloc.ClientStatus == structs.AllocClientStatusRunning, fmt.Errorf("expected status running, but was: %s", alloc.ClientStatus) 118 }, func(err error) { 119 require.NoError(t, err, "failed to keep alloc running") 120 }) 121 122 scriptPath := filepath.Join(filepath.Dir(j.jobfile), j.script) 123 ctx, cancel := context.WithTimeout(context.Background(), time.Second*10) 124 defer cancel() 125 cmd := exec.CommandContext(ctx, scriptPath) 126 nmdBin, err := discover.NomadExecutable() 127 assert.NoError(t, err) 128 cmd.Env = append(os.Environ(), 129 "NOMAD_BIN="+nmdBin, 130 "NOMAD_ALLOC_ID="+allocID, 131 "NOMAD_ADDR="+nomadClient.Address(), 132 ) 133 134 assert.NoError(t, cmd.Start()) 135 waitCh := make(chan error) 136 go func() { 137 select { 138 case waitCh <- cmd.Wait(): 139 case <-ctx.Done(): 140 } 141 }() 142 143 select { 144 case <-ctx.Done(): 145 case err := <-waitCh: 146 assert.NoError(t, err) 147 assert.Zero(t, cmd.ProcessState.ExitCode()) 148 } 149 150 // stop the job 151 _, _, err = nomadClient.Jobs().Deregister(j.jobID, false, nil) 152 require.NoError(t, err) 153 WaitForAllocStopped(t, nomadClient, allocID) 154 } 155 156 //e2e:batch fail=false 157 //e2e:service running=5s check=script.sh 158 159 func NewE2EJob(jobfile string) framework.TestCase { 160 return &e2eJob{ 161 jobfile: jobfile, 162 } 163 164 } 165 166 func parseServiceJobLine(t *testing.T, j *e2eJob, line string) *e2eServiceJob { 167 job := &e2eServiceJob{ 168 e2eJob: j, 169 runningDuration: time.Second * 5, 170 } 171 for _, options := range strings.Split(line, " ")[1:] { 172 o := strings.SplitN(options, "=", 2) 173 switch o[0] { 174 case "script": 175 job.script = o[1] 176 case "running": 177 dur, err := time.ParseDuration(o[1]) 178 if err != nil { 179 t.Logf("could not parse running duration %q for e2e job spec: %v", o[1], err) 180 } else { 181 job.runningDuration = dur 182 } 183 } 184 } 185 186 return job 187 } 188 189 func parseBatchJobLine(t *testing.T, j *e2eJob, line string) *e2eBatchJob { 190 job := &e2eBatchJob{ 191 e2eJob: j, 192 } 193 for _, options := range strings.Split(line, " ")[1:] { 194 o := strings.SplitN(options, "=", 2) 195 switch o[0] { 196 case "shouldFail": 197 job.shouldFail, _ = strconv.ParseBool(o[1]) 198 } 199 } 200 201 return job 202 }