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.
@@ -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()