From 97f3a1d5451473a3758024d5da8725be0b71ca13 Mon Sep 17 00:00:00 2001 From: Allen Bai Date: Wed, 9 Oct 2019 12:38:49 -0400 Subject: [PATCH] *: cleanup redundant code imported from Afterburn --- src/main.rs | 16 +- src/minimal/instance_type.rs | 9 ++ src/minimal/mod.rs | 3 +- src/minimal/os_release.rs | 5 +- src/minimal/platform.rs | 42 ++--- src/providers/aws/mock_tests.rs | 28 ---- src/providers/aws/mod.rs | 85 ---------- src/providers/mod.rs | 36 ----- src/retry/client.rs | 276 -------------------------------- src/retry/mod.rs | 92 ----------- src/retry/raw_deserializer.rs | 248 ---------------------------- src/util/cmdline.rs | 72 +++++++++ src/util/mod.rs | 18 +++ 13 files changed, 114 insertions(+), 816 deletions(-) create mode 100644 src/minimal/instance_type.rs delete mode 100644 src/providers/aws/mock_tests.rs delete mode 100644 src/providers/aws/mod.rs delete mode 100644 src/providers/mod.rs delete mode 100644 src/retry/client.rs delete mode 100644 src/retry/mod.rs delete mode 100644 src/retry/raw_deserializer.rs create mode 100644 src/util/cmdline.rs create mode 100644 src/util/mod.rs diff --git a/src/main.rs b/src/main.rs index d732381..a7a4209 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,20 +1,9 @@ //! Telemetry service for FCOS. -extern crate base64; #[macro_use] extern crate error_chain; -extern crate openssh_keys; -extern crate openssl; -#[macro_use] extern crate serde_derive; -extern crate serde; -extern crate serde_json; -extern crate serde_xml_rs; -#[macro_use] extern crate slog; -extern crate slog_async; -extern crate slog_term; -#[macro_use] extern crate slog_scope; #[cfg(test)] @@ -24,14 +13,13 @@ extern crate mockito; mod config; /// `Minimal` Agent identity. mod minimal; -/// Provider metadata -mod providers; /// Generic retrying function -mod retry; +// mod retry; /// A library for consistent and reliable error handling mod errors; /// rpm-ostree client. mod rpm_ostree; +mod util; use clap::{Arg, crate_authors, crate_description, crate_name, crate_version}; use config::inputs; diff --git a/src/minimal/instance_type.rs b/src/minimal/instance_type.rs new file mode 100644 index 0000000..b48e25a --- /dev/null +++ b/src/minimal/instance_type.rs @@ -0,0 +1,9 @@ +//! Cloud instance type parsing - utility functions + +/// Call Afterburn to retrieve metadata from cloud providers +pub(crate) fn fetch_metadata() -> Fallible +where + T: AsRef, +{ + +} \ No newline at end of file diff --git a/src/minimal/mod.rs b/src/minimal/mod.rs index e0aabb9..7d301f1 100644 --- a/src/minimal/mod.rs +++ b/src/minimal/mod.rs @@ -2,7 +2,6 @@ mod platform; mod os_release; use crate::config::inputs; -use crate::errors::*; use crate::rpm_ostree; use failure::{Fallible, ResultExt}; use serde::Serialize; @@ -41,7 +40,7 @@ impl Identity { /// Try to fetch default data pub fn try_default(level: &str) -> Fallible { - let platform = platform::read_id(KERNEL_ARGS_FILE)?; + let platform = platform::get_platform(KERNEL_ARGS_FILE)?; let original_os_version = os_release::read_original_os_version(OS_ALEPH_VERSION_FILE)?; let current_os_version = rpm_ostree::booted()?.version; diff --git a/src/minimal/os_release.rs b/src/minimal/os_release.rs index 8243449..da48fb9 100644 --- a/src/minimal/os_release.rs +++ b/src/minimal/os_release.rs @@ -1,8 +1,7 @@ //! OS version parsing - utility functions -use failure::{bail, format_err, Fallible, ResultExt}; -use std::io::Read; -use std::{fs, io}; +use failure::{format_err, Fallible, ResultExt}; +use std::fs; use serde_json; /// Read aleph version info from os version json file. diff --git a/src/minimal/platform.rs b/src/minimal/platform.rs index cd2c2f2..4cf128c 100644 --- a/src/minimal/platform.rs +++ b/src/minimal/platform.rs @@ -9,12 +9,17 @@ use failure::{bail, format_err, Fallible, ResultExt}; use std::io::Read; use std::{fs, io}; +use crate::util; /// Platform key. -static CMDLINE_PLATFORM_FLAG: &str = "ignition.platform.id"; +#[cfg(not(feature = "cl-legacy"))] +const CMDLINE_PLATFORM_FLAG: &str = "ignition.platform.id"; +/// Platform key (CL and RHCOS legacy name: "OEM"). +#[cfg(feature = "cl-legacy")] +const CMDLINE_PLATFORM_FLAG: &str = "coreos.oem.id"; /// Read platform value from cmdline file. -pub(crate) fn read_id(cmdline_path: T) -> Fallible +pub(crate) fn get_platform(cmdline_path: T) -> Fallible where T: AsRef, { @@ -31,11 +36,11 @@ where .with_context(|e| format_err!("failed to read cmdline file {}: {}", fpath, e))?; // lookup flag by key name - match find_flag_value(CMDLINE_PLATFORM_FLAG, &contents) { + match util::find_flag_value(CMDLINE_PLATFORM_FLAG, &contents) { Some(platform) => { log::trace!("found platform id: {}", platform); Ok(platform) - } + }, None => bail!( "could not find flag '{}' in {}", CMDLINE_PLATFORM_FLAG, @@ -44,33 +49,6 @@ where } } -/// Find OEM ID flag value in cmdline string. -fn find_flag_value(flagname: &str, cmdline: &str) -> Option { - // split content into elements and keep key-value tuples only. - let params: Vec<(&str, &str)> = cmdline - .split(' ') - .filter_map(|s| { - let kv: Vec<&str> = s.splitn(2, '=').collect(); - match kv.len() { - 2 => Some((kv[0], kv[1])), - _ => None, - } - }) - .collect(); - - // find the oem flag - for (key, val) in params { - if key != flagname { - continue; - } - let bare_val = val.trim(); - if !bare_val.is_empty() { - return Some(bare_val.to_string()); - } - } - None -} - #[cfg(test)] mod tests { use super::*; @@ -90,7 +68,7 @@ mod tests { ("ignition.platform.id=ec2 foo=bar", Some("ec2".to_string())), ]; for (tcase, tres) in tests { - let res = find_flag_value(flagname, tcase); + let res = util::find_flag_value(flagname, tcase); assert_eq!(res, tres, "failed testcase: '{}'", tcase); } } diff --git a/src/providers/aws/mock_tests.rs b/src/providers/aws/mock_tests.rs deleted file mode 100644 index f6f5726..0000000 --- a/src/providers/aws/mock_tests.rs +++ /dev/null @@ -1,28 +0,0 @@ -use crate::errors::*; -use crate::providers::aws; -use crate::providers::MetadataProvider; -use mockito; - -#[test] -fn test_aws_basic() { - let ep = "/meta-data/instance-id"; - let client = crate::retry::Client::try_new() - .chain_err(|| "failed to create http client") - .unwrap() - .max_attempts(1) - .return_on_404(true); - let provider = aws::AwsProvider { client }; - - provider.get_attributes().unwrap_err(); - - let _m = mockito::mock("GET", ep).with_status(503).create(); - provider.get_attributes().unwrap_err(); - - let _m = mockito::mock("GET", ep).with_status(200).create(); - let v = provider.get_attributes().unwrap(); - // assert_eq!(v.len(), 0); - - let _m = mockito::mock("GET", ep).with_status(404).create(); - let v = provider.get_attributes().unwrap(); - // assert_eq!(v.len(), 0); -} diff --git a/src/providers/aws/mod.rs b/src/providers/aws/mod.rs deleted file mode 100644 index 9b6c843..0000000 --- a/src/providers/aws/mod.rs +++ /dev/null @@ -1,85 +0,0 @@ -// Copyright 2019 Red Hat, Inc. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -//! aws ec2 metadata fetcher -//! - -use std::collections::HashMap; - -#[cfg(test)] -use mockito; - -use crate::errors::*; -use crate::providers::MetadataProvider; -use crate::retry; - -#[cfg(test)] -mod mock_tests; - -#[cfg(not(feature = "cl-legacy"))] -static ENV_PREFIX: &str = "AWS"; -#[cfg(feature = "cl-legacy")] -static ENV_PREFIX: &str = "EC2"; - -#[derive(Clone, Debug)] -pub struct AwsProvider { - client: retry::Client, -} - -impl AwsProvider { - pub fn try_new() -> Result { - let client = retry::Client::try_new()?.return_on_404(true); - - Ok(AwsProvider { client }) - } - - #[cfg(test)] - fn endpoint_for(key: &str) -> String { - let url = mockito::server_url(); - format!("{}/{}", url, key) - } - - #[cfg(not(test))] - fn endpoint_for(key: &str) -> String { - const URL: &str = "http://169.254.169.254/2009-04-04"; - format!("{}/{}", URL, key) - } -} - -impl MetadataProvider for AwsProvider { - fn get_attributes(&self) -> Result> { - let mut out = HashMap::with_capacity(6); - - let add_value = |map: &mut HashMap<_, _>, key: &str, name| -> Result<()> { - let value = self - .client - .get(retry::Raw, AwsProvider::endpoint_for(name)) - .send()?; - - if let Some(value) = value { - map.insert(key.to_string(), value); - } - - Ok(()) - }; - - add_value( - &mut out, - &format!("{}_INSTANCE_ID", ENV_PREFIX), - "meta-data/instance-id", - )?; - - Ok(out) - } -} diff --git a/src/providers/mod.rs b/src/providers/mod.rs deleted file mode 100644 index e0a5a42..0000000 --- a/src/providers/mod.rs +++ /dev/null @@ -1,36 +0,0 @@ -// Copyright 2019 Red Hat, Inc. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -//! Providers -//! -//! These are the providers which Fedora CoreOS Pinger knows how to retrieve metadata -//! from. Internally, they handle the ins and outs of each providers metadata -//! services, and externally, they provide a function to fetch that metadata in -//! a regular format. -//! -//! To add a provider, put a `pub mod provider;` line in this file, export a -//! function to fetch the metadata, and then add a match line in the top-level -//! `fetch_metadata()` function in metadata.rs. -//! -//! _Note_: This module is directly adjusted from Afterburn (https://github.com/coreos/afterburn) - -pub mod aws; - -use std::collections::HashMap; - -use crate::errors::*; - -pub trait MetadataProvider { - fn get_attributes(&self) -> Result>; -} diff --git a/src/retry/client.rs b/src/retry/client.rs deleted file mode 100644 index aba1394..0000000 --- a/src/retry/client.rs +++ /dev/null @@ -1,276 +0,0 @@ -// Copyright 2017 CoreOS, Inc. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -//! retry -//! -//! this is an abstraction over the regular http get request. it allows you to -//! have a request retry until it succeeds, with a configurable number of -//! of attempts and a backoff strategy. It also takes care of automatically -//! deserializing responses and handles headers in a sane way. - -use std::borrow::Cow; -use std::io::Read; -use std::time::Duration; - -use reqwest::header; -use reqwest::{self, Method, Request}; - -use serde; -use serde_json; -use serde_xml_rs; - -use crate::errors::*; -use crate::retry::Retry; - -use crate::retry::raw_deserializer; - -pub trait Deserializer { - fn deserialize(&self, r: R) -> Result - where - T: for<'de> serde::Deserialize<'de>, - R: Read; - fn content_type(&self) -> header::HeaderValue; -} - -#[derive(Debug, Clone, Copy)] -pub struct Xml; - -impl Deserializer for Xml { - fn deserialize(&self, r: R) -> Result - where - T: for<'de> serde::Deserialize<'de>, - R: Read, - { - serde_xml_rs::de::from_reader(r).chain_err(|| "failed xml deserialization") - } - - fn content_type(&self) -> header::HeaderValue { - header::HeaderValue::from_static("text/xml; charset=utf-8") - } -} - -#[derive(Debug, Clone, Copy)] -pub struct Json; - -impl Deserializer for Json { - fn deserialize(&self, r: R) -> Result - where - T: serde::de::DeserializeOwned, - R: Read, - { - serde_json::from_reader(r).chain_err(|| "failed json deserialization") - } - fn content_type(&self) -> header::HeaderValue { - header::HeaderValue::from_static("application/json") - } -} - -#[derive(Debug, Clone, Copy)] -pub struct Raw; - -impl Deserializer for Raw { - fn deserialize(&self, r: R) -> Result - where - T: for<'de> serde::Deserialize<'de>, - R: Read, - { - raw_deserializer::from_reader(r).chain_err(|| "failed raw deserialization") - } - fn content_type(&self) -> header::HeaderValue { - header::HeaderValue::from_static("text/plain; charset=utf-8") - } -} - -#[derive(Debug, Clone)] -pub struct Client { - client: reqwest::Client, - headers: header::HeaderMap, - retry: Retry, - return_on_404: bool, -} - -impl Client { - pub fn try_new() -> Result { - let client = reqwest::Client::builder() - .build() - .chain_err(|| "failed to initialize client")?; - Ok(Client { - client, - headers: header::HeaderMap::new(), - retry: Retry::new(), - return_on_404: false, - }) - } - - pub fn header(mut self, k: header::HeaderName, v: header::HeaderValue) -> Self { - self.headers.append(k, v); - self - } - - pub fn initial_backoff(mut self, initial_backoff: Duration) -> Self { - self.retry = self.retry.initial_backoff(initial_backoff); - self - } - - pub fn max_backoff(mut self, max_backoff: Duration) -> Self { - self.retry = self.retry.max_backoff(max_backoff); - self - } - - /// max_attempts will panic if the argument is greater than 500 - pub fn max_attempts(mut self, max_attempts: u32) -> Self { - self.retry = self.retry.max_attempts(max_attempts); - self - } - - pub fn return_on_404(mut self, return_on_404: bool) -> Self { - self.return_on_404 = return_on_404; - self - } - - pub fn get(&self, d: D, url: String) -> RequestBuilder - where - D: Deserializer, - { - RequestBuilder { - url, - body: None, - d, - client: self.client.clone(), - headers: self.headers.clone(), - retry: self.retry.clone(), - return_on_404: self.return_on_404, - } - } - - pub fn post(&self, d: D, url: String, body: Option>) -> RequestBuilder - where - D: Deserializer, - { - RequestBuilder { - url, - body: body.map(Cow::into_owned), - d, - client: self.client.clone(), - headers: self.headers.clone(), - retry: self.retry.clone(), - return_on_404: self.return_on_404, - } - } -} - -pub struct RequestBuilder -where - D: Deserializer, -{ - url: String, - body: Option, - d: D, - client: reqwest::Client, - headers: header::HeaderMap, - retry: Retry, - return_on_404: bool, -} - -impl RequestBuilder -where - D: Deserializer, -{ - pub fn header(mut self, k: header::HeaderName, v: header::HeaderValue) -> Self { - self.headers.append(k, v); - self - } - - pub fn send(self) -> Result> - where - T: for<'de> serde::Deserialize<'de>, - { - let url = reqwest::Url::parse(self.url.as_str()).chain_err(|| "failed to parse uri")?; - let mut req = Request::new(Method::GET, url); - req.headers_mut().extend(self.headers.clone().into_iter()); - - self.retry.clone().retry(|attempt| { - info!("Fetching {}: Attempt #{}", req.url(), attempt + 1); - self.dispatch_request(&req) - }) - } - - pub fn dispatch_post(self) -> Result { - let url = reqwest::Url::parse(self.url.as_str()).chain_err(|| "failed to parse uri")?; - - self.retry.clone().retry(|attempt| { - let mut builder = reqwest::Client::new() - .post(url.clone()) - .headers(self.headers.clone()) - .header(header::CONTENT_TYPE, self.d.content_type()); - if let Some(ref content) = self.body { - builder = builder.body(content.clone()); - }; - let req = builder - .build() - .chain_err(|| "failed to build POST request")?; - - info!("Posting {}: Attempt #{}", req.url(), attempt + 1); - let status = self - .client - .execute(req) - .chain_err(|| "failed to POST request")? - .status(); - if status.is_success() { - Ok(status) - } else { - Err(format!("POST failed: {}", status).into()) - } - }) - } - - fn dispatch_request(&self, req: &Request) -> Result> - where - T: for<'de> serde::Deserialize<'de>, - { - match self.client.execute(clone_request(req)) { - Ok(resp) => match (resp.status(), self.return_on_404) { - (reqwest::StatusCode::OK, _) => { - info!("Fetch successful"); - self.d - .deserialize(resp) - .map(Some) - .chain_err(|| "failed to deserialize data") - } - (reqwest::StatusCode::NOT_FOUND, true) => { - info!("Fetch failed with 404: resource not found"); - Ok(None) - } - (s, _) => { - info!("Failed to fetch: {}", s); - Err(format!("failed to fetch: {}", s).into()) - } - }, - Err(e) => { - info!("Failed to fetch: {}", e); - Err(Error::with_chain(e, "failed to fetch")) - } - } - } -} - -/// Reqwests Request struct doesn't implement `Clone`, -/// so we have to do it here. -fn clone_request(req: &Request) -> Request { - let mut newreq = Request::new(req.method().clone(), req.url().clone()); - newreq - .headers_mut() - .extend(req.headers().clone().into_iter()); - newreq -} diff --git a/src/retry/mod.rs b/src/retry/mod.rs deleted file mode 100644 index 74405b8..0000000 --- a/src/retry/mod.rs +++ /dev/null @@ -1,92 +0,0 @@ -// Copyright 2017 CoreOS, Inc. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -//! retry is a generic function that retrys functions until they succeed. - -use crate::errors::*; -use std::thread; -use std::time::Duration; - -mod client; -pub mod raw_deserializer; -pub use self::client::*; - -#[derive(Clone, Debug)] -pub struct Retry { - initial_backoff: Duration, - max_backoff: Duration, - max_attempts: u32, -} - -impl ::std::default::Default for Retry { - fn default() -> Self { - Retry { - initial_backoff: Duration::new(1, 0), - max_backoff: Duration::new(5, 0), - max_attempts: 10, - } - } -} - -impl Retry { - pub fn new() -> Self { - Retry::default() - } - - pub fn initial_backoff(mut self, initial_backoff: Duration) -> Self { - self.initial_backoff = initial_backoff; - self - } - - pub fn max_backoff(mut self, max_backoff: Duration) -> Self { - self.max_backoff = max_backoff; - self - } - - pub fn max_attempts(mut self, max_attempts: u32) -> Self { - self.max_attempts = max_attempts; - self - } - - pub fn retry(self, try_fn: F) -> Result - where - F: Fn(u32) -> Result, - { - let mut delay = self.initial_backoff; - let mut attempts = 0; - - loop { - let res = try_fn(attempts); - - // if the result is ok, we don't need to try again - if res.is_ok() { - break res; - } - - // otherwise, perform the retry-backoff logic - attempts += 1; - if attempts == self.max_attempts { - break res.map_err(|e| Error::with_chain(e, "timed out")); - } - - thread::sleep(delay); - - delay = if self.max_backoff != Duration::new(0, 0) && delay * 2 > self.max_backoff { - self.max_backoff - } else { - delay * 2 - }; - } - } -} diff --git a/src/retry/raw_deserializer.rs b/src/retry/raw_deserializer.rs deleted file mode 100644 index dc2095c..0000000 --- a/src/retry/raw_deserializer.rs +++ /dev/null @@ -1,248 +0,0 @@ -// Copyright 2017 CoreOS, Inc. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -use std::io::Read; - -use std::result; - -use serde::de::{self, DeserializeOwned, Visitor}; - -use crate::errors::*; - -pub struct RawDeserializer { - s: String, -} - -impl RawDeserializer { - pub fn from_reader(mut r: R) -> Result - where - R: Read, - { - let mut s = String::new(); - r.read_to_string(&mut s).chain_err(|| "error reading")?; - Ok(RawDeserializer { s }) - } -} - -pub fn from_reader(r: R) -> Result -where - T: DeserializeOwned, - R: Read, -{ - let mut deserializer = RawDeserializer::from_reader(r)?; - Ok(T::deserialize(&mut deserializer).chain_err(|| "error deserializing")?) -} - -impl<'de, 'a> de::Deserializer<'de> for &'a mut RawDeserializer { - type Error = de::value::Error; - - fn deserialize_any(self, visitor: V) -> result::Result - where - V: Visitor<'de>, - { - self.deserialize_string(visitor) - } - - fn deserialize_bool(self, _: V) -> result::Result - where - V: Visitor<'de>, - { - unimplemented!() - } - fn deserialize_i8(self, _: V) -> result::Result - where - V: Visitor<'de>, - { - unimplemented!() - } - fn deserialize_i16(self, _: V) -> result::Result - where - V: Visitor<'de>, - { - unimplemented!() - } - fn deserialize_i32(self, _: V) -> result::Result - where - V: Visitor<'de>, - { - unimplemented!() - } - fn deserialize_i64(self, _: V) -> result::Result - where - V: Visitor<'de>, - { - unimplemented!() - } - fn deserialize_u8(self, _: V) -> result::Result - where - V: Visitor<'de>, - { - unimplemented!() - } - fn deserialize_u16(self, _: V) -> result::Result - where - V: Visitor<'de>, - { - unimplemented!() - } - fn deserialize_u32(self, _: V) -> result::Result - where - V: Visitor<'de>, - { - unimplemented!() - } - fn deserialize_u64(self, _: V) -> result::Result - where - V: Visitor<'de>, - { - unimplemented!() - } - fn deserialize_f32(self, _: V) -> result::Result - where - V: Visitor<'de>, - { - unimplemented!() - } - fn deserialize_f64(self, _: V) -> result::Result - where - V: Visitor<'de>, - { - unimplemented!() - } - fn deserialize_char(self, _: V) -> result::Result - where - V: Visitor<'de>, - { - unimplemented!() - } - fn deserialize_str(self, _: V) -> result::Result - where - V: Visitor<'de>, - { - unimplemented!() - } - fn deserialize_string(self, visitor: V) -> result::Result - where - V: Visitor<'de>, - { - visitor.visit_string(self.s.clone()) - } - fn deserialize_bytes(self, _: V) -> result::Result - where - V: Visitor<'de>, - { - unimplemented!() - } - fn deserialize_byte_buf(self, _: V) -> result::Result - where - V: Visitor<'de>, - { - unimplemented!() - } - fn deserialize_option(self, _: V) -> result::Result - where - V: Visitor<'de>, - { - unimplemented!() - } - fn deserialize_unit(self, _: V) -> result::Result - where - V: Visitor<'de>, - { - unimplemented!() - } - fn deserialize_unit_struct( - self, - _: &'static str, - _: V, - ) -> result::Result - where - V: Visitor<'de>, - { - unimplemented!() - } - fn deserialize_newtype_struct( - self, - _: &'static str, - _: V, - ) -> result::Result - where - V: Visitor<'de>, - { - unimplemented!() - } - fn deserialize_seq(self, _: V) -> result::Result - where - V: Visitor<'de>, - { - unimplemented!() - } - fn deserialize_tuple(self, _: usize, _: V) -> result::Result - where - V: Visitor<'de>, - { - unimplemented!() - } - fn deserialize_tuple_struct( - self, - _: &'static str, - _: usize, - _: V, - ) -> result::Result - where - V: Visitor<'de>, - { - unimplemented!() - } - fn deserialize_map(self, _: V) -> result::Result - where - V: Visitor<'de>, - { - unimplemented!() - } - fn deserialize_struct( - self, - _: &'static str, - _: &'static [&'static str], - _: V, - ) -> result::Result - where - V: Visitor<'de>, - { - unimplemented!() - } - fn deserialize_enum( - self, - _: &'static str, - _: &'static [&'static str], - _: V, - ) -> result::Result - where - V: Visitor<'de>, - { - unimplemented!() - } - fn deserialize_identifier(self, _: V) -> result::Result - where - V: Visitor<'de>, - { - unimplemented!() - } - fn deserialize_ignored_any(self, _: V) -> result::Result - where - V: Visitor<'de>, - { - unimplemented!() - } -} diff --git a/src/util/cmdline.rs b/src/util/cmdline.rs new file mode 100644 index 0000000..bbe6d4a --- /dev/null +++ b/src/util/cmdline.rs @@ -0,0 +1,72 @@ +// Copyright 2018 CoreOS, Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +//! Kernel cmdline parsing - utility functions +//! +//! NOTE(lucab): this is not a complete/correct cmdline parser, as it implements +//! just enough logic to extract the OEM ID value. In particular, it doesn't +//! handle separator quoting/escaping, list of values, and merging of repeated +//! flags. + +// Find OEM ID flag value in cmdline string. +pub fn find_flag_value(flagname: &str, cmdline: &str) -> Option { + // split the contents into elements and keep key-value tuples only. + let params: Vec<(&str, &str)> = cmdline + .split(' ') + .filter_map(|s| { + let kv: Vec<&str> = s.splitn(2, '=').collect(); + match kv.len() { + 2 => Some((kv[0], kv[1])), + _ => None, + } + }) + .collect(); + + // find the oem flag + for (key, val) in params { + if key != flagname { + continue; + } + let bare_val = val.trim(); + if !bare_val.is_empty() { + return Some(bare_val.to_string()); + } + } + None +} + +#[cfg(test)] +mod tests { + use super::*; + #[test] + fn test_find_flag() { + let flagname = "coreos.oem.id"; + let tests = vec![ + ("", None), + ("foo=bar", None), + ("coreos.oem.id", None), + ("coreos.oem.id=", None), + ("coreos.oem.id=\t", None), + ("coreos.oem.id=ec2", Some("ec2".to_string())), + ("coreos.oem.id=\tec2", Some("ec2".to_string())), + ("coreos.oem.id=ec2\n", Some("ec2".to_string())), + ("foo=bar coreos.oem.id=ec2", Some("ec2".to_string())), + ("coreos.oem.id=ec2 foo=bar", Some("ec2".to_string())), + ]; + for (tcase, tres) in tests { + let res = find_flag_value(flagname, tcase); + assert_eq!(res, tres, "failed testcase: '{}'", tcase); + } + } +} diff --git a/src/util/mod.rs b/src/util/mod.rs new file mode 100644 index 0000000..61b6349 --- /dev/null +++ b/src/util/mod.rs @@ -0,0 +1,18 @@ +// Copyright 2017 CoreOS, Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +//! utility functions + +mod cmdline; +pub use self::cmdline::find_flag_value;