-
Notifications
You must be signed in to change notification settings - Fork 4
/
config.go
138 lines (126 loc) · 3.96 KB
/
config.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
package main
import (
"fmt"
"os"
"slices"
"sync"
"gopkg.in/yaml.v3"
)
// Location is a name plus geo coordinates.
type Location struct {
Name string
Latitude string
Longitude string
}
// InfluxConfig is the configuration for Influx/VictoriaMetrics.
type InfluxConfig struct {
Host string
AuthToken string `yaml:"auth_token"`
Org string
Bucket string
}
type ServerConfig struct {
Port int64
CertFile string `yaml:"cert_file"`
KeyFile string `yaml:"key_file"`
}
// Config is the configuration for ForecastMetrics.
type Config struct {
InfluxDB InfluxConfig `yaml:"influxdb"`
ForecastMeasurementName string `yaml:"forecast_measurement_name"`
AstronomyMeasurementName string `yaml:"astronomy_measurement_name"`
PrecipProbability float64 `yaml:"precip_probability"`
HttpCacheDir string `yaml:"http_cache_dir"`
OverwriteData bool `yaml:"overwrite_data"`
AzureSharedKey string `yaml:"azure_shared_key"`
ServerConfig ServerConfig `yaml:"server"`
AdHocCacheEntries int `yaml:"ad_hoc_cache_entries"`
Sources struct {
Enabled []string
VisualCrossing struct {
Key string
} `yaml:"visualcrossing"`
}
}
// ConfigService provides a way to update and get the latest list of locations that have regular
// forecasts exported to the database.
type ConfigService struct {
Config Config
locationsFile string
lock *sync.Mutex
locations []Location
}
// NewConfigService initializes a ConfigService by parsing the main config and the locations files.
// It panics if it can't read or parse the configs.
func NewConfigService(configFile, locationsFile string) *ConfigService {
// read config
cf, err := os.ReadFile(configFile)
if err != nil {
panic(fmt.Sprintf("Error reading config file %s: %s", configFile, err))
}
var config Config
err = yaml.Unmarshal(cf, &config)
if err != nil {
panic(fmt.Sprintf("Error loading config from %s: %s", configFile, err))
}
lf, err := os.ReadFile(locationsFile)
if err != nil {
panic(fmt.Sprintf("Error reading locations file %s: %s", locationsFile, err))
}
var locations []Location
err = yaml.Unmarshal(lf, &locations)
if err != nil {
panic(fmt.Sprintf("Error loading locations from %s: %s", locationsFile, err))
}
return &ConfigService{
Config: config,
locationsFile: locationsFile,
lock: &sync.Mutex{},
locations: locations,
}
}
// GetLocations returns a copy of all actively exported locations.
func (c *ConfigService) GetLocations() []Location {
c.lock.Lock()
defer c.lock.Unlock()
locsCopy := make([]Location, len(c.locations))
copy(locsCopy, c.locations)
return locsCopy
}
// AddLocation adds a new location to be regularly exported. It is saved to the config file.
func (c *ConfigService) AddLocation(location Location) {
c.lock.Lock()
defer c.lock.Unlock()
c.locations = append(c.locations, location)
c.marshall()
}
// RemoveLocation removes a location from being regularly exported, and removes it from the config file.
func (c *ConfigService) RemoveLocation(location Location) {
c.lock.Lock()
defer c.lock.Unlock()
locs := c.locations
idx := slices.Index(locs, location)
if idx > -1 {
c.locations = append(locs[:idx], locs[idx+1:]...)
c.marshall()
}
}
// marshall writes the current configuration to the config file.
// It should only be called while holding the lock.
func (c *ConfigService) marshall() {
f, err := os.OpenFile(c.locationsFile, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0644)
if err != nil {
panic(fmt.Sprintf("Error opening locations file %s: %s", c.locationsFile, err))
}
defer f.Close()
encoder := yaml.NewEncoder(f)
encoder.SetIndent(2)
err = encoder.Encode(c.locations)
if err != nil {
panic(fmt.Sprintf("Error saving locations to %s: %s", c.locationsFile, err))
}
err = encoder.Close()
if err != nil {
panic(fmt.Sprintf("Error saving locations to %s: %s", c.locationsFile, err))
}
}