use std::{
collections::{BTreeMap, BTreeSet},
fmt,
net::IpAddr,
};
use crate::{
Diagnostic, DiagnosticCode, InventoryFinding, JsonValueKind, ResourceEvidence, ResourceIdentity, ResourceKind,
SensitiveEnvironmentValue,
};
#[derive(Clone, Eq, PartialEq)]
#[non_exhaustive]
pub enum ObservationField<T> {
Absent,
Observed(ObservedValue<T>),
Unavailable,
Malformed,
VersionInapplicable,
NotApplicable,
Unmodelled(UnmodelledFieldId),
}
impl<T> fmt::Debug for ObservationField<T> {
fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
formatter
.debug_tuple("ObservationField")
.field(&match self {
Self::Absent => "absent",
Self::Observed(_) => "observed",
Self::Unavailable => "unavailable",
Self::Malformed => "malformed",
Self::VersionInapplicable => "version_inapplicable",
Self::NotApplicable => "not_applicable",
Self::Unmodelled(id) => id.as_str(),
})
.finish()
}
}
impl<T> ObservationField<T> {
#[must_use]
pub const fn observed(&self) -> Option<&ObservedValue<T>> {
match self {
Self::Observed(value) => Some(value),
_ => None,
}
}
#[must_use]
pub const fn is_observed(&self) -> bool {
matches!(self, Self::Observed(_))
}
#[must_use]
pub const fn is_malformed(&self) -> bool {
matches!(self, Self::Malformed)
}
}
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
#[non_exhaustive]
pub enum ObservationOrigin {
Configured,
Effective,
RuntimeAssigned,
LocalResolution,
}
#[derive(Clone, Eq, PartialEq)]
pub struct ObservedValue<T> {
value: T,
origin: ObservationOrigin,
}
impl<T> fmt::Debug for ObservedValue<T> {
fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
formatter
.debug_struct("ObservedValue")
.field("origin", &self.origin)
.finish_non_exhaustive()
}
}
impl<T> ObservedValue<T> {
#[must_use]
pub const fn new(value: T, origin: ObservationOrigin) -> Self {
Self { value, origin }
}
#[must_use]
pub const fn value(&self) -> &T {
&self.value
}
#[must_use]
pub const fn origin(&self) -> ObservationOrigin {
self.origin
}
}
#[derive(Clone, Copy, Debug, Eq, Ord, PartialEq, PartialOrd)]
#[non_exhaustive]
pub enum UnmodelledFieldId {
ContainerHostConfig,
ContainerSecretGrant,
ContainerConfig,
ContainerNetworkSettings,
ContainerMount,
ContainerTopLevel,
PodMember,
PodInfraConfig,
PodTopLevel,
NetworkSubnet,
NetworkRoute,
NetworkTopLevel,
VolumeTopLevel,
ImageConfig,
ImageTopLevel,
SecretSpec,
SecretTopLevel,
}
impl UnmodelledFieldId {
#[must_use]
pub const fn as_str(self) -> &'static str {
match self {
Self::ContainerHostConfig => "podman.native.container.host-config",
Self::ContainerSecretGrant => "podman.native.container.secret-grant",
Self::ContainerConfig => "podman.native.container.config",
Self::ContainerNetworkSettings => "podman.native.container.network-settings",
Self::ContainerMount => "podman.native.container.mount",
Self::ContainerTopLevel => "podman.native.container.top-level",
Self::PodMember => "podman.native.pod.member",
Self::PodInfraConfig => "podman.native.pod.infra-config",
Self::PodTopLevel => "podman.native.pod.top-level",
Self::NetworkSubnet => "podman.native.network.subnet",
Self::NetworkRoute => "podman.native.network.route",
Self::NetworkTopLevel => "podman.native.network.top-level",
Self::VolumeTopLevel => "podman.native.volume.top-level",
Self::ImageConfig => "podman.native.image.config",
Self::ImageTopLevel => "podman.native.image.top-level",
Self::SecretSpec => "podman.native.secret.spec",
Self::SecretTopLevel => "podman.native.secret.top-level",
}
}
}
#[derive(Clone, Debug, Eq, PartialEq)]
pub struct UnmodelledField {
id: UnmodelledFieldId,
path: String,
json_kind: JsonValueKind,
resource: ResourceIdentity,
evidence: ResourceEvidence,
}
impl UnmodelledField {
#[allow(clippy::too_many_arguments)] pub(crate) fn new(
path: String,
json_kind: JsonValueKind,
resource: ResourceIdentity,
evidence: ResourceEvidence,
) -> Self {
Self {
id: semantic_unmodelled_id(resource.kind(), &path),
path,
json_kind,
resource,
evidence,
}
}
#[must_use]
pub fn id(&self) -> &UnmodelledFieldId {
&self.id
}
#[must_use]
pub fn path(&self) -> &str {
&self.path
}
#[must_use]
pub const fn json_kind(&self) -> JsonValueKind {
self.json_kind
}
#[must_use]
pub fn resource(&self) -> &ResourceIdentity {
&self.resource
}
#[must_use]
pub fn evidence(&self) -> &ResourceEvidence {
&self.evidence
}
}
fn semantic_unmodelled_id(kind: ResourceKind, path: &str) -> UnmodelledFieldId {
match (kind, path) {
(ResourceKind::Container, value) if value.starts_with("$.HostConfig") => UnmodelledFieldId::ContainerHostConfig,
(ResourceKind::Container, value) if value.starts_with("$.Config.Secrets") => {
UnmodelledFieldId::ContainerSecretGrant
}
(ResourceKind::Container, value) if value.starts_with("$.Config") => UnmodelledFieldId::ContainerConfig,
(ResourceKind::Container, value) if value.starts_with("$.NetworkSettings") => {
UnmodelledFieldId::ContainerNetworkSettings
}
(ResourceKind::Container, value) if value.starts_with("$.Mounts") => UnmodelledFieldId::ContainerMount,
(ResourceKind::Pod, value) if value.starts_with("$.Containers") => UnmodelledFieldId::PodMember,
(ResourceKind::Pod, value) if value.starts_with("$.InfraConfig") => UnmodelledFieldId::PodInfraConfig,
(ResourceKind::Network, value) if value.starts_with("$.subnets") => UnmodelledFieldId::NetworkSubnet,
(ResourceKind::Network, value) if value.starts_with("$.routes") => UnmodelledFieldId::NetworkRoute,
(ResourceKind::Image, value) if value.starts_with("$.Config") => UnmodelledFieldId::ImageConfig,
(ResourceKind::Secret, value) if value.starts_with("$.Spec") => UnmodelledFieldId::SecretSpec,
(ResourceKind::Container, _) => UnmodelledFieldId::ContainerTopLevel,
(ResourceKind::Pod, _) => UnmodelledFieldId::PodTopLevel,
(ResourceKind::Network, _) => UnmodelledFieldId::NetworkTopLevel,
(ResourceKind::Volume, _) => UnmodelledFieldId::VolumeTopLevel,
(ResourceKind::Image, _) => UnmodelledFieldId::ImageTopLevel,
(ResourceKind::Secret, _) => UnmodelledFieldId::SecretTopLevel,
}
}
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub enum UnmodelledCompleteness {
Complete,
Incomplete,
}
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
#[non_exhaustive]
pub enum ResourceObservationState {
Complete,
Unavailable,
Malformed,
}
#[derive(Clone, Debug, Eq, PartialEq)]
pub struct ObservationHeader {
identity: ResourceIdentity,
state: ResourceObservationState,
evidence: ResourceEvidence,
findings: Vec<InventoryFinding>,
unmodelled: Vec<UnmodelledField>,
unmodelled_completeness: UnmodelledCompleteness,
}
impl ObservationHeader {
pub(crate) fn complete(
identity: ResourceIdentity,
evidence: ResourceEvidence,
findings: Vec<InventoryFinding>,
unmodelled: Vec<UnmodelledField>,
unmodelled_completeness: UnmodelledCompleteness,
) -> Self {
Self {
identity,
state: ResourceObservationState::Complete,
evidence,
findings,
unmodelled,
unmodelled_completeness,
}
}
pub(crate) fn incomplete(
identity: ResourceIdentity,
evidence: ResourceEvidence,
state: ResourceObservationState,
findings: Vec<InventoryFinding>,
) -> Self {
Self {
identity,
state,
evidence,
findings,
unmodelled: Vec::new(),
unmodelled_completeness: UnmodelledCompleteness::Incomplete,
}
}
#[must_use]
pub fn identity(&self) -> &ResourceIdentity {
&self.identity
}
#[must_use]
pub const fn state(&self) -> ResourceObservationState {
self.state
}
#[must_use]
pub fn evidence(&self) -> &ResourceEvidence {
&self.evidence
}
#[must_use]
pub fn findings(&self) -> &[InventoryFinding] {
&self.findings
}
pub(crate) fn findings_mut(&mut self) -> &mut Vec<InventoryFinding> {
&mut self.findings
}
#[must_use]
pub fn unmodelled_fields(&self) -> &[UnmodelledField] {
&self.unmodelled
}
#[must_use]
pub const fn unmodelled_completeness(&self) -> UnmodelledCompleteness {
self.unmodelled_completeness
}
}
#[derive(Clone, Debug, Eq, PartialEq)]
pub(crate) struct NativeRelationship {
pub(crate) kind: ResourceKind,
pub(crate) references: Vec<String>,
pub(crate) field_paths: Vec<String>,
}
impl NativeRelationship {
pub(crate) fn new(kind: ResourceKind, target_id: impl Into<String>, field_path: impl Into<String>) -> Self {
Self {
kind,
references: vec![target_id.into()],
field_paths: vec![field_path.into()],
}
}
pub(crate) fn coalesced(
kind: ResourceKind,
references: impl IntoIterator<Item = (String, String)>,
) -> Option<Self> {
let mut values = Vec::new();
let mut paths = Vec::new();
for (value, path) in references {
if !values.contains(&value) {
values.push(value);
}
paths.push(path);
}
(!values.is_empty()).then_some(Self {
kind,
references: values,
field_paths: paths,
})
}
}
#[derive(Clone, Debug, Eq, PartialEq)]
pub struct ProtectedEnvironment {
entries: Vec<ProtectedEnvironmentEntry>,
}
impl ProtectedEnvironment {
pub(crate) fn new(entries: Vec<ProtectedEnvironmentEntry>) -> Self {
Self { entries }
}
#[must_use]
pub fn entries(&self) -> &[ProtectedEnvironmentEntry] {
&self.entries
}
}
#[derive(Clone, Debug, Eq, PartialEq)]
pub struct ProtectedEnvironmentEntry {
name: String,
value: ProtectedEnvironmentValue,
}
impl ProtectedEnvironmentEntry {
pub(crate) fn new(name: String, value: ProtectedEnvironmentValue) -> Self {
Self { name, value }
}
#[must_use]
pub fn name(&self) -> &str {
&self.name
}
#[must_use]
pub fn value(&self) -> &ProtectedEnvironmentValue {
&self.value
}
}
#[derive(Clone, Debug, Eq, PartialEq)]
#[non_exhaustive]
pub enum ProtectedEnvironmentValue {
Redacted,
AuthorizedOpaque(SensitiveEnvironmentValue),
}
pub type Labels = BTreeMap<String, String>;
#[derive(Clone, Eq, PartialEq)]
pub struct ConfiguredContainerCommand(Vec<String>);
impl ConfiguredContainerCommand {
pub(crate) const fn new(arguments: Vec<String>) -> Self {
Self(arguments)
}
#[must_use]
pub fn arguments(&self) -> &[String] {
&self.0
}
}
impl fmt::Debug for ConfiguredContainerCommand {
fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
formatter
.debug_struct("ConfiguredContainerCommand")
.field("argument_count", &self.0.len())
.finish()
}
}
#[derive(Clone, Eq, PartialEq)]
pub struct ConfiguredContainerEntrypoint(Vec<String>);
impl ConfiguredContainerEntrypoint {
pub(crate) const fn new(arguments: Vec<String>) -> Self {
Self(arguments)
}
#[must_use]
pub fn arguments(&self) -> &[String] {
&self.0
}
}
impl fmt::Debug for ConfiguredContainerEntrypoint {
fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
formatter
.debug_struct("ConfiguredContainerEntrypoint")
.field("argument_count", &self.0.len())
.finish()
}
}
macro_rules! configured_container_text {
($type:ident, $doc:literal) => {
#[doc = $doc]
#[derive(Clone, Eq, PartialEq)]
pub struct $type(String);
impl $type {
pub(crate) fn new(value: String) -> Self {
Self(value)
}
#[must_use]
pub fn value(&self) -> &str {
&self.0
}
}
impl fmt::Debug for $type {
fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
formatter.write_str(concat!(stringify!($type), "([redacted])"))
}
}
};
}
configured_container_text!(
ConfiguredContainerUser,
"A configured container user from `Config.User`."
);
configured_container_text!(
ConfiguredContainerWorkdir,
"A configured container working directory from `Config.WorkingDir`."
);
configured_container_text!(
ConfiguredContainerHostname,
"A configured container hostname from `Config.Hostname`."
);
#[derive(Clone, Eq, PartialEq)]
pub struct NativeResourceReference {
reference: String,
field_path: String,
}
impl NativeResourceReference {
pub(crate) fn new(reference: String, field_path: String) -> Self {
Self { reference, field_path }
}
#[must_use]
pub fn reference(&self) -> &str {
&self.reference
}
#[must_use]
pub fn field_path(&self) -> &str {
&self.field_path
}
}
impl fmt::Debug for NativeResourceReference {
fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
formatter
.debug_struct("NativeResourceReference")
.field("field_path", &self.field_path)
.finish_non_exhaustive()
}
}
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
#[non_exhaustive]
pub enum ContainerMountKind {
NamedVolume,
Bind,
}
#[derive(Clone, Eq, PartialEq)]
#[non_exhaustive]
pub enum ContainerMountSource {
NamedVolume(String),
LocalBindPath(String),
}
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
#[non_exhaustive]
pub enum ContainerMountSelinuxRelabel {
Shared,
Private,
}
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
#[non_exhaustive]
pub enum AuthoredImageSpellingHint {
MatchesConfiguredImage,
MatchesLocalImageId,
Contradictory,
}
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
#[non_exhaustive]
pub enum AuthoredMountRelabelHint {
Shared {
mount_index: usize,
},
Private {
mount_index: usize,
},
Contradictory {
mount_index: usize,
},
}
#[derive(Clone, Eq, PartialEq)]
pub struct ContainerCreationEvidence {
image: ObservationField<AuthoredImageSpellingHint>,
mount_relabels: ObservationField<Vec<AuthoredMountRelabelHint>>,
}
impl ContainerCreationEvidence {
pub(crate) fn new(
image: ObservationField<AuthoredImageSpellingHint>,
mount_relabels: ObservationField<Vec<AuthoredMountRelabelHint>>,
) -> Self {
Self { image, mount_relabels }
}
#[must_use]
pub const fn image(&self) -> &ObservationField<AuthoredImageSpellingHint> {
&self.image
}
#[must_use]
pub const fn mount_relabels(&self) -> &ObservationField<Vec<AuthoredMountRelabelHint>> {
&self.mount_relabels
}
}
impl fmt::Debug for ContainerCreationEvidence {
fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
formatter
.debug_struct("ContainerCreationEvidence")
.field("image", &self.image)
.field("mount_relabels", &self.mount_relabels)
.finish()
}
}
impl ContainerMountSource {
#[must_use]
pub fn value(&self) -> &str {
match self {
Self::NamedVolume(value) | Self::LocalBindPath(value) => value,
}
}
}
impl fmt::Debug for ContainerMountSource {
fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
let kind = match self {
Self::NamedVolume(_) => "named_volume",
Self::LocalBindPath(_) => "local_bind_path",
};
formatter.debug_tuple("ContainerMountSource").field(&kind).finish()
}
}
#[derive(Clone, Eq, PartialEq)]
pub struct ContainerMountObservation {
kind: ContainerMountKind,
source: ObservationField<ContainerMountSource>,
local_backing_path: ObservationField<String>,
destination: ObservationField<String>,
writable: ObservationField<bool>,
options: ObservationField<Vec<String>>,
selinux_relabel: ObservationField<ContainerMountSelinuxRelabel>,
propagation: ObservationField<String>,
subpath: ObservationField<String>,
}
impl ContainerMountObservation {
#[allow(clippy::too_many_arguments)]
pub(crate) const fn new(
kind: ContainerMountKind,
source: ObservationField<ContainerMountSource>,
local_backing_path: ObservationField<String>,
destination: ObservationField<String>,
writable: ObservationField<bool>,
options: ObservationField<Vec<String>>,
selinux_relabel: ObservationField<ContainerMountSelinuxRelabel>,
propagation: ObservationField<String>,
subpath: ObservationField<String>,
) -> Self {
Self {
kind,
source,
local_backing_path,
destination,
writable,
options,
selinux_relabel,
propagation,
subpath,
}
}
#[must_use]
pub const fn kind(&self) -> ContainerMountKind {
self.kind
}
#[must_use]
pub fn source(&self) -> &ObservationField<ContainerMountSource> {
&self.source
}
#[must_use]
pub fn local_backing_path(&self) -> &ObservationField<String> {
&self.local_backing_path
}
#[must_use]
pub fn destination(&self) -> &ObservationField<String> {
&self.destination
}
#[must_use]
pub fn writable(&self) -> &ObservationField<bool> {
&self.writable
}
#[must_use]
pub fn options(&self) -> &ObservationField<Vec<String>> {
&self.options
}
#[must_use]
pub fn selinux_relabel(&self) -> &ObservationField<ContainerMountSelinuxRelabel> {
&self.selinux_relabel
}
#[must_use]
pub fn propagation(&self) -> &ObservationField<String> {
&self.propagation
}
#[must_use]
pub fn subpath(&self) -> &ObservationField<String> {
&self.subpath
}
}
impl fmt::Debug for ContainerMountObservation {
fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
formatter
.debug_struct("ContainerMountObservation")
.field("kind", &self.kind)
.field("source", &self.source)
.field(
"local_backing_path_state",
&observation_field_state(&self.local_backing_path),
)
.field("destination_state", &observation_field_state(&self.destination))
.field("writable", &self.writable)
.field(
"option_count",
&self.options.observed().map_or(0, |options| options.value().len()),
)
.field("selinux_relabel", &self.selinux_relabel)
.field("propagation_state", &observation_field_state(&self.propagation))
.field("subpath_state", &observation_field_state(&self.subpath))
.finish()
}
}
fn observation_field_state<T>(field: &ObservationField<T>) -> &'static str {
match field {
ObservationField::Observed(_) => "observed",
ObservationField::Absent => "absent",
ObservationField::Unavailable => "unavailable",
ObservationField::Malformed => "malformed",
ObservationField::VersionInapplicable => "version-inapplicable",
ObservationField::NotApplicable => "not-applicable",
ObservationField::Unmodelled(_) => "unmodelled",
}
}
#[derive(Clone, Eq, PartialEq)]
pub struct ContainerSecretReference {
id: Option<NativeResourceReference>,
name: Option<NativeResourceReference>,
}
impl ContainerSecretReference {
pub(crate) const fn new(id: Option<NativeResourceReference>, name: Option<NativeResourceReference>) -> Self {
Self { id, name }
}
#[must_use]
pub fn id(&self) -> Option<&NativeResourceReference> {
self.id.as_ref()
}
#[must_use]
pub fn name(&self) -> Option<&NativeResourceReference> {
self.name.as_ref()
}
}
impl fmt::Debug for ContainerSecretReference {
fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
formatter
.debug_struct("ContainerSecretReference")
.field("has_id", &self.id.is_some())
.field("has_name", &self.name.is_some())
.finish()
}
}
#[derive(Clone, Eq, PartialEq)]
pub struct ContainerSecretGrantObservation {
reference: ObservationField<ContainerSecretReference>,
uid: ObservationField<u32>,
gid: ObservationField<u32>,
mode: ObservationField<u32>,
}
impl ContainerSecretGrantObservation {
pub(crate) const fn new(
reference: ObservationField<ContainerSecretReference>,
uid: ObservationField<u32>,
gid: ObservationField<u32>,
mode: ObservationField<u32>,
) -> Self {
Self {
reference,
uid,
gid,
mode,
}
}
#[must_use]
pub fn reference(&self) -> &ObservationField<ContainerSecretReference> {
&self.reference
}
#[must_use]
pub fn uid(&self) -> &ObservationField<u32> {
&self.uid
}
#[must_use]
pub fn gid(&self) -> &ObservationField<u32> {
&self.gid
}
#[must_use]
pub fn mode(&self) -> &ObservationField<u32> {
&self.mode
}
}
impl fmt::Debug for ContainerSecretGrantObservation {
fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
formatter
.debug_struct("ContainerSecretGrantObservation")
.field("reference", &self.reference)
.field("uid", &self.uid)
.field("gid", &self.gid)
.field("mode", &self.mode)
.finish()
}
}
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
#[non_exhaustive]
pub enum NativeRestartPolicyName {
No,
Always,
OnFailure,
UnlessStopped,
}
#[derive(Clone, Debug, Eq, PartialEq)]
pub struct NativeRestartPolicyObservation {
name: ObservationField<NativeRestartPolicyName>,
maximum_retry_count: ObservationField<u64>,
}
impl NativeRestartPolicyObservation {
pub(crate) const fn new(
name: ObservationField<NativeRestartPolicyName>,
maximum_retry_count: ObservationField<u64>,
) -> Self {
Self {
name,
maximum_retry_count,
}
}
#[must_use]
pub fn name(&self) -> &ObservationField<NativeRestartPolicyName> {
&self.name
}
#[must_use]
pub fn maximum_retry_count(&self) -> &ObservationField<u64> {
&self.maximum_retry_count
}
}
#[derive(Clone, Eq, PartialEq)]
pub struct ProtectedHealthCommand {
arguments: Vec<String>,
}
impl ProtectedHealthCommand {
pub(crate) const fn new(arguments: Vec<String>) -> Self {
Self { arguments }
}
#[must_use]
pub fn argument_count(&self) -> usize {
self.arguments.len()
}
pub fn expose<R>(&self, use_arguments: impl FnOnce(&[String]) -> R) -> R {
use_arguments(&self.arguments)
}
}
impl fmt::Debug for ProtectedHealthCommand {
fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
formatter
.debug_struct("ProtectedHealthCommand")
.field("argument_count", &self.arguments.len())
.finish()
}
}
impl fmt::Display for ProtectedHealthCommand {
fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
formatter.write_str("[redacted]")
}
}
#[derive(Clone, Debug, Eq, PartialEq)]
#[non_exhaustive]
pub enum NativeHealthCommand {
Disabled,
Shell(ProtectedHealthCommand),
Exec(ProtectedHealthCommand),
}
#[derive(Clone, Debug, Eq, PartialEq)]
pub struct NativeHealthCheckObservation {
command: ObservationField<NativeHealthCommand>,
interval: ObservationField<i64>,
timeout: ObservationField<i64>,
retries: ObservationField<u64>,
start_period: ObservationField<i64>,
}
impl NativeHealthCheckObservation {
pub(crate) const fn new(
command: ObservationField<NativeHealthCommand>,
interval: ObservationField<i64>,
timeout: ObservationField<i64>,
retries: ObservationField<u64>,
start_period: ObservationField<i64>,
) -> Self {
Self {
command,
interval,
timeout,
retries,
start_period,
}
}
#[must_use]
pub fn command(&self) -> &ObservationField<NativeHealthCommand> {
&self.command
}
#[must_use]
pub fn interval(&self) -> &ObservationField<i64> {
&self.interval
}
#[must_use]
pub fn timeout(&self) -> &ObservationField<i64> {
&self.timeout
}
#[must_use]
pub fn retries(&self) -> &ObservationField<u64> {
&self.retries
}
#[must_use]
pub fn start_period(&self) -> &ObservationField<i64> {
&self.start_period
}
}
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
#[non_exhaustive]
pub enum NativeHealthFailureAction {
None,
Kill,
Restart,
Stop,
}
#[derive(Clone, Debug, Eq, PartialEq)]
pub struct NativeStartupHealthCheckObservation {
command: ObservationField<NativeHealthCommand>,
interval: ObservationField<i64>,
timeout: ObservationField<i64>,
retries: ObservationField<u64>,
start_period: ObservationField<i64>,
successes: ObservationField<u64>,
}
impl NativeStartupHealthCheckObservation {
pub(crate) const fn new(
command: ObservationField<NativeHealthCommand>,
interval: ObservationField<i64>,
timeout: ObservationField<i64>,
retries: ObservationField<u64>,
start_period: ObservationField<i64>,
successes: ObservationField<u64>,
) -> Self {
Self {
command,
interval,
timeout,
retries,
start_period,
successes,
}
}
#[must_use]
pub fn command(&self) -> &ObservationField<NativeHealthCommand> {
&self.command
}
#[must_use]
pub fn interval(&self) -> &ObservationField<i64> {
&self.interval
}
#[must_use]
pub fn timeout(&self) -> &ObservationField<i64> {
&self.timeout
}
#[must_use]
pub fn retries(&self) -> &ObservationField<u64> {
&self.retries
}
#[must_use]
pub fn start_period(&self) -> &ObservationField<i64> {
&self.start_period
}
#[must_use]
pub fn successes(&self) -> &ObservationField<u64> {
&self.successes
}
}
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
#[non_exhaustive]
pub enum NativeLogDriver {
Journald,
K8sFile,
}
#[derive(Clone, Debug, Eq, PartialEq)]
pub struct NativeLoggingObservation {
driver: ObservationField<NativeLogDriver>,
size: ObservationField<String>,
}
impl NativeLoggingObservation {
pub(crate) const fn new(driver: ObservationField<NativeLogDriver>, size: ObservationField<String>) -> Self {
Self { driver, size }
}
#[must_use]
pub fn driver(&self) -> &ObservationField<NativeLogDriver> {
&self.driver
}
#[must_use]
pub fn size(&self) -> &ObservationField<String> {
&self.size
}
}
#[derive(Clone, Debug, Eq, PartialEq)]
pub struct NativeCapability(String);
impl NativeCapability {
pub(crate) fn new(value: String) -> Self {
Self(value)
}
#[must_use]
pub fn as_str(&self) -> &str {
&self.0
}
}
#[derive(Clone, Debug, Eq, PartialEq)]
pub struct NativeOpaqueSecurityOptions {
count: usize,
}
impl NativeOpaqueSecurityOptions {
pub(crate) const fn new(count: usize) -> Self {
Self { count }
}
#[must_use]
pub const fn len(&self) -> usize {
self.count
}
#[must_use]
pub const fn is_empty(&self) -> bool {
self.count == 0
}
}
#[derive(Clone, Debug, Eq, PartialEq)]
pub struct NativeSecurityObservation {
privileged: ObservationField<bool>,
cap_add: ObservationField<Vec<NativeCapability>>,
cap_drop: ObservationField<Vec<NativeCapability>>,
security_options: ObservationField<NativeOpaqueSecurityOptions>,
read_only_root_filesystem: ObservationField<bool>,
}
impl NativeSecurityObservation {
pub(crate) const fn new(
privileged: ObservationField<bool>,
cap_add: ObservationField<Vec<NativeCapability>>,
cap_drop: ObservationField<Vec<NativeCapability>>,
security_options: ObservationField<NativeOpaqueSecurityOptions>,
read_only_root_filesystem: ObservationField<bool>,
) -> Self {
Self {
privileged,
cap_add,
cap_drop,
security_options,
read_only_root_filesystem,
}
}
#[must_use]
pub fn privileged(&self) -> &ObservationField<bool> {
&self.privileged
}
#[must_use]
pub fn cap_add(&self) -> &ObservationField<Vec<NativeCapability>> {
&self.cap_add
}
#[must_use]
pub fn cap_drop(&self) -> &ObservationField<Vec<NativeCapability>> {
&self.cap_drop
}
#[must_use]
pub fn security_options(&self) -> &ObservationField<NativeOpaqueSecurityOptions> {
&self.security_options
}
#[must_use]
pub fn read_only_root_filesystem(&self) -> &ObservationField<bool> {
&self.read_only_root_filesystem
}
}
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
#[non_exhaustive]
pub enum NativeNamespaceMode {
Private,
Host,
}
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
#[non_exhaustive]
pub enum NativeIpcNamespaceMode {
Private,
Host,
Shareable,
None,
}
#[derive(Clone, Debug, Eq, PartialEq)]
pub struct NativeNamespaceObservation {
pid: ObservationField<NativeNamespaceMode>,
ipc: ObservationField<NativeIpcNamespaceMode>,
uts: ObservationField<NativeNamespaceMode>,
cgroup: ObservationField<NativeNamespaceMode>,
}
impl NativeNamespaceObservation {
pub(crate) const fn new(
pid: ObservationField<NativeNamespaceMode>,
ipc: ObservationField<NativeIpcNamespaceMode>,
uts: ObservationField<NativeNamespaceMode>,
cgroup: ObservationField<NativeNamespaceMode>,
) -> Self {
Self { pid, ipc, uts, cgroup }
}
#[must_use]
pub fn pid(&self) -> &ObservationField<NativeNamespaceMode> {
&self.pid
}
#[must_use]
pub fn ipc(&self) -> &ObservationField<NativeIpcNamespaceMode> {
&self.ipc
}
#[must_use]
pub fn uts(&self) -> &ObservationField<NativeNamespaceMode> {
&self.uts
}
#[must_use]
pub fn cgroup(&self) -> &ObservationField<NativeNamespaceMode> {
&self.cgroup
}
}
#[derive(Clone, Debug, Eq, PartialEq)]
pub struct NativeUlimitObservation {
name: ObservationField<String>,
soft: ObservationField<i64>,
hard: ObservationField<i64>,
}
impl NativeUlimitObservation {
pub(crate) const fn new(
name: ObservationField<String>,
soft: ObservationField<i64>,
hard: ObservationField<i64>,
) -> Self {
Self { name, soft, hard }
}
#[must_use]
pub fn name(&self) -> &ObservationField<String> {
&self.name
}
#[must_use]
pub fn soft(&self) -> &ObservationField<i64> {
&self.soft
}
#[must_use]
pub fn hard(&self) -> &ObservationField<i64> {
&self.hard
}
}
#[derive(Clone, Debug, Eq, PartialEq)]
pub struct NativeResourceControlObservation {
cpu_shares: ObservationField<u64>,
cpu_period: ObservationField<u64>,
cpu_quota: ObservationField<i64>,
memory: ObservationField<i64>,
pids_limit: ObservationField<i64>,
ulimits: ObservationField<Vec<NativeUlimitObservation>>,
}
impl NativeResourceControlObservation {
pub(crate) const fn new(
cpu_shares: ObservationField<u64>,
cpu_period: ObservationField<u64>,
cpu_quota: ObservationField<i64>,
memory: ObservationField<i64>,
pids_limit: ObservationField<i64>,
ulimits: ObservationField<Vec<NativeUlimitObservation>>,
) -> Self {
Self {
cpu_shares,
cpu_period,
cpu_quota,
memory,
pids_limit,
ulimits,
}
}
#[must_use]
pub fn cpu_shares(&self) -> &ObservationField<u64> {
&self.cpu_shares
}
#[must_use]
pub fn cpu_period(&self) -> &ObservationField<u64> {
&self.cpu_period
}
#[must_use]
pub fn cpu_quota(&self) -> &ObservationField<i64> {
&self.cpu_quota
}
#[must_use]
pub fn memory(&self) -> &ObservationField<i64> {
&self.memory
}
#[must_use]
pub fn pids_limit(&self) -> &ObservationField<i64> {
&self.pids_limit
}
#[must_use]
pub fn ulimits(&self) -> &ObservationField<Vec<NativeUlimitObservation>> {
&self.ulimits
}
}
#[derive(Clone, Eq, PartialEq)]
pub struct ContainerObservation {
configured_image: ObservationField<String>,
labels: ObservationField<Labels>,
local_image_id: ObservationField<String>,
relationships: ObservationField<Vec<NativeRelationship>>,
environment: ObservationField<ProtectedEnvironment>,
command: ObservationField<ConfiguredContainerCommand>,
entrypoint: ObservationField<ConfiguredContainerEntrypoint>,
user: ObservationField<ConfiguredContainerUser>,
working_directory: ObservationField<ConfiguredContainerWorkdir>,
hostname: ObservationField<ConfiguredContainerHostname>,
pod_membership: ObservationField<NativeResourceReference>,
native_dependencies: ObservationField<Vec<NativeResourceReference>>,
mounts: ObservationField<Vec<ContainerMountObservation>>,
secret_grants: ObservationField<Vec<ContainerSecretGrantObservation>>,
memory_swappiness: ObservationField<u64>,
infra: ObservationField<bool>,
restart_policy: ObservationField<NativeRestartPolicyObservation>,
health_check: ObservationField<NativeHealthCheckObservation>,
health_failure_action: ObservationField<NativeHealthFailureAction>,
startup_health_check: ObservationField<NativeStartupHealthCheckObservation>,
logging: ObservationField<NativeLoggingObservation>,
security: ObservationField<NativeSecurityObservation>,
namespaces: ObservationField<NativeNamespaceObservation>,
resource_controls: ObservationField<NativeResourceControlObservation>,
networking: ObservationField<NativeNetworkingObservation>,
creation_evidence: ObservationField<ContainerCreationEvidence>,
}
macro_rules! observation_debug {
($type:ty, $($field:ident),+ $(,)?) => {
impl fmt::Debug for $type {
fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
let mut debug = formatter.debug_struct(stringify!($type));
$(debug.field(stringify!($field), &self.$field);)+
debug.finish()
}
}
};
}
observation_debug!(
ContainerObservation,
labels,
configured_image,
local_image_id,
relationships,
environment,
command,
entrypoint,
user,
working_directory,
hostname,
pod_membership,
native_dependencies,
mounts,
secret_grants,
memory_swappiness,
infra,
networking,
restart_policy,
health_check,
health_failure_action,
startup_health_check,
logging,
security,
namespaces,
resource_controls,
);
impl ContainerObservation {
#[allow(clippy::too_many_arguments)] pub(crate) fn new(
labels: ObservationField<Labels>,
configured_image: ObservationField<String>,
local_image_id: ObservationField<String>,
relationships: ObservationField<Vec<NativeRelationship>>,
environment: ObservationField<ProtectedEnvironment>,
command: ObservationField<ConfiguredContainerCommand>,
entrypoint: ObservationField<ConfiguredContainerEntrypoint>,
user: ObservationField<ConfiguredContainerUser>,
working_directory: ObservationField<ConfiguredContainerWorkdir>,
hostname: ObservationField<ConfiguredContainerHostname>,
pod_membership: ObservationField<NativeResourceReference>,
native_dependencies: ObservationField<Vec<NativeResourceReference>>,
mounts: ObservationField<Vec<ContainerMountObservation>>,
secret_grants: ObservationField<Vec<ContainerSecretGrantObservation>>,
memory_swappiness: ObservationField<u64>,
infra: ObservationField<bool>,
restart_policy: ObservationField<NativeRestartPolicyObservation>,
health_check: ObservationField<NativeHealthCheckObservation>,
health_failure_action: ObservationField<NativeHealthFailureAction>,
startup_health_check: ObservationField<NativeStartupHealthCheckObservation>,
logging: ObservationField<NativeLoggingObservation>,
security: ObservationField<NativeSecurityObservation>,
namespaces: ObservationField<NativeNamespaceObservation>,
resource_controls: ObservationField<NativeResourceControlObservation>,
networking: ObservationField<NativeNetworkingObservation>,
creation_evidence: ObservationField<ContainerCreationEvidence>,
) -> Self {
Self {
configured_image,
labels,
local_image_id,
relationships,
environment,
command,
entrypoint,
user,
working_directory,
hostname,
pod_membership,
native_dependencies,
mounts,
secret_grants,
memory_swappiness,
infra,
restart_policy,
health_check,
health_failure_action,
startup_health_check,
logging,
security,
namespaces,
resource_controls,
networking,
creation_evidence,
}
}
#[must_use]
pub fn labels(&self) -> &ObservationField<Labels> {
&self.labels
}
#[must_use]
pub fn configured_image(&self) -> &ObservationField<String> {
&self.configured_image
}
#[must_use]
pub fn local_image_id(&self) -> &ObservationField<String> {
&self.local_image_id
}
#[must_use]
pub fn environment(&self) -> &ObservationField<ProtectedEnvironment> {
&self.environment
}
#[must_use]
pub fn command(&self) -> &ObservationField<ConfiguredContainerCommand> {
&self.command
}
#[must_use]
pub fn entrypoint(&self) -> &ObservationField<ConfiguredContainerEntrypoint> {
&self.entrypoint
}
#[must_use]
pub fn user(&self) -> &ObservationField<ConfiguredContainerUser> {
&self.user
}
#[must_use]
pub fn working_directory(&self) -> &ObservationField<ConfiguredContainerWorkdir> {
&self.working_directory
}
#[must_use]
pub fn hostname(&self) -> &ObservationField<ConfiguredContainerHostname> {
&self.hostname
}
#[must_use]
pub fn pod_membership(&self) -> &ObservationField<NativeResourceReference> {
&self.pod_membership
}
#[must_use]
pub fn native_dependencies(&self) -> &ObservationField<Vec<NativeResourceReference>> {
&self.native_dependencies
}
#[must_use]
pub fn mounts(&self) -> &ObservationField<Vec<ContainerMountObservation>> {
&self.mounts
}
#[must_use]
pub fn creation_evidence(&self) -> &ObservationField<ContainerCreationEvidence> {
&self.creation_evidence
}
#[must_use]
pub fn secret_grants(&self) -> &ObservationField<Vec<ContainerSecretGrantObservation>> {
&self.secret_grants
}
#[must_use]
pub fn memory_swappiness(&self) -> &ObservationField<u64> {
&self.memory_swappiness
}
#[must_use]
pub fn restart_policy(&self) -> &ObservationField<NativeRestartPolicyObservation> {
&self.restart_policy
}
#[must_use]
pub fn health_check(&self) -> &ObservationField<NativeHealthCheckObservation> {
&self.health_check
}
#[must_use]
pub fn health_failure_action(&self) -> &ObservationField<NativeHealthFailureAction> {
&self.health_failure_action
}
#[must_use]
pub fn startup_health_check(&self) -> &ObservationField<NativeStartupHealthCheckObservation> {
&self.startup_health_check
}
#[must_use]
pub fn logging(&self) -> &ObservationField<NativeLoggingObservation> {
&self.logging
}
#[must_use]
pub fn security(&self) -> &ObservationField<NativeSecurityObservation> {
&self.security
}
#[must_use]
pub fn namespaces(&self) -> &ObservationField<NativeNamespaceObservation> {
&self.namespaces
}
#[must_use]
pub fn resource_controls(&self) -> &ObservationField<NativeResourceControlObservation> {
&self.resource_controls
}
#[must_use]
pub fn infra(&self) -> &ObservationField<bool> {
&self.infra
}
#[must_use]
pub fn networking(&self) -> &ObservationField<NativeNetworkingObservation> {
&self.networking
}
pub(crate) fn relationships(&self) -> &ObservationField<Vec<NativeRelationship>> {
&self.relationships
}
}
#[derive(Clone, Eq, PartialEq)]
pub struct PodObservation {
labels: ObservationField<Labels>,
relationships: ObservationField<Vec<NativeRelationship>>,
create_infra: ObservationField<bool>,
networking: ObservationField<NativeNetworkingObservation>,
}
observation_debug!(PodObservation, labels, relationships, create_infra, networking);
impl PodObservation {
pub(crate) fn new(
labels: ObservationField<Labels>,
relationships: ObservationField<Vec<NativeRelationship>>,
create_infra: ObservationField<bool>,
networking: ObservationField<NativeNetworkingObservation>,
) -> Self {
Self {
labels,
relationships,
create_infra,
networking,
}
}
#[must_use]
pub fn labels(&self) -> &ObservationField<Labels> {
&self.labels
}
#[must_use]
pub fn create_infra(&self) -> &ObservationField<bool> {
&self.create_infra
}
#[must_use]
pub fn networking(&self) -> &ObservationField<NativeNetworkingObservation> {
&self.networking
}
pub(crate) fn relationships(&self) -> &ObservationField<Vec<NativeRelationship>> {
&self.relationships
}
}
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
#[non_exhaustive]
pub enum NativePortProtocol {
Tcp,
Udp,
Sctp,
}
#[derive(Clone, Debug, Eq, PartialEq)]
pub struct NativePortBindingObservation {
container_port: u16,
protocol: NativePortProtocol,
host_ip: ObservationField<IpAddr>,
host_port: ObservationField<u16>,
}
impl NativePortBindingObservation {
pub(crate) const fn new(
container_port: u16,
protocol: NativePortProtocol,
host_ip: ObservationField<IpAddr>,
host_port: ObservationField<u16>,
) -> Self {
Self {
container_port,
protocol,
host_ip,
host_port,
}
}
#[must_use]
pub const fn container_port(&self) -> u16 {
self.container_port
}
#[must_use]
pub const fn protocol(&self) -> NativePortProtocol {
self.protocol
}
#[must_use]
pub fn host_ip(&self) -> &ObservationField<IpAddr> {
&self.host_ip
}
#[must_use]
pub fn host_port(&self) -> &ObservationField<u16> {
&self.host_port
}
}
#[derive(Clone, Debug, Eq, PartialEq)]
pub struct NativeOpaqueNetworkOptions {
count: usize,
}
impl NativeOpaqueNetworkOptions {
pub(crate) const fn new(count: usize) -> Self {
Self { count }
}
#[must_use]
pub const fn len(&self) -> usize {
self.count
}
#[must_use]
pub const fn is_empty(&self) -> bool {
self.count == 0
}
}
#[derive(Clone, Debug, Eq, PartialEq)]
pub struct NativeNetworkingObservation {
port_bindings: ObservationField<Vec<NativePortBindingObservation>>,
create_net_ns: ObservationField<bool>,
host_network: ObservationField<bool>,
dns_servers: ObservationField<Vec<IpAddr>>,
dns_search: ObservationField<Vec<String>>,
dns_options: ObservationField<Vec<String>>,
host_entries: ObservationField<NativeOpaqueNetworkOptions>,
networks: ObservationField<Vec<NativeResourceReference>>,
network_options: ObservationField<NativeOpaqueNetworkOptions>,
no_manage_resolv_conf: ObservationField<bool>,
no_manage_hosts: ObservationField<bool>,
static_ip: ObservationField<IpAddr>,
static_mac: ObservationField<String>,
}
impl NativeNetworkingObservation {
#[allow(clippy::too_many_arguments)] pub(crate) fn new(
port_bindings: ObservationField<Vec<NativePortBindingObservation>>,
create_net_ns: ObservationField<bool>,
host_network: ObservationField<bool>,
dns_servers: ObservationField<Vec<IpAddr>>,
dns_search: ObservationField<Vec<String>>,
dns_options: ObservationField<Vec<String>>,
host_entries: ObservationField<NativeOpaqueNetworkOptions>,
networks: ObservationField<Vec<NativeResourceReference>>,
network_options: ObservationField<NativeOpaqueNetworkOptions>,
no_manage_resolv_conf: ObservationField<bool>,
no_manage_hosts: ObservationField<bool>,
static_ip: ObservationField<IpAddr>,
static_mac: ObservationField<String>,
) -> Self {
Self {
port_bindings,
create_net_ns,
host_network,
dns_servers,
dns_search,
dns_options,
host_entries,
networks,
network_options,
no_manage_resolv_conf,
no_manage_hosts,
static_ip,
static_mac,
}
}
#[must_use]
pub fn port_bindings(&self) -> &ObservationField<Vec<NativePortBindingObservation>> {
&self.port_bindings
}
#[must_use]
pub fn create_net_ns(&self) -> &ObservationField<bool> {
&self.create_net_ns
}
#[must_use]
pub fn host_network(&self) -> &ObservationField<bool> {
&self.host_network
}
#[must_use]
pub fn dns_servers(&self) -> &ObservationField<Vec<IpAddr>> {
&self.dns_servers
}
#[must_use]
pub fn dns_search(&self) -> &ObservationField<Vec<String>> {
&self.dns_search
}
#[must_use]
pub fn dns_options(&self) -> &ObservationField<Vec<String>> {
&self.dns_options
}
#[must_use]
pub fn host_entries(&self) -> &ObservationField<NativeOpaqueNetworkOptions> {
&self.host_entries
}
#[must_use]
pub fn networks(&self) -> &ObservationField<Vec<NativeResourceReference>> {
&self.networks
}
#[must_use]
pub fn network_options(&self) -> &ObservationField<NativeOpaqueNetworkOptions> {
&self.network_options
}
#[must_use]
pub fn no_manage_resolv_conf(&self) -> &ObservationField<bool> {
&self.no_manage_resolv_conf
}
#[must_use]
pub fn no_manage_hosts(&self) -> &ObservationField<bool> {
&self.no_manage_hosts
}
#[must_use]
pub fn static_ip(&self) -> &ObservationField<IpAddr> {
&self.static_ip
}
#[must_use]
pub fn static_mac(&self) -> &ObservationField<String> {
&self.static_mac
}
}
#[derive(Clone, Eq, PartialEq)]
pub struct NetworkObservation {
labels: ObservationField<Labels>,
internal: ObservationField<bool>,
options: ObservationField<NetworkOptionKeys>,
subnets: ObservationField<Vec<NativeNetworkSubnetObservation>>,
routes: ObservationField<Vec<NativeNetworkRouteObservation>>,
}
impl NetworkObservation {
pub(crate) fn new(
labels: ObservationField<Labels>,
internal: ObservationField<bool>,
options: ObservationField<NetworkOptionKeys>,
subnets: ObservationField<Vec<NativeNetworkSubnetObservation>>,
routes: ObservationField<Vec<NativeNetworkRouteObservation>>,
) -> Self {
Self {
labels,
internal,
options,
subnets,
routes,
}
}
#[must_use]
pub fn labels(&self) -> &ObservationField<Labels> {
&self.labels
}
#[must_use]
pub fn internal(&self) -> &ObservationField<bool> {
&self.internal
}
#[must_use]
pub fn options(&self) -> &ObservationField<NetworkOptionKeys> {
&self.options
}
#[must_use]
pub fn subnets(&self) -> &ObservationField<Vec<NativeNetworkSubnetObservation>> {
&self.subnets
}
#[must_use]
pub fn routes(&self) -> &ObservationField<Vec<NativeNetworkRouteObservation>> {
&self.routes
}
}
observation_debug!(NetworkObservation, labels, internal, options, subnets, routes);
#[derive(Clone, Debug, Eq, PartialEq)]
pub struct NativeNetworkCidr {
spelling: String,
network: IpAddr,
prefix: u8,
}
impl NativeNetworkCidr {
pub(crate) fn parse(spelling: String) -> Option<Self> {
let (network, prefix) = spelling.split_once('/')?;
let network = network.parse::<IpAddr>().ok()?;
let prefix = prefix.parse::<u8>().ok()?;
(prefix <= if network.is_ipv4() { 32 } else { 128 }).then_some(Self {
spelling,
network,
prefix,
})
}
#[must_use]
pub fn as_str(&self) -> &str {
&self.spelling
}
#[must_use]
pub(crate) fn contains(&self, address: IpAddr) -> bool {
self.network.is_ipv4() == address.is_ipv4()
&& native_masked_address(self.network, self.prefix) == native_masked_address(address, self.prefix)
}
#[must_use]
pub(crate) const fn has_address_family(&self, address: IpAddr) -> bool {
self.network.is_ipv4() == address.is_ipv4()
}
}
#[derive(Clone, Debug, Eq, PartialEq)]
pub struct NativeNetworkLeaseRange {
start_ip: ObservationField<IpAddr>,
end_ip: ObservationField<IpAddr>,
}
impl NativeNetworkLeaseRange {
pub(crate) const fn new(start_ip: ObservationField<IpAddr>, end_ip: ObservationField<IpAddr>) -> Self {
Self { start_ip, end_ip }
}
#[must_use]
pub const fn start_ip(&self) -> &ObservationField<IpAddr> {
&self.start_ip
}
#[must_use]
pub const fn end_ip(&self) -> &ObservationField<IpAddr> {
&self.end_ip
}
}
#[derive(Clone, Debug, Eq, PartialEq)]
pub struct NativeNetworkSubnetObservation {
cidr: ObservationField<NativeNetworkCidr>,
gateway: ObservationField<IpAddr>,
lease_range: ObservationField<NativeNetworkLeaseRange>,
}
impl NativeNetworkSubnetObservation {
pub(crate) const fn new(
cidr: ObservationField<NativeNetworkCidr>,
gateway: ObservationField<IpAddr>,
lease_range: ObservationField<NativeNetworkLeaseRange>,
) -> Self {
Self {
cidr,
gateway,
lease_range,
}
}
#[must_use]
pub fn cidr(&self) -> &ObservationField<NativeNetworkCidr> {
&self.cidr
}
#[must_use]
pub fn gateway(&self) -> &ObservationField<IpAddr> {
&self.gateway
}
#[must_use]
pub fn lease_range(&self) -> &ObservationField<NativeNetworkLeaseRange> {
&self.lease_range
}
}
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
#[non_exhaustive]
pub enum NativeNetworkRouteType {
Unicast,
Blackhole,
Unreachable,
Prohibit,
}
#[derive(Clone, Debug, Eq, PartialEq)]
pub struct NativeNetworkRouteObservation {
destination: ObservationField<NativeNetworkCidr>,
gateway: ObservationField<IpAddr>,
metric: ObservationField<u32>,
route_type: ObservationField<NativeNetworkRouteType>,
}
impl NativeNetworkRouteObservation {
pub(crate) const fn new(
destination: ObservationField<NativeNetworkCidr>,
gateway: ObservationField<IpAddr>,
metric: ObservationField<u32>,
route_type: ObservationField<NativeNetworkRouteType>,
) -> Self {
Self {
destination,
gateway,
metric,
route_type,
}
}
#[must_use]
pub fn destination(&self) -> &ObservationField<NativeNetworkCidr> {
&self.destination
}
#[must_use]
pub fn gateway(&self) -> &ObservationField<IpAddr> {
&self.gateway
}
#[must_use]
pub fn metric(&self) -> &ObservationField<u32> {
&self.metric
}
#[must_use]
pub fn route_type(&self) -> &ObservationField<NativeNetworkRouteType> {
&self.route_type
}
}
fn native_masked_address(address: IpAddr, prefix: u8) -> IpAddr {
match address {
IpAddr::V4(address) => {
let mask = if prefix == 0 { 0 } else { u32::MAX << (32 - prefix) };
IpAddr::V4(std::net::Ipv4Addr::from(u32::from(address) & mask))
}
IpAddr::V6(address) => {
let mask = if prefix == 0 { 0 } else { u128::MAX << (128 - prefix) };
IpAddr::V6(std::net::Ipv6Addr::from(u128::from(address) & mask))
}
}
}
#[derive(Clone, Eq, Ord, PartialEq, PartialOrd)]
pub struct NetworkOptionKeys(BTreeSet<String>);
impl NetworkOptionKeys {
pub(crate) fn new(keys: impl IntoIterator<Item = String>) -> Self {
Self(keys.into_iter().collect())
}
pub fn keys(&self) -> impl Iterator<Item = &str> {
self.0.iter().map(String::as_str)
}
#[must_use]
pub fn len(&self) -> usize {
self.0.len()
}
#[must_use]
pub fn is_empty(&self) -> bool {
self.0.is_empty()
}
}
impl fmt::Debug for NetworkOptionKeys {
fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
formatter
.debug_struct("NetworkOptionKeys")
.field("count", &self.len())
.finish()
}
}
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub enum VolumeOwnerIdWireValue {
WireAbsentMayMeanZero,
Explicit(UnixId),
}
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub struct UnixId(u32);
impl UnixId {
pub(crate) const fn new(value: u32) -> Self {
Self(value)
}
#[must_use]
pub const fn get(self) -> u32 {
self.0
}
}
#[derive(Clone, Debug, Eq, PartialEq)]
pub struct NativeTimestamp(String);
impl NativeTimestamp {
pub(crate) fn new(value: String) -> Self {
Self(value)
}
#[must_use]
pub fn as_str(&self) -> &str {
&self.0
}
}
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub struct NativeSecretDriverOptions {
count: usize,
}
impl NativeSecretDriverOptions {
pub(crate) const fn new(count: usize) -> Self {
Self { count }
}
#[must_use]
pub const fn len(&self) -> usize {
self.count
}
#[must_use]
pub const fn is_empty(&self) -> bool {
self.count == 0
}
}
#[derive(Clone, Debug, Eq, PartialEq)]
pub struct NativeSecretDriverObservation {
name: ObservationField<String>,
options: ObservationField<NativeSecretDriverOptions>,
}
impl NativeSecretDriverObservation {
pub(crate) const fn new(
name: ObservationField<String>,
options: ObservationField<NativeSecretDriverOptions>,
) -> Self {
Self { name, options }
}
#[must_use]
pub fn name(&self) -> &ObservationField<String> {
&self.name
}
#[must_use]
pub fn options(&self) -> &ObservationField<NativeSecretDriverOptions> {
&self.options
}
}
#[derive(Clone, Eq, PartialEq)]
pub struct VolumeObservation {
labels: ObservationField<Labels>,
uid: ObservationField<VolumeOwnerIdWireValue>,
gid: ObservationField<VolumeOwnerIdWireValue>,
driver: ObservationField<String>,
created_at: ObservationField<NativeTimestamp>,
anonymous: ObservationField<bool>,
}
observation_debug!(VolumeObservation, labels, uid, gid, driver, created_at, anonymous);
impl VolumeObservation {
pub(crate) fn new(
labels: ObservationField<Labels>,
uid: ObservationField<VolumeOwnerIdWireValue>,
gid: ObservationField<VolumeOwnerIdWireValue>,
driver: ObservationField<String>,
created_at: ObservationField<NativeTimestamp>,
anonymous: ObservationField<bool>,
) -> Self {
Self {
labels,
uid,
gid,
driver,
created_at,
anonymous,
}
}
#[must_use]
pub fn labels(&self) -> &ObservationField<Labels> {
&self.labels
}
#[must_use]
pub fn uid(&self) -> &ObservationField<VolumeOwnerIdWireValue> {
&self.uid
}
#[must_use]
pub fn gid(&self) -> &ObservationField<VolumeOwnerIdWireValue> {
&self.gid
}
#[must_use]
pub fn driver(&self) -> &ObservationField<String> {
&self.driver
}
#[must_use]
pub fn created_at(&self) -> &ObservationField<NativeTimestamp> {
&self.created_at
}
#[must_use]
pub fn anonymous(&self) -> &ObservationField<bool> {
&self.anonymous
}
}
#[derive(Clone, Eq, PartialEq)]
pub struct ImageObservation {
labels: ObservationField<Labels>,
repo_tags: ObservationField<Vec<String>>,
repo_digests: ObservationField<Vec<String>>,
environment: ObservationField<ProtectedEnvironment>,
digest: ObservationField<String>,
created: ObservationField<NativeTimestamp>,
author: ObservationField<String>,
architecture: ObservationField<String>,
operating_system: ObservationField<String>,
manifest_type: ObservationField<String>,
}
pub(crate) struct ImageObservationFields {
pub(crate) labels: ObservationField<Labels>,
pub(crate) repo_tags: ObservationField<Vec<String>>,
pub(crate) repo_digests: ObservationField<Vec<String>>,
pub(crate) environment: ObservationField<ProtectedEnvironment>,
pub(crate) digest: ObservationField<String>,
pub(crate) created: ObservationField<NativeTimestamp>,
pub(crate) author: ObservationField<String>,
pub(crate) architecture: ObservationField<String>,
pub(crate) operating_system: ObservationField<String>,
pub(crate) manifest_type: ObservationField<String>,
}
observation_debug!(
ImageObservation,
labels,
repo_tags,
repo_digests,
environment,
digest,
created,
author,
architecture,
operating_system,
manifest_type
);
impl ImageObservation {
pub(crate) fn new(fields: ImageObservationFields) -> Self {
let ImageObservationFields {
labels,
repo_tags,
repo_digests,
environment,
digest,
created,
author,
architecture,
operating_system,
manifest_type,
} = fields;
Self {
labels,
repo_tags,
repo_digests,
environment,
digest,
created,
author,
architecture,
operating_system,
manifest_type,
}
}
#[must_use]
pub fn labels(&self) -> &ObservationField<Labels> {
&self.labels
}
#[must_use]
pub fn repo_tags(&self) -> &ObservationField<Vec<String>> {
&self.repo_tags
}
#[must_use]
pub fn repo_digests(&self) -> &ObservationField<Vec<String>> {
&self.repo_digests
}
#[must_use]
pub fn environment(&self) -> &ObservationField<ProtectedEnvironment> {
&self.environment
}
#[must_use]
pub fn digest(&self) -> &ObservationField<String> {
&self.digest
}
#[must_use]
pub fn created(&self) -> &ObservationField<NativeTimestamp> {
&self.created
}
#[must_use]
pub fn author(&self) -> &ObservationField<String> {
&self.author
}
#[must_use]
pub fn architecture(&self) -> &ObservationField<String> {
&self.architecture
}
#[must_use]
pub fn operating_system(&self) -> &ObservationField<String> {
&self.operating_system
}
#[must_use]
pub fn manifest_type(&self) -> &ObservationField<String> {
&self.manifest_type
}
}
#[derive(Clone, Eq, PartialEq)]
pub struct SecretObservation {
labels: ObservationField<Labels>,
driver: ObservationField<NativeSecretDriverObservation>,
created_at: ObservationField<NativeTimestamp>,
updated_at: ObservationField<NativeTimestamp>,
}
observation_debug!(SecretObservation, labels, driver, created_at, updated_at);
impl SecretObservation {
pub(crate) fn new(
labels: ObservationField<Labels>,
driver: ObservationField<NativeSecretDriverObservation>,
created_at: ObservationField<NativeTimestamp>,
updated_at: ObservationField<NativeTimestamp>,
) -> Self {
Self {
labels,
driver,
created_at,
updated_at,
}
}
#[must_use]
pub fn labels(&self) -> &ObservationField<Labels> {
&self.labels
}
#[must_use]
pub fn driver(&self) -> &ObservationField<NativeSecretDriverObservation> {
&self.driver
}
#[must_use]
pub fn created_at(&self) -> &ObservationField<NativeTimestamp> {
&self.created_at
}
#[must_use]
pub fn updated_at(&self) -> &ObservationField<NativeTimestamp> {
&self.updated_at
}
}
#[derive(Clone, Eq, PartialEq)]
#[non_exhaustive]
#[allow(clippy::large_enum_variant)] pub enum ResourceDetails {
Container(ContainerObservation),
Pod(PodObservation),
Network(NetworkObservation),
Volume(VolumeObservation),
Image(ImageObservation),
Secret(SecretObservation),
}
impl fmt::Debug for ResourceDetails {
fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Self::Container(value) => formatter
.debug_tuple("ResourceDetails::Container")
.field(value)
.finish(),
Self::Pod(value) => formatter.debug_tuple("ResourceDetails::Pod").field(value).finish(),
Self::Network(value) => formatter.debug_tuple("ResourceDetails::Network").field(value).finish(),
Self::Volume(value) => formatter.debug_tuple("ResourceDetails::Volume").field(value).finish(),
Self::Image(value) => formatter.debug_tuple("ResourceDetails::Image").field(value).finish(),
Self::Secret(value) => formatter.debug_tuple("ResourceDetails::Secret").field(value).finish(),
}
}
}
impl ResourceDetails {
#[must_use]
pub const fn kind(&self) -> ResourceKind {
match self {
Self::Container(_) => ResourceKind::Container,
Self::Pod(_) => ResourceKind::Pod,
Self::Network(_) => ResourceKind::Network,
Self::Volume(_) => ResourceKind::Volume,
Self::Image(_) => ResourceKind::Image,
Self::Secret(_) => ResourceKind::Secret,
}
}
}
#[derive(Clone, Eq, PartialEq)]
pub struct ResourceObservation {
header: ObservationHeader,
details: ResourceDetails,
}
impl ResourceObservation {
pub(crate) fn try_new(header: ObservationHeader, details: ResourceDetails) -> Result<Self, Diagnostic> {
if header.identity().kind() != details.kind() {
return Err(Diagnostic::new(DiagnosticCode::ResourceMalformed));
}
Ok(Self { header, details })
}
pub(crate) fn incomplete(header: ObservationHeader) -> Self {
let details = incomplete_details(header.identity().kind(), header.state());
Self { header, details }
}
#[must_use]
pub fn header(&self) -> &ObservationHeader {
&self.header
}
#[must_use]
pub fn details(&self) -> &ResourceDetails {
&self.details
}
pub(crate) fn header_mut(&mut self) -> &mut ObservationHeader {
&mut self.header
}
pub(crate) fn relationships(&self) -> Option<&ObservationField<Vec<NativeRelationship>>> {
match &self.details {
ResourceDetails::Container(value) => Some(value.relationships()),
ResourceDetails::Pod(value) => Some(value.relationships()),
_ => None,
}
}
pub(crate) fn labels(&self) -> &ObservationField<Labels> {
match &self.details {
ResourceDetails::Container(value) => value.labels(),
ResourceDetails::Pod(value) => value.labels(),
ResourceDetails::Network(value) => value.labels(),
ResourceDetails::Volume(value) => value.labels(),
ResourceDetails::Image(value) => value.labels(),
ResourceDetails::Secret(value) => value.labels(),
}
}
pub(crate) fn image_repo_tags(&self) -> Option<&ObservationField<Vec<String>>> {
match &self.details {
ResourceDetails::Image(value) => Some(value.repo_tags()),
_ => None,
}
}
pub(crate) fn image_repo_digests(&self) -> Option<&ObservationField<Vec<String>>> {
match &self.details {
ResourceDetails::Image(value) => Some(value.repo_digests()),
_ => None,
}
}
}
fn incomplete_field<T>(state: ResourceObservationState) -> ObservationField<T> {
if state == ResourceObservationState::Malformed {
ObservationField::Malformed
} else {
ObservationField::Unavailable
}
}
fn incomplete_details(kind: ResourceKind, state: ResourceObservationState) -> ResourceDetails {
match kind {
ResourceKind::Container => ResourceDetails::Container(ContainerObservation::new(
incomplete_field(state),
incomplete_field(state),
incomplete_field(state),
incomplete_field(state),
incomplete_field(state),
incomplete_field(state),
incomplete_field(state),
incomplete_field(state),
incomplete_field(state),
incomplete_field(state),
incomplete_field(state),
incomplete_field(state),
incomplete_field(state),
incomplete_field(state),
incomplete_field(state),
incomplete_field(state),
incomplete_field(state),
incomplete_field(state),
incomplete_field(state),
incomplete_field(state),
incomplete_field(state),
incomplete_field(state),
incomplete_field(state),
incomplete_field(state),
incomplete_field(state),
incomplete_field(state),
)),
ResourceKind::Pod => ResourceDetails::Pod(PodObservation::new(
incomplete_field(state),
incomplete_field(state),
incomplete_field(state),
incomplete_field(state),
)),
ResourceKind::Network => ResourceDetails::Network(NetworkObservation::new(
incomplete_field(state),
incomplete_field(state),
incomplete_field(state),
incomplete_field(state),
incomplete_field(state),
)),
ResourceKind::Volume => ResourceDetails::Volume(VolumeObservation::new(
incomplete_field(state),
incomplete_field(state),
incomplete_field(state),
incomplete_field(state),
incomplete_field(state),
incomplete_field(state),
)),
ResourceKind::Image => ResourceDetails::Image(ImageObservation::new(ImageObservationFields {
labels: incomplete_field(state),
repo_tags: incomplete_field(state),
repo_digests: incomplete_field(state),
environment: incomplete_field(state),
digest: incomplete_field(state),
created: incomplete_field(state),
author: incomplete_field(state),
architecture: incomplete_field(state),
operating_system: incomplete_field(state),
manifest_type: incomplete_field(state),
})),
ResourceKind::Secret => ResourceDetails::Secret(SecretObservation::new(
incomplete_field(state),
incomplete_field(state),
incomplete_field(state),
incomplete_field(state),
)),
}
}
impl fmt::Debug for ResourceObservation {
fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
formatter
.debug_struct("ResourceObservation")
.field("identity", self.header.identity())
.field("state", &self.header.state())
.field("finding_count", &self.header.findings().len())
.field("unmodelled_field_count", &self.header.unmodelled_fields().len())
.field("detail_kind", &self.details.kind())
.finish()
}
}