| Index: milo/appengine/job_source/buildbot/structs.go
|
| diff --git a/milo/appengine/job_source/buildbot/structs.go b/milo/appengine/job_source/buildbot/structs.go
|
| deleted file mode 100644
|
| index d3f7667a6a5f36c4094c9af1972950643eab0b2d..0000000000000000000000000000000000000000
|
| --- a/milo/appengine/job_source/buildbot/structs.go
|
| +++ /dev/null
|
| @@ -1,439 +0,0 @@
|
| -// Copyright 2016 The LUCI Authors. All rights reserved.
|
| -// Use of this source code is governed under the Apache License, Version 2.0
|
| -// that can be found in the LICENSE file.
|
| -
|
| -package buildbot
|
| -
|
| -import (
|
| - "bytes"
|
| - "compress/gzip"
|
| - "encoding/json"
|
| - "fmt"
|
| - "io/ioutil"
|
| - "strconv"
|
| -
|
| - "github.com/luci/gae/service/datastore"
|
| - "github.com/luci/luci-go/milo/api/resp"
|
| - "github.com/luci/luci-go/milo/appengine/common/model"
|
| -)
|
| -
|
| -// This file contains all of the structs that define buildbot json endpoints.
|
| -// This is primarily used for unmarshalling buildbot master and build json.
|
| -// json.UnmarshalJSON can directly unmarshal buildbot jsons into these structs.
|
| -// Many of the structs were initially built using https://mholt.github.io/json-to-go/
|
| -
|
| -// buildbotStep represents a single step in a buildbot build.
|
| -type buildbotStep struct {
|
| - // We actually don't care about ETA. This is represented as a string if
|
| - // it's fetched from a build json, but a float if it's dug out of the
|
| - // slave portion of a master json. We'll just set it to interface and
|
| - // ignore it.
|
| - Eta interface{} `json:"eta"`
|
| - Expectations [][]interface{} `json:"expectations"`
|
| - Hidden bool `json:"hidden"`
|
| - IsFinished bool `json:"isFinished"`
|
| - IsStarted bool `json:"isStarted"`
|
| - Logs [][]string `json:"logs"`
|
| - Name string `json:"name"`
|
| - Results []interface{} `json:"results"`
|
| - Statistics struct {
|
| - } `json:"statistics"`
|
| - StepNumber int `json:"step_number"`
|
| - Text []string `json:"text"`
|
| - Times []*float64 `json:"times"`
|
| - Urls map[string]string `json:"urls"`
|
| -
|
| - // Log link aliases. The key is a log name that is being aliases. It should,
|
| - // generally, exist within the Logs. The value is the set of aliases attached
|
| - // to that key.
|
| - Aliases map[string][]*buildbotLinkAlias `json:"aliases"`
|
| -}
|
| -
|
| -// buildbotSourceStamp is a list of changes (commits) tagged with where the changes
|
| -// came from, ie. the project/repository. Also includes a "main" revision."
|
| -type buildbotSourceStamp struct {
|
| - Branch *string `json:"branch"`
|
| - Changes []buildbotChange `json:"changes"`
|
| - Haspatch bool `json:"hasPatch"`
|
| - Project string `json:"project"`
|
| - Repository string `json:"repository"`
|
| - Revision string `json:"revision"`
|
| -}
|
| -
|
| -type buildbotLinkAlias struct {
|
| - URL string `json:"url"`
|
| - Text string `json:"text"`
|
| -}
|
| -
|
| -func (a *buildbotLinkAlias) toLink() *resp.Link {
|
| - return resp.NewLink(a.Text, a.URL)
|
| -}
|
| -
|
| -type buildbotProperty struct {
|
| - Name string
|
| - Value interface{}
|
| - Source string
|
| -}
|
| -
|
| -func (p *buildbotProperty) MarshalJSON() ([]byte, error) {
|
| - return json.Marshal([]interface{}{p.Name, p.Value, p.Source})
|
| -}
|
| -
|
| -func (p *buildbotProperty) UnmarshalJSON(d []byte) error {
|
| - // The raw BuildBot representation is a slice of interfaces.
|
| - var raw []interface{}
|
| - if err := json.Unmarshal(d, &raw); err != nil {
|
| - return err
|
| - }
|
| -
|
| - switch len(raw) {
|
| - case 3:
|
| - if s, ok := raw[2].(string); ok {
|
| - p.Source = s
|
| - }
|
| - fallthrough
|
| -
|
| - case 2:
|
| - p.Value = raw[1]
|
| - fallthrough
|
| -
|
| - case 1:
|
| - if s, ok := raw[0].(string); ok {
|
| - p.Name = s
|
| - }
|
| - }
|
| - return nil
|
| -}
|
| -
|
| -// buildbotBuild is a single build json on buildbot.
|
| -type buildbotBuild struct {
|
| - Master string `gae:"$master"`
|
| - Blame []string `json:"blame" gae:"-"`
|
| - Buildername string `json:"builderName"`
|
| - // This needs to be reflected. This can be either a String or a buildbotStep.
|
| - Currentstep interface{} `json:"currentStep" gae:"-"`
|
| - // We don't care about this one.
|
| - Eta interface{} `json:"eta" gae:"-"`
|
| - Logs [][]string `json:"logs" gae:"-"`
|
| - Number int `json:"number"`
|
| - // This is a slice of tri-tuples of [property name, value, source].
|
| - // property name is always a string
|
| - // value can be a string or float
|
| - // source is optional, but is always a string if present
|
| - Properties []*buildbotProperty `json:"properties" gae:"-"`
|
| - Reason string `json:"reason"`
|
| - Results *int `json:"results" gae:"-"`
|
| - Slave string `json:"slave"`
|
| - Sourcestamp *buildbotSourceStamp `json:"sourceStamp" gae:"-"`
|
| - Steps []buildbotStep `json:"steps" gae:"-"`
|
| - Text []string `json:"text" gae:"-"`
|
| - Times []*float64 `json:"times" gae:"-"`
|
| - // This one is injected by Milo. Does not exist in a normal json query.
|
| - TimeStamp *int `json:"timeStamp" gae:"-"`
|
| - // This one is marked by Milo, denotes whether or not the build is internal.
|
| - Internal bool `json:"internal" gae:"-"`
|
| - // This one is computed by Milo for indexing purposes. It does so by
|
| - // checking to see if times[1] is null or not.
|
| - Finished bool `json:"finished"`
|
| - // OS is a string representation of the OS the build ran on. This is
|
| - // derived best-effort from the slave information in the master JSON.
|
| - // This information is injected into the buildbot builds via puppet, and
|
| - // comes as Family + Version. Family is (windows, Darwin, Debian), while
|
| - // Version is the version of the OS, such as (XP, 7, 10) for windows.
|
| - OSFamily string `json:"osFamily"`
|
| - OSVersion string `json:"osVersion"`
|
| -}
|
| -
|
| -func (b *buildbotBuild) toStatus() model.Status {
|
| - var result model.Status
|
| - if b.Currentstep != nil {
|
| - result = model.Running
|
| - } else {
|
| - result = result2Status(b.Results)
|
| - }
|
| - return result
|
| -}
|
| -
|
| -var _ datastore.PropertyLoadSaver = (*buildbotBuild)(nil)
|
| -var _ datastore.MetaGetterSetter = (*buildbotBuild)(nil)
|
| -
|
| -// getID is a helper function that returns the datastore key for a given
|
| -// build.
|
| -func (b *buildbotBuild) getID() string {
|
| - s := []string{b.Master, b.Buildername, strconv.Itoa(b.Number)}
|
| - id, err := json.Marshal(s)
|
| - if err != nil {
|
| - panic(err) // This really shouldn't fail.
|
| - }
|
| - return string(id)
|
| -}
|
| -
|
| -// setKeys is the inverse of getID().
|
| -func (b *buildbotBuild) setKeys(id string) error {
|
| - s := []string{}
|
| - err := json.Unmarshal([]byte(id), &s)
|
| - if err != nil {
|
| - return err
|
| - }
|
| - if len(s) != 3 {
|
| - return fmt.Errorf("%s does not have 3 items", id)
|
| - }
|
| - b.Master = s[0]
|
| - b.Buildername = s[1]
|
| - b.Number, err = strconv.Atoi(s[2])
|
| - return err // or nil.
|
| -}
|
| -
|
| -// GetMeta is overridden so that a query for "id" calls getID() instead of
|
| -// the superclass method.
|
| -func (b *buildbotBuild) GetMeta(key string) (interface{}, bool) {
|
| - if key == "id" {
|
| - if b.Master == "" || b.Buildername == "" {
|
| - panic(fmt.Errorf("No Master or Builder found"))
|
| - }
|
| - return b.getID(), true
|
| - }
|
| - return datastore.GetPLS(b).GetMeta(key)
|
| -}
|
| -
|
| -// GetAllMeta is overridden for the same reason GetMeta() is.
|
| -func (b *buildbotBuild) GetAllMeta() datastore.PropertyMap {
|
| - p := datastore.GetPLS(b).GetAllMeta()
|
| - p.SetMeta("id", b.getID())
|
| - return p
|
| -}
|
| -
|
| -// SetMeta is the inverse of GetMeta().
|
| -func (b *buildbotBuild) SetMeta(key string, val interface{}) bool {
|
| - if key == "id" {
|
| - err := b.setKeys(val.(string))
|
| - if err != nil {
|
| - panic(err)
|
| - }
|
| - }
|
| - return datastore.GetPLS(b).SetMeta(key, val)
|
| -}
|
| -
|
| -// Load translates a propertymap into the struct and loads the data into
|
| -// the struct.
|
| -func (b *buildbotBuild) Load(p datastore.PropertyMap) error {
|
| - if _, ok := p["data"]; !ok {
|
| - // This is probably from a keys-only query. No need to load the rest.
|
| - return datastore.GetPLS(b).Load(p)
|
| - }
|
| - gz, err := p.Slice("data")[0].Project(datastore.PTBytes)
|
| - if err != nil {
|
| - return err
|
| - }
|
| - reader, err := gzip.NewReader(bytes.NewReader(gz.([]byte)))
|
| - if err != nil {
|
| - return err
|
| - }
|
| - bs, err := ioutil.ReadAll(reader)
|
| - if err != nil {
|
| - return err
|
| - }
|
| - return json.Unmarshal(bs, b)
|
| -}
|
| -
|
| -func (b *buildbotBuild) getPropertyValue(name string) interface{} {
|
| - for _, prop := range b.Properties {
|
| - if prop.Name == name {
|
| - return prop.Value
|
| - }
|
| - }
|
| - return ""
|
| -}
|
| -
|
| -type errTooBig struct {
|
| - error
|
| -}
|
| -
|
| -// Save returns a property map of the struct to save in the datastore. It
|
| -// contains two fields, the ID which is the key, and a data field which is a
|
| -// serialized and gzipped representation of the entire struct.
|
| -func (b *buildbotBuild) Save(withMeta bool) (datastore.PropertyMap, error) {
|
| - bs, err := json.Marshal(b)
|
| - if err != nil {
|
| - return nil, err
|
| - }
|
| - gzbs := bytes.Buffer{}
|
| - gsw := gzip.NewWriter(&gzbs)
|
| - _, err = gsw.Write(bs)
|
| - if err != nil {
|
| - return nil, err
|
| - }
|
| - err = gsw.Close()
|
| - if err != nil {
|
| - return nil, err
|
| - }
|
| - blob := gzbs.Bytes()
|
| - // Datastore has a max size of 1MB. If the blob is over 9.5MB, it probably
|
| - // won't fit after accounting for overhead.
|
| - if len(blob) > 950000 {
|
| - return nil, errTooBig{
|
| - fmt.Errorf("buildbotBuild: Build too big to store (%d bytes)", len(blob))}
|
| - }
|
| - p := datastore.PropertyMap{
|
| - "data": datastore.MkPropertyNI(blob),
|
| - }
|
| - if withMeta {
|
| - p["id"] = datastore.MkPropertyNI(b.getID())
|
| - p["master"] = datastore.MkProperty(b.Master)
|
| - p["builder"] = datastore.MkProperty(b.Buildername)
|
| - p["number"] = datastore.MkProperty(b.Number)
|
| - p["finished"] = datastore.MkProperty(b.Finished)
|
| - }
|
| - return p, nil
|
| -}
|
| -
|
| -type buildbotPending struct {
|
| - Source buildbotSourceStamp `json:"source"`
|
| - Reason string `json:"reason"`
|
| - SubmittedAt int `json:"submittedAt"`
|
| - BuilderName string `json:"builderName"`
|
| -}
|
| -
|
| -// buildbotBuilder is a builder struct from the master json, _not_ the builder json.
|
| -type buildbotBuilder struct {
|
| - Basedir string `json:"basedir"`
|
| - CachedBuilds []int `json:"cachedBuilds"`
|
| - PendingBuilds int `json:"pendingBuilds"`
|
| - // This one is specific to the pubsub interface. This is limited to 75,
|
| - // so it could differ from PendingBuilds
|
| - PendingBuildStates []*buildbotPending `json:"pendingBuildStates"`
|
| - Category string `json:"category"`
|
| - CurrentBuilds []int `json:"currentBuilds"`
|
| - Slaves []string `json:"slaves"`
|
| - State string `json:"state"`
|
| -}
|
| -
|
| -// buildbotChangeSource is a changesource (ie polling source) usually tied to a master's scheduler.
|
| -type buildbotChangeSource struct {
|
| - Description string `json:"description"`
|
| -}
|
| -
|
| -// buildbotChange describes a commit in a repository as part of a changesource of blamelist.
|
| -type buildbotChange struct {
|
| - At string `json:"at"`
|
| - Branch *string `json:"branch"`
|
| - Category string `json:"category"`
|
| - Comments string `json:"comments"`
|
| - // This could be a list of strings or list of struct { Name string } .
|
| - Files []interface{} `json:"files"`
|
| - Number int `json:"number"`
|
| - Project string `json:"project"`
|
| - Properties [][]interface{} `json:"properties"`
|
| - Repository string `json:"repository"`
|
| - Rev string `json:"rev"`
|
| - Revision string `json:"revision"`
|
| - Revlink string `json:"revlink"`
|
| - When int `json:"when"`
|
| - Who string `json:"who"`
|
| -}
|
| -
|
| -func (bc *buildbotChange) GetFiles() []string {
|
| - files := make([]string, 0, len(bc.Files))
|
| - for _, f := range bc.Files {
|
| - // Buildbot stores files both as a string, or as a dict with a single entry
|
| - // named "name". It doesn't matter to us what the type is, but we need
|
| - // to reflect on the type anyways.
|
| - switch fn := f.(type) {
|
| - case string:
|
| - files = append(files, fn)
|
| - case map[string]interface{}:
|
| - if name, ok := fn["name"]; ok {
|
| - files = append(files, fmt.Sprintf("%s", name))
|
| - }
|
| - }
|
| - }
|
| - return files
|
| -}
|
| -
|
| -// buildbotSlave describes a slave on a master from a master json, and also includes the
|
| -// full builds of any currently running builds.
|
| -type buildbotSlave struct {
|
| - // RecentBuilds is a map of builder name to a list of recent finished build
|
| - // numbers on that builder.
|
| - RecentBuilds map[string][]int `json:"builders"`
|
| - Connected bool `json:"connected"`
|
| - Host string `json:"host"`
|
| - Name string `json:"name"`
|
| - Runningbuilds []*buildbotBuild `json:"runningBuilds"`
|
| - Version string `json:"version"`
|
| - // This is like runningbuilds, but instead of storing the full build,
|
| - // just reference the build by builder: build num.
|
| - RunningbuildsMap map[string][]int `json:"runningBuildsMap"`
|
| -}
|
| -
|
| -type buildbotProject struct {
|
| - BuildbotURL string `json:"buildbotURL"`
|
| - Title string `json:"title"`
|
| - Titleurl string `json:"titleURL"`
|
| -}
|
| -
|
| -// buildbotMaster This is json definition for https://build.chromium.org/p/<master>/json
|
| -// endpoints.
|
| -type buildbotMaster struct {
|
| - AcceptingBuilds struct {
|
| - AcceptingBuilds bool `json:"accepting_builds"`
|
| - } `json:"accepting_builds"`
|
| -
|
| - Builders map[string]*buildbotBuilder `json:"builders"`
|
| -
|
| - Buildstate struct {
|
| - AcceptingBuilds bool `json:"accepting_builds"`
|
| - Builders []struct {
|
| - Basedir string `json:"basedir"`
|
| - Buildername string `json:"builderName"`
|
| - Cachedbuilds []int `json:"cachedBuilds"`
|
| - Category string `json:"category"`
|
| - Currentbuilds []int `json:"currentBuilds"`
|
| - Slaves []string `json:"slaves"`
|
| - State string `json:"state"`
|
| - } `json:"builders"`
|
| - Project struct {
|
| - BuildbotURL string `json:"buildbotURL"`
|
| - Title string `json:"title"`
|
| - Titleurl string `json:"titleURL"`
|
| - } `json:"project"`
|
| - Timestamp float64 `json:"timestamp"`
|
| - } `json:"buildstate"`
|
| -
|
| - ChangeSources map[string]buildbotChangeSource `json:"change_sources"`
|
| -
|
| - Changes map[string]buildbotChange `json:"changes"`
|
| -
|
| - Clock struct {
|
| - Current struct {
|
| - Local string `json:"local"`
|
| - Utc string `json:"utc"`
|
| - UtcTs float64 `json:"utc_ts"`
|
| - } `json:"current"`
|
| - ServerStarted struct {
|
| - Local string `json:"local"`
|
| - Utc string `json:"utc"`
|
| - UtcTs float64 `json:"utc_ts"`
|
| - } `json:"server_started"`
|
| - ServerUptime float64 `json:"server_uptime"`
|
| - } `json:"clock"`
|
| -
|
| - Project buildbotProject `json:"project"`
|
| -
|
| - Slaves map[string]*buildbotSlave `json:"slaves"`
|
| -
|
| - Varz struct {
|
| - AcceptingBuilds bool `json:"accepting_builds"`
|
| - Builders map[string]struct {
|
| - ConnectedSlaves int `json:"connected_slaves"`
|
| - CurrentBuilds int `json:"current_builds"`
|
| - PendingBuilds int `json:"pending_builds"`
|
| - State string `json:"state"`
|
| - TotalSlaves int `json:"total_slaves"`
|
| - } `json:"builders"`
|
| - ServerUptime float64 `json:"server_uptime"`
|
| - } `json:"varz"`
|
| -
|
| - // This is injected by the pubsub publisher on the buildbot side.
|
| - Name string `json:"name"`
|
| -}
|
|
|