Brian Picciano
8c3e6a2845
In a world where the daemon can manage more than one network, the Daemon is really responsible only for knowing which networks are currently joined, creating/joining/leaving networks, and routing incoming RPC requests to the correct network handler as needed. The new network package, with its Network interface, inherits most of the logic that Daemon used to have, leaving Daemon only the parts needed for the functionality just described. There's a lot of cleanup done here in order to really nail down the separation of concerns between the two, especially around directory creation.
176 lines
4.1 KiB
Go
176 lines
4.1 KiB
Go
// Package children manages the creation, lifetime, and shutdown of child
|
|
// processes created by the daemon.
|
|
package children
|
|
|
|
import (
|
|
"context"
|
|
"errors"
|
|
"fmt"
|
|
"io"
|
|
"os"
|
|
|
|
"code.betamike.com/micropelago/pmux/pmuxlib"
|
|
"dev.mediocregopher.com/mediocre-go-lib.git/mlog"
|
|
|
|
"isle/bootstrap"
|
|
"isle/daemon/daecommon"
|
|
"isle/secrets"
|
|
"isle/toolkit"
|
|
)
|
|
|
|
// Opts are optional parameters which can be passed in when initializing a new
|
|
// Children instance. A nil Opts is equivalent to a zero value.
|
|
type Opts struct {
|
|
// Stdout and Stderr are what the associated outputs from child processes
|
|
// will be directed to.
|
|
Stdout, Stderr io.Writer
|
|
}
|
|
|
|
func (o *Opts) withDefaults() *Opts {
|
|
if o == nil {
|
|
o = new(Opts)
|
|
}
|
|
|
|
if o.Stdout == nil {
|
|
o.Stdout = os.Stdout
|
|
}
|
|
|
|
if o.Stderr == nil {
|
|
o.Stderr = os.Stderr
|
|
}
|
|
|
|
return o
|
|
}
|
|
|
|
// Children manages all child processes of a network. Child processes are
|
|
// comprised of:
|
|
// - nebula
|
|
// - dnsmasq
|
|
// - garage (0 or more, depending on configured storage allocations)
|
|
type Children struct {
|
|
logger *mlog.Logger
|
|
daemonConfig daecommon.Config
|
|
runtimeDir toolkit.Dir
|
|
opts Opts
|
|
|
|
pmux *pmuxlib.Pmux
|
|
}
|
|
|
|
// New initializes and returns a Children instance. If initialization fails an
|
|
// error is returned.
|
|
func New(
|
|
ctx context.Context,
|
|
logger *mlog.Logger,
|
|
binDirPath string,
|
|
secretsStore secrets.Store,
|
|
daemonConfig daecommon.Config,
|
|
runtimeDir toolkit.Dir,
|
|
garageAdminToken string,
|
|
hostBootstrap bootstrap.Bootstrap,
|
|
opts *Opts,
|
|
) (
|
|
*Children, error,
|
|
) {
|
|
opts = opts.withDefaults()
|
|
|
|
logger.Info(ctx, "Loading secrets")
|
|
garageRPCSecret, err := daecommon.GetGarageRPCSecret(ctx, secretsStore)
|
|
if err != nil && !errors.Is(err, secrets.ErrNotFound) {
|
|
return nil, fmt.Errorf("loading garage RPC secret: %w", err)
|
|
}
|
|
|
|
c := &Children{
|
|
logger: logger,
|
|
daemonConfig: daemonConfig,
|
|
runtimeDir: runtimeDir,
|
|
opts: *opts,
|
|
}
|
|
|
|
pmuxConfig, err := c.newPmuxConfig(
|
|
ctx,
|
|
garageRPCSecret,
|
|
binDirPath,
|
|
daemonConfig,
|
|
garageAdminToken,
|
|
hostBootstrap,
|
|
)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("generating pmux config: %w", err)
|
|
}
|
|
|
|
c.pmux = pmuxlib.NewPmux(pmuxConfig, c.opts.Stdout, c.opts.Stderr)
|
|
|
|
initErr := c.postPmuxInit(
|
|
ctx, daemonConfig, garageAdminToken, hostBootstrap,
|
|
)
|
|
if initErr != nil {
|
|
logger.Warn(ctx, "failed to initialize Children, shutting down child processes", err)
|
|
c.Shutdown()
|
|
return nil, initErr
|
|
}
|
|
|
|
return c, nil
|
|
}
|
|
|
|
// RestartDNSMasq rewrites the dnsmasq config and restarts the process.
|
|
//
|
|
// TODO block until process has been confirmed to have come back up
|
|
// successfully.
|
|
func (c *Children) RestartDNSMasq(hostBootstrap bootstrap.Bootstrap) error {
|
|
_, err := dnsmasqWriteConfig(
|
|
c.runtimeDir.Path, c.daemonConfig, hostBootstrap,
|
|
)
|
|
if err != nil {
|
|
return fmt.Errorf("writing new dnsmasq config: %w", err)
|
|
}
|
|
|
|
c.pmux.Restart("dnsmasq")
|
|
return nil
|
|
}
|
|
|
|
// RestartNebula rewrites the nebula config and restarts the process.
|
|
//
|
|
// TODO block until process has been confirmed to have come back up
|
|
// successfully.
|
|
func (c *Children) RestartNebula(hostBootstrap bootstrap.Bootstrap) error {
|
|
_, err := nebulaWriteConfig(
|
|
c.runtimeDir.Path, c.daemonConfig, hostBootstrap,
|
|
)
|
|
if err != nil {
|
|
return fmt.Errorf("writing a new nebula config: %w", err)
|
|
}
|
|
|
|
c.pmux.Restart("nebula")
|
|
return nil
|
|
}
|
|
|
|
// Reload applies a ReloadDiff to the Children, using the given bootstrap which
|
|
// must be the same one which was passed to CalculateReloadDiff.
|
|
func (c *Children) Reload(
|
|
ctx context.Context, newBootstrap bootstrap.Bootstrap, diff ReloadDiff,
|
|
) error {
|
|
var errs []error
|
|
|
|
if diff.DNSChanged {
|
|
c.logger.Info(ctx, "Restarting dnsmasq to account for bootstrap changes")
|
|
if err := c.RestartDNSMasq(newBootstrap); err != nil {
|
|
errs = append(errs, fmt.Errorf("restarting dnsmasq: %w", err))
|
|
}
|
|
}
|
|
|
|
if diff.NebulaChanged {
|
|
c.logger.Info(ctx, "Restarting nebula to account for bootstrap changes")
|
|
if err := c.RestartNebula(newBootstrap); err != nil {
|
|
errs = append(errs, fmt.Errorf("restarting nebula: %w", err))
|
|
}
|
|
}
|
|
|
|
return errors.Join(errs...)
|
|
}
|
|
|
|
// Shutdown blocks until all child processes have gracefully shut themselves
|
|
// down.
|
|
func (c *Children) Shutdown() {
|
|
c.pmux.Stop()
|
|
}
|