Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
66 commits
Select commit Hold shift + click to select a range
b98b6c8
Add files via upload
snehapar9 Nov 3, 2023
f04f8d9
Add files via upload
snehapar9 Nov 3, 2023
8e5fe69
Delete containerapp-0.3.42-py2.py3-none-any.whl
snehapar9 Nov 3, 2023
5c450eb
Add files via upload
snehapar9 Nov 3, 2023
7979a7a
Delete containerapp-0.3.42-py2.py3-none-any 3.whl
snehapar9 Nov 3, 2023
a961579
Add files via upload
snehapar9 Nov 9, 2023
9dc9bc1
Delete containerapp-0.3.42-py2.py3-none-any.whl
snehapar9 Nov 9, 2023
54df756
Delete containerapp-0.3.43-py2.py3-none-any 3.whl
snehapar9 Nov 9, 2023
6f0aece
Add files via upload
snehapar9 Nov 9, 2023
bf0eb3f
Delete containerapp-0.3.43-py2.py3-none-any.whl
snehapar9 Nov 9, 2023
3d2ec14
Add files via upload
snehapar9 Nov 9, 2023
cb74e19
Delete containerapp-0.3.43-py2.py3-none-any.whl
snehapar9 Nov 9, 2023
65e6c65
Add files via upload
snehapar9 Nov 9, 2023
c0f54c9
Merge branch 'Azure:main' into main
snehapar9 Nov 22, 2023
6ee1d72
Merge pull request #7 from Azure/main
snehapar9 Feb 13, 2024
ad060c6
Merge branch 'main' of https://github.com/Azure/azure-cli-extensions
snehapar9 Feb 14, 2024
c73ec7b
Merge branch 'Azure:main' into main
snehapar9 Feb 15, 2024
96cac1f
Merge branch 'main' of https://github.com/Azure/azure-cli-extensions
snehapar9 Feb 16, 2024
1258969
Remove bootstrapped container for source to cloud flow
snehapar9 Feb 16, 2024
bc6c550
Delete whl
snehapar9 Feb 16, 2024
daeaadf
Add license header
snehapar9 Feb 16, 2024
34bf7cf
Address PR comments
snehapar9 Feb 19, 2024
d621909
[Container Apps] Fix issue in Cloud Build when code relies on UTF-8 logs
daniv-msft Feb 10, 2024
183ded8
Removed check for build image starting with default
snehapar9 Feb 21, 2024
f16e673
Fix integration test failures
snehapar9 Feb 21, 2024
ec78b3b
Fix integration failure
snehapar9 Feb 21, 2024
f320089
Fix integration test failures
snehapar9 Feb 21, 2024
73a4bf8
Merge branch 'main' of https://github.com/Azure/azure-cli-extensions
snehapar9 Feb 21, 2024
337ca2c
Merge branch 'main' into snehapar/fix-port-error
snehapar9 Feb 21, 2024
3923a4f
Fix linting errors
snehapar9 Feb 21, 2024
de12f90
Update Release History
snehapar9 Feb 21, 2024
6e397f7
Rerun pipeline
snehapar9 Feb 21, 2024
958ca88
Delete recording
snehapar9 Feb 22, 2024
bd199e9
Refactor test
snehapar9 Feb 22, 2024
41a516a
Merge branch 'main' of https://github.com/Azure/azure-cli-extensions
snehapar9 Feb 22, 2024
6cfe0cf
Fix integration test
snehapar9 Feb 22, 2024
2afe506
Delete recording and re-record
snehapar9 Feb 22, 2024
88f484c
Re-trigger pipeline
snehapar9 Feb 22, 2024
5196c5c
Merge branch 'main' into snehapar/fix-port-error
snehapar9 Feb 22, 2024
00b84a7
Add live attribute to test
snehapar9 Feb 22, 2024
862f91e
Address PR comments
snehapar9 Feb 22, 2024
bb98071
Merge remote-tracking branch 'dan/daniv/fixCloudBuildIssueWithUTF8Log…
snehapar9 Feb 22, 2024
a4e94ae
Fixed linting errors
snehapar9 Feb 23, 2024
a6edfb0
Merge branch 'main' of https://github.com/Azure/azure-cli-extensions
snehapar9 Feb 23, 2024
8d2915f
Merge branch 'main' into snehapar/fix-port-error
snehapar9 Feb 23, 2024
9293859
Address PR comments
snehapar9 Feb 23, 2024
39f10bc
Merge branch 'main' of https://github.com/Azure/azure-cli-extensions
snehapar9 Feb 23, 2024
b2eed4c
Merge branch 'main' into snehapar/fix-port-error
snehapar9 Feb 23, 2024
08d326c
Address PR comments
snehapar9 Feb 26, 2024
24efc8e
Address PR comments
snehapar9 Feb 26, 2024
ac13f8c
Address PR comments
snehapar9 Feb 27, 2024
2801e09
Fix linting errors
snehapar9 Feb 27, 2024
4405e2e
Refactor
snehapar9 Feb 27, 2024
05c6abf
Fix test failure
snehapar9 Feb 27, 2024
4e8825c
Refactor
snehapar9 Feb 27, 2024
786b1e6
Fix test failure
snehapar9 Feb 27, 2024
45da9bf
re-run test
snehapar9 Feb 27, 2024
7e055c1
Merge branch 'main' of https://github.com/Azure/azure-cli-extensions
snehapar9 Feb 27, 2024
8f2868f
Merge branch 'main' into snehapar/fix-port-error
snehapar9 Feb 27, 2024
f0ce6c4
Fix linting error and re-trigger pipeline
snehapar9 Feb 27, 2024
09e2866
Merge branch 'main' of https://github.com/Azure/azure-cli-extensions
snehapar9 Feb 28, 2024
ad5186a
Merge branch 'main' into snehapar/fix-port-error
snehapar9 Feb 28, 2024
dc83674
Re-record test and re-trigger CI
snehapar9 Feb 28, 2024
92c30ab
Merge branch 'main' into snehapar/fix-port-error
Greedygre Feb 28, 2024
e14a0ec
remove secret in yaml file
Greedygre Feb 29, 2024
eeba679
fix
Greedygre Feb 29, 2024
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
2 changes: 2 additions & 0 deletions src/containerapp/HISTORY.rst
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,8 @@ upcoming
* 'az containerapp env java-component spring-cloud-config': Support create/update/show/delete Spring Cloud Config
* 'az containerapp env java-component spring-cloud-eureka': Support create/update/show/delete Spring Cloud Eureka
* 'az containerapp create/update': Support bind Java component with --bind
* 'az containerapp create/update/up': Fix issue with logs when the Cloud Build project to use generates UTF-8 logs.
* 'az containerapp update/up': Fix bug for multiple containers provisioned for source to cloud build

0.3.47
++++++
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -183,7 +183,7 @@ def spin():
thread = display_spinner("Streaming Cloud Build logs")
headers = {'Authorization': 'Bearer ' + token}
logs_stream_retries = 0
maximum_logs_stream_retries = 5
maximum_logs_stream_retries = 8
while logs_stream_retries < maximum_logs_stream_retries:
logs_stream_retries += 1
response_log_streaming = requests.get(
Expand Down
2 changes: 1 addition & 1 deletion src/containerapp/azext_containerapp/_params.py
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,7 @@ def load_arguments(self, _):
help='Boolean indicating whether to parse json string log into dynamic json columns. Only work for destination log-analytics.', is_preview=True)

with self.argument_context('containerapp env storage') as c:
c.argument('storage_type', arg_type = get_enum_type(['AzureFile', 'NfsAzureFile']), help="Type of the storage. Assumed to be AzureFile if not specified.", is_preview=True)
c.argument('storage_type', arg_type=get_enum_type(['AzureFile', 'NfsAzureFile']), help="Type of the storage. Assumed to be AzureFile if not specified.", is_preview=True)
c.argument('access_mode', id_part=None, arg_type=get_enum_type(["ReadWrite", "ReadOnly"]),
help="Access mode for the AzureFile or nfs AzureFile storage.")
c.argument('azure_file_share_name', options_list=["--azure-file-share-name", "--file-share", "-f"],
Expand Down
10 changes: 8 additions & 2 deletions src/containerapp/azext_containerapp/_up_utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -410,6 +410,7 @@ def __init__(
env_vars=None,
workload_profile_name=None,
ingress=None,
force_single_container_updates=None
):

super().__init__(cmd, name, resource_group, exists)
Expand All @@ -422,6 +423,7 @@ def __init__(
self.env_vars = env_vars
self.ingress = ingress
self.workload_profile_name = workload_profile_name
self.force_single_container_updates = force_single_container_updates

self.should_create_acr = False
self.acr: "AzureContainerRegistry" = None
Expand Down Expand Up @@ -453,9 +455,13 @@ def create(self, no_registry=False):
env_vars=self.env_vars,
workload_profile_name=self.workload_profile_name,
ingress=self.ingress,
environment_type=CONNECTED_ENVIRONMENT_TYPE if self.env.is_connected_environment() else MANAGED_ENVIRONMENT_TYPE
environment_type=CONNECTED_ENVIRONMENT_TYPE if self.env.is_connected_environment() else MANAGED_ENVIRONMENT_TYPE,
force_single_container_updates=self.force_single_container_updates
)

def set_force_single_container_updates(self, force_single_container_updates):
self.force_single_container_updates = force_single_container_updates

def create_acr_if_needed(self):
if self.should_create_acr:
logger.warning(
Expand Down Expand Up @@ -521,7 +527,7 @@ def build_container_from_source_with_cloud_build_service(self, source, build_env

run_full_id = uuid.uuid4().hex
logs_file_path = os.path.join(tempfile.gettempdir(), f"{'build{}'.format(run_full_id)[:12]}.txt")
logs_file = open(logs_file_path, "w")
logs_file = open(logs_file_path, "w", encoding="utf-8")
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Is the encoding part coming from this other PR? https://github.com/Azure/azure-cli-extensions/pull/7281/files
If yes, should we remove it from here (or take the rest of the changes so that we can close the other PR)?

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

I left a comment for this PR https://github.com/Azure/azure-cli-extensions/pull/7281/files early this Week, about if there is any test cover this issue.
Do we have test case to cover this issue when code relies on UTF-8 logs?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Merged these changes into this PR.


try:
resource_group_name = self.resource_group.name
Expand Down
2 changes: 1 addition & 1 deletion src/containerapp/azext_containerapp/_validators.py
Original file line number Diff line number Diff line change
Expand Up @@ -168,4 +168,4 @@ def validate_build_env_vars(cmd, namespace):
raise ValidationError(
"Duplicate build environment variable {env} found, environment variable names must be unique.".format(
env=key_val[0]))
env_pairs[key_val[0]] = key_val[1]
env_pairs[key_val[0]] = key_val[1]
191 changes: 110 additions & 81 deletions src/containerapp/azext_containerapp/containerapp_decorator.py
Original file line number Diff line number Diff line change
Expand Up @@ -221,87 +221,7 @@ def construct_payload(self):
"Usage error: --container-name is required when adding or updating a container")

# Check if updating existing container
updating_existing_container = False
for c in self.new_containerapp["properties"]["template"]["containers"]:
if c["name"].lower() == self.get_argument_container_name().lower():
updating_existing_container = True

if self.get_argument_image() is not None:
c["image"] = self.get_argument_image()

if self.get_argument_set_env_vars() is not None:
if "env" not in c or not c["env"]:
c["env"] = []
# env vars
_add_or_update_env_vars(c["env"], parse_env_var_flags(self.get_argument_set_env_vars()))

if self.get_argument_replace_env_vars() is not None:
# Remove other existing env_vars, then add them
c["env"] = []
_add_or_update_env_vars(c["env"], parse_env_var_flags(self.get_argument_replace_env_vars()))

if self.get_argument_remove_env_vars() is not None:
if "env" not in c or not c["env"]:
c["env"] = []
# env vars
_remove_env_vars(c["env"], self.get_argument_remove_env_vars())

if self.get_argument_remove_all_env_vars():
c["env"] = []

if self.get_argument_startup_command() is not None:
if isinstance(self.get_argument_startup_command(), list) and not self.get_argument_startup_command():
c["command"] = None
else:
c["command"] = self.get_argument_startup_command()
if self.get_argument_args() is not None:
if isinstance(self.get_argument_args(), list) and not self.get_argument_args():
c["args"] = None
else:
c["args"] = self.get_argument_args()
if self.get_argument_cpu() is not None or self.get_argument_memory() is not None:
if "resources" in c and c["resources"]:
if self.get_argument_cpu() is not None:
c["resources"]["cpu"] = self.get_argument_cpu()
if self.get_argument_memory() is not None:
c["resources"]["memory"] = self.get_argument_memory()
else:
c["resources"] = {
"cpu": self.get_argument_cpu(),
"memory": self.get_argument_memory()
}
if self.get_argument_secret_volume_mount() is not None:
self.new_containerapp["properties"]["template"]["volumes"] = self.containerapp_def["properties"]["template"]["volumes"]
if "volumeMounts" not in c or not c["volumeMounts"]:
# if no volume mount exists, create a new volume and then mount
volume_def = VolumeModel
volume_mount_def = VolumeMountModel
volume_def["name"] = _generate_secret_volume_name()
volume_def["storageType"] = "Secret"

volume_mount_def["volumeName"] = volume_def["name"]
volume_mount_def["mountPath"] = self.get_argument_secret_volume_mount()

if "volumes" not in self.new_containerapp["properties"]["template"] or self.new_containerapp["properties"]["template"]["volumes"] is None:
self.new_containerapp["properties"]["template"]["volumes"] = [volume_def]
else:
self.new_containerapp["properties"]["template"]["volumes"].append(volume_def)
c["volumeMounts"] = [volume_mount_def]
else:
if len(c["volumeMounts"]) > 1:
raise ValidationError(
"Usage error: --secret-volume-mount can only be used with a container that has a single volume mount, to define multiple volumes and mounts please use --yaml")
else:
# check that the only volume is of type secret
volume_name = c["volumeMounts"][0]["volumeName"]
for v in self.new_containerapp["properties"]["template"]["volumes"]:
if v["name"].lower() == volume_name.lower():
if v["storageType"] != "Secret":
raise ValidationError(
"Usage error: --secret-volume-mount can only be used to update volume mounts with volumes of type secret. To update other types of volumes please use --yaml")
break
c["volumeMounts"][0]["mountPath"] = self.get_argument_secret_volume_mount()

updating_existing_container = self.set_up_existing_container_update()
# If not updating existing container, add as new container
if not updating_existing_container:
if self.get_argument_image() is None:
Expand Down Expand Up @@ -585,6 +505,93 @@ def set_up_update_containerapp_yaml(self, name, file_name):
if safe_get(self.new_containerapp, "properties", "environmentId") and safe_get(self.new_containerapp, "properties", "environmentId").lower() == existed_environment_id.lower():
del self.new_containerapp["properties"]['environmentId']

def set_up_existing_container_update(self):
updating_existing_container = False
for c in self.new_containerapp["properties"]["template"]["containers"]:
# Update existing container if container name matches the argument container name
if self.should_update_existing_container(c):
updating_existing_container = True

if self.get_argument_image() is not None:
c["image"] = self.get_argument_image()

if self.get_argument_set_env_vars() is not None:
if "env" not in c or not c["env"]:
c["env"] = []
# env vars
_add_or_update_env_vars(c["env"], parse_env_var_flags(self.get_argument_set_env_vars()))

if self.get_argument_replace_env_vars() is not None:
# Remove other existing env_vars, then add them
c["env"] = []
_add_or_update_env_vars(c["env"], parse_env_var_flags(self.get_argument_replace_env_vars()))

if self.get_argument_remove_env_vars() is not None:
if "env" not in c or not c["env"]:
c["env"] = []
# env vars
_remove_env_vars(c["env"], self.get_argument_remove_env_vars())

if self.get_argument_remove_all_env_vars():
c["env"] = []

if self.get_argument_startup_command() is not None:
if isinstance(self.get_argument_startup_command(), list) and not self.get_argument_startup_command():
c["command"] = None
else:
c["command"] = self.get_argument_startup_command()
if self.get_argument_args() is not None:
if isinstance(self.get_argument_args(), list) and not self.get_argument_args():
c["args"] = None
else:
c["args"] = self.get_argument_args()
if self.get_argument_cpu() is not None or self.get_argument_memory() is not None:
if "resources" in c and c["resources"]:
if self.get_argument_cpu() is not None:
c["resources"]["cpu"] = self.get_argument_cpu()
if self.get_argument_memory() is not None:
c["resources"]["memory"] = self.get_argument_memory()
else:
c["resources"] = {
"cpu": self.get_argument_cpu(),
"memory": self.get_argument_memory()
}
if self.get_argument_secret_volume_mount() is not None:
self.new_containerapp["properties"]["template"]["volumes"] = self.containerapp_def["properties"]["template"]["volumes"]
if "volumeMounts" not in c or not c["volumeMounts"]:
# if no volume mount exists, create a new volume and then mount
volume_def = VolumeModel
volume_mount_def = VolumeMountModel
volume_def["name"] = _generate_secret_volume_name()
volume_def["storageType"] = "Secret"

volume_mount_def["volumeName"] = volume_def["name"]
volume_mount_def["mountPath"] = self.get_argument_secret_volume_mount()

if "volumes" not in self.new_containerapp["properties"]["template"] or self.new_containerapp["properties"]["template"]["volumes"] is None:
self.new_containerapp["properties"]["template"]["volumes"] = [volume_def]
else:
self.new_containerapp["properties"]["template"]["volumes"].append(volume_def)
c["volumeMounts"] = [volume_mount_def]
else:
if len(c["volumeMounts"]) > 1:
raise ValidationError(
"Usage error: --secret-volume-mount can only be used with a container that has a single volume mount, to define multiple volumes and mounts please use --yaml")
else:
# check that the only volume is of type secret
volume_name = c["volumeMounts"][0]["volumeName"]
for v in self.new_containerapp["properties"]["template"]["volumes"]:
if v["name"].lower() == volume_name.lower():
if v["storageType"] != "Secret":
raise ValidationError(
"Usage error: --secret-volume-mount can only be used to update volume mounts with volumes of type secret. To update other types of volumes please use --yaml")
break
c["volumeMounts"][0]["mountPath"] = self.get_argument_secret_volume_mount()
return updating_existing_container

def should_update_existing_container(self, c):
return c["name"].lower() == self.get_argument_container_name().lower()


# decorator for preview create
class ContainerAppPreviewCreateDecorator(ContainerAppCreateDecorator):
Expand Down Expand Up @@ -973,6 +980,7 @@ def get_argument_service_principal_tenant_id(self):
def get_argument_max_inactive_revisions(self):
return self.get_param("max_inactive_revisions")


# decorator for preview update
class ContainerAppPreviewUpdateDecorator(ContainerAppUpdateDecorator):
def get_argument_service_bindings(self):
Expand Down Expand Up @@ -1002,6 +1010,10 @@ def get_argument_artifact(self):
def get_argument_build_env_vars(self):
return self.get_param("build_env_vars")

# This argument is set when cloud build is used to build the image and this argument ensures that only one container with the new cloud build image is
def get_argument_force_single_container_updates(self):
return self.get_param("force_single_container_updates")

def validate_arguments(self):
super().validate_arguments()
if self.get_argument_service_bindings() and len(self.get_argument_service_bindings()) > 1 and self.get_argument_customized_keys():
Expand Down Expand Up @@ -1196,6 +1208,23 @@ def set_up_unbind_service_bindings(self):
def get_argument_max_inactive_revisions(self):
return self.get_param("max_inactive_revisions")

def _need_update_container(self):
return self.get_argument_image() or self.get_argument_force_single_container_updates() or self.get_argument_container_name() or self.get_argument_set_env_vars() is not None or self.get_argument_remove_env_vars() is not None or self.get_argument_replace_env_vars() is not None or self.get_argument_remove_all_env_vars() or self.get_argument_cpu() or self.get_argument_memory() or self.get_argument_startup_command() is not None or self.get_argument_args() is not None or self.get_argument_secret_volume_mount() is not None

def set_up_existing_container_update(self):
if self.get_argument_force_single_container_updates():
# Remove n-1 containers where n is the number of containers in the containerapp and replace remaining container with the new container
# Fails if all containers are removed
while (len(self.new_containerapp["properties"]["template"]["containers"]) > 1):
self.new_containerapp["properties"]["template"]["containers"].pop()
return super().set_up_existing_container_update()

def should_update_existing_container(self, c):
# Update existing container if container name matches the argument container name or if force_single_container_updates is set
# force_single_container_updates argument is set when cloud build is used to build the image and this argument ensures that only one container with the new cloud build image is present in the containerapp
return c["name"].lower() == self.get_argument_container_name().lower() or self.get_argument_force_single_container_updates()


# decorator for preview list
class ContainerAppPreviewListDecorator(BaseContainerAppDecorator):
def __init__(
Expand Down
Loading