Skip to content

Commit 1035f22

Browse files
author
Artem Rys
authored
fix: revert helmut's library clean up
This reverts commit e947cb2.
1 parent 23deab5 commit 1035f22

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

61 files changed

+8606
-49
lines changed

poetry.lock

Lines changed: 65 additions & 47 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

pyproject.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@ classifiers = [
3232
include = ["pytest_splunk_addon/**/*.json", "pytest_splunk_addon/**/*.txt"]
3333

3434
[tool.poetry.dependencies]
35+
httplib2 = "*"
3536
python = "^3.7"
3637
pytest = ">5.4.0,<6.3"
3738
splunk-sdk = "^1.6"
Lines changed: 214 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,214 @@
1+
#
2+
# Copyright 2021 Splunk Inc.
3+
#
4+
# Licensed under the Apache License, Version 2.0 (the "License");
5+
# you may not use this file except in compliance with the License.
6+
# You may obtain a copy of the License at
7+
#
8+
# http://www.apache.org/licenses/LICENSE-2.0
9+
#
10+
# Unless required by applicable law or agreed to in writing, software
11+
# distributed under the License is distributed on an "AS IS" BASIS,
12+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
# See the License for the specific language governing permissions and
14+
# limitations under the License.
15+
#
16+
"""
17+
Summary
18+
=======
19+
A module which deals with a Splunk app
20+
"""
21+
22+
import logging
23+
import os
24+
from builtins import object
25+
26+
from pytest_splunk_addon.helmut.manager.confs import Confs
27+
28+
29+
class App(object):
30+
"""
31+
A representation of a Splunk Application
32+
33+
@ivar __logger: The logger we use
34+
@ivar _required_indexes: Sub classes can indexes to this list and they will
35+
be added as part of the setup. The indexes must be
36+
know by the filegetter.py, the format of each
37+
entry is a dictionary with the same parameters as
38+
the filegetter.get_file method.
39+
@ivar _required_configs: A list of paths to config files that should be
40+
copied to the app's local config directory as part
41+
of the setup
42+
@ivar _required_lookups: A list of paths to lookup files that should be
43+
copied to the app's lookup directory as part of
44+
the setup
45+
@ivar _name: The name of this app
46+
@ivar _splunk: The splunk instance this app belongs to
47+
@ivar _config_manager: The config manager for this app
48+
@ivar _shared_service: The shared service for this app.
49+
@ivar package: The package this app will be installed from or None if not
50+
package exists.
51+
"""
52+
53+
DEFAULT_NAMESPACE = "nobody:{app_name}"
54+
55+
def __init__(self, name, splunk, package=None):
56+
"""
57+
Constructs this app
58+
59+
@param name: The name of the app, should be all lower case.
60+
@type name: str
61+
62+
@param splunk: The splunk instance this app belongs to.
63+
@type splunk: Splunk
64+
65+
@param package: An optional path to any package this app can be
66+
installed from
67+
@type package: str
68+
"""
69+
super(App, self).__init__()
70+
71+
self.__logger = logging.getLogger("App-{0}".format(name))
72+
73+
self._name = name
74+
self._splunk = splunk
75+
self._confs = None
76+
77+
self.package = package
78+
79+
@property
80+
def confs(self):
81+
"""
82+
The confs manager that is used for this app.
83+
84+
The manager will have a namespace that has the app portion set to this
85+
app.
86+
87+
@rtype: L{Confs}
88+
"""
89+
if self._confs is None:
90+
self._confs = self._create_confs_manager()
91+
return self._confs
92+
93+
def _create_confs_manager(self):
94+
"""
95+
Creates a confs manager for this app.
96+
97+
It uses the same connector factory as the Splunk instance.
98+
99+
@return: The newly created confs manager.
100+
@rtype: L{Confs}
101+
"""
102+
return Confs(self.splunk.default_connector)
103+
104+
@property
105+
def namespace(self):
106+
"""
107+
The namespace for this app.
108+
109+
@rtype: str
110+
"""
111+
return self.DEFAULT_NAMESPACE.format(app_name=self._name)
112+
113+
@confs.setter
114+
def confs(self, value):
115+
"""
116+
Updates the confs manager for this app.
117+
118+
@param value: The new manager.
119+
@type value: L{Confs}
120+
"""
121+
self._confs = value
122+
123+
@property
124+
def installed(self):
125+
"""
126+
Checks too see whether this app is already installed or not.
127+
128+
It does this by checking if the directory exists which means that there
129+
is no guarantee that it was installed this session.
130+
131+
@rtype: bool
132+
"""
133+
return self.splunk.has_app(self.name)
134+
135+
@property
136+
def name(self):
137+
"""
138+
The name for this app
139+
140+
@rtype: str
141+
"""
142+
return self._name
143+
144+
@property
145+
def splunk(self):
146+
"""
147+
The Splunk instance this app belongs to
148+
149+
@rtype: L{Splunk}
150+
"""
151+
return self._splunk
152+
153+
@property
154+
def apps_dir(self):
155+
"""
156+
The path to the directory that splunk stores it's apps
157+
158+
@rtype: str
159+
"""
160+
return self.splunk.apps_dir
161+
162+
@property
163+
def install_path(self):
164+
"""
165+
The path to the directory where this app will be/is installed
166+
167+
@rtype: str
168+
"""
169+
return os.path.join(self.apps_dir, self.name)
170+
171+
def can_install(self):
172+
"""
173+
Checks if this app can be installed meaning if a package has been
174+
supplied.
175+
176+
@rtype: bool
177+
@return: True if this app can be installed
178+
"""
179+
return self.package is not None
180+
181+
def install(self):
182+
"""
183+
Installs this app.
184+
185+
@rtype: bool
186+
@return: True if the app was installed and splunk needs to restart
187+
"""
188+
self._verify_can_install()
189+
return self.splunk.install_app(self.name, self.package)
190+
191+
def _verify_can_install(self):
192+
"""
193+
Checks that this app can be installed and raising an exception if it
194+
can't.
195+
196+
@raise AppHasNoPackage: If the app can't be installed.
197+
"""
198+
if not self.package:
199+
raise AppHasNoPackage(self.name)
200+
201+
def uninstall(self):
202+
"""
203+
Uninstalls this app
204+
205+
@rtype: bool
206+
@return: True if the app was installed and has now been removed
207+
"""
208+
return self.splunk.uninstall_app(self.name)
209+
210+
211+
class AppHasNoPackage(RuntimeError):
212+
def __init__(self, app_name):
213+
msg = "The app {0} has no package to install from".format(app_name)
214+
super(AppHasNoPackage, self).__init__(msg)

pytest_splunk_addon/helmut/connector/__init__.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@
2121
@since: 2011-11-21
2222
"""
2323

24-
__all__ = ["sdk"]
24+
__all__ = ["sdk", "rest"]
2525

26+
from .rest import RESTConnector
2627
from .sdk import SDKConnector

pytest_splunk_addon/helmut/connector/base.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,8 @@
2222
"""
2323

2424
from abc import ABCMeta
25+
from builtins import range
26+
from builtins import str
2527

2628
from future.utils import with_metaclass
2729

Lines changed: 82 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,82 @@
1+
#
2+
# Copyright 2021 Splunk Inc.
3+
#
4+
# Licensed under the Apache License, Version 2.0 (the "License");
5+
# you may not use this file except in compliance with the License.
6+
# You may obtain a copy of the License at
7+
#
8+
# http://www.apache.org/licenses/LICENSE-2.0
9+
#
10+
# Unless required by applicable law or agreed to in writing, software
11+
# distributed under the License is distributed on an "AS IS" BASIS,
12+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
# See the License for the specific language governing permissions and
14+
# limitations under the License.
15+
#
16+
from future import standard_library
17+
18+
standard_library.install_aliases()
19+
from builtins import range
20+
import httplib2
21+
import logging
22+
import time
23+
import random
24+
from io import BytesIO
25+
from http.client import ResponseNotReady
26+
27+
RETRIES = 3
28+
29+
30+
def sdk_request_adapter(url, message, **kwargs):
31+
"""
32+
:param url: the URL to make the request to (including any query and fragment sections)
33+
:param message: a dictionary with the following keys:
34+
- method: The method for the request, typically ``GET``, ``POST``, or ``DELETE``.
35+
- headers: A list of pairs specifying the HTTP headers (for example: ``[('key': value), ...]``).
36+
- body: A string containing the body to send with the request (this string
37+
should default to '').
38+
:param kwargs: not used
39+
:return: response_dict, a dictionary with the following keys:
40+
- status: An integer containing the HTTP status code (such as 200 or 404).
41+
- reason: The reason phrase, if any, returned by the server.
42+
- headers: A list of pairs containing the response headers (for example, ``[('key': value), ...]``).
43+
- body: A stream-like object supporting ``read(size=None)`` and ``close()`` methods to get the body of the response.
44+
"""
45+
method = message.get("method", "GET").upper()
46+
body = message.get("body", "") if method == "POST" else None
47+
headers = dict(message.get("headers", []))
48+
h = httplib2.Http(disable_ssl_certificate_validation=True)
49+
for i in range(RETRIES):
50+
try:
51+
resp, content = h.request(url, method=method, body=body, headers=headers)
52+
break
53+
except ResponseNotReady:
54+
# splunk restart is still in progress
55+
time.sleep(30)
56+
except Exception as ex: # noqa: E722
57+
logging.getLogger("..connector").exception(
58+
"request failed, url=%s, attempts=%s", url, i + 1
59+
)
60+
if i == RETRIES - 1 or not _is_retry_safe(method, url):
61+
raise
62+
else:
63+
# intermediate network error
64+
time.sleep(random.randint(5, 17))
65+
66+
return {
67+
"status": resp.status,
68+
"reason": resp.reason,
69+
"headers": resp,
70+
"body": BytesIO(content),
71+
}
72+
73+
74+
def _is_retry_safe(method, url):
75+
if method in ("GET", "HEAD", "DELETE", "PUT"):
76+
# idempotent request
77+
return True
78+
elif method == "POST":
79+
for rest_url in ["/auth/login"]:
80+
if rest_url in url:
81+
return True
82+
return False

0 commit comments

Comments
 (0)