Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Splunkbase download updates courtesy res260 #322

Open
wants to merge 5 commits into
base: main
Choose a base branch
from

Conversation

pyth0n1c
Copy link
Contributor

@pyth0n1c pyth0n1c commented Nov 4, 2024

This larger PR aggregates a number of great changes courtesy @Res260 .
These updates fix broken logic to allow Apps to be downloaded from Splunkbase at contentctl test runtime

Res260 and others added 4 commits October 24, 2024 13:11
Do not serialize TestApp.hardcoded_path if it has value None. Fixes #…
Fix a bug where App_Base.getSplunkbasePath() was broken and did not w…
@Res260
Copy link
Contributor

Res260 commented Nov 5, 2024

There might be one more bug before it works. I currently have a problem with the "infer app path" part of the ansible task inside docker-splunk. I'm not sure what the fix is yet, I'm investigating. I'll post a screenshot tomorrow when I get back on my work computer, if you have an idea.

@Res260
Copy link
Contributor

Res260 commented Nov 5, 2024

Here is the error:

image

@Res260
Copy link
Contributor

Res260 commented Nov 6, 2024

Found the bug, will include the patch soon

@Res260
Copy link
Contributor

Res260 commented Nov 6, 2024

Here is the patch. Sorry again for the manual work, after this PR I'll probably be able to contribute back normally to upstream.

This fixes the error mentioned in the last comment, as well as adds a proper "wait for app installation" that actually checks for app installation instead of assuming that apps are installed once you get a connection to the splunk API (which is not the case).

From cc6c63fe160bb9f76c740a371be3f4ca5af4c3b2 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=C3=89milio=20Gonzalez?= <[email protected]>
Date: Tue, 5 Nov 2024 20:41:59 -0500
Subject: =?UTF-8?q?Fix=20du=20setup=20initial=20+=20t=C3=A9l=C3=A9chargeme?=
 =?UTF-8?q?nt=20depuis=20splunkbase.?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

---
 .../DetectionTestingInfrastructure.py         | 44 +++++++++++++++++--
 ...DetectionTestingInfrastructureContainer.py |  5 +++
 contentctl/objects/config.py                  |  6 +--
 3 files changed, 48 insertions(+), 7 deletions(-)

diff --git a/contentctl/actions/detection_testing/infrastructures/DetectionTestingInfrastructure.py b/contentctl/actions/detection_testing/infrastructures/DetectionTestingInfrastructure.py
index d443ea2..8bb1f1e 100644
--- a/contentctl/actions/detection_testing/infrastructures/DetectionTestingInfrastructure.py
+++ b/contentctl/actions/detection_testing/infrastructures/DetectionTestingInfrastructure.py
@@ -1,3 +1,4 @@
+import logging
 import time
 import uuid
 import abc
@@ -17,12 +18,13 @@ from pydantic import ConfigDict, BaseModel, PrivateAttr, Field, dataclasses
 import requests                                                                                     # type: ignore
 import splunklib.client as client                                                                   # type: ignore
 from splunklib.binding import HTTPError                                                             # type: ignore
+from splunklib.client import Service
 from splunklib.results import JSONResultsReader, Message                                            # type: ignore
 import splunklib.results
 from urllib3 import disable_warnings
 import urllib.parse
 
-from contentctl.objects.config import test_common, Infrastructure
+from contentctl.objects.config import test_common, Infrastructure, ENTERPRISE_SECURITY_UID
 from contentctl.objects.enums import PostTestBehavior, AnalyticsType
 from contentctl.objects.detection import Detection
 from contentctl.objects.base_test import BaseTest
@@ -42,6 +44,12 @@ from contentctl.actions.detection_testing.progress_bar import (
     TestingStates
 )
 
+LOG = logging.getLogger(__name__)
+LOG.setLevel(logging.DEBUG)
+handler = logging.StreamHandler()
+handler.setLevel(logging.DEBUG)
+LOG.addHandler(handler)
+
 
 class SetupTestGroupResults(BaseModel):
     exception: Union[Exception, None] = None
@@ -107,6 +115,7 @@ class DetectionTestingInfrastructure(BaseModel, abc.ABC):
 
     def __init__(self, **data):
         super().__init__(**data)
+        self._conn: Optional[Service] = None
 
     # TODO: why not use @abstractmethod
     def start(self):
@@ -138,7 +147,8 @@ class DetectionTestingInfrastructure(BaseModel, abc.ABC):
         try:
             for func, msg in [
                 (self.start, "Starting"),
-                (self.get_conn, "Waiting for App Installation"),
+                (self.get_conn, "Getting initial connection"),
+                (self.wait_for_app_installation, "Waiting for App Installation"),
                 (self.configure_conf_file_datamodels, "Configuring Datamodels"),
                 (self.create_replay_index, f"Create index '{self.sync_obj.replay_index}'"),
                 (self.get_all_indexes, "Getting all indexes from server"),
@@ -210,6 +220,29 @@ class DetectionTestingInfrastructure(BaseModel, abc.ABC):
         except Exception as e:
             raise (Exception(f"Failure getting indexes: {str(e)}"))
 
+    def wait_for_app_installation(self):
+        config_apps = self.global_config.apps
+        installed_config_apps = []
+        while len(installed_config_apps) < len(config_apps):
+            try:
+                # Get apps installed in the Splunk instance
+                splunk_instance_apps = self.get_conn().apps.list()
+
+                # Try to find all the apps we want to be installed (config_apps)
+                installed_config_apps = []
+                for config_app in config_apps:
+                    for splunk_instance_app in splunk_instance_apps:
+                        if config_app.appid == splunk_instance_app.name:
+                            # For Enterprise Security, we need to make sure the app is also configured.
+                            if config_app.uid == ENTERPRISE_SECURITY_UID and splunk_instance_app.content.get('configured') != '1':
+                                continue
+                            installed_config_apps.append(config_app.appid)
+                LOG.debug("Apps in the Splunk instance: " + str(list(map(lambda x: x.name, splunk_instance_apps))))
+                LOG.debug(f"apps in contentctl package found in Splunk instance: {installed_config_apps}")
+            except Exception as e:
+                LOG.exception(e)
+            time.sleep(5)
+
     def get_conn(self) -> client.Service:
         try:
             if not self._conn:
@@ -218,9 +251,11 @@ class DetectionTestingInfrastructure(BaseModel, abc.ABC):
                 # continue trying to re-establish a connection until after
                 # the server has restarted
                 self.connect_to_api()
-        except Exception:
+        except Exception as e:
             # there was some issue getting the connection. Try again just once
+            LOG.exception(e)
             self.connect_to_api()
+
         return self._conn
 
     def check_for_teardown(self):
@@ -295,7 +330,7 @@ class DetectionTestingInfrastructure(BaseModel, abc.ABC):
     ):  
         try:
             # Set which roles should be configured. For Enterprise Security/Integration Testing,
-            # we must add some extra foles.
+            # we must add some extra roles.
             if self.global_config.enable_integration_testing:
                 roles = imported_roles + enterprise_security_roles
             else:
@@ -334,6 +369,7 @@ class DetectionTestingInfrastructure(BaseModel, abc.ABC):
             self.check_for_teardown()
             time.sleep(1)
             try:
                 _ = self.get_conn().get(
                     f"configs/conf-{conf_file_name}", app=app_name
                 )
diff --git a/contentctl/actions/detection_testing/infrastructures/DetectionTestingInfrastructureContainer.py b/contentctl/actions/detection_testing/infrastructures/DetectionTestingInfrastructureContainer.py
index f588703..37326f9 100644
--- a/contentctl/actions/detection_testing/infrastructures/DetectionTestingInfrastructureContainer.py
+++ b/contentctl/actions/detection_testing/infrastructures/DetectionTestingInfrastructureContainer.py
@@ -1,3 +1,5 @@
+import time
+
 from contentctl.actions.detection_testing.infrastructures.DetectionTestingInfrastructure import (
     DetectionTestingInfrastructure,
 )
@@ -25,6 +27,9 @@ class DetectionTestingInfrastructureContainer(DetectionTestingInfrastructure):
 
         self.container = self.make_container()
         self.container.start()
+        # There might be a small delay between the starting of the container and the binding of the ports for splunk.
+        # To avoid a "connection refused" error, wait a little bit before finishing the method call.
+        time.sleep(20)
 
     def finish(self):
         if self.container is not None:
diff --git a/contentctl/objects/config.py b/contentctl/objects/config.py
index 4228ba9..1931c15 100644
--- a/contentctl/objects/config.py
+++ b/contentctl/objects/config.py
@@ -31,7 +31,8 @@ from contentctl.helper.splunk_app import SplunkApp
 ENTERPRISE_SECURITY_UID = 263
 COMMON_INFORMATION_MODEL_UID = 1621
 
-SPLUNKBASE_URL = "https://splunkbase.splunk.com/app/{uid}/release/{version}/download"
+SPLUNKBASE_BASE_URL = "https://splunkbase.splunk.com"
+SPLUNKBASE_URL = SPLUNKBASE_BASE_URL + "/app/{uid}/release/{version}/download"
 
 
 # TODO (#266): disable the use_enum_values configuration
@@ -836,7 +837,6 @@ class test(test_common):
 
     def __init__(self, **kwargs):
         if "SPLUNKBASE_USERNAME" in os.environ:
-            breakpoint()
             kwargs['splunk_api_username'] = os.environ["SPLUNKBASE_USERNAME"]
         if "SPLUNKBASE_PASSWORD" in os.environ:
             kwargs['splunk_api_password'] = os.environ["SPLUNKBASE_PASSWORD"]
@@ -886,7 +886,7 @@ class test(test_common):
 
         container_paths = []
         for path in paths:
-            if path.startswith(SPLUNKBASE_URL):
+            if path.startswith(SPLUNKBASE_BASE_URL):
                 container_paths.append(path)
             else:
                 container_paths.append((self.getContainerAppDir()/pathlib.Path(path).name).as_posix())
-- 
2.34.1


@Res260
Copy link
Contributor

Res260 commented Nov 14, 2024

This PR should be closed and is replaced by #327

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants