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

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: Refactors GCE backend config updates. 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(default='')
30 # Text-formatted proto.config_pb2.InstanceGroupManagerConfig. 33 # Text-formatted proto.config_pb2.InstanceGroupManagerConfig.
31 manager_config = ndb.TextProperty() 34 manager_config = ndb.TextProperty(default='')
32 # Revision of the configs. 35 # Revision of the configs.
33 revision = ndb.StringProperty() 36 revision = ndb.StringProperty(default='')
34 37
35 @classmethod 38 @classmethod
36 def load(cls): 39 def load(cls):
37 """Loads text-formatted template and manager configs into message.Messages. 40 """Loads text-formatted template and manager configs into message.Messages.
38 41
39 Returns: 42 Returns:
40 A 2-tuple of (InstanceTemplateConfig, InstanceGroupManagerConfig). 43 A 2-tuple of (InstanceTemplateConfig, InstanceGroupManagerConfig).
41 """ 44 """
45 def _adds_cfg_to_message(name, text_cfg, proto_cfg):
46 try:
47 protobuf.text_format.Merge(text_cfg, proto_cfg)
48 except protobuf.text_format.ParseError as ex:
49 logging.error('Invalid %s: %s', name, ex)
50 raise ValueError(ex)
51 return proto_cfg
52
42 configuration = cls.cached() 53 configuration = cls.cached()
43 template_config = config_pb2.InstanceTemplateConfig() 54 template_cfg = _adds_cfg_to_message(
44 protobuf.text_format.Merge(configuration.template_config, template_config) 55 'template.cfg', configuration.template_config,
45 manager_config = config_pb2.InstanceGroupManagerConfig() 56 config_pb2.InstanceTemplateConfig()
46 protobuf.text_format.Merge(configuration.manager_config, manager_config) 57 )
47 return template_config, manager_config 58 manager_cfg = _adds_cfg_to_message(
59 'manager.cfg', configuration.manager_config,
60 config_pb2.InstanceGroupManagerConfig()
61 )
62 return template_cfg, manager_cfg
48 63
49 64
50 def update_config(): 65 def update_template_configs():
51 """Updates the local configuration from the config service.""" 66 """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 67
65 # Validity of each config. 68 Ensures that all config files are at the same path revision.
66 configs = { 69 """
67 'managers.cfg': True, 70 template_revision, template_config = config.get_self_config(
smut 2016/08/12 21:34:56 You can't get_self_config because the GCE Backend'
M-A Ruel 2016/08/12 23:16:02 I'm confused, this kind of difference, where the G
smut 2016/08/12 23:22:44 This has nothing to do with the GCE hosting projec
Vadim Sh. 2016/08/12 23:54:24 I think it's safe to change get_self_config to alw
ryanmartens 2016/08/15 13:57:05 I went with the last solution proposed and trimmed
68 'templates.cfg': True, 71 TEMPLATES_CFG_FILENAME, config_pb2.InstanceTemplateConfig,
69 } 72 store_last_good=True)
73 manager_revision, manager_config = config.get_self_config(
74 MANAGERS_CFG_FILENAME, config_pb2.InstanceGroupManagerConfig,
75 store_last_good=True)
70 76
71 context = config.validation_context.Context.logging() 77 if template_revision != manager_revision:
72 validate_template_config(template_config, context) 78 logging.error('Not updating configuration due to revision mismatch.')
73 if context.result().has_errors:
74 logging.error('Not updating configuration due to errors in templates.cfg')
75 configs['templates.cfg'] = False
76
77 context = config.validation_context.Context.logging()
78 validate_manager_config(manager_config, context)
79 if context.result().has_errors:
80 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 79 return
88 80
89 stored_config = Configuration.fetch() 81 stored_config = Configuration.cached()
90 if stored_config.revision != revision: 82 if stored_config.revision != template_revision:
91 logging.info('Updating configuration to %s', revision) 83 context = config.validation_context.Context.logging()
84 if template_config:
85 validate_template_config(template_config, context)
86 if context.result().has_errors:
87 logging.warning(
88 'Not updating configuration due to errors in templates.cfg')
89 return
90
91 context = config.validation_context.Context.logging()
92 if manager_config:
93 validate_manager_config(manager_config, context)
94 if context.result().has_errors:
95 logging.error('Not updating configuration due to errors in managers.cfg')
96 return
97
98 logging.info('Updating configuration to %s', template_revision)
92 stored_config.modify( 99 stored_config.modify(
93 manager_config=protobuf.text_format.MessageToString(manager_config), 100 manager_config = protobuf.text_format.MessageToString(manager_config),
94 revision=revision, 101 revision = template_revision,
95 template_config=protobuf.text_format.MessageToString(template_config), 102 template_config = protobuf.text_format.MessageToString(template_config),
96 ) 103 )
97 104
98 105
106 @validation.self_rule(TEMPLATES_CFG_FILENAME, config_pb2.InstanceTemplateConfig)
99 def validate_template_config(config, context): 107 def validate_template_config(config, context):
100 """Validates an InstanceTemplateConfig instance.""" 108 """Validates an InstanceTemplateConfig instance."""
101 # We don't do any GCE-specific validation here. Just require globally 109 # 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. 110 # unique base name because base name is used as the key in the datastore.
103 base_names = set() 111 base_names = set()
112 valid = True
104 for template in config.templates: 113 for template in config.templates:
105 if template.base_name in base_names: 114 if template.base_name in base_names:
106 context.error('base_name %s is not globally unique.', template.base_name) 115 context.error('base_name %s is not globally unique.', template.base_name)
116 valid = False
107 else: 117 else:
108 base_names.add(template.base_name) 118 base_names.add(template.base_name)
109 119
120 metrics.config_valid.set(valid, fields={'config': TEMPLATES_CFG_FILENAME})
110 121
122
123 @validation.self_rule(
124 MANAGERS_CFG_FILENAME, config_pb2.InstanceGroupManagerConfig)
111 def validate_manager_config(config, context): 125 def validate_manager_config(config, context):
112 """Validates an InstanceGroupManagerConfig instance.""" 126 """Validates an InstanceGroupManagerConfig instance."""
113 # We don't do any GCE-specific validation here. Just require per-template 127 # 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. 128 # unique zone because template+zone is used as a key in the datastore.
115 zones = collections.defaultdict(set) 129 zones = collections.defaultdict(set)
130 valid = True
116 for manager in config.managers: 131 for manager in config.managers:
117 if manager.zone in zones[manager.template_base_name]: 132 if manager.zone in zones[manager.template_base_name]:
118 context.error( 133 context.error(
119 'zone %s is not unique in template %s.', 134 'zone %s is not unique in template %s.',
120 manager.zone, 135 manager.zone,
121 manager.template_base_name, 136 manager.template_base_name,
122 ) 137 )
138 valid = False
123 else: 139 else:
124 zones[manager.template_base_name].add(manager.zone) 140 zones[manager.template_base_name].add(manager.zone)
141
142 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