From 9bb917a6ec6c3d5eca067aa5a0d4f451eacadcd1 Mon Sep 17 00:00:00 2001 From: Moji Date: Fri, 8 Nov 2024 18:54:35 +0330 Subject: [PATCH] chore: add E2E tests for sidecars (#577) * chore: add E2E tests for sidecars * fix: rename sidecar file * chore: applied feedback --- .github/workflows/knuu_testing.yml | 2 + e2e/basic/logs_test.go | 84 ------------------------------ e2e/sidecars/cmd_exec_test.go | 28 ++++++++++ e2e/sidecars/file_test.go | 28 ++++++++++ e2e/sidecars/logs_test.go | 43 +++++++++++++++ e2e/sidecars/sidecar_def_test.go | 64 +++++++++++++++++++++++ e2e/sidecars/suite_setup_test.go | 59 +++++++++++++++++++++ 7 files changed, 224 insertions(+), 84 deletions(-) create mode 100644 e2e/sidecars/cmd_exec_test.go create mode 100644 e2e/sidecars/file_test.go create mode 100644 e2e/sidecars/logs_test.go create mode 100644 e2e/sidecars/sidecar_def_test.go create mode 100644 e2e/sidecars/suite_setup_test.go diff --git a/.github/workflows/knuu_testing.yml b/.github/workflows/knuu_testing.yml index 024f871..2206f07 100644 --- a/.github/workflows/knuu_testing.yml +++ b/.github/workflows/knuu_testing.yml @@ -17,6 +17,8 @@ jobs: timeout: 10m - pkg: "./e2e/basic" timeout: 15m + - pkg: "./e2e/sidecars" + timeout: 5m - pkg: "./e2e/system" timeout: 15m - pkg: "./e2e/netshaper" diff --git a/e2e/basic/logs_test.go b/e2e/basic/logs_test.go index 0355d0e..e7ac5b3 100644 --- a/e2e/basic/logs_test.go +++ b/e2e/basic/logs_test.go @@ -2,23 +2,13 @@ package basic import ( "context" - "errors" "fmt" "io" "time" - - "github.com/celestiaorg/knuu/pkg/instance" - "github.com/celestiaorg/knuu/pkg/system" ) const expectedLogMsg = "Hello World" -type sidecarLogsTest struct { - instance *instance.Instance -} - -var _ instance.SidecarManager = (*sidecarLogsTest)(nil) - func (s *Suite) TestLogs() { const namePrefix = "logs" ctx := context.Background() @@ -46,77 +36,3 @@ func (s *Suite) TestLogs() { logOutput := string(logs) s.Contains(logOutput, expectedLogMsg) } - -func (s *Suite) TestLogsWithSidecar() { - const namePrefix = "logs-sidecar" - ctx := context.Background() - - // Create a new instance - target, err := s.Knuu.NewInstance(namePrefix + "-target") - s.Require().NoError(err) - - sidecar := &sidecarLogsTest{} - - s.Require().NoError(target.Build().SetImage(ctx, alpineImage)) - s.Require().NoError(target.Build().SetStartCommand("sh", "-c", "sleep infinity")) - s.Require().NoError(target.Build().Commit(ctx)) - s.Require().NoError(target.Sidecars().Add(ctx, sidecar)) - s.Require().NoError(target.Execution().Start(ctx)) - - // Wait for a short duration to allow log generation - time.Sleep(5 * time.Second) - - logStream, err := sidecar.Instance().Monitoring().Logs(ctx) - s.Require().NoError(err) - defer logStream.Close() - - logs, err := io.ReadAll(logStream) - s.Require().NoError(err) - - logOutput := string(logs) - s.Contains(logOutput, expectedLogMsg) -} - -func (sl *sidecarLogsTest) Initialize(ctx context.Context, namePrefix string, sysDeps *system.SystemDependencies) error { - var err error - sl.instance, err = instance.New(namePrefix+"-sidecar-logs", sysDeps) - if err != nil { - return err - } - sl.instance.Sidecars().SetIsSidecar(true) - - if err := sl.instance.Build().SetImage(ctx, alpineImage); err != nil { - return err - } - - err = sl.instance.Build().SetStartCommand("sh", "-c", fmt.Sprintf("while true; do echo '%s'; sleep 1; done", expectedLogMsg)) - if err != nil { - return err - } - - if err := sl.instance.Build().Commit(ctx); err != nil { - return err - } - return nil -} - -func (sl *sidecarLogsTest) PreStart(ctx context.Context) error { - if sl.instance == nil { - return errors.New("instance not initialized") - } - return nil -} - -func (sl *sidecarLogsTest) Instance() *instance.Instance { - return sl.instance -} - -func (sl *sidecarLogsTest) Clone(namePrefix string) (instance.SidecarManager, error) { - clone, err := sl.instance.CloneWithName(namePrefix + "-" + sl.instance.Name()) - if err != nil { - return nil, err - } - return &sidecarLogsTest{ - instance: clone, - }, nil -} diff --git a/e2e/sidecars/cmd_exec_test.go b/e2e/sidecars/cmd_exec_test.go new file mode 100644 index 0000000..30ef5d7 --- /dev/null +++ b/e2e/sidecars/cmd_exec_test.go @@ -0,0 +1,28 @@ +package sidecars + +import ( + "context" + "strings" +) + +func (s *Suite) TestExecuteCommandInSidecar() { + const ( + namePrefix = "execute-command-in-sidecar" + cmdMsg = "Hello World!" + command = "echo " + cmdMsg + ) + + ctx := context.Background() + + sidecar := &testSidecar{ + StartCommand: []string{"sh", "-c", "sleep infinity"}, + } + s.startNewInstanceWithSidecar(ctx, namePrefix, sidecar) + + // Create a file in the sidecar instance + out, err := sidecar.Instance().Execution().ExecuteCommand(ctx, command) + s.Require().NoError(err, "executing command output: %v", out) + + outTrimmed := strings.TrimSpace(out) + s.Assert().Equal(cmdMsg, outTrimmed) +} diff --git a/e2e/sidecars/file_test.go b/e2e/sidecars/file_test.go new file mode 100644 index 0000000..9cbf6e5 --- /dev/null +++ b/e2e/sidecars/file_test.go @@ -0,0 +1,28 @@ +package sidecars + +import ( + "context" +) + +func (s *Suite) TestDownloadFileFromRunningSidecar() { + const ( + namePrefix = "download-file-running-sidecar" + fileContent = "Hello World!" + filePath = "/hello.txt" + ) + ctx := context.Background() + + sidecar := &testSidecar{ + StartCommand: []string{"sh", "-c", "sleep infinity"}, + } + s.startNewInstanceWithSidecar(ctx, namePrefix, sidecar) + + // Create a file in the sidecar instance + out, err := sidecar.Instance().Execution().ExecuteCommand(ctx, "echo", "-n", fileContent, ">", filePath) + s.Require().NoError(err, "executing command output: %v", out) + + gotContent, err := sidecar.Instance().Storage().GetFileBytes(ctx, filePath) + s.Require().NoError(err, "Failed to read file %s from sidecar: %v", filePath, err) + + s.Assert().Equal(fileContent, string(gotContent)) +} diff --git a/e2e/sidecars/logs_test.go b/e2e/sidecars/logs_test.go new file mode 100644 index 0000000..edefe10 --- /dev/null +++ b/e2e/sidecars/logs_test.go @@ -0,0 +1,43 @@ +package sidecars + +import ( + "context" + "fmt" + "io" + "strings" + "time" +) + +func (s *Suite) TestLogsWithSidecar() { + const ( + namePrefix = "logs-sidecar" + expectedLogMsg = "Hello World" + timeout = 10 * time.Second + interval = 1 * time.Second + ) + ctx := context.Background() + + sidecar := &testSidecar{ + StartCommand: []string{ + "sh", "-c", + fmt.Sprintf("while true; do echo '%s'; sleep 1; done", expectedLogMsg), + }, + } + s.startNewInstanceWithSidecar(ctx, namePrefix, sidecar) + + // Wait for a short duration to allow log generation + s.Require().Eventually(func() bool { + logStream, err := sidecar.Instance().Monitoring().Logs(ctx) + if err != nil { + return false + } + defer logStream.Close() + + logs, err := io.ReadAll(logStream) + if err != nil { + return false + } + + return strings.Contains(string(logs), expectedLogMsg) + }, timeout, interval, "failed to get expected log message") +} diff --git a/e2e/sidecars/sidecar_def_test.go b/e2e/sidecars/sidecar_def_test.go new file mode 100644 index 0000000..c9f80ae --- /dev/null +++ b/e2e/sidecars/sidecar_def_test.go @@ -0,0 +1,64 @@ +package sidecars + +import ( + "context" + "errors" + + "github.com/celestiaorg/knuu/pkg/instance" + "github.com/celestiaorg/knuu/pkg/system" +) + +type testSidecar struct { + instance *instance.Instance + StartCommand []string +} + +var _ instance.SidecarManager = (*testSidecar)(nil) + +func (s *testSidecar) Initialize(ctx context.Context, namePrefix string, sysDeps *system.SystemDependencies) error { + var err error + s.instance, err = instance.New(namePrefix+"-sidecar-logs", sysDeps) + if err != nil { + return err + } + s.instance.Sidecars().SetIsSidecar(true) + + if err := s.instance.Build().SetImage(ctx, alpineImage); err != nil { + return err + } + + err = s.instance.Build().SetStartCommand(s.StartCommand...) + if err != nil { + return err + } + + if err := s.instance.Build().Commit(ctx); err != nil { + return err + } + return nil +} + +func (s *testSidecar) PreStart(ctx context.Context) error { + if s.instance == nil { + return errors.New("instance not initialized") + } + if len(s.StartCommand) == 0 { + return errors.New("start command not configured") + } + return nil +} + +func (s *testSidecar) Instance() *instance.Instance { + return s.instance +} + +func (s *testSidecar) Clone(namePrefix string) (instance.SidecarManager, error) { + clone, err := s.instance.CloneWithName(namePrefix + "-" + s.instance.Name()) + if err != nil { + return nil, err + } + return &testSidecar{ + instance: clone, + StartCommand: append([]string{}, s.StartCommand...), + }, nil +} diff --git a/e2e/sidecars/suite_setup_test.go b/e2e/sidecars/suite_setup_test.go new file mode 100644 index 0000000..3d52ee9 --- /dev/null +++ b/e2e/sidecars/suite_setup_test.go @@ -0,0 +1,59 @@ +package sidecars + +import ( + "context" + "testing" + "time" + + "github.com/sirupsen/logrus" + "github.com/stretchr/testify/suite" + + "github.com/celestiaorg/knuu/e2e" + "github.com/celestiaorg/knuu/pkg/instance" + "github.com/celestiaorg/knuu/pkg/knuu" +) + +const ( + testTimeout = time.Minute * 5 // the same time that is used in the ci/cd pipeline + alpineImage = "alpine:3.20.3" +) + +type Suite struct { + e2e.Suite +} + +func TestRunSuite(t *testing.T) { + suite.Run(t, new(Suite)) +} + +func (s *Suite) SetupSuite() { + var ( + ctx = context.Background() + logger = logrus.New() + err error + ) + + s.Knuu, err = knuu.New(ctx, knuu.Options{ + Timeout: testTimeout, + Logger: logger, + }) + s.Require().NoError(err) + + s.T().Logf("Scope: %s", s.Knuu.Scope) + s.Knuu.HandleStopSignal(ctx) + + s.Executor.Kn = s.Knuu +} + +func (s *Suite) startNewInstanceWithSidecar(ctx context.Context, namePrefix string, sidecar *testSidecar) *instance.Instance { + target, err := s.Knuu.NewInstance(namePrefix + "-target") + s.Require().NoError(err) + + s.Require().NoError(target.Build().SetImage(ctx, alpineImage)) + s.Require().NoError(target.Build().SetStartCommand("sh", "-c", "sleep infinity")) + s.Require().NoError(target.Build().Commit(ctx)) + s.Require().NoError(target.Sidecars().Add(ctx, sidecar)) + s.Require().NoError(target.Execution().Start(ctx)) + + return target +}