NeoFS CLI refactor proposal #404

Closed
opened 2025-12-28 17:19:21 +00:00 by sami · 2 comments
Owner

Originally created by @alexvanin on GitHub (Jan 10, 2022).

Originally assigned to: @alexvanin on GitHub.

This is the draft of NeoFS CLI refactor proposal. I believe that refactor will simplify introduction of new sub sections and sub commands into the application, such as util eacl-builder. Right now we have flat structure with one package for whole application.

└── modules
    ├── accounting.go
    ├── acl.go
    ├── completion.go
    ├── container.go
    ├── control.go
    ├── netmap.go
    ├── object.go
    ├── root.go
    ├── storagegroup.go
    └── util.go

Single package is great for sharing helper functions between sections. We share private key obtaining function, flag initialization, etc. However these section files slowly grow larger. util section already includes multiple sub sections and sub commands, and this is becoming hard to maintain.

I want to address this issue with two changes.

Define sections and sub sections in packages

$ neofs-cli util locode generate ...

cmd/neofs-cli/modules/util/locode/
├── generate.go
├── info.go
└── root.go

In the example above, util section is defined in modules/util package and locode sub section is stored in modules/util/locode package. generate command is defined in corresponding generate.go file there.

Every section will contain root.go file that includes definition of the section. Files that describe commands define command and it's implementation.

// structure of root.go files
var Cmd = &cobra.Command{
	Use:   "locode",
	Short: "Working with NeoFS UN/LOCODE database",
}

func init() {
	Cmd.AddCommand(locodeInfoCmd, locodeGenerateCmd)
}
// structure of command implementation files
var locodeGenerateCmd = &cobra.Command{
	Use:   "generate",
	Short: "generate UN/LOCODE database for NeoFS",
	Run:   locodeGenerate,
}

func init() {
	flags := locodeGenerateCmd.Flags()
	// process flags for the command
}

func locodeGenerate(cmd *cobra.Command, _ []string) {
	...
}

Share code in internal packages

There are plenty of code that shared among these sections. It can be stored in internal package the same way as client, even though it should be removed.

I tried such structure

cmd/neofs-cli/internal
├── client
│   ├── client.go
│   ├── doc.go
│   └── prm.go
├── errors
│   └── errors.go
├── flags
│   └── flags.go
└── io
    ├── eacl.go
    ├── key.go
    ├── prettyprint.go
    ├── token.go
    └── verbose.go
  • error package defines ErrF and ExitOnError wrappers, widely used in application
  • flags contains constants for all shared flags and bindings
  • io package defines function for reading values from flags (fetching private key, bearer and session tokens, etc.) and providing wrappers to print formatted values.

Example

PoC for util sub command can be found there https://github.com/alexvanin/neofs-node/tree/refactor/group-cli-commands

Comments are appreciated.

/cc @fyrchik @cthulhu-rider @carpawell

Work scope

Originally created by @alexvanin on GitHub (Jan 10, 2022). Originally assigned to: @alexvanin on GitHub. This is the draft of NeoFS CLI refactor proposal. I believe that refactor will simplify introduction of new sub sections and sub commands into the application, such as `util eacl-builder`. Right now we have flat structure with one package for whole application. ``` └── modules ├── accounting.go ├── acl.go ├── completion.go ├── container.go ├── control.go ├── netmap.go ├── object.go ├── root.go ├── storagegroup.go └── util.go ``` Single package is great for sharing helper functions between sections. We share private key obtaining function, flag initialization, etc. However these section files slowly grow larger. `util` section already includes multiple sub sections and sub commands, and this is becoming hard to maintain. I want to address this issue with two changes. ## Define sections and sub sections in packages ``` $ neofs-cli util locode generate ... cmd/neofs-cli/modules/util/locode/ ├── generate.go ├── info.go └── root.go ``` In the example above, `util` section is defined in `modules/util` package and locode sub section is stored in `modules/util/locode` package. `generate` command is defined in corresponding `generate.go` file there. Every section will contain `root.go` file that includes definition of the section. Files that describe commands define command and it's implementation. ```go // structure of root.go files var Cmd = &cobra.Command{ Use: "locode", Short: "Working with NeoFS UN/LOCODE database", } func init() { Cmd.AddCommand(locodeInfoCmd, locodeGenerateCmd) } ``` ```go // structure of command implementation files var locodeGenerateCmd = &cobra.Command{ Use: "generate", Short: "generate UN/LOCODE database for NeoFS", Run: locodeGenerate, } func init() { flags := locodeGenerateCmd.Flags() // process flags for the command } func locodeGenerate(cmd *cobra.Command, _ []string) { ... } ``` ## Share code in internal packages There are plenty of code that shared among these sections. It can be stored in internal package the same way as client, even though it [should be removed](https://github.com/nspcc-dev/neofs-node/pull/950#pullrequestreview-796681663). I tried such structure ``` cmd/neofs-cli/internal ├── client │   ├── client.go │   ├── doc.go │   └── prm.go ├── errors │   └── errors.go ├── flags │   └── flags.go └── io ├── eacl.go ├── key.go ├── prettyprint.go ├── token.go └── verbose.go ``` - `error` package defines `ErrF` and `ExitOnError` wrappers, widely used in application - `flags` contains constants for all shared flags and bindings - `io` package defines function for reading values from flags (fetching private key, bearer and session tokens, etc.) and providing wrappers to print formatted values. ## Example PoC for `util` sub command can be found there https://github.com/alexvanin/neofs-node/tree/refactor/group-cli-commands Comments are appreciated. /cc @fyrchik @cthulhu-rider @carpawell ## Work scope - [x] #1379 - [x] #1380 - [x] #1381 - [x] #1382 - [x] #1383 - [x] #1384 - [x] #1385
sami 2025-12-28 17:19:21 +00:00
Author
Owner

@cthulhu-rider commented on GitHub (Jan 11, 2022):

I think the proposal will have a very good impact on readability and maintainability. The package/directory tree can mimic the command hierarchy in the CLI which will make it easier to navigate through the code.

Don't you think that such a restructuring will be an argument for keeping the client package? The need to share client features between components is in great demand in neofs-cli, and the proposed division will quite noticeably split the client's use cases into many small ones, which can complicate its support and understanding.

@cthulhu-rider commented on GitHub (Jan 11, 2022): I think the proposal will have a very good impact on readability and maintainability. The package/directory tree can mimic the command hierarchy in the CLI which will make it easier to navigate through the code. Don't you think that such a restructuring will be an argument for keeping the `client` package? The need to share client features between components is in great demand in `neofs-cli`, and the proposed division will quite noticeably split the client's use cases into many small ones, which can complicate its support and understanding.
Author
Owner

@realloc commented on GitHub (Jan 11, 2022):

Agree, this change would make the neofs-cli more maintainable.

@realloc commented on GitHub (Jan 11, 2022): Agree, this change would make the `neofs-cli` more maintainable.
Sign in to join this conversation.
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference
nspcc-dev/neofs-node#404
No description provided.