mirror of https://github.com/dapr/cli.git
334 lines
7.9 KiB
Go
334 lines
7.9 KiB
Go
// ------------------------------------------------------------
|
|
// Copyright (c) Microsoft Corporation.
|
|
// Licensed under the MIT License.
|
|
// ------------------------------------------------------------
|
|
|
|
package standalone
|
|
|
|
import (
|
|
"errors"
|
|
"fmt"
|
|
"io/ioutil"
|
|
"os"
|
|
"os/exec"
|
|
"path"
|
|
"runtime"
|
|
"strings"
|
|
|
|
"gopkg.in/yaml.v2"
|
|
|
|
"github.com/Pallinder/sillyname-go"
|
|
"github.com/phayes/freeport"
|
|
|
|
"github.com/dapr/dapr/pkg/components"
|
|
modes "github.com/dapr/dapr/pkg/config/modes"
|
|
)
|
|
|
|
const (
|
|
componentsDirName = "components"
|
|
redisMessageBusYamlFileName = "redis_messagebus.yaml"
|
|
redisStateStoreYamlFileName = "redis.yaml"
|
|
)
|
|
|
|
// RunConfig to represent application configuration parameters
|
|
type RunConfig struct {
|
|
AppID string
|
|
AppPort int
|
|
HTTPPort int
|
|
GRPCPort int
|
|
ConfigFile string
|
|
Protocol string
|
|
Arguments []string
|
|
EnableProfiling bool
|
|
ProfilePort int
|
|
LogLevel string
|
|
MaxConcurrency int
|
|
RedisHost string
|
|
PlacementHost string
|
|
}
|
|
|
|
// RunOutput to represent the run output
|
|
type RunOutput struct {
|
|
DaprCMD *exec.Cmd
|
|
DaprHTTPPort int
|
|
DaprGRPCPort int
|
|
AppID string
|
|
AppCMD *exec.Cmd
|
|
}
|
|
|
|
type component struct {
|
|
APIVersion string `yaml:"apiVersion"`
|
|
Kind string `yaml:"kind"`
|
|
Metadata struct {
|
|
Name string `yaml:"name"`
|
|
} `yaml:"metadata"`
|
|
Spec struct {
|
|
Type string `yaml:"type"`
|
|
Metadata []componentMetadataItem `yaml:"metadata"`
|
|
} `yaml:"spec"`
|
|
}
|
|
|
|
type componentMetadataItem struct {
|
|
Name string `yaml:"name"`
|
|
Value string `yaml:"value"`
|
|
}
|
|
|
|
func getDaprCommand(appID string, daprHTTPPort int, daprGRPCPort int, appPort int, configFile, protocol string, enableProfiling bool, profilePort int, logLevel string, maxConcurrency int, placementHost string) (*exec.Cmd, int, int, error) {
|
|
if daprHTTPPort < 0 {
|
|
port, err := freeport.GetFreePort()
|
|
if err != nil {
|
|
return nil, -1, -1, err
|
|
}
|
|
|
|
daprHTTPPort = port
|
|
}
|
|
|
|
if daprGRPCPort < 0 {
|
|
grpcPort, err := freeport.GetFreePort()
|
|
if err != nil {
|
|
return nil, -1, -1, err
|
|
}
|
|
|
|
daprGRPCPort = grpcPort
|
|
}
|
|
|
|
if maxConcurrency < 1 {
|
|
maxConcurrency = -1
|
|
}
|
|
|
|
daprCMD := "daprd"
|
|
if runtime.GOOS == "windows" {
|
|
daprCMD = fmt.Sprintf("%s.exe", daprCMD)
|
|
}
|
|
|
|
args := []string{"--dapr-id", appID, "--dapr-http-port", fmt.Sprintf("%v", daprHTTPPort), "--dapr-grpc-port", fmt.Sprintf("%v", daprGRPCPort), "--log-level", logLevel, "--max-concurrency", fmt.Sprintf("%v", maxConcurrency), "--protocol", protocol}
|
|
if appPort > -1 {
|
|
args = append(args, "--app-port")
|
|
args = append(args, fmt.Sprintf("%v", appPort))
|
|
}
|
|
|
|
args = append(args, "--placement-address")
|
|
|
|
if runtime.GOOS == "windows" {
|
|
args = append(args, fmt.Sprintf("%s:6050", placementHost))
|
|
} else {
|
|
args = append(args, fmt.Sprintf("%s:50005", placementHost))
|
|
}
|
|
|
|
if configFile != "" {
|
|
args = append(args, "--config")
|
|
args = append(args, configFile)
|
|
}
|
|
|
|
if enableProfiling {
|
|
if profilePort == -1 {
|
|
pp, err := freeport.GetFreePort()
|
|
if err != nil {
|
|
return nil, -1, -1, err
|
|
}
|
|
profilePort = pp
|
|
}
|
|
|
|
args = append(args, "--enable-profiling")
|
|
args = append(args, "true")
|
|
args = append(args, "--profile-port")
|
|
args = append(args, fmt.Sprintf("%v", profilePort))
|
|
}
|
|
|
|
cmd := exec.Command(daprCMD, args...)
|
|
return cmd, daprHTTPPort, daprGRPCPort, nil
|
|
}
|
|
|
|
func getAppCommand(httpPort, grpcPort int, command string, args []string) (*exec.Cmd, error) {
|
|
cmd := exec.Command(command, args...)
|
|
cmd.Env = os.Environ()
|
|
cmd.Env = append(cmd.Env, fmt.Sprintf("DAPR_HTTP_PORT=%v", httpPort))
|
|
cmd.Env = append(cmd.Env, fmt.Sprintf("DAPR_GRPC_PORT=%v", grpcPort))
|
|
|
|
return cmd, nil
|
|
}
|
|
|
|
func absoluteComponentsDir() (string, error) {
|
|
wd, err := os.Getwd()
|
|
if err != nil {
|
|
return "", err
|
|
}
|
|
|
|
return path.Join(wd, componentsDirName), nil
|
|
}
|
|
|
|
func createRedisStateStore(redisHost string) error {
|
|
redisStore := component{
|
|
APIVersion: "dapr.io/v1alpha1",
|
|
Kind: "Component",
|
|
}
|
|
|
|
redisStore.Metadata.Name = "statestore"
|
|
redisStore.Spec.Type = "state.redis"
|
|
redisStore.Spec.Metadata = []componentMetadataItem{}
|
|
redisStore.Spec.Metadata = append(redisStore.Spec.Metadata, componentMetadataItem{
|
|
Name: "redisHost",
|
|
Value: fmt.Sprintf("%s:6379", redisHost),
|
|
})
|
|
redisStore.Spec.Metadata = append(redisStore.Spec.Metadata, componentMetadataItem{
|
|
Name: "redisPassword",
|
|
Value: "",
|
|
})
|
|
|
|
b, err := yaml.Marshal(&redisStore)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
componentsDir, err := absoluteComponentsDir()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
err = ioutil.WriteFile(path.Join(componentsDir, redisStateStoreYamlFileName), b, 0644)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
func createDirectory(dir string) error {
|
|
if _, err := os.Stat(dir); !os.IsNotExist(err) {
|
|
return nil
|
|
}
|
|
return os.Mkdir(dir, 0777)
|
|
}
|
|
|
|
func createRedisPubSub(redisHost string) error {
|
|
redisMessageBus := component{
|
|
APIVersion: "dapr.io/v1alpha1",
|
|
Kind: "Component",
|
|
}
|
|
|
|
redisMessageBus.Metadata.Name = "messagebus"
|
|
redisMessageBus.Spec.Type = "pubsub.redis"
|
|
redisMessageBus.Spec.Metadata = []componentMetadataItem{}
|
|
redisMessageBus.Spec.Metadata = append(redisMessageBus.Spec.Metadata, componentMetadataItem{
|
|
Name: "redisHost",
|
|
Value: fmt.Sprintf("%s:6379", redisHost),
|
|
})
|
|
redisMessageBus.Spec.Metadata = append(redisMessageBus.Spec.Metadata, componentMetadataItem{
|
|
Name: "redisPassword",
|
|
Value: "",
|
|
})
|
|
|
|
b, err := yaml.Marshal(&redisMessageBus)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
componentsDir, err := absoluteComponentsDir()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
err = ioutil.WriteFile(path.Join(componentsDir, redisMessageBusYamlFileName), b, 0644)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
func Run(config *RunConfig) (*RunOutput, error) {
|
|
appID := config.AppID
|
|
if appID == "" {
|
|
appID = strings.Replace(sillyname.GenerateStupidName(), " ", "-", -1)
|
|
}
|
|
|
|
dapr, err := List()
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
for _, a := range dapr {
|
|
if appID == a.AppID {
|
|
return nil, fmt.Errorf("Dapr with ID %s is already running", appID)
|
|
}
|
|
}
|
|
|
|
componentsDir, err := absoluteComponentsDir()
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
err = createDirectory(componentsDir)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
componentsLoader := components.NewStandaloneComponents(modes.StandaloneConfig{ComponentsPath: componentsDir})
|
|
components, err := componentsLoader.LoadComponents()
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
var stateStore, pubSub string
|
|
|
|
for _, component := range components {
|
|
if strings.HasPrefix(component.Spec.Type, "state") {
|
|
stateStore = component.Spec.Type
|
|
}
|
|
if strings.HasPrefix(component.Spec.Type, "pubsub") {
|
|
pubSub = component.Spec.Type
|
|
}
|
|
}
|
|
|
|
if stateStore == "" {
|
|
err = createRedisStateStore(config.RedisHost)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
}
|
|
|
|
if pubSub == "" {
|
|
err = createRedisPubSub(config.RedisHost)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
}
|
|
|
|
daprCMD, daprHTTPPort, daprGRPCPort, err := getDaprCommand(appID, config.HTTPPort, config.GRPCPort, config.AppPort, config.ConfigFile, config.Protocol, config.EnableProfiling, config.ProfilePort, config.LogLevel, config.MaxConcurrency, config.PlacementHost)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
for _, a := range dapr {
|
|
if daprHTTPPort == a.HTTPPort {
|
|
return nil, fmt.Errorf("there's already a Dapr instance running with http port %v", daprHTTPPort)
|
|
} else if daprGRPCPort == a.GRPCPort {
|
|
return nil, fmt.Errorf("there's already a Dapr instance running with gRPC port %v", daprGRPCPort)
|
|
}
|
|
}
|
|
|
|
runArgs := []string{}
|
|
argCount := len(config.Arguments)
|
|
|
|
if argCount == 0 {
|
|
return nil, errors.New("No app entrypoint given")
|
|
}
|
|
|
|
cmd := config.Arguments[0]
|
|
if len(config.Arguments) > 1 {
|
|
runArgs = config.Arguments[1:]
|
|
}
|
|
|
|
appCMD, err := getAppCommand(daprHTTPPort, daprGRPCPort, cmd, runArgs)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
return &RunOutput{
|
|
DaprCMD: daprCMD,
|
|
AppCMD: appCMD,
|
|
AppID: appID,
|
|
DaprHTTPPort: daprHTTPPort,
|
|
DaprGRPCPort: daprGRPCPort,
|
|
}, nil
|
|
}
|