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

proto1: update to operate over stdin #36

Open
wants to merge 2 commits into
base: develop
Choose a base branch
from
Open
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
1 change: 1 addition & 0 deletions bottlerocket-settings-sdk/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ ctor = "0.2"
env_logger = "0.10"
log = "0.4"
maplit = "1"
tempfile = { version = "3", default-features = false }

[features]
default = ["extension", "proto1"]
Expand Down
110 changes: 87 additions & 23 deletions bottlerocket-settings-sdk/src/cli/proto1.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
//! Bottlerocket Settings Extension CLI proto1 definition.
#![allow(missing_docs)]
use argh::FromArgs;
use serde::{Deserialize, Serialize};

/// Use Settings Extension CLI protocol proto1.
#[derive(FromArgs, Debug)]
Expand All @@ -9,6 +10,12 @@ pub struct Protocol1 {
/// the command to invoke against the settings extension
#[argh(subcommand)]
pub command: Proto1Command,

#[argh(
option,
description = "file that contains input json for the proto1 command"
)]
pub input_file: Option<input::InputFile>,
}

/// The command to invoke against the settings extension.
Expand Down Expand Up @@ -39,97 +46,154 @@ impl Proto1Command {}
/// Validates that a new setting value can be persisted to the Bottlerocket datastore.
#[derive(FromArgs, Debug)]
#[argh(subcommand, name = "set")]
pub struct SetCommand {
pub struct SetCommand {}

#[derive(Debug, Deserialize, Serialize)]
#[serde(rename_all = "kebab-case")]
pub struct SetArguments {
/// the version of the setting which should be used
#[argh(option)]
pub setting_version: String,

/// the requested value to be set for the incoming setting
#[argh(option)]
pub value: serde_json::Value,

/// the current value of this settings tree
#[argh(option)]
pub current_value: Option<serde_json::Value>,
}

/// Dynamically generates a value for this setting given, possibly from other settings.
#[derive(FromArgs, Debug)]
#[argh(subcommand, name = "generate")]
pub struct GenerateCommand {
pub struct GenerateCommand {}

#[derive(Debug, Deserialize, Serialize)]
#[serde(rename_all = "kebab-case")]
pub struct GenerateArguments {
/// the version of the setting which should be used
#[argh(option)]
pub setting_version: String,

/// a json value containing any partially generated data for this setting
#[argh(option)]
pub existing_partial: Option<serde_json::Value>,

/// a json value containing any requested settings partials needed to generate this one
#[argh(option)]
pub required_settings: Option<serde_json::Value>,
}

/// Validates an incoming setting, possibly cross-validated with other settings.
#[derive(FromArgs, Debug)]
#[argh(subcommand, name = "validate")]
pub struct ValidateCommand {
pub struct ValidateCommand {}

#[derive(Debug, Deserialize, Serialize)]
#[serde(rename_all = "kebab-case")]
pub struct ValidateArguments {
/// the version of the setting which should be used
#[argh(option)]
pub setting_version: String,

/// a json value containing any partially generated data for this setting
#[argh(option)]
pub value: serde_json::Value,

/// a json value containing any requested settings partials needed to generate this one
#[argh(option)]
pub required_settings: Option<serde_json::Value>,
}

/// Migrates a setting value from one version to another.
#[derive(FromArgs, Debug)]
#[argh(subcommand, name = "migrate")]
pub struct MigrateCommand {
pub struct MigrateCommand {}

#[derive(Debug, Deserialize, Serialize)]
#[serde(rename_all = "kebab-case")]
pub struct MigrateArguments {
/// a json value containing the current value of the setting
#[argh(option)]
pub value: serde_json::Value,

/// the version of the settings data being migrated
#[argh(option)]
pub from_version: String,

/// the desired resulting version for the settings data
#[argh(option)]
pub target_version: String,
}

/// Migrates a setting value from one version to all other known versions.
#[derive(FromArgs, Debug)]
#[argh(subcommand, name = "flood-migrate")]
pub struct FloodMigrateCommand {
pub struct FloodMigrateCommand {}

#[derive(Debug, Deserialize, Serialize)]
#[serde(rename_all = "kebab-case")]
pub struct FloodMigrateArguments {
/// a json value containing the current value of the setting
#[argh(option)]
pub value: serde_json::Value,

/// the version of the settings data being migrated
#[argh(option)]
pub from_version: String,
}

/// Executes a template helper to assist in rendering values to a configuration file.
#[derive(FromArgs, Debug)]
#[argh(subcommand, name = "helper")]
pub struct TemplateHelperCommand {
pub struct TemplateHelperCommand {}

#[derive(Debug, Deserialize, Serialize)]
#[serde(rename_all = "kebab-case")]
pub struct TemplateHelperArguments {
/// the version of the setting which should be used
#[argh(option)]
pub setting_version: String,

/// the name of the helper to call
#[argh(option)]
pub helper_name: String,

/// the arguments for the given helper
#[argh(option)]
pub arg: Vec<serde_json::Value>,
}

pub mod input {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would maybe add a doc comment here just for sanity

use core::fmt::Display;
use core::str::FromStr;
use std::convert::Infallible;
use std::path::Path;

#[derive(Debug)]
pub enum InputFile {
Stdin,
NormalFile(String),
}

impl Display for InputFile {
fn fmt(&self, formatter: &mut std::fmt::Formatter) -> Result<(), std::fmt::Error> {
match self {
Self::Stdin => formatter.write_str("stdin"),
Self::NormalFile(filename) => formatter.write_str(&filename),
}
}
}

impl Default for InputFile {
fn default() -> InputFile {
InputFile::Stdin
}
}

impl AsRef<Path> for InputFile {
fn as_ref(&self) -> &Path {
match self {
Self::Stdin => Path::new("/dev/stdin"),
Self::NormalFile(filename) => Path::new(filename),
}
}
}

impl FromStr for InputFile {
type Err = Infallible;

fn from_str(input: &str) -> Result<Self, Self::Err> {
match input {
"/dev/stdin" => Ok(Self::Stdin),
"-" => Ok(Self::Stdin),
"stdin" => Ok(Self::Stdin),
x => Ok(Self::NormalFile(String::from(x))),
}
}
}
}
17 changes: 15 additions & 2 deletions bottlerocket-settings-sdk/src/extension/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -108,7 +108,9 @@ where
debug!(?args, "CLI arguments");

match args.protocol {
cli::Protocol::Proto1(p) => proto1::run_extension(self, p.command),
cli::Protocol::Proto1(p) => {
proto1::run_extension(self, p.command, p.input_file.unwrap_or_default())
}
}
}

Expand Down Expand Up @@ -140,7 +142,9 @@ where
info!(cli_protocol = %args.protocol, "Starting settings extensions.");

match args.protocol {
cli::Protocol::Proto1(p) => proto1::try_run_extension(self, p.command),
cli::Protocol::Proto1(p) => {
proto1::try_run_extension(self, p.command, p.input_file.unwrap_or_default())
}
}
}

Expand Down Expand Up @@ -247,6 +251,15 @@ pub mod error {
#[snafu(display("Failed to parse CLI arguments: {}", parser_output))]
ParseCLIArgs { parser_output: String },

#[snafu(display("Failed to parse to JSON: {}", source))]
ParseJSON { source: serde_json::Error },

#[snafu(display("Failed to read from '{}': {}", filename, source))]
ReadInput {
filename: String,
source: std::io::Error,
},

#[snafu(display("Failed to write settings extension output as JSON: {}", source))]
SerializeResult { source: serde_json::Error },

Expand Down
Loading