207 lines
4.0 KiB
Go
207 lines
4.0 KiB
Go
|
package main
|
||
|
|
||
|
import (
|
||
|
lru "github.com/hashicorp/golang-lru"
|
||
|
"github.com/oschwald/maxminddb-golang"
|
||
|
"github.com/prometheus/client_golang/prometheus"
|
||
|
"github.com/prometheus/client_golang/prometheus/promauto"
|
||
|
log "github.com/sirupsen/logrus"
|
||
|
"github.com/spf13/viper"
|
||
|
"net"
|
||
|
"net/url"
|
||
|
"strings"
|
||
|
"sync"
|
||
|
)
|
||
|
|
||
|
func reloadConfig() {
|
||
|
err := viper.ReadInConfig() // Find and read the config file
|
||
|
|
||
|
if err != nil { // Handle errors reading the config file
|
||
|
log.WithError(err).Fatalln("Unable to load config file")
|
||
|
}
|
||
|
|
||
|
// db will never be reloaded.
|
||
|
if db == nil {
|
||
|
// Load maxmind database
|
||
|
db, err = maxminddb.Open(viper.GetString("geodb"))
|
||
|
|
||
|
if err != nil {
|
||
|
log.WithError(err).Fatalln("Unable to open database")
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// Refresh server cache if size changed
|
||
|
if serverCache == nil {
|
||
|
serverCache, err = lru.New(viper.GetInt("cacheSize"))
|
||
|
} else {
|
||
|
serverCache.Resize(viper.GetInt("cacheSize"))
|
||
|
}
|
||
|
|
||
|
// Set top choice count
|
||
|
topChoices = viper.GetInt("topChoices")
|
||
|
|
||
|
// Reload map file
|
||
|
reloadMap()
|
||
|
|
||
|
// Reload server list
|
||
|
reloadServers()
|
||
|
|
||
|
// Check top choices size
|
||
|
if topChoices > len(servers) {
|
||
|
topChoices = len(servers)
|
||
|
}
|
||
|
|
||
|
// Force check
|
||
|
go servers.Check()
|
||
|
}
|
||
|
|
||
|
func reloadServers() {
|
||
|
var serverList []ServerConfig
|
||
|
viper.UnmarshalKey("servers", &serverList)
|
||
|
|
||
|
var wg sync.WaitGroup
|
||
|
|
||
|
existing := make(map[string]int)
|
||
|
|
||
|
for i, server := range servers {
|
||
|
existing[server.Host] = i
|
||
|
}
|
||
|
|
||
|
hosts := make(map[string]bool)
|
||
|
|
||
|
for _, server := range serverList {
|
||
|
wg.Add(1)
|
||
|
|
||
|
var prefix string
|
||
|
|
||
|
if !strings.HasPrefix(server.Server, "http") {
|
||
|
prefix = "https://"
|
||
|
}
|
||
|
|
||
|
u, err := url.Parse(prefix + server.Server)
|
||
|
|
||
|
if err != nil {
|
||
|
log.WithFields(log.Fields{
|
||
|
"error": err,
|
||
|
"server": server,
|
||
|
}).Warning("Server is invalid")
|
||
|
return
|
||
|
}
|
||
|
|
||
|
hosts[u.Host] = true
|
||
|
|
||
|
i := -1
|
||
|
|
||
|
if v, exists := existing[u.Host]; exists {
|
||
|
i = v
|
||
|
}
|
||
|
|
||
|
go func(i int, server ServerConfig, u *url.URL) {
|
||
|
defer wg.Done()
|
||
|
|
||
|
s := addServer(server, u)
|
||
|
|
||
|
if _, ok := existing[u.Host]; ok {
|
||
|
s.Redirects = servers[i].Redirects
|
||
|
|
||
|
servers[i] = s
|
||
|
} else {
|
||
|
s.Redirects = promauto.NewCounter(prometheus.CounterOpts{
|
||
|
Name: "armbian_router_redirects_" + metricReplacer.Replace(u.Host),
|
||
|
Help: "The number of redirects for server " + u.Host,
|
||
|
})
|
||
|
|
||
|
servers = append(servers, s)
|
||
|
|
||
|
log.WithFields(log.Fields{
|
||
|
"server": u.Host,
|
||
|
"path": u.Path,
|
||
|
"latitude": s.Latitude,
|
||
|
"longitude": s.Longitude,
|
||
|
}).Info("Added server")
|
||
|
}
|
||
|
}(i, server, u)
|
||
|
}
|
||
|
|
||
|
wg.Wait()
|
||
|
|
||
|
// Remove servers that no longer exist in the config
|
||
|
for i := len(servers) - 1; i >= 0; i-- {
|
||
|
if _, exists := hosts[servers[i].Host]; exists {
|
||
|
continue
|
||
|
}
|
||
|
|
||
|
log.WithFields(log.Fields{
|
||
|
"server": servers[i].Host,
|
||
|
}).Info("Removed server")
|
||
|
|
||
|
servers = append(servers[:i], servers[i+1:]...)
|
||
|
}
|
||
|
}
|
||
|
|
||
|
var metricReplacer = strings.NewReplacer(".", "_", "-", "_")
|
||
|
|
||
|
// addServer takes ServerConfig and constructs a server.
|
||
|
// This will create duplicate servers, but it will overwrite existing ones when changed.
|
||
|
func addServer(server ServerConfig, u *url.URL) *Server {
|
||
|
s := &Server{
|
||
|
Available: true,
|
||
|
Host: u.Host,
|
||
|
Path: u.Path,
|
||
|
Latitude: server.Latitude,
|
||
|
Longitude: server.Longitude,
|
||
|
Weight: server.Weight,
|
||
|
}
|
||
|
|
||
|
if s.Weight == 0 {
|
||
|
s.Weight = 1
|
||
|
}
|
||
|
|
||
|
if s.Latitude == 0 && s.Longitude == 0 {
|
||
|
ips, err := net.LookupIP(u.Host)
|
||
|
|
||
|
if err != nil {
|
||
|
log.WithFields(log.Fields{
|
||
|
"error": err,
|
||
|
"server": s.Host,
|
||
|
}).Warning("Could not resolve address")
|
||
|
return nil
|
||
|
}
|
||
|
|
||
|
var city City
|
||
|
err = db.Lookup(ips[0], &city)
|
||
|
|
||
|
if err != nil {
|
||
|
log.WithFields(log.Fields{
|
||
|
"error": err,
|
||
|
"server": s.Host,
|
||
|
"ip": ips[0],
|
||
|
}).Warning("Could not geolocate address")
|
||
|
return nil
|
||
|
}
|
||
|
|
||
|
s.Latitude = city.Location.Latitude
|
||
|
s.Longitude = city.Location.Longitude
|
||
|
}
|
||
|
|
||
|
return s
|
||
|
}
|
||
|
|
||
|
func reloadMap() {
|
||
|
mapFile := viper.GetString("dl_map")
|
||
|
|
||
|
if mapFile == "" {
|
||
|
return
|
||
|
}
|
||
|
|
||
|
log.WithField("file", mapFile).Info("Loading download map")
|
||
|
|
||
|
newMap, err := loadMap(mapFile)
|
||
|
|
||
|
if err != nil {
|
||
|
return
|
||
|
}
|
||
|
|
||
|
dlMap = newMap
|
||
|
}
|