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

Side by Side Diff: appengine/chrome_infra_packages/cipd/api.py

Issue 816433004: cipd: registerPackage method implementation. (Closed) Base URL: https://chromium.googlesource.com/infra/infra.git@master
Patch Set: Created 5 years, 11 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 # Copyright 2014 The Chromium Authors. All rights reserved.
2 # Use of this source code is governed by a BSD-style license that can be
3 # found in the LICENSE file.
4
5 """Cloud Endpoints API for Package Repository service."""
6
7 import endpoints
8
9 from protorpc import message_types
10 from protorpc import messages
11 from protorpc import remote
12
13 from components import auth
14 from components import utils
15
16 from . import acl
17 from . import impl
18
19
20 # This is used by endpoints indirectly.
21 package = 'cipd'
22
23
24 class InstanceMetadata(messages.Message):
25 """Description of how the package instance was built and registered."""
26 date = messages.StringField(1, required=True)
27 hostname = messages.StringField(2, required=True)
28 user = messages.StringField(3, required=True)
29
30 # Output only fields.
31 registered_by = messages.StringField(4, required=False)
32 registered_ts = messages.IntegerField(5, required=False)
33
34
35 def metadata_from_entity(ent):
36 """PackageInstanceMetadata entity -> InstanceMetadata message."""
37 return InstanceMetadata(
38 date=ent.date,
39 hostname=ent.hostname,
40 user=ent.user,
41 registered_by=ent.registered_by.to_bytes(),
42 registered_ts=utils.datetime_to_timestamp(ent.registered_ts))
43
44
45 def metadata_to_entity(msg):
46 """InstanceMetadata message -> PackageInstanceMetadata entity."""
47 return impl.PackageInstanceMetadata(
48 date=msg.date,
49 hostname=msg.hostname,
50 user=msg.user)
51
52
53 class Signature(messages.Message):
54 """Single signature. Each package instance can have multiple signatures.
55
56 See also SignatureBlock struct in infra/tools/cipd/common.go.
57 """
58 hash_algo = messages.StringField(1, required=True)
59 digest = messages.BytesField(2, required=True)
60 signature_algo = messages.StringField(3, required=True)
61 signature_key = messages.StringField(4, required=True)
62 signature = messages.BytesField(5, required=True)
63
64 # Output only fields.
65 added_by = messages.StringField(6, required=False)
66 added_ts = messages.IntegerField(7, required=False)
67
68
69 def signature_from_entity(ent):
70 """PackageInstanceSignature entity -> Signature message."""
71 return Signature(
72 hash_algo=ent.hash_algo,
73 digest=ent.digest,
74 signature_algo=ent.signature_algo,
75 signature_key=ent.signature_key,
76 signature=ent.signature,
77 added_by=ent.added_by.to_bytes(),
78 added_ts=utils.datetime_to_timestamp(ent.added_ts))
79
80
81 def signature_to_entity(msg):
82 """Signature message -> PackageInstanceSignature entity."""
83 return impl.PackageInstanceSignature(
84 hash_algo=msg.hash_algo,
85 digest=msg.digest,
86 signature_algo=msg.signature_algo,
87 signature_key=msg.signature_key,
88 signature=msg.signature)
89
90
91 class RegisterPackageRequest(messages.Message):
92 """Request to add a new package instance if it is not yet present.
93
94 Instance metadata is recorded only if package instance is not yet present.
95 Signatures are appended to the list of signatures (even for existing package).
96
97 Callers are expected to execute following protocol:
98 1. Attempt to register a package instance by callling registerPackage(msg).
nodir 2014/12/30 22:54:00 typo: calllling
Vadim Sh. 2014/12/31 01:27:35 Done.
99 2. On UPLOAD_FIRST response, upload package data and finalize the upload by
100 using upload_session_id and upload_url and calling cas.finishUpload.
101 3. Once upload is finalized, call registerPackage(msg) again.
102 """
103 package_name = messages.StringField(1, required=True)
104 instance_id = messages.StringField(2, required=True)
105 metadata = messages.MessageField(InstanceMetadata, 3, required=True)
106 signatures = messages.MessageField(Signature, 4, repeated=True)
107
108
109 class RegisterPackageResponse(messages.Message):
110 """Results of registerPackage call.
111
112 upload_session_id and upload_url (if present) can be used with CAS service
113 (finishUpload call in particular).
114 """
115 class Status(messages.Enum):
nodir 2014/12/30 22:54:00 Blank line after """
Vadim Sh. 2014/12/31 01:27:35 Done.
116 # Package instance successfully registered.
117 REGISTERED = 1
118 # Such package instance already exists. It is not an error.
119 ALREADY_REGISTERED = 2
120 # Package data has to be upload to CAS first.
121 UPLOAD_FIRST = 3
122 # Some unexpected fatal error happened.
123 ERROR = 4
124
125 # Status of this operation, defines what other fields to expect.
126 status = messages.EnumField(
127 'RegisterPackageResponse.Status', 1, required=True)
nodir 2014/12/30 22:54:00 Why not EnumField(Status, ...) ?
Vadim Sh. 2014/12/31 01:27:35 Done. For some reason I though it does like local
128
129 # For REGISTERED or ALREADY_REGISTERED a current metadata of package instance.
130 metadata = messages.MessageField(InstanceMetadata, 2, required=False)
131
132 # For UPLOAD_FIRST status, a unique identifier of the upload operation.
133 upload_session_id = messages.StringField(3, required=False)
134 # For UPLOAD_FIRST status, URL to PUT file to via resumable upload protocol.
135 upload_url = messages.StringField(4, required=False)
136
137 # For ERROR status, a error message.
138 error_message = messages.StringField(5, required=False)
139
140
141 @auth.endpoints_api(
142 name='repo',
143 version='v1',
144 title='Package Repository API')
145 class PackageRepositoryApi(remote.Service):
146 """Package Repository API."""
147
148 @auth.endpoints_method(
149 RegisterPackageRequest,
150 RegisterPackageResponse,
151 http_method='POST',
152 name='registerPackage')
153 @auth.require(lambda: not auth.get_current_identity().is_anonymous)
154 def register_package(self, request):
155 """Registers a new package instance in the repository."""
156 if not impl.is_valid_package_name(request.package_name):
157 raise endpoints.BadRequestException('Invalid package name')
158 if not impl.is_valid_instance_id(request.instance_id):
159 raise endpoints.BadRequestException('Invalid instance ID')
nodir 2014/12/30 22:54:00 Consider returning different error reasons. A prog
Vadim Sh. 2014/12/31 01:27:35 Client will do client side validation. Checks here
160
nodir 2014/12/30 22:54:00 metadata is not validated? To me, either it should
Vadim Sh. 2014/12/31 01:27:35 I was planing to add very few required FYI only fi
nodir 2015/01/02 19:13:03 Acknowledged.
161 caller = auth.get_current_identity()
162 if not acl.can_register_package(request.package_name, caller):
163 raise auth.AuthorizationError()
164
165 service = impl.get_repo_service()
166 if service is None:
167 raise endpoints.InternalServerErrorException('Service is not configured')
168
169 # Metadata proto -> entity.
170 metadata = metadata_to_entity(request.metadata)
171 metadata.registered_by = caller
172 metadata.registered_ts = utils.utcnow()
173
174 # Signature list proto -> entity.
175 signatures = []
176 for sig in request.signatures:
177 ent = signature_to_entity(sig)
178 ent.added_by = caller
179 ent.added_ts = utils.utcnow()
nodir 2014/12/30 22:54:00 call utcnow once per request
Vadim Sh. 2014/12/31 01:27:35 Done.
180 signatures.append(ent)
181
182 # Already registered? Just attach any new signatures.
183 pkg = service.get_instance(request.package_name, request.instance_id)
184 if pkg is not None:
185 service.add_signatures(
186 request.package_name, request.instance_id, signatures)
nodir 2014/12/30 22:54:00 I think you should not add signatures
Vadim Sh. 2014/12/31 01:27:35 Then there's no way to resign a package. I mean it
187 return RegisterPackageResponse(
188 status=RegisterPackageResponse.Status.ALREADY_REGISTERED,
189 metadata=metadata_from_entity(pkg.metadata))
190
191 # Need to upload to CAS first? Open an upload session. Caller must use
192 # CASServiceApi to finish the upload and then call registerPackage again.
193 if not service.is_data_uploaded(request.package_name, request.instance_id):
194 upload_url, upload_session_id = service.create_upload_session(
Vadim Sh. 2014/12/30 02:22:26 It makes 'cas' service and 'repo' service a bit ta
195 request.package_name, request.instance_id, caller)
196 return RegisterPackageResponse(
197 status=RegisterPackageResponse.Status.UPLOAD_FIRST,
198 upload_session_id=upload_session_id,
199 upload_url=upload_url)
200
201 # Package data is in the store. Make an entity.
202 pkg, registered = service.register_instance(
203 request.package_name, request.instance_id, metadata, signatures)
204 if registered:
205 status = RegisterPackageResponse.Status.REGISTERED
206 else: # pragma: no cover
207 status = RegisterPackageResponse.Status.ALREADY_REGISTERED
208 return RegisterPackageResponse(
209 status=status,
210 metadata=metadata_from_entity(pkg.metadata))
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698