cli/cmd/run.go

229 lines
6.9 KiB
Go

// ------------------------------------------------------------
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.
// ------------------------------------------------------------
package cmd
import (
"bufio"
"fmt"
"os"
"os/signal"
"strings"
"syscall"
"time"
"github.com/dapr/cli/pkg/kubernetes"
"github.com/dapr/cli/pkg/print"
"github.com/dapr/cli/pkg/rundata"
"github.com/dapr/cli/pkg/standalone"
"github.com/google/uuid"
"github.com/spf13/cobra"
"github.com/spf13/viper"
)
var appPort int
var profilePort int
var appID string
var configFile string
var port int
var grpcPort int
var maxConcurrency int
var image string
var enableProfiling bool
var logLevel string
var protocol string
var RunCmd = &cobra.Command{
Use: "run",
Short: "Launches Dapr and your app side by side",
Args: cobra.MinimumNArgs(1),
PreRun: func(cmd *cobra.Command, args []string) {
viper.BindPFlag("placement-host", cmd.Flags().Lookup("placement-host"))
viper.BindPFlag("redis-host", cmd.Flags().Lookup("redis-host"))
},
Run: func(cmd *cobra.Command, args []string) {
uuid, err := uuid.NewRandom()
if err != nil {
print.FailureStatusEvent(os.Stdout, err.Error())
return
}
daprRunID := uuid.String()
if kubernetesMode {
output, err := kubernetes.Run(&kubernetes.RunConfig{
AppID: appID,
AppPort: appPort,
GRPCPort: grpcPort,
HTTPPort: port,
Arguments: args,
Image: image,
CodeDirectory: args[0],
})
if err != nil {
print.FailureStatusEvent(os.Stdout, err.Error())
return
}
print.InfoStatusEvent(os.Stdout, output.Message)
} else {
output, err := standalone.Run(&standalone.RunConfig{
AppID: appID,
AppPort: appPort,
HTTPPort: port,
GRPCPort: grpcPort,
ConfigFile: configFile,
Arguments: args,
EnableProfiling: enableProfiling,
ProfilePort: profilePort,
LogLevel: logLevel,
MaxConcurrency: maxConcurrency,
Protocol: protocol,
RedisHost: viper.GetString("redis-host"),
PlacementHost: viper.GetString("placement-host"),
})
if err != nil {
print.FailureStatusEvent(os.Stdout, err.Error())
return
}
var sigCh = make(chan os.Signal)
signal.Notify(sigCh, syscall.SIGTERM, syscall.SIGINT)
daprRunning := make(chan bool, 1)
appRunning := make(chan bool, 1)
daprRunCreatedTime := time.Now()
go func() {
print.InfoStatusEvent(os.Stdout, fmt.Sprintf("Starting Dapr with id %s. HTTP Port: %v. gRPC Port: %v", output.AppID, output.DaprHTTPPort, output.DaprGRPCPort))
stdErrPipe, err := output.DaprCMD.StderrPipe()
if err != nil {
print.FailureStatusEvent(os.Stdout, fmt.Sprintf("Error creating stderr for Dapr: %s", err.Error()))
os.Exit(1)
}
stdOutPipe, err := output.DaprCMD.StdoutPipe()
if err != nil {
print.FailureStatusEvent(os.Stdout, fmt.Sprintf("Error creating stdout for Dapr: %s", err.Error()))
os.Exit(1)
}
errScanner := bufio.NewScanner(stdErrPipe)
outScanner := bufio.NewScanner(stdOutPipe)
go func() {
for errScanner.Scan() {
fmt.Printf(print.Yellow(fmt.Sprintf("== DAPR == %s\n", errScanner.Text())))
}
}()
go func() {
for outScanner.Scan() {
fmt.Printf(print.Yellow(fmt.Sprintf("== DAPR == %s\n", outScanner.Text())))
}
}()
err = output.DaprCMD.Start()
if err != nil {
print.FailureStatusEvent(os.Stdout, err.Error())
os.Exit(1)
}
daprRunning <- true
}()
<-daprRunning
go func() {
stdErrPipe, err := output.AppCMD.StderrPipe()
if err != nil {
print.FailureStatusEvent(os.Stdout, fmt.Sprintf("Error creating stderr for App: %s", err.Error()))
os.Exit(1)
}
stdOutPipe, err := output.AppCMD.StdoutPipe()
if err != nil {
print.FailureStatusEvent(os.Stdout, fmt.Sprintf("Error creating stdout for App: %s", err.Error()))
os.Exit(1)
}
errScanner := bufio.NewScanner(stdErrPipe)
outScanner := bufio.NewScanner(stdOutPipe)
go func() {
for errScanner.Scan() {
fmt.Printf(print.Blue(fmt.Sprintf("== APP == %s\n", errScanner.Text())))
}
}()
go func() {
for outScanner.Scan() {
fmt.Printf(print.Blue(fmt.Sprintf("== APP == %s\n", outScanner.Text())))
}
}()
err = output.AppCMD.Start()
if err != nil {
print.FailureStatusEvent(os.Stdout, err.Error())
os.Exit(1)
}
appRunning <- true
}()
<-appRunning
rundata.AppendRunData(&rundata.RunData{
DaprRunId: daprRunID,
AppId: output.AppID,
DaprHTTPPort: output.DaprHTTPPort,
DaprGRPCPort: output.DaprGRPCPort,
AppPort: appPort,
Command: strings.Join(args, " "),
Created: daprRunCreatedTime,
PID: os.Getpid(),
})
print.SuccessStatusEvent(os.Stdout, "You're up and running! Both Dapr and your app logs will appear here.\n")
<-sigCh
print.InfoStatusEvent(os.Stdout, "\nterminated signal received: shutting down")
rundata.ClearRunData(daprRunID)
err = output.DaprCMD.Process.Kill()
if err != nil {
print.FailureStatusEvent(os.Stdout, fmt.Sprintf("Error exiting Dapr: %s", err))
} else {
print.SuccessStatusEvent(os.Stdout, "Exited Dapr successfully")
}
err = output.AppCMD.Process.Kill()
if err != nil {
print.FailureStatusEvent(os.Stdout, fmt.Sprintf("Error exiting App: %s", err))
} else {
print.SuccessStatusEvent(os.Stdout, "Exited App successfully")
}
}
},
}
func init() {
RunCmd.Flags().IntVarP(&appPort, "app-port", "", -1, "the port your application is listening on")
RunCmd.Flags().StringVarP(&appID, "app-id", "", "", "an id for your application, used for service discovery")
RunCmd.Flags().StringVarP(&configFile, "config", "", "", "Dapr configuration file")
RunCmd.Flags().IntVarP(&port, "port", "p", -1, "the HTTP port for Dapr to listen on")
RunCmd.Flags().IntVarP(&grpcPort, "grpc-port", "", -1, "the gRPC port for Dapr to listen on")
RunCmd.Flags().StringVarP(&image, "image", "", "", "the image to build the code in. input is repository/image")
RunCmd.Flags().BoolVar(&enableProfiling, "enable-profiling", false, "Enable pprof profiling via an HTTP endpoint")
RunCmd.Flags().IntVarP(&profilePort, "profile-port", "", -1, "the port for the profile server to listen on")
RunCmd.Flags().StringVarP(&logLevel, "log-level", "", "info", "Sets the log verbosity. Valid values are: debug, info, warning, error, fatal, or panic. Default is info")
RunCmd.Flags().IntVarP(&maxConcurrency, "max-concurrency", "", -1, "controls the concurrency level of the app. Default is unlimited")
RunCmd.Flags().StringVarP(&protocol, "protocol", "", "http", "tells Dapr to use HTTP or gRPC to talk to the app. Default is http")
RunCmd.Flags().String("redis-host", "localhost", "the host on which the Redis service resides")
RunCmd.Flags().String("placement-host", "localhost", "the host on which the placement service resides")
RootCmd.AddCommand(RunCmd)
}