Index: milo/appengine/common/config.go |
diff --git a/milo/appengine/common/config.go b/milo/appengine/common/config.go |
deleted file mode 100644 |
index 360df2da73c7df0872e4d50a7e79606172386fa5..0000000000000000000000000000000000000000 |
--- a/milo/appengine/common/config.go |
+++ /dev/null |
@@ -1,280 +0,0 @@ |
-// 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 common |
- |
-import ( |
- "fmt" |
- "time" |
- |
- "github.com/golang/protobuf/proto" |
- "golang.org/x/net/context" |
- |
- "github.com/luci/gae/service/datastore" |
- "github.com/luci/gae/service/info" |
- "github.com/luci/luci-go/common/data/caching/proccache" |
- "github.com/luci/luci-go/common/logging" |
- "github.com/luci/luci-go/luci_config/server/cfgclient" |
- "github.com/luci/luci-go/luci_config/server/cfgclient/backend" |
- "github.com/luci/luci-go/luci_config/server/cfgclient/textproto" |
- |
- "github.com/luci/luci-go/milo/api/config" |
-) |
- |
-// Project is a LUCI project. |
-type Project struct { |
- // The ID of the project, as per self defined. This is not the luci-config |
- // name. |
- ID string `gae:"$id"` |
- // The luci-config name of the project. |
- Name string |
- // The Project data in protobuf binary format. |
- Data []byte `gae:",noindex"` |
-} |
- |
-// The key for the service config entity in datastore. |
-const ServiceConfigID = "service_config" |
- |
-// ServiceConfig is a container for the instance's service config. |
-type ServiceConfig struct { |
- // ID is the datastore key. This should be static, as there should only be |
- // one service config. |
- ID string `gae:"$id"` |
- // Revision is the revision of the config, taken from luci-config. This is used |
- // to determine if the entry needs to be refreshed. |
- Revision string |
- // Data is the binary proto of the config. |
- Data []byte `gae:",noindex"` |
- // Text is the text format of the config. For human consumption only. |
- Text string `gae:",noindex"` |
- // LastUpdated is the time this config was last updated. |
- LastUpdated time.Time |
-} |
- |
-// GetServiceConfig returns the service (aka global) config for the current |
-// instance of Milo from the datastore. Returns an empty config and warn heavily |
-// if none is found. |
-// TODO(hinoka): Use process cache to cache configs. |
-func GetSettings(c context.Context) *config.Settings { |
- settings := config.Settings{ |
- Buildbot: &config.Settings_Buildbot{}, |
- } |
- |
- msg, err := GetCurrentServiceConfig(c) |
- if err != nil { |
- // The service config does not exist, just return an empty config |
- // and complain loudly in the logs. |
- logging.WithError(err).Errorf(c, |
- "Encountered error while loading service config, using empty config.") |
- return &settings |
- } |
- |
- err = proto.Unmarshal(msg.Data, &settings) |
- if err != nil { |
- // The service config is broken, just return an empty config |
- // and complain loudly in the logs. |
- logging.WithError(err).Errorf(c, |
- "Encountered error while unmarshalling service config, using empty config.") |
- // Zero out the message just incase something got written in. |
- settings = config.Settings{Buildbot: &config.Settings_Buildbot{}} |
- } |
- |
- return &settings |
-} |
- |
-// GetCurrentServiceConfig gets the service config for the instance from either |
-// process cache or datastore cache. |
-func GetCurrentServiceConfig(c context.Context) (*ServiceConfig, error) { |
- // This maker function is used to do the actual fetch of the ServiceConfig |
- // from datastore. It is called if the ServiceConfig is not in proc cache. |
- maker := func() (interface{}, time.Duration, error) { |
- msg := ServiceConfig{ID: ServiceConfigID} |
- err := datastore.Get(c, &msg) |
- if err != nil { |
- return nil, time.Minute, err |
- } |
- logging.Infof(c, "loaded service config from datastore") |
- return msg, time.Minute, nil |
- } |
- item, err := proccache.GetOrMake(c, ServiceConfigID, maker) |
- if err != nil { |
- return nil, fmt.Errorf("failed to get service config: %s", err.Error()) |
- } |
- if msg, ok := item.(ServiceConfig); ok { |
- logging.Infof(c, "loaded config entry from %s", msg.LastUpdated.Format(time.RFC3339)) |
- return &msg, nil |
- } |
- return nil, fmt.Errorf("could not load service config %#v", item) |
-} |
- |
-// UpdateServiceConfig fetches the service config from luci-config |
-// and then stores a snapshot of the configuration in datastore. |
-func UpdateServiceConfig(c context.Context) error { |
- // Load the settings from luci-config. |
- cs := string(cfgclient.CurrentServiceConfigSet(c)) |
- // Acquire the raw config client. |
- lucicfg := backend.Get(c).GetConfigInterface(c, backend.AsService) |
- // Our global config name is called settings.cfg. |
- cfg, err := lucicfg.GetConfig(c, cs, "settings.cfg", false) |
- if err != nil { |
- return fmt.Errorf("could not load settings.cfg from luci-config: %s", err) |
- } |
- settings := &config.Settings{} |
- err = proto.UnmarshalText(cfg.Content, settings) |
- if err != nil { |
- return fmt.Errorf("could not unmarshal proto from luci-config:\n%s", cfg.Content) |
- } |
- newConfig := ServiceConfig{ |
- ID: ServiceConfigID, |
- Text: cfg.Content, |
- Revision: cfg.Revision, |
- LastUpdated: time.Now().UTC(), |
- } |
- newConfig.Data, err = proto.Marshal(settings) |
- if err != nil { |
- return fmt.Errorf("could not marshal proto into binary\n%s", newConfig.Text) |
- } |
- |
- // Do the revision check & swap in a datastore transaction. |
- err = datastore.RunInTransaction(c, func(c context.Context) error { |
- oldConfig := ServiceConfig{ID: ServiceConfigID} |
- err := datastore.Get(c, &oldConfig) |
- switch err { |
- case datastore.ErrNoSuchEntity: |
- // Might be the first time this has run. |
- logging.WithError(err).Warningf(c, "No existing service config.") |
- case nil: |
- // Continue |
- default: |
- return fmt.Errorf("could not load existing config: %s", err) |
- } |
- // Check to see if we need to update |
- if oldConfig.Revision == newConfig.Revision { |
- logging.Infof(c, "revisions matched (%s), no need to update", oldConfig.Revision) |
- return nil |
- } |
- logging.Infof(c, "revisions differ (old %s, new %s), updating", |
- oldConfig.Revision, newConfig.Revision) |
- return datastore.Put(c, &newConfig) |
- }, nil) |
- |
- if err != nil { |
- return fmt.Errorf("failed to update config entry in transaction", err) |
- } |
- logging.Infof(c, "successfully updated to new config") |
- |
- return nil |
-} |
- |
-// UpdateProjectConfigs internal project configuration based off luci-config. |
-// update updates Milo's configuration based off luci config. This includes |
-// scanning through all project and extract all console configs. |
-func UpdateProjectConfigs(c context.Context) error { |
- cfgName := info.AppID(c) + ".cfg" |
- |
- var ( |
- configs []*config.Project |
- metas []*cfgclient.Meta |
- ) |
- logging.Debugf(c, "fetching configs for %s", cfgName) |
- if err := cfgclient.Projects(c, cfgclient.AsService, cfgName, textproto.Slice(&configs), &metas); err != nil { |
- logging.WithError(err).Errorf(c, "Encountered error while getting project config for %s", cfgName) |
- return err |
- } |
- |
- // A map of project ID to project. |
- projects := map[string]*Project{} |
- for i, proj := range configs { |
- projectName, _, _ := metas[i].ConfigSet.SplitProject() |
- |
- logging.Infof(c, "Prossing %s", projectName) |
- if dup, ok := projects[proj.ID]; ok { |
- return fmt.Errorf( |
- "Duplicate project ID: %s. (%s and %s)", proj.ID, dup.Name, projectName) |
- } |
- p := &Project{ |
- ID: proj.ID, |
- Name: string(projectName), |
- } |
- projects[proj.ID] = p |
- |
- var err error |
- p.Data, err = proto.Marshal(proj) |
- if err != nil { |
- return err |
- } |
- } |
- |
- // Now load all the data into the datastore. |
- projs := make([]*Project, 0, len(projects)) |
- for _, proj := range projects { |
- projs = append(projs, proj) |
- } |
- if err := datastore.Put(c, projs); err != nil { |
- return err |
- } |
- |
- // Delete entries that no longer exist. |
- q := datastore.NewQuery("Project").KeysOnly(true) |
- allProjs := []Project{} |
- datastore.GetAll(c, q, &allProjs) |
- toDelete := []Project{} |
- for _, proj := range allProjs { |
- if _, ok := projects[proj.ID]; !ok { |
- toDelete = append(toDelete, proj) |
- } |
- } |
- datastore.Delete(c, toDelete) |
- |
- return nil |
-} |
- |
-// GetAllProjects returns all registered projects. |
-func GetAllProjects(c context.Context) ([]*config.Project, error) { |
- q := datastore.NewQuery("Project") |
- q.Order("ID") |
- |
- ps := []*Project{} |
- err := datastore.GetAll(c, q, &ps) |
- if err != nil { |
- return nil, err |
- } |
- results := make([]*config.Project, len(ps)) |
- for i, p := range ps { |
- results[i] = &config.Project{} |
- if err := proto.Unmarshal(p.Data, results[i]); err != nil { |
- return nil, err |
- } |
- } |
- return results, nil |
-} |
- |
-// GetProject returns the requested project. |
-func GetProject(c context.Context, projName string) (*config.Project, error) { |
- // Next, Try datastore |
- p := Project{ID: projName} |
- if err := datastore.Get(c, &p); err != nil { |
- return nil, err |
- } |
- mp := config.Project{} |
- if err := proto.Unmarshal(p.Data, &mp); err != nil { |
- return nil, err |
- } |
- |
- return &mp, nil |
-} |
- |
-// GetConsole returns the requested console instance. |
-func GetConsole(c context.Context, projName, consoleName string) (*config.Console, error) { |
- p, err := GetProject(c, projName) |
- if err != nil { |
- return nil, err |
- } |
- for _, cs := range p.Consoles { |
- if cs.Name == consoleName { |
- return cs, nil |
- } |
- } |
- return nil, fmt.Errorf("Console %s not found in project %s", consoleName, projName) |
-} |