Dernière activité 1 month ago

nuno a révisé ce gist 1 month ago. Aller à la révision

1 file changed, 288 insertions

import.command.sh(fichier créé)

@@ -0,0 +1,288 @@
1 + #!/usr/bin/env bash
2 + # commands/import.command.sh
3 +
4 + function cmd::import::on_load() {
5 + flag::register --file
6 + flag::register --peer
7 + flag::register --dry-run
8 + flag::register --force
9 + }
10 +
11 + function cmd::import::help() {
12 + cat <<EOF
13 + Usage: wgctl import --file <file> [options]
14 +
15 + Import a wgctl JSON export bundle.
16 +
17 + Options:
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 +
23 + Export 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 +
30 + Examples:
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
35 + EOF
36 + }
37 +
38 + function 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 +
101 + function 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 +
118 + function 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 +
150 + function 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 "
166 + import json, base64
167 + d = json.load(open('${file}'))
168 + conf = base64.b64decode(d['data']['conf']).decode()
169 + open('${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 +
180 + function 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 "
193 + import json
194 + d = json.load(open('${file}'))
195 + meta = d['data']['meta']
196 + open('$(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 +
207 + function 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 +
236 + function 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 +
264 + function 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 + }
Plus récent Plus ancien