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

Add /os API #777

Merged
merged 4 commits into from
Feb 26, 2020
Merged
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
13 changes: 13 additions & 0 deletions sources/Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions sources/api/apiserver/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ build = "build.rs"

[dependencies]
actix-web = { version = "1.0.5", default-features = false, features = ["uds"] }
bottlerocket-release = { path = "../../bottlerocket-release" }
libc = "0.2"
log = "0.4"
models = { path = "../../models" }
Expand Down
5 changes: 5 additions & 0 deletions sources/api/apiserver/src/server/error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,11 @@ pub enum Error {
#[snafu(display("Tried to commit with no pending changes"))]
CommitWithNoPending,

#[snafu(display("Unable to get OS release data: {}", source))]
ReleaseData {
source: bottlerocket_release::Error,
},

// =^..^= =^..^= =^..^= =^..^= =^..^= =^..^= =^..^= =^..^= =^..^=

// Controller errors
Expand Down
33 changes: 24 additions & 9 deletions sources/api/apiserver/src/server/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,23 +5,22 @@ mod controller;
mod error;
pub use error::Error;

use crate::datastore::{Committed, FilesystemDataStore, Key, Value};
use actix_web::{error::ResponseError, web, App, HttpRequest, HttpResponse, HttpServer, Responder};
use bottlerocket_release::BottlerocketRelease;
use error::Result;
use log::info;
use model::{ConfigurationFiles, Services, Settings};
use nix::unistd::{chown, Gid};
use snafu::{ensure, OptionExt, ResultExt};
use std::collections::{HashMap, HashSet};
use std::env;
use std::path::Path;
use std::process::Command;
use std::sync;

use crate::datastore::{Committed, FilesystemDataStore, Key, Value};
use error::Result;
use model::{ConfigurationFiles, Services, Settings};

use nix::unistd::{chown, Gid};
use std::fs::set_permissions;
use std::fs::Permissions;
use std::os::unix::fs::PermissionsExt;
use std::path::Path;
use std::process::Command;
use std::sync;

// =^..^= =^..^= =^..^= =^..^= =^..^= =^..^= =^..^= =^..^= =^..^=

Expand Down Expand Up @@ -83,6 +82,10 @@ where
web::post().to(commit_transaction_and_apply),
),
)
.service(
web::scope("/os")
.route("", web::get().to(get_os_info))
)
.service(
web::scope("/metadata")
.route("/affected-services", web::get().to(get_affected_services))
Expand Down Expand Up @@ -240,6 +243,13 @@ fn commit_transaction_and_apply(
Ok(ChangedKeysResponse(changes))
}

// The "os" APIs don't deal with the data store at all, they just read a release field, so we
// handle them here.
fn get_os_info() -> Result<BottlerocketReleaseResponse> {
let br = BottlerocketRelease::new().context(error::ReleaseData)?;
Ok(BottlerocketReleaseResponse(br))
}

tjkirch marked this conversation as resolved.
Show resolved Hide resolved
/// Get the affected services for a list of data keys
fn get_affected_services(
query: web::Query<HashMap<String, String>>,
Expand Down Expand Up @@ -369,6 +379,7 @@ impl ResponseError for error::Error {
SystemdNotifyStatus {} => HttpResponse::InternalServerError(),
SetPermissions { .. } => HttpResponse::InternalServerError(),
SetGroup { .. } => HttpResponse::InternalServerError(),
ReleaseData { .. } => HttpResponse::InternalServerError(),
}
.finish()
}
Expand Down Expand Up @@ -403,6 +414,10 @@ macro_rules! impl_responder_for {
struct SettingsResponse(Settings);
impl_responder_for!(SettingsResponse, self, self.0);

/// This lets us respond from our handler methods with a BottlerocketRelease (or Result<BottlerocketRelease>)
struct BottlerocketReleaseResponse(BottlerocketRelease);
impl_responder_for!(BottlerocketReleaseResponse, self, self.0);

/// This lets us respond from our handler methods with a HashMap (or Result<HashMap>) for metadata
struct MetadataResponse(HashMap<String, Value>);
impl_responder_for!(MetadataResponse, self, self.0);
Expand Down
18 changes: 18 additions & 0 deletions sources/api/openapi.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -176,6 +176,24 @@ paths:
500:
description: "Server error"

/os:
get:
summary: "Get OS information such as version, variant, and architecture"
operationId: "get_os_info"
responses:
200:
description: "Successful request"
content:
application/json:
# The response is a hashmap of string to string. Example:
# { "arch": "x86_64" }
schema:
type: object
additionalProperties:
type: string
500:
description: "Server error"

/metadata/affected-services:
get:
summary: "Get affected services"
Expand Down
2 changes: 2 additions & 0 deletions sources/api/schnauzer/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,8 @@ build = "build.rs"
[dependencies]
apiclient = { path = "../apiclient" }
base64 = "0.11"
bottlerocket-release = { path = "../../bottlerocket-release" }
erased-serde = "0.3"
handlebars = "3.0"
http = "0.1"
log = "0.4"
Expand Down
24 changes: 15 additions & 9 deletions sources/api/schnauzer/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ extern crate log;

mod helpers;

use bottlerocket_release::BottlerocketRelease;
use handlebars::Handlebars;
use serde::de::DeserializeOwned;
use snafu::ResultExt;
Expand Down Expand Up @@ -94,23 +95,28 @@ where

/// Requests all settings from the API and wraps them in a "settings" key, so they can be used as
/// the data source for a handlebars templating call.
pub fn get_settings<P>(socket_path: P) -> Result<HashMap<String, model::Settings>>
pub fn get_settings<P>(socket_path: P) -> Result<HashMap<String, Box<dyn erased_serde::Serialize>>>
where
P: AsRef<Path>,
{
debug!("Querying API for settings data");
let settings: model::Settings =
get_json(socket_path, "/settings", None as Option<(String, String)>)?;
get_json(&socket_path, "/settings", None as Option<(String, String)>)?;

trace!("Settings values: {:?}", settings);

// The following helps satisfy the Handlebars templating library.
// The variables in the templates are prefixed with "settings"
// {{ settings.foo.bar }} so we need to wrap the Settings struct in a map
// with the key "settings".
let mut wrapped_template_keys: HashMap<String, model::Settings> = HashMap::new();
wrapped_template_keys.insert("settings".to_string(), settings);
trace!("Final template keys map: {:?}", &wrapped_template_keys);
debug!("Querying API for OS data");
let br: BottlerocketRelease =
get_json(&socket_path, "/os", None as Option<(String, String)>)?;

trace!("OS values: {:?}", br);

// The following helps satisfy the Handlebars templating library. The variables in the
// templates include a prefix like "settings" or "os", for example {{ settings.foo.bar }}
// so we need to wrap the structs struct in a map with the relevant prefixes.
let mut wrapped_template_keys: HashMap<String, Box<dyn erased_serde::Serialize>> = HashMap::new();
wrapped_template_keys.insert("settings".to_string(), Box::new(settings));
wrapped_template_keys.insert("os".to_string(), Box::new(br));

Ok(wrapped_template_keys)
}
Expand Down
2 changes: 1 addition & 1 deletion sources/api/storewolf/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -131,7 +131,7 @@ type Result<T> = std::result::Result<T, StorewolfError>;
/// Given a base path, create a brand new datastore with the appropriate
/// symlink structure for the desired datastore version.
///
/// If `version` is given, uses it, otherwise pulls version from /etc/os-release.
/// If `version` is given, uses it, otherwise pulls version from bottlerocket-release.
///
/// An example setup for theoretical version 1.5:
/// /path/to/datastore/current
Expand Down
1 change: 1 addition & 0 deletions sources/api/thar-be-settings/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ build = "build.rs"

[dependencies]
apiclient = { path = "../apiclient" }
erased-serde = "0.3"
handlebars = "3.0"
http = "0.1"
itertools = "0.8"
Expand Down
2 changes: 1 addition & 1 deletion sources/api/thar-be-settings/src/config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ pub fn get_config_file_names(services: &model::Services) -> HashSet<String> {
pub fn render_config_files(
registry: &handlebars::Handlebars<'_>,
config_files: model::ConfigurationFiles,
settings: HashMap<String, model::Settings>,
settings: HashMap<String, Box<dyn erased_serde::Serialize>>,
strict: bool,
) -> Result<Vec<RenderedConfigFile>> {
// Go write all the configuration files from template
Expand Down
4 changes: 3 additions & 1 deletion sources/bottlerocket-release/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,11 @@ Current version: 0.1.0

## Background

This library lets you get a BottlerocketRelease struct that represents the data in the /etc/os-release file, or another file you point to.
This library lets you get a BottlerocketRelease struct that represents the data in the standard os-release file, or another file you point to.
The VERSION_ID is returned as a semver::Version for convenience.

The information is pulled at runtime because build_id changes frequently and would cause unnecessary rebuilds.

## Colophon

This text was generated from `README.tpl` using [cargo-readme](https://crates.io/crates/cargo-readme), and includes the rustdoc from `src/lib.rs`.
19 changes: 16 additions & 3 deletions sources/bottlerocket-release/build.rs
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
// Automatically generate README.md from rustdoc.

use std::env;
use std::fs::File;
use std::fs::{self, File};
use std::io::Write;
use std::path::PathBuf;
use std::path::{Path, PathBuf};

fn main() {
fn generate_readme() {
// Check for environment variable "SKIP_README". If it is set,
// skip README generation
if env::var_os("SKIP_README").is_some() {
Expand All @@ -30,3 +30,16 @@ fn main() {
let mut readme = File::create("README.md").unwrap();
readme.write_all(content.as_bytes()).unwrap();
}

fn generate_constants() {
let out_dir = env::var("OUT_DIR").unwrap();
let arch = env::var("CARGO_CFG_TARGET_ARCH").unwrap();
let contents = format!("const ARCH: &str = \"{}\";", arch);
let path = Path::new(&out_dir).join("constants.rs");
fs::write(path, contents).unwrap();
}

fn main() {
generate_readme();
generate_constants();
}
21 changes: 16 additions & 5 deletions sources/bottlerocket-release/src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,26 +1,34 @@
/*!
# Background

This library lets you get a BottlerocketRelease struct that represents the data in the /etc/os-release file, or another file you point to.
This library lets you get a BottlerocketRelease struct that represents the data in the standard os-release file, or another file you point to.
The VERSION_ID is returned as a semver::Version for convenience.

The information is pulled at runtime because build_id changes frequently and would cause unnecessary rebuilds.
*/

const DEFAULT_RELEASE_FILE: &str = "/etc/os-release";
const DEFAULT_RELEASE_FILE: &str = "/usr/lib/os-release";

include!(concat!(env!("OUT_DIR"), "/constants.rs"));

use log::debug;
use semver::Version;
use serde::Deserialize;
use serde::{Deserialize, Serialize};
use snafu::ResultExt;
use std::fs;
use std::path::Path;

/// BottlerocketRelease represents the data found in the release file.
#[derive(Debug, Deserialize, Clone)]
#[derive(Debug, Deserialize, Serialize, Clone)]
pub struct BottlerocketRelease {
// Fields from os-release
pub pretty_name: String,
pub variant_id: String,
pub version_id: Version,
pub build_id: String,
tjkirch marked this conversation as resolved.
Show resolved Hide resolved

// Other system information
pub arch: String,
}

mod error {
Expand Down Expand Up @@ -55,7 +63,7 @@ impl BottlerocketRelease {
let release_data = fs::read_to_string(path).context(error::ReadReleaseFile { path })?;

// Split and process each line
let pairs: Vec<(String, String)> = release_data
let mut pairs: Vec<(String, String)> = release_data
.lines()
.filter_map(|line| {
// Allow for comments
Expand Down Expand Up @@ -84,6 +92,9 @@ impl BottlerocketRelease {
})
.collect();

// Add information from other sources
pairs.push(("arch".to_string(), ARCH.to_string()));

envy::from_iter(pairs).context(error::LoadReleaseData { path })
}
}