core/internal/network: move to supervisor
Test Plan: behaviour shouldn't change, covered by existing tests
Bug: T653
X-Origin-Diff: phab/D430
GitOrigin-RevId: b92f0953daba6da84bad96120cde2021c4a82e5c
diff --git a/core/internal/network/BUILD.bazel b/core/internal/network/BUILD.bazel
index 78e4885..7e45086 100644
--- a/core/internal/network/BUILD.bazel
+++ b/core/internal/network/BUILD.bazel
@@ -9,7 +9,7 @@
importpath = "git.monogon.dev/source/nexantic.git/core/internal/network",
visibility = ["//:__subpackages__"],
deps = [
- "//core/internal/common/service:go_default_library",
+ "//core/internal/common/supervisor:go_default_library",
"@com_github_insomniacslk_dhcp//dhcpv4:go_default_library",
"@com_github_insomniacslk_dhcp//dhcpv4/nclient4:go_default_library",
"@com_github_vishvananda_netlink//:go_default_library",
diff --git a/core/internal/network/dhcp.go b/core/internal/network/dhcp.go
index 9a2ba8c..983c25c 100644
--- a/core/internal/network/dhcp.go
+++ b/core/internal/network/dhcp.go
@@ -21,6 +21,8 @@
"fmt"
"net"
+ "git.monogon.dev/source/nexantic.git/core/internal/common/supervisor"
+
"github.com/insomniacslk/dhcp/dhcpv4"
"github.com/insomniacslk/dhcp/dhcpv4/nclient4"
"github.com/vishvananda/netlink"
@@ -28,14 +30,12 @@
)
type dhcpClient struct {
- reqC chan *dhcpStatusReq
- logger *zap.Logger
+ reqC chan *dhcpStatusReq
}
-func newDHCPClient(logger *zap.Logger) *dhcpClient {
+func newDHCPClient() *dhcpClient {
return &dhcpClient{
- logger: logger,
- reqC: make(chan *dhcpStatusReq),
+ reqC: make(chan *dhcpStatusReq),
}
}
@@ -65,54 +65,69 @@
return fmt.Sprintf("Address: %s, Gateway: %s, DNS: %v", s.address.String(), s.gateway.String(), s.dns)
}
-func (c *dhcpClient) run(ctx context.Context, iface netlink.Link) {
- // Channel updated with address once one gets assigned/updated
- newC := make(chan *dhcpStatus)
- // Status requests waiting for configuration
- waiters := []*dhcpStatusReq{}
+func (c *dhcpClient) run(iface netlink.Link) supervisor.Runnable {
+ return func(ctx context.Context) error {
+ logger := supervisor.Logger(ctx)
- // Start lease acquisition
- // TODO(q3k): actually maintain the lease instead of hoping we never get
- // kicked off.
- client, err := nclient4.New(iface.Attrs().Name)
- if err != nil {
- panic(err)
- }
- go func() {
- _, ack, err := client.Request(ctx)
+ // Channel updated with address once one gets assigned/updated
+ newC := make(chan *dhcpStatus)
+ // Status requests waiting for configuration
+ waiters := []*dhcpStatusReq{}
+
+ // Start lease acquisition
+ // TODO(q3k): actually maintain the lease instead of hoping we never get
+ // kicked off.
+
+ client, err := nclient4.New(iface.Attrs().Name)
if err != nil {
- c.logger.Error("DHCP lease request failed", zap.Error(err))
- // TODO(q3k): implement retry logic with full state machine
+ return fmt.Errorf("nclient4.New: %w", err)
}
- newC <- parseAck(ack)
- }()
- // State machine
- // Two implicit states: WAITING -> ASSIGNED
- // We start at WAITING, once we get a current config we move to ASSIGNED
- // Once this becomes more complex (ie. has to handle link state changes)
- // this should grow into a real state machine.
- var current *dhcpStatus
- c.logger.Info("DHCP client WAITING")
- for {
- select {
- case <-ctx.Done():
- // TODO(q3k): don't leave waiters hanging
- return
-
- case cfg := <-newC:
- current = cfg
- c.logger.Info("DHCP client ASSIGNED", zap.String("ip", current.String()))
- for _, w := range waiters {
- w.fulfill(current)
+ err = supervisor.Run(ctx, "client", func(ctx context.Context) error {
+ supervisor.Signal(ctx, supervisor.SignalHealthy)
+ _, ack, err := client.Request(ctx)
+ if err != nil {
+ // TODO(q3k): implement retry logic with full state machine
+ logger.Error("DHCP lease request failed", zap.Error(err))
+ return err
}
- waiters = []*dhcpStatusReq{}
+ newC <- parseAck(ack)
+ supervisor.Signal(ctx, supervisor.SignalDone)
+ return nil
+ })
+ if err != nil {
+ return err
+ }
- case r := <-c.reqC:
- if current != nil || !r.wait {
- r.fulfill(current)
- } else {
- waiters = append(waiters, r)
+ supervisor.Signal(ctx, supervisor.SignalHealthy)
+
+ // State machine
+ // Two implicit states: WAITING -> ASSIGNED
+ // We start at WAITING, once we get a current config we move to ASSIGNED
+ // Once this becomes more complex (ie. has to handle link state changes)
+ // this should grow into a real state machine.
+ var current *dhcpStatus
+ logger.Info("DHCP client WAITING")
+ for {
+ select {
+ case <-ctx.Done():
+ // TODO(q3k): don't leave waiters hanging
+ return err
+
+ case cfg := <-newC:
+ current = cfg
+ logger.Info("DHCP client ASSIGNED", zap.String("ip", current.String()))
+ for _, w := range waiters {
+ w.fulfill(current)
+ }
+ waiters = []*dhcpStatusReq{}
+
+ case r := <-c.reqC:
+ if current != nil || !r.wait {
+ r.fulfill(current)
+ } else {
+ waiters = append(waiters, r)
+ }
}
}
}
diff --git a/core/internal/network/main.go b/core/internal/network/main.go
index 01760c7..00d7fb2 100644
--- a/core/internal/network/main.go
+++ b/core/internal/network/main.go
@@ -22,7 +22,7 @@
"net"
"os"
- "git.monogon.dev/source/nexantic.git/core/internal/common/service"
+ "git.monogon.dev/source/nexantic.git/core/internal/common/supervisor"
"github.com/vishvananda/netlink"
"go.uber.org/zap"
@@ -35,22 +35,20 @@
)
type Service struct {
- *service.BaseService
config Config
+ dhcp *dhcpClient
- dhcp *dhcpClient
+ logger *zap.Logger
}
type Config struct {
}
-func NewNetworkService(config Config, logger *zap.Logger) (*Service, error) {
- s := &Service{
+func New(config Config) *Service {
+ return &Service{
config: config,
- dhcp: newDHCPClient(logger),
+ dhcp: newDHCPClient(),
}
- s.BaseService = service.NewBaseService("network", logger, s)
- return s, nil
}
func setResolvconf(nameservers []net.IP, searchDomains []string) error {
@@ -82,7 +80,7 @@
}
if gw.IsUnspecified() {
- s.Logger.Info("No default route set, only local network will be reachable", zap.String("local", addr.String()))
+ s.logger.Info("No default route set, only local network will be reachable", zap.String("local", addr.String()))
return nil
}
@@ -97,21 +95,24 @@
return nil
}
-func (s *Service) useInterface(iface netlink.Link) error {
- go s.dhcp.run(s.Context(), iface)
-
- status, err := s.dhcp.status(s.Context(), true)
+func (s *Service) useInterface(ctx context.Context, iface netlink.Link) error {
+ err := supervisor.Run(ctx, "dhcp", s.dhcp.run(iface))
if err != nil {
- return fmt.Errorf("could not get DHCP status: %v", err)
+ return err
+ }
+ status, err := s.dhcp.status(ctx, true)
+ if err != nil {
+ return fmt.Errorf("could not get DHCP status: %w", err)
}
if err := setResolvconf(status.dns, []string{}); err != nil {
- s.Logger.Warn("failed to set resolvconf", zap.Error(err))
+ s.logger.Warn("failed to set resolvconf", zap.Error(err))
}
if err := s.addNetworkRoutes(iface, net.IPNet{IP: status.address.IP, Mask: status.address.Mask}, status.gateway); err != nil {
- s.Logger.Warn("failed to add routes", zap.Error(err))
+ s.logger.Warn("failed to add routes", zap.Error(err))
}
+
return nil
}
@@ -124,12 +125,15 @@
return &status.address.IP, nil
}
-func (s *Service) OnStart() error {
- s.Logger.Info("Starting network service")
+func (s *Service) Run(ctx context.Context) error {
+ s.logger = supervisor.Logger(ctx)
+ s.logger.Info("Starting network service")
+
links, err := netlink.LinkList()
if err != nil {
- s.Logger.Fatal("Failed to list network links", zap.Error(err))
+ s.logger.Fatal("Failed to list network links", zap.Error(err))
}
+
var ethernetLinks []netlink.Link
for _, link := range links {
attrs := link.Attrs()
@@ -140,25 +144,24 @@
}
ethernetLinks = append(ethernetLinks, link)
} else {
- s.Logger.Info("Ignoring non-Ethernet interface", zap.String("interface", attrs.Name))
+ s.logger.Info("Ignoring non-Ethernet interface", zap.String("interface", attrs.Name))
}
} else if link.Attrs().Name == "lo" {
if err := netlink.LinkSetUp(link); err != nil {
- s.Logger.Error("Failed to take up loopback interface", zap.Error(err))
+ s.logger.Error("Failed to take up loopback interface", zap.Error(err))
}
}
}
if len(ethernetLinks) != 1 {
- s.Logger.Warn("Network service needs exactly one link, bailing")
- return nil
+ s.logger.Warn("Network service needs exactly one link, bailing")
+ } else {
+ link := ethernetLinks[0]
+ if err := s.useInterface(ctx, link); err != nil {
+ return fmt.Errorf("failed to bring up link %s: %w", link.Attrs().Name, err)
+ }
}
- link := ethernetLinks[0]
- go s.useInterface(link)
-
- return nil
-}
-
-func (s *Service) OnStop() error {
+ supervisor.Signal(ctx, supervisor.SignalHealthy)
+ supervisor.Signal(ctx, supervisor.SignalDone)
return nil
}