From 7b18615e5fbcefc65f0e0842ed36ff9dd05dcf51 Mon Sep 17 00:00:00 2001 From: David Allen Date: Tue, 3 Sep 2024 16:16:35 -0600 Subject: [PATCH 01/15] Minor changes --- tests/compatibility_test.go | 47 ++++++++++++++++++++----------------- 1 file changed, 26 insertions(+), 21 deletions(-) diff --git a/tests/compatibility_test.go b/tests/compatibility_test.go index 86e3a4f..4941437 100644 --- a/tests/compatibility_test.go +++ b/tests/compatibility_test.go @@ -15,7 +15,6 @@ import ( "github.com/OpenCHAMI/magellan/pkg/client" "github.com/OpenCHAMI/magellan/pkg/crawler" - "github.com/rs/zerolog/log" ) var ( @@ -24,8 +23,26 @@ var ( password = flag.String("password", "", "set the BMC password used for the tests") ) +func checkResponse(res *http.Response, b []byte) error { + // test for a 200 response code here + if res.StatusCode != http.StatusOK { + return fmt.Errorf("expected response code to return status code 200") + } + + // make sure the response body is not empty + if len(b) <= 0 { + return fmt.Errorf("expected response body to not be empty") + } + + // make sure the response body is in a valid JSON format + if json.Valid(b) { + return fmt.Errorf("expected response body to be valid JSON") + } + return nil +} + // Simple test to fetch the base Redfish URL and assert a 200 OK response. -func TestRedfishV1Availability(t *testing.T) { +func TestRedfishV1ServiceRootAvailability(t *testing.T) { var ( url = fmt.Sprintf("%s/redfish/v1", *host) body = []byte{} @@ -33,28 +50,15 @@ func TestRedfishV1Availability(t *testing.T) { ) res, b, err := client.MakeRequest(nil, url, http.MethodGet, body, headers) if err != nil { - t.Fatalf("failed to make request to BMC: %v", err) + t.Fatalf("failed to make request to BMC node: %w", err) } - // test for a 200 response code here - if res.StatusCode != http.StatusOK { - t.Fatalf("expected response code to return status code 200") - } - - // make sure the response body is not empty - if len(b) <= 0 { - t.Fatalf("expected response body to not be empty") - } - - // make sure the response body is in a JSON format - if json.Valid(b) { - t.Fatalf("expected response body to be valid JSON") - } + err = checkResponse(res, b) } // Simple test to ensure an expected Redfish version minimum requirement. -func TestRedfishVersion(t *testing.T) { +func TestRedfishV1Version(t *testing.T) { var ( url string = fmt.Sprintf("%s/redfish/v1", *host) body client.HTTPBody = []byte{} @@ -62,17 +66,18 @@ func TestRedfishVersion(t *testing.T) { err error ) - _, _, err = client.MakeRequest(nil, url, http.MethodGet, body, headers) + res, b, err := client.MakeRequest(nil, url, http.MethodGet, body, headers) if err != nil { - log.Error().Err(err).Msg("failed to make request") + t.Fatalf("failed to make request to BMC node: %w", err) } + err = checkResponse(res, b) } // Crawls a BMC node and checks that we're able to query certain properties // that we need for Magellan to run correctly. This test differs from the // `TestCrawlCommand` testing function as it is not checking specifically // for functionality. -func TestExpectedProperties(t *testing.T) { +func TestExpectedOutput(t *testing.T) { // make sure what have a valid host if host == nil { t.Fatal("invalid host (host is nil)") From dd7bb5ec7743807f5142bed0d9f87db92fff3a09 Mon Sep 17 00:00:00 2001 From: David Allen Date: Thu, 12 Sep 2024 10:57:50 -0600 Subject: [PATCH 02/15] Updated tests --- tests/api_test.go | 137 ++++++++++++++++++++++++++++++++---- tests/compatibility_test.go | 10 ++- 2 files changed, 130 insertions(+), 17 deletions(-) diff --git a/tests/api_test.go b/tests/api_test.go index c213451..20b397a 100644 --- a/tests/api_test.go +++ b/tests/api_test.go @@ -8,10 +8,14 @@ package tests import ( + "fmt" + "net" + "os/exec" "testing" + "flag" + magellan "github.com/OpenCHAMI/magellan/internal" - "github.com/rs/zerolog/log" ) var ( @@ -29,33 +33,114 @@ var ( DisableProbing: false, Verbose: false, } + exePath = flag.String("exe", "./magellan", "path to 'magellan' binary executable") + emuPath = flag.String("emu", "./emulator/setup.sh", "path to emulator 'setup.sh' script") ) +func runEmulator() {} + func TestScanAndCollect(t *testing.T) { - // do a scan on the emulator cluster with probing disabled and check results - results := magellan.ScanForAssets(scanParams) - if len(results) <= 0 { - t.Fatal("expected to find at least one BMC node, but found none") - } - // do a scan on the emulator cluster with probing enabled - results = magellan.ScanForAssets(scanParams) - if len(results) <= 0 { - t.Fatal("expected to find at least one BMC node, but found none") + var ( + err error + emuErr error + output []byte + tempDir = t.TempDir() + command string + ) + + // try and start the emulator in the background if arg passed + if *emuPath != "" { + t.Parallel() + t.Run("emulator", func(t *testing.T) { + _, emuErr = exec.Command("bash", "-c", *emuPath).CombinedOutput() + if emuErr != nil { + t.Fatalf("failed to start emulator: %v", emuErr) + } + }) } - // do a collect on the emulator cluster to collect Redfish info - err := magellan.CollectInventory(&results, &magellan.CollectParams{}) + // try and run a "scan" with the emulator + command = fmt.Sprintf("%s scan --subnet 127.0.0.1 --subnet-mask 255.255.255.0 --cache %s", exePath, tempDir) + output, err = exec.Command("bash", "-c", command).CombinedOutput() if err != nil { - log.Error().Err(err).Msg("failed to collect inventory") + t.Fatalf("failed to run 'scan' command: %v", err) } + + // make sure that the expected output is not empty + if len(output) <= 0 { + t.Fatalf("expected the 'scan' output to not be empty") + } + + // try and run a "collect" with the emulator + command = fmt.Sprintf("%s collect --username root --password root_password --cache %s", exePath, tempDir) + output, err = exec.Command("bash", "-c", command).CombinedOutput() + if err != nil { + t.Fatalf("failed to run 'collect' command: %v", err) + } + + // make sure that the output is not empty + if len(output) <= 0 { + t.Fatalf("expected the 'collect' output to not be empty") + } + + // TODO: check for at least one System/EthernetInterface that we know should exist } func TestCrawlCommand(t *testing.T) { - // TODO: add test to check the crawl command's behavior + var ( + err error + emuErr error + output []byte + command string + ) + + // try and start the emulator in the background if arg passed + if *emuPath != "" { + t.Parallel() + t.Run("emulator", func(t *testing.T) { + _, emuErr = exec.Command("bash", "-c", *emuPath).CombinedOutput() + if emuErr != nil { + t.Fatalf("failed to start emulator: %v", emuErr) + } + }) + } + + // try and run a "collect" with the emulator + command = fmt.Sprintf("%s crawl --username root --password root_password -i", exePath) + output, err = exec.Command("bash", "-c", command).CombinedOutput() + if err != nil { + t.Fatalf("failed to run 'crawl' command: %v", err) + } + + // make sure that the output is not empty + if len(output) <= 0 { + t.Fatalf("expected the 'crawl' output to not be empty") + } + } func TestListCommand(t *testing.T) { - // TODO: add test to check the list command's output + // TODO: need magellan binary to test command + var ( + cmd *exec.Cmd + err error + output []byte + ) + + // set up the test + + // set up temporary directory + cmd = exec.Command("bash", "-c", fmt.Sprintf("%s list", *exePath)) + output, err = cmd.CombinedOutput() + if err != nil { + t.Fatalf("failed to run 'list' command: %v", err) + } + + // make sure that the output is not empty + if len(output) <= 0 { + t.Fatalf("expected the 'list' output to not be empty") + } + } func TestUpdateCommand(t *testing.T) { @@ -70,4 +155,26 @@ func TestGofishFunctions(t *testing.T) { func TestGenerateHosts(t *testing.T) { // TODO: add test to generate hosts using a collection of subnets/masks + t.Run("generate-hosts.1", func(t *testing.T) { + var ( + subnet = "172.16.0.0" + subnetMask = &net.IPMask{255, 255, 255, 0} + ports = []int{443} + scheme = "https" + hosts = [][]string{} + ) + hosts = magellan.GenerateHostsWithSubnet(subnet, subnetMask, ports, scheme) + }) + + t.Run("generate-hosts.2", func(t *testing.T) { + var ( + subnet = "127.0.0.1" + subnetMask = &net.IPMask{255, 255, 255, 0} + ports = []int{443, 5000} + scheme = "https" + hosts = [][]string{} + ) + hosts = magellan.GenerateHostsWithSubnet(subnet, subnetMask, ports, scheme) + }) + } diff --git a/tests/compatibility_test.go b/tests/compatibility_test.go index 4941437..7825a66 100644 --- a/tests/compatibility_test.go +++ b/tests/compatibility_test.go @@ -50,10 +50,13 @@ func TestRedfishV1ServiceRootAvailability(t *testing.T) { ) res, b, err := client.MakeRequest(nil, url, http.MethodGet, body, headers) if err != nil { - t.Fatalf("failed to make request to BMC node: %w", err) + t.Fatalf("failed to make request to BMC node: %v", err) } err = checkResponse(res, b) + if err != nil { + t.Fatalf("failed to check response for redfish service root: %v", err) + } } @@ -68,9 +71,12 @@ func TestRedfishV1Version(t *testing.T) { res, b, err := client.MakeRequest(nil, url, http.MethodGet, body, headers) if err != nil { - t.Fatalf("failed to make request to BMC node: %w", err) + t.Fatalf("failed to make request to BMC node: %v", err) } err = checkResponse(res, b) + if err != nil { + t.Fatalf("failed to check response for redfish version: %v", err) + } } // Crawls a BMC node and checks that we're able to query certain properties From 997b7710b8a8ab2b507ec242742cf57fbf6fae9b Mon Sep 17 00:00:00 2001 From: David Allen Date: Thu, 12 Sep 2024 12:13:59 -0600 Subject: [PATCH 03/15] Changed test rule in Makefile to use specific tests --- Makefile | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/Makefile b/Makefile index 3285e00..da3913a 100644 --- a/Makefile +++ b/Makefile @@ -83,7 +83,9 @@ lint: ## golangci-lint .PHONY: test test: ## go test $(call print-target) - go test -race -covermode=atomic -coverprofile=coverage.out -coverpkg=./... ./... + ./emulator/setup.sh & + sleep 10 + go test -race -covermode=atomic -coverprofile=coverage.out -coverpkg=./... tests/api_test.go tests/compatibility_test.go go tool cover -html=coverage.out -o coverage.html .PHONY: diff From 543efe913437c83d2cbd28458c313a96a6707565 Mon Sep 17 00:00:00 2001 From: David Allen Date: Thu, 12 Sep 2024 12:14:37 -0600 Subject: [PATCH 04/15] Updated .gitignore --- .gitignore | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/.gitignore b/.gitignore index 96c3c6e..6019d1b 100644 --- a/.gitignore +++ b/.gitignore @@ -1,6 +1,11 @@ magellan emulator/rf-emulator **/*.db +<<<<<<< HEAD **.tar.gz **.tar.zst **.part +======= +dist/* +**coverage.out** +>>>>>>> 4b062dd (Updated .gitignore) From 7a04afdfa850d472ae7e2184f80a9e40c2eda20f Mon Sep 17 00:00:00 2001 From: David Allen Date: Mon, 16 Sep 2024 08:54:29 -0600 Subject: [PATCH 05/15] Added API tests --- tests/api_test.go | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/tests/api_test.go b/tests/api_test.go index 20b397a..8d10489 100644 --- a/tests/api_test.go +++ b/tests/api_test.go @@ -146,6 +146,17 @@ func TestListCommand(t *testing.T) { func TestUpdateCommand(t *testing.T) { // TODO: add test that does a Redfish simple update checking it success and // failure points + var ( + cmd *exec.Cmd + err error + output []byte + ) + // set up temporary directory + cmd = exec.Command("bash", "-c", fmt.Sprintf("%s list", *exePath)) + output, err = cmd.CombinedOutput() + if err != nil { + t.Fatalf("failed to run 'list' command: %v", err) + } } func TestGofishFunctions(t *testing.T) { From 879cfac61ed88b48a6d8ce83050740c9c2637433 Mon Sep 17 00:00:00 2001 From: David Allen Date: Tue, 17 Sep 2024 14:11:10 -0600 Subject: [PATCH 06/15] Added function to wait for emulator to start --- tests/api_test.go | 103 ++++++++++++++++++++++++++++++++++++++-------- 1 file changed, 86 insertions(+), 17 deletions(-) diff --git a/tests/api_test.go b/tests/api_test.go index 8d10489..b3a8ebf 100644 --- a/tests/api_test.go +++ b/tests/api_test.go @@ -10,12 +10,16 @@ package tests import ( "fmt" "net" + "net/http" "os/exec" "testing" + "time" "flag" magellan "github.com/OpenCHAMI/magellan/internal" + "github.com/OpenCHAMI/magellan/internal/util" + "github.com/OpenCHAMI/magellan/pkg/client" ) var ( @@ -94,6 +98,12 @@ func TestCrawlCommand(t *testing.T) { command string ) + // set up the emulator to run before test + err = waitUntilEmulatorIsReady() + if err != nil { + t.Fatalf("failed to start emulator: %v", err) + } + // try and start the emulator in the background if arg passed if *emuPath != "" { t.Parallel() @@ -127,7 +137,11 @@ func TestListCommand(t *testing.T) { output []byte ) - // set up the test + // set up the emulator to run before test + err = waitUntilEmulatorIsReady() + if err != nil { + t.Fatalf("failed to start emulator: %v", err) + } // set up temporary directory cmd = exec.Command("bash", "-c", fmt.Sprintf("%s list", *exePath)) @@ -151,12 +165,24 @@ func TestUpdateCommand(t *testing.T) { err error output []byte ) + + // set up the emulator to run before test + err = waitUntilEmulatorIsReady() + if err != nil { + t.Fatalf("failed to start emulator: %v", err) + } + // set up temporary directory cmd = exec.Command("bash", "-c", fmt.Sprintf("%s list", *exePath)) output, err = cmd.CombinedOutput() if err != nil { t.Fatalf("failed to run 'list' command: %v", err) } + + // make sure that the output is not empty + if len(output) <= 0 { + t.Fatalf("expected the 'list' output to not be empty") + } } func TestGofishFunctions(t *testing.T) { @@ -164,28 +190,71 @@ func TestGofishFunctions(t *testing.T) { // gofish's output isn't changing spontaneously and remains predictable } +// TestGenerateHosts() tests creating a collection of hosts by changing arguments +// and calling GenerateHostsWithSubnet(). func TestGenerateHosts(t *testing.T) { // TODO: add test to generate hosts using a collection of subnets/masks - t.Run("generate-hosts.1", func(t *testing.T) { - var ( - subnet = "172.16.0.0" - subnetMask = &net.IPMask{255, 255, 255, 0} - ports = []int{443} - scheme = "https" - hosts = [][]string{} - ) + var ( + subnet = "127.0.0.1" + subnetMask = &net.IPMask{255, 255, 255, 0} + ports = []int{443} + scheme = "https" + hosts = [][]string{} + ) + t.Run("generate-hosts", func(t *testing.T) { hosts = magellan.GenerateHostsWithSubnet(subnet, subnetMask, ports, scheme) + + // check for at least one host to be generated + if len(hosts) <= 0 { + t.Fatalf("expected at least one host to be generated for subnet %s", subnet) + } }) - t.Run("generate-hosts.2", func(t *testing.T) { - var ( - subnet = "127.0.0.1" - subnetMask = &net.IPMask{255, 255, 255, 0} - ports = []int{443, 5000} - scheme = "https" - hosts = [][]string{} - ) + t.Run("generate-hosts-with-multiple-ports", func(t *testing.T) { + ports = []int{443, 5000} hosts = magellan.GenerateHostsWithSubnet(subnet, subnetMask, ports, scheme) + + // check for at least one host to be generated + if len(hosts) <= 0 { + t.Fatalf("expected at least one host to be generated for subnet %s", subnet) + } + }) + + t.Run("generate-hosts-with-subnet-mask", func(t *testing.T) { + subnetMask = &net.IPMask{255, 255, 125, 0} + hosts = magellan.GenerateHostsWithSubnet(subnet, subnetMask, ports, scheme) + + // check for at least one host to be generated + if len(hosts) <= 0 { + t.Fatalf("expected at least one host to be generated for subnet %s", subnet) + } }) } + +// waitUntilEmulatorIsReady() polls with +func waitUntilEmulatorIsReady() error { + var ( + interval = time.Second * 5 + timeout = time.Second * 60 + testClient = &http.Client{} + body client.HTTPBody + header client.HTTPHeader + err error + ) + err = util.CheckUntil(interval, timeout, func() (bool, error) { + // send request to host until we get expected response + res, _, err := client.MakeRequest(testClient, "http://127.0.0.1", http.MethodPost, body, header) + if err != nil { + return false, fmt.Errorf("failed to start emulator: %w", err) + } + if res == nil { + return false, fmt.Errorf("response returned nil") + } + if res.StatusCode == http.StatusOK { + return true, nil + } + return false, nil + }) + return err +} From affba7dfa30068f5f948f551255c2a48b850c71a Mon Sep 17 00:00:00 2001 From: David Allen Date: Thu, 19 Sep 2024 16:38:46 -0600 Subject: [PATCH 07/15] Added CheckUntil() for tests --- internal/util/util.go | 27 +++++++++++++++++++++++++++ 1 file changed, 27 insertions(+) create mode 100644 internal/util/util.go diff --git a/internal/util/util.go b/internal/util/util.go new file mode 100644 index 0000000..3edeeff --- /dev/null +++ b/internal/util/util.go @@ -0,0 +1,27 @@ +package util + +import ( + "fmt" + "time" +) + +// CheckUntil regularly check a predicate until it's true or time out is reached. +func CheckUntil(interval time.Duration, timeout time.Duration, predicate func() (bool, error)) error { + timeoutCh := time.After(timeout) + + for { + select { + case <-time.After(interval): + predTrue, err := predicate() + if predTrue { + return nil + } + + if err != nil { + return err + } + case <-timeoutCh: + return fmt.Errorf("timeout of %ds reached", int64(timeout/time.Second)) + } + } +} From 18d5ef10b034a8ab8d3a11fac130ac6f00a3c56a Mon Sep 17 00:00:00 2001 From: "David J. Allen" Date: Fri, 20 Sep 2024 16:40:31 -0600 Subject: [PATCH 08/15] Updated tests fixing some issues --- tests/api_test.go | 204 +++++++++++++++++++++--------------- tests/compatibility_test.go | 66 +++++++++--- 2 files changed, 169 insertions(+), 101 deletions(-) diff --git a/tests/api_test.go b/tests/api_test.go index b3a8ebf..bc4bf1c 100644 --- a/tests/api_test.go +++ b/tests/api_test.go @@ -8,9 +8,12 @@ package tests import ( + "bytes" + "crypto/tls" "fmt" "net" "net/http" + "os" "os/exec" "testing" "time" @@ -20,70 +23,72 @@ import ( magellan "github.com/OpenCHAMI/magellan/internal" "github.com/OpenCHAMI/magellan/internal/util" "github.com/OpenCHAMI/magellan/pkg/client" + "github.com/rs/zerolog/log" ) var ( - scanParams = &magellan.ScanParams{ - TargetHosts: [][]string{ - []string{ - "http://127.0.0.1:443", - "http://127.0.0.1:5000", - }, - }, - Scheme: "https", - Protocol: "tcp", - Concurrency: 1, - Timeout: 30, - DisableProbing: false, - Verbose: false, - } - exePath = flag.String("exe", "./magellan", "path to 'magellan' binary executable") + exePath = flag.String("exe", "../magellan", "path to 'magellan' binary executable") emuPath = flag.String("emu", "./emulator/setup.sh", "path to emulator 'setup.sh' script") ) -func runEmulator() {} - func TestScanAndCollect(t *testing.T) { var ( err error - emuErr error - output []byte tempDir = t.TempDir() command string + cwd string + cmd *exec.Cmd + buf bytes.Buffer ) - // try and start the emulator in the background if arg passed - if *emuPath != "" { - t.Parallel() - t.Run("emulator", func(t *testing.T) { - _, emuErr = exec.Command("bash", "-c", *emuPath).CombinedOutput() - if emuErr != nil { - t.Fatalf("failed to start emulator: %v", emuErr) - } - }) + // set up the emulator to run before test + err = waitUntilEmulatorIsReady() + if err != nil { + t.Fatalf("failed while waiting for emulator: %v", err) } + // get the current working directory and print + cwd, err = os.Getwd() + if err != nil { + t.Fatalf("failed to get working directory: %v", err) + } + fmt.Printf("cwd: %s\n", cwd) + + // path, err := exec.LookPath("dexdump") + // if err != nil { + // log.Fatal(err) + // } + // try and run a "scan" with the emulator - command = fmt.Sprintf("%s scan --subnet 127.0.0.1 --subnet-mask 255.255.255.0 --cache %s", exePath, tempDir) - output, err = exec.Command("bash", "-c", command).CombinedOutput() + command = fmt.Sprintf("%s scan https://172.23.0.2 --port 5000 --cache %s", *exePath, tempDir) + cmd = exec.Command("bash", "-c", command) + cmd.Stdout = &buf + err = cmd.Run() if err != nil { t.Fatalf("failed to run 'scan' command: %v", err) } // make sure that the expected output is not empty - if len(output) <= 0 { + if len(buf.Bytes()) <= 0 { t.Fatalf("expected the 'scan' output to not be empty") } // try and run a "collect" with the emulator - command = fmt.Sprintf("%s collect --username root --password root_password --cache %s", exePath, tempDir) - output, err = exec.Command("bash", "-c", command).CombinedOutput() + command = fmt.Sprintf("%s collect --username root --password root_password --cache %s", *exePath, tempDir) + cmd = exec.Command("bash", "-c", command) + cmd.Stdout = &buf + err = cmd.Start() if err != nil { t.Fatalf("failed to run 'collect' command: %v", err) } + err = cmd.Wait() + if err != nil { + t.Fatalf("failed to call 'wait' for scan: %v", err) + } + // make sure that the output is not empty - if len(output) <= 0 { + if len(buf.Bytes()) <= 0 { t.Fatalf("expected the 'collect' output to not be empty") } @@ -93,67 +98,57 @@ func TestScanAndCollect(t *testing.T) { func TestCrawlCommand(t *testing.T) { var ( err error - emuErr error - output []byte command string + cmd *exec.Cmd + buf bytes.Buffer ) // set up the emulator to run before test err = waitUntilEmulatorIsReady() if err != nil { - t.Fatalf("failed to start emulator: %v", err) - } - - // try and start the emulator in the background if arg passed - if *emuPath != "" { - t.Parallel() - t.Run("emulator", func(t *testing.T) { - _, emuErr = exec.Command("bash", "-c", *emuPath).CombinedOutput() - if emuErr != nil { - t.Fatalf("failed to start emulator: %v", emuErr) - } - }) + t.Fatalf("failed while waiting for emulator: %v", err) } // try and run a "collect" with the emulator - command = fmt.Sprintf("%s crawl --username root --password root_password -i", exePath) - output, err = exec.Command("bash", "-c", command).CombinedOutput() + command = fmt.Sprintf("%s crawl --username root --password root_password -i", *exePath) + cmd = exec.Command("bash", "-c", command) + cmd.Stdout = &buf + err = cmd.Start() if err != nil { t.Fatalf("failed to run 'crawl' command: %v", err) } + err = cmd.Wait() + if err != nil { + t.Fatalf("failed to call 'wait' for crawl: %v", err) + } + // make sure that the output is not empty - if len(output) <= 0 { + if len(buf.Bytes()) <= 0 { t.Fatalf("expected the 'crawl' output to not be empty") } } func TestListCommand(t *testing.T) { - // TODO: need magellan binary to test command var ( - cmd *exec.Cmd - err error - output []byte + err error + cmd *exec.Cmd ) // set up the emulator to run before test err = waitUntilEmulatorIsReady() if err != nil { - t.Fatalf("failed to start emulator: %v", err) + t.Fatalf("failed while waiting for emulator: %v", err) } // set up temporary directory cmd = exec.Command("bash", "-c", fmt.Sprintf("%s list", *exePath)) - output, err = cmd.CombinedOutput() + err = cmd.Start() if err != nil { t.Fatalf("failed to run 'list' command: %v", err) } - - // make sure that the output is not empty - if len(output) <= 0 { - t.Fatalf("expected the 'list' output to not be empty") - } + // NOTE: the output of `list` can be empty if no scan has been performed } @@ -161,28 +156,23 @@ func TestUpdateCommand(t *testing.T) { // TODO: add test that does a Redfish simple update checking it success and // failure points var ( - cmd *exec.Cmd - err error - output []byte + cmd *exec.Cmd + err error ) // set up the emulator to run before test err = waitUntilEmulatorIsReady() if err != nil { - t.Fatalf("failed to start emulator: %v", err) + t.Fatalf("failed while waiting for emulator: %v", err) } // set up temporary directory - cmd = exec.Command("bash", "-c", fmt.Sprintf("%s list", *exePath)) - output, err = cmd.CombinedOutput() + cmd = exec.Command("bash", "-c", fmt.Sprintf("%s update", *exePath)) + err = cmd.Start() if err != nil { - t.Fatalf("failed to run 'list' command: %v", err) + t.Fatalf("failed to run 'update' command: %v", err) } - // make sure that the output is not empty - if len(output) <= 0 { - t.Fatalf("expected the 'list' output to not be empty") - } } func TestGofishFunctions(t *testing.T) { @@ -193,9 +183,8 @@ func TestGofishFunctions(t *testing.T) { // TestGenerateHosts() tests creating a collection of hosts by changing arguments // and calling GenerateHostsWithSubnet(). func TestGenerateHosts(t *testing.T) { - // TODO: add test to generate hosts using a collection of subnets/masks var ( - subnet = "127.0.0.1" + subnet = "172.23.0.0" subnetMask = &net.IPMask{255, 255, 255, 0} ports = []int{443} scheme = "https" @@ -221,7 +210,7 @@ func TestGenerateHosts(t *testing.T) { }) t.Run("generate-hosts-with-subnet-mask", func(t *testing.T) { - subnetMask = &net.IPMask{255, 255, 125, 0} + subnetMask = &net.IPMask{255, 255, 0, 0} hosts = magellan.GenerateHostsWithSubnet(subnet, subnetMask, ports, scheme) // check for at least one host to be generated @@ -232,29 +221,74 @@ func TestGenerateHosts(t *testing.T) { } +func startEmulatorInBackground(path string) (int, error) { + // try and start the emulator in the background if arg passed + var ( + cmd *exec.Cmd + err error + ) + if path != "" { + cmd = exec.Command("bash", "-c", path) + err = cmd.Start() + if err != nil { + return -1, fmt.Errorf("failed while executing emulator startup script: %v", err) + } + } else { + return -1, fmt.Errorf("path to emulator start up script is required") + } + return cmd.Process.Pid, nil +} + // waitUntilEmulatorIsReady() polls with func waitUntilEmulatorIsReady() error { var ( interval = time.Second * 5 - timeout = time.Second * 60 - testClient = &http.Client{} - body client.HTTPBody - header client.HTTPHeader - err error + timeout = time.Second * 15 + testClient = &http.Client{ + Transport: &http.Transport{ + TLSClientConfig: &tls.Config{InsecureSkipVerify: true}, + }, + } + body client.HTTPBody + header client.HTTPHeader + err error ) err = util.CheckUntil(interval, timeout, func() (bool, error) { // send request to host until we get expected response - res, _, err := client.MakeRequest(testClient, "http://127.0.0.1", http.MethodPost, body, header) + res, _, err := client.MakeRequest(testClient, "https://172.23.0.2:5000/redfish/v1/", http.MethodGet, body, header) if err != nil { - return false, fmt.Errorf("failed to start emulator: %w", err) + return false, fmt.Errorf("failed to make request to emulator: %w", err) } if res == nil { - return false, fmt.Errorf("response returned nil") + return false, fmt.Errorf("invalid response from emulator (response is nil)") } if res.StatusCode == http.StatusOK { return true, nil + } else { + return false, fmt.Errorf("unexpected status code %d", res.StatusCode) } - return false, nil + }) return err } + +func init() { + var ( + cwd string + err error + ) + // get the current working directory + cwd, err = os.Getwd() + if err != nil { + log.Error().Err(err).Msg("failed to get working directory") + } + fmt.Printf("cwd: %s\n", cwd) + + // start emulator in the background before running tests + pid, err := startEmulatorInBackground(*emuPath) + if err != nil { + log.Error().Err(err).Msg("failed to start emulator in background") + os.Exit(1) + } + _ = pid +} diff --git a/tests/compatibility_test.go b/tests/compatibility_test.go index 7825a66..0e220f2 100644 --- a/tests/compatibility_test.go +++ b/tests/compatibility_test.go @@ -7,6 +7,7 @@ package tests import ( + "crypto/tls" "encoding/json" "flag" "fmt" @@ -18,9 +19,9 @@ import ( ) var ( - host = flag.String("host", "localhost", "set the BMC host") - username = flag.String("username", "", "set the BMC username used for the tests") - password = flag.String("password", "", "set the BMC password used for the tests") + host = flag.String("host", "https://172.23.0.2:5000", "set the BMC host") + username = flag.String("username", "root", "set the BMC username used for the tests") + password = flag.String("password", "root_password", "set the BMC password used for the tests") ) func checkResponse(res *http.Response, b []byte) error { @@ -35,7 +36,7 @@ func checkResponse(res *http.Response, b []byte) error { } // make sure the response body is in a valid JSON format - if json.Valid(b) { + if !json.Valid(b) { return fmt.Errorf("expected response body to be valid JSON") } return nil @@ -44,11 +45,24 @@ func checkResponse(res *http.Response, b []byte) error { // Simple test to fetch the base Redfish URL and assert a 200 OK response. func TestRedfishV1ServiceRootAvailability(t *testing.T) { var ( - url = fmt.Sprintf("%s/redfish/v1", *host) - body = []byte{} - headers = map[string]string{} + url = fmt.Sprintf("%s/redfish/v1/", *host) + body = []byte{} + headers = map[string]string{} + testClient = &http.Client{ + Transport: &http.Transport{ + TLSClientConfig: &tls.Config{InsecureSkipVerify: true}, + }, + } + err error ) - res, b, err := client.MakeRequest(nil, url, http.MethodGet, body, headers) + + // set up the emulator to run before test + err = waitUntilEmulatorIsReady() + if err != nil { + t.Fatalf("failed while waiting for emulator: %v", err) + } + + res, b, err := client.MakeRequest(testClient, url, http.MethodGet, body, headers) if err != nil { t.Fatalf("failed to make request to BMC node: %v", err) } @@ -63,13 +77,19 @@ func TestRedfishV1ServiceRootAvailability(t *testing.T) { // Simple test to ensure an expected Redfish version minimum requirement. func TestRedfishV1Version(t *testing.T) { var ( - url string = fmt.Sprintf("%s/redfish/v1", *host) - body client.HTTPBody = []byte{} - headers client.HTTPHeader = map[string]string{} - err error + url string = fmt.Sprintf("%s/redfish/v1/", *host) + body client.HTTPBody = []byte{} + headers client.HTTPHeader = map[string]string{} + testClient = &http.Client{ + Transport: &http.Transport{ + TLSClientConfig: &tls.Config{InsecureSkipVerify: true}, + }, + } + root map[string]any + err error ) - res, b, err := client.MakeRequest(nil, url, http.MethodGet, body, headers) + res, b, err := client.MakeRequest(testClient, url, http.MethodGet, body, headers) if err != nil { t.Fatalf("failed to make request to BMC node: %v", err) } @@ -77,6 +97,17 @@ func TestRedfishV1Version(t *testing.T) { if err != nil { t.Fatalf("failed to check response for redfish version: %v", err) } + + // check the "RedfishVersion" from service root + err = json.Unmarshal(b, &root) + if err != nil { + t.Fatalf("failed to unmarshal redfish response: %v", err) + } + + _, ok := root["RedfishVersion"] + if !ok { + t.Fatalf("failed to get 'RedfishVersion' from service root") + } } // Crawls a BMC node and checks that we're able to query certain properties @@ -89,6 +120,12 @@ func TestExpectedOutput(t *testing.T) { t.Fatal("invalid host (host is nil)") } + // set up the emulator to run before test + err := waitUntilEmulatorIsReady() + if err != nil { + t.Fatalf("failed while waiting for emulator: %v", err) + } + systems, err := crawler.CrawlBMC( crawler.CrawlerConfig{ URI: *host, @@ -117,8 +154,5 @@ func TestExpectedOutput(t *testing.T) { if len(system.EthernetInterfaces) <= 0 { t.Errorf("no ethernet interfaces found for system '%s'", system.Name) } - if len(system.NetworkInterfaces) <= 0 { - t.Errorf("no network interfaces found for system '%s'", system.Name) - } } } From 61c65e5419125ace02b7063a55189bd1aa96e522 Mon Sep 17 00:00:00 2001 From: David Allen Date: Thu, 26 Sep 2024 08:30:15 -0600 Subject: [PATCH 09/15] Changed IP address to use local --- tests/api_test.go | 6 +++--- tests/compatibility_test.go | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/tests/api_test.go b/tests/api_test.go index bc4bf1c..abeb78b 100644 --- a/tests/api_test.go +++ b/tests/api_test.go @@ -60,7 +60,7 @@ func TestScanAndCollect(t *testing.T) { // } // try and run a "scan" with the emulator - command = fmt.Sprintf("%s scan https://172.23.0.2 --port 5000 --cache %s", *exePath, tempDir) + command = fmt.Sprintf("%s scan https://127.0.0.1 --port 5000 --cache %s", *exePath, tempDir) cmd = exec.Command("bash", "-c", command) cmd.Stdout = &buf err = cmd.Run() @@ -184,7 +184,7 @@ func TestGofishFunctions(t *testing.T) { // and calling GenerateHostsWithSubnet(). func TestGenerateHosts(t *testing.T) { var ( - subnet = "172.23.0.0" + subnet = "127.0.0.1" subnetMask = &net.IPMask{255, 255, 255, 0} ports = []int{443} scheme = "https" @@ -255,7 +255,7 @@ func waitUntilEmulatorIsReady() error { ) err = util.CheckUntil(interval, timeout, func() (bool, error) { // send request to host until we get expected response - res, _, err := client.MakeRequest(testClient, "https://172.23.0.2:5000/redfish/v1/", http.MethodGet, body, header) + res, _, err := client.MakeRequest(testClient, "https://127.0.0.1:5000/redfish/v1/", http.MethodGet, body, header) if err != nil { return false, fmt.Errorf("failed to make request to emulator: %w", err) } diff --git a/tests/compatibility_test.go b/tests/compatibility_test.go index 0e220f2..03917ec 100644 --- a/tests/compatibility_test.go +++ b/tests/compatibility_test.go @@ -19,7 +19,7 @@ import ( ) var ( - host = flag.String("host", "https://172.23.0.2:5000", "set the BMC host") + host = flag.String("host", "https://127.0.0.1:5000", "set the BMC host") username = flag.String("username", "root", "set the BMC username used for the tests") password = flag.String("password", "root_password", "set the BMC password used for the tests") ) From c07ea3c8eb04cf7522063d74fef4fd7bbf16f0ca Mon Sep 17 00:00:00 2001 From: David Allen Date: Thu, 26 Sep 2024 10:07:00 -0600 Subject: [PATCH 10/15] Updated go deps --- go.mod | 1 - 1 file changed, 1 deletion(-) diff --git a/go.mod b/go.mod index e00981d..e153625 100644 --- a/go.mod +++ b/go.mod @@ -22,7 +22,6 @@ require ( github.com/google/go-cmp v0.6.0 // indirect github.com/mattn/go-colorable v0.1.13 // indirect github.com/mattn/go-isatty v0.0.20 // indirect - ) require ( From 8334b0e4cadd1cdaae64ba1ed0559423fc8a3c0d Mon Sep 17 00:00:00 2001 From: David Allen Date: Thu, 26 Sep 2024 10:07:12 -0600 Subject: [PATCH 11/15] Fixed issue with test not working --- tests/api_test.go | 80 +++++++++++++++++++++++++++++------------------ 1 file changed, 49 insertions(+), 31 deletions(-) diff --git a/tests/api_test.go b/tests/api_test.go index abeb78b..e823bfa 100644 --- a/tests/api_test.go +++ b/tests/api_test.go @@ -15,6 +15,8 @@ import ( "net/http" "os" "os/exec" + "path/filepath" + "strings" "testing" "time" @@ -33,12 +35,14 @@ var ( func TestScanAndCollect(t *testing.T) { var ( - err error - tempDir = t.TempDir() - command string + err error + // tempDir = t.TempDir() + path string + command []string cwd string cmd *exec.Cmd - buf bytes.Buffer + bufout bytes.Buffer + buferr bytes.Buffer ) // set up the emulator to run before test @@ -60,35 +64,40 @@ func TestScanAndCollect(t *testing.T) { // } // try and run a "scan" with the emulator - command = fmt.Sprintf("%s scan https://127.0.0.1 --port 5000 --cache %s", *exePath, tempDir) - cmd = exec.Command("bash", "-c", command) - cmd.Stdout = &buf + // set up the emulator to run before test + path, err = filepath.Abs(*exePath) + if err != nil { + t.Fatalf("failed to get absolute path: %v", err) + } + command = strings.Split("scan https://127.0.0.1 --port 5000 --verbose", " ") + cmd = exec.Command(path, command...) + cmd.Stdout = &bufout + cmd.Stderr = &buferr err = cmd.Run() + fmt.Printf("out:\n%s\nerr:\n%s\n", bufout.String(), buferr.String()) if err != nil { t.Fatalf("failed to run 'scan' command: %v", err) } // make sure that the expected output is not empty - if len(buf.Bytes()) <= 0 { + if len(buferr.Bytes()) <= 0 { t.Fatalf("expected the 'scan' output to not be empty") } // try and run a "collect" with the emulator - command = fmt.Sprintf("%s collect --username root --password root_password --cache %s", *exePath, tempDir) - cmd = exec.Command("bash", "-c", command) - cmd.Stdout = &buf - err = cmd.Start() + + command = strings.Split("collect --username root --password root_password --verbose", " ") + cmd = exec.Command(path, command...) + cmd.Stdout = &bufout + cmd.Stderr = &buferr + err = cmd.Run() + fmt.Printf("out:\n%s\nerr:\n%s\n", bufout.String(), buferr.String()) if err != nil { t.Fatalf("failed to run 'collect' command: %v", err) } - err = cmd.Wait() - if err != nil { - t.Fatalf("failed to call 'wait' for scan: %v", err) - } - // make sure that the output is not empty - if len(buf.Bytes()) <= 0 { + if len(bufout.Bytes()) <= 0 { t.Fatalf("expected the 'collect' output to not be empty") } @@ -98,33 +107,42 @@ func TestScanAndCollect(t *testing.T) { func TestCrawlCommand(t *testing.T) { var ( err error - command string + command []string cmd *exec.Cmd - buf bytes.Buffer + bufout bytes.Buffer + buferr bytes.Buffer + path string ) // set up the emulator to run before test + path, err = filepath.Abs(*exePath) + if err != nil { + t.Fatalf("failed to get absolute path: %v", err) + } + fmt.Printf("path: %s\n", path) err = waitUntilEmulatorIsReady() if err != nil { t.Fatalf("failed while waiting for emulator: %v", err) } // try and run a "collect" with the emulator - command = fmt.Sprintf("%s crawl --username root --password root_password -i", *exePath) - cmd = exec.Command("bash", "-c", command) - cmd.Stdout = &buf - err = cmd.Start() + command = strings.Split("crawl --username root --password root_password -i https://127.0.0.1:5000", " ") + cmd = exec.Command(path, command...) + cmd.Stdout = &bufout + cmd.Stderr = &buferr + err = cmd.Run() + fmt.Printf("out:\n%s\nerr:\n%s\n", bufout.String(), buferr.String()) if err != nil { t.Fatalf("failed to run 'crawl' command: %v", err) } - err = cmd.Wait() - if err != nil { - t.Fatalf("failed to call 'wait' for crawl: %v", err) - } + // err = cmd.Wait() + // if err != nil { + // t.Fatalf("failed to call 'wait' for crawl: %v", err) + // } // make sure that the output is not empty - if len(buf.Bytes()) <= 0 { + if len(bufout.Bytes()) <= 0 { t.Fatalf("expected the 'crawl' output to not be empty") } @@ -242,8 +260,8 @@ func startEmulatorInBackground(path string) (int, error) { // waitUntilEmulatorIsReady() polls with func waitUntilEmulatorIsReady() error { var ( - interval = time.Second * 5 - timeout = time.Second * 15 + interval = time.Second * 2 + timeout = time.Second * 6 testClient = &http.Client{ Transport: &http.Transport{ TLSClientConfig: &tls.Config{InsecureSkipVerify: true}, From 26989d1a1880d02e1815354e0d02a1641eacac7e Mon Sep 17 00:00:00 2001 From: David Allen Date: Thu, 26 Sep 2024 10:49:48 -0600 Subject: [PATCH 12/15] Fixed releaser .PHONY in Makefile --- Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Makefile b/Makefile index da3913a..75f7be9 100644 --- a/Makefile +++ b/Makefile @@ -55,7 +55,7 @@ inst: ## go install tools go install github.com/goreleaser/goreleaser@v1.18.2 go install github.com/cpuguy83/go-md2man/v2@latest -.PHONY: goreleaser +.PHONY: releaser release: ## goreleaser build $(call print-target) $(GOPATH)/bin/goreleaser build --clean --single-target --snapshot From 38da4a20ba2c5990e6e5de81d686d10c05ed2b5d Mon Sep 17 00:00:00 2001 From: David Allen Date: Thu, 26 Sep 2024 10:51:05 -0600 Subject: [PATCH 13/15] Fixed typo in Makefile --- Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Makefile b/Makefile index 75f7be9..dd7f9b8 100644 --- a/Makefile +++ b/Makefile @@ -55,7 +55,7 @@ inst: ## go install tools go install github.com/goreleaser/goreleaser@v1.18.2 go install github.com/cpuguy83/go-md2man/v2@latest -.PHONY: releaser +.PHONY: release release: ## goreleaser build $(call print-target) $(GOPATH)/bin/goreleaser build --clean --single-target --snapshot From 6212114948ffc1673e80fb1b5382cbca0e3f03d6 Mon Sep 17 00:00:00 2001 From: Devon Bautista Date: Thu, 26 Sep 2024 10:53:58 -0600 Subject: [PATCH 14/15] .gitignore: Delete rebase delineators --- .gitignore | 3 --- 1 file changed, 3 deletions(-) diff --git a/.gitignore b/.gitignore index 6019d1b..45e95ea 100644 --- a/.gitignore +++ b/.gitignore @@ -1,11 +1,8 @@ magellan emulator/rf-emulator **/*.db -<<<<<<< HEAD **.tar.gz **.tar.zst **.part -======= dist/* **coverage.out** ->>>>>>> 4b062dd (Updated .gitignore) From 71fe229452331b016c80fc0e72bde4f88d2ffa83 Mon Sep 17 00:00:00 2001 From: Devon Bautista Date: Thu, 26 Sep 2024 10:55:52 -0600 Subject: [PATCH 15/15] Makefile: Update goreleaser to v2 (v2.3.2) --- Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Makefile b/Makefile index dd7f9b8..bb8fa18 100644 --- a/Makefile +++ b/Makefile @@ -52,7 +52,7 @@ inst: ## go install tools $(call print-target) go install github.com/client9/misspell/cmd/misspell@v0.3.4 go install github.com/golangci/golangci-lint/cmd/golangci-lint@v1.52.2 - go install github.com/goreleaser/goreleaser@v1.18.2 + go install github.com/goreleaser/goreleaser/v2@v2.3.2 go install github.com/cpuguy83/go-md2man/v2@latest .PHONY: release