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

Side by Side Diff: appengine/gce-backend/config.py

Issue 2243923002: Refactors GCE backend config updates. (Closed) Base URL: https://chromium.googlesource.com/external/github.com/luci/luci-py@master
Patch Set: Created 4 years, 4 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 | « appengine/gce-backend/README.md ('k') | appengine/gce-backend/config_test.py » ('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 """Utilities for reading GCE Backend configuration.""" 5 """Utilities for reading GCE Backend configuration."""
6 6
7 import collections 7 import collections
8 import logging 8 import logging
9 9
10 from components import utils 10 from components import utils
11 utils.fix_protobuf_package() 11 utils.fix_protobuf_package()
12 12
13 from google import protobuf 13 from google import protobuf
14 from google.appengine.ext import ndb 14 from google.appengine.ext import ndb
15 15
16 import metrics 16 import metrics
17 17
18 from components import config 18 from components import config
19 from components import datastore_utils 19 from components import datastore_utils
20 from components.config import validation
20 21
21 from proto import config_pb2 22 from proto import config_pb2
22 23
23 24
25 TEMPLATES_CFG_FILENAME = 'templates.cfg'
26 MANAGERS_CFG_FILENAME = 'managers.cfg'
27
28
24 class Configuration(datastore_utils.config.GlobalConfig): 29 class Configuration(datastore_utils.config.GlobalConfig):
25 """Configuration for this service.""" 30 """Configuration for this service."""
26 # Name of the config set in the config service.
27 config_set = ndb.StringProperty()
28 # Text-formatted proto.config_pb2.InstanceTemplateConfig. 31 # Text-formatted proto.config_pb2.InstanceTemplateConfig.
29 template_config = ndb.TextProperty() 32 template_config = ndb.TextProperty()
30 # Text-formatted proto.config_pb2.InstanceGroupManagerConfig. 33 # Text-formatted proto.config_pb2.InstanceGroupManagerConfig.
31 manager_config = ndb.TextProperty() 34 manager_config = ndb.TextProperty()
32 # Revision of the configs. 35 # Revision of the configs.
33 revision = ndb.StringProperty() 36 revision = ndb.StringProperty()
34 37
38 def set_defaults(self):
39 """Sets defaults for Configuration entity."""
40 self.template_config = ''
M-A Ruel 2016/08/12 18:50:25 why not use default='' on each property?
ryanmartens 2016/08/12 21:02:49 Done.
41 self.manager_config = ''
42 self.revision = ''
43
35 @classmethod 44 @classmethod
36 def load(cls): 45 def load(cls):
37 """Loads text-formatted template and manager configs into message.Messages. 46 """Loads text-formatted template and manager configs into message.Messages.
38 47
39 Returns: 48 Returns:
40 A 2-tuple of (InstanceTemplateConfig, InstanceGroupManagerConfig). 49 A 2-tuple of (InstanceTemplateConfig, InstanceGroupManagerConfig).
41 """ 50 """
42 configuration = cls.cached() 51 configuration = cls.cached()
43 template_config = config_pb2.InstanceTemplateConfig() 52 template_cfg = config_pb2.InstanceTemplateConfig()
44 protobuf.text_format.Merge(configuration.template_config, template_config) 53 if configuration.template_config:
45 manager_config = config_pb2.InstanceGroupManagerConfig() 54 protobuf.text_format.Merge(configuration.template_config, template_cfg)
M-A Ruel 2016/08/12 18:50:25 do you want to trap protobuf.text_format.ParseErro
ryanmartens 2016/08/12 21:02:48 Sure. I've now loosely followed auth_service. It'd
46 protobuf.text_format.Merge(configuration.manager_config, manager_config) 55 manager_cfg = config_pb2.InstanceGroupManagerConfig()
47 return template_config, manager_config 56 if configuration.manager_config:
57 protobuf.text_format.Merge(configuration.manager_config, manager_cfg)
58 return template_cfg, manager_cfg
48 59
49 60
50 def update_config(): 61 def update_template_configs():
51 """Updates the local configuration from the config service.""" 62 """Updates the local template configuration from the config service.
52 config_set = Configuration.cached().config_set
53 revision, template_config = config.get(
54 config_set,
55 'templates.cfg',
56 dest_type=config_pb2.InstanceTemplateConfig,
57 )
58 _, manager_config = config.get(
59 config_set,
60 'managers.cfg',
61 dest_type=config_pb2.InstanceGroupManagerConfig,
62 revision=revision,
63 )
64 63
65 # Validity of each config. 64 Ensures that all config files are at the same path revision.
66 configs = { 65 """
67 'managers.cfg': True, 66 template_revision, template_config = config.get_self_config(
68 'templates.cfg': True, 67 TEMPLATES_CFG_FILENAME, config_pb2.InstanceTemplateConfig,
M-A Ruel 2016/08/12 18:50:25 align arguments at +4
ryanmartens 2016/08/12 21:02:48 Done.
69 } 68 store_last_good=True)
69 manager_revision, manager_config = config.get_self_config(
70 MANAGERS_CFG_FILENAME, config_pb2.InstanceGroupManagerConfig,
71 store_last_good=True)
70 72
71 context = config.validation_context.Context.logging() 73 context = config.validation_context.Context.logging()
72 validate_template_config(template_config, context) 74 if template_config:
75 validate_template_config(template_config, context)
73 if context.result().has_errors: 76 if context.result().has_errors:
74 logging.error('Not updating configuration due to errors in templates.cfg') 77 logging.error('Not updating configuration due to errors in templates.cfg')
75 configs['templates.cfg'] = False 78 return
76 79
77 context = config.validation_context.Context.logging() 80 context = config.validation_context.Context.logging()
78 validate_manager_config(manager_config, context) 81 if manager_config:
82 validate_manager_config(manager_config, context)
79 if context.result().has_errors: 83 if context.result().has_errors:
80 logging.error('Not updating configuration due to errors in managers.cfg') 84 logging.error('Not updating configuration due to errors in managers.cfg')
81 configs['managers.cfg'] = False
82
83 for config_name, valid in configs.iteritems():
84 metrics.config_valid.set(valid, fields={'config': config_name})
85
86 if not all(configs.values()):
87 return 85 return
88 86
89 stored_config = Configuration.fetch() 87 if template_revision != manager_revision:
M-A Ruel 2016/08/12 18:50:25 I think this check should be done first (?)
ryanmartens 2016/08/12 21:02:48 Done.
90 if stored_config.revision != revision: 88 logging.error('Not updating configuration due to revision mismatch.')
M-A Ruel 2016/08/12 18:50:25 warning?
ryanmartens 2016/08/12 21:02:48 Done.
91 logging.info('Updating configuration to %s', revision) 89 return
90
91 stored_config = Configuration.cached()
92 if stored_config.revision != template_revision:
M-A Ruel 2016/08/12 18:50:25 No need to validate at every cron job when the sto
ryanmartens 2016/08/12 21:02:48 Done.
93 logging.info('Updating configuration to %s', template_revision)
92 stored_config.modify( 94 stored_config.modify(
93 manager_config=protobuf.text_format.MessageToString(manager_config), 95 manager_config = protobuf.text_format.MessageToString(manager_config),
M-A Ruel 2016/08/12 18:50:25 align at +4 while at it
ryanmartens 2016/08/12 21:02:48 Done.
94 revision=revision, 96 revision = template_revision,
95 template_config=protobuf.text_format.MessageToString(template_config), 97 template_config = protobuf.text_format.MessageToString(template_config),
96 ) 98 )
97 99
98 100
101 @validation.self_rule(TEMPLATES_CFG_FILENAME, config_pb2.InstanceTemplateConfig)
99 def validate_template_config(config, context): 102 def validate_template_config(config, context):
100 """Validates an InstanceTemplateConfig instance.""" 103 """Validates an InstanceTemplateConfig instance."""
101 # We don't do any GCE-specific validation here. Just require globally 104 # We don't do any GCE-specific validation here. Just require globally
102 # unique base name because base name is used as the key in the datastore. 105 # unique base name because base name is used as the key in the datastore.
103 base_names = set() 106 base_names = set()
107 valid = True
104 for template in config.templates: 108 for template in config.templates:
105 if template.base_name in base_names: 109 if template.base_name in base_names:
106 context.error('base_name %s is not globally unique.', template.base_name) 110 context.error('base_name %s is not globally unique.', template.base_name)
111 valid = False
107 else: 112 else:
108 base_names.add(template.base_name) 113 base_names.add(template.base_name)
109 114
115 metrics.config_valid.set(valid, fields={'config': TEMPLATES_CFG_FILENAME})
110 116
117
118 @validation.self_rule(MANAGERS_CFG_FILENAME,
M-A Ruel 2016/08/12 18:50:25 @validation.self_rule( MANAGERS_CFG_FILENAME,
ryanmartens 2016/08/12 21:02:49 Done.
119 config_pb2.InstanceGroupManagerConfig)
111 def validate_manager_config(config, context): 120 def validate_manager_config(config, context):
112 """Validates an InstanceGroupManagerConfig instance.""" 121 """Validates an InstanceGroupManagerConfig instance."""
113 # We don't do any GCE-specific validation here. Just require per-template 122 # We don't do any GCE-specific validation here. Just require per-template
114 # unique zone because template+zone is used as a key in the datastore. 123 # unique zone because template+zone is used as a key in the datastore.
115 zones = collections.defaultdict(set) 124 zones = collections.defaultdict(set)
125 valid = True
116 for manager in config.managers: 126 for manager in config.managers:
117 if manager.zone in zones[manager.template_base_name]: 127 if manager.zone in zones[manager.template_base_name]:
118 context.error( 128 context.error(
119 'zone %s is not unique in template %s.', 129 'zone %s is not unique in template %s.',
120 manager.zone, 130 manager.zone,
121 manager.template_base_name, 131 manager.template_base_name,
122 ) 132 )
133 valid = False
123 else: 134 else:
124 zones[manager.template_base_name].add(manager.zone) 135 zones[manager.template_base_name].add(manager.zone)
136
137 metrics.config_valid.set(valid, fields={'config': MANAGERS_CFG_FILENAME})
OLDNEW
« no previous file with comments | « appengine/gce-backend/README.md ('k') | appengine/gce-backend/config_test.py » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698