This commit is contained in:
2025-11-22 09:27:58 -08:00
parent 0c06324882
commit fec776b0bf
3 changed files with 95 additions and 21 deletions

View File

@@ -35,11 +35,19 @@ class ConsulBackup:
try:
resp = requests.get(url, headers=self.headers, params=params)
# Handle 404 (empty KV store) gracefully
if resp.status_code == 404:
print("Consul KV store appears to be empty or path not found")
return []
resp.raise_for_status()
keys = resp.json()
return keys if keys else []
except requests.exceptions.RequestException as e:
print(f"Error retrieving KV keys: {e}")
if hasattr(e.response, 'status_code'):
print(f"HTTP Status: {e.response.status_code}")
sys.exit(1)
def get_kv_value(self, key: str) -> Optional[Dict[str, Any]]:
@@ -50,7 +58,18 @@ class ConsulBackup:
try:
resp = requests.get(url, headers=self.headers, params=params)
resp.raise_for_status()
data = resp.json()
# Handle different response types
if resp.headers.get('content-type') == 'application/json':
data = resp.json()
else:
# Handle non-JSON responses (binary data)
return {
'key': key,
'value': resp.content, # Keep as bytes for binary data
'flags': 0,
'is_binary': True
}
# Extract the actual value from the response
if isinstance(data, list) and len(data) > 0:
@@ -61,12 +80,21 @@ class ConsulBackup:
'flags': kv_data.get('Flags', 0),
'lock_index': kv_data.get('LockIndex', 0),
'create_index': kv_data.get('CreateIndex', 0),
'modify_index': kv_data.get('ModifyIndex', 0)
'modify_index': kv_data.get('ModifyIndex', 0),
'is_binary': False
}
return None
except requests.exceptions.RequestException as e:
print(f"Error retrieving KV value for {key}: {e}")
return None
except json.JSONDecodeError:
# Handle binary data that can't be parsed as JSON
return {
'key': key,
'value': resp.content, # Keep as bytes for binary data
'flags': 0,
'is_binary': True
}
def sanitize_key_path(self, key: str) -> str:
"""Convert Consul key to safe filesystem path."""
@@ -90,7 +118,26 @@ class ConsulBackup:
# Get all keys recursively
keys = self.get_kv_keys(recurse=True)
if not keys:
print("No keys found in Consul KV store")
print("No keys found in Consul KV store - creating empty backup")
# Create metadata file for empty backup
metadata = {
'backup_timestamp': datetime.now().isoformat(),
'total_keys': 0,
'successful_backups': 0,
'failed_backups': 0,
'consul_address': self.consul_addr,
'status': 'empty_kv_store'
}
metadata_path = backup_path / "metadata.json"
try:
with open(metadata_path, 'w') as f:
json.dump(metadata, f, indent=2)
print(f" ✓ Created empty backup metadata")
except IOError as e:
print(f" ✗ Failed to write metadata: {e}")
return 0
print(f"Found {len(keys)} keys in Consul KV store")
@@ -115,8 +162,18 @@ class ConsulBackup:
file_path.parent.mkdir(parents=True, exist_ok=True)
try:
with open(file_path, 'w') as f:
json.dump(kv_data, f, indent=2)
if kv_data.get('is_binary', False):
# For binary data, write directly with original extension
file_path = backup_path / key_path
file_path.parent.mkdir(parents=True, exist_ok=True)
with open(file_path, 'wb') as f:
f.write(kv_data['value'])
else:
# For JSON data, write with .json extension
file_path = backup_path / f"{key_path}.json"
file_path.parent.mkdir(parents=True, exist_ok=True)
with open(file_path, 'w') as f:
json.dump(kv_data, f, indent=2)
print(f" ✓ Saved to {file_path.relative_to(backup_path)}")
success_count += 1
except IOError as e: