cmd/storagenode: enable migration of configs of different types (#3189)
* move deprecated flags code to deprecated.go, refactor to allow migration of other flags * hide deprecated flags
This commit is contained in:
parent
447c219d92
commit
10b364a2da
116
cmd/storagenode/deprecated.go
Normal file
116
cmd/storagenode/deprecated.go
Normal file
@ -0,0 +1,116 @@
|
||||
// Copyright (C) 2019 Storj Labs, Inc.
|
||||
// See LICENSE for copying information.
|
||||
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"reflect"
|
||||
"strconv"
|
||||
"time"
|
||||
|
||||
"go.uber.org/zap"
|
||||
)
|
||||
|
||||
// Deprecated contains deprecated config structs
|
||||
type Deprecated struct {
|
||||
Kademlia struct {
|
||||
ExternalAddress string `default:"" hidden:"true"`
|
||||
Operator struct {
|
||||
Email string `default:"" hidden:"true"`
|
||||
Wallet string `default:"" hidden:"true"`
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// maps deprecated config values to new values if applicable
|
||||
func mapDeprecatedConfigs(log *zap.Logger) {
|
||||
type migration struct {
|
||||
newValue interface{}
|
||||
newConfigString string
|
||||
oldValue string
|
||||
oldConfigString string
|
||||
}
|
||||
migrations := []migration{
|
||||
{
|
||||
newValue: &runCfg.Contact.ExternalAddress,
|
||||
newConfigString: "contact.external-address",
|
||||
oldValue: runCfg.Deprecated.Kademlia.ExternalAddress,
|
||||
oldConfigString: "kademlia.external-address",
|
||||
},
|
||||
{
|
||||
newValue: &runCfg.Operator.Wallet,
|
||||
newConfigString: "operator.wallet",
|
||||
oldValue: runCfg.Deprecated.Kademlia.Operator.Wallet,
|
||||
oldConfigString: "kademlia.operator.wallet",
|
||||
},
|
||||
{
|
||||
newValue: &runCfg.Operator.Email,
|
||||
newConfigString: "operator.email",
|
||||
oldValue: runCfg.Deprecated.Kademlia.Operator.Email,
|
||||
oldConfigString: "kademlia.operator.email",
|
||||
},
|
||||
}
|
||||
|
||||
for _, migration := range migrations {
|
||||
if migration.oldValue != "" {
|
||||
typ := reflect.TypeOf(migration.newValue).Elem()
|
||||
override := parseOverride(typ, migration.oldValue)
|
||||
|
||||
reflect.ValueOf(migration.newValue).Elem().Set(reflect.ValueOf(override))
|
||||
|
||||
log.Sugar().Debugf("Found deprecated flag. Migrating value %v from %s to %s", reflect.ValueOf(migration.newValue).Elem(), migration.oldConfigString, migration.newConfigString)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func parseOverride(typ reflect.Type, value string) interface{} {
|
||||
switch typ {
|
||||
case reflect.TypeOf(int(0)):
|
||||
val, err := strconv.ParseInt(value, 0, strconv.IntSize)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
return int(val)
|
||||
case reflect.TypeOf(int64(0)):
|
||||
val, err := strconv.ParseInt(value, 0, 64)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
return val
|
||||
case reflect.TypeOf(uint(0)):
|
||||
val, err := strconv.ParseUint(value, 0, strconv.IntSize)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
return uint(val)
|
||||
case reflect.TypeOf(uint64(0)):
|
||||
val, err := strconv.ParseUint(value, 0, 64)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
return val
|
||||
case reflect.TypeOf(time.Duration(0)):
|
||||
val, err := time.ParseDuration(value)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
return val
|
||||
case reflect.TypeOf(float64(0)):
|
||||
val, err := strconv.ParseFloat(value, 64)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
return val
|
||||
case reflect.TypeOf(string("")):
|
||||
return value
|
||||
case reflect.TypeOf(bool(false)):
|
||||
val, err := strconv.ParseBool(value)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
return val
|
||||
default:
|
||||
panic(fmt.Sprintf("invalid field type: %s", typ.String()))
|
||||
}
|
||||
}
|
@ -35,14 +35,6 @@ type StorageNodeFlags struct {
|
||||
Deprecated
|
||||
}
|
||||
|
||||
// Deprecated contains deprecated config structs
|
||||
type Deprecated struct {
|
||||
Kademlia struct {
|
||||
ExternalAddress string `user:"true" help:"the public address of the Kademlia node, useful for nodes behind NAT" default:""`
|
||||
Operator storagenode.OperatorConfig
|
||||
}
|
||||
}
|
||||
|
||||
var (
|
||||
rootCmd = &cobra.Command{
|
||||
Use: "storagenode",
|
||||
@ -303,48 +295,6 @@ func cmdDiag(cmd *cobra.Command, args []string) (err error) {
|
||||
return nil
|
||||
}
|
||||
|
||||
// maps deprecated config values to new values if applicable
|
||||
func mapDeprecatedConfigs(log *zap.Logger) {
|
||||
type migration struct {
|
||||
newValue *string
|
||||
newConfigString string
|
||||
oldValue *string
|
||||
oldConfigString string
|
||||
}
|
||||
migrations := []migration{
|
||||
{
|
||||
newValue: &runCfg.Contact.ExternalAddress,
|
||||
newConfigString: "contact.external-address",
|
||||
oldValue: &runCfg.Kademlia.ExternalAddress,
|
||||
oldConfigString: "kademlia.external-address",
|
||||
},
|
||||
{
|
||||
newValue: &runCfg.Operator.Wallet,
|
||||
newConfigString: "operator.wallet",
|
||||
oldValue: &runCfg.Kademlia.Operator.Wallet,
|
||||
oldConfigString: "kademlia.operator.wallet",
|
||||
},
|
||||
{
|
||||
newValue: &runCfg.Operator.Email,
|
||||
newConfigString: "operator.email",
|
||||
oldValue: &runCfg.Kademlia.Operator.Email,
|
||||
oldConfigString: "kademlia.operator.email",
|
||||
},
|
||||
}
|
||||
|
||||
for _, migration := range migrations {
|
||||
if *migration.newValue != "" && *migration.oldValue != "" {
|
||||
log.Sugar().Debugf("Both %s and %s are designated in your config.yaml. %s is deprecated. Using %s with the value of %v. Please update your config.",
|
||||
migration.oldConfigString, migration.newConfigString, migration.oldConfigString, migration.newConfigString, *migration.newValue)
|
||||
}
|
||||
if *migration.newValue == "" && *migration.oldValue != "" {
|
||||
*migration.newValue = *migration.oldValue
|
||||
log.Sugar().Debugf("%s is deprecated. Please update your config file with %s.", migration.oldConfigString, migration.newConfigString)
|
||||
log.Sugar().Debugf("Setting %s to the value of %s: %v.", migration.newConfigString, migration.oldConfigString, *migration.oldValue)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func main() {
|
||||
process.Exec(rootCmd)
|
||||
}
|
||||
|
@ -4,81 +4,61 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"reflect"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/stretchr/testify/require"
|
||||
"go.uber.org/zap"
|
||||
)
|
||||
|
||||
func TestMapConfigs(t *testing.T) {
|
||||
func TestMapDeprecatedConfigs(t *testing.T) {
|
||||
log := zap.L()
|
||||
|
||||
cases := []struct {
|
||||
testID string
|
||||
newExternalAddr string
|
||||
oldExternalAddr string
|
||||
expectedAddr string
|
||||
newWallet string
|
||||
oldWallet string
|
||||
expectedWallet string
|
||||
newEmail string
|
||||
oldEmail string
|
||||
expectedEmail string
|
||||
testID string
|
||||
newExternalAddr string
|
||||
deprecatedExternalAddr string
|
||||
expectedAddr string
|
||||
newWallet string
|
||||
deprecatedWallet string
|
||||
expectedWallet string
|
||||
newEmail string
|
||||
deprecatedEmail string
|
||||
expectedEmail string
|
||||
}{
|
||||
{testID: "new and old present, use new",
|
||||
newExternalAddr: "newAddr",
|
||||
oldExternalAddr: "oldAddr",
|
||||
expectedAddr: "newAddr",
|
||||
newWallet: "newWallet",
|
||||
oldWallet: "oldWallet",
|
||||
expectedWallet: "newWallet",
|
||||
newEmail: "newEmail",
|
||||
oldEmail: "oldEmail",
|
||||
expectedEmail: "newEmail",
|
||||
{testID: "deprecated present, override",
|
||||
newExternalAddr: "newAddr",
|
||||
deprecatedExternalAddr: "oldAddr",
|
||||
expectedAddr: "oldAddr",
|
||||
newWallet: "newWallet",
|
||||
deprecatedWallet: "oldWallet",
|
||||
expectedWallet: "oldWallet",
|
||||
newEmail: "newEmail",
|
||||
deprecatedEmail: "oldEmail",
|
||||
expectedEmail: "oldEmail",
|
||||
},
|
||||
{testID: "old present, new not, use old",
|
||||
newExternalAddr: "",
|
||||
oldExternalAddr: "oldAddr",
|
||||
expectedAddr: "oldAddr",
|
||||
newWallet: "",
|
||||
oldWallet: "oldWallet",
|
||||
expectedWallet: "oldWallet",
|
||||
newEmail: "",
|
||||
oldEmail: "oldEmail",
|
||||
expectedEmail: "oldEmail",
|
||||
},
|
||||
{testID: "new present, old not, use new",
|
||||
newExternalAddr: "newAddr",
|
||||
oldExternalAddr: "",
|
||||
expectedAddr: "newAddr",
|
||||
newWallet: "newWallet",
|
||||
oldWallet: "",
|
||||
expectedWallet: "newWallet",
|
||||
newEmail: "newEmail",
|
||||
oldEmail: "",
|
||||
expectedEmail: "newEmail",
|
||||
},
|
||||
{testID: "neither present",
|
||||
newExternalAddr: "",
|
||||
oldExternalAddr: "",
|
||||
expectedAddr: "",
|
||||
newWallet: "",
|
||||
oldWallet: "",
|
||||
expectedWallet: "",
|
||||
newEmail: "",
|
||||
oldEmail: "",
|
||||
expectedEmail: "",
|
||||
{testID: "deprecated absent, do not override",
|
||||
newExternalAddr: "newAddr",
|
||||
deprecatedExternalAddr: "",
|
||||
expectedAddr: "newAddr",
|
||||
newWallet: "newWallet",
|
||||
deprecatedWallet: "",
|
||||
expectedWallet: "newWallet",
|
||||
newEmail: "newEmail",
|
||||
deprecatedEmail: "",
|
||||
expectedEmail: "newEmail",
|
||||
},
|
||||
}
|
||||
for _, c := range cases {
|
||||
testCase := c
|
||||
t.Run(testCase.testID, func(t *testing.T) {
|
||||
runCfg.Contact.ExternalAddress = testCase.newExternalAddr
|
||||
runCfg.Kademlia.ExternalAddress = testCase.oldExternalAddr
|
||||
runCfg.Deprecated.Kademlia.ExternalAddress = testCase.deprecatedExternalAddr
|
||||
runCfg.Operator.Wallet = testCase.newWallet
|
||||
runCfg.Kademlia.Operator.Wallet = testCase.oldWallet
|
||||
runCfg.Deprecated.Kademlia.Operator.Wallet = testCase.deprecatedWallet
|
||||
runCfg.Operator.Email = testCase.newEmail
|
||||
runCfg.Kademlia.Operator.Email = testCase.oldEmail
|
||||
runCfg.Deprecated.Kademlia.Operator.Email = testCase.deprecatedEmail
|
||||
mapDeprecatedConfigs(log)
|
||||
require.Equal(t, testCase.expectedAddr, runCfg.Contact.ExternalAddress)
|
||||
require.Equal(t, testCase.expectedWallet, runCfg.Operator.Wallet)
|
||||
@ -86,3 +66,68 @@ func TestMapConfigs(t *testing.T) {
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestParseOverride(t *testing.T) {
|
||||
cases := []struct {
|
||||
testID string
|
||||
newConfigValue interface{}
|
||||
oldConfigValue string
|
||||
expectedNewConfig interface{}
|
||||
}{
|
||||
{testID: "test new config is untouched if deprecated is undefined",
|
||||
newConfigValue: "test-string",
|
||||
oldConfigValue: "",
|
||||
expectedNewConfig: "test-string",
|
||||
},
|
||||
{testID: "test migrate int",
|
||||
newConfigValue: int(1),
|
||||
oldConfigValue: "100",
|
||||
expectedNewConfig: int(100),
|
||||
},
|
||||
{testID: "test migrate int64",
|
||||
newConfigValue: int64(1),
|
||||
oldConfigValue: "100",
|
||||
expectedNewConfig: int64(100),
|
||||
},
|
||||
{testID: "test migrate uint",
|
||||
newConfigValue: uint(1),
|
||||
oldConfigValue: "2",
|
||||
expectedNewConfig: uint(2),
|
||||
},
|
||||
{testID: "test migrate uint64",
|
||||
newConfigValue: uint64(1),
|
||||
oldConfigValue: "100",
|
||||
expectedNewConfig: uint64(100),
|
||||
},
|
||||
{testID: "test migrate time.Duration",
|
||||
newConfigValue: 1 * time.Hour,
|
||||
oldConfigValue: "100h",
|
||||
expectedNewConfig: 100 * time.Hour,
|
||||
},
|
||||
{testID: "test migrate float64",
|
||||
newConfigValue: 0.5,
|
||||
oldConfigValue: "1.5",
|
||||
expectedNewConfig: 1.5,
|
||||
},
|
||||
{testID: "test migrate string",
|
||||
newConfigValue: "example@example.com",
|
||||
oldConfigValue: "migrate@migrate.com",
|
||||
expectedNewConfig: "migrate@migrate.com",
|
||||
},
|
||||
{testID: "test migrate bool",
|
||||
newConfigValue: false,
|
||||
oldConfigValue: "true",
|
||||
expectedNewConfig: true,
|
||||
},
|
||||
}
|
||||
for _, c := range cases {
|
||||
testCase := c
|
||||
t.Run(testCase.testID, func(t *testing.T) {
|
||||
if testCase.oldConfigValue != "" {
|
||||
typ := reflect.TypeOf(testCase.newConfigValue)
|
||||
testCase.newConfigValue = parseOverride(typ, testCase.oldConfigValue)
|
||||
}
|
||||
require.Equal(t, testCase.expectedNewConfig, testCase.newConfigValue)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user