diff --git a/entrypoint/src/cmd/entrypoint/admin.go b/entrypoint/src/cmd/entrypoint/admin.go index 7e7a4f0..3ab05aa 100644 --- a/entrypoint/src/cmd/entrypoint/admin.go +++ b/entrypoint/src/cmd/entrypoint/admin.go @@ -5,6 +5,7 @@ import ( crypticnet "cryptic-net" "cryptic-net/admin" "cryptic-net/bootstrap" + "cryptic-net/daemon" "cryptic-net/garage" "cryptic-net/nebula" "crypto/rand" @@ -54,7 +55,7 @@ var subCmdAdminCreateNetwork = subCmd{ flags := subCmdCtx.flagSet(false) - daemonYmlPath := flags.StringP( + daemonConfigPath := flags.StringP( "config-path", "c", "", "Optional path to a daemon.yml file to load configuration from.", ) @@ -86,7 +87,7 @@ var subCmdAdminCreateNetwork = subCmd{ env := subCmdCtx.env if *dumpConfig { - return writeBuiltinDaemonYml(env, os.Stdout) + return daemon.CopyDefaultConfig(os.Stdout, env.AppDirPath) } if *domain == "" || *subnetStr == "" || *hostName == "" { @@ -128,14 +129,13 @@ var subCmdAdminCreateNetwork = subCmd{ }() } - if err := writeMergedDaemonYml(env, *daemonYmlPath); err != nil { - return fmt.Errorf("merging and writing daemon.yml file: %w", err) + daemonConfig, err := daemon.LoadConfig(env.AppDirPath, *daemonConfigPath) + if err != nil { + return fmt.Errorf("loading daemon config: %w", err) } - daemon := env.ThisDaemon() - - if len(daemon.Storage.Allocations) < 3 { - return fmt.Errorf("daemon.yml with at least 3 allocations was not provided") + if len(daemonConfig.Storage.Allocations) < 3 { + return fmt.Errorf("daemon config with at least 3 allocations was not provided") } nebulaCACert, err := nebula.NewCACert(*domain, subnet) @@ -165,16 +165,16 @@ var subCmdAdminCreateNetwork = subCmd{ GarageGlobalBucketS3APICredentials: garage.NewS3APICredentials(), } - if env, err = mergeDaemonIntoBootstrap(env); err != nil { - return fmt.Errorf("merging daemon.yml into bootstrap data: %w", err) + if env, err = mergeDaemonConfigIntoBootstrap(env, daemonConfig); err != nil { + return fmt.Errorf("merging daemon config into bootstrap data: %w", err) } - nebulaPmuxProcConfig, err := nebulaPmuxProcConfig(env) + nebulaPmuxProcConfig, err := nebulaPmuxProcConfig(env, daemonConfig) if err != nil { return fmt.Errorf("generating nebula config: %w", err) } - garagePmuxProcConfigs, err := garagePmuxProcConfigs(env) + garagePmuxProcConfigs, err := garagePmuxProcConfigs(env, daemonConfig) if err != nil { return fmt.Errorf("generating garage configs: %w", err) } @@ -206,17 +206,17 @@ var subCmdAdminCreateNetwork = subCmd{ }() fmt.Fprintln(os.Stderr, "waiting for garage instances to come online") - if err := waitForGarageAndNebula(ctx, env); err != nil { + if err := waitForGarageAndNebula(ctx, env, daemonConfig); err != nil { return fmt.Errorf("waiting for garage to start up: %w", err) } fmt.Fprintln(os.Stderr, "applying initial garage layout") - if err := garageApplyLayout(ctx, env); err != nil { + if err := garageApplyLayout(ctx, env, daemonConfig); err != nil { return fmt.Errorf("applying initial garage layout: %w", err) } fmt.Fprintln(os.Stderr, "initializing garage shared global bucket") - err = garageInitializeGlobalBucket(ctx, env) + err = garageInitializeGlobalBucket(ctx, env, daemonConfig) if cErr := (garage.AdminClientError{}); errors.As(err, &cErr) && cErr.StatusCode == 409 { return fmt.Errorf("shared global bucket has already been created, are the storage allocations from a previously initialized cryptic-net being used?") diff --git a/entrypoint/src/cmd/entrypoint/daemon.go b/entrypoint/src/cmd/entrypoint/daemon.go index 38d7e0f..c6f7b62 100644 --- a/entrypoint/src/cmd/entrypoint/daemon.go +++ b/entrypoint/src/cmd/entrypoint/daemon.go @@ -11,6 +11,7 @@ import ( crypticnet "cryptic-net" "cryptic-net/bootstrap" + "cryptic-net/daemon" "cryptic-net/garage" "code.betamike.com/cryptic-io/pmux/pmuxlib" @@ -25,11 +26,8 @@ import ( // * Creates the data directory and copies the appdir bootstrap file into there, // if it's not already there. // -// * Merges the user-provided daemon.yml file with the default, and writes the -// result to the runtime dir. -// -// * Merges daemon.yml configuration into the bootstrap configuration, and -// rewrites the bootstrap file. +// * Merges daemon configuration into the bootstrap configuration, and rewrites +// the bootstrap file. // // * Sets up environment variables that all other sub-processes then use, based // on the runtime dir. @@ -78,10 +76,13 @@ func reloadBootstrap(env crypticnet.Env, s3Client garage.S3APIClient) (crypticne // been canceled or bootstrap info has been changed. This will always block // until the spawned pmux has returned, and returns a copy of Env with updated // boostrap info. -func runDaemonPmuxOnce(env crypticnet.Env) (crypticnet.Env, error) { +func runDaemonPmuxOnce( + env crypticnet.Env, daemonConfig daemon.Config, +) ( + crypticnet.Env, error, +) { thisHost := env.Bootstrap.ThisHost() - thisDaemon := env.ThisDaemon() fmt.Fprintf(os.Stderr, "host name is %q, ip is %q\n", thisHost.Name, thisHost.Nebula.IP) // create s3Client anew on every loop, in case the topology has @@ -89,33 +90,31 @@ func runDaemonPmuxOnce(env crypticnet.Env) (crypticnet.Env, error) { // endpoint. s3Client := env.Bootstrap.GlobalBucketS3APIClient() - nebulaPmuxProcConfig, err := nebulaPmuxProcConfig(env) + nebulaPmuxProcConfig, err := nebulaPmuxProcConfig(env, daemonConfig) if err != nil { return crypticnet.Env{}, fmt.Errorf("generating nebula config: %w", err) } - dnsmasqPmuxProcConfig, err := dnsmasqPmuxProcConfig(env) + dnsmasqPmuxProcConfig, err := dnsmasqPmuxProcConfig(env, daemonConfig) if err != nil { return crypticnet.Env{}, fmt.Errorf("generating dnsmasq config: %w", err) } - pmuxProcConfigs := []pmuxlib.ProcessConfig{ - nebulaPmuxProcConfig, - dnsmasqPmuxProcConfig, + garagePmuxProcConfigs, err := garagePmuxProcConfigs(env, daemonConfig) + if err != nil { + return crypticnet.Env{}, fmt.Errorf("generating garage children configs: %w", err) } - if len(thisDaemon.Storage.Allocations) > 0 { - - garagePmuxProcConfigs, err := garagePmuxProcConfigs(env) - if err != nil { - return crypticnet.Env{}, fmt.Errorf("generating garage children configs: %w", err) - } - - pmuxProcConfigs = append(pmuxProcConfigs, garagePmuxProcConfigs...) + pmuxConfig := pmuxlib.Config{ + Processes: append( + []pmuxlib.ProcessConfig{ + nebulaPmuxProcConfig, + dnsmasqPmuxProcConfig, + }, + garagePmuxProcConfigs..., + ), } - pmuxConfig := pmuxlib.Config{Processes: pmuxProcConfigs} - doneCh := env.Context.Done() var wg sync.WaitGroup @@ -134,7 +133,7 @@ func runDaemonPmuxOnce(env crypticnet.Env) (crypticnet.Env, error) { go func() { defer wg.Done() - if err := waitForGarageAndNebula(ctx, env); err != nil { + if err := waitForGarageAndNebula(ctx, env, daemonConfig); err != nil { fmt.Fprintf(os.Stderr, "aborted waiting for garage instances to be accessible: %v\n", err) return } @@ -151,19 +150,19 @@ func runDaemonPmuxOnce(env crypticnet.Env) (crypticnet.Env, error) { } }() - if len(thisDaemon.Storage.Allocations) > 0 { + if len(daemonConfig.Storage.Allocations) > 0 { wg.Add(1) go func() { defer wg.Done() - if err := waitForGarageAndNebula(ctx, env); err != nil { + if err := waitForGarageAndNebula(ctx, env, daemonConfig); err != nil { fmt.Fprintf(os.Stderr, "aborted waiting for garage instances to be accessible: %v\n", err) return } err := doOnce(ctx, func(ctx context.Context) error { fmt.Fprintln(os.Stderr, "applying garage layout") - return garageApplyLayout(ctx, env) + return garageApplyLayout(ctx, env, daemonConfig) }) if err != nil { @@ -208,7 +207,7 @@ var subCmdDaemon = subCmd{ flags := subCmdCtx.flagSet(false) - daemonYmlPath := flags.StringP( + daemonConfigPath := flags.StringP( "config-path", "c", "", "Optional path to a daemon.yml file to load configuration from.", ) @@ -230,7 +229,7 @@ var subCmdDaemon = subCmd{ env := subCmdCtx.env if *dumpConfig { - return writeBuiltinDaemonYml(env, os.Stdout) + return daemon.CopyDefaultConfig(os.Stdout, env.AppDirPath) } runtimeDirPath := env.RuntimeDirPath @@ -285,24 +284,25 @@ var subCmdDaemon = subCmd{ } } - if err := writeMergedDaemonYml(env, *daemonYmlPath); err != nil { - return fmt.Errorf("merging and writing daemon.yml file: %w", err) + daemonConfig, err := daemon.LoadConfig(env.AppDirPath, *daemonConfigPath) + if err != nil { + return fmt.Errorf("loading daemon config: %w", err) } - var err error - // we update this Host's data using whatever configuration has been - // provided by daemon.yml. This way the daemon has the most - // up-to-date possible bootstrap. This updated bootstrap will later - // get updated in garage using update-global-bucket, so other hosts - // will see it as well. - if env, err = mergeDaemonIntoBootstrap(env); err != nil { - return fmt.Errorf("merging daemon.yml into bootstrap data: %w", err) + // provided by the daemon config. This way the daemon has the most + // up-to-date possible bootstrap. This updated bootstrap will later get + // updated in garage using bootstrap.PutGarageBoostrapHost, so other + // hosts will see it as well. + if env, err = mergeDaemonConfigIntoBootstrap(env, daemonConfig); err != nil { + return fmt.Errorf("merging daemon config into bootstrap data: %w", err) } for { - if env, err = runDaemonPmuxOnce(env); errors.Is(err, context.Canceled) { + env, err = runDaemonPmuxOnce(env, daemonConfig) + + if errors.Is(err, context.Canceled) { return nil } else if err != nil { diff --git a/entrypoint/src/cmd/entrypoint/daemon_util.go b/entrypoint/src/cmd/entrypoint/daemon_util.go index d19fe45..5c56c4c 100644 --- a/entrypoint/src/cmd/entrypoint/daemon_util.go +++ b/entrypoint/src/cmd/entrypoint/daemon_util.go @@ -5,6 +5,7 @@ import ( "context" crypticnet "cryptic-net" "cryptic-net/bootstrap" + "cryptic-net/daemon" "fmt" "io" "os" @@ -36,15 +37,18 @@ func copyBootstrapToDataDirAndReload(env crypticnet.Env, r io.Reader) (crypticne return env.LoadBootstrap(path) } -func mergeDaemonIntoBootstrap(env crypticnet.Env) (crypticnet.Env, error) { - daemon := env.ThisDaemon() +func mergeDaemonConfigIntoBootstrap( + env crypticnet.Env, daemonConfig daemon.Config, +) ( + crypticnet.Env, error, +) { host := env.Bootstrap.ThisHost() - host.Nebula.PublicAddr = daemon.VPN.PublicAddr + host.Nebula.PublicAddr = daemonConfig.VPN.PublicAddr host.Garage = nil - if allocs := daemon.Storage.Allocations; len(allocs) > 0 { + if allocs := daemonConfig.Storage.Allocations; len(allocs) > 0 { host.Garage = new(bootstrap.GarageHost) diff --git a/entrypoint/src/cmd/entrypoint/daemon_yml.go b/entrypoint/src/cmd/entrypoint/daemon_yml.go deleted file mode 100644 index f12efe4..0000000 --- a/entrypoint/src/cmd/entrypoint/daemon_yml.go +++ /dev/null @@ -1,72 +0,0 @@ -package main - -import ( - crypticnet "cryptic-net" - "cryptic-net/yamlutil" - "fmt" - "io" - "io/ioutil" - "os" - "path/filepath" - - "github.com/imdario/mergo" - "gopkg.in/yaml.v3" -) - -func builtinDaemonYmlPath(env crypticnet.Env) string { - return filepath.Join(env.AppDirPath, "etc", "daemon.yml") -} - -func writeBuiltinDaemonYml(env crypticnet.Env, w io.Writer) error { - - builtinDaemonYmlPath := builtinDaemonYmlPath(env) - - builtinDaemonYml, err := os.ReadFile(builtinDaemonYmlPath) - if err != nil { - return fmt.Errorf("reading default daemon.yml at %q: %w", builtinDaemonYmlPath, err) - } - - if _, err := w.Write(builtinDaemonYml); err != nil { - return fmt.Errorf("writing default daemon.yml: %w", err) - } - - return nil -} - -func writeMergedDaemonYml(env crypticnet.Env, userDaemonYmlPath string) error { - - builtinDaemonYmlPath := builtinDaemonYmlPath(env) - - var fullDaemonYml map[string]interface{} - - if err := yamlutil.LoadYamlFile(&fullDaemonYml, builtinDaemonYmlPath); err != nil { - return fmt.Errorf("parsing builtin daemon.yml file: %w", err) - } - - if userDaemonYmlPath != "" { - - var daemonYml map[string]interface{} - if err := yamlutil.LoadYamlFile(&daemonYml, userDaemonYmlPath); err != nil { - return fmt.Errorf("parsing %q: %w", userDaemonYmlPath, err) - } - - err := mergo.Merge(&fullDaemonYml, daemonYml, mergo.WithOverride) - if err != nil { - return fmt.Errorf("merging contents of file %q: %w", userDaemonYmlPath, err) - } - } - - fullDaemonYmlB, err := yaml.Marshal(fullDaemonYml) - - if err != nil { - return fmt.Errorf("yaml marshaling daemon config: %w", err) - } - - daemonYmlPath := filepath.Join(env.RuntimeDirPath, "daemon.yml") - - if err := ioutil.WriteFile(daemonYmlPath, fullDaemonYmlB, 0400); err != nil { - return fmt.Errorf("writing daemon.yml file to %q: %w", daemonYmlPath, err) - } - - return nil -} diff --git a/entrypoint/src/cmd/entrypoint/dnsmasq_util.go b/entrypoint/src/cmd/entrypoint/dnsmasq_util.go index 6616f39..80fc7c8 100644 --- a/entrypoint/src/cmd/entrypoint/dnsmasq_util.go +++ b/entrypoint/src/cmd/entrypoint/dnsmasq_util.go @@ -3,6 +3,7 @@ package main import ( crypticnet "cryptic-net" "cryptic-net/bootstrap" + "cryptic-net/daemon" "cryptic-net/dnsmasq" "fmt" "path/filepath" @@ -11,9 +12,11 @@ import ( "code.betamike.com/cryptic-io/pmux/pmuxlib" ) -func dnsmasqPmuxProcConfig(env crypticnet.Env) (pmuxlib.ProcessConfig, error) { - - thisDaemon := env.ThisDaemon() +func dnsmasqPmuxProcConfig( + env crypticnet.Env, daemonConfig daemon.Config, +) ( + pmuxlib.ProcessConfig, error, +) { confPath := filepath.Join(env.RuntimeDirPath, "dnsmasq.conf") @@ -27,7 +30,7 @@ func dnsmasqPmuxProcConfig(env crypticnet.Env) (pmuxlib.ProcessConfig, error) { }) confData := dnsmasq.ConfData{ - Resolvers: thisDaemon.DNS.Resolvers, + Resolvers: daemonConfig.DNS.Resolvers, Domain: env.Bootstrap.AdminCreationParams.Domain, IP: env.Bootstrap.ThisHost().Nebula.IP, Hosts: hostsSlice, diff --git a/entrypoint/src/cmd/entrypoint/garage.go b/entrypoint/src/cmd/entrypoint/garage.go index a684cfb..b3bf3fd 100644 --- a/entrypoint/src/cmd/entrypoint/garage.go +++ b/entrypoint/src/cmd/entrypoint/garage.go @@ -52,7 +52,7 @@ var subCmdGarageMC = subCmd{ args = append([]string{"mc"}, args...) var ( - binPath = env.BinPath("mc") + binPath = "mc" cliEnv = append( os.Environ(), fmt.Sprintf( @@ -86,7 +86,7 @@ var subCmdGarageCLI = subCmd{ env := subCmdCtx.env var ( - binPath = env.BinPath("garage") + binPath = "garage" args = append([]string{"garage"}, subCmdCtx.args...) cliEnv = append( os.Environ(), diff --git a/entrypoint/src/cmd/entrypoint/garage_util.go b/entrypoint/src/cmd/entrypoint/garage_util.go index d912d8a..91f101e 100644 --- a/entrypoint/src/cmd/entrypoint/garage_util.go +++ b/entrypoint/src/cmd/entrypoint/garage_util.go @@ -3,6 +3,7 @@ package main import ( "context" crypticnet "cryptic-net" + "cryptic-net/daemon" "cryptic-net/garage" "fmt" "net" @@ -13,9 +14,28 @@ import ( "code.betamike.com/cryptic-io/pmux/pmuxlib" ) -func waitForGarageAndNebula(ctx context.Context, env crypticnet.Env) error { +// newGarageAdminClient will return an AdminClient for a local garage instance, +// or it will _panic_ if there is no local instance configured. +func newGarageAdminClient( + env crypticnet.Env, daemonConfig daemon.Config, +) *garage.AdminClient { - allocs := env.ThisDaemon().Storage.Allocations + thisHost := env.Bootstrap.ThisHost() + + return garage.NewAdminClient( + net.JoinHostPort( + thisHost.Nebula.IP, + strconv.Itoa(daemonConfig.Storage.Allocations[0].AdminPort), + ), + env.Bootstrap.GarageAdminToken, + ) +} + +func waitForGarageAndNebula( + ctx context.Context, env crypticnet.Env, daemonConfig daemon.Config, +) error { + + allocs := daemonConfig.Storage.Allocations // if this host doesn't have any allocations specified then fall back to // waiting for nebula @@ -44,9 +64,9 @@ func waitForGarageAndNebula(ctx context.Context, env crypticnet.Env) error { } -func garageWriteChildConf( +func garageWriteChildConfig( env crypticnet.Env, - alloc crypticnet.DaemonYmlStorageAllocation, + alloc daemon.ConfigStorageAllocation, ) ( string, error, ) { @@ -100,13 +120,17 @@ func garageWriteChildConf( return garageTomlPath, nil } -func garagePmuxProcConfigs(env crypticnet.Env) ([]pmuxlib.ProcessConfig, error) { +func garagePmuxProcConfigs( + env crypticnet.Env, daemonConfig daemon.Config, +) ( + []pmuxlib.ProcessConfig, error, +) { var pmuxProcConfigs []pmuxlib.ProcessConfig - for _, alloc := range env.ThisDaemon().Storage.Allocations { + for _, alloc := range daemonConfig.Storage.Allocations { - childConfPath, err := garageWriteChildConf(env, alloc) + childConfigPath, err := garageWriteChildConfig(env, alloc) if err != nil { return nil, fmt.Errorf("writing child config file for alloc %+v: %w", alloc, err) @@ -115,7 +139,7 @@ func garagePmuxProcConfigs(env crypticnet.Env) ([]pmuxlib.ProcessConfig, error) pmuxProcConfigs = append(pmuxProcConfigs, pmuxlib.ProcessConfig{ Name: fmt.Sprintf("garage-%d", alloc.RPCPort), Cmd: "garage", - Args: []string{"-c", childConfPath, "server"}, + Args: []string{"-c", childConfigPath, "server"}, StartAfterFunc: func(ctx context.Context) error { return waitForNebula(ctx, env) }, @@ -125,10 +149,12 @@ func garagePmuxProcConfigs(env crypticnet.Env) ([]pmuxlib.ProcessConfig, error) return pmuxProcConfigs, nil } -func garageInitializeGlobalBucket(ctx context.Context, env crypticnet.Env) error { +func garageInitializeGlobalBucket( + ctx context.Context, env crypticnet.Env, daemonConfig daemon.Config, +) error { var ( - adminClient = env.GarageAdminClient() + adminClient = newGarageAdminClient(env, daemonConfig) globalBucketCreds = env.Bootstrap.GarageGlobalBucketS3APICredentials ) @@ -183,14 +209,16 @@ func garageInitializeGlobalBucket(ctx context.Context, env crypticnet.Env) error return nil } -func garageApplyLayout(ctx context.Context, env crypticnet.Env) error { +func garageApplyLayout( + ctx context.Context, env crypticnet.Env, daemonConfig daemon.Config, +) error { var ( - adminClient = env.GarageAdminClient() + adminClient = newGarageAdminClient(env, daemonConfig) thisHost = env.Bootstrap.ThisHost() hostName = thisHost.Name ip = thisHost.Nebula.IP - allocs = env.ThisDaemon().Storage.Allocations + allocs = daemonConfig.Storage.Allocations ) type peerLayout struct { diff --git a/entrypoint/src/cmd/entrypoint/nebula_util.go b/entrypoint/src/cmd/entrypoint/nebula_util.go index d0f5e8d..9dc50c8 100644 --- a/entrypoint/src/cmd/entrypoint/nebula_util.go +++ b/entrypoint/src/cmd/entrypoint/nebula_util.go @@ -3,6 +3,7 @@ package main import ( "context" crypticnet "cryptic-net" + "cryptic-net/daemon" "cryptic-net/yamlutil" "fmt" "net" @@ -33,9 +34,11 @@ func waitForNebula(ctx context.Context, env crypticnet.Env) error { }) } -func nebulaPmuxProcConfig(env crypticnet.Env) (pmuxlib.ProcessConfig, error) { - - thisDaemon := env.ThisDaemon() +func nebulaPmuxProcConfig( + env crypticnet.Env, daemonConfig daemon.Config, +) ( + pmuxlib.ProcessConfig, error, +) { var ( lighthouseHostIPs []string @@ -66,10 +69,10 @@ func nebulaPmuxProcConfig(env crypticnet.Env) (pmuxlib.ProcessConfig, error) { "tun": map[string]interface{}{ "dev": "cryptic-net-nebula", }, - "firewall": thisDaemon.VPN.Firewall, + "firewall": daemonConfig.VPN.Firewall, } - if publicAddr := env.ThisDaemon().VPN.PublicAddr; publicAddr == "" { + if publicAddr := daemonConfig.VPN.PublicAddr; publicAddr == "" { config["listen"] = map[string]string{ "host": "0.0.0.0", diff --git a/entrypoint/src/daemon_yml.go b/entrypoint/src/daemon/config.go similarity index 66% rename from entrypoint/src/daemon_yml.go rename to entrypoint/src/daemon/config.go index a8275ab..1e45458 100644 --- a/entrypoint/src/daemon_yml.go +++ b/entrypoint/src/daemon/config.go @@ -1,4 +1,4 @@ -package crypticnet +package daemon import "strconv" @@ -27,9 +27,9 @@ type ConfigFirewallRule struct { CAName string `yaml:"ca_name,omitempty"` } -// DaemonYmlStorageAllocation describes the structure of each storage allocation -// within the daemon.yml file. -type DaemonYmlStorageAllocation struct { +// ConfigStorageAllocation describes the structure of each storage allocation +// within the daemon config file. +type ConfigStorageAllocation struct { DataPath string `yaml:"data_path"` MetaPath string `yaml:"meta_path"` Capacity int `yaml:"capacity"` @@ -38,8 +38,8 @@ type DaemonYmlStorageAllocation struct { AdminPort int `yaml:"admin_port"` } -// DaemonYml describes the structure of the daemon.yml file. -type DaemonYml struct { +// Config describes the structure of the daemon config file. +type Config struct { DNS struct { Resolvers []string `yaml:"resolvers"` } `yaml:"dns"` @@ -48,30 +48,29 @@ type DaemonYml struct { Firewall ConfigFirewall `yaml:"firewall"` } `yaml:"vpn"` Storage struct { - Allocations []DaemonYmlStorageAllocation + Allocations []ConfigStorageAllocation } `yaml:"storage"` } -// FillDefaults fills in default values in the DaemonYml. -func (d *DaemonYml) FillDefaults() { +func (c *Config) fillDefaults() { var firewallGarageInbound []ConfigFirewallRule - for i := range d.Storage.Allocations { + for i := range c.Storage.Allocations { - if d.Storage.Allocations[i].RPCPort == 0 { - d.Storage.Allocations[i].RPCPort = 3900 + (i * 10) + if c.Storage.Allocations[i].RPCPort == 0 { + c.Storage.Allocations[i].RPCPort = 3900 + (i * 10) } - if d.Storage.Allocations[i].S3APIPort == 0 { - d.Storage.Allocations[i].S3APIPort = 3901 + (i * 10) + if c.Storage.Allocations[i].S3APIPort == 0 { + c.Storage.Allocations[i].S3APIPort = 3901 + (i * 10) } - if d.Storage.Allocations[i].AdminPort == 0 { - d.Storage.Allocations[i].AdminPort = 3902 + (i * 10) + if c.Storage.Allocations[i].AdminPort == 0 { + c.Storage.Allocations[i].AdminPort = 3902 + (i * 10) } - alloc := d.Storage.Allocations[i] + alloc := c.Storage.Allocations[i] firewallGarageInbound = append( firewallGarageInbound, @@ -88,8 +87,8 @@ func (d *DaemonYml) FillDefaults() { ) } - d.VPN.Firewall.Inbound = append( - d.VPN.Firewall.Inbound, + c.VPN.Firewall.Inbound = append( + c.VPN.Firewall.Inbound, firewallGarageInbound..., ) } diff --git a/entrypoint/src/daemon/daemon.go b/entrypoint/src/daemon/daemon.go new file mode 100644 index 0000000..2e34f0f --- /dev/null +++ b/entrypoint/src/daemon/daemon.go @@ -0,0 +1,85 @@ +// Package daemon contains types and functions related specifically to the +// cryptic-net daemon. +package daemon + +import ( + "cryptic-net/yamlutil" + "fmt" + "io" + "os" + "path/filepath" + + "github.com/imdario/mergo" + "gopkg.in/yaml.v3" +) + +func defaultConfigPath(appDirPath string) string { + return filepath.Join(appDirPath, "etc", "daemon.yml") +} + +// CopyDefaultConfig copies the daemon config file embedded in the AppDir into +// the given io.Writer. +func CopyDefaultConfig(into io.Writer, appDirPath string) error { + + defaultConfigPath := defaultConfigPath(appDirPath) + + f, err := os.Open(defaultConfigPath) + if err != nil { + return fmt.Errorf("opening daemon config at %q: %w", defaultConfigPath, err) + } + + defer f.Close() + + if _, err := io.Copy(into, f); err != nil { + return fmt.Errorf("copying daemon config from %q: %w", defaultConfigPath, err) + } + + return nil +} + +// LoadConfig loads the daemon config from userConfigPath, merges it with +// the default found in the appDirPath, and returns the result. +// +// If userConfigPath is not given then the default is loaded and returned. +func LoadConfig( + appDirPath, userConfigPath string, +) ( + 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 != "" { + + 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 { + return Config{}, fmt.Errorf("yaml marshaling: %w", err) + } + + var config Config + if err := yaml.Unmarshal(fullDaemonB, &config); err != nil { + return Config{}, fmt.Errorf("yaml unmarshaling back into Config struct: %w", err) + } + + config.fillDefaults() + + return config, nil +} diff --git a/entrypoint/src/env.go b/entrypoint/src/env.go index 19a3b86..e1c4c5b 100644 --- a/entrypoint/src/env.go +++ b/entrypoint/src/env.go @@ -3,17 +3,12 @@ package crypticnet import ( "context" "cryptic-net/bootstrap" - "cryptic-net/garage" - "cryptic-net/yamlutil" "errors" "fmt" "io/fs" - "net" "os" "os/signal" "path/filepath" - "strconv" - "sync" "syscall" "github.com/adrg/xdg" @@ -25,7 +20,6 @@ type Env struct { Context context.Context AppDirPath string - DaemonYmlPath string RuntimeDirPath string DataDirPath string @@ -33,9 +27,6 @@ type Env struct { // found, then these fields will not be set. BootstrapPath string Bootstrap bootstrap.Bootstrap - - thisDaemon DaemonYml - thisDaemonOnce sync.Once } func getAppDirPath() string { @@ -58,7 +49,6 @@ func NewEnv(bootstrapOptional bool) (Env, error) { env := Env{ AppDirPath: appDirPath, - DaemonYmlPath: filepath.Join(runtimeDirPath, "daemon.yml"), RuntimeDirPath: runtimeDirPath, DataDirPath: filepath.Join(xdg.DataHome, "cryptic-net"), } @@ -154,37 +144,3 @@ func (e Env) init(bootstrapOptional bool) (Env, error) { return e.initBootstrap(bootstrapOptional) } - -// ThisDaemon returns the DaemonYml (loaded from DaemonYmlPath) for the -// currently running process. -func (e Env) ThisDaemon() DaemonYml { - e.thisDaemonOnce.Do(func() { - if err := yamlutil.LoadYamlFile(&e.thisDaemon, e.DaemonYmlPath); err != nil { - panic(err) - } - - e.thisDaemon.FillDefaults() - }) - return e.thisDaemon -} - -// BinPath returns the absolute path to a binary in the AppDir. -func (e Env) BinPath(name string) string { - return filepath.Join(e.AppDirPath, "bin", name) -} - -// GarageAdminClient will return an AdminClient for a local garage instance, or -// it will _panic_ if there is no local instance configured. -func (e Env) GarageAdminClient() *garage.AdminClient { - - thisHost := e.Bootstrap.ThisHost() - thisDaemon := e.ThisDaemon() - - return garage.NewAdminClient( - net.JoinHostPort( - thisHost.Nebula.IP, - strconv.Itoa(thisDaemon.Storage.Allocations[0].AdminPort), - ), - e.Bootstrap.GarageAdminToken, - ) -}