github.com/billybanfield/evergreen@v0.0.0-20170525200750-eeee692790f7/service/api_status_test.go (about) 1 package service 2 3 import ( 4 "encoding/json" 5 "fmt" 6 "net/http" 7 "net/http/httptest" 8 "os" 9 "path/filepath" 10 "testing" 11 12 "github.com/evergreen-ci/evergreen" 13 "github.com/evergreen-ci/evergreen/db" 14 "github.com/evergreen-ci/evergreen/model/host" 15 "github.com/evergreen-ci/evergreen/model/task" 16 "github.com/evergreen-ci/evergreen/plugin" 17 "github.com/evergreen-ci/evergreen/testutil" 18 "github.com/evergreen-ci/evergreen/util" 19 . "github.com/smartystreets/goconvey/convey" 20 ) 21 22 // getCTAEndpoint is a helper that creates a test API server, 23 // GETs the consistent_task_assignment endpoint, and returns 24 // the response. 25 func getCTAEndpoint(t *testing.T) *httptest.ResponseRecorder { 26 if err := os.MkdirAll(filepath.Join(evergreen.FindEvergreenHome(), evergreen.ClientDirectory), 0644); err != nil { 27 t.Fatal("could not create client directory required to start the API server:", err.Error()) 28 } 29 30 as, err := NewAPIServer(testutil.TestConfig(), nil) 31 if err != nil { 32 t.Fatalf("creating test API server: %v", err) 33 } 34 handler, err := as.Handler() 35 if err != nil { 36 t.Fatalf("creating test API handler: %v", err) 37 } 38 url := "/api/status/consistent_task_assignment" 39 request, err := http.NewRequest("GET", url, nil) 40 if err != nil { 41 t.Fatalf("building request: %v", err) 42 } 43 44 w := httptest.NewRecorder() 45 handler.ServeHTTP(w, request) 46 return w 47 } 48 49 // EVG-1602: this fixture is not used yet. Commented to avoid deadcode lint error. 50 func getStuckHostEndpoint(t *testing.T) *httptest.ResponseRecorder { 51 if err := os.MkdirAll(filepath.Join(evergreen.FindEvergreenHome(), evergreen.ClientDirectory), 0644); err != nil { 52 t.Fatal("could not create client directory required to start the API server:", err.Error()) 53 } 54 55 as, err := NewAPIServer(testutil.TestConfig(), nil) 56 if err != nil { 57 t.Fatalf("creating test API server: %v", err) 58 } 59 handler, err := as.Handler() 60 if err != nil { 61 t.Fatalf("creating test API handler: %v", err) 62 } 63 url := "/api/status/stuck_hosts" 64 request, err := http.NewRequest("GET", url, nil) 65 if err != nil { 66 t.Fatalf("building request: %v", err) 67 } 68 69 w := httptest.NewRecorder() 70 handler.ServeHTTP(w, request) 71 return w 72 } 73 74 func TestConsistentTaskAssignment(t *testing.T) { 75 76 Convey("With various states of tasks and hosts in the DB", t, func() { 77 if err := db.ClearCollections(host.Collection, task.Collection); err != nil { 78 t.Fatalf("clearing db: %v", err) 79 } 80 Convey("A correct host/task mapping", func() { 81 h1 := host.Host{Id: "h1", Status: evergreen.HostRunning, RunningTask: "t1"} 82 h2 := host.Host{Id: "h2", Status: evergreen.HostRunning, RunningTask: "t2"} 83 h3 := host.Host{Id: "h3", Status: evergreen.HostRunning} 84 t1 := task.Task{Id: "t1", Status: evergreen.TaskStarted, HostId: "h1"} 85 t2 := task.Task{Id: "t2", Status: evergreen.TaskDispatched, HostId: "h2"} 86 t3 := task.Task{Id: "t3", Status: evergreen.TaskFailed} 87 So(h1.Insert(), ShouldBeNil) 88 So(h2.Insert(), ShouldBeNil) 89 So(h3.Insert(), ShouldBeNil) 90 So(t1.Insert(), ShouldBeNil) 91 So(t2.Insert(), ShouldBeNil) 92 So(t3.Insert(), ShouldBeNil) 93 resp := getCTAEndpoint(t) 94 So(resp, ShouldNotBeNil) 95 Convey("should return HTTP 200", func() { 96 So(resp.Code, ShouldEqual, http.StatusOK) 97 Convey("and JSON with a SUCCESS message and nothing else", func() { 98 tar := taskAssignmentResp{} 99 So(json.NewDecoder(resp.Body).Decode(&tar), ShouldBeNil) 100 So(tar.Status, ShouldEqual, apiStatusSuccess) 101 So(len(tar.Errors), ShouldEqual, 0) 102 So(len(tar.HostIds), ShouldEqual, 0) 103 So(len(tar.TaskIds), ShouldEqual, 0) 104 }) 105 }) 106 }) 107 Convey("An incorrect host/task mapping", func() { 108 h1 := host.Host{Id: "h1", Status: evergreen.HostRunning, RunningTask: "t1"} 109 h2 := host.Host{Id: "h2", Status: evergreen.HostRunning, RunningTask: "t2"} 110 h3 := host.Host{Id: "h3", Status: evergreen.HostRunning, RunningTask: "t1000"} 111 t1 := task.Task{Id: "t1", Status: evergreen.TaskStarted, HostId: "h1"} 112 t2 := task.Task{Id: "t2", Status: evergreen.TaskDispatched, HostId: "h3"} 113 t3 := task.Task{Id: "t3", Status: evergreen.TaskFailed} 114 So(h1.Insert(), ShouldBeNil) 115 So(h2.Insert(), ShouldBeNil) 116 So(h3.Insert(), ShouldBeNil) 117 So(t1.Insert(), ShouldBeNil) 118 So(t2.Insert(), ShouldBeNil) 119 So(t3.Insert(), ShouldBeNil) 120 resp := getCTAEndpoint(t) 121 So(resp, ShouldNotBeNil) 122 Convey("should return HTTP 200", func() { 123 So(resp.Code, ShouldEqual, http.StatusOK) 124 Convey("and JSON with an ERROR message and info about each issue", func() { 125 tar := taskAssignmentResp{} 126 So(json.NewDecoder(resp.Body).Decode(&tar), ShouldBeNil) 127 So(tar.Status, ShouldEqual, apiStatusError) 128 // ERROR 1: h2 thinks it is running t2, which thinks it is running on h3 129 // ERROR 2: h3 thinks it is running t1000, which does not exist 130 // ERROR 3: t2 thinks it is running on h3, which thinks it is running t1000 131 So(len(tar.Errors), ShouldEqual, 3) 132 So(len(tar.HostIds), ShouldEqual, 2) 133 So(tar.HostIds, ShouldContain, "h2") 134 So(tar.HostIds, ShouldContain, "h3") 135 So(len(tar.TaskIds), ShouldEqual, 1) 136 So(len(tar.HostRunningTasks), ShouldEqual, 2) 137 So(tar.TaskIds, ShouldContain, "t2") 138 So(tar.HostRunningTasks, ShouldContain, "t1000") 139 }) 140 }) 141 }) 142 }) 143 } 144 145 func TestServiceStatusEndPoints(t *testing.T) { 146 testConfig := testutil.TestConfig() 147 testServer, err := CreateTestServer(testConfig, nil, plugin.APIPlugins) 148 testutil.HandleTestingErr(err, t, "Couldn't create apiserver: %v", err) 149 defer testServer.Close() 150 151 url := fmt.Sprintf("%s/api/status/info", testServer.URL) 152 153 Convey("Service Status endpoints should report the status of the service", t, func() { 154 Convey("basic endpoint should have one key, that reports the build id", func() { 155 request, err := http.NewRequest("GET", url, nil) 156 So(err, ShouldBeNil) 157 158 resp, err := http.DefaultClient.Do(request) 159 So(err, ShouldBeNil) 160 So(resp.StatusCode, ShouldEqual, 200) 161 out := map[string]string{} 162 163 So(util.ReadJSONInto(resp.Body, &out), ShouldBeNil) 164 So(len(out), ShouldEqual, 1) 165 _, ok := out["build_revision"] 166 So(ok, ShouldBeTrue) 167 }) 168 Convey("auth endpoint should report extended information", func() { 169 request, err := http.NewRequest("GET", url, nil) 170 So(err, ShouldBeNil) 171 request.AddCookie(&http.Cookie{Name: evergreen.AuthTokenCookie, Value: "token"}) 172 173 resp, err := http.DefaultClient.Do(request) 174 So(err, ShouldBeNil) 175 So(resp.StatusCode, ShouldEqual, 200) 176 out := map[string]interface{}{} 177 178 So(util.ReadJSONInto(resp.Body, &out), ShouldBeNil) 179 180 So(len(out), ShouldEqual, 3) 181 for _, key := range []string{"build_revision", "sys_info", "pid"} { 182 _, ok := out[key] 183 So(ok, ShouldBeTrue) 184 } 185 186 }) 187 }) 188 } 189 190 func TestStuckHostEndpoints(t *testing.T) { 191 Convey("With a test server and test config", t, func() { 192 testConfig := testutil.TestConfig() 193 testServer, err := CreateTestServer(testConfig, nil, plugin.APIPlugins) 194 testutil.HandleTestingErr(err, t, "Couldn't create apiserver: %v", err) 195 defer testServer.Close() 196 197 if err := db.ClearCollections(host.Collection, task.Collection); err != nil { 198 t.Fatalf("clearing db: %v", err) 199 } 200 201 url := fmt.Sprintf("%s/api/status/stuck_hosts", testServer.URL) 202 Convey("With hosts and tasks that are all consistent, the response should success", func() { 203 h1 := host.Host{Id: "h1", Status: evergreen.HostRunning, RunningTask: "t1"} 204 h2 := host.Host{Id: "h2", Status: evergreen.HostRunning, RunningTask: "t2"} 205 h3 := host.Host{Id: "h3", Status: evergreen.HostRunning} 206 t1 := task.Task{Id: "t1", Status: evergreen.TaskStarted, HostId: "h1"} 207 t2 := task.Task{Id: "t2", Status: evergreen.TaskDispatched, HostId: "h2"} 208 t3 := task.Task{Id: "t3", Status: evergreen.TaskFailed} 209 210 So(h1.Insert(), ShouldBeNil) 211 So(h2.Insert(), ShouldBeNil) 212 So(h3.Insert(), ShouldBeNil) 213 So(t1.Insert(), ShouldBeNil) 214 So(t2.Insert(), ShouldBeNil) 215 So(t3.Insert(), ShouldBeNil) 216 217 request, err := http.NewRequest("GET", url, nil) 218 So(err, ShouldBeNil) 219 220 resp, err := http.DefaultClient.Do(request) 221 So(err, ShouldBeNil) 222 So(resp.StatusCode, ShouldEqual, 200) 223 out := stuckHostResp{} 224 225 So(util.ReadJSONInto(resp.Body, &out), ShouldBeNil) 226 So(out.Status, ShouldEqual, apiStatusSuccess) 227 228 }) 229 Convey("With hosts that have running tasks that have completed", func() { 230 h1 := host.Host{Id: "h1", Status: evergreen.HostRunning, RunningTask: "t1"} 231 h2 := host.Host{Id: "h2", Status: evergreen.HostRunning, RunningTask: "t2"} 232 h3 := host.Host{Id: "h3", Status: evergreen.HostRunning} 233 t1 := task.Task{Id: "t1", Status: evergreen.TaskStarted, HostId: "h1"} 234 t2 := task.Task{Id: "t2", Status: evergreen.TaskFailed, HostId: "h2"} 235 t3 := task.Task{Id: "t3", Status: evergreen.TaskFailed} 236 237 So(h1.Insert(), ShouldBeNil) 238 So(h2.Insert(), ShouldBeNil) 239 So(h3.Insert(), ShouldBeNil) 240 So(t1.Insert(), ShouldBeNil) 241 So(t2.Insert(), ShouldBeNil) 242 So(t3.Insert(), ShouldBeNil) 243 244 resp := getStuckHostEndpoint(t) 245 So(resp, ShouldNotBeNil) 246 So(resp.Code, ShouldEqual, http.StatusOK) 247 248 out := stuckHostResp{} 249 250 So(json.NewDecoder(resp.Body).Decode(&out), ShouldBeNil) 251 So(out.Status, ShouldEqual, apiStatusError) 252 So(len(out.HostIds), ShouldEqual, 1) 253 So(len(out.TaskIds), ShouldEqual, 1) 254 So(out.HostIds[0], ShouldEqual, "h2") 255 So(out.TaskIds[0], ShouldEqual, "t2") 256 257 }) 258 }) 259 }