Skip to content

Commit

Permalink
Merge pull request #19 from mpartio/develop
Browse files Browse the repository at this point in the history
Add a REST API for ecFlow server
  • Loading branch information
iainrussell authored Oct 14, 2022
2 parents be564cf + 00e4d4f commit 38d7e1c
Show file tree
Hide file tree
Showing 41 changed files with 39,198 additions and 19 deletions.
3 changes: 3 additions & 0 deletions ANode/src/Node.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -494,6 +494,9 @@ class Node : public std::enable_shared_from_this<Node> {
void deleteInlimit(const std::string& name);
void deleteZombie(const std::string& type); // string must be one of [ user | ecf | path ]
void deleteLate();
void deleteAutoCancel();
void deleteAutoRestore();
void deleteAutoArchive();

// Change functions: ================================================================
/// returns true the change was made else false, Can throw std::runtime_error for parse errors
Expand Down
19 changes: 19 additions & 0 deletions ANode/src/NodeDelete.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,9 @@
#include "LateAttr.hpp"
#include "MiscAttrs.hpp"
#include "Expression.hpp"
#include "AutoCancelAttr.hpp"
#include "AutoArchiveAttr.hpp"
#include "AutoRestoreAttr.hpp"

using namespace ecf;
using namespace std;
Expand Down Expand Up @@ -441,4 +444,20 @@ void Node::deleteLate()
state_change_no_ = Ecf::incr_state_change_no();
}

void Node::deleteAutoCancel()
{
auto_cancel_.reset(nullptr);
state_change_no_ = Ecf::incr_state_change_no();
}

void Node::deleteAutoArchive()
{
auto_archive_.reset(nullptr);
state_change_no_ = Ecf::incr_state_change_no();
}

void Node::deleteAutoRestore()
{
auto_restore_.reset(nullptr);
state_change_no_ = Ecf::incr_state_change_no();
}
3 changes: 3 additions & 0 deletions Base/src/AbstractClientEnv.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,9 @@ class AbstractClientEnv {
// returns a user specified user name. When this is used a password must be provided
virtual const std::string& get_user_name() const = 0;
virtual void set_user_name(const std::string&) = 0;
// set password is needed when user is authenticated from an http server call: in this case
// the password is given in the url (or some other means), so it will not be read from a local file
virtual void set_password(const std::string&) = 0;

/// Some commands work on construction. to avoid this under test. Call set_test
/// i.e Command like CtsCmd::SERVER_LOAD can be client side only, in which case
Expand Down
10 changes: 5 additions & 5 deletions Base/src/cts/EditScriptCmd.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -380,14 +380,14 @@ void EditScriptCmd::create( Cmd_ptr& cmd,
string path_to_script = args[2];
std::vector<std::string> script_lines;

if (!fs::exists(path_to_script)) {
ss << "The script file specified '" << path_to_script << "' does not exist\n";
if (!fs::exists(path_to_script)) {
ss << "The script file specified '" << path_to_script << "' does not exist\n";
throw std::runtime_error(ss.str());
}
if (!File::splitFileIntoLines(path_to_script, script_lines)) {
ss << "Could not open script file " << path_to_script << " (" << strerror(errno) << ")";
if (!File::splitFileIntoLines(path_to_script, script_lines)) {
ss << "Could not open script file " << path_to_script << " (" << strerror(errno) << ")";
throw std::runtime_error(ss.str());
}
}

if (edit_type == EditScriptCmd::SUBMIT || edit_type == EditScriptCmd::SUBMIT_USER_FILE) {
// extract the Used variables from the script file
Expand Down
24 changes: 20 additions & 4 deletions Base/src/cts/LoadDefsCmd.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
#include "Defs.hpp"
#include "Log.hpp"
#include "PrintStyle.hpp"
#include <boost/filesystem.hpp>

using namespace ecf;
using namespace std;
Expand All @@ -46,12 +47,26 @@ LoadDefsCmd::LoadDefsCmd(const std::string& defs_filename, bool force, bool chec
throw std::runtime_error(ss.str());
}

// At the end of the parse check the trigger/complete expressions and resolve in-limits
defs_ptr defs = Defs::create();
std::string errMsg, warningMsg;
if (defs->restore(defs_filename_, errMsg , warningMsg) ) {

defs->handle_migration();
bool load_ok = false;

if (defs_filename.find("suite") != std::string::npos && defs_filename.find("endsuite") != std::string::npos) {
load_ok = defs->restore_from_string(defs_filename, errMsg, warningMsg);
defs_filename_ = "";
}
else if (boost::filesystem::exists(defs_filename)) {
// defs_filename is actually a file, open the file and read it. This is the method
// when loading definitions with ecflow_client

// At the end of the parse check the trigger/complete expressions and resolve in-limits

load_ok = defs->restore(defs_filename_, errMsg , warningMsg);
}

if (load_ok) {
defs->handle_migration();
defs->set_server().add_or_update_user_variables( client_env ); // use in test environment

if (print) {
Expand All @@ -68,10 +83,11 @@ LoadDefsCmd::LoadDefsCmd(const std::string& defs_filename, bool force, bool chec
cout << warningMsg;
}
else {
std::stringstream ss; ss << "\nLoadDefsCmd::LoadDefsCmd. Failed to parse file " << defs_filename_ << "\n";
std::stringstream ss; ss << "\nLoadDefsCmd::LoadDefsCmd. Failed to parse file/definition " << defs_filename_ << "\n";
ss << errMsg;
throw std::runtime_error( ss.str() );
}

}

bool LoadDefsCmd::equals(ClientToServerCmd* rhs) const
Expand Down
9 changes: 8 additions & 1 deletion Base/src/cts/ReplaceNodeCmd.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,14 @@ ReplaceNodeCmd::ReplaceNodeCmd(const std::string& node_path, bool createNodesAsN
// Parse the file and load the defs file into memory.
std::string errMsg, warningMsg;
defs_ptr client_defs = Defs::create();
if ( ! client_defs->restore( path_to_defs , errMsg , warningMsg) ) {
bool ok = false;
if (path_to_defs.find("suite") != std::string::npos && path_to_defs.find("endsuite") != std::string::npos) {
ok = client_defs->restore_from_string(path_to_defs, errMsg, warningMsg);
}
else {
ok = client_defs->restore( path_to_defs , errMsg , warningMsg);
}
if (!ok) {
std::stringstream ss;
ss << "ReplaceNodeCmd::ReplaceNodeCmd: Could not parse file " << path_to_defs << " : " << errMsg;
throw std::runtime_error( ss.str() );
Expand Down
5 changes: 5 additions & 0 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,7 @@ option( ENABLE_UI_USAGE_LOG "Enable UI usage logging" OFF )
option( ENABLE_SSL "Enable SSL encrypted communication" ON )
option( ENABLE_PYTHON_PTR_REGISTER "Some compilers/boost versions do not register shared ptr automatically" OFF )
option( ENABLE_PYTHON_UNDEF_LOOKUP "Some boost/python versions are too closely linked" OFF )
option( ENABLE_HTTP "Enable HTTP server (experimental)" OFF )

# =========================================================================================
# Qt for ecFlowUI.
Expand Down Expand Up @@ -178,6 +179,7 @@ ecbuild_info( "ENABLE_TESTS : ${ENABLE_TESTS} *if* disabled no nee
ecbuild_info( "ENABLE_ALL_TESTS : ${ENABLE_ALL_TESTS}" )
ecbuild_info( "ENABLE_STATIC_BOOST_LIBS : ${ENABLE_STATIC_BOOST_LIBS}" )
ecbuild_info( "ENABLE_SSL : ${ENABLE_SSL} *if* openssl libraries available" )
ecbuild_info( "ENABLE_HTTP : ${ENABLE_HTTP}" )


if (ENABLE_UI)
Expand Down Expand Up @@ -347,6 +349,9 @@ if (ENABLE_UI)
add_subdirectory( share )
endif()

if (ENABLE_HTTP)
add_subdirectory( Http )
endif()

# =========================================================================================
# DOXYGEN to use: make doxygen -> ${CMAKE_CURRENT_BINARY_DIR}/Doc/doxygen/html/index.html
Expand Down
4 changes: 4 additions & 0 deletions Client/src/ClientEnvironment.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -123,6 +123,7 @@ class ClientEnvironment final : public AbstractClientEnv {
void clear_user_password() override { passwd_.clear();}
const std::string& get_user_name() const override { return user_name_;}
void set_user_name(const std::string& name) override { user_name_ = name;}
void set_password(const std::string& password) override { passwd_ = password; }
bool debug() const override { return debug_;} //enabled if ECF_DEBUG_CLIENT set
void set_test() override { under_test_ = true; }
bool under_test() const override { return under_test_; }
Expand All @@ -134,6 +135,9 @@ class ClientEnvironment final : public AbstractClientEnv {
void set_child_try_no(unsigned int try_no) { task_try_num_ = try_no;}
void set_child_init_add_vars(const std::vector<Variable>& vars) { init_add_vars_ = vars;}
void set_child_complete_del_vars(std::vector<std::string>& vars) { complete_del_vars_ = vars;}
void set_child_host_file(const std::string& host_file) { host_file_ = host_file;}
void set_child_denied(bool denied) { denied_ = denied;}
void set_child_no_ecf(bool no_ecf) { no_ecf_ = no_ecf;}

const std::vector<Variable>& init_add_vars() const { return init_add_vars_;}
const std::vector<std::string>& complete_del_vars() const { return complete_del_vars_;}
Expand Down
4 changes: 4 additions & 0 deletions Client/src/ClientInvoker.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -125,6 +125,10 @@ void ClientInvoker::set_user_name(const std::string& user) {
clientEnv_.clear_user_password(); // force re-check of password
}

void ClientInvoker::set_password(const std::string& password) {
clientEnv_.set_password(password);
}

void ClientInvoker::taskPath(const std::string& s) {
test_ = true;
clientEnv_.taskPath(s);
Expand Down
5 changes: 5 additions & 0 deletions Client/src/ClientInvoker.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,7 @@ class ClientInvoker {
/// This will override the environment setting. ECF_USER or --user
/// Clear password, so that re-validate users password from password file
void set_user_name(const std::string& user);
void set_password(const std::string& password);

/// Whenever there is a connections failure we wait a number of seconds
/// before trying again. ( i.e. to get round glitches in the network.)
Expand Down Expand Up @@ -176,6 +177,10 @@ class ClientInvoker {
void set_child_pid(const std::string& pid) { clientEnv_.set_child_pid(pid);}
void set_child_try_no(unsigned int try_no) { clientEnv_.set_child_try_no(try_no);}
void set_child_timeout(unsigned int seconds) { clientEnv_.set_child_cmd_timeout(seconds);} // ECF_TIMEOUT default is 24 hours allow python jobs to override
void set_child_host_file(const std::string& host_file) { clientEnv_.set_child_host_file(host_file);}
void set_child_denied(bool denied) { clientEnv_.set_child_denied(denied);}
void set_child_no_ecf(bool no_ecf) { clientEnv_.set_child_no_ecf(no_ecf);}

void set_child_init_add_vars(const std::vector<Variable>& vars) { clientEnv_.set_child_init_add_vars(vars);}
void set_child_complete_del_vars(std::vector<std::string>& vars) { clientEnv_.set_child_complete_del_vars(vars);}
void set_zombie_child_timeout(unsigned int seconds){clientEnv_.set_zombie_child_cmd_timeout(seconds);} // ECF_ZOMBIE_TIMEOUT default is 24 hours allow python jobs to override
Expand Down
2 changes: 2 additions & 0 deletions Client/src/ClientOptions.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,8 @@ ClientOptions::ClientOptions()
"host: If specified will override the environment variable ECF_HOST and default host, localhost");
desc_->add_options()("user",po::value< string >()->implicit_value( string("") ),
"user: The user name to be used when contacting the server. Can only be used when password is also specified");
desc_->add_options()("password",po::value< string >()->implicit_value( string("") ),
"password: The password to be used when contacting the server");
#ifdef ECF_OPENSSL
desc_->add_options()("ssl","ssl: If specified will override the environment variable ECF_SSL");
#endif
Expand Down
96 changes: 96 additions & 0 deletions Http/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,96 @@
# =======================================================
# LIB
# to list all sources to build use:
# cd $WK/Client
# find src -name \*.cpp --print
# =======================================================

# Excludes src/HttpServerMain.cpp
list( APPEND srcs
src/HttpServer.cpp
src/ApiV1.cpp
src/ApiV1Impl.cpp
src/BasicAuth.cpp
src/TypeToJson.cpp
src/TokenStorage.cpp
)
ecbuild_add_library( TARGET libhttp
NOINSTALL
TYPE STATIC
SOURCES ${srcs}
)

target_link_libraries(libhttp base node nodeattr core pthread)
target_include_directories(libhttp PUBLIC src
../ACore/src
../ANattr/src
../ANode/src
../Base/src
../Base/src/cts
../Base/src/stc
../Client/src
../cpp-httplib
../json
)



# ========================================================================
# EXE ecflow_http, if OpenSSL not enabled ${OPENSSL_LIBRARIES}, is empty

ecbuild_add_executable( TARGET ecflow_http
SOURCES src/HttpMain.cpp
LIBS libhttp libclient ${OPENSSL_LIBRARIES}
INCLUDES ${Boost_INCLUDE_DIRS} ../cpp-httplib
)

# Override default behaviour that add RPATHS during install
# The only thing that seem to work is set INSTALL_RPATH to ""
# Using SKIP_BUILD_RPATH,BUILD_WITH_INSTALL_RPATH,INSTALL_RPATH_USE_LINK_PATH
# had no effect
#
SET_TARGET_PROPERTIES(ecflow_http PROPERTIES
INSTALL_RPATH ""
)

# use, i.e. don't skip the full RPATH for the build tree
#SET(CMAKE_SKIP_BUILD_RPATH FALSE)

# when building, don't use the install RPATH already
# (but later on when installing)
#SET(CMAKE_BUILD_WITH_INSTALL_RPATH FALSE)

# the RPATH to be used when installing
#SET(CMAKE_INSTALL_RPATH "")

# don't add the automatically determined parts of the RPATH
# which point to directories outside the build tree to the install RPATH
#SET(CMAKE_INSTALL_RPATH_USE_LINK_PATH FALSE)

if (ENABLE_HTTP AND ENABLE_SERVER)

list(APPEND test_srcs
test/TestApiV1.cpp
)

ecbuild_add_test( TARGET s_http
SOURCES ${test_srcs}
LIBS libhttp libclient libserver ${OPENSSL_LIBRARIES}
${Boost_UNIT_TEST_FRAMEWORK_LIBRARY} ${Boost_TEST_EXEC_MONITOR_LIBRARY}
${Boost_TIMER_LIBRARY} ${Boost_CHRONO_LIBRARY} ${LIBRT}
INCLUDES src
../ANode/test
../Base/test
../Server/src
${Boost_INCLUDE_DIRS}
DEFINITIONS ${BOOST_TEST_DYN_LINK}
TEST_DEPENDS u_base s_client
)

endif()


# ===================================================================
# install
# ===================================================================
install (TARGETS ecflow_http DESTINATION bin)
Loading

0 comments on commit 38d7e1c

Please sign in to comment.