github.com/anuvu/nomad@v0.8.7-atom1/api/fs_test.go (about) 1 package api 2 3 import ( 4 "bytes" 5 "fmt" 6 "io" 7 "reflect" 8 "strings" 9 "testing" 10 "time" 11 12 units "github.com/docker/go-units" 13 "github.com/hashicorp/nomad/helper" 14 "github.com/hashicorp/nomad/testutil" 15 "github.com/kr/pretty" 16 "github.com/stretchr/testify/assert" 17 "github.com/stretchr/testify/require" 18 ) 19 20 func TestFS_Logs(t *testing.T) { 21 t.Parallel() 22 require := require.New(t) 23 rpcPort := 0 24 c, s := makeClient(t, nil, func(c *testutil.TestServerConfig) { 25 rpcPort = c.Ports.RPC 26 c.Client = &testutil.ClientConfig{ 27 Enabled: true, 28 } 29 }) 30 defer s.Stop() 31 32 //TODO There should be a way to connect the client to the servers in 33 //makeClient above 34 require.NoError(c.Agent().SetServers([]string{fmt.Sprintf("127.0.0.1:%d", rpcPort)})) 35 36 index := uint64(0) 37 testutil.WaitForResult(func() (bool, error) { 38 nodes, qm, err := c.Nodes().List(&QueryOptions{WaitIndex: index}) 39 if err != nil { 40 return false, err 41 } 42 index = qm.LastIndex 43 if len(nodes) != 1 { 44 return false, fmt.Errorf("expected 1 node but found: %s", pretty.Sprint(nodes)) 45 } 46 if nodes[0].Status != "ready" { 47 return false, fmt.Errorf("node not ready: %s", nodes[0].Status) 48 } 49 return true, nil 50 }, func(err error) { 51 t.Fatalf("err: %v", err) 52 }) 53 54 var input strings.Builder 55 input.Grow(units.MB) 56 lines := 80 * units.KB 57 for i := 0; i < lines; i++ { 58 fmt.Fprintf(&input, "%d\n", i) 59 } 60 61 job := &Job{ 62 ID: helper.StringToPtr("TestFS_Logs"), 63 Region: helper.StringToPtr("global"), 64 Datacenters: []string{"dc1"}, 65 Type: helper.StringToPtr("batch"), 66 TaskGroups: []*TaskGroup{ 67 { 68 Name: helper.StringToPtr("TestFS_LogsGroup"), 69 Tasks: []*Task{ 70 { 71 Name: "logger", 72 Driver: "mock_driver", 73 Config: map[string]interface{}{ 74 "stdout_string": input.String(), 75 }, 76 }, 77 }, 78 }, 79 }, 80 } 81 82 jobs := c.Jobs() 83 jobResp, _, err := jobs.Register(job, nil) 84 require.NoError(err) 85 86 index = jobResp.EvalCreateIndex 87 evals := c.Evaluations() 88 testutil.WaitForResult(func() (bool, error) { 89 evalResp, qm, err := evals.Info(jobResp.EvalID, &QueryOptions{WaitIndex: index}) 90 if err != nil { 91 return false, err 92 } 93 if evalResp.BlockedEval != "" { 94 t.Fatalf("Eval blocked: %s", pretty.Sprint(evalResp)) 95 } 96 index = qm.LastIndex 97 if evalResp.Status != "complete" { 98 return false, fmt.Errorf("eval status: %v", evalResp.Status) 99 } 100 return true, nil 101 }, func(err error) { 102 t.Fatalf("err: %v", err) 103 }) 104 105 allocID := "" 106 testutil.WaitForResult(func() (bool, error) { 107 allocs, _, err := jobs.Allocations(*job.ID, true, &QueryOptions{WaitIndex: index}) 108 if err != nil { 109 return false, err 110 } 111 if len(allocs) != 1 { 112 return false, fmt.Errorf("unexpected number of allocs: %d", len(allocs)) 113 } 114 if allocs[0].ClientStatus != "complete" { 115 return false, fmt.Errorf("alloc not complete: %s", allocs[0].ClientStatus) 116 } 117 allocID = allocs[0].ID 118 return true, nil 119 }, func(err error) { 120 t.Fatalf("err: %v", err) 121 }) 122 123 alloc, _, err := c.Allocations().Info(allocID, nil) 124 require.NoError(err) 125 126 for i := 0; i < 3; i++ { 127 stopCh := make(chan struct{}) 128 defer close(stopCh) 129 130 frames, errors := c.AllocFS().Logs(alloc, false, "logger", "stdout", "start", 0, stopCh, nil) 131 132 var result bytes.Buffer 133 READ_FRAMES: 134 for { 135 select { 136 case f := <-frames: 137 if f == nil { 138 break READ_FRAMES 139 } 140 result.Write(f.Data) 141 case err := <-errors: 142 // Don't Fatal here as the other assertions may 143 // contain helpeful information. 144 t.Errorf("Error: %v", err) 145 } 146 } 147 148 // Check length 149 assert.Equal(t, input.Len(), result.Len(), "file size mismatch") 150 151 // Check complete ordering 152 for i := 0; i < lines; i++ { 153 line, err := result.ReadBytes('\n') 154 require.NoErrorf(err, "unexpected error on line %d: %v", i, err) 155 require.Equal(fmt.Sprintf("%d\n", i), string(line)) 156 } 157 } 158 } 159 160 func TestFS_FrameReader(t *testing.T) { 161 t.Parallel() 162 // Create a channel of the frames and a cancel channel 163 framesCh := make(chan *StreamFrame, 3) 164 errCh := make(chan error) 165 cancelCh := make(chan struct{}) 166 167 r := NewFrameReader(framesCh, errCh, cancelCh) 168 169 // Create some frames and send them 170 f1 := &StreamFrame{ 171 File: "foo", 172 Offset: 5, 173 Data: []byte("hello"), 174 } 175 f2 := &StreamFrame{ 176 File: "foo", 177 Offset: 10, 178 Data: []byte(", wor"), 179 } 180 f3 := &StreamFrame{ 181 File: "foo", 182 Offset: 12, 183 Data: []byte("ld"), 184 } 185 framesCh <- f1 186 framesCh <- f2 187 framesCh <- f3 188 close(framesCh) 189 190 expected := []byte("hello, world") 191 192 // Read a little 193 p := make([]byte, 12) 194 195 n, err := r.Read(p[:5]) 196 if err != nil { 197 t.Fatalf("Read failed: %v", err) 198 } 199 if off := r.Offset(); off != n { 200 t.Fatalf("unexpected read bytes: got %v; wanted %v", n, off) 201 } 202 203 off := n 204 for { 205 n, err = r.Read(p[off:]) 206 if err != nil { 207 if err == io.EOF { 208 break 209 } 210 t.Fatalf("Read failed: %v", err) 211 } 212 off += n 213 } 214 215 if !reflect.DeepEqual(p, expected) { 216 t.Fatalf("read %q, wanted %q", string(p), string(expected)) 217 } 218 219 if err := r.Close(); err != nil { 220 t.Fatalf("Close() failed: %v", err) 221 } 222 if _, ok := <-cancelCh; ok { 223 t.Fatalf("Close() didn't close cancel channel") 224 } 225 if len(expected) != r.Offset() { 226 t.Fatalf("offset %d, wanted %d", r.Offset(), len(expected)) 227 } 228 } 229 230 func TestFS_FrameReader_Unblock(t *testing.T) { 231 t.Parallel() 232 // Create a channel of the frames and a cancel channel 233 framesCh := make(chan *StreamFrame, 3) 234 errCh := make(chan error) 235 cancelCh := make(chan struct{}) 236 237 r := NewFrameReader(framesCh, errCh, cancelCh) 238 r.SetUnblockTime(10 * time.Millisecond) 239 240 // Read a little 241 p := make([]byte, 12) 242 243 n, err := r.Read(p) 244 if err != nil { 245 t.Fatalf("Read failed: %v", err) 246 } 247 248 if n != 0 { 249 t.Fatalf("should have unblocked") 250 } 251 252 // Unset the unblock 253 r.SetUnblockTime(0) 254 255 resultCh := make(chan struct{}) 256 go func() { 257 r.Read(p) 258 close(resultCh) 259 }() 260 261 select { 262 case <-resultCh: 263 t.Fatalf("shouldn't have unblocked") 264 case <-time.After(300 * time.Millisecond): 265 } 266 } 267 268 func TestFS_FrameReader_Error(t *testing.T) { 269 t.Parallel() 270 // Create a channel of the frames and a cancel channel 271 framesCh := make(chan *StreamFrame, 3) 272 errCh := make(chan error, 1) 273 cancelCh := make(chan struct{}) 274 275 r := NewFrameReader(framesCh, errCh, cancelCh) 276 r.SetUnblockTime(10 * time.Millisecond) 277 278 // Send an error 279 expected := fmt.Errorf("test error") 280 errCh <- expected 281 282 // Read a little 283 p := make([]byte, 12) 284 285 _, err := r.Read(p) 286 if err == nil || !strings.Contains(err.Error(), expected.Error()) { 287 t.Fatalf("bad error: %v", err) 288 } 289 }