diff --git a/sources/api/early-boot-config/src/main.rs b/sources/api/early-boot-config/src/main.rs index 3c3b039e2ea..6064d7bf215 100644 --- a/sources/api/early-boot-config/src/main.rs +++ b/sources/api/early-boot-config/src/main.rs @@ -24,7 +24,7 @@ use std::{env, process}; mod compression; mod provider; mod settings; -use crate::provider::PlatformDataProvider; +use crate::provider::{Platform, PlatformDataProvider}; // TODO // Tests! @@ -40,30 +40,6 @@ const TRANSACTION: &str = "bottlerocket-launch"; // We create it after running successfully. const MARKER_FILE: &str = "/var/lib/bottlerocket/early-boot-config.ran"; -/// This function returns the appropriate data provider for this variant. It exists primarily to -/// keep the ugly bits of conditional compilation out of the main function. -fn create_provider() -> Result> { - #[cfg(bottlerocket_platform = "aws")] - { - Ok(Box::new(provider::aws::AwsDataProvider)) - } - - #[cfg(bottlerocket_platform = "aws-dev")] - { - use std::path::Path; - if Path::new(provider::local_file::LocalFileDataProvider::USER_DATA_FILE).exists() { - Ok(Box::new(provider::local_file::LocalFileDataProvider)) - } else { - Ok(Box::new(provider::aws::AwsDataProvider)) - } - } - - #[cfg(bottlerocket_platform = "vmware")] - { - Ok(Box::new(provider::vmware::VmwareDataProvider)) - } -} - /// Store the args we receive on the command line #[derive(Debug)] struct Args { @@ -134,13 +110,10 @@ async fn run() -> Result<()> { info!("early-boot-config started"); - // Figure out the current provider - let data_provider = create_provider()?; - info!("Retrieving platform-specific data"); let uri = &format!("{}?tx={}", API_SETTINGS_URI, TRANSACTION); let method = "PATCH"; - for settings_json in data_provider + for settings_json in Platform .platform_data() .await .context(error::ProviderError)? diff --git a/sources/api/early-boot-config/src/provider.rs b/sources/api/early-boot-config/src/provider.rs index 33529c61bba..3c30202c313 100644 --- a/sources/api/early-boot-config/src/provider.rs +++ b/sources/api/early-boot-config/src/provider.rs @@ -3,14 +3,18 @@ use crate::settings::SettingsJson; use async_trait::async_trait; -#[cfg(any(bottlerocket_platform = "aws", bottlerocket_platform = "aws-dev"))] -pub(crate) mod aws; - #[cfg(bottlerocket_platform = "aws-dev")] -pub(crate) mod local_file; +mod local_file; + +#[cfg(any(bottlerocket_platform = "aws", bottlerocket_platform = "aws-dev"))] +mod aws; +#[cfg(any(bottlerocket_platform = "aws", bottlerocket_platform = "aws-dev"))] +pub(crate) use aws::AwsDataProvider as Platform; #[cfg(bottlerocket_platform = "vmware")] -pub(crate) mod vmware; +mod vmware; +#[cfg(bottlerocket_platform = "vmware")] +pub(crate) use vmware::VmwareDataProvider as Platform; /// Support for new platforms can be added by implementing this trait. #[async_trait] diff --git a/sources/api/early-boot-config/src/provider/aws.rs b/sources/api/early-boot-config/src/provider/aws.rs index a60e3a323f0..ecacaaac050 100644 --- a/sources/api/early-boot-config/src/provider/aws.rs +++ b/sources/api/early-boot-config/src/provider/aws.rs @@ -9,6 +9,9 @@ use snafu::{OptionExt, ResultExt}; use std::fs; use std::path::Path; +#[cfg(bottlerocket_platform = "aws-dev")] +use crate::provider::local_file::{local_file_user_data, USER_DATA_FILE}; + /// Unit struct for AWS so we can implement the PlatformDataProvider trait. pub(crate) struct AwsDataProvider; @@ -82,7 +85,16 @@ impl PlatformDataProvider for AwsDataProvider { let mut client = ImdsClient::new().await.context(error::ImdsClient)?; - // Instance identity doc first, so the user has a chance to override + // Attempt to read from local file first on the `aws-dev` variant + #[cfg(bottlerocket_platform = "aws-dev")] + { + match local_file_user_data()? { + None => warn!("No user data found via local file: {}", USER_DATA_FILE), + Some(s) => output.push(s), + } + } + + // Instance identity doc next, so the user has a chance to override match Self::identity_document(&mut client).await? { None => warn!("No instance identity document found."), Some(s) => output.push(s), diff --git a/sources/api/early-boot-config/src/provider/local_file.rs b/sources/api/early-boot-config/src/provider/local_file.rs index 69dcb45cb53..5c660ded577 100644 --- a/sources/api/early-boot-config/src/provider/local_file.rs +++ b/sources/api/early-boot-config/src/provider/local_file.rs @@ -1,42 +1,35 @@ -//! The local_file module implements the `PlatformDataProvider` trait for gathering userdata from -//! local file +//! The local_file module provides a method for gathering userdata from local file -use super::{PlatformDataProvider, SettingsJson}; +use super::SettingsJson; use crate::compression::expand_file_maybe; -use async_trait::async_trait; use snafu::ResultExt; +use std::path::Path; -pub(crate) struct LocalFileDataProvider; +pub(crate) const USER_DATA_FILE: &'static str = "/etc/early-boot-config/user-data"; -impl LocalFileDataProvider { - pub(crate) const USER_DATA_FILE: &'static str = "/etc/early-boot-config/user-data"; -} +pub(crate) fn local_file_user_data( +) -> std::result::Result, Box> { + if !Path::new(USER_DATA_FILE).exists() { + return Ok(None); + } + info!("'{}' exists, using it", USER_DATA_FILE); + + // Read the file, decompressing it if compressed. + let user_data_str = expand_file_maybe(USER_DATA_FILE).context(error::InputFileRead { + path: USER_DATA_FILE, + })?; -#[async_trait] -impl PlatformDataProvider for LocalFileDataProvider { - async fn platform_data(&self) -> std::result::Result, Box> { - let mut output = Vec::new(); - info!("'{}' exists, using it", Self::USER_DATA_FILE); - - // Read the file, decompressing it if compressed. - let user_data_str = - expand_file_maybe(Self::USER_DATA_FILE).context(error::InputFileRead { - path: Self::USER_DATA_FILE, - })?; - - if user_data_str.is_empty() { - return Ok(output); - } - - let json = SettingsJson::from_toml_str(&user_data_str, "user data").context( - error::SettingsToJSON { - from: Self::USER_DATA_FILE, - }, - )?; - output.push(json); - - Ok(output) + if user_data_str.is_empty() { + return Ok(None); } + + let json = SettingsJson::from_toml_str(&user_data_str, "user data").context( + error::SettingsToJSON { + from: USER_DATA_FILE, + }, + )?; + + Ok(Some(json)) } mod error {