awspub 0.0.7__py3-none-any.whl → 0.0.8__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/configmodels.py +32 -0
- awspub/exceptions.py +8 -0
- awspub/image.py +18 -0
- awspub/sns.py +88 -0
- awspub/tests/fixtures/config1.yaml +29 -0
- awspub/tests/test_api.py +2 -0
- awspub/tests/test_image.py +24 -6
- awspub/tests/test_sns.py +140 -0
- {awspub-0.0.7.dist-info → awspub-0.0.8.dist-info}/METADATA +3 -19
- {awspub-0.0.7.dist-info → awspub-0.0.8.dist-info}/RECORD +13 -11
- {awspub-0.0.7.dist-info → awspub-0.0.8.dist-info}/WHEEL +1 -1
- {awspub-0.0.7.dist-info → awspub-0.0.8.dist-info}/LICENSE +0 -0
- {awspub-0.0.7.dist-info → awspub-0.0.8.dist-info}/entry_points.txt +0 -0
awspub/configmodels.py
CHANGED
@@ -1,4 +1,5 @@
|
|
1
1
|
import pathlib
|
2
|
+
from enum import Enum
|
2
3
|
from typing import Dict, List, Literal, Optional
|
3
4
|
|
4
5
|
from pydantic import BaseModel, ConfigDict, Field, field_validator
|
@@ -94,6 +95,34 @@ class ConfigImageSSMParameterModel(BaseModel):
|
|
94
95
|
)
|
95
96
|
|
96
97
|
|
98
|
+
class SNSNotificationProtocol(str, Enum):
|
99
|
+
DEFAULT = "default"
|
100
|
+
EMAIL = "email"
|
101
|
+
|
102
|
+
|
103
|
+
class ConfigImageSNSNotificationModel(BaseModel):
|
104
|
+
"""
|
105
|
+
Image/AMI SNS Notification specific configuration to notify subscribers about new images availability
|
106
|
+
"""
|
107
|
+
|
108
|
+
model_config = ConfigDict(extra="forbid")
|
109
|
+
|
110
|
+
subject: str = Field(description="The subject of SNS Notification", min_length=1, max_length=99)
|
111
|
+
message: Dict[SNSNotificationProtocol, str] = Field(
|
112
|
+
description="The body of the message to be sent to subscribers.",
|
113
|
+
default={SNSNotificationProtocol.DEFAULT: ""},
|
114
|
+
)
|
115
|
+
|
116
|
+
@field_validator("message")
|
117
|
+
def check_message(cls, value):
|
118
|
+
# Check message protocols have default key
|
119
|
+
# Message should contain at least a top-level JSON key of “default”
|
120
|
+
# with a value that is a string
|
121
|
+
if SNSNotificationProtocol.DEFAULT not in value:
|
122
|
+
raise ValueError(f"{SNSNotificationProtocol.DEFAULT.value} key is required to send SNS notification")
|
123
|
+
return value
|
124
|
+
|
125
|
+
|
97
126
|
class ConfigImageModel(BaseModel):
|
98
127
|
"""
|
99
128
|
Image/AMI configuration.
|
@@ -148,6 +177,9 @@ class ConfigImageModel(BaseModel):
|
|
148
177
|
)
|
149
178
|
groups: Optional[List[str]] = Field(description="Optional list of groups this image is part of", default=[])
|
150
179
|
tags: Optional[Dict[str, str]] = Field(description="Optional Tags to apply to this image only", default={})
|
180
|
+
sns: Optional[List[Dict[str, ConfigImageSNSNotificationModel]]] = Field(
|
181
|
+
description="Optional list of SNS Notification related configuration", default=None
|
182
|
+
)
|
151
183
|
|
152
184
|
@field_validator("share")
|
153
185
|
@classmethod
|
awspub/exceptions.py
CHANGED
@@ -14,3 +14,11 @@ class BucketDoesNotExistException(Exception):
|
|
14
14
|
def __init__(self, bucket_name: str, *args, **kwargs):
|
15
15
|
msg = f"The bucket named '{bucket_name}' does not exist. You will need to create the bucket before proceeding."
|
16
16
|
super().__init__(msg, *args, **kwargs)
|
17
|
+
|
18
|
+
|
19
|
+
class AWSNotificationException(Exception):
|
20
|
+
pass
|
21
|
+
|
22
|
+
|
23
|
+
class AWSAuthorizationException(Exception):
|
24
|
+
pass
|
awspub/image.py
CHANGED
@@ -14,6 +14,7 @@ from awspub.context import Context
|
|
14
14
|
from awspub.image_marketplace import ImageMarketplace
|
15
15
|
from awspub.s3 import S3
|
16
16
|
from awspub.snapshot import Snapshot
|
17
|
+
from awspub.sns import SNSNotification
|
17
18
|
|
18
19
|
logger = logging.getLogger(__name__)
|
19
20
|
|
@@ -353,6 +354,19 @@ class Image:
|
|
353
354
|
else:
|
354
355
|
logger.error(f"image {self.image_name} not available in region {region}. can not make public")
|
355
356
|
|
357
|
+
def _sns_publish(self) -> None:
|
358
|
+
"""
|
359
|
+
Publish SNS notifiations about newly available images to subscribers
|
360
|
+
"""
|
361
|
+
for region in self.image_regions:
|
362
|
+
ec2client_region: EC2Client = boto3.client("ec2", region_name=region)
|
363
|
+
image_info: Optional[_ImageInfo] = self._get(ec2client_region)
|
364
|
+
|
365
|
+
if not image_info:
|
366
|
+
logger.error(f"can not send SNS notification for {self.image_name} because no image found in {region}")
|
367
|
+
return
|
368
|
+
SNSNotification(self._ctx, self.image_name, region).publish()
|
369
|
+
|
356
370
|
def cleanup(self) -> None:
|
357
371
|
"""
|
358
372
|
Cleanup/delete the temporary images
|
@@ -556,6 +570,10 @@ class Image:
|
|
556
570
|
f"currently using partition {partition}. Ignoring marketplace config."
|
557
571
|
)
|
558
572
|
|
573
|
+
# send ssn notification
|
574
|
+
if self.conf["sns"]:
|
575
|
+
self._sns_publish()
|
576
|
+
|
559
577
|
def _verify(self, region: str) -> List[ImageVerificationErrors]:
|
560
578
|
"""
|
561
579
|
Verify (but don't modify or create anything) the image in a single region
|
awspub/sns.py
ADDED
@@ -0,0 +1,88 @@
|
|
1
|
+
"""
|
2
|
+
Methods used to handle notifications for AWS using SNS
|
3
|
+
"""
|
4
|
+
|
5
|
+
import json
|
6
|
+
import logging
|
7
|
+
from typing import Any, Dict, List
|
8
|
+
|
9
|
+
import boto3
|
10
|
+
from botocore.exceptions import ClientError
|
11
|
+
from mypy_boto3_sns.client import SNSClient
|
12
|
+
from mypy_boto3_sts.client import STSClient
|
13
|
+
|
14
|
+
from awspub.context import Context
|
15
|
+
from awspub.exceptions import AWSAuthorizationException, AWSNotificationException
|
16
|
+
|
17
|
+
logger = logging.getLogger(__name__)
|
18
|
+
|
19
|
+
|
20
|
+
class SNSNotification(object):
|
21
|
+
"""
|
22
|
+
A data object that contains validation logic and
|
23
|
+
structuring rules for SNS notification JSON
|
24
|
+
"""
|
25
|
+
|
26
|
+
def __init__(self, context: Context, image_name: str, region_name: str):
|
27
|
+
"""
|
28
|
+
Construct a message and verify that it is valid
|
29
|
+
"""
|
30
|
+
self._ctx: Context = context
|
31
|
+
self._image_name: str = image_name
|
32
|
+
self._region_name: str = region_name
|
33
|
+
|
34
|
+
@property
|
35
|
+
def conf(self) -> List[Dict[str, Any]]:
|
36
|
+
"""
|
37
|
+
The sns configuration for the current image (based on "image_name") from context
|
38
|
+
"""
|
39
|
+
return self._ctx.conf["images"][self._image_name]["sns"]
|
40
|
+
|
41
|
+
def _get_topic_arn(self, topic_name: str) -> str:
|
42
|
+
"""
|
43
|
+
Calculate topic ARN based on partition, region, account and topic name
|
44
|
+
:param topic_name: Name of topic
|
45
|
+
:type topic_name: str
|
46
|
+
:param region_name: name of region
|
47
|
+
:type region_name: str
|
48
|
+
:return: return topic ARN
|
49
|
+
:rtype: str
|
50
|
+
"""
|
51
|
+
|
52
|
+
stsclient: STSClient = boto3.client("sts", region_name=self._region_name)
|
53
|
+
resp = stsclient.get_caller_identity()
|
54
|
+
|
55
|
+
account = resp["Account"]
|
56
|
+
# resp["Arn"] has string format "arn:partition:iam::accountnumber:user/iam_role"
|
57
|
+
partition = resp["Arn"].rsplit(":")[1]
|
58
|
+
|
59
|
+
return f"arn:{partition}:sns:{self._region_name}:{account}:{topic_name}"
|
60
|
+
|
61
|
+
def publish(self) -> None:
|
62
|
+
"""
|
63
|
+
send notification to subscribers
|
64
|
+
"""
|
65
|
+
|
66
|
+
snsclient: SNSClient = boto3.client("sns", region_name=self._region_name)
|
67
|
+
|
68
|
+
for topic in self.conf:
|
69
|
+
for topic_name, topic_config in topic.items():
|
70
|
+
try:
|
71
|
+
snsclient.publish(
|
72
|
+
TopicArn=self._get_topic_arn(topic_name),
|
73
|
+
Subject=topic_config["subject"],
|
74
|
+
Message=json.dumps(topic_config["message"]),
|
75
|
+
MessageStructure="json",
|
76
|
+
)
|
77
|
+
except ClientError as e:
|
78
|
+
exception_code: str = e.response["Error"]["Code"]
|
79
|
+
if exception_code == "AuthorizationError":
|
80
|
+
raise AWSAuthorizationException(
|
81
|
+
"Profile does not have a permission to send the SNS notification. Please review the policy."
|
82
|
+
)
|
83
|
+
else:
|
84
|
+
raise AWSNotificationException(str(e))
|
85
|
+
logger.info(
|
86
|
+
f"The SNS notification {topic_config['subject']}"
|
87
|
+
f" for the topic {topic_name} in {self._region_name} has been sent."
|
88
|
+
)
|
@@ -118,6 +118,35 @@ awspub:
|
|
118
118
|
-
|
119
119
|
name: /awspub-test/param2
|
120
120
|
allow_overwrite: true
|
121
|
+
"test-image-10":
|
122
|
+
boot_mode: "uefi"
|
123
|
+
description: |
|
124
|
+
A test image without a separate snapshot but single sns configs
|
125
|
+
regions:
|
126
|
+
- "us-east-1"
|
127
|
+
sns:
|
128
|
+
- "topic1":
|
129
|
+
subject: "topic1-subject"
|
130
|
+
message:
|
131
|
+
default: "default-message"
|
132
|
+
email: "email-message"
|
133
|
+
"test-image-11":
|
134
|
+
boot_mode: "uefi"
|
135
|
+
description: |
|
136
|
+
A test image without a separate snapshot but multiple sns configs
|
137
|
+
regions:
|
138
|
+
- "us-east-1"
|
139
|
+
- "eu-central-1"
|
140
|
+
sns:
|
141
|
+
- "topic1":
|
142
|
+
subject: "topic1-subject"
|
143
|
+
message:
|
144
|
+
default: "default-message"
|
145
|
+
email: "email-message"
|
146
|
+
- "topic2":
|
147
|
+
subject: "topic2-subject"
|
148
|
+
message:
|
149
|
+
default: "default-message"
|
121
150
|
|
122
151
|
tags:
|
123
152
|
name: "foobar"
|
awspub/tests/test_api.py
CHANGED
awspub/tests/test_image.py
CHANGED
@@ -127,16 +127,32 @@ def test_image___get_root_device_snapshot_id(root_device_name, block_device_mapp
|
|
127
127
|
|
128
128
|
|
129
129
|
@pytest.mark.parametrize(
|
130
|
-
|
130
|
+
(
|
131
|
+
"imagename",
|
132
|
+
"partition",
|
133
|
+
"called_mod_image",
|
134
|
+
"called_mod_snapshot",
|
135
|
+
"called_start_change_set",
|
136
|
+
"called_put_parameter",
|
137
|
+
"called_sns_publish",
|
138
|
+
),
|
131
139
|
[
|
132
|
-
("test-image-6", "aws", True, True, False, False),
|
133
|
-
("test-image-7", "aws", False, False, False, False),
|
134
|
-
("test-image-8", "aws", True, True, True, True),
|
135
|
-
("test-image-8", "aws-cn", True, True, False, True),
|
140
|
+
("test-image-6", "aws", True, True, False, False, False),
|
141
|
+
("test-image-7", "aws", False, False, False, False, False),
|
142
|
+
("test-image-8", "aws", True, True, True, True, False),
|
143
|
+
("test-image-8", "aws-cn", True, True, False, True, False),
|
144
|
+
("test-image-10", "aws", False, False, False, False, True),
|
145
|
+
("test-image-11", "aws", False, False, False, False, True),
|
136
146
|
],
|
137
147
|
)
|
138
148
|
def test_image_publish(
|
139
|
-
imagename,
|
149
|
+
imagename,
|
150
|
+
partition,
|
151
|
+
called_mod_image,
|
152
|
+
called_mod_snapshot,
|
153
|
+
called_start_change_set,
|
154
|
+
called_put_parameter,
|
155
|
+
called_sns_publish,
|
140
156
|
):
|
141
157
|
"""
|
142
158
|
Test the publish() for a given image
|
@@ -167,6 +183,7 @@ def test_image_publish(
|
|
167
183
|
"Regions": [{"RegionName": "eu-central-1"}, {"RegionName": "us-east-1"}]
|
168
184
|
}
|
169
185
|
instance.list_buckets.return_value = {"Buckets": [{"Name": "bucket1"}]}
|
186
|
+
instance.list_topics.return_value = {"Topics": [{"TopicArn": "arn:aws:sns:topic1"}]}
|
170
187
|
ctx = context.Context(curdir / "fixtures/config1.yaml", None)
|
171
188
|
img = image.Image(ctx, imagename)
|
172
189
|
img.publish()
|
@@ -174,6 +191,7 @@ def test_image_publish(
|
|
174
191
|
assert instance.modify_snapshot_attribute.called == called_mod_snapshot
|
175
192
|
assert instance.start_change_set.called == called_start_change_set
|
176
193
|
assert instance.put_parameter.called == called_put_parameter
|
194
|
+
assert instance.publish.called == called_sns_publish
|
177
195
|
|
178
196
|
|
179
197
|
def test_image__get_zero_images():
|
awspub/tests/test_sns.py
ADDED
@@ -0,0 +1,140 @@
|
|
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, 4),
|
17
|
+
],
|
18
|
+
)
|
19
|
+
def test_sns_publish(imagename, called_sns_publish, publish_call_count):
|
20
|
+
"""
|
21
|
+
Test the send_notification logic
|
22
|
+
"""
|
23
|
+
with patch("boto3.client") as bclient_mock:
|
24
|
+
instance = bclient_mock.return_value
|
25
|
+
ctx = context.Context(curdir / "fixtures/config1.yaml", None)
|
26
|
+
image_conf = ctx.conf["images"][imagename]
|
27
|
+
|
28
|
+
for region in image_conf["regions"]:
|
29
|
+
sns.SNSNotification(ctx, imagename, region).publish()
|
30
|
+
|
31
|
+
assert instance.publish.called == called_sns_publish
|
32
|
+
assert instance.publish.call_count == publish_call_count
|
33
|
+
|
34
|
+
|
35
|
+
@pytest.mark.parametrize(
|
36
|
+
"imagename",
|
37
|
+
[
|
38
|
+
("test-image-10"),
|
39
|
+
("test-image-11"),
|
40
|
+
],
|
41
|
+
)
|
42
|
+
def test_sns_publish_fail_with_invalid_topic(imagename):
|
43
|
+
"""
|
44
|
+
Test the send_notification logic
|
45
|
+
"""
|
46
|
+
with patch("boto3.client") as bclient_mock:
|
47
|
+
instance = bclient_mock.return_value
|
48
|
+
ctx = context.Context(curdir / "fixtures/config1.yaml", None)
|
49
|
+
image_conf = ctx.conf["images"][imagename]
|
50
|
+
|
51
|
+
# topic1 is invalid topic
|
52
|
+
def side_effect(*args, **kwargs):
|
53
|
+
topic_arn = kwargs.get("TopicArn")
|
54
|
+
if "topic1" in topic_arn:
|
55
|
+
error_reponse = {
|
56
|
+
"Error": {
|
57
|
+
"Code": "NotFoundException",
|
58
|
+
"Message": "An error occurred (NotFound) when calling the Publish operation: "
|
59
|
+
"Topic does not exist.",
|
60
|
+
}
|
61
|
+
}
|
62
|
+
raise botocore.exceptions.ClientError(error_reponse, "")
|
63
|
+
|
64
|
+
instance.publish.side_effect = side_effect
|
65
|
+
|
66
|
+
for region in image_conf["regions"]:
|
67
|
+
with pytest.raises(exceptions.AWSNotificationException):
|
68
|
+
sns.SNSNotification(ctx, imagename, region).publish()
|
69
|
+
|
70
|
+
|
71
|
+
@pytest.mark.parametrize(
|
72
|
+
"imagename",
|
73
|
+
[
|
74
|
+
("test-image-10"),
|
75
|
+
("test-image-11"),
|
76
|
+
],
|
77
|
+
)
|
78
|
+
def test_sns_publish_fail_with_unauthorized_user(imagename):
|
79
|
+
"""
|
80
|
+
Test the send_notification logic
|
81
|
+
"""
|
82
|
+
with patch("boto3.client") as bclient_mock:
|
83
|
+
instance = bclient_mock.return_value
|
84
|
+
ctx = context.Context(curdir / "fixtures/config1.yaml", None)
|
85
|
+
image_conf = ctx.conf["images"][imagename]
|
86
|
+
|
87
|
+
error_reponse = {
|
88
|
+
"Error": {
|
89
|
+
"Code": "AuthorizationError",
|
90
|
+
"Message": "User are not authorized perform SNS Notification service",
|
91
|
+
}
|
92
|
+
}
|
93
|
+
instance.publish.side_effect = botocore.exceptions.ClientError(error_reponse, "")
|
94
|
+
|
95
|
+
for region in image_conf["regions"]:
|
96
|
+
with pytest.raises(exceptions.AWSAuthorizationException):
|
97
|
+
sns.SNSNotification(ctx, imagename, region).publish()
|
98
|
+
|
99
|
+
|
100
|
+
@pytest.mark.parametrize(
|
101
|
+
"imagename, partition, expected",
|
102
|
+
[
|
103
|
+
(
|
104
|
+
"test-image-10",
|
105
|
+
"aws-cn",
|
106
|
+
[
|
107
|
+
"arn:aws-cn:sns:us-east-1:1234:topic1",
|
108
|
+
],
|
109
|
+
),
|
110
|
+
(
|
111
|
+
"test-image-11",
|
112
|
+
"aws",
|
113
|
+
[
|
114
|
+
"arn:aws:sns:us-east-1:1234:topic1",
|
115
|
+
"arn:aws:sns:us-east-1:1234:topic2",
|
116
|
+
"arn:aws:sns:eu-central-1:1234:topic1",
|
117
|
+
"arn:aws:sns:eu-central-1:1234:topic2",
|
118
|
+
],
|
119
|
+
),
|
120
|
+
],
|
121
|
+
)
|
122
|
+
def test_sns__get_topic_arn(imagename, partition, expected):
|
123
|
+
"""
|
124
|
+
Test the send_notification logic
|
125
|
+
"""
|
126
|
+
with patch("boto3.client") as bclient_mock:
|
127
|
+
instance = bclient_mock.return_value
|
128
|
+
ctx = context.Context(curdir / "fixtures/config1.yaml", None)
|
129
|
+
image_conf = ctx.conf["images"][imagename]
|
130
|
+
|
131
|
+
instance.get_caller_identity.return_value = {"Account": "1234", "Arn": f"arn:{partition}:iam::1234:user/test"}
|
132
|
+
|
133
|
+
topic_arns = []
|
134
|
+
for region in image_conf["regions"]:
|
135
|
+
for topic in image_conf["sns"]:
|
136
|
+
topic_name = next(iter(topic))
|
137
|
+
res_arn = sns.SNSNotification(ctx, imagename, region)._get_topic_arn(topic_name)
|
138
|
+
topic_arns.append(res_arn)
|
139
|
+
|
140
|
+
assert topic_arns == expected
|
@@ -1,6 +1,6 @@
|
|
1
1
|
Metadata-Version: 2.1
|
2
2
|
Name: awspub
|
3
|
-
Version: 0.0.
|
3
|
+
Version: 0.0.8
|
4
4
|
Summary: Publish images to AWS EC2
|
5
5
|
Home-page: https://github.com/canonical/awspub
|
6
6
|
License: GPL-3.0-or-later
|
@@ -14,9 +14,10 @@ Classifier: Programming Language :: Python :: 3.9
|
|
14
14
|
Classifier: Programming Language :: Python :: 3.10
|
15
15
|
Classifier: Programming Language :: Python :: 3.11
|
16
16
|
Classifier: Programming Language :: Python :: 3.12
|
17
|
+
Classifier: Programming Language :: Python :: 3.13
|
17
18
|
Requires-Dist: autodoc-pydantic (>=2.0.1,<3.0.0)
|
18
19
|
Requires-Dist: boto3
|
19
|
-
Requires-Dist: boto3-stubs[essential,marketplace-catalog,s3,ssm] (>=1.28.83,<2.0.0)
|
20
|
+
Requires-Dist: boto3-stubs[essential,marketplace-catalog,s3,sns,ssm,sts] (>=1.28.83,<2.0.0)
|
20
21
|
Requires-Dist: pydantic (>=2,<3)
|
21
22
|
Requires-Dist: ruamel-yaml (>=0.18.6,<0.19.0)
|
22
23
|
Project-URL: Repository, https://github.com/canonical/awspub
|
@@ -44,20 +45,3 @@ License
|
|
44
45
|
|
45
46
|
The project uses `GPL-3.0` as license.
|
46
47
|
|
47
|
-
Doing a new release
|
48
|
-
===================
|
49
|
-
|
50
|
-
New releases are mostly automated.
|
51
|
-
|
52
|
-
pypi
|
53
|
-
----
|
54
|
-
|
55
|
-
For a new release on pypi, create a new tag (following semantic versioning)
|
56
|
-
with a `v` as prefix (eg. `v0.2.1`).
|
57
|
-
|
58
|
-
snapstore
|
59
|
-
---------
|
60
|
-
|
61
|
-
The latest git commit will be automatically build and published to the `latest/edge`
|
62
|
-
channel. Manually promote from `latest/edge` to `latest/stable`.
|
63
|
-
|
@@ -2,32 +2,34 @@ awspub/__init__.py,sha256=7hgLrq6k53yaJrjFe7X5Cm45z3SIc1Vxocb5k3G8xPc,124
|
|
2
2
|
awspub/api.py,sha256=d1gx9LdqdYXRLf8yZ_spIz_93WhB2GNnCG_x3ABrMkI,6497
|
3
3
|
awspub/cli/__init__.py,sha256=-zCBEbnt5zbvSZ8PxQALpPAy0CiQUf-qZnikJ7U4Sf0,5621
|
4
4
|
awspub/common.py,sha256=M_Ibw8DoAHG3oLoK5qRUggEjI7kJSSslC7r9VySe8vk,562
|
5
|
-
awspub/configmodels.py,sha256=
|
5
|
+
awspub/configmodels.py,sha256=eHYS09ZA0tIYUWf2upD-Ymy-KMjfVanBl6UHRvDvjpA,9055
|
6
6
|
awspub/context.py,sha256=LDkp9Sz5AqRxQq70ICgFIJn5g2qrc5qiVawTyS_rXZE,4064
|
7
|
-
awspub/exceptions.py,sha256=
|
8
|
-
awspub/image.py,sha256=
|
7
|
+
awspub/exceptions.py,sha256=2JUEPhZ3sir2NoAuqteFYlh84LsrD7vaeIpkiWtiBpc,556
|
8
|
+
awspub/image.py,sha256=G3vRj0TF-WsTn0Jr4Q2xmS1sTLfntUG1jydbG3NwAEM,28308
|
9
9
|
awspub/image_marketplace.py,sha256=oiD7yNU5quG5CQG9Ql5Ut9hLWA1yewg6qVwTbyadGwc,5314
|
10
10
|
awspub/s3.py,sha256=ivR8DuAkYilph73EjFkTgUelkXxU7pZfosnsHHyoZkQ,11274
|
11
11
|
awspub/snapshot.py,sha256=V5e_07SnmCwEPjRmwZh43spWparhH8X4ugG16uQfGuo,10040
|
12
|
+
awspub/sns.py,sha256=dOO5_Rs1ZUeDiBCNVyh4U6oo-Qht-jaAENOg3g5WJpA,3106
|
12
13
|
awspub/tests/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
13
14
|
awspub/tests/fixtures/config-invalid-s3-extra.yaml,sha256=TdgqE-quxgueXS9L8ixsRuG6eTVfqalZ41G3JNCWn58,288
|
14
15
|
awspub/tests/fixtures/config-minimal.yaml,sha256=oHupXHYQXxmqgN2qFCAwvxzR7Bch-3yScrmMXeMIICE,176
|
15
16
|
awspub/tests/fixtures/config-valid-nonawspub.yaml,sha256=Md-YINQQRo3kveikUxk8Co9BYIZfDftmPT2LmIqoTL4,330
|
16
17
|
awspub/tests/fixtures/config1.vmdk,sha256=YlJHVAi5-e5kRSthHXBqB4gxqZsSPbadFE2HigSIoKg,65536
|
17
|
-
awspub/tests/fixtures/config1.yaml,sha256=
|
18
|
+
awspub/tests/fixtures/config1.yaml,sha256=TjGAhzgvDiVu1ILAIKPg_ka0TozYGDoZTrhNUO2LCYw,4089
|
18
19
|
awspub/tests/fixtures/config2-mapping.yaml,sha256=lqJE0ej9DdGsE8O5dqG5PX7bOJrY4nMciXoOzMzV-so,31
|
19
20
|
awspub/tests/fixtures/config2.yaml,sha256=m2v-n1T-XPGDHyrJXArC_rYV-ZPMr9cgzHkLXiSRuDs,1250
|
20
21
|
awspub/tests/fixtures/config3-duplicate-keys.yaml,sha256=Cn0tTQawpEFocDNpWxDz1651uQa7aw88XjNyPcCG4iQ,324
|
21
|
-
awspub/tests/test_api.py,sha256=
|
22
|
+
awspub/tests/test_api.py,sha256=vdMUwICUoDwuDd-HuATUttHv6Up1xX5PZebAfdQEK-w,2570
|
22
23
|
awspub/tests/test_cli.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
23
24
|
awspub/tests/test_common.py,sha256=kytMUU47uZYYe302XswdO15qX_i1vO2LS5n96--TcSU,478
|
24
25
|
awspub/tests/test_context.py,sha256=wMXQqj4vi2U3q5w1xPV-stB3mp3K6puUyXhsShJG4wA,3115
|
25
|
-
awspub/tests/test_image.py,sha256=
|
26
|
+
awspub/tests/test_image.py,sha256=T7vDFHRSaqTG-jthJLDiKcv791Q4LMM4Crh-6U1ImjM,19548
|
26
27
|
awspub/tests/test_image_marketplace.py,sha256=JP7PrFjix1AyQg7eEaQ-wCROVoIOb873koseniOqGQQ,1456
|
27
28
|
awspub/tests/test_s3.py,sha256=UJL8CQDEvhA42MwPGeSvSbQFj8h86c1LrLFDvcMcRws,2857
|
28
29
|
awspub/tests/test_snapshot.py,sha256=8KPTqGVyzrpivWuq3HE7ZhgtLllcr3rA_3hZcxu2xjg,4123
|
29
|
-
awspub
|
30
|
-
awspub-0.0.
|
31
|
-
awspub-0.0.
|
32
|
-
awspub-0.0.
|
33
|
-
awspub-0.0.
|
30
|
+
awspub/tests/test_sns.py,sha256=vm99g_PRZiwc8b87wcnR86lVX4TnthYvtvjlZ4QbwVk,4421
|
31
|
+
awspub-0.0.8.dist-info/LICENSE,sha256=9GbrzFQ3rWjVKj-IZnX1kGDsIGIdjc25KGRmAp03Jn0,35150
|
32
|
+
awspub-0.0.8.dist-info/METADATA,sha256=WsTaPubZWaJ86ZNx1syaj5F9sgC0Kc4VZeRzWMh420k,1458
|
33
|
+
awspub-0.0.8.dist-info/WHEEL,sha256=Nq82e9rUAnEjt98J6MlVmMCZb-t9cYE2Ir1kpBmnWfs,88
|
34
|
+
awspub-0.0.8.dist-info/entry_points.txt,sha256=hrQzy9P5yO58nj6W0UDPdQPUTqEkQLpMvuyDDRu7LRQ,42
|
35
|
+
awspub-0.0.8.dist-info/RECORD,,
|
File without changes
|
File without changes
|