adm/ape: Adopt policy reader #1607
|
@ -0,0 +1,96 @@
|
|||||
package chains
|
||||||
|
||||||
import (
|
||||||
"math/big"
|
||||||
|
||||||
"git.frostfs.info/TrueCloudLab/frostfs-contract/commonclient"
|
||||||
policycontract "git.frostfs.info/TrueCloudLab/frostfs-contract/policy"
|
||||||
"git.frostfs.info/TrueCloudLab/frostfs-node/cmd/frostfs-adm/internal/commonflags"
|
||||||
"git.frostfs.info/TrueCloudLab/frostfs-node/cmd/frostfs-adm/internal/modules/morph/ape/raw/output"
|
||||||
"git.frostfs.info/TrueCloudLab/frostfs-node/cmd/frostfs-adm/internal/modules/morph/helper"
|
||||||
commonCmd "git.frostfs.info/TrueCloudLab/frostfs-node/cmd/internal/common"
|
||||||
apeCmd "git.frostfs.info/TrueCloudLab/frostfs-node/cmd/internal/common/ape"
|
||||||
"git.frostfs.info/TrueCloudLab/frostfs-sdk-go/container"
|
||||||
cid "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/container/id"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/rpcclient/invoker"
|
||||||
fyrchik marked this conversation as resolved
Outdated
|
||||||
"github.com/nspcc-dev/neo-go/pkg/rpcclient/management"
|
||||||
"github.com/spf13/cobra"
|
||||||
)
|
||||||
|
||||||
var listContainerCmd = &cobra.Command{
|
||||||
Use: "list-container",
|
||||||
dstepanov-yadro
commented
What is the difference with What is the difference with `list-rule-chains` command?
aarifullin
commented
It seems this runs listing under It seems this runs listing under `chains` subcommand
`ape chains list-container` (although it can be just `ape chains list` and flags can define targets) instead of `ape list-rule-chains`.
dkirillov
commented
This command is aimed to list all container related chains. Not only for This command is aimed to list all container related chains. Not only for `container` target but also for `namespace` target (in which container is location)
|
||||||
Short: "List container related (namespace) policies",
|
||||||
fyrchik
commented
I would use different naming: I would use different naming: `namespace` somewhere in the command name and `--container` as an optional argument (aka filter).
Current implementation looks too specific.
dkirillov
commented
Indeed it is. This command is aimed to get all container related policies. In The similar command for user that get all user related (affected) policies (from I'm not sure if using " > Current implementation looks too specific.
Indeed it is. This command is aimed to get all **container** related policies. In `container` target and in `namespace` target.
The similar command for user that get all user related (affected) policies (from `namespace`, `group`, `user` targets)
I'm not sure if using "`--container` as optional argument (aka filter)" is appropriate here
|
||||||
Long: "List container related (namespace) policies along with filtering by service (s3/storage)",
|
||||||
Example: `chains list-container -r http://localhost:40332 list --container 7h7NcXcF6k6b1yidqEHc1jkyXUm1MfUDrrTuHAefhiDe
|
||||||
chains list-container -r http://localhost:40332 --policy-hash 81c1a41d09e08087a4b679418b12be5d3ab15742 list --container 7h7NcXcF6k6b1yidqEHc1jkyXUm1MfUDrrTuHAefhiDe --namespace test`,
|
||||||
RunE: runListContainerCmd,
|
||||||
}
|
||||||
fyrchik
commented
We use We use `BindPFlag` to allow providing command values via configuration.
I doubt it is useful for any of these 4 flags.
(`containerFlag` is required, to this bind seems definitely useless).
|
||||||
|
||||||
const (
|
||||||
containerFlag = "container"
|
||||||
)
|
||||||
|
||||||
func initListContainerCmd() {
|
||||||
listContainerCmd.Flags().StringP(commonflags.EndpointFlag, commonflags.EndpointFlagShort, "", commonflags.EndpointFlagDesc)
|
||||||
listContainerCmd.Flags().String(apeCmd.ChainNameFlag, "", apeCmd.ChainNameFlagDesc)
|
||||||
listContainerCmd.Flags().String(containerFlag, "", "Container id or bucket name in nns (if name is provided than 'namespace' should be set too)")
|
||||||
listContainerCmd.Flags().String(namespaceFlag, "", "Namespace where container name will be looked up")
|
||||||
listContainerCmd.Flags().Bool(decodeChainFlag, false, "Use this flag to decode chain")
|
||||||
listContainerCmd.Flags().Bool(decodeIDFlag, false, "Use this flag to additionally decode chain id (without --decode-chain no take effect)")
|
||||||
|
||||||
_ = listContainerCmd.MarkFlagRequired(containerFlag)
|
||||||
}
|
||||||
|
||||||
func runListContainerCmd(cmd *cobra.Command, _ []string) error {
|
||||||
chainName := parseChainName(cmd)
|
||||||
namespace := parseNamespace(cmd)
|
||||||
|
||||||
inv, policyHash, _ := initReaders(cmd)
|
||||||
cnrID := parseContainer(cmd, inv, namespace)
|
||||||
printContainer(cmd, namespace, cnrID)
|
||||||
|
||||||
res, err := commonclient.ReadIteratorItems(inv, 100, policyHash, methodIteratorChainsByPrefix, big.NewInt(int64(policycontract.Namespace)), namespace, string(chainName))
|
||||||
commonCmd.ExitOnErr(cmd, "can't read iterator: %w", err)
|
||||||
|
||||||
decodeChain, _ := cmd.Flags().GetBool(decodeChainFlag)
|
||||||
dstepanov-yadro
commented
Other ape-related commands have
Other ape-related commands have `root` to empty string replacement: https://git.frostfs.info/TrueCloudLab/frostfs-node/src/commit/c98357606b4e387f7a8063331438dccfb24d255e/cmd/frostfs-adm/internal/modules/morph/ape/ape_util.go#L27
|
||||||
decodeID, _ := cmd.Flags().GetBool(decodeIDFlag)
|
||||||
|
||||||
cmd.Printf("\nnamespace policies: %d\n", len(res))
|
||||||
err = output.PrintChains(cmd, res, decodeChain, decodeID)
|
||||||
commonCmd.ExitOnErr(cmd, "can't print chains: %w", err)
|
||||||
|
||||||
res, err = commonclient.ReadIteratorItems(inv, 100, policyHash, methodIteratorChainsByPrefix, big.NewInt(int64(policycontract.Container)), cnrID.EncodeToString(), string(chainName))
|
||||||
commonCmd.ExitOnErr(cmd, "can't read iterator: %w", err)
|
||||||
|
||||||
cmd.Printf("\ncontainer policies: %d\n", len(res))
|
||||||
return output.PrintChains(cmd, res, decodeChain, decodeID)
|
||||||
}
|
||||||
|
||||||
func printContainer(cmd *cobra.Command, namespace string, cnrID cid.ID) {
|
||||||
cmd.Println("container:")
|
||||||
cmd.Printf("\tnamespace: '%s'\n", namespace)
|
||||||
cmd.Printf("\tCID: '%s'\n", cnrID.EncodeToString())
|
||||||
}
|
||||||
|
||||||
func parseContainer(cmd *cobra.Command, inv *invoker.Invoker, namespace string) cid.ID {
|
||||||
fyrchik marked this conversation as resolved
Outdated
fyrchik
commented
Why do we have Why do we have `%s` for namespace message, but no CID for this one?
|
||||||
containerName, _ := cmd.Flags().GetString(containerFlag)
|
||||||
|
||||||
var cnrID cid.ID
|
||||||
if err := cnrID.DecodeString(containerName); err == nil {
|
||||||
return cnrID
|
||||||
fyrchik marked this conversation as resolved
Outdated
fyrchik
commented
It is used only once and in this package, why is it public? It is used only once and in this package, why is it public?
fyrchik
commented
Why have you decided to move it to Why have you decided to move it to `helper` package instead of making it private?
dkirillov
commented
To make it similar to To make it similar to `NNSResolveHash`. Probably it can be helpful in some other places
fyrchik
commented
Probably can, probably not. Probably can, probably not.
To me it complicates things now.
|
||||||
}
|
||||||
|
||||||
var domain container.Domain
|
||||||
domain.SetName(containerName)
|
||||||
if namespace != "" {
|
||||||
domain.SetZone(namespace + ".ns")
|
||||||
}
|
||||||
|
||||||
nnsCs, err := helper.GetContractByID(management.NewReader(inv), 1)
|
||||||
commonCmd.ExitOnErr(cmd, "can't get NNS contract state: %w", err)
|
||||||
|
||||||
cnrID, err = helper.NNSResolveContainerDomain(inv, nnsCs.Hash, domain.Name()+"."+domain.Zone())
|
||||||
commonCmd.ExitOnErr(cmd, "can't resolve container id: %w", err)
|
||||||
|
||||||
return cnrID
|
||||||
}
|
203
cmd/frostfs-adm/internal/modules/morph/ape/chains/list_user.go
Normal file
|
@ -0,0 +1,203 @@
|
|||
package chains
|
||||
|
||||
import (
|
||||
"encoding/hex"
|
||||
"errors"
|
||||
"fmt"
|
||||
"math/big"
|
||||
|
||||
"git.frostfs.info/TrueCloudLab/frostfs-contract/commonclient"
|
||||
ffsidclient "git.frostfs.info/TrueCloudLab/frostfs-contract/frostfsid/client"
|
||||
policycontract "git.frostfs.info/TrueCloudLab/frostfs-contract/policy"
|
||||
"git.frostfs.info/TrueCloudLab/frostfs-node/cmd/frostfs-adm/internal/commonflags"
|
||||
"git.frostfs.info/TrueCloudLab/frostfs-node/cmd/frostfs-adm/internal/modules/morph/ape/raw/output"
|
||||
"git.frostfs.info/TrueCloudLab/frostfs-node/cmd/frostfs-adm/internal/modules/morph/helper"
|
||||
|
||||
commonCmd "git.frostfs.info/TrueCloudLab/frostfs-node/cmd/internal/common"
|
||||
apeCmd "git.frostfs.info/TrueCloudLab/frostfs-node/cmd/internal/common/ape"
|
||||
apechain "git.frostfs.info/TrueCloudLab/policy-engine/pkg/chain"
|
||||
neoflags "github.com/nspcc-dev/neo-go/cli/flags"
|
||||
"github.com/nspcc-dev/neo-go/pkg/rpcclient"
|
||||
"github.com/nspcc-dev/neo-go/pkg/rpcclient/invoker"
|
||||
"github.com/nspcc-dev/neo-go/pkg/rpcclient/management"
|
||||
"github.com/nspcc-dev/neo-go/pkg/util"
|
||||
"github.com/nspcc-dev/neo-go/pkg/wallet"
|
||||
"github.com/spf13/cobra"
|
||||
"github.com/spf13/viper"
|
||||
)
|
||||
|
||||
var listUserCmd = &cobra.Command{
|
||||
Use: "list-user",
|
||||
Short: "List user related (groups/namespace) policies",
|
||||
Long: "List user related (groups/namespace) policies along with filtering by service (s3/storage)",
|
||||
Example: `policy-reader list-user -r http://localhost:40332 list --user NiGqBpUdMvAC68SxUeyYwVPyBCsqzNuof
|
||||
policy-reader list-user -r http://localhost:40332 --policy-hash 81c1a41d09e08087a4b679418b12be5d3ab15742 list --user NiGqBpUdMvAC68SxUeyYwVPyBCsqzNuofL --service s3`,
|
||||
RunE: runListCmd,
|
||||
}
|
||||
|
||||
var errUnknownChainNameType = errors.New("unknown chain-name")
|
||||
|
||||
const (
|
||||
userFlag = "user"
|
||||
namespaceFlag = "namespace"
|
||||
decodeChainFlag = "decode-chain"
|
||||
decodeIDFlag = "decode-id"
|
||||
)
|
||||
|
||||
const methodIteratorChainsByPrefix = "iteratorChainsByPrefix"
|
||||
|
||||
func initListUserCmd() {
|
||||
listUserCmd.Flags().StringP(commonflags.EndpointFlag, commonflags.EndpointFlagShort, "", commonflags.EndpointFlagDesc)
|
||||
listUserCmd.Flags().String(apeCmd.ChainNameFlag, "", apeCmd.ChainNameFlagDesc)
|
||||
listUserCmd.Flags().String(userFlag, "", "User address or name in frostfsid contract (if name is provided than 'namespace' should be set too)")
|
||||
listUserCmd.Flags().String(namespaceFlag, "", "Namespace where user name will be looked up")
|
||||
listUserCmd.Flags().Bool(decodeChainFlag, false, "Use this flag to decode chain")
|
||||
listUserCmd.Flags().Bool(decodeIDFlag, false, "Use this flag to additionally decode chain id (without --decode-chain no take effect)")
|
||||
|
||||
_ = listUserCmd.MarkFlagRequired(userFlag)
|
||||
}
|
||||
|
||||
func runListCmd(cmd *cobra.Command, _ []string) error {
|
||||
chainName := parseChainName(cmd)
|
||||
namespace := parseNamespace(cmd)
|
||||
|
||||
inv, policyHash, ffsidCli := initReaders(cmd)
|
||||
user, _ := cmd.Flags().GetString(userFlag)
|
||||
subj, err := resolveSubject(ffsidCli, namespace, user)
|
||||
commonCmd.ExitOnErr(cmd, "can't resolve frostfsid subject: %w", err)
|
||||
|
||||
printSubject(cmd, subj)
|
||||
|
||||
res, err := commonclient.ReadIteratorItems(inv, 100, policyHash, methodIteratorChainsByPrefix, big.NewInt(int64(policycontract.Namespace)), subj.Namespace, string(chainName))
|
||||
commonCmd.ExitOnErr(cmd, "can't read iterator: %w", err)
|
||||
|
||||
decodeChain, _ := cmd.Flags().GetBool(decodeChainFlag)
|
||||
decodeID, _ := cmd.Flags().GetBool(decodeIDFlag)
|
||||
|
||||
cmd.Printf("\nnamespace policies: %d\n", len(res))
|
||||
err = output.PrintChains(cmd, res, decodeChain, decodeID)
|
||||
commonCmd.ExitOnErr(cmd, "can't print chains: %w", err)
|
||||
|
||||
userEntity := big.NewInt(int64(policycontract.User))
|
||||
userEntityName := fmt.Sprintf("%s:%s", subj.Namespace, subj.PrimaryKey.Address())
|
||||
|
||||
res, err = commonclient.ReadIteratorItems(inv, 100, policyHash, methodIteratorChainsByPrefix, userEntity, userEntityName, string(chainName))
|
||||
commonCmd.ExitOnErr(cmd, "can't read iterator: %w", err)
|
||||
|
||||
cmd.Printf("\nuser policies: %d\n", len(res))
|
||||
err = output.PrintChains(cmd, res, decodeChain, decodeID)
|
||||
commonCmd.ExitOnErr(cmd, "can't print chains: %w", err)
|
||||
|
||||
cmd.Printf("\ngroup policies: %d\n", len(subj.Groups))
|
||||
|
||||
groupEntity := big.NewInt(int64(policycontract.Group))
|
||||
for _, group := range subj.Groups {
|
||||
groupEntityName := fmt.Sprintf("%s:%d", group.Namespace, group.ID)
|
||||
|
||||
res, err = commonclient.ReadIteratorItems(inv, 100, policyHash, methodIteratorChainsByPrefix, groupEntity, groupEntityName, string(chainName))
|
||||
commonCmd.ExitOnErr(cmd, "can't read iterator: %w", err)
|
||||
|
||||
cmd.Printf("user group '%s' (id: %d) policies: %d\n", group.Name, group.ID, len(res))
|
||||
err = output.PrintChains(cmd, res, decodeChain, decodeID)
|
||||
commonCmd.ExitOnErr(cmd, "can't print chains: %w", err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func resolveSubject(ffsid *ffsidclient.Client, namespace, userName string) (*ffsidclient.SubjectExtended, error) {
|
||||
if userHash, err := neoflags.ParseAddress(userName); err == nil {
|
||||
subj, err := ffsid.GetSubject(userHash)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return ffsid.GetSubjectExtended(subj.PrimaryKey.GetScriptHash())
|
||||
}
|
||||
|
||||
subj, err := ffsid.GetSubjectByName(namespace, userName)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return ffsid.GetSubjectExtended(subj.PrimaryKey.GetScriptHash())
|
||||
}
|
||||
|
||||
func parseChainName(cmd *cobra.Command) apechain.Name {
|
||||
chainName, _ := cmd.Flags().GetString(apeCmd.ChainNameFlag)
|
||||
|
||||
switch chainName {
|
||||
case "":
|
||||
return ""
|
||||
fyrchik marked this conversation as resolved
Outdated
fyrchik
commented
To my complete surprise, there is an old and undone issue: https://github.com/spf13/pflag/issues/236 which could be helpful here. To my complete surprise, there is an old and undone issue: https://github.com/spf13/pflag/issues/236 which could be helpful here.
|
||||
case "s3":
|
||||
return apechain.S3
|
||||
case "ingress":
|
||||
return apechain.Ingress
|
||||
}
|
||||
|
||||
commonCmd.ExitOnErr(cmd, "can't parse chain-name: %w", errUnknownChainNameType)
|
||||
panic("unreachable")
|
||||
}
|
||||
|
||||
func parseNamespace(cmd *cobra.Command) string {
|
||||
namespace, _ := cmd.Flags().GetString(namespaceFlag)
|
||||
if namespace == "root" {
|
||||
fyrchik marked this conversation as resolved
Outdated
fyrchik
commented
It accepts It accepts `SubjectExtended` but doesn't print `AdditionalKeys`. It this intended?
dkirillov
commented
Probably this was done (in Probably this was done (in `policy-reader` tool) before additional keys starts to be useful to see
|
||||
namespace = ""
|
||||
}
|
||||
|
||||
return namespace
|
||||
}
|
||||
|
||||
func printSubject(cmd *cobra.Command, subj *ffsidclient.SubjectExtended) {
|
||||
cmd.Println("subject:")
|
||||
cmd.Printf("\tnamespace: '%s'\n", subj.Namespace)
|
||||
cmd.Printf("\tname: '%s'\n", subj.Name)
|
||||
cmd.Printf("\tkey: '%s'\n", hex.EncodeToString(subj.PrimaryKey.Bytes()))
|
||||
|
||||
cmd.Printf("\tadditional keys:\n")
|
||||
for _, key := range subj.AdditionalKeys {
|
||||
cmd.Printf("\t\t%s\n", hex.EncodeToString(key.Bytes()))
|
||||
}
|
||||
|
||||
fyrchik
commented
We print all other arrays line-by-line, and here we use We print all other arrays line-by-line, and here we use `%v`, why so?
dkirillov
commented
Because we don't need extra formatting in this case Because we don't need extra formatting in this case
fyrchik
commented
I mean, why we don't need it? I mean, why we don't need it?
Having keys line-by-line is easier to read and will correspond to the formatting of other array values.
|
||||
cmd.Printf("\tclaims:\n")
|
||||
for k, v := range subj.KV {
|
||||
cmd.Printf("\t\t%s: '%s'\n", k, v)
|
||||
}
|
||||
cmd.Printf("\tgroups:\n")
|
||||
for _, gr := range subj.Groups {
|
||||
cmd.Printf("\t\t%d: '%s'\n", gr.ID, gr.Name)
|
||||
}
|
||||
}
|
||||
|
||||
func initReaders(cmd *cobra.Command) (*invoker.Invoker, util.Uint160, *ffsidclient.Client) {
|
||||
endpoint := viper.GetString(commonflags.EndpointFlag)
|
||||
|
||||
rpcCli, err := rpcclient.New(cmd.Context(), endpoint, rpcclient.Options{})
|
||||
commonCmd.ExitOnErr(cmd, "can't init rpc client: %w", err)
|
||||
|
||||
inv := invoker.New(rpcCli, nil)
|
||||
nnsCs, err := helper.GetContractByID(management.NewReader(inv), 1)
|
||||
commonCmd.ExitOnErr(cmd, "can't get NNS contract state: %w", err)
|
||||
|
||||
policyHashStr, _ := cmd.Flags().GetString(policyHashFlag)
|
||||
policyHash, err := util.Uint160DecodeStringLE(policyHashStr)
|
||||
if err != nil {
|
||||
policyHash, err = helper.NNSResolveHash(inv, nnsCs.Hash, policyHashStr)
|
||||
commonCmd.ExitOnErr(cmd, "can't resolve NNS policy contract: %w", err)
|
||||
}
|
||||
|
||||
frostfsidHashStr, _ := cmd.Flags().GetString(frostfsidHashFlag)
|
||||
frostfsidHash, err := util.Uint160DecodeStringLE(policyHashStr)
|
||||
fyrchik
commented
Is it really Is it really `policyHashStr`?
|
||||
if err != nil {
|
||||
frostfsidHash, err = helper.NNSResolveHash(inv, nnsCs.Hash, frostfsidHashStr)
|
||||
commonCmd.ExitOnErr(cmd, "can't resolve NNS frostfsid contract: %w", err)
|
||||
}
|
||||
|
||||
acc, err := wallet.NewAccount()
|
||||
commonCmd.ExitOnErr(cmd, "can't create new account: %w", err)
|
||||
|
||||
ffsidCli, err := ffsidclient.New(rpcCli, acc, frostfsidHash, ffsidclient.Options{})
|
||||
commonCmd.ExitOnErr(cmd, "can't init frostfsid client: %w", err)
|
||||
|
||||
return inv, policyHash, ffsidCli
|
||||
}
|
32
cmd/frostfs-adm/internal/modules/morph/ape/chains/root.go
Normal file
|
@ -0,0 +1,32 @@
|
|||
package chains
|
||||
|
||||
import (
|
||||
"git.frostfs.info/TrueCloudLab/frostfs-node/cmd/frostfs-adm/internal/commonflags"
|
||||
"github.com/spf13/cobra"
|
||||
"github.com/spf13/viper"
|
||||
)
|
||||
|
||||
var Cmd = &cobra.Command{
|
||||
Use: "chains",
|
||||
Short: "Chain related policy operations",
|
||||
Long: "Chain related policy operations. Complex scenarios like: list all user chains (including groups, namespaces).",
|
||||
PersistentPreRun: func(cmd *cobra.Command, _ []string) {
|
||||
_ = viper.BindPFlag(commonflags.EndpointFlag, cmd.Flags().Lookup(commonflags.EndpointFlag))
|
||||
},
|
||||
}
|
||||
|
||||
const (
|
||||
policyHashFlag = "policy-hash"
|
||||
frostfsidHashFlag = "frostfsid-hash"
|
||||
)
|
||||
|
||||
func init() {
|
||||
aarifullin marked this conversation as resolved
Outdated
aarifullin
commented
We don't need to pass it. See We don't need to pass it. [See](https://git.frostfs.info/TrueCloudLab/frostfs-node/src/branch/master/cmd/frostfs-adm/internal/modules/morph/ape/ape_util.go#L64-L65)
dkirillov
commented
These commands aimed to debug These commands aimed to debug `policy` contract in any environment. In some environment this contract isn't registered in NNS
aarifullin
commented
Okay. You haven't marked the flag as required but it's not processed when it's empty either. Okay. You haven't marked the flag as required but it's not processed when it's empty either.
Could we use searching within NNS as default behavior if its value is empty?
|
||||
Cmd.PersistentFlags().String(policyHashFlag, "policy.frostfs", "NNS name or script hash of policy contract")
|
||||
Cmd.PersistentFlags().String(frostfsidHashFlag, "frostfsid.frostfs", "NNS name or script hash of frostfsid contract")
|
||||
|
||||
Cmd.AddCommand(listUserCmd)
|
||||
initListUserCmd()
|
||||
fyrchik
commented
Every other command may resolve contract hash from NNS. Every other command may resolve contract hash from NNS.
`frostfs-adm` may resolve any domain too.
Why this argument exists?
dkirillov
commented
https://git.frostfs.info/TrueCloudLab/frostfs-node/pulls/1607#issuecomment-64644
|
||||
|
||||
Cmd.AddCommand(listContainerCmd)
|
||||
initListContainerCmd()
|
||||
}
|
|
@ -0,0 +1,74 @@
|
|||
package raw
|
||||
|
||||
import (
|
||||
"encoding/base64"
|
||||
|
||||
"git.frostfs.info/TrueCloudLab/frostfs-contract/commonclient"
|
||||
"git.frostfs.info/TrueCloudLab/frostfs-node/cmd/frostfs-adm/internal/commonflags"
|
||||
commonCmd "git.frostfs.info/TrueCloudLab/frostfs-node/cmd/internal/common"
|
||||
"github.com/spf13/cobra"
|
||||
"github.com/spf13/viper"
|
||||
)
|
||||
|
||||
var listChainNamesCmd = &cobra.Command{
|
||||
Use: "list-chain-names",
|
||||
Short: "Invoke 'listChainNames' method",
|
||||
Long: "Invoke 'listChainNames' method in policy contract and print results to stdout",
|
||||
Example: `raw -r http://localhost:40332 list-chain-names --kind n --name ''
|
||||
raw -r http://localhost:40332 --policy-hash 81c1a41d09e08087a4b679418b12be5d3ab15742 list-chain-names --kind c --name 7h7NcXcF6k6b1yidqEHc1jkyXUm1MfUDrrTuHAefhiDe`,
|
||||
RunE: runListChainNamesCmd,
|
||||
}
|
||||
|
||||
const (
|
||||
nameFlag = "name"
|
||||
nameBase64Flag = "name-base64"
|
||||
)
|
||||
|
||||
func initListChainNamesCmd() {
|
||||
listChainNamesCmd.Flags().StringP(commonflags.EndpointFlag, commonflags.EndpointFlagShort, "", commonflags.EndpointFlagDesc)
|
||||
listChainNamesCmd.Flags().String(kindFlag, "n", "Target kind (1-byte) to list (n(namespace)/c(container)/g(group)/u(user)/i(iam)) or its integer representation")
|
||||
listChainNamesCmd.Flags().String(nameFlag, "", "Target name to list")
|
||||
listChainNamesCmd.Flags().Bool(nameBase64Flag, false, "Use this flag if you provide name in base64 format")
|
||||
|
||||
_ = listChainNamesCmd.MarkFlagRequired(kindFlag)
|
||||
_ = listChainNamesCmd.MarkFlagRequired(nameFlag)
|
||||
}
|
||||
|
||||
func runListChainNamesCmd(cmd *cobra.Command, _ []string) error {
|
||||
kind, _ := cmd.Flags().GetString(kindFlag)
|
||||
entity, err := parseTargetKind(kind)
|
||||
commonCmd.ExitOnErr(cmd, "can't parse target kind: %w", err)
|
||||
|
||||
entityName, err := parseEntityName(cmd)
|
||||
commonCmd.ExitOnErr(cmd, "can't parse name: %w", err)
|
||||
|
||||
inv, policyHash := initPolicyReader(cmd)
|
||||
res, err := commonclient.ReadIteratorItems(inv, 100, policyHash, methodListChainNames, entity, entityName)
|
||||
commonCmd.ExitOnErr(cmd, "can't read iterator: %w", err)
|
||||
|
||||
cmd.Printf("%s target chain names: %d\n", kind, len(res))
|
||||
|
||||
for _, re := range res {
|
||||
bytes, err := re.TryBytes()
|
||||
commonCmd.ExitOnErr(cmd, "can't parse result: %w", err)
|
||||
|
||||
cmd.Printf("%s\t(base64: '%s')\n", string(bytes), base64.StdEncoding.EncodeToString(bytes))
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func parseEntityName(cmd *cobra.Command) ([]byte, error) {
|
||||
entityNameStr, _ := cmd.Flags().GetString(nameFlag)
|
||||
var entityName []byte
|
||||
if viper.GetBool(nameBase64Flag) {
|
||||
return base64.StdEncoding.DecodeString(entityNameStr)
|
||||
}
|
||||
|
||||
if entityNameStr == "root" {
|
||||
entityNameStr = ""
|
||||
}
|
||||
entityName = []byte(entityNameStr)
|
||||
|
||||
return entityName, nil
|
||||
}
|
|
@ -0,0 +1,71 @@
|
|||
package raw
|
||||
|
||||
import (
|
||||
"encoding/base64"
|
||||
|
||||
"git.frostfs.info/TrueCloudLab/frostfs-contract/commonclient"
|
||||
"git.frostfs.info/TrueCloudLab/frostfs-node/cmd/frostfs-adm/internal/commonflags"
|
||||
"git.frostfs.info/TrueCloudLab/frostfs-node/cmd/frostfs-adm/internal/modules/morph/ape/raw/output"
|
||||
commonCmd "git.frostfs.info/TrueCloudLab/frostfs-node/cmd/internal/common"
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
var listChainsByPrefixCmd = &cobra.Command{
|
||||
Use: "list-chains-by-prefix",
|
||||
Short: "Invoke 'iteratorChainsByPrefix' method",
|
||||
Long: "Invoke 'iteratorChainsByPrefix' method in policy contract and print results to stdout",
|
||||
Example: `raw -r http://localhost:40332 list-chains-by-prefix --kind n --name ''
|
||||
raw -r http://localhost:40332 --policy-hash 81c1a41d09e08087a4b679418b12be5d3ab15742 list-chains-by-prefix --kind c --name 7h7NcXcF6k6b1yidqEHc1jkyXUm1MfUDrrTuHAefhiDe`,
|
||||
RunE: runListChainsByPrefixCmd,
|
||||
}
|
||||
|
||||
const (
|
||||
prefixFlag = "prefix"
|
||||
prefixBase64Flag = "prefix-base64"
|
||||
decodeChainFlag = "decode-chain"
|
||||
decodeIDFlag = "decode-id"
|
||||
)
|
||||
|
||||
func initListChainsByPrefixCmd() {
|
||||
listChainsByPrefixCmd.Flags().StringP(commonflags.EndpointFlag, commonflags.EndpointFlagShort, "", commonflags.EndpointFlagDesc)
|
||||
listChainsByPrefixCmd.Flags().String(kindFlag, "n", "Target kind (1-byte) to list (n(namespace)/c(container)/g(group)/u(user)/i(iam)) or its integer representation")
|
||||
listChainsByPrefixCmd.Flags().String(nameFlag, "", "Target name to list")
|
||||
listChainsByPrefixCmd.Flags().String(prefixFlag, "", "Prefix to list")
|
||||
listChainsByPrefixCmd.Flags().Bool(prefixBase64Flag, false, "Use this flag if you provide prefix in base64 format")
|
||||
listChainsByPrefixCmd.Flags().Bool(nameBase64Flag, false, "Use this flag if you provide name in base64 format")
|
||||
listChainsByPrefixCmd.Flags().Bool(decodeChainFlag, false, "Use this flag to decode chain")
|
||||
listChainsByPrefixCmd.Flags().Bool(decodeIDFlag, false, "Use this flag to additionally decode chain id (without --decode-chain no take effect)")
|
||||
|
||||
_ = listChainsByPrefixCmd.MarkFlagRequired(kindFlag)
|
||||
_ = listChainsByPrefixCmd.MarkFlagRequired(nameFlag)
|
||||
}
|
||||
|
||||
func runListChainsByPrefixCmd(cmd *cobra.Command, _ []string) error {
|
||||
kind, _ := cmd.Flags().GetString(kindFlag)
|
||||
entity, err := parseTargetKind(kind)
|
||||
commonCmd.ExitOnErr(cmd, "can't parse target kind: %w", err)
|
||||
|
||||
entityName, err := parseEntityName(cmd)
|
||||
commonCmd.ExitOnErr(cmd, "can't parse name: %w", err)
|
||||
|
||||
prefixStr, _ := cmd.Flags().GetString(prefixFlag)
|
||||
prefixBase64, _ := cmd.Flags().GetBool(prefixBase64Flag)
|
||||
var prefix []byte
|
||||
if prefixBase64 {
|
||||
if prefix, err = base64.StdEncoding.DecodeString(prefixStr); err != nil {
|
||||
return err
|
||||
}
|
||||
} else {
|
||||
prefix = []byte(prefixStr)
|
||||
}
|
||||
|
||||
inv, policyHash := initPolicyReader(cmd)
|
||||
res, err := commonclient.ReadIteratorItems(inv, 100, policyHash, methodIteratorChainsByPrefix, entity, entityName, prefix)
|
||||
commonCmd.ExitOnErr(cmd, "can't read iterator: %w", err)
|
||||
|
||||
cmd.Printf("%s target chains names: %d\n", kind, len(res))
|
||||
|
||||
decodeChain, _ := cmd.Flags().GetBool(decodeChainFlag)
|
||||
decodeID, _ := cmd.Flags().GetBool(decodeIDFlag)
|
||||
return output.PrintChains(cmd, res, decodeChain, decodeID)
|
||||
}
|
100
cmd/frostfs-adm/internal/modules/morph/ape/raw/list_targets.go
Normal file
|
@ -0,0 +1,100 @@
|
|||||
package raw
|
||||||
|
||||||
import (
|
||||||
"encoding/base64"
|
||||||
"fmt"
|
||||||
"math/big"
|
||||||
"strconv"
|
||||||
|
||||||
"git.frostfs.info/TrueCloudLab/frostfs-contract/commonclient"
|
||||||
"git.frostfs.info/TrueCloudLab/frostfs-node/cmd/frostfs-adm/internal/commonflags"
|
||||||
"git.frostfs.info/TrueCloudLab/frostfs-node/cmd/frostfs-adm/internal/modules/morph/helper"
|
||||||
commonCmd "git.frostfs.info/TrueCloudLab/frostfs-node/cmd/internal/common"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/rpcclient"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/rpcclient/invoker"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/rpcclient/management"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/util"
|
||||||
"github.com/spf13/cobra"
|
||||||
"github.com/spf13/viper"
|
||||||
)
|
||||||
|
||||||
var listTargetsCmd = &cobra.Command{
|
||||||
Use: "list-targets",
|
||||||
Short: "Invoke 'listTargets' method",
|
||||||
Long: "Invoke 'listTargets' method in policy contract and print results to stdout",
|
||||||
Example: `raw -r http://localhost:40332 list-targets
|
||||||
raw -r http://localhost:40332 --policy-hash 81c1a41d09e08087a4b679418b12be5d3ab15742 list-targets --kind c
|
||||||
raw -r http://localhost:40332 --policy-hash 81c1a41d09e08087a4b679418b12be5d3ab15742 list-targets --kind 99`,
|
||||||
RunE: runListTargetsCmd,
|
||||||
}
|
||||||
|
||||||
const (
|
||||||
kindFlag = "kind"
|
||||||
)
|
||||||
|
||||||
const (
|
||||||
methodIteratorChainsByPrefix = "iteratorChainsByPrefix"
|
||||||
methodListTargets = "listTargets"
|
||||||
methodListChainNames = "listChainNames"
|
||||||
)
|
||||||
|
||||||
func initListTargetsCmd() {
|
||||||
listTargetsCmd.Flags().StringP(commonflags.EndpointFlag, commonflags.EndpointFlagShort, "", commonflags.EndpointFlagDesc)
|
||||||
listTargetsCmd.Flags().String(kindFlag, "n", "Target kind (1-byte) to list (n(namespace)/c(container)/g(group)/u(user)/i(iam)) or its integer representation")
|
||||||
}
|
||||||
|
||||||
func runListTargetsCmd(cmd *cobra.Command, _ []string) error {
|
||||||
kind, _ := cmd.Flags().GetString(kindFlag)
|
||||||
entity, err := parseTargetKind(kind)
|
||||||
commonCmd.ExitOnErr(cmd, "can't parse target kind: %w", err)
|
||||||
|
||||||
inv, policyHash := initPolicyReader(cmd)
|
||||||
res, err := commonclient.ReadIteratorItems(inv, 100, policyHash, methodListTargets, entity)
|
||||||
commonCmd.ExitOnErr(cmd, "can't read iterator: %w", err)
|
||||||
|
||||||
cmd.Printf("%s targets: %d\n", kind, len(res))
|
||||||
|
||||||
for _, re := range res {
|
||||||
bytes, err := re.TryBytes()
|
||||||
commonCmd.ExitOnErr(cmd, "can't parse result: %w", err)
|
||||||
|
||||||
cmd.Printf("%s\t(base64: '%s')\n", string(bytes), base64.StdEncoding.EncodeToString(bytes))
|
||||||
}
|
||||||
|
||||||
return nil
|
||||||
}
|
||||||
|
||||||
func parseTargetKind(typ string) (*big.Int, error) {
|
||||||
val, err := strconv.ParseInt(typ, 10, 64)
|
||||||
if err == nil {
|
||||||
return big.NewInt(val), nil
|
||||||
}
|
||||||
|
||||||
if len(typ) != 1 {
|
||||||
fyrchik
commented
This is very error-prone: we connect some constants from contracts with CLI interface in the frostfs-adm. And, to be fair, full multi-byte names or even boolean flags look better IMO. This is very error-prone: we connect some constants from contracts with CLI interface in the frostfs-adm.
If something changes, we won't notice.
Can we make this an implicit switch that uses some exported constants from the `policy` contract?
And, to be fair, full multi-byte names or even boolean flags look better IMO.
dkirillov
commented
I consider this command as low-level one, so I would like to be able to invoke ListTarget using any valid one-byte parameter (the same way as I can do this by I consider this command as low-level one, so I would like to be able to invoke [ListTarget](https://git.frostfs.info/TrueCloudLab/frostfs-contract/src/commit/201db45bd739fb729448ea8c806ae51ff01a5f2d/policy/policy_contract.go#L254) using any valid one-byte parameter (the same way as I can do this by `neo-go contract testinvokefuntion`)
fyrchik
commented
But can we provide e.g. But can we provide e.g. `\x00`?
dkirillov
commented
Update parsing to be able provide just number Update parsing to be able provide just number
|
||||||
return nil, fmt.Errorf("invalid type: %s", typ)
|
||||||
}
|
||||||
|
||||||
return big.NewInt(int64(typ[0])), nil
|
||||||
}
|
||||||
|
||||||
func initPolicyReader(cmd *cobra.Command) (*invoker.Invoker, util.Uint160) {
|
||||||
endpoint := viper.GetString(commonflags.EndpointFlag)
|
||||||
|
||||||
rpcCli, err := rpcclient.New(cmd.Context(), endpoint, rpcclient.Options{})
|
||||||
commonCmd.ExitOnErr(cmd, "can't init rpc client: %w", err)
|
||||||
aarifullin marked this conversation as resolved
Outdated
aarifullin
commented
Please, let this flag be optional (see this) Please, let this flag be optional (see [this](https://git.frostfs.info/TrueCloudLab/frostfs-node/pulls/1607#issuecomment-64674))
dkirillov
commented
This is already optional, If I understood you correctly. I have default Line 23 in dkirillov/frostfs-node@4022349
This is already optional, If I understood you correctly. I have default `policy.frostfs` value for this flag https://git.frostfs.info/dkirillov/frostfs-node/src/commit/4022349fbe06f374bd15cf9a29f043d5827c2f2a/cmd/frostfs-adm/internal/modules/morph/ape/raw/root.go#L23
aarifullin
commented
Ah, yeah. Correct! Ah, yeah. Correct!
|
||||||
|
||||||
inv := invoker.New(rpcCli, nil)
|
||||||
|
||||||
policyHashStr, _ := cmd.Flags().GetString(policyHashFlag)
|
||||||
if policyHash, err := util.Uint160DecodeStringLE(policyHashStr); err == nil {
|
||||||
return inv, policyHash
|
||||||
fyrchik
commented
Could you explain, why this code works? What am I missing here? Could you explain, why this code works?
We get policyhash, which is hex-encoded contract hash we would like to use.
But instead of decoding it we use `NNSResolveHash` where `policyHashStr` is provided as a _domain_ and resolved.
Secondly, regardless of how it works, if the user provides explicit policy hash, we should not care about NNS at all.
What am I missing here?
dkirillov
commented
Oh, It seems I've lost decoding during refactor. I'll fix it Oh, It seems I've lost decoding during refactor. I'll fix it
fyrchik
commented
1. There are other places like this too, please check them.
2. Now we skip `err != nil` for some reason. Why? We expect the hash, if it is invalid command should fail.
dkirillov
commented
2. It's similar to the getting flags across frostfs-adm code e.g. https://git.frostfs.info/TrueCloudLab/frostfs-node/src/commit/0991077cb364c94e40f6838035acab4754b4d8af/cmd/frostfs-adm/internal/modules/morph/ape/ape.go#L202
fyrchik
commented
We skip an error there because it cannot possibly happen (the flag is defined and all flags can be used as string). We skip an error there because it cannot possibly happen (the flag is defined and all flags can be used as string).
The error you skip (in the `if` below this line) happens during parsing.
dkirillov
commented
If parsing failed then we should try to resolve it in NNS, because it can have format like If parsing failed then we should try to resolve it in NNS, because it can have format like `policy.frostfs`
fyrchik
commented
Then it should be a Then it should be a `--policy-domain` and not `--policy-hash`.
However, I think `policy-hash` is enough, if we want to use a custom domain, we might as well resolve it separately.
|
||||||
}
|
||||||
|
||||||
nnsCs, err := helper.GetContractByID(management.NewReader(inv), 1)
|
||||||
commonCmd.ExitOnErr(cmd, "can't get NNS contract state: %w", err)
|
||||||
|
||||||
policyHash, err := helper.NNSResolveHash(inv, nnsCs.Hash, policyHashStr)
|
||||||
commonCmd.ExitOnErr(cmd, "can't resolve NNS policy contract: %w", err)
|
||||||
|
||||||
return inv, policyHash
|
||||||
}
|
|
@ -0,0 +1,56 @@
|
|||
package output
|
||||
|
||||
import (
|
||||
"encoding/base64"
|
||||
|
||||
apechain "git.frostfs.info/TrueCloudLab/policy-engine/pkg/chain"
|
||||
"github.com/nspcc-dev/neo-go/pkg/vm/stackitem"
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
const (
|
||||
minPrintable = 32
|
||||
maxPrintable = 127
|
||||
)
|
||||
|
||||
func PrintChains(cmd *cobra.Command, list []stackitem.Item, decodeChain, decodeID bool) error {
|
||||
for _, item := range list {
|
||||
bytes, err := item.TryBytes()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if !decodeChain {
|
||||
cmd.Printf("\t%s\n", string(bytes))
|
||||
continue
|
||||
}
|
||||
|
||||
var chain apechain.Chain
|
||||
if err = chain.DecodeBytes(bytes); err != nil {
|
||||
cmd.PrintErrf("invalid chain format: %s\n", base64.StdEncoding.EncodeToString(bytes))
|
||||
continue
|
||||
}
|
||||
|
||||
raw, err := chain.MarshalJSON()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if decodeID {
|
||||
var printableID string
|
||||
|
||||
for _, r := range string(chain.ID) {
|
||||
if minPrintable <= r && r <= maxPrintable {
|
||||
printableID += string(r)
|
||||
} else {
|
||||
printableID += "."
|
||||
}
|
||||
}
|
||||
cmd.Printf("\t%s - %s\n", printableID, string(raw))
|
||||
} else {
|
||||
cmd.Printf("\t%s\n", string(raw))
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
32
cmd/frostfs-adm/internal/modules/morph/ape/raw/root.go
Normal file
|
@ -0,0 +1,32 @@
|
|||
package raw
|
||||
|
||||
import (
|
||||
"git.frostfs.info/TrueCloudLab/frostfs-node/cmd/frostfs-adm/internal/commonflags"
|
||||
"github.com/spf13/cobra"
|
||||
"github.com/spf13/viper"
|
||||
)
|
||||
|
||||
var Cmd = &cobra.Command{
|
||||
Use: "raw",
|
||||
Short: "FrostFS policy contract raw reader",
|
||||
PersistentPreRun: func(cmd *cobra.Command, _ []string) {
|
||||
aarifullin marked this conversation as resolved
Outdated
aarifullin
commented
`raw` is subcommand and `Long` won't be showed in the prompt. You can leave `Short` only
|
||||
_ = viper.BindPFlag(commonflags.EndpointFlag, cmd.Flags().Lookup(commonflags.EndpointFlag))
|
||||
},
|
||||
}
|
||||
|
||||
const (
|
||||
policyHashFlag = "policy-hash"
|
||||
)
|
||||
|
||||
func init() {
|
||||
Cmd.PersistentFlags().String(policyHashFlag, "policy.frostfs", "NNS name or script hash of policy contract")
|
||||
|
||||
Cmd.AddCommand(listTargetsCmd)
|
||||
initListTargetsCmd()
|
||||
|
||||
Cmd.AddCommand(listChainNamesCmd)
|
||||
initListChainNamesCmd()
|
||||
|
||||
Cmd.AddCommand(listChainsByPrefixCmd)
|
||||
initListChainsByPrefixCmd()
|
||||
}
|
|
@ -1,6 +1,10 @@
|
|||
package ape
|
||||
|
||||
import "github.com/spf13/cobra"
|
||||
import (
|
||||
"git.frostfs.info/TrueCloudLab/frostfs-node/cmd/frostfs-adm/internal/modules/morph/ape/chains"
|
||||
"git.frostfs.info/TrueCloudLab/frostfs-node/cmd/frostfs-adm/internal/modules/morph/ape/raw"
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
var Cmd = &cobra.Command{
|
||||
Use: "ape",
|
||||
|
@ -8,6 +12,9 @@ var Cmd = &cobra.Command{
|
|||
}
|
||||
|
||||
func init() {
|
||||
Cmd.AddCommand(raw.Cmd)
|
||||
Cmd.AddCommand(chains.Cmd)
|
||||
|
||||
initAddRuleChainCmd()
|
||||
initRemoveRuleChainCmd()
|
||||
initListRuleChainsCmd()
|
||||
|
|
|
@ -10,6 +10,7 @@ import (
|
|||
"git.frostfs.info/TrueCloudLab/frostfs-node/cmd/frostfs-adm/internal/modules/config"
|
||||
"git.frostfs.info/TrueCloudLab/frostfs-node/cmd/frostfs-adm/internal/modules/morph/constants"
|
||||
commonCmd "git.frostfs.info/TrueCloudLab/frostfs-node/cmd/internal/common"
|
||||
cid "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/container/id"
|
||||
"github.com/nspcc-dev/neo-go/pkg/core/native/nativenames"
|
||||
"github.com/nspcc-dev/neo-go/pkg/crypto/keys"
|
||||
"github.com/nspcc-dev/neo-go/pkg/encoding/address"
|
||||
|
@ -158,6 +159,40 @@ func NNSResolveHash(inv *invoker.Invoker, nnsHash util.Uint160, domain string) (
|
|||
return ParseNNSResolveResult(item)
|
||||
}
|
||||
|
||||
// NNSResolveContainerDomain returns errMissingNNSRecord if invocation fault exception contains "token not found".
|
||||
func NNSResolveContainerDomain(inv *invoker.Invoker, nnsHash util.Uint160, domain string) (cid.ID, error) {
|
||||
item, err := NNSResolve(inv, nnsHash, domain)
|
||||
if err != nil {
|
||||
return cid.ID{}, err
|
||||
}
|
||||
|
||||
return parseNNSResolveResultCID(item)
|
||||
}
|
||||
|
||||
func parseNNSResolveResultCID(res stackitem.Item) (cid.ID, error) {
|
||||
arr, ok := res.Value().([]stackitem.Item)
|
||||
if !ok {
|
||||
arr = []stackitem.Item{res}
|
||||
}
|
||||
if _, ok := res.Value().(stackitem.Null); ok || len(arr) == 0 {
|
||||
return cid.ID{}, errors.New("NNS record is missing")
|
||||
}
|
||||
|
||||
var cnrID cid.ID
|
||||
|
||||
for i := range arr {
|
||||
bs, err := arr[i].TryBytes()
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
|
||||
if err = cnrID.DecodeString(string(bs)); err == nil {
|
||||
return cnrID, nil
|
||||
}
|
||||
}
|
||||
return cid.ID{}, errors.New("no valid CIDs are found")
|
||||
}
|
||||
|
||||
func DomainOf(contract string) string {
|
||||
return contract + ".frostfs"
|
||||
}
|
||||
|
|
|
@ -7,7 +7,7 @@ const (
|
|||
TargetNameFlag = "target-name"
|
||||
TargetNameFlagDesc = "Resource name in APE resource name format"
|
||||
TargetTypeFlag = "target-type"
|
||||
TargetTypeFlagDesc = "Resource type(container/namespace)"
|
||||
TargetTypeFlagDesc = "Resource type(container/namespace/group/user)"
|
||||
fyrchik marked this conversation as resolved
Outdated
fyrchik
commented
This should be in a separate commit. This should be in a separate commit.
|
||||
ChainIDFlag = "chain-id"
|
||||
ChainIDFlagDesc = "Chain id"
|
||||
ChainIDHexFlag = "chain-id-hex"
|
||||
|
|
I would like to avoid using this package in this repo.
Ideally all node <-> neo-go communication is done via autogenerated code or helpers from the
frostfs-contract
.We already have multiple commands using different mechanisms (actors vs common client from the frostfsid), let's not add yet another one.
Specifically, I would like to make this a thing #1035, so connection handling should be decoupled from the argument parsing.