diff --git a/Makefile b/Makefile index 9b80db77..8515b6aa 100644 --- a/Makefile +++ b/Makefile @@ -121,6 +121,11 @@ else echo "LINODE_API_TOKEN must be set"; \ exit 1; endif + # Add producer/consumer tokens, may be empty + @echo "producer_api_token: $${LINODE_PRODUCER_API_TOKEN:-}" >> $(INTEGRATION_CONFIG) + @echo "consumer_api_token: $${LINODE_CONSUMER_API_TOKEN:-}" >> $(INTEGRATION_CONFIG) + + # Common settings @echo "ua_prefix: E2E" >> $(INTEGRATION_CONFIG) @echo "api_url: $$(url=$${LINODE_API_URL:-$${TEST_API_URL:-https://api.linode.com}}; echo $${url%/}/)" >> $(INTEGRATION_CONFIG) @echo "api_version: $${LINODE_API_VERSION:-$${TEST_API_VERSION:-v4beta}}" >> $(INTEGRATION_CONFIG) diff --git a/README.md b/README.md index 321eba22..49f6afc2 100644 --- a/README.md +++ b/README.md @@ -33,6 +33,9 @@ Name | Description | [linode.cloud.firewall_device](./docs/modules/firewall_device.md)|Manage Linode Firewall Devices.| [linode.cloud.firewall_settings](./docs/modules/firewall_settings.md)|Configure the firewall settings for the account.| [linode.cloud.image](./docs/modules/image.md)|Manage a Linode Image.| +[linode.cloud.image_share_group](./docs/modules/image_share_group.md)|Manage an Image Share Group.| +[linode.cloud.image_share_group_member](./docs/modules/image_share_group_member.md)|Manage an Image Share Group Member.| +[linode.cloud.image_share_group_token](./docs/modules/image_share_group_token.md)|Manage an Image Share Group Token.| [linode.cloud.instance](./docs/modules/instance.md)|Manage Linode Instances, Configs, and Disks.| [linode.cloud.instance_interface_settings](./docs/modules/instance_interface_settings.md)|Create, read, and update the interface settings for a Linode instance.| [linode.cloud.ip](./docs/modules/ip.md)|Allocates a new IPv4 Address on your Account. The Linode must be configured to support additional addresses - please Open a support ticket requesting additional addresses before attempting allocation.| @@ -66,6 +69,7 @@ Name | Description | [linode.cloud.account_availability_info](./docs/modules/account_availability_info.md)|Get info about a Linode Account Availability.| [linode.cloud.account_info](./docs/modules/account_info.md)|Get info about a Linode Account.| [linode.cloud.child_account_info](./docs/modules/child_account_info.md)|Get info about a Linode Child Account.| +[linode.cloud.consumer_image_share_group_info](./docs/modules/consumer_image_share_group_info.md)|Get info about a Linode Image Share Group.| [linode.cloud.database_config_info](./docs/modules/database_config_info.md)|Get info about a Linode Configuration.| [linode.cloud.database_mysql_info](./docs/modules/database_mysql_info.md)|Get info about a Linode MySQL Managed Database.| [linode.cloud.database_postgresql_info](./docs/modules/database_postgresql_info.md)|Get info about a Linode PostgreSQL Managed Database.| @@ -75,6 +79,9 @@ Name | Description | [linode.cloud.firewall_settings_info](./docs/modules/firewall_settings_info.md)|Get info about a Linode Firewall Settings.| [linode.cloud.firewall_template_info](./docs/modules/firewall_template_info.md)|Get info about a Linode Firewall Template.| [linode.cloud.image_info](./docs/modules/image_info.md)|Get info about a Linode Image.| +[linode.cloud.image_share_group_info](./docs/modules/image_share_group_info.md)|Get info about a Linode Image Share Group.| +[linode.cloud.image_share_group_member_info](./docs/modules/image_share_group_member_info.md)|Get info about a Linode Image Share Group Member.| +[linode.cloud.image_share_group_token_info](./docs/modules/image_share_group_token_info.md)|Get info about a Linode Image Share Group Token.| [linode.cloud.instance_info](./docs/modules/instance_info.md)|Get info about a Linode Instance.| [linode.cloud.instance_interface_settings_info](./docs/modules/instance_interface_settings_info.md)|Get the interface settings for a Linode instance.| [linode.cloud.ip_info](./docs/modules/ip_info.md)|Get info about a Linode IP.| @@ -107,6 +114,7 @@ Name | Description | --- | ------------ | [linode.cloud.account_availability_list](./docs/modules/account_availability_list.md)|List and filter on Account Availabilities.| [linode.cloud.child_account_list](./docs/modules/child_account_list.md)|List and filter on Child Account.| +[linode.cloud.consumer_image_share_group_image_list](./docs/modules/consumer_image_share_group_image_list.md)|List and filter on Image Share Group Images.| [linode.cloud.database_engine_list](./docs/modules/database_engine_list.md)|List and filter on Managed Database engine types.| [linode.cloud.database_list](./docs/modules/database_list.md)|List and filter on Linode Managed Databases.| [linode.cloud.domain_list](./docs/modules/domain_list.md)|List and filter on Domains.| @@ -114,6 +122,10 @@ Name | Description | [linode.cloud.firewall_list](./docs/modules/firewall_list.md)|List and filter on Firewalls.| [linode.cloud.firewall_template_list](./docs/modules/firewall_template_list.md)|List and filter on Firewall Templates.| [linode.cloud.image_list](./docs/modules/image_list.md)|List and filter on Images.| +[linode.cloud.image_share_group_image_list](./docs/modules/image_share_group_image_list.md)|List and filter on Image Share Group Images.| +[linode.cloud.image_share_group_list](./docs/modules/image_share_group_list.md)|List and filter on Image Share Groups.| +[linode.cloud.image_share_group_member_list](./docs/modules/image_share_group_member_list.md)|List and filter on Image Share Group Members.| +[linode.cloud.image_share_group_token_list](./docs/modules/image_share_group_token_list.md)|List and filter on Image Share Group Tokens.| [linode.cloud.instance_interface_firewall_list](./docs/modules/instance_interface_firewall_list.md)|List and filter on Linode Interface Firewalls.| [linode.cloud.instance_list](./docs/modules/instance_list.md)|List and filter on Instances.| [linode.cloud.instance_type_list](./docs/modules/instance_type_list.md)|**NOTE: This module has been deprecated in favor of `type_list`.**| diff --git a/docs/modules/consumer_image_share_group_image_list.md b/docs/modules/consumer_image_share_group_image_list.md new file mode 100644 index 00000000..565763b0 --- /dev/null +++ b/docs/modules/consumer_image_share_group_image_list.md @@ -0,0 +1,93 @@ +# consumer_image_share_group_image_list + +List and filter on Image Share Group Images. + +- [Minimum Required Fields](#minimum-required-fields) +- [Examples](#examples) +- [Parameters](#parameters) +- [Return Values](#return-values) + +## Minimum Required Fields +| Field | Type | Required | Description | +|-------------|-------|--------------|--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| +| `api_token` | `str` | **Required** | The Linode account personal access token. It is necessary to run the module.
It can be exposed by the environment variable `LINODE_API_TOKEN` instead.
See details in [Usage](https://github.com/linode/ansible_linode?tab=readme-ov-file#usage). | + +## Examples + +```yaml +- name: List all of the Image Share Group Images for the specified Token UUID + linode.cloud.consumer_image_share_group_image_list: + token_uuid: "9e64b99e-92f7-4c7b-a616-8f622fffb94c" +``` + + +## Parameters + +| Field | Type | Required | Description | +|-----------|------|----------|------------------------------------------------------------------------------| +| `token_uuid` |
`str`
|
**Required**
| The parent Token for the Image Share Group Images. | +| `order` |
`str`
|
Optional
| The order to list Image Share Group Images in. **(Choices: `desc`, `asc`; Default: `asc`)** | +| `order_by` |
`str`
|
Optional
| The attribute to order Image Share Group Images by. | +| [`filters` (sub-options)](#filters) |
`list`
|
Optional
| A list of filters to apply to the resulting Image Share Group Images. | +| `count` |
`int`
|
Optional
| The number of Image Share Group Images to return. If undefined, all results will be returned. | + +### filters + +| Field | Type | Required | Description | +|-----------|------|----------|------------------------------------------------------------------------------| +| `name` |
`str`
|
**Required**
| The name of the field to filter on. Valid filterable fields can be found [here](https://techdocs.akamai.com/linode-api/reference/get-sharegroup-images-by-token). | +| `values` |
`list`
|
**Required**
| A list of values to allow for this field. Fields will pass this filter if at least one of these values matches. | + +## Return Values + +- `image_share_group_images` - The returned Image Share Group Images. + + - Sample Response: + ```json + [ + { + "capabilities": [ + "cloud-init", + "distributed-sites" + ], + "created": "2025-08-04T10:07:59", + "created_by": null, + "deprecated": true, + "description": "Official Debian Linux image for server deployment", + "eol": "2025-12-31T18:13:44.756Z", + "expiry": "2025-12-31T18:13:44.756Z", + "id": "shared/1", + "image_sharing": { + "shared_by": { + "sharegroup_id": 123, + "sharegroup_label": "DevOps Base Images", + "sharegroup_uuid": "8d64b99e-92f7-4c7b-a616-8f622fffb94c", + "source_image_id": "private/15" + }, + "shared_with": null + }, + "is_public": true, + "is_shared": "none", + "label": "Linux Debian", + "regions": [ + { + "region": "us-iad", + "status": "available" + } + ], + "size": 256, + "status": "available", + "tags": [ + "repair-image", + "fix-1" + ], + "total_size": 256, + "type": "shared", + "updated": null, + "vendor": "string" + } + ] + ``` + - See the [Linode API response documentation](https://techdocs.akamai.com/linode-api/reference/get-sharegroup-images-by-token) for a list of returned fields + + diff --git a/docs/modules/consumer_image_share_group_info.md b/docs/modules/consumer_image_share_group_info.md new file mode 100644 index 00000000..404e6531 --- /dev/null +++ b/docs/modules/consumer_image_share_group_info.md @@ -0,0 +1,48 @@ +# consumer_image_share_group_info + +Get info about a Linode Image Share Group. + +- [Minimum Required Fields](#minimum-required-fields) +- [Examples](#examples) +- [Parameters](#parameters) +- [Return Values](#return-values) + +## Minimum Required Fields +| Field | Type | Required | Description | +|-------------|-------|--------------|--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| +| `api_token` | `str` | **Required** | The Linode account personal access token. It is necessary to run the module.
It can be exposed by the environment variable `LINODE_API_TOKEN` instead.
See details in [Usage](https://github.com/linode/ansible_linode?tab=readme-ov-file#usage). | + +## Examples + +```yaml +- name: Get info about an Image Share Group by a Consumer's Token UUID + linode.cloud.consumer_image_share_group_info: + token_uuid: "1433863e-16a4-47b5-b829-ac0f35c13278" +``` + + +## Parameters + +| Field | Type | Required | Description | +|-----------|------|----------|------------------------------------------------------------------------------| +| `token_uuid` |
`str`
|
**Required**
| The ID of the Token for this resource. | + +## Return Values + +- `image_share_group` - The returned Image Share Group. + + - Sample Response: + ```json + { + "created": "2025-04-14T22:44:02", + "description": "Group of base operating system images and engineers used for CI/CD pipelines and infrastructure automation", + "id": 1, + "is_suspended": false, + "label": "DevOps Base Images", + "updated": "2025-04-14T22:44:03", + "uuid": "1533863e-16a4-47b5-b829-ac0f35c13278" + } + ``` + - See the [Linode API response documentation](https://techdocs.akamai.com/linode-api/reference/get-sharegroup-by-token) for a list of returned fields + + diff --git a/docs/modules/image.md b/docs/modules/image.md index d16b3835..ea9aa386 100644 --- a/docs/modules/image.md +++ b/docs/modules/image.md @@ -83,33 +83,43 @@ Manage a Linode Image. - Sample Response: ```json { - "capabilities": [], + "capabilities": [ + "cloud-init", + "distributed-sites" + ], "created": "2021-08-14T22:44:02", - "created_by": "my-account", + "created_by": "linode", "deprecated": false, - "description": "Example Image description.", + "description": "Example image description.", "eol": "2026-07-01T04:00:00", "expiry": null, - "id": "private/123", - "is_public": true, - "label": "my-image", - "size": 2500, - "status": null, - "type": "manual", - "updated": "2021-08-14T22:44:02", - "vendor": "Debian", - "tags": ["test"], - "total_size": 5000, + "id": "private/15", + "image_sharing": { + "shared_by": null, + "shared_with": { + "sharegroup_count": 0, + "sharegroup_list_url": "/images/private/15/sharegroups" + } + }, + "is_public": false, + "is_shared": false, + "label": "Debian 11", "regions": [ { - "region": "us-east", - "status": "available" - }, - { - "region": "us-central", - "status": "pending" + "region": "us-iad", + "status": "available" } - ] + ], + "size": 2500, + "status": "available", + "tags": [ + "repair-image", + "fix-1" + ], + "total_size": 1234567, + "type": "manual", + "updated": "2021-08-14T22:44:02", + "vendor": "Debian" } ``` - See the [Linode API response documentation](https://techdocs.akamai.com/linode-api/reference/get-image) for a list of returned fields diff --git a/docs/modules/image_info.md b/docs/modules/image_info.md index abf339b4..0fdf84d9 100644 --- a/docs/modules/image_info.md +++ b/docs/modules/image_info.md @@ -41,33 +41,43 @@ Get info about a Linode Image. - Sample Response: ```json { - "capabilities": [], + "capabilities": [ + "cloud-init", + "distributed-sites" + ], "created": "2021-08-14T22:44:02", - "created_by": "my-account", + "created_by": "linode", "deprecated": false, - "description": "Example Image description.", + "description": "Example image description.", "eol": "2026-07-01T04:00:00", "expiry": null, - "id": "private/123", - "is_public": true, - "label": "my-image", - "size": 2500, - "status": null, - "type": "manual", - "updated": "2021-08-14T22:44:02", - "vendor": "Debian", - "tags": ["test"], - "total_size": 5000, + "id": "private/15", + "image_sharing": { + "shared_by": null, + "shared_with": { + "sharegroup_count": 0, + "sharegroup_list_url": "/images/private/15/sharegroups" + } + }, + "is_public": false, + "is_shared": false, + "label": "Debian 11", "regions": [ { - "region": "us-east", - "status": "available" - }, - { - "region": "us-central", - "status": "pending" + "region": "us-iad", + "status": "available" } - ] + ], + "size": 2500, + "status": "available", + "tags": [ + "repair-image", + "fix-1" + ], + "total_size": 1234567, + "type": "manual", + "updated": "2021-08-14T22:44:02", + "vendor": "Debian" } ``` - See the [Linode API response documentation](https://techdocs.akamai.com/linode-api/reference/get-image) for a list of returned fields diff --git a/docs/modules/image_list.md b/docs/modules/image_list.md index 03345cf8..9f67eac6 100644 --- a/docs/modules/image_list.md +++ b/docs/modules/image_list.md @@ -59,33 +59,45 @@ List and filter on Images. - Sample Response: ```json [ - { - "created":"2021-08-14T22:44:02", - "created_by":"my-account", - "deprecated":false, - "description":"Example Image description.", - "eol":"2026-07-01T04:00:00", - "expiry":null, - "id":"private/123", - "is_public":false, - "label":"test", - "size":2500, - "status":null, - "type":"manual", - "updated":"2021-08-14T22:44:02", - "vendor":"Debian", - "tags": ["test"], - "total_size": 5000, + { + "capabilities": [ + "cloud-init", + "distributed-sites" + ], + "created": "2021-08-14T22:44:02", + "created_by": "linode", + "deprecated": false, + "description": "Example image description.", + "eol": "2026-07-01T04:00:00", + "expiry": null, + "id": "private/15", + "image_sharing": { + "shared_by": null, + "shared_with": { + "sharegroup_count": 0, + "sharegroup_list_url": "/images/private/15/sharegroups" + } + }, + "is_public": false, + "is_shared": false, + "label": "Debian 11", "regions": [ { - "region": "us-east", - "status": "available" - }, - { - "region": "us-central", - "status": "pending" - }] - } + "region": "us-iad", + "status": "available" + } + ], + "size": 2500, + "status": "available", + "tags": [ + "repair-image", + "fix-1" + ], + "total_size": 1234567, + "type": "manual", + "updated": "2021-08-14T22:44:02", + "vendor": "Debian" + } ] ``` - See the [Linode API response documentation](https://techdocs.akamai.com/linode-api/reference/get-images) for a list of returned fields diff --git a/docs/modules/image_share_group.md b/docs/modules/image_share_group.md new file mode 100644 index 00000000..3ffc598d --- /dev/null +++ b/docs/modules/image_share_group.md @@ -0,0 +1,84 @@ +# image_share_group + +Manage an Image Share Group. + +- [Minimum Required Fields](#minimum-required-fields) +- [Examples](#examples) +- [Parameters](#parameters) +- [Return Values](#return-values) + +## Minimum Required Fields +| Field | Type | Required | Description | +|-------------|-------|--------------|--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| +| `api_token` | `str` | **Required** | The Linode account personal access token. It is necessary to run the module.
It can be exposed by the environment variable `LINODE_API_TOKEN` instead.
See details in [Usage](https://github.com/linode/ansible_linode?tab=readme-ov-file#usage). | + +## Examples + +```yaml +- name: Create a basic image share group + linode.cloud.image_share_group: + label: "my-sharegroup" + description: "My image share group." + state: present +``` + +```yaml +- name: Create a basic image share group with an image + linode.cloud.image_share_group: + label: "my-sharegroup" + description: "My image share group." + images: + - id: "private/123" + label: "My shared image" + description: "An image shared in the group." + state: present + +``` + +```yaml +- name: Delete an image share group + linode.cloud.image_share_group: + label: "my-sharegroup" + state: absent +``` + + +## Parameters + +| Field | Type | Required | Description | +|-----------|------|----------|------------------------------------------------------------------------------| +| `label` |
`str`
|
**Required**
| This Image Share Group's unique label. | +| `state` |
`str`
|
**Required**
| The desired state of the target. **(Choices: `present`, `absent`)** | +| `description` |
`str`
|
Optional
| A description of this Image Share Group. | +| [`images` (sub-options)](#images) |
`list`
|
Optional
| A list of images to include in this Image Share Group. | + +### images + +| Field | Type | Required | Description | +|-----------|------|----------|------------------------------------------------------------------------------| +| `id` |
`str`
|
**Required**
| The id of the Private Image to include in an Image Share Group. | +| `label` |
`str`
|
Optional
| A label to assign to the Image within the context of an Image Share Group. | +| `description` |
`str`
|
Optional
| A description to assign to the Image within the context of an Image Share Group. | + +## Return Values + +- `image_share_group` - The Image Share Group in JSON serialized form. + + - Sample Response: + ```json + { + "created": "2025-04-14T22:44:02", + "description": "My image share group.", + "expiry": null, + "id": 1, + "images_count": 0, + "is_suspended": false, + "label": "my-sharegroup", + "members_count": 0, + "updated": null, + "uuid": "1533863e-16a4-47b5-b829-ac0f35c13278" + } + ``` + - See the [Linode API response documentation](https://techdocs.akamai.com/linode-api/reference/get-sharegroup) for a list of returned fields + + diff --git a/docs/modules/image_share_group_image_list.md b/docs/modules/image_share_group_image_list.md new file mode 100644 index 00000000..7cd80de2 --- /dev/null +++ b/docs/modules/image_share_group_image_list.md @@ -0,0 +1,93 @@ +# image_share_group_image_list + +List and filter on Image Share Group Images. + +- [Minimum Required Fields](#minimum-required-fields) +- [Examples](#examples) +- [Parameters](#parameters) +- [Return Values](#return-values) + +## Minimum Required Fields +| Field | Type | Required | Description | +|-------------|-------|--------------|--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| +| `api_token` | `str` | **Required** | The Linode account personal access token. It is necessary to run the module.
It can be exposed by the environment variable `LINODE_API_TOKEN` instead.
See details in [Usage](https://github.com/linode/ansible_linode?tab=readme-ov-file#usage). | + +## Examples + +```yaml +- name: List all of the Image Share Group Images for the specified Share Group + linode.cloud.image_share_group_image_list: + sharegroup_id: 123 +``` + + +## Parameters + +| Field | Type | Required | Description | +|-----------|------|----------|------------------------------------------------------------------------------| +| `sharegroup_id` |
`int`
|
**Required**
| The parent Image Share Group for the Image Share Group Images. | +| `order` |
`str`
|
Optional
| The order to list Image Share Group Images in. **(Choices: `desc`, `asc`; Default: `asc`)** | +| `order_by` |
`str`
|
Optional
| The attribute to order Image Share Group Images by. | +| [`filters` (sub-options)](#filters) |
`list`
|
Optional
| A list of filters to apply to the resulting Image Share Group Images. | +| `count` |
`int`
|
Optional
| The number of Image Share Group Images to return. If undefined, all results will be returned. | + +### filters + +| Field | Type | Required | Description | +|-----------|------|----------|------------------------------------------------------------------------------| +| `name` |
`str`
|
**Required**
| The name of the field to filter on. Valid filterable fields can be found [here](https://techdocs.akamai.com/linode-api/reference/get-sharegroup-images). | +| `values` |
`list`
|
**Required**
| A list of values to allow for this field. Fields will pass this filter if at least one of these values matches. | + +## Return Values + +- `image_share_group_images` - The returned Image Share Group Images. + + - Sample Response: + ```json + [ + { + "capabilities": [ + "cloud-init", + "distributed-sites" + ], + "created": "2025-08-04T10:07:59", + "created_by": null, + "deprecated": true, + "description": "Official Debian Linux image for server deployment", + "eol": "2025-12-31T18:13:44.756Z", + "expiry": "2025-12-31T18:13:44.756Z", + "id": "shared/1", + "image_sharing": { + "shared_by": { + "sharegroup_id": 123, + "sharegroup_label": "DevOps Base Images", + "sharegroup_uuid": "8d64b99e-92f7-4c7b-a616-8f622fffb94c", + "source_image_id": "private/15" + }, + "shared_with": null + }, + "is_public": true, + "is_shared": "none", + "label": "Linux Debian", + "regions": [ + { + "region": "us-iad", + "status": "available" + } + ], + "size": 256, + "status": "available", + "tags": [ + "repair-image", + "fix-1" + ], + "total_size": 256, + "type": "shared", + "updated": null, + "vendor": "string" + } + ] + ``` + - See the [Linode API response documentation](https://techdocs.akamai.com/linode-api/reference/get-sharegroup-images) for a list of returned fields + + diff --git a/docs/modules/image_share_group_info.md b/docs/modules/image_share_group_info.md new file mode 100644 index 00000000..7ee3b691 --- /dev/null +++ b/docs/modules/image_share_group_info.md @@ -0,0 +1,52 @@ +# image_share_group_info + +Get info about a Linode Image Share Group. + +- [Minimum Required Fields](#minimum-required-fields) +- [Examples](#examples) +- [Parameters](#parameters) +- [Return Values](#return-values) + +## Minimum Required Fields +| Field | Type | Required | Description | +|-------------|-------|--------------|--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| +| `api_token` | `str` | **Required** | The Linode account personal access token. It is necessary to run the module.
It can be exposed by the environment variable `LINODE_API_TOKEN` instead.
See details in [Usage](https://github.com/linode/ansible_linode?tab=readme-ov-file#usage). | + +## Examples + +```yaml +- name: Get info about an Image Share Group by label + linode.cloud.image_share_group_info: + label: example-image-share-group +``` + + +## Parameters + +| Field | Type | Required | Description | +|-----------|------|----------|------------------------------------------------------------------------------| +| `id` |
`int`
|
Optional
| The ID of the Image Share Group to resolve. **(Conflicts With: `label`)** | +| `label` |
`str`
|
Optional
| The label of the Image Share Group to resolve. **(Conflicts With: `id`)** | + +## Return Values + +- `image_share_group` - The returned Image Share Group. + + - Sample Response: + ```json + { + "created": "2025-04-14T22:44:02", + "description": "Example.", + "expiry": "2025-04-14T22:44:02", + "id": 1, + "images_count": 0, + "is_suspended": false, + "label": "example-image-share-group", + "members_count": 0, + "updated": "2025-04-14T22:44:02", + "uuid": "1533863e-16a4-47b5-b829-ac0f35c13278" + } + ``` + - See the [Linode API response documentation](https://techdocs.akamai.com/linode-api/reference/get-sharegroup) for a list of returned fields + + diff --git a/docs/modules/image_share_group_list.md b/docs/modules/image_share_group_list.md new file mode 100644 index 00000000..62f9dbd2 --- /dev/null +++ b/docs/modules/image_share_group_list.md @@ -0,0 +1,69 @@ +# image_share_group_list + +List and filter on Image Share Groups. + +- [Minimum Required Fields](#minimum-required-fields) +- [Examples](#examples) +- [Parameters](#parameters) +- [Return Values](#return-values) + +## Minimum Required Fields +| Field | Type | Required | Description | +|-------------|-------|--------------|--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| +| `api_token` | `str` | **Required** | The Linode account personal access token. It is necessary to run the module.
It can be exposed by the environment variable `LINODE_API_TOKEN` instead.
See details in [Usage](https://github.com/linode/ansible_linode?tab=readme-ov-file#usage). | + +## Examples + +```yaml +- name: List all of the Image Share Groups for the current Linode Account + linode.cloud.image_share_group_list: {} +``` + +```yaml +- name: List all of the Image Share Groups that contain a specific private image + linode.cloud.image_share_group_list: + image_id: "private/12345" +``` + + +## Parameters + +| Field | Type | Required | Description | +|-----------|------|----------|------------------------------------------------------------------------------| +| `order` |
`str`
|
Optional
| The order to list Image Share Groups in. **(Choices: `desc`, `asc`; Default: `asc`)** | +| `order_by` |
`str`
|
Optional
| The attribute to order Image Share Groups by. | +| [`filters` (sub-options)](#filters) |
`list`
|
Optional
| A list of filters to apply to the resulting Image Share Groups. | +| `count` |
`int`
|
Optional
| The number of Image Share Groups to return. If undefined, all results will be returned. | +| `image_id` |
`str`
|
Optional
| Specifies the private image ID to list share groups for. If provided, only share groups containing the specified image will be returned. | + +### filters + +| Field | Type | Required | Description | +|-----------|------|----------|------------------------------------------------------------------------------| +| `name` |
`str`
|
**Required**
| The name of the field to filter on. Valid filterable fields can be found [here](https://techdocs.akamai.com/linode-api/reference/get-sharegroups). | +| `values` |
`list`
|
**Required**
| A list of values to allow for this field. Fields will pass this filter if at least one of these values matches. | + +## Return Values + +- `image_share_groups` - The returned Image Share Groups. + + - Sample Response: + ```json + [ + { + "created": "2025-04-14T22:44:02", + "description": "Group of base operating system images and engineers used for CI/CD pipelines and infrastructure automation", + "expiry": null, + "id": 1, + "images_count": 0, + "is_suspended": false, + "label": "DevOps Base Images", + "members_count": 0, + "updated": null, + "uuid": "1533863e-16a4-47b5-b829-ac0f35c13278" + } + ] + ``` + - See the [Linode API response documentation](https://techdocs.akamai.com/linode-api/reference/get-sharegroups) for a list of returned fields + + diff --git a/docs/modules/image_share_group_member.md b/docs/modules/image_share_group_member.md new file mode 100644 index 00000000..0ae7287c --- /dev/null +++ b/docs/modules/image_share_group_member.md @@ -0,0 +1,60 @@ +# image_share_group_member + +Manage an Image Share Group Member. + +- [Minimum Required Fields](#minimum-required-fields) +- [Examples](#examples) +- [Parameters](#parameters) +- [Return Values](#return-values) + +## Minimum Required Fields +| Field | Type | Required | Description | +|-------------|-------|--------------|--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| +| `api_token` | `str` | **Required** | The Linode account personal access token. It is necessary to run the module.
It can be exposed by the environment variable `LINODE_API_TOKEN` instead.
See details in [Usage](https://github.com/linode/ansible_linode?tab=readme-ov-file#usage). | + +## Examples + +```yaml +- name: Create an image share group member + linode.cloud.image_share_group_member: + label: "my-sharegroup-member" + token: "abcdefghijklmnopqrstuvwxyz1234567890" + state: present +``` + +```yaml +- name: Delete an image share group member + linode.cloud.image_share_group_member: + label: "my-sharegroup-member" + state: absent +``` + + +## Parameters + +| Field | Type | Required | Description | +|-----------|------|----------|------------------------------------------------------------------------------| +| `label` |
`str`
|
**Required**
| This Image Share Group Member's unique label. | +| `sharegroup_id` |
`int`
|
**Required**
| The ID of the Image Share Group this member belongs to. | +| `state` |
`str`
|
**Required**
| The desired state of the target. **(Choices: `present`, `absent`)** | +| `token` |
`str`
|
Optional
| A single-use Image Share Group Token provided by the Consumer. This value is required when creating a member and is never returned. | + +## Return Values + +- `image_share_group_member` - The Image Share Group Member in JSON serialized form. + + - Sample Response: + ```json + { + "token_uuid": "24wef-243qg-45wgg-q343q", + "status": "active", + "label": "my-sharegroup-member", + "created": "2016-03-16T17:30:49", + "updated": "2016-03-18T17:30:49", + "expiry": "2016-03-18T17:30:49" + } + + ``` + - See the [Linode API response documentation](https://techdocs.akamai.com/linode-api/reference/get-sharegroup-member-token) for a list of returned fields + + diff --git a/docs/modules/image_share_group_member_info.md b/docs/modules/image_share_group_member_info.md new file mode 100644 index 00000000..832a14b4 --- /dev/null +++ b/docs/modules/image_share_group_member_info.md @@ -0,0 +1,50 @@ +# image_share_group_member_info + +Get info about a Linode Image Share Group Member. + +- [Minimum Required Fields](#minimum-required-fields) +- [Examples](#examples) +- [Parameters](#parameters) +- [Return Values](#return-values) + +## Minimum Required Fields +| Field | Type | Required | Description | +|-------------|-------|--------------|--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| +| `api_token` | `str` | **Required** | The Linode account personal access token. It is necessary to run the module.
It can be exposed by the environment variable `LINODE_API_TOKEN` instead.
See details in [Usage](https://github.com/linode/ansible_linode?tab=readme-ov-file#usage). | + +## Examples + +```yaml +- name: Get info about an Image Share Group Member by label + linode.cloud.image_share_group_member_info: + sharegroup_id: 123456 + label: example-image-share-group-member +``` + + +## Parameters + +| Field | Type | Required | Description | +|-----------|------|----------|------------------------------------------------------------------------------| +| `sharegroup_id` |
`int`
|
**Required**
| The ID of the Image Share Group for this resource. | +| `token_uuid` |
`str`
|
Optional
| The Token UUID of the Image Share Group Member to resolve. **(Conflicts With: `label`)** | +| `label` |
`str`
|
Optional
| The label of the Image Share Group Member to resolve. **(Conflicts With: `token_uuid`)** | + +## Return Values + +- `image_share_group_member` - The returned Image Share Group Member. + + - Sample Response: + ```json + { + "created": "2025-08-04T10:07:59", + "expiry": "2025-08-04T10:08:01", + "label": "Engineering - Backend", + "status": "active", + "token_uuid": "4591075e-4ba8-43c9-a521-928c3d4a135d", + "updated": "2025-08-04T10:08:00" + } + ``` + - See the [Linode API response documentation](https://techdocs.akamai.com/linode-api/reference/get-sharegroup-member-token) for a list of returned fields + + diff --git a/docs/modules/image_share_group_member_list.md b/docs/modules/image_share_group_member_list.md new file mode 100644 index 00000000..b8c9b43a --- /dev/null +++ b/docs/modules/image_share_group_member_list.md @@ -0,0 +1,60 @@ +# image_share_group_member_list + +List and filter on Image Share Group Members. + +- [Minimum Required Fields](#minimum-required-fields) +- [Examples](#examples) +- [Parameters](#parameters) +- [Return Values](#return-values) + +## Minimum Required Fields +| Field | Type | Required | Description | +|-------------|-------|--------------|--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| +| `api_token` | `str` | **Required** | The Linode account personal access token. It is necessary to run the module.
It can be exposed by the environment variable `LINODE_API_TOKEN` instead.
See details in [Usage](https://github.com/linode/ansible_linode?tab=readme-ov-file#usage). | + +## Examples + +```yaml +- name: List all of the Image Share Group Members for the specified Share Group + linode.cloud.image_share_group_member_list: + sharegroup_id: 123 +``` + + +## Parameters + +| Field | Type | Required | Description | +|-----------|------|----------|------------------------------------------------------------------------------| +| `sharegroup_id` |
`int`
|
**Required**
| The parent Image Share Group for the Image Share Group Members. | +| `order` |
`str`
|
Optional
| The order to list Image Share Group Members in. **(Choices: `desc`, `asc`; Default: `asc`)** | +| `order_by` |
`str`
|
Optional
| The attribute to order Image Share Group Members by. | +| [`filters` (sub-options)](#filters) |
`list`
|
Optional
| A list of filters to apply to the resulting Image Share Group Members. | +| `count` |
`int`
|
Optional
| The number of Image Share Group Members to return. If undefined, all results will be returned. | + +### filters + +| Field | Type | Required | Description | +|-----------|------|----------|------------------------------------------------------------------------------| +| `name` |
`str`
|
**Required**
| The name of the field to filter on. Valid filterable fields can be found [here](https://techdocs.akamai.com/linode-api/reference/get-sharegroup-members). | +| `values` |
`list`
|
**Required**
| A list of values to allow for this field. Fields will pass this filter if at least one of these values matches. | + +## Return Values + +- `image_share_group_members` - The returned Image Share Group Members. + + - Sample Response: + ```json + [ + { + "created": "2025-08-04T10:07:59", + "expiry": "2025-08-04T10:08:01", + "label": "member-label", + "status": "active", + "token_uuid": "4591075e-4ba8-43c9-a521-928c3d4a135d", + "updated": "2025-08-04T10:08:00" + } + ] + ``` + - See the [Linode API response documentation](https://techdocs.akamai.com/linode-api/reference/get-sharegroup-members) for a list of returned fields + + diff --git a/docs/modules/image_share_group_token.md b/docs/modules/image_share_group_token.md new file mode 100644 index 00000000..2c9ca49d --- /dev/null +++ b/docs/modules/image_share_group_token.md @@ -0,0 +1,71 @@ +# image_share_group_token + +Manage an Image Share Group Token. + +- [Minimum Required Fields](#minimum-required-fields) +- [Examples](#examples) +- [Parameters](#parameters) +- [Return Values](#return-values) + +## Minimum Required Fields +| Field | Type | Required | Description | +|-------------|-------|--------------|--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| +| `api_token` | `str` | **Required** | The Linode account personal access token. It is necessary to run the module.
It can be exposed by the environment variable `LINODE_API_TOKEN` instead.
See details in [Usage](https://github.com/linode/ansible_linode?tab=readme-ov-file#usage). | + +## Examples + +```yaml +- name: Create an image share group token + linode.cloud.image_share_group_token: + label: "my-sharegroup-token" + valid_for_sharegroup_uuid: "1533863e-16a4-47b5-b829-ac0f35c13278" + state: present +``` + +```yaml +- name: Delete an image share group token + linode.cloud.image_share_group_token: + label: "my-sharegroup-token" + state: absent +``` + + +## Parameters + +| Field | Type | Required | Description | +|-----------|------|----------|------------------------------------------------------------------------------| +| `label` |
`str`
|
**Required**
| This Image Share Group Token's unique label. | +| `state` |
`str`
|
**Required**
| The desired state of the target. **(Choices: `present`, `absent`)** | +| `valid_for_sharegroup_uuid` |
`str`
|
Optional
| The UUID of the Image Share Group that this token is valid for. | + +## Return Values + +- `image_share_group_token` - The Image Share Group Token in JSON serialized form. + + - Sample Response: + ```json + { + "created": "2025-08-04T10:09:09", + "expiry": null, + "label": "Backend Services - Engineering", + "sharegroup_label": "DevOps Base Images", + "sharegroup_uuid": "e1d0e58b-f89f-4237-84ab-b82077342359", + "status": "active", + "token_uuid": "13428362-5458-4dad-b14b-8d0d4d648f8c", + "updated": null, + "valid_for_sharegroup_uuid": "e1d0e58b-f89f-4237-84ab-b82077342359" + } + ``` + - See the [Linode API response documentation](https://techdocs.akamai.com/linode-api/reference/get-sharegroup-token) for a list of returned fields + + +- `single_use_token` - The single use token string to provide to a Image Share Group Producer to be added to the share group. + + - Sample Response: + ```json + { + "token": "abcdefghijklmnopqrstuvwxyz1234567890" + } + ``` + + diff --git a/docs/modules/image_share_group_token_info.md b/docs/modules/image_share_group_token_info.md new file mode 100644 index 00000000..482d2a32 --- /dev/null +++ b/docs/modules/image_share_group_token_info.md @@ -0,0 +1,51 @@ +# image_share_group_token_info + +Get info about a Linode Image Share Group Token. + +- [Minimum Required Fields](#minimum-required-fields) +- [Examples](#examples) +- [Parameters](#parameters) +- [Return Values](#return-values) + +## Minimum Required Fields +| Field | Type | Required | Description | +|-------------|-------|--------------|--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| +| `api_token` | `str` | **Required** | The Linode account personal access token. It is necessary to run the module.
It can be exposed by the environment variable `LINODE_API_TOKEN` instead.
See details in [Usage](https://github.com/linode/ansible_linode?tab=readme-ov-file#usage). | + +## Examples + +```yaml +- name: Get info about an Image Share Group Token by label + linode.cloud.image_share_group_token_info: + label: example-image-share-group-token +``` + + +## Parameters + +| Field | Type | Required | Description | +|-----------|------|----------|------------------------------------------------------------------------------| +| `token_uuid` |
`str`
|
Optional
| The Token UUID of the Image Share Group Token to resolve. **(Conflicts With: `label`)** | +| `label` |
`str`
|
Optional
| The label of the Image Share Group Token to resolve. **(Conflicts With: `token_uuid`)** | + +## Return Values + +- `image_share_group_token` - The returned Image Share Group Token. + + - Sample Response: + ```json + { + "created": "2025-08-04T10:09:09", + "expiry": "2025-08-04T10:09:11", + "label": "example-token", + "sharegroup_label": "example-sharegroup", + "sharegroup_uuid": "e1d0e58b-f89f-4237-84ab-b82077342359", + "status": "active", + "token_uuid": "13428362-5458-4dad-b14b-8d0d4d648f8c", + "updated": "2025-08-04T10:09:10", + "valid_for_sharegroup_uuid": "e1d0e58b-f89f-4237-84ab-b82077342359" + } + ``` + - See the [Linode API response documentation](https://techdocs.akamai.com/linode-api/reference/get-sharegroup-token) for a list of returned fields + + diff --git a/docs/modules/image_share_group_token_list.md b/docs/modules/image_share_group_token_list.md new file mode 100644 index 00000000..b5f1120a --- /dev/null +++ b/docs/modules/image_share_group_token_list.md @@ -0,0 +1,61 @@ +# image_share_group_token_list + +List and filter on Image Share Group Tokens. + +- [Minimum Required Fields](#minimum-required-fields) +- [Examples](#examples) +- [Parameters](#parameters) +- [Return Values](#return-values) + +## Minimum Required Fields +| Field | Type | Required | Description | +|-------------|-------|--------------|--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| +| `api_token` | `str` | **Required** | The Linode account personal access token. It is necessary to run the module.
It can be exposed by the environment variable `LINODE_API_TOKEN` instead.
See details in [Usage](https://github.com/linode/ansible_linode?tab=readme-ov-file#usage). | + +## Examples + +```yaml +- name: List all of the Image Share Group Tokens for the current Linode Account + linode.cloud.image_share_group_token_list: {} +``` + + +## Parameters + +| Field | Type | Required | Description | +|-----------|------|----------|------------------------------------------------------------------------------| +| `order` |
`str`
|
Optional
| The order to list Image Share Group Tokens in. **(Choices: `desc`, `asc`; Default: `asc`)** | +| `order_by` |
`str`
|
Optional
| The attribute to order Image Share Group Tokens by. | +| [`filters` (sub-options)](#filters) |
`list`
|
Optional
| A list of filters to apply to the resulting Image Share Group Tokens. | +| `count` |
`int`
|
Optional
| The number of Image Share Group Tokens to return. If undefined, all results will be returned. | + +### filters + +| Field | Type | Required | Description | +|-----------|------|----------|------------------------------------------------------------------------------| +| `name` |
`str`
|
**Required**
| The name of the field to filter on. Valid filterable fields can be found [here](https://techdocs.akamai.com/linode-api/reference/get-user-tokens). | +| `values` |
`list`
|
**Required**
| A list of values to allow for this field. Fields will pass this filter if at least one of these values matches. | + +## Return Values + +- `image_share_group_tokens` - The returned Image Share Group Tokens. + + - Sample Response: + ```json + [ + { + "created": "2025-08-04T10:09:09", + "expiry": "2025-08-04T10:09:11", + "label": "example-token", + "sharegroup_label": "example-sharegroup", + "sharegroup_uuid": "e1d0e58b-f89f-4237-84ab-b82077342359", + "status": "active", + "token_uuid": "13428362-5458-4dad-b14b-8d0d4d648f8c", + "updated": "2025-08-04T10:09:10", + "valid_for_sharegroup_uuid": "e1d0e58b-f89f-4237-84ab-b82077342359" + } + ] + ``` + - See the [Linode API response documentation](https://techdocs.akamai.com/linode-api/reference/get-user-tokens) for a list of returned fields + + diff --git a/plugins/module_utils/doc_fragments/consumer_image_share_group_image_list.py b/plugins/module_utils/doc_fragments/consumer_image_share_group_image_list.py new file mode 100644 index 00000000..0893664c --- /dev/null +++ b/plugins/module_utils/doc_fragments/consumer_image_share_group_image_list.py @@ -0,0 +1,50 @@ +"""Documentation fragments for the consumer_image_share_group_image_list module""" + +specdoc_examples = [''' +- name: List all of the Image Share Group Images for the specified Token UUID + linode.cloud.consumer_image_share_group_image_list: + token_uuid: "9e64b99e-92f7-4c7b-a616-8f622fffb94c"'''] + +result_consumer_image_share_group_images_samples = ['''[ + { + "capabilities": [ + "cloud-init", + "distributed-sites" + ], + "created": "2025-08-04T10:07:59", + "created_by": null, + "deprecated": true, + "description": "Official Debian Linux image for server deployment", + "eol": "2025-12-31T18:13:44.756Z", + "expiry": "2025-12-31T18:13:44.756Z", + "id": "shared/1", + "image_sharing": { + "shared_by": { + "sharegroup_id": 123, + "sharegroup_label": "DevOps Base Images", + "sharegroup_uuid": "8d64b99e-92f7-4c7b-a616-8f622fffb94c", + "source_image_id": "private/15" + }, + "shared_with": null + }, + "is_public": true, + "is_shared": "none", + "label": "Linux Debian", + "regions": [ + { + "region": "us-iad", + "status": "available" + } + ], + "size": 256, + "status": "available", + "tags": [ + "repair-image", + "fix-1" + ], + "total_size": 256, + "type": "shared", + "updated": null, + "vendor": "string" + } +]'''] diff --git a/plugins/module_utils/doc_fragments/consumer_image_share_group_info.py b/plugins/module_utils/doc_fragments/consumer_image_share_group_info.py new file mode 100644 index 00000000..10e2eb64 --- /dev/null +++ b/plugins/module_utils/doc_fragments/consumer_image_share_group_info.py @@ -0,0 +1,16 @@ +"""Documentation fragments for the image_share_group_info module""" + +specdoc_examples = [''' +- name: Get info about an Image Share Group by a Consumer's Token UUID + linode.cloud.consumer_image_share_group_info: + token_uuid: "1433863e-16a4-47b5-b829-ac0f35c13278"'''] + +result_consumer_image_share_group_samples = ['''{ + "created": "2025-04-14T22:44:02", + "description": "Group of base operating system images and engineers used for CI/CD pipelines and infrastructure automation", + "id": 1, + "is_suspended": false, + "label": "DevOps Base Images", + "updated": "2025-04-14T22:44:03", + "uuid": "1533863e-16a4-47b5-b829-ac0f35c13278" +}'''] diff --git a/plugins/module_utils/doc_fragments/image.py b/plugins/module_utils/doc_fragments/image.py index a6e70cb2..c5aa3e59 100644 --- a/plugins/module_utils/doc_fragments/image.py +++ b/plugins/module_utils/doc_fragments/image.py @@ -34,31 +34,41 @@ state: absent'''] result_image_samples = ['''{ - "capabilities": [], + "capabilities": [ + "cloud-init", + "distributed-sites" + ], "created": "2021-08-14T22:44:02", - "created_by": "my-account", + "created_by": "linode", "deprecated": false, - "description": "Example Image description.", + "description": "Example image description.", "eol": "2026-07-01T04:00:00", "expiry": null, - "id": "private/123", - "is_public": true, - "label": "my-image", - "size": 2500, - "status": null, - "type": "manual", - "updated": "2021-08-14T22:44:02", - "vendor": "Debian", - "tags": ["test"], - "total_size": 5000, + "id": "private/15", + "image_sharing": { + "shared_by": null, + "shared_with": { + "sharegroup_count": 0, + "sharegroup_list_url": "/images/private/15/sharegroups" + } + }, + "is_public": false, + "is_shared": false, + "label": "Debian 11", "regions": [ { - "region": "us-east", - "status": "available" - }, - { - "region": "us-central", - "status": "pending" + "region": "us-iad", + "status": "available" } - ] + ], + "size": 2500, + "status": "available", + "tags": [ + "repair-image", + "fix-1" + ], + "total_size": 1234567, + "type": "manual", + "updated": "2021-08-14T22:44:02", + "vendor": "Debian" }'''] diff --git a/plugins/module_utils/doc_fragments/image_list.py b/plugins/module_utils/doc_fragments/image_list.py index 589d39c9..37396626 100644 --- a/plugins/module_utils/doc_fragments/image_list.py +++ b/plugins/module_utils/doc_fragments/image_list.py @@ -15,31 +15,43 @@ values: Alpine'''] result_images_samples = ['''[ - { - "created":"2021-08-14T22:44:02", - "created_by":"my-account", - "deprecated":false, - "description":"Example Image description.", - "eol":"2026-07-01T04:00:00", - "expiry":null, - "id":"private/123", - "is_public":false, - "label":"test", - "size":2500, - "status":null, - "type":"manual", - "updated":"2021-08-14T22:44:02", - "vendor":"Debian", - "tags": ["test"], - "total_size": 5000, + { + "capabilities": [ + "cloud-init", + "distributed-sites" + ], + "created": "2021-08-14T22:44:02", + "created_by": "linode", + "deprecated": false, + "description": "Example image description.", + "eol": "2026-07-01T04:00:00", + "expiry": null, + "id": "private/15", + "image_sharing": { + "shared_by": null, + "shared_with": { + "sharegroup_count": 0, + "sharegroup_list_url": "/images/private/15/sharegroups" + } + }, + "is_public": false, + "is_shared": false, + "label": "Debian 11", "regions": [ { - "region": "us-east", - "status": "available" - }, - { - "region": "us-central", - "status": "pending" - }] - } + "region": "us-iad", + "status": "available" + } + ], + "size": 2500, + "status": "available", + "tags": [ + "repair-image", + "fix-1" + ], + "total_size": 1234567, + "type": "manual", + "updated": "2021-08-14T22:44:02", + "vendor": "Debian" + } ]'''] diff --git a/plugins/module_utils/doc_fragments/image_share_group.py b/plugins/module_utils/doc_fragments/image_share_group.py new file mode 100644 index 00000000..b3770375 --- /dev/null +++ b/plugins/module_utils/doc_fragments/image_share_group.py @@ -0,0 +1,34 @@ +"""Documentation fragments for the Image Share Group module""" +specdoc_examples = [''' +- name: Create a basic image share group + linode.cloud.image_share_group: + label: "my-sharegroup" + description: "My image share group." + state: present''', ''' +- name: Create a basic image share group with an image + linode.cloud.image_share_group: + label: "my-sharegroup" + description: "My image share group." + images: + - id: "private/123" + label: "My shared image" + description: "An image shared in the group." + state: present + ''', ''' +- name: Delete an image share group + linode.cloud.image_share_group: + label: "my-sharegroup" + state: absent'''] + +result_image_share_group_samples = ['''{ + "created": "2025-04-14T22:44:02", + "description": "My image share group.", + "expiry": null, + "id": 1, + "images_count": 0, + "is_suspended": false, + "label": "my-sharegroup", + "members_count": 0, + "updated": null, + "uuid": "1533863e-16a4-47b5-b829-ac0f35c13278" +}'''] diff --git a/plugins/module_utils/doc_fragments/image_share_group_image_list.py b/plugins/module_utils/doc_fragments/image_share_group_image_list.py new file mode 100644 index 00000000..7fd437bc --- /dev/null +++ b/plugins/module_utils/doc_fragments/image_share_group_image_list.py @@ -0,0 +1,50 @@ +"""Documentation fragments for the image_share_group_image_list module""" + +specdoc_examples = [''' +- name: List all of the Image Share Group Images for the specified Share Group + linode.cloud.image_share_group_image_list: + sharegroup_id: 123'''] + +result_image_share_group_images_samples = ['''[ + { + "capabilities": [ + "cloud-init", + "distributed-sites" + ], + "created": "2025-08-04T10:07:59", + "created_by": null, + "deprecated": true, + "description": "Official Debian Linux image for server deployment", + "eol": "2025-12-31T18:13:44.756Z", + "expiry": "2025-12-31T18:13:44.756Z", + "id": "shared/1", + "image_sharing": { + "shared_by": { + "sharegroup_id": 123, + "sharegroup_label": "DevOps Base Images", + "sharegroup_uuid": "8d64b99e-92f7-4c7b-a616-8f622fffb94c", + "source_image_id": "private/15" + }, + "shared_with": null + }, + "is_public": true, + "is_shared": "none", + "label": "Linux Debian", + "regions": [ + { + "region": "us-iad", + "status": "available" + } + ], + "size": 256, + "status": "available", + "tags": [ + "repair-image", + "fix-1" + ], + "total_size": 256, + "type": "shared", + "updated": null, + "vendor": "string" + } +]'''] diff --git a/plugins/module_utils/doc_fragments/image_share_group_info.py b/plugins/module_utils/doc_fragments/image_share_group_info.py new file mode 100644 index 00000000..baa6e402 --- /dev/null +++ b/plugins/module_utils/doc_fragments/image_share_group_info.py @@ -0,0 +1,19 @@ +"""Documentation fragments for the image_share_group_info module""" + +specdoc_examples = [''' +- name: Get info about an Image Share Group by label + linode.cloud.image_share_group_info: + label: example-image-share-group'''] + +result_image_share_group_samples = ['''{ + "created": "2025-04-14T22:44:02", + "description": "Example.", + "expiry": "2025-04-14T22:44:02", + "id": 1, + "images_count": 0, + "is_suspended": false, + "label": "example-image-share-group", + "members_count": 0, + "updated": "2025-04-14T22:44:02", + "uuid": "1533863e-16a4-47b5-b829-ac0f35c13278" +}'''] diff --git a/plugins/module_utils/doc_fragments/image_share_group_list.py b/plugins/module_utils/doc_fragments/image_share_group_list.py new file mode 100644 index 00000000..271ca492 --- /dev/null +++ b/plugins/module_utils/doc_fragments/image_share_group_list.py @@ -0,0 +1,23 @@ +"""Documentation fragments for the image_share_group_list module""" + +specdoc_examples = [''' +- name: List all of the Image Share Groups for the current Linode Account + linode.cloud.image_share_group_list: {}''', ''' +- name: List all of the Image Share Groups that contain a specific private image + linode.cloud.image_share_group_list: + image_id: "private/12345"'''] + +result_image_share_groups_samples = ['''[ + { + "created": "2025-04-14T22:44:02", + "description": "Group of base operating system images and engineers used for CI/CD pipelines and infrastructure automation", + "expiry": null, + "id": 1, + "images_count": 0, + "is_suspended": false, + "label": "DevOps Base Images", + "members_count": 0, + "updated": null, + "uuid": "1533863e-16a4-47b5-b829-ac0f35c13278" + } +]'''] diff --git a/plugins/module_utils/doc_fragments/image_share_group_member.py b/plugins/module_utils/doc_fragments/image_share_group_member.py new file mode 100644 index 00000000..8810793d --- /dev/null +++ b/plugins/module_utils/doc_fragments/image_share_group_member.py @@ -0,0 +1,21 @@ +"""Documentation fragments for the Image Share Group Member module""" +specdoc_examples = [''' +- name: Create an image share group member + linode.cloud.image_share_group_member: + label: "my-sharegroup-member" + token: "abcdefghijklmnopqrstuvwxyz1234567890" + state: present''', ''' +- name: Delete an image share group member + linode.cloud.image_share_group_member: + label: "my-sharegroup-member" + state: absent'''] + +result_image_share_group_member_samples = ['''{ + "token_uuid": "24wef-243qg-45wgg-q343q", + "status": "active", + "label": "my-sharegroup-member", + "created": "2016-03-16T17:30:49", + "updated": "2016-03-18T17:30:49", + "expiry": "2016-03-18T17:30:49" +} +'''] diff --git a/plugins/module_utils/doc_fragments/image_share_group_member_info.py b/plugins/module_utils/doc_fragments/image_share_group_member_info.py new file mode 100644 index 00000000..1e5670ec --- /dev/null +++ b/plugins/module_utils/doc_fragments/image_share_group_member_info.py @@ -0,0 +1,16 @@ +"""Documentation fragments for the image_share_group_member_info module""" + +specdoc_examples = [''' +- name: Get info about an Image Share Group Member by label + linode.cloud.image_share_group_member_info: + sharegroup_id: 123456 + label: example-image-share-group-member'''] + +result_image_share_group_member_samples = ['''{ + "created": "2025-08-04T10:07:59", + "expiry": "2025-08-04T10:08:01", + "label": "Engineering - Backend", + "status": "active", + "token_uuid": "4591075e-4ba8-43c9-a521-928c3d4a135d", + "updated": "2025-08-04T10:08:00" +}'''] diff --git a/plugins/module_utils/doc_fragments/image_share_group_member_list.py b/plugins/module_utils/doc_fragments/image_share_group_member_list.py new file mode 100644 index 00000000..594f49bb --- /dev/null +++ b/plugins/module_utils/doc_fragments/image_share_group_member_list.py @@ -0,0 +1,17 @@ +"""Documentation fragments for the image_share_group_member_list module""" + +specdoc_examples = [''' +- name: List all of the Image Share Group Members for the specified Share Group + linode.cloud.image_share_group_member_list: + sharegroup_id: 123'''] + +result_image_share_group_members_samples = ['''[ + { + "created": "2025-08-04T10:07:59", + "expiry": "2025-08-04T10:08:01", + "label": "member-label", + "status": "active", + "token_uuid": "4591075e-4ba8-43c9-a521-928c3d4a135d", + "updated": "2025-08-04T10:08:00" + } +]'''] diff --git a/plugins/module_utils/doc_fragments/image_share_group_token.py b/plugins/module_utils/doc_fragments/image_share_group_token.py new file mode 100644 index 00000000..ddd0ba9d --- /dev/null +++ b/plugins/module_utils/doc_fragments/image_share_group_token.py @@ -0,0 +1,27 @@ +"""Documentation fragments for the Image Share Group Token module""" +specdoc_examples = [''' +- name: Create an image share group token + linode.cloud.image_share_group_token: + label: "my-sharegroup-token" + valid_for_sharegroup_uuid: "1533863e-16a4-47b5-b829-ac0f35c13278" + state: present''', ''' +- name: Delete an image share group token + linode.cloud.image_share_group_token: + label: "my-sharegroup-token" + state: absent'''] + +result_image_share_group_token_samples = ['''{ + "created": "2025-08-04T10:09:09", + "expiry": null, + "label": "Backend Services - Engineering", + "sharegroup_label": "DevOps Base Images", + "sharegroup_uuid": "e1d0e58b-f89f-4237-84ab-b82077342359", + "status": "active", + "token_uuid": "13428362-5458-4dad-b14b-8d0d4d648f8c", + "updated": null, + "valid_for_sharegroup_uuid": "e1d0e58b-f89f-4237-84ab-b82077342359" +}'''] + +result_single_use_token_samples = ['''{ + "token": "abcdefghijklmnopqrstuvwxyz1234567890" +}'''] diff --git a/plugins/module_utils/doc_fragments/image_share_group_token_info.py b/plugins/module_utils/doc_fragments/image_share_group_token_info.py new file mode 100644 index 00000000..c9a2d772 --- /dev/null +++ b/plugins/module_utils/doc_fragments/image_share_group_token_info.py @@ -0,0 +1,18 @@ +"""Documentation fragments for the image_share_group_token_info module""" + +specdoc_examples = [''' +- name: Get info about an Image Share Group Token by label + linode.cloud.image_share_group_token_info: + label: example-image-share-group-token'''] + +result_image_share_group_token_samples = ['''{ + "created": "2025-08-04T10:09:09", + "expiry": "2025-08-04T10:09:11", + "label": "example-token", + "sharegroup_label": "example-sharegroup", + "sharegroup_uuid": "e1d0e58b-f89f-4237-84ab-b82077342359", + "status": "active", + "token_uuid": "13428362-5458-4dad-b14b-8d0d4d648f8c", + "updated": "2025-08-04T10:09:10", + "valid_for_sharegroup_uuid": "e1d0e58b-f89f-4237-84ab-b82077342359" +}'''] diff --git a/plugins/module_utils/doc_fragments/image_share_group_token_list.py b/plugins/module_utils/doc_fragments/image_share_group_token_list.py new file mode 100644 index 00000000..495b44d1 --- /dev/null +++ b/plugins/module_utils/doc_fragments/image_share_group_token_list.py @@ -0,0 +1,19 @@ +"""Documentation fragments for the image_share_group_token_list module""" + +specdoc_examples = [''' +- name: List all of the Image Share Group Tokens for the current Linode Account + linode.cloud.image_share_group_token_list: {}'''] + +result_image_share_group_tokens_samples = ['''[ + { + "created": "2025-08-04T10:09:09", + "expiry": "2025-08-04T10:09:11", + "label": "example-token", + "sharegroup_label": "example-sharegroup", + "sharegroup_uuid": "e1d0e58b-f89f-4237-84ab-b82077342359", + "status": "active", + "token_uuid": "13428362-5458-4dad-b14b-8d0d4d648f8c", + "updated": "2025-08-04T10:09:10", + "valid_for_sharegroup_uuid": "e1d0e58b-f89f-4237-84ab-b82077342359" + } +]'''] diff --git a/plugins/modules/consumer_image_share_group_image_list.py b/plugins/modules/consumer_image_share_group_image_list.py new file mode 100644 index 00000000..38ecee46 --- /dev/null +++ b/plugins/modules/consumer_image_share_group_image_list.py @@ -0,0 +1,47 @@ +#!/usr/bin/python +# -*- coding: utf-8 -*- + +"""This module allows Consumers to list the Images in an Image Share Group a specific +Image Share Group Token gives access to.""" + +from __future__ import absolute_import, division, print_function + +from ansible_collections.linode.cloud.plugins.module_utils.doc_fragments import ( + consumer_image_share_group_image_list as docs, +) +from ansible_collections.linode.cloud.plugins.module_utils.linode_common_list import ( + ListModule, + ListModuleParam, +) +from ansible_specdoc.objects import FieldType + +module = ListModule( + result_display_name="Image Share Group Images", + result_field_name="image_share_group_images", + endpoint_template="/images/sharegroups/tokens/{token_uuid}/sharegroup/images", + result_docs_url=( + "https://techdocs.akamai.com/linode-api/reference/" + "get-sharegroup-images-by-token" + ), + result_samples=docs.result_consumer_image_share_group_images_samples, + examples=docs.specdoc_examples, + params=[ + ListModuleParam( + display_name="Token", + name="token_uuid", + type=FieldType.string, + ), + ], +) + +SPECDOC_META = module.spec + +DOCUMENTATION = r""" +""" +EXAMPLES = r""" +""" +RETURN = r""" +""" + +if __name__ == "__main__": + module.run() diff --git a/plugins/modules/consumer_image_share_group_info.py b/plugins/modules/consumer_image_share_group_info.py new file mode 100644 index 00000000..962d5602 --- /dev/null +++ b/plugins/modules/consumer_image_share_group_info.py @@ -0,0 +1,54 @@ +#!/usr/bin/python +# -*- coding: utf-8 -*- + +"""This module contains the functionality for the Consumer Image Share Group info module. +This module allows a consumer to retrieve information about the Image Share Group a specific +Image Share Group Token gives access to.""" + +from __future__ import absolute_import, division, print_function + +from ansible_collections.linode.cloud.plugins.module_utils.doc_fragments import ( + consumer_image_share_group_info as docs, +) +from ansible_collections.linode.cloud.plugins.module_utils.linode_common_info import ( + InfoModule, + InfoModuleParam, + InfoModuleResult, +) +from ansible_specdoc.objects import FieldType +from linode_api4 import ImageShareGroupToken + +module = InfoModule( + primary_result=InfoModuleResult( + field_name="image_share_group", + field_type=FieldType.dict, + display_name="Image Share Group", + docs_url="https://techdocs.akamai.com/linode-api/reference/get-sharegroup-by-token", + samples=docs.result_consumer_image_share_group_samples, + get=lambda client, params, _current=None: ( + client.load(ImageShareGroupToken, params["token_uuid"]) + .get_sharegroup() + .__dict__ + ), + ), + params=[ + InfoModuleParam( + display_name="Token", + name="token_uuid", + type=FieldType.string, + ) + ], + examples=docs.specdoc_examples, +) + +SPECDOC_META = module.spec + +DOCUMENTATION = r""" +""" +EXAMPLES = r""" +""" +RETURN = r""" +""" + +if __name__ == "__main__": + module.run() diff --git a/plugins/modules/image_share_group.py b/plugins/modules/image_share_group.py new file mode 100644 index 00000000..80d51661 --- /dev/null +++ b/plugins/modules/image_share_group.py @@ -0,0 +1,372 @@ +#!/usr/bin/python +# -*- coding: utf-8 -*- + +"""This module contains all the functionality for Image Share Groups for a Producer.""" + +from __future__ import absolute_import, division, print_function + +import copy +from typing import Any, Optional + +import ansible_collections.linode.cloud.plugins.module_utils.doc_fragments.image_share_group as docs +from ansible_collections.linode.cloud.plugins.module_utils.linode_common import ( + LinodeModuleBase, +) +from ansible_collections.linode.cloud.plugins.module_utils.linode_docs import ( + global_authors, + global_requirements, +) +from ansible_collections.linode.cloud.plugins.module_utils.linode_helper import ( + filter_null_values_recursive, + handle_updates, +) +from ansible_specdoc.objects import ( + FieldType, + SpecDocMeta, + SpecField, + SpecReturnValue, +) +from linode_api4 import ( + ImageShareGroup, + ImageShareGroupImagesToAdd, + ImageShareGroupImageToAdd, + ImageShareGroupImageToUpdate, +) + +image_share_group_images_spec = { + "id": SpecField( + type=FieldType.string, + required=True, + description=[ + "The id of the Private Image to include in an Image Share Group." + ], + ), + "label": SpecField( + type=FieldType.string, + description=[ + "A label to assign to the Image within the context of an Image Share Group." + ], + ), + "description": SpecField( + type=FieldType.string, + description=[ + "A description to assign to the Image within the context of an Image Share Group." + ], + ), +} + +SPEC = { + "label": SpecField( + type=FieldType.string, + description=["This Image Share Group's unique label."], + required=True, + ), + "description": SpecField( + type=FieldType.string, + description=["A description of this Image Share Group."], + ), + "images": SpecField( + type=FieldType.list, + element_type=FieldType.dict, + suboptions=image_share_group_images_spec, + description=["A list of images to include in this Image Share Group."], + ), + "state": SpecField( + type=FieldType.string, + description=["The desired state of the target."], + choices=["present", "absent"], + required=True, + ), +} + +SPECDOC_META = SpecDocMeta( + description=["Manage an Image Share Group."], + requirements=global_requirements, + author=global_authors, + options=SPEC, + examples=docs.specdoc_examples, + return_values={ + "image_share_group": SpecReturnValue( + description="The Image Share Group in JSON serialized form.", + docs_url="https://techdocs.akamai.com/linode-api/reference/get-sharegroup", + type=FieldType.dict, + sample=docs.result_image_share_group_samples, + ) + }, +) + +MUTABLE_FIELDS = {"description"} + +DOCUMENTATION = r""" +""" +EXAMPLES = r""" +""" +RETURN = r""" +""" + + +class Module(LinodeModuleBase): + """Module for creating and destroying Image Share Groups""" + + def __init__(self) -> None: + self.module_arg_spec = SPECDOC_META.ansible_spec + self.results = { + "changed": False, + "actions": [], + "image_share_group": None, + } + + super().__init__(module_arg_spec=self.module_arg_spec) + + def _get_image_share_group_by_label( + self, label: str + ) -> Optional[ImageShareGroup]: + try: + return self.client.sharegroups(ImageShareGroup.label == label)[0] + except IndexError: + return None + except Exception as exception: + return self.fail( + msg="failed to get image share group {0}: {1}".format( + label, exception + ) + ) + + def _create(self) -> Optional[ImageShareGroup]: + params = filter_null_values_recursive( + { + "label": self.module.params.get("label"), + "description": self.module.params.get("description"), + "images": self.module.params.get("images"), + } + ) + + try: + return self.client.sharegroups.create_sharegroup(**params) + except Exception as exception: + return self.fail( + msg="failed to create Image Share Group: {0}".format(exception) + ) + + def _update( + self, image_share_group: ImageShareGroup + ) -> Optional[ImageShareGroup]: + image_share_group._api_get() + + new_params = filter_null_values_recursive( + copy.deepcopy(self.module.params) + ) + new_params = { + k: v for k, v in new_params.items() if k in MUTABLE_FIELDS + } + + images_to_update = self.module.params.get("images") + + handle_updates( + image_share_group, new_params, MUTABLE_FIELDS, self.register_action + ) + + # Only touch images if explicitly provided + if images_to_update is not None: + self._update_images(image_share_group, images_to_update) + + return image_share_group + + # pylint: disable=too-many-statements + def _update_images( + self, image_share_group: ImageShareGroup, desired_images: list + ) -> None: + desired_images = desired_images or [] + + # Build a map of the desired state of images by their private IDs + plan_map = {img["id"]: img for img in desired_images} + + # Build a map of the remote state of images by their private IDs + remote_map = {} + for img in image_share_group.get_image_shares(): + shared_by = getattr( + getattr(img, "image_sharing", None), + "shared_by", + None, + ) + private_id = getattr(shared_by, "source_image_id", None) + if not private_id: + continue + + remote_map[private_id] = { + "shared_id": getattr(img, "id", None), + "label": getattr(img, "label", None), + "description": getattr(img, "description", None), + } + + to_add = [] + to_update = [] + to_remove = [] + + # Determine which images to add and update + for private_id, desired_image in plan_map.items(): + remote = remote_map.get(private_id) + + if not remote: + # Not present remotely, should be added + to_add.append( + { + "id": private_id, + "label": desired_image.get("label"), + "description": desired_image.get("description"), + } + ) + continue + + # Present remotely, check if an update is needed + label_changed = desired_image.get("label") != remote.get("label") + desc_changed = desired_image.get("description") != remote.get( + "description" + ) + + if label_changed or desc_changed: + opts = {} + if label_changed: + opts["label"] = desired_image.get("label") + if desc_changed: + opts["description"] = desired_image.get("description") + + to_update.append( + { + "shared_id": remote["shared_id"], + "opts": opts, + } + ) + + # Determine which images to remove + for private_id, remote in remote_map.items(): + if private_id not in plan_map: + to_remove.append(remote["shared_id"]) + + # Add + if to_add: + try: + images_to_add = ImageShareGroupImagesToAdd( + images=[ + ImageShareGroupImageToAdd( + id=img["id"], + label=img.get("label"), + description=img.get("description"), + ) + for img in to_add + ] + ) + + image_share_group.add_images(images_to_add) + + for img in to_add: + self.register_action(f"Added image {img['id']}") + + self.results["changed"] = True + + except Exception as e: + self.fail(msg=f"Failed to add images: {e}") + + # Update + for update in to_update: + try: + kwargs = {} + if "label" in update["opts"]: + kwargs["label"] = update["opts"]["label"] + if "description" in update["opts"]: + kwargs["description"] = update["opts"]["description"] + + images_to_update = ImageShareGroupImageToUpdate( + image_share_id=update["shared_id"], **kwargs + ) + + image_share_group.update_image_share(images_to_update) + self.register_action(f"Updated image {update['shared_id']}") + self.results["changed"] = True + except Exception as e: + self.fail( + msg=f"Failed to update image {update['shared_id']}: {e}" + ) + + # Remove + for img_shared_id in to_remove: + try: + image_share_group.revoke_image_share(img_shared_id) + self.register_action(f"Removed image {img_shared_id}") + self.results["changed"] = True + except Exception as e: + self.fail(msg=f"Failed to remove image {img_shared_id}: {e}") + + def _populate_results(self, sharegroup: ImageShareGroup) -> None: + sharegroup._api_get() + raw = sharegroup._raw_json + + images_param = self.module.params.get("images") + + if images_param is not None: + # User explicitly provided images + raw["images"] = images_param + else: + # User omitted images so fetch current images from API + images = [] + for img in sharegroup.get_image_shares(): + shared_by = getattr( + getattr(img, "image_sharing", None), "shared_by", None + ) + private_id = getattr(shared_by, "source_image_id", None) + if private_id: + images.append( + { + "id": private_id, + "label": getattr(img, "label", None), + "description": getattr(img, "description", None), + } + ) + raw["images"] = images + + self.results["image_share_group"] = raw + + def _handle_present(self) -> None: + label = self.module.params.get("label") + image_share_group = self._get_image_share_group_by_label(label) + + if not image_share_group: + image_share_group = self._create() + self.register_action( + "Created Image Share Group {0}".format(image_share_group.label) + ) + + self._update(image_share_group) + + self._populate_results(image_share_group) + + def _handle_absent(self) -> None: + label: str = self.module.params.get("label") + + sharegroup = self._get_image_share_group_by_label(label) + + if sharegroup is not None: + self.results["image_share_group"] = sharegroup._raw_json + sharegroup.delete() + self.register_action("Deleted image share group {0}".format(label)) + + def exec_module(self, **kwargs: Any) -> Optional[dict]: + """Entrypoint for Image Share Group module""" + state = kwargs.get("state") + + if state == "absent": + self._handle_absent() + return self.results + + self._handle_present() + + return self.results + + +def main() -> None: + """Constructs and calls the Image Share Group module""" + Module() + + +if __name__ == "__main__": + main() diff --git a/plugins/modules/image_share_group_image_list.py b/plugins/modules/image_share_group_image_list.py new file mode 100644 index 00000000..75b81999 --- /dev/null +++ b/plugins/modules/image_share_group_image_list.py @@ -0,0 +1,43 @@ +#!/usr/bin/python +# -*- coding: utf-8 -*- + +"""This module allows users to list Image Share Group Images.""" + +from __future__ import absolute_import, division, print_function + +from ansible_collections.linode.cloud.plugins.module_utils.doc_fragments import ( + image_share_group_image_list as docs, +) +from ansible_collections.linode.cloud.plugins.module_utils.linode_common_list import ( + ListModule, + ListModuleParam, +) +from ansible_specdoc.objects import FieldType + +module = ListModule( + result_display_name="Image Share Group Images", + result_field_name="image_share_group_images", + endpoint_template="/images/sharegroups/{sharegroup_id}/images", + result_docs_url="https://techdocs.akamai.com/linode-api/reference/get-sharegroup-images", + result_samples=docs.result_image_share_group_images_samples, + examples=docs.specdoc_examples, + params=[ + ListModuleParam( + display_name="Image Share Group", + name="sharegroup_id", + type=FieldType.integer, + ), + ], +) + +SPECDOC_META = module.spec + +DOCUMENTATION = r""" +""" +EXAMPLES = r""" +""" +RETURN = r""" +""" + +if __name__ == "__main__": + module.run() diff --git a/plugins/modules/image_share_group_info.py b/plugins/modules/image_share_group_info.py new file mode 100644 index 00000000..ff707f97 --- /dev/null +++ b/plugins/modules/image_share_group_info.py @@ -0,0 +1,64 @@ +#!/usr/bin/python +# -*- coding: utf-8 -*- + +"""This module contains the functionality for the Image Share Group info module.""" + +from __future__ import absolute_import, division, print_function + +from ansible_collections.linode.cloud.plugins.module_utils.doc_fragments import ( + image_share_group_info as docs, +) +from ansible_collections.linode.cloud.plugins.module_utils.linode_common_info import ( + InfoModule, + InfoModuleAttr, + InfoModuleResult, +) +from ansible_collections.linode.cloud.plugins.module_utils.linode_helper import ( + safe_find, +) +from ansible_specdoc.objects import FieldType +from linode_api4 import ImageShareGroup + +module = InfoModule( + primary_result=InfoModuleResult( + field_name="image_share_group", + field_type=FieldType.dict, + display_name="Image Share Group", + docs_url="https://techdocs.akamai.com/linode-api/reference/get-sharegroup", + samples=docs.result_image_share_group_samples, + ), + attributes=[ + InfoModuleAttr( + display_name="ID", + name="id", + type=FieldType.integer, + get=lambda client, params: client.load( + ImageShareGroup, + params.get("id"), + )._raw_json, + ), + InfoModuleAttr( + display_name="label", + name="label", + type=FieldType.string, + get=lambda client, params: safe_find( + client.sharegroups, + ImageShareGroup.label == params.get("label"), + raise_not_found=True, + )._raw_json, + ), + ], + examples=docs.specdoc_examples, +) + +SPECDOC_META = module.spec + +DOCUMENTATION = r""" +""" +EXAMPLES = r""" +""" +RETURN = r""" +""" + +if __name__ == "__main__": + module.run() diff --git a/plugins/modules/image_share_group_list.py b/plugins/modules/image_share_group_list.py new file mode 100644 index 00000000..e55230e0 --- /dev/null +++ b/plugins/modules/image_share_group_list.py @@ -0,0 +1,66 @@ +#!/usr/bin/python +# -*- coding: utf-8 -*- + +"""This module allows users to list Image Share Groups.""" + +from __future__ import absolute_import, division, print_function + +from typing import Dict + +from ansible_collections.linode.cloud.plugins.module_utils.doc_fragments import ( + image_share_group_list as docs, +) +from ansible_collections.linode.cloud.plugins.module_utils.linode_common_list import ( + ListModule, +) +from ansible_specdoc.objects import FieldType, SpecField + + +def custom_field_resolver(params: Dict[str, str]) -> Dict[str, str]: + """ + Resolves the appropriate endpoint based on the 'image_id' parameter. + + :param params: The parameters passed to the module. + + :returns: The appropriate documentation and examples. + """ + if params.get("image_id"): + return { + "endpoint_template": f"/images/{params.get('image_id')}/sharegroups", + } + return { + "endpoint_template": "/images/sharegroups", + } + + +module = ListModule( + result_display_name="Image Share Groups", + result_field_name="image_share_groups", + endpoint_template="/images/sharegroups", + result_docs_url="https://techdocs.akamai.com/linode-api/reference/get-sharegroups", + result_samples=docs.result_image_share_groups_samples, + examples=docs.specdoc_examples, + custom_options={ + "image_id": SpecField( + type=FieldType.string, + description=[ + "Specifies the private image ID to list share groups for.", + "If provided, only share groups containing the specified image will be returned.", + ], + required=False, + ), + }, + custom_field_resolver=custom_field_resolver, +) + +SPECDOC_META = module.spec + +DOCUMENTATION = r""" +""" +EXAMPLES = r""" +""" +RETURN = r""" +""" + +if __name__ == "__main__": + module.run() diff --git a/plugins/modules/image_share_group_member.py b/plugins/modules/image_share_group_member.py new file mode 100644 index 00000000..ec83cb41 --- /dev/null +++ b/plugins/modules/image_share_group_member.py @@ -0,0 +1,196 @@ +#!/usr/bin/python +# -*- coding: utf-8 -*- + +"""This module contains all the functionality for Image Share Group Members for a Producer.""" + +from __future__ import absolute_import, division, print_function + +from typing import Any, Dict, Optional + +from ansible_collections.linode.cloud.plugins.module_utils.doc_fragments import ( + image_share_group_member as docs, +) +from ansible_collections.linode.cloud.plugins.module_utils.linode_common import ( + LinodeModuleBase, +) +from ansible_collections.linode.cloud.plugins.module_utils.linode_docs import ( + global_authors, + global_requirements, +) +from ansible_specdoc.objects import ( + FieldType, + SpecDocMeta, + SpecField, + SpecReturnValue, +) +from linode_api4 import ImageShareGroup, ImageShareGroupMemberToAdd + +SPEC = { + "label": SpecField( + type=FieldType.string, + description=["This Image Share Group Member's unique label."], + required=True, + ), + "token": SpecField( + type=FieldType.string, + description=[ + "A single-use Image Share Group Token provided by the Consumer. " + "This value is required when creating a member and is never returned." + ], + no_log=True, + ), + "sharegroup_id": SpecField( + type=FieldType.integer, + description=["The ID of the Image Share Group this member belongs to."], + required=True, + ), + "state": SpecField( + type=FieldType.string, + description=["The desired state of the target."], + choices=["present", "absent"], + required=True, + ), +} + +SPECDOC_META = SpecDocMeta( + description=["Manage an Image Share Group Member."], + requirements=global_requirements, + author=global_authors, + options=SPEC, + examples=docs.specdoc_examples, + return_values={ + "image_share_group_member": SpecReturnValue( + description="The Image Share Group Member in JSON serialized form.", + docs_url="https://techdocs.akamai.com/linode-api/reference/get-sharegroup-member-token", + type=FieldType.dict, + sample=docs.result_image_share_group_member_samples, + ), + }, +) + +DOCUMENTATION = r""" +""" +EXAMPLES = r""" +""" +RETURN = r""" +""" + + +class Module(LinodeModuleBase): + """Module for creating and destroying Image Share Group Members""" + + def __init__(self) -> None: + self.module_arg_spec = SPECDOC_META.ansible_spec + self.results = { + "changed": False, + "actions": [], + "image_share_group_member": None, + } + + super().__init__( + module_arg_spec=self.module_arg_spec, + required_if=[ + ("state", "present", ["token"]), + ], + ) + + def _get_image_share_group_member_by_label( + self, label: str + ) -> Optional[Dict[str, Any]]: + try: + sharegroup = self.client.load( + ImageShareGroup, self.module.params.get("sharegroup_id") + ) + return next( + ( + m.__dict__ + for m in sharegroup.get_members() + if m.label == label + ), + None, + ) + except Exception as exception: + return self.fail( + msg="failed to get image share group member for label {0}: {1}".format( + label, exception + ) + ) + + def _create(self) -> Optional[Dict[str, Any]]: + try: + sharegroup = self.client.load( + ImageShareGroup, + self.module.params.get("sharegroup_id"), + ) + + member = ImageShareGroupMemberToAdd( + token=self.module.params.get("token"), + label=self.module.params.get("label"), + ) + + member_obj = sharegroup.add_member(member) + return member_obj.__dict__ + + except Exception as exception: + return self.fail( + msg="failed to create Image Share Group Member: {0}".format( + exception + ) + ) + + def _handle_present(self) -> None: + label = self.module.params.get("label") + member = self._get_image_share_group_member_by_label(label) + + if not member: + member = self._create() + self.register_action( + "Created Image Share Group Member {0}".format(label) + ) + + self.results["image_share_group_member"] = member + + def _handle_absent(self) -> None: + label = self.module.params.get("label") + member = self._get_image_share_group_member_by_label(label) + + if member is None: + return + + sharegroup = self.client.load( + ImageShareGroup, + self.module.params.get("sharegroup_id"), + ) + + try: + token_uuid = member.get("token_uuid") + sharegroup.remove_member(token_uuid=token_uuid) + self.results["image_share_group_member"] = member + self.register_action(f"Deleted Image Share Group Member {label}") + except Exception as exception: + return self.fail( + msg="failed to delete Image Share Group Member {0}: {1}".format( + label, exception + ) + ) + + def exec_module(self, **kwargs: Any) -> Optional[dict]: + """Entrypoint for Image Share Group Member module""" + state = kwargs.get("state") + + if state == "absent": + self._handle_absent() + return self.results + + self._handle_present() + + return self.results + + +def main() -> None: + """Constructs and calls the Image Share Group Member module""" + Module() + + +if __name__ == "__main__": + main() diff --git a/plugins/modules/image_share_group_member_info.py b/plugins/modules/image_share_group_member_info.py new file mode 100644 index 00000000..44a7c9f0 --- /dev/null +++ b/plugins/modules/image_share_group_member_info.py @@ -0,0 +1,82 @@ +#!/usr/bin/python +# -*- coding: utf-8 -*- + +"""This module contains the functionality for the Image Share Group Member info module.""" + +from __future__ import absolute_import, division, print_function + +from ansible_collections.linode.cloud.plugins.module_utils.doc_fragments import ( + image_share_group_member_info as docs, +) +from ansible_collections.linode.cloud.plugins.module_utils.linode_common_info import ( + InfoModule, + InfoModuleAttr, + InfoModuleParam, + InfoModuleResult, +) +from ansible_specdoc.objects import FieldType +from linode_api4 import ImageShareGroup + +module = InfoModule( + primary_result=InfoModuleResult( + field_name="image_share_group_member", + field_type=FieldType.dict, + display_name="Image Share Group Member", + docs_url="https://techdocs.akamai.com/linode-api/reference/get-sharegroup-member-token", + samples=docs.result_image_share_group_member_samples, + ), + params=[ + InfoModuleParam( + display_name="Image Share Group", + name="sharegroup_id", + type=FieldType.integer, + ) + ], + attributes=[ + InfoModuleAttr( + display_name="Token UUID", + name="token_uuid", + type=FieldType.string, + get=lambda client, params: next( + ( + m.__dict__ + for m in client.load( + ImageShareGroup, + params.get("sharegroup_id"), + ).get_members() + if m.token_uuid == params.get("token_uuid") + ), + None, + ), + ), + InfoModuleAttr( + display_name="label", + name="label", + type=FieldType.string, + get=lambda client, params: next( + ( + m.__dict__ + for m in client.load( + ImageShareGroup, + params.get("sharegroup_id"), + ).get_members() + if m.label == params.get("label") + ), + None, + ), + ), + ], + examples=docs.specdoc_examples, +) + +SPECDOC_META = module.spec + +DOCUMENTATION = r""" +""" +EXAMPLES = r""" +""" +RETURN = r""" +""" + +if __name__ == "__main__": + module.run() diff --git a/plugins/modules/image_share_group_member_list.py b/plugins/modules/image_share_group_member_list.py new file mode 100644 index 00000000..9a617296 --- /dev/null +++ b/plugins/modules/image_share_group_member_list.py @@ -0,0 +1,43 @@ +#!/usr/bin/python +# -*- coding: utf-8 -*- + +"""This module allows users to list Image Share Group Members.""" + +from __future__ import absolute_import, division, print_function + +from ansible_collections.linode.cloud.plugins.module_utils.doc_fragments import ( + image_share_group_member_list as docs, +) +from ansible_collections.linode.cloud.plugins.module_utils.linode_common_list import ( + ListModule, + ListModuleParam, +) +from ansible_specdoc.objects import FieldType + +module = ListModule( + result_display_name="Image Share Group Members", + result_field_name="image_share_group_members", + endpoint_template="/images/sharegroups/{sharegroup_id}/members", + result_docs_url="https://techdocs.akamai.com/linode-api/reference/get-sharegroup-members", + result_samples=docs.result_image_share_group_members_samples, + examples=docs.specdoc_examples, + params=[ + ListModuleParam( + display_name="Image Share Group", + name="sharegroup_id", + type=FieldType.integer, + ), + ], +) + +SPECDOC_META = module.spec + +DOCUMENTATION = r""" +""" +EXAMPLES = r""" +""" +RETURN = r""" +""" + +if __name__ == "__main__": + module.run() diff --git a/plugins/modules/image_share_group_token.py b/plugins/modules/image_share_group_token.py new file mode 100644 index 00000000..5a76727c --- /dev/null +++ b/plugins/modules/image_share_group_token.py @@ -0,0 +1,196 @@ +#!/usr/bin/python +# -*- coding: utf-8 -*- + +"""This module contains all the functionality for Image Share Group Tokens for a Consumer.""" + +from __future__ import absolute_import, division, print_function + +from typing import Any, Optional + +from ansible_collections.linode.cloud.plugins.module_utils.doc_fragments import ( + image_share_group_token as docs, +) +from ansible_collections.linode.cloud.plugins.module_utils.linode_common import ( + LinodeModuleBase, +) +from ansible_collections.linode.cloud.plugins.module_utils.linode_docs import ( + global_authors, + global_requirements, +) +from ansible_collections.linode.cloud.plugins.module_utils.linode_helper import ( + filter_null_values, +) +from ansible_specdoc.objects import ( + FieldType, + SpecDocMeta, + SpecField, + SpecReturnValue, +) +from linode_api4 import ImageShareGroupToken + +SPEC = { + "label": SpecField( + type=FieldType.string, + description=["This Image Share Group Token's unique label."], + required=True, + ), + "valid_for_sharegroup_uuid": SpecField( + type=FieldType.string, + description=[ + "The UUID of the Image Share Group that this token is valid for." + ], + ), + "state": SpecField( + type=FieldType.string, + description=["The desired state of the target."], + choices=["present", "absent"], + required=True, + ), +} + +SPECDOC_META = SpecDocMeta( + description=["Manage an Image Share Group Token."], + requirements=global_requirements, + author=global_authors, + options=SPEC, + examples=docs.specdoc_examples, + return_values={ + "image_share_group_token": SpecReturnValue( + description="The Image Share Group Token in JSON serialized form.", + docs_url="https://techdocs.akamai.com/linode-api/reference/get-sharegroup-token", + type=FieldType.dict, + sample=docs.result_image_share_group_token_samples, + ), + "single_use_token": SpecReturnValue( + description="The single use token string to provide to a Image Share Group Producer " + "to be added to the share group.", + type=FieldType.string, + sample=docs.result_single_use_token_samples, + ), + }, +) + +DOCUMENTATION = r""" +""" +EXAMPLES = r""" +""" +RETURN = r""" +""" + + +class Module(LinodeModuleBase): + """Module for creating and destroying Image Share Group Tokens""" + + def __init__(self) -> None: + self.module_arg_spec = SPECDOC_META.ansible_spec + self.results = { + "changed": False, + "actions": [], + "image_share_group_token": None, + "single_use_token": None, + } + + super().__init__( + module_arg_spec=self.module_arg_spec, + required_if=[ + ("state", "present", ["valid_for_sharegroup_uuid"]), + ], + ) + + def _get_image_share_group_token_by_label( + self, label: str + ) -> Optional[ImageShareGroupToken]: + try: + return self.client.sharegroups.tokens( + ImageShareGroupToken.label == label + )[0] + except IndexError: + return None + except Exception as exception: + return self.fail( + msg="failed to get image share group token for label {0}: {1}".format( + label, exception + ) + ) + + def _create(self) -> Optional[ImageShareGroupToken]: + params = filter_null_values( + { + "label": self.module.params.get("label"), + "valid_for_sharegroup_uuid": self.module.params.get( + "valid_for_sharegroup_uuid" + ), + } + ) + + try: + token_obj, single_use_token = self.client.sharegroups.create_token( + **params + ) + self.results["single_use_token"] = single_use_token + return token_obj + except Exception as exception: + return self.fail( + msg="failed to create Image Share Group Token: {0}".format( + exception + ) + ) + + def _handle_present(self) -> None: + label = self.module.params.get("label") + requested_uuid = self.module.params.get("valid_for_sharegroup_uuid") + + token = self._get_image_share_group_token_by_label(label) + + if token: + token._api_get() + + existing_uuid = token.valid_for_sharegroup_uuid + + if requested_uuid != existing_uuid: + self.fail( + msg=( + "failed to update {} -> {}: valid_for_sharegroup_uuid " + "is a non-updatable field" + ).format(existing_uuid, requested_uuid) + ) + else: + token = self._create() + self.register_action( + "Created Image Share Group Token {0}".format(label) + ) + token._api_get() + + self.results["image_share_group_token"] = token._raw_json + + def _handle_absent(self) -> None: + label: str = self.module.params.get("label") + token = self._get_image_share_group_token_by_label(label) + + if token is not None: + self.results["image_share_group_token"] = token._raw_json + token.delete() + self.register_action( + "Deleted image share group token {0}".format(label) + ) + + def exec_module(self, **kwargs: Any) -> Optional[dict]: + """Entrypoint for Image Share Group Token module""" + state = kwargs.get("state") + + if state == "absent": + self._handle_absent() + return self.results + + self._handle_present() + + return self.results + + +def main() -> None: + """Constructs and calls the Image Share Group Token module""" + Module() + + +if __name__ == "__main__": + main() diff --git a/plugins/modules/image_share_group_token_info.py b/plugins/modules/image_share_group_token_info.py new file mode 100644 index 00000000..7d8ad58e --- /dev/null +++ b/plugins/modules/image_share_group_token_info.py @@ -0,0 +1,64 @@ +#!/usr/bin/python +# -*- coding: utf-8 -*- + +"""This module contains the functionality for the Image Share Group Token info module.""" + +from __future__ import absolute_import, division, print_function + +from ansible_collections.linode.cloud.plugins.module_utils.doc_fragments import ( + image_share_group_token_info as docs, +) +from ansible_collections.linode.cloud.plugins.module_utils.linode_common_info import ( + InfoModule, + InfoModuleAttr, + InfoModuleResult, +) +from ansible_collections.linode.cloud.plugins.module_utils.linode_helper import ( + safe_find, +) +from ansible_specdoc.objects import FieldType +from linode_api4 import ImageShareGroupToken + +module = InfoModule( + primary_result=InfoModuleResult( + field_name="image_share_group_token", + field_type=FieldType.dict, + display_name="Image Share Group Token", + docs_url="https://techdocs.akamai.com/linode-api/reference/get-sharegroup-token", + samples=docs.result_image_share_group_token_samples, + ), + attributes=[ + InfoModuleAttr( + display_name="Token UUID", + name="token_uuid", + type=FieldType.string, + get=lambda client, params: client.load( + ImageShareGroupToken, + params.get("token_uuid"), + )._raw_json, + ), + InfoModuleAttr( + display_name="label", + name="label", + type=FieldType.string, + get=lambda client, params: safe_find( + client.sharegroups.tokens, + ImageShareGroupToken.label == params.get("label"), + raise_not_found=True, + )._raw_json, + ), + ], + examples=docs.specdoc_examples, +) + +SPECDOC_META = module.spec + +DOCUMENTATION = r""" +""" +EXAMPLES = r""" +""" +RETURN = r""" +""" + +if __name__ == "__main__": + module.run() diff --git a/plugins/modules/image_share_group_token_list.py b/plugins/modules/image_share_group_token_list.py new file mode 100644 index 00000000..7aa0fdbe --- /dev/null +++ b/plugins/modules/image_share_group_token_list.py @@ -0,0 +1,34 @@ +#!/usr/bin/python +# -*- coding: utf-8 -*- + +"""This module allows users to list Image Share Group Tokens.""" + +from __future__ import absolute_import, division, print_function + +from ansible_collections.linode.cloud.plugins.module_utils.doc_fragments import ( + image_share_group_token_list as docs, +) +from ansible_collections.linode.cloud.plugins.module_utils.linode_common_list import ( + ListModule, +) + +module = ListModule( + result_display_name="Image Share Group Tokens", + result_field_name="image_share_group_tokens", + endpoint_template="/images/sharegroups/tokens", + result_docs_url="https://techdocs.akamai.com/linode-api/reference/get-user-tokens", + result_samples=docs.result_image_share_group_tokens_samples, + examples=docs.specdoc_examples, +) + +SPECDOC_META = module.spec + +DOCUMENTATION = r""" +""" +EXAMPLES = r""" +""" +RETURN = r""" +""" + +if __name__ == "__main__": + module.run() diff --git a/requirements.txt b/requirements.txt index 85c35e1a..119a88e8 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,3 +1,3 @@ -linode-api4>=5.38.0 +linode-api4>=5.39.0 polling==0.3.2 ansible-specdoc>=0.0.19 diff --git a/tests/integration/targets/consumer_image_share_group_image_list/tasks/main.yaml b/tests/integration/targets/consumer_image_share_group_image_list/tasks/main.yaml new file mode 100644 index 00000000..4a21e097 --- /dev/null +++ b/tests/integration/targets/consumer_image_share_group_image_list/tasks/main.yaml @@ -0,0 +1,218 @@ +- name: consumer_image_share_group_image_list + block: + - name: Normalize producer/consumer tokens + set_fact: + _producer_token: "{{ producer_api_token | default('', true) | trim }}" + _consumer_token: "{{ consumer_api_token | default('', true) | trim }}" + + - name: Skip test if producer/consumer tokens are missing + meta: end_play + when: _producer_token == '' or _consumer_token == '' + + - set_fact: + r: "{{ 1000000000 | random }}" + disallowed_image_regions: ["gb-lon", "au-mel", "sg-sin-2", "jp-tyo-3", "no-osl-1"] + + - name: List regions + linode.cloud.region_list: { } + register: all_regions + environment: + LINODE_API_TOKEN: "{{ producer_api_token }}" + + - set_fact: + capable_regions: '{{ ( all_regions.regions | selectattr("site_type", "equalto", "core") | selectattr("capabilities", "search", "Object Storage") | rejectattr("id", "in", disallowed_image_regions) | map(attribute="id") | list) }}' + + - name: Create instance one to image + linode.cloud.instance: + label: 'ansible-test-{{ r }}-one' + region: '{{ capable_regions[1] }}' + type: g6-standard-1 + image: linode/alpine3.19 + state: present + firewall_id: '{{ firewall_id }}' + register: instance_one_create + environment: + LINODE_API_TOKEN: "{{ producer_api_token }}" + + - name: Create image one + linode.cloud.image: + disk_id: "{{ instance_one_create.disks.0.id }}" + label: image-one + description: first image + state: present + register: image_one_create + environment: + LINODE_API_TOKEN: "{{ producer_api_token }}" + + - name: Create instance two to image + linode.cloud.instance: + label: 'ansible-test-{{ r }}-two' + region: '{{ capable_regions[1] }}' + type: g6-standard-1 + image: linode/alpine3.19 + state: present + firewall_id: '{{ firewall_id }}' + register: instance_two_create + environment: + LINODE_API_TOKEN: "{{ producer_api_token }}" + + - name: Create image two + linode.cloud.image: + disk_id: "{{ instance_two_create.disks.0.id }}" + label: image-two + description: second-image + state: present + register: image_two_create + environment: + LINODE_API_TOKEN: "{{ producer_api_token }}" + + - name: Create image share group (producer task) + linode.cloud.image_share_group: + label: "ansible-test-{{ r }}" + images: + - id: '{{ image_one_create.image.id }}' + label: 'image-one' + description: 'first image' + - id: '{{ image_two_create.image.id }}' + label: 'image-two' + description: 'second image' + state: present + register: share_group_create + environment: + LINODE_API_TOKEN: "{{ producer_api_token }}" + + - name: Create image share group token (consumer task) + linode.cloud.image_share_group_token: + label: "ansible-test-{{ r }}" + valid_for_sharegroup_uuid: "{{ share_group_create.image_share_group.uuid }}" + state: present + register: share_group_token_create + environment: + LINODE_API_TOKEN: "{{ consumer_api_token }}" + + - name: Add consumer as member to share group (producer task) + linode.cloud.image_share_group_member: + label: "ansible-test-{{ r }}" + token: "{{ share_group_token_create.single_use_token }}" + sharegroup_id: "{{ share_group_create.image_share_group.id }}" + state: present + register: share_group_member_add + environment: + LINODE_API_TOKEN: "{{ producer_api_token }}" + + - name: List image share group images with no filter + linode.cloud.consumer_image_share_group_image_list: + token_uuid: "{{ share_group_token_create.image_share_group_token.token_uuid }}" + count: 2 + register: no_filter + environment: + LINODE_API_TOKEN: "{{ consumer_api_token }}" + + - name: Assert consumer_image_share_group_image_list with no filter + assert: + that: + - no_filter.image_share_group_images | length == 2 + + - name: List image share group images with filter on label + linode.cloud.consumer_image_share_group_image_list: + token_uuid: "{{ share_group_token_create.image_share_group_token.token_uuid }}" + count: 1 + order_by: created + order: desc + filters: + - name: label + values: 'image-one' + register: filter + environment: + LINODE_API_TOKEN: "{{ consumer_api_token }}" + + - name: Assert consumer_image_share_group_image_list with filter on label + assert: + that: + - filter.image_share_group_images | length == 1 + - filter.image_share_group_images[0].label == "image-one" + + - name: Remove remaining images in share group + linode.cloud.image_share_group: + label: '{{ share_group_create.image_share_group.label }}' + images: [ ] + state: present + register: share_group_images_removed + environment: + LINODE_API_TOKEN: "{{ producer_api_token }}" + + - name: Assert images removed from share group + assert: + that: + - share_group_images_removed.image_share_group.images | length == 0 + - share_group_images_removed.image_share_group.images_count == 0 + + always: + - ignore_errors: yes + block: + - name: Delete image share group member + linode.cloud.image_share_group_member: + label: "ansible-test-{{ r }}" + sharegroup_id: "{{ share_group_create.image_share_group.id }}" + state: absent + when: + - share_group_create is defined + - share_group_create.image_share_group is defined + environment: + LINODE_API_TOKEN: "{{ producer_api_token }}" + + - name: Delete image share group token + linode.cloud.image_share_group_token: + label: "{{ share_group_token_create.image_share_group_token.label }}" + state: absent + when: + - share_group_token_create is defined + - share_group_token_create.image_share_group_token is defined + environment: + LINODE_API_TOKEN: "{{ consumer_api_token }}" + + - name: Delete image share group + linode.cloud.image_share_group: + label: "{{ share_group_create.image_share_group.label }}" + state: absent + when: + - share_group_create is defined + - share_group_create.image_share_group is defined + environment: + LINODE_API_TOKEN: "{{ producer_api_token }}" + + - name: Delete image + linode.cloud.image: + label: '{{ image_one_create.image.label }}' + state: absent + environment: + LINODE_API_TOKEN: "{{ producer_api_token }}" + + - name: Delete image + linode.cloud.image: + label: '{{ image_two_create.image.label }}' + state: absent + environment: + LINODE_API_TOKEN: "{{ producer_api_token }}" + + - name: Delete instance + linode.cloud.instance: + label: '{{ instance_one_create.instance.label }}' + state: absent + environment: + LINODE_API_TOKEN: "{{ producer_api_token }}" + + - name: Delete instance + linode.cloud.instance: + label: '{{ instance_two_create.instance.label }}' + state: absent + environment: + LINODE_API_TOKEN: "{{ producer_api_token }}" + + environment: + LINODE_UA_PREFIX: "{{ ua_prefix }}" + LINODE_PRODUCER_API_TOKEN: "{{ producer_api_token }}" + LINODE_CONSUMER_API_TOKEN: "{{ consumer_api_token }}" + LINODE_API_URL: "{{ api_url }}" + LINODE_API_VERSION: "{{ api_version }}" + LINODE_CA: "{{ ca_file or '' }}" \ No newline at end of file diff --git a/tests/integration/targets/consumer_image_share_group_info/tasks/main.yaml b/tests/integration/targets/consumer_image_share_group_info/tasks/main.yaml new file mode 100644 index 00000000..b05e0c16 --- /dev/null +++ b/tests/integration/targets/consumer_image_share_group_info/tasks/main.yaml @@ -0,0 +1,94 @@ +- name: consumer_image_share_group_info + block: + - name: Normalize producer/consumer tokens + set_fact: + _producer_token: "{{ producer_api_token | default('', true) | trim }}" + _consumer_token: "{{ consumer_api_token | default('', true) | trim }}" + + - name: Skip test if producer/consumer tokens are missing + meta: end_play + when: _producer_token == '' or _consumer_token == '' + + - set_fact: + r: "{{ 1000000000 | random }}" + + - name: Create image share group (producer task) + linode.cloud.image_share_group: + label: "ansible-test-{{ r }}" + state: present + register: share_group_create + environment: + LINODE_API_TOKEN: "{{ producer_api_token }}" + + - name: Create image share group token (consumer task) + linode.cloud.image_share_group_token: + label: "ansible-test-{{ r }}" + valid_for_sharegroup_uuid: "{{ share_group_create.image_share_group.uuid }}" + state: present + register: share_group_token_create + environment: + LINODE_API_TOKEN: "{{ consumer_api_token }}" + + - name: Add consumer as member to share group (producer task) + linode.cloud.image_share_group_member: + label: "ansible-test-{{ r }}" + token: "{{ share_group_token_create.single_use_token }}" + sharegroup_id: "{{ share_group_create.image_share_group.id }}" + state: present + register: share_group_member_add + environment: + LINODE_API_TOKEN: "{{ producer_api_token }}" + + - name: Get image share group info (consumer task) + linode.cloud.consumer_image_share_group_info: + token_uuid: "{{ share_group_token_create.image_share_group_token.token_uuid }}" + register: consumer_share_group_info + environment: + LINODE_API_TOKEN: "{{ consumer_api_token }}" + + - name: Assert consumer image share group info is correct + assert: + that: + - consumer_share_group_info.image_share_group.label == "ansible-test-" ~ r + + always: + - ignore_errors: yes + block: + - name: Delete image share group member + linode.cloud.image_share_group_member: + label: "ansible-test-{{ r }}" + sharegroup_id: "{{ share_group_create.image_share_group.id }}" + state: absent + when: + - share_group_create is defined + - share_group_create.image_share_group is defined + environment: + LINODE_API_TOKEN: "{{ producer_api_token }}" + + - name: Delete image share group token + linode.cloud.image_share_group_token: + label: "{{ share_group_token_create.image_share_group_token.label }}" + state: absent + when: + - share_group_token_create is defined + - share_group_token_create.image_share_group_token is defined + environment: + LINODE_API_TOKEN: "{{ consumer_api_token }}" + + - name: Delete image share group + linode.cloud.image_share_group: + label: "{{ share_group_create.image_share_group.label }}" + state: absent + when: + - share_group_create is defined + - share_group_create.image_share_group is defined + environment: + LINODE_API_TOKEN: "{{ producer_api_token }}" + + environment: + LINODE_UA_PREFIX: "{{ ua_prefix }}" + LINODE_PRODUCER_API_TOKEN: "{{ producer_api_token }}" + LINODE_CONSUMER_API_TOKEN: "{{ consumer_api_token }}" + LINODE_API_URL: "{{ api_url }}" + LINODE_API_VERSION: "{{ api_version }}" + LINODE_CA: "{{ ca_file or '' }}" \ No newline at end of file diff --git a/tests/integration/targets/image_basic/tasks/main.yaml b/tests/integration/targets/image_basic/tasks/main.yaml index 69e6ea57..4f501215 100644 --- a/tests/integration/targets/image_basic/tasks/main.yaml +++ b/tests/integration/targets/image_basic/tasks/main.yaml @@ -41,6 +41,8 @@ - image_create.image.capabilities[0] == 'cloud-init' - image_create.image.tags | length == 2 - image_create.image.size == image_create.image.total_size + - image_create.image.is_shared == false + - image_create.image.image_sharing.shared_with.sharegroup_count == 0 - name: Get image_info by ID linode.cloud.image_info: @@ -53,6 +55,8 @@ - info_id.image.status == 'available' - info_id.image.description == 'cool' - info_id.image.id == image_create.image.id + - info_id.image.is_shared == false + - info_id.image.image_sharing.shared_with.sharegroup_count == 0 - name: Get image_info by label linode.cloud.image_info: diff --git a/tests/integration/targets/image_share_group_basic/tasks/main.yaml b/tests/integration/targets/image_share_group_basic/tasks/main.yaml new file mode 100644 index 00000000..1b338094 --- /dev/null +++ b/tests/integration/targets/image_share_group_basic/tasks/main.yaml @@ -0,0 +1,234 @@ +- name: image_share_group_basic + block: + - set_fact: + r: "{{ 1000000000 | random }}" + disallowed_image_regions: ["gb-lon", "au-mel", "sg-sin-2", "jp-tyo-3", "no-osl-1"] + + - name: List regions + linode.cloud.region_list: { } + register: all_regions + + - set_fact: + capable_regions: '{{ ( all_regions.regions | selectattr("site_type", "equalto", "core") | selectattr("capabilities", "search", "Object Storage") | rejectattr("id", "in", disallowed_image_regions) | map(attribute="id") | list) }}' + + - name: Create instance one to image + linode.cloud.instance: + label: 'ansible-test-{{ r }}-one' + region: '{{ capable_regions[1] }}' + type: g6-standard-1 + image: linode/alpine3.19 + state: present + firewall_id: '{{ firewall_id }}' + register: instance_one_create + + - name: Create image one + linode.cloud.image: + disk_id: "{{ instance_one_create.disks.0.id }}" + label: image-one + description: first image + state: present + register: image_one_create + + - name: Create instance two to image + linode.cloud.instance: + label: 'ansible-test-{{ r }}-two' + region: '{{ capable_regions[1] }}' + type: g6-standard-1 + image: linode/alpine3.19 + state: present + firewall_id: '{{ firewall_id }}' + register: instance_two_create + + - name: Create image two + linode.cloud.image: + disk_id: "{{ instance_two_create.disks.0.id }}" + label: image-two + description: second-image + state: present + register: image_two_create + + - name: Assert images are created + assert: + that: + - image_one_create.image.status == 'available' + - image_two_create.image.status == 'available' + - image_one_create.image.image_sharing.shared_with.sharegroup_count == 0 + - image_two_create.image.image_sharing.shared_with.sharegroup_count == 0 + + - name: Create an image share group without any images + linode.cloud.image_share_group: + label: 'ansible-share-group-empty' + description: 'share group desc' + state: present + register: share_group_empty_create + + - name: Assert image share group is created + assert: + that: + - share_group_empty_create.image_share_group.label == 'ansible-share-group-empty' + - share_group_empty_create.image_share_group.description == 'share group desc' + - share_group_empty_create.image_share_group.images_count == 0 + + - name: Add images to share group + linode.cloud.image_share_group: + label: '{{ share_group_empty_create.image_share_group.label }}' + images: + - id: '{{ image_one_create.image.id }}' + label: 'image-one' + description: 'first image' + - id: '{{ image_two_create.image.id }}' + label: 'image-two' + state: present + register: share_group_empty_add_images + + - name: Select images for assertions + set_fact: + img_one: "{{ share_group_empty_add_images.image_share_group.images | selectattr('id', 'equalto', image_one_create.image.id) | first }}" + img_two: "{{ share_group_empty_add_images.image_share_group.images | selectattr('id', 'equalto', image_two_create.image.id) | first }}" + + - name: Assert images added to share group + assert: + that: + - img_one.label == 'image-one' + - img_one.description == 'first image' + - img_two.label == 'image-two' + - img_two.description is none + - share_group_empty_add_images.image_share_group.images | length == 2 + - share_group_empty_add_images.image_share_group.images_count == 2 + + - name: Update images in share group + linode.cloud.image_share_group: + label: '{{ share_group_empty_add_images.image_share_group.label }}' + images: + - id: '{{ image_one_create.image.id }}' + label: 'image-one-updated' + description: 'first image updated' + state: present + register: share_group_empty_update_images + + - name: Select updated images for assertions + set_fact: + img_one_updated: "{{ share_group_empty_update_images.image_share_group.images | selectattr('id', 'equalto', image_one_create.image.id) | first }}" + img_two_removed: "{{ share_group_empty_update_images.image_share_group.images | selectattr('id', 'equalto', image_two_create.image.id) | list }}" + + - name: Assert images updated in share group + assert: + that: + - img_one_updated.label == 'image-one-updated' + - img_one_updated.description == 'first image updated' + - img_two_removed | length == 0 + - share_group_empty_update_images.image_share_group.images | length == 1 + - share_group_empty_update_images.image_share_group.images_count == 1 + + - name: Remove remaining images in share group + linode.cloud.image_share_group: + label: '{{ share_group_empty_update_images.image_share_group.label }}' + images: [] + state: present + register: share_group_empty_images_removed + + - name: Select removed images for assertions + set_fact: + img_one_removed: "{{ share_group_empty_images_removed.image_share_group.images | selectattr('id', 'equalto', image_one_create.image.id) | list }}" + img_two_removed: "{{ share_group_empty_images_removed.image_share_group.images | selectattr('id', 'equalto', image_two_create.image.id) | list }}" + + - name: Assert images removed from share group + assert: + that: + - img_one_removed | length == 0 + - img_two_removed | length == 0 + - share_group_empty_images_removed.image_share_group.images | length == 0 + - share_group_empty_images_removed.image_share_group.images_count == 0 + + - name: Update image share group details + linode.cloud.image_share_group: + label: '{{ share_group_empty_images_removed.image_share_group.label }}' + description: 'share group desc updated' + state: present + register: share_group_update_details + + - name: Assert image share group details updated + assert: + that: + - share_group_update_details.image_share_group.description == 'share group desc updated' + + - name: Create an image share group with an image + linode.cloud.image_share_group: + label: 'ansible-share-group-populated' + images: + - id: '{{ image_one_create.image.id }}' + label: 'image-one' + description: 'first image' + state: present + register: share_group_populated_create + + - name: Assert image share group is created + assert: + that: + - share_group_populated_create.image_share_group.label == 'ansible-share-group-populated' + - share_group_populated_create.image_share_group.images_count == 1 + + - name: Check that removing images field leaves images unchanged + linode.cloud.image_share_group: + label: '{{ share_group_populated_create.image_share_group.label }}' + state: present + register: share_group_populated_no_change + + - name: Assert images unchanged in share group + assert: + that: + - share_group_populated_no_change.image_share_group.images | length == 1 + - share_group_populated_no_change.image_share_group.images_count == 1 + + - name: Remove remaining images in share group + linode.cloud.image_share_group: + label: '{{ share_group_populated_create.image_share_group.label }}' + images: [] + state: present + register: share_group_populated_images_removed + + - name: Assert images removed from share group + assert: + that: + - share_group_populated_images_removed.image_share_group.images | length == 0 + - share_group_populated_images_removed.image_share_group.images_count == 0 + + always: + - ignore_errors: yes + block: + - name: Delete image share group + linode.cloud.image_share_group: + label: '{{ share_group_update_details.image_share_group.label }}' + state: absent + + - name: Delete image share group + linode.cloud.image_share_group: + label: '{{ share_group_populated_images_removed.image_share_group.label }}' + state: absent + + - name: Delete image + linode.cloud.image: + label: '{{ image_one_create.image.label }}' + state: absent + + - name: Delete image + linode.cloud.image: + label: '{{ image_two_create.image.label }}' + state: absent + + - name: Delete instance + linode.cloud.instance: + label: '{{ instance_one_create.instance.label }}' + state: absent + + - name: Delete instance + linode.cloud.instance: + label: '{{ instance_two_create.instance.label }}' + state: absent + + environment: + LINODE_UA_PREFIX: '{{ ua_prefix }}' + LINODE_API_TOKEN: '{{ api_token }}' + LINODE_API_URL: '{{ api_url }}' + LINODE_API_VERSION: '{{ api_version }}' + LINODE_CA: '{{ ca_file or "" }}' \ No newline at end of file diff --git a/tests/integration/targets/image_share_group_image_list/tasks/main.yaml b/tests/integration/targets/image_share_group_image_list/tasks/main.yaml new file mode 100644 index 00000000..4572cb50 --- /dev/null +++ b/tests/integration/targets/image_share_group_image_list/tasks/main.yaml @@ -0,0 +1,145 @@ +- name: image_share_group_image_list + block: + - set_fact: + r: "{{ 1000000000 | random }}" + disallowed_image_regions: ["gb-lon", "au-mel", "sg-sin-2", "jp-tyo-3", "no-osl-1"] + + - name: List regions + linode.cloud.region_list: { } + register: all_regions + + - set_fact: + capable_regions: '{{ ( all_regions.regions | selectattr("site_type", "equalto", "core") | selectattr("capabilities", "search", "Object Storage") | rejectattr("id", "in", disallowed_image_regions) | map(attribute="id") | list) }}' + + - name: Create instance one to image + linode.cloud.instance: + label: 'ansible-test-{{ r }}-one' + region: '{{ capable_regions[1] }}' + type: g6-standard-1 + image: linode/alpine3.19 + state: present + firewall_id: '{{ firewall_id }}' + register: instance_one_create + + - name: Create image one + linode.cloud.image: + disk_id: "{{ instance_one_create.disks.0.id }}" + label: image-one + description: first image + state: present + register: image_one_create + + - name: Create instance two to image + linode.cloud.instance: + label: 'ansible-test-{{ r }}-two' + region: '{{ capable_regions[1] }}' + type: g6-standard-1 + image: linode/alpine3.19 + state: present + firewall_id: '{{ firewall_id }}' + register: instance_two_create + + - name: Create image two + linode.cloud.image: + disk_id: "{{ instance_two_create.disks.0.id }}" + label: image-two + description: second-image + state: present + register: image_two_create + + - name: Assert images are created + assert: + that: + - image_one_create.image.status == 'available' + - image_two_create.image.status == 'available' + - image_one_create.image.image_sharing.shared_with.sharegroup_count == 0 + - image_two_create.image.image_sharing.shared_with.sharegroup_count == 0 + + - name: Create an image share group + linode.cloud.image_share_group: + label: "ansible-test-{{ r }}" + images: + - id: '{{ image_one_create.image.id }}' + label: 'image-one' + description: 'first image' + - id: '{{ image_two_create.image.id }}' + label: 'image-two' + description: 'second image' + state: present + register: share_group_create + + - name: List image share group images with no filter + linode.cloud.image_share_group_image_list: + sharegroup_id: "{{ share_group_create.image_share_group.id }}" + count: 2 + register: no_filter + + - name: Assert image_share_group_image_list with no filter + assert: + that: + - no_filter.image_share_group_images | length == 2 + + - name: List image share group images with filter on label + linode.cloud.image_share_group_image_list: + sharegroup_id: "{{ share_group_create.image_share_group.id }}" + count: 1 + order_by: created + order: desc + filters: + - name: label + values: 'image-one' + register: filter + + - name: Assert image_share_group_image_list with filter on label + assert: + that: + - filter.image_share_group_images | length == 1 + - filter.image_share_group_images[0].label == "image-one" + + - name: Remove remaining images in share group + linode.cloud.image_share_group: + label: '{{ share_group_create.image_share_group.label }}' + images: [] + state: present + register: share_group_images_removed + + - name: Assert images removed from share group + assert: + that: + - share_group_images_removed.image_share_group.images | length == 0 + - share_group_images_removed.image_share_group.images_count == 0 + + always: + - ignore_errors: yes + block: + - name: Delete image share group + linode.cloud.image_share_group: + label: '{{ share_group_images_removed.image_share_group.label }}' + state: absent + + - name: Delete image + linode.cloud.image: + label: '{{ image_one_create.image.label }}' + state: absent + + - name: Delete image + linode.cloud.image: + label: '{{ image_two_create.image.label }}' + state: absent + + - name: Delete instance + linode.cloud.instance: + label: '{{ instance_one_create.instance.label }}' + state: absent + + - name: Delete instance + linode.cloud.instance: + label: '{{ instance_two_create.instance.label }}' + state: absent + + environment: + LINODE_UA_PREFIX: '{{ ua_prefix }}' + LINODE_API_TOKEN: '{{ api_token }}' + LINODE_API_URL: '{{ api_url }}' + LINODE_API_VERSION: '{{ api_version }}' + LINODE_CA: '{{ ca_file or "" }}' diff --git a/tests/integration/targets/image_share_group_info/tasks/main.yaml b/tests/integration/targets/image_share_group_info/tasks/main.yaml new file mode 100644 index 00000000..4d9192d3 --- /dev/null +++ b/tests/integration/targets/image_share_group_info/tasks/main.yaml @@ -0,0 +1,35 @@ +- name: image_share_group_info + block: + - set_fact: + r: "{{ 1000000000 | random }}" + + - name: Create an image share group + linode.cloud.image_share_group: + label: 'ansible-test-{{ r }}' + state: present + register: share_group_create + + - name: Get information about the Image Share Group + linode.cloud.image_share_group_info: + label: "{{ share_group_create.image_share_group.label }}" + register: share_group_info + + - name: Assert Image Share Group information + assert: + that: + - share_group_info.image_share_group.label == "ansible-test-" ~ r + + always: + - ignore_errors: yes + block: + - name: Delete image share group + linode.cloud.image_share_group: + label: '{{ share_group_create.image_share_group.label }}' + state: absent + + environment: + LINODE_UA_PREFIX: '{{ ua_prefix }}' + LINODE_API_TOKEN: '{{ api_token }}' + LINODE_API_URL: '{{ api_url }}' + LINODE_API_VERSION: '{{ api_version }}' + LINODE_CA: '{{ ca_file or "" }}' \ No newline at end of file diff --git a/tests/integration/targets/image_share_group_list/tasks/main.yaml b/tests/integration/targets/image_share_group_list/tasks/main.yaml new file mode 100644 index 00000000..dfaf93b1 --- /dev/null +++ b/tests/integration/targets/image_share_group_list/tasks/main.yaml @@ -0,0 +1,156 @@ +- name: image_share_group_list + block: + - set_fact: + r: "{{ 1000000000 | random }}" + disallowed_image_regions: [ "gb-lon", "au-mel", "sg-sin-2", "jp-tyo-3", "no-osl-1" ] + + - name: List regions + linode.cloud.region_list: { } + register: all_regions + + - set_fact: + capable_regions: '{{ ( all_regions.regions | selectattr("site_type", "equalto", "core") | selectattr("capabilities", "search", "Object Storage") | rejectattr("id", "in", disallowed_image_regions) | map(attribute="id") | list) }}' + + - name: Create an instance to image + linode.cloud.instance: + label: 'ansible-test-{{ r }}' + region: '{{ capable_regions[1] }}' + type: g6-standard-1 + image: linode/alpine3.19 + state: present + firewall_id: '{{ firewall_id }}' + register: instance_create + + - name: Create an image + linode.cloud.image: + disk_id: "{{ instance_create.disks.0.id }}" + label: 'ansible-test-{{ r }}' + state: present + register: image_create + + - name: Create an image share group + linode.cloud.image_share_group: + label: 'ansible-test-{{ r }}-one' + images: + - id: '{{ image_create.image.id }}' + state: present + register: share_group_one_create + + - name: Create another image share group + linode.cloud.image_share_group: + label: 'ansible-test-{{ r }}-two' + images: + - id: '{{ image_create.image.id }}' + state: present + register: share_group_two_create + + - name: Create another image share group + linode.cloud.image_share_group: + label: 'ansible-test-{{ r }}-three' + state: present + register: share_group_three_create + + - name: List Image Share Groups with no filter + linode.cloud.image_share_group_list: + count: 3 + register: no_filter + + - name: Assert image_share_group_list with no filter + assert: + that: + - no_filter.image_share_groups | length == 3 + + - name: List Image Share Groups with filter on label + linode.cloud.image_share_group_list: + count: 1 + order_by: created + order: desc + filters: + - name: label + values: 'ansible-test-{{ r }}-one' + register: filter + + - name: Assert image_share_group_list with filter on label + assert: + that: + - filter.image_share_groups | length == 1 + - filter.image_share_groups[0].label == "ansible-test-" ~ r ~ "-one" + + - name: List Image Share Groups by Image ID with no filter + linode.cloud.image_share_group_list: + count: 2 + order_by: created + order: desc + image_id: '{{ image_create.image.id }}' + register: by_image_id_no_filter + + - name: Assert image_share_group_list by Image ID with no filter + assert: + that: + - by_image_id_no_filter.image_share_groups | length == 2 + + - name: List Image Share Groups by Image ID with filter on label + linode.cloud.image_share_group_list: + count: 1 + order_by: created + order: desc + image_id: '{{ image_create.image.id }}' + filters: + - name: label + values: 'ansible-test-{{ r }}-one' + register: by_image_id_filter + + - name: Assert image_share_group_list by Image ID with filter on label + assert: + that: + - by_image_id_filter.image_share_groups | length == 1 + - by_image_id_filter.image_share_groups[0].label == "ansible-test-" ~ r ~ "-one" + + - name: Remove remaining images in share group one + linode.cloud.image_share_group: + label: '{{ share_group_one_create.image_share_group.label }}' + images: [] + state: present + register: share_group_one_empty + + - name: Remove remaining images in share group two + linode.cloud.image_share_group: + label: '{{ share_group_two_create.image_share_group.label }}' + images: [ ] + state: present + register: share_group_two_empty + + always: + - ignore_errors: yes + block: + - name: Delete image + linode.cloud.image: + label: '{{ image_create.image.label }}' + state: absent + + - name: Delete instance + linode.cloud.instance: + label: '{{ instance_create.instance.label }}' + state: absent + + - name: Delete image share group + linode.cloud.image_share_group: + label: '{{ share_group_one_create.image_share_group.label }}' + state: absent + + - name: Delete image share group + linode.cloud.image_share_group: + label: '{{ share_group_two_create.image_share_group.label }}' + state: absent + + - name: Delete image share group + linode.cloud.image_share_group: + label: '{{ share_group_three_create.image_share_group.label }}' + state: absent + + environment: + LINODE_UA_PREFIX: '{{ ua_prefix }}' + LINODE_API_TOKEN: '{{ api_token }}' + LINODE_API_URL: '{{ api_url }}' + LINODE_API_VERSION: '{{ api_version }}' + LINODE_CA: '{{ ca_file or "" }}' diff --git a/tests/integration/targets/image_share_group_member/tasks/main.yaml b/tests/integration/targets/image_share_group_member/tasks/main.yaml new file mode 100644 index 00000000..32850727 --- /dev/null +++ b/tests/integration/targets/image_share_group_member/tasks/main.yaml @@ -0,0 +1,102 @@ +- name: image_share_group_member + block: + - name: Normalize producer/consumer tokens + set_fact: + _producer_token: "{{ producer_api_token | default('', true) | trim }}" + _consumer_token: "{{ consumer_api_token | default('', true) | trim }}" + + - name: Skip test if producer/consumer tokens are missing + meta: end_play + when: _producer_token == '' or _consumer_token == '' + + - set_fact: + r: "{{ 1000000000 | random }}" + + - name: Create image share group (producer task) + linode.cloud.image_share_group: + label: "ansible-test-{{ r }}" + state: present + register: share_group_create + environment: + LINODE_API_TOKEN: "{{ producer_api_token }}" + + - name: Assert image share group is created + assert: + that: + - share_group_create.image_share_group.label == "ansible-test-" ~ r + - share_group_create.image_share_group.images_count == 0 + - share_group_create.image_share_group.members_count == 0 + + - name: Create image share group token (consumer task) + linode.cloud.image_share_group_token: + label: "ansible-test-{{ r }}" + valid_for_sharegroup_uuid: "{{ share_group_create.image_share_group.uuid }}" + state: present + register: share_group_token_create + environment: + LINODE_API_TOKEN: "{{ consumer_api_token }}" + + - name: Assert image share group token is created + assert: + that: + - share_group_token_create.image_share_group_token.label == "ansible-test-" ~ r + - share_group_token_create.image_share_group_token.valid_for_sharegroup_uuid == share_group_create.image_share_group.uuid + - share_group_token_create.single_use_token is defined + - share_group_token_create.single_use_token | length > 0 + + - name: Add consumer as member to share group (producer task) + linode.cloud.image_share_group_member: + label: "ansible-test-{{ r }}" + token: "{{ share_group_token_create.single_use_token }}" + sharegroup_id: "{{ share_group_create.image_share_group.id }}" + state: present + register: share_group_member_add + environment: + LINODE_API_TOKEN: "{{ producer_api_token }}" + + - name: Assert image share group member is added + assert: + that: + - share_group_member_add.image_share_group_member.label == "ansible-test-" ~ r + + always: + - ignore_errors: yes + block: + - name: Delete image share group member + linode.cloud.image_share_group_member: + label: "ansible-test-{{ r }}" + sharegroup_id: "{{ share_group_create.image_share_group.id }}" + state: absent + when: + - share_group_create is defined + - share_group_create.image_share_group is defined + environment: + LINODE_API_TOKEN: "{{ producer_api_token }}" + + - name: Delete image share group token + linode.cloud.image_share_group_token: + label: "{{ share_group_token_create.image_share_group_token.label }}" + state: absent + when: + - share_group_token_create is defined + - share_group_token_create.image_share_group_token is defined + environment: + LINODE_API_TOKEN: "{{ consumer_api_token }}" + + - name: Delete image share group + linode.cloud.image_share_group: + label: "{{ share_group_create.image_share_group.label }}" + state: absent + when: + - share_group_create is defined + - share_group_create.image_share_group is defined + environment: + LINODE_API_TOKEN: "{{ producer_api_token }}" + + environment: + LINODE_UA_PREFIX: "{{ ua_prefix }}" + LINODE_PRODUCER_API_TOKEN: "{{ producer_api_token }}" + LINODE_CONSUMER_API_TOKEN: "{{ consumer_api_token }}" + LINODE_API_URL: "{{ api_url }}" + LINODE_API_VERSION: "{{ api_version }}" + LINODE_CA: "{{ ca_file or '' }}" \ No newline at end of file diff --git a/tests/integration/targets/image_share_group_member_info/tasks/main.yaml b/tests/integration/targets/image_share_group_member_info/tasks/main.yaml new file mode 100644 index 00000000..ddad9ff0 --- /dev/null +++ b/tests/integration/targets/image_share_group_member_info/tasks/main.yaml @@ -0,0 +1,108 @@ +- name: image_share_group_member_info + block: + - name: Normalize producer/consumer tokens + set_fact: + _producer_token: "{{ producer_api_token | default('', true) | trim }}" + _consumer_token: "{{ consumer_api_token | default('', true) | trim }}" + + - name: Skip test if producer/consumer tokens are missing + meta: end_play + when: _producer_token == '' or _consumer_token == '' + + - set_fact: + r: "{{ 1000000000 | random }}" + + - name: Create image share group (producer task) + linode.cloud.image_share_group: + label: "ansible-test-{{ r }}" + state: present + register: share_group_create + environment: + LINODE_API_TOKEN: "{{ producer_api_token }}" + + - name: Create image share group token (consumer task) + linode.cloud.image_share_group_token: + label: "ansible-test-{{ r }}" + valid_for_sharegroup_uuid: "{{ share_group_create.image_share_group.uuid }}" + state: present + register: share_group_token_create + environment: + LINODE_API_TOKEN: "{{ consumer_api_token }}" + + - name: Add consumer as member to share group (producer task) + linode.cloud.image_share_group_member: + label: "ansible-test-{{ r }}" + token: "{{ share_group_token_create.single_use_token }}" + sharegroup_id: "{{ share_group_create.image_share_group.id }}" + state: present + register: share_group_member_add + environment: + LINODE_API_TOKEN: "{{ producer_api_token }}" + + - name: Get information about the Image Share Group Member by label + linode.cloud.image_share_group_member_info: + sharegroup_id: "{{ share_group_create.image_share_group.id }}" + label: "{{ share_group_member_add.image_share_group_member.label }}" + register: share_group_member_info_label + environment: + LINODE_API_TOKEN: "{{ producer_api_token }}" + + - name: Assert Image Share Group Member information + assert: + that: + - share_group_member_info_label.image_share_group_member.label == "ansible-test-" ~ r + + - name: Get information about the Image Share Group Member by token_uuid + linode.cloud.image_share_group_member_info: + sharegroup_id: "{{ share_group_create.image_share_group.id }}" + token_uuid: "{{ share_group_member_add.image_share_group_member.token_uuid }}" + register: share_group_member_info_token_uuid + environment: + LINODE_API_TOKEN: "{{ producer_api_token }}" + + - name: Assert Image Share Group Member information + assert: + that: + - share_group_member_info_token_uuid.image_share_group_member.label == "ansible-test-" ~ r + + always: + - ignore_errors: yes + block: + - name: Delete image share group member + linode.cloud.image_share_group_member: + label: "ansible-test-{{ r }}" + sharegroup_id: "{{ share_group_create.image_share_group.id }}" + state: absent + when: + - share_group_create is defined + - share_group_create.image_share_group is defined + environment: + LINODE_API_TOKEN: "{{ producer_api_token }}" + + - name: Delete image share group token + linode.cloud.image_share_group_token: + label: "{{ share_group_token_create.image_share_group_token.label }}" + state: absent + when: + - share_group_token_create is defined + - share_group_token_create.image_share_group_token is defined + environment: + LINODE_API_TOKEN: "{{ consumer_api_token }}" + + - name: Delete image share group + linode.cloud.image_share_group: + label: "{{ share_group_create.image_share_group.label }}" + state: absent + when: + - share_group_create is defined + - share_group_create.image_share_group is defined + environment: + LINODE_API_TOKEN: "{{ producer_api_token }}" + + environment: + LINODE_UA_PREFIX: "{{ ua_prefix }}" + LINODE_PRODUCER_API_TOKEN: "{{ producer_api_token }}" + LINODE_CONSUMER_API_TOKEN: "{{ consumer_api_token }}" + LINODE_API_URL: "{{ api_url }}" + LINODE_API_VERSION: "{{ api_version }}" + LINODE_CA: "{{ ca_file or '' }}" \ No newline at end of file diff --git a/tests/integration/targets/image_share_group_member_list/tasks/main.yaml b/tests/integration/targets/image_share_group_member_list/tasks/main.yaml new file mode 100644 index 00000000..cd2166ad --- /dev/null +++ b/tests/integration/targets/image_share_group_member_list/tasks/main.yaml @@ -0,0 +1,154 @@ +- name: image_share_group_member_list + block: + - name: Normalize producer/consumer tokens + set_fact: + _producer_token: "{{ producer_api_token | default('', true) | trim }}" + _consumer_token: "{{ consumer_api_token | default('', true) | trim }}" + + - name: Skip test if producer/consumer tokens are missing + meta: end_play + when: _producer_token == '' or _consumer_token == '' + + - set_fact: + r: "{{ 1000000000 | random }}" + + - name: Create image share group (producer task) + linode.cloud.image_share_group: + label: "ansible-test-{{ r }}" + state: present + register: share_group_create + environment: + LINODE_API_TOKEN: "{{ producer_api_token }}" + + - name: Create image share group token (consumer task) + linode.cloud.image_share_group_token: + label: "ansible-test-{{ r }}-one" + valid_for_sharegroup_uuid: "{{ share_group_create.image_share_group.uuid }}" + state: present + register: share_group_token_create_one + environment: + LINODE_API_TOKEN: "{{ consumer_api_token }}" + + - name: Create another image share group token (consumer task) + linode.cloud.image_share_group_token: + label: "ansible-test-{{ r }}-two" + valid_for_sharegroup_uuid: "{{ share_group_create.image_share_group.uuid }}" + state: present + register: share_group_token_create_two + environment: + LINODE_API_TOKEN: "{{ consumer_api_token }}" + + - name: Add consumer as member to share group (producer task) + linode.cloud.image_share_group_member: + label: "ansible-test-{{ r }}-one" + token: "{{ share_group_token_create_one.single_use_token }}" + sharegroup_id: "{{ share_group_create.image_share_group.id }}" + state: present + register: share_group_member_add_one + environment: + LINODE_API_TOKEN: "{{ producer_api_token }}" + + - name: Add consumer as member to share group (producer task) + linode.cloud.image_share_group_member: + label: "ansible-test-{{ r }}-two" + token: "{{ share_group_token_create_two.single_use_token }}" + sharegroup_id: "{{ share_group_create.image_share_group.id }}" + state: present + register: share_group_member_add_two + environment: + LINODE_API_TOKEN: "{{ producer_api_token }}" + + - name: List image share group members with no filter + linode.cloud.image_share_group_member_list: + sharegroup_id: "{{ share_group_create.image_share_group.id }}" + count: 2 + register: no_filter + environment: + LINODE_API_TOKEN: "{{ producer_api_token }}" + + - name: Assert image_share_group_member_list with no filter + assert: + that: + - no_filter.image_share_group_members | length == 2 + + - name: List image share group members with filter on label + linode.cloud.image_share_group_member_list: + sharegroup_id: "{{ share_group_create.image_share_group.id }}" + count: 1 + order_by: created + order: desc + filters: + - name: label + values: 'ansible-test-{{ r }}-two' + register: filter + environment: + LINODE_API_TOKEN: "{{ producer_api_token }}" + + - name: Assert image_share_group_member_list with filter on label + assert: + that: + - filter.image_share_group_members | length == 1 + - filter.image_share_group_members[0].label == "ansible-test-" ~ r ~ "-two" + + always: + - ignore_errors: yes + block: + - name: Delete image share group member + linode.cloud.image_share_group_member: + label: "ansible-test-{{ r }}-one" + sharegroup_id: "{{ share_group_create.image_share_group.id }}" + state: absent + when: + - share_group_create is defined + - share_group_create.image_share_group is defined + environment: + LINODE_API_TOKEN: "{{ producer_api_token }}" + + - name: Delete image share group member + linode.cloud.image_share_group_member: + label: "ansible-test-{{ r }}-two" + sharegroup_id: "{{ share_group_create.image_share_group.id }}" + state: absent + when: + - share_group_create is defined + - share_group_create.image_share_group is defined + environment: + LINODE_API_TOKEN: "{{ producer_api_token }}" + + - name: Delete image share group token + linode.cloud.image_share_group_token: + label: "{{ share_group_token_create_one.image_share_group_token.label }}" + state: absent + when: + - share_group_token_create_one is defined + - share_group_token_create_one.image_share_group_token is defined + environment: + LINODE_API_TOKEN: "{{ consumer_api_token }}" + + - name: Delete image share group token + linode.cloud.image_share_group_token: + label: "{{ share_group_token_create_two.image_share_group_token.label }}" + state: absent + when: + - share_group_token_create_two is defined + - share_group_token_create_two.image_share_group_token is defined + environment: + LINODE_API_TOKEN: "{{ consumer_api_token }}" + + - name: Delete image share group + linode.cloud.image_share_group: + label: "{{ share_group_create.image_share_group.label }}" + state: absent + when: + - share_group_create is defined + - share_group_create.image_share_group is defined + environment: + LINODE_API_TOKEN: "{{ producer_api_token }}" + + environment: + LINODE_UA_PREFIX: "{{ ua_prefix }}" + LINODE_PRODUCER_API_TOKEN: "{{ producer_api_token }}" + LINODE_CONSUMER_API_TOKEN: "{{ consumer_api_token }}" + LINODE_API_URL: "{{ api_url }}" + LINODE_API_VERSION: "{{ api_version }}" + LINODE_CA: "{{ ca_file or '' }}" \ No newline at end of file diff --git a/tests/integration/targets/image_share_group_token/tasks/main.yaml b/tests/integration/targets/image_share_group_token/tasks/main.yaml new file mode 100644 index 00000000..07bad84b --- /dev/null +++ b/tests/integration/targets/image_share_group_token/tasks/main.yaml @@ -0,0 +1,92 @@ +- name: image_share_group_token + block: + - name: Normalize producer/consumer tokens + set_fact: + _producer_token: "{{ producer_api_token | default('', true) | trim }}" + _consumer_token: "{{ consumer_api_token | default('', true) | trim }}" + + - name: Skip test if producer/consumer tokens are missing + meta: end_play + when: _producer_token == '' or _consumer_token == '' + + - set_fact: + r: "{{ 1000000000 | random }}" + + - name: Create image share group (producer task) + linode.cloud.image_share_group: + label: "ansible-test-{{ r }}" + state: present + register: share_group_create + environment: + LINODE_API_TOKEN: "{{ producer_api_token }}" + + - name: Assert image share group is created + assert: + that: + - share_group_create.image_share_group.label == "ansible-test-" ~ r + - share_group_create.image_share_group.images_count == 0 + - share_group_create.image_share_group.members_count == 0 + + - name: Create image share group token (consumer task) + linode.cloud.image_share_group_token: + label: "ansible-test-{{ r }}" + valid_for_sharegroup_uuid: "{{ share_group_create.image_share_group.uuid }}" + state: present + register: share_group_token_create + environment: + LINODE_API_TOKEN: "{{ consumer_api_token }}" + + - name: Assert image share group token is created + assert: + that: + - share_group_token_create.image_share_group_token.label == "ansible-test-" ~ r + - share_group_token_create.image_share_group_token.valid_for_sharegroup_uuid == share_group_create.image_share_group.uuid + - share_group_token_create.single_use_token is defined + - share_group_token_create.single_use_token | length > 0 + + - name: Attempt to update image share group token with modified share group UUID (should fail) + linode.cloud.image_share_group_token: + label: "ansible-test-{{ r }}" + valid_for_sharegroup_uuid: "{{ share_group_create.image_share_group.uuid }}x" + state: present + register: share_group_token_update_attempt + failed_when: false + environment: + LINODE_API_TOKEN: "{{ consumer_api_token }}" + + - name: Assert image share group token update fails for non-mutable field + assert: + that: + - share_group_token_update_attempt.msg is defined + - "'non-updatable field' in share_group_token_update_attempt.msg" + + always: + - ignore_errors: yes + block: + - name: Delete image share group token + linode.cloud.image_share_group_token: + label: "{{ share_group_token_create.image_share_group_token.label }}" + state: absent + when: + - share_group_token_create is defined + - share_group_token_create.image_share_group_token is defined + environment: + LINODE_API_TOKEN: "{{ consumer_api_token }}" + + - name: Delete image share group + linode.cloud.image_share_group: + label: "{{ share_group_create.image_share_group.label }}" + state: absent + when: + - share_group_create is defined + - share_group_create.image_share_group is defined + environment: + LINODE_API_TOKEN: "{{ producer_api_token }}" + + environment: + LINODE_UA_PREFIX: "{{ ua_prefix }}" + LINODE_PRODUCER_API_TOKEN: "{{ producer_api_token }}" + LINODE_CONSUMER_API_TOKEN: "{{ consumer_api_token }}" + LINODE_API_URL: "{{ api_url }}" + LINODE_API_VERSION: "{{ api_version }}" + LINODE_CA: "{{ ca_file or '' }}" \ No newline at end of file diff --git a/tests/integration/targets/image_share_group_token_info/tasks/main.yaml b/tests/integration/targets/image_share_group_token_info/tasks/main.yaml new file mode 100644 index 00000000..940a6c91 --- /dev/null +++ b/tests/integration/targets/image_share_group_token_info/tasks/main.yaml @@ -0,0 +1,85 @@ +- name: image_share_group_token_info + block: + - name: Normalize producer/consumer tokens + set_fact: + _producer_token: "{{ producer_api_token | default('', true) | trim }}" + _consumer_token: "{{ consumer_api_token | default('', true) | trim }}" + + - name: Skip test if producer/consumer tokens are missing + meta: end_play + when: _producer_token == '' or _consumer_token == '' + + - set_fact: + r: "{{ 1000000000 | random }}" + + - name: Create image share group (producer task) + linode.cloud.image_share_group: + label: "ansible-test-{{ r }}" + state: present + register: share_group_create + environment: + LINODE_API_TOKEN: "{{ producer_api_token }}" + + - name: Create image share group token (consumer task) + linode.cloud.image_share_group_token: + label: "ansible-test-{{ r }}" + valid_for_sharegroup_uuid: "{{ share_group_create.image_share_group.uuid }}" + state: present + register: share_group_token_create + environment: + LINODE_API_TOKEN: "{{ consumer_api_token }}" + + - name: Get information about the Image Share Group Token by label + linode.cloud.image_share_group_token_info: + label: "{{ share_group_token_create.image_share_group_token.label }}" + register: share_group_token_info_label + environment: + LINODE_API_TOKEN: "{{ consumer_api_token }}" + + - name: Assert Image Share Group Token information + assert: + that: + - share_group_token_info_label.image_share_group_token.label == "ansible-test-" ~ r + + - name: Get information about the Image Share Group Token by Token UUID + linode.cloud.image_share_group_token_info: + token_uuid: "{{ share_group_token_create.image_share_group_token.token_uuid }}" + register: share_group_token_info_token_uuid + environment: + LINODE_API_TOKEN: "{{ consumer_api_token }}" + + - name: Assert Image Share Group Token information + assert: + that: + - share_group_token_info_token_uuid.image_share_group_token.label == "ansible-test-" ~ r + + always: + - ignore_errors: yes + block: + - name: Delete image share group token + linode.cloud.image_share_group_token: + label: "{{ share_group_token_create.image_share_group_token.label }}" + state: absent + when: + - share_group_token_create is defined + - share_group_token_create.image_share_group_token is defined + environment: + LINODE_API_TOKEN: "{{ consumer_api_token }}" + + - name: Delete image share group + linode.cloud.image_share_group: + label: "{{ share_group_create.image_share_group.label }}" + state: absent + when: + - share_group_create is defined + - share_group_create.image_share_group is defined + environment: + LINODE_API_TOKEN: "{{ producer_api_token }}" + + environment: + LINODE_UA_PREFIX: "{{ ua_prefix }}" + LINODE_PRODUCER_API_TOKEN: "{{ producer_api_token }}" + LINODE_CONSUMER_API_TOKEN: "{{ consumer_api_token }}" + LINODE_API_URL: "{{ api_url }}" + LINODE_API_VERSION: "{{ api_version }}" + LINODE_CA: "{{ ca_file or '' }}" \ No newline at end of file diff --git a/tests/integration/targets/image_share_group_token_list/tasks/main.yaml b/tests/integration/targets/image_share_group_token_list/tasks/main.yaml new file mode 100644 index 00000000..416e5066 --- /dev/null +++ b/tests/integration/targets/image_share_group_token_list/tasks/main.yaml @@ -0,0 +1,110 @@ +- name: image_share_group_token_list + block: + - name: Normalize producer/consumer tokens + set_fact: + _producer_token: "{{ producer_api_token | default('', true) | trim }}" + _consumer_token: "{{ consumer_api_token | default('', true) | trim }}" + + - name: Skip test if producer/consumer tokens are missing + meta: end_play + when: _producer_token == '' or _consumer_token == '' + + - set_fact: + r: "{{ 1000000000 | random }}" + + - name: Create image share group (producer task) + linode.cloud.image_share_group: + label: "ansible-test-{{ r }}" + state: present + register: share_group_create + environment: + LINODE_API_TOKEN: "{{ producer_api_token }}" + + - name: Create image share group token (consumer task) + linode.cloud.image_share_group_token: + label: "ansible-test-{{ r }}-one" + valid_for_sharegroup_uuid: "{{ share_group_create.image_share_group.uuid }}" + state: present + register: share_group_token_create_one + environment: + LINODE_API_TOKEN: "{{ consumer_api_token }}" + + - name: Create another image share group token (consumer task) + linode.cloud.image_share_group_token: + label: "ansible-test-{{ r }}-two" + valid_for_sharegroup_uuid: "{{ share_group_create.image_share_group.uuid }}" + state: present + register: share_group_token_create_two + environment: + LINODE_API_TOKEN: "{{ consumer_api_token }}" + + - name: List Image Share Group Tokens with no filter + linode.cloud.image_share_group_token_list: + count: 2 + register: no_filter + environment: + LINODE_API_TOKEN: "{{ consumer_api_token }}" + + - name: Assert image_share_group_token_list with no filter + assert: + that: + - no_filter.image_share_group_tokens | length == 2 + + - name: List Image Share Group Tokens with filter on label + linode.cloud.image_share_group_token_list: + count: 1 + order_by: created + order: desc + filters: + - name: label + values: 'ansible-test-{{ r }}-one' + register: filter + environment: + LINODE_API_TOKEN: "{{ consumer_api_token }}" + + - name: Assert image_share_group_token_list with filter on label + assert: + that: + - filter.image_share_group_tokens | length == 1 + - filter.image_share_group_tokens[0].label == "ansible-test-" ~ r ~ "-one" + + always: + - ignore_errors: yes + block: + - name: Delete image share group token + linode.cloud.image_share_group_token: + label: "{{ share_group_token_create_one.image_share_group_token.label }}" + state: absent + when: + - share_group_token_create_one is defined + - share_group_token_create_one.image_share_group_token is defined + environment: + LINODE_API_TOKEN: "{{ consumer_api_token }}" + + - name: Delete image share group token + linode.cloud.image_share_group_token: + label: "{{ share_group_token_create_two.image_share_group_token.label }}" + state: absent + when: + - share_group_token_create_two is defined + - share_group_token_create_two.image_share_group_token is defined + environment: + LINODE_API_TOKEN: "{{ consumer_api_token }}" + + - name: Delete image share group + linode.cloud.image_share_group: + label: "{{ share_group_create.image_share_group.label }}" + state: absent + when: + - share_group_create is defined + - share_group_create.image_share_group is defined + environment: + LINODE_API_TOKEN: "{{ producer_api_token }}" + + environment: + LINODE_UA_PREFIX: "{{ ua_prefix }}" + LINODE_PRODUCER_API_TOKEN: "{{ producer_api_token }}" + LINODE_CONSUMER_API_TOKEN: "{{ consumer_api_token }}" + LINODE_API_URL: "{{ api_url }}" + LINODE_API_VERSION: "{{ api_version }}" + LINODE_CA: "{{ ca_file or '' }}" \ No newline at end of file