awspub 0.0.10__py3-none-any.whl
Sign up to get free protection for your applications and to get access to all the features.
- awspub/__init__.py +3 -0
- awspub/api.py +165 -0
- awspub/cli/__init__.py +146 -0
- awspub/common.py +64 -0
- awspub/configmodels.py +216 -0
- awspub/context.py +108 -0
- awspub/exceptions.py +28 -0
- awspub/image.py +656 -0
- awspub/image_marketplace.py +120 -0
- awspub/s3.py +262 -0
- awspub/snapshot.py +241 -0
- awspub/sns.py +105 -0
- awspub/tests/__init__.py +0 -0
- awspub/tests/fixtures/config-invalid-s3-extra.yaml +12 -0
- awspub/tests/fixtures/config-minimal.yaml +12 -0
- awspub/tests/fixtures/config-valid-nonawspub.yaml +13 -0
- awspub/tests/fixtures/config1.vmdk +0 -0
- awspub/tests/fixtures/config1.yaml +171 -0
- awspub/tests/fixtures/config2-mapping.yaml +2 -0
- awspub/tests/fixtures/config2.yaml +48 -0
- awspub/tests/fixtures/config3-duplicate-keys.yaml +18 -0
- awspub/tests/test_api.py +89 -0
- awspub/tests/test_cli.py +0 -0
- awspub/tests/test_common.py +34 -0
- awspub/tests/test_context.py +88 -0
- awspub/tests/test_image.py +556 -0
- awspub/tests/test_image_marketplace.py +44 -0
- awspub/tests/test_s3.py +74 -0
- awspub/tests/test_snapshot.py +122 -0
- awspub/tests/test_sns.py +189 -0
- awspub-0.0.10.dist-info/LICENSE +675 -0
- awspub-0.0.10.dist-info/METADATA +46 -0
- awspub-0.0.10.dist-info/RECORD +35 -0
- awspub-0.0.10.dist-info/WHEEL +4 -0
- awspub-0.0.10.dist-info/entry_points.txt +3 -0
@@ -0,0 +1,122 @@
|
|
1
|
+
import pathlib
|
2
|
+
from unittest.mock import MagicMock
|
3
|
+
|
4
|
+
import pytest
|
5
|
+
|
6
|
+
from awspub import context, snapshot
|
7
|
+
|
8
|
+
curdir = pathlib.Path(__file__).parent.resolve()
|
9
|
+
|
10
|
+
|
11
|
+
def test_snapshot__get_none_exist():
|
12
|
+
"""
|
13
|
+
No snapshot exist - should return None
|
14
|
+
"""
|
15
|
+
ctx = context.Context(curdir / "fixtures/config1.yaml", None)
|
16
|
+
s = snapshot.Snapshot(ctx)
|
17
|
+
client_mock = MagicMock()
|
18
|
+
assert s._get(client_mock, "snapshot-name") is None
|
19
|
+
client_mock.describe_snapshots.assert_called_with(
|
20
|
+
Filters=[
|
21
|
+
{"Name": "tag:Name", "Values": ["snapshot-name"]},
|
22
|
+
{"Name": "status", "Values": ["pending", "completed"]},
|
23
|
+
],
|
24
|
+
OwnerIds=["self"],
|
25
|
+
)
|
26
|
+
|
27
|
+
|
28
|
+
def test_snapshot__get_one_exist():
|
29
|
+
"""
|
30
|
+
One snapshot exist with the same name - should return the snapshot id
|
31
|
+
"""
|
32
|
+
ctx = context.Context(curdir / "fixtures/config1.yaml", None)
|
33
|
+
s = snapshot.Snapshot(ctx)
|
34
|
+
client_mock = MagicMock()
|
35
|
+
client_mock.describe_snapshots = MagicMock(return_value={"Snapshots": [{"SnapshotId": "snap-1"}]})
|
36
|
+
assert s._get(client_mock, "snapshot-name") == "snap-1"
|
37
|
+
client_mock.describe_snapshots.assert_called_with(
|
38
|
+
Filters=[
|
39
|
+
{"Name": "tag:Name", "Values": ["snapshot-name"]},
|
40
|
+
{"Name": "status", "Values": ["pending", "completed"]},
|
41
|
+
],
|
42
|
+
OwnerIds=["self"],
|
43
|
+
)
|
44
|
+
|
45
|
+
|
46
|
+
def test_snapshot__get_multiple_exist():
|
47
|
+
"""
|
48
|
+
Multiple snapshots exist - _get() should raise an Exception
|
49
|
+
"""
|
50
|
+
ctx = context.Context(curdir / "fixtures/config1.yaml", None)
|
51
|
+
s = snapshot.Snapshot(ctx)
|
52
|
+
client_mock = MagicMock()
|
53
|
+
client_mock.describe_snapshots = MagicMock(
|
54
|
+
return_value={"Snapshots": [{"SnapshotId": "snap-1"}, {"SnapshotId": "snap-2"}]}
|
55
|
+
)
|
56
|
+
with pytest.raises(Exception):
|
57
|
+
s._get(client_mock, "snapshot-name")
|
58
|
+
client_mock.describe_snapshots.assert_called_with(
|
59
|
+
Filters=[
|
60
|
+
{"Name": "tag:Name", "Values": ["snapshot-name"]},
|
61
|
+
{"Name": "status", "Values": ["pending", "completed"]},
|
62
|
+
],
|
63
|
+
OwnerIds=["self"],
|
64
|
+
)
|
65
|
+
|
66
|
+
|
67
|
+
def test_snapshot__get_import_snapshot_task_completed():
|
68
|
+
"""
|
69
|
+
Test the Snapshot._get_import_snapshot_task() method
|
70
|
+
"""
|
71
|
+
ctx = context.Context(curdir / "fixtures/config1.yaml", None)
|
72
|
+
s = snapshot.Snapshot(ctx)
|
73
|
+
client_mock = MagicMock()
|
74
|
+
client_mock.describe_import_snapshot_tasks = MagicMock(
|
75
|
+
return_value={
|
76
|
+
"ImportSnapshotTasks": [
|
77
|
+
{
|
78
|
+
"ImportTaskId": "import-snap-08b79d7b5d382d56b",
|
79
|
+
"SnapshotTaskDetail": {
|
80
|
+
"SnapshotId": "snap-0e0f3407a1b541c40",
|
81
|
+
"Status": "completed",
|
82
|
+
},
|
83
|
+
"Tags": [
|
84
|
+
{"Key": "Name", "Value": "021abb3f2338b5e57b5d870816565429659bc70769d71c486234ad60fe6aec67"},
|
85
|
+
],
|
86
|
+
}
|
87
|
+
],
|
88
|
+
}
|
89
|
+
)
|
90
|
+
assert (
|
91
|
+
s._get_import_snapshot_task(client_mock, "021abb3f2338b5e57b5d870816565429659bc70769d71c486234ad60fe6aec67")
|
92
|
+
is None
|
93
|
+
)
|
94
|
+
|
95
|
+
|
96
|
+
def test_snapshot__get_import_snapshot_task_active():
|
97
|
+
"""
|
98
|
+
Test the Snapshot._get_import_snapshot_task() method
|
99
|
+
"""
|
100
|
+
ctx = context.Context(curdir / "fixtures/config1.yaml", None)
|
101
|
+
s = snapshot.Snapshot(ctx)
|
102
|
+
client_mock = MagicMock()
|
103
|
+
client_mock.describe_import_snapshot_tasks = MagicMock(
|
104
|
+
return_value={
|
105
|
+
"ImportSnapshotTasks": [
|
106
|
+
{
|
107
|
+
"ImportTaskId": "import-snap-08b79d7b5d382d56b",
|
108
|
+
"SnapshotTaskDetail": {
|
109
|
+
"SnapshotId": "snap-0e0f3407a1b541c40",
|
110
|
+
"Status": "active",
|
111
|
+
},
|
112
|
+
"Tags": [
|
113
|
+
{"Key": "Name", "Value": "021abb3f2338b5e57b5d870816565429659bc70769d71c486234ad60fe6aec67"},
|
114
|
+
],
|
115
|
+
}
|
116
|
+
],
|
117
|
+
}
|
118
|
+
)
|
119
|
+
assert (
|
120
|
+
s._get_import_snapshot_task(client_mock, "021abb3f2338b5e57b5d870816565429659bc70769d71c486234ad60fe6aec67")
|
121
|
+
== "import-snap-08b79d7b5d382d56b"
|
122
|
+
)
|
awspub/tests/test_sns.py
ADDED
@@ -0,0 +1,189 @@
|
|
1
|
+
import pathlib
|
2
|
+
from unittest.mock import patch
|
3
|
+
|
4
|
+
import botocore.exceptions
|
5
|
+
import pytest
|
6
|
+
|
7
|
+
from awspub import context, exceptions, sns
|
8
|
+
|
9
|
+
curdir = pathlib.Path(__file__).parent.resolve()
|
10
|
+
|
11
|
+
|
12
|
+
@pytest.mark.parametrize(
|
13
|
+
"imagename,called_sns_publish, publish_call_count",
|
14
|
+
[
|
15
|
+
("test-image-10", True, 1),
|
16
|
+
("test-image-11", True, 2),
|
17
|
+
("test-image-12", True, 2),
|
18
|
+
],
|
19
|
+
)
|
20
|
+
def test_sns_publish(imagename, called_sns_publish, publish_call_count):
|
21
|
+
"""
|
22
|
+
Test the send_notification logic
|
23
|
+
"""
|
24
|
+
with patch("boto3.client") as bclient_mock:
|
25
|
+
instance = bclient_mock.return_value
|
26
|
+
ctx = context.Context(curdir / "fixtures/config1.yaml", None)
|
27
|
+
instance.describe_regions.return_value = {
|
28
|
+
"Regions": [{"RegionName": "eu-central-1"}, {"RegionName": "us-east-1"}]
|
29
|
+
}
|
30
|
+
instance.list_buckets.return_value = {"Buckets": [{"Name": "bucket1"}]}
|
31
|
+
|
32
|
+
sns.SNSNotification(ctx, imagename).publish()
|
33
|
+
assert instance.publish.called == called_sns_publish
|
34
|
+
assert instance.publish.call_count == publish_call_count
|
35
|
+
|
36
|
+
|
37
|
+
@pytest.mark.parametrize(
|
38
|
+
"imagename",
|
39
|
+
[
|
40
|
+
("test-image-10"),
|
41
|
+
("test-image-11"),
|
42
|
+
("test-image-12"),
|
43
|
+
],
|
44
|
+
)
|
45
|
+
def test_sns_publish_fail_with_invalid_topic(imagename):
|
46
|
+
"""
|
47
|
+
Test the send_notification logic
|
48
|
+
"""
|
49
|
+
with patch("boto3.client") as bclient_mock:
|
50
|
+
instance = bclient_mock.return_value
|
51
|
+
ctx = context.Context(curdir / "fixtures/config1.yaml", None)
|
52
|
+
instance.describe_regions.return_value = {
|
53
|
+
"Regions": [{"RegionName": "eu-central-1"}, {"RegionName": "us-east-1"}]
|
54
|
+
}
|
55
|
+
instance.list_buckets.return_value = {"Buckets": [{"Name": "bucket1"}]}
|
56
|
+
|
57
|
+
# topic1 is invalid topic
|
58
|
+
def side_effect(*args, **kwargs):
|
59
|
+
topic_arn = kwargs.get("TopicArn")
|
60
|
+
if "topic1" in topic_arn and "us-east-1" in topic_arn:
|
61
|
+
error_reponse = {
|
62
|
+
"Error": {
|
63
|
+
"Code": "NotFoundException",
|
64
|
+
"Message": "An error occurred (NotFound) when calling the Publish operation: "
|
65
|
+
"Topic does not exist.",
|
66
|
+
}
|
67
|
+
}
|
68
|
+
raise botocore.exceptions.ClientError(error_reponse, "")
|
69
|
+
|
70
|
+
instance.publish.side_effect = side_effect
|
71
|
+
|
72
|
+
with pytest.raises(exceptions.AWSNotificationException):
|
73
|
+
sns.SNSNotification(ctx, imagename).publish()
|
74
|
+
|
75
|
+
|
76
|
+
@pytest.mark.parametrize(
|
77
|
+
"imagename",
|
78
|
+
[
|
79
|
+
("test-image-10"),
|
80
|
+
("test-image-11"),
|
81
|
+
("test-image-12"),
|
82
|
+
],
|
83
|
+
)
|
84
|
+
def test_sns_publish_fail_with_unauthorized_user(imagename):
|
85
|
+
"""
|
86
|
+
Test the send_notification logic
|
87
|
+
"""
|
88
|
+
with patch("boto3.client") as bclient_mock:
|
89
|
+
instance = bclient_mock.return_value
|
90
|
+
ctx = context.Context(curdir / "fixtures/config1.yaml", None)
|
91
|
+
instance.describe_regions.return_value = {
|
92
|
+
"Regions": [{"RegionName": "eu-central-1"}, {"RegionName": "us-east-1"}]
|
93
|
+
}
|
94
|
+
instance.list_buckets.return_value = {"Buckets": [{"Name": "bucket1"}]}
|
95
|
+
|
96
|
+
error_reponse = {
|
97
|
+
"Error": {
|
98
|
+
"Code": "AuthorizationError",
|
99
|
+
"Message": "User are not authorized perform SNS Notification service",
|
100
|
+
}
|
101
|
+
}
|
102
|
+
instance.publish.side_effect = botocore.exceptions.ClientError(error_reponse, "")
|
103
|
+
|
104
|
+
with pytest.raises(exceptions.AWSAuthorizationException):
|
105
|
+
sns.SNSNotification(ctx, imagename).publish()
|
106
|
+
|
107
|
+
|
108
|
+
@pytest.mark.parametrize(
|
109
|
+
"imagename, partition, regions_in_partition, expected",
|
110
|
+
[
|
111
|
+
(
|
112
|
+
"test-image-10",
|
113
|
+
"aws-cn",
|
114
|
+
["cn-north1", "cn-northwest-1"],
|
115
|
+
[],
|
116
|
+
),
|
117
|
+
(
|
118
|
+
"test-image-11",
|
119
|
+
"aws",
|
120
|
+
["us-east-1", "eu-central-1"],
|
121
|
+
[
|
122
|
+
"arn:aws:sns:us-east-1:1234:topic1",
|
123
|
+
"arn:aws:sns:eu-central-1:1234:topic2",
|
124
|
+
],
|
125
|
+
),
|
126
|
+
(
|
127
|
+
"test-image-12",
|
128
|
+
"aws",
|
129
|
+
["us-east-1", "eu-central-1"],
|
130
|
+
[
|
131
|
+
"arn:aws:sns:us-east-1:1234:topic1",
|
132
|
+
"arn:aws:sns:eu-central-1:1234:topic1",
|
133
|
+
],
|
134
|
+
),
|
135
|
+
],
|
136
|
+
)
|
137
|
+
def test_sns__get_topic_arn(imagename, partition, regions_in_partition, expected):
|
138
|
+
"""
|
139
|
+
Test the send_notification logic
|
140
|
+
"""
|
141
|
+
with patch("boto3.client") as bclient_mock:
|
142
|
+
instance = bclient_mock.return_value
|
143
|
+
ctx = context.Context(curdir / "fixtures/config1.yaml", None)
|
144
|
+
sns_conf = ctx.conf["images"][imagename]["sns"]
|
145
|
+
instance.describe_regions.return_value = {"Regions": [{"RegionName": r} for r in regions_in_partition]}
|
146
|
+
instance.list_buckets.return_value = {"Buckets": [{"Name": "bucket1"}]}
|
147
|
+
|
148
|
+
instance.get_caller_identity.return_value = {"Account": "1234", "Arn": f"arn:{partition}:iam::1234:user/test"}
|
149
|
+
|
150
|
+
topic_arns = []
|
151
|
+
for topic in sns_conf:
|
152
|
+
for topic_name, topic_conf in topic.items():
|
153
|
+
sns_regions = sns.SNSNotification(ctx, imagename)._sns_regions(topic_conf)
|
154
|
+
for region in sns_regions:
|
155
|
+
res_arn = sns.SNSNotification(ctx, imagename)._get_topic_arn(topic_name, region)
|
156
|
+
topic_arns.append(res_arn)
|
157
|
+
|
158
|
+
assert topic_arns == expected
|
159
|
+
|
160
|
+
|
161
|
+
@pytest.mark.parametrize(
|
162
|
+
"imagename,regions_in_partition,regions_expected",
|
163
|
+
[
|
164
|
+
("test-image-10", ["us-east-1", "eu-west-1"], {"topic1": ["us-east-1"]}),
|
165
|
+
(
|
166
|
+
"test-image-11",
|
167
|
+
["us-east-1", "eu-west-1"],
|
168
|
+
{"topic1": ["us-east-1"], "topic2": []},
|
169
|
+
),
|
170
|
+
("test-image-12", ["eu-northwest-1", "ap-southeast-1"], {"topic1": ["eu-northwest-1", "ap-southeast-1"]}),
|
171
|
+
],
|
172
|
+
)
|
173
|
+
def test_sns_regions(imagename, regions_in_partition, regions_expected):
|
174
|
+
"""
|
175
|
+
Test the regions for a given image
|
176
|
+
"""
|
177
|
+
with patch("boto3.client") as bclient_mock:
|
178
|
+
instance = bclient_mock.return_value
|
179
|
+
instance.describe_regions.return_value = {"Regions": [{"RegionName": r} for r in regions_in_partition]}
|
180
|
+
ctx = context.Context(curdir / "fixtures/config1.yaml", None)
|
181
|
+
sns_conf = ctx.conf["images"][imagename]["sns"]
|
182
|
+
instance.list_buckets.return_value = {"Buckets": [{"Name": "bucket1"}]}
|
183
|
+
|
184
|
+
sns_regions = {}
|
185
|
+
for topic in sns_conf:
|
186
|
+
for topic_name, topic_conf in topic.items():
|
187
|
+
sns_regions[topic_name] = sns.SNSNotification(ctx, imagename)._sns_regions(topic_conf)
|
188
|
+
|
189
|
+
assert sns_regions == regions_expected
|