Última actividad 1 month ago

Revisión d438182ac6c401461de72e240fa4102e2fc6ca12

import.command.sh Sin formato
1#!/usr/bin/env bash
2# commands/import.command.sh
3
4function cmd::import::on_load() {
5 flag::register --file
6 flag::register --peer
7 flag::register --dry-run
8 flag::register --force
9}
10
11function cmd::import::help() {
12 cat <<EOF
13Usage: wgctl import --file <file> [options]
14
15Import a wgctl JSON export bundle.
16
17Options:
18 --file <path> Path to export bundle (required)
19 --peer <name> Import only this peer from a full backup
20 --dry-run Show what would be imported without making changes
21 --force Overwrite existing data
22
23Export types handled:
24 peer Single peer bundle
25 peer_conf Peer conf only
26 peer_meta Peer meta only
27 identity Identity bundle
28 full Full backup
29
30Examples:
31 wgctl import --file backup.json
32 wgctl import --file backup.json --peer phone-nuno
33 wgctl import --file phone-nuno.json --dry-run
34 wgctl import --file backup.json --force
35EOF
36}
37
38function cmd::import::run() {
39 local file="" peer="" dry_run=false force=false
40
41 while [[ $# -gt 0 ]]; do
42 case "$1" in
43 --file) file="$2"; shift 2 ;;
44 --peer) peer="$2"; shift 2 ;;
45 --dry-run) dry_run=true; shift ;;
46 --force) force=true; shift ;;
47 --help) cmd::import::help; return ;;
48 *) log::error "Unknown flag: $1"; return 1 ;;
49 esac
50 done
51
52 [[ -z "$file" ]] && log::error "Missing required flag: --file" && return 1
53 [[ ! -f "$file" ]] && log::error "File not found: ${file}" && return 1
54
55 # Read export metadata via Python
56 local export_type export_version
57 export_type=$(json::import_get_field \
58 "$file" "export_type" 2>/dev/null)
59 export_version=$(json::import_get_field \
60 "$file" "wgctl_version" 2>/dev/null)
61
62 [[ -z "$export_type" ]] && \
63 log::error "Invalid export file: ${file}" && return 1
64
65 # Version check
66 local current_version
67 current_version=$(wgctl::version 2>/dev/null || echo "unknown")
68 if [[ "$export_version" != "$current_version" ]]; then
69 log::wg_warning "Export version (${export_version}) differs from current (${current_version})"
70 fi
71
72 log::section "wgctl Import"
73 printf " File: %s\n" "$file"
74 printf " Type: %s\n" "$export_type"
75 $dry_run && printf " Mode: \033[2mdry run\033[0m\n"
76 printf "\n"
77
78 case "$export_type" in
79 peer) cmd::import::_peer "$file" "$dry_run" "$force" ;;
80 peer_conf) cmd::import::_peer_conf "$file" "$dry_run" "$force" ;;
81 peer_meta) cmd::import::_peer_meta "$file" "$dry_run" "$force" ;;
82 identity) cmd::import::_identity "$file" "$dry_run" "$force" ;;
83 full)
84 if [[ -n "$peer" ]]; then
85 cmd::import::_peer_from_full "$file" "$peer" "$dry_run" "$force"
86 else
87 cmd::import::_full "$file" "$dry_run" "$force"
88 fi
89 ;;
90 *)
91 log::error "Unknown export type: ${export_type}"
92 return 1
93 ;;
94 esac
95}
96
97# ======================================================
98# Helpers
99# ======================================================
100
101function cmd::import::_print_results() {
102 while IFS= read -r line; do
103 [[ -z "$line" ]] && continue
104 case "$line" in
105 error:*) log::error "${line#error:}" ;;
106 skip:*) printf " \033[2mskip: %s (already exists)\033[0m\n" "${line#skip:}" ;;
107 peer:*) printf " ✓ peer: %s\n" "${line#peer:}" ;;
108 group:*) printf " ✓ group: %s\n" "${line#group:}" ;;
109 *) printf " ✓ %s\n" "$line" ;;
110 esac
111 done
112}
113
114# ======================================================
115# Peer import
116# ======================================================
117
118function cmd::import::_peer() {
119 local file="${1:-}" dry_run="${2:-false}" force="${3:-false}"
120
121 local name
122 name=$(json::import_get_field "$file" "data" "name" 2>/dev/null)
123 [[ -z "$name" ]] && log::error "Could not read peer name from export" && return 1
124
125 $dry_run && \
126 printf " \033[2m[dry-run]\033[0m Would import peer '%s'\n" "$name" && return 0
127
128 local err
129 err=$(json::import_peer \
130 "$file" "data" "$name" \
131 "$(ctx::clients)" "$(ctx::meta)" \
132 "$(ctx::groups)" "$(ctx::blocks)" \
133 "$($force && echo true || echo false)" \
134 2>&1 >/dev/null) || { log::error "$err"; return 1; }
135
136 json::import_peer \
137 "$file" "data" "$name" \
138 "$(ctx::clients)" "$(ctx::meta)" \
139 "$(ctx::groups)" "$(ctx::blocks)" \
140 "$($force && echo true || echo false)" \
141 2>/dev/null | cmd::import::_print_results
142
143 log::wg_success "Imported peer '${name}'"
144}
145
146# ======================================================
147# Peer conf only
148# ======================================================
149
150function cmd::import::_peer_conf() {
151 local file="${1:-}" dry_run="${2:-false}" force="${3:-false}"
152
153 local name
154 name=$(json::import_get_field "$file" "data" "name" 2>/dev/null)
155 local conf_file="$(ctx::clients)/${name}.conf"
156
157 if [[ -f "$conf_file" ]] && ! $force; then
158 log::error "Peer conf '${name}' already exists. Use --force to overwrite."
159 return 1
160 fi
161
162 $dry_run && \
163 printf " \033[2m[dry-run]\033[0m Would import conf for '%s'\n" "$name" && return 0
164
165 python3 -c "
166import json, base64
167d = json.load(open('${file}'))
168conf = base64.b64decode(d['data']['conf']).decode()
169open('${conf_file}', 'w').write(conf)
170" 2>/dev/null || { log::error "Failed to write conf"; return 1; }
171
172 printf " ✓ conf\n"
173 log::wg_success "Imported conf for '${name}'"
174}
175
176# ======================================================
177# Peer meta only
178# ======================================================
179
180function cmd::import::_peer_meta() {
181 local file="${1:-}" dry_run="${2:-false}" force="${3:-false}"
182
183 local name
184 name=$(json::import_get_field "$file" "data" "name" 2>/dev/null)
185
186 [[ ! -f "$(ctx::clients)/${name}.conf" ]] && \
187 log::error "Peer '${name}' does not exist — import the peer conf first" && return 1
188
189 $dry_run && \
190 printf " \033[2m[dry-run]\033[0m Would import meta for '%s'\n" "$name" && return 0
191
192 python3 -c "
193import json
194d = json.load(open('${file}'))
195meta = d['data']['meta']
196open('$(ctx::meta)/${name}.meta', 'w').write(json.dumps(meta, indent=2))
197" 2>/dev/null || { log::error "Failed to write meta"; return 1; }
198
199 printf " ✓ meta\n"
200 log::wg_success "Imported meta for '${name}'"
201}
202
203# ======================================================
204# Identity import
205# ======================================================
206
207function cmd::import::_identity() {
208 local file="${1:-}" dry_run="${2:-false}" force="${3:-false}"
209
210 local name
211 name=$(json::import_get_field "$file" "data" "name" 2>/dev/null)
212
213 $dry_run && \
214 printf " \033[2m[dry-run]\033[0m Would import identity '%s'\n" "$name" && return 0
215
216 local err
217 err=$(json::import_identity \
218 "$file" "$name" \
219 "$(ctx::identities)" "$(ctx::clients)" \
220 "$($force && echo true || echo false)" \
221 2>&1 >/dev/null) || { log::error "$err"; return 1; }
222
223 json::import_identity \
224 "$file" "$name" \
225 "$(ctx::identities)" "$(ctx::clients)" \
226 "$($force && echo true || echo false)" \
227 2>/dev/null | cmd::import::_print_results
228
229 log::wg_success "Imported identity '${name}'"
230}
231
232# ======================================================
233# Single peer from full backup
234# ======================================================
235
236function cmd::import::_peer_from_full() {
237 local file="${1:-}" name="${2:-}" dry_run="${3:-false}" force="${4:-false}"
238
239 $dry_run && \
240 printf " \033[2m[dry-run]\033[0m Would import peer '%s' from backup\n" "$name" && return 0
241
242 local err
243 err=$(json::import_peer \
244 "$file" "peers" "$name" \
245 "$(ctx::clients)" "$(ctx::meta)" \
246 "$(ctx::groups)" "$(ctx::blocks)" \
247 "$($force && echo true || echo false)" \
248 2>&1 >/dev/null) || { log::error "$err"; return 1; }
249
250 json::import_peer \
251 "$file" "peers" "$name" \
252 "$(ctx::clients)" "$(ctx::meta)" \
253 "$(ctx::groups)" "$(ctx::blocks)" \
254 "$($force && echo true || echo false)" \
255 2>/dev/null | cmd::import::_print_results
256
257 log::wg_success "Imported peer '${name}' from backup"
258}
259
260# ======================================================
261# Full backup import
262# ======================================================
263
264function cmd::import::_full() {
265 local file="${1:-}" dry_run="${2:-false}" force="${3:-false}"
266
267 local peer_count
268 peer_count=$(json::import_get_field \
269 "$file" "data" "peers" 2>/dev/null | python3 -c \
270 "import json,sys; print(len(json.load(sys.stdin)))" 2>/dev/null || echo "?")
271
272 printf " Importing full backup (%s peers)...\n\n" "$peer_count"
273
274 $dry_run && \
275 log::wg_warning "Dry run — no changes would be made" && return 0
276
277 json::import_full \
278 "$file" \
279 "$(ctx::clients)" "$(ctx::meta)" \
280 "$(ctx::rules)" "$(ctx::identities)" \
281 "$(ctx::groups)" "$(ctx::blocks)" \
282 "$(ctx::policies)" "$(ctx::subnets)" \
283 "$(ctx::net)" "$(ctx::hosts)" \
284 "$($force && echo true || echo false)" \
285 2>/dev/null | cmd::import::_print_results
286
287 log::wg_success "Import complete"
288}