Index: server/internal/logdog/service/service.go |
diff --git a/server/internal/logdog/service/service.go b/server/internal/logdog/service/service.go |
index b3d5874eec9d1c9c4f5fca65424e8f65e8c0953e..0850cf501e52bc3f0a5197fe08c1412e9fc1d903 100644 |
--- a/server/internal/logdog/service/service.go |
+++ b/server/internal/logdog/service/service.go |
@@ -14,10 +14,12 @@ import ( |
"path/filepath" |
"runtime/pprof" |
"sync/atomic" |
+ "time" |
"github.com/luci/luci-go/client/authcli" |
"github.com/luci/luci-go/common/api/logdog_coordinator/services/v1" |
"github.com/luci/luci-go/common/auth" |
+ luciConfig "github.com/luci/luci-go/common/config" |
"github.com/luci/luci-go/common/gcloud/gs" |
log "github.com/luci/luci-go/common/logging" |
"github.com/luci/luci-go/common/logging/gologger" |
@@ -32,6 +34,7 @@ import ( |
"github.com/luci/luci-go/server/logdog/storage/bigtable" |
"golang.org/x/net/context" |
"google.golang.org/cloud" |
+ "google.golang.org/cloud/compute/metadata" |
) |
var ( |
@@ -49,6 +52,10 @@ var ( |
"Set to one when service is present and ready. Alert on missing values.") |
) |
+// projectConfigCacheDuration is the amount of time to cache a project's |
+// configuration before reloading. |
+const projectConfigCacheDuration = 30 * time.Minute |
+ |
// Service is a base class full of common LogDog service application parameters. |
type Service struct { |
// Name is the name of this service. It is used for logging, metrics, and |
@@ -68,12 +75,15 @@ type Service struct { |
coordinatorHost string |
coordinatorInsecure bool |
+ projectID string |
storageCredentialJSONPath string |
cpuProfilePath string |
heapProfilePath string |
coord logdog.ServicesClient |
config *config.Manager |
+ |
+ onGCE bool |
} |
// Run performs service-wide initialization and invokes the specified run |
@@ -141,6 +151,17 @@ func (s *Service) runImpl(c context.Context, f func(context.Context) error) erro |
}() |
} |
+ // Are we running on a GCE intance? |
+ if err := s.probeGCEEnvironment(c); err != nil { |
+ log.WithError(err).Errorf(c, "Failed to probe GCE environment.") |
+ return err |
+ } |
+ |
+ // Validate the runtime environment. |
+ if s.projectID == "" { |
+ return errors.New("no project ID was configured") |
+ } |
+ |
// Configure our signal handler. It will listen for terminating signals and |
// issue a shutdown signal if one is received. |
signalC := make(chan os.Signal) |
@@ -221,6 +242,10 @@ func (s *Service) addFlags(c context.Context, fs *flag.FlagSet) { |
s.authFlags.Register(fs, auth.Options{}) |
s.configFlags.AddToFlagSet(fs) |
+ fs.StringVar(&s.projectID, "project-id", "", |
+ "Specify the cloud project ID that this instance is supporting. If empty, the project ID "+ |
+ "will attempt to be resolved by probing the local environment. This probably will match the "+ |
+ "App ID of the Coordinator.") |
fs.StringVar(&s.coordinatorHost, "coordinator", "", |
"The Coordinator service's [host][:port].") |
fs.BoolVar(&s.coordinatorInsecure, "coordinator-insecure", false, |
@@ -233,6 +258,28 @@ func (s *Service) addFlags(c context.Context, fs *flag.FlagSet) { |
"If supplied, enable CPU profiling and write the profile here.") |
} |
+// probeGCEEnvironment fills in any parameters that can be probed from Google |
+// Compute Engine metadata. |
+// |
+// If we're not running on GCE, this will return nil. An error will only be |
+// returned if an operation that is expected to work fails. |
+func (s *Service) probeGCEEnvironment(c context.Context) error { |
+ s.onGCE = metadata.OnGCE() |
+ if !s.onGCE { |
+ return nil |
+ } |
+ |
+ // Determine our project ID. |
+ if s.projectID == "" { |
+ var err error |
+ if s.projectID, err = metadata.ProjectID(); err != nil { |
+ log.WithError(err).Errorf(c, "Failed to probe GCE project ID.") |
+ return err |
+ } |
+ } |
+ return nil |
+} |
+ |
func (s *Service) initCoordinatorClient(c context.Context) (logdog.ServicesClient, error) { |
if s.coordinatorHost == "" { |
log.Errorf(c, "Missing Coordinator URL (-coordinator).") |
@@ -275,6 +322,8 @@ func (s *Service) initConfig(c context.Context) (*config.Manager, error) { |
log.WithError(err).Errorf(c, "Failed to load configuration parameters.") |
return nil, err |
} |
+ o.ProjectID = s.projectID |
+ o.ProjectConfigCacheDuration = projectConfigCacheDuration |
o.KillFunc = s.shutdown |
return config.NewManager(c, *o) |
@@ -303,11 +352,25 @@ func (s *Service) Config() *svcconfig.Config { |
return s.config.Config() |
} |
+// ProjectConfig returns the cached project configuration. |
+// |
+// If the project configuration is not available, nil will be returned. |
+func (s *Service) ProjectConfig(c context.Context, proj luciConfig.ProjectName) (*svcconfig.ProjectConfig, error) { |
+ return s.config.ProjectConfig(c, proj) |
+} |
+ |
// Coordinator returns the cached Coordinator client. |
func (s *Service) Coordinator() logdog.ServicesClient { |
return s.coord |
} |
+// ProjectID returns the project ID. |
+// |
+// This is synonymous with the AppEngine "app ID". |
+func (s *Service) ProjectID() string { |
+ return s.projectID |
+} |
+ |
// IntermediateStorage instantiates the configured intermediate Storage |
// instance. |
func (s *Service) IntermediateStorage(c context.Context) (storage.Storage, error) { |