OLD | NEW |
| (Empty) |
1 // Copyright 2016 The LUCI Authors. All rights reserved. | |
2 // Use of this source code is governed under the Apache License, Version 2.0 | |
3 // that can be found in the LICENSE file. | |
4 | |
5 package buildbot | |
6 | |
7 import ( | |
8 "bytes" | |
9 "compress/gzip" | |
10 "encoding/json" | |
11 "fmt" | |
12 "sort" | |
13 "time" | |
14 | |
15 ds "github.com/luci/gae/service/datastore" | |
16 "github.com/luci/luci-go/common/logging" | |
17 "github.com/luci/luci-go/milo/api/resp" | |
18 "github.com/luci/luci-go/milo/appengine/common" | |
19 "github.com/luci/luci-go/server/auth" | |
20 "github.com/luci/luci-go/server/auth/identity" | |
21 | |
22 "golang.org/x/net/context" | |
23 ) | |
24 | |
25 func decodeMasterEntry( | |
26 c context.Context, entry *buildbotMasterEntry, master *buildbotMaster) e
rror { | |
27 | |
28 reader, err := gzip.NewReader(bytes.NewReader(entry.Data)) | |
29 if err != nil { | |
30 return err | |
31 } | |
32 defer reader.Close() | |
33 if err = json.NewDecoder(reader).Decode(master); err != nil { | |
34 return err | |
35 } | |
36 return nil | |
37 } | |
38 | |
39 // User not logged in, master found, master public: nil | |
40 // User not logged in, master not found: 401 | |
41 // User not logged in, master internal: 401 | |
42 // User logged in, master found, master internal: nil | |
43 // User logged in, master not found: 404 | |
44 // User logged in, master found, master internal: 404 | |
45 // Other error: 500 | |
46 func checkAccess(c context.Context, err error, internal bool) error { | |
47 cu := auth.CurrentUser(c) | |
48 switch { | |
49 case err == ds.ErrNoSuchEntity: | |
50 if cu.Identity == identity.AnonymousIdentity { | |
51 return errNotAuth | |
52 } | |
53 return errMasterNotFound | |
54 case err != nil: | |
55 return err | |
56 } | |
57 | |
58 // Do the ACL check if the entry is internal. | |
59 if internal { | |
60 allowed, err := common.IsAllowedInternal(c) | |
61 if err != nil { | |
62 return err | |
63 } | |
64 if !allowed { | |
65 if cu.Identity == identity.AnonymousIdentity { | |
66 return errNotAuth | |
67 } | |
68 return errMasterNotFound | |
69 } | |
70 } | |
71 | |
72 return nil | |
73 } | |
74 | |
75 // getMasterEntry feches the named master and does an ACL check on the | |
76 // current user. | |
77 // It returns: | |
78 func getMasterEntry(c context.Context, name string) (*buildbotMasterEntry, error
) { | |
79 entry := buildbotMasterEntry{Name: name} | |
80 err := ds.Get(c, &entry) | |
81 err = checkAccess(c, err, entry.Internal) | |
82 return &entry, err | |
83 } | |
84 | |
85 // getMasterJSON fetches the latest known buildbot master data and returns | |
86 // the buildbotMaster struct (if found), whether or not it is internal, | |
87 // the last modified time, and an error if not found. | |
88 func getMasterJSON(c context.Context, name string) ( | |
89 master *buildbotMaster, internal bool, t time.Time, err error) { | |
90 master = &buildbotMaster{} | |
91 entry, err := getMasterEntry(c, name) | |
92 if err != nil { | |
93 return | |
94 } | |
95 t = entry.Modified | |
96 internal = entry.Internal | |
97 err = decodeMasterEntry(c, entry, master) | |
98 return | |
99 } | |
100 | |
101 // GetAllBuilders returns a resp.Module object containing all known masters | |
102 // and builders. | |
103 func GetAllBuilders(c context.Context) (*resp.CIService, error) { | |
104 result := &resp.CIService{Name: "Buildbot"} | |
105 // Fetch all Master entries from datastore | |
106 q := ds.NewQuery("buildbotMasterEntry") | |
107 // TODO(hinoka): Maybe don't look past like a month or so? | |
108 entries := []*buildbotMasterEntry{} | |
109 err := (&ds.Batcher{}).GetAll(c, q, &entries) | |
110 if err != nil { | |
111 return nil, err | |
112 } | |
113 | |
114 // Add each builder from each master entry into the result. | |
115 // TODO(hinoka): FanInOut this? | |
116 for _, entry := range entries { | |
117 if entry.Internal { | |
118 // Bypass the master if it's an internal master and the
user is not | |
119 // part of the buildbot-private project. | |
120 allowed, err := common.IsAllowedInternal(c) | |
121 if err != nil { | |
122 logging.WithError(err).Errorf(c, "Could not proc
ess master %s", entry.Name) | |
123 return nil, err | |
124 } | |
125 if !allowed { | |
126 continue | |
127 } | |
128 } | |
129 master := &buildbotMaster{} | |
130 err = decodeMasterEntry(c, entry, master) | |
131 if err != nil { | |
132 logging.WithError(err).Errorf(c, "Could not decode %s",
entry.Name) | |
133 continue | |
134 } | |
135 ml := resp.BuilderGroup{Name: entry.Name} | |
136 // Sort the builder listing. | |
137 sb := make([]string, 0, len(master.Builders)) | |
138 for bn := range master.Builders { | |
139 sb = append(sb, bn) | |
140 } | |
141 sort.Strings(sb) | |
142 for _, bn := range sb { | |
143 // Go templates escapes this for us, and also | |
144 // slashes are not allowed in builder names. | |
145 ml.Builders = append(ml.Builders, *resp.NewLink( | |
146 bn, fmt.Sprintf("/buildbot/%s/%s", entry.Name, b
n))) | |
147 } | |
148 result.BuilderGroups = append(result.BuilderGroups, ml) | |
149 } | |
150 return result, nil | |
151 } | |
OLD | NEW |