package host import ( "net" "strings" "github.com/carlosframework/platform/internal/edgesfile" ) // BoxAddresses are the addresses this box publishes in its edges file for // other edges to dial (region HA spec 2 §6). Public is for an edge in // another region. Private, with PrivateCIDR (the region network it lies // in), is for an edge in the same region. They are routing data only: an // edge dials them, and nothing grants anything on them. "" is an address // the source could not report, or one that failed validation, and nothing // is published for it. type BoxAddresses struct { Private string PrivateCIDR string Public string } // AddressSource reads this box's addresses afresh on every call. The edges // file publisher asks it before every publication and publishes only what // it reports, so an address the box no longer holds is never republished. // Every source is root-configured: the cloud's metadata service, or // host.env. None reads a file the agent can write. // // ok is false when the source could not tell: a metadata request that // failed, timed out or answered something other than "here it is" or "there // is none". Unknown is not blank. A caller that gets !ok writes nothing and // leaves what it published before standing; only an authoritative answer, // blank fields included, changes the published address. type AddressSource interface { BoxAddresses() (BoxAddresses, bool) } // envAddresses is the provider-neutral source (§25), from host.env: // CARLOS_PRIVATE_IP with CARLOS_PRIVATE_CIDR, and CARLOS_PUBLIC_IP. A field // whose setting is present is the answer for that field, and a value that // does not validate publishes nothing: an operator's setting is never // silently replaced. CARLOS_PRIVATE_IP without CARLOS_PRIVATE_CIDR is // invalid (census ruling region-ha-2a). A field left unset comes from // provider, the cloud's adapter, when there is one. type envAddresses struct { getenv func(string) string provider AddressSource } func (s envAddresses) BoxAddresses() (BoxAddresses, bool) { ip := strings.TrimSpace(s.getenv("CARLOS_PRIVATE_IP")) cidr := strings.TrimSpace(s.getenv("CARLOS_PRIVATE_CIDR")) public := strings.TrimSpace(s.getenv("CARLOS_PUBLIC_IP")) privateSet, publicSet := ip != "" || cidr != "", public != "" out := BoxAddresses{} if privateSet { out.Private, out.PrivateCIDR = edgesfile.PrivateAddress(ip, []string{cidr}) } if publicSet { out.Public = ipv4Only(public) } if (!privateSet || !publicSet) && s.provider != nil { p, ok := s.provider.BoxAddresses() if !ok { return BoxAddresses{}, false } if !privateSet { out.Private, out.PrivateCIDR = edgesfile.PrivateAddress(p.Private, []string{p.PrivateCIDR}) } if !publicSet { out.Public = ipv4Only(p.Public) } } return out, true } // ipv4Only is s when it is an IPv4 address, in its canonical form, and "" // otherwise. func ipv4Only(s string) string { ip := net.ParseIP(strings.TrimSpace(s)) if ip == nil || ip.To4() == nil { return "" } return ip.To4().String() }