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

Side by Side Diff: infra/tools/master_manager_launcher/desired_state_parser.py

Issue 1128783003: Add master_manager_launch script which launches master_manager scripts for each master on a host. (Closed) Base URL: https://chromium.googlesource.com/infra/infra.git@master
Patch Set: Add coverage. Created 5 years, 7 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
OLDNEW
(Empty)
1 #!/usr/bin/python
2 # Copyright 2015 Google Inc. All Rights Reserved.
3 # pylint: disable=F0401
4
5 """Parse, validate and query the desired master state json."""
6
7 import bisect
8 import json
9 import logging
10 import operator
11 import os
12
13 from infra.libs.buildbot import master
14 from infra.libs.time_functions import timestamp
15 from infra.services.master_lifecycle import buildbot_state
16
17
18 LOGGER = logging.getLogger(__name__)
19
20
21 def load_desired_state_file(filename): # pragma: no cover
agable 2015/05/06 23:29:30 Feels odd. Not finding the file is exception-worth
ghost stip (do not use) 2015/05/07 06:50:57 Done.
22 with open(filename) as f:
23 desired_state = json.load(f)
24 if not desired_master_state_is_valid(desired_state):
25 return None
26 return desired_state
27
28
29 def desired_master_state_is_valid(desired_state):
30 """Verify that the desired_master_state file is valid."""
31 now = timestamp.utcnow_ts()
32
33 for mastername, states in desired_state.iteritems():
34 # Verify desired_state and transition_time_utc are present.
35 for k in ('desired_state', 'transition_time_utc'):
36 if not all(k in state for state in states):
37 LOGGER.error(
38 'one or more states for master %s do not contain %s', mastername, k)
39 return False
40
41 # Verify the list is properly sorted.
42 sorted_states = sorted(
agable 2015/05/06 23:29:30 Why does it matter if they're sorted in the file w
ghost stip (do not use) 2015/05/07 06:50:57 Because a human editing / reading the file would b
agable 2015/05/07 17:59:31 Fair enough, that makes sense.
43 states, key=operator.itemgetter('transition_time_utc'))
44 if sorted_states != states:
45 LOGGER.error('master %s does not have states sorted by timestamp',
46 mastername)
47 LOGGER.error('should be:\n%s', json.dumps(sorted_states, indent=2))
48 return False
49
50 # Verify desired_state and timestamp are valid.
51 for state in states:
52 if (state['desired_state'] not in
53 buildbot_state.STATES['desired_buildbot_state']):
54 LOGGER.error(
55 'desired_state \'%s\' is not one of %s',
56 state['desired_state'],
57 buildbot_state.STATES['desired_buildbot_state'])
58 return False
59
60 if not isinstance(state['transition_time_utc'], (int, float)):
61 LOGGER.error(
62 'transition_time_utc \'%s\' is not an int or float',
63 state['transition_time_utc'])
64 return False
65
66 # Verify there is at least one state in the past.
67 if not get_master_state(states, now=now):
agable 2015/05/06 23:29:30 Why? Why can't I create the file containing just a
iannucci 2015/05/06 23:48:05 Because then the script doesn't know what state it
ghost stip (do not use) 2015/05/07 06:50:57 Right, this is a discussion Robbie and I had in mo
agable 2015/05/07 17:59:31 I don't see how my proposal goes against your disc
ghost stip (do not use) 2015/05/07 19:49:39 There is only one case where a person who wants to
agable 2015/05/08 00:14:53 Case 1.5: The file used to contain a bunch of entr
ghost stip (do not use) 2015/05/08 00:22:13 You can't clean it out for readability fully, beca
68 LOGGER.error(
69 'master %s does not have a state older than %s', mastername, now)
70 return False
71
72 return True
73
74
75 def get_master_state(states, now=None):
76 """Returns the latest state earlier than the current (or specified) time.
77
78 If there are three items, each with transition times of 100, 200 and 300:
79 * calling when 'now' is 50 will return None
80 * calling when 'now' is 150 will return the first item
81 * calling when 'now' is 400 will return the third item
82 """
83 now = now or timestamp.utcnow_ts()
84
85 times = [x['transition_time_utc'] for x in states]
86 index = bisect.bisect_left(times, now)
87 if index:
88 return states[index - 1]
89 return None
90
91
92 def get_masters_for_host(desired_state, build_dir, hostname):
93 """Identify which masters on this host should be managed.
94
95 Returns two lists: triggered_masters and ignored_masters.
agable 2015/05/06 23:29:30 Any reason they're sets instead of lists? Ordering
ghost stip (do not use) 2015/05/07 06:50:57 TypeError: unhashable type: 'dict' ignored_master
96
97 triggered_masters is a list of dicts. Each dict is the full dict from
98 mastermap.py with two extra keys: 'fulldir' (the absolute path to the master
99 directory), and 'states' (a list of desired states sorted by transition time,
100 pulled from the desired states file).
101
102 ignored_masters is merely a list of 'dirname' strings (ex: master.chromium).
agable 2015/05/06 23:29:30 Docstring should explain why they're ignored
ghost stip (do not use) 2015/05/07 06:50:57 Done.
103 """
104 triggered_masters = []
105 ignored_masters = []
106 for master_dict in master.get_mastermap_for_host(
107 build_dir, hostname):
108 if master_dict['dirname'] in desired_state:
109 if master_dict['internal']:
110 master_dir = os.path.abspath(os.path.join(
111 build_dir, os.pardir, 'build_internal', 'masters',
112 master_dict['dirname']))
113 else:
114 master_dir = os.path.abspath(os.path.join(
115 build_dir, 'masters', master_dict['dirname']))
116 master_dict['fulldir'] = master_dir
117 master_dict['states'] = desired_state[master_dict['dirname']]
118
119 triggered_masters.append(master_dict)
120 else:
121 ignored_masters.append(master_dict['dirname'])
122 return triggered_masters, ignored_masters
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698