//go:build windows // +build windows // Copyright 2016 Amazon.com, Inc. or its affiliates. All Rights Reserved. // // Licensed under the Apache License, Version 2.0 (the "License"). You may not // use this file except in compliance with the License. A copy of the // License is located at // // http://aws.amazon.com/apache2.0/ // // or in the "license" file accompanying this file. This file 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 rundaemon implements rundaemon plugin and its configuration package rundaemon import ( "fmt" "os" "os/exec" "testing" "time" "github.com/aws/amazon-ssm-agent/agent/appconfig" "github.com/aws/amazon-ssm-agent/agent/context" "github.com/aws/amazon-ssm-agent/agent/framework/processor/executer/iohandler" iohandlermocks "github.com/aws/amazon-ssm-agent/agent/framework/processor/executer/iohandler/mock" contextmocks "github.com/aws/amazon-ssm-agent/agent/mocks/context" "github.com/aws/amazon-ssm-agent/agent/mocks/task" "github.com/stretchr/testify/assert" ) var pluginConfig = iohandler.PluginConfig{ StdoutFileName: "stdout", StderrFileName: "stderr", MaxStdoutLength: 2500, MaxStderrLength: 2500, OutputTruncatedSuffix: "cw", } func NewPlugin(context context.T, pluginConfig iohandler.PluginConfig) (*Plugin, error) { var plugin Plugin plugin.MaxStdoutLength = pluginConfig.MaxStdoutLength plugin.MaxStderrLength = pluginConfig.MaxStderrLength plugin.StdoutFileName = pluginConfig.StdoutFileName plugin.StderrFileName = pluginConfig.StderrFileName plugin.OutputTruncatedSuffix = pluginConfig.OutputTruncatedSuffix plugin.Context = context return &plugin, nil } func MockRunDaemonExecutorWithNoError(daemonInvoke *exec.Cmd) (err error) { return nil } func MockStopDaemonExecutorWithNoError(p *Plugin) { return } func MockStartDaemonHelperExecutor(p *Plugin, configuration string) error { return nil } func MockBlockWhileDaemonRunning(context context.T, pid int) error { time.Sleep(2 * time.Second) return nil } func MockIsDaemonRunningExecutor(p *Plugin) bool { return true } // Test to perform a Start followed by a Stop operation func TestSingleStartStop(t *testing.T) { context := contextmocks.NewMockDefault() cancelFlag := task.NewMockDefault() ioHandler := &iohandlermocks.MockIOHandler{} p, _ := NewPlugin(context, pluginConfig) p.Name = "TestSingleStartStop" DaemonCmdExecutor = MockRunDaemonExecutorWithNoError BlockWhileDaemonRunningExecutor = MockBlockWhileDaemonRunning StopDaemonExecutor = MockStopDaemonExecutorWithNoError StartDaemonHelperExecutor = MockStartDaemonHelperExecutor IsDaemonRunningExecutor = MockIsDaemonRunningExecutor t.Logf("Daemon starting") err := p.Start(appconfig.PowerShellPluginCommandName+" Sleep 5", "", cancelFlag, ioHandler) assert.NoError(t, err, fmt.Sprintf("Expected no error but got %v", err)) time.Sleep(2 * time.Second) t.Logf("Daemon is running") if IsDaemonRunningExecutor(p) { } else { t.Fatalf("Daemon is not running. Bail out") } time.Sleep(2 * time.Second) err = p.Stop(cancelFlag) assert.NoError(t, err, fmt.Sprintf("Expected no error but got %v", err)) if p.Process != nil { err = BlockWhileDaemonRunningExecutor(context, p.Process.Pid) assert.NoError(t, err, fmt.Sprintf("Expected no error but got %v", err)) } t.Logf("Daemon stopped") } // Test to perform Successive Starts func TestSuccessiveStarts(t *testing.T) { context := contextmocks.NewMockDefault() cancelFlag := task.NewMockDefault() ioHandler := &iohandlermocks.MockIOHandler{} p, _ := NewPlugin(context, pluginConfig) var pid int p.Name = "TestSuccessiveStarts" DaemonCmdExecutor = MockRunDaemonExecutorWithNoError BlockWhileDaemonRunningExecutor = MockBlockWhileDaemonRunning StopDaemonExecutor = MockStopDaemonExecutorWithNoError StartDaemonHelperExecutor = MockStartDaemonHelperExecutor IsDaemonRunningExecutor = MockIsDaemonRunningExecutor t.Logf("Daemon starting") p.Start(appconfig.PowerShellPluginCommandName+" Sleep 5", "", cancelFlag, ioHandler) time.Sleep(1 * time.Second) t.Logf("Daemon is running") if IsDaemonRunningExecutor(p) { } else { t.Fatalf("Daemon is not running. Bail out") } time.Sleep(1 * time.Second) if p.Process != nil { pid = p.Process.Pid } p.Start("", "", cancelFlag, ioHandler) time.Sleep(2 * time.Second) if p.Process != nil { if p.Process.Pid == pid { t.Logf("Daemon was already running") } else { t.Fatalf("Another instance of daemon started while one running") } } p.Stop(cancelFlag) BlockWhileDaemonRunning(context, pid) t.Logf("Daemon stopped") } // Test to perform Multiple Start-Stops func TestMultipleStartStop(t *testing.T) { context := contextmocks.NewMockDefault() cancelFlag := task.NewMockDefault() ioHandler := &iohandlermocks.MockIOHandler{} p, _ := NewPlugin(context, pluginConfig) p.Name = "TestMultipleStartStop" DaemonCmdExecutor = RunDaemon BlockWhileDaemonRunningExecutor = BlockWhileDaemonRunning StopDaemonExecutor = StopDaemon StartDaemonHelperExecutor = StartDaemonHelper IsDaemonRunningExecutor = IsDaemonRunning for i := 0; i < 50; i++ { t.Logf("Daemon starting") p.Start(appconfig.PowerShellPluginCommandName+" Sleep 5", "", cancelFlag, ioHandler) time.Sleep(5 * time.Second) if p.Process != nil { proc, err := os.FindProcess(p.Process.Pid) if err != nil { t.Fatalf("Daemon is not running. Bail out") return } else { t.Logf("Process pid %v", proc.Pid) } } else { t.Fatalf("Daemon is not running. Bail out") return } pid := p.Process.Pid t.Logf("Daemon stopping") p.Stop(cancelFlag) BlockWhileDaemonRunningExecutor(context, pid) } } // Test to perform stop without an associated start func TestStopWithoutStart(t *testing.T) { context := contextmocks.NewMockDefault() cancelFlag := task.NewMockDefault() p, _ := NewPlugin(context, pluginConfig) DaemonCmdExecutor = MockRunDaemonExecutorWithNoError BlockWhileDaemonRunningExecutor = MockBlockWhileDaemonRunning StopDaemonExecutor = MockStopDaemonExecutorWithNoError StartDaemonHelperExecutor = MockStartDaemonHelperExecutor IsDaemonRunningExecutor = MockIsDaemonRunningExecutor p.Name = "TestStopWithoutStart" t.Logf("Attempting to Stopping a Daemon without starting") err := p.Stop(cancelFlag) if err != nil { t.Fatalf("Stop returned errors") } return }