From aa6d58f191287b3e19cfa9f66c0764d6248c755b 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 | 1 + 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 | 37 ++++++++++++----------- cmd/neofs-cli/modules/storagegroup/put.go | 25 ++++++++++----- 7 files changed, 66 insertions(+), 30 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index b7b593635d1..25b0173cb81 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -18,6 +18,7 @@ minor release, the component will be purged, so be prepared (see `Updating` sect - SN's version and capacity is announced via the attributes automatically but can be overwritten explicitly (#2455, #602) - `peapod` command for `neofs-lens` (#2507) - New CLI exit code for awaiting timeout (#2380) +- To `neofs-cli` commands: `bearer create`, `object put` added `--lifetime` flag; to `neofs-cli` commands `session create`, `storagegroup put` added `--expired-at` flag (#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 c826e3f5c7e..3c34ae2ddbf 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" @@ -46,27 +47,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() @@ -83,6 +89,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 fd0f602fd6b..c825b4928ee 100644 --- a/cmd/neofs-cli/modules/object/lock.go +++ b/cmd/neofs-cli/modules/object/lock.go @@ -53,7 +53,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 38a875a1627..2067f44247a 100644 --- a/cmd/neofs-cli/modules/object/put.go +++ b/cmd/neofs-cli/modules/object/put.go @@ -52,10 +52,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) { @@ -99,6 +101,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 5aa9d418b45..b36f62b71ee 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) 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 ca280b6117f..438118431ef 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" @@ -43,10 +44,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) { @@ -61,14 +64,24 @@ 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) common.ExitOnErr(cmd, "can't create session: %w", err) var data []byte @@ -90,21 +103,11 @@ 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) error { var sessionPrm internalclient.CreateSessionPrm sessionPrm.SetClient(c) - sessionPrm.SetExp(exp) + sessionPrm.SetExp(expireAt) sessionPrm.SetPrivateKey(key) sessionRes, err := internalclient.CreateSession(ctx, sessionPrm) @@ -129,9 +132,7 @@ 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.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 50fdffd5b90..fac669a425e 100644 --- a/cmd/neofs-cli/modules/storagegroup/put.go +++ b/cmd/neofs-cli/modules/storagegroup/put.go @@ -43,10 +43,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() @@ -100,14 +108,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)