// 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
}
Exemple #2
0
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
}