| OLD | NEW |
| 1 // Copyright 2016 The LUCI Authors. All rights reserved. | 1 // Copyright 2016 The LUCI Authors. All rights reserved. |
| 2 // Use of this source code is governed under the Apache License, Version 2.0 | 2 // Use of this source code is governed under the Apache License, Version 2.0 |
| 3 // that can be found in the LICENSE file. | 3 // that can be found in the LICENSE file. |
| 4 | 4 |
| 5 package buildbot | 5 package buildbot |
| 6 | 6 |
| 7 import ( | 7 import ( |
| 8 "encoding/json" | 8 "encoding/json" |
| 9 "fmt" | 9 "fmt" |
| 10 "net/http" |
| 10 "os" | 11 "os" |
| 11 "sort" | 12 "sort" |
| 12 "strings" | 13 "strings" |
| 13 "time" | 14 "time" |
| 14 | 15 |
| 15 "github.com/luci/gae/service/datastore" | 16 "github.com/luci/gae/service/datastore" |
| 16 | 17 |
| 17 "github.com/luci/luci-go/common/clock" | 18 "github.com/luci/luci-go/common/clock" |
| 18 » log "github.com/luci/luci-go/common/logging" | 19 » "github.com/luci/luci-go/common/logging" |
| 19 "github.com/luci/luci-go/milo/api/resp" | 20 "github.com/luci/luci-go/milo/api/resp" |
| 21 "github.com/luci/luci-go/milo/appengine/settings" |
| 22 "github.com/luci/luci-go/milo/common/miloerror" |
| 20 "golang.org/x/net/context" | 23 "golang.org/x/net/context" |
| 21 ) | 24 ) |
| 22 | 25 |
| 23 // builderRef is used for keying specific builds in a master json. | 26 // builderRef is used for keying specific builds in a master json. |
| 24 type builderRef struct { | 27 type builderRef struct { |
| 25 builder string | 28 builder string |
| 26 buildNum int | 29 buildNum int |
| 27 } | 30 } |
| 28 | 31 |
| 29 // buildMap contains all of the current build within a master json. We use this | 32 // buildMap contains all of the current build within a master json. We use this |
| (...skipping 25 matching lines...) Expand all Loading... |
| 55 Started: started, | 58 Started: started, |
| 56 Finished: finished, | 59 Finished: finished, |
| 57 Duration: duration, | 60 Duration: duration, |
| 58 }, | 61 }, |
| 59 Text: b.Text, | 62 Text: b.Text, |
| 60 Blame: blame(b), | 63 Blame: blame(b), |
| 61 Revision: b.Sourcestamp.Revision, | 64 Revision: b.Sourcestamp.Revision, |
| 62 } | 65 } |
| 63 } | 66 } |
| 64 | 67 |
| 65 // getBuilds fetches all of the recent builds from the datastore. | 68 // getBuilds fetches all of the recent builds from the datastore. Note that |
| 69 // getBuilds() does not perform ACL checks. |
| 66 func getBuilds(c context.Context, masterName, builderName string, finished bool)
([]*resp.BuildSummary, error) { | 70 func getBuilds(c context.Context, masterName, builderName string, finished bool)
([]*resp.BuildSummary, error) { |
| 67 // TODO(hinoka): Builder specific structs. | 71 // TODO(hinoka): Builder specific structs. |
| 68 result := []*resp.BuildSummary{} | 72 result := []*resp.BuildSummary{} |
| 69 ds := datastore.Get(c) | 73 ds := datastore.Get(c) |
| 70 q := datastore.NewQuery("buildbotBuild") | 74 q := datastore.NewQuery("buildbotBuild") |
| 71 q = q.Eq("finished", finished) | 75 q = q.Eq("finished", finished) |
| 72 q = q.Eq("master", masterName) | 76 q = q.Eq("master", masterName) |
| 73 q = q.Eq("builder", builderName) | 77 q = q.Eq("builder", builderName) |
| 74 q = q.Limit(25) // TODO(hinoka): This should be adjustable | 78 q = q.Limit(25) // TODO(hinoka): This should be adjustable |
| 75 q = q.Order("-number") | 79 q = q.Order("-number") |
| 76 buildbots := []*buildbotBuild{} | 80 buildbots := []*buildbotBuild{} |
| 77 err := ds.GetAll(q, &buildbots) | 81 err := ds.GetAll(q, &buildbots) |
| 78 if err != nil { | 82 if err != nil { |
| 79 return nil, err | 83 return nil, err |
| 80 } | 84 } |
| 81 for _, b := range buildbots { | 85 for _, b := range buildbots { |
| 82 result = append(result, getBuildSummary(b)) | 86 result = append(result, getBuildSummary(b)) |
| 83 } | 87 } |
| 84 return result, nil | 88 return result, nil |
| 85 } | 89 } |
| 86 | 90 |
| 87 // getCurrentBuild extracts a build from a map of current builds, and translates | 91 // getCurrentBuild extracts a build from a map of current builds, and translates |
| 88 // it into the milo version of the build. | 92 // it into the milo version of the build. |
| 89 func getCurrentBuild(c context.Context, bMap buildMap, builder string, buildNum
int) *resp.BuildSummary { | 93 func getCurrentBuild(c context.Context, bMap buildMap, builder string, buildNum
int) *resp.BuildSummary { |
| 90 b, ok := bMap[builderRef{builder, buildNum}] | 94 b, ok := bMap[builderRef{builder, buildNum}] |
| 91 if !ok { | 95 if !ok { |
| 92 » » log.Warningf(c, "Could not find %s/%d in builder map:\n %s", bui
lder, buildNum, bMap) | 96 » » logging.Warningf(c, "Could not find %s/%d in builder map:\n %s",
builder, buildNum, bMap) |
| 93 return nil | 97 return nil |
| 94 } | 98 } |
| 95 return getBuildSummary(b) | 99 return getBuildSummary(b) |
| 96 } | 100 } |
| 97 | 101 |
| 98 // getCurrentBuilds extracts the list of all the current builds from a master js
on | 102 // getCurrentBuilds extracts the list of all the current builds from a master js
on |
| 99 // from the slaves' runningBuilds portion. | 103 // from the slaves' runningBuilds portion. |
| 100 func getCurrentBuilds(c context.Context, master *buildbotMaster, builderName str
ing) []*resp.BuildSummary { | 104 func getCurrentBuilds(c context.Context, master *buildbotMaster, builderName str
ing) []*resp.BuildSummary { |
| 101 b := master.Builders[builderName] | 105 b := master.Builders[builderName] |
| 102 results := []*resp.BuildSummary{} | 106 results := []*resp.BuildSummary{} |
| 103 bMap := createRunningBuildMap(master) | 107 bMap := createRunningBuildMap(master) |
| 104 for _, bn := range b.Currentbuilds { | 108 for _, bn := range b.Currentbuilds { |
| 105 cb := getCurrentBuild(c, bMap, builderName, bn) | 109 cb := getCurrentBuild(c, bMap, builderName, bn) |
| 106 if cb != nil { | 110 if cb != nil { |
| 107 results = append(results, cb) | 111 results = append(results, cb) |
| 108 } | 112 } |
| 109 } | 113 } |
| 110 return results | 114 return results |
| 111 } | 115 } |
| 112 | 116 |
| 117 var errMasterNotFound = miloerror.Error{ |
| 118 Message: "Master not found", |
| 119 Code: http.StatusNotFound, |
| 120 } |
| 121 |
| 113 // builderImpl is the implementation for getting a milo builder page from buildb
ot. | 122 // builderImpl is the implementation for getting a milo builder page from buildb
ot. |
| 114 // This gets: | 123 // This gets: |
| 115 // * Current Builds from querying the master json from the datastore. | 124 // * Current Builds from querying the master json from the datastore. |
| 116 // * Recent Builds from a cron job that backfills the recent builds. | 125 // * Recent Builds from a cron job that backfills the recent builds. |
| 117 func builderImpl(c context.Context, masterName, builderName string) (*resp.Build
er, error) { | 126 func builderImpl(c context.Context, masterName, builderName string) (*resp.Build
er, error) { |
| 118 result := &resp.Builder{} | 127 result := &resp.Builder{} |
| 119 master, internal, t, err := getMasterJSON(c, masterName) | 128 master, internal, t, err := getMasterJSON(c, masterName) |
| 120 if internal { | 129 if internal { |
| 121 » » // TODO(hinoka): Implement ACL support and remove this. | 130 » » allowed, err := settings.IsAllowedInternal(c) |
| 122 » » return nil, fmt.Errorf("Internal masters are not yet supported."
) | 131 » » if err != nil { |
| 132 » » » return nil, err |
| 133 » » } |
| 134 » » if !allowed { |
| 135 » » » return nil, errMasterNotFound |
| 136 » » } |
| 123 } | 137 } |
| 124 if err != nil { | 138 if err != nil { |
| 125 return nil, fmt.Errorf("Cannot find master %s\n%s", masterName,
err.Error()) | 139 return nil, fmt.Errorf("Cannot find master %s\n%s", masterName,
err.Error()) |
| 126 } | 140 } |
| 127 if clock.Now(c).Sub(t) > 2*time.Minute { | 141 if clock.Now(c).Sub(t) > 2*time.Minute { |
| 128 warning := fmt.Sprintf( | 142 warning := fmt.Sprintf( |
| 129 "WARNING: Master data is stale (last updated %s)", t) | 143 "WARNING: Master data is stale (last updated %s)", t) |
| 130 » » log.Warningf(c, warning) | 144 » » logging.Warningf(c, warning) |
| 131 result.Warning = warning | 145 result.Warning = warning |
| 132 } | 146 } |
| 133 | 147 |
| 134 s, _ := json.Marshal(master) | 148 s, _ := json.Marshal(master) |
| 135 » log.Debugf(c, "Master: %s", s) | 149 » logging.Debugf(c, "Master: %s", s) |
| 136 | 150 |
| 137 _, ok := master.Builders[builderName] | 151 _, ok := master.Builders[builderName] |
| 138 if !ok { | 152 if !ok { |
| 139 // This long block is just to return a good error message when a
n invalid | 153 // This long block is just to return a good error message when a
n invalid |
| 140 // buildbot builder is specified. | 154 // buildbot builder is specified. |
| 141 keys := make([]string, 0, len(master.Builders)) | 155 keys := make([]string, 0, len(master.Builders)) |
| 142 for k := range master.Builders { | 156 for k := range master.Builders { |
| 143 keys = append(keys, k) | 157 keys = append(keys, k) |
| 144 } | 158 } |
| 145 sort.Strings(keys) | 159 sort.Strings(keys) |
| (...skipping 12 matching lines...) Expand all Loading... |
| 158 result.CurrentBuilds = currentBuilds | 172 result.CurrentBuilds = currentBuilds |
| 159 for _, fb := range recentBuilds { | 173 for _, fb := range recentBuilds { |
| 160 // Yes recent builds is synonymous with finished builds. | 174 // Yes recent builds is synonymous with finished builds. |
| 161 // TODO(hinoka): Implement limits. | 175 // TODO(hinoka): Implement limits. |
| 162 if fb != nil { | 176 if fb != nil { |
| 163 result.FinishedBuilds = append(result.FinishedBuilds, fb
) | 177 result.FinishedBuilds = append(result.FinishedBuilds, fb
) |
| 164 } | 178 } |
| 165 } | 179 } |
| 166 return result, nil | 180 return result, nil |
| 167 } | 181 } |
| OLD | NEW |