Added delete governance bypass option when deleting file versions (#2536)
Signed-off-by: Benjamin Perez <benjamin@bexsoft.net>
This commit is contained in:
@@ -1006,6 +1006,11 @@ func init() {
|
||||
"name": "all_versions",
|
||||
"in": "query"
|
||||
},
|
||||
{
|
||||
"type": "boolean",
|
||||
"name": "bypass",
|
||||
"in": "query"
|
||||
},
|
||||
{
|
||||
"name": "files",
|
||||
"in": "body",
|
||||
@@ -1539,6 +1544,11 @@ func init() {
|
||||
"type": "boolean",
|
||||
"name": "non_current_versions",
|
||||
"in": "query"
|
||||
},
|
||||
{
|
||||
"type": "boolean",
|
||||
"name": "bypass",
|
||||
"in": "query"
|
||||
}
|
||||
],
|
||||
"responses": {
|
||||
@@ -9438,6 +9448,11 @@ func init() {
|
||||
"name": "all_versions",
|
||||
"in": "query"
|
||||
},
|
||||
{
|
||||
"type": "boolean",
|
||||
"name": "bypass",
|
||||
"in": "query"
|
||||
},
|
||||
{
|
||||
"name": "files",
|
||||
"in": "body",
|
||||
@@ -9971,6 +9986,11 @@ func init() {
|
||||
"type": "boolean",
|
||||
"name": "non_current_versions",
|
||||
"in": "query"
|
||||
},
|
||||
{
|
||||
"type": "boolean",
|
||||
"name": "bypass",
|
||||
"in": "query"
|
||||
}
|
||||
],
|
||||
"responses": {
|
||||
|
||||
@@ -61,6 +61,10 @@ type DeleteMultipleObjectsParams struct {
|
||||
In: path
|
||||
*/
|
||||
BucketName string
|
||||
/*
|
||||
In: query
|
||||
*/
|
||||
Bypass *bool
|
||||
/*
|
||||
Required: true
|
||||
In: body
|
||||
@@ -89,6 +93,11 @@ func (o *DeleteMultipleObjectsParams) BindRequest(r *http.Request, route *middle
|
||||
res = append(res, err)
|
||||
}
|
||||
|
||||
qBypass, qhkBypass, _ := qs.GetOK("bypass")
|
||||
if err := o.bindBypass(qBypass, qhkBypass, route.Formats); err != nil {
|
||||
res = append(res, err)
|
||||
}
|
||||
|
||||
if runtime.HasBody(r) {
|
||||
defer r.Body.Close()
|
||||
var body []*models.DeleteFile
|
||||
@@ -160,3 +169,26 @@ func (o *DeleteMultipleObjectsParams) bindBucketName(rawData []string, hasKey bo
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// bindBypass binds and validates parameter Bypass from query.
|
||||
func (o *DeleteMultipleObjectsParams) bindBypass(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("bypass", "query", "bool", raw)
|
||||
}
|
||||
o.Bypass = &value
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -36,6 +36,7 @@ type DeleteMultipleObjectsURL struct {
|
||||
BucketName string
|
||||
|
||||
AllVersions *bool
|
||||
Bypass *bool
|
||||
|
||||
_basePath string
|
||||
// avoid unkeyed usage
|
||||
@@ -86,6 +87,14 @@ func (o *DeleteMultipleObjectsURL) Build() (*url.URL, error) {
|
||||
qs.Set("all_versions", allVersionsQ)
|
||||
}
|
||||
|
||||
var bypassQ string
|
||||
if o.Bypass != nil {
|
||||
bypassQ = swag.FormatBool(*o.Bypass)
|
||||
}
|
||||
if bypassQ != "" {
|
||||
qs.Set("bypass", bypassQ)
|
||||
}
|
||||
|
||||
_result.RawQuery = qs.Encode()
|
||||
|
||||
return &_result, nil
|
||||
|
||||
@@ -62,6 +62,10 @@ type DeleteObjectParams struct {
|
||||
/*
|
||||
In: query
|
||||
*/
|
||||
Bypass *bool
|
||||
/*
|
||||
In: query
|
||||
*/
|
||||
NonCurrentVersions *bool
|
||||
/*
|
||||
Required: true
|
||||
@@ -99,6 +103,11 @@ func (o *DeleteObjectParams) BindRequest(r *http.Request, route *middleware.Matc
|
||||
res = append(res, err)
|
||||
}
|
||||
|
||||
qBypass, qhkBypass, _ := qs.GetOK("bypass")
|
||||
if err := o.bindBypass(qBypass, qhkBypass, route.Formats); err != nil {
|
||||
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)
|
||||
@@ -161,6 +170,29 @@ func (o *DeleteObjectParams) bindBucketName(rawData []string, hasKey bool, forma
|
||||
return nil
|
||||
}
|
||||
|
||||
// bindBypass binds and validates parameter Bypass from query.
|
||||
func (o *DeleteObjectParams) bindBypass(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("bypass", "query", "bool", raw)
|
||||
}
|
||||
o.Bypass = &value
|
||||
|
||||
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
|
||||
|
||||
@@ -36,6 +36,7 @@ type DeleteObjectURL struct {
|
||||
BucketName string
|
||||
|
||||
AllVersions *bool
|
||||
Bypass *bool
|
||||
NonCurrentVersions *bool
|
||||
Path string
|
||||
Recursive *bool
|
||||
@@ -90,6 +91,14 @@ func (o *DeleteObjectURL) Build() (*url.URL, error) {
|
||||
qs.Set("all_versions", allVersionsQ)
|
||||
}
|
||||
|
||||
var bypassQ string
|
||||
if o.Bypass != nil {
|
||||
bypassQ = swag.FormatBool(*o.Bypass)
|
||||
}
|
||||
if bypassQ != "" {
|
||||
qs.Set("bypass", bypassQ)
|
||||
}
|
||||
|
||||
var nonCurrentVersionsQ string
|
||||
if o.NonCurrentVersions != nil {
|
||||
nonCurrentVersionsQ = swag.FormatBool(*o.NonCurrentVersions)
|
||||
|
||||
@@ -579,7 +579,7 @@ func getDownloadFolderResponse(session *models.Principal, params objectApi.Downl
|
||||
}), nil
|
||||
}
|
||||
|
||||
// getDeleteObjectResponse returns whether there was an errors on deletion of object
|
||||
// getDeleteObjectResponse returns whether there was an error on deletion of object
|
||||
func getDeleteObjectResponse(session *models.Principal, params objectApi.DeleteObjectParams) *models.Error {
|
||||
ctx, cancel := context.WithCancel(params.HTTPRequest.Context())
|
||||
defer cancel()
|
||||
@@ -603,6 +603,7 @@ func getDeleteObjectResponse(session *models.Principal, params objectApi.DeleteO
|
||||
var version string
|
||||
var allVersions bool
|
||||
var nonCurrentVersions bool
|
||||
var bypass bool
|
||||
if params.Recursive != nil {
|
||||
rec = *params.Recursive
|
||||
}
|
||||
@@ -615,28 +616,35 @@ func getDeleteObjectResponse(session *models.Principal, params objectApi.DeleteO
|
||||
if params.NonCurrentVersions != nil {
|
||||
nonCurrentVersions = *params.NonCurrentVersions
|
||||
}
|
||||
if params.Bypass != nil {
|
||||
bypass = *params.Bypass
|
||||
}
|
||||
|
||||
if allVersions && nonCurrentVersions {
|
||||
err := errors.New("cannot set delete all versions and delete non-current versions flags at the same time")
|
||||
return ErrorWithContext(ctx, err)
|
||||
}
|
||||
|
||||
err = deleteObjects(ctx, mcClient, params.BucketName, prefix, version, rec, allVersions, nonCurrentVersions)
|
||||
err = deleteObjects(ctx, mcClient, params.BucketName, prefix, version, rec, allVersions, nonCurrentVersions, bypass)
|
||||
if err != nil {
|
||||
return ErrorWithContext(ctx, err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// getDeleteMultiplePathsResponse returns whether there was an errors on deletion of any object
|
||||
// getDeleteMultiplePathsResponse returns whether there was an error on deletion of any object
|
||||
func getDeleteMultiplePathsResponse(session *models.Principal, params objectApi.DeleteMultipleObjectsParams) *models.Error {
|
||||
ctx, cancel := context.WithCancel(params.HTTPRequest.Context())
|
||||
defer cancel()
|
||||
var version string
|
||||
var allVersions bool
|
||||
var bypass bool
|
||||
if params.AllVersions != nil {
|
||||
allVersions = *params.AllVersions
|
||||
}
|
||||
if params.Bypass != nil {
|
||||
bypass = *params.Bypass
|
||||
}
|
||||
for i := 0; i < len(params.Files); i++ {
|
||||
if params.Files[i].VersionID != "" {
|
||||
version = params.Files[i].VersionID
|
||||
@@ -649,7 +657,7 @@ func getDeleteMultiplePathsResponse(session *models.Principal, params objectApi.
|
||||
// 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, false)
|
||||
err = deleteObjects(ctx, mcClient, params.BucketName, params.Files[i].Path, version, params.Files[i].Recursive, allVersions, false, bypass)
|
||||
if err != nil {
|
||||
return ErrorWithContext(ctx, err)
|
||||
}
|
||||
@@ -658,30 +666,29 @@ func getDeleteMultiplePathsResponse(session *models.Principal, params objectApi.
|
||||
}
|
||||
|
||||
// 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, nonCurrentVersionsOnly bool) error {
|
||||
func deleteObjects(ctx context.Context, client MCClient, bucket string, path string, versionID string, recursive, allVersions, nonCurrentVersionsOnly, bypass bool) error {
|
||||
// Delete All non-Current versions only.
|
||||
if nonCurrentVersionsOnly {
|
||||
return deleteNonCurrentVersions(ctx, client, bucket, path)
|
||||
return deleteNonCurrentVersions(ctx, client, bucket, path, bypass)
|
||||
}
|
||||
|
||||
if recursive || allVersions {
|
||||
return deleteMultipleObjects(ctx, client, recursive, allVersions)
|
||||
return deleteMultipleObjects(ctx, client, recursive, allVersions, bypass)
|
||||
}
|
||||
|
||||
return deleteSingleObject(ctx, client, bucket, path, versionID)
|
||||
return deleteSingleObject(ctx, client, bucket, path, versionID, bypass)
|
||||
}
|
||||
|
||||
// deleteMultipleObjects uses listing before removal, it can list recursively or not,
|
||||
//
|
||||
// Use cases:
|
||||
// * Remove objects recursively
|
||||
func deleteMultipleObjects(ctx context.Context, client MCClient, recursive, allVersions bool) error {
|
||||
isBypass := false
|
||||
func deleteMultipleObjects(ctx context.Context, client MCClient, recursive, allVersions, isBypass bool) error {
|
||||
isIncomplete := false
|
||||
isRemoveBucket := false
|
||||
forceDelete := false
|
||||
|
||||
if recursive || allVersions {
|
||||
if recursive || (allVersions && !isBypass) {
|
||||
forceDelete = true
|
||||
}
|
||||
|
||||
@@ -722,13 +729,12 @@ func deleteMultipleObjects(ctx context.Context, client MCClient, recursive, allV
|
||||
return nil
|
||||
}
|
||||
|
||||
func deleteSingleObject(ctx context.Context, client MCClient, bucket, object string, versionID string) error {
|
||||
func deleteSingleObject(ctx context.Context, client MCClient, bucket, object string, versionID string, isBypass bool) error {
|
||||
targetURL := fmt.Sprintf("%s/%s", bucket, object)
|
||||
contentCh := make(chan *mc.ClientContent, 1)
|
||||
contentCh <- &mc.ClientContent{URL: *newClientURL(targetURL), VersionID: versionID}
|
||||
close(contentCh)
|
||||
|
||||
isBypass := false
|
||||
isIncomplete := false
|
||||
isRemoveBucket := false
|
||||
|
||||
@@ -741,7 +747,7 @@ func deleteSingleObject(ctx context.Context, client MCClient, bucket, object str
|
||||
return nil
|
||||
}
|
||||
|
||||
func deleteNonCurrentVersions(ctx context.Context, client MCClient, bucket, path string) error {
|
||||
func deleteNonCurrentVersions(ctx context.Context, client MCClient, bucket, path string, isBypass bool) error {
|
||||
lctx, cancel := context.WithCancel(ctx)
|
||||
defer cancel()
|
||||
|
||||
@@ -773,7 +779,7 @@ func deleteNonCurrentVersions(ctx context.Context, client MCClient, bucket, path
|
||||
}
|
||||
}()
|
||||
|
||||
for result := range client.remove(ctx, false, false, false, false, contentCh) {
|
||||
for result := range client.remove(ctx, false, false, isBypass, false, contentCh) {
|
||||
if result.Err != nil {
|
||||
return result.Err.Cause
|
||||
}
|
||||
|
||||
@@ -734,7 +734,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, tt.args.nonCurrent)
|
||||
err := deleteObjects(ctx, s3Client1, tt.args.bucket, tt.args.path, tt.args.versionID, tt.args.recursive, false, tt.args.nonCurrent, false)
|
||||
switch {
|
||||
case err == nil && tt.wantError != nil:
|
||||
t.Errorf("deleteObjects() error: %v, wantErr: %v", err, tt.wantError)
|
||||
|
||||
Reference in New Issue
Block a user