From 824d3ead9fccc5dfedb842becfcb1c40a104411a Mon Sep 17 00:00:00 2001 From: Lena Garber Date: Tue, 25 Jun 2024 15:44:53 -0400 Subject: [PATCH 1/9] WIP --- plugins/modules/object_keys.py | 56 ++++++++++++++++++++++++++++++---- 1 file changed, 50 insertions(+), 6 deletions(-) diff --git a/plugins/modules/object_keys.py b/plugins/modules/object_keys.py index e81e8197..460d586f 100644 --- a/plugins/modules/object_keys.py +++ b/plugins/modules/object_keys.py @@ -5,7 +5,7 @@ from __future__ import absolute_import, division, print_function -from typing import Any, List, Optional, Union +from typing import Any, Dict, List, Optional, Union import ansible_collections.linode.cloud.plugins.module_utils.doc_fragments.object_keys as docs from ansible_collections.linode.cloud.plugins.module_utils.linode_common import ( @@ -15,6 +15,9 @@ global_authors, global_requirements, ) +from ansible_collections.linode.cloud.plugins.module_utils.linode_helper import ( + handle_updates, +) from ansible_specdoc.objects import ( FieldType, SpecDocMeta, @@ -24,12 +27,19 @@ from linode_api4 import ObjectStorageKeys linode_access_spec = { + "region": SpecField( + type=FieldType.string, + description=[ + "The region of the cluster that the provided bucket exists under." + ], + conflicts_with=["cluster"], + ), "cluster": SpecField( type=FieldType.string, - required=True, description=[ "The id of the cluster that the provided bucket exists under." ], + conflicts_with=["cluster"], ), "bucket_name": SpecField( type=FieldType.string, @@ -57,6 +67,12 @@ suboptions=linode_access_spec, description=["A list of access permissions to give the key."], ), + "regions": SpecField( + type=FieldType.list, + element_type=FieldType.string, + description=["A list of regions to scope this key to."], + editable=True, + ), "state": SpecField( type=FieldType.string, description=["The desired state of the target."], @@ -100,6 +116,7 @@ def __init__(self) -> None: super().__init__( module_arg_spec=self.module_arg_spec, required_one_of=self.required_one_of, + mutually_exclusive=[("access.cluster", "access.region")], ) def _get_key_by_label(self, label: str) -> Optional[ObjectStorageKeys]: @@ -124,32 +141,59 @@ def _get_key_by_label(self, label: str) -> Optional[ObjectStorageKeys]: ) def _create_key( - self, label: str, bucket_access: Union[dict, List[dict]] + self, + label: str, + bucket_access: Union[dict, List[dict]], + regions: List[str], ) -> Optional[ObjectStorageKeys]: """Creates an Object Storage key with the given label and access""" try: return self.client.object_storage.keys_create( - label, bucket_access=bucket_access + label, bucket_access=bucket_access, regions=regions ) except Exception as exception: return self.fail( msg="failed to create object storage key: {0}".format(exception) ) + def _attempt_update_key( + self, key: ObjectStorageKeys, params: Dict[str, Any] + ): + """ + Attempts to update the given OBJ key. + """ + key.invalidate() + + changed_keys = handle_updates( + key, + params, + {"regions"}, + self.register_action, + ) + + # Refresh the key if it was updated + if len(changed_keys) > 0: + key._api_get() + def _handle_key(self) -> None: """Updates the key defined in kwargs""" params = self.module.params - label: str = params.pop("label") + label: str = params.get("label") access: dict = params.get("access") + regions: List[str] = params.get("regions") self._key = self._get_key_by_label(label) if self._key is None: - self._key = self._create_key(label, bucket_access=access) + self._key = self._create_key( + label, bucket_access=access, regions=regions + ) self.register_action("Created key {0}".format(label)) + self._attempt_update_key(self._key, params) + self.results["key"] = self._key._raw_json def _handle_key_absent(self) -> None: From 1e53741dc523ee18375fb7e20a791c59e0a1ee6a Mon Sep 17 00:00:00 2001 From: Lena Garber Date: Wed, 26 Jun 2024 14:01:46 -0400 Subject: [PATCH 2/9] WIP --- plugins/modules/object_keys.py | 5 +- requirements.txt | 5 +- .../targets/object_keys_basic/tasks/main.yaml | 78 +++++++++++++++++++ 3 files changed, 86 insertions(+), 2 deletions(-) create mode 100644 tests/integration/targets/object_keys_basic/tasks/main.yaml diff --git a/plugins/modules/object_keys.py b/plugins/modules/object_keys.py index 460d586f..ee78f529 100644 --- a/plugins/modules/object_keys.py +++ b/plugins/modules/object_keys.py @@ -192,7 +192,10 @@ def _handle_key(self) -> None: ) self.register_action("Created key {0}".format(label)) - self._attempt_update_key(self._key, params) + # NOTE: If the key is refreshed at all after creation, + # make sure you preserve the secret_key :) + else: + self._attempt_update_key(self._key, params) self.results["key"] = self._key._raw_json diff --git a/requirements.txt b/requirements.txt index 15282d3b..05da3c40 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,4 +1,7 @@ -linode_api4>=5.17.0 +# TODO: Revert before merging to dev +# linode-api4>=5.17.0 +git+https://github.com/zliang-akamai/linode_api4-python@zhiwei/bucket-key-update + polling>=0.3.2 types-requests==2.32.0.20240602 ansible-specdoc>=0.0.14 diff --git a/tests/integration/targets/object_keys_basic/tasks/main.yaml b/tests/integration/targets/object_keys_basic/tasks/main.yaml new file mode 100644 index 00000000..3297ee83 --- /dev/null +++ b/tests/integration/targets/object_keys_basic/tasks/main.yaml @@ -0,0 +1,78 @@ +- name: object_keys_basic + block: + - set_fact: + r: "{{ 1000000000 | random }}" + + - name: Create an OBJ key + linode.cloud.object_keys: + label: "test-ansible-key-{{ r }}" + regions: + - us-mia + - us-iad + state: present + register: create + + - name: Assert OBJ key was created successfully + assert: + that: + - create.changed + - "not 'REDACTED' in create.key.secret_key" + - create.key.regions[0].id in ("us-mia", "us-iad") + - create.key.regions[0].s3_endpoint != None + - create.key.regions[1].id in ("us-mia", "us-iad") + - create.key.regions[1].s3_endpoint != None + + - name: Refresh the OBJ key + linode.cloud.object_keys: + label: "{{ create.key.label }}" + regions: + - us-mia + - us-iad + state: present + register: refresh + + - name: Assert OBJ key was not updated + assert: + that: + # - not refresh.changed + - "'REDACTED' in refresh.key.secret_key" + +# - name: Create an OBJ key with access restrictions +# linode.cloud.object_keys: +# label: 'test-ansible-key-access-{{ r }}' +# access: +# - cluster: us-ord-1 +# bucket_name: '{{ create_bucket.name }}' +# permissions: read_write +# - cluster: us-ord-1 +# bucket_name: '{{ create_bucket.name }}' +# permissions: read_only +# state: present +# register: create_access +# +# - name: Assert key created and access is valid +# assert: +# that: +# - create_access.changed +# - 'not "REDACTED" in create_access.key.secret_key' +# - create_access.key.bucket_access[0].cluster == 'us-ord-1' +# - create_access.key.bucket_access[0].bucket_name == create_bucket.name +# - create_access.key.bucket_access[0].permissions == 'read_write' +# - create_access.key.bucket_access[1].cluster == 'us-ord-1' +# - create_access.key.bucket_access[1].bucket_name == create_bucket.name +# - create_access.key.bucket_access[1].permissions == 'read_only' + + always: + - ignore_errors: yes + block: + - name: Delete the OBJ key + linode.cloud.object_keys: + label: "{{ create.key.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 "" }}' From 4e2718328832fed1727c618422cadcafff57f844 Mon Sep 17 00:00:00 2001 From: Lena Garber Date: Thu, 27 Jun 2024 11:04:55 -0400 Subject: [PATCH 3/9] Update logic WIP --- plugins/modules/object_keys.py | 32 +++++++++++-------- .../targets/object_keys_basic/tasks/main.yaml | 20 +++++++++++- 2 files changed, 38 insertions(+), 14 deletions(-) diff --git a/plugins/modules/object_keys.py b/plugins/modules/object_keys.py index ee78f529..73604ebd 100644 --- a/plugins/modules/object_keys.py +++ b/plugins/modules/object_keys.py @@ -15,9 +15,6 @@ global_authors, global_requirements, ) -from ansible_collections.linode.cloud.plugins.module_utils.linode_helper import ( - handle_updates, -) from ansible_specdoc.objects import ( FieldType, SpecDocMeta, @@ -163,18 +160,27 @@ def _attempt_update_key( """ Attempts to update the given OBJ key. """ - key.invalidate() - changed_keys = handle_updates( - key, - params, - {"regions"}, - self.register_action, - ) + put_body = {} + + # We can't use handle_updates here because the structure under `regions` + # differs between the request and response + configured_regions = params.get("regions") + flattened_regions = set(v for v in key.regions) + + if ( + configured_regions is not None + and set(configured_regions) != flattened_regions + ): + put_body["regions"] = configured_regions + + if len(put_body) > 0: + self._client.put( + ObjectStorageKeys.api_endpoint, model=key, data=put_body + ) - # Refresh the key if it was updated - if len(changed_keys) > 0: - key._api_get() + # Refresh the key object + self._key._api_get() def _handle_key(self) -> None: """Updates the key defined in kwargs""" diff --git a/tests/integration/targets/object_keys_basic/tasks/main.yaml b/tests/integration/targets/object_keys_basic/tasks/main.yaml index 3297ee83..b505d754 100644 --- a/tests/integration/targets/object_keys_basic/tasks/main.yaml +++ b/tests/integration/targets/object_keys_basic/tasks/main.yaml @@ -34,9 +34,27 @@ - name: Assert OBJ key was not updated assert: that: - # - not refresh.changed + - not refresh.changed - "'REDACTED' in refresh.key.secret_key" + - refresh.key.regions[0].id in ("us-mia", "us-iad") + - refresh.key.regions[0].s3_endpoint != None + - refresh.key.regions[1].id in ("us-mia", "us-iad") + - refresh.key.regions[1].s3_endpoint != None + - name: Update the regions for the OBJ key + linode.cloud.object_keys: + label: "{{ create.key.label }}" + regions: + - us-ord + state: present + register: update + + - name: Assert OBJ key was updated + assert: + that: + - update.changed + - update.key.regions | length == 1 + - update.key.regions[0].id == "us-ord" # - name: Create an OBJ key with access restrictions # linode.cloud.object_keys: # label: 'test-ansible-key-access-{{ r }}' From 647a009f1ebc322a8adcf9a6242082bd7134b426 Mon Sep 17 00:00:00 2001 From: Lena Garber Date: Thu, 27 Jun 2024 14:21:07 -0400 Subject: [PATCH 4/9] Finish test --- plugins/modules/object_keys.py | 65 +++++++++- .../targets/object_keys_basic/tasks/main.yaml | 118 ++++++++++++++---- 2 files changed, 157 insertions(+), 26 deletions(-) diff --git a/plugins/modules/object_keys.py b/plugins/modules/object_keys.py index 73604ebd..4fead73a 100644 --- a/plugins/modules/object_keys.py +++ b/plugins/modules/object_keys.py @@ -154,6 +154,56 @@ def _create_key( msg="failed to create object storage key: {0}".format(exception) ) + @staticmethod + def _access_changed(key: ObjectStorageKeys, params: Dict[str, Any]) -> bool: + """ + Returns whether the user has made any effective changes to the `access` field. + + NOTE: This requires special logic to maintain backwards compatibility + with the `cluster` field. + """ + + configured_access = params.get("access") + if configured_access is None: + return False + + # Map the region and bucket name to a grant + access = { + (grant.region, grant.bucket_name): grant + for grant in key.bucket_access + } + + for configured_grant in configured_access: + configured_region = configured_grant.get("region") + configured_permissions = configured_grant.get("permissions") + + # Hack to extract the region from a configured cluster + if configured_region is None: + configured_region = configured_grant.get("cluster").split("-1")[ + 0 + ] + + grant_key = (configured_region, configured_grant.get("bucket_name")) + + grant = access.get(grant_key) + if grant is None or configured_permissions != grant.permissions: + return True + + del access[grant_key] + + # If true, the user attempted to remove a grant + return len(access) > 0 + + def _validate_updates( + self, key: ObjectStorageKeys, params: Dict[str, Any] + ) -> None: + """ + Raises an error if any invalid update operations are attempted. + """ + + if self._access_changed(key, params): + self.fail("`access` is not an updatable field") + def _attempt_update_key( self, key: ObjectStorageKeys, params: Dict[str, Any] ): @@ -161,19 +211,30 @@ def _attempt_update_key( Attempts to update the given OBJ key. """ + self._validate_updates(key, params) + put_body = {} # We can't use handle_updates here because the structure under `regions` # differs between the request and response - configured_regions = params.get("regions") - flattened_regions = set(v for v in key.regions) + configured_regions = params.get("regions") or [] + flattened_regions = set(v.id for v in key.regions) + + # Regions from bucket_access will implicitly be added to the + # `regions` attribute, so we should account for that here + for grant in key.bucket_access or []: + configured_regions.append(grant.region) if ( configured_regions is not None and set(configured_regions) != flattened_regions ): put_body["regions"] = configured_regions + self.register_action( + f"Updated regions from {list(flattened_regions)} to {configured_regions}" + ) + # Apply changes if len(put_body) > 0: self._client.put( ObjectStorageKeys.api_endpoint, model=key, data=put_body diff --git a/tests/integration/targets/object_keys_basic/tasks/main.yaml b/tests/integration/targets/object_keys_basic/tasks/main.yaml index b505d754..dfa68bc1 100644 --- a/tests/integration/targets/object_keys_basic/tasks/main.yaml +++ b/tests/integration/targets/object_keys_basic/tasks/main.yaml @@ -55,34 +55,104 @@ - update.changed - update.key.regions | length == 1 - update.key.regions[0].id == "us-ord" -# - name: Create an OBJ key with access restrictions -# linode.cloud.object_keys: -# label: 'test-ansible-key-access-{{ r }}' -# access: -# - cluster: us-ord-1 -# bucket_name: '{{ create_bucket.name }}' -# permissions: read_write -# - cluster: us-ord-1 -# bucket_name: '{{ create_bucket.name }}' -# permissions: read_only -# state: present -# register: create_access -# -# - name: Assert key created and access is valid -# assert: -# that: -# - create_access.changed -# - 'not "REDACTED" in create_access.key.secret_key' -# - create_access.key.bucket_access[0].cluster == 'us-ord-1' -# - create_access.key.bucket_access[0].bucket_name == create_bucket.name -# - create_access.key.bucket_access[0].permissions == 'read_write' -# - create_access.key.bucket_access[1].cluster == 'us-ord-1' -# - create_access.key.bucket_access[1].bucket_name == create_bucket.name -# - create_access.key.bucket_access[1].permissions == 'read_only' + + - name: Create an S3 bucket using the key + amazon.aws.s3_bucket: + s3_url: 'https://{{ update.key.regions[0].s3_endpoint }}/' + aws_access_key: '{{ create.key.access_key }}' + aws_secret_key: '{{ create.key.secret_key }}' + name: 'test-ansible-bucket-{{ r }}' + state: present + register: create_bucket + + - name: Assert bucket was created + assert: + that: + - create_bucket.changed + + - name: Create an OBJ key scoped to the new bucket + linode.cloud.object_keys: + label: "test-ansible-key-scoped-{{ r }}" + regions: + - us-mia + access: + - region: "{{ update.key.regions[0].id }}" + bucket_name: "{{ create_bucket.name }}" + permissions: read_write + state: present + register: create_scoped + + - name: Assert the scoped key was created + assert: + that: + - create_scoped.changed + - create_scoped.key.bucket_access[0].region == update.key.regions[0].id + - create_scoped.key.bucket_access[0].bucket_name == create_bucket.name + - create_scoped.key.bucket_access[0].permissions == "read_write" + - create_scoped.key.regions[0].id in ("us-ord", "us-mia") + - create_scoped.key.regions[1].id in ("us-ord", "us-mia") + + - name: Refresh the scoped key + linode.cloud.object_keys: + label: "{{ create_scoped.key.label }}" + regions: + - us-mia + access: + - cluster: "{{ update.key.regions[0].id }}-1" + bucket_name: "{{ create_bucket.name }}" + permissions: read_write + state: present + register: refresh_scoped + + - name: Assert the scoped key was not changed + assert: + that: + - not refresh_scoped.changed + + - name: Refresh the scoped key (region -> cluster in access grant) + linode.cloud.object_keys: + label: "{{ create_scoped.key.label }}" + regions: + - us-mia + access: + - cluster: "{{ update.key.regions[0].id }}-1" + bucket_name: "{{ create_bucket.name }}" + permissions: read_write + state: present + register: refresh_scoped + + - name: Assert the scoped key was not changed + assert: + that: + - not refresh_scoped.changed + + - name: Update the scoped key; expect failure + linode.cloud.object_keys: + label: "{{ create_scoped.key.label }}" + regions: + - us-mia + access: [] + state: present + register: update_scoped + failed_when: "'`access` is not an updatable field' not in update_scoped.msg" always: - ignore_errors: yes block: + - name: Delete the OBJ bucket + amazon.aws.s3_bucket: + s3_url: 'https://{{ update.key.regions[0].s3_endpoint }}//' + aws_access_key: '{{ create.key.access_key }}' + aws_secret_key: '{{ create.key.secret_key }}' + name: "{{ create_bucket.name }}" + state: absent + register: delete_bucket + + - name: Delete the scoped OBJ key + linode.cloud.object_keys: + label: "{{ create_scoped.key.label }}" + state: absent + - name: Delete the OBJ key linode.cloud.object_keys: label: "{{ create.key.label }}" From 08de68c1d0814cf365f9e82ad671c98a79f8f7a9 Mon Sep 17 00:00:00 2001 From: Lena Garber Date: Thu, 27 Jun 2024 14:35:02 -0400 Subject: [PATCH 5/9] bug fixes --- docs/modules/object_keys.md | 4 +++- plugins/modules/object_keys.py | 18 +++++++++++++----- 2 files changed, 16 insertions(+), 6 deletions(-) diff --git a/docs/modules/object_keys.md b/docs/modules/object_keys.md index 2c8f7c58..41bcc0bc 100644 --- a/docs/modules/object_keys.md +++ b/docs/modules/object_keys.md @@ -47,14 +47,16 @@ Manage Linode Object Storage Keys. | `state` |
`str`
|
**Required**
| The desired state of the target. **(Choices: `present`, `absent`)** | | `label` |
`str`
|
Optional
| The unique label to give this key. | | [`access` (sub-options)](#access) |
`list`
|
Optional
| A list of access permissions to give the key. | +| `regions` |
`list`
|
Optional
| A list of regions to scope this key to. **(Updatable)** | ### access | Field | Type | Required | Description | |-----------|------|----------|------------------------------------------------------------------------------| -| `cluster` |
`str`
|
**Required**
| The id of the cluster that the provided bucket exists under. | | `bucket_name` |
`str`
|
**Required**
| The name of the bucket to set the key's permissions for. | | `permissions` |
`str`
|
**Required**
| The permissions to give the key. **(Choices: `read_only`, `write_only`, `read_write`)** | +| `region` |
`str`
|
Optional
| The region of the cluster that the provided bucket exists under. **(Conflicts With: `cluster`)** | +| `cluster` |
`str`
|
Optional
| The id of the cluster that the provided bucket exists under. **(Conflicts With: `region`)** | ## Return Values diff --git a/plugins/modules/object_keys.py b/plugins/modules/object_keys.py index 4fead73a..e06e9ee3 100644 --- a/plugins/modules/object_keys.py +++ b/plugins/modules/object_keys.py @@ -5,7 +5,7 @@ from __future__ import absolute_import, division, print_function -from typing import Any, Dict, List, Optional, Union +from typing import Any, Dict, List, Optional import ansible_collections.linode.cloud.plugins.module_utils.doc_fragments.object_keys as docs from ansible_collections.linode.cloud.plugins.module_utils.linode_common import ( @@ -15,6 +15,9 @@ 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, @@ -36,7 +39,7 @@ description=[ "The id of the cluster that the provided bucket exists under." ], - conflicts_with=["cluster"], + conflicts_with=["region"], ), "bucket_name": SpecField( type=FieldType.string, @@ -113,7 +116,6 @@ def __init__(self) -> None: super().__init__( module_arg_spec=self.module_arg_spec, required_one_of=self.required_one_of, - mutually_exclusive=[("access.cluster", "access.region")], ) def _get_key_by_label(self, label: str) -> Optional[ObjectStorageKeys]: @@ -140,11 +142,17 @@ def _get_key_by_label(self, label: str) -> Optional[ObjectStorageKeys]: def _create_key( self, label: str, - bucket_access: Union[dict, List[dict]], - regions: List[str], + bucket_access: Optional[List[Dict[str, Any]]], + regions: Optional[List[str]], ) -> Optional[ObjectStorageKeys]: """Creates an Object Storage key with the given label and access""" + # The API will reject explicit null values for `bucket_access.region` + if bucket_access is not None: + bucket_access = [ + filter_null_values(grant) for grant in bucket_access + ] + try: return self.client.object_storage.keys_create( label, bucket_access=bucket_access, regions=regions From 0a44cb15845500e508001aadee92fa69eda45127 Mon Sep 17 00:00:00 2001 From: Lena Garber Date: Thu, 27 Jun 2024 14:43:37 -0400 Subject: [PATCH 6/9] Change quotes --- .../targets/object_keys_basic/tasks/main.yaml | 22 +++++++++---------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/tests/integration/targets/object_keys_basic/tasks/main.yaml b/tests/integration/targets/object_keys_basic/tasks/main.yaml index dfa68bc1..b40fd7c7 100644 --- a/tests/integration/targets/object_keys_basic/tasks/main.yaml +++ b/tests/integration/targets/object_keys_basic/tasks/main.yaml @@ -58,10 +58,10 @@ - name: Create an S3 bucket using the key amazon.aws.s3_bucket: - s3_url: 'https://{{ update.key.regions[0].s3_endpoint }}/' - aws_access_key: '{{ create.key.access_key }}' - aws_secret_key: '{{ create.key.secret_key }}' - name: 'test-ansible-bucket-{{ r }}' + s3_url: "https://{{ update.key.regions[0].s3_endpoint }}/" + aws_access_key: "{{ create.key.access_key }}" + aws_secret_key: "{{ create.key.secret_key }}" + name: "test-ansible-bucket-{{ r }}" state: present register: create_bucket @@ -141,9 +141,9 @@ block: - name: Delete the OBJ bucket amazon.aws.s3_bucket: - s3_url: 'https://{{ update.key.regions[0].s3_endpoint }}//' - aws_access_key: '{{ create.key.access_key }}' - aws_secret_key: '{{ create.key.secret_key }}' + s3_url: "https://{{ update.key.regions[0].s3_endpoint }}//" + aws_access_key: "{{ create.key.access_key }}" + aws_secret_key: "{{ create.key.secret_key }}" name: "{{ create_bucket.name }}" state: absent register: delete_bucket @@ -159,8 +159,8 @@ state: absent environment: - LINODE_UA_PREFIX: '{{ ua_prefix }}' - LINODE_API_TOKEN: '{{ api_token }}' - LINODE_API_URL: '{{ api_url }}' - LINODE_API_VERSION: '{{ api_version }}' + 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 "" }}' From 183679e7d556ab0219a6671e0c3998afe5e115b9 Mon Sep 17 00:00:00 2001 From: Lena Garber Date: Thu, 27 Jun 2024 14:58:34 -0400 Subject: [PATCH 7/9] Update docs --- docs/modules/object_keys.md | 41 +++++++++++++++---- .../module_utils/doc_fragments/object_keys.py | 38 +++++++++++++---- 2 files changed, 63 insertions(+), 16 deletions(-) diff --git a/docs/modules/object_keys.md b/docs/modules/object_keys.md index 04eff748..4a21a8c9 100644 --- a/docs/modules/object_keys.md +++ b/docs/modules/object_keys.md @@ -22,11 +22,21 @@ Manage Linode Object Storage Keys. ``` ```yaml -- name: Create a limited Object Storage key +- name: Create an Object Storage key limited to specific regions + linode.cloud.object_keys: + label: 'my-region-limited-key' + regions: + - us-mia + - us-ord + state: present +``` + +```yaml +- name: Create an Object Storage key limited to specific buckets linode.cloud.object_keys: label: 'my-limited-key' access: - - cluster: us-east-1 + - cluster: us-mia bucket_name: my-bucket permissions: read_write state: present @@ -65,18 +75,33 @@ Manage Linode Object Storage Keys. - Sample Response: ```json { - "access_key": "ACCESSKEY", + "access_key": "redacted", "bucket_access": [ { - "bucket_name": "example-bucket", - "cluster": "ap-south-1", - "permissions": "read_only" + "bucket_name": "my-bucket", + "cluster": "us-iad-1", + "permissions": "read_write", + "region": "us-iad" } ], - "id": 123, + "id": 12345, "label": "my-key", "limited": true, - "secret_key": "SECRETKEY" + "regions": [ + { + "id": "us-iad", + "s3_endpoint": "us-iad-1.linodeobjects.com" + }, + { + "id": "us-ord", + "s3_endpoint": "us-ord-1.linodeobjects.com" + }, + { + "id": "us-sea", + "s3_endpoint": "us-sea-1.linodeobjects.com" + } + ], + "secret_key": "[REDACTED]" } ``` - See the [Linode API response documentation](https://www.linode.com/docs/api/object-storage/#object-storage-key-view__responses) for a list of returned fields diff --git a/plugins/module_utils/doc_fragments/object_keys.py b/plugins/module_utils/doc_fragments/object_keys.py index ab38d357..e7ebb728 100644 --- a/plugins/module_utils/doc_fragments/object_keys.py +++ b/plugins/module_utils/doc_fragments/object_keys.py @@ -5,11 +5,18 @@ linode.cloud.object_keys: label: 'my-fullaccess-key' state: present''', ''' -- name: Create a limited Object Storage key +- name: Create an Object Storage key limited to specific regions + linode.cloud.object_keys: + label: 'my-region-limited-key' + regions: + - us-mia + - us-ord + state: present''', ''' +- name: Create an Object Storage key limited to specific buckets linode.cloud.object_keys: label: 'my-limited-key' access: - - cluster: us-east-1 + - cluster: us-mia bucket_name: my-bucket permissions: read_write state: present''', ''' @@ -19,16 +26,31 @@ state: absent'''] result_key_samples = ['''{ - "access_key": "ACCESSKEY", + "access_key": "redacted", "bucket_access": [ { - "bucket_name": "example-bucket", - "cluster": "ap-south-1", - "permissions": "read_only" + "bucket_name": "my-bucket", + "cluster": "us-iad-1", + "permissions": "read_write", + "region": "us-iad" } ], - "id": 123, + "id": 12345, "label": "my-key", "limited": true, - "secret_key": "SECRETKEY" + "regions": [ + { + "id": "us-iad", + "s3_endpoint": "us-iad-1.linodeobjects.com" + }, + { + "id": "us-ord", + "s3_endpoint": "us-ord-1.linodeobjects.com" + }, + { + "id": "us-sea", + "s3_endpoint": "us-sea-1.linodeobjects.com" + } + ], + "secret_key": "[REDACTED]" }'''] From d7f9e5b563bffcb05ca3f1d16715b037a7806b5a Mon Sep 17 00:00:00 2001 From: Lena Garber Date: Thu, 27 Jun 2024 15:00:27 -0400 Subject: [PATCH 8/9] fix lint --- plugins/modules/object_keys.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/plugins/modules/object_keys.py b/plugins/modules/object_keys.py index 822110f9..396b6906 100644 --- a/plugins/modules/object_keys.py +++ b/plugins/modules/object_keys.py @@ -218,7 +218,7 @@ def _validate_updates( def _attempt_update_key( self, key: ObjectStorageKeys, params: Dict[str, Any] - ): + ) -> None: """ Attempts to update the given OBJ key. """ @@ -260,7 +260,7 @@ def _handle_key(self) -> None: params = self.module.params label: str = params.get("label") - access: dict = params.get("access") + access: List[Dict[str, Any]] = params.get("access") regions: List[str] = params.get("regions") self._key = self._get_key_by_label(label) From 2b2c7308c445cce258346237e0680b259b320bc8 Mon Sep 17 00:00:00 2001 From: Lena Garber <114949949+lgarber-akamai@users.noreply.github.com> Date: Wed, 3 Jul 2024 09:30:55 -0400 Subject: [PATCH 9/9] Update requirements.txt Co-authored-by: Zhiwei Liang <121905282+zliang-akamai@users.noreply.github.com> --- requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements.txt b/requirements.txt index e8021c93..17973a6c 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,6 +1,6 @@ # TODO(Multicluster OBJ): Revert before merging to dev # linode-api4>=5.17.0 -git+https://github.com/zliang-akamai/linode_api4-python@zhiwei/bucket-key-update +git+https://github.com/linode/linode_api4-python@proj/multicluster-obj polling>=0.3.2 types-requests==2.32.0.20240622