mirror of
https://github.com/mainflux/mainflux.git
synced 2025-04-29 13:49:28 +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:
parent
46c675cd5f
commit
3273c30d8b
49
cli/certs.go
49
cli/certs.go
@ -1,54 +1,57 @@
|
|||||||
package cli
|
package cli
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"errors"
|
|
||||||
"strconv"
|
"strconv"
|
||||||
|
|
||||||
"github.com/spf13/cobra"
|
"github.com/spf13/cobra"
|
||||||
)
|
)
|
||||||
|
|
||||||
var cmdCerts = []cobra.Command{
|
// NewCertsCmd returns certificate command.
|
||||||
cobra.Command{
|
func NewCertsCmd() *cobra.Command {
|
||||||
|
var keySize uint16
|
||||||
|
var keyType string
|
||||||
|
var ttl uint32
|
||||||
|
|
||||||
|
issueCmd := cobra.Command{
|
||||||
Use: "issue",
|
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`,
|
Long: `Issues new certificate for a thing`,
|
||||||
Run: func(cmd *cobra.Command, args []string) {
|
Run: func(cmd *cobra.Command, args []string) {
|
||||||
if len(args) != 5 {
|
if len(args) != 1 {
|
||||||
logUsage(cmd.Short)
|
logUsage(cmd.Short)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
thingID := args[0]
|
thingID := args[0]
|
||||||
keyBits, err := strconv.Atoi(args[1])
|
valid := strconv.FormatUint(uint64(ttl), 10)
|
||||||
if err != nil {
|
token := getUserAuthToken()
|
||||||
logError(errors.New("invalid format for keybits"))
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
keyType := args[2]
|
c, err := sdk.IssueCert(thingID, int(keySize), keyType, valid, token)
|
||||||
valid := args[3]
|
|
||||||
token := args[4]
|
|
||||||
|
|
||||||
c, err := sdk.IssueCert(thingID, keyBits, keyType, valid, token)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
logError(err)
|
logError(err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
logJSON(c)
|
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{
|
cmd := cobra.Command{
|
||||||
Use: "cert",
|
Use: "certs",
|
||||||
Short: "Certificate management",
|
Short: "Certificates management",
|
||||||
Long: `Certificate management: create certificates for things"`,
|
Long: `Certificates management: create certificates for things"`,
|
||||||
Run: func(cmd *cobra.Command, args []string) {
|
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 {
|
for i := range cmdCerts {
|
||||||
cmd.AddCommand(&cmdCerts[i])
|
cmd.AddCommand(&cmdCerts[i])
|
||||||
}
|
}
|
||||||
|
81
cli/config.go
Normal file
81
cli/config.go
Normal 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
|
||||||
|
}
|
||||||
|
}
|
@ -172,7 +172,7 @@ func NewGroupsCmd() *cobra.Command {
|
|||||||
Short: "Groups management",
|
Short: "Groups management",
|
||||||
Long: `Groups management: create groups and assigns user to groups"`,
|
Long: `Groups management: create groups and assigns user to groups"`,
|
||||||
Run: func(cmd *cobra.Command, args []string) {
|
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 {
|
for i := range cmdGroups {
|
||||||
|
@ -13,10 +13,10 @@ import (
|
|||||||
var cmdThings = []cobra.Command{
|
var cmdThings = []cobra.Command{
|
||||||
cobra.Command{
|
cobra.Command{
|
||||||
Use: "create",
|
Use: "create",
|
||||||
Short: "create <JSON_thing> <user_auth_token>",
|
Short: "create <JSON_thing>",
|
||||||
Long: `Create new thing, generate his UUID and store it`,
|
Long: `Create new thing, generate his UUID and store it`,
|
||||||
Run: func(cmd *cobra.Command, args []string) {
|
Run: func(cmd *cobra.Command, args []string) {
|
||||||
if len(args) != 2 {
|
if len(args) != 1 {
|
||||||
logUsage(cmd.Short)
|
logUsage(cmd.Short)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@ -27,7 +27,8 @@ var cmdThings = []cobra.Command{
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
id, err := sdk.CreateThing(thing, args[1])
|
token := getUserAuthToken()
|
||||||
|
id, err := sdk.CreateThing(thing, token)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
logError(err)
|
logError(err)
|
||||||
return
|
return
|
||||||
|
37
cli/users.go
37
cli/users.go
@ -10,8 +10,9 @@ import (
|
|||||||
"github.com/spf13/cobra"
|
"github.com/spf13/cobra"
|
||||||
)
|
)
|
||||||
|
|
||||||
var cmdUsers = []cobra.Command{
|
// NewUsersCmd returns users command.
|
||||||
cobra.Command{
|
func NewUsersCmd() *cobra.Command {
|
||||||
|
createCmd := cobra.Command{
|
||||||
Use: "create",
|
Use: "create",
|
||||||
Short: "create <username> <password>",
|
Short: "create <username> <password>",
|
||||||
Long: `Creates new user`,
|
Long: `Creates new user`,
|
||||||
@ -33,8 +34,9 @@ var cmdUsers = []cobra.Command{
|
|||||||
|
|
||||||
logCreated(id)
|
logCreated(id)
|
||||||
},
|
},
|
||||||
},
|
}
|
||||||
cobra.Command{
|
|
||||||
|
getCmd := cobra.Command{
|
||||||
Use: "get",
|
Use: "get",
|
||||||
Short: "get <user_auth_token>",
|
Short: "get <user_auth_token>",
|
||||||
Long: `Returns user object`,
|
Long: `Returns user object`,
|
||||||
@ -52,8 +54,9 @@ var cmdUsers = []cobra.Command{
|
|||||||
|
|
||||||
logJSON(u)
|
logJSON(u)
|
||||||
},
|
},
|
||||||
},
|
}
|
||||||
cobra.Command{
|
|
||||||
|
tokenCmd := cobra.Command{
|
||||||
Use: "token",
|
Use: "token",
|
||||||
Short: "token <username> <password>",
|
Short: "token <username> <password>",
|
||||||
Long: `Creates new token`,
|
Long: `Creates new token`,
|
||||||
@ -74,9 +77,11 @@ var cmdUsers = []cobra.Command{
|
|||||||
}
|
}
|
||||||
|
|
||||||
logCreated(token)
|
logCreated(token)
|
||||||
|
|
||||||
},
|
},
|
||||||
},
|
}
|
||||||
cobra.Command{
|
|
||||||
|
updateCmd := cobra.Command{
|
||||||
Use: "update",
|
Use: "update",
|
||||||
Short: "update <JSON_string> <user_auth_token>",
|
Short: "update <JSON_string> <user_auth_token>",
|
||||||
Long: `Update user metadata`,
|
Long: `Update user metadata`,
|
||||||
@ -99,8 +104,9 @@ var cmdUsers = []cobra.Command{
|
|||||||
|
|
||||||
logOK()
|
logOK()
|
||||||
},
|
},
|
||||||
},
|
}
|
||||||
cobra.Command{
|
|
||||||
|
passwordCmd := cobra.Command{
|
||||||
Use: "password",
|
Use: "password",
|
||||||
Short: "password <old_password> <password> <user_auth_token>",
|
Short: "password <old_password> <password> <user_auth_token>",
|
||||||
Long: `Update user password`,
|
Long: `Update user password`,
|
||||||
@ -117,20 +123,21 @@ var cmdUsers = []cobra.Command{
|
|||||||
|
|
||||||
logOK()
|
logOK()
|
||||||
},
|
},
|
||||||
},
|
}
|
||||||
}
|
|
||||||
|
|
||||||
// NewUsersCmd returns users command.
|
|
||||||
func NewUsersCmd() *cobra.Command {
|
|
||||||
cmd := cobra.Command{
|
cmd := cobra.Command{
|
||||||
Use: "users",
|
Use: "users",
|
||||||
Short: "Users management",
|
Short: "Users management",
|
||||||
Long: `Users management: create accounts and tokens"`,
|
Long: `Users management: create accounts and tokens"`,
|
||||||
Run: func(cmd *cobra.Command, args []string) {
|
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 {
|
for i := range cmdUsers {
|
||||||
cmd.AddCommand(&cmdUsers[i])
|
cmd.AddCommand(&cmdUsers[i])
|
||||||
}
|
}
|
||||||
|
26
cli/utils.go
26
cli/utils.go
@ -6,6 +6,7 @@ package cli
|
|||||||
import (
|
import (
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"log"
|
||||||
|
|
||||||
"github.com/fatih/color"
|
"github.com/fatih/color"
|
||||||
prettyjson "github.com/hokaccha/go-prettyjson"
|
prettyjson "github.com/hokaccha/go-prettyjson"
|
||||||
@ -18,6 +19,12 @@ var (
|
|||||||
Offset uint = 0
|
Offset uint = 0
|
||||||
// Name query parameter
|
// Name query parameter
|
||||||
Name string = ""
|
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{}) {
|
func logJSON(iList ...interface{}) {
|
||||||
@ -43,7 +50,10 @@ func logUsage(u string) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func logError(err error) {
|
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() {
|
func logOK() {
|
||||||
@ -51,5 +61,17 @@ func logOK() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func logCreated(e string) {
|
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
|
||||||
}
|
}
|
||||||
|
@ -32,6 +32,8 @@ func main() {
|
|||||||
var rootCmd = &cobra.Command{
|
var rootCmd = &cobra.Command{
|
||||||
Use: "mainflux-cli",
|
Use: "mainflux-cli",
|
||||||
PersistentPreRun: func(cmd *cobra.Command, args []string) {
|
PersistentPreRun: func(cmd *cobra.Command, args []string) {
|
||||||
|
cli.ParseConfig()
|
||||||
|
|
||||||
sdkConf.MsgContentType = sdk.ContentType(msgContentType)
|
sdkConf.MsgContentType = sdk.ContentType(msgContentType)
|
||||||
s := sdk.NewSDK(sdkConf)
|
s := sdk.NewSDK(sdkConf)
|
||||||
cli.SetSDK(s)
|
cli.SetSDK(s)
|
||||||
@ -117,6 +119,27 @@ func main() {
|
|||||||
"Do not check for TLS cert",
|
"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
|
// Client and Channels Flags
|
||||||
rootCmd.PersistentFlags().UintVarP(
|
rootCmd.PersistentFlags().UintVarP(
|
||||||
&cli.Limit,
|
&cli.Limit,
|
||||||
|
Loading…
x
Reference in New Issue
Block a user