Skip to content

Commit ead501b

Browse files
wip
1 parent d18b54e commit ead501b

3 files changed

Lines changed: 43 additions & 24 deletions

File tree

linode_api4/groups/linode.py

Lines changed: 22 additions & 18 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,
@@ -280,10 +281,18 @@ def instance_create(
280281
:type ltype: str or Type
281282
:param region: The Region in which we are creating the Instance
282283
: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.
284+
:param image: The Image to deploy to this Instance.
286285
:type image: str or Image
286+
:param authorized_keys: The ssh public keys to install in the linode's
287+
/root/.ssh/authorized_keys file. Each entry may
288+
be a single key, or a path to a file containing
289+
the key. At least one of authorized_keys or
290+
root_pass is required.
291+
:type authorized_keys: list or str
292+
:param root_pass: The root password for the new Instance. At least one
293+
of root_pass or authorized_keys is required. Both may
294+
be provided.
295+
:type root_pass: str
287296
:param stackscript: The StackScript to deploy to the new Instance. If
288297
provided, "image" is required and must be compatible
289298
with the chosen StackScript.
@@ -295,11 +304,6 @@ def instance_create(
295304
:param backup: The Backup to restore to the new Instance. May not be
296305
provided if "image" is given.
297306
: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
303307
:param label: The display label for the new Instance
304308
:type label: str
305309
:param group: The display group for the new Instance
@@ -337,25 +341,27 @@ def instance_create(
337341
If not provided, the default policy (linode/migrate) will be applied.
338342
:type maintenance_policy: str
339343
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)
344+
:returns: A new Instance object.
345+
:rtype: Instance
346+
:raises ValueError: If neither root_pass nor authorized_keys is provided.
343347
:raises ApiError: If contacting the API fails
344348
:raises UnexpectedResponseError: If the API response is somehow malformed.
345349
This usually indicates that you are using
346350
an outdated library.
347351
"""
348352

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
353+
# Validation: require at least one of root_pass or authorized_keys
354+
if not root_pass and not authorized_keys:
355+
raise ValueError(
356+
"At least one of root_pass or authorized_keys is required."
357+
)
353358

354359
params = {
355360
"type": ltype,
356361
"region": region,
357362
"image": image,
358363
"authorized_keys": load_and_validate_keys(authorized_keys),
364+
"root_pass": root_pass,
359365
# These will automatically be flattened below
360366
"firewall_id": firewall,
361367
"backup_id": backup,
@@ -388,9 +394,7 @@ def instance_create(
388394
)
389395

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

395399
@staticmethod
396400
def build_instance_metadata(user_data=None, encode_user_data=True):

test/unit/groups/linode_test.py

Lines changed: 7 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,6 +143,7 @@ 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

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)