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

Side by Side Diff: appengine/swarming/cipd.py

Issue 2267363004: Add CIPD pin reporting to swarming. (Closed) Base URL: https://chromium.googlesource.com/external/github.com/luci/luci-py@master
Patch Set: Address comments Created 4 years, 3 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 | « no previous file | appengine/swarming/cipd_test.py » ('j') | appengine/swarming/handlers_frontend.py » ('J')
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 """CIPD-specific code is concentrated here.""" 5 """CIPD-specific code is concentrated here."""
6 6
7 import contextlib
7 import re 8 import re
8 9
9 # Regular expressions below are copied from 10 # Regular expressions below are copied from
10 # https://chromium.googlesource.com/infra/infra/+/468bb43/appengine/chrome_infra _packages/cipd/impl.py 11 # https://chromium.googlesource.com/infra/infra/+/468bb43/appengine/chrome_infra _packages/cipd/impl.py
11 # https://chromium.googlesource.com/infra/infra/+/468bb43/appengine/chrome_infra _packages/cas/impl.py 12 # https://chromium.googlesource.com/infra/infra/+/468bb43/appengine/chrome_infra _packages/cas/impl.py
12 13
13 PACKAGE_NAME_RE = re.compile(r'^([a-z0-9_\-]+/)*[a-z0-9_\-]+$') 14 PACKAGE_NAME_RE = re.compile(r'^([a-z0-9_\-]+/)*[a-z0-9_\-]+$')
14 INSTANCE_ID_RE = re.compile(r'^[0-9a-f]{40}$') 15 INSTANCE_ID_RE = re.compile(r'^[0-9a-f]{40}$')
15 TAG_KEY_RE = re.compile(r'^[a-z0-9_\-]+$') 16 TAG_KEY_RE = re.compile(r'^[a-z0-9_\-]+$')
16 REF_RE = re.compile(r'^[a-z0-9_\-]{1,100}$') 17 REF_RE = re.compile(r'^[a-z0-9_\-]{1,100}$')
17 TAG_MAX_LEN = 400 18 TAG_MAX_LEN = 400
18 19
19 20
20 # CIPD package name template parameters allow a user to reference different 21 # CIPD package name template parameters allow a user to reference different
21 # packages for different enviroments. Inspired by 22 # packages for different enviroments. Inspired by
22 # https://chromium.googlesource.com/infra/infra/+/f1072a132c68532b548458392c5444 f04386d684/build/README.md 23 # https://chromium.googlesource.com/infra/infra/+/f1072a132c68532b548458392c5444 f04386d684/build/README.md
23 # The values of the parameters are computed on the bot. 24 # The values of the parameters are computed on the bot.
24 # 25 #
25 # Platform parameter value is "<os>-<arch>" string, where 26 # Platform parameter value is "<os>-<arch>" string, where
26 # os can be "linux", "mac" or "windows" and arch can be "386", "amd64" or 27 # os can be "linux", "mac" or "windows" and arch can be "386", "amd64" or
27 # "armv6l". 28 # "armv6l".
28 PARAM_PLATFORM = '${platform}' 29 PARAM_PLATFORM = '${platform}'
30 PARAM_PLATFORM_ESC = re.escape(PARAM_PLATFORM)
29 # OS version parameter defines major and minor version of the OS distribution. 31 # OS version parameter defines major and minor version of the OS distribution.
30 # It is useful if package depends on .dll/.so libraries provided by the OS. 32 # It is useful if package depends on .dll/.so libraries provided by the OS.
31 # Example values: "ubuntu14_04", "mac10_9", "win6_1". 33 # Example values: "ubuntu14_04", "mac10_9", "win6_1".
32 PARAM_OS_VER = '${os_ver}' 34 PARAM_OS_VER = '${os_ver}'
35 PARAM_OS_VER_ESC = re.escape(PARAM_OS_VER)
33 ALL_PARAMS = (PARAM_PLATFORM, PARAM_OS_VER) 36 ALL_PARAMS = (PARAM_PLATFORM, PARAM_OS_VER)
34 37
35 38
39 @contextlib.contextmanager
40 def pin_check_fn(platform=None, os_ver=None):
M-A Ruel 2016/08/30 18:54:12 I'd prefer no default value.
41 """Yields a function that verifies that an input CipdPackage could have been
42 plausibly expanded, via pinning, to another CipdPackage. Repeated invocations
43 of the function will retain knowledge of any resolved name template paramters
44 like ${platform} and ${os_ver}.
45
46 Args:
47 platform - a pre-defined expansion of ${platform}, or None to learn from the
48 first valid checked CipdPackage containing ${platform}.
49 os_ver - a pre-defined expansion of ${os_ver}, or None to learn from the
50 first valid checked CipdPackage containing ${os_ver}.
51
52 Args of yielded function:
53 original - a CipdPackage which may contain template params like ${platform}
54 expanded - a CipdPackage which is nominally an expansion of original.
55
56 CipdPackage is a duck-typed object which has three string properties:
57 'package_name', 'path' and 'version'.
58
59 Yielded function raises:
60 ValueError if expanded is not a valid derivation of original.
61
62 Example:
63 with pin_check_fn() as check:
64 check(CipdPackage('', '${platform}', 'ref'),
65 CipdPackage('', 'windows-amd64', 'deadbeef'*5))
66 check(CipdPackage('', '${platform}', 'ref'),
67 CipdPackage('', 'linux-amd64', 'deadbeef'*5)) ## will raise ValueError
68 """
69 plat_ref = [platform]
70 os_ver_ref = [os_ver]
71 def _check_fn(original, expanded):
72 if original.path != expanded.path:
73 raise ValueError('Mismatched path')
74
75 def sub_param(regex, param_esc, param_re, param_const):
76 # The 1 allows each substitution to only show up in the pattern once.
77 # If it shows up more than once, then this is a very strange package name,
78 # and should be subject to further scrutiny. If it turns out that for some
79 # reason the substitutions SHOULD be allowed more than once per name, we
80 # will need to assert that the matched-values of them are also all the
81 # same (e.g. ${platform} cannot resolve to more than one value)
82 ret = False
83 if param_const is None:
84 ret = param_esc in regex
M-A Ruel 2016/08/30 18:54:12 Then what about: regex.count(param_esc) and check
85 if ret:
86 regex = regex.replace(param_esc, param_re, 1)
87 else:
88 regex = regex.replace(param_esc, param_const, 1)
89 return regex, ret
90
91 name_regex = re.escape(original.package_name)
92 name_regex, scan_plat = sub_param(
93 name_regex, PARAM_PLATFORM_ESC, r'(?P<platform>\w+-[a-z0-9]+)',
94 plat_ref[0])
95 name_regex, scan_os_ver = sub_param(
96 name_regex, PARAM_OS_VER_ESC, r'(?P<os_ver>[_a-z0-9]+)',
97 os_ver_ref[0])
98
99 match = re.match(name_regex, expanded.package_name)
100 if not match:
101 raise ValueError('Mismatched package_name')
102
103 if is_pinned_version(original.version):
104 if original.version != expanded.version:
105 raise ValueError('Mismatched pins')
106 else:
107 if not is_pinned_version(expanded.version):
108 raise ValueError('Pin value is not a pin')
109
110 if scan_plat:
111 plat_ref[0] = re.escape(match.group('platform'))
112 if scan_os_ver:
113 os_ver_ref[0] = re.escape(match.group('os_ver'))
114
115 yield _check_fn
116
117
36 def is_valid_package_name(package_name): 118 def is_valid_package_name(package_name):
37 """Returns True if |package_name| is a valid CIPD package name.""" 119 """Returns True if |package_name| is a valid CIPD package name."""
38 return bool(PACKAGE_NAME_RE.match(package_name)) 120 return bool(PACKAGE_NAME_RE.match(package_name))
39 121
40 122
41 def is_valid_package_name_template(template): 123 def is_valid_package_name_template(template):
42 """Returns True if |package_name| is a valid CIPD package name template.""" 124 """Returns True if |package_name| is a valid CIPD package name template."""
43 # Render known parameters first. 125 # Render known parameters first.
44 for p in ALL_PARAMS: 126 for p in ALL_PARAMS:
45 template = template.replace(p, 'x') 127 template = template.replace(p, 'x')
(...skipping 13 matching lines...) Expand all
59 """True if string looks like a valid package instance tag.""" 141 """True if string looks like a valid package instance tag."""
60 if not tag or ':' not in tag or len(tag) > TAG_MAX_LEN: 142 if not tag or ':' not in tag or len(tag) > TAG_MAX_LEN:
61 return False 143 return False
62 # Care only about the key. Value can be anything (including empty string). 144 # Care only about the key. Value can be anything (including empty string).
63 return bool(TAG_KEY_RE.match(tag.split(':', 1)[0])) 145 return bool(TAG_KEY_RE.match(tag.split(':', 1)[0]))
64 146
65 147
66 def is_pinned_version(version): 148 def is_pinned_version(version):
67 """Returns True if |version| is pinned.""" 149 """Returns True if |version| is pinned."""
68 return bool(INSTANCE_ID_RE.match(version)) or is_valid_tag(version) 150 return bool(INSTANCE_ID_RE.match(version)) or is_valid_tag(version)
OLDNEW
« no previous file with comments | « no previous file | appengine/swarming/cipd_test.py » ('j') | appengine/swarming/handlers_frontend.py » ('J')

Powered by Google App Engine
This is Rietveld 408576698