-
Notifications
You must be signed in to change notification settings - Fork 519
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
settings: create oci defaults settings extension
- Loading branch information
Showing
7 changed files
with
313 additions
and
0 deletions.
There are no files selected for viewing
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,20 @@ | ||
[package] | ||
name = "settings-extension-oci-defaults" | ||
version = "0.1.0" | ||
authors = ["Gaurav Sharma <[email protected]>"] | ||
license = "Apache-2.0 OR MIT" | ||
edition = "2021" | ||
publish = false | ||
|
||
[dependencies] | ||
env_logger = "0.10" | ||
modeled-types = { path = "../../models/modeled-types", version = "0.1" } | ||
model-derive = { path = "../../models/model-derive", version = "0.1" } | ||
serde = { version = "1", features = ["derive"] } | ||
serde_json = "1" | ||
toml = "0.8" | ||
|
||
[dependencies.bottlerocket-settings-sdk] | ||
git = "https://github.com/bottlerocket-os/bottlerocket-settings-sdk" | ||
tag = "bottlerocket-settings-sdk-v0.1.0-alpha.2" | ||
version = "0.1.0-alpha" |
13 changes: 13 additions & 0 deletions
13
sources/settings-extensions/oci-defaults/oci-defaults.toml
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,13 @@ | ||
[extension] | ||
supported-versions = [ | ||
"v1" | ||
] | ||
default-version = "v1" | ||
|
||
[v1] | ||
[v1.validation.cross-validates] | ||
|
||
[v1.templating] | ||
helpers = [] | ||
|
||
[v1.generation.requires] |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,126 @@ | ||
use serde::de::Error; | ||
use serde::{Deserialize, Deserializer}; | ||
|
||
/// This specifies that any non negative i64 integer, -1, and "unlimited" | ||
/// are the valid resource-limits. The hard-limit set to "unlimited" or -1 | ||
/// and soft-limit set to "unlimited" or -1 are converted to u64::MAX in | ||
/// the spec file for the container runtime which ultimately represents | ||
/// unlimited for that resource | ||
pub(crate) fn deserialize_limit<'de, D>(deserializer: D) -> Result<i64, D::Error> | ||
where | ||
D: Deserializer<'de>, | ||
{ | ||
#[derive(Deserialize)] | ||
#[serde(untagged)] | ||
enum StringOrInt64 { | ||
String(String), | ||
Int(i64), | ||
} | ||
|
||
match StringOrInt64::deserialize(deserializer)? { | ||
StringOrInt64::String(s) => { | ||
if s == "unlimited" { | ||
Ok(-1) | ||
} else { | ||
Err(Error::custom(format!( | ||
"Invalid rlimit {}, expected -1 to {} or \"unlimited\"", | ||
s, | ||
i64::MAX | ||
))) | ||
} | ||
} | ||
StringOrInt64::Int(i) => { | ||
if (-1..=i64::MAX).contains(&i) { | ||
Ok(i) | ||
} else { | ||
Err(Error::custom(format!( | ||
"Invalid rlimit {}, expected -1 to {} or \"unlimited\"", | ||
i, | ||
i64::MAX | ||
))) | ||
} | ||
} | ||
} | ||
} | ||
|
||
#[cfg(test)] | ||
mod oci_default_resource_limit_tests { | ||
use crate::OciDefaultsResourceLimitV1; | ||
|
||
#[test] | ||
fn valid_any_integer_i_64() { | ||
assert!(toml::from_str::<OciDefaultsResourceLimitV1>( | ||
r#" | ||
hard-limit = 200000 | ||
soft-limit = 10000 | ||
"# | ||
) | ||
.is_ok()); | ||
} | ||
|
||
#[test] | ||
fn valid_string_unlimited() { | ||
assert!(toml::from_str::<OciDefaultsResourceLimitV1>( | ||
r#" | ||
hard-limit = 'unlimited' | ||
soft-limit = 10000 | ||
"# | ||
) | ||
.is_ok()); | ||
} | ||
|
||
#[test] | ||
fn valid_integer_i_64_max() { | ||
assert!(toml::from_str::<OciDefaultsResourceLimitV1>( | ||
r#" | ||
hard-limit = 9223372036854775807 | ||
soft-limit = 10000 | ||
"# | ||
) | ||
.is_ok()); | ||
} | ||
|
||
#[test] | ||
fn valid_integer_minus_one() { | ||
assert!(toml::from_str::<OciDefaultsResourceLimitV1>( | ||
r#" | ||
hard-limit = -1 | ||
soft-limit = 10000 | ||
"# | ||
) | ||
.is_ok()); | ||
} | ||
|
||
#[test] | ||
fn invalid_integer_greater_than_i_64_max() { | ||
assert!(toml::from_str::<OciDefaultsResourceLimitV1>( | ||
r#" | ||
hard-limit = 9223372036854775808 | ||
soft-limit = 10000 | ||
"# | ||
) | ||
.is_err()); | ||
} | ||
|
||
#[test] | ||
fn invalid_minus_2() { | ||
assert!(toml::from_str::<OciDefaultsResourceLimitV1>( | ||
r#" | ||
hard-limit = -2 | ||
soft-limit = 10000 | ||
"# | ||
) | ||
.is_err()); | ||
} | ||
|
||
#[test] | ||
fn invalid_string_abc() { | ||
assert!(toml::from_str::<OciDefaultsResourceLimitV1>( | ||
r#" | ||
hard-limit = 'abc' | ||
soft-limit = 10000 | ||
"# | ||
) | ||
.is_err()); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,122 @@ | ||
/// Settings related to orchestrated containers for overriding the OCI runtime spec defaults | ||
mod de; | ||
|
||
use crate::de::deserialize_limit; | ||
use bottlerocket_settings_sdk::{GenerateResult, SettingsModel}; | ||
use model_derive::model; | ||
use modeled_types::{OciDefaultsCapability, OciDefaultsResourceLimitType}; | ||
use serde::{Deserialize, Serialize}; | ||
use std::collections::HashMap; | ||
use std::convert::Infallible; | ||
|
||
///// OCI defaults specifies the default values that will be used in cri-base-json. | ||
#[model(impl_default = true)] | ||
struct OciDefaultsV1 { | ||
capabilities: HashMap<OciDefaultsCapability, bool>, | ||
resource_limits: HashMap<OciDefaultsResourceLimitType, OciDefaultsResourceLimitV1>, | ||
} | ||
|
||
///// The hard and soft limit values for an OCI defaults resource limit. | ||
#[model(add_option = false)] | ||
#[derive(Copy, Clone, Debug, Deserialize, Serialize, Eq, Ord, PartialOrd, PartialEq)] | ||
struct OciDefaultsResourceLimitV1 { | ||
#[serde(deserialize_with = "deserialize_limit")] | ||
hard_limit: i64, | ||
#[serde(deserialize_with = "deserialize_limit")] | ||
soft_limit: i64, | ||
} | ||
|
||
type Result<T> = std::result::Result<T, Infallible>; | ||
|
||
impl SettingsModel for OciDefaultsV1 { | ||
type PartialKind = Self; | ||
type ErrorKind = Infallible; | ||
|
||
fn get_version() -> &'static str { | ||
"v1" | ||
} | ||
|
||
fn set(_current_value: Option<Self>, _target: Self) -> Result<()> { | ||
// Set anything that can be parsed as OciDefaultsV1. | ||
Ok(()) | ||
} | ||
|
||
fn generate( | ||
existing_partial: Option<Self::PartialKind>, | ||
_dependent_settings: Option<serde_json::Value>, | ||
) -> Result<GenerateResult<Self::PartialKind, Self>> { | ||
Ok(GenerateResult::Complete( | ||
existing_partial.unwrap_or_default(), | ||
)) | ||
} | ||
|
||
fn validate(_value: Self, _validated_settings: Option<serde_json::Value>) -> Result<()> { | ||
// OciDefaultsV1 is validated during deserialization. | ||
Ok(()) | ||
} | ||
} | ||
|
||
#[cfg(test)] | ||
mod test { | ||
use super::*; | ||
use serde_json::json; | ||
use std::collections::HashMap; | ||
|
||
#[test] | ||
fn test_generate_oci_defaults() { | ||
assert_eq!( | ||
OciDefaultsV1::generate(None, None), | ||
Ok(GenerateResult::Complete(OciDefaultsV1 { | ||
capabilities: None, | ||
resource_limits: None, | ||
})) | ||
) | ||
} | ||
|
||
#[test] | ||
fn test_serde_oci_defaults() { | ||
let test_json = json!({ | ||
"capabilities": { | ||
"sys-admin": true, | ||
"net-admin": false | ||
}, | ||
"resource-limits": { | ||
"max-cpu-time": { | ||
"hard-limit": 1000, | ||
"soft-limit": 500 | ||
} | ||
} | ||
}); | ||
|
||
let test_json_str = test_json.to_string(); | ||
|
||
let oci_defaults: OciDefaultsV1 = serde_json::from_str(&test_json_str).unwrap(); | ||
|
||
let mut expected_capabilities = HashMap::new(); | ||
expected_capabilities.insert(OciDefaultsCapability::SysAdmin, true); | ||
expected_capabilities.insert(OciDefaultsCapability::NetAdmin, false); | ||
|
||
let mut expected_resource_limits = HashMap::new(); | ||
expected_resource_limits.insert( | ||
OciDefaultsResourceLimitType::MaxCpuTime, | ||
OciDefaultsResourceLimitV1 { | ||
hard_limit: 1000, | ||
soft_limit: 500, | ||
}, | ||
); | ||
|
||
assert_eq!( | ||
oci_defaults, | ||
OciDefaultsV1 { | ||
capabilities: Some(expected_capabilities), | ||
resource_limits: Some(expected_resource_limits), | ||
} | ||
); | ||
|
||
let serialized_json: serde_json::Value = serde_json::to_string(&oci_defaults) | ||
.map(|s| serde_json::from_str(&s).unwrap()) | ||
.unwrap(); | ||
|
||
assert_eq!(serialized_json, test_json); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,18 @@ | ||
use bottlerocket_settings_sdk::{BottlerocketSetting, NullMigratorExtensionBuilder}; | ||
use settings_extension_oci_defaults::OciDefaultsV1; | ||
use std::process::ExitCode; | ||
|
||
fn main() -> ExitCode { | ||
env_logger::init(); | ||
|
||
match NullMigratorExtensionBuilder::with_name("oci-defaults") | ||
.with_models(vec![BottlerocketSetting::<OciDefaultsV1>::model()]) | ||
.build() | ||
{ | ||
Ok(extension) => extension.run(), | ||
Err(e) => { | ||
println!("{}", e); | ||
ExitCode::FAILURE | ||
} | ||
} | ||
} |