Chromium Code Reviews| OLD | NEW |
|---|---|
| 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 service | 5 package service |
| 6 | 6 |
| 7 import ( | 7 import ( |
| 8 "flag" | 8 "flag" |
| 9 "fmt" | 9 "fmt" |
| 10 "net/http" | 10 "net/http" |
| 11 "net/url" | 11 "net/url" |
| 12 "os" | 12 "os" |
| 13 "os/signal" | 13 "os/signal" |
| 14 "path/filepath" | 14 "path/filepath" |
| 15 "runtime/pprof" | 15 "runtime/pprof" |
| 16 "sync/atomic" | 16 "sync/atomic" |
| 17 "time" | 17 "time" |
| 18 | 18 |
| 19 "github.com/luci/luci-go/appengine/gaesettings" | |
| 19 "github.com/luci/luci-go/client/authcli" | 20 "github.com/luci/luci-go/client/authcli" |
| 20 "github.com/luci/luci-go/common/auth" | 21 "github.com/luci/luci-go/common/auth" |
| 21 "github.com/luci/luci-go/common/clock/clockflag" | 22 "github.com/luci/luci-go/common/clock/clockflag" |
| 22 "github.com/luci/luci-go/common/config/impl/filesystem" | 23 "github.com/luci/luci-go/common/config/impl/filesystem" |
| 23 "github.com/luci/luci-go/common/data/caching/proccache" | 24 "github.com/luci/luci-go/common/data/caching/proccache" |
| 24 "github.com/luci/luci-go/common/errors" | 25 "github.com/luci/luci-go/common/errors" |
| 25 "github.com/luci/luci-go/common/gcloud/gs" | 26 "github.com/luci/luci-go/common/gcloud/gs" |
| 26 log "github.com/luci/luci-go/common/logging" | 27 log "github.com/luci/luci-go/common/logging" |
| 27 "github.com/luci/luci-go/common/logging/gologger" | 28 "github.com/luci/luci-go/common/logging/gologger" |
| 28 "github.com/luci/luci-go/common/proto/google" | 29 "github.com/luci/luci-go/common/proto/google" |
| 29 "github.com/luci/luci-go/common/tsmon" | 30 "github.com/luci/luci-go/common/tsmon" |
| 30 "github.com/luci/luci-go/common/tsmon/target" | 31 "github.com/luci/luci-go/common/tsmon/target" |
| 31 "github.com/luci/luci-go/grpc/prpc" | 32 "github.com/luci/luci-go/grpc/prpc" |
| 32 "github.com/luci/luci-go/logdog/api/config/svcconfig" | 33 "github.com/luci/luci-go/logdog/api/config/svcconfig" |
| 33 "github.com/luci/luci-go/logdog/api/endpoints/coordinator/services/v1" | 34 "github.com/luci/luci-go/logdog/api/endpoints/coordinator/services/v1" |
| 34 "github.com/luci/luci-go/logdog/common/storage" | 35 "github.com/luci/luci-go/logdog/common/storage" |
| 35 "github.com/luci/luci-go/logdog/common/storage/bigtable" | 36 "github.com/luci/luci-go/logdog/common/storage/bigtable" |
| 36 "github.com/luci/luci-go/logdog/server/retryServicesClient" | 37 "github.com/luci/luci-go/logdog/server/retryServicesClient" |
| 37 "github.com/luci/luci-go/logdog/server/service/config" | 38 "github.com/luci/luci-go/logdog/server/service/config" |
| 38 "github.com/luci/luci-go/luci_config/common/cfgtypes" | 39 "github.com/luci/luci-go/luci_config/common/cfgtypes" |
| 39 "github.com/luci/luci-go/luci_config/server/cfgclient" | 40 "github.com/luci/luci-go/luci_config/server/cfgclient" |
| 40 "github.com/luci/luci-go/luci_config/server/cfgclient/backend/client" | 41 "github.com/luci/luci-go/luci_config/server/cfgclient/backend/client" |
| 41 "github.com/luci/luci-go/luci_config/server/cfgclient/backend/testconfig " | 42 "github.com/luci/luci-go/luci_config/server/cfgclient/backend/testconfig " |
| 42 "github.com/luci/luci-go/luci_config/server/cfgclient/textproto" | 43 "github.com/luci/luci-go/luci_config/server/cfgclient/textproto" |
| 44 "github.com/luci/luci-go/server/settings" | |
| 45 | |
| 46 "github.com/luci/gae/impl/cloud" | |
| 43 | 47 |
| 44 "cloud.google.com/go/compute/metadata" | 48 "cloud.google.com/go/compute/metadata" |
| 49 "cloud.google.com/go/datastore" | |
| 45 "golang.org/x/net/context" | 50 "golang.org/x/net/context" |
| 46 "golang.org/x/oauth2" | 51 "golang.org/x/oauth2" |
| 47 "google.golang.org/api/option" | 52 "google.golang.org/api/option" |
| 48 ) | 53 ) |
| 49 | 54 |
| 50 var ( | 55 var ( |
| 51 // ErrInvalidConfig is an error that is returned when the supplied | 56 // ErrInvalidConfig is an error that is returned when the supplied |
| 52 // configuration is invalid. | 57 // configuration is invalid. |
| 53 ErrInvalidConfig = errors.New("invalid configuration") | 58 ErrInvalidConfig = errors.New("invalid configuration") |
| 54 | 59 |
| (...skipping 30 matching lines...) Expand all Loading... | |
| 85 authFlags authcli.Flags | 90 authFlags authcli.Flags |
| 86 tsMonFlags tsmon.Flags | 91 tsMonFlags tsmon.Flags |
| 87 | 92 |
| 88 coordinatorHost string | 93 coordinatorHost string |
| 89 coordinatorInsecure bool | 94 coordinatorInsecure bool |
| 90 storageCredentialJSONPath string | 95 storageCredentialJSONPath string |
| 91 cpuProfilePath string | 96 cpuProfilePath string |
| 92 heapProfilePath string | 97 heapProfilePath string |
| 93 | 98 |
| 94 // onGCE is true if we're on GCE. We probe this exactly once. | 99 // onGCE is true if we're on GCE. We probe this exactly once. |
| 95 » onGCE bool | 100 » onGCE bool |
| 101 » hasDatastore bool | |
| 96 | 102 |
| 97 killCheckInterval clockflag.Duration | 103 killCheckInterval clockflag.Duration |
| 98 configFilePath string | 104 configFilePath string |
| 99 serviceConfig svcconfig.Config | 105 serviceConfig svcconfig.Config |
| 100 configCache config.ProcCache | 106 configCache config.ProcCache |
| 101 | 107 |
| 102 // serviceID is the cloud project ID, which is also this service's uniqu e | 108 // serviceID is the cloud project ID, which is also this service's uniqu e |
| 103 // ID. This can be specified by flag or, if on GCE, will automatically b e | 109 // ID. This can be specified by flag or, if on GCE, will automatically b e |
| 104 // probed from metadata. | 110 // probed from metadata. |
| 105 serviceID string | 111 serviceID string |
| (...skipping 14 matching lines...) Expand all Loading... | |
| 120 | 126 |
| 121 rc := 0 | 127 rc := 0 |
| 122 if err := s.runImpl(c, f); err != nil { | 128 if err := s.runImpl(c, f); err != nil { |
| 123 log.WithError(err).Errorf(c, "Application exiting with error.") | 129 log.WithError(err).Errorf(c, "Application exiting with error.") |
| 124 rc = 1 | 130 rc = 1 |
| 125 } | 131 } |
| 126 os.Exit(rc) | 132 os.Exit(rc) |
| 127 } | 133 } |
| 128 | 134 |
| 129 func (s *Service) runImpl(c context.Context, f func(context.Context) error) erro r { | 135 func (s *Service) runImpl(c context.Context, f func(context.Context) error) erro r { |
| 136 // Probe our environment for default values. | |
| 137 s.probeGCEEnvironment(c) | |
|
iannucci
2017/01/21 00:32:24
should this have been in the previous CL?
dnj
2017/01/21 02:00:02
Ugh, looks like it was removed when I split. Oh we
| |
| 138 | |
| 139 // Install service flags and parse. | |
| 130 s.addFlags(c, &s.Flags) | 140 s.addFlags(c, &s.Flags) |
| 131 if err := s.Flags.Parse(os.Args[1:]); err != nil { | 141 if err := s.Flags.Parse(os.Args[1:]); err != nil { |
| 132 log.WithError(err).Errorf(c, "Failed to parse command-line.") | 142 log.WithError(err).Errorf(c, "Failed to parse command-line.") |
| 133 return err | 143 return err |
| 134 } | 144 } |
| 135 | 145 |
| 136 // Install logging configuration. | 146 // Install logging configuration. |
| 137 c = s.loggingFlags.Set(c) | 147 c = s.loggingFlags.Set(c) |
| 138 | 148 |
| 139 if p := s.cpuProfilePath; p != "" { | 149 if p := s.cpuProfilePath; p != "" { |
| (...skipping 25 matching lines...) Expand all Loading... | |
| 165 | 175 |
| 166 if err := pprof.WriteHeapProfile(fd); err != nil { | 176 if err := pprof.WriteHeapProfile(fd); err != nil { |
| 167 log.Fields{ | 177 log.Fields{ |
| 168 log.ErrorKey: err, | 178 log.ErrorKey: err, |
| 169 "path": p, | 179 "path": p, |
| 170 }.Warningf(c, "Failed to write heap profile.") | 180 }.Warningf(c, "Failed to write heap profile.") |
| 171 } | 181 } |
| 172 }() | 182 }() |
| 173 } | 183 } |
| 174 | 184 |
| 185 // Cancel our Context after we're done our run loop. | |
| 186 c, cancelFunc := context.WithCancel(c) | |
| 187 defer cancelFunc() | |
| 188 | |
| 175 // Validate the runtime environment. | 189 // Validate the runtime environment. |
| 176 if s.serviceID == "" { | 190 if s.serviceID == "" { |
| 177 return errors.New("no service ID was configured (-service-id)") | 191 return errors.New("no service ID was configured (-service-id)") |
| 178 } | 192 } |
| 179 | 193 |
| 194 // Install a cloud datastore client. This is non-fatal if it fails. | |
| 195 dsClient, err := s.initDatastoreClient(c) | |
| 196 if err == nil { | |
| 197 defer dsClient.Close() | |
| 198 | |
| 199 ccfg := cloud.Config{ | |
| 200 DS: dsClient, | |
| 201 } | |
| 202 c = ccfg.Use(c) | |
| 203 c = settings.Use(c, settings.New(gaesettings.Storage{})) | |
| 204 | |
| 205 s.hasDatastore = true | |
| 206 log.Infof(c, "Enabled cloud datastore access.") | |
| 207 } else { | |
| 208 log.WithError(err).Debugf(c, "Failed to create cloud datastore c lient.") | |
|
iannucci
2017/01/21 00:32:24
wdyt about reversing the severities? maybe debug f
dnj
2017/01/21 02:00:02
Okay.
| |
| 209 } | |
| 210 | |
| 180 // Install a process-wide cache. | 211 // Install a process-wide cache. |
| 181 c = proccache.Use(c, &proccache.Cache{}) | 212 c = proccache.Use(c, &proccache.Cache{}) |
| 182 | 213 |
| 183 // Configure our signal handler. It will listen for terminating signals and | 214 // Configure our signal handler. It will listen for terminating signals and |
| 184 // issue a shutdown signal if one is received. | 215 // issue a shutdown signal if one is received. |
| 185 signalC := make(chan os.Signal) | 216 signalC := make(chan os.Signal) |
| 186 go func(c context.Context) { | 217 go func(c context.Context) { |
| 187 hasShutdownAlready := false | 218 hasShutdownAlready := false |
| 188 for sig := range signalC { | 219 for sig := range signalC { |
| 189 if !hasShutdownAlready { | 220 if !hasShutdownAlready { |
| (...skipping 17 matching lines...) Expand all Loading... | |
| 207 | 238 |
| 208 // Initialize our tsmon library. | 239 // Initialize our tsmon library. |
| 209 c = tsmon.WithState(c, tsmon.NewState()) | 240 c = tsmon.WithState(c, tsmon.NewState()) |
| 210 | 241 |
| 211 if err := tsmon.InitializeFromFlags(c, &s.tsMonFlags); err != nil { | 242 if err := tsmon.InitializeFromFlags(c, &s.tsMonFlags); err != nil { |
| 212 log.WithError(err).Warningf(c, "Failed to initialize monitoring; will continue without metrics.") | 243 log.WithError(err).Warningf(c, "Failed to initialize monitoring; will continue without metrics.") |
| 213 } | 244 } |
| 214 defer tsmon.Shutdown(c) | 245 defer tsmon.Shutdown(c) |
| 215 | 246 |
| 216 // Initialize our Client instantiations. | 247 // Initialize our Client instantiations. |
| 217 var err error | |
| 218 if s.coord, err = s.initCoordinatorClient(c); err != nil { | 248 if s.coord, err = s.initCoordinatorClient(c); err != nil { |
| 219 log.WithError(err).Errorf(c, "Failed to setup Coordinator client .") | 249 log.WithError(err).Errorf(c, "Failed to setup Coordinator client .") |
| 220 return err | 250 return err |
| 221 } | 251 } |
| 222 | 252 |
| 223 // Initialize and install our config service and caching layers, and loa d our | 253 // Initialize and install our config service and caching layers, and loa d our |
| 224 // initial service config. | 254 // initial service config. |
| 225 if err := s.initConfig(&c); err != nil { | 255 if err := s.initConfig(&c); err != nil { |
| 226 log.WithError(err).Errorf(c, "Failed to setup configuration.") | 256 log.WithError(err).Errorf(c, "Failed to setup configuration.") |
| 227 return err | 257 return err |
| (...skipping 36 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 264 "If supplied, enable CPU profiling and write the profile here.") | 294 "If supplied, enable CPU profiling and write the profile here.") |
| 265 fs.Var(&s.killCheckInterval, "config-kill-interval", | 295 fs.Var(&s.killCheckInterval, "config-kill-interval", |
| 266 "If non-zero, poll for configuration changes and kill the applic ation if one is detected.") | 296 "If non-zero, poll for configuration changes and kill the applic ation if one is detected.") |
| 267 fs.StringVar(&s.configFilePath, "config-file-path", "", | 297 fs.StringVar(&s.configFilePath, "config-file-path", "", |
| 268 "If set, load configuration from a local filesystem rooted here. ") | 298 "If set, load configuration from a local filesystem rooted here. ") |
| 269 } | 299 } |
| 270 | 300 |
| 271 // probeGCEEnvironment fills in any parameters that can be probed from Google | 301 // probeGCEEnvironment fills in any parameters that can be probed from Google |
| 272 // Compute Engine metadata. | 302 // Compute Engine metadata. |
| 273 // | 303 // |
| 274 // If we're not running on GCE, this will return nil. An error will only be | 304 // If we're not running on GCE, this will do nothing. It is non-fatal if any |
| 275 // returned if an operation that is expected to work fails. | 305 // given GCE field fails to be probed. |
| 276 func (s *Service) probeGCEEnvironment(c context.Context) error { | 306 func (s *Service) probeGCEEnvironment(c context.Context) { |
| 277 s.onGCE = metadata.OnGCE() | 307 s.onGCE = metadata.OnGCE() |
| 278 if !s.onGCE { | 308 if !s.onGCE { |
| 279 » » return nil | 309 » » return |
| 280 } | 310 } |
| 281 | 311 |
| 282 // Determine our service ID from metadata. The service ID will equal the cloud | 312 // Determine our service ID from metadata. The service ID will equal the cloud |
| 283 // project ID. | 313 // project ID. |
| 284 if s.serviceID == "" { | 314 if s.serviceID == "" { |
| 285 var err error | 315 var err error |
| 286 if s.serviceID, err = metadata.ProjectID(); err != nil { | 316 if s.serviceID, err = metadata.ProjectID(); err != nil { |
| 287 » » » log.WithError(err).Errorf(c, "Failed to probe GCE projec t ID.") | 317 » » » log.WithError(err).Warningf(c, "Failed to probe GCE proj ect ID.") |
| 288 » » » return err | |
| 289 } | 318 } |
| 290 } | 319 } |
| 291 » return nil | 320 } |
| 321 | |
| 322 func (s *Service) initDatastoreClient(c context.Context) (*datastore.Client, err or) { | |
| 323 » // Initialize Storage authentication. | |
| 324 » tokenSource, err := s.TokenSource(c, func(o *auth.Options) { | |
| 325 » » o.Scopes = []string{datastore.ScopeDatastore} | |
| 326 » }) | |
| 327 » if err != nil { | |
| 328 » » log.WithError(err).Errorf(c, "Failed to create datastore TokenSo urce.") | |
| 329 » » return nil, err | |
| 330 » } | |
| 331 | |
| 332 » return datastore.NewClient(c, s.serviceID, | |
| 333 » » option.WithTokenSource(tokenSource)) | |
| 292 } | 334 } |
| 293 | 335 |
| 294 func (s *Service) initCoordinatorClient(c context.Context) (logdog.ServicesClien t, error) { | 336 func (s *Service) initCoordinatorClient(c context.Context) (logdog.ServicesClien t, error) { |
| 295 if s.coordinatorHost == "" { | 337 if s.coordinatorHost == "" { |
| 296 log.Errorf(c, "Missing Coordinator URL (-coordinator).") | 338 log.Errorf(c, "Missing Coordinator URL (-coordinator).") |
| 297 return nil, ErrInvalidConfig | 339 return nil, ErrInvalidConfig |
| 298 } | 340 } |
| 299 | 341 |
| 300 httpClient, err := s.AuthenticatedClient(c, func(o *auth.Options) { | 342 httpClient, err := s.AuthenticatedClient(c, func(o *auth.Options) { |
| 301 o.Scopes = CoordinatorScopes | 343 o.Scopes = CoordinatorScopes |
| (...skipping 51 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 353 if ccfg.ConfigSet == "" { | 395 if ccfg.ConfigSet == "" { |
| 354 return errors.New("coordinator does not specify a config set") | 396 return errors.New("coordinator does not specify a config set") |
| 355 } | 397 } |
| 356 | 398 |
| 357 log.Fields{ | 399 log.Fields{ |
| 358 "host": host, | 400 "host": host, |
| 359 }.Debugf(*c, "Using remote configuration service client.") | 401 }.Debugf(*c, "Using remote configuration service client.") |
| 360 p = &client.RemoteProvider{ | 402 p = &client.RemoteProvider{ |
| 361 Host: host, | 403 Host: host, |
| 362 } | 404 } |
| 405 | |
| 406 // If using a remote config provider, enable datastore access an d caching. | |
| 407 opts.DatastoreCacheAvailable = s.hasDatastore | |
| 363 } else { | 408 } else { |
| 364 // Test / Local: use filesystem config path. | 409 // Test / Local: use filesystem config path. |
| 365 ci, err := filesystem.New(s.configFilePath) | 410 ci, err := filesystem.New(s.configFilePath) |
| 366 if err != nil { | 411 if err != nil { |
| 367 return err | 412 return err |
| 368 } | 413 } |
| 369 p = &testconfig.Provider{Base: ci} | 414 p = &testconfig.Provider{Base: ci} |
| 370 } | 415 } |
| 371 | 416 |
| 372 // Add config caching layers. | 417 // Add config caching layers. |
| (...skipping 186 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 559 // | 604 // |
| 560 // An optional permutation function can be provided to modify those Options | 605 // An optional permutation function can be provided to modify those Options |
| 561 // before the Authenticator is created. | 606 // before the Authenticator is created. |
| 562 func (s *Service) TokenSource(c context.Context, f func(o *auth.Options)) (oauth 2.TokenSource, error) { | 607 func (s *Service) TokenSource(c context.Context, f func(o *auth.Options)) (oauth 2.TokenSource, error) { |
| 563 a, err := s.Authenticator(c, f) | 608 a, err := s.Authenticator(c, f) |
| 564 if err != nil { | 609 if err != nil { |
| 565 return nil, err | 610 return nil, err |
| 566 } | 611 } |
| 567 return a.TokenSource() | 612 return a.TokenSource() |
| 568 } | 613 } |
| OLD | NEW |