Skip to content

Conversation

@jepetty
Copy link
Contributor

@jepetty jepetty commented Feb 5, 2026

Related command

az containerapp job start --resource-group jepetty-week-1 --name job-test-1 --command 'echo' --args 'Hi, Jessica!'

Description

Related to microsoft/azure-container-apps#1360
This is supported through the API and yaml deployments, but we were missing the logic to pass --args or --cmd when specified if the user also doesn't specify --image. This resolves that

Testing Guide

  1. Create a container app environment
    az containerapp env create --name job-test-cc --resource-group jepetty-week1 --location canadacentral
  2. Create a container app job with hello-world image
    az containerapp job create --name job-test-1 --resource-group jepetty-week1 --environment job-test-cc --trigger-type "Manual" --replica-timeout 1800 --image "mcr.microsoft.com/k8se/quickstart-jobs:latest" --cpu '0.25' --memory '0.5Gi' --replica-retry-limit 1
  3. Start the job to print "Hi, Jessica!"
    az containerapp job start --resource-group jepetty-week-1 --name job-test-1 --command 'echo' --args 'Hi, Jessica!'

The output of the job execution should show "Hi, Jessica" printed to console, but today it doesn't.

This checklist is used to make sure that common guidelines for a pull request are followed.

@jepetty jepetty requested a review from zhoxing-ms as a code owner February 5, 2026 01:37
Copilot AI review requested due to automatic review settings February 5, 2026 01:37
@azure-client-tools-bot-prd
Copy link

azure-client-tools-bot-prd bot commented Feb 5, 2026

️✔️AzureCLI-FullTest
️✔️acr
️✔️latest
️✔️3.12
️✔️3.13
️✔️acs
️✔️latest
️✔️3.12
️✔️3.13
️✔️advisor
️✔️latest
️✔️3.12
️✔️3.13
️✔️ams
️✔️latest
️✔️3.12
️✔️3.13
️✔️apim
️✔️latest
️✔️3.12
️✔️3.13
️✔️appconfig
️✔️latest
️✔️3.12
️✔️3.13
️✔️appservice
️✔️latest
️✔️3.12
️✔️3.13
️✔️aro
️✔️latest
️✔️3.12
️✔️3.13
️✔️backup
️✔️latest
️✔️3.12
️✔️3.13
️✔️batch
️✔️latest
️✔️3.12
️✔️3.13
️✔️batchai
️✔️latest
️✔️3.12
️✔️3.13
️✔️billing
️✔️latest
️✔️3.12
️✔️3.13
️✔️botservice
️✔️latest
️✔️3.12
️✔️3.13
️✔️cdn
️✔️latest
️✔️3.12
️✔️3.13
️✔️cloud
️✔️latest
️✔️3.12
️✔️3.13
️✔️cognitiveservices
️✔️latest
️✔️3.12
️✔️3.13
️✔️compute_recommender
️✔️latest
️✔️3.12
️✔️3.13
️✔️computefleet
️✔️latest
️✔️3.12
️✔️3.13
️✔️config
️✔️latest
️✔️3.12
️✔️3.13
️✔️configure
️✔️latest
️✔️3.12
️✔️3.13
️✔️consumption
️✔️latest
️✔️3.12
️✔️3.13
️✔️container
️✔️latest
️✔️3.12
️✔️3.13
️✔️containerapp
️✔️latest
️✔️3.12
️✔️3.13
️✔️core
️✔️latest
️✔️3.12
️✔️3.13
️✔️cosmosdb
️✔️latest
️✔️3.12
️✔️3.13
️✔️databoxedge
️✔️latest
️✔️3.12
️✔️3.13
️✔️dls
️✔️latest
️✔️3.12
️✔️3.13
️✔️dms
️✔️latest
️✔️3.12
️✔️3.13
️✔️eventgrid
️✔️latest
️✔️3.12
️✔️3.13
️✔️eventhubs
️✔️latest
️✔️3.12
️✔️3.13
️✔️feedback
️✔️latest
️✔️3.12
️✔️3.13
️✔️find
️✔️latest
️✔️3.12
️✔️3.13
️✔️hdinsight
️✔️latest
️✔️3.12
️✔️3.13
️✔️identity
️✔️latest
️✔️3.12
️✔️3.13
️✔️iot
️✔️latest
️✔️3.12
️✔️3.13
️✔️keyvault
️✔️latest
️✔️3.12
️✔️3.13
️✔️lab
️✔️latest
️✔️3.12
️✔️3.13
️✔️managedservices
️✔️latest
️✔️3.12
️✔️3.13
️✔️maps
️✔️latest
️✔️3.12
️✔️3.13
️✔️marketplaceordering
️✔️latest
️✔️3.12
️✔️3.13
️✔️monitor
️✔️latest
️✔️3.12
️✔️3.13
️✔️mysql
️✔️latest
️✔️3.12
️✔️3.13
️✔️netappfiles
️✔️latest
️✔️3.12
️✔️3.13
️✔️network
️✔️latest
️✔️3.12
️✔️3.13
️✔️policyinsights
️✔️latest
️✔️3.12
️✔️3.13
️✔️postgresql
️✔️latest
️✔️3.12
️✔️3.13
️✔️privatedns
️✔️latest
️✔️3.12
️✔️3.13
️✔️profile
️✔️latest
️✔️3.12
️✔️3.13
️✔️rdbms
️✔️latest
️✔️3.12
️✔️3.13
️✔️redis
️✔️latest
️✔️3.12
️✔️3.13
️✔️relay
️✔️latest
️✔️3.12
️✔️3.13
️✔️resource
️✔️latest
️✔️3.12
️✔️3.13
️✔️role
️✔️latest
️✔️3.12
️✔️3.13
️✔️search
️✔️latest
️✔️3.12
️✔️3.13
️✔️security
️✔️latest
️✔️3.12
️✔️3.13
️✔️servicebus
️✔️latest
️✔️3.12
️✔️3.13
️✔️serviceconnector
️✔️latest
️✔️3.12
️✔️3.13
️✔️servicefabric
️✔️latest
️✔️3.12
️✔️3.13
️✔️signalr
️✔️latest
️✔️3.12
️✔️3.13
️✔️sql
️✔️latest
️✔️3.12
️✔️3.13
️✔️sqlvm
️✔️latest
️✔️3.12
️✔️3.13
️✔️storage
️✔️latest
️✔️3.12
️✔️3.13
️✔️synapse
️✔️latest
️✔️3.12
️✔️3.13
️✔️telemetry
️✔️latest
️✔️3.12
️✔️3.13
️✔️util
️✔️latest
️✔️3.12
️✔️3.13
️✔️vm
️✔️latest
️✔️3.12
️✔️3.13

@azure-client-tools-bot-prd
Copy link

azure-client-tools-bot-prd bot commented Feb 5, 2026

️✔️AzureCLI-BreakingChangeTest
️✔️Non Breaking Changes

@yonzhan
Copy link
Collaborator

yonzhan commented Feb 5, 2026

Thank you for your contribution! We will review the pull request and get back to you soon.

@github-actions
Copy link

github-actions bot commented Feb 5, 2026

The git hooks are available for azure-cli and azure-cli-extensions repos. They could help you run required checks before creating the PR.

Please sync the latest code with latest dev branch (for azure-cli) or main branch (for azure-cli-extensions).
After that please run the following commands to enable git hooks:

pip install azdev --upgrade
azdev setup -c <your azure-cli repo path> -r <your azure-cli-extensions repo path>

Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR fixes a bug where az containerapp job start command was not applying --command and --args arguments when the user didn't specify the --image argument. The fix allows users to override the command and args while using the job's existing default image.

Changes:

  • Modified start_containerappsjob function to check for any container override parameters (not just image) before building the template
  • Added logic to fetch the existing job's image when image parameter is not provided but other overrides are specified
  • Added comprehensive E2E test validating both scenarios: with and without --image parameter

Reviewed changes

Copilot reviewed 2 out of 3 changed files in this pull request and generated 3 comments.

File Description
custom.py Updated start_containerappsjob to handle command/args overrides without requiring --image, fetches existing job's image when needed
test_containerappjob_executions.py Added new E2E test test_containerappjob_start_with_command_and_args_e2e to verify the fix works correctly
test_containerappjob_start_with_command_and_args_e2e.yaml Test recording file for the new E2E test

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines 1578 to 1582
Fetch the existing job definition to get the default image
try:
containerappjob_def = ContainerAppsJobClient.show(cmd=cmd, resource_group_name=resource_group_name, name=name)
except Exception as e:
handle_raw_exception(e)
Copy link

Copilot AI Feb 5, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The error handling pattern here is inconsistent with other parts of the codebase. In start_containerappjob_execution_yaml (line 1620), a bare except Exception: with pass is used, but here (lines 1581-1582) the exception is re-raised with handle_raw_exception(e). If the job doesn't exist, handle_raw_exception will be called twice - once here and once at line 1606. Consider using a similar pattern to lines 1618-1621 where exceptions are caught and the existence check is done separately.

Suggested change
Fetch the existing job definition to get the default image
try:
containerappjob_def = ContainerAppsJobClient.show(cmd=cmd, resource_group_name=resource_group_name, name=name)
except Exception as e:
handle_raw_exception(e)
# Fetch the existing job definition to get the default image
containerappjob_def = None
try:
containerappjob_def = ContainerAppsJobClient.show(cmd=cmd, resource_group_name=resource_group_name, name=name)
except Exception: # pylint: disable=broad-except
pass

Copilot uses AI. Check for mistakes.
jobProvisioning = self.cmd("az containerapp job show --resource-group {} --name {}".format(resource_group, job)).get_output_in_json()['properties']['provisioningState'] != "Succeeded"
if(time.time() > timeout):
break

Copy link

Copilot AI Feb 5, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The test waits for job provisioning with a timeout of 60 seconds (line 298), but there's no error handling or assertion if the timeout is reached. If jobProvisioning is still True after the timeout, the test will continue and may fail with unclear errors. Consider adding an assertion after the loop to verify that provisioning succeeded, e.g., self.assertFalse(jobProvisioning, "Job failed to provision within the timeout period").

Suggested change
# Ensure the job provisioned successfully before proceeding
self.assertFalse(jobProvisioning, "Job failed to provision within the timeout period")

Copilot uses AI. Check for mistakes.
Comment on lines 1574 to 1590
# If no image is provided, fetch the existing job's image
if image is not None:
container_def["image"] = image if not is_registry_msi_system(registry_identity) else HELLO_WORLD_IMAGE
else:
Fetch the existing job definition to get the default image
try:
containerappjob_def = ContainerAppsJobClient.show(cmd=cmd, resource_group_name=resource_group_name, name=name)
except Exception as e:
handle_raw_exception(e)

if not containerappjob_def:
raise ResourceNotFoundError("The containerapp job '{}' does not exist".format(name))

existing_image = safe_get(containerappjob_def, "properties", "template", "containers", default=[{}])[0].get("image")
if not existing_image:
raise ValidationError("Could not find an existing image for the containerapp job '{}'. Please specify --image.".format(name))
container_def["image"] = existing_image if not is_registry_msi_system(registry_identity) else HELLO_WORLD_IMAGE
Copy link

Copilot AI Feb 5, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The logic for fetching the existing job's image (lines 1578-1590) will be executed every time has_container_overrides is true but image is None. This means even if the user only wants to override cpu or memory without changing the image, the code will unnecessarily fetch the job definition. Consider moving this fetch outside the conditional block or only fetching when needed for command/args overrides without an image.

Copilot uses AI. Check for mistakes.
existing_image = safe_get(containerappjob_def, "properties", "template", "containers", default=[{}])[0].get("image")
if not existing_image:
raise ValidationError("Could not find an existing image for the containerapp job '{}'. Please specify --image.".format(name))
container_def["image"] = existing_image if not is_registry_msi_system(registry_identity) else HELLO_WORLD_IMAGE
Copy link
Contributor

@Greedygre Greedygre Feb 5, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I just noticed the original code looks weird:

        container_def["image"] = image if not is_registry_msi_system(registry_identity) else HELLO_WORLD_IMAGE

It set image to HELLO_WORLD_IMAGE when registry_identity == system, It will never use the input --image value.
NOT sure what is the original design, or the command might never work when command without --yaml.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

These part of code are migrated from azure-cli-extensions

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Auto-Assign Auto assign by bot ContainerApp

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants