ts some minor update HOPEFULLY wont make it run 16k times worse HOPEFULLY
This commit is contained in:
@@ -15,6 +15,7 @@
|
|||||||
# WAKEY_CC_VERSION Bundle version label (default: v0.0.0-local-<short_sha>)
|
# WAKEY_CC_VERSION Bundle version label (default: v0.0.0-local-<short_sha>)
|
||||||
# WAKEY_CC_NO_RESTART If set, skip systemd restart
|
# WAKEY_CC_NO_RESTART If set, skip systemd restart
|
||||||
# WAKEY_CC_KEEP_TMP If set, keep temp checkout/build dir for debugging
|
# WAKEY_CC_KEEP_TMP If set, keep temp checkout/build dir for debugging
|
||||||
|
# SUDO Privilege escalation command/path (default: sudo when needed)
|
||||||
#
|
#
|
||||||
# Requires: git, cargo, tar, pnpm, systemctl (optional for restart)
|
# Requires: git, cargo, tar, pnpm, systemctl (optional for restart)
|
||||||
|
|
||||||
@@ -45,12 +46,31 @@ require_cmd() {
|
|||||||
command -v "$1" >/dev/null 2>&1 || fail "required command not found: $1"
|
command -v "$1" >/dev/null 2>&1 || fail "required command not found: $1"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
sudo_cmd() {
|
||||||
|
if [ "$(id -u)" -eq 0 ]; then
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ -n "${SUDO:-}" ]; then
|
||||||
|
printf '%s' "$SUDO"
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
if command -v sudo >/dev/null 2>&1; then
|
||||||
|
printf '%s' sudo
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
fail 'root privileges are required for install/restart steps; set SUDO or run as root'
|
||||||
|
}
|
||||||
|
|
||||||
main() {
|
main() {
|
||||||
REPO_URL="${WAKEY_CC_REPO_URL:-https://git.ldlda.com/lda/wakey.git}"
|
REPO_URL="${WAKEY_CC_REPO_URL:-https://git.ldlda.com/lda/wakey.git}"
|
||||||
REF="${WAKEY_CC_REF:-main}"
|
REF="${WAKEY_CC_REF:-main}"
|
||||||
TARGET="${WAKEY_CC_TARGET:-$(default_target)}"
|
TARGET="${WAKEY_CC_TARGET:-$(default_target)}"
|
||||||
ROOT="${WAKEY_CC_ROOT:-$PWD}"
|
ROOT="${WAKEY_CC_ROOT:-$PWD}"
|
||||||
SERVICE="${WAKEY_CC_SERVICE:-wakey-cc.service}"
|
SERVICE="${WAKEY_CC_SERVICE:-wakey-cc.service}"
|
||||||
|
SUDO_BIN=$(sudo_cmd)
|
||||||
|
|
||||||
require_cmd git
|
require_cmd git
|
||||||
require_cmd cargo
|
require_cmd cargo
|
||||||
@@ -104,15 +124,26 @@ main() {
|
|||||||
[ -f "$STAGING/bin/wakey-control-plane" ] || fail 'bundle missing bin/wakey-control-plane'
|
[ -f "$STAGING/bin/wakey-control-plane" ] || fail 'bundle missing bin/wakey-control-plane'
|
||||||
[ -f "$STAGING/ui/dist/index.html" ] || fail 'bundle missing ui/dist/index.html'
|
[ -f "$STAGING/ui/dist/index.html" ] || fail 'bundle missing ui/dist/index.html'
|
||||||
|
|
||||||
mkdir -p "$ROOT"
|
if [ "$(id -u)" -ne 0 ] && [ -n "$SUDO_BIN" ]; then
|
||||||
cp -a "$STAGING/." "$ROOT/"
|
$SUDO_BIN mkdir -p "$ROOT"
|
||||||
|
$SUDO_BIN cp -a "$STAGING/." "$ROOT/"
|
||||||
|
else
|
||||||
|
mkdir -p "$ROOT"
|
||||||
|
cp -a "$STAGING/." "$ROOT/"
|
||||||
|
fi
|
||||||
|
|
||||||
if [ -z "${WAKEY_CC_NO_RESTART:-}" ] && command -v systemctl >/dev/null 2>&1; then
|
if [ -z "${WAKEY_CC_NO_RESTART:-}" ] && command -v systemctl >/dev/null 2>&1; then
|
||||||
if systemctl list-unit-files "$SERVICE" >/dev/null 2>&1; then
|
if systemctl list-unit-files "$SERVICE" >/dev/null 2>&1; then
|
||||||
log "restarting $SERVICE"
|
log "restarting $SERVICE"
|
||||||
systemctl daemon-reload
|
if [ "$(id -u)" -ne 0 ] && [ -n "$SUDO_BIN" ]; then
|
||||||
systemctl restart "$SERVICE"
|
$SUDO_BIN systemctl daemon-reload
|
||||||
systemctl --no-pager --full status "$SERVICE" | sed -n '1,16p'
|
$SUDO_BIN systemctl restart "$SERVICE"
|
||||||
|
$SUDO_BIN systemctl --no-pager --full status "$SERVICE" | sed -n '1,16p'
|
||||||
|
else
|
||||||
|
systemctl daemon-reload
|
||||||
|
systemctl restart "$SERVICE"
|
||||||
|
systemctl --no-pager --full status "$SERVICE" | sed -n '1,16p'
|
||||||
|
fi
|
||||||
else
|
else
|
||||||
log "service $SERVICE not installed; skipped restart"
|
log "service $SERVICE not installed; skipped restart"
|
||||||
fi
|
fi
|
||||||
|
|||||||
+2
-3
@@ -3,9 +3,8 @@ pub mod utils;
|
|||||||
|
|
||||||
pub use service::{
|
pub use service::{
|
||||||
broadcast_wake_targets, get_interface_summaries, get_interface_summary, get_ips, get_leases,
|
broadcast_wake_targets, get_interface_summaries, get_interface_summary, get_ips, get_leases,
|
||||||
inventory, leases_without_state, merge_devices, query_to_inventory_query, resolve_devices,
|
inventory, leases_without_state, merge_devices, resolve_devices, resolve_query,
|
||||||
resolve_query, resolve_selector, resolve_wake_targets, wake_explicit, wake_from_query,
|
resolve_selector, resolve_wake_targets, wake_explicit, wake_from_query, wake_targets,
|
||||||
wake_targets,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
|
|||||||
+20
-37
@@ -1,3 +1,5 @@
|
|||||||
|
use std::collections::HashSet;
|
||||||
|
|
||||||
use anyhow::Result;
|
use anyhow::Result;
|
||||||
use wakey_core::{
|
use wakey_core::{
|
||||||
Device, DeviceInventory, DhcpLease, DhcpLeaseWithState, InventoryQuery, NeighborEntry,
|
Device, DeviceInventory, DhcpLease, DhcpLeaseWithState, InventoryQuery, NeighborEntry,
|
||||||
@@ -59,48 +61,29 @@ pub fn merge_devices(
|
|||||||
.map(|(neighbors, leases)| Device::from_parts(neighbors, leases))
|
.map(|(neighbors, leases)| Device::from_parts(neighbors, leases))
|
||||||
.collect();
|
.collect();
|
||||||
|
|
||||||
let mut texts: Vec<&str> = Vec::new();
|
let mut texts = HashSet::new();
|
||||||
let mut devs: Vec<&str> = Vec::new();
|
let mut devs = HashSet::new();
|
||||||
let mut ips = Vec::new();
|
let mut ips = HashSet::new();
|
||||||
let mut macs = Vec::new();
|
let mut macs = HashSet::new();
|
||||||
let mut nuds = Vec::new();
|
let mut nuds = HashSet::new();
|
||||||
|
|
||||||
for term in query {
|
for term in query {
|
||||||
match term {
|
match term {
|
||||||
Query::Text(v) => texts.push(v.as_str()),
|
Query::Text(v) => texts.insert(v.as_str()),
|
||||||
Query::Interface(v) => devs.push(v.as_str()),
|
Query::Interface(v) => devs.insert(v.as_str()),
|
||||||
Query::Ip(v) => ips.push(*v),
|
Query::Ip(v) => ips.insert(*v),
|
||||||
Query::Mac(v) => macs.push(*v),
|
Query::Mac(v) => macs.insert(*v),
|
||||||
Query::NeighborState(v) => nuds.push(*v),
|
Query::NeighborState(v) => nuds.insert(*v),
|
||||||
}
|
};
|
||||||
}
|
|
||||||
|
|
||||||
if !texts.is_empty() {
|
|
||||||
devices.retain(|device| device.names.iter().any(|n| texts.iter().any(|t| n == t)));
|
|
||||||
}
|
|
||||||
if !devs.is_empty() {
|
|
||||||
devices.retain(|device| {
|
|
||||||
device
|
|
||||||
.interfaces
|
|
||||||
.iter()
|
|
||||||
.any(|iface| devs.iter().any(|d| iface == d))
|
|
||||||
});
|
|
||||||
}
|
|
||||||
if !ips.is_empty() {
|
|
||||||
devices.retain(|device| device.ips.iter().any(|ip| ips.contains(ip)));
|
|
||||||
}
|
|
||||||
if !macs.is_empty() {
|
|
||||||
devices.retain(|device| device.macs.iter().any(|mac| macs.contains(mac)));
|
|
||||||
}
|
|
||||||
if !nuds.is_empty() {
|
|
||||||
devices.retain(|device| {
|
|
||||||
device
|
|
||||||
.neighbors
|
|
||||||
.iter()
|
|
||||||
.any(|neighbor| nuds.contains(&neighbor.state))
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
devices.retain(|device| {
|
||||||
|
(texts.is_empty() || device.names.iter().any(|n| texts.contains(n.as_str())))
|
||||||
|
&& (devs.is_empty() || device.interfaces.iter().any(|i| devs.contains(i.as_str())))
|
||||||
|
&& (ips.is_empty() || device.ips.iter().any(|ip| ips.contains(ip)))
|
||||||
|
&& (macs.is_empty() || device.macs.iter().any(|mac| macs.contains(mac)))
|
||||||
|
&& (nuds.is_empty() || device.neighbors.iter().any(|n| nuds.contains(&n.state)))
|
||||||
|
});
|
||||||
devices.sort_by(|a, b| {
|
devices.sort_by(|a, b| {
|
||||||
presence_rank(b.presence)
|
presence_rank(b.presence)
|
||||||
.cmp(&presence_rank(a.presence))
|
.cmp(&presence_rank(a.presence))
|
||||||
|
|||||||
+1
-1
@@ -7,7 +7,7 @@ pub mod wake;
|
|||||||
pub use interfaces::{get_interface_summaries, get_interface_summary, get_ips};
|
pub use interfaces::{get_interface_summaries, get_interface_summary, get_ips};
|
||||||
pub use inventory::{inventory, merge_devices, resolve_devices};
|
pub use inventory::{inventory, merge_devices, resolve_devices};
|
||||||
pub use leases::{get_leases, leases_without_state};
|
pub use leases::{get_leases, leases_without_state};
|
||||||
pub use query::{query_to_inventory_query, resolve_query, resolve_selector};
|
pub use query::{resolve_query, resolve_selector};
|
||||||
pub use wake::{
|
pub use wake::{
|
||||||
broadcast_wake_targets, resolve_wake_targets, wake_explicit, wake_from_query, wake_targets,
|
broadcast_wake_targets, resolve_wake_targets, wake_explicit, wake_from_query, wake_targets,
|
||||||
};
|
};
|
||||||
|
|||||||
+1
-12
@@ -2,11 +2,8 @@ use anyhow::Result;
|
|||||||
use wakey_core::{InventoryQuery, Query, QueryInput};
|
use wakey_core::{InventoryQuery, Query, QueryInput};
|
||||||
|
|
||||||
/// Resolve free-form user input into an `InventoryQuery` filter shape.
|
/// Resolve free-form user input into an `InventoryQuery` filter shape.
|
||||||
///
|
|
||||||
/// This is the compatibility entrypoint used by CLI and HTTP paths that still
|
|
||||||
/// speak in terms of query/filter payloads.
|
|
||||||
pub async fn resolve_query(input: impl Into<String>) -> Result<InventoryQuery> {
|
pub async fn resolve_query(input: impl Into<String>) -> Result<InventoryQuery> {
|
||||||
query_to_inventory_query(resolve_selector(input).await?)
|
Ok(vec![resolve_selector(input).await?])
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Classify one piece of free-form user input into a typed selector.
|
/// Classify one piece of free-form user input into a typed selector.
|
||||||
@@ -24,11 +21,3 @@ pub async fn resolve_selector(input: impl Into<String>) -> Result<Query> {
|
|||||||
},
|
},
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Convert the newer selector-oriented `Query` model into an `InventoryQuery`.
|
|
||||||
///
|
|
||||||
/// This keeps the old filter-based service and HTTP surfaces working while the
|
|
||||||
/// internals migrate toward selector- and device-oriented APIs.
|
|
||||||
pub fn query_to_inventory_query(query: Query) -> Result<InventoryQuery> {
|
|
||||||
Ok(vec![query])
|
|
||||||
}
|
|
||||||
|
|||||||
+9
-7
@@ -6,6 +6,7 @@ use wakey_core::{InterfaceSummary, WakeResult, WakeTarget};
|
|||||||
|
|
||||||
use crate::service::interfaces::get_interface_summaries;
|
use crate::service::interfaces::get_interface_summaries;
|
||||||
use crate::service::inventory::resolve_devices;
|
use crate::service::inventory::resolve_devices;
|
||||||
|
use crate::utils::product;
|
||||||
|
|
||||||
/// Send Wake-on-LAN packets for already-concrete wake targets.
|
/// Send Wake-on-LAN packets for already-concrete wake targets.
|
||||||
#[instrument(skip_all, fields(targets = targets.len()))]
|
#[instrument(skip_all, fields(targets = targets.len()))]
|
||||||
@@ -50,14 +51,15 @@ pub async fn wake_explicit(mac: MacAddr, ip: Option<IpAddr>) -> Result<WakeResul
|
|||||||
#[instrument(skip_all)]
|
#[instrument(skip_all)]
|
||||||
pub async fn resolve_wake_targets(input: impl Into<String>) -> Result<Vec<WakeTarget>> {
|
pub async fn resolve_wake_targets(input: impl Into<String>) -> Result<Vec<WakeTarget>> {
|
||||||
let devices = resolve_devices(input).await?;
|
let devices = resolve_devices(input).await?;
|
||||||
|
|
||||||
let targets: Vec<WakeTarget> = devices
|
let targets: Vec<WakeTarget> = devices
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.flat_map(|device| {
|
.flat_map(|device| {
|
||||||
let mac = device.macs.first().copied();
|
// TRY EVERYTHING some has to work
|
||||||
device
|
product(device.ips, device.macs).map(|(i, m)| WakeTarget {
|
||||||
.ips
|
ip: Some(i),
|
||||||
.into_iter()
|
mac: Some(m),
|
||||||
.map(move |ip| WakeTarget { ip: Some(ip), mac })
|
})
|
||||||
})
|
})
|
||||||
.collect();
|
.collect();
|
||||||
debug!(targets = targets.len(), "resolved wake targets");
|
debug!(targets = targets.len(), "resolved wake targets");
|
||||||
@@ -79,8 +81,8 @@ fn broadcast_wake_targets_from_interfaces(
|
|||||||
.iter()
|
.iter()
|
||||||
.flat_map(|iface| iface.addrs.iter())
|
.flat_map(|iface| iface.addrs.iter())
|
||||||
.filter_map(|addr| addr.broadcast)
|
.filter_map(|addr| addr.broadcast)
|
||||||
.map(|ip| WakeTarget {
|
.map(|br| WakeTarget {
|
||||||
ip: Some(IpAddr::V4(ip)),
|
ip: Some(IpAddr::V4(br)), // some will hit eventually AHHH logic
|
||||||
mac: Some(mac),
|
mac: Some(mac),
|
||||||
})
|
})
|
||||||
.collect();
|
.collect();
|
||||||
|
|||||||
+15
-1
@@ -1 +1,15 @@
|
|||||||
// Intentionally left empty.
|
//! rare fns that belong nowhere
|
||||||
|
|
||||||
|
/// absolute dih cheese
|
||||||
|
/// took some inspo from itertools
|
||||||
|
pub fn product<A, B, T1, T2>(i1: T1, i2: T2) -> impl Iterator<Item = (A, B)>
|
||||||
|
where
|
||||||
|
A: Clone,
|
||||||
|
T1: IntoIterator<Item = A>,
|
||||||
|
T2: IntoIterator<Item = B>,
|
||||||
|
T2::IntoIter: Clone,
|
||||||
|
{
|
||||||
|
let i2 = i2.into_iter();
|
||||||
|
i1.into_iter()
|
||||||
|
.flat_map(move |i1i| i2.clone().map(move |i2i| (i1i.clone(), i2i)))
|
||||||
|
}
|
||||||
|
|||||||
@@ -109,7 +109,7 @@ fn init_config(args: InitConfigArgs) -> Result<()> {
|
|||||||
let cfg = config::AgentConfig {
|
let cfg = config::AgentConfig {
|
||||||
server_url: args
|
server_url: args
|
||||||
.server_url
|
.server_url
|
||||||
.unwrap_or_else(|| "https://control-plane.example.com".to_string()),
|
.unwrap_or_else(|| "https://wakey.ldlda.com".to_string()),
|
||||||
agent_id: args
|
agent_id: args
|
||||||
.agent_id
|
.agent_id
|
||||||
.unwrap_or_else(|| "REPLACE_ME_AGENT_ID".to_string()),
|
.unwrap_or_else(|| "REPLACE_ME_AGENT_ID".to_string()),
|
||||||
|
|||||||
@@ -36,7 +36,7 @@ async fn run_once(config: &AgentConfig) -> Result<()> {
|
|||||||
.unwrap_or_default()
|
.unwrap_or_default()
|
||||||
);
|
);
|
||||||
let span = info_span!("agent_session", session_id = %session_id, agent_id = %config.agent_id);
|
let span = info_span!("agent_session", session_id = %session_id, agent_id = %config.agent_id);
|
||||||
let _span_guard = span.enter();
|
let _span_guard = span.enter(); // the ENTIRETY of below is agent_session.
|
||||||
|
|
||||||
let ws_url = websocket_url(&config.server_url)?;
|
let ws_url = websocket_url(&config.server_url)?;
|
||||||
info!(%ws_url, agent_id = %config.agent_id, "connecting agent websocket");
|
info!(%ws_url, agent_id = %config.agent_id, "connecting agent websocket");
|
||||||
|
|||||||
@@ -18,7 +18,7 @@ pub struct Store {
|
|||||||
agent_meta: sled::Tree,
|
agent_meta: sled::Tree,
|
||||||
audit_events: sled::Tree,
|
audit_events: sled::Tree,
|
||||||
active_alerts: sled::Tree,
|
active_alerts: sled::Tree,
|
||||||
alert_transitions: sled::Tree,
|
alert_transitions: sled::Tree, // Where are them Types!
|
||||||
}
|
}
|
||||||
|
|
||||||
const SCHEMA_VERSION_KEY: &[u8] = b"schema_version";
|
const SCHEMA_VERSION_KEY: &[u8] = b"schema_version";
|
||||||
|
|||||||
@@ -71,8 +71,8 @@ impl Device {
|
|||||||
for lease in &leases {
|
for lease in &leases {
|
||||||
ips.insert(lease.ip);
|
ips.insert(lease.ip);
|
||||||
macs.insert(lease.mac);
|
macs.insert(lease.mac);
|
||||||
if let Some(name) = &lease.name {
|
if let Some(name) = lease.name.as_deref() {
|
||||||
names.insert(name.clone());
|
names.insert(name);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
for neighbor in &neighbors {
|
for neighbor in &neighbors {
|
||||||
@@ -80,8 +80,8 @@ impl Device {
|
|||||||
if let Some(mac) = neighbor.mac {
|
if let Some(mac) = neighbor.mac {
|
||||||
macs.insert(mac);
|
macs.insert(mac);
|
||||||
}
|
}
|
||||||
if let Some(dev) = &neighbor.dev {
|
if let Some(dev) = neighbor.dev.as_deref() {
|
||||||
interfaces.insert(dev.clone());
|
interfaces.insert(dev);
|
||||||
}
|
}
|
||||||
presence = std::cmp::max(
|
presence = std::cmp::max(
|
||||||
presence_rank(presence),
|
presence_rank(presence),
|
||||||
@@ -93,10 +93,10 @@ impl Device {
|
|||||||
let macs: Vec<MacAddr> = macs.into_iter().collect();
|
let macs: Vec<MacAddr> = macs.into_iter().collect();
|
||||||
Self {
|
Self {
|
||||||
id: macs.first().copied().map(|mac| DeviceId { mac }),
|
id: macs.first().copied().map(|mac| DeviceId { mac }),
|
||||||
names: names.into_iter().collect(),
|
names: names.into_iter().map(|n| n.to_owned()).collect(),
|
||||||
ips: ips.into_iter().collect(),
|
ips: ips.into_iter().collect(),
|
||||||
macs,
|
macs,
|
||||||
interfaces: interfaces.into_iter().collect(),
|
interfaces: interfaces.into_iter().map(|d| d.to_owned()).collect(),
|
||||||
neighbors,
|
neighbors,
|
||||||
leases,
|
leases,
|
||||||
presence,
|
presence,
|
||||||
|
|||||||
@@ -50,7 +50,7 @@ pub struct WakeTargetResult {
|
|||||||
#[serde(rename_all = "snake_case")]
|
#[serde(rename_all = "snake_case")]
|
||||||
pub enum WakeStatus {
|
pub enum WakeStatus {
|
||||||
Succeed,
|
Succeed,
|
||||||
NonexistentAddress,
|
NonexistentAddress, // how do i know this isnt some other errors? what!
|
||||||
WrongSize,
|
WrongSize,
|
||||||
Incomplete,
|
Incomplete,
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -13,7 +13,7 @@ use wakey_core::{InterfaceAddr, InterfaceSummary};
|
|||||||
/// Loopback is intentionally excluded.
|
/// Loopback is intentionally excluded.
|
||||||
pub async fn list_interface_summaries() -> Result<Vec<InterfaceSummary>> {
|
pub async fn list_interface_summaries() -> Result<Vec<InterfaceSummary>> {
|
||||||
#[cfg(unix)]
|
#[cfg(unix)]
|
||||||
let rows = address::nl::get(None)
|
let rows = address::nl::get(None) // <- why not do filtering here
|
||||||
.await
|
.await
|
||||||
.context("rtnetlink address query failed")?;
|
.context("rtnetlink address query failed")?;
|
||||||
|
|
||||||
@@ -22,6 +22,7 @@ pub async fn list_interface_summaries() -> Result<Vec<InterfaceSummary>> {
|
|||||||
.await
|
.await
|
||||||
.context("ip -j address show failed")?;
|
.context("ip -j address show failed")?;
|
||||||
|
|
||||||
|
// convert semi raw [AddrOutput] into Almost the same [InterfaceSummary]
|
||||||
let mut out: Vec<InterfaceSummary> = rows
|
let mut out: Vec<InterfaceSummary> = rows
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.filter(|row| row.ifname != "lo")
|
.filter(|row| row.ifname != "lo")
|
||||||
|
|||||||
Reference in New Issue
Block a user