mirror of
https://github.com/windmill-labs/windmill.git
synced 2026-08-23 00:00:33 +00:00
feat: apply workspace specific tags only to some workspaces (#4107)
This commit is contained in:
+17
-12
@@ -23,11 +23,11 @@ use windmill_common::ee::schedule_key_renewal;
|
||||
use windmill_common::{
|
||||
global_settings::{
|
||||
BASE_URL_SETTING, BUNFIG_INSTALL_SCOPES_SETTING, CRITICAL_ERROR_CHANNELS_SETTING,
|
||||
CUSTOM_TAGS_SETTING, DEFAULT_TAGS_PER_WORKSPACE_SETTING, ENV_SETTINGS,
|
||||
EXPOSE_DEBUG_METRICS_SETTING, EXPOSE_METRICS_SETTING, EXTRA_PIP_INDEX_URL_SETTING,
|
||||
HUB_BASE_URL_SETTING, JOB_DEFAULT_TIMEOUT_SECS_SETTING, JWT_SECRET_SETTING,
|
||||
KEEP_JOB_DIR_SETTING, LICENSE_KEY_SETTING, NPM_CONFIG_REGISTRY_SETTING, OAUTH_SETTING,
|
||||
PIP_INDEX_URL_SETTING, REQUEST_SIZE_LIMIT_SETTING,
|
||||
CUSTOM_TAGS_SETTING, DEFAULT_TAGS_PER_WORKSPACE_SETTING, DEFAULT_TAGS_WORKSPACES_SETTING,
|
||||
ENV_SETTINGS, EXPOSE_DEBUG_METRICS_SETTING, EXPOSE_METRICS_SETTING,
|
||||
EXTRA_PIP_INDEX_URL_SETTING, HUB_BASE_URL_SETTING, JOB_DEFAULT_TIMEOUT_SECS_SETTING,
|
||||
JWT_SECRET_SETTING, KEEP_JOB_DIR_SETTING, LICENSE_KEY_SETTING, NPM_CONFIG_REGISTRY_SETTING,
|
||||
OAUTH_SETTING, PIP_INDEX_URL_SETTING, REQUEST_SIZE_LIMIT_SETTING,
|
||||
REQUIRE_PREEXISTING_USER_FOR_OAUTH_SETTING, RETENTION_PERIOD_SECS_SETTING,
|
||||
SAML_METADATA_SETTING, SCIM_TOKEN_SETTING,
|
||||
},
|
||||
@@ -61,11 +61,11 @@ use windmill_worker::{
|
||||
|
||||
use crate::monitor::{
|
||||
initial_load, load_keep_job_dir, load_metrics_debug_enabled, load_require_preexisting_user,
|
||||
load_tag_per_workspace_enabled, monitor_db, monitor_pool, reload_base_url_setting,
|
||||
reload_bunfig_install_scopes_setting, reload_critical_error_channels_setting,
|
||||
reload_extra_pip_index_url_setting, reload_hub_base_url_setting,
|
||||
reload_job_default_timeout_setting, reload_jwt_secret_setting, reload_license_key,
|
||||
reload_npm_config_registry_setting, reload_pip_index_url_setting,
|
||||
load_tag_per_workspace_enabled, load_tag_per_workspace_workspaces, monitor_db, monitor_pool,
|
||||
reload_base_url_setting, reload_bunfig_install_scopes_setting,
|
||||
reload_critical_error_channels_setting, reload_extra_pip_index_url_setting,
|
||||
reload_hub_base_url_setting, reload_job_default_timeout_setting, reload_jwt_secret_setting,
|
||||
reload_license_key, reload_npm_config_registry_setting, reload_pip_index_url_setting,
|
||||
reload_retention_period_setting, reload_scim_token_setting, reload_server_config,
|
||||
reload_worker_config,
|
||||
};
|
||||
@@ -365,7 +365,7 @@ Windmill Community Edition {GIT_VERSION}
|
||||
|
||||
#[cfg(feature = "tantivy")]
|
||||
let should_index_jobs =
|
||||
mode == Mode::Indexer || (enable_standalone_indexer && mode == Mode::Standalone);
|
||||
mode == Mode::Indexer || (enable_standalone_indexer && mode == Mode::Standalone);
|
||||
|
||||
#[cfg(not(feature = "tantivy"))]
|
||||
let should_index_jobs = false;
|
||||
@@ -504,9 +504,14 @@ Windmill Community Edition {GIT_VERSION}
|
||||
},
|
||||
DEFAULT_TAGS_PER_WORKSPACE_SETTING => {
|
||||
if let Err(e) = load_tag_per_workspace_enabled(&db).await {
|
||||
tracing::error!("Error loading default tag per workpsace: {e:#}");
|
||||
tracing::error!("Error loading default tag per workspace: {e:#}");
|
||||
}
|
||||
},
|
||||
DEFAULT_TAGS_WORKSPACES_SETTING => {
|
||||
if let Err(e) = load_tag_per_workspace_workspaces(&db).await {
|
||||
tracing::error!("Error loading default tag per workspace workspaces: {e:#}");
|
||||
}
|
||||
}
|
||||
RETENTION_PERIOD_SECS_SETTING => {
|
||||
reload_retention_period_setting(&db).await
|
||||
},
|
||||
|
||||
+33
-6
@@ -28,10 +28,11 @@ use windmill_common::{
|
||||
flow_status::FlowStatusModule,
|
||||
global_settings::{
|
||||
BASE_URL_SETTING, BUNFIG_INSTALL_SCOPES_SETTING, CRITICAL_ERROR_CHANNELS_SETTING,
|
||||
DEFAULT_TAGS_PER_WORKSPACE_SETTING, EXPOSE_DEBUG_METRICS_SETTING, EXPOSE_METRICS_SETTING,
|
||||
EXTRA_PIP_INDEX_URL_SETTING, HUB_BASE_URL_SETTING, JOB_DEFAULT_TIMEOUT_SECS_SETTING,
|
||||
JWT_SECRET_SETTING, KEEP_JOB_DIR_SETTING, LICENSE_KEY_SETTING, NPM_CONFIG_REGISTRY_SETTING,
|
||||
OAUTH_SETTING, PIP_INDEX_URL_SETTING, REQUEST_SIZE_LIMIT_SETTING,
|
||||
DEFAULT_TAGS_PER_WORKSPACE_SETTING, DEFAULT_TAGS_WORKSPACES_SETTING,
|
||||
EXPOSE_DEBUG_METRICS_SETTING, EXPOSE_METRICS_SETTING, EXTRA_PIP_INDEX_URL_SETTING,
|
||||
HUB_BASE_URL_SETTING, JOB_DEFAULT_TIMEOUT_SECS_SETTING, JWT_SECRET_SETTING,
|
||||
KEEP_JOB_DIR_SETTING, LICENSE_KEY_SETTING, NPM_CONFIG_REGISTRY_SETTING, OAUTH_SETTING,
|
||||
PIP_INDEX_URL_SETTING, REQUEST_SIZE_LIMIT_SETTING,
|
||||
REQUIRE_PREEXISTING_USER_FOR_OAUTH_SETTING, RETENTION_PERIOD_SECS_SETTING,
|
||||
SAML_METADATA_SETTING, SCIM_TOKEN_SETTING,
|
||||
},
|
||||
@@ -41,8 +42,8 @@ use windmill_common::{
|
||||
users::truncate_token,
|
||||
utils::{now_from_db, rd_string},
|
||||
worker::{
|
||||
load_worker_config, reload_custom_tags_setting, DEFAULT_TAGS_PER_WORKSPACE, SERVER_CONFIG,
|
||||
WORKER_CONFIG,
|
||||
load_worker_config, reload_custom_tags_setting, DEFAULT_TAGS_PER_WORKSPACE,
|
||||
DEFAULT_TAGS_WORKSPACES, SERVER_CONFIG, WORKER_CONFIG,
|
||||
},
|
||||
BASE_URL, CRITICAL_ERROR_CHANNELS, DB, DEFAULT_HUB_BASE_URL, HUB_BASE_URL,
|
||||
METRICS_DEBUG_ENABLED, METRICS_ENABLED,
|
||||
@@ -127,6 +128,10 @@ pub async fn initial_load(
|
||||
tracing::error!("Error loading default tag per workpsace: {e:#}");
|
||||
}
|
||||
|
||||
if let Err(e) = load_tag_per_workspace_workspaces(db).await {
|
||||
tracing::error!("Error loading default tag per workpsace workspaces: {e:#}");
|
||||
}
|
||||
|
||||
if server_mode {
|
||||
load_require_preexisting_user(db).await;
|
||||
}
|
||||
@@ -204,6 +209,28 @@ pub async fn load_tag_per_workspace_enabled(db: &DB) -> error::Result<()> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub async fn load_tag_per_workspace_workspaces(db: &DB) -> error::Result<()> {
|
||||
let workspaces = load_value_from_global_settings(db, DEFAULT_TAGS_WORKSPACES_SETTING).await;
|
||||
|
||||
match workspaces {
|
||||
Ok(Some(serde_json::Value::Array(t))) => {
|
||||
let workspaces = t
|
||||
.iter()
|
||||
.filter_map(|x| x.as_str())
|
||||
.map(|x| x.to_string())
|
||||
.collect::<Vec<String>>();
|
||||
let mut w = DEFAULT_TAGS_WORKSPACES.write().await;
|
||||
*w = Some(workspaces);
|
||||
}
|
||||
Ok(None) => {
|
||||
let mut w = DEFAULT_TAGS_WORKSPACES.write().await;
|
||||
*w = None;
|
||||
}
|
||||
_ => (),
|
||||
};
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub async fn load_metrics_debug_enabled(db: &DB) -> error::Result<()> {
|
||||
let metrics_enabled = load_value_from_global_settings(db, EXPOSE_DEBUG_METRICS_SETTING).await;
|
||||
match metrics_enabled {
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
pub const CUSTOM_TAGS_SETTING: &str = "custom_tags";
|
||||
pub const DEFAULT_TAGS_PER_WORKSPACE_SETTING: &str = "default_tags_per_workspace";
|
||||
pub const DEFAULT_TAGS_WORKSPACES_SETTING: &str = "default_tags_workspaces";
|
||||
pub const BASE_URL_SETTING: &str = "base_url";
|
||||
pub const OAUTH_SETTING: &str = "oauths";
|
||||
pub const RETENTION_PERIOD_SECS_SETTING: &str = "retention_period_secs";
|
||||
|
||||
@@ -43,6 +43,7 @@ lazy_static::lazy_static! {
|
||||
];
|
||||
|
||||
pub static ref DEFAULT_TAGS_PER_WORKSPACE: AtomicBool = AtomicBool::new(false);
|
||||
pub static ref DEFAULT_TAGS_WORKSPACES: Arc<RwLock<Option<Vec<String>>>> = Arc::new(RwLock::new(None));
|
||||
|
||||
|
||||
pub static ref WORKER_CONFIG: Arc<RwLock<WorkerConfig>> = Arc::new(RwLock::new(WorkerConfig {
|
||||
|
||||
@@ -63,7 +63,9 @@ use windmill_common::{
|
||||
scripts::{get_full_hub_script_by_path, ScriptHash, ScriptLang},
|
||||
users::{SUPERADMIN_NOTIFICATION_EMAIL, SUPERADMIN_SECRET_EMAIL},
|
||||
utils::{not_found_if_none, report_critical_error, StripPath},
|
||||
worker::{to_raw_value, DEFAULT_TAGS_PER_WORKSPACE, NO_LOGS, WORKER_CONFIG},
|
||||
worker::{
|
||||
to_raw_value, DEFAULT_TAGS_PER_WORKSPACE, DEFAULT_TAGS_WORKSPACES, NO_LOGS, WORKER_CONFIG,
|
||||
},
|
||||
BASE_URL, DB, METRICS_ENABLED,
|
||||
};
|
||||
|
||||
@@ -3640,7 +3642,13 @@ pub async fn push<'c, 'd, R: rsmq_async::RsmqConnection + Send + 'c>(
|
||||
.map(|e| (Some(e.0), e.1))
|
||||
.unwrap_or_else(|| (None, None));
|
||||
|
||||
let per_workspace: bool = DEFAULT_TAGS_PER_WORKSPACE.load(std::sync::atomic::Ordering::Relaxed);
|
||||
let per_workspace_workspaces = DEFAULT_TAGS_WORKSPACES.read().await;
|
||||
let per_workspace = DEFAULT_TAGS_PER_WORKSPACE.load(std::sync::atomic::Ordering::Relaxed)
|
||||
&& (per_workspace_workspaces.is_none()
|
||||
|| per_workspace_workspaces
|
||||
.as_ref()
|
||||
.unwrap()
|
||||
.contains(&workspace_id.to_string()));
|
||||
|
||||
let tag = if dedicated_worker.is_some_and(|x| x) {
|
||||
format!(
|
||||
|
||||
@@ -6,6 +6,7 @@
|
||||
import DefaultTagsInner from './DefaultTagsInner.svelte'
|
||||
|
||||
export let defaultTagPerWorkspace: boolean | undefined = undefined
|
||||
export let defaultTagWorkspaces: string[] | undefined = undefined
|
||||
|
||||
let placement: 'bottom-end' | 'top-end' = 'bottom-end'
|
||||
</script>
|
||||
@@ -24,5 +25,5 @@
|
||||
>
|
||||
</Button>
|
||||
</svelte:fragment>
|
||||
<DefaultTagsInner bind:defaultTagPerWorkspace />
|
||||
<DefaultTagsInner bind:defaultTagPerWorkspace bind:defaultTagWorkspaces />
|
||||
</Popup>
|
||||
|
||||
@@ -1,25 +1,39 @@
|
||||
<script lang="ts">
|
||||
import { Button } from './common'
|
||||
import { AlertTriangle, Loader2 } from 'lucide-svelte'
|
||||
import { SettingService, WorkerService } from '$lib/gen'
|
||||
import { SettingService, WorkerService, WorkspaceService } from '$lib/gen'
|
||||
import Tooltip from './Tooltip.svelte'
|
||||
import { sendUserToast } from '$lib/toast'
|
||||
import { enterpriseLicense, superadmin } from '$lib/stores'
|
||||
import { DEFAULT_TAGS_PER_WORKSPACE_SETTING } from '$lib/consts'
|
||||
import { DEFAULT_TAGS_PER_WORKSPACE_SETTING, DEFAULT_TAGS_WORKSPACES_SETTING } from '$lib/consts'
|
||||
import Toggle from './Toggle.svelte'
|
||||
import MultiSelectWrapper from './multiselect/MultiSelectWrapper.svelte'
|
||||
|
||||
let defaultTags: string[] | undefined = undefined
|
||||
export let defaultTagPerWorkspace: boolean | undefined = undefined
|
||||
export let defaultTagWorkspaces: string[] | undefined = undefined
|
||||
let limitToWorkspaces = false
|
||||
|
||||
let workspaces: string[] = []
|
||||
async function loadWorkspaces() {
|
||||
workspaces = (await WorkspaceService.listWorkspacesAsSuperAdmin()).map((m) => m.id)
|
||||
}
|
||||
|
||||
async function loadDefaultTags() {
|
||||
try {
|
||||
defaultTags = (await WorkerService.geDefaultTags()) ?? []
|
||||
defaultTagWorkspaces =
|
||||
((await SettingService.getGlobal({
|
||||
key: DEFAULT_TAGS_WORKSPACES_SETTING
|
||||
})) as any) ?? []
|
||||
limitToWorkspaces = defaultTagWorkspaces ? defaultTagWorkspaces.length > 0 : false
|
||||
} catch (err) {
|
||||
sendUserToast(`Could not load default tags: ${err}`, true)
|
||||
}
|
||||
}
|
||||
|
||||
loadDefaultTags()
|
||||
loadWorkspaces()
|
||||
</script>
|
||||
|
||||
<div class="flex flex-col w-80 p-2 gap-2">
|
||||
@@ -48,11 +62,17 @@
|
||||
</div>
|
||||
{/each}
|
||||
</div>
|
||||
<div class="py-4">
|
||||
<div class="py-4 flex flex-col gap-2">
|
||||
<Toggle
|
||||
bind:checked={defaultTagPerWorkspace}
|
||||
options={{ right: 'workspace specific default tags' }}
|
||||
/>
|
||||
{#if defaultTagPerWorkspace}
|
||||
<Toggle bind:checked={limitToWorkspaces} options={{ right: 'only for some workspaces' }} />
|
||||
{#if limitToWorkspaces}
|
||||
<MultiSelectWrapper items={workspaces} bind:value={defaultTagWorkspaces} />
|
||||
{/if}
|
||||
{/if}
|
||||
</div>
|
||||
<Button
|
||||
variant="contained"
|
||||
@@ -65,6 +85,15 @@
|
||||
value: defaultTagPerWorkspace
|
||||
}
|
||||
})
|
||||
await SettingService.setGlobal({
|
||||
key: DEFAULT_TAGS_WORKSPACES_SETTING,
|
||||
requestBody: {
|
||||
value:
|
||||
limitToWorkspaces && defaultTagWorkspaces && defaultTagWorkspaces.length > 0
|
||||
? defaultTagWorkspaces
|
||||
: undefined
|
||||
}
|
||||
})
|
||||
loadDefaultTags()
|
||||
sendUserToast('Saved')
|
||||
}}
|
||||
|
||||
@@ -327,17 +327,17 @@
|
||||
size="xs"
|
||||
on:click={() => {
|
||||
if (nconfig != undefined) {
|
||||
nconfig.worker_tags = defaultTagPerWorkspace
|
||||
? defaultTags.concat(nativeTags).map((nt) => `${nt}-${workspaceTag}`)
|
||||
: defaultTags.concat(nativeTags)
|
||||
nconfig.worker_tags =
|
||||
defaultTagPerWorkspace && workspaceTag
|
||||
? defaultTags.concat(nativeTags).map((nt) => `${nt}-${workspaceTag}`)
|
||||
: defaultTags.concat(nativeTags)
|
||||
|
||||
dirty = true
|
||||
}
|
||||
}}
|
||||
disabled={defaultTagPerWorkspace && !workspaceTag}
|
||||
>
|
||||
Reset to all tags <Tooltip
|
||||
>{(defaultTagPerWorkspace
|
||||
>{(defaultTagPerWorkspace && workspaceTag
|
||||
? defaultTags.concat(nativeTags).map((nt) => `${nt}-${workspaceTag}`)
|
||||
: defaultTags.concat(nativeTags)
|
||||
).join(', ')}</Tooltip
|
||||
@@ -349,13 +349,13 @@
|
||||
size="xs"
|
||||
on:click={() => {
|
||||
if (nconfig != undefined) {
|
||||
nconfig.worker_tags = defaultTagPerWorkspace
|
||||
? defaultTags.map((nt) => `${nt}-${workspaceTag}`)
|
||||
: defaultTags
|
||||
nconfig.worker_tags =
|
||||
defaultTagPerWorkspace && workspaceTag
|
||||
? defaultTags.map((nt) => `${nt}-${workspaceTag}`)
|
||||
: defaultTags
|
||||
dirty = true
|
||||
}
|
||||
}}
|
||||
disabled={defaultTagPerWorkspace && !workspaceTag}
|
||||
>
|
||||
Reset to all tags minus native ones <Tooltip
|
||||
>{(defaultTagPerWorkspace
|
||||
@@ -370,16 +370,16 @@
|
||||
size="xs"
|
||||
on:click={() => {
|
||||
if (nconfig != undefined) {
|
||||
nconfig.worker_tags = defaultTagPerWorkspace
|
||||
? nativeTags.map((nt) => `${nt}-${workspaceTag}`)
|
||||
: nativeTags
|
||||
nconfig.worker_tags =
|
||||
defaultTagPerWorkspace && workspaceTag
|
||||
? nativeTags.map((nt) => `${nt}-${workspaceTag}`)
|
||||
: nativeTags
|
||||
dirty = true
|
||||
}
|
||||
}}
|
||||
disabled={defaultTagPerWorkspace && !workspaceTag}
|
||||
>
|
||||
Reset to native tags <Tooltip
|
||||
>{(defaultTagPerWorkspace
|
||||
>{(defaultTagPerWorkspace && workspaceTag
|
||||
? nativeTags.map((nt) => `${nt}-${workspaceTag}`)
|
||||
: nativeTags
|
||||
).join(', ')}</Tooltip
|
||||
@@ -392,8 +392,7 @@
|
||||
noInputStyles
|
||||
hideArrow={true}
|
||||
items={workspaces.map((w) => w.id)}
|
||||
inputClassName={'flex !font-gray-600 !font-primary !bg-surface-primary ' +
|
||||
(!workspaceTag ? '!border-red-600/60 !dark:border-red-400/70' : '')}
|
||||
inputClassName={'flex !font-gray-600 !font-primary !bg-surface-primary'}
|
||||
dropdownClassName="!text-sm !py-2 !rounded-sm !border-gray-200 !border !shadow-md"
|
||||
className="!font-gray-600 !font-primary !bg-surface-primary"
|
||||
create
|
||||
|
||||
@@ -38,6 +38,7 @@ export const SCRIPT_CUSTOMISE_SHOW_KIND = true
|
||||
export const WORKER_S3_BUCKET_SYNC_SETTING = 'worker_s3_bucket_sync'
|
||||
export const CUSTOM_TAGS_SETTING = 'custom_tags'
|
||||
export const DEFAULT_TAGS_PER_WORKSPACE_SETTING = 'default_tags_per_workspace'
|
||||
export const DEFAULT_TAGS_WORKSPACES_SETTING = 'default_tags_workspaces'
|
||||
|
||||
export const WORKSPACE_SLACK_BOT_TOKEN_PATH = 'f/slack_bot/bot_token'
|
||||
|
||||
|
||||
@@ -14,7 +14,7 @@
|
||||
import Head from '$lib/components/table/Head.svelte'
|
||||
import Tooltip from '$lib/components/Tooltip.svelte'
|
||||
import WorkspaceGroup from '$lib/components/WorkspaceGroup.svelte'
|
||||
import { WorkerService, type WorkerPing, ConfigService } from '$lib/gen'
|
||||
import { WorkerService, type WorkerPing, ConfigService, SettingService } from '$lib/gen'
|
||||
import { enterpriseLicense, superadmin } from '$lib/stores'
|
||||
import { sendUserToast } from '$lib/toast'
|
||||
import { displayDate, groupBy, pluralize, truncate } from '$lib/utils'
|
||||
@@ -23,6 +23,7 @@
|
||||
import AutoComplete from 'simple-svelte-autocomplete'
|
||||
|
||||
import YAML from 'yaml'
|
||||
import { DEFAULT_TAGS_WORKSPACES_SETTING } from '$lib/consts'
|
||||
|
||||
let workers: WorkerPing[] | undefined = undefined
|
||||
let workerGroups: Record<string, any> | undefined = undefined
|
||||
@@ -90,9 +91,13 @@
|
||||
}
|
||||
|
||||
let defaultTagPerWorkspace: boolean | undefined = undefined
|
||||
let defaultTagWorkspaces: string[] | undefined = undefined
|
||||
async function loadDefaultTagsPerWorkspace() {
|
||||
try {
|
||||
defaultTagPerWorkspace = await WorkerService.isDefaultTagsPerWorkspace()
|
||||
defaultTagWorkspaces = (await SettingService.getGlobal({
|
||||
key: DEFAULT_TAGS_WORKSPACES_SETTING
|
||||
})) as any
|
||||
} catch (err) {
|
||||
sendUserToast(`Could not load default tag per workspace setting: ${err}`, true)
|
||||
}
|
||||
@@ -271,7 +276,7 @@
|
||||
/>
|
||||
</div>
|
||||
<div>
|
||||
<DefaultTags bind:defaultTagPerWorkspace />
|
||||
<DefaultTags bind:defaultTagPerWorkspace bind:defaultTagWorkspaces />
|
||||
</div>
|
||||
<div>
|
||||
<Button
|
||||
|
||||
Reference in New Issue
Block a user