Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Enable support for HTTP communication between components ECFLOW-1957 #121

Draft
wants to merge 24 commits into
base: develop
Choose a base branch
from
Draft
Changes from 1 commit
Commits
Show all changes
24 commits
Select commit Hold shift + click to select a range
8d73a55
Refactor DefaultServer class template
marcosbento Feb 20, 2024
9906a73
Enable HTTP connection between Client/Server
marcosbento Feb 21, 2024
28bc5e7
Enable UI to use HTTP connection
marcosbento Jun 20, 2024
ac25b60
Enable REST/UDP proxies to use HTTP connection
marcosbento Sep 4, 2024
47004a3
Enable ecFlow REST API tests using HTTP backend
marcosbento Oct 2, 2024
6899cfc
Improve TestFixture by extracting ScratchDir
marcosbento Oct 10, 2024
f736cee
Improve TestFixture by extracting LocalServerLauncher
marcosbento Oct 11, 2024
a32ab82
Enable s_test with HTTP backend
marcosbento Oct 11, 2024
75933f2
Simplify tests AbortCmd and WaitCmd
marcosbento Oct 11, 2024
8623e09
Correct typo in source code comment
marcosbento Oct 11, 2024
ef822e6
Replace deprecated use of BOOST_GLOBAL_FIXTURE
marcosbento Oct 11, 2024
4028343
Allow REST API tests to run in parallel
marcosbento Oct 15, 2024
9674040
Remove the use of std::filesystem
marcosbento Oct 15, 2024
ebffe69
Ensure parallel s_test* do not use same port or directory
marcosbento Oct 16, 2024
fab3359
Test ecFlow Python using HTTP ecFlow server
marcosbento Oct 17, 2024
abe99b6
Enable HTTPS on the Client side
marcosbento Oct 18, 2024
e3c2683
Use notion of selected Protocol
marcosbento Oct 18, 2024
f42dd5d
Move test methods in ClientInvoker to SCPort
marcosbento Oct 21, 2024
fcf00c7
Use notion of selected Protocol
marcosbento Oct 21, 2024
d68b5d9
Add description to HttpServer
marcosbento Oct 21, 2024
2ee69df
Correct #include guards
marcosbento Oct 21, 2024
d36d36b
Correct outstanding compilation warnings
marcosbento Oct 21, 2024
d3f94ee
Update default HTTP connection timeouts
marcosbento Oct 24, 2024
f0e0adf
Replace use of io.post() with boost::asio::post
marcosbento Jan 28, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Prev Previous commit
Next Next commit
Improve TestFixture by extracting ScratchDir
Re ECFLOW-1957
marcosbento committed Mar 12, 2025
commit 6899cfc00d13d14941853de5db2a515ffdcd28c7
4 changes: 2 additions & 2 deletions libs/test/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -65,7 +65,7 @@ if (ENABLE_ALL_TESTS)

ecbuild_add_test(
TARGET
s_test_http
s_test_using_http_backend
LABELS
integration
nightly
@@ -80,7 +80,7 @@ if (ENABLE_ALL_TESTS)
TEST_DEPENDS
s_client
)
target_clangformat(s_test_http CONDITION ENABLE_TESTS)
target_clangformat(s_test_using_http_backend CONDITION ENABLE_TESTS)

ecbuild_add_test(
TARGET
1 change: 1 addition & 0 deletions libs/test/harness/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -10,6 +10,7 @@

set(srcs
# Headers
ScratchDir.hpp
ServerTestHarness.hpp
TestFixture.hpp
ZombieUtil.hpp
69 changes: 69 additions & 0 deletions libs/test/harness/ScratchDir.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
/*
* Copyright 2009- ECMWF.
*
* This software is licensed under the terms of the Apache Licence version 2.0
* which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
* In applying this licence, ECMWF does not waive the privileges and immunities
* granted to it by virtue of its status as an intergovernmental organisation
* nor does it submit to any jurisdiction.
*/

#ifndef ecflow_test_harness_ScratchDir_HPP
#define ecflow_test_harness_ScratchDir_HPP

#include <cassert>
#include <filesystem>
#include <string>

#include "ecflow/core/Environment.hpp"

/**
* This class is used to create a scratch directory for testing purposes.
*
* The scratch directory location is based on the $SCRATCH environment variable,
* and the must be accessible by the test processes.
*/
class ScratchDir {
public:
/**
* Create a scratch directory for testing purposes.
*
* Throws an EnvVarNotFound exception if:
* - the $SCRATCH environment variable is not defined
* - the $SCRATCH environment variable is empty
*/
ScratchDir()
: base_dir_(get_scratch_base_dir()),
test_dir_(base_dir_ + "/test_dir"),
home_dir_(test_dir_ + "/ECF_HOME") {
clear_scratch_test_dir(test_dir_);
}
~ScratchDir() { clear_scratch_test_dir(test_dir_); }

const std::string& home_dir() const { return home_dir_; }
const std::string& test_dir() const { return test_dir_; }

private:
std::string base_dir_;
std::string test_dir_;
std::string home_dir_;

inline static const char* SCRATCH = "SCRATCH";

inline static std::string get_scratch_base_dir() {
auto scratch = ecf::environment::get(SCRATCH);
if (scratch.empty()) {
throw ecf::environment::EnvVarNotFound(ecf::Message(SCRATCH).str());
}
return scratch;
}

inline static void clear_scratch_test_dir(std::string_view test_dir) {
namespace fs = std::filesystem;
if (fs::exists(test_dir)) {
fs::remove_all(test_dir);
}
}
};

#endif /* ecflow_test_harness_ScratchDir_HPP */
105 changes: 65 additions & 40 deletions libs/test/harness/TestFixture.cpp
Original file line number Diff line number Diff line change
@@ -31,7 +31,7 @@ std::string rtt_filename = "rtt.dat";
#else
std::string rtt_filename = "rtt_d.dat";
#endif
std::string TestFixture::scratchSmsHome_ = "";
std::unique_ptr<ScratchDir> TestFixture::scratch_dir_;
std::string TestFixture::host_;
std::string TestFixture::port_;
std::string TestFixture::test_dir_;
@@ -40,14 +40,31 @@ std::string TestFixture::project_test_dir_ = "libs/test";
using namespace std;
using namespace ecf;

namespace /* anonymous */ {

bool is_external_server_running_remotelly(std::string_view host) {
return !host.empty() && host != Str::LOCALHOST();
}

bool is_external_server_running_locally(std::string_view host) {
return !host.empty() && host == Str::LOCALHOST();
}

bool is_local_server(std::string_view host) {
return host.empty() || host == Str::LOCALHOST();
}

} // namespace

// ************************************************************************************************
// For test purpose the server can be started:
// 1/ By this test fixture
// 2/ Externally but on the same machine. by defining env variable: export ECF_HOST=localhost
// WHY? To test for memory leak. (i.e. with valgrind)
// 3/ Externally but on a different platform. by defining env variable: export ECF_HOST=itanium
// In this case we NEED to copy the test data, so that it is
// accessible by the client AND server
//
// (1) Externally but on a different platform. by defining env variable: export ECF_HOST=itanium
// In this case we NEED to copy the test data, so that it is
// accessible by the client AND server
// (2) Externally but on the same machine. by defining env variable: export ECF_HOST=localhost
// WHY? To test for memory leak. (i.e. with valgrind)
// (3) By this test fixture
//
// When invoking the server Externally _MUST_ use .
// ./Server/bin/gcc.<version>/debug/server --ecfinterval=2
@@ -102,48 +119,54 @@ void TestFixture::init(const std::string& project_test_dir) {
std::cout << " Openssl enabled\n";
}
#endif
if (!host_.empty() && host_ != Str::LOCALHOST()) {
if (is_external_server_running_remotelly(host_)) {

// Option (1)
//
// Going to perform the tests using an external server, running on a remote machine.
// We require the external server file system to be mounted locally,
// and use the $SCRATCH environment variable to locate the mount point of the server data.
// This allows to deploy the necessary test data, reset ECF_HOME, and configure the server.

client().set_host_port(host_, port_);
std::cout << " EXTERNAL SERVER running on _ANOTHER_ PLATFORM, assuming " << host_ << ":" << port_
<< " copying test data ...\n";

// Must use a file system accessible from the server. Use $SCRATCH
// Duplicate test data, required to scratch area and reset ECF_HOME
auto scratchEnv = ecf::environment::fetch("SCRATCH");
assert(scratchEnv);
std::string theSCRATCHArea(scratchEnv.value());
assert(!theSCRATCHArea.empty());

theSCRATCHArea += "/test_dir";
test_dir_ = theSCRATCHArea; // test_dir_ needed in destructor
if (fs::exists(test_dir_)) {
fs::remove_all(test_dir_);

std::cout << " _EXTERNAL_ SERVER running on a _REMOTE_ platform";
std::cout << " (" << host_ << ":" << port_ << ").";
std::cout << " Copying test data ...\n";

scratch_dir_ = std::make_unique<ScratchDir>();

if (const auto& d = scratch_dir_->test_dir(); fs::exists(d)) {
fs::remove_all(d);
}
theSCRATCHArea += "/ECF_HOME";
scratchSmsHome_ = theSCRATCHArea;

BOOST_REQUIRE_MESSAGE(File::createDirectories(theSCRATCHArea),
BOOST_REQUIRE_MESSAGE(File::createDirectories(scratch_dir_->home_dir()),
"File::createDirectories(theSCRATCHArea) failed");
BOOST_REQUIRE_MESSAGE(fs::exists(theSCRATCHArea), "theSCRATCHArea does not exist");
BOOST_REQUIRE_MESSAGE(fs::exists(scratch_dir_->home_dir()), "theSCRATCHArea does not exist");

// Ensure that local includes data exists. This needs to be copied to SCRATCH
BOOST_REQUIRE_MESSAGE(fs::exists(includes()), "The includes dir does not exist does not exist");

// Copy over the includes directory to the SCRATCH area.
std::string scratchIncludes = test_dir_ + "/";
std::string scratchIncludes = scratch_dir_->test_dir() + "/";
std::string do_copy = "cp -r " + includes() + " " + scratchIncludes;
if (system(do_copy.c_str()) != 0)
if (system(do_copy.c_str()) != 0) {
assert(false);
}

// clear log file
clearLog();
}
else if (!host_.empty() && host_ == Str::LOCALHOST()) {
else if (is_external_server_running_locally(host_)) {

// Option (2)
//
// Going to perform the tests using an external server, running on the local machine.

client().set_host_port(host_, port_);
std::cout << " EXTERNAL SERVER running on _SAME_ PLATFORM. Assuming " << client().host() << ":"
<< client().port() << "\n";

std::cout << " _EXTERNAL_ SERVER running on the _LOCAL_ platform";
std::cout << " (" << client().host() << ":" << client().port() << ").\n";

// Print the server stats before we start + checks if it is up and running::
client().set_cli(true); // so server stats are written to standard out
@@ -166,10 +189,16 @@ void TestFixture::init(const std::string& project_test_dir) {
cout << " Log file " << TestFixture::pathToLogFile() << " creation failed " << client().errorMsg()
<< "\n";
}
else
else {
cout << " Log file " << the_log_file << " already exists\n";
}
}
else {

// Option (3)
//
// Going to perform the tests using a server launched as part of the test setup.

// For local host start by removing log file. Server invocation should create a new log file
fs::remove(fs::path(pathToLogFile()));
host_ = Str::LOCALHOST();
@@ -301,18 +330,14 @@ int TestFixture::job_submission_interval() {
}

std::string TestFixture::smshome() {
if (serverOnLocalMachine()) {
if (is_local_server(TestFixture::host_)) {
return local_ecf_home();
}
return scratchSmsHome_;
}

bool TestFixture::serverOnLocalMachine() {
return (host_.empty() || host_ == Str::LOCALHOST());
return scratch_dir_->home_dir();
}

std::string TestFixture::theClientExePath() {
if (serverOnLocalMachine()) {
if (is_local_server(TestFixture::host_)) {
return File::find_ecf_client_path();
}

@@ -340,7 +365,7 @@ void TestFixture::clearLog() {
}

std::string TestFixture::pathToLogFile() {
if (serverOnLocalMachine()) {
if (is_local_server(TestFixture::host_)) {
Host host;
return host.ecf_log_file(port_);
}
11 changes: 4 additions & 7 deletions libs/test/harness/TestFixture.hpp
Original file line number Diff line number Diff line change
@@ -18,13 +18,14 @@
/// We will use $SCRATCH as this is accessible by both client and server.
/// This means copying over the test data
///
/// When TextFixture is GLOBAL, then we can't seem to call any of the
/// When TestFixture is GLOBAL, then we can't seem to call any of the
/// BOOST_REQUIRE_MESSAGE() macro in constructor/descructor as this causes a crash
/// i.e order of initialisation issues
///

#include <string>

#include "ScratchDir.hpp"
#include "ecflow/client/ClientInvoker.hpp"
#include "ecflow/core/PrintStyle.hpp"

@@ -56,10 +57,6 @@ struct TestFixture
/// that was created in the constructor
static std::string smshome();

/// Will end up checking to see if ECF_HOST is specified. This specifies the name
/// of the machine that is running the server. Otherwise return true
static bool serverOnLocalMachine();

/// If running locally returns location of client exe, if a server is on a remote
/// machine, we need to determine its location.
// Several options:
@@ -89,15 +86,15 @@ struct TestFixture

// Use for all comms with server
static ClientInvoker& client();
static std::string port() { return port_; }
static const std::string& port() { return port_; }

private:
static std::string local_ecf_home();

void init(const std::string& project_test_dir);

private:
static std::string scratchSmsHome_;
static std::unique_ptr<ScratchDir> scratch_dir_;
static std::string host_;
static std::string port_;
static std::string test_dir_; // used when we have an external server, different platform