isle/go/daemon/global_bucket.go

163 lines
3.5 KiB
Go

package daemon
import (
"bytes"
"context"
"encoding/json"
"fmt"
"isle/bootstrap"
"isle/garage"
"isle/nebula"
"path/filepath"
"dev.mediocregopher.com/mediocre-go-lib.git/mctx"
"dev.mediocregopher.com/mediocre-go-lib.git/mlog"
"github.com/minio/minio-go/v7"
)
// Paths within garage's global bucket.
const (
garageGlobalBucketBootstrapHostsDirPath = "bootstrap/hosts"
)
func garageInitializeGlobalBucket(
ctx context.Context,
logger *mlog.Logger,
daemonConfig Config,
hostBootstrap bootstrap.Bootstrap,
) (
garage.S3APICredentials, error,
) {
adminClient := NewGarageAdminClient(
logger, daemonConfig, hostBootstrap,
)
creds, err := adminClient.CreateS3APICredentials(
ctx, garage.GlobalBucketS3APICredentialsName,
)
if err != nil {
return creds, fmt.Errorf("creating global bucket credentials: %w", err)
}
bucketID, err := adminClient.CreateBucket(ctx, garage.GlobalBucket)
if err != nil {
return creds, fmt.Errorf("creating global bucket: %w", err)
}
if err := adminClient.GrantBucketPermissions(
ctx,
bucketID,
creds.ID,
garage.BucketPermissionRead,
garage.BucketPermissionWrite,
); err != nil {
return creds, fmt.Errorf(
"granting permissions to shared global bucket key: %w", err,
)
}
return creds, nil
}
// 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.currBootstrap
host = b.ThisHost()
client = b.GarageClientParams().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,
string(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 getGarageBootstrapHosts(
ctx context.Context, logger *mlog.Logger, currBootstrap bootstrap.Bootstrap,
) (
map[nebula.HostName]bootstrap.Host, error,
) {
var (
b = currBootstrap
client = b.GarageClientParams().GlobalBucketS3APIClient()
hosts = map[nebula.HostName]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 {
logger.Warn(ctx, "Object contains invalid json", err)
continue
}
host, err := authedHost.Unwrap(b.CAPublicCredentials)
if err != nil {
logger.Warn(ctx, "Host could not be authenticated", err)
}
hosts[host.Name] = host
}
return hosts, nil
}