Skip to content

Commit

Permalink
Support Azure in integration test pipeline
Browse files Browse the repository at this point in the history
Provide cleanup code for community gallery images identified from
a component descriptor and provide a dedicated publishing
configuration for Gardener integration tests that includes Azure.
  • Loading branch information
MrBatschner committed Jan 17, 2024
1 parent ec89031 commit c65bfdd
Show file tree
Hide file tree
Showing 5 changed files with 163 additions and 4 deletions.
49 changes: 48 additions & 1 deletion cleanup.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
import glci.aws
import glci.gcp
import glci.openstack_image
import glci.az

import glci.model as gm
import glci.util
Expand Down Expand Up @@ -40,7 +41,7 @@ def cleanup_image(
elif release.platform == 'gcp':
cleanup_function = None # cleanup_gcp_images
elif release.platform == 'azure':
cleanup_function = None
cleanup_function = cleanup_azure_community_gallery_images
elif release.platform == 'openstack':
cleanup_function = cleanup_openstack_images_by_id
elif release.platform == 'oci':
Expand Down Expand Up @@ -215,6 +216,52 @@ def cleanup_openstack_images(
)


def cleanup_azure_community_gallery_images(
release: gm.OnlineReleaseManifest,
publishing_cfg: gm.PublishingCfg,
dry_run: bool = False
):
cfg_factory = ci.util.ctx().cfg_factory()
azure_publishing_cfg: gm.PublishingTargetAzure = publishing_cfg.target(platform=release.platform)

azure_principal = cfg_factory.azure_service_principal(
cfg_name=azure_publishing_cfg.service_principal_cfg_name,
)

azure_principal_serialized = gm.AzureServicePrincipalCfg(
tenant_id=azure_principal.tenant_id(),
client_id=azure_principal.client_id(),
client_secret=azure_principal.client_secret(),
subscription_id=azure_principal.subscription_id(),
)

shared_gallery_cfg = cfg_factory.azure_shared_gallery(
cfg_name=azure_publishing_cfg.gallery_cfg_name,
)
shared_gallery_cfg_serialized = gm.AzureSharedGalleryCfg(
resource_group_name=shared_gallery_cfg.resource_group_name(),
gallery_name=shared_gallery_cfg.gallery_name(),
location=shared_gallery_cfg.location(),
published_name=shared_gallery_cfg.published_name(),
description=shared_gallery_cfg.description(),
eula=shared_gallery_cfg.eula(),
release_note_uri=shared_gallery_cfg.release_note_uri(),
identifier_publisher=shared_gallery_cfg.identifier_publisher(),
identifier_offer=shared_gallery_cfg.identifier_offer(),
identifier_sku=shared_gallery_cfg.identifier_sku(),
)

published_gallery_images = release.published_image_metadata.published_gallery_images

for gallery_image in published_gallery_images:
glci.az.delete_from_azure_community_gallery(
community_gallery_image_id=gallery_image.community_gallery_image_id,
service_principal_cfg=azure_principal_serialized,
shared_gallery_cfg=shared_gallery_cfg_serialized,
dry_run=dry_run
)


def clean_release_manifest_sets(
max_age_days: int=14,
cicd_cfg=None,
Expand Down
104 changes: 103 additions & 1 deletion glci/az.py
Original file line number Diff line number Diff line change
Expand Up @@ -757,6 +757,7 @@ def publish_azure_image(
storage_account_cfg: glci.model.AzureStorageAccountCfg,
shared_gallery_cfg: glci.model.AzureSharedGalleryCfg,
marketplace_cfg: glci.model.AzureMarketplaceCfg,
hyper_v_generations: list[glci.model.AzureHyperVGeneration],
publish_to_community_gallery: bool = True,
publish_to_marketplace: bool = False,
) -> glci.model.OnlineReleaseManifest:
Expand Down Expand Up @@ -808,7 +809,7 @@ def publish_azure_image(
published_gallery_images=[],
)

for hyper_v_generation in glci.model.AzureHyperVGeneration:
for hyper_v_generation in hyper_v_generations:
if publish_to_marketplace:
logger.info(f'Publishing Azure Marketplace image for {hyper_v_generation}...')
marketplace_published_image = publish_to_azure_marketplace(
Expand Down Expand Up @@ -836,3 +837,104 @@ def publish_azure_image(
published_image.published_gallery_images.append(gallery_published_image)

return dataclasses.replace(release, published_image_metadata=published_image)


def delete_from_azure_community_gallery(
community_gallery_image_id: str,
service_principal_cfg: glci.model.AzureServicePrincipalCfg,
shared_gallery_cfg: glci.model.AzureSharedGalleryCfg,
dry_run: bool
):
credential = ClientSecretCredential(
tenant_id=service_principal_cfg.tenant_id,
client_id=service_principal_cfg.client_id,
client_secret=service_principal_cfg.client_secret
)
cclient = ComputeManagementClient(credential, service_principal_cfg.subscription_id)

# unfortunately, it is not possible to obtain image information from its
# community gallery image id through the API
# so we have to dissect the string and apply some implicit knowledge about its structure
gallery_image_id_parts = community_gallery_image_id.split('/')
if len(gallery_image_id_parts) != 7:
raise RuntimeError(f"community gallery image id {community_gallery_image_id} does not follow expected semantics")

image_community_gallery_name = gallery_image_id_parts[2]
image_definition = gallery_image_id_parts[4]
image_version = gallery_image_id_parts[6]

# check if the gallery names from the released artefact and the publishing cfg match
configured_gallery = cclient.galleries.get(
resource_group_name=shared_gallery_cfg.resource_group_name,
gallery_name=shared_gallery_cfg.gallery_name
)

image_gallery_is_configured_gallery = False
for public_name in configured_gallery.sharing_profile.community_gallery_info.public_names:
if public_name == image_community_gallery_name:
image_gallery_is_configured_gallery = True

if not image_gallery_is_configured_gallery:
raise RuntimeError(f"The community gallery of image {community_gallery_image_id} is not from the configured community gallery.")

gallery_image_version = cclient.gallery_image_versions.get(
resource_group_name=shared_gallery_cfg.resource_group_name,
gallery_name=shared_gallery_cfg.gallery_name,
gallery_image_name=image_definition,
gallery_image_version_name=image_version
)

# once again, resource group and image name has to be extracted from this string
image_vhd = gallery_image_version.storage_profile.source.id
image_vhd_parts = image_vhd.split('/')
if len(image_vhd_parts) != 9:
raise RuntimeError(f"image resource string {image_vhd} does not follow expected semantics")

image_vhd_resource_group = image_vhd_parts[4]
image_vhd_name = image_vhd_parts[8]

if dry_run:
logger.warning(f"DRY RUN: would delete gallery image version {gallery_image_version.name}")
logger.warning(f"DRY RUN: would delete image VHD {image_vhd_name} in resource group {image_vhd_resource_group}")
else:
logger.info(f"Deleting {image_version=} for {image_definition=} in gallery {shared_gallery_cfg.gallery_name}...")
result = cclient.gallery_image_versions.begin_delete(
resource_group_name=shared_gallery_cfg.resource_group_name,
gallery_name=shared_gallery_cfg.gallery_name,
gallery_image_name=image_definition,
gallery_image_version_name=image_version
)
logger.info('...waiting for asynchronous operation to complete')
result = result.result()

logger.info(f"Deleting image VHD {image_vhd_name} in resource group {image_vhd_resource_group}...")
result = cclient.images.begin_delete(
resource_group_name=image_vhd_resource_group,
image_name=image_vhd_name
)
logger.info('...waiting for asynchronous operation to complete')
result = result.result()

# check how many image versions are present in this image definition
# if none, that delete the image definition
gallery_image_versions = cclient.gallery_image_versions.list_by_gallery_image(
resource_group_name=shared_gallery_cfg.resource_group_name,
gallery_name=shared_gallery_cfg.gallery_name,
gallery_image_name=image_definition
)

image_version_count = sum(1 for _ in gallery_image_versions)
if image_version_count == 0:
if dry_run:
logger.warning(f"DRY RUN: would delete {image_definition=} in gallery {shared_gallery_cfg.gallery_name}")
else:
logger.info(f"Deleting {image_definition=} in gallery {shared_gallery_cfg.gallery_name}...")
result = cclient.gallery_images.begin_delete(
resource_group_name=shared_gallery_cfg.resource_group_name,
gallery_name=shared_gallery_cfg.gallery_name,
gallery_image_name=image_definition
)
logger.info('...waiting for asynchronous operation to complete')
result = result.result()
else:
logger.warning(f"{image_definition=} still contains {image_version_count} image versions - keeping definition")
3 changes: 2 additions & 1 deletion glci/model.py
Original file line number Diff line number Diff line change
Expand Up @@ -758,7 +758,8 @@ class PublishingTargetAzure:
gallery_cfg_name: str
storage_account_cfg_name: str
service_principal_cfg_name: str
marketplace_cfg: AzureMarketplaceCfg
marketplace_cfg: typing.Optional[AzureMarketplaceCfg]
hyper_v_generations: typing.List[AzureHyperVGeneration]
publish_to_marketplace: bool
publish_to_community_galleries: bool
platform: Platform = 'azure' # should not overwrite
Expand Down
3 changes: 2 additions & 1 deletion publish.py
Original file line number Diff line number Diff line change
Expand Up @@ -161,8 +161,9 @@ def _publish_azure_image(
storage_account_cfg=storage_account_cfg_serialized,
shared_gallery_cfg=shared_gallery_cfg_serialized,
marketplace_cfg=azure_publishing_cfg.marketplace_cfg,
hyper_v_generations=azure_publishing_cfg.hyper_v_generations,
publish_to_community_gallery=azure_publishing_cfg.publish_to_community_galleries,
publish_to_marketplace=azure_publishing_cfg.publish_to_marketplace,
publish_to_marketplace=azure_publishing_cfg.publish_to_marketplace
)


Expand Down
8 changes: 8 additions & 0 deletions publishing-cfg.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@
gallery_cfg_name: 'gardenlinux-community-gallery'
storage_account_cfg_name: 'gardenlinux-community-gallery'
service_principal_cfg_name: 'gardenlinux'
hyper_v_generations: ['V1', 'V2']
marketplace_cfg:
offer_id: 'gardenlinux'
publisher_id: 'sap'
Expand Down Expand Up @@ -102,3 +103,10 @@
hw_disk_bus: scsi
vmware_adaptertype: paraVirtual
vmware_disktype: streamOptimized
- platform: 'azure'
service_principal_cfg_name: 'gardenlinux-integration-test'
storage_account_cfg_name: 'gardenlinux-integration-test'
gallery_cfg_name: 'gardenlinux-integration-test'
hyper_v_generations: ['V1']
publish_to_marketplace: false
publish_to_community_galleries: true

0 comments on commit c65bfdd

Please sign in to comment.