isle/go/daemon/children/children.go

185 lines
4.2 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
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,
networkConfig daecommon.NetworkConfig,
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,
runtimeDir: runtimeDir,
opts: *opts,
}
pmuxConfig, err := c.newPmuxConfig(
ctx,
garageRPCSecret,
binDirPath,
networkConfig,
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, networkConfig, 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
}
// TODO block until process has been confirmed to have come back up
// successfully.
func (c *Children) reloadDNSMasq(
networkConfig daecommon.NetworkConfig,
hostBootstrap bootstrap.Bootstrap,
) error {
_, err := dnsmasqWriteConfig(
c.runtimeDir.Path, networkConfig, hostBootstrap,
)
if err != nil {
return fmt.Errorf("writing new dnsmasq config: %w", err)
}
c.pmux.Restart("dnsmasq")
return nil
}
func (c *Children) reloadNebula(
ctx context.Context,
networkConfig daecommon.NetworkConfig,
hostBootstrap bootstrap.Bootstrap,
) error {
_, err := nebulaWriteConfig(
c.runtimeDir.Path, networkConfig, hostBootstrap,
)
if err != nil {
return fmt.Errorf("writing a new nebula config: %w", err)
}
c.pmux.Restart("nebula")
c.logger.Info(ctx, "Waiting for nebula VPN to come online")
if err := waitForNebula(ctx, c.logger, hostBootstrap); err != nil {
return fmt.Errorf("waiting for nebula to start: %w", err)
}
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,
newNetworkConfig daecommon.NetworkConfig,
newBootstrap bootstrap.Bootstrap,
diff ReloadDiff,
) error {
var errs []error
if diff.DNSChanged {
c.logger.Info(ctx, "Reloading dnsmasq to account for bootstrap changes")
if err := c.reloadDNSMasq(newNetworkConfig, newBootstrap); err != nil {
errs = append(errs, fmt.Errorf("reloading dnsmasq: %w", err))
}
}
if diff.NebulaChanged {
c.logger.Info(ctx, "Reloading nebula to account for bootstrap changes")
err := c.reloadNebula(ctx, newNetworkConfig, newBootstrap)
if err != nil {
errs = append(errs, fmt.Errorf("reloading 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()
}