def schedule_delete_from_backend(uri, conf, context, image_id, **kwargs): """ Given a uri and a time, schedule the deletion of an image. """ conf.register_opts(delete_opts) if not conf.delayed_delete: registry.update_image_metadata(context, image_id, {'status': 'deleted'}) try: return delete_from_backend(uri, **kwargs) except (UnsupportedBackend, exception.NotFound): msg = _("Failed to delete image from store (%(uri)s).") % locals() logger.error(msg) datadir = get_scrubber_datadir(conf) delete_time = time.time() + conf.scrub_time file_path = os.path.join(datadir, str(image_id)) utils.safe_mkdirs(datadir) if os.path.exists(file_path): msg = _("Image id %(image_id)s already queued for delete") % { 'image_id': image_id} raise exception.Duplicate(msg) with open(file_path, 'w') as f: f.write('\n'.join([uri, str(int(delete_time))])) os.chmod(file_path, 0600) os.utime(file_path, (delete_time, delete_time)) registry.update_image_metadata(context, image_id, {'status': 'pending_delete'})
def schedule_delete_from_backend(uri, conf, context, image_id, **kwargs): """ Given a uri and a time, schedule the deletion of an image. """ conf.register_opts(delete_opts) if not conf.delayed_delete: registry.update_image_metadata(context, image_id, {'status': 'deleted'}) try: return delete_from_backend(uri, **kwargs) except (UnsupportedBackend, exception.NotFound): msg = _("Failed to delete image from store (%(uri)s).") % locals() logger.error(msg) datadir = get_scrubber_datadir(conf) delete_time = time.time() + conf.scrub_time file_path = os.path.join(datadir, str(image_id)) utils.safe_mkdirs(datadir) if os.path.exists(file_path): msg = _("Image id %(image_id)s already queued for delete") % { 'image_id': image_id } raise exception.Duplicate(msg) with open(file_path, 'w') as f: f.write('\n'.join([uri, str(int(delete_time))])) os.chmod(file_path, 0600) os.utime(file_path, (delete_time, delete_time)) registry.update_image_metadata(context, image_id, {'status': 'pending_delete'})
def _kill(self, req, image_id): """ Marks the image status to `killed`. :param req: The WSGI/Webob Request object :param image_id: Opaque image identifier """ registry.update_image_metadata(req.context, image_id, {'status': 'killed'})
def _activate(self, req, image_id, location): """ Sets the image status to `active` and the image's location attribute. :param req: The WSGI/Webob Request object :param image_id: Opaque image identifier :param location: Location of where Tank stored this image """ image_meta = {} image_meta['location'] = location image_meta['status'] = 'active' return registry.update_image_metadata(req.context, image_id, image_meta)
def update(self, req, id, image_meta, image_data): """ Updates an existing image with the registry. :param request: The WSGI/Webob Request object :param id: The opaque image identifier :retval Returns the updated image information as a mapping """ if req.context.read_only: msg = _("Read-only access") logger.debug(msg) raise HTTPForbidden(msg, request=req, content_type="text/plain") orig_image_meta = self.get_image_meta_or_404(req, id) orig_status = orig_image_meta['status'] # The default behaviour for a PUT /images/<IMAGE_ID> is to # override any properties that were previously set. This, however, # leads to a number of issues for the common use case where a caller # registers an image with some properties and then almost immediately # uploads an image file along with some more properties. Here, we # check for a special header value to be false in order to force # properties NOT to be purged. However we also disable purging of # properties if an image file is being uploaded... purge_props = req.headers.get('x-tank-registry-purge-props', True) purge_props = (utils.bool_from_string(purge_props) and image_data is None) if image_data is not None and orig_status != 'queued': raise HTTPConflict(_("Cannot upload to an unqueued image")) try: image_meta = registry.update_image_metadata(req.context, id, image_meta, purge_props) if image_data is not None: image_meta = self._upload_and_activate(req, image_meta) except exception.Invalid, e: msg = (_("Failed to update image metadata. Got error: %(e)s") % locals()) for line in msg.split('\n'): logger.error(line) self.notifier.error('image.update', msg) raise HTTPBadRequest(msg, request=req, content_type="text/plain")
def _upload(self, req, image_meta): """ Uploads the payload of the request to a backend store in Tank. If the `x-image-meta-store` header is set, Tank will attempt to use that store, if not, Tank will use the store set by the flag `default_store`. :param req: The WSGI/Webob Request object :param image_meta: Mapping of metadata about image :raises HTTPConflict if image already exists :retval The location where the image was stored """ try: req.get_content_type('application/octet-stream') except exception.InvalidContentType: self._safe_kill(req, image_meta['id']) msg = _("Content-Type must be application/octet-stream") logger.error(msg) raise HTTPBadRequest(explanation=msg) store_name = req.headers.get('x-image-meta-store', self.conf.default_store) store = self.get_store_or_400(req, store_name) image_id = image_meta['id'] logger.debug(_("Setting image %s to status 'saving'"), image_id) registry.update_image_metadata(req.context, image_id, {'status': 'saving'}) try: logger.debug(_("Uploading image data for image %(image_id)s " "to %(store_name)s store"), locals()) if req.content_length: image_size = int(req.content_length) elif 'x-image-meta-size' in req.headers: image_size = int(req.headers['x-image-meta-size']) else: logger.debug(_("Got request with no content-length and no " "x-image-meta-size header")) image_size = 0 location, size, checksum = store.add(image_meta['id'], req.body_file, image_size) # Verify any supplied checksum value matches checksum # returned from store when adding image supplied_checksum = image_meta.get('checksum') if supplied_checksum and supplied_checksum != checksum: msg = _("Supplied checksum (%(supplied_checksum)s) and " "checksum generated from uploaded image " "(%(checksum)s) did not match. Setting image " "status to 'killed'.") % locals() logger.error(msg) self._safe_kill(req, image_id) raise HTTPBadRequest(msg, content_type="text/plain", request=req) # Update the database with the checksum returned # from the backend store logger.debug(_("Updating image %(image_id)s data. " "Checksum set to %(checksum)s, size set " "to %(size)d"), locals()) registry.update_image_metadata(req.context, image_id, {'checksum': checksum, 'size': size}) self.notifier.info('image.upload', image_meta) return location except exception.Duplicate, e: msg = _("Attempt to upload duplicate image: %s") % e logger.error(msg) self._safe_kill(req, image_id) self.notifier.error('image.upload', msg) raise HTTPConflict(msg, request=req)