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.
This commit is contained in:
parent
90a30bef5e
commit
629a8ec9b2
@ -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"
|
||||||
];
|
];
|
||||||
|
@ -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
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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,
|
||||||
|
@ -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,
|
||||||
) (
|
) (
|
||||||
@ -128,13 +132,13 @@ func runDaemonPmuxOnce(
|
|||||||
pmuxlib.Run(ctx, os.Stdout, os.Stderr, pmuxConfig)
|
pmuxlib.Run(ctx, os.Stdout, os.Stderr, pmuxConfig)
|
||||||
}()
|
}()
|
||||||
|
|
||||||
if err := waitForGarageAndNebula(ctx, hostBootstrap, daemonConfig); err != nil {
|
if err := waitForGarageAndNebula(ctx, logger, hostBootstrap, daemonConfig); err != nil {
|
||||||
return bootstrap.Bootstrap{}, fmt.Errorf("waiting for nebula/garage to start up: %w", err)
|
return bootstrap.Bootstrap{}, fmt.Errorf("waiting for nebula/garage to start up: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
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 := hostBootstrap.PutGarageBoostrapHost(ctx); err != nil {
|
||||||
fmt.Fprintf(os.Stderr, "updating host info in garage: %v\n", err)
|
logger.Error(ctx, "updating host info in garage", err)
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -148,8 +152,8 @@ func runDaemonPmuxOnce(
|
|||||||
if len(daemonConfig.Storage.Allocations) > 0 {
|
if len(daemonConfig.Storage.Allocations) > 0 {
|
||||||
|
|
||||||
err := doOnce(ctx, func(ctx context.Context) error {
|
err := doOnce(ctx, func(ctx context.Context) error {
|
||||||
if err := garageApplyLayout(ctx, hostBootstrap, daemonConfig); err != nil {
|
if err := garageApplyLayout(ctx, logger, hostBootstrap, daemonConfig); err != nil {
|
||||||
fmt.Fprintf(os.Stderr, "applying garage layout: %v\n", err)
|
logger.Error(ctx, "applying garage layout", err)
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -179,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 {
|
||||||
@ -212,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)
|
||||||
}
|
}
|
||||||
@ -249,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
|
||||||
}
|
}
|
||||||
@ -289,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
|
||||||
|
@ -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,11 +34,13 @@ 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 {
|
||||||
@ -48,6 +57,8 @@ func waitForGarageAndNebula(
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
logger = garageAdminClientLogger(logger)
|
||||||
|
|
||||||
for _, alloc := range allocs {
|
for _, alloc := range allocs {
|
||||||
|
|
||||||
adminAddr := net.JoinHostPort(
|
adminAddr := net.JoinHostPort(
|
||||||
@ -58,6 +69,7 @@ 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 {
|
||||||
@ -165,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
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -227,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
|
||||||
|
@ -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)
|
||||||
}
|
}
|
||||||
|
56
entrypoint/src/cmd/entrypoint/logger.go
Normal file
56
entrypoint/src/cmd/entrypoint/logger.go
Normal 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
|
||||||
|
}
|
@ -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)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -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
|
||||||
}
|
}
|
||||||
|
@ -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 {
|
||||||
|
@ -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 {
|
||||||
|
@ -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
|
||||||
|
@ -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=
|
||||||
|
Loading…
Reference in New Issue
Block a user