2021-04-20 21:31:37 +00:00
|
|
|
// Package garage contains helper functions and types which are useful for
|
|
|
|
// setting up garage configs, processes, and deployments.
|
|
|
|
package garage
|
|
|
|
|
2022-10-28 22:09:18 +00:00
|
|
|
import (
|
|
|
|
"encoding/hex"
|
|
|
|
"errors"
|
|
|
|
"fmt"
|
|
|
|
"io/fs"
|
|
|
|
"os"
|
|
|
|
"path/filepath"
|
2023-01-17 19:31:22 +00:00
|
|
|
"strconv"
|
2022-10-28 22:09:18 +00:00
|
|
|
)
|
|
|
|
|
2021-04-20 21:31:37 +00:00
|
|
|
const (
|
|
|
|
|
|
|
|
// Region is the region which garage is configured with.
|
|
|
|
Region = "garage"
|
|
|
|
|
|
|
|
// GlobalBucket is the name of the global garage bucket which is
|
|
|
|
// accessible to all hosts in the network.
|
2023-08-13 14:14:59 +00:00
|
|
|
GlobalBucket = "global-shared"
|
2022-10-16 20:17:26 +00:00
|
|
|
|
|
|
|
// ReplicationFactor indicates the replication factor set on the garage
|
|
|
|
// cluster. We currently only support a factor of 3.
|
|
|
|
ReplicationFactor = 3
|
2021-04-20 21:31:37 +00:00
|
|
|
)
|
2022-10-28 22:09:18 +00:00
|
|
|
|
|
|
|
func nodeKeyPath(metaDirPath string) string {
|
|
|
|
return filepath.Join(metaDirPath, "node_key")
|
|
|
|
}
|
|
|
|
|
|
|
|
func nodeKeyPubPath(metaDirPath string) string {
|
|
|
|
return filepath.Join(metaDirPath, "node_key.pub")
|
|
|
|
}
|
|
|
|
|
2023-01-17 19:31:22 +00:00
|
|
|
func nodeRPCPortPath(metaDirPath string) string {
|
2023-08-07 20:12:51 +00:00
|
|
|
return filepath.Join(metaDirPath, "isle", "rpc_port")
|
2023-01-17 19:31:22 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// loadAllocID returns the peer ID (ie the public key) of the node at the given
|
2022-10-28 22:09:18 +00:00
|
|
|
// meta directory.
|
2023-01-17 19:31:22 +00:00
|
|
|
func loadAllocID(metaDirPath string) (string, error) {
|
2022-10-28 22:09:18 +00:00
|
|
|
nodeKeyPubPath := nodeKeyPubPath(metaDirPath)
|
|
|
|
|
|
|
|
pubKey, err := os.ReadFile(nodeKeyPubPath)
|
|
|
|
if err != nil {
|
|
|
|
return "", fmt.Errorf("reading %q: %w", nodeKeyPubPath, err)
|
|
|
|
}
|
|
|
|
|
|
|
|
return hex.EncodeToString(pubKey), nil
|
|
|
|
}
|
|
|
|
|
|
|
|
// InitAlloc initializes the meta directory and keys for a particular
|
|
|
|
// allocation, if it hasn't been done so already. It returns the peer ID (ie the
|
2023-01-17 19:31:22 +00:00
|
|
|
// public key) and the rpc port in any case.
|
|
|
|
func InitAlloc(metaDirPath string, initRPCPort int) (string, int, error) {
|
|
|
|
|
|
|
|
initDirFor := func(path string) error {
|
|
|
|
dir := filepath.Dir(path)
|
|
|
|
return os.MkdirAll(dir, 0750)
|
|
|
|
}
|
2022-10-28 22:09:18 +00:00
|
|
|
|
|
|
|
var err error
|
|
|
|
|
|
|
|
exists := func(path string) bool {
|
|
|
|
|
|
|
|
if err != nil {
|
|
|
|
return false
|
|
|
|
|
|
|
|
} else if _, err = os.Stat(path); errors.Is(err, fs.ErrNotExist) {
|
2022-11-03 13:54:46 +00:00
|
|
|
err = nil
|
2022-10-28 22:09:18 +00:00
|
|
|
return false
|
|
|
|
|
|
|
|
} else if err != nil {
|
|
|
|
err = fmt.Errorf("checking if %q exists: %w", path, err)
|
|
|
|
return false
|
|
|
|
}
|
|
|
|
|
|
|
|
return true
|
|
|
|
}
|
|
|
|
|
|
|
|
nodeKeyPath := nodeKeyPath(metaDirPath)
|
|
|
|
nodeKeyPubPath := nodeKeyPubPath(metaDirPath)
|
2023-01-17 19:31:22 +00:00
|
|
|
nodeRPCPortPath := nodeRPCPortPath(metaDirPath)
|
2022-10-28 22:09:18 +00:00
|
|
|
|
|
|
|
nodeKeyPathExists := exists(nodeKeyPath)
|
|
|
|
nodeKeyPubPathExists := exists(nodeKeyPubPath)
|
2023-01-17 19:31:22 +00:00
|
|
|
nodeRPCPortPathExists := exists(nodeRPCPortPath)
|
2022-10-28 22:09:18 +00:00
|
|
|
|
|
|
|
if err != nil {
|
2023-01-17 19:31:22 +00:00
|
|
|
return "", 0, err
|
2022-10-28 22:09:18 +00:00
|
|
|
|
|
|
|
} else if nodeKeyPubPathExists != nodeKeyPathExists {
|
2023-01-17 19:31:22 +00:00
|
|
|
return "", 0, fmt.Errorf("%q or %q exist without the other existing", nodeKeyPath, nodeKeyPubPath)
|
2022-10-28 22:09:18 +00:00
|
|
|
|
|
|
|
}
|
|
|
|
|
2023-01-17 19:31:22 +00:00
|
|
|
var (
|
|
|
|
pubKeyStr string
|
|
|
|
rpcPort int
|
|
|
|
)
|
|
|
|
|
|
|
|
if nodeKeyPathExists {
|
|
|
|
|
|
|
|
if pubKeyStr, err = loadAllocID(metaDirPath); err != nil {
|
|
|
|
return "", 0, fmt.Errorf("reading node public key file: %w", err)
|
|
|
|
}
|
|
|
|
|
|
|
|
} else {
|
|
|
|
|
|
|
|
if err := initDirFor(nodeKeyPath); err != nil {
|
|
|
|
return "", 0, fmt.Errorf("creating directory for %q: %w", nodeKeyPath, err)
|
|
|
|
}
|
|
|
|
|
|
|
|
pubKey, privKey := GeneratePeerKey()
|
|
|
|
|
|
|
|
if err := os.WriteFile(nodeKeyPath, privKey, 0400); err != nil {
|
|
|
|
return "", 0, fmt.Errorf("writing private key to %q: %w", nodeKeyPath, err)
|
2022-10-28 22:09:18 +00:00
|
|
|
|
2023-01-17 19:31:22 +00:00
|
|
|
} else if err := os.WriteFile(nodeKeyPubPath, pubKey, 0440); err != nil {
|
|
|
|
return "", 0, fmt.Errorf("writing public key to %q: %w", nodeKeyPubPath, err)
|
|
|
|
}
|
|
|
|
|
|
|
|
pubKeyStr = hex.EncodeToString(pubKey)
|
2022-10-28 22:09:18 +00:00
|
|
|
}
|
|
|
|
|
2023-01-17 19:31:22 +00:00
|
|
|
if nodeRPCPortPathExists {
|
|
|
|
|
|
|
|
if rpcPortStr, err := os.ReadFile(nodeRPCPortPath); err != nil {
|
|
|
|
return "", 0, fmt.Errorf("reading rpc port from %q: %w", nodeRPCPortPath, err)
|
2022-10-28 22:09:18 +00:00
|
|
|
|
2023-01-17 19:31:22 +00:00
|
|
|
} else if rpcPort, err = strconv.Atoi(string(rpcPortStr)); err != nil {
|
|
|
|
return "", 0, fmt.Errorf("parsing rpc port %q from %q: %w", rpcPortStr, nodeRPCPortPath, err)
|
|
|
|
}
|
|
|
|
|
|
|
|
} else {
|
2022-10-28 22:09:18 +00:00
|
|
|
|
2023-01-17 19:31:22 +00:00
|
|
|
if err := initDirFor(nodeRPCPortPath); err != nil {
|
|
|
|
return "", 0, fmt.Errorf("creating directory for %q: %w", nodeRPCPortPath, err)
|
|
|
|
}
|
|
|
|
|
|
|
|
rpcPortStr := strconv.Itoa(initRPCPort)
|
|
|
|
|
|
|
|
if err := os.WriteFile(nodeRPCPortPath, []byte(rpcPortStr), 0440); err != nil {
|
|
|
|
return "", 0, fmt.Errorf("writing rpc port %q to %q: %w", rpcPortStr, nodeRPCPortPath, err)
|
|
|
|
}
|
|
|
|
|
|
|
|
rpcPort = initRPCPort
|
2022-10-28 22:09:18 +00:00
|
|
|
}
|
|
|
|
|
2023-01-17 19:31:22 +00:00
|
|
|
return pubKeyStr, rpcPort, nil
|
2022-10-28 22:09:18 +00:00
|
|
|
}
|