OLD | NEW |
1 // Copyright 2015 The Chromium Authors. All rights reserved. | 1 // Copyright 2015 The Chromium Authors. All rights reserved. |
2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
4 | 4 |
5 package coordinatorTest | 5 package coordinatorTest |
6 | 6 |
7 import ( | 7 import ( |
8 "fmt" | 8 "fmt" |
| 9 "strings" |
| 10 "time" |
9 | 11 |
10 "github.com/golang/protobuf/proto" | 12 "github.com/golang/protobuf/proto" |
11 ds "github.com/luci/gae/service/datastore" | 13 ds "github.com/luci/gae/service/datastore" |
| 14 "github.com/luci/gae/service/info" |
12 "github.com/luci/luci-go/appengine/logdog/coordinator" | 15 "github.com/luci/luci-go/appengine/logdog/coordinator" |
13 "github.com/luci/luci-go/appengine/logdog/coordinator/config" | 16 "github.com/luci/luci-go/appengine/logdog/coordinator/config" |
14 "github.com/luci/luci-go/appengine/tumble" | 17 "github.com/luci/luci-go/appengine/tumble" |
15 "github.com/luci/luci-go/common/clock" | 18 "github.com/luci/luci-go/common/clock" |
16 "github.com/luci/luci-go/common/clock/testclock" | 19 "github.com/luci/luci-go/common/clock/testclock" |
17 luciConfig "github.com/luci/luci-go/common/config" | 20 luciConfig "github.com/luci/luci-go/common/config" |
18 "github.com/luci/luci-go/common/config/impl/memory" | 21 "github.com/luci/luci-go/common/config/impl/memory" |
19 "github.com/luci/luci-go/common/gcloud/gs" | 22 "github.com/luci/luci-go/common/gcloud/gs" |
20 "github.com/luci/luci-go/common/logging" | 23 "github.com/luci/luci-go/common/logging" |
21 "github.com/luci/luci-go/common/logging/gologger" | 24 "github.com/luci/luci-go/common/logging/gologger" |
22 » configProto "github.com/luci/luci-go/common/proto/config" | 25 » "github.com/luci/luci-go/common/proto/google" |
23 "github.com/luci/luci-go/common/proto/logdog/svcconfig" | 26 "github.com/luci/luci-go/common/proto/logdog/svcconfig" |
24 "github.com/luci/luci-go/server/auth" | 27 "github.com/luci/luci-go/server/auth" |
25 "github.com/luci/luci-go/server/auth/authtest" | 28 "github.com/luci/luci-go/server/auth/authtest" |
26 "github.com/luci/luci-go/server/logdog/storage" | 29 "github.com/luci/luci-go/server/logdog/storage" |
27 memoryStorage "github.com/luci/luci-go/server/logdog/storage/memory" | 30 memoryStorage "github.com/luci/luci-go/server/logdog/storage/memory" |
28 "github.com/luci/luci-go/server/settings" | 31 "github.com/luci/luci-go/server/settings" |
29 "golang.org/x/net/context" | 32 "golang.org/x/net/context" |
30 ) | 33 ) |
31 | 34 |
32 // Environment contains all of the testing facilities that are installed into | 35 // Environment contains all of the testing facilities that are installed into |
(...skipping 27 matching lines...) Expand all Loading... |
60 // default) into Services. | 63 // default) into Services. |
61 ArchivalPublisher ArchivalPublisher | 64 ArchivalPublisher ArchivalPublisher |
62 } | 65 } |
63 | 66 |
64 // JoinGroup adds the named group the to the list of groups for the current | 67 // JoinGroup adds the named group the to the list of groups for the current |
65 // identity. | 68 // identity. |
66 func (e *Environment) JoinGroup(g string) { | 69 func (e *Environment) JoinGroup(g string) { |
67 e.AuthState.IdentityGroups = append(e.AuthState.IdentityGroups, g) | 70 e.AuthState.IdentityGroups = append(e.AuthState.IdentityGroups, g) |
68 } | 71 } |
69 | 72 |
| 73 // LeaveAllGroups clears all auth groups that the user is currently a member of. |
| 74 func (e *Environment) LeaveAllGroups() { |
| 75 e.AuthState.IdentityGroups = nil |
| 76 e.JoinGroup("all") |
| 77 } |
| 78 |
70 // ClearCoordinatorConfig removes the Coordinator configuration entry, | 79 // ClearCoordinatorConfig removes the Coordinator configuration entry, |
71 // simulating a missing config. | 80 // simulating a missing config. |
72 func (e *Environment) ClearCoordinatorConfig(c context.Context) { | 81 func (e *Environment) ClearCoordinatorConfig(c context.Context) { |
73 configSet, _ := config.ServiceConfigPath(c) | 82 configSet, _ := config.ServiceConfigPath(c) |
74 delete(e.Config, configSet) | 83 delete(e.Config, configSet) |
75 } | 84 } |
76 | 85 |
77 // ModServiceConfig loads the current service configuration, invokes the | 86 // ModServiceConfig loads the current service configuration, invokes the |
78 // callback with its contents, and writes the result back to config. | 87 // callback with its contents, and writes the result back to config. |
79 func (e *Environment) ModServiceConfig(c context.Context, fn func(*svcconfig.Coo
rdinator)) { | 88 func (e *Environment) ModServiceConfig(c context.Context, fn func(*svcconfig.Con
fig)) { |
80 configSet, configPath := config.ServiceConfigPath(c) | 89 configSet, configPath := config.ServiceConfigPath(c) |
81 | 90 |
82 var cfg svcconfig.Config | 91 var cfg svcconfig.Config |
83 e.modTextProtobuf(configSet, configPath, &cfg, func() { | 92 e.modTextProtobuf(configSet, configPath, &cfg, func() { |
84 » » if cfg.Coordinator == nil { | 93 » » fn(&cfg) |
85 » » » cfg.Coordinator = &svcconfig.Coordinator{} | |
86 » » } | |
87 » » fn(cfg.Coordinator) | |
88 }) | 94 }) |
89 } | 95 } |
90 | 96 |
| 97 // ModProjectConfig loads the current configuration for the named project, |
| 98 // invokes the callback with its contents, and writes the result back to config. |
| 99 func (e *Environment) ModProjectConfig(c context.Context, proj luciConfig.Projec
tName, fn func(*svcconfig.ProjectConfig)) { |
| 100 configSet, configPath := config.ProjectConfigPath(c, proj) |
| 101 |
| 102 var pcfg svcconfig.ProjectConfig |
| 103 e.modTextProtobuf(configSet, configPath, &pcfg, func() { |
| 104 fn(&pcfg) |
| 105 }) |
| 106 } |
| 107 |
91 // DrainTumbleAll drains all Tumble instances across all namespaces. | 108 // DrainTumbleAll drains all Tumble instances across all namespaces. |
92 func (e *Environment) DrainTumbleAll(c context.Context) { | 109 func (e *Environment) DrainTumbleAll(c context.Context) { |
93 projects, err := luciConfig.Get(c).GetProjects() | 110 projects, err := luciConfig.Get(c).GetProjects() |
94 if err != nil { | 111 if err != nil { |
95 panic(err) | 112 panic(err) |
96 } | 113 } |
97 | 114 |
98 for _, proj := range projects { | 115 for _, proj := range projects { |
99 WithProjectNamespace(c, luciConfig.ProjectName(proj.ID), func(c
context.Context) { | 116 WithProjectNamespace(c, luciConfig.ProjectName(proj.ID), func(c
context.Context) { |
100 e.Tumble.Drain(c) | 117 e.Tumble.Drain(c) |
(...skipping 11 matching lines...) Expand all Loading... |
112 } | 129 } |
113 | 130 |
114 case luciConfig.ErrNoConfig: | 131 case luciConfig.ErrNoConfig: |
115 break | 132 break |
116 | 133 |
117 default: | 134 default: |
118 panic(err) | 135 panic(err) |
119 } | 136 } |
120 | 137 |
121 fn() | 138 fn() |
| 139 e.addConfigEntry(configSet, path, proto.MarshalTextString(msg)) |
| 140 } |
122 | 141 |
| 142 func (e *Environment) addConfigEntry(configSet, path, content string) { |
123 cset := e.Config[configSet] | 143 cset := e.Config[configSet] |
124 if cset == nil { | 144 if cset == nil { |
125 cset = make(map[string]string) | 145 cset = make(map[string]string) |
126 e.Config[configSet] = cset | 146 e.Config[configSet] = cset |
127 } | 147 } |
128 » cset[path] = proto.MarshalTextString(msg) | 148 » cset[path] = content |
129 } | 149 } |
130 | 150 |
131 // Install creates a testing Context and installs common test facilities into | 151 // Install creates a testing Context and installs common test facilities into |
132 // it, returning the Environment to which they're bound. | 152 // it, returning the Environment to which they're bound. |
133 func Install() (context.Context, *Environment) { | 153 func Install() (context.Context, *Environment) { |
134 e := Environment{ | 154 e := Environment{ |
135 Config: make(map[string]memory.ConfigSet), | 155 Config: make(map[string]memory.ConfigSet), |
136 } | 156 } |
137 | 157 |
138 // Get our starting context. This installs, among other things, in-memor
y | 158 // Get our starting context. This installs, among other things, in-memor
y |
(...skipping 37 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
176 e.Clock = clock.Get(c).(testclock.TestClock) | 196 e.Clock = clock.Get(c).(testclock.TestClock) |
177 | 197 |
178 // Install GAE config service settings. | 198 // Install GAE config service settings. |
179 c = settings.Use(c, settings.New(&settings.MemoryStorage{})) | 199 c = settings.Use(c, settings.New(&settings.MemoryStorage{})) |
180 | 200 |
181 // Setup luci-config configuration. | 201 // Setup luci-config configuration. |
182 c = memory.Use(c, e.Config) | 202 c = memory.Use(c, e.Config) |
183 e.ConfigIface = luciConfig.Get(c) | 203 e.ConfigIface = luciConfig.Get(c) |
184 | 204 |
185 // luci-config: Projects. | 205 // luci-config: Projects. |
186 » addProjectConfig := func(proj luciConfig.ProjectName, localName string,
access ...string) { | 206 » projectName := info.Get(c).AppID() |
187 » » configSet, configPath := config.ProjectConfigPath(c, proj) | 207 » addProjectConfig := func(proj luciConfig.ProjectName, access ...string)
{ |
188 | 208 » » e.ModProjectConfig(c, proj, func(pcfg *svcconfig.ProjectConfig)
{ |
189 » » var cfg configProto.ProjectCfg | 209 » » » for _, a := range access { |
190 » » e.modTextProtobuf(configSet, configPath, &cfg, func() { | 210 » » » » parts := strings.SplitN(a, ":", 2) |
191 » » » cfg.Name = &localName | 211 » » » » group, field := parts[0], &pcfg.ReaderAuthGroups |
192 » » » cfg.Access = access | 212 » » » » if len(parts) == 2 { |
| 213 » » » » » switch parts[1] { |
| 214 » » » » » case "R": |
| 215 » » » » » » break |
| 216 » » » » » case "W": |
| 217 » » » » » » field = &pcfg.WriterAuthGroups |
| 218 » » » » » default: |
| 219 » » » » » » panic(a) |
| 220 » » » » » } |
| 221 » » » » } |
| 222 » » » » *field = append(*field, group) |
| 223 » » » } |
193 }) | 224 }) |
194 } | 225 } |
195 » addProjectConfig("proj-foo", "Foo Project", "group:all") | 226 » addProjectConfig("proj-foo", "all:R", "all:W") |
196 » addProjectConfig("proj-bar", "Bar Project", "group:all") | 227 » addProjectConfig("proj-bar", "all:R", "auth:W") |
197 » addProjectConfig("proj-baz", "Baz Project", "group:all") | 228 » addProjectConfig("proj-exclusive", "auth:R", "auth:W") |
198 » addProjectConfig("proj-qux", "Qux Project", "group:all") | 229 |
199 » addProjectConfig("proj-exclusive", "Exclusive Project", "group:auth") | 230 » // Add a project without a LogDog project config. |
| 231 » e.addConfigEntry("projects/proj-unconfigured", "not-logdog.cfg", "junk") |
| 232 |
| 233 » configSet, configName := config.ProjectConfigPath(c, "proj-malformed") |
| 234 » e.addConfigEntry(configSet, configName, "!!! not a text protobuf !!!") |
200 | 235 |
201 // luci-config: Coordinator Defaults | 236 // luci-config: Coordinator Defaults |
202 » e.ModServiceConfig(c, func(cfg *svcconfig.Coordinator) { | 237 » e.ModServiceConfig(c, func(cfg *svcconfig.Config) { |
203 » » *cfg = svcconfig.Coordinator{ | 238 » » cfg.Transport = &svcconfig.Transport{ |
| 239 » » » Type: &svcconfig.Transport_Pubsub{ |
| 240 » » » » Pubsub: &svcconfig.Transport_PubSub{ |
| 241 » » » » » Project: projectName, |
| 242 » » » » » Topic: "test-topic", |
| 243 » » » » }, |
| 244 » » » }, |
| 245 » » } |
| 246 » » cfg.Coordinator = &svcconfig.Coordinator{ |
204 AdminAuthGroup: "admin", | 247 AdminAuthGroup: "admin", |
205 ServiceAuthGroup: "services", | 248 ServiceAuthGroup: "services", |
| 249 PrefixExpiration: google.NewDuration(24 * time.Hour), |
206 } | 250 } |
207 }) | 251 }) |
208 | 252 |
209 // Setup Tumble. This also adds the two Tumble indexes to datastore. | 253 // Setup Tumble. This also adds the two Tumble indexes to datastore. |
210 e.Tumble.EnableDelayedMutations(c) | 254 e.Tumble.EnableDelayedMutations(c) |
211 | 255 |
212 tcfg := e.Tumble.GetConfig(c) | 256 tcfg := e.Tumble.GetConfig(c) |
213 tcfg.Namespaced = true | 257 tcfg.Namespaced = true |
214 e.Tumble.UpdateSettings(c, tcfg) | 258 e.Tumble.UpdateSettings(c, tcfg) |
215 | 259 |
216 // Install authentication state. | 260 // Install authentication state. |
217 c = auth.WithState(c, &e.AuthState) | 261 c = auth.WithState(c, &e.AuthState) |
218 | 262 |
219 // Setup authentication state. | 263 // Setup authentication state. |
220 » e.JoinGroup("all") | 264 » e.LeaveAllGroups() |
221 | 265 |
222 // Setup our default Coordinator services. | 266 // Setup our default Coordinator services. |
223 e.Services = Services{ | 267 e.Services = Services{ |
224 IS: func() (storage.Storage, error) { | 268 IS: func() (storage.Storage, error) { |
225 return &e.IntermediateStorage, nil | 269 return &e.IntermediateStorage, nil |
226 }, | 270 }, |
227 GS: func() (gs.Client, error) { | 271 GS: func() (gs.Client, error) { |
228 return &e.GSClient, nil | 272 return &e.GSClient, nil |
229 }, | 273 }, |
230 AP: func() (coordinator.ArchivalPublisher, error) { | 274 AP: func() (coordinator.ArchivalPublisher, error) { |
231 return &e.ArchivalPublisher, nil | 275 return &e.ArchivalPublisher, nil |
232 }, | 276 }, |
233 } | 277 } |
234 c = coordinator.WithServices(c, &e.Services) | 278 c = coordinator.WithServices(c, &e.Services) |
235 | 279 |
236 return c, &e | 280 return c, &e |
237 } | 281 } |
238 | 282 |
239 // WithProjectNamespace runs f in proj's namespace, bypassing authentication | 283 // WithProjectNamespace runs f in proj's namespace, bypassing authentication |
240 // checks. | 284 // checks. |
241 func WithProjectNamespace(c context.Context, proj luciConfig.ProjectName, f func
(context.Context)) { | 285 func WithProjectNamespace(c context.Context, proj luciConfig.ProjectName, f func
(context.Context)) { |
242 if err := coordinator.WithProjectNamespaceNoAuth(&c, proj); err != nil { | 286 if err := coordinator.WithProjectNamespaceNoAuth(&c, proj); err != nil { |
243 panic(err) | 287 panic(err) |
244 } | 288 } |
245 f(c) | 289 f(c) |
246 } | 290 } |
OLD | NEW |