awspub 0.0.10__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- 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
|