Skip to content

Commit 42b0b9d

Browse files
Merge pull request #651 from linode/dev
Merge dev into proj/block-storage-volume-limits
2 parents 96daa7e + 6dc0564 commit 42b0b9d

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

61 files changed

+2117
-73
lines changed

.github/pull_request_template.md

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,3 @@
77
**What are the steps to reproduce the issue or verify the changes?**
88

99
**How do I run the relevant unit/integration tests?**
10-
11-
## 📷 Preview
12-
13-
**If applicable, include a screenshot or code snippet of this change. Otherwise, please remove this section.**

.github/workflows/codeql.yml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -26,13 +26,13 @@ jobs:
2626
uses: actions/checkout@v6
2727

2828
- name: Initialize CodeQL
29-
uses: github/codeql-action/init@v3
29+
uses: github/codeql-action/init@v4
3030
with:
3131
languages: ${{ matrix.language }}
3232
build-mode: ${{ matrix.build-mode }}
3333
queries: security-and-quality
3434

3535
- name: Perform CodeQL Analysis
36-
uses: github/codeql-action/analyze@v3
36+
uses: github/codeql-action/analyze@v4
3737
with:
3838
category: "/language:${{matrix.language}}"

.github/workflows/e2e-test-pr.yml

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,14 @@ on:
2727
pull_request_number:
2828
description: 'The number of the PR.'
2929
required: false
30+
test_report_upload:
31+
description: 'Indicates whether to upload the test report to object storage. Defaults to "false"'
32+
required: false
33+
default: 'false'
34+
type: choice
35+
options:
36+
- 'true'
37+
- 'false'
3038

3139
name: PR E2E Tests
3240

@@ -101,7 +109,7 @@ jobs:
101109
LINODE_TOKEN: ${{ secrets.LINODE_TOKEN }}
102110

103111
- name: Upload test results
104-
if: always()
112+
if: always() && github.repository == 'linode/linode_api4-python' && (github.event_name == 'pull_request' || (github.event_name == 'workflow_dispatch' && inputs.test_report_upload == 'true'))
105113
run: |
106114
filename=$(ls | grep -E '^[0-9]{12}_sdk_test_report\.xml$')
107115
python3 e2e_scripts/tod_scripts/xml_to_obj_storage/scripts/add_gha_info_to_xml.py \

.github/workflows/e2e-test.yml

Lines changed: 13 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,14 @@ on:
4141
options:
4242
- 'true'
4343
- 'false'
44+
test_report_upload:
45+
description: 'Indicates whether to upload the test report to object storage. Defaults to "false"'
46+
type: choice
47+
required: false
48+
default: 'false'
49+
options:
50+
- 'true'
51+
- 'false'
4452
push:
4553
branches:
4654
- main
@@ -97,7 +105,7 @@ jobs:
97105

98106
- name: Upload Test Report as Artifact
99107
if: always()
100-
uses: actions/upload-artifact@v5
108+
uses: actions/upload-artifact@v6
101109
with:
102110
name: test-report-file
103111
if-no-files-found: ignore
@@ -172,7 +180,8 @@ jobs:
172180
process-upload-report:
173181
runs-on: ubuntu-latest
174182
needs: [integration-tests]
175-
if: always() && github.repository == 'linode/linode_api4-python' # Run even if integration tests fail and only on main repository
183+
# Run even if integration tests fail on main repository AND push event OR test_report_upload is true in case of manual run
184+
if: always() && github.repository == 'linode/linode_api4-python' && (github.event_name == 'push' || (github.event_name == 'workflow_dispatch' && inputs.test_report_upload == 'true'))
176185
outputs:
177186
summary: ${{ steps.set-test-summary.outputs.summary }}
178187

@@ -184,7 +193,7 @@ jobs:
184193
submodules: 'recursive'
185194

186195
- name: Download test report
187-
uses: actions/download-artifact@v5
196+
uses: actions/download-artifact@v7
188197
with:
189198
name: test-report-file
190199

@@ -271,4 +280,4 @@ jobs:
271280
payload: |
272281
channel: ${{ secrets.SLACK_CHANNEL_ID }}
273282
thread_ts: "${{ steps.main_message.outputs.ts }}"
274-
text: "${{ needs.process-upload-report.outputs.summary }}"
283+
text: "${{ needs.process-upload-report.outputs.summary }}"

MANIFEST.in

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,6 @@
1+
# Include all files under test/ directory in source distribution only
12
graft test
3+
4+
# Exclude Python bytecode
25
global-exclude *.pyc
3-
include baked_version
6+
global-exclude __pycache__

docs/linode_api4/linode_client.rst

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -125,6 +125,15 @@ Includes methods for interacting with our Longview service.
125125
:members:
126126
:special-members:
127127

128+
LockGroup
129+
^^^^^^^^^^^^^
130+
131+
Includes methods for interacting with our Lock service.
132+
133+
.. autoclass:: linode_api4.linode_client.LockGroup
134+
:members:
135+
:special-members:
136+
128137
NetworkingGroup
129138
^^^^^^^^^^^^^^^
130139

linode_api4/groups/__init__.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,9 +6,11 @@
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 *
13+
from .lock import *
1214
from .longview import *
1315
from .maintenance import *
1416
from .monitor 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 & 5 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

@@ -337,8 +335,6 @@ def instance_create(
337335
:type network_helper: bool
338336
:param maintenance_policy: The slug of the maintenance policy to apply during maintenance.
339337
If not provided, the default policy (linode/migrate) will be applied.
340-
NOTE: This field is in beta and may only
341-
function if base_url is set to `https://api.linode.com/v4beta`.
342338
:type maintenance_policy: str
343339
344340
:returns: A new Instance object, or a tuple containing the new Instance and

linode_api4/groups/lke.py

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -62,8 +62,8 @@ def cluster_create(
6262
self,
6363
region,
6464
label,
65-
node_pools,
6665
kube_version,
66+
node_pools: Optional[list] = None,
6767
control_plane: Union[
6868
LKEClusterControlPlaneOptions, Dict[str, Any]
6969
] = None,
@@ -119,6 +119,15 @@ def cluster_create(
119119
:returns: The new LKE Cluster
120120
:rtype: LKECluster
121121
"""
122+
if node_pools is None:
123+
node_pools = []
124+
125+
if len(node_pools) == 0 and (
126+
tier is None or tier.lower() != "enterprise"
127+
):
128+
raise ValueError(
129+
"LKE standard clusters must have at least one node pool."
130+
)
122131

123132
params = {
124133
"label": label,

0 commit comments

Comments
 (0)