Skip to content

Commit c4404ae

Browse files
committed
feat: add image builder CLI entry point
Separate the image builder CLI entry point from the offer CLIs, as it is a different product type with specific delivery options and a more user-friendly lifecycle. also, separate the file to improve code readability.
1 parent 4638f12 commit c4404ae

3 files changed

Lines changed: 359 additions & 3 deletions

File tree

awsmp/cli.py renamed to awsmp/cli/__init__.py

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -11,8 +11,8 @@
1111
import yaml
1212
from botocore.exceptions import ClientError
1313

14-
from . import _driver, models, yaml_utils
15-
from .errors import (
14+
from .. import _driver, models, yaml_utils
15+
from ..errors import (
1616
AccessDeniedException,
1717
NoProductIdProvidedException,
1818
YamlMissingKeyException,
@@ -439,7 +439,6 @@ def _load_configuration(config_path: TextIO, required_fields: List[List[str]]) -
439439
if key not in temp_config:
440440
idx = keys.index(key)
441441
missing_keys = keys[idx:]
442-
443442
break
444443
else:
445444
if isinstance(temp_config[key], dict):
@@ -452,6 +451,11 @@ def _load_configuration(config_path: TextIO, required_fields: List[List[str]]) -
452451
return config
453452

454453

454+
from . import ( # noqa: F401 — must be after cli/group definitions to avoid circular import
455+
ib_cli,
456+
)
457+
458+
455459
def main():
456460
# Setting log format
457461
log_formatter = logging.Formatter("%(asctime)s:%(name)s:%(levelname)s:%(message)s")
@@ -460,6 +464,7 @@ def main():
460464
console_handler = logging.StreamHandler()
461465
console_handler.setFormatter(log_formatter)
462466
root_logger.addHandler(console_handler)
467+
463468
cli()
464469

465470

awsmp/cli/ib_cli.py

Lines changed: 192 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,192 @@
1+
#!/usr/bin/env python
2+
3+
import logging
4+
from typing import Dict, List, TextIO
5+
6+
import click
7+
8+
from .. import _driver, models
9+
from . import _load_configuration, cli
10+
11+
logger = logging.getLogger(__name__)
12+
13+
14+
def _load_ib_config(config: TextIO, required_fields: List[List[str]]) -> Dict:
15+
"""Load and return the ec2_image_builder section from config."""
16+
full_fields = [["ec2_image_builder"] + field for field in required_fields]
17+
return _load_configuration(config, full_fields)["ec2_image_builder"]
18+
19+
20+
@cli.group("image-builder")
21+
def image_builder():
22+
"""
23+
Manage EC2 Image Builder component products
24+
"""
25+
pass
26+
27+
28+
@image_builder.command("create")
29+
@click.option("--dry-run/--no-dry-run", is_flag=True)
30+
def ib_create(dry_run):
31+
"""
32+
Create a new AMI product listing for Image Builder components.
33+
"""
34+
response = _driver.AmiProduct.create(dry_run=dry_run)
35+
print(f'ChangeSet created (ID: {response["ChangeSetId"]})')
36+
print(f'https://aws.amazon.com/marketplace/management/requests/{response["ChangeSetId"]}')
37+
38+
39+
@image_builder.command("add-version")
40+
@click.option("--product-id", required=True, prompt=True)
41+
@click.option("--config", type=click.File("r"), required=True, prompt=True)
42+
@click.option("--dry-run/--no-dry-run", is_flag=True)
43+
def ib_add_version(product_id: str, config: TextIO, dry_run: bool) -> None:
44+
"""
45+
Create IB component(s) and submit AddDeliveryOptions.
46+
"""
47+
configs = _load_configuration(config, [["ec2_image_builder"]])
48+
ib_product = models.IBProduct(**configs["ec2_image_builder"])
49+
product = _driver.IbProduct(product_id=product_id, dry_run=dry_run)
50+
response = product.add_version(ib_product)
51+
print(f'ChangeSet created (ID: {response["ChangeSetId"]})')
52+
print(f'https://aws.amazon.com/marketplace/management/requests/{response["ChangeSetId"]}')
53+
54+
55+
@image_builder.command("restrict-version")
56+
@click.option("--product-id", required=True, prompt=True)
57+
@click.option("--delivery-option-id", required=True, multiple=True)
58+
@click.option("--dry-run/--no-dry-run", is_flag=True)
59+
def ib_restrict_version(product_id: str, delivery_option_id: tuple, dry_run: bool) -> None:
60+
"""
61+
Submit RestrictDeliveryOptions for given delivery option IDs.
62+
"""
63+
product = _driver.IbProduct(product_id=product_id, dry_run=dry_run)
64+
response = product.restrict_version(list(delivery_option_id))
65+
print(f'ChangeSet created (ID: {response["ChangeSetId"]})')
66+
print(f'https://aws.amazon.com/marketplace/management/requests/{response["ChangeSetId"]}')
67+
68+
69+
@image_builder.command("update-description")
70+
@click.option("--product-id", required=True, prompt=True)
71+
@click.option("--config", type=click.File("r"), required=True, prompt=True)
72+
@click.option("--dry-run/--no-dry-run", is_flag=True)
73+
def ib_update_description(product_id, config, dry_run):
74+
"""
75+
Update product description from ec2_image_builder config.
76+
"""
77+
ib_config = _load_ib_config(config, [["description"]])
78+
response = _driver.AmiProduct(product_id=product_id, dry_run=dry_run).update_description(ib_config["description"])
79+
print(f'ChangeSet created (ID: {response["ChangeSetId"]})')
80+
print(f'https://aws.amazon.com/marketplace/management/requests/{response["ChangeSetId"]}')
81+
82+
83+
@image_builder.command("update-region")
84+
@click.option("--product-id", required=True, prompt=True)
85+
@click.option("--config", type=click.File("r"), required=True, prompt=True)
86+
@click.option("--dry-run/--no-dry-run", is_flag=True)
87+
def ib_update_region(product_id, config, dry_run):
88+
"""
89+
Update product regions from ec2_image_builder config.
90+
"""
91+
ib_config = _load_ib_config(config, [["region"]])
92+
response = _driver.AmiProduct(product_id=product_id, dry_run=dry_run).update_regions(ib_config["region"])
93+
print(f'ChangeSet created (ID: {response["ChangeSetId"]})')
94+
print(f'https://aws.amazon.com/marketplace/management/requests/{response["ChangeSetId"]}')
95+
96+
97+
@image_builder.command("update-instance-type")
98+
@click.option("--product-id", required=True, prompt=True)
99+
@click.option("--config", type=click.File("r"), required=True, prompt=True)
100+
@click.option(
101+
"--allow-price-change/--no-allow-price-change",
102+
required=True,
103+
default=False,
104+
is_flag=True,
105+
prompt="Is price update allowed? (y/N). Default is False.",
106+
)
107+
@click.option("--dry-run/--no-dry-run", is_flag=True)
108+
def ib_update_instance_type(product_id: str, config: TextIO, allow_price_change: bool, dry_run: bool) -> None:
109+
"""
110+
Update instance types and pricing from ec2_image_builder config.
111+
"""
112+
ib_config = _load_ib_config(config, [["offer"]])
113+
product = _driver.AmiProduct(product_id=product_id, dry_run=dry_run)
114+
response = product.update_instance_types(ib_config["offer"], allow_price_change)
115+
if response:
116+
print(f'ChangeSet created (ID: {response["ChangeSetId"]})')
117+
print(f'https://aws.amazon.com/marketplace/management/requests/{response["ChangeSetId"]}')
118+
119+
120+
@image_builder.command("update-legal-terms")
121+
@click.option("--product-id", required=True, prompt=True)
122+
@click.option("--config", type=click.File("r"), required=True, prompt=True)
123+
@click.option("--dry-run/--no-dry-run", is_flag=True)
124+
def ib_update_legal_terms(product_id, config, dry_run):
125+
"""
126+
Update legal terms from ec2_image_builder config.
127+
"""
128+
ib_config = _load_ib_config(config, [["offer", "eula_document"]])
129+
eula_document = ib_config["offer"]["eula_document"][0]
130+
response = _driver.AmiProduct(product_id=product_id, dry_run=dry_run).update_legal_terms(eula_document)
131+
print(f'ChangeSet created (ID: {response["ChangeSetId"]})')
132+
print(f'https://aws.amazon.com/marketplace/management/requests/{response["ChangeSetId"]}')
133+
134+
135+
@image_builder.command("update-support-terms")
136+
@click.option("--product-id", required=True, prompt=True)
137+
@click.option("--config", type=click.File("r"), required=True, prompt=True)
138+
@click.option("--dry-run/--no-dry-run", is_flag=True)
139+
def ib_update_support_terms(product_id, config, dry_run):
140+
"""
141+
Update support terms from ec2_image_builder config.
142+
"""
143+
ib_config = _load_ib_config(config, [["offer", "refund_policy"]])
144+
response = _driver.AmiProduct(product_id=product_id, dry_run=dry_run).update_support_terms(
145+
ib_config["offer"]["refund_policy"]
146+
)
147+
print(f'ChangeSet created (ID: {response["ChangeSetId"]})')
148+
print(f'https://aws.amazon.com/marketplace/management/requests/{response["ChangeSetId"]}')
149+
150+
151+
@image_builder.command("release")
152+
@click.option("--product-id", required=True, prompt=True)
153+
@click.option("--dry-run/--no-dry-run", is_flag=True)
154+
def ib_release(product_id, dry_run):
155+
"""
156+
Publish Image Builder product as Limited.
157+
"""
158+
product = _driver.AmiProduct(product_id=product_id, dry_run=dry_run)
159+
response = product.release()
160+
print(f'ChangeSet created (ID: {response["ChangeSetId"]})')
161+
print(f'https://aws.amazon.com/marketplace/management/requests/{response["ChangeSetId"]}')
162+
163+
164+
@image_builder.command("update")
165+
@click.option("--product-id", required=True, prompt=True)
166+
@click.option("--config", type=click.File("r"), required=True, prompt=True)
167+
@click.option(
168+
"--allow-price-change/--no-allow-price-change",
169+
required=True,
170+
default=False,
171+
is_flag=True,
172+
prompt="Is price update allowed? (y/N). Default is False.",
173+
)
174+
@click.option("--dry-run/--no-dry-run", is_flag=True)
175+
def ib_update(product_id: str, config: TextIO, allow_price_change: bool, dry_run: bool) -> None:
176+
"""
177+
Update product details (description, region, instance type and pricing) in a single call
178+
from ec2_image_builder config.
179+
"""
180+
ib_config = _load_ib_config(config, [["description"], ["region"], ["offer"]])
181+
configs = {
182+
"product": {
183+
"description": ib_config["description"],
184+
"region": ib_config["region"],
185+
},
186+
"offer": ib_config["offer"],
187+
}
188+
product = _driver.AmiProduct(product_id=product_id, dry_run=dry_run)
189+
response = product.update(configs, allow_price_change)
190+
if response:
191+
print(f'ChangeSet created (ID: {response["ChangeSetId"]})')
192+
print(f'https://aws.amazon.com/marketplace/management/requests/{response["ChangeSetId"]}')

0 commit comments

Comments
 (0)