Skip to content

Commit f08c0cd

Browse files
Project: Private Image Sharing (#633)
* Added support for Private Image Sharing features and unit tests * Addressed PR comments * Integration tests for private image sharing (#632) * Create integration tests for share groups - part 1 * Create test test_try_to_add_member_invalid_token * Update integration tests for private image sharing feature * Apply code review sugestions --------- Co-authored-by: Erik Zilber <ezilber@akamai.com> --------- Co-authored-by: Pawel <100145168+psnoch-akamai@users.noreply.github.com>
1 parent 43d8ec3 commit f08c0cd

25 files changed

+1503
-16
lines changed

linode_api4/groups/__init__.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
from .database import *
77
from .domain import *
88
from .image import *
9+
from .image_share_group import *
910
from .linode import *
1011
from .lke import *
1112
from .lke_tier import *
Lines changed: 142 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,142 @@
1+
from typing import Optional
2+
3+
from linode_api4.groups import Group
4+
from linode_api4.objects import (
5+
ImageShareGroup,
6+
ImageShareGroupImagesToAdd,
7+
ImageShareGroupToken,
8+
)
9+
from linode_api4.objects.base import _flatten_request_body_recursive
10+
from linode_api4.util import drop_null_keys
11+
12+
13+
class ImageShareGroupAPIGroup(Group):
14+
"""
15+
Collections related to Private Image Sharing.
16+
17+
NOTE: Private Image Sharing features are in beta and may not be generally available.
18+
"""
19+
20+
def __call__(self, *filters):
21+
"""
22+
Retrieves a list of Image Share Groups created by the user (producer).
23+
You can filter this query to retrieve only Image Share Groups
24+
relevant to a specific query, for example::
25+
26+
filtered_share_groups = client.sharegroups(
27+
ImageShareGroup.label == "my-label")
28+
29+
API Documentation: https://techdocs.akamai.com/linode-api/reference/get-sharegroups
30+
31+
:param filters: Any number of filters to apply to this query.
32+
See :doc:`Filtering Collections</linode_api4/objects/filtering>`
33+
for more details on filtering.
34+
35+
:returns: A list of Image Share Groups.
36+
:rtype: PaginatedList of ImageShareGroup
37+
"""
38+
return self.client._get_and_filter(ImageShareGroup, *filters)
39+
40+
def sharegroups_by_image_id(self, image_id: str):
41+
"""
42+
Retrieves a list of Image Share Groups that share a specific Private Image.
43+
44+
API Documentation: https://techdocs.akamai.com/linode-api/reference/get-images-sharegroups-image
45+
46+
:param image_id: The ID of the Image to query for.
47+
:type image_id: str
48+
49+
:returns: A list of Image Share Groups sharing the specified Image.
50+
:rtype: PaginatedList of ImageShareGroup
51+
"""
52+
return self.client._get_and_filter(
53+
ImageShareGroup, endpoint="/images/{}/sharegroups".format(image_id)
54+
)
55+
56+
def tokens(self, *filters):
57+
"""
58+
Retrieves a list of Image Share Group Tokens created by the user (consumer).
59+
You can filter this query to retrieve only Image Share Group Tokens
60+
relevant to a specific query, for example::
61+
62+
filtered_share_group_tokens = client.sharegroups.tokens(
63+
ImageShareGroupToken.label == "my-label")
64+
65+
API Documentation: https://techdocs.akamai.com/linode-api/reference/get-user-tokens
66+
67+
:param filters: Any number of filters to apply to this query.
68+
See :doc:`Filtering Collections</linode_api4/objects/filtering>`
69+
for more details on filtering.
70+
71+
:returns: A list of Image Share Group Tokens.
72+
:rtype: PaginatedList of ImageShareGroupToken
73+
"""
74+
return self.client._get_and_filter(ImageShareGroupToken, *filters)
75+
76+
def create_sharegroup(
77+
self,
78+
label: Optional[str] = None,
79+
description: Optional[str] = None,
80+
images: Optional[ImageShareGroupImagesToAdd] = None,
81+
):
82+
"""
83+
Creates a new Image Share Group.
84+
85+
API Documentation: https://techdocs.akamai.com/linode-api/reference/post-sharegroups
86+
87+
:param label: The label for the resulting Image Share Group.
88+
:type label: str
89+
:param description: The description for the new Image Share Group.
90+
:type description: str
91+
:param images: A list of Images to share in the new Image Share Group, formatted in JSON.
92+
:type images: Optional[ImageShareGroupImagesToAdd]
93+
94+
:returns: The new Image Share Group.
95+
:rtype: ImageShareGroup
96+
"""
97+
params = {
98+
"label": label,
99+
"description": description,
100+
}
101+
102+
if images:
103+
params["images"] = images
104+
105+
result = self.client.post(
106+
"/images/sharegroups",
107+
data=_flatten_request_body_recursive(drop_null_keys(params)),
108+
)
109+
110+
return ImageShareGroup(self.client, result["id"], result)
111+
112+
def create_token(
113+
self, valid_for_sharegroup_uuid: str, label: Optional[str] = None
114+
):
115+
"""
116+
Creates a new Image Share Group Token and returns the token value.
117+
118+
API Documentation: https://techdocs.akamai.com/linode-api/reference/post-sharegroup-tokens
119+
120+
:param valid_for_sharegroup_uuid: The UUID of the Image Share Group that this token will be valid for.
121+
:type valid_for_sharegroup_uuid: Optional[str]
122+
:param label: The label for the resulting Image Share Group Token.
123+
:type label: str
124+
125+
:returns: The new Image Share Group Token object and the one-time use token itself.
126+
:rtype: (ImageShareGroupToken, str)
127+
"""
128+
params = {"valid_for_sharegroup_uuid": valid_for_sharegroup_uuid}
129+
130+
if label:
131+
params["label"] = label
132+
133+
result = self.client.post(
134+
"/images/sharegroups/tokens",
135+
data=_flatten_request_body_recursive(drop_null_keys(params)),
136+
)
137+
138+
token_value = result.pop("token", None)
139+
token_obj = ImageShareGroupToken(
140+
self.client, result["token_uuid"], result
141+
)
142+
return token_obj, token_value

linode_api4/groups/linode.py

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -23,9 +23,7 @@
2323
NetworkInterface,
2424
_expand_placement_group_assignment,
2525
)
26-
from linode_api4.objects.linode_interfaces import (
27-
LinodeInterfaceOptions,
28-
)
26+
from linode_api4.objects.linode_interfaces import LinodeInterfaceOptions
2927
from linode_api4.util import drop_null_keys
3028

3129

linode_api4/linode_client.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
DatabaseGroup,
1717
DomainGroup,
1818
ImageGroup,
19+
ImageShareGroupAPIGroup,
1920
LinodeGroup,
2021
LKEGroup,
2122
LockGroup,
@@ -441,6 +442,9 @@ def __init__(
441442
#: Access methods related to Images - See :any:`ImageGroup` for more information.
442443
self.images = ImageGroup(self)
443444

445+
#: Access methods related to Image Share Groups - See :any:`ImageShareGroupAPIGroup` for more information.
446+
self.sharegroups = ImageShareGroupAPIGroup(self)
447+
444448
#: Access methods related to VPCs - See :any:`VPCGroup` for more information.
445449
self.vpcs = VPCGroup(self)
446450

linode_api4/objects/__init__.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,4 +24,5 @@
2424
from .placement import *
2525
from .monitor import *
2626
from .monitor_api import *
27+
from .image_share_group import *
2728
from .lock import *

linode_api4/objects/image.py

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,38 @@ class ImageRegion(JSONObject):
3030
status: Optional[ReplicationStatus] = None
3131

3232

33+
@dataclass
34+
class ImageSharingSharedWith(JSONObject):
35+
"""
36+
Data representing who an Image has been shared with.
37+
"""
38+
39+
sharegroup_count: Optional[int] = None
40+
sharegroup_list_url: Optional[str] = None
41+
42+
43+
@dataclass
44+
class ImageSharingSharedBy(JSONObject):
45+
"""
46+
Data representing who shared an Image.
47+
"""
48+
49+
sharegroup_id: Optional[int] = None
50+
sharegroup_uuid: Optional[str] = None
51+
sharegroup_label: Optional[str] = None
52+
source_image_id: Optional[str] = None
53+
54+
55+
@dataclass
56+
class ImageSharing(JSONObject):
57+
"""
58+
The Image Sharing status of an Image.
59+
"""
60+
61+
shared_with: Optional[ImageSharingSharedWith] = None
62+
shared_by: Optional[ImageSharingSharedBy] = None
63+
64+
3365
class Image(Base):
3466
"""
3567
An Image is something a Linode Instance or Disk can be deployed from.
@@ -51,6 +83,7 @@ class Image(Base):
5183
"updated": Property(is_datetime=True),
5284
"type": Property(),
5385
"is_public": Property(),
86+
"is_shared": Property(),
5487
"vendor": Property(),
5588
"size": Property(),
5689
"deprecated": Property(),
@@ -60,6 +93,7 @@ class Image(Base):
6093
"tags": Property(mutable=True, unordered=True),
6194
"total_size": Property(),
6295
"regions": Property(json_object=ImageRegion, unordered=True),
96+
"image_sharing": Property(json_object=ImageSharing),
6397
}
6498

6599
def replicate(self, regions: Union[List[str], List[Region]]):

0 commit comments

Comments
 (0)