Skip to content

Commit a29b93a

Browse files
Implemented changes for SLADE and CLEO projects
1 parent d18b54e commit a29b93a

File tree

5 files changed

+132
-62
lines changed

5 files changed

+132
-62
lines changed

linode_api4/groups/linode.py

Lines changed: 32 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -140,6 +140,7 @@ def instance_create(
140140
region,
141141
image=None,
142142
authorized_keys=None,
143+
root_pass=None,
143144
firewall: Optional[Union[Firewall, int]] = None,
144145
backup: Optional[Union[Backup, int]] = None,
145146
stackscript: Optional[Union[StackScript, int]] = None,
@@ -162,6 +163,8 @@ def instance_create(
162163
interface_generation: Optional[Union[InterfaceGeneration, str]] = None,
163164
network_helper: Optional[bool] = None,
164165
maintenance_policy: Optional[str] = None,
166+
kernel: Optional[str] = None,
167+
boot_size: Optional[int] = None,
165168
**kwargs,
166169
):
167170
"""
@@ -280,10 +283,18 @@ def instance_create(
280283
:type ltype: str or Type
281284
:param region: The Region in which we are creating the Instance
282285
:type region: str or Region
283-
:param image: The Image to deploy to this Instance. If this is provided
284-
and no root_pass is given, a password will be generated
285-
and returned along with the new Instance.
286+
:param image: The Image to deploy to this Instance.
286287
:type image: str or Image
288+
:param authorized_keys: The ssh public keys to install in the linode's
289+
/root/.ssh/authorized_keys file. Each entry may
290+
be a single key, or a path to a file containing
291+
the key. At least one of authorized_keys or
292+
root_pass is required.
293+
:type authorized_keys: list or str
294+
:param root_pass: The root password for the new Instance. At least one
295+
of root_pass or authorized_keys is required. Both may
296+
be provided.
297+
:type root_pass: str
287298
:param stackscript: The StackScript to deploy to the new Instance. If
288299
provided, "image" is required and must be compatible
289300
with the chosen StackScript.
@@ -295,11 +306,6 @@ def instance_create(
295306
:param backup: The Backup to restore to the new Instance. May not be
296307
provided if "image" is given.
297308
:type backup: int of Backup
298-
:param authorized_keys: The ssh public keys to install in the linode's
299-
/root/.ssh/authorized_keys file. Each entry may
300-
be a single key, or a path to a file containing
301-
the key.
302-
:type authorized_keys: list or str
303309
:param label: The display label for the new Instance
304310
:type label: str
305311
:param group: The display group for the new Instance
@@ -336,26 +342,33 @@ def instance_create(
336342
:param maintenance_policy: The slug of the maintenance policy to apply during maintenance.
337343
If not provided, the default policy (linode/migrate) will be applied.
338344
:type maintenance_policy: str
339-
340-
:returns: A new Instance object, or a tuple containing the new Instance and
341-
the generated password.
342-
:rtype: Instance or tuple(Instance, str)
345+
:param kernel: The kernel to boot the Instance with.
346+
:type kernel: str
347+
:param boot_size: The size, in MB, of the boot disk to create for this
348+
Instance.
349+
:type boot_size: int
350+
351+
:returns: A new Instance object.
352+
:rtype: Instance
353+
:raises ValueError: If neither root_pass nor authorized_keys is provided.
343354
:raises ApiError: If contacting the API fails
344355
:raises UnexpectedResponseError: If the API response is somehow malformed.
345356
This usually indicates that you are using
346357
an outdated library.
347358
"""
348359

349-
ret_pass = None
350-
if image and not "root_pass" in kwargs:
351-
ret_pass = Instance.generate_root_password()
352-
kwargs["root_pass"] = ret_pass
360+
# Validation: require at least one of root_pass or authorized_keys
361+
if not root_pass and not authorized_keys:
362+
raise ValueError(
363+
"At least one of root_pass or authorized_keys is required."
364+
)
353365

354366
params = {
355367
"type": ltype,
356368
"region": region,
357369
"image": image,
358370
"authorized_keys": load_and_validate_keys(authorized_keys),
371+
"root_pass": root_pass,
359372
# These will automatically be flattened below
360373
"firewall_id": firewall,
361374
"backup_id": backup,
@@ -373,6 +386,8 @@ def instance_create(
373386
"interfaces": interfaces,
374387
"interface_generation": interface_generation,
375388
"network_helper": network_helper,
389+
"kernel": kernel,
390+
"boot_size": boot_size,
376391
}
377392

378393
params.update(kwargs)
@@ -388,9 +403,7 @@ def instance_create(
388403
)
389404

390405
l = Instance(self.client, result["id"], result)
391-
if not ret_pass:
392-
return l
393-
return l, ret_pass
406+
return l
394407

395408
@staticmethod
396409
def build_instance_metadata(user_data=None, encode_user_data=True):

linode_api4/objects/linode.py

Lines changed: 25 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -1397,10 +1397,11 @@ def disk_create(
13971397
:param read_only: If True, creates a read-only disk
13981398
:param image: The Image to deploy to the disk.
13991399
:param root_pass: The password to configure for the root user when deploying an
1400-
image to this disk. Not used if image is not given. If an
1401-
image is given and root_pass is not, a password will be
1402-
generated and returned alongside the new disk.
1400+
image to this disk. At least one of root_pass or authorized_keys
1401+
is required. Both may be provided.
1402+
:type root_pass: str
14031403
:param authorized_keys: A list of SSH keys to install as trusted for the root user.
1404+
At least one of authorized_keys or root_pass is required.
14041405
:param authorized_users: A list of usernames whose keys should be installed
14051406
as trusted for the root user. These user's keys
14061407
should already be set up, see :any:`ProfileGroup.ssh_keys`
@@ -1412,12 +1413,15 @@ def disk_create(
14121413
disk. Requires deploying a compatible image.
14131414
:param **stackscript_args: Any arguments to pass to the StackScript, as defined
14141415
by its User Defined Fields.
1416+
1417+
:returns: The new Disk.
1418+
:rtype: Disk
14151419
"""
14161420

1417-
gen_pass = None
1418-
if image and not root_pass:
1419-
gen_pass = Instance.generate_root_password()
1420-
root_pass = gen_pass
1421+
if not root_pass and not authorized_keys:
1422+
raise ValueError(
1423+
"At least one of root_pass or authorized_keys is required."
1424+
)
14211425

14221426
authorized_keys = load_and_validate_keys(authorized_keys)
14231427

@@ -1436,19 +1440,13 @@ def disk_create(
14361440
"authorized_keys": authorized_keys,
14371441
"authorized_users": authorized_users,
14381442
"stackscript_id": stackscript,
1443+
"image": image,
1444+
"root_pass": root_pass,
14391445
}
14401446

14411447
if disk_encryption is not None:
14421448
params["disk_encryption"] = str(disk_encryption)
14431449

1444-
if image:
1445-
params.update(
1446-
{
1447-
"image": image,
1448-
"root_pass": root_pass,
1449-
}
1450-
)
1451-
14521450
if stackscript_args:
14531451
params["stackscript_data"] = stackscript_args
14541452

@@ -1466,8 +1464,6 @@ def disk_create(
14661464

14671465
d = Disk(self._client, result["id"], self.id, result)
14681466

1469-
if gen_pass:
1470-
return d, gen_pass
14711467
return d
14721468

14731469
def enable_backups(self):
@@ -1591,26 +1587,27 @@ def rebuild(
15911587
15921588
:param image: The Image to deploy to this Instance
15931589
:type image: str or Image
1594-
:param root_pass: The root password for the newly rebuilt Instance. If
1595-
omitted, a password will be generated and returned.
1590+
:param root_pass: The root password for the newly rebuilt Instance.
1591+
At least one of root_pass or authorized_keys is
1592+
required. Both may be provided.
15961593
:type root_pass: str
15971594
:param authorized_keys: The ssh public keys to install in the linode's
15981595
/root/.ssh/authorized_keys file. Each entry may
15991596
be a single key, or a path to a file containing
1600-
the key.
1597+
the key. At least one of authorized_keys or
1598+
root_pass is required.
16011599
:type authorized_keys: list or str
16021600
:param disk_encryption: The disk encryption policy for this Linode.
16031601
NOTE: Disk encryption may not currently be available to all users.
16041602
:type disk_encryption: InstanceDiskEncryptionType or str
16051603
1606-
:returns: The newly generated password, if one was not provided
1607-
(otherwise True)
1608-
:rtype: str or bool
1604+
:returns: True if the rebuild was successful.
1605+
:rtype: bool
16091606
"""
1610-
ret_pass = None
1611-
if not root_pass:
1612-
ret_pass = Instance.generate_root_password()
1613-
root_pass = ret_pass
1607+
if not root_pass and not authorized_keys:
1608+
raise ValueError(
1609+
"At least one of root_pass or authorized_keys is required."
1610+
)
16141611

16151612
authorized_keys = load_and_validate_keys(authorized_keys)
16161613

@@ -1639,10 +1636,7 @@ def rebuild(
16391636
# update ourself with the newly-returned information
16401637
self._populate(result)
16411638

1642-
if not ret_pass:
1643-
return True
1644-
else:
1645-
return ret_pass
1639+
return True
16461640

16471641
def rescue(self, *disks):
16481642
"""

test/unit/groups/linode_test.py

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ def test_instance_create_with_user_data(self):
2323
self.client.linode.instance_create(
2424
"g6-nanode-1",
2525
"us-southeast",
26+
root_pass="test123ABC!",
2627
metadata=self.client.linode.build_instance_metadata(
2728
user_data="cool"
2829
),
@@ -33,6 +34,7 @@ def test_instance_create_with_user_data(self):
3334
{
3435
"region": "us-southeast",
3536
"type": "g6-nanode-1",
37+
"root_pass": "test123ABC!",
3638
"metadata": {"user_data": "Y29vbA=="},
3739
},
3840
)
@@ -51,6 +53,7 @@ def test_instance_create_with_interfaces_legacy(self):
5153
self.client.linode.instance_create(
5254
"us-southeast",
5355
"g6-nanode-1",
56+
root_pass="test123ABC!",
5457
interface_generation=InterfaceGeneration.LEGACY_CONFIG,
5558
interfaces=interfaces,
5659
)
@@ -92,6 +95,7 @@ def test_create_with_placement_group(self):
9295
self.client.linode.instance_create(
9396
"g6-nanode-1",
9497
"eu-west",
98+
root_pass="test123ABC!",
9599
placement_group=InstancePlacementGroupAssignment(
96100
id=123,
97101
compliant_only=True,
@@ -117,13 +121,15 @@ def test_instance_create_with_interfaces_linode(self):
117121
self.client.linode.instance_create(
118122
"g6-nanode-1",
119123
"us-mia",
124+
root_pass="test123ABC!",
120125
interface_generation=InterfaceGeneration.LINODE,
121126
interfaces=interfaces,
122127
)
123128

124129
assert m.call_data == {
125130
"region": "us-mia",
126131
"type": "g6-nanode-1",
132+
"root_pass": "test123ABC!",
127133
"interface_generation": "linode",
128134
"interfaces": [iface._serialize() for iface in interfaces],
129135
}
@@ -137,11 +143,59 @@ def test_create_with_maintenance_policy(self):
137143
self.client.linode.instance_create(
138144
"g6-nanode-1",
139145
"eu-west",
146+
root_pass="test123ABC!",
140147
maintenance_policy="linode/migrate",
141148
)
142149

143150
self.assertEqual(m.call_data["maintenance_policy"], "linode/migrate")
144151

152+
def test_instance_create_with_kernel(self):
153+
"""
154+
Tests that you can create a Linode with a kernel
155+
"""
156+
157+
with self.mock_post("linode/instances/123") as m:
158+
self.client.linode.instance_create(
159+
"g6-nanode-1",
160+
"eu-west",
161+
root_pass="test123ABC!",
162+
kernel="linode/grub2",
163+
)
164+
165+
self.assertEqual(m.call_data["kernel"], "linode/grub2")
166+
167+
def test_instance_create_with_boot_size(self):
168+
"""
169+
Tests that you can create a Linode with a boot_size
170+
"""
171+
172+
with self.mock_post("linode/instances/123") as m:
173+
self.client.linode.instance_create(
174+
"g6-nanode-1",
175+
"eu-west",
176+
root_pass="test123ABC!",
177+
boot_size=8192,
178+
)
179+
180+
self.assertEqual(m.call_data["boot_size"], 8192)
181+
182+
def test_instance_create_with_kernel_and_boot_size(self):
183+
"""
184+
Tests that you can create a Linode with both kernel and boot_size
185+
"""
186+
187+
with self.mock_post("linode/instances/123") as m:
188+
self.client.linode.instance_create(
189+
"g6-nanode-1",
190+
"eu-west",
191+
root_pass="test123ABC!",
192+
kernel="linode/grub2",
193+
boot_size=8192,
194+
)
195+
196+
self.assertEqual(m.call_data["kernel"], "linode/grub2")
197+
self.assertEqual(m.call_data["boot_size"], 8192)
198+
145199

146200
class TypeTest(ClientBaseCase):
147201
def test_get_types(self):

test/unit/linode_client_test.py

Lines changed: 14 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -672,7 +672,7 @@ def test_instance_create(self):
672672
"""
673673
with self.mock_post("linode/instances/123") as m:
674674
l = self.client.linode.instance_create(
675-
"g6-standard-1", "us-east-1a"
675+
"g6-standard-1", "us-east-1a", root_pass="test123ABC!"
676676
)
677677

678678
self.assertIsNotNone(l)
@@ -681,16 +681,24 @@ def test_instance_create(self):
681681
self.assertEqual(m.call_url, "/linode/instances")
682682

683683
self.assertEqual(
684-
m.call_data, {"region": "us-east-1a", "type": "g6-standard-1"}
684+
m.call_data,
685+
{
686+
"region": "us-east-1a",
687+
"type": "g6-standard-1",
688+
"root_pass": "test123ABC!",
689+
},
685690
)
686691

687692
def test_instance_create_with_image(self):
688693
"""
689-
Tests that a Linode Instance can be created with an image, and a password generated
694+
Tests that a Linode Instance can be created with an image and root_pass
690695
"""
691696
with self.mock_post("linode/instances/123") as m:
692-
l, pw = self.client.linode.instance_create(
693-
"g6-standard-1", "us-east-1a", image="linode/debian9"
697+
l = self.client.linode.instance_create(
698+
"g6-standard-1",
699+
"us-east-1a",
700+
image="linode/debian9",
701+
root_pass="test123ABC!",
694702
)
695703

696704
self.assertIsNotNone(l)
@@ -704,7 +712,7 @@ def test_instance_create_with_image(self):
704712
"region": "us-east-1a",
705713
"type": "g6-standard-1",
706714
"image": "linode/debian9",
707-
"root_pass": pw,
715+
"root_pass": "test123ABC!",
708716
},
709717
)
710718

0 commit comments

Comments
 (0)