diff --git a/Cargo.toml b/Cargo.toml index 6a68f2bc1f..1c328ba6e5 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -30,6 +30,8 @@ serde_json = "1.0.66" quickcheck = { version = "1.0.3", optional = true } derive_builder = "0.20.0" getset = "0.1.1" +strum = "0.26.2" +strum_macros = "0.26.2" [dev-dependencies] tempfile = "3.2.0" diff --git a/src/distribution/error.rs b/src/distribution/error.rs index 1d3e475dd0..166f6551ee 100644 --- a/src/distribution/error.rs +++ b/src/distribution/error.rs @@ -5,13 +5,15 @@ use derive_builder::Builder; use getset::Getters; use serde::{Deserialize, Serialize}; use std::fmt::{self, Display, Formatter}; +use strum_macros::{Display as StrumDisplay, EnumString}; use thiserror::Error; /// The string returned by and ErrorResponse error. pub const ERR_REGISTRY: &str = "distribution: registry returned error"; /// Unique identifier representing error code. -#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)] +#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize, StrumDisplay, EnumString)] +#[strum(serialize_all = "SCREAMING_SNAKE_CASE")] #[serde(rename_all = "SCREAMING_SNAKE_CASE")] pub enum ErrorCode { /// Blob unknown to registry. diff --git a/src/runtime/capability.rs b/src/runtime/capability.rs index dba261bd1d..5cbb27d227 100644 --- a/src/runtime/capability.rs +++ b/src/runtime/capability.rs @@ -4,10 +4,12 @@ use serde::{ }; use std::collections::HashSet; +use strum_macros::{Display, EnumString}; + /// Capabilities is a unique set of Capability values. pub type Capabilities = HashSet; -#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq, Serialize)] +#[derive(Clone, Copy, Debug, EnumString, Eq, Display, Hash, PartialEq, Serialize)] /// All available capabilities. /// /// For the purpose of performing permission checks, traditional UNIX @@ -21,6 +23,7 @@ pub type Capabilities = HashSet; /// Starting with kernel 2.2, Linux divides the privileges traditionally /// associated with superuser into distinct units, known as capabilities, which /// can be independently enabled and disabled. Capabilities are a per-thread attribute. +#[strum(serialize_all = "SCREAMING_SNAKE_CASE")] pub enum Capability { #[serde(rename = "CAP_AUDIT_CONTROL")] /// Enable and disable kernel auditing; change auditing filter rules; @@ -609,4 +612,60 @@ mod tests { assert!(res.contains(&Capability::Chown)); Ok(()) } + + #[test] + fn invalid_string2enum() { + let invalid_cap_str = "INVALID_CAP"; + let unknown_cap = invalid_cap_str.parse::(); + assert!(unknown_cap.is_err()); + } + + #[test] + fn cap_enum_to_string() { + let cap = Capability::AuditControl; + assert_eq!(cap.to_string(), "AUDIT_CONTROL"); + + let cap = Capability::AuditRead; + assert_eq!(cap.to_string(), "AUDIT_READ"); + + let cap = Capability::SysAdmin; + assert_eq!(cap.to_string(), "SYS_ADMIN"); + } + + #[test] + fn cap_string_to_enum() { + let cap_str = "AUDIT_CONTROL"; + let cap_enum: Capability = cap_str.parse().unwrap(); + assert_eq!(cap_enum, Capability::AuditControl); + + let cap_str = "AUDIT_READ"; + let cap_enum: Capability = cap_str.parse().unwrap(); + assert_eq!(cap_enum, Capability::AuditRead); + + let cap_str = "SYS_ADMIN"; + let cap_enum: Capability = cap_str.parse().unwrap(); + assert_eq!(cap_enum, Capability::SysAdmin); + } + + #[test] + fn test_serde_serialization() { + let cap = Capability::AuditControl; + let serialized = serde_json::to_string(&cap).unwrap(); + assert_eq!(serialized, "\"CAP_AUDIT_CONTROL\""); + + let cap = Capability::SysAdmin; + let serialized = serde_json::to_string(&cap).unwrap(); + assert_eq!(serialized, "\"CAP_SYS_ADMIN\""); + } + + #[test] + fn test_serde_deserialization() { + let serialized = "\"CAP_AUDIT_CONTROL\""; + let cap: Capability = serde_json::from_str(serialized).unwrap(); + assert_eq!(cap, Capability::AuditControl); + + let serialized = "\"CAP_SYS_ADMIN\""; + let cap: Capability = serde_json::from_str(serialized).unwrap(); + assert_eq!(cap, Capability::SysAdmin); + } } diff --git a/src/runtime/linux.rs b/src/runtime/linux.rs index 0d11040359..ac357490ff 100644 --- a/src/runtime/linux.rs +++ b/src/runtime/linux.rs @@ -4,6 +4,7 @@ use derive_builder::Builder; use getset::{CopyGetters, Getters, MutGetters, Setters}; use serde::{Deserialize, Serialize}; use std::{collections::HashMap, fmt::Display, path::PathBuf, vec}; +use strum_macros::{Display as StrumDisplay, EnumString}; #[derive( Builder, Clone, Debug, Deserialize, Eq, Getters, MutGetters, Setters, PartialEq, Serialize, @@ -193,7 +194,8 @@ pub struct LinuxIdMapping { size: u32, } -#[derive(Clone, Copy, Debug, Deserialize, Eq, PartialEq, Serialize)] +#[derive(Clone, Copy, Debug, Deserialize, Eq, PartialEq, Serialize, EnumString)] +#[strum(serialize_all = "lowercase")] #[serde(rename_all = "lowercase")] /// Device types pub enum LinuxDeviceType { @@ -796,10 +798,12 @@ pub struct LinuxRdma { hca_objects: Option, } -#[derive(Clone, Copy, Debug, Deserialize, Eq, PartialEq, Serialize, Hash)] +#[derive(Clone, Copy, Debug, Deserialize, Eq, PartialEq, Serialize, Hash, StrumDisplay)] +#[strum(serialize_all = "lowercase")] #[serde(rename_all = "snake_case")] /// Available Linux namespaces. pub enum LinuxNamespaceType { + #[strum(to_string = "mnt")] /// Mount Namespace for isolating mount points Mount = 0x00020000, @@ -818,6 +822,7 @@ pub enum LinuxNamespaceType { /// PID Namespace for isolating process ids Pid = 0x20000000, + #[strum(to_string = "net")] /// Network Namespace for isolating network devices, ports, stacks etc. Network = 0x40000000, @@ -1043,7 +1048,8 @@ pub struct LinuxSeccomp { syscalls: Option>, } -#[derive(Clone, Copy, Debug, Deserialize, Eq, PartialEq, Serialize)] +#[derive(Clone, Copy, Debug, Deserialize, Eq, PartialEq, Serialize, StrumDisplay, EnumString)] +#[strum(serialize_all = "SCREAMING_SNAKE_CASE")] #[serde(rename_all = "SCREAMING_SNAKE_CASE")] #[repr(u32)] /// Available seccomp actions. @@ -1080,7 +1086,8 @@ impl Default for LinuxSeccompAction { } #[allow(clippy::enum_clike_unportable_variant)] -#[derive(Clone, Copy, Debug, Deserialize, Eq, PartialEq, Serialize)] +#[derive(Clone, Copy, Debug, Deserialize, Eq, PartialEq, Serialize, StrumDisplay, EnumString)] +#[strum(serialize_all = "SCREAMING_SNAKE_CASE")] #[serde(rename_all = "SCREAMING_SNAKE_CASE")] /// Available seccomp architectures. pub enum Arch { @@ -1139,7 +1146,8 @@ pub enum Arch { ScmpArchS390x = 0x80000016, } -#[derive(Clone, Copy, Debug, Deserialize, Eq, PartialEq, Serialize)] +#[derive(Clone, Copy, Debug, Deserialize, Eq, PartialEq, Serialize, StrumDisplay, EnumString)] +#[strum(serialize_all = "SCREAMING_SNAKE_CASE")] #[serde(rename_all = "SCREAMING_SNAKE_CASE")] /// Available seccomp filter flags. pub enum LinuxSeccompFilterFlag { @@ -1163,7 +1171,8 @@ pub enum LinuxSeccompFilterFlag { SeccompFilterFlagSpecAllow, } -#[derive(Clone, Copy, Debug, Deserialize, Eq, PartialEq, Serialize)] +#[derive(Clone, Copy, Debug, Deserialize, Eq, PartialEq, Serialize, StrumDisplay, EnumString)] +#[strum(serialize_all = "SCREAMING_SNAKE_CASE")] #[serde(rename_all = "SCREAMING_SNAKE_CASE")] #[repr(u32)] /// The seccomp operator to be used for args. @@ -1378,14 +1387,16 @@ pub struct LinuxPersonality { flags: Option>, } -#[derive(Clone, Copy, Debug, Deserialize, Eq, PartialEq, Serialize)] +#[derive(Clone, Copy, Debug, Deserialize, Eq, PartialEq, Serialize, StrumDisplay, EnumString)] /// Define domain and flags for LinuxPersonality. pub enum LinuxPersonalityDomain { #[serde(rename = "LINUX")] + #[strum(serialize = "LINUX")] /// PerLinux is the standard Linux personality. PerLinux, #[serde(rename = "LINUX32")] + #[strum(serialize = "LINUX32")] /// PerLinux32 sets personality to 32 bit. PerLinux32, } @@ -1465,3 +1476,209 @@ impl Arbitrary for LinuxHugepageLimit { } } } + +#[cfg(test)] +mod tests { + use super::*; + + // LinuxDeviceType test cases + #[test] + fn device_type_enum_to_str() { + let type_a = LinuxDeviceType::A; + assert_eq!(type_a.as_str(), "a"); + + let type_b = LinuxDeviceType::B; + assert_eq!(type_b.as_str(), "b"); + + let type_c = LinuxDeviceType::C; + assert_eq!(type_c.as_str(), "c"); + } + + #[test] + fn device_type_string_to_enum() { + let devtype_str = "a"; + let devtype_enum: LinuxDeviceType = devtype_str.parse().unwrap(); + assert_eq!(devtype_enum, LinuxDeviceType::A); + + let devtype_str = "b"; + let devtype_enum: LinuxDeviceType = devtype_str.parse().unwrap(); + assert_eq!(devtype_enum, LinuxDeviceType::B); + + let devtype_str = "c"; + let devtype_enum: LinuxDeviceType = devtype_str.parse().unwrap(); + assert_eq!(devtype_enum, LinuxDeviceType::C); + + let invalid_devtype_str = "x"; + let unknown_devtype = invalid_devtype_str.parse::(); + assert!(unknown_devtype.is_err()); + } + + // LinuxNamespaceType test cases + #[test] + fn ns_type_enum_to_string() { + let type_a = LinuxNamespaceType::Network; + assert_eq!(type_a.to_string(), "net"); + + let type_b = LinuxNamespaceType::Mount; + assert_eq!(type_b.to_string(), "mnt"); + + let type_c = LinuxNamespaceType::Ipc; + assert_eq!(type_c.to_string(), "ipc"); + } + + #[test] + fn ns_type_string_to_enum() { + let nstype_str = "net"; + let nstype_enum = LinuxNamespaceType::try_from(nstype_str).unwrap(); + assert_eq!(nstype_enum, LinuxNamespaceType::Network); + + let nstype_str = "ipc"; + let nstype_enum = LinuxNamespaceType::try_from(nstype_str).unwrap(); + assert_eq!(nstype_enum, LinuxNamespaceType::Ipc); + + let nstype_str = "cgroup"; + let nstype_enum = LinuxNamespaceType::try_from(nstype_str).unwrap(); + assert_eq!(nstype_enum, LinuxNamespaceType::Cgroup); + + let invalid_nstype_str = "mount"; + let unknown_nstype = LinuxNamespaceType::try_from(invalid_nstype_str); + assert!(unknown_nstype.is_err()); + } + + // LinuxSeccompAction test cases + #[test] + fn seccomp_action_enum_to_string() { + let type_a = LinuxSeccompAction::ScmpActKill; + assert_eq!(type_a.to_string(), "SCMP_ACT_KILL"); + + let type_b = LinuxSeccompAction::ScmpActAllow; + assert_eq!(type_b.to_string(), "SCMP_ACT_ALLOW"); + + let type_c = LinuxSeccompAction::ScmpActNotify; + assert_eq!(type_c.to_string(), "SCMP_ACT_NOTIFY"); + } + + #[test] + fn seccomp_action_string_to_enum() { + let action_str = "SCMP_ACT_KILL"; + let action_enum: LinuxSeccompAction = action_str.parse().unwrap(); + assert_eq!(action_enum, LinuxSeccompAction::ScmpActKill); + + let action_str = "SCMP_ACT_ALLOW"; + let action_enum: LinuxSeccompAction = action_str.parse().unwrap(); + assert_eq!(action_enum, LinuxSeccompAction::ScmpActAllow); + + let action_str = "SCMP_ACT_NOTIFY"; + let action_enum: LinuxSeccompAction = action_str.parse().unwrap(); + assert_eq!(action_enum, LinuxSeccompAction::ScmpActNotify); + + let invalid_action_str = "x"; + let unknown_action = invalid_action_str.parse::(); + assert!(unknown_action.is_err()); + } + + // LinuxSeccomp Arch test cases + #[test] + fn seccomp_arch_enum_to_string() { + let type_a = Arch::ScmpArchX86_64; + assert_eq!(type_a.to_string(), "SCMP_ARCH_X86_64"); + + let type_b = Arch::ScmpArchAarch64; + assert_eq!(type_b.to_string(), "SCMP_ARCH_AARCH64"); + + let type_c = Arch::ScmpArchPpc64le; + assert_eq!(type_c.to_string(), "SCMP_ARCH_PPC64LE"); + } + + #[test] + fn seccomp_arch_string_to_enum() { + let arch_type_str = "SCMP_ARCH_X86_64"; + let arch_type_enum: Arch = arch_type_str.parse().unwrap(); + assert_eq!(arch_type_enum, Arch::ScmpArchX86_64); + + let arch_type_str = "SCMP_ARCH_AARCH64"; + let arch_type_enum: Arch = arch_type_str.parse().unwrap(); + assert_eq!(arch_type_enum, Arch::ScmpArchAarch64); + + let arch_type_str = "SCMP_ARCH_PPC64LE"; + let arch_type_enum: Arch = arch_type_str.parse().unwrap(); + assert_eq!(arch_type_enum, Arch::ScmpArchPpc64le); + + let invalid_arch_str = "x"; + let unknown_arch = invalid_arch_str.parse::(); + assert!(unknown_arch.is_err()); + } + + // LinuxSeccompFilterFlag test cases + #[test] + fn seccomp_filter_flag_enum_to_string() { + let type_a = LinuxSeccompFilterFlag::SeccompFilterFlagLog; + assert_eq!(type_a.to_string(), "SECCOMP_FILTER_FLAG_LOG"); + + let type_b = LinuxSeccompFilterFlag::SeccompFilterFlagTsync; + assert_eq!(type_b.to_string(), "SECCOMP_FILTER_FLAG_TSYNC"); + + let type_c = LinuxSeccompFilterFlag::SeccompFilterFlagSpecAllow; + assert_eq!(type_c.to_string(), "SECCOMP_FILTER_FLAG_SPEC_ALLOW"); + } + + #[test] + fn seccomp_filter_flag_string_to_enum() { + let filter_flag_type_str = "SECCOMP_FILTER_FLAG_LOG"; + let filter_flag_type_enum: LinuxSeccompFilterFlag = filter_flag_type_str.parse().unwrap(); + assert_eq!( + filter_flag_type_enum, + LinuxSeccompFilterFlag::SeccompFilterFlagLog + ); + + let filter_flag_type_str = "SECCOMP_FILTER_FLAG_TSYNC"; + let filter_flag_type_enum: LinuxSeccompFilterFlag = filter_flag_type_str.parse().unwrap(); + assert_eq!( + filter_flag_type_enum, + LinuxSeccompFilterFlag::SeccompFilterFlagTsync + ); + + let filter_flag_type_str = "SECCOMP_FILTER_FLAG_SPEC_ALLOW"; + let filter_flag_type_enum: LinuxSeccompFilterFlag = filter_flag_type_str.parse().unwrap(); + assert_eq!( + filter_flag_type_enum, + LinuxSeccompFilterFlag::SeccompFilterFlagSpecAllow + ); + + let invalid_filter_flag_str = "x"; + let unknown_arch = invalid_filter_flag_str.parse::(); + assert!(unknown_arch.is_err()); + } + + // LinuxSeccompOperator test cases + #[test] + fn seccomp_operator_enum_to_string() { + let type_a = LinuxSeccompOperator::ScmpCmpNe; + assert_eq!(type_a.to_string(), "SCMP_CMP_NE"); + + let type_b = LinuxSeccompOperator::ScmpCmpMaskedEq; + assert_eq!(type_b.to_string(), "SCMP_CMP_MASKED_EQ"); + + let type_c = LinuxSeccompOperator::ScmpCmpGt; + assert_eq!(type_c.to_string(), "SCMP_CMP_GT"); + } + + #[test] + fn seccomp_operator_string_to_enum() { + let seccomp_operator_str = "SCMP_CMP_GT"; + let seccomp_operator_enum: LinuxSeccompOperator = seccomp_operator_str.parse().unwrap(); + assert_eq!(seccomp_operator_enum, LinuxSeccompOperator::ScmpCmpGt); + + let seccomp_operator_str = "SCMP_CMP_NE"; + let seccomp_operator_enum: LinuxSeccompOperator = seccomp_operator_str.parse().unwrap(); + assert_eq!(seccomp_operator_enum, LinuxSeccompOperator::ScmpCmpNe); + + let seccomp_operator_str = "SCMP_CMP_MASKED_EQ"; + let seccomp_operator_enum: LinuxSeccompOperator = seccomp_operator_str.parse().unwrap(); + assert_eq!(seccomp_operator_enum, LinuxSeccompOperator::ScmpCmpMaskedEq); + + let invalid_seccomp_operator_str = "x"; + let unknown_operator = invalid_seccomp_operator_str.parse::(); + assert!(unknown_operator.is_err()); + } +} diff --git a/src/runtime/process.rs b/src/runtime/process.rs index 39f26561d4..9c01116eca 100644 --- a/src/runtime/process.rs +++ b/src/runtime/process.rs @@ -6,6 +6,7 @@ use derive_builder::Builder; use getset::{CopyGetters, Getters, MutGetters, Setters}; use serde::{Deserialize, Serialize}; use std::path::PathBuf; +use strum_macros::{Display as StrumDisplay, EnumString}; #[derive( Builder, @@ -177,7 +178,8 @@ pub struct Box { width: u64, } -#[derive(Clone, Copy, Debug, Deserialize, Eq, PartialEq, Serialize)] +#[derive(Clone, Copy, Debug, Deserialize, Eq, PartialEq, Serialize, StrumDisplay, EnumString)] +#[strum(serialize_all = "SCREAMING_SNAKE_CASE")] #[serde(rename_all = "SCREAMING_SNAKE_CASE")] /// Available rlimit types (see ) pub enum PosixRlimitType { @@ -408,7 +410,8 @@ pub struct LinuxIOPriority { priority: i64, } -#[derive(Clone, Copy, Debug, Deserialize, Eq, PartialEq, Serialize)] +#[derive(Clone, Copy, Debug, Deserialize, Eq, PartialEq, Serialize, StrumDisplay, EnumString)] +#[strum(serialize_all = "SCREAMING_SNAKE_CASE")] #[serde(rename_all = "SCREAMING_SNAKE_CASE")] /// IOPriorityClass represents an I/O scheduling class. pub enum IOPriorityClass { @@ -496,7 +499,8 @@ impl Default for Scheduler { } } -#[derive(Clone, Copy, Debug, Deserialize, Eq, PartialEq, Serialize)] +#[derive(Clone, Copy, Debug, Deserialize, Eq, PartialEq, Serialize, StrumDisplay, EnumString)] +#[strum(serialize_all = "SCREAMING_SNAKE_CASE")] #[serde(rename_all = "SCREAMING_SNAKE_CASE")] /// LinuxSchedulerPolicy represents different scheduling policies used with the Linux Scheduler pub enum LinuxSchedulerPolicy { @@ -523,7 +527,8 @@ impl Default for LinuxSchedulerPolicy { } } -#[derive(Clone, Copy, Debug, Deserialize, Eq, PartialEq, Serialize)] +#[derive(Clone, Copy, Debug, Deserialize, Eq, PartialEq, Serialize, StrumDisplay, EnumString)] +#[strum(serialize_all = "SCREAMING_SNAKE_CASE")] #[serde(rename_all = "SCREAMING_SNAKE_CASE")] /// LinuxSchedulerFlag represents the flags used by the Linux Scheduler. pub enum LinuxSchedulerFlag { @@ -549,3 +554,40 @@ impl Default for LinuxSchedulerFlag { LinuxSchedulerFlag::SchedResetOnFork } } + +#[cfg(test)] +mod tests { + use super::*; + + // PosixRlimitType test cases + #[test] + fn posix_rlimit_type_enum_to_string() { + let type_a = PosixRlimitType::RlimitCpu; + assert_eq!(type_a.to_string(), "RLIMIT_CPU"); + + let type_b = PosixRlimitType::RlimitData; + assert_eq!(type_b.to_string(), "RLIMIT_DATA"); + + let type_c = PosixRlimitType::RlimitNofile; + assert_eq!(type_c.to_string(), "RLIMIT_NOFILE"); + } + + #[test] + fn posix_rlimit_type_string_to_enum() { + let posix_rlimit_type_str = "RLIMIT_CPU"; + let posix_rlimit_type_enum: PosixRlimitType = posix_rlimit_type_str.parse().unwrap(); + assert_eq!(posix_rlimit_type_enum, PosixRlimitType::RlimitCpu); + + let posix_rlimit_type_str = "RLIMIT_DATA"; + let posix_rlimit_type_enum: PosixRlimitType = posix_rlimit_type_str.parse().unwrap(); + assert_eq!(posix_rlimit_type_enum, PosixRlimitType::RlimitData); + + let posix_rlimit_type_str = "RLIMIT_NOFILE"; + let posix_rlimit_type_enum: PosixRlimitType = posix_rlimit_type_str.parse().unwrap(); + assert_eq!(posix_rlimit_type_enum, PosixRlimitType::RlimitNofile); + + let invalid_posix_rlimit_type_str = "x"; + let unknown_rlimit = invalid_posix_rlimit_type_str.parse::(); + assert!(unknown_rlimit.is_err()); + } +}