@@ 7,6 7,8 @@ import (
"fmt"
"io/ioutil"
"os"
+ "sort"
+ "strings"
"git.sr.ht/~egtann/up"
)
@@ 16,7 18,7 @@ func main() {
srpOut, err := run()
fmt.Printf(srpOut)
if err != nil {
- fmt.Fprintln(os.Stderr, err)
+ fmt.Fprintln(os.Stderr, fmt.Sprintf("\nerror: %v", err))
os.Exit(1)
}
}
@@ 29,6 31,8 @@ func run() (string, error) {
)
flag.Parse()
+ // Parse our SRP config file, but retain the original bytes in case any
+ // error occurs
srpByt, err := ioutil.ReadFile(*srpFile)
if err != nil {
return "", fmt.Errorf("read srp config: %w", err)
@@ 44,26 48,40 @@ func run() (string, error) {
return origSRP, fmt.Errorf("parse inventory: %w", err)
}
- // Gee willikers, Batman, this is O(N^3)
+ // Convert our inventory into a data structure for fast lookups
+ serviceToIPs := map[string][]string{}
for ip, serviceNames := range inventory {
- middle:
for _, name := range serviceNames {
- for _, backend := range conf.Services {
- if backend.Service != name {
- continue
- }
- if ip == "" {
- continue
- }
- ipPort := fmt.Sprintf("%s:%d", ip, backend.Port)
- if backend.Port == 0 {
- ipPort = ip
- }
- backend.Backends = append(backend.Backends, ipPort)
- conf.Services[name] = backend
- break middle
+ serviceToIPs[name] = append(serviceToIPs[name], ip)
+ }
+ }
+
+ // Update ip:port combos for each backend
+ missingServices := serviceMap{}
+ for _, backend := range conf.Services {
+ service := backend.Service
+ if service == "" {
+ continue
+ }
+ ips, ok := serviceToIPs[service]
+ if !ok {
+ missingServices[service] = struct{}{}
+ continue
+ }
+ ips = append([]string{}, ips...)
+ if backend.Port > 0 {
+ for i, ip := range ips {
+ ips[i] = fmt.Sprintf("%s:%d", ip, backend.Port)
}
}
+ sort.Strings(ips)
+ backend.Backends = append([]string{}, ips...)
+ conf.Services[service] = backend
+ }
+ if len(missingServices) > 0 {
+ err := fmt.Errorf("services undefined in inventory: %v",
+ missingServices)
+ return origSRP, err
}
buf := &bytes.Buffer{}
@@ 72,13 90,6 @@ func run() (string, error) {
if err := enc.Encode(conf); err != nil {
return origSRP, fmt.Errorf("encode config: %w", err)
}
-
- // TODO(egtann) output the original config via stdout if any error
- // happens.
- //
- // TODO(egtann) on success, output the config formatted for human
- // editing (i.e. prettify)
-
return string(buf.Bytes()), nil
}
@@ 95,3 106,13 @@ func parseInventory(filename string) (up.Inventory, error) {
}
return inv, nil
}
+
+type serviceMap map[string]struct{}
+
+func (s serviceMap) String() string {
+ out := make([]string, 0, len(s))
+ for srv := range s {
+ out = append(out, srv)
+ }
+ return strings.Join(out, ", ")
+}