Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
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
1 change: 1 addition & 0 deletions src/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ add_library(overlaybd_image_lib
prefetch.cpp
tools/sha256file.cpp
tools/comm_func.cpp
api_server.cpp
)
target_include_directories(overlaybd_image_lib PUBLIC
${CURL_INCLUDE_DIRS}
Expand Down
142 changes: 142 additions & 0 deletions src/api_server.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,142 @@
/*
Copyright The Overlaybd Authors

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

#include <photon/net/http/url.h>
#include <string_view>
#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"
})delimiter");
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"
})delimiter");
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`"
})delimiter");
goto EXIT;
}

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));
}
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();
}

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;
}
}

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);
}
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");
}

ApiServer::~ApiServer() {
delete tcpserver;
delete httpserver;
}
45 changes: 45 additions & 0 deletions src/api_server.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
/*
Copyright The Overlaybd Authors

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

#pragma once

#include <map>
#include <string>
#include <photon/net/http/server.h>
#include <photon/net/socket.h>

class ImageService;

class ApiHandler : public photon::net::http::HTTPHandler {
public:
ImageService *imgservice;
std::map<std::string, std::string> 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();
};
8 changes: 8 additions & 0 deletions src/config.h
Original file line number Diff line number Diff line change
Expand Up @@ -131,6 +131,12 @@ struct CertConfig : public ConfigUtils::Config {
APPCFG_PARA(keyFile, std::string, "");
};

struct ServiceConfig : public ConfigUtils::Config {
APPCFG_CLASS
APPCFG_PARA(enable, bool, false);
APPCFG_PARA(address, std::string, "http://127.0.0.1:9862");
};

struct GlobalConfig : public ConfigUtils::Config {
APPCFG_CLASS

Expand All @@ -155,6 +161,7 @@ struct GlobalConfig : public ConfigUtils::Config {
APPCFG_PARA(prefetchConfig, PrefetchConfig);
APPCFG_PARA(certConfig, CertConfig);
APPCFG_PARA(userAgent, std::string, OVERLAYBD_VERSION);
APPCFG_PARA(serviceConfig, ServiceConfig);
};

struct AuthConfig : public ConfigUtils::Config {
Expand All @@ -170,4 +177,5 @@ struct ImageAuthResponse : public ConfigUtils::Config {
APPCFG_PARA(data, AuthConfig);
};


} // namespace ImageConfigNS
6 changes: 5 additions & 1 deletion src/example_config/overlaybd.json
Original file line number Diff line number Diff line change
Expand Up @@ -36,5 +36,9 @@
},
"enableAudit": true,
"auditPath": "/var/log/overlaybd-audit.log",
"registryFsVersion": "v2"
"registryFsVersion": "v2",
"serviceConfig": {
"enable": false,
"address": "http://127.0.0.1:9862"
}
}
17 changes: 16 additions & 1 deletion src/image_file.h
Original file line number Diff line number Diff line change
Expand Up @@ -41,10 +41,15 @@ static std::string SEALED_FILE_NAME = "overlaybd.sealed";

class ImageFile : public photon::fs::ForwardFile {
public:
ImageFile(ImageConfigNS::ImageConfig &_conf, ImageService &is)
ImageFile(ImageConfigNS::ImageConfig &_conf, ImageService &is, const std::string &dev_id)
: ForwardFile(nullptr), image_service(is), m_lower_file(nullptr) {
conf.CopyFrom(_conf, conf.GetAllocator());
m_exception = "";
if(image_service.register_image_file(dev_id, this) != 0) { // register itself
set_failed("duplicated dev id: " + dev_id);
return;
}
m_dev_id = dev_id;
m_status = init_image_file();
if (m_status == 1) {
struct stat st;
Expand All @@ -55,6 +60,7 @@ class ImageFile : public photon::fs::ForwardFile {

~ImageFile() {
m_status = -1;
image_service.unregister_image_file(m_dev_id); // unregister itself
if (dl_thread_jh != nullptr)
photon::thread_join(dl_thread_jh);
delete m_prefetcher;
Expand Down Expand Up @@ -113,6 +119,14 @@ class ImageFile : public photon::fs::ForwardFile {

int compact(IFile *as);

int create_snapshot(const char *config_path) {
// load new config file to get the snapshot layer path
// open new upper layer
// restack() current RW layer as snapshot layer
LOG_INFO("call create_snapshot, dev_id: `", m_dev_id);
return 0;
}

private:
Prefetcher *m_prefetcher = nullptr;
ImageConfigNS::ImageConfig conf;
Expand All @@ -121,6 +135,7 @@ class ImageFile : public photon::fs::ForwardFile {
ImageService &image_service;
photon::fs::IFile *m_lower_file = nullptr;
photon::fs::IFile *m_upper_file = nullptr;
std::string m_dev_id = "";

int init_image_file();
template<typename...Ts> void set_failed(const Ts&...xs);
Expand Down
50 changes: 47 additions & 3 deletions src/image_service.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
#include "image_service.h"
#include "config.h"
#include "image_file.h"
#include "api_server.h"
#include <photon/common/alog.h>
#include <photon/common/alog-stdstring.h>
#include <photon/common/io-alloc.h>
Expand Down Expand Up @@ -440,6 +441,24 @@ int ImageService::init() {
10000000, (uint64_t)1048576 * 4096, global_fs.io_alloc);
}
}
if (global_conf.serviceConfig().enable()) {
// auto sock_path = global_conf.serviceConfig().domainSocket();
// if (access(sock_path.c_str(), 0) == 0) {
// if (unlink(sock_path.c_str()) != 0) {
// LOG_ERRNO_RETURN(0, -1, "failed to remove old socket file");
// }
// }
// listen the domainSocket and create a HTTP SERVER
/*
handler definition:
- 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)
LOG_ERROR_RETURN(0, -1, "Failed to start http server for live snapshot");
}
return 0;
}

Expand All @@ -456,7 +475,7 @@ bool ImageService::enable_acceleration() {
}
}

ImageFile *ImageService::create_image_file(const char *config_path) {
ImageFile *ImageService::create_image_file(const char *config_path, const std::string &dev_id) {
ImageConfigNS::GlobalConfig defaultDlCfg;
if (!defaultDlCfg.ParseJSON(m_config_path)) {
LOG_WARN("default download config parse failed, ignore");
Expand All @@ -479,7 +498,7 @@ ImageFile *ImageService::create_image_file(const char *config_path) {
}

auto resFile = cfg.resultFile();
ImageFile *ret = new ImageFile(cfg, *this);
ImageFile *ret = new ImageFile(cfg, *this, dev_id);
if (ret->m_status <= 0) {
std::string data = "failed:" + ret->m_exception;
set_result_file(resFile, data);
Expand All @@ -491,6 +510,29 @@ ImageFile *ImageService::create_image_file(const char *config_path) {
return ret;
}

int ImageService::register_image_file(const std::string& dev_id, ImageFile* file) {
if (dev_id.empty())
return 0;
if(find_image_file(dev_id) != nullptr)
LOG_ERROR_RETURN(0, -1, "dev id exists: `", dev_id);
m_image_files[dev_id] = file;
LOG_INFO("Registered image file for dev_id: `", dev_id);
return 0;
}

int ImageService::unregister_image_file(const std::string& dev_id) {
if (dev_id.empty())
return 0;
m_image_files.erase(dev_id);
LOG_INFO("Unregistered image file for dev_id: `", dev_id);
return 0;
}

ImageFile* ImageService::find_image_file(const std::string& dev_id) {
auto it = m_image_files.find(dev_id);
return (it != m_image_files.end()) ? it->second : nullptr;
}

ImageService::ImageService(const char *config_path) {
m_config_path = config_path ? config_path : DEFAULT_CONFIG_PATH;
}
Expand All @@ -503,6 +545,8 @@ ImageService::~ImageService() {
delete global_fs.srcfs;
delete global_fs.io_alloc;
delete exporter;
delete api_server;

LOG_INFO("image service is fully stopped");
}

Expand All @@ -513,4 +557,4 @@ ImageService *create_image_service(const char *config_path) {
return nullptr;
}
return ret;
}
}
Loading