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.
@@ -0,0 +1,556 @@
1
+ import pathlib
2
+ from unittest.mock import MagicMock, patch
3
+
4
+ import botocore.exceptions
5
+ import pytest
6
+
7
+ from awspub import context, exceptions, image
8
+
9
+ curdir = pathlib.Path(__file__).parent.resolve()
10
+
11
+
12
+ @pytest.mark.parametrize(
13
+ "imagename,snapshotname",
14
+ [
15
+ # test-image-1 without any separate snapshot or billing products.
16
+ # so snapshotname should match source sha256sum
17
+ ("test-image-1", "6252475408b9f9ee64452b611d706a078831a99b123db69d144d878a0488a0a8"),
18
+ # test-image-2 with separate snapshot but without billing products.
19
+ # so snapshotname should be the shasum of the concatenated string of:
20
+ # - 6252475408b9f9ee64452b611d706a078831a99b123db69d144d878a0488a0a8
21
+ # - echo -n test-image-2 | sha256sum
22
+ ("test-image-2", "0c274a96fe840cdd9cf65b0bf8e4d755d94fddf00916aa6f26ee3f08e412c88f"),
23
+ # test-image-3 with separate snapshot and billing products
24
+ ("test-image-3", "ef7c5bbbc2816c60acfa4f3954e431c849054f7370bf351055f6d665b60623e7"),
25
+ # test-image-4 without separate snapshot but with billing products
26
+ ("test-image-4", "bf795c602d53ff9c9548cc6305aa1240bd0f3d4429869abe4c96bcef65c4e48d"),
27
+ # test-image-5 without separate snapshot but with multiple billing products
28
+ ("test-image-5", "8171cd4d36d06150a5ff8bb519439c5efd4e91841be62f50736db3b82e4aaedc"),
29
+ ],
30
+ )
31
+ def test_snapshot_names(imagename, snapshotname):
32
+ """
33
+ Test the snapshot name calculation based on the image properties
34
+ """
35
+ ctx = context.Context(curdir / "fixtures/config1.yaml", None)
36
+ assert ctx.conf["source"]["path"] == curdir / "fixtures/config1.vmdk"
37
+ assert ctx.source_sha256 == "6252475408b9f9ee64452b611d706a078831a99b123db69d144d878a0488a0a8"
38
+
39
+ img = image.Image(ctx, imagename)
40
+ assert img.snapshot_name == snapshotname
41
+
42
+
43
+ @pytest.mark.parametrize(
44
+ "imagename,regions_in_partition,regions_expected",
45
+ [
46
+ # test-image-1 has 2 regions defined
47
+ ("test-image-1", ["region1", "region2"], ["region1", "region2"]),
48
+ # test-image-1 has 2 regions defined and there are more regions in the partition
49
+ ("test-image-1", ["region1", "region2", "region3"], ["region1", "region2"]),
50
+ ("test-image-1", ["region1", "region2"], ["region1", "region2"]),
51
+ # test-image-2 has no regions defined, so whatever the ec2 client returns should be valid
52
+ ("test-image-2", ["all-region-1", "all-region-2"], ["all-region-1", "all-region-2"]),
53
+ # test-image-1 has 2 regions defined, but those regions are not in the partition
54
+ ("test-image-1", ["region3", "region4"], []),
55
+ # test-image-1 has 2 regions defined, but those regions are not partially in the partition
56
+ ("test-image-1", ["region2", "region4"], ["region2"]),
57
+ # test-image-2 has no regions defined and the ec2 client doesn't return any regions
58
+ ("test-image-2", [], []),
59
+ ],
60
+ )
61
+ @patch("awspub.s3.S3.bucket_region", return_value="region1")
62
+ def test_image_regions(s3_region_mock, imagename, regions_in_partition, regions_expected):
63
+ """
64
+ Test the regions for a given image
65
+ """
66
+ with patch("boto3.client") as bclient_mock:
67
+ instance = bclient_mock.return_value
68
+ instance.describe_regions.return_value = {"Regions": [{"RegionName": r} for r in regions_in_partition]}
69
+ ctx = context.Context(curdir / "fixtures/config1.yaml", None)
70
+ img = image.Image(ctx, imagename)
71
+ assert sorted(img.image_regions) == sorted(regions_expected)
72
+
73
+
74
+ @pytest.mark.parametrize(
75
+ "imagename,cleanup",
76
+ [
77
+ ("test-image-1", True),
78
+ ("test-image-2", False),
79
+ ],
80
+ )
81
+ def test_image_cleanup(imagename, cleanup):
82
+ """
83
+ Test the cleanup for a given image
84
+ """
85
+ with patch("boto3.client") as bclient_mock:
86
+ instance = bclient_mock.return_value
87
+ instance.describe_images.return_value = {"Images": [{"Name": imagename, "Public": False, "ImageId": "ami-123"}]}
88
+ instance.describe_regions.return_value = {"Regions": [{"RegionName": "region1"}, {"RegionName": "region2"}]}
89
+ instance.list_buckets.return_value = {"Buckets": [{"Name": "bucket1"}]}
90
+ ctx = context.Context(curdir / "fixtures/config1.yaml", None)
91
+ img = image.Image(ctx, imagename)
92
+ img.cleanup()
93
+ assert instance.deregister_image.called == cleanup
94
+
95
+
96
+ @pytest.mark.parametrize(
97
+ "root_device_name,block_device_mappings,snapshot_id",
98
+ [
99
+ ("", [], None),
100
+ ("/dev/sda1", [], None),
101
+ (
102
+ "/dev/sda1",
103
+ [
104
+ {
105
+ "DeviceName": "/dev/sda1",
106
+ "Ebs": {
107
+ "DeleteOnTermination": True,
108
+ "SnapshotId": "snap-0be0763f84af34e05",
109
+ "VolumeSize": 17,
110
+ "VolumeType": "gp2",
111
+ "Encrypted": False,
112
+ },
113
+ },
114
+ {"DeviceName": "/dev/sdb", "VirtualName": "ephemeral0"},
115
+ ],
116
+ "snap-0be0763f84af34e05",
117
+ ),
118
+ ],
119
+ )
120
+ def test_image___get_root_device_snapshot_id(root_device_name, block_device_mappings, snapshot_id):
121
+ """
122
+ Test the _get_root_device_snapshot_id() method
123
+ """
124
+ i = {"RootDeviceName": root_device_name, "BlockDeviceMappings": block_device_mappings}
125
+ ctx = context.Context(curdir / "fixtures/config1.yaml", None)
126
+ img = image.Image(ctx, "test-image-1")
127
+ assert img._get_root_device_snapshot_id(i) == snapshot_id
128
+
129
+
130
+ @pytest.mark.parametrize(
131
+ (
132
+ "imagename",
133
+ "partition",
134
+ "called_mod_image",
135
+ "called_mod_snapshot",
136
+ "called_start_change_set",
137
+ "called_put_parameter",
138
+ "called_sns_publish",
139
+ ),
140
+ [
141
+ ("test-image-6", "aws", True, True, False, False, False),
142
+ ("test-image-7", "aws", False, False, False, False, False),
143
+ ("test-image-8", "aws", True, True, True, True, False),
144
+ ("test-image-8", "aws-cn", True, True, False, True, False),
145
+ ("test-image-10", "aws", False, False, False, False, True),
146
+ ("test-image-11", "aws", False, False, False, False, True),
147
+ ("test-image-12", "aws", False, False, False, False, True),
148
+ ],
149
+ )
150
+ def test_image_publish(
151
+ imagename,
152
+ partition,
153
+ called_mod_image,
154
+ called_mod_snapshot,
155
+ called_start_change_set,
156
+ called_put_parameter,
157
+ called_sns_publish,
158
+ ):
159
+ """
160
+ Test the publish() for a given image
161
+ """
162
+ with patch("boto3.client") as bclient_mock:
163
+ instance = bclient_mock.return_value
164
+ instance.meta.partition = partition
165
+ instance.describe_images.return_value = {
166
+ "Images": [
167
+ {
168
+ "Name": imagename,
169
+ "ImageId": "ami-abc",
170
+ "RootDeviceName": "/dev/sda1",
171
+ "BlockDeviceMappings": [
172
+ {
173
+ "DeviceName": "/dev/sda1",
174
+ "Ebs": {
175
+ "DeleteOnTermination": True,
176
+ "SnapshotId": "snap-0be0763f84af34e05",
177
+ },
178
+ },
179
+ ],
180
+ }
181
+ ]
182
+ }
183
+ instance.get_parameters.return_value = {"Parameters": []}
184
+ instance.describe_regions.return_value = {
185
+ "Regions": [{"RegionName": "eu-central-1"}, {"RegionName": "us-east-1"}]
186
+ }
187
+ instance.list_buckets.return_value = {"Buckets": [{"Name": "bucket1"}]}
188
+ ctx = context.Context(curdir / "fixtures/config1.yaml", None)
189
+ img = image.Image(ctx, imagename)
190
+ img.publish()
191
+ assert instance.modify_image_attribute.called == called_mod_image
192
+ assert instance.modify_snapshot_attribute.called == called_mod_snapshot
193
+ assert instance.start_change_set.called == called_start_change_set
194
+ assert instance.put_parameter.called == called_put_parameter
195
+ assert instance.publish.called == called_sns_publish
196
+
197
+
198
+ def test_image__get_zero_images():
199
+ """
200
+ Test the Image._get() method with zero matching image
201
+ """
202
+ with patch("boto3.client") as bclient_mock:
203
+ instance = bclient_mock.return_value
204
+ instance.describe_images.return_value = {"Images": []}
205
+ ctx = context.Context(curdir / "fixtures/config1.yaml", None)
206
+ img = image.Image(ctx, "test-image-1")
207
+ assert img._get(instance) is None
208
+
209
+
210
+ def test_image__get_one_images():
211
+ """
212
+ Test the Image._get() method with a single matching image
213
+ """
214
+ with patch("boto3.client") as bclient_mock:
215
+ instance = bclient_mock.return_value
216
+ instance.describe_images.return_value = {
217
+ "Images": [
218
+ {
219
+ "Name": "test-image-1",
220
+ "ImageId": "ami-abc",
221
+ "RootDeviceName": "/dev/sda1",
222
+ "BlockDeviceMappings": [
223
+ {
224
+ "DeviceName": "/dev/sda1",
225
+ "Ebs": {
226
+ "DeleteOnTermination": True,
227
+ "SnapshotId": "snap-abc",
228
+ },
229
+ },
230
+ ],
231
+ }
232
+ ]
233
+ }
234
+ ctx = context.Context(curdir / "fixtures/config1.yaml", None)
235
+ img = image.Image(ctx, "test-image-1")
236
+ assert img._get(instance) == image._ImageInfo("ami-abc", "snap-abc")
237
+
238
+
239
+ def test_image__get_multiple_images():
240
+ """
241
+ Test the Image._get() method with a multiple matching image
242
+ """
243
+ with patch("boto3.client") as bclient_mock:
244
+ instance = bclient_mock.return_value
245
+ instance.describe_images.return_value = {
246
+ "Images": [
247
+ {
248
+ "Name": "test-image-1",
249
+ "ImageId": "ami-1,",
250
+ },
251
+ {
252
+ "Name": "test-image-1",
253
+ "ImageId": "ami-2,",
254
+ },
255
+ ]
256
+ }
257
+ ctx = context.Context(curdir / "fixtures/config1.yaml", None)
258
+ img = image.Image(ctx, "test-image-1")
259
+ with pytest.raises(exceptions.MultipleImagesException):
260
+ img._get(instance)
261
+
262
+
263
+ @pytest.mark.parametrize(
264
+ "imagename,expected_tags",
265
+ [
266
+ # no image specific tags - assume the common tags
267
+ (
268
+ "test-image-1",
269
+ [
270
+ {"Key": "awspub:source:filename", "Value": "config1.vmdk"},
271
+ {"Key": "awspub:source:architecture", "Value": "x86_64"},
272
+ {
273
+ "Key": "awspub:source:sha256",
274
+ "Value": "6252475408b9f9ee64452b611d706a078831a99b123db69d144d878a0488a0a8",
275
+ },
276
+ {"Key": "name", "Value": "foobar"},
277
+ ],
278
+ ),
279
+ # with image specific tag but no override
280
+ (
281
+ "test-image-6",
282
+ [
283
+ {"Key": "awspub:source:filename", "Value": "config1.vmdk"},
284
+ {"Key": "awspub:source:architecture", "Value": "x86_64"},
285
+ {
286
+ "Key": "awspub:source:sha256",
287
+ "Value": "6252475408b9f9ee64452b611d706a078831a99b123db69d144d878a0488a0a8",
288
+ },
289
+ {"Key": "name", "Value": "foobar"},
290
+ {"Key": "key1", "Value": "value1"},
291
+ ],
292
+ ),
293
+ # with image specific tag which overrides common tag
294
+ (
295
+ "test-image-7",
296
+ [
297
+ {"Key": "awspub:source:filename", "Value": "config1.vmdk"},
298
+ {"Key": "awspub:source:architecture", "Value": "x86_64"},
299
+ {
300
+ "Key": "awspub:source:sha256",
301
+ "Value": "6252475408b9f9ee64452b611d706a078831a99b123db69d144d878a0488a0a8",
302
+ },
303
+ {"Key": "name", "Value": "not-foobar"},
304
+ {"Key": "key2", "Value": "name"},
305
+ ],
306
+ ),
307
+ ],
308
+ )
309
+ def test_image__tags(imagename, expected_tags):
310
+ """
311
+ Test the Image._tags() method
312
+ """
313
+ ctx = context.Context(curdir / "fixtures/config1.yaml", None)
314
+ img = image.Image(ctx, imagename)
315
+ assert img._tags == expected_tags
316
+
317
+
318
+ @pytest.mark.parametrize(
319
+ "available_images,expected",
320
+ [
321
+ # image available
322
+ ([{"Name": "test-image-6", "ImageId": "ami-123"}], {"eu-central-1": image._ImageInfo("ami-123", None)}),
323
+ # image not available
324
+ ([], {}),
325
+ ],
326
+ )
327
+ def test_image_list(available_images, expected):
328
+ """
329
+ Test the list for a given image
330
+ """
331
+ with patch("boto3.client") as bclient_mock:
332
+ instance = bclient_mock.return_value
333
+ instance.describe_images.return_value = {"Images": available_images}
334
+ instance.describe_regions.return_value = {
335
+ "Regions": [{"RegionName": "eu-central-1"}, {"RegionName": "us-east-1"}]
336
+ }
337
+ instance.list_buckets.return_value = {"Buckets": [{"Name": "bucket1"}]}
338
+ ctx = context.Context(curdir / "fixtures/config1.yaml", None)
339
+ img = image.Image(ctx, "test-image-6")
340
+ assert img.list() == expected
341
+
342
+
343
+ @patch("awspub.s3.S3.bucket_region", return_value="region1")
344
+ def test_image_create_existing(s3_bucket_mock):
345
+ """
346
+ Test the create() method for a given image that already exist
347
+ """
348
+ with patch("boto3.client") as bclient_mock:
349
+ instance = bclient_mock.return_value
350
+ instance.describe_snapshots.return_value = {"Snapshots": [{"SnapshotId": "snap-123"}]}
351
+ instance.describe_images.return_value = {
352
+ "Images": [
353
+ {
354
+ "Name": "test-image-6",
355
+ "ImageId": "ami-123",
356
+ "RootDeviceName": "/dev/sda1",
357
+ "BlockDeviceMappings": [
358
+ {
359
+ "DeviceName": "/dev/sda1",
360
+ "Ebs": {
361
+ "DeleteOnTermination": True,
362
+ "SnapshotId": "snap-123",
363
+ },
364
+ },
365
+ ],
366
+ }
367
+ ]
368
+ }
369
+ instance.describe_regions.return_value = {
370
+ "Regions": [{"RegionName": "eu-central-1"}, {"RegionName": "us-east-1"}]
371
+ }
372
+ instance.list_buckets.return_value = {"Buckets": [{"Name": "bucket1"}]}
373
+ ctx = context.Context(curdir / "fixtures/config1.yaml", None)
374
+ img = image.Image(ctx, "test-image-6")
375
+ assert img.create() == {"eu-central-1": image._ImageInfo(image_id="ami-123", snapshot_id="snap-123")}
376
+ # register and create_tags shouldn't be called given that the image was already there
377
+ assert not instance.register_image.called
378
+ assert not instance.create_tags.called
379
+
380
+
381
+ @pytest.mark.parametrize(
382
+ "imagename,describe_images,get_parameters,get_parameters_called,put_parameter_called",
383
+ [
384
+ # no image, no parameters (this should actually never happen but test it anyway)
385
+ ("test-image-8", [], [], False, False),
386
+ # with image, no parameter, no overwrite
387
+ ("test-image-8", [{"Name": "test-image-8", "ImageId": "ami-123"}], [], True, True),
388
+ # with image, with parameter, no overwrite
389
+ (
390
+ "test-image-8",
391
+ [{"Name": "test-image-8", "ImageId": "ami-123"}],
392
+ [{"Name": "/awspub-test/param1", "Value": "ami-123"}],
393
+ True,
394
+ False,
395
+ ),
396
+ # with image, no parameter, with overwrite
397
+ ("test-image-9", [{"Name": "test-image-8", "ImageId": "ami-123"}], [], False, True),
398
+ # with image, with parameter, with overwrite
399
+ (
400
+ "test-image-9",
401
+ [{"Name": "test-image-8", "ImageId": "ami-123"}],
402
+ [{"Name": "/awspub-test/param1", "Value": "ami-123"}],
403
+ False,
404
+ True,
405
+ ),
406
+ ],
407
+ )
408
+ def test_image__put_ssm_parameters(
409
+ imagename, describe_images, get_parameters, get_parameters_called, put_parameter_called
410
+ ):
411
+ """
412
+ Test the _put_ssm_parameters() method
413
+ """
414
+ with patch("boto3.client") as bclient_mock:
415
+ instance = bclient_mock.return_value
416
+ instance.describe_images.return_value = {"Images": describe_images}
417
+ instance.get_parameters.return_value = {"Parameters": get_parameters}
418
+ instance.describe_regions.return_value = {
419
+ "Regions": [{"RegionName": "eu-central-1"}, {"RegionName": "us-east-1"}]
420
+ }
421
+ instance.list_buckets.return_value = {"Buckets": [{"Name": "bucket1"}]}
422
+ ctx = context.Context(curdir / "fixtures/config1.yaml", None)
423
+ img = image.Image(ctx, imagename)
424
+ img._put_ssm_parameters()
425
+ assert instance.get_parameters.called == get_parameters_called
426
+ assert instance.put_parameter.called == put_parameter_called
427
+
428
+
429
+ @pytest.mark.parametrize(
430
+ "image_found,config,config_image_name, expected_problems",
431
+ [
432
+ # image not available
433
+ ([], "fixtures/config1.yaml", "test-image-6", [image.ImageVerificationErrors.NOT_EXIST]),
434
+ # image matches expectations from config
435
+ (
436
+ [
437
+ {
438
+ "Name": "test-image-6",
439
+ "State": "available",
440
+ "ImageId": "ami-123",
441
+ "RootDeviceName": "/dev/sda1",
442
+ "RootDeviceType": "ebs",
443
+ "BootMode": "uefi-preferred",
444
+ "BlockDeviceMappings": [
445
+ {
446
+ "DeviceName": "/dev/sda1",
447
+ "Ebs": {
448
+ "DeleteOnTermination": True,
449
+ "VolumeType": "gp3",
450
+ "VolumeSize": 8,
451
+ "SnapshotId": "snap-123",
452
+ },
453
+ },
454
+ ],
455
+ "Tags": [
456
+ {"Key": "name", "Value": "foobar"},
457
+ ],
458
+ }
459
+ ],
460
+ "fixtures/config1.yaml",
461
+ "test-image-6",
462
+ [],
463
+ ),
464
+ ],
465
+ )
466
+ def test_image__verify(image_found, config, config_image_name, expected_problems):
467
+ """
468
+ Test _verify() for a given image and configuration
469
+ """
470
+ with patch("boto3.client") as bclient_mock:
471
+ instance = bclient_mock.return_value
472
+ instance.describe_images.return_value = {"Images": image_found}
473
+ instance.describe_snapshots.return_value = {"Snapshots": [{"State": "completed"}]}
474
+ ctx = context.Context(curdir / config, None)
475
+ img = image.Image(ctx, config_image_name)
476
+ problems = img._verify("eu-central-1")
477
+ assert problems == expected_problems
478
+
479
+
480
+ @pytest.mark.parametrize(
481
+ "partition,imagename,share_list_expected",
482
+ [
483
+ ("aws", "test-image-8", [{"UserId": "123456789123"}, {"UserId": "221020170000"}, {"UserId": "290620200000"}]),
484
+ ("aws-cn", "test-image-8", [{"UserId": "334455667788"}]),
485
+ ("aws-us-gov", "test-image-8", []),
486
+ ],
487
+ )
488
+ def test_image__share_list_filtered(partition, imagename, share_list_expected):
489
+ """
490
+ Test _share_list_filtered() for a given image
491
+ """
492
+ with patch("boto3.client") as bclient_mock:
493
+ instance = bclient_mock.return_value
494
+ instance.meta.partition = partition
495
+ ctx = context.Context(curdir / "fixtures/config1.yaml", None)
496
+ img = image.Image(ctx, imagename)
497
+ assert img._share_list_filtered(img.conf["share"]) == share_list_expected
498
+
499
+
500
+ @patch("awspub.s3.S3.bucket_region", return_value="region1")
501
+ def test_create__should_allow_partial_registration(s3_bucket_mock):
502
+ """
503
+ Test that the create() method allows a partial upload set
504
+ """
505
+ with patch("boto3.client") as bclient_mock:
506
+ instance = bclient_mock.return_value
507
+
508
+ ctx = context.Context(curdir / "fixtures/config1.yaml", None)
509
+ img = image.Image(ctx, "test-image-6")
510
+ img._image_regions = ["region1", "region2"]
511
+ img._image_regions_cached = True
512
+ with patch.object(img, "_get") as get_mock, patch.object(img._snapshot, "copy") as copy_mock:
513
+ copy_mock.return_value = {r: f"snapshot{i}" for i, r in enumerate(img.image_regions)}
514
+ get_mock.return_value = None
515
+ instance.register_image.side_effect = [
516
+ botocore.exceptions.ClientError(
517
+ {
518
+ "Error": {
519
+ "Code": "OperationNotPermitted",
520
+ "Message": "Intentional permission failure for snapshot0",
521
+ }
522
+ },
523
+ "awspub Testing",
524
+ ),
525
+ {"ImageId": "id1"},
526
+ ]
527
+ with pytest.raises(exceptions.IncompleteImageSetException):
528
+ img.create() == {"region2": image._ImageInfo("id1", "snapshot1")}
529
+ # register and create_tags should be called since at least one snapshot made it
530
+ assert instance.register_image.called
531
+ assert instance.create_tags.called
532
+
533
+
534
+ def test_register_image__should_return_none_on_permission_failures():
535
+ instance = MagicMock()
536
+
537
+ instance.register_image.side_effect = botocore.exceptions.ClientError(
538
+ {"Error": {"Code": "OperationNotPermitted", "Message": "Testing"}}, "Testing"
539
+ )
540
+ ctx = context.Context(curdir / "fixtures/config1.yaml", None)
541
+ img = image.Image(ctx, "test-image-6")
542
+ snapshot_ids = {"eu-central-1": "my-snapshot"}
543
+ assert img._register_image(snapshot_ids["eu-central-1"], instance) is None
544
+
545
+
546
+ def test_register_image__should_raise_on_unhandled_client_error():
547
+ instance = MagicMock()
548
+
549
+ instance.register_image.side_effect = botocore.exceptions.ClientError(
550
+ {"Error": {"Code": "UnsupportedOperation", "Message": "Testing"}}, "Testing"
551
+ )
552
+ ctx = context.Context(curdir / "fixtures/config1.yaml", None)
553
+ img = image.Image(ctx, "test-image-6")
554
+ snapshot_ids = {"eu-central-1": "my-snapshot"}
555
+ with pytest.raises(botocore.exceptions.ClientError):
556
+ img._register_image(snapshot_ids["eu-central-1"], instance) is None
@@ -0,0 +1,44 @@
1
+ import pathlib
2
+ from unittest.mock import patch
3
+
4
+ import pytest
5
+
6
+ from awspub import context, image_marketplace
7
+
8
+ curdir = pathlib.Path(__file__).parent.resolve()
9
+
10
+
11
+ @pytest.mark.parametrize(
12
+ "imagename,new_version,called_start_change_set",
13
+ [
14
+ # same version that already exists
15
+ ("test-image-8", "1.0.0", False),
16
+ # new version
17
+ ("test-image-8", "2.0.0", True),
18
+ ],
19
+ )
20
+ def test_image_marketplace_request_new_version(imagename, new_version, called_start_change_set):
21
+ """
22
+ Test the request_new_version logic
23
+ """
24
+ with patch("boto3.client") as bclient_mock:
25
+ instance = bclient_mock.return_value
26
+ instance.describe_entity.return_value = {"DetailsDocument": {"Versions": [{"VersionTitle": new_version}]}}
27
+ ctx = context.Context(curdir / "fixtures/config1.yaml", None)
28
+ img = image_marketplace.ImageMarketplace(ctx, imagename)
29
+ img.request_new_version("ami-123")
30
+ assert instance.start_change_set.called == called_start_change_set
31
+
32
+
33
+ @pytest.mark.parametrize(
34
+ "name,expected",
35
+ [
36
+ ("1.0.0", "1.0.0"),
37
+ ("1.0.0 (testing)", "1.0.0 testing"),
38
+ ("a sentence with spaces", "a sentence with spaces"),
39
+ ("_+=.:@-", "_+=.:@-"),
40
+ ("(parens) [brackets] |pipes|", "parens brackets pipes"),
41
+ ],
42
+ )
43
+ def test_changeset_name_sanitization(name, expected):
44
+ assert image_marketplace.ImageMarketplace.sanitize_changeset_name(name) == expected
@@ -0,0 +1,74 @@
1
+ import pathlib
2
+ from unittest.mock import patch
3
+
4
+ import pytest
5
+
6
+ from awspub import context, s3
7
+ from awspub.exceptions import BucketDoesNotExistException
8
+
9
+ curdir = pathlib.Path(__file__).parent.resolve()
10
+
11
+
12
+ @pytest.mark.parametrize(
13
+ "list_multipart_uploads_resp,create_multipart_upload_called",
14
+ [
15
+ # no available uploads - create one
16
+ ([], True),
17
+ # one available upload with non-matching key
18
+ ([{"UploadId": "abc", "Key": "does-not-match"}], True),
19
+ # multiple available upload with non-matching key
20
+ ([{"UploadId": "abc", "Key": "does-not-match"}, {"UploadId": "def", "Key": "does-not-match2"}], True),
21
+ # one available upload with matching key
22
+ ([{"UploadId": "abc", "Key": "6252475408b9f9ee64452b611d706a078831a99b123db69d144d878a0488a0a8"}], False),
23
+ # multiple available upload with one matching key
24
+ (
25
+ [
26
+ {"UploadId": "abc", "Key": "6252475408b9f9ee64452b611d706a078831a99b123db69d144d878a0488a0a8"},
27
+ {"UploadId": "abc", "Key": "does-not-match"},
28
+ ],
29
+ False,
30
+ ),
31
+ # multiple available upload with multiple matching keys
32
+ (
33
+ [
34
+ {"UploadId": "abc", "Key": "6252475408b9f9ee64452b611d706a078831a99b123db69d144d878a0488a0a8"},
35
+ {"UploadId": "def", "Key": "6252475408b9f9ee64452b611d706a078831a99b123db69d144d878a0488a0a8"},
36
+ ],
37
+ False,
38
+ ),
39
+ ],
40
+ )
41
+ def test_s3__get_multipart_upload_id(list_multipart_uploads_resp, create_multipart_upload_called):
42
+ """
43
+ test the _get_multipart_upload_id() function
44
+ """
45
+
46
+ with patch("boto3.client") as bclient_mock:
47
+ instance = bclient_mock.return_value
48
+ instance.list_multipart_uploads.return_value = {"Uploads": list_multipart_uploads_resp}
49
+ ctx = context.Context(curdir / "fixtures/config1.yaml", None)
50
+ sthree = s3.S3(ctx)
51
+ sthree._get_multipart_upload_id()
52
+ assert instance.create_multipart_upload.called == create_multipart_upload_called
53
+
54
+
55
+ @patch("awspub.s3.S3._bucket_exists", return_value=True)
56
+ @patch("awspub.s3.boto3")
57
+ def test_s3_bucket_region_bucket_exists(boto3_mock, bucket_exists_mock):
58
+ region_name = "sample-region-1"
59
+ head_bucket = {"BucketRegion": region_name}
60
+ boto3_mock.client.return_value.head_bucket.return_value = head_bucket
61
+ ctx = context.Context(curdir / "fixtures/config1.yaml", None)
62
+ sthree = s3.S3(ctx)
63
+
64
+ assert sthree.bucket_region == region_name
65
+
66
+
67
+ @patch("awspub.s3.S3._bucket_exists", return_value=False)
68
+ @patch("boto3.client")
69
+ def test_s3_bucket_region_bucket_not_exists(bclient_mock, bucket_exists_mock):
70
+ ctx = context.Context(curdir / "fixtures/config1.yaml", None)
71
+ sthree = s3.S3(ctx)
72
+
73
+ with pytest.raises(BucketDoesNotExistException):
74
+ sthree.bucket_region()