cosmos-sdk/tools/confix/upgrade.go
mergify[bot] 2ee5230b2c
feat(confix): add migration to v2 (backport #21052) (#21103)
Co-authored-by: Akhil Kumar P <36399231+akhilkumarpilli@users.noreply.github.com>
Co-authored-by: akhilkumarpilli <akhilkumar7947@gmail.com>
2024-07-30 10:33:06 +02:00

105 lines
3.2 KiB
Go

package confix
import (
"bytes"
"context"
"errors"
"fmt"
"os"
"strings"
"github.com/creachadair/atomicfile"
"github.com/creachadair/tomledit"
"github.com/creachadair/tomledit/transform"
"github.com/spf13/viper"
clientcfg "github.com/cosmos/cosmos-sdk/client/config"
srvcfg "github.com/cosmos/cosmos-sdk/server/config"
)
// Upgrade reads the configuration file at configPath and applies any
// transformations necessary to Upgrade it to the current version. If this
// succeeds, the transformed output is written to outputPath. As a special
// case, if outputPath == "" the output is written to stdout.
//
// It is safe if outputPath == inputPath. If a regular file outputPath already
// exists, it is overwritten. In case of error, the output is not written.
//
// Upgrade is a convenience wrapper for calls to LoadConfig, ApplyFixes, and
// CheckValid. If the caller requires more control over the behavior of the
// Upgrade, call those functions directly.
func Upgrade(ctx context.Context, plan transform.Plan, doc *tomledit.Document, configPath, outputPath string, skipValidate bool) error {
if configPath == "" {
return errors.New("empty input configuration path")
}
// transforms doc and reports whether it succeeded.
if err := plan.Apply(ctx, doc); err != nil {
return fmt.Errorf("updating %q: %w", configPath, err)
}
var buf bytes.Buffer
if err := tomledit.Format(&buf, doc); err != nil {
return fmt.Errorf("formatting config: %w", err)
}
// ignore validation for serverv2 by checking any default field found in doc
isServerV2 := doc.First(strings.Split("store.options.ss-pruning-option", ".")...) != nil
// allow to skip validation
if !skipValidate && !isServerV2 {
// verify that file is valid after applying fixes
if err := CheckValid(configPath, buf.Bytes()); err != nil {
return fmt.Errorf("updated config is invalid: %w", err)
}
}
var err error
if outputPath == "" {
_, err = os.Stdout.Write(buf.Bytes())
} else {
err = atomicfile.WriteData(outputPath, buf.Bytes(), 0o600)
}
return err
}
// CheckValid checks whether the specified config appears to be a valid Cosmos SDK config file.
// It tries to unmarshal the config into both the server and client config structs.
func CheckValid(fileName string, data []byte) error {
v := viper.New()
v.SetConfigType("toml")
if err := v.ReadConfig(bytes.NewReader(data)); err != nil {
return fmt.Errorf("reading config: %w", err)
}
switch {
case strings.HasSuffix(fileName, AppConfig):
var cfg srvcfg.Config
if err := v.Unmarshal(&cfg); err != nil {
return fmt.Errorf("failed to unmarshal as server config: %w", err)
}
if err := cfg.ValidateBasic(); err != nil {
return fmt.Errorf("server config invalid: %w", err)
}
case strings.HasSuffix(fileName, ClientConfig):
var cfg clientcfg.ClientConfig
if err := v.Unmarshal(&cfg); err != nil {
return fmt.Errorf("failed to unmarshal as client config: %w", err)
}
if cfg.ChainID == "" {
return errors.New("client config invalid: chain-id is empty")
}
case strings.HasSuffix(fileName, CMTConfig):
return errors.New("cometbft config is not supported")
default:
return fmt.Errorf("unknown config: %s", fileName)
}
return nil
}