2021-04-20 21:31:37 +00:00
|
|
|
// Package bootstrap deals with the creation of bootstrap files
|
|
|
|
package bootstrap
|
|
|
|
|
|
|
|
import (
|
2022-10-11 19:24:53 +00:00
|
|
|
"cryptic-net/garage"
|
2021-04-20 21:31:37 +00:00
|
|
|
"cryptic-net/tarutil"
|
2022-10-15 14:28:03 +00:00
|
|
|
"cryptic-net/yamlutil"
|
2021-04-20 21:31:37 +00:00
|
|
|
"fmt"
|
|
|
|
"io"
|
|
|
|
"io/fs"
|
2022-10-15 14:28:03 +00:00
|
|
|
"os"
|
|
|
|
"strings"
|
2021-04-20 21:31:37 +00:00
|
|
|
)
|
|
|
|
|
2022-10-15 14:28:03 +00:00
|
|
|
// Paths within the bootstrap FS which for general data.
|
|
|
|
const (
|
|
|
|
HostNamePath = "hostname"
|
|
|
|
)
|
2021-04-20 21:31:37 +00:00
|
|
|
|
2022-10-15 14:28:03 +00:00
|
|
|
// Bootstrap is used for accessing all information contained within a
|
|
|
|
// bootstrap.tgz file.
|
|
|
|
//
|
|
|
|
// An instance of Bootstrap is read-only, the creator sub-package should be used
|
|
|
|
// to create new instances.
|
|
|
|
type Bootstrap struct {
|
|
|
|
Hosts map[string]Host
|
|
|
|
HostName string
|
|
|
|
|
|
|
|
NebulaCertsCACert string
|
|
|
|
NebulaCertsHostCert string
|
|
|
|
NebulaCertsHostKey string
|
|
|
|
|
|
|
|
GarageRPCSecret string
|
|
|
|
GarageGlobalBucketS3APICredentials garage.S3APICredentials
|
|
|
|
|
|
|
|
// Hash is a determinstic hash of the contents of the bootstrap file. This
|
|
|
|
// will be populated when parsing a Bootstrap from a bootstrap.tgz, but will
|
|
|
|
// be ignored when creating a new bootstrap.tgz.
|
|
|
|
Hash []byte
|
|
|
|
|
|
|
|
// DEPRECATED do not use
|
|
|
|
FS fs.FS
|
|
|
|
}
|
2021-04-20 21:31:37 +00:00
|
|
|
|
2022-10-15 14:28:03 +00:00
|
|
|
// FromFS loads a Boostrap instance from the given fs.FS, which presumably
|
|
|
|
// represents the file structure of a bootstrap.tgz file.
|
|
|
|
func FromFS(bootstrapFS fs.FS) (Bootstrap, error) {
|
2021-04-20 21:31:37 +00:00
|
|
|
|
2022-10-15 14:28:03 +00:00
|
|
|
var (
|
|
|
|
b Bootstrap
|
|
|
|
err error
|
|
|
|
)
|
2021-04-20 21:31:37 +00:00
|
|
|
|
2022-10-15 14:28:03 +00:00
|
|
|
b.FS = bootstrapFS
|
2021-04-20 21:31:37 +00:00
|
|
|
|
2022-10-15 14:28:03 +00:00
|
|
|
if b.Hosts, err = loadHosts(bootstrapFS); err != nil {
|
|
|
|
return Bootstrap{}, fmt.Errorf("loading hosts info from fs: %w", err)
|
2021-04-20 21:31:37 +00:00
|
|
|
}
|
|
|
|
|
2022-10-15 14:28:03 +00:00
|
|
|
if err = yamlutil.LoadYamlFSFile(
|
|
|
|
&b.GarageGlobalBucketS3APICredentials,
|
|
|
|
bootstrapFS,
|
|
|
|
GarageGlobalBucketKeyYmlPath,
|
|
|
|
); err != nil {
|
|
|
|
return Bootstrap{}, fmt.Errorf("loading %q from fs: %w", b.GarageGlobalBucketS3APICredentials, err)
|
|
|
|
}
|
2022-10-11 19:24:53 +00:00
|
|
|
|
2022-10-15 14:28:03 +00:00
|
|
|
filesToLoadAsString := []struct {
|
|
|
|
into *string
|
|
|
|
path string
|
2022-10-11 19:24:53 +00:00
|
|
|
}{
|
2022-10-15 14:28:03 +00:00
|
|
|
{&b.HostName, HostNamePath},
|
|
|
|
{&b.NebulaCertsCACert, NebulaCertsCACertPath},
|
|
|
|
{&b.NebulaCertsHostCert, NebulaCertsHostCertPath},
|
|
|
|
{&b.NebulaCertsHostKey, NebulaCertsHostKeyPath},
|
|
|
|
{&b.GarageRPCSecret, GarageRPCSecretPath},
|
2022-10-11 19:24:53 +00:00
|
|
|
}
|
|
|
|
|
2022-10-15 14:28:03 +00:00
|
|
|
for _, f := range filesToLoadAsString {
|
|
|
|
body, err := fs.ReadFile(bootstrapFS, f.path)
|
|
|
|
if err != nil {
|
|
|
|
return Bootstrap{}, fmt.Errorf("loading %q from fs: %w", f.path, err)
|
|
|
|
}
|
|
|
|
*f.into = string(body)
|
|
|
|
}
|
2022-10-11 19:24:53 +00:00
|
|
|
|
2022-10-15 14:28:03 +00:00
|
|
|
// TODO confirm if this is necessary
|
|
|
|
b.GarageRPCSecret = strings.TrimSpace(b.GarageRPCSecret)
|
2022-10-11 19:24:53 +00:00
|
|
|
|
2022-10-15 14:28:03 +00:00
|
|
|
if b.Hash, err = fs.ReadFile(bootstrapFS, tarutil.HashBinPath); err != nil {
|
|
|
|
return Bootstrap{}, fmt.Errorf("loading %q from fs: %w", tarutil.HashBinPath, err)
|
2022-10-11 19:24:53 +00:00
|
|
|
}
|
|
|
|
|
2022-10-15 14:28:03 +00:00
|
|
|
return b, nil
|
2022-10-11 19:24:53 +00:00
|
|
|
}
|
|
|
|
|
2022-10-15 14:28:03 +00:00
|
|
|
// FromReader reads a bootstrap.tgz file from the given io.Reader.
|
|
|
|
func FromReader(r io.Reader) (Bootstrap, error) {
|
2022-10-11 19:24:53 +00:00
|
|
|
|
2022-10-15 14:28:03 +00:00
|
|
|
fs, err := tarutil.FSFromReader(r)
|
2022-10-11 19:24:53 +00:00
|
|
|
if err != nil {
|
2022-10-15 14:28:03 +00:00
|
|
|
return Bootstrap{}, fmt.Errorf("reading bootstrap.tgz: %w", err)
|
2022-10-11 19:24:53 +00:00
|
|
|
}
|
|
|
|
|
2022-10-15 14:28:03 +00:00
|
|
|
return FromFS(fs)
|
2022-10-11 19:24:53 +00:00
|
|
|
}
|
|
|
|
|
2022-10-15 14:28:03 +00:00
|
|
|
// FromFile reads a bootstrap.tgz from a file at the given path.
|
|
|
|
func FromFile(path string) (Bootstrap, error) {
|
2022-10-11 19:24:53 +00:00
|
|
|
|
2022-10-15 14:28:03 +00:00
|
|
|
f, err := os.Open(path)
|
2022-10-11 19:24:53 +00:00
|
|
|
if err != nil {
|
2022-10-15 14:28:03 +00:00
|
|
|
return Bootstrap{}, fmt.Errorf("opening file: %w", err)
|
2022-10-11 19:24:53 +00:00
|
|
|
}
|
2022-10-15 14:28:03 +00:00
|
|
|
defer f.Close()
|
2022-10-11 19:24:53 +00:00
|
|
|
|
2022-10-15 14:28:03 +00:00
|
|
|
return FromReader(f)
|
|
|
|
}
|
2022-10-11 19:24:53 +00:00
|
|
|
|
2022-10-15 14:28:03 +00:00
|
|
|
// ThisHost is a shortcut for b.Hosts[b.HostName], but will panic if the
|
|
|
|
// HostName isn't found in the Hosts map.
|
|
|
|
func (b Bootstrap) ThisHost() Host {
|
|
|
|
|
|
|
|
host, ok := b.Hosts[b.HostName]
|
|
|
|
if !ok {
|
|
|
|
panic(fmt.Sprintf("hostname %q not defined in bootstrap's hosts", b.HostName))
|
2022-10-11 19:24:53 +00:00
|
|
|
}
|
|
|
|
|
2022-10-15 14:28:03 +00:00
|
|
|
return host
|
2022-10-11 19:24:53 +00:00
|
|
|
}
|