package config import ( "encoding/json" "fmt" "net/netip" "os" ) type Peer struct { ID string `json:"id"` Address string `json:"address"` Secret string `json:"secret"` } type Bundle struct { WAN string `json:"wan"` Peers []Peer `json:"peers"` } type Runtime struct { Bundle Path Path `json:"path"` } type Cloud struct { Tenancy string `json:"tenancy"` User string `json:"user"` Fingerprint string `json:"fingerprint"` Region string `json:"region"` Compartment string `json:"compartment"` DRG string `json:"drg"` Address string `json:"address"` Network string `json:"network"` } type Path struct { Address string `json:"address"` Network string `json:"network"` Gateway string `json:"gateway"` Ports []uint16 `json:"ports"` } func Read[T any](path string) (T, error) { var value T data, err := os.ReadFile(path) if err != nil { return value, err } err = json.Unmarshal(data, &value) return value, err } func (b Bundle) Validate() error { if err := IPv4(b.WAN); err != nil { return fmt.Errorf("WAN: %w", err) } if len(b.Peers) != 2 { return fmt.Errorf("expected two OCI headends") } for _, p := range b.Peers { if p.ID == "" || p.Secret == "" { return fmt.Errorf("incomplete headend") } if err := IPv4(p.Address); err != nil { return fmt.Errorf("headend: %w", err) } } if b.Peers[0].ID == b.Peers[1].ID || b.Peers[0].Address == b.Peers[1].Address { return fmt.Errorf("duplicate headend") } return nil } func (p Path) Validate() error { if err := IPv4(p.Address); err != nil { return err } if err := IPv4(p.Gateway); err != nil { return fmt.Errorf("Gateway: %w", err) } n, err := netip.ParsePrefix(p.Network) if err != nil || !n.Addr().Is4() { return fmt.Errorf("invalid OCI network") } if len(p.Ports) == 0 { return fmt.Errorf("empty service allowlist") } for _, port := range p.Ports { if port == 0 { return fmt.Errorf("invalid service port") } } return nil } func IPv4(value string) error { ip, err := netip.ParseAddr(value) if err != nil || !ip.Is4() || !ip.IsGlobalUnicast() { return fmt.Errorf("invalid unicast IPv4") } return nil }