Skip to content

Commit 1cf00cb

Browse files
authored
Merge pull request #146 from release-engineering/draft_to_preview
Azure: ensure draft is published when SAS found
2 parents 531bed6 + adc7721 commit 1cf00cb

File tree

2 files changed

+103
-25
lines changed

2 files changed

+103
-25
lines changed

cloudpub/ms_azure/service.py

Lines changed: 22 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
import json
33
import logging
44
import os
5+
from enum import IntEnum
56
from typing import Any, Dict, Iterator, List, Optional, Tuple, Union, cast
67

78
from deepdiff import DeepDiff
@@ -71,6 +72,15 @@
7172
]
7273

7374

75+
class SasFoundStatus(IntEnum):
76+
"""Represent the submission target level of SAS found in a given product."""
77+
78+
missing = 0
79+
draft = 1
80+
preview = 2
81+
live = 3
82+
83+
7484
class AzureService(BaseService[AzurePublishingMetadata]):
7585
"""Service provider for Microsoft Azure using the Product Ingestion API."""
7686

@@ -570,7 +580,7 @@ def compute_targets(self, product_id: str) -> List[str]:
570580
"""List all the possible publishing targets order to seek data from Azure.
571581
572582
It also returns the ordered list of targets with the following precedence:
573-
``preview`` -> ``live`` -> ``draft``
583+
``live`` -> ``preview`` -> ``draft``
574584
575585
Args:
576586
product_id (str)
@@ -579,7 +589,7 @@ def compute_targets(self, product_id: str) -> List[str]:
579589
Returns:
580590
List[Str]: The ordered list with targets to lookup.
581591
"""
582-
all_targets = ["preview", "live", "draft"]
592+
all_targets = ["live", "preview", "draft"]
583593
computed_targets = []
584594

585595
# We cannot simply return all targets above because the existing product might
@@ -622,7 +632,7 @@ def _publish_preview(
622632
self, product: Product, product_name: str, resources: Optional[List[AzureResource]] = None
623633
) -> None:
624634
"""
625-
Submit the product to 'preview' if it's not already in this state.
635+
Submit the product to 'preview' after going through Azure Marketplace Validatoin.
626636
627637
This is required to execute the validation pipeline on Azure side.
628638
@@ -634,19 +644,6 @@ def _publish_preview(
634644
resources:
635645
Additional resources for modular push.
636646
"""
637-
# We just want to set the ProductSubmission to 'preview' if it's not in this status.
638-
#
639-
# The `preview` stage runs the Azure pipeline which takes up to 4 days.
640-
# Meanwhile the `submit_for_status` will be blocked querying the `job_status`until
641-
# all the Azure verification pipeline finishes.
642-
submission: ProductSubmission = cast(
643-
List[ProductSubmission],
644-
self.filter_product_resources(product=product, resource="submission"),
645-
)[0]
646-
if self._is_submission_in_preview(submission):
647-
log.info("The product \"%s\" is already set to preview", product_name)
648-
return
649-
650647
res = self.submit_to_status(product_id=product.id, status='preview', resources=resources)
651648

652649
if res.job_result != 'succeeded' or not self.get_submission_state(
@@ -817,7 +814,7 @@ def publish(self, metadata: AzurePublishingMetadata) -> None:
817814
product_name = metadata.destination.split("/")[0]
818815
plan_name = metadata.destination.split("/")[-1]
819816
product_id = self.get_productid(product_name)
820-
disk_version = None
817+
sas_in_target = SasFoundStatus.missing
821818
log.info(
822819
"Preparing to associate the image \"%s\" with the plan \"%s\" from product \"%s\"",
823820
metadata.image_path,
@@ -838,7 +835,6 @@ def publish(self, metadata: AzurePublishingMetadata) -> None:
838835
target = "draft" # It's expected to exist for whenever product.
839836
res = self._overwrite_disk_version(metadata, product_name, plan_name, source, target)
840837
tech_config = res["tech_config"]
841-
disk_version = tech_config.disk_versions[0] # only 1 as it was overwritten
842838
else:
843839
# Otherwise we need to check whether SAS isn't already present
844840
# in any of the targets "preview", "live" or "draft" and if not attach and publish it.
@@ -849,6 +845,7 @@ def publish(self, metadata: AzurePublishingMetadata) -> None:
849845
tech_config = res["tech_config"]
850846
# We don't want to seek for SAS anymore as it was already found
851847
if res["sas_found"]:
848+
sas_in_target = SasFoundStatus[target]
852849
break
853850
else:
854851
# At this point there's no SAS URI in any target so we can safely add it
@@ -862,10 +859,10 @@ def publish(self, metadata: AzurePublishingMetadata) -> None:
862859
metadata.image_path,
863860
)
864861
dv = seek_disk_version(tech_config, metadata.disk_version)
865-
disk_version = self._create_or_update_disk_version(res, source, dv)
862+
self._create_or_update_disk_version(res, source, dv)
866863

867864
# 4. With the updated disk_version we should adjust the SKUs and submit the changes
868-
if disk_version:
865+
if sas_in_target == SasFoundStatus.missing:
869866
log.info("Updating SKUs for \"%s\" on \"%s\".", metadata.destination, target)
870867
tech_config.skus = update_skus(
871868
disk_versions=tech_config.disk_versions,
@@ -892,7 +889,7 @@ def publish(self, metadata: AzurePublishingMetadata) -> None:
892889

893890
# We should only publish if there are new changes OR
894891
# the existing offer was already in preview
895-
if disk_version or self._is_submission_in_preview(submission):
892+
if sas_in_target <= SasFoundStatus.draft or self._is_submission_in_preview(submission):
896893
log.info(
897894
"Publishing the new changes for \"%s\" on plan \"%s\"", product_name, plan_name
898895
)
@@ -905,8 +902,10 @@ def publish(self, metadata: AzurePublishingMetadata) -> None:
905902
modular_resources = None
906903
if metadata.modular_push:
907904
modular_resources = self.get_modular_resources_to_publish(product, tech_config)
908-
self._publish_preview(product, product_name, resources=modular_resources)
909-
self._publish_live(product, product_name)
905+
if sas_in_target < SasFoundStatus.preview:
906+
self._publish_preview(product, product_name, resources=modular_resources)
907+
if sas_in_target < SasFoundStatus.live:
908+
self._publish_live(product, product_name)
910909

911910
log.info(
912911
"Finished publishing the image \"%s\" to \"%s\"",

tests/ms_azure/test_service.py

Lines changed: 81 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -161,7 +161,7 @@ def test_get_product_id_not_found(
161161
@pytest.mark.parametrize(
162162
"targets_list",
163163
[
164-
["preview", "live", "draft"],
164+
["live", "preview", "draft"],
165165
["live", "draft"],
166166
["preview", "draft"],
167167
["draft"],
@@ -1651,6 +1651,86 @@ def test_publish_live_arm64_only(
16511651
mock_submit.assert_has_calls(submit_calls)
16521652
mock_ensure_publish.assert_called_once_with(product_obj.id)
16531653

1654+
@mock.patch("cloudpub.ms_azure.AzureService._publish_live")
1655+
@mock.patch("cloudpub.ms_azure.AzureService._publish_preview")
1656+
@mock.patch("cloudpub.ms_azure.AzureService.ensure_can_publish")
1657+
@mock.patch("cloudpub.ms_azure.AzureService._create_or_update_disk_version")
1658+
@mock.patch("cloudpub.ms_azure.AzureService._overwrite_disk_version")
1659+
@mock.patch("cloudpub.ms_azure.AzureService.compute_targets")
1660+
@mock.patch("cloudpub.ms_azure.AzureService.get_productid")
1661+
@mock.patch("cloudpub.ms_azure.AzureService.configure")
1662+
def test_publish_live_when_sas_is_present_in_draft(
1663+
self,
1664+
mock_configure: mock.MagicMock,
1665+
mock_get_productid: mock.MagicMock,
1666+
mock_compute_targets: mock.MagicMock,
1667+
mock_create_diskversion: mock.MagicMock,
1668+
mock_overwrite: mock.MagicMock,
1669+
mock_ensure_can_publish: mock.MagicMock,
1670+
mock_publish_preview: mock.MagicMock,
1671+
mock_publish_live: mock.MagicMock,
1672+
token: Dict[str, Any],
1673+
auth_dict: Dict[str, Any],
1674+
configure_success_response: Dict[str, Any],
1675+
product: Dict[str, Any],
1676+
products_list: Dict[str, Any],
1677+
product_summary: Dict[str, Any],
1678+
metadata_azure_obj: mock.MagicMock,
1679+
vmimage_source: Dict[str, Any],
1680+
caplog: pytest.LogCaptureFixture,
1681+
) -> None:
1682+
"""Ensure live publish works as expected when the SAS is already present as draft."""
1683+
# Prepare testing data
1684+
metadata_azure_obj.keepdraft = False
1685+
metadata_azure_obj.destination = "example-product/plan-1"
1686+
# SAS URI will be already present during draft
1687+
metadata_azure_obj.image_path = vmimage_source["osDisk"]["uri"]
1688+
1689+
# targets
1690+
mock_get_productid.return_value = "fake-id"
1691+
targets = ["draft"]
1692+
mock_compute_targets.return_value = targets
1693+
1694+
# Constants
1695+
login_url = "https://login.microsoftonline.com/foo/oauth2/token"
1696+
base_url = "https://graph.microsoft.com/rp/product-ingestion"
1697+
product_id = str(product_summary['id']).split("/")[-1]
1698+
1699+
mock_configure.return_value = ConfigureStatus.from_json(configure_success_response)
1700+
1701+
# Constants
1702+
login_url = "https://login.microsoftonline.com/foo/oauth2/token"
1703+
base_url = "https://graph.microsoft.com/rp/product-ingestion"
1704+
product_id = str(product_summary['id']).split("/")[-1]
1705+
1706+
# Test
1707+
with caplog.at_level(logging.INFO):
1708+
with requests_mock.Mocker() as m:
1709+
m.post(login_url, json=token)
1710+
m.get(f"{base_url}/product", json=products_list)
1711+
m.get(f"{base_url}/resource-tree/product/{product_id}", json=product)
1712+
azure_svc = AzureService(auth_dict)
1713+
azure_svc.publish(metadata=metadata_azure_obj)
1714+
1715+
# Present messages
1716+
assert (
1717+
"Requesting the product ID \"ffffffff-ffff-ffff-ffff-ffffffffffff\""
1718+
" with state \"draft\"."
1719+
) in caplog.messages
1720+
assert (
1721+
"Retrieving the technical config for \"example-product/plan-1\" on \"draft\"."
1722+
in caplog.messages
1723+
)
1724+
assert (
1725+
"The destination \"example-product/plan-1\" on \"draft\" "
1726+
"already contains the SAS URI: \"https://uri.test.com\"."
1727+
) in caplog.messages
1728+
mock_publish_preview.assert_called_once()
1729+
mock_publish_live.assert_called_once()
1730+
mock_ensure_can_publish.assert_called_once()
1731+
mock_create_diskversion.assert_not_called()
1732+
mock_overwrite.assert_not_called()
1733+
16541734
@mock.patch("cloudpub.ms_azure.AzureService.compute_targets")
16551735
@mock.patch("cloudpub.ms_azure.AzureService.get_productid")
16561736
def test_publish_live_when_state_is_preview(
@@ -1761,7 +1841,6 @@ def test_publish_live_when_state_is_preview(
17611841
'Looking up for submission in state "live" for "ffffffff-ffff-ffff-ffff-ffffffffffff"'
17621842
in caplog.text
17631843
)
1764-
assert 'The product "example-product" is already set to preview' in caplog.text
17651844
assert (
17661845
'Submitting the status of "ffffffff-ffff-ffff-ffff-ffffffffffff" to "live"'
17671846
in caplog.text

0 commit comments

Comments
 (0)