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
snapshot_server.cpp
)
target_include_directories(overlaybd_image_lib PUBLIC
${CURL_INCLUDE_DIRS}
Expand Down
3 changes: 1 addition & 2 deletions src/config.h
Original file line number Diff line number Diff line change
Expand Up @@ -134,10 +134,9 @@ struct CertConfig : public ConfigUtils::Config {
struct ServiceConfig : public ConfigUtils::Config {
APPCFG_CLASS
APPCFG_PARA(enable, bool, false);
APPCFG_PARA(domainSocket, std::string, "");
APPCFG_PARA(address, std::string, "http://127.0.0.1:12345");
};


struct GlobalConfig : public ConfigUtils::Config {
APPCFG_CLASS

Expand Down
2 changes: 1 addition & 1 deletion src/example_config/overlaybd.json
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,6 @@
"registryFsVersion": "v2",
"serviceConfig": {
"enable": false,
"domainSocket": "/var/run/overlaybd.sock"
"address": "http://127.0.0.1:12345"
}
}
1 change: 1 addition & 0 deletions src/image_file.h
Original file line number Diff line number Diff line change
Expand Up @@ -123,6 +123,7 @@ class ImageFile : public photon::fs::ForwardFile {
// 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;
}

Expand Down
21 changes: 14 additions & 7 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 "snapshot_server.h"
#include <photon/common/alog.h>
#include <photon/common/alog-stdstring.h>
#include <photon/common/io-alloc.h>
Expand Down Expand Up @@ -441,18 +442,22 @@ int ImageService::init() {
}
}
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");
}
}
// 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}
*/
snapshot_handler.reset(new SnapshotHandler(this));
snapshoter = new SnapshotServer(global_conf.serviceConfig().address(), snapshot_handler.get());
if(!snapshoter->ready)
LOG_ERROR_RETURN(0, -1, "Failed to start http server for live snapshot");
}
return 0;
}
Expand Down Expand Up @@ -540,6 +545,8 @@ ImageService::~ImageService() {
delete global_fs.srcfs;
delete global_fs.io_alloc;
delete exporter;
delete snapshoter;

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

Expand All @@ -550,4 +557,4 @@ ImageService *create_image_service(const char *config_path) {
return nullptr;
}
return ret;
}
}
4 changes: 4 additions & 0 deletions src/image_service.h
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,8 @@ struct ImageAuthResponse : public ConfigUtils::Config {
};

struct ImageFile;
class SnapshotHandler;
struct SnapshotServer;

class ImageService {
public:
Expand All @@ -67,6 +69,8 @@ class ImageService {
struct GlobalFs global_fs;
std::unique_ptr<OverlayBDMetric> metrics;
ExporterServer *exporter = nullptr;
std::unique_ptr<SnapshotHandler> snapshot_handler;
SnapshotServer *snapshoter = nullptr;

private:
int read_global_config_and_set();
Expand Down
142 changes: 142 additions & 0 deletions src/snapshot_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 "snapshot_server.h"
#include <photon/net/http/url.h>
#include <string_view>
#include "image_service.h"
#include "image_file.h"

int SnapshotHandler::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 SnapshotHandler::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;
}
}

SnapshotServer::SnapshotServer(const std::string &addr, SnapshotHandler* 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 snapshoter port `", url.port());
if(tcpserver->listen() < 0)
LOG_ERRNO_RETURN(0, , "Failed to listen snapshoter 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("Snapshoter listening on `:`, path: `", host, url.port(), "/snapshot");
}

SnapshotServer::~SnapshotServer() {
delete tcpserver;
delete httpserver;
}
45 changes: 45 additions & 0 deletions src/snapshot_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 SnapshotHandler : public photon::net::http::HTTPHandler {
public:
ImageService *imgservice;
std::map<std::string, std::string> params;

SnapshotHandler(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 SnapshotServer {
photon::net::ISocketServer* tcpserver = nullptr;
photon::net::http::HTTPServer* httpserver = nullptr;
bool ready = false;

SnapshotServer(const std::string &addr, SnapshotHandler* handler);
~SnapshotServer();
};
Loading