github.com/ddev/ddev@v1.23.2-0.20240519125000-d824ffe36ff3/pkg/config/remoteconfig/remote_config.go (about)

     1  package remoteconfig
     2  
     3  import (
     4  	"sync"
     5  	"time"
     6  
     7  	"github.com/ddev/ddev/pkg/config/remoteconfig/internal"
     8  	"github.com/ddev/ddev/pkg/config/remoteconfig/storage"
     9  	"github.com/ddev/ddev/pkg/config/remoteconfig/types"
    10  	statetypes "github.com/ddev/ddev/pkg/config/state/types"
    11  	"github.com/ddev/ddev/pkg/util"
    12  )
    13  
    14  // New creates and returns a new RemoteConfig.
    15  func New(config *Config, stateManager statetypes.State, isInternetActive func() bool) types.RemoteConfig {
    16  	// defer util.TimeTrack()()
    17  
    18  	// Create RemoteConfig.
    19  	cfg := &remoteConfig{
    20  		state:            newState(stateManager),
    21  		fileStorage:      storage.NewFileStorage(config.getLocalSourceFileName()),
    22  		updateInterval:   config.UpdateInterval,
    23  		tickerInterval:   config.TickerInterval,
    24  		isInternetActive: isInternetActive,
    25  	}
    26  
    27  	// Load local remote config.
    28  	cfg.loadFromLocalStorage()
    29  
    30  	// Configure remote and initiate update.
    31  	cfg.githubStorage = storage.NewGithubStorage(
    32  		config.getRemoteSourceOwner(&cfg.remoteConfig),
    33  		config.getRemoteSourceRepo(&cfg.remoteConfig),
    34  		config.getRemoteSourceFilepath(&cfg.remoteConfig),
    35  		storage.Options{Ref: config.getRemoteSourceRef(&cfg.remoteConfig)},
    36  	)
    37  	cfg.updateFromGithub()
    38  
    39  	return cfg
    40  }
    41  
    42  const (
    43  	localFileName = ".remote-config"
    44  	// Default intervals in hours
    45  	updateInterval        = 10
    46  	notificationsInterval = 20
    47  	tickerInterval        = 20
    48  )
    49  
    50  // remoteConfig is a in memory representation of the DDEV RemoteConfig.
    51  type remoteConfig struct {
    52  	state        *state
    53  	remoteConfig internal.RemoteConfig
    54  
    55  	fileStorage   types.RemoteConfigStorage
    56  	githubStorage types.RemoteConfigStorage
    57  
    58  	updateInterval   int
    59  	tickerInterval   int
    60  	isInternetActive func() bool
    61  
    62  	mu sync.Mutex
    63  }
    64  
    65  // write saves the remote config to the local storage.
    66  func (c *remoteConfig) write() {
    67  	// defer util.TimeTrack()()
    68  
    69  	err := c.fileStorage.Write(c.remoteConfig)
    70  
    71  	if err != nil {
    72  		util.Debug("Error while writing remote config: %s", err)
    73  	}
    74  }
    75  
    76  // loadFromLocalStorage loads the remote config from the local storage and
    77  // initiates an update from the remote asynchronously.
    78  func (c *remoteConfig) loadFromLocalStorage() {
    79  	// defer util.TimeTrack()()
    80  
    81  	c.mu.Lock()
    82  	defer c.mu.Unlock()
    83  
    84  	var err error
    85  
    86  	c.remoteConfig, err = c.fileStorage.Read()
    87  
    88  	if err != nil {
    89  		util.Debug("Error while loading remote config: %s", err)
    90  	}
    91  }
    92  
    93  // updateFromGithub downloads the remote config from GitHub.
    94  func (c *remoteConfig) updateFromGithub() {
    95  	// defer util.TimeTrack()()
    96  
    97  	if !c.isInternetActive() {
    98  		util.Debug("No internet connection.")
    99  
   100  		return
   101  	}
   102  
   103  	// Check if an update is needed.
   104  	if c.state.UpdatedAt.Add(c.getUpdateInterval()).Before(time.Now()) {
   105  		util.Debug("Downloading remote config.")
   106  
   107  		c.mu.Lock()
   108  		defer c.mu.Unlock()
   109  
   110  		var err error
   111  
   112  		// Download the remote config.
   113  		c.remoteConfig, err = c.githubStorage.Read()
   114  
   115  		if err != nil {
   116  			util.Debug("Error while downloading remote config: %s", err)
   117  
   118  			return
   119  		}
   120  
   121  		c.write()
   122  
   123  		// Update state.
   124  		c.state.UpdatedAt = time.Now()
   125  		if err = c.state.save(); err != nil {
   126  			util.Debug("Error while saving state: %s", err)
   127  		}
   128  	}
   129  }
   130  
   131  // getUpdateInterval returns the update interval for the remote config. The
   132  // processing order is defined as follows, the first defined value is returned:
   133  //   - global config
   134  //   - remote config
   135  //   - const updateInterval
   136  func (c *remoteConfig) getUpdateInterval() time.Duration {
   137  	if c.updateInterval > 0 {
   138  		return time.Duration(c.updateInterval) * time.Hour
   139  	}
   140  
   141  	if c.remoteConfig.UpdateInterval > 0 {
   142  		return time.Duration(c.remoteConfig.UpdateInterval) * time.Hour
   143  	}
   144  
   145  	return time.Duration(updateInterval) * time.Hour
   146  }