2024-09-07 13:46:59 +00:00
|
|
|
// Package children manages the creation, lifetime, and shutdown of child
|
|
|
|
// processes created by the daemon.
|
|
|
|
package children
|
2024-07-06 13:36:48 +00:00
|
|
|
|
|
|
|
import (
|
|
|
|
"context"
|
2024-07-13 12:34:06 +00:00
|
|
|
"errors"
|
2024-07-06 13:36:48 +00:00
|
|
|
"fmt"
|
|
|
|
|
|
|
|
"code.betamike.com/micropelago/pmux/pmuxlib"
|
2024-10-27 13:31:10 +00:00
|
|
|
"dev.mediocregopher.com/mediocre-go-lib.git/mctx"
|
2024-07-06 13:36:48 +00:00
|
|
|
"dev.mediocregopher.com/mediocre-go-lib.git/mlog"
|
2024-09-07 13:46:59 +00:00
|
|
|
|
|
|
|
"isle/bootstrap"
|
|
|
|
"isle/daemon/daecommon"
|
|
|
|
"isle/secrets"
|
2024-09-09 14:34:00 +00:00
|
|
|
"isle/toolkit"
|
2024-07-06 13:36:48 +00:00
|
|
|
)
|
|
|
|
|
2024-09-07 13:46:59 +00:00
|
|
|
// Opts are optional parameters which can be passed in when initializing a new
|
|
|
|
// Children instance. A nil Opts is equivalent to a zero value.
|
2024-10-29 14:11:13 +00:00
|
|
|
type Opts struct{}
|
2024-09-07 13:46:59 +00:00
|
|
|
|
|
|
|
func (o *Opts) withDefaults() *Opts {
|
|
|
|
if o == nil {
|
|
|
|
o = new(Opts)
|
|
|
|
}
|
|
|
|
|
|
|
|
return o
|
|
|
|
}
|
|
|
|
|
2024-07-06 13:36:48 +00:00
|
|
|
// 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 {
|
2024-10-27 13:31:10 +00:00
|
|
|
logger *mlog.Logger
|
2024-10-29 14:11:13 +00:00
|
|
|
binDirPath string
|
2024-10-27 13:31:10 +00:00
|
|
|
runtimeDir toolkit.Dir
|
|
|
|
garageAdminToken string
|
|
|
|
opts Opts
|
|
|
|
|
|
|
|
garageRPCSecret string
|
2024-07-06 13:36:48 +00:00
|
|
|
|
2024-10-29 14:11:13 +00:00
|
|
|
nebulaProc *pmuxlib.Process
|
|
|
|
dnsmasqProc *pmuxlib.Process
|
|
|
|
garageProcs map[string]*pmuxlib.Process
|
2024-07-06 13:36:48 +00:00
|
|
|
}
|
|
|
|
|
2024-09-07 13:46:59 +00:00
|
|
|
// New initializes and returns a Children instance. If initialization fails an
|
|
|
|
// error is returned.
|
|
|
|
func New(
|
2024-07-06 13:36:48 +00:00
|
|
|
ctx context.Context,
|
|
|
|
logger *mlog.Logger,
|
2024-07-13 12:34:06 +00:00
|
|
|
binDirPath string,
|
|
|
|
secretsStore secrets.Store,
|
2024-09-10 20:51:33 +00:00
|
|
|
networkConfig daecommon.NetworkConfig,
|
2024-09-09 14:34:00 +00:00
|
|
|
runtimeDir toolkit.Dir,
|
2024-07-14 10:19:39 +00:00
|
|
|
garageAdminToken string,
|
2024-07-06 13:36:48 +00:00
|
|
|
hostBootstrap bootstrap.Bootstrap,
|
|
|
|
opts *Opts,
|
|
|
|
) (
|
|
|
|
*Children, error,
|
|
|
|
) {
|
|
|
|
opts = opts.withDefaults()
|
|
|
|
|
2024-07-13 12:34:06 +00:00
|
|
|
logger.Info(ctx, "Loading secrets")
|
2024-09-07 13:11:04 +00:00
|
|
|
garageRPCSecret, err := daecommon.GetGarageRPCSecret(ctx, secretsStore)
|
2024-07-13 12:34:06 +00:00
|
|
|
if err != nil && !errors.Is(err, secrets.ErrNotFound) {
|
|
|
|
return nil, fmt.Errorf("loading garage RPC secret: %w", err)
|
|
|
|
}
|
|
|
|
|
2024-07-06 13:36:48 +00:00
|
|
|
c := &Children{
|
2024-10-27 13:31:10 +00:00
|
|
|
logger: logger,
|
2024-10-29 14:11:13 +00:00
|
|
|
binDirPath: binDirPath,
|
2024-10-27 13:31:10 +00:00
|
|
|
runtimeDir: runtimeDir,
|
|
|
|
garageAdminToken: garageAdminToken,
|
|
|
|
opts: *opts,
|
|
|
|
garageRPCSecret: garageRPCSecret,
|
2024-07-06 13:36:48 +00:00
|
|
|
}
|
|
|
|
|
2024-10-29 14:11:13 +00:00
|
|
|
if c.nebulaProc, err = nebulaPmuxProc(
|
|
|
|
ctx,
|
|
|
|
c.logger,
|
|
|
|
c.runtimeDir.Path,
|
|
|
|
c.binDirPath,
|
|
|
|
networkConfig,
|
|
|
|
hostBootstrap,
|
|
|
|
); err != nil {
|
|
|
|
return nil, fmt.Errorf("starting nebula: %w", err)
|
|
|
|
}
|
|
|
|
|
|
|
|
if err := waitForNebula(ctx, c.logger, hostBootstrap); err != nil {
|
|
|
|
logger.Warn(ctx, "Failed waiting for nebula to initialize, shutting down child processes", err)
|
|
|
|
c.Shutdown()
|
|
|
|
return nil, fmt.Errorf("waiting for nebula to start: %w", err)
|
|
|
|
}
|
|
|
|
|
|
|
|
if c.dnsmasqProc, err = dnsmasqPmuxProc(
|
|
|
|
ctx,
|
|
|
|
c.logger,
|
|
|
|
c.runtimeDir.Path,
|
|
|
|
c.binDirPath,
|
|
|
|
networkConfig,
|
|
|
|
hostBootstrap,
|
|
|
|
); err != nil {
|
|
|
|
logger.Warn(ctx, "Failed to start dnsmasq, shutting down child processes", err)
|
|
|
|
c.Shutdown()
|
|
|
|
return nil, fmt.Errorf("starting dnsmasq: %w", err)
|
|
|
|
}
|
|
|
|
|
|
|
|
// TODO wait for dnsmasq to come up
|
|
|
|
|
|
|
|
if c.garageProcs, err = garagePmuxProcs(
|
2024-07-14 10:19:39 +00:00
|
|
|
ctx,
|
2024-10-29 14:11:13 +00:00
|
|
|
c.logger,
|
2024-07-14 10:19:39 +00:00
|
|
|
garageRPCSecret,
|
2024-10-29 14:11:13 +00:00
|
|
|
c.runtimeDir.Path,
|
|
|
|
c.binDirPath,
|
2024-09-10 20:51:33 +00:00
|
|
|
networkConfig,
|
2024-07-14 10:19:39 +00:00
|
|
|
garageAdminToken,
|
|
|
|
hostBootstrap,
|
2024-10-29 14:11:13 +00:00
|
|
|
); err != nil {
|
|
|
|
logger.Warn(ctx, "Failed to start garage processes, shutting down child processes", err)
|
|
|
|
c.Shutdown()
|
|
|
|
return nil, fmt.Errorf("starting garage processes: %w", err)
|
2024-07-06 13:36:48 +00:00
|
|
|
}
|
|
|
|
|
2024-10-29 14:11:13 +00:00
|
|
|
if err := waitForGarage(
|
|
|
|
ctx, c.logger, networkConfig, garageAdminToken, hostBootstrap,
|
|
|
|
); err != nil {
|
|
|
|
logger.Warn(ctx, "Failed waiting for garage processes to initialize, shutting down child processes", err)
|
2024-07-20 09:07:11 +00:00
|
|
|
c.Shutdown()
|
2024-10-29 14:11:13 +00:00
|
|
|
return nil, fmt.Errorf("waiting for garage processes to initialize: %w", err)
|
2024-07-06 13:36:48 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
return c, nil
|
|
|
|
}
|
|
|
|
|
2024-09-07 13:46:59 +00:00
|
|
|
// TODO block until process has been confirmed to have come back up
|
|
|
|
// successfully.
|
2024-10-24 19:19:58 +00:00
|
|
|
func (c *Children) reloadDNSMasq(
|
2024-10-27 13:31:10 +00:00
|
|
|
ctx context.Context,
|
2024-10-24 19:19:58 +00:00
|
|
|
networkConfig daecommon.NetworkConfig,
|
|
|
|
hostBootstrap bootstrap.Bootstrap,
|
|
|
|
) error {
|
2024-10-27 13:31:10 +00:00
|
|
|
if _, changed, err := dnsmasqWriteConfig(
|
|
|
|
ctx, c.logger, c.runtimeDir.Path, networkConfig, hostBootstrap,
|
|
|
|
); err != nil {
|
2024-07-19 18:49:04 +00:00
|
|
|
return fmt.Errorf("writing new dnsmasq config: %w", err)
|
2024-10-27 13:31:10 +00:00
|
|
|
} else if !changed {
|
|
|
|
c.logger.Info(ctx, "No changes to dnsmasq config file")
|
|
|
|
return nil
|
2024-07-19 18:49:04 +00:00
|
|
|
}
|
|
|
|
|
2024-10-27 13:31:10 +00:00
|
|
|
c.logger.Info(ctx, "dnsmasq config file has changed, restarting process")
|
2024-10-29 14:11:13 +00:00
|
|
|
c.dnsmasqProc.Restart()
|
2024-10-27 13:31:10 +00:00
|
|
|
|
2024-07-19 18:49:04 +00:00
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
2024-10-24 19:19:58 +00:00
|
|
|
func (c *Children) reloadNebula(
|
|
|
|
ctx context.Context,
|
|
|
|
networkConfig daecommon.NetworkConfig,
|
|
|
|
hostBootstrap bootstrap.Bootstrap,
|
|
|
|
) error {
|
2024-10-27 13:31:10 +00:00
|
|
|
if _, changed, err := nebulaWriteConfig(
|
|
|
|
ctx, c.logger, c.runtimeDir.Path, networkConfig, hostBootstrap,
|
|
|
|
); err != nil {
|
2024-07-19 18:49:04 +00:00
|
|
|
return fmt.Errorf("writing a new nebula config: %w", err)
|
2024-10-27 13:31:10 +00:00
|
|
|
} else if !changed {
|
|
|
|
c.logger.Info(ctx, "No changes to nebula config file")
|
|
|
|
return nil
|
2024-07-19 18:49:04 +00:00
|
|
|
}
|
|
|
|
|
2024-10-27 13:31:10 +00:00
|
|
|
c.logger.Info(ctx, "nebula config file has changed, restarting process")
|
2024-10-29 14:11:13 +00:00
|
|
|
c.nebulaProc.Restart()
|
2024-10-24 19:19:58 +00:00
|
|
|
|
|
|
|
if err := waitForNebula(ctx, c.logger, hostBootstrap); err != nil {
|
|
|
|
return fmt.Errorf("waiting for nebula to start: %w", err)
|
|
|
|
}
|
|
|
|
|
2024-07-19 18:49:04 +00:00
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
2024-10-27 13:31:10 +00:00
|
|
|
// TODO this doesn't handle removing garage nodes
|
|
|
|
func (c *Children) reloadGarage(
|
|
|
|
ctx context.Context,
|
|
|
|
networkConfig daecommon.NetworkConfig,
|
|
|
|
hostBootstrap bootstrap.Bootstrap,
|
|
|
|
) error {
|
|
|
|
allocs := networkConfig.Storage.Allocations
|
|
|
|
if len(allocs) == 0 {
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
var anyChanged bool
|
|
|
|
for _, alloc := range allocs {
|
|
|
|
var (
|
|
|
|
procName = garagePmuxProcName(alloc)
|
|
|
|
ctx = mctx.Annotate(
|
|
|
|
ctx,
|
|
|
|
"garageProcName", procName,
|
|
|
|
"garageDataPath", alloc.DataPath,
|
|
|
|
)
|
|
|
|
)
|
|
|
|
|
2024-10-29 14:11:13 +00:00
|
|
|
childConfigPath, changed, err := garageWriteChildConfig(
|
2024-10-27 13:31:10 +00:00
|
|
|
ctx,
|
|
|
|
c.logger,
|
|
|
|
c.garageRPCSecret,
|
|
|
|
c.runtimeDir.Path,
|
|
|
|
c.garageAdminToken,
|
|
|
|
hostBootstrap,
|
|
|
|
alloc,
|
|
|
|
)
|
|
|
|
if err != nil {
|
|
|
|
return fmt.Errorf("writing child config file for alloc %+v: %w", alloc, err)
|
|
|
|
} else if !changed {
|
|
|
|
c.logger.Info(ctx, "No changes to garage config file")
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
|
|
|
|
anyChanged = true
|
|
|
|
|
2024-10-29 14:11:13 +00:00
|
|
|
if proc, ok := c.garageProcs[procName]; ok {
|
|
|
|
c.logger.Info(ctx, "garage config has changed, restarting process")
|
|
|
|
proc.Restart()
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
|
|
|
|
c.logger.Info(ctx, "garage config has been added, creating process")
|
|
|
|
c.garageProcs[procName] = garagePmuxProc(
|
|
|
|
ctx, c.logger, c.binDirPath, procName, childConfigPath,
|
|
|
|
)
|
2024-10-27 13:31:10 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
if anyChanged {
|
|
|
|
if err := waitForGarage(
|
|
|
|
ctx, c.logger, networkConfig, c.garageAdminToken, hostBootstrap,
|
|
|
|
); err != nil {
|
|
|
|
return fmt.Errorf("waiting for garage to start: %w", err)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
2024-09-07 13:46:59 +00:00
|
|
|
// 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(
|
2024-10-24 19:19:58 +00:00
|
|
|
ctx context.Context,
|
|
|
|
newNetworkConfig daecommon.NetworkConfig,
|
|
|
|
newBootstrap bootstrap.Bootstrap,
|
2024-09-07 13:46:59 +00:00
|
|
|
) error {
|
2024-10-27 13:31:10 +00:00
|
|
|
if err := c.reloadNebula(ctx, newNetworkConfig, newBootstrap); err != nil {
|
|
|
|
return fmt.Errorf("reloading nebula: %w", err)
|
|
|
|
}
|
|
|
|
|
2024-09-07 13:46:59 +00:00
|
|
|
var errs []error
|
|
|
|
|
2024-10-27 13:31:10 +00:00
|
|
|
if err := c.reloadDNSMasq(ctx, newNetworkConfig, newBootstrap); err != nil {
|
|
|
|
errs = append(errs, fmt.Errorf("reloading dnsmasq: %w", err))
|
2024-09-07 13:46:59 +00:00
|
|
|
}
|
|
|
|
|
2024-10-27 13:31:10 +00:00
|
|
|
if err := c.reloadGarage(ctx, newNetworkConfig, newBootstrap); err != nil {
|
|
|
|
errs = append(errs, fmt.Errorf("reloading garage: %w", err))
|
2024-09-07 13:46:59 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
return errors.Join(errs...)
|
|
|
|
}
|
|
|
|
|
2024-07-06 13:36:48 +00:00
|
|
|
// Shutdown blocks until all child processes have gracefully shut themselves
|
|
|
|
// down.
|
2024-07-20 09:07:11 +00:00
|
|
|
func (c *Children) Shutdown() {
|
2024-10-29 14:11:13 +00:00
|
|
|
for _, proc := range c.garageProcs {
|
|
|
|
proc.Stop()
|
|
|
|
}
|
|
|
|
|
|
|
|
if c.dnsmasqProc != nil {
|
|
|
|
c.dnsmasqProc.Stop()
|
|
|
|
}
|
|
|
|
|
|
|
|
if c.nebulaProc != nil {
|
|
|
|
c.nebulaProc.Stop()
|
|
|
|
}
|
2024-07-06 13:36:48 +00:00
|
|
|
}
|