fix: improve hubl errors (#14766)
This commit is contained in:
parent
2b07c7961c
commit
8cc3346ed0
@ -32,14 +32,13 @@ hubl --help
|
||||
|
||||
### Add chain
|
||||
|
||||
To configure a new chain just run this command using the --init flag and the name of the chain as it's listed in the chain registry (https://github.com/cosmos/chain-registry).
|
||||
To configure a new chain just run this command using the --init flag and the name of the chain as it's listed in the chain registry (<https://github.com/cosmos/chain-registry>).
|
||||
|
||||
If the chain is not listed in the chain registry, you can use any unique name.
|
||||
|
||||
|
||||
```shell
|
||||
hubl --init [chain-name]
|
||||
hubl --init regen
|
||||
hubl init [chain-name]
|
||||
hubl init regen
|
||||
```
|
||||
|
||||
The chain configuration is stored in `~/.hubl/config.toml`.
|
||||
@ -56,6 +55,12 @@ endpoint = 'localhost:9090'
|
||||
insecure = true
|
||||
```
|
||||
|
||||
Or use the `--insecure` flag:
|
||||
|
||||
```shell
|
||||
hubl init regen --insecure
|
||||
```
|
||||
|
||||
:::
|
||||
|
||||
### Query
|
||||
|
||||
@ -10,8 +10,7 @@ func main() {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
err = cmd.Execute()
|
||||
if err != nil {
|
||||
if err = cmd.Execute(); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
}
|
||||
|
||||
@ -19,7 +19,7 @@ import (
|
||||
// loadFileDescriptorsGRPCReflection attempts to load the file descriptor set using gRPC reflection when cosmos.reflection.v1
|
||||
// is unavailable.
|
||||
func loadFileDescriptorsGRPCReflection(ctx context.Context, client *grpc.ClientConn) (*descriptorpb.FileDescriptorSet, error) {
|
||||
fmt.Printf("This chain does not support cosmos.reflection.v1 yet, we will attempt to use a fallback. Some features may be unsupported and it may not be possible to read all data.\n")
|
||||
fmt.Printf("This chain does not support cosmos.reflection.v1 yet... attempting to use a fallback. Some features may be unsupported and it may not be possible to read all data.\n")
|
||||
|
||||
var interfaceImplNames []string
|
||||
cosmosReflectBetaClient := reflectionv1beta1.NewReflectionServiceClient(client)
|
||||
@ -30,9 +30,7 @@ func loadFileDescriptorsGRPCReflection(ctx context.Context, client *grpc.ClientC
|
||||
InterfaceName: iface,
|
||||
})
|
||||
if err == nil {
|
||||
for _, name := range implRes.ImplementationMessageNames {
|
||||
interfaceImplNames = append(interfaceImplNames, name)
|
||||
}
|
||||
interfaceImplNames = append(interfaceImplNames, implRes.ImplementationMessageNames...)
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -66,10 +64,9 @@ func loadFileDescriptorsGRPCReflection(ctx context.Context, client *grpc.ClientC
|
||||
}
|
||||
}()
|
||||
|
||||
err = reflectClient.Send(&grpc_reflection_v1alpha.ServerReflectionRequest{
|
||||
if err = reflectClient.Send(&grpc_reflection_v1alpha.ServerReflectionRequest{
|
||||
MessageRequest: &grpc_reflection_v1alpha.ServerReflectionRequest_ListServices{},
|
||||
})
|
||||
if err != nil {
|
||||
}); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
@ -95,11 +92,9 @@ func loadFileDescriptorsGRPCReflection(ctx context.Context, client *grpc.ClientC
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
err = reflectClient.CloseSend()
|
||||
if err != nil {
|
||||
if err = reflectClient.CloseSend(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
@ -209,7 +204,7 @@ func addMissingFileDescriptors(ctx context.Context, client *grpc.ClientConn, fdM
|
||||
}
|
||||
|
||||
func guessAutocli(files *protoregistry.Files) *autocliv1.AppOptionsResponse {
|
||||
fmt.Printf("This chain does not support autocli directly yet. We will use some default mappings in the meantime to support a subset of the available services.\n")
|
||||
fmt.Printf("This chain does not support autocli directly yet. Using some default mappings in the meantime to support a subset of the available services.\n")
|
||||
res := map[string]*autocliv1.ModuleOptions{}
|
||||
files.RangeFiles(func(descriptor protoreflect.FileDescriptor) bool {
|
||||
services := descriptor.Services()
|
||||
|
||||
@ -2,7 +2,6 @@ package internal
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"os"
|
||||
"path"
|
||||
|
||||
@ -25,8 +24,8 @@ type GRPCEndpoint struct {
|
||||
|
||||
func LoadConfig(configDir string) (*Config, error) {
|
||||
configPath := configFilename(configDir)
|
||||
|
||||
if _, err := os.Stat(configPath); os.IsNotExist(err) {
|
||||
// file doesn't exist
|
||||
return &Config{Chains: map[string]*ChainConfig{}}, nil
|
||||
}
|
||||
|
||||
@ -36,8 +35,7 @@ func LoadConfig(configDir string) (*Config, error) {
|
||||
}
|
||||
|
||||
config := &Config{}
|
||||
err = toml.Unmarshal(bz, config)
|
||||
if err != nil {
|
||||
if err = toml.Unmarshal(bz, config); err != nil {
|
||||
return nil, errors.Wrapf(err, "can't load config file: %s", configPath)
|
||||
}
|
||||
|
||||
@ -45,26 +43,21 @@ func LoadConfig(configDir string) (*Config, error) {
|
||||
}
|
||||
|
||||
func SaveConfig(configDir string, config *Config) error {
|
||||
configPath := configFilename(configDir)
|
||||
buf := &bytes.Buffer{}
|
||||
enc := toml.NewEncoder(buf)
|
||||
err := enc.Encode(config)
|
||||
if err != nil {
|
||||
if err := enc.Encode(config); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
err = os.MkdirAll(configDir, 0755)
|
||||
if err != nil {
|
||||
if err := os.MkdirAll(configDir, 0755); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
err = os.WriteFile(configPath, buf.Bytes(), 0644)
|
||||
if err != nil {
|
||||
configPath := configFilename(configDir)
|
||||
if err := os.WriteFile(configPath, buf.Bytes(), 0644); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
fmt.Printf("Saved config in %s\n", configPath)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
|
||||
@ -20,16 +20,17 @@ import (
|
||||
"google.golang.org/protobuf/types/descriptorpb"
|
||||
)
|
||||
|
||||
const DefaultDirName = ".hubl"
|
||||
const DefaultConfigDirName = ".hubl"
|
||||
|
||||
type ChainInfo struct {
|
||||
client *grpc.ClientConn
|
||||
|
||||
ConfigDir string
|
||||
Chain string
|
||||
ModuleOptions map[string]*autocliv1.ModuleOptions
|
||||
ProtoFiles *protoregistry.Files
|
||||
Context context.Context
|
||||
Config *ChainConfig
|
||||
client *grpc.ClientConn
|
||||
}
|
||||
|
||||
func NewChainInfo(configDir string, chain string, config *ChainConfig) *ChainInfo {
|
||||
@ -91,8 +92,7 @@ func (c *ChainInfo) Load(reload bool) error {
|
||||
return err
|
||||
}
|
||||
|
||||
err = os.WriteFile(fdsFilename, bz, 0644)
|
||||
if err != nil {
|
||||
if err = os.WriteFile(fdsFilename, bz, 0644); err != nil {
|
||||
return err
|
||||
}
|
||||
} else {
|
||||
@ -101,15 +101,14 @@ func (c *ChainInfo) Load(reload bool) error {
|
||||
return err
|
||||
}
|
||||
|
||||
err = proto.Unmarshal(bz, fdSet)
|
||||
if err != nil {
|
||||
if err = proto.Unmarshal(bz, fdSet); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
c.ProtoFiles, err = protodesc.FileOptions{AllowUnresolvable: true}.NewFiles(fdSet)
|
||||
if err != nil {
|
||||
return errors.Wrapf(err, "error building protoregistry.Files")
|
||||
return fmt.Errorf("error building protoregistry.Files: %w", err)
|
||||
}
|
||||
|
||||
appOptsFilename, err := c.appOptsCacheFilename()
|
||||
@ -165,7 +164,6 @@ func (c *ChainInfo) OpenClient() (*grpc.ClientConn, error) {
|
||||
|
||||
var res error
|
||||
for _, endpoint := range c.Config.GRPCEndpoints {
|
||||
var err error
|
||||
var creds credentials.TransportCredentials
|
||||
if endpoint.Insecure {
|
||||
creds = insecure.NewCredentials()
|
||||
@ -174,6 +172,8 @@ func (c *ChainInfo) OpenClient() (*grpc.ClientConn, error) {
|
||||
MinVersion: tls.VersionTLS12,
|
||||
})
|
||||
}
|
||||
|
||||
var err error
|
||||
c.client, err = grpc.Dial(endpoint.Endpoint, grpc.WithTransportCredentials(creds))
|
||||
if err != nil {
|
||||
res = multierror.Append(res, err)
|
||||
|
||||
@ -5,6 +5,7 @@ import (
|
||||
"fmt"
|
||||
"io"
|
||||
"net/http"
|
||||
"strings"
|
||||
|
||||
"github.com/manifoldco/promptui"
|
||||
)
|
||||
@ -34,20 +35,40 @@ func GetChainRegistryEntry(chain string) (*ChainRegistryEntry, error) {
|
||||
}
|
||||
|
||||
data := &ChainRegistryEntry{}
|
||||
err = json.Unmarshal(bz, data)
|
||||
if err != nil {
|
||||
if err = json.Unmarshal(bz, data); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
fmt.Printf("Found data for %s in the chain registry\n", chain)
|
||||
// clean-up the URL
|
||||
cleanEntries := make([]*APIEntry, 0)
|
||||
for i, apiEntry := range data.APIs.GRPC {
|
||||
// clean-up the http(s):// prefix
|
||||
if strings.Contains(apiEntry.Address, "https://") {
|
||||
data.APIs.GRPC[i].Address = strings.Replace(apiEntry.Address, "https://", "", 1)
|
||||
} else if strings.Contains(apiEntry.Address, "http://") {
|
||||
data.APIs.GRPC[i].Address = strings.Replace(apiEntry.Address, "http://", "", 1)
|
||||
}
|
||||
|
||||
// remove trailing slashes
|
||||
data.APIs.GRPC[i].Address = strings.TrimSuffix(data.APIs.GRPC[i].Address, "/")
|
||||
|
||||
// remove addresses without a port
|
||||
if !strings.Contains(data.APIs.GRPC[i].Address, ":") {
|
||||
continue
|
||||
}
|
||||
|
||||
cleanEntries = append(cleanEntries, data.APIs.GRPC[i])
|
||||
}
|
||||
|
||||
data.APIs.GRPC = cleanEntries
|
||||
fmt.Printf("Found data for %s in the chain registry\n", chain)
|
||||
return data, nil
|
||||
}
|
||||
|
||||
func SelectGRPCEndpoints(chain string) (string, error) {
|
||||
entry, err := GetChainRegistryEntry(chain)
|
||||
if err != nil {
|
||||
fmt.Printf("Unable to load data for %s in the chain registry. You'll have to specify a gRPC endpoint manually.\n", chain)
|
||||
fmt.Printf("Unable to load data for %s in the chain registry. Specify a custom gRPC endpoint manually.\n", chain)
|
||||
prompt := &promptui.Prompt{
|
||||
Label: "Enter a gRPC endpoint that you trust",
|
||||
}
|
||||
|
||||
@ -13,52 +13,75 @@ import (
|
||||
"google.golang.org/protobuf/types/dynamicpb"
|
||||
|
||||
"cosmossdk.io/client/v2/autocli"
|
||||
|
||||
"cosmossdk.io/client/v2/autocli/flag"
|
||||
)
|
||||
|
||||
var (
|
||||
flagInsecure string = "insecure"
|
||||
flagUpdate string = "update"
|
||||
flagConfig string = "config"
|
||||
)
|
||||
|
||||
func RootCommand() (*cobra.Command, error) {
|
||||
userCfgDir, err := os.UserHomeDir()
|
||||
homeDir, err := os.UserHomeDir()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
configDir := path.Join(userCfgDir, DefaultDirName)
|
||||
|
||||
configDir := path.Join(homeDir, DefaultConfigDirName)
|
||||
config, err := LoadConfig(configDir)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var initChain string
|
||||
cmd := &cobra.Command{
|
||||
Use: "hubl",
|
||||
Short: "Hubl is a CLI for interacting with Cosmos SDK chains",
|
||||
Long: "Hubl is a CLI for interacting with Cosmos SDK chains",
|
||||
}
|
||||
|
||||
// add commands
|
||||
commands, err := RemoteCommand(config, configDir)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
commands = append(commands, InitCommand(config, configDir))
|
||||
|
||||
cmd.AddCommand(commands...)
|
||||
return cmd, nil
|
||||
}
|
||||
|
||||
func InitCommand(config *Config, configDir string) *cobra.Command {
|
||||
var insecure bool
|
||||
|
||||
cmd := &cobra.Command{
|
||||
Use: "init [foochain]",
|
||||
Short: "Initialize a new chain",
|
||||
Long: `To configure a new chain just run this command using the --init flag and the name of the chain as it's listed in the chain registry (https://github.com/cosmos/chain-registry).
|
||||
If the chain is not listed in the chain registry, you can use any unique name.`,
|
||||
Example: "hubl --init foochain",
|
||||
Args: cobra.ExactArgs(1),
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
if initChain != "" {
|
||||
return reconfigure(configDir, initChain, config)
|
||||
}
|
||||
chainName := strings.ToLower(args[0])
|
||||
|
||||
return cmd.Help()
|
||||
return reconfigure(cmd, config, configDir, chainName)
|
||||
},
|
||||
}
|
||||
|
||||
cmd.Flags().StringVar(&initChain, "init", "", "initialize a new chain with the specified name")
|
||||
cmd.Flags().BoolVar(&insecure, flagInsecure, false, "allow setting up insecure gRPC connection")
|
||||
|
||||
return cmd
|
||||
}
|
||||
|
||||
func RemoteCommand(config *Config, configDir string) ([]*cobra.Command, error) {
|
||||
commands := []*cobra.Command{}
|
||||
|
||||
for chain, chainConfig := range config.Chains {
|
||||
chain, chainConfig := chain, chainConfig
|
||||
|
||||
// load chain info
|
||||
chainInfo := NewChainInfo(configDir, chain, chainConfig)
|
||||
err = chainInfo.Load(false)
|
||||
if err != nil {
|
||||
cmd.AddCommand(&cobra.Command{
|
||||
Use: chain,
|
||||
Short: "Unable to load data",
|
||||
Long: "Unable to load data, reconfiguration needed.",
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
fmt.Printf("Error loading chain data for %s: %+v\n", chain, err)
|
||||
return reconfigure(configDir, chain, config)
|
||||
},
|
||||
})
|
||||
if err := chainInfo.Load(false); err != nil {
|
||||
commands = append(commands, RemoteErrorCommand(config, configDir, chain, chainConfig, err))
|
||||
continue
|
||||
}
|
||||
|
||||
@ -77,62 +100,87 @@ If the chain is not listed in the chain registry, you can use any unique name.`,
|
||||
AddQueryConnFlags: func(command *cobra.Command) {},
|
||||
}
|
||||
|
||||
var update bool
|
||||
var reconfig bool
|
||||
var (
|
||||
update bool
|
||||
reconfig bool
|
||||
insecure bool
|
||||
)
|
||||
chainCmd := &cobra.Command{
|
||||
Use: chain,
|
||||
Short: fmt.Sprintf("Commands for the %s chain", chain),
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
if reconfig {
|
||||
return reconfigure(configDir, chain, config)
|
||||
return reconfigure(cmd, config, configDir, chain)
|
||||
} else if update {
|
||||
fmt.Printf("Updating autocli data for %s\n", chain)
|
||||
chainInfo := NewChainInfo(configDir, chain, chainConfig)
|
||||
err := chainInfo.Load(true)
|
||||
return err
|
||||
cmd.Printf("Updating autocli data for %s\n", chain)
|
||||
return chainInfo.Load(true)
|
||||
} else {
|
||||
return cmd.Help()
|
||||
}
|
||||
},
|
||||
}
|
||||
chainCmd.Flags().BoolVar(&update, "update", false, "update the CLI commands for the selected chain (should be used after every chain upgrade)")
|
||||
chainCmd.Flags().BoolVar(&reconfig, "config", false, "re-configure the selected chain (allows choosing a new gRPC endpoint and refreshes data)")
|
||||
chainCmd.Flags().BoolVar(&update, flagUpdate, false, "update the CLI commands for the selected chain (should be used after every chain upgrade)")
|
||||
chainCmd.Flags().BoolVar(&reconfig, flagConfig, false, "re-configure the selected chain (allows choosing a new gRPC endpoint and refreshes data")
|
||||
chainCmd.Flags().BoolVar(&insecure, flagInsecure, false, "allow re-configuring the selected chain using an insecure gRPC connection")
|
||||
|
||||
err = appOpts.EnhanceRootCommandWithBuilder(chainCmd, builder)
|
||||
if err != nil {
|
||||
if err := appOpts.EnhanceRootCommandWithBuilder(chainCmd, builder); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
cmd.AddCommand(chainCmd)
|
||||
commands = append(commands, chainCmd)
|
||||
}
|
||||
|
||||
return cmd, nil
|
||||
return commands, nil
|
||||
}
|
||||
|
||||
func reconfigure(configDir, chain string, config *Config) error {
|
||||
fmt.Printf("Configuring %s\n", chain)
|
||||
func RemoteErrorCommand(config *Config, configDir string, chain string, chainConfig *ChainConfig, err error) *cobra.Command {
|
||||
cmd := &cobra.Command{
|
||||
Use: chain,
|
||||
Short: "Unable to load data",
|
||||
Long: "Unable to load data, reconfiguration needed.",
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
cmd.Printf("Error loading chain data for %s: %+v\n", chain, err)
|
||||
|
||||
return reconfigure(cmd, config, configDir, chain)
|
||||
},
|
||||
}
|
||||
|
||||
cmd.Flags().Bool(flagInsecure, chainConfig.GRPCEndpoints[0].Insecure, "allow setting up insecure gRPC connection")
|
||||
|
||||
return cmd
|
||||
}
|
||||
|
||||
func reconfigure(cmd *cobra.Command, config *Config, configDir, chain string) error {
|
||||
insecure, _ := cmd.Flags().GetBool(flagInsecure)
|
||||
|
||||
cmd.Printf("Configuring %s\n", chain)
|
||||
endpoint, err := SelectGRPCEndpoints(chain)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
fmt.Printf("Selected: %s\n", endpoint)
|
||||
cmd.Printf("%s endpoint selected\n", endpoint)
|
||||
chainConfig := &ChainConfig{
|
||||
GRPCEndpoints: []GRPCEndpoint{
|
||||
{
|
||||
Endpoint: endpoint,
|
||||
Insecure: insecure,
|
||||
},
|
||||
},
|
||||
}
|
||||
config.Chains[chain] = chainConfig
|
||||
|
||||
chainInfo := NewChainInfo(configDir, chain, chainConfig)
|
||||
err = chainInfo.Load(true)
|
||||
if err != nil {
|
||||
if err = chainInfo.Load(true); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return SaveConfig(configDir, config)
|
||||
config.Chains[chain] = chainConfig
|
||||
if err := SaveConfig(configDir, config); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
cmd.Printf("Configuration saved to %s\n", configDir)
|
||||
return nil
|
||||
}
|
||||
|
||||
type dynamicTypeResolver struct {
|
||||
|
||||
Loading…
Reference in New Issue
Block a user