Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(903)

Unified Diff: server/config/service.go

Issue 2580713002: Implement a server-side config service interface. (Closed)
Patch Set: Update MultiResolver interface, add test for MultiError. Created 4 years ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
Index: server/config/service.go
diff --git a/server/config/service.go b/server/config/service.go
new file mode 100644
index 0000000000000000000000000000000000000000..6900dbcb54cad46752e2f72af4a630cd1fb0d3fa
--- /dev/null
+++ b/server/config/service.go
@@ -0,0 +1,164 @@
+// 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 config
+
+import (
+ "net/http"
+ "net/url"
+ "sync"
+
+ "github.com/luci/luci-go/common/config"
+ "github.com/luci/luci-go/common/config/impl/remote"
+ "github.com/luci/luci-go/common/errors"
+ "github.com/luci/luci-go/server/auth"
+
+ "golang.org/x/net/context"
+)
+
+// ClientProvider returns a config.Interface for the supplied parameters.
+type ClientProvider interface {
+ GetConfigClient(context.Context, Authority) config.Interface
+}
+
+// ClientBackend returns a Backend implementation that falls through to the
+// supplied client config.Interface supplied by the Provider.
+//
+// url is the base URL to the configuration service, e.g.,
+// https://example.appspot.com.
+type ClientBackend struct {
+ Provider ClientProvider
+}
+
+var _ Backend = (*ClientBackend)(nil)
+
+// ServiceURL implements Backend.
+func (cb *ClientBackend) ServiceURL(c context.Context) url.URL {
+ return cb.getIface(c, AsAnonymous).ServiceURL(c)
+}
+
+// ConfigSetURL implements Backend.
+func (cb *ClientBackend) ConfigSetURL(c context.Context, a Authority, configSet string) (url.URL, error) {
+ u, err := cb.getIface(c, a).GetConfigSetLocation(c, configSet)
+ if err != nil || u == nil {
+ return url.URL{}, err
+ }
+ return *u, nil
+}
+
+// Get implements Backend.
+func (cb *ClientBackend) Get(c context.Context, configSet, path string, p Params) (*Item, error) {
+ svc := cb.getIface(c, p.Authority)
+
+ cfg, err := svc.GetConfig(c, configSet, path, !p.Content)
+ if err != nil {
+ return nil, translateConfigErr(err)
+ }
+
+ return makeItem(cfg), nil
+}
+
+// GetAll implements Backend.
+func (cb *ClientBackend) GetAll(c context.Context, t GetAllType, path string, p Params) ([]*Item, error) {
+ svc := cb.getIface(c, p.Authority)
+
+ var fn func(context.Context, string, bool) ([]config.Config, error)
+ switch t {
+ case Project:
+ fn = svc.GetProjectConfigs
+ case Ref:
+ fn = svc.GetRefConfigs
+ default:
+ return nil, errors.Reason("unknown GetAllType: %(type)q").D("type", t).Err()
+ }
+
+ cfgs, err := fn(c, path, !p.Content)
+ if err != nil || len(cfgs) == 0 {
+ return nil, translateConfigErr(err)
+ }
+
+ items := make([]*Item, len(cfgs))
+ for i := range cfgs {
+ items[i] = makeItem(&cfgs[i])
+ }
+ return items, nil
+}
+
+func (cb *ClientBackend) getIface(c context.Context, a Authority) config.Interface {
+ return cb.Provider.GetConfigClient(c, a)
+}
+
+// RemoteClientProvider is a ClientProvider implementation that binds to
+// a remote configuration service.
+type RemoteClientProvider struct {
+ // BaseURL is the base URL to the configuration service, e.g.,
+ // https://example.appspot.com.
+ BaseURL string
+
+ cacheLock sync.RWMutex
+ cache map[Authority]config.Interface
+
+ // testUserDelegationToken, if not nil, is the delegation token to use for
+ // AsUser calls. This is done to mock delegation token generation.
+ testUserDelegationToken string
+}
+
+var _ ClientProvider = (*RemoteClientProvider)(nil)
+
+// GetConfigClient implements ClientProvider.
+func (p *RemoteClientProvider) GetConfigClient(c context.Context, a Authority) config.Interface {
+ p.cacheLock.RLock()
+ impl, ok := p.cache[a]
+ p.cacheLock.RUnlock()
+ if ok {
+ return impl
+ }
+
+ p.cacheLock.Lock()
+ defer p.cacheLock.Unlock()
+
+ if impl, ok := p.cache[a]; ok {
+ return impl
+ }
+
+ // Create our remote implementation.
+ impl = remote.New(p.BaseURL+"/_ah/api/config/v1/", func(c context.Context) (*http.Client, error) {
+ var opts []auth.RPCOption
+ if a == AsUser && p.testUserDelegationToken != "" {
+ opts = append(opts, auth.WithDelegationToken(p.testUserDelegationToken))
+ }
+ t, err := auth.GetRPCTransport(c, a.rpcAuthorityKind(), opts...)
+ if err != nil {
+ return nil, err
+ }
+ return &http.Client{Transport: t}, nil
+ })
+ if p.cache == nil {
+ p.cache = make(map[Authority]config.Interface, 3)
+ }
+ p.cache[a] = impl
+
+ return impl
+}
+
+func translateConfigErr(err error) error {
+ switch err {
+ case config.ErrNoConfig:
+ return ErrNoConfig
+ default:
+ return err
+ }
+}
+
+func makeItem(cfg *config.Config) *Item {
+ return &Item{
+ Meta: Meta{
+ ConfigSet: cfg.ConfigSet,
+ Path: cfg.Path,
+ ContentHash: cfg.ContentHash,
+ Revision: cfg.Revision,
+ },
+ Content: cfg.Content,
+ }
+}

Powered by Google App Engine
This is Rietveld 408576698