Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Multiapp run and stop implementation for windows #1315

Merged
merged 17 commits into from
Aug 30, 2023
Merged
Show file tree
Hide file tree
Changes from 14 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 2 additions & 4 deletions cmd/run.go
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down Expand Up @@ -562,6 +558,8 @@ func executeRun(runTemplateName, runFilePath string, apps []runfileconfig.App) (

if runState.AppCMD.Command.Process != nil {
putAppProcessIDInMeta(runState)
// Attach a job object to the app process.
daprsyscall.AttachJobObjectToProcess(strconv.Itoa(os.Getpid())+"-"+utils.WindowsDaprAppProcJobName, runState.AppCMD.Command.Process)
pravinpushkar marked this conversation as resolved.
Show resolved Hide resolved
}
}

Expand Down
5 changes: 0 additions & 5 deletions cmd/stop.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,6 @@ import (
"fmt"
"os"
"path/filepath"
"runtime"

"github.com/spf13/cobra"

Expand All @@ -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)
Expand Down
1 change: 1 addition & 0 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
8 changes: 8 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -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=
Expand Down Expand Up @@ -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=
Expand Down Expand Up @@ -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=
Expand All @@ -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=
Expand Down Expand Up @@ -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=
Expand All @@ -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=
Expand Down Expand Up @@ -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=
Expand Down
51 changes: 40 additions & 11 deletions pkg/standalone/stop_windows.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,31 +14,60 @@ 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"
)

// Stop terminates the application process.
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))
eventHandle, err := windows.OpenEvent(windows.EVENT_MODIFY_STATE, false, &eventName[0])
if err != nil {
return err
}

err = windows.SetEvent(eventHandle)
return err
return handleEvent(a.CliPID)
pravinpushkar marked this conversation as resolved.
Show resolved Hide resolved
}
}
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")
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 {
jbobj, err := winjob.Open(strconv.Itoa(cliPID) + "-" + utils.WindowsDaprAppProcJobName)
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 handleEvent(cliPID)
}

func handleEvent(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
}

err = windows.SetEvent(eventHandle)
return err
}
6 changes: 6 additions & 0 deletions pkg/syscall/syscall.go
Original file line number Diff line number Diff line change
Expand Up @@ -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.
}
44 changes: 42 additions & 2 deletions pkg/syscall/syscall_windows.go
Original file line number Diff line number Diff line change
@@ -1,3 +1,6 @@
//go:build windows
// +build windows

/*
Copyright 2021 The Dapr Authors
Licensed under the Apache License, Version 2.0 (the "License");
Expand All @@ -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)

Expand All @@ -43,6 +51,38 @@ 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
}
info := windows.JOBOBJECT_EXTENDED_LIMIT_INFORMATION{
BasicLimitInformation: windows.JOBOBJECT_BASIC_LIMIT_INFORMATION{
LimitFlags: windows.JOB_OBJECT_LIMIT_KILL_ON_JOB_CLOSE,
},
}
pravinpushkar marked this conversation as resolved.
Show resolved Hide resolved
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())
}
}
10 changes: 2 additions & 8 deletions tests/e2e/standalone/run_template_test.go
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
//go:build e2e || template
//go:build !windows && (e2e || template)
// +build !windows
// +build e2e template

/*
Expand All @@ -24,7 +25,6 @@ import (
"io/ioutil"
"os"
"path/filepath"
"runtime"
"strings"
"testing"
"time"
Expand All @@ -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.
Expand Down Expand Up @@ -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) {
Expand Down
7 changes: 2 additions & 5 deletions tests/e2e/standalone/stop_with_run_template_test.go
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
//go:build e2e || template
//go:build !windows && (e2e || template)
// +build !windows
// +build e2e template

/*
Expand All @@ -22,7 +23,6 @@ import (
"encoding/json"
"fmt"
"os"
"runtime"
"testing"
"time"

Expand All @@ -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.
Expand Down
Loading
Loading