Skip to content

Commit eb758bf

Browse files
authored
Remove distro specific checks to configure path of tools (#86)
Remove checks for specific distros, to be flexible and scalable. Per-distro path variables will be added in later commits. In libazureinit, add build script `build.rs` to configure build-time custom paths. Use `env!` to consume the paths instead of hard-coding. Split create_user() into 2 functions, create_user_with_useradd() and set_password_with_passwd(), rename set_hostname to set_hostname_with_hostnamectl(). This is to prepare migration to builder pattern to make each command customizable by users in later PRs.
1 parent 001dce2 commit eb758bf

File tree

5 files changed

+72
-85
lines changed

5 files changed

+72
-85
lines changed

libazureinit/Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ name = "libazureinit"
33
version = "0.1.1"
44
edition = "2021"
55
rust-version = "1.76"
6+
build = "build.rs"
67
repository = "https://github.com/Azure/azure-init/"
78
homepage = "https://github.com/Azure/azure-init/"
89
license = "MIT"

libazureinit/build.rs

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
fn main() {
2+
println!("cargo:rerun-if-changed=build.rs");
3+
4+
// Pass in build-time environment variables, which could be used in
5+
// crates by `env!` macros.
6+
println!("cargo:rustc-env=PATH_HOSTNAMECTL=hostnamectl");
7+
println!("cargo:rustc-env=PATH_USERADD=useradd");
8+
println!("cargo:rustc-env=PATH_PASSWD=passwd");
9+
}

libazureinit/src/distro.rs

Lines changed: 46 additions & 70 deletions
Original file line numberDiff line numberDiff line change
@@ -5,29 +5,11 @@ use std::process::Command;
55

66
use crate::error::Error;
77

8-
pub trait Distribution {
9-
fn create_user(&self, username: &str, password: &str)
10-
-> Result<i32, Error>;
11-
fn set_hostname(&self, hostname: &str) -> Result<i32, Error>;
12-
}
13-
14-
pub enum Distributions {
15-
Debian,
16-
Ubuntu,
17-
}
18-
19-
impl Distribution for Distributions {
20-
fn create_user(
21-
&self,
22-
username: &str,
23-
password: &str,
24-
) -> Result<i32, Error> {
25-
match self {
26-
Distributions::Debian | Distributions::Ubuntu => {
27-
let mut home_path = "/home/".to_string();
28-
home_path.push_str(username);
8+
pub fn create_user_with_useradd(username: &str) -> Result<i32, Error> {
9+
let path_useradd = env!("PATH_USERADD");
10+
let home_path = format!("/home/{username}");
2911

30-
let status = Command::new("useradd")
12+
let status = Command::new(path_useradd)
3113
.arg(username)
3214
.arg("--comment")
3315
.arg(
@@ -39,58 +21,52 @@ impl Distribution for Distributions {
3921
.arg(home_path.clone())
4022
.arg("-m")
4123
.status()?;
42-
if !status.success() {
43-
return Err(Error::SubprocessFailed {
44-
command: "useradd".to_string(),
45-
status,
46-
});
47-
}
24+
if !status.success() {
25+
return Err(Error::SubprocessFailed {
26+
command: path_useradd.to_string(),
27+
status,
28+
});
29+
}
4830

49-
if password.is_empty() {
50-
let status = Command::new("passwd")
51-
.arg("-d")
52-
.arg(username)
53-
.status()?;
54-
if !status.success() {
55-
return Err(Error::SubprocessFailed {
56-
command: "passwd".to_string(),
57-
status,
58-
});
59-
}
60-
} else {
61-
// creating user with a non-empty password is not allowed.
62-
return Err(Error::NonEmptyPassword);
63-
}
31+
Ok(0)
32+
}
6433

65-
Ok(0)
66-
}
67-
}
68-
}
69-
fn set_hostname(&self, hostname: &str) -> Result<i32, Error> {
70-
match self {
71-
Distributions::Debian | Distributions::Ubuntu => {
72-
let status = Command::new("hostnamectl")
73-
.arg("set-hostname")
74-
.arg(hostname)
75-
.status()?;
76-
if status.success() {
77-
Ok(status.code().unwrap_or(1))
78-
} else {
79-
Err(Error::SubprocessFailed {
80-
command: "chpasswd".to_string(),
81-
status,
82-
})
83-
}
84-
}
34+
pub fn set_password_with_passwd(
35+
username: &str,
36+
password: &str,
37+
) -> Result<i32, Error> {
38+
let path_passwd = env!("PATH_PASSWD");
39+
40+
if password.is_empty() {
41+
let status =
42+
Command::new(path_passwd).arg("-d").arg(username).status()?;
43+
if !status.success() {
44+
return Err(Error::SubprocessFailed {
45+
command: path_passwd.to_string(),
46+
status,
47+
});
8548
}
49+
} else {
50+
// creating user with a non-empty password is not allowed.
51+
return Err(Error::NonEmptyPassword);
8652
}
53+
54+
Ok(0)
8755
}
88-
impl From<&str> for Distributions {
89-
fn from(s: &str) -> Self {
90-
match s {
91-
"debian" => Distributions::Debian,
92-
"ubuntu" => Distributions::Ubuntu,
93-
_ => panic!("Unknown distribution"),
94-
}
56+
57+
pub fn set_hostname_with_hostnamectl(hostname: &str) -> Result<i32, Error> {
58+
let path_hostnamectl = env!("PATH_HOSTNAMECTL");
59+
60+
let status = Command::new(path_hostnamectl)
61+
.arg("set-hostname")
62+
.arg(hostname)
63+
.status()?;
64+
if status.success() {
65+
Ok(status.code().unwrap_or(1))
66+
} else {
67+
Err(Error::SubprocessFailed {
68+
command: path_hostnamectl.to_string(),
69+
status,
70+
})
9571
}
9672
}

src/main.rs

Lines changed: 9 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -5,9 +5,9 @@ use std::process::ExitCode;
55

66
use anyhow::Context;
77

8-
use libazureinit::distro::{Distribution, Distributions};
98
use libazureinit::imds::InstanceMetadata;
109
use libazureinit::{
10+
distro,
1111
error::Error as LibError,
1212
goalstate, imds, media,
1313
media::Environment,
@@ -93,9 +93,11 @@ async fn provision() -> Result<(), anyhow::Error> {
9393
file_path.push_str(username.as_str());
9494

9595
// always pass an empty password
96-
Distributions::from("ubuntu")
97-
.create_user(username.as_str(), "")
96+
distro::create_user_with_useradd(username.as_str())
9897
.with_context(|| format!("Unabled to create user '{username}'"))?;
98+
distro::set_password_with_passwd(username.as_str(), "").with_context(
99+
|| format!("Unabled to set an empty password for user '{username}'"),
100+
)?;
99101

100102
user::create_ssh_directory(username.as_str(), &file_path)
101103
.await
@@ -111,11 +113,10 @@ async fn provision() -> Result<(), anyhow::Error> {
111113
.await
112114
.with_context(|| "Failed to write ssh public keys.")?;
113115

114-
Distributions::from("ubuntu")
115-
.set_hostname(
116-
instance_metadata.compute.os_profile.computer_name.as_str(),
117-
)
118-
.with_context(|| "Failed to set hostname.")?;
116+
distro::set_hostname_with_hostnamectl(
117+
instance_metadata.compute.os_profile.computer_name.as_str(),
118+
)
119+
.with_context(|| "Failed to set hostname.")?;
119120

120121
let vm_goalstate = goalstate::get_goalstate(&client)
121122
.await

tests/functional_tests.rs

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,9 @@
11
// Copyright (c) Microsoft Corporation.
22
// Licensed under the MIT License.
33

4-
use libazureinit::distro::{Distribution, Distributions};
54
use libazureinit::imds::PublicKeys;
65
use libazureinit::{
7-
goalstate,
6+
distro, goalstate,
87
reqwest::{header, Client},
98
user,
109
};
@@ -61,9 +60,10 @@ async fn main() {
6160
username.as_str()
6261
);
6362

64-
Distributions::from("ubuntu")
65-
.create_user(username.as_str(), "")
66-
.expect("Failed to create user");
63+
distro::create_user_with_useradd(username.as_str())
64+
.expect("Failed to create user for user '{username}'");
65+
distro::set_password_with_passwd(username.as_str(), "")
66+
.expect("Unabled to set an empty passord for user '{username}'");
6767

6868
println!("User {} was successfully created", username.as_str());
6969

@@ -102,9 +102,9 @@ async fn main() {
102102
println!();
103103
println!("Attempting to set the VM hostname");
104104

105-
Distributions::from("ubuntu")
106-
.set_hostname("test-hostname-set")
105+
distro::set_hostname_with_hostnamectl("test-hostname-set")
107106
.expect("Failed to set hostname");
107+
108108
println!("VM hostname successfully set");
109109
println!();
110110

0 commit comments

Comments
 (0)