| 
									
										
										
										
											2018-03-16 04:03:41 +08:00
										 |  |  | /* | 
					
						
							| 
									
										
										
										
											2019-04-10 02:39:42 +08:00
										 |  |  |  * MinIO Cloud Storage, (C) 2018, 2019 MinIO, Inc. | 
					
						
							| 
									
										
										
										
											2018-03-16 04:03:41 +08:00
										 |  |  |  * | 
					
						
							|  |  |  |  * 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 ( | 
					
						
							| 
									
										
										
										
											2019-01-14 14:44:20 +08:00
										 |  |  | 	"archive/zip" | 
					
						
							| 
									
										
										
										
											2018-10-10 05:00:01 +08:00
										 |  |  | 	"bytes" | 
					
						
							| 
									
										
										
										
											2018-03-16 04:27:16 +08:00
										 |  |  | 	"context" | 
					
						
							| 
									
										
										
										
											2018-03-16 04:03:41 +08:00
										 |  |  | 	"encoding/json" | 
					
						
							|  |  |  | 	"encoding/xml" | 
					
						
							|  |  |  | 	"fmt" | 
					
						
							| 
									
										
										
										
											2019-01-14 14:44:20 +08:00
										 |  |  | 	"io" | 
					
						
							| 
									
										
										
										
											2018-12-19 02:05:26 +08:00
										 |  |  | 	"net" | 
					
						
							| 
									
										
										
										
											2018-03-16 04:03:41 +08:00
										 |  |  | 	"net/url" | 
					
						
							|  |  |  | 	"path" | 
					
						
							| 
									
										
										
										
											2018-08-24 14:31:14 +08:00
										 |  |  | 	"strings" | 
					
						
							| 
									
										
										
										
											2018-03-16 04:03:41 +08:00
										 |  |  | 	"sync" | 
					
						
							| 
									
										
										
										
											2018-06-06 16:51:56 +08:00
										 |  |  | 	"time" | 
					
						
							| 
									
										
										
										
											2018-03-16 04:03:41 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-05-30 13:29:37 +08:00
										 |  |  | 	"github.com/minio/minio/cmd/crypto" | 
					
						
							| 
									
										
										
										
											2018-04-06 06:04:40 +08:00
										 |  |  | 	"github.com/minio/minio/cmd/logger" | 
					
						
							| 
									
										
										
										
											2018-03-16 04:03:41 +08:00
										 |  |  | 	"github.com/minio/minio/pkg/event" | 
					
						
							|  |  |  | 	xnet "github.com/minio/minio/pkg/net" | 
					
						
							| 
									
										
										
										
											2018-04-25 06:53:30 +08:00
										 |  |  | 	"github.com/minio/minio/pkg/policy" | 
					
						
							| 
									
										
										
										
											2018-03-16 04:03:41 +08:00
										 |  |  | ) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // NotificationSys - notification system.
 | 
					
						
							|  |  |  | type NotificationSys struct { | 
					
						
							|  |  |  | 	sync.RWMutex | 
					
						
							|  |  |  | 	targetList                 *event.TargetList | 
					
						
							|  |  |  | 	bucketRulesMap             map[string]event.RulesMap | 
					
						
							|  |  |  | 	bucketRemoteTargetRulesMap map[string]map[event.TargetID]event.RulesMap | 
					
						
							| 
									
										
										
										
											2019-03-15 07:27:31 +08:00
										 |  |  | 	peerClients                []*peerRESTClient | 
					
						
							| 
									
										
										
										
											2018-03-16 04:03:41 +08:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // GetARNList - returns available ARNs.
 | 
					
						
							|  |  |  | func (sys *NotificationSys) GetARNList() []string { | 
					
						
							|  |  |  | 	arns := []string{} | 
					
						
							|  |  |  | 	region := globalServerConfig.GetRegion() | 
					
						
							|  |  |  | 	for _, targetID := range sys.targetList.List() { | 
					
						
							| 
									
										
										
										
											2018-08-24 14:31:14 +08:00
										 |  |  | 		// httpclient target is part of ListenBucketNotification
 | 
					
						
							|  |  |  | 		// which doesn't need to be listed as part of the ARN list
 | 
					
						
							|  |  |  | 		// This list is only meant for external targets, filter
 | 
					
						
							|  |  |  | 		// this out pro-actively.
 | 
					
						
							|  |  |  | 		if !strings.HasPrefix(targetID.ID, "httpclient+") { | 
					
						
							|  |  |  | 			arns = append(arns, targetID.ToARN(region).String()) | 
					
						
							|  |  |  | 		} | 
					
						
							| 
									
										
										
										
											2018-03-16 04:03:41 +08:00
										 |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	return arns | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-05-10 02:25:38 +08:00
										 |  |  | // NotificationPeerErr returns error associated for a remote peer.
 | 
					
						
							|  |  |  | type NotificationPeerErr struct { | 
					
						
							|  |  |  | 	Host xnet.Host // Remote host on which the rpc call was initiated
 | 
					
						
							|  |  |  | 	Err  error     // Error returned by the remote peer for an rpc call
 | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-03-16 04:03:41 +08:00
										 |  |  | // DeleteBucket - calls DeleteBucket RPC call on all peers.
 | 
					
						
							| 
									
										
										
										
											2018-07-04 02:09:36 +08:00
										 |  |  | func (sys *NotificationSys) DeleteBucket(ctx context.Context, bucketName string) { | 
					
						
							| 
									
										
										
										
											2018-05-10 06:11:51 +08:00
										 |  |  | 	go func() { | 
					
						
							|  |  |  | 		var wg sync.WaitGroup | 
					
						
							| 
									
										
										
										
											2019-03-15 07:27:31 +08:00
										 |  |  | 		for _, client := range sys.peerClients { | 
					
						
							| 
									
										
										
										
											2018-05-10 06:11:51 +08:00
										 |  |  | 			wg.Add(1) | 
					
						
							| 
									
										
										
										
											2019-03-15 07:27:31 +08:00
										 |  |  | 			go func(client *peerRESTClient) { | 
					
						
							| 
									
										
										
										
											2018-05-10 06:11:51 +08:00
										 |  |  | 				defer wg.Done() | 
					
						
							|  |  |  | 				if err := client.DeleteBucket(bucketName); err != nil { | 
					
						
							| 
									
										
										
										
											2019-03-15 07:27:31 +08:00
										 |  |  | 					logger.GetReqInfo(ctx).AppendTags("remotePeer", client.host.Name) | 
					
						
							| 
									
										
										
										
											2018-07-04 02:09:36 +08:00
										 |  |  | 					logger.LogIf(ctx, err) | 
					
						
							| 
									
										
										
										
											2018-05-10 02:25:38 +08:00
										 |  |  | 				} | 
					
						
							| 
									
										
										
										
											2019-03-15 07:27:31 +08:00
										 |  |  | 			}(client) | 
					
						
							| 
									
										
										
										
											2018-05-10 06:11:51 +08:00
										 |  |  | 		} | 
					
						
							|  |  |  | 		wg.Wait() | 
					
						
							|  |  |  | 	}() | 
					
						
							| 
									
										
										
										
											2018-03-16 04:03:41 +08:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-01-14 14:44:20 +08:00
										 |  |  | // A NotificationGroup is a collection of goroutines working on subtasks that are part of
 | 
					
						
							|  |  |  | // the same overall task.
 | 
					
						
							|  |  |  | //
 | 
					
						
							|  |  |  | // A zero NotificationGroup is valid and does not cancel on error.
 | 
					
						
							|  |  |  | type NotificationGroup struct { | 
					
						
							|  |  |  | 	wg   sync.WaitGroup | 
					
						
							|  |  |  | 	errs []NotificationPeerErr | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // WithNPeers returns a new NotificationGroup with length of errs slice upto nerrs,
 | 
					
						
							|  |  |  | // upon Wait() errors are returned collected from all tasks.
 | 
					
						
							|  |  |  | func WithNPeers(nerrs int) *NotificationGroup { | 
					
						
							|  |  |  | 	return &NotificationGroup{errs: make([]NotificationPeerErr, nerrs)} | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // Wait blocks until all function calls from the Go method have returned, then
 | 
					
						
							|  |  |  | // returns the slice of errors from all function calls.
 | 
					
						
							|  |  |  | func (g *NotificationGroup) Wait() []NotificationPeerErr { | 
					
						
							|  |  |  | 	g.wg.Wait() | 
					
						
							|  |  |  | 	return g.errs | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // Go calls the given function in a new goroutine.
 | 
					
						
							|  |  |  | //
 | 
					
						
							|  |  |  | // The first call to return a non-nil error will be
 | 
					
						
							|  |  |  | // collected in errs slice and returned by Wait().
 | 
					
						
							|  |  |  | func (g *NotificationGroup) Go(ctx context.Context, f func() error, index int, addr xnet.Host) { | 
					
						
							|  |  |  | 	g.wg.Add(1) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	go func() { | 
					
						
							|  |  |  | 		defer g.wg.Done() | 
					
						
							|  |  |  | 		g.errs[index] = NotificationPeerErr{ | 
					
						
							|  |  |  | 			Host: addr, | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		for i := 0; i < 3; i++ { | 
					
						
							|  |  |  | 			if err := f(); err != nil { | 
					
						
							|  |  |  | 				g.errs[index].Err = err | 
					
						
							|  |  |  | 				// Last iteration log the error.
 | 
					
						
							|  |  |  | 				if i == 2 { | 
					
						
							|  |  |  | 					reqInfo := (&logger.ReqInfo{}).AppendTags("peerAddress", addr.String()) | 
					
						
							|  |  |  | 					ctx := logger.SetReqInfo(ctx, reqInfo) | 
					
						
							|  |  |  | 					logger.LogIf(ctx, err) | 
					
						
							| 
									
										
										
										
											2018-12-19 06:39:21 +08:00
										 |  |  | 				} | 
					
						
							|  |  |  | 				// Wait for one second and no need wait after last attempt.
 | 
					
						
							|  |  |  | 				if i < 2 { | 
					
						
							|  |  |  | 					time.Sleep(1 * time.Second) | 
					
						
							|  |  |  | 				} | 
					
						
							| 
									
										
										
										
											2019-01-14 14:44:20 +08:00
										 |  |  | 				continue | 
					
						
							| 
									
										
										
										
											2018-12-19 06:39:21 +08:00
										 |  |  | 			} | 
					
						
							| 
									
										
										
										
											2019-01-14 14:44:20 +08:00
										 |  |  | 			break | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	}() | 
					
						
							|  |  |  | } | 
					
						
							| 
									
										
										
										
											2018-12-19 06:39:21 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-03-15 07:27:31 +08:00
										 |  |  | // ReloadFormat - calls ReloadFormat REST call on all peers.
 | 
					
						
							| 
									
										
										
										
											2019-01-14 14:44:20 +08:00
										 |  |  | func (sys *NotificationSys) ReloadFormat(dryRun bool) []NotificationPeerErr { | 
					
						
							| 
									
										
										
										
											2019-03-15 07:27:31 +08:00
										 |  |  | 	ng := WithNPeers(len(sys.peerClients)) | 
					
						
							|  |  |  | 	for idx, client := range sys.peerClients { | 
					
						
							|  |  |  | 		if client == nil { | 
					
						
							|  |  |  | 			continue | 
					
						
							|  |  |  | 		} | 
					
						
							| 
									
										
										
										
											2019-01-14 14:44:20 +08:00
										 |  |  | 		client := client | 
					
						
							|  |  |  | 		ng.Go(context.Background(), func() error { | 
					
						
							|  |  |  | 			return client.ReloadFormat(dryRun) | 
					
						
							| 
									
										
										
										
											2019-03-15 07:27:31 +08:00
										 |  |  | 		}, idx, *client.host) | 
					
						
							| 
									
										
										
										
											2019-01-14 14:44:20 +08:00
										 |  |  | 	} | 
					
						
							|  |  |  | 	return ng.Wait() | 
					
						
							| 
									
										
										
										
											2018-12-19 06:39:21 +08:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // LoadUsers - calls LoadUsers RPC call on all peers.
 | 
					
						
							| 
									
										
										
										
											2019-01-14 14:44:20 +08:00
										 |  |  | func (sys *NotificationSys) LoadUsers() []NotificationPeerErr { | 
					
						
							| 
									
										
										
										
											2019-03-15 07:27:31 +08:00
										 |  |  | 	ng := WithNPeers(len(sys.peerClients)) | 
					
						
							|  |  |  | 	for idx, client := range sys.peerClients { | 
					
						
							|  |  |  | 		if client == nil { | 
					
						
							|  |  |  | 			continue | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		client := client | 
					
						
							|  |  |  | 		ng.Go(context.Background(), client.LoadUsers, idx, *client.host) | 
					
						
							| 
									
										
										
										
											2018-12-19 06:39:21 +08:00
										 |  |  | 	} | 
					
						
							| 
									
										
										
										
											2019-01-14 14:44:20 +08:00
										 |  |  | 	return ng.Wait() | 
					
						
							| 
									
										
										
										
											2018-12-19 06:39:21 +08:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-01-14 14:44:20 +08:00
										 |  |  | // StartProfiling - start profiling on remote peers, by initiating a remote RPC.
 | 
					
						
							|  |  |  | func (sys *NotificationSys) StartProfiling(profiler string) []NotificationPeerErr { | 
					
						
							| 
									
										
										
										
											2019-03-15 07:27:31 +08:00
										 |  |  | 	ng := WithNPeers(len(sys.peerClients)) | 
					
						
							|  |  |  | 	for idx, client := range sys.peerClients { | 
					
						
							|  |  |  | 		if client == nil { | 
					
						
							|  |  |  | 			continue | 
					
						
							|  |  |  | 		} | 
					
						
							| 
									
										
										
										
											2019-01-14 14:44:20 +08:00
										 |  |  | 		client := client | 
					
						
							|  |  |  | 		ng.Go(context.Background(), func() error { | 
					
						
							|  |  |  | 			return client.StartProfiling(profiler) | 
					
						
							| 
									
										
										
										
											2019-03-15 07:27:31 +08:00
										 |  |  | 		}, idx, *client.host) | 
					
						
							| 
									
										
										
										
											2019-01-14 14:44:20 +08:00
										 |  |  | 	} | 
					
						
							|  |  |  | 	return ng.Wait() | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // DownloadProfilingData - download profiling data from all remote peers.
 | 
					
						
							|  |  |  | func (sys *NotificationSys) DownloadProfilingData(ctx context.Context, writer io.Writer) bool { | 
					
						
							|  |  |  | 	profilingDataFound := false | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	// Initialize a zip writer which will provide a zipped content
 | 
					
						
							|  |  |  | 	// of profiling data of all nodes
 | 
					
						
							|  |  |  | 	zipWriter := zip.NewWriter(writer) | 
					
						
							|  |  |  | 	defer zipWriter.Close() | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-03-15 07:27:31 +08:00
										 |  |  | 	for _, client := range sys.peerClients { | 
					
						
							|  |  |  | 		if client == nil { | 
					
						
							|  |  |  | 			continue | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		data, err := client.DownloadProfileData() | 
					
						
							| 
									
										
										
										
											2019-01-14 14:44:20 +08:00
										 |  |  | 		if err != nil { | 
					
						
							| 
									
										
										
										
											2019-03-15 07:27:31 +08:00
										 |  |  | 			reqInfo := (&logger.ReqInfo{}).AppendTags("peerAddress", client.host.String()) | 
					
						
							| 
									
										
										
										
											2019-01-14 14:44:20 +08:00
										 |  |  | 			ctx := logger.SetReqInfo(ctx, reqInfo) | 
					
						
							|  |  |  | 			logger.LogIf(ctx, err) | 
					
						
							|  |  |  | 			continue | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		profilingDataFound = true | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		// Send profiling data to zip as file
 | 
					
						
							|  |  |  | 		header, zerr := zip.FileInfoHeader(dummyFileInfo{ | 
					
						
							| 
									
										
										
										
											2019-03-15 07:27:31 +08:00
										 |  |  | 			name:    fmt.Sprintf("profiling-%s.pprof", client.host.String()), | 
					
						
							| 
									
										
										
										
											2019-01-14 14:44:20 +08:00
										 |  |  | 			size:    int64(len(data)), | 
					
						
							|  |  |  | 			mode:    0600, | 
					
						
							|  |  |  | 			modTime: UTCNow(), | 
					
						
							|  |  |  | 			isDir:   false, | 
					
						
							|  |  |  | 			sys:     nil, | 
					
						
							|  |  |  | 		}) | 
					
						
							|  |  |  | 		if zerr != nil { | 
					
						
							| 
									
										
										
										
											2019-03-15 07:27:31 +08:00
										 |  |  | 			reqInfo := (&logger.ReqInfo{}).AppendTags("peerAddress", client.host.String()) | 
					
						
							| 
									
										
										
										
											2019-01-14 14:44:20 +08:00
										 |  |  | 			ctx := logger.SetReqInfo(ctx, reqInfo) | 
					
						
							|  |  |  | 			logger.LogIf(ctx, zerr) | 
					
						
							|  |  |  | 			continue | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		zwriter, zerr := zipWriter.CreateHeader(header) | 
					
						
							|  |  |  | 		if zerr != nil { | 
					
						
							| 
									
										
										
										
											2019-03-15 07:27:31 +08:00
										 |  |  | 			reqInfo := (&logger.ReqInfo{}).AppendTags("peerAddress", client.host.String()) | 
					
						
							| 
									
										
										
										
											2019-01-14 14:44:20 +08:00
										 |  |  | 			ctx := logger.SetReqInfo(ctx, reqInfo) | 
					
						
							|  |  |  | 			logger.LogIf(ctx, zerr) | 
					
						
							|  |  |  | 			continue | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		if _, err = io.Copy(zwriter, bytes.NewBuffer(data)); err != nil { | 
					
						
							| 
									
										
										
										
											2019-03-15 07:27:31 +08:00
										 |  |  | 			reqInfo := (&logger.ReqInfo{}).AppendTags("peerAddress", client.host.String()) | 
					
						
							| 
									
										
										
										
											2019-01-14 14:44:20 +08:00
										 |  |  | 			ctx := logger.SetReqInfo(ctx, reqInfo) | 
					
						
							|  |  |  | 			logger.LogIf(ctx, err) | 
					
						
							|  |  |  | 			continue | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	thisAddr, err := xnet.ParseHost(GetLocalPeer(globalEndpoints)) | 
					
						
							|  |  |  | 	if err != nil { | 
					
						
							|  |  |  | 		logger.LogIf(ctx, err) | 
					
						
							|  |  |  | 		return profilingDataFound | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	data, err := getProfileData() | 
					
						
							|  |  |  | 	if err != nil { | 
					
						
							|  |  |  | 		reqInfo := (&logger.ReqInfo{}).AppendTags("peerAddress", thisAddr.String()) | 
					
						
							|  |  |  | 		ctx := logger.SetReqInfo(ctx, reqInfo) | 
					
						
							|  |  |  | 		logger.LogIf(ctx, err) | 
					
						
							|  |  |  | 		return profilingDataFound | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	profilingDataFound = true | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	// Send profiling data to zip as file
 | 
					
						
							|  |  |  | 	header, zerr := zip.FileInfoHeader(dummyFileInfo{ | 
					
						
							|  |  |  | 		name:    fmt.Sprintf("profiling-%s.pprof", thisAddr), | 
					
						
							|  |  |  | 		size:    int64(len(data)), | 
					
						
							|  |  |  | 		mode:    0600, | 
					
						
							|  |  |  | 		modTime: UTCNow(), | 
					
						
							|  |  |  | 		isDir:   false, | 
					
						
							|  |  |  | 		sys:     nil, | 
					
						
							|  |  |  | 	}) | 
					
						
							| 
									
										
										
										
											2019-02-13 20:59:36 +08:00
										 |  |  | 	if zerr != nil { | 
					
						
							|  |  |  | 		return profilingDataFound | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2019-01-14 14:44:20 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	zwriter, zerr := zipWriter.CreateHeader(header) | 
					
						
							|  |  |  | 	if zerr != nil { | 
					
						
							|  |  |  | 		return profilingDataFound | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	if _, err = io.Copy(zwriter, bytes.NewBuffer(data)); err != nil { | 
					
						
							|  |  |  | 		return profilingDataFound | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	return profilingDataFound | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // SignalService - calls signal service RPC call on all peers.
 | 
					
						
							|  |  |  | func (sys *NotificationSys) SignalService(sig serviceSignal) []NotificationPeerErr { | 
					
						
							| 
									
										
										
										
											2019-03-15 07:27:31 +08:00
										 |  |  | 	ng := WithNPeers(len(sys.peerClients)) | 
					
						
							|  |  |  | 	for idx, client := range sys.peerClients { | 
					
						
							|  |  |  | 		if client == nil { | 
					
						
							|  |  |  | 			continue | 
					
						
							|  |  |  | 		} | 
					
						
							| 
									
										
										
										
											2019-01-14 14:44:20 +08:00
										 |  |  | 		client := client | 
					
						
							|  |  |  | 		ng.Go(context.Background(), func() error { | 
					
						
							|  |  |  | 			return client.SignalService(sig) | 
					
						
							| 
									
										
										
										
											2019-03-15 07:27:31 +08:00
										 |  |  | 		}, idx, *client.host) | 
					
						
							| 
									
										
										
										
											2019-01-14 14:44:20 +08:00
										 |  |  | 	} | 
					
						
							|  |  |  | 	return ng.Wait() | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // ServerInfo - calls ServerInfo RPC call on all peers.
 | 
					
						
							|  |  |  | func (sys *NotificationSys) ServerInfo(ctx context.Context) []ServerInfo { | 
					
						
							| 
									
										
										
										
											2019-03-15 07:27:31 +08:00
										 |  |  | 	serverInfo := make([]ServerInfo, len(sys.peerClients)) | 
					
						
							| 
									
										
										
										
											2018-06-06 16:51:56 +08:00
										 |  |  | 	var wg sync.WaitGroup | 
					
						
							| 
									
										
										
										
											2019-03-15 07:27:31 +08:00
										 |  |  | 	for index, client := range sys.peerClients { | 
					
						
							|  |  |  | 		if client == nil { | 
					
						
							|  |  |  | 			continue | 
					
						
							|  |  |  | 		} | 
					
						
							| 
									
										
										
										
											2018-06-06 16:51:56 +08:00
										 |  |  | 		wg.Add(1) | 
					
						
							| 
									
										
										
										
											2019-03-15 07:27:31 +08:00
										 |  |  | 		go func(idx int, client *peerRESTClient) { | 
					
						
							| 
									
										
										
										
											2018-06-06 16:51:56 +08:00
										 |  |  | 			defer wg.Done() | 
					
						
							| 
									
										
										
										
											2019-01-14 14:44:20 +08:00
										 |  |  | 			// Try to fetch serverInfo remotely in three attempts.
 | 
					
						
							| 
									
										
										
										
											2018-06-06 16:51:56 +08:00
										 |  |  | 			for i := 0; i < 3; i++ { | 
					
						
							| 
									
										
										
										
											2019-01-14 14:44:20 +08:00
										 |  |  | 				info, err := client.ServerInfo() | 
					
						
							| 
									
										
										
										
											2018-06-06 16:51:56 +08:00
										 |  |  | 				if err == nil { | 
					
						
							| 
									
										
										
										
											2019-01-14 14:44:20 +08:00
										 |  |  | 					serverInfo[idx] = ServerInfo{ | 
					
						
							| 
									
										
										
										
											2019-03-15 07:27:31 +08:00
										 |  |  | 						Addr: client.host.String(), | 
					
						
							| 
									
										
										
										
											2019-01-14 14:44:20 +08:00
										 |  |  | 						Data: &info, | 
					
						
							|  |  |  | 					} | 
					
						
							|  |  |  | 					return | 
					
						
							|  |  |  | 				} | 
					
						
							|  |  |  | 				serverInfo[idx] = ServerInfo{ | 
					
						
							| 
									
										
										
										
											2019-03-15 07:27:31 +08:00
										 |  |  | 					Addr:  client.host.String(), | 
					
						
							| 
									
										
										
										
											2019-01-14 14:44:20 +08:00
										 |  |  | 					Data:  &info, | 
					
						
							|  |  |  | 					Error: err.Error(), | 
					
						
							|  |  |  | 				} | 
					
						
							|  |  |  | 				// Last iteration log the error.
 | 
					
						
							|  |  |  | 				if i == 2 { | 
					
						
							| 
									
										
										
										
											2019-03-15 07:27:31 +08:00
										 |  |  | 					reqInfo := (&logger.ReqInfo{}).AppendTags("peerAddress", client.host.String()) | 
					
						
							| 
									
										
										
										
											2019-01-14 14:44:20 +08:00
										 |  |  | 					ctx := logger.SetReqInfo(ctx, reqInfo) | 
					
						
							|  |  |  | 					logger.LogIf(ctx, err) | 
					
						
							| 
									
										
										
										
											2018-06-06 16:51:56 +08:00
										 |  |  | 				} | 
					
						
							|  |  |  | 				// Wait for one second and no need wait after last attempt.
 | 
					
						
							|  |  |  | 				if i < 2 { | 
					
						
							|  |  |  | 					time.Sleep(1 * time.Second) | 
					
						
							|  |  |  | 				} | 
					
						
							|  |  |  | 			} | 
					
						
							| 
									
										
										
										
											2019-03-15 07:27:31 +08:00
										 |  |  | 		}(index, client) | 
					
						
							| 
									
										
										
										
											2018-06-06 16:51:56 +08:00
										 |  |  | 	} | 
					
						
							|  |  |  | 	wg.Wait() | 
					
						
							| 
									
										
										
										
											2019-01-14 14:44:20 +08:00
										 |  |  | 	return serverInfo | 
					
						
							| 
									
										
										
										
											2018-06-06 16:51:56 +08:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-01-24 23:22:14 +08:00
										 |  |  | // GetLocks - makes GetLocks RPC call on all peers.
 | 
					
						
							|  |  |  | func (sys *NotificationSys) GetLocks(ctx context.Context) []*PeerLocks { | 
					
						
							| 
									
										
										
										
											2019-03-15 07:27:31 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	locksResp := make([]*PeerLocks, len(sys.peerClients)) | 
					
						
							| 
									
										
										
										
											2019-01-24 23:22:14 +08:00
										 |  |  | 	var wg sync.WaitGroup | 
					
						
							| 
									
										
										
										
											2019-03-15 07:27:31 +08:00
										 |  |  | 	for index, client := range sys.peerClients { | 
					
						
							|  |  |  | 		if client == nil { | 
					
						
							|  |  |  | 			continue | 
					
						
							|  |  |  | 		} | 
					
						
							| 
									
										
										
										
											2019-01-24 23:22:14 +08:00
										 |  |  | 		wg.Add(1) | 
					
						
							| 
									
										
										
										
											2019-03-15 07:27:31 +08:00
										 |  |  | 		go func(idx int, client *peerRESTClient) { | 
					
						
							| 
									
										
										
										
											2019-01-24 23:22:14 +08:00
										 |  |  | 			defer wg.Done() | 
					
						
							|  |  |  | 			// Try to fetch serverInfo remotely in three attempts.
 | 
					
						
							|  |  |  | 			for i := 0; i < 3; i++ { | 
					
						
							|  |  |  | 				serverLocksResp, err := client.GetLocks() | 
					
						
							|  |  |  | 				if err == nil { | 
					
						
							|  |  |  | 					locksResp[idx] = &PeerLocks{ | 
					
						
							| 
									
										
										
										
											2019-03-15 07:27:31 +08:00
										 |  |  | 						Addr:  client.host.String(), | 
					
						
							| 
									
										
										
										
											2019-01-24 23:22:14 +08:00
										 |  |  | 						Locks: serverLocksResp, | 
					
						
							|  |  |  | 					} | 
					
						
							|  |  |  | 					return | 
					
						
							|  |  |  | 				} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 				// Last iteration log the error.
 | 
					
						
							|  |  |  | 				if i == 2 { | 
					
						
							| 
									
										
										
										
											2019-03-15 07:27:31 +08:00
										 |  |  | 					reqInfo := (&logger.ReqInfo{}).AppendTags("peerAddress", client.host.String()) | 
					
						
							| 
									
										
										
										
											2019-01-24 23:22:14 +08:00
										 |  |  | 					ctx := logger.SetReqInfo(ctx, reqInfo) | 
					
						
							| 
									
										
										
										
											2019-03-15 07:27:31 +08:00
										 |  |  | 					logger.LogOnceIf(ctx, err, client.host.String()) | 
					
						
							| 
									
										
										
										
											2019-01-24 23:22:14 +08:00
										 |  |  | 				} | 
					
						
							|  |  |  | 				// Wait for one second and no need wait after last attempt.
 | 
					
						
							|  |  |  | 				if i < 2 { | 
					
						
							|  |  |  | 					time.Sleep(1 * time.Second) | 
					
						
							|  |  |  | 				} | 
					
						
							|  |  |  | 			} | 
					
						
							| 
									
										
										
										
											2019-03-15 07:27:31 +08:00
										 |  |  | 		}(index, client) | 
					
						
							| 
									
										
										
										
											2019-01-24 23:22:14 +08:00
										 |  |  | 	} | 
					
						
							|  |  |  | 	wg.Wait() | 
					
						
							|  |  |  | 	return locksResp | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-04-25 06:53:30 +08:00
										 |  |  | // SetBucketPolicy - calls SetBucketPolicy RPC call on all peers.
 | 
					
						
							| 
									
										
										
										
											2018-07-04 02:09:36 +08:00
										 |  |  | func (sys *NotificationSys) SetBucketPolicy(ctx context.Context, bucketName string, bucketPolicy *policy.Policy) { | 
					
						
							| 
									
										
										
										
											2018-05-10 06:11:51 +08:00
										 |  |  | 	go func() { | 
					
						
							|  |  |  | 		var wg sync.WaitGroup | 
					
						
							| 
									
										
										
										
											2019-03-15 07:27:31 +08:00
										 |  |  | 		for _, client := range sys.peerClients { | 
					
						
							|  |  |  | 			if client == nil { | 
					
						
							|  |  |  | 				continue | 
					
						
							|  |  |  | 			} | 
					
						
							| 
									
										
										
										
											2018-05-10 06:11:51 +08:00
										 |  |  | 			wg.Add(1) | 
					
						
							| 
									
										
										
										
											2019-03-15 07:27:31 +08:00
										 |  |  | 			go func(client *peerRESTClient) { | 
					
						
							| 
									
										
										
										
											2018-05-10 06:11:51 +08:00
										 |  |  | 				defer wg.Done() | 
					
						
							|  |  |  | 				if err := client.SetBucketPolicy(bucketName, bucketPolicy); err != nil { | 
					
						
							| 
									
										
										
										
											2019-03-15 07:27:31 +08:00
										 |  |  | 					logger.GetReqInfo(ctx).AppendTags("remotePeer", client.host.Name) | 
					
						
							| 
									
										
										
										
											2018-07-04 02:09:36 +08:00
										 |  |  | 					logger.LogIf(ctx, err) | 
					
						
							| 
									
										
										
										
											2018-05-10 02:25:38 +08:00
										 |  |  | 				} | 
					
						
							| 
									
										
										
										
											2019-03-15 07:27:31 +08:00
										 |  |  | 			}(client) | 
					
						
							| 
									
										
										
										
											2018-05-10 06:11:51 +08:00
										 |  |  | 		} | 
					
						
							|  |  |  | 		wg.Wait() | 
					
						
							|  |  |  | 	}() | 
					
						
							| 
									
										
										
										
											2018-04-25 06:53:30 +08:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // RemoveBucketPolicy - calls RemoveBucketPolicy RPC call on all peers.
 | 
					
						
							| 
									
										
										
										
											2018-07-04 02:09:36 +08:00
										 |  |  | func (sys *NotificationSys) RemoveBucketPolicy(ctx context.Context, bucketName string) { | 
					
						
							| 
									
										
										
										
											2018-05-10 06:11:51 +08:00
										 |  |  | 	go func() { | 
					
						
							|  |  |  | 		var wg sync.WaitGroup | 
					
						
							| 
									
										
										
										
											2019-03-15 07:27:31 +08:00
										 |  |  | 		for _, client := range sys.peerClients { | 
					
						
							|  |  |  | 			if client == nil { | 
					
						
							|  |  |  | 				continue | 
					
						
							|  |  |  | 			} | 
					
						
							| 
									
										
										
										
											2018-05-10 06:11:51 +08:00
										 |  |  | 			wg.Add(1) | 
					
						
							| 
									
										
										
										
											2019-03-15 07:27:31 +08:00
										 |  |  | 			go func(client *peerRESTClient) { | 
					
						
							| 
									
										
										
										
											2018-05-10 06:11:51 +08:00
										 |  |  | 				defer wg.Done() | 
					
						
							|  |  |  | 				if err := client.RemoveBucketPolicy(bucketName); err != nil { | 
					
						
							| 
									
										
										
										
											2019-03-15 07:27:31 +08:00
										 |  |  | 					logger.GetReqInfo(ctx).AppendTags("remotePeer", client.host.Name) | 
					
						
							| 
									
										
										
										
											2018-07-04 02:09:36 +08:00
										 |  |  | 					logger.LogIf(ctx, err) | 
					
						
							| 
									
										
										
										
											2018-05-10 02:25:38 +08:00
										 |  |  | 				} | 
					
						
							| 
									
										
										
										
											2019-03-15 07:27:31 +08:00
										 |  |  | 			}(client) | 
					
						
							| 
									
										
										
										
											2018-05-10 06:11:51 +08:00
										 |  |  | 		} | 
					
						
							|  |  |  | 		wg.Wait() | 
					
						
							|  |  |  | 	}() | 
					
						
							| 
									
										
										
										
											2018-03-16 04:03:41 +08:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // PutBucketNotification - calls PutBucketNotification RPC call on all peers.
 | 
					
						
							| 
									
										
										
										
											2018-07-04 02:09:36 +08:00
										 |  |  | func (sys *NotificationSys) PutBucketNotification(ctx context.Context, bucketName string, rulesMap event.RulesMap) { | 
					
						
							| 
									
										
										
										
											2018-05-10 06:11:51 +08:00
										 |  |  | 	go func() { | 
					
						
							|  |  |  | 		var wg sync.WaitGroup | 
					
						
							| 
									
										
										
										
											2019-03-15 07:27:31 +08:00
										 |  |  | 		for _, client := range sys.peerClients { | 
					
						
							|  |  |  | 			if client == nil { | 
					
						
							|  |  |  | 				continue | 
					
						
							|  |  |  | 			} | 
					
						
							| 
									
										
										
										
											2018-05-10 06:11:51 +08:00
										 |  |  | 			wg.Add(1) | 
					
						
							| 
									
										
										
										
											2019-03-15 07:27:31 +08:00
										 |  |  | 			go func(client *peerRESTClient, rulesMap event.RulesMap) { | 
					
						
							| 
									
										
										
										
											2018-05-10 06:11:51 +08:00
										 |  |  | 				defer wg.Done() | 
					
						
							|  |  |  | 				if err := client.PutBucketNotification(bucketName, rulesMap); err != nil { | 
					
						
							| 
									
										
										
										
											2019-03-15 07:27:31 +08:00
										 |  |  | 					logger.GetReqInfo(ctx).AppendTags("remotePeer", client.host.Name) | 
					
						
							| 
									
										
										
										
											2018-07-04 02:09:36 +08:00
										 |  |  | 					logger.LogIf(ctx, err) | 
					
						
							| 
									
										
										
										
											2018-05-10 02:25:38 +08:00
										 |  |  | 				} | 
					
						
							| 
									
										
										
										
											2019-03-15 07:27:31 +08:00
										 |  |  | 			}(client, rulesMap.Clone()) | 
					
						
							| 
									
										
										
										
											2018-05-10 06:11:51 +08:00
										 |  |  | 		} | 
					
						
							|  |  |  | 		wg.Wait() | 
					
						
							|  |  |  | 	}() | 
					
						
							| 
									
										
										
										
											2018-03-16 04:03:41 +08:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // ListenBucketNotification - calls ListenBucketNotification RPC call on all peers.
 | 
					
						
							| 
									
										
										
										
											2018-07-04 02:09:36 +08:00
										 |  |  | func (sys *NotificationSys) ListenBucketNotification(ctx context.Context, bucketName string, eventNames []event.Name, pattern string, | 
					
						
							|  |  |  | 	targetID event.TargetID, localPeer xnet.Host) { | 
					
						
							| 
									
										
										
										
											2018-05-10 06:11:51 +08:00
										 |  |  | 	go func() { | 
					
						
							|  |  |  | 		var wg sync.WaitGroup | 
					
						
							| 
									
										
										
										
											2019-03-15 07:27:31 +08:00
										 |  |  | 		for _, client := range sys.peerClients { | 
					
						
							|  |  |  | 			if client == nil { | 
					
						
							|  |  |  | 				continue | 
					
						
							|  |  |  | 			} | 
					
						
							| 
									
										
										
										
											2018-05-10 06:11:51 +08:00
										 |  |  | 			wg.Add(1) | 
					
						
							| 
									
										
										
										
											2019-03-15 07:27:31 +08:00
										 |  |  | 			go func(client *peerRESTClient) { | 
					
						
							| 
									
										
										
										
											2018-05-10 06:11:51 +08:00
										 |  |  | 				defer wg.Done() | 
					
						
							|  |  |  | 				if err := client.ListenBucketNotification(bucketName, eventNames, pattern, targetID, localPeer); err != nil { | 
					
						
							| 
									
										
										
										
											2019-03-15 07:27:31 +08:00
										 |  |  | 					logger.GetReqInfo(ctx).AppendTags("remotePeer", client.host.Name) | 
					
						
							| 
									
										
										
										
											2018-07-04 02:09:36 +08:00
										 |  |  | 					logger.LogIf(ctx, err) | 
					
						
							| 
									
										
										
										
											2018-05-10 02:25:38 +08:00
										 |  |  | 				} | 
					
						
							| 
									
										
										
										
											2019-03-15 07:27:31 +08:00
										 |  |  | 			}(client) | 
					
						
							| 
									
										
										
										
											2018-05-10 06:11:51 +08:00
										 |  |  | 		} | 
					
						
							|  |  |  | 		wg.Wait() | 
					
						
							|  |  |  | 	}() | 
					
						
							| 
									
										
										
										
											2018-03-16 04:03:41 +08:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // AddRemoteTarget - adds event rules map, HTTP/PeerRPC client target to bucket name.
 | 
					
						
							|  |  |  | func (sys *NotificationSys) AddRemoteTarget(bucketName string, target event.Target, rulesMap event.RulesMap) error { | 
					
						
							|  |  |  | 	if err := sys.targetList.Add(target); err != nil { | 
					
						
							|  |  |  | 		return err | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	sys.Lock() | 
					
						
							|  |  |  | 	targetMap := sys.bucketRemoteTargetRulesMap[bucketName] | 
					
						
							|  |  |  | 	if targetMap == nil { | 
					
						
							|  |  |  | 		targetMap = make(map[event.TargetID]event.RulesMap) | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2018-08-24 01:23:18 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	rulesMap = rulesMap.Clone() | 
					
						
							|  |  |  | 	targetMap[target.ID()] = rulesMap | 
					
						
							| 
									
										
										
										
											2018-03-16 04:03:41 +08:00
										 |  |  | 	sys.bucketRemoteTargetRulesMap[bucketName] = targetMap | 
					
						
							| 
									
										
										
										
											2018-08-24 01:23:18 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	rulesMap = rulesMap.Clone() | 
					
						
							|  |  |  | 	rulesMap.Add(sys.bucketRulesMap[bucketName]) | 
					
						
							|  |  |  | 	sys.bucketRulesMap[bucketName] = rulesMap | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-03-16 04:03:41 +08:00
										 |  |  | 	sys.Unlock() | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	return nil | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // RemoteTargetExist - checks whether given target ID is a HTTP/PeerRPC client target or not.
 | 
					
						
							|  |  |  | func (sys *NotificationSys) RemoteTargetExist(bucketName string, targetID event.TargetID) bool { | 
					
						
							|  |  |  | 	sys.Lock() | 
					
						
							|  |  |  | 	defer sys.Unlock() | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	targetMap, ok := sys.bucketRemoteTargetRulesMap[bucketName] | 
					
						
							|  |  |  | 	if ok { | 
					
						
							|  |  |  | 		_, ok = targetMap[targetID] | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	return ok | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-03-15 07:27:31 +08:00
										 |  |  | // ListenBucketNotificationArgs - listen bucket notification RPC arguments.
 | 
					
						
							|  |  |  | type ListenBucketNotificationArgs struct { | 
					
						
							|  |  |  | 	BucketName string         `json:"-"` | 
					
						
							|  |  |  | 	EventNames []event.Name   `json:"eventNames"` | 
					
						
							|  |  |  | 	Pattern    string         `json:"pattern"` | 
					
						
							|  |  |  | 	TargetID   event.TargetID `json:"targetId"` | 
					
						
							|  |  |  | 	Addr       xnet.Host      `json:"addr"` | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // initListeners - initializes PeerREST clients available in listener.json.
 | 
					
						
							| 
									
										
										
										
											2018-04-06 06:04:40 +08:00
										 |  |  | func (sys *NotificationSys) initListeners(ctx context.Context, objAPI ObjectLayer, bucketName string) error { | 
					
						
							| 
									
										
										
										
											2018-03-16 04:03:41 +08:00
										 |  |  | 	// listener.json is available/applicable only in DistXL mode.
 | 
					
						
							|  |  |  | 	if !globalIsDistXL { | 
					
						
							|  |  |  | 		return nil | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	// Construct path to listener.json for the given bucket.
 | 
					
						
							|  |  |  | 	configFile := path.Join(bucketConfigPrefix, bucketName, bucketListenerConfig) | 
					
						
							|  |  |  | 	transactionConfigFile := configFile + ".transaction" | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	// As object layer's GetObject() and PutObject() take respective lock on minioMetaBucket
 | 
					
						
							|  |  |  | 	// and configFile, take a transaction lock to avoid data race between readConfig()
 | 
					
						
							|  |  |  | 	// and saveConfig().
 | 
					
						
							|  |  |  | 	objLock := globalNSMutex.NewNSLock(minioMetaBucket, transactionConfigFile) | 
					
						
							| 
									
										
										
										
											2018-10-09 06:47:13 +08:00
										 |  |  | 	if err := objLock.GetRLock(globalOperationTimeout); err != nil { | 
					
						
							| 
									
										
										
										
											2018-03-16 04:03:41 +08:00
										 |  |  | 		return err | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2018-10-09 06:47:13 +08:00
										 |  |  | 	defer objLock.RUnlock() | 
					
						
							| 
									
										
										
										
											2018-03-16 04:03:41 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-10-10 05:00:01 +08:00
										 |  |  | 	configData, e := readConfig(ctx, objAPI, configFile) | 
					
						
							| 
									
										
										
										
											2018-04-25 06:53:30 +08:00
										 |  |  | 	if e != nil && !IsErrIgnored(e, errDiskNotFound, errConfigNotFound) { | 
					
						
							| 
									
										
										
										
											2018-04-20 08:24:43 +08:00
										 |  |  | 		return e | 
					
						
							| 
									
										
										
										
											2018-03-16 04:03:41 +08:00
										 |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	listenerList := []ListenBucketNotificationArgs{} | 
					
						
							| 
									
										
										
										
											2018-10-10 05:00:01 +08:00
										 |  |  | 	if configData != nil { | 
					
						
							|  |  |  | 		if err := json.Unmarshal(configData, &listenerList); err != nil { | 
					
						
							| 
									
										
										
										
											2018-04-06 06:04:40 +08:00
										 |  |  | 			logger.LogIf(ctx, err) | 
					
						
							|  |  |  | 			return err | 
					
						
							| 
									
										
										
										
											2018-03-16 04:03:41 +08:00
										 |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	if len(listenerList) == 0 { | 
					
						
							|  |  |  | 		// Nothing to initialize for empty listener list.
 | 
					
						
							|  |  |  | 		return nil | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	for _, args := range listenerList { | 
					
						
							| 
									
										
										
										
											2018-04-20 08:24:43 +08:00
										 |  |  | 		found, err := isLocalHost(args.Addr.Name) | 
					
						
							|  |  |  | 		if err != nil { | 
					
						
							| 
									
										
										
										
											2018-04-06 06:04:40 +08:00
										 |  |  | 			logger.GetReqInfo(ctx).AppendTags("host", args.Addr.Name) | 
					
						
							|  |  |  | 			logger.LogIf(ctx, err) | 
					
						
							| 
									
										
										
										
											2018-03-16 04:03:41 +08:00
										 |  |  | 			return err | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		if found { | 
					
						
							|  |  |  | 			// As this function is called at startup, skip HTTP listener to this host.
 | 
					
						
							|  |  |  | 			continue | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-03-15 07:27:31 +08:00
										 |  |  | 		client, err := newPeerRESTClient(&args.Addr) | 
					
						
							|  |  |  | 		if err != nil { | 
					
						
							|  |  |  | 			return fmt.Errorf("unable to find PeerHost by address %v in listener.json for bucket %v", args.Addr, bucketName) | 
					
						
							| 
									
										
										
										
											2018-03-16 04:03:41 +08:00
										 |  |  | 		} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-03-15 07:27:31 +08:00
										 |  |  | 		exist, err := client.RemoteTargetExist(bucketName, args.TargetID) | 
					
						
							| 
									
										
										
										
											2018-04-20 08:24:43 +08:00
										 |  |  | 		if err != nil { | 
					
						
							| 
									
										
										
										
											2018-04-06 06:04:40 +08:00
										 |  |  | 			logger.GetReqInfo(ctx).AppendTags("targetID", args.TargetID.Name) | 
					
						
							|  |  |  | 			logger.LogIf(ctx, err) | 
					
						
							| 
									
										
										
										
											2018-03-16 04:03:41 +08:00
										 |  |  | 			return err | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		if !exist { | 
					
						
							|  |  |  | 			// Skip previously connected HTTP listener which is not found in remote peer.
 | 
					
						
							|  |  |  | 			continue | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-03-15 07:27:31 +08:00
										 |  |  | 		target := NewPeerRESTClientTarget(bucketName, args.TargetID, client) | 
					
						
							| 
									
										
										
										
											2018-03-16 04:03:41 +08:00
										 |  |  | 		rulesMap := event.NewRulesMap(args.EventNames, args.Pattern, target.ID()) | 
					
						
							|  |  |  | 		if err = sys.AddRemoteTarget(bucketName, target, rulesMap); err != nil { | 
					
						
							| 
									
										
										
										
											2018-04-20 08:24:43 +08:00
										 |  |  | 			logger.GetReqInfo(ctx).AppendTags("targetName", target.id.Name) | 
					
						
							| 
									
										
										
										
											2018-04-06 06:04:40 +08:00
										 |  |  | 			logger.LogIf(ctx, err) | 
					
						
							| 
									
										
										
										
											2018-03-16 04:03:41 +08:00
										 |  |  | 			return err | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-10-09 06:47:13 +08:00
										 |  |  | 	return nil | 
					
						
							| 
									
										
										
										
											2018-03-16 04:03:41 +08:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-10-09 06:47:13 +08:00
										 |  |  | func (sys *NotificationSys) refresh(objAPI ObjectLayer) error { | 
					
						
							| 
									
										
										
										
											2018-03-16 04:27:16 +08:00
										 |  |  | 	buckets, err := objAPI.ListBuckets(context.Background()) | 
					
						
							| 
									
										
										
										
											2018-03-16 04:03:41 +08:00
										 |  |  | 	if err != nil { | 
					
						
							|  |  |  | 		return err | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	for _, bucket := range buckets { | 
					
						
							| 
									
										
										
										
											2018-04-06 06:04:40 +08:00
										 |  |  | 		ctx := logger.SetReqInfo(context.Background(), &logger.ReqInfo{BucketName: bucket.Name}) | 
					
						
							|  |  |  | 		config, err := readNotificationConfig(ctx, objAPI, bucket.Name) | 
					
						
							| 
									
										
										
										
											2018-10-09 06:47:13 +08:00
										 |  |  | 		if err != nil && err != errNoSuchNotifications { | 
					
						
							|  |  |  | 			return err | 
					
						
							| 
									
										
										
										
											2018-03-16 04:03:41 +08:00
										 |  |  | 		} | 
					
						
							| 
									
										
										
										
											2018-10-09 06:47:13 +08:00
										 |  |  | 		if err == errNoSuchNotifications { | 
					
						
							|  |  |  | 			continue | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		sys.AddRulesMap(bucket.Name, config.ToRulesMap()) | 
					
						
							| 
									
										
										
										
											2018-04-06 06:04:40 +08:00
										 |  |  | 		if err = sys.initListeners(ctx, objAPI, bucket.Name); err != nil { | 
					
						
							| 
									
										
										
										
											2018-03-16 04:03:41 +08:00
										 |  |  | 			return err | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	return nil | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-10-09 06:47:13 +08:00
										 |  |  | // Init - initializes notification system from notification.xml and listener.json of all buckets.
 | 
					
						
							|  |  |  | func (sys *NotificationSys) Init(objAPI ObjectLayer) error { | 
					
						
							|  |  |  | 	if objAPI == nil { | 
					
						
							|  |  |  | 		return errInvalidArgument | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	doneCh := make(chan struct{}) | 
					
						
							|  |  |  | 	defer close(doneCh) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	// Initializing notification needs a retry mechanism for
 | 
					
						
							|  |  |  | 	// the following reasons:
 | 
					
						
							|  |  |  | 	//  - Read quorum is lost just after the initialization
 | 
					
						
							|  |  |  | 	//    of the object layer.
 | 
					
						
							| 
									
										
										
										
											2019-02-13 20:59:36 +08:00
										 |  |  | 	for range newRetryTimerSimple(doneCh) { | 
					
						
							|  |  |  | 		if err := sys.refresh(objAPI); err != nil { | 
					
						
							|  |  |  | 			if err == errDiskNotFound || | 
					
						
							|  |  |  | 				strings.Contains(err.Error(), InsufficientReadQuorum{}.Error()) || | 
					
						
							|  |  |  | 				strings.Contains(err.Error(), InsufficientWriteQuorum{}.Error()) { | 
					
						
							|  |  |  | 				logger.Info("Waiting for notification subsystem to be initialized..") | 
					
						
							|  |  |  | 				continue | 
					
						
							| 
									
										
										
										
											2018-10-09 06:47:13 +08:00
										 |  |  | 			} | 
					
						
							| 
									
										
										
										
											2019-02-13 20:59:36 +08:00
										 |  |  | 			return err | 
					
						
							| 
									
										
										
										
											2018-10-09 06:47:13 +08:00
										 |  |  | 		} | 
					
						
							| 
									
										
										
										
											2019-02-13 20:59:36 +08:00
										 |  |  | 		break | 
					
						
							| 
									
										
										
										
											2018-10-09 06:47:13 +08:00
										 |  |  | 	} | 
					
						
							| 
									
										
										
										
											2019-02-13 20:59:36 +08:00
										 |  |  | 	return nil | 
					
						
							| 
									
										
										
										
											2018-10-09 06:47:13 +08:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-03-16 04:03:41 +08:00
										 |  |  | // AddRulesMap - adds rules map for bucket name.
 | 
					
						
							|  |  |  | func (sys *NotificationSys) AddRulesMap(bucketName string, rulesMap event.RulesMap) { | 
					
						
							|  |  |  | 	sys.Lock() | 
					
						
							|  |  |  | 	defer sys.Unlock() | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	rulesMap = rulesMap.Clone() | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	for _, targetRulesMap := range sys.bucketRemoteTargetRulesMap[bucketName] { | 
					
						
							|  |  |  | 		rulesMap.Add(targetRulesMap) | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-08-24 01:23:18 +08:00
										 |  |  | 	// Do not add for an empty rulesMap.
 | 
					
						
							|  |  |  | 	if len(rulesMap) == 0 { | 
					
						
							|  |  |  | 		delete(sys.bucketRulesMap, bucketName) | 
					
						
							|  |  |  | 	} else { | 
					
						
							|  |  |  | 		sys.bucketRulesMap[bucketName] = rulesMap | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2018-03-16 04:03:41 +08:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // RemoveRulesMap - removes rules map for bucket name.
 | 
					
						
							|  |  |  | func (sys *NotificationSys) RemoveRulesMap(bucketName string, rulesMap event.RulesMap) { | 
					
						
							|  |  |  | 	sys.Lock() | 
					
						
							|  |  |  | 	defer sys.Unlock() | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	sys.bucketRulesMap[bucketName].Remove(rulesMap) | 
					
						
							|  |  |  | 	if len(sys.bucketRulesMap[bucketName]) == 0 { | 
					
						
							|  |  |  | 		delete(sys.bucketRulesMap, bucketName) | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // RemoveNotification - removes all notification configuration for bucket name.
 | 
					
						
							|  |  |  | func (sys *NotificationSys) RemoveNotification(bucketName string) { | 
					
						
							|  |  |  | 	sys.Lock() | 
					
						
							|  |  |  | 	defer sys.Unlock() | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	delete(sys.bucketRulesMap, bucketName) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	for targetID := range sys.bucketRemoteTargetRulesMap[bucketName] { | 
					
						
							|  |  |  | 		sys.targetList.Remove(targetID) | 
					
						
							|  |  |  | 		delete(sys.bucketRemoteTargetRulesMap[bucketName], targetID) | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	delete(sys.bucketRemoteTargetRulesMap, bucketName) | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // RemoveAllRemoteTargets - closes and removes all HTTP/PeerRPC client targets.
 | 
					
						
							|  |  |  | func (sys *NotificationSys) RemoveAllRemoteTargets() { | 
					
						
							|  |  |  | 	for _, targetMap := range sys.bucketRemoteTargetRulesMap { | 
					
						
							|  |  |  | 		for targetID := range targetMap { | 
					
						
							|  |  |  | 			sys.targetList.Remove(targetID) | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // RemoveRemoteTarget - closes and removes target by target ID.
 | 
					
						
							|  |  |  | func (sys *NotificationSys) RemoveRemoteTarget(bucketName string, targetID event.TargetID) { | 
					
						
							| 
									
										
										
										
											2018-05-10 06:11:51 +08:00
										 |  |  | 	for terr := range sys.targetList.Remove(targetID) { | 
					
						
							|  |  |  | 		reqInfo := (&logger.ReqInfo{}).AppendTags("targetID", terr.ID.Name) | 
					
						
							| 
									
										
										
										
											2018-04-06 06:04:40 +08:00
										 |  |  | 		ctx := logger.SetReqInfo(context.Background(), reqInfo) | 
					
						
							| 
									
										
										
										
											2018-05-10 06:11:51 +08:00
										 |  |  | 		logger.LogIf(ctx, terr.Err) | 
					
						
							| 
									
										
										
										
											2018-03-16 04:03:41 +08:00
										 |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	sys.Lock() | 
					
						
							|  |  |  | 	defer sys.Unlock() | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	if _, ok := sys.bucketRemoteTargetRulesMap[bucketName]; ok { | 
					
						
							|  |  |  | 		delete(sys.bucketRemoteTargetRulesMap[bucketName], targetID) | 
					
						
							|  |  |  | 		if len(sys.bucketRemoteTargetRulesMap[bucketName]) == 0 { | 
					
						
							|  |  |  | 			delete(sys.bucketRemoteTargetRulesMap, bucketName) | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-05-10 06:11:51 +08:00
										 |  |  | func (sys *NotificationSys) send(bucketName string, eventData event.Event, targetIDs ...event.TargetID) (errs []event.TargetIDErr) { | 
					
						
							|  |  |  | 	errCh := sys.targetList.Send(eventData, targetIDs...) | 
					
						
							|  |  |  | 	for terr := range errCh { | 
					
						
							|  |  |  | 		errs = append(errs, terr) | 
					
						
							|  |  |  | 		if sys.RemoteTargetExist(bucketName, terr.ID) { | 
					
						
							|  |  |  | 			sys.RemoveRemoteTarget(bucketName, terr.ID) | 
					
						
							| 
									
										
										
										
											2018-03-16 04:03:41 +08:00
										 |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-05-10 06:11:51 +08:00
										 |  |  | 	return errs | 
					
						
							| 
									
										
										
										
											2018-03-16 04:03:41 +08:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // Send - sends event data to all matching targets.
 | 
					
						
							| 
									
										
										
										
											2018-05-10 06:11:51 +08:00
										 |  |  | func (sys *NotificationSys) Send(args eventArgs) []event.TargetIDErr { | 
					
						
							| 
									
										
										
										
											2018-03-16 04:03:41 +08:00
										 |  |  | 	sys.RLock() | 
					
						
							|  |  |  | 	targetIDSet := sys.bucketRulesMap[args.BucketName].Match(args.EventName, args.Object.Name) | 
					
						
							|  |  |  | 	sys.RUnlock() | 
					
						
							| 
									
										
										
										
											2018-10-13 03:25:59 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-03-16 04:03:41 +08:00
										 |  |  | 	if len(targetIDSet) == 0 { | 
					
						
							|  |  |  | 		return nil | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	targetIDs := targetIDSet.ToSlice() | 
					
						
							|  |  |  | 	return sys.send(args.BucketName, args.ToEvent(), targetIDs...) | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-01-01 01:46:44 +08:00
										 |  |  | // DrivePerfInfo - Drive speed (read and write) information
 | 
					
						
							|  |  |  | func (sys *NotificationSys) DrivePerfInfo() []ServerDrivesPerfInfo { | 
					
						
							| 
									
										
										
										
											2019-03-15 07:27:31 +08:00
										 |  |  | 	reply := make([]ServerDrivesPerfInfo, len(sys.peerClients)) | 
					
						
							| 
									
										
										
										
											2019-01-01 01:46:44 +08:00
										 |  |  | 	var wg sync.WaitGroup | 
					
						
							| 
									
										
										
										
											2019-03-15 07:27:31 +08:00
										 |  |  | 	for i, client := range sys.peerClients { | 
					
						
							|  |  |  | 		if client == nil { | 
					
						
							|  |  |  | 			continue | 
					
						
							|  |  |  | 		} | 
					
						
							| 
									
										
										
										
											2019-01-01 01:46:44 +08:00
										 |  |  | 		wg.Add(1) | 
					
						
							| 
									
										
										
										
											2019-03-15 07:27:31 +08:00
										 |  |  | 		go func(client *peerRESTClient, idx int) { | 
					
						
							| 
									
										
										
										
											2019-01-01 01:46:44 +08:00
										 |  |  | 			defer wg.Done() | 
					
						
							|  |  |  | 			di, err := client.DrivePerfInfo() | 
					
						
							|  |  |  | 			if err != nil { | 
					
						
							| 
									
										
										
										
											2019-03-15 07:27:31 +08:00
										 |  |  | 				reqInfo := (&logger.ReqInfo{}).AppendTags("remotePeer", client.host.String()) | 
					
						
							| 
									
										
										
										
											2019-01-01 01:46:44 +08:00
										 |  |  | 				ctx := logger.SetReqInfo(context.Background(), reqInfo) | 
					
						
							|  |  |  | 				logger.LogIf(ctx, err) | 
					
						
							| 
									
										
										
										
											2019-03-15 07:27:31 +08:00
										 |  |  | 				di.Addr = client.host.String() | 
					
						
							| 
									
										
										
										
											2019-01-01 01:46:44 +08:00
										 |  |  | 				di.Error = err.Error() | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 			reply[idx] = di | 
					
						
							| 
									
										
										
										
											2019-03-15 07:27:31 +08:00
										 |  |  | 		}(client, i) | 
					
						
							| 
									
										
										
										
											2019-01-01 01:46:44 +08:00
										 |  |  | 	} | 
					
						
							|  |  |  | 	wg.Wait() | 
					
						
							|  |  |  | 	return reply | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-01-10 11:04:19 +08:00
										 |  |  | // MemUsageInfo - Mem utilization information
 | 
					
						
							|  |  |  | func (sys *NotificationSys) MemUsageInfo() []ServerMemUsageInfo { | 
					
						
							| 
									
										
										
										
											2019-03-15 07:27:31 +08:00
										 |  |  | 	reply := make([]ServerMemUsageInfo, len(sys.peerClients)) | 
					
						
							| 
									
										
										
										
											2019-01-10 11:04:19 +08:00
										 |  |  | 	var wg sync.WaitGroup | 
					
						
							| 
									
										
										
										
											2019-03-15 07:27:31 +08:00
										 |  |  | 	for i, client := range sys.peerClients { | 
					
						
							|  |  |  | 		if client == nil { | 
					
						
							|  |  |  | 			continue | 
					
						
							|  |  |  | 		} | 
					
						
							| 
									
										
										
										
											2019-01-10 11:04:19 +08:00
										 |  |  | 		wg.Add(1) | 
					
						
							| 
									
										
										
										
											2019-03-15 07:27:31 +08:00
										 |  |  | 		go func(client *peerRESTClient, idx int) { | 
					
						
							| 
									
										
										
										
											2019-01-10 11:04:19 +08:00
										 |  |  | 			defer wg.Done() | 
					
						
							|  |  |  | 			memi, err := client.MemUsageInfo() | 
					
						
							|  |  |  | 			if err != nil { | 
					
						
							| 
									
										
										
										
											2019-03-15 07:27:31 +08:00
										 |  |  | 				reqInfo := (&logger.ReqInfo{}).AppendTags("remotePeer", client.host.String()) | 
					
						
							| 
									
										
										
										
											2019-01-10 11:04:19 +08:00
										 |  |  | 				ctx := logger.SetReqInfo(context.Background(), reqInfo) | 
					
						
							|  |  |  | 				logger.LogIf(ctx, err) | 
					
						
							| 
									
										
										
										
											2019-03-15 07:27:31 +08:00
										 |  |  | 				memi.Addr = client.host.String() | 
					
						
							| 
									
										
										
										
											2019-01-10 11:04:19 +08:00
										 |  |  | 				memi.Error = err.Error() | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 			reply[idx] = memi | 
					
						
							| 
									
										
										
										
											2019-03-15 07:27:31 +08:00
										 |  |  | 		}(client, i) | 
					
						
							| 
									
										
										
										
											2019-01-10 11:04:19 +08:00
										 |  |  | 	} | 
					
						
							|  |  |  | 	wg.Wait() | 
					
						
							|  |  |  | 	return reply | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // CPULoadInfo - CPU utilization information
 | 
					
						
							|  |  |  | func (sys *NotificationSys) CPULoadInfo() []ServerCPULoadInfo { | 
					
						
							| 
									
										
										
										
											2019-03-15 07:27:31 +08:00
										 |  |  | 	reply := make([]ServerCPULoadInfo, len(sys.peerClients)) | 
					
						
							| 
									
										
										
										
											2019-01-10 11:04:19 +08:00
										 |  |  | 	var wg sync.WaitGroup | 
					
						
							| 
									
										
										
										
											2019-03-15 07:27:31 +08:00
										 |  |  | 	for i, client := range sys.peerClients { | 
					
						
							|  |  |  | 		if client == nil { | 
					
						
							|  |  |  | 			continue | 
					
						
							|  |  |  | 		} | 
					
						
							| 
									
										
										
										
											2019-01-10 11:04:19 +08:00
										 |  |  | 		wg.Add(1) | 
					
						
							| 
									
										
										
										
											2019-03-15 07:27:31 +08:00
										 |  |  | 		go func(client *peerRESTClient, idx int) { | 
					
						
							| 
									
										
										
										
											2019-01-10 11:04:19 +08:00
										 |  |  | 			defer wg.Done() | 
					
						
							|  |  |  | 			cpui, err := client.CPULoadInfo() | 
					
						
							|  |  |  | 			if err != nil { | 
					
						
							| 
									
										
										
										
											2019-03-15 07:27:31 +08:00
										 |  |  | 				reqInfo := (&logger.ReqInfo{}).AppendTags("remotePeer", client.host.String()) | 
					
						
							| 
									
										
										
										
											2019-01-10 11:04:19 +08:00
										 |  |  | 				ctx := logger.SetReqInfo(context.Background(), reqInfo) | 
					
						
							|  |  |  | 				logger.LogIf(ctx, err) | 
					
						
							| 
									
										
										
										
											2019-03-15 07:27:31 +08:00
										 |  |  | 				cpui.Addr = client.host.String() | 
					
						
							| 
									
										
										
										
											2019-01-10 11:04:19 +08:00
										 |  |  | 				cpui.Error = err.Error() | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 			reply[idx] = cpui | 
					
						
							| 
									
										
										
										
											2019-03-15 07:27:31 +08:00
										 |  |  | 		}(client, i) | 
					
						
							| 
									
										
										
										
											2019-01-10 11:04:19 +08:00
										 |  |  | 	} | 
					
						
							|  |  |  | 	wg.Wait() | 
					
						
							|  |  |  | 	return reply | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-03-16 04:03:41 +08:00
										 |  |  | // NewNotificationSys - creates new notification system object.
 | 
					
						
							| 
									
										
										
										
											2018-07-10 09:50:31 +08:00
										 |  |  | func NewNotificationSys(config *serverConfig, endpoints EndpointList) *NotificationSys { | 
					
						
							|  |  |  | 	targetList := getNotificationTargets(config) | 
					
						
							| 
									
										
										
										
											2019-03-15 07:27:31 +08:00
										 |  |  | 	remoteHosts := getRemoteHosts(endpoints) | 
					
						
							|  |  |  | 	remoteClients, err := getRestClients(remoteHosts) | 
					
						
							|  |  |  | 	if err != nil { | 
					
						
							|  |  |  | 		logger.FatalIf(err, "Unable to start notification sub system") | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2018-03-16 04:03:41 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	// bucketRulesMap/bucketRemoteTargetRulesMap are initialized by NotificationSys.Init()
 | 
					
						
							|  |  |  | 	return &NotificationSys{ | 
					
						
							|  |  |  | 		targetList:                 targetList, | 
					
						
							|  |  |  | 		bucketRulesMap:             make(map[string]event.RulesMap), | 
					
						
							|  |  |  | 		bucketRemoteTargetRulesMap: make(map[string]map[event.TargetID]event.RulesMap), | 
					
						
							| 
									
										
										
										
											2019-03-15 07:27:31 +08:00
										 |  |  | 		peerClients:                remoteClients, | 
					
						
							| 
									
										
										
										
											2018-07-10 09:50:31 +08:00
										 |  |  | 	} | 
					
						
							| 
									
										
										
										
											2018-03-16 04:03:41 +08:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | type eventArgs struct { | 
					
						
							| 
									
										
										
										
											2018-08-24 05:40:54 +08:00
										 |  |  | 	EventName    event.Name | 
					
						
							|  |  |  | 	BucketName   string | 
					
						
							|  |  |  | 	Object       ObjectInfo | 
					
						
							|  |  |  | 	ReqParams    map[string]string | 
					
						
							|  |  |  | 	RespElements map[string]string | 
					
						
							|  |  |  | 	Host         string | 
					
						
							|  |  |  | 	UserAgent    string | 
					
						
							| 
									
										
										
										
											2018-03-16 04:03:41 +08:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // ToEvent - converts to notification event.
 | 
					
						
							|  |  |  | func (args eventArgs) ToEvent() event.Event { | 
					
						
							|  |  |  | 	getOriginEndpoint := func() string { | 
					
						
							|  |  |  | 		host := globalMinioHost | 
					
						
							|  |  |  | 		if host == "" { | 
					
						
							|  |  |  | 			// FIXME: Send FQDN or hostname of this machine than sending IP address.
 | 
					
						
							| 
									
										
										
										
											2018-12-19 02:05:26 +08:00
										 |  |  | 			host = sortIPs(localIP4.ToSlice())[0] | 
					
						
							| 
									
										
										
										
											2018-03-16 04:03:41 +08:00
										 |  |  | 		} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-12-19 02:05:26 +08:00
										 |  |  | 		return fmt.Sprintf("%s://%s", getURLScheme(globalIsSSL), net.JoinHostPort(host, globalMinioPort)) | 
					
						
							| 
									
										
										
										
											2018-03-16 04:03:41 +08:00
										 |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	eventTime := UTCNow() | 
					
						
							|  |  |  | 	uniqueID := fmt.Sprintf("%X", eventTime.UnixNano()) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-08-24 05:40:54 +08:00
										 |  |  | 	respElements := map[string]string{ | 
					
						
							| 
									
										
										
										
											2018-11-03 09:40:08 +08:00
										 |  |  | 		"x-amz-request-id":        args.RespElements["requestId"], | 
					
						
							| 
									
										
										
										
											2019-04-10 02:39:42 +08:00
										 |  |  | 		"x-minio-origin-endpoint": getOriginEndpoint(), // MinIO specific custom elements.
 | 
					
						
							| 
									
										
										
										
											2018-08-24 05:40:54 +08:00
										 |  |  | 	} | 
					
						
							| 
									
										
										
										
											2018-12-19 02:05:26 +08:00
										 |  |  | 	// Add deployment as part of
 | 
					
						
							|  |  |  | 	if globalDeploymentID != "" { | 
					
						
							|  |  |  | 		respElements["x-minio-deployment-id"] = globalDeploymentID | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2018-08-24 05:40:54 +08:00
										 |  |  | 	if args.RespElements["content-length"] != "" { | 
					
						
							|  |  |  | 		respElements["content-length"] = args.RespElements["content-length"] | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2018-03-16 04:03:41 +08:00
										 |  |  | 	newEvent := event.Event{ | 
					
						
							|  |  |  | 		EventVersion:      "2.0", | 
					
						
							|  |  |  | 		EventSource:       "minio:s3", | 
					
						
							| 
									
										
										
										
											2018-11-03 09:40:08 +08:00
										 |  |  | 		AwsRegion:         args.ReqParams["region"], | 
					
						
							| 
									
										
										
										
											2018-03-16 04:03:41 +08:00
										 |  |  | 		EventTime:         eventTime.Format(event.AMZTimeFormat), | 
					
						
							|  |  |  | 		EventName:         args.EventName, | 
					
						
							| 
									
										
										
										
											2018-11-15 02:23:44 +08:00
										 |  |  | 		UserIdentity:      event.Identity{PrincipalID: args.ReqParams["accessKey"]}, | 
					
						
							| 
									
										
										
										
											2018-03-16 04:03:41 +08:00
										 |  |  | 		RequestParameters: args.ReqParams, | 
					
						
							| 
									
										
										
										
											2018-08-24 05:40:54 +08:00
										 |  |  | 		ResponseElements:  respElements, | 
					
						
							| 
									
										
										
										
											2018-03-16 04:03:41 +08:00
										 |  |  | 		S3: event.Metadata{ | 
					
						
							|  |  |  | 			SchemaVersion:   "1.0", | 
					
						
							|  |  |  | 			ConfigurationID: "Config", | 
					
						
							|  |  |  | 			Bucket: event.Bucket{ | 
					
						
							|  |  |  | 				Name:          args.BucketName, | 
					
						
							| 
									
										
										
										
											2018-11-15 02:23:44 +08:00
										 |  |  | 				OwnerIdentity: event.Identity{PrincipalID: args.ReqParams["accessKey"]}, | 
					
						
							| 
									
										
										
										
											2018-04-25 06:53:30 +08:00
										 |  |  | 				ARN:           policy.ResourceARNPrefix + args.BucketName, | 
					
						
							| 
									
										
										
										
											2018-03-16 04:03:41 +08:00
										 |  |  | 			}, | 
					
						
							|  |  |  | 			Object: event.Object{ | 
					
						
							|  |  |  | 				Key:       url.QueryEscape(args.Object.Name), | 
					
						
							|  |  |  | 				VersionID: "1", | 
					
						
							|  |  |  | 				Sequencer: uniqueID, | 
					
						
							|  |  |  | 			}, | 
					
						
							|  |  |  | 		}, | 
					
						
							|  |  |  | 		Source: event.Source{ | 
					
						
							|  |  |  | 			Host:      args.Host, | 
					
						
							|  |  |  | 			UserAgent: args.UserAgent, | 
					
						
							|  |  |  | 		}, | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	if args.EventName != event.ObjectRemovedDelete { | 
					
						
							|  |  |  | 		newEvent.S3.Object.ETag = args.Object.ETag | 
					
						
							|  |  |  | 		newEvent.S3.Object.Size = args.Object.Size | 
					
						
							| 
									
										
										
										
											2018-12-12 09:30:15 +08:00
										 |  |  | 		if args.Object.IsCompressed() { | 
					
						
							|  |  |  | 			newEvent.S3.Object.Size = args.Object.GetActualSize() | 
					
						
							|  |  |  | 		} | 
					
						
							| 
									
										
										
										
											2018-03-16 04:03:41 +08:00
										 |  |  | 		newEvent.S3.Object.ContentType = args.Object.ContentType | 
					
						
							|  |  |  | 		newEvent.S3.Object.UserMetadata = args.Object.UserDefined | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	return newEvent | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | func sendEvent(args eventArgs) { | 
					
						
							| 
									
										
										
										
											2019-05-30 13:29:37 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	// remove sensitive encryption entries in metadata.
 | 
					
						
							|  |  |  | 	crypto.RemoveSensitiveEntries(args.Object.UserDefined) | 
					
						
							|  |  |  | 	crypto.RemoveInternalEntries(args.Object.UserDefined) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-03-16 04:03:41 +08:00
										 |  |  | 	// globalNotificationSys is not initialized in gateway mode.
 | 
					
						
							|  |  |  | 	if globalNotificationSys == nil { | 
					
						
							|  |  |  | 		return | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-07-04 02:09:36 +08:00
										 |  |  | 	notifyCh := globalNotificationSys.Send(args) | 
					
						
							|  |  |  | 	go func() { | 
					
						
							|  |  |  | 		for _, err := range notifyCh { | 
					
						
							|  |  |  | 			reqInfo := &logger.ReqInfo{BucketName: args.BucketName, ObjectName: args.Object.Name} | 
					
						
							|  |  |  | 			reqInfo.AppendTags("EventName", args.EventName.String()) | 
					
						
							|  |  |  | 			reqInfo.AppendTags("targetID", err.ID.Name) | 
					
						
							|  |  |  | 			ctx := logger.SetReqInfo(context.Background(), reqInfo) | 
					
						
							|  |  |  | 			logger.LogOnceIf(ctx, err.Err, err.ID) | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	}() | 
					
						
							| 
									
										
										
										
											2018-03-16 04:03:41 +08:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-04-06 06:04:40 +08:00
										 |  |  | func readNotificationConfig(ctx context.Context, objAPI ObjectLayer, bucketName string) (*event.Config, error) { | 
					
						
							| 
									
										
										
										
											2018-03-16 04:03:41 +08:00
										 |  |  | 	// Construct path to notification.xml for the given bucket.
 | 
					
						
							|  |  |  | 	configFile := path.Join(bucketConfigPrefix, bucketName, bucketNotificationConfig) | 
					
						
							| 
									
										
										
										
											2018-10-10 05:00:01 +08:00
										 |  |  | 	configData, err := readConfig(ctx, objAPI, configFile) | 
					
						
							| 
									
										
										
										
											2018-03-16 04:03:41 +08:00
										 |  |  | 	if err != nil { | 
					
						
							| 
									
										
										
										
											2018-04-25 06:53:30 +08:00
										 |  |  | 		if err == errConfigNotFound { | 
					
						
							|  |  |  | 			err = errNoSuchNotifications | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-03-16 04:03:41 +08:00
										 |  |  | 		return nil, err | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-10-10 05:00:01 +08:00
										 |  |  | 	config, err := event.ParseConfig(bytes.NewReader(configData), globalServerConfig.GetRegion(), globalNotificationSys.targetList) | 
					
						
							| 
									
										
										
										
											2018-04-06 06:04:40 +08:00
										 |  |  | 	logger.LogIf(ctx, err) | 
					
						
							|  |  |  | 	return config, err | 
					
						
							| 
									
										
										
										
											2018-03-16 04:03:41 +08:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-10-10 05:00:01 +08:00
										 |  |  | func saveNotificationConfig(ctx context.Context, objAPI ObjectLayer, bucketName string, config *event.Config) error { | 
					
						
							| 
									
										
										
										
											2018-03-16 04:03:41 +08:00
										 |  |  | 	data, err := xml.Marshal(config) | 
					
						
							|  |  |  | 	if err != nil { | 
					
						
							|  |  |  | 		return err | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	configFile := path.Join(bucketConfigPrefix, bucketName, bucketNotificationConfig) | 
					
						
							| 
									
										
										
										
											2018-10-10 05:00:01 +08:00
										 |  |  | 	return saveConfig(ctx, objAPI, configFile, data) | 
					
						
							| 
									
										
										
										
											2018-03-16 04:03:41 +08:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // SaveListener - saves HTTP client currently listening for events to listener.json.
 | 
					
						
							|  |  |  | func SaveListener(objAPI ObjectLayer, bucketName string, eventNames []event.Name, pattern string, targetID event.TargetID, addr xnet.Host) error { | 
					
						
							|  |  |  | 	// listener.json is available/applicable only in DistXL mode.
 | 
					
						
							|  |  |  | 	if !globalIsDistXL { | 
					
						
							|  |  |  | 		return nil | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-04-06 06:04:40 +08:00
										 |  |  | 	ctx := logger.SetReqInfo(context.Background(), &logger.ReqInfo{BucketName: bucketName}) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-03-16 04:03:41 +08:00
										 |  |  | 	// Construct path to listener.json for the given bucket.
 | 
					
						
							|  |  |  | 	configFile := path.Join(bucketConfigPrefix, bucketName, bucketListenerConfig) | 
					
						
							|  |  |  | 	transactionConfigFile := configFile + ".transaction" | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	// As object layer's GetObject() and PutObject() take respective lock on minioMetaBucket
 | 
					
						
							|  |  |  | 	// and configFile, take a transaction lock to avoid data race between readConfig()
 | 
					
						
							|  |  |  | 	// and saveConfig().
 | 
					
						
							|  |  |  | 	objLock := globalNSMutex.NewNSLock(minioMetaBucket, transactionConfigFile) | 
					
						
							|  |  |  | 	if err := objLock.GetLock(globalOperationTimeout); err != nil { | 
					
						
							|  |  |  | 		return err | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	defer objLock.Unlock() | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-10-10 05:00:01 +08:00
										 |  |  | 	configData, err := readConfig(ctx, objAPI, configFile) | 
					
						
							| 
									
										
										
										
											2018-04-25 06:53:30 +08:00
										 |  |  | 	if err != nil && !IsErrIgnored(err, errDiskNotFound, errConfigNotFound) { | 
					
						
							| 
									
										
										
										
											2018-03-16 04:03:41 +08:00
										 |  |  | 		return err | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	listenerList := []ListenBucketNotificationArgs{} | 
					
						
							| 
									
										
										
										
											2018-10-10 05:00:01 +08:00
										 |  |  | 	if configData != nil { | 
					
						
							|  |  |  | 		if err = json.Unmarshal(configData, &listenerList); err != nil { | 
					
						
							| 
									
										
										
										
											2018-04-06 06:04:40 +08:00
										 |  |  | 			logger.LogIf(ctx, err) | 
					
						
							|  |  |  | 			return err | 
					
						
							| 
									
										
										
										
											2018-03-16 04:03:41 +08:00
										 |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	listenerList = append(listenerList, ListenBucketNotificationArgs{ | 
					
						
							|  |  |  | 		EventNames: eventNames, | 
					
						
							|  |  |  | 		Pattern:    pattern, | 
					
						
							|  |  |  | 		TargetID:   targetID, | 
					
						
							|  |  |  | 		Addr:       addr, | 
					
						
							|  |  |  | 	}) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	data, err := json.Marshal(listenerList) | 
					
						
							|  |  |  | 	if err != nil { | 
					
						
							| 
									
										
										
										
											2018-04-06 06:04:40 +08:00
										 |  |  | 		logger.LogIf(ctx, err) | 
					
						
							| 
									
										
										
										
											2018-03-16 04:03:41 +08:00
										 |  |  | 		return err | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-10-10 05:00:01 +08:00
										 |  |  | 	return saveConfig(ctx, objAPI, configFile, data) | 
					
						
							| 
									
										
										
										
											2018-03-16 04:03:41 +08:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // RemoveListener - removes HTTP client currently listening for events from listener.json.
 | 
					
						
							|  |  |  | func RemoveListener(objAPI ObjectLayer, bucketName string, targetID event.TargetID, addr xnet.Host) error { | 
					
						
							|  |  |  | 	// listener.json is available/applicable only in DistXL mode.
 | 
					
						
							|  |  |  | 	if !globalIsDistXL { | 
					
						
							|  |  |  | 		return nil | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-04-06 06:04:40 +08:00
										 |  |  | 	ctx := logger.SetReqInfo(context.Background(), &logger.ReqInfo{BucketName: bucketName}) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-03-16 04:03:41 +08:00
										 |  |  | 	// Construct path to listener.json for the given bucket.
 | 
					
						
							|  |  |  | 	configFile := path.Join(bucketConfigPrefix, bucketName, bucketListenerConfig) | 
					
						
							|  |  |  | 	transactionConfigFile := configFile + ".transaction" | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	// As object layer's GetObject() and PutObject() take respective lock on minioMetaBucket
 | 
					
						
							|  |  |  | 	// and configFile, take a transaction lock to avoid data race between readConfig()
 | 
					
						
							|  |  |  | 	// and saveConfig().
 | 
					
						
							|  |  |  | 	objLock := globalNSMutex.NewNSLock(minioMetaBucket, transactionConfigFile) | 
					
						
							|  |  |  | 	if err := objLock.GetLock(globalOperationTimeout); err != nil { | 
					
						
							|  |  |  | 		return err | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	defer objLock.Unlock() | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-10-10 05:00:01 +08:00
										 |  |  | 	configData, err := readConfig(ctx, objAPI, configFile) | 
					
						
							| 
									
										
										
										
											2018-04-25 06:53:30 +08:00
										 |  |  | 	if err != nil && !IsErrIgnored(err, errDiskNotFound, errConfigNotFound) { | 
					
						
							| 
									
										
										
										
											2018-03-16 04:03:41 +08:00
										 |  |  | 		return err | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	listenerList := []ListenBucketNotificationArgs{} | 
					
						
							| 
									
										
										
										
											2018-10-10 05:00:01 +08:00
										 |  |  | 	if configData != nil { | 
					
						
							|  |  |  | 		if err = json.Unmarshal(configData, &listenerList); err != nil { | 
					
						
							| 
									
										
										
										
											2018-04-06 06:04:40 +08:00
										 |  |  | 			logger.LogIf(ctx, err) | 
					
						
							|  |  |  | 			return err | 
					
						
							| 
									
										
										
										
											2018-03-16 04:03:41 +08:00
										 |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	if len(listenerList) == 0 { | 
					
						
							|  |  |  | 		// Nothing to remove.
 | 
					
						
							|  |  |  | 		return nil | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	activeListenerList := []ListenBucketNotificationArgs{} | 
					
						
							|  |  |  | 	for _, args := range listenerList { | 
					
						
							|  |  |  | 		if args.TargetID == targetID && args.Addr.Equal(addr) { | 
					
						
							|  |  |  | 			// Skip if matches
 | 
					
						
							|  |  |  | 			continue | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		activeListenerList = append(activeListenerList, args) | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	data, err := json.Marshal(activeListenerList) | 
					
						
							|  |  |  | 	if err != nil { | 
					
						
							| 
									
										
										
										
											2018-04-06 06:04:40 +08:00
										 |  |  | 		logger.LogIf(ctx, err) | 
					
						
							| 
									
										
										
										
											2018-03-16 04:03:41 +08:00
										 |  |  | 		return err | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-10-10 05:00:01 +08:00
										 |  |  | 	return saveConfig(ctx, objAPI, configFile, data) | 
					
						
							| 
									
										
										
										
											2018-03-16 04:03:41 +08:00
										 |  |  | } |