Support configuring more than one network

This commit is contained in:
Brian Picciano 2024-09-10 22:51:33 +02:00
parent c022c97b19
commit df4eae8a5c
17 changed files with 280 additions and 206 deletions

View File

@ -1,4 +1,3 @@
# #
# This file defines all configuration directives which can be modified for # This file defines all configuration directives which can be modified for
# the isle daemon at runtime. All values specified here are the # the isle daemon at runtime. All values specified here are the
@ -6,80 +5,76 @@
# #
################################################################################ ################################################################################
# A DNS service runs as part of every isle process. # Configuration broken down by network. Each network can be identified by its
dns: # ID, its name, or its domain.
#networks:
# list of IPs that the DNS service will use to resolve requests outside the #id-or-name-or-domain:
# network's domain.
resolvers:
- 1.1.1.1
- 8.8.8.8
# A VPN service runs as part of every isle process. # A DNS service runs as part of every isle process.
vpn: #dns:
# Enable this field if the vpn will be made to be publicly accessible at a # list of IPs that the DNS service will use to resolve requests outside the
# particular IP or hostname. At least one host must have a publicly accessible # network's domain.
# VPN process at any given moment. #resolvers:
#public_addr: "host:port" # - 1.1.1.1
# - 8.8.8.8
# Firewall directives, as described here: # A VPN service runs as part of every isle process.
# https://github.com/slackhq/nebula/blob/v1.6.1/examples/config.yml#L260 #vpn:
firewall:
conntrack: # Enable this field if the vpn will be made to be publicly accessible at a
tcp_timeout: 12m # particular IP or hostname. At least one host must have a publicly accessible
udp_timeout: 3m # VPN process at any given moment.
default_timeout: 10m #public_addr: "host:port"
max_connections: 100000
outbound: # Firewall directives, as described here:
# https://github.com/slackhq/nebula/blob/v1.6.1/examples/config.yml#L260
#firewall:
# Allow all outbound traffic from this node. # Allow all outbound traffic from this node.
- port: any #outbound:
proto: any # - port: any
host: any # proto: any
# host: any
inbound: # Allow ICMP between hosts.
#inbound:
# - port: any
# proto: icmp
# host: any
#
# # If any storage allocations are declared below, the ports used will be
# # allowed here automatically.
# If any storage allocations are declared below, the ports used will be #tun:
# allowed here automatically. # Name of the tun network device which will route VPN traffic.
#device: isle-tun
# Allow ICMP between hosts. #storage:
- port: any
proto: icmp
host: any
# That's it. # Allocations defined here are used to store data in the distributed storage
# network. If no allocations are defined then no data is replicated to this
# node.
#
# Each allocation should have its own data/meta directories, separate from the
# other allocations.
#
# The data directory of each allocation should be on a different drive, while
# the meta directories can be anywhere (ideally on an SSD).
#
# Capacity declares how many gigabytes can be stored in each allocation, and
# is required.
#
# The ports are all _optional_, and will be automatically assigned if they are
# not specified. If ports any ports are specified then all should be
# specified, and each should be unique across all allocations.
#
#allocations:
tun: #- data_path: /foo/bar/data
# Name of the tun network device which will route VPN traffic. # meta_path: /foo/bar/meta
device: isle-tun # capacity: 1200
# #rpc_port: 3900
storage: # #s3_api_port: 3901
# #admin_port: 3902
# Allocations defined here are used to store data in the distributed storage
# network. If no allocations are defined then no data is replicated to this
# node.
#
# Each allocation should have its own data/meta directories, separate from the
# other allocations.
#
# The data directory of each allocation should be on a different drive, while
# the meta directories can be anywhere (ideally on an SSD).
#
# Capacity declares how many gigabytes can be stored in each allocation, and
# is required.
#
# The ports are all _optional_, and will be automatically assigned if they are
# not specified. If ports any ports are specified then all should be
# specified, and each should be unique across all allocations.
#
#allocations:
#- data_path: /foo/bar/data
# meta_path: /foo/bar/meta
# capacity: 1200
# #rpc_port: 3900
# #s3_api_port: 3901
# #admin_port: 3902

View File

@ -56,7 +56,7 @@ var subCmdDaemon = subCmd{
// required linux capabilities are set. // required linux capabilities are set.
// TODO check that the tun module is loaded (for nebula). // TODO check that the tun module is loaded (for nebula).
daemonConfig, err := daecommon.LoadConfig(envAppDirPath, *daemonConfigPath) daemonConfig, err := daecommon.LoadConfig(*daemonConfigPath)
if err != nil { if err != nil {
return fmt.Errorf("loading daemon config: %w", err) return fmt.Errorf("loading daemon config: %w", err)
} }

View File

@ -48,10 +48,10 @@ func (o *Opts) withDefaults() *Opts {
// - dnsmasq // - dnsmasq
// - garage (0 or more, depending on configured storage allocations) // - garage (0 or more, depending on configured storage allocations)
type Children struct { type Children struct {
logger *mlog.Logger logger *mlog.Logger
daemonConfig daecommon.Config networkConfig daecommon.NetworkConfig
runtimeDir toolkit.Dir runtimeDir toolkit.Dir
opts Opts opts Opts
pmux *pmuxlib.Pmux pmux *pmuxlib.Pmux
} }
@ -63,7 +63,7 @@ func New(
logger *mlog.Logger, logger *mlog.Logger,
binDirPath string, binDirPath string,
secretsStore secrets.Store, secretsStore secrets.Store,
daemonConfig daecommon.Config, networkConfig daecommon.NetworkConfig,
runtimeDir toolkit.Dir, runtimeDir toolkit.Dir,
garageAdminToken string, garageAdminToken string,
hostBootstrap bootstrap.Bootstrap, hostBootstrap bootstrap.Bootstrap,
@ -80,17 +80,17 @@ func New(
} }
c := &Children{ c := &Children{
logger: logger, logger: logger,
daemonConfig: daemonConfig, networkConfig: networkConfig,
runtimeDir: runtimeDir, runtimeDir: runtimeDir,
opts: *opts, opts: *opts,
} }
pmuxConfig, err := c.newPmuxConfig( pmuxConfig, err := c.newPmuxConfig(
ctx, ctx,
garageRPCSecret, garageRPCSecret,
binDirPath, binDirPath,
daemonConfig, networkConfig,
garageAdminToken, garageAdminToken,
hostBootstrap, hostBootstrap,
) )
@ -101,7 +101,7 @@ func New(
c.pmux = pmuxlib.NewPmux(pmuxConfig, c.opts.Stdout, c.opts.Stderr) c.pmux = pmuxlib.NewPmux(pmuxConfig, c.opts.Stdout, c.opts.Stderr)
initErr := c.postPmuxInit( initErr := c.postPmuxInit(
ctx, daemonConfig, garageAdminToken, hostBootstrap, ctx, networkConfig, garageAdminToken, hostBootstrap,
) )
if initErr != nil { if initErr != nil {
logger.Warn(ctx, "failed to initialize Children, shutting down child processes", err) logger.Warn(ctx, "failed to initialize Children, shutting down child processes", err)
@ -118,7 +118,7 @@ func New(
// successfully. // successfully.
func (c *Children) RestartDNSMasq(hostBootstrap bootstrap.Bootstrap) error { func (c *Children) RestartDNSMasq(hostBootstrap bootstrap.Bootstrap) error {
_, err := dnsmasqWriteConfig( _, err := dnsmasqWriteConfig(
c.runtimeDir.Path, c.daemonConfig, hostBootstrap, c.runtimeDir.Path, c.networkConfig, hostBootstrap,
) )
if err != nil { if err != nil {
return fmt.Errorf("writing new dnsmasq config: %w", err) return fmt.Errorf("writing new dnsmasq config: %w", err)
@ -134,7 +134,7 @@ func (c *Children) RestartDNSMasq(hostBootstrap bootstrap.Bootstrap) error {
// successfully. // successfully.
func (c *Children) RestartNebula(hostBootstrap bootstrap.Bootstrap) error { func (c *Children) RestartNebula(hostBootstrap bootstrap.Bootstrap) error {
_, err := nebulaWriteConfig( _, err := nebulaWriteConfig(
c.runtimeDir.Path, c.daemonConfig, hostBootstrap, c.runtimeDir.Path, c.networkConfig, hostBootstrap,
) )
if err != nil { if err != nil {
return fmt.Errorf("writing a new nebula config: %w", err) return fmt.Errorf("writing a new nebula config: %w", err)

View File

@ -18,14 +18,14 @@ type ReloadDiff struct {
// CalculateReloadDiff calculates a ReloadDiff based on an old and new // CalculateReloadDiff calculates a ReloadDiff based on an old and new
// bootstrap. // bootstrap.
func CalculateReloadDiff( func CalculateReloadDiff(
daemonConfig daecommon.Config, networkConfig daecommon.NetworkConfig,
prevBootstrap, nextBootstrap bootstrap.Bootstrap, prevBootstrap, nextBootstrap bootstrap.Bootstrap,
) ( ) (
diff ReloadDiff, err error, diff ReloadDiff, err error,
) { ) {
{ {
prevNebulaConfig, prevErr := nebulaConfig(daemonConfig, prevBootstrap) prevNebulaConfig, prevErr := nebulaConfig(networkConfig, prevBootstrap)
nextNebulaConfig, nextErr := nebulaConfig(daemonConfig, nextBootstrap) nextNebulaConfig, nextErr := nebulaConfig(networkConfig, nextBootstrap)
if err = errors.Join(prevErr, nextErr); err != nil { if err = errors.Join(prevErr, nextErr); err != nil {
err = fmt.Errorf("calculating nebula config: %w", err) err = fmt.Errorf("calculating nebula config: %w", err)
return return
@ -38,8 +38,8 @@ func CalculateReloadDiff(
{ {
diff.DNSChanged = !reflect.DeepEqual( diff.DNSChanged = !reflect.DeepEqual(
dnsmasqConfig(daemonConfig, prevBootstrap), dnsmasqConfig(networkConfig, prevBootstrap),
dnsmasqConfig(daemonConfig, nextBootstrap), dnsmasqConfig(networkConfig, nextBootstrap),
) )
} }

View File

@ -14,7 +14,7 @@ import (
) )
func dnsmasqConfig( func dnsmasqConfig(
daemonConfig daecommon.Config, hostBootstrap bootstrap.Bootstrap, networkConfig daecommon.NetworkConfig, hostBootstrap bootstrap.Bootstrap,
) dnsmasq.ConfData { ) dnsmasq.ConfData {
hostsSlice := make([]dnsmasq.ConfDataHost, 0, len(hostBootstrap.Hosts)) hostsSlice := make([]dnsmasq.ConfDataHost, 0, len(hostBootstrap.Hosts))
for _, host := range hostBootstrap.Hosts { for _, host := range hostBootstrap.Hosts {
@ -29,7 +29,7 @@ func dnsmasqConfig(
}) })
return dnsmasq.ConfData{ return dnsmasq.ConfData{
Resolvers: daemonConfig.DNS.Resolvers, Resolvers: networkConfig.DNS.Resolvers,
Domain: hostBootstrap.NetworkCreationParams.Domain, Domain: hostBootstrap.NetworkCreationParams.Domain,
IP: hostBootstrap.ThisHost().IP().String(), IP: hostBootstrap.ThisHost().IP().String(),
Hosts: hostsSlice, Hosts: hostsSlice,
@ -38,14 +38,14 @@ func dnsmasqConfig(
func dnsmasqWriteConfig( func dnsmasqWriteConfig(
runtimeDirPath string, runtimeDirPath string,
daemonConfig daecommon.Config, networkConfig daecommon.NetworkConfig,
hostBootstrap bootstrap.Bootstrap, hostBootstrap bootstrap.Bootstrap,
) ( ) (
string, error, string, error,
) { ) {
var ( var (
confPath = filepath.Join(runtimeDirPath, "dnsmasq.conf") confPath = filepath.Join(runtimeDirPath, "dnsmasq.conf")
confData = dnsmasqConfig(daemonConfig, hostBootstrap) confData = dnsmasqConfig(networkConfig, hostBootstrap)
) )
if err := dnsmasq.WriteConfFile(confPath, confData); err != nil { if err := dnsmasq.WriteConfFile(confPath, confData); err != nil {
@ -58,13 +58,13 @@ func dnsmasqWriteConfig(
func dnsmasqPmuxProcConfig( func dnsmasqPmuxProcConfig(
logger *mlog.Logger, logger *mlog.Logger,
runtimeDirPath, binDirPath string, runtimeDirPath, binDirPath string,
daemonConfig daecommon.Config, networkConfig daecommon.NetworkConfig,
hostBootstrap bootstrap.Bootstrap, hostBootstrap bootstrap.Bootstrap,
) ( ) (
pmuxlib.ProcessConfig, error, pmuxlib.ProcessConfig, error,
) { ) {
confPath, err := dnsmasqWriteConfig( confPath, err := dnsmasqWriteConfig(
runtimeDirPath, daemonConfig, hostBootstrap, runtimeDirPath, networkConfig, hostBootstrap,
) )
if err != nil { if err != nil {
return pmuxlib.ProcessConfig{}, fmt.Errorf( return pmuxlib.ProcessConfig{}, fmt.Errorf(

View File

@ -23,12 +23,12 @@ func garageAdminClientLogger(logger *mlog.Logger) *mlog.Logger {
func waitForGarage( func waitForGarage(
ctx context.Context, ctx context.Context,
logger *mlog.Logger, logger *mlog.Logger,
daemonConfig daecommon.Config, networkConfig daecommon.NetworkConfig,
adminToken string, adminToken string,
hostBootstrap bootstrap.Bootstrap, hostBootstrap bootstrap.Bootstrap,
) error { ) error {
allocs := daemonConfig.Storage.Allocations allocs := networkConfig.Storage.Allocations
// if this host doesn't have any allocations specified then fall back to // if this host doesn't have any allocations specified then fall back to
// waiting for nebula // waiting for nebula
@ -108,7 +108,7 @@ func garagePmuxProcConfigs(
ctx context.Context, ctx context.Context,
logger *mlog.Logger, logger *mlog.Logger,
rpcSecret, runtimeDirPath, binDirPath string, rpcSecret, runtimeDirPath, binDirPath string,
daemonConfig daecommon.Config, networkConfig daecommon.NetworkConfig,
adminToken string, adminToken string,
hostBootstrap bootstrap.Bootstrap, hostBootstrap bootstrap.Bootstrap,
) ( ) (
@ -116,7 +116,7 @@ func garagePmuxProcConfigs(
) { ) {
var ( var (
pmuxProcConfigs = map[string]pmuxlib.ProcessConfig{} pmuxProcConfigs = map[string]pmuxlib.ProcessConfig{}
allocs = daemonConfig.Storage.Allocations allocs = networkConfig.Storage.Allocations
) )
if len(allocs) > 0 && rpcSecret == "" { if len(allocs) > 0 && rpcSecret == "" {

View File

@ -48,7 +48,7 @@ func waitForNebula(
} }
func nebulaConfig( func nebulaConfig(
daemonConfig daecommon.Config, networkConfig daecommon.NetworkConfig,
hostBootstrap bootstrap.Bootstrap, hostBootstrap bootstrap.Bootstrap,
) ( ) (
map[string]any, error, map[string]any, error,
@ -95,12 +95,12 @@ func nebulaConfig(
"respond": true, "respond": true,
}, },
"tun": map[string]any{ "tun": map[string]any{
"dev": daemonConfig.VPN.Tun.Device, "dev": networkConfig.VPN.Tun.Device,
}, },
"firewall": daemonConfig.VPN.Firewall, "firewall": networkConfig.VPN.Firewall,
} }
if publicAddr := daemonConfig.VPN.PublicAddr; publicAddr == "" { if publicAddr := networkConfig.VPN.PublicAddr; publicAddr == "" {
config["listen"] = map[string]string{ config["listen"] = map[string]string{
"host": "0.0.0.0", "host": "0.0.0.0",
@ -137,12 +137,12 @@ func nebulaConfig(
func nebulaWriteConfig( func nebulaWriteConfig(
runtimeDirPath string, runtimeDirPath string,
daemonConfig daecommon.Config, networkConfig daecommon.NetworkConfig,
hostBootstrap bootstrap.Bootstrap, hostBootstrap bootstrap.Bootstrap,
) ( ) (
string, error, string, error,
) { ) {
config, err := nebulaConfig(daemonConfig, hostBootstrap) config, err := nebulaConfig(networkConfig, hostBootstrap)
if err != nil { if err != nil {
return "", fmt.Errorf("creating nebula config: %w", err) return "", fmt.Errorf("creating nebula config: %w", err)
} }
@ -158,12 +158,12 @@ func nebulaWriteConfig(
func nebulaPmuxProcConfig( func nebulaPmuxProcConfig(
runtimeDirPath, binDirPath string, runtimeDirPath, binDirPath string,
daemonConfig daecommon.Config, networkConfig daecommon.NetworkConfig,
hostBootstrap bootstrap.Bootstrap, hostBootstrap bootstrap.Bootstrap,
) ( ) (
pmuxlib.ProcessConfig, error, pmuxlib.ProcessConfig, error,
) { ) {
config, err := nebulaConfig(daemonConfig, hostBootstrap) config, err := nebulaConfig(networkConfig, hostBootstrap)
if err != nil { if err != nil {
return pmuxlib.ProcessConfig{}, fmt.Errorf( return pmuxlib.ProcessConfig{}, fmt.Errorf(
"creating nebula config: %w", err, "creating nebula config: %w", err,

View File

@ -12,7 +12,7 @@ import (
func (c *Children) newPmuxConfig( func (c *Children) newPmuxConfig(
ctx context.Context, ctx context.Context,
garageRPCSecret, binDirPath string, garageRPCSecret, binDirPath string,
daemonConfig daecommon.Config, networkConfig daecommon.NetworkConfig,
garageAdminToken string, garageAdminToken string,
hostBootstrap bootstrap.Bootstrap, hostBootstrap bootstrap.Bootstrap,
) ( ) (
@ -21,7 +21,7 @@ func (c *Children) newPmuxConfig(
nebulaPmuxProcConfig, err := nebulaPmuxProcConfig( nebulaPmuxProcConfig, err := nebulaPmuxProcConfig(
c.runtimeDir.Path, c.runtimeDir.Path,
binDirPath, binDirPath,
daemonConfig, networkConfig,
hostBootstrap, hostBootstrap,
) )
if err != nil { if err != nil {
@ -32,7 +32,7 @@ func (c *Children) newPmuxConfig(
c.logger, c.logger,
c.runtimeDir.Path, c.runtimeDir.Path,
binDirPath, binDirPath,
daemonConfig, networkConfig,
hostBootstrap, hostBootstrap,
) )
if err != nil { if err != nil {
@ -47,7 +47,7 @@ func (c *Children) newPmuxConfig(
garageRPCSecret, garageRPCSecret,
c.runtimeDir.Path, c.runtimeDir.Path,
binDirPath, binDirPath,
daemonConfig, networkConfig,
garageAdminToken, garageAdminToken,
hostBootstrap, hostBootstrap,
) )
@ -68,7 +68,7 @@ func (c *Children) newPmuxConfig(
func (c *Children) postPmuxInit( func (c *Children) postPmuxInit(
ctx context.Context, ctx context.Context,
daemonConfig daecommon.Config, networkConfig daecommon.NetworkConfig,
garageAdminToken string, garageAdminToken string,
hostBootstrap bootstrap.Bootstrap, hostBootstrap bootstrap.Bootstrap,
) error { ) error {
@ -79,7 +79,7 @@ func (c *Children) postPmuxInit(
c.logger.Info(ctx, "Waiting for garage instances to come online") c.logger.Info(ctx, "Waiting for garage instances to come online")
err := waitForGarage( err := waitForGarage(
ctx, c.logger, daemonConfig, garageAdminToken, hostBootstrap, ctx, c.logger, networkConfig, garageAdminToken, hostBootstrap,
) )
if err != nil { if err != nil {
return fmt.Errorf("waiting for garage to start: %w", err) return fmt.Errorf("waiting for garage to start: %w", err)

View File

@ -4,6 +4,8 @@ import (
"errors" "errors"
"fmt" "fmt"
"io/fs" "io/fs"
"isle/bootstrap"
"isle/daemon/daecommon"
"os" "os"
"path/filepath" "path/filepath"
"slices" "slices"
@ -40,6 +42,33 @@ var HTTPSocketPath = sync.OnceValue(func() string {
) )
}) })
func pickNetworkConfig(
daemonConfig daecommon.Config, creationParams bootstrap.CreationParams,
) (
daecommon.NetworkConfig, bool,
) {
if c, ok := daemonConfig.Networks[creationParams.ID]; ok {
return c, true
}
if c, ok := daemonConfig.Networks[creationParams.Name]; ok {
return c, true
}
if c, ok := daemonConfig.Networks[creationParams.Domain]; ok {
return c, true
}
{ // DEPRECATED
c, ok := daemonConfig.Networks[daecommon.DeprecatedNetworkID]
if len(daemonConfig.Networks) == 1 && ok {
return c, true
}
}
return daecommon.NetworkConfig{}, false
}
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
// Jigs // Jigs

View File

@ -4,15 +4,20 @@ import (
"fmt" "fmt"
"io" "io"
"isle/bootstrap" "isle/bootstrap"
"isle/yamlutil" "isle/toolkit"
"os" "os"
"path/filepath" "path/filepath"
"strconv" "strconv"
"github.com/imdario/mergo"
"gopkg.in/yaml.v3" "gopkg.in/yaml.v3"
) )
const (
// Network ID used when translating from the old single-network daemon
// config to the multi-network config.
DeprecatedNetworkID = "_" // DEPRECATED
)
func defaultConfigPath(appDirPath string) string { func defaultConfigPath(appDirPath string) string {
return filepath.Join(appDirPath, "etc", "daemon.yml") return filepath.Join(appDirPath, "etc", "daemon.yml")
} }
@ -22,16 +27,8 @@ type ConfigTun struct {
} }
type ConfigFirewall struct { type ConfigFirewall struct {
Conntrack ConfigConntrack `yaml:"conntrack"` Outbound []ConfigFirewallRule `yaml:"outbound"`
Outbound []ConfigFirewallRule `yaml:"outbound"` Inbound []ConfigFirewallRule `yaml:"inbound"`
Inbound []ConfigFirewallRule `yaml:"inbound"`
}
type ConfigConntrack struct {
TCPTimeout string `yaml:"tcp_timeout"`
UDPTimeout string `yaml:"udp_timeout"`
DefaultTimeout string `yaml:"default_timeout"`
MaxConnections int `yaml:"max_connections"`
} }
type ConfigFirewallRule struct { type ConfigFirewallRule struct {
@ -61,8 +58,8 @@ type ConfigStorageAllocation struct {
Zone string `yaml:"zone"` Zone string `yaml:"zone"`
} }
// Config describes the structure of the daemon config file. // NetworkConfig describes the configuration of a single network.
type Config struct { type NetworkConfig struct {
DNS struct { DNS struct {
Resolvers []string `yaml:"resolvers"` Resolvers []string `yaml:"resolvers"`
} `yaml:"dns"` } `yaml:"dns"`
@ -76,7 +73,37 @@ type Config struct {
} `yaml:"storage"` } `yaml:"storage"`
} }
func (c *Config) fillDefaults() { func (c *NetworkConfig) fillDefaults() {
if c.DNS.Resolvers == nil {
c.DNS.Resolvers = []string{
"1.1.1.1",
"8.8.8.8",
}
}
if c.VPN.Firewall.Outbound == nil {
c.VPN.Firewall.Outbound = []ConfigFirewallRule{
{
Port: "any",
Proto: "any",
Host: "any",
},
}
}
if c.VPN.Firewall.Inbound == nil {
c.VPN.Firewall.Inbound = []ConfigFirewallRule{
{
Port: "any",
Proto: "icmp",
Host: "any",
},
}
}
if c.VPN.Tun.Device == "" {
c.VPN.Tun.Device = "isle-tun"
}
var firewallGarageInbound []ConfigFirewallRule var firewallGarageInbound []ConfigFirewallRule
@ -116,6 +143,11 @@ func (c *Config) fillDefaults() {
) )
} }
// Config describes the structure of the daemon config file.
type Config struct {
Networks map[string]NetworkConfig `yaml:"networks"`
}
// CopyDefaultConfig copies the daemon config file embedded in the AppDir into // CopyDefaultConfig copies the daemon config file embedded in the AppDir into
// the given io.Writer. // the given io.Writer.
func CopyDefaultConfig(into io.Writer, appDirPath string) error { func CopyDefaultConfig(into io.Writer, appDirPath string) error {
@ -136,49 +168,42 @@ func CopyDefaultConfig(into io.Writer, appDirPath string) error {
return nil return nil
} }
// LoadConfig loads the daemon config from userConfigPath, merges it with // LoadConfig loads the daemon config from userConfigPath.
// the default found in the appDirPath, and returns the result.
// //
// If userConfigPath is not given then the default is loaded and returned. // If userConfigPath is not given then the default is loaded and returned.
func LoadConfig( func LoadConfig(userConfigPath string) (Config, error) {
appDirPath, userConfigPath string, if userConfigPath == "" {
) ( return Config{}, nil
Config, error,
) {
defaultConfigPath := defaultConfigPath(appDirPath)
var fullDaemon map[string]interface{}
if err := yamlutil.LoadYamlFile(&fullDaemon, defaultConfigPath); err != nil {
return Config{}, fmt.Errorf("parsing default daemon config file: %w", err)
} }
if userConfigPath != "" { userConfigB, err := os.ReadFile(userConfigPath)
var daemonConfig map[string]interface{}
if err := yamlutil.LoadYamlFile(&daemonConfig, userConfigPath); err != nil {
return Config{}, fmt.Errorf("parsing %q: %w", userConfigPath, err)
}
err := mergo.Merge(&fullDaemon, daemonConfig, mergo.WithOverride)
if err != nil {
return Config{}, fmt.Errorf("merging contents of file %q: %w", userConfigPath, err)
}
}
fullDaemonB, err := yaml.Marshal(fullDaemon)
if err != nil { if err != nil {
return Config{}, fmt.Errorf("yaml marshaling: %w", err) return Config{}, fmt.Errorf("reading from file: %w", err)
}
{ // DEPRECATED
var config NetworkConfig
_ = yaml.Unmarshal(userConfigB, &config)
if !toolkit.IsZero(config) {
config.fillDefaults()
return Config{
Networks: map[string]NetworkConfig{
DeprecatedNetworkID: config,
},
}, nil
}
} }
var config Config var config Config
if err := yaml.Unmarshal(fullDaemonB, &config); err != nil { if err := yaml.Unmarshal(userConfigB, &config); err != nil {
return Config{}, fmt.Errorf("yaml unmarshaling back into Config struct: %w", err) return Config{}, fmt.Errorf("yaml unmarshaling back into Config struct: %w", err)
} }
config.fillDefaults() for id := range config.Networks {
network := config.Networks[id]
network.fillDefaults()
config.Networks[id] = network
}
return config, nil return config, nil
} }

View File

@ -4,6 +4,7 @@ package daemon
import ( import (
"context" "context"
"errors"
"fmt" "fmt"
"isle/bootstrap" "isle/bootstrap"
"isle/daemon/children" "isle/daemon/children"
@ -134,11 +135,13 @@ func New(
) )
} }
networkConfig, _ := pickNetworkConfig(daemonConfig, loadableNetworks[0])
d.network, err = network.Load( d.network, err = network.Load(
ctx, ctx,
logger.WithNamespace("network"), logger.WithNamespace("network"),
id, id,
d.daemonConfig, networkConfig,
d.envBinDirPath, d.envBinDirPath,
networkStateDir, networkStateDir,
networkRuntimeDir, networkRuntimeDir,
@ -174,6 +177,13 @@ func (d *Daemon) CreateNetwork(
creationParams := bootstrap.NewCreationParams(name, domain) creationParams := bootstrap.NewCreationParams(name, domain)
ctx = mctx.WithAnnotator(ctx, creationParams) ctx = mctx.WithAnnotator(ctx, creationParams)
networkConfig, ok := pickNetworkConfig(
d.daemonConfig, creationParams,
)
if !ok {
return errors.New("couldn't find network config for network being created")
}
d.l.Lock() d.l.Lock()
defer d.l.Unlock() defer d.l.Unlock()
@ -196,7 +206,7 @@ func (d *Daemon) CreateNetwork(
n, err := network.Create( n, err := network.Create(
ctx, ctx,
d.logger.WithNamespace("network"), d.logger.WithNamespace("network"),
d.daemonConfig, networkConfig,
d.envBinDirPath, d.envBinDirPath,
networkStateDir, networkStateDir,
networkRuntimeDir, networkRuntimeDir,
@ -224,7 +234,12 @@ func (d *Daemon) CreateNetwork(
func (d *Daemon) JoinNetwork( func (d *Daemon) JoinNetwork(
ctx context.Context, newBootstrap network.JoiningBootstrap, ctx context.Context, newBootstrap network.JoiningBootstrap,
) error { ) error {
networkID := newBootstrap.Bootstrap.NetworkCreationParams.ID var (
creationParams = newBootstrap.Bootstrap.NetworkCreationParams
networkConfig, _ = pickNetworkConfig(d.daemonConfig, creationParams)
networkID = creationParams.ID
)
ctx = mctx.WithAnnotator(ctx, newBootstrap.Bootstrap.NetworkCreationParams) ctx = mctx.WithAnnotator(ctx, newBootstrap.Bootstrap.NetworkCreationParams)
d.l.Lock() d.l.Lock()
@ -247,7 +262,7 @@ func (d *Daemon) JoinNetwork(
n, err := network.Join( n, err := network.Join(
ctx, ctx,
d.logger.WithNamespace("network"), d.logger.WithNamespace("network"),
d.daemonConfig, networkConfig,
newBootstrap, newBootstrap,
d.envBinDirPath, d.envBinDirPath,
networkStateDir, networkStateDir,

View File

@ -14,6 +14,7 @@ import (
"dev.mediocregopher.com/mediocre-go-lib.git/mlog" "dev.mediocregopher.com/mediocre-go-lib.git/mlog"
) )
// DEPRECATED
func migrateToMultiNetworkStateDirectory( func migrateToMultiNetworkStateDirectory(
ctx context.Context, logger *mlog.Logger, envVars daecommon.EnvVars, ctx context.Context, logger *mlog.Logger, envVars daecommon.EnvVars,
) error { ) error {

View File

@ -29,8 +29,8 @@ func writeBootstrapToStateDir(
return nil return nil
} }
func coalesceDaemonConfigAndBootstrap( func coalesceNetworkConfigAndBootstrap(
daemonConfig daecommon.Config, hostBootstrap bootstrap.Bootstrap, networkConfig daecommon.NetworkConfig, hostBootstrap bootstrap.Bootstrap,
) ( ) (
bootstrap.Bootstrap, error, bootstrap.Bootstrap, error,
) { ) {
@ -38,12 +38,12 @@ func coalesceDaemonConfigAndBootstrap(
HostAssigned: hostBootstrap.HostAssigned, HostAssigned: hostBootstrap.HostAssigned,
HostConfigured: bootstrap.HostConfigured{ HostConfigured: bootstrap.HostConfigured{
Nebula: bootstrap.NebulaHost{ Nebula: bootstrap.NebulaHost{
PublicAddr: daemonConfig.VPN.PublicAddr, PublicAddr: networkConfig.VPN.PublicAddr,
}, },
}, },
} }
if allocs := daemonConfig.Storage.Allocations; len(allocs) > 0 { if allocs := networkConfig.Storage.Allocations; len(allocs) > 0 {
for i, alloc := range allocs { for i, alloc := range allocs {

View File

@ -57,7 +57,7 @@ func garageAdminClientLogger(logger *mlog.Logger) *mlog.Logger {
// or it will _panic_ if there is no local instance configured. // or it will _panic_ if there is no local instance configured.
func newGarageAdminClient( func newGarageAdminClient(
logger *mlog.Logger, logger *mlog.Logger,
daemonConfig daecommon.Config, networkConfig daecommon.NetworkConfig,
adminToken string, adminToken string,
hostBootstrap bootstrap.Bootstrap, hostBootstrap bootstrap.Bootstrap,
) *garage.AdminClient { ) *garage.AdminClient {
@ -68,7 +68,7 @@ func newGarageAdminClient(
garageAdminClientLogger(logger), garageAdminClientLogger(logger),
net.JoinHostPort( net.JoinHostPort(
thisHost.IP().String(), thisHost.IP().String(),
strconv.Itoa(daemonConfig.Storage.Allocations[0].AdminPort), strconv.Itoa(networkConfig.Storage.Allocations[0].AdminPort),
), ),
adminToken, adminToken,
) )
@ -77,18 +77,18 @@ func newGarageAdminClient(
func garageApplyLayout( func garageApplyLayout(
ctx context.Context, ctx context.Context,
logger *mlog.Logger, logger *mlog.Logger,
daemonConfig daecommon.Config, networkConfig daecommon.NetworkConfig,
adminToken string, adminToken string,
hostBootstrap bootstrap.Bootstrap, hostBootstrap bootstrap.Bootstrap,
) error { ) error {
var ( var (
adminClient = newGarageAdminClient( adminClient = newGarageAdminClient(
logger, daemonConfig, adminToken, hostBootstrap, logger, networkConfig, adminToken, hostBootstrap,
) )
thisHost = hostBootstrap.ThisHost() thisHost = hostBootstrap.ThisHost()
hostName = thisHost.Name hostName = thisHost.Name
allocs = daemonConfig.Storage.Allocations allocs = networkConfig.Storage.Allocations
peers = make([]garage.PeerLayout, len(allocs)) peers = make([]garage.PeerLayout, len(allocs))
) )
@ -115,14 +115,14 @@ func garageApplyLayout(
func garageInitializeGlobalBucket( func garageInitializeGlobalBucket(
ctx context.Context, ctx context.Context,
logger *mlog.Logger, logger *mlog.Logger,
daemonConfig daecommon.Config, networkConfig daecommon.NetworkConfig,
adminToken string, adminToken string,
hostBootstrap bootstrap.Bootstrap, hostBootstrap bootstrap.Bootstrap,
) ( ) (
garage.S3APICredentials, error, garage.S3APICredentials, error,
) { ) {
adminClient := newGarageAdminClient( adminClient := newGarageAdminClient(
logger, daemonConfig, adminToken, hostBootstrap, logger, networkConfig, adminToken, hostBootstrap,
) )
creds, err := adminClient.CreateS3APICredentials( creds, err := adminClient.CreateS3APICredentials(

View File

@ -18,7 +18,6 @@ import (
"isle/nebula" "isle/nebula"
"isle/secrets" "isle/secrets"
"isle/toolkit" "isle/toolkit"
"log"
"net/netip" "net/netip"
"slices" "slices"
"sync" "sync"
@ -153,8 +152,8 @@ func (o *Opts) withDefaults() *Opts {
} }
type network struct { type network struct {
logger *mlog.Logger logger *mlog.Logger
daemonConfig daecommon.Config networkConfig daecommon.NetworkConfig
envBinDirPath string envBinDirPath string
stateDir toolkit.Dir stateDir toolkit.Dir
@ -178,16 +177,15 @@ type network struct {
func instatiateNetwork( func instatiateNetwork(
logger *mlog.Logger, logger *mlog.Logger,
networkID string, networkID string,
daemonConfig daecommon.Config, networkConfig daecommon.NetworkConfig,
envBinDirPath string, envBinDirPath string,
stateDir toolkit.Dir, stateDir toolkit.Dir,
runtimeDir toolkit.Dir, runtimeDir toolkit.Dir,
opts *Opts, opts *Opts,
) *network { ) *network {
log.Printf("DEBUG: network stateDir:%+v runtimeDir:%+v", stateDir, runtimeDir)
return &network{ return &network{
logger: logger, logger: logger,
daemonConfig: daemonConfig, networkConfig: networkConfig,
envBinDirPath: envBinDirPath, envBinDirPath: envBinDirPath,
stateDir: stateDir, stateDir: stateDir,
runtimeDir: runtimeDir, runtimeDir: runtimeDir,
@ -227,7 +225,7 @@ func Load(
ctx context.Context, ctx context.Context,
logger *mlog.Logger, logger *mlog.Logger,
networkID string, networkID string,
daemonConfig daecommon.Config, networkConfig daecommon.NetworkConfig,
envBinDirPath string, envBinDirPath string,
stateDir toolkit.Dir, stateDir toolkit.Dir,
runtimeDir toolkit.Dir, runtimeDir toolkit.Dir,
@ -238,7 +236,7 @@ func Load(
n := instatiateNetwork( n := instatiateNetwork(
logger, logger,
networkID, networkID,
daemonConfig, networkConfig,
envBinDirPath, envBinDirPath,
stateDir, stateDir,
runtimeDir, runtimeDir,
@ -272,7 +270,7 @@ func Load(
func Join( func Join(
ctx context.Context, ctx context.Context,
logger *mlog.Logger, logger *mlog.Logger,
daemonConfig daecommon.Config, networkConfig daecommon.NetworkConfig,
joiningBootstrap JoiningBootstrap, joiningBootstrap JoiningBootstrap,
envBinDirPath string, envBinDirPath string,
stateDir toolkit.Dir, stateDir toolkit.Dir,
@ -284,7 +282,7 @@ func Join(
n := instatiateNetwork( n := instatiateNetwork(
logger, logger,
joiningBootstrap.Bootstrap.NetworkCreationParams.ID, joiningBootstrap.Bootstrap.NetworkCreationParams.ID,
daemonConfig, networkConfig,
envBinDirPath, envBinDirPath,
stateDir, stateDir,
runtimeDir, runtimeDir,
@ -324,7 +322,7 @@ func Join(
func Create( func Create(
ctx context.Context, ctx context.Context,
logger *mlog.Logger, logger *mlog.Logger,
daemonConfig daecommon.Config, networkConfig daecommon.NetworkConfig,
envBinDirPath string, envBinDirPath string,
stateDir toolkit.Dir, stateDir toolkit.Dir,
runtimeDir toolkit.Dir, runtimeDir toolkit.Dir,
@ -335,7 +333,7 @@ func Create(
) ( ) (
Network, error, Network, error,
) { ) {
if len(daemonConfig.Storage.Allocations) < 3 { if len(networkConfig.Storage.Allocations) < 3 {
return nil, ErrInvalidConfig.WithData( return nil, ErrInvalidConfig.WithData(
"At least three storage allocations are required.", "At least three storage allocations are required.",
) )
@ -351,7 +349,7 @@ func Create(
n := instatiateNetwork( n := instatiateNetwork(
logger, logger,
creationParams.ID, creationParams.ID,
daemonConfig, networkConfig,
envBinDirPath, envBinDirPath,
stateDir, stateDir,
runtimeDir, runtimeDir,
@ -409,8 +407,8 @@ func (n *network) initialize(
// by the daemon config. This way the network has the most up-to-date // by the daemon config. This way the network has the most up-to-date
// possible bootstrap. This updated bootstrap will later get updated in // possible bootstrap. This updated bootstrap will later get updated in
// garage as a background task, so other hosts will see it as well. // garage as a background task, so other hosts will see it as well.
currBootstrap, err := coalesceDaemonConfigAndBootstrap( currBootstrap, err := coalesceNetworkConfigAndBootstrap(
n.daemonConfig, currBootstrap, n.networkConfig, currBootstrap,
) )
if err != nil { if err != nil {
return fmt.Errorf("combining configuration into bootstrap: %w", err) return fmt.Errorf("combining configuration into bootstrap: %w", err)
@ -429,7 +427,7 @@ func (n *network) initialize(
n.logger.WithNamespace("children"), n.logger.WithNamespace("children"),
n.envBinDirPath, n.envBinDirPath,
n.secretsStore, n.secretsStore,
n.daemonConfig, n.networkConfig,
n.runtimeDir, n.runtimeDir,
n.garageAdminToken, n.garageAdminToken,
currBootstrap, currBootstrap,
@ -467,10 +465,10 @@ func (n *network) initialize(
} }
func (n *network) postInit(ctx context.Context) error { func (n *network) postInit(ctx context.Context) error {
if len(n.daemonConfig.Storage.Allocations) > 0 { if len(n.networkConfig.Storage.Allocations) > 0 {
n.logger.Info(ctx, "Applying garage layout") n.logger.Info(ctx, "Applying garage layout")
if err := garageApplyLayout( if err := garageApplyLayout(
ctx, n.logger, n.daemonConfig, n.garageAdminToken, n.currBootstrap, ctx, n.logger, n.networkConfig, n.garageAdminToken, n.currBootstrap,
); err != nil { ); err != nil {
return fmt.Errorf("applying garage layout: %w", err) return fmt.Errorf("applying garage layout: %w", err)
} }
@ -489,7 +487,7 @@ func (n *network) postInit(ctx context.Context) error {
garageGlobalBucketCreds, err := garageInitializeGlobalBucket( garageGlobalBucketCreds, err := garageInitializeGlobalBucket(
ctx, ctx,
n.logger, n.logger,
n.daemonConfig, n.networkConfig,
n.garageAdminToken, n.garageAdminToken,
n.currBootstrap, n.currBootstrap,
) )
@ -568,7 +566,7 @@ func (n *network) reload(
newBootstrap.Hosts[thisHost.Name] = thisHost newBootstrap.Hosts[thisHost.Name] = thisHost
diff, err := children.CalculateReloadDiff( diff, err := children.CalculateReloadDiff(
n.daemonConfig, currBootstrap, newBootstrap, n.networkConfig, currBootstrap, newBootstrap,
) )
if err != nil { if err != nil {
return fmt.Errorf("calculating diff between bootstraps: %w", err) return fmt.Errorf("calculating diff between bootstraps: %w", err)

View File

@ -1,3 +1,12 @@
// Package toolkit contains useful utilities which are not specific to any // Package toolkit contains useful utilities which are not specific to any
// specific part of isle. // specific part of isle.
package toolkit package toolkit
import "reflect"
// IsZero returns true if the value is equal to its zero value according to
// reflect.DeepEqual.
func IsZero[T any](v T) bool {
var zero T
return reflect.DeepEqual(v, zero)
}

View File

@ -33,21 +33,23 @@ if [ ! -d "$XDG_RUNTIME_DIR/isle" ]; then
mkdir c mkdir c
cat >daemon.yml <<EOF cat >daemon.yml <<EOF
vpn: networks:
public_addr: 127.0.0.1:60000 testing:
tun: vpn:
device: isle-primus public_addr: 127.0.0.1:60000
storage: tun:
allocations: device: isle-primus
- data_path: a/data storage:
meta_path: a/meta allocations:
capacity: 1 - data_path: a/data
- data_path: b/data meta_path: a/meta
meta_path: b/meta capacity: 1
capacity: 1 - data_path: b/data
- data_path: c/data meta_path: b/meta
meta_path: c/meta capacity: 1
capacity: 1 - data_path: c/data
meta_path: c/meta
capacity: 1
EOF EOF
isle daemon -l debug --config-path daemon.yml >daemon.log 2>&1 & isle daemon -l debug --config-path daemon.yml >daemon.log 2>&1 &