mirror of
https://github.com/windmill-labs/windmill.git
synced 2026-09-11 00:06:06 +00:00
feat: powershell private repo support (#6684)
* feat: powershell private repo support * nits
This commit is contained in:
+12
-4
@@ -10,9 +10,10 @@ use monitor::{
|
||||
load_base_url, load_otel, reload_critical_alerts_on_db_oversize,
|
||||
reload_delete_logs_periodically_setting, reload_indexer_config,
|
||||
reload_instance_python_version_setting, reload_maven_repos_setting,
|
||||
reload_no_default_maven_setting, reload_nuget_config_setting, reload_ruby_repos_setting,
|
||||
reload_timeout_wait_result_setting, send_current_log_file_to_object_store,
|
||||
send_logs_to_object_store, WORKERS_NAMES,
|
||||
reload_no_default_maven_setting, reload_nuget_config_setting,
|
||||
reload_powershell_repo_pat_setting, reload_powershell_repo_url_setting,
|
||||
reload_ruby_repos_setting, reload_timeout_wait_result_setting,
|
||||
send_current_log_file_to_object_store, send_logs_to_object_store, WORKERS_NAMES,
|
||||
};
|
||||
use rand::Rng;
|
||||
use sqlx::postgres::PgListener;
|
||||
@@ -45,7 +46,8 @@ use windmill_common::{
|
||||
JOB_DEFAULT_TIMEOUT_SECS_SETTING, JWT_SECRET_SETTING, KEEP_JOB_DIR_SETTING,
|
||||
LICENSE_KEY_SETTING, MAVEN_REPOS_SETTING, MONITOR_LOGS_ON_OBJECT_STORE_SETTING,
|
||||
NO_DEFAULT_MAVEN_SETTING, NPM_CONFIG_REGISTRY_SETTING, NUGET_CONFIG_SETTING, OAUTH_SETTING,
|
||||
OTEL_SETTING, PIP_INDEX_URL_SETTING, REQUEST_SIZE_LIMIT_SETTING,
|
||||
OTEL_SETTING, PIP_INDEX_URL_SETTING, POWERSHELL_REPO_PAT_SETTING,
|
||||
POWERSHELL_REPO_URL_SETTING, REQUEST_SIZE_LIMIT_SETTING,
|
||||
REQUIRE_PREEXISTING_USER_FOR_OAUTH_SETTING, RETENTION_PERIOD_SECS_SETTING,
|
||||
RUBY_REPOS_SETTING, SAML_METADATA_SETTING, SCIM_TOKEN_SETTING, SMTP_SETTING, TEAMS_SETTING,
|
||||
TIMEOUT_WAIT_RESULT_SETTING,
|
||||
@@ -1054,6 +1056,12 @@ Windmill Community Edition {GIT_VERSION}
|
||||
NUGET_CONFIG_SETTING => {
|
||||
reload_nuget_config_setting(&conn).await
|
||||
},
|
||||
POWERSHELL_REPO_URL_SETTING => {
|
||||
reload_powershell_repo_url_setting(&conn).await
|
||||
},
|
||||
POWERSHELL_REPO_PAT_SETTING => {
|
||||
reload_powershell_repo_pat_setting(&conn).await
|
||||
},
|
||||
MAVEN_REPOS_SETTING => {
|
||||
reload_maven_repos_setting(&conn).await
|
||||
},
|
||||
|
||||
+26
-2
@@ -53,7 +53,8 @@ use windmill_common::{
|
||||
HUB_BASE_URL_SETTING, INSTANCE_PYTHON_VERSION_SETTING, JOB_DEFAULT_TIMEOUT_SECS_SETTING,
|
||||
JWT_SECRET_SETTING, KEEP_JOB_DIR_SETTING, LICENSE_KEY_SETTING,
|
||||
MONITOR_LOGS_ON_OBJECT_STORE_SETTING, NPM_CONFIG_REGISTRY_SETTING, NUGET_CONFIG_SETTING,
|
||||
OTEL_SETTING, PIP_INDEX_URL_SETTING, REQUEST_SIZE_LIMIT_SETTING,
|
||||
OTEL_SETTING, PIP_INDEX_URL_SETTING, POWERSHELL_REPO_PAT_SETTING,
|
||||
POWERSHELL_REPO_URL_SETTING, REQUEST_SIZE_LIMIT_SETTING,
|
||||
REQUIRE_PREEXISTING_USER_FOR_OAUTH_SETTING, RETENTION_PERIOD_SECS_SETTING,
|
||||
SAML_METADATA_SETTING, SCIM_TOKEN_SETTING, TIMEOUT_WAIT_RESULT_SETTING,
|
||||
},
|
||||
@@ -83,7 +84,8 @@ use windmill_queue::{cancel_job, MiniPulledJob, SameWorkerPayload};
|
||||
use windmill_worker::{
|
||||
handle_job_error, JobCompletedSender, SameWorkerSender, BUNFIG_INSTALL_SCOPES,
|
||||
INSTANCE_PYTHON_VERSION, JOB_DEFAULT_TIMEOUT, KEEP_JOB_DIR, MAVEN_REPOS, NO_DEFAULT_MAVEN,
|
||||
NPM_CONFIG_REGISTRY, NUGET_CONFIG, PIP_EXTRA_INDEX_URL, PIP_INDEX_URL,
|
||||
NPM_CONFIG_REGISTRY, NUGET_CONFIG, PIP_EXTRA_INDEX_URL, PIP_INDEX_URL, POWERSHELL_REPO_PAT,
|
||||
POWERSHELL_REPO_URL,
|
||||
};
|
||||
|
||||
#[cfg(feature = "parquet")]
|
||||
@@ -298,6 +300,8 @@ pub async fn initial_load(
|
||||
reload_bunfig_install_scopes_setting(&conn).await;
|
||||
reload_instance_python_version_setting(&conn).await;
|
||||
reload_nuget_config_setting(&conn).await;
|
||||
reload_powershell_repo_url_setting(&conn).await;
|
||||
reload_powershell_repo_pat_setting(&conn).await;
|
||||
reload_maven_repos_setting(&conn).await;
|
||||
reload_no_default_maven_setting(&conn).await;
|
||||
reload_ruby_repos_setting(&conn).await;
|
||||
@@ -1125,6 +1129,26 @@ pub async fn reload_nuget_config_setting(conn: &Connection) {
|
||||
.await;
|
||||
}
|
||||
|
||||
pub async fn reload_powershell_repo_url_setting(conn: &Connection) {
|
||||
reload_option_setting_with_tracing(
|
||||
conn,
|
||||
POWERSHELL_REPO_URL_SETTING,
|
||||
"POWERSHELL_REPO_URL",
|
||||
POWERSHELL_REPO_URL.clone(),
|
||||
)
|
||||
.await;
|
||||
}
|
||||
|
||||
pub async fn reload_powershell_repo_pat_setting(conn: &Connection) {
|
||||
reload_option_setting_with_tracing(
|
||||
conn,
|
||||
POWERSHELL_REPO_PAT_SETTING,
|
||||
"POWERSHELL_REPO_PAT",
|
||||
POWERSHELL_REPO_PAT.clone(),
|
||||
)
|
||||
.await;
|
||||
}
|
||||
|
||||
pub async fn reload_maven_repos_setting(conn: &Connection) {
|
||||
reload_option_setting_with_tracing(
|
||||
conn,
|
||||
|
||||
@@ -11,6 +11,8 @@ pub const LICENSE_KEY_SETTING: &str = "license_key";
|
||||
pub const NPM_CONFIG_REGISTRY_SETTING: &str = "npm_config_registry";
|
||||
pub const BUNFIG_INSTALL_SCOPES_SETTING: &str = "bunfig_install_scopes";
|
||||
pub const NUGET_CONFIG_SETTING: &str = "nuget_config";
|
||||
pub const POWERSHELL_REPO_URL_SETTING: &str = "powershell_repo_url";
|
||||
pub const POWERSHELL_REPO_PAT_SETTING: &str = "powershell_repo_pat";
|
||||
pub const MAVEN_REPOS_SETTING: &str = "maven_repos";
|
||||
pub const NO_DEFAULT_MAVEN_SETTING: &str = "no_default_maven";
|
||||
pub const RUBY_REPOS_SETTING: &str = "ruby_repos";
|
||||
|
||||
@@ -33,7 +33,7 @@ const NSJAIL_CONFIG_RUN_POWERSHELL_CONTENT: &str =
|
||||
include_str!("../nsjail/run.powershell.config.proto");
|
||||
|
||||
lazy_static::lazy_static! {
|
||||
static ref RE_POWERSHELL_IMPORTS: Regex = Regex::new(r#"^Import-Module\s+(?:-Name\s+)?"?([^-\s"]+)"?"#).unwrap();
|
||||
static ref RE_POWERSHELL_IMPORTS: Regex = Regex::new(r#"^Import-Module\s+(?:-Name\s+)?"?([^\s"]+)"?"#).unwrap();
|
||||
}
|
||||
|
||||
#[cfg(feature = "dind")]
|
||||
@@ -46,7 +46,7 @@ use crate::{
|
||||
},
|
||||
handle_child::handle_child,
|
||||
DISABLE_NSJAIL, DISABLE_NUSER, HOME_ENV, NSJAIL_PATH, PATH_ENV, POWERSHELL_CACHE_DIR,
|
||||
POWERSHELL_PATH, PROXY_ENVS, TZ_ENV,
|
||||
POWERSHELL_PATH, POWERSHELL_REPO_PAT, POWERSHELL_REPO_URL, PROXY_ENVS, TZ_ENV,
|
||||
};
|
||||
use windmill_common::client::AuthedClient;
|
||||
|
||||
@@ -520,21 +520,77 @@ fn raw_to_string(x: &str) -> String {
|
||||
}
|
||||
}
|
||||
|
||||
const POWERSHELL_INSTALL_CODE: &str = r#"
|
||||
fn generate_powershell_install_code_with_private_repo() -> String {
|
||||
r#"
|
||||
$ErrorActionPreference = 'Stop'
|
||||
$availableModules = Get-Module -ListAvailable
|
||||
$path = '{path}'
|
||||
$jobId = '{job_id}'
|
||||
$privateRepoUrl = '{private_repo_url}'
|
||||
$privateRepoPat = '{private_repo_pat}'
|
||||
|
||||
# Register temporary private repository with job ID
|
||||
$repoName = "windmill-private-$jobId"
|
||||
$repoUri = "$privateRepoUrl"
|
||||
|
||||
# Create PSCredential for authentication
|
||||
$username = "token"
|
||||
$patToken = ConvertTo-SecureString $privateRepoPat -AsPlainText -Force
|
||||
$credentials = New-Object System.Management.Automation.PSCredential($username, $patToken)
|
||||
|
||||
Write-Host "Registering temporary repository: $repoName"
|
||||
|
||||
# Remove repository if it already exists
|
||||
Unregister-PSResourceRepository -Name $repoName -ErrorAction SilentlyContinue
|
||||
Register-PSResourceRepository -Name $repoName -Uri $repoUri -Trusted
|
||||
|
||||
try {
|
||||
$moduleNames = @({modules})
|
||||
foreach ($module in $moduleNames) {
|
||||
if (-not ($availableModules | Where-Object { $_.Name -eq $module })) {
|
||||
$moduleFound = $false
|
||||
# First try private repository if configured
|
||||
$privateModule = Find-PSResource -Name $module -Repository $repoName -ErrorAction SilentlyContinue -Credential $credentials
|
||||
if ($privateModule) {
|
||||
$moduleFound = $true
|
||||
Write-Host "Found module $module in private repository, installing from there..."
|
||||
Save-PSResource -Name $module -Path $path -Repository $repoName -Credential $credentials
|
||||
}
|
||||
|
||||
# If not found in private repo, try all repositories
|
||||
if (-not $moduleFound) {
|
||||
Write-Host "Installing module $module from all repositories..."
|
||||
Save-PSResource -Name $module -Path $path -TrustRepository
|
||||
}
|
||||
} else {
|
||||
Write-Host "Module $module already installed"
|
||||
}
|
||||
}
|
||||
} finally {
|
||||
Write-Host "Unregistering temporary repository: $repoName"
|
||||
Unregister-PSResourceRepository -Name $repoName
|
||||
}
|
||||
"#.to_string()
|
||||
}
|
||||
|
||||
fn generate_powershell_install_code_public_only() -> String {
|
||||
r#"
|
||||
$ErrorActionPreference = 'Stop'
|
||||
$availableModules = Get-Module -ListAvailable
|
||||
$path = '{path}'
|
||||
|
||||
$moduleNames = @({modules})
|
||||
|
||||
foreach ($module in $moduleNames) {
|
||||
if (-not ($availableModules | Where-Object { $_.Name -eq $module })) {
|
||||
Write-Host "Installing module $module..."
|
||||
Save-Module -Name $module -Path $path -Force
|
||||
Save-PSResource -Name $module -Path $path -TrustRepository
|
||||
} else {
|
||||
Write-Host "Module $module already installed"
|
||||
}
|
||||
}
|
||||
"#;
|
||||
"#
|
||||
.to_string()
|
||||
}
|
||||
|
||||
#[tracing::instrument(level = "trace", skip_all)]
|
||||
pub async fn handle_powershell_job(
|
||||
@@ -617,16 +673,33 @@ pub async fn handle_powershell_job(
|
||||
}
|
||||
|
||||
if !modules_to_install.is_empty() {
|
||||
let install_string = POWERSHELL_INSTALL_CODE
|
||||
.replace("{path}", POWERSHELL_CACHE_DIR)
|
||||
.replace(
|
||||
"{modules}",
|
||||
&modules_to_install
|
||||
.iter()
|
||||
.map(|x| format!("'{x}'"))
|
||||
.collect::<Vec<_>>()
|
||||
.join(", "),
|
||||
);
|
||||
let powershell_repo_url = POWERSHELL_REPO_URL.read().await.clone();
|
||||
let powershell_repo_pat = POWERSHELL_REPO_PAT.read().await.clone();
|
||||
let has_private_repo = powershell_repo_url.is_some() && powershell_repo_pat.is_some();
|
||||
let install_template = if has_private_repo {
|
||||
generate_powershell_install_code_with_private_repo()
|
||||
} else {
|
||||
generate_powershell_install_code_public_only()
|
||||
};
|
||||
|
||||
let modules_list = modules_to_install
|
||||
.iter()
|
||||
.map(|x| format!("'{x}'"))
|
||||
.collect::<Vec<_>>()
|
||||
.join(", ");
|
||||
|
||||
let install_string = if has_private_repo {
|
||||
install_template
|
||||
.replace("{path}", POWERSHELL_CACHE_DIR)
|
||||
.replace("{job_id}", &job.id.to_string())
|
||||
.replace("{private_repo_url}", &powershell_repo_url.unwrap())
|
||||
.replace("{private_repo_pat}", &powershell_repo_pat.unwrap())
|
||||
.replace("{modules}", &modules_list)
|
||||
} else {
|
||||
install_template
|
||||
.replace("{path}", POWERSHELL_CACHE_DIR)
|
||||
.replace("{modules}", &modules_list)
|
||||
};
|
||||
let mut cmd = Command::new(POWERSHELL_PATH.as_str());
|
||||
cmd.args(&["-Command", &install_string])
|
||||
.stdout(Stdio::piped())
|
||||
|
||||
@@ -262,6 +262,12 @@ const DOTNET_DEFAULT_PATH: &str = "/usr/bin/dotnet";
|
||||
pub const SAME_WORKER_REQUIREMENTS: &'static str =
|
||||
"SameWorkerSender is required because this job may be part of a flow";
|
||||
|
||||
#[derive(Deserialize, Clone)]
|
||||
pub struct PowershellRepo {
|
||||
pub url: String,
|
||||
pub pat: String,
|
||||
}
|
||||
|
||||
lazy_static::lazy_static! {
|
||||
|
||||
pub static ref SLEEP_QUEUE: u64 = std::env::var("SLEEP_QUEUE")
|
||||
@@ -343,6 +349,8 @@ lazy_static::lazy_static! {
|
||||
.and_then(|x| x.parse::<bool>().ok())
|
||||
.unwrap_or(false);
|
||||
pub static ref NUGET_CONFIG: Arc<RwLock<Option<String>>> = Arc::new(RwLock::new(None));
|
||||
pub static ref POWERSHELL_REPO_URL: Arc<RwLock<Option<String>>> = Arc::new(RwLock::new(None));
|
||||
pub static ref POWERSHELL_REPO_PAT: Arc<RwLock<Option<String>>> = Arc::new(RwLock::new(None));
|
||||
pub static ref MAVEN_REPOS: Arc<RwLock<Option<String>>> = Arc::new(RwLock::new(None));
|
||||
pub static ref NO_DEFAULT_MAVEN: AtomicBool = AtomicBool::new(std::env::var("NO_DEFAULT_MAVEN")
|
||||
.ok()
|
||||
|
||||
@@ -340,6 +340,24 @@ export const settings: Record<string, Setting[]> = {
|
||||
placeholder: 'https://user:password@gems.foo.com/',
|
||||
storage: 'setting',
|
||||
ee_only: ''
|
||||
},
|
||||
{
|
||||
label: 'PowerShell Repository URL',
|
||||
description: 'Add private PowerShell repository URL',
|
||||
key: 'powershell_repo_url',
|
||||
placeholder:
|
||||
'https://pkgs.dev.azure.com/<org>/<project>/_packaging/<feed>/nuget/v3/index.json',
|
||||
fieldType: 'text',
|
||||
storage: 'setting',
|
||||
ee_only: ''
|
||||
},
|
||||
{
|
||||
label: 'PowerShell Repository PAT',
|
||||
description: 'Add private PowerShell repository Personal Access Token',
|
||||
key: 'powershell_repo_pat',
|
||||
fieldType: 'text',
|
||||
storage: 'setting',
|
||||
ee_only: ''
|
||||
}
|
||||
],
|
||||
Alerts: [
|
||||
|
||||
Reference in New Issue
Block a user