mirror of
https://github.com/absmach/magistrala.git
synced 2026-06-23 04:10:28 +00:00
61d0427898
Signed-off-by: dusan <borovcanindusan1@gmail.com>
411 lines
9.4 KiB
Go
411 lines
9.4 KiB
Go
// Copyright (c) Abstract Machines
|
|
// SPDX-License-Identifier: Apache-2.0
|
|
|
|
package cli
|
|
|
|
import (
|
|
"encoding/csv"
|
|
"encoding/json"
|
|
"errors"
|
|
"fmt"
|
|
"io"
|
|
"math/rand"
|
|
"os"
|
|
"path/filepath"
|
|
"time"
|
|
|
|
"github.com/0x6flab/namegenerator"
|
|
smqsdk "github.com/absmach/magistrala/pkg/sdk"
|
|
"github.com/spf13/cobra"
|
|
)
|
|
|
|
const (
|
|
jsonExt = ".json"
|
|
csvExt = ".csv"
|
|
PublishType = "publish"
|
|
SubscribeType = "subscribe"
|
|
)
|
|
|
|
var (
|
|
msgFormat = `[{"bn":"provision:", "bu":"V", "t": %d, "bver":5, "n":"voltage", "u":"V", "v":%d}]`
|
|
namesgenerator = namegenerator.NewGenerator()
|
|
)
|
|
|
|
var cmdProvision = []cobra.Command{
|
|
{
|
|
Use: "clients <clients_file> <domain_id> <user_token>",
|
|
Short: "Provision clients",
|
|
Long: `Bulk create clients`,
|
|
Run: func(cmd *cobra.Command, args []string) {
|
|
if len(args) != 3 {
|
|
logUsageCmd(*cmd, cmd.Use)
|
|
return
|
|
}
|
|
|
|
if _, err := os.Stat(args[0]); os.IsNotExist(err) {
|
|
logErrorCmd(*cmd, err)
|
|
return
|
|
}
|
|
|
|
clients, err := clientsFromFile(args[0])
|
|
if err != nil {
|
|
logErrorCmd(*cmd, err)
|
|
return
|
|
}
|
|
|
|
clients, err = sdk.CreateClients(cmd.Context(), clients, args[1], args[2])
|
|
if err != nil {
|
|
logErrorCmd(*cmd, err)
|
|
return
|
|
}
|
|
|
|
logJSONCmd(*cmd, clients)
|
|
},
|
|
},
|
|
{
|
|
Use: "channels <channels_file> <domain_id> <user_token>",
|
|
Short: "Provision channels",
|
|
Long: `Bulk create channels`,
|
|
Run: func(cmd *cobra.Command, args []string) {
|
|
if len(args) != 3 {
|
|
logUsageCmd(*cmd, cmd.Use)
|
|
return
|
|
}
|
|
|
|
channels, err := channelsFromFile(args[0])
|
|
if err != nil {
|
|
logErrorCmd(*cmd, err)
|
|
return
|
|
}
|
|
|
|
var chs []smqsdk.Channel
|
|
for _, c := range channels {
|
|
c, err = sdk.CreateChannel(cmd.Context(), c, args[1], args[2])
|
|
if err != nil {
|
|
logErrorCmd(*cmd, err)
|
|
return
|
|
}
|
|
chs = append(chs, c)
|
|
}
|
|
channels = chs
|
|
|
|
logJSONCmd(*cmd, channels)
|
|
},
|
|
},
|
|
{
|
|
Use: "connect <connections_file> <domain_id> <user_token>",
|
|
Short: "Provision connections",
|
|
Long: `Bulk connect clients to channels`,
|
|
Run: func(cmd *cobra.Command, args []string) {
|
|
if len(args) != 3 {
|
|
logUsageCmd(*cmd, cmd.Use)
|
|
return
|
|
}
|
|
|
|
connIDs, err := connectionsFromFile(args[0])
|
|
if err != nil {
|
|
logErrorCmd(*cmd, err)
|
|
return
|
|
}
|
|
for _, conn := range connIDs {
|
|
if err := sdk.Connect(cmd.Context(), conn, args[1], args[2]); err != nil {
|
|
logErrorCmd(*cmd, err)
|
|
return
|
|
}
|
|
}
|
|
|
|
logOKCmd(*cmd)
|
|
},
|
|
},
|
|
{
|
|
Use: "test",
|
|
Short: "test",
|
|
Long: `Provisions test setup: one test user, two clients and two channels. \
|
|
Connect both clients to one of the channels, \
|
|
and only on client to other channel.`,
|
|
Run: func(cmd *cobra.Command, args []string) {
|
|
numClients := 2
|
|
numChan := 2
|
|
clients := []smqsdk.Client{}
|
|
channels := []smqsdk.Channel{}
|
|
|
|
if len(args) != 0 {
|
|
logUsageCmd(*cmd, cmd.Use)
|
|
return
|
|
}
|
|
|
|
// Create test user
|
|
name := namesgenerator.Generate()
|
|
user := smqsdk.User{
|
|
FirstName: name,
|
|
Email: fmt.Sprintf("%s@email.com", name),
|
|
Credentials: smqsdk.Credentials{
|
|
Username: name,
|
|
Secret: "12345678",
|
|
},
|
|
Status: smqsdk.EnabledStatus,
|
|
}
|
|
user, err := sdk.CreateUser(cmd.Context(), user, "")
|
|
if err != nil {
|
|
logErrorCmd(*cmd, err)
|
|
return
|
|
}
|
|
|
|
ut, err := sdk.CreateToken(cmd.Context(), smqsdk.Login{Username: user.Credentials.Username, Password: user.Credentials.Secret})
|
|
if err != nil {
|
|
logErrorCmd(*cmd, err)
|
|
return
|
|
}
|
|
|
|
// create domain
|
|
domain := smqsdk.Domain{
|
|
Name: fmt.Sprintf("%s-domain", name),
|
|
Status: smqsdk.EnabledStatus,
|
|
}
|
|
domain, err = sdk.CreateDomain(cmd.Context(), domain, ut.AccessToken)
|
|
if err != nil {
|
|
logErrorCmd(*cmd, err)
|
|
return
|
|
}
|
|
|
|
ut, err = sdk.CreateToken(cmd.Context(), smqsdk.Login{Username: user.Email, Password: user.Credentials.Secret})
|
|
if err != nil {
|
|
logErrorCmd(*cmd, err)
|
|
return
|
|
}
|
|
|
|
// Create clients
|
|
for i := 0; i < numClients; i++ {
|
|
t := smqsdk.Client{
|
|
Name: fmt.Sprintf("%s-client-%d", name, i),
|
|
Status: smqsdk.EnabledStatus,
|
|
}
|
|
|
|
clients = append(clients, t)
|
|
}
|
|
clients, err = sdk.CreateClients(cmd.Context(), clients, domain.ID, ut.AccessToken)
|
|
if err != nil {
|
|
logErrorCmd(*cmd, err)
|
|
return
|
|
}
|
|
|
|
// Create channels
|
|
for i := 0; i < numChan; i++ {
|
|
c := smqsdk.Channel{
|
|
Name: fmt.Sprintf("%s-channel-%d", name, i),
|
|
Status: smqsdk.EnabledStatus,
|
|
}
|
|
c, err = sdk.CreateChannel(cmd.Context(), c, domain.ID, ut.AccessToken)
|
|
if err != nil {
|
|
logErrorCmd(*cmd, err)
|
|
return
|
|
}
|
|
|
|
channels = append(channels, c)
|
|
}
|
|
|
|
// Connect clients to channels - first client to both channels, second only to first
|
|
conIDs := smqsdk.Connection{
|
|
ChannelIDs: []string{channels[0].ID},
|
|
ClientIDs: []string{clients[0].ID},
|
|
Types: []string{PublishType, SubscribeType},
|
|
}
|
|
if err := sdk.Connect(cmd.Context(), conIDs, domain.ID, ut.AccessToken); err != nil {
|
|
logErrorCmd(*cmd, err)
|
|
return
|
|
}
|
|
|
|
conIDs = smqsdk.Connection{
|
|
ChannelIDs: []string{channels[1].ID},
|
|
ClientIDs: []string{clients[0].ID},
|
|
Types: []string{PublishType, SubscribeType},
|
|
}
|
|
if err := sdk.Connect(cmd.Context(), conIDs, domain.ID, ut.AccessToken); err != nil {
|
|
logErrorCmd(*cmd, err)
|
|
return
|
|
}
|
|
|
|
conIDs = smqsdk.Connection{
|
|
ChannelIDs: []string{channels[0].ID},
|
|
ClientIDs: []string{clients[1].ID},
|
|
Types: []string{PublishType, SubscribeType},
|
|
}
|
|
if err := sdk.Connect(cmd.Context(), conIDs, domain.ID, ut.AccessToken); err != nil {
|
|
logErrorCmd(*cmd, err)
|
|
return
|
|
}
|
|
|
|
// send message to test connectivity
|
|
if err := sdk.SendMessage(cmd.Context(), domain.ID, channels[0].ID, clients[0].Credentials.Secret, fmt.Sprintf(msgFormat, time.Now().Unix(), rand.Int())); err != nil {
|
|
logErrorCmd(*cmd, err)
|
|
return
|
|
}
|
|
if err := sdk.SendMessage(cmd.Context(), domain.ID, channels[0].ID, clients[1].Credentials.Secret, fmt.Sprintf(msgFormat, time.Now().Unix(), rand.Int())); err != nil {
|
|
logErrorCmd(*cmd, err)
|
|
return
|
|
}
|
|
if err := sdk.SendMessage(cmd.Context(), domain.ID, channels[1].ID, clients[0].Credentials.Secret, fmt.Sprintf(msgFormat, time.Now().Unix(), rand.Int())); err != nil {
|
|
logErrorCmd(*cmd, err)
|
|
return
|
|
}
|
|
|
|
logJSONCmd(*cmd, user, ut, clients, channels)
|
|
},
|
|
},
|
|
}
|
|
|
|
// NewProvisionCmd returns provision command.
|
|
func NewProvisionCmd() *cobra.Command {
|
|
cmd := cobra.Command{
|
|
Use: "provision [clients | channels | connect | test]",
|
|
Short: "Provision clients and channels from a config file",
|
|
Long: `Provision clients and channels: use json or csv file to bulk provision clients and channels`,
|
|
}
|
|
|
|
for i := range cmdProvision {
|
|
cmd.AddCommand(&cmdProvision[i])
|
|
}
|
|
|
|
return &cmd
|
|
}
|
|
|
|
func clientsFromFile(path string) ([]smqsdk.Client, error) {
|
|
if _, err := os.Stat(path); os.IsNotExist(err) {
|
|
return []smqsdk.Client{}, err
|
|
}
|
|
|
|
file, err := os.OpenFile(path, os.O_RDONLY, os.ModePerm)
|
|
if err != nil {
|
|
return []smqsdk.Client{}, err
|
|
}
|
|
defer file.Close()
|
|
|
|
clients := []smqsdk.Client{}
|
|
switch filepath.Ext(path) {
|
|
case csvExt:
|
|
reader := csv.NewReader(file)
|
|
|
|
for {
|
|
l, err := reader.Read()
|
|
if err == io.EOF {
|
|
break
|
|
}
|
|
if err != nil {
|
|
return []smqsdk.Client{}, err
|
|
}
|
|
|
|
if len(l) < 1 {
|
|
return []smqsdk.Client{}, errors.New("empty line found in file")
|
|
}
|
|
|
|
client := smqsdk.Client{
|
|
Name: l[0],
|
|
}
|
|
|
|
clients = append(clients, client)
|
|
}
|
|
case jsonExt:
|
|
err := json.NewDecoder(file).Decode(&clients)
|
|
if err != nil {
|
|
return []smqsdk.Client{}, err
|
|
}
|
|
default:
|
|
return []smqsdk.Client{}, err
|
|
}
|
|
|
|
return clients, nil
|
|
}
|
|
|
|
func channelsFromFile(path string) ([]smqsdk.Channel, error) {
|
|
if _, err := os.Stat(path); os.IsNotExist(err) {
|
|
return []smqsdk.Channel{}, err
|
|
}
|
|
|
|
file, err := os.OpenFile(path, os.O_RDONLY, os.ModePerm)
|
|
if err != nil {
|
|
return []smqsdk.Channel{}, err
|
|
}
|
|
defer file.Close()
|
|
|
|
channels := []smqsdk.Channel{}
|
|
switch filepath.Ext(path) {
|
|
case csvExt:
|
|
reader := csv.NewReader(file)
|
|
|
|
for {
|
|
l, err := reader.Read()
|
|
if err == io.EOF {
|
|
break
|
|
}
|
|
if err != nil {
|
|
return []smqsdk.Channel{}, err
|
|
}
|
|
|
|
if len(l) < 1 {
|
|
return []smqsdk.Channel{}, errors.New("empty line found in file")
|
|
}
|
|
|
|
channel := smqsdk.Channel{
|
|
Name: l[0],
|
|
}
|
|
|
|
channels = append(channels, channel)
|
|
}
|
|
case jsonExt:
|
|
err := json.NewDecoder(file).Decode(&channels)
|
|
if err != nil {
|
|
return []smqsdk.Channel{}, err
|
|
}
|
|
default:
|
|
return []smqsdk.Channel{}, err
|
|
}
|
|
|
|
return channels, nil
|
|
}
|
|
|
|
func connectionsFromFile(path string) ([]smqsdk.Connection, error) {
|
|
if _, err := os.Stat(path); os.IsNotExist(err) {
|
|
return []smqsdk.Connection{}, err
|
|
}
|
|
|
|
file, err := os.OpenFile(path, os.O_RDONLY, os.ModePerm)
|
|
if err != nil {
|
|
return []smqsdk.Connection{}, err
|
|
}
|
|
defer file.Close()
|
|
|
|
connections := []smqsdk.Connection{}
|
|
switch filepath.Ext(path) {
|
|
case csvExt:
|
|
reader := csv.NewReader(file)
|
|
|
|
for {
|
|
l, err := reader.Read()
|
|
if err == io.EOF {
|
|
break
|
|
}
|
|
if err != nil {
|
|
return []smqsdk.Connection{}, err
|
|
}
|
|
|
|
if len(l) < 1 {
|
|
return []smqsdk.Connection{}, errors.New("empty line found in file")
|
|
}
|
|
connections = append(connections, smqsdk.Connection{
|
|
ClientIDs: []string{l[0]},
|
|
ChannelIDs: []string{l[1]},
|
|
Types: []string{PublishType, SubscribeType},
|
|
})
|
|
}
|
|
case jsonExt:
|
|
err := json.NewDecoder(file).Decode(&connections)
|
|
if err != nil {
|
|
return []smqsdk.Connection{}, err
|
|
}
|
|
default:
|
|
return []smqsdk.Connection{}, err
|
|
}
|
|
|
|
return connections, nil
|
|
}
|