github.com/minio/mc@v0.0.0-20240503112107-b471de8d1882/cmd/watch.go (about)

     1  // Copyright (c) 2015-2022 MinIO, Inc.
     2  //
     3  // This file is part of MinIO Object Storage stack
     4  //
     5  // This program is free software: you can redistribute it and/or modify
     6  // it under the terms of the GNU Affero General Public License as published by
     7  // the Free Software Foundation, either version 3 of the License, or
     8  // (at your option) any later version.
     9  //
    10  // This program is distributed in the hope that it will be useful
    11  // but WITHOUT ANY WARRANTY; without even the implied warranty of
    12  // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    13  // GNU Affero General Public License for more details.
    14  //
    15  // You should have received a copy of the GNU Affero General Public License
    16  // along with this program.  If not, see <http://www.gnu.org/licenses/>.
    17  
    18  package cmd
    19  
    20  import (
    21  	"context"
    22  	"sync"
    23  	"time"
    24  
    25  	"github.com/minio/mc/pkg/probe"
    26  	"github.com/minio/minio-go/v7/pkg/notification"
    27  )
    28  
    29  // EventInfo contains the information of the event that occurred and the source
    30  // IP:PORT of the client which triggerred the event.
    31  type EventInfo struct {
    32  	Time         string
    33  	Size         int64
    34  	UserMetadata map[string]string
    35  	Path         string
    36  	Host         string
    37  	Port         string
    38  	UserAgent    string
    39  	Type         notification.EventType
    40  }
    41  
    42  // WatchOptions contains watch configuration options
    43  type WatchOptions struct {
    44  	Prefix    string
    45  	Suffix    string
    46  	Events    []string
    47  	Recursive bool
    48  }
    49  
    50  // WatchObject captures watch channels to read and listen on.
    51  type WatchObject struct {
    52  	// eventInfo will be put on this chan
    53  	EventInfoChan chan []EventInfo
    54  	// errors will be put on this chan
    55  	ErrorChan chan *probe.Error
    56  	// will stop the watcher goroutines
    57  	DoneChan chan struct{}
    58  }
    59  
    60  // Events returns the chan receiving events
    61  func (w *WatchObject) Events() chan []EventInfo {
    62  	return w.EventInfoChan
    63  }
    64  
    65  // Errors returns the chan receiving errors
    66  func (w *WatchObject) Errors() chan *probe.Error {
    67  	return w.ErrorChan
    68  }
    69  
    70  // Watcher can be used to have one or multiple clients watch for notifications
    71  type Watcher struct {
    72  	sessionStartTime time.Time
    73  
    74  	// all error will be added to this chan
    75  	ErrorChan chan *probe.Error
    76  	// all events will be added to this chan
    77  	EventInfoChan chan []EventInfo
    78  
    79  	// array of watchers joined
    80  	o []*WatchObject
    81  
    82  	// all watchers joining will enter this waitgroup
    83  	wg sync.WaitGroup
    84  }
    85  
    86  // NewWatcher creates a new watcher
    87  func NewWatcher(sessionStartTime time.Time) *Watcher {
    88  	return &Watcher{
    89  		sessionStartTime: sessionStartTime,
    90  		ErrorChan:        make(chan *probe.Error),
    91  		EventInfoChan:    make(chan []EventInfo),
    92  		o:                []*WatchObject{},
    93  	}
    94  }
    95  
    96  // Errors returns a channel which will receive errors
    97  func (w *Watcher) Errors() chan *probe.Error {
    98  	return w.ErrorChan
    99  }
   100  
   101  // Events returns a channel which will receive events
   102  func (w *Watcher) Events() chan []EventInfo {
   103  	return w.EventInfoChan
   104  }
   105  
   106  // Stop all watchers
   107  func (w *Watcher) Stop() {
   108  	for _, w := range w.o {
   109  		close(w.DoneChan)
   110  	}
   111  	w.wg.Wait()
   112  }
   113  
   114  // Join the watcher with client
   115  func (w *Watcher) Join(ctx context.Context, client Client, recursive bool) *probe.Error {
   116  	wo, err := client.Watch(ctx, WatchOptions{
   117  		Recursive: recursive,
   118  		Events:    []string{"put", "delete", "bucket-creation", "bucket-removal"},
   119  	})
   120  	if err != nil {
   121  		return err
   122  	}
   123  
   124  	w.o = append(w.o, wo)
   125  
   126  	// join monitoring waitgroup
   127  	w.wg.Add(1)
   128  
   129  	// wait for events and errors of individual client watchers
   130  	// and sent then to eventsChan and errorsChan
   131  	go func() {
   132  		defer w.wg.Done()
   133  
   134  		for {
   135  			select {
   136  			case <-wo.DoneChan:
   137  				return
   138  			case events, ok := <-wo.Events():
   139  				if !ok {
   140  					return
   141  				}
   142  				w.EventInfoChan <- events
   143  			case err, ok := <-wo.Errors():
   144  				if !ok {
   145  					return
   146  				}
   147  
   148  				w.ErrorChan <- err
   149  			}
   150  		}
   151  	}()
   152  
   153  	return nil
   154  }