github.com/justinjmoses/evergreen@v0.0.0-20170530173719-1d50e381ff0d/agent/comm/task_json.go (about) 1 package comm 2 3 import ( 4 "fmt" 5 "io/ioutil" 6 "net/http" 7 "time" 8 9 "github.com/evergreen-ci/evergreen/model" 10 "github.com/evergreen-ci/evergreen/model/artifact" 11 "github.com/evergreen-ci/evergreen/model/task" 12 "github.com/evergreen-ci/evergreen/util" 13 "github.com/pkg/errors" 14 ) 15 16 // TaskJSONCommunicator handles plugin-specific JSON-encoded 17 // communication with the API server. 18 type TaskJSONCommunicator struct { 19 PluginName string 20 TaskCommunicator 21 } 22 23 // TaskPostJSON does an HTTP POST for the communicator's plugin + task. 24 func (t *TaskJSONCommunicator) TaskPostJSON(endpoint string, data interface{}) (*http.Response, error) { 25 return t.TryTaskPost(fmt.Sprintf("%s/%s", t.PluginName, endpoint), data) 26 } 27 28 // TaskGetJSON does an HTTP GET for the communicator's plugin + task. 29 func (t *TaskJSONCommunicator) TaskGetJSON(endpoint string) (*http.Response, error) { 30 return t.TryTaskGet(fmt.Sprintf("%s/%s", t.PluginName, endpoint)) 31 } 32 33 // TaskPostResults posts a set of test results for the communicator's task. 34 // If results are empty or nil, this operation is a noop. 35 func (t *TaskJSONCommunicator) TaskPostResults(results *task.TestResults) error { 36 if results == nil || len(results.Results) == 0 { 37 return nil 38 } 39 40 retriableSendFile := func() error { 41 resp, err := t.TryTaskPost("results", results) 42 if resp != nil { 43 defer resp.Body.Close() 44 } 45 if err != nil { 46 return util.RetriableError{errors.Wrap(err, "error posting results")} 47 } 48 49 body, _ := ioutil.ReadAll(resp.Body) 50 bodyErr := errors.Errorf("error posting results (%v): %s", 51 resp.StatusCode, string(body)) 52 switch resp.StatusCode { 53 case http.StatusOK: 54 return nil 55 case http.StatusBadRequest: 56 return bodyErr 57 default: 58 return util.RetriableError{bodyErr} 59 } 60 } 61 62 retryFail, err := util.Retry(retriableSendFile, httpMaxAttempts, 1*time.Second) 63 if retryFail { 64 return errors.Wrapf(err, "attaching test results failed after %d tries", httpMaxAttempts) 65 } 66 return nil 67 } 68 69 // PostTaskFiles is used by the PluginCommunicator interface for attaching task files. 70 func (t *TaskJSONCommunicator) PostTaskFiles(task_files []*artifact.File) error { 71 retriableSendFile := func() error { 72 resp, err := t.TryTaskPost("files", task_files) 73 if resp != nil { 74 defer resp.Body.Close() 75 } 76 if err != nil { 77 return util.RetriableError{errors.Wrap(err, "error posting results")} 78 } 79 body, readAllErr := ioutil.ReadAll(resp.Body) 80 bodyErr := errors.Errorf("error posting results (%v): %v", 81 resp.StatusCode, string(body)) 82 if readAllErr != nil { 83 return bodyErr 84 } 85 switch resp.StatusCode { 86 case http.StatusOK: 87 return nil 88 case http.StatusBadRequest: 89 return bodyErr 90 default: 91 return util.RetriableError{bodyErr} 92 } 93 } 94 95 retryFail, err := util.Retry(retriableSendFile, httpMaxAttempts, 1*time.Second) 96 if retryFail { 97 return errors.Wrapf(err, "attaching task files failed after %d tries", httpMaxAttempts) 98 } 99 return nil 100 } 101 102 // TaskPostTestLog posts a test log for a communicator's task. Is a 103 // noop if the test Log is nil. 104 func (t *TaskJSONCommunicator) TaskPostTestLog(log *model.TestLog) (string, error) { 105 if log == nil { 106 return "", nil 107 } 108 109 var logId string 110 retriableSendFile := func() error { 111 resp, err := t.TryTaskPost("test_logs", log) 112 if err != nil { 113 return util.RetriableError{errors.Wrap(err, "error posting logs")} 114 } 115 defer resp.Body.Close() 116 117 if resp.StatusCode == http.StatusOK { 118 logReply := struct { 119 Id string `json:"_id"` 120 }{} 121 122 err = util.ReadJSONInto(resp.Body, &logReply) 123 logId = logReply.Id 124 return errors.WithStack(err) 125 } 126 127 bodyErr, err := ioutil.ReadAll(resp.Body) 128 if err != nil { 129 return util.RetriableError{errors.WithStack(err)} 130 } 131 132 if resp.StatusCode == http.StatusBadRequest { 133 return errors.Errorf("bad request posting logs: %s", string(bodyErr)) 134 } 135 136 return util.RetriableError{errors.Errorf("failed posting logs: %s", string(bodyErr))} 137 } 138 139 retryFail, err := util.Retry(retriableSendFile, httpMaxAttempts, 1*time.Second) 140 if err != nil { 141 if retryFail { 142 return "", errors.Wrapf(err, "attaching test logs failed after %v tries", httpMaxAttempts) 143 } 144 return "", errors.Wrap(err, "attaching test logs failed") 145 } 146 return logId, nil 147 }