From e7be2c8f38a0ecfa1f3006ed62114f8923ea76c7 Mon Sep 17 00:00:00 2001 From: Splunk Git Pusher Date: Sat, 24 Jan 2026 21:55:04 +0100 Subject: [PATCH] App_Pusher Pushed by: unknown_user Timestamp: 2026-01-24T21:55:04.885348 --- .../pusher_app/appserver/static/git_pusher.js | 406 ++++++++++++++++++ apps/pusher_app/bin/README | 1 + apps/pusher_app/bin/git_pusher.pid | 1 + apps/pusher_app/bin/git_pusher.py | 319 ++++++++++++++ apps/pusher_app/bin/start_git_pusher.sh | 5 + apps/pusher_app/certs/server.crt | 19 + apps/pusher_app/certs/server.key | 28 ++ apps/pusher_app/default/app.conf | 16 + .../default/data/ui/nav/default.xml | 8 + apps/pusher_app/default/data/ui/views/README | 1 + apps/pusher_app/local/app.conf | 3 + .../git_pusher_-_push_applications_to_git.xml | 255 +++++++++++ .../git_pusher_-_push_dashboards_to_git.xml | 254 +++++++++++ apps/pusher_app/metadata/default.meta | 35 ++ apps/pusher_app/metadata/local.meta | 19 + 15 files changed, 1370 insertions(+) create mode 100755 apps/pusher_app/appserver/static/git_pusher.js create mode 100755 apps/pusher_app/bin/README create mode 100755 apps/pusher_app/bin/git_pusher.pid create mode 100755 apps/pusher_app/bin/git_pusher.py create mode 100755 apps/pusher_app/bin/start_git_pusher.sh create mode 100755 apps/pusher_app/certs/server.crt create mode 100755 apps/pusher_app/certs/server.key create mode 100755 apps/pusher_app/default/app.conf create mode 100755 apps/pusher_app/default/data/ui/nav/default.xml create mode 100755 apps/pusher_app/default/data/ui/views/README create mode 100755 apps/pusher_app/local/app.conf create mode 100644 apps/pusher_app/local/data/ui/views/git_pusher_-_push_applications_to_git.xml create mode 100644 apps/pusher_app/local/data/ui/views/git_pusher_-_push_dashboards_to_git.xml create mode 100755 apps/pusher_app/metadata/default.meta create mode 100644 apps/pusher_app/metadata/local.meta diff --git a/apps/pusher_app/appserver/static/git_pusher.js b/apps/pusher_app/appserver/static/git_pusher.js new file mode 100755 index 00000000..ddb5ff7f --- /dev/null +++ b/apps/pusher_app/appserver/static/git_pusher.js @@ -0,0 +1,406 @@ +// ============================================ +// CHARGER LES DASHBOARDS DYNAMIQUEMENT +// ============================================ + +// Charger les applications +function loadAvailableApps() { + console.log("loadAvailableApps called"); + + const apiUrl = '/en-US/splunkd/__raw/services/apps/local?output_mode=json&count=0'; + + fetch(apiUrl) + .then(response => { + if (!response.ok) { + throw new Error('HTTP ' + response.status); + } + return response.json(); + }) + .then(data => { + console.log("Apps Response: Found " + (data.entry ? data.entry.length : 0) + " apps"); + + if (data.entry && data.entry.length > 0) { + const apps = data.entry + .filter(item => { + // Filtrer les apps système + const isHidden = item.content && item.content.is_visible === 0; + const appName = item.name; + // Exclure les apps système + const systemApps = ['launcher', 'splunk_monitoring_console', 'introspection']; + return !isHidden && !systemApps.includes(appName); + }) + .map(item => ({ + id: item.name, + name: item.content.label || item.name, + description: item.content.description || '' + })) + .sort((a, b) => a.name.localeCompare(b.name)); + + console.log("Filtered apps: " + apps.length); + populateAppsList(apps); + } else { + console.warn("No apps found"); + showAppsEmpty(); + } + }) + .catch(error => { + console.error("API Error:", error); + showAppsEmpty(); + }); +} + +function populateAppsList(apps) { + console.log("populateAppsList called with", apps.length, "apps"); + + const container = document.getElementById('dashboard-list'); + + if (!container) { + console.error("dashboard-list container not found"); + return; + } + + if (!apps || apps.length === 0) { + showAppsEmpty(); + return; + } + + let html = '
'; + html += ''; + html += ''; + html += '
'; + + apps.forEach((app, index) => { + const checkboxId = 'app-' + index; + html += '
'; + html += ''; + html += ''; + html += '
'; + }); + + container.innerHTML = html; + console.log('Successfully populated ' + apps.length + ' apps'); +} + +function showAppsEmpty() { + const container = document.getElementById('dashboard-list'); + if (container) { + container.innerHTML = '
No apps found
'; + } + console.log("Displayed empty state"); +} + +// Attendre que le DOM soit chargé +function initScript() { + console.log("initScript called"); + + // Charger les applications + loadAvailableApps(); + + // Attacher les event listeners au bouton + const pushBtn = document.getElementById('push-btn'); + if (pushBtn) { + console.log("Push button found, attaching listener"); + pushBtn.addEventListener('click', function(e) { + e.preventDefault(); + console.log("Push button clicked!"); + pushDashboards(); + }); + } else { + console.warn("Push button not found"); + } +} + +if (document.readyState === 'loading') { + document.addEventListener('DOMContentLoaded', function() { + console.log("DOM Ready - Initializing script..."); + setTimeout(function() { + initScript(); + }, 1000); + }); +} else { + console.log("DOM already ready - Initializing script..."); + setTimeout(function() { + initScript(); + }, 1000); +} + +function getFormKeyValue() { + try { + // Chercher dans les meta tags du DOM + const metaTag = document.querySelector('meta[name="splunk_form_key"]'); + if (metaTag) { + return metaTag.getAttribute('content'); + } + + // Chercher dans les cookies + const cookies = document.cookie.split(';'); + for (let cookie of cookies) { + const [name, value] = cookie.trim().split('='); + if (name === 'splunk_form_key') { + return decodeURIComponent(value); + } + } + + console.warn("Could not find form key, proceeding without it"); + return ''; + } catch (e) { + console.error("Error getting form key:", e); + return ''; + } +} + +function loadAvailableApps() { + console.log("loadAvailableApps called"); + + const apiUrl = '/en-US/splunkd/__raw/services/apps/local?output_mode=json&count=0'; + + fetch(apiUrl) + .then(response => { + if (!response.ok) { + throw new Error('HTTP ' + response.status); + } + return response.json(); + }) + .then(data => { + console.log("Apps Response: Found " + (data.entry ? data.entry.length : 0) + " apps"); + + if (data.entry && data.entry.length > 0) { + const apps = data.entry + .filter(item => { + // Filtrer les apps système + const isHidden = item.content && item.content.is_visible === 0; + const appName = item.name; + // Exclure les apps système + const systemApps = ['launcher', 'splunk_monitoring_console', 'introspection']; + return !isHidden && !systemApps.includes(appName); + }) + .map(item => ({ + id: item.name, + name: item.content.label || item.name, + description: item.content.description || '' + })) + .sort((a, b) => a.name.localeCompare(b.name)); + + console.log("Filtered apps: " + apps.length); + populateAppsList(apps); + } else { + console.warn("No apps found"); + showAppsEmpty(); + } + }) + .catch(error => { + console.error("API Error:", error); + showAppsEmpty(); + }); +} + +function populateAppsList(apps) { + console.log("populateAppsList called with", apps.length, "apps"); + + const container = document.getElementById('dashboard-list'); + + if (!container) { + console.error("dashboard-list container not found"); + return; + } + + if (!apps || apps.length === 0) { + showAppsEmpty(); + return; + } + + let html = '
'; + html += ''; + html += ''; + html += '
'; + + apps.forEach((app, index) => { + const checkboxId = 'app-' + index; + html += '
'; + html += ''; + html += ''; + html += '
'; + }); + + container.innerHTML = html; + console.log('Successfully populated ' + apps.length + ' apps'); +} + +function showAppsEmpty() { + const container = document.getElementById('dashboard-list'); + if (container) { + container.innerHTML = '
No apps found
'; + } + console.log("Displayed empty state"); +} + +function toggleSelectAll(checkbox) { + const checkboxes = document.querySelectorAll('#dashboard-list input[type="checkbox"]:not(#select-all)'); + checkboxes.forEach(cb => cb.checked = checkbox.checked); +} + +// ============================================ +// POUSSER LES DASHBOARDS VERS GIT +// ============================================ + +function pushDashboards() { + console.log("pushDashboards called"); + + const gitUrl = document.getElementById('git-url').value; + const gitBranch = document.getElementById('git-branch').value; + const gitToken = document.getElementById('git-token').value; + const commitMessage = document.getElementById('commit-message').value; + + console.log("Git URL:", gitUrl); + console.log("Git Branch:", gitBranch); + console.log("Commit Message:", commitMessage); + + const checkboxes = document.querySelectorAll('#dashboard-list input[type="checkbox"]:not(#select-all):checked'); + const selectedApps = Array.from(checkboxes).map(cb => ({ + id: cb.value, + name: cb.getAttribute('data-name') + })); + + console.log("Selected apps:", selectedApps); + + // Validation + if (!gitUrl.trim()) { + console.warn("Validation failed: No Git URL"); + showError('Please enter a Git repository URL'); + return; + } + + if (!gitToken.trim()) { + console.warn("Validation failed: No Git token"); + showError('Please enter your Git token or password'); + return; + } + + if (!commitMessage.trim()) { + console.warn("Validation failed: No commit message"); + showError('Please enter a commit message'); + return; + } + + if (selectedApps.length === 0) { + console.warn("Validation failed: No apps selected"); + showError('Please select at least one application'); + return; + } + + console.log("Validation passed, showing loading state..."); + + // Afficher le loading + document.getElementById('loading').style.display = 'block'; + document.getElementById('success-msg').style.display = 'none'; + document.getElementById('error-msg').style.display = 'none'; + document.getElementById('push-btn').disabled = true; + + // Préparer les données - passer les apps au lieu des dashboards + const payload = { + git_url: gitUrl, + git_branch: gitBranch, + git_token: gitToken, + apps: selectedApps, + commit_message: commitMessage, + timestamp: new Date().toISOString(), + user: getCurrentUser() + }; + + console.log("Payload prepared:", payload); + + // Appeler le script Python via serveur + callPushScript(payload); +} + +function callPushScript(payload) { + console.log("callPushScript called"); + console.log("Payload:", payload); + + // Construire l'URL vers le serveur Python sur le port 9999 en HTTP + const hostname = window.location.hostname; + const baseUrl = `http://${hostname}:9999`; + + const url = new URL('/push', baseUrl); + + // Ajouter les paramètres en query string + url.searchParams.append('git_url', payload.git_url); + url.searchParams.append('git_branch', payload.git_branch); + url.searchParams.append('git_token', payload.git_token); + url.searchParams.append('commit_message', payload.commit_message); + + // Encoder correctement les apps en JSON + const appsJson = JSON.stringify(payload.apps); + console.log("Apps JSON:", appsJson); + url.searchParams.append('apps', appsJson); + + url.searchParams.append('user', payload.user); + + console.log("Calling:", url.toString()); + + fetch(url.toString(), { + method: 'POST', + mode: 'no-cors' + }) + .then(response => { + // Avec no-cors, on ne peut pas lire response.json() + // Donc on suppose que si la requête arrive au serveur, c'est bon + console.log("Request sent successfully"); + document.getElementById('loading').style.display = 'none'; + document.getElementById('push-btn').disabled = false; + showSuccess('Push request sent! Check server logs for details.'); + resetForm(); + return; + }) + .catch(error => { + console.error('Fetch error:', error); + document.getElementById('loading').style.display = 'none'; + document.getElementById('push-btn').disabled = false; + showError('Network error: ' + error.message); + }); +} + +function getCurrentUser() { + try { + // Essayer plusieurs méthodes + if (Splunk && Splunk.util && typeof Splunk.util.getCurrentUser === 'function') { + return Splunk.util.getCurrentUser(); + } + + // Fallback: chercher dans le DOM ou retourner 'unknown' + return 'unknown_user'; + } catch (e) { + console.warn("Could not get current user:", e); + return 'unknown_user'; + } +} + +function showSuccess(message) { + const successMsg = document.getElementById('success-msg'); + document.getElementById('success-text').textContent = message; + successMsg.style.display = 'block'; + setTimeout(() => { + successMsg.style.display = 'none'; + }, 5000); +} + +function showError(message) { + const errorMsg = document.getElementById('error-msg'); + document.getElementById('error-text').textContent = 'X ' + message; + errorMsg.style.display = 'block'; +} + +function resetForm() { + document.getElementById('git-url').value = ''; + document.getElementById('git-branch').value = 'main'; + document.getElementById('git-token').value = ''; + document.getElementById('commit-message').value = ''; + document.querySelectorAll('#dashboard-list input[type="checkbox"]').forEach(cb => cb.checked = false); +} \ No newline at end of file diff --git a/apps/pusher_app/bin/README b/apps/pusher_app/bin/README new file mode 100755 index 00000000..9a70db09 --- /dev/null +++ b/apps/pusher_app/bin/README @@ -0,0 +1 @@ +This is where you put any scripts you want to add to this app. diff --git a/apps/pusher_app/bin/git_pusher.pid b/apps/pusher_app/bin/git_pusher.pid new file mode 100755 index 00000000..81d2a971 --- /dev/null +++ b/apps/pusher_app/bin/git_pusher.pid @@ -0,0 +1 @@ +919562 diff --git a/apps/pusher_app/bin/git_pusher.py b/apps/pusher_app/bin/git_pusher.py new file mode 100755 index 00000000..58b72cad --- /dev/null +++ b/apps/pusher_app/bin/git_pusher.py @@ -0,0 +1,319 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- + +import sys +import os +import json +import logging +import tempfile +import shutil +import subprocess +from datetime import datetime +from http.server import HTTPServer, BaseHTTPRequestHandler +from urllib.parse import parse_qs, urlparse + +# Configuration du logging +log_dir = '/opt/splunk/var/log/splunk' +os.makedirs(log_dir, exist_ok=True) + +logging.basicConfig( + level=logging.INFO, + format='%(asctime)s - %(name)s - %(levelname)s - %(message)s', + handlers=[ + logging.FileHandler(os.path.join(log_dir, 'git_pusher.log')), + logging.StreamHandler() + ] +) +logger = logging.getLogger('git_pusher') + + +class GitPusherRequestHandler(BaseHTTPRequestHandler): + """Handler pour les requêtes HTTP""" + + def do_OPTIONS(self): + """Traiter les requêtes OPTIONS (CORS preflight)""" + self.send_response(200) + self.send_header('Access-Control-Allow-Origin', '*') + self.send_header('Access-Control-Allow-Methods', 'POST, GET, OPTIONS') + self.send_header('Access-Control-Allow-Headers', 'Content-Type') + self.end_headers() + + def do_POST(self): + """Traiter les requêtes POST""" + # Envoyer les headers CORS EN PREMIER + self.send_response(200) + self.send_header('Content-type', 'application/json') + self.send_header('Access-Control-Allow-Origin', '*') + self.send_header('Access-Control-Allow-Methods', 'POST, GET, OPTIONS') + self.send_header('Access-Control-Allow-Headers', 'Content-Type') + self.end_headers() + + try: + logger.info(f"POST request to {self.path}") + + # Parser l'URL et les paramètres + parsed_url = urlparse(self.path) + query_params = parse_qs(parsed_url.query) + + logger.info(f"Query params keys: {list(query_params.keys())}") + + # Extraire les paramètres + git_url = query_params.get('git_url', [''])[0] + git_branch = query_params.get('git_branch', ['main'])[0] + git_token = query_params.get('git_token', [''])[0] + commit_message = query_params.get('commit_message', [''])[0] + + # Accepter soit 'apps' soit 'dashboards' + apps_json = query_params.get('apps', query_params.get('dashboards', ['[]']))[0] + user = query_params.get('user', ['unknown'])[0] + + logger.info(f"Parameters received: git_url={git_url}, branch={git_branch}, user={user}") + logger.info(f"Raw apps_json: '{apps_json}'") + + # Parser les apps + try: + # parse_qs décode déjà, mais au cas où + if isinstance(apps_json, str): + apps = json.loads(apps_json) + else: + apps = apps_json + except (json.JSONDecodeError, TypeError) as e: + logger.error(f"JSON parse error: {e} - trying to parse: {apps_json}") + apps = [] + + logger.info(f"Parsed apps: {len(apps)} items - {apps}") + + # Valider + if not git_url or not git_token or not commit_message or not apps: + logger.warning(f"Validation failed: git_url={bool(git_url)}, git_token={bool(git_token)}, commit_message={bool(commit_message)}, apps={len(apps)}") + response = { + 'status': 'error', + 'message': 'Missing required parameters' + } + self.wfile.write(json.dumps(response).encode()) + return + + # Créer un répertoire temporaire + temp_dir = tempfile.mkdtemp(prefix='splunk_git_') + logger.info(f"Created temp directory: {temp_dir}") + + try: + # Préparer l'URL Git + git_url_with_token = self.prepare_git_url(git_url, git_token) + + # Cloner + logger.info("Cloning repository...") + self.clone_repository(temp_dir, git_url_with_token, git_branch) + + # Récupérer TOUTES les applications (dossiers complets) + logger.info("Fetching applications from Splunk...") + dashboard_contents = self.fetch_apps_directories(apps) + + # Créer le dossier apps + apps_dir = os.path.join(temp_dir, 'apps') + os.makedirs(apps_dir, exist_ok=True) + + # Copier les applications + logger.info("Copying applications to repository...") + for app_data in dashboard_contents: + app_name = app_data['name'] + app_path = app_data['path'] + dest_path = os.path.join(apps_dir, app_name) + + if os.path.exists(app_path): + logger.info(f"Copying app {app_name} from {app_path}") + shutil.copytree(app_path, dest_path) + logger.info(f"Copied app: {app_name}") + else: + logger.warning(f"App path not found: {app_path}") + + # Configurer git + logger.info("Configuring git...") + subprocess.run(['git', 'config', 'user.email', 'splunk@splunk.local'], + cwd=temp_dir, capture_output=True) + subprocess.run(['git', 'config', 'user.name', 'Splunk Git Pusher'], + cwd=temp_dir, capture_output=True) + + # Commit et push + logger.info("Adding files...") + subprocess.run(['git', 'add', '-A'], cwd=temp_dir, capture_output=True) + + full_message = f"{commit_message}\n\nPushed by: {user}\nTimestamp: {datetime.now().isoformat()}" + logger.info("Committing...") + result = subprocess.run(['git', 'commit', '-m', full_message], + cwd=temp_dir, capture_output=True, text=True) + + if result.returncode != 0: + logger.warning(f"Commit may have failed or had no changes: {result.stderr}") + + logger.info("Pushing...") + result = subprocess.run(['git', 'push', 'origin', git_branch], + cwd=temp_dir, capture_output=True, text=True, timeout=60) + + if result.returncode != 0: + raise Exception(f"Push failed: {result.stderr}") + + logger.info("Push successful!") + response = { + 'status': 'success', + 'message': f'Successfully pushed {len(dashboard_contents)} dashboards from {len(apps)} application(s) to Git', + 'dashboards_pushed': len(dashboard_contents) + } + self.wfile.write(json.dumps(response).encode()) + + finally: + logger.info(f"Cleaning up {temp_dir}") + shutil.rmtree(temp_dir, ignore_errors=True) + + except Exception as e: + logger.error(f"Error: {str(e)}", exc_info=True) + response = { + 'status': 'error', + 'message': f'Error: {str(e)}' + } + self.wfile.write(json.dumps(response).encode()) + + def log_message(self, format, *args): + """Éviter les logs HTTP par défaut""" + logger.debug(format % args) + + @staticmethod + def prepare_git_url(git_url, token): + """Préparer l'URL Git avec le token""" + if git_url.startswith('https://'): + parts = git_url.replace('https://', '').split('/') + return f"https://{token}@{'/'.join(parts)}" + return git_url + + @staticmethod + def clone_repository(dest_dir, git_url, branch): + """Cloner le repository""" + try: + cmd = ['git', 'clone', '--depth', '1', '--branch', branch, git_url, dest_dir] + result = subprocess.run(cmd, capture_output=True, text=True, timeout=60) + + if result.returncode != 0: + raise Exception(f"Clone failed: {result.stderr}") + + logger.info("Repository cloned successfully") + except subprocess.TimeoutExpired: + raise Exception("Git clone operation timed out") + except FileNotFoundError: + raise Exception("Git is not installed on this system") + + @staticmethod + def fetch_apps_directories(apps): + """Récupérer les dossiers complets des applications""" + logger.info(f"Fetching directories for {len(apps)} applications") + + splunk_home = '/opt/splunk' + apps_base_path = os.path.join(splunk_home, 'etc', 'apps') + + app_directories = [] + + for app in apps: + app_id = app.get('id') or app.get('app_id') + app_path = os.path.join(apps_base_path, app_id) + + logger.info(f"Checking app directory: {app_path}") + + if os.path.isdir(app_path): + app_directories.append({ + 'name': app_id, + 'path': app_path, + 'size': sum(os.path.getsize(os.path.join(dirpath, filename)) + for dirpath, dirnames, filenames in os.walk(app_path) + for filename in filenames) + }) + logger.info(f"Found app: {app_id} at {app_path}") + else: + logger.warning(f"App directory not found: {app_path}") + + logger.info(f"Successfully found {len(app_directories)} application directories") + return app_directories + """Récupérer TOUS les dashboards de chaque application""" + logger.info(f"Fetching dashboards from {len(apps)} applications") + + import urllib.request + import urllib.error + import ssl + import base64 + + # Ignorer les certificats SSL auto-signés + ssl_context = ssl.create_default_context() + ssl_context.check_hostname = False + ssl_context.verify_mode = ssl.CERT_NONE + + dashboard_contents = [] + + # Lire le fichier de configuration Splunk pour obtenir les credentials + # Ou utiliser des credentials par défaut + splunk_username = os.environ.get('SPLUNK_USERNAME', 'admin') + splunk_password = os.environ.get('SPLUNK_PASSWORD', 'changeme') + + # Créer l'authentification Basic + credentials = base64.b64encode(f"{splunk_username}:{splunk_password}".encode()).decode() + + for app in apps: + app_id = app.get('id') or app.get('app_id') + logger.info(f"Fetching all dashboards from app: {app_id}") + + try: + # Récupérer la liste de TOUS les dashboards de cette app + api_url = f"https://127.0.0.1:8089/servicesNS/-/{app_id}/data/ui/views?output_mode=json&count=0" + + logger.debug(f"API URL: {api_url}") + + req = urllib.request.Request(api_url) + req.add_header('Authorization', f'Basic {credentials}') + + with urllib.request.urlopen(req, timeout=15, context=ssl_context) as response: + api_data = json.loads(response.read().decode('utf-8')) + + if 'entry' in api_data and len(api_data['entry']) > 0: + for entry in api_data['entry']: + try: + dashboard_id = entry.get('name') + content = entry.get('content', {}) + + # eai:data contient le XML complet du dashboard + dashboard_xml = content.get('eai:data', '') + + if dashboard_xml: + dashboard_contents.append({ + 'id': f"{app_id}_{dashboard_id}", + 'app': app_id, + 'content': dashboard_xml, + 'name': dashboard_id + }) + logger.debug(f"Fetched: {dashboard_id} from {app_id}") + except Exception as e: + logger.error(f"Error processing dashboard entry: {str(e)}") + + logger.info(f"Found {len([d for d in dashboard_contents if d['app'] == app_id])} dashboards in {app_id}") + else: + logger.warning(f"No dashboards found in app {app_id}") + + except urllib.error.HTTPError as e: + logger.error(f"HTTP {e.code} when fetching app {app_id}: {e.reason}") + except urllib.error.URLError as e: + logger.error(f"Cannot reach Splunk API for app {app_id}: {e.reason}") + except Exception as e: + logger.error(f"Error fetching dashboards from {app_id}: {str(e)}") + + logger.info(f"Successfully fetched {len(dashboard_contents)} dashboards total") + return dashboard_contents + + +def start_server(port=9999): + """Démarrer le serveur HTTP""" + server = HTTPServer(('0.0.0.0', port), GitPusherRequestHandler) + logger.info(f"Git Pusher server listening on 0.0.0.0:{port} (HTTP)") + server.serve_forever() + + +if __name__ == '__main__': + # Démarrer le serveur en background + port = 9999 + logger.info(f"Starting Git Pusher on port {port}") + start_server(port) \ No newline at end of file diff --git a/apps/pusher_app/bin/start_git_pusher.sh b/apps/pusher_app/bin/start_git_pusher.sh new file mode 100755 index 00000000..951e1a0e --- /dev/null +++ b/apps/pusher_app/bin/start_git_pusher.sh @@ -0,0 +1,5 @@ +#!/bin/bash +export SPLUNK_USERNAME=admin +export SPLUNK_PASSWORD='2312Jocpam!?' +python3 /opt/splunk/etc/apps/pusher_app/bin/git_pusher.py > /opt/splunk/var/log/splunk/git_pusher_startup.log 2>&1 & +echo $! > /opt/splunk/etc/apps/pusher_app/bin/git_pusher.pid \ No newline at end of file diff --git a/apps/pusher_app/certs/server.crt b/apps/pusher_app/certs/server.crt new file mode 100755 index 00000000..11fa39a7 --- /dev/null +++ b/apps/pusher_app/certs/server.crt @@ -0,0 +1,19 @@ +-----BEGIN CERTIFICATE----- +MIIDCTCCAfGgAwIBAgIUCuKo8SLloS5cjBOR04+X6ayZ40cwDQYJKoZIhvcNAQEL +BQAwFDESMBAGA1UEAwwJbG9jYWxob3N0MB4XDTI2MDEyMzIyMTIxOFoXDTI3MDEy +MzIyMTIxOFowFDESMBAGA1UEAwwJbG9jYWxob3N0MIIBIjANBgkqhkiG9w0BAQEF +AAOCAQ8AMIIBCgKCAQEAs0vF6sFTseKgZC1nZ6CVZdw45yk1Ni0W9Mc24KZ9NKCJ +rP0tHy0hs6mME/sq8DV1fh0YtqIvBCxcKEE84/cVXmUfZF9JRXO95734+JGPmo07 +zpiu7p3r4WyIWmCXX5VB0UkMEXsPQmonqG1Kwtz+R1cfgis2lUk+xsC2zSjER8l4 +2UODjHvtD25usgxKjpwPrCuZt43miArnVnwfB8OLbAqpwQeYIf18bPt/TrnQsdgd +ZZiQdE6UTaJ5xhqztwpYJO9pvZA24Bi3bGNfBciITds5RCGY2wQo8yxbeJsidTuW +7Z64DK9t33oVnB2PqlP6hVGD5Agthsv9ehRPxdd3MwIDAQABo1MwUTAdBgNVHQ4E +FgQUy0dni+ogqC7YuvfD/Pn0AuebsXQwHwYDVR0jBBgwFoAUy0dni+ogqC7YuvfD +/Pn0AuebsXQwDwYDVR0TAQH/BAUwAwEB/zANBgkqhkiG9w0BAQsFAAOCAQEAlyxg +vR15lsYp4TxJPi1WPzLZl1e6ewTl8GhyE1saxS8LRtyTyr8sa9EFRLQ0OIsqYrUw +zZi7FIDoDPZDKpd0/+U94UKlhUuPUyufQwl5vNu0A+SEpwKeznUMaj4Y98tHvVGd +1SCndZBWn/v2U4nXqHoTd6Y0xEOga0jUEsUMBckNC236BTo88Zk65/oa9Gncyb27 +9vGVCbmPyzE70H4KFoVtxkoZrKywn+0ajHhgH5gqZNRPWpe6i8xTbMAeIXkCjmWL +LmOA7MkjeQBBEWewu4vMOXsvf+gCtxUj5owsAcOQlZ3g72Sng4MeMjuVx4ZRVxX9 +fj+vCP9EFI8rX48tjQ== +-----END CERTIFICATE----- diff --git a/apps/pusher_app/certs/server.key b/apps/pusher_app/certs/server.key new file mode 100755 index 00000000..8b790585 --- /dev/null +++ b/apps/pusher_app/certs/server.key @@ -0,0 +1,28 @@ +-----BEGIN PRIVATE KEY----- +MIIEuwIBADANBgkqhkiG9w0BAQEFAASCBKUwggShAgEAAoIBAQCzS8XqwVOx4qBk +LWdnoJVl3DjnKTU2LRb0xzbgpn00oIms/S0fLSGzqYwT+yrwNXV+HRi2oi8ELFwo +QTzj9xVeZR9kX0lFc73nvfj4kY+ajTvOmK7unevhbIhaYJdflUHRSQwRew9Caieo +bUrC3P5HVx+CKzaVST7GwLbNKMRHyXjZQ4OMe+0Pbm6yDEqOnA+sK5m3jeaICudW +fB8Hw4tsCqnBB5gh/Xxs+39OudCx2B1lmJB0TpRNonnGGrO3Clgk72m9kDbgGLds +Y18FyIhN2zlEIZjbBCjzLFt4myJ1O5btnrgMr23fehWcHY+qU/qFUYPkCC2Gy/16 +FE/F13czAgMBAAECggEAMrEMrvej0xpQ4KHZp3nGY3sk9242JjAPWntsb42CvrtY +0XjvJe5bpfEcspWDqVBj/Jj7YL9v7Y0hLRxsu8Mi3oJWoskx7RnxKjES0CxPXpHp +w9p1Mu+hPiWyU2MVySdo6WPuro6NXOiod70WswtKNR9TwDi5gPGpdwYLaOvKusSp +Rncm0m0H3IBhgVA691X0AUIomAW3Wmh+5If1XHfjrNHTB8cjcNf6koPMkCqHCEZ9 +wtINxOJior+gGkjMXaDszqzNlicVBXFEFjaXWcp38xAif1uimpqKsRzZEF6RAUzi +H7cI3aF2dXG3C9l6Byi7OSgd8X4JUnE0dlCpC7qweQKBgQDgvoavo8G0kYruCUIQ +6vcSs1YBByOkl6yZBCZWk10NgRpU1wyu9zmlvEwNVlUfALs5eoLxnhe8Wklq0ckQ +r/Rl+r/lj/MZUFn49TgUCsUOIi/G7nWQG0bPo4bCB2QXsAiKdY+KZeC56620uyom +1VY+nS3y8O4EP0YHX0qHFfmIZwKBgQDMOywO0DSrZMDyvmbwL0ISzHRcNpRn0jk7 +pEtzM/VOx+v0O93E+5OygzmXlBKjF0MwMXBidf8IZu4xO8qWqAM4EP2DD0cpoS1Z +WiHHkc5NZhjgeG6C4XaCXR++7CuY25VKKe01yz/+j51linDD8OAibKUspkjVufEN +R/AT0GFLVQKBgBxMYTEkcXOHD/NA/yyaKVoVcrLWb0p+PqFVwG4OSB03MFWWbmZp +gry3pOvY/wbUVL68CljaCysQQ0ZL/AE55pAgrqD9KyL41xtd5R3A7WcGLvXheLQY +eyYR9RnhTF0fMTQd8WD/yvgeENU86+XP3vgrWmnIpG+sd+jdusifn7fpAn9QkwfO +0FX3SMjW/EegewSWZhOCTgY+77Gk1izuRpGBg16T/QqBrL+Yri0KoGC593OKj/bG +4ca8id9vjSdgSOj8NbfO/TgWNICvv9+T3PKHlsA5z0nKWSloRVVA/ew1YmyD1gbA +MnAM/pwac4QJyf6jljmUZAZYTAPOOZN+PbglAoGBAJ9cOGDgT+BCOoNc0T1GJDAk +xOR8d+tD+j4JH5IVxB51DXjJOZxw9U3XhNH1OcE0x3fRzKJOtlQLxP6fHYVtMVFq +VpeekmTtJ9OfMg68ELOlf7ykA3GhMJ3FarM6e8+X+KliGf6ND4HBMb112FlMgIi6 +yYi7sfSL53Dzp1Q2DxXV +-----END PRIVATE KEY----- diff --git a/apps/pusher_app/default/app.conf b/apps/pusher_app/default/app.conf new file mode 100755 index 00000000..e473dbe9 --- /dev/null +++ b/apps/pusher_app/default/app.conf @@ -0,0 +1,16 @@ +# +# Splunk app configuration file +# + +[install] +is_configured = 0 + +[ui] +is_visible = true +label = Pusher + +[launcher] +author = +description = +version = 1.0.0 + diff --git a/apps/pusher_app/default/data/ui/nav/default.xml b/apps/pusher_app/default/data/ui/nav/default.xml new file mode 100755 index 00000000..8e98eadb --- /dev/null +++ b/apps/pusher_app/default/data/ui/nav/default.xml @@ -0,0 +1,8 @@ + diff --git a/apps/pusher_app/default/data/ui/views/README b/apps/pusher_app/default/data/ui/views/README new file mode 100755 index 00000000..6cf74f0b --- /dev/null +++ b/apps/pusher_app/default/data/ui/views/README @@ -0,0 +1 @@ +Add all the views that your app needs in this directory diff --git a/apps/pusher_app/local/app.conf b/apps/pusher_app/local/app.conf new file mode 100755 index 00000000..78666a91 --- /dev/null +++ b/apps/pusher_app/local/app.conf @@ -0,0 +1,3 @@ +[ui] + +[launcher] diff --git a/apps/pusher_app/local/data/ui/views/git_pusher_-_push_applications_to_git.xml b/apps/pusher_app/local/data/ui/views/git_pusher_-_push_applications_to_git.xml new file mode 100644 index 00000000..7cc88dfa --- /dev/null +++ b/apps/pusher_app/local/data/ui/views/git_pusher_-_push_applications_to_git.xml @@ -0,0 +1,255 @@ + + + + Push Splunk applications to Git repository + + + + | rest /services/apps/local | search disabled=0 | fields name, label, description | sort label + -4h@h + now + + + + + Configuration & Application Selection + + + +
+
+ ℹ️ Configure your Git settings and select the applications you want to push to your repository. +
+ +
+ + +
+ +
+ + +
+ +
+ + +
+ +
+ +
+
+
+ Loading applications... +
+
+ Select one or more applications to push +
+ +
+ + +
+ +
+ + +
+ +
+
+ Pushing applications to Git... +
+ +
+ ✓ Applications successfully pushed to Git! +
+ +
+ ✗ Error occurred while pushing applications +
+
+ +
+
+ + + + Push History + + + index=_internal source=*git_pusher* action=push_attempt | table _time, user, apps, commit_message, status, error_msg | reverse | rename _time as "Timestamp", user as "User", apps as "Applications", commit_message as "Message", status as "Status", error_msg as "Error" | head 20 + -30d@d + now + + + + {"success": "#28a745", "error": "#dc3545", "pending": "#ffc107"} + +
+
+
+ +
\ No newline at end of file diff --git a/apps/pusher_app/local/data/ui/views/git_pusher_-_push_dashboards_to_git.xml b/apps/pusher_app/local/data/ui/views/git_pusher_-_push_dashboards_to_git.xml new file mode 100644 index 00000000..79d34cc9 --- /dev/null +++ b/apps/pusher_app/local/data/ui/views/git_pusher_-_push_dashboards_to_git.xml @@ -0,0 +1,254 @@ + + + Push Splunk dashboards to Git repository + + + + | rest /services/data/ui/views | search title!="" | fields label, id, eai:acl.app | rename label as "Dashboard Name", id as "dashboard_id", "eai:acl.app" as "app" | sort "Dashboard Name" + -4h@h + now + + + + + Configuration & Dashboard Selection + + + +
+
+ ℹ️ Configure your Git settings and select the dashboards you want to push to your repository. +
+ +
+ + +
+ +
+ + +
+ +
+ + +
+ +
+ +
+
+
+ Loading dashboards... +
+
+ Select one or more dashboards to push +
+ +
+ + +
+ +
+ + +
+ +
+
+ Pushing dashboards to Git... +
+ +
+ ✓ Dashboards successfully pushed to Git! +
+ +
+ ✗ Error occurred while pushing dashboards +
+
+ +
+
+ + + + Push History + + + index=_internal source=*git_pusher* action=push_attempt | table _time, user, dashboards, commit_message, status, error_msg | reverse | rename _time as "Timestamp", user as "User", dashboards as "Dashboards", commit_message as "Message", status as "Status", error_msg as "Error" | head 20 + -30d@d + now + + + + {"success": "#28a745", "error": "#dc3545", "pending": "#ffc107"} + +
+
+
+ +
\ No newline at end of file diff --git a/apps/pusher_app/metadata/default.meta b/apps/pusher_app/metadata/default.meta new file mode 100755 index 00000000..b77b8cb9 --- /dev/null +++ b/apps/pusher_app/metadata/default.meta @@ -0,0 +1,35 @@ + +# Application-level permissions + +[] +access = read : [ * ], write : [ admin, power ] + +### EVENT TYPES + +[eventtypes] +export = system + + +### PROPS + +[props] +export = system + + +### TRANSFORMS + +[transforms] +export = system + + +### LOOKUPS + +[lookups] +export = system + + +### VIEWSTATES: even normal users should be able to create shared viewstates + +[viewstates] +access = read : [ * ], write : [ * ] +export = system diff --git a/apps/pusher_app/metadata/local.meta b/apps/pusher_app/metadata/local.meta new file mode 100644 index 00000000..2e9cddbb --- /dev/null +++ b/apps/pusher_app/metadata/local.meta @@ -0,0 +1,19 @@ +[app/ui] +version = 10.0.2 +modtime = 1769115948.043388000 + +[app/launcher] +version = 10.0.2 +modtime = 1769115948.046389000 + +[views/git_pusher_-_push_dashboards_to_git] +access = read : [ admin ], write : [ admin ] +export = system +owner = admin +version = 10.0.2 +modtime = 1769276443.812957000 + +[views/git_pusher_-_push_applications_to_git] +owner = admin +version = 10.0.2 +modtime = 1769283309.402236000