Compare commits

..

4 Commits

Author SHA1 Message Date
Maxwell Becker
f9b2994d44 1.16.2 (#145)
* Env vars written using same quotes (single vs double) as the user passes

* fmt

* trim start matches '-'

* ts client version
2024-10-22 11:41:17 -07:00
mbecker20
c0d6d96b64 get username works for service users 2024-10-22 03:36:20 -04:00
mbecker20
34496b948a bump ts client to 1.16.1 2024-10-22 02:58:42 -04:00
mbecker20
90c6adf923 fix periphery installer force file recreation command 2024-10-22 02:55:39 -04:00
24 changed files with 168 additions and 149 deletions

24
Cargo.lock generated
View File

@@ -41,7 +41,7 @@ dependencies = [
[[package]]
name = "alerter"
version = "1.16.1"
version = "1.16.2"
dependencies = [
"anyhow",
"axum",
@@ -943,7 +943,7 @@ dependencies = [
[[package]]
name = "command"
version = "1.16.1"
version = "1.16.2"
dependencies = [
"komodo_client",
"run_command",
@@ -1355,7 +1355,7 @@ dependencies = [
[[package]]
name = "environment_file"
version = "1.16.1"
version = "1.16.2"
dependencies = [
"thiserror",
]
@@ -1439,7 +1439,7 @@ dependencies = [
[[package]]
name = "formatting"
version = "1.16.1"
version = "1.16.2"
dependencies = [
"serror",
]
@@ -1571,7 +1571,7 @@ checksum = "07e28edb80900c19c28f1072f2e8aeca7fa06b23cd4169cefe1af5aa3260783f"
[[package]]
name = "git"
version = "1.16.1"
version = "1.16.2"
dependencies = [
"anyhow",
"command",
@@ -2191,7 +2191,7 @@ dependencies = [
[[package]]
name = "komodo_cli"
version = "1.16.1"
version = "1.16.2"
dependencies = [
"anyhow",
"clap",
@@ -2207,7 +2207,7 @@ dependencies = [
[[package]]
name = "komodo_client"
version = "1.16.1"
version = "1.16.2"
dependencies = [
"anyhow",
"async_timing_util",
@@ -2238,7 +2238,7 @@ dependencies = [
[[package]]
name = "komodo_core"
version = "1.16.1"
version = "1.16.2"
dependencies = [
"anyhow",
"async_timing_util",
@@ -2296,7 +2296,7 @@ dependencies = [
[[package]]
name = "komodo_periphery"
version = "1.16.1"
version = "1.16.2"
dependencies = [
"anyhow",
"async_timing_util",
@@ -2383,7 +2383,7 @@ dependencies = [
[[package]]
name = "logger"
version = "1.16.1"
version = "1.16.2"
dependencies = [
"anyhow",
"komodo_client",
@@ -3089,7 +3089,7 @@ checksum = "e3148f5046208a5d56bcfc03053e3ca6334e51da8dfb19b6cdc8b306fae3283e"
[[package]]
name = "periphery_client"
version = "1.16.1"
version = "1.16.2"
dependencies = [
"anyhow",
"komodo_client",
@@ -4863,7 +4863,7 @@ checksum = "8ecb6da28b8a351d773b68d5825ac39017e680750f980f3a1a85cd8dd28a47c1"
[[package]]
name = "update_logger"
version = "1.16.1"
version = "1.16.2"
dependencies = [
"anyhow",
"komodo_client",

View File

@@ -9,7 +9,7 @@ members = [
]
[workspace.package]
version = "1.16.1"
version = "1.16.2"
edition = "2021"
authors = ["mbecker20 <becker.maxh@gmail.com>"]
license = "GPL-3.0-or-later"

View File

@@ -97,7 +97,10 @@ impl Resolve<RunAction, (User, Update)> for State {
// Keep this stage name as is, the UI will find the latest update log by matching the stage name
"Execute Action",
None,
format!("deno run --allow-read --allow-net --allow-import {}", path.display()),
format!(
"deno run --allow-read --allow-net --allow-import {}",
path.display()
),
false,
)
.await;

View File

@@ -69,15 +69,16 @@ impl Resolve<GetAlertersSummary, User> for State {
GetAlertersSummary {}: GetAlertersSummary,
user: User,
) -> anyhow::Result<GetAlertersSummaryResponse> {
let query =
match resource::get_resource_object_ids_for_user::<Alerter>(&user)
.await?
{
Some(ids) => doc! {
"_id": { "$in": ids }
},
None => Document::new(),
};
let query = match resource::get_resource_object_ids_for_user::<
Alerter,
>(&user)
.await?
{
Some(ids) => doc! {
"_id": { "$in": ids }
},
None => Document::new(),
};
let total = db_client()
.alerters
.count_documents(query)

View File

@@ -69,15 +69,16 @@ impl Resolve<GetBuildersSummary, User> for State {
GetBuildersSummary {}: GetBuildersSummary,
user: User,
) -> anyhow::Result<GetBuildersSummaryResponse> {
let query =
match resource::get_resource_object_ids_for_user::<Builder>(&user)
.await?
{
Some(ids) => doc! {
"_id": { "$in": ids }
},
None => Document::new(),
};
let query = match resource::get_resource_object_ids_for_user::<
Builder,
>(&user)
.await?
{
Some(ids) => doc! {
"_id": { "$in": ids }
},
None => Document::new(),
};
let total = db_client()
.builders
.count_documents(query)

View File

@@ -6,7 +6,7 @@ use komodo_client::{
ListApiKeysForServiceUserResponse, ListApiKeysResponse,
ListUsers, ListUsersResponse,
},
entities::user::{User, UserConfig},
entities::user::{admin_service_user, User, UserConfig},
};
use mungos::{
by_id::find_one_by_id,
@@ -26,6 +26,13 @@ impl Resolve<GetUsername, User> for State {
GetUsername { user_id }: GetUsername,
_: User,
) -> anyhow::Result<GetUsernameResponse> {
if let Some(user) = admin_service_user(&user_id) {
return Ok(GetUsernameResponse {
username: user.username,
avatar: None,
});
}
let user = find_one_by_id(&db_client().users, &user_id)
.await
.context("failed at mongo query for user")?

View File

@@ -130,7 +130,7 @@ impl Resolve<RenameRepo, User> for State {
let server =
resource::get::<Server>(&repo.config.server_id).await?;
let log = match periphery_client(&server)?
.request(api::git::RenameRepo {
curr_name: to_komodo_name(&repo.name),

View File

@@ -182,11 +182,10 @@ impl AllResourcesById {
id_to_tags, match_tags,
)
.await?,
actions:
crate::resource::get_id_to_resource_map::<Action>(
id_to_tags, match_tags,
)
.await?,
actions: crate::resource::get_id_to_resource_map::<Action>(
id_to_tags, match_tags,
)
.await?,
builders: crate::resource::get_id_to_resource_map::<Builder>(
id_to_tags, match_tags,
)

View File

@@ -1,12 +1,15 @@
use anyhow::{anyhow, Context};
use command::run_komodo_command;
use formatting::format_serror;
use komodo_client::entities::{
build::{Build, BuildConfig},
environment_vars_from_str, get_image_name, optional_string,
to_komodo_name,
update::Log,
EnvironmentVar, Version,
use komodo_client::{
entities::{
build::{Build, BuildConfig},
environment_vars_from_str, get_image_name, optional_string,
to_komodo_name,
update::Log,
EnvironmentVar, Version,
},
parsers::QUOTE_PATTERN,
};
use periphery_client::api::build::{
self, PruneBuilders, PruneBuildx,
@@ -101,8 +104,9 @@ impl Resolve<build::Build> for State {
let secret_args = environment_vars_from_str(secret_args)
.context("Invalid secret_args")?;
let _secret_args =
let command_secret_args =
parse_secret_args(&secret_args, *skip_secret_interp)?;
let labels = parse_labels(
&environment_vars_from_str(labels).context("Invalid labels")?,
);
@@ -118,7 +122,7 @@ impl Resolve<build::Build> for State {
// Construct command
let command = format!(
"docker{buildx} build{build_args}{_secret_args}{extra_args}{labels}{image_tags} -f {dockerfile_path} .{push_command}",
"docker{buildx} build{build_args}{command_secret_args}{extra_args}{labels}{image_tags} -f {dockerfile_path} .{push_command}",
);
if *skip_secret_interp {
@@ -190,7 +194,16 @@ fn image_tags(
fn parse_build_args(build_args: &[EnvironmentVar]) -> String {
build_args
.iter()
.map(|p| format!(" --build-arg {}=\"{}\"", p.variable, p.value))
.map(|p| {
if p.value.starts_with(QUOTE_PATTERN)
&& p.value.ends_with(QUOTE_PATTERN)
{
// If the value already wrapped in quotes, don't wrap it again
format!(" --build-arg {}={}", p.variable, p.value)
} else {
format!(" --build-arg {}=\"{}\"", p.variable, p.value)
}
})
.collect::<Vec<_>>()
.join("")
}

View File

@@ -1,14 +1,17 @@
use anyhow::Context;
use command::run_komodo_command;
use formatting::format_serror;
use komodo_client::entities::{
deployment::{
conversions_from_str, extract_registry_domain, Conversion,
Deployment, DeploymentConfig, DeploymentImage, RestartMode,
use komodo_client::{
entities::{
deployment::{
conversions_from_str, extract_registry_domain, Conversion,
Deployment, DeploymentConfig, DeploymentImage, RestartMode,
},
environment_vars_from_str, to_komodo_name,
update::Log,
EnvironmentVar,
},
environment_vars_from_str, to_komodo_name,
update::Log,
EnvironmentVar,
parsers::QUOTE_PATTERN,
};
use periphery_client::api::container::{Deploy, RemoveContainer};
use resolver_api::Resolve;
@@ -175,7 +178,16 @@ fn parse_conversions(
fn parse_environment(environment: &[EnvironmentVar]) -> String {
environment
.iter()
.map(|p| format!(" --env {}=\"{}\"", p.variable, p.value))
.map(|p| {
if p.value.starts_with(QUOTE_PATTERN)
&& p.value.ends_with(QUOTE_PATTERN)
{
// If the value already wrapped in quotes, don't wrap it again
format!(" --env {}={}", p.variable, p.value)
} else {
format!(" --env {}=\"{}\"", p.variable, p.value)
}
})
.collect::<Vec<_>>()
.join("")
}

View File

@@ -1,5 +1,8 @@
use anyhow::Context;
use komodo_client::entities::{EnvironmentVar, SearchCombinator};
use komodo_client::{
entities::{EnvironmentVar, SearchCombinator},
parsers::QUOTE_PATTERN,
};
use crate::config::periphery_config;
@@ -43,7 +46,16 @@ pub fn parse_extra_args(extra_args: &[String]) -> String {
pub fn parse_labels(labels: &[EnvironmentVar]) -> String {
labels
.iter()
.map(|p| format!(" --label {}=\"{}\"", p.variable, p.value))
.map(|p| {
if p.value.starts_with(QUOTE_PATTERN)
&& p.value.ends_with(QUOTE_PATTERN)
{
// If the value already wrapped in quotes, don't wrap it again
format!(" --label {}={}", p.variable, p.value)
} else {
format!(" --label {}=\"{}\"", p.variable, p.value)
}
})
.collect::<Vec<_>>()
.join("")
}

View File

@@ -46,7 +46,7 @@ async fn task(
request: crate::api::PeripheryRequest,
) -> anyhow::Result<String> {
let variant = request.extract_variant();
let res =
State
.resolve_request(request, ())

View File

@@ -12,7 +12,7 @@
//! - X-Api-Secret: `your_api_secret`
//! - Use either Authorization *or* X-Api-Key and X-Api-Secret to authenticate requests.
//! - Body: JSON specifying the request type (`type`) and the parameters (`params`).
//!
//!
//! You can create API keys for your user, or for a Service User with limited permissions,
//! from the Komodo UI Settings page.
//!
@@ -31,11 +31,11 @@
//!
//! The request's parent module (eg. [read], [mod@write]) determines the http path which
//! must be used for the requests. For example, requests under [read] are made using http path `/read`.
//!
//!
//! ## Curl Example
//!
//!
//! Putting it all together, here is an example `curl` for [write::UpdateBuild], to update the version:
//!
//!
//! ```text
//! curl --header "Content-Type: application/json" \
//! --header "X-Api-Key: your_api_key" \

View File

@@ -3,7 +3,10 @@ use resolver_api::derive::Request;
use serde::{Deserialize, Serialize};
use typeshare::typeshare;
use crate::entities::{builder::{Builder, PartialBuilderConfig}, update::Update};
use crate::entities::{
builder::{Builder, PartialBuilderConfig},
update::Update,
};
use super::KomodoWriteRequest;
@@ -94,4 +97,4 @@ pub struct RenameBuilder {
pub id: String,
/// The new name.
pub name: String,
}
}

View File

@@ -3,9 +3,10 @@ use resolver_api::derive::Request;
use serde::{Deserialize, Serialize};
use typeshare::typeshare;
use crate::entities::{procedure::{
Procedure, _PartialProcedureConfig,
}, update::Update};
use crate::entities::{
procedure::{Procedure, _PartialProcedureConfig},
update::Update,
};
use super::KomodoWriteRequest;
@@ -108,4 +109,4 @@ pub struct RenameProcedure {
pub id: String,
/// The new name.
pub name: String,
}
}

View File

@@ -4,7 +4,9 @@ use serde::{Deserialize, Serialize};
use typeshare::typeshare;
use crate::entities::{
repo::{Repo, _PartialRepoConfig}, update::Update, NoData
repo::{Repo, _PartialRepoConfig},
update::Update,
NoData,
};
use super::KomodoWriteRequest;

View File

@@ -3,9 +3,10 @@ use resolver_api::derive::Request;
use serde::{Deserialize, Serialize};
use typeshare::typeshare;
use crate::entities::{server_template::{
PartialServerTemplateConfig, ServerTemplate,
}, update::Update};
use crate::entities::{
server_template::{PartialServerTemplateConfig, ServerTemplate},
update::Update,
};
use super::KomodoWriteRequest;
@@ -96,4 +97,4 @@ pub struct RenameServerTemplate {
pub id: String,
/// The new name.
pub name: String,
}
}

View File

@@ -46,7 +46,7 @@ pub type UpdateUserPasswordResponse = NoData;
/// **Admin only**. Delete a user.
/// Admins can delete any non-admin user.
/// Only Super Admin can delete an admin.
/// No users can delete a Super Admin user.
/// No users can delete a Super Admin user.
/// User cannot delete themselves.
/// Response: [NoData].
#[typeshare]

View File

@@ -34,10 +34,10 @@ use serde::Deserialize;
pub mod api;
pub mod busy;
pub mod deserializers;
pub mod entities;
pub mod parsers;
pub mod ws;
pub mod deserializers;
mod request;

View File

@@ -1,24 +1,40 @@
use anyhow::Context;
pub const QUOTE_PATTERN: &[char] = &['"', '\''];
/// Parses a list of key value pairs from a multiline string
///
/// Example source:
/// ```text
/// # Supports comments
/// KEY_1 = value_1 # end of line comments
///
///
/// # Supports string wrapped values
/// KEY_2="value_2"
/// 'KEY_3 = value_3'
///
///
/// # Also supports yaml list formats
/// - KEY_4: 'value_4'
/// - "KEY_5=value_5"
///
/// # Wrapping outer quotes are removed while inner quotes are preserved
/// "KEY_6 = 'value_6'"
/// ```
///
/// Note this preserves the wrapping string around value.
/// Writing environment file should format the value exactly as it comes in,
/// including the given wrapping quotes.
///
/// Returns:
/// ```text
/// [("KEY_1", "value_1"), ("KEY_2", "value_2"), ("KEY_3", "value_3"), ("KEY_4", "value_4"), ("KEY_5", "value_5")]
/// [
/// ("KEY_1", "value_1"),
/// ("KEY_2", "\"value_2\""),
/// ("KEY_3", "value_3"),
/// ("KEY_4", "'value_4'"),
/// ("KEY_5", "value_5"),
/// ("KEY_6", "'value_6'"),
/// ]
/// ```
pub fn parse_key_value_list(
input: &str,
@@ -46,13 +62,6 @@ pub fn parse_key_value_list(
// Remove preceding '-' (yaml list)
.trim_start_matches('-')
.trim();
// Remove wrapping quotes (from yaml list)
let line = if let Some(line) = line.strip_prefix(['"', '\'']) {
line.strip_suffix(['"', '\'']).unwrap_or(line)
} else {
line
};
// Remove any preceding '"' (from yaml list) (wrapping quotes open)
let (key, value) = line
.split_once(['=', ':'])
.with_context(|| {
@@ -61,15 +70,23 @@ pub fn parse_key_value_list(
)
})
.map(|(key, value)| {
let key = key.trim();
let value = value.trim();
// Remove wrapping quotes around value
let value =
if let Some(value) = value.strip_prefix(['"', '\'']) {
value.strip_suffix(['"', '\'']).unwrap_or(value)
} else {
value
};
(key.trim().to_string(), value.trim().to_string())
// Remove wrapping quotes when around key AND value
let (key, value) = if key.starts_with(QUOTE_PATTERN)
&& !key.ends_with(QUOTE_PATTERN)
&& value.ends_with(QUOTE_PATTERN)
{
(
key.strip_prefix(QUOTE_PATTERN).unwrap().trim(),
value.strip_suffix(QUOTE_PATTERN).unwrap().trim(),
)
} else {
(key, value)
};
(key.to_string(), value.to_string())
})?;
anyhow::Ok((key, value))
})

View File

@@ -1,6 +1,6 @@
{
"name": "komodo_client",
"version": "1.16.0",
"version": "1.16.2",
"description": "Komodo client package",
"homepage": "https://komo.do",
"main": "dist/lib.js",

View File

@@ -28,7 +28,6 @@ import { Link } from "react-router-dom";
import { fmt_duration, fmt_operation, fmt_version } from "@lib/formatting";
import {
cn,
is_service_user,
updateLogToHtml,
usableResourcePath,
version_is_none,
@@ -50,44 +49,6 @@ export const UpdateUser = ({
iconSize?: number;
defaultAvatar?: boolean;
muted?: boolean;
}) => {
if (is_service_user(user_id)) {
return (
<div
className={cn(
"flex items-center gap-2 text-nowrap",
muted && "text-muted-foreground",
className
)}
>
<User className={`w-${iconSize} h-${iconSize}`} />
{user_id}
</div>
);
}
return (
<RealUpdateUser
user_id={user_id}
className={className}
iconSize={iconSize}
defaultAvatar={defaultAvatar}
muted={muted}
/>
);
};
const RealUpdateUser = ({
user_id,
className,
iconSize = 4,
defaultAvatar,
muted,
}: {
user_id: string;
className?: string;
iconSize?: number;
defaultAvatar?: boolean;
muted?: boolean;
}) => {
const res = useRead("GetUsername", { user_id }).data;
const username = res?.username;

View File

@@ -231,20 +231,6 @@ export const sync_no_changes = (sync: Types.ResourceSync) => {
);
};
export const is_service_user = (user_id: string) => {
return (
user_id === "System" ||
user_id === "Procedure" ||
user_id === "Github" ||
user_id === "Git Webhook" ||
user_id === "Auto Redeploy" ||
user_id === "Resource Sync" ||
user_id === "Stack Wizard" ||
user_id === "Build Manager" ||
user_id === "Repo Manager"
);
};
export const extract_registry_domain = (image_name: string) => {
if (!image_name) return "docker.io";
const maybe_domain = image_name.split("/")[0];

View File

@@ -61,5 +61,5 @@ to the current default.
Example:
```sh
curl -sSL https://raw.githubusercontent.com/mbecker20/komodo/main/scripts/setup-periphery.py | python3 --force-service-file
curl -sSL https://raw.githubusercontent.com/mbecker20/komodo/main/scripts/setup-periphery.py | python3 - --force-service-file
```