func reportHandler(w http.ResponseWriter, r *http.Request) { w.Header().Set("Content-Type", "application/json") protocolVersion := r.Header.Get("X-Ops-Reporting-Protocol-Version") if protocolVersion == "" { // try a param (makes working with webui easier) form, e := url.ParseQuery(r.URL.RawQuery) if e != nil { jsonErrorReport(w, r, e.Error(), http.StatusBadRequest) return } if p, f := form["protocol-version"]; f { if len(p) > 0 { protocolVersion = p[0] } } } // someday there may be other protocol versions if protocolVersion != "0.1.0" { jsonErrorReport(w, r, "Unsupported reporting protocol version", http.StatusNotFound) return } opUser, oerr := actor.GetReqUser(r.Header.Get("X-OPS-USERID")) if oerr != nil { jsonErrorReport(w, r, oerr.Error(), oerr.Status()) return } // TODO: some params for time ranges exist and need to be handled // properly pathArray := splitPath(r.URL.Path) pathArrayLen := len(pathArray) reportResponse := make(map[string]interface{}) switch r.Method { case "GET": // Making an informed guess that admin rights are needed // to see the node run reports r.ParseForm() var rows int var from, until time.Time var status string if fr, found := r.Form["rows"]; found { if len(fr) < 0 { jsonErrorReport(w, r, "invalid rows", http.StatusBadRequest) return } var err error rows, err = strconv.Atoi(fr[0]) if err != nil { jsonErrorReport(w, r, err.Error(), http.StatusBadRequest) return } } else { // default is 10 rows = 10 } if ff, found := r.Form["from"]; found { if len(ff) < 0 { jsonErrorReport(w, r, "invalid from", http.StatusBadRequest) return } fromUnix, err := strconv.ParseInt(ff[0], 10, 64) if err != nil { jsonErrorReport(w, r, err.Error(), http.StatusBadRequest) return } from = time.Unix(fromUnix, 0) } else { from = time.Now().Add(-(time.Duration(24*90) * time.Hour)) } if fu, found := r.Form["until"]; found { if len(fu) < 0 { jsonErrorReport(w, r, "invalid until", http.StatusBadRequest) return } untilUnix, err := strconv.ParseInt(fu[0], 10, 64) if err != nil { jsonErrorReport(w, r, err.Error(), http.StatusBadRequest) return } until = time.Unix(untilUnix, 0) } else { until = time.Now() } if st, found := r.Form["status"]; found { if len(st) < 0 { jsonErrorReport(w, r, "invalid status", http.StatusBadRequest) return } status = st[0] if status != "started" && status != "success" && status != "failure" { jsonErrorReport(w, r, "invalid status given", http.StatusBadRequest) return } } // If the end time is more than 90 days ahead of the // start time, give an error if from.Truncate(time.Hour).Sub(until.Truncate(time.Hour)) >= (time.Duration(24*90) * time.Hour) { msg := fmt.Sprintf("End time %s is too far ahead of start time %s (max 90 days)", until.String(), from.String()) jsonErrorReport(w, r, msg, http.StatusNotAcceptable) return } if !opUser.IsAdmin() { jsonErrorReport(w, r, "You are not allowed to perform this action", http.StatusForbidden) return } if pathArrayLen < 3 || pathArrayLen > 4 { jsonErrorReport(w, r, "Bad request", http.StatusBadRequest) return } op := pathArray[1] if op == "nodes" && pathArrayLen == 4 { nodeName := pathArray[2] runs, nerr := report.GetNodeList(nodeName, from, until, rows, status) if nerr != nil { jsonErrorReport(w, r, nerr.Error(), http.StatusInternalServerError) return } reportResponse["run_history"] = runs } else if op == "org" { if pathArrayLen == 4 { runID := pathArray[3] run, err := report.Get(runID) if err != nil { jsonErrorReport(w, r, err.Error(), err.Status()) return } reportResponse = formatRunShow(run) } else { runs, rerr := report.GetReportList(from, until, rows, status) if rerr != nil { jsonErrorReport(w, r, rerr.Error(), http.StatusInternalServerError) return } reportResponse["run_history"] = runs } } else { jsonErrorReport(w, r, "Bad request", http.StatusBadRequest) return } case "POST": // Can't use the usual parseObjJSON function here, since // the reporting "run_list" type is a string rather // than []interface{}. jsonReport := make(map[string]interface{}) dec := json.NewDecoder(r.Body) if jerr := dec.Decode(&jsonReport); jerr != nil { jsonErrorReport(w, r, jerr.Error(), http.StatusBadRequest) return } if pathArrayLen < 4 || pathArrayLen > 5 { jsonErrorReport(w, r, "Bad request", http.StatusBadRequest) return } nodeName := pathArray[2] if pathArrayLen == 4 { rep, err := report.NewFromJSON(nodeName, jsonReport) if err != nil { jsonErrorReport(w, r, err.Error(), err.Status()) return } // what's the expected response? serr := rep.Save() if serr != nil { jsonErrorReport(w, r, serr.Error(), http.StatusInternalServerError) return } reportResponse["run_detail"] = rep } else { runID := pathArray[4] rep, err := report.Get(runID) if err != nil { jsonErrorReport(w, r, err.Error(), err.Status()) return } err = rep.UpdateFromJSON(jsonReport) if err != nil { jsonErrorReport(w, r, err.Error(), err.Status()) return } serr := rep.Save() if serr != nil { jsonErrorReport(w, r, err.Error(), http.StatusInternalServerError) return } // .... and? reportResponse["run_detail"] = rep } default: jsonErrorReport(w, r, "Bad request", http.StatusBadRequest) return } enc := json.NewEncoder(w) if err := enc.Encode(&reportResponse); err != nil { jsonErrorReport(w, r, err.Error(), http.StatusInternalServerError) } }
func importAll(fileName string) error { fp, err := os.Open(fileName) if err != nil { return err } exportedData := &ExportData{} dec := json.NewDecoder(fp) if err := dec.Decode(&exportedData); err != nil { return err } // What versions of the exported data are supported? // At the moment it's only 1.0. if exportedData.MajorVersion == 1 && (exportedData.MinorVersion == 0 || exportedData.MinorVersion == 1) { logger.Infof("Importing data, version %d.%d created on %s", exportedData.MajorVersion, exportedData.MinorVersion, exportedData.CreatedTime) // load clients logger.Infof("Loading clients") for _, v := range exportedData.Data["client"] { c, err := client.NewFromJSON(v.(map[string]interface{})) if err != nil { return err } pkerr := c.SetPublicKey(v.(map[string]interface{})["public_key"]) if pkerr != nil { return pkerr } gerr := c.Save() if gerr != nil { return gerr } } // load users logger.Infof("Loading users") for _, v := range exportedData.Data["user"] { pwhash, _ := v.(map[string]interface{})["password"].(string) v.(map[string]interface{})["password"] = "" u, err := user.NewFromJSON(v.(map[string]interface{})) if err != nil { return err } u.SetPasswdHash(pwhash) pkerr := u.SetPublicKey(v.(map[string]interface{})["public_key"]) if pkerr != nil { return pkerr } gerr := u.Save() if gerr != nil { return gerr } } // load filestore logger.Infof("Loading filestore") for _, v := range exportedData.Data["filestore"] { fileData, err := base64.StdEncoding.DecodeString(v.(map[string]interface{})["Data"].(string)) if err != nil { return err } fdBuf := bytes.NewBuffer(fileData) fdRc := ioutil.NopCloser(fdBuf) fs, err := filestore.New(v.(map[string]interface{})["Chksum"].(string), fdRc, int64(fdBuf.Len())) if err != nil { return err } if err = fs.Save(); err != nil { return err } } // load cookbooks logger.Infof("Loading cookbooks") for _, v := range exportedData.Data["cookbook"] { cb, err := cookbook.New(v.(map[string]interface{})["Name"].(string)) if err != nil { return err } gerr := cb.Save() if gerr != nil { return gerr } for ver, cbvData := range v.(map[string]interface{})["Versions"].(map[string]interface{}) { cbvData, cerr := checkAttrs(cbvData.(map[string]interface{})) if cerr != nil { return cerr } _, cbverr := cb.NewVersion(ver, cbvData) if cbverr != nil { return cbverr } } } // load data bags logger.Infof("Loading data bags") for _, v := range exportedData.Data["data_bag"] { dbag, err := databag.New(v.(map[string]interface{})["Name"].(string)) if err != nil { return err } gerr := dbag.Save() if gerr != nil { return gerr } for _, dbagData := range v.(map[string]interface{})["DataBagItems"].(map[string]interface{}) { _, dbierr := dbag.NewDBItem(dbagData.(map[string]interface{})["raw_data"].(map[string]interface{})) if dbierr != nil { return dbierr } } gerr = dbag.Save() if gerr != nil { return gerr } } // load environments logger.Infof("Loading environments") for _, v := range exportedData.Data["environment"] { envData, cerr := checkAttrs(v.(map[string]interface{})) if cerr != nil { return nil } if envData["name"].(string) != "_default" { e, err := environment.NewFromJSON(envData) if err != nil { return err } gerr := e.Save() if gerr != nil { return gerr } } } // load nodes logger.Infof("Loading nodes") for _, v := range exportedData.Data["node"] { nodeData, cerr := checkAttrs(v.(map[string]interface{})) if cerr != nil { return nil } n, err := node.NewFromJSON(nodeData) if err != nil { return err } gerr := n.Save() if gerr != nil { return gerr } } // load roles logger.Infof("Loading roles") for _, v := range exportedData.Data["role"] { roleData, cerr := checkAttrs(v.(map[string]interface{})) if cerr != nil { return nil } r, err := role.NewFromJSON(roleData) if err != nil { return err } gerr := r.Save() if gerr != nil { return gerr } } // load sandboxes logger.Infof("Loading sandboxes") for _, v := range exportedData.Data["sandbox"] { sbid, _ := v.(map[string]interface{})["Id"].(string) sbts, _ := v.(map[string]interface{})["CreationTime"].(string) sbcomplete, _ := v.(map[string]interface{})["Completed"].(bool) sbck, _ := v.(map[string]interface{})["Checksums"].([]interface{}) sbTime, err := time.Parse(time.RFC3339, sbts) if err != nil { return err } sbChecksums := make([]string, len(sbck)) for i, c := range sbck { sbChecksums[i] = c.(string) } sbox := &sandbox.Sandbox{ID: sbid, CreationTime: sbTime, Completed: sbcomplete, Checksums: sbChecksums} if err = sbox.Save(); err != nil { return err } } // load loginfos logger.Infof("Loading loginfo") for _, v := range exportedData.Data["loginfo"] { if err := loginfo.Import(v.(map[string]interface{})); err != nil { return err } } // load reports logger.Infof("Loading reports") for _, o := range exportedData.Data["report"] { // handle data exported from a bugged report export var nodeName string v := o.(map[string]interface{}) if n, ok := v["node_name"]; ok { nodeName = n.(string) } else if n, ok := v["nodeName"]; ok { nodeName = n.(string) } v["action"] = "start" if st, ok := v["start_time"].(string); ok { t, err := time.Parse(time.RFC3339, st) if err != nil { return err } v["start_time"] = t.Format(report.ReportTimeFormat) } if et, ok := v["end_time"].(string); ok { t, err := time.Parse(time.RFC3339, et) if err != nil { return err } v["end_time"] = t.Format(report.ReportTimeFormat) } r, err := report.NewFromJSON(nodeName, v) if err != nil { return err } gerr := r.Save() if gerr != nil { return gerr } v["action"] = "end" if err := r.UpdateFromJSON(v); err != nil { return err } gerr = r.Save() if gerr != nil { return gerr } } if exportedData.MinorVersion == 1 { // import shovey jobs, run, and streams, and node // statuses logger.Infof("Loading node statuses...") for _, v := range exportedData.Data["node_status"] { ns := v.(map[string]interface{}) err := node.ImportStatus(ns) if err != nil { return err } } logger.Infof("Loading shoveys...") for _, v := range exportedData.Data["shovey"] { s := v.(map[string]interface{}) err := shovey.ImportShovey(s) if err != nil { return err } } logger.Infof("Loading shovey runs...") for _, v := range exportedData.Data["shovey_run"] { s := v.(map[string]interface{}) err := shovey.ImportShoveyRun(s) if err != nil { return err } } logger.Infof("Loading shovey run streams...") for _, v := range exportedData.Data["shovey_run_stream"] { s := v.(map[string]interface{}) err := shovey.ImportShoveyRunStream(s) if err != nil { return err } } } } else { err := fmt.Errorf("goiardi export data version %d.%d is not supported by this version of goiardi", exportedData.MajorVersion, exportedData.MinorVersion) return err } return nil }