Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[WIP] provider: add boot check-in on azure and packet #130

Closed
wants to merge 1 commit into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions src/errors.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
// limitations under the License.

use reqwest::header;
use serde_json;

error_chain!{
links {
Expand All @@ -24,6 +25,7 @@ error_chain!{
XmlDeserialize(::serde_xml_rs::Error);
Base64Decode(::base64::DecodeError);
Io(::std::io::Error);
Json(serde_json::Error);
Reqwest(::reqwest::Error);
OpensslStack(::openssl::error::ErrorStack);
HeaderValue(header::InvalidHeaderValue);
Expand Down
12 changes: 12 additions & 0 deletions src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ extern crate reqwest;
#[macro_use]
extern crate serde_derive;
extern crate serde;
#[cfg_attr(test, macro_use)]
extern crate serde_json;
extern crate serde_xml_rs;
#[macro_use]
Expand Down Expand Up @@ -63,6 +64,7 @@ const CMDLINE_OEM_FLAG: &str = "coreos.oem.id";
struct Config {
provider: String,
attributes_file: Option<String>,
check_in: bool,
ssh_keys_user: Option<String>,
hostname_file: Option<String>,
network_units_dir: Option<String>,
Expand Down Expand Up @@ -110,6 +112,12 @@ fn run() -> Result<()> {
.map_or(Ok(()), |x| metadata.write_network_units(x))
.chain_err(|| "writing network units")?;

// perform boot check-in.
if config.check_in {
metadata.boot_checkin()
.chain_err(|| "checking-in instance boot to cloud provider")?;
}

debug!("Done!");

Ok(())
Expand Down Expand Up @@ -142,6 +150,9 @@ fn init() -> Result<Config> {
.long("attributes")
.help("The file into which the metadata attributes are written")
.takes_value(true))
.arg(Arg::with_name("check-in")
.long("cmdline")
.help("Check-in this instance boot with the cloud provider"))
.arg(Arg::with_name("cmdline")
.long("cmdline")
.help("Read the cloud provider from the kernel cmdline"))
Expand Down Expand Up @@ -174,6 +185,7 @@ fn init() -> Result<Config> {
}
},
attributes_file: matches.value_of("attributes").map(String::from),
check_in: matches.is_present("check-in"),
ssh_keys_user: matches.value_of("ssh-keys").map(String::from),
hostname_file: matches.value_of("hostname").map(String::from),
network_units_dir: matches.value_of("network-units").map(String::from),
Expand Down
82 changes: 82 additions & 0 deletions src/providers/azure/mock_tests.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
use mockito::{self, Matcher};
use providers::{azure, MetadataProvider};

#[test]
fn test_boot_checkin() {
let fab_version = "/?comp=versions";
let ver_body = r#"<?xml version="1.0" encoding="utf-8"?>
<Versions>
<Preferred>
<Version>2015-04-05</Version>
</Preferred>
<Supported>
<Version>2015-04-05</Version>
<Version>2012-11-30</Version>
<Version>2012-09-15</Version>
<Version>2012-05-15</Version>
<Version>2011-12-31</Version>
<Version>2011-10-15</Version>
<Version>2011-08-31</Version>
<Version>2011-04-07</Version>
<Version>2010-12-15</Version>
<Version>2010-28-10</Version>
</Supported>
</Versions>"#;
let m_version = mockito::mock("GET", fab_version)
.with_body(ver_body)
.with_status(200)
.create();

let fab_goalstate = "/machine/?comp=goalstate";
let gs_body = r#"<?xml version="1.0" encoding="utf-8"?>
<GoalState xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="goalstate10.xsd">
<Version>2012-11-30</Version>
<Incarnation>1</Incarnation>
<Machine>
<ExpectedState>Started</ExpectedState>
<StopRolesDeadlineHint>300000</StopRolesDeadlineHint>
<LBProbePorts>
<Port>16001</Port>
</LBProbePorts>
<ExpectHealthReport>FALSE</ExpectHealthReport>
</Machine>
<Container>
<ContainerId>a511aa6d-29e7-4f53-8788-55655dfe848f</ContainerId>
<RoleInstanceList>
<RoleInstance>
<InstanceId>f6cd1d7ef1644557b9059345e5ba890c.lars-test-1</InstanceId>
<State>Started</State>
<Configuration>
<HostingEnvironmentConfig>http://100.115.176.3:80/machine/a511aa6d-29e7-4f53-8788-55655dfe848f/f6cd1d7ef1644557b9059345e5ba890c.lars%2Dtest%2D1?comp=config&amp;type=hostingEnvironmentConfig&amp;incarnation=1</HostingEnvironmentConfig>
<SharedConfig>http://100.115.176.3:80/machine/a511aa6d-29e7-4f53-8788-55655dfe848f/f6cd1d7ef1644557b9059345e5ba890c.lars%2Dtest%2D1?comp=config&amp;type=sharedConfig&amp;incarnation=1</SharedConfig>
<ExtensionsConfig>http://100.115.176.3:80/machine/a511aa6d-29e7-4f53-8788-55655dfe848f/f6cd1d7ef1644557b9059345e5ba890c.lars%2Dtest%2D1?comp=config&amp;type=extensionsConfig&amp;incarnation=1</ExtensionsConfig>
<FullConfig>http://100.115.176.3:80/machine/a511aa6d-29e7-4f53-8788-55655dfe848f/f6cd1d7ef1644557b9059345e5ba890c.lars%2Dtest%2D1?comp=config&amp;type=fullConfig&amp;incarnation=1</FullConfig>
<Certificates>http://100.115.176.3:80/machine/a511aa6d-29e7-4f53-8788-55655dfe848f/f6cd1d7ef1644557b9059345e5ba890c.lars%2Dtest%2D1?comp=certificates&amp;incarnation=1</Certificates>
<ConfigName>f6cd1d7ef1644557b9059345e5ba890c.0.f6cd1d7ef1644557b9059345e5ba890c.0.lars-test-1.1.xml</ConfigName>
</Configuration>
</RoleInstance>
</RoleInstanceList>
</Container>
</GoalState>
"#;
let m_goalstate = mockito::mock("GET", fab_goalstate)
.with_body(gs_body)
.with_status(200)
.create();

let fab_health = "/machine/?comp=health";
let m_health = mockito::mock("POST", fab_health)
.match_header("content-type", Matcher::Regex("text/xml".to_string()))
.match_header("x-ms-version", Matcher::Regex("2012-11-30".to_string()))
.match_body(Matcher::Regex("<State>Ready</State>".to_string()))
.with_status(200)
.create();

let provider = azure::Azure::try_new();
let r = provider.unwrap().boot_checkin();

m_version.assert();
m_goalstate.assert();
m_health.assert();
r.unwrap();
}
80 changes: 73 additions & 7 deletions src/providers/azure/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -28,14 +28,15 @@ use errors::*;
use network;
use providers::MetadataProvider;
use retry;
use util;

#[cfg(test)]
mod mock_tests;

static HDR_AGENT_NAME: &str = "x-ms-agent-name";
static HDR_VERSION: &str = "x-ms-version";
static HDR_CIPHER_NAME: &str = "x-ms-cipher-name";
static HDR_CERT: &str = "x-ms-guest-agent-public-x509-cert";

const OPTION_245: &str = "OPTION_245";
const MS_AGENT_NAME: &str = "com.coreos.metadata";
const MS_VERSION: &str = "2012-11-30";
const SMIME_HEADER: &str = "\
Expand All @@ -46,6 +47,28 @@ Content-Transfer-Encoding: base64

";

macro_rules! ready_state {
($container:expr, $instance:expr) => {
format!(r#"<?xml version="1.0" encoding="utf-8"?>
<Health xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<GoalStateIncarnation>1</GoalStateIncarnation>
<Container>
<ContainerId>{}</ContainerId>
<RoleInstanceList>
<Role>
<InstanceId>{}</InstanceId>
<Health>
<State>Ready</State>
</Health>
</Role>
</RoleInstanceList>
</Container>
</Health>
"#,
$container, $instance)
}
}

#[derive(Debug, Deserialize, Clone, Default)]
struct GoalState {
#[serde(rename = "Container")]
Expand All @@ -54,8 +77,10 @@ struct GoalState {

#[derive(Debug, Deserialize, Clone, Default)]
struct Container {
#[serde(rename = "ContainerId")]
pub container_id: String,
#[serde(rename = "RoleInstanceList")]
pub role_instance_list: RoleInstanceList
pub role_instance_list: RoleInstanceList,
}

#[derive(Debug, Deserialize, Clone, Default)]
Expand All @@ -67,7 +92,9 @@ struct RoleInstanceList {
#[derive(Debug, Deserialize, Clone)]
struct RoleInstance {
#[serde(rename = "Configuration")]
pub configuration: Configuration
pub configuration: Configuration,
#[serde(rename = "InstanceId")]
pub instance_id: String,
}

#[derive(Debug, Deserialize, Clone)]
Expand Down Expand Up @@ -174,13 +201,14 @@ impl Azure {
}

fn get_goal_state(&self) -> Result<GoalState> {
self.client.get(retry::Xml, format!("http://{}/machine/?comp=goalstate", self.endpoint)).send()
self.client.get(retry::Xml, format!("{}/machine/?comp=goalstate", self.fabric_base_url())).send()
.chain_err(|| "failed to get goal state")?
.ok_or_else(|| "failed to get goal state: not found response".into())
}

#[cfg(not(test))]
fn get_fabric_address() -> Result<IpAddr> {
let v = util::dns_lease_key_lookup(OPTION_245)?;
let v = ::util::dns_lease_key_lookup("OPTION_245")?;
// value is an 8 digit hex value. convert it to u32 and
// then parse that into an ip. Ipv4Addr::from(u32)
// performs conversion from big-endian
Expand All @@ -190,8 +218,24 @@ impl Azure {
Ok(IpAddr::V4(dec.into()))
}

#[cfg(not(test))]
fn fabric_base_url(&self) -> String {
format!("http://{}", self.endpoint)
}

#[cfg(test)]
fn get_fabric_address() -> Result<IpAddr> {
use std::net::Ipv4Addr;
Ok(IpAddr::from(Ipv4Addr::new(127, 0, 0, 1)))
}

#[cfg(test)]
fn fabric_base_url(&self) -> String {
::mockito::server_url().to_string()
}

fn is_fabric_compatible(&self, version: &str) -> Result<()> {
let versions: Versions = self.client.get(retry::Xml, format!("http://{}/?comp=versions", self.endpoint)).send()
let versions: Versions = self.client.get(retry::Xml, format!("{}/?comp=versions", self.fabric_base_url())).send()
.chain_err(|| "failed to get versions")?
.ok_or_else(|| "failed to get versions: not found")?;

Expand Down Expand Up @@ -282,6 +326,20 @@ impl Azure {

Ok(attributes)
}

/// Return this instance `ContainerId`.
pub(crate) fn container_id(&self) -> &str {
&self.goal_state.container.container_id
}

/// Return this instance `InstanceId`.
pub(crate) fn instance_id(&self) -> Result<&str> {
Ok(&self.goal_state.container
.role_instance_list
.role_instances.get(0)
.ok_or_else(|| "empty RoleInstanceList".to_string())?
.instance_id)
}
}

impl MetadataProvider for Azure {
Expand Down Expand Up @@ -316,4 +374,12 @@ impl MetadataProvider for Azure {
fn network_devices(&self) -> Result<Vec<network::Device>> {
Ok(vec![])
}

fn boot_checkin(&self) -> Result<()> {
let body = ready_state!(self.container_id(), self.instance_id()?);
let url = self.fabric_base_url() + "/machine/?comp=health";
self.client.post(retry::Xml, url, body.into())
.dispatch_post()?;
Ok(())
}
}
5 changes: 5 additions & 0 deletions src/providers/cloudstack/configdrive.rs
Original file line number Diff line number Diff line change
Expand Up @@ -137,6 +137,11 @@ impl MetadataProvider for ConfigDrive {
fn network_devices(&self) -> Result<Vec<network::Device>> {
Ok(vec![])
}

fn boot_checkin(&self) -> Result<()> {
warn!("boot check-in requested, but not supported on this platform");
Ok(())
}
}

impl ::std::ops::Drop for ConfigDrive {
Expand Down
5 changes: 5 additions & 0 deletions src/providers/cloudstack/network.rs
Original file line number Diff line number Diff line change
Expand Up @@ -101,4 +101,9 @@ impl MetadataProvider for CloudstackNetwork {
fn network_devices(&self) -> Result<Vec<network::Device>> {
Ok(vec![])
}

fn boot_checkin(&self) -> Result<()> {
warn!("boot check-in requested, but not supported on this platform");
Ok(())
}
}
5 changes: 5 additions & 0 deletions src/providers/digitalocean/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -265,4 +265,9 @@ impl MetadataProvider for DigitalOceanProvider {
fn network_devices(&self) -> Result<Vec<network::Device>> {
Ok(vec![])
}

fn boot_checkin(&self) -> Result<()> {
warn!("boot check-in requested, but not supported on this platform");
Ok(())
}
}
5 changes: 5 additions & 0 deletions src/providers/ec2/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -140,4 +140,9 @@ impl MetadataProvider for Ec2Provider {
fn network_devices(&self) -> Result<Vec<network::Device>> {
Ok(vec![])
}

fn boot_checkin(&self) -> Result<()> {
warn!("boot check-in requested, but not supported on this platform");
Ok(())
}
}
5 changes: 5 additions & 0 deletions src/providers/gce/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -146,4 +146,9 @@ impl MetadataProvider for GceProvider {
fn network_devices(&self) -> Result<Vec<network::Device>> {
Ok(vec![])
}

fn boot_checkin(&self) -> Result<()> {
warn!("boot check-in requested, but not supported on this platform");
Ok(())
}
}
1 change: 1 addition & 0 deletions src/providers/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,7 @@ pub trait MetadataProvider {
fn ssh_keys(&self) -> Result<Vec<AuthorizedKeyEntry>>;
fn networks(&self) -> Result<Vec<network::Interface>>;
fn network_devices(&self) -> Result<Vec<network::Device>>;
fn boot_checkin(&self) -> Result<()>;

fn write_attributes(&self, attributes_file_path: String) -> Result<()> {
let mut attributes_file = create_file(&attributes_file_path)?;
Expand Down
5 changes: 5 additions & 0 deletions src/providers/openstack/network.rs
Original file line number Diff line number Diff line change
Expand Up @@ -91,4 +91,9 @@ impl MetadataProvider for OpenstackProvider {
fn network_devices(&self) -> Result<Vec<network::Device>> {
Ok(vec![])
}

fn boot_checkin(&self) -> Result<()> {
warn!("boot check-in requested, but not supported on this platform");
Ok(())
}
}
Loading