diff --git a/e2e/assert_cleanups.go b/e2e/assert_cleanups.go deleted file mode 100644 index 179b8b2..0000000 --- a/e2e/assert_cleanups.go +++ /dev/null @@ -1,43 +0,0 @@ -package e2e - -import ( - "os" - "testing" - - "github.com/celestiaorg/knuu/pkg/knuu" -) - -// AssertCleanupInstance is a helper function that cleans up a single instance. -func AssertCleanupInstance(t *testing.T, instance *knuu.Instance) error { - if instance == nil { - t.Fatal("Instance is nil") - } - - if err := instance.Destroy(); err != nil { - t.Fatalf("Error destroying instance: %v", err) - } - return nil -} - -// AssertCleanupInstances is a helper function that cleans up a list of instances. -func AssertCleanupInstances(t *testing.T, executor *knuu.Executor, instances []*knuu.Instance) error { - if os.Getenv("KNUU_SKIP_CLEANUP") == "true" { - t.Log("Skipping cleanup") - return nil - } - - if executor == nil { - t.Fatal("Executor is nil") - } - - if err := executor.Destroy(); err != nil { - t.Fatalf("Error destroying executor: %v", err) - } - - err := knuu.BatchDestroy(instances...) - if err != nil { - t.Fatalf("Error destroying instances: %v", err) - } - - return nil -} diff --git a/e2e/assert_create.go b/e2e/assert_create.go deleted file mode 100644 index 469ee2d..0000000 --- a/e2e/assert_create.go +++ /dev/null @@ -1,55 +0,0 @@ -package e2e - -import ( - "testing" - - "github.com/celestiaorg/knuu/pkg/knuu" -) - -const ( - // nginxImage is the image used to create the instances. - nginxImage = "docker.io/nginx:latest" - // nginxVolume is the volume used to create the instances. - nginxVolume = "1Gi" - // nginxVolumeOwner is the owner of the volume used to create the instances. - nginxVolumeOwner = 0 - // nginxPort is the port used to create the instances. - nginxPort = 80 - // nginxPath is the path used to create the instances. - nginxPath = "/usr/share/nginx/html" -) - -// AssertCreateInstanceNginxWithVolumeOwner creates and configures an instance with common settings used across tests but doesn't call commit. -func AssertCreateInstanceNginxWithVolumeOwnerWithoutCommit(t *testing.T, instanceName string) *knuu.Instance { - instance, err := knuu.NewInstance(instanceName) - if err != nil { - t.Fatalf("Error creating instance '%v': %v", instanceName, err) - } - err = instance.SetImage(nginxImage) - if err != nil { - t.Fatalf("Error setting image for '%v': %v", instanceName, err) - } - err = instance.AddPortTCP(nginxPort) - if err != nil { - t.Fatalf("Error adding port for '%v': %v", instanceName, err) - } - _, err = instance.ExecuteCommand("mkdir", "-p", nginxPath) - if err != nil { - t.Fatalf("Error executing command for '%v': %v", instanceName, err) - } - err = instance.AddVolumeWithOwner(nginxPath, nginxVolume, nginxVolumeOwner) - if err != nil { - t.Fatalf("Error adding volume: %v", err) - } - return instance -} - -// AssertCreateInstanceNginxWithVolumeOwner creates and configures an instance with common settings used across tests. -func AssertCreateInstanceNginxWithVolumeOwner(t *testing.T, instanceName string) *knuu.Instance { - instance := AssertCreateInstanceNginxWithVolumeOwnerWithoutCommit(t, instanceName) - err := instance.Commit() - if err != nil { - t.Fatalf("Error committing instance '%v': %v", instanceName, err) - } - return instance -} diff --git a/e2e/basic/probe_test.go b/e2e/basic/probe_test.go index 6f21415..8800763 100644 --- a/e2e/basic/probe_test.go +++ b/e2e/basic/probe_test.go @@ -1,13 +1,14 @@ package basic import ( + "context" "testing" "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/require" v1 "k8s.io/api/core/v1" "k8s.io/apimachinery/pkg/util/intstr" + "github.com/celestiaorg/knuu/e2e" "github.com/celestiaorg/knuu/pkg/knuu" ) @@ -15,7 +16,13 @@ func TestProbe(t *testing.T) { t.Parallel() // Setup - executor, err := knuu.NewExecutor() + // Ideally this has to be defined in the test suit setup + exe := e2e.Executor{ + Kn: knuu.GetKnuuObj(), + } + + ctx := context.Background() + executor, err := exe.NewInstance(ctx, "prob-executor") if err != nil { t.Fatalf("Error creating executor: %v", err) } @@ -60,7 +67,16 @@ func TestProbe(t *testing.T) { } t.Cleanup(func() { - require.NoError(t, knuu.BatchDestroy(executor.Instance, web)) + // after refactor, we can use instance.BatchDestroy for simplicity + err := executor.Destroy(ctx) + if err != nil { + t.Logf("Error destroying instance: %v", err) + } + + err = web.Destroy() + if err != nil { + t.Logf("Error destroying instance: %v", err) + } }) // Test logic @@ -79,7 +95,7 @@ func TestProbe(t *testing.T) { t.Fatalf("Error waiting for instance to be running: %v", err) } - wget, err := executor.ExecuteCommand("wget", "-q", "-O", "-", webIP) + wget, err := executor.ExecuteCommand(ctx, "wget", "-q", "-O", "-", webIP) if err != nil { t.Fatalf("Error executing command '%v':", err) } diff --git a/pkg/instance/executor.go b/e2e/executor.go similarity index 54% rename from pkg/instance/executor.go rename to e2e/executor.go index a9def8b..329cb70 100644 --- a/pkg/instance/executor.go +++ b/e2e/executor.go @@ -1,59 +1,58 @@ -package instance +package e2e import ( "context" "k8s.io/apimachinery/pkg/api/resource" - "github.com/celestiaorg/knuu/pkg/system" + "github.com/celestiaorg/knuu/pkg/instance" + "github.com/celestiaorg/knuu/pkg/knuu" ) const ( executorDefaultImage = "docker.io/nicolaka/netshoot:latest" - executorName = "executor" sleepCommand = "sleep" infinityArg = "infinity" ) +type Executor struct { + Kn *knuu.Knuu +} + var ( executorMemoryLimit = resource.MustParse("100Mi") executorCpuLimit = resource.MustParse("100m") ) -type Executor struct { - *Instance -} - -func NewExecutor(ctx context.Context, sysDeps system.SystemDependencies) (*Executor, error) { - i, err := New(executorName, sysDeps) +func (e *Executor) NewInstance(ctx context.Context, name string) (*instance.Instance, error) { + i, err := e.Kn.NewInstance(name) if err != nil { - return nil, ErrCreatingInstance.Wrap(err) + return nil, err } if err := i.SetImage(ctx, executorDefaultImage); err != nil { - return nil, ErrSettingImage.Wrap(err) + return nil, err } if err := i.Commit(); err != nil { - return nil, ErrCommittingInstance.Wrap(err) + return nil, err } if err := i.SetArgs(sleepCommand, infinityArg); err != nil { - return nil, ErrSettingArgs.Wrap(err) + return nil, err } if err := i.SetMemory(executorMemoryLimit, executorMemoryLimit); err != nil { - return nil, ErrSettingMemory.Wrap(err) + return nil, err } if err := i.SetCPU(executorCpuLimit); err != nil { - return nil, ErrSettingCPU.Wrap(err) + return nil, err } - i.instanceType = ExecutorInstance if err := i.Start(ctx); err != nil { - return nil, ErrStartingInstance.Wrap(err) + return nil, err } - return &Executor{Instance: i}, nil + return i, nil } diff --git a/e2e/system/build_from_git_test.go b/e2e/system/build_from_git_test.go index 6e283e5..09fa5c5 100644 --- a/e2e/system/build_from_git_test.go +++ b/e2e/system/build_from_git_test.go @@ -3,32 +3,21 @@ package system import ( "context" "strings" - "testing" - - "github.com/sirupsen/logrus" - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/require" "github.com/celestiaorg/knuu/pkg/builder" - "github.com/celestiaorg/knuu/pkg/k8s" - "github.com/celestiaorg/knuu/pkg/knuu" - "github.com/celestiaorg/knuu/pkg/minio" ) -func TestBuildFromGit(t *testing.T) { - t.Parallel() +func (s *Suite) TestBuildFromGit() { + const namePrefix = "build-from-git" + s.T().Parallel() // Setup ctx := context.Background() - // The default image builder is kaniko here - kn, err := knuu.New(ctx, knuu.Options{}) - require.NoError(t, err, "Error creating knuu") - - target, err := kn.NewInstance("git-builder") - require.NoError(t, err, "Error creating instance") + target, err := s.Knuu.NewInstance(namePrefix) + s.Require().NoError(err) - t.Log("Building the image") + s.T().Log("Building the image") // This is a blocking call which builds the image from git repo err = target.SetGitRepo(ctx, builder.GitContext{ @@ -37,84 +26,66 @@ func TestBuildFromGit(t *testing.T) { Username: "", Password: "", }) - require.NoError(t, err, "Error setting git repo") + s.Require().NoError(err) - t.Log("Image built") + s.T().Log("Image built") - t.Cleanup(func() { - if err := kn.CleanUp(ctx); err != nil { - t.Logf("Error cleaning up knuu: %v", err) + s.T().Cleanup(func() { + if err := target.Destroy(ctx); err != nil { + s.T().Logf("Error cleaning up knuu: %v", err) } }) - require.NoError(t, target.Commit()) - - t.Logf("Starting instance") + s.Require().NoError(target.Commit()) - assert.NoError(t, target.Start(ctx)) + s.T().Logf("Starting instance") + s.Require().NoError(target.Start(ctx)) - t.Logf("Instance started") + s.T().Logf("Instance started") // The file is created by the dockerfile in the repo, // so to make sure it is built correctly, we check the file data, err := target.GetFileBytes(ctx, "/test.txt") - require.NoError(t, err, "Error getting file bytes") + s.Require().NoError(err) data = []byte(strings.TrimSpace(string(data))) - assert.Equal(t, []byte("Hello, World!"), data, "File bytes do not match") + s.Assert().Equal([]byte("Hello, World!"), data, "File bytes do not match") } -func TestBuildFromGitWithModifications(t *testing.T) { - t.Parallel() +func (s *Suite) TestBuildFromGitWithModifications() { + const namePrefix = "build-from-git-with-modifications" + s.T().Parallel() // Setup - ctx := context.Background() - - logger := logrus.New() - - k8sClient, err := k8s.NewClient(ctx, knuu.DefaultScope(), logger) - require.NoError(t, err, "Error creating k8s client") - - // Since we are modifying the git repo, - // we need to setup minio to allow the builder to push the changes - minioClient, err := minio.New(ctx, k8sClient, logger) - require.NoError(t, err, "Error creating minio client") - - // The default image builder is kaniko here - kn, err := knuu.New(ctx, knuu.Options{ - K8sClient: k8sClient, - MinioClient: minioClient, - }) - require.NoError(t, err, "Error creating knuu") - - sampleInstance, err := kn.NewInstance("git-builder") - require.NoError(t, err, "Error creating instance") + target, err := s.Knuu.NewInstance(namePrefix) + s.Require().NoError(err) + ctx := context.Background() // This is a blocking call which builds the image from git repo - err = sampleInstance.SetGitRepo(ctx, builder.GitContext{ + err = target.SetGitRepo(ctx, builder.GitContext{ Repo: "https://github.com/celestiaorg/knuu.git", Branch: "test/build-from-git", // This branch has a Dockerfile and is protected as to not be deleted Username: "", Password: "", }) - require.NoError(t, err, "Error setting git repo") + s.Require().NoError(err) - require.NoError(t, sampleInstance.SetCommand("sleep", "infinity"), "Error setting command") + s.Require().NoError(target.SetCommand("sleep", "infinity")) - err = sampleInstance.AddFileBytes([]byte("Hello, world!"), "/home/hello.txt", "root:root") - require.NoError(t, err, "Error adding file") + err = target.AddFileBytes([]byte("Hello, world!"), "/home/hello.txt", "root:root") + s.Require().NoError(err, "Error adding file") - require.NoError(t, sampleInstance.Commit(), "Error committing instance") + s.Require().NoError(target.Commit()) - t.Cleanup(func() { - if err := kn.CleanUp(ctx); err != nil { - t.Logf("Error cleaning up knuu: %v", err) + s.T().Cleanup(func() { + if err := target.Destroy(ctx); err != nil { + s.T().Logf("Error cleaning up knuu: %v", err) } }) - require.NoError(t, sampleInstance.Start(ctx), "Error starting instance") + s.Require().NoError(target.Start(ctx)) - data, err := sampleInstance.GetFileBytes(ctx, "/home/hello.txt") - require.NoError(t, err, "Error getting file bytes") + data, err := target.GetFileBytes(ctx, "/home/hello.txt") + s.Require().NoError(err, "Error getting file bytes") - require.Equal(t, []byte("Hello, world!"), data, "File bytes do not match") + s.Assert().Equal([]byte("Hello, world!"), data, "File bytes do not match") } diff --git a/e2e/system/env_to_json_test.go b/e2e/system/env_to_json_test.go index 75f924f..41bfe13 100644 --- a/e2e/system/env_to_json_test.go +++ b/e2e/system/env_to_json_test.go @@ -1,138 +1,84 @@ package system import ( + "context" "encoding/json" "fmt" - "os" - "testing" + "strings" - "github.com/sirupsen/logrus" - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/require" - - "github.com/celestiaorg/knuu/pkg/knuu" + "github.com/celestiaorg/knuu/pkg/instance" ) -func TestEnvToJSON(t *testing.T) { - t.Parallel() +func (s *Suite) TestEnvToJSON() { + const ( + namePrefix = "env-to-json" + numberOfInstances = 2 + ) - // Setup - executor, err := knuu.NewExecutor() - if err != nil { - t.Fatalf("Error creating executor: %v", err) - } + s.T().Parallel() - const numberOfInstances = 2 - instances := make([]*knuu.Instance, numberOfInstances) + // Setup + ctx := context.Background() + executor, err := s.Executor.NewInstance(ctx, namePrefix+"-executor") + s.Require().NoError(err) // Define the env vars - testEnvVarKey1 := "TESTKEY1" - testEnvVarKey2 := "TESTKEY2" - testEnvVarKey3 := "TESTKEY3" - testEnvVarValue1 := "testvalue1" - testEnvVarValue2 := "testvalue2" - testEnvVarValue3 := "testvalue3" - - // Set the OS env vars - setEnv := func(key, value string) { - err = os.Setenv(key, value) - if err != nil { - t.Fatalf("Error setting env var '%v': %v", key, err) - } - } - setEnv(testEnvVarKey1, testEnvVarValue1) - setEnv(testEnvVarKey2, testEnvVarValue2) - setEnv(testEnvVarKey3, testEnvVarValue3) - - // Define helper function to get env vars - getEnv := func(key string) string { - value := os.Getenv(key) - if value == "" { - t.Fatalf("Error getting env var '%v'", key) - } - return value - } - jsonBytes, err := json.Marshal(map[string]string{ - testEnvVarKey1: getEnv(testEnvVarKey1), - testEnvVarKey2: getEnv(testEnvVarKey2), - testEnvVarKey3: getEnv(testEnvVarKey3), - }) - if err != nil { - t.Fatalf("Error converting env vars to JSON: %v", err) + envVars := map[string]string{ + "TESTKEY1": "testvalue1", + "TESTKEY2": "testvalue2", + "TESTKEY3": "testvalue3", } + + jsonBytes, err := json.Marshal(envVars) + s.Require().NoError(err) + jsonString := string(jsonBytes) - logrus.Debugf("JSON: %v", jsonString) + s.T().Logf("JSON: %v", jsonString) + instances := make([]*instance.Instance, numberOfInstances) for i := 0; i < numberOfInstances; i++ { - instanceName := fmt.Sprintf("web%d", i+1) - instance, err := knuu.NewInstance(instanceName) - if err != nil { - t.Fatalf("Error creating instance '%v': %v", instanceName, err) - } - err = instance.SetImage("docker.io/nginx:latest") - if err != nil { - t.Fatalf("Error setting image for '%v': %v", instanceName, err) - } - instance.AddPortTCP(80) - _, err = instance.ExecuteCommand("mkdir", "-p", "/usr/share/nginx/html") - if err != nil { - t.Fatalf("Error executing command for '%v': %v", instanceName, err) - } + name := fmt.Sprintf("%s-web%d", namePrefix, i+1) - logrus.Debugf("Writing JSON to instance '%v': %v", instanceName, jsonString) - _, err = instance.ExecuteCommand("mkdir", "-p", "/opt/env") - if err != nil { - t.Fatalf("Error writing JSON to instance '%v': %v", instanceName, err) - } - // write the json file to the instance - _, err = instance.ExecuteCommand("echo", fmt.Sprintf("'%s'", jsonString), ">", "/opt/env/env.json") - if err != nil { - t.Fatalf("Error writing JSON to instance '%v': %v", instanceName, err) - } - // for testing it, we also add it as index.html to the nginx server - _, err = instance.ExecuteCommand("echo", fmt.Sprintf("'%s'", jsonString), ">", "/usr/share/nginx/html/index.html") - if err != nil { - t.Fatalf("Error writing JSON to instance '%v': %v", instanceName, err) - } + ins := s.createNginxInstance(ctx, name) + s.Require().NoError(ins.Commit()) + s.Require().NoError(ins.Start(ctx)) - err = instance.Commit() - if err != nil { - t.Fatalf("Error committing instance '%v': %v", instanceName, err) - } + _, err = ins.ExecuteCommand(ctx, "mkdir", "-p", nginxHTMLPath) + s.Require().NoError(err) - instances[i] = instance - } + s.T().Logf("Writing JSON to instance '%v': %v", name, jsonString) + _, err = ins.ExecuteCommand(ctx, "mkdir", "-p", "/opt/env") + s.Require().NoError(err) - t.Cleanup(func() { - all := append(instances, executor.Instance) - require.NoError(t, knuu.BatchDestroy(all...)) - }) + // write the json file to the instance + _, err = ins.ExecuteCommand(ctx, "echo", fmt.Sprintf("'%s'", jsonString), ">", "/opt/env/env.json") + s.Require().NoError(err) - // Test logic - for _, instance := range instances { - err = instance.StartAsync() - if err != nil { - t.Fatalf("Error waiting for instance to be running: %v", err) - } + // for testing it, we also add it as index.html to the nginx server + _, err = ins.ExecuteCommand(ctx, "echo", fmt.Sprintf("'%s'", jsonString), ">", nginxHTMLPath+"/index.html") + s.Require().NoError(err, "writing JSON to instance '%v': %v", name, err) + + instances[i] = ins } - for _, instance := range instances { - webIP, err := instance.GetIP() + s.T().Cleanup(func() { + all := append(instances, executor) + err := instance.BatchDestroy(ctx, all...) if err != nil { - t.Fatalf("Error getting IP: %v", err) + s.T().Logf("error destroying instances: %v", err) } + }) - err = instance.WaitInstanceIsRunning() - if err != nil { - t.Fatalf("Error waiting for instance to be running: %v", err) - } + // Test logic + for _, i := range instances { + webIP, err := i.GetIP(ctx) + s.Require().NoError(err) - wget, err := executor.ExecuteCommand("wget", "-q", "-O", "-", webIP) - if err != nil { - t.Fatalf("Error executing command: %v", err) - } + wget, err := executor.ExecuteCommand(ctx, "wget", "-q", "-O", "-", webIP) + s.Require().NoError(err) - expected := fmt.Sprintf("{\"%s\":\"%s\",\"%s\":\"%s\",\"%s\":\"%s\"}\n", testEnvVarKey1, testEnvVarValue1, testEnvVarKey2, testEnvVarValue2, testEnvVarKey3, testEnvVarValue3) - assert.Equal(t, expected, wget) + expectedBytes, err := json.Marshal(envVars) + s.Require().NoError(err) + s.Assert().Equal(string(expectedBytes), strings.TrimSpace(wget)) } } diff --git a/e2e/system/external_file_test.go b/e2e/system/external_file_test.go index 9ace375..aa6231f 100644 --- a/e2e/system/external_file_test.go +++ b/e2e/system/external_file_test.go @@ -1,97 +1,63 @@ package system import ( + "context" "io" "os" - "testing" + "path/filepath" - "github.com/celestiaorg/knuu/pkg/knuu" - - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/require" + "github.com/celestiaorg/knuu/pkg/instance" ) -func TestExternalFile(t *testing.T) { - t.Parallel() +func (s *Suite) TestExternalFile() { + const namePrefix = "external-file" + s.T().Parallel() // Setup - executor, err := knuu.NewExecutor() - if err != nil { - t.Fatalf("Error creating executor: %v", err) - } - - server, err := knuu.NewInstance("server") - if err != nil { - t.Fatalf("Error creating instance '%v':", err) - } - err = server.SetImage("docker.io/nginx:latest") - if err != nil { - t.Fatalf("Error setting image '%v':", err) - } - server.AddPortTCP(80) - _, err = server.ExecuteCommand("mkdir", "-p", "/usr/share/nginx/html") - if err != nil { - t.Fatalf("Error executing command '%v':", err) - } + ctx := context.Background() + executor, err := s.Executor.NewInstance(ctx, namePrefix+"-executor") + s.Require().NoError(err) + + server := s.createNginxInstance(ctx, namePrefix+"-server") + // copy resources/html/index.html to /tmp/index.html - srcFile, err := os.Open("resources/html/index.html") - if err != nil { - t.Fatalf("Error opening source file '%v':", err) - } + srcFile, err := os.Open(resourcesHTML + "/index.html") + s.Require().NoError(err) defer srcFile.Close() // Create the destination file - dstFile, err := os.Create("/tmp/index.html") - if err != nil { - t.Fatalf("Error creating destination file '%v':", err) - } + htmlTmpPath := filepath.Join(os.TempDir(), "index.html") + dstFile, err := os.Create(htmlTmpPath) + s.Require().NoError(err) defer dstFile.Close() // Copy the contents of the source file into the destination file _, err = io.Copy(dstFile, srcFile) - if err != nil { - t.Fatalf("Error copying contents '%v':", err) - } + s.Require().NoError(err) // Ensure that the copy is successful by syncing the written data to the disk - err = dstFile.Sync() - if err != nil { - t.Fatalf("Error syncing data to disk '%v':", err) - } - - err = server.AddFile("/tmp/index.html", "/usr/share/nginx/html/index.html", "0:0") - if err != nil { - t.Fatalf("Error adding file '%v':", err) - } - err = server.Commit() - if err != nil { - t.Fatalf("Error committing instance: %v", err) - } - - t.Cleanup(func() { - require.NoError(t, knuu.BatchDestroy(executor.Instance, server)) + s.Require().NoError(dstFile.Sync()) + + err = server.AddFile(htmlTmpPath, nginxHTMLPath+"/index.html", "0:0") + s.Require().NoError(err) + + s.Require().NoError(server.Commit()) + + s.T().Cleanup(func() { + err := instance.BatchDestroy(ctx, executor, server) + if err != nil { + s.T().Logf("error destroying instance: %v", err) + } }) // Test logic + serverIP, err := server.GetIP(ctx) + s.Require().NoError(err) + + s.Require().NoError(server.Start(ctx)) + + wget, err := executor.ExecuteCommand(ctx, "wget", "-q", "-O", "-", serverIP) + s.Require().NoError(err) - serverIP, err := server.GetIP() - if err != nil { - t.Fatalf("Error getting IP '%v':", err) - } - - err = server.Start() - if err != nil { - t.Fatalf("Error starting instance: %v", err) - } - err = server.WaitInstanceIsRunning() - if err != nil { - t.Fatalf("Error waiting for instance to be running: %v", err) - } - - wget, err := executor.ExecuteCommand("wget", "-q", "-O", "-", serverIP) - if err != nil { - t.Fatalf("Error executing command '%v':", err) - } - - assert.Contains(t, wget, "Hello World!") + s.Assert().Contains(wget, "Hello World!") } diff --git a/e2e/system/file_test.go b/e2e/system/file_test.go index 2d8b94a..854c8ee 100644 --- a/e2e/system/file_test.go +++ b/e2e/system/file_test.go @@ -2,164 +2,146 @@ package system import ( "context" - "fmt" "io" "net/http" "os" - "testing" "time" - "github.com/celestiaorg/knuu/pkg/knuu" + "github.com/celestiaorg/knuu/pkg/instance" "github.com/google/uuid" - - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/require" ) -func TestFile(t *testing.T) { - t.Parallel() +func (s *Suite) TestFile() { + const namePrefix = "file" + s.T().Parallel() // Setup - executor, err := knuu.NewExecutor() - if err != nil { - t.Fatalf("Error creating executor: %v", err) - } - - serverfile, err := knuu.NewInstance("serverfile") - if err != nil { - t.Fatalf("Error creating instance '%v':", err) - } - err = serverfile.SetImage("docker.io/nginx:latest") - if err != nil { - t.Fatalf("Error setting image '%v':", err) - } - serverfile.AddPortTCP(80) - _, err = serverfile.ExecuteCommand("mkdir", "-p", "/usr/share/nginx/html") - if err != nil { - t.Fatalf("Error executing command '%v':", err) - } - err = serverfile.AddFile("resources/html/index.html", "/usr/share/nginx/html/index.html", "0:0") - if err != nil { - t.Fatalf("Error adding file '%v':", err) - } - err = serverfile.AddVolumeWithOwner("/usr/share/nginx/html", "1Gi", 0) - if err != nil { - t.Fatalf("Error adding volume: %v", err) - } - err = serverfile.Commit() - if err != nil { - t.Fatalf("Error committing instance: %v", err) - } - - t.Cleanup(func() { - require.NoError(t, knuu.BatchDestroy(executor.Instance, serverfile)) + ctx := context.Background() + executor, err := s.Executor.NewInstance(ctx, namePrefix+"-executor") + s.Require().NoError(err) + + serverfile := s.createNginxInstanceWithVolume(ctx, namePrefix+"-serverfile") + + err = serverfile.AddFile(resourcesHTML+"/index.html", nginxHTMLPath+"/index.html", "0:0") + s.Require().NoError(err) + + s.Require().NoError(serverfile.Commit()) + + s.T().Cleanup(func() { + err := instance.BatchDestroy(ctx, serverfile, executor) + if err != nil { + s.T().Logf("Error destroying instance: %v", err) + } }) // Test logic - serverfileIP, err := serverfile.GetIP() - if err != nil { - t.Fatalf("Error getting IP '%v':", err) - } - - err = serverfile.Start() - if err != nil { - t.Fatalf("Error starting instance: %v", err) - } - err = serverfile.WaitInstanceIsRunning() - if err != nil { - t.Fatalf("Error waiting for instance to be running: %v", err) - } - - wget, err := executor.ExecuteCommand("wget", "-q", "-O", "-", serverfileIP) - if err != nil { - t.Fatalf("Error executing command '%v':", err) - } - - assert.Contains(t, wget, "Hello World!") + serverfileIP, err := serverfile.GetIP(ctx) + s.Require().NoError(err) + + s.Require().NoError(serverfile.Start(ctx)) + + wget, err := executor.ExecuteCommand(ctx, "wget", "-q", "-O", "-", serverfileIP) + s.Require().NoError(err) + + s.Assert().Contains(wget, "Hello World!") } -func TestDownloadFileFromRunningInstance(t *testing.T) { - t.Parallel() +func (s *Suite) TestDownloadFileFromRunningInstance() { + const namePrefix = "download-file-running" + s.T().Parallel() // Setup - target, err := knuu.NewInstance("target") - require.NoError(t, err, "Error creating instance") + target, err := s.Knuu.NewInstance(namePrefix + "-target") + s.Require().NoError(err) - require.NoError(t, target.SetImage("alpine:latest"), "Error setting image") - require.NoError(t, target.SetArgs("tail", "-f", "/dev/null"), "Error setting args") // Keep the container running - require.NoError(t, target.Commit(), "Error committing instance") - require.NoError(t, target.Start(), "Error starting instance") + ctx := context.Background() + s.Require().NoError(target.SetImage(ctx, "alpine:latest")) + s.Require().NoError(target.SetArgs("tail", "-f", "/dev/null")) // Keep the container running + s.Require().NoError(target.Commit()) + s.Require().NoError(target.Start(ctx)) - t.Cleanup(func() { - require.NoError(t, knuu.BatchDestroy(target)) + s.T().Cleanup(func() { + if err := target.Destroy(ctx); err != nil { + s.T().Logf("error destroying instance: %v", err) + } }) // Test logic - fileContent := "Hello World!" - filePath := "/hello.txt" + const ( + fileContent = "Hello World!" + filePath = "/hello.txt" + ) // Create a file in the target instance - out, err := target.ExecuteCommand("echo", "-n", fileContent, ">", filePath) - require.NoError(t, err, fmt.Sprintf("Error executing command: %v", out)) + out, err := target.ExecuteCommand(ctx, "echo", "-n", fileContent, ">", filePath) + s.Require().NoError(err, "executing command output: %v", out) - gotContent, err := target.GetFileBytes(filePath) - require.NoError(t, err, "Error getting file bytes") + gotContent, err := target.GetFileBytes(ctx, filePath) + s.Require().NoError(err, "Error getting file bytes") - assert.Equal(t, fileContent, string(gotContent)) + s.Assert().Equal(fileContent, string(gotContent)) } -func TestMinio(t *testing.T) { - t.Parallel() +func (s *Suite) TestMinio() { + const ( + namePrefix = "minio" + minioBucketName = "knuu-e2e-test" + minioPushTimeout = 1 * time.Minute + ) + s.T().Parallel() // Setup - - target, err := knuu.NewInstance("target") - require.NoError(t, err, "Error creating instance") - - require.NoError(t, target.SetImage("alpine:latest"), "Error setting image") - require.NoError(t, target.SetArgs("tail", "-f", "/dev/null"), "Error setting args") // Keep the container running - require.NoError(t, target.Commit(), "Error committing instance") - require.NoError(t, target.Start(), "Error starting instance") - - t.Cleanup(func() { - require.NoError(t, knuu.BatchDestroy(target)) + target, err := s.Knuu.NewInstance(namePrefix + "-target") + s.Require().NoError(err) + + ctx := context.Background() + s.Require().NoError(target.SetImage(ctx, "alpine:latest")) + s.Require().NoError(target.SetArgs("tail", "-f", "/dev/null")) // Keep the container running + s.Require().NoError(target.Commit()) + s.Require().NoError(target.Start(ctx)) + + s.T().Cleanup(func() { + if err := target.Destroy(ctx); err != nil { + s.T().Logf("error destroying instance: %v", err) + } }) - fileContent := "Hello World!" - contentName := uuid.New().String() + var ( + fileContent = "Hello World!" + contentName = uuid.New().String() + ) + s.T().Logf("contentName: %v", contentName) tmpFile, err := os.CreateTemp("", "hello.txt") - require.NoError(t, err, "Error creating temporary file") + s.Require().NoError(err, "Error creating temporary file") defer os.Remove(tmpFile.Name()) - fmt.Printf("contentName: %v\n", contentName) - _, err = tmpFile.WriteString(fileContent) - require.NoError(t, err, "Error writing to temporary file") + s.Require().NoError(err, "writing to temporary file") tmpFile.Close() // Write to file did not work, so need to open it again tmpFile, err = os.Open(tmpFile.Name()) - require.NoError(t, err, "Error opening temporary file") + s.Require().NoError(err, "opening temporary file") defer tmpFile.Close() - fmt.Printf("tmpFile.Name(): %v\n", tmpFile.Name()) + s.T().Logf("tmpFile name: %v", tmpFile.Name()) - ctx, cancel := context.WithTimeout(context.Background(), 1*time.Minute) + mCtx, cancel := context.WithTimeout(ctx, minioPushTimeout) defer cancel() - err = knuu.PushFileToMinio(ctx, contentName, tmpFile) - require.NoError(t, err, "Error pushing file to Minio") + err = s.Knuu.MinioClient.Push(mCtx, tmpFile, contentName, minioBucketName) + s.Require().NoError(err) - url, err := knuu.GetMinioURL(ctx, contentName) - require.NoError(t, err, "Error getting Minio URL") + url, err := s.Knuu.MinioClient.GetURL(ctx, contentName, minioBucketName) + s.Require().NoError(err) resp, err := http.Get(url) - require.NoError(t, err, "Error downloading the file from URL") + s.Require().NoError(err) defer resp.Body.Close() gotContent, err := io.ReadAll(resp.Body) - require.NoError(t, err, "Error reading the response body") + s.Require().NoError(err) - assert.Equal(t, fileContent, string(gotContent)) + s.Assert().Equal(fileContent, string(gotContent)) } diff --git a/e2e/system/file_test_image_cached_test.go b/e2e/system/file_test_image_cached_test.go index cf23422..077e7e9 100644 --- a/e2e/system/file_test_image_cached_test.go +++ b/e2e/system/file_test_image_cached_test.go @@ -1,79 +1,67 @@ package system import ( + "context" "fmt" "sync" - "testing" - "github.com/stretchr/testify/assert" - - "github.com/celestiaorg/knuu/e2e" - "github.com/celestiaorg/knuu/pkg/knuu" + "github.com/celestiaorg/knuu/pkg/instance" ) -func TestFileCached(t *testing.T) { - t.Parallel() +func (s *Suite) TestFileCached() { + const namePrefix = "file-cached" + s.T().Parallel() // Setup - executor, err := knuu.NewExecutor() - if err != nil { - t.Fatalf("Error creating executor: %v", err) - } + ctx := context.Background() + executor, err := s.Executor.NewInstance(ctx, namePrefix+"-executor") + s.Require().NoError(err) const numberOfInstances = 10 - instances := make([]*knuu.Instance, numberOfInstances) + instances := make([]*instance.Instance, numberOfInstances) + + instanceName := func(i int) string { + return fmt.Sprintf("%s-web%d", namePrefix, i+1) + } for i := 0; i < numberOfInstances; i++ { - instanceName := fmt.Sprintf("web%d", i+1) - instances[i] = e2e.AssertCreateInstanceNginxWithVolumeOwner(t, instanceName) + instances[i] = s.createNginxInstanceWithVolume(ctx, instanceName(i)) } var wgFolders sync.WaitGroup - for i, instance := range instances { + for i, ins := range instances { wgFolders.Add(1) - go func(i int, instance *knuu.Instance) { + go func(i int, instance *instance.Instance) { defer wgFolders.Done() - instanceName := fmt.Sprintf("web%d", i+1) // adding the folder after the Commit, it will help us to use a cached image. - err = instance.AddFile("resources/html/index.html", "/usr/share/nginx/html/index.html", "0:0") - if err != nil { - t.Errorf("Error adding file to '%v': %v", instanceName, err) - } - }(i, instance) + err = instance.AddFile(resourcesHTML+"/index.html", nginxHTMLPath+"/index.html", "0:0") + s.Require().NoError(err, "adding file to '%v'", instanceName(i)) + }(i, ins) } wgFolders.Wait() - t.Cleanup(func() { - // Cleanup - err := e2e.AssertCleanupInstances(t, executor, instances) + s.T().Cleanup(func() { + all := append(instances, executor) + err := instance.BatchDestroy(ctx, all...) if err != nil { - t.Fatalf("Error cleaning up: %v", err) + s.T().Logf("error destroying instance: %v", err) } }) // Test logic - for _, instance := range instances { - err = instance.StartAsync() - if err != nil { - t.Fatalf("Error waiting for instance to be running: %v", err) - } + for _, i := range instances { + s.Require().NoError(i.Commit()) + s.Require().NoError(i.StartAsync(ctx)) } - for _, instance := range instances { - webIP, err := instance.GetIP() - if err != nil { - t.Fatalf("Error getting IP: %v", err) - } + for _, i := range instances { + webIP, err := i.GetIP(ctx) + s.Require().NoError(err) - err = instance.WaitInstanceIsRunning() - if err != nil { - t.Fatalf("Error waiting for instance to be running: %v", err) - } + s.Require().NoError(i.WaitInstanceIsRunning(ctx)) - wget, err := executor.ExecuteCommand("wget", "-q", "-O", "-", webIP) - if err != nil { - t.Fatalf("Error executing command: %v", err) - } + wget, err := executor.ExecuteCommand(ctx, "wget", "-q", "-O", "-", webIP) + s.Require().NoError(err) - assert.Contains(t, wget, "Hello World!") + s.Assert().Contains(wget, "Hello World!") } } diff --git a/e2e/system/files_to_volumes_cm_test.go b/e2e/system/files_to_volumes_cm_test.go index 9fa7a11..fbae178 100644 --- a/e2e/system/files_to_volumes_cm_test.go +++ b/e2e/system/files_to_volumes_cm_test.go @@ -1,397 +1,283 @@ package system import ( + "context" "fmt" "strings" "sync" - "testing" - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/require" + "k8s.io/apimachinery/pkg/api/resource" - "github.com/celestiaorg/knuu/e2e" - "github.com/celestiaorg/knuu/pkg/knuu" + "github.com/celestiaorg/knuu/pkg/instance" ) // TestOneVolumeNoFiles tests the scenario where we have one volume and no files. // the initContainer command that it generates looks like: // no initContainer command, as there is no volumes, nor files. -func TestNoVolumesNoFiles(t *testing.T) { - t.Parallel() +func (s *Suite) TestNoVolumesNoFiles() { + const namePrefix = "no-volumes-no-files" + s.T().Parallel() // Setup - executor, err := knuu.NewExecutor() - if err != nil { - t.Fatalf("Error creating executor: %v", err) - } + ctx := context.Background() + executor, err := s.Executor.NewInstance(ctx, namePrefix+"-executor") + s.Require().NoError(err) - instanceName := "web-1" - instance, err := knuu.NewInstance(instanceName) - if err != nil { - t.Fatalf("Error creating instance '%v': %v", instanceName, err) - } - err = instance.SetImage("docker.io/nginx:latest") - if err != nil { - t.Fatalf("Error setting image for '%v': %v", instanceName, err) - } - err = instance.AddPortTCP(80) - if err != nil { - t.Fatalf("Error adding port for '%v': %v", instanceName, err) - } - err = instance.Commit() - if err != nil { - t.Fatalf("Error committing instance '%v': %v", instanceName, err) - } + target := s.createNginxInstance(ctx, namePrefix+"-target") + s.Require().NoError(target.Commit()) // Cleanup - t.Cleanup(func() { - err := e2e.AssertCleanupInstance(t, instance) + s.T().Cleanup(func() { + err := instance.BatchDestroy(ctx, executor, target) if err != nil { - t.Fatalf("Error cleaning up: %v", err) + s.T().Logf("error destroying instance: %v", err) } }) // Test logic - err = instance.StartAsync() - if err != nil { - t.Fatalf("Error waiting for instance to be running: %v", err) - } + s.Require().NoError(target.StartAsync(ctx)) - webIP, err := instance.GetIP() - if err != nil { - t.Fatalf("Error getting IP: %v", err) - } + webIP, err := target.GetIP(ctx) + s.Require().NoError(err) - err = instance.WaitInstanceIsRunning() - if err != nil { - t.Fatalf("Error waiting for instance to be running: %v", err) - } + s.Require().NoError(target.WaitInstanceIsRunning(ctx)) - wget, err := executor.ExecuteCommand("wget", "-q", "-O", "-", webIP) - if err != nil { - t.Fatalf("Error executing command: %v", err) - } + wget, err := executor.ExecuteCommand(ctx, "wget", "-q", "-O", "-", webIP) + s.Require().NoError(err) - assert.Contains(t, wget, "Welcome to nginx!") + s.Assert().Contains(wget, "Welcome to nginx!") } // TestOneVolumeNoFiles tests the scenario where we have one volume and no files. // the initContainer command that it generates looks like: // mkdir -p /knuu && if [ -d /opt/vol1 ] && [ \"$(ls -A /opt/vol1)\" ]; then cp -r /opt/vol1/* /knuu//opt/vol1 && chown -R 0:0 /knuu/* ;fi -func TestOneVolumeNoFiles(t *testing.T) { - t.Parallel() +func (s *Suite) TestOneVolumeNoFiles() { + const namePrefix = "one-volume-no-files" + s.T().Parallel() // Setup - executor, err := knuu.NewExecutor() - if err != nil { - t.Fatalf("Error creating executor: %v", err) - } + ctx := context.Background() + executor, err := s.Executor.NewInstance(ctx, namePrefix+"-executor") + s.Require().NoError(err) - instanceName := "web-1" - instance, err := knuu.NewInstance(instanceName) - if err != nil { - t.Fatalf("Error creating instance '%v': %v", instanceName, err) - } - err = instance.SetImage("docker.io/nginx:latest") - if err != nil { - t.Fatalf("Error setting image for '%v': %v", instanceName, err) - } - err = instance.AddPortTCP(80) - if err != nil { - t.Fatalf("Error adding port for '%v': %v", instanceName, err) - } - err = instance.AddVolumeWithOwner("/opt/vol1", "1Gi", 0) - if err != nil { - t.Fatalf("Error adding volume: %v", err) - } - err = instance.Commit() - if err != nil { - t.Fatalf("Error committing instance '%v': %v", instanceName, err) - } + target := s.createNginxInstance(ctx, namePrefix+"-target") - // Cleanup - t.Cleanup(func() { - err := e2e.AssertCleanupInstance(t, instance) + err = target.AddVolumeWithOwner("/opt/vol1", resource.MustParse("1Gi"), 0) + s.Require().NoError(err) + + s.Require().NoError(target.Commit()) + + s.T().Cleanup(func() { + err := instance.BatchDestroy(ctx, executor, target) if err != nil { - t.Fatalf("Error cleaning up: %v", err) + s.T().Logf("error destroying instance: %v", err) } }) // Test logic - err = instance.StartAsync() - if err != nil { - t.Fatalf("Error waiting for instance to be running: %v", err) - } + s.Require().NoError(target.StartAsync(ctx)) - webIP, err := instance.GetIP() - if err != nil { - t.Fatalf("Error getting IP: %v", err) - } + webIP, err := target.GetIP(ctx) + s.Require().NoError(err) - err = instance.WaitInstanceIsRunning() - if err != nil { - t.Fatalf("Error waiting for instance to be running: %v", err) - } + s.Require().NoError(target.WaitInstanceIsRunning(ctx)) - wget, err := executor.ExecuteCommand("wget", "-q", "-O", "-", webIP) - if err != nil { - t.Fatalf("Error executing command: %v", err) - } + wget, err := executor.ExecuteCommand(ctx, "wget", "-q", "-O", "-", webIP) + s.Require().NoError(err) - assert.Contains(t, wget, "Welcome to nginx!") + s.Assert().Contains(wget, "Welcome to nginx!") } // TestNoVolumesOneFile tests the scenario where we have no volumes and one file. // the initContainer command that it generates looks like: // no initContainer command, as we do not have volumes. -func TestNoVolumesOneFile(t *testing.T) { - t.Parallel() - // Setup - executor, err := knuu.NewExecutor() - if err != nil { - t.Fatalf("Error creating executor: %v", err) - } +func (s *Suite) TestNoVolumesOneFile() { + const ( + namePrefix = "no-volumes-one-file" + numberOfInstances = 2 + ) - const numberOfInstances = 2 - instances := make([]*knuu.Instance, numberOfInstances) + s.T().Parallel() + // Setup + ctx := context.Background() + executor, err := s.Executor.NewInstance(ctx, namePrefix+"-executor") + s.Require().NoError(err) + instances := make([]*instance.Instance, numberOfInstances) for i := 0; i < numberOfInstances; i++ { - instanceName := fmt.Sprintf("web%d", i+1) - instances[i] = e2e.AssertCreateInstanceNginxWithVolumeOwner(t, instanceName) + name := fmt.Sprintf("%s-%d", namePrefix, i+1) + instances[i] = s.createNginxInstance(ctx, name) } - var wgFolders sync.WaitGroup - errorChannel := make(chan error, len(instances)) + var ( + wgFolders sync.WaitGroup + ) - for i, instance := range instances { + for _, i := range instances { wgFolders.Add(1) - go func(i int, instance *knuu.Instance) { + go func(i *instance.Instance) { defer wgFolders.Done() - instanceName := fmt.Sprintf("web%d", i+1) // adding the folder after the Commit, it will help us to use a cached image. - err = instance.AddFile("resources/file_cm_to_folder/test_1", "/usr/share/nginx/html/index.html", "0:0") - if err != nil { - errorChannel <- fmt.Errorf("Error adding file to '%v': %v", instanceName, err) - return - } - errorChannel <- nil - }(i, instance) + err = i.AddFile(resourcesFileCMToFolder+"/test_1", nginxHTMLPath+"/index.html", "0:0") + s.Require().NoError(err, "adding file to '%v'", i.Name()) + }(i) } wgFolders.Wait() - close(errorChannel) - for err := range errorChannel { + s.T().Cleanup(func() { + all := append(instances, executor) + err := instance.BatchDestroy(ctx, all...) if err != nil { - t.Fatalf("%v", err) - } - } - - // Cleanup - t.Cleanup(func() { - err := e2e.AssertCleanupInstances(t, executor, instances) - if err != nil { - t.Fatalf("Error cleaning up: %v", err) + s.T().Logf("error destroying instance: %v", err) } }) // Test logic - for _, instance := range instances { - err = instance.StartAsync() - if err != nil { - t.Fatalf("Error waiting for instance to be running: %v", err) - } + for _, i := range instances { + s.Require().NoError(i.Commit()) + s.Require().NoError(i.StartAsync(ctx)) } - for _, instance := range instances { - webIP, err := instance.GetIP() - if err != nil { - t.Fatalf("Error getting IP: %v", err) - } + for _, i := range instances { + webIP, err := i.GetIP(ctx) + s.Require().NoError(err) - err = instance.WaitInstanceIsRunning() - if err != nil { - t.Fatalf("Error waiting for instance to be running: %v", err) - } + err = i.WaitInstanceIsRunning(ctx) + s.Require().NoError(err) - wget, err := executor.ExecuteCommand("wget", "-q", "-O", "-", webIP) - if err != nil { - t.Fatalf("Error executing command: %v", err) - } + wget, err := executor.ExecuteCommand(ctx, "wget", "-q", "-O", "-", webIP) + s.Require().NoError(err) wget = strings.TrimSpace(wget) - assert.Equal(t, "hello from 1", wget) + s.Assert().Equal("hello from 1", wget) } } // TestOneVolumeOneFile tests the scenario where we have one volume and one file. // the initContainer command that it generates looks like: // mkdir -p /knuu && mkdir -p /knuu/usr/share/nginx/html && chmod -R 777 /knuu//usr/share/nginx/html && if [ -d /usr/share/nginx/html ] && [ \"$(ls -A /usr/share/nginx/html)\" ]; then cp -r /usr/share/nginx/html/* /knuu//usr/share/nginx/html && chown -R 0:0 /knuu/* ;fi -func TestOneVolumeOneFile(t *testing.T) { - t.Parallel() +func (s *Suite) TestOneVolumeOneFile() { + const ( + namePrefix = "one-volume-one-file" + numberOfInstances = 2 + ) + s.T().Parallel() // Setup - executor, err := knuu.NewExecutor() - if err != nil { - t.Fatalf("Error creating executor: %v", err) - } - - const numberOfInstances = 2 - instances := make([]*knuu.Instance, numberOfInstances) + ctx := context.Background() + executor, err := s.Executor.NewInstance(ctx, namePrefix+"-executor") + s.Require().NoError(err) + instances := make([]*instance.Instance, numberOfInstances) for i := 0; i < numberOfInstances; i++ { - instanceName := fmt.Sprintf("web%d", i+1) - instances[i] = e2e.AssertCreateInstanceNginxWithVolumeOwner(t, instanceName) + name := fmt.Sprintf("%s-%d", namePrefix, i+1) + instances[i] = s.createNginxInstanceWithVolume(ctx, name) } var wgFolders sync.WaitGroup - errorChannel := make(chan error, len(instances)) - - for i, instance := range instances { + for _, i := range instances { wgFolders.Add(1) - go func(i int, instance *knuu.Instance) { + go func(ins *instance.Instance) { defer wgFolders.Done() - instanceName := fmt.Sprintf("web%d", i+1) // adding the folder after the Commit, it will help us to use a cached image. - err := instance.AddFile("resources/file_cm_to_folder/test_1", "/usr/share/nginx/html/index.html", "0:0") - if err != nil { - errorChannel <- fmt.Errorf("Error adding file to '%v': %v", instanceName, err) - return - } - errorChannel <- nil - }(i, instance) + err = ins.AddFile(resourcesFileCMToFolder+"/test_1", nginxHTMLPath+"/index.html", "0:0") + s.Require().NoError(err, "adding file to '%v': %v", i.Name()) + }(i) } wgFolders.Wait() - close(errorChannel) - - for err := range errorChannel { - require.NoError(t, err) - } - t.Cleanup(func() { - // Cleanup - err := e2e.AssertCleanupInstances(t, executor, instances) + s.T().Cleanup(func() { + all := append(instances, executor) + err := instance.BatchDestroy(ctx, all...) if err != nil { - t.Fatalf("Error cleaning up: %v", err) + s.T().Logf("error destroying instance: %v", err) } }) // Test logic - for _, instance := range instances { - err = instance.StartAsync() - if err != nil { - t.Fatalf("Error waiting for instance to be running: %v", err) - } + for _, i := range instances { + s.Require().NoError(i.Commit()) + s.Require().NoError(i.StartAsync(ctx)) } - for _, instance := range instances { - webIP, err := instance.GetIP() - if err != nil { - t.Fatalf("Error getting IP: %v", err) - } + for _, i := range instances { + webIP, err := i.GetIP(ctx) + s.Require().NoError(err) + s.Require().NoError(i.WaitInstanceIsRunning(ctx)) - err = instance.WaitInstanceIsRunning() - if err != nil { - t.Fatalf("Error waiting for instance to be running: %v", err) - } - - wget, err := executor.ExecuteCommand("wget", "-q", "-O", "-", webIP) - if err != nil { - t.Fatalf("Error executing command: %v", err) - } + wget, err := executor.ExecuteCommand(ctx, "wget", "-q", "-O", "-", webIP) + s.Require().NoError(err) wget = strings.TrimSpace(wget) - assert.Equal(t, "hello from 1", wget) + s.Assert().Equal("hello from 1", wget) } } // TestOneVolumeOneFile tests the scenario where we have one volume and one file. // the initContainer command that it generates looks like: // mkdir -p /knuu && mkdir -p /knuu/usr/share/nginx/html && chmod -R 777 /knuu//usr/share/nginx/html && if [ -d /usr/share/nginx/html ] && [ \"$(ls -A /usr/share/nginx/html)\" ]; then cp -r /usr/share/nginx/html/* /knuu//usr/share/nginx/html && chown -R 0:0 /knuu/* ;fi -func TestOneVolumeTwoFiles(t *testing.T) { - t.Parallel() +func (s *Suite) TestOneVolumeTwoFiles() { + const ( + namePrefix = "one-volume-two-files" + numberOfInstances = 2 + ) + s.T().Parallel() // Setup - executor, err := knuu.NewExecutor() - if err != nil { - t.Fatalf("Error creating executor: %v", err) - } + ctx := context.Background() + executor, err := s.Executor.NewInstance(ctx, namePrefix+"-executor") + s.Require().NoError(err) - const numberOfInstances = 2 - instances := make([]*knuu.Instance, numberOfInstances) + instances := make([]*instance.Instance, numberOfInstances) for i := 0; i < numberOfInstances; i++ { - instanceName := fmt.Sprintf("web%d", i+1) - instances[i] = e2e.AssertCreateInstanceNginxWithVolumeOwner(t, instanceName) + name := fmt.Sprintf("%s-%d", namePrefix, i+1) + instances[i] = s.createNginxInstanceWithVolume(ctx, name) } var wgFolders sync.WaitGroup - errorChannel := make(chan error, len(instances)*2) // Allocate space for potential errors from each file addition in each instance - - for i, instance := range instances { + for _, i := range instances { wgFolders.Add(1) - go func(i int, instance *knuu.Instance) { + go func(i *instance.Instance) { defer wgFolders.Done() - instanceName := fmt.Sprintf("web%d", i+1) // adding the folder after the Commit, it will help us to use a cached image. - if err := instance.AddFile("resources/file_cm_to_folder/test_1", "/usr/share/nginx/html/index.html", "0:0"); err != nil { - errorChannel <- fmt.Errorf("Error adding file test_1 to '%v': %w", instanceName, err) - return - } - if err := instance.AddFile("resources/file_cm_to_folder/test_2", "/usr/share/nginx/html/index-2.html", "0:0"); err != nil { - errorChannel <- fmt.Errorf("Error adding file test_2 to '%v': %w", instanceName, err) - return - } - }(i, instance) - } - wgFolders.Wait() - close(errorChannel) + err := i.AddFile(resourcesFileCMToFolder+"/test_1", nginxHTMLPath+"/index.html", "0:0") + s.Require().NoError(err, "adding file to '%v'", i.Name()) - // Handle errors from the error channel - for err := range errorChannel { - require.NoError(t, err) + err = i.AddFile(resourcesFileCMToFolder+"/test_2", nginxHTMLPath+"/index-2.html", "0:0") + s.Require().NoError(err, "adding file to '%v'", i.Name()) + }(i) } + wgFolders.Wait() - t.Cleanup(func() { - // Cleanup - err := e2e.AssertCleanupInstances(t, executor, instances) + s.T().Cleanup(func() { + all := append(instances, executor) + err := instance.BatchDestroy(ctx, all...) if err != nil { - t.Fatalf("Error cleaning up: %v", err) + s.T().Logf("error destroying instance: %v", err) } }) // Test logic - for _, instance := range instances { - err = instance.StartAsync() - if err != nil { - t.Fatalf("Error waiting for instance to be running: %v", err) - } + for _, i := range instances { + s.Require().NoError(i.Commit()) + s.Require().NoError(i.StartAsync(ctx)) } - for _, instance := range instances { - webIP, err := instance.GetIP() - if err != nil { - t.Fatalf("Error getting IP: %v", err) - } + for _, i := range instances { + webIP, err := i.GetIP(ctx) + s.Require().NoError(err) + s.Require().NoError(i.WaitInstanceIsRunning(ctx)) - err = instance.WaitInstanceIsRunning() - if err != nil { - t.Fatalf("Error waiting for instance to be running: %v", err) - } - - wgetIndex, err := executor.ExecuteCommand("wget", "-q", "-O", "-", webIP) - if err != nil { - t.Fatalf("Error executing command: %v", err) - } + wgetIndex, err := executor.ExecuteCommand(ctx, "wget", "-q", "-O", "-", webIP) + s.Require().NoError(err) wgetIndex = strings.TrimSpace(wgetIndex) + s.Assert().Equal("hello from 1", wgetIndex) webIP2 := webIP + "/index-2.html" - wgetIndex2, err := executor.ExecuteCommand("wget", "-q", "-O", "-", webIP2) - if err != nil { - t.Fatalf("Error executing command: %v", err) - } + wgetIndex2, err := executor.ExecuteCommand(ctx, "wget", "-q", "-O", "-", webIP2) + s.Require().NoError(err) wgetIndex2 = strings.TrimSpace(wgetIndex2) - - assert.Equal(t, "hello from 1", wgetIndex) - assert.Equal(t, "hello from 2", wgetIndex2) + s.Assert().Equal("hello from 2", wgetIndex2) } } diff --git a/e2e/system/folder_test.go b/e2e/system/folder_test.go index 75833ff..6200756 100644 --- a/e2e/system/folder_test.go +++ b/e2e/system/folder_test.go @@ -1,59 +1,43 @@ package system import ( - "testing" + "context" - "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" - "github.com/celestiaorg/knuu/e2e" - "github.com/celestiaorg/knuu/pkg/knuu" + "github.com/celestiaorg/knuu/pkg/instance" ) -func TestFolder(t *testing.T) { - t.Parallel() +func (s *Suite) TestFolder() { + const namePrefix = "folder" + s.T().Parallel() + // Setup + ctx := context.Background() + executor, err := s.Executor.NewInstance(ctx, namePrefix) + require.NoError(s.T(), err) + + web := s.createNginxInstanceWithVolume(ctx, namePrefix) + err = web.AddFolder(resourcesHTML, nginxHTMLPath, "0:0") + require.NoError(s.T(), err) + + require.NoError(s.T(), web.Commit()) - executor, err := knuu.NewExecutor() - if err != nil { - t.Fatalf("Error creating executor: %v", err) - } - - // Create and commit the instance - instanceName := "web" - web := e2e.AssertCreateInstanceNginxWithVolumeOwnerWithoutCommit(t, instanceName) - err = web.AddFolder("resources/html", "/usr/share/nginx/html", "0:0") - if err != nil { - t.Fatalf("Error adding file to '%v': %v", instanceName, err) - } - err = web.Commit() - if err != nil { - t.Fatalf("Error committing instance '%v': %v", instanceName, err) - } - - t.Cleanup(func() { - require.NoError(t, knuu.BatchDestroy(executor.Instance, web)) + s.T().Cleanup(func() { + err := instance.BatchDestroy(ctx, web, executor) + if err != nil { + s.T().Logf("Error destroying instance: %v", err) + } }) // Test logic - webIP, err := web.GetIP() - if err != nil { - t.Fatalf("Error getting IP '%v':", err) - } - - err = web.Start() - if err != nil { - t.Fatalf("Error starting instance: %v", err) - } - err = web.WaitInstanceIsRunning() - if err != nil { - t.Fatalf("Error waiting for instance to be running: %v", err) - } - - wget, err := executor.ExecuteCommand("wget", "-q", "-O", "-", webIP) - if err != nil { - t.Fatalf("Error executing command '%v':", err) - } - - assert.Contains(t, wget, "Hello World!") + webIP, err := web.GetIP(ctx) + s.Require().NoError(err) + + s.Require().NoError(web.Start(ctx)) + + wget, err := executor.ExecuteCommand(ctx, "wget", "-q", "-O", "-", webIP) + s.Require().NoError(err) + + s.Assert().Contains(wget, "Hello World!") } diff --git a/e2e/system/folder_test_image_cached_test.go b/e2e/system/folder_test_image_cached_test.go index 0df91db..b778fb3 100644 --- a/e2e/system/folder_test_image_cached_test.go +++ b/e2e/system/folder_test_image_cached_test.go @@ -1,80 +1,67 @@ package system import ( + "context" "fmt" "sync" - "testing" - "github.com/stretchr/testify/assert" - - "github.com/celestiaorg/knuu/e2e" - "github.com/celestiaorg/knuu/pkg/knuu" + "github.com/celestiaorg/knuu/pkg/instance" ) -func TestFolderCached(t *testing.T) { - t.Parallel() +func (s *Suite) TestFolderCached() { + const ( + namePrefix = "folder-cached" + numberOfInstances = 10 + ) + s.T().Parallel() // Setup - executor, err := knuu.NewExecutor() - if err != nil { - t.Fatalf("Error creating executor: %v", err) - } - - // Test logic - const numberOfInstances = 10 - instances := make([]*knuu.Instance, numberOfInstances) + ctx := context.Background() + executor, err := s.Executor.NewInstance(ctx, namePrefix+"-executor") + s.Require().NoError(err) + instances := make([]*instance.Instance, numberOfInstances) for i := 0; i < numberOfInstances; i++ { - instanceName := fmt.Sprintf("web%d", i+1) - instances[i] = e2e.AssertCreateInstanceNginxWithVolumeOwner(t, instanceName) + name := fmt.Sprintf("%s-%d", namePrefix, i+1) + instances[i] = s.createNginxInstanceWithVolume(ctx, name) } var wgFolders sync.WaitGroup - for _, instance := range instances { + for _, i := range instances { wgFolders.Add(1) - go func(instance *knuu.Instance) { + go func(i *instance.Instance) { defer wgFolders.Done() // adding the folder after the Commit, it will help us to use a cached image. - err := instance.AddFolder("resources/html", "/usr/share/nginx/html", "0:0") - if err != nil { - t.Errorf("Error adding file to '%v': %v", instance.Name(), err) - } - }(instance) + err := i.AddFolder(resourcesHTML, nginxHTMLPath, "0:0") + s.Require().NoError(err, "adding file to '%v'", i.Name()) + }(i) } wgFolders.Wait() // Cleanup - t.Cleanup(func() { - err := e2e.AssertCleanupInstances(t, executor, instances) + s.T().Cleanup(func() { + all := append(instances, executor) + err := instance.BatchDestroy(ctx, all...) if err != nil { - t.Fatalf("Error cleaning up: %v", err) + s.T().Logf("error destroying instance: %v", err) } }) // Test logic - for _, instance := range instances { - err = instance.StartAsync() - if err != nil { - t.Fatalf("Error waiting for instance to be running: %v", err) - } + for _, i := range instances { + s.Require().NoError(i.Commit()) + s.Require().NoError(i.StartAsync(ctx)) } - for _, instance := range instances { - webIP, err := instance.GetIP() - if err != nil { - t.Fatalf("Error getting IP: %v", err) - } + for _, i := range instances { + webIP, err := i.GetIP(ctx) + s.Require().NoError(err) - err = instance.WaitInstanceIsRunning() - if err != nil { - t.Fatalf("Error waiting for instance to be running: %v", err) - } + s.Require().NoError(i.WaitInstanceIsRunning(ctx)) - wget, err := executor.ExecuteCommand("wget", "-q", "-O", "-", webIP) - if err != nil { - t.Fatalf("Error executing command: %v", err) - } + wget, err := executor.ExecuteCommand(ctx, "wget", "-q", "-O", "-", webIP) + s.Require().NoError(err) - assert.Contains(t, wget, "Hello World!") + s.Assert().Contains(wget, "Hello World!") } } diff --git a/e2e/system/main_test.go b/e2e/system/main_test.go deleted file mode 100644 index b8f33f1..0000000 --- a/e2e/system/main_test.go +++ /dev/null @@ -1,20 +0,0 @@ -package system - -import ( - "os" - "testing" - - "github.com/sirupsen/logrus" - - "github.com/celestiaorg/knuu/pkg/knuu" -) - -func TestMain(m *testing.M) { - err := knuu.Initialize() - if err != nil { - logrus.Fatalf("error initializing knuu: %v", err) - } - logrus.Infof("Scope: %s", knuu.Scope()) - exitVal := m.Run() - os.Exit(exitVal) -} diff --git a/e2e/system/start_callback_test.go b/e2e/system/start_callback_test.go index 9fd420a..db1b1de 100644 --- a/e2e/system/start_callback_test.go +++ b/e2e/system/start_callback_test.go @@ -15,9 +15,6 @@ import ( const ( callbackName = "callback-test" - nginxImage = "nginx:latest" - nginxPort = 80 - nginxCommand = "nginx -g daemon off" sleepTimeBeforeReady = "1" // second ) diff --git a/e2e/system/suite_setup_test.go b/e2e/system/suite_setup_test.go new file mode 100644 index 0000000..058eb87 --- /dev/null +++ b/e2e/system/suite_setup_test.go @@ -0,0 +1,96 @@ +package system + +import ( + "context" + "testing" + + "github.com/sirupsen/logrus" + "github.com/stretchr/testify/suite" + "k8s.io/apimachinery/pkg/api/resource" + + "github.com/celestiaorg/knuu/e2e" + "github.com/celestiaorg/knuu/pkg/instance" + "github.com/celestiaorg/knuu/pkg/k8s" + "github.com/celestiaorg/knuu/pkg/knuu" + "github.com/celestiaorg/knuu/pkg/minio" +) + +const ( + nginxImage = "docker.io/nginx:latest" + nginxVolumeOwner = 0 + nginxPort = 80 + nginxHTMLPath = "/usr/share/nginx/html" + nginxCommand = "nginx -g daemon off" + + resourcesHTML = "resources/html" + resourcesFileCMToFolder = "resources/file_cm_to_folder" +) + +type Suite struct { + suite.Suite + Knuu *knuu.Knuu + Executor e2e.Executor +} + +var ( + nginxVolume = resource.MustParse("1Gi") +) + +func (s *Suite) SetupSuite() { + var ( + ctx = context.Background() + logger = logrus.New() + ) + + k8sClient, err := k8s.NewClient(ctx, knuu.DefaultScope(), logger) + s.Require().NoError(err, "Error creating k8s client") + + minioClient, err := minio.New(ctx, k8sClient, logger) + s.Require().NoError(err, "Error creating minio client") + + s.Knuu, err = knuu.New(ctx, knuu.Options{ + ProxyEnabled: true, + K8sClient: k8sClient, + MinioClient: minioClient, // needed for build from git tests + }) + s.Require().NoError(err) + + s.T().Logf("Scope: %s", s.Knuu.Scope) + s.Knuu.HandleStopSignal(ctx) + + s.Executor.Kn = s.Knuu +} + +func (s *Suite) TearDownSuite() { + s.T().Cleanup(func() { + logrus.Info("Tearing down test suite...") + err := s.Knuu.CleanUp(context.Background()) + if err != nil { + s.T().Logf("Error cleaning up test suite: %v", err) + } + }) +} + +func TestRunSuite(t *testing.T) { + suite.Run(t, new(Suite)) +} + +func (s *Suite) createNginxInstance(ctx context.Context, name string) *instance.Instance { + ins, err := s.Knuu.NewInstance(name) + s.Require().NoError(err) + + s.Require().NoError(ins.SetImage(ctx, nginxImage)) + s.Require().NoError(ins.AddPortTCP(nginxPort)) + + return ins +} + +func (s *Suite) createNginxInstanceWithVolume(ctx context.Context, name string) *instance.Instance { + ins := s.createNginxInstance(ctx, name) + + _, err := ins.ExecuteCommand(ctx, "mkdir", "-p", nginxHTMLPath) + s.Require().NoError(err) + + s.Require().NoError(ins.AddVolumeWithOwner(nginxHTMLPath, nginxVolume, nginxVolumeOwner)) + return ins +} diff --git a/pkg/instance/errors.go b/pkg/instance/errors.go index 7a1713a..34b9d63 100644 --- a/pkg/instance/errors.go +++ b/pkg/instance/errors.go @@ -136,7 +136,7 @@ var ( ErrSettingPrivilegedNotAllowed = errors.New("SettingPrivilegedNotAllowed", "setting privileged is only allowed in state 'Preparing' or 'Committed'. Current state is '%s") ErrAddingCapabilityNotAllowed = errors.New("AddingCapabilityNotAllowed", "adding capability is only allowed in state 'Preparing' or 'Committed'. Current state is '%s") ErrAddingCapabilitiesNotAllowed = errors.New("AddingCapabilitiesNotAllowed", "adding capabilities is only allowed in state 'Preparing' or 'Committed'. Current state is '%s") - ErrStartingNotAllowed = errors.New("StartingNotAllowed", "starting is only allowed in state 'Committed' or 'Stopped'. Current state of sidecar '%s' is '%s'") + ErrStartingNotAllowed = errors.New("StartingNotAllowed", "starting is only allowed in state 'Committed' or 'Stopped'. Current state of instance '%s' is '%s'") ErrStartingNotAllowedForSidecar = errors.New("StartingNotAllowedForSidecar", "starting is only allowed in state 'Committed' or 'Stopped'. Current state of sidecar '%s' is '%s") ErrStartingSidecarNotAllowed = errors.New("StartingSidecarNotAllowed", "starting a sidecar is not allowed") ErrAddingOtelCollectorSidecar = errors.New("AddingOtelCollectorSidecar", "error adding OpenTelemetry collector sidecar for instance '%s'") diff --git a/pkg/instance/instance.go b/pkg/instance/instance.go index a7ab69e..1d78bd0 100644 --- a/pkg/instance/instance.go +++ b/pkg/instance/instance.go @@ -1112,7 +1112,7 @@ func (i *Instance) StartWithCallback(ctx context.Context, callback func()) error // This function can only be called in the state 'Committed' or 'Stopped' func (i *Instance) StartAsync(ctx context.Context) error { if !i.IsInState(StateCommitted, StateStopped) { - return ErrStartingNotAllowed.WithParams(i.state.String()) + return ErrStartingNotAllowed.WithParams(i.k8sName, i.state.String()) } err := applyFunctionToInstances(i.sidecars, func(sidecar *Instance) error { if !sidecar.IsInState(StateCommitted, StateStopped) { @@ -1218,17 +1218,13 @@ func (i *Instance) WaitInstanceIsRunning(ctx context.Context) error { } // DisableNetwork disables the network of the instance -// This does not apply to executor instances // This function can only be called in the state 'Started' func (i *Instance) DisableNetwork(ctx context.Context) error { if !i.IsInState(StateStarted) { return ErrDisablingNetworkNotAllowed.WithParams(i.state.String()) } - executorSelectorMap := map[string]string{ - labelType: ExecutorInstance.String(), - } - err := i.K8sClient.CreateNetworkPolicy(ctx, i.k8sName, i.getLabels(), executorSelectorMap, executorSelectorMap) + err := i.K8sClient.CreateNetworkPolicy(ctx, i.k8sName, i.getLabels(), nil, nil) if err != nil { return ErrDisablingNetwork.WithParams(i.k8sName).Wrap(err) } diff --git a/pkg/instance/type.go b/pkg/instance/type.go index 1343883..1ebac35 100644 --- a/pkg/instance/type.go +++ b/pkg/instance/type.go @@ -7,7 +7,6 @@ type InstanceType int const ( UnknownInstance InstanceType = iota BasicInstance - ExecutorInstance TimeoutHandlerInstance ) @@ -16,8 +15,6 @@ func (s InstanceType) String() string { switch s { case BasicInstance: return "BasicInstance" - case ExecutorInstance: - return "ExecutorInstance" case TimeoutHandlerInstance: return "TimeoutHandlerInstance" case UnknownInstance: diff --git a/pkg/instance/type_test.go b/pkg/instance/type_test.go index 990f3c7..99dbc1f 100644 --- a/pkg/instance/type_test.go +++ b/pkg/instance/type_test.go @@ -16,11 +16,6 @@ func TestInstanceType(t *testing.T) { in: BasicInstance, // Input want: "BasicInstance", // Expected output }, - { - name: "ExecutorInstance", - in: ExecutorInstance, - want: "ExecutorInstance", - }, { name: "TimeoutHandlerInstance", in: TimeoutHandlerInstance, diff --git a/pkg/knuu/errors.go b/pkg/knuu/errors.go index b620420..aae1981 100644 --- a/pkg/knuu/errors.go +++ b/pkg/knuu/errors.go @@ -209,4 +209,5 @@ var ( ErrK8sClientNotSet = errors.New("K8sClientNotSet", "k8s client is not set") ErrScopeMistMatch = errors.New("ScopeMistMatch", "scope '%s' set in options does not match scope '%s' set by the k8sClient namespace") ErrHandleTimeout = errors.New("HandleTimeout", "error starting handle timeout") + ErrDeprecated = errors.New("Deprecated", "deprecated") ) diff --git a/pkg/knuu/instance.go b/pkg/knuu/instance.go index 7f8398a..c9e6e75 100644 --- a/pkg/knuu/instance.go +++ b/pkg/knuu/instance.go @@ -2,8 +2,6 @@ package knuu import ( - "context" - "github.com/celestiaorg/knuu/pkg/instance" "github.com/celestiaorg/knuu/pkg/preloader" ) @@ -12,10 +10,6 @@ func (k *Knuu) NewInstance(name string) (*instance.Instance, error) { return instance.New(name, k.SystemDependencies) } -func (k *Knuu) NewExecutor(ctx context.Context) (*instance.Executor, error) { - return instance.NewExecutor(ctx, k.SystemDependencies) -} - func (k *Knuu) NewPreloader() (*preloader.Preloader, error) { return preloader.New(k.SystemDependencies) } diff --git a/pkg/knuu/instance_old.go b/pkg/knuu/instance_old.go index 0667630..e9f361c 100644 --- a/pkg/knuu/instance_old.go +++ b/pkg/knuu/instance_old.go @@ -354,18 +354,7 @@ func (i *Instance) CustomResourceDefinitionExists(gvr *schema.GroupVersionResour // Deprecated: Use the new package knuu instead. func NewExecutor() (*Executor, error) { - if tmpKnuu == nil { - return nil, errors.New("tmpKnuu is not initialized") - } - e, err := tmpKnuu.NewExecutor(context.Background()) - if err != nil { - return nil, err - } - return &Executor{ - Instance: &Instance{ - Instance: *e.Instance, - }, - }, nil + return nil, ErrDeprecated } // Deprecated: Use the new package knuu instead. diff --git a/pkg/knuu/knuu_old.go b/pkg/knuu/knuu_old.go index ca2439e..db8eca3 100644 --- a/pkg/knuu/knuu_old.go +++ b/pkg/knuu/knuu_old.go @@ -160,3 +160,7 @@ func PushFileToMinio(ctx context.Context, contentName string, reader io.Reader) func GetMinioURL(ctx context.Context, contentName string) (string, error) { return tmpKnuu.MinioClient.GetURL(ctx, contentName, minioBucketName) } + +func GetKnuuObj() *Knuu { + return tmpKnuu +}