adm: Add commands to invoke methods of policy
contract #868
4 changed files with 302 additions and 0 deletions
162
cmd/frostfs-adm/internal/modules/morph/ape.go
Normal file
162
cmd/frostfs-adm/internal/modules/morph/ape.go
Normal file
|
@ -0,0 +1,162 @@
|
||||||
|
package morph
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"encoding/json"
|
||||||
|
|
||||||
|
parseutil "git.frostfs.info/TrueCloudLab/frostfs-node/cmd/frostfs-cli/modules/util"
|
||||||
|
commonCmd "git.frostfs.info/TrueCloudLab/frostfs-node/cmd/internal/common"
|
||||||
|
apechain "git.frostfs.info/TrueCloudLab/policy-engine/pkg/chain"
|
||||||
|
"github.com/spf13/cobra"
|
||||||
|
"github.com/spf13/viper"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
namespaceTarget = "namespace"
|
||||||
|
containerTarget = "container"
|
||||||
|
jsonFlag = "json"
|
||||||
|
jsonFlagDesc = "Output rule chains in JSON format"
|
||||||
|
chainIDFlag = "chain-id"
|
||||||
|
chainIDDesc = "Rule chain ID"
|
||||||
|
ruleFlag = "rule"
|
||||||
|
ruleFlagDesc = "Rule chain in text format"
|
||||||
|
ruleJSONFlag = "rule-json"
|
||||||
|
ruleJSONFlagDesc = "Chain rule in JSON format or path to the file"
|
||||||
|
targetNameFlag = "target-name"
|
||||||
|
targetNameDesc = "Resource name in APE resource name format"
|
||||||
|
targetTypeFlag = "target-type"
|
||||||
|
targetTypeDesc = "Resource type(container/namespace)"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
apeCmd = &cobra.Command{
|
||||||
|
Use: "ape",
|
||||||
|
Short: "Section for APE configuration commands",
|
||||||
|
}
|
||||||
|
|
||||||
|
addRuleChainCmd = &cobra.Command{
|
||||||
|
Use: "add-rule-chain",
|
||||||
|
Short: "Add rule chain",
|
||||||
|
PreRun: func(cmd *cobra.Command, _ []string) {
|
||||||
|
_ = viper.BindPFlag(endpointFlag, cmd.Flags().Lookup(endpointFlag))
|
||||||
|
_ = viper.BindPFlag(alphabetWalletsFlag, cmd.Flags().Lookup(alphabetWalletsFlag))
|
||||||
|
},
|
||||||
|
Run: addRuleChain,
|
||||||
|
}
|
||||||
|
|
||||||
|
removeRuleChainCmd = &cobra.Command{
|
||||||
|
Use: "rm-rule-chain",
|
||||||
|
Short: "Remove rule chain",
|
||||||
|
PreRun: func(cmd *cobra.Command, _ []string) {
|
||||||
|
_ = viper.BindPFlag(endpointFlag, cmd.Flags().Lookup(endpointFlag))
|
||||||
|
_ = viper.BindPFlag(alphabetWalletsFlag, cmd.Flags().Lookup(alphabetWalletsFlag))
|
||||||
|
},
|
||||||
|
Run: removeRuleChain,
|
||||||
|
}
|
||||||
|
|
||||||
|
listRuleChainsCmd = &cobra.Command{
|
||||||
|
Use: "list-rule-chains",
|
||||||
|
Short: "List rule chains",
|
||||||
|
PreRun: func(cmd *cobra.Command, _ []string) {
|
||||||
|
_ = viper.BindPFlag(endpointFlag, cmd.Flags().Lookup(endpointFlag))
|
||||||
|
_ = viper.BindPFlag(alphabetWalletsFlag, cmd.Flags().Lookup(alphabetWalletsFlag))
|
||||||
|
},
|
||||||
|
Run: listRuleChains,
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
func initAddRuleChainCmd() {
|
||||||
|
apeCmd.AddCommand(addRuleChainCmd)
|
||||||
|
|
||||||
|
addRuleChainCmd.Flags().StringP(endpointFlag, endpointFlagShort, "", endpointFlagDesc)
|
||||||
|
addRuleChainCmd.Flags().String(alphabetWalletsFlag, "", alphabetWalletsFlagDesc)
|
||||||
|
|
||||||
|
addRuleChainCmd.Flags().String(targetTypeFlag, "", targetTypeDesc)
|
||||||
|
_ = addRuleChainCmd.MarkFlagRequired(targetTypeFlag)
|
||||||
|
addRuleChainCmd.Flags().String(targetNameFlag, "", targetNameDesc)
|
||||||
|
_ = addRuleChainCmd.MarkFlagRequired(targetNameFlag)
|
||||||
|
|
||||||
|
addRuleChainCmd.Flags().String(chainIDFlag, "", chainIDDesc)
|
||||||
|
_ = addRuleChainCmd.MarkFlagRequired(chainIDFlag)
|
||||||
|
addRuleChainCmd.Flags().String(ruleFlag, "", ruleFlagDesc)
|
||||||
|
addRuleChainCmd.Flags().String(ruleJSONFlag, "", ruleJSONFlagDesc)
|
||||||
|
|
||||||
|
addRuleChainCmd.MarkFlagsMutuallyExclusive(ruleFlag, ruleJSONFlag)
|
||||||
|
}
|
||||||
|
|
||||||
|
func initRemoveRuleChainCmd() {
|
||||||
aarifullin marked this conversation as resolved
Outdated
|
|||||||
|
apeCmd.AddCommand(removeRuleChainCmd)
|
||||||
|
|
||||||
|
removeRuleChainCmd.Flags().StringP(endpointFlag, endpointFlagShort, "", endpointFlagDesc)
|
||||||
|
removeRuleChainCmd.Flags().String(alphabetWalletsFlag, "", alphabetWalletsFlagDesc)
|
||||||
|
|
||||||
|
removeRuleChainCmd.Flags().String(targetTypeFlag, "", targetTypeDesc)
|
||||||
|
_ = removeRuleChainCmd.MarkFlagRequired(targetTypeFlag)
|
||||||
|
removeRuleChainCmd.Flags().String(targetNameFlag, "", targetNameDesc)
|
||||||
|
_ = removeRuleChainCmd.MarkFlagRequired(targetNameFlag)
|
||||||
|
removeRuleChainCmd.Flags().String(chainIDFlag, "", chainIDDesc)
|
||||||
aarifullin
commented
After @fyrchik, could you tell, please. Do we need to After `MorphRuleChainStorage`'s methods have been [changed](https://git.frostfs.info/TrueCloudLab/policy-engine/pulls/24/files#diff-73bcddb63d5304b12f2db25f6b19d47e9a7252aa), since `AddMorphRuleChain` and `RemoveMorphRuleChain` return `txHash util.Uint256, vub uint32` .
@fyrchik, could you tell, please. Do we need to `awaitTx` here?
The current [way](https://git.frostfs.info/TrueCloudLab/frostfs-node/src/commit/b892feeaf65b89af3b5aa712dca5aefd11d5d660/cmd/frostfs-adm/internal/modules/morph/initialize.go#L377) to await a transaction is not applicable for the contract interface because it is a part of `initializeContext` where contracts are invoked in another manner
fyrchik
commented
Yes, we receive hash and VUB, we need to poll until VUB block and then check that tx with Yes, we receive hash and VUB, we need to poll until VUB block and then check that tx with `hash` exists.
acid-ant
commented
Added wait for result. Added wait for result.
|
|||||||
|
_ = removeRuleChainCmd.MarkFlagRequired(chainIDFlag)
|
||||||
|
}
|
||||||
|
|
||||||
|
func initListRuleChainsCmd() {
|
||||||
|
apeCmd.AddCommand(listRuleChainsCmd)
|
||||||
|
|
||||||
|
listRuleChainsCmd.Flags().StringP(endpointFlag, endpointFlagShort, "", endpointFlagDesc)
|
||||||
|
listRuleChainsCmd.Flags().String(alphabetWalletsFlag, "", alphabetWalletsFlagDesc)
|
||||||
|
listRuleChainsCmd.Flags().StringP(targetTypeFlag, "t", "", targetTypeDesc)
|
||||||
|
_ = listRuleChainsCmd.MarkFlagRequired(targetTypeFlag)
|
||||||
|
listRuleChainsCmd.Flags().String(targetNameFlag, "", targetNameDesc)
|
||||||
|
_ = listRuleChainsCmd.MarkFlagRequired(targetNameFlag)
|
||||||
|
listRuleChainsCmd.Flags().Bool(jsonFlag, false, jsonFlagDesc)
|
||||||
|
}
|
||||||
|
|
||||||
|
func addRuleChain(cmd *cobra.Command, _ []string) {
|
||||||
|
chain := parseChain(cmd)
|
||||||
|
target := parseTarget(cmd)
|
||||||
|
pci, ac := newPolicyContractInterface(cmd)
|
||||||
|
h, vub, err := pci.AddMorphRuleChain(apechain.Ingress, target, chain)
|
||||||
|
cmd.Println("Waiting for transaction to persist...")
|
||||||
|
_, err = ac.Wait(h, vub, err)
|
||||||
|
commonCmd.ExitOnErr(cmd, "add rule chain error: %w", err)
|
||||||
|
cmd.Println("Rule chain added successfully")
|
||||||
|
}
|
||||||
|
|
||||||
|
func removeRuleChain(cmd *cobra.Command, _ []string) {
|
||||||
|
chainID := parseChainID(cmd)
|
||||||
|
target := parseTarget(cmd)
|
||||||
|
pci, ac := newPolicyContractInterface(cmd)
|
||||||
|
h, vub, err := pci.RemoveMorphRuleChain(apechain.Ingress, target, chainID)
|
||||||
|
cmd.Println("Waiting for transaction to persist...")
|
||||||
|
_, err = ac.Wait(h, vub, err)
|
||||||
|
commonCmd.ExitOnErr(cmd, "remove rule chain error: %w", err)
|
||||||
|
cmd.Println("Rule chain removed successfully")
|
||||||
|
}
|
||||||
|
|
||||||
|
func listRuleChains(cmd *cobra.Command, _ []string) {
|
||||||
|
target := parseTarget(cmd)
|
||||||
|
pci, _ := newPolicyContractInterface(cmd)
|
||||||
|
chains, err := pci.ListMorphRuleChains(apechain.Ingress, target)
|
||||||
|
commonCmd.ExitOnErr(cmd, "list rule chains error: %w", err)
|
||||||
|
if len(chains) == 0 {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
toJSON, _ := cmd.Flags().GetBool(jsonFlag)
|
||||||
|
if toJSON {
|
||||||
|
prettyJSONFormat(cmd, chains)
|
||||||
|
} else {
|
||||||
|
for _, c := range chains {
|
||||||
|
parseutil.PrintHumanReadableAPEChain(cmd, c)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func prettyJSONFormat(cmd *cobra.Command, chains []*apechain.Chain) {
|
||||||
|
wr := bytes.NewBufferString("")
|
||||||
|
data, err := json.Marshal(chains)
|
||||||
|
if err == nil {
|
||||||
|
err = json.Indent(wr, data, "", " ")
|
||||||
|
}
|
||||||
|
commonCmd.ExitOnErr(cmd, "print rule chain error: %w", err)
|
||||||
|
cmd.Println(wr)
|
||||||
|
}
|
103
cmd/frostfs-adm/internal/modules/morph/ape_util.go
Normal file
103
cmd/frostfs-adm/internal/modules/morph/ape_util.go
Normal file
|
@ -0,0 +1,103 @@
|
||||||
|
package morph
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
"fmt"
|
||||||
|
"os"
|
||||||
|
|
||||||
|
"git.frostfs.info/TrueCloudLab/frostfs-node/cmd/frostfs-adm/internal/modules/config"
|
||||||
|
parseutil "git.frostfs.info/TrueCloudLab/frostfs-node/cmd/frostfs-cli/modules/util"
|
||||||
|
commonCmd "git.frostfs.info/TrueCloudLab/frostfs-node/cmd/internal/common"
|
||||||
|
apechain "git.frostfs.info/TrueCloudLab/policy-engine/pkg/chain"
|
||||||
|
policyengine "git.frostfs.info/TrueCloudLab/policy-engine/pkg/engine"
|
||||||
|
morph "git.frostfs.info/TrueCloudLab/policy-engine/pkg/morph/policy"
|
||||||
|
"github.com/nspcc-dev/neo-go/pkg/rpcclient/actor"
|
||||||
|
"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"
|
||||||
|
)
|
||||||
|
|
||||||
|
func parseTarget(cmd *cobra.Command) policyengine.Target {
|
||||||
|
var targetType policyengine.TargetType
|
||||||
|
typ, _ := cmd.Flags().GetString(targetTypeFlag)
|
||||||
|
switch typ {
|
||||||
|
case namespaceTarget:
|
||||||
|
targetType = policyengine.Namespace
|
||||||
|
case containerTarget:
|
||||||
|
targetType = policyengine.Container
|
||||||
|
default:
|
||||||
|
commonCmd.ExitOnErr(cmd, "read target type error: %w", fmt.Errorf("unknown target type"))
|
||||||
|
}
|
||||||
|
name, _ := cmd.Flags().GetString(targetNameFlag)
|
||||||
|
|
||||||
|
return policyengine.Target{
|
||||||
|
Name: name,
|
||||||
|
Type: targetType,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func parseChainID(cmd *cobra.Command) apechain.ID {
|
||||||
|
chainID, _ := cmd.Flags().GetString(chainIDFlag)
|
||||||
|
if chainID == "" {
|
||||||
|
commonCmd.ExitOnErr(cmd, "read chain id error: %w",
|
||||||
|
fmt.Errorf("chain id cannot be empty"))
|
||||||
|
}
|
||||||
|
return apechain.ID(chainID)
|
||||||
|
}
|
||||||
|
|
||||||
|
func parseChain(cmd *cobra.Command) *apechain.Chain {
|
||||||
|
chain := new(apechain.Chain)
|
||||||
|
|
||||||
|
if ruleStmt, _ := cmd.Flags().GetString(ruleFlag); ruleStmt != "" {
|
||||||
|
parseErr := parseutil.ParseAPEChain(chain, []string{ruleStmt})
|
||||||
|
commonCmd.ExitOnErr(cmd, "ape chain parser error: %w", parseErr)
|
||||||
|
} else if ruleJSON, _ := cmd.Flags().GetString(ruleJSONFlag); ruleJSON != "" {
|
||||||
|
var rule []byte
|
||||||
|
if _, err := os.Stat(ruleJSON); err == nil {
|
||||||
|
rule, err = os.ReadFile(ruleJSON)
|
||||||
|
commonCmd.ExitOnErr(cmd, "read file error: %w", err)
|
||||||
|
} else {
|
||||||
|
rule = []byte(ruleJSON)
|
||||||
|
if !json.Valid(rule) {
|
||||||
|
commonCmd.ExitOnErr(cmd, "read raw rule error: %w",
|
||||||
|
fmt.Errorf("invalid JSON"))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
err := chain.DecodeBytes(rule)
|
||||||
|
commonCmd.ExitOnErr(cmd, "chain decode error: %w", err)
|
||||||
|
} else {
|
||||||
|
commonCmd.ExitOnErr(cmd, "", fmt.Errorf("rule is not passed"))
|
||||||
|
}
|
||||||
|
|
||||||
|
chain.ID = parseChainID(cmd)
|
||||||
|
|
||||||
|
return chain
|
||||||
|
}
|
||||||
|
|
||||||
|
func newPolicyContractInterface(cmd *cobra.Command) (*morph.ContractStorage, *actor.Actor) {
|
||||||
|
v := viper.GetViper()
|
||||||
|
c, err := getN3Client(v)
|
||||||
|
commonCmd.ExitOnErr(cmd, "unable to create NEO rpc client: %w", err)
|
||||||
|
|
||||||
|
walletDir := config.ResolveHomePath(viper.GetString(alphabetWalletsFlag))
|
||||||
|
wallets, err := getAlphabetWallets(v, walletDir)
|
||||||
|
commonCmd.ExitOnErr(cmd, "unable to get alphabet wallets: %w", err)
|
||||||
|
|
||||||
|
committeeAcc, err := getWalletAccount(wallets[0], committeeAccountName)
|
||||||
|
commonCmd.ExitOnErr(cmd, "can't find committee account: %w", err)
|
||||||
|
|
||||||
|
ac, err := newActor(c, committeeAcc)
|
||||||
|
commonCmd.ExitOnErr(cmd, "can't create actor: %w", err)
|
||||||
|
|
||||||
|
inv := &ac.Invoker
|
||||||
|
var ch util.Uint160
|
||||||
|
r := management.NewReader(inv)
|
||||||
|
nnsCs, err := r.GetContractByID(1)
|
||||||
|
commonCmd.ExitOnErr(cmd, "can't get NNS contract state: %w", err)
|
||||||
|
|
||||||
|
ch, err = nnsResolveHash(inv, nnsCs.Hash, policyContract+".frostfs")
|
||||||
|
commonCmd.ExitOnErr(cmd, "unable to resolve policy contract hash: %w", err)
|
||||||
|
|
||||||
|
return morph.NewContractStorage(ac, ch), ac
|
||||||
|
}
|
|
@ -263,6 +263,10 @@ func init() {
|
||||||
initRefillGasCmd()
|
initRefillGasCmd()
|
||||||
initDepositoryNotaryCmd()
|
initDepositoryNotaryCmd()
|
||||||
initNetmapCandidatesCmd()
|
initNetmapCandidatesCmd()
|
||||||
|
RootCmd.AddCommand(apeCmd)
|
||||||
|
initAddRuleChainCmd()
|
||||||
|
initRemoveRuleChainCmd()
|
||||||
|
initListRuleChainsCmd()
|
||||||
}
|
}
|
||||||
|
|
||||||
func initNetmapCandidatesCmd() {
|
func initNetmapCandidatesCmd() {
|
||||||
|
|
|
@ -3,11 +3,13 @@ package util
|
||||||
import (
|
import (
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
apechain "git.frostfs.info/TrueCloudLab/policy-engine/pkg/chain"
|
apechain "git.frostfs.info/TrueCloudLab/policy-engine/pkg/chain"
|
||||||
nativeschema "git.frostfs.info/TrueCloudLab/policy-engine/schema/native"
|
nativeschema "git.frostfs.info/TrueCloudLab/policy-engine/schema/native"
|
||||||
"github.com/flynn-archive/go-shlex"
|
"github.com/flynn-archive/go-shlex"
|
||||||
|
"github.com/spf13/cobra"
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
|
@ -20,6 +22,37 @@ var (
|
||||||
errUnknownCondObjectType = errors.New("condition object type is not recognized")
|
errUnknownCondObjectType = errors.New("condition object type is not recognized")
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// PrintHumanReadableAPEChain print APE chain rules.
|
||||||
|
func PrintHumanReadableAPEChain(cmd *cobra.Command, chain *apechain.Chain) {
|
||||||
|
cmd.Println("ChainID: " + string(chain.ID))
|
||||||
|
cmd.Println("Rules:")
|
||||||
|
for _, rule := range chain.Rules {
|
||||||
|
cmd.Println("\tStatus: " + rule.Status.String())
|
||||||
|
cmd.Println("\tAny: " + strconv.FormatBool(rule.Any))
|
||||||
|
cmd.Println("\tConditions:")
|
||||||
|
for _, c := range rule.Condition {
|
||||||
|
var ot string
|
||||||
|
switch c.Object {
|
||||||
|
case apechain.ObjectResource:
|
||||||
|
ot = "Resource"
|
||||||
|
case apechain.ObjectRequest:
|
||||||
|
ot = "Request"
|
||||||
|
default:
|
||||||
|
panic("unknown object type")
|
||||||
|
}
|
||||||
|
cmd.Println(fmt.Sprintf("\t\t%s %s %s %s", ot, c.Key, c.Op, c.Value))
|
||||||
|
}
|
||||||
|
cmd.Println("\tActions:\tInverted:" + strconv.FormatBool(rule.Actions.Inverted))
|
||||||
|
for _, name := range rule.Actions.Names {
|
||||||
|
cmd.Println("\t\t" + name)
|
||||||
|
}
|
||||||
|
cmd.Println("\tResources:\tInverted:" + strconv.FormatBool(rule.Resources.Inverted))
|
||||||
|
for _, name := range rule.Resources.Names {
|
||||||
|
cmd.Println("\t\t" + name)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// ParseAPEChain parses APE chain rules.
|
// ParseAPEChain parses APE chain rules.
|
||||||
func ParseAPEChain(chain *apechain.Chain, rules []string) error {
|
func ParseAPEChain(chain *apechain.Chain, rules []string) error {
|
||||||
if len(rules) == 0 {
|
if len(rules) == 0 {
|
||||||
|
|
Loading…
Reference in a new issue
I suppose we need
meaning a rule has been given neither by json nor by statement
Agree, fixed.