OLD | NEW |
1 # Copyright 2013 The LUCI Authors. All rights reserved. | 1 # Copyright 2013 The LUCI Authors. All rights reserved. |
2 # Use of this source code is governed by the Apache v2.0 license that can be | 2 # Use of this source code is governed by the Apache v2.0 license that can be |
3 # found in the LICENSE file. | 3 # found in the LICENSE file. |
4 | 4 |
5 """Instance specific settings.""" | 5 """Instance specific settings.""" |
6 | 6 |
7 from google.appengine.ext import ndb | 7 import logging |
| 8 import posixpath |
8 | 9 |
9 from components.datastore_utils import config | 10 from components import config |
| 11 from components import gitiles |
| 12 from components import net |
| 13 from components import utils |
| 14 from components.config import validation |
| 15 |
| 16 from proto import config_pb2 |
| 17 |
| 18 SETTINGS_CFG_FILENAME = 'settings.cfg' |
10 | 19 |
11 | 20 |
12 class GlobalConfig(config.GlobalConfig): | 21 ConfigApi = config.ConfigApi |
13 """Application wide settings.""" | |
14 # id to inject into pages if applicable. | |
15 google_analytics = ndb.StringProperty(indexed=False, default='') | |
16 | |
17 # The number of seconds an old task can be deduped from. | |
18 reusable_task_age_secs = ndb.IntegerProperty( | |
19 indexed=False, default=7*24*60*60) | |
20 | |
21 # The amount of time that has to pass before a machine is considered dead. | |
22 bot_death_timeout_secs = ndb.IntegerProperty(default=10*60) | |
23 | |
24 # Enable ts_mon based monitoring. | |
25 enable_ts_monitoring = ndb.BooleanProperty(indexed=False, default=False) | |
26 | 22 |
27 | 23 |
28 def settings(fresh=False): | 24 @validation.self_rule(SETTINGS_CFG_FILENAME, config_pb2.SettingsCfg) |
29 """Loads GlobalConfig or a default one if not present. | 25 def validate_settings(cfg, ctx): |
| 26 """Validates settings.cfg file against proto message schema.""" |
| 27 def within_year(value): |
| 28 if value < 0: |
| 29 ctx.error('cannot be negative') |
| 30 elif value > 60 * 60 * 24 * 365: |
| 31 ctx.error('cannot be more than a year') |
30 | 32 |
31 If fresh=True, a full fetch from NDB is done. | 33 with ctx.prefix('bot_death_timeout_secs '): |
| 34 within_year(cfg.bot_death_timeout_secs) |
| 35 with ctx.prefix('reusable_task_age_secs '): |
| 36 within_year(cfg.reusable_task_age_secs) |
| 37 |
| 38 |
| 39 @utils.memcache('config:get_configs_url', time=60) |
| 40 def _get_configs_url(): |
| 41 """Returns URL where luci-config fetches configs from.""" |
| 42 url = None |
| 43 try: |
| 44 url = config.get_config_set_location(config.self_config_set()) |
| 45 except net.Error: |
| 46 logging.info( |
| 47 'Could not get configs URL. Possibly config directory for this ' |
| 48 'instance of swarming does not exist') |
| 49 return url or 'about:blank' |
| 50 |
| 51 |
| 52 def _gitiles_url(configs_url, rev, path): |
| 53 """URL to a directory in gitiles -> URL to a file at concrete revision.""" |
| 54 try: |
| 55 loc = gitiles.Location.parse(configs_url) |
| 56 return str(loc._replace( |
| 57 treeish=rev or loc.treeish, |
| 58 path=posixpath.join(loc.path, path))) |
| 59 except ValueError: |
| 60 # Not a gitiles URL, return as is. |
| 61 return configs_url |
| 62 |
| 63 |
| 64 def _get_settings(): |
| 65 """Returns (rev, cfg) where cfg is a parsed SettingsCfg message. |
| 66 |
| 67 If config does not exists, returns (None, <cfg with defaults>). |
| 68 |
| 69 The config is cached in the datastore. |
32 """ | 70 """ |
33 if fresh: | 71 rev = None |
34 GlobalConfig.clear_cache() | 72 cfg = None |
35 return GlobalConfig.cached() | 73 try: |
| 74 # store_last_good=True tells config component to update the config file |
| 75 # in a cron job. Here we just read from datastore. |
| 76 rev, cfg = config.get_self_config( |
| 77 SETTINGS_CFG_FILENAME, config_pb2.SettingsCfg, store_last_good=True) |
| 78 except config.CannotLoadConfigError as ex: |
| 79 logging.info('Could not load settings.cfg: %s; using defaults', ex) |
| 80 if not cfg: |
| 81 cfg = config_pb2.SettingsCfg( |
| 82 reusable_task_age_secs=7*24*60*60, |
| 83 bot_death_timeout_secs=10*60) |
| 84 return rev, cfg |
| 85 |
| 86 |
| 87 def settings_info(): |
| 88 """Returns information about the settings file. |
| 89 |
| 90 Returns a dict with keys: |
| 91 'cfg': parsed SettingsCfg message |
| 92 'rev': revision of cfg |
| 93 'rev_url': URL of a human-consumable page that displays the config |
| 94 'config_service_url': URL of the config_service. |
| 95 """ |
| 96 rev, cfg = _get_settings() |
| 97 rev_url = _gitiles_url(_get_configs_url(), rev, SETTINGS_CFG_FILENAME) |
| 98 cfg_service_hostname = config.config_service_hostname() |
| 99 return { |
| 100 'cfg': cfg, |
| 101 'rev': rev, |
| 102 'rev_url': rev_url, |
| 103 'config_service_url': ( |
| 104 'https://%s' % cfg_service_hostname if cfg_service_hostname else '' |
| 105 ), |
| 106 } |
| 107 |
| 108 |
| 109 def settings(): |
| 110 """Loads settings from an NDB-based cache or a default one if not present.""" |
| 111 return _get_settings()[1] |
OLD | NEW |