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

Side by Side Diff: milo/appengine/common/config.go

Issue 2801463002: Milo: Use custom config caching layer (Closed)
Patch Set: Review: Remove double logging Created 3 years, 8 months 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 unified diff | Download patch
« no previous file with comments | « milo/appengine/common/acl.go ('k') | milo/appengine/common/config_test.go » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
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 common 5 package common
6 6
7 import ( 7 import (
8 "fmt" 8 "fmt"
9 "time"
9 10
10 "github.com/luci/gae/service/datastore" 11 "github.com/luci/gae/service/datastore"
11 "github.com/luci/gae/service/info" 12 "github.com/luci/gae/service/info"
13 "github.com/luci/luci-go/common/data/caching/proccache"
12 "github.com/luci/luci-go/common/logging" 14 "github.com/luci/luci-go/common/logging"
13 "github.com/luci/luci-go/luci_config/server/cfgclient" 15 "github.com/luci/luci-go/luci_config/server/cfgclient"
16 "github.com/luci/luci-go/luci_config/server/cfgclient/backend"
14 "github.com/luci/luci-go/luci_config/server/cfgclient/textproto" 17 "github.com/luci/luci-go/luci_config/server/cfgclient/textproto"
15 "github.com/luci/luci-go/milo/common/config" 18 "github.com/luci/luci-go/milo/common/config"
16 19
17 "github.com/golang/protobuf/proto" 20 "github.com/golang/protobuf/proto"
18 "golang.org/x/net/context" 21 "golang.org/x/net/context"
19 ) 22 )
20 23
21 // Project is a LUCI project. 24 // Project is a LUCI project.
22 type Project struct { 25 type Project struct {
23 » // The ID of the project, as per self defined. This is not the luci-cfg 26 » // The ID of the project, as per self defined. This is not the luci-con fig
24 // name. 27 // name.
25 ID string `gae:"$id"` 28 ID string `gae:"$id"`
26 » // The luci-cfg name of the project. 29 » // The luci-config name of the project.
27 Name string 30 Name string
28 // The Project data in protobuf binary format. 31 // The Project data in protobuf binary format.
29 Data []byte `gae:",noindex"` 32 Data []byte `gae:",noindex"`
30 } 33 }
31 34
32 // GetServiceConfig returns the service (aka global) config for the current 35 // The key for the service config entity in datastore.
33 // instance of Milo. 36 const ServiceConfigID = "service_config"
34 func GetSettings(c context.Context) (*config.Settings, error) { 37
35 » cs := cfgclient.CurrentServiceConfigSet(c) 38 // ServiceConfig is a container for the instance's service config.
36 » msg := &config.Settings{} 39 type ServiceConfig struct {
37 » // Our global config name is called settings.cfg. 40 » // ID is the datastore key. This should be static, as there should only be
38 » err := cfgclient.Get(c, cfgclient.AsService, cs, "settings.cfg", textpro to.Message(msg), nil) 41 » // one service config.
39 » switch err { 42 » ID string `gae:"$id"`
40 » case cfgclient.ErrNoConfig: 43 » // Revision is the revision of the config, taken from luci-config. This is used
41 » » // Just warn very heavily in the logs, but don't 500, instead re turn an 44 » // to determine if the entry needs to be refreshed.
42 » » // empty config. 45 » Revision string
43 » » logging.WithError(err).Errorf(c, "settings.cfg does not exist") 46 » // Data is the binary proto of the config.
44 » » msg.Buildbot = &config.Settings_Buildbot{} 47 » Data []byte `gae:",noindex"`
45 » » err = nil 48 » // Text is the text format of the config. For human consumption only.
46 » case nil: 49 » Text string `gae:",noindex"`
47 » » // continue 50 » // LastUpdated is the time this config was last updated.
48 » default: 51 » LastUpdated time.Time
49 » » return nil, err
50 » }
51 » return msg, err
52 } 52 }
53 53
54 // UpdateProjectConfigs internal project configuration based off luci-cfg. 54 // GetServiceConfig returns the service (aka global) config for the current
55 // instance of Milo from the datastore. Returns an empty config and warn heavil y
56 // if none is found.
57 // TODO(hinoka): Use process cache to cache configs.
58 func GetSettings(c context.Context) *config.Settings {
59 » settings := config.Settings{
60 » » Buildbot: &config.Settings_Buildbot{},
61 » }
62
63 » msg, err := GetCurrentServiceConfig(c)
64 » if err != nil {
65 » » // The service config does not exist, just return an empty confi g
66 » » // and complain loudly in the logs.
67 » » logging.WithError(err).Errorf(c,
68 » » » "Encountered error while loading service config, using e mpty config.")
69 » » return &settings
70 » }
71
72 » err = proto.Unmarshal(msg.Data, &settings)
73 » if err != nil {
74 » » // The service config is broken, just return an empty config
75 » » // and complain loudly in the logs.
76 » » logging.WithError(err).Errorf(c,
77 » » » "Encountered error while unmarshalling service config, u sing empty config.")
78 » » // Zero out the message just incase something got written in.
79 » » settings = config.Settings{Buildbot: &config.Settings_Buildbot{} }
80 » }
81
82 » return &settings
83 }
84
85 // GetCurrentServiceConfig gets the service config for the instance from either
86 // process cache or datastore cache.
87 func GetCurrentServiceConfig(c context.Context) (*ServiceConfig, error) {
88 » // This maker function is used to do the actual fetch of the ServiceConf ig
89 » // from datastore. It is called if the ServiceConfig is not in proc cac he.
90 » maker := func() (interface{}, time.Duration, error) {
91 » » msg := ServiceConfig{ID: ServiceConfigID}
92 » » err := datastore.Get(c, &msg)
93 » » if err != nil {
94 » » » return nil, time.Minute, err
95 » » }
96 » » logging.Infof(c, "loaded service config from datastore")
97 » » return msg, time.Minute, nil
98 » }
99 » item, err := proccache.GetOrMake(c, ServiceConfigID, maker)
100 » if err != nil {
101 » » return nil, fmt.Errorf("failed to get service config: %s", err.E rror())
102 » }
103 » if msg, ok := item.(ServiceConfig); ok {
104 » » logging.Infof(c, "loaded config entry from %s", msg.LastUpdated. Format(time.RFC3339))
105 » » return &msg, nil
106 » }
107 » return nil, fmt.Errorf("could not load service config %#v", item)
108 }
109
110 // UpdateServiceConfig fetches the service config from luci-config
111 // and then stores a snapshot of the configuration in datastore.
112 func UpdateServiceConfig(c context.Context) error {
113 » // Load the settings from luci-config.
114 » cs := string(cfgclient.CurrentServiceConfigSet(c))
115 » // Acquire the raw config client.
116 » lucicfg := backend.Get(c).GetConfigInterface(c, backend.AsService)
117 » // Our global config name is called settings.cfg.
118 » cfg, err := lucicfg.GetConfig(c, cs, "settings.cfg", false)
119 » if err != nil {
120 » » return fmt.Errorf("could not load settings.cfg from luci-config: %s", err)
121 » }
122 » settings := &config.Settings{}
123 » err = proto.UnmarshalText(cfg.Content, settings)
124 » if err != nil {
125 » » return fmt.Errorf("could not unmarshal proto from luci-config:\n %s", cfg.Content)
126 » }
127 » newConfig := ServiceConfig{
128 » » ID: ServiceConfigID,
129 » » Text: cfg.Content,
130 » » Revision: cfg.Revision,
131 » » LastUpdated: time.Now().UTC(),
132 » }
133 » newConfig.Data, err = proto.Marshal(settings)
134 » if err != nil {
135 » » return fmt.Errorf("could not marshal proto into binary\n%s", new Config.Text)
136 » }
137
138 » // Do the revision check & swap in a datastore transaction.
139 » err = datastore.RunInTransaction(c, func(c context.Context) error {
140 » » oldConfig := ServiceConfig{ID: ServiceConfigID}
141 » » err := datastore.Get(c, &oldConfig)
142 » » switch err {
143 » » case datastore.ErrNoSuchEntity:
144 » » » // Might be the first time this has run.
145 » » » logging.WithError(err).Warningf(c, "No existing service config.")
146 » » case nil:
147 » » » // Continue
148 » » default:
149 » » » return fmt.Errorf("could not load existing config: %s", err)
150 » » }
151 » » // Check to see if we need to update
152 » » if oldConfig.Revision == newConfig.Revision {
153 » » » logging.Infof(c, "revisions matched (%s), no need to upd ate", oldConfig.Revision)
154 » » » return nil
155 » » }
156 » » logging.Infof(c, "revisions differ (old %s, new %s), updating",
157 » » » oldConfig.Revision, newConfig.Revision)
158 » » return datastore.Put(c, &newConfig)
159 » }, nil)
160
161 » if err != nil {
162 » » return fmt.Errorf("failed to update config entry in transaction" , err)
163 » }
164 » logging.Infof(c, "successfully updated to new config")
165
166 » return nil
167 }
168
169 // UpdateProjectConfigs internal project configuration based off luci-config.
55 // update updates Milo's configuration based off luci config. This includes 170 // update updates Milo's configuration based off luci config. This includes
56 // scanning through all project and extract all console configs. 171 // scanning through all project and extract all console configs.
57 func UpdateProjectConfigs(c context.Context) error { 172 func UpdateProjectConfigs(c context.Context) error {
58 cfgName := info.AppID(c) + ".cfg" 173 cfgName := info.AppID(c) + ".cfg"
59 174
60 var ( 175 var (
61 configs []*config.Project 176 configs []*config.Project
62 metas []*cfgclient.Meta 177 metas []*cfgclient.Meta
63 ) 178 )
179 logging.Debugf(c, "fetching configs for %s", cfgName)
64 if err := cfgclient.Projects(c, cfgclient.AsService, cfgName, textproto. Slice(&configs), &metas); err != nil { 180 if err := cfgclient.Projects(c, cfgclient.AsService, cfgName, textproto. Slice(&configs), &metas); err != nil {
65 logging.WithError(err).Errorf(c, "Encountered error while gettin g project config for %s", cfgName) 181 logging.WithError(err).Errorf(c, "Encountered error while gettin g project config for %s", cfgName)
66 return err 182 return err
67 } 183 }
68 184
69 // A map of project ID to project. 185 // A map of project ID to project.
70 projects := map[string]*Project{} 186 projects := map[string]*Project{}
71 for i, proj := range configs { 187 for i, proj := range configs {
72 projectName, _, _ := metas[i].ConfigSet.SplitProject() 188 projectName, _, _ := metas[i].ConfigSet.SplitProject()
73 189
(...skipping 80 matching lines...) Expand 10 before | Expand all | Expand 10 after
154 if err != nil { 270 if err != nil {
155 return nil, err 271 return nil, err
156 } 272 }
157 for _, cs := range p.Consoles { 273 for _, cs := range p.Consoles {
158 if cs.Name == consoleName { 274 if cs.Name == consoleName {
159 return cs, nil 275 return cs, nil
160 } 276 }
161 } 277 }
162 return nil, fmt.Errorf("Console %s not found in project %s", consoleName , projName) 278 return nil, fmt.Errorf("Console %s not found in project %s", consoleName , projName)
163 } 279 }
OLDNEW
« no previous file with comments | « milo/appengine/common/acl.go ('k') | milo/appengine/common/config_test.go » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698