1
1
package cmd
2
2
3
3
import (
4
+ "context"
4
5
"fmt"
5
6
6
7
"github.com/go-spatial/cobra"
7
8
"github.com/go-spatial/tegola/atlas"
8
9
"github.com/go-spatial/tegola/cmd/internal/register"
9
10
cachecmd "github.com/go-spatial/tegola/cmd/tegola/cmd/cache"
10
11
"github.com/go-spatial/tegola/config"
12
+ "github.com/go-spatial/tegola/config/source"
11
13
"github.com/go-spatial/tegola/dict"
12
14
"github.com/go-spatial/tegola/internal/build"
15
+ "github.com/go-spatial/tegola/internal/env"
13
16
"github.com/go-spatial/tegola/internal/log"
17
+ "github.com/go-spatial/tegola/provider"
14
18
)
15
19
16
20
var (
@@ -114,22 +118,22 @@ func initConfig(configFile string, cacheRequired bool, logLevel string, logger s
114
118
return err
115
119
}
116
120
117
- // init our providers
118
- // but first convert []env.Map -> []dict.Dicter
119
- provArr := make ([]dict.Dicter , len (conf .Providers ))
120
- for i := range provArr {
121
- provArr [i ] = conf .Providers [i ]
121
+ // Init providers from the primary config file.
122
+ providers , err := initProviders (conf .Providers , conf .Maps )
123
+ if err != nil {
124
+ return err
122
125
}
123
126
124
- providers , err := register . Providers ( provArr , conf . Maps )
125
- if err != nil {
126
- return fmt . Errorf ( "could not register providers: %v" , err )
127
+ // Init maps from the primary config file.
128
+ if err = initMaps ( conf . Maps , providers ); err != nil {
129
+ return err
127
130
}
128
131
129
- // init our maps
130
- if err = register . Maps ( nil , conf . Maps , providers ); err != nil {
131
- return fmt . Errorf ( "could not register maps: %v" , err )
132
+ // Setup the app config source.
133
+ if err = initAppConfigSource ( conf ); err != nil {
134
+ return err
132
135
}
136
+
133
137
if len (conf .Cache ) == 0 && cacheRequired {
134
138
return fmt .Errorf ("no cache defined in config, please check your config (%v)" , configFile )
135
139
}
@@ -152,3 +156,123 @@ func initConfig(configFile string, cacheRequired bool, logLevel string, logger s
152
156
atlas .SetObservability (observer )
153
157
return nil
154
158
}
159
+
160
+ // initProviders translate provider config from a TOML file into usable Provider objects.
161
+ func initProviders (providersConfig []env.Dict , maps []provider.Map ) (map [string ]provider.TilerUnion , error ) {
162
+ // first convert []env.Map -> []dict.Dicter
163
+ provArr := make ([]dict.Dicter , len (providersConfig ))
164
+ for i := range provArr {
165
+ provArr [i ] = providersConfig [i ]
166
+ }
167
+
168
+ providers , err := register .Providers (provArr , conf .Maps )
169
+ if err != nil {
170
+ return nil , fmt .Errorf ("could not register providers: %v" , err )
171
+ }
172
+
173
+ return providers , nil
174
+ }
175
+
176
+ // initMaps registers maps with Atlas to be ready for service.
177
+ func initMaps (maps []provider.Map , providers map [string ]provider.TilerUnion ) error {
178
+ if err := register .Maps (nil , maps , providers ); err != nil {
179
+ return fmt .Errorf ("could not register maps: %v" , err )
180
+ }
181
+
182
+ return nil
183
+ }
184
+
185
+ // initAppConfigSource sets up an additional configuration source for "apps" (groups of providers and maps) to be loaded and unloaded on-the-fly.
186
+ func initAppConfigSource (conf config.Config ) error {
187
+ // Get the config source type. If none, return.
188
+ val , err := conf .AppConfigSource .String ("type" , nil )
189
+ if err != nil || val == "" {
190
+ return nil
191
+ }
192
+
193
+ // Initialize the source.
194
+ ctx := context .Background () // Not doing anything with context now, but could use it for stopping this goroutine.
195
+ src , err := source .InitSource (val , conf .AppConfigSource , conf .BaseDir )
196
+ if err != nil {
197
+ return err
198
+ }
199
+
200
+ // Load and start watching for new apps.
201
+ watcher , err := src .LoadAndWatch (ctx )
202
+ if err != nil {
203
+ return err
204
+ }
205
+
206
+ go func () {
207
+ // Keep a record of what we've loaded so that we can unload when needed.
208
+ apps := make (map [string ]source.App )
209
+
210
+ for {
211
+ select {
212
+ case app , ok := <- watcher .Updates :
213
+ if ! ok {
214
+ return
215
+ }
216
+
217
+ // Check for validity first.
218
+ if err := config .ValidateApp (& app ); err != nil {
219
+ log .Errorf ("Failed validating app %s. %s" , app .Key , err )
220
+ }
221
+
222
+ // If the new app is named the same as an existing app, first unload the existing one.
223
+ if old , exists := apps [app .Key ]; exists {
224
+ log .Infof ("Unloading app %s..." , old .Key )
225
+ // We need only unload maps, since the providers don't live outside of maps.
226
+ register .UnloadMaps (nil , getMapNames (old ))
227
+ delete (apps , app .Key )
228
+ }
229
+
230
+ log .Infof ("Loading app %s..." , app .Key )
231
+
232
+ // Init new providers
233
+ providers , err := initProviders (app .Providers , app .Maps )
234
+ if err != nil {
235
+ log .Errorf ("Failed initializing providers from %s: %s" , app .Key , err )
236
+ continue
237
+ }
238
+
239
+ // Init new maps
240
+ if err = initMaps (app .Maps , providers ); err != nil {
241
+ log .Errorf ("Failed initializing maps from %s: %s" , app .Key , err )
242
+ continue
243
+ }
244
+
245
+ // Record that we've loaded this app.
246
+ apps [app .Key ] = app
247
+
248
+ case deleted , ok := <- watcher .Deletions :
249
+ if ! ok {
250
+ return
251
+ }
252
+
253
+ // Unload an app's maps if it was previously loaded.
254
+ if app , exists := apps [deleted ]; exists {
255
+ log .Infof ("Unloading app %s..." , app .Key )
256
+ register .UnloadMaps (nil , getMapNames (app ))
257
+ delete (apps , app .Key )
258
+ } else {
259
+ log .Infof ("Received an unload event for app %s, but couldn't find it." , deleted )
260
+ }
261
+
262
+ case <- ctx .Done ():
263
+ return
264
+ }
265
+ }
266
+ }()
267
+
268
+ return nil
269
+ }
270
+
271
+ func getMapNames (app source.App ) []string {
272
+ names := make ([]string , 0 , len (app .Maps ))
273
+ for _ , m := range app .Maps {
274
+ names = append (names , string (m .Name ))
275
+ }
276
+
277
+ return names
278
+ }
0 commit comments