summaryrefslogtreecommitdiffstats
path: root/azure/aria/aria-extension-cloudify/src/aria/aria/orchestrator/execution_plugin/ctx_proxy/client.py
blob: 84d66f196850111abe498cf256985e5ddca2c23e (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
#! /usr/bin/env python
# Licensed to the Apache Software Foundation (ASF) under one or more
# contributor license agreements.  See the NOTICE file distributed with
# this work for additional information regarding copyright ownership.
# The ASF licenses this file to You 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.

"""
``ctx`` proxy client implementation.
"""

import argparse
import json
import os
import sys
import urllib2


# Environment variable for the socket url (used by clients to locate the socket)
CTX_SOCKET_URL = 'CTX_SOCKET_URL'


class _RequestError(RuntimeError):

    def __init__(self, ex_message, ex_type, ex_traceback):
        super(_RequestError, self).__init__(self, '{0}: {1}'.format(ex_type, ex_message))
        self.ex_type = ex_type
        self.ex_message = ex_message
        self.ex_traceback = ex_traceback


def _http_request(socket_url, request, method, timeout):
    opener = urllib2.build_opener(urllib2.HTTPHandler)
    request = urllib2.Request(socket_url, data=json.dumps(request))
    request.get_method = lambda: method
    response = opener.open(request, timeout=timeout)

    if response.code != 200:
        raise RuntimeError('Request failed: {0}'.format(response))
    return json.loads(response.read())


def _client_request(socket_url, args, timeout, method='POST'):
    response = _http_request(
        socket_url=socket_url,
        request={'args': args},
        method=method,
        timeout=timeout
    )
    payload = response.get('payload')
    response_type = response.get('type')
    if response_type == 'error':
        ex_type = payload['type']
        ex_message = payload['message']
        ex_traceback = payload['traceback']
        raise _RequestError(ex_message, ex_type, ex_traceback)
    elif response_type == 'stop_operation':
        raise SystemExit(payload['message'])
    else:
        return payload


def _parse_args(args):
    parser = argparse.ArgumentParser()
    parser.add_argument('-t', '--timeout', type=int, default=30)
    parser.add_argument('--socket-url', default=os.environ.get(CTX_SOCKET_URL))
    parser.add_argument('--json-arg-prefix', default='@')
    parser.add_argument('-j', '--json-output', action='store_true')
    parser.add_argument('args', nargs='*')
    args = parser.parse_args(args=args)
    if not args.socket_url:
        raise RuntimeError('Missing CTX_SOCKET_URL environment variable '
                           'or socket_url command line argument. (ctx is supposed to be executed '
                           'within an operation context)')
    return args


def _process_args(json_prefix, args):
    processed_args = []
    for arg in args:
        if arg.startswith(json_prefix):
            arg = json.loads(arg[1:])
        processed_args.append(arg)
    return processed_args


def main(args=None):
    args = _parse_args(args)
    response = _client_request(
        args.socket_url,
        args=_process_args(args.json_arg_prefix, args.args),
        timeout=args.timeout)
    if args.json_output:
        response = json.dumps(response)
    else:
        if response is None:
            response = ''
        try:
            response = str(response)
        except UnicodeEncodeError:
            response = unicode(response).encode('utf8')
    sys.stdout.write(response)

if __name__ == '__main__':
    main()