Skip to content

Commit

Permalink
fix: persist node name (#1543)
Browse files Browse the repository at this point in the history
  • Loading branch information
EclesioMeloJunior committed Apr 30, 2021
1 parent a632dc4 commit 88b88f2
Show file tree
Hide file tree
Showing 10 changed files with 411 additions and 40 deletions.
113 changes: 81 additions & 32 deletions cmd/gossamer/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,6 @@
package main

import (
"encoding/binary"
"fmt"
"strconv"
"strings"
Expand All @@ -33,7 +32,7 @@ import (
"github.com/ChainSafe/gossamer/lib/runtime/life"
"github.com/ChainSafe/gossamer/lib/runtime/wasmer"
"github.com/ChainSafe/gossamer/lib/runtime/wasmtime"
"github.com/cosmos/go-bip39"
"github.com/ChainSafe/gossamer/lib/utils"

log "github.com/ChainSafe/log15"
"github.com/urfave/cli"
Expand Down Expand Up @@ -132,7 +131,10 @@ func createDotConfig(ctx *cli.Context) (*dot.Config, error) {
logger.Info("loaded package log configuration", "cfg", cfg.Log)

// set global configuration values
setDotGlobalConfig(ctx, tomlCfg, &cfg.Global)
if err := setDotGlobalConfig(ctx, tomlCfg, &cfg.Global); err != nil {
logger.Error("failed to set global node configuration", "error", err)
return nil, err
}

// set remaining cli configuration values
setDotInitConfig(ctx, tomlCfg.Init, &cfg.Init)
Expand Down Expand Up @@ -160,7 +162,11 @@ func createInitConfig(ctx *cli.Context) (*dot.Config, error) {
}

// set global configuration values
setDotGlobalConfig(ctx, tomlCfg, &cfg.Global)
err = setDotGlobalConfig(ctx, tomlCfg, &cfg.Global)
if err != nil {
logger.Error("failed to set global node configuration", "error", err)
return nil, err
}

// set log config
err = setLogConfig(ctx, tomlCfg, &cfg.Global, &cfg.Log)
Expand Down Expand Up @@ -196,7 +202,11 @@ func createImportStateConfig(ctx *cli.Context) (*dot.Config, error) {
}

// set global configuration values
setDotGlobalConfig(ctx, tomlCfg, &cfg.Global)
if err := setDotGlobalConfig(ctx, tomlCfg, &cfg.Global); err != nil {
logger.Error("failed to set global node configuration", "error", err)
return nil, err
}

return cfg, nil
}

Expand All @@ -210,7 +220,11 @@ func createBuildSpecConfig(ctx *cli.Context) (*dot.Config, error) {
}

// set global configuration values
setDotGlobalConfig(ctx, tomlCfg, &cfg.Global)
if err := setDotGlobalConfig(ctx, tomlCfg, &cfg.Global); err != nil {
logger.Error("failed to set global node configuration", "error", err)
return nil, err
}

return cfg, nil
}

Expand All @@ -229,7 +243,11 @@ func createExportConfig(ctx *cli.Context) (*dot.Config, error) {
updateDotConfigFromGenesisJSONRaw(*tomlCfg, cfg)

// set global configuration values
setDotGlobalConfig(ctx, tomlCfg, &cfg.Global)
err = setDotGlobalConfig(ctx, tomlCfg, &cfg.Global)
if err != nil {
logger.Error("failed to set global node configuration", "error", err)
return nil, err
}

// set log config
err = setLogConfig(ctx, &ctoml.Config{}, &cfg.Global, &cfg.Log)
Expand Down Expand Up @@ -385,13 +403,27 @@ func setDotInitConfig(ctx *cli.Context, tomlCfg ctoml.InitConfig, cfg *dot.InitC
)
}

// setDotGlobalConfig sets dot.GlobalConfig using flag values from the cli context
func setDotGlobalConfig(ctx *cli.Context, tomlCfg *ctoml.Config, cfg *dot.GlobalConfig) {
if tomlCfg != nil {
if tomlCfg.Global.Name != "" {
cfg.Name = tomlCfg.Global.Name
}
func setDotGlobalConfig(ctx *cli.Context, tomlConfig *ctoml.Config, cfg *dot.GlobalConfig) error {
setDotGlobalConfigFromToml(tomlConfig, cfg)
setDotGlobalConfigFromFlags(ctx, cfg)

if err := setDotGlobalConfigName(ctx, tomlConfig, cfg); err != nil {
return fmt.Errorf("could not set global node name: %w", err)
}

logger.Debug(
"global configuration",
"name", cfg.Name,
"id", cfg.ID,
"basepath", cfg.BasePath,
)

return nil
}

// setDotGlobalConfigFromToml will apply the toml configs to dot global config
func setDotGlobalConfigFromToml(tomlCfg *ctoml.Config, cfg *dot.GlobalConfig) {
if tomlCfg != nil {
if tomlCfg.Global.ID != "" {
cfg.ID = tomlCfg.Global.ID
}
Expand All @@ -406,20 +438,10 @@ func setDotGlobalConfig(ctx *cli.Context, tomlCfg *ctoml.Config, cfg *dot.Global

cfg.MetricsPort = tomlCfg.Global.MetricsPort
}
}

// TODO: generate random name if one is not assigned (see issue #1496)
// check --name flag and update node configuration
if name := ctx.GlobalString(NameFlag.Name); name != "" {
cfg.Name = name
} else {
// generate random name
entropy, _ := bip39.NewEntropy(128)
randomNamesString, _ := bip39.NewMnemonic(entropy)
randomNames := strings.Split(randomNamesString, " ")
number := binary.BigEndian.Uint16(entropy)
cfg.Name = randomNames[0] + "-" + randomNames[1] + "-" + fmt.Sprint(number)
}

// setDotGlobalConfigFromFlags sets dot.GlobalConfig using flag values from the cli context
func setDotGlobalConfigFromFlags(ctx *cli.Context, cfg *dot.GlobalConfig) {
// check --basepath flag and update node configuration
if basepath := ctx.GlobalString(BasePathFlag.Name); basepath != "" {
cfg.BasePath = basepath
Expand All @@ -429,6 +451,7 @@ func setDotGlobalConfig(ctx *cli.Context, tomlCfg *ctoml.Config, cfg *dot.Global
if cfg.BasePath == "" {
cfg.BasePath = dot.GssmrConfig().Global.BasePath
}

// check --log flag
if lvlToInt, err := strconv.Atoi(ctx.String(LogFlag.Name)); err == nil {
cfg.LogLvl = log.Lvl(lvlToInt)
Expand All @@ -444,13 +467,39 @@ func setDotGlobalConfig(ctx *cli.Context, tomlCfg *ctoml.Config, cfg *dot.Global
}

cfg.NoTelemetry = ctx.Bool("no-telemetry")
}

logger.Debug(
"global configuration",
"name", cfg.Name,
"id", cfg.ID,
"basepath", cfg.BasePath,
)
func setDotGlobalConfigName(ctx *cli.Context, tomlCfg *ctoml.Config, cfg *dot.GlobalConfig) error {
globalBasePath := utils.ExpandDir(cfg.BasePath)
initialised := dot.NodeInitialized(globalBasePath, false)

// consider the --name flag as higher priority
if ctx.GlobalString(NameFlag.Name) != "" {
cfg.Name = ctx.GlobalString(NameFlag.Name)
return nil
}

// consider the name on config as a second priority
if tomlCfg.Global.Name != "" {
cfg.Name = tomlCfg.Global.Name
return nil
}

// if node was previously initialised and is not the init command
if initialised && ctx.Command.Name != initCommandName {
var err error
if cfg.Name, err = dot.LoadGlobalNodeName(globalBasePath); err != nil {
return err
}

if cfg.Name != "" {
logger.Debug("load global node name from database", "name", cfg.Name)
return nil
}
}

cfg.Name = dot.RandomNodeName()
return nil
}

// setDotAccountConfig sets dot.AccountConfig using flag values from the cli context
Expand Down
140 changes: 140 additions & 0 deletions cmd/gossamer/config_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ import (
"github.com/ChainSafe/gossamer/chain/gssmr"
"github.com/ChainSafe/gossamer/dot"
"github.com/ChainSafe/gossamer/dot/state"
"github.com/ChainSafe/gossamer/dot/types"
"github.com/ChainSafe/gossamer/lib/genesis"
"github.com/ChainSafe/gossamer/lib/utils"

Expand Down Expand Up @@ -833,3 +834,142 @@ func TestUpdateConfigFromGenesisData(t *testing.T) {

require.Equal(t, expected, cfg)
}

func TestGlobalNodeName_WhenNodeAlreadyHasStoredName(t *testing.T) {
// Initialise a node with a random name
globalName := dot.RandomNodeName()

cfg := dot.NewTestConfig(t)
cfg.Global.Name = globalName
require.NotNil(t, cfg)

genPath := dot.NewTestGenesisAndRuntime(t)
require.NotNil(t, genPath)

defer utils.RemoveTestDir(t)

cfg.Core.Roles = types.FullNodeRole
cfg.Core.BabeAuthority = false
cfg.Core.GrandpaAuthority = false
cfg.Core.BabeThresholdNumerator = 0
cfg.Core.BabeThresholdDenominator = 0
cfg.Init.Genesis = genPath

err := dot.InitNode(cfg)
require.NoError(t, err)

// call another command and test the name
testApp := cli.NewApp()
testApp.Writer = ioutil.Discard

testcases := []struct {
description string
flags []string
values []interface{}
expected string
}{
{
"Test gossamer --roles --basepath",
[]string{"basepath", "roles"},
[]interface{}{cfg.Global.BasePath, "4"},
globalName,
},
{
"Test gossamer --roles",
[]string{"basepath", "roles"},
[]interface{}{cfg.Global.BasePath, "0"},
globalName,
},
}

for _, c := range testcases {
c := c // bypass scopelint false positive
t.Run(c.description, func(t *testing.T) {
ctx, err := newTestContext(c.description, c.flags, c.values)
require.Nil(t, err)
createdCfg, err := createDotConfig(ctx)
require.Nil(t, err)
require.Equal(t, c.expected, createdCfg.Global.Name)
})
}
}

func TestGlobalNodeNamePriorityOrder(t *testing.T) {
cfg, testCfgFile := newTestConfigWithFile(t)
require.NotNil(t, cfg)
require.NotNil(t, testCfgFile)

defer utils.RemoveTestDir(t)

// call another command and test the name
testApp := cli.NewApp()
testApp.Writer = ioutil.Discard

// when name flag is defined
whenNameFlagIsDefined := struct {
description string
flags []string
values []interface{}
expected string
}{
"Test gossamer --basepath --name --config",
[]string{"basepath", "name", "config"},
[]interface{}{cfg.Global.BasePath, "mydefinedname", testCfgFile.Name()},
"mydefinedname",
}

c := whenNameFlagIsDefined
t.Run(c.description, func(t *testing.T) {
ctx, err := newTestContext(c.description, c.flags, c.values)
require.Nil(t, err)
createdCfg, err := createDotConfig(ctx)
require.Nil(t, err)
require.Equal(t, c.expected, createdCfg.Global.Name)
})

// when name flag is not defined
// then should load name from toml if it exists
whenNameIsDefinedOnTomlConfig := struct {
description string
flags []string
values []interface{}
expected string
}{
"Test gossamer --basepath --config",
[]string{"basepath", "config"},
[]interface{}{cfg.Global.BasePath, testCfgFile.Name()},
cfg.Global.Name,
}

c = whenNameIsDefinedOnTomlConfig
t.Run(c.description, func(t *testing.T) {
ctx, err := newTestContext(c.description, c.flags, c.values)
require.Nil(t, err)
createdCfg, err := createDotConfig(ctx)
require.Nil(t, err)
require.Equal(t, c.expected, createdCfg.Global.Name)
})

// when there is no name flag and no name in config
// should check the load is initialised or generate a new random name
cfg.Global.Name = ""

whenThereIsNoName := struct {
description string
flags []string
values []interface{}
}{
"Test gossamer --basepath",
[]string{"basepath"},
[]interface{}{cfg.Global.BasePath},
}

t.Run(c.description, func(t *testing.T) {
ctx, err := newTestContext(whenThereIsNoName.description, whenThereIsNoName.flags, whenThereIsNoName.values)
require.Nil(t, err)
createdCfg, err := createDotConfig(ctx)
require.Nil(t, err)
require.NotEmpty(t, createdCfg.Global.Name)
require.NotEqual(t, cfg.Global.Name, createdCfg.Global.Name)
})
}
Loading

0 comments on commit 88b88f2

Please sign in to comment.