diff --git a/models/update_bucket_lifecycle.go b/models/update_bucket_lifecycle.go index b21150281..aee949e74 100644 --- a/models/update_bucket_lifecycle.go +++ b/models/update_bucket_lifecycle.go @@ -34,11 +34,35 @@ import ( // swagger:model updateBucketLifecycle type UpdateBucketLifecycle struct { - // disable + // Non required, toggle to disable or enable rule Disable bool `json:"disable,omitempty"` - // tags + // Non required, toggle to disable or enable rule + ExpiredObjectDeleteMarker bool `json:"expired_object_delete_marker,omitempty"` + + // Required in case of expiry_date or transition fields are not set. it defines an expiry days for ILM + ExpiryDays int32 `json:"expiry_days,omitempty"` + + // Non required, can be set in case of expiration is enabled + NoncurrentversionExpirationDays int32 `json:"noncurrentversion_expiration_days,omitempty"` + + // Non required, can be set in case of transition is enabled + NoncurrentversionTransitionDays int32 `json:"noncurrentversion_transition_days,omitempty"` + + // Non required, can be set in case of transition is enabled + NoncurrentversionTransitionStorageClass string `json:"noncurrentversion_transition_storage_class,omitempty"` + + // Non required field, it matches a prefix to perform ILM operations on it + Prefix string `json:"prefix,omitempty"` + + // Required only in case of transition is set. it refers to a tier + StorageClass string `json:"storage_class,omitempty"` + + // Non required field, tags to match ILM files Tags string `json:"tags,omitempty"` + + // Required in case of transition_date or expiry fields are not set. it defines a transition days for ILM + TransitionDays int32 `json:"transition_days,omitempty"` } // Validate validates this update bucket lifecycle diff --git a/restapi/embedded_spec.go b/restapi/embedded_spec.go index 0acbc0099..2336034d5 100644 --- a/restapi/embedded_spec.go +++ b/restapi/embedded_spec.go @@ -5956,10 +5956,52 @@ func init() { "type": "object", "properties": { "disable": { + "description": "Non required, toggle to disable or enable rule", "type": "boolean" }, - "tags": { + "expired_object_delete_marker": { + "description": "Non required, toggle to disable or enable rule", + "type": "boolean" + }, + "expiry_days": { + "description": "Required in case of expiry_date or transition fields are not set. it defines an expiry days for ILM", + "type": "integer", + "format": "int32", + "default": 0 + }, + "noncurrentversion_expiration_days": { + "description": "Non required, can be set in case of expiration is enabled", + "type": "integer", + "format": "int32", + "default": 0 + }, + "noncurrentversion_transition_days": { + "description": "Non required, can be set in case of transition is enabled", + "type": "integer", + "format": "int32", + "default": 0 + }, + "noncurrentversion_transition_storage_class": { + "description": "Non required, can be set in case of transition is enabled", "type": "string" + }, + "prefix": { + "description": "Non required field, it matches a prefix to perform ILM operations on it", + "type": "string" + }, + "storage_class": { + "description": "Required only in case of transition is set. it refers to a tier", + "type": "string" + }, + "tags": { + "description": "Non required field, tags to match ILM files", + "type": "string" + }, + "transition_days": { + "description": "Required in case of transition_date or expiry fields are not set. it defines a transition days for ILM", + "type": "integer", + "format": "int32", + "default": 0 } } }, @@ -12183,10 +12225,52 @@ func init() { "type": "object", "properties": { "disable": { + "description": "Non required, toggle to disable or enable rule", "type": "boolean" }, - "tags": { + "expired_object_delete_marker": { + "description": "Non required, toggle to disable or enable rule", + "type": "boolean" + }, + "expiry_days": { + "description": "Required in case of expiry_date or transition fields are not set. it defines an expiry days for ILM", + "type": "integer", + "format": "int32", + "default": 0 + }, + "noncurrentversion_expiration_days": { + "description": "Non required, can be set in case of expiration is enabled", + "type": "integer", + "format": "int32", + "default": 0 + }, + "noncurrentversion_transition_days": { + "description": "Non required, can be set in case of transition is enabled", + "type": "integer", + "format": "int32", + "default": 0 + }, + "noncurrentversion_transition_storage_class": { + "description": "Non required, can be set in case of transition is enabled", "type": "string" + }, + "prefix": { + "description": "Non required field, it matches a prefix to perform ILM operations on it", + "type": "string" + }, + "storage_class": { + "description": "Required only in case of transition is set. it refers to a tier", + "type": "string" + }, + "tags": { + "description": "Non required field, tags to match ILM files", + "type": "string" + }, + "transition_days": { + "description": "Required in case of transition_date or expiry fields are not set. it defines a transition days for ILM", + "type": "integer", + "format": "int32", + "default": 0 } } }, diff --git a/restapi/user_buckets_lifecycle.go b/restapi/user_buckets_lifecycle.go index f3747a98f..3879a0424 100644 --- a/restapi/user_buckets_lifecycle.go +++ b/restapi/user_buckets_lifecycle.go @@ -53,6 +53,14 @@ func registerBucketsLifecycleHandlers(api *operations.ConsoleAPI) { } return user_api.NewAddBucketLifecycleCreated() }) + api.UserAPIUpdateBucketLifecycleHandler = user_api.UpdateBucketLifecycleHandlerFunc(func(params user_api.UpdateBucketLifecycleParams, session *models.Principal) middleware.Responder { + err := getEditBucketLifecycleRule(session, params) + if err != nil { + user_api.NewUpdateBucketLifecycleDefault(int(err.Code)).WithPayload(err) + } + + return user_api.NewUpdateBucketLifecycleOK() + }) } // getBucketLifecycle() gets lifecycle lists for a bucket from MinIO API and returns their implementations @@ -224,7 +232,7 @@ func addBucketLifecycle(ctx context.Context, client MinioClient, params user_api return client.setBucketLifecycle(ctx, params.BucketName, lfcCfg) } -// getAddBucketLifecycleResponse returns the respose of adding a bucket lifecycle response +// getAddBucketLifecycleResponse returns the response of adding a bucket lifecycle response func getAddBucketLifecycleResponse(session *models.Principal, params user_api.AddBucketLifecycleParams) *models.Error { ctx := context.Background() mClient, err := newMinioClient(session) @@ -242,3 +250,91 @@ func getAddBucketLifecycleResponse(session *models.Principal, params user_api.Ad return nil } + +// addBucketLifecycle gets lifecycle lists for a bucket from MinIO API and returns their implementations +func editBucketLifecycle(ctx context.Context, client MinioClient, params user_api.UpdateBucketLifecycleParams) error { + // Configuration that is already set. + lfcCfg, err := client.getLifecycleRules(ctx, params.BucketName) + if err != nil { + if e := err; minio.ToErrorResponse(e).Code == "NoSuchLifecycleConfiguration" { + lfcCfg = lifecycle.NewConfiguration() + } else { + return err + } + } + + id := params.LifecycleID + + opts := ilm.LifecycleOptions{} + + // Verify if transition items are set + if params.Body.ExpiryDays == 0 && params.Body.TransitionDays != 0 { + + if params.Body.NoncurrentversionExpirationDays != 0 { + return errors.New("non current version expiration days cannot be set when transition is being configured") + } + + opts = ilm.LifecycleOptions{ + ID: id, + Prefix: params.Body.Prefix, + Status: !params.Body.Disable, + IsTagsSet: params.Body.Tags != "", + Tags: params.Body.Tags, + TransitionDays: strconv.Itoa(int(params.Body.TransitionDays)), + StorageClass: strings.ToUpper(params.Body.StorageClass), + ExpiredObjectDeleteMarker: params.Body.ExpiredObjectDeleteMarker, + NoncurrentVersionTransitionDays: int(params.Body.NoncurrentversionTransitionDays), + NoncurrentVersionTransitionStorageClass: strings.ToUpper(params.Body.NoncurrentversionTransitionStorageClass), + } + } else if params.Body.TransitionDays == 0 && params.Body.ExpiryDays != 0 { // Verify if expiry configuration is set + if params.Body.NoncurrentversionTransitionDays != 0 { + return errors.New("non current version Transition Days cannot be set when expiry is being configured") + } + + if params.Body.NoncurrentversionTransitionStorageClass != "" { + return errors.New("non current version Transition Storage Class cannot be set when expiry is being configured") + } + + opts = ilm.LifecycleOptions{ + ID: id, + Prefix: params.Body.Prefix, + Status: !params.Body.Disable, + IsTagsSet: params.Body.Tags != "", + Tags: params.Body.Tags, + ExpiryDays: strconv.Itoa(int(params.Body.ExpiryDays)), + ExpiredObjectDeleteMarker: params.Body.ExpiredObjectDeleteMarker, + NoncurrentVersionExpirationDays: int(params.Body.NoncurrentversionExpirationDays), + } + + } else { + // Non set, we return error + return errors.New("transition and expiry cannot be set for the same rule") + } + + var err2 *probe.Error + lfcCfg, err2 = opts.ToConfig(lfcCfg) + if err2.ToGoError() != nil { + return err2.ToGoError() + } + + return client.setBucketLifecycle(ctx, params.BucketName, lfcCfg) +} + +// getEditBucketLifecycleRule returns the response of bucket lyfecycle tier edit +func getEditBucketLifecycleRule(session *models.Principal, params user_api.UpdateBucketLifecycleParams) *models.Error { + ctx := context.Background() + mClient, err := newMinioClient(session) + if err != nil { + return prepareError(err) + } + // create a minioClient interface implementation + // defining the client to be used + minioClient := minioClient{client: mClient} + + err = editBucketLifecycle(ctx, minioClient, params) + if err != nil { + return prepareError(err) + } + + return nil +} diff --git a/restapi/user_buckets_lifecycle_test.go b/restapi/user_buckets_lifecycle_test.go index 2344ab123..260b04783 100644 --- a/restapi/user_buckets_lifecycle_test.go +++ b/restapi/user_buckets_lifecycle_test.go @@ -198,3 +198,65 @@ func TestSetLifecycleRule(t *testing.T) { assert.Equal(errors.New("error setting lifecycle"), err2, fmt.Sprintf("Failed on %s: Error returned", function)) } + +func TestUpdateLifecycleRule(t *testing.T) { + assert := assert.New(t) + // mock minIO client + minClient := minioClientMock{} + + function := "editBucketLifecycle()" + ctx := context.Background() + + // Test-1 : editBucketLifecycle() get list of events for a particular bucket only one config (get lifecycle mock) + // mock create request + mockLifecycle := lifecycle.Configuration{ + Rules: []lifecycle.Rule{ + { + ID: "TESTRULE", + Expiration: lifecycle.Expiration{Days: 15}, + Status: "Enabled", + RuleFilter: lifecycle.Filter{Tag: lifecycle.Tag{Key: "tag1", Value: "val1"}, And: lifecycle.And{Prefix: "prefix1"}}, + }, + }, + } + + minioGetLifecycleRulesMock = func(ctx context.Context, bucketName string) (lifecycle *lifecycle.Configuration, err error) { + return &mockLifecycle, nil + } + + // Test-2 : editBucketLifecycle() Update lifecycle rule + + editMock := user_api.UpdateBucketLifecycleParams{ + BucketName: "testBucket", + Body: &models.UpdateBucketLifecycle{ + Disable: false, + ExpiredObjectDeleteMarker: false, + ExpiryDays: int32(16), + NoncurrentversionExpirationDays: 0, + NoncurrentversionTransitionDays: 0, + NoncurrentversionTransitionStorageClass: "", + Prefix: "pref1", + StorageClass: "", + Tags: "", + TransitionDays: 0, + }, + } + + minioSetBucketLifecycleMock = func(ctx context.Context, bucketName string, config *lifecycle.Configuration) error { + return nil + } + + err := editBucketLifecycle(ctx, minClient, editMock) + + assert.Equal(nil, err, fmt.Sprintf("Failed on %s: Error returned", function)) + + // Test-3 : editBucketLifecycle() returns error + + minioSetBucketLifecycleMock = func(ctx context.Context, bucketName string, config *lifecycle.Configuration) error { + return errors.New("error setting lifecycle") + } + + err2 := editBucketLifecycle(ctx, minClient, editMock) + + assert.Equal(errors.New("error setting lifecycle"), err2, fmt.Sprintf("Failed on %s: Error returned", function)) +} diff --git a/swagger-console.yml b/swagger-console.yml index dd34a0c07..6662f030c 100644 --- a/swagger-console.yml +++ b/swagger-console.yml @@ -3906,10 +3906,44 @@ definitions: updateBucketLifecycle: type: object properties: + prefix: + description: Non required field, it matches a prefix to perform ILM operations on it + type: string tags: + description: Non required field, tags to match ILM files + type: string + expiry_days: + description: Required in case of expiry_date or transition fields are not set. it defines an expiry days for ILM + type: integer + format: int32 + default: 0 + transition_days: + description: Required in case of transition_date or expiry fields are not set. it defines a transition days for ILM + type: integer + format: int32 + default: 0 + storage_class: + description: Required only in case of transition is set. it refers to a tier type: string disable: + description: Non required, toggle to disable or enable rule type: boolean + expired_object_delete_marker: + description: Non required, toggle to disable or enable rule + type: boolean + noncurrentversion_expiration_days: + description: Non required, can be set in case of expiration is enabled + type: integer + format: int32 + default: 0 + noncurrentversion_transition_days: + description: Non required, can be set in case of transition is enabled + type: integer + format: int32 + default: 0 + noncurrentversion_transition_storage_class: + description: Non required, can be set in case of transition is enabled + type: string prefixAccessPair: type: object