forked from TrueCloudLab/frostfs-node
Airat Arifullin
7a7ee71a4d
* Introduce common parsing commands to use them in `frostfs-cli` and `frostfs-adm` APE-related subcommands * Introduce common flags for these parsing commands Signed-off-by: Airat Arifullin <a.arifullin@yadro.com>
166 lines
5.1 KiB
Go
166 lines
5.1 KiB
Go
package ape
|
|
|
|
import (
|
|
"encoding/hex"
|
|
"errors"
|
|
"fmt"
|
|
"strconv"
|
|
"strings"
|
|
|
|
commonCmd "git.frostfs.info/TrueCloudLab/frostfs-node/cmd/internal/common"
|
|
apeutil "git.frostfs.info/TrueCloudLab/frostfs-node/pkg/util/ape"
|
|
cid "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/container/id"
|
|
apechain "git.frostfs.info/TrueCloudLab/policy-engine/pkg/chain"
|
|
"git.frostfs.info/TrueCloudLab/policy-engine/pkg/engine"
|
|
"github.com/nspcc-dev/neo-go/cli/input"
|
|
"github.com/spf13/cobra"
|
|
)
|
|
|
|
const (
|
|
defaultNamespace = "root"
|
|
namespaceTarget = "namespace"
|
|
containerTarget = "container"
|
|
userTarget = "user"
|
|
groupTarget = "group"
|
|
|
|
Ingress = "ingress"
|
|
S3 = "s3"
|
|
)
|
|
|
|
var mChainName = map[string]apechain.Name{
|
|
Ingress: apechain.Ingress,
|
|
S3: apechain.S3,
|
|
}
|
|
|
|
var (
|
|
errSettingDefaultValueWasDeclined = errors.New("setting default value was declined")
|
|
errUnknownTargetType = errors.New("unknown target type")
|
|
errUnsupportedChainName = errors.New("unsupported chain name")
|
|
)
|
|
|
|
// PrintHumanReadableAPEChain print APE chain rules.
|
|
func PrintHumanReadableAPEChain(cmd *cobra.Command, chain *apechain.Chain) {
|
|
cmd.Println("Chain ID: " + string(chain.ID))
|
|
cmd.Printf(" HEX: %x\n", chain.ID)
|
|
cmd.Println("Rules:")
|
|
for _, rule := range chain.Rules {
|
|
cmd.Println("\n\tStatus: " + rule.Status.String())
|
|
cmd.Println("\tAny: " + strconv.FormatBool(rule.Any))
|
|
cmd.Println("\tConditions:")
|
|
for _, c := range rule.Condition {
|
|
var ot string
|
|
switch c.Kind {
|
|
case apechain.KindResource:
|
|
ot = "Resource"
|
|
case apechain.KindRequest:
|
|
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)
|
|
}
|
|
}
|
|
}
|
|
|
|
// ParseTarget handles target parsing of an APE chain.
|
|
func ParseTarget(cmd *cobra.Command) engine.Target {
|
|
typ := ParseTargetType(cmd)
|
|
name, _ := cmd.Flags().GetString(TargetNameFlag)
|
|
switch typ {
|
|
case engine.Namespace:
|
|
if name == "" {
|
|
ln, err := input.ReadLine(fmt.Sprintf("Target name is not set. Confirm to use %s namespace (n|Y)> ", defaultNamespace))
|
|
commonCmd.ExitOnErr(cmd, "read line error: %w", err)
|
|
ln = strings.ToLower(ln)
|
|
if len(ln) > 0 && (ln[0] == 'n') {
|
|
commonCmd.ExitOnErr(cmd, "read namespace error: %w", errSettingDefaultValueWasDeclined)
|
|
}
|
|
name = defaultNamespace
|
|
}
|
|
return engine.NamespaceTarget(name)
|
|
case engine.Container:
|
|
var cnr cid.ID
|
|
commonCmd.ExitOnErr(cmd, "can't decode container ID: %w", cnr.DecodeString(name))
|
|
return engine.ContainerTarget(name)
|
|
case engine.User:
|
|
return engine.UserTarget(name)
|
|
case engine.Group:
|
|
return engine.GroupTarget(name)
|
|
default:
|
|
commonCmd.ExitOnErr(cmd, "read target type error: %w", errUnknownTargetType)
|
|
}
|
|
return engine.Target{}
|
|
}
|
|
|
|
// ParseTargetType handles target type parsing of an APE chain.
|
|
func ParseTargetType(cmd *cobra.Command) engine.TargetType {
|
|
typ, _ := cmd.Flags().GetString(TargetTypeFlag)
|
|
switch typ {
|
|
case namespaceTarget:
|
|
return engine.Namespace
|
|
case containerTarget:
|
|
return engine.Container
|
|
case userTarget:
|
|
return engine.User
|
|
case groupTarget:
|
|
return engine.Group
|
|
}
|
|
commonCmd.ExitOnErr(cmd, "parse target type error: %w", errUnknownTargetType)
|
|
return engine.TargetType(0)
|
|
}
|
|
|
|
// ParseChainID handles the parsing of APE-chain identifier.
|
|
// For some subcommands, chain ID is optional as an input parameter and should be generated by
|
|
// the service instead.
|
|
func ParseChainID(cmd *cobra.Command) (id apechain.ID) {
|
|
chainID, _ := cmd.Flags().GetString(ChainIDFlag)
|
|
id = apechain.ID(chainID)
|
|
|
|
hexEncoded, _ := cmd.Flags().GetBool(ChainIDHexFlag)
|
|
if !hexEncoded {
|
|
return
|
|
}
|
|
|
|
chainIDRaw, err := hex.DecodeString(chainID)
|
|
commonCmd.ExitOnErr(cmd, "can't decode chain ID as hex: %w", err)
|
|
id = apechain.ID(chainIDRaw)
|
|
return
|
|
}
|
|
|
|
// ParseChain parses an APE chain which can be provided either as a rule statement
|
|
// or loaded from a binary/JSON file path.
|
|
func ParseChain(cmd *cobra.Command) *apechain.Chain {
|
|
chain := new(apechain.Chain)
|
|
chain.ID = ParseChainID(cmd)
|
|
|
|
if rules, _ := cmd.Flags().GetStringArray(RuleFlag); len(rules) > 0 {
|
|
commonCmd.ExitOnErr(cmd, "parser error: %w", apeutil.ParseAPEChain(chain, rules))
|
|
} else if encPath, _ := cmd.Flags().GetString(PathFlag); encPath != "" {
|
|
commonCmd.ExitOnErr(cmd, "decode binary or json error: %w", apeutil.ParseAPEChainBinaryOrJSON(chain, encPath))
|
|
} else {
|
|
commonCmd.ExitOnErr(cmd, "parser error", errors.New("rule is not passed"))
|
|
}
|
|
|
|
cmd.Println("Parsed chain:")
|
|
PrintHumanReadableAPEChain(cmd, chain)
|
|
|
|
return chain
|
|
}
|
|
|
|
// ParseChainName parses chain name: the place in the request lifecycle where policy is applied.
|
|
func ParseChainName(cmd *cobra.Command) apechain.Name {
|
|
chainName, _ := cmd.Flags().GetString(ChainNameFlag)
|
|
apeChainName, ok := mChainName[strings.ToLower(chainName)]
|
|
if !ok {
|
|
commonCmd.ExitOnErr(cmd, "", errUnsupportedChainName)
|
|
}
|
|
return apeChainName
|
|
}
|