diff --git a/apps/pusher_app_prem/appserver/static/git_pusher.js b/apps/pusher_app_prem/appserver/static/git_pusher.js index 951f20e1..bf2c5a3a 100755 --- a/apps/pusher_app_prem/appserver/static/git_pusher.js +++ b/apps/pusher_app_prem/appserver/static/git_pusher.js @@ -1,18 +1,29 @@ // ============================================ // GIT PUSHER - MAIN JAVASCRIPT -// Version 2.0 avec système de licence par fichier +// Version 2.1 avec déploiement vers SH Cluster // ============================================ // Configuration const GIT_PUSHER_CONFIG = { + // Détecter automatiquement l'URL du serveur serverUrl: window.location.protocol + '//' + window.location.hostname + ':9999', credentialsKey: 'git_pusher_credentials', - version: '2.0.0' + deployerConfigKey: 'git_pusher_deployer_config', + version: '2.1.0' +}; + +// Configuration SH Deployer (peut être modifiée via l'interface) +let SH_DEPLOYER_CONFIG = { + enabled: false, + host: '10.10.40.14', + port: 9998, + token: '' }; // État global let selectedApps = []; let isProcessing = false; +let deployerAvailable = false; // ============================================ // INITIALISATION @@ -25,7 +36,7 @@ require([ 'splunkjs/mvc/simplexml/ready!' ], function($, mvc, SearchManager) { - console.log("Git Pusher v2.0 initializing..."); + console.log("Git Pusher v2.1 initializing..."); // Initialiser le système de licence if (typeof initializeLicense === 'function') { @@ -37,6 +48,12 @@ require([ // Charger les credentials sauvegardés loadSavedCredentials(); + // Charger la config du deployer + loadDeployerConfig(); + + // Vérifier la disponibilité du SH Deployer + checkDeployerHealth(); + // Récupérer les résultats de recherche pour les apps const searchManager = mvc.Components.get('dsearch'); @@ -59,23 +76,6 @@ require([ window.toggleSelectAll = toggleSelectAll; }); -// Attacher l'événement au bouton après chargement du DOM -document.addEventListener('DOMContentLoaded', function() { - setTimeout(function() { - var pushBtn = document.getElementById('push-btn'); - if (pushBtn) { - pushBtn.onclick = function() { - if (typeof pushDashboards === 'function') { - pushDashboards(); - } else if (typeof window.pushDashboards === 'function') { - window.pushDashboards(); - } - }; - console.log("Push button event attached"); - } - }, 2000); // Attendre 2 secondes que tout soit chargé -}); - // ============================================ // RENDU DE LA LISTE DES APPLICATIONS // ============================================ @@ -84,16 +84,11 @@ function renderAppsList(rows, fields) { const container = document.getElementById('dashboard-list'); if (!container) return; - console.log("Fields:", fields); - console.log("First row:", rows[0]); - // Trouver les index des colonnes const nameIdx = fields.indexOf('name'); const labelIdx = fields.indexOf('label'); const descIdx = fields.indexOf('description'); - console.log("Index - name:", nameIdx, "label:", labelIdx); - // Générer le HTML let html = `
@@ -105,34 +100,16 @@ function renderAppsList(rows, fields) {
`; - let validApps = 0; - rows.forEach((row, index) => { - // Récupérer le nom - essayer plusieurs méthodes - let name = ''; - if (nameIdx >= 0 && row[nameIdx]) { - name = row[nameIdx]; - } else if (labelIdx >= 0 && row[labelIdx]) { - // Si name est null, utiliser label comme fallback temporaire - // On va chercher le vrai nom via l'API - name = row[labelIdx]; - } else if (row[0]) { - name = row[0]; - } else if (row[1]) { - name = row[1]; // Deuxième élément si premier est null - } - - const label = (labelIdx >= 0 && row[labelIdx]) ? row[labelIdx] : name; - - console.log(`Row ${index}: name="${name}", label="${label}"`); + const name = row[nameIdx] || ''; + const label = row[labelIdx] || name; + const desc = row[descIdx] || ''; - // Ignorer si pas de nom ou apps système - if (!name || name.startsWith('splunk_') || name === 'learned' || name === 'launcher') { + // Ignorer certaines apps système + if (name.startsWith('splunk_') || name === 'learned' || name === 'launcher') { return; } - validApps++; - html += `
`; @@ -150,7 +127,7 @@ function renderAppsList(rows, fields) { container.innerHTML = html; - console.log(`Rendered ${validApps} valid applications out of ${rows.length}`); + console.log(`Rendered ${rows.length} applications`); } // ============================================ @@ -203,27 +180,6 @@ async function pushDashboards() { return; } - // Récupérer les apps sélectionnées directement depuis le DOM - var apps = []; - var checkboxes = document.querySelectorAll('#dashboard-list input[type="checkbox"][data-app-id]:checked'); - checkboxes.forEach(function(cb) { - apps.push({ - id: cb.getAttribute('data-app-id'), - label: cb.getAttribute('data-app-label') || cb.getAttribute('data-app-id') - }); - }); - - console.log("Apps selected from DOM:", apps); - - // Mettre à jour selectedApps - selectedApps = apps; - - // Vérifier si déjà en cours - if (isProcessing) { - console.log("Push already in progress"); - return; - } - // Vérifier la licence AVANT tout if (typeof checkLicenseBeforePush === 'function') { const licenseOk = await checkLicenseBeforePush(); @@ -269,9 +225,14 @@ async function pushDashboards() { saveCredentialsToStorage(gitUrl, gitBranch, gitToken); } + // Vérifier si le déploiement vers SH Cluster est activé + const deployToSHCluster = document.getElementById('deploy-to-shcluster')?.checked || false; + const shAuthUser = document.getElementById('sh-auth-user')?.value?.trim() || ''; + const shAuthPass = document.getElementById('sh-auth-pass')?.value?.trim() || ''; + // Démarrer le push isProcessing = true; - showLoading(true); + showLoading(true, deployToSHCluster); hideMessages(); try { @@ -285,10 +246,17 @@ async function pushDashboards() { git_token: gitToken, commit_message: commitMessage, apps: JSON.stringify(selectedApps), - user: currentUser + user: currentUser, + deploy_to_shcluster: deployToSHCluster.toString(), + deployer_host: SH_DEPLOYER_CONFIG.host, + deployer_token: SH_DEPLOYER_CONFIG.token }); - console.log(`Pushing ${selectedApps.length} apps to ${gitUrl}`); + // Ajouter les credentials SH si fournis + if (shAuthUser) params.append('sh_auth_user', shAuthUser); + if (shAuthPass) params.append('sh_auth_pass', shAuthPass); + + console.log(`Pushing ${selectedApps.length} apps to ${gitUrl}${deployToSHCluster ? ' + SH Cluster deployment' : ''}`); // Appeler le serveur const response = await fetch(`${GIT_PUSHER_CONFIG.serverUrl}/push?${params.toString()}`, { @@ -302,7 +270,18 @@ async function pushDashboards() { console.log("Push result:", result); if (result.status === 'success') { - showMessage('success', `✅ Successfully deployed ${result.apps_pushed || selectedApps.length} application(s) to Git!`); + let message = `✅ Successfully deployed ${result.apps_pushed || selectedApps.length} application(s) to Git!`; + + // Ajouter le statut du déploiement SH Cluster + if (deployToSHCluster && result.shcluster_deployment) { + if (result.shcluster_deployment.success) { + message += '\n🚀 SH Cluster deployment triggered successfully!'; + } else { + message += `\n⚠️ SH Cluster deployment failed: ${result.shcluster_deployment.message}`; + } + } + + showMessage('success', message); // Reset la sélection après succès setTimeout(() => { @@ -334,17 +313,30 @@ async function pushDashboards() { // UTILITAIRES UI // ============================================ -function showLoading(show) { +function showLoading(show, deployToSHCluster = false) { const loading = document.getElementById('loading'); const pushBtn = document.getElementById('push-btn'); + const loadingText = document.querySelector('.loading-text'); if (loading) { loading.classList.toggle('active', show); } + if (loadingText && show) { + if (deployToSHCluster) { + loadingText.textContent = 'Deploying to Git and SH Cluster... Please wait'; + } else { + loadingText.textContent = 'Deploying applications to Git... Please wait'; + } + } + if (pushBtn) { pushBtn.disabled = show; - pushBtn.textContent = show ? '⏳ Deploying...' : '✈️ Deploy to Git'; + if (show) { + pushBtn.textContent = deployToSHCluster ? '⏳ Deploying to Git + SH...' : '⏳ Deploying...'; + } else { + pushBtn.textContent = '✈️ Deploy to Git'; + } } } @@ -493,6 +485,190 @@ async function checkServerHealth() { } } +// ============================================ +// SH DEPLOYER FUNCTIONS +// ============================================ + +async function checkDeployerHealth() { + try { + const response = await fetch(`${GIT_PUSHER_CONFIG.serverUrl}/deployer/health`, { + method: 'GET', + timeout: 5000 + }); + const data = await response.json(); + + deployerAvailable = data.status === 'ok'; + + // Mettre à jour l'UI + updateDeployerUI(); + + console.log("SH Deployer status:", deployerAvailable ? "Available" : "Unavailable"); + return deployerAvailable; + } catch (error) { + console.error("Deployer health check failed:", error); + deployerAvailable = false; + updateDeployerUI(); + return false; + } +} + +function updateDeployerUI() { + const deployerCheckbox = document.getElementById('deploy-to-shcluster'); + const deployerStatus = document.getElementById('deployer-status'); + const deployerSection = document.getElementById('deployer-section'); + + if (deployerCheckbox) { + deployerCheckbox.disabled = !deployerAvailable; + } + + if (deployerStatus) { + if (deployerAvailable) { + deployerStatus.innerHTML = '● Connected'; + } else { + deployerStatus.innerHTML = '● Disconnected'; + } + } + + if (deployerSection && !deployerAvailable) { + deployerSection.style.opacity = '0.6'; + } +} + +function loadDeployerConfig() { + try { + const saved = localStorage.getItem(GIT_PUSHER_CONFIG.deployerConfigKey); + if (saved) { + const config = JSON.parse(saved); + SH_DEPLOYER_CONFIG = { ...SH_DEPLOYER_CONFIG, ...config }; + console.log("Deployer config loaded"); + } + } catch (error) { + console.error("Error loading deployer config:", error); + } +} + +function saveDeployerConfig() { + try { + localStorage.setItem(GIT_PUSHER_CONFIG.deployerConfigKey, JSON.stringify(SH_DEPLOYER_CONFIG)); + console.log("Deployer config saved"); + } catch (error) { + console.error("Error saving deployer config:", error); + } +} + +function showDeployerConfigModal() { + const modal = document.createElement('div'); + modal.id = 'deployer-config-modal'; + modal.style.cssText = ` + position: fixed; + top: 0; + left: 0; + width: 100%; + height: 100%; + background: rgba(0, 0, 0, 0.7); + display: flex; + align-items: center; + justify-content: center; + z-index: 10000; + font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif; + `; + + modal.innerHTML = ` +
+

⚙️ SH Deployer Configuration

+ +
+ + +
+ +
+ + +
+ +
+ + +
+ +
+ + +
+ + +
+ `; + + document.body.appendChild(modal); +} + +function closeDeployerConfigModal() { + const modal = document.getElementById('deployer-config-modal'); + if (modal) modal.remove(); +} + +async function saveDeployerConfigFromModal() { + const host = document.getElementById('deployer-config-host')?.value?.trim(); + const port = parseInt(document.getElementById('deployer-config-port')?.value) || 9998; + const token = document.getElementById('deployer-config-token')?.value?.trim(); + const msgEl = document.getElementById('deployer-config-message'); + + if (!host) { + if (msgEl) { + msgEl.style.display = 'block'; + msgEl.style.background = '#ffebee'; + msgEl.style.color = '#c62828'; + msgEl.textContent = 'Please enter a host'; + } + return; + } + + // Mettre à jour la config + SH_DEPLOYER_CONFIG.host = host; + SH_DEPLOYER_CONFIG.port = port; + SH_DEPLOYER_CONFIG.token = token; + + // Sauvegarder + saveDeployerConfig(); + + // Tester la connexion + if (msgEl) { + msgEl.style.display = 'block'; + msgEl.style.background = '#e3f2fd'; + msgEl.style.color = '#1565c0'; + msgEl.textContent = 'Testing connection...'; + } + + const isHealthy = await checkDeployerHealth(); + + if (isHealthy) { + if (msgEl) { + msgEl.style.background = '#e8f5e9'; + msgEl.style.color = '#2e7d32'; + msgEl.textContent = '✓ Connected successfully!'; + } + setTimeout(closeDeployerConfigModal, 1500); + } else { + if (msgEl) { + msgEl.style.background = '#ffebee'; + msgEl.style.color = '#c62828'; + msgEl.textContent = '✗ Connection failed. Check host and port.'; + } + } +} + // Vérifier la santé du serveur au démarrage setTimeout(async () => { const healthy = await checkServerHealth(); @@ -503,12 +679,31 @@ setTimeout(async () => { } }, 1000); +// ============================================ +// EXPORT FONCTIONS GLOBALES +// ============================================ + +// Exposer les fonctions du deployer globalement pour les onclick du HTML +window.showDeployerConfigModal = showDeployerConfigModal; +window.closeDeployerConfigModal = closeDeployerConfigModal; +window.saveDeployerConfigFromModal = saveDeployerConfigFromModal; + +// Fonction toggle pour le HTML +window.toggleDeployerAuth = function() { + var checkbox = document.getElementById('deploy-to-shcluster'); + var authSection = document.getElementById('deployer-auth'); + if (checkbox && authSection) { + authSection.classList.toggle('visible', checkbox.checked); + } +}; + // ============================================ // EXPORT POUR DEBUG // ============================================ window.GitPusher = { config: GIT_PUSHER_CONFIG, + deployerConfig: SH_DEPLOYER_CONFIG, getSelectedApps: () => selectedApps, checkServer: checkServerHealth, version: GIT_PUSHER_CONFIG.version @@ -530,29 +725,44 @@ window.GitPusher = { console.log("✓ Push button event attached"); } - // Bouton Reset - chercher par le texte ou la classe - var resetBtn = document.querySelector('button.btn-secondary'); - if (!resetBtn) { - // Chercher par le contenu - var allButtons = document.querySelectorAll('button.btn'); - allButtons.forEach(function(btn) { - if (btn.textContent.includes('Reset') || btn.textContent.includes('🔄')) { - resetBtn = btn; - } + // Bouton Reset + var buttons = document.querySelectorAll('button.btn'); + buttons.forEach(function(btn) { + if (btn.textContent.includes('Reset') || btn.textContent.includes('🔄')) { + btn.addEventListener('click', function(e) { + e.preventDefault(); + console.log("Reset button clicked"); + resetForm(true); + }); + console.log("✓ Reset button event attached"); + } + }); + + // Bouton Configure Deployer + var configBtn = document.querySelector('.deployer-config-btn'); + if (configBtn) { + configBtn.addEventListener('click', function(e) { + e.preventDefault(); + console.log("Configure button clicked"); + showDeployerConfigModal(); }); + console.log("✓ Configure button event attached"); } - if (resetBtn) { - resetBtn.addEventListener('click', function(e) { - e.preventDefault(); - console.log("Reset button clicked"); - resetForm(true); + // Checkbox deploy to shcluster + var deployCheckbox = document.getElementById('deploy-to-shcluster'); + if (deployCheckbox) { + deployCheckbox.addEventListener('change', function() { + var authSection = document.getElementById('deployer-auth'); + if (authSection) { + authSection.classList.toggle('visible', this.checked); + } }); - console.log("✓ Reset button event attached"); + console.log("✓ Deploy checkbox event attached"); } // Si les boutons ne sont pas encore là, réessayer - if (!pushBtn || !resetBtn) { + if (!pushBtn) { setTimeout(tryAttach, 500); } } diff --git a/apps/pusher_app_prem/bin/git_pusher.pid b/apps/pusher_app_prem/bin/git_pusher.pid index b76d2172..049d42b3 100644 --- a/apps/pusher_app_prem/bin/git_pusher.pid +++ b/apps/pusher_app_prem/bin/git_pusher.pid @@ -1 +1 @@ -1285462 +2311220 diff --git a/apps/pusher_app_prem/bin/git_pusher.py b/apps/pusher_app_prem/bin/git_pusher.py index 60c76cce..42ac57c7 100755 --- a/apps/pusher_app_prem/bin/git_pusher.py +++ b/apps/pusher_app_prem/bin/git_pusher.py @@ -3,6 +3,7 @@ """ Git Pusher - Main Server Serveur HTTP pour pousser les applications Splunk vers Git +et déployer vers le Search Head Cluster via le SH Deployer Avec système de licence par fichier .lic """ @@ -14,6 +15,9 @@ import logging import tempfile import shutil import subprocess +import ssl +import urllib.request +import urllib.error from datetime import datetime from http.server import HTTPServer, BaseHTTPRequestHandler from urllib.parse import parse_qs, urlparse @@ -41,6 +45,20 @@ except ImportError: def get_usage_stats(): return {} def parse_license_content(c): return {} +# ============================================ +# CONFIGURATION SH DEPLOYER +# ============================================ + +# Configuration du SH Deployer (peut être surchargée par les paramètres) +SH_DEPLOYER_CONFIG = { + "enabled": True, + "host": "10.10.40.14", + "port": 9998, + "use_ssl": True, + "token": "deployer_agent_secret_token_change_me_in_production", + "timeout": 30 +} + # Configuration du logging log_dir = '/opt/splunk/var/log/splunk' os.makedirs(log_dir, exist_ok=True) @@ -119,7 +137,54 @@ class GitPusherRequestHandler(BaseHTTPRequestHandler): response = { "status": "ok", "service": "git_pusher", - "timestamp": datetime.now().isoformat() + "timestamp": datetime.now().isoformat(), + "sh_deployer": { + "enabled": SH_DEPLOYER_CONFIG.get("enabled", True), + "host": SH_DEPLOYER_CONFIG.get("host"), + "port": SH_DEPLOYER_CONFIG.get("port") + } + } + self.wfile.write(json.dumps(response).encode()) + + # ============================================ + # ENDPOINTS SH DEPLOYER + # ============================================ + + elif path == '/deployer/health': + # Vérifier la santé du SH Deployer + result = call_deployer_agent("/health") + if result.get("success"): + response = { + "status": "ok", + "deployer": result.get("data"), + "config": { + "host": SH_DEPLOYER_CONFIG.get("host"), + "port": SH_DEPLOYER_CONFIG.get("port") + } + } + else: + response = { + "status": "error", + "error": result.get("error"), + "config": { + "host": SH_DEPLOYER_CONFIG.get("host"), + "port": SH_DEPLOYER_CONFIG.get("port") + } + } + self.wfile.write(json.dumps(response).encode()) + + elif path == '/deployer/status': + # Statut du SH Deployer + result = get_deployer_status() + self.wfile.write(json.dumps(result).encode()) + + elif path == '/deployer/config': + # Configuration actuelle du SH Deployer + response = { + "enabled": SH_DEPLOYER_CONFIG.get("enabled", True), + "host": SH_DEPLOYER_CONFIG.get("host"), + "port": SH_DEPLOYER_CONFIG.get("port"), + "use_ssl": SH_DEPLOYER_CONFIG.get("use_ssl", True) } self.wfile.write(json.dumps(response).encode()) @@ -224,7 +289,7 @@ class GitPusherRequestHandler(BaseHTTPRequestHandler): self.wfile.write(json.dumps(response).encode()) def handle_git_push(self, query_params): - """Gérer le push Git""" + """Gérer le push Git et optionnellement le déploiement vers SH Cluster""" try: # Extraire les paramètres git_url = query_params.get('git_url', [''])[0] @@ -234,7 +299,14 @@ class GitPusherRequestHandler(BaseHTTPRequestHandler): apps_json = query_params.get('apps', query_params.get('dashboards', ['[]']))[0] user = query_params.get('user', ['unknown'])[0] - logger.info(f"Parameters: git_url={git_url}, branch={git_branch}, user={user}") + # Paramètres pour le déploiement SH Cluster + deploy_to_shcluster = query_params.get('deploy_to_shcluster', ['false'])[0].lower() == 'true' + deployer_host = query_params.get('deployer_host', [SH_DEPLOYER_CONFIG.get('host', '')])[0] + deployer_token = query_params.get('deployer_token', [SH_DEPLOYER_CONFIG.get('token', '')])[0] + sh_auth_user = query_params.get('sh_auth_user', [''])[0] + sh_auth_pass = query_params.get('sh_auth_pass', [''])[0] + + logger.info(f"Parameters: git_url={git_url}, branch={git_branch}, user={user}, deploy_to_shcluster={deploy_to_shcluster}") # Parser les apps try: @@ -322,7 +394,7 @@ class GitPusherRequestHandler(BaseHTTPRequestHandler): if result.returncode != 0: logger.warning(f"Commit warning: {result.stderr}") - logger.info("Pushing...") + logger.info("Pushing to Git...") result = subprocess.run(['git', 'push', 'origin', git_branch], cwd=temp_dir, capture_output=True, text=True, timeout=60) @@ -332,13 +404,59 @@ class GitPusherRequestHandler(BaseHTTPRequestHandler): # Incrémenter les stats d'utilisation increment_usage() - logger.info("Push successful!") + logger.info("Git push successful!") + + # ============================================ + # DÉPLOIEMENT VERS SH CLUSTER (optionnel) + # ============================================ + + deployer_result = None + + if deploy_to_shcluster: + logger.info("Triggering deployment to SH Cluster...") + + # Configurer le deployer + deployer_config = SH_DEPLOYER_CONFIG.copy() + if deployer_host: + deployer_config["host"] = deployer_host + if deployer_token: + deployer_config["token"] = deployer_token + + # Appeler le SH Deployer pour pull + deploy + deployer_result = trigger_deployer_pull_and_deploy( + git_url=git_url, + git_token=git_token, + auth_user=sh_auth_user if sh_auth_user else None, + auth_pass=sh_auth_pass if sh_auth_pass else None, + config=deployer_config + ) + + if deployer_result.get("success"): + logger.info("SH Cluster deployment triggered successfully") + else: + logger.error(f"SH Cluster deployment failed: {deployer_result.get('error')}") + + # Préparer la réponse response = { "status": "success", "message": f"Successfully pushed {len(app_directories)} application(s) to Git", "apps_pushed": len(app_directories), "license_type": license_info.get("type_name", "N/A") } + + # Ajouter les infos de déploiement si activé + if deploy_to_shcluster: + response["shcluster_deployment"] = { + "triggered": True, + "success": deployer_result.get("success", False) if deployer_result else False, + "message": deployer_result.get("data", {}).get("message") if deployer_result and deployer_result.get("success") else deployer_result.get("error") if deployer_result else "Not triggered" + } + + if deployer_result and deployer_result.get("success"): + response["message"] += " and triggered SH Cluster deployment" + else: + response["message"] += " (SH Cluster deployment failed)" + self.wfile.write(json.dumps(response).encode()) finally: @@ -417,6 +535,146 @@ class GitPusherRequestHandler(BaseHTTPRequestHandler): return app_directories +# ============================================ +# FONCTIONS SH DEPLOYER +# ============================================ + +def call_deployer_agent(endpoint, method="GET", data=None, config=None): + """ + Appeler l'agent SH Deployer + + Args: + endpoint: Endpoint à appeler (ex: /health, /pull, /deploy) + method: GET ou POST + data: Données à envoyer (dict) + config: Configuration (override SH_DEPLOYER_CONFIG) + + Returns: + dict avec success, data ou error + """ + if config is None: + config = SH_DEPLOYER_CONFIG + + if not config.get("enabled", True): + return {"success": False, "error": "SH Deployer is disabled"} + + host = config.get("host", "10.10.40.14") + port = config.get("port", 9998) + use_ssl = config.get("use_ssl", True) + token = config.get("token", "") + timeout = config.get("timeout", 30) + + protocol = "https" if use_ssl else "http" + url = f"{protocol}://{host}:{port}{endpoint}" + + logger.info(f"Calling SH Deployer: {method} {url}") + + try: + # Créer le contexte SSL (ignorer les certificats auto-signés) + ssl_context = ssl.create_default_context() + ssl_context.check_hostname = False + ssl_context.verify_mode = ssl.CERT_NONE + + # Préparer les données + if data: + json_data = json.dumps(data).encode('utf-8') + else: + json_data = None + + # Créer la requête + req = urllib.request.Request(url, data=json_data, method=method) + req.add_header('Content-Type', 'application/json') + req.add_header('X-Auth-Token', token) + + # Exécuter la requête + with urllib.request.urlopen(req, timeout=timeout, context=ssl_context) as response: + response_data = json.loads(response.read().decode('utf-8')) + logger.info(f"SH Deployer response: {response_data}") + return {"success": True, "data": response_data} + + except urllib.error.HTTPError as e: + error_body = e.read().decode('utf-8') if e.fp else str(e) + logger.error(f"SH Deployer HTTP error {e.code}: {error_body}") + return {"success": False, "error": f"HTTP {e.code}: {error_body}"} + + except urllib.error.URLError as e: + logger.error(f"SH Deployer connection error: {e.reason}") + return {"success": False, "error": f"Connection error: {e.reason}"} + + except Exception as e: + logger.error(f"SH Deployer error: {str(e)}") + return {"success": False, "error": str(e)} + + +def check_deployer_health(config=None): + """Vérifier si l'agent SH Deployer est accessible""" + result = call_deployer_agent("/health", config=config) + return result.get("success", False) + + +def trigger_deployer_pull(git_url, git_token, config=None): + """ + Déclencher un pull sur le SH Deployer + + Args: + git_url: URL du repository Git + git_token: Token Git pour l'authentification + config: Configuration du deployer + """ + data = { + "repo_url": git_url, + "git_token": git_token, + "apps_subdir": "apps" + } + + return call_deployer_agent("/pull", method="POST", data=data, config=config) + + +def trigger_deployer_deploy(target_uri=None, auth_user=None, auth_pass=None, config=None): + """ + Déclencher le déploiement du bundle sur le SH Cluster + + Args: + target_uri: URI du captain du SH Cluster (optionnel) + auth_user: Utilisateur Splunk + auth_pass: Mot de passe Splunk + config: Configuration du deployer + """ + data = {} + if target_uri: + data["target_uri"] = target_uri + if auth_user: + data["auth_user"] = auth_user + if auth_pass: + data["auth_pass"] = auth_pass + + return call_deployer_agent("/deploy", method="POST", data=data, config=config) + + +def trigger_deployer_pull_and_deploy(git_url, git_token, target_uri=None, auth_user=None, auth_pass=None, config=None): + """ + Déclencher pull + deploy en une seule opération + """ + data = { + "repo_url": git_url, + "git_token": git_token, + "apps_subdir": "apps" + } + if target_uri: + data["target_uri"] = target_uri + if auth_user: + data["auth_user"] = auth_user + if auth_pass: + data["auth_pass"] = auth_pass + + return call_deployer_agent("/pull-and-deploy", method="POST", data=data, config=config) + + +def get_deployer_status(config=None): + """Récupérer le statut du SH Deployer""" + return call_deployer_agent("/status", config=config) + + def start_server(port=9999, use_ssl=True): """Démarrer le serveur HTTP/HTTPS""" import ssl diff --git a/apps/pusher_app_prem/local/data/ui/views/git_pusher_-_deploy_applications.xml b/apps/pusher_app_prem/local/data/ui/views/git_pusher_-_deploy_applications.xml index 6c6c977a..01cf8409 100644 --- a/apps/pusher_app_prem/local/data/ui/views/git_pusher_-_deploy_applications.xml +++ b/apps/pusher_app_prem/local/data/ui/views/git_pusher_-_deploy_applications.xml @@ -1,451 +1,535 @@ - - - - Modern interface to push Splunk applications to Git repository + + + Push Splunk applications to Git repository and deploy to SH Cluster - - - - -
- -
-
- | rest /services/apps/local | search disabled=0 | table title, label, description | rename title as name | sort label - -4h@h + -1m now - + - -
-

🚀 Git Pusher

-

Deploy your Splunk applications to Git with confidence

-
- -
-
- ✨ Configure your Git settings below and select the applications you want to deploy to your repository + +
+ +
+
+

🚀 Git Pusher

+ v2.1 +
+
+ +
- -
- -
-
⚙️ Configuration
+ + +
+ +
+

📦 Applications

+
+ +

Loading applications...

+
+
+ + +
+

⚙️ Git Configuration

- - + + +
HTTPS URL of your Git repository
- +
- - + +
- +
- - -
- -
- - + + +
Personal access token with write permissions
- +
- + +
+ + +
+ + +
+
+
+ 🎯 Deploy to Search Head Cluster + + ● Checking... + +
+ +
+ +
+ + +
+ +
+
+ Splunk credentials for applying shcluster-bundle (optional if using default) +
+
+ + +
+
+
+ +
-
- - -
-
📦 Applications
- -
-
-
-

Loading applications...

-
+ + +
+
+ Deploying applications to Git... Please wait
+ +
+
- -
-
-
Deploying applications to Git... Please wait
-
- -
- ✅ Applications successfully deployed to Git! -
- -
- ❌ Error occurred while deploying applications -
- -
+ + - \ No newline at end of file diff --git a/apps/pusher_app_prem/local/usage_stats.json b/apps/pusher_app_prem/local/usage_stats.json index 5dda266c..7163ffaf 100644 --- a/apps/pusher_app_prem/local/usage_stats.json +++ b/apps/pusher_app_prem/local/usage_stats.json @@ -1 +1 @@ -{"pushes_today": 10, "pushes_total": 19, "last_push_date": "2026-02-01", "apps_pushed": []} \ No newline at end of file +{"pushes_today": 7, "pushes_total": 28, "last_push_date": "2026-02-06", "apps_pushed": []} \ No newline at end of file