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

feat(worker): support workers to run natively on windows #4446

Merged
merged 49 commits into from
Oct 3, 2024
Merged
Show file tree
Hide file tree
Changes from 3 commits
Commits
Show all changes
49 commits
Select commit Hold shift + click to select a range
0dfdc95
minimal code change to get windmill worker on windows for bun and pyt…
alpetric Sep 25, 2024
3ed1d5f
adding support for powershell
alpetric Sep 25, 2024
6551494
compiling error on unix
alpetric Sep 25, 2024
648cb71
merge conflict, make ansible build (not run yet) on windows
alpetric Sep 25, 2024
3c3fdbe
rust linting comments
alpetric Sep 26, 2024
674a11d
comments hugo: PSModulePath
alpetric Sep 26, 2024
e81555a
merge main
alpetric Sep 26, 2024
fb81321
comments ruben, refactor to simplify
alpetric Sep 26, 2024
ceb221a
adding build workflow
alpetric Sep 26, 2024
b6f8d46
editing workflow
alpetric Sep 26, 2024
3b02fbb
editing workflow
alpetric Sep 26, 2024
b3dad9e
editing workflow
alpetric Sep 26, 2024
9b84cb9
editing workflow
alpetric Sep 26, 2024
11e8ca7
editing workflow
alpetric Sep 26, 2024
951bd8e
skip migration env, ee fixes
alpetric Sep 27, 2024
10423c6
Merge branch 'alp/build_windows' of https://github.com/windmill-labs/…
alpetric Sep 27, 2024
5102e0a
improvements powershell
alpetric Sep 27, 2024
01d7c27
testing windows runner
alpetric Sep 27, 2024
e49ba39
testing windows runner
alpetric Sep 27, 2024
d5f3e75
testing windows runner
alpetric Sep 27, 2024
d1b5abd
testing windows runner
alpetric Sep 27, 2024
2f80ba1
testing windows runner
alpetric Sep 27, 2024
1566c56
install postgres on runner
alpetric Oct 1, 2024
9e8874e
install postgres on runner
alpetric Oct 1, 2024
6cc600e
install postgres on runner
alpetric Oct 1, 2024
549f8bc
install postgres on runner
alpetric Oct 1, 2024
ac9f409
install postgres on runner
alpetric Oct 1, 2024
ff94bc4
install postgres on runner
alpetric Oct 1, 2024
7c055ab
install postgres on runner
alpetric Oct 1, 2024
af699e1
install postgres on runner
alpetric Oct 1, 2024
44750c0
killing process tree in windows
alpetric Oct 1, 2024
1e15baa
sqlx_offline
alpetric Oct 1, 2024
008975c
install openssl for github windows runner
alpetric Oct 1, 2024
a991058
used pre-installed openssl
alpetric Oct 1, 2024
48627e0
used pre-installed openssl
alpetric Oct 1, 2024
b09e08d
build ee
alpetric Oct 1, 2024
4c002d2
build ee
alpetric Oct 1, 2024
a673a96
build ee
alpetric Oct 1, 2024
e536dd7
build ee
alpetric Oct 1, 2024
45dc9cd
adding commented out steps for artifact publishing
alpetric Oct 1, 2024
9eeea89
build on tag matchinv v* pattern
alpetric Oct 2, 2024
fab831c
ren instead of mv on Windows
alpetric Oct 2, 2024
0a79304
Merge branch 'main' into alp/build_windows
alpetric Oct 2, 2024
493e149
fix merging issue
alpetric Oct 2, 2024
2a3dd20
gate imports for windows
alpetric Oct 2, 2024
965c8b7
fixing default cargo home path...
alpetric Oct 2, 2024
7a0da5e
fixing default cargo home path...
alpetric Oct 2, 2024
fdf0b01
comments ruben
alpetric Oct 3, 2024
bbe6b0b
make pwsh default modules loading more robust on unix (#4448)
HugoCasa Oct 3, 2024
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
4 changes: 2 additions & 2 deletions backend/windmill-api/src/jobs.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4644,8 +4644,8 @@ async fn get_job_update(
.fetch_optional(&db)
.await?;

let progress: Option<i32> = if get_progress == Some(true){
sqlx::query_scalar!(
let progress: Option<i32> = if get_progress == Some(true) {
sqlx::query_scalar!(
"SELECT scalar_int FROM job_stats WHERE workspace_id = $1 AND job_id = $2 AND metric_id = $3",
&w_id,
job_id,
Expand Down
5 changes: 4 additions & 1 deletion backend/windmill-api/src/resources.rs
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,10 @@ pub fn workspaced_service() -> Router {
.route("/type/exists/:name", get(exists_resource_type))
.route("/type/update/:name", post(update_resource_type))
.route("/type/delete/:name", delete(delete_resource_type))
.route("/file_resource_type_to_file_ext_map", get(file_resource_ext_to_resource_type))
.route(
"/file_resource_type_to_file_ext_map",
get(file_resource_ext_to_resource_type),
)
.route("/type/create", post(create_resource_type))
}

Expand Down
9 changes: 7 additions & 2 deletions backend/windmill-common/src/worker.rs
Original file line number Diff line number Diff line change
Expand Up @@ -195,7 +195,11 @@ fn normalize_path(path: &Path) -> PathBuf {
}
ret
}
pub fn write_file_at_user_defined_location(job_dir: &str, user_defined_path: &str, content: &str) -> error::Result<File> {
pub fn write_file_at_user_defined_location(
job_dir: &str,
user_defined_path: &str,
content: &str,
) -> error::Result<File> {
let job_dir = Path::new(job_dir);
let user_path = PathBuf::from(user_defined_path);

Expand All @@ -210,7 +214,8 @@ pub fn write_file_at_user_defined_location(job_dir: &str, user_defined_path: &st
return Err(std::io::Error::new(
std::io::ErrorKind::PermissionDenied,
"Path is outside the allowed job directory.",
).into());
)
.into());
}

if let Some(parent_dir) = full_path.parent() {
Expand Down
2 changes: 1 addition & 1 deletion backend/windmill-indexer/src/indexer_ee.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
use anyhow::anyhow;
use sqlx::{Pool, Postgres};
use windmill_common::error::Error;
use anyhow::anyhow;

#[derive(Clone)]
pub struct IndexReader;
Expand Down
4 changes: 2 additions & 2 deletions backend/windmill-worker/src/ansible_executor.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,8 @@ use windmill_queue::{append_logs, CanceledBy};

use crate::{
common::{
get_reserved_variables, handle_child, read_and_check_result,
start_child_process, transform_json,
get_reserved_variables, handle_child, read_and_check_result, start_child_process,
transform_json,
},
python_executor::{create_dependencies_dir, handle_python_reqs, pip_compile},
AuthedClientBackgroundTask, DISABLE_NSJAIL, HOME_ENV, PATH_ENV, TZ_ENV,
Expand Down
75 changes: 72 additions & 3 deletions backend/windmill-worker/src/bash_executor.rs
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ use crate::{
start_child_process,
},
AuthedClientBackgroundTask, DISABLE_NSJAIL, DISABLE_NUSER, HOME_ENV, NSJAIL_PATH, PATH_ENV,
POWERSHELL_CACHE_DIR, POWERSHELL_PATH, TZ_ENV,
POWERSHELL_CACHE_DIR, POWERSHELL_PATH, SYSTEM_ROOT, TZ_ENV,
};

lazy_static::lazy_static! {
Expand Down Expand Up @@ -195,7 +195,7 @@ pub async fn handle_powershell_job(
worker_name: &str,
envs: HashMap<String, String>,
) -> Result<Box<RawValue>, Error> {
let pwsh_args = {
let mut pwsh_args = {
let args = build_args_map(job, client, db).await?.map(Json);
let job_args = if args.is_some() {
args.as_ref()
Expand All @@ -222,13 +222,19 @@ pub async fn handle_powershell_job(
.collect::<Vec<_>>()
};

#[cfg(windows)]
let split_char = '\\';

#[cfg(unix)]
let split_char = '/';

let installed_modules = fs::read_dir(POWERSHELL_CACHE_DIR)?
.filter_map(|x| {
x.ok().map(|x| {
x.path()
.display()
.to_string()
.split('/')
.split(split_char)
.last()
.unwrap_or_default()
.to_lowercase()
Expand Down Expand Up @@ -284,6 +290,7 @@ pub async fn handle_powershell_job(
append_logs(&job.id, &job.workspace_id, logs2, db).await;

// make sure default (only allhostsallusers) modules are loaded, disable autoload (cache can be large to explore especially on cloud) and add /tmp/windmill/cache to PSModulePath
#[cfg(unix)]
let profile = format!(
"$PSModuleAutoloadingPreference = 'None'
$PSModulePathBackup = $env:PSModulePath
Expand All @@ -292,6 +299,17 @@ Get-Module -ListAvailable | Import-Module
$env:PSModulePath = \"{}:$PSModulePathBackup\"",
POWERSHELL_CACHE_DIR
);

#[cfg(windows)]
let profile = format!(
"#$PSModuleAutoloadingPreference = 'None'
$PSModulePathBackup = $env:PSModulePath
#$env:PSModulePath = \"C:\\WINDOWS\\System32\\WindowsPowerShell\\v1.0\\Modules;C:\\Program Files\\PowerShell\\7\\Modules\"
Get-Module -ListAvailable | Import-Module
$env:PSModulePath = \"{};$PSModulePathBackup\"",
POWERSHELL_CACHE_DIR
);

// make sure param() is first
let param_match = windmill_parser_bash::RE_POWERSHELL_PARAM.find(&content);
let content: String = if let Some(param_match) = param_match {
Expand All @@ -307,11 +325,29 @@ $env:PSModulePath = \"{}:$PSModulePathBackup\"",
};

write_file(job_dir, "main.ps1", content.as_str())?;

#[cfg(unix)]
write_file(
job_dir,
"wrapper.sh",
&format!("set -o pipefail\nset -e\nmkfifo bp\ncat bp | tail -1 > ./result2.out &\n{} -F ./main.ps1 \"$@\" 2>&1 | tee bp\nwait $!", POWERSHELL_PATH.as_str()),
)?;

#[cfg(windows)]
write_file(
job_dir,
"wrapper.ps1",
&format!(
" param([string[]]$args)\n\
$ErrorActionPreference = 'Stop'\n\
$pipe = New-TemporaryFile\n\
& \"{}\" -File ./main.ps1 @args 2>&1 | Tee-Object -FilePath $pipe\n\
Get-Content -Path $pipe | Select-Object -Last 1 | Set-Content -Path './result2.out'\n\
Remove-Item $pipe\n",
POWERSHELL_PATH.as_str()
),
)?;

let token = client.get_token().await;
let mut reserved_variables = get_reserved_variables(job, &token, db).await?;
reserved_variables.insert("RUST_LOG".to_string(), "info".to_string());
Expand All @@ -320,6 +356,7 @@ $env:PSModulePath = \"{}:$PSModulePathBackup\"",
let _ = write_file(job_dir, "result.out", "")?;
let _ = write_file(job_dir, "result2.out", "")?;

#[cfg(unix)]
let child = if !*DISABLE_NSJAIL {
let _ = write_file(
job_dir,
Expand Down Expand Up @@ -366,6 +403,38 @@ $env:PSModulePath = \"{}:$PSModulePathBackup\"",
.stderr(Stdio::piped())
.spawn()?
};

#[cfg(windows)]
pwsh_args.insert(0, r".\wrapper.ps1".to_string());

#[cfg(windows)]
let child = Command::new(POWERSHELL_PATH.as_str())
.current_dir(job_dir)
.env_clear()
.envs(envs)
.envs(reserved_variables)
.env("PATH", PATH_ENV.as_str())
.env("BASE_INTERNAL_URL", base_internal_url)
.env("HOME", HOME_ENV.as_str())
.env("SystemRoot", SYSTEM_ROOT.as_str())
.env(
"TMP",
std::env::var("TMP").unwrap_or_else(|_| String::from("/tmp")),
)
.env(
"PATHEXT",
".COM;.EXE;.BAT;.CMD;.VBS;.VBE;.JS;.JSE;.WSF;.WSH;.MSC;.CPL",
)
.args(
pwsh_args
.into_iter()
.map(|arg| arg.replace("--", "-"))
.collect::<Vec<String>>(),
)
.stdout(Stdio::piped())
.stderr(Stdio::piped())
.spawn()?;

handle_child(
&job.id,
db,
Expand Down
39 changes: 36 additions & 3 deletions backend/windmill-worker/src/bun_executor.rs
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ use crate::{
},
AuthedClientBackgroundTask, BUNFIG_INSTALL_SCOPES, BUN_BUNDLE_CACHE_DIR, BUN_CACHE_DIR,
BUN_DEPSTAR_CACHE_DIR, BUN_PATH, DISABLE_NSJAIL, DISABLE_NUSER, HOME_ENV, NODE_BIN_PATH,
NODE_PATH, NPM_CONFIG_REGISTRY, NPM_PATH, NSJAIL_PATH, PATH_ENV, TZ_ENV,
NODE_PATH, NPM_CONFIG_REGISTRY, NPM_PATH, NSJAIL_PATH, PATH_ENV, SYSTEM_ROOT, TZ_ENV,
};

use tokio::{fs::File, process::Command};
Expand Down Expand Up @@ -110,6 +110,9 @@ pub async fn gen_bun_lockfile(
.stdout(Stdio::piped())
.stderr(Stdio::piped());

#[cfg(windows)]
child_cmd.env("SystemRoot", SYSTEM_ROOT.as_str());

let mut child_process = start_child_process(child_cmd, &*BUN_PATH).await?;

if let Some(db) = db {
Expand Down Expand Up @@ -240,6 +243,9 @@ pub async fn install_bun_lockfile(
.stdout(Stdio::piped())
.stderr(Stdio::piped());

#[cfg(windows)]
child_cmd.env("SystemRoot", SYSTEM_ROOT.as_str());

let mut npm_logs = if npm_mode {
"NPM mode\n".to_string()
} else {
Expand Down Expand Up @@ -446,6 +452,10 @@ pub async fn generate_wrapper_mjs(
.args(vec!["run", "node_builder.ts"])
.stdout(Stdio::piped())
.stderr(Stdio::piped());

#[cfg(windows)]
child.env("SystemRoot", SYSTEM_ROOT.as_str());

let child_process = start_child_process(child, &*BUN_PATH).await?;
handle_child(
job_id,
Expand Down Expand Up @@ -489,6 +499,10 @@ pub async fn generate_bun_bundle(
.args(vec!["run", "node_builder.ts"])
.stdout(Stdio::piped())
.stderr(Stdio::piped());

#[cfg(windows)]
child.env("SystemRoot", SYSTEM_ROOT.as_str());

let mut child_process = start_child_process(child, &*BUN_PATH).await?;
if let Some(db) = db {
handle_child(
Expand Down Expand Up @@ -710,6 +724,10 @@ async fn compute_bundle_local_and_remote_path(

let hash = windmill_common::utils::calculate_hash(&input_src);
let local_path = format!("{BUN_BUNDLE_CACHE_DIR}/{hash}");

#[cfg(windows)]
let local_path = local_path.replace("/tmp", r"C:\tmp").replace("/", r"\");

let remote_path = format!("{BUN_BUNDLE_OBJECT_STORE_PREFIX}{hash}");
(local_path, remote_path)
}
Expand Down Expand Up @@ -802,14 +820,25 @@ pub async fn handle_bun_job(
));
}

let mut gbuntar_name = None;
let mut gbuntar_name: Option<String> = None;
if has_bundle_cache {
#[cfg(unix)]
let target = format!("{job_dir}/main.js");
#[cfg(unix)]
std::os::unix::fs::symlink(&local_path, &target).map_err(|e| {
error::Error::ExecutionErr(format!(
"could not copy cached binary from {local_path} to {job_dir}/main: {e:?}"
))
})?;

#[cfg(windows)]
let target = format!("{job_dir}\\main.js");
#[cfg(windows)]
std::os::windows::fs::symlink_dir(&local_path, &target).map_err(|e| {
error::Error::ExecutionErr(format!(
"could not copy cached binary from {local_path} to {job_dir}\\main: {e:?}"
))
})?;
} else if let Some(codebase) = codebase.as_ref() {
pull_codebase(&job.workspace_id, codebase, job_dir).await?;
} else if let Some(reqs) = requirements_o.as_ref() {
Expand Down Expand Up @@ -1295,7 +1324,7 @@ try {{
.stderr(Stdio::piped());
start_child_process(nsjail_cmd, NSJAIL_PATH.as_str()).await?
} else {
let cmd = if annotation.nodejs_mode {
let mut cmd = if annotation.nodejs_mode {
let script_path = format!("{job_dir}/wrapper.mjs");

let mut bun_cmd = Command::new(&*NODE_BIN_PATH);
Expand Down Expand Up @@ -1336,6 +1365,10 @@ try {{
.stderr(Stdio::piped());
bun_cmd
};

#[cfg(windows)]
cmd.env("SystemRoot", SYSTEM_ROOT.as_str());

start_child_process(
cmd,
if annotation.nodejs_mode {
Expand Down
8 changes: 8 additions & 0 deletions backend/windmill-worker/src/go_executor.rs
Original file line number Diff line number Diff line change
Expand Up @@ -221,12 +221,20 @@ func Run(req Req) (interface{{}}, error){{
}
} else {
let target = format!("{job_dir}/main");
#[cfg(unix)]
std::os::unix::fs::symlink(&bin_path, &target).map_err(|e| {
Error::ExecutionErr(format!(
"could not copy cached binary from {bin_path} to {job_dir}/main: {e:?}"
))
})?;

#[cfg(windows)]
std::os::windows::fs::symlink_dir(&bin_path, &target).map_err(|e| {
Error::ExecutionErr(format!(
"could not copy cached binary from {bin_path} to {job_dir}/main: {e:?}"
))
})?;

create_args_and_out_file(client, job, job_dir, db).await?;
cache_logs
};
Expand Down
2 changes: 1 addition & 1 deletion backend/windmill-worker/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ mod mssql_executor;
#[cfg(feature = "enterprise")]
mod snowflake_executor;

mod ansible_executor;
mod bash_executor;
mod bun_executor;
pub mod common;
Expand All @@ -25,7 +26,6 @@ mod rust_executor;
mod worker;
mod worker_flow;
mod worker_lockfiles;
mod ansible_executor;
pub use worker::*;

pub use result_processor::handle_job_error;
Expand Down
Loading
Loading