Skip to content

Commit 3dc7147

Browse files
Merge pull request #457 from linode/dev
Release v5.38.0
2 parents 0b915fb + 6a9b589 commit 3dc7147

14 files changed

+309
-188
lines changed

.github/workflows/e2e-suite-pr-command.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ jobs:
1010
if: ${{ github.event.issue.pull_request }}
1111
steps:
1212
- name: Slash Command Dispatch
13-
uses: peter-evans/slash-command-dispatch@v3
13+
uses: peter-evans/slash-command-dispatch@v1.2.0
1414
with:
1515
token: ${{ secrets.GITHUB_TOKEN }}
1616
issue-type: pull-request

.github/workflows/unit-tests.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ jobs:
2020
python-version: '3.x'
2121

2222
- name: Install Python wheel
23-
run: pip install wheel boto3 mock
23+
run: pip install wheel boto3
2424

2525
- name: Update cert
2626
run: pip install certifi -U

linodecli/operation.py

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,8 @@
1111

1212
from linodecli.helpers import handle_url_overrides
1313

14+
from .overrides import OUTPUT_OVERRIDES
15+
1416

1517
def parse_boolean(value):
1618
"""
@@ -346,6 +348,12 @@ def process_response_json(
346348
if self.response_model is None:
347349
return
348350

351+
override = OUTPUT_OVERRIDES.get(
352+
(self.command, self.action, handler.mode)
353+
)
354+
if override is not None and not override(self, handler, json):
355+
return
356+
349357
json = self.response_model.fix_json(json)
350358

351359
handler.print(self.response_model, json)

linodecli/overrides.py

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
"""
2+
Contains wrappers for overriding certain pieces of command-handling logic.
3+
This allows us to easily alter per-command outputs, etc. without making
4+
large changes to the OpenAPI spec.
5+
"""
6+
7+
from linodecli.output import OutputMode
8+
9+
OUTPUT_OVERRIDES = {}
10+
11+
12+
def output_override(command: str, action: str, output_mode: OutputMode):
13+
"""
14+
A decorator function for adding a new output override handler.
15+
16+
Output override functions should have the following signature::
17+
18+
@output_override("command", "action", OutputMode.{output_mode})
19+
def my_override(operation, output_handler, json_data) -> bool:
20+
...
21+
22+
If the returned bool is False, the original output functionality will be skipped.
23+
Otherwise, the original output functionality will continue as normal.
24+
"""
25+
26+
def inner(func):
27+
OUTPUT_OVERRIDES[(command, action, output_mode)] = func
28+
29+
return inner
30+
31+
32+
@output_override("domains", "zone-file", OutputMode.delimited)
33+
def handle_domains_zone_file(operation, output_handler, json_data) -> bool:
34+
# pylint: disable=unused-argument
35+
"""
36+
Fix for output of 'linode-cli domains zone-file --text {id}'.
37+
"""
38+
print("\n".join(json_data["zone_file"]))
39+
return False

linodecli/plugins/obj/__init__.py

Lines changed: 5 additions & 115 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,11 @@
5454
get_object,
5555
upload_object,
5656
)
57+
from linodecli.plugins.obj.website import (
58+
disable_static_site,
59+
enable_static_site,
60+
static_site_info,
61+
)
5762

5863
try:
5964
import boto3
@@ -260,97 +265,6 @@ def set_acl(get_client, args):
260265
print("ACL updated")
261266

262267

263-
def enable_static_site(get_client, args):
264-
"""
265-
Turns a bucket into a static website
266-
"""
267-
parser = inherit_plugin_args(ArgumentParser(PLUGIN_BASE + " ws-create"))
268-
269-
parser.add_argument(
270-
"bucket",
271-
metavar="BUCKET",
272-
type=str,
273-
help="The bucket to turn into a static site",
274-
)
275-
parser.add_argument(
276-
"--ws-index",
277-
metavar="INDEX",
278-
required=True,
279-
type=str,
280-
help="The file to serve as the index of the website",
281-
)
282-
parser.add_argument(
283-
"--ws-error",
284-
metavar="ERROR",
285-
type=str,
286-
help="The file to serve as the error page of the website",
287-
)
288-
289-
parsed = parser.parse_args(args)
290-
client = get_client()
291-
bucket = parsed.bucket
292-
293-
# make the site
294-
print(f"Setting bucket {bucket} access control to be 'public-read'")
295-
296-
client.put_bucket_acl(
297-
Bucket=bucket,
298-
ACL="public-read",
299-
)
300-
301-
index_page = parsed.ws_index
302-
303-
ws_config = {"IndexDocument": {"Suffix": index_page}}
304-
if parsed.ws_error:
305-
ws_config["ErrorDocument"] = {"Key": parsed.ws_error}
306-
307-
client.put_bucket_website(
308-
Bucket=bucket,
309-
WebsiteConfiguration=ws_config,
310-
)
311-
312-
print(
313-
"Static site now available at "
314-
f"{BASE_WEBSITE_TEMPLATE.format(cluster=client.cluster, bucket=bucket)}"
315-
"\nIf you still can't access the website, please check the "
316-
"Access Control List setting of the website related objects (files) "
317-
"in your bucket."
318-
)
319-
320-
321-
def static_site_info(get_client, args):
322-
"""
323-
Returns info about a configured static site
324-
"""
325-
parser = inherit_plugin_args(ArgumentParser(PLUGIN_BASE + " ws-info"))
326-
327-
parser.add_argument(
328-
"bucket",
329-
metavar="BUCKET",
330-
type=str,
331-
help="The bucket to return static site information on.",
332-
)
333-
334-
parsed = parser.parse_args(args)
335-
client = get_client()
336-
337-
bucket = parsed.bucket
338-
339-
response = client.get_bucket_website(Bucket=bucket)
340-
341-
index = response.get("IndexDocument", {}).get("Suffix", "Not Configured")
342-
error = response.get("ErrorDocument", {}).get("Key", "Not Configured")
343-
344-
endpoint = BASE_WEBSITE_TEMPLATE.format(
345-
cluster=client.cluster, bucket=bucket
346-
)
347-
348-
print(f"Bucket {bucket}: Website configuration")
349-
print(f"Website endpoint: {endpoint}")
350-
print(f"Index document: {index}")
351-
print(f"Error document: {error}")
352-
353-
354268
def show_usage(get_client, args):
355269
"""
356270
Shows space used by all buckets in this cluster, and total space
@@ -437,30 +351,6 @@ def list_all_objects(get_client, args):
437351
sys.exit(0)
438352

439353

440-
def disable_static_site(get_client, args):
441-
"""
442-
Disables static site for a bucket
443-
"""
444-
parser = inherit_plugin_args(ArgumentParser(PLUGIN_BASE + " du"))
445-
446-
parser.add_argument(
447-
"bucket",
448-
metavar="BUCKET",
449-
type=str,
450-
nargs="?",
451-
help="The bucket to disable static site for.",
452-
)
453-
454-
parsed = parser.parse_args(args)
455-
client = get_client()
456-
457-
bucket = parsed.bucket
458-
459-
client.delete_bucket_website(Bucket=bucket)
460-
461-
print(f"Website configuration deleted for {parsed.bucket}")
462-
463-
464354
COMMAND_MAP = {
465355
"mb": create_bucket,
466356
"rb": delete_bucket,

linodecli/plugins/obj/website.py

Lines changed: 123 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,123 @@
1+
"""
2+
The static website module of CLI Plugin for handling object storage
3+
"""
4+
5+
from argparse import ArgumentParser
6+
7+
from linodecli.plugins import inherit_plugin_args
8+
from linodecli.plugins.obj.config import BASE_WEBSITE_TEMPLATE, PLUGIN_BASE
9+
10+
11+
def enable_static_site(get_client, args):
12+
"""
13+
Turns a bucket into a static website
14+
"""
15+
parser = inherit_plugin_args(ArgumentParser(PLUGIN_BASE + " ws-create"))
16+
17+
parser.add_argument(
18+
"bucket",
19+
metavar="BUCKET",
20+
type=str,
21+
help="The bucket to turn into a static site",
22+
)
23+
parser.add_argument(
24+
"--ws-index",
25+
metavar="INDEX",
26+
required=True,
27+
type=str,
28+
help="The file to serve as the index of the website",
29+
)
30+
parser.add_argument(
31+
"--ws-error",
32+
metavar="ERROR",
33+
type=str,
34+
help="The file to serve as the error page of the website",
35+
)
36+
37+
parsed = parser.parse_args(args)
38+
client = get_client()
39+
bucket = parsed.bucket
40+
41+
# make the site
42+
print(f"Setting bucket {bucket} access control to be 'public-read'")
43+
44+
client.put_bucket_acl(
45+
Bucket=bucket,
46+
ACL="public-read",
47+
)
48+
49+
index_page = parsed.ws_index
50+
51+
ws_config = {"IndexDocument": {"Suffix": index_page}}
52+
if parsed.ws_error:
53+
ws_config["ErrorDocument"] = {"Key": parsed.ws_error}
54+
55+
client.put_bucket_website(
56+
Bucket=bucket,
57+
WebsiteConfiguration=ws_config,
58+
)
59+
60+
print(
61+
"Static site now available at "
62+
f"{BASE_WEBSITE_TEMPLATE.format(cluster=client.cluster, bucket=bucket)}"
63+
"\nIf you still can't access the website, please check the "
64+
"Access Control List setting of the website related objects (files) "
65+
"in your bucket."
66+
)
67+
68+
69+
def static_site_info(get_client, args):
70+
"""
71+
Returns info about a configured static site
72+
"""
73+
parser = inherit_plugin_args(ArgumentParser(PLUGIN_BASE + " ws-info"))
74+
75+
parser.add_argument(
76+
"bucket",
77+
metavar="BUCKET",
78+
type=str,
79+
help="The bucket to return static site information on.",
80+
)
81+
82+
parsed = parser.parse_args(args)
83+
client = get_client()
84+
85+
bucket = parsed.bucket
86+
87+
response = client.get_bucket_website(Bucket=bucket)
88+
89+
index = response.get("IndexDocument", {}).get("Suffix", "Not Configured")
90+
error = response.get("ErrorDocument", {}).get("Key", "Not Configured")
91+
92+
endpoint = BASE_WEBSITE_TEMPLATE.format(
93+
cluster=client.cluster, bucket=bucket
94+
)
95+
96+
print(f"Bucket {bucket}: Website configuration")
97+
print(f"Website endpoint: {endpoint}")
98+
print(f"Index document: {index}")
99+
print(f"Error document: {error}")
100+
101+
102+
def disable_static_site(get_client, args):
103+
"""
104+
Disables static site for a bucket
105+
"""
106+
parser = inherit_plugin_args(ArgumentParser(PLUGIN_BASE + " du"))
107+
108+
parser.add_argument(
109+
"bucket",
110+
metavar="BUCKET",
111+
type=str,
112+
nargs="?",
113+
help="The bucket to disable static site for.",
114+
)
115+
116+
parsed = parser.parse_args(args)
117+
client = get_client()
118+
119+
bucket = parsed.bucket
120+
121+
client.delete_bucket_website(Bucket=bucket)
122+
123+
print(f"Website configuration deleted for {parsed.bucket}")

requirements-dev.txt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
pylint==2.17.3
1+
pylint==2.17.4
22
pytest==7.3.1
33
black>=23.1.0
44
isort>=5.12.0

tests/unit/test_api_request.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,9 +6,9 @@
66
import io
77
import json
88
from types import SimpleNamespace
9+
from unittest.mock import Mock, patch
910

1011
import requests
11-
from mock import Mock, patch
1212

1313
from linodecli import api_request
1414

tests/unit/test_completion.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
Unit tests for linodecli.completion
44
"""
55

6-
from mock import mock_open, patch
6+
from unittest.mock import mock_open, patch
77

88
from linodecli import completion
99

0 commit comments

Comments
 (0)