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

Docker wrapper: always mount slot dir at WORKDIR #5886

Merged
merged 6 commits into from
Nov 7, 2024
Merged
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
52 changes: 11 additions & 41 deletions client/app_test.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -58,23 +58,15 @@
// app main main yes
// input infile in
// output outfile out
//#define APP_DOCKER_WRAPPER_COPY
//#define APP_DOCKER_WRAPPER
// type physical logical copy?
// app worker worker yes
// app job_copy.toml job_copy.toml yes
// app Dockerfile_copy Dockerfile_copy yes
// app docker_wrapper docker_wrapper
// input infile in yes
// output outfile out yes
//#define APP_DOCKER_WRAPPER_MOUNT
// type physical logical copy?
// app worker worker
// app job_mount.toml job_mount.toml yes
// app Dockerfile_mount Dockerfile_mount yes
// app job.toml job.toml yes
// app Dockerfile Dockerfile yes
// app main.sh main.sh yes
// app docker_wrapper docker_wrapper
// input infile in
// output outfile out
// input infile in no
// output outfile out yes

#ifdef APP_NONE
void CLIENT_STATE::app_test_init() {}
Expand Down Expand Up @@ -210,32 +202,21 @@ void CLIENT_STATE::app_test_init() {
*make_file(app->project, "worker", NULL, INPUT_FILE, false)
);
#endif
#ifdef APP_DOCKER_WRAPPER_COPY
#ifdef APP_DOCKER_WRAPPER
av->app_files.push_back(
*make_file(app->project, "docker_wrapper.exe", NULL, MAIN_PROG, false)
);
av->app_files.push_back(
*make_file(app->project, "worker", NULL, INPUT_FILE, true)
);
av->app_files.push_back(
*make_file(app->project, "job_copy.toml", "job.toml", INPUT_FILE, true)
);
av->app_files.push_back(
*make_file(app->project, "Dockerfile_copy", "Dockerfile", INPUT_FILE, true)
);
#endif
#ifdef APP_DOCKER_WRAPPER_MOUNT
av->app_files.push_back(
*make_file(app->project, "docker_wrapper.exe", NULL, MAIN_PROG, false)
);
av->app_files.push_back(
*make_file(app->project, "worker", NULL, INPUT_FILE, false)
*make_file(app->project, "main.sh", "main.sh", INPUT_FILE, true)
);
av->app_files.push_back(
*make_file(app->project, "job_copy.toml", "job.toml", INPUT_FILE, true)
*make_file(app->project, "job.toml", "job.toml", INPUT_FILE, true)
);
av->app_files.push_back(
*make_file(app->project, "Dockerfile_copy", "Dockerfile", INPUT_FILE, true)
*make_file(app->project, "Dockerfile", "Dockerfile", INPUT_FILE, true)
);
#endif

Expand All @@ -256,13 +237,7 @@ void CLIENT_STATE::app_test_init() {
*make_file(proj, "infile", "in", INPUT_FILE, false)
);
#endif
#ifdef APP_DOCKER_WRAPPER_COPY
wu->command_line = "--verbose";
wu->input_files.push_back(
*make_file(proj, "infile", "in", INPUT_FILE, true)
);
#endif
#ifdef APP_DOCKER_WRAPPER_MOUNT
#ifdef APP_DOCKER_WRAPPER
wu->command_line = "--verbose";
wu->input_files.push_back(
*make_file(proj, "infile", "in", INPUT_FILE, false)
Expand All @@ -277,16 +252,11 @@ void CLIENT_STATE::app_test_init() {
*make_file(proj, "outfile", "out", OUTPUT_FILE, false)
);
#endif
#ifdef APP_DOCKER_WRAPPER_COPY
#ifdef APP_DOCKER_WRAPPER
result->output_files.push_back(
*make_file(proj, "outfile", "out", OUTPUT_FILE, true)
);
#endif
#ifdef APP_DOCKER_WRAPPER_MOUNT
result->output_files.push_back(
*make_file(proj, "outfile", "out", OUTPUT_FILE, false)
);
#endif

// tell the client not to get work or run benchmarks
//
Expand Down
152 changes: 39 additions & 113 deletions samples/docker_wrapper/docker_wrapper.cpp
Original file line number Diff line number Diff line change
@@ -1,25 +1,21 @@
// docker_wrapper: runs a BOINC job in a Docker container
//
// runs in a directory (normally slot dir) containing
// runs in a directory (i.e. slot dir) containing
//
// Dockerfile
// job.toml
// optional job config file
// files added to container via Dockerfile
// other input files
//
// For now all files must be <copy_file>
// input files (link or physical)
// executable files (link or physical)
//
// Win:
// There must be a WSL image containing the Docker engine
// e.g. an Ubuntu image downloaded from the Windows app store.
// This image can access the host (Win) filesystem.
// There must be a WSL image containing Docker or Podman
// The wrapper runs a pipe-connected shell in WSL
// (running in the current dir)
// and sends commands (e.g. docker commands) via the pipe.
//
// Unix:
// The host must have the Docker engine.
// The host must have Docker or Podman
// The wrapper runs Docker commands using popen()
//
// Logic:
Expand Down Expand Up @@ -50,16 +46,15 @@
// max 255 chars
// we'll use: boinc__<proj>__<resultname>

// standalone mode:
// standalone mode (debugging):
// image name: boinc
// container name: boinc
// slot dir: .
// project dir (mount mode): project/
// project dir (if any indirect files): ./project/

// enable standalone tests on Win
// enable standalone test on Win
//
#define WIN_STANDALONE_COPY 0
#define WIN_STANDALONE_MOUNT 0
//#define WIN_STANDALONE_TEST

#include <cstdio>
#include <string>
Expand Down Expand Up @@ -93,37 +88,27 @@ struct RSC_USAGE {
}
};

struct FILE_COPY {
string src;
string dst;
};

// parsed version of job.toml
//
struct CONFIG {
string slot_dir_mount;
// mount slot dir here
string workdir;
// WORKDIR in Dockerfile; default "/app"
// slot dir will be mounted here
string project_dir_mount;
// mount project dir here
vector<FILE_COPY> copy_to_container;
vector<FILE_COPY> copy_from_container;
// mount project dir here in container
// default: don't mount it
void print() {
fprintf(stderr, "Wrapper config file:\n");
if (!slot_dir_mount.empty()) {
fprintf(stderr, " slot dir mounted at: %s\n", slot_dir_mount.c_str());
if (!workdir.empty()) {
fprintf(stderr, " workdir: %s\n", workdir.c_str());
}
if (!project_dir_mount.empty()) {
fprintf(stderr, " project dir mounted at: %s\n", project_dir_mount.c_str());
}
for (FILE_COPY c:copy_to_container) {
fprintf(stderr, " copy to:src %s dst %s\n", c.src.c_str(), c.dst.c_str());
}
for (FILE_COPY c:copy_from_container) {
fprintf(stderr, " copy from: src %s dst %s\n", c.src.c_str(), c.dst.c_str());
}
}
};

const char* project_dir;
char image_name[512];
char container_name[512];
APP_INIT_DATA aid;
Expand All @@ -134,27 +119,9 @@ const char* config_file = "job.toml";
const char* dockerfile = "Dockerfile";
DOCKER_CONN docker_conn;

// parse a list of file copy specs
//
int parse_config_copies(const toml::Value *x, vector<FILE_COPY> &copies) {
const toml::Array& ar = x->as<toml::Array>();
for (const toml::Value& a : ar) {
FILE_COPY copy;
const toml::Value *b = a.find("src");
if (!b) return -1;
copy.src = b->as<string>();
const toml::Value *c = a.find("dst");
if (!c) return -1;
copy.dst = c->as<string>();
copies.push_back(copy);
}
return 0;
}

// parse job config file
//
int parse_config_file() {
int retval;
std::ifstream ifs(config_file);
if (ifs.fail()) {
return -1;
Expand All @@ -166,25 +133,16 @@ int parse_config_file() {
}
const toml::Value &v = r.value;
const toml::Value *x;
x = v.find("slot_dir_mount");
x = v.find("workdir");
if (x) {
config.slot_dir_mount = x->as<string>();
config.workdir = x->as<string>();
} else {
config.workdir = "/app";
}
x = v.find("project_dir_mount");
if (x) {
config.project_dir_mount = x->as<string>();
}
x = v.find("copy_to_container");
if (x) {
retval = parse_config_copies(x, config.copy_to_container);
if (retval) return retval;
}
x = v.find("copy_from_container");
if (x) {
retval = parse_config_copies(x, config.copy_from_container);
if (retval) return retval;
}

return 0;
}

Expand All @@ -202,8 +160,7 @@ int error_output(vector<string> &out) {
////////// IMAGE ////////////

void get_image_name() {
char *p = strrchr(aid.project_dir, '/');
string s = docker_image_name(p+1, aid.wu_name);
string s = docker_image_name(project_dir, aid.wu_name);
strcpy(image_name, s.c_str());
}

Expand Down Expand Up @@ -257,8 +214,7 @@ int get_image() {
////////// CONTAINER ////////////

void get_container_name() {
char *p = strrchr(aid.project_dir, '/');
string s = docker_container_name(p+1, aid.result_name);
string s = docker_container_name(project_dir, aid.result_name);
strcpy(container_name, s.c_str());
}

Expand Down Expand Up @@ -289,19 +245,21 @@ int create_container() {
retval = get_image();
if (retval) return retval;

if (config.slot_dir_mount.empty()) {
slot_cmd[0] = 0;
} else {
sprintf(slot_cmd, " -v .:%s",
config.slot_dir_mount.c_str()
);
}
sprintf(slot_cmd, " -v .:%s",
config.workdir.c_str()
);
if (config.project_dir_mount.empty()) {
project_cmd[0] = 0;
} else {
sprintf(project_cmd, " -v %s:%s",
aid.project_dir, config.project_dir_mount.c_str()
);
if (boinc_is_standalone()) {
sprintf(project_cmd, " -v %s:%s",
project_dir, config.project_dir_mount.c_str()
);
} else {
sprintf(project_cmd, " -v ../../projects/%s:%s",
project_dir, config.project_dir_mount.c_str()
);
}
}
sprintf(cmd, "create --name %s %s %s %s",
container_name,
Expand All @@ -312,31 +270,6 @@ int create_container() {
if (retval) return retval;
if (error_output(out)) return -1;

// copy files into container
//
for (FILE_COPY &c: config.copy_to_container) {
sprintf(cmd, "cp %s %s:%s",
c.src.c_str(), container_name, c.dst.c_str()
);
retval = docker_conn.command(cmd, out);
if (retval) return retval;
if (error_output(out)) return -1;
}
return 0;
}

int copy_files_from_container() {
char cmd[1024];
int retval;
vector<string> out;

for (FILE_COPY &c: config.copy_from_container) {
sprintf(cmd, "cp %s:%s %s",
container_name, c.src.c_str(), c.dst.c_str()
);
retval = docker_conn.command(cmd, out);
if (retval) return retval;
}
return 0;
}

Expand Down Expand Up @@ -505,15 +438,8 @@ int main(int argc, char** argv) {
}
}

#if WIN_STANDALONE_COPY
SetCurrentDirectoryA("C:/ProgramData/BOINC/slots/test_docker_copy");
config_file = "job_copy.toml";
dockerfile = "Dockerfile_copy";
#endif
#if WIN_STANDALONE_MOUNT
SetCurrentDirectoryA("C:/ProgramData/BOINC/slots/test_docker_mount");
config_file = "job_mount.toml";
dockerfile = "Dockerfile_mount";
#ifdef WIN_STANDALONE_TEST
SetCurrentDirectoryA("C:/ProgramData/BOINC/slots/test_docker");
#endif

memset(&options, 0, sizeof(options));
Expand All @@ -526,9 +452,10 @@ int main(int argc, char** argv) {
verbose = true;
strcpy(image_name, "boinc");
strcpy(container_name, "boinc");
strcpy(aid.project_dir, "./project");
project_dir = "project";
} else {
boinc_get_init_data(aid);
project_dir = strrchr(aid.project_dir, '/')+1;
get_image_name();
get_container_name();
}
Expand Down Expand Up @@ -590,7 +517,6 @@ int main(int argc, char** argv) {
boinc_finish(1);
break;
case JOB_SUCCESS:
copy_files_from_container();
cleanup();
boinc_finish(0);
break;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
FROM debian
WORKDIR /app
COPY main.sh /app
CMD ./main.sh
1 change: 1 addition & 0 deletions samples/docker_wrapper/test/in
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
<soft_link>../../projects/foobar/infile</soft_link>
1 change: 1 addition & 0 deletions samples/docker_wrapper/test/infile
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
A screaming comes across the sky.
1 change: 1 addition & 0 deletions samples/docker_wrapper/test/job.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
project_dir_mount = "/project"
7 changes: 7 additions & 0 deletions samples/docker_wrapper/test/main.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
#! /bin/bash

resolve () {
sed 's/<soft_link>..\/..\/projects\/[^\/]*\//\/project\//; s/<\/soft_link>//' $1 | tr -d '\r\n'
}

./worker --nsecs 1 $(resolve in) out
Loading
Loading