Skip to content

Commit

Permalink
Merge pull request #145 from 2gis/deprecation-warnings
Browse files Browse the repository at this point in the history
Replace Deprecation Checker with Warning response header support
  • Loading branch information
seleznev committed Nov 9, 2022
2 parents cbade9e + c79e1be commit d9fc23a
Show file tree
Hide file tree
Showing 16 changed files with 316 additions and 461 deletions.
12 changes: 0 additions & 12 deletions k8s_handle/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,10 +11,8 @@
from k8s_handle import templating
from k8s_handle.exceptions import ProvisioningError, ResourceNotAvailableError
from k8s_handle.filesystem import InvalidYamlError
from k8s_handle.k8s.deprecation_checker import ApiDeprecationChecker
from k8s_handle.k8s.provisioner import Provisioner
from k8s_handle.k8s.diff import Diff
from k8s_handle.k8s.availability_checker import ResourceAvailabilityChecker, make_resource_getters_list

COMMAND_DEPLOY = 'deploy'
COMMAND_DIFF = 'diff'
Expand Down Expand Up @@ -109,16 +107,6 @@ def _handler_provision(command, resources, priority_evaluator, use_kubeconfig, s
log.info("Default namespace is not set. "
"This may lead to provisioning error, if namespace is not set for each resource.")

try:
deprecation_checker = ApiDeprecationChecker(client.VersionApi().get_code().git_version[1:])
available_checker = ResourceAvailabilityChecker(make_resource_getters_list())

for resource in resources:
deprecation_checker.run(resource)
available_checker.run(resource)
except client.exceptions.ApiException:
log.warning("Error while getting API version, deprecation check will be skipped.")

if command == COMMAND_DIFF:
executor = Diff()
else:
Expand Down
4 changes: 4 additions & 0 deletions k8s_handle/exceptions.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,10 @@ class ResourceNotAvailableError(Exception):
pass


class InvalidWarningHeader(Exception):
pass


class InvalidYamlError(Exception):
pass

Expand Down
11 changes: 7 additions & 4 deletions k8s_handle/k8s/adapters.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
from k8s_handle.exceptions import ProvisioningError
from k8s_handle.transforms import add_indent, split_str_by_capital_letters
from .api_extensions import ResourcesAPI
from .api_clients import ApiClientWithWarningHandler
from .mocks import K8sClientMock

log = logging.getLogger(__name__)
Expand Down Expand Up @@ -48,7 +49,9 @@ def __init__(self, spec):
self.namespace = spec.get('metadata', {}).get('namespace', "") or settings.K8S_NAMESPACE

@staticmethod
def get_instance(spec, api_custom_objects=None, api_resources=None):
def get_instance(spec, api_custom_objects=None, api_resources=None, warning_handler=None):
api_client = ApiClientWithWarningHandler(warning_handler=warning_handler)

# due to https://github.com/kubernetes-client/python/issues/387
if spec.get('kind') in Adapter.kinds_builtin:
if spec.get('apiVersion') == 'test/test':
Expand All @@ -59,10 +62,10 @@ def get_instance(spec, api_custom_objects=None, api_resources=None):
if not api:
return None

return AdapterBuiltinKind(spec, api())
return AdapterBuiltinKind(spec, api(api_client=api_client))

api_custom_objects = api_custom_objects or client.CustomObjectsApi()
api_resources = api_resources or ResourcesAPI()
api_custom_objects = api_custom_objects or client.CustomObjectsApi(api_client=api_client)
api_resources = api_resources or ResourcesAPI(api_client=api_client)
return AdapterCustomKind(spec, api_custom_objects, api_resources)


Expand Down
154 changes: 154 additions & 0 deletions k8s_handle/k8s/api_clients.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,154 @@
import re
import logging

from kubernetes.client.api_client import ApiClient

from k8s_handle.exceptions import InvalidWarningHeader

log = logging.getLogger(__name__)


class ApiClientWithWarningHandler(ApiClient):
def __init__(self, *args, **kwargs):
self.warning_handler = kwargs.pop("warning_handler", None)

ApiClient.__init__(self, *args, **kwargs)

def request(self, *args, **kwargs):
response_data = ApiClient.request(self, *args, **kwargs)

if self.warning_handler is not None:
headers = response_data.getheaders()

if "Warning" in headers:
self._handle_warnings([headers["Warning"]], self.warning_handler)

return response_data

@staticmethod
def _handle_warnings(headers, handler):
try:
warnings = ApiClientWithWarningHandler._parse_warning_headers(headers)
except InvalidWarningHeader as e:
log.debug("Warning headers: {}".format(headers))
log.error(e)
return

for warning in warnings:
handler.handle_warning_header(*warning)

@staticmethod
def _parse_warning_headers(headers):
"""
Based on `ParseWarningHeaders()` from k8s.io/apimachinery/pkg/util/net package.
"""
results = []

for header in headers:
while len(header) > 0:
result, remainder = ApiClientWithWarningHandler._parse_warning_header(header)
results += [result]
header = remainder

return results

@staticmethod
def _parse_warning_header(header):
"""
Based on `ParseWarningHeader()` from k8s.io/apimachinery/pkg/util/net package,
but with much more permissive validation rules.
"""

parts = header.split(" ", maxsplit=2)
if len(parts) != 3:
raise InvalidWarningHeader("Invalid warning header: fewer than 3 segments")

(code, agent, textDateRemainder) = (parts[0], parts[1], parts[2])

# verify code format
codeMatcher = re.compile("^[0-9]{3}$")
if not codeMatcher.match(code):
raise InvalidWarningHeader("Invalid warning header: code segment is not 3 digits")

code = int(code)

# verify agent presence
if len(agent) == 0:
raise InvalidWarningHeader("Invalid warning header: empty agent segment")

# verify textDateRemainder presence
if len(textDateRemainder) == 0:
raise InvalidWarningHeader("Invalid warning header: empty text segment")

# extract text
text, dateAndRemainder = ApiClientWithWarningHandler._parse_quoted_string(textDateRemainder)

result = (code, agent, text)
remainder = ""

if len(dateAndRemainder) > 0:
if dateAndRemainder[0] == '"':
# consume date
foundEndQuote = False
for i in range(1, len(dateAndRemainder)):
if dateAndRemainder[i] == '"':
foundEndQuote = True
remainder = dateAndRemainder[i+1:].strip()
break

if not foundEndQuote:
raise InvalidWarningHeader("Invalid warning header: unterminated date segment")
else:
remainder = dateAndRemainder

if len(remainder) > 0:
if remainder[0] == ',':
# consume comma if present
remainder = remainder[1:].strip()
else:
raise InvalidWarningHeader("Invalid warning header: unexpected token after warn-date")

return result, remainder

@staticmethod
def _parse_quoted_string(quotedString):
"""
Based on `parseQuotedString()` from k8s.io/apimachinery/pkg/util/net package.
"""

if len(quotedString) == 0:
raise InvalidWarningHeader("Invalid warning header: invalid quoted string: 0-length")

if quotedString[0] != '"':
raise InvalidWarningHeader("Invalid warning header: invalid quoted string: missing initial quote")

quotedString = quotedString[1:]
remainder = ""
escaping = False
closedQuote = False
result = ""

for i in range(0, len(quotedString)):
b = quotedString[i]
if b == '"':
if escaping:
result += b
escaping = False
else:
closedQuote = True
remainder = quotedString[i+1:].strip()
break
elif b == '\\':
if escaping:
result += b
escaping = False
else:
escaping = True
else:
result += b
escaping = False

if not closedQuote:
raise InvalidWarningHeader("Invalid warning header: invalid quoted string: missing closing quote")

return (result, remainder)
4 changes: 0 additions & 4 deletions k8s_handle/k8s/availability_checker/__init__.py

This file was deleted.

30 changes: 0 additions & 30 deletions k8s_handle/k8s/availability_checker/checker.py

This file was deleted.

3 changes: 0 additions & 3 deletions k8s_handle/k8s/availability_checker/mocks.py

This file was deleted.

71 changes: 0 additions & 71 deletions k8s_handle/k8s/availability_checker/resource_getters.py

This file was deleted.

39 changes: 0 additions & 39 deletions k8s_handle/k8s/availability_checker/test_availability_checker.py

This file was deleted.

Loading

0 comments on commit d9fc23a

Please sign in to comment.