From e40dc0412d6e2a2eb78f2e85d6f6627b53915683 Mon Sep 17 00:00:00 2001 From: Alex Vanin Date: Tue, 20 Oct 2020 15:21:17 +0300 Subject: [PATCH] [#26] Add get and set EACL commands in CLI Signed-off-by: Alex Vanin --- cmd/neofs-cli/modules/container.go | 174 +++++++++++++++++++++++++++++ 1 file changed, 174 insertions(+) diff --git a/cmd/neofs-cli/modules/container.go b/cmd/neofs-cli/modules/container.go index e0401f02b..0444b8742 100644 --- a/cmd/neofs-cli/modules/container.go +++ b/cmd/neofs-cli/modules/container.go @@ -1,7 +1,9 @@ package cmd import ( + "bytes" "context" + "encoding/json" "errors" "fmt" "io/ioutil" @@ -13,11 +15,14 @@ import ( "github.com/google/uuid" "github.com/nspcc-dev/neofs-api-go/pkg/acl" + "github.com/nspcc-dev/neofs-api-go/pkg/acl/eacl" "github.com/nspcc-dev/neofs-api-go/pkg/client" "github.com/nspcc-dev/neofs-api-go/pkg/container" "github.com/nspcc-dev/neofs-api-go/pkg/netmap" "github.com/nspcc-dev/neofs-api-go/pkg/object" "github.com/nspcc-dev/neofs-api-go/pkg/owner" + v2ACL "github.com/nspcc-dev/neofs-api-go/v2/acl" + grpcACL "github.com/nspcc-dev/neofs-api-go/v2/acl/grpc" v2container "github.com/nspcc-dev/neofs-api-go/v2/container" grpccontainer "github.com/nspcc-dev/neofs-api-go/v2/container/grpc" "github.com/nspcc-dev/neofs-node/pkg/policy" @@ -44,6 +49,10 @@ var ( containerPathFrom string containerPathTo string + + containerJSON bool + + eaclPathFrom string ) // containerCmd represents the container command @@ -308,6 +317,122 @@ var getContainerInfoCmd = &cobra.Command{ }, } +var getExtendedACLCmd = &cobra.Command{ + Use: "get-eacl", + Short: "Get extended ACL table of container", + Long: `Get extended ACL talbe of container`, + RunE: func(cmd *cobra.Command, args []string) error { + ctx := context.Background() + + cli, err := getSDKClient() + if err != nil { + return err + } + + id, err := parseContainerID(containerID) + if err != nil { + return err + } + + eaclTable, err := cli.GetEACL(ctx, id) + if err != nil { + return fmt.Errorf("rpc error: %w", err) + } + + v := eaclTable.Version() + if v.GetMajor() == 0 && v.GetMajor() == 0 { + fmt.Println("extended ACL table is not set for this container") + return nil + } + + if containerPathTo == "" { + prettyPrintEACL(eaclTable) + return nil + } + + var data []byte + + if containerJSON { + data = v2ACL.TableToJSON(eaclTable.ToV2()) + if len(data) == 0 { + return errors.New("can't encode to JSON") + } + } else { + data, err = eaclTable.ToV2().StableMarshal(nil) + if err != nil { + return errors.New("can't encode to binary") + } + } + + fmt.Println("dumping data to file:", containerPathTo) + + return ioutil.WriteFile(containerPathTo, data, 0644) + }, +} + +var setExtendedACLCmd = &cobra.Command{ + Use: "set-eacl", + Short: "Set new extended ACL table for container", + Long: `Set new extended ACL table for container. +Container ID in EACL table will be substituted with ID from the CLI.`, + RunE: func(cmd *cobra.Command, args []string) error { + ctx := context.Background() + + cli, err := getSDKClient() + if err != nil { + return err + } + + id, err := parseContainerID(containerID) + if err != nil { + return err + } + + eaclTable, err := parseEACL(eaclPathFrom) + if err != nil { + return err + } + + eaclTable.SetCID(id) + + err = cli.SetEACL(ctx, eaclTable) + if err != nil { + return fmt.Errorf("rpc error: %w", err) + } + + if containerAwait { + exp, err := eaclTable.ToV2().StableMarshal(nil) + if err != nil { + return errors.New("broken EACL table") + } + + fmt.Println("awaiting...") + + for i := 0; i < awaitTimeout; i++ { + time.Sleep(1 * time.Second) + + table, err := cli.GetEACL(ctx, id) + if err == nil { + // compare binary values because EACL could have been set already + got, err := table.ToV2().StableMarshal(nil) + if err != nil { + continue + } + + if bytes.Equal(exp, got) { + fmt.Println("EACL has been persisted on sidechain") + return nil + } + } + } + + return errors.New("timeout: EACL has not been persisted on sidechain") + } + + return nil + }, +} + func init() { rootCmd.AddCommand(containerCmd) containerCmd.AddCommand(listContainersCmd) @@ -315,6 +440,8 @@ func init() { containerCmd.AddCommand(deleteContainerCmd) containerCmd.AddCommand(listContainerObjectsCmd) containerCmd.AddCommand(getContainerInfoCmd) + containerCmd.AddCommand(getExtendedACLCmd) + containerCmd.AddCommand(setExtendedACLCmd) // Here you will define your flags and configuration settings. @@ -350,6 +477,16 @@ func init() { getContainerInfoCmd.Flags().StringVar(&containerID, "cid", "", "container ID") getContainerInfoCmd.Flags().StringVar(&containerPathTo, "to", "", "path to dump binary encoded container") getContainerInfoCmd.Flags().StringVar(&containerPathFrom, "from", "", "path to file with binary encoded container") + + // container get-eacl + getExtendedACLCmd.Flags().StringVar(&containerID, "cid", "", "container ID") + getExtendedACLCmd.Flags().StringVar(&containerPathTo, "to", "", "path to dump encoded container (default: binary encoded)") + getExtendedACLCmd.Flags().BoolVar(&containerJSON, "json", false, "encode EACL table in json format") + + // container set-eacl + setExtendedACLCmd.Flags().StringVar(&containerID, "cid", "", "container ID") + setExtendedACLCmd.Flags().StringVar(&eaclPathFrom, "table", "", "path to file with JSON or binary encoded EACL table") + setExtendedACLCmd.Flags().BoolVar(&containerAwait, "await", false, "block execution until EACL is persisted") } func prettyPrintContainerList(list []*container.ID) { @@ -491,3 +628,40 @@ func prettyPrintContainer(cnr *container.Container) { fmt.Println("placement policy:") fmt.Println(strings.Join(policy.Encode(cnr.GetPlacementPolicy()), "\n")) } + +func parseEACL(eaclPath string) (*eacl.Table, error) { + _, err := os.Stat(eaclPath) // check if `eaclPath` is an existing file + if err != nil { + return nil, errors.New("incorrect path to file with EACL") + } + + printVerbose("Reading EACL from file: %s", eaclPath) + + data, err := ioutil.ReadFile(eaclPath) + if err != nil { + return nil, fmt.Errorf("can't read file with EACL: %w", err) + } + + msg := new(grpcACL.EACLTable) + if proto.Unmarshal(data, msg) == nil { + printVerbose("Parsed binary encoded EACL table") + v2 := v2ACL.TableFromGRPCMessage(msg) + return eacl.NewTableFromV2(v2), nil + } + + if v2 := v2ACL.TableFromJSON(data); v2 != nil { + printVerbose("Parsed JSON encoded EACL table") + return eacl.NewTableFromV2(v2), nil + } + + return nil, errors.New("can't parse EACL table") +} + +func prettyPrintEACL(table *eacl.Table) { + data := v2ACL.TableToJSON(table.ToV2()) + buf := new(bytes.Buffer) + if err := json.Indent(buf, data, "", " "); err != nil { + printVerbose("Can't pretty print json: %w", err) + } + fmt.Println(buf) +}