forked from TrueCloudLab/frostfs-s3-gw
[#242] Fix versions sorting
Signed-off-by: Denis Kirillov <denis@nspcc.ru>
This commit is contained in:
parent
42ed6a16ea
commit
458f9cf17b
5 changed files with 231 additions and 76 deletions
|
@ -419,7 +419,7 @@ func containsACLHeaders(r *http.Request) bool {
|
||||||
r.Header.Get(api.AmzGrantFullControl) != "" || r.Header.Get(api.AmzGrantWrite) != ""
|
r.Header.Get(api.AmzGrantFullControl) != "" || r.Header.Get(api.AmzGrantWrite) != ""
|
||||||
}
|
}
|
||||||
|
|
||||||
func (h *handler) getNewEAclTable(r *http.Request, objInfo *layer.ObjectInfo) (*eacl.Table, error) {
|
func (h *handler) getNewEAclTable(r *http.Request, objInfo *api.ObjectInfo) (*eacl.Table, error) {
|
||||||
var newEaclTable *eacl.Table
|
var newEaclTable *eacl.Table
|
||||||
objectACL, err := parseACLHeaders(r)
|
objectACL, err := parseACLHeaders(r)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
|
@ -249,7 +249,7 @@ func updateCRDT2PSetHeaders(p *PutObjectParams, versions *objectVersions, versio
|
||||||
}
|
}
|
||||||
|
|
||||||
if versioningEnabled {
|
if versioningEnabled {
|
||||||
if len(versions.addList) != 0 {
|
if !versions.isAddListEmpty() {
|
||||||
p.Header[versionsAddAttr] = versions.getAddHeader()
|
p.Header[versionsAddAttr] = versions.getAddHeader()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -319,11 +319,11 @@ func (n *layer) headVersions(ctx context.Context, bkt *api.BucketInfo, objectNam
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
versions := newObjectVersions(objectName)
|
||||||
if len(ids) == 0 {
|
if len(ids) == 0 {
|
||||||
return nil, apiErrors.GetAPIError(apiErrors.ErrNoSuchKey)
|
return versions, apiErrors.GetAPIError(apiErrors.ErrNoSuchKey)
|
||||||
}
|
}
|
||||||
|
|
||||||
versions := newObjectVersions(objectName)
|
|
||||||
for _, id := range ids {
|
for _, id := range ids {
|
||||||
meta, err := n.objectHead(ctx, bkt.CID, id)
|
meta, err := n.objectHead(ctx, bkt.CID, id)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -526,16 +526,6 @@ func (n *layer) getAllObjectsVersions(ctx context.Context, bkt *api.BucketInfo,
|
||||||
return versions, nil
|
return versions, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func getExistedVersions(versions *objectVersions) []string {
|
|
||||||
var res []string
|
|
||||||
for _, add := range versions.addList {
|
|
||||||
if !contains(versions.delList, add) {
|
|
||||||
res = append(res, add)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return res
|
|
||||||
}
|
|
||||||
|
|
||||||
func splitVersions(header string) []string {
|
func splitVersions(header string) []string {
|
||||||
if len(header) == 0 {
|
if len(header) == 0 {
|
||||||
return nil
|
return nil
|
||||||
|
|
|
@ -2,6 +2,7 @@ package layer
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
|
"math"
|
||||||
"sort"
|
"sort"
|
||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
|
@ -34,15 +35,15 @@ func newObjectVersions(name string) *objectVersions {
|
||||||
return &objectVersions{name: name}
|
return &objectVersions{name: name}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (v *objectVersions) isAddListEmpty() bool {
|
||||||
|
v.sort()
|
||||||
|
return len(v.addList) == 0
|
||||||
|
}
|
||||||
|
|
||||||
func (v *objectVersions) appendVersion(oi *api.ObjectInfo) {
|
func (v *objectVersions) appendVersion(oi *api.ObjectInfo) {
|
||||||
addVers := append(splitVersions(oi.Headers[versionsAddAttr]), oi.Version())
|
|
||||||
delVers := splitVersions(oi.Headers[versionsDelAttr])
|
delVers := splitVersions(oi.Headers[versionsDelAttr])
|
||||||
v.objects = append(v.objects, oi)
|
v.objects = append(v.objects, oi)
|
||||||
for _, add := range addVers {
|
|
||||||
if !contains(v.addList, add) {
|
|
||||||
v.addList = append(v.addList, add)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
for _, del := range delVers {
|
for _, del := range delVers {
|
||||||
if !contains(v.delList, del) {
|
if !contains(v.delList, del) {
|
||||||
v.delList = append(v.delList, del)
|
v.delList = append(v.delList, del)
|
||||||
|
@ -54,19 +55,114 @@ func (v *objectVersions) appendVersion(oi *api.ObjectInfo) {
|
||||||
func (v *objectVersions) sort() {
|
func (v *objectVersions) sort() {
|
||||||
if !v.isSorted {
|
if !v.isSorted {
|
||||||
sort.Slice(v.objects, func(i, j int) bool {
|
sort.Slice(v.objects, func(i, j int) bool {
|
||||||
return less(v.objects[i], v.objects[j])
|
o1, o2 := v.objects[i], v.objects[j]
|
||||||
|
if o1.CreationEpoch == o2.CreationEpoch {
|
||||||
|
l1, l2 := o1.Headers[versionsAddAttr], o2.Headers[versionsAddAttr]
|
||||||
|
if len(l1) != len(l2) {
|
||||||
|
if strings.HasPrefix(l1, l2) {
|
||||||
|
return false
|
||||||
|
} else if strings.HasPrefix(l2, l1) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return o1.Version() < o2.Version()
|
||||||
|
}
|
||||||
|
return o1.CreationEpoch < o2.CreationEpoch
|
||||||
})
|
})
|
||||||
|
|
||||||
|
v.formAddList()
|
||||||
v.isSorted = true
|
v.isSorted = true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (v *objectVersions) formAddList() {
|
||||||
|
for i := 0; i < len(v.objects); i++ {
|
||||||
|
var conflicts [][]string
|
||||||
|
for { // forming conflicts set (objects with the same creation epoch)
|
||||||
|
addVers := append(splitVersions(v.objects[i].Headers[versionsAddAttr]), v.objects[i].Version())
|
||||||
|
conflicts = append(conflicts, addVers)
|
||||||
|
if i == len(v.objects)-1 || v.objects[i].CreationEpoch != v.objects[i+1].CreationEpoch ||
|
||||||
|
containsVersions(v.objects[i+1], addVers) {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
i++
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(conflicts) == 1 {
|
||||||
|
v.addList = addIfNotContains(v.addList, conflicts[0])
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
commonVersions, prevConflictedVersions, conflictedVersions := mergeVersionsConflicts(conflicts)
|
||||||
|
v.addList = commonVersions
|
||||||
|
v.addList = addIfNotContains(v.addList, prevConflictedVersions)
|
||||||
|
v.addList = addIfNotContains(v.addList, conflictedVersions)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func containsVersions(obj *api.ObjectInfo, versions []string) bool {
|
||||||
|
header := obj.Headers[versionsAddAttr]
|
||||||
|
for _, version := range versions {
|
||||||
|
if !strings.Contains(header, version) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
func addIfNotContains(list1, list2 []string) []string {
|
||||||
|
for _, add := range list2 {
|
||||||
|
if !contains(list1, add) {
|
||||||
|
list1 = append(list1, add)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return list1
|
||||||
|
}
|
||||||
|
|
||||||
|
func mergeVersionsConflicts(conflicts [][]string) ([]string, []string, []string) {
|
||||||
|
var currentVersions []string
|
||||||
|
var prevVersions []string
|
||||||
|
minLength := math.MaxInt32
|
||||||
|
for _, conflicted := range conflicts {
|
||||||
|
if len(conflicted)-1 < minLength {
|
||||||
|
minLength = len(conflicted) - 1
|
||||||
|
}
|
||||||
|
//last := conflicted[len(conflicted)-1]
|
||||||
|
//conflicts[j] = conflicted[:len(conflicted)-1]
|
||||||
|
//currentVersions = append(currentVersions, last)
|
||||||
|
}
|
||||||
|
var commonAddedVersions []string
|
||||||
|
diffIndex := 0
|
||||||
|
LOOP:
|
||||||
|
for k := 0; k < minLength; k++ {
|
||||||
|
candidate := conflicts[0][k]
|
||||||
|
for _, conflicted := range conflicts {
|
||||||
|
if conflicted[k] != candidate {
|
||||||
|
diffIndex = k
|
||||||
|
break LOOP
|
||||||
|
}
|
||||||
|
}
|
||||||
|
commonAddedVersions = append(commonAddedVersions, candidate)
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, conflicted := range conflicts {
|
||||||
|
for j := diffIndex; j < len(conflicted); j++ {
|
||||||
|
prevVersions = append(prevVersions, conflicted[j])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
sort.Strings(prevVersions)
|
||||||
|
sort.Strings(currentVersions)
|
||||||
|
return commonAddedVersions, prevVersions, currentVersions
|
||||||
|
}
|
||||||
|
|
||||||
func (v *objectVersions) getLast() *api.ObjectInfo {
|
func (v *objectVersions) getLast() *api.ObjectInfo {
|
||||||
if len(v.objects) == 0 {
|
if v == nil || len(v.objects) == 0 {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
v.sort()
|
v.sort()
|
||||||
existedVersions := getExistedVersions(v)
|
existedVersions := v.existedVersions()
|
||||||
for i := len(v.objects) - 1; i >= 0; i-- {
|
for i := len(v.objects) - 1; i >= 0; i-- {
|
||||||
if contains(existedVersions, v.objects[i].Version()) {
|
if contains(existedVersions, v.objects[i].Version()) {
|
||||||
delMarkHeader := v.objects[i].Headers[versionsDeleteMarkAttr]
|
delMarkHeader := v.objects[i].Headers[versionsDeleteMarkAttr]
|
||||||
|
@ -82,13 +178,24 @@ func (v *objectVersions) getLast() *api.ObjectInfo {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (v *objectVersions) getFiltered() []*api.ObjectInfo {
|
func (v *objectVersions) existedVersions() []string {
|
||||||
|
v.sort()
|
||||||
|
var res []string
|
||||||
|
for _, add := range v.addList {
|
||||||
|
if !contains(v.delList, add) {
|
||||||
|
res = append(res, add)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return res
|
||||||
|
}
|
||||||
|
|
||||||
|
func (v *objectVersions) getFiltered(reverse bool) []*api.ObjectInfo {
|
||||||
if len(v.objects) == 0 {
|
if len(v.objects) == 0 {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
v.sort()
|
v.sort()
|
||||||
existedVersions := getExistedVersions(v)
|
existedVersions := v.existedVersions()
|
||||||
res := make([]*api.ObjectInfo, 0, len(v.objects))
|
res := make([]*api.ObjectInfo, 0, len(v.objects))
|
||||||
|
|
||||||
for _, version := range v.objects {
|
for _, version := range v.objects {
|
||||||
|
@ -98,10 +205,17 @@ func (v *objectVersions) getFiltered() []*api.ObjectInfo {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if reverse {
|
||||||
|
for i, j := 0, len(res)-1; i < j; i, j = i+1, j-1 {
|
||||||
|
res[i], res[j] = res[j], res[i]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return res
|
return res
|
||||||
}
|
}
|
||||||
|
|
||||||
func (v *objectVersions) getAddHeader() string {
|
func (v *objectVersions) getAddHeader() string {
|
||||||
|
v.sort()
|
||||||
return strings.Join(v.addList, ",")
|
return strings.Join(v.addList, ",")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -149,6 +263,7 @@ func (n *layer) ListObjectVersions(ctx context.Context, p *ListObjectVersionsPar
|
||||||
versions map[string]*objectVersions
|
versions map[string]*objectVersions
|
||||||
allObjects = make([]*api.ObjectInfo, 0, p.MaxKeys)
|
allObjects = make([]*api.ObjectInfo, 0, p.MaxKeys)
|
||||||
res = &ListObjectVersionsInfo{}
|
res = &ListObjectVersionsInfo{}
|
||||||
|
reverse = true
|
||||||
)
|
)
|
||||||
|
|
||||||
bkt, err := n.GetBucketInfo(ctx, p.Bucket)
|
bkt, err := n.GetBucketInfo(ctx, p.Bucket)
|
||||||
|
@ -167,7 +282,7 @@ func (n *layer) ListObjectVersions(ctx context.Context, p *ListObjectVersionsPar
|
||||||
sort.Strings(sortedNames)
|
sort.Strings(sortedNames)
|
||||||
|
|
||||||
for _, name := range sortedNames {
|
for _, name := range sortedNames {
|
||||||
allObjects = append(allObjects, versions[name].getFiltered()...)
|
allObjects = append(allObjects, versions[name].getFiltered(reverse)...)
|
||||||
}
|
}
|
||||||
|
|
||||||
for i, obj := range allObjects {
|
for i, obj := range allObjects {
|
||||||
|
@ -192,7 +307,7 @@ func (n *layer) ListObjectVersions(ctx context.Context, p *ListObjectVersionsPar
|
||||||
objects := make([]*ObjectVersionInfo, len(allObjects))
|
objects := make([]*ObjectVersionInfo, len(allObjects))
|
||||||
for i, obj := range allObjects {
|
for i, obj := range allObjects {
|
||||||
objects[i] = &ObjectVersionInfo{Object: obj}
|
objects[i] = &ObjectVersionInfo{Object: obj}
|
||||||
if i == len(allObjects)-1 || allObjects[i+1].Name != obj.Name {
|
if i == 0 || allObjects[i-1].Name != obj.Name {
|
||||||
objects[i].IsLatest = true
|
objects[i].IsLatest = true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -220,13 +335,6 @@ func triageVersions(objVersions []*ObjectVersionInfo) ([]*ObjectVersionInfo, []*
|
||||||
return resVersion, resDelMarkVersions
|
return resVersion, resDelMarkVersions
|
||||||
}
|
}
|
||||||
|
|
||||||
func less(ov1, ov2 *api.ObjectInfo) bool {
|
|
||||||
if ov1.CreationEpoch == ov2.CreationEpoch {
|
|
||||||
return ov1.Version() < ov2.Version()
|
|
||||||
}
|
|
||||||
return ov1.CreationEpoch < ov2.CreationEpoch
|
|
||||||
}
|
|
||||||
|
|
||||||
func contains(list []string, elem string) bool {
|
func contains(list []string, elem string) bool {
|
||||||
for _, item := range list {
|
for _, item := range list {
|
||||||
if elem == item {
|
if elem == item {
|
||||||
|
@ -267,7 +375,7 @@ func (n *layer) checkVersionsExist(ctx context.Context, bkt *api.BucketInfo, obj
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
if !contains(getExistedVersions(versions), obj.VersionID) {
|
if !contains(versions.existedVersions(), obj.VersionID) {
|
||||||
return nil, errors.GetAPIError(errors.ErrInvalidVersion)
|
return nil, errors.GetAPIError(errors.ErrInvalidVersion)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -7,6 +7,7 @@ import (
|
||||||
"crypto/sha256"
|
"crypto/sha256"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
|
@ -445,13 +446,13 @@ func TestNoVersioningDeleteObject(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestGetLastVersion(t *testing.T) {
|
func TestGetLastVersion(t *testing.T) {
|
||||||
obj1 := getTestObjectInfo(1, getOID(1), "", "", "")
|
obj1 := getTestObjectInfo(1, "", "", "")
|
||||||
obj1V2 := getTestObjectInfo(1, getOID(2), "", "", "")
|
obj1V2 := getTestObjectInfo(2, "", "", "")
|
||||||
obj2 := getTestObjectInfo(2, getOID(2), obj1.Version(), "", "")
|
obj2 := getTestObjectInfoEpoch(1, 2, obj1.Version(), "", "")
|
||||||
obj3 := getTestObjectInfo(3, getOID(3), joinVers(obj1, obj2), "", "*")
|
obj3 := getTestObjectInfoEpoch(1, 3, joinVers(obj1, obj2), "", "*")
|
||||||
obj4 := getTestObjectInfo(4, getOID(4), joinVers(obj1, obj2), obj2.Version(), obj2.Version())
|
obj4 := getTestObjectInfoEpoch(1, 4, joinVers(obj1, obj2), obj2.Version(), obj2.Version())
|
||||||
obj5 := getTestObjectInfo(5, getOID(5), obj1.Version(), obj1.Version(), obj1.Version())
|
obj5 := getTestObjectInfoEpoch(1, 5, obj1.Version(), obj1.Version(), obj1.Version())
|
||||||
obj6 := getTestObjectInfo(6, getOID(6), joinVers(obj1, obj2, obj3), obj3.Version(), obj3.Version())
|
obj6 := getTestObjectInfoEpoch(1, 6, joinVers(obj1, obj2, obj3), obj3.Version(), obj3.Version())
|
||||||
|
|
||||||
for _, tc := range []struct {
|
for _, tc := range []struct {
|
||||||
versions *objectVersions
|
versions *objectVersions
|
||||||
|
@ -510,9 +511,7 @@ func TestGetLastVersion(t *testing.T) {
|
||||||
objects: []*api.ObjectInfo{obj1, obj1V2},
|
objects: []*api.ObjectInfo{obj1, obj1V2},
|
||||||
addList: []string{obj1.Version(), obj1V2.Version()},
|
addList: []string{obj1.Version(), obj1V2.Version()},
|
||||||
},
|
},
|
||||||
// creation epochs are equal
|
expected: obj1V2,
|
||||||
// obj1 version/oid > obj1_1 version/oid
|
|
||||||
expected: obj1,
|
|
||||||
},
|
},
|
||||||
} {
|
} {
|
||||||
actualObjInfo := tc.versions.getLast()
|
actualObjInfo := tc.versions.getLast()
|
||||||
|
@ -521,10 +520,12 @@ func TestGetLastVersion(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestAppendVersions(t *testing.T) {
|
func TestAppendVersions(t *testing.T) {
|
||||||
obj1 := getTestObjectInfo(1, getOID(1), "", "", "")
|
obj1 := getTestObjectInfo(1, "", "", "")
|
||||||
obj2 := getTestObjectInfo(2, getOID(2), obj1.Version(), "", "")
|
obj2 := getTestObjectInfo(2, obj1.Version(), "", "")
|
||||||
obj3 := getTestObjectInfo(3, getOID(3), joinVers(obj1, obj2), "", "*")
|
obj3 := getTestObjectInfo(3, joinVers(obj1, obj2), "", "*")
|
||||||
obj4 := getTestObjectInfo(4, getOID(4), joinVers(obj1, obj2), obj2.Version(), obj2.Version())
|
obj4 := getTestObjectInfo(4, joinVers(obj1, obj2), obj2.Version(), obj2.Version())
|
||||||
|
obj5 := getTestObjectInfo(5, joinVers(obj1, obj2), "", "")
|
||||||
|
obj6 := getTestObjectInfo(6, joinVers(obj1, obj3), "", "")
|
||||||
|
|
||||||
for _, tc := range []struct {
|
for _, tc := range []struct {
|
||||||
versions *objectVersions
|
versions *objectVersions
|
||||||
|
@ -537,6 +538,7 @@ func TestAppendVersions(t *testing.T) {
|
||||||
expectedVersions: &objectVersions{
|
expectedVersions: &objectVersions{
|
||||||
objects: []*api.ObjectInfo{obj1},
|
objects: []*api.ObjectInfo{obj1},
|
||||||
addList: []string{obj1.Version()},
|
addList: []string{obj1.Version()},
|
||||||
|
isSorted: true,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
@ -545,6 +547,7 @@ func TestAppendVersions(t *testing.T) {
|
||||||
expectedVersions: &objectVersions{
|
expectedVersions: &objectVersions{
|
||||||
objects: []*api.ObjectInfo{obj1, obj2},
|
objects: []*api.ObjectInfo{obj1, obj2},
|
||||||
addList: []string{obj1.Version(), obj2.Version()},
|
addList: []string{obj1.Version(), obj2.Version()},
|
||||||
|
isSorted: true,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
@ -553,6 +556,7 @@ func TestAppendVersions(t *testing.T) {
|
||||||
expectedVersions: &objectVersions{
|
expectedVersions: &objectVersions{
|
||||||
objects: []*api.ObjectInfo{obj1, obj2, obj3},
|
objects: []*api.ObjectInfo{obj1, obj2, obj3},
|
||||||
addList: []string{obj1.Version(), obj2.Version(), obj3.Version()},
|
addList: []string{obj1.Version(), obj2.Version(), obj3.Version()},
|
||||||
|
isSorted: true,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
@ -562,14 +566,61 @@ func TestAppendVersions(t *testing.T) {
|
||||||
objects: []*api.ObjectInfo{obj1, obj2, obj4},
|
objects: []*api.ObjectInfo{obj1, obj2, obj4},
|
||||||
addList: []string{obj1.Version(), obj2.Version(), obj4.Version()},
|
addList: []string{obj1.Version(), obj2.Version(), obj4.Version()},
|
||||||
delList: []string{obj2.Version()},
|
delList: []string{obj2.Version()},
|
||||||
|
isSorted: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
versions: &objectVersions{objects: []*api.ObjectInfo{obj5}},
|
||||||
|
objectToAdd: obj6,
|
||||||
|
expectedVersions: &objectVersions{
|
||||||
|
objects: []*api.ObjectInfo{obj5, obj6},
|
||||||
|
addList: []string{obj1.Version(), obj2.Version(), obj3.Version(), obj5.Version(), obj6.Version()},
|
||||||
|
isSorted: true,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
} {
|
} {
|
||||||
tc.versions.appendVersion(tc.objectToAdd)
|
tc.versions.appendVersion(tc.objectToAdd)
|
||||||
|
tc.versions.sort()
|
||||||
require.Equal(t, tc.expectedVersions, tc.versions)
|
require.Equal(t, tc.expectedVersions, tc.versions)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestSortAddHeaders(t *testing.T) {
|
||||||
|
obj1 := getTestObjectInfo(1, "", "", "")
|
||||||
|
obj2 := getTestObjectInfo(2, "", "", "")
|
||||||
|
obj3 := getTestObjectInfo(3, "", "", "")
|
||||||
|
obj4 := getTestObjectInfo(4, "", "", "")
|
||||||
|
obj5 := getTestObjectInfo(5, "", "", "")
|
||||||
|
|
||||||
|
obj6 := getTestObjectInfoEpoch(1, 6, joinVers(obj1, obj2, obj3), "", "")
|
||||||
|
obj7 := getTestObjectInfoEpoch(1, 7, joinVers(obj1, obj4), "", "")
|
||||||
|
obj8 := getTestObjectInfoEpoch(1, 8, joinVers(obj5), "", "")
|
||||||
|
obj9 := getTestObjectInfoEpoch(1, 8, joinVers(obj1, obj5), "", "")
|
||||||
|
obj10 := getTestObjectInfo(11, "", "", "")
|
||||||
|
obj11 := getTestObjectInfo(10, joinVers(obj10), "", "")
|
||||||
|
obj12 := getTestObjectInfo(9, joinVers(obj10, obj11), "", "")
|
||||||
|
|
||||||
|
for _, tc := range []struct {
|
||||||
|
versions *objectVersions
|
||||||
|
expectedAddHeaders string
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
versions: &objectVersions{objects: []*api.ObjectInfo{obj6, obj7, obj8}},
|
||||||
|
expectedAddHeaders: joinVers(obj1, obj2, obj3, obj4, obj5, obj6, obj7, obj8),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
versions: &objectVersions{objects: []*api.ObjectInfo{obj7, obj9}},
|
||||||
|
expectedAddHeaders: joinVers(obj1, obj4, obj5, obj7, obj9),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
versions: &objectVersions{objects: []*api.ObjectInfo{obj11, obj10, obj12}},
|
||||||
|
expectedAddHeaders: joinVers(obj10, obj11, obj12),
|
||||||
|
},
|
||||||
|
} {
|
||||||
|
require.Equal(t, tc.expectedAddHeaders, tc.versions.getAddHeader())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func joinVers(objs ...*api.ObjectInfo) string {
|
func joinVers(objs ...*api.ObjectInfo) string {
|
||||||
if len(objs) == 0 {
|
if len(objs) == 0 {
|
||||||
return ""
|
return ""
|
||||||
|
@ -584,14 +635,14 @@ func joinVers(objs ...*api.ObjectInfo) string {
|
||||||
}
|
}
|
||||||
|
|
||||||
func getOID(id byte) *object.ID {
|
func getOID(id byte) *object.ID {
|
||||||
b := make([]byte, 32)
|
b := [32]byte{}
|
||||||
b[0] = id
|
b[31] = id
|
||||||
oid := object.NewID()
|
oid := object.NewID()
|
||||||
oid.SetSHA256(sha256.Sum256(b))
|
oid.SetSHA256(b)
|
||||||
return oid
|
return oid
|
||||||
}
|
}
|
||||||
|
|
||||||
func getTestObjectInfo(epoch uint64, oid *object.ID, addAttr, delAttr, delMarkAttr string) *api.ObjectInfo {
|
func getTestObjectInfo(id byte, addAttr, delAttr, delMarkAttr string) *api.ObjectInfo {
|
||||||
headers := make(map[string]string)
|
headers := make(map[string]string)
|
||||||
if addAttr != "" {
|
if addAttr != "" {
|
||||||
headers[versionsAddAttr] = addAttr
|
headers[versionsAddAttr] = addAttr
|
||||||
|
@ -604,8 +655,14 @@ func getTestObjectInfo(epoch uint64, oid *object.ID, addAttr, delAttr, delMarkAt
|
||||||
}
|
}
|
||||||
|
|
||||||
return &api.ObjectInfo{
|
return &api.ObjectInfo{
|
||||||
ID: oid,
|
ID: getOID(id),
|
||||||
CreationEpoch: epoch,
|
Name: strconv.Itoa(int(id)),
|
||||||
Headers: headers,
|
Headers: headers,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func getTestObjectInfoEpoch(epoch uint64, id byte, addAttr, delAttr, delMarkAttr string) *api.ObjectInfo {
|
||||||
|
obj := getTestObjectInfo(id, addAttr, delAttr, delMarkAttr)
|
||||||
|
obj.CreationEpoch = epoch
|
||||||
|
return obj
|
||||||
|
}
|
||||||
|
|
|
@ -377,34 +377,34 @@ Compatibility: 9/8/11
|
||||||
|
|
||||||
## Versioning
|
## Versioning
|
||||||
|
|
||||||
Compatibility: 1/24/26
|
Compatibility: 11/24/26
|
||||||
|
|
||||||
| | Test | s3-gw | aws s3 |
|
| | Test | s3-gw | aws s3 |
|
||||||
|----|---------------------------------------------------------------------------------------------|-------|--------|
|
|----|---------------------------------------------------------------------------------------------|-------|--------|
|
||||||
| 1 | s3tests_boto3.functional.test_s3.test_versioning_bucket_create_suspend | ok | ok |
|
| 1 | s3tests_boto3.functional.test_s3.test_versioning_bucket_create_suspend | ok | ok |
|
||||||
| 2 | s3tests_boto3.functional.test_s3.test_versioning_obj_create_read_remove | ERROR | ok |
|
| 2 | s3tests_boto3.functional.test_s3.test_versioning_obj_create_read_remove | ok | ok |
|
||||||
| 3 | s3tests_boto3.functional.test_s3.test_versioning_obj_create_read_remove_head | ERROR | ok |
|
| 3 | s3tests_boto3.functional.test_s3.test_versioning_obj_create_read_remove_head | ERROR | ok |
|
||||||
| 4 | s3tests_boto3.functional.test_s3.test_versioning_obj_plain_null_version_removal | ERROR | ok |
|
| 4 | s3tests_boto3.functional.test_s3.test_versioning_obj_plain_null_version_removal | ERROR | ok |
|
||||||
| 5 | s3tests_boto3.functional.test_s3.test_versioning_obj_plain_null_version_overwrite | ERROR | ok |
|
| 5 | s3tests_boto3.functional.test_s3.test_versioning_obj_plain_null_version_overwrite | ERROR | ok |
|
||||||
| 6 | s3tests_boto3.functional.test_s3.test_versioning_obj_plain_null_version_overwrite_suspended | ERROR | ok |
|
| 6 | s3tests_boto3.functional.test_s3.test_versioning_obj_plain_null_version_overwrite_suspended | ERROR | ok |
|
||||||
| 7 | s3tests_boto3.functional.test_s3.test_versioning_obj_suspend_versions | ERROR | ok |
|
| 7 | s3tests_boto3.functional.test_s3.test_versioning_obj_suspend_versions | ERROR | ok |
|
||||||
| 8 | s3tests_boto3.functional.test_s3.test_versioning_obj_create_versions_remove_all | ERROR | ok |
|
| 8 | s3tests_boto3.functional.test_s3.test_versioning_obj_create_versions_remove_all | ok | ok |
|
||||||
| 9 | s3tests_boto3.functional.test_s3.test_versioning_obj_create_versions_remove_special_names | ERROR | ok |
|
| 9 | s3tests_boto3.functional.test_s3.test_versioning_obj_create_versions_remove_special_names | ok | ok |
|
||||||
| 10 | s3tests_boto3.functional.test_s3.test_versioning_obj_create_overwrite_multipart | ERROR | ok |
|
| 10 | s3tests_boto3.functional.test_s3.test_versioning_obj_create_overwrite_multipart | ERROR | ok |
|
||||||
| 11 | s3tests_boto3.functional.test_s3.test_versioning_obj_list_marker | ERROR | ok |
|
| 11 | s3tests_boto3.functional.test_s3.test_versioning_obj_list_marker | ok | ok |
|
||||||
| 12 | s3tests_boto3.functional.test_s3.test_versioning_copy_obj_version | ERROR | ok |
|
| 12 | s3tests_boto3.functional.test_s3.test_versioning_copy_obj_version | ok | ok |
|
||||||
| 13 | s3tests_boto3.functional.test_s3.test_versioning_multi_object_delete | ERROR | ok |
|
| 13 | s3tests_boto3.functional.test_s3.test_versioning_multi_object_delete | ok | ok |
|
||||||
| 14 | s3tests_boto3.functional.test_s3.test_versioning_multi_object_delete_with_marker | ERROR | ok |
|
| 14 | s3tests_boto3.functional.test_s3.test_versioning_multi_object_delete_with_marker | ok | ok |
|
||||||
| 15 | s3tests_boto3.functional.test_s3.test_versioning_multi_object_delete_with_marker_create | ERROR | ok |
|
| 15 | s3tests_boto3.functional.test_s3.test_versioning_multi_object_delete_with_marker_create | ERROR | ok |
|
||||||
| 16 | s3tests_boto3.functional.test_s3.test_versioned_object_acl | ERROR | FAIL |
|
| 16 | s3tests_boto3.functional.test_s3.test_versioned_object_acl | ERROR | FAIL |
|
||||||
| 17 | s3tests_boto3.functional.test_s3.test_versioned_object_acl_no_version_specified | ERROR | FAIL |
|
| 17 | s3tests_boto3.functional.test_s3.test_versioned_object_acl_no_version_specified | ERROR | FAIL |
|
||||||
| 18 | s3tests_boto3.functional.test_s3.test_versioned_concurrent_object_create_concurrent_remove | ERROR | ok |
|
| 18 | s3tests_boto3.functional.test_s3.test_versioned_concurrent_object_create_concurrent_remove | ERROR | ok |
|
||||||
| 19 | s3tests_boto3.functional.test_s3.test_versioned_concurrent_object_create_and_remove | ERROR | ok |
|
| 19 | s3tests_boto3.functional.test_s3.test_versioned_concurrent_object_create_and_remove | ERROR | ok |
|
||||||
| 20 | s3tests_boto3.functional.test_s3.test_versioning_bucket_atomic_upload_return_version_id | ERROR | ok |
|
| 20 | s3tests_boto3.functional.test_s3.test_versioning_bucket_atomic_upload_return_version_id | ok | ok |
|
||||||
| 21 | s3tests_boto3.functional.test_s3.test_versioning_bucket_multipart_upload_return_version_id | ERROR | ok |
|
| 21 | s3tests_boto3.functional.test_s3.test_versioning_bucket_multipart_upload_return_version_id | ERROR | ok |
|
||||||
| 22 | s3tests_boto3.functional.test_s3.test_bucket_list_return_data_versioning | ERROR | ok |
|
| 22 | s3tests_boto3.functional.test_s3.test_bucket_list_return_data_versioning | ERROR | ok |
|
||||||
| 23 | s3tests_boto3.functional.test_s3.test_object_copy_versioned_bucket | ERROR | ok |
|
| 23 | s3tests_boto3.functional.test_s3.test_object_copy_versioned_bucket | ok | ok |
|
||||||
| 24 | s3tests_boto3.functional.test_s3.test_object_copy_versioned_url_encoding | ERROR | ok |
|
| 24 | s3tests_boto3.functional.test_s3.test_object_copy_versioned_url_encoding | ok | ok |
|
||||||
| 25 | s3tests_boto3.functional.test_s3.test_object_copy_versioning_multipart_upload | ERROR | ok |
|
| 25 | s3tests_boto3.functional.test_s3.test_object_copy_versioning_multipart_upload | ERROR | ok |
|
||||||
| 26 | s3tests_boto3.functional.test_s3.test_multipart_copy_versioned | ERROR | ok |
|
| 26 | s3tests_boto3.functional.test_s3.test_multipart_copy_versioned | ERROR | ok |
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue