feat: --json for hosts/subnet/policy list commands

- cmd::hosts::_output_json: hosts with type/tags array
- cmd::subnet::_output_json: subnets with is_group bool
- cmd::policy::_output_json: policies with proper booleans
This commit is contained in:
Nuno Duque Nunes 2026-05-27 00:52:45 +00:00
parent 087f735790
commit 0e3f281519
4 changed files with 105 additions and 2 deletions

View file

@ -14,6 +14,8 @@ function cmd::hosts::on_load() {
flag::register --tag flag::register --tag
flag::register --tags flag::register --tags
flag::register --force flag::register --force
command::mixin json_output
} }
# ============================================ # ============================================
@ -71,6 +73,12 @@ EOF
function cmd::hosts::run() { function cmd::hosts::run() {
local subcmd="${1:-list}" local subcmd="${1:-list}"
shift || true shift || true
if command::json; then
cmd::hosts::_output_json
return 0
fi
case "$subcmd" in case "$subcmd" in
list) cmd::hosts::list "$@" ;; list) cmd::hosts::list "$@" ;;
show) cmd::hosts::show "$@" ;; show) cmd::hosts::show "$@" ;;
@ -288,4 +296,30 @@ function cmd::hosts::rm() {
json::hosts_remove "$(ctx::hosts)" "$entry_type" "$key" json::hosts_remove "$(ctx::hosts)" "$entry_type" "$key"
log::wg_success "Removed ${entry_type}: ${key}" log::wg_success "Removed ${entry_type}: ${key}"
}
function cmd::hosts::_output_json() {
local data
data=$(json::hosts_list "$(ctx::hosts)" 2>/dev/null)
local -a hosts=()
while IFS='|' read -r type ip name desc tags; do
[[ -z "$type" ]] && continue
local tags_json="[]"
if [[ -n "$tags" ]]; then
local tags_array
tags_array=$(echo "$tags" | tr ',' '\n' | \
while IFS= read -r t; do [[ -n "$t" ]] && printf '"%s",' "$t"; done | sed 's/,$//')
tags_json="[${tags_array}]"
fi
hosts+=("$(printf '{"type":"%s","ip":"%s","name":"%s","desc":"%s","tags":%s}' \
"$type" "$ip" "$name" "$desc" "$tags_json")")
done <<< "$data"
local count=${#hosts[@]}
local array
array=$(printf '%s\n' "${hosts[@]:-}" | paste -sd ',' -)
printf '{"hosts":[%s]}' "${array:-}" | json::envelope "hosts list" "$count"
} }

View file

@ -27,6 +27,8 @@ function cmd::policy::on_load() {
flag::register --desc flag::register --desc
flag::register --field flag::register --field
flag::register --value flag::register --value
command::mixin json_output
} }
# ============================================ # ============================================
@ -79,6 +81,11 @@ function cmd::policy::run() {
local subcmd="${1:-list}" local subcmd="${1:-list}"
shift || true shift || true
if command::json; then
cmd::policy::_output_json
return 0
fi
case "$subcmd" in case "$subcmd" in
list) cmd::policy::_list "$@" ;; list) cmd::policy::_list "$@" ;;
show) cmd::policy::_show "$@" ;; show) cmd::policy::_show "$@" ;;
@ -244,4 +251,28 @@ function cmd::policy::_set() {
json::policy_set_field "$(ctx::policies)" "$name" "$field" "$value" json::policy_set_field "$(ctx::policies)" "$name" "$field" "$value"
log::ok "Policy '${name}': ${field} = ${value}" log::ok "Policy '${name}': ${field} = ${value}"
}
function cmd::policy::_output_json() {
local data
data=$(policy::list_data 2>/dev/null)
local -a policies=()
while IFS='|' read -r name tunnel_mode default_rule strict_rule auto_apply desc; do
[[ -z "$name" ]] && continue
local strict_json="false"
[[ "$strict_rule" == "true" ]] && strict_json="true"
local auto_json="true"
[[ "$auto_apply" == "false" ]] && auto_json="false"
policies+=("$(printf '{"name":"%s","tunnel_mode":"%s","default_rule":"%s","strict_rule":%s,"auto_apply":%s,"desc":"%s"}' \
"$name" "$tunnel_mode" "${default_rule:-}" \
"$strict_json" "$auto_json" "$desc")")
done <<< "$data"
local count=${#policies[@]}
local array
array=$(printf '%s\n' "${policies[@]:-}" | paste -sd ',' -)
printf '{"policies":[%s]}' "${array:-}" | json::envelope "policy list" "$count"
} }

View file

@ -22,6 +22,8 @@ function cmd::subnet::on_load() {
flag::register --desc flag::register --desc
flag::register --group flag::register --group
flag::register --new-name flag::register --new-name
command::mixin json_output
} }
# ============================================ # ============================================
@ -65,6 +67,11 @@ function cmd::subnet::run() {
local subcmd="${1:-list}" local subcmd="${1:-list}"
shift || true shift || true
if command::json; then
cmd::subnet::_output_json
return 0
fi
case "$subcmd" in case "$subcmd" in
list) cmd::subnet::_list "$@" ;; list) cmd::subnet::_list "$@" ;;
show) cmd::subnet::_show "$@" ;; show) cmd::subnet::_show "$@" ;;
@ -291,4 +298,26 @@ function cmd::subnet::_validate_cidr() {
log::error "Invalid CIDR format: '${cidr}'" log::error "Invalid CIDR format: '${cidr}'"
return 1 return 1
fi fi
}
function cmd::subnet::_output_json() {
local data
data=$(subnet::list_data 2>/dev/null)
local -a subnets=()
while IFS='|' read -r type cidr display_name tunnel_mode desc is_group group_parent; do
[[ -z "$type" ]] && continue
local is_group_json="false"
[[ "$is_group" == "true" ]] && is_group_json="true"
subnets+=("$(printf '{"type":"%s","cidr":"%s","display_name":"%s","tunnel_mode":"%s","desc":"%s","is_group":%s,"group_parent":"%s"}' \
"$type" "$cidr" "$display_name" "$tunnel_mode" \
"$desc" "$is_group_json" "${group_parent:-}")")
done <<< "$data"
local count=${#subnets[@]}
local array
array=$(printf '%s\n' "${subnets[@]:-}" | paste -sd ',' -)
printf '{"subnets":[%s]}' "${array:-}" | json::envelope "subnet list" "$count"
} }

View file

@ -132,6 +132,7 @@ function cmd::test::run_all_integration_sections() {
cmd::test::section_net cmd::test::section_net
cmd::test::section_subnet cmd::test::section_subnet
cmd::test::section_identity cmd::test::section_identity
cmd::test::section_activity
cmd::test::section_hosts cmd::test::section_hosts
cmd::test::section_peer_cmd cmd::test::section_peer_cmd
cmd::test::section_group_purge cmd::test::section_group_purge
@ -180,6 +181,7 @@ function cmd::test::section_rules() {
cmd::test::run_cmd "rule show --name guest" "Description" rule show --name guest cmd::test::run_cmd "rule show --name guest" "Description" rule show --name guest
cmd::test::run_cmd "rule show --name user" "Description" rule show --name user cmd::test::run_cmd "rule show --name user" "Description" rule show --name user
cmd::test::run_cmd "rule show --name admin" "Description" rule show --name admin cmd::test::run_cmd "rule show --name admin" "Description" rule show --name admin
cmd::test::run_cmd "rule list --json" '"rules":' rule list --json
cmd::test::run_cmd_fails "rule show nonexistent" rule show --name nonexistent cmd::test::run_cmd_fails "rule show nonexistent" rule show --name nonexistent
} }
@ -187,6 +189,7 @@ function cmd::test::section_groups() {
test::section "Groups" test::section "Groups"
cmd::test::run_cmd "group list" "Groups" group list cmd::test::run_cmd "group list" "Groups" group list
cmd::test::run_cmd "group show --name family" "Peers:" group show --name family cmd::test::run_cmd "group show --name family" "Peers:" group show --name family
cmd::test::run_cmd "group list --json" '"groups":' group list --json
cmd::test::run_cmd_fails "group show nonexistent" group show --name nonexistent cmd::test::run_cmd_fails "group show nonexistent" group show --name nonexistent
} }
@ -238,8 +241,9 @@ function cmd::test::section_net() {
cmd::test::run_cmd "net add port again" "Added" net add --name test-svc:web --port 9999:tcp cmd::test::run_cmd "net add port again" "Added" net add --name test-svc:web --port 9999:tcp
cmd::test::run_cmd "net rm all ports" "Removed" net rm --name test-svc:ports --force cmd::test::run_cmd "net rm all ports" "Removed" net rm --name test-svc:ports --force
cmd::test::run_cmd "net rm service" "Removed" net rm --name test-svc --force cmd::test::run_cmd "net rm service" "Removed" net rm --name test-svc --force
cmd::test::run_cmd_fails "net show nonexistent" net show --name nonexistent-svc cmd::test::run_cmd "net list --json" '"services":' net list --json
cmd::test::run_cmd_fails "net add port no service" net add --name nonexistent:web --port 80:tcp cmd::test::run_cmd_fails "net show nonexistent" net show --name nonexistent-svc
cmd::test::run_cmd_fails "net add port no service" net add --name nonexistent:web --port 80:tcp
} }
function cmd::test::section_subnet() { function cmd::test::section_subnet() {
@ -271,9 +275,14 @@ function cmd::test::section_identity() {
cmd::test::run_cmd "identity list" "" identity list cmd::test::run_cmd "identity list" "" identity list
cmd::test::run_cmd "identity migrate --dry-run" "Dry run" identity migrate --dry-run cmd::test::run_cmd "identity migrate --dry-run" "Dry run" identity migrate --dry-run
cmd::test::run_cmd "identity show nuno" "nuno" identity show --name nuno cmd::test::run_cmd "identity show nuno" "nuno" identity show --name nuno
cmd::test::run_cmd "identity list --json" '"identities":' identity list --json
cmd::test::run_cmd_fails "identity show nonexistent" identity show --name nonexistent cmd::test::run_cmd_fails "identity show nonexistent" identity show --name nonexistent
} }
function cmd::test::section_activity() {
cmd::test::run_cmd "activity --json" '"peers":' activity --json
}
function cmd::test::section_hosts() { function cmd::test::section_hosts() {
test::section "Hosts" test::section "Hosts"