mirror of https://github.com/dapr/cli.git
Multiapp run and stop implementation for windows (#1315)
* windows impl for multiapp run Signed-off-by: Pravin Pushkar <ppushkar@microsoft.com> * Uncommenting run on windows Signed-off-by: Pravin Pushkar <ppushkar@microsoft.com> * fix static checks Signed-off-by: Pravin Pushkar <ppushkar@microsoft.com> * Kill children and grand children forcefully Signed-off-by: Pravin Pushkar <ppushkar@microsoft.com> * ommiting tests for wiondows Signed-off-by: Pravin Pushkar <ppushkar@microsoft.com> * rename method Signed-off-by: Pravin Pushkar <ppushkar@microsoft.com> * shut down all processes Signed-off-by: Pravin Pushkar <ppushkar@microsoft.com> * Use job handle and named evens together to kill the processes Signed-off-by: Pravin Pushkar <ppushkar@microsoft.com> * lint fix Signed-off-by: Pravin Pushkar <ppushkar@microsoft.com> * revert wait to kill Signed-off-by: Pravin Pushkar <ppushkar@microsoft.com> * Adding E2E for windows template file run Signed-off-by: Pravin Pushkar <ppushkar@microsoft.com> * rename job name Signed-off-by: Pravin Pushkar <ppushkar@microsoft.com> * review comments Signed-off-by: Pravin Pushkar <ppushkar@microsoft.com> * build failure Signed-off-by: Pravin Pushkar <ppushkar@microsoft.com> --------- Signed-off-by: Pravin Pushkar <ppushkar@microsoft.com> Co-authored-by: Mukundan Sundararajan <65565396+mukundansundar@users.noreply.github.com>
This commit is contained in:
parent
4d586752bc
commit
6738eefe2b
|
@ -112,10 +112,6 @@ dapr run --run-file /path/to/directory
|
|||
},
|
||||
Run: func(cmd *cobra.Command, args []string) {
|
||||
if len(runFilePath) > 0 {
|
||||
if runtime.GOOS == string(windowsOsType) {
|
||||
print.FailureStatusEvent(os.Stderr, "The run command with run file is not supported on Windows")
|
||||
os.Exit(1)
|
||||
}
|
||||
runConfigFilePath, err := getRunFilePath(runFilePath)
|
||||
if err != nil {
|
||||
print.FailureStatusEvent(os.Stderr, "Failed to get run file path: %v", err)
|
||||
|
@ -562,6 +558,8 @@ func executeRun(runTemplateName, runFilePath string, apps []runfileconfig.App) (
|
|||
|
||||
if runState.AppCMD.Command.Process != nil {
|
||||
putAppProcessIDInMeta(runState)
|
||||
// Attach a windows job object to the app process.
|
||||
utils.AttachJobObjectToProcess(strconv.Itoa(os.Getpid()), runState.AppCMD.Command.Process)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -17,7 +17,6 @@ import (
|
|||
"fmt"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"runtime"
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
|
||||
|
@ -43,10 +42,6 @@ dapr stop --run-file /path/to/directory
|
|||
Run: func(cmd *cobra.Command, args []string) {
|
||||
var err error
|
||||
if len(runFilePath) > 0 {
|
||||
if runtime.GOOS == string(windowsOsType) {
|
||||
print.FailureStatusEvent(os.Stderr, "Stop command with run file is not supported on Windows")
|
||||
os.Exit(1)
|
||||
}
|
||||
runFilePath, err = getRunFilePath(runFilePath)
|
||||
if err != nil {
|
||||
print.FailureStatusEvent(os.Stderr, "Failed to get run file path: %v", err)
|
||||
|
|
1
go.mod
1
go.mod
|
@ -130,6 +130,7 @@ require (
|
|||
github.com/josharian/intern v1.0.0 // indirect
|
||||
github.com/json-iterator/go v1.1.12 // indirect
|
||||
github.com/klauspost/compress v1.16.3 // indirect
|
||||
github.com/kolesnikovae/go-winjob v1.0.0
|
||||
github.com/lann/builder v0.0.0-20180802200727-47ae307949d0 // indirect
|
||||
github.com/lann/ps v0.0.0-20150810152359-62de8c46ede0 // indirect
|
||||
github.com/lestrrat-go/blackmagic v1.0.1 // indirect
|
||||
|
|
8
go.sum
8
go.sum
|
@ -119,6 +119,7 @@ github.com/Microsoft/hcsshim v0.9.6/go.mod h1:7pLA8lDk46WKDWlVsENo92gC0XFa8rbKfy
|
|||
github.com/Microsoft/hcsshim/test v0.0.0-20201218223536-d3e5debf77da/go.mod h1:5hlzMzRKMLyo42nCZ9oml8AdTlq/0cvIaBv6tK1RehU=
|
||||
github.com/Microsoft/hcsshim/test v0.0.0-20210227013316-43a75bb4edd3/go.mod h1:mw7qgWloBUl75W/gVH3cQszUg1+gUITj7D6NY7ywVnY=
|
||||
github.com/NYTimes/gziphandler v0.0.0-20170623195520-56545f4a5d46/go.mod h1:3wb06e3pkSAbeQ52E9H9iFoQsEEwGN64994WTCIhntQ=
|
||||
github.com/NYTimes/gziphandler v1.1.1/go.mod h1:n/CVRwUEOgIxrgPvAQhUUr9oeUtvrhMomdKFjzJNB0c=
|
||||
github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU=
|
||||
github.com/Pallinder/sillyname-go v0.0.0-20130730142914-97aeae9e6ba1 h1:ReSY7H5Nf08bSzShfWAUTCthIsK08iNitWGX5YFQGXE=
|
||||
github.com/Pallinder/sillyname-go v0.0.0-20130730142914-97aeae9e6ba1/go.mod h1:cTmXjiBQMtbZnpc/yLode6SPqKmzeL7xJlD+9R9zxoc=
|
||||
|
@ -417,6 +418,7 @@ github.com/fatih/color v1.15.0 h1:kOqh6YHBtK8aywxGerMG2Eq3H6Qgoqeo13Bk2Mv/nBs=
|
|||
github.com/fatih/color v1.15.0/go.mod h1:0h5ZqXfHYED7Bhv2ZJamyIOUej9KtShiJESRwBDUSsw=
|
||||
github.com/felixge/httpsnoop v1.0.3 h1:s/nj+GCswXYzN5v2DpNMuMQYe+0DDwt5WVCU6CWBdXk=
|
||||
github.com/flowstack/go-jsonschema v0.1.1/go.mod h1:yL7fNggx1o8rm9RlgXv7hTBWxdBM0rVwpMwimd3F3N0=
|
||||
github.com/form3tech-oss/jwt-go v3.2.3+incompatible/go.mod h1:pbq4aXjuKjdthFRnoDwaVPLA+WlJuPGy+QneDUgJi2k=
|
||||
github.com/frankban/quicktest v1.11.3/go.mod h1:wRf/ReqHper53s+kmmSZizM8NamnL3IM0I9ntUbOk+k=
|
||||
github.com/frankban/quicktest v1.14.4 h1:g2rn0vABPOOXmZUj+vbmUp0lPoXEMuhTpIluN0XL9UY=
|
||||
github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
|
||||
|
@ -715,6 +717,7 @@ github.com/jmoiron/sqlx v1.3.5 h1:vFFPA71p1o5gAeqtEAwLU4dnX2napprKtHr7PYIcN3g=
|
|||
github.com/jmoiron/sqlx v1.3.5/go.mod h1:nRVWtLre0KfCLJvgxzCsLVMogSvQ1zNJtpYr2Ccp0mQ=
|
||||
github.com/joefitzgerald/rainbow-reporter v0.1.0/go.mod h1:481CNgqmVHQZzdIbN52CupLJyoVwB10FQ/IQlF1pdL8=
|
||||
github.com/jonboulle/clockwork v0.1.0/go.mod h1:Ii8DK3G1RaLaWxj9trq07+26W01tbo22gdxWY5EU2bo=
|
||||
github.com/jonboulle/clockwork v0.2.2/go.mod h1:Pkfl5aHPm1nk2H9h0bjmnJD/BcgbGXUBGnn1kMkgxc8=
|
||||
github.com/josharian/intern v1.0.0 h1:vlS4z54oSdjm0bgjRigI+G1HpF+tI+9rE5LLzOg8HmY=
|
||||
github.com/josharian/intern v1.0.0/go.mod h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFFd8Hwg//Y=
|
||||
github.com/jpillora/backoff v1.0.0/go.mod h1:J/6gKK9jxlEcS3zixgDgUAsiuZ7yrSoa/FX5e0EB2j4=
|
||||
|
@ -739,6 +742,8 @@ github.com/klauspost/compress v1.11.3/go.mod h1:aoV0uJVorq1K+umq18yTdKaF57EivdYs
|
|||
github.com/klauspost/compress v1.11.13/go.mod h1:aoV0uJVorq1K+umq18yTdKaF57EivdYsUV+/s2qKfXs=
|
||||
github.com/klauspost/compress v1.16.3 h1:XuJt9zzcnaz6a16/OU53ZjWp/v7/42WcR5t2a0PcNQY=
|
||||
github.com/klauspost/compress v1.16.3/go.mod h1:ntbaceVETuRiXiv4DpjP66DpAtAGkEQskQzEyD//IeE=
|
||||
github.com/kolesnikovae/go-winjob v1.0.0 h1:OKEtCHB3sYNAiqNwGDhf08Y6luM7C8mP+42rp1N6SeE=
|
||||
github.com/kolesnikovae/go-winjob v1.0.0/go.mod h1:k0joOLP3/NBrRmDQjPV2+oN1TPmEWt6arTNtFjVeQuM=
|
||||
github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
|
||||
github.com/konsorten/go-windows-terminal-sequences v1.0.2/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
|
||||
github.com/konsorten/go-windows-terminal-sequences v1.0.3/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
|
||||
|
@ -1049,6 +1054,7 @@ github.com/sclevine/spec v1.2.0/go.mod h1:W4J29eT/Kzv7/b9IWLB055Z+qvVC9vt0Arko24
|
|||
github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529/go.mod h1:DxrIzT+xaE7yg65j358z/aeFdxmN0P9QXhEzd20vsDc=
|
||||
github.com/seccomp/libseccomp-golang v0.9.1/go.mod h1:GbW5+tmTXfcxTToHLXlScSlAvWlF4P2Ca7zGrPiEpWo=
|
||||
github.com/seccomp/libseccomp-golang v0.9.2-0.20220502022130-f33da4d89646/go.mod h1:JA8cRccbGaA1s33RQf7Y1+q9gHmZX1yB/z9WDN1C6fg=
|
||||
github.com/segmentio/asm v1.2.0/go.mod h1:BqMnlJP91P8d+4ibuonYZw9mfnzI9HfxselHZr5aAcs=
|
||||
github.com/sergi/go-diff v1.2.0 h1:XU+rvMAioB0UC3q1MFrIQy4Vo5/4VsRDQQXHsEya6xQ=
|
||||
github.com/shirou/gopsutil v3.21.11+incompatible h1:+1+c1VGhc88SSonWP6foOcLhvnKlUeu/erjjvaPEYiI=
|
||||
github.com/shirou/gopsutil v3.21.11+incompatible/go.mod h1:5b4v6he4MtMOwMlS0TUMTu2PcXUg8+E1lC7eC3UO/RA=
|
||||
|
@ -1069,6 +1075,7 @@ github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1
|
|||
github.com/smartystreets/goconvey v0.0.0-20190330032615-68dc04aab96a/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA=
|
||||
github.com/smartystreets/goconvey v1.6.4/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA=
|
||||
github.com/soheilhy/cmux v0.1.4/go.mod h1:IM3LyeVVIOuxMH7sFAkER9+bJ4dT7Ms6E4xg4kGIyLM=
|
||||
github.com/soheilhy/cmux v0.1.5/go.mod h1:T7TcVDs9LWfQgPlPsdngu6I6QIoyIFZDDC6sNE1GqG0=
|
||||
github.com/sony/gobreaker v0.5.0 h1:dRCvqm0P490vZPmy7ppEk2qCnCieBooFJ+YoXGYB+yg=
|
||||
github.com/sony/gobreaker v0.5.0/go.mod h1:ZKptC7FHNvhBz7dN2LGjPVBz2sZJmc0/PkyDJOjmxWY=
|
||||
github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA=
|
||||
|
@ -1139,6 +1146,7 @@ github.com/tklauser/numcpus v0.4.0 h1:E53Dm1HjH1/R2/aoCtXtPgzmElmn51aOkhCFSuZq//
|
|||
github.com/tklauser/numcpus v0.4.0/go.mod h1:1+UI3pD8NW14VMwdgJNJ1ESk2UnwhAnz5hMwiKKqXCQ=
|
||||
github.com/tmc/grpc-websocket-proxy v0.0.0-20170815181823-89b8d40f7ca8/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U=
|
||||
github.com/tmc/grpc-websocket-proxy v0.0.0-20190109142713-0ad062ec5ee5/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U=
|
||||
github.com/tmc/grpc-websocket-proxy v0.0.0-20201229170055-e5319fda7802/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U=
|
||||
github.com/ugorji/go v1.1.4/go.mod h1:uQMGLiO92mf5W77hV/PUCpI3pbzQx3CRekS0kk+RGrc=
|
||||
github.com/urfave/cli v0.0.0-20171014202726-7bc6a0acffa5/go.mod h1:70zkFmudgCuE/ngEzBv17Jvp/497gISqfk5gWijbERA=
|
||||
github.com/urfave/cli v1.20.0/go.mod h1:70zkFmudgCuE/ngEzBv17Jvp/497gISqfk5gWijbERA=
|
||||
|
|
|
@ -14,10 +14,13 @@ limitations under the License.
|
|||
package standalone
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"strconv"
|
||||
"syscall"
|
||||
"time"
|
||||
|
||||
"github.com/dapr/cli/utils"
|
||||
"github.com/kolesnikovae/go-winjob"
|
||||
"golang.org/x/sys/windows"
|
||||
)
|
||||
|
||||
|
@ -25,7 +28,42 @@ import (
|
|||
func Stop(appID string, cliPIDToNoOfApps map[int]int, apps []ListOutput) error {
|
||||
for _, a := range apps {
|
||||
if a.AppID == appID {
|
||||
eventName, _ := syscall.UTF16FromString(fmt.Sprintf("dapr_cli_%v", a.CliPID))
|
||||
return setStopEvent(a.CliPID)
|
||||
}
|
||||
}
|
||||
return fmt.Errorf("couldn't find app id %s", appID)
|
||||
}
|
||||
|
||||
// StopAppsWithRunFile terminates the daprd and application processes with the given run file.
|
||||
func StopAppsWithRunFile(runTemplatePath string) error {
|
||||
apps, err := List()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
for _, a := range apps {
|
||||
if a.RunTemplatePath == runTemplatePath {
|
||||
return disposeJobHandle(a.CliPID)
|
||||
}
|
||||
}
|
||||
return fmt.Errorf("couldn't find apps with run file %q", runTemplatePath)
|
||||
}
|
||||
|
||||
func disposeJobHandle(cliPID int) error {
|
||||
jobObjectName := utils.GetJobObjectNameFromPID(strconv.Itoa(cliPID))
|
||||
jbobj, err := winjob.Open(jobObjectName)
|
||||
if err != nil {
|
||||
return fmt.Errorf("error opening job object: %w", err)
|
||||
}
|
||||
err = jbobj.TerminateWithExitCode(0)
|
||||
if err != nil {
|
||||
return fmt.Errorf("error terminating job object: %w", err)
|
||||
}
|
||||
time.Sleep(5 * time.Second)
|
||||
return setStopEvent(cliPID)
|
||||
}
|
||||
|
||||
func setStopEvent(cliPID int) error {
|
||||
eventName, _ := syscall.UTF16FromString(fmt.Sprintf("dapr_cli_%v", cliPID))
|
||||
eventHandle, err := windows.OpenEvent(windows.EVENT_MODIFY_STATE, false, &eventName[0])
|
||||
if err != nil {
|
||||
return err
|
||||
|
@ -34,11 +72,3 @@ func Stop(appID string, cliPIDToNoOfApps map[int]int, apps []ListOutput) error {
|
|||
err = windows.SetEvent(eventHandle)
|
||||
return err
|
||||
}
|
||||
}
|
||||
return fmt.Errorf("couldn't find app id %s", appID)
|
||||
}
|
||||
|
||||
// StopAppsWithRunFile terminates the daprd and application processes with the given run file.
|
||||
func StopAppsWithRunFile(runFilePath string) error {
|
||||
return errors.New("stopping apps with run template file is not supported on windows")
|
||||
}
|
||||
|
|
|
@ -40,3 +40,9 @@ func CreateProcessGroupID() {
|
|||
print.WarningStatusEvent(os.Stdout, "Failed to create process group id: %s", err.Error())
|
||||
}
|
||||
}
|
||||
|
||||
// AttachJobObjectToProcess attaches the process to a job object.
|
||||
func AttachJobObjectToProcess(jobName string, proc *os.Process) {
|
||||
// This is a no-op on Linux/Mac.
|
||||
// Instead, we use process group ID to kill all the processes.
|
||||
}
|
||||
|
|
|
@ -1,3 +1,6 @@
|
|||
//go:build windows
|
||||
// +build windows
|
||||
|
||||
/*
|
||||
Copyright 2021 The Dapr Authors
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
|
@ -18,12 +21,17 @@ import (
|
|||
"os"
|
||||
"os/signal"
|
||||
"syscall"
|
||||
"unsafe"
|
||||
|
||||
"golang.org/x/sys/windows"
|
||||
|
||||
"github.com/dapr/cli/pkg/print"
|
||||
"github.com/kolesnikovae/go-winjob"
|
||||
"github.com/kolesnikovae/go-winjob/jobapi"
|
||||
)
|
||||
|
||||
var jbObj *winjob.JobObject
|
||||
|
||||
func SetupShutdownNotify(sigCh chan os.Signal) {
|
||||
signal.Notify(sigCh, syscall.SIGTERM, syscall.SIGINT)
|
||||
|
||||
|
@ -43,6 +51,41 @@ func SetupShutdownNotify(sigCh chan os.Signal) {
|
|||
|
||||
// CreateProcessGroupID creates a process group ID for the current process.
|
||||
func CreateProcessGroupID() {
|
||||
// No-op on Windows
|
||||
print.WarningStatusEvent(os.Stdout, "Creating process group id is not implemented on Windows")
|
||||
// This is a no-op on windows.
|
||||
// Process group ID is not used for killing all the processes on windows.
|
||||
// Instead, we use combination of named event and job object to kill all the processes.
|
||||
}
|
||||
|
||||
// AttachJobObjectToProcess attaches the process to a job object.
|
||||
// It creates the job object if it doesn't exist.
|
||||
func AttachJobObjectToProcess(jobName string, proc *os.Process) {
|
||||
if jbObj != nil {
|
||||
err := jbObj.Assign(proc)
|
||||
if err != nil {
|
||||
print.WarningStatusEvent(os.Stdout, "failed to assign process to job object: %s", err.Error())
|
||||
}
|
||||
return
|
||||
}
|
||||
jbObj, err := winjob.Create(jobName)
|
||||
if err != nil {
|
||||
print.WarningStatusEvent(os.Stdout, "failed to create job object: %s", err.Error())
|
||||
return
|
||||
}
|
||||
// Below lines control the relation between Job object and processes attached to it.
|
||||
// By passing JOB_OBJECT_LIMIT_KILL_ON_JOB_CLOSE flag, it will make sure that when
|
||||
// job object is closed all the processed must also be exited.
|
||||
info := windows.JOBOBJECT_EXTENDED_LIMIT_INFORMATION{
|
||||
BasicLimitInformation: windows.JOBOBJECT_BASIC_LIMIT_INFORMATION{
|
||||
LimitFlags: windows.JOB_OBJECT_LIMIT_KILL_ON_JOB_CLOSE,
|
||||
},
|
||||
}
|
||||
err = jobapi.SetInformationJobObject(jbObj.Handle, jobapi.JobObjectExtendedLimitInformation, unsafe.Pointer(&info), uint32(unsafe.Sizeof(info)))
|
||||
if err != nil {
|
||||
print.WarningStatusEvent(os.Stdout, "failed to set job object info: %s", err.Error())
|
||||
return
|
||||
}
|
||||
err = jbObj.Assign(proc)
|
||||
if err != nil {
|
||||
print.WarningStatusEvent(os.Stdout, "failed to assign process to job object: %s", err.Error())
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
//go:build e2e || template
|
||||
//go:build !windows && (e2e || template)
|
||||
// +build !windows
|
||||
// +build e2e template
|
||||
|
||||
/*
|
||||
|
@ -24,7 +25,6 @@ import (
|
|||
"io/ioutil"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"runtime"
|
||||
"strings"
|
||||
"testing"
|
||||
"time"
|
||||
|
@ -43,9 +43,6 @@ type AppTestOutput struct {
|
|||
}
|
||||
|
||||
func TestRunWithTemplateFile(t *testing.T) {
|
||||
if runtime.GOOS == "windows" {
|
||||
t.Skip("Skipping test on Windows")
|
||||
}
|
||||
ensureDaprInstallation(t)
|
||||
t.Cleanup(func() {
|
||||
// remove dapr installation after all tests in this function.
|
||||
|
@ -373,9 +370,6 @@ func TestRunWithTemplateFile(t *testing.T) {
|
|||
}
|
||||
|
||||
func TestRunTemplateFileWithoutDaprInit(t *testing.T) {
|
||||
if runtime.GOOS == "windows" {
|
||||
t.Skip("Skipping test on Windows")
|
||||
}
|
||||
// remove any dapr installation before this test.
|
||||
must(t, cmdUninstall, "failed to uninstall Dapr")
|
||||
t.Run("valid template file without dapr init", func(t *testing.T) {
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
//go:build e2e || template
|
||||
//go:build !windows && (e2e || template)
|
||||
// +build !windows
|
||||
// +build e2e template
|
||||
|
||||
/*
|
||||
|
@ -22,7 +23,6 @@ import (
|
|||
"encoding/json"
|
||||
"fmt"
|
||||
"os"
|
||||
"runtime"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
|
@ -31,9 +31,6 @@ import (
|
|||
)
|
||||
|
||||
func TestStopAppsStartedWithRunTemplate(t *testing.T) {
|
||||
if runtime.GOOS == "windows" {
|
||||
t.Skip("Skipping test on windows")
|
||||
}
|
||||
ensureDaprInstallation(t)
|
||||
t.Cleanup(func() {
|
||||
// remove dapr installation after all tests in this function.
|
||||
|
|
|
@ -0,0 +1,144 @@
|
|||
//go:build windows && (e2e || template)
|
||||
// +build windows
|
||||
// +build e2e template
|
||||
|
||||
/*
|
||||
Copyright 2023 The Dapr Authors
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package standalone_test
|
||||
|
||||
import (
|
||||
"strings"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
type AppTestOutput struct {
|
||||
appID string
|
||||
appLogContents []string
|
||||
daprdLogContent []string
|
||||
baseLogDirPath string
|
||||
appLogDoesNotExist bool
|
||||
daprdLogFileDoesNotExist bool
|
||||
}
|
||||
|
||||
func TestRunWithTemplateFile(t *testing.T) {
|
||||
ensureDaprInstallation(t)
|
||||
t.Cleanup(func() {
|
||||
// remove dapr installation after all tests in this function.
|
||||
must(t, cmdUninstall, "failed to uninstall Dapr")
|
||||
})
|
||||
// These tests are dependent on run template files in ../testdata/run-template-files folder.
|
||||
t.Run("valid template file", func(t *testing.T) {
|
||||
runFilePath := "../testdata/run-template-files/dapr.yaml"
|
||||
go startAppsWithValidRunTemplate(t, runFilePath)
|
||||
time.Sleep(10 * time.Second)
|
||||
output, err := cmdStopWithRunTemplate(runFilePath)
|
||||
assert.NoError(t, err, "failed to stop apps started with run template")
|
||||
assert.Contains(t, output, "Dapr and app processes stopped successfully")
|
||||
time.Sleep(5 * time.Second)
|
||||
})
|
||||
|
||||
t.Run("valid template file with App output written to only file", func(t *testing.T) {
|
||||
runFilePath := "../testdata/run-template-files/app_output_to_file_and_console.yaml"
|
||||
go startAppsWithAppLogDestFile(t, runFilePath)
|
||||
time.Sleep(10 * time.Second)
|
||||
output, err := cmdStopWithRunTemplate(runFilePath)
|
||||
assert.NoError(t, err, "failed to stop apps started with run template")
|
||||
assert.Contains(t, output, "Dapr and app processes stopped successfully")
|
||||
time.Sleep(5 * time.Second)
|
||||
})
|
||||
|
||||
t.Run("valid template file with App output written to only console", func(t *testing.T) {
|
||||
runFilePath := "../testdata/run-template-files/app_output_to_only_console.yaml"
|
||||
go startAppsWithAppLogDestConsole(t, runFilePath)
|
||||
time.Sleep(10 * time.Second)
|
||||
output, err := cmdStopWithRunTemplate(runFilePath)
|
||||
assert.NoError(t, err, "failed to stop apps started with run template")
|
||||
assert.Contains(t, output, "Dapr and app processes stopped successfully")
|
||||
time.Sleep(5 * time.Second)
|
||||
})
|
||||
}
|
||||
|
||||
func startAppsWithValidRunTemplate(t *testing.T, file string) {
|
||||
args := []string{
|
||||
"-f", file,
|
||||
}
|
||||
output, err := cmdRun("", args...)
|
||||
t.Logf(output)
|
||||
require.NoError(t, err, "run failed")
|
||||
lines := strings.Split(output, "\n")
|
||||
assert.GreaterOrEqual(t, len(lines), 7, "expected at least 7 lines in output of starting two apps")
|
||||
assert.Contains(t, lines[0], "This is a preview feature and subject to change in future releases.")
|
||||
assert.Contains(t, lines[1], "Validating config and starting app \"processor\"")
|
||||
assert.Contains(t, lines[2], "Started Dapr with app id \"processor\". HTTP Port: 3510.")
|
||||
assert.Contains(t, lines[3], "Writing log files to directory")
|
||||
assert.Contains(t, lines[3], "tests\\apps\\processor\\.dapr\\logs")
|
||||
assert.Contains(t, lines[4], "Validating config and starting app \"emit-metrics\"")
|
||||
assert.Contains(t, lines[5], "Started Dapr with app id \"emit-metrics\". HTTP Port: 3511.")
|
||||
assert.Contains(t, lines[6], "Writing log files to directory")
|
||||
assert.Contains(t, lines[6], "tests\\apps\\emit-metrics\\.dapr\\logs")
|
||||
}
|
||||
|
||||
func startAppsWithAppLogDestFile(t *testing.T, file string) {
|
||||
args := []string{
|
||||
"-f", file,
|
||||
}
|
||||
output, err := cmdRun("", args...)
|
||||
t.Logf(output)
|
||||
require.NoError(t, err, "run failed")
|
||||
|
||||
// App logs for processor app should not be printed to console and only written to file.
|
||||
assert.NotContains(t, output, "== APP - processor")
|
||||
|
||||
// Daprd logs for processor app should only be printed to console and not written to file.
|
||||
assert.Contains(t, output, "msg=\"All outstanding components processed\" app_id=processor")
|
||||
|
||||
// App logs for emit-metrics app should be printed to console and written to file.
|
||||
assert.Contains(t, output, "== APP - emit-metrics")
|
||||
|
||||
// Daprd logs for emit-metrics app should only be written to file.
|
||||
assert.NotContains(t, output, "msg=\"All outstanding components processed\" app_id=emit-metrics")
|
||||
|
||||
assert.Contains(t, output, "Received signal to stop Dapr and app processes. Shutting down Dapr and app processes.")
|
||||
|
||||
}
|
||||
|
||||
func startAppsWithAppLogDestConsole(t *testing.T, file string) {
|
||||
args := []string{
|
||||
"-f", file,
|
||||
}
|
||||
output, err := cmdRun("", args...)
|
||||
t.Logf(output)
|
||||
require.NoError(t, err, "run failed")
|
||||
|
||||
// App logs for processor app should be printed to console.
|
||||
assert.Contains(t, output, "== APP - processor")
|
||||
|
||||
// Daprd logs for processor app should only be written to file.
|
||||
assert.NotContains(t, output, "msg=\"All outstanding components processed\" app_id=processor")
|
||||
|
||||
// App logs for emit-metrics app should be printed to console.
|
||||
assert.Contains(t, output, "== APP - emit-metrics")
|
||||
|
||||
// Daprd logs for emit-metrics app should only be written to file.
|
||||
assert.NotContains(t, output, "msg=\"All outstanding components processed\" app_id=emit-metrics")
|
||||
|
||||
assert.Contains(t, output, "Received signal to stop Dapr and app processes. Shutting down Dapr and app processes.")
|
||||
|
||||
}
|
|
@ -0,0 +1,14 @@
|
|||
version: 1
|
||||
apps:
|
||||
- appDirPath: ../../../apps/processor/
|
||||
appPort: 9081
|
||||
daprHTTPPort: 3510
|
||||
command: ["go","run", "app.go"]
|
||||
appLogDestination: console
|
||||
- appID: emit-metrics
|
||||
appDirPath: ../../../apps/emit-metrics/
|
||||
daprHTTPPort: 3511
|
||||
env:
|
||||
DAPR_HOST_ADD: localhost
|
||||
command: ["go","run", "app.go"]
|
||||
appLogDestination: console
|
|
@ -29,6 +29,7 @@ import (
|
|||
"time"
|
||||
|
||||
"github.com/dapr/cli/pkg/print"
|
||||
daprsyscall "github.com/dapr/cli/pkg/syscall"
|
||||
|
||||
"github.com/docker/docker/client"
|
||||
"github.com/gocarina/gocsv"
|
||||
|
@ -51,6 +52,9 @@ const (
|
|||
|
||||
// DefaultAppChannelAddress is the default local network address that user application listen on.
|
||||
DefaultAppChannelAddress = "127.0.0.1"
|
||||
|
||||
// windowsDaprAppProcJobName is the name of the Windows job object that is used to manage the Daprized app's processes on windows.
|
||||
windowsDaprAppProcJobName = "dapr-app-process-job"
|
||||
)
|
||||
|
||||
// IsValidContainerRuntime checks if the input is a valid container runtime.
|
||||
|
@ -408,3 +412,14 @@ func FindFileInDir(dirPath, fileName string) (string, error) {
|
|||
func SanitizeDir(destDir string) string {
|
||||
return strings.ReplaceAll(destDir, "'", "''")
|
||||
}
|
||||
|
||||
// Attach Job object to App Process.
|
||||
func AttachJobObjectToProcess(pid string, proc *os.Process) {
|
||||
// Attach a job object to the app process.
|
||||
daprsyscall.AttachJobObjectToProcess(GetJobObjectNameFromPID(pid), proc)
|
||||
}
|
||||
|
||||
// GetJobObjectNameFromPID returns the name of the Windows job object that is used to manage the Daprized app's processes on windows.
|
||||
func GetJobObjectNameFromPID(pid string) string {
|
||||
return pid + "-" + windowsDaprAppProcJobName
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue