Skip to content

Commit

Permalink
Merge pull request #73 from jeronimoalbi/develop
Browse files Browse the repository at this point in the history
 Version update to `1.0.3`
  • Loading branch information
jeronimoalbi authored Apr 27, 2017
2 parents 911e857 + 024a0ed commit ce2332e
Show file tree
Hide file tree
Showing 12 changed files with 175 additions and 52 deletions.
12 changes: 12 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,18 @@ All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](http://keepachangelog.com/)
and this project adheres to [Semantic Versioning](http://semver.org/).

## [1.0.3] - 2017-04-27
### Added
- Added support to run a component server and handle a single request
where the payload is send from the CLI in a JSON file (#72).

### Changed
- Logging was changed to use byte strings without encoding them first.
- `HttpActionSchema.get_method()` now returns method names in lower case.

### Fixed
- Engine variables now works with request and response middlewares.

## [1.0.2] - 2017-03-28
### Added
- Version wildcards support for single '*' to match all.
Expand Down
2 changes: 1 addition & 1 deletion katana/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,4 +12,4 @@

__license__ = "MIT"
__copyright__ = "Copyright (c) 2016-2017 KUSANAGI S.L. (http://kusanagi.io)"
__version__ = '1.0.2'
__version__ = '1.0.3'
4 changes: 2 additions & 2 deletions katana/api/schema/action.py
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,7 @@ def entity_from_payload(entity_payload, entity=None):
if path_exists(payload, 'field') or path_exists(payload, 'fields'):
fieldset = entity_from_payload(payload, fieldset)

entity['fields'].append(fieldset)
entity['fields'].append(fieldset)

return entity

Expand Down Expand Up @@ -505,7 +505,7 @@ def get_method(self):
"""

return self.__payload.get('method', 'get')
return self.__payload.get('method', 'get').lower()

def get_path(self):
"""Get URL path for the action.
Expand Down
3 changes: 2 additions & 1 deletion katana/logging.py
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,8 @@ def value_to_log_string(value, max_chars=100000):
elif isinstance(value, str):
output = value
elif isinstance(value, bytes):
output = value.decode('utf8')
# Binary data is logged directly
output = value
elif isinstance(value, (dict, list, tuple)):
output = json.serialize(value, prettify=True).decode('utf8')
elif isinstance(value, types.FunctionType):
Expand Down
2 changes: 2 additions & 0 deletions katana/middleware.py
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,7 @@ def _create_request_component_instance(self, payload):
self.component_name,
self.component_version,
self.framework_version,
variables=self.variables,
debug=self.debug,
# TODO: Use meta and call as arguments instead these many kwargs
service_name=payload.get('call/service'),
Expand All @@ -98,6 +99,7 @@ def _create_response_component_instance(self, payload):
self.component_version,
self.framework_version,
debug=self.debug,
variables=self.variables,
# TODO: Use meta and argument
gateway_protocol=payload.get('meta/protocol'),
gateway_addresses=payload.get('meta/gateway'),
Expand Down
83 changes: 61 additions & 22 deletions katana/sdk/runner.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,13 +13,15 @@
import asyncio
import functools
import inspect
import json
import logging
import os

import click
import katana.payload
import zmq.asyncio

from ..errors import KatanaError
from ..logging import setup_katana_logging
from ..utils import EXIT_ERROR
from ..utils import EXIT_OK
Expand Down Expand Up @@ -222,45 +224,52 @@ def get_argument_options(self):
"""

return [
click.option(
'-A', '--action',
help=(
'Name of the action to call when request message '
'is given as JSON through stdin.'
),
),
click.option(
'-c', '--component',
type=click.Choice(['service', 'middleware']),
help='Component type',
help='Component type.',
required=True,
),
click.option(
'-d', '--disable-compact-names',
is_flag=True,
help='Use full property names instead of compact in payloads.',
help='Use full property names in payloads.',
),
click.option(
'-n', '--name',
required=True,
help='Component name',
help='Component name.',
),
click.option(
'-p', '--framework-version',
required=True,
help='KATANA framework version',
help='KATANA framework version.',
),
click.option(
'-q', '--quiet',
is_flag=True,
help='Disable all logs',
help='Disable all logs.',
),
click.option(
'-s', '--socket',
help='IPC socket name',
help='IPC socket name.',
),
click.option(
'-t', '--tcp',
help='TCP port',
help='TCP port to use when IPC socket is not used.',
type=click.INT,
),
click.option(
'-v', '--version',
required=True,
help='Component version',
help='Component version.',
),
click.option(
'-D', '--debug',
Expand All @@ -270,7 +279,7 @@ def get_argument_options(self):
'-V', '--var',
multiple=True,
callback=key_value_strings_callback,
help='Variables',
help='Component variables.',
),
]

Expand Down Expand Up @@ -326,23 +335,38 @@ def run(self, **kwargs):

self._args = kwargs

# Initialize component logging only when `quiet` argument is False
# Standard input is read only when action name is given
message = {}
if kwargs.get('action'):
contents = click.get_text_stream('stdin', encoding='utf8').read()

# Add JSON file contents to message
try:
message['payload'] = json.loads(contents)
except:
LOG.exception('Stdin input value is not valid JSON')
os._exit(EXIT_ERROR)

# Add action name to message
message['action'] = kwargs['action']

# Initialize component logging only when `quiet` argument is False, or
# if an input message is given init logging only when debug is True
if not kwargs.get('quiet'):
setup_katana_logging(logging.DEBUG if self.debug else logging.INFO)

LOG.debug('Using PID: "%s"', os.getpid())

# Set main event loop
install_uvevent_loop()
self.loop = zmq.asyncio.ZMQEventLoop()
asyncio.set_event_loop(self.loop)

# Create channel for TCP or IPC conections
if self.tcp_port:
channel = tcp('127.0.0.1:{}'.format(self.tcp_port))
# Skip zeromq initialization when transport payload is given
# as an input file in the CLI.
if message:
# Use standard event loop to run component server without zeromq
self.loop = asyncio.get_event_loop()
else:
# Abstract domain unix socket
channel = 'ipc://{}'.format(self.socket_name)
# Set zeromq event loop when component is run as server
install_uvevent_loop()
self.loop = zmq.asyncio.ZMQEventLoop()
asyncio.set_event_loop(self.loop)

# When compact mode is enabled use long payload field names
if not self.compact_names:
Expand All @@ -353,14 +377,25 @@ def run(self, **kwargs):

# Create component server and run it as a task
server = self.server_cls(
channel,
self.callbacks,
self.args,
debug=self.debug,
source_file=self.source_file,
error_callback=self.__error_callback,
)
server_task = self.loop.create_task(server.listen())

if message:
server_task = self.loop.create_task(server.process_input(message))
else:
# Create channel for TCP or IPC conections
if self.tcp_port:
channel = tcp('127.0.0.1:{}'.format(self.tcp_port))
else:
# Abstract domain unix socket
channel = 'ipc://{}'.format(self.socket_name)

server_task = self.loop.create_task(server.listen(channel))

ctx.tasks.append(server_task)

# By default exit successfully
Expand All @@ -387,6 +422,10 @@ def run(self, **kwargs):
else:
LOG.error(err.strerror)

LOG.error('Component failed')
except KatanaError as err:
exit_code = EXIT_ERROR
LOG.error(err)
LOG.error('Component failed')
except Exception as exc:
exit_code = EXIT_ERROR
Expand Down
Loading

0 comments on commit ce2332e

Please sign in to comment.