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" crypticnet "cryptic-net"
"cryptic-net/admin" "cryptic-net/admin"
"cryptic-net/bootstrap" "cryptic-net/bootstrap"
"cryptic-net/daemon"
"cryptic-net/garage" "cryptic-net/garage"
"cryptic-net/nebula" "cryptic-net/nebula"
"crypto/rand" "crypto/rand"
@ -54,7 +55,7 @@ var subCmdAdminCreateNetwork = subCmd{
flags := subCmdCtx.flagSet(false) flags := subCmdCtx.flagSet(false)
daemonYmlPath := flags.StringP( daemonConfigPath := flags.StringP(
"config-path", "c", "", "config-path", "c", "",
"Optional path to a daemon.yml file to load configuration from.", "Optional path to a daemon.yml file to load configuration from.",
) )
@ -86,7 +87,7 @@ var subCmdAdminCreateNetwork = subCmd{
env := subCmdCtx.env env := subCmdCtx.env
if *dumpConfig { if *dumpConfig {
return writeBuiltinDaemonYml(env, os.Stdout) return daemon.CopyDefaultConfig(os.Stdout, env.AppDirPath)
} }
if *domain == "" || *subnetStr == "" || *hostName == "" { if *domain == "" || *subnetStr == "" || *hostName == "" {
@ -128,14 +129,13 @@ var subCmdAdminCreateNetwork = subCmd{
}() }()
} }
if err := writeMergedDaemonYml(env, *daemonYmlPath); err != nil { daemonConfig, err := daemon.LoadConfig(env.AppDirPath, *daemonConfigPath)
return fmt.Errorf("merging and writing daemon.yml file: %w", err) if err != nil {
return fmt.Errorf("loading daemon config: %w", err)
} }
daemon := env.ThisDaemon() if len(daemonConfig.Storage.Allocations) < 3 {
return fmt.Errorf("daemon config with at least 3 allocations was not provided")
if len(daemon.Storage.Allocations) < 3 {
return fmt.Errorf("daemon.yml with at least 3 allocations was not provided")
} }
nebulaCACert, err := nebula.NewCACert(*domain, subnet) nebulaCACert, err := nebula.NewCACert(*domain, subnet)
@ -165,16 +165,16 @@ var subCmdAdminCreateNetwork = subCmd{
GarageGlobalBucketS3APICredentials: garage.NewS3APICredentials(), GarageGlobalBucketS3APICredentials: garage.NewS3APICredentials(),
} }
if env, err = mergeDaemonIntoBootstrap(env); err != nil { if env, err = mergeDaemonConfigIntoBootstrap(env, daemonConfig); err != nil {
return fmt.Errorf("merging daemon.yml into bootstrap data: %w", err) return fmt.Errorf("merging daemon config into bootstrap data: %w", err)
} }
nebulaPmuxProcConfig, err := nebulaPmuxProcConfig(env) nebulaPmuxProcConfig, err := nebulaPmuxProcConfig(env, daemonConfig)
if err != nil { if err != nil {
return fmt.Errorf("generating nebula config: %w", err) return fmt.Errorf("generating nebula config: %w", err)
} }
garagePmuxProcConfigs, err := garagePmuxProcConfigs(env) garagePmuxProcConfigs, err := garagePmuxProcConfigs(env, daemonConfig)
if err != nil { if err != nil {
return fmt.Errorf("generating garage configs: %w", err) 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") 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) return fmt.Errorf("waiting for garage to start up: %w", err)
} }
fmt.Fprintln(os.Stderr, "applying initial garage layout") 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) return fmt.Errorf("applying initial garage layout: %w", err)
} }
fmt.Fprintln(os.Stderr, "initializing garage shared global bucket") 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 { 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?") 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" crypticnet "cryptic-net"
"cryptic-net/bootstrap" "cryptic-net/bootstrap"
"cryptic-net/daemon"
"cryptic-net/garage" "cryptic-net/garage"
"code.betamike.com/cryptic-io/pmux/pmuxlib" "code.betamike.com/cryptic-io/pmux/pmuxlib"
@ -25,11 +26,8 @@ import (
// * Creates the data directory and copies the appdir bootstrap file into there, // * Creates the data directory and copies the appdir bootstrap file into there,
// if it's not already there. // if it's not already there.
// //
// * Merges the user-provided daemon.yml file with the default, and writes the // * Merges daemon configuration into the bootstrap configuration, and rewrites
// result to the runtime dir. // the bootstrap file.
//
// * Merges daemon.yml configuration into the bootstrap configuration, and
// rewrites the bootstrap file.
// //
// * Sets up environment variables that all other sub-processes then use, based // * Sets up environment variables that all other sub-processes then use, based
// on the runtime dir. // 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 // 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 // until the spawned pmux has returned, and returns a copy of Env with updated
// boostrap info. // 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() thisHost := env.Bootstrap.ThisHost()
thisDaemon := env.ThisDaemon()
fmt.Fprintf(os.Stderr, "host name is %q, ip is %q\n", thisHost.Name, thisHost.Nebula.IP) 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 // create s3Client anew on every loop, in case the topology has
@ -89,33 +90,31 @@ func runDaemonPmuxOnce(env crypticnet.Env) (crypticnet.Env, error) {
// endpoint. // endpoint.
s3Client := env.Bootstrap.GlobalBucketS3APIClient() s3Client := env.Bootstrap.GlobalBucketS3APIClient()
nebulaPmuxProcConfig, err := nebulaPmuxProcConfig(env) nebulaPmuxProcConfig, err := nebulaPmuxProcConfig(env, daemonConfig)
if err != nil { if err != nil {
return crypticnet.Env{}, fmt.Errorf("generating nebula config: %w", err) return crypticnet.Env{}, fmt.Errorf("generating nebula config: %w", err)
} }
dnsmasqPmuxProcConfig, err := dnsmasqPmuxProcConfig(env) dnsmasqPmuxProcConfig, err := dnsmasqPmuxProcConfig(env, daemonConfig)
if err != nil { if err != nil {
return crypticnet.Env{}, fmt.Errorf("generating dnsmasq config: %w", err) return crypticnet.Env{}, fmt.Errorf("generating dnsmasq config: %w", err)
} }
pmuxProcConfigs := []pmuxlib.ProcessConfig{ garagePmuxProcConfigs, err := garagePmuxProcConfigs(env, daemonConfig)
nebulaPmuxProcConfig, if err != nil {
dnsmasqPmuxProcConfig, return crypticnet.Env{}, fmt.Errorf("generating garage children configs: %w", err)
} }
if len(thisDaemon.Storage.Allocations) > 0 { pmuxConfig := pmuxlib.Config{
Processes: append(
garagePmuxProcConfigs, err := garagePmuxProcConfigs(env) []pmuxlib.ProcessConfig{
if err != nil { nebulaPmuxProcConfig,
return crypticnet.Env{}, fmt.Errorf("generating garage children configs: %w", err) dnsmasqPmuxProcConfig,
} },
garagePmuxProcConfigs...,
pmuxProcConfigs = append(pmuxProcConfigs, garagePmuxProcConfigs...) ),
} }
pmuxConfig := pmuxlib.Config{Processes: pmuxProcConfigs}
doneCh := env.Context.Done() doneCh := env.Context.Done()
var wg sync.WaitGroup var wg sync.WaitGroup
@ -134,7 +133,7 @@ func runDaemonPmuxOnce(env crypticnet.Env) (crypticnet.Env, error) {
go func() { go func() {
defer wg.Done() 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) fmt.Fprintf(os.Stderr, "aborted waiting for garage instances to be accessible: %v\n", err)
return 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) wg.Add(1)
go func() { go func() {
defer wg.Done() 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) fmt.Fprintf(os.Stderr, "aborted waiting for garage instances to be accessible: %v\n", err)
return return
} }
err := doOnce(ctx, func(ctx context.Context) error { err := doOnce(ctx, func(ctx context.Context) error {
fmt.Fprintln(os.Stderr, "applying garage layout") fmt.Fprintln(os.Stderr, "applying garage layout")
return garageApplyLayout(ctx, env) return garageApplyLayout(ctx, env, daemonConfig)
}) })
if err != nil { if err != nil {
@ -208,7 +207,7 @@ var subCmdDaemon = subCmd{
flags := subCmdCtx.flagSet(false) flags := subCmdCtx.flagSet(false)
daemonYmlPath := flags.StringP( daemonConfigPath := flags.StringP(
"config-path", "c", "", "config-path", "c", "",
"Optional path to a daemon.yml file to load configuration from.", "Optional path to a daemon.yml file to load configuration from.",
) )
@ -230,7 +229,7 @@ var subCmdDaemon = subCmd{
env := subCmdCtx.env env := subCmdCtx.env
if *dumpConfig { if *dumpConfig {
return writeBuiltinDaemonYml(env, os.Stdout) return daemon.CopyDefaultConfig(os.Stdout, env.AppDirPath)
} }
runtimeDirPath := env.RuntimeDirPath runtimeDirPath := env.RuntimeDirPath
@ -285,24 +284,25 @@ var subCmdDaemon = subCmd{
} }
} }
if err := writeMergedDaemonYml(env, *daemonYmlPath); err != nil { daemonConfig, err := daemon.LoadConfig(env.AppDirPath, *daemonConfigPath)
return fmt.Errorf("merging and writing daemon.yml file: %w", err) 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 // we update this Host's data using whatever configuration has been
// provided by daemon.yml. This way the daemon has the most // provided by the daemon config. This way the daemon has the most
// up-to-date possible bootstrap. This updated bootstrap will later // up-to-date possible bootstrap. This updated bootstrap will later get
// get updated in garage using update-global-bucket, so other hosts // updated in garage using bootstrap.PutGarageBoostrapHost, so other
// will see it as well. // hosts will see it as well.
if env, err = mergeDaemonIntoBootstrap(env); err != nil { if env, err = mergeDaemonConfigIntoBootstrap(env, daemonConfig); err != nil {
return fmt.Errorf("merging daemon.yml into bootstrap data: %w", err) return fmt.Errorf("merging daemon config into bootstrap data: %w", err)
} }
for { for {
if env, err = runDaemonPmuxOnce(env); errors.Is(err, context.Canceled) { env, err = runDaemonPmuxOnce(env, daemonConfig)
if errors.Is(err, context.Canceled) {
return nil return nil
} else if err != nil { } else if err != nil {

View File

@ -5,6 +5,7 @@ import (
"context" "context"
crypticnet "cryptic-net" crypticnet "cryptic-net"
"cryptic-net/bootstrap" "cryptic-net/bootstrap"
"cryptic-net/daemon"
"fmt" "fmt"
"io" "io"
"os" "os"
@ -36,15 +37,18 @@ func copyBootstrapToDataDirAndReload(env crypticnet.Env, r io.Reader) (crypticne
return env.LoadBootstrap(path) return env.LoadBootstrap(path)
} }
func mergeDaemonIntoBootstrap(env crypticnet.Env) (crypticnet.Env, error) { func mergeDaemonConfigIntoBootstrap(
daemon := env.ThisDaemon() env crypticnet.Env, daemonConfig daemon.Config,
) (
crypticnet.Env, error,
) {
host := env.Bootstrap.ThisHost() host := env.Bootstrap.ThisHost()
host.Nebula.PublicAddr = daemon.VPN.PublicAddr host.Nebula.PublicAddr = daemonConfig.VPN.PublicAddr
host.Garage = nil host.Garage = nil
if allocs := daemon.Storage.Allocations; len(allocs) > 0 { if allocs := daemonConfig.Storage.Allocations; len(allocs) > 0 {
host.Garage = new(bootstrap.GarageHost) 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 ( import (
crypticnet "cryptic-net" crypticnet "cryptic-net"
"cryptic-net/bootstrap" "cryptic-net/bootstrap"
"cryptic-net/daemon"
"cryptic-net/dnsmasq" "cryptic-net/dnsmasq"
"fmt" "fmt"
"path/filepath" "path/filepath"
@ -11,9 +12,11 @@ import (
"code.betamike.com/cryptic-io/pmux/pmuxlib" "code.betamike.com/cryptic-io/pmux/pmuxlib"
) )
func dnsmasqPmuxProcConfig(env crypticnet.Env) (pmuxlib.ProcessConfig, error) { func dnsmasqPmuxProcConfig(
env crypticnet.Env, daemonConfig daemon.Config,
thisDaemon := env.ThisDaemon() ) (
pmuxlib.ProcessConfig, error,
) {
confPath := filepath.Join(env.RuntimeDirPath, "dnsmasq.conf") confPath := filepath.Join(env.RuntimeDirPath, "dnsmasq.conf")
@ -27,7 +30,7 @@ func dnsmasqPmuxProcConfig(env crypticnet.Env) (pmuxlib.ProcessConfig, error) {
}) })
confData := dnsmasq.ConfData{ confData := dnsmasq.ConfData{
Resolvers: thisDaemon.DNS.Resolvers, Resolvers: daemonConfig.DNS.Resolvers,
Domain: env.Bootstrap.AdminCreationParams.Domain, Domain: env.Bootstrap.AdminCreationParams.Domain,
IP: env.Bootstrap.ThisHost().Nebula.IP, IP: env.Bootstrap.ThisHost().Nebula.IP,
Hosts: hostsSlice, Hosts: hostsSlice,

View File

@ -52,7 +52,7 @@ var subCmdGarageMC = subCmd{
args = append([]string{"mc"}, args...) args = append([]string{"mc"}, args...)
var ( var (
binPath = env.BinPath("mc") binPath = "mc"
cliEnv = append( cliEnv = append(
os.Environ(), os.Environ(),
fmt.Sprintf( fmt.Sprintf(
@ -86,7 +86,7 @@ var subCmdGarageCLI = subCmd{
env := subCmdCtx.env env := subCmdCtx.env
var ( var (
binPath = env.BinPath("garage") binPath = "garage"
args = append([]string{"garage"}, subCmdCtx.args...) args = append([]string{"garage"}, subCmdCtx.args...)
cliEnv = append( cliEnv = append(
os.Environ(), os.Environ(),

View File

@ -3,6 +3,7 @@ package main
import ( import (
"context" "context"
crypticnet "cryptic-net" crypticnet "cryptic-net"
"cryptic-net/daemon"
"cryptic-net/garage" "cryptic-net/garage"
"fmt" "fmt"
"net" "net"
@ -13,9 +14,28 @@ import (
"code.betamike.com/cryptic-io/pmux/pmuxlib" "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 // if this host doesn't have any allocations specified then fall back to
// waiting for nebula // waiting for nebula
@ -44,9 +64,9 @@ func waitForGarageAndNebula(ctx context.Context, env crypticnet.Env) error {
} }
func garageWriteChildConf( func garageWriteChildConfig(
env crypticnet.Env, env crypticnet.Env,
alloc crypticnet.DaemonYmlStorageAllocation, alloc daemon.ConfigStorageAllocation,
) ( ) (
string, error, string, error,
) { ) {
@ -100,13 +120,17 @@ func garageWriteChildConf(
return garageTomlPath, nil 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 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 { if err != nil {
return nil, fmt.Errorf("writing child config file for alloc %+v: %w", alloc, err) 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{ pmuxProcConfigs = append(pmuxProcConfigs, pmuxlib.ProcessConfig{
Name: fmt.Sprintf("garage-%d", alloc.RPCPort), Name: fmt.Sprintf("garage-%d", alloc.RPCPort),
Cmd: "garage", Cmd: "garage",
Args: []string{"-c", childConfPath, "server"}, Args: []string{"-c", childConfigPath, "server"},
StartAfterFunc: func(ctx context.Context) error { StartAfterFunc: func(ctx context.Context) error {
return waitForNebula(ctx, env) return waitForNebula(ctx, env)
}, },
@ -125,10 +149,12 @@ func garagePmuxProcConfigs(env crypticnet.Env) ([]pmuxlib.ProcessConfig, error)
return pmuxProcConfigs, nil 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 ( var (
adminClient = env.GarageAdminClient() adminClient = newGarageAdminClient(env, daemonConfig)
globalBucketCreds = env.Bootstrap.GarageGlobalBucketS3APICredentials globalBucketCreds = env.Bootstrap.GarageGlobalBucketS3APICredentials
) )
@ -183,14 +209,16 @@ func garageInitializeGlobalBucket(ctx context.Context, env crypticnet.Env) error
return nil return nil
} }
func garageApplyLayout(ctx context.Context, env crypticnet.Env) error { func garageApplyLayout(
ctx context.Context, env crypticnet.Env, daemonConfig daemon.Config,
) error {
var ( var (
adminClient = env.GarageAdminClient() adminClient = newGarageAdminClient(env, daemonConfig)
thisHost = env.Bootstrap.ThisHost() thisHost = env.Bootstrap.ThisHost()
hostName = thisHost.Name hostName = thisHost.Name
ip = thisHost.Nebula.IP ip = thisHost.Nebula.IP
allocs = env.ThisDaemon().Storage.Allocations allocs = daemonConfig.Storage.Allocations
) )
type peerLayout struct { type peerLayout struct {

View File

@ -3,6 +3,7 @@ package main
import ( import (
"context" "context"
crypticnet "cryptic-net" crypticnet "cryptic-net"
"cryptic-net/daemon"
"cryptic-net/yamlutil" "cryptic-net/yamlutil"
"fmt" "fmt"
"net" "net"
@ -33,9 +34,11 @@ func waitForNebula(ctx context.Context, env crypticnet.Env) error {
}) })
} }
func nebulaPmuxProcConfig(env crypticnet.Env) (pmuxlib.ProcessConfig, error) { func nebulaPmuxProcConfig(
env crypticnet.Env, daemonConfig daemon.Config,
thisDaemon := env.ThisDaemon() ) (
pmuxlib.ProcessConfig, error,
) {
var ( var (
lighthouseHostIPs []string lighthouseHostIPs []string
@ -66,10 +69,10 @@ func nebulaPmuxProcConfig(env crypticnet.Env) (pmuxlib.ProcessConfig, error) {
"tun": map[string]interface{}{ "tun": map[string]interface{}{
"dev": "cryptic-net-nebula", "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{ config["listen"] = map[string]string{
"host": "0.0.0.0", "host": "0.0.0.0",

View File

@ -1,4 +1,4 @@
package crypticnet package daemon
import "strconv" import "strconv"
@ -27,9 +27,9 @@ type ConfigFirewallRule struct {
CAName string `yaml:"ca_name,omitempty"` CAName string `yaml:"ca_name,omitempty"`
} }
// DaemonYmlStorageAllocation describes the structure of each storage allocation // ConfigStorageAllocation describes the structure of each storage allocation
// within the daemon.yml file. // within the daemon config file.
type DaemonYmlStorageAllocation struct { type ConfigStorageAllocation struct {
DataPath string `yaml:"data_path"` DataPath string `yaml:"data_path"`
MetaPath string `yaml:"meta_path"` MetaPath string `yaml:"meta_path"`
Capacity int `yaml:"capacity"` Capacity int `yaml:"capacity"`
@ -38,8 +38,8 @@ type DaemonYmlStorageAllocation struct {
AdminPort int `yaml:"admin_port"` AdminPort int `yaml:"admin_port"`
} }
// DaemonYml describes the structure of the daemon.yml file. // Config describes the structure of the daemon config file.
type DaemonYml struct { type Config struct {
DNS struct { DNS struct {
Resolvers []string `yaml:"resolvers"` Resolvers []string `yaml:"resolvers"`
} `yaml:"dns"` } `yaml:"dns"`
@ -48,30 +48,29 @@ type DaemonYml struct {
Firewall ConfigFirewall `yaml:"firewall"` Firewall ConfigFirewall `yaml:"firewall"`
} `yaml:"vpn"` } `yaml:"vpn"`
Storage struct { Storage struct {
Allocations []DaemonYmlStorageAllocation Allocations []ConfigStorageAllocation
} `yaml:"storage"` } `yaml:"storage"`
} }
// FillDefaults fills in default values in the DaemonYml. func (c *Config) fillDefaults() {
func (d *DaemonYml) FillDefaults() {
var firewallGarageInbound []ConfigFirewallRule var firewallGarageInbound []ConfigFirewallRule
for i := range d.Storage.Allocations { for i := range c.Storage.Allocations {
if d.Storage.Allocations[i].RPCPort == 0 { if c.Storage.Allocations[i].RPCPort == 0 {
d.Storage.Allocations[i].RPCPort = 3900 + (i * 10) c.Storage.Allocations[i].RPCPort = 3900 + (i * 10)
} }
if d.Storage.Allocations[i].S3APIPort == 0 { if c.Storage.Allocations[i].S3APIPort == 0 {
d.Storage.Allocations[i].S3APIPort = 3901 + (i * 10) c.Storage.Allocations[i].S3APIPort = 3901 + (i * 10)
} }
if d.Storage.Allocations[i].AdminPort == 0 { if c.Storage.Allocations[i].AdminPort == 0 {
d.Storage.Allocations[i].AdminPort = 3902 + (i * 10) c.Storage.Allocations[i].AdminPort = 3902 + (i * 10)
} }
alloc := d.Storage.Allocations[i] alloc := c.Storage.Allocations[i]
firewallGarageInbound = append( firewallGarageInbound = append(
firewallGarageInbound, firewallGarageInbound,
@ -88,8 +87,8 @@ func (d *DaemonYml) FillDefaults() {
) )
} }
d.VPN.Firewall.Inbound = append( c.VPN.Firewall.Inbound = append(
d.VPN.Firewall.Inbound, c.VPN.Firewall.Inbound,
firewallGarageInbound..., 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 ( import (
"context" "context"
"cryptic-net/bootstrap" "cryptic-net/bootstrap"
"cryptic-net/garage"
"cryptic-net/yamlutil"
"errors" "errors"
"fmt" "fmt"
"io/fs" "io/fs"
"net"
"os" "os"
"os/signal" "os/signal"
"path/filepath" "path/filepath"
"strconv"
"sync"
"syscall" "syscall"
"github.com/adrg/xdg" "github.com/adrg/xdg"
@ -25,7 +20,6 @@ type Env struct {
Context context.Context Context context.Context
AppDirPath string AppDirPath string
DaemonYmlPath string
RuntimeDirPath string RuntimeDirPath string
DataDirPath string DataDirPath string
@ -33,9 +27,6 @@ type Env struct {
// found, then these fields will not be set. // found, then these fields will not be set.
BootstrapPath string BootstrapPath string
Bootstrap bootstrap.Bootstrap Bootstrap bootstrap.Bootstrap
thisDaemon DaemonYml
thisDaemonOnce sync.Once
} }
func getAppDirPath() string { func getAppDirPath() string {
@ -58,7 +49,6 @@ func NewEnv(bootstrapOptional bool) (Env, error) {
env := Env{ env := Env{
AppDirPath: appDirPath, AppDirPath: appDirPath,
DaemonYmlPath: filepath.Join(runtimeDirPath, "daemon.yml"),
RuntimeDirPath: runtimeDirPath, RuntimeDirPath: runtimeDirPath,
DataDirPath: filepath.Join(xdg.DataHome, "cryptic-net"), DataDirPath: filepath.Join(xdg.DataHome, "cryptic-net"),
} }
@ -154,37 +144,3 @@ func (e Env) init(bootstrapOptional bool) (Env, error) {
return e.initBootstrap(bootstrapOptional) 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,
)
}