From 3b463c0347b2b42bbcdf1449f287ecf21c264114 Mon Sep 17 00:00:00 2001 From: Ekaterina Pavlova Date: Mon, 21 Aug 2023 00:30:50 +0400 Subject: [PATCH] cli: unify usage of expired-at flag and lifetime flag Refactor CLI usage to standardize the expired-at flag and lifetime flag. This change is aimed at preventing confusion during flag interpretation and empowering users. Closes #1574. Signed-off-by: Ekaterina Pavlova --- CHANGELOG.md | 2 ++ cmd/neofs-cli/modules/bearer/create.go | 13 +++++-- cmd/neofs-cli/modules/object/lock.go | 2 +- cmd/neofs-cli/modules/object/put.go | 11 ++++++ cmd/neofs-cli/modules/object/util.go | 7 ++-- cmd/neofs-cli/modules/session/create.go | 41 ++++++++++++----------- cmd/neofs-cli/modules/storagegroup/put.go | 25 ++++++++++---- 7 files changed, 69 insertions(+), 32 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 3d8dcf839b..1d0283f765 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -19,6 +19,8 @@ minor release, the component will be purged, so be prepared (see `Updating` sect - `peapod` command for `neofs-lens` (#2507) - New CLI exit code for awaiting timeout (#2380) - Validation of excessive positional arguments to `neofs-cli` commands (#1941) +- `--lifetime` flag to `bearer create` and `object put` CLI commands (#1574) +- `--expired-at` flag to `session create` and `storagegroup put` CLI commands (#1574) ### Fixed - `neo-go` RPC connection loss handling (#1337) diff --git a/cmd/neofs-cli/modules/bearer/create.go b/cmd/neofs-cli/modules/bearer/create.go index 87d7bcfc05..48a3f0848c 100644 --- a/cmd/neofs-cli/modules/bearer/create.go +++ b/cmd/neofs-cli/modules/bearer/create.go @@ -3,6 +3,7 @@ package bearer import ( "context" "encoding/json" + "errors" "fmt" "os" "time" @@ -47,27 +48,32 @@ func init() { createCmd.Flags().String(outFlag, "", "File to write token to") createCmd.Flags().Bool(jsonFlag, false, "Output token in JSON") createCmd.Flags().StringP(commonflags.RPC, commonflags.RPCShorthand, commonflags.RPCDefault, commonflags.RPCUsage) + createCmd.Flags().Uint64P(commonflags.Lifetime, "l", 0, "Number of epochs for token to stay valid") _ = cobra.MarkFlagFilename(createCmd.Flags(), eaclFlag) _ = cobra.MarkFlagRequired(createCmd.Flags(), issuedAtFlag) _ = cobra.MarkFlagRequired(createCmd.Flags(), notValidBeforeFlag) - _ = cobra.MarkFlagRequired(createCmd.Flags(), commonflags.ExpireAt) _ = cobra.MarkFlagRequired(createCmd.Flags(), ownerFlag) _ = cobra.MarkFlagRequired(createCmd.Flags(), outFlag) + createCmd.MarkFlagsMutuallyExclusive(commonflags.ExpireAt, commonflags.Lifetime) } func createToken(cmd *cobra.Command, _ []string) { iat, iatRelative, err := common.ParseEpoch(cmd, issuedAtFlag) common.ExitOnErr(cmd, "can't parse --"+issuedAtFlag+" flag: %w", err) + lifetime, _ := cmd.Flags().GetUint64(commonflags.Lifetime) exp, expRelative, err := common.ParseEpoch(cmd, commonflags.ExpireAt) common.ExitOnErr(cmd, "can't parse --"+commonflags.ExpireAt+" flag: %w", err) + if exp == 0 && lifetime == 0 { + common.ExitOnErr(cmd, "", errors.New("expiration epoch or lifetime period is required")) + } nvb, nvbRelative, err := common.ParseEpoch(cmd, notValidBeforeFlag) common.ExitOnErr(cmd, "can't parse --"+notValidBeforeFlag+" flag: %w", err) - if iatRelative || expRelative || nvbRelative { + if iatRelative || expRelative || nvbRelative || lifetime != 0 { ctx, cancel := context.WithTimeout(context.Background(), time.Second*30) defer cancel() @@ -84,6 +90,9 @@ func createToken(cmd *cobra.Command, _ []string) { if nvbRelative { nvb += currEpoch } + if lifetime != 0 { + exp = currEpoch + lifetime + } } if exp < nvb { common.ExitOnErr(cmd, "", diff --git a/cmd/neofs-cli/modules/object/lock.go b/cmd/neofs-cli/modules/object/lock.go index f30181a0d3..b30d46d14c 100644 --- a/cmd/neofs-cli/modules/object/lock.go +++ b/cmd/neofs-cli/modules/object/lock.go @@ -54,7 +54,7 @@ var objectLockCmd = &cobra.Command{ exp, _ := cmd.Flags().GetUint64(commonflags.ExpireAt) lifetime, _ := cmd.Flags().GetUint64(commonflags.Lifetime) if exp == 0 && lifetime == 0 { // mutual exclusion is ensured by cobra - common.ExitOnErr(cmd, "", errors.New("either expiration epoch of a lifetime is required")) + common.ExitOnErr(cmd, "", errors.New("expiration epoch or lifetime period is required")) } if lifetime != 0 { diff --git a/cmd/neofs-cli/modules/object/put.go b/cmd/neofs-cli/modules/object/put.go index 56025aa753..d9b0bc287a 100644 --- a/cmd/neofs-cli/modules/object/put.go +++ b/cmd/neofs-cli/modules/object/put.go @@ -53,10 +53,12 @@ func initObjectPutCmd() { flags.Bool("disable-filename", false, "Do not set well-known filename attribute") flags.Bool("disable-timestamp", false, "Do not set well-known timestamp attribute") flags.Uint64VarP(&putExpiredOn, commonflags.ExpireAt, "e", 0, "The last active epoch in the life of the object") + flags.Uint64P(commonflags.Lifetime, "l", 0, "Number of epochs for object to stay valid") flags.Bool(noProgressFlag, false, "Do not show progress bar") flags.String(notificationFlag, "", "Object notification in the form of *epoch*:*topic*; '-' topic means using default") flags.Bool(binaryFlag, false, "Deserialize object structure from given file.") + objectPutCmd.MarkFlagsMutuallyExclusive(commonflags.ExpireAt, commonflags.Lifetime) } func putObject(cmd *cobra.Command, _ []string) { @@ -100,6 +102,15 @@ func putObject(cmd *cobra.Command, _ []string) { common.ExitOnErr(cmd, "can't parse object attributes: %w", err) expiresOn, _ := cmd.Flags().GetUint64(commonflags.ExpireAt) + lifetime, _ := cmd.Flags().GetUint64(commonflags.Lifetime) + if lifetime > 0 { + endpoint, _ := cmd.Flags().GetString(commonflags.RPC) + currEpoch, err := internalclient.GetCurrentEpoch(ctx, endpoint) + common.ExitOnErr(cmd, "Request current epoch: %w", err) + + expiresOn = currEpoch + lifetime + } + if expiresOn > 0 { var expAttrFound bool expAttrValue := strconv.FormatUint(expiresOn, 10) diff --git a/cmd/neofs-cli/modules/object/util.go b/cmd/neofs-cli/modules/object/util.go index 5aa9d418b4..2479f21678 100644 --- a/cmd/neofs-cli/modules/object/util.go +++ b/cmd/neofs-cli/modules/object/util.go @@ -278,8 +278,11 @@ func OpenSessionViaClient(ctx context.Context, cmd *cobra.Command, dst SessionPr const sessionLifetime = 10 // in NeoFS epochs common.PrintVerbose(cmd, "Opening remote session with the node...") - - err := sessionCli.CreateSession(ctx, &tok, cli, *key, sessionLifetime) + endpoint, _ := cmd.Flags().GetString(commonflags.RPC) + currEpoch, err := internal.GetCurrentEpoch(ctx, endpoint) + common.ExitOnErr(cmd, "can't fetch current epoch: %w", err) + exp := currEpoch + sessionLifetime + err = sessionCli.CreateSession(ctx, &tok, cli, *key, exp, currEpoch) common.ExitOnErr(cmd, "open remote session: %w", err) common.PrintVerbose(cmd, "Session successfully opened.") diff --git a/cmd/neofs-cli/modules/session/create.go b/cmd/neofs-cli/modules/session/create.go index caa77977f4..a3f5fe04e7 100644 --- a/cmd/neofs-cli/modules/session/create.go +++ b/cmd/neofs-cli/modules/session/create.go @@ -3,6 +3,7 @@ package session import ( "context" "crypto/ecdsa" + "errors" "fmt" "os" @@ -44,10 +45,12 @@ func init() { createCmd.Flags().String(outFlag, "", "File to write session token to") createCmd.Flags().Bool(jsonFlag, false, "Output token in JSON") createCmd.Flags().StringP(commonflags.RPC, commonflags.RPCShorthand, commonflags.RPCDefault, commonflags.RPCUsage) + createCmd.Flags().Uint64P(commonflags.ExpireAt, "e", 0, "The last active epoch for token to stay valid") _ = cobra.MarkFlagRequired(createCmd.Flags(), commonflags.WalletPath) _ = cobra.MarkFlagRequired(createCmd.Flags(), outFlag) _ = cobra.MarkFlagRequired(createCmd.Flags(), commonflags.RPC) + createCmd.MarkFlagsMutuallyExclusive(commonflags.ExpireAt, commonflags.Lifetime) } func createSession(cmd *cobra.Command, _ []string) { @@ -62,14 +65,23 @@ func createSession(cmd *cobra.Command, _ []string) { c, err := internalclient.GetSDKClient(ctx, netAddr) common.ExitOnErr(cmd, "can't create client: %w", err) + endpoint, _ := cmd.Flags().GetString(commonflags.RPC) + currEpoch, err := internalclient.GetCurrentEpoch(ctx, endpoint) + common.ExitOnErr(cmd, "can't get current epoch: %w", err) + + exp, _ := cmd.Flags().GetUint64(commonflags.ExpireAt) lifetime := uint64(defaultLifetime) if lfArg, _ := cmd.Flags().GetUint64(commonflags.Lifetime); lfArg != 0 { - lifetime = lfArg + exp = currEpoch + lfArg + } + if exp == 0 { + exp = currEpoch + lifetime + } + if exp <= currEpoch { + common.ExitOnErr(cmd, "", errors.New("expiration epoch must be greater than current epoch")) } - var tok session.Object - - err = CreateSession(ctx, &tok, c, *privKey, lifetime) + err = CreateSession(ctx, &tok, c, *privKey, exp, currEpoch) common.ExitOnErr(cmd, "can't create session: %w", err) var data []byte @@ -91,21 +103,10 @@ func createSession(cmd *cobra.Command, _ []string) { // number of epochs. // // Fills ID, lifetime and session key. -func CreateSession(ctx context.Context, dst *session.Object, c *client.Client, key ecdsa.PrivateKey, lifetime uint64) error { - var netInfoPrm internalclient.NetworkInfoPrm - netInfoPrm.SetClient(c) - - ni, err := internalclient.NetworkInfo(ctx, netInfoPrm) - if err != nil { - return fmt.Errorf("can't fetch network info: %w", err) - } - - cur := ni.NetworkInfo().CurrentEpoch() - exp := cur + lifetime - +func CreateSession(ctx context.Context, dst *session.Object, c *client.Client, key ecdsa.PrivateKey, expireAt uint64, currEpoch uint64) error { var sessionPrm internalclient.CreateSessionPrm sessionPrm.SetClient(c) - sessionPrm.SetExp(exp) + sessionPrm.SetExp(expireAt) sessionPrm.SetPrivateKey(key) sessionRes, err := internalclient.CreateSession(ctx, sessionPrm) @@ -130,9 +131,9 @@ func CreateSession(ctx context.Context, dst *session.Object, c *client.Client, k } dst.SetID(idSession) - dst.SetNbf(cur) - dst.SetIat(cur) - dst.SetExp(exp) + dst.SetNbf(currEpoch) + dst.SetIat(currEpoch) + dst.SetExp(expireAt) dst.SetAuthKey(&keySession) return nil diff --git a/cmd/neofs-cli/modules/storagegroup/put.go b/cmd/neofs-cli/modules/storagegroup/put.go index 14d93b9c5a..161052a1ba 100644 --- a/cmd/neofs-cli/modules/storagegroup/put.go +++ b/cmd/neofs-cli/modules/storagegroup/put.go @@ -44,10 +44,18 @@ func initSGPutCmd() { _ = sgPutCmd.MarkFlagRequired(sgMembersFlag) flags.Uint64(commonflags.Lifetime, 0, "Storage group lifetime in epochs") - _ = sgPutCmd.MarkFlagRequired(commonflags.Lifetime) + flags.Uint64P(commonflags.ExpireAt, "e", 0, "The last active epoch of the storage group") + sgPutCmd.MarkFlagsMutuallyExclusive(commonflags.ExpireAt, commonflags.Lifetime) } func putSG(cmd *cobra.Command, _ []string) { + // with 1.8.0 cobra release we can use this instead of below + // sgPutCmd.MarkFlagsOneRequired("expire-at", "lifetime") + exp, _ := cmd.Flags().GetUint64(commonflags.ExpireAt) + lifetime, _ := cmd.Flags().GetUint64(commonflags.Lifetime) + if exp == 0 && lifetime == 0 { // mutual exclusion is ensured by cobra + common.ExitOnErr(cmd, "", errors.New("expiration epoch or lifetime period is required")) + } ctx, cancel := commonflags.GetCommandContext(cmd) defer cancel() @@ -101,14 +109,17 @@ func putSG(cmd *cobra.Command, _ []string) { }, cnr, members, !resGetCnr.Container().IsHomomorphicHashingDisabled()) common.ExitOnErr(cmd, "could not collect storage group members: %w", err) - var netInfoPrm internalclient.NetworkInfoPrm - netInfoPrm.SetClient(cli) + if lifetime != 0 { + var netInfoPrm internalclient.NetworkInfoPrm + netInfoPrm.SetClient(cli) - ni, err := internalclient.NetworkInfo(ctx, netInfoPrm) - common.ExitOnErr(cmd, "can't fetch network info: %w", err) + ni, err := internalclient.NetworkInfo(ctx, netInfoPrm) + common.ExitOnErr(cmd, "can't fetch network info: %w", err) + currEpoch := ni.NetworkInfo().CurrentEpoch() + exp = currEpoch + lifetime + } - lifetime, _ := cmd.Flags().GetUint64(commonflags.Lifetime) - sg.SetExpirationEpoch(ni.NetworkInfo().CurrentEpoch() + lifetime) + sg.SetExpirationEpoch(exp) obj := object.New() obj.SetContainerID(cnr)