service: multiple fixes around init and identities
* Fix error messages (they must be in the form "doing something") * Improve/reword some error messages * Document the identity.json existance in the cli docs * Fix a bunch of typos * Fix missing folder path in the --help * Fix cluster not locking when configuration is not there but folder is * Fix force flag not overriding the config overwrite prompt * Fix deletion of Raft state on re-init (not necessary if identity persists) * Fix overwriting on identity (should not be overwritten if already exists) Much of this paves the way to be able to run without service.json: * Either taking default values (and using env vars) - maybe someday * Either by getting a configuration template it from somewhere (ipfs, http) at runtime - sooner.
This commit is contained in:
parent
7a66fc3484
commit
e62d10f83a
|
@ -1,6 +1,7 @@
|
||||||
package main
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"errors"
|
||||||
"os"
|
"os"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
|
|
||||||
|
@ -96,35 +97,47 @@ func loadIdentity() *config.Identity {
|
||||||
_, err := os.Stat(identityPath)
|
_, err := os.Stat(identityPath)
|
||||||
|
|
||||||
ident := &config.Identity{}
|
ident := &config.Identity{}
|
||||||
|
// temporary hack to convert identity
|
||||||
if os.IsNotExist(err) {
|
if os.IsNotExist(err) {
|
||||||
clusterConfig, err := config.GetClusterConfig(configPath)
|
clusterConfig, err := config.GetClusterConfig(configPath)
|
||||||
checkErr("couldn not get cluster config", err)
|
checkErr("loading configuration", err)
|
||||||
err = ident.LoadJSON(clusterConfig)
|
err = ident.LoadJSON(clusterConfig)
|
||||||
checkErr("could not load identity from cluster config", err)
|
if err != nil {
|
||||||
|
checkErr("", errors.New("error loading identity"))
|
||||||
|
}
|
||||||
|
|
||||||
err = ident.SaveJSON(identityPath)
|
err = ident.SaveJSON(identityPath)
|
||||||
checkErr("could not save identity.json ", err)
|
checkErr("saving identity.json ", err)
|
||||||
|
|
||||||
err = ident.ApplyEnvVars()
|
err = ident.ApplyEnvVars()
|
||||||
checkErr("could not apply environment variables tot the identity ", err)
|
checkErr("applying environment variables to the identity", err)
|
||||||
|
|
||||||
|
out("\nNOTICE: identity information extracted from %s and saved as %s.\n\n", DefaultConfigFile, DefaultIdentityFile)
|
||||||
return ident
|
return ident
|
||||||
}
|
}
|
||||||
|
|
||||||
err = ident.LoadJSONFromFile(identityPath)
|
err = ident.LoadJSONFromFile(identityPath)
|
||||||
checkErr("could not load identity from identity.json", err)
|
checkErr("loading identity from %s", err, DefaultIdentityFile)
|
||||||
|
|
||||||
err = ident.ApplyEnvVars()
|
err = ident.ApplyEnvVars()
|
||||||
checkErr("could not apply environment variables to the identity ", err)
|
checkErr("applying environment variables to the identity", err)
|
||||||
|
|
||||||
return ident
|
return ident
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func makeConfigFolder() {
|
||||||
|
f := filepath.Dir(configPath)
|
||||||
|
if _, err := os.Stat(f); os.IsNotExist(err) {
|
||||||
|
err := os.MkdirAll(f, 0700)
|
||||||
|
checkErr("creating configuration folder (%s)", err, f)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func saveConfig(cfg *config.Manager) {
|
func saveConfig(cfg *config.Manager) {
|
||||||
err := os.MkdirAll(filepath.Dir(configPath), 0700)
|
makeConfigFolder()
|
||||||
err = cfg.SaveJSON(configPath)
|
err := cfg.SaveJSON(configPath)
|
||||||
checkErr("saving new configuration", err)
|
checkErr("saving new configuration", err)
|
||||||
out("%s configuration written to %s\n", programName, configPath)
|
out("configuration written to %s\n", configPath)
|
||||||
}
|
}
|
||||||
|
|
||||||
func propagateTracingConfig(ident *config.Identity, cfgs *cfgs, tracingFlag bool) *cfgs {
|
func propagateTracingConfig(ident *config.Identity, cfgs *cfgs, tracingFlag bool) *cfgs {
|
||||||
|
|
|
@ -4,7 +4,6 @@ import (
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
"os"
|
|
||||||
"path"
|
"path"
|
||||||
|
|
||||||
fslock "github.com/ipfs/go-fs-lock"
|
fslock "github.com/ipfs/go-fs-lock"
|
||||||
|
@ -28,11 +27,8 @@ func (l *lock) lock() {
|
||||||
checkErr("", errors.New("cannot acquire lock twice"))
|
checkErr("", errors.New("cannot acquire lock twice"))
|
||||||
}
|
}
|
||||||
|
|
||||||
if _, err := os.Stat(configPath); os.IsNotExist(err) {
|
// we should have a config folder whenever we try to lock
|
||||||
errMsg := "%s config hasn't been initialized. Please run '%s init'"
|
makeConfigFolder()
|
||||||
errMsg = fmt.Sprintf(errMsg, programName, programName)
|
|
||||||
checkErr("", errors.New(errMsg))
|
|
||||||
}
|
|
||||||
|
|
||||||
// set the lock file within this function
|
// set the lock file within this function
|
||||||
logger.Debug("checking lock")
|
logger.Debug("checking lock")
|
||||||
|
|
|
@ -30,8 +30,8 @@ const (
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
stateCleanupPrompt = "The peer's state will be removed from the load path. Existing pins may be lost."
|
stateCleanupPrompt = "The peer state will be removed. Existing pins may be lost."
|
||||||
configurationOverwritePrompt = "Configuration(service.json) and Identity(identity.json) will be overwritten."
|
configurationOverwritePrompt = "The configuration file will be overwritten."
|
||||||
)
|
)
|
||||||
|
|
||||||
// We store a commit id here
|
// We store a commit id here
|
||||||
|
@ -70,16 +70,17 @@ ipfs-cluster-service | HTTP
|
||||||
+----------+--------+-----+----------------------+ +-------------+
|
+----------+--------+-----+----------------------+ +-------------+
|
||||||
|
|
||||||
|
|
||||||
%s needs a valid configuration to run. This configuration is
|
%s needs valid configuration and identity files to run.
|
||||||
independent from IPFS and includes its own LibP2P key-pair. It can be
|
These are independent from IPFS. The identity includes its own
|
||||||
initialized with "init" and its default location is
|
libp2p key-pair. They can be initialized with "init" and their
|
||||||
~/%s/%s.
|
default locations are ~/%s/%s
|
||||||
|
and ~/%s/%s.
|
||||||
|
|
||||||
For feedback, bug reports or any additional information, visit
|
For feedback, bug reports or any additional information, visit
|
||||||
https://github.com/ipfs/ipfs-cluster.
|
https://github.com/ipfs/ipfs-cluster.
|
||||||
|
|
||||||
|
|
||||||
EXAMPLES
|
EXAMPLES:
|
||||||
|
|
||||||
Initial configuration:
|
Initial configuration:
|
||||||
|
|
||||||
|
@ -95,14 +96,19 @@ $ ipfs-cluster-service daemon --bootstrap /ip4/192.168.1.2/tcp/9096/ipfs/QmPSoSa
|
||||||
`,
|
`,
|
||||||
programName,
|
programName,
|
||||||
programName,
|
programName,
|
||||||
DefaultPath,
|
DefaultFolder,
|
||||||
DefaultConfigFile)
|
DefaultConfigFile,
|
||||||
|
DefaultFolder,
|
||||||
|
DefaultIdentityFile,
|
||||||
|
)
|
||||||
|
|
||||||
var logger = logging.Logger("service")
|
var logger = logging.Logger("service")
|
||||||
|
|
||||||
// Default location for the configurations and data
|
// Default location for the configurations and data
|
||||||
var (
|
var (
|
||||||
// DefaultPath is initialized to $HOME/.ipfs-cluster
|
// DefaultFolder is the name of the cluster folder
|
||||||
|
DefaultFolder = ".ipfs-cluster"
|
||||||
|
// DefaultPath is set on init() to $HOME/DefaultFolder
|
||||||
// and holds all the ipfs-cluster data
|
// and holds all the ipfs-cluster data
|
||||||
DefaultPath string
|
DefaultPath string
|
||||||
// The name of the configuration file inside DefaultPath
|
// The name of the configuration file inside DefaultPath
|
||||||
|
@ -135,7 +141,7 @@ func init() {
|
||||||
home = usr.HomeDir
|
home = usr.HomeDir
|
||||||
}
|
}
|
||||||
|
|
||||||
DefaultPath = filepath.Join(home, ".ipfs-cluster")
|
DefaultPath = filepath.Join(home, DefaultFolder)
|
||||||
}
|
}
|
||||||
|
|
||||||
func out(m string, a ...interface{}) {
|
func out(m string, a ...interface{}) {
|
||||||
|
@ -145,7 +151,7 @@ func out(m string, a ...interface{}) {
|
||||||
func checkErr(doing string, err error, args ...interface{}) {
|
func checkErr(doing string, err error, args ...interface{}) {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
if len(args) > 0 {
|
if len(args) > 0 {
|
||||||
doing = fmt.Sprintf(doing, args)
|
doing = fmt.Sprintf(doing, args...)
|
||||||
}
|
}
|
||||||
out("error %s: %s\n", doing, err)
|
out("error %s: %s\n", doing, err)
|
||||||
err = locker.tryUnlock()
|
err = locker.tryUnlock()
|
||||||
|
@ -207,21 +213,26 @@ func main() {
|
||||||
app.Commands = []cli.Command{
|
app.Commands = []cli.Command{
|
||||||
{
|
{
|
||||||
Name: "init",
|
Name: "init",
|
||||||
Usage: "create a default configuration and exit",
|
Usage: "Creates a configuration and generates an identity",
|
||||||
Description: fmt.Sprintf(`
|
Description: fmt.Sprintf(`
|
||||||
This command will initialize a new service.json configuration file
|
This command will initialize a new %s configuration file and, if it
|
||||||
for %s.
|
does already exist, generate a new %s for %s.
|
||||||
|
|
||||||
By default, %s requires a cluster secret. This secret will be
|
By default, %s requires a cluster secret. This secret will be
|
||||||
automatically generated, but can be manually provided with --custom-secret
|
automatically generated, but can be manually provided with --custom-secret
|
||||||
(in which case it will be prompted), or by setting the CLUSTER_SECRET
|
(in which case it will be prompted), or by setting the CLUSTER_SECRET
|
||||||
environment variable.
|
environment variable.
|
||||||
|
|
||||||
The private key for the libp2p node is randomly generated in all cases.
|
|
||||||
|
|
||||||
Note that the --force first-level-flag allows to overwrite an existing
|
Note that the --force first-level-flag allows to overwrite an existing
|
||||||
configuration.
|
configuration with default values. To generate a new identity, please
|
||||||
`, programName, programName),
|
remove the %s file first and clean any Raft state.
|
||||||
|
`,
|
||||||
|
DefaultConfigFile,
|
||||||
|
DefaultIdentityFile,
|
||||||
|
programName,
|
||||||
|
programName,
|
||||||
|
DefaultIdentityFile,
|
||||||
|
),
|
||||||
ArgsUsage: " ",
|
ArgsUsage: " ",
|
||||||
Flags: []cli.Flag{
|
Flags: []cli.Flag{
|
||||||
cli.BoolFlag{
|
cli.BoolFlag{
|
||||||
|
@ -235,37 +246,35 @@ configuration.
|
||||||
cfgMgr, cfgs := makeConfigs()
|
cfgMgr, cfgs := makeConfigs()
|
||||||
defer cfgMgr.Shutdown() // wait for saves
|
defer cfgMgr.Shutdown() // wait for saves
|
||||||
|
|
||||||
var alreadyInitialized bool
|
configExists := false
|
||||||
if _, err := os.Stat(configPath); !os.IsNotExist(err) {
|
if _, err := os.Stat(configPath); !os.IsNotExist(err) {
|
||||||
alreadyInitialized = true
|
configExists = true
|
||||||
}
|
}
|
||||||
|
|
||||||
if alreadyInitialized {
|
identityExists := false
|
||||||
|
if _, err := os.Stat(identityPath); !os.IsNotExist(err) {
|
||||||
|
identityExists = true
|
||||||
|
}
|
||||||
|
|
||||||
|
if configExists || identityExists {
|
||||||
|
// cluster might be running
|
||||||
// acquire lock for config folder
|
// acquire lock for config folder
|
||||||
locker.lock()
|
locker.lock()
|
||||||
defer locker.tryUnlock()
|
defer locker.tryUnlock()
|
||||||
|
}
|
||||||
|
|
||||||
|
if configExists {
|
||||||
confirm := fmt.Sprintf(
|
confirm := fmt.Sprintf(
|
||||||
"%s\n%s Continue? [y/n]:",
|
"%s Continue? [y/n]:",
|
||||||
stateCleanupPrompt,
|
|
||||||
configurationOverwritePrompt,
|
configurationOverwritePrompt,
|
||||||
)
|
)
|
||||||
|
|
||||||
if !c.Bool("force") && !yesNoPrompt(confirm) {
|
// --force allows override of the prompt
|
||||||
|
if !c.GlobalBool("force") {
|
||||||
|
if !yesNoPrompt(confirm) {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
}
|
||||||
ident := loadIdentity()
|
|
||||||
|
|
||||||
err := cfgMgr.LoadJSONFileAndEnv(configPath)
|
|
||||||
checkErr("reading configuration", err)
|
|
||||||
|
|
||||||
// rafts needs cleanup on re-init because
|
|
||||||
// the peer ID of this peer changes
|
|
||||||
// and is no longer part of the old
|
|
||||||
// peerset.
|
|
||||||
mgr := newStateManager("raft", ident, cfgs)
|
|
||||||
checkErr("cleaning up raft data", mgr.Clean())
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Generate defaults for all registered components
|
// Generate defaults for all registered components
|
||||||
|
@ -283,23 +292,24 @@ configuration.
|
||||||
// Save
|
// Save
|
||||||
saveConfig(cfgMgr)
|
saveConfig(cfgMgr)
|
||||||
|
|
||||||
|
if !identityExists {
|
||||||
// Create a new identity and save it
|
// Create a new identity and save it
|
||||||
ident, err := config.NewIdentity()
|
ident, err := config.NewIdentity()
|
||||||
checkErr("could not generate a public-private key pair", err)
|
checkErr("generating an identity", err)
|
||||||
|
|
||||||
err = ident.ApplyEnvVars()
|
err = ident.ApplyEnvVars()
|
||||||
checkErr("could not apply environment variables to the identity ", err)
|
checkErr("applying environment variables to the identity", err)
|
||||||
|
|
||||||
err = ident.SaveJSON(identityPath)
|
err = ident.SaveJSON(identityPath)
|
||||||
checkErr("could not save identity.json", err)
|
checkErr("saving "+DefaultIdentityFile, err)
|
||||||
out("%s identitry written to %s\n", programName, identityPath)
|
out("new identity written to %s\n", identityPath)
|
||||||
|
}
|
||||||
return nil
|
return nil
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
Name: "daemon",
|
Name: "daemon",
|
||||||
Usage: "run the IPFS Cluster peer (default)",
|
Usage: "Runs the IPFS Cluster peer (default)",
|
||||||
Flags: []cli.Flag{
|
Flags: []cli.Flag{
|
||||||
cli.BoolFlag{
|
cli.BoolFlag{
|
||||||
Name: "upgrade, u",
|
Name: "upgrade, u",
|
||||||
|
@ -343,7 +353,7 @@ configuration.
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
Name: "state",
|
Name: "state",
|
||||||
Usage: "Manage the peer's consensus state (pinset)",
|
Usage: "Manages the peer's consensus state (pinset)",
|
||||||
Subcommands: []cli.Command{
|
Subcommands: []cli.Command{
|
||||||
{
|
{
|
||||||
Name: "export",
|
Name: "export",
|
||||||
|
@ -486,7 +496,7 @@ to all effects. Peers may need to bootstrap and sync from scratch after this.
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
Name: "version",
|
Name: "version",
|
||||||
Usage: "Print the ipfs-cluster version",
|
Usage: "Prints the ipfs-cluster version",
|
||||||
Action: func(c *cli.Context) error {
|
Action: func(c *cli.Context) error {
|
||||||
fmt.Printf("%s\n", version.Version)
|
fmt.Printf("%s\n", version.Version)
|
||||||
return nil
|
return nil
|
||||||
|
|
|
@ -64,7 +64,7 @@ func (ident *Identity) ConfigKey() string {
|
||||||
// SaveJSON saves the JSON representation of the Identity to
|
// SaveJSON saves the JSON representation of the Identity to
|
||||||
// the given path.
|
// the given path.
|
||||||
func (ident *Identity) SaveJSON(path string) error {
|
func (ident *Identity) SaveJSON(path string) error {
|
||||||
logger.Info("Saving configuration")
|
logger.Info("Saving identity")
|
||||||
|
|
||||||
bs, err := ident.ToJSON()
|
bs, err := ident.ToJSON()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
Loading…
Reference in New Issue
Block a user