diff --git a/kubernetes/base/api_client.py b/kubernetes/base/api_client.py new file mode 100644 index 000000000..0e5e14abc --- /dev/null +++ b/kubernetes/base/api_client.py @@ -0,0 +1,647 @@ +# 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 + +from . import models +from . import ws_client +from .rest import RESTClientObject +from .rest import ApiException + +import os +import re +import json +import mimetypes +import tempfile +import threading + +from datetime import datetime +from datetime import date + +# python 2 and python 3 compatibility library +from six import PY3, integer_types, iteritems, text_type +from six.moves.urllib.parse import quote + +from .configuration import configuration + + +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: + callback(return_data) if _return_http_data_only 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 type(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 type(v) is 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 diff --git a/kubernetes/config/__init__.py b/kubernetes/base/config/__init__.py similarity index 100% rename from kubernetes/config/__init__.py rename to kubernetes/base/config/__init__.py diff --git a/kubernetes/base/config/config b/kubernetes/base/config/config new file mode 120000 index 000000000..30fa1ceaf --- /dev/null +++ b/kubernetes/base/config/config @@ -0,0 +1 @@ +config \ No newline at end of file diff --git a/kubernetes/config/config_exception.py b/kubernetes/base/config/config_exception.py similarity index 100% rename from kubernetes/config/config_exception.py rename to kubernetes/base/config/config_exception.py diff --git a/kubernetes/config/incluster_config.py b/kubernetes/base/config/incluster_config.py similarity index 100% rename from kubernetes/config/incluster_config.py rename to kubernetes/base/config/incluster_config.py diff --git a/kubernetes/config/incluster_config_test.py b/kubernetes/base/config/incluster_config_test.py similarity index 100% rename from kubernetes/config/incluster_config_test.py rename to kubernetes/base/config/incluster_config_test.py diff --git a/kubernetes/config/kube_config.py b/kubernetes/base/config/kube_config.py similarity index 100% rename from kubernetes/config/kube_config.py rename to kubernetes/base/config/kube_config.py diff --git a/kubernetes/config/kube_config_test.py b/kubernetes/base/config/kube_config_test.py similarity index 100% rename from kubernetes/config/kube_config_test.py rename to kubernetes/base/config/kube_config_test.py diff --git a/kubernetes/base/configuration.py b/kubernetes/base/configuration.py new file mode 100644 index 000000000..bf0fd7334 --- /dev/null +++ b/kubernetes/base/configuration.py @@ -0,0 +1,237 @@ +# coding: utf-8 + +""" + Kubernetes + + No description provided (generated by Swagger Codegen https://github.com/swagger-api/swagger-codegen) + + OpenAPI spec version: v1.5.0-snapshot + + Generated by: https://github.com/swagger-api/swagger-codegen.git + + 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 urllib3 + +import sys +import logging + +from six import iteritems +from six.moves import http_client as httplib + + +class ConfigurationObject(object): + """ + 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. + """ + + 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 + + @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 diff --git a/kubernetes/base/rest.py b/kubernetes/base/rest.py new file mode 100644 index 000000000..8b3a5dabd --- /dev/null +++ b/kubernetes/base/rest.py @@ -0,0 +1,324 @@ +# coding: utf-8 + +""" + Kubernetes + + No description provided (generated by Swagger Codegen https://github.com/swagger-api/swagger-codegen) + + OpenAPI spec version: v1.5.0-snapshot + + Generated by: https://github.com/swagger-api/swagger-codegen.git + + 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 ssl +import certifi +import logging +import re + +# 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.assert_hostname is not None: + kwargs['assert_hostname'] = config.assert_hostname + + # https pool manager + 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. + Please 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 diff --git a/kubernetes/watch/__init__.py b/kubernetes/base/watch/__init__.py similarity index 100% rename from kubernetes/watch/__init__.py rename to kubernetes/base/watch/__init__.py diff --git a/kubernetes/base/watch/watch b/kubernetes/base/watch/watch new file mode 120000 index 000000000..1655a60ff --- /dev/null +++ b/kubernetes/base/watch/watch @@ -0,0 +1 @@ +watch \ No newline at end of file diff --git a/kubernetes/watch/watch.py b/kubernetes/base/watch/watch.py similarity index 100% rename from kubernetes/watch/watch.py rename to kubernetes/base/watch/watch.py diff --git a/kubernetes/watch/watch_test.py b/kubernetes/base/watch/watch_test.py similarity index 100% rename from kubernetes/watch/watch_test.py rename to kubernetes/base/watch/watch_test.py diff --git a/kubernetes/client/api_client.py b/kubernetes/client/api_client.py deleted file mode 100644 index 0e5e14abc..000000000 --- a/kubernetes/client/api_client.py +++ /dev/null @@ -1,647 +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 - -from . import models -from . import ws_client -from .rest import RESTClientObject -from .rest import ApiException - -import os -import re -import json -import mimetypes -import tempfile -import threading - -from datetime import datetime -from datetime import date - -# python 2 and python 3 compatibility library -from six import PY3, integer_types, iteritems, text_type -from six.moves.urllib.parse import quote - -from .configuration import configuration - - -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: - callback(return_data) if _return_http_data_only 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 type(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 type(v) is 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 diff --git a/kubernetes/client/api_client.py b/kubernetes/client/api_client.py new file mode 120000 index 000000000..b07acd767 --- /dev/null +++ b/kubernetes/client/api_client.py @@ -0,0 +1 @@ +../base/api_client.py \ No newline at end of file diff --git a/kubernetes/client/configuration.py b/kubernetes/client/configuration.py deleted file mode 100644 index bf0fd7334..000000000 --- a/kubernetes/client/configuration.py +++ /dev/null @@ -1,237 +0,0 @@ -# coding: utf-8 - -""" - Kubernetes - - No description provided (generated by Swagger Codegen https://github.com/swagger-api/swagger-codegen) - - OpenAPI spec version: v1.5.0-snapshot - - Generated by: https://github.com/swagger-api/swagger-codegen.git - - 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 urllib3 - -import sys -import logging - -from six import iteritems -from six.moves import http_client as httplib - - -class ConfigurationObject(object): - """ - 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. - """ - - 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 - - @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 diff --git a/kubernetes/client/configuration.py b/kubernetes/client/configuration.py new file mode 120000 index 000000000..68b37bf02 --- /dev/null +++ b/kubernetes/client/configuration.py @@ -0,0 +1 @@ +../base/configuration.py \ No newline at end of file diff --git a/kubernetes/client/rest.py b/kubernetes/client/rest.py deleted file mode 100644 index 8b3a5dabd..000000000 --- a/kubernetes/client/rest.py +++ /dev/null @@ -1,324 +0,0 @@ -# coding: utf-8 - -""" - Kubernetes - - No description provided (generated by Swagger Codegen https://github.com/swagger-api/swagger-codegen) - - OpenAPI spec version: v1.5.0-snapshot - - Generated by: https://github.com/swagger-api/swagger-codegen.git - - 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 ssl -import certifi -import logging -import re - -# 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.assert_hostname is not None: - kwargs['assert_hostname'] = config.assert_hostname - - # https pool manager - 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. - Please 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 diff --git a/kubernetes/client/rest.py b/kubernetes/client/rest.py new file mode 120000 index 000000000..dd1544768 --- /dev/null +++ b/kubernetes/client/rest.py @@ -0,0 +1 @@ +../base/rest.py \ No newline at end of file diff --git a/kubernetes/config b/kubernetes/config new file mode 120000 index 000000000..e65cfe84e --- /dev/null +++ b/kubernetes/config @@ -0,0 +1 @@ +base/config \ No newline at end of file diff --git a/kubernetes/watch b/kubernetes/watch new file mode 120000 index 000000000..b3079b32f --- /dev/null +++ b/kubernetes/watch @@ -0,0 +1 @@ +base/watch \ No newline at end of file