Skip to content

Commit 8ef47d3

Browse files
committed
fix(ci): handle empty API responses safely in azure_marketplace_publish.py
Signed-off-by: shellyco-code <shellychahar57@gmail.com>
1 parent f64f070 commit 8ef47d3

1 file changed

Lines changed: 76 additions & 5 deletions

File tree

ci-automation/release/azure_marketplace_publish.py

Lines changed: 76 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -52,22 +52,43 @@ def get_active_plans():
5252
if resp.status_code != 200:
5353
logging.error("There is some issue with the channel-info.txt file. Please check https://flatcar.cdn.cncf.io/channel-info.txt")
5454
logging.error(f"Returned status code: {resp.status_code}")
55+
return []
5556

5657
plans = [i.split("=")[0].replace('_CURRENT','').lower() for i in resp.text.strip().split('\n')]
5758
plans = [plan for plan in plans if plan != 'lts']
5859

5960
return plans
6061

6162

63+
def _parse_json_dict(resp, context):
64+
try:
65+
data = resp.json()
66+
except Exception as e:
67+
logging.error(
68+
f"Failed to parse JSON response for {context}. Status: {resp.status_code}, Error: {e}"
69+
)
70+
return None
71+
72+
if not isinstance(data, dict):
73+
logging.error(
74+
f"Unexpected response format for {context}. Status: {resp.status_code}, Response: {resp.text}"
75+
)
76+
return None
77+
78+
return data
79+
80+
6281
def generate_partner_center_token(tenant_id, client_id, secret_value):
6382
data = f"grant_type=client_credentials&client_id={client_id}&client_secret={secret_value}&resource=https://graph.microsoft.com"
6483
resp = requests.post(
6584
url=f"https://login.microsoftonline.com/{tenant_id}/oauth2/token",
6685
headers={"Content-Type": "application/x-www-form-urlencoded"},
6786
data=data,
6887
)
69-
access_token = resp.json().get("access_token")
70-
return access_token
88+
parsed = _parse_json_dict(resp, f"partner center token (tenant '{tenant_id}')")
89+
if not parsed:
90+
return None
91+
return parsed.get("access_token")
7192

7293

7394
def generate_az_sas_url(plan, version, arch, **kwargs):
@@ -118,7 +139,21 @@ def get_product_durable_id(access_token, offer):
118139
headers={"Authorization": f"Bearer {access_token}"},
119140
)
120141

121-
return resp.json().get("value", [])[0].get("id")
142+
data = _parse_json_dict(resp, f"product durable ID (offer '{offer}')")
143+
if not data:
144+
return None
145+
146+
values = data.get("value", [])
147+
if not values:
148+
logging.error(
149+
f"Failed to fetch product durable ID for offer '{offer}'. Status: {resp.status_code}, Response: {resp.text}"
150+
)
151+
return None
152+
153+
first_val = values[0]
154+
if isinstance(first_val, dict):
155+
return first_val.get("id")
156+
return None
122157

123158

124159
def get_plan_durable_id(access_token, product_durable_id, plan):
@@ -127,7 +162,23 @@ def get_plan_durable_id(access_token, product_durable_id, plan):
127162
headers={"Authorization": f"Bearer {access_token}"},
128163
)
129164

130-
return resp.json().get("value", [])[0].get("id")
165+
data = _parse_json_dict(
166+
resp, f"plan durable ID (product '{product_durable_id}', plan '{plan}')"
167+
)
168+
if not data:
169+
return None
170+
171+
values = data.get("value", [])
172+
if not values:
173+
logging.error(
174+
f"Failed to fetch plan durable ID for product '{product_durable_id}', plan '{plan}'. Status: {resp.status_code}, Response: {resp.text}"
175+
)
176+
return None
177+
178+
first_val = values[0]
179+
if isinstance(first_val, dict):
180+
return first_val.get("id")
181+
return None
131182

132183

133184
def get_image_versions(access_token, product_durable_id, plan_durable_id, corevm=False):
@@ -140,7 +191,20 @@ def get_image_versions(access_token, product_durable_id, plan_durable_id, corevm
140191
headers={"Authorization": f"Bearer {access_token}"},
141192
)
142193

143-
return resp.json().get("vmImageVersions")
194+
data = _parse_json_dict(
195+
resp, f"vmImageVersions (product '{product_durable_id}', plan '{plan_durable_id}')"
196+
)
197+
if not data:
198+
return []
199+
200+
image_versions = data.get("vmImageVersions")
201+
if image_versions is None:
202+
logging.warning(
203+
f"No existing vmImageVersions found for product '{product_durable_id}', plan '{plan_durable_id}'."
204+
)
205+
return []
206+
207+
return image_versions
144208

145209

146210
def draft_new_image_versions(
@@ -342,7 +406,14 @@ def main():
342406
continue
343407

344408
product_durable_id = get_product_durable_id(access_token, offer)
409+
if not product_durable_id:
410+
logging.error(f"Skipping offer '{offer}' due to missing product durable ID.")
411+
continue
412+
345413
plan_durable_id = get_plan_durable_id(access_token, product_durable_id, plan)
414+
if not plan_durable_id:
415+
logging.error(f"Skipping offer '{offer}' due to missing plan durable ID.")
416+
continue
346417

347418
product_durable_id = product_durable_id.split("/")[1]
348419
plan_durable_id = plan_durable_id.split("/")[2]

0 commit comments

Comments
 (0)