rgw: keep default-placement in zone config
Signed-off-by: Artem Torubarov <artem.torubarov@clyso.com>
This commit is contained in:
1 parent
4c812f573b
commit
8ad2bf88bb
7 files changed
+233
-35
No files matched your search
@@ -10965,7 +10965,9 @@ bool
|
||||
</em>
|
||||
</td>
|
||||
<td>
|
||||
<p>Sets given placement as default. Only one placement in the list can be marked as default.</p>
|
||||
<em>(Optional)</em>
|
||||
<p>Sets given placement as default. Only one placement in the list can be marked as default.
|
||||
Default is false.</p>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
|
||||
@@ -12384,7 +12384,9 @@ spec:
|
||||
minLength: 1
|
||||
type: string
|
||||
default:
|
||||
description: Sets given placement as default. Only one placement in the list can be marked as default.
|
||||
description: |-
|
||||
Sets given placement as default. Only one placement in the list can be marked as default.
|
||||
Default is false.
|
||||
type: boolean
|
||||
metadataPoolName:
|
||||
description: The metadata pool used to store ObjectStore bucket index.
|
||||
@@ -12423,7 +12425,6 @@ spec:
|
||||
type: array
|
||||
required:
|
||||
- dataPoolName
|
||||
- default
|
||||
- metadataPoolName
|
||||
- name
|
||||
type: object
|
||||
@@ -13310,7 +13311,9 @@ spec:
|
||||
minLength: 1
|
||||
type: string
|
||||
default:
|
||||
description: Sets given placement as default. Only one placement in the list can be marked as default.
|
||||
description: |-
|
||||
Sets given placement as default. Only one placement in the list can be marked as default.
|
||||
Default is false.
|
||||
type: boolean
|
||||
metadataPoolName:
|
||||
description: The metadata pool used to store ObjectStore bucket index.
|
||||
@@ -13349,7 +13352,6 @@ spec:
|
||||
type: array
|
||||
required:
|
||||
- dataPoolName
|
||||
- default
|
||||
- metadataPoolName
|
||||
- name
|
||||
type: object
|
||||
|
||||
@@ -12375,7 +12375,9 @@ spec:
|
||||
minLength: 1
|
||||
type: string
|
||||
default:
|
||||
description: Sets given placement as default. Only one placement in the list can be marked as default.
|
||||
description: |-
|
||||
Sets given placement as default. Only one placement in the list can be marked as default.
|
||||
Default is false.
|
||||
type: boolean
|
||||
metadataPoolName:
|
||||
description: The metadata pool used to store ObjectStore bucket index.
|
||||
@@ -12414,7 +12416,6 @@ spec:
|
||||
type: array
|
||||
required:
|
||||
- dataPoolName
|
||||
- default
|
||||
- metadataPoolName
|
||||
- name
|
||||
type: object
|
||||
@@ -13298,7 +13299,9 @@ spec:
|
||||
minLength: 1
|
||||
type: string
|
||||
default:
|
||||
description: Sets given placement as default. Only one placement in the list can be marked as default.
|
||||
description: |-
|
||||
Sets given placement as default. Only one placement in the list can be marked as default.
|
||||
Default is false.
|
||||
type: boolean
|
||||
metadataPoolName:
|
||||
description: The metadata pool used to store ObjectStore bucket index.
|
||||
@@ -13337,7 +13340,6 @@ spec:
|
||||
type: array
|
||||
required:
|
||||
- dataPoolName
|
||||
- default
|
||||
- metadataPoolName
|
||||
- name
|
||||
type: object
|
||||
|
||||
@@ -1564,6 +1564,8 @@ type PoolPlacementSpec struct {
|
||||
Name string `json:"name"`
|
||||
|
||||
// Sets given placement as default. Only one placement in the list can be marked as default.
|
||||
// Default is false.
|
||||
// +optional
|
||||
Default bool `json:"default"`
|
||||
|
||||
// The metadata pool used to store ObjectStore bucket index.
|
||||
|
||||
@@ -152,7 +152,7 @@ const (
|
||||
"notif_pool": "%[1]s:store-a.log.notif"
|
||||
}`
|
||||
|
||||
objectZonegroupJsonTempl = `{
|
||||
objectZonegroupJson = `{
|
||||
"id": "610c9e3d-19e7-40b0-9f88-03319c4bc65a",
|
||||
"name": "store-a",
|
||||
"api_name": "test",
|
||||
@@ -174,14 +174,14 @@ const (
|
||||
],
|
||||
"placement_targets": [
|
||||
{
|
||||
"name": "%[1]s",
|
||||
"name": "default-placement",
|
||||
"tags": [],
|
||||
"storage_classes": [
|
||||
"STANDARD"
|
||||
]
|
||||
}
|
||||
],
|
||||
"default_placement": "%[1]s",
|
||||
"default_placement": "default-placement",
|
||||
"realm_id": "29e28253-be54-4581-90dd-206020d2fcdd",
|
||||
"sync_policy": {
|
||||
"groups": []
|
||||
@@ -229,7 +229,6 @@ func TestReconcileRealm(t *testing.T) {
|
||||
|
||||
func TestConfigureStoreWithSharedPools(t *testing.T) {
|
||||
sharedMetaPoolAlreadySet, sharedDataPoolAlreadySet := "", ""
|
||||
defaultPlacement := defaultPlacementCephConfigName
|
||||
zoneGetCalled := false
|
||||
zoneSetCalled := false
|
||||
zoneGroupGetCalled := false
|
||||
@@ -274,7 +273,7 @@ func TestConfigureStoreWithSharedPools(t *testing.T) {
|
||||
} else if args[0] == "zonegroup" {
|
||||
if args[1] == "get" {
|
||||
zoneGroupGetCalled = true
|
||||
return fmt.Sprintf(objectZonegroupJsonTempl, defaultPlacement), nil
|
||||
return objectZonegroupJson, nil
|
||||
} else if args[1] == "set" {
|
||||
zoneGroupSetCalled = true
|
||||
for _, arg := range args {
|
||||
@@ -288,7 +287,7 @@ func TestConfigureStoreWithSharedPools(t *testing.T) {
|
||||
}
|
||||
return string(inBytes), nil
|
||||
}
|
||||
return fmt.Sprintf(objectZonegroupJsonTempl, defaultPlacement), nil
|
||||
return objectZonegroupJson, nil
|
||||
}
|
||||
}
|
||||
return "", errors.Errorf("unexpected ceph command %q", args)
|
||||
@@ -331,7 +330,7 @@ func TestConfigureStoreWithSharedPools(t *testing.T) {
|
||||
assert.True(t, zoneGroupGetCalled)
|
||||
assert.False(t, zoneGroupSetCalled) // zone group is set only if extra pool placements specified
|
||||
})
|
||||
t.Run("configure with default placement", func(t *testing.T) {
|
||||
t.Run("configure with new default placement", func(t *testing.T) {
|
||||
sharedPools := cephv1.ObjectSharedPoolsSpec{
|
||||
PoolPlacements: []cephv1.PoolPlacementSpec{
|
||||
{
|
||||
@@ -342,22 +341,22 @@ func TestConfigureStoreWithSharedPools(t *testing.T) {
|
||||
},
|
||||
},
|
||||
}
|
||||
defaultPlacement = "default"
|
||||
err := ConfigureSharedPoolsForZone(context, sharedPools)
|
||||
assert.NoError(t, err)
|
||||
assert.True(t, zoneGetCalled)
|
||||
assert.True(t, zoneSetCalled)
|
||||
assert.False(t, placementModifyCalled) // mock returns applied namespases, no workaround needed
|
||||
assert.True(t, zoneGroupGetCalled)
|
||||
assert.False(t, zoneGroupSetCalled) // zone group is set only if extra pool placements specified
|
||||
assert.True(t, zoneGroupSetCalled)
|
||||
})
|
||||
t.Run("data pool already set", func(t *testing.T) {
|
||||
// reset
|
||||
zoneGroupSetCalled = false
|
||||
// Simulate that the data pool has already been set and the zone update can be skipped
|
||||
sharedPools := cephv1.ObjectSharedPoolsSpec{
|
||||
MetadataPoolName: "test-meta",
|
||||
DataPoolName: "test-data",
|
||||
}
|
||||
defaultPlacement = defaultPlacementCephConfigName
|
||||
sharedMetaPoolAlreadySet, sharedDataPoolAlreadySet = "test-meta", "test-data"
|
||||
zoneGetCalled = false
|
||||
zoneSetCalled = false
|
||||
|
||||
@@ -89,7 +89,6 @@ func adjustZonePlacementPools(zone map[string]interface{}, spec cephv1.ObjectSha
|
||||
|
||||
fromSpec := toZonePlacementPools(spec, name)
|
||||
|
||||
defaultPlacementName := getDefaultPlacementName(spec)
|
||||
inConfig := map[string]struct{}{}
|
||||
idxToRemove := map[int]struct{}{}
|
||||
for i, p := range placements {
|
||||
@@ -102,10 +101,19 @@ func adjustZonePlacementPools(zone map[string]interface{}, spec cephv1.ObjectSha
|
||||
return nil, fmt.Errorf("unable to get pool placement name for zone %s: %w", name, err)
|
||||
}
|
||||
// check if placement should be removed
|
||||
if _, inSpec := fromSpec[placementID]; !inSpec && placementID != defaultPlacementName {
|
||||
// remove placement if it is not in spec, but don't remove default placement
|
||||
idxToRemove[i] = struct{}{}
|
||||
continue
|
||||
if _, inSpec := fromSpec[placementID]; !inSpec {
|
||||
if placementID == defaultPlacementCephConfigName {
|
||||
// 'default-placement' should always be kept as a workaround for https://tracker.ceph.com/issues/68775.
|
||||
// if user specified other placement as default, then copy pool names to 'default-placement' from it:
|
||||
if userDefault, inSpec := fromSpec[getDefaultPlacementName(spec)]; inSpec {
|
||||
// duplicate user defined default placement under 'default-placement' name in spec to update pools on the next step
|
||||
fromSpec[defaultPlacementCephConfigName] = userDefault
|
||||
}
|
||||
} else {
|
||||
// remove placement if it is not in spec
|
||||
idxToRemove[i] = struct{}{}
|
||||
continue
|
||||
}
|
||||
}
|
||||
// update placement with values from spec:
|
||||
if pSpec, inSpec := fromSpec[placementID]; inSpec {
|
||||
@@ -155,6 +163,29 @@ func adjustZonePlacementPools(zone map[string]interface{}, spec cephv1.ObjectSha
|
||||
placements = append(placements, pObj)
|
||||
}
|
||||
|
||||
// sort placements array.
|
||||
// Reason: 'radosgw-admin zone set --infile' sorts placement_pools by key before storing it in ceph
|
||||
// and returns JSON with sorted placement_pools array. So we sort input array for easy comparison with applied JSON.
|
||||
sort.Slice(placements, func(i, j int) bool {
|
||||
pI, ok := placements[i].(map[string]interface{})
|
||||
if !ok {
|
||||
return false
|
||||
}
|
||||
nameI, err := getObjProperty[string](pI, "key")
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
pJ, ok := placements[j].(map[string]interface{})
|
||||
if !ok {
|
||||
return false
|
||||
}
|
||||
nameJ, err := getObjProperty[string](pJ, "key")
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
return nameI < nameJ
|
||||
})
|
||||
|
||||
_, err = updateObjProperty(zone, placements, "placement_pools")
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("unable to set pool placements for zone %q: %w", name, err)
|
||||
@@ -182,16 +213,9 @@ func getDefaultMetadataPool(spec cephv1.ObjectSharedPoolsSpec) string {
|
||||
|
||||
// toZonePlacementPools converts pool placement CRD definition to zone config json format structures
|
||||
func toZonePlacementPools(spec cephv1.ObjectSharedPoolsSpec, ns string) map[string]ZonePlacementPool {
|
||||
hasDefault := false
|
||||
res := make(map[string]ZonePlacementPool, len(spec.PoolPlacements)+1)
|
||||
for _, pp := range spec.PoolPlacements {
|
||||
if pp.Default {
|
||||
hasDefault = true
|
||||
}
|
||||
res[pp.Name] = toZonePlacementPool(pp, ns)
|
||||
}
|
||||
if !hasDefault && spec.DataPoolName != "" && spec.MetadataPoolName != "" {
|
||||
// set shared pools as default if no default placement was provided
|
||||
// map sharedPools if presented:
|
||||
if spec.DataPoolName != "" && spec.MetadataPoolName != "" {
|
||||
res[defaultPlacementCephConfigName] = ZonePlacementPool{
|
||||
Key: defaultPlacementCephConfigName,
|
||||
Val: ZonePlacementPoolVal{
|
||||
@@ -211,6 +235,10 @@ func toZonePlacementPools(spec cephv1.ObjectSharedPoolsSpec, ns string) map[stri
|
||||
},
|
||||
}
|
||||
}
|
||||
// map pool placements:
|
||||
for _, pp := range spec.PoolPlacements {
|
||||
res[pp.Name] = toZonePlacementPool(pp, ns)
|
||||
}
|
||||
return res
|
||||
}
|
||||
|
||||
@@ -493,7 +521,7 @@ func updateZoneGroupJSON(objContext *Context, group map[string]interface{}) (map
|
||||
args := []string{"zonegroup", "set", zoneArg, "--infile=" + configFilename, realmArg, zoneGroupArg}
|
||||
updatedBytes, err := RunAdminCommandNoMultisite(objContext, false, args...)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "failed to set zone config")
|
||||
return nil, errors.Wrap(err, fmt.Sprintf("failed to set zonegroup config %s", updatedBytes))
|
||||
}
|
||||
updated := map[string]interface{}{}
|
||||
err = json.Unmarshal([]byte(updatedBytes), &updated)
|
||||
|
||||
@@ -630,7 +630,7 @@ func Test_toZonePlacementPools(t *testing.T) {
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "default placement overrides shared pools",
|
||||
name: "shared pools not removed if default placement set",
|
||||
args: args{
|
||||
spec: cephv1.ObjectSharedPoolsSpec{
|
||||
MetadataPoolName: "meta",
|
||||
@@ -655,6 +655,19 @@ func Test_toZonePlacementPools(t *testing.T) {
|
||||
ns: "rgw-instance",
|
||||
},
|
||||
want: map[string]ZonePlacementPool{
|
||||
defaultPlacementCephConfigName: {
|
||||
Key: defaultPlacementCephConfigName,
|
||||
Val: ZonePlacementPoolVal{
|
||||
DataExtraPool: "meta:rgw-instance.buckets.non-ec",
|
||||
IndexPool: "meta:rgw-instance.buckets.index",
|
||||
StorageClasses: map[string]ZonePlacementStorageClass{
|
||||
"STANDARD": {
|
||||
DataPool: "data:rgw-instance.buckets.data",
|
||||
},
|
||||
},
|
||||
InlineData: true,
|
||||
},
|
||||
},
|
||||
"some_name": {
|
||||
Key: "some_name",
|
||||
Val: ZonePlacementPoolVal{
|
||||
@@ -1444,6 +1457,156 @@ func Test_adjustZonePlacementPools(t *testing.T) {
|
||||
}
|
||||
}
|
||||
]
|
||||
}`,
|
||||
wantChanged: true,
|
||||
wantErr: false,
|
||||
},
|
||||
{
|
||||
name: "sharedPools placement not removed if default set",
|
||||
args: args{
|
||||
beforeJSON: `{
|
||||
"id": "f539c2c0-e1ed-4c42-9294-41742352eeae",
|
||||
"name": "test",
|
||||
"placement_pools": [
|
||||
{
|
||||
"key": "default-placement",
|
||||
"val": {
|
||||
"index_pool": "test.rgw.buckets.index",
|
||||
"storage_classes": {
|
||||
"STANDARD": {
|
||||
"data_pool": "test.rgw.buckets.data"
|
||||
}
|
||||
},
|
||||
"data_extra_pool": "test.rgw.buckets.non-ec",
|
||||
"index_type": 5,
|
||||
"inline_data": true
|
||||
}
|
||||
}
|
||||
]
|
||||
}`,
|
||||
spec: cephv1.ObjectSharedPoolsSpec{
|
||||
MetadataPoolName: "meta-pool",
|
||||
DataPoolName: "data-pool",
|
||||
PreserveRadosNamespaceDataOnDelete: false,
|
||||
PoolPlacements: []cephv1.PoolPlacementSpec{
|
||||
{
|
||||
Name: "slow",
|
||||
Default: true,
|
||||
MetadataPoolName: "slow-meta",
|
||||
DataPoolName: "slow-data",
|
||||
DataNonECPoolName: "slow-non-ec",
|
||||
StorageClasses: []cephv1.PlacementStorageClassSpec{},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
wantJSON: `{
|
||||
"id": "f539c2c0-e1ed-4c42-9294-41742352eeae",
|
||||
"name": "test",
|
||||
"placement_pools": [
|
||||
{
|
||||
"key": "default-placement",
|
||||
"val": {
|
||||
"index_pool": "meta-pool:test.buckets.index",
|
||||
"storage_classes": {
|
||||
"STANDARD": {
|
||||
"data_pool": "data-pool:test.buckets.data"
|
||||
}
|
||||
},
|
||||
"data_extra_pool": "meta-pool:test.buckets.non-ec",
|
||||
"index_type": 5,
|
||||
"inline_data": true
|
||||
}
|
||||
},
|
||||
{
|
||||
"key": "slow",
|
||||
"val": {
|
||||
"index_pool": "slow-meta:test.slow.index",
|
||||
"storage_classes": {
|
||||
"STANDARD": {
|
||||
"data_pool": "slow-data:test.slow.data"
|
||||
}
|
||||
},
|
||||
"data_extra_pool": "slow-non-ec:test.slow.data.non-ec",
|
||||
"inline_data": true
|
||||
}
|
||||
}
|
||||
]
|
||||
}`,
|
||||
wantChanged: true,
|
||||
wantErr: false,
|
||||
},
|
||||
{
|
||||
name: "if sharedPools not set and default placement is set, then default placements values are copied to 'default-placement'",
|
||||
args: args{
|
||||
beforeJSON: `{
|
||||
"id": "f539c2c0-e1ed-4c42-9294-41742352eeae",
|
||||
"name": "test",
|
||||
"placement_pools": [
|
||||
{
|
||||
"key": "default-placement",
|
||||
"val": {
|
||||
"index_pool": "test.rgw.buckets.index",
|
||||
"storage_classes": {
|
||||
"STANDARD": {
|
||||
"data_pool": "test.rgw.buckets.data"
|
||||
}
|
||||
},
|
||||
"data_extra_pool": "test.rgw.buckets.non-ec",
|
||||
"index_type": 5,
|
||||
"inline_data": true
|
||||
}
|
||||
}
|
||||
]
|
||||
}`,
|
||||
spec: cephv1.ObjectSharedPoolsSpec{
|
||||
MetadataPoolName: "",
|
||||
DataPoolName: "",
|
||||
PreserveRadosNamespaceDataOnDelete: false,
|
||||
PoolPlacements: []cephv1.PoolPlacementSpec{
|
||||
{
|
||||
Name: "slow",
|
||||
Default: true,
|
||||
MetadataPoolName: "slow-meta",
|
||||
DataPoolName: "slow-data",
|
||||
DataNonECPoolName: "slow-non-ec",
|
||||
StorageClasses: []cephv1.PlacementStorageClassSpec{},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
wantJSON: `{
|
||||
"id": "f539c2c0-e1ed-4c42-9294-41742352eeae",
|
||||
"name": "test",
|
||||
"placement_pools": [
|
||||
{
|
||||
"key": "default-placement",
|
||||
"val": {
|
||||
"index_pool": "slow-meta:test.slow.index",
|
||||
"storage_classes": {
|
||||
"STANDARD": {
|
||||
"data_pool": "slow-data:test.slow.data"
|
||||
}
|
||||
},
|
||||
"data_extra_pool": "slow-non-ec:test.slow.data.non-ec",
|
||||
"index_type": 5,
|
||||
"inline_data": true
|
||||
}
|
||||
},
|
||||
{
|
||||
"key": "slow",
|
||||
"val": {
|
||||
"index_pool": "slow-meta:test.slow.index",
|
||||
"storage_classes": {
|
||||
"STANDARD": {
|
||||
"data_pool": "slow-data:test.slow.data"
|
||||
}
|
||||
},
|
||||
"data_extra_pool": "slow-non-ec:test.slow.data.non-ec",
|
||||
"inline_data": true
|
||||
}
|
||||
}
|
||||
]
|
||||
}`,
|
||||
wantChanged: true,
|
||||
wantErr: false,
|
||||
|
||||
Reference in new issue
Block a user