コード例 #1
0
ファイル: main.go プロジェクト: saltmueller/skia-buildbot
// ingestBuild encapsulates many of the steps of ingesting a build:
//   - Record the mapping between the codename (build.Builder) and the internal target name.
//   - If no matching build exists, assign a new build number for this build and insert it.
//   - Otherwise, update the existing build to match the given build.
func ingestBuild(build *buildbot.Build, commitHash, target string) error {
	// Store build.Builder (the codename) with its pair build.Target.Name in a local leveldb to serve redirects.
	if err := codenameDB.Put([]byte(build.Builder), []byte(target), nil); err != nil {
		glog.Errorf("Failed to write codename to data store: %s", err)
	}

	buildNumber, err := db.GetBuildNumberForCommit(build.Master, build.Builder, commitHash)
	if err != nil {
		return fmt.Errorf("Failed to find the build in the database: %s", err)
	}
	glog.Infof("GetBuildForCommit at hash: %s returned %d", commitHash, buildNumber)
	var cachedBuild *buildbot.Build
	if buildNumber != -1 {
		cachedBuild, err = db.GetBuildFromDB(build.Master, build.Builder, buildNumber)
		if err != nil {
			return fmt.Errorf("Failed to retrieve build from database: %s", err)
		}
	}
	if cachedBuild == nil {
		// This is a new build we've never seen before, so add it to the buildbot database.

		// TODO(benjaminwagner): This logic won't work well for concurrent requests. Revisit
		// after borenet's "giant datahopper change."

		// First calculate a new unique build.Number.
		number, err := db.GetMaxBuildNumber(build.Master, build.Builder)
		if err != nil {
			return fmt.Errorf("Failed to find next build number: %s", err)
		}
		build.Number = number + 1
		glog.Infof("Writing new build to the database: %s %d", build.Builder, build.Number)
	} else {
		// If the state of the build has changed then write it to the buildbot database.
		glog.Infof("Writing updated build to the database: %s %d", build.Builder, build.Number)
	}
	if err := buildbot.IngestBuild(db, build, repos); err != nil {
		return fmt.Errorf("Failed to ingest build: %s", err)
	}
	return nil
}
コード例 #2
0
ファイル: build_queue.go プロジェクト: 1394/skia-buildbot
// getBestCandidate finds the best BuildCandidate for the given builder.
func (q *BuildQueue) getBestCandidate(bc *buildCache, recentCommits []string, now time.Time) (float64, *buildbot.Build, *buildbot.Build, error) {
	errMsg := fmt.Sprintf("Failed to get best candidate for %s: %%v", bc.Builder)
	repo, err := q.repos.Repo(bc.Repo)
	if err != nil {
		return 0.0, nil, nil, fmt.Errorf(errMsg, err)
	}
	// Find the current scores for each commit.
	currentScores := map[string]float64{}
	for _, commit := range recentCommits {
		currentBuild, err := bc.getBuildForCommit(commit)
		if err != nil {
			return 0.0, nil, nil, fmt.Errorf(errMsg, err)
		}
		d, err := repo.Details(commit)
		if err != nil {
			return 0.0, nil, nil, err
		}
		currentScores[commit] = scoreBuild(d, currentBuild, now, q.timeLambda)
	}

	// For each commit/builder pair, determine the score increase obtained
	// by running a build at that commit.
	scoreIncrease := map[string]float64{}
	newBuildsByCommit := map[string]*buildbot.Build{}
	stoleFromByCommit := map[string]*buildbot.Build{}
	for _, commit := range recentCommits {
		// Shortcut: Don't bisect builds with a huge number
		// of commits.  This saves lots of time and only affects
		// the first successful build for a bot.
		b, err := bc.getBuildForCommit(commit)
		if err != nil {
			return 0.0, nil, nil, fmt.Errorf(errMsg, err)
		}
		if b != nil {
			if len(b.Commits) > NO_BISECT_COMMIT_LIMIT {
				glog.Warningf("Skipping %s on %s; previous build has too many commits (#%d)", commit[0:7], b.Builder, b.Number)
				scoreIncrease[commit] = 0.0
				break // Don't bother looking at previous commits either, since these will be out of range.
			}
		}

		newScores := map[string]float64{}
		// Pretend to create a new Build at the given commit.
		newBuild := buildbot.Build{
			Builder:     bc.Builder,
			Master:      bc.Master,
			Number:      bc.MaxBuildNum + 1,
			GotRevision: commit,
			Repository:  bc.Repo,
		}
		commits, stealFrom, stolen, err := buildbot.FindCommitsForBuild(bc, &newBuild, q.repos)
		if err != nil {
			return 0.0, nil, nil, fmt.Errorf(errMsg, err)
		}
		// Re-score all commits in the new build.
		newBuild.Commits = commits
		for _, c := range commits {
			d, err := repo.Details(c)
			if err != nil {
				return 0.0, nil, nil, fmt.Errorf(errMsg, err)
			}
			if _, ok := currentScores[c]; !ok {
				// If this build has commits which are outside of our window,
				// insert them into currentScores to account for them.
				b, err := bc.getBuildForCommit(c)
				if err != nil {
					return 0.0, nil, nil, fmt.Errorf(errMsg, err)
				}
				score := scoreBuild(d, b, now, q.timeLambda)
				currentScores[c] = score
			}
			newScores[c] = scoreBuild(d, &newBuild, now, q.timeLambda)
		}
		newBuildsByCommit[commit] = &newBuild
		// If the new build includes commits previously included in
		// another build, update scores for commits in the build we stole
		// them from.
		if stealFrom != -1 {
			stoleFromOrig, err := bc.getByNumber(stealFrom)
			if err != nil {
				return 0.0, nil, nil, fmt.Errorf(errMsg, err)
			}
			if stoleFromOrig == nil {
				// The build may not be cached. Fall back on getting it from the DB.
				stoleFromOrig, err = buildbot.GetBuildFromDB(bc.Builder, bc.Master, stealFrom)
				if err != nil {
					return 0.0, nil, nil, fmt.Errorf(errMsg, err)
				}
				if err := bc.Put(stoleFromOrig); err != nil {
					return 0.0, nil, nil, err
				}
			}
			// "copy" the build so that we can assign new commits to it
			// without modifying the cached build.
			stoleFromBuild := *stoleFromOrig
			newCommits := []string{}
			for _, c := range stoleFromBuild.Commits {
				if !util.In(c, stolen) {
					newCommits = append(newCommits, c)
				}
			}
			stoleFromBuild.Commits = newCommits
			for _, c := range stoleFromBuild.Commits {
				d, err := repo.Details(c)
				if err != nil {
					return 0.0, nil, nil, err
				}
				newScores[c] = scoreBuild(d, &stoleFromBuild, now, q.timeLambda)
			}
			stoleFromByCommit[commit] = &stoleFromBuild
		}
		// Sum the old and new scores.
		oldScoresList := make([]float64, 0, len(newScores))
		newScoresList := make([]float64, 0, len(newScores))
		for c, score := range newScores {
			oldScoresList = append(oldScoresList, currentScores[c])
			newScoresList = append(newScoresList, score)
		}
		oldTotal := util.Float64StableSum(oldScoresList)
		newTotal := util.Float64StableSum(newScoresList)
		scoreIncrease[commit] = newTotal - oldTotal
	}

	// Arrange the score increases by builder.
	candidates := []*BuildCandidate{}
	for commit, increase := range scoreIncrease {
		candidates = append(candidates, &BuildCandidate{
			Commit: commit,
			Score:  increase,
		})
	}
	sort.Sort(BuildCandidateSlice(candidates))
	best := candidates[len(candidates)-1]

	return best.Score, newBuildsByCommit[best.Commit], stoleFromByCommit[best.Commit], nil
}
コード例 #3
0
ファイル: main.go プロジェクト: 1394/skia-buildbot
// step does a single step in ingesting builds from tradefed and pushing the results into the buildbot database.
func step(targets []string, buildService *androidbuildinternal.Service, repos *gitinfo.RepoMap) {
	glog.Errorf("step: Begin")

	if err := repos.Update(); err != nil {
		glog.Errorf("Failed to update repos: %s", err)
		return
	}
	// Loop over every target and look for skia commits in the builds.
	for _, target := range targets {
		r, err := buildService.Build.List().Branch(SKIA_BRANCH).BuildType("submitted").Target(target).ExtraFields("changeInfo").MaxResults(40).Do()
		if err != nil {
			glog.Errorf("Failed to load internal builds: %v", err)
			continue
		}
		// Iterate over the builds in reverse order so we ingest the earlier Git
		// hashes first and the more recent Git hashes later.
		for i := len(r.Builds) - 1; i >= 0; i-- {
			b := r.Builds[i]
			commits := androidbuild.CommitsFromChanges(b.Changes)
			glog.Infof("Commits: %#v", commits)
			if len(commits) > 0 {
				var cachedBuild *buildbot.Build = nil
				// Only look at the first commit in the list. The commits always appear in reverse chronological order, so
				// the 0th entry is the most recent commit.
				c := commits[0]
				// Create a buildbot.Build from the build info.
				key, build := buildFromCommit(b, c)
				glog.Infof("Key: %s Hash: %s", key, c.Hash)

				// Store build.Builder (the codename) with its pair build.Target.Name in a local leveldb to serve redirects.
				if err := codenameDB.Put([]byte(build.Builder), []byte(b.Target.Name), nil); err != nil {
					glog.Errorf("Failed to write codename to data store: %s", err)
				}

				buildNumber, err := buildbot.GetBuildForCommit(build.Builder, build.Master, c.Hash)
				if err != nil {
					glog.Errorf("Failed to find the build in the database: %s", err)
					continue
				}
				glog.Infof("GetBuildForCommit at hash: %s returned %d", c.Hash, buildNumber)
				if buildNumber != -1 {
					cachedBuild, err = buildbot.GetBuildFromDB(build.Builder, build.Master, buildNumber)
					if err != nil {
						glog.Errorf("Failed to retrieve build from database: %s", err)
						continue
					}
				}
				if cachedBuild == nil {
					// This is a new build we've never seen before, so add it to the buildbot database.

					// First calculate a new unique build.Number.
					number, err := buildbot.GetMaxBuildNumber(build.Builder)
					if err != nil {
						glog.Infof("Failed to find next build number: %s", err)
						continue
					}
					build.Number = number + 1
					if err := buildbot.IngestBuild(build, repos); err != nil {
						glog.Errorf("Failed to ingest build: %s", err)
						continue
					}
					cachedBuild = build
				}

				// If the state of the build has changed then write it to the buildbot database.
				if buildsDiffer(build, cachedBuild) {
					// If this was a failure then we need to check that there is a mirror
					// failure on the main branch, at which point we will say that this
					// is a warning.
					if build.Results == buildbot.BUILDBOT_FAILURE && brokenOnMaster(buildService, target, b.BuildId) {
						build.Results = buildbot.BUILDBOT_WARNING
					}
					cachedBuild.Results = build.Results
					cachedBuild.Finished = build.Finished
					glog.Infof("Writing updated build to the database: %s %d", cachedBuild.Builder, cachedBuild.Number)
					if err := buildbot.IngestBuild(cachedBuild, repos); err != nil {
						glog.Errorf("Failed to ingest build: %s", err)
					}
				}
			}
			liveness.Update()
		}
	}
}
コード例 #4
0
ファイル: main.go プロジェクト: 1394/skia-buildbot
// buildsDiffer returns true if the given Build's have different finished or results status.
func buildsDiffer(a, b *buildbot.Build) bool {
	return a.IsFinished() != b.IsFinished() || a.Results != b.Results
}
コード例 #5
0
// updateRepo syncs the given repo and returns a set of BuildCandidates for it.
func (q *BuildQueue) updateRepo(repoUrl string, repo *gitinfo.GitInfo, now time.Time) (map[string][]*BuildCandidate, error) {
	from := now.Add(-q.period)
	if q.period == PERIOD_FOREVER {
		from = time.Unix(0, 0)
	}
	recentCommits := repo.From(from)
	commitDetails := map[string]*gitinfo.LongCommit{}
	for _, c := range recentCommits {
		details, err := repo.Details(c)
		if err != nil {
			return nil, err
		}
		commitDetails[c] = details
	}

	// Get all builds associated with the recent commits.
	buildsByCommit, err := buildbot.GetBuildsForCommits(recentCommits, map[int]bool{})
	if err != nil {
		return nil, err
	}

	// Find the sets of all bots and masters, organize builds by
	// commit/builder and builder/number.
	masters := map[string]string{}
	builds := map[string]map[string]*buildbot.Build{}
	buildsByBuilderAndNum := map[string]map[int]*buildbot.Build{}
	for commit, buildsForCommit := range buildsByCommit {
		builds[commit] = map[string]*buildbot.Build{}
		for _, build := range buildsForCommit {
			if !util.In(build.Builder, q.botWhitelist) {
				continue
			}
			masters[build.Builder] = build.Master
			builds[commit][build.Builder] = build
			if _, ok := buildsByBuilderAndNum[build.Builder]; !ok {
				buildsByBuilderAndNum[build.Builder] = map[int]*buildbot.Build{}
			}
			buildsByBuilderAndNum[build.Builder][build.Number] = build
		}
	}
	allBots := make([]string, 0, len(masters))
	for builder, _ := range masters {
		allBots = append(allBots, builder)
	}

	// Find the current scores for each commit/builder pair.
	currentScores := map[string]map[string]float64{}
	for _, commit := range recentCommits {
		myBuilds, ok := builds[commit]
		if !ok {
			myBuilds = map[string]*buildbot.Build{}
		}
		currentScores[commit] = map[string]float64{}
		for _, builder := range allBots {
			currentScores[commit][builder] = scoreBuild(commitDetails[commit], myBuilds[builder], now, q.timeLambda)
		}
	}

	// For each commit/builder pair, determine the score increase obtained
	// by running a build at that commit.
	scoreIncrease := map[string]map[string]float64{}
	for _, commit := range recentCommits {
		scoreIncrease[commit] = map[string]float64{}
		for _, builder := range allBots {
			// Shortcut: Don't bisect builds with a huge number
			// of commits.  This saves lots of time and only affects
			// the first successful build for a bot.
			if _, ok := builds[commit][builder]; ok {
				if len(builds[commit][builder].Commits) > NO_BISECT_COMMIT_LIMIT {
					glog.Warningf("Skipping %s on %s; previous build has too many commits.", commit[0:7], builder)
					scoreIncrease[commit][builder] = 0.0
					continue
				}
			}

			newScores := map[string]float64{}
			// Pretend to create a new Build at the given commit.
			newBuild := buildbot.Build{
				Builder:     builder,
				Master:      masters[builder],
				Number:      math.MaxInt32,
				GotRevision: commit,
				Repository:  repoUrl,
			}
			commits, stealFrom, stolen, err := buildbot.FindCommitsForBuild(&newBuild, q.repos)
			if err != nil {
				return nil, err
			}
			// Re-score all commits in the new build.
			newBuild.Commits = commits
			for _, c := range commits {
				if _, ok := currentScores[c]; !ok {
					// If this build has commits which are outside of our window,
					// insert them into currentScores to account for them.
					score := scoreBuild(commitDetails[commit], builds[commit][builder], now, q.timeLambda)
					currentScores[c] = map[string]float64{
						builder: score,
					}
				}
				if _, ok := commitDetails[c]; !ok {
					d, err := repo.Details(c)
					if err != nil {
						return nil, err
					}
					commitDetails[c] = d
				}
				newScores[c] = scoreBuild(commitDetails[c], &newBuild, now, q.timeLambda)
			}
			// If the new build includes commits previously included in
			// another build, update scores for commits in the build we stole
			// them from.
			if stealFrom != -1 {
				stoleFromOrig, ok := buildsByBuilderAndNum[builder][stealFrom]
				if !ok {
					// The build may not be cached. Fall back on getting it from the DB.
					stoleFromOrig, err = buildbot.GetBuildFromDB(builder, masters[builder], stealFrom)
					if err != nil {
						return nil, err
					}
					buildsByBuilderAndNum[builder][stealFrom] = stoleFromOrig
				}
				// "copy" the build so that we can assign new commits to it
				// without modifying the cached build.
				stoleFromBuild := *stoleFromOrig
				newCommits := []string{}
				for _, c := range stoleFromBuild.Commits {
					if !util.In(c, stolen) {
						newCommits = append(newCommits, c)
					}
				}
				stoleFromBuild.Commits = newCommits
				for _, c := range stoleFromBuild.Commits {
					newScores[c] = scoreBuild(commitDetails[c], &stoleFromBuild, now, q.timeLambda)
				}
			}
			// Sum the old and new scores.
			// First, sort the old and new scores to help with numerical stability.
			oldScoresList := make([]float64, 0, len(newScores))
			newScoresList := make([]float64, 0, len(newScores))
			for c, score := range newScores {
				oldScoresList = append(oldScoresList, currentScores[c][builder])
				newScoresList = append(newScoresList, score)
			}
			sort.Sort(sort.Float64Slice(oldScoresList))
			sort.Sort(sort.Float64Slice(newScoresList))
			oldTotal := 0.0
			newTotal := 0.0
			for i, _ := range oldScoresList {
				oldTotal += oldScoresList[i]
				newTotal += newScoresList[i]
			}
			scoreIncrease[commit][builder] = newTotal - oldTotal
		}
	}

	// Arrange the score increases by builder.
	candidates := map[string][]*BuildCandidate{}
	for commit, builders := range scoreIncrease {
		for builder, scoreIncrease := range builders {
			if _, ok := candidates[builder]; !ok {
				candidates[builder] = []*BuildCandidate{}
			}
			// Don't schedule builds below the given threshold.
			if scoreIncrease > q.scoreThreshold {
				candidates[builder] = append(candidates[builder], &BuildCandidate{
					Author:  commitDetails[commit].Author,
					Builder: builder,
					Commit:  commit,
					Repo:    repoUrl,
					Score:   scoreIncrease,
				})
			}
		}
	}

	return candidates, nil
}