// GET: /user func (cr *UserController) ReadMany(cx *goweb.Context) { // Log Request and check for Auth LogRequest(cx.Request) u, err := request.Authenticate(cx.Request) if err != nil { if err.Error() == e.NoAuth || err.Error() == e.UnAuth { cx.RespondWithError(http.StatusUnauthorized) return } else { logger.Error("Err@user_Read: " + err.Error()) cx.RespondWithError(http.StatusInternalServerError) return } } if u.Admin { users := user.Users{} user.AdminGet(&users) if len(users) > 0 { cx.RespondWithData(users) return } else { cx.RespondWithNotFound() return } } else { cx.RespondWithError(http.StatusUnauthorized) return } }
func ResourceDescription(cx *goweb.Context) { LogRequest(cx.Request) r := resource{ R: []string{}, F: core.JobInfoIndexes, U: apiUrl(cx) + "/", D: siteUrl(cx) + "/", Title: conf.TITLE, C: conf.ADMIN_EMAIL, I: "AWE", T: core.Service, S: core.QMgr.QueueStatus(), V: conf.VERSION, Time: time.Now().String(), GitCommitHash: conf.GIT_COMMIT_HASH, } if core.Service == "server" { r.R = []string{"job", "work", "client", "queue", "awf", "event"} } else if core.Service == "proxy" { r.R = []string{"client", "work"} } cx.WriteResponse(r, 200) return }
// GET: /client/{id} func (cr *ClientController) Read(id string, cx *goweb.Context) { // Gather query params query := &Query{Li: cx.Request.URL.Query()} if query.Has("heartbeat") { //handle heartbeat hbmsg, err := core.QMgr.ClientHeartBeat(id) if err != nil { cx.RespondWithErrorMessage(err.Error(), http.StatusBadRequest) } else { cx.RespondWithData(hbmsg) } return } LogRequest(cx.Request) //skip heartbeat in access log client, err := core.QMgr.GetClient(id) if err != nil { if err.Error() == e.ClientNotFound { cx.RespondWithErrorMessage(e.ClientNotFound, http.StatusBadRequest) } else { logger.Error("Error in GET client:" + err.Error()) cx.RespondWithError(http.StatusBadRequest) } return } cx.RespondWithData(client) }
// DELETE: /job?suspend, /job?zombie func (cr *JobController) DeleteMany(cx *goweb.Context) { LogRequest(cx.Request) // Try to authenticate user. u, err := request.Authenticate(cx.Request) if err != nil && err.Error() != e.NoAuth { cx.RespondWithErrorMessage(err.Error(), http.StatusUnauthorized) return } // If no auth was provided, and anonymous delete is allowed, use the public user if u == nil { if conf.ANON_DELETE == true { u = &user.User{Uuid: "public"} } else { cx.RespondWithErrorMessage(e.NoAuth, http.StatusUnauthorized) return } } // Gather query params query := &Query{Li: cx.Request.URL.Query()} if query.Has("suspend") { num := core.QMgr.DeleteSuspendedJobsByUser(u) cx.RespondWithData(fmt.Sprintf("deleted %d suspended jobs", num)) } else if query.Has("zombie") { num := core.QMgr.DeleteZombieJobsByUser(u) cx.RespondWithData(fmt.Sprintf("deleted %d zombie jobs", num)) } else { cx.RespondWithError(http.StatusNotImplemented) } return }
// PUT: /client func (cr *ClientController) UpdateMany(cx *goweb.Context) { LogRequest(cx.Request) // Try to authenticate user. u, err := request.Authenticate(cx.Request) if err != nil && err.Error() != e.NoAuth { cx.RespondWithErrorMessage(err.Error(), http.StatusUnauthorized) return } // If no auth was provided, and anonymous read is allowed, use the public user if u == nil { if conf.ANON_WRITE == true { u = &user.User{Uuid: "public"} } else { cx.RespondWithErrorMessage(e.NoAuth, http.StatusUnauthorized) return } } // Gather query params query := &Query{Li: cx.Request.URL.Query()} if query.Has("resumeall") { //resume the suspended client num := core.QMgr.ResumeSuspendedClientsByUser(u) cx.RespondWithData(fmt.Sprintf("%d suspended clients resumed", num)) return } if query.Has("suspendall") { //resume the suspended client num := core.QMgr.SuspendAllClientsByUser(u) cx.RespondWithData(fmt.Sprintf("%d clients suspended", num)) return } cx.RespondWithError(http.StatusNotImplemented) return }
// PUT: /work/{id} -> status update func (cr *WorkController) Update(id string, cx *goweb.Context) { // Log Request and check for Auth LogRequest(cx.Request) // Gather query params query := &Query{Li: cx.Request.URL.Query()} if query.Has("status") && query.Has("client") { //notify execution result: "done" or "fail" notice := core.Notice{WorkId: id, Status: query.Value("status"), ClientId: query.Value("client"), Notes: ""} if query.Has("report") { // if "report" is specified in query, parse performance statistics or errlog if _, files, err := ParseMultipartForm(cx.Request); err == nil { if _, ok := files["perf"]; ok { core.QMgr.FinalizeWorkPerf(id, files["perf"].Path) } if _, ok := files["notes"]; ok { if notes, err := ioutil.ReadFile(files["notes"].Path); err == nil { notice.Notes = string(notes) } } } } core.QMgr.NotifyWorkStatus(notice) } cx.RespondWithData("ok") return }
// PUT: /logger func (cr *LoggerController) UpdateMany(cx *goweb.Context) { LogRequest(cx.Request) // Try to authenticate user. u, err := request.Authenticate(cx.Request) if err != nil && err.Error() != e.NoAuth { cx.RespondWithErrorMessage(err.Error(), http.StatusUnauthorized) return } // must be admin user if u == nil || u.Admin == false { cx.RespondWithErrorMessage(e.NoAuth, http.StatusUnauthorized) return } // Gather query params query := &Query{Li: cx.Request.URL.Query()} // currently can only reset debug level if query.Has("debug") { levelStr := query.Value("debug") levelInt, err := strconv.Atoi(levelStr) if err != nil { cx.RespondWithErrorMessage("invalid debug level: "+err.Error(), http.StatusBadRequest) } conf.DEBUG_LEVEL = levelInt logger.Event(event.DEBUG_LEVEL, "level="+levelStr+";user="******"debuglevel": conf.DEBUG_LEVEL}) return } cx.RespondWithError(http.StatusNotImplemented) return }
// PUT: /queue func (cr *QueueController) UpdateMany(cx *goweb.Context) { LogRequest(cx.Request) // Try to authenticate user. u, err := request.Authenticate(cx.Request) if err != nil && err.Error() != e.NoAuth { cx.RespondWithErrorMessage(err.Error(), http.StatusUnauthorized) return } // must be admin user if u == nil || u.Admin == false { cx.RespondWithErrorMessage(e.NoAuth, http.StatusUnauthorized) return } // Gather query params query := &Query{Li: cx.Request.URL.Query()} if query.Has("resume") { core.QMgr.ResumeQueue() logger.Event(event.QUEUE_RESUME, "user="******"work queue resumed") return } if query.Has("suspend") { core.QMgr.SuspendQueue() logger.Event(event.QUEUE_SUSPEND, "user="******"work queue suspended") return } cx.RespondWithErrorMessage("requested queue operation not supported", http.StatusBadRequest) return }
func AuthError(err error, cx *goweb.Context) { if err.Error() == e.InvalidAuth { cx.RespondWithErrorMessage("Invalid authorization header or content", http.StatusBadRequest) return } logger.Error("Error at Auth: " + err.Error()) cx.RespondWithError(http.StatusInternalServerError) return }
func RespondPrivateEnvInHeader(cx *goweb.Context, Envs map[string]string) (err error) { env_stream, err := json.Marshal(Envs) if err != nil { return err } cx.ResponseWriter.Header().Set("Privateenv", string(env_stream[:])) cx.Respond(nil, http.StatusOK, nil, cx) return }
// DELETE: /job/{id} func (cr *JobController) Delete(id string, cx *goweb.Context) { LogRequest(cx.Request) if err := core.QMgr.DeleteJob(id); err != nil { cx.RespondWithErrorMessage("fail to delete job: "+id, http.StatusBadRequest) return } cx.RespondWithData("job deleted: " + id) return }
// GET: /queue // get status from queue manager func (cr *QueueController) ReadMany(cx *goweb.Context) { LogRequest(cx.Request) // Gather query params // query := &Query{list: cx.Request.URL.Query()} msg := core.QMgr.ShowStatus() cx.RespondWithData(msg) return }
// PUT: /job func (cr *JobController) UpdateMany(cx *goweb.Context) { LogRequest(cx.Request) // Gather query params query := &Query{Li: cx.Request.URL.Query()} if query.Has("resumeall") { //resume the suspended job num := core.QMgr.ResumeSuspendedJobs() cx.RespondWithData(fmt.Sprintf("%d suspended jobs resumed", num)) return } cx.RespondWithError(http.StatusNotImplemented) }
// GET: /awf/{name} // get a workflow by name, read-only func (cr *AwfController) Read(id string, cx *goweb.Context) { LogRequest(cx.Request) // Load workunit by id workflow, err := core.AwfMgr.GetWorkflow(id) if err != nil { cx.RespondWithErrorMessage(err.Error(), http.StatusBadRequest) return } // Base case respond with workunit in json cx.RespondWithData(workflow) return }
// GET: /queue // get status from queue manager func (cr *QueueController) ReadMany(cx *goweb.Context) { LogRequest(cx.Request) // Gather query params // query := &Query{list: cx.Request.URL.Query()} msg, err := json.Marshal(core.QMgr.ShowStatus()) if err == nil { cx.RespondWithData(string(msg)) } else { cx.RespondWithData("Err") } }
func handleAuthError(err error, cx *goweb.Context) { switch err.Error() { case e.MongoDocNotFound: cx.RespondWithErrorMessage("Invalid username or password", http.StatusBadRequest) return // case e.InvalidAuth: // cx.RespondWithErrorMessage("Invalid Authorization header", http.StatusBadRequest) // return } logger.Error("Error at Auth: " + err.Error()) cx.RespondWithError(http.StatusInternalServerError) return }
// DELETE: /client/{id} func (cr *ClientController) Delete(id string, cx *goweb.Context) { LogRequest(cx.Request) // Try to authenticate user. u, err := request.Authenticate(cx.Request) if err != nil && err.Error() != e.NoAuth { cx.RespondWithErrorMessage(err.Error(), http.StatusUnauthorized) return } // If no auth was provided, and anonymous read is allowed, use the public user if u == nil { if conf.ANON_DELETE == true { u = &user.User{Uuid: "public"} } else { cx.RespondWithErrorMessage(e.NoAuth, http.StatusUnauthorized) return } } if err := core.QMgr.DeleteClientByUser(id, u); err != nil { cx.RespondWithErrorMessage(err.Error(), http.StatusBadRequest) } else { cx.RespondWithData("client deleted") } return }
// GET: /work/{id} // get a workunit by id, read-only func (cr *WorkController) Read(id string, cx *goweb.Context) { LogRequest(cx.Request) // Gather query params query := &Query{Li: cx.Request.URL.Query()} if query.Has("datatoken") && query.Has("client") { //a client is requesting data token for this job //***insert code to authenticate and check ACL*** clientid := query.Value("client") token, err := core.QMgr.FetchDataToken(id, clientid) if err != nil { cx.RespondWithErrorMessage("error in getting token for job "+id, http.StatusBadRequest) } cx.RespondWithData(token) return } // Load workunit by id workunit, err := core.QMgr.GetWorkById(id) if err != nil { if err.Error() != e.QueueEmpty { logger.Error("Err@work_Read:core.QMgr.GetWorkById(): " + err.Error()) } cx.RespondWithErrorMessage(err.Error(), http.StatusBadRequest) return } // Base case respond with workunit in json cx.RespondWithData(workunit) return }
// POST: /clientgroup/{name} func (cr *ClientGroupController) CreateWithId(name string, cx *goweb.Context) { LogRequest(cx.Request) // Try to authenticate user. u, err := request.Authenticate(cx.Request) if err != nil && err.Error() != e.NoAuth { cx.RespondWithErrorMessage(err.Error(), http.StatusUnauthorized) return } // If no auth was provided and ANON_CG_WRITE is true, use the public user. // Otherwise if no auth was provided or user is not an admin, and ANON_CG_WRITE is false, throw an error. // Otherwise, proceed with creation of the clientgroup with the user. if u == nil && conf.ANON_CG_WRITE == true { u = &user.User{Uuid: "public"} } else if u == nil || !u.Admin { if conf.ANON_CG_WRITE == false { cx.RespondWithErrorMessage(e.UnAuth, http.StatusUnauthorized) return } } cg, err := core.CreateClientGroup(name, u) if err != nil { cx.RespondWithErrorMessage(err.Error(), http.StatusBadRequest) return } cx.RespondWithData(cg) return }
func AdminAuthenticated(cx *goweb.Context) bool { user, err := request.Authenticate(cx.Request) if err != nil { if err.Error() == e.NoAuth || err.Error() == e.UnAuth { cx.RespondWithError(http.StatusUnauthorized) } else { request.AuthError(err, cx) } return false } if _, ok := conf.Admin_Users[user.Username]; !ok { msg := fmt.Sprintf("user %s has no admin right", user.Username) cx.RespondWithErrorMessage(msg, http.StatusBadRequest) return false } return true }
func ResourceDescription(cx *goweb.Context) { LogRequest(cx.Request) r := resource{ R: []string{}, U: apiUrl(cx) + "/", D: siteUrl(cx) + "/", C: conf.ADMIN_EMAIL, I: "AWE", T: core.Service, } if core.Service == "server" { r.R = []string{"job", "work", "client", "queue", "awf"} } else if core.Service == "proxy" { r.R = []string{"client", "work"} } cx.WriteResponse(r, 200) }
// POST: /client - register a new client func (cr *ClientController) Create(cx *goweb.Context) { // Log Request and check for Auth LogRequest(cx.Request) cg, err := request.AuthenticateClientGroup(cx.Request) if err != nil { if err.Error() == e.NoAuth || err.Error() == e.UnAuth || err.Error() == e.InvalidAuth { if conf.CLIENT_AUTH_REQ == true { cx.RespondWithError(http.StatusUnauthorized) return } } else { logger.Error("Err@AuthenticateClientGroup: " + err.Error()) cx.RespondWithError(http.StatusInternalServerError) return } } // Parse uploaded form _, files, err := ParseMultipartForm(cx.Request) if err != nil { if err.Error() != "request Content-Type isn't multipart/form-data" { logger.Error("Error parsing form: " + err.Error()) cx.RespondWithError(http.StatusBadRequest) return } } client, err := core.QMgr.RegisterNewClient(files, cg) if err != nil { msg := "Error in registering new client:" + err.Error() logger.Error(msg) cx.RespondWithErrorMessage(msg, http.StatusBadRequest) return } //log event about client registration (CR) logger.Event(event.CLIENT_REGISTRATION, "clientid="+client.Id+";name="+client.Name+";host="+client.Host+";group="+client.Group+";instance_id="+client.InstanceId+";instance_type="+client.InstanceType+";domain="+client.Domain) cx.RespondWithData(client) return }
// GET: /client func (cr *ClientController) ReadMany(cx *goweb.Context) { LogRequest(cx.Request) clients := core.QMgr.GetAllClients() if len(clients) == 0 { cx.RespondWithErrorMessage(e.ClientNotFound, http.StatusBadRequest) return } query := &Query{Li: cx.Request.URL.Query()} filtered := []*core.Client{} if query.Has("busy") { for _, client := range clients { if len(client.Current_work) > 0 { filtered = append(filtered, client) } } } else { filtered = clients } cx.RespondWithData(filtered) }
// GET: /client func (cr *ClientController) ReadMany(cx *goweb.Context) { LogRequest(cx.Request) // Try to authenticate user. u, err := request.Authenticate(cx.Request) if err != nil && err.Error() != e.NoAuth { cx.RespondWithErrorMessage(err.Error(), http.StatusUnauthorized) return } // If no auth was provided, and anonymous read is allowed, use the public user if u == nil { if conf.ANON_READ == true { u = &user.User{Uuid: "public"} } else { cx.RespondWithErrorMessage(e.NoAuth, http.StatusUnauthorized) return } } clients := core.QMgr.GetAllClientsByUser(u) query := &Query{Li: cx.Request.URL.Query()} filtered := []*core.Client{} if query.Has("busy") { for _, client := range clients { if client.Current_work_length() > 0 { filtered = append(filtered, client) } } } else if query.Has("group") { for _, client := range clients { if client.Group == query.Value("group") { filtered = append(filtered, client) } } } else if query.Has("status") { for _, client := range clients { status := client.Get_Status() stat := strings.Split(status, "-") if status == query.Value("status") { filtered = append(filtered, client) } else if (len(stat) == 2) && (stat[1] == query.Value("status")) { filtered = append(filtered, client) } } } else if query.Has("app") { for _, client := range clients { for _, app := range client.Apps { if app == query.Value("app") { filtered = append(filtered, client) } } } } else { filtered = clients } cx.RespondWithData(filtered) return }
// GET: /work // checkout a workunit with earliest submission time // to-do: to support more options for workunit checkout func (cr *WorkController) ReadMany(cx *goweb.Context) { // Gather query params query := &Query{Li: cx.Request.URL.Query()} if !query.Has("client") { //view workunits var workunits []*core.Workunit if query.Has("state") { workunits = core.QMgr.ShowWorkunits(query.Value("state")) } else { workunits = core.QMgr.ShowWorkunits("") } cx.RespondWithData(workunits) return } if core.Service == "proxy" { //drive proxy workStealer to checkout work from server core.ProxyWorkChan <- true } //checkout a workunit in FCFS order clientid := query.Value("client") workunits, err := core.QMgr.CheckoutWorkunits("FCFS", clientid, 1) if err != nil { if err.Error() != e.QueueEmpty && err.Error() != e.NoEligibleWorkunitFound && err.Error() != e.ClientNotFound { logger.Error("Err@work_ReadMany:core.QMgr.GetWorkByFCFS(): " + err.Error() + ";client=" + clientid) } cx.RespondWithErrorMessage(err.Error(), http.StatusBadRequest) return } //log access info only when the queue is not empty, save some log LogRequest(cx.Request) //log event about workunit checkout (WO) workids := []string{} for _, work := range workunits { workids = append(workids, work.Id) } logger.Event(event.WORK_CHECKOUT, "workids="+strings.Join(workids, ","), "clientid="+clientid) // Base case respond with node in json cx.RespondWithData(workunits[0]) return }
// DELETE: /job?suspend func (cr *JobController) DeleteMany(cx *goweb.Context) { LogRequest(cx.Request) // Gather query params query := &Query{Li: cx.Request.URL.Query()} if query.Has("suspend") { num := core.QMgr.DeleteSuspendedJobs() cx.RespondWithData(fmt.Sprintf("deleted %d suspended jobs", num)) } else if query.Has("zombie") { num := core.QMgr.DeleteZombieJobs() cx.RespondWithData(fmt.Sprintf("deleted %d zombie jobs", num)) } else { cx.RespondWithError(http.StatusNotImplemented) } return }
// GET: /logger func (cr *LoggerController) ReadMany(cx *goweb.Context) { LogRequest(cx.Request) // Gather query params query := &Query{Li: cx.Request.URL.Query()} // public logging info if query.Has("event") { cx.RespondWithData(event.EventDiscription) return } if query.Has("debug") { cx.RespondWithData(map[string]int{"debuglevel": conf.DEBUG_LEVEL}) return } cx.RespondWithError(http.StatusNotImplemented) return }
// GET: /client func (cr *ClientController) ReadMany(cx *goweb.Context) { LogRequest(cx.Request) // Try to authenticate user. u, err := request.Authenticate(cx.Request) if err != nil && err.Error() != e.NoAuth { cx.RespondWithErrorMessage(err.Error(), http.StatusUnauthorized) return } // If no auth was provided, and anonymous read is allowed, use the public user if u == nil { if conf.ANON_READ == true { u = &user.User{Uuid: "public"} } else { cx.RespondWithErrorMessage(e.NoAuth, http.StatusUnauthorized) return } } clients := core.QMgr.GetAllClientsByUser(u) query := &Query{Li: cx.Request.URL.Query()} filtered := []*core.Client{} if query.Has("busy") { for _, client := range clients { if len(client.Current_work) > 0 { filtered = append(filtered, client) } } } else { filtered = clients } cx.RespondWithData(filtered) return }
// GET: /work/{id} // get a workunit by id, read-only func (cr *WorkController) Read(id string, cx *goweb.Context) { LogRequest(cx.Request) // Gather query params query := &Query{Li: cx.Request.URL.Query()} if (query.Has("datatoken") || query.Has("privateenv")) && query.Has("client") { cg, err := request.AuthenticateClientGroup(cx.Request) if err != nil { if err.Error() == e.NoAuth || err.Error() == e.UnAuth || err.Error() == e.InvalidAuth { if conf.CLIENT_AUTH_REQ == true { cx.RespondWithError(http.StatusUnauthorized) return } } else { logger.Error("Err@AuthenticateClientGroup: " + err.Error()) cx.RespondWithError(http.StatusInternalServerError) return } } // check that clientgroup auth token matches group of client clientid := query.Value("client") client, ok := core.QMgr.GetClient(clientid) if !ok { cx.RespondWithErrorMessage(e.ClientNotFound, http.StatusBadRequest) return } if cg != nil && client.Group != cg.Name { cx.RespondWithErrorMessage("Clientgroup name in token does not match that in the client configuration.", http.StatusBadRequest) return } if query.Has("datatoken") { //a client is requesting data token for this job token, err := core.QMgr.FetchDataToken(id, clientid) if err != nil { cx.RespondWithErrorMessage("error in getting token for job "+id, http.StatusBadRequest) return } //cx.RespondWithData(token) RespondTokenInHeader(cx, token) return } if query.Has("privateenv") { //a client is requesting data token for this job envs, err := core.QMgr.FetchPrivateEnv(id, clientid) if err != nil { cx.RespondWithErrorMessage("error in getting token for job "+id, http.StatusBadRequest) return } //cx.RespondWithData(token) RespondPrivateEnvInHeader(cx, envs) return } } // Try to authenticate user. u, err := request.Authenticate(cx.Request) if err != nil && err.Error() != e.NoAuth { cx.RespondWithErrorMessage(err.Error(), http.StatusUnauthorized) return } // If no auth was provided, and anonymous read is allowed, use the public user if u == nil { if conf.ANON_READ == true { u = &user.User{Uuid: "public"} } else { cx.RespondWithErrorMessage(e.NoAuth, http.StatusUnauthorized) return } } jobid, err := core.GetJobIdByWorkId(id) if err != nil { cx.RespondWithErrorMessage(err.Error(), http.StatusBadRequest) return } job, err := core.LoadJob(jobid) if err != nil { cx.RespondWithErrorMessage(err.Error(), http.StatusBadRequest) return } // User must have read permissions on job or be job owner or be an admin rights := job.Acl.Check(u.Uuid) if job.Acl.Owner != u.Uuid && rights["read"] == false && u.Admin == false { cx.RespondWithErrorMessage(e.UnAuth, http.StatusUnauthorized) return } if query.Has("report") { //retrieve report: stdout or stderr or worknotes reportmsg, err := core.QMgr.GetReportMsg(id, query.Value("report")) if err != nil { cx.RespondWithErrorMessage(err.Error(), http.StatusBadRequest) return } cx.RespondWithData(reportmsg) return } if err != nil { if err.Error() != e.QueueEmpty { logger.Error("Err@work_Read:core.QMgr.GetWorkById(): " + err.Error()) } cx.RespondWithErrorMessage(err.Error(), http.StatusBadRequest) return } // Base case respond with workunit in json workunit, err := core.QMgr.GetWorkById(id) if err != nil { cx.RespondWithErrorMessage(err.Error(), http.StatusBadRequest) return } cx.RespondWithData(workunit) return }
// PUT: /work/{id} -> status update func (cr *WorkController) Update(id string, cx *goweb.Context) { LogRequest(cx.Request) // Gather query params query := &Query{Li: cx.Request.URL.Query()} if !query.Has("client") { cx.RespondWithErrorMessage("This request type requires the client=clientid parameter.", http.StatusBadRequest) return } // Check auth cg, err := request.AuthenticateClientGroup(cx.Request) if err != nil { if err.Error() == e.NoAuth || err.Error() == e.UnAuth || err.Error() == e.InvalidAuth { if conf.CLIENT_AUTH_REQ == true { cx.RespondWithError(http.StatusUnauthorized) return } } else { logger.Error("Err@AuthenticateClientGroup: " + err.Error()) cx.RespondWithError(http.StatusInternalServerError) return } } // check that clientgroup auth token matches group of client clientid := query.Value("client") client, ok := core.QMgr.GetClient(clientid) if !ok { cx.RespondWithErrorMessage(e.ClientNotFound, http.StatusBadRequest) return } if cg != nil && client.Group != cg.Name { cx.RespondWithErrorMessage("Clientgroup name in token does not match that in the client configuration.", http.StatusBadRequest) return } if query.Has("status") && query.Has("client") { //notify execution result: "done" or "fail" notice := core.Notice{WorkId: id, Status: query.Value("status"), ClientId: query.Value("client"), Notes: ""} if query.Has("computetime") { if comptime, err := strconv.Atoi(query.Value("computetime")); err == nil { notice.ComputeTime = comptime } } if query.Has("report") { // if "report" is specified in query, parse performance statistics or errlog if _, files, err := ParseMultipartForm(cx.Request); err == nil { if _, ok := files["perf"]; ok { core.QMgr.FinalizeWorkPerf(id, files["perf"].Path) } if _, ok := files["notes"]; ok { if notes, err := ioutil.ReadFile(files["notes"].Path); err == nil { notice.Notes = string(notes) } } for _, log := range conf.WORKUNIT_LOGS { if _, ok := files[log]; ok { core.QMgr.SaveStdLog(id, log, files[log].Path) } } } } core.QMgr.NotifyWorkStatus(notice) } cx.RespondWithData("ok") return }