Mitch Garnaat

A bunch of changes to support new unit testing strategy with placebo. More tests to come.

Showing 42 changed files with 649 additions and 262 deletions
...@@ -3,6 +3,7 @@ python: ...@@ -3,6 +3,7 @@ python:
3 - "2.7" 3 - "2.7"
4 - "3.3" 4 - "3.3"
5 - "3.4" 5 - "3.4"
6 + - "3.5"
6 install: 7 install:
7 - pip install -r requirements.txt 8 - pip install -r requirements.txt
8 - pip install coverage python-coveralls 9 - pip install coverage python-coveralls
......
...@@ -16,18 +16,18 @@ import logging ...@@ -16,18 +16,18 @@ import logging
16 16
17 import jmespath 17 import jmespath
18 import boto3 18 import boto3
19 -import placebo
20 19
21 20
22 LOG = logging.getLogger(__name__) 21 LOG = logging.getLogger(__name__)
23 22
23 +_session_cache = {}
24 +
24 25
25 class AWSClient(object): 26 class AWSClient(object):
26 27
27 - def __init__(self, service_name, region_name, profile_name): 28 + def __init__(self, service_name, session):
28 self._service_name = service_name 29 self._service_name = service_name
29 - self._region_name = region_name 30 + self._session = session
30 - self._profile_name = profile_name
31 self.client = self._create_client() 31 self.client = self._create_client()
32 32
33 @property 33 @property
...@@ -35,20 +35,11 @@ class AWSClient(object): ...@@ -35,20 +35,11 @@ class AWSClient(object):
35 return self._service_name 35 return self._service_name
36 36
37 @property 37 @property
38 - def region_name(self): 38 + def session(self):
39 - return self._region_name 39 + return self._session
40 -
41 - @property
42 - def profile_name(self):
43 - return self._profile_name
44 40
45 def _create_client(self): 41 def _create_client(self):
46 - global recording_path 42 + client = self._session.client(self._service_name)
47 - session = boto3.session.Session(
48 - region_name=self._region_name, profile_name=self._profile_name)
49 - if recording_path:
50 - placebo.attach(session)
51 - client = session.client(self._service_name)
52 return client 43 return client
53 44
54 def call(self, op_name, query=None, **kwargs): 45 def call(self, op_name, query=None, **kwargs):
...@@ -93,17 +84,15 @@ class AWSClient(object): ...@@ -93,17 +84,15 @@ class AWSClient(object):
93 return data 84 return data
94 85
95 86
96 -_client_cache = {} 87 +def create_session(profile_name, region_name):
97 - 88 + global _session_cache
98 -recording_path = None 89 + session_key = '{}:{}'.format(profile_name, region_name)
90 + if session_key not in _session_cache:
91 + session = boto3.session.Session(
92 + region_name=region_name, profile_name=profile_name)
93 + _session_cache[session_key] = session
94 + return _session_cache[session_key]
99 95
100 96
101 -def create_client(service_name, context): 97 +def create_client(service_name, session):
102 - global _client_cache 98 + return AWSClient(service_name, session)
103 - client_key = '{}:{}:{}'.format(service_name, context.region,
104 - context.profile)
105 - if client_key not in _client_cache:
106 - client = AWSClient(service_name, context.region,
107 - context.profile)
108 - _client_cache[client_key] = client
109 - return _client_cache[client_key]
......
...@@ -24,6 +24,8 @@ import kappa.policy ...@@ -24,6 +24,8 @@ import kappa.policy
24 import kappa.role 24 import kappa.role
25 import kappa.awsclient 25 import kappa.awsclient
26 26
27 +import placebo
28 +
27 LOG = logging.getLogger(__name__) 29 LOG = logging.getLogger(__name__)
28 30
29 DebugFmtString = '%(asctime)s - %(name)s - %(levelname)s - %(message)s' 31 DebugFmtString = '%(asctime)s - %(name)s - %(levelname)s - %(message)s'
...@@ -38,10 +40,15 @@ class Context(object): ...@@ -38,10 +40,15 @@ class Context(object):
38 self.set_logger('kappa', logging.DEBUG) 40 self.set_logger('kappa', logging.DEBUG)
39 else: 41 else:
40 self.set_logger('kappa', logging.INFO) 42 self.set_logger('kappa', logging.INFO)
41 - kappa.awsclient.recording_path = recording_path
42 self._load_cache() 43 self._load_cache()
43 self.config = yaml.load(config_file) 44 self.config = yaml.load(config_file)
44 self.environment = environment 45 self.environment = environment
46 + profile = self.config['environments'][self.environment]['profile']
47 + region = self.config['environments'][self.environment]['region']
48 + self.session = kappa.awsclient.create_session(profile, region)
49 + if recording_path:
50 + self.pill = placebo.attach(self.session, recording_path)
51 + self.pill.record()
45 self.policy = kappa.policy.Policy( 52 self.policy = kappa.policy.Policy(
46 self, self.config['environments'][self.environment]) 53 self, self.config['environments'][self.environment])
47 self.role = kappa.role.Role( 54 self.role = kappa.role.Role(
......
...@@ -48,7 +48,8 @@ class KinesisEventSource(EventSource): ...@@ -48,7 +48,8 @@ class KinesisEventSource(EventSource):
48 48
49 def __init__(self, context, config): 49 def __init__(self, context, config):
50 super(KinesisEventSource, self).__init__(context, config) 50 super(KinesisEventSource, self).__init__(context, config)
51 - self._lambda = kappa.awsclient.create_client('kinesis', context) 51 + self._lambda = kappa.awsclient.create_client(
52 + 'kinesis', context.session)
52 53
53 def _get_uuid(self, function): 54 def _get_uuid(self, function):
54 uuid = None 55 uuid = None
...@@ -150,7 +151,7 @@ class S3EventSource(EventSource): ...@@ -150,7 +151,7 @@ class S3EventSource(EventSource):
150 151
151 def __init__(self, context, config): 152 def __init__(self, context, config):
152 super(S3EventSource, self).__init__(context, config) 153 super(S3EventSource, self).__init__(context, config)
153 - self._s3 = kappa.awsclient.create_client('s3', config) 154 + self._s3 = kappa.awsclient.create_client('s3', context.session)
154 155
155 def _make_notification_id(self, function_name): 156 def _make_notification_id(self, function_name):
156 return 'Kappa-%s-notification' % function_name 157 return 'Kappa-%s-notification' % function_name
...@@ -213,7 +214,7 @@ class SNSEventSource(EventSource): ...@@ -213,7 +214,7 @@ class SNSEventSource(EventSource):
213 214
214 def __init__(self, context, config): 215 def __init__(self, context, config):
215 super(SNSEventSource, self).__init__(context, config) 216 super(SNSEventSource, self).__init__(context, config)
216 - self._sns = kappa.awsclient.create_client('sns', context) 217 + self._sns = kappa.awsclient.create_client('sns', context.session)
217 218
218 def _make_notification_id(self, function_name): 219 def _make_notification_id(self, function_name):
219 return 'Kappa-%s-notification' % function_name 220 return 'Kappa-%s-notification' % function_name
......
...@@ -33,7 +33,7 @@ class Function(object): ...@@ -33,7 +33,7 @@ class Function(object):
33 self._context = context 33 self._context = context
34 self._config = config 34 self._config = config
35 self._lambda_client = kappa.awsclient.create_client( 35 self._lambda_client = kappa.awsclient.create_client(
36 - 'lambda', context) 36 + 'lambda', context.session)
37 self._response = None 37 self._response = None
38 self._log = None 38 self._log = None
39 39
......
...@@ -26,7 +26,8 @@ class Log(object): ...@@ -26,7 +26,8 @@ class Log(object):
26 def __init__(self, context, log_group_name): 26 def __init__(self, context, log_group_name):
27 self._context = context 27 self._context = context
28 self.log_group_name = log_group_name 28 self.log_group_name = log_group_name
29 - self._log_client = kappa.awsclient.create_client('logs', context) 29 + self._log_client = kappa.awsclient.create_client(
30 + 'logs', context.session)
30 31
31 def _check_for_log_group(self): 32 def _check_for_log_group(self):
32 LOG.debug('checking for log group') 33 LOG.debug('checking for log group')
......
...@@ -26,7 +26,8 @@ class Policy(object): ...@@ -26,7 +26,8 @@ class Policy(object):
26 def __init__(self, context, config): 26 def __init__(self, context, config):
27 self._context = context 27 self._context = context
28 self._config = config 28 self._config = config
29 - self._iam_client = kappa.awsclient.create_client('iam', self._context) 29 + self._iam_client = kappa.awsclient.create_client(
30 + 'iam', self._context.session)
30 self._arn = self._config['policy'].get('arn', None) 31 self._arn = self._config['policy'].get('arn', None)
31 32
32 @property 33 @property
...@@ -75,10 +76,11 @@ class Policy(object): ...@@ -75,10 +76,11 @@ class Policy(object):
75 def _find_all_policies(self): 76 def _find_all_policies(self):
76 try: 77 try:
77 response = self._iam_client.call( 78 response = self._iam_client.call(
78 - 'list_policies') 79 + 'list_policies', PathPrefix=self.path)
79 except Exception: 80 except Exception:
80 LOG.exception('Error listing policies') 81 LOG.exception('Error listing policies')
81 - return response['Policies'] 82 + response = {}
83 + return response.get('Policies', list())
82 84
83 def _list_versions(self): 85 def _list_versions(self):
84 try: 86 try:
......
...@@ -40,7 +40,8 @@ class Role(object): ...@@ -40,7 +40,8 @@ class Role(object):
40 def __init__(self, context, config): 40 def __init__(self, context, config):
41 self._context = context 41 self._context = context
42 self._config = config 42 self._config = config
43 - self._iam_client = kappa.awsclient.create_client('iam', context) 43 + self._iam_client = kappa.awsclient.create_client(
44 + 'iam', context.session)
44 self._arn = None 45 self._arn = None
45 46
46 @property 47 @property
...@@ -64,7 +65,8 @@ class Role(object): ...@@ -64,7 +65,8 @@ class Role(object):
64 response = self._iam_client.call('list_roles') 65 response = self._iam_client.call('list_roles')
65 except Exception: 66 except Exception:
66 LOG.exception('Error listing roles') 67 LOG.exception('Error listing roles')
67 - return response['Roles'] 68 + response = {}
69 + return response.get('Roles', list())
68 70
69 def exists(self): 71 def exists(self):
70 for role in self._find_all_roles(): 72 for role in self._find_all_roles():
......
1 boto3>=1.2.2 1 boto3>=1.2.2
2 -placebo>=0.3.0 2 +placebo>=0.4.1
3 click==5.1 3 click==5.1
4 PyYAML>=3.11 4 PyYAML>=3.11
5 mock>=1.0.1 5 mock>=1.0.1
......
1 +.kappa/
2 +kappa.yml
...@@ -6,7 +6,7 @@ import os ...@@ -6,7 +6,7 @@ import os
6 6
7 requires = [ 7 requires = [
8 'boto3>=1.2.2', 8 'boto3>=1.2.2',
9 - 'placebo>=0.3.0', 9 + 'placebo>=0.4.1',
10 'click>=5.0', 10 'click>=5.0',
11 'PyYAML>=3.11' 11 'PyYAML>=3.11'
12 ] 12 ]
...@@ -36,10 +36,10 @@ setup( ...@@ -36,10 +36,10 @@ setup(
36 'Natural Language :: English', 36 'Natural Language :: English',
37 'License :: OSI Approved :: Apache Software License', 37 'License :: OSI Approved :: Apache Software License',
38 'Programming Language :: Python', 38 'Programming Language :: Python',
39 - 'Programming Language :: Python :: 2.6',
40 'Programming Language :: Python :: 2.7', 39 'Programming Language :: Python :: 2.7',
41 'Programming Language :: Python :: 3', 40 'Programming Language :: Python :: 3',
42 'Programming Language :: Python :: 3.3', 41 'Programming Language :: Python :: 3.3',
43 - 'Programming Language :: Python :: 3.4' 42 + 'Programming Language :: Python :: 3.4',
43 + 'Programming Language :: Python :: 3.5'
44 ), 44 ),
45 ) 45 )
......
1 +[foobar]
2 +aws_access_key_id = foo
3 +aws_secret_access_key = bar
1 -{
2 - "Statement":[
3 - {"Condition":
4 - {"ArnLike":{"AWS:SourceArn":"arn:aws:sns:us-east-1:123456789012:lambda_topic"}},
5 - "Resource":"arn:aws:lambda:us-east-1:123456789023:function:messageStore",
6 - "Action":"lambda:invokeFunction",
7 - "Principal":{"Service":"sns.amazonaws.com"},
8 - "Sid":"sns invoke","Effect":"Allow"
9 - }],
10 - "Id":"default",
11 - "Version":"2012-10-17"
12 -}
1 +dev: {config_md5: 3ccd0a5630fa4e0d0effeb9de0b551a3, policy_md5: 12273b7917929c02cfc755f4700e1e2b,
2 + zip_md5: b6605fd4990542106fa95b62ea62d70e}
1 +
2 +
3 +def handler(event, context):
4 + return {'status': 'success'}
No preview for this file type
1 +---
2 +name: kappa-simple
3 +environments:
4 + dev:
5 + profile: foobar
6 + region: us-west-2
7 + policy:
8 + resources:
9 + - arn: arn:aws:logs:*:*:*
10 + actions:
11 + - "*"
12 +lambda:
13 + description: Foo the Bar
14 + handler: simple.handler
15 + runtime: python2.7
16 + memory_size: 256
17 + timeout: 3
1 -import inspect
2 -
3 -import mock
4 -
5 -import tests.unit.responses as responses
6 -
7 -
8 -class MockAWS(object):
9 -
10 - def __init__(self, profile=None, region=None):
11 - self.response_map = {}
12 - for name, value in inspect.getmembers(responses):
13 - if name.startswith('__'):
14 - continue
15 - if '_' in name:
16 - service_name, request_name = name.split('_', 1)
17 - if service_name not in self.response_map:
18 - self.response_map[service_name] = {}
19 - self.response_map[service_name][request_name] = value
20 -
21 - def create_client(self, client_name):
22 - client = None
23 - if client_name in self.response_map:
24 - client = mock.Mock()
25 - for request in self.response_map[client_name]:
26 - response = self.response_map[client_name][request]
27 - setattr(client, request, mock.Mock(side_effect=response))
28 - return client
29 -
30 -
31 -def get_aws(context):
32 - return MockAWS()
This diff is collapsed. Click to expand it.
1 +{
2 + "status_code": 200,
3 + "data": {
4 + "ResponseMetadata": {
5 + "HTTPStatusCode": 200,
6 + "RequestId": "1276680a-a219-11e5-8386-d3391e1d709e"
7 + }
8 + }
9 +}
...\ No newline at end of file ...\ No newline at end of file
1 +{
2 + "status_code": 200,
3 + "data": {
4 + "Policy": {
5 + "PolicyName": "kappa-simple_dev",
6 + "CreateDate": {
7 + "hour": 4,
8 + "__class__": "datetime",
9 + "month": 12,
10 + "second": 46,
11 + "microsecond": 302000,
12 + "year": 2015,
13 + "day": 14,
14 + "minute": 13
15 + },
16 + "AttachmentCount": 0,
17 + "IsAttachable": true,
18 + "PolicyId": "ANPAJ6USPUIU5QKQ7DWMG",
19 + "DefaultVersionId": "v1",
20 + "Path": "/kappa/",
21 + "Arn": "arn:aws:iam::123456789012:policy/kappa/kappa-simple_dev",
22 + "UpdateDate": {
23 + "hour": 4,
24 + "__class__": "datetime",
25 + "month": 12,
26 + "second": 46,
27 + "microsecond": 302000,
28 + "year": 2015,
29 + "day": 14,
30 + "minute": 13
31 + }
32 + },
33 + "ResponseMetadata": {
34 + "HTTPStatusCode": 200,
35 + "RequestId": "11cdf3d8-a219-11e5-a392-d5ea3c3fc695"
36 + }
37 + }
38 +}
1 +{
2 + "status_code": 200,
3 + "data": {
4 + "Role": {
5 + "AssumeRolePolicyDocument": "%7B%0A%20%20%20%20%22Version%22%20%3A%20%222012-10-17%22%2C%0A%20%20%20%20%22Statement%22%3A%20%5B%20%7B%0A%20%20%20%20%20%20%20%20%22Effect%22%3A%20%22Allow%22%2C%0A%20%20%20%20%20%20%20%20%22Principal%22%3A%20%7B%0A%20%20%20%20%20%20%20%20%20%20%20%20%22Service%22%3A%20%5B%20%22lambda.amazonaws.com%22%20%5D%0A%20%20%20%20%20%20%20%20%7D%2C%0A%20%20%20%20%20%20%20%20%22Action%22%3A%20%5B%20%22sts%3AAssumeRole%22%20%5D%0A%20%20%20%20%7D%20%5D%0A%7D",
6 + "RoleId": "AROAICWPJDQLUTEOHRQZO",
7 + "CreateDate": {
8 + "hour": 4,
9 + "__class__": "datetime",
10 + "month": 12,
11 + "second": 46,
12 + "microsecond": 988000,
13 + "year": 2015,
14 + "day": 14,
15 + "minute": 13
16 + },
17 + "RoleName": "kappa-simple_dev",
18 + "Path": "/kappa/",
19 + "Arn": "arn:aws:iam::123456789012:role/kappa/kappa-simple_dev"
20 + },
21 + "ResponseMetadata": {
22 + "HTTPStatusCode": 200,
23 + "RequestId": "123d5777-a219-11e5-8386-d3391e1d709e"
24 + }
25 + }
26 +}
1 +{
2 + "status_code": 200,
3 + "data": {
4 + "Role": {
5 + "AssumeRolePolicyDocument": "%7B%22Version%22%3A%222012-10-17%22%2C%22Statement%22%3A%5B%7B%22Effect%22%3A%22Allow%22%2C%22Principal%22%3A%7B%22Service%22%3A%22lambda.amazonaws.com%22%7D%2C%22Action%22%3A%22sts%3AAssumeRole%22%7D%5D%7D",
6 + "RoleId": "AROAICWPJDQLUTEOHRQZO",
7 + "CreateDate": {
8 + "hour": 4,
9 + "__class__": "datetime",
10 + "month": 12,
11 + "second": 46,
12 + "microsecond": 0,
13 + "year": 2015,
14 + "day": 14,
15 + "minute": 13
16 + },
17 + "RoleName": "kappa-simple_dev",
18 + "Path": "/kappa/",
19 + "Arn": "arn:aws:iam::123456789012:role/kappa/kappa-simple_dev"
20 + },
21 + "ResponseMetadata": {
22 + "HTTPStatusCode": 200,
23 + "RequestId": "12dca49a-a219-11e5-9912-d70327f9be2c"
24 + }
25 + }
26 +}
1 +{
2 + "status_code": 200,
3 + "data": {
4 + "Role": {
5 + "AssumeRolePolicyDocument": "%7B%22Version%22%3A%222012-10-17%22%2C%22Statement%22%3A%5B%7B%22Effect%22%3A%22Allow%22%2C%22Principal%22%3A%7B%22Service%22%3A%22lambda.amazonaws.com%22%7D%2C%22Action%22%3A%22sts%3AAssumeRole%22%7D%5D%7D",
6 + "RoleId": "AROAICWPJDQLUTEOHRQZO",
7 + "CreateDate": {
8 + "hour": 4,
9 + "__class__": "datetime",
10 + "month": 12,
11 + "second": 46,
12 + "microsecond": 0,
13 + "year": 2015,
14 + "day": 14,
15 + "minute": 13
16 + },
17 + "RoleName": "kappa-simple_dev",
18 + "Path": "/kappa/",
19 + "Arn": "arn:aws:iam::123456789012:role/kappa/kappa-simple_dev"
20 + },
21 + "ResponseMetadata": {
22 + "HTTPStatusCode": 200,
23 + "RequestId": "1bd39022-a219-11e5-bb1e-6b18bfdcba09"
24 + }
25 + }
26 +}
This diff is collapsed. Click to expand it.
1 +{
2 + "status_code": 200,
3 + "data": {
4 + "ResponseMetadata": {
5 + "HTTPStatusCode": 200,
6 + "RequestId": "1264405a-a219-11e5-ad54-c769aa17a0a1"
7 + },
8 + "IsTruncated": false,
9 + "Policies": [
10 + {
11 + "PolicyName": "kappa-simple_dev",
12 + "CreateDate": {
13 + "hour": 4,
14 + "__class__": "datetime",
15 + "month": 12,
16 + "second": 46,
17 + "microsecond": 0,
18 + "year": 2015,
19 + "day": 14,
20 + "minute": 13
21 + },
22 + "AttachmentCount": 0,
23 + "IsAttachable": true,
24 + "PolicyId": "ANPAJ6USPUIU5QKQ7DWMG",
25 + "DefaultVersionId": "v1",
26 + "Path": "/kappa/",
27 + "Arn": "arn:aws:iam::123456789012:policy/kappa/kappa-simple_dev",
28 + "UpdateDate": {
29 + "hour": 4,
30 + "__class__": "datetime",
31 + "month": 12,
32 + "second": 46,
33 + "microsecond": 0,
34 + "year": 2015,
35 + "day": 14,
36 + "minute": 13
37 + }
38 + },
39 + {
40 + "PolicyName": "FooBar15",
41 + "CreateDate": {
42 + "hour": 19,
43 + "__class__": "datetime",
44 + "month": 12,
45 + "second": 15,
46 + "microsecond": 0,
47 + "year": 2015,
48 + "day": 10,
49 + "minute": 22
50 + },
51 + "AttachmentCount": 1,
52 + "IsAttachable": true,
53 + "PolicyId": "ANPAJ3MM445EFVC6OWPIO",
54 + "DefaultVersionId": "v1",
55 + "Path": "/kappa/",
56 + "Arn": "arn:aws:iam::123456789012:policy/kappa/FooBar15",
57 + "UpdateDate": {
58 + "hour": 19,
59 + "__class__": "datetime",
60 + "month": 12,
61 + "second": 15,
62 + "microsecond": 0,
63 + "year": 2015,
64 + "day": 10,
65 + "minute": 22
66 + }
67 + }
68 + ]
69 + }
70 +}
1 +{
2 + "status_code": 200,
3 + "data": {
4 + "ResponseMetadata": {
5 + "HTTPStatusCode": 200,
6 + "RequestId": "1b40516e-a219-11e5-bb1e-6b18bfdcba09"
7 + },
8 + "IsTruncated": false,
9 + "Policies": [
10 + {
11 + "PolicyName": "kappa-simple_dev",
12 + "CreateDate": {
13 + "hour": 4,
14 + "__class__": "datetime",
15 + "month": 12,
16 + "second": 46,
17 + "microsecond": 0,
18 + "year": 2015,
19 + "day": 14,
20 + "minute": 13
21 + },
22 + "AttachmentCount": 1,
23 + "IsAttachable": true,
24 + "PolicyId": "ANPAJ6USPUIU5QKQ7DWMG",
25 + "DefaultVersionId": "v1",
26 + "Path": "/kappa/",
27 + "Arn": "arn:aws:iam::123456789012:policy/kappa/kappa-simple_dev",
28 + "UpdateDate": {
29 + "hour": 4,
30 + "__class__": "datetime",
31 + "month": 12,
32 + "second": 46,
33 + "microsecond": 0,
34 + "year": 2015,
35 + "day": 14,
36 + "minute": 13
37 + }
38 + },
39 + {
40 + "PolicyName": "FooBar15",
41 + "CreateDate": {
42 + "hour": 19,
43 + "__class__": "datetime",
44 + "month": 12,
45 + "second": 15,
46 + "microsecond": 0,
47 + "year": 2015,
48 + "day": 10,
49 + "minute": 22
50 + },
51 + "AttachmentCount": 1,
52 + "IsAttachable": true,
53 + "PolicyId": "ANPAJ3MM445EFVC6OWPIO",
54 + "DefaultVersionId": "v1",
55 + "Path": "/kappa/",
56 + "Arn": "arn:aws:iam::123456789012:policy/kappa/FooBar15",
57 + "UpdateDate": {
58 + "hour": 19,
59 + "__class__": "datetime",
60 + "month": 12,
61 + "second": 15,
62 + "microsecond": 0,
63 + "year": 2015,
64 + "day": 10,
65 + "minute": 22
66 + }
67 + }
68 + ]
69 + }
70 +}
1 +{
2 + "status_code": 200,
3 + "data": {
4 + "ResponseMetadata": {
5 + "HTTPStatusCode": 200,
6 + "RequestId": "120be6dd-a219-11e5-ad54-c769aa17a0a1"
7 + },
8 + "IsTruncated": false,
9 + "Roles": [
10 + {
11 + "AssumeRolePolicyDocument": "%7B%22Version%22%3A%222012-10-17%22%2C%22Statement%22%3A%5B%7B%22Sid%22%3A%22%22%2C%22Effect%22%3A%22Allow%22%2C%22Principal%22%3A%7B%22Service%22%3A%22lambda.amazonaws.com%22%7D%2C%22Action%22%3A%22sts%3AAssumeRole%22%7D%5D%7D",
12 + "RoleId": "AROAJC6I44KNC2N4C6DUO",
13 + "CreateDate": {
14 + "hour": 13,
15 + "__class__": "datetime",
16 + "month": 8,
17 + "second": 29,
18 + "microsecond": 0,
19 + "year": 2015,
20 + "day": 12,
21 + "minute": 10
22 + },
23 + "RoleName": "FooBar1",
24 + "Path": "/kappa/",
25 + "Arn": "arn:aws:iam::123456789012:role/kappa/FooBar1"
26 + },
27 + {
28 + "AssumeRolePolicyDocument": "%7B%22Version%22%3A%222012-10-17%22%2C%22Statement%22%3A%5B%7B%22Sid%22%3A%22%22%2C%22Effect%22%3A%22Allow%22%2C%22Principal%22%3A%7B%22AWS%22%3A%22arn%3Aaws%3Aiam%3A%3A433502988969%3Aroot%22%7D%2C%22Action%22%3A%22sts%3AAssumeRole%22%7D%5D%7D",
29 + "RoleId": "AROAIPICAZWCWSIUY6WBC",
30 + "CreateDate": {
31 + "hour": 6,
32 + "__class__": "datetime",
33 + "month": 5,
34 + "second": 3,
35 + "microsecond": 0,
36 + "year": 2015,
37 + "day": 5,
38 + "minute": 31
39 + },
40 + "RoleName": "FooBar2",
41 + "Path": "/",
42 + "Arn": "arn:aws:iam::123456789012:role/FooBar2"
43 + }
44 + ]
45 + }
46 +}
1 +{
2 + "status_code": 200,
3 + "data": {
4 + "ResponseMetadata": {
5 + "HTTPStatusCode": 200,
6 + "RequestId": "1b6a1fab-a219-11e5-bb1e-6b18bfdcba09"
7 + },
8 + "IsTruncated": false,
9 + "Roles": [
10 + {
11 + "AssumeRolePolicyDocument": "%7B%22Version%22%3A%222012-10-17%22%2C%22Statement%22%3A%5B%7B%22Effect%22%3A%22Allow%22%2C%22Principal%22%3A%7B%22Service%22%3A%22lambda.amazonaws.com%22%7D%2C%22Action%22%3A%22sts%3AAssumeRole%22%7D%5D%7D",
12 + "RoleId": "AROAICWPJDQLUTEOHRQZO",
13 + "CreateDate": {
14 + "hour": 4,
15 + "__class__": "datetime",
16 + "month": 12,
17 + "second": 46,
18 + "microsecond": 0,
19 + "year": 2015,
20 + "day": 14,
21 + "minute": 13
22 + },
23 + "RoleName": "kappa-simple_dev",
24 + "Path": "/kappa/",
25 + "Arn": "arn:aws:iam::123456789012:role/kappa/kappa-simple_dev"
26 + },
27 + {
28 + "AssumeRolePolicyDocument": "%7B%22Version%22%3A%222012-10-17%22%2C%22Statement%22%3A%5B%7B%22Sid%22%3A%22%22%2C%22Effect%22%3A%22Allow%22%2C%22Principal%22%3A%7B%22AWS%22%3A%22arn%3Aaws%3Aiam%3A%3A123456789012%3Aroot%22%7D%2C%22Action%22%3A%22sts%3AAssumeRole%22%2C%22Condition%22%3A%7B%22StringEquals%22%3A%7B%22sts%3AExternalId%22%3A%22c196gvft3%22%7D%7D%7D%5D%7D",
29 + "RoleId": "AROAJGQVUYMCJZYCM3MR4",
30 + "CreateDate": {
31 + "hour": 15,
32 + "__class__": "datetime",
33 + "month": 6,
34 + "second": 2,
35 + "microsecond": 0,
36 + "year": 2015,
37 + "day": 12,
38 + "minute": 53
39 + },
40 + "RoleName": "kate-test-policy-role",
41 + "Path": "/",
42 + "Arn": "arn:aws:iam::123456789012:role/kate-test-policy-role"
43 + }
44 + ]
45 + }
46 +}
1 +{
2 + "status_code": 201,
3 + "data": {
4 + "AliasArn": "arn:aws:lambda:us-west-2:123456789012:function:kappa-simple:dev",
5 + "FunctionVersion": "12",
6 + "Name": "dev",
7 + "ResponseMetadata": {
8 + "HTTPStatusCode": 201,
9 + "RequestId": "1872d8ff-a219-11e5-9579-ab6c3f6de03e"
10 + },
11 + "Description": "For stage dev"
12 + }
13 +}
1 +{
2 + "status_code": 400,
3 + "data": {
4 + "ResponseMetadata": {
5 + "HTTPStatusCode": 400,
6 + "RequestId": "12ed468e-a219-11e5-89fa-9b1d3e60e617"
7 + },
8 + "Error": {
9 + "Message": "The role defined for the task cannot be assumed by Lambda.",
10 + "Code": "InvalidParameterValueException"
11 + }
12 + }
13 +}
...\ No newline at end of file ...\ No newline at end of file
1 +{
2 + "status_code": 400,
3 + "data": {
4 + "ResponseMetadata": {
5 + "HTTPStatusCode": 400,
6 + "RequestId": "14375279-a219-11e5-b9da-196ca0eccf24"
7 + },
8 + "Error": {
9 + "Message": "The role defined for the task cannot be assumed by Lambda.",
10 + "Code": "InvalidParameterValueException"
11 + }
12 + }
13 +}
...\ No newline at end of file ...\ No newline at end of file
1 +{
2 + "status_code": 400,
3 + "data": {
4 + "ResponseMetadata": {
5 + "HTTPStatusCode": 400,
6 + "RequestId": "158815a1-a219-11e5-b354-111009c28f60"
7 + },
8 + "Error": {
9 + "Message": "The role defined for the task cannot be assumed by Lambda.",
10 + "Code": "InvalidParameterValueException"
11 + }
12 + }
13 +}
...\ No newline at end of file ...\ No newline at end of file
1 +{
2 + "status_code": 400,
3 + "data": {
4 + "ResponseMetadata": {
5 + "HTTPStatusCode": 400,
6 + "RequestId": "16d88a59-a219-11e5-abfc-a3c6c8e4d88f"
7 + },
8 + "Error": {
9 + "Message": "The role defined for the task cannot be assumed by Lambda.",
10 + "Code": "InvalidParameterValueException"
11 + }
12 + }
13 +}
...\ No newline at end of file ...\ No newline at end of file
1 +{
2 + "status_code": 201,
3 + "data": {
4 + "CodeSha256": "JklpzNjuO6TLDiNe6nVYWeo1Imq6bF5uaMt2L0bqp5Y=",
5 + "FunctionName": "kappa-simple",
6 + "ResponseMetadata": {
7 + "HTTPStatusCode": 201,
8 + "RequestId": "1820256f-a219-11e5-acaa-ebe01320cf02"
9 + },
10 + "CodeSize": 948,
11 + "MemorySize": 256,
12 + "FunctionArn": "arn:aws:lambda:us-west-2:123456789012:function:kappa-simple",
13 + "Version": "12",
14 + "Role": "arn:aws:iam::123456789012:role/kappa/kappa-simple_dev",
15 + "Timeout": 3,
16 + "LastModified": "2015-12-14T04:13:56.737+0000",
17 + "Handler": "simple.handler",
18 + "Runtime": "python2.7",
19 + "Description": "A very simple Kappa example"
20 + }
21 +}
1 +{
2 + "status_code": 404,
3 + "data": {
4 + "ResponseMetadata": {
5 + "HTTPStatusCode": 404,
6 + "RequestId": "12caa276-a219-11e5-bc80-bb0600635952"
7 + },
8 + "Error": {
9 + "Message": "Function not found: arn:aws:lambda:us-west-2:860421987956:function:kappa-simple",
10 + "Code": "ResourceNotFoundException"
11 + }
12 + }
13 +}
...\ No newline at end of file ...\ No newline at end of file
1 +{
2 + "status_code": 200,
3 + "data": {
4 + "Code": {
5 + "RepositoryType": "S3",
6 + "Location": "https://awslambda-us-west-2-tasks.s3-us-west-2.amazonaws.com/snapshots/123456789012/kappa-simple-99dba060-c458-48c6-ab7b-501063603e69?x-amz-security-token=AQoDYXdzECQa4AOvxYmkiVqa3ost0drsHs84f3tyUBYSVQUm%2BVvFZgAqx9JDt55l4N4T%2FwH8302pH0ICUZfCRRfc%2FuWtukJsT33XIsG6Xw0Br8w00y07RRpZYQLiJqTXi0i2EFZ6LMIRsGBgKV%2BdufXXu7P9yfzqBiFUrfUD6fYeRNLdv34aXUDto0G0gTj3ZDv9gqO9q7YEXbeu1NI62cIfuEGph2ptFj5V1E%2BijK0h9XEW0mkfuomQt6oeii%2FkkNNm5tEyUlpeX17z1sbX3NYoqJrap0QdoqXkak%2BFPvJQG7hm7eJ40b2ymve9L3gvIOiKNzmQrzay77uEkYDNLxK89QMlYRtRG6vTHppdZzTVIooTFVdA6NSSvYHnjryStLA3VUnDG%2FsL9xAiHH8l4kzq%2ByvatF%2Fg8wTNXOdFxt0VMVkJVbwG%2FUex7juyEcRAJUGNaHBZNLPJVUL%2BfAQljCwJAnjXxD%2FpjEtyLi9YbdfLGywkBKccoKh7AmjJXwzT8TusWNKmmW0XJL%2Fn81NE84Ni9iVB8JHxRbwaJXT2ou0ytwn%2BIIlRcmwXSIwA3xm%2FXynUTfOuXZ3UMGuBlHtt45uKGJvvp5d6RQicK5q5LXFQgGxj5gUqgty0jPhPE%2BN%2BF8WUwSk3eNwPiwMgwOS4swU%3D&AWSAccessKeyId=ASIAIHZZJVPM3RQS3QOQ&Expires=1450067042&Signature=QeC65kDb6N4CNRGn9IiQNBSpl4g%3D"
7 + },
8 + "Configuration": {
9 + "Version": "$LATEST",
10 + "CodeSha256": "JklpzNjuO6TLDiNe6nVYWeo1Imq6bF5uaMt2L0bqp5Y=",
11 + "FunctionName": "kappa-simple",
12 + "MemorySize": 256,
13 + "CodeSize": 948,
14 + "FunctionArn": "arn:aws:lambda:us-west-2:123456789012:function:kappa-simple",
15 + "Handler": "simple.handler",
16 + "Role": "arn:aws:iam::123456789012:role/kappa/kappa-simple_dev",
17 + "Timeout": 3,
18 + "LastModified": "2015-12-14T04:13:56.737+0000",
19 + "Runtime": "python2.7",
20 + "Description": "A very simple Kappa example"
21 + },
22 + "ResponseMetadata": {
23 + "HTTPStatusCode": 200,
24 + "RequestId": "1bc69855-a219-11e5-990d-c158fa575e6a"
25 + }
26 + }
27 +}
1 +{
2 + "status_code": 200,
3 + "data": {
4 + "ResponseMetadata": {
5 + "HTTPStatusCode": 200,
6 + "RequestId": "1860ff11-a219-11e5-b9da-196ca0eccf24"
7 + },
8 + "Versions": [
9 + {
10 + "Version": "$LATEST",
11 + "CodeSha256": "JklpzNjuO6TLDiNe6nVYWeo1Imq6bF5uaMt2L0bqp5Y=",
12 + "FunctionName": "kappa-simple",
13 + "MemorySize": 256,
14 + "CodeSize": 948,
15 + "FunctionArn": "arn:aws:lambda:us-west-2:123456789012:function:kappa-simple:$LATEST",
16 + "Handler": "simple.handler",
17 + "Role": "arn:aws:iam::123456789012:role/kappa/kappa-simple_dev",
18 + "Timeout": 3,
19 + "LastModified": "2015-12-14T04:13:56.737+0000",
20 + "Runtime": "python2.7",
21 + "Description": "A very simple Kappa example"
22 + },
23 + {
24 + "Version": "12",
25 + "CodeSha256": "JklpzNjuO6TLDiNe6nVYWeo1Imq6bF5uaMt2L0bqp5Y=",
26 + "FunctionName": "kappa-simple",
27 + "MemorySize": 256,
28 + "CodeSize": 948,
29 + "FunctionArn": "arn:aws:lambda:us-west-2:123456789012:function:kappa-simple:12",
30 + "Handler": "simple.handler",
31 + "Role": "arn:aws:iam::123456789012:role/kappa/kappa-simple_dev",
32 + "Timeout": 3,
33 + "LastModified": "2015-12-14T04:13:56.737+0000",
34 + "Runtime": "python2.7",
35 + "Description": "A very simple Kappa example"
36 + }
37 + ]
38 + }
39 +}
1 +# Copyright (c) 2015 Mitch Garnaat http://garnaat.org/
2 +#
3 +# Licensed under the Apache License, Version 2.0 (the "License"). You
4 +# may not use this file except in compliance with the License. A copy of
5 +# the License is located at
6 +#
7 +# http://aws.amazon.com/apache2.0/
8 +#
9 +# or in the "license" file accompanying this file. This file is
10 +# distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF
11 +# ANY KIND, either express or implied. See the License for the specific
12 +# language governing permissions and limitations under the License.
13 +
14 +import unittest
15 +import os
16 +import shutil
17 +
18 +import mock
19 +import placebo
20 +
21 +import kappa.context
22 +import kappa.awsclient
23 +
24 +
25 +class TestLog(unittest.TestCase):
26 +
27 + def setUp(self):
28 + self.environ = {}
29 + self.environ_patch = mock.patch('os.environ', self.environ)
30 + self.environ_patch.start()
31 + credential_path = os.path.join(os.path.dirname(__file__), 'cfg',
32 + 'aws_credentials')
33 + self.environ['AWS_SHARED_CREDENTIALS_FILE'] = credential_path
34 + self.prj_path = os.path.join(os.path.dirname(__file__), 'foobar')
35 + cache_file = os.path.join(self.prj_path, '.kappa')
36 + if os.path.exists(cache_file):
37 + shutil.rmtree(cache_file)
38 + self.data_path = os.path.join(os.path.dirname(__file__), 'responses')
39 + self.data_path = os.path.join(self.data_path, 'deploy')
40 + self.session = kappa.awsclient.create_session('foobar', 'us-west-2')
41 +
42 + def tearDown(self):
43 + pass
44 +
45 + def test_deploy(self):
46 + pill = placebo.attach(self.session, self.data_path)
47 + pill.playback()
48 + os.chdir(self.prj_path)
49 + cfg_filepath = os.path.join(self.prj_path, 'kappa.yml')
50 + cfg_fp = open(cfg_filepath)
51 + ctx = kappa.context.Context(cfg_fp, 'dev')
52 + ctx.deploy()
53 + ctx.deploy()
1 -# Copyright (c) 2014 Mitch Garnaat http://garnaat.org/
2 -#
3 -# Licensed under the Apache License, Version 2.0 (the "License"). You
4 -# may not use this file except in compliance with the License. A copy of
5 -# the License is located at
6 -#
7 -# http://aws.amazon.com/apache2.0/
8 -#
9 -# or in the "license" file accompanying this file. This file is
10 -# distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF
11 -# ANY KIND, either express or implied. See the License for the specific
12 -# language governing permissions and limitations under the License.
13 -
14 -import unittest
15 -
16 -import mock
17 -
18 -from kappa.log import Log
19 -from tests.unit.mock_aws import get_aws
20 -
21 -
22 -class TestLog(unittest.TestCase):
23 -
24 - def setUp(self):
25 - self.aws_patch = mock.patch('kappa.aws.get_aws', get_aws)
26 - self.mock_aws = self.aws_patch.start()
27 -
28 - def tearDown(self):
29 - self.aws_patch.stop()
30 -
31 - def test_streams(self):
32 - mock_context = mock.Mock()
33 - log = Log(mock_context, 'foo/bar')
34 - streams = log.streams()
35 - self.assertEqual(len(streams), 6)
36 -
37 - def test_tail(self):
38 - mock_context = mock.Mock()
39 - log = Log(mock_context, 'foo/bar')
40 - events = log.tail()
41 - self.assertEqual(len(events), 6)
42 - self.assertEqual(events[0]['ingestionTime'], 1420569036909)
43 - self.assertIn('RequestId: 23007242-95d2-11e4-a10e-7b2ab60a7770',
44 - events[-1]['message'])
1 -# Copyright (c) 2015 Mitch Garnaat http://garnaat.org/
2 -#
3 -# Licensed under the Apache License, Version 2.0 (the "License"). You
4 -# may not use this file except in compliance with the License. A copy of
5 -# the License is located at
6 -#
7 -# http://aws.amazon.com/apache2.0/
8 -#
9 -# or in the "license" file accompanying this file. This file is
10 -# distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF
11 -# ANY KIND, either express or implied. See the License for the specific
12 -# language governing permissions and limitations under the License.
13 -
14 -import unittest
15 -import os
16 -
17 -import mock
18 -
19 -from kappa.policy import Policy
20 -from tests.unit.mock_aws import get_aws
21 -
22 -Config1 = {
23 - 'name': 'FooPolicy',
24 - 'description': 'This is the Foo policy',
25 - 'document': 'FooPolicy.json'}
26 -
27 -Config2 = {
28 - 'name': 'BazPolicy',
29 - 'description': 'This is the Baz policy',
30 - 'document': 'BazPolicy.json'}
31 -
32 -
33 -def path(filename):
34 - return os.path.join(os.path.dirname(__file__), 'data', filename)
35 -
36 -
37 -class TestPolicy(unittest.TestCase):
38 -
39 - def setUp(self):
40 - self.aws_patch = mock.patch('kappa.aws.get_aws', get_aws)
41 - self.mock_aws = self.aws_patch.start()
42 - Config1['document'] = path(Config1['document'])
43 - Config2['document'] = path(Config2['document'])
44 -
45 - def tearDown(self):
46 - self.aws_patch.stop()
47 -
48 - def test_properties(self):
49 - mock_context = mock.Mock()
50 - policy = Policy(mock_context, Config1)
51 - self.assertEqual(policy.name, Config1['name'])
52 - self.assertEqual(policy.document, Config1['document'])
53 - self.assertEqual(policy.description, Config1['description'])
54 -
55 - def test_exists(self):
56 - mock_context = mock.Mock()
57 - policy = Policy(mock_context, Config1)
58 - self.assertTrue(policy.exists())
59 -
60 - def test_not_exists(self):
61 - mock_context = mock.Mock()
62 - policy = Policy(mock_context, Config2)
63 - self.assertFalse(policy.exists())
64 -
65 - def test_create(self):
66 - mock_context = mock.Mock()
67 - policy = Policy(mock_context, Config2)
68 - policy.create()
69 -
70 - def test_delete(self):
71 - mock_context = mock.Mock()
72 - policy = Policy(mock_context, Config1)
73 - policy.delete()
1 -# Copyright (c) 2015 Mitch Garnaat http://garnaat.org/
2 -#
3 -# Licensed under the Apache License, Version 2.0 (the "License"). You
4 -# may not use this file except in compliance with the License. A copy of
5 -# the License is located at
6 -#
7 -# http://aws.amazon.com/apache2.0/
8 -#
9 -# or in the "license" file accompanying this file. This file is
10 -# distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF
11 -# ANY KIND, either express or implied. See the License for the specific
12 -# language governing permissions and limitations under the License.
13 -
14 -import unittest
15 -
16 -import mock
17 -
18 -from kappa.role import Role
19 -from tests.unit.mock_aws import get_aws
20 -
21 -Config1 = {'name': 'FooRole'}
22 -
23 -Config2 = {'name': 'BazRole'}
24 -
25 -
26 -class TestRole(unittest.TestCase):
27 -
28 - def setUp(self):
29 - self.aws_patch = mock.patch('kappa.aws.get_aws', get_aws)
30 - self.mock_aws = self.aws_patch.start()
31 -
32 - def tearDown(self):
33 - self.aws_patch.stop()
34 -
35 - def test_properties(self):
36 - mock_context = mock.Mock()
37 - role = Role(mock_context, Config1)
38 - self.assertEqual(role.name, Config1['name'])
39 -
40 - def test_exists(self):
41 - mock_context = mock.Mock()
42 - role = Role(mock_context, Config1)
43 - self.assertTrue(role.exists())
44 -
45 - def test_not_exists(self):
46 - mock_context = mock.Mock()
47 - role = Role(mock_context, Config2)
48 - self.assertFalse(role.exists())
49 -
50 - def test_create(self):
51 - mock_context = mock.Mock()
52 - role = Role(mock_context, Config2)
53 - role.create()
54 -
55 - def test_delete(self):
56 - mock_context = mock.Mock()
57 - role = Role(mock_context, Config1)
58 - role.delete()