Compare commits

...

3 Commits

Author SHA1 Message Date
Brian Picciano
da100c6170 Fix garage Wait method not sleeping properly between tries 2022-11-13 16:49:23 +01:00
Brian Picciano
629a8ec9b2 Improve logging, introduce log levels
I switched to using mlog for logging, as opposed to writing directly to
Stderr. This gives us control over log levels, as well as coordination
so that we don't have multiple go-routines writing to stderr at the same
time.
2022-11-13 16:45:42 +01:00
Brian Picciano
90a30bef5e Fix startup sequence for daemon
Putting bootstrap host data into garage, and applying garage layout
diff, no longer happen simultaneously in the background. This was
causing some weird non-determinism in the startup which wasn't really
breaking anything, but made the logs harder to debug.

This also potentially fixes `waitForGarageAndNebula`, which was
neglecting to wait for nebula if there were allocations defined.
2022-11-13 14:55:25 +01:00
14 changed files with 251 additions and 88 deletions

View File

@ -7,7 +7,7 @@
pname = "cryptic-net-entrypoint"; pname = "cryptic-net-entrypoint";
version = "unstable"; version = "unstable";
src = ./src; src = ./src;
vendorSha256 = "sha256-1mHD0tmITlGjeo6F+Dvd2TdEPzxWtndy/J+uGHWKen4="; vendorSha256 = "sha256-TTTXwztv4xwF1uXcYoSka6HwgHwU1AnClF4fguXVtK4=";
subPackages = [ subPackages = [
"cmd/entrypoint" "cmd/entrypoint"
]; ];

View File

@ -6,10 +6,11 @@ import (
"cryptic-net/garage" "cryptic-net/garage"
"cryptic-net/nebula" "cryptic-net/nebula"
"fmt" "fmt"
"os"
"path/filepath" "path/filepath"
"strings" "strings"
"github.com/mediocregopher/mediocre-go-lib/v2/mctx"
"github.com/mediocregopher/mediocre-go-lib/v2/mlog"
"github.com/minio/minio-go/v7" "github.com/minio/minio-go/v7"
"gopkg.in/yaml.v3" "gopkg.in/yaml.v3"
) )
@ -85,6 +86,7 @@ func RemoveGarageBootstrapHost(
// stored in garage. // stored in garage.
func (b Bootstrap) GetGarageBootstrapHosts( func (b Bootstrap) GetGarageBootstrapHosts(
ctx context.Context, ctx context.Context,
logger *mlog.Logger,
) ( ) (
map[string]Host, error, map[string]Host, error,
) { ) {
@ -103,6 +105,8 @@ func (b Bootstrap) GetGarageBootstrapHosts(
for objInfo := range objInfoCh { for objInfo := range objInfoCh {
ctx := mctx.Annotate(ctx, "object-key", objInfo.Key)
if objInfo.Err != nil { if objInfo.Err != nil {
return nil, fmt.Errorf("listing objects: %w", objInfo.Err) return nil, fmt.Errorf("listing objects: %w", objInfo.Err)
} }
@ -132,7 +136,7 @@ func (b Bootstrap) GetGarageBootstrapHosts(
) )
if err != nil { if err != nil {
fmt.Fprintf(os.Stderr, "unwrapping signed public creds for %q: %v\n", objInfo.Key, err) logger.Warn(ctx, "unwrapping signed public creds", err)
continue continue
} }
@ -143,20 +147,20 @@ func (b Bootstrap) GetGarageBootstrapHosts(
) )
if err != nil { if err != nil {
fmt.Fprintf(os.Stderr, "invalid signed public creds for %q: %v\n", objInfo.Key, err) logger.Warn(ctx, "invalid signed public creds", err)
continue continue
} }
var hostPublicCreds nebula.HostPublicCredentials var hostPublicCreds nebula.HostPublicCredentials
if err := yaml.Unmarshal(hostPublicCredsB, &hostPublicCreds); err != nil { if err := yaml.Unmarshal(hostPublicCredsB, &hostPublicCreds); err != nil {
fmt.Fprintf(os.Stderr, "yaml unmarshaling signed public creds for %q: %v\n", objInfo.Key, err) logger.Warn(ctx, "yaml unmarshaling signed public creds", err)
continue continue
} }
err = nebula.ValidateSignature(hostPublicCreds.SigningKeyPEM, hostB, hostSig) err = nebula.ValidateSignature(hostPublicCreds.SigningKeyPEM, hostB, hostSig)
if err != nil { if err != nil {
fmt.Fprintf(os.Stderr, "invalid host data for %q: %v\n", objInfo.Key, err) logger.Warn(ctx, "invalid host data", err)
continue continue
} }

View File

@ -16,6 +16,7 @@ import (
"strings" "strings"
"code.betamike.com/cryptic-io/pmux/pmuxlib" "code.betamike.com/cryptic-io/pmux/pmuxlib"
"github.com/mediocregopher/mediocre-go-lib/v2/mlog"
) )
func randStr(l int) string { func randStr(l int) string {
@ -84,10 +85,17 @@ var subCmdAdminCreateNetwork = subCmd{
"Name of this host, which will be the first host in the network", "Name of this host, which will be the first host in the network",
) )
logLevelStr := flags.StringP(
"log-level", "l", "info",
`Maximum log level which should be output. Values can be "debug", "info", "warn", "error", "fatal". Does not apply to sub-processes`,
)
if err := flags.Parse(subCmdCtx.args); err != nil { if err := flags.Parse(subCmdCtx.args); err != nil {
return fmt.Errorf("parsing flags: %w", err) return fmt.Errorf("parsing flags: %w", err)
} }
ctx := subCmdCtx.ctx
if *dumpConfig { if *dumpConfig {
return daemon.CopyDefaultConfig(os.Stdout, envAppDirPath) return daemon.CopyDefaultConfig(os.Stdout, envAppDirPath)
} }
@ -96,6 +104,13 @@ var subCmdAdminCreateNetwork = subCmd{
return errors.New("--name, --domain, --ip-net, and --hostname are required") return errors.New("--name, --domain, --ip-net, and --hostname are required")
} }
logLevel := mlog.LevelFromString(*logLevelStr)
if logLevel == nil {
return fmt.Errorf("couldn't parse log level %q", *logLevelStr)
}
logger := subCmdCtx.logger.WithMaxLevel(logLevel.Int())
*domain = strings.TrimRight(strings.TrimLeft(*domain, "."), ".") *domain = strings.TrimRight(strings.TrimLeft(*domain, "."), ".")
ip, subnet, err := net.ParseCIDR(*ipNetStr) ip, subnet, err := net.ParseCIDR(*ipNetStr)
@ -107,7 +122,7 @@ var subCmdAdminCreateNetwork = subCmd{
return fmt.Errorf("invalid hostname %q: %w", *hostName, err) return fmt.Errorf("invalid hostname %q: %w", *hostName, err)
} }
runtimeDirCleanup, err := setupAndLockRuntimeDir() runtimeDirCleanup, err := setupAndLockRuntimeDir(ctx, logger)
if err != nil { if err != nil {
return fmt.Errorf("setting up runtime directory: %w", err) return fmt.Errorf("setting up runtime directory: %w", err)
} }
@ -191,10 +206,10 @@ var subCmdAdminCreateNetwork = subCmd{
), ),
} }
ctx, cancel := context.WithCancel(subCmdCtx.ctx) ctx, cancel := context.WithCancel(ctx)
pmuxDoneCh := make(chan struct{}) pmuxDoneCh := make(chan struct{})
fmt.Fprintln(os.Stderr, "starting child processes") logger.Info(ctx, "starting child processes")
go func() { go func() {
// NOTE both stdout and stderr are sent to stderr, so that the user // NOTE both stdout and stderr are sent to stderr, so that the user
// can pipe the resulting admin.yml to stdout. // can pipe the resulting admin.yml to stdout.
@ -204,22 +219,22 @@ var subCmdAdminCreateNetwork = subCmd{
defer func() { defer func() {
cancel() cancel()
fmt.Fprintln(os.Stderr, "waiting for child processes to exit") logger.Info(ctx, "waiting for child processes to exit")
<-pmuxDoneCh <-pmuxDoneCh
}() }()
fmt.Fprintln(os.Stderr, "waiting for garage instances to come online") logger.Info(ctx, "waiting for garage instances to come online")
if err := waitForGarageAndNebula(ctx, hostBootstrap, daemonConfig); err != nil { if err := waitForGarageAndNebula(ctx, logger, hostBootstrap, 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") logger.Info(ctx, "applying initial garage layout")
if err := garageApplyLayout(ctx, hostBootstrap, daemonConfig); err != nil { if err := garageApplyLayout(ctx, logger, hostBootstrap, 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") logger.Info(ctx, "initializing garage shared global bucket")
err = garageInitializeGlobalBucket(ctx, hostBootstrap, daemonConfig) err = garageInitializeGlobalBucket(ctx, logger, hostBootstrap, 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?")
@ -228,7 +243,7 @@ var subCmdAdminCreateNetwork = subCmd{
return fmt.Errorf("initializing garage shared global bucket: %w", err) return fmt.Errorf("initializing garage shared global bucket: %w", err)
} }
fmt.Fprintln(os.Stderr, "cluster initialized successfully, writing admin.yml to stdout") logger.Info(ctx, "cluster initialized successfully, writing admin.yml to stdout")
adm := admin.Admin{ adm := admin.Admin{
CreationParams: adminCreationParams, CreationParams: adminCreationParams,

View File

@ -14,6 +14,8 @@ import (
"cryptic-net/daemon" "cryptic-net/daemon"
"code.betamike.com/cryptic-io/pmux/pmuxlib" "code.betamike.com/cryptic-io/pmux/pmuxlib"
"github.com/mediocregopher/mediocre-go-lib/v2/mctx"
"github.com/mediocregopher/mediocre-go-lib/v2/mlog"
) )
// The daemon sub-command deals with starting an actual cryptic-net daemon // The daemon sub-command deals with starting an actual cryptic-net daemon
@ -40,6 +42,7 @@ import (
// is overwritten and true is returned. // is overwritten and true is returned.
func reloadBootstrap( func reloadBootstrap(
ctx context.Context, ctx context.Context,
logger *mlog.Logger,
hostBootstrap bootstrap.Bootstrap, hostBootstrap bootstrap.Bootstrap,
) ( ) (
bootstrap.Bootstrap, bool, error, bootstrap.Bootstrap, bool, error,
@ -47,7 +50,7 @@ func reloadBootstrap(
thisHost := hostBootstrap.ThisHost() thisHost := hostBootstrap.ThisHost()
newHosts, err := hostBootstrap.GetGarageBootstrapHosts(ctx) newHosts, err := hostBootstrap.GetGarageBootstrapHosts(ctx, logger)
if err != nil { if err != nil {
return bootstrap.Bootstrap{}, false, fmt.Errorf("getting hosts from garage: %w", err) return bootstrap.Bootstrap{}, false, fmt.Errorf("getting hosts from garage: %w", err)
} }
@ -85,6 +88,7 @@ func reloadBootstrap(
// updated boostrap info. // updated boostrap info.
func runDaemonPmuxOnce( func runDaemonPmuxOnce(
ctx context.Context, ctx context.Context,
logger *mlog.Logger,
hostBootstrap bootstrap.Bootstrap, hostBootstrap bootstrap.Bootstrap,
daemonConfig daemon.Config, daemonConfig daemon.Config,
) ( ) (
@ -116,8 +120,6 @@ func runDaemonPmuxOnce(
), ),
} }
doneCh := ctx.Done()
var wg sync.WaitGroup var wg sync.WaitGroup
defer wg.Wait() defer wg.Wait()
@ -130,18 +132,28 @@ func runDaemonPmuxOnce(
pmuxlib.Run(ctx, os.Stdout, os.Stderr, pmuxConfig) pmuxlib.Run(ctx, os.Stdout, os.Stderr, pmuxConfig)
}() }()
wg.Add(1) if err := waitForGarageAndNebula(ctx, logger, hostBootstrap, daemonConfig); err != nil {
go func() { return bootstrap.Bootstrap{}, fmt.Errorf("waiting for nebula/garage to start up: %w", err)
defer wg.Done() }
if err := waitForGarageAndNebula(ctx, hostBootstrap, daemonConfig); err != nil { err = doOnce(ctx, func(ctx context.Context) error {
fmt.Fprintf(os.Stderr, "aborted waiting for garage instances to be accessible: %v\n", err) if err := hostBootstrap.PutGarageBoostrapHost(ctx); err != nil {
return logger.Error(ctx, "updating host info in garage", err)
return err
} }
return nil
})
if err != nil {
return bootstrap.Bootstrap{}, fmt.Errorf("updating host info in garage: %w", err)
}
if len(daemonConfig.Storage.Allocations) > 0 {
err := doOnce(ctx, func(ctx context.Context) error { err := doOnce(ctx, func(ctx context.Context) error {
if err := hostBootstrap.PutGarageBoostrapHost(ctx); err != nil { if err := garageApplyLayout(ctx, logger, hostBootstrap, daemonConfig); err != nil {
fmt.Fprintf(os.Stderr, "updating host info in garage: %v\n", err) logger.Error(ctx, "applying garage layout", err)
return err return err
} }
@ -149,33 +161,8 @@ func runDaemonPmuxOnce(
}) })
if err != nil { if err != nil {
fmt.Fprintf(os.Stderr, "aborted updating host info in garage: %v\n", err) return bootstrap.Bootstrap{}, fmt.Errorf("applying garage layout: %w", err)
} }
}()
if len(daemonConfig.Storage.Allocations) > 0 {
wg.Add(1)
go func() {
defer wg.Done()
if err := waitForGarageAndNebula(ctx, hostBootstrap, 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 {
if err := garageApplyLayout(ctx, hostBootstrap, daemonConfig); err != nil {
fmt.Fprintf(os.Stderr, "applying garage layout: %v\n", err)
return err
}
return nil
})
if err != nil {
fmt.Fprintf(os.Stderr, "aborted applying garage layout: %v\n", err)
}
}()
} }
ticker := time.NewTicker(3 * time.Minute) ticker := time.NewTicker(3 * time.Minute)
@ -184,7 +171,7 @@ func runDaemonPmuxOnce(
for { for {
select { select {
case <-doneCh: case <-ctx.Done():
return bootstrap.Bootstrap{}, ctx.Err() return bootstrap.Bootstrap{}, ctx.Err()
case <-ticker.C: case <-ticker.C:
@ -196,7 +183,7 @@ func runDaemonPmuxOnce(
err error err error
) )
if hostBootstrap, changed, err = reloadBootstrap(ctx, hostBootstrap); err != nil { if hostBootstrap, changed, err = reloadBootstrap(ctx, logger, hostBootstrap); err != nil {
return bootstrap.Bootstrap{}, fmt.Errorf("reloading bootstrap: %w", err) return bootstrap.Bootstrap{}, fmt.Errorf("reloading bootstrap: %w", err)
} else if changed { } else if changed {
@ -229,15 +216,29 @@ var subCmdDaemon = subCmd{
`Path to a bootstrap.yml file. This only needs to be provided the first time the daemon is started, after that it is ignored. If the cryptic-net binary has a bootstrap built into it then this argument is always optional.`, `Path to a bootstrap.yml file. This only needs to be provided the first time the daemon is started, after that it is ignored. If the cryptic-net binary has a bootstrap built into it then this argument is always optional.`,
) )
logLevelStr := flags.StringP(
"log-level", "l", "info",
`Maximum log level which should be output. Values can be "debug", "info", "warn", "error", "fatal". Does not apply to sub-processes`,
)
if err := flags.Parse(subCmdCtx.args); err != nil { if err := flags.Parse(subCmdCtx.args); err != nil {
return fmt.Errorf("parsing flags: %w", err) return fmt.Errorf("parsing flags: %w", err)
} }
ctx := subCmdCtx.ctx
if *dumpConfig { if *dumpConfig {
return daemon.CopyDefaultConfig(os.Stdout, envAppDirPath) return daemon.CopyDefaultConfig(os.Stdout, envAppDirPath)
} }
runtimeDirCleanup, err := setupAndLockRuntimeDir() logLevel := mlog.LevelFromString(*logLevelStr)
if logLevel == nil {
return fmt.Errorf("couldn't parse log level %q", *logLevelStr)
}
logger := subCmdCtx.logger.WithMaxLevel(logLevel.Int())
runtimeDirCleanup, err := setupAndLockRuntimeDir(ctx, logger)
if err != nil { if err != nil {
return fmt.Errorf("setting up runtime directory: %w", err) return fmt.Errorf("setting up runtime directory: %w", err)
} }
@ -266,7 +267,11 @@ var subCmdDaemon = subCmd{
return false return false
} }
fmt.Fprintf(os.Stderr, "bootstrap file found at %q\n", path) logger.Info(
mctx.Annotate(ctx, "bootstrap-file-path", path),
"bootstrap file found",
)
hostBootstrapPath = path hostBootstrapPath = path
return true return true
} }
@ -306,7 +311,7 @@ var subCmdDaemon = subCmd{
for { for {
hostBootstrap, err = runDaemonPmuxOnce(subCmdCtx.ctx, hostBootstrap, daemonConfig) hostBootstrap, err = runDaemonPmuxOnce(ctx, logger, hostBootstrap, daemonConfig)
if errors.Is(err, context.Canceled) { if errors.Is(err, context.Canceled) {
return nil return nil

View File

@ -58,6 +58,6 @@ func doOnce(ctx context.Context, fn func(context.Context) error) error {
return ctxErr return ctxErr
} }
time.Sleep(250 * time.Millisecond) time.Sleep(1 * time.Second)
} }
} }

View File

@ -11,12 +11,19 @@ import (
"strconv" "strconv"
"code.betamike.com/cryptic-io/pmux/pmuxlib" "code.betamike.com/cryptic-io/pmux/pmuxlib"
"github.com/mediocregopher/mediocre-go-lib/v2/mlog"
) )
func garageAdminClientLogger(logger *mlog.Logger) *mlog.Logger {
return logger.WithNamespace("garage-admin-client")
}
// newGarageAdminClient will return an AdminClient for a local garage instance, // newGarageAdminClient will return an AdminClient for a local garage instance,
// or it will _panic_ if there is no local instance configured. // or it will _panic_ if there is no local instance configured.
func newGarageAdminClient( func newGarageAdminClient(
hostBootstrap bootstrap.Bootstrap, daemonConfig daemon.Config, logger *mlog.Logger,
hostBootstrap bootstrap.Bootstrap,
daemonConfig daemon.Config,
) *garage.AdminClient { ) *garage.AdminClient {
thisHost := hostBootstrap.ThisHost() thisHost := hostBootstrap.ThisHost()
@ -27,23 +34,31 @@ func newGarageAdminClient(
strconv.Itoa(daemonConfig.Storage.Allocations[0].AdminPort), strconv.Itoa(daemonConfig.Storage.Allocations[0].AdminPort),
), ),
hostBootstrap.Garage.AdminToken, hostBootstrap.Garage.AdminToken,
garageAdminClientLogger(logger),
) )
} }
func waitForGarageAndNebula( func waitForGarageAndNebula(
ctx context.Context, ctx context.Context,
logger *mlog.Logger,
hostBootstrap bootstrap.Bootstrap, hostBootstrap bootstrap.Bootstrap,
daemonConfig daemon.Config, daemonConfig daemon.Config,
) error { ) error {
if err := waitForNebula(ctx, hostBootstrap); err != nil {
return fmt.Errorf("waiting for nebula to start: %w", err)
}
allocs := daemonConfig.Storage.Allocations 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
if len(allocs) == 0 { if len(allocs) == 0 {
return waitForNebula(ctx, hostBootstrap) return nil
} }
logger = garageAdminClientLogger(logger)
for _, alloc := range allocs { for _, alloc := range allocs {
adminAddr := net.JoinHostPort( adminAddr := net.JoinHostPort(
@ -54,10 +69,11 @@ func waitForGarageAndNebula(
adminClient := garage.NewAdminClient( adminClient := garage.NewAdminClient(
adminAddr, adminAddr,
hostBootstrap.Garage.AdminToken, hostBootstrap.Garage.AdminToken,
logger,
) )
if err := adminClient.Wait(ctx); err != nil { if err := adminClient.Wait(ctx); err != nil {
return fmt.Errorf("waiting for instance %q to start up: %w", adminAddr, err) return fmt.Errorf("waiting for garage instance %q to start up: %w", adminAddr, err)
} }
} }
@ -161,12 +177,13 @@ func garagePmuxProcConfigs(
func garageInitializeGlobalBucket( func garageInitializeGlobalBucket(
ctx context.Context, ctx context.Context,
logger *mlog.Logger,
hostBootstrap bootstrap.Bootstrap, hostBootstrap bootstrap.Bootstrap,
daemonConfig daemon.Config, daemonConfig daemon.Config,
) error { ) error {
var ( var (
adminClient = newGarageAdminClient(hostBootstrap, daemonConfig) adminClient = newGarageAdminClient(logger, hostBootstrap, daemonConfig)
globalBucketCreds = hostBootstrap.Garage.GlobalBucketS3APICredentials globalBucketCreds = hostBootstrap.Garage.GlobalBucketS3APICredentials
) )
@ -223,12 +240,13 @@ func garageInitializeGlobalBucket(
func garageApplyLayout( func garageApplyLayout(
ctx context.Context, ctx context.Context,
logger *mlog.Logger,
hostBootstrap bootstrap.Bootstrap, hostBootstrap bootstrap.Bootstrap,
daemonConfig daemon.Config, daemonConfig daemon.Config,
) error { ) error {
var ( var (
adminClient = newGarageAdminClient(hostBootstrap, daemonConfig) adminClient = newGarageAdminClient(logger, hostBootstrap, daemonConfig)
thisHost = hostBootstrap.ThisHost() thisHost = hostBootstrap.ThisHost()
hostName = thisHost.Name hostName = thisHost.Name
allocs = daemonConfig.Storage.Allocations allocs = daemonConfig.Storage.Allocations

View File

@ -8,6 +8,7 @@ import (
"regexp" "regexp"
"sort" "sort"
"github.com/mediocregopher/mediocre-go-lib/v2/mlog"
"gopkg.in/yaml.v3" "gopkg.in/yaml.v3"
) )
@ -28,12 +29,32 @@ var subCmdHostsList = subCmd{
checkLock: true, checkLock: true,
do: func(subCmdCtx subCmdCtx) error { do: func(subCmdCtx subCmdCtx) error {
flags := subCmdCtx.flagSet(false)
logLevelStr := flags.StringP(
"log-level", "l", "info",
`Maximum log level which should be output. Values can be "debug", "info", "warn", "error", "fatal". Does not apply to sub-processes`,
)
if err := flags.Parse(subCmdCtx.args); err != nil {
return fmt.Errorf("parsing flags: %w", err)
}
ctx := subCmdCtx.ctx
logLevel := mlog.LevelFromString(*logLevelStr)
if logLevel == nil {
return fmt.Errorf("couldn't parse log level %q", *logLevelStr)
}
logger := subCmdCtx.logger.WithMaxLevel(logLevel.Int())
hostBootstrap, err := loadHostBootstrap() hostBootstrap, err := loadHostBootstrap()
if err != nil { if err != nil {
return fmt.Errorf("loading host bootstrap: %w", err) return fmt.Errorf("loading host bootstrap: %w", err)
} }
hostsMap, err := hostBootstrap.GetGarageBootstrapHosts(subCmdCtx.ctx) hostsMap, err := hostBootstrap.GetGarageBootstrapHosts(ctx, logger)
if err != nil { if err != nil {
return fmt.Errorf("retrieving hosts from garage: %w", err) return fmt.Errorf("retrieving hosts from garage: %w", err)
} }

View File

@ -0,0 +1,56 @@
package main
import (
"fmt"
"os"
"path"
"sync"
"github.com/mediocregopher/mediocre-go-lib/v2/mctx"
"github.com/mediocregopher/mediocre-go-lib/v2/mlog"
)
type logMsgHandler struct {
stderr *os.File
l sync.Mutex
}
func newLogMsgHandler() mlog.MessageHandler {
return &logMsgHandler{
stderr: os.Stderr,
}
}
func (h *logMsgHandler) Sync() error {
return h.stderr.Sync()
}
func (h *logMsgHandler) Handle(msg mlog.FullMessage) error {
h.l.Lock()
defer h.l.Unlock()
var namespaceStr string
if len(msg.Namespace) > 0 {
namespaceStr = "[" + path.Join(msg.Namespace...) + "] "
}
var annotationsStr string
if m := mctx.EvaluateAnnotations(msg.Context, nil).StringMap(); len(m) > 0 {
for k, v := range m {
annotationsStr += fmt.Sprintf(" %q=%q", k, v)
}
}
fmt.Fprintf(
h.stderr, "%s %s%s%s\n",
msg.Level.String(),
namespaceStr,
msg.Description,
annotationsStr,
)
return nil
}

View File

@ -2,13 +2,14 @@ package main
import ( import (
"context" "context"
"fmt"
"os" "os"
"os/signal" "os/signal"
"path/filepath" "path/filepath"
"syscall" "syscall"
"github.com/adrg/xdg" "github.com/adrg/xdg"
"github.com/mediocregopher/mediocre-go-lib/v2/mctx"
"github.com/mediocregopher/mediocre-go-lib/v2/mlog"
) )
// The purpose of this binary is to act as the entrypoint of the cryptic-net // The purpose of this binary is to act as the entrypoint of the cryptic-net
@ -32,6 +33,12 @@ var (
func main() { func main() {
logger := mlog.NewLogger(&mlog.LoggerOpts{
MessageHandler: newLogMsgHandler(),
MaxLevel: mlog.LevelInfo.Int(),
})
defer logger.Close()
ctx, cancel := context.WithCancel(context.Background()) ctx, cancel := context.WithCancel(context.Background())
signalCh := make(chan os.Signal, 2) signalCh := make(chan os.Signal, 2)
@ -40,18 +47,20 @@ func main() {
go func() { go func() {
sig := <-signalCh sig := <-signalCh
cancel() cancel()
fmt.Fprintf(os.Stderr, "got signal %v, will exit gracefully\n", sig)
ctx := mctx.Annotate(ctx, "signal", sig.String())
logger.Info(ctx, "got signal, exiting gracefully")
sig = <-signalCh sig = <-signalCh
fmt.Fprintf(os.Stderr, "second interrupt signal %v received, force quitting, there may be zombie children left behind, good luck!\n", sig)
os.Stderr.Sync() ctx = mctx.Annotate(ctx, "signal", sig.String())
os.Exit(1) logger.FatalString(ctx, "second signal received, force quitting, there may be zombie children left behind, good luck!")
}() }()
err := subCmdCtx{ err := subCmdCtx{
args: os.Args[1:], args: os.Args[1:],
ctx: ctx, ctx: ctx,
logger: logger,
}.doSubCmd( }.doSubCmd(
subCmdAdmin, subCmdAdmin,
subCmdDaemon, subCmdDaemon,
@ -61,6 +70,6 @@ func main() {
) )
if err != nil { if err != nil {
panic(err) logger.Fatal(ctx, "error running command", err)
} }
} }

View File

@ -1,12 +1,15 @@
package main package main
import ( import (
"context"
"errors" "errors"
"fmt" "fmt"
"io/fs" "io/fs"
"os" "os"
"path/filepath" "path/filepath"
"github.com/mediocregopher/mediocre-go-lib/v2/mctx"
"github.com/mediocregopher/mediocre-go-lib/v2/mlog"
"github.com/shirou/gopsutil/process" "github.com/shirou/gopsutil/process"
) )
@ -44,9 +47,10 @@ func writeLock() error {
} }
// returns a cleanup function which will clean up the created runtime directory. // returns a cleanup function which will clean up the created runtime directory.
func setupAndLockRuntimeDir() (func(), error) { func setupAndLockRuntimeDir(ctx context.Context, logger *mlog.Logger) (func(), error) {
fmt.Fprintf(os.Stderr, "will use runtime directory %q for temporary state\n", envRuntimeDirPath) ctx = mctx.Annotate(ctx, "runtime-dir-path", envRuntimeDirPath)
logger.Info(ctx, "will use runtime directory for temporary state")
if err := os.MkdirAll(envRuntimeDirPath, 0700); err != nil { if err := os.MkdirAll(envRuntimeDirPath, 0700); err != nil {
return nil, fmt.Errorf("creating directory %q: %w", envRuntimeDirPath, err) return nil, fmt.Errorf("creating directory %q: %w", envRuntimeDirPath, err)
@ -56,9 +60,9 @@ func setupAndLockRuntimeDir() (func(), error) {
} }
return func() { return func() {
fmt.Fprintf(os.Stderr, "cleaning up runtime directory %q\n", envRuntimeDirPath) logger.Info(ctx, "cleaning up runtime directory")
if err := os.RemoveAll(envRuntimeDirPath); err != nil { if err := os.RemoveAll(envRuntimeDirPath); err != nil {
fmt.Fprintf(os.Stderr, "error removing temporary directory %q: %v", envRuntimeDirPath, err) logger.Error(ctx, "removing temporary directory", err)
} }
}, nil }, nil
} }

View File

@ -6,6 +6,7 @@ import (
"os" "os"
"strings" "strings"
"github.com/mediocregopher/mediocre-go-lib/v2/mlog"
"github.com/spf13/pflag" "github.com/spf13/pflag"
) )
@ -15,7 +16,8 @@ type subCmdCtx struct {
args []string // command-line arguments, excluding the subCmd itself. args []string // command-line arguments, excluding the subCmd itself.
subCmdNames []string // names of subCmds so far, including this one subCmdNames []string // names of subCmds so far, including this one
ctx context.Context ctx context.Context
logger *mlog.Logger
} }
type subCmd struct { type subCmd struct {
@ -110,6 +112,7 @@ func (ctx subCmdCtx) doSubCmd(subCmds ...subCmd) error {
args: args, args: args,
subCmdNames: append(ctx.subCmdNames, subCmdName), subCmdNames: append(ctx.subCmdNames, subCmdName),
ctx: ctx.ctx, ctx: ctx.ctx,
logger: ctx.logger,
}) })
if err != nil { if err != nil {

View File

@ -7,7 +7,10 @@ import (
"fmt" "fmt"
"io" "io"
"net/http" "net/http"
"net/http/httputil"
"time" "time"
"github.com/mediocregopher/mediocre-go-lib/v2/mlog"
) )
// AdminClientError gets returned from AdminClient's Do method for non-200 // AdminClientError gets returned from AdminClient's Do method for non-200
@ -27,17 +30,21 @@ type AdminClient struct {
c *http.Client c *http.Client
addr string addr string
adminToken string adminToken string
logger *mlog.Logger
} }
// NewAdminClient initializes and returns an AdminClient which will use the // NewAdminClient initializes and returns an AdminClient which will use the
// given address and adminToken for all requests made. // given address and adminToken for all requests made.
func NewAdminClient(addr, adminToken string) *AdminClient { //
// If Logger is nil then logs will be suppressed.
func NewAdminClient(addr, adminToken string, logger *mlog.Logger) *AdminClient {
return &AdminClient{ return &AdminClient{
c: &http.Client{ c: &http.Client{
Transport: http.DefaultTransport.(*http.Transport).Clone(), Transport: http.DefaultTransport.(*http.Transport).Clone(),
}, },
addr: addr, addr: addr,
adminToken: adminToken, adminToken: adminToken,
logger: logger,
} }
} }
@ -68,11 +75,31 @@ func (c *AdminClient) Do(
req.Header.Set("Authorization", "Bearer "+c.adminToken) req.Header.Set("Authorization", "Bearer "+c.adminToken)
if c.logger.MaxLevel() >= mlog.LevelDebug.Int() {
reqB, err := httputil.DumpRequestOut(req, true)
if err != nil {
c.logger.Error(ctx, "failed to dump http request", err)
} else {
c.logger.Debug(ctx, "------ request ------\n"+string(reqB)+"\n")
}
}
res, err := c.c.Do(req) res, err := c.c.Do(req)
if err != nil { if err != nil {
return fmt.Errorf("performing http request: %w", err) return fmt.Errorf("performing http request: %w", err)
} }
if c.logger.MaxLevel() >= mlog.LevelDebug.Int() {
resB, err := httputil.DumpResponse(res, true)
if err != nil {
c.logger.Error(ctx, "failed to dump http response", err)
} else {
c.logger.Debug(ctx, "------ response ------\n"+string(resB)+"\n")
}
}
defer res.Body.Close() defer res.Body.Close()
if res.StatusCode != 200 { if res.StatusCode != 200 {
@ -103,7 +130,12 @@ func (c *AdminClient) Do(
// ReplicationFactor-1 other garage instances. If the context is canceled it // ReplicationFactor-1 other garage instances. If the context is canceled it
// will return the context error. // will return the context error.
func (c *AdminClient) Wait(ctx context.Context) error { func (c *AdminClient) Wait(ctx context.Context) error {
for {
for first := true; ; first = false {
if !first {
time.Sleep(250 * time.Millisecond)
}
var clusterStatus struct { var clusterStatus struct {
KnownNodes map[string]struct { KnownNodes map[string]struct {
@ -132,6 +164,5 @@ func (c *AdminClient) Wait(ctx context.Context) error {
return nil return nil
} }
time.Sleep(250 * time.Millisecond)
} }
} }

View File

@ -6,8 +6,8 @@ require (
code.betamike.com/cryptic-io/pmux v0.0.0-20221025185405-29241f144a2d code.betamike.com/cryptic-io/pmux v0.0.0-20221025185405-29241f144a2d
github.com/adrg/xdg v0.4.0 github.com/adrg/xdg v0.4.0
github.com/imdario/mergo v0.3.12 github.com/imdario/mergo v0.3.12
github.com/mediocregopher/mediocre-go-lib/v2 v2.0.0-beta.1.0.20221113151154-07f3889a705b
github.com/minio/minio-go/v7 v7.0.28 github.com/minio/minio-go/v7 v7.0.28
github.com/nlepage/go-tarfs v1.1.0
github.com/shirou/gopsutil v3.21.11+incompatible github.com/shirou/gopsutil v3.21.11+incompatible
github.com/slackhq/nebula v1.6.1 github.com/slackhq/nebula v1.6.1
github.com/spf13/pflag v1.0.5 github.com/spf13/pflag v1.0.5

View File

@ -1,5 +1,3 @@
code.betamike.com/cryptic-io/pmux v0.0.0-20221020185531-7a7868003822 h1:c7Eu2h8gXOpOfhC1LvSYLNfiSsWTyvdI1XVpUuqMFHE=
code.betamike.com/cryptic-io/pmux v0.0.0-20221020185531-7a7868003822/go.mod h1:cBuEN/rkaM/GH24uQroX/++qDmte+mLudDUqMt6XJWs=
code.betamike.com/cryptic-io/pmux v0.0.0-20221025185405-29241f144a2d h1:s6nDTg23o9ujZZnl8ohZBDoG4SqPUyFfvod9DQjwmNU= code.betamike.com/cryptic-io/pmux v0.0.0-20221025185405-29241f144a2d h1:s6nDTg23o9ujZZnl8ohZBDoG4SqPUyFfvod9DQjwmNU=
code.betamike.com/cryptic-io/pmux v0.0.0-20221025185405-29241f144a2d/go.mod h1:cBuEN/rkaM/GH24uQroX/++qDmte+mLudDUqMt6XJWs= code.betamike.com/cryptic-io/pmux v0.0.0-20221025185405-29241f144a2d/go.mod h1:cBuEN/rkaM/GH24uQroX/++qDmte+mLudDUqMt6XJWs=
github.com/adrg/xdg v0.4.0 h1:RzRqFcjH4nE5C6oTAxhBtoE2IRyjBSa62SCbyPidvls= github.com/adrg/xdg v0.4.0 h1:RzRqFcjH4nE5C6oTAxhBtoE2IRyjBSa62SCbyPidvls=
@ -36,6 +34,8 @@ github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfn
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE= github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE=
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
github.com/mediocregopher/mediocre-go-lib/v2 v2.0.0-beta.1.0.20221113151154-07f3889a705b h1:A+IqPY72GXChyCje7YqnZrb8Q4ajUqft/etsmIOobu4=
github.com/mediocregopher/mediocre-go-lib/v2 v2.0.0-beta.1.0.20221113151154-07f3889a705b/go.mod h1:wOZVlnKYvIbkzyCJ3dxy1k40XkirvCd1pisX2O91qoQ=
github.com/minio/md5-simd v1.1.0 h1:QPfiOqlZH+Cj9teu0t9b1nTBfPbyTl16Of5MeuShdK4= github.com/minio/md5-simd v1.1.0 h1:QPfiOqlZH+Cj9teu0t9b1nTBfPbyTl16Of5MeuShdK4=
github.com/minio/md5-simd v1.1.0/go.mod h1:XpBqgZULrMYD3R+M28PcmP0CkI7PEMzB3U77ZrKZ0Gw= github.com/minio/md5-simd v1.1.0/go.mod h1:XpBqgZULrMYD3R+M28PcmP0CkI7PEMzB3U77ZrKZ0Gw=
github.com/minio/minio-go/v7 v7.0.28 h1:VMr3K5qGIEt+/KW3poopRh8mzi5RwuCjmrmstK196Fg= github.com/minio/minio-go/v7 v7.0.28 h1:VMr3K5qGIEt+/KW3poopRh8mzi5RwuCjmrmstK196Fg=
@ -49,8 +49,6 @@ github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M= github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M=
github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk= github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk=
github.com/nlepage/go-tarfs v1.1.0 h1:bsACOiZMB/zFjYG/sE01070i9Fl26MnRpw0L6WuyfVs=
github.com/nlepage/go-tarfs v1.1.0/go.mod h1:IhxRcLhLkawBetnwu/JNuoPkq/6cclAllhgEa6SmzS8=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/rs/xid v1.2.1 h1:mhH9Nq+C1fY2l1XIpgxIiUOfNpRBYH1kKcr+qfKgjRc= github.com/rs/xid v1.2.1 h1:mhH9Nq+C1fY2l1XIpgxIiUOfNpRBYH1kKcr+qfKgjRc=
@ -71,7 +69,6 @@ github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXf
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.7.1 h1:5TQK59W5E3v0r2duFAb7P95B6hEeOyEnHRa8MjYSMTY= github.com/stretchr/testify v1.7.1 h1:5TQK59W5E3v0r2duFAb7P95B6hEeOyEnHRa8MjYSMTY=
github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/tklauser/go-sysconf v0.3.10 h1:IJ1AZGZRWbY8T5Vfk04D9WOA5WSejdflXxP03OUqALw= github.com/tklauser/go-sysconf v0.3.10 h1:IJ1AZGZRWbY8T5Vfk04D9WOA5WSejdflXxP03OUqALw=
github.com/tklauser/go-sysconf v0.3.10/go.mod h1:C8XykCvCb+Gn0oNCWPIlcb0RuglQTYaQ2hGm7jmxEFk= github.com/tklauser/go-sysconf v0.3.10/go.mod h1:C8XykCvCb+Gn0oNCWPIlcb0RuglQTYaQ2hGm7jmxEFk=
github.com/tklauser/numcpus v0.4.0 h1:E53Dm1HjH1/R2/aoCtXtPgzmElmn51aOkhCFSuZq//o= github.com/tklauser/numcpus v0.4.0 h1:E53Dm1HjH1/R2/aoCtXtPgzmElmn51aOkhCFSuZq//o=