Skip to content

Commit

Permalink
Merge pull request #30 from synkd/improve_cli_functionality_and_tests
Browse files Browse the repository at this point in the history
Improve CLI functionality and CLI tests
  • Loading branch information
synkd authored Mar 13, 2024
2 parents 2fd8162 + d6e6423 commit 8ff1e29
Show file tree
Hide file tree
Showing 4 changed files with 227 additions and 83 deletions.
72 changes: 66 additions & 6 deletions manifester/commands.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,12 @@
"""Defines the CLI commands for Manifester."""
import os
from pathlib import Path

import click
from logzero import logger

from manifester import Manifester
from manifester import Manifester, helpers
from manifester.settings import settings


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

@cli.command()
@click.option(
"--manifest_category",
"--manifest-category",
type=str,
help="Category of manifest (golden_ticket or robottelo_automation by default)",
)
@click.option("--allocation_name", type=str, help="Name of upstream subscription allocation")
def get_manifest(manifest_category, allocation_name):
@click.option("--allocation-name", type=str, help="Name of upstream subscription allocation")
@click.option("--requester", type=str, default=None)
def get_manifest(manifest_category, allocation_name, requester):
"""Return a subscription manifester based on the settings for the provided manifest_category."""
manifester = Manifester(manifest_category, allocation_name)
manifester = Manifester(manifest_category, allocation_name, requester=requester)
manifester.create_subscription_allocation()
for sub in manifester.subscription_data:
manifester.process_subscription_pools(
subscription_pools=manifester.subscription_pools,
subscription_data=sub,
)
manifester.trigger_manifest_export()
return manifester.trigger_manifest_export()


@cli.command()
@click.argument("allocations", type=str, nargs=-1)
@click.option(
"--all",
"all_",
is_flag=True,
default=False,
help="Delete all subscription allocations in inventory",
)
@click.option(
"--remove-manifest-file",
is_flag=True,
default=False,
help="Delete local manifest files in addition to upstream subscription allocations",
)
def delete(allocations, all_, remove_manifest_file):
"""Delete subscription allocations in inventory and optionally delete local manifest files."""
inv = helpers.load_inventory_file(Path(settings.inventory_path))
for num, allocation in enumerate(inv):
if str(num) in allocations or allocation.get("name") in allocations or all_:
Manifester(minimal_init=True).delete_subscription_allocation(
uuid=allocation.get("uuid")
)
if remove_manifest_file:
Path(
f"{os.environ['MANIFESTER_DIRECTORY']}/manifests/{allocation.get('name')}_manifest.zip"
).unlink()


@cli.command()
@click.option("--details", is_flag=True, help="Display full inventory details")
@click.option("--sync", is_flag=True, help="Fetch inventory data from RHSM before displaying")
def inventory(details, sync):
"""Display the local inventory file's contents."""
border = "-" * 38
if sync:
helpers.update_inventory(Manifester(minimal_init=True).subscription_allocations)
inv = helpers.load_inventory_file(Path(settings.inventory_path))
if not details:
logger.info("Displaying local inventory data")
click.echo(border)
click.echo(f"| {'Index'} | {'Allocation Name':<26} |")
click.echo(border)
for num, allocation in enumerate(inv):
click.echo(f"| {num:<5} | {allocation['name']:<26} |")
click.echo(border)
else:
logger.info("Displaying detailed local inventory data")
for num, allocation in enumerate(inv):
click.echo(f"{num}:")
for key, value in allocation.items():
click.echo(f"{'':<4}{key}: {value}")
7 changes: 4 additions & 3 deletions manifester/helpers.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,6 @@

from manifester.settings import settings

MAX_RESULTS_PER_PAGE = 50
RESULTS_LIMIT = 10000


Expand Down Expand Up @@ -64,9 +63,11 @@ def fetch_paginated_data(manifester, endpoint):
if endpoint == "allocations":
_endpoint_url = manifester.allocations_url
_endpoint_data = manifester._allocations
MAX_RESULTS_PER_PAGE = 100
elif endpoint == "pools":
_endpoint_url = f"{manifester.allocations_url}/{manifester.allocation_uuid}/pools"
_endpoint_data = manifester._subscription_pools
MAX_RESULTS_PER_PAGE = 50
else:
raise ValueError(
f"Received value {endpoint} for endpoint argument. Valid values "
Expand Down Expand Up @@ -104,7 +105,7 @@ def fetch_paginated_data(manifester, endpoint):
# around this limit by repeating calls with a progressively larger value for the `offset`
# parameter.
while _results == MAX_RESULTS_PER_PAGE:
_offset += 50
_offset += MAX_RESULTS_PER_PAGE
logger.debug(f"Fetching additional data with an offset of {_offset}.")
data = {
"headers": {"Authorization": f"Bearer {manifester.access_token}"},
Expand Down Expand Up @@ -135,7 +136,7 @@ def fetch_paginated_data(manifester, endpoint):
if hasattr(_endpoint_data, "force_export_failure"):
return [
a
for a in _endpoint_data.allocation_data["body"]
for a in _endpoint_data.allocations_response["body"]
if a["name"].startswith(manifester.username_prefix)
]
else:
Expand Down
118 changes: 75 additions & 43 deletions manifester/manifester.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,51 +20,81 @@
)
from manifester.settings import settings

MAX_RESULTS_PER_PAGE = 50
RESULTS_LIMIT = 10000


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

def __init__(self, manifest_category, allocation_name=None, **kwargs):
if isinstance(manifest_category, dict):
self.manifest_data = DynaBox(manifest_category)
else:
self.manifest_data = settings.manifest_category.get(manifest_category)
if kwargs.get("requester") is not None:
self.requester = kwargs["requester"]
self.is_mock = True
def __init__(
self,
manifest_category=None,
allocation_name=None,
minimal_init=False,
proxies=None,
**kwargs,
):
if minimal_init:
self.offline_token = settings.get("offline_token")
self.token_request_url = settings.get("url").get("token_request")
self.allocations_url = settings.get("url").get("allocations")
self._access_token = None
self._allocations = None
self.token_request_data = {
"grant_type": "refresh_token",
"client_id": "rhsm-api",
"refresh_token": self.offline_token,
}
self.manifest_data = {"proxies": proxies}
self.username_prefix = settings.get("username_prefix")
if kwargs.get("requester") is not None:
self.requester = kwargs["requester"]
self.is_mock = True
else:
import requests

self.requester = requests
self.is_mock = False
else:
import requests
if isinstance(manifest_category, dict):
self.manifest_data = DynaBox(manifest_category)
else:
self.manifest_data = settings.manifest_category.get(manifest_category)
if kwargs.get("requester") is not None:
self.requester = kwargs["requester"]
self.is_mock = True
else:
import requests

self.requester = requests
self.is_mock = False
self.username_prefix = settings.username_prefix or self.manifest_data.username_prefix
self.allocation_name = allocation_name or f"{self.username_prefix}-" + "".join(
random.sample(string.ascii_letters, 8)
)
self.manifest_name = Path(f"{self.allocation_name}_manifest.zip")
self.offline_token = kwargs.get("offline_token", self.manifest_data.offline_token)
self.subscription_data = self.manifest_data.subscription_data
self.token_request_data = {
"grant_type": "refresh_token",
"client_id": "rhsm-api",
"refresh_token": self.offline_token,
}
self.simple_content_access = kwargs.get(
"simple_content_access", self.manifest_data.simple_content_access
)
self.token_request_url = self.manifest_data.get("url").get("token_request")
self.allocations_url = self.manifest_data.get("url").get("allocations")
self._access_token = None
self._allocations = None
self._subscription_pools = None
self._active_pools = []
self.sat_version = process_sat_version(
kwargs.get("sat_version", self.manifest_data.sat_version),
self.valid_sat_versions,
)
self.requester = requests
self.is_mock = False
self.username_prefix = (
self.manifest_data.get("username_prefix") or settings.username_prefix
)
self.allocation_name = allocation_name or f"{self.username_prefix}-" + "".join(
random.sample(string.ascii_letters, 8)
)
self.manifest_name = Path(f"{self.allocation_name}_manifest.zip")
self.offline_token = self.manifest_data.get(
"offline_token", settings.get("offline_token")
)
self.subscription_data = self.manifest_data.subscription_data
self.token_request_data = {
"grant_type": "refresh_token",
"client_id": "rhsm-api",
"refresh_token": self.offline_token,
}
self.simple_content_access = kwargs.get(
"simple_content_access", self.manifest_data.simple_content_access
)
self.token_request_url = self.manifest_data.get("url").get("token_request")
self.allocations_url = self.manifest_data.get("url").get("allocations")
self._access_token = None
self._allocations = None
self._subscription_pools = None
self._active_pools = []
self.sat_version = process_sat_version(
kwargs.get("sat_version", self.manifest_data.sat_version),
self.valid_sat_versions,
)

@property
def access_token(self):
Expand Down Expand Up @@ -113,7 +143,7 @@ def subscription_allocations(self):

@property
def subscription_pools(self):
"""Reprentation of subscription pools in an account."""
"""Representation of subscription pools in an account."""
return fetch_paginated_data(self, "pools")

def create_subscription_allocation(self):
Expand Down Expand Up @@ -151,7 +181,7 @@ def create_subscription_allocation(self):
update_inventory(self.subscription_allocations)
return self.allocation_uuid

def delete_subscription_allocation(self):
def delete_subscription_allocation(self, uuid=None):
"""Deletes the specified subscription allocation and returns the RHSM API's response."""
self._access_token = None
data = {
Expand All @@ -163,9 +193,10 @@ def delete_subscription_allocation(self):
self.allocation_uuid = self.allocation_uuid.uuid
response = simple_retry(
self.requester.delete,
cmd_args=[f"{self.allocations_url}/{self.allocation_uuid}"],
cmd_args=[f"{self.allocations_url}/{uuid if uuid else self.allocation_uuid}"],
cmd_kwargs=data,
)
update_inventory(self.subscription_allocations)
return response

def add_entitlements_to_allocation(self, pool_id, entitlement_quantity):
Expand Down Expand Up @@ -367,6 +398,7 @@ def trigger_manifest_export(self):
local_file.write_bytes(manifest.content)
manifest.path = local_file
manifest.name = self.manifest_name
update_inventory(self.subscription_allocations)
return manifest

def get_manifest(self):
Expand All @@ -392,6 +424,6 @@ def __enter__(self):
raise

def __exit__(self, *tb_args):
"""Deletes subscription allocation on teardown."""
"""Deletes subscription allocation on teardown unless using CLI."""
self.delete_subscription_allocation()
update_inventory(self.subscription_allocations)
Loading

0 comments on commit 8ff1e29

Please sign in to comment.