in edgelet/iotedge/src/config/import/mod.rs [105:602]
fn execute_inner(
old_config_file: &Path,
old_master_encryption_key_path: Option<PathBuf>,
) -> Result<String, std::borrow::Cow<'static, str>> {
let old_config_file_display = old_config_file.display();
let old_config_contents = match std::fs::read_to_string(old_config_file) {
Ok(old_config) => old_config,
Err(err) => match err.kind() {
std::io::ErrorKind::NotFound => {
return Err(format!(
"there is no old config at {old_config_file_display} available to migrate"
)
.into())
}
_ => return Err(format!("could not open {old_config_file_display}: {err}").into()),
},
};
let old_config: old_config::Config = {
// We use YamlFileSource::String to load the file rather than YamlFileSource::File
// because config::ConfigError makes it harder to recognize an error from a missing file.
let old_config = config::ConfigBuilder::<config::builder::DefaultState>::default()
.add_source(YamlFileSource::String(old_config::DEFAULTS.into()))
.add_source(YamlFileSource::String(old_config_contents.into()))
.build();
match old_config.and_then(config::Config::try_deserialize) {
Ok(old_config) => old_config,
Err(err) => {
return Err(format!("could not parse {old_config_file_display}: {err}").into())
}
}
};
let old_config::Config {
provisioning,
agent,
hostname,
parent_hostname,
connect,
listen,
// Ignore the old config's `homedir` value. We want to use a fresh directory and have the right ACLs.
homedir: _,
certificates,
watchdog,
moby_runtime,
} = old_config;
let (provisioning, auto_reprovisioning_mode) = {
let old_config::Provisioning {
provisioning,
dynamic_reprovisioning,
} = provisioning;
let (provisioning, always_reprovision_on_startup) = match provisioning {
old_config::ProvisioningType::Manual(old_config::Manual {
authentication:
old_config::ManualAuthMethod::DeviceConnectionString(
old_config::ManualDeviceConnectionString {
device_id,
hostname,
shared_access_key,
},
),
}) => (
common_config::super_config::Provisioning {
provisioning: common_config::super_config::ProvisioningType::Manual {
inner: common_config::super_config::ManualProvisioning::Explicit {
iothub_hostname: hostname,
device_id,
authentication:
common_config::super_config::ManualAuthMethod::SharedPrivateKey {
device_id_pk:
common_config::super_config::SymmetricKey::Inline {
value: shared_access_key,
},
},
},
},
},
false,
),
old_config::ProvisioningType::Manual(old_config::Manual {
authentication:
old_config::ManualAuthMethod::X509(old_config::ManualX509Auth {
iothub_hostname,
device_id,
identity_cert,
identity_pk,
}),
}) => (
common_config::super_config::Provisioning {
provisioning: common_config::super_config::ProvisioningType::Manual {
inner: common_config::super_config::ManualProvisioning::Explicit {
iothub_hostname,
device_id,
authentication: common_config::super_config::ManualAuthMethod::X509 {
identity: common_config::super_config::X509Identity::Preloaded {
identity_cert,
identity_pk: {
let identity_pk: aziot_keys_common::PreloadedKeyLocation =
identity_pk.to_string()
.parse()
.map_err(|err| format!("could not parse provisioning.authentication.identity_pk: {err}"))?;
identity_pk
},
},
},
},
},
},
false,
),
old_config::ProvisioningType::Dps(old_config::Dps {
global_endpoint,
scope_id,
attestation:
old_config::AttestationMethod::SymmetricKey(
old_config::SymmetricKeyAttestationInfo {
registration_id,
symmetric_key,
},
),
always_reprovision_on_startup,
}) => (
common_config::super_config::Provisioning {
provisioning: common_config::super_config::ProvisioningType::Dps {
global_endpoint,
id_scope: scope_id,
attestation:
common_config::super_config::DpsAttestationMethod::SymmetricKey {
registration_id,
symmetric_key: common_config::super_config::SymmetricKey::Inline {
value: symmetric_key,
},
},
payload: None,
},
},
always_reprovision_on_startup,
),
old_config::ProvisioningType::Dps(old_config::Dps {
global_endpoint,
scope_id,
attestation:
old_config::AttestationMethod::X509(old_config::X509AttestationInfo {
registration_id,
identity_cert,
identity_pk,
}),
always_reprovision_on_startup,
}) => (
common_config::super_config::Provisioning {
provisioning: common_config::super_config::ProvisioningType::Dps {
global_endpoint,
id_scope: scope_id,
attestation: common_config::super_config::DpsAttestationMethod::X509 {
registration_id,
identity: common_config::super_config::X509Identity::Preloaded {
identity_cert,
identity_pk: {
let identity_pk: aziot_keys_common::PreloadedKeyLocation =
identity_pk.to_string()
.parse()
.map_err(|err| format!("could not parse provisioning.attestation.identity_pk: {err}"))?;
identity_pk
},
},
},
payload: None,
},
},
always_reprovision_on_startup,
),
old_config::ProvisioningType::Dps(old_config::Dps {
global_endpoint,
scope_id,
attestation:
old_config::AttestationMethod::Tpm(old_config::TpmAttestationInfo {
registration_id,
}),
always_reprovision_on_startup,
}) => (
common_config::super_config::Provisioning {
provisioning: common_config::super_config::ProvisioningType::Dps {
global_endpoint,
id_scope: scope_id,
attestation: common_config::super_config::DpsAttestationMethod::Tpm {
registration_id,
},
payload: None,
},
},
always_reprovision_on_startup,
),
old_config::ProvisioningType::External(_) => {
return Err("external provisioning is not supported.".into())
}
};
// When both 'dynamic reprovisioning' and 'always reprovision on startup' settings
// are set in the old configuration, 'dynamic reprovisioning' is prioritized,
// since it also triggers a reprovisioning before restarting the daemon.
let auto_reprovisioning_mode = if dynamic_reprovisioning {
edgelet_settings::aziot::AutoReprovisioningMode::Dynamic
} else if always_reprovision_on_startup {
edgelet_settings::aziot::AutoReprovisioningMode::AlwaysOnStartup
} else {
edgelet_settings::aziot::AutoReprovisioningMode::OnErrorOnly
};
(provisioning, auto_reprovisioning_mode)
};
let (edge_ca, trust_bundle_cert) = {
if let Some(old_config::Certificates {
device_cert,
auto_generated_ca_lifetime_days,
}) = certificates
{
if let Some(old_config::DeviceCertificate {
device_ca_cert,
device_ca_pk,
trusted_ca_certs,
}) = device_cert
{
(
Some(super_config::EdgeCa::Preloaded {
cert: device_ca_cert,
pk: device_ca_pk,
}),
Some(trusted_ca_certs),
)
} else {
(
Some(super_config::EdgeCa::Quickstart {
auto_generated_edge_ca_expiry_days: auto_generated_ca_lifetime_days.into(),
auto_renew: cert_renewal::AutoRenewConfig::default(),
subject: None,
}),
None,
)
}
} else {
(None, None)
}
};
let config = super_config::Config {
allow_elevated_docker_permissions: None,
trust_bundle_cert,
auto_reprovisioning_mode,
imported_master_encryption_key: old_master_encryption_key_path,
#[cfg(contenttrust)]
manifest_trust_bundle_cert: None,
additional_info: None,
iotedge_max_requests: Default::default(),
aziot: common_config::super_config::Config {
hostname: Some(hostname),
parent_hostname,
provisioning,
localid: None,
cloud_timeout_sec: aziot_identityd_config::Settings::default_cloud_timeout(),
cloud_retries: aziot_identityd_config::Settings::default_cloud_retries(),
aziot_max_requests: Default::default(),
prefer_module_identity_cache: Default::default(),
aziot_keys: Default::default(),
preloaded_keys: Default::default(),
cert_issuance: Default::default(),
preloaded_certs: Default::default(),
tpm: Default::default(),
endpoints: Default::default(),
},
agent: {
let old_config::ModuleSpec {
name,
type_,
config,
env,
image_pull_policy,
} = agent;
edgelet_settings::ModuleSpec::new(
name,
type_,
{
let old_config::DockerConfig {
image,
image_id,
create_options,
digest,
auth,
} = config;
let new_config = edgelet_settings::DockerConfig::new(
image,
create_options,
digest,
auth,
true,
)?;
// Clippy wants map_or here, but that's not possible without cloning.
#[allow(clippy::option_if_let_else)]
if let Some(image_id) = image_id {
new_config.with_image_hash(image_id)
} else {
new_config
}
},
env,
match image_pull_policy {
old_config::ImagePullPolicy::OnCreate => {
edgelet_settings::module::ImagePullPolicy::OnCreate
}
old_config::ImagePullPolicy::Never => {
edgelet_settings::module::ImagePullPolicy::Never
}
},
)?
},
connect: {
let old_config::Connect {
management_uri,
workload_uri,
} = connect;
edgelet_settings::uri::Connect {
workload_uri,
management_uri,
}
},
listen: {
fn map_listen_uri(uri: url::Url) -> Result<url::Url, url::Url> {
if uri.scheme() == "fd" {
match uri.host_str() {
Some(old_config::DEFAULT_MGMT_SOCKET_UNIT) => {
Ok(AZIOT_EDGED_LISTEN_MGMT_SOCKET_ACTIVATED_URI
.parse()
.expect("hard-coded URI must parse successfully"))
}
Some(old_config::DEFAULT_WORKLOAD_SOCKET_UNIT) => {
Ok(AZIOT_EDGED_LISTEN_WORKLOAD_SOCKET_ACTIVATED_URI
.parse()
.expect("hard-coded URI must parse successfully"))
}
_ => Err(uri),
}
} else {
Ok(uri)
}
}
let old_config::Listen {
management_uri,
workload_uri,
} = listen;
let management_uri = map_listen_uri(management_uri).map_err(|management_uri| {
format!("unexpected value of listen.management_uri {management_uri}",)
})?;
let workload_uri = map_listen_uri(workload_uri).map_err(|workload_uri| {
format!("unexpected value of listen.workload_uri {workload_uri}")
})?;
edgelet_settings::uri::Listen {
workload_uri,
management_uri,
}
},
watchdog: {
let old_config::WatchdogSettings { max_retries } = watchdog;
edgelet_settings::watchdog::Settings {
max_retries: match max_retries {
old_config::RetryLimit::Infinite => {
edgelet_settings::watchdog::MaxRetries::Infinite
}
old_config::RetryLimit::Num(num) => {
edgelet_settings::watchdog::MaxRetries::Num(num)
}
},
}
},
edge_ca,
moby_runtime: {
let old_config::MobyRuntime {
uri,
network,
content_trust,
} = moby_runtime;
super_config::MobyRuntime {
uri,
network: match network {
old_config::MobyNetwork::Network(network) => {
edgelet_settings::MobyNetwork::Network({
let old_config::Network { name, ipv6, ipam } = network;
edgelet_settings::docker::network::Network {
name,
ipv6,
ipam: ipam.map(|ipam| {
let old_config::Ipam { config } = ipam;
edgelet_settings::Ipam {
config: config.map(|config| {
config
.into_iter()
.map(|config| {
let old_config::IpamConfig {
gateway,
subnet,
ip_range,
} = config;
edgelet_settings::docker::network::IpamConfig {
gateway,
subnet,
ip_range,
}
})
.collect()
}),
}
}),
}
})
}
old_config::MobyNetwork::Name(name) => {
edgelet_settings::MobyNetwork::Name(name)
}
},
content_trust: content_trust
.map(
|content_trust| -> Result<_, std::borrow::Cow<'static, str>> {
let old_config::ContentTrust { ca_certs } = content_trust;
Ok(super_config::ContentTrust {
ca_certs: ca_certs
.map(|ca_certs| -> Result<_, std::borrow::Cow<'static, str>> {
let mut new_ca_certs: std::collections::BTreeMap<_, _> =
Default::default();
for (hostname, cert_path) in ca_certs {
let cert_uri = url::Url::from_file_path(&cert_path)
.map_err(|()| {
format!(
"could not convert path {} to file URI",
cert_path.display()
)
})?;
new_ca_certs.insert(hostname, cert_uri);
}
Ok(new_ca_certs)
})
.transpose()?,
})
},
)
.transpose()?,
}
},
image_garbage_collection: ImagePruneSettings::default(),
};
let config =
toml::to_string(&config).map_err(|err| format!("could not serialize config: {err}"))?;
Ok(config)
}