Skip to content

Commit 8ff1e29

Browse files
authored
Merge pull request #30 from synkd/improve_cli_functionality_and_tests
Improve CLI functionality and CLI tests
2 parents 2fd8162 + d6e6423 commit 8ff1e29

File tree

4 files changed

+227
-83
lines changed

4 files changed

+227
-83
lines changed

manifester/commands.py

+66-6
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,12 @@
11
"""Defines the CLI commands for Manifester."""
2+
import os
3+
from pathlib import Path
4+
25
import click
6+
from logzero import logger
37

4-
from manifester import Manifester
8+
from manifester import Manifester, helpers
9+
from manifester.settings import settings
510

611

712
# To do: add a command for returning subscription pools
@@ -13,18 +18,73 @@ def cli():
1318

1419
@cli.command()
1520
@click.option(
16-
"--manifest_category",
21+
"--manifest-category",
1722
type=str,
1823
help="Category of manifest (golden_ticket or robottelo_automation by default)",
1924
)
20-
@click.option("--allocation_name", type=str, help="Name of upstream subscription allocation")
21-
def get_manifest(manifest_category, allocation_name):
25+
@click.option("--allocation-name", type=str, help="Name of upstream subscription allocation")
26+
@click.option("--requester", type=str, default=None)
27+
def get_manifest(manifest_category, allocation_name, requester):
2228
"""Return a subscription manifester based on the settings for the provided manifest_category."""
23-
manifester = Manifester(manifest_category, allocation_name)
29+
manifester = Manifester(manifest_category, allocation_name, requester=requester)
2430
manifester.create_subscription_allocation()
2531
for sub in manifester.subscription_data:
2632
manifester.process_subscription_pools(
2733
subscription_pools=manifester.subscription_pools,
2834
subscription_data=sub,
2935
)
30-
manifester.trigger_manifest_export()
36+
return manifester.trigger_manifest_export()
37+
38+
39+
@cli.command()
40+
@click.argument("allocations", type=str, nargs=-1)
41+
@click.option(
42+
"--all",
43+
"all_",
44+
is_flag=True,
45+
default=False,
46+
help="Delete all subscription allocations in inventory",
47+
)
48+
@click.option(
49+
"--remove-manifest-file",
50+
is_flag=True,
51+
default=False,
52+
help="Delete local manifest files in addition to upstream subscription allocations",
53+
)
54+
def delete(allocations, all_, remove_manifest_file):
55+
"""Delete subscription allocations in inventory and optionally delete local manifest files."""
56+
inv = helpers.load_inventory_file(Path(settings.inventory_path))
57+
for num, allocation in enumerate(inv):
58+
if str(num) in allocations or allocation.get("name") in allocations or all_:
59+
Manifester(minimal_init=True).delete_subscription_allocation(
60+
uuid=allocation.get("uuid")
61+
)
62+
if remove_manifest_file:
63+
Path(
64+
f"{os.environ['MANIFESTER_DIRECTORY']}/manifests/{allocation.get('name')}_manifest.zip"
65+
).unlink()
66+
67+
68+
@cli.command()
69+
@click.option("--details", is_flag=True, help="Display full inventory details")
70+
@click.option("--sync", is_flag=True, help="Fetch inventory data from RHSM before displaying")
71+
def inventory(details, sync):
72+
"""Display the local inventory file's contents."""
73+
border = "-" * 38
74+
if sync:
75+
helpers.update_inventory(Manifester(minimal_init=True).subscription_allocations)
76+
inv = helpers.load_inventory_file(Path(settings.inventory_path))
77+
if not details:
78+
logger.info("Displaying local inventory data")
79+
click.echo(border)
80+
click.echo(f"| {'Index'} | {'Allocation Name':<26} |")
81+
click.echo(border)
82+
for num, allocation in enumerate(inv):
83+
click.echo(f"| {num:<5} | {allocation['name']:<26} |")
84+
click.echo(border)
85+
else:
86+
logger.info("Displaying detailed local inventory data")
87+
for num, allocation in enumerate(inv):
88+
click.echo(f"{num}:")
89+
for key, value in allocation.items():
90+
click.echo(f"{'':<4}{key}: {value}")

manifester/helpers.py

+4-3
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,6 @@
1010

1111
from manifester.settings import settings
1212

13-
MAX_RESULTS_PER_PAGE = 50
1413
RESULTS_LIMIT = 10000
1514

1615

@@ -64,9 +63,11 @@ def fetch_paginated_data(manifester, endpoint):
6463
if endpoint == "allocations":
6564
_endpoint_url = manifester.allocations_url
6665
_endpoint_data = manifester._allocations
66+
MAX_RESULTS_PER_PAGE = 100
6767
elif endpoint == "pools":
6868
_endpoint_url = f"{manifester.allocations_url}/{manifester.allocation_uuid}/pools"
6969
_endpoint_data = manifester._subscription_pools
70+
MAX_RESULTS_PER_PAGE = 50
7071
else:
7172
raise ValueError(
7273
f"Received value {endpoint} for endpoint argument. Valid values "
@@ -104,7 +105,7 @@ def fetch_paginated_data(manifester, endpoint):
104105
# around this limit by repeating calls with a progressively larger value for the `offset`
105106
# parameter.
106107
while _results == MAX_RESULTS_PER_PAGE:
107-
_offset += 50
108+
_offset += MAX_RESULTS_PER_PAGE
108109
logger.debug(f"Fetching additional data with an offset of {_offset}.")
109110
data = {
110111
"headers": {"Authorization": f"Bearer {manifester.access_token}"},
@@ -135,7 +136,7 @@ def fetch_paginated_data(manifester, endpoint):
135136
if hasattr(_endpoint_data, "force_export_failure"):
136137
return [
137138
a
138-
for a in _endpoint_data.allocation_data["body"]
139+
for a in _endpoint_data.allocations_response["body"]
139140
if a["name"].startswith(manifester.username_prefix)
140141
]
141142
else:

manifester/manifester.py

+75-43
Original file line numberDiff line numberDiff line change
@@ -20,51 +20,81 @@
2020
)
2121
from manifester.settings import settings
2222

23-
MAX_RESULTS_PER_PAGE = 50
24-
RESULTS_LIMIT = 10000
25-
2623

2724
class Manifester:
2825
"""Main Manifester class responsible for generating a manifest from the provided settings."""
2926

30-
def __init__(self, manifest_category, allocation_name=None, **kwargs):
31-
if isinstance(manifest_category, dict):
32-
self.manifest_data = DynaBox(manifest_category)
33-
else:
34-
self.manifest_data = settings.manifest_category.get(manifest_category)
35-
if kwargs.get("requester") is not None:
36-
self.requester = kwargs["requester"]
37-
self.is_mock = True
27+
def __init__(
28+
self,
29+
manifest_category=None,
30+
allocation_name=None,
31+
minimal_init=False,
32+
proxies=None,
33+
**kwargs,
34+
):
35+
if minimal_init:
36+
self.offline_token = settings.get("offline_token")
37+
self.token_request_url = settings.get("url").get("token_request")
38+
self.allocations_url = settings.get("url").get("allocations")
39+
self._access_token = None
40+
self._allocations = None
41+
self.token_request_data = {
42+
"grant_type": "refresh_token",
43+
"client_id": "rhsm-api",
44+
"refresh_token": self.offline_token,
45+
}
46+
self.manifest_data = {"proxies": proxies}
47+
self.username_prefix = settings.get("username_prefix")
48+
if kwargs.get("requester") is not None:
49+
self.requester = kwargs["requester"]
50+
self.is_mock = True
51+
else:
52+
import requests
53+
54+
self.requester = requests
55+
self.is_mock = False
3856
else:
39-
import requests
57+
if isinstance(manifest_category, dict):
58+
self.manifest_data = DynaBox(manifest_category)
59+
else:
60+
self.manifest_data = settings.manifest_category.get(manifest_category)
61+
if kwargs.get("requester") is not None:
62+
self.requester = kwargs["requester"]
63+
self.is_mock = True
64+
else:
65+
import requests
4066

41-
self.requester = requests
42-
self.is_mock = False
43-
self.username_prefix = settings.username_prefix or self.manifest_data.username_prefix
44-
self.allocation_name = allocation_name or f"{self.username_prefix}-" + "".join(
45-
random.sample(string.ascii_letters, 8)
46-
)
47-
self.manifest_name = Path(f"{self.allocation_name}_manifest.zip")
48-
self.offline_token = kwargs.get("offline_token", self.manifest_data.offline_token)
49-
self.subscription_data = self.manifest_data.subscription_data
50-
self.token_request_data = {
51-
"grant_type": "refresh_token",
52-
"client_id": "rhsm-api",
53-
"refresh_token": self.offline_token,
54-
}
55-
self.simple_content_access = kwargs.get(
56-
"simple_content_access", self.manifest_data.simple_content_access
57-
)
58-
self.token_request_url = self.manifest_data.get("url").get("token_request")
59-
self.allocations_url = self.manifest_data.get("url").get("allocations")
60-
self._access_token = None
61-
self._allocations = None
62-
self._subscription_pools = None
63-
self._active_pools = []
64-
self.sat_version = process_sat_version(
65-
kwargs.get("sat_version", self.manifest_data.sat_version),
66-
self.valid_sat_versions,
67-
)
67+
self.requester = requests
68+
self.is_mock = False
69+
self.username_prefix = (
70+
self.manifest_data.get("username_prefix") or settings.username_prefix
71+
)
72+
self.allocation_name = allocation_name or f"{self.username_prefix}-" + "".join(
73+
random.sample(string.ascii_letters, 8)
74+
)
75+
self.manifest_name = Path(f"{self.allocation_name}_manifest.zip")
76+
self.offline_token = self.manifest_data.get(
77+
"offline_token", settings.get("offline_token")
78+
)
79+
self.subscription_data = self.manifest_data.subscription_data
80+
self.token_request_data = {
81+
"grant_type": "refresh_token",
82+
"client_id": "rhsm-api",
83+
"refresh_token": self.offline_token,
84+
}
85+
self.simple_content_access = kwargs.get(
86+
"simple_content_access", self.manifest_data.simple_content_access
87+
)
88+
self.token_request_url = self.manifest_data.get("url").get("token_request")
89+
self.allocations_url = self.manifest_data.get("url").get("allocations")
90+
self._access_token = None
91+
self._allocations = None
92+
self._subscription_pools = None
93+
self._active_pools = []
94+
self.sat_version = process_sat_version(
95+
kwargs.get("sat_version", self.manifest_data.sat_version),
96+
self.valid_sat_versions,
97+
)
6898

6999
@property
70100
def access_token(self):
@@ -113,7 +143,7 @@ def subscription_allocations(self):
113143

114144
@property
115145
def subscription_pools(self):
116-
"""Reprentation of subscription pools in an account."""
146+
"""Representation of subscription pools in an account."""
117147
return fetch_paginated_data(self, "pools")
118148

119149
def create_subscription_allocation(self):
@@ -151,7 +181,7 @@ def create_subscription_allocation(self):
151181
update_inventory(self.subscription_allocations)
152182
return self.allocation_uuid
153183

154-
def delete_subscription_allocation(self):
184+
def delete_subscription_allocation(self, uuid=None):
155185
"""Deletes the specified subscription allocation and returns the RHSM API's response."""
156186
self._access_token = None
157187
data = {
@@ -163,9 +193,10 @@ def delete_subscription_allocation(self):
163193
self.allocation_uuid = self.allocation_uuid.uuid
164194
response = simple_retry(
165195
self.requester.delete,
166-
cmd_args=[f"{self.allocations_url}/{self.allocation_uuid}"],
196+
cmd_args=[f"{self.allocations_url}/{uuid if uuid else self.allocation_uuid}"],
167197
cmd_kwargs=data,
168198
)
199+
update_inventory(self.subscription_allocations)
169200
return response
170201

171202
def add_entitlements_to_allocation(self, pool_id, entitlement_quantity):
@@ -367,6 +398,7 @@ def trigger_manifest_export(self):
367398
local_file.write_bytes(manifest.content)
368399
manifest.path = local_file
369400
manifest.name = self.manifest_name
401+
update_inventory(self.subscription_allocations)
370402
return manifest
371403

372404
def get_manifest(self):
@@ -392,6 +424,6 @@ def __enter__(self):
392424
raise
393425

394426
def __exit__(self, *tb_args):
395-
"""Deletes subscription allocation on teardown."""
427+
"""Deletes subscription allocation on teardown unless using CLI."""
396428
self.delete_subscription_allocation()
397429
update_inventory(self.subscription_allocations)

0 commit comments

Comments
 (0)