Move daemon.yml types and functionality out of entrypoint and Env

This commit is contained in:
Brian Picciano 2022-10-26 23:21:31 +02:00
parent 03618ba72c
commit 08f47bd514
11 changed files with 223 additions and 217 deletions

View File

@ -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?")

View File

@ -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 {

View File

@ -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)

View File

@ -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
}

View File

@ -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,

View File

@ -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(),

View File

@ -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 {

View File

@ -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",

View File

@ -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...,
)
}

View File

@ -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
}

View File

@ -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,
)
}