Added delete governance bypass option when deleting file versions (#2536)

Signed-off-by: Benjamin Perez <benjamin@bexsoft.net>
This commit is contained in:
Alex
2023-01-03 12:35:37 -06:00
committed by GitHub
parent 25e486ef18
commit 255e3d14d0
15 changed files with 364 additions and 49 deletions

View File

@@ -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": {

View File

@@ -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
}

View File

@@ -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

View File

@@ -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

View File

@@ -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)

View File

@@ -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
}

View File

@@ -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)