github.com/GoogleContainerTools/skaffold@v1.39.18/pkg/skaffold/filemon/monitor.go (about)

     1  /*
     2  Copyright 2019 The Skaffold Authors
     3  
     4  Licensed under the Apache License, Version 2.0 (the "License");
     5  you may not use this file except in compliance with the License.
     6  You may obtain a copy of the License at
     7  
     8      http://www.apache.org/licenses/LICENSE-2.0
     9  
    10  Unless required by applicable law or agreed to in writing, software
    11  distributed under the License is distributed on an "AS IS" BASIS,
    12  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    13  See the License for the specific language governing permissions and
    14  limitations under the License.
    15  */
    16  
    17  package filemon
    18  
    19  // Monitor monitors files changes for multiples components.
    20  type Monitor interface {
    21  	Register(deps func() ([]string, error), onChange func(Events)) error
    22  	Run(debounce bool) error
    23  	Reset()
    24  }
    25  
    26  type watchList struct {
    27  	changedComponents map[int]bool
    28  	components        []*component
    29  }
    30  
    31  // NewMonitor creates a new Monitor.
    32  func NewMonitor() Monitor {
    33  	return &watchList{
    34  		changedComponents: map[int]bool{},
    35  	}
    36  }
    37  
    38  type component struct {
    39  	deps     func() ([]string, error)
    40  	onChange func(Events)
    41  	state    FileMap
    42  	events   Events
    43  }
    44  
    45  // Register adds a new component to the watch list.
    46  func (w *watchList) Register(deps func() ([]string, error), onChange func(Events)) error {
    47  	state, err := Stat(deps)
    48  	if err != nil {
    49  		return err
    50  	}
    51  
    52  	w.components = append(w.components, &component{
    53  		deps:     deps,
    54  		onChange: onChange,
    55  		state:    state,
    56  	})
    57  	return nil
    58  }
    59  
    60  func (w *watchList) Reset() {
    61  	w.changedComponents = map[int]bool{}
    62  }
    63  
    64  // Run watches files until the context is cancelled or an error occurs.
    65  func (w *watchList) Run(debounce bool) error {
    66  	changed := 0
    67  	for i, component := range w.components {
    68  		state, err := Stat(component.deps)
    69  		if err != nil {
    70  			return err
    71  		}
    72  		e := events(component.state, state)
    73  
    74  		if e.HasChanged() {
    75  			w.changedComponents[i] = true
    76  			component.state = state
    77  			component.events = e
    78  			changed++
    79  		}
    80  	}
    81  
    82  	// Rapid file changes that are more frequent than the poll interval would trigger
    83  	// multiple rebuilds.
    84  	// To prevent that, we debounce changes that happen too quickly
    85  	// by waiting for a full turn where nothing happens and trigger a rebuild for
    86  	// the accumulated changes.
    87  	if (!debounce && changed > 0) || (debounce && changed == 0 && len(w.changedComponents) > 0) {
    88  		for i, component := range w.components {
    89  			if w.changedComponents[i] {
    90  				component.onChange(component.events)
    91  			}
    92  		}
    93  	}
    94  	return nil
    95  }