-
Notifications
You must be signed in to change notification settings - Fork 13
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
1 parent
99039d2
commit 83f96e6
Showing
22 changed files
with
1,647 additions
and
109 deletions.
There are no files selected for viewing
Large diffs are not rendered by default.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,17 @@ | ||
[package] | ||
name = "devolutions-agent-shared" | ||
version = "0.0.0" | ||
authors = ["Devolutions Inc. <[email protected]>"] | ||
description = "Shared code between the Devolutions Agent and the Devolutions Gateway" | ||
edition = "2021" | ||
publish = false | ||
|
||
|
||
[dependencies] | ||
camino = { version = "1.1" } | ||
cfg-if = "1.0" | ||
serde = { version = "1.0", features = ["derive"] } | ||
thiserror = "1.0" | ||
|
||
[dev-dependencies] | ||
serde_json = "1.0" |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,110 @@ | ||
use std::{fmt, str::FromStr}; | ||
|
||
use serde::{Deserialize, Serialize}; | ||
use thiserror::Error; | ||
|
||
#[derive(Debug, Error)] | ||
#[error("invalid date (`YYYY.MM.DD.R`) version")] | ||
pub struct DateVersionError; | ||
|
||
/// Parsed application version represented in the format `YYYY.MM.DD.R` | ||
#[derive(Debug, Default, Eq, PartialEq, PartialOrd, Ord, Clone, Copy)] | ||
pub struct DateVersion { | ||
// NOTE: Field order is important for `PartialOrd` and `Ord` derives | ||
pub year: u32, | ||
pub month: u32, | ||
pub day: u32, | ||
pub revision: u32, | ||
} | ||
|
||
impl Serialize for DateVersion { | ||
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error> | ||
where | ||
S: serde::ser::Serializer, | ||
{ | ||
self.to_string().serialize(serializer) | ||
} | ||
} | ||
|
||
impl<'de> Deserialize<'de> for DateVersion { | ||
fn deserialize<D>(deserializer: D) -> Result<DateVersion, D::Error> | ||
where | ||
D: serde::de::Deserializer<'de>, | ||
{ | ||
let s = String::deserialize(deserializer)?; | ||
DateVersion::from_str(&s).map_err(serde::de::Error::custom) | ||
} | ||
} | ||
|
||
impl FromStr for DateVersion { | ||
type Err = DateVersionError; | ||
|
||
fn from_str(s: &str) -> Result<Self, Self::Err> { | ||
let mut parts = s.splitn(4, '.'); | ||
|
||
let mut next_part = || parts.next().and_then(|s| u32::from_str(s).ok()).ok_or(DateVersionError); | ||
|
||
let year = next_part()?; | ||
let month = next_part()?; | ||
let day = next_part()?; | ||
// Allow version without revision | ||
let revision = next_part().unwrap_or(0); | ||
|
||
if parts.next().is_some() { | ||
return Err(DateVersionError); | ||
} | ||
|
||
Ok(DateVersion { | ||
year, | ||
month, | ||
day, | ||
revision, | ||
}) | ||
} | ||
} | ||
|
||
impl fmt::Display for DateVersion { | ||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { | ||
write!(f, "{}.{}.{}.{}", self.year, self.month, self.day, self.revision) | ||
} | ||
} | ||
|
||
#[cfg(test)] | ||
mod tests { | ||
use super::*; | ||
|
||
#[test] | ||
fn date_version_rountrip() { | ||
let version = DateVersion { | ||
year: 2022, | ||
month: 10, | ||
day: 1, | ||
revision: 2, | ||
}; | ||
|
||
let version_str = version.to_string(); | ||
assert_eq!(version_str, "2022.10.1.2"); | ||
let parsed_version = DateVersion::from_str(&version_str).unwrap(); | ||
|
||
assert_eq!(version, parsed_version); | ||
} | ||
|
||
// Regression test in case field order gets changed | ||
#[test] | ||
fn date_version_ordering() { | ||
const VERSIONS_ASCENDING_PAIRS: &[(&'static str, &'static str)] = &[ | ||
// cases (>) for fields in order | ||
("2022.10.1.2", "2022.10.1.1"), | ||
("2022.10.2.1", "2022.10.1.1"), | ||
("2022.11.1.1", "2022.10.1.1"), | ||
("2023.10.1.1", "2022.10.1.1"), | ||
]; | ||
|
||
for (v1, v2) in VERSIONS_ASCENDING_PAIRS { | ||
let greater = DateVersion::from_str(v1).unwrap(); | ||
let lesser = DateVersion::from_str(v2).unwrap(); | ||
|
||
assert!(greater > lesser); | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,54 @@ | ||
mod date_version; | ||
mod update_json; | ||
|
||
use std::env; | ||
|
||
use camino::Utf8PathBuf; | ||
use cfg_if::cfg_if; | ||
|
||
pub use date_version::{DateVersion, DateVersionError}; | ||
pub use update_json::{ProductUpdateInfo, UpdateJson, VersionSpecification}; | ||
|
||
cfg_if! { | ||
if #[cfg(target_os = "windows")] { | ||
const COMPANY_DIR: &str = "Devolutions"; | ||
const PROGRAM_DIR: &str = "Agent"; | ||
const APPLICATION_DIR: &str = "Devolutions\\Agent"; | ||
} else if #[cfg(target_os = "macos")] { | ||
const COMPANY_DIR: &str = "Devolutions"; | ||
const PROGRAM_DIR: &str = "Agent"; | ||
const APPLICATION_DIR: &str = "Devolutions Agent"; | ||
} else { | ||
const COMPANY_DIR: &str = "devolutions"; | ||
const PROGRAM_DIR: &str = "agent"; | ||
const APPLICATION_DIR: &str = "devolutions-agent"; | ||
} | ||
} | ||
|
||
pub fn get_data_dir() -> Utf8PathBuf { | ||
if let Ok(config_path_env) = env::var("DAGENT_CONFIG_PATH") { | ||
Utf8PathBuf::from(config_path_env) | ||
} else { | ||
let mut config_path = Utf8PathBuf::new(); | ||
|
||
if cfg!(target_os = "windows") { | ||
let program_data_env = env::var("ProgramData").expect("ProgramData env variable"); | ||
config_path.push(program_data_env); | ||
config_path.push(COMPANY_DIR); | ||
config_path.push(PROGRAM_DIR); | ||
} else if cfg!(target_os = "macos") { | ||
config_path.push("/Library/Application Support"); | ||
config_path.push(APPLICATION_DIR); | ||
} else { | ||
config_path.push("/etc"); | ||
config_path.push(APPLICATION_DIR); | ||
} | ||
|
||
config_path | ||
} | ||
} | ||
|
||
/// Returns the path to the `update.json` file | ||
pub fn get_updater_file_path() -> Utf8PathBuf { | ||
get_data_dir().join("update.json") | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,68 @@ | ||
use crate::DateVersion; | ||
use serde::{Deserialize, Serialize}; | ||
use std::fmt; | ||
|
||
/// Example JSON structure: | ||
/// | ||
/// ```json | ||
/// { | ||
/// "Gateway": { | ||
/// "TargetVersion": "1.2.3.4" | ||
/// } | ||
/// } | ||
/// ``` | ||
/// | ||
#[derive(Debug, Default, Deserialize, Serialize)] | ||
#[serde(rename_all = "PascalCase")] | ||
pub struct UpdateJson { | ||
#[serde(skip_serializing_if = "Option::is_none")] | ||
pub gateway: Option<ProductUpdateInfo>, | ||
} | ||
|
||
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)] | ||
#[serde(rename_all = "snake_case")] | ||
pub enum VersionSpecification { | ||
Latest, | ||
#[serde(untagged)] | ||
Specific(DateVersion), | ||
} | ||
|
||
impl fmt::Display for VersionSpecification { | ||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { | ||
match self { | ||
VersionSpecification::Latest => write!(f, "latest"), | ||
VersionSpecification::Specific(version) => write!(f, "{}", version), | ||
} | ||
} | ||
} | ||
|
||
#[derive(Debug, Clone, Deserialize, Serialize)] | ||
#[serde(rename_all = "PascalCase")] | ||
pub struct ProductUpdateInfo { | ||
/// The version of the product to update to. | ||
pub target_version: VersionSpecification, | ||
} | ||
|
||
#[cfg(test)] | ||
mod tests { | ||
use super::*; | ||
|
||
#[test] | ||
fn version_specification_roundtrip() { | ||
let cases: &[(&'static str, VersionSpecification)] = &[ | ||
( | ||
"2022.2.24.0", | ||
VersionSpecification::Specific("2022.2.24.0".parse().unwrap()), | ||
), | ||
("latest", VersionSpecification::Latest), | ||
]; | ||
|
||
for (serialized, deserizlized) in cases { | ||
let parsed = serde_json::from_str::<VersionSpecification>(&format!("\"{}\"", serialized)).unwrap(); | ||
assert_eq!(parsed, *deserizlized); | ||
|
||
let reserialized = serde_json::to_string(&parsed).unwrap(); | ||
assert_eq!(reserialized, format!("\"{}\"", serialized)); | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -4,38 +4,63 @@ version = "2024.1.2" | |
edition = "2021" | ||
license = "MIT/Apache-2.0" | ||
authors = ["Devolutions Inc. <[email protected]>"] | ||
description = "Agent companion service for Devolutions Gateway" | ||
build = "build.rs" | ||
publish = false | ||
|
||
[dependencies] | ||
# In-house | ||
devolutions-gateway-task = { path = "../crates/devolutions-gateway-task" } | ||
devolutions-log = { path = "../crates/devolutions-log" } | ||
|
||
# Lifecycle | ||
anyhow = "1.0" | ||
async-trait = "0.1" | ||
# TODO: serde not needed? | ||
camino = { version = "1.1", features = ["serde1"] } | ||
ceviche = "0.6" | ||
ctrlc = "3.1" | ||
|
||
# Serialization | ||
devolutions-agent-shared = { path = "../crates/devolutions-agent-shared" } | ||
devolutions-gateway-task = { path = "../crates/devolutions-gateway-task" } | ||
devolutions-log = { path = "../crates/devolutions-log" } | ||
futures = "0.3" | ||
hex = "0.4" | ||
notify-debouncer-mini = "0.4.1" | ||
parking_lot = "0.12" | ||
reqwest = { version = "0.12", default-features = false, features = ["rustls-tls-native-roots"] } | ||
serde = "1.0" | ||
serde_derive = "1.0" | ||
serde_json = "1.0" | ||
|
||
# Error handling | ||
anyhow = "1.0" | ||
|
||
# Utils, misc | ||
camino = { version = "1.1", features = ["serde1"] } | ||
cfg-if = "1.0" | ||
parking_lot = "0.12" | ||
sha2 = "0.10" | ||
smallvec = "1" | ||
tap = "1.0" | ||
thiserror = "1" | ||
tracing = "0.1" | ||
uuid = { version = "1.5", features = ["v4"] } | ||
|
||
# Async | ||
futures = "0.3" | ||
tokio = { version = "1.37", features = ["signal", "net", "io-util", "time", "rt", "rt-multi-thread", "sync", "macros", "parking_lot", "fs"] } | ||
[dependencies.tokio] | ||
version = "1.37" | ||
features = [ | ||
"signal", | ||
"net", | ||
"io-util", | ||
"rt", | ||
"rt-multi-thread", | ||
"macros", | ||
"parking_lot", | ||
"fs", | ||
"process" | ||
] | ||
|
||
# Logging | ||
tracing = "0.1" | ||
[target.'cfg(windows)'.dependencies] | ||
winreg = "0.52" | ||
|
||
[target.'cfg(windows)'.dependencies.windows] | ||
version = "0.57" | ||
features = [ | ||
"Win32_Foundation", | ||
"Win32_Storage_FileSystem", | ||
"Win32_Security", | ||
"Win32_System_Threading", | ||
"Win32_Security_Cryptography", | ||
"Win32_Security_Authorization", | ||
"Win32_System_ApplicationInstallationAndServicing" | ||
] | ||
|
||
[target.'cfg(windows)'.build-dependencies] | ||
embed-resource = "2.4" | ||
embed-resource = "2.4" |
Oops, something went wrong.