Skip to content

Commit

Permalink
Merge branch 'prevent-apt-and-dnf-from-being-confused-about-package-d…
Browse files Browse the repository at this point in the history
…es-1412'
  • Loading branch information
MarkusPettersson98 committed Nov 8, 2024
2 parents 3de3c63 + e2d67b8 commit e914beb
Show file tree
Hide file tree
Showing 14 changed files with 143 additions and 44 deletions.
61 changes: 61 additions & 0 deletions mullvad-version/src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,2 +1,63 @@
use std::fmt::Display;
use std::str::FromStr;
use std::sync::LazyLock;

use regex::Regex;

/// The Mullvad VPN app product version
pub const VERSION: &str = include_str!(concat!(env!("OUT_DIR"), "/product-version.txt"));

#[derive(Debug, Clone, PartialEq)]
pub struct Version {
pub year: String,
pub incremental: String,
pub beta: Option<String>,
}

impl Version {
pub fn parse(version: &str) -> Version {
Version::from_str(version).unwrap()
}
}

impl Display for Version {
/// Format Version as a string: year.incremental{-beta}
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
let Version {
year,
incremental,
beta,
} = &self;
match beta {
Some(beta) => write!(f, "{year}.{incremental}-{beta}"),
None => write!(f, "{year}.{incremental}"),
}
}
}

impl FromStr for Version {
type Err = String;

fn from_str(version: &str) -> Result<Self, Self::Err> {
const VERSION_REGEX: &str =
r"^20([0-9]{2})\.([1-9][0-9]?)(-beta([1-9][0-9]?))?(-dev-[0-9a-f]+)?$";
static RE: LazyLock<Regex> = LazyLock::new(|| Regex::new(VERSION_REGEX).unwrap());

let captures = RE
.captures(version)
.ok_or_else(|| format!("Version does not match expected format: {version}"))?;
let year = captures.get(1).expect("Missing year").as_str().to_owned();
let incremental = captures
.get(2)
.ok_or("Missing incremental")?
.as_str()
.to_owned();
let beta = captures.get(4).map(|m| m.as_str().to_owned());

Ok(Version {
year,
incremental,
beta,
})
}
}
38 changes: 5 additions & 33 deletions mullvad-version/src/main.rs
Original file line number Diff line number Diff line change
@@ -1,13 +1,9 @@
use regex::Regex;
use mullvad_version::Version;
use std::{env, process::exit};

const ANDROID_VERSION: &str =
include_str!(concat!(env!("OUT_DIR"), "/android-product-version.txt"));

const VERSION_REGEX: &str = r"^20([0-9]{2})\.([1-9][0-9]?)(-beta([1-9][0-9]?))?(-dev-[0-9a-f]+)?$";

const ANDROID_STABLE_VERSION_CODE_SUFFIX: &str = "99";

fn main() {
let command = env::args().nth(1);
match command.as_deref() {
Expand Down Expand Up @@ -53,7 +49,9 @@ fn to_semver(version: &str) -> String {
/// Version: 2021.34
/// versionCode: 21340099
fn to_android_version_code(version: &str) -> String {
let version = parse_version(version);
const ANDROID_STABLE_VERSION_CODE_SUFFIX: &str = "99";

let version = Version::parse(version);
format!(
"{}{:0>2}00{:0>2}",
version.year,
Expand All @@ -67,7 +65,7 @@ fn to_android_version_code(version: &str) -> String {
fn to_windows_h_format(version: &str) -> String {
let Version {
year, incremental, ..
} = parse_version(version);
} = Version::parse(version);

format!(
"#define MAJOR_VERSION 20{year}
Expand All @@ -76,29 +74,3 @@ fn to_windows_h_format(version: &str) -> String {
#define PRODUCT_VERSION \"{version}\""
)
}

struct Version {
year: String,
incremental: String,
beta: Option<String>,
}

fn parse_version(version: &str) -> Version {
let re = Regex::new(VERSION_REGEX).unwrap();
let captures = re
.captures(version)
.expect("Version does not match expected format");
let year = captures.get(1).expect("Missing year").as_str().to_owned();
let incremental = captures
.get(2)
.expect("Missing incremental")
.as_str()
.to_owned();
let beta = captures.get(4).map(|m| m.as_str().to_owned());

Version {
year,
incremental,
beta,
}
}
10 changes: 8 additions & 2 deletions test/Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 0 additions & 1 deletion test/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -75,7 +75,6 @@ shadowsocks-service = "1.20.3"
windows-sys = "0.52.0"
chrono = { version = "0.4.26", default-features = false }
clap = { version = "4.2.7", features = ["cargo", "derive"] }
once_cell = "1.16.0"
bytes = "1.3.0"
async-trait = "0.1.58"
surge-ping = "0.8"
Expand Down
2 changes: 1 addition & 1 deletion test/test-manager/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,6 @@ thiserror = { workspace = true }
bytes = { workspace = true }
test_macro = { path = "./test_macro" }
ipnetwork = "0.20"
once_cell = { workspace = true }
inventory = "0.3"
data-encoding-macro = "0.1.12"
itertools = "0.10.5"
Expand Down Expand Up @@ -57,6 +56,7 @@ mullvad-api = { path = "../../mullvad-api", features = ["api-override"] }
mullvad-management-interface = { path = "../../mullvad-management-interface" }
mullvad-relay-selector = { path = "../../mullvad-relay-selector" }
mullvad-types = { path = "../../mullvad-types" }
mullvad-version = { path = "../../mullvad-version" }
talpid-types = { path = "../../talpid-types" }

ssh2 = "0.9.4"
Expand Down
12 changes: 8 additions & 4 deletions test/test-manager/src/tests/config.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
use once_cell::sync::OnceCell;
use std::sync::OnceLock;
use std::{ops::Deref, path::Path};
use test_rpc::meta::Os;

pub static TEST_CONFIG: TestConfigContainer = TestConfigContainer::new();

/// Default `mullvad_host`. This should match the production env.
pub const DEFAULT_MULLVAD_HOST: &str = "mullvad.net";
/// Bundled OpenVPN CA certificate use with the installed Mullvad app.
Expand Down Expand Up @@ -110,9 +112,13 @@ impl Default for BootstrapScript {
}

#[derive(Debug, Clone)]
pub struct TestConfigContainer(OnceCell<TestConfig>);
pub struct TestConfigContainer(OnceLock<TestConfig>);

impl TestConfigContainer {
const fn new() -> Self {
TestConfigContainer(OnceLock::new())
}

/// Initializes the constants.
///
/// # Panics
Expand All @@ -130,5 +136,3 @@ impl Deref for TestConfigContainer {
self.0.get().unwrap()
}
}

pub static TEST_CONFIG: TestConfigContainer = TestConfigContainer(OnceCell::new());
14 changes: 14 additions & 0 deletions test/test-manager/src/tests/install.rs
Original file line number Diff line number Diff line change
Expand Up @@ -107,6 +107,20 @@ pub async fn test_upgrade_app(
if rpc.mullvad_daemon_get_status().await? != ServiceStatus::Running {
bail!(Error::DaemonNotRunning);
}

// Verify that the correct version was installed
let running_daemon_version = rpc.mullvad_daemon_version().await?;
let running_daemon_version =
mullvad_version::Version::parse(&running_daemon_version).to_string();
ensure!(
&TEST_CONFIG
.app_package_filename
.contains(&running_daemon_version),
"Incorrect deamon version installed. Expected {expected} but {actual} is installed",
expected = TEST_CONFIG.app_package_filename.clone(),
actual = running_daemon_version
);

// Check if any traffic was observed
//
let guest_ip = pinger.guest_ip;
Expand Down
10 changes: 10 additions & 0 deletions test/test-rpc/src/client.rs
Original file line number Diff line number Diff line change
Expand Up @@ -141,6 +141,16 @@ impl ServiceClient {
.map_err(Error::Tarpc)
}

/// Return the version string as reported by `mullvad --version`.
///
/// TODO: Replace with nicer version type.
pub async fn mullvad_daemon_version(&self) -> Result<String, Error> {
self.client
.mullvad_version(tarpc::context::current())
.await
.map_err(Error::Tarpc)?
}

/// Returns all Mullvad app files, directories, and other data found on the system.
pub async fn find_mullvad_app_traces(&self) -> Result<Vec<AppTrace>, Error> {
self.client
Expand Down
3 changes: 3 additions & 0 deletions test/test-rpc/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -156,6 +156,9 @@ mod service {
/// Return status of the system service.
async fn mullvad_daemon_get_status() -> mullvad_daemon::ServiceStatus;

/// Return version number of installed daemon.
async fn mullvad_version() -> Result<String, Error>;

/// Returns all Mullvad app files, directories, and other data found on the system.
async fn find_mullvad_app_traces() -> Result<Vec<AppTrace>, Error>;

Expand Down
1 change: 0 additions & 1 deletion test/test-runner/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,6 @@ tokio = { workspace = true }
tokio-serial = { workspace = true }
thiserror = { workspace = true }
log = { workspace = true }
once_cell = { workspace = true }
parity-tokio-ipc = "0.9"
bytes = { workspace = true }
serde = { workspace = true }
Expand Down
19 changes: 19 additions & 0 deletions test/test-runner/src/app.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,25 @@ use std::path::{Path, PathBuf};

use test_rpc::{AppTrace, Error};

/// Get the installed app version string
pub async fn version() -> Result<String, Error> {
let version = tokio::process::Command::new("mullvad")
.arg("--version")
.output()
.await
.map_err(|e| Error::Service(e.to_string()))?;
let version = String::from_utf8(version.stdout).map_err(|err| Error::Other(err.to_string()))?;
// HACK: The output from `mullvad --version` includes the `mullvad-cli` binary name followed by
// the version string. Simply remove the leading noise and get at the version string.
let Some(version) = version.split_whitespace().nth(1) else {
return Err(Error::Other(
"Could not parse version number from `mullvad-cli --version`".to_string(),
));
};
let version = version.to_string();
Ok(version)
}

#[cfg(target_os = "windows")]
pub fn find_traces() -> Result<Vec<AppTrace>, Error> {
// TODO: Check GUI data
Expand Down
5 changes: 5 additions & 0 deletions test/test-runner/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -136,6 +136,11 @@ impl Service for TestServer {
get_pipe_status()
}

/// Get the installed app version
async fn mullvad_version(self, _: context::Context) -> Result<String, test_rpc::Error> {
app::version().await
}

async fn find_mullvad_app_traces(
self,
_: context::Context,
Expand Down
4 changes: 2 additions & 2 deletions test/test-runner/src/net.rs
Original file line number Diff line number Diff line change
Expand Up @@ -251,10 +251,10 @@ pub fn get_interface_mac(_interface: &str) -> Result<Option<[u8; 6]>, test_rpc::

#[cfg(target_os = "windows")]
pub fn get_default_interface() -> &'static str {
use once_cell::sync::OnceCell;
use std::sync::OnceLock;
use talpid_platform_metadata::WindowsVersion;

static WINDOWS_VERSION: OnceCell<WindowsVersion> = OnceCell::new();
static WINDOWS_VERSION: OnceLock<WindowsVersion> = OnceLock::new();
let version = WINDOWS_VERSION
.get_or_init(|| WindowsVersion::new().expect("failed to obtain Windows version"));

Expand Down
7 changes: 7 additions & 0 deletions test/test-runner/src/package.rs
Original file line number Diff line number Diff line change
Expand Up @@ -173,6 +173,13 @@ fn apt_command() -> Command {
// instead.
cmd.args(["-o", "DPkg::Lock::Timeout=60"]);
cmd.arg("-qy");
// `apt` may consider installing a development build to be a downgrade from the baseline if the
// major version is identical, in which case the ordering is incorrectly based on the git hash
// suffix.
//
// Note that this is only sound if we take precaution to check the installed version after
// running this command.
cmd.arg("--allow-downgrades");

cmd.env("DEBIAN_FRONTEND", "noninteractive");

Expand Down

0 comments on commit e914beb

Please sign in to comment.