//go:generate mockery --name Loader --inpackage --filename loader_mock.go package network import ( "context" "fmt" "isle/bootstrap" "isle/daemon/children" "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 nebulaDeviceNamer *children.NebulaDeviceNamer } // 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, children.NewNebulaDeviceNamer(), }, 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, l.envBinDirPath, l.nebulaDeviceNamer, 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, l.envBinDirPath, l.nebulaDeviceNamer, 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, l.envBinDirPath, l.nebulaDeviceNamer, networkStateDir, networkRuntimeDir, creationParams, ipNet, hostName, opts, ) }