Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

new: Add rebooted field to the instance module #355

Merged
merged 3 commits into from
May 19, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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 }}'