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 }