// UnmarshalJSON deserializes the Build from JSON. func (b *Build) UnmarshalJSON(data []byte) error { var build jsonBuild if err := json.NewDecoder(bytes.NewBuffer(data)).Decode(&build); err != nil { return err } b.Builder = build.Builder b.Number = build.Number b.Properties = build.Properties b.Results = build.Results if len(build.Times) != 2 { return fmt.Errorf("times array must have length 2: %v", build.Times) } b.Started = util.UnixFloatToTime(build.Times[0]) b.Finished = util.UnixFloatToTime(build.Times[1]) // Parse the following from build properties. var err error b.Repository, err = b.GetStringProperty("repository") if err != nil { return err } b.GotRevision, err = b.GetStringProperty("got_revision") if err != nil { b.GotRevision = "" } b.Branch, err = b.GetStringProperty("branch") if err != nil { return err } b.BuildSlave, err = b.GetStringProperty("slavename") if err != nil { return err } b.Master, err = b.GetStringProperty("mastername") if err != nil { return err } b.Steps = make([]*BuildStep, 0, len(build.Steps)) for _, s := range build.Steps { if len(s.Times) != 2 { return fmt.Errorf("times array must have length 2 (step): %v", s.Times) } results := 0 if len(s.Results) > 0 { if s.Results[0] != nil { results = int(s.Results[0].(float64)) } } b.Steps = append(b.Steps, &BuildStep{ Name: s.Name, Number: s.Number, Results: results, Started: util.UnixFloatToTime(s.Times[0]).UTC(), Finished: util.UnixFloatToTime(s.Times[1]).UTC(), }) } b.fixup() return nil }
func (b *Build) replaceIntoDBTx(tx *sqlx.Tx) (rv error) { // Determine whether we've already ingested this build. If so, fix up the ID // so that we update it rather than insert a new copy. existingBuildID, err := GetBuildIDFromDBTx(b.Builder, b.Master, b.Number, tx) if err == nil && existingBuildID >= 0 { b.Id = existingBuildID } buildID := b.Id var stepIDMap map[*BuildStep]int var commentIDMap map[*BuildComment]int // If successful, update the build with new IDs. defer func() { if rv == nil { // Measure the time between build start and first DB insertion. if b.Id == 0 { t := int64(time.Now().Sub(util.UnixFloatToTime(b.Started))) metrics.GetOrRegisterSlidingWindow("buildbot.startToIngestLatency", metrics.DEFAULT_WINDOW).Update(t) } // Update the build with any new IDs. b.Id = buildID for _, s := range b.Steps { s.BuildID = b.Id } for s, id := range stepIDMap { s.Id = id } for c, id := range commentIDMap { c.Id = id } } }() if buildID == 0 { stmt := fmt.Sprintf("INSERT INTO %s (builder,master,number,results,gotRevision,buildslave,started,finished,properties,branch,repository) VALUES (?,?,?,?,?,?,?,?,?,?,?);", TABLE_BUILDS) res, err := tx.Exec(stmt, b.Builder, b.Master, b.Number, b.Results, b.GotRevision, b.BuildSlave, b.Started, b.Finished, b.PropertiesStr, b.Branch, b.Repository) if err != nil { return fmt.Errorf("Failed to push build into database: %v", err) } id, err := res.LastInsertId() if err != nil { return fmt.Errorf("Failed to push build into database; LastInsertId failed: %v", err) } buildID = int(id) } else { stmt := fmt.Sprintf("UPDATE %s set builder=?, master=?, number=?, results=?, gotRevision=?, buildslave=?, started=?, finished=?, properties=?, branch=?, repository=? WHERE id=?;", TABLE_BUILDS) if _, err := tx.Exec(stmt, b.Builder, b.Master, b.Number, b.Results, b.GotRevision, b.BuildSlave, b.Started, b.Finished, b.PropertiesStr, b.Branch, b.Repository, buildID); err != nil { return fmt.Errorf("Failed to push build into database: %v", err) } } // Build steps. stepIDMap, err = replaceStepsIntoDB(tx, b.Steps, buildID) if err != nil { return err } // Commits. if err = replaceCommitsIntoDB(tx, b.Commits, buildID); err != nil { return err } // Comments. commentIDMap, err = replaceCommentsIntoDB(tx, b.Comments, buildID) if err != nil { return err } // The transaction is committed during the deferred function. return nil }