Skip to content

Commit e422b0a

Browse files
Merge pull request #480 from linode/dev
v5.25.0
2 parents 9c56e1e + 65b1ea5 commit e422b0a

File tree

20 files changed

+321
-281
lines changed

20 files changed

+321
-281
lines changed

linode_api4/objects/image.py

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -64,12 +64,8 @@ class Image(Base):
6464

6565
def replicate(self, regions: Union[List[str], List[Region]]):
6666
"""
67-
NOTE: Image replication may not currently be available to all users.
68-
6967
Replicate the image to other regions.
7068
71-
Note: Image replication may not currently be available to all users.
72-
7369
API Documentation: https://techdocs.akamai.com/linode-api/reference/post-replicate-image
7470
7571
:param regions: A list of regions that the customer wants to replicate this image in.

linode_api4/objects/placement.py

Lines changed: 22 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
from dataclasses import dataclass
2-
from typing import List, Union
2+
from typing import List, Optional, Union
33

44
from linode_api4.objects.base import Base, Property
55
from linode_api4.objects.linode import Instance
@@ -34,6 +34,26 @@ class PlacementGroupMember(JSONObject):
3434
is_compliant: bool = False
3535

3636

37+
@dataclass
38+
class MigratedInstance(JSONObject):
39+
"""
40+
The ID for a compute instance being migrated into or out of the placement group.
41+
"""
42+
43+
linode_id: int = 0
44+
45+
46+
@dataclass
47+
class PlacementGroupMigrations(JSONObject):
48+
"""
49+
Any compute instances that are being migrated to or from the placement group.
50+
Returns an empty object if no migrations are taking place.
51+
"""
52+
53+
inbound: Optional[List[MigratedInstance]] = None
54+
outbound: Optional[List[MigratedInstance]] = None
55+
56+
3757
class PlacementGroup(Base):
3858
"""
3959
NOTE: Placement Groups may not currently be available to all users.
@@ -54,6 +74,7 @@ class PlacementGroup(Base):
5474
"placement_group_policy": Property(),
5575
"is_compliant": Property(),
5676
"members": Property(json_object=PlacementGroupMember),
77+
"migrations": Property(json_object=PlacementGroupMigrations),
5778
}
5879

5980
def assign(

test/fixtures/placement_groups.json

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,19 @@
1212
"linode_id": 123,
1313
"is_compliant": true
1414
}
15-
]
15+
],
16+
"migrations": {
17+
"inbound": [
18+
{
19+
"linode_id": 123
20+
}
21+
],
22+
"outbound": [
23+
{
24+
"linode_id": 456
25+
}
26+
]
27+
}
1628
}
1729
],
1830
"page": 1,

test/fixtures/placement_groups_123.json

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,5 +10,17 @@
1010
"linode_id": 123,
1111
"is_compliant": true
1212
}
13-
]
13+
],
14+
"migrations": {
15+
"inbound": [
16+
{
17+
"linode_id": 123
18+
}
19+
],
20+
"outbound": [
21+
{
22+
"linode_id": 456
23+
}
24+
]
25+
}
1426
}

test/integration/conftest.py

Lines changed: 35 additions & 74 deletions
Original file line numberDiff line numberDiff line change
@@ -2,13 +2,17 @@
22
import os
33
import random
44
import time
5+
from test.integration.helpers import (
6+
get_test_label,
7+
send_request_when_resource_available,
8+
)
59
from typing import Optional, Set
610

711
import pytest
812
import requests
913
from requests.exceptions import ConnectionError, RequestException
1014

11-
from linode_api4 import ApiError, PlacementGroupPolicy, PlacementGroupType
15+
from linode_api4 import PlacementGroupPolicy, PlacementGroupType
1216
from linode_api4.linode_client import LinodeClient
1317
from linode_api4.objects import Region
1418

@@ -27,13 +31,6 @@ def get_api_url():
2731
return os.environ.get(ENV_API_URL_NAME, "https://api.linode.com/v4beta")
2832

2933

30-
def get_random_label():
31-
timestamp = str(time.time_ns())[:-5]
32-
label = "label_" + timestamp
33-
34-
return label
35-
36-
3734
def get_regions(
3835
client: LinodeClient,
3936
capabilities: Optional[Set[str]] = None,
@@ -59,7 +56,7 @@ def get_regions(
5956

6057

6158
def get_region(
62-
client: LinodeClient, capabilities: Set[str] = None, site_type: str = None
59+
client: LinodeClient, capabilities: Set[str] = None, site_type: str = "core"
6360
):
6461
return random.choice(get_regions(client, capabilities, site_type))
6562

@@ -161,14 +158,12 @@ def create_inbound_rule(ipv4_address, ipv6_address):
161158
def create_linode(test_linode_client, e2e_test_firewall):
162159
client = test_linode_client
163160

164-
available_regions = client.regions()
165-
chosen_region = available_regions[4]
166-
timestamp = str(time.time_ns())
167-
label = "TestSDK-" + timestamp
161+
region = get_region(client, {"Linodes", "Cloud Firewall"}, site_type="core")
162+
label = get_test_label(length=8)
168163

169164
linode_instance, password = client.linode.instance_create(
170165
"g6-nanode-1",
171-
chosen_region,
166+
region,
172167
image="linode/debian12",
173168
label=label,
174169
firewall=e2e_test_firewall,
@@ -183,14 +178,12 @@ def create_linode(test_linode_client, e2e_test_firewall):
183178
def create_linode_for_pass_reset(test_linode_client, e2e_test_firewall):
184179
client = test_linode_client
185180

186-
available_regions = client.regions()
187-
chosen_region = available_regions[4]
188-
timestamp = str(time.time_ns())
189-
label = "TestSDK-" + timestamp
181+
region = get_region(client, {"Linodes", "Cloud Firewall"}, site_type="core")
182+
label = get_test_label(length=8)
190183

191184
linode_instance, password = client.linode.instance_create(
192185
"g6-nanode-1",
193-
chosen_region,
186+
region,
194187
image="linode/debian10",
195188
label=label,
196189
firewall=e2e_test_firewall,
@@ -271,65 +264,36 @@ def test_domain(test_linode_client):
271264
@pytest.fixture(scope="session")
272265
def test_volume(test_linode_client):
273266
client = test_linode_client
274-
timestamp = str(time.time_ns())
275-
region = client.regions()[4]
276-
label = "TestSDK-" + timestamp
267+
region = get_region(client, {"Linodes", "Cloud Firewall"}, site_type="core")
268+
label = get_test_label(length=8)
277269

278270
volume = client.volume_create(label=label, region=region)
279271

280272
yield volume
281273

282-
timeout = 100 # give 100s for volume to be detached before deletion
283-
284-
start_time = time.time()
285-
286-
while time.time() - start_time < timeout:
287-
try:
288-
res = volume.delete()
289-
if res:
290-
break
291-
else:
292-
time.sleep(3)
293-
except ApiError as e:
294-
if time.time() - start_time > timeout:
295-
raise e
274+
send_request_when_resource_available(timeout=100, func=volume.delete)
296275

297276

298277
@pytest.fixture(scope="session")
299278
def test_volume_with_encryption(test_linode_client):
300279
client = test_linode_client
301-
timestamp = str(time.time_ns())
302280
region = get_region(client, {"Block Storage Encryption"})
303-
label = "TestSDK-" + timestamp
281+
label = get_test_label(length=8)
304282

305283
volume = client.volume_create(
306284
label=label, region=region, encryption="enabled"
307285
)
308286

309287
yield volume
310288

311-
timeout = 100 # give 100s for volume to be detached before deletion
312-
313-
start_time = time.time()
314-
315-
while time.time() - start_time < timeout:
316-
try:
317-
res = volume.delete()
318-
if res:
319-
break
320-
else:
321-
time.sleep(3)
322-
except ApiError as e:
323-
if time.time() - start_time > timeout:
324-
raise e
289+
send_request_when_resource_available(timeout=100, func=volume.delete)
325290

326291

327292
@pytest.fixture
328293
def test_tag(test_linode_client):
329294
client = test_linode_client
330295

331-
timestamp = str(time.time_ns())
332-
label = "TestSDK-" + timestamp
296+
label = get_test_label(length=8)
333297

334298
tag = client.tag_create(label=label)
335299

@@ -342,11 +306,10 @@ def test_tag(test_linode_client):
342306
def test_nodebalancer(test_linode_client):
343307
client = test_linode_client
344308

345-
timestamp = str(time.time_ns())
346-
label = "TestSDK-" + timestamp
309+
label = get_test_label(length=8)
347310

348311
nodebalancer = client.nodebalancer_create(
349-
region=get_region(client), label=label
312+
region=get_region(client, capabilities={"NodeBalancers"}), label=label
350313
)
351314

352315
yield nodebalancer
@@ -357,8 +320,7 @@ def test_nodebalancer(test_linode_client):
357320
@pytest.fixture
358321
def test_longview_client(test_linode_client):
359322
client = test_linode_client
360-
timestamp = str(time.time_ns())
361-
label = "TestSDK-" + timestamp
323+
label = get_test_label(length=8)
362324
longview_client = client.longview.client_create(label=label)
363325

364326
yield longview_client
@@ -370,7 +332,8 @@ def test_longview_client(test_linode_client):
370332
def test_sshkey(test_linode_client, ssh_key_gen):
371333
pub_key = ssh_key_gen[0]
372334
client = test_linode_client
373-
key = client.profile.ssh_key_upload(pub_key, "IntTestSDK-sshkey")
335+
key_label = get_test_label(8) + "_key"
336+
key = client.profile.ssh_key_upload(pub_key, key_label)
374337

375338
yield key
376339

@@ -380,7 +343,7 @@ def test_sshkey(test_linode_client, ssh_key_gen):
380343
@pytest.fixture
381344
def access_keys_object_storage(test_linode_client):
382345
client = test_linode_client
383-
label = "TestSDK-obj-storage-key"
346+
label = get_test_label(length=8)
384347
key = client.object_storage.keys_create(label)
385348

386349
yield key
@@ -398,8 +361,7 @@ def test_firewall(test_linode_client):
398361
"inbound_policy": "ACCEPT",
399362
}
400363

401-
timestamp = str(time.time_ns())
402-
label = "firewall_" + timestamp
364+
label = get_test_label(8) + "_firewall"
403365

404366
firewall = client.networking.firewall_create(
405367
label=label, rules=rules, status="enabled"
@@ -413,7 +375,7 @@ def test_firewall(test_linode_client):
413375
@pytest.fixture
414376
def test_oauth_client(test_linode_client):
415377
client = test_linode_client
416-
label = get_random_label() + "_oauth"
378+
label = get_test_label(length=8) + "_oauth"
417379

418380
oauth_client = client.account.oauth_client_create(
419381
label, "https://localhost/oauth/callback"
@@ -428,10 +390,10 @@ def test_oauth_client(test_linode_client):
428390
def create_vpc(test_linode_client):
429391
client = test_linode_client
430392

431-
timestamp = str(int(time.time()))
393+
label = get_test_label(length=10)
432394

433395
vpc = client.vpcs.create(
434-
"pythonsdk-" + timestamp,
396+
label,
435397
get_region(test_linode_client, {"VPCs"}),
436398
description="test description",
437399
)
@@ -455,8 +417,7 @@ def create_vpc_with_subnet_and_linode(
455417
):
456418
vpc, subnet = create_vpc_with_subnet
457419

458-
timestamp = str(int(time.time()))
459-
label = "TestSDK-" + timestamp
420+
label = get_test_label(length=8)
460421

461422
instance, password = test_linode_client.linode.instance_create(
462423
"g6-standard-1",
@@ -475,18 +436,18 @@ def create_vpc_with_subnet_and_linode(
475436
def create_multiple_vpcs(test_linode_client):
476437
client = test_linode_client
477438

478-
timestamp = str(int(time.time_ns() % 10**10))
439+
label = get_test_label(length=10)
479440

480-
timestamp_2 = str(int(time.time_ns() % 10**10))
441+
label_2 = get_test_label(length=10)
481442

482443
vpc_1 = client.vpcs.create(
483-
"pythonsdk-" + timestamp,
444+
label,
484445
get_region(test_linode_client, {"VPCs"}),
485446
description="test description",
486447
)
487448

488449
vpc_2 = client.vpcs.create(
489-
"pythonsdk-" + timestamp_2,
450+
label_2,
490451
get_region(test_linode_client, {"VPCs"}),
491452
description="test description",
492453
)
@@ -502,10 +463,10 @@ def create_multiple_vpcs(test_linode_client):
502463
def create_placement_group(test_linode_client):
503464
client = test_linode_client
504465

505-
timestamp = str(int(time.time()))
466+
label = get_test_label(10)
506467

507468
pg = client.placement.group_create(
508-
"pythonsdk-" + timestamp,
469+
label,
509470
get_region(test_linode_client, {"Placement Group"}),
510471
PlacementGroupType.anti_affinity_local,
511472
PlacementGroupPolicy.flexible,

0 commit comments

Comments
 (0)