Skip to content

Commit

Permalink
Merge pull request #5886 from BOINC/dpa_docker3
Browse files Browse the repository at this point in the history
Docker wrapper: always mount slot dir at WORKDIR
  • Loading branch information
AenBleidd authored Nov 7, 2024
2 parents ef5d42c + d06999a commit fa6d6c8
Show file tree
Hide file tree
Showing 14 changed files with 60 additions and 176 deletions.
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
File renamed without changes.
Loading

0 comments on commit fa6d6c8

Please sign in to comment.