2024-07-01 07:00:00 +00:00
|
|
|
package main
|
|
|
|
|
|
|
|
import (
|
|
|
|
"fmt"
|
|
|
|
"os"
|
|
|
|
"path"
|
|
|
|
"runtime"
|
|
|
|
"strings"
|
|
|
|
"time"
|
|
|
|
|
2024-07-04 13:44:14 +00:00
|
|
|
"git.frostfs.info/TrueCloudLab/frostfs-node/pkg/morph/client"
|
|
|
|
"git.frostfs.info/TrueCloudLab/frostfs-s3-lifecycler/internal/logs"
|
|
|
|
"github.com/nspcc-dev/neo-go/cli/flags"
|
|
|
|
"github.com/nspcc-dev/neo-go/cli/input"
|
|
|
|
"github.com/nspcc-dev/neo-go/pkg/crypto/keys"
|
|
|
|
"github.com/nspcc-dev/neo-go/pkg/util"
|
|
|
|
"github.com/nspcc-dev/neo-go/pkg/wallet"
|
2024-07-01 07:00:00 +00:00
|
|
|
"github.com/spf13/pflag"
|
|
|
|
"github.com/spf13/viper"
|
2024-07-04 13:44:14 +00:00
|
|
|
"go.uber.org/zap"
|
2024-07-01 07:00:00 +00:00
|
|
|
)
|
|
|
|
|
|
|
|
const (
|
2024-07-04 13:44:14 +00:00
|
|
|
// Wallet.
|
|
|
|
cfgWalletPath = "wallet.path"
|
|
|
|
cfgWalletAddress = "wallet.address"
|
|
|
|
cfgWalletPassphrase = "wallet.passphrase"
|
|
|
|
|
|
|
|
// Metrics.
|
2024-07-01 07:00:00 +00:00
|
|
|
cfgPrometheusEnabled = "prometheus.enabled"
|
|
|
|
cfgPrometheusAddress = "prometheus.address"
|
|
|
|
cfgPprofEnabled = "pprof.enabled"
|
|
|
|
cfgPprofAddress = "pprof.address"
|
|
|
|
|
|
|
|
// Logger.
|
|
|
|
cfgLoggerLevel = "logger.level"
|
|
|
|
cfgLoggerDestination = "logger.destination"
|
|
|
|
|
2024-07-04 13:44:14 +00:00
|
|
|
// Morph.
|
|
|
|
cfgMorphRPCEndpointPrefixTmpl = "morph.rpc_endpoint.%d."
|
|
|
|
cfgMorphRPCEndpointAddressTmpl = cfgMorphRPCEndpointPrefixTmpl + "address"
|
|
|
|
cfgMorphRPCEndpointPriorityTmpl = cfgMorphRPCEndpointPrefixTmpl + "priority"
|
|
|
|
cfgMorphRPCEndpointTrustedCAListTmpl = cfgMorphRPCEndpointPrefixTmpl + "trusted_ca_list"
|
|
|
|
cfgMorphRPCEndpointCertificateTmpl = cfgMorphRPCEndpointPrefixTmpl + "certificate"
|
|
|
|
cfgMorphRPCEndpointKeyTmpl = cfgMorphRPCEndpointPrefixTmpl + "key"
|
|
|
|
cfgMorphContractNetmap = "morph.contract.netmap"
|
|
|
|
cfgMorphReconnectClientInterval = "morph.reconnect_clients_interval"
|
|
|
|
|
2024-07-01 07:00:00 +00:00
|
|
|
// Command line args.
|
|
|
|
cmdHelp = "help"
|
|
|
|
cmdVersion = "version"
|
|
|
|
cmdConfig = "config"
|
|
|
|
cmdConfigDir = "config-dir"
|
|
|
|
)
|
|
|
|
|
|
|
|
const (
|
|
|
|
defaultShutdownTimeout = 15 * time.Second
|
|
|
|
componentName = "frostfs-s3-lifecycler"
|
2024-07-04 13:44:14 +00:00
|
|
|
|
|
|
|
defaultMorphRPCEndpointPriority = 1
|
2024-07-01 07:00:00 +00:00
|
|
|
)
|
|
|
|
|
|
|
|
func settings() *viper.Viper {
|
|
|
|
v := viper.New()
|
|
|
|
v.AutomaticEnv()
|
|
|
|
v.SetEnvPrefix(Prefix)
|
|
|
|
v.AllowEmptyEnv(true)
|
|
|
|
v.SetConfigType("yaml")
|
|
|
|
v.SetEnvKeyReplacer(strings.NewReplacer(".", "_"))
|
|
|
|
|
|
|
|
// flags setup:
|
|
|
|
flags := pflag.NewFlagSet("commandline", pflag.ExitOnError)
|
|
|
|
flags.SetOutput(os.Stdout)
|
|
|
|
flags.SortFlags = false
|
|
|
|
|
|
|
|
help := flags.BoolP(cmdHelp, "h", false, "show help")
|
|
|
|
version := flags.BoolP(cmdVersion, "v", false, "show version")
|
|
|
|
|
2024-07-04 13:44:14 +00:00
|
|
|
flags.StringArrayP(cmdConfig, "c", nil, "config paths")
|
2024-07-01 07:00:00 +00:00
|
|
|
flags.String(cmdConfigDir, "", "config dir path")
|
|
|
|
|
|
|
|
// set defaults:
|
|
|
|
|
|
|
|
// logger:
|
2024-07-04 13:44:14 +00:00
|
|
|
v.SetDefault(cfgLoggerLevel, "info")
|
2024-07-01 07:00:00 +00:00
|
|
|
v.SetDefault(cfgLoggerDestination, "stdout")
|
|
|
|
|
|
|
|
// services:
|
|
|
|
v.SetDefault(cfgPrometheusEnabled, false)
|
|
|
|
v.SetDefault(cfgPprofEnabled, false)
|
|
|
|
|
2024-07-04 13:44:14 +00:00
|
|
|
// morph:
|
|
|
|
v.SetDefault(cfgMorphContractNetmap, "netmap.frostfs")
|
|
|
|
v.SetDefault(cfgMorphReconnectClientInterval, 30*time.Second)
|
|
|
|
|
2024-07-01 07:00:00 +00:00
|
|
|
// Bind flags with configuration values.
|
|
|
|
if err := v.BindPFlags(flags); err != nil {
|
|
|
|
panic(err)
|
|
|
|
}
|
|
|
|
|
|
|
|
if err := flags.Parse(os.Args); err != nil {
|
|
|
|
panic(err)
|
|
|
|
}
|
|
|
|
|
|
|
|
switch {
|
|
|
|
case help != nil && *help:
|
|
|
|
printVersion()
|
|
|
|
|
|
|
|
flags.PrintDefaults()
|
|
|
|
os.Exit(0)
|
|
|
|
case version != nil && *version:
|
|
|
|
printVersion()
|
|
|
|
os.Exit(0)
|
|
|
|
}
|
|
|
|
|
|
|
|
if err := readInConfig(v); err != nil {
|
|
|
|
panic(err)
|
|
|
|
}
|
|
|
|
|
|
|
|
return v
|
|
|
|
}
|
|
|
|
|
|
|
|
func readInConfig(v *viper.Viper) error {
|
|
|
|
if v.IsSet(cmdConfig) {
|
|
|
|
if err := readConfig(v); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if v.IsSet(cmdConfigDir) {
|
|
|
|
if err := readConfigDir(v); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func readConfigDir(v *viper.Viper) error {
|
|
|
|
cfgSubConfigDir := v.GetString(cmdConfigDir)
|
|
|
|
entries, err := os.ReadDir(cfgSubConfigDir)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
for _, entry := range entries {
|
|
|
|
if entry.IsDir() {
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
ext := path.Ext(entry.Name())
|
|
|
|
if ext != ".yaml" && ext != ".yml" {
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
|
|
|
|
if err = mergeConfig(v, path.Join(cfgSubConfigDir, entry.Name())); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func readConfig(v *viper.Viper) error {
|
|
|
|
for _, fileName := range v.GetStringSlice(cmdConfig) {
|
|
|
|
if err := mergeConfig(v, fileName); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func mergeConfig(v *viper.Viper, fileName string) error {
|
|
|
|
cfgFile, err := os.Open(fileName)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
defer func() {
|
|
|
|
if err2 := cfgFile.Close(); err2 != nil {
|
|
|
|
panic(err2)
|
|
|
|
}
|
|
|
|
}()
|
|
|
|
|
|
|
|
err = v.MergeConfig(cfgFile)
|
|
|
|
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
func printVersion() {
|
|
|
|
fmt.Printf("%s\nVersion: %s\nGoVersion: %s\n", componentName, Version, runtime.Version())
|
|
|
|
}
|
2024-07-04 13:44:14 +00:00
|
|
|
|
|
|
|
func fetchKey(v *viper.Viper) (*keys.PrivateKey, error) {
|
|
|
|
var password *string
|
|
|
|
if v.IsSet(cfgWalletPassphrase) {
|
|
|
|
pwd := v.GetString(cfgWalletPassphrase)
|
|
|
|
password = &pwd
|
|
|
|
}
|
|
|
|
|
|
|
|
walletPath := v.GetString(cfgWalletPath)
|
|
|
|
if len(walletPath) == 0 {
|
|
|
|
return nil, fmt.Errorf("wallet path must not be empty")
|
|
|
|
}
|
|
|
|
w, err := wallet.NewWalletFromFile(walletPath)
|
|
|
|
if err != nil {
|
|
|
|
return nil, fmt.Errorf("parse wallet: %w", err)
|
|
|
|
}
|
|
|
|
|
|
|
|
walletAddress := v.GetString(cfgWalletAddress)
|
|
|
|
|
|
|
|
var addr util.Uint160
|
|
|
|
if len(walletAddress) == 0 {
|
|
|
|
addr = w.GetChangeAddress()
|
|
|
|
} else {
|
|
|
|
addr, err = flags.ParseAddress(walletAddress)
|
|
|
|
if err != nil {
|
|
|
|
return nil, fmt.Errorf("invalid address")
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
acc := w.GetAccount(addr)
|
|
|
|
if acc == nil {
|
|
|
|
return nil, fmt.Errorf("couldn't find wallet account for %s", walletAddress)
|
|
|
|
}
|
|
|
|
|
|
|
|
if password == nil {
|
|
|
|
pwd, err := input.ReadPassword(fmt.Sprintf("Enter password for %s > ", walletPath))
|
|
|
|
if err != nil {
|
|
|
|
return nil, fmt.Errorf("couldn't read password")
|
|
|
|
}
|
|
|
|
password = &pwd
|
|
|
|
}
|
|
|
|
if err = acc.Decrypt(*password, w.Scrypt); err != nil {
|
|
|
|
return nil, fmt.Errorf("couldn't decrypt account: %w", err)
|
|
|
|
}
|
|
|
|
|
|
|
|
return acc.PrivateKey(), nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func fetchMorphEndpoints(v *viper.Viper, l *zap.Logger) []client.Endpoint {
|
|
|
|
var res []client.Endpoint
|
|
|
|
|
|
|
|
for i := 0; ; i++ {
|
|
|
|
addr := v.GetString(fmt.Sprintf(cfgMorphRPCEndpointAddressTmpl, i))
|
|
|
|
if addr == "" {
|
|
|
|
break
|
|
|
|
}
|
|
|
|
|
|
|
|
priority := v.GetInt(fmt.Sprintf(cfgMorphRPCEndpointPriorityTmpl, i))
|
|
|
|
if priority <= 0 {
|
|
|
|
priority = defaultMorphRPCEndpointPriority
|
|
|
|
}
|
|
|
|
|
|
|
|
var mtlsConfig *client.MTLSConfig
|
|
|
|
rootCAs := v.GetStringSlice(fmt.Sprintf(cfgMorphRPCEndpointTrustedCAListTmpl, i))
|
|
|
|
if len(rootCAs) != 0 {
|
|
|
|
mtlsConfig = &client.MTLSConfig{
|
|
|
|
TrustedCAList: rootCAs,
|
|
|
|
KeyFile: v.GetString(fmt.Sprintf(cfgMorphRPCEndpointKeyTmpl, i)),
|
|
|
|
CertFile: v.GetString(fmt.Sprintf(cfgMorphRPCEndpointCertificateTmpl, i)),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
res = append(res, client.Endpoint{
|
|
|
|
Address: addr,
|
|
|
|
Priority: priority,
|
|
|
|
MTLSConfig: mtlsConfig,
|
|
|
|
})
|
|
|
|
}
|
|
|
|
|
|
|
|
if len(res) == 0 {
|
|
|
|
l.Fatal(logs.NoMorphRPCEndpoints)
|
|
|
|
}
|
|
|
|
return res
|
|
|
|
}
|