Change utility functions to new set_default Configuration model, preparing to use swagger-codegen HEAD
This commit is contained in:
parent
31e13b1f28
commit
b7a9f4a07e
657
api_client.py
657
api_client.py
@ -1,657 +0,0 @@
|
||||
# coding: utf-8
|
||||
|
||||
"""
|
||||
Copyright 2016 SmartBear Software
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
|
||||
ref: https://github.com/swagger-api/swagger-codegen
|
||||
"""
|
||||
|
||||
from __future__ import absolute_import
|
||||
|
||||
import json
|
||||
import mimetypes
|
||||
import os
|
||||
import re
|
||||
import tempfile
|
||||
import threading
|
||||
from datetime import date, datetime
|
||||
|
||||
# python 2 and python 3 compatibility library
|
||||
from six import PY3, integer_types, iteritems, text_type
|
||||
from six.moves.urllib.parse import quote
|
||||
|
||||
from . import models, ws_client
|
||||
from .configuration import configuration
|
||||
from .rest import ApiException, RESTClientObject
|
||||
|
||||
|
||||
class ApiClient(object):
|
||||
"""
|
||||
Generic API client for Swagger client library builds.
|
||||
|
||||
Swagger generic API client. This client handles the client-
|
||||
server communication, and is invariant across implementations. Specifics of
|
||||
the methods and models for each application are generated from the Swagger
|
||||
templates.
|
||||
|
||||
NOTE: This class is auto generated by the swagger code generator program.
|
||||
Ref: https://github.com/swagger-api/swagger-codegen
|
||||
Do not edit the class manually.
|
||||
|
||||
:param host: The base path for the server to call.
|
||||
:param header_name: a header to pass when making calls to the API.
|
||||
:param header_value: a header value to pass when making calls to the API.
|
||||
"""
|
||||
|
||||
def __init__(self, host=None, header_name=None, header_value=None,
|
||||
cookie=None, config=configuration):
|
||||
"""
|
||||
Constructor of the class.
|
||||
"""
|
||||
self.config = config
|
||||
self.rest_client = RESTClientObject(config=self.config)
|
||||
self.default_headers = {}
|
||||
if header_name is not None:
|
||||
self.default_headers[header_name] = header_value
|
||||
if host is None:
|
||||
self.host = self.config.host
|
||||
else:
|
||||
self.host = host
|
||||
self.cookie = cookie
|
||||
# Set default User-Agent.
|
||||
self.user_agent = 'Swagger-Codegen/1.0.0-snapshot/python'
|
||||
|
||||
@property
|
||||
def user_agent(self):
|
||||
"""
|
||||
Gets user agent.
|
||||
"""
|
||||
return self.default_headers['User-Agent']
|
||||
|
||||
@user_agent.setter
|
||||
def user_agent(self, value):
|
||||
"""
|
||||
Sets user agent.
|
||||
"""
|
||||
self.default_headers['User-Agent'] = value
|
||||
|
||||
def set_default_header(self, header_name, header_value):
|
||||
self.default_headers[header_name] = header_value
|
||||
|
||||
def __call_api(self, resource_path, method,
|
||||
path_params=None, query_params=None, header_params=None,
|
||||
body=None, post_params=None, files=None,
|
||||
response_type=None, auth_settings=None, callback=None,
|
||||
_return_http_data_only=None, collection_formats=None,
|
||||
_preload_content=True, _request_timeout=None):
|
||||
|
||||
# header parameters
|
||||
header_params = header_params or {}
|
||||
header_params.update(self.default_headers)
|
||||
if self.cookie:
|
||||
header_params['Cookie'] = self.cookie
|
||||
if header_params:
|
||||
header_params = self.sanitize_for_serialization(header_params)
|
||||
header_params = dict(self.parameters_to_tuples(header_params,
|
||||
collection_formats))
|
||||
|
||||
# path parameters
|
||||
if path_params:
|
||||
path_params = self.sanitize_for_serialization(path_params)
|
||||
path_params = self.parameters_to_tuples(path_params,
|
||||
collection_formats)
|
||||
for k, v in path_params:
|
||||
resource_path = resource_path.replace(
|
||||
'{%s}' % k, quote(str(v)))
|
||||
|
||||
# query parameters
|
||||
if query_params:
|
||||
query_params = self.sanitize_for_serialization(query_params)
|
||||
query_params = self.parameters_to_tuples(query_params,
|
||||
collection_formats)
|
||||
|
||||
# post parameters
|
||||
if post_params or files:
|
||||
post_params = self.prepare_post_parameters(post_params, files)
|
||||
post_params = self.sanitize_for_serialization(post_params)
|
||||
post_params = self.parameters_to_tuples(post_params,
|
||||
collection_formats)
|
||||
|
||||
# auth setting
|
||||
self.update_params_for_auth(header_params, query_params, auth_settings)
|
||||
|
||||
# body
|
||||
if body:
|
||||
body = self.sanitize_for_serialization(body)
|
||||
|
||||
# request url
|
||||
url = self.host + resource_path
|
||||
|
||||
# perform request and return response
|
||||
response_data = self.request(method, url,
|
||||
query_params=query_params,
|
||||
headers=header_params,
|
||||
post_params=post_params, body=body,
|
||||
_preload_content=_preload_content,
|
||||
_request_timeout=_request_timeout)
|
||||
|
||||
self.last_response = response_data
|
||||
|
||||
return_data = response_data
|
||||
if _preload_content:
|
||||
# deserialize response data
|
||||
if response_type:
|
||||
return_data = self.deserialize(response_data, response_type)
|
||||
else:
|
||||
return_data = None
|
||||
|
||||
if callback:
|
||||
if _return_http_data_only:
|
||||
callback(return_data)
|
||||
else:
|
||||
callback((return_data,
|
||||
response_data.status, response_data.getheaders()))
|
||||
elif _return_http_data_only:
|
||||
return (return_data)
|
||||
else:
|
||||
return (return_data, response_data.status,
|
||||
response_data.getheaders())
|
||||
|
||||
def sanitize_for_serialization(self, obj):
|
||||
"""
|
||||
Builds a JSON POST object.
|
||||
|
||||
If obj is None, return None.
|
||||
If obj is str, int, long, float, bool, return directly.
|
||||
If obj is datetime.datetime, datetime.date
|
||||
convert to string in iso8601 format.
|
||||
If obj is list, sanitize each element in the list.
|
||||
If obj is dict, return the dict.
|
||||
If obj is swagger model, return the properties dict.
|
||||
|
||||
:param obj: The data to serialize.
|
||||
:return: The serialized form of data.
|
||||
"""
|
||||
types = (str, float, bool, bytes) + tuple(integer_types) + (text_type,)
|
||||
if isinstance(obj, type(None)):
|
||||
return None
|
||||
elif isinstance(obj, types):
|
||||
return obj
|
||||
elif isinstance(obj, list):
|
||||
return [self.sanitize_for_serialization(sub_obj)
|
||||
for sub_obj in obj]
|
||||
elif isinstance(obj, tuple):
|
||||
return tuple(self.sanitize_for_serialization(sub_obj)
|
||||
for sub_obj in obj)
|
||||
elif isinstance(obj, (datetime, date)):
|
||||
return obj.isoformat()
|
||||
else:
|
||||
if isinstance(obj, dict):
|
||||
obj_dict = obj
|
||||
else:
|
||||
# Convert model obj to dict except
|
||||
# attributes `swagger_types`, `attribute_map`
|
||||
# and attributes which value is not None.
|
||||
# Convert attribute name to json key in
|
||||
# model definition for request.
|
||||
obj_dict = {obj.attribute_map[attr]: getattr(obj, attr)
|
||||
for attr, _ in iteritems(obj.swagger_types)
|
||||
if getattr(obj, attr) is not None}
|
||||
|
||||
return {key: self.sanitize_for_serialization(val)
|
||||
for key, val in iteritems(obj_dict)}
|
||||
|
||||
def deserialize(self, response, response_type):
|
||||
"""
|
||||
Deserializes response into an object.
|
||||
|
||||
:param response: RESTResponse object to be deserialized.
|
||||
:param response_type: class literal for
|
||||
deserialized object, or string of class name.
|
||||
|
||||
:return: deserialized object.
|
||||
"""
|
||||
# handle file downloading
|
||||
# save response body into a tmp file and return the instance
|
||||
if "file" == response_type:
|
||||
return self.__deserialize_file(response)
|
||||
|
||||
# fetch data from response object
|
||||
try:
|
||||
data = json.loads(response.data)
|
||||
except ValueError:
|
||||
data = response.data
|
||||
|
||||
return self.__deserialize(data, response_type)
|
||||
|
||||
def __deserialize(self, data, klass):
|
||||
"""
|
||||
Deserializes dict, list, str into an object.
|
||||
|
||||
:param data: dict, list or str.
|
||||
:param klass: class literal, or string of class name.
|
||||
|
||||
:return: object.
|
||||
"""
|
||||
if data is None:
|
||||
return None
|
||||
|
||||
if isinstance(klass, str):
|
||||
if klass.startswith('list['):
|
||||
sub_kls = re.match('list\[(.*)\]', klass).group(1)
|
||||
return [self.__deserialize(sub_data, sub_kls)
|
||||
for sub_data in data]
|
||||
|
||||
if klass.startswith('dict('):
|
||||
sub_kls = re.match('dict\(([^,]*), (.*)\)', klass).group(2)
|
||||
return {k: self.__deserialize(v, sub_kls)
|
||||
for k, v in iteritems(data)}
|
||||
|
||||
# convert str to class
|
||||
# for native types
|
||||
if klass in ['int', 'float', 'str', 'bool',
|
||||
"date", 'datetime', "object"]:
|
||||
klass = eval(klass)
|
||||
elif klass == 'long':
|
||||
klass = int if PY3 else long
|
||||
# for model types
|
||||
else:
|
||||
klass = eval('models.' + klass)
|
||||
|
||||
if klass in integer_types or klass in (float, str, bool):
|
||||
return self.__deserialize_primitive(data, klass)
|
||||
elif klass == object:
|
||||
return self.__deserialize_object(data)
|
||||
elif klass == date:
|
||||
return self.__deserialize_date(data)
|
||||
elif klass == datetime:
|
||||
return self.__deserialize_datatime(data)
|
||||
else:
|
||||
return self.__deserialize_model(data, klass)
|
||||
|
||||
def call_api(self, resource_path, method,
|
||||
path_params=None, query_params=None, header_params=None,
|
||||
body=None, post_params=None, files=None,
|
||||
response_type=None, auth_settings=None, callback=None,
|
||||
_return_http_data_only=None, collection_formats=None,
|
||||
_preload_content=True, _request_timeout=None):
|
||||
"""
|
||||
Makes the HTTP request (synchronous) and return the deserialized data.
|
||||
To make an async request, define a function for callback.
|
||||
|
||||
:param resource_path: Path to method endpoint.
|
||||
:param method: Method to call.
|
||||
:param path_params: Path parameters in the url.
|
||||
:param query_params: Query parameters in the url.
|
||||
:param header_params: Header parameters to be
|
||||
placed in the request header.
|
||||
:param body: Request body.
|
||||
:param post_params dict: Request post form parameters,
|
||||
for `application/x-www-form-urlencoded`, `multipart/form-data`.
|
||||
:param auth_settings list: Auth Settings names for the request.
|
||||
:param response: Response data type.
|
||||
:param files dict: key -> filename, value -> filepath,
|
||||
for `multipart/form-data`.
|
||||
:param callback function: Callback function for asynchronous request.
|
||||
If provide this parameter,
|
||||
the request will be called asynchronously.
|
||||
:param _return_http_data_only: response data without head status code
|
||||
and headers
|
||||
:param collection_formats: dict of collection formats for path, query,
|
||||
header, and post parameters.
|
||||
:param _preload_content: if False, the urllib3.HTTPResponse object will
|
||||
be returned without
|
||||
reading/decoding response data.
|
||||
Default is True.
|
||||
:param _request_timeout: timeout setting for this request. If one
|
||||
number provided, it will be total request
|
||||
timeout. It can also be a pair (tuple) of
|
||||
(connection, read) timeouts.
|
||||
:return:
|
||||
If provide parameter callback,
|
||||
the request will be called asynchronously.
|
||||
The method will return the request thread.
|
||||
If parameter callback is None,
|
||||
then the method will return the response directly.
|
||||
"""
|
||||
if callback is None:
|
||||
return self.__call_api(resource_path, method,
|
||||
path_params, query_params, header_params,
|
||||
body, post_params, files,
|
||||
response_type, auth_settings, callback,
|
||||
_return_http_data_only, collection_formats,
|
||||
_preload_content, _request_timeout)
|
||||
else:
|
||||
thread = threading.Thread(target=self.__call_api,
|
||||
args=(resource_path, method,
|
||||
path_params, query_params,
|
||||
header_params, body,
|
||||
post_params, files,
|
||||
response_type, auth_settings,
|
||||
callback, _return_http_data_only,
|
||||
collection_formats,
|
||||
_preload_content,
|
||||
_request_timeout))
|
||||
thread.start()
|
||||
return thread
|
||||
|
||||
def request(self, method, url, query_params=None, headers=None,
|
||||
post_params=None, body=None, _preload_content=True,
|
||||
_request_timeout=None):
|
||||
"""
|
||||
Makes the HTTP request using RESTClient.
|
||||
"""
|
||||
# FIXME(dims) : We need a better way to figure out which
|
||||
# calls end up using web sockets
|
||||
if (url.endswith('/exec') or url.endswith('/attach')) and \
|
||||
(method == "GET" or method == "POST"):
|
||||
return ws_client.websocket_call(self.config,
|
||||
url,
|
||||
query_params=query_params,
|
||||
_request_timeout=_request_timeout,
|
||||
_preload_content=_preload_content,
|
||||
headers=headers)
|
||||
if method == "GET":
|
||||
return self.rest_client.GET(url,
|
||||
query_params=query_params,
|
||||
_preload_content=_preload_content,
|
||||
_request_timeout=_request_timeout,
|
||||
headers=headers)
|
||||
elif method == "HEAD":
|
||||
return self.rest_client.HEAD(url,
|
||||
query_params=query_params,
|
||||
_preload_content=_preload_content,
|
||||
_request_timeout=_request_timeout,
|
||||
headers=headers)
|
||||
elif method == "OPTIONS":
|
||||
return self.rest_client.OPTIONS(url,
|
||||
query_params=query_params,
|
||||
headers=headers,
|
||||
post_params=post_params,
|
||||
_preload_content=_preload_content,
|
||||
_request_timeout=_request_timeout,
|
||||
body=body)
|
||||
elif method == "POST":
|
||||
return self.rest_client.POST(url,
|
||||
query_params=query_params,
|
||||
headers=headers,
|
||||
post_params=post_params,
|
||||
_preload_content=_preload_content,
|
||||
_request_timeout=_request_timeout,
|
||||
body=body)
|
||||
elif method == "PUT":
|
||||
return self.rest_client.PUT(url,
|
||||
query_params=query_params,
|
||||
headers=headers,
|
||||
post_params=post_params,
|
||||
_preload_content=_preload_content,
|
||||
_request_timeout=_request_timeout,
|
||||
body=body)
|
||||
elif method == "PATCH":
|
||||
return self.rest_client.PATCH(url,
|
||||
query_params=query_params,
|
||||
headers=headers,
|
||||
post_params=post_params,
|
||||
_preload_content=_preload_content,
|
||||
_request_timeout=_request_timeout,
|
||||
body=body)
|
||||
elif method == "DELETE":
|
||||
return self.rest_client.DELETE(url,
|
||||
query_params=query_params,
|
||||
headers=headers,
|
||||
_preload_content=_preload_content,
|
||||
_request_timeout=_request_timeout,
|
||||
body=body)
|
||||
else:
|
||||
raise ValueError(
|
||||
"http method must be `GET`, `HEAD`, `OPTIONS`,"
|
||||
" `POST`, `PATCH`, `PUT` or `DELETE`."
|
||||
)
|
||||
|
||||
def parameters_to_tuples(self, params, collection_formats):
|
||||
"""
|
||||
Get parameters as list of tuples, formatting collections.
|
||||
|
||||
:param params: Parameters as dict or list of two-tuples
|
||||
:param dict collection_formats: Parameter collection formats
|
||||
:return: Parameters as list of tuples, collections formatted
|
||||
"""
|
||||
new_params = []
|
||||
if collection_formats is None:
|
||||
collection_formats = {}
|
||||
for k, v in iteritems(params) if isinstance(params, dict) else params:
|
||||
if k in collection_formats:
|
||||
collection_format = collection_formats[k]
|
||||
if collection_format == 'multi':
|
||||
new_params.extend((k, value) for value in v)
|
||||
else:
|
||||
if collection_format == 'ssv':
|
||||
delimiter = ' '
|
||||
elif collection_format == 'tsv':
|
||||
delimiter = '\t'
|
||||
elif collection_format == 'pipes':
|
||||
delimiter = '|'
|
||||
else: # csv is the default
|
||||
delimiter = ','
|
||||
new_params.append(
|
||||
(k, delimiter.join(str(value) for value in v)))
|
||||
else:
|
||||
new_params.append((k, v))
|
||||
return new_params
|
||||
|
||||
def prepare_post_parameters(self, post_params=None, files=None):
|
||||
"""
|
||||
Builds form parameters.
|
||||
|
||||
:param post_params: Normal form parameters.
|
||||
:param files: File parameters.
|
||||
:return: Form parameters with files.
|
||||
"""
|
||||
params = []
|
||||
|
||||
if post_params:
|
||||
params = post_params
|
||||
|
||||
if files:
|
||||
for k, v in iteritems(files):
|
||||
if not v:
|
||||
continue
|
||||
file_names = v if isinstance(v, list) else [v]
|
||||
for n in file_names:
|
||||
with open(n, 'rb') as f:
|
||||
filename = os.path.basename(f.name)
|
||||
filedata = f.read()
|
||||
mimetype = (mimetypes.guess_type(filename)[0] or
|
||||
'application/octet-stream')
|
||||
params.append(tuple([k, tuple([filename, filedata,
|
||||
mimetype])]))
|
||||
|
||||
return params
|
||||
|
||||
def select_header_accept(self, accepts):
|
||||
"""
|
||||
Returns `Accept` based on an array of accepts provided.
|
||||
|
||||
:param accepts: List of headers.
|
||||
:return: Accept (e.g. application/json).
|
||||
"""
|
||||
if not accepts:
|
||||
return
|
||||
|
||||
accepts = list(map(lambda x: x.lower(), accepts))
|
||||
|
||||
if 'application/json' in accepts:
|
||||
return 'application/json'
|
||||
else:
|
||||
return ', '.join(accepts)
|
||||
|
||||
def select_header_content_type(self, content_types):
|
||||
"""
|
||||
Returns `Content-Type` based on an array of content_types provided.
|
||||
|
||||
:param content_types: List of content-types.
|
||||
:return: Content-Type (e.g. application/json).
|
||||
"""
|
||||
if not content_types:
|
||||
return 'application/json'
|
||||
|
||||
content_types = list(map(lambda x: x.lower(), content_types))
|
||||
|
||||
if 'application/json' in content_types or '*/*' in content_types:
|
||||
return 'application/json'
|
||||
else:
|
||||
return content_types[0]
|
||||
|
||||
def update_params_for_auth(self, headers, querys, auth_settings):
|
||||
"""
|
||||
Updates header and query params based on authentication setting.
|
||||
|
||||
:param headers: Header parameters dict to be updated.
|
||||
:param querys: Query parameters tuple list to be updated.
|
||||
:param auth_settings: Authentication setting identifiers list.
|
||||
"""
|
||||
|
||||
if not auth_settings:
|
||||
return
|
||||
|
||||
for auth in auth_settings:
|
||||
auth_setting = self.config.auth_settings().get(auth)
|
||||
if auth_setting:
|
||||
if not auth_setting['value']:
|
||||
continue
|
||||
elif auth_setting['in'] == 'header':
|
||||
headers[auth_setting['key']] = auth_setting['value']
|
||||
elif auth_setting['in'] == 'query':
|
||||
querys.append((auth_setting['key'], auth_setting['value']))
|
||||
else:
|
||||
raise ValueError(
|
||||
'Authentication token must be in `query` or `header`'
|
||||
)
|
||||
|
||||
def __deserialize_file(self, response):
|
||||
"""
|
||||
Saves response body into a file in a temporary folder,
|
||||
using the filename from the `Content-Disposition` header if provided.
|
||||
|
||||
:param response: RESTResponse.
|
||||
:return: file path.
|
||||
"""
|
||||
fd, path = tempfile.mkstemp(dir=self.config.temp_folder_path)
|
||||
os.close(fd)
|
||||
os.remove(path)
|
||||
|
||||
content_disposition = response.getheader("Content-Disposition")
|
||||
if content_disposition:
|
||||
filename = re.search(r'filename=[\'"]?([^\'"\s]+)[\'"]?',
|
||||
content_disposition).group(1)
|
||||
path = os.path.join(os.path.dirname(path), filename)
|
||||
|
||||
with open(path, "w") as f:
|
||||
f.write(response.data)
|
||||
|
||||
return path
|
||||
|
||||
def __deserialize_primitive(self, data, klass):
|
||||
"""
|
||||
Deserializes string to primitive type.
|
||||
|
||||
:param data: str.
|
||||
:param klass: class literal.
|
||||
|
||||
:return: int, long, float, str, bool.
|
||||
"""
|
||||
try:
|
||||
value = klass(data)
|
||||
except UnicodeEncodeError:
|
||||
value = unicode(data)
|
||||
except TypeError:
|
||||
value = data
|
||||
return value
|
||||
|
||||
def __deserialize_object(self, value):
|
||||
"""
|
||||
Return a original value.
|
||||
|
||||
:return: object.
|
||||
"""
|
||||
return value
|
||||
|
||||
def __deserialize_date(self, string):
|
||||
"""
|
||||
Deserializes string to date.
|
||||
|
||||
:param string: str.
|
||||
:return: date.
|
||||
"""
|
||||
if not string:
|
||||
return None
|
||||
try:
|
||||
from dateutil.parser import parse
|
||||
return parse(string).date()
|
||||
except ImportError:
|
||||
return string
|
||||
except ValueError:
|
||||
raise ApiException(
|
||||
status=0,
|
||||
reason="Failed to parse `{0}` into a date object"
|
||||
.format(string)
|
||||
)
|
||||
|
||||
def __deserialize_datatime(self, string):
|
||||
"""
|
||||
Deserializes string to datetime.
|
||||
|
||||
The string should be in iso8601 datetime format.
|
||||
|
||||
:param string: str.
|
||||
:return: datetime.
|
||||
"""
|
||||
if not string:
|
||||
return None
|
||||
try:
|
||||
from dateutil.parser import parse
|
||||
return parse(string)
|
||||
except ImportError:
|
||||
return string
|
||||
except ValueError:
|
||||
raise ApiException(
|
||||
status=0,
|
||||
reason="Failed to parse `{0}` into a datetime object".
|
||||
format(string)
|
||||
)
|
||||
|
||||
def __deserialize_model(self, data, klass):
|
||||
"""
|
||||
Deserializes list or dict to model.
|
||||
|
||||
:param data: dict, list.
|
||||
:param klass: class literal.
|
||||
:return: model object.
|
||||
"""
|
||||
instance = klass()
|
||||
|
||||
if not instance.swagger_types:
|
||||
return data
|
||||
|
||||
for attr, attr_type in iteritems(instance.swagger_types):
|
||||
if data is not None \
|
||||
and instance.attribute_map[attr] in data\
|
||||
and isinstance(data, (list, dict)):
|
||||
value = data[instance.attribute_map[attr]]
|
||||
if value is None:
|
||||
value = [] if isinstance(data, list) else {}
|
||||
setattr(instance, attr, self.__deserialize(value, attr_type))
|
||||
|
||||
return instance
|
||||
@ -14,7 +14,7 @@
|
||||
|
||||
import os
|
||||
|
||||
from kubernetes.client import configuration
|
||||
from kubernetes.client import Configuration
|
||||
|
||||
from .config_exception import ConfigException
|
||||
|
||||
@ -77,9 +77,11 @@ class InClusterConfigLoader(object):
|
||||
self.ssl_ca_cert = self._cert_filename
|
||||
|
||||
def _set_config(self):
|
||||
configuration = Configuration()
|
||||
configuration.host = self.host
|
||||
configuration.ssl_ca_cert = self.ssl_ca_cert
|
||||
configuration.api_key['authorization'] = "bearer " + self.token
|
||||
Configuration.set_default(configuration)
|
||||
|
||||
|
||||
def load_incluster_config():
|
||||
|
||||
@ -23,7 +23,7 @@ import google.auth.transport.requests
|
||||
import urllib3
|
||||
import yaml
|
||||
|
||||
from kubernetes.client import ApiClient, ConfigurationObject, configuration
|
||||
from kubernetes.client import ApiClient, Configuration
|
||||
|
||||
from .config_exception import ConfigException
|
||||
from .dateutil import UTC, format_rfc3339, parse_rfc3339
|
||||
@ -118,7 +118,6 @@ class KubeConfigLoader(object):
|
||||
|
||||
def __init__(self, config_dict, active_context=None,
|
||||
get_google_credentials=None,
|
||||
client_configuration=configuration,
|
||||
config_base_path="",
|
||||
config_persister=None):
|
||||
self._config = ConfigNode('kube-config', config_dict)
|
||||
@ -139,7 +138,6 @@ class KubeConfigLoader(object):
|
||||
self._get_google_credentials = get_google_credentials
|
||||
else:
|
||||
self._get_google_credentials = _refresh_credentials
|
||||
self._client_configuration = client_configuration
|
||||
|
||||
def set_active_context(self, context_name=None):
|
||||
if context_name is None:
|
||||
@ -240,19 +238,19 @@ class KubeConfigLoader(object):
|
||||
if 'insecure-skip-tls-verify' in self._cluster:
|
||||
self.verify_ssl = not self._cluster['insecure-skip-tls-verify']
|
||||
|
||||
def _set_config(self):
|
||||
def _set_config(self, client_configuration):
|
||||
if 'token' in self.__dict__:
|
||||
self._client_configuration.api_key['authorization'] = self.token
|
||||
client_configuration.api_key['authorization'] = self.token
|
||||
# copy these keys directly from self to configuration object
|
||||
keys = ['host', 'ssl_ca_cert', 'cert_file', 'key_file', 'verify_ssl']
|
||||
for key in keys:
|
||||
if key in self.__dict__:
|
||||
setattr(self._client_configuration, key, getattr(self, key))
|
||||
setattr(client_configuration, key, getattr(self, key))
|
||||
|
||||
def load_and_set(self):
|
||||
def load_and_set(self, client_configuration):
|
||||
self._load_authentication()
|
||||
self._load_cluster_info()
|
||||
self._set_config()
|
||||
self._set_config(client_configuration)
|
||||
|
||||
def list_contexts(self):
|
||||
return [context.value for context in self._config['contexts']]
|
||||
@ -331,7 +329,7 @@ def list_kube_config_contexts(config_file=None):
|
||||
|
||||
|
||||
def load_kube_config(config_file=None, context=None,
|
||||
client_configuration=configuration,
|
||||
client_configuration=None,
|
||||
persist_config=True):
|
||||
"""Loads authentication and cluster information from kube-config file
|
||||
and stores them in kubernetes.client.configuration.
|
||||
@ -339,7 +337,7 @@ def load_kube_config(config_file=None, context=None,
|
||||
:param config_file: Name of the kube-config file.
|
||||
:param context: set the active context. If is set to None, current_context
|
||||
from config file will be used.
|
||||
:param client_configuration: The kubernetes.client.ConfigurationObject to
|
||||
:param client_configuration: The kubernetes.client.Configuration to
|
||||
set configs to.
|
||||
:param persist_config: If True, config file will be updated when changed
|
||||
(e.g GCP token refresh).
|
||||
@ -355,10 +353,15 @@ def load_kube_config(config_file=None, context=None,
|
||||
yaml.safe_dump(config_map, f, default_flow_style=False)
|
||||
config_persister = _save_kube_config
|
||||
|
||||
_get_kube_config_loader_for_yaml_file(
|
||||
loader = _get_kube_config_loader_for_yaml_file(
|
||||
config_file, active_context=context,
|
||||
client_configuration=client_configuration,
|
||||
config_persister=config_persister).load_and_set()
|
||||
config_persister=config_persister)
|
||||
if client_configuration is None:
|
||||
config = type.__call__(Configuration)
|
||||
loader.load_and_set(config)
|
||||
Configuration.set_default(config)
|
||||
else:
|
||||
loader.load_and_set(client_configuration)
|
||||
|
||||
|
||||
def new_client_from_config(
|
||||
@ -368,8 +371,8 @@ def new_client_from_config(
|
||||
"""Loads configuration the same as load_kube_config but returns an ApiClient
|
||||
to be used with any API object. This will allow the caller to concurrently
|
||||
talk with multiple clusters."""
|
||||
client_config = ConfigurationObject()
|
||||
client_config = type.__call__(Configuration)
|
||||
load_kube_config(config_file=config_file, context=context,
|
||||
client_configuration=client_config,
|
||||
persist_config=persist_config)
|
||||
return ApiClient(config=client_config)
|
||||
return ApiClient(configuration=client_config)
|
||||
|
||||
@ -83,9 +83,9 @@ class BaseTestCase(unittest.TestCase):
|
||||
os.close(handler)
|
||||
return name
|
||||
|
||||
def expect_exception(self, func, message_part):
|
||||
def expect_exception(self, func, message_part, *args, **kwargs):
|
||||
with self.assertRaises(ConfigException) as context:
|
||||
func()
|
||||
func(*args, **kwargs)
|
||||
self.assertIn(message_part, str(context.exception))
|
||||
|
||||
|
||||
@ -473,8 +473,7 @@ class TestKubeConfigLoader(BaseTestCase):
|
||||
actual = FakeConfig()
|
||||
KubeConfigLoader(
|
||||
config_dict=self.TEST_KUBE_CONFIG,
|
||||
active_context="no_user",
|
||||
client_configuration=actual).load_and_set()
|
||||
active_context="no_user").load_and_set(actual)
|
||||
self.assertEqual(expected, actual)
|
||||
|
||||
def test_simple_token(self):
|
||||
@ -483,8 +482,7 @@ class TestKubeConfigLoader(BaseTestCase):
|
||||
actual = FakeConfig()
|
||||
KubeConfigLoader(
|
||||
config_dict=self.TEST_KUBE_CONFIG,
|
||||
active_context="simple_token",
|
||||
client_configuration=actual).load_and_set()
|
||||
active_context="simple_token").load_and_set(actual)
|
||||
self.assertEqual(expected, actual)
|
||||
|
||||
def test_load_user_token(self):
|
||||
@ -502,9 +500,8 @@ class TestKubeConfigLoader(BaseTestCase):
|
||||
KubeConfigLoader(
|
||||
config_dict=self.TEST_KUBE_CONFIG,
|
||||
active_context="gcp",
|
||||
client_configuration=actual,
|
||||
get_google_credentials=lambda: _raise_exception(
|
||||
"SHOULD NOT BE CALLED")).load_and_set()
|
||||
"SHOULD NOT BE CALLED")).load_and_set(actual)
|
||||
self.assertEqual(expected, actual)
|
||||
|
||||
def test_load_gcp_token_no_refresh(self):
|
||||
@ -536,8 +533,7 @@ class TestKubeConfigLoader(BaseTestCase):
|
||||
actual = FakeConfig()
|
||||
KubeConfigLoader(
|
||||
config_dict=self.TEST_KUBE_CONFIG,
|
||||
active_context="user_pass",
|
||||
client_configuration=actual).load_and_set()
|
||||
active_context="user_pass").load_and_set(actual)
|
||||
self.assertEqual(expected, actual)
|
||||
|
||||
def test_load_user_pass_token(self):
|
||||
@ -548,12 +544,13 @@ class TestKubeConfigLoader(BaseTestCase):
|
||||
self.assertEqual(TEST_BASIC_TOKEN, loader.token)
|
||||
|
||||
def test_ssl_no_cert_files(self):
|
||||
actual = FakeConfig()
|
||||
loader = KubeConfigLoader(
|
||||
config_dict=self.TEST_KUBE_CONFIG,
|
||||
active_context="ssl-no_file",
|
||||
client_configuration=actual)
|
||||
self.expect_exception(loader.load_and_set, "does not exists")
|
||||
active_context="ssl-no_file")
|
||||
self.expect_exception(
|
||||
loader.load_and_set,
|
||||
"does not exists",
|
||||
FakeConfig())
|
||||
|
||||
def test_ssl(self):
|
||||
expected = FakeConfig(
|
||||
@ -566,8 +563,7 @@ class TestKubeConfigLoader(BaseTestCase):
|
||||
actual = FakeConfig()
|
||||
KubeConfigLoader(
|
||||
config_dict=self.TEST_KUBE_CONFIG,
|
||||
active_context="ssl",
|
||||
client_configuration=actual).load_and_set()
|
||||
active_context="ssl").load_and_set(actual)
|
||||
self.assertEqual(expected, actual)
|
||||
|
||||
def test_ssl_no_verification(self):
|
||||
@ -582,8 +578,7 @@ class TestKubeConfigLoader(BaseTestCase):
|
||||
actual = FakeConfig()
|
||||
KubeConfigLoader(
|
||||
config_dict=self.TEST_KUBE_CONFIG,
|
||||
active_context="no_ssl_verification",
|
||||
client_configuration=actual).load_and_set()
|
||||
active_context="no_ssl_verification").load_and_set(actual)
|
||||
self.assertEqual(expected, actual)
|
||||
|
||||
def test_list_contexts(self):
|
||||
@ -631,8 +626,7 @@ class TestKubeConfigLoader(BaseTestCase):
|
||||
KubeConfigLoader(
|
||||
config_dict=self.TEST_KUBE_CONFIG,
|
||||
active_context="ssl-local-file",
|
||||
config_base_path=temp_dir,
|
||||
client_configuration=actual).load_and_set()
|
||||
config_base_path=temp_dir).load_and_set(actual)
|
||||
self.assertEqual(expected, actual)
|
||||
finally:
|
||||
shutil.rmtree(temp_dir)
|
||||
@ -663,9 +657,9 @@ class TestKubeConfigLoader(BaseTestCase):
|
||||
config_file = self._create_temp_file(yaml.dump(self.TEST_KUBE_CONFIG))
|
||||
client = new_client_from_config(
|
||||
config_file=config_file, context="simple_token")
|
||||
self.assertEqual(TEST_HOST, client.config.host)
|
||||
self.assertEqual(TEST_HOST, client.configuration.host)
|
||||
self.assertEqual(BEARER_TOKEN_FORMAT % TEST_DATA_BASE64,
|
||||
client.config.api_key['authorization'])
|
||||
client.configuration.api_key['authorization'])
|
||||
|
||||
def test_no_users_section(self):
|
||||
expected = FakeConfig(host=TEST_HOST)
|
||||
@ -674,8 +668,7 @@ class TestKubeConfigLoader(BaseTestCase):
|
||||
del test_kube_config['users']
|
||||
KubeConfigLoader(
|
||||
config_dict=test_kube_config,
|
||||
active_context="gcp",
|
||||
client_configuration=actual).load_and_set()
|
||||
active_context="gcp").load_and_set(actual)
|
||||
self.assertEqual(expected, actual)
|
||||
|
||||
def test_non_existing_user(self):
|
||||
@ -683,8 +676,7 @@ class TestKubeConfigLoader(BaseTestCase):
|
||||
actual = FakeConfig()
|
||||
KubeConfigLoader(
|
||||
config_dict=self.TEST_KUBE_CONFIG,
|
||||
active_context="non_existing_user",
|
||||
client_configuration=actual).load_and_set()
|
||||
active_context="non_existing_user").load_and_set(actual)
|
||||
self.assertEqual(expected, actual)
|
||||
|
||||
|
||||
|
||||
247
configuration.py
247
configuration.py
@ -1,247 +0,0 @@
|
||||
# coding: utf-8
|
||||
|
||||
"""
|
||||
Kubernetes
|
||||
|
||||
First version Generated by: https://github.com/swagger-api/swagger-codegen
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
"""
|
||||
|
||||
from __future__ import absolute_import
|
||||
|
||||
import multiprocessing
|
||||
import logging
|
||||
import sys
|
||||
|
||||
import urllib3
|
||||
from six import iteritems
|
||||
from six.moves import http_client as httplib
|
||||
|
||||
|
||||
class ConfigurationObject(object):
|
||||
"""
|
||||
Configuration options for RESTClientObject
|
||||
"""
|
||||
|
||||
def __init__(self):
|
||||
"""
|
||||
Constructor
|
||||
"""
|
||||
# Default Base url
|
||||
self.host = "https://localhost"
|
||||
# Default api client
|
||||
self.api_client = None
|
||||
# Temp file folder for downloading files
|
||||
self.temp_folder_path = None
|
||||
|
||||
# Authentication Settings
|
||||
# dict to store API key(s)
|
||||
self.api_key = {}
|
||||
# dict to store API prefix (e.g. Bearer)
|
||||
self.api_key_prefix = {}
|
||||
# Username for HTTP basic authentication
|
||||
self.username = ""
|
||||
# Password for HTTP basic authentication
|
||||
self.password = ""
|
||||
|
||||
# Logging Settings
|
||||
self.logger = {}
|
||||
self.logger["package_logger"] = logging.getLogger("client")
|
||||
self.logger["urllib3_logger"] = logging.getLogger("urllib3")
|
||||
# Log format
|
||||
self.logger_format = '%(asctime)s %(levelname)s %(message)s'
|
||||
# Log stream handler
|
||||
self.logger_stream_handler = None
|
||||
# Log file handler
|
||||
self.logger_file_handler = None
|
||||
# Debug file location
|
||||
self.logger_file = None
|
||||
# Debug switch
|
||||
self.debug = False
|
||||
|
||||
# SSL/TLS verification
|
||||
# Set this to false to skip verifying SSL certificate when calling API
|
||||
# from https server.
|
||||
self.verify_ssl = True
|
||||
# Set this to customize the certificate file to verify the peer.
|
||||
self.ssl_ca_cert = None
|
||||
# client certificate file
|
||||
self.cert_file = None
|
||||
# client key file
|
||||
self.key_file = None
|
||||
# check host name
|
||||
# Set this to True/False to enable/disable SSL hostname verification.
|
||||
self.assert_hostname = None
|
||||
# urllib3 connection pool's maximum number of connections saved
|
||||
# per pool. urllib3 uses 1 connection as default value, but this is
|
||||
# not the best value when you are making a lot of possibly parallel
|
||||
# requests to the same host, which is often the case here.
|
||||
# cpu_count * 5 is used as default value to increase performance
|
||||
# This is used because it's the default value for ThreadPoolExecutor
|
||||
self.connection_pool_maxsize = multiprocessing.cpu_count() * 5
|
||||
# http proxy setting
|
||||
self.http_proxy_url = None
|
||||
|
||||
# WebSocket subprotocol to use for exec and portforward.
|
||||
self.ws_streaming_protocol = "v4.channel.k8s.io"
|
||||
|
||||
@property
|
||||
def logger_file(self):
|
||||
"""
|
||||
Gets the logger_file.
|
||||
"""
|
||||
return self.__logger_file
|
||||
|
||||
@logger_file.setter
|
||||
def logger_file(self, value):
|
||||
"""
|
||||
Sets the logger_file.
|
||||
|
||||
If the logger_file is None, then add stream handler and remove file
|
||||
handler. Otherwise, add file handler and remove stream handler.
|
||||
|
||||
:param value: The logger_file path.
|
||||
:type: str
|
||||
"""
|
||||
self.__logger_file = value
|
||||
if self.__logger_file:
|
||||
# If set logging file,
|
||||
# then add file handler and remove stream handler.
|
||||
self.logger_file_handler = logging.FileHandler(self.__logger_file)
|
||||
self.logger_file_handler.setFormatter(self.logger_formatter)
|
||||
for _, logger in iteritems(self.logger):
|
||||
logger.addHandler(self.logger_file_handler)
|
||||
if self.logger_stream_handler:
|
||||
logger.removeHandler(self.logger_stream_handler)
|
||||
else:
|
||||
# If not set logging file,
|
||||
# then add stream handler and remove file handler.
|
||||
self.logger_stream_handler = logging.StreamHandler()
|
||||
self.logger_stream_handler.setFormatter(self.logger_formatter)
|
||||
for _, logger in iteritems(self.logger):
|
||||
logger.addHandler(self.logger_stream_handler)
|
||||
if self.logger_file_handler:
|
||||
logger.removeHandler(self.logger_file_handler)
|
||||
|
||||
@property
|
||||
def debug(self):
|
||||
"""
|
||||
Gets the debug status.
|
||||
"""
|
||||
return self.__debug
|
||||
|
||||
@debug.setter
|
||||
def debug(self, value):
|
||||
"""
|
||||
Sets the debug status.
|
||||
|
||||
:param value: The debug status, True or False.
|
||||
:type: bool
|
||||
"""
|
||||
self.__debug = value
|
||||
if self.__debug:
|
||||
# if debug status is True, turn on debug logging
|
||||
for _, logger in iteritems(self.logger):
|
||||
logger.setLevel(logging.DEBUG)
|
||||
# turn on httplib debug
|
||||
httplib.HTTPConnection.debuglevel = 1
|
||||
else:
|
||||
# if debug status is False, turn off debug logging,
|
||||
# setting log level to default `logging.WARNING`
|
||||
for _, logger in iteritems(self.logger):
|
||||
logger.setLevel(logging.WARNING)
|
||||
# turn off httplib debug
|
||||
httplib.HTTPConnection.debuglevel = 0
|
||||
|
||||
@property
|
||||
def logger_format(self):
|
||||
"""
|
||||
Gets the logger_format.
|
||||
"""
|
||||
return self.__logger_format
|
||||
|
||||
@logger_format.setter
|
||||
def logger_format(self, value):
|
||||
"""
|
||||
Sets the logger_format.
|
||||
|
||||
The logger_formatter will be updated when sets logger_format.
|
||||
|
||||
:param value: The format string.
|
||||
:type: str
|
||||
"""
|
||||
self.__logger_format = value
|
||||
self.logger_formatter = logging.Formatter(self.__logger_format)
|
||||
|
||||
def get_api_key_with_prefix(self, identifier):
|
||||
"""
|
||||
Gets API key (with prefix if set).
|
||||
|
||||
:param identifier: The identifier of apiKey.
|
||||
:return: The token for api key authentication.
|
||||
"""
|
||||
if (self.api_key.get(identifier) and
|
||||
self.api_key_prefix.get(identifier)):
|
||||
return (self.api_key_prefix[identifier] + ' ' +
|
||||
self.api_key[identifier])
|
||||
elif self.api_key.get(identifier):
|
||||
return self.api_key[identifier]
|
||||
|
||||
def get_basic_auth_token(self):
|
||||
"""
|
||||
Gets HTTP basic authentication header (string).
|
||||
|
||||
:return: The token for basic HTTP authentication.
|
||||
"""
|
||||
return urllib3.util.make_headers(
|
||||
basic_auth=self.username + ':' + self.password).get(
|
||||
'authorization')
|
||||
|
||||
def auth_settings(self):
|
||||
"""
|
||||
Gets Auth Settings dict for api client.
|
||||
|
||||
:return: The Auth Settings information dict.
|
||||
"""
|
||||
return {
|
||||
'BearerToken':
|
||||
{
|
||||
'type': 'api_key',
|
||||
'in': 'header',
|
||||
'key': 'authorization',
|
||||
'value': self.get_api_key_with_prefix('authorization')
|
||||
},
|
||||
|
||||
}
|
||||
|
||||
def to_debug_report(self):
|
||||
"""
|
||||
Gets the essential information for debugging.
|
||||
|
||||
:return: The report for debugging.
|
||||
"""
|
||||
return "Python SDK Debug Report:\n"\
|
||||
"OS: {env}\n"\
|
||||
"Python Version: {pyversion}\n"\
|
||||
"Version of the API: v1.5.0-snapshot\n"\
|
||||
"SDK Package Version: 1.0.0-snapshot".\
|
||||
format(env=sys.platform, pyversion=sys.version)
|
||||
|
||||
|
||||
configuration = ConfigurationObject()
|
||||
|
||||
|
||||
def Configuration():
|
||||
"""Simulate a singelton Configuration object for backward compatibility."""
|
||||
return configuration
|
||||
337
rest.py
337
rest.py
@ -1,337 +0,0 @@
|
||||
# coding: utf-8
|
||||
|
||||
"""
|
||||
Kubernetes
|
||||
|
||||
First version Generated by: https://github.com/swagger-api/swagger-codegen
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
"""
|
||||
|
||||
from __future__ import absolute_import
|
||||
|
||||
import io
|
||||
import json
|
||||
import logging
|
||||
import re
|
||||
import ssl
|
||||
|
||||
import certifi
|
||||
# python 2 and python 3 compatibility library
|
||||
from six import PY3
|
||||
from six.moves.urllib.parse import urlencode
|
||||
|
||||
from .configuration import configuration
|
||||
|
||||
try:
|
||||
import urllib3
|
||||
except ImportError:
|
||||
raise ImportError('Swagger python client requires urllib3.')
|
||||
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class RESTResponse(io.IOBase):
|
||||
|
||||
def __init__(self, resp):
|
||||
self.urllib3_response = resp
|
||||
self.status = resp.status
|
||||
self.reason = resp.reason
|
||||
self.data = resp.data
|
||||
|
||||
def getheaders(self):
|
||||
"""
|
||||
Returns a dictionary of the response headers.
|
||||
"""
|
||||
return self.urllib3_response.getheaders()
|
||||
|
||||
def getheader(self, name, default=None):
|
||||
"""
|
||||
Returns a given response header.
|
||||
"""
|
||||
return self.urllib3_response.getheader(name, default)
|
||||
|
||||
|
||||
class RESTClientObject(object):
|
||||
|
||||
def __init__(self, pools_size=4, config=configuration):
|
||||
# urllib3.PoolManager will pass all kw parameters to connectionpool
|
||||
# https://github.com/shazow/urllib3/blob/f9409436f83aeb79fbaf090181cd81b784f1b8ce/urllib3/poolmanager.py#L75
|
||||
# https://github.com/shazow/urllib3/blob/f9409436f83aeb79fbaf090181cd81b784f1b8ce/urllib3/connectionpool.py#L680
|
||||
# ca_certs vs cert_file vs key_file
|
||||
# http://stackoverflow.com/a/23957365/2985775
|
||||
|
||||
# cert_reqs
|
||||
if config.verify_ssl:
|
||||
cert_reqs = ssl.CERT_REQUIRED
|
||||
else:
|
||||
cert_reqs = ssl.CERT_NONE
|
||||
|
||||
# ca_certs
|
||||
if config.ssl_ca_cert:
|
||||
ca_certs = config.ssl_ca_cert
|
||||
else:
|
||||
# if not set certificate file, use Mozilla's root certificates.
|
||||
ca_certs = certifi.where()
|
||||
|
||||
# cert_file
|
||||
cert_file = config.cert_file
|
||||
|
||||
# key file
|
||||
key_file = config.key_file
|
||||
|
||||
kwargs = {
|
||||
'num_pools': pools_size,
|
||||
'cert_reqs': cert_reqs,
|
||||
'ca_certs': ca_certs,
|
||||
'cert_file': cert_file,
|
||||
'key_file': key_file,
|
||||
}
|
||||
|
||||
if config.connection_pool_maxsize is not None:
|
||||
kwargs['maxsize'] = config.connection_pool_maxsize
|
||||
|
||||
if config.assert_hostname is not None:
|
||||
kwargs['assert_hostname'] = config.assert_hostname
|
||||
|
||||
# https pool manager
|
||||
if config.http_proxy_url is not None:
|
||||
self.pool_manager = urllib3.proxy_from_url(
|
||||
config.http_proxy_url, **kwargs
|
||||
)
|
||||
else:
|
||||
self.pool_manager = urllib3.PoolManager(
|
||||
**kwargs
|
||||
)
|
||||
|
||||
def request(self, method, url, query_params=None, headers=None,
|
||||
body=None, post_params=None, _preload_content=True,
|
||||
_request_timeout=None):
|
||||
"""
|
||||
:param method: http request method
|
||||
:param url: http request url
|
||||
:param query_params: query parameters in the url
|
||||
:param headers: http request headers
|
||||
:param body: request json body, for `application/json`
|
||||
:param post_params: request post parameters,
|
||||
`application/x-www-form-urlencoded`
|
||||
and `multipart/form-data`
|
||||
:param _preload_content: if False, the urllib3.HTTPResponse object will
|
||||
be returned without reading/decoding response
|
||||
data. Default is True.
|
||||
:param _request_timeout: timeout setting for this request. If one
|
||||
number provided, it will be total request
|
||||
timeout. It can also be a pair (tuple) of
|
||||
(connection, read) timeouts.
|
||||
"""
|
||||
method = method.upper()
|
||||
assert method in ['GET', 'HEAD', 'DELETE', 'POST', 'PUT', 'PATCH',
|
||||
'OPTIONS']
|
||||
|
||||
if post_params and body:
|
||||
raise ValueError(
|
||||
"body parameter cannot be used with post_params parameter."
|
||||
)
|
||||
|
||||
post_params = post_params or {}
|
||||
headers = headers or {}
|
||||
|
||||
timeout = None
|
||||
if _request_timeout:
|
||||
if isinstance(_request_timeout, (int, ) if PY3 else (int, long)):
|
||||
timeout = urllib3.Timeout(total=_request_timeout)
|
||||
elif (isinstance(_request_timeout, tuple) and
|
||||
len(_request_timeout) == 2):
|
||||
timeout = urllib3.Timeout(connect=_request_timeout[0],
|
||||
read=_request_timeout[1])
|
||||
|
||||
if 'Content-Type' not in headers:
|
||||
headers['Content-Type'] = 'application/json'
|
||||
|
||||
try:
|
||||
# For `POST`, `PUT`, `PATCH`, `OPTIONS`, `DELETE`
|
||||
if method in ['POST', 'PUT', 'PATCH', 'OPTIONS', 'DELETE']:
|
||||
if query_params:
|
||||
url += '?' + urlencode(query_params)
|
||||
if headers['Content-Type'] == 'application/json-patch+json':
|
||||
if not isinstance(body, list):
|
||||
headers['Content-Type'] = \
|
||||
'application/strategic-merge-patch+json'
|
||||
request_body = None
|
||||
if body:
|
||||
request_body = json.dumps(body)
|
||||
r = self.pool_manager.request(
|
||||
method, url, body=request_body,
|
||||
preload_content=_preload_content,
|
||||
timeout=timeout, headers=headers)
|
||||
elif re.search('json', headers['Content-Type'], re.IGNORECASE):
|
||||
request_body = None
|
||||
if body:
|
||||
request_body = json.dumps(body)
|
||||
r = self.pool_manager.request(
|
||||
method, url, body=request_body,
|
||||
preload_content=_preload_content, timeout=timeout,
|
||||
headers=headers)
|
||||
elif headers['Content-Type'] == \
|
||||
'application/x-www-form-urlencoded':
|
||||
r = self.pool_manager.request(
|
||||
method, url, fields=post_params,
|
||||
encode_multipart=False,
|
||||
preload_content=_preload_content, timeout=timeout,
|
||||
headers=headers)
|
||||
elif headers['Content-Type'] == 'multipart/form-data':
|
||||
# must del headers['Content-Type'], or the correct
|
||||
# Content-Type which generated by urllib3 will be
|
||||
# overwritten.
|
||||
del headers['Content-Type']
|
||||
r = self.pool_manager.request(
|
||||
method, url, fields=post_params, encode_multipart=True,
|
||||
preload_content=_preload_content, timeout=timeout,
|
||||
headers=headers)
|
||||
# Pass a `string` parameter directly in the body to support
|
||||
# other content types than Json when `body` argument is
|
||||
# provided in serialized form
|
||||
elif isinstance(body, str):
|
||||
request_body = body
|
||||
r = self.pool_manager.request(
|
||||
method, url, body=request_body,
|
||||
preload_content=_preload_content, timeout=timeout,
|
||||
headers=headers)
|
||||
else:
|
||||
# Cannot generate the request from given parameters
|
||||
msg = "Cannot prepare a request message for provided " \
|
||||
"arguments. \nPlease check that your arguments " \
|
||||
"match declared content type."
|
||||
raise ApiException(status=0, reason=msg)
|
||||
# For `GET`, `HEAD`
|
||||
else:
|
||||
r = self.pool_manager.request(method, url,
|
||||
fields=query_params,
|
||||
preload_content=_preload_content,
|
||||
timeout=timeout,
|
||||
headers=headers)
|
||||
except urllib3.exceptions.SSLError as e:
|
||||
msg = "{0}\n{1}".format(type(e).__name__, str(e))
|
||||
raise ApiException(status=0, reason=msg)
|
||||
|
||||
if _preload_content:
|
||||
r = RESTResponse(r)
|
||||
|
||||
# In the python 3, the response.data is bytes.
|
||||
# we need to decode it to string.
|
||||
if PY3:
|
||||
r.data = r.data.decode('utf8')
|
||||
|
||||
# log response body
|
||||
logger.debug("response body: %s", r.data)
|
||||
|
||||
if r.status not in range(200, 206):
|
||||
raise ApiException(http_resp=r)
|
||||
|
||||
return r
|
||||
|
||||
def GET(self, url, headers=None, query_params=None, _preload_content=True,
|
||||
_request_timeout=None):
|
||||
return self.request("GET", url,
|
||||
headers=headers,
|
||||
_preload_content=_preload_content,
|
||||
_request_timeout=_request_timeout,
|
||||
query_params=query_params)
|
||||
|
||||
def HEAD(self, url, headers=None, query_params=None, _preload_content=True,
|
||||
_request_timeout=None):
|
||||
return self.request("HEAD", url,
|
||||
headers=headers,
|
||||
_preload_content=_preload_content,
|
||||
_request_timeout=_request_timeout,
|
||||
query_params=query_params)
|
||||
|
||||
def OPTIONS(self, url, headers=None, query_params=None, post_params=None,
|
||||
body=None, _preload_content=True, _request_timeout=None):
|
||||
return self.request("OPTIONS", url,
|
||||
headers=headers,
|
||||
query_params=query_params,
|
||||
post_params=post_params,
|
||||
_preload_content=_preload_content,
|
||||
_request_timeout=_request_timeout,
|
||||
body=body)
|
||||
|
||||
def DELETE(self, url, headers=None, query_params=None, body=None,
|
||||
_preload_content=True, _request_timeout=None):
|
||||
return self.request("DELETE", url,
|
||||
headers=headers,
|
||||
query_params=query_params,
|
||||
_preload_content=_preload_content,
|
||||
_request_timeout=_request_timeout,
|
||||
body=body)
|
||||
|
||||
def POST(self, url, headers=None, query_params=None, post_params=None,
|
||||
body=None, _preload_content=True, _request_timeout=None):
|
||||
return self.request("POST", url,
|
||||
headers=headers,
|
||||
query_params=query_params,
|
||||
post_params=post_params,
|
||||
_preload_content=_preload_content,
|
||||
_request_timeout=_request_timeout,
|
||||
body=body)
|
||||
|
||||
def PUT(self, url, headers=None, query_params=None, post_params=None,
|
||||
body=None, _preload_content=True, _request_timeout=None):
|
||||
return self.request("PUT", url,
|
||||
headers=headers,
|
||||
query_params=query_params,
|
||||
post_params=post_params,
|
||||
_preload_content=_preload_content,
|
||||
_request_timeout=_request_timeout,
|
||||
body=body)
|
||||
|
||||
def PATCH(self, url, headers=None, query_params=None, post_params=None,
|
||||
body=None, _preload_content=True, _request_timeout=None):
|
||||
return self.request("PATCH", url,
|
||||
headers=headers,
|
||||
query_params=query_params,
|
||||
post_params=post_params,
|
||||
_preload_content=_preload_content,
|
||||
_request_timeout=_request_timeout,
|
||||
body=body)
|
||||
|
||||
|
||||
class ApiException(Exception):
|
||||
|
||||
def __init__(self, status=None, reason=None, http_resp=None):
|
||||
if http_resp:
|
||||
self.status = http_resp.status
|
||||
self.reason = http_resp.reason
|
||||
self.body = http_resp.data
|
||||
self.headers = http_resp.getheaders()
|
||||
else:
|
||||
self.status = status
|
||||
self.reason = reason
|
||||
self.body = None
|
||||
self.headers = None
|
||||
|
||||
def __str__(self):
|
||||
"""
|
||||
Custom error messages for exception
|
||||
"""
|
||||
error_message = "({0})\n"\
|
||||
"Reason: {1}\n".format(self.status, self.reason)
|
||||
if self.headers:
|
||||
error_message += "HTTP response headers: {0}\n"\
|
||||
.format(self.headers)
|
||||
|
||||
if self.body:
|
||||
error_message += "HTTP response body: {0}\n".format(self.body)
|
||||
|
||||
return error_message
|
||||
42
rest_test.py
42
rest_test.py
@ -1,42 +0,0 @@
|
||||
# Copyright 2017 The Kubernetes Authors.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
import unittest
|
||||
import urllib3
|
||||
|
||||
from mock import patch
|
||||
|
||||
from kubernetes.client import ConfigurationObject
|
||||
from kubernetes.client.rest import RESTClientObject
|
||||
|
||||
|
||||
class RestTest(unittest.TestCase):
|
||||
|
||||
def test_poolmanager(self):
|
||||
'Test that a poolmanager is created for rest client'
|
||||
with patch.object(urllib3, 'PoolManager') as pool:
|
||||
RESTClientObject(config=ConfigurationObject())
|
||||
pool.assert_called_once()
|
||||
|
||||
def test_proxy(self):
|
||||
'Test that proxy is created when the config especifies it'
|
||||
config = ConfigurationObject()
|
||||
config.http_proxy_url = 'http://proxy.example.com'
|
||||
|
||||
with patch.object(urllib3, 'proxy_from_url') as proxy:
|
||||
RESTClientObject(config=config)
|
||||
proxy.assert_called_once()
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
unittest.main()
|
||||
@ -48,9 +48,10 @@ class WSClient:
|
||||
if headers and 'authorization' in headers:
|
||||
header.append("authorization: %s" % headers['authorization'])
|
||||
|
||||
if configuration.ws_streaming_protocol:
|
||||
header.append("Sec-WebSocket-Protocol: %s" %
|
||||
configuration.ws_streaming_protocol)
|
||||
if headers and 'sec-websocket-protocol' in headers:
|
||||
header.append("sec-websocket-protocol: %s" % headers['sec-websocket-protocol'])
|
||||
else:
|
||||
header.append("sec-websocket-protocol: v4.channel.k8s.io")
|
||||
|
||||
if url.startswith('wss://') and configuration.verify_ssl:
|
||||
ssl_opts = {
|
||||
|
||||
Loading…
Reference in New Issue
Block a user