Skip to content
This repository has been archived by the owner on Aug 21, 2024. It is now read-only.

Get more data from GCP about images #63

Merged
merged 1 commit into from
Oct 24, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
47 changes: 40 additions & 7 deletions src/rhelocator/update_images/gcp.py
Original file line number Diff line number Diff line change
@@ -1,25 +1,58 @@
"""Update images from public cloud APIs."""
from __future__ import annotations

from typing import Any

from google.cloud import compute_v1

from rhelocator import config


def get_images() -> list[str]:
def get_images() -> list[dict[str, str]]:
"""Get a list of RHEL images from Google Cloud.

Returns:
List of Google Compute image names.
"""
images_client = compute_v1.ImagesClient()

# NOTE(mhayden): Google's examples suggest using a filter here for "deprecated.state
# != DEPRECATED" but it returns no images when I tried it.
# https://github.com/googleapis/python-compute/blob/main/samples/recipes/images/pagination.py
images_list_request = compute_v1.ListImagesRequest(project=config.GCP_PROJECTNAME)

return [
x.name
for x in images_client.list(request=images_list_request)
if x.deprecated.state != "DEPRECATED"
]
# Normalize the data first.

return normalize_google_images(
[
x
for x in images_client.list(request=images_list_request)
if x.deprecated.state != "DEPRECATED"
]
)


def normalize_google_images(image_list: list[Any]) -> list[dict[str, str]]:
"""Normalize the data returned from Google's image listing.

The GCP SDK returns an unusual object with repeated attributes and some attributes
lead to other interesting objects. The goal here is to normalize this data so that
it's dict-like, similar to the Azure and AWS functions.

Args:
image_list: A Google image listing from the ImagesClient class.

Returns:
List of dictionaries containing normalized image data.
"""
normalized_images = []

for img in image_list:
image_data = {
"architecture": img.architecture.lower(),
"creation_timestamp": img.creation_timestamp,
"description": img.description,
"name": img.name,
}
normalized_images.append(image_data)

return normalized_images
37 changes: 28 additions & 9 deletions tests/conftest.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
"""Configuration for tests."""
from __future__ import annotations

from unittest.mock import MagicMock

import pytest


Expand All @@ -18,13 +20,6 @@
"9.0.2022090601",
]

MOCKED_GCP_IMAGE_LIST = [
"rhel-7-v20220920",
"rhel-8-v20220920",
"rhel-9-arm64-v20220920",
"rhel-9-v20220920",
]


def pytest_configure(config: any) -> None:
config.addinivalue_line("markers", "e2e: mark as end-to-end test.")
Expand Down Expand Up @@ -66,5 +61,29 @@ def mock_azure_image_versions_latest(mocker):
def mock_gcp_images(mocker):
"""Provide an offline result for calls to get_google_images."""
mock = mocker.patch("rhelocator.update_images.gcp.get_images")
mock.return_value = MOCKED_GCP_IMAGE_LIST
return mock

mocked_image = {
"architecture": "x86_64",
"creation_timestamp": "2022-09-20T16:32:45.572-07:00",
"description": "Red Hat, Red Hat Enterprise Linux, 7, x86_64",
"name": "rhel-7-v20220920",
}
mock.return_value = [mocked_image]

return mock


@pytest.fixture
def mock_normalize_google_images(mocker):
"""Provide an offline result for calls to normalize_google_images."""
mock = mocker.patch("rhelocator.update_images.gcp.normalize_google_images")

# Fake a Google image listing.
mocked_image = MagicMock()
mocked_image.architecture = "X86_64"
mocked_image.creation_timestamp = "20221018"
mocked_image.description = "RHEL"
mocked_image.name = "rhel-9-20221018"
mock.return_value = [mocked_image]

return mock
16 changes: 11 additions & 5 deletions tests/test_cli.py
Original file line number Diff line number Diff line change
Expand Up @@ -125,8 +125,11 @@ def test_gcp_images_live(runner):

assert isinstance(parsed, list)

for image in parsed:
assert image.startswith("rhel")
for img in parsed:
assert img["architecture"] in ["arm64", "x86_64"]
assert "creation_timestamp" in img
assert "Red Hat" in img["description"]
assert "rhel" in img["name"]

assert result.exit_code == 0

Expand All @@ -138,7 +141,10 @@ def test_gcp_images_offline(mock_gcp_images, runner):

assert isinstance(parsed, list)

for image in parsed:
assert image.startswith("rhel")
for img in parsed:
assert img["architecture"] in ["arm64", "x86_64"]
assert "creation_timestamp" in img
assert "Red Hat" in img["description"]
assert "rhel" in img["name"]

assert result.exit_code == 0
assert result.exit_code == 0
45 changes: 36 additions & 9 deletions tests/update_images/test_gcp.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,6 @@
from unittest.mock import Mock
from unittest.mock import patch

import pytest

from rhelocator import config
from rhelocator.update_images import gcp


Expand All @@ -18,15 +15,45 @@ def test_get_images(mock_gcp: MagicMock) -> None:
mock_image = Mock()
mock_image.name = "valid_image"

# Fake a deprecated image.
mock_deprecated_image = Mock()
mock_deprecated_image.name = "deprecated_image"
mock_deprecated_image.deprecated.state = "DEPRECATED"
# Fake a deprecated Google image listing.
mocked_deprecated_image = MagicMock()
mocked_deprecated_image.architecture = "X86_64"
mocked_deprecated_image.creation_timestamp = "19750101"
mocked_deprecated_image.description = "RHEL"
mocked_deprecated_image.name = "rhel-0-19750101"
mocked_deprecated_image.deprecated.state = "DEPRECATED"

# Fake a valid Google image listing.
mocked_valid_image = MagicMock()
mocked_valid_image.architecture = "X86_64"
mocked_valid_image.creation_timestamp = "20221018"
mocked_valid_image.description = "RHEL"
mocked_valid_image.name = "rhel-9-20221018"

# Connect the mock to the ImagesClient return value.
mock_response = MagicMock()
mock_response.list.return_value = [mock_image, mock_deprecated_image]
mock_response.list.return_value = [mocked_valid_image, mocked_deprecated_image]
mock_gcp.return_value = mock_response

images = gcp.get_images()
assert images == ["valid_image"]
assert len(images) == 1


def test_normalize_google_images() -> None:
"""Test normalizing Google image data."""
# Fake a Google image listing.
mocked_image = MagicMock()
mocked_image.architecture = "X86_64"
mocked_image.creation_timestamp = "20221018"
mocked_image.description = "RHEL"
mocked_image.name = "rhel-9-20221018"

images = gcp.normalize_google_images([mocked_image])

assert isinstance(images, list)
assert images[0] == {
"architecture": "x86_64",
"creation_timestamp": "20221018",
"description": "RHEL",
"name": "rhel-9-20221018",
}