Add E2E for Run template (#1170)

* refactor run into functions

Signed-off-by: Mukundan Sundararajan <65565396+mukundansundar@users.noreply.github.com>

* renamed Apps struct as App

Signed-off-by: Mukundan Sundararajan <65565396+mukundansundar@users.noreply.github.com>

* redirect logs to files. set informational logs. set command dirs

Signed-off-by: Mukundan Sundararajan <65565396+mukundansundar@users.noreply.github.com>

* fix linter errors

Signed-off-by: Mukundan Sundararajan <65565396+mukundansundar@users.noreply.github.com>

* fix e2e test on non-existent resources-path

Signed-off-by: Mukundan Sundararajan <65565396+mukundansundar@users.noreply.github.com>

* more fixes

Signed-off-by: Mukundan Sundararajan <65565396+mukundansundar@users.noreply.github.com>

* fix merge conflict

Signed-off-by: Mukundan Sundararajan <65565396+mukundansundar@users.noreply.github.com>

* set default ports for zero values

Signed-off-by: Mukundan Sundararajan <65565396+mukundansundar@users.noreply.github.com>

* fix linter errors

Signed-off-by: Mukundan Sundararajan <65565396+mukundansundar@users.noreply.github.com>

* add more details in error, fix logs in run -f

Signed-off-by: Mukundan Sundararajan <65565396+mukundansundar@users.noreply.github.com>

* address review comments

Signed-off-by: Mukundan Sundararajan <65565396+mukundansundar@users.noreply.github.com>

* address review comments

Signed-off-by: Mukundan Sundararajan <65565396+mukundansundar@users.noreply.github.com>

* add more fixes in template run based on e2e tests

Signed-off-by: Mukundan Sundararajan <65565396+mukundansundar@users.noreply.github.com>

* add basic happy path e2e for dapr run -f

Signed-off-by: Mukundan Sundararajan <65565396+mukundansundar@users.noreply.github.com>

* add positive and negative e2e tests for run template

Signed-off-by: Mukundan Sundararajan <65565396+mukundansundar@users.noreply.github.com>

* address review comments

Signed-off-by: Mukundan Sundararajan <65565396+mukundansundar@users.noreply.github.com>

Signed-off-by: Mukundan Sundararajan <65565396+mukundansundar@users.noreply.github.com>
This commit is contained in:
Mukundan Sundararajan 2023-01-25 00:28:05 +05:30 committed by GitHub
parent 14c04f9a87
commit 17dbe5bb13
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
26 changed files with 694 additions and 33 deletions

2
.gitignore vendored
View File

@ -24,6 +24,8 @@ cli
# CLI's auto-generated components directory
**/components
# Auto generated logs dir inside .dapr directory
**/.dapr/logs
test_output.json

View File

@ -153,7 +153,7 @@ test: test-deps
################################################################################
.PHONY: test-e2e-k8s
test-e2e-k8s: test-deps
gotestsum --jsonfile $(TEST_OUTPUT_FILE) --format standard-verbose -- -timeout 20m -count=1 -tags=e2e ./tests/e2e/kubernetes/...
gotestsum --jsonfile $(TEST_OUTPUT_FILE) --format standard-verbose -- -timeout 20m -count=1 -tags=e2e ./tests/e2e/kubernetes/...
################################################################################
# Build, E2E Tests for Kubernetes #
@ -175,6 +175,13 @@ test-e2e-upgrade: test-deps
e2e-build-run-upgrade: build test-e2e-upgrade
################################################################################
# E2E Tests for Self-Hosted Template exec #
################################################################################
.PHONY: test-e2e-sh-template
test-e2e-sh-template: test-deps
gotestsum --jsonfile $(TEST_OUTPUT_FILE) --format standard-verbose -- -timeout $(E2E_SH_TEST_TIMEOUT) -count=1 -tags=template ./tests/e2e/standalone/...
################################################################################
# E2E Tests for Self-Hosted #
################################################################################

View File

@ -474,7 +474,7 @@ func executeRun(runFilePath string, apps []runfileconfig.App) (bool, error) {
runConfig := app.RunConfig
err = app.CreateDaprdLogFile()
if err != nil {
print.StatusEvent(os.Stderr, print.LogFailure, "Error getting log file for app %q present in %s: %s", runConfig.AppID, runFilePath, err.Error())
print.StatusEvent(os.Stderr, print.LogFailure, "Error getting daprd log file for app %q present in %s: %s", runConfig.AppID, runFilePath, err.Error())
exitWithError = true
break
}
@ -490,7 +490,7 @@ func executeRun(runFilePath string, apps []runfileconfig.App) (bool, error) {
} else {
err = app.CreateAppLogFile()
if err != nil {
print.StatusEvent(os.Stderr, print.LogFailure, "Error getting log file for app %q present in %s: %s", runConfig.AppID, runFilePath, err.Error())
print.StatusEvent(os.Stderr, print.LogFailure, "Error getting app log file for app %q present in %s: %s", runConfig.AppID, runFilePath, err.Error())
exitWithError = true
break
}
@ -531,7 +531,7 @@ func executeRun(runFilePath string, apps []runfileconfig.App) (bool, error) {
}
// Stop daprd and app processes for each runState.
_, closeError := gracefullyShutdownAppsAndCloseResources(runStates, apps)
closeError := gracefullyShutdownAppsAndCloseResources(runStates, apps)
for _, app := range apps {
runConfig := app.RunConfig
@ -551,13 +551,9 @@ func logInformationalStatusToStdout(app runfileconfig.App) {
print.InfoStatusEvent(os.Stdout, "Writing log files to directory : %s", app.GetLogsDir())
}
func gracefullyShutdownAppsAndCloseResources(runState []*runExec.RunExec, apps []runfileconfig.App) (bool, error) {
exitWithError := false
func gracefullyShutdownAppsAndCloseResources(runState []*runExec.RunExec, apps []runfileconfig.App) error {
for _, s := range runState {
hasErr := stopDaprdAndAppProcesses(s)
if !exitWithError && hasErr {
exitWithError = true
}
stopDaprdAndAppProcesses(s)
}
var err error
// close log file resources.
@ -571,7 +567,7 @@ func gracefullyShutdownAppsAndCloseResources(runState []*runExec.RunExec, apps [
err = hasErr
}
}
return exitWithError, err
return err
}
func executeRunWithAppsConfigFile(runFilePath string) {

View File

@ -0,0 +1,77 @@
/*
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 main
import (
"bytes"
"encoding/json"
"log"
"net/http"
"os"
"time"
)
type Metrics struct {
MetricsID int `json:"metricsID"`
}
func main() {
var host string
var port string
client := http.Client{}
if val, ok := os.LookupEnv("DAPR_HTTP_PORT"); !ok {
log.Fatalf("DAPR_HTTP_PORT not automatically injected")
} else {
log.Println("DAPR_HTTP_PORT set to", val)
port = val
}
// DAPR_HOST_ADD needs to be an env set in dapr.yaml file
if val, ok := os.LookupEnv("DAPR_HOST_ADD"); !ok {
log.Fatalf("DAPR_HOST_ADD not set")
} else {
log.Println("DAPR_HOST_ADD set to", val)
host = val
}
finalURL := "http://" + host + ":" + port + "/metrics"
log.Println("Sending metrics to ", finalURL)
for i := 0; i < 2000; i++ {
time.Sleep(1 * time.Second)
metrics := Metrics{
MetricsID: i,
}
b, err := json.Marshal(metrics)
if err != nil {
log.Println("Got error while marshalling metrics ", err)
continue
}
// Send metrics to Dapr
req, _ := http.NewRequest(http.MethodPost, finalURL, bytes.NewBuffer(b))
req.Header.Set("Content-Type", "application/json")
req.Header.Set("dapr-app-id", "processor")
r, err := client.Do(req)
if err != nil {
log.Println("Got error while sending a request to 'processor' app ", err)
continue
}
defer r.Body.Close()
if r.StatusCode != http.StatusOK {
log.Printf("Error sending metrics with %d to 'processor' app got status code %d\n", i, r.StatusCode)
log.Printf("Status %s \n", r.Status)
continue
}
log.Printf("Metrics with ID %d sent \n", i)
}
}

View File

@ -0,0 +1,3 @@
module emit-metrics
go 1.19

View File

@ -0,0 +1,87 @@
/*
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 main
import (
"context"
"encoding/json"
"fmt"
"log"
"net"
"net/http"
"os"
"os/signal"
"syscall"
"time"
)
type handler struct{}
type Metrics struct {
MetricsID int `json:"metricsID"`
}
func (h *handler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
fmt.Println("Received request: ", r.Method)
defer r.Body.Close()
var metrics Metrics
err := json.NewDecoder(r.Body).Decode(&metrics)
if err != nil {
fmt.Println("Error decoding body: ", err)
w.WriteHeader(http.StatusBadRequest)
return
}
fmt.Println("Received metrics: ", metrics)
w.WriteHeader(http.StatusOK)
}
func main() {
fmt.Println("Starting server in port 9081...")
StartServer(9081, &handler{})
}
// StartServer starts a HTTP or HTTP2 server
func StartServer(port int, handler http.Handler) {
// Create a listener
addr := fmt.Sprintf(":%d", port)
ln, err := net.Listen("tcp", addr)
if err != nil {
log.Fatalf("Failed to create listener: %v", err)
}
//nolint:gosec
server := &http.Server{
Addr: addr,
Handler: handler,
}
// Stop the server when we get a termination signal
stopCh := make(chan os.Signal, 1)
signal.Notify(stopCh, syscall.SIGKILL, syscall.SIGTERM, syscall.SIGINT) //nolint:staticcheck
go func() {
// Wait for cancelation signal
<-stopCh
log.Println("Shutdown signal received")
ctx, cancel := context.WithTimeout(context.Background(), 1*time.Second)
defer cancel()
server.Shutdown(ctx)
}()
err = server.Serve(ln)
if err != http.ErrServerClosed {
log.Fatalf("Failed to run server: %v", err)
}
log.Println("Server shut down")
}

View File

@ -0,0 +1,3 @@
module processor
go 1.19

View File

@ -15,8 +15,12 @@ package spawn
import (
"bufio"
"bytes"
"context"
"fmt"
"os/exec"
"syscall"
"time"
)
// CommandWithContext runs a command with its arguments in background.
@ -72,3 +76,39 @@ func Command(command string, arguments ...string) (string, error) {
return string(outBytes), err
}
// CommandExecWithContext runs a command with its arguments, kills the command after context is done
// and returns the combined stdout, stderr or the error.
func CommandExecWithContext(ctx context.Context, command string, arguments ...string) (string, error) {
cmd := exec.Command(command, arguments...)
var b bytes.Buffer
cmd.Stdout = &b
cmd.Stderr = &b
err := cmd.Start()
if err != nil {
return "", fmt.Errorf("error starting command : %w", err)
}
waitErrChan := make(chan error, 1)
go func(errChan chan error) {
waitErr := cmd.Wait()
if waitErr != nil {
fmt.Printf("error waiting for command : %s\n", waitErr)
}
waitErrChan <- waitErr
}(waitErrChan)
<-ctx.Done()
if cmd.ProcessState == nil || !cmd.ProcessState.Exited() {
cmd.Process.Signal(syscall.SIGTERM)
time.Sleep(10 * time.Second)
if cmd.ProcessState == nil || !cmd.ProcessState.Exited() {
err = cmd.Process.Kill()
if err != nil {
return b.String(), fmt.Errorf("error killing command : %w", err)
}
}
}
return b.String(), <-waitErrChan
}

View File

@ -1,5 +1,5 @@
//go:build e2e
// +build e2e
//go:build e2e || template
// +build e2e template
/*
Copyright 2022 The Dapr Authors
@ -119,6 +119,18 @@ func cmdRun(unixDomainSocket string, args ...string) (string, error) {
return spawn.Command(common.GetDaprPath(), runArgs...)
}
// cmdRun runs a Dapr instance and returns the command output and error.
func cmdRunWithContext(ctx context.Context, unixDomainSocket string, args ...string) (string, error) {
runArgs := []string{"run"}
if unixDomainSocket != "" {
runArgs = append(runArgs, "--unix-domain-socket", unixDomainSocket)
}
runArgs = append(runArgs, args...)
return spawn.CommandExecWithContext(ctx, common.GetDaprPath(), runArgs...)
}
// cmdStop stops the specified app and returns the command output and error.
func cmdStop(appId string, args ...string) (string, error) {
stopArgs := append([]string{"stop", "--log-as-json", "--app-id", appId}, args...)

View File

@ -1,5 +1,5 @@
//go:build e2e
// +build e2e
//go:build e2e && !template
// +build e2e,!template
/*
Copyright 2022 The Dapr Authors

View File

@ -1,5 +1,5 @@
//go:build e2e
// +build e2e
//go:build e2e && !template
// +build e2e,!template
/*
Copyright 2022 The Dapr Authors

View File

@ -1,5 +1,5 @@
//go:build e2e
// +build e2e
//go:build e2e && !template
// +build e2e,!template
/*
Copyright 2022 The Dapr Authors

View File

@ -1,5 +1,5 @@
//go:build e2e
// +build e2e
//go:build e2e && !template
// +build e2e,!template
/*
Copyright 2022 The Dapr Authors

View File

@ -1,5 +1,5 @@
//go:build e2e
// +build e2e
//go:build e2e && !template
// +build e2e,!template
/*
Copyright 2022 The Dapr Authors

View File

@ -0,0 +1,365 @@
//go:build e2e || template
// +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 (
"context"
"fmt"
"io/ioutil"
"os"
"path/filepath"
"runtime"
"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
}
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.
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) {
t.Cleanup(func() {
// assumption in the test is that there is only one set of app and daprd logs in the logs directory.
os.RemoveAll("../../apps/emit-metrics/.dapr/logs")
os.RemoveAll("../../apps/processor/.dapr/logs")
})
args := []string{
"-f", "../testdata/run-template-files/wrong_emit_metrics_app_dapr.yaml",
}
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) {
t.Cleanup(func() {
// assumption in the test is that there is only one set of app and daprd logs in the logs directory.
os.RemoveAll("../../apps/emit-metrics/.dapr/logs")
os.RemoveAll("../../apps/processor/.dapr/logs")
})
args := []string{
"-f", "../testdata/run-template-files/dapr.yaml",
}
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: {3}",
},
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 3 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) {
t.Cleanup(func() {
// assumption in the test is that there is only one set of app and daprd logs in the logs directory.
os.RemoveAll("../../apps/emit-metrics/.dapr/logs")
os.RemoveAll("../../apps/processor/.dapr/logs")
})
args := []string{
"-f", "../testdata/run-template-files/env_var_not_set_dapr.yaml",
}
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) {
t.Cleanup(func() {
// assumption in the test is that there is only one set of app and daprd logs in the logs directory.
os.RemoveAll("../../apps/emit-metrics/.dapr/logs")
os.RemoveAll("../../apps/processor/.dapr/logs")
})
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.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) {
t.Cleanup(func() {
// assumption in the test is that there is only one set of app and daprd logs in the logs directory.
os.RemoveAll("../../apps/emit-metrics/.dapr/logs")
os.RemoveAll("../../apps/processor/.dapr/logs")
})
args := []string{
"-f", "../testdata/run-template-files/empty_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")
// 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)
})
}
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) {
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.
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)
}

View File

@ -1,5 +1,5 @@
//go:build e2e
// +build e2e
//go:build e2e && !template
// +build e2e,!template
/*
Copyright 2022 The Dapr Authors

View File

@ -1,5 +1,5 @@
//go:build e2e
// +build e2e
//go:build e2e && !template
// +build e2e,!template
/*
Copyright 2022 The Dapr Authors

View File

@ -1,5 +1,5 @@
//go:build e2e
// +build e2e
//go:build e2e && !template
// +build e2e,!template
/*
Copyright 2022 The Dapr Authors

View File

@ -1,5 +1,5 @@
//go:build e2e
// +build e2e
//go:build e2e || template
// +build e2e template
/*
Copyright 2022 The Dapr Authors

View File

@ -1,5 +1,5 @@
//go:build e2e
// +build e2e
//go:build e2e && !template
// +build e2e,!template
/*
Copyright 2022 The Dapr Authors

View File

@ -0,0 +1,5 @@
apiVersion: dapr.io/v1alpha1
kind: Configuration
metadata:
name: appconfig
spec:

View File

@ -0,0 +1,12 @@
version: 1
apps:
- app_dir_path: ../../../apps/processor/
app_port: 9081
dapr_http_port: 3510
command: ["go","run", "app.go"]
- app_id: emit-metrics
app_dir_path: ../../../apps/emit-metrics/
dapr_http_port: 3511
env:
DAPR_HOST_ADD: localhost
command: ["go","run", "app.go"]

View File

@ -0,0 +1,12 @@
version: 1
apps:
- app_dir_path: ../../../apps/processor/
app_port: 9081
dapr_http_port: 3510
command: ["go","run", "app.go"]
- app_id: emit-metrics
app_dir_path: ../../../apps/emit-metrics/
dapr_http_port: 3511
env:
DAPR_HOST_ADD: localhost
command: [""]

View File

@ -0,0 +1,10 @@
version: 1
apps:
- app_dir_path: ../../../apps/processor/
app_port: 9081
dapr_http_port: 3510
command: ["go","run", "app.go"]
- app_id: emit-metrics
app_dir_path: ../../../apps/emit-metrics/
dapr_http_port: 3511 # required env var DAPR_HOST_ADD not set in the yaml file
command: ["go","run", "app.go"]

View File

@ -0,0 +1,17 @@
version: 1
common:
# Unused resources. This is set so that when dapr is not init validation does not fail.
# Used in test run template when dapr is not init.
resources_path: ../resources
config_file_path: appconfig.yaml
apps:
- app_dir_path: ../../../apps/processor/
app_port: 9081
dapr_http_port: 3510
command: ["go","run", "app.go"]
- app_id: emit-metrics
app_dir_path: ../../../apps/emit-metrics/
dapr_http_port: 3511
env:
DAPR_HOST_ADD: localhost

View File

@ -0,0 +1,13 @@
version: 1
apps:
- app_dir_path: ../../../apps/processor/
app_port: 9081
dapr_http_port: 3510
command: ["go","run", "app.go"]
- app_id: emit-metrics
app_dir_path: ../../../apps/emit-metrics/
dapr_http_port: 3511
env:
DAPR_HOST_ADD: localhost
# Set wrong app name
command: ["go","run", "wrongappname.go"]