Skip to content

Commit

Permalink
Merge pull request ceph#52791 from clwluvw/location-constraint
Browse files Browse the repository at this point in the history
rgw: check for location constraint on master zonegroup

Reviewed-by: Casey Bodley <[email protected]>
  • Loading branch information
cbodley authored Jan 9, 2025
2 parents 65271d8 + c21b5f7 commit 46a6f8e
Show file tree
Hide file tree
Showing 19 changed files with 70 additions and 95 deletions.
2 changes: 2 additions & 0 deletions qa/tasks/rgw_multisite.py
Original file line number Diff line number Diff line change
Expand Up @@ -361,6 +361,8 @@ def create_zonegroup(cluster, gateways, period, config):
if endpoints:
# replace client names with their gateway endpoints
config['endpoints'] = extract_gateway_endpoints(gateways, endpoints)
if not config.get('api_name'): # otherwise it will be set to an empty string
config['api_name'] = config['name']
zonegroup = multisite.ZoneGroup(config['name'], period)
# `zonegroup set` needs --default on command line, and 'is_master' in json
args = is_default_arg(config)
Expand Down
2 changes: 0 additions & 2 deletions src/rgw/driver/daos/rgw_sal_daos.cc
Original file line number Diff line number Diff line change
Expand Up @@ -858,8 +858,6 @@ bool DaosZone::is_writeable() { return true; }

bool DaosZone::get_redirect_endpoint(std::string* endpoint) { return false; }

bool DaosZone::has_zonegroup_api(const std::string& api) const { return false; }

const std::string& DaosZone::get_current_period_id() {
return current_period->get_id();
}
Expand Down
1 change: 0 additions & 1 deletion src/rgw/driver/daos/rgw_sal_daos.h
Original file line number Diff line number Diff line change
Expand Up @@ -484,7 +484,6 @@ class DaosZone : public StoreZone {
virtual const std::string& get_name() const override;
virtual bool is_writeable() override;
virtual bool get_redirect_endpoint(std::string* endpoint) override;
virtual bool has_zonegroup_api(const std::string& api) const override;
virtual const std::string& get_current_period_id() override;
virtual const RGWAccessKey& get_system_key() {
return zone_params->system_key;
Expand Down
5 changes: 0 additions & 5 deletions src/rgw/driver/motr/rgw_sal_motr.cc
Original file line number Diff line number Diff line change
Expand Up @@ -1111,11 +1111,6 @@ bool MotrZone::get_redirect_endpoint(std::string* endpoint)
return false;
}

bool MotrZone::has_zonegroup_api(const std::string& api) const
{
return (zonegroup.group.api_name == api);
}

const std::string& MotrZone::get_current_period_id()
{
return current_period->get_id();
Expand Down
1 change: 0 additions & 1 deletion src/rgw/driver/motr/rgw_sal_motr.h
Original file line number Diff line number Diff line change
Expand Up @@ -525,7 +525,6 @@ class MotrZone : public StoreZone {
virtual const std::string& get_name() const override;
virtual bool is_writeable() override;
virtual bool get_redirect_endpoint(std::string* endpoint) override;
virtual bool has_zonegroup_api(const std::string& api) const override;
virtual const std::string& get_current_period_id() override;
virtual const RGWAccessKey& get_system_key() { return zone_params->system_key; }
virtual const std::string& get_realm_name() { return realm->get_name(); }
Expand Down
14 changes: 0 additions & 14 deletions src/rgw/driver/rados/rgw_period.cc
Original file line number Diff line number Diff line change
Expand Up @@ -68,20 +68,6 @@ int RGWPeriod::delete_obj(const DoutPrefixProvider *dpp, optional_yield y)
return ret;
}

int RGWPeriod::add_zonegroup(const DoutPrefixProvider *dpp, const RGWZoneGroup& zonegroup, optional_yield y)
{
if (zonegroup.realm_id != realm_id) {
return 0;
}
int ret = period_map.update(zonegroup, cct);
if (ret < 0) {
ldpp_dout(dpp, 0) << "ERROR: updating period map: " << cpp_strerror(-ret) << dendl;
return ret;
}

return store_info(dpp, false, y);
}

int RGWPeriod::update(const DoutPrefixProvider *dpp, optional_yield y)
{
auto zone_svc = sysobj_svc->get_zone_svc();
Expand Down
5 changes: 0 additions & 5 deletions src/rgw/driver/rados/rgw_sal_rados.cc
Original file line number Diff line number Diff line change
Expand Up @@ -4500,11 +4500,6 @@ bool RadosZone::get_redirect_endpoint(std::string* endpoint)
return true;
}

bool RadosZone::has_zonegroup_api(const std::string& api) const
{
return store->svc()->zone->has_zonegroup_api(api);
}

const std::string& RadosZone::get_current_period_id()
{
return store->svc()->zone->get_current_period_id();
Expand Down
1 change: 0 additions & 1 deletion src/rgw/driver/rados/rgw_sal_rados.h
Original file line number Diff line number Diff line change
Expand Up @@ -107,7 +107,6 @@ class RadosZone : public StoreZone {
virtual const std::string& get_name() const override;
virtual bool is_writeable() override;
virtual bool get_redirect_endpoint(std::string* endpoint) override;
virtual bool has_zonegroup_api(const std::string& api) const override;
virtual const std::string& get_current_period_id() override;
virtual const RGWAccessKey& get_system_key() override;
virtual const std::string& get_realm_name() override;
Expand Down
1 change: 0 additions & 1 deletion src/rgw/driver/rados/rgw_zone.h
Original file line number Diff line number Diff line change
Expand Up @@ -769,7 +769,6 @@ class RGWPeriod
int create(const DoutPrefixProvider *dpp, optional_yield y, bool exclusive = true);
int delete_obj(const DoutPrefixProvider *dpp, optional_yield y);
int store_info(const DoutPrefixProvider *dpp, bool exclusive, optional_yield y);
int add_zonegroup(const DoutPrefixProvider *dpp, const RGWZoneGroup& zonegroup, optional_yield y);

void fork();
int update(const DoutPrefixProvider *dpp, optional_yield y);
Expand Down
1 change: 1 addition & 0 deletions src/rgw/rgw_common.cc
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,7 @@ rgw_http_errors rgw_http_s3_errors({
{ ERR_INVALID_DIGEST, {400, "InvalidDigest" }},
{ ERR_BAD_DIGEST, {400, "BadDigest" }},
{ ERR_INVALID_LOCATION_CONSTRAINT, {400, "InvalidLocationConstraint" }},
{ ERR_ILLEGAL_LOCATION_CONSTRAINT_EXCEPTION, {400, "IllegalLocationConstraintException" }},
{ ERR_ZONEGROUP_DEFAULT_PLACEMENT_MISCONFIGURATION, {400, "ZonegroupDefaultPlacementMisconfiguration" }},
{ ERR_INVALID_BUCKET_NAME, {400, "InvalidBucketName" }},
{ ERR_INVALID_OBJECT_NAME, {400, "InvalidObjectName" }},
Expand Down
1 change: 1 addition & 0 deletions src/rgw/rgw_common.h
Original file line number Diff line number Diff line change
Expand Up @@ -337,6 +337,7 @@ inline constexpr const char* RGW_REST_STS_XMLNS =
#define ERR_PRESIGNED_URL_EXPIRED 2223
#define ERR_PRESIGNED_URL_DISABLED 2224
#define ERR_AUTHORIZATION 2225 // SNS 403 AuthorizationError
#define ERR_ILLEGAL_LOCATION_CONSTRAINT_EXCEPTION 2226

#define ERR_BUSY_RESHARDING 2300 // also in cls_rgw_types.h, don't change!
#define ERR_NO_SUCH_ENTITY 2301
Expand Down
82 changes: 45 additions & 37 deletions src/rgw/rgw_op.cc
Original file line number Diff line number Diff line change
Expand Up @@ -3558,54 +3558,62 @@ void RGWCreateBucket::execute(optional_yield y)
const rgw::SiteConfig& site = *s->penv.site;
const std::optional<RGWPeriod>& period = site.get_period();
const RGWZoneGroup& my_zonegroup = site.get_zonegroup();

if (s->system_request) {
// allow system requests to override the target zonegroup. for forwarded
// requests, we'll create the bucket for the originating zonegroup
createparams.zonegroup_id = s->info.args.get(RGW_SYS_PARAM_PREFIX "zonegroup");
}

const std::string rgwx_zonegroup = s->info.args.get(RGW_SYS_PARAM_PREFIX "zonegroup");
const RGWZoneGroup* bucket_zonegroup = &my_zonegroup;
if (createparams.zonegroup_id.empty()) {
// default to the local zonegroup
createparams.zonegroup_id = my_zonegroup.id;
} else if (period) {
auto z = period->period_map.zonegroups.find(createparams.zonegroup_id);
if (z == period->period_map.zonegroups.end()) {
ldpp_dout(this, 0) << "could not find zonegroup "
<< createparams.zonegroup_id << " in current period" << dendl;
op_ret = -ENOENT;
return;
}
bucket_zonegroup = &z->second;
} else if (createparams.zonegroup_id != my_zonegroup.id) {
ldpp_dout(this, 0) << "zonegroup does not match current zonegroup "
<< createparams.zonegroup_id << dendl;
op_ret = -ENOENT;
return;
}

// validate the LocationConstraint
// Validate LocationConstraint if it's provided and enforcement is strict
if (!location_constraint.empty() && !relaxed_region_enforcement) {
// on the master zonegroup, allow any valid api_name. otherwise it has to
// match the bucket's zonegroup
if (period && my_zonegroup.is_master) {
if (!period->period_map.zonegroups_by_api.count(location_constraint)) {
if (period) {
auto location_iter = period->period_map.zonegroups_by_api.find(location_constraint);
if (location_iter == period->period_map.zonegroups_by_api.end()) {
ldpp_dout(this, 0) << "location constraint (" << location_constraint
<< ") can't be found." << dendl;
op_ret = -ERR_INVALID_LOCATION_CONSTRAINT;
s->err.message = "The specified location-constraint is not valid";
s->err.message = fmt::format("The {} location constraint is not valid.",
location_constraint);
return;
}
} else if (bucket_zonegroup->api_name != location_constraint) {
bucket_zonegroup = &location_iter->second;
} else if (location_constraint != my_zonegroup.api_name) { // if we don't have a period, we can only use the current zonegroup - so check if the location matches by api name here
ldpp_dout(this, 0) << "location constraint (" << location_constraint
<< ") doesn't match zonegroup (" << bucket_zonegroup->api_name
<< ')' << dendl;
op_ret = -ERR_INVALID_LOCATION_CONSTRAINT;
s->err.message = "The specified location-constraint is not valid";
<< ") doesn't match zonegroup (" << my_zonegroup.api_name << ")" << dendl;
op_ret = -ERR_ILLEGAL_LOCATION_CONSTRAINT_EXCEPTION;
s->err.message = fmt::format("The {} location constraint is incompatible "
"for the region specific endpoint this request was sent to.",
location_constraint);
return;
}
}
// If it's a system request, use the provided zonegroup if available
else if (s->system_request && !rgwx_zonegroup.empty()) {
if (period) {
auto zonegroup_iter = period->period_map.zonegroups.find(rgwx_zonegroup);
if (zonegroup_iter == period->period_map.zonegroups.end()) {
ldpp_dout(this, 0) << "could not find zonegroup " << rgwx_zonegroup
<< " in current period" << dendl;
op_ret = -ENOENT;
return;
}
bucket_zonegroup = &zonegroup_iter->second;
}
}

const bool enforce_location_match =
!period || // No period: no multisite, so no need to enforce location match.
!s->system_request || // All user requests are enforced to match zonegroup's location.
!my_zonegroup.is_master; // but if it's a system request (forwarded) only allow remote creation on master zonegroup.
if (enforce_location_match && !my_zonegroup.equals(bucket_zonegroup->get_id())) {
ldpp_dout(this, 0) << "location constraint (" << bucket_zonegroup->api_name
<< ") doesn't match zonegroup (" << my_zonegroup.api_name << ")" << dendl;
op_ret = -ERR_ILLEGAL_LOCATION_CONSTRAINT_EXCEPTION;
s->err.message = fmt::format("The {} location constraint is incompatible "
"for the region specific endpoint this request was sent to.",
bucket_zonegroup->api_name);
return;
}

// Set the final zonegroup ID
createparams.zonegroup_id = bucket_zonegroup->id;

// select and validate the placement target
op_ret = select_bucket_placement(this, *bucket_zonegroup, s->user->get_info(),
Expand All @@ -3614,7 +3622,7 @@ void RGWCreateBucket::execute(optional_yield y)
return;
}

if (bucket_zonegroup == &my_zonegroup) {
if (my_zonegroup.equals(bucket_zonegroup->get_id())) {
// look up the zone placement pool
createparams.zone_placement = rgw::find_zone_placement(
this, site.get_zone_params(), createparams.placement_rule);
Expand Down
2 changes: 0 additions & 2 deletions src/rgw/rgw_sal.h
Original file line number Diff line number Diff line change
Expand Up @@ -1751,8 +1751,6 @@ class Zone {
virtual bool is_writeable() = 0;
/** Get the URL for the endpoint for redirecting to this zone */
virtual bool get_redirect_endpoint(std::string* endpoint) = 0;
/** Check to see if the given API is supported in this zone */
virtual bool has_zonegroup_api(const std::string& api) const = 0;
/** Get the current period ID for this zone */
virtual const std::string& get_current_period_id() = 0;
/** Get thes system access key for this zone */
Expand Down
8 changes: 0 additions & 8 deletions src/rgw/rgw_sal_dbstore.cc
Original file line number Diff line number Diff line change
Expand Up @@ -458,14 +458,6 @@ namespace rgw::sal {
return false;
}

bool DBZone::has_zonegroup_api(const std::string& api) const
{
if (api == "default")
return true;

return false;
}

const std::string& DBZone::get_current_period_id()
{
return current_period->get_id();
Expand Down
1 change: 0 additions & 1 deletion src/rgw/rgw_sal_dbstore.h
Original file line number Diff line number Diff line change
Expand Up @@ -303,7 +303,6 @@ class DBNotification : public StoreNotification {
virtual const std::string& get_name() const override;
virtual bool is_writeable() override;
virtual bool get_redirect_endpoint(std::string* endpoint) override;
virtual bool has_zonegroup_api(const std::string& api) const override;
virtual const std::string& get_current_period_id() override;
virtual const RGWAccessKey& get_system_key() override;
virtual const std::string& get_realm_name() override;
Expand Down
3 changes: 0 additions & 3 deletions src/rgw/rgw_sal_filter.h
Original file line number Diff line number Diff line change
Expand Up @@ -108,9 +108,6 @@ class FilterZone : public Zone {
virtual bool get_redirect_endpoint(std::string* endpoint) override {
return next->get_redirect_endpoint(endpoint);
}
virtual bool has_zonegroup_api(const std::string& api) const override {
return next->has_zonegroup_api(api);
}
virtual const std::string& get_current_period_id() override {
return next->get_current_period_id();
}
Expand Down
12 changes: 0 additions & 12 deletions src/rgw/services/svc_zone.cc
Original file line number Diff line number Diff line change
Expand Up @@ -657,18 +657,6 @@ const string& RGWSI_Zone::get_current_period_id() const
return current_period->get_id();
}

bool RGWSI_Zone::has_zonegroup_api(const std::string& api) const
{
if (!current_period->get_id().empty()) {
const auto& zonegroups_by_api = current_period->get_map().zonegroups_by_api;
if (zonegroups_by_api.find(api) != zonegroups_by_api.end())
return true;
} else if (zonegroup->api_name == api) {
return true;
}
return false;
}

bool RGWSI_Zone::zone_is_writeable()
{
return writeable_zone && !get_zone().is_read_only();
Expand Down
1 change: 0 additions & 1 deletion src/rgw/services/svc_zone.h
Original file line number Diff line number Diff line change
Expand Up @@ -96,7 +96,6 @@ class RGWSI_Zone : public RGWServiceInstance
uint32_t get_zone_short_id() const;

const std::string& get_current_period_id() const;
bool has_zonegroup_api(const std::string& api) const;

bool zone_is_writeable();
bool zone_syncs_from(const RGWZone& target_zone, const RGWZone& source_zone) const;
Expand Down
22 changes: 21 additions & 1 deletion src/test/rgw/rgw_multi/tests.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
import boto.s3.connection
from boto.s3.website import WebsiteConfiguration
from boto.s3.cors import CORSConfiguration
from botocore.exceptions import ClientError

from nose.tools import eq_ as eq
from nose.tools import assert_not_equal, assert_equal, assert_true, assert_false
Expand Down Expand Up @@ -3638,4 +3639,23 @@ def test_copy_object_different_bucket():
CopySource = source_bucket.name + '/' + objname)

zonegroup_bucket_checkpoint(zonegroup_conns, dest_bucket.name)


def test_bucket_create_location_constraint():
for zonegroup in realm.current_period.zonegroups:
zonegroup_conns = ZonegroupConns(zonegroup)
for zg in realm.current_period.zonegroups:
z = zonegroup_conns.rw_zones[0]
bucket_name = gen_bucket_name()
if zg.name == zonegroup.name:
# my zonegroup should pass
z.s3_client.create_bucket(Bucket=bucket_name, CreateBucketConfiguration={'LocationConstraint': zg.name})
# check bucket location
response = z.s3_client.get_bucket_location(Bucket=bucket_name)
assert_equal(response['LocationConstraint'], zg.name)
else:
# other zonegroup should fail with 400
e = assert_raises(ClientError,
z.s3_client.create_bucket,
Bucket=bucket_name,
CreateBucketConfiguration={'LocationConstraint': zg.name})
assert e.response['ResponseMetadata']['HTTPStatusCode'] == 400

0 comments on commit 46a6f8e

Please sign in to comment.