Move some Bootstrap methods onto Daemon
This commit is contained in:
parent
a8893e4fc6
commit
4e5d3b28ab
@ -1,12 +1,10 @@
|
|||||||
package bootstrap
|
package bootstrap
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"bytes"
|
|
||||||
"context"
|
"context"
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"fmt"
|
"fmt"
|
||||||
"isle/garage"
|
"isle/garage"
|
||||||
"isle/nebula"
|
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
|
|
||||||
"github.com/mediocregopher/mediocre-go-lib/v2/mctx"
|
"github.com/mediocregopher/mediocre-go-lib/v2/mctx"
|
||||||
@ -14,56 +12,13 @@ import (
|
|||||||
"github.com/minio/minio-go/v7"
|
"github.com/minio/minio-go/v7"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Paths within garage's global bucket
|
// Paths within garage's global bucket.
|
||||||
|
//
|
||||||
|
// TODO this is getting moved into daemon package.
|
||||||
const (
|
const (
|
||||||
garageGlobalBucketBootstrapHostsDirPath = "bootstrap/hosts"
|
garageGlobalBucketBootstrapHostsDirPath = "bootstrap/hosts"
|
||||||
)
|
)
|
||||||
|
|
||||||
// PutGarageBoostrapHost places the <hostname>.json.signed file for this host
|
|
||||||
// into garage so that other hosts are able to see relevant configuration for
|
|
||||||
// it.
|
|
||||||
func (b Bootstrap) PutGarageBoostrapHost(ctx context.Context) error {
|
|
||||||
var (
|
|
||||||
host = b.ThisHost()
|
|
||||||
client = b.GlobalBucketS3APIClient()
|
|
||||||
)
|
|
||||||
|
|
||||||
configured, err := nebula.Sign(
|
|
||||||
host.HostConfigured, b.PrivateCredentials.SigningPrivateKey,
|
|
||||||
)
|
|
||||||
if err != nil {
|
|
||||||
return fmt.Errorf("signing host configured data: %w", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
hostB, err := json.Marshal(AuthenticatedHost{
|
|
||||||
Assigned: b.SignedHostAssigned,
|
|
||||||
Configured: configured,
|
|
||||||
})
|
|
||||||
if err != nil {
|
|
||||||
return fmt.Errorf("encoding host data: %w", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
filePath := filepath.Join(
|
|
||||||
garageGlobalBucketBootstrapHostsDirPath,
|
|
||||||
host.Name+".json.signed",
|
|
||||||
)
|
|
||||||
|
|
||||||
_, err = client.PutObject(
|
|
||||||
ctx,
|
|
||||||
garage.GlobalBucket,
|
|
||||||
filePath,
|
|
||||||
bytes.NewReader(hostB),
|
|
||||||
int64(len(hostB)),
|
|
||||||
minio.PutObjectOptions{},
|
|
||||||
)
|
|
||||||
|
|
||||||
if err != nil {
|
|
||||||
return fmt.Errorf("writing to %q in global bucket: %w", filePath, err)
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// RemoveGarageBootstrapHost removes the <hostname>.json.signed for the given
|
// RemoveGarageBootstrapHost removes the <hostname>.json.signed for the given
|
||||||
// host from garage.
|
// host from garage.
|
||||||
//
|
//
|
||||||
@ -85,6 +40,8 @@ func RemoveGarageBootstrapHost(
|
|||||||
|
|
||||||
// GetGarageBootstrapHosts loads the <hostname>.json.signed file for all hosts
|
// GetGarageBootstrapHosts loads the <hostname>.json.signed file for all hosts
|
||||||
// stored in garage.
|
// stored in garage.
|
||||||
|
//
|
||||||
|
// Deprecated: should use the method off Daemon instead.
|
||||||
func (b Bootstrap) GetGarageBootstrapHosts(
|
func (b Bootstrap) GetGarageBootstrapHosts(
|
||||||
ctx context.Context,
|
ctx context.Context,
|
||||||
logger *mlog.Logger,
|
logger *mlog.Logger,
|
||||||
|
@ -173,6 +173,7 @@ var subCmdAdminCreateNetwork = subCmd{
|
|||||||
hostBootstrap,
|
hostBootstrap,
|
||||||
envRuntimeDirPath,
|
envRuntimeDirPath,
|
||||||
envBinDirPath,
|
envBinDirPath,
|
||||||
|
envStateDirPath,
|
||||||
&daemon.Opts{
|
&daemon.Opts{
|
||||||
// SkipHostBootstrapPush is required, because the global bucket
|
// SkipHostBootstrapPush is required, because the global bucket
|
||||||
// hasn't actually been initialized yet, so there's nowhere to
|
// hasn't actually been initialized yet, so there's nowhere to
|
||||||
@ -208,7 +209,7 @@ var subCmdAdminCreateNetwork = subCmd{
|
|||||||
|
|
||||||
// rewrite the bootstrap now that the global bucket creds have been
|
// rewrite the bootstrap now that the global bucket creds have been
|
||||||
// added to it.
|
// added to it.
|
||||||
if err := writeBootstrapToDataDir(hostBootstrap); err != nil {
|
if err := writeBootstrapToStateDir(hostBootstrap); err != nil {
|
||||||
return fmt.Errorf("writing bootstrap file: %w", err)
|
return fmt.Errorf("writing bootstrap file: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -27,7 +27,7 @@ func loadHostBootstrap() (bootstrap.Bootstrap, error) {
|
|||||||
return hostBootstrap, nil
|
return hostBootstrap, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func writeBootstrapToDataDir(hostBootstrap bootstrap.Bootstrap) error {
|
func writeBootstrapToStateDir(hostBootstrap bootstrap.Bootstrap) error {
|
||||||
|
|
||||||
path := bootstrap.StateDirPath(envStateDirPath)
|
path := bootstrap.StateDirPath(envStateDirPath)
|
||||||
dirPath := filepath.Dir(path)
|
dirPath := filepath.Dir(path)
|
||||||
|
@ -41,6 +41,7 @@ import (
|
|||||||
func reloadBootstrap(
|
func reloadBootstrap(
|
||||||
ctx context.Context,
|
ctx context.Context,
|
||||||
logger *mlog.Logger,
|
logger *mlog.Logger,
|
||||||
|
daemonInst daemon.Daemon,
|
||||||
hostBootstrap bootstrap.Bootstrap,
|
hostBootstrap bootstrap.Bootstrap,
|
||||||
) (
|
) (
|
||||||
bootstrap.Bootstrap, bool, error,
|
bootstrap.Bootstrap, bool, error,
|
||||||
@ -48,7 +49,7 @@ func reloadBootstrap(
|
|||||||
|
|
||||||
thisHost := hostBootstrap.ThisHost()
|
thisHost := hostBootstrap.ThisHost()
|
||||||
|
|
||||||
newHosts, err := hostBootstrap.GetGarageBootstrapHosts(ctx, logger)
|
newHosts, err := daemonInst.GetGarageBootstrapHosts(ctx)
|
||||||
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)
|
||||||
}
|
}
|
||||||
@ -73,7 +74,7 @@ func reloadBootstrap(
|
|||||||
|
|
||||||
hostBootstrap.Hosts = newHosts
|
hostBootstrap.Hosts = newHosts
|
||||||
|
|
||||||
if err := writeBootstrapToDataDir(hostBootstrap); err != nil {
|
if err := writeBootstrapToStateDir(hostBootstrap); err != nil {
|
||||||
return bootstrap.Bootstrap{}, false, fmt.Errorf("writing new bootstrap to data dir: %w", err)
|
return bootstrap.Bootstrap{}, false, fmt.Errorf("writing new bootstrap to data dir: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -99,6 +100,7 @@ func runDaemonPmuxOnce(
|
|||||||
hostBootstrap,
|
hostBootstrap,
|
||||||
envRuntimeDirPath,
|
envRuntimeDirPath,
|
||||||
envBinDirPath,
|
envBinDirPath,
|
||||||
|
envStateDirPath,
|
||||||
nil,
|
nil,
|
||||||
)
|
)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -131,7 +133,9 @@ func runDaemonPmuxOnce(
|
|||||||
err error
|
err error
|
||||||
)
|
)
|
||||||
|
|
||||||
if hostBootstrap, changed, err = reloadBootstrap(ctx, logger, hostBootstrap); err != nil {
|
if hostBootstrap, changed, err = reloadBootstrap(
|
||||||
|
ctx, logger, daemonInst, 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 {
|
||||||
@ -236,7 +240,7 @@ var subCmdDaemon = subCmd{
|
|||||||
|
|
||||||
// If the bootstrap file is not being stored in the data dir, copy
|
// If the bootstrap file is not being stored in the data dir, copy
|
||||||
// it there, so it can be loaded from there next time.
|
// it there, so it can be loaded from there next time.
|
||||||
if err := writeBootstrapToDataDir(hostBootstrap); err != nil {
|
if err := writeBootstrapToStateDir(hostBootstrap); err != nil {
|
||||||
return fmt.Errorf("writing bootstrap.json to data dir: %w", err)
|
return fmt.Errorf("writing bootstrap.json to data dir: %w", err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -8,12 +8,10 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
func coalesceDaemonConfigAndBootstrap(
|
func coalesceDaemonConfigAndBootstrap(
|
||||||
hostBootstrap bootstrap.Bootstrap,
|
hostBootstrap bootstrap.Bootstrap, daemonConfig daemon.Config,
|
||||||
daemonConfig daemon.Config,
|
|
||||||
) (
|
) (
|
||||||
bootstrap.Bootstrap, error,
|
bootstrap.Bootstrap, error,
|
||||||
) {
|
) {
|
||||||
|
|
||||||
host := bootstrap.Host{
|
host := bootstrap.Host{
|
||||||
HostAssigned: hostBootstrap.HostAssigned,
|
HostAssigned: hostBootstrap.HostAssigned,
|
||||||
HostConfigured: bootstrap.HostConfigured{
|
HostConfigured: bootstrap.HostConfigured{
|
||||||
@ -29,7 +27,9 @@ func coalesceDaemonConfigAndBootstrap(
|
|||||||
|
|
||||||
id, rpcPort, err := garagesrv.InitAlloc(alloc.MetaPath, alloc.RPCPort)
|
id, rpcPort, err := garagesrv.InitAlloc(alloc.MetaPath, alloc.RPCPort)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return bootstrap.Bootstrap{}, fmt.Errorf("initializing alloc at %q: %w", alloc.MetaPath, err)
|
return bootstrap.Bootstrap{}, fmt.Errorf(
|
||||||
|
"initializing alloc at %q: %w", alloc.MetaPath, err,
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
host.Garage.Instances = append(host.Garage.Instances, bootstrap.GarageHostInstance{
|
host.Garage.Instances = append(host.Garage.Instances, bootstrap.GarageHostInstance{
|
||||||
@ -44,7 +44,7 @@ func coalesceDaemonConfigAndBootstrap(
|
|||||||
|
|
||||||
hostBootstrap.Hosts[host.Name] = host
|
hostBootstrap.Hosts[host.Name] = host
|
||||||
|
|
||||||
if err := writeBootstrapToDataDir(hostBootstrap); err != nil {
|
if err := writeBootstrapToStateDir(hostBootstrap); err != nil {
|
||||||
return bootstrap.Bootstrap{}, fmt.Errorf("writing bootstrap file: %w", err)
|
return bootstrap.Bootstrap{}, fmt.Errorf("writing bootstrap file: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -18,6 +18,7 @@ type daemon struct {
|
|||||||
logger *mlog.Logger
|
logger *mlog.Logger
|
||||||
config Config
|
config Config
|
||||||
hostBootstrap bootstrap.Bootstrap
|
hostBootstrap bootstrap.Bootstrap
|
||||||
|
stateDirPath string
|
||||||
opts Opts
|
opts Opts
|
||||||
|
|
||||||
pmuxCancelFn context.CancelFunc
|
pmuxCancelFn context.CancelFunc
|
||||||
@ -28,6 +29,14 @@ type daemon struct {
|
|||||||
// with isle, typically via the unix socket.
|
// with isle, typically via the unix socket.
|
||||||
type Daemon interface {
|
type Daemon interface {
|
||||||
|
|
||||||
|
// GetGarageBootstrapHosts loads (and verifies) the <hostname>.json.signed
|
||||||
|
// file for all hosts stored in garage.
|
||||||
|
GetGarageBootstrapHosts(
|
||||||
|
ctx context.Context,
|
||||||
|
) (
|
||||||
|
map[string]bootstrap.Host, error,
|
||||||
|
)
|
||||||
|
|
||||||
// Shutdown blocks until all resources held or created by the daemon,
|
// Shutdown blocks until all resources held or created by the daemon,
|
||||||
// including child processes it has started, have been cleaned up, or until
|
// including child processes it has started, have been cleaned up, or until
|
||||||
// the context is canceled.
|
// the context is canceled.
|
||||||
@ -72,7 +81,7 @@ func New(
|
|||||||
logger *mlog.Logger,
|
logger *mlog.Logger,
|
||||||
config Config,
|
config Config,
|
||||||
hostBootstrap bootstrap.Bootstrap,
|
hostBootstrap bootstrap.Bootstrap,
|
||||||
runtimeDirPath, binDirPath string,
|
runtimeDirPath, binDirPath, stateDirPath string,
|
||||||
opts *Opts,
|
opts *Opts,
|
||||||
) (
|
) (
|
||||||
Daemon, error,
|
Daemon, error,
|
||||||
@ -116,6 +125,7 @@ func New(
|
|||||||
logger: logger,
|
logger: logger,
|
||||||
config: config,
|
config: config,
|
||||||
hostBootstrap: hostBootstrap,
|
hostBootstrap: hostBootstrap,
|
||||||
|
stateDirPath: stateDirPath,
|
||||||
opts: *opts,
|
opts: *opts,
|
||||||
pmuxCancelFn: pmuxCancelFn,
|
pmuxCancelFn: pmuxCancelFn,
|
||||||
pmuxStoppedCh: make(chan struct{}),
|
pmuxStoppedCh: make(chan struct{}),
|
||||||
@ -175,7 +185,7 @@ func (d *daemon) postPmuxInit(ctx context.Context) error {
|
|||||||
|
|
||||||
if !d.opts.SkipHostBootstrapPush {
|
if !d.opts.SkipHostBootstrapPush {
|
||||||
if err := until(ctx, func(ctx context.Context) error {
|
if err := until(ctx, func(ctx context.Context) error {
|
||||||
if err := d.hostBootstrap.PutGarageBoostrapHost(ctx); err != nil {
|
if err := d.putGarageBoostrapHost(ctx); err != nil {
|
||||||
d.logger.Error(ctx, "updating host info in garage", err)
|
d.logger.Error(ctx, "updating host info in garage", err)
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
122
go/daemon/global_bucket.go
Normal file
122
go/daemon/global_bucket.go
Normal file
@ -0,0 +1,122 @@
|
|||||||
|
package daemon
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"context"
|
||||||
|
"encoding/json"
|
||||||
|
"fmt"
|
||||||
|
"isle/bootstrap"
|
||||||
|
"isle/garage"
|
||||||
|
"isle/nebula"
|
||||||
|
"path/filepath"
|
||||||
|
|
||||||
|
"github.com/mediocregopher/mediocre-go-lib/v2/mctx"
|
||||||
|
"github.com/minio/minio-go/v7"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Paths within garage's global bucket.
|
||||||
|
const (
|
||||||
|
garageGlobalBucketBootstrapHostsDirPath = "bootstrap/hosts"
|
||||||
|
)
|
||||||
|
|
||||||
|
// putGarageBoostrapHost places the <hostname>.json.signed file for this host
|
||||||
|
// into garage so that other hosts are able to see relevant configuration for
|
||||||
|
// it.
|
||||||
|
func (d *daemon) putGarageBoostrapHost(ctx context.Context) error {
|
||||||
|
var (
|
||||||
|
b = d.hostBootstrap
|
||||||
|
host = b.ThisHost()
|
||||||
|
client = b.GlobalBucketS3APIClient()
|
||||||
|
)
|
||||||
|
|
||||||
|
configured, err := nebula.Sign(
|
||||||
|
host.HostConfigured, b.PrivateCredentials.SigningPrivateKey,
|
||||||
|
)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("signing host configured data: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
hostB, err := json.Marshal(bootstrap.AuthenticatedHost{
|
||||||
|
Assigned: b.SignedHostAssigned,
|
||||||
|
Configured: configured,
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("encoding host data: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
filePath := filepath.Join(
|
||||||
|
garageGlobalBucketBootstrapHostsDirPath,
|
||||||
|
host.Name+".json.signed",
|
||||||
|
)
|
||||||
|
|
||||||
|
_, err = client.PutObject(
|
||||||
|
ctx,
|
||||||
|
garage.GlobalBucket,
|
||||||
|
filePath,
|
||||||
|
bytes.NewReader(hostB),
|
||||||
|
int64(len(hostB)),
|
||||||
|
minio.PutObjectOptions{},
|
||||||
|
)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("writing to %q in global bucket: %w", filePath, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *daemon) GetGarageBootstrapHosts(
|
||||||
|
ctx context.Context,
|
||||||
|
) (
|
||||||
|
map[string]bootstrap.Host, error,
|
||||||
|
) {
|
||||||
|
var (
|
||||||
|
b = d.hostBootstrap
|
||||||
|
client = b.GlobalBucketS3APIClient()
|
||||||
|
hosts = map[string]bootstrap.Host{}
|
||||||
|
|
||||||
|
objInfoCh = client.ListObjects(
|
||||||
|
ctx, garage.GlobalBucket,
|
||||||
|
minio.ListObjectsOptions{
|
||||||
|
Prefix: garageGlobalBucketBootstrapHostsDirPath,
|
||||||
|
Recursive: true,
|
||||||
|
},
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
for objInfo := range objInfoCh {
|
||||||
|
|
||||||
|
ctx := mctx.Annotate(ctx, "objectKey", objInfo.Key)
|
||||||
|
|
||||||
|
if objInfo.Err != nil {
|
||||||
|
return nil, fmt.Errorf("listing objects: %w", objInfo.Err)
|
||||||
|
}
|
||||||
|
|
||||||
|
obj, err := client.GetObject(
|
||||||
|
ctx, garage.GlobalBucket, objInfo.Key, minio.GetObjectOptions{},
|
||||||
|
)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("retrieving object %q: %w", objInfo.Key, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
var authedHost bootstrap.AuthenticatedHost
|
||||||
|
|
||||||
|
err = json.NewDecoder(obj).Decode(&authedHost)
|
||||||
|
obj.Close()
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
d.logger.Warn(ctx, "object contains invalid json", err)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
host, err := authedHost.Unwrap(b.CAPublicCredentials)
|
||||||
|
if err != nil {
|
||||||
|
d.logger.Warn(ctx, "host could not be authenticated", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
hosts[host.Name] = host
|
||||||
|
}
|
||||||
|
|
||||||
|
return hosts, nil
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user