diff --git a/src/api_server.cpp b/src/api_server.cpp index 1c7d876f..54c4d2fe 100644 --- a/src/api_server.cpp +++ b/src/api_server.cpp @@ -14,129 +14,163 @@ limitations under the License. */ +#include +#include #include +#include +#include #include #include "image_service.h" #include "image_file.h" #include "api_server.h" -int ApiHandler::handle_request(photon::net::http::Request& req, - photon::net::http::Response& resp, - std::string_view) { - auto target = req.target(); // string view, format: /snapshot?dev_id=${devID}&config=${config} - std::string_view query(""); - auto pos = target.find('?'); - if (pos != std::string_view::npos) { - query = target.substr(pos + 1); - } - LOG_DEBUG("Snapshot query: `", query); // string view, format: dev_id=${devID}&config=${config} - parse_params(query); - auto dev_id = params["dev_id"]; - auto config_path = params["config"]; - LOG_DEBUG("dev_id: `, config: `", dev_id, config_path); - - int code; - std::string msg; - ImageFile* img_file = nullptr; - - if (dev_id.empty() || config_path.empty()) { - code = 400; - msg = std::string(R"delimiter({ - "success": false, - "message": "Missing dev_id or config in snapshot request" +class ApiHandler : public photon::net::http::HTTPHandler { +public: + ImageService *imgservice; + std::map params; + + ApiHandler(ImageService *imgservice) : imgservice(imgservice) {} + int handle_request(photon::net::http::Request& req, + photon::net::http::Response& resp, + std::string_view) override { + auto target = req.target(); // string view, format: /snapshot?dev_id=${devID}&config=${config} + std::string_view query(""); + auto pos = target.find('?'); + if (pos != std::string_view::npos) { + query = target.substr(pos + 1); + } + // auto query = req.query(); + LOG_INFO("Snapshot query: `", query); // string view, format: dev_id=${devID}&config=${config} + parse_params(query); + auto dev_id = params["dev_id"]; + auto config_path = params["config"]; + LOG_DEBUG("dev_id: `, config: `", dev_id, config_path); + + int code; + std::string msg; + ImageFile* img_file = nullptr; + + if (dev_id.empty() || config_path.empty()) { + code = 400; + msg = std::string(R"delimiter({ + "success": false, + "message": "Missing dev_id or config in snapshot request" })delimiter"); - goto EXIT; - } + goto EXIT; + } - img_file = imgservice->find_image_file(dev_id); - if (!img_file) { - code = 404; - msg = std::string(R"delimiter({ - "success": false, - "message": "Image file not found" + img_file = imgservice->find_image_file(dev_id); + if (!img_file) { + code = 404; + msg = std::string(R"delimiter({ + "success": false, + "message": "Image file not found" })delimiter"); - goto EXIT; - } + goto EXIT; + } - if (img_file->create_snapshot(config_path.c_str()) < 0) { - code = 500; - msg = std::string(R"delimiter({ - "success": false, - "message": "Failed to create snapshot`" + if (img_file->create_snapshot(config_path.c_str()) < 0) { + code = 500; + msg = std::string(R"delimiter({ + "success": false, + "message": "Failed to create snapshot`" })delimiter"); - goto EXIT; - } + goto EXIT; + } - code = 200; - msg = std::string(R"delimiter({ - "success": true, - "message": "Snapshot created successfully" + code = 200; + msg = std::string(R"delimiter({ + "success": true, + "message": "Snapshot created successfully" })delimiter"); EXIT: - resp.set_result(code); - resp.headers.content_length(msg.size()); - resp.keep_alive(true); - auto ret_w = resp.write((void*)msg.c_str(), msg.size()); - if (ret_w != (ssize_t)msg.size()) { - LOG_ERRNO_RETURN(0, -1, "send body failed, target: `, `", req.target(), VALUE(ret_w)); + resp.set_result(code); + resp.headers.content_length(msg.size()); + resp.keep_alive(true); + auto ret_w = resp.write((void*)msg.c_str(), msg.size()); + if (ret_w != (ssize_t)msg.size()) { + LOG_ERRNO_RETURN(0, -1, "send body failed, target: `, `", req.target(), VALUE(ret_w)); + } + LOG_DEBUG("send body done"); + return 0; } - LOG_DEBUG("send body done"); - return 0; -} -void ApiHandler::parse_params(std::string_view query) { // format: dev_id=${devID}&config=${config}... - if (query.empty()) - return; - - size_t start = 0; - while (start < query.length()) { - auto end = query.find('&', start); - if (end == std::string_view::npos) { // last one - end = query.length(); - } + void parse_params(std::string_view query) { // format: dev_id=${devID}&config=${config}... + if (query.empty()) + return; - auto param = query.substr(start, end - start); - auto eq_pos = param.find('='); - if (eq_pos != std::string_view::npos) { - auto key = param.substr(0, eq_pos); - auto value = param.substr(eq_pos + 1); + size_t start = 0; + while (start < query.length()) { + auto end = query.find('&', start); + if (end == std::string_view::npos) { // last one + end = query.length(); + } - // url decode - auto decoded_key = photon::net::http::url_unescape(key); - auto decoded_value = photon::net::http::url_unescape(value); - params[decoded_key] = decoded_value; - } else { - // key without value - auto key = photon::net::http::url_unescape(param); - params[key] = ""; + auto param = query.substr(start, end - start); + auto eq_pos = param.find('='); + if (eq_pos != std::string_view::npos) { + auto key = param.substr(0, eq_pos); + auto value = param.substr(eq_pos + 1); + + // url decode + auto decoded_key = photon::net::http::url_unescape(key); + auto decoded_value = photon::net::http::url_unescape(value); + params[decoded_key] = decoded_value; + } else { + // key without value + auto key = photon::net::http::url_unescape(param); + params[key] = ""; + } + start = end + 1; } - start = end + 1; } -} +}; + +struct ApiServer { + photon::net::ISocketServer* tcpserver = nullptr; + photon::net::http::HTTPServer* httpserver = nullptr; + ApiHandler* handler = nullptr; + + ApiServer(ImageService *imgservice) : handler(new ApiHandler(imgservice)) {} -ApiServer::ApiServer(const std::string &addr, ApiHandler* handler) { - photon::net::http::URL url(addr); - std::string host = url.host().data(); // the string pointed by data() doesn't end up with '\0' - auto pos = host.find(":"); - if (pos != host.npos) { - host.resize(pos); + ~ApiServer() { + delete handler; + if(tcpserver) { + safe_delete(tcpserver); + } + if(httpserver) { + safe_delete(httpserver); + } } - tcpserver = photon::net::new_tcp_socket_server(); - tcpserver->setsockopt(SOL_SOCKET, SO_REUSEPORT, 1); - if(tcpserver->bind(url.port(), photon::net::IPAddr(host.c_str())) < 0) - LOG_ERRNO_RETURN(0, , "Failed to bind api server port `", url.port()); - if(tcpserver->listen() < 0) - LOG_ERRNO_RETURN(0, , "Failed to listen api server port `", url.port()); - httpserver = photon::net::http::new_http_server(); - httpserver->add_handler(handler, false, "/snapshot"); - tcpserver->set_handler(httpserver->get_connection_handler()); - tcpserver->start_loop(); - ready = true; - LOG_DEBUG("Api server listening on `:`, path: `", host, url.port(), "/snapshot"); + + int init(const std::string &addr) { + photon::net::http::URL url(addr); + std::string host = url.host().data(); // the string pointed by data() doesn't end up with '\0' + auto pos = host.find(":"); + if (pos != host.npos) { + host.resize(pos); + } + tcpserver = photon::net::new_tcp_socket_server(); + tcpserver->setsockopt(SOL_SOCKET, SO_REUSEPORT, 1); + if(tcpserver->bind(url.port(), photon::net::IPAddr(host.c_str())) < 0) + LOG_ERRNO_RETURN(0, -1, "Failed to bind api server port `", url.port()); + if(tcpserver->listen() < 0) + LOG_ERRNO_RETURN(0, -1, "Failed to listen api server port `", url.port()); + httpserver = photon::net::http::new_http_server(); + httpserver->add_handler(handler, false, "/snapshot"); + tcpserver->set_handler(httpserver->get_connection_handler()); + tcpserver->start_loop(); + LOG_DEBUG("Api server listening on `:`, path: `", host, url.port(), "/snapshot"); + return 0; + } +}; + +int start_api_server(ApiServer *&api_server , ImageService *imgservice, const std::string &addr) { + api_server = new ApiServer(imgservice); + return api_server->init(addr); } -ApiServer::~ApiServer() { - delete tcpserver; - delete httpserver; -} \ No newline at end of file +int stop_api_server(ApiServer *api_server) { + safe_delete(api_server); +} diff --git a/src/api_server.h b/src/api_server.h index 1b8c0641..97c3a222 100644 --- a/src/api_server.h +++ b/src/api_server.h @@ -16,30 +16,5 @@ #pragma once -#include -#include -#include -#include - -class ImageService; - -class ApiHandler : public photon::net::http::HTTPHandler { -public: - ImageService *imgservice; - std::map params; - - ApiHandler(ImageService *imgservice) : imgservice(imgservice) {} - int handle_request(photon::net::http::Request& req, - photon::net::http::Response& resp, - std::string_view) override; - void parse_params(std::string_view query); -}; - -struct ApiServer { - photon::net::ISocketServer* tcpserver = nullptr; - photon::net::http::HTTPServer* httpserver = nullptr; - bool ready = false; - - ApiServer(const std::string &addr, ApiHandler* handler); - ~ApiServer(); -}; \ No newline at end of file +int start_api_server(ApiServer *&api_server , ImageService *imgservice, const std::string &addr); +int stop_api_server(ApiServer *api_server); diff --git a/src/image_file.cpp b/src/image_file.cpp index 654c5242..1281e74b 100644 --- a/src/image_file.cpp +++ b/src/image_file.cpp @@ -555,7 +555,7 @@ void ImageFile::set_failed(const Ts &...xs) { } } -int ImageFile::create_snapshot(const char *config_path) { +int ImageFile::create_snapshot(const char *new_config_path) { // load new config file to get the snapshot layer path // open new upper layer // restack() current RW layer as snapshot layer @@ -565,13 +565,13 @@ int ImageFile::create_snapshot(const char *config_path) { ImageConfigNS::ImageConfig new_cfg; LSMT::IFileRW *upper_file = nullptr; - LOG_INFO("Load new config `.", config_path); - if (!new_cfg.ParseJSON(config_path)) { - LOG_ERROR_RETURN(0, -1, "Error parse new config json: `.", config_path); + LOG_INFO("Load new config `.", new_config_path); + if (!new_cfg.ParseJSON(new_config_path)) { + LOG_ERROR_RETURN(0, -1, "Error parse new config json: `.", new_config_path); } auto upper = new_cfg.upper(); - auto lowers = new_cfg.lowers(); + // auto lowers = new_cfg.lowers(); // if(lowers[lowers.size()-1].file() != conf.upper().data()) // LOG_ERROR_RETURN(0, -1, "The last lower layer(`) should be the same as old upper layer(`) after restack.", lowers[lowers.size()-1].file(), conf.upper().data()); if(upper.index() == conf.upper().index() || upper.data() == conf.upper().data()) @@ -589,7 +589,7 @@ int ImageFile::create_snapshot(const char *config_path) { auto sealed = ((LSMT::IFileRW *)m_upper_file)->get_file(0); ((LSMT::IFileRO *)m_lower_file)->insert_file(sealed); ((LSMT::IFileRW *)m_upper_file)->clear_files(); - delete m_upper_file; + safe_delete(m_upper_file); } // set m_lower_file->m_index = m_file->m_index->m_backing_index because m_file is not responsible for the destruction of m_backing_index auto combo_index = (LSMT::IComboIndex *)((LSMT::IFileRW *)m_file)->index(); // m_file->m_index @@ -599,6 +599,18 @@ int ImageFile::create_snapshot(const char *config_path) { combo_index->front_index(upper_file_index); m_upper_file = upper_file; + + // overwrite the config file in use in case the old files are used again after the process restarts + auto lfs = photon::fs::new_localfs_adaptor(); + if (lfs == nullptr) { + LOG_ERRNO_RETURN(0, -1, "new localfs_adaptor failed"); + } + DEFER(delete lfs); + int ret = lfs->rename(new_config_path, this->config_path.c_str()); + if (ret != 0) { + LOG_ERRNO_RETURN(0, -1, "rename(`,`) failed", new_config_path, this->config_path); + } + LOG_INFO("rename(`,`) success", new_config_path, this->config_path); return 0; } diff --git a/src/image_file.h b/src/image_file.h index d1f3a63e..f5870963 100644 --- a/src/image_file.h +++ b/src/image_file.h @@ -41,8 +41,8 @@ static std::string SEALED_FILE_NAME = "overlaybd.sealed"; class ImageFile : public photon::fs::ForwardFile { public: - ImageFile(ImageConfigNS::ImageConfig &_conf, ImageService &is, const std::string &dev_id) - : ForwardFile(nullptr), image_service(is), m_lower_file(nullptr) { + ImageFile(ImageConfigNS::ImageConfig &_conf, ImageService &is, const std::string &dev_id, const char *_config_path) + : ForwardFile(nullptr), image_service(is), m_lower_file(nullptr), config_path(_config_path) { conf.CopyFrom(_conf, conf.GetAllocator()); m_exception = ""; if(image_service.register_image_file(dev_id, this) != 0) { // register itself @@ -119,11 +119,12 @@ class ImageFile : public photon::fs::ForwardFile { int compact(IFile *as); - int create_snapshot(const char *config_path); + int create_snapshot(const char *new_config_path); private: Prefetcher *m_prefetcher = nullptr; ImageConfigNS::ImageConfig conf; + const std::string config_path; std::list dl_list; photon::join_handle *dl_thread_jh = nullptr; ImageService &image_service; diff --git a/src/image_service.cpp b/src/image_service.cpp index 20e648f5..3d39c57a 100644 --- a/src/image_service.cpp +++ b/src/image_service.cpp @@ -454,10 +454,10 @@ int ImageService::init() { - create a live snapshot for a imageFile /snapshot?dev_id=${devID}&config=${config} */ - api_handler.reset(new ApiHandler(this)); - api_server = new ApiServer(global_conf.serviceConfig().address(), api_handler.get()); - if(!api_server->ready) + auto ret = start_api_server(api_server, this, global_conf.serviceConfig().address()); + if (ret == -1) { LOG_ERROR_RETURN(0, -1, "Failed to start http server for live snapshot"); + } } return 0; } @@ -498,7 +498,7 @@ ImageFile *ImageService::create_image_file(const char *config_path, const std::s } auto resFile = cfg.resultFile(); - ImageFile *ret = new ImageFile(cfg, *this, dev_id); + ImageFile *ret = new ImageFile(cfg, *this, dev_id, config_path); if (ret->m_status <= 0) { std::string data = "failed:" + ret->m_exception; set_result_file(resFile, data); @@ -545,7 +545,7 @@ ImageService::~ImageService() { delete global_fs.srcfs; delete global_fs.io_alloc; delete exporter; - delete api_server; + stop_api_server(api_server); LOG_INFO("image service is fully stopped"); } @@ -557,4 +557,4 @@ ImageService *create_image_service(const char *config_path) { return nullptr; } return ret; -} \ No newline at end of file +} diff --git a/src/image_service.h b/src/image_service.h index df260e9f..a2278e23 100644 --- a/src/image_service.h +++ b/src/image_service.h @@ -49,7 +49,6 @@ struct ImageAuthResponse : public ConfigUtils::Config { }; struct ImageFile; -class ApiHandler; struct ApiServer; class ImageService { @@ -69,7 +68,6 @@ class ImageService { struct GlobalFs global_fs; std::unique_ptr metrics; ExporterServer *exporter = nullptr; - std::unique_ptr api_handler; ApiServer *api_server = nullptr; private: @@ -85,4 +83,4 @@ ImageService *create_image_service(const char *config_path = nullptr); int load_cred_from_file(const std::string path, const std::string &remote_path, std::string &username, std::string &password); -void destroy(); \ No newline at end of file +void destroy(); diff --git a/src/overlaybd/config_util.h b/src/overlaybd/config_util.h index e2ea0e98..4b2b4191 100644 --- a/src/overlaybd/config_util.h +++ b/src/overlaybd/config_util.h @@ -182,4 +182,4 @@ GetResult( return ConfigUtils::GetResult(this, "/" #paraname, ##__VA_ARGS__); \ } -} // namespace ConfigUtils \ No newline at end of file +} // namespace ConfigUtils diff --git a/src/overlaybd/lsmt/file.cpp b/src/overlaybd/lsmt/file.cpp index d1b3421a..0bac3181 100644 --- a/src/overlaybd/lsmt/file.cpp +++ b/src/overlaybd/lsmt/file.cpp @@ -502,12 +502,10 @@ class LSMTReadOnlyFile : public IFileRW { virtual int index(const IMemoryIndex *index) override { if(!index || !index->buffer()) { - errno = EINVAL; - LOG_ERROR("Invalid index!"); - return -1; + LOG_ERROR_RETURN(EINVAL, -1, "Invalid index!"); } if (m_index != nullptr) { - delete m_index; + safe_delete(m_index); m_index = nullptr; } m_index = (IMemoryIndex *)index; @@ -1070,8 +1068,8 @@ class LSMTFile : public LSMTReadOnlyFile { m_vsize = u->m_vsize; ((IComboIndex *)m_index)->commit_index0(); - delete fseal; - delete gc_layer; + safe_delete(fseal); + safe_delete(gc_layer); return 0; } diff --git a/src/overlaybd/lsmt/index.cpp b/src/overlaybd/lsmt/index.cpp index 6e77fdb6..7c1c59ec 100644 --- a/src/overlaybd/lsmt/index.cpp +++ b/src/overlaybd/lsmt/index.cpp @@ -652,12 +652,10 @@ class ComboIndex : public Index0 { virtual int front_index(const IMemoryIndex0 *fi) override { if (!fi) { - errno = EINVAL; - LOG_ERROR("Invalid index!"); - return -1; + LOG_ERROR_RETURN(EINVAL, -1, "Invalid index!"); } if (m_ownership && m_index0 != nullptr) { // !!! - delete m_index0; + safe_delete(m_index0); // delete m_index0; m_index0 = nullptr; } m_index0 = (Index0 *)fi; @@ -774,10 +772,10 @@ class ComboIndex : public Index0 { merged_index->insert(p); } if(m_ownership) { // !!! - delete m_backing_index; + safe_delete(m_backing_index); } m_backing_index = (Index*)(merged_index->make_read_only_index()); // set ownership=false - delete merged_index; + safe_delete(merged_index); LOG_INFO("rebuild backing index done. {count: `}", m_backing_index->size()); // Clear original index0 mapping.clear(); diff --git a/src/test/image_service_test.cpp b/src/test/image_service_test.cpp index 020c5e2b..ae1a8a5f 100644 --- a/src/test/image_service_test.cpp +++ b/src/test/image_service_test.cpp @@ -278,15 +278,30 @@ class HTTPServerTest : public DevIDRegisterTest { DevIDRegisterTest::SetUp(); } - long request_snapshot(const char* request_url) { - auto request = new photon::net::cURL(); - DEFER({ delete request; }); - - LOG_INFO("request url: `", request_url); - photon::net::StringWriter writer; - auto ret = request->POST(request_url, &writer, (int64_t)1000000); - LOG_INFO("response: `", writer.string); - return ret; + int request_snapshot(const char* request_url) { + // auto request = new photon::net::cURL(); + // DEFER({ delete request; }); + + // LOG_INFO("request url: `", request_url); + // photon::net::StringWriter writer; + // auto ret = request->POST(request_url, &writer, (int64_t)1000000); + // LOG_INFO("response: `", writer.string); + // return ret; + + auto client = photon::net::http::new_http_client(); + DEFER(delete client); + auto op = client->new_operation(photon::net::http::Verb::GET, request_url); + DEFER(delete op); + op->req.headers.content_length(0); + // std::cout << "op->req.target(): " << op->req.target() << " op->req.query(): " << op->req.query() << std::endl; + client->call(op); + auto code = op->status_code; + std::string buf; + buf.resize(op->resp.headers.content_length()); + op->resp.read((void*)buf.data(), op->resp.headers.content_length()); + LOG_INFO(VALUE(buf)); + + return code; } }; @@ -388,7 +403,7 @@ TEST_F(CreateSnapshotTest, create_snapshot) { EXPECT_EQ(imgfile0->create_snapshot(new_image_config_path.c_str()), 0); - ImageFile* imgfile1 = imgservice->create_image_file(new_image_config_path.c_str(), ""); + ImageFile* imgfile1 = imgservice->create_image_file(image_config_path.c_str(), ""); EXPECT_NE(imgfile1, nullptr); std::cout << "create_snapshot & verify" << std::endl; @@ -447,7 +462,7 @@ TEST_F(CreateSnapshotTest, create_snapshot_sparse) { EXPECT_EQ(imgfile0->create_snapshot(new_image_config_path.c_str()), 0); - ImageFile* imgfile1 = imgservice->create_image_file(new_image_config_path.c_str(), ""); + ImageFile* imgfile1 = imgservice->create_image_file(image_config_path.c_str(), ""); EXPECT_NE(imgfile1, nullptr); std::cout << "create_snapshot & verify" << std::endl;