As per AWS spec (https://docs.aws.amazon.com/AmazonS3/latest/API/API_RestoreObject.html),
if a `restore-object` request is re-issued on already restored copy, server needs to
update restoration period relative to the current time. These changes handles the same.
Note: this applies to only temporary restored copies
Signed-off-by: Soumya Koduri <skoduri@redhat.com>
This will restore the object ``doc1.rtf`` at an optional version,
for the duration of 10 days.
+.. note:: The restoration period of these temporary copies can be updated by reissuing the request with a new period.
+
+
Example 2:
.. prompt:: bash $
real_time delete_at = real_time();
if (days) { //temp copy; do not change mtime and set expiry date
- int expiry_days = days.value();
- constexpr int32_t secs_in_a_day = 24 * 60 * 60;
ceph::real_time expiration_date ;
+ (void)restore->get_expiration_date(dpp, days.value(), expiration_date);
- if (cct->_conf->rgw_restore_debug_interval > 0) {
- expiration_date = restore_time + make_timespan(double(expiry_days)*cct->_conf->rgw_restore_debug_interval);
- ldpp_dout(dpp, 20) << "Setting expiration time to rgw_restore_debug_interval: " << double(expiry_days)*cct->_conf->rgw_restore_debug_interval << ", days:" << expiry_days << dendl;
- } else {
- expiration_date = restore_time + make_timespan(double(expiry_days) * secs_in_a_day);
- }
delete_at = expiration_date;
+ ldpp_dout(dpp, 5) << "Setting Restore expiration time to: " << expiration_date << " , restore_time: " << restore_time << ", restore_interval: " << cct->_conf->rgw_restore_debug_interval << dendl;
{
bufferlist bl;
encode(expiration_date, bl);
attrs[RGW_ATTR_RESTORE_EXPIRY_DATE] = std::move(bl);
+ attrs[RGW_ATTR_DELETE_AT] = attrs[RGW_ATTR_RESTORE_EXPIRY_DATE];
}
{
bufferlist bl;
int64_t poolid = ioctx.get_id();
// Retain Object category as CloudTiered while restore is in
- // progress or failed
+ // progress or failed or if its temporarily restored copy
RGWObjCategory category = RGWObjCategory::Main;
auto r_iter = attrs.find(RGW_ATTR_RESTORE_STATUS);
+ auto t_iter = attrs.find(RGW_ATTR_RESTORE_TYPE);
if (r_iter != attrs.end()) {
rgw::sal::RGWRestoreStatus st = rgw::sal::RGWRestoreStatus::None;
auto iter = r_iter->second.cbegin();
if (st != rgw::sal::RGWRestoreStatus::CloudRestored) {
category = RGWObjCategory::CloudTiered;
+ } else { // check if its temporary copy
+ if (t_iter != attrs.end()) {
+ rgw::sal::RGWRestoreType rt;
+ decode(rt, t_iter->second);
+
+ if (rt == rgw::sal::RGWRestoreType::Temporary) {
+ category = RGWObjCategory::CloudTiered;
+ // temporary restore; set storage-class to cloudtier storage class
+ auto c_iter = attrs.find(RGW_ATTR_CLOUDTIER_STORAGE_CLASS);
+
+ if (c_iter != attrs.end()) {
+ storage_class = rgw_bl_str(c_iter->second);
+ }
+ }
+ }
}
} catch (buffer::error& err) {
}
}
+ ldpp_dout(dpp, 20) << "Setting obj category:" << category << ", storage_class:" << storage_class << dendl;
r = index_op.complete(dpp, poolid, epoch, state->size, state->accounted_size,
mtime, etag, content_type, storage_class, owner,
category, nullptr, y, nullptr, false, log_op);
obj_key.instance = "null";
}
- real_time read_mtime;
- std::unique_ptr<rgw::sal::Object::ReadOp> read_op(get_read_op());
- read_op->params.lastmod = &read_mtime;
- ret = read_op->prepare(y, dpp);
+ ret = get_obj_attrs(y, dpp);
if (ret < 0) {
ldpp_dout(dpp, -1) << "handle_obj_expiry Obj:" << get_key() <<
- ", read_op failed ret=" << ret << dendl;
+ ", getting object attrs failed ret=" << ret << dendl;
return ret;
}
obj_key.instance.clear();
}
+ // ensure object is not overwritten and is really expired
+ if (!is_expired()) {
+ return 0;
+ }
+
set_atomic(true);
map<string, bufferlist> attrs = get_attrs();
RGWRados::Object op_target(store->getRados(), bucket->get_info(), *rados_ctx, get_obj());
obj_op.meta.if_nomatch = NULL;
obj_op.meta.user_data = NULL;
obj_op.meta.zones_trace = NULL;
- obj_op.meta.set_mtime = read_mtime;
+ obj_op.meta.set_mtime = state.mtime;
RGWObjManifest *pmanifest;
pmanifest = &m;
attrs.erase(RGW_ATTR_RESTORE_EXPIRY_DATE);
attrs.erase(RGW_ATTR_CLOUDTIER_STORAGE_CLASS);
attrs.erase(RGW_ATTR_RESTORE_VERSIONED_EPOCH);
+ attrs.erase(RGW_ATTR_DELETE_AT);
bufferlist bl;
bl.append(tier_config.name);
}
}
// object is not restored/temporary; go for regular deletion
- // ensure object is not overwritten and is really expired
- if (is_expired()) {
ldpp_dout(dpp, 10) << "Deleting expired obj:" << get_key() << dendl;
ret = obj->delete_object(dpp, null_yield, rgw::sal::FLAG_LOG_OP, nullptr, nullptr);
- }
return ret;
}
}
} else if (restore_status == rgw::sal::RGWRestoreStatus::CloudRestored) {
// corresponds to CLOUD_RESTORED
+ if (!read_through) { //update expiry date iff its temp restored copy
+ op_ret = driver->get_rgwrestore()->update_cloud_restore_exp_date(s->bucket.get(),
+ s->object.get(), days, dpp, y);
+
+ if (op_ret < 0) {
+ ldpp_dout(dpp, 20) << "Updating expiry-date of restored object " << s->object->get_key() << " failed - " << op_ret << dendl;
+ s->err.message = "failed to update expiry-date of the restored object";
+ return op_ret;
+ }
+ }
return static_cast<int>(rgw::sal::RGWRestoreStatus::CloudRestored);
} else { // first time restore or previous restore failed.
// Restore the object.
decode(restore_status, iter);
}
if (restore_status != rgw::sal::RGWRestoreStatus::RestoreAlreadyInProgress) {
- // XXX: Check if expiry-date needs to be update
ldpp_dout(this, 5) << __PRETTY_FUNCTION__ << ": Restore of object " << obj->get_key()
<< " not in progress state" << dendl;
return ret;
}
+void Restore::get_expiration_date(const DoutPrefixProvider* dpp,
+ int expiry_days, ceph::real_time& exp_date) {
+ constexpr int32_t secs_in_a_day = 24 * 60 * 60;
+ ceph::real_time cur_time = real_clock::now();
+
+ ldpp_dout(dpp, 5) << "Calculating expiration date for days:" << expiry_days << dendl;
+ if (cct->_conf->rgw_restore_debug_interval > 0) {
+ exp_date = cur_time + make_timespan(double(expiry_days)*cct->_conf->rgw_restore_debug_interval);
+ } else {
+ exp_date = cur_time + make_timespan(double(expiry_days) * secs_in_a_day);
+ }
+ ldpp_dout(dpp, 5) << "expiration date: " << exp_date << " , cur_time: " << cur_time << ", restore_interval: " << cct->_conf->rgw_restore_debug_interval << dendl;
+}
+
+/*
+ * As per AWS spec (https://docs.aws.amazon.com/AmazonS3/latest/API/API_RestoreObject.html),
+ * After restoring an archived object, you can update the restoration period by reissuing the
+ * request with a new period. Amazon S3 updates the restoration period relative to the current time.
+ * You cannot update the restoration period when Amazon S3 is actively processing your current restore
+ * request for the object.
+ */
+int Restore::update_cloud_restore_exp_date(rgw::sal::Bucket* pbucket,
+ rgw::sal::Object* pobj,
+ std::optional<uint64_t> days,
+ const DoutPrefixProvider* dpp,
+ optional_yield y)
+{
+ int ret = -1;
+ ceph::real_time cur_time = real_clock::now();
+
+ if (!pobj)
+ return ret;
+
+ if (!days) {// days should be present as we should update
+ // expiry date only for temp copies
+ return ret;
+ }
+
+ ret = pobj->get_obj_attrs(y, dpp);
+ if (ret < 0) {
+ ldpp_dout(dpp, 0) << "ERROR: failed to read object attrs " << cpp_strerror(-ret) << dendl;
+ return ret;
+ }
+
+ ceph::real_time expiration_date;
+ get_expiration_date(dpp, days.value(), expiration_date);
+
+ auto& attrs = pobj->get_attrs();
+ {
+ bufferlist bl;
+ using ceph::encode;
+ encode(expiration_date, bl);
+ attrs[RGW_ATTR_RESTORE_EXPIRY_DATE] = attrs[RGW_ATTR_DELETE_AT] = bl;
+ }
+ {
+ bufferlist bl;
+ using ceph::encode;
+ encode(cur_time, bl);
+ attrs[RGW_ATTR_INTERNAL_MTIME] = bl;
+ }
+
+ pobj->set_atomic(true);
+
+
+ ret = pobj->set_obj_attrs(dpp, &attrs, nullptr, y, 0);
+
+ if (ret < 0) {
+ ldpp_dout(dpp, 0) << "ERROR: failed to update restore expiry date ret=" << ret << dendl;
+ return ret;
+ }
+
+ ldpp_dout(dpp, 5) << "Updated Restore expiry time to: " << expiration_date << " , cur_time: " << cur_time << ", restore_interval: " << cct->_conf->rgw_restore_debug_interval << dendl;
+ pobj->set_atomic(false);
+
+
+ return ret;
+}
+
int Restore::restore_obj_from_cloud(rgw::sal::Bucket* pbucket,
rgw::sal::Object* pobj,
rgw::sal::PlacementTier* tier,
optional_yield y,
const rgw::sal::RGWRestoreStatus& restore_status);
+ /** Calculate expiration date based on expiry days */
+ void get_expiration_date(const DoutPrefixProvider* dpp,
+ int expiry_days, ceph::real_time& exp_date);
+
+ /** Update expiry date for temp restored copies */
+ int update_cloud_restore_exp_date(rgw::sal::Bucket* pbucket,
+ rgw::sal::Object* pobj, std::optional<uint64_t> days,
+ const DoutPrefixProvider* dpp, optional_yield y);
+
/** Given <bucket, obj>, restore the object from the cloud-tier. In case the
* object cannot be restored immediately, save that restore state(/entry)
* to be procesed later by RestoreWorker thread. */