Delete Non-current versions (#1735)
- Delete Non-current API - Delete non current modal implementation Signed-off-by: Benjamin Perez <benjamin@bexsoft.net>
This commit is contained in:
@@ -1315,6 +1315,11 @@ func init() {
|
||||
"type": "boolean",
|
||||
"name": "all_versions",
|
||||
"in": "query"
|
||||
},
|
||||
{
|
||||
"type": "boolean",
|
||||
"name": "non_current_versions",
|
||||
"in": "query"
|
||||
}
|
||||
],
|
||||
"responses": {
|
||||
@@ -7799,6 +7804,11 @@ func init() {
|
||||
"type": "boolean",
|
||||
"name": "all_versions",
|
||||
"in": "query"
|
||||
},
|
||||
{
|
||||
"type": "boolean",
|
||||
"name": "non_current_versions",
|
||||
"in": "query"
|
||||
}
|
||||
],
|
||||
"responses": {
|
||||
|
||||
@@ -59,6 +59,10 @@ type DeleteObjectParams struct {
|
||||
In: path
|
||||
*/
|
||||
BucketName string
|
||||
/*
|
||||
In: query
|
||||
*/
|
||||
NonCurrentVersions *bool
|
||||
/*
|
||||
Required: true
|
||||
In: query
|
||||
@@ -95,6 +99,11 @@ func (o *DeleteObjectParams) BindRequest(r *http.Request, route *middleware.Matc
|
||||
res = append(res, err)
|
||||
}
|
||||
|
||||
qNonCurrentVersions, qhkNonCurrentVersions, _ := qs.GetOK("non_current_versions")
|
||||
if err := o.bindNonCurrentVersions(qNonCurrentVersions, qhkNonCurrentVersions, route.Formats); err != nil {
|
||||
res = append(res, err)
|
||||
}
|
||||
|
||||
qPath, qhkPath, _ := qs.GetOK("path")
|
||||
if err := o.bindPath(qPath, qhkPath, route.Formats); err != nil {
|
||||
res = append(res, err)
|
||||
@@ -152,6 +161,29 @@ func (o *DeleteObjectParams) bindBucketName(rawData []string, hasKey bool, forma
|
||||
return nil
|
||||
}
|
||||
|
||||
// bindNonCurrentVersions binds and validates parameter NonCurrentVersions from query.
|
||||
func (o *DeleteObjectParams) bindNonCurrentVersions(rawData []string, hasKey bool, formats strfmt.Registry) error {
|
||||
var raw string
|
||||
if len(rawData) > 0 {
|
||||
raw = rawData[len(rawData)-1]
|
||||
}
|
||||
|
||||
// Required: false
|
||||
// AllowEmptyValue: false
|
||||
|
||||
if raw == "" { // empty values pass all other validations
|
||||
return nil
|
||||
}
|
||||
|
||||
value, err := swag.ConvertBool(raw)
|
||||
if err != nil {
|
||||
return errors.InvalidType("non_current_versions", "query", "bool", raw)
|
||||
}
|
||||
o.NonCurrentVersions = &value
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// bindPath binds and validates parameter Path from query.
|
||||
func (o *DeleteObjectParams) bindPath(rawData []string, hasKey bool, formats strfmt.Registry) error {
|
||||
if !hasKey {
|
||||
|
||||
@@ -35,10 +35,11 @@ import (
|
||||
type DeleteObjectURL struct {
|
||||
BucketName string
|
||||
|
||||
AllVersions *bool
|
||||
Path string
|
||||
Recursive *bool
|
||||
VersionID *string
|
||||
AllVersions *bool
|
||||
NonCurrentVersions *bool
|
||||
Path string
|
||||
Recursive *bool
|
||||
VersionID *string
|
||||
|
||||
_basePath string
|
||||
// avoid unkeyed usage
|
||||
@@ -89,6 +90,14 @@ func (o *DeleteObjectURL) Build() (*url.URL, error) {
|
||||
qs.Set("all_versions", allVersionsQ)
|
||||
}
|
||||
|
||||
var nonCurrentVersionsQ string
|
||||
if o.NonCurrentVersions != nil {
|
||||
nonCurrentVersionsQ = swag.FormatBool(*o.NonCurrentVersions)
|
||||
}
|
||||
if nonCurrentVersionsQ != "" {
|
||||
qs.Set("non_current_versions", nonCurrentVersionsQ)
|
||||
}
|
||||
|
||||
pathQ := o.Path
|
||||
if pathQ != "" {
|
||||
qs.Set("path", pathQ)
|
||||
|
||||
@@ -570,6 +570,7 @@ func getDeleteObjectResponse(session *models.Principal, params user_api.DeleteOb
|
||||
var rec bool
|
||||
var version string
|
||||
var allVersions bool
|
||||
var nonCurrentVersions bool
|
||||
if params.Recursive != nil {
|
||||
rec = *params.Recursive
|
||||
}
|
||||
@@ -579,7 +580,16 @@ func getDeleteObjectResponse(session *models.Principal, params user_api.DeleteOb
|
||||
if params.AllVersions != nil {
|
||||
allVersions = *params.AllVersions
|
||||
}
|
||||
err = deleteObjects(ctx, mcClient, params.BucketName, prefix, version, rec, allVersions)
|
||||
if params.NonCurrentVersions != nil {
|
||||
nonCurrentVersions = *params.NonCurrentVersions
|
||||
}
|
||||
|
||||
if allVersions && nonCurrentVersions {
|
||||
err := errors.New("cannot set delete all versions and delete non-current versions flags at the same time")
|
||||
return prepareError(err)
|
||||
}
|
||||
|
||||
err = deleteObjects(ctx, mcClient, params.BucketName, prefix, version, rec, allVersions, nonCurrentVersions)
|
||||
if err != nil {
|
||||
return prepareError(err)
|
||||
}
|
||||
@@ -606,7 +616,7 @@ func getDeleteMultiplePathsResponse(session *models.Principal, params user_api.D
|
||||
// create a mc S3Client interface implementation
|
||||
// defining the client to be used
|
||||
mcClient := mcClient{client: s3Client}
|
||||
err = deleteObjects(ctx, mcClient, params.BucketName, params.Files[i].Path, version, params.Files[i].Recursive, allVersions)
|
||||
err = deleteObjects(ctx, mcClient, params.BucketName, params.Files[i].Path, version, params.Files[i].Recursive, allVersions, false)
|
||||
if err != nil {
|
||||
return prepareError(err)
|
||||
}
|
||||
@@ -615,7 +625,15 @@ func getDeleteMultiplePathsResponse(session *models.Principal, params user_api.D
|
||||
}
|
||||
|
||||
// deleteObjects deletes either a single object or multiple objects based on recursive flag
|
||||
func deleteObjects(ctx context.Context, client MCClient, bucket string, path string, versionID string, recursive bool, allVersions bool) error {
|
||||
func deleteObjects(ctx context.Context, client MCClient, bucket string, path string, versionID string, recursive bool, allVersions bool, nonCurrentVersionsOnly bool) error {
|
||||
// Delete All non-Current versions only.
|
||||
if nonCurrentVersionsOnly {
|
||||
if err := deleteNonCurrentVersions(ctx, client, bucket, path); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
if allVersions {
|
||||
if err := deleteMultipleObjects(ctx, client, recursive, true); err != nil {
|
||||
return err
|
||||
@@ -718,6 +736,25 @@ func deleteSingleObject(ctx context.Context, client MCClient, bucket, object str
|
||||
return nil
|
||||
}
|
||||
|
||||
func deleteNonCurrentVersions(ctx context.Context, client MCClient, bucket, path string) error {
|
||||
// Get current object versions
|
||||
for lsObj := range client.list(ctx, mc.ListOptions{WithDeleteMarkers: true, WithOlderVersions: true, Recursive: true}) {
|
||||
if lsObj.Err != nil {
|
||||
return errors.New(lsObj.Err.String())
|
||||
}
|
||||
|
||||
if !lsObj.IsLatest {
|
||||
err := deleteSingleObject(ctx, client, bucket, path, lsObj.VersionID)
|
||||
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func getUploadObjectResponse(session *models.Principal, params user_api.PostBucketsBucketNameObjectsUploadParams) *models.Error {
|
||||
ctx := context.Background()
|
||||
mClient, err := newMinioClient(session)
|
||||
|
||||
@@ -583,6 +583,7 @@ func Test_deleteObjects(t *testing.T) {
|
||||
path string
|
||||
versionID string
|
||||
recursive bool
|
||||
nonCurrent bool
|
||||
listFunc func(ctx context.Context, opts mc.ListOptions) <-chan *mc.ClientContent
|
||||
removeFunc func(ctx context.Context, isIncomplete, isRemoveBucket, isBypass bool, contentCh <-chan *mc.ClientContent) <-chan mc.RemoveResult
|
||||
}
|
||||
@@ -594,9 +595,10 @@ func Test_deleteObjects(t *testing.T) {
|
||||
{
|
||||
test: "Remove single object",
|
||||
args: args{
|
||||
path: "obj.txt",
|
||||
versionID: "",
|
||||
recursive: false,
|
||||
path: "obj.txt",
|
||||
versionID: "",
|
||||
recursive: false,
|
||||
nonCurrent: false,
|
||||
removeFunc: func(ctx context.Context, isIncomplete, isRemoveBucket, isBypass bool, contentCh <-chan *mc.ClientContent) <-chan mc.RemoveResult {
|
||||
resultCh := make(chan mc.RemoveResult, 1)
|
||||
resultCh <- mc.RemoveResult{Err: nil}
|
||||
@@ -609,9 +611,10 @@ func Test_deleteObjects(t *testing.T) {
|
||||
{
|
||||
test: "Error on Remove single object",
|
||||
args: args{
|
||||
path: "obj.txt",
|
||||
versionID: "",
|
||||
recursive: false,
|
||||
path: "obj.txt",
|
||||
versionID: "",
|
||||
recursive: false,
|
||||
nonCurrent: false,
|
||||
removeFunc: func(ctx context.Context, isIncomplete, isRemoveBucket, isBypass bool, contentCh <-chan *mc.ClientContent) <-chan mc.RemoveResult {
|
||||
resultCh := make(chan mc.RemoveResult, 1)
|
||||
resultCh <- mc.RemoveResult{Err: probe.NewError(errors.New("probe error"))}
|
||||
@@ -624,9 +627,10 @@ func Test_deleteObjects(t *testing.T) {
|
||||
{
|
||||
test: "Remove multiple objects",
|
||||
args: args{
|
||||
path: "path/",
|
||||
versionID: "",
|
||||
recursive: true,
|
||||
path: "path/",
|
||||
versionID: "",
|
||||
recursive: true,
|
||||
nonCurrent: false,
|
||||
removeFunc: func(ctx context.Context, isIncomplete, isRemoveBucket, isBypass bool, contentCh <-chan *mc.ClientContent) <-chan mc.RemoveResult {
|
||||
resultCh := make(chan mc.RemoveResult, 1)
|
||||
resultCh <- mc.RemoveResult{Err: nil}
|
||||
@@ -647,9 +651,10 @@ func Test_deleteObjects(t *testing.T) {
|
||||
// while deleting multiple objects
|
||||
test: "Error on Remove multiple objects 1",
|
||||
args: args{
|
||||
path: "path/",
|
||||
versionID: "",
|
||||
recursive: true,
|
||||
path: "path/",
|
||||
versionID: "",
|
||||
recursive: true,
|
||||
nonCurrent: false,
|
||||
removeFunc: func(ctx context.Context, isIncomplete, isRemoveBucket, isBypass bool, contentCh <-chan *mc.ClientContent) <-chan mc.RemoveResult {
|
||||
resultCh := make(chan mc.RemoveResult, 1)
|
||||
resultCh <- mc.RemoveResult{Err: nil}
|
||||
@@ -670,9 +675,58 @@ func Test_deleteObjects(t *testing.T) {
|
||||
// while deleting multiple objects
|
||||
test: "Error on Remove multiple objects 2",
|
||||
args: args{
|
||||
path: "path/",
|
||||
versionID: "",
|
||||
recursive: true,
|
||||
path: "path/",
|
||||
versionID: "",
|
||||
recursive: true,
|
||||
nonCurrent: false,
|
||||
removeFunc: func(ctx context.Context, isIncomplete, isRemoveBucket, isBypass bool, contentCh <-chan *mc.ClientContent) <-chan mc.RemoveResult {
|
||||
resultCh := make(chan mc.RemoveResult, 1)
|
||||
resultCh <- mc.RemoveResult{Err: probe.NewError(errors.New("probe error"))}
|
||||
close(resultCh)
|
||||
return resultCh
|
||||
},
|
||||
listFunc: func(ctx context.Context, opts mc.ListOptions) <-chan *mc.ClientContent {
|
||||
ch := make(chan *mc.ClientContent, 1)
|
||||
ch <- &mc.ClientContent{}
|
||||
close(ch)
|
||||
return ch
|
||||
},
|
||||
},
|
||||
wantError: errors.New("probe error"),
|
||||
},
|
||||
{
|
||||
// Description handle error when error happens on remove function
|
||||
// while deleting multiple objects
|
||||
test: "Remove non current objects",
|
||||
args: args{
|
||||
path: "path/",
|
||||
versionID: "",
|
||||
recursive: true,
|
||||
nonCurrent: true,
|
||||
removeFunc: func(ctx context.Context, isIncomplete, isRemoveBucket, isBypass bool, contentCh <-chan *mc.ClientContent) <-chan mc.RemoveResult {
|
||||
resultCh := make(chan mc.RemoveResult, 1)
|
||||
resultCh <- mc.RemoveResult{Err: nil}
|
||||
close(resultCh)
|
||||
return resultCh
|
||||
},
|
||||
listFunc: func(ctx context.Context, opts mc.ListOptions) <-chan *mc.ClientContent {
|
||||
ch := make(chan *mc.ClientContent, 1)
|
||||
ch <- &mc.ClientContent{}
|
||||
close(ch)
|
||||
return ch
|
||||
},
|
||||
},
|
||||
wantError: nil,
|
||||
},
|
||||
{
|
||||
// Description handle error when error happens on remove function
|
||||
// while deleting multiple objects
|
||||
test: "Error deleting non current objects",
|
||||
args: args{
|
||||
path: "path/",
|
||||
versionID: "",
|
||||
recursive: true,
|
||||
nonCurrent: true,
|
||||
removeFunc: func(ctx context.Context, isIncomplete, isRemoveBucket, isBypass bool, contentCh <-chan *mc.ClientContent) <-chan mc.RemoveResult {
|
||||
resultCh := make(chan mc.RemoveResult, 1)
|
||||
resultCh <- mc.RemoveResult{Err: probe.NewError(errors.New("probe error"))}
|
||||
@@ -695,7 +749,7 @@ func Test_deleteObjects(t *testing.T) {
|
||||
t.Run(tt.test, func(t *testing.T) {
|
||||
mcListMock = tt.args.listFunc
|
||||
mcRemoveMock = tt.args.removeFunc
|
||||
err := deleteObjects(ctx, s3Client1, tt.args.bucket, tt.args.path, tt.args.versionID, tt.args.recursive, false)
|
||||
err := deleteObjects(ctx, s3Client1, tt.args.bucket, tt.args.path, tt.args.versionID, tt.args.recursive, false, tt.args.nonCurrent)
|
||||
if err == nil && tt.wantError != nil {
|
||||
t.Errorf("deleteObjects() error: %v, wantErr: %v", err, tt.wantError)
|
||||
} else if err != nil && tt.wantError == nil {
|
||||
|
||||
Reference in New Issue
Block a user