fix and simplify docker detection

Signed-off-by: Vitalii Koshura <lestat.de.lionkur@gmail.com>
This commit is contained in:
Vitalii Koshura 2024-08-14 05:20:52 +02:00
parent 6c3d4eb832
commit ecc6d7899c
No known key found for this signature in database
GPG Key ID: CE0DB1726070A5A3
18 changed files with 423 additions and 377 deletions

View File

@ -280,24 +280,15 @@ void CLIENT_STATE::show_host_info() {
}
#endif
}
if (host_info.docker_use){
if (host_info.docker_available) {
msg_printf(NULL, MSG_INFO, "Docker is installed and available");
}
else{
} else {
msg_printf(NULL, MSG_INFO, "Docker is not installed or is not available for running task");
}
if (strlen(host_info.docker_compose_version)){
if ((strstr(host_info.docker_compose_version, "v1")) && (strstr(host_info.docker_compose_version, "v2"))){
msg_printf(NULL, MSG_INFO, "Docker compose (new and old versions: docker-compose and docker compose) is installed and available for running task");
}else if (strstr(host_info.docker_compose_version, "v1")) {
msg_printf(NULL, MSG_INFO, "Docker compose (old version: docker-compose) is installed and available for running task");
}else if (strstr(host_info.docker_compose_version, "v2")){
msg_printf(NULL, MSG_INFO, "Docker compose (new version: docker compose) is installed and available for running task");
}
else{
msg_printf(NULL, MSG_INFO, "Docker compose is not installed or is not available for running task");
}
if (host_info.docker_compose_available) {
msg_printf(NULL, MSG_INFO, "Docker compose is installed and available");
} else {
msg_printf(NULL, MSG_INFO, "Docker compose is not installed or is not available for running task");
}
}

View File

@ -1,6 +1,6 @@
// This file is part of BOINC.
// http://boinc.berkeley.edu
// Copyright (C) 2022 University of California
// https://boinc.berkeley.edu
// Copyright (C) 2024 University of California
//
// BOINC is free software; you can redistribute it and/or modify it
// under the terms of the GNU Lesser General Public License
@ -977,13 +977,6 @@ int CLIENT_STATE::parse_app_info(PROJECT* p, FILE* in) {
delete avp;
continue;
}
if (cc_config.dont_use_docker_compose && strstr(avp->plan_class, "docker")) {
msg_printf(p, MSG_INFO,
"skipping app with docker compose in app_info.xml; docker compose disabled in cc_config.xml"
);
delete avp;
continue;
}
if (strlen(avp->platform) == 0) {
safe_strcpy(avp->platform, get_primary_platform());
}

View File

@ -1,6 +1,6 @@
// This file is part of BOINC.
// http://boinc.berkeley.edu
// Copyright (C) 2021 University of California
// https://boinc.berkeley.edu
// Copyright (C) 2024 University of California
//
// BOINC is free software; you can redistribute it and/or modify it
// under the terms of the GNU Lesser General Public License
@ -20,7 +20,6 @@
// Try to keep this well-organized and not nested.
#include "version.h" // version numbers from autoconf
#include <fstream>
#include "cpp.h"
#include "config.h"
@ -1237,91 +1236,38 @@ int HOST_INFO::get_virtualbox_version() {
//check if docker compose or docker-compose is installed on volunteer's host
//
int HOST_INFO::get_docker_compose_info(){
FILE* fd;
char buf[MAXPATHLEN];
std::ofstream compose_file ("docker-compose.yaml");
compose_file << "version: \"2\"\nservices: \n hello: \n image: \"hello-world\" \n" << std::endl;
char* docker_command = "docker-compose up 2>&1";
fd = popen(docker_command, "r");
if (fd){
while(!feof(fd)){
if (fgets(buf, sizeof(buf), fd)){
if (strstr(buf, "Hello from Docker!")){
safe_strcat(docker_compose_version, "v1");
break;
}
}
FILE* f = popen(command_get_docker_compose_version, "r");
if (f) {
char buf[256];
fgets(buf, 256, f);
std::string version;
if (get_docker_compose_version_string(buf, version)) {
docker_compose_available = true;
safe_strcpy(docker_compose_version, version.c_str());
}
pclose(fd);
pclose(f);
return 0;
}
docker_command = "docker compose up 2>&1";
fd = popen(docker_command, "r");
if (fd){
while(!feof(fd)){
if (fgets(buf, sizeof(buf), fd)){
if (strstr(buf, "Hello from Docker!")){
safe_strcat(docker_compose_version, "v2");
break;
}
}
}
pclose(fd);
}
std::remove("docker-compose.yaml");
if (!(strstr(docker_compose_version, "v1"))){
if (!(strstr(docker_compose_version, "v2"))){
safe_strcat(docker_compose_version, "not_used");
}
}
return 0;
return 1;
}
//check if docker is installed on volunteer's host
//
int HOST_INFO::get_docker_info(bool& docker_use){
char buf[256];
char buf_command[256];
FILE* fd;
FILE* fd_1;
char docker_cmd [256];
strcpy(docker_cmd, "which -a docker 2>&1");
fd = popen(docker_cmd, "r");
if (fd){
while(!feof(fd)){
if (fgets(buf, sizeof(buf), fd)){
strip_whitespace(buf);
if (!(access(buf, X_OK))){
strcpy(docker_cmd, buf);
strcat(docker_cmd, " run --rm hello-world 2>&1");
fd_1 = popen(docker_cmd, "r");
if (fd_1){
while(!feof(fd_1)){
if (fgets(buf_command, sizeof(buf_command), fd_1)){
if (strstr(buf_command, "Hello from Docker!")){
docker_use = true;
break;
}
}
}
pclose(fd_1);
}
}
if (docker_use){
break;
}
}
int HOST_INFO::get_docker_info(){
FILE* f = popen(command_get_docker_version, "r");
if (f) {
char buf[256];
fgets(buf, 256, f);
std::string version;
if (get_docker_version_string(buf, version)) {
docker_available = true;
safe_strcpy(docker_version, version.c_str());
}
pclose(fd);
pclose(f);
return 0;
}
return 0;
return 1;
}
@ -1773,10 +1719,7 @@ int HOST_INFO::get_host_info(bool init) {
}
if(!cc_config.dont_use_docker){
get_docker_info(docker_use);
}
if(!cc_config.dont_use_docker_compose){
get_docker_info();
get_docker_compose_info();
}

View File

@ -1,6 +1,6 @@
// This file is part of BOINC.
// http://boinc.berkeley.edu
// Copyright (C) 2018 University of California
// https://boinc.berkeley.edu
// Copyright (C) 2024 University of California
//
// BOINC is free software; you can redistribute it and/or modify it
// under the terms of the GNU Lesser General Public License
@ -29,8 +29,6 @@
#include "str_util.h"
#include "str_replace.h"
#include "util.h"
#include <fstream>
#include "client_msgs.h"
#include "client_types.h"
@ -1552,98 +1550,6 @@ int get_network_usage_totals(unsigned int& total_received, unsigned int& total_s
return iRetVal;
}
//check if docker compose or docker-compose is installed on volunteer's host
//
int HOST_INFO::get_docker_compose_info(){
FILE* fd;
char buf[MAXPATHLEN];
std::ofstream compose_file ("docker-compose.yaml");
compose_file << "version: \"2\"\nservices: \n hello: \n image: \"hello-world\" \n" << std::endl;
char* docker_command = "wsl docker-compose up 2>&1";
fd = _popen(docker_command, "r");
if (fd){
while(!feof(fd)){
if (fgets(buf, sizeof(buf), fd)){
if (strstr(buf, "Hello from Docker!")){
safe_strcat(docker_compose_version, "v1");
break;
}
}
}
_pclose(fd);
}
docker_command = "wsl docker compose up 2>&1";
fd = _popen(docker_command, "r");
if (fd){
while(!feof(fd)){
if (fgets(buf, sizeof(buf), fd)){
if (strstr(buf, "Hello from Docker!")){
safe_strcat(docker_compose_version, "v2");
break;
}
}
}
_pclose(fd);
}
std::remove("docker-compose.yaml");
if (!(strstr(docker_compose_version, "v1"))){
if (!(strstr(docker_compose_version, "v2"))){
safe_strcat(docker_compose_version, "not_used");
}
}
return 0;
}
//check if docker is installed on volunteer's host
//
int HOST_INFO::get_docker_info(bool& docker_use){
char buf[256];
FILE* fd;
FILE *fd_1;
char* docker_command = "wsl which -a docker 2>&1";
fd = _popen(docker_command, "r");
if (fd) {
while (!feof(fd)){
if (fgets(buf + 4, sizeof(buf), fd)){
buf[0] = 'w';
buf[1] = 's';
buf[2] = 'l';
buf[3] = ' ';
int i, j;
for (i = 0, j = 0; buf[i]; i++) {
if (buf[i] != '\n') {
buf[j++] = buf[i];
}
}
buf[j] = '\0';
docker_command = strcat(buf, " run --rm hello-world 2>&1");
fd_1 = _popen(docker_command, "r");
if (fd_1){
while (!feof(fd_1)){
if (fgets(buf, sizeof(buf), fd_1)){
if (strstr(buf, "Hello from Docker!")){
docker_use = true;
break;
}
}
}
_pclose(fd_1);
}
}
}
_pclose(fd);
}
return 0;
}
// see if Virtualbox is installed
//
int HOST_INFO::get_virtualbox_version() {
@ -1763,26 +1669,9 @@ int HOST_INFO::get_host_info(bool init) {
if (!cc_config.dont_use_wsl) {
OSVERSIONINFOEX osvi;
if (get_OSVERSIONINFO(osvi) && osvi.dwMajorVersion >= 10) {
get_wsl_information(wsl_available, wsls);
get_wsl_information(wsl_available, wsls, !cc_config.dont_use_docker, docker_available, docker_compose_available);
}
}
if ((!cc_config.dont_use_docker) && (!cc_config.dont_use_wsl)){
if (wsl_available){
for (size_t i = 0; i < wsls.wsls.size(); ++i){
const WSL& wsl = wsls.wsls[i];
if (wsl.is_default){
if (wsl.version.find("WSL2") != std::string::npos){
get_docker_info(docker_use);
if (!cc_config.dont_use_docker_compose){
get_docker_compose_info();
}
}
}
}
}
}
#endif
if (!cc_config.dont_use_vbox) {
get_virtualbox_version();

View File

@ -98,15 +98,74 @@ bool get_available_wsls(std::vector<std::pair<std::string, DWORD>>& wsls, std::s
typedef HRESULT(WINAPI *PWslLaunch)(PCWSTR, PCWSTR, BOOL, HANDLE, HANDLE, HANDLE, HANDLE*);
HINSTANCE wsl_lib = NULL;
struct resource_helper {
public:
HINSTANCE wsl_lib = NULL;
HANDLE in_read = NULL;
HANDLE in_write = NULL;
HANDLE out_read = NULL;
HANDLE out_write = NULL;
PWslLaunch pWslLaunch = NULL;
HANDLE in_read = NULL;
HANDLE in_write = NULL;
HANDLE out_read = NULL;
HANDLE out_write = NULL;
~resource_helper() {
close_handle(in_read);
close_handle(in_write);
close_handle(out_read);
close_handle(out_write);
PWslLaunch pWslLaunch = NULL;
if (wsl_lib) {
FreeLibrary(wsl_lib);
}
}
// prepare resources
int prepare_resources() {
wsl_lib = NULL;
in_read = NULL;
in_write = NULL;
out_read = NULL;
out_write = NULL;
pWslLaunch = NULL;
wsl_lib = LoadLibrary("wslapi.dll");
if (!wsl_lib) {
return 1;
}
pWslLaunch = (PWslLaunch)GetProcAddress(wsl_lib, "WslLaunch");
if (!pWslLaunch) {
return 1;
}
SECURITY_ATTRIBUTES sa;
sa.nLength = sizeof(SECURITY_ATTRIBUTES);
sa.bInheritHandle = TRUE;
sa.lpSecurityDescriptor = NULL;
if (!CreatePipe(&out_read, &out_write, &sa, 0)) {
return 1;
}
if (!SetHandleInformation(out_read, HANDLE_FLAG_INHERIT, 0)) {
return 1;
}
if (!CreatePipe(&in_read, &in_write, &sa, 0)) {
return 1;
}
if (!SetHandleInformation(in_write, HANDLE_FLAG_INHERIT, 0)) {
return 1;
}
return 0;
}
private:
inline void close_handle(HANDLE handle) {
if (handle) {
CloseHandle(handle);
}
}
};
//convert std::string to PCWSTR
//taken from https://stackoverflow.com/questions/27220/how-to-convert-stdstring-to-lpcwstr-in-c-unicode
@ -121,11 +180,11 @@ std::wstring s2ws(const std::string& s)
return r;
}
bool create_wsl_process(const std::string& wsl_distro_name, const std::string& command, HANDLE* handle, bool use_current_work_dir = false) {
return (pWslLaunch(s2ws(wsl_distro_name).c_str(), s2ws(command).c_str(), use_current_work_dir, in_read, out_write, out_write, handle) == S_OK);
bool create_wsl_process(const resource_helper& rs, const std::string& wsl_distro_name, const std::string& command, HANDLE* handle, bool use_current_work_dir = false) {
return (rs.pWslLaunch(s2ws(wsl_distro_name).c_str(), s2ws(command).c_str(), use_current_work_dir, rs.in_read, rs.out_write, rs.out_write, handle) == S_OK);
}
bool CreateWslProcess(const std::string& wsl_app, const std::string& command, HANDLE& handle) {
bool CreateWslProcess(const HANDLE& out_write, const std::string& wsl_app, const std::string& command, HANDLE& handle) {
PROCESS_INFORMATION pi;
STARTUPINFO si;
@ -152,26 +211,7 @@ bool CreateWslProcess(const std::string& wsl_app, const std::string& command, HA
return res;
}
inline void close_handle(HANDLE handle) {
if (handle) {
CloseHandle(handle);
}
}
int free_resources_and_exit(const int return_code) {
close_handle(in_read);
close_handle(in_write);
close_handle(out_read);
close_handle(out_write);
if (wsl_lib) {
FreeLibrary(wsl_lib);
}
return return_code;
}
std::string read_from_pipe(HANDLE handle) {
std::string read_from_pipe(const HANDLE& handle, const HANDLE& out_read) {
DWORD avail, read, exitcode;
const int bufsize = 256;
char buf[bufsize];
@ -222,14 +262,7 @@ void parse_sysctl_output(const std::vector<std::string>& lines, std::string& ost
// Returns the OS name and version for WSL when enabled
//
int get_wsl_information(bool& wsl_available, WSLS& wsls) {
wsl_lib = NULL;
in_read = NULL;
in_write = NULL;
out_read = NULL;
out_write = NULL;
pWslLaunch = NULL;
int get_wsl_information(bool& wsl_available, WSLS& wsls, bool detect_docker, bool& docker_available, bool& docker_compose_available) {
std::vector<std::pair<std::string, DWORD>> distros;
std::string default_distro;
@ -237,44 +270,25 @@ int get_wsl_information(bool& wsl_available, WSLS& wsls) {
return 1;
}
wsl_lib = LoadLibrary("wslapi.dll");
if (!wsl_lib) {
resource_helper rs;
if (rs.prepare_resources()) {
return 1;
}
pWslLaunch = (PWslLaunch) GetProcAddress(wsl_lib, "WslLaunch");
if (!pWslLaunch) {
free_resources_and_exit(1);
}
wsl_available = false;
docker_available = false;
docker_compose_available = false;
SECURITY_ATTRIBUTES sa;
HANDLE handle;
sa.nLength = sizeof(SECURITY_ATTRIBUTES);
sa.bInheritHandle = TRUE;
sa.lpSecurityDescriptor = NULL;
if (!CreatePipe(&out_read, &out_write, &sa, 0)) {
return 1;
}
if (!SetHandleInformation(out_read, HANDLE_FLAG_INHERIT, 0)) {
return free_resources_and_exit(1);
}
if (!CreatePipe(&in_read, &in_write, &sa, 0)) {
return free_resources_and_exit(1);
}
if (!SetHandleInformation(in_write, HANDLE_FLAG_INHERIT, 0)) {
return free_resources_and_exit(1);
}
for (size_t i = 0; i < distros.size(); ++i) {
char wsl_dist_name[256];
char wsl_dist_version[256];
const std::string& distro = distros[i].first;
// skip 'docker-desktop-data'
// Ref: https://stackoverflow.com/a/61431088/4210508
if (distro == "docker-desktop-data"){
continue;
}
@ -288,32 +302,32 @@ int get_wsl_information(bool& wsl_available, WSLS& wsls) {
wsl.wsl_version = std::to_string(distros[i].second);
// lsbrelease
if (!create_wsl_process(distro, command_lsbrelease, &handle)) {
if (!create_wsl_process(rs, distro, command_lsbrelease, &handle)) {
continue;
}
wsl_available = HOST_INFO::parse_linux_os_info(
read_from_pipe(handle), lsbrelease, wsl_dist_name, sizeof(wsl_dist_name), wsl_dist_version, sizeof(wsl_dist_version));
read_from_pipe(handle, rs.out_read), lsbrelease, wsl_dist_name, sizeof(wsl_dist_name), wsl_dist_version, sizeof(wsl_dist_version));
CloseHandle(handle);
if (!wsl_available) {
//osrelease
const std::string command_osrelease = "cat " + std::string(file_osrelease);
if (!create_wsl_process(distro, command_osrelease, &handle)) {
if (!create_wsl_process(rs, distro, command_osrelease, &handle)) {
continue;
}
wsl_available = HOST_INFO::parse_linux_os_info(
read_from_pipe(handle), osrelease, wsl_dist_name, sizeof(wsl_dist_name), wsl_dist_version, sizeof(wsl_dist_version));
read_from_pipe(handle, rs.out_read), osrelease, wsl_dist_name, sizeof(wsl_dist_name), wsl_dist_version, sizeof(wsl_dist_version));
CloseHandle(handle);
}
//redhatrelease
if (!wsl_available) {
const std::string command_redhatrelease = "cat " + std::string(file_redhatrelease);
if (!create_wsl_process(distro, command_redhatrelease, &handle)) {
if (!create_wsl_process(rs, distro, command_redhatrelease, &handle)) {
continue;
}
wsl_available = HOST_INFO::parse_linux_os_info(
read_from_pipe(handle), redhatrelease, wsl_dist_name, sizeof(wsl_dist_name), wsl_dist_version, sizeof(wsl_dist_version));
read_from_pipe(handle, rs.out_read), redhatrelease, wsl_dist_name, sizeof(wsl_dist_name), wsl_dist_version, sizeof(wsl_dist_version));
CloseHandle(handle);
}
@ -326,16 +340,16 @@ int get_wsl_information(bool& wsl_available, WSLS& wsls) {
// sysctl -a
const std::string command_sysctl = "sysctl -a";
if (create_wsl_process(distro, command_sysctl, &handle)) {
parse_sysctl_output(split(read_from_pipe(handle), '\n'), os_name, os_version_extra);
if (create_wsl_process(rs, distro, command_sysctl, &handle)) {
parse_sysctl_output(split(read_from_pipe(handle, rs.out_read), '\n'), os_name, os_version_extra);
CloseHandle(handle);
}
// uname -s
if (os_name.empty()) {
const std::string command_uname_s = "uname -s";
if (create_wsl_process(distro, command_uname_s, &handle)) {
os_name = read_from_pipe(handle);
if (create_wsl_process(rs, distro, command_uname_s, &handle)) {
os_name = read_from_pipe(handle, rs.out_read);
strip_whitespace(os_name);
CloseHandle(handle);
}
@ -344,8 +358,8 @@ int get_wsl_information(bool& wsl_available, WSLS& wsls) {
// uname -r
if (os_version_extra.empty()) {
const std::string command_uname_r = "uname -r";
if (create_wsl_process(distro, command_uname_r ,&handle)) {
os_version_extra = read_from_pipe(handle);
if (create_wsl_process(rs, distro, command_uname_r ,&handle)) {
os_version_extra = read_from_pipe(handle, rs.out_read);
strip_whitespace(os_version_extra);
CloseHandle(handle);
}
@ -363,10 +377,34 @@ int get_wsl_information(bool& wsl_available, WSLS& wsls) {
else {
wsl.os_version = wsl_dist_version;
}
if (detect_docker) {
if (create_wsl_process(rs, distro, command_get_docker_version, &handle)) {
std::string raw = read_from_pipe(handle, rs.out_read);
std::string version;
wsl.is_docker_available = HOST_INFO::get_docker_version_string(raw, version);
if (wsl.is_docker_available) {
docker_available = true;
wsl.docker_version = version;
}
CloseHandle(handle);
}
if (create_wsl_process(rs, distro, command_get_docker_compose_version, &handle)) {
std::string raw = read_from_pipe(handle, rs.out_read);
std::string version;
wsl.is_docker_compose_available = HOST_INFO::get_docker_compose_version_string(raw, version);
if (wsl.is_docker_compose_available) {
docker_compose_available = true;
wsl.docker_compose_version = version;
}
CloseHandle(handle);
}
}
wsls.wsls.push_back(wsl);
}
return free_resources_and_exit(0);
return 0;
}
#endif // _WIN64

View File

@ -1,6 +1,6 @@
// This file is part of BOINC.
// http://boinc.berkeley.edu
// Copyright (C) 2018 University of California
// https://boinc.berkeley.edu
// Copyright (C) 2024 University of California
//
// BOINC is free software; you can redistribute it and/or modify it
// under the terms of the GNU Lesser General Public License
@ -198,11 +198,8 @@ void CC_CONFIG::show() {
if (dont_use_wsl) {
msg_printf(NULL, MSG_INFO, "Config: don't use the Windows Subsystem for Linux");
}
if (dont_use_docker){
if (dont_use_docker) {
msg_printf(NULL, MSG_INFO, "Config: don't use the Docker");
}
if (dont_use_docker_compose){
msg_printf(NULL, MSG_INFO, "Config: don't use the Docker compose");
}
for (i=0; i<alt_platforms.size(); i++) {
msg_printf(NULL, MSG_INFO,
@ -380,7 +377,6 @@ int CC_CONFIG::parse_options_client(XML_PARSER& xp) {
if (xp.parse_bool("dont_use_vbox", dont_use_vbox)) continue;
if (xp.parse_bool("dont_use_wsl", dont_use_wsl)) continue;
if (xp.parse_bool("dont_use_docker", dont_use_docker)) continue;
if (xp.parse_bool("dont_use_docker_compose", dont_use_docker_compose)) continue;
if (xp.match_tag("exclude_gpu")) {
EXCLUDE_GPU eg;
retval = eg.parse(xp);

View File

@ -1,6 +1,6 @@
// This file is part of BOINC.
// http://boinc.berkeley.edu
// Copyright (C) 2023 University of California
// https://boinc.berkeley.edu
// Copyright (C) 2024 University of California
//
// BOINC is free software; you can redistribute it and/or modify it
// under the terms of the GNU Lesser General Public License
@ -376,7 +376,9 @@ struct HOST {
WSLS wsls;
//Docker available
bool docker_use;
bool docker_available;
bool docker_compose_available;
char docker_version[256];
char docker_compose_version[256];
// stuff from time_stats

View File

@ -1,6 +1,6 @@
// This file is part of BOINC.
// http://boinc.berkeley.edu
// Copyright (C) 2018 University of California
// https://boinc.berkeley.edu
// Copyright (C) 2024 University of California
//
// BOINC is free software; you can redistribute it and/or modify it
// under the terms of the GNU Lesser General Public License
@ -216,7 +216,6 @@ void CC_CONFIG::defaults() {
dont_use_vbox = false;
dont_use_wsl = false;
dont_use_docker = false;
dont_use_docker_compose = false;
exclude_gpus.clear();
exclusive_apps.clear();
exclusive_gpu_apps.clear();
@ -350,7 +349,6 @@ int CC_CONFIG::parse_options(XML_PARSER& xp) {
if (xp.parse_bool("dont_suspend_nci", dont_suspend_nci)) continue;
if (xp.parse_bool("dont_use_vbox", dont_use_vbox)) continue;
if (xp.parse_bool("dont_use_docker", dont_use_docker)) continue;
if (xp.parse_bool("dont_use_docker_compose", dont_use_docker_compose)) continue;
if (xp.parse_bool("dont_use_wsl", dont_use_wsl)) continue;
if (xp.match_tag("exclude_gpu")) {
EXCLUDE_GPU eg;
@ -570,8 +568,7 @@ int CC_CONFIG::write(MIOFILE& out, LOG_FLAGS& log_flags) {
" <dont_suspend_nci>%d</dont_suspend_nci>\n"
" <dont_use_vbox>%d</dont_use_vbox>\n"
" <dont_use_wsl>%d</dont_use_wsl>\n"
" <dont_use_docker>%d</dont_use_docker>\n"
" <dont_use_docker_compose>%d</dont_use_docker_compose>\n",
" <dont_use_docker>%d</dont_use_docker>\n",
disallow_attach,
dont_check_file_sizes,
dont_contact_ref_site,
@ -579,8 +576,7 @@ int CC_CONFIG::write(MIOFILE& out, LOG_FLAGS& log_flags) {
dont_suspend_nci,
dont_use_vbox,
dont_use_wsl,
dont_use_docker,
dont_use_docker_compose
dont_use_docker
);
for (i=0; i<exclude_gpus.size(); i++) {

View File

@ -1,6 +1,6 @@
// This file is part of BOINC.
// http://boinc.berkeley.edu
// Copyright (C) 2018 University of California
// https://boinc.berkeley.edu
// Copyright (C) 2024 University of California
//
// BOINC is free software; you can redistribute it and/or modify it
// under the terms of the GNU Lesser General Public License
@ -168,7 +168,6 @@ struct CC_CONFIG {
bool dont_use_vbox;
bool dont_use_wsl;
bool dont_use_docker;
bool dont_use_docker_compose;
std::vector<EXCLUDE_GPU> exclude_gpus;
std::vector<std::string> exclusive_apps;
std::vector<std::string> exclusive_gpu_apps;

View File

@ -1,6 +1,6 @@
// This file is part of BOINC.
// http://boinc.berkeley.edu
// Copyright (C) 2023 University of California
// https://boinc.berkeley.edu
// Copyright (C) 2024 University of California
//
// BOINC is free software; you can redistribute it and/or modify it
// under the terms of the GNU Lesser General Public License
@ -71,9 +71,15 @@ void HOST_INFO::clear_host_info() {
safe_strcpy(os_name, "");
safe_strcpy(os_version, "");
#ifdef _WIN64
wsl_available = false;
docker_use =false;
#endif
docker_available = false;
docker_compose_available = false;
#ifndef _WIN64
safe_strcpy(docker_version, "");
safe_strcpy(docker_compose_version, "");
#endif
#ifdef _WIN64
wsls.clear();
#endif
@ -135,14 +141,17 @@ int HOST_INFO::parse(XML_PARSER& xp, bool static_items_only) {
if (xp.parse_str("os_name", os_name, sizeof(os_name))) continue;
if (xp.parse_str("os_version", os_version, sizeof(os_version))) continue;
#ifdef _WIN64
if (xp.parse_bool("os_wsl_enabled", wsl_available)) continue;
if (xp.parse_bool("wsl_available", wsl_available)) continue;
if (xp.match_tag("wsl")) {
this->wsls.parse(xp);
continue;
}
#endif
if (xp.parse_bool("docker_use", docker_use)) continue;
if (xp.parse_bool("docker_available", docker_available)) continue;
#ifndef _WIN64
if (xp.parse_str("docker_version", docker_version, sizeof(docker_version))) continue;
if (xp.parse_str("docker_compose_version", docker_compose_version, sizeof(docker_compose_version))) continue;
#endif
if (xp.parse_str("product_name", product_name, sizeof(product_name))) continue;
if (xp.parse_str("virtualbox_version", virtualbox_version, sizeof(virtualbox_version))) continue;
if (xp.match_tag("coprocs")) {
@ -212,7 +221,7 @@ int HOST_INFO::write(
" <os_version>%s</os_version>\n"
" <n_usable_coprocs>%d</n_usable_coprocs>\n"
" <wsl_available>%d</wsl_available>\n"
" <docker_use>%d</docker_use>\n",
" <docker_available>%d</docker_available>\n",
host_cpid,
p_ncpus,
pv,
@ -236,12 +245,26 @@ int HOST_INFO::write(
#else
0,
#endif
docker_use ? 1 : 0
docker_available ? 1 : 0
);
#ifdef _WIN64
if (wsl_available) {
wsls.write_xml(out);
}
#endif
#ifndef _WIN64
if (strlen(docker_version)) {
out.printf(
" <docker_version>%s</docker_version>\n",
docker_version
);
}
if (strlen(docker_compose_version)) {
out.printf(
" <docker_compose_version>%s</docker_compose_version>\n",
docker_compose_version
);
}
#endif
if (strlen(product_name)) {
xml_escape(product_name, pn, sizeof(pn));
@ -264,14 +287,20 @@ int HOST_INFO::write(
buf
);
}
if (strlen(docker_compose_version)){
char buf[256];
xml_escape(docker_compose_version, buf, sizeof(buf));
#ifndef _WIN64
if (docker_available){
out.printf(
" <docker_compose_version>%s</docker_compose_version>\n",
buf
" <docker_version>%s</docker_compose_version>\n",
docker_version
);
}
if (docker_compose_available){
out.printf(
" <docker_compose_version>%s</docker_compose_version>\n",
docker_compose_version
);
}
#endif
if (include_coprocs) {
this->coprocs.write_xml(out, false);
}
@ -331,3 +360,27 @@ int HOST_INFO::write_cpu_benchmarks(FILE* out) {
);
return 0;
}
bool HOST_INFO::get_docker_version_string(std::string raw, std::string& parsed) {
std::string prefix = "Docker version";
size_t pos1 = raw.find(prefix);
if (pos1 == std::string::npos) {
return false;
}
size_t pos2 = raw.find(",");
if (pos2 == std::string::npos) {
return false;
}
parsed = raw.substr(pos1 + prefix.size() + 1, pos2 - pos1 - prefix.size() - 1);
return true;
}
bool HOST_INFO::get_docker_compose_version_string(std::string raw, std::string& parsed) {
std::string prefix = "Docker Compose version v";
size_t pos1 = raw.find(prefix);
if (pos1 == std::string::npos) {
return false;
}
parsed = raw.substr(pos1 + prefix.size(), raw.size() - pos1 - prefix.size());
return true;
}

View File

@ -47,6 +47,8 @@ enum LINUX_OS_INFO_PARSER {
const char command_lsbrelease[] = "/usr/bin/lsb_release -a 2>&1";
const char file_osrelease[] = "/etc/os-release";
const char file_redhatrelease[] = "/etc/redhat-release";
const char command_get_docker_version[] = "docker --version";
const char command_get_docker_compose_version[] = "docker compose version";
// if you add fields, update clear_host_info()
@ -80,11 +82,19 @@ public:
char os_name[256];
char os_version[256];
bool docker_available;
bool docker_compose_available;
#ifndef _WIN64
// on Windows we can have several docker installation within WSL
// that is why it makes no sense to have this information put here
// instead the information about the available 'docker' and 'docker compose'
// installations should be taken from every particular WSL distro
char docker_version[256];
char docker_compose_version[256];
#endif
#ifdef _WIN64
// WSL information for Win10 only
bool wsl_available;
bool docker_use;
char docker_compose_version[256];
#ifdef _WIN64
WSLS wsls;
#endif
@ -126,8 +136,14 @@ public:
int get_host_battery_state();
int get_local_network_info();
int get_virtualbox_version();
int get_docker_info(bool& docker_use);
#ifndef _WIN64
// on Windows we can have several docker installation within WSL
// that is why it makes no sense to have this information put here
// instead the information about the available 'docker' and 'docker compose'
// installations should be taken from every particular WSL distro
int get_docker_info();
int get_docker_compose_info();
#endif
void make_random_string(const char* salt, char* out);
void generate_host_cpid();
static bool parse_linux_os_info(
@ -146,6 +162,8 @@ public:
char* os_name, const int os_name_size, char* os_version,
const int os_version_size
);
static bool get_docker_version_string(std::string raw, std::string& parsed);
static bool get_docker_compose_version_string(std::string raw, std::string& parsed);
#ifdef _WIN32
void win_get_processor_info();
#endif
@ -154,7 +172,7 @@ public:
extern void make_secure_random_string(char*);
#ifdef _WIN64
extern int get_wsl_information(bool& wsl_available, WSLS& wsls);
extern int get_wsl_information(bool& wsl_available, WSLS& wsls, bool detect_docker, bool& docker_available, bool& docker_compose_available);
extern int get_processor_group(HANDLE);
#endif

View File

@ -27,6 +27,10 @@ void WSL::clear() {
os_version = "";
is_default = false;
wsl_version = "1";
is_docker_available = false;
is_docker_compose_available = false;
docker_version = "";
docker_compose_version = "";
}
void WSL::write_xml(MIOFILE& f) {
@ -41,12 +45,20 @@ void WSL::write_xml(MIOFILE& f) {
" <os_version>%s</os_version>\n"
" <is_default>%d</is_default>\n"
" <wsl_version>%s</wsl_version>\n"
" <is_docker_available>%d</is_docker_available>\n"
" <is_docker_compose_available>%d</is_docker_compose_available>\n"
" <docker_version>%s</docker_version>\n"
" <docker_compose_version>%s</docker_compose_version>\n"
" </distro>\n",
dn,
n,
v,
is_default ? 1 : 0,
wsl_version.c_str()
wsl_version.c_str(),
is_docker_available ? 1 : 0,
is_docker_compose_available ? 1 : 0,
docker_version.c_str(),
docker_compose_version.c_str()
);
}
@ -61,6 +73,10 @@ int WSL::parse(XML_PARSER& xp) {
if (xp.parse_string("os_version", os_version)) continue;
if (xp.parse_bool("is_default", is_default)) continue;
if (xp.parse_string("wsl_version", wsl_version)) continue;
if (xp.parse_bool("is_docker_available", is_docker_available)) continue;
if (xp.parse_bool("is_docker_compose_available", is_docker_compose_available)) continue;
if (xp.parse_string("docker_version", docker_version)) continue;
if (xp.parse_string("docker_compose_version", docker_compose_version)) continue;
}
return ERR_XML_PARSE;
}

View File

@ -35,6 +35,14 @@ struct WSL {
std::string wsl_version;
// flag indicating whether this is the default WSL distribution
bool is_default;
// flag indicating whether Docker is available in this WSL distribution
bool is_docker_available;
// flag indicating whether Docker Compose is available in this WSL distribution
bool is_docker_compose_available;
// version of Docker installed in this WSL distribution
std::string docker_version;
// version of Docker Compose installed in this WSL distribution
std::string docker_compose_version;
WSL();

View File

@ -1,6 +1,6 @@
// This file is part of BOINC.
// http://boinc.berkeley.edu
// Copyright (C) 2023 University of California
// https://boinc.berkeley.edu
// Copyright (C) 2024 University of California
//
// BOINC is free software; you can redistribute it and/or modify it
// under the terms of the GNU Lesser General Public License
@ -456,23 +456,120 @@ bool PLAN_CLASS_SPEC::check(
}
if (docker){
if (sreq.core_client_major_version < 8) {
add_no_work_message("BOINC client 8.0+ required for Docker jobs");
return false;
}
if (!(sreq.host.docker_use)) {
if (!(sreq.host.docker_available)) {
add_no_work_message("Docker is not installed or is not available");
return false;
}
if ((strstr(docker_compose_version, "v1")) && (!(strstr(sreq.host.docker_compose_version, "v1")))){
add_no_work_message("Docker compose (older version: docker-compose) is required, but is not installed or is not available");
if (docker_compose && !(sreq.host.docker_compose_available)) {
add_no_work_message("Docker compose is not installed or is not available");
return false;
}
if (sreq.host.wsl_available) {
bool docker_found = false;
bool docker_compose_found = false;
for (int i = 0; i < sreq.host.wsls.wsls.size(); i++) {
if (sreq.host.wsls.wsls[i].is_docker_available) {
if (docker_compose && sreq.host.wsls.wsls[i].is_docker_compose_available) {
int maj, min, rel;
int n = sscanf(sreq.host.wsls.wsls[i].docker_version.c_str(), "%d.%d.%d", &maj, &min, &rel);
if (n != 3) {
if (config.debug_version_select) {
log_messages.printf(MSG_NORMAL,
"[version] plan_class_spec: can't parse docker version\n"
);
continue;
}
}
int v = maj*10000 + min*100 + rel;
if (min_docker_version && v < min_docker_version) {
if (config.debug_version_select) {
log_messages.printf(MSG_NORMAL,
"[version] plan_class_spec: docker version too low: %d < %d\n",
v, min_docker_version
);
continue;
}
}
docker_found = true;
}
if (!docker_compose && docker_found) {
break;
}
}
if (docker_compose && sreq.host.wsls.wsls[i].is_docker_compose_available) {
int maj, min, rel;
int n = sscanf(sreq.host.wsls.wsls[i].docker_compose_version.c_str(), "%d.%d.%d", &maj, &min, &rel);
if (n != 3) {
if (config.debug_version_select) {
log_messages.printf(MSG_NORMAL,
"[version] plan_class_spec: can't parse docker compose version\n"
);
continue;
}
}
int v = maj*10000 + min*100 + rel;
if (min_docker_compose_version && v < min_docker_compose_version) {
if (config.debug_version_select) {
log_messages.printf(MSG_NORMAL,
"[version] plan_class_spec: docker compose version too low: %d < %d\n",
v, min_docker_compose_version
);
continue;
}
}
docker_compose_found = true;
}
}
if (!docker_found) {
add_no_work_message("Suitable Docker is not installed or is not available");
return false;
}
if (docker_compose && !docker_compose_found) {
add_no_work_message("Suitable Docker compose is not installed or is not available");
return false;
}
} else {
int maj, min, rel;
int n = sscanf(sreq.host.docker_version, "%d.%d.%d", &maj, &min, &rel);
if (n != 3) {
if (config.debug_version_select) {
log_messages.printf(MSG_NORMAL,
"[version] plan_class_spec: can't parse docker version\n"
);
}
return false;
}
int v = maj*10000 + min*100 + rel;
if (min_docker_version && v < min_docker_version) {
if (config.debug_version_select) {
log_messages.printf(MSG_NORMAL,
"[version] plan_class_spec: docker version too low: %d < %d\n",
v, min_docker_version
);
}
return false;
}
if ((strstr(docker_compose_version, "v2")) && (!(strstr(sreq.host.docker_compose_version, "v2")))){
add_no_work_message("Docker compose (newer version: docker compose) is required, but is not installed or is not available");
return false;
n = sscanf(sreq.host.docker_compose_version, "%d.%d.%d", &maj, &min, &rel);
if (n != 3) {
if (config.debug_version_select) {
log_messages.printf(MSG_NORMAL,
"[version] plan_class_spec: can't parse docker compose version\n"
);
}
return false;
}
v = maj*10000 + min*100 + rel;
if (min_docker_compose_version && v < min_docker_compose_version) {
if (config.debug_version_select) {
log_messages.printf(MSG_NORMAL,
"[version] plan_class_spec: docker compose version too low: %d < %d\n",
v, min_docker_compose_version
);
}
return false;
}
}
}
@ -1121,7 +1218,9 @@ int PLAN_CLASS_SPEC::parse(XML_PARSER& xp) {
if (xp.parse_bool("opencl", opencl)) continue;
if (xp.parse_bool("virtualbox", virtualbox)) continue;
if (xp.parse_bool("docker", docker)) continue;
if (xp.parse_str("docker_compose_version", docker_compose_version, sizeof(docker_compose_version))) continue;
if (xp.parse_bool("docker_compose", docker_compose)) continue;
if (xp.parse_int("min_docker_version", min_docker_version)) continue;
if (xp.parse_int("min_docker_compose_version", min_docker_compose_version)) continue;
if (xp.parse_bool("is64bit", is64bit)) continue;
if (xp.parse_str("cpu_feature", buf, sizeof(buf))) {
cpu_features.push_back(" " + (string)buf + " ");
@ -1269,7 +1368,9 @@ PLAN_CLASS_SPEC::PLAN_CLASS_SPEC() {
opencl = false;
virtualbox = false;
docker = false;
strcpy(docker_compose_version, "");
docker_compose = false;
min_docker_version = 0;
min_docker_compose_version = 0;
is64bit = false;
min_ncpus = 0;
max_threads = 1;

View File

@ -1,6 +1,6 @@
// This file is part of BOINC.
// http://boinc.berkeley.edu
// Copyright (C) 2023 University of California
// https://boinc.berkeley.edu
// Copyright (C) 2024 University of California
//
// BOINC is free software; you can redistribute it and/or modify it
// under the terms of the GNU Lesser General Public License
@ -33,7 +33,9 @@ struct PLAN_CLASS_SPEC {
bool opencl;
bool virtualbox;
bool docker;
char docker_compose_version[256];
bool docker_compose;
int min_docker_version;
int min_docker_compose_version;
bool is64bit;
std::vector<std::string> cpu_features;
double min_ncpus;

View File

@ -120,4 +120,16 @@
<min_ncpus> 2 </min_ncpus>
<max_threads> 2 </max_threads>
</plan_class>
<plan_class>
<name> docker </name>
<docker/>
<min_docker_version> 270102 </min_docker_version>
</plan_class>
<plan_class>
<name> docker_compose </name>
<docker/>
<docker_compose/>
<min_docker_version> 270102 </min_docker_version>
<min_docker_compose_version>32901 </min_docker_compose_version>
</plan_class>
</plan_classes>

View File

@ -1,6 +1,6 @@
// This file is part of BOINC.
// http://boinc.berkeley.edu
// Copyright (C) 2008 University of California
// https://boinc.berkeley.edu
// Copyright (C) 2024 University of California
//
// BOINC is free software; you can redistribute it and/or modify it
// under the terms of the GNU Lesser General Public License
@ -869,40 +869,27 @@ static inline bool app_plan_opencl(
}
}
// handles vbox[32|64][_[mt]|[hwaccel]]
// "mt" is tailored to the needs of CERN:
// use 1 or 2 CPUs
//plan class for Docker jobs
//
static inline bool app_plan_docker(
SCHEDULER_REQUEST& sreq, char* plan_class
){
if (sreq.core_client_major_version < 8) {
add_no_work_message("BOINC client 8.0+ required for Docker jobs");
return false;
}
if (!(sreq.host.docker_use)) {
if (!(sreq.host.docker_available)) {
add_no_work_message("Docker is not installed or is not available");
return false;
}
if ((strstr(plan_class, "v1")) && (!(strstr(sreq.host.docker_compose_version, "v1")))){
add_no_work_message("Docker compose (older version: docker-compose) is required, but is not installed or is not available");
return false;
}
if ((strstr(plan_class, "v2")) && (!(strstr(sreq.host.docker_compose_version, "v2")))){
add_no_work_message("Docker compose (newer version: docker compose) is required, but is not installed or is not available");
if (strstr(plan_class, "compose") && !(sreq.host.docker_compose_available)) {
add_no_work_message("Docker compose is not installed or is not available");
return false;
}
return true;
}
// handles vbox[32|64][_[mt]|[hwaccel]]
// "mt" is tailored to the needs of CERN:
// use 1 or 2 CPUs
static inline bool app_plan_vbox(
SCHEDULER_REQUEST& sreq, char* plan_class, HOST_USAGE& hu

View File

@ -1,6 +1,6 @@
// This file is part of BOINC.
// http://boinc.berkeley.edu
// Copyright (C) 2023 University of California
// https://boinc.berkeley.edu
// Copyright (C) 2024 University of California
//
// BOINC is free software; you can redistribute it and/or modify it
// under the terms of the GNU Lesser General Public License
@ -1401,7 +1401,9 @@ int HOST::parse(XML_PARSER& xp) {
if (xp.parse_double("n_bwup", n_bwup)) continue;
if (xp.parse_double("n_bwdown", n_bwdown)) continue;
if (xp.parse_str("p_features", p_features, sizeof(p_features))) continue;
if (xp.parse_bool("docker_use", docker_use)) continue;
if (xp.parse_bool("docker_available", docker_available)) continue;
if (xp.parse_bool("docker_compose_available", docker_compose_available)) continue;
if (xp.parse_str("docker_version", docker_version, sizeof(docker_version))) continue;
if (xp.parse_str("docker_compose_version", docker_compose_version, sizeof(docker_compose_version))) continue;
if (xp.parse_str("virtualbox_version", virtualbox_version, sizeof(virtualbox_version))) continue;
if (xp.parse_bool("p_vm_extensions_disabled", p_vm_extensions_disabled)) continue;