| 
									
										
										
										
											2019-08-10 01:02:41 +08:00
										 |  |  | /* | 
					
						
							|  |  |  |  * MinIO Cloud Storage, (C) 2019 MinIO, Inc. | 
					
						
							|  |  |  |  * | 
					
						
							|  |  |  |  * Licensed under the Apache License, Version 2.0 (the "License"); | 
					
						
							|  |  |  |  * you may not use this file except in compliance with the License. | 
					
						
							|  |  |  |  * You may obtain a copy of the License at | 
					
						
							|  |  |  |  * | 
					
						
							|  |  |  |  *     http://www.apache.org/licenses/LICENSE-2.0
 | 
					
						
							|  |  |  |  * | 
					
						
							|  |  |  |  * Unless required by applicable law or agreed to in writing, software | 
					
						
							|  |  |  |  * distributed under the License is distributed on an "AS IS" BASIS, | 
					
						
							|  |  |  |  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | 
					
						
							|  |  |  |  * See the License for the specific language governing permissions and | 
					
						
							|  |  |  |  * limitations under the License. | 
					
						
							|  |  |  |  */ | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | package cmd | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | import ( | 
					
						
							|  |  |  | 	"context" | 
					
						
							|  |  |  | 	"time" | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	"github.com/minio/minio/cmd/logger" | 
					
						
							| 
									
										
										
										
											2019-11-05 07:52:03 +08:00
										 |  |  | 	"github.com/minio/minio/pkg/event" | 
					
						
							| 
									
										
										
										
											2019-08-10 01:02:41 +08:00
										 |  |  | 	"github.com/minio/minio/pkg/lifecycle" | 
					
						
							|  |  |  | ) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | const ( | 
					
						
							|  |  |  | 	bgLifecycleInterval = 24 * time.Hour | 
					
						
							|  |  |  | 	bgLifecycleTick     = time.Hour | 
					
						
							|  |  |  | ) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | type lifecycleOps struct { | 
					
						
							|  |  |  | 	LastActivity time.Time | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // Register to the daily objects listing
 | 
					
						
							|  |  |  | var globalLifecycleOps = &lifecycleOps{} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | func getLocalBgLifecycleOpsStatus() BgLifecycleOpsStatus { | 
					
						
							|  |  |  | 	return BgLifecycleOpsStatus{ | 
					
						
							|  |  |  | 		LastActivity: globalLifecycleOps.LastActivity, | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // initDailyLifecycle starts the routine that receives the daily
 | 
					
						
							|  |  |  | // listing of all objects and applies any matching bucket lifecycle
 | 
					
						
							|  |  |  | // rules.
 | 
					
						
							|  |  |  | func initDailyLifecycle() { | 
					
						
							|  |  |  | 	go startDailyLifecycle() | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | func startDailyLifecycle() { | 
					
						
							|  |  |  | 	var objAPI ObjectLayer | 
					
						
							|  |  |  | 	var ctx = context.Background() | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	// Wait until the object API is ready
 | 
					
						
							|  |  |  | 	for { | 
					
						
							| 
									
										
										
										
											2019-11-10 01:27:23 +08:00
										 |  |  | 		objAPI = newObjectLayerWithoutSafeModeFn() | 
					
						
							| 
									
										
										
										
											2019-08-10 01:02:41 +08:00
										 |  |  | 		if objAPI == nil { | 
					
						
							|  |  |  | 			time.Sleep(time.Second) | 
					
						
							|  |  |  | 			continue | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		break | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	// Calculate the time of the last lifecycle operation in all peers node of the cluster
 | 
					
						
							|  |  |  | 	computeLastLifecycleActivity := func(status []BgOpsStatus) time.Time { | 
					
						
							|  |  |  | 		var lastAct time.Time | 
					
						
							|  |  |  | 		for _, st := range status { | 
					
						
							|  |  |  | 			if st.LifecycleOps.LastActivity.After(lastAct) { | 
					
						
							|  |  |  | 				lastAct = st.LifecycleOps.LastActivity | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		return lastAct | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	for { | 
					
						
							|  |  |  | 		// Check if we should perform lifecycle ops based on the last lifecycle activity, sleep one hour otherwise
 | 
					
						
							|  |  |  | 		allLifecycleStatus := []BgOpsStatus{ | 
					
						
							|  |  |  | 			{LifecycleOps: getLocalBgLifecycleOpsStatus()}, | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		if globalIsDistXL { | 
					
						
							|  |  |  | 			allLifecycleStatus = append(allLifecycleStatus, globalNotificationSys.BackgroundOpsStatus()...) | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		lastAct := computeLastLifecycleActivity(allLifecycleStatus) | 
					
						
							|  |  |  | 		if !lastAct.IsZero() && time.Since(lastAct) < bgLifecycleInterval { | 
					
						
							|  |  |  | 			time.Sleep(bgLifecycleTick) | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		// Perform one lifecycle operation
 | 
					
						
							|  |  |  | 		err := lifecycleRound(ctx, objAPI) | 
					
						
							|  |  |  | 		switch err.(type) { | 
					
						
							|  |  |  | 		// Unable to hold a lock means there is another
 | 
					
						
							|  |  |  | 		// instance doing the lifecycle round round
 | 
					
						
							|  |  |  | 		case OperationTimedOut: | 
					
						
							|  |  |  | 			time.Sleep(bgLifecycleTick) | 
					
						
							|  |  |  | 		default: | 
					
						
							|  |  |  | 			logger.LogIf(ctx, err) | 
					
						
							|  |  |  | 			time.Sleep(time.Minute) | 
					
						
							|  |  |  | 			continue | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-08-20 02:22:32 +08:00
										 |  |  | var lifecycleTimeout = newDynamicTimeout(60*time.Second, time.Second) | 
					
						
							| 
									
										
										
										
											2019-08-10 01:02:41 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-08-20 02:22:32 +08:00
										 |  |  | func lifecycleRound(ctx context.Context, objAPI ObjectLayer) error { | 
					
						
							| 
									
										
										
										
											2019-08-10 01:02:41 +08:00
										 |  |  | 	// Lock to avoid concurrent lifecycle ops from other nodes
 | 
					
						
							| 
									
										
										
										
											2019-11-14 04:17:45 +08:00
										 |  |  | 	sweepLock := objAPI.NewNSLock(ctx, "system", "daily-lifecycle-ops") | 
					
						
							| 
									
										
										
										
											2019-08-20 02:22:32 +08:00
										 |  |  | 	if err := sweepLock.GetLock(lifecycleTimeout); err != nil { | 
					
						
							| 
									
										
										
										
											2019-08-10 01:02:41 +08:00
										 |  |  | 		return err | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	defer sweepLock.Unlock() | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	buckets, err := objAPI.ListBuckets(ctx) | 
					
						
							|  |  |  | 	if err != nil { | 
					
						
							|  |  |  | 		return err | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	for _, bucket := range buckets { | 
					
						
							|  |  |  | 		// Check if the current bucket has a configured lifecycle policy, skip otherwise
 | 
					
						
							|  |  |  | 		l, ok := globalLifecycleSys.Get(bucket.Name) | 
					
						
							|  |  |  | 		if !ok { | 
					
						
							|  |  |  | 			continue | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		// Calculate the common prefix of all lifecycle rules
 | 
					
						
							|  |  |  | 		var prefixes []string | 
					
						
							|  |  |  | 		for _, rule := range l.Rules { | 
					
						
							|  |  |  | 			prefixes = append(prefixes, rule.Filter.Prefix) | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		commonPrefix := lcp(prefixes) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		// List all objects and calculate lifecycle action based on object name & object modtime
 | 
					
						
							|  |  |  | 		marker := "" | 
					
						
							|  |  |  | 		for { | 
					
						
							| 
									
										
										
										
											2019-10-31 04:20:01 +08:00
										 |  |  | 			res, err := objAPI.ListObjects(ctx, bucket.Name, commonPrefix, marker, "", maxObjectList) | 
					
						
							| 
									
										
										
										
											2019-08-10 01:02:41 +08:00
										 |  |  | 			if err != nil { | 
					
						
							|  |  |  | 				continue | 
					
						
							|  |  |  | 			} | 
					
						
							| 
									
										
										
										
											2019-09-11 03:08:44 +08:00
										 |  |  | 			var objects []string | 
					
						
							| 
									
										
										
										
											2019-08-10 01:02:41 +08:00
										 |  |  | 			for _, obj := range res.Objects { | 
					
						
							|  |  |  | 				// Find the action that need to be executed
 | 
					
						
							|  |  |  | 				action := l.ComputeAction(obj.Name, obj.ModTime) | 
					
						
							|  |  |  | 				switch action { | 
					
						
							|  |  |  | 				case lifecycle.DeleteAction: | 
					
						
							| 
									
										
										
										
											2019-09-11 03:08:44 +08:00
										 |  |  | 					objects = append(objects, obj.Name) | 
					
						
							| 
									
										
										
										
											2019-08-10 01:02:41 +08:00
										 |  |  | 				default: | 
					
						
							| 
									
										
										
										
											2019-09-11 03:08:44 +08:00
										 |  |  | 					// Do nothing, for now.
 | 
					
						
							| 
									
										
										
										
											2019-08-10 01:02:41 +08:00
										 |  |  | 				} | 
					
						
							|  |  |  | 			} | 
					
						
							| 
									
										
										
										
											2019-11-05 07:52:03 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-09-11 03:08:44 +08:00
										 |  |  | 			// Deletes a list of objects.
 | 
					
						
							| 
									
										
										
										
											2019-11-05 07:52:03 +08:00
										 |  |  | 			deleteErrs, err := objAPI.DeleteObjects(ctx, bucket.Name, objects) | 
					
						
							|  |  |  | 			if err != nil { | 
					
						
							|  |  |  | 				logger.LogIf(ctx, err) | 
					
						
							|  |  |  | 			} else { | 
					
						
							|  |  |  | 				for i := range deleteErrs { | 
					
						
							|  |  |  | 					if deleteErrs[i] != nil { | 
					
						
							|  |  |  | 						logger.LogIf(ctx, deleteErrs[i]) | 
					
						
							|  |  |  | 						continue | 
					
						
							|  |  |  | 					} | 
					
						
							|  |  |  | 					// Notify object deleted event.
 | 
					
						
							|  |  |  | 					sendEvent(eventArgs{ | 
					
						
							|  |  |  | 						EventName:  event.ObjectRemovedDelete, | 
					
						
							|  |  |  | 						BucketName: bucket.Name, | 
					
						
							|  |  |  | 						Object: ObjectInfo{ | 
					
						
							|  |  |  | 							Name: objects[i], | 
					
						
							|  |  |  | 						}, | 
					
						
							|  |  |  | 						Host: "Internal: [ILM-EXPIRY]", | 
					
						
							|  |  |  | 					}) | 
					
						
							|  |  |  | 				} | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-08-10 01:02:41 +08:00
										 |  |  | 			if !res.IsTruncated { | 
					
						
							| 
									
										
										
										
											2019-09-11 03:08:44 +08:00
										 |  |  | 				// We are done here, proceed to next bucket.
 | 
					
						
							| 
									
										
										
										
											2019-08-10 01:02:41 +08:00
										 |  |  | 				break | 
					
						
							|  |  |  | 			} | 
					
						
							| 
									
										
										
										
											2019-09-11 03:08:44 +08:00
										 |  |  | 			marker = res.NextMarker | 
					
						
							| 
									
										
										
										
											2019-08-10 01:02:41 +08:00
										 |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	return nil | 
					
						
							|  |  |  | } |