Skip to content

Commit e35783d

Browse files
authored
Release/1.6.6 (#234)
* Add return code for invalid args block Return non-zero rc instead of None * Added headers argument to get requests so that additional headers can be passed in similar to post reqeusts * Added the option of always adding a particular header to a request. Also fixed unicode xml decoding error * Adding additional comments to denote that a headers object is possible to pass in as a kwarg * auto-doc all class constructor parameters * Searchcommands: add full support for unicode Python's cstringio doesn't support unicode characters, when encountering them Exceptions are raised. We've decided to use stringio instead, trading potential performance improvements for full unicode support. In Python 2, this may lead to a slight performance hit since we're no longer explicitly using the native version. However, in Python 3 stringio will automatically use the native version if available and there should be no noticeable performance changes. * update assertion * Make the explorer example compatible w/ Python 3 ... by using the six library * Update binding.py * Run CI against Splunk 7.2 * tests: relax restart timeout * Fix ssl verify to require certs when true (#233) - Correct logic to always look for cert files when verify=True - Pass key_file and cert_file to handler from HttpLib - Changed default value of verify to False for backward compat * Preparing Release 1.6.6 * Cleaned up CHANGELOG.md * Update version to 1.6.6 in User-Agent header * Rev version of examples/searchcommands_app/setup.py to 1.6.6
1 parent 9e82ab7 commit e35783d

File tree

17 files changed

+111
-70
lines changed

17 files changed

+111
-70
lines changed

.gitignore

+2-1
Original file line numberDiff line numberDiff line change
@@ -26,4 +26,5 @@ dist/
2626
examples/searchcommands_app/package/default/commands.conf
2727
examples/searchcommands_app/package/bin/packages
2828
tests/searchcommands/apps/app_with_logging_configuration/*.log
29-
*.observed
29+
*.observed
30+
venv/

.travis.yml

+1-1
Original file line numberDiff line numberDiff line change
@@ -30,8 +30,8 @@ before_install:
3030
- mkdir -p $SPLUNK_HOME/var/log/splunk
3131

3232
env:
33-
- SPLUNK_VERSION=6.6-sdk
3433
- SPLUNK_VERSION=7.0-sdk
34+
- SPLUNK_VERSION=7.2-sdk
3535

3636
language: python
3737

CHANGELOG.md

+12
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,17 @@
11
# Splunk SDK for Python Changelog
22

3+
## Versiom 1.6.6
4+
5+
### Bug fixes
6+
7+
* Fix ssl verify to require certs when true
8+
9+
### Minor changes
10+
11+
* Make the explorer example compatible w/ Python 3
12+
* Add full support for unicode in SearchCommands
13+
* Add return code for invalid_args block
14+
315
## Version 1.6.5
416

517
### Bug fixes

README.md

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
[![Build Status](https://travis-ci.org/splunk/splunk-sdk-python.svg?branch=master)](https://travis-ci.org/splunk/splunk-sdk-python)
22
# The Splunk Software Development Kit for Python
33

4-
#### Version 1.6.5
4+
#### Version 1.6.6
55

66
The Splunk Software Development Kit (SDK) for Python contains library code and
77
examples designed to enable developers to build applications using Splunk.

docs/conf.py

+2
Original file line numberDiff line numberDiff line change
@@ -248,3 +248,5 @@
248248

249249
# How to display URL addresses: 'footnote', 'no', or 'inline'.
250250
#texinfo_show_urls = 'footnote'
251+
252+
autoclass_content = 'both'

examples/explorer/explorer.py

+3-2
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,8 @@
2727
except ImportError:
2828
raise Exception("Add the SDK repository to your PYTHONPATH to run the examples "
2929
"(e.g., export PYTHONPATH=~/splunk-sdk-python.")
30-
import urllib
30+
31+
from splunklib.six.moves import urllib
3132

3233
PORT = 8080
3334

@@ -57,7 +58,7 @@ def main(argv):
5758
args.append(('owner', opts.kwargs['owner']))
5859

5960
# Encode these arguments
60-
args = urllib.urlencode(args)
61+
args = urllib.parse.urlencode(args)
6162

6263
# Launch the browser
6364
webbrowser.open("file://%s" % os.path.join(os.getcwd(), "explorer.html?%s" % args))

examples/explorer/server.py

+16-14
Original file line numberDiff line numberDiff line change
@@ -16,16 +16,18 @@
1616

1717
from __future__ import absolute_import
1818
from __future__ import print_function
19-
import splunklib.six.moves.SimpleHTTPServer
20-
import splunklib.six.moves.socketserver
21-
import urllib2
2219
import sys
23-
import StringIO
24-
from splunklib import six
20+
import os
21+
22+
sys.path.insert(0, os.path.join(os.path.dirname(__file__), "..", ".."))
23+
24+
from splunklib.six import iteritems
25+
from splunklib.six.moves import socketserver, SimpleHTTPServer, StringIO, urllib
2526

2627
PORT = 8080
2728

28-
class RedirectHandler(six.moves.SimpleHTTPServer.SimpleHTTPRequestHandler):
29+
30+
class RedirectHandler(SimpleHTTPServer.SimpleHTTPRequestHandler):
2931
def do_GET(self):
3032
redirect_url, headers = self.get_url_and_headers()
3133
if redirect_url is None:
@@ -83,13 +85,13 @@ def make_request(self, url, method, data, headers):
8385

8486
try:
8587
# Make the request
86-
request = urllib2.Request(url, data, headers)
88+
request = urllib.Request(url, data, headers)
8789
request.get_method = lambda: method
88-
response = urllib2.urlopen(request)
90+
response = urllib.urlopen(request)
8991

9092
# We were successful, so send the response code
9193
self.send_response(response.code, message=response.msg)
92-
for key, value in six.iteritems(dict(response.headers)):
94+
for key, value in iteritems(dict(response.headers)):
9395
# Optionally log the headers
9496
#self.log_message("%s: %s" % (key, value))
9597

@@ -105,16 +107,16 @@ def make_request(self, url, method, data, headers):
105107

106108
# Copy the response to the output
107109
self.copyfile(response, self.wfile)
108-
except urllib2.HTTPError as e:
110+
except urllib.HTTPError as e:
109111
# On errors, log the response code and message
110112
self.log_message("Code: %s (%s)", e.code, e.msg)
111113

112-
for key, value in six.iteritems(dict(e.hdrs)):
114+
for key, value in iteritems(dict(e.hdrs)):
113115
# On errors, we always log the headers
114116
self.log_message("%s: %s", key, value)
115117

116118
response_text = e.fp.read()
117-
response_file = StringIO.StringIO(response_text)
119+
response_file = StringIO(response_text)
118120

119121
# On errors, we also log the response text
120122
self.log_message("Response: %s", response_text)
@@ -135,10 +137,10 @@ def make_request(self, url, method, data, headers):
135137
# Finally, send the error itself
136138
self.copyfile(response_file, self.wfile)
137139

138-
class ReuseableSocketTCPServer(six.moves.socketserver.TCPServer):
140+
class ReuseableSocketTCPServer(socketserver.TCPServer):
139141
def __init__(self, *args, **kwargs):
140142
self.allow_reuse_address = True
141-
six.moves.socketserver.TCPServer.__init__(self, *args, **kwargs)
143+
socketserver.TCPServer.__init__(self, *args, **kwargs)
142144

143145
def serve(port = PORT):
144146
Handler = RedirectHandler

examples/searchcommands_app/setup.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -439,7 +439,7 @@ def run(self):
439439
setup(
440440
description='Custom Search Command examples',
441441
name=os.path.basename(project_dir),
442-
version='1.6.5',
442+
version='1.6.6',
443443
author='Splunk, Inc.',
444444
author_email='[email protected]',
445445
url='http://github.com/splunk/splunk-sdk-python',

splunklib/__init__.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -16,5 +16,5 @@
1616

1717
from __future__ import absolute_import
1818
from splunklib.six.moves import map
19-
__version_info__ = (1, 6, 5)
19+
__version_info__ = (1, 6, 6)
2020
__version__ = ".".join(map(str, __version_info__))

splunklib/binding.py

+38-22
Original file line numberDiff line numberDiff line change
@@ -25,30 +25,30 @@
2525
"""
2626

2727
from __future__ import absolute_import
28+
29+
import io
2830
import logging
2931
import socket
3032
import ssl
31-
from io import BytesIO
32-
33-
from splunklib.six.moves import urllib
34-
import io
3533
import sys
36-
3734
from base64 import b64encode
35+
from contextlib import contextmanager
3836
from datetime import datetime
3937
from functools import wraps
38+
from io import BytesIO
39+
from xml.etree.ElementTree import XML
40+
41+
from splunklib import six
4042
from splunklib.six import StringIO
43+
from splunklib.six.moves import urllib
4144

42-
from contextlib import contextmanager
45+
from .data import record
4346

44-
from xml.etree.ElementTree import XML
45-
from splunklib import six
4647
try:
4748
from xml.etree.ElementTree import ParseError
4849
except ImportError as e:
4950
from xml.parsers.expat import ExpatError as ParseError
5051

51-
from .data import record
5252

5353
__all__ = [
5454
"AuthenticationError",
@@ -449,6 +449,8 @@ class Context(object):
449449
:type username: ``string``
450450
:param password: The password for the Splunk account.
451451
:type password: ``string``
452+
:param headers: List of extra HTTP headers to send (optional).
453+
:type headers: ``list`` of 2-tuples.
452454
:param handler: The HTTP request handler (optional).
453455
:returns: A ``Context`` instance.
454456
@@ -465,7 +467,8 @@ class Context(object):
465467
c = binding.Context(cookie="splunkd_8089=...")
466468
"""
467469
def __init__(self, handler=None, **kwargs):
468-
self.http = HttpLib(handler, kwargs.get("verify", True))
470+
self.http = HttpLib(handler, kwargs.get("verify", False), key_file=kwargs.get("key_file"),
471+
cert_file=kwargs.get("cert_file")) # Default to False for backward compat
469472
self.token = kwargs.get("token", _NoAuthenticationToken)
470473
if self.token is None: # In case someone explicitly passes token=None
471474
self.token = _NoAuthenticationToken
@@ -478,6 +481,7 @@ def __init__(self, handler=None, **kwargs):
478481
self.password = kwargs.get("password", "")
479482
self.basic = kwargs.get("basic", False)
480483
self.autologin = kwargs.get("autologin", False)
484+
self.additional_headers = kwargs.get("headers", [])
481485

482486
# Store any cookies in the self.http._cookies dict
483487
if "cookie" in kwargs and kwargs['cookie'] not in [None, _NoAuthenticationToken]:
@@ -613,7 +617,7 @@ def delete(self, path_segment, owner=None, app=None, sharing=None, **query):
613617

614618
@_authentication
615619
@_log_duration
616-
def get(self, path_segment, owner=None, app=None, sharing=None, **query):
620+
def get(self, path_segment, owner=None, app=None, headers=None, sharing=None, **query):
617621
"""Performs a GET operation from the REST path segment with the given
618622
namespace and query.
619623
@@ -636,6 +640,8 @@ def get(self, path_segment, owner=None, app=None, sharing=None, **query):
636640
:type owner: ``string``
637641
:param app: The app context of the namespace (optional).
638642
:type app: ``string``
643+
:param headers: List of extra HTTP headers to send (optional).
644+
:type headers: ``list`` of 2-tuples.
639645
:param sharing: The sharing mode of the namespace (optional).
640646
:type sharing: ``string``
641647
:param query: All other keyword arguments, which are used as query
@@ -663,10 +669,14 @@ def get(self, path_segment, owner=None, app=None, sharing=None, **query):
663669
c.logout()
664670
c.get('apps/local') # raises AuthenticationError
665671
"""
672+
if headers is None:
673+
headers = []
674+
666675
path = self.authority + self._abspath(path_segment, owner=owner,
667676
app=app, sharing=sharing)
668677
logging.debug("GET request to %s (body: %s)", path, repr(query))
669-
response = self.http.get(path, self._auth_headers, **query)
678+
all_headers = headers + self.additional_headers + self._auth_headers
679+
response = self.http.get(path, all_headers, **query)
670680
return response
671681

672682
@_authentication
@@ -738,7 +748,7 @@ def post(self, path_segment, owner=None, app=None, sharing=None, headers=None, *
738748

739749
path = self.authority + self._abspath(path_segment, owner=owner, app=app, sharing=sharing)
740750
logging.debug("POST request to %s (body: %s)", path, repr(query))
741-
all_headers = headers + self._auth_headers
751+
all_headers = headers + self.additional_headers + self._auth_headers
742752
response = self.http.post(path, all_headers, **query)
743753
return response
744754

@@ -804,7 +814,7 @@ def request(self, path_segment, method="GET", headers=None, body="",
804814
path = self.authority \
805815
+ self._abspath(path_segment, owner=owner,
806816
app=app, sharing=sharing)
807-
all_headers = headers + self._auth_headers
817+
all_headers = headers + self.additional_headers + self._auth_headers
808818
logging.debug("%s request to %s (headers: %s, body: %s)",
809819
method, path, str(all_headers), repr(body))
810820
response = self.http.request(path,
@@ -858,6 +868,7 @@ def login(self):
858868
self.authority + self._abspath("/services/auth/login"),
859869
username=self.username,
860870
password=self.password,
871+
headers=self.additional_headers,
861872
cookie="1") # In Splunk 6.2+, passing "cookie=1" will return the "set-cookie" header
862873

863874
body = response.body.read()
@@ -968,6 +979,8 @@ def connect(**kwargs):
968979
:type username: ``string``
969980
:param password: The password for the Splunk account.
970981
:type password: ``string``
982+
:param headers: List of extra HTTP headers to send (optional).
983+
:type headers: ``list`` of 2-tuples.
971984
:param autologin: When ``True``, automatically tries to log in again if the
972985
session terminates.
973986
:type autologin: ``Boolean``
@@ -1108,8 +1121,11 @@ class HttpLib(object):
11081121
11091122
If using the default handler, SSL verification can be disabled by passing verify=False.
11101123
"""
1111-
def __init__(self, custom_handler=None, verify=True):
1112-
self.handler = handler(verify=verify) if custom_handler is None else custom_handler
1124+
def __init__(self, custom_handler=None, verify=False, key_file=None, cert_file=None):
1125+
if custom_handler is None:
1126+
self.handler = handler(verify=verify, key_file=key_file, cert_file=cert_file)
1127+
else:
1128+
self.handler = custom_handler
11131129
self._cookies = {}
11141130

11151131
def delete(self, url, headers=None, **kwargs):
@@ -1190,7 +1206,7 @@ def post(self, url, headers=None, **kwargs):
11901206
# to support the receivers/stream endpoint.
11911207
if 'body' in kwargs:
11921208
# We only use application/x-www-form-urlencoded if there is no other
1193-
# Content-Type header present. This can happen in cases where we
1209+
# Content-Type header present. This can happen in cases where we
11941210
# send requests as application/json, e.g. for KV Store.
11951211
if len([x for x in headers if x[0].lower() == "content-type"]) == 0:
11961212
headers.append(("Content-Type", "application/x-www-form-urlencoded"))
@@ -1280,8 +1296,8 @@ def peek(self, size):
12801296

12811297
def close(self):
12821298
"""Closes this response."""
1283-
if _connection:
1284-
_connection.close()
1299+
if self._connection:
1300+
self._connection.close()
12851301
self._response.close()
12861302

12871303
def read(self, size = None):
@@ -1317,7 +1333,7 @@ def readinto(self, byte_array):
13171333
return bytes_read
13181334

13191335

1320-
def handler(key_file=None, cert_file=None, timeout=None, verify=True):
1336+
def handler(key_file=None, cert_file=None, timeout=None, verify=False):
13211337
"""This class returns an instance of the default HTTP request handler using
13221338
the values you provide.
13231339
@@ -1341,7 +1357,7 @@ def connect(scheme, host, port):
13411357
if cert_file is not None: kwargs['cert_file'] = cert_file
13421358

13431359
# If running Python 2.7.9+, disable SSL certificate validation
1344-
if (sys.version_info >= (2,7,9) and key_file is None and cert_file is None) or not verify:
1360+
if (sys.version_info >= (2,7,9) and key_file is None and cert_file is None) and not verify:
13451361
kwargs['context'] = ssl._create_unverified_context()
13461362
return six.moves.http_client.HTTPSConnection(host, port, **kwargs)
13471363
raise ValueError("unsupported scheme: %s" % scheme)
@@ -1352,7 +1368,7 @@ def request(url, message, **kwargs):
13521368
head = {
13531369
"Content-Length": str(len(body)),
13541370
"Host": host,
1355-
"User-Agent": "splunk-sdk-python/1.6.5",
1371+
"User-Agent": "splunk-sdk-python/1.6.6",
13561372
"Accept": "*/*",
13571373
"Connection": "Close",
13581374
} # defaults

0 commit comments

Comments
 (0)