| OLD | NEW |
| 1 # Copyright 2015 The LUCI Authors. All rights reserved. | 1 # Copyright 2015 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 """This module facilitates conversion from dictionaries to ProtoRPC messages. | 5 """This module facilitates conversion from dictionaries to ProtoRPC messages. |
| 6 | 6 |
| 7 Given a dictionary whose keys' names and values' types comport with the | 7 Given a dictionary whose keys' names and values' types comport with the |
| 8 fields defined for a protorpc.messages.Message subclass, this module tries to | 8 fields defined for a protorpc.messages.Message subclass, this module tries to |
| 9 generate a Message instance that corresponds to the provided dictionary. The | 9 generate a Message instance that corresponds to the provided dictionary. The |
| 10 "normal" use case is for ndb.Models which need to be represented as a | 10 "normal" use case is for ndb.Models which need to be represented as a |
| 11 ProtoRPC. | 11 ProtoRPC. |
| 12 """ | 12 """ |
| 13 | 13 |
| 14 import datetime | 14 import datetime |
| 15 import json | 15 import json |
| 16 | 16 |
| 17 import swarming_rpcs | 17 import swarming_rpcs |
| 18 | 18 |
| 19 from components import utils | 19 from components import utils |
| 20 from server import task_pack | 20 from server import task_pack |
| 21 from server import task_request | 21 from server import task_request |
| 22 from server import task_result | 22 from server import task_result |
| 23 | 23 |
| 24 | 24 |
| 25 ### Private API. | 25 ### Private API. |
| 26 | 26 |
| 27 | 27 |
| 28 def _string_pairs_from_dict(dictionary): | 28 def _string_pairs_from_list_of_tuples(dictionary): |
| 29 return [ | 29 return [ |
| 30 swarming_rpcs.StringPair(key=k, value=v) | 30 swarming_rpcs.StringPair(key=k, value=v) |
| 31 for k, v in sorted((dictionary or {}).iteritems()) | 31 for k, v in sorted((dictionary or [])) |
| 32 ] | 32 ] |
| 33 | 33 |
| 34 | 34 |
| 35 def _string_pairs_from_dict(dictionary): |
| 36 return _string_pairs_from_list_of_tuples((dictionary or {}).iteritems()) |
| 37 |
| 38 |
| 35 def _string_list_pairs_from_dict(dictionary): | 39 def _string_list_pairs_from_dict(dictionary): |
| 36 return [ | 40 return [ |
| 37 swarming_rpcs.StringListPair(key=k, value=v) | 41 swarming_rpcs.StringListPair(key=k, value=v) |
| 38 for k, v in sorted((dictionary or {}).iteritems()) | 42 for k, v in sorted((dictionary or {}).iteritems()) |
| 39 ] | 43 ] |
| 40 | 44 |
| 41 | 45 |
| 42 def _ndb_to_rpc(cls, entity, **overrides): | 46 def _ndb_to_rpc(cls, entity, **overrides): |
| 43 members = (f.name for f in cls.all_fields()) | 47 members = (f.name for f in cls.all_fields()) |
| 44 kwargs = {m: getattr(entity, m) for m in members if not m in overrides} | 48 kwargs = {m: getattr(entity, m) for m in members if not m in overrides} |
| (...skipping 73 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 118 inputs_ref = _ndb_to_rpc( | 122 inputs_ref = _ndb_to_rpc( |
| 119 swarming_rpcs.FilesRef, entity.properties.inputs_ref) | 123 swarming_rpcs.FilesRef, entity.properties.inputs_ref) |
| 120 | 124 |
| 121 props = entity.properties | 125 props = entity.properties |
| 122 properties = _ndb_to_rpc( | 126 properties = _ndb_to_rpc( |
| 123 swarming_rpcs.TaskProperties, | 127 swarming_rpcs.TaskProperties, |
| 124 props, | 128 props, |
| 125 caches=[_ndb_to_rpc(swarming_rpcs.CacheEntry, c) for c in props.caches], | 129 caches=[_ndb_to_rpc(swarming_rpcs.CacheEntry, c) for c in props.caches], |
| 126 cipd_input=cipd_input, | 130 cipd_input=cipd_input, |
| 127 secret_bytes='<REDACTED>' if props.has_secret_bytes else None, | 131 secret_bytes='<REDACTED>' if props.has_secret_bytes else None, |
| 128 dimensions=_string_pairs_from_dict(props.dimensions), | 132 dimensions=_string_pairs_from_list_of_tuples(props.dimensions), |
| 129 env=_string_pairs_from_dict(props.env), | 133 env=_string_pairs_from_dict(props.env), |
| 130 inputs_ref=inputs_ref) | 134 inputs_ref=inputs_ref) |
| 131 | 135 |
| 132 return _ndb_to_rpc( | 136 return _ndb_to_rpc( |
| 133 swarming_rpcs.TaskRequest, | 137 swarming_rpcs.TaskRequest, |
| 134 entity, | 138 entity, |
| 135 authenticated=entity.authenticated.to_bytes(), | 139 authenticated=entity.authenticated.to_bytes(), |
| 136 properties=properties) | 140 properties=properties) |
| 137 | 141 |
| 138 | 142 |
| (...skipping 25 matching lines...) Expand all Loading... |
| 164 ]) | 168 ]) |
| 165 | 169 |
| 166 inputs_ref = None | 170 inputs_ref = None |
| 167 if props.inputs_ref: | 171 if props.inputs_ref: |
| 168 inputs_ref = _rpc_to_ndb(task_request.FilesRef, props.inputs_ref) | 172 inputs_ref = _rpc_to_ndb(task_request.FilesRef, props.inputs_ref) |
| 169 | 173 |
| 170 secret_bytes = None | 174 secret_bytes = None |
| 171 if props.secret_bytes: | 175 if props.secret_bytes: |
| 172 secret_bytes = task_request.SecretBytes(secret_bytes=props.secret_bytes) | 176 secret_bytes = task_request.SecretBytes(secret_bytes=props.secret_bytes) |
| 173 | 177 |
| 174 if len(set(i.key for i in props.dimensions)) != len(props.dimensions): | 178 # keys with ':' would corrupt the data. Rest of validation is done in |
| 175 raise ValueError('same dimension key cannot be specified twice') | 179 # TaskProperties. |
| 180 if any(':' in i.key for i in props.dimensions): |
| 181 raise ValueError('dimension key cannot contain ":"') |
| 182 # Repeated keys would be lost. Rest of validation is done in TaskProperties. |
| 176 if len(set(i.key for i in props.env)) != len(props.env): | 183 if len(set(i.key for i in props.env)) != len(props.env): |
| 177 raise ValueError('same environment variable key cannot be specified twice') | 184 raise ValueError('same environment variable key cannot be specified twice') |
| 178 | 185 |
| 179 properties = _rpc_to_ndb( | 186 properties = _rpc_to_ndb( |
| 180 task_request.TaskProperties, | 187 task_request.TaskProperties, |
| 181 props, | 188 props, |
| 182 caches=[_rpc_to_ndb(task_request.CacheEntry, c) for c in props.caches], | 189 caches=[_rpc_to_ndb(task_request.CacheEntry, c) for c in props.caches], |
| 183 cipd_input=cipd_input, | 190 cipd_input=cipd_input, |
| 184 # Passing command=None is supported at API level but not at NDB level. | 191 # Passing command=None is supported at API level but not at NDB level. |
| 185 command=props.command or [], | 192 command=props.command or [], |
| 186 has_secret_bytes=secret_bytes is not None, | 193 has_secret_bytes=secret_bytes is not None, |
| 187 secret_bytes=None, # ignore this, it's handled out of band | 194 secret_bytes=None, # ignore this, it's handled out of band |
| 188 dimensions=None, | 195 dimensions=None, |
| 189 dimensions_dict={i.key: i.value for i in props.dimensions}, | 196 dimensions_flat=sorted( |
| 190 dimensions_flat=[], | 197 u'%s:%s' % (i.key, i.value) for i in props.dimensions), |
| 191 env={i.key: i.value for i in props.env}, | 198 env={i.key: i.value for i in props.env}, |
| 192 inputs_ref=inputs_ref) | 199 inputs_ref=inputs_ref) |
| 193 | 200 |
| 194 req = _rpc_to_ndb( | 201 req = _rpc_to_ndb( |
| 195 task_request.TaskRequest, | 202 task_request.TaskRequest, |
| 196 msg, | 203 msg, |
| 197 created_ts=now, | 204 created_ts=now, |
| 198 expiration_ts=now+datetime.timedelta(seconds=msg.expiration_secs), | 205 expiration_ts=now+datetime.timedelta(seconds=msg.expiration_secs), |
| 199 # It is set in task_request.init_new_request(). | 206 # It is set in task_request.init_new_request(). |
| 200 authenticated=None, | 207 authenticated=None, |
| (...skipping 56 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 257 kwargs['run_id'] = entity.task_id | 264 kwargs['run_id'] = entity.task_id |
| 258 else: | 265 else: |
| 259 assert entity.__class__ is task_result.TaskResultSummary, entity | 266 assert entity.__class__ is task_result.TaskResultSummary, entity |
| 260 kwargs['properties_hash'] = ( | 267 kwargs['properties_hash'] = ( |
| 261 entity.properties_hash.encode('hex') | 268 entity.properties_hash.encode('hex') |
| 262 if entity.properties_hash else None) | 269 if entity.properties_hash else None) |
| 263 # This returns the right value for deduped tasks too. | 270 # This returns the right value for deduped tasks too. |
| 264 k = entity.run_result_key | 271 k = entity.run_result_key |
| 265 kwargs['run_id'] = task_pack.pack_run_result_key(k) if k else None | 272 kwargs['run_id'] = task_pack.pack_run_result_key(k) if k else None |
| 266 return _ndb_to_rpc(swarming_rpcs.TaskResult, entity, **kwargs) | 273 return _ndb_to_rpc(swarming_rpcs.TaskResult, entity, **kwargs) |
| OLD | NEW |