Skip to content

Commit

Permalink
new: Add rebooted field to the instance module (#355)
Browse files Browse the repository at this point in the history
* Add rebooted field to instance module

* Guard clause comes first

---------

Co-authored-by: Zhiwei Liang <121905282+zliang-akamai@users.noreply.github.com>
  • Loading branch information
lgarber-akamai and zliang-akamai authored May 19, 2023
1 parent 2668cf3 commit b4db280
Show file tree
Hide file tree
Showing 3 changed files with 118 additions and 9 deletions.
1 change: 1 addition & 0 deletions docs/modules/instance.md
Original file line number Diff line number Diff line change
Expand Up @@ -108,6 +108,7 @@ Manage Linode Instances, Configs, and Disks.
| `wait` | <center>`bool`</center> | <center>Optional</center> | Wait for the instance to have status "running" before returning. **(Default: `True`)** |
| `wait_timeout` | <center>`int`</center> | <center>Optional</center> | The amount of time, in seconds, to wait for an instance to have status "running". **(Default: `240`)** |
| [`additional_ipv4` (sub-options)](#additional_ipv4) | <center>`list`</center> | <center>Optional</center> | Additional ipv4 addresses to allocate. |
| `rebooted` | <center>`bool`</center> | <center>Optional</center> | If true, the Linode Instance will be rebooted. NOTE: The instance will only be rebooted if it was previously in a running state. To ensure your Linode will always be rebooted, consider also setting the `booted` field. **(Default: `False`)** |

### configs

Expand Down
71 changes: 62 additions & 9 deletions plugins/modules/instance.py
Original file line number Diff line number Diff line change
Expand Up @@ -386,6 +386,17 @@
description=["Additional ipv4 addresses to allocate."],
editable=False,
),
rebooted=SpecField(
type=FieldType.bool,
description=[
"If true, the Linode Instance will be rebooted.",
"NOTE: The instance will only be rebooted if it was "
"previously in a running state.",
"To ensure your Linode will always be rebooted, consider "
"also setting the `booted` field.",
],
default=False,
),
)

SPECDOC_META = SpecDocMeta(
Expand Down Expand Up @@ -831,7 +842,7 @@ def _update_instance(self) -> None:
if not hasattr(self._instance, key):
continue

if key in {"configs", "disks", "boot_config_label"}:
if key in {"configs", "disks", "boot_config_label", "reboot"}:
continue

old_value = parse_linode_types(getattr(self._instance, key))
Expand All @@ -857,15 +868,18 @@ def _update_instance(self) -> None:
if should_update:
self._instance.save()

ipv4_length = len(self.module.params.get("additional_ipv4") or [])
needs_private_ip = self.module.params.get("private_ip")
additional_ipv4 = self.module.params.get("additional_ipv4")

min_ips = 2 if self.module.params.get("private_ip") else 1
if ipv4_length != len(getattr(self._instance, "ipv4")) - min_ips:
self.fail(
"failed to update instance {0}:additional_ipv4 is a non-updatable field".format(
self._instance.label
if needs_private_ip or additional_ipv4:
ipv4_length = len(additional_ipv4 or [])

min_ips = 2 if needs_private_ip else 1
if ipv4_length != len(getattr(self._instance, "ipv4")) - min_ips:
self.fail(
"failed to update instance {0}: additional_ipv4 is a "
"non-updatable field".format(self._instance.label)
)
)

# Update interfaces
self._update_interfaces()
Expand Down Expand Up @@ -918,15 +932,50 @@ def _handle_instance_boot(self) -> None:
self._timeout_ctx.seconds_remaining
)

def _handle_instance_reboot(self) -> None:
if not self.module.params.get("rebooted"):
return

should_poll = self.module.params.get("wait")

# Wait for instance to not be busy
wait_for_resource_free(
self.client,
"linode",
self._instance.id,
self._timeout_ctx.seconds_remaining,
)

self._instance._api_get()

# We don't want to reboot if the Linode is already offline
if self._instance.status != "running":
return

reboot_poller = EventPoller(
self.client, "linode", "linode_reboot", entity_id=self._instance.id
)

self._instance.reboot()
self.register_action(
"Rebooted instance {}".format(self._instance.label)
)

if should_poll:
reboot_poller.wait_for_next_event_finished(
self._timeout_ctx.seconds_remaining
)

def _handle_present(self) -> None:
"""Updates the instance defined in kwargs"""

label = self.module.params.get("label")
should_wait = self.module.params.get("wait")

self._instance = self._get_instance_by_label(label)
already_exists = self._instance is not None

if self._instance is None:
if not already_exists:
create_poller = EventPoller(self.client, "linode", "linode_create")

result = self._create_instance()
Expand Down Expand Up @@ -967,6 +1016,10 @@ def _handle_present(self) -> None:
self._update_disks()
self._update_configs()

# Don't reboot on instance creation
if self.module.params.get("rebooted") is not None and already_exists:
self._handle_instance_reboot()

if self.module.params.get("booted") is not None:
self._handle_instance_boot()

Expand Down
55 changes: 55 additions & 0 deletions tests/integration/targets/instance_reboot/tasks/main.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
- name: instance_reboot
block:
- set_fact:
r: "{{ 1000000000 | random }}"

- name: Create a booted Linode instance
linode.cloud.instance:
label: 'ansible-test-{{ r }}'
region: us-southeast
type: g6-standard-1
image: linode/ubuntu20.04
root_pass: Fn$$oobar123
private_ip: true
rebooted: true
booted: true
state: present
register: create

- name: Assert instance created
assert:
that:
- create.changed
- create.instance.status == 'running'
- create.instance.ipv4|length > 1

- name: Reboot the instance
linode.cloud.instance:
label: '{{ create.instance.label }}'
rebooted: true
state: present
register: reboot

- name: Assert instance online
assert:
that:
- reboot.changed
- reboot.instance.status == 'running'
always:
- ignore_errors: yes
block:
- name: Delete a Linode instance
linode.cloud.instance:
label: '{{ create.instance.label }}'
state: absent
register: delete

- name: Assert instance delete succeeded
assert:
that:
- delete.changed
- delete.instance.id == create.instance.id

environment:
LINODE_UA_PREFIX: '{{ ua_prefix }}'
LINODE_API_TOKEN: '{{ api_token }}'

0 comments on commit b4db280

Please sign in to comment.