cli/tests/e2e/standalone/run_template_test.go

432 lines
17 KiB
Go

//go:build !windows && (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 (
"context"
"fmt"
"io/ioutil"
"path/filepath"
"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) {
cleanUpLogs()
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("invalid template file wrong emit metrics app run", func(t *testing.T) {
runFilePath := "../testdata/run-template-files/wrong_emit_metrics_app_dapr.yaml"
t.Cleanup(func() {
// assumption in the test is that there is only one set of app and daprd logs in the logs directory.
cleanUpLogs()
stopAllApps(t, runFilePath)
})
args := []string{
"-f", runFilePath,
}
ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
defer cancel()
output, err := cmdRunWithContext(ctx, "", args...)
t.Logf(output)
require.NoError(t, err, "run failed")
// Deterministic output for template file, so we can assert line by line
lines := strings.Split(output, "\n")
assert.GreaterOrEqual(t, len(lines), 4, "expected at least 4 lines in output of starting two apps")
assert.Contains(t, lines[1], "Started Dapr with app id \"processor\". HTTP Port: 3510.")
assert.Contains(t, lines[2], "Writing log files to directory")
assert.Contains(t, lines[2], "tests/apps/processor/.dapr/logs")
assert.Contains(t, lines[4], "Started Dapr with app id \"emit-metrics\". HTTP Port: 3511.")
assert.Contains(t, lines[5], "Writing log files to directory")
assert.Contains(t, lines[5], "tests/apps/emit-metrics/.dapr/logs")
assert.Contains(t, output, "Received signal to stop Dapr and app processes. Shutting down Dapr and app processes.")
appTestOutput := AppTestOutput{
appID: "processor",
baseLogDirPath: "../../apps/processor/.dapr/logs",
daprdLogContent: []string{
"HTTP server is running on port 3510",
"You're up and running! Dapr logs will appear here.",
},
}
assertLogOutputForRunTemplateExec(t, appTestOutput)
appTestOutput = AppTestOutput{
appID: "emit-metrics",
baseLogDirPath: "../../apps/emit-metrics/.dapr/logs",
appLogContents: []string{
"stat wrongappname.go: no such file or directory",
"The App process exited with error code: exit status 1",
},
daprdLogContent: []string{
"termination signal received: shutting down",
"Exited Dapr successfully",
},
}
assertLogOutputForRunTemplateExec(t, appTestOutput)
})
t.Run("valid template file", func(t *testing.T) {
runFilePath := "../testdata/run-template-files/dapr.yaml"
t.Cleanup(func() {
// assumption in the test is that there is only one set of app and daprd logs in the logs directory.
cleanUpLogs()
stopAllApps(t, runFilePath)
})
args := []string{
"-f", runFilePath,
}
ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
defer cancel()
output, err := cmdRunWithContext(ctx, "", args...)
t.Logf(output)
require.NoError(t, err, "run failed")
// Deterministic output for template file, so we can assert line by line
lines := strings.Split(output, "\n")
assert.GreaterOrEqual(t, len(lines), 6, "expected at least 6 lines in output of starting two apps")
assert.Contains(t, lines[0], "Validating config and starting app \"processor\"")
assert.Contains(t, lines[1], "Started Dapr with app id \"processor\". HTTP Port: 3510.")
assert.Contains(t, lines[2], "Writing log files to directory")
assert.Contains(t, lines[2], "tests/apps/processor/.dapr/logs")
assert.Contains(t, lines[3], "Validating config and starting app \"emit-metrics\"")
assert.Contains(t, lines[4], "Started Dapr with app id \"emit-metrics\". HTTP Port: 3511.")
assert.Contains(t, lines[5], "Writing log files to directory")
assert.Contains(t, lines[5], "tests/apps/emit-metrics/.dapr/logs")
assert.Contains(t, output, "Received signal to stop Dapr and app processes. Shutting down Dapr and app processes.")
appTestOutput := AppTestOutput{
appID: "processor",
baseLogDirPath: "../../apps/processor/.dapr/logs",
appLogContents: []string{
"Received metrics: {1}",
},
daprdLogContent: []string{
"HTTP server is running on port 3510",
"You're up and running! Dapr logs will appear here.",
},
}
assertLogOutputForRunTemplateExec(t, appTestOutput)
appTestOutput = AppTestOutput{
appID: "emit-metrics",
baseLogDirPath: "../../apps/emit-metrics/.dapr/logs",
appLogContents: []string{
"DAPR_HTTP_PORT set to 3511",
"DAPR_HOST_ADD set to localhost",
"Metrics with ID 1 sent",
},
daprdLogContent: []string{
"termination signal received: shutting down",
"Exited Dapr successfully",
"Exited App successfully",
},
}
assertLogOutputForRunTemplateExec(t, appTestOutput)
})
t.Run("invalid template file env var not set", func(t *testing.T) {
runFilePath := "../testdata/run-template-files/env_var_not_set_dapr.yaml"
t.Cleanup(func() {
// assumption in the test is that there is only one set of app and daprd logs in the logs directory.
cleanUpLogs()
stopAllApps(t, runFilePath)
})
args := []string{
"-f", runFilePath,
}
ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
defer cancel()
output, err := cmdRunWithContext(ctx, "", args...)
t.Logf(output)
require.NoError(t, err, "run failed")
// Deterministic output for template file, so we can assert line by line
lines := strings.Split(output, "\n")
assert.GreaterOrEqual(t, len(lines), 6, "expected at least 6 lines in output of starting two apps")
assert.Contains(t, lines[1], "Started Dapr with app id \"processor\". HTTP Port: 3510.")
assert.Contains(t, lines[2], "Writing log files to directory")
assert.Contains(t, lines[2], "tests/apps/processor/.dapr/logs")
assert.Contains(t, lines[4], "Started Dapr with app id \"emit-metrics\". HTTP Port: 3511.")
assert.Contains(t, lines[5], "Writing log files to directory")
assert.Contains(t, lines[5], "tests/apps/emit-metrics/.dapr/logs")
assert.Contains(t, output, "Received signal to stop Dapr and app processes. Shutting down Dapr and app processes.")
appTestOutput := AppTestOutput{
appID: "processor",
baseLogDirPath: "../../apps/processor/.dapr/logs",
daprdLogContent: []string{
"HTTP server is running on port 3510",
"You're up and running! Dapr logs will appear here.",
},
}
assertLogOutputForRunTemplateExec(t, appTestOutput)
appTestOutput = AppTestOutput{
appID: "emit-metrics",
baseLogDirPath: "../../apps/emit-metrics/.dapr/logs",
appLogContents: []string{
"DAPR_HTTP_PORT set to 3511",
"exit status 1",
"Error exiting App: exit status 1",
},
daprdLogContent: []string{
"termination signal received: shutting down",
"Exited Dapr successfully",
},
}
assertLogOutputForRunTemplateExec(t, appTestOutput)
})
t.Run("valid template file no app command", func(t *testing.T) {
runFilePath := "../testdata/run-template-files/no_app_command.yaml"
t.Cleanup(func() {
// assumption in the test is that there is only one set of app and daprd logs in the logs directory.
cleanUpLogs()
stopAllApps(t, runFilePath)
})
args := []string{
"-f", runFilePath,
}
ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
defer cancel()
output, err := cmdRunWithContext(ctx, "", args...)
t.Logf(output)
require.NoError(t, err, "run failed")
// Deterministic output for template file, so we can assert line by line
lines := strings.Split(output, "\n")
assert.GreaterOrEqual(t, len(lines), 7, "expected at least 7 lines in output of starting two apps with one app not having a command")
assert.Contains(t, lines[1], "Started Dapr with app id \"processor\". HTTP Port: 3510.")
assert.Contains(t, lines[2], "Writing log files to directory")
assert.Contains(t, lines[2], "tests/apps/processor/.dapr/logs")
assert.Contains(t, lines[4], "No application command found for app \"emit-metrics\" present in")
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")
assert.Contains(t, output, "Received signal to stop Dapr and app processes. Shutting down Dapr and app processes.")
appTestOutput := AppTestOutput{
appID: "processor",
baseLogDirPath: "../../apps/processor/.dapr/logs",
appLogContents: []string{
"Starting server in port 9081...",
"termination signal received: shutting down",
},
daprdLogContent: []string{
"HTTP server is running on port 3510",
"You're up and running! Dapr logs will appear here.",
},
}
assertLogOutputForRunTemplateExec(t, appTestOutput)
appTestOutput = AppTestOutput{
appID: "emit-metrics",
baseLogDirPath: "../../apps/emit-metrics/.dapr/logs",
appLogDoesNotExist: true,
daprdLogContent: []string{
"termination signal received: shutting down",
"Exited Dapr successfully",
},
}
assertLogOutputForRunTemplateExec(t, appTestOutput)
})
t.Run("valid template file empty app command", func(t *testing.T) {
runFilePath := "../testdata/run-template-files/empty_app_command.yaml"
t.Cleanup(func() {
// assumption in the test is that there is only one set of app and daprd logs in the logs directory.
cleanUpLogs()
stopAllApps(t, runFilePath)
})
args := []string{
"-f", runFilePath,
}
ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
defer cancel()
output, err := cmdRunWithContext(ctx, "", args...)
t.Logf(output)
require.Error(t, err, "run must fail")
// Deterministic output for template file, so we can assert line by line
lines := strings.Split(output, "\n")
assert.GreaterOrEqual(t, len(lines), 5, "expected at least 5 lines in output of starting two apps with last app having an empty command")
assert.Contains(t, lines[1], "Started Dapr with app id \"processor\". HTTP Port: 3510.")
assert.Contains(t, lines[2], "Writing log files to directory")
assert.Contains(t, lines[2], "tests/apps/processor/.dapr/logs")
assert.Contains(t, lines[4], "Error starting Dapr and app (\"emit-metrics\"): exec: no command")
appTestOutput := AppTestOutput{
appID: "processor",
baseLogDirPath: "../../apps/processor/.dapr/logs",
appLogContents: []string{
"Starting server in port 9081...",
"termination signal received: shutting down",
},
daprdLogContent: []string{
"HTTP server is running on port 3510",
"You're up and running! Dapr logs will appear here.",
},
}
assertLogOutputForRunTemplateExec(t, appTestOutput)
appTestOutput = AppTestOutput{
appID: "emit-metrics",
baseLogDirPath: "../../apps/emit-metrics/.dapr/logs",
appLogContents: []string{
"Error starting app process: exec: no command",
},
daprdLogContent: []string{
"Error starting Dapr and app (\"emit-metrics\"): exec: no command",
},
}
assertLogOutputForRunTemplateExec(t, appTestOutput)
})
t.Run("valid template file with app/daprd log destinations", func(t *testing.T) {
runFilePath := "../testdata/run-template-files/app_output_to_file_and_console.yaml"
t.Cleanup(func() {
// assumption in the test is that there is only one set of app and daprd logs in the logs directory.
cleanUpLogs()
stopAllApps(t, runFilePath)
})
args := []string{
"-f", runFilePath,
}
ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
defer cancel()
output, err := cmdRunWithContext(ctx, "", 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.")
appTestOutput := AppTestOutput{
appID: "processor",
baseLogDirPath: "../../apps/processor/.dapr/logs",
appLogContents: []string{
"Received metrics: {1}",
},
daprdLogContent: []string{},
daprdLogFileDoesNotExist: true,
}
assertLogOutputForRunTemplateExec(t, appTestOutput)
appTestOutput = AppTestOutput{
appID: "emit-metrics",
baseLogDirPath: "../../apps/emit-metrics/.dapr/logs",
appLogContents: []string{
"DAPR_HTTP_PORT set to 3511",
"DAPR_HOST_ADD set to localhost",
"Metrics with ID 1 sent",
},
daprdLogContent: []string{
"termination signal received: shutting down",
"Exited Dapr successfully",
"Exited App successfully",
},
}
assertLogOutputForRunTemplateExec(t, appTestOutput)
})
}
func TestRunTemplateFileWithoutDaprInit(t *testing.T) {
// 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) {
t.Cleanup(func() {
// assumption in the test is that there is only one set of app and daprd logs in the logs directory.
cleanUpLogs()
})
args := []string{
"-f", "../testdata/run-template-files/no_app_command.yaml",
}
ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
defer cancel()
output, err := cmdRunWithContext(ctx, "", args...)
t.Logf(output)
require.Error(t, err, "run must fail")
assert.Contains(t, output, "Error starting Dapr and app (\"processor\"): fork/exec")
assert.Contains(t, output, "daprd: no such file or directory")
})
}
func assertLogOutputForRunTemplateExec(t *testing.T, appTestOutput AppTestOutput) {
// assumption in the test is that there is only one set of app and daprd logs in the logs directory.
// This is true for the tests in this file.
if !appTestOutput.daprdLogFileDoesNotExist {
daprdLogFileName, err := lookUpFileFullName(appTestOutput.baseLogDirPath, "daprd")
require.NoError(t, err, "failed to find daprd log file")
daprdLogPath := filepath.Join(appTestOutput.baseLogDirPath, daprdLogFileName)
readAndAssertLogFileContents(t, daprdLogPath, appTestOutput.daprdLogContent)
}
if appTestOutput.appLogDoesNotExist {
return
}
appLogFileName, err := lookUpFileFullName(appTestOutput.baseLogDirPath, "app")
require.NoError(t, err, "failed to find app log file")
appLogPath := filepath.Join(appTestOutput.baseLogDirPath, appLogFileName)
readAndAssertLogFileContents(t, appLogPath, appTestOutput.appLogContents)
}
func readAndAssertLogFileContents(t *testing.T, logFilePath string, expectedContent []string) {
assert.FileExists(t, logFilePath, "log file %s must exist", logFilePath)
fileContents, err := ioutil.ReadFile(logFilePath)
assert.NoError(t, err, "failed to read %s log", logFilePath)
contentString := string(fileContents)
for _, line := range expectedContent {
assert.Contains(t, contentString, line, "expected logline to be present")
}
}
// lookUpFileFullName looks up the full name of the first file with partial name match in the directory.
func lookUpFileFullName(dirPath, partialFilename string) (string, error) {
// Look for the file in the current directory
files, err := ioutil.ReadDir(dirPath)
if err != nil {
return "", err
}
for _, file := range files {
if strings.Contains(file.Name(), partialFilename) {
return file.Name(), nil
}
}
return "", fmt.Errorf("failed to find file with partial name %s in directory %s", partialFilename, dirPath)
}
func stopAllApps(t *testing.T, runfile string) {
_, err := cmdStopWithRunTemplate(runfile)
require.NoError(t, err, "failed to stop apps")
time.Sleep(5 * time.Second)
}