spaceapid/config/config.go
Bennett Wetters a53a5c3517 feat: SpaceAPI v15 support
Delete types/v14.go, replace with v15.go.

Rename internal types to not include message version.
I don't currently see the need to support multiple message
versions that have incompatible layouts.
v15 is backwards compatible with v14, so the config example now
specifies both.

Rename the "response" field in the config file to "static" as
dynamic/static makes more sense from the user's perspective.
(Internally it's still SpaceapidConfig.{Dynamic,Response}
to align with type names and usage.)
2026-07-25 23:04:20 +02:00

104 lines
2.5 KiB
Go

package config
import (
"bytes"
"encoding/json"
"flag"
"fmt"
"log"
"os"
"slices"
"strings"
"git.hamburg.ccc.de/ccchh/spaceapid/types"
)
const (
envConfigPath = "SPACEAPID_CONFIG_PATH"
)
type pathsT []string
func (p *pathsT) String() string {
return fmt.Sprint(*p)
}
func (p *pathsT) Set(s string) error {
*p = make(pathsT, 0)
for _, path := range strings.Split(s, ",") {
*p = append(*p, path)
}
return nil
}
var (
flagConfigPaths pathsT
)
func init() {
flag.Var(&flagConfigPaths, "c", "Comma-separated list of config file paths. Parsed in order of appearance. Values get overwritten by files parsed later.")
}
func getConfigPaths() (paths pathsT) {
// Get paths from env variable and if env var present override cli flags
configPath, ok := os.LookupEnv(envConfigPath)
if ok {
if configPath == "" {
log.Fatalln("Env variable", envConfigPath, "is present but empty.")
}
_ = flagConfigPaths.Set(configPath)
}
for _, path := range flagConfigPaths {
_, err := os.Stat(path)
if err != nil {
log.Fatalln("Config file", path, "doesn't exist or is not accessible, error:", err)
}
paths = append(paths, path)
}
// If paths is still empty we are missing something
if len(paths) == 0 {
log.Fatalln("Error: Neither env:", envConfigPath, "nor cli flag was specified, unable to start without config.")
}
return
}
// ParseConfiguration returns the config from file
func ParseConfiguration() (conf SpaceapidConfig) {
log.Println("Parsing configuration files")
paths := getConfigPaths()
log.Println("Config files:", paths)
for _, path := range paths {
// Read file
file, err := os.ReadFile(path)
if err != nil {
log.Fatalln("Failed reading file:", err)
}
// Parse JSON
dec := json.NewDecoder(bytes.NewReader(file))
dec.DisallowUnknownFields()
err = dec.Decode(&conf)
if err != nil {
log.Fatalln("Could not parse spaceapid config file:", err)
}
}
// Sanity check: Config should declare compatibility with current SpaceAPI message version
if !slices.Contains(conf.Response.APICompatibility, types.CurrentMessageVersion) {
log.Fatalln("Provided config doesn't specify compatibility with current API version", types.CurrentMessageVersion)
}
// Initialize fields for environment sensors
conf.Response.Sensors = make(map[string][]types.EnvironmentSensor)
for key, sensorConfigs := range conf.Dynamic.Sensors {
conf.Response.Sensors[key] = make([]types.EnvironmentSensor, len(sensorConfigs))
for i, sensorConfig := range sensorConfigs {
conf.Response.Sensors[key][i] = sensorConfig.SensorData
}
}
return
}