isle/go/daemon/network/loader.go

297 lines
6.5 KiB
Go

//go:generate mockery --name Loader --inpackage --filename loader_mock.go
package network
import (
"context"
"fmt"
"isle/bootstrap"
"isle/daemon/daecommon"
"isle/nebula"
"isle/toolkit"
"dev.mediocregopher.com/mediocre-go-lib.git/mlog"
)
func networkStateDir(
networksStateDir toolkit.Dir, networkID string, mayExist bool,
) (
toolkit.Dir, error,
) {
return networksStateDir.MkChildDir(networkID, mayExist)
}
func networkRuntimeDir(
networksRuntimeDir toolkit.Dir, networkID string, mayExist bool,
) (
toolkit.Dir, error,
) {
return networksRuntimeDir.MkChildDir(networkID, mayExist)
}
func networkDirs(
networksStateDir, networksRuntimeDir toolkit.Dir,
networkID string,
mayExist bool,
) (
stateDir, runtimeDir toolkit.Dir, err error,
) {
h := new(toolkit.MkDirHelper)
stateDir, _ = h.Maybe(
networkStateDir(networksStateDir, networkID, mayExist),
)
runtimeDir, _ = h.Maybe(
networkRuntimeDir(networksRuntimeDir, networkID, mayExist),
)
err = h.Err()
return
}
// Loader is responsible for joining/creating Networks and making them loadable
// later.
type Loader interface {
// Loadable returns the CreationParams for all Networks which can be Loaded.
Loadable(context.Context) ([]bootstrap.CreationParams, error)
// Load initializes and returns a Network instance for a network which was
// previously joined or created, and which has the given CreationParams.
Load(
context.Context,
*mlog.Logger,
bootstrap.CreationParams,
*Opts,
) (
Network, error,
)
// Join initializes and returns a Network instance for an existing network
// which was not previously joined to on this host. Once Join has been
// called for a particular network it will error on subsequent calls for
// that same network, Load should be used instead.
Join(
context.Context,
*mlog.Logger,
JoiningBootstrap,
*Opts,
) (
Network, error,
)
// Create initializes and returns a Network for a brand new network which
// uses the given creation parameters.
//
// - name: Human-readable name of the network.
// - domain: Primary domain name that network services are served under.
// - ipNet: An IP subnet, in CIDR form, which will be the overall range of
// possible IPs in the network. The first IP in this network range will
// become this first host's IP.
// - hostName: The name of this first host in the network.
//
// Errors:
// - ErrInvalidConfig - If the Opts.Config field is not valid. It must be
// non-nil and have at least 3 storage allocations.
Create(
context.Context,
*mlog.Logger,
bootstrap.CreationParams,
nebula.IPNet,
nebula.HostName,
*Opts,
) (
Network, error,
)
}
// LoaderOpts are optional parameters which can be passed in when initializing a
// new Loader instance. A nil LoaderOpts is equivalent to a zero value.
type LoaderOpts struct {
// Defaults to that returned by daecommon.GetEnvVars.
EnvVars daecommon.EnvVars
}
func (o *LoaderOpts) withDefaults() *LoaderOpts {
if o == nil {
o = new(LoaderOpts)
}
if o.EnvVars == (daecommon.EnvVars{}) {
o.EnvVars = daecommon.GetEnvVars()
}
return o
}
type loader struct {
envBinDirPath string
networksStateDir toolkit.Dir
networksRuntimeDir toolkit.Dir
}
// NewLoader returns a new Loader which will use the given directories to load
// and create Network instances.
func NewLoader(
ctx context.Context,
logger *mlog.Logger,
envBinDirPath string,
opts *LoaderOpts,
) (
Loader, error,
) {
opts = opts.withDefaults()
if err := migrateToMultiNetworkStateDirectory(
ctx,
logger.WithNamespace("migration-multi-network-state-dir"),
opts.EnvVars.StateDir,
); err != nil {
return nil, fmt.Errorf(
"migrating to multi-network state directory: %w", err,
)
}
h := new(toolkit.MkDirHelper)
networksStateDir, _ := h.Maybe(
opts.EnvVars.StateDir.MkChildDir("networks", true),
)
networksRuntimeDir, _ := h.Maybe(
opts.EnvVars.RuntimeDir.MkChildDir("networks", true),
)
if err := h.Err(); err != nil {
return nil, fmt.Errorf("creating networks sub-directories: %w", err)
}
return &loader{
envBinDirPath,
networksStateDir,
networksRuntimeDir,
}, nil
}
func (l *loader) Loadable(
ctx context.Context,
) (
[]bootstrap.CreationParams, error,
) {
networkStateDirs, err := l.networksStateDir.ChildDirs()
if err != nil {
return nil, fmt.Errorf(
"listing children of %q: %w", l.networksStateDir.Path, err,
)
}
creationParams := make([]bootstrap.CreationParams, 0, len(networkStateDirs))
for _, networkStateDir := range networkStateDirs {
thisCreationParams, err := loadCreationParams(networkStateDir)
if err != nil {
return nil, fmt.Errorf(
"loading creation params from %q: %w",
networkStateDir.Path,
err,
)
}
creationParams = append(creationParams, thisCreationParams)
}
return creationParams, nil
}
func (l *loader) Load(
ctx context.Context,
logger *mlog.Logger,
creationParams bootstrap.CreationParams,
opts *Opts,
) (
Network, error,
) {
networkID := creationParams.ID
networkStateDir, networkRuntimeDir, err := networkDirs(
l.networksStateDir, l.networksRuntimeDir, networkID, true,
)
if err != nil {
return nil, fmt.Errorf(
"creating sub-directories for network %q: %w", networkID, err,
)
}
return load(
ctx,
logger.WithNamespace("network"),
l.envBinDirPath,
networkStateDir,
networkRuntimeDir,
opts,
)
}
func (l *loader) Join(
ctx context.Context,
logger *mlog.Logger,
joiningBootstrap JoiningBootstrap,
opts *Opts,
) (
Network, error,
) {
var (
creationParams = joiningBootstrap.Bootstrap.NetworkCreationParams
networkID = creationParams.ID
)
networkStateDir, networkRuntimeDir, err := networkDirs(
l.networksStateDir, l.networksRuntimeDir, networkID, false,
)
if err != nil {
return nil, fmt.Errorf(
"creating sub-directories for network %q: %w", networkID, err,
)
}
return join(
ctx,
logger.WithNamespace("network"),
l.envBinDirPath,
joiningBootstrap,
networkStateDir,
networkRuntimeDir,
opts,
)
}
func (l *loader) Create(
ctx context.Context,
logger *mlog.Logger,
creationParams bootstrap.CreationParams,
ipNet nebula.IPNet,
hostName nebula.HostName,
opts *Opts,
) (
Network, error,
) {
networkID := creationParams.ID
networkStateDir, networkRuntimeDir, err := networkDirs(
l.networksStateDir, l.networksRuntimeDir, networkID, false,
)
if err != nil {
return nil, fmt.Errorf(
"creating sub-directories for network %q: %w", networkID, err,
)
}
return create(
ctx,
logger.WithNamespace("network"),
l.envBinDirPath,
networkStateDir,
networkRuntimeDir,
creationParams,
ipNet,
hostName,
opts,
)
}