| 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 "bytes" | 8 "bytes" |
| 9 "compress/gzip" | 9 "compress/gzip" |
| 10 "encoding/json" | 10 "encoding/json" |
| 11 | 11 |
| 12 "golang.org/x/net/context" | 12 "golang.org/x/net/context" |
| 13 "google.golang.org/grpc" | 13 "google.golang.org/grpc" |
| 14 "google.golang.org/grpc/codes" | 14 "google.golang.org/grpc/codes" |
| 15 | 15 |
| 16 "github.com/golang/protobuf/ptypes/timestamp" | 16 "github.com/golang/protobuf/ptypes/timestamp" |
| 17 » ds "github.com/luci/gae/service/datastore" | 17 » "github.com/luci/gae/service/datastore" |
| 18 "github.com/luci/luci-go/common/iotools" | 18 "github.com/luci/luci-go/common/iotools" |
| 19 "github.com/luci/luci-go/common/logging" | 19 "github.com/luci/luci-go/common/logging" |
| 20 milo "github.com/luci/luci-go/milo/api/proto" | 20 milo "github.com/luci/luci-go/milo/api/proto" |
| 21 "github.com/luci/luci-go/server/auth" | 21 "github.com/luci/luci-go/server/auth" |
| 22 ) | 22 ) |
| 23 | 23 |
| 24 // Service is a service implementation that displays BuildBot builds. | 24 // Service is a service implementation that displays BuildBot builds. |
| 25 type Service struct{} | 25 type Service struct{} |
| 26 | 26 |
| 27 var errNotFoundGRPC = grpc.Errorf(codes.NotFound, "Master Not Found") | 27 var errNotFoundGRPC = grpc.Errorf(codes.NotFound, "Master Not Found") |
| (...skipping 43 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 71 if req.Builder == "" { | 71 if req.Builder == "" { |
| 72 return nil, grpc.Errorf(codes.InvalidArgument, "No builder speci
fied") | 72 return nil, grpc.Errorf(codes.InvalidArgument, "No builder speci
fied") |
| 73 } | 73 } |
| 74 | 74 |
| 75 limit := req.Limit | 75 limit := req.Limit |
| 76 if limit == 0 { | 76 if limit == 0 { |
| 77 limit = 20 | 77 limit = 20 |
| 78 } | 78 } |
| 79 | 79 |
| 80 cu := auth.CurrentUser(c) | 80 cu := auth.CurrentUser(c) |
| 81 » logging.Debugf(c, "%s is requesting %s/%s (limit %d)", | 81 » logging.Debugf(c, "%s is requesting %s/%s (limit %d, cursor %s)", |
| 82 » » cu.Identity, req.Master, req.Builder, limit) | 82 » » cu.Identity, req.Master, req.Builder, limit, req.Cursor) |
| 83 | 83 |
| 84 // Perform an ACL check by fetching the master. | 84 // Perform an ACL check by fetching the master. |
| 85 _, err := getMasterEntry(c, req.Master) | 85 _, err := getMasterEntry(c, req.Master) |
| 86 switch { | 86 switch { |
| 87 case err == errMasterNotFound: | 87 case err == errMasterNotFound: |
| 88 return nil, grpc.Errorf(codes.NotFound, "Master not found") | 88 return nil, grpc.Errorf(codes.NotFound, "Master not found") |
| 89 case err == errNotAuth: | 89 case err == errNotAuth: |
| 90 return nil, grpc.Errorf(codes.Unauthenticated, "Unauthenticated
request") | 90 return nil, grpc.Errorf(codes.Unauthenticated, "Unauthenticated
request") |
| 91 case err != nil: | 91 case err != nil: |
| 92 return nil, err | 92 return nil, err |
| 93 } | 93 } |
| 94 | 94 |
| 95 » q := ds.NewQuery("buildbotBuild") | 95 » q := datastore.NewQuery("buildbotBuild") |
| 96 q = q.Eq("master", req.Master). | 96 q = q.Eq("master", req.Master). |
| 97 Eq("builder", req.Builder). | 97 Eq("builder", req.Builder). |
| 98 Limit(limit). | 98 Limit(limit). |
| 99 Order("-number") | 99 Order("-number") |
| 100 if req.IncludeCurrent == false { | 100 if req.IncludeCurrent == false { |
| 101 q = q.Eq("finished", true) | 101 q = q.Eq("finished", true) |
| 102 } | 102 } |
| 103 » builds := []*buildbotBuild{} | 103 » // Insert the cursor or offset. |
| 104 » err = getBuildQueryBatcher(c).GetAll(c, q, &builds) | 104 » if req.Cursor != "" { |
| 105 » » cursor, err := datastore.DecodeCursor(c, req.Cursor) |
| 106 » » if err != nil { |
| 107 » » » return nil, grpc.Errorf(codes.InvalidArgument, "Invalid
cursor: %s", err.Error()) |
| 108 » » } |
| 109 » » q = q.Start(cursor) |
| 110 » } |
| 111 » builds, nextCursor, err := runBuildsQuery(c, q, int32(req.Limit)) |
| 105 if err != nil { | 112 if err != nil { |
| 106 return nil, err | 113 return nil, err |
| 107 } | 114 } |
| 108 | 115 |
| 109 results := make([]*milo.BuildbotBuildJSON, len(builds)) | 116 results := make([]*milo.BuildbotBuildJSON, len(builds)) |
| 110 for i, b := range builds { | 117 for i, b := range builds { |
| 111 updatePostProcessBuild(b) | 118 updatePostProcessBuild(b) |
| 112 | 119 |
| 113 // In theory we could do this in parallel, but it doesn't actual
ly go faster | 120 // In theory we could do this in parallel, but it doesn't actual
ly go faster |
| 114 // since AppEngine is single-cored. | 121 // since AppEngine is single-cored. |
| 115 bs, err := json.Marshal(b) | 122 bs, err := json.Marshal(b) |
| 116 if err != nil { | 123 if err != nil { |
| 117 return nil, err | 124 return nil, err |
| 118 } | 125 } |
| 119 results[i] = &milo.BuildbotBuildJSON{Data: bs} | 126 results[i] = &milo.BuildbotBuildJSON{Data: bs} |
| 120 } | 127 } |
| 121 » return &milo.BuildbotBuildsJSON{ | 128 » buildsJSON := &milo.BuildbotBuildsJSON{ |
| 122 Builds: results, | 129 Builds: results, |
| 123 » }, nil | 130 » } |
| 131 » if nextCursor != nil { |
| 132 » » buildsJSON.Cursor = (*nextCursor).String() |
| 133 » } |
| 134 » return buildsJSON, nil |
| 124 } | 135 } |
| 125 | 136 |
| 126 // GetCompressedMasterJSON assembles a CompressedMasterJSON object from the | 137 // GetCompressedMasterJSON assembles a CompressedMasterJSON object from the |
| 127 // provided MasterRequest. | 138 // provided MasterRequest. |
| 128 func (s *Service) GetCompressedMasterJSON(c context.Context, req *milo.MasterReq
uest) ( | 139 func (s *Service) GetCompressedMasterJSON(c context.Context, req *milo.MasterReq
uest) ( |
| 129 *milo.CompressedMasterJSON, error) { | 140 *milo.CompressedMasterJSON, error) { |
| 130 | 141 |
| 131 if req.Name == "" { | 142 if req.Name == "" { |
| 132 return nil, grpc.Errorf(codes.InvalidArgument, "No master specif
ied") | 143 return nil, grpc.Errorf(codes.InvalidArgument, "No master specif
ied") |
| 133 } | 144 } |
| (...skipping 23 matching lines...) Expand all Loading... |
| 157 slave.Runningbuilds = make([]*buildbotBuild, 0, numBuilds) | 168 slave.Runningbuilds = make([]*buildbotBuild, 0, numBuilds) |
| 158 for builderName, builds := range slave.RunningbuildsMap { | 169 for builderName, builds := range slave.RunningbuildsMap { |
| 159 for _, buildNum := range builds { | 170 for _, buildNum := range builds { |
| 160 slave.Runningbuilds = append(slave.Runningbuilds
, &buildbotBuild{ | 171 slave.Runningbuilds = append(slave.Runningbuilds
, &buildbotBuild{ |
| 161 Master: req.Name, | 172 Master: req.Name, |
| 162 Buildername: builderName, | 173 Buildername: builderName, |
| 163 Number: buildNum, | 174 Number: buildNum, |
| 164 }) | 175 }) |
| 165 } | 176 } |
| 166 } | 177 } |
| 167 » » if err := ds.Get(c, slave.Runningbuilds); err != nil { | 178 » » if err := datastore.Get(c, slave.Runningbuilds); err != nil { |
| 168 logging.WithError(err).Errorf(c, | 179 logging.WithError(err).Errorf(c, |
| 169 "Encountered error while trying to fetch running
builds for %s: %v", | 180 "Encountered error while trying to fetch running
builds for %s: %v", |
| 170 master.Name, slave.Runningbuilds) | 181 master.Name, slave.Runningbuilds) |
| 171 return nil, err | 182 return nil, err |
| 172 } | 183 } |
| 173 | 184 |
| 174 for _, b := range slave.Runningbuilds { | 185 for _, b := range slave.Runningbuilds { |
| 175 updatePostProcessBuild(b) | 186 updatePostProcessBuild(b) |
| 176 } | 187 } |
| 177 } | 188 } |
| 178 | 189 |
| 179 // Also inject cached builds information. | 190 // Also inject cached builds information. |
| 180 for builderName, builder := range master.Builders { | 191 for builderName, builder := range master.Builders { |
| 181 // Get the most recent 50 buildNums on the builder to simulate w
hat the | 192 // Get the most recent 50 buildNums on the builder to simulate w
hat the |
| 182 // cachedBuilds field looks like from the real buildbot master j
son. | 193 // cachedBuilds field looks like from the real buildbot master j
son. |
| 183 » » q := ds.NewQuery("buildbotBuild"). | 194 » » q := datastore.NewQuery("buildbotBuild"). |
| 184 Eq("finished", true). | 195 Eq("finished", true). |
| 185 Eq("master", req.Name). | 196 Eq("master", req.Name). |
| 186 Eq("builder", builderName). | 197 Eq("builder", builderName). |
| 187 Limit(50). | 198 Limit(50). |
| 188 Order("-number"). | 199 Order("-number"). |
| 189 KeysOnly(true) | 200 KeysOnly(true) |
| 190 var builds []*buildbotBuild | 201 var builds []*buildbotBuild |
| 191 err := getBuildQueryBatcher(c).GetAll(c, q, &builds) | 202 err := getBuildQueryBatcher(c).GetAll(c, q, &builds) |
| 192 if err != nil { | 203 if err != nil { |
| 193 return nil, err | 204 return nil, err |
| (...skipping 19 matching lines...) Expand all Loading... |
| 213 | 224 |
| 214 return &milo.CompressedMasterJSON{ | 225 return &milo.CompressedMasterJSON{ |
| 215 Internal: entry.Internal, | 226 Internal: entry.Internal, |
| 216 Modified: ×tamp.Timestamp{ | 227 Modified: ×tamp.Timestamp{ |
| 217 Seconds: entry.Modified.Unix(), | 228 Seconds: entry.Modified.Unix(), |
| 218 Nanos: int32(entry.Modified.Nanosecond()), | 229 Nanos: int32(entry.Modified.Nanosecond()), |
| 219 }, | 230 }, |
| 220 Data: gzbs.Bytes(), | 231 Data: gzbs.Bytes(), |
| 221 }, nil | 232 }, nil |
| 222 } | 233 } |
| OLD | NEW |