adm/ape: Adopt policy reader #1607
11 changed files with 708 additions and 2 deletions
|
@ -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"
|
||||||
|
"github.com/nspcc-dev/neo-go/pkg/rpcclient/management"
|
||||||
|
"github.com/spf13/cobra"
|
||||||
|
)
|
||||||
|
|
||||||
|
var listContainerCmd = &cobra.Command{
|
||||||
|
Use: "list-container",
|
||||||
|
Short: "List container related (namespace) policies",
|
||||||
|
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,
|
||||||
|
}
|
||||||
|
|
||||||
|
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)
|
||||||
|
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 {
|
||||||
|
containerName, _ := cmd.Flags().GetString(containerFlag)
|
||||||
|
|
||||||
|
var cnrID cid.ID
|
||||||
|
if err := cnrID.DecodeString(containerName); err == nil {
|
||||||
|
return cnrID
|
||||||
|
}
|
||||||
|
|
||||||
|
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
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 ""
|
||||||
|
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" {
|
||||||
|
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()))
|
||||||
|
}
|
||||||
|
|
||||||
|
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)
|
||||||
|
|||||||
|
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
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() {
|
||||||
|
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()
|
||||||
|
|
||||||
|
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
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 {
|
||||||
|
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)
|
||||||
|
|
||||||
|
inv := invoker.New(rpcCli, nil)
|
||||||
|
|
||||||
|
policyHashStr, _ := cmd.Flags().GetString(policyHashFlag)
|
||||||
|
if policyHash, err := util.Uint160DecodeStringLE(policyHashStr); err == nil {
|
||||||
|
return inv, policyHash
|
||||||
|
}
|
||||||
|
|
||||||
|
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
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) {
|
||||||
|
_ = 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
|
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{
|
var Cmd = &cobra.Command{
|
||||||
Use: "ape",
|
Use: "ape",
|
||||||
|
@ -8,6 +12,9 @@ var Cmd = &cobra.Command{
|
||||||
}
|
}
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
|
Cmd.AddCommand(raw.Cmd)
|
||||||
|
Cmd.AddCommand(chains.Cmd)
|
||||||
|
|
||||||
initAddRuleChainCmd()
|
initAddRuleChainCmd()
|
||||||
initRemoveRuleChainCmd()
|
initRemoveRuleChainCmd()
|
||||||
initListRuleChainsCmd()
|
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/config"
|
||||||
"git.frostfs.info/TrueCloudLab/frostfs-node/cmd/frostfs-adm/internal/modules/morph/constants"
|
"git.frostfs.info/TrueCloudLab/frostfs-node/cmd/frostfs-adm/internal/modules/morph/constants"
|
||||||
commonCmd "git.frostfs.info/TrueCloudLab/frostfs-node/cmd/internal/common"
|
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/core/native/nativenames"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/crypto/keys"
|
"github.com/nspcc-dev/neo-go/pkg/crypto/keys"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/encoding/address"
|
"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)
|
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 {
|
func DomainOf(contract string) string {
|
||||||
return contract + ".frostfs"
|
return contract + ".frostfs"
|
||||||
}
|
}
|
||||||
|
|
|
@ -7,7 +7,7 @@ const (
|
||||||
TargetNameFlag = "target-name"
|
TargetNameFlag = "target-name"
|
||||||
TargetNameFlagDesc = "Resource name in APE resource name format"
|
TargetNameFlagDesc = "Resource name in APE resource name format"
|
||||||
TargetTypeFlag = "target-type"
|
TargetTypeFlag = "target-type"
|
||||||
TargetTypeFlagDesc = "Resource type(container/namespace)"
|
TargetTypeFlagDesc = "Resource type(container/namespace/group/user)"
|
||||||
ChainIDFlag = "chain-id"
|
ChainIDFlag = "chain-id"
|
||||||
ChainIDFlagDesc = "Chain id"
|
ChainIDFlagDesc = "Chain id"
|
||||||
ChainIDHexFlag = "chain-id-hex"
|
ChainIDHexFlag = "chain-id-hex"
|
||||||
|
|
Loading…
Add table
Reference in a new issue
Is it really
policyHashStr
?