Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
2 changes: 1 addition & 1 deletion devolutions-agent/src/updater/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -678,7 +678,7 @@ async fn check_for_updates(
}
// Target MSI found, proceed with update.

if product == Product::Agent && version <= AGENT_MIN_SELF_UPDATE_VERSION {
if product == Product::Agent && version < AGENT_MIN_SELF_UPDATE_VERSION {
warn!(
%product,
%version,
Expand Down
3 changes: 2 additions & 1 deletion devolutions-agent/src/updater/package.rs
Original file line number Diff line number Diff line change
Expand Up @@ -152,7 +152,8 @@ async fn launch_updater_shim_detached(
return Err(UpdaterError::AgentUpdateAlreadyInProgress);
}

let shim_log_path = shim_path.with_extension("shim.log");
// The shim derives its log path from the MSI path (see `devolutions-agent-updater/src/main.rs`).
let shim_log_path = format!("{}.shim.log", msi_path);

let mut cmd = tokio::process::Command::new(shim_path.as_str());
if let Some(code) = downgrade_product_code {
Expand Down
26 changes: 5 additions & 21 deletions devolutions-gateway/openapi/gateway-api.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -682,7 +682,7 @@ paths:
- Update
summary: Retrieve the current Devolutions Agent auto-update schedule.
description: |-
Reads the `Schedule` field from `update.json`. When the field is absent the response
Reads the `Schedule` field from `agent_status.json`. When the field is absent the response
contains zeroed defaults (`Enabled: false`, interval `0`, window start `0`, no products).
operationId: GetUpdateSchedule
responses:
Expand Down Expand Up @@ -1661,28 +1661,11 @@ components:
- tcp
- udp
UpdateProduct:
oneOf:
- type: string
enum:
- Gateway
- type: string
enum:
- HubService
- type: string
enum:
- Agent
- type: object
required:
- Other
properties:
Other:
type: string
description: A product name not recognised by this gateway version.
type: string
description: |-
Known product names accepted by the update endpoint.
Product names accepted by the update endpoint.

`Other` captures any product name not yet known to this gateway version;
it is forwarded to the agent unchanged so future agents can act on it.
Known values are `Gateway`, `HubService`, and `Agent`. Any other product name is also accepted and forwarded to the agent unchanged so future product types are supported transparently.
UpdateProductRequest:
type: object
description: Per-product version request.
Expand Down Expand Up @@ -1738,3 +1721,4 @@ components:
scheme: bearer
bearerFormat: JWT
description: Token allowing usage of the standalone web application

33 changes: 31 additions & 2 deletions devolutions-gateway/src/api/update.rs
Original file line number Diff line number Diff line change
Expand Up @@ -95,7 +95,6 @@ pub(crate) struct UpdateProductRequest {
///
/// `Other` captures any product name not yet known to this gateway version;
/// it is forwarded to the agent unchanged so future agents can act on it.
#[cfg_attr(feature = "openapi", derive(utoipa::ToSchema))]
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub(crate) enum UpdateProduct {
Gateway,
Expand All @@ -105,6 +104,29 @@ pub(crate) enum UpdateProduct {
Other(String),
}

#[cfg(feature = "openapi")]
impl<'__s> utoipa::ToSchema<'__s> for UpdateProduct {
fn schema() -> (&'__s str, utoipa::openapi::RefOr<utoipa::openapi::schema::Schema>) {
use utoipa::openapi::schema::{ObjectBuilder, SchemaType};
use utoipa::openapi::RefOr;
(
"UpdateProduct",
RefOr::T(
ObjectBuilder::new()
.schema_type(SchemaType::String)
.description(Some(
"Product names accepted by the update endpoint.\n\n\
Known values are `Gateway`, `HubService`, and `Agent`. \
Any other product name is also accepted and forwarded to the agent \
unchanged so future product types are supported transparently.",
))
.build()
.into(),
),
)
}
}

impl<'de> serde::Deserialize<'de> for UpdateProduct {
fn deserialize<D: serde::Deserializer<'de>>(d: D) -> Result<Self, D::Error> {
struct V;
Expand Down Expand Up @@ -297,6 +319,13 @@ pub(super) async fn trigger_update_check(
.insert(UpdateProduct::Gateway, UpdateProductRequest { version });
}

// Reject requests that specify no products at all; an empty write would clear any
// pending update requests already written into update.json.
if request.products.is_empty() {
return Err(HttpErrorBuilder::new(StatusCode::BAD_REQUEST)
.msg("request must specify at least one product to update"));
}

// Read the existing manifest (503 when agent is not installed).
// `was_v2` tells us if the file on disk was V2; determines which products are accepted.
let (mut manifest, was_v2) = read_manifest().await?;
Expand Down Expand Up @@ -440,7 +469,7 @@ pub(crate) struct SetUpdateScheduleResponse {}

/// Retrieve the current Devolutions Agent auto-update schedule.
///
/// Reads the `Schedule` field from `update.json`. When the field is absent the response
/// Reads the `Schedule` field from `agent_status.json`. When the field is absent the response
/// contains zeroed defaults (`Enabled: false`, interval `0`, window start `0`, no products).
#[cfg_attr(feature = "openapi", utoipa::path(
get,
Expand Down
Loading