Skip to content

Commit dee43fa

Browse files
authored
Get username either by IMDS or from a local OVF file (Azure#100)
* Get username either by IMDS or from a local OVF file Username can be obtained either via fetching instance metadata from IMDS or mounting a local device for OVF environment file. It should not fail immediately in a single failure, instead it should fall back to the other mechanism. So it is not a good idea to use `?` for query() or get_environment(). Explicitly get instance metadata to handle error of missing instance metadata before dealing with setting ssh keys or hostname. * tests: pass in security type when creating VM When running functional tests, pass in --security-type, usually `TrustedLaunch`, which is nowadays the default for most VMs. It is still possible to override the security type by passing in an environment variable $VM_SECURITY_TYPE, like `Standard`. * tests: address shellcheck warnings in functional_tests.sh
1 parent bc83b93 commit dee43fa

File tree

3 files changed

+60
-35
lines changed

3 files changed

+60
-35
lines changed

libazureinit/src/error.rs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,10 @@ pub enum Error {
2727
Nix(#[from] nix::Error),
2828
#[error("The user {user} does not exist")]
2929
UserMissing { user: String },
30+
#[error("failed to get username from IMDS or local OVF files")]
31+
UsernameFailure,
32+
#[error("failed to get instance metadata from IMDS")]
33+
InstanceMetadataFailure,
3034
#[error("Provisioning a user with a non-empty password is not supported")]
3135
NonEmptyPassword,
3236
#[error("Unable to get list of block devices")]

src/main.rs

Lines changed: 40 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -34,25 +34,28 @@ fn get_environment() -> Result<Environment, anyhow::Error> {
3434
}
3535

3636
fn get_username(
37-
instance_metadata: &InstanceMetadata,
38-
environment: &Environment,
37+
instance_metadata: Option<&InstanceMetadata>,
38+
environment: Option<&Environment>,
3939
) -> Result<String, anyhow::Error> {
40-
if instance_metadata
41-
.compute
42-
.os_profile
43-
.disable_password_authentication
44-
{
45-
// password authentication is disabled
46-
Ok(instance_metadata.compute.os_profile.admin_username.clone())
47-
} else {
48-
// password authentication is enabled
49-
50-
Ok(environment
51-
.clone()
52-
.provisioning_section
53-
.linux_prov_conf_set
54-
.username)
40+
if let Some(metadata) = instance_metadata {
41+
if metadata.compute.os_profile.disable_password_authentication {
42+
// If password authentication is disabled,
43+
// simply read from IMDS metadata if available.
44+
return Ok(metadata.compute.os_profile.admin_username.clone());
45+
}
46+
// If password authentication is enabled,
47+
// fall back to reading from OVF environment file.
5548
}
49+
50+
// Read username from OVF environment via mounted local device.
51+
environment
52+
.map(|env| {
53+
env.clone()
54+
.provisioning_section
55+
.linux_prov_conf_set
56+
.username
57+
})
58+
.ok_or(LibError::UsernameFailure.into())
5659
}
5760

5861
#[tokio::main]
@@ -86,11 +89,27 @@ async fn provision() -> Result<(), anyhow::Error> {
8689
.default_headers(default_headers)
8790
.build()?;
8891

89-
let instance_metadata = imds::query(&client).await?;
90-
let username = get_username(&instance_metadata, &get_environment()?)?;
91-
let user = User::new(username, instance_metadata.compute.public_keys);
92+
// Username can be obtained either via fetching instance metadata from IMDS
93+
// or mounting a local device for OVF environment file. It should not fail
94+
// immediately in a single failure, instead it should fall back to the other
95+
// mechanism. So it is not a good idea to use `?` for query() or
96+
// get_environment().
97+
let instance_metadata = imds::query(&client).await.ok();
98+
99+
let environment = get_environment().ok();
100+
101+
let username =
102+
get_username(instance_metadata.as_ref(), environment.as_ref())?;
103+
104+
// It is necessary to get the actual instance metadata after getting username,
105+
// as it is not desirable to immediately return error before get_username.
106+
let im = instance_metadata
107+
.clone()
108+
.ok_or::<LibError>(LibError::InstanceMetadataFailure)?;
109+
110+
let user = User::new(username, im.compute.public_keys);
92111

93-
Provision::new(instance_metadata.compute.os_profile.computer_name, user)
112+
Provision::new(im.compute.os_profile.computer_name, user)
94113
.hostname_provisioners([
95114
#[cfg(feature = "hostnamectl")]
96115
HostnameProvisioner::Hostnamectl,

tests/functional_tests.sh

Lines changed: 16 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ VM_SIZE="${VM_SIZE:-Standard_D2lds_v5}"
1414
VM_ADMIN_USERNAME="${VM_ADMIN_USERNAME:-azureuser}"
1515
AZURE_SSH_KEY_NAME="${AZURE_SSH_KEY_NAME:-azure-ssh-key}"
1616
VM_NAME_WITH_TIMESTAMP=$VM_NAME-$EPOCH
17+
VM_SECURITY_TYPE="${VM_SECURITY_TYPE:-TrustedLaunch}"
1718

1819
set -e
1920

@@ -40,32 +41,33 @@ else
4041
fi
4142

4243
# Set the subscription you want to use
43-
az account set --subscription $SUBSCRIPTION_ID
44+
az account set --subscription "$SUBSCRIPTION_ID"
4445

4546
# Create resource group
46-
az group create -g $RG -l $LOCATION
47+
az group create -g "$RG" -l "$LOCATION"
4748

4849
echo "Creating VM..."
49-
az vm create -n $VM_NAME_WITH_TIMESTAMP \
50-
-g $RG \
51-
--image $VM_IMAGE \
52-
--size $VM_SIZE \
53-
--admin-username $VM_ADMIN_USERNAME \
54-
--ssh-key-value $PATH_TO_PUBLIC_SSH_KEY \
55-
--public-ip-sku Standard
50+
az vm create -n "$VM_NAME_WITH_TIMESTAMP" \
51+
-g "$RG" \
52+
--image "$VM_IMAGE" \
53+
--size "$VM_SIZE" \
54+
--admin-username "$VM_ADMIN_USERNAME" \
55+
--ssh-key-value "$PATH_TO_PUBLIC_SSH_KEY" \
56+
--public-ip-sku Standard \
57+
--security-type "$VM_SECURITY_TYPE"
5658
echo "VM successfully created"
5759

5860
echo "Sleeping to ensure SSH access set up"
5961
sleep 15
6062

6163
echo "Getting VM Public IP Address..."
62-
PUBLIC_IP=$(az vm show -d -g $RG -n $VM_NAME_WITH_TIMESTAMP --query publicIps -o tsv)
63-
echo $PUBLIC_IP
64+
PUBLIC_IP=$(az vm show -d -g "$RG" -n "$VM_NAME_WITH_TIMESTAMP" --query publicIps -o tsv)
65+
echo "$PUBLIC_IP"
6466

65-
scp -o StrictHostKeyChecking=no -i $PATH_TO_PRIVATE_SSH_KEY ./target/debug/functional_tests $VM_ADMIN_USERNAME@$PUBLIC_IP:~
67+
scp -o StrictHostKeyChecking=no -i "$PATH_TO_PRIVATE_SSH_KEY" ./target/debug/functional_tests "$VM_ADMIN_USERNAME"@"$PUBLIC_IP":~
6668

6769
echo "Logging into VM..."
68-
ssh -o StrictHostKeyChecking=no -i $PATH_TO_PRIVATE_SSH_KEY $VM_ADMIN_USERNAME@$PUBLIC_IP 'sudo ./functional_tests test_user'
70+
ssh -o StrictHostKeyChecking=no -i "$PATH_TO_PRIVATE_SSH_KEY" "$VM_ADMIN_USERNAME"@"$PUBLIC_IP" 'sudo ./functional_tests test_user'
6971

7072
# Delete the resource group
71-
az group delete -g $RG --yes --no-wait
73+
az group delete -g "$RG" --yes --no-wait

0 commit comments

Comments
 (0)