diff --git a/cmd/hepto/root.go b/cmd/hepto/root.go index 54a4ed286277369d1324778a5274607c8b3f1982..b16cb83eac6846bd250ed5abe93bbafb5dc47cc6 100644 --- a/cmd/hepto/root.go +++ b/cmd/hepto/root.go @@ -21,56 +21,69 @@ var rootCmd = &cobra.Command{ Run: func(cmd *cobra.Command, args []string) { logrus.SetLevel(logrus.DebugLevel) if config.Contained { - currentIP := WaitForIP() - logrus.Debug("Current IP is: ", currentIP.String()) - c := cluster.New( - config.ClusterName, config.NodeName, - currentIP, config.DiscoveryPort, - config.Anchors.IPSlice(), - config.Key, - ) - err := c.Run() - if err != nil { - logrus.Fatal(err) - } + run() } else { - c, err := selfcontain.New( - config.NodeName, - config.LibcontainerPath(), - config.RootPath(), - append(os.Args[1:], "--contained"), - ) - if err != nil { - logrus.Fatal(err) - } - dns := config.DNS.IPSlice() - // Use default DNS if required - if len(dns) == 0 { - dns = defaultDNS - } - err = c.SetupNetworking( - config.Master, - *config.IP.IPNet(), config.Gw.IP, dns, - config.RootPath(), - ) - if err != nil { - logrus.Fatal(err) - } - s := make(chan os.Signal, 1) - signal.Notify(s, os.Interrupt) - go func() { - <-s - c.Destroy() - }() - err = c.Run() - if err != nil { - logrus.Fatal(err) - } + containerize() } }, } -func WaitForIP() net.IP { +// Actually run hepto +func run() { + currentIP := waitForIP() + logrus.Debug("Current IP is: ", currentIP.String()) + c := cluster.New( + config.ClusterName, config.NodeName, + currentIP, config.DiscoveryPort, + config.Anchors.IPSlice(), + config.Key, + ) + err := c.Run() + if err != nil { + logrus.Fatal(err) + } +} + +// Run ourselves inside a container +func containerize() { + c, err := selfcontain.New( + config.NodeName, + config.LibcontainerPath(), + config.RootPath(), + append(os.Args[1:], "--contained"), + ) + if err != nil { + logrus.Fatal(err) + } + dns := config.DNS.IPSlice() + // Use default DNS if required + if len(dns) == 0 { + dns = defaultDNS + } + err = c.SetupNetworking( + config.Master, + *config.IP.IPNet(), config.Gw.IP, dns, + config.RootPath(), + ) + if err != nil { + logrus.Fatal(err) + } + // Make sure we are notified and that we destroy the + // container upon being interrupted + s := make(chan os.Signal, 1) + signal.Notify(s, os.Interrupt) + go func() { + <-s + c.Destroy() + }() + err = c.Run() + if err != nil { + logrus.Fatal(err) + } +} + +// Guess the current IP address +func waitForIP() net.IP { for { logrus.Debug("Determining current IP address") conn, err := net.Dial("udp", fmt.Sprintf("[%s]:53", defaultDNS[0].String())) diff --git a/pkg/cluster/cluster.go b/pkg/cluster/cluster.go index 810552cfdc7280512ff7b4b1943e774f698d6448..f985c4f2b75265a76226f85f87b2d635e6176449 100644 --- a/pkg/cluster/cluster.go +++ b/pkg/cluster/cluster.go @@ -1,3 +1,5 @@ +// Hepto cluster implements the main clustering logics, based on +// sml (Simple Memberlist) for cluster bootstraping and node discovery package cluster import ( diff --git a/pkg/cluster/meta.go b/pkg/cluster/meta.go index 3bd9f37795875d77dafec6a1424b9b35bf735d61..1fb459122d1fba130c68b51de8fc0f09c8e6310e 100644 --- a/pkg/cluster/meta.go +++ b/pkg/cluster/meta.go @@ -6,8 +6,9 @@ import ( "forge.tedomum.net/acides/hepto/hepto/pkg/sml" ) -// Represents a node metadata metadata +// Represents a node metadata type HeptoMeta struct { + // Public key for the wireguard mesh VPN VpnKey []byte `json:"vpnKey"` } diff --git a/pkg/selfcontain/defaults.go b/pkg/selfcontain/defaults.go index 39926238d27f1089e8abccc546cb1f391f3d0f5f..c303ee38a3da4b26c791654f275d9288c7e0c6f8 100644 --- a/pkg/selfcontain/defaults.go +++ b/pkg/selfcontain/defaults.go @@ -102,7 +102,7 @@ var readOnlyPath = []string{ "/proc/bus", } -// Make a container configuration +// Make a container configuration using defaults func makeConfig(name string, newRoot string) *configs.Config { deviceRules := []*devices.Rule{} for _, device := range allowedDevices { diff --git a/pkg/selfcontain/net.go b/pkg/selfcontain/net.go index 1fa4b74b71505b24b0a5c0b760038d06d6789b4d..9fe86f5be68548a8a8c51fb5e0d0c3440ee106af 100644 --- a/pkg/selfcontain/net.go +++ b/pkg/selfcontain/net.go @@ -17,6 +17,9 @@ import ( const ACCEPT_PINFO = "net.ipv6.conf.eth0.accept_ra_pinfo" const ACCEPT_DFTRTR = "net.ipv6.conf.eth0.accept_ra_defrtr" +// Setup networking inside the container +// This must be called from outside the container, since it requires both access to the +// host networking stack and the namespace networking stack func (c *Container) SetupNetworking(master string, ip net.IPNet, gw net.IP, dns []net.IP, root string) error { ifaceName, err := c.setupIPVlan(master, 1500) if err != nil { diff --git a/pkg/sml/memberlist.go b/pkg/sml/memberlist.go index 45374a5d124f5554d688cfc05413d35e17059e97..174311539c25787a65a08636c3a285fa60b5f48d 100644 --- a/pkg/sml/memberlist.go +++ b/pkg/sml/memberlist.go @@ -1,3 +1,8 @@ +// Simple Memberlist is a wrapper around hashicorp memberlist, that +// leverages the gossip protocol and exposes a simpler interface. +// Main features include: automatic rejoining a list of cluster anchors, +// providing a channel of node changes, encoding and decoding of node +// metadata, plus caching of decoded metadata. package sml import ( @@ -24,7 +29,7 @@ type Memberlist struct { func New(nodeName string, nodeIP net.IP, port int, anchors []net.IP, key []byte, newMeta func() NodeMeta) *Memberlist { config := memberlist.DefaultLANConfig() - config.LogOutput = logrus.StandardLogger().Writer() + config.LogOutput = logrus.StandardLogger().WriterLevel(logrus.DebugLevel) config.SecretKey = key config.BindAddr = nodeIP.String() config.BindPort = port @@ -51,6 +56,8 @@ func New(nodeName string, nodeIP net.IP, port int, anchors []net.IP, key []byte, return m } +// Start the memberlist cluster main loop, that awaits cluster changes, maintains +// the cluster state and propagates information to channels func (m *Memberlist) Run() error { // Initialize memberlist ml, err := memberlist.Create(m.config) @@ -64,26 +71,31 @@ func (m *Memberlist) Run() error { case <-ticker: go m.join() case <-m.nodeChanges: - logrus.Info("Network topology changed") + logrus.Debug("Network topology changed, a node just joined, left or was updated") for _, node := range m.Nodes() { - logrus.Debugf("Node %s: %s ; %v\n", node.Name, string(node.Meta), node.NodeMeta) + logrus.Debugf("* Node %s: %s ; %v\n", node.Name, string(node.Meta), node.NodeMeta) } } } } +// Get the list of current cluster nodes func (m *Memberlist) Nodes() []Node { return m.nodeCache } +// Build a local node representation from an upstream memberlist node, using +// empty metadata (before decoding) func (m *Memberlist) newNode(mlNode *memberlist.Node) Node { return Node{mlNode, m.newMeta()} } +// Update the node cache after a network change, goes through all the +// nodes and decodes metadata func (m *Memberlist) updateCache() { // Do not update the node cache until memberlist is setup if m.ml == nil { - logrus.Debug("Not updating the node cache before ml is ready") + logrus.Debug("Not updating the node cache before memberlist is ready") return } members := m.ml.Members() @@ -97,6 +109,7 @@ func (m *Memberlist) updateCache() { m.nodeChanges <- struct{}{} } +// Try and join any anchor that is not currently a cluster member func (m *Memberlist) join() error { addrs := []string{} for _, candidate := range m.anchors { @@ -113,7 +126,7 @@ func (m *Memberlist) join() error { addrs = append(addrs, candidate.String()) } if len(addrs) > 0 { - logrus.Info("Joining nodes: ", addrs) + logrus.Info("Joining cluster nodes: ", addrs) } _, err := m.ml.Join(addrs) return err