Skip to content

Commit

Permalink
Merge pull request #458 from linode/dev
Browse files Browse the repository at this point in the history
Release v0.24.0
  • Loading branch information
zliang-akamai authored Jan 9, 2024
2 parents 9aba768 + e8d08ec commit 6cc68dd
Show file tree
Hide file tree
Showing 55 changed files with 486 additions and 165 deletions.
2 changes: 1 addition & 1 deletion .github/workflows/integration-tests-pr.yml
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ on:
inputs:
tests:
description: 'The tests to run.'
required: true
required: false
sha:
description: 'The hash value of the commit.'
required: true
Expand Down
1 change: 1 addition & 0 deletions .github/workflows/integration-tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -46,3 +46,4 @@ jobs:
run: make testall
env:
LINODE_API_TOKEN: ${{ secrets.DX_LINODE_TOKEN }}

2 changes: 2 additions & 0 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,8 @@ gendocs:
ansible-doc-extractor --template=template/module.rst.j2 $(DOCS_PATH)/inventory plugins/inventory/*.py
python scripts/render_readme.py $(COLLECTION_VERSION)

# if want to add all the test add the tag --tags never at the end
# ansible-test integration $(TEST_ARGS) --tags never
integration-test: create-integration-config
ansible-test integration $(TEST_ARGS)

Expand Down
2 changes: 1 addition & 1 deletion docs/modules/account_availability_info.md
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ Get info about a Linode Account Availability.

| Field | Type | Required | Description |
|-----------|------|----------|------------------------------------------------------------------------------|
| `region` | <center>`str`</center> | <center>Optional</center> | The Region of the Account Availability to resolve. |
| `region` | <center>`str`</center> | <center>**Required**</center> | The Region of the Account Availability to resolve. |

## Return Values

Expand Down
5 changes: 4 additions & 1 deletion docs/modules/instance.md
Original file line number Diff line number Diff line change
Expand Up @@ -124,9 +124,12 @@ Manage Linode Instances, Configs, and Disks.
| [`metadata` (sub-options)](#metadata) | <center>`dict`</center> | <center>Optional</center> | Fields relating to the Linode Metadata service. |
| `backups_enabled` | <center>`bool`</center> | <center>Optional</center> | Enroll Instance in Linode Backup service. |
| `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`)** |
| `wait_timeout` | <center>`int`</center> | <center>Optional</center> | The amount of time, in seconds, to wait for an instance to have status "running". **(Default: `1500`)** |
| [`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`)** |
| `migration_type` | <center>`str`</center> | <center>Optional</center> | The type of migration to use for Region and Type migrations. **(Choices: `cold`, `warm`; Default: `cold`)** |
| `auto_disk_resize` | <center>`bool`</center> | <center>Optional</center> | Whether implicitly created disks should be resized during a type change operation. **(Default: `False`)** |
| `tags` | <center>`list`</center> | <center>Optional</center> | An array of tags applied to this object. Tags are for organizational purposes only. **(Updatable)** |

### configs

Expand Down
4 changes: 2 additions & 2 deletions docs/modules/instance_info.md
Original file line number Diff line number Diff line change
Expand Up @@ -25,8 +25,8 @@ Get info about a Linode Instance.
| Field | Type | Required | Description |
|-----------|------|----------|------------------------------------------------------------------------------|
| `label` | <center>`str`</center> | <center>Optional</center> | The label of the Instance to resolve. |
| `id` | <center>`int`</center> | <center>Optional</center> | The ID of the Instance to resolve. |
| `label` | <center>`str`</center> | <center>Optional</center> | The label of the Instance to resolve. **(Conflicts With: `id`)** |
| `id` | <center>`int`</center> | <center>Optional</center> | The ID of the Instance to resolve. **(Conflicts With: `label`)** |

## Return Values

Expand Down
4 changes: 2 additions & 2 deletions docs/modules/stackscript_info.md
Original file line number Diff line number Diff line change
Expand Up @@ -25,8 +25,8 @@ Get info about a Linode StackScript.
| Field | Type | Required | Description |
|-----------|------|----------|------------------------------------------------------------------------------|
| `label` | <center>`str`</center> | <center>Optional</center> | The label of the StackScript to resolve. |
| `id` | <center>`int`</center> | <center>Optional</center> | The ID of the StackScript to resolve. |
| `label` | <center>`str`</center> | <center>Optional</center> | The label of the StackScript to resolve. **(Conflicts With: `id`)** |
| `id` | <center>`int`</center> | <center>Optional</center> | The ID of the StackScript to resolve. **(Conflicts With: `label`)** |

## Return Values

Expand Down
2 changes: 1 addition & 1 deletion docs/modules/type_info.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ Get info about a Linode Type.
| Field | Type | Required | Description |
|-----------|------|----------|------------------------------------------------------------------------------|
| `id` | <center>`str`</center> | <center>Optional</center> | The ID of the Type to resolve. |
| `id` | <center>`str`</center> | <center>**Required**</center> | The ID of the Type to resolve. |

## Return Values

Expand Down
4 changes: 2 additions & 2 deletions docs/modules/vpc_info.md
Original file line number Diff line number Diff line change
Expand Up @@ -25,8 +25,8 @@ Get info about a Linode VPC.
| Field | Type | Required | Description |
|-----------|------|----------|------------------------------------------------------------------------------|
| `label` | <center>`str`</center> | <center>Optional</center> | The label of the VPC to resolve. |
| `id` | <center>`int`</center> | <center>Optional</center> | The ID of the VPC to resolve. |
| `label` | <center>`str`</center> | <center>Optional</center> | The label of the VPC to resolve. **(Conflicts With: `id`)** |
| `id` | <center>`int`</center> | <center>Optional</center> | The ID of the VPC to resolve. **(Conflicts With: `label`)** |

## Return Values

Expand Down
4 changes: 2 additions & 2 deletions docs/modules/vpc_subnet_info.md
Original file line number Diff line number Diff line change
Expand Up @@ -28,8 +28,8 @@ Get info about a Linode VPC Subnet.
| Field | Type | Required | Description |
|-----------|------|----------|------------------------------------------------------------------------------|
| `vpc_id` | <center>`int`</center> | <center>**Required**</center> | The ID of the VPC for this resource. |
| `label` | <center>`str`</center> | <center>Optional</center> | The label of the VPC Subnet to resolve. |
| `id` | <center>`int`</center> | <center>Optional</center> | The ID of the VPC Subnet to resolve. |
| `label` | <center>`str`</center> | <center>Optional</center> | The label of the VPC Subnet to resolve. **(Conflicts With: `id`)** |
| `id` | <center>`int`</center> | <center>Optional</center> | The ID of the VPC Subnet to resolve. **(Conflicts With: `label`)** |

## Return Values

Expand Down
4 changes: 4 additions & 0 deletions plugins/module_utils/linode_common_info.py
Original file line number Diff line number Diff line change
Expand Up @@ -176,6 +176,10 @@ def spec(self):
for attr in self.attributes:
options[attr.name] = SpecField(
type=attr.type,
required=len(self.attributes) == 1,
conflicts_with=[
v.name for v in self.attributes if v.name != attr.name
],
description=f"The {attr.display_name} of the "
f"{self.primary_result.display_name} to resolve.",
)
Expand Down
142 changes: 141 additions & 1 deletion plugins/modules/instance.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,8 @@
from typing import Any, Dict, List, Optional, Union, cast

import ansible_collections.linode.cloud.plugins.module_utils.doc_fragments.instance as docs
import linode_api4
import polling
from ansible_collections.linode.cloud.plugins.module_utils.linode_common import (
LinodeModuleBase,
)
Expand All @@ -23,6 +25,7 @@
filter_null_values_recursive,
paginated_list_to_json,
parse_linode_types,
poll_condition,
)
from ansible_specdoc.objects import (
FieldType,
Expand Down Expand Up @@ -447,7 +450,7 @@
),
"wait_timeout": SpecField(
type=FieldType.integer,
default=240,
default=1500,
description=[
"The amount of time, in seconds, to wait for an instance to "
'have status "running".'
Expand All @@ -471,6 +474,30 @@
],
default=False,
),
"migration_type": SpecField(
type=FieldType.string,
description=[
"The type of migration to use for Region and Type migrations."
],
choices=["cold", "warm"],
default="cold",
),
"auto_disk_resize": SpecField(
type=FieldType.bool,
description=[
"Whether implicitly created disks should be resized during a type change operation."
],
default=False,
),
"tags": SpecField(
type=FieldType.list,
element_type=FieldType.string,
description=[
"An array of tags applied to this object.",
"Tags are for organizational purposes only.",
],
editable=True,
),
}

SPECDOC_META = SpecDocMeta(
Expand Down Expand Up @@ -864,6 +891,111 @@ def _update_firewall(self) -> None:
"and 'firewall_device' modules."
)

def _wait_for_instance_status(self, status: str) -> None:
def poll_func() -> bool:
self._instance.invalidate()
return self._instance.status == status

try:
poll_condition(poll_func, 4, self._timeout_ctx.seconds_remaining)
except polling.TimeoutException:
self.fail(
f"failed to wait for instance to reach status {status}: timeout period expired"
)

def _update_type(self) -> None:
"""
Handles updates on the type field.
"""

new_type = self.module.params.get("type")
auto_disk_resize = self.module.params.get("auto_disk_resize")
migration_type = self.module.params.get("migration_type")

# Graceful handling for a potential edge case
# where the type is stored as a string rather than
# an instance of the Type class.
current_type = self._instance.type
if isinstance(current_type, linode_api4.Type):
current_type = current_type.id

previously_booted = self._instance.status == "running"

if new_type is None or new_type == current_type:
return

resize_poller = self.client.polling.event_poller_create(
"linode", "linode_resize", entity_id=self._instance.id
)

self.client.polling.wait_for_entity_free(
"linode",
self._instance.id,
timeout=self._timeout_ctx.seconds_remaining,
)

self._instance.resize(
new_type=new_type,
allow_auto_disk_resize=auto_disk_resize,
migration_type=migration_type,
)

self.register_action(
f"Resized instance from type {self._instance.type.id} to {new_type}"
)

resize_poller.wait_for_next_event_finished(
timeout=self._timeout_ctx.seconds_remaining
)

# The boot process for the instance is handled implicitly by the resize operation,
# so we wait for the instance to reach running status if necessary.
if previously_booted:
self._wait_for_instance_status("running")

def _update_region(self) -> None:
"""
Handles updates on the region field.
"""

new_region = self.module.params.get("region")
migration_type = self.module.params.get("migration_type")

# Graceful handling for a potential edge case
# where the region is stored as a string rather than
# an instance of the Region class.
current_region = self._instance.region
if isinstance(current_region, linode_api4.Region):
current_region = current_region.id

if new_region is None or new_region == current_region:
return

migration_poller = self.client.polling.event_poller_create(
"linode", "linode_migrate_datacenter", entity_id=self._instance.id
)

self.client.polling.wait_for_entity_free(
"linode",
self._instance.id,
timeout=self._timeout_ctx.seconds_remaining,
)

# TODO: Include type change in request if necessary
# so only one migration needs to be run.
self._instance.initiate_migration(
region=new_region,
migration_type=migration_type,
)

self.register_action(
f"Migrated Instance from {self._instance.region.id} to {new_region}"
)

migration_poller.wait_for_next_event_finished(
timeout=self._timeout_ctx.seconds_remaining
)

def _update_config(
self, config: Config, config_params: Dict[str, Any]
) -> None:
Expand Down Expand Up @@ -1037,6 +1169,8 @@ def _update_instance(self) -> None:
"boot_config_label",
"reboot",
"backups_enabled",
"type",
"region",
):
continue

Expand Down Expand Up @@ -1094,6 +1228,12 @@ def _update_instance(self) -> None:
# Handle updating on the target Firewall ID
self._update_firewall()

# Handle migrating the instance if necessary
self._update_region()

# Handle updating the instance type
self._update_type()

def _handle_instance_boot(self) -> None:
boot_status = self.module.params.get("booted")
should_poll = self.module.params.get("wait")
Expand Down
46 changes: 24 additions & 22 deletions plugins/modules/object_cluster_info.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,16 +17,13 @@
global_authors,
global_requirements,
)
from ansible_collections.linode.cloud.plugins.module_utils.linode_helper import (
create_filter_and,
)
from ansible_specdoc.objects import (
FieldType,
SpecDocMeta,
SpecField,
SpecReturnValue,
)
from linode_api4 import ObjectStorageCluster
from linode_api4 import ObjectStorageCluster, Region

linode_object_cluster_info_spec = {
# We need to overwrite attributes to exclude them as requirements
Expand Down Expand Up @@ -70,7 +67,7 @@
},
)

linode_object_cluster_valid_filters = [
FILTERABLE_FIELDS = [
"id",
"region",
"domain",
Expand All @@ -91,26 +88,31 @@ def __init__(self) -> None:
required_one_of=self.required_one_of,
)

def _get_matching_cluster(self) -> Optional[List[ObjectStorageCluster]]:
filter_items = {
k: v
for k, v in self.module.params.items()
if k in linode_object_cluster_valid_filters and v is not None
}
def _cluster_matches_filter(self, cluster: ObjectStorageCluster) -> bool:
for k in FILTERABLE_FIELDS:
user_input = self.module.params.get(k)
if user_input is None:
continue

filter_statement = create_filter_and(ObjectStorageCluster, filter_items)
cluster_value = getattr(cluster, k)

try:
# Special case because ID is not filterable
if "id" in filter_items.keys():
result = ObjectStorageCluster(
self.client, self.module.params.get("id")
)
result._api_get() # Force lazy-loading
# This is necessary because regions are populated as
# Region objects by linode_api4.
if isinstance(cluster_value, Region):
cluster_value = cluster_value.id

return [result]
if cluster_value != user_input:
return False

return self.client.object_storage.clusters(filter_statement)
return True

def _get_matching_clusters(self) -> Optional[List[ObjectStorageCluster]]:
try:
return [
v
for v in self.client.object_storage.clusters()
if self._cluster_matches_filter(v)
]
except IndexError:
return None
except Exception as exception:
Expand All @@ -119,7 +121,7 @@ def _get_matching_cluster(self) -> Optional[List[ObjectStorageCluster]]:
def exec_module(self, **kwargs: Any) -> Optional[dict]:
"""Constructs and calls the Linode Object Storage Clusters module"""

clusters = self._get_matching_cluster()
clusters = self._get_matching_clusters()

if clusters is None:
return self.fail("failed to get clusters")
Expand Down
1 change: 1 addition & 0 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,7 @@ disable = [
"missing-timeout",
"use-sequence-for-iteration",
"broad-exception-raised",
"fixme"
]
py-version = "3.8"
extension-pkg-whitelist = "math"
Loading

0 comments on commit 6cc68dd

Please sign in to comment.