369 lines
11 KiB
Go
369 lines
11 KiB
Go
package main
|
|
|
|
import (
|
|
"context"
|
|
"fmt"
|
|
"github.com/anytypeio/go-anytype-infrastructure-experiments/common/app/logger"
|
|
"github.com/spf13/cobra"
|
|
"go.uber.org/zap"
|
|
"os"
|
|
"os/exec"
|
|
"path"
|
|
"path/filepath"
|
|
"sync"
|
|
)
|
|
|
|
var log = logger.NewNamed("cmd.deploy")
|
|
|
|
type rootArgs struct {
|
|
configPath string
|
|
nodePkgPath string
|
|
nodeBinaryPath string
|
|
clientPkgPath string
|
|
clientBinaryPath string
|
|
fileNodePkgPath string
|
|
fileNodeBinaryPath string
|
|
dbPath string
|
|
initialPath string
|
|
|
|
nodePkgName string
|
|
clientPkgName string
|
|
fileNodePkgName string
|
|
|
|
isDebug bool
|
|
}
|
|
|
|
type appPath struct {
|
|
wdPath string
|
|
binaryPath string
|
|
configPath string
|
|
logPath string
|
|
debugPortNum int
|
|
isDebug bool
|
|
}
|
|
|
|
const (
|
|
anytypeClientBinaryName = "anytype-client"
|
|
anytypeNodeBinaryName = "anytype-node"
|
|
anytypeFileNodeBinaryName = "anytype-file"
|
|
)
|
|
|
|
var rootCmd = &cobra.Command{
|
|
Use: "deploy",
|
|
PersistentPreRun: func(cmd *cobra.Command, args []string) {
|
|
rootArguments := rootArgs{}
|
|
|
|
rootArguments.nodePkgName, _ = cmd.Flags().GetString("node-pkg")
|
|
rootArguments.clientPkgName, _ = cmd.Flags().GetString("client-pkg")
|
|
rootArguments.fileNodePkgName, _ = cmd.Flags().GetString("filenode-pkg")
|
|
|
|
// checking configs
|
|
cfgPath, _ := cmd.Flags().GetString("config-path")
|
|
if _, err := os.Stat(cfgPath); os.IsNotExist(err) {
|
|
log.With(zap.Error(err)).Fatal("the config directory doesn't exist")
|
|
}
|
|
rootArguments.configPath, _ = filepath.Abs(cfgPath)
|
|
|
|
// checking node package
|
|
nodePath, _ := cmd.Flags().GetString("node-path")
|
|
if _, err := os.Stat(path.Join(nodePath, "go.mod")); os.IsNotExist(err) {
|
|
log.With(zap.Error(err)).Fatal("the path to node does not contain a go module")
|
|
}
|
|
rootArguments.nodePkgPath, _ = filepath.Abs(nodePath)
|
|
|
|
// checking client package
|
|
clientPath, _ := cmd.Flags().GetString("client-path")
|
|
if _, err := os.Stat(path.Join(clientPath, "go.mod")); os.IsNotExist(err) {
|
|
log.With(zap.Error(err)).Fatal("the path to client does not contain a go module")
|
|
}
|
|
rootArguments.clientPkgPath, _ = filepath.Abs(clientPath)
|
|
|
|
// checking file-node package
|
|
fileNodePath, _ := cmd.Flags().GetString("file-node-path")
|
|
if _, err := os.Stat(path.Join(clientPath, "go.mod")); os.IsNotExist(err) {
|
|
log.With(zap.Error(err)).Fatal("the path to file-node does not contain a go module")
|
|
}
|
|
rootArguments.fileNodePkgPath, _ = filepath.Abs(fileNodePath)
|
|
|
|
// checking binary path
|
|
binaryPath, _ := cmd.Flags().GetString("bin-path")
|
|
err := createDirectoryIfNotExists(binaryPath)
|
|
if err != nil {
|
|
log.With(zap.Error(err)).Fatal("failed to create directory")
|
|
}
|
|
|
|
absoluteBinPath, _ := filepath.Abs(binaryPath)
|
|
rootArguments.clientBinaryPath = path.Join(absoluteBinPath, anytypeClientBinaryName)
|
|
rootArguments.nodeBinaryPath = path.Join(absoluteBinPath, anytypeNodeBinaryName)
|
|
rootArguments.fileNodeBinaryPath = path.Join(absoluteBinPath, anytypeFileNodeBinaryName)
|
|
|
|
// getting debug mode
|
|
rootArguments.isDebug, _ = cmd.Flags().GetBool("debug")
|
|
|
|
// checking db path
|
|
dbPath, _ := cmd.Flags().GetString("db-path")
|
|
err = createDirectoryIfNotExists(dbPath)
|
|
if err != nil {
|
|
log.With(zap.Error(err)).Fatal("failed to create directory")
|
|
}
|
|
rootArguments.dbPath, _ = filepath.Abs(dbPath)
|
|
|
|
ctx := context.WithValue(context.Background(), "rootArguments", rootArguments)
|
|
cmd.SetContext(ctx)
|
|
},
|
|
}
|
|
|
|
var buildRunAllCmd = &cobra.Command{
|
|
Use: "build-run-all",
|
|
Long: "build and then run all clients and nodes",
|
|
Run: func(cmd *cobra.Command, args []string) {
|
|
rootArguments, ok := cmd.Context().Value("rootArguments").(rootArgs)
|
|
if !ok {
|
|
log.Fatal("did not get context")
|
|
}
|
|
|
|
numNodes, _ := cmd.Flags().GetUint("nodes")
|
|
numClients, _ := cmd.Flags().GetUint("clients")
|
|
numFileNodes, _ := cmd.Flags().GetUint("filenodes")
|
|
|
|
// running the script
|
|
err := buildRunAll(rootArguments, numClients, numNodes, numFileNodes)
|
|
if err != nil {
|
|
log.With(zap.Error(err)).Fatal("failed to run the command")
|
|
}
|
|
},
|
|
}
|
|
|
|
var buildAllCmd = &cobra.Command{
|
|
Use: "build-all",
|
|
Long: "builds both the clients and nodes",
|
|
Run: func(cmd *cobra.Command, args []string) {
|
|
rootArguments, ok := cmd.Context().Value("rootArguments").(rootArgs)
|
|
if !ok {
|
|
log.Fatal("did not get context")
|
|
}
|
|
|
|
err := buildAll(rootArguments)
|
|
if err != nil {
|
|
log.With(zap.Error(err)).Fatal("failed to run the command")
|
|
return
|
|
}
|
|
},
|
|
}
|
|
|
|
var runAllCmd = &cobra.Command{
|
|
Use: "run-all",
|
|
Long: "runs all clients and nodes",
|
|
Run: func(cmd *cobra.Command, args []string) {
|
|
rootArguments, ok := cmd.Context().Value("rootArguments").(rootArgs)
|
|
if !ok {
|
|
log.Fatal("did not get context")
|
|
}
|
|
|
|
numNodes, _ := cmd.Flags().GetUint("nodes")
|
|
numClients, _ := cmd.Flags().GetUint("clients")
|
|
numFileNodes, _ := cmd.Flags().GetUint("filenodes")
|
|
|
|
err := runAll(rootArguments, numClients, numNodes, numFileNodes)
|
|
if err != nil {
|
|
log.With(zap.Error(err)).Fatal("failed to run the command")
|
|
return
|
|
}
|
|
},
|
|
}
|
|
|
|
func init() {
|
|
rootCmd.PersistentFlags().String("config-path", "etc/configs", "generated configs")
|
|
rootCmd.PersistentFlags().String("node-pkg", "github.com/anytypeio/go-anytype-infrastructure-experiments/node/cmd", "node package")
|
|
rootCmd.PersistentFlags().String("client-pkg", "github.com/anytypeio/go-anytype-infrastructure-experiments/client/cmd", "client package")
|
|
rootCmd.PersistentFlags().String("filenode-pkg", "github.com/anytypeio/go-anytype-infrastructure-experiments/filenode/cmd", "client package")
|
|
rootCmd.PersistentFlags().String("node-path", "node", "path to node go.mod")
|
|
rootCmd.PersistentFlags().String("client-path", "client", "path to client go.mod")
|
|
rootCmd.PersistentFlags().String("filenode-path", "filenode", "path to client go.mod")
|
|
rootCmd.PersistentFlags().String("bin-path", "bin", "path to folder where all the binaries are")
|
|
rootCmd.PersistentFlags().String("db-path", "db", "path to folder where the working directories should be placed")
|
|
rootCmd.PersistentFlags().Bool("debug", false, "this tells if we should run the profiler")
|
|
|
|
buildRunAllCmd.Flags().UintP("nodes", "n", 3, "number of nodes to be generated")
|
|
buildRunAllCmd.Flags().UintP("clients", "c", 2, "number of clients to be generated")
|
|
buildRunAllCmd.Flags().UintP("filenodes", "f", 1, "number of filenodes to be generated")
|
|
|
|
runAllCmd.Flags().UintP("nodes", "n", 3, "number of nodes to be generated")
|
|
runAllCmd.Flags().UintP("clients", "c", 2, "number of clients to be generated")
|
|
runAllCmd.Flags().UintP("filenodes", "f", 1, "number of clients to be generated")
|
|
|
|
rootCmd.AddCommand(buildRunAllCmd)
|
|
rootCmd.AddCommand(buildAllCmd)
|
|
rootCmd.AddCommand(runAllCmd)
|
|
}
|
|
|
|
func main() {
|
|
err := rootCmd.Execute()
|
|
if err != nil {
|
|
log.With(zap.Error(err)).Fatal("failed to execute the command")
|
|
}
|
|
}
|
|
|
|
func createDirectoryIfNotExists(dirPath string) (err error) {
|
|
if _, err = os.Stat(dirPath); !os.IsNotExist(err) {
|
|
return
|
|
}
|
|
return os.Mkdir(dirPath, os.ModePerm)
|
|
}
|
|
|
|
func createAppPaths(args rootArgs, binaryPath, appName string, portNum, num int) (appPaths []appPath, err error) {
|
|
appTypePath := path.Join(args.dbPath, appName)
|
|
err = createDirectoryIfNotExists(appTypePath)
|
|
if err != nil {
|
|
return
|
|
}
|
|
|
|
for i := 0; i < num; i++ {
|
|
// checking if relevant config exists
|
|
cfgPath := path.Join(args.configPath, fmt.Sprintf("%s%d.yml", appName, i+1))
|
|
if _, err = os.Stat(cfgPath); os.IsNotExist(err) {
|
|
err = fmt.Errorf("not enough %s configs are generated: %w", appName, err)
|
|
return
|
|
}
|
|
|
|
// creating directory for each app
|
|
resPath := path.Join(appTypePath, fmt.Sprintf("%d", i+1))
|
|
err = createDirectoryIfNotExists(resPath)
|
|
if err != nil {
|
|
return
|
|
}
|
|
|
|
appPaths = append(appPaths, appPath{
|
|
wdPath: resPath,
|
|
binaryPath: binaryPath,
|
|
configPath: cfgPath,
|
|
logPath: path.Join(resPath, "app.log"),
|
|
debugPortNum: portNum + i + 1,
|
|
isDebug: args.isDebug,
|
|
})
|
|
}
|
|
return
|
|
}
|
|
|
|
func buildRunAll(args rootArgs, numClients, numNodes, numFileNodes uint) (err error) {
|
|
err = buildAll(args)
|
|
if err != nil {
|
|
err = fmt.Errorf("failed to build all: %w", err)
|
|
return
|
|
}
|
|
|
|
return runAll(args, numClients, numNodes, numFileNodes)
|
|
}
|
|
|
|
func runAll(args rootArgs, numClients, numNodes, numFileNodes uint) (err error) {
|
|
nodePaths, err := createAppPaths(args, args.nodeBinaryPath, "node", 6060, int(numNodes))
|
|
if err != nil {
|
|
err = fmt.Errorf("failed to create working directories for nodes: %w", err)
|
|
return
|
|
}
|
|
|
|
clientPaths, err := createAppPaths(args, args.clientBinaryPath, "client", 6070, int(numClients))
|
|
if err != nil {
|
|
err = fmt.Errorf("failed to create working directories for clients: %w", err)
|
|
return
|
|
}
|
|
|
|
filePaths, err := createAppPaths(args, args.fileNodeBinaryPath, "file", 6070, int(numFileNodes))
|
|
if err != nil {
|
|
err = fmt.Errorf("failed to create working directories for clients: %w", err)
|
|
return
|
|
}
|
|
wg := sync.WaitGroup{}
|
|
for _, nodePath := range nodePaths {
|
|
wg.Add(1)
|
|
go func(path appPath) {
|
|
err = runApp(path, &wg)
|
|
if err != nil {
|
|
log.With(zap.Error(err)).Error("running node failed with error")
|
|
}
|
|
}(nodePath)
|
|
}
|
|
for _, clientPath := range clientPaths {
|
|
wg.Add(1)
|
|
go func(path appPath) {
|
|
err = runApp(path, &wg)
|
|
if err != nil {
|
|
log.With(zap.Error(err)).Error("running client failed with error")
|
|
}
|
|
}(clientPath)
|
|
}
|
|
for _, filePath := range filePaths {
|
|
wg.Add(1)
|
|
go func(path appPath) {
|
|
err = runApp(path, &wg)
|
|
if err != nil {
|
|
log.With(zap.Error(err)).Error("running filenode failed with error")
|
|
}
|
|
}(filePath)
|
|
}
|
|
wg.Wait()
|
|
return
|
|
}
|
|
|
|
func buildAll(args rootArgs) (err error) {
|
|
err = build(args.nodePkgPath, args.nodeBinaryPath, args.nodePkgName)
|
|
if err != nil {
|
|
err = fmt.Errorf("failed to build node: %w", err)
|
|
return
|
|
}
|
|
|
|
err = build(args.clientPkgPath, args.clientBinaryPath, args.clientPkgName)
|
|
if err != nil {
|
|
err = fmt.Errorf("failed to build client: %w", err)
|
|
return
|
|
}
|
|
|
|
err = build(args.fileNodePkgPath, args.fileNodeBinaryPath, args.fileNodePkgName)
|
|
if err != nil {
|
|
err = fmt.Errorf("failed to build filenode: %w", err)
|
|
return
|
|
}
|
|
return
|
|
}
|
|
|
|
func build(dirPath, binaryPath, packageName string) (err error) {
|
|
cmd := exec.Command("go", "build", "-v", "-o", binaryPath, packageName)
|
|
cmd.Dir = dirPath
|
|
cmd.Stdout = os.Stdout
|
|
cmd.Stderr = os.Stderr
|
|
err = cmd.Start()
|
|
if err != nil {
|
|
return
|
|
}
|
|
log.With(zap.String("cmd", cmd.String())).Info("building the app")
|
|
return cmd.Wait()
|
|
}
|
|
|
|
func runApp(app appPath, wg *sync.WaitGroup) (err error) {
|
|
cmd := exec.Command(app.binaryPath, "-c", app.configPath)
|
|
cmd.Dir = app.wdPath
|
|
log := log
|
|
if app.isDebug {
|
|
log = log.With(zap.String("debug on", fmt.Sprintf("localhost:%d/debug/pprof", app.debugPortNum)))
|
|
cmd.Env = os.Environ()
|
|
cmd.Env = append(cmd.Env, fmt.Sprintf("ANYPROF=127.0.0.1:%d", app.debugPortNum))
|
|
}
|
|
|
|
file, err := os.OpenFile(app.logPath, os.O_RDWR|os.O_CREATE|os.O_APPEND, os.ModePerm)
|
|
if err != nil {
|
|
return
|
|
}
|
|
defer file.Close()
|
|
cmd.Stdout = file
|
|
cmd.Stderr = file
|
|
err = cmd.Start()
|
|
log.With(zap.String("working directory", app.wdPath), zap.String("log path", app.logPath)).Info("running the app")
|
|
if err != nil {
|
|
return
|
|
}
|
|
|
|
err = cmd.Wait()
|
|
wg.Done()
|
|
return
|
|
}
|