diff --git a/Dockerfile.cli b/Dockerfile.cli new file mode 100644 index 000000000..53ca7d23c --- /dev/null +++ b/Dockerfile.cli @@ -0,0 +1,21 @@ +FROM golang:1.14-alpine as basebuilder +RUN apk add --update make bash + +FROM basebuilder as builder +ARG BUILD=now +ARG VERSION=dev +ARG REPO=repository +WORKDIR /src +COPY . /src + +RUN make bin/neofs-cli + +# Executable image +FROM scratch AS neofs-cli + +WORKDIR / + +COPY --from=builder /etc/ssl/certs/ca-certificates.crt /etc/ssl/certs/ +COPY --from=builder /src/bin/neofs-cli /bin/neofs-cli + +CMD ["neofs-cli"] diff --git a/Makefile b/Makefile index b191e99d6..987544d05 100644 --- a/Makefile +++ b/Makefile @@ -2,7 +2,9 @@ SHELL = bash REPO ?= $(shell go list -m) -VERSION ?= "$(shell git describe --tags --dirty --always)" +VERSION ?= $(shell git describe --tags --dirty --always) +BUILD ?= $(shell date -u --iso=seconds) +DEBUG ?= false HUB_IMAGE ?= nspccdev/neofs HUB_TAG ?= "$(shell echo ${VERSION} | sed 's/^v//')" @@ -10,8 +12,8 @@ HUB_TAG ?= "$(shell echo ${VERSION} | sed 's/^v//')" BIN = bin DIRS= $(BIN) -# List of binaries to build. May be automated. -CMDS = neofs-node neofs-ir +# List of binaries to build. +CMDS = $(notdir $(basename $(wildcard cmd/*))) CMS = $(addprefix $(BIN)/, $(CMDS)) BINS = $(addprefix $(BIN)/, $(CMDS)) @@ -27,7 +29,9 @@ $(BINS): $(DIRS) dep CGO_ENABLED=0 \ GO111MODULE=on \ go build -v -trimpath \ - -ldflags "-X ${REPO}/misc.Version=$(VERSION) -X ${REPO}/misc.Build=${BUILD}" \ + -ldflags "-X $(REPO)/misc.Version=$(VERSION) \ + -X $(REPO)/misc.Build=$(BUILD) \ + -X $(REPO)/misc.Debug=$(DEBUG)" \ -o $@ ./cmd/$(notdir $@) $(DIRS): @@ -76,7 +80,7 @@ image-%: -t $(HUB_IMAGE)-$*:$(HUB_TAG) . # Build all Docker images -images: image-storage image-ir +images: image-storage image-ir image-cli # Run all code formaters fmts: fmt imports diff --git a/cmd/neofs-cli/main.go b/cmd/neofs-cli/main.go new file mode 100644 index 000000000..abdf9a4d5 --- /dev/null +++ b/cmd/neofs-cli/main.go @@ -0,0 +1,7 @@ +package main + +import cmd "github.com/nspcc-dev/neofs-node/cmd/neofs-cli/modules" + +func main() { + cmd.Execute() +} diff --git a/cmd/neofs-cli/modules/accounting.go b/cmd/neofs-cli/modules/accounting.go new file mode 100644 index 000000000..8d22d5cdd --- /dev/null +++ b/cmd/neofs-cli/modules/accounting.go @@ -0,0 +1,31 @@ +package cmd + +import ( + "fmt" + + "github.com/spf13/cobra" +) + +// accountingCmd represents the accounting command +var accountingCmd = &cobra.Command{ + Use: "accounting", + Short: "Operations with accounts and balances", + Long: `Operations with accounts and balances`, + Run: func(cmd *cobra.Command, args []string) { + fmt.Println("accounting called") + }, +} + +func init() { + rootCmd.AddCommand(accountingCmd) + + // Here you will define your flags and configuration settings. + + // Cobra supports Persistent Flags which will work for this command + // and all subcommands, e.g.: + // accountingCmd.PersistentFlags().String("foo", "", "A help for foo") + + // Cobra supports local flags which will only run when this command + // is called directly, e.g.: + // accountingCmd.Flags().BoolP("toggle", "t", false, "Help message for toggle") +} diff --git a/cmd/neofs-cli/modules/acl.go b/cmd/neofs-cli/modules/acl.go new file mode 100644 index 000000000..101e48ec7 --- /dev/null +++ b/cmd/neofs-cli/modules/acl.go @@ -0,0 +1,31 @@ +package cmd + +import ( + "fmt" + + "github.com/spf13/cobra" +) + +// aclCmd represents the acl command +var aclCmd = &cobra.Command{ + Use: "acl", + Short: "Operations with Access Control Lists", + Long: `Operations with Access Control Lists`, + Run: func(cmd *cobra.Command, args []string) { + fmt.Println("acl called") + }, +} + +func init() { + rootCmd.AddCommand(aclCmd) + + // Here you will define your flags and configuration settings. + + // Cobra supports Persistent Flags which will work for this command + // and all subcommands, e.g.: + // aclCmd.PersistentFlags().String("foo", "", "A help for foo") + + // Cobra supports local flags which will only run when this command + // is called directly, e.g.: + // aclCmd.Flags().BoolP("toggle", "t", false, "Help message for toggle") +} diff --git a/cmd/neofs-cli/modules/completion.go b/cmd/neofs-cli/modules/completion.go new file mode 100644 index 000000000..2a45d5c92 --- /dev/null +++ b/cmd/neofs-cli/modules/completion.go @@ -0,0 +1,62 @@ +package cmd + +import ( + "os" + + "github.com/spf13/cobra" +) + +var completionCmd = &cobra.Command{ + Use: "completion [bash|zsh|fish|powershell]", + Short: "Generate completion script", + Long: `To load completions: + +Bash: + +$ source <(neofs-cli completion bash) + +# To load completions for each session, execute once: +Linux: + $ neofs-cli completion bash > /etc/bash_completion.d/neofs-cli +MacOS: + $ neofs-cli completion bash > /usr/local/etc/bash_completion.d/neofs-cli + +Zsh: + +# If shell completion is not already enabled in your environment you will need +# to enable it. You can execute the following once: + +$ echo "autoload -U compinit; compinit" >> ~/.zshrc + +# To load completions for each session, execute once: +$ neofs-cli completion zsh > "${fpath[1]}/_neofs-cli" + +# You will need to start a new shell for this setup to take effect. + +Fish: + +$ neofs-cli completion fish | source + +# To load completions for each session, execute once: +$ neofs-cli completion fish > ~/.config/fish/completions/neofs-cli.fish +`, + DisableFlagsInUseLine: true, + ValidArgs: []string{"bash", "zsh", "fish", "powershell"}, + Args: cobra.ExactValidArgs(1), + Run: func(cmd *cobra.Command, args []string) { + switch args[0] { + case "bash": + _ = cmd.Root().GenBashCompletion(os.Stdout) + case "zsh": + _ = cmd.Root().GenZshCompletion(os.Stdout) + case "fish": + _ = cmd.Root().GenFishCompletion(os.Stdout, true) + case "powershell": + _ = cmd.Root().GenPowerShellCompletion(os.Stdout) + } + }, +} + +func init() { + rootCmd.AddCommand(completionCmd) +} diff --git a/cmd/neofs-cli/modules/container.go b/cmd/neofs-cli/modules/container.go new file mode 100644 index 000000000..d7ec4765b --- /dev/null +++ b/cmd/neofs-cli/modules/container.go @@ -0,0 +1,31 @@ +package cmd + +import ( + "fmt" + + "github.com/spf13/cobra" +) + +// containerCmd represents the container command +var containerCmd = &cobra.Command{ + Use: "container", + Short: "Operations with Containers", + Long: `Operations with Containers`, + Run: func(cmd *cobra.Command, args []string) { + fmt.Println("container called") + }, +} + +func init() { + rootCmd.AddCommand(containerCmd) + + // Here you will define your flags and configuration settings. + + // Cobra supports Persistent Flags which will work for this command + // and all subcommands, e.g.: + // containerCmd.PersistentFlags().String("foo", "", "A help for foo") + + // Cobra supports local flags which will only run when this command + // is called directly, e.g.: + // containerCmd.Flags().BoolP("toggle", "t", false, "Help message for toggle") +} diff --git a/cmd/neofs-cli/modules/netmap.go b/cmd/neofs-cli/modules/netmap.go new file mode 100644 index 000000000..fbb09cd91 --- /dev/null +++ b/cmd/neofs-cli/modules/netmap.go @@ -0,0 +1,31 @@ +package cmd + +import ( + "fmt" + + "github.com/spf13/cobra" +) + +// netmapCmd represents the netmap command +var netmapCmd = &cobra.Command{ + Use: "netmap", + Short: "Operations with Network Map", + Long: `Operations with Network Map`, + Run: func(cmd *cobra.Command, args []string) { + fmt.Println("netmap called") + }, +} + +func init() { + rootCmd.AddCommand(netmapCmd) + + // Here you will define your flags and configuration settings. + + // Cobra supports Persistent Flags which will work for this command + // and all subcommands, e.g.: + // netmapCmd.PersistentFlags().String("foo", "", "A help for foo") + + // Cobra supports local flags which will only run when this command + // is called directly, e.g.: + // netmapCmd.Flags().BoolP("toggle", "t", false, "Help message for toggle") +} diff --git a/cmd/neofs-cli/modules/object.go b/cmd/neofs-cli/modules/object.go new file mode 100644 index 000000000..b5c5d543e --- /dev/null +++ b/cmd/neofs-cli/modules/object.go @@ -0,0 +1,31 @@ +package cmd + +import ( + "fmt" + + "github.com/spf13/cobra" +) + +// objectCmd represents the object command +var objectCmd = &cobra.Command{ + Use: "object", + Short: "Operations with Objects", + Long: `Operations with Objects`, + Run: func(cmd *cobra.Command, args []string) { + fmt.Println("object called") + }, +} + +func init() { + rootCmd.AddCommand(objectCmd) + + // Here you will define your flags and configuration settings. + + // Cobra supports Persistent Flags which will work for this command + // and all subcommands, e.g.: + // objectCmd.PersistentFlags().String("foo", "", "A help for foo") + + // Cobra supports local flags which will only run when this command + // is called directly, e.g.: + // objectCmd.Flags().BoolP("toggle", "t", false, "Help message for toggle") +} diff --git a/cmd/neofs-cli/modules/root.go b/cmd/neofs-cli/modules/root.go new file mode 100644 index 000000000..9d8fdab17 --- /dev/null +++ b/cmd/neofs-cli/modules/root.go @@ -0,0 +1,74 @@ +package cmd + +import ( + "fmt" + "os" + + "github.com/spf13/cobra" + + homedir "github.com/mitchellh/go-homedir" + "github.com/spf13/viper" +) + +var cfgFile string + +// rootCmd represents the base command when called without any subcommands +var rootCmd = &cobra.Command{ + Use: "neofs-cli", + Short: "Command Line Tool to work with NeoFS", + Long: `NeoFS CLI provides all basic interactions with NeoFS and it's services. + +It contains commands for interaction with NeoFS nodes using different versions +of neofs-api and some useful utilities for compiling ACL rules from JSON +notation, managing container access through protocol gates, querying network map +and much more!`, +} + +// Execute adds all child commands to the root command and sets flags appropriately. +// This is called by main.main(). It only needs to happen once to the rootCmd. +func Execute() { + if err := rootCmd.Execute(); err != nil { + fmt.Println(err) + os.Exit(1) + } +} + +func init() { + cobra.OnInitialize(initConfig) + + // Here you will define your flags and configuration settings. + // Cobra supports persistent flags, which, if defined here, + // will be global for your application. + + rootCmd.PersistentFlags().StringVar(&cfgFile, "config", "", "config file (default is $HOME/.config/neofs-cli/config.yaml)") + + // Cobra also supports local flags, which will only run + // when this action is called directly. + // rootCmd.Flags().BoolP("toggle", "t", false, "Help message for toggle") +} + +// initConfig reads in config file and ENV variables if set. +func initConfig() { + if cfgFile != "" { + // Use config file from the flag. + viper.SetConfigFile(cfgFile) + } else { + // Find home directory. + home, err := homedir.Dir() + if err != nil { + fmt.Println(err) + os.Exit(1) + } + + // Search config in home directory with name ".main" (without extension). + viper.AddConfigPath(home) + viper.SetConfigName(".config/neofs-cli") + } + + viper.AutomaticEnv() // read in environment variables that match + + // If a config file is found, read it in. + if err := viper.ReadInConfig(); err == nil { + fmt.Println("Using config file:", viper.ConfigFileUsed()) + } +} diff --git a/cmd/neofs-cli/modules/storagegroup.go b/cmd/neofs-cli/modules/storagegroup.go new file mode 100644 index 000000000..c7302ff0c --- /dev/null +++ b/cmd/neofs-cli/modules/storagegroup.go @@ -0,0 +1,31 @@ +package cmd + +import ( + "fmt" + + "github.com/spf13/cobra" +) + +// storagegroupCmd represents the storagegroup command +var storagegroupCmd = &cobra.Command{ + Use: "storagegroup", + Short: "Operations with Storage Groups", + Long: `Operations with Storage Groups`, + Run: func(cmd *cobra.Command, args []string) { + fmt.Println("storagegroup called") + }, +} + +func init() { + rootCmd.AddCommand(storagegroupCmd) + + // Here you will define your flags and configuration settings. + + // Cobra supports Persistent Flags which will work for this command + // and all subcommands, e.g.: + // storagegroupCmd.PersistentFlags().String("foo", "", "A help for foo") + + // Cobra supports local flags which will only run when this command + // is called directly, e.g.: + // storagegroupCmd.Flags().BoolP("toggle", "t", false, "Help message for toggle") +} diff --git a/cmd/neofs-cli/modules/version.go b/cmd/neofs-cli/modules/version.go new file mode 100644 index 000000000..939b3d3b9 --- /dev/null +++ b/cmd/neofs-cli/modules/version.go @@ -0,0 +1,47 @@ +package cmd + +import ( + "encoding/json" + "fmt" + "github.com/nspcc-dev/neofs-node/misc" + + "github.com/spf13/cobra" +) + +var ( + // versionCmd represents the version command + versionCmd = &cobra.Command{ + Use: "version", + Short: "Print version and exit", + Run: versionRun, + } +) + +var flagJSON bool + +type VersionInfo struct { + Version string `json:"Version,omitempty"` + Build string `json:"Build,omitempty"` + Debug string `json:"Debug,omitempty"` +} + +func init() { + rootCmd.AddCommand(versionCmd) + versionCmd.Flags().BoolVarP(&flagJSON, "json", "j", false, "Print version information in JSON") +} + +func versionRun(cmd *cobra.Command, args []string) { + + versionInfo := VersionInfo{Version: misc.Version, Build: misc.Build, Debug: misc.Debug} + + if flagJSON { + bytes, _ := json.Marshal(versionInfo) + fmt.Printf("%s", string(bytes)+"\n") + return + } + + fmt.Printf("Version: %s \nBuild: %s \nDebug: %s\n", + versionInfo.Version, + versionInfo.Build, + versionInfo.Debug) +} diff --git a/go.mod b/go.mod index 0b66e86c8..3483090f0 100644 --- a/go.mod +++ b/go.mod @@ -6,6 +6,7 @@ require ( bou.ke/monkey v1.0.2 github.com/golang/protobuf v1.4.2 github.com/google/uuid v1.1.1 + github.com/mitchellh/go-homedir v1.1.0 github.com/mr-tron/base58 v1.1.3 github.com/multiformats/go-multiaddr v0.2.0 github.com/multiformats/go-multiaddr-net v0.1.2 // v0.1.1 => v0.1.2 @@ -17,6 +18,7 @@ require ( github.com/pkg/errors v0.9.1 github.com/prometheus/client_golang v1.6.0 github.com/soheilhy/cmux v0.1.4 + github.com/spf13/cobra v1.0.0 github.com/spf13/pflag v1.0.5 // indirect github.com/spf13/viper v1.7.0 github.com/stretchr/testify v1.6.1 diff --git a/go.sum b/go.sum index a99aeddd9..c54ce5c71 100644 Binary files a/go.sum and b/go.sum differ diff --git a/misc/build.go b/misc/build.go index 61b4613ef..243977986 100644 --- a/misc/build.go +++ b/misc/build.go @@ -23,5 +23,6 @@ var ( Version = "dev" // Debug is an application debug mode flag. - Debug = "true" + Debug = "false" ) +