any-sync/util/cmd/deploy/deploy.go
2022-12-23 21:23:44 +03:00

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
}