1
0
mirror of https://github.com/mainflux/mainflux.git synced 2025-04-24 13:48:49 +08:00

MF-1268 - CLI improvements (#1274)

* Prefix error messages in CLI with a bold "error: ".

Signed-off-by: Joao Matos <joao@tritao.eu>

* Remove duplicated "Usage: " from groups command help.

Signed-off-by: Joao Matos <joao@tritao.eu>

* Add a raw output mode for CLI and use it on logCreated.

Signed-off-by: Joao Matos <joao@tritao.eu>

* Add CLI global flag for user auth token.

Signed-off-by: Joao Matos <joao@tritao.eu>

* Add CLI config flag and parsing logic.

Signed-off-by: Joao Matos <joao@tritao.eu>

* Refactor CLI users commands outside array structure.

Signed-off-by: Joao Matos <joao@tritao.eu>

* Refactor CLI certificates commands using flags.

Signed-off-by: Joao Matos <joao@tritao.eu>

* Refactor CLI things create command using flags.

Signed-off-by: Joao Matos <joao@tritao.eu>

Co-authored-by: Drasko DRASKOVIC <drasko.draskovic@gmail.com>
This commit is contained in:
João Matos 2020-10-31 23:29:06 +00:00 committed by GitHub
parent 46c675cd5f
commit 3273c30d8b
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
7 changed files with 181 additions and 44 deletions

View File

@ -1,54 +1,57 @@
package cli
import (
"errors"
"strconv"
"github.com/spf13/cobra"
)
var cmdCerts = []cobra.Command{
cobra.Command{
// NewCertsCmd returns certificate command.
func NewCertsCmd() *cobra.Command {
var keySize uint16
var keyType string
var ttl uint32
issueCmd := cobra.Command{
Use: "issue",
Short: "issue <thing_id> <keybits> <keytype> <hoursvalid> <user_auth_token>",
Short: "issue <thing_id> [--keysize=2048] [--keytype=rsa] [--ttl=8760]",
Long: `Issues new certificate for a thing`,
Run: func(cmd *cobra.Command, args []string) {
if len(args) != 5 {
if len(args) != 1 {
logUsage(cmd.Short)
return
}
thingID := args[0]
keyBits, err := strconv.Atoi(args[1])
if err != nil {
logError(errors.New("invalid format for keybits"))
return
}
valid := strconv.FormatUint(uint64(ttl), 10)
token := getUserAuthToken()
keyType := args[2]
valid := args[3]
token := args[4]
c, err := sdk.IssueCert(thingID, keyBits, keyType, valid, token)
c, err := sdk.IssueCert(thingID, int(keySize), keyType, valid, token)
if err != nil {
logError(err)
return
}
logJSON(c)
},
},
}
}
issueCmd.Flags().Uint16Var(&keySize, "keysize", 2048, "certificate key strength in bits: 2048, 4096 (RSA) or 224, 256, 384, 512 (EC)")
issueCmd.Flags().StringVar(&keyType, "keytype", "rsa", "certificate key type: RSA or EC")
issueCmd.Flags().Uint32Var(&ttl, "ttl", 8760, "certificate time to live in hours")
// NewCertsCmd returns certificate command.
func NewCertsCmd() *cobra.Command {
cmd := cobra.Command{
Use: "cert",
Short: "Certificate management",
Long: `Certificate management: create certificates for things"`,
Use: "certs",
Short: "Certificates management",
Long: `Certificates management: create certificates for things"`,
Run: func(cmd *cobra.Command, args []string) {
logUsage("cert issue <thing_id> <keybits> <keytype> <hoursvalid> <user_auth_token>")
logUsage("certs [issue]")
},
}
cmdCerts := []cobra.Command{
issueCmd,
}
for i := range cmdCerts {
cmd.AddCommand(&cmdCerts[i])
}

81
cli/config.go Normal file
View File

@ -0,0 +1,81 @@
package cli
import (
"fmt"
"io/ioutil"
"log"
"os"
"path"
"github.com/mainflux/mainflux/pkg/errors"
"github.com/pelletier/go-toml"
)
type Config struct {
AuthToken string `toml:"auth_token"`
}
// save - store config in a file
func save(c Config, file string) error {
b, err := toml.Marshal(c)
if err != nil {
return errors.New(fmt.Sprintf("failed to read config file: %s", err))
}
if err := ioutil.WriteFile(file, b, 0644); err != nil {
return errors.New(fmt.Sprintf("failed to write config TOML: %s", err))
}
return nil
}
// read - retrieve config from a file
func read(file string) (Config, error) {
data, err := ioutil.ReadFile(file)
c := Config{}
if err != nil {
return c, errors.New(fmt.Sprintf("failed to read config file: %s", err))
}
if err := toml.Unmarshal(data, &c); err != nil {
return Config{}, errors.New(fmt.Sprintf("failed to unmarshal config TOML: %s", err))
}
return c, nil
}
func getConfigPath() (string, error) {
// Check if a config path passed by user exists.
if ConfigPath != "" {
if _, err := os.Stat(ConfigPath); os.IsNotExist(err) {
errConfigNotFound := errors.Wrap(errors.New("config file was not found"), err)
logError(errConfigNotFound)
return "", err
}
}
// If not, then read it from the user config directory.
if ConfigPath == "" {
userConfigDir, _ := os.UserConfigDir()
ConfigPath = path.Join(userConfigDir, "mainflux", "cli.toml")
}
if _, err := os.Stat(ConfigPath); os.IsNotExist(err) {
return "", err
}
return ConfigPath, nil
}
func ParseConfig() {
path, err := getConfigPath()
if err != nil {
return
}
config, err := read(path)
if err != nil {
log.Fatal(err)
}
if UserAuthToken == "" {
UserAuthToken = config.AuthToken
}
}

View File

@ -172,7 +172,7 @@ func NewGroupsCmd() *cobra.Command {
Short: "Groups management",
Long: `Groups management: create groups and assigns user to groups"`,
Run: func(cmd *cobra.Command, args []string) {
logUsage("Usage: Groups [create | get | delete | assign | unassign | members | membership]")
logUsage("groups [create | get | delete | assign | unassign | members | membership]")
},
}
for i := range cmdGroups {

View File

@ -13,10 +13,10 @@ import (
var cmdThings = []cobra.Command{
cobra.Command{
Use: "create",
Short: "create <JSON_thing> <user_auth_token>",
Short: "create <JSON_thing>",
Long: `Create new thing, generate his UUID and store it`,
Run: func(cmd *cobra.Command, args []string) {
if len(args) != 2 {
if len(args) != 1 {
logUsage(cmd.Short)
return
}
@ -27,7 +27,8 @@ var cmdThings = []cobra.Command{
return
}
id, err := sdk.CreateThing(thing, args[1])
token := getUserAuthToken()
id, err := sdk.CreateThing(thing, token)
if err != nil {
logError(err)
return

View File

@ -10,8 +10,9 @@ import (
"github.com/spf13/cobra"
)
var cmdUsers = []cobra.Command{
cobra.Command{
// NewUsersCmd returns users command.
func NewUsersCmd() *cobra.Command {
createCmd := cobra.Command{
Use: "create",
Short: "create <username> <password>",
Long: `Creates new user`,
@ -33,8 +34,9 @@ var cmdUsers = []cobra.Command{
logCreated(id)
},
},
cobra.Command{
}
getCmd := cobra.Command{
Use: "get",
Short: "get <user_auth_token>",
Long: `Returns user object`,
@ -52,8 +54,9 @@ var cmdUsers = []cobra.Command{
logJSON(u)
},
},
cobra.Command{
}
tokenCmd := cobra.Command{
Use: "token",
Short: "token <username> <password>",
Long: `Creates new token`,
@ -74,9 +77,11 @@ var cmdUsers = []cobra.Command{
}
logCreated(token)
},
},
cobra.Command{
}
updateCmd := cobra.Command{
Use: "update",
Short: "update <JSON_string> <user_auth_token>",
Long: `Update user metadata`,
@ -99,8 +104,9 @@ var cmdUsers = []cobra.Command{
logOK()
},
},
cobra.Command{
}
passwordCmd := cobra.Command{
Use: "password",
Short: "password <old_password> <password> <user_auth_token>",
Long: `Update user password`,
@ -117,20 +123,21 @@ var cmdUsers = []cobra.Command{
logOK()
},
},
}
}
// NewUsersCmd returns users command.
func NewUsersCmd() *cobra.Command {
cmd := cobra.Command{
Use: "users",
Short: "Users management",
Long: `Users management: create accounts and tokens"`,
Run: func(cmd *cobra.Command, args []string) {
logUsage("Usage: users [create | get | update | token | password]")
logUsage("users [create | get | update | token | password]")
},
}
cmdUsers := []cobra.Command{
createCmd, getCmd, tokenCmd, updateCmd, passwordCmd,
}
for i := range cmdUsers {
cmd.AddCommand(&cmdUsers[i])
}

View File

@ -6,6 +6,7 @@ package cli
import (
"encoding/json"
"fmt"
"log"
"github.com/fatih/color"
prettyjson "github.com/hokaccha/go-prettyjson"
@ -18,6 +19,12 @@ var (
Offset uint = 0
// Name query parameter
Name string = ""
// UserAuthToken user auth token parameter
UserAuthToken string = ""
// ConfigPath config path parameter
ConfigPath string = ""
// RawOutput raw output mode
RawOutput bool = false
)
func logJSON(iList ...interface{}) {
@ -43,7 +50,10 @@ func logUsage(u string) {
}
func logError(err error) {
fmt.Printf("\n%s\n\n", color.RedString(err.Error()))
boldRed := color.New(color.FgRed, color.Bold)
boldRed.Print("\nerror: ")
fmt.Printf("%s\n\n", color.RedString(err.Error()))
}
func logOK() {
@ -51,5 +61,17 @@ func logOK() {
}
func logCreated(e string) {
fmt.Printf(color.BlueString("\ncreated: %s\n\n"), e)
if RawOutput {
fmt.Println(e)
} else {
fmt.Printf(color.BlueString("\ncreated: %s\n\n"), e)
}
}
func getUserAuthToken() string {
if UserAuthToken == "" {
log.Fatal("user auth token not valid, please pass using --user-auth-token flag or config file")
}
return UserAuthToken
}

View File

@ -32,6 +32,8 @@ func main() {
var rootCmd = &cobra.Command{
Use: "mainflux-cli",
PersistentPreRun: func(cmd *cobra.Command, args []string) {
cli.ParseConfig()
sdkConf.MsgContentType = sdk.ContentType(msgContentType)
s := sdk.NewSDK(sdkConf)
cli.SetSDK(s)
@ -117,6 +119,27 @@ func main() {
"Do not check for TLS cert",
)
rootCmd.PersistentFlags().StringVar(
&cli.UserAuthToken,
"user-auth-token",
"",
"Mainflux user auth token",
)
rootCmd.PersistentFlags().StringVar(
&cli.ConfigPath,
"config",
"",
"Mainflux config path",
)
rootCmd.PersistentFlags().BoolVar(
&cli.RawOutput,
"raw",
false,
"Enables raw output mode for easier parsing of output",
)
// Client and Channels Flags
rootCmd.PersistentFlags().UintVarP(
&cli.Limit,