Move common daemon types and values into daecommon
This commit is contained in:
parent
ef86c1bbd1
commit
a840d0e701
@ -6,6 +6,7 @@ import (
|
||||
"os"
|
||||
|
||||
"isle/daemon"
|
||||
"isle/daemon/daecommon"
|
||||
|
||||
"dev.mediocregopher.com/mediocre-go-lib.git/mlog"
|
||||
)
|
||||
@ -41,7 +42,7 @@ var subCmdDaemon = subCmd{
|
||||
}
|
||||
|
||||
if *dumpConfig {
|
||||
return daemon.CopyDefaultConfig(os.Stdout, envAppDirPath)
|
||||
return daecommon.CopyDefaultConfig(os.Stdout, envAppDirPath)
|
||||
}
|
||||
|
||||
logLevel := mlog.LevelFromString(*logLevelStr)
|
||||
@ -55,7 +56,7 @@ var subCmdDaemon = subCmd{
|
||||
// required linux capabilities are set.
|
||||
// TODO check that the tun module is loaded (for nebula).
|
||||
|
||||
daemonConfig, err := daemon.LoadConfig(envAppDirPath, *daemonConfigPath)
|
||||
daemonConfig, err := daecommon.LoadConfig(envAppDirPath, *daemonConfigPath)
|
||||
if err != nil {
|
||||
return fmt.Errorf("loading daemon config: %w", err)
|
||||
}
|
||||
|
@ -2,12 +2,13 @@ package main
|
||||
|
||||
import (
|
||||
"context"
|
||||
"isle/daemon"
|
||||
"os"
|
||||
"os/signal"
|
||||
"path/filepath"
|
||||
"syscall"
|
||||
|
||||
"isle/daemon/daecommon"
|
||||
|
||||
"dev.mediocregopher.com/mediocre-go-lib.git/mctx"
|
||||
"dev.mediocregopher.com/mediocre-go-lib.git/mlog"
|
||||
)
|
||||
@ -21,7 +22,7 @@ func getAppDirPath() string {
|
||||
}
|
||||
|
||||
var (
|
||||
daemonEnvVars = daemon.GetEnvVars()
|
||||
daemonEnvVars = daecommon.GetEnvVars()
|
||||
envAppDirPath = getAppDirPath()
|
||||
envBinDirPath = filepath.Join(envAppDirPath, "bin")
|
||||
)
|
||||
|
@ -130,7 +130,9 @@ func (ctx subCmdCtx) doSubCmd(subCmds ...subCmd) error {
|
||||
}
|
||||
|
||||
daemonRPC := daemon.RPCFromClient(
|
||||
jsonrpc2.NewUnixHTTPClient(daemon.HTTPSocketPath(), daemonHTTPRPCPath),
|
||||
jsonrpc2.NewUnixHTTPClient(
|
||||
daemon.HTTPSocketPath(), daemonHTTPRPCPath,
|
||||
),
|
||||
)
|
||||
|
||||
err := subCmd.do(subCmdCtx{
|
||||
|
@ -10,6 +10,7 @@ import (
|
||||
"reflect"
|
||||
|
||||
"isle/bootstrap"
|
||||
"isle/daemon/daecommon"
|
||||
"isle/garage/garagesrv"
|
||||
"isle/jsonutil"
|
||||
"isle/secrets"
|
||||
@ -42,7 +43,7 @@ func writeBootstrapToStateDir(
|
||||
}
|
||||
|
||||
func coalesceDaemonConfigAndBootstrap(
|
||||
daemonConfig Config, hostBootstrap bootstrap.Bootstrap,
|
||||
daemonConfig daecommon.Config, hostBootstrap bootstrap.Bootstrap,
|
||||
) (
|
||||
bootstrap.Bootstrap, error,
|
||||
) {
|
||||
@ -88,7 +89,7 @@ type bootstrapDiff struct {
|
||||
}
|
||||
|
||||
func calcBootstrapDiff(
|
||||
daemonConfig Config,
|
||||
daemonConfig daecommon.Config,
|
||||
prevBootstrap, nextBootstrap bootstrap.Bootstrap,
|
||||
) (
|
||||
diff bootstrapDiff, err error,
|
||||
|
@ -4,6 +4,7 @@ import (
|
||||
"context"
|
||||
"fmt"
|
||||
"isle/bootstrap"
|
||||
"isle/daemon/daecommon"
|
||||
"isle/dnsmasq"
|
||||
"path/filepath"
|
||||
"sort"
|
||||
@ -13,7 +14,7 @@ import (
|
||||
)
|
||||
|
||||
func dnsmasqConfig(
|
||||
daemonConfig Config, hostBootstrap bootstrap.Bootstrap,
|
||||
daemonConfig daecommon.Config, hostBootstrap bootstrap.Bootstrap,
|
||||
) dnsmasq.ConfData {
|
||||
hostsSlice := make([]dnsmasq.ConfDataHost, 0, len(hostBootstrap.Hosts))
|
||||
for _, host := range hostBootstrap.Hosts {
|
||||
@ -37,7 +38,7 @@ func dnsmasqConfig(
|
||||
|
||||
func dnsmasqWriteConfig(
|
||||
runtimeDirPath string,
|
||||
daemonConfig Config,
|
||||
daemonConfig daecommon.Config,
|
||||
hostBootstrap bootstrap.Bootstrap,
|
||||
) (
|
||||
string, error,
|
||||
@ -57,7 +58,7 @@ func dnsmasqWriteConfig(
|
||||
func dnsmasqPmuxProcConfig(
|
||||
logger *mlog.Logger,
|
||||
runtimeDirPath, binDirPath string,
|
||||
daemonConfig Config,
|
||||
daemonConfig daecommon.Config,
|
||||
hostBootstrap bootstrap.Bootstrap,
|
||||
) (
|
||||
pmuxlib.ProcessConfig, error,
|
||||
|
@ -4,6 +4,7 @@ import (
|
||||
"context"
|
||||
"fmt"
|
||||
"isle/bootstrap"
|
||||
"isle/daemon/daecommon"
|
||||
"isle/garage"
|
||||
"isle/garage/garagesrv"
|
||||
"net"
|
||||
@ -23,7 +24,7 @@ func garageAdminClientLogger(logger *mlog.Logger) *mlog.Logger {
|
||||
// or it will _panic_ if there is no local instance configured.
|
||||
func newGarageAdminClient(
|
||||
logger *mlog.Logger,
|
||||
daemonConfig Config,
|
||||
daemonConfig daecommon.Config,
|
||||
adminToken string,
|
||||
hostBootstrap bootstrap.Bootstrap,
|
||||
) *garage.AdminClient {
|
||||
@ -43,7 +44,7 @@ func newGarageAdminClient(
|
||||
func waitForGarage(
|
||||
ctx context.Context,
|
||||
logger *mlog.Logger,
|
||||
daemonConfig Config,
|
||||
daemonConfig daecommon.Config,
|
||||
adminToken string,
|
||||
hostBootstrap bootstrap.Bootstrap,
|
||||
) error {
|
||||
@ -88,7 +89,7 @@ func waitForGarage(
|
||||
// This assumes that coalesceDaemonConfigAndBootstrap has already been called.
|
||||
func bootstrapGarageHostForAlloc(
|
||||
host bootstrap.Host,
|
||||
alloc ConfigStorageAllocation,
|
||||
alloc daecommon.ConfigStorageAllocation,
|
||||
) bootstrap.GarageHostInstance {
|
||||
|
||||
for _, inst := range host.Garage.Instances {
|
||||
@ -103,7 +104,7 @@ func bootstrapGarageHostForAlloc(
|
||||
func garageWriteChildConfig(
|
||||
rpcSecret, runtimeDirPath, adminToken string,
|
||||
hostBootstrap bootstrap.Bootstrap,
|
||||
alloc ConfigStorageAllocation,
|
||||
alloc daecommon.ConfigStorageAllocation,
|
||||
) (
|
||||
string, error,
|
||||
) {
|
||||
@ -147,7 +148,7 @@ func garagePmuxProcConfigs(
|
||||
ctx context.Context,
|
||||
logger *mlog.Logger,
|
||||
rpcSecret, runtimeDirPath, binDirPath string,
|
||||
daemonConfig Config,
|
||||
daemonConfig daecommon.Config,
|
||||
adminToken string,
|
||||
hostBootstrap bootstrap.Bootstrap,
|
||||
) (
|
||||
@ -188,7 +189,7 @@ func garagePmuxProcConfigs(
|
||||
func garageApplyLayout(
|
||||
ctx context.Context,
|
||||
logger *mlog.Logger,
|
||||
daemonConfig Config,
|
||||
daemonConfig daecommon.Config,
|
||||
adminToken string,
|
||||
hostBootstrap bootstrap.Bootstrap,
|
||||
) error {
|
||||
|
@ -4,6 +4,7 @@ import (
|
||||
"context"
|
||||
"fmt"
|
||||
"isle/bootstrap"
|
||||
"isle/daemon/daecommon"
|
||||
"isle/yamlutil"
|
||||
"net"
|
||||
"path/filepath"
|
||||
@ -47,7 +48,7 @@ func waitForNebula(
|
||||
}
|
||||
|
||||
func nebulaConfig(
|
||||
daemonConfig Config,
|
||||
daemonConfig daecommon.Config,
|
||||
hostBootstrap bootstrap.Bootstrap,
|
||||
) (
|
||||
map[string]any, error,
|
||||
@ -136,7 +137,7 @@ func nebulaConfig(
|
||||
|
||||
func nebulaWriteConfig(
|
||||
runtimeDirPath string,
|
||||
daemonConfig Config,
|
||||
daemonConfig daecommon.Config,
|
||||
hostBootstrap bootstrap.Bootstrap,
|
||||
) (
|
||||
string, error,
|
||||
@ -157,7 +158,7 @@ func nebulaWriteConfig(
|
||||
|
||||
func nebulaPmuxProcConfig(
|
||||
runtimeDirPath, binDirPath string,
|
||||
daemonConfig Config,
|
||||
daemonConfig daecommon.Config,
|
||||
hostBootstrap bootstrap.Bootstrap,
|
||||
) (
|
||||
pmuxlib.ProcessConfig, error,
|
||||
|
@ -4,6 +4,7 @@ import (
|
||||
"context"
|
||||
"fmt"
|
||||
"isle/bootstrap"
|
||||
"isle/daemon/daecommon"
|
||||
|
||||
"code.betamike.com/micropelago/pmux/pmuxlib"
|
||||
)
|
||||
@ -11,7 +12,7 @@ import (
|
||||
func (c *Children) newPmuxConfig(
|
||||
ctx context.Context,
|
||||
garageRPCSecret, binDirPath string,
|
||||
daemonConfig Config,
|
||||
daemonConfig daecommon.Config,
|
||||
garageAdminToken string,
|
||||
hostBootstrap bootstrap.Bootstrap,
|
||||
) (
|
||||
@ -67,7 +68,7 @@ func (c *Children) newPmuxConfig(
|
||||
|
||||
func (c *Children) postPmuxInit(
|
||||
ctx context.Context,
|
||||
daemonConfig Config,
|
||||
daemonConfig daecommon.Config,
|
||||
garageAdminToken string,
|
||||
hostBootstrap bootstrap.Bootstrap,
|
||||
) error {
|
||||
|
@ -5,6 +5,7 @@ import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"isle/bootstrap"
|
||||
"isle/daemon/daecommon"
|
||||
"isle/secrets"
|
||||
|
||||
"code.betamike.com/micropelago/pmux/pmuxlib"
|
||||
@ -18,7 +19,7 @@ import (
|
||||
// - garage (0 or more, depending on configured storage allocations)
|
||||
type Children struct {
|
||||
logger *mlog.Logger
|
||||
daemonConfig Config
|
||||
daemonConfig daecommon.Config
|
||||
opts Opts
|
||||
|
||||
pmux *pmuxlib.Pmux
|
||||
@ -31,7 +32,7 @@ func NewChildren(
|
||||
logger *mlog.Logger,
|
||||
binDirPath string,
|
||||
secretsStore secrets.Store,
|
||||
daemonConfig Config,
|
||||
daemonConfig daecommon.Config,
|
||||
garageAdminToken string,
|
||||
hostBootstrap bootstrap.Bootstrap,
|
||||
opts *Opts,
|
||||
@ -41,7 +42,7 @@ func NewChildren(
|
||||
opts = opts.withDefaults()
|
||||
|
||||
logger.Info(ctx, "Loading secrets")
|
||||
garageRPCSecret, err := getGarageRPCSecret(ctx, secretsStore)
|
||||
garageRPCSecret, err := daecommon.GetGarageRPCSecret(ctx, secretsStore)
|
||||
if err != nil && !errors.Is(err, secrets.ErrNotFound) {
|
||||
return nil, fmt.Errorf("loading garage RPC secret: %w", err)
|
||||
}
|
||||
|
@ -1,183 +1,81 @@
|
||||
package daemon
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"isle/yamlutil"
|
||||
"io/fs"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strconv"
|
||||
|
||||
"github.com/imdario/mergo"
|
||||
"gopkg.in/yaml.v3"
|
||||
"slices"
|
||||
"strings"
|
||||
"sync"
|
||||
)
|
||||
|
||||
func defaultConfigPath(appDirPath string) string {
|
||||
return filepath.Join(appDirPath, "etc", "daemon.yml")
|
||||
}
|
||||
func getDefaultHTTPSocketDirPath() string {
|
||||
path, err := firstExistingDir(
|
||||
"/tmp",
|
||||
|
||||
type ConfigTun struct {
|
||||
Device string `yaml:"device"`
|
||||
}
|
||||
|
||||
type ConfigFirewall struct {
|
||||
Conntrack ConfigConntrack `yaml:"conntrack"`
|
||||
Outbound []ConfigFirewallRule `yaml:"outbound"`
|
||||
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 {
|
||||
Port string `yaml:"port,omitempty"`
|
||||
Code string `yaml:"code,omitempty"`
|
||||
Proto string `yaml:"proto,omitempty"`
|
||||
Host string `yaml:"host,omitempty"`
|
||||
Group string `yaml:"group,omitempty"`
|
||||
Groups []string `yaml:"groups,omitempty"`
|
||||
CIDR string `yaml:"cidr,omitempty"`
|
||||
CASha string `yaml:"ca_sha,omitempty"`
|
||||
CAName string `yaml:"ca_name,omitempty"`
|
||||
}
|
||||
|
||||
// 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"`
|
||||
S3APIPort int `yaml:"s3_api_port"`
|
||||
RPCPort int `yaml:"rpc_port"`
|
||||
AdminPort int `yaml:"admin_port"`
|
||||
|
||||
// Zone is a secret option which makes it easier to test garage bugs, but
|
||||
// which we don't want users to otherwise know about.
|
||||
Zone string `yaml:"zone"`
|
||||
}
|
||||
|
||||
// Config describes the structure of the daemon config file.
|
||||
type Config struct {
|
||||
DNS struct {
|
||||
Resolvers []string `yaml:"resolvers"`
|
||||
} `yaml:"dns"`
|
||||
VPN struct {
|
||||
PublicAddr string `yaml:"public_addr"`
|
||||
Firewall ConfigFirewall `yaml:"firewall"`
|
||||
Tun ConfigTun `yaml:"tun"`
|
||||
} `yaml:"vpn"`
|
||||
Storage struct {
|
||||
Allocations []ConfigStorageAllocation
|
||||
} `yaml:"storage"`
|
||||
}
|
||||
|
||||
func (c *Config) fillDefaults() {
|
||||
|
||||
var firewallGarageInbound []ConfigFirewallRule
|
||||
|
||||
for i := range c.Storage.Allocations {
|
||||
if c.Storage.Allocations[i].RPCPort == 0 {
|
||||
c.Storage.Allocations[i].RPCPort = 3900 + (i * 10)
|
||||
}
|
||||
|
||||
if c.Storage.Allocations[i].S3APIPort == 0 {
|
||||
c.Storage.Allocations[i].S3APIPort = 3901 + (i * 10)
|
||||
}
|
||||
|
||||
if c.Storage.Allocations[i].AdminPort == 0 {
|
||||
c.Storage.Allocations[i].AdminPort = 3902 + (i * 10)
|
||||
}
|
||||
|
||||
alloc := c.Storage.Allocations[i]
|
||||
|
||||
firewallGarageInbound = append(
|
||||
firewallGarageInbound,
|
||||
ConfigFirewallRule{
|
||||
Port: strconv.Itoa(alloc.S3APIPort),
|
||||
Proto: "tcp",
|
||||
Host: "any",
|
||||
},
|
||||
ConfigFirewallRule{
|
||||
Port: strconv.Itoa(alloc.RPCPort),
|
||||
Proto: "tcp",
|
||||
Host: "any",
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
c.VPN.Firewall.Inbound = append(
|
||||
c.VPN.Firewall.Inbound,
|
||||
firewallGarageInbound...,
|
||||
// TODO it's possible the daemon process can't actually write to these
|
||||
"/run",
|
||||
"/var/run",
|
||||
"/dev/shm",
|
||||
)
|
||||
}
|
||||
|
||||
// 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)
|
||||
panic(fmt.Sprintf("Failed to find directory for HTTP socket: %v", 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
|
||||
return path
|
||||
}
|
||||
|
||||
// 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,
|
||||
) {
|
||||
// HTTPSocketPath returns the path to the daemon's HTTP socket which is used for
|
||||
// RPC and other functionality.
|
||||
var HTTPSocketPath = sync.OnceValue(func() string {
|
||||
return envOr(
|
||||
"ISLE_DAEMON_HTTP_SOCKET_PATH",
|
||||
func() string {
|
||||
return filepath.Join(
|
||||
getDefaultHTTPSocketDirPath(), "isle-daemon.sock",
|
||||
)
|
||||
},
|
||||
)
|
||||
})
|
||||
|
||||
defaultConfigPath := defaultConfigPath(appDirPath)
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// Jigs
|
||||
|
||||
var fullDaemon map[string]interface{}
|
||||
|
||||
if err := yamlutil.LoadYamlFile(&fullDaemon, defaultConfigPath); err != nil {
|
||||
return Config{}, fmt.Errorf("parsing default daemon config file: %w", err)
|
||||
func envOr(name string, fallback func() string) string {
|
||||
if v := os.Getenv(name); v != "" {
|
||||
return v
|
||||
}
|
||||
return fallback()
|
||||
}
|
||||
|
||||
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)
|
||||
func firstExistingDir(paths ...string) (string, error) {
|
||||
var errs []error
|
||||
for _, path := range paths {
|
||||
stat, err := os.Stat(path)
|
||||
switch {
|
||||
case errors.Is(err, fs.ErrExist):
|
||||
continue
|
||||
case err != nil:
|
||||
errs = append(
|
||||
errs, fmt.Errorf("checking if path %q exists: %w", path, err),
|
||||
)
|
||||
case !stat.IsDir():
|
||||
errs = append(
|
||||
errs, fmt.Errorf("path %q exists but is not a directory", path),
|
||||
)
|
||||
default:
|
||||
return path, nil
|
||||
}
|
||||
}
|
||||
|
||||
fullDaemonB, err := yaml.Marshal(fullDaemon)
|
||||
|
||||
if err != nil {
|
||||
return Config{}, fmt.Errorf("yaml marshaling: %w", err)
|
||||
err := fmt.Errorf(
|
||||
"no directory found at any of the following paths: %s",
|
||||
strings.Join(paths, ", "),
|
||||
)
|
||||
if len(errs) > 0 {
|
||||
err = errors.Join(slices.Insert(errs, 0, 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
|
||||
return "", err
|
||||
}
|
||||
|
183
go/daemon/daecommon/config.go
Normal file
183
go/daemon/daecommon/config.go
Normal file
@ -0,0 +1,183 @@
|
||||
package daecommon
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io"
|
||||
"isle/yamlutil"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strconv"
|
||||
|
||||
"github.com/imdario/mergo"
|
||||
"gopkg.in/yaml.v3"
|
||||
)
|
||||
|
||||
func defaultConfigPath(appDirPath string) string {
|
||||
return filepath.Join(appDirPath, "etc", "daemon.yml")
|
||||
}
|
||||
|
||||
type ConfigTun struct {
|
||||
Device string `yaml:"device"`
|
||||
}
|
||||
|
||||
type ConfigFirewall struct {
|
||||
Conntrack ConfigConntrack `yaml:"conntrack"`
|
||||
Outbound []ConfigFirewallRule `yaml:"outbound"`
|
||||
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 {
|
||||
Port string `yaml:"port,omitempty"`
|
||||
Code string `yaml:"code,omitempty"`
|
||||
Proto string `yaml:"proto,omitempty"`
|
||||
Host string `yaml:"host,omitempty"`
|
||||
Group string `yaml:"group,omitempty"`
|
||||
Groups []string `yaml:"groups,omitempty"`
|
||||
CIDR string `yaml:"cidr,omitempty"`
|
||||
CASha string `yaml:"ca_sha,omitempty"`
|
||||
CAName string `yaml:"ca_name,omitempty"`
|
||||
}
|
||||
|
||||
// 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"`
|
||||
S3APIPort int `yaml:"s3_api_port"`
|
||||
RPCPort int `yaml:"rpc_port"`
|
||||
AdminPort int `yaml:"admin_port"`
|
||||
|
||||
// Zone is a secret option which makes it easier to test garage bugs, but
|
||||
// which we don't want users to otherwise know about.
|
||||
Zone string `yaml:"zone"`
|
||||
}
|
||||
|
||||
// Config describes the structure of the daemon config file.
|
||||
type Config struct {
|
||||
DNS struct {
|
||||
Resolvers []string `yaml:"resolvers"`
|
||||
} `yaml:"dns"`
|
||||
VPN struct {
|
||||
PublicAddr string `yaml:"public_addr"`
|
||||
Firewall ConfigFirewall `yaml:"firewall"`
|
||||
Tun ConfigTun `yaml:"tun"`
|
||||
} `yaml:"vpn"`
|
||||
Storage struct {
|
||||
Allocations []ConfigStorageAllocation
|
||||
} `yaml:"storage"`
|
||||
}
|
||||
|
||||
func (c *Config) fillDefaults() {
|
||||
|
||||
var firewallGarageInbound []ConfigFirewallRule
|
||||
|
||||
for i := range c.Storage.Allocations {
|
||||
if c.Storage.Allocations[i].RPCPort == 0 {
|
||||
c.Storage.Allocations[i].RPCPort = 3900 + (i * 10)
|
||||
}
|
||||
|
||||
if c.Storage.Allocations[i].S3APIPort == 0 {
|
||||
c.Storage.Allocations[i].S3APIPort = 3901 + (i * 10)
|
||||
}
|
||||
|
||||
if c.Storage.Allocations[i].AdminPort == 0 {
|
||||
c.Storage.Allocations[i].AdminPort = 3902 + (i * 10)
|
||||
}
|
||||
|
||||
alloc := c.Storage.Allocations[i]
|
||||
|
||||
firewallGarageInbound = append(
|
||||
firewallGarageInbound,
|
||||
ConfigFirewallRule{
|
||||
Port: strconv.Itoa(alloc.S3APIPort),
|
||||
Proto: "tcp",
|
||||
Host: "any",
|
||||
},
|
||||
ConfigFirewallRule{
|
||||
Port: strconv.Itoa(alloc.RPCPort),
|
||||
Proto: "tcp",
|
||||
Host: "any",
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
c.VPN.Firewall.Inbound = append(
|
||||
c.VPN.Firewall.Inbound,
|
||||
firewallGarageInbound...,
|
||||
)
|
||||
}
|
||||
|
||||
// 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
|
||||
}
|
3
go/daemon/daecommon/daecommon.go
Normal file
3
go/daemon/daecommon/daecommon.go
Normal file
@ -0,0 +1,3 @@
|
||||
// Package daecommon holds types and functionality which are required the daemon
|
||||
// package and other of its subpackages.
|
||||
package daecommon
|
45
go/daemon/daecommon/env.go
Normal file
45
go/daemon/daecommon/env.go
Normal file
@ -0,0 +1,45 @@
|
||||
package daecommon
|
||||
|
||||
import (
|
||||
"os"
|
||||
"path/filepath"
|
||||
"sync"
|
||||
|
||||
"github.com/adrg/xdg"
|
||||
)
|
||||
|
||||
// EnvVars are variables which are derived based on the environment which the
|
||||
// process is running in.
|
||||
type EnvVars struct {
|
||||
RuntimeDirPath string
|
||||
StateDirPath string
|
||||
}
|
||||
|
||||
// GetEnvVars will return the EnvVars of the current processes, as determined by
|
||||
// the process's environment.
|
||||
var GetEnvVars = sync.OnceValue(func() (v EnvVars) {
|
||||
// RUNTIME_DIRECTORY/STATE_DIRECTORY are used by the systemd service in
|
||||
// conjunction with the RuntimeDirectory/StateDirectory directives.
|
||||
|
||||
v.RuntimeDirPath = envOr(
|
||||
"RUNTIME_DIRECTORY",
|
||||
func() string { return filepath.Join(xdg.RuntimeDir, "isle") },
|
||||
)
|
||||
|
||||
v.StateDirPath = envOr(
|
||||
"STATE_DIRECTORY",
|
||||
func() string { return filepath.Join(xdg.StateHome, "isle") },
|
||||
)
|
||||
|
||||
return
|
||||
})
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// Jigs
|
||||
|
||||
func envOr(name string, fallback func() string) string {
|
||||
if v := os.Getenv(name); v != "" {
|
||||
return v
|
||||
}
|
||||
return fallback()
|
||||
}
|
51
go/daemon/daecommon/secrets.go
Normal file
51
go/daemon/daecommon/secrets.go
Normal file
@ -0,0 +1,51 @@
|
||||
package daecommon
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"isle/garage"
|
||||
"isle/nebula"
|
||||
"isle/secrets"
|
||||
)
|
||||
|
||||
const (
|
||||
secretsNSNebula = "nebula"
|
||||
secretsNSGarage = "garage"
|
||||
)
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// Nebula-related secrets
|
||||
|
||||
// IDs and Get/Set functions for nebula-related secrets.
|
||||
var (
|
||||
NebulaCASigningPrivateKeySecretID = secrets.NewID(secretsNSNebula, "ca-signing-private-key")
|
||||
|
||||
GetNebulaCASigningPrivateKey, SetNebulaCASigningPrivateKey = secrets.GetSetFunctions[nebula.SigningPrivateKey](
|
||||
NebulaCASigningPrivateKeySecretID,
|
||||
)
|
||||
)
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// Garage-related secrets
|
||||
|
||||
func garageS3APIBucketCredentialsSecretID(credsName string) secrets.ID {
|
||||
return secrets.NewID(
|
||||
secretsNSGarage, fmt.Sprintf("s3-api-bucket-credentials-%s", credsName),
|
||||
)
|
||||
}
|
||||
|
||||
// IDs and Get/Set functions for garage-related secrets.
|
||||
var (
|
||||
GarageRPCSecretSecretID = secrets.NewID(secretsNSGarage, "rpc-secret")
|
||||
GarageS3APIGlobalBucketCredentialsSecretID = garageS3APIBucketCredentialsSecretID(
|
||||
garage.GlobalBucketS3APICredentialsName,
|
||||
)
|
||||
|
||||
GetGarageRPCSecret, SetGarageRPCSecret = secrets.GetSetFunctions[string](
|
||||
GarageRPCSecretSecretID,
|
||||
)
|
||||
|
||||
GetGarageS3APIGlobalBucketCredentials,
|
||||
SetGarageS3APIGlobalBucketCredentials = secrets.GetSetFunctions[garage.S3APICredentials](
|
||||
GarageS3APIGlobalBucketCredentialsSecretID,
|
||||
)
|
||||
)
|
@ -12,6 +12,7 @@ import (
|
||||
"io"
|
||||
"io/fs"
|
||||
"isle/bootstrap"
|
||||
"isle/daemon/daecommon"
|
||||
"isle/jsonutil"
|
||||
"isle/nebula"
|
||||
"isle/secrets"
|
||||
@ -33,7 +34,8 @@ type Opts struct {
|
||||
// will be directed to.
|
||||
Stdout, Stderr io.Writer
|
||||
|
||||
EnvVars EnvVars // Defaults to that returned by GetEnvVars.
|
||||
// Defaults to that returned by daecommon.GetEnvVars.
|
||||
EnvVars daecommon.EnvVars
|
||||
}
|
||||
|
||||
func (o *Opts) withDefaults() *Opts {
|
||||
@ -49,8 +51,8 @@ func (o *Opts) withDefaults() *Opts {
|
||||
o.Stderr = os.Stderr
|
||||
}
|
||||
|
||||
if o.EnvVars == (EnvVars{}) {
|
||||
o.EnvVars = GetEnvVars()
|
||||
if o.EnvVars == (daecommon.EnvVars{}) {
|
||||
o.EnvVars = daecommon.GetEnvVars()
|
||||
}
|
||||
|
||||
return o
|
||||
@ -83,7 +85,7 @@ var _ RPC = (*Daemon)(nil)
|
||||
// canceled.
|
||||
type Daemon struct {
|
||||
logger *mlog.Logger
|
||||
daemonConfig Config
|
||||
daemonConfig daecommon.Config
|
||||
envBinDirPath string
|
||||
opts *Opts
|
||||
|
||||
@ -103,7 +105,7 @@ type Daemon struct {
|
||||
func NewDaemon(
|
||||
ctx context.Context,
|
||||
logger *mlog.Logger,
|
||||
daemonConfig Config,
|
||||
daemonConfig daecommon.Config,
|
||||
envBinDirPath string,
|
||||
opts *Opts,
|
||||
) (
|
||||
@ -121,7 +123,7 @@ func NewDaemon(
|
||||
bootstrapFilePath = bootstrap.StateDirPath(d.opts.EnvVars.StateDirPath)
|
||||
)
|
||||
|
||||
if err := d.opts.EnvVars.init(); err != nil {
|
||||
if err := createDirs(d.opts.EnvVars); err != nil {
|
||||
return nil, fmt.Errorf("initializing daemon directories: %w", err)
|
||||
}
|
||||
|
||||
@ -151,6 +153,27 @@ func NewDaemon(
|
||||
return d, nil
|
||||
}
|
||||
|
||||
func createDirs(e daecommon.EnvVars) error {
|
||||
var errs []error
|
||||
if err := mkDir(e.RuntimeDirPath); err != nil {
|
||||
errs = append(errs, fmt.Errorf(
|
||||
"creating runtime directory %q: %w",
|
||||
e.RuntimeDirPath,
|
||||
err,
|
||||
))
|
||||
}
|
||||
|
||||
if err := mkDir(e.StateDirPath); err != nil {
|
||||
errs = append(errs, fmt.Errorf(
|
||||
"creating state directory %q: %w",
|
||||
e.StateDirPath,
|
||||
err,
|
||||
))
|
||||
}
|
||||
|
||||
return errors.Join(errs...)
|
||||
}
|
||||
|
||||
// initialize must be called with d.l write lock held.
|
||||
func (d *Daemon) initialize(
|
||||
ctx context.Context, currBootstrap bootstrap.Bootstrap,
|
||||
@ -312,7 +335,9 @@ func (d *Daemon) postInit(ctx context.Context) error {
|
||||
//
|
||||
// TODO this is pretty hacky, but there doesn't seem to be a better way to
|
||||
// manage it at the moment.
|
||||
_, err := getGarageS3APIGlobalBucketCredentials(ctx, d.secretsStore)
|
||||
_, err := daecommon.GetGarageS3APIGlobalBucketCredentials(
|
||||
ctx, d.secretsStore,
|
||||
)
|
||||
if errors.Is(err, secrets.ErrNotFound) {
|
||||
d.logger.Info(ctx, "Initializing garage shared global bucket")
|
||||
garageGlobalBucketCreds, err := garageInitializeGlobalBucket(
|
||||
@ -326,7 +351,7 @@ func (d *Daemon) postInit(ctx context.Context) error {
|
||||
return fmt.Errorf("initializing global bucket: %w", err)
|
||||
}
|
||||
|
||||
err = setGarageS3APIGlobalBucketCredentials(
|
||||
err = daecommon.SetGarageS3APIGlobalBucketCredentials(
|
||||
ctx, d.secretsStore, garageGlobalBucketCreds,
|
||||
)
|
||||
if err != nil {
|
||||
@ -406,12 +431,14 @@ func (d *Daemon) CreateNetwork(
|
||||
garageRPCSecret = randStr(32)
|
||||
)
|
||||
|
||||
err = setGarageRPCSecret(ctx, d.secretsStore, garageRPCSecret)
|
||||
err = daecommon.SetGarageRPCSecret(ctx, d.secretsStore, garageRPCSecret)
|
||||
if err != nil {
|
||||
return fmt.Errorf("setting garage RPC secret: %w", err)
|
||||
}
|
||||
|
||||
err = setNebulaCASigningPrivateKey(ctx, d.secretsStore, nebulaCACreds.SigningPrivateKey)
|
||||
err = daecommon.SetNebulaCASigningPrivateKey(
|
||||
ctx, d.secretsStore, nebulaCACreds.SigningPrivateKey,
|
||||
)
|
||||
if err != nil {
|
||||
return fmt.Errorf("setting nebula CA signing key secret: %w", err)
|
||||
}
|
||||
@ -679,7 +706,7 @@ func (d *Daemon) CreateHost(
|
||||
}
|
||||
// TODO if the ip is given, check that it's not already in use.
|
||||
|
||||
caSigningPrivateKey, err := getNebulaCASigningPrivateKey(
|
||||
caSigningPrivateKey, err := daecommon.GetNebulaCASigningPrivateKey(
|
||||
ctx, d.secretsStore,
|
||||
)
|
||||
if err != nil {
|
||||
@ -701,12 +728,14 @@ func (d *Daemon) CreateHost(
|
||||
}
|
||||
|
||||
secretsIDs := []secrets.ID{
|
||||
garageRPCSecretSecretID,
|
||||
garageS3APIGlobalBucketCredentialsSecretID,
|
||||
daecommon.GarageRPCSecretSecretID,
|
||||
daecommon.GarageS3APIGlobalBucketCredentialsSecretID,
|
||||
}
|
||||
|
||||
if opts.CanCreateHosts {
|
||||
secretsIDs = append(secretsIDs, nebulaCASigningPrivateKeySecretID)
|
||||
secretsIDs = append(
|
||||
secretsIDs, daecommon.NebulaCASigningPrivateKeySecretID,
|
||||
)
|
||||
}
|
||||
|
||||
if joiningBootstrap.Secrets, err = secrets.Export(
|
||||
@ -760,7 +789,7 @@ func (d *Daemon) CreateNebulaCertificate(
|
||||
}
|
||||
ip := host.IP()
|
||||
|
||||
caSigningPrivateKey, err := getNebulaCASigningPrivateKey(
|
||||
caSigningPrivateKey, err := daecommon.GetNebulaCASigningPrivateKey(
|
||||
ctx, d.secretsStore,
|
||||
)
|
||||
if err != nil {
|
||||
|
130
go/daemon/env.go
130
go/daemon/env.go
@ -1,130 +0,0 @@
|
||||
package daemon
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"io/fs"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"slices"
|
||||
"strings"
|
||||
"sync"
|
||||
|
||||
"github.com/adrg/xdg"
|
||||
)
|
||||
|
||||
// EnvVars are variables which are derived based on the environment which the
|
||||
// process is running in.
|
||||
type EnvVars struct {
|
||||
RuntimeDirPath string
|
||||
StateDirPath string
|
||||
}
|
||||
|
||||
func (e EnvVars) init() error {
|
||||
var errs []error
|
||||
if err := mkDir(e.RuntimeDirPath); err != nil {
|
||||
errs = append(errs, fmt.Errorf(
|
||||
"creating runtime directory %q: %w",
|
||||
e.RuntimeDirPath,
|
||||
err,
|
||||
))
|
||||
}
|
||||
|
||||
if err := mkDir(e.StateDirPath); err != nil {
|
||||
errs = append(errs, fmt.Errorf(
|
||||
"creating state directory %q: %w",
|
||||
e.StateDirPath,
|
||||
err,
|
||||
))
|
||||
}
|
||||
|
||||
return errors.Join(errs...)
|
||||
}
|
||||
|
||||
func getDefaultHTTPSocketDirPath() string {
|
||||
path, err := firstExistingDir(
|
||||
"/tmp",
|
||||
|
||||
// TODO it's possible the daemon process can't actually write to these
|
||||
"/run",
|
||||
"/var/run",
|
||||
"/dev/shm",
|
||||
)
|
||||
if err != nil {
|
||||
panic(fmt.Sprintf("Failed to find directory for HTTP socket: %v", err))
|
||||
}
|
||||
|
||||
return path
|
||||
}
|
||||
|
||||
// HTTPSocketPath returns the path to the daemon's HTTP socket which is used for
|
||||
// RPC and other functionality.
|
||||
var HTTPSocketPath = sync.OnceValue(func() string {
|
||||
return envOr(
|
||||
"ISLE_DAEMON_HTTP_SOCKET_PATH",
|
||||
func() string {
|
||||
return filepath.Join(
|
||||
getDefaultHTTPSocketDirPath(), "isle-daemon.sock",
|
||||
)
|
||||
},
|
||||
)
|
||||
})
|
||||
|
||||
// GetEnvVars will return the EnvVars of the current processes, as determined by
|
||||
// the process's environment.
|
||||
var GetEnvVars = sync.OnceValue(func() (v EnvVars) {
|
||||
// RUNTIME_DIRECTORY/STATE_DIRECTORY are used by the systemd service in
|
||||
// conjunction with the RuntimeDirectory/StateDirectory directives.
|
||||
|
||||
v.RuntimeDirPath = envOr(
|
||||
"RUNTIME_DIRECTORY",
|
||||
func() string { return filepath.Join(xdg.RuntimeDir, "isle") },
|
||||
)
|
||||
|
||||
v.StateDirPath = envOr(
|
||||
"STATE_DIRECTORY",
|
||||
func() string { return filepath.Join(xdg.StateHome, "isle") },
|
||||
)
|
||||
|
||||
return
|
||||
})
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// Jigs
|
||||
|
||||
func envOr(name string, fallback func() string) string {
|
||||
if v := os.Getenv(name); v != "" {
|
||||
return v
|
||||
}
|
||||
return fallback()
|
||||
}
|
||||
|
||||
func firstExistingDir(paths ...string) (string, error) {
|
||||
var errs []error
|
||||
for _, path := range paths {
|
||||
stat, err := os.Stat(path)
|
||||
switch {
|
||||
case errors.Is(err, fs.ErrExist):
|
||||
continue
|
||||
case err != nil:
|
||||
errs = append(
|
||||
errs, fmt.Errorf("checking if path %q exists: %w", path, err),
|
||||
)
|
||||
case !stat.IsDir():
|
||||
errs = append(
|
||||
errs, fmt.Errorf("path %q exists but is not a directory", path),
|
||||
)
|
||||
default:
|
||||
return path, nil
|
||||
}
|
||||
}
|
||||
|
||||
err := fmt.Errorf(
|
||||
"no directory found at any of the following paths: %s",
|
||||
strings.Join(paths, ", "),
|
||||
)
|
||||
if len(errs) > 0 {
|
||||
err = errors.Join(slices.Insert(errs, 0, err)...)
|
||||
}
|
||||
return "", err
|
||||
}
|
@ -5,6 +5,7 @@ import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"isle/bootstrap"
|
||||
"isle/daemon/daecommon"
|
||||
"isle/garage"
|
||||
"isle/secrets"
|
||||
)
|
||||
@ -24,12 +25,14 @@ func (d *Daemon) getGarageClientParams(
|
||||
) (
|
||||
GarageClientParams, error,
|
||||
) {
|
||||
creds, err := getGarageS3APIGlobalBucketCredentials(ctx, d.secretsStore)
|
||||
creds, err := daecommon.GetGarageS3APIGlobalBucketCredentials(
|
||||
ctx, d.secretsStore,
|
||||
)
|
||||
if err != nil {
|
||||
return GarageClientParams{}, fmt.Errorf("getting garage global bucket creds: %w", err)
|
||||
}
|
||||
|
||||
rpcSecret, err := getGarageRPCSecret(ctx, d.secretsStore)
|
||||
rpcSecret, err := daecommon.GetGarageRPCSecret(ctx, d.secretsStore)
|
||||
if err != nil && !errors.Is(err, secrets.ErrNotFound) {
|
||||
return GarageClientParams{}, fmt.Errorf("getting garage rpc secret: %w", err)
|
||||
}
|
||||
|
@ -6,6 +6,7 @@ import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"isle/bootstrap"
|
||||
"isle/daemon/daecommon"
|
||||
"isle/garage"
|
||||
"isle/nebula"
|
||||
"path/filepath"
|
||||
@ -23,7 +24,7 @@ const (
|
||||
func garageInitializeGlobalBucket(
|
||||
ctx context.Context,
|
||||
logger *mlog.Logger,
|
||||
daemonConfig Config,
|
||||
daemonConfig daecommon.Config,
|
||||
adminToken string,
|
||||
hostBootstrap bootstrap.Bootstrap,
|
||||
) (
|
||||
|
@ -1,52 +0,0 @@
|
||||
package daemon
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"isle/garage"
|
||||
"isle/nebula"
|
||||
"isle/secrets"
|
||||
)
|
||||
|
||||
const (
|
||||
secretsNSNebula = "nebula"
|
||||
secretsNSGarage = "garage"
|
||||
)
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// Nebula-related secrets
|
||||
|
||||
var (
|
||||
nebulaCASigningPrivateKeySecretID = secrets.NewID(secretsNSNebula, "ca-signing-private-key")
|
||||
)
|
||||
|
||||
var getNebulaCASigningPrivateKey, setNebulaCASigningPrivateKey = secrets.GetSetFunctions[nebula.SigningPrivateKey](
|
||||
nebulaCASigningPrivateKeySecretID,
|
||||
)
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// Garage-related secrets
|
||||
|
||||
func garageS3APIBucketCredentialsSecretID(credsName string) secrets.ID {
|
||||
return secrets.NewID(
|
||||
secretsNSGarage, fmt.Sprintf("s3-api-bucket-credentials-%s", credsName),
|
||||
)
|
||||
}
|
||||
|
||||
var (
|
||||
garageRPCSecretSecretID = secrets.NewID(secretsNSGarage, "rpc-secret")
|
||||
garageS3APIGlobalBucketCredentialsSecretID = garageS3APIBucketCredentialsSecretID(
|
||||
garage.GlobalBucketS3APICredentialsName,
|
||||
)
|
||||
)
|
||||
|
||||
// Get/Set functions for garage-related secrets.
|
||||
var (
|
||||
getGarageRPCSecret, setGarageRPCSecret = secrets.GetSetFunctions[string](
|
||||
garageRPCSecretSecretID,
|
||||
)
|
||||
|
||||
getGarageS3APIGlobalBucketCredentials,
|
||||
setGarageS3APIGlobalBucketCredentials = secrets.GetSetFunctions[garage.S3APICredentials](
|
||||
garageS3APIGlobalBucketCredentialsSecretID,
|
||||
)
|
||||
)
|
Loading…
Reference in New Issue
Block a user