From 440f6f652bdedadcd59388cd062ee90434d60a28 Mon Sep 17 00:00:00 2001 From: NostraDavid <55331731+NostraDavid@users.noreply.github.com> Date: Wed, 14 Oct 2026 19:43:22 +0200 Subject: [PATCH 01/13] packaging: require Python 3.11 and use timezone-aware UTC datetimes --- .../templates/automation_test.yml | 2 +- .../messaging-cmdlet-gen.md | 4 +-- azure-pipelines.yml | 8 +++-- doc/command_guidelines.md | 2 +- doc/debug/debug_in_vs_code.md | 2 +- doc/extensions/authoring.md | 2 +- doc/install_linux_prerequisites.md | 2 +- scripts/ci/build.sh | 2 +- scripts/ci/codegen_report.py | 2 +- scripts/release/debian/Dockerfile | 2 +- .../azure/cli/core/commands/__init__.py | 2 +- .../azure/cli/core/intercept_survey.py | 12 ++++---- .../azure/cli/core/telemetry.py | 10 +++---- .../cli/core/tests/test_intercept_survey.py | 24 +++++++++++++++ src/azure-cli-core/pyproject.toml | 3 +- src/azure-cli-telemetry/pyproject.toml | 2 +- .../azure/cli/testsdk/preparers.py | 6 ++-- src/azure-cli-testsdk/pyproject.toml | 2 +- .../azure/cli/command_modules/acr/_format.py | 4 +-- .../azure/cli/command_modules/acr/_utils.py | 6 ++-- .../acs/managed_cluster_decorator.py | 2 +- .../acs/tests/latest/custom_preparers.py | 4 +-- .../acs/tests/latest/test_custom.py | 8 ++--- .../latest/test_managed_cluster_decorator.py | 4 +-- .../command_modules/apim/tests/__init__.py | 6 ++-- .../appservice/_github_oauth.py | 6 ++-- .../appservice/appservice_domains.py | 4 +-- .../cli/command_modules/appservice/custom.py | 6 ++-- .../appservice/static_sites.py | 4 +-- .../cli/command_modules/backup/custom.py | 8 ++--- .../backup/tests/latest/preparers.py | 6 ++-- .../backup/tests/latest/test_afs_commands.py | 6 ++-- .../tests/latest/test_backup_commands.py | 8 ++--- .../cli/command_modules/batchai/custom.py | 4 +-- .../command_modules/billing/tests/__init__.py | 6 ++-- .../containerapp/_github_oauth.py | 6 ++-- .../command_modules/containerapp/_utils.py | 16 +++++----- .../cli/command_modules/cosmosdb/custom.py | 10 ++----- .../test_cosmosdb_backuprestore_scenario.py | 23 +++++++++++++- .../tests/latest/test_cosmosdb_commands.py | 20 ++++++------- .../databoxedge/tests/__init__.py | 6 ++-- .../tests/latest/test_eventgrid_commands.py | 10 +++---- .../cli/command_modules/feedback/custom.py | 4 +-- .../feedback/tests/latest/test_feedback.py | 16 ++++++++++ .../azure/cli/command_modules/iot/_utils.py | 4 +-- .../iot/tests/latest/_test_utils.py | 8 ++--- .../tests/latest/test_keyvault_commands.py | 14 ++++----- .../lab/tests/latest/test_lab_validators.py | 13 +++++++- .../cli/command_modules/lab/validators.py | 4 ++- .../marketplaceordering/tests/__init__.py | 6 ++-- .../command_modules/monitor/_legacy/custom.py | 8 ++--- .../cli/command_modules/monitor/custom.py | 8 ++--- .../azure/cli/command_modules/mysql/_util.py | 7 ++--- .../azure/cli/command_modules/mysql/custom.py | 5 ++-- .../mysql/tests/latest/test_mysql_scenario.py | 15 +++++----- .../tests/latest/test_bucket_commands.py | 4 +-- .../tests/latest/test_network_commands.py | 21 ++++--------- .../postgresql/commands/backup_commands.py | 4 +-- .../commands/server_logs_commands.py | 7 ++--- .../postgresql/utils/_flexible_server_util.py | 5 ++-- .../rdbms/_flexible_server_util.py | 5 ++-- .../azure/cli/command_modules/rdbms/custom.py | 5 ++-- .../rdbms/flexible_server_custom_common.py | 5 ++-- .../rdbms/tests/latest/test_rdbms_commands.py | 5 ++-- .../redis/tests/latest/test_redis_scenario.py | 4 +-- .../resource/tests/latest/test_resource.py | 8 ++--- .../azure/cli/command_modules/role/custom.py | 10 +++---- .../role/tests/latest/test_role.py | 2 +- .../role/tests/latest/test_role_custom.py | 9 ++++++ .../sql/tests/latest/test_sql_commands.py | 22 +++++++------- .../command_modules/storage/_validators.py | 10 +++---- .../storage/operations/blob.py | 16 +++++----- .../storage/operations/file.py | 16 +++++----- .../storage/operations/fileshare.py | 4 +-- .../storage/operations/filesystem.py | 4 +-- .../storage/operations/fs_directory.py | 4 +-- .../storage/operations/fs_file.py | 4 +-- .../storage/operations/queue.py | 4 +-- .../latest/test_storage_account_scenarios.py | 6 ++-- .../test_storage_adls_gen2_scenarios.py | 30 +++++++++---------- .../latest/test_storage_azcopy_scenarios.py | 14 ++++----- .../latest/test_storage_batch_operations.py | 14 ++++----- .../test_storage_blob_copy_scenarios.py | 12 ++++---- .../test_storage_blob_live_scenarios.py | 6 ++-- .../test_storage_blob_rewrite_scenarios.py | 4 +-- .../latest/test_storage_blob_scenarios.py | 26 ++++++++-------- .../test_storage_container_access_policy.py | 8 ++--- .../latest/test_storage_file_scenarios.py | 12 ++++---- .../test_storage_file_share_scenarios.py | 14 ++++----- .../latest/test_storage_queue_scenarios.py | 14 ++++----- .../latest/test_storage_sas_scenarios.py | 28 ++++++++--------- .../test_storage_sas_tokan_url_scenarios.py | 6 ++-- .../latest/test_storage_table_scenarios.py | 4 +-- .../tests/latest/test_storage_validators.py | 5 ++++ .../azure/cli/command_modules/storage/util.py | 14 ++++----- src/azure-cli/pyproject.toml | 3 +- 96 files changed, 425 insertions(+), 357 deletions(-) create mode 100644 src/azure-cli-core/azure/cli/core/tests/test_intercept_survey.py diff --git a/.azure-pipelines/templates/automation_test.yml b/.azure-pipelines/templates/automation_test.yml index 742745a5ca6..0d6f5a02be4 100644 --- a/.azure-pipelines/templates/automation_test.yml +++ b/.azure-pipelines/templates/automation_test.yml @@ -3,7 +3,7 @@ parameters: - name: pythonVersion type: string - default: 3.10 + default: '3.11' - name: profile type: string default: latest diff --git a/.github/agents/Messaging-cmdlets-Generation/messaging-cmdlet-gen.md b/.github/agents/Messaging-cmdlets-Generation/messaging-cmdlet-gen.md index 7503cb292e6..70ad53134e4 100644 --- a/.github/agents/Messaging-cmdlets-Generation/messaging-cmdlet-gen.md +++ b/.github/agents/Messaging-cmdlets-Generation/messaging-cmdlet-gen.md @@ -83,7 +83,7 @@ $VENV = "$SPEC_ROOT\.venv-aaz" ## 2. Environment -**Python 3.10-3.14 required.** Verify with the explicit path, not `PATH`; do not install it yourself. +**Python 3.11-3.14 required.** Verify with the explicit path, not `PATH`; do not install it yourself. * Windows: `& "$env:LOCALAPPDATA\Programs\Python\Python312\python.exe" --version` (installer from ; path form `C:\Users\{xxxx}\AppData\Local\Programs\Python\Python3{xxxx}`) @@ -734,7 +734,7 @@ Further reading: azure-cli `doc/` (extensions, command guidelines, authoring tes - [ ] Plan echoed back and confirmed (service, `` -> ``, swagger path, branch, scope). - [ ] **Section 4.1 pre-flight**: `az provider show` confirms whether ARM serves `` yet. - [ ] **Section 4.2**: pre-existing aaz<->azure-cli drift isolated and listed before generating. -- [ ] Python 3.10-3.14 venv active; `aaz-dev` + `azdev` installed. +- [ ] Python 3.11-3.14 venv active; `aaz-dev` + `azdev` installed. - [ ] Repos synced from upstream; `aaz` and `azure-cli` on identically named `feature-*` branches. - [ ] `azdev setup --cli $CLI --repo $EXT` clean. - [ ] `aaz-dev run` generation complete for the agreed scope. diff --git a/azure-pipelines.yml b/azure-pipelines.yml index 280c045d7eb..550a02fd604 100644 --- a/azure-pipelines.yml +++ b/azure-pipelines.yml @@ -491,6 +491,8 @@ jobs: displayName: Test Python Wheels strategy: matrix: + Python311: + python.version: '3.11' Python312: python.version: '3.12' Python314: @@ -531,6 +533,8 @@ jobs: name: ${{ variables.ubuntu_pool }} strategy: matrix: + Python311: + python.version: '3.11' Python312: python.version: '3.12' Python314: @@ -548,8 +552,8 @@ jobs: name: ${{ variables.ubuntu_pool }} strategy: matrix: - Python310: - python.version: '3.10' + Python311: + python.version: '3.11' Python314: python.version: '3.14' steps: diff --git a/doc/command_guidelines.md b/doc/command_guidelines.md index 75059f0ff48..3aa07544da6 100644 --- a/doc/command_guidelines.md +++ b/doc/command_guidelines.md @@ -426,7 +426,7 @@ Follow the [Error Handling Guidelines](https://github.com/Azure/azure-cli/blob/d ## Coding Practices -- All code must support Python 3.10 ~ 3.14 +- All code must support Python 3.11 ~ 3.14 - PRs to Azure/azure-cli and Azure/azure-cli-extensions must pass CI - Code must pass style checks with pylint and pep8 - (*) All commands should have tests diff --git a/doc/debug/debug_in_vs_code.md b/doc/debug/debug_in_vs_code.md index 6a23e8383f7..1c49fb2cf77 100644 --- a/doc/debug/debug_in_vs_code.md +++ b/doc/debug/debug_in_vs_code.md @@ -4,7 +4,7 @@ * Visual Studio Code [Link](http://code.visualstudio.com/) * Visual Studio Code Python Extension [Link](https://marketplace.visualstudio.com/items?itemName=ms-python.python) -* Python 3.10+ +* Python 3.11+ * Set up development environment [Link](https://github.com/Azure/azure-cli/blob/master/doc/configuring_your_machine.md) ## Quick start diff --git a/doc/extensions/authoring.md b/doc/extensions/authoring.md index 2258e70bda1..ec2805e7afd 100644 --- a/doc/extensions/authoring.md +++ b/doc/extensions/authoring.md @@ -130,7 +130,7 @@ See [Extension Metadata](metadata.md) for more information. ### Test your extension on Python 3 -- The Azure CLI supports Python 3.10 ~ 3.14 so verify that your extension does the same. +- The Azure CLI supports Python 3.11 ~ 3.14 so verify that your extension does the same. - You can create virtual environments for different versions and run your extension in them. - e.g. `python3.14 -m venv env314`. diff --git a/doc/install_linux_prerequisites.md b/doc/install_linux_prerequisites.md index 5b006179282..5503c9b8377 100644 --- a/doc/install_linux_prerequisites.md +++ b/doc/install_linux_prerequisites.md @@ -6,7 +6,7 @@ Some native Linux packages are required when installing the CLI with: - Interactive installation script - `pip` -Current supported Python versions are Python 3.10 ~ 3.14. +Current supported Python versions are Python 3.11 ~ 3.14. The commands to run to install the dependencies for some common distributions are listed below. diff --git a/scripts/ci/build.sh b/scripts/ci/build.sh index 845a5d8b67b..6699b2dbce3 100755 --- a/scripts/ci/build.sh +++ b/scripts/ci/build.sh @@ -110,7 +110,6 @@ CLASSIFIERS = [ 'Intended Audience :: Developers', 'Programming Language :: Python', 'Programming Language :: Python :: 3', - 'Programming Language :: Python :: 3.10', 'Programming Language :: Python :: 3.11', 'Programming Language :: Python :: 3.12', 'License :: OSI Approved :: MIT License', @@ -125,6 +124,7 @@ setup( name='azure-cli-fulltest', version=VERSION, description='Microsoft Azure Command-Line Tools Full Tests', + python_requires='>=3.11.0', license='MIT', author='Microsoft Corporation', author_email='azpycli@microsoft.com', diff --git a/scripts/ci/codegen_report.py b/scripts/ci/codegen_report.py index b6e3810fb7d..f0b1a552549 100644 --- a/scripts/ci/codegen_report.py +++ b/scripts/ci/codegen_report.py @@ -111,7 +111,7 @@ def generate_csv(): total = ref['total'] manual = total - codegenv1 - codegenv2 is_release = True if BUILD_BRANCH == 'release' else False - date = (datetime.datetime.utcnow() + datetime.timedelta(hours=8)).strftime("%Y-%m-%d") + date = (datetime.datetime.now(datetime.UTC) + datetime.timedelta(hours=8)).strftime("%Y-%m-%d") data.append([BUILD_ID, manual, codegenv1, codegenv2, total, is_release, date]) logger.info('Finish generate data for codegen report:') logger.info("BUILD_ID, manual, codegenv1, codegenv2, total, is_release, date") diff --git a/scripts/release/debian/Dockerfile b/scripts/release/debian/Dockerfile index 11f4eaafdc1..73d81b42e1a 100644 --- a/scripts/release/debian/Dockerfile +++ b/scripts/release/debian/Dockerfile @@ -6,7 +6,7 @@ RUN apt-get update RUN apt-get install -y libssl-dev libffi-dev python3-dev debhelper zlib1g-dev wget # Download Python source code -ARG python_version="3.10.10" +ARG python_version="3.11.17" ENV PYTHON_SRC_DIR=/usr/src/python RUN mkdir -p ${PYTHON_SRC_DIR} && \ wget -qO- https://www.python.org/ftp/python/${python_version}/Python-${python_version}.tgz \ diff --git a/src/azure-cli-core/azure/cli/core/commands/__init__.py b/src/azure-cli-core/azure/cli/core/commands/__init__.py index 2700595662a..11dd3cb98d8 100644 --- a/src/azure-cli-core/azure/cli/core/commands/__init__.py +++ b/src/azure-cli-core/azure/cli/core/commands/__init__.py @@ -1060,7 +1060,7 @@ def _generate_template_progress(self, correlation_id): # pylint: disable=no-sel if correlation_id is not None: # pylint: disable=too-many-nested-blocks formatter = "eventTimestamp ge {}" - end_time = datetime.datetime.utcnow() + end_time = datetime.datetime.now(datetime.UTC) start_time = end_time - datetime.timedelta(seconds=DEFAULT_QUERY_TIME_RANGE) odata_filters = formatter.format(start_time.strftime('%Y-%m-%dT%H:%M:%SZ')) diff --git a/src/azure-cli-core/azure/cli/core/intercept_survey.py b/src/azure-cli-core/azure/cli/core/intercept_survey.py index 20d9c82fe11..1f4b4db363f 100644 --- a/src/azure-cli-core/azure/cli/core/intercept_survey.py +++ b/src/azure-cli-core/azure/cli/core/intercept_survey.py @@ -6,7 +6,7 @@ import os import sys import json -from datetime import datetime, timedelta +from datetime import UTC, datetime, timedelta from azure.cli.core._config import GLOBAL_CONFIG_DIR from azure.cli.core._profile import Profile from azure.cli.core.style import print_styled_text @@ -56,7 +56,7 @@ def should_prompt(cli): # using their installationId installation_id = Profile(cli_ctx=cli).get_installation_id() prompt_period = EXPERIENCE_PERIOD_IN_DAYS + (uuid.UUID(installation_id).int & 127) - next_prompt_time = datetime.utcnow() + timedelta(days=prompt_period) + next_prompt_time = datetime.now(UTC) + timedelta(days=prompt_period) survey_note = { 'last_prompt_time': '', 'next_prompt_time': next_prompt_time.strftime('%Y-%m-%dT%H:%M:%S') @@ -67,8 +67,8 @@ def should_prompt(cli): with open(GLOBAL_SURVEY_NOTE_PATH, 'r', encoding='utf-8-sig') as f: survey_note = json.load(f) - next_prompt_time = datetime.strptime(survey_note['next_prompt_time'], '%Y-%m-%dT%H:%M:%S') - if datetime.utcnow() < next_prompt_time: + next_prompt_time = datetime.strptime(survey_note['next_prompt_time'], '%Y-%m-%dT%H:%M:%S').replace(tzinfo=UTC) + if datetime.now(UTC) < next_prompt_time: return False return True @@ -92,9 +92,9 @@ def prompt_survey_message(cli): telemetry.set_survey_info(show_survey_message=True) # log prompt time - next_prompt_time = datetime.utcnow() + timedelta(days=PROMPT_INTERVAL_IN_DAYS) + next_prompt_time = datetime.now(UTC) + timedelta(days=PROMPT_INTERVAL_IN_DAYS) survey_note = { - 'last_prompt_time': datetime.utcnow().strftime('%Y-%m-%dT%H:%M:%S'), + 'last_prompt_time': datetime.now(UTC).strftime('%Y-%m-%dT%H:%M:%S'), 'next_prompt_time': next_prompt_time.strftime('%Y-%m-%dT%H:%M:%S') } with open(GLOBAL_SURVEY_NOTE_PATH, 'w') as f: diff --git a/src/azure-cli-core/azure/cli/core/telemetry.py b/src/azure-cli-core/azure/cli/core/telemetry.py index 1bb248f7bdf..c65611bffd4 100644 --- a/src/azure-cli-core/azure/cli/core/telemetry.py +++ b/src/azure-cli-core/azure/cli/core/telemetry.py @@ -292,7 +292,7 @@ def _wrapper(*args, **kwargs): def start(mode=None): if mode: _session.mode = mode - _session.start_time = datetime.datetime.utcnow() + _session.start_time = datetime.datetime.now(datetime.UTC) @decorators.suppress_all_exceptions() @@ -307,12 +307,12 @@ def set_invoke_time_elapsed(invoke_time_elapsed): @decorators.suppress_all_exceptions() def poll_start(): - _session.poll_start_time = datetime.datetime.utcnow() + _session.poll_start_time = datetime.datetime.now(datetime.UTC) @decorators.suppress_all_exceptions() def poll_end(): - _session.poll_end_time = datetime.datetime.utcnow() + _session.poll_end_time = datetime.datetime.now(datetime.UTC) @_user_agrees_to_telemetry @@ -322,7 +322,7 @@ def flush(): from azure.cli.telemetry import save # flush out current information - _session.end_time = datetime.datetime.utcnow() + _session.end_time = datetime.datetime.now(datetime.UTC) save(get_config_dir(), _session.generate_payload()) # reset session fields, retaining correlation id and application @@ -335,7 +335,7 @@ def conclude(): from azure.cli.core._environment import get_config_dir from azure.cli.telemetry import save - _session.end_time = datetime.datetime.utcnow() + _session.end_time = datetime.datetime.now(datetime.UTC) save(get_config_dir(), _session.generate_payload()) diff --git a/src/azure-cli-core/azure/cli/core/tests/test_intercept_survey.py b/src/azure-cli-core/azure/cli/core/tests/test_intercept_survey.py new file mode 100644 index 00000000000..a684f32b205 --- /dev/null +++ b/src/azure-cli-core/azure/cli/core/tests/test_intercept_survey.py @@ -0,0 +1,24 @@ +# -------------------------------------------------------------------------------------------- +# Copyright (c) Microsoft Corporation. All rights reserved. +# Licensed under the MIT License. See License.txt in the project root for license information. +# -------------------------------------------------------------------------------------------- + +import json +import unittest +from unittest import mock + +from azure.cli.core import intercept_survey + + +class TestInterceptSurvey(unittest.TestCase): + def test_should_prompt_with_existing_utc_note(self): + cli = mock.Mock() + cli.config.getboolean.return_value = True + for timestamp, expected in [('2000-01-01T00:00:00', True), ('2999-01-01T00:00:00', False)]: + with self.subTest(timestamp=timestamp): + note = json.dumps({'next_prompt_time': timestamp}) + with mock.patch.object(intercept_survey.sys.stderr, 'isatty', return_value=True), \ + mock.patch.object(intercept_survey.sys, 'argv', ['az', 'group', 'list']), \ + mock.patch.object(intercept_survey.os.path, 'isfile', return_value=True), \ + mock.patch('azure.cli.core.intercept_survey.open', mock.mock_open(read_data=note)): + self.assertEqual(intercept_survey.should_prompt(cli), expected) diff --git a/src/azure-cli-core/pyproject.toml b/src/azure-cli-core/pyproject.toml index e63644e9ed2..b166313eb41 100644 --- a/src/azure-cli-core/pyproject.toml +++ b/src/azure-cli-core/pyproject.toml @@ -7,7 +7,7 @@ name = "azure-cli-core" version = "2.91.0" description = "Microsoft Azure Command-Line Tools Core Module" readme = { file = "README.rst", content-type = "text/x-rst" } -requires-python = ">=3.10.0" +requires-python = ">=3.11.0" license = "MIT" license-files = ["LICENSE.txt"] authors = [{ name = "Microsoft Corporation", email = "azpycli@microsoft.com" }] @@ -17,7 +17,6 @@ classifiers = [ "Intended Audience :: System Administrators", "Programming Language :: Python", "Programming Language :: Python :: 3", - "Programming Language :: Python :: 3.10", "Programming Language :: Python :: 3.11", "Programming Language :: Python :: 3.12", "Programming Language :: Python :: 3.13", diff --git a/src/azure-cli-telemetry/pyproject.toml b/src/azure-cli-telemetry/pyproject.toml index 9998646086e..00f99b256ad 100644 --- a/src/azure-cli-telemetry/pyproject.toml +++ b/src/azure-cli-telemetry/pyproject.toml @@ -7,6 +7,7 @@ name = "azure-cli-telemetry" version = "1.1.0" description = "Microsoft Azure CLI Telemetry Package" dynamic = ["readme"] +requires-python = ">=3.11.0" license = "MIT" license-files = ["LICENSE.txt"] authors = [{ name = "Microsoft Corporation", email = "azpycli@microsoft.com" }] @@ -16,7 +17,6 @@ classifiers = [ "Intended Audience :: System Administrators", "Programming Language :: Python", "Programming Language :: Python :: 3", - "Programming Language :: Python :: 3.10", "Programming Language :: Python :: 3.11", "Programming Language :: Python :: 3.12", "Programming Language :: Python :: 3.13", diff --git a/src/azure-cli-testsdk/azure/cli/testsdk/preparers.py b/src/azure-cli-testsdk/azure/cli/testsdk/preparers.py index 8e7f079f118..92d8588c2be 100644 --- a/src/azure-cli-testsdk/azure/cli/testsdk/preparers.py +++ b/src/azure-cli-testsdk/azure/cli/testsdk/preparers.py @@ -5,7 +5,7 @@ import os import sys -from datetime import datetime +from datetime import UTC, datetime from .scenario_tests import AbstractPreparer, SingleValueReplacer from .base import LiveScenarioTest @@ -85,7 +85,7 @@ def create_resource(self, name, **kwargs): module = test_class_path[index_of_module] if index_of_module >= 0 else 'unknown' tags = {'product': 'azurecli', 'cause': 'automation test', - 'date': datetime.utcnow().strftime('%Y-%m-%dT%H:%M:%SZ'), + 'date': datetime.now(UTC).strftime('%Y-%m-%dT%H:%M:%SZ'), 'test': self.test_class_instance._testMethodName, 'module': module} if 'ENV_JOB_NAME' in os.environ: @@ -419,7 +419,7 @@ def create_resource(self, name, **kwargs): return {self.parameter_name: self.dev_setting_name, } tags = {'product': 'azurecli', 'cause': 'automation', - 'date': datetime.utcnow().strftime('%Y-%m-%dT%H:%M:%SZ')} + 'date': datetime.now(UTC).strftime('%Y-%m-%dT%H:%M:%SZ')} if 'ENV_JOB_NAME' in os.environ: tags['job'] = os.environ['ENV_JOB_NAME'] tags = ' '.join(['{}={}'.format(key, value) diff --git a/src/azure-cli-testsdk/pyproject.toml b/src/azure-cli-testsdk/pyproject.toml index efb4889660f..d51b9cd4c57 100644 --- a/src/azure-cli-testsdk/pyproject.toml +++ b/src/azure-cli-testsdk/pyproject.toml @@ -7,6 +7,7 @@ name = "azure-cli-testsdk" version = "0.3.0" description = "Microsoft Azure Command-Line Tools Test SDK Module" dynamic = ["readme"] +requires-python = ">=3.11.0" license = "MIT" license-files = ["LICENSE.txt"] authors = [{ name = "Microsoft Corporation", email = "azpycli@microsoft.com" }] @@ -15,7 +16,6 @@ classifiers = [ "Intended Audience :: Developers", "Programming Language :: Python", "Programming Language :: Python :: 3", - "Programming Language :: Python :: 3.10", "Programming Language :: Python :: 3.11", "Programming Language :: Python :: 3.12", "Programming Language :: Python :: 3.13", diff --git a/src/azure-cli/azure/cli/command_modules/acr/_format.py b/src/azure-cli/azure/cli/command_modules/acr/_format.py index d8f0f8307f4..7df8ad10c69 100644 --- a/src/azure-cli/azure/cli/command_modules/acr/_format.py +++ b/src/azure-cli/azure/cli/command_modules/acr/_format.py @@ -4,7 +4,7 @@ # -------------------------------------------------------------------------------------------- from collections import OrderedDict -from datetime import datetime +from datetime import UTC, datetime from knack.log import get_logger from ._constants import ( REF_KEY @@ -530,4 +530,4 @@ def _get_duration(start_time, finish_time): def add_timestamp(message): - return "{} {}".format(datetime.utcnow(), message) + return "{} {}".format(datetime.now(UTC), message) diff --git a/src/azure-cli/azure/cli/command_modules/acr/_utils.py b/src/azure-cli/azure/cli/command_modules/acr/_utils.py index 3873b4a4124..7a2d175445a 100644 --- a/src/azure-cli/azure/cli/command_modules/acr/_utils.py +++ b/src/azure-cli/azure/cli/command_modules/acr/_utils.py @@ -415,11 +415,11 @@ def remove_timer_trigger(task_name, def add_days_to_now(days): if days <= 0: raise CLIError('Days must be positive.') - from datetime import datetime, timedelta + from datetime import UTC, datetime, timedelta try: - return datetime.utcnow() + timedelta(days=days) + return datetime.now(UTC) + timedelta(days=days) except OverflowError: - return datetime.max + return datetime.max.replace(tzinfo=UTC) def is_vault_secret(cmd, credential): diff --git a/src/azure-cli/azure/cli/command_modules/acs/managed_cluster_decorator.py b/src/azure-cli/azure/cli/command_modules/acs/managed_cluster_decorator.py index 0fa7a008657..fb007b9763d 100644 --- a/src/azure-cli/azure/cli/command_modules/acs/managed_cluster_decorator.py +++ b/src/azure-cli/azure/cli/command_modules/acs/managed_cluster_decorator.py @@ -9601,7 +9601,7 @@ def update_upgrade_settings(self, mc: ManagedCluster) -> ManagedCluster: f"{override_until} is not a valid datatime format." ) elif force_upgrade: - default_extended_until = datetime.datetime.utcnow() + datetime.timedelta(days=3) + default_extended_until = datetime.datetime.now(datetime.UTC) + datetime.timedelta(days=3) if existing_until is None or existing_until.timestamp() < default_extended_until.timestamp(): mc.upgrade_settings.override_settings.until = default_extended_until diff --git a/src/azure-cli/azure/cli/command_modules/acs/tests/latest/custom_preparers.py b/src/azure-cli/azure/cli/command_modules/acs/tests/latest/custom_preparers.py index 3d4c47b98fe..fe8db67be60 100644 --- a/src/azure-cli/azure/cli/command_modules/acs/tests/latest/custom_preparers.py +++ b/src/azure-cli/azure/cli/command_modules/acs/tests/latest/custom_preparers.py @@ -3,7 +3,7 @@ # Licensed under the MIT License. See License.txt in the project root for license information. # -------------------------------------------------------------------------------------------- -from datetime import datetime +from datetime import UTC, datetime import os import unittest @@ -126,7 +126,7 @@ def create_resource(self, name, **kwargs): tags = { "product": "azurecli", "cause": "automation", - "date": datetime.utcnow().strftime("%Y-%m-%dT%H:%M:%SZ"), + "date": datetime.now(UTC).strftime("%Y-%m-%dT%H:%M:%SZ"), } if "ENV_JOB_NAME" in os.environ: tags["job"] = os.environ["ENV_JOB_NAME"] diff --git a/src/azure-cli/azure/cli/command_modules/acs/tests/latest/test_custom.py b/src/azure-cli/azure/cli/command_modules/acs/tests/latest/test_custom.py index 71ddc158a97..efaa08a39aa 100644 --- a/src/azure-cli/azure/cli/command_modules/acs/tests/latest/test_custom.py +++ b/src/azure-cli/azure/cli/command_modules/acs/tests/latest/test_custom.py @@ -2140,8 +2140,8 @@ def test_update_upgrade_settings(self, rg_def, get_resource_groups_client, get_r instance.upgrade_settings = None instance = _update_upgrade_settings(MockCmd(self.cli), instance, enable_force_upgrade=True, disable_force_upgrade=False, upgrade_override_until=None) self.assertTrue(instance.upgrade_settings.override_settings.force_upgrade) - self.assertGreater(instance.upgrade_settings.override_settings.until.timestamp(), (datetime.datetime.utcnow() + datetime.timedelta(days=2)).timestamp()) - self.assertLess(instance.upgrade_settings.override_settings.until.timestamp(), (datetime.datetime.utcnow() + datetime.timedelta(days=4)).timestamp()) + self.assertGreater(instance.upgrade_settings.override_settings.until.timestamp(), (datetime.datetime.now(datetime.UTC) + datetime.timedelta(days=2)).timestamp()) + self.assertLess(instance.upgrade_settings.override_settings.until.timestamp(), (datetime.datetime.now(datetime.UTC) + datetime.timedelta(days=4)).timestamp()) instance.upgrade_settings = self.models.ClusterUpgradeSettings( override_settings = self.models.UpgradeOverrideSettings( @@ -2150,8 +2150,8 @@ def test_update_upgrade_settings(self, rg_def, get_resource_groups_client, get_r ) instance = _update_upgrade_settings(MockCmd(self.cli), instance, enable_force_upgrade=True, disable_force_upgrade=False, upgrade_override_until=None) self.assertTrue(instance.upgrade_settings.override_settings.force_upgrade) - self.assertGreater(instance.upgrade_settings.override_settings.until.timestamp(), (datetime.datetime.utcnow() + datetime.timedelta(days=2)).timestamp()) - self.assertLess(instance.upgrade_settings.override_settings.until.timestamp(), (datetime.datetime.utcnow() + datetime.timedelta(days=4)).timestamp()) + self.assertGreater(instance.upgrade_settings.override_settings.until.timestamp(), (datetime.datetime.now(datetime.UTC) + datetime.timedelta(days=2)).timestamp()) + self.assertLess(instance.upgrade_settings.override_settings.until.timestamp(), (datetime.datetime.now(datetime.UTC) + datetime.timedelta(days=4)).timestamp()) # force_upgrade False instance.upgrade_settings = None diff --git a/src/azure-cli/azure/cli/command_modules/acs/tests/latest/test_managed_cluster_decorator.py b/src/azure-cli/azure/cli/command_modules/acs/tests/latest/test_managed_cluster_decorator.py index 02a3af58251..68c0e808603 100644 --- a/src/azure-cli/azure/cli/command_modules/acs/tests/latest/test_managed_cluster_decorator.py +++ b/src/azure-cli/azure/cli/command_modules/acs/tests/latest/test_managed_cluster_decorator.py @@ -10556,8 +10556,8 @@ def test_update_upgrade_settings(self): dec_3.context.attach_mc(mc_3) dec_mc_3 = dec_3.update_upgrade_settings(mc_3) self.assertEqual(dec_mc_3.upgrade_settings.override_settings.force_upgrade, True) - self.assertGreater(dec_mc_3.upgrade_settings.override_settings.until.timestamp(), (datetime.datetime.utcnow() + datetime.timedelta(days=2)).timestamp()) - self.assertLess(dec_mc_3.upgrade_settings.override_settings.until.timestamp(), (datetime.datetime.utcnow() + datetime.timedelta(days=4)).timestamp()) + self.assertGreater(dec_mc_3.upgrade_settings.override_settings.until.timestamp(), (datetime.datetime.now(datetime.UTC) + datetime.timedelta(days=2)).timestamp()) + self.assertLess(dec_mc_3.upgrade_settings.override_settings.until.timestamp(), (datetime.datetime.now(datetime.UTC) + datetime.timedelta(days=4)).timestamp()) # Set Until dec_4 = AKSManagedClusterUpdateDecorator( diff --git a/src/azure-cli/azure/cli/command_modules/apim/tests/__init__.py b/src/azure-cli/azure/cli/command_modules/apim/tests/__init__.py index ba281d0650b..73e0338f818 100644 --- a/src/azure-cli/azure/cli/command_modules/apim/tests/__init__.py +++ b/src/azure-cli/azure/cli/command_modules/apim/tests/__init__.py @@ -61,14 +61,14 @@ def wrapper(*args, **kwargs): test_map[func.__name__]["error_message"] = "" test_map[func.__name__]["error_stack"] = "" test_map[func.__name__]["error_normalized"] = "" - test_map[func.__name__]["start_dt"] = dt.datetime.utcnow() + test_map[func.__name__]["start_dt"] = dt.datetime.now(dt.UTC) ret = func_to_call(*args, **kwargs) except (AssertionError, AzureError, CliTestError, CliExecutionError, SystemExit, JMESPathCheckAssertionError) as e: use_exception_cache = os.getenv("TEST_EXCEPTION_CACHE") if use_exception_cache is None or use_exception_cache.lower() != "true": raise - test_map[func.__name__]["end_dt"] = dt.datetime.utcnow() + test_map[func.__name__]["end_dt"] = dt.datetime.now(dt.UTC) test_map[func.__name__]["result"] = FAILED test_map[func.__name__]["error_message"] = str(e).replace("\r\n", " ").replace("\n", " ")[:500] test_map[func.__name__]["error_stack"] = traceback.format_exc().replace( @@ -80,7 +80,7 @@ def wrapper(*args, **kwargs): logger.info(traceback.format_exc()) exceptions.append((func.__name__, sys.exc_info())) else: - test_map[func.__name__]["end_dt"] = dt.datetime.utcnow() + test_map[func.__name__]["end_dt"] = dt.datetime.now(dt.UTC) return ret if inspect.isclass(func): diff --git a/src/azure-cli/azure/cli/command_modules/appservice/_github_oauth.py b/src/azure-cli/azure/cli/command_modules/appservice/_github_oauth.py index c43f257f3fa..fca076c29b1 100644 --- a/src/azure-cli/azure/cli/command_modules/appservice/_github_oauth.py +++ b/src/azure-cli/azure/cli/command_modules/appservice/_github_oauth.py @@ -6,7 +6,7 @@ import os import sys -from datetime import datetime +from datetime import UTC, datetime from knack.log import get_logger from azure.cli.core.util import open_page_in_browser @@ -49,10 +49,10 @@ def cache_github_token(cmd, token, repo): if isinstance(entry, dict) and entry.get("value") == token: if repo not in entry.get("repos", []): entry["repos"] = [*entry.get("repos", []), repo] - entry["last_modified_timestamp"] = datetime.utcnow().timestamp() + entry["last_modified_timestamp"] = datetime.now(UTC).timestamp() break else: - cache_entry = {"last_modified_timestamp": datetime.utcnow().timestamp(), "value": token, "repos": [repo]} + cache_entry = {"last_modified_timestamp": datetime.now(UTC).timestamp(), "value": token, "repos": [repo]} cache = [cache_entry, *cache] secret_store.save(cache) diff --git a/src/azure-cli/azure/cli/command_modules/appservice/appservice_domains.py b/src/azure-cli/azure/cli/command_modules/appservice/appservice_domains.py index 6ee75725a80..b81cc880396 100644 --- a/src/azure-cli/azure/cli/command_modules/appservice/appservice_domains.py +++ b/src/azure-cli/azure/cli/command_modules/appservice/appservice_domains.py @@ -21,7 +21,7 @@ def create_domain(cmd, resource_group_name, hostname, contact_info, privacy=True accept_terms=False, tags=None, dryrun=False, no_wait=False): from azure.cli.core.commands.arm import ArmTemplateBuilder from azure.cli.command_modules.appservice._template_builder import (build_dns_zone, build_domain) - from datetime import datetime + from datetime import UTC, datetime import socket import json @@ -41,7 +41,7 @@ def create_domain(cmd, resource_group_name, hostname, contact_info, privacy=True '/blob/master/contact_info.json') contact_info = verify_contact_info_and_format(contact_info) - current_time = str(datetime.utcnow()).replace('+00:00', 'Z') + current_time = str(datetime.now(UTC)).replace('+00:00', 'Z') local_ip_address = '' try: local_ip_address = socket.gethostbyname(socket.gethostname()) diff --git a/src/azure-cli/azure/cli/command_modules/appservice/custom.py b/src/azure-cli/azure/cli/command_modules/appservice/custom.py index 218ed67f79f..1e91400bd5b 100644 --- a/src/azure-cli/azure/cli/command_modules/appservice/custom.py +++ b/src/azure-cli/azure/cli/command_modules/appservice/custom.py @@ -2217,7 +2217,7 @@ def progress_callback(current, total): blob_client = container_client.upload_blob(blob_name, zip_content, validate_content=True, progress_hook=progress_callback) - now = datetime.datetime.utcnow() + now = datetime.datetime.now(datetime.UTC) blob_start = now - datetime.timedelta(minutes=10) blob_end = now + datetime.timedelta(weeks=520) BlobSharedAccessSignature = get_sdk(cmd.cli_ctx, ResourceType.DATA_STORAGE_BLOB, @@ -6482,7 +6482,7 @@ def update_backup_schedule(cmd, resource_group_name, webapp_name, storage_accoun if backup_name and backup_name.lower().endswith('.zip'): backup_name = backup_name[:-4] if not backup_name: - backup_name = '{0}_{1}'.format(webapp_name, datetime.datetime.utcnow().strftime('%Y%m%d%H%M')) + backup_name = '{0}_{1}'.format(webapp_name, datetime.datetime.now(datetime.UTC).strftime('%Y%m%d%H%M')) try: configuration = _generic_site_operation(cmd.cli_ctx, resource_group_name, webapp_name, @@ -9106,7 +9106,7 @@ def _is_valid_runtime_setting(cls, runtime_setting, include_eol=False): end_of_life_dt = end_of_life.replace(tzinfo=datetime.timezone.utc) else: end_of_life_dt = end_of_life - now_utc = datetime.datetime.utcnow().replace(tzinfo=datetime.timezone.utc) + now_utc = datetime.datetime.now(datetime.UTC) if now_utc >= end_of_life_dt: return False except (ValueError, AttributeError): diff --git a/src/azure-cli/azure/cli/command_modules/appservice/static_sites.py b/src/azure-cli/azure/cli/command_modules/appservice/static_sites.py index de9cc8829d5..d4aa68ff669 100644 --- a/src/azure-cli/azure/cli/command_modules/appservice/static_sites.py +++ b/src/azure-cli/azure/cli/command_modules/appservice/static_sites.py @@ -14,7 +14,7 @@ from knack.log import get_logger from urllib.parse import urlparse import uuid -from datetime import datetime, timedelta +from datetime import UTC, datetime, timedelta from .utils import normalize_sku_for_staticapp, raise_missing_token_suggestion, raise_missing_ado_token_suggestion from .custom import show_app, _build_identities_info @@ -406,7 +406,7 @@ def _get_ado_token(cmd, source): body = { "displayName": f"SWA Deployment Token (created via the Azure CLI) - {uuid.uuid4().hex}", "scope": "vso.build_execute vso.code_full vso.variablegroups_manage", - "validTo": (datetime.utcnow() + timedelta(days=6 * 30)).isoformat(), + "validTo": (datetime.now(UTC) + timedelta(days=6 * 30)).isoformat(), "allOrgs": False, "targetAccounts": [org_id], } diff --git a/src/azure-cli/azure/cli/command_modules/backup/custom.py b/src/azure-cli/azure/cli/command_modules/backup/custom.py index e4b18cbc6f3..9e41769ab73 100644 --- a/src/azure-cli/azure/cli/command_modules/backup/custom.py +++ b/src/azure-cli/azure/cli/command_modules/backup/custom.py @@ -5,7 +5,7 @@ import time import os -from datetime import datetime, timedelta, timezone +from datetime import UTC, datetime, timedelta, timezone # pylint: disable=too-many-lines from knack.log import get_logger from knack.prompting import prompt_y_n @@ -1860,7 +1860,7 @@ def stop_job(client, resource_group_name, vault_name, name, use_secondary_region def wait_for_job(cmd, client, resource_group_name, vault_name, name, timeout=None, use_secondary_region=None): logger.warning("Waiting for job '%s' ...", name) - start_timestamp = datetime.utcnow() + start_timestamp = datetime.now(UTC) if use_secondary_region: vault = vaults_cf(cmd.cli_ctx).get(resource_group_name, vault_name) vault_location = vault.location @@ -1869,7 +1869,7 @@ def wait_for_job(cmd, client, resource_group_name, vault_name, name, timeout=Non job_details = client.get(azure_region, CrrJobRequest(resource_id=vault.id, job_name=name)) while cust_help.job_in_progress(job_details.properties.status): if timeout: - elapsed_time = datetime.utcnow() - start_timestamp + elapsed_time = datetime.now(UTC) - start_timestamp if elapsed_time.seconds > timeout: logger.warning("Command timed out while waiting for job '%s'", name) break @@ -1879,7 +1879,7 @@ def wait_for_job(cmd, client, resource_group_name, vault_name, name, timeout=Non job_details = client.get(vault_name, resource_group_name, name) while cust_help.job_in_progress(job_details.properties.status): if timeout: - elapsed_time = datetime.utcnow() - start_timestamp + elapsed_time = datetime.now(UTC) - start_timestamp if elapsed_time.seconds > timeout: logger.warning("Command timed out while waiting for job '%s'", name) break diff --git a/src/azure-cli/azure/cli/command_modules/backup/tests/latest/preparers.py b/src/azure-cli/azure/cli/command_modules/backup/tests/latest/preparers.py index 48b8f09df07..a06a09fcdec 100644 --- a/src/azure-cli/azure/cli/command_modules/backup/tests/latest/preparers.py +++ b/src/azure-cli/azure/cli/command_modules/backup/tests/latest/preparers.py @@ -5,7 +5,7 @@ import json import os -from datetime import datetime, timedelta +from datetime import UTC, datetime, timedelta from azure.cli.testsdk import CliTestError from azure.cli.testsdk.preparers import AbstractPreparer, SingleValueReplacer, KeyVaultPreparer @@ -395,7 +395,7 @@ def create_resource(self, name, **kwargs): vault = self._get_vault(**kwargs) vm = self._get_vm(**kwargs) - retain_date = datetime.utcnow() + timedelta(days=30) + retain_date = datetime.now(UTC) + timedelta(days=30) command_string = 'az backup protection backup-now --backup-management-type AzureIaasVM --workload-type VM -g {} -v {} -c {} -i {} --retain-until {} --query name' command_string = command_string.format(self.resource_group, vault, vm, vm, retain_date.strftime('%d-%m-%Y')) @@ -817,7 +817,7 @@ def create_resource(self, name, **kwargs): vault = self._get_vault(**kwargs) afs = self._get_file_share(**kwargs) - retain_date = datetime.utcnow() + timedelta(days=30) + retain_date = datetime.now(UTC) + timedelta(days=30) retain_date = retain_date.strftime('%d-%m-%Y') command_string = 'az backup protection backup-now' command_string += ' -g {} -v {} -i {} -c {} --backup-management-type AzureStorage --retain-until {} --query name' diff --git a/src/azure-cli/azure/cli/command_modules/backup/tests/latest/test_afs_commands.py b/src/azure-cli/azure/cli/command_modules/backup/tests/latest/test_afs_commands.py index 54039dabdd3..ed24a3cda3f 100644 --- a/src/azure-cli/azure/cli/command_modules/backup/tests/latest/test_afs_commands.py +++ b/src/azure-cli/azure/cli/command_modules/backup/tests/latest/test_afs_commands.py @@ -4,7 +4,7 @@ # -------------------------------------------------------------------------------------------- import json -from datetime import datetime, timedelta +from datetime import UTC, datetime, timedelta import unittest import time from azure.cli.testsdk import ScenarioTest, JMESPathCheckExists, \ @@ -42,7 +42,7 @@ def test_afs_backup_scenario(self, resource_group, vault_name, storage_account, }) self.cmd('backup protection enable-for-azurefileshare -g {rg} -v {vault} --storage-account {container} --azure-file-share {item} -p {policy}') - self.kwargs['retain_date'] = (datetime.utcnow() + timedelta(days=30)).strftime('%d-%m-%Y') + self.kwargs['retain_date'] = (datetime.now(UTC) + timedelta(days=30)).strftime('%d-%m-%Y') self.kwargs['job'] = self.cmd('backup protection backup-now -g {rg} -v {vault} -c {container} -i {item} --retain-until {retain_date} --backup-management-type AzureStorage --query name').get_output_in_json() self.cmd('backup job wait -g {rg} -v {vault} -n {job}') @@ -384,7 +384,7 @@ def test_afs_backup_rp(self, resource_group, vault_name, storage_account, afs_na rp_list = self.cmd('backup recoverypoint list -g {rg} -v {vault} -c {container} -i {item1} --backup-management-type {type}').get_output_in_json() rp_count1 = len(rp_list) - self.kwargs['retain_date'] = (datetime.utcnow() + timedelta(days=30)).strftime('%d-%m-%Y') + self.kwargs['retain_date'] = (datetime.now(UTC) + timedelta(days=30)).strftime('%d-%m-%Y') self.kwargs['job'] = self.cmd('backup protection backup-now -g {rg} -v {vault} -c {container} -i {item1} --retain-until {retain_date} --backup-management-type AzureStorage --query name').get_output_in_json() self.cmd('backup job wait -g {rg} -v {vault} -n {job}') diff --git a/src/azure-cli/azure/cli/command_modules/backup/tests/latest/test_backup_commands.py b/src/azure-cli/azure/cli/command_modules/backup/tests/latest/test_backup_commands.py index cb8404fe7d6..06bf14528df 100644 --- a/src/azure-cli/azure/cli/command_modules/backup/tests/latest/test_backup_commands.py +++ b/src/azure-cli/azure/cli/command_modules/backup/tests/latest/test_backup_commands.py @@ -4,7 +4,7 @@ # -------------------------------------------------------------------------------------------- import json -from datetime import datetime, timedelta +from datetime import UTC, datetime, timedelta from types import SimpleNamespace import unittest import time @@ -107,7 +107,7 @@ def test_backup_scenario(self, resource_group, vault_name, vm_name, storage_acco self.kwargs['item'] = self.cmd('backup item list -g {rg} -v {vault} -c {container} --backup-management-type AzureIaasVM --workload-type VM --query [0].properties.friendlyName').get_output_in_json() # Trigger Backup - self.kwargs['retain_date'] = (datetime.utcnow() + timedelta(days=30)).strftime('%d-%m-%Y') + self.kwargs['retain_date'] = (datetime.now(UTC) + timedelta(days=30)).strftime('%d-%m-%Y') self.kwargs['job'] = self.cmd('backup protection backup-now -g {rg} -v {vault} -c {container} -i {item} --backup-management-type AzureIaasVM --workload-type VM --retain-until {retain_date} --query name').get_output_in_json() self.cmd('backup job wait -g {rg} -v {vault} -n {job}') @@ -1028,7 +1028,7 @@ def _free_csb_vm(): self.assertEqual(item['properties']['containerSubscriptionId'], self.kwargs['vm_sub']) # Take an on-demand backup so there is a recovery point to restore from. - self.kwargs['retain_date'] = (datetime.utcnow() + timedelta(days=30)).strftime('%d-%m-%Y') + self.kwargs['retain_date'] = (datetime.now(UTC) + timedelta(days=30)).strftime('%d-%m-%Y') self.kwargs['backup_job'] = self.cmd('backup protection backup-now --backup-management-type AzureIaasVM ' '--workload-type VM -g {rg} -v {vault} -c {vm_name} -i {vm_name} ' '--retain-until {retain_date} --query name').get_output_in_json() @@ -1510,7 +1510,7 @@ def test_backup_disk_exclusion(self, resource_group, vault_name, vm_name, storag self.check('properties.extendedProperties.diskExclusionProperties.isInclusionList', False), ]) - self.kwargs['retain_date'] = (datetime.utcnow() + timedelta(days=30)).strftime('%d-%m-%Y') + self.kwargs['retain_date'] = (datetime.now(UTC) + timedelta(days=30)).strftime('%d-%m-%Y') self.kwargs['job'] = self.cmd('backup protection backup-now -g {rg} -v {vault} -c {vm} -i {vm} --backup-management-type AzureIaasVM --workload-type VM --retain-until {retain_date} --query name').get_output_in_json() self.cmd('backup job wait -g {rg} -v {vault} -n {job}') diff --git a/src/azure-cli/azure/cli/command_modules/batchai/custom.py b/src/azure-cli/azure/cli/command_modules/batchai/custom.py index b5c256d1618..56ffea22c4c 100644 --- a/src/azure-cli/azure/cli/command_modules/batchai/custom.py +++ b/src/azure-cli/azure/cli/command_modules/batchai/custom.py @@ -794,7 +794,7 @@ def _get_files_from_bfs(cli_ctx, bfs, path, expiry): if b.properties.content_settings.contentMd5 is not None: name = os.path.basename(b.name) sas = sas_client.generate_blob(bfs.container_name, b.name, permission=BlobSasPermissions(read=True), - expiry=datetime.datetime.utcnow() + datetime.timedelta(minutes=expiry)) + expiry=datetime.datetime.now(datetime.UTC) + datetime.timedelta(minutes=expiry)) blob_url = b.url if '?' not in blob_url: blob_url += '?' + sas @@ -843,7 +843,7 @@ def _get_files_from_afs(cli_ctx, afs, path, expiry): if not f.is_directory: sas = sas_client.generate_file(share_name, directory_name=effective_path, file_name=f.name, permission=FileSasPermissions(read=True), - expiry=datetime.datetime.utcnow() + datetime.timedelta(minutes=expiry)) + expiry=datetime.datetime.now(datetime.UTC) + datetime.timedelta(minutes=expiry)) file_url = share_client.url + '/' + effective_path + '/' + f.name if '?' not in file_url: file_url += '?' + sas diff --git a/src/azure-cli/azure/cli/command_modules/billing/tests/__init__.py b/src/azure-cli/azure/cli/command_modules/billing/tests/__init__.py index 79f05307443..eb720283df1 100644 --- a/src/azure-cli/azure/cli/command_modules/billing/tests/__init__.py +++ b/src/azure-cli/azure/cli/command_modules/billing/tests/__init__.py @@ -62,11 +62,11 @@ def wrapper(*args, **kwargs): test_map[func.__name__]["error_message"] = "" test_map[func.__name__]["error_stack"] = "" test_map[func.__name__]["error_normalized"] = "" - test_map[func.__name__]["start_dt"] = dt.datetime.utcnow() + test_map[func.__name__]["start_dt"] = dt.datetime.now(dt.UTC) ret = func_to_call(*args, **kwargs) except (AssertionError, AzureError, CliTestError, CliExecutionError, SystemExit, JMESPathCheckAssertionError) as e: - test_map[func.__name__]["end_dt"] = dt.datetime.utcnow() + test_map[func.__name__]["end_dt"] = dt.datetime.now(dt.UTC) test_map[func.__name__]["result"] = FAILED test_map[func.__name__]["error_message"] = str(e).replace("\r\n", " ").replace("\n", " ")[:500] test_map[func.__name__]["error_stack"] = traceback.format_exc().replace( @@ -78,7 +78,7 @@ def wrapper(*args, **kwargs): logger.info(traceback.format_exc()) exceptions.append((func.__name__, sys.exc_info())) else: - test_map[func.__name__]["end_dt"] = dt.datetime.utcnow() + test_map[func.__name__]["end_dt"] = dt.datetime.now(dt.UTC) return ret if inspect.isclass(func): diff --git a/src/azure-cli/azure/cli/command_modules/containerapp/_github_oauth.py b/src/azure-cli/azure/cli/command_modules/containerapp/_github_oauth.py index ce1ac41d337..0e2583c4cd7 100644 --- a/src/azure-cli/azure/cli/command_modules/containerapp/_github_oauth.py +++ b/src/azure-cli/azure/cli/command_modules/containerapp/_github_oauth.py @@ -6,7 +6,7 @@ import os import sys -from datetime import datetime +from datetime import UTC, datetime from knack.log import get_logger from azure.cli.core.util import open_page_in_browser @@ -49,10 +49,10 @@ def cache_github_token(cmd, token, repo): if isinstance(entry, dict) and entry.get("value") == token: if repo not in entry.get("repos", []): entry["repos"] = [*entry.get("repos", []), repo] - entry["last_modified_timestamp"] = datetime.utcnow().timestamp() + entry["last_modified_timestamp"] = datetime.now(UTC).timestamp() break else: - cache_entry = {"last_modified_timestamp": datetime.utcnow().timestamp(), "value": token, "repos": [repo]} + cache_entry = {"last_modified_timestamp": datetime.now(UTC).timestamp(), "value": token, "repos": [repo]} cache = [cache_entry, *cache] secret_store.save(cache) diff --git a/src/azure-cli/azure/cli/command_modules/containerapp/_utils.py b/src/azure-cli/azure/cli/command_modules/containerapp/_utils.py index 35e98f1ab82..7a5af84a314 100644 --- a/src/azure-cli/azure/cli/command_modules/containerapp/_utils.py +++ b/src/azure-cli/azure/cli/command_modules/containerapp/_utils.py @@ -21,7 +21,7 @@ from urllib.parse import urlparse from urllib.request import urlopen -from datetime import datetime +from datetime import UTC, datetime from dateutil.relativedelta import relativedelta from azure.cli.core.azclierror import (ValidationError, RequiredArgumentMissingError, CLIInternalError, ResourceNotFoundError, FileOperationError, CLIError, UnauthorizedError, @@ -161,7 +161,7 @@ def create_service_principal_for_github_action(cmd, scopes=None, role="contribut RETRY_LIMIT = 36 client = graph_client_factory(cmd.cli_ctx) - now = datetime.utcnow() + now = datetime.now(UTC) app_display_name = 'azure-cli-' + now.strftime('%Y-%m-%d-%H-%M-%S') app = retry_until_success(_create_application, SP_CREATION_ERR_TXT, RETRY_LIMIT, client, display_name=app_display_name) sp = retry_until_success(_create_service_principal, SP_CREATION_ERR_TXT, RETRY_LIMIT, client, app_id=app["appId"]) @@ -221,7 +221,7 @@ def trigger_workflow(token, repo, workflow_name, branch): def await_github_action(token, repo, workflow_name, timeout_secs=1200): from ._clients import PollingAnimation - start = datetime.utcnow() + start = datetime.now(UTC) animation = PollingAnimation() animation.tick() @@ -237,14 +237,14 @@ def await_github_action(token, repo, workflow_name, timeout_secs=1200): pass animation.flush() - if (datetime.utcnow() - start).seconds >= timeout_secs: + if (datetime.now(UTC) - start).seconds >= timeout_secs: raise CLIInternalError("Timed out while waiting for the Github action to start.") runs = workflow.get_runs() while runs is None or not [r for r in runs if r.status in ('queued', 'in_progress')]: time.sleep(SHORT_POLLING_INTERVAL_SECS) runs = workflow.get_runs() - if (datetime.utcnow() - start).seconds >= timeout_secs: + if (datetime.now(UTC) - start).seconds >= timeout_secs: raise CLIInternalError("Timed out while waiting for the Github action to be started.") runs = [r for r in runs if r.status in ('queued', 'in_progress')] runs.sort(key=lambda r: r.created_at, reverse=True) @@ -258,7 +258,7 @@ def await_github_action(token, repo, workflow_name, timeout_secs=1200): animation.tick() status = github_repo.get_workflow_run(run_id).status animation.flush() - if (datetime.utcnow() - start).seconds >= timeout_secs: + if (datetime.now(UTC) - start).seconds >= timeout_secs: raise CLIInternalError("Timed out while waiting for the Github action to complete.") animation.flush() # needed to clear the animation from the terminal @@ -294,11 +294,11 @@ def _register_resource_provider(cmd, resource_provider): # wait for registration to finish timeout_secs = 120 registration = _is_resource_provider_registered(cmd, resource_provider) - start = datetime.utcnow() + start = datetime.now(UTC) while not registration: registration = _is_resource_provider_registered(cmd, resource_provider) time.sleep(SHORT_POLLING_INTERVAL_SECS) - if (datetime.utcnow() - start).seconds >= timeout_secs: + if (datetime.now(UTC) - start).seconds >= timeout_secs: raise CLIInternalError(f"Timed out while waiting for the {resource_provider} resource provider to be registered.") except Exception as e: diff --git a/src/azure-cli/azure/cli/command_modules/cosmosdb/custom.py b/src/azure-cli/azure/cli/command_modules/cosmosdb/custom.py index 466842c2698..2274aff75b4 100644 --- a/src/azure-cli/azure/cli/command_modules/cosmosdb/custom.py +++ b/src/azure-cli/azure/cli/command_modules/cosmosdb/custom.py @@ -1900,11 +1900,7 @@ def cli_cosmosdb_restore(cmd, target_restorable_account = None restore_timestamp_datetime_utc = _convert_to_utc_timestamp(restore_timestamp) - # If restore timestamp is timezone aware, get the utcnow as timezone aware as well - from datetime import timezone - current_dateTime = datetime.datetime.utcnow() - if restore_timestamp_datetime_utc.tzinfo is not None and restore_timestamp_datetime_utc.tzinfo.utcoffset(restore_timestamp_datetime_utc) is not None: - current_dateTime = datetime.datetime.now(timezone.utc) + current_dateTime = datetime.datetime.now(datetime.UTC) # Fail if provided restoretimesamp is greater than current timestamp if restore_timestamp_datetime_utc > current_dateTime: @@ -1952,11 +1948,11 @@ def _convert_to_utc_timestamp(timestamp_string): import dateutil.parser timestamp_datetime = dateutil.parser.parse(timestamp_string) timestamp_datetime_utc = None - # Convert to utc only if timezone aware + # Treat timestamps without an offset as UTC. if timestamp_datetime.tzinfo is not None and timestamp_datetime.tzinfo.utcoffset(timestamp_datetime) is not None: timestamp_datetime_utc = timestamp_datetime.astimezone(datetime.timezone.utc) else: - timestamp_datetime_utc = timestamp_datetime + timestamp_datetime_utc = timestamp_datetime.replace(tzinfo=datetime.UTC) return timestamp_datetime_utc diff --git a/src/azure-cli/azure/cli/command_modules/cosmosdb/tests/latest/test_cosmosdb_backuprestore_scenario.py b/src/azure-cli/azure/cli/command_modules/cosmosdb/tests/latest/test_cosmosdb_backuprestore_scenario.py index eee0536c6ff..a86521137e0 100644 --- a/src/azure-cli/azure/cli/command_modules/cosmosdb/tests/latest/test_cosmosdb_backuprestore_scenario.py +++ b/src/azure-cli/azure/cli/command_modules/cosmosdb/tests/latest/test_cosmosdb_backuprestore_scenario.py @@ -10,7 +10,7 @@ from azure.cli.testsdk import (ScenarioTest, ResourceGroupPreparer, live_only) from azure.cli.testsdk.scenario_tests import AllowLargeResponse -from datetime import datetime, timedelta +from datetime import UTC, datetime, timedelta from dateutil import parser @@ -530,6 +530,27 @@ def test_cosmosdb_xrr_single_region_account(self, resource_group): class CosmosDBRestoreUnitTests(unittest.TestCase): + def test_restore_timestamp_is_utc(self): + from azure.cli.command_modules.cosmosdb.custom import _convert_to_utc_timestamp + + expected = datetime(2020, 1, 1, tzinfo=UTC) + for timestamp in ['2020-01-01T00:00:00', '2020-01-01T00:00:00Z', + '2020-01-01T02:00:00+02:00', '2019-12-31T19:00:00-05:00']: + with self.subTest(timestamp=timestamp): + self.assertEqual(_convert_to_utc_timestamp(timestamp), expected) + + @mock.patch('azure.cli.command_modules.cosmosdb.custom._create_database_account') + @mock.patch('azure.cli.command_modules.cosmosdb._client_factory.cf_restorable_database_accounts') + def test_restore_accepts_timestamp_without_offset(self, get_accounts, create_account): + from azure.cli.command_modules.cosmosdb.custom import cli_cosmosdb_restore + + account = mock.Mock(account_name='source', deletion_time=None, + creation_time=datetime(2000, 1, 1, tzinfo=UTC)) + get_accounts.return_value.list.return_value = [account] + cli_cosmosdb_restore(mock.Mock(), mock.Mock(), 'rg', 'source', 'target', + '2020-01-01T00:00:00', 'westus') + self.assertEqual(create_account.call_args.kwargs['restore_timestamp'], '2020-01-01T00:00:00+00:00') + def setUp(self): # Only mock dependencies that cannot actually be imported. Never replace a # real, importable package in sys.modules: these unit tests share a diff --git a/src/azure-cli/azure/cli/command_modules/cosmosdb/tests/latest/test_cosmosdb_commands.py b/src/azure-cli/azure/cli/command_modules/cosmosdb/tests/latest/test_cosmosdb_commands.py index 5762e48bf99..ed6be07b4dc 100644 --- a/src/azure-cli/azure/cli/command_modules/cosmosdb/tests/latest/test_cosmosdb_commands.py +++ b/src/azure-cli/azure/cli/command_modules/cosmosdb/tests/latest/test_cosmosdb_commands.py @@ -10,7 +10,7 @@ from azure.cli.testsdk import JMESPathCheck, ScenarioTest, ResourceGroupPreparer, KeyVaultPreparer, record_only from knack.util import CLIError from azure.cli.testsdk.scenario_tests import AllowLargeResponse -from datetime import datetime, timedelta, timezone +from datetime import UTC, datetime, timedelta, timezone from dateutil import parser class CosmosDBTests(ScenarioTest): @@ -2934,7 +2934,7 @@ def test_cosmosdb_sql_normal_database_restore(self, resource_group): database_list = self.cmd('az cosmosdb sql database list -g {rg} -a {acc}').get_output_in_json() assert len(database_list) == 1 - restore_ts_string = datetime.utcnow().isoformat() + restore_ts_string = datetime.now(UTC).isoformat() self.kwargs.update({ 'rts': restore_ts_string @@ -3019,7 +3019,7 @@ def test_cosmosdb_sql_shared_database_restore(self, resource_group): container_list = self.cmd('az cosmosdb sql container list -g {rg} -a {acc} -d {db_name}').get_output_in_json() assert len(container_list) == 1 - restore_ts_string = datetime.utcnow().isoformat() + restore_ts_string = datetime.now(UTC).isoformat() self.kwargs.update({ 'rts': restore_ts_string, @@ -3125,7 +3125,7 @@ def test_cosmosdb_sql_normal_database_prov_container_restore(self, resource_grou container_list = self.cmd('az cosmosdb sql container list -g {rg} -a {acc} -d {db_name}').get_output_in_json() assert len(container_list) == 1 - restore_ts_string = datetime.utcnow().isoformat() + restore_ts_string = datetime.now(UTC).isoformat() self.kwargs.update({ 'rts': restore_ts_string @@ -3209,7 +3209,7 @@ def test_cosmosdb_sql_normal_database_prov_container_restore_nt(self, resource_g container_list = self.cmd('az cosmosdb sql container list -g {rg} -a {acc} -d {db_name}').get_output_in_json() assert len(container_list) == 1 - restore_ts_string = datetime.utcnow().isoformat() + restore_ts_string = datetime.now(UTC).isoformat() self.kwargs.update({ 'rts': restore_ts_string @@ -3301,7 +3301,7 @@ def test_cosmosdb_sql_shared_database_prov_container_restore(self, resource_grou container_list = self.cmd('az cosmosdb sql container list -g {rg} -a {acc} -d {db_name}').get_output_in_json() assert len(container_list) == 1 - restore_ts_string = datetime.utcnow().isoformat() + restore_ts_string = datetime.now(UTC).isoformat() self.kwargs.update({ 'rts': restore_ts_string @@ -3381,7 +3381,7 @@ def test_cosmosdb_mongodb_normal_database_prov_collection_restore(self, resource assert self.cmd('az cosmosdb mongodb collection exists -g {rg} -a {acc} -d {db_name} -n {col_name}').get_output_in_json() - restore_ts_string = datetime.utcnow().isoformat() + restore_ts_string = datetime.now(UTC).isoformat() self.kwargs.update({ 'rts': restore_ts_string @@ -3508,7 +3508,7 @@ def test_cosmosdb_mongodb_shared_database_prov_collection_restore(self, resource assert self.cmd('az cosmosdb mongodb collection exists -g {rg} -a {acc} -d {db_name} -n {col_name}').get_output_in_json() - restore_ts_string = datetime.utcnow().isoformat() + restore_ts_string = datetime.now(UTC).isoformat() self.kwargs.update({ 'rts': restore_ts_string @@ -3595,7 +3595,7 @@ def test_cosmosdb_gremlin_database_graph_restore(self, resource_group): ]).get_output_in_json() #restore time - restore_ts_string = datetime.utcnow().isoformat() + restore_ts_string = datetime.now(UTC).isoformat() self.kwargs.update({ 'rts': restore_ts_string }) @@ -3729,7 +3729,7 @@ def test_cosmosdb_table(self, resource_group): assert self.cmd('az cosmosdb table exists -g {rg} -a {acc} -n {table_name}').get_output_in_json() - restore_ts_string = datetime.utcnow().isoformat() + restore_ts_string = datetime.now(UTC).isoformat() self.kwargs.update({ 'rts': restore_ts_string diff --git a/src/azure-cli/azure/cli/command_modules/databoxedge/tests/__init__.py b/src/azure-cli/azure/cli/command_modules/databoxedge/tests/__init__.py index ba281d0650b..73e0338f818 100644 --- a/src/azure-cli/azure/cli/command_modules/databoxedge/tests/__init__.py +++ b/src/azure-cli/azure/cli/command_modules/databoxedge/tests/__init__.py @@ -61,14 +61,14 @@ def wrapper(*args, **kwargs): test_map[func.__name__]["error_message"] = "" test_map[func.__name__]["error_stack"] = "" test_map[func.__name__]["error_normalized"] = "" - test_map[func.__name__]["start_dt"] = dt.datetime.utcnow() + test_map[func.__name__]["start_dt"] = dt.datetime.now(dt.UTC) ret = func_to_call(*args, **kwargs) except (AssertionError, AzureError, CliTestError, CliExecutionError, SystemExit, JMESPathCheckAssertionError) as e: use_exception_cache = os.getenv("TEST_EXCEPTION_CACHE") if use_exception_cache is None or use_exception_cache.lower() != "true": raise - test_map[func.__name__]["end_dt"] = dt.datetime.utcnow() + test_map[func.__name__]["end_dt"] = dt.datetime.now(dt.UTC) test_map[func.__name__]["result"] = FAILED test_map[func.__name__]["error_message"] = str(e).replace("\r\n", " ").replace("\n", " ")[:500] test_map[func.__name__]["error_stack"] = traceback.format_exc().replace( @@ -80,7 +80,7 @@ def wrapper(*args, **kwargs): logger.info(traceback.format_exc()) exceptions.append((func.__name__, sys.exc_info())) else: - test_map[func.__name__]["end_dt"] = dt.datetime.utcnow() + test_map[func.__name__]["end_dt"] = dt.datetime.now(dt.UTC) return ret if inspect.isclass(func): diff --git a/src/azure-cli/azure/cli/command_modules/eventgrid/tests/latest/test_eventgrid_commands.py b/src/azure-cli/azure/cli/command_modules/eventgrid/tests/latest/test_eventgrid_commands.py index bd1fb3930df..ba2a2009000 100644 --- a/src/azure-cli/azure/cli/command_modules/eventgrid/tests/latest/test_eventgrid_commands.py +++ b/src/azure-cli/azure/cli/command_modules/eventgrid/tests/latest/test_eventgrid_commands.py @@ -10,7 +10,7 @@ from azure.cli.testsdk import ScenarioTest, ResourceGroupPreparer, StorageAccountPreparer from knack.util import CLIError -from datetime import datetime, timedelta +from datetime import UTC, datetime, timedelta EVENTGRID_TRIGGER_ENDPOINT_URL = "https://devexpfuncappdestination.azurewebsites.net/runtime/webhooks/EventGrid?functionName=EventGridTrigger1&code=" EVENTGRID_TRIGGER_ENDPOINT_BASE_URL = "https://devexpfuncappdestination.azurewebsites.net/runtime/webhooks/EventGrid" @@ -1480,9 +1480,9 @@ def test_Partner_scenarios(self, resource_group): guid3 = uuid.uuid1() destination_subscription_id = '5b4b650e-28b9-4790-b3ab-ddbd88d727c4' source = self.create_random_name(prefix='cli', length=40) - exp_time = datetime.utcnow() + timedelta(hours=24) + exp_time = datetime.now(UTC) + timedelta(hours=24) exp_time = exp_time.isoformat() - creation_time = datetime.utcnow().isoformat() + creation_time = datetime.now(UTC).isoformat() long_description = 'This is long description sample' customer_service_number1 = '+1 800 123 4567' @@ -1865,9 +1865,9 @@ def test_Partner_scenarios_v2(self, resource_group): destination_endpoint_url = SECURED_WEBBOOK_DESTINATION_ENDPOINT_URL source = self.create_random_name(prefix='cli', length=40) - exp_time = datetime.utcnow() + timedelta(hours=24) + exp_time = datetime.now(UTC) + timedelta(hours=24) exp_time = exp_time.isoformat() - creation_time = datetime.utcnow().isoformat() + creation_time = datetime.now(UTC).isoformat() default_maximum_expiration_time_in_days = 5 updated_default_max_expiration_time_in_days = 15 diff --git a/src/azure-cli/azure/cli/command_modules/feedback/custom.py b/src/azure-cli/azure/cli/command_modules/feedback/custom.py index a86a19ffa3f..21fc5227bae 100644 --- a/src/azure-cli/azure/cli/command_modules/feedback/custom.py +++ b/src/azure-cli/azure/cli/command_modules/feedback/custom.py @@ -605,8 +605,8 @@ def handle_survey(cmd): with open(GLOBAL_SURVEY_NOTE_PATH, 'r') as f: survey_note = json.load(f) if survey_note['last_prompt_time']: - last_prompt_time = datetime.datetime.strptime(survey_note['last_prompt_time'], '%Y-%m-%dT%H:%M:%S') - use_duration = datetime.datetime.utcnow() - last_prompt_time + last_prompt_time = datetime.datetime.strptime(survey_note['last_prompt_time'], '%Y-%m-%dT%H:%M:%S').replace(tzinfo=datetime.UTC) + use_duration = datetime.datetime.now(datetime.UTC) - last_prompt_time url = _SURVEY_URL.format(installation_id=Profile(cli_ctx=cmd.cli_ctx).get_installation_id(), version=core_version, diff --git a/src/azure-cli/azure/cli/command_modules/feedback/tests/latest/test_feedback.py b/src/azure-cli/azure/cli/command_modules/feedback/tests/latest/test_feedback.py index 89ecd734a9f..986c3e5a889 100644 --- a/src/azure-cli/azure/cli/command_modules/feedback/tests/latest/test_feedback.py +++ b/src/azure-cli/azure/cli/command_modules/feedback/tests/latest/test_feedback.py @@ -207,5 +207,21 @@ def _handle_exception_with_log(ex, *args, **kwargs): return result +class TestSurvey(unittest.TestCase): + def test_existing_utc_note(self): + from azure.cli.command_modules.feedback import custom + + note = '{"last_prompt_time": "2000-01-01T00:00:00"}' + with mock.patch.object(custom.os.path, 'isfile', return_value=True), \ + mock.patch('azure.cli.command_modules.feedback.custom.open', mock.mock_open(read_data=note)), \ + mock.patch('azure.cli.core._profile.Profile') as profile, \ + mock.patch.object(custom, 'can_launch_browser', return_value=False), \ + mock.patch('builtins.print') as output: + profile.return_value.get_installation_id.return_value = 'test-installation' + custom.handle_survey(mock.Mock()) + self.assertIn('test-installation', output.call_args.args[0]) + self.assertNotIn('d=-1', output.call_args.args[0]) + + if __name__ == '__main__': unittest.main() diff --git a/src/azure-cli/azure/cli/command_modules/iot/_utils.py b/src/azure-cli/azure/cli/command_modules/iot/_utils.py index 59c767c7396..9799c91f697 100644 --- a/src/azure-cli/azure/cli/command_modules/iot/_utils.py +++ b/src/azure-cli/azure/cli/command_modules/iot/_utils.py @@ -31,9 +31,9 @@ def create_self_signed_certificate(device_id, valid_days, cert_output_dir): .issuer_name(subject_name) .public_key(key.public_key()) .serial_number(x509.random_serial_number()) - .not_valid_before(datetime.datetime.utcnow()) + .not_valid_before(datetime.datetime.now(datetime.UTC)) .not_valid_after( - datetime.datetime.utcnow() + datetime.timedelta(days=valid_days) + datetime.datetime.now(datetime.UTC) + datetime.timedelta(days=valid_days) ) .sign(key, hashes.SHA256()) ) diff --git a/src/azure-cli/azure/cli/command_modules/iot/tests/latest/_test_utils.py b/src/azure-cli/azure/cli/command_modules/iot/tests/latest/_test_utils.py index e3c74c77af6..917401f1943 100644 --- a/src/azure-cli/azure/cli/command_modules/iot/tests/latest/_test_utils.py +++ b/src/azure-cli/azure/cli/command_modules/iot/tests/latest/_test_utils.py @@ -29,9 +29,9 @@ def _create_test_cert(cert_file, key_file, subject, valid_days, serial_number): .issuer_name(subject_name) .public_key(key.public_key()) .serial_number(serial_number) - .not_valid_before(datetime.datetime.utcnow()) + .not_valid_before(datetime.datetime.now(datetime.UTC)) .not_valid_after( - datetime.datetime.utcnow() + datetime.timedelta(days=valid_days) + datetime.datetime.now(datetime.UTC) + datetime.timedelta(days=valid_days) ) .sign(key, hashes.SHA256()) ) @@ -92,9 +92,9 @@ def _create_verification_cert(cert_file, key_file, verification_file, nonce, val .issuer_name(signing_cert.subject) .public_key(key.public_key()) .serial_number(serial_number) - .not_valid_before(datetime.datetime.utcnow()) + .not_valid_before(datetime.datetime.now(datetime.UTC)) .not_valid_after( - datetime.datetime.utcnow() + datetime.timedelta(days=valid_days) + datetime.datetime.now(datetime.UTC) + datetime.timedelta(days=valid_days) ) .sign(k, hashes.SHA256()) ) diff --git a/src/azure-cli/azure/cli/command_modules/keyvault/tests/latest/test_keyvault_commands.py b/src/azure-cli/azure/cli/command_modules/keyvault/tests/latest/test_keyvault_commands.py index c96ebf92ef5..8406e98afd6 100644 --- a/src/azure-cli/azure/cli/command_modules/keyvault/tests/latest/test_keyvault_commands.py +++ b/src/azure-cli/azure/cli/command_modules/keyvault/tests/latest/test_keyvault_commands.py @@ -11,7 +11,7 @@ import time from unittest import mock import unittest -from datetime import datetime, timedelta +from datetime import UTC, datetime, timedelta from dateutil import tz from ipaddress import ip_network @@ -975,8 +975,8 @@ def test_keyvault_hsm_selective_key_restore(self): 'key_name': self.create_random_name('selective-restore-', 24), 'storage_account': self.create_random_name('clitesthsmsa', 24), 'blob': self.create_random_name('clitesthsmblob', 24), - 'sas_start': (datetime.utcnow() - timedelta(minutes=30)).strftime('%Y-%m-%dT%H:%M:%SZ'), - 'sas_expiry': (datetime.utcnow() + timedelta(minutes=30)).strftime('%Y-%m-%dT%H:%M:%SZ') + 'sas_start': (datetime.now(UTC) - timedelta(minutes=30)).strftime('%Y-%m-%dT%H:%M:%SZ'), + 'sas_expiry': (datetime.now(UTC) + timedelta(minutes=30)).strftime('%Y-%m-%dT%H:%M:%SZ') }) _clear_hsm(self, hsm_url=self.kwargs['hsm_url']) @@ -1031,8 +1031,8 @@ def test_keyvault_hsm_full_backup_restore(self, resource_group, managed_hsm, sto 'hsm_name': managed_hsm, 'storage_account': storage_account, 'blob': self.create_random_name('clitesthsmblob', 24), - 'sas_start': (datetime.utcnow() - timedelta(minutes=30)).strftime('%Y-%m-%dT%H:%M:%SZ'), - 'sas_expiry': (datetime.utcnow() + timedelta(minutes=30)).strftime('%Y-%m-%dT%H:%M:%SZ') + 'sas_start': (datetime.now(UTC) - timedelta(minutes=30)).strftime('%Y-%m-%dT%H:%M:%SZ'), + 'sas_expiry': (datetime.now(UTC) + timedelta(minutes=30)).strftime('%Y-%m-%dT%H:%M:%SZ') }) self.cmd('az storage container create -n {blob} --account-name {storage_account} -g {rg}') @@ -2662,8 +2662,8 @@ def _generate_certificate(path, keyfile=None, password=None): .issuer_name(issuer) \ .public_key(key.public_key()) \ .serial_number(x509.random_serial_number()) \ - .not_valid_before(datetime.utcnow()) \ - .not_valid_after(datetime.utcnow() + timedelta(days=10)) \ + .not_valid_before(datetime.now(UTC)) \ + .not_valid_after(datetime.now(UTC) + timedelta(days=10)) \ .sign(key, hashes.SHA256(), default_backend()) # Write the cert out to disk diff --git a/src/azure-cli/azure/cli/command_modules/lab/tests/latest/test_lab_validators.py b/src/azure-cli/azure/cli/command_modules/lab/tests/latest/test_lab_validators.py index b624a177420..343c1524787 100644 --- a/src/azure-cli/azure/cli/command_modules/lab/tests/latest/test_lab_validators.py +++ b/src/azure-cli/azure/cli/command_modules/lab/tests/latest/test_lab_validators.py @@ -4,9 +4,10 @@ # -------------------------------------------------------------------------------------------- import unittest +from unittest import mock from azure.mgmt.core.tools import is_valid_resource_id from azure.cli.core.azclierror import ArgumentUsageError -from azure.cli.command_modules.lab.validators import _update_artifacts +from azure.cli.command_modules.lab.validators import _update_artifacts, _validate_expiration_date class ValidatorsCommandTest(unittest.TestCase): @@ -65,3 +66,13 @@ def test_update_artifacts(self): del invalid_artifact['artifact_id'] with self.assertRaises(ArgumentUsageError): _update_artifacts([invalid_artifact], self.lab_resource_id) + + def test_expiration_date_with_and_without_timezone(self): + for suffix in ['', 'Z', '+02:00', '-05:00']: + with self.subTest(suffix=suffix): + args = mock.Mock() + args.expiration_date.to_serialized_data.return_value = '2999-01-01T00:00:00' + suffix + _validate_expiration_date(args) + args.expiration_date.to_serialized_data.return_value = '2000-01-01T00:00:00' + suffix + with self.assertRaises(ArgumentUsageError): + _validate_expiration_date(args) diff --git a/src/azure-cli/azure/cli/command_modules/lab/validators.py b/src/azure-cli/azure/cli/command_modules/lab/validators.py index 6cf9bcbc450..77ef72a7536 100644 --- a/src/azure-cli/azure/cli/command_modules/lab/validators.py +++ b/src/azure-cli/azure/cli/command_modules/lab/validators.py @@ -115,7 +115,9 @@ def _validate_expiration_date(args): if has_value(args.expiration_date): import datetime import dateutil.parser - if datetime.datetime.utcnow() >= dateutil.parser.parse(args.expiration_date.to_serialized_data()): + expiration_date = dateutil.parser.parse(args.expiration_date.to_serialized_data()) + expiration_date = expiration_date.replace(tzinfo=expiration_date.tzinfo or datetime.UTC) + if datetime.datetime.now(datetime.UTC) >= expiration_date: raise ArgumentUsageError( "Expiration date '{}' must be in future.".format(args.expiration_date.to_serialized_data())) diff --git a/src/azure-cli/azure/cli/command_modules/marketplaceordering/tests/__init__.py b/src/azure-cli/azure/cli/command_modules/marketplaceordering/tests/__init__.py index ba281d0650b..73e0338f818 100644 --- a/src/azure-cli/azure/cli/command_modules/marketplaceordering/tests/__init__.py +++ b/src/azure-cli/azure/cli/command_modules/marketplaceordering/tests/__init__.py @@ -61,14 +61,14 @@ def wrapper(*args, **kwargs): test_map[func.__name__]["error_message"] = "" test_map[func.__name__]["error_stack"] = "" test_map[func.__name__]["error_normalized"] = "" - test_map[func.__name__]["start_dt"] = dt.datetime.utcnow() + test_map[func.__name__]["start_dt"] = dt.datetime.now(dt.UTC) ret = func_to_call(*args, **kwargs) except (AssertionError, AzureError, CliTestError, CliExecutionError, SystemExit, JMESPathCheckAssertionError) as e: use_exception_cache = os.getenv("TEST_EXCEPTION_CACHE") if use_exception_cache is None or use_exception_cache.lower() != "true": raise - test_map[func.__name__]["end_dt"] = dt.datetime.utcnow() + test_map[func.__name__]["end_dt"] = dt.datetime.now(dt.UTC) test_map[func.__name__]["result"] = FAILED test_map[func.__name__]["error_message"] = str(e).replace("\r\n", " ").replace("\n", " ")[:500] test_map[func.__name__]["error_stack"] = traceback.format_exc().replace( @@ -80,7 +80,7 @@ def wrapper(*args, **kwargs): logger.info(traceback.format_exc()) exceptions.append((func.__name__, sys.exc_info())) else: - test_map[func.__name__]["end_dt"] = dt.datetime.utcnow() + test_map[func.__name__]["end_dt"] = dt.datetime.now(dt.UTC) return ret if inspect.isclass(func): diff --git a/src/azure-cli/azure/cli/command_modules/monitor/_legacy/custom.py b/src/azure-cli/azure/cli/command_modules/monitor/_legacy/custom.py index 7c37796989c..8bbff592c6f 100644 --- a/src/azure-cli/azure/cli/command_modules/monitor/_legacy/custom.py +++ b/src/azure-cli/azure/cli/command_modules/monitor/_legacy/custom.py @@ -33,12 +33,12 @@ def list_activity_log(cmd, correlation_id=None, resource_group=None, resource_id def _build_activity_log_odata_filter(correlation_id=None, resource_group=None, resource_id=None, resource_provider=None, start_time=None, end_time=None, caller=None, status=None, offset=None): - from datetime import datetime + from datetime import UTC, datetime import dateutil.parser if not start_time and not end_time: # if neither value provided, end_time is now - end_time = datetime.utcnow().isoformat() + end_time = datetime.now(UTC).isoformat() if not start_time: # if no start_time, apply offset backwards from end_time start_time = (dateutil.parser.parse(end_time) - offset).isoformat() @@ -108,13 +108,13 @@ def list_metrics(cmd, resource, metadata=None, dimension=None, aggregation=None, metrics=None, filters=None, metric_namespace=None, orderby=None, top=10): - from datetime import datetime + from datetime import UTC, datetime import dateutil.parser from urllib.parse import quote_plus if not start_time and not end_time: # if neither value provided, end_time is now - end_time = datetime.utcnow().isoformat() + end_time = datetime.now(UTC).isoformat() if not start_time: # if no start_time, apply offset backwards from end_time start_time = (dateutil.parser.parse(end_time) - offset).isoformat() diff --git a/src/azure-cli/azure/cli/command_modules/monitor/custom.py b/src/azure-cli/azure/cli/command_modules/monitor/custom.py index e94e814d847..1d6bf1680e9 100644 --- a/src/azure-cli/azure/cli/command_modules/monitor/custom.py +++ b/src/azure-cli/azure/cli/command_modules/monitor/custom.py @@ -33,12 +33,12 @@ def list_activity_log(cmd, correlation_id=None, resource_group=None, resource_id def _build_activity_log_odata_filter(correlation_id=None, resource_group=None, resource_id=None, resource_provider=None, start_time=None, end_time=None, caller=None, status=None, offset=None): - from datetime import datetime + from datetime import UTC, datetime import dateutil.parser if not start_time and not end_time: # if neither value provided, end_time is now - end_time = datetime.utcnow().isoformat() + end_time = datetime.now(UTC).isoformat() if not start_time: # if no start_time, apply offset backwards from end_time start_time = (dateutil.parser.parse(end_time) - offset).isoformat() @@ -108,13 +108,13 @@ def list_metrics(cmd, resource, metadata=None, dimension=None, aggregation=None, metrics=None, filters=None, metric_namespace=None, orderby=None, top=10): - from datetime import datetime + from datetime import UTC, datetime import dateutil.parser from urllib.parse import quote_plus if not start_time and not end_time: # if neither value provided, end_time is now - end_time = datetime.utcnow().isoformat() + end_time = datetime.now(UTC).isoformat() if not start_time: # if no start_time, apply offset backwards from end_time start_time = (dateutil.parser.parse(end_time) - offset).isoformat() diff --git a/src/azure-cli/azure/cli/command_modules/mysql/_util.py b/src/azure-cli/azure/cli/command_modules/mysql/_util.py index 7e20ebf304b..064ef9df6a5 100644 --- a/src/azure-cli/azure/cli/command_modules/mysql/_util.py +++ b/src/azure-cli/azure/cli/command_modules/mysql/_util.py @@ -15,8 +15,7 @@ import sys import yaml from time import sleep -import datetime as dt -from datetime import datetime +from datetime import UTC, datetime from knack.log import get_logger from knack.arguments import ignore_type from knack.prompting import prompt_pass, prompt_y_n, NoTTYException @@ -326,7 +325,7 @@ def _map_maintenance_window(day_of_week): def get_current_time(): - return datetime.utcnow().replace(tzinfo=dt.timezone.utc, microsecond=0).isoformat() + return datetime.now(UTC).replace(microsecond=0).isoformat() def get_id_components(rid): @@ -616,7 +615,7 @@ def get_firewall_rules_from_paged_response(firewall_rules): def get_current_utc_time(): - return datetime.utcnow().replace(tzinfo=dt.timezone.utc) + return datetime.now(UTC) class ImportFromStorageState(Enum): diff --git a/src/azure-cli/azure/cli/command_modules/mysql/custom.py b/src/azure-cli/azure/cli/command_modules/mysql/custom.py index 901a00ead35..7ac6286c76c 100644 --- a/src/azure-cli/azure/cli/command_modules/mysql/custom.py +++ b/src/azure-cli/azure/cli/command_modules/mysql/custom.py @@ -8,8 +8,7 @@ import re import math -from datetime import datetime, timedelta -from dateutil.tz import tzutc +from datetime import UTC, datetime, timedelta from knack.log import get_logger from urllib.request import urlretrieve from importlib import import_module @@ -273,7 +272,7 @@ def flexible_server_log_list(client, resource_group_name, server_name, filename_ if file_last_written is None: file_last_written = 72 - time_line = datetime.utcnow().replace(tzinfo=tzutc()) - timedelta(hours=file_last_written) + time_line = datetime.now(UTC) - timedelta(hours=file_last_written) for f in all_files: if f.last_modified_time < time_line: diff --git a/src/azure-cli/azure/cli/command_modules/mysql/tests/latest/test_mysql_scenario.py b/src/azure-cli/azure/cli/command_modules/mysql/tests/latest/test_mysql_scenario.py index e4338270c43..369936f086d 100644 --- a/src/azure-cli/azure/cli/command_modules/mysql/tests/latest/test_mysql_scenario.py +++ b/src/azure-cli/azure/cli/command_modules/mysql/tests/latest/test_mysql_scenario.py @@ -7,10 +7,9 @@ import uuid import unittest -from datetime import datetime, timedelta +from datetime import UTC, datetime, timedelta from time import sleep from dateutil import parser -from dateutil.tz import tzutc from azure.cli.testsdk.scenario_tests import AllowLargeResponse from azure.cli.testsdk.base import execute from azure.cli.testsdk.scenario_tests.const import ENV_LIVE_TEST @@ -458,7 +457,7 @@ def _test_flexible_server_restore_mgmt(self, database_engine, resource_group): result = self.cmd('{} flexible-server show -g {} -n {}'.format(database_engine, resource_group, source_server)).get_output_in_json() # Wait until snapshot is created - current_time = datetime.utcnow().replace(tzinfo=tzutc()).isoformat() + current_time = datetime.now(UTC).isoformat() earliest_restore_time = result['backup']['earliestRestoreDate'] seconds_to_wait = (parser.isoparse(earliest_restore_time) - parser.isoparse(current_time)).total_seconds() os.environ.get(ENV_LIVE_TEST, False) and sleep(max(0, seconds_to_wait) + 180) @@ -690,7 +689,7 @@ def _test_flexible_server_georestore_update_mgmt(self, database_engine, resource .format(database_engine, resource_group, source_server), checks=[JMESPathCheck('backup.geoRedundantBackup', 'Enabled')]).get_output_in_json() - current_time = datetime.utcnow().replace(tzinfo=tzutc()).isoformat() + current_time = datetime.now(UTC).isoformat() earliest_restore_time = result['backup']['earliestRestoreDate'] seconds_to_wait = (parser.isoparse(earliest_restore_time) - parser.isoparse(current_time)).total_seconds() os.environ.get(ENV_LIVE_TEST, False) and sleep(max(0, seconds_to_wait) + 1200) @@ -936,7 +935,7 @@ def main_tests(geo_redundant_backup): expect_failure=True) # restore backup - current_time = datetime.utcnow().replace(tzinfo=tzutc()).isoformat() + current_time = datetime.now(UTC).isoformat() earliest_restore_time = result['backup']['earliestRestoreDate'] seconds_to_wait = (parser.isoparse(earliest_restore_time) - parser.isoparse(current_time)).total_seconds() sleep(max(0, seconds_to_wait)) @@ -1519,7 +1518,7 @@ def _test_mysql_flexible_server_public_access_georestore(self, database_engine, restore_server = self.cmd('resource show --id {} --api-version {}'.format(result['id'], api_version)).get_output_in_json() - current_time = datetime.utcnow().replace(tzinfo=tzutc()).isoformat() + current_time = datetime.now(UTC).isoformat() earliest_restore_time = restore_server['properties']['backup']['earliestRestoreDate'] seconds_to_wait = (parser.isoparse(earliest_restore_time) - parser.isoparse(current_time)).total_seconds() time.sleep(max(0, seconds_to_wait) + 180) @@ -1542,7 +1541,7 @@ def _test_mysql_flexible_server_public_access_restore(self, database_engine, res restore_server = self.cmd('resource show --id {} --api-version {}'.format(result['id'], api_version)).get_output_in_json() - current_time = datetime.utcnow().replace(tzinfo=tzutc()).isoformat() + current_time = datetime.now(UTC).isoformat() earliest_restore_time = restore_server['properties']['backup']['earliestRestoreDate'] seconds_to_wait = (parser.isoparse(earliest_restore_time) - parser.isoparse(current_time)).total_seconds() print(current_time) @@ -2408,7 +2407,7 @@ def _test_flexible_server_export_create_mgmt(self, database_engine, resource_gro # Create the blob container container = self.create_container(target_account_info) - expiry = (datetime.utcnow() + timedelta(hours=1)).strftime('%Y-%m-%dT%H:%MZ') + expiry = (datetime.now(UTC) + timedelta(hours=1)).strftime('%Y-%m-%dT%H:%MZ') self.kwargs.update({ 'expiry': expiry, diff --git a/src/azure-cli/azure/cli/command_modules/netappfiles/tests/latest/test_bucket_commands.py b/src/azure-cli/azure/cli/command_modules/netappfiles/tests/latest/test_bucket_commands.py index 6489a740e7d..c9816fa2737 100644 --- a/src/azure-cli/azure/cli/command_modules/netappfiles/tests/latest/test_bucket_commands.py +++ b/src/azure-cli/azure/cli/command_modules/netappfiles/tests/latest/test_bucket_commands.py @@ -39,8 +39,8 @@ def _generate_self_signed_cert(fqdn): .issuer_name(issuer) .public_key(key.public_key()) .serial_number(x509.random_serial_number()) - .not_valid_before(datetime.datetime.utcnow()) - .not_valid_after(datetime.datetime.utcnow() + datetime.timedelta(days=365)) + .not_valid_before(datetime.datetime.now(datetime.UTC)) + .not_valid_after(datetime.datetime.now(datetime.UTC) + datetime.timedelta(days=365)) .sign(key, hashes.SHA256()) ) pem_data = cert.public_bytes(serialization.Encoding.PEM) + key.private_bytes( diff --git a/src/azure-cli/azure/cli/command_modules/network/tests/latest/test_network_commands.py b/src/azure-cli/azure/cli/command_modules/network/tests/latest/test_network_commands.py index c12e4b8d5a9..fdd94694d76 100644 --- a/src/azure-cli/azure/cli/command_modules/network/tests/latest/test_network_commands.py +++ b/src/azure-cli/azure/cli/command_modules/network/tests/latest/test_network_commands.py @@ -6596,11 +6596,7 @@ class VpnConnectionAuthenticationScenarioTest(ScenarioTest): @staticmethod def _get_now_utc(): import datetime - try: - from datetime import timezone - return datetime.datetime.now(timezone.utc) - except ImportError: - return datetime.datetime.utcnow() + return datetime.datetime.now(datetime.UTC) @staticmethod def _format_subject_dn(common_name: str): @@ -7715,7 +7711,7 @@ def __init__(self, method_name): @ResourceGroupPreparer(name_prefix='cli_test_active_active_vnet_vnet_connection') @StorageAccountPreparer(name_prefix='clitestvpncnn') def test_network_active_active_vnet_connection(self, resource_group, storage_account): - from datetime import datetime, timedelta + from datetime import UTC, datetime, timedelta self.kwargs.update({ 'subnet': 'GatewaySubnet', 'vnet1': 'vnet1', @@ -7739,7 +7735,7 @@ def test_network_active_active_vnet_connection(self, resource_group, storage_acc 'bgp_peer2': '10.53.255.253', 'storage_account': storage_account, 'ctn': self.create_random_name(prefix='clitestvpngw', length=24), - 'expiry': (datetime.utcnow() + timedelta(hours=3)).strftime('%Y-%m-%dT%H:%MZ') + 'expiry': (datetime.now(UTC) + timedelta(hours=3)).strftime('%Y-%m-%dT%H:%MZ') }) self.cmd('storage container create --account-name {storage_account} --name {ctn}') @@ -7993,7 +7989,7 @@ def test_network_vpn_gateway_disconnect_connects(self, resource_group): @ResourceGroupPreparer(name_prefix='cli_test_vpn_gateway_package_capture', location='westus') @StorageAccountPreparer(name_prefix='clitestvpngw') def test_network_vpn_gateway_package_capture(self, resource_group, storage_account): - from datetime import datetime, timedelta + from datetime import UTC, datetime, timedelta self.kwargs.update({ 'vnet1': 'myvnet1', 'gw1': 'gateway1', @@ -8001,7 +7997,7 @@ def test_network_vpn_gateway_package_capture(self, resource_group, storage_accou 'ip1': 'pubip1', 'storage_account': storage_account, 'ctn': self.create_random_name(prefix='clitestvpngw', length=24), - 'expiry': (datetime.utcnow() + timedelta(hours=3)).strftime('%Y-%m-%dT%H:%MZ') + 'expiry': (datetime.now(UTC) + timedelta(hours=3)).strftime('%Y-%m-%dT%H:%MZ') }) self.cmd('storage container create --account-name {storage_account} --name {ctn}') @@ -8037,12 +8033,7 @@ def _generate_p2s_root_cert_public_file(cert_path, common_name="VpnGatewayAuthCe from cryptography.hazmat.backends import default_backend import datetime - # timezone-aware now - try: - from datetime import timezone - now = datetime.datetime.now(timezone.utc) - except ImportError: - now = datetime.datetime.utcnow() + now = datetime.datetime.now(datetime.UTC) # Root key (private key not exported; we only write public cert) root_key = rsa.generate_private_key( diff --git a/src/azure-cli/azure/cli/command_modules/postgresql/commands/backup_commands.py b/src/azure-cli/azure/cli/command_modules/postgresql/commands/backup_commands.py index a2416c33e39..4aef2082653 100644 --- a/src/azure-cli/azure/cli/command_modules/postgresql/commands/backup_commands.py +++ b/src/azure-cli/azure/cli/command_modules/postgresql/commands/backup_commands.py @@ -4,7 +4,7 @@ # -------------------------------------------------------------------------------------------- # pylint: disable=unused-argument, line-too-long -from datetime import datetime +from datetime import UTC, datetime from azure.cli.core.util import user_confirmation from knack.log import get_logger from ..utils.validators import validate_resource_group, validate_backup_name @@ -20,7 +20,7 @@ def _generate_backup_name(client, resource_group_name, server_name): on_demand_count = sum(1 for name in existing_names if name.startswith(_BACKUP_NAME_PREFIX)) - date_str = datetime.utcnow().strftime("%m%d%Y") + date_str = datetime.now(UTC).strftime("%m%d%Y") suffix = on_demand_count + 1 backup_name = f"{_BACKUP_NAME_PREFIX}-{date_str}-{suffix}" while backup_name in existing_names: diff --git a/src/azure-cli/azure/cli/command_modules/postgresql/commands/server_logs_commands.py b/src/azure-cli/azure/cli/command_modules/postgresql/commands/server_logs_commands.py index c690751be77..d90b96817a5 100644 --- a/src/azure-cli/azure/cli/command_modules/postgresql/commands/server_logs_commands.py +++ b/src/azure-cli/azure/cli/command_modules/postgresql/commands/server_logs_commands.py @@ -7,8 +7,7 @@ import re from azure.cli.core.util import CLIError, user_confirmation -from datetime import datetime, timedelta -from dateutil.tz import tzutc # pylint: disable=import-error +from datetime import UTC, datetime, timedelta from knack.log import get_logger from urllib.request import urlretrieve from .._client_factory import cf_postgres_flexible_replica @@ -62,7 +61,7 @@ def flexible_server_list_log_files_with_filter(client, resource_group_name, serv if file_last_written is None: file_last_written = 72 - time_line = datetime.utcnow().replace(tzinfo=tzutc()) - timedelta(hours=file_last_written) + time_line = datetime.now(UTC) - timedelta(hours=file_last_written) for f in all_files: if f.last_modified_time < time_line: @@ -86,7 +85,7 @@ def flexible_server_log_list(client, resource_group_name, server_name, filename_ if file_last_written is None: file_last_written = 72 - time_line = datetime.utcnow().replace(tzinfo=tzutc()) - timedelta(hours=file_last_written) + time_line = datetime.now(UTC) - timedelta(hours=file_last_written) for f in all_files: if f.last_modified_time < time_line: diff --git a/src/azure-cli/azure/cli/command_modules/postgresql/utils/_flexible_server_util.py b/src/azure-cli/azure/cli/command_modules/postgresql/utils/_flexible_server_util.py index 9957faeb0e2..198d655fbb3 100644 --- a/src/azure-cli/azure/cli/command_modules/postgresql/utils/_flexible_server_util.py +++ b/src/azure-cli/azure/cli/command_modules/postgresql/utils/_flexible_server_util.py @@ -4,8 +4,7 @@ # -------------------------------------------------------------------------------------------- # pylint: disable=unused-argument, line-too-long, import-outside-toplevel, raise-missing-from -import datetime as dt -from datetime import datetime +from datetime import UTC, datetime import os import random import subprocess @@ -201,7 +200,7 @@ def _map_maintenance_window(day_of_week): def get_current_time(): - return datetime.utcnow().replace(tzinfo=dt.timezone.utc, microsecond=0).isoformat() + return datetime.now(UTC).replace(microsecond=0).isoformat() def get_id_components(rid): diff --git a/src/azure-cli/azure/cli/command_modules/rdbms/_flexible_server_util.py b/src/azure-cli/azure/cli/command_modules/rdbms/_flexible_server_util.py index c870582db9c..6bc966762b5 100644 --- a/src/azure-cli/azure/cli/command_modules/rdbms/_flexible_server_util.py +++ b/src/azure-cli/azure/cli/command_modules/rdbms/_flexible_server_util.py @@ -4,8 +4,7 @@ # -------------------------------------------------------------------------------------------- # pylint: disable=unused-argument, line-too-long, import-outside-toplevel, raise-missing-from -import datetime as dt -from datetime import datetime +from datetime import UTC, datetime import os import random import subprocess @@ -246,7 +245,7 @@ def _map_maintenance_window(day_of_week): def get_current_time(): - return datetime.utcnow().replace(tzinfo=dt.timezone.utc, microsecond=0).isoformat() + return datetime.now(UTC).replace(microsecond=0).isoformat() def get_id_components(rid): diff --git a/src/azure-cli/azure/cli/command_modules/rdbms/custom.py b/src/azure-cli/azure/cli/command_modules/rdbms/custom.py index 13d644df06f..3c787b8b91b 100644 --- a/src/azure-cli/azure/cli/command_modules/rdbms/custom.py +++ b/src/azure-cli/azure/cli/command_modules/rdbms/custom.py @@ -4,10 +4,9 @@ # -------------------------------------------------------------------------------------------- # pylint: disable=unused-argument, line-too-long -from datetime import datetime, timedelta +from datetime import UTC, datetime, timedelta from importlib import import_module import re -from dateutil.tz import tzutc # pylint: disable=import-error from knack.log import get_logger from knack.util import todict from urllib.request import urlretrieve @@ -603,7 +602,7 @@ def _list_log_files_with_filter(client, resource_group_name, server_name, filena if file_last_written is None: file_last_written = 72 - time_line = datetime.utcnow().replace(tzinfo=tzutc()) - timedelta(hours=file_last_written) + time_line = datetime.now(UTC) - timedelta(hours=file_last_written) for f in all_files: if f.last_modified_time < time_line: diff --git a/src/azure-cli/azure/cli/command_modules/rdbms/flexible_server_custom_common.py b/src/azure-cli/azure/cli/command_modules/rdbms/flexible_server_custom_common.py index 6974f6951ba..6aa639b21f4 100644 --- a/src/azure-cli/azure/cli/command_modules/rdbms/flexible_server_custom_common.py +++ b/src/azure-cli/azure/cli/command_modules/rdbms/flexible_server_custom_common.py @@ -6,8 +6,7 @@ # pylint: disable=unused-argument, line-too-long import re -from datetime import datetime, timedelta -from dateutil.tz import tzutc +from datetime import UTC, datetime, timedelta from knack.log import get_logger from knack.util import CLIError from urllib.request import urlretrieve @@ -228,7 +227,7 @@ def flexible_server_log_list(client, resource_group_name, server_name, filename_ if file_last_written is None: file_last_written = 72 - time_line = datetime.utcnow().replace(tzinfo=tzutc()) - timedelta(hours=file_last_written) + time_line = datetime.now(UTC) - timedelta(hours=file_last_written) for f in all_files: if f.last_modified_time < time_line: diff --git a/src/azure-cli/azure/cli/command_modules/rdbms/tests/latest/test_rdbms_commands.py b/src/azure-cli/azure/cli/command_modules/rdbms/tests/latest/test_rdbms_commands.py index 9150f66feec..a9883870a8d 100644 --- a/src/azure-cli/azure/cli/command_modules/rdbms/tests/latest/test_rdbms_commands.py +++ b/src/azure-cli/azure/cli/command_modules/rdbms/tests/latest/test_rdbms_commands.py @@ -4,8 +4,7 @@ # -------------------------------------------------------------------------------------------- import time -from datetime import datetime -from dateutil.tz import tzutc # pylint: disable=import-error +from datetime import UTC, datetime from azure.cli.testsdk.scenario_tests import AllowLargeResponse from azure.core.exceptions import HttpResponseError from azure.cli.core.util import CLIError @@ -223,7 +222,7 @@ def _test_server_mgmt(self, database_engine, resource_group_1, resource_group_2) JMESPathCheck('administratorLogin', admin_login)]) # Before we do restore we will have to check whether the current time is less than earliest restore time - current_time = datetime.utcnow().replace(tzinfo=tzutc()).isoformat() + current_time = datetime.now(UTC).isoformat() earliest_restore_time = result['earliestRestoreDate'] date_format = '%Y-%m-%dT%H:%M:%S.%f+00:00' diff --git a/src/azure-cli/azure/cli/command_modules/redis/tests/latest/test_redis_scenario.py b/src/azure-cli/azure/cli/command_modules/redis/tests/latest/test_redis_scenario.py index 9b6ecf1a569..9e174d43195 100644 --- a/src/azure-cli/azure/cli/command_modules/redis/tests/latest/test_redis_scenario.py +++ b/src/azure-cli/azure/cli/command_modules/redis/tests/latest/test_redis_scenario.py @@ -309,8 +309,8 @@ def test_redis_cache_export_import(self, resource_group): 'containerName': "testcontainer", 'userIdentity': user_identity, 'storageSubscriptionId': "6364f508-1150-4431-b973-c3f133466e56", # replace it with your subscription id while running tests in live mode - 'startTime': (datetime.datetime.utcnow() - datetime.timedelta(minutes=60)).strftime(f"%Y-%m-%dT%H:%MZ"), - 'expiryTime': (datetime.datetime.utcnow() + datetime.timedelta(minutes=200)).strftime(f"%Y-%m-%dT%H:%MZ") + 'startTime': (datetime.datetime.now(datetime.UTC) - datetime.timedelta(minutes=60)).strftime("%Y-%m-%dT%H:%MZ"), + 'expiryTime': (datetime.datetime.now(datetime.UTC) + datetime.timedelta(minutes=200)).strftime("%Y-%m-%dT%H:%MZ") } if self.is_live : diff --git a/src/azure-cli/azure/cli/command_modules/resource/tests/latest/test_resource.py b/src/azure-cli/azure/cli/command_modules/resource/tests/latest/test_resource.py index 3875c8c1501..2c947da715b 100644 --- a/src/azure-cli/azure/cli/command_modules/resource/tests/latest/test_resource.py +++ b/src/azure-cli/azure/cli/command_modules/resource/tests/latest/test_resource.py @@ -1331,8 +1331,8 @@ def test_resource_group_level_deployment_with_query_string(self, resource_group, self.cmd('storage blob upload -c {container_name} -f "{tf}" -n mainTemplate --account-name {storage_account} --account-key {storage_key}') self.cmd('storage blob upload -c {container_name} -f "{linked_tf}" -n storage_account_linked_template.json --account-name {storage_account} --account-key {storage_key}') - from datetime import datetime, timedelta - self.kwargs['expiry'] = (datetime.utcnow() + timedelta(hours=12)).strftime('%Y-%m-%dT%H:%MZ') + from datetime import UTC, datetime, timedelta + self.kwargs['expiry'] = (datetime.now(UTC) + timedelta(hours=12)).strftime('%Y-%m-%dT%H:%MZ') self.kwargs['sas_token'] = self.cmd( 'storage container generate-sas --account-name {storage_account} --account-key {storage_key} --name {container_name} --permissions rw --expiry {expiry} -otsv').output.strip() @@ -1379,8 +1379,8 @@ def test_subscription_level_deployment_with_query_string(self, resource_group, r self.cmd('storage blob upload -c {container_name} -f "{linked_tf1}" -n createKeyVault.json --account-name {storage_account} --account-key {storage_key}') self.cmd('storage blob upload -c {container_name} -f "{linked_tf2}" -n createKeyVaultWithSecret.json --account-name {storage_account} --account-key {storage_key}') - from datetime import datetime, timedelta - self.kwargs['expiry'] = (datetime.utcnow() + timedelta(hours=12)).strftime('%Y-%m-%dT%H:%MZ') + from datetime import UTC, datetime, timedelta + self.kwargs['expiry'] = (datetime.now(UTC) + timedelta(hours=12)).strftime('%Y-%m-%dT%H:%MZ') self.kwargs['sas_token'] = self.cmd( 'storage container generate-sas --account-name {storage_account} --name {container_name} --permissions dlrw --expiry {expiry} --https-only -otsv').output.strip() diff --git a/src/azure-cli/azure/cli/command_modules/role/custom.py b/src/azure-cli/azure/cli/command_modules/role/custom.py index 279ad0d219b..687bdd7c80d 100644 --- a/src/azure-cli/azure/cli/command_modules/role/custom.py +++ b/src/azure-cli/azure/cli/command_modules/role/custom.py @@ -489,15 +489,15 @@ def _get_assignment_events(cli_ctx, start_time=None, end_time=None): DATE_TIME_FORMAT = '%Y-%m-%dT%H:%M:%SZ' if end_time: try: - end_time = datetime.datetime.strptime(end_time, DATE_TIME_FORMAT) + end_time = datetime.datetime.strptime(end_time, DATE_TIME_FORMAT).replace(tzinfo=datetime.UTC) except ValueError: raise CLIError("Input '{}' is not valid datetime. Valid example: 2000-12-31T12:59:59Z".format(end_time)) else: - end_time = datetime.datetime.utcnow() + end_time = datetime.datetime.now(datetime.UTC) if start_time: try: - start_time = datetime.datetime.strptime(start_time, DATE_TIME_FORMAT) + start_time = datetime.datetime.strptime(start_time, DATE_TIME_FORMAT).replace(tzinfo=datetime.UTC) if start_time >= end_time: raise CLIError("Start time cannot be later than end time.") except ValueError: @@ -1493,7 +1493,7 @@ def create_service_principal_for_rbac( if not display_name: # No display name is provided, create a new one - app_display_name = 'azure-cli-' + datetime.datetime.utcnow().strftime('%Y-%m-%d-%H-%M-%S') + app_display_name = 'azure-cli-' + datetime.datetime.now(datetime.UTC).strftime('%Y-%m-%d-%H-%M-%S') else: app_display_name = display_name # patch existing app with the same displayName to make the command idempotent @@ -1980,7 +1980,7 @@ def _build_key_credentials(key_value=None, key_type=None, key_usage=None, return [] if not start_date: - start_date = datetime.datetime.utcnow() + start_date = datetime.datetime.now(datetime.UTC) elif isinstance(start_date, str): start_date = dateutil.parser.parse(start_date) diff --git a/src/azure-cli/azure/cli/command_modules/role/tests/latest/test_role.py b/src/azure-cli/azure/cli/command_modules/role/tests/latest/test_role.py index 165fffd8998..79f576dbf43 100644 --- a/src/azure-cli/azure/cli/command_modules/role/tests/latest/test_role.py +++ b/src/azure-cli/azure/cli/command_modules/role/tests/latest/test_role.py @@ -698,7 +698,7 @@ def test_role_assignment_audits(self, resource_group): self.cmd('role assignment create --assignee {upn} --role reader --scope {rg_id}') if self.is_live or self.in_recording: - now = datetime.datetime.utcnow() + now = datetime.datetime.now(datetime.UTC) start = now - datetime.timedelta(minutes=1) end = now + datetime.timedelta(minutes=1) start_time = '{}-{}-{}T{}:{}:{}Z'.format(start.year, start.month, start.day, start.hour, diff --git a/src/azure-cli/azure/cli/command_modules/role/tests/latest/test_role_custom.py b/src/azure-cli/azure/cli/command_modules/role/tests/latest/test_role_custom.py index 8e62ee971df..f89ec64703a 100644 --- a/src/azure-cli/azure/cli/command_modules/role/tests/latest/test_role_custom.py +++ b/src/azure-cli/azure/cli/command_modules/role/tests/latest/test_role_custom.py @@ -26,3 +26,12 @@ def test_resolve_role_id(self, ): # action (using a full id) test_full_id = '/subscriptions/0b1f6471-1bf0-4dda-aec3-cb9272123456/providers/microsoft.authorization/roleDefinitions/5370bbf4-6b73-4417-969b-8f2e6e123456' self.assertEqual(test_full_id, _resolve_role_id(test_full_id, 'foobar', mock_client)) + + @mock.patch('azure.cli.core.commands.client_factory.get_mgmt_service_client') + def test_assignment_events_with_start_time_and_default_end_time(self, get_client): + from azure.cli.command_modules.role.custom import _get_assignment_events + + get_client.return_value.activity_logs.list.return_value = [] + _get_assignment_events(mock.Mock(), start_time='2000-01-01T00:00:00Z') + event_filter = get_client.return_value.activity_logs.list.call_args.kwargs['filter'] + self.assertIn('eventTimestamp ge 2000-01-01T00:00:00Z', event_filter) diff --git a/src/azure-cli/azure/cli/command_modules/sql/tests/latest/test_sql_commands.py b/src/azure-cli/azure/cli/command_modules/sql/tests/latest/test_sql_commands.py index c6e90b95630..8ea906b9467 100644 --- a/src/azure-cli/azure/cli/command_modules/sql/tests/latest/test_sql_commands.py +++ b/src/azure-cli/azure/cli/command_modules/sql/tests/latest/test_sql_commands.py @@ -33,7 +33,7 @@ ClientType, ComputeModelType, ResourceIdType) -from datetime import datetime, timedelta +from datetime import UTC, datetime, timedelta # Constants @@ -2045,12 +2045,12 @@ def _create_db_wait_for_first_backup(test, resource_group, server, database_name db = test.cmd('sql db show -g {} -s {} -n {}' .format(resource_group, server, database_name)).get_output_in_json() - earliest_restore_date = _get_earliest_restore_date(db) + earliest_restore_date = _get_earliest_restore_date(db).replace(tzinfo=UTC) - if datetime.utcnow() <= earliest_restore_date: + if datetime.now(UTC) <= earliest_restore_date: print('Waiting until earliest restore date', earliest_restore_date) - while datetime.utcnow() <= earliest_restore_date: + while datetime.now(UTC) <= earliest_restore_date: time.sleep(10) return db @@ -5378,7 +5378,7 @@ def test_sql_zone_resilient_restore_hyperscale_database(self, resource_group, se # Verify restored vldb has correct values (specifically zone redundancy == true and backup storage redundancy == Zone) self.cmd('sql db restore -g {} --server {} --name {} --dest-name {} --time {} ' '--edition {} --family {} --capacity {} --backup-storage-redundancy {} --z' - .format(resource_group, server, source_non_zr_db_name, restore_source_non_zr_true_param_db_name, datetime.utcnow().isoformat(), + .format(resource_group, server, source_non_zr_db_name, restore_source_non_zr_true_param_db_name, datetime.now(UTC).isoformat(), "Hyperscale", 'Gen5', 2, 'zone'), checks=[ JMESPathCheck('resourceGroup', resource_group), @@ -5389,7 +5389,7 @@ def test_sql_zone_resilient_restore_hyperscale_database(self, resource_group, se # Restore zone redundant source vldb with zone redundancy == false # Verify restored vldb has correct values (specifically zone redundancy == false and backup storage redundancy == Zone) self.cmd('sql db restore -g {} --server {} --name {} --dest-name {} --time {} --z {}' - .format(resource_group, server, source_zr_db_name, restore_source_zr_false_param_db_name, datetime.utcnow().isoformat(), False), + .format(resource_group, server, source_zr_db_name, restore_source_zr_false_param_db_name, datetime.now(UTC).isoformat(), False), checks=[ JMESPathCheck('resourceGroup', resource_group), JMESPathCheck('name', restore_source_zr_false_param_db_name), @@ -5400,7 +5400,7 @@ def test_sql_zone_resilient_restore_hyperscale_database(self, resource_group, se # Restore non zone redundant source vldb with no parameters passed in # Verify restored vldb has correct values (specifically zone redundancy == false and backup storage redundancy == Geo) self.cmd('sql db restore -g {} --server {} --name {} --dest-name {} --time {}' - .format(resource_group, server, source_non_zr_db_name, restore_source_non_zr_no_param_db_name, datetime.utcnow().isoformat()), + .format(resource_group, server, source_non_zr_db_name, restore_source_non_zr_no_param_db_name, datetime.now(UTC).isoformat()), checks=[ JMESPathCheck('resourceGroup', resource_group), JMESPathCheck('name', restore_source_non_zr_no_param_db_name), @@ -5410,7 +5410,7 @@ def test_sql_zone_resilient_restore_hyperscale_database(self, resource_group, se # Restore zone redundant source vldb with no parameters passed in # Verify restored vldb has correct values (specifically zone redundancy == true and backup storage redundancy == Zone) self.cmd('sql db restore -g {} --server {} --name {} --dest-name {} --time {}' - .format(resource_group, server, source_zr_db_name, restore_source_zr_no_param_db_name, datetime.utcnow().isoformat()), + .format(resource_group, server, source_zr_db_name, restore_source_zr_no_param_db_name, datetime.now(UTC).isoformat()), checks=[ JMESPathCheck('resourceGroup', resource_group), JMESPathCheck('name', restore_source_zr_no_param_db_name), @@ -6582,7 +6582,7 @@ def test_sql_managed_db_mgmt(self, mi, rg): # test sql db restore command # db1 = self.cmd('sql midb restore -g {} --mi {} -n {} --dest-name {} --time {}' # .format(resource_group_1, managed_instance_name_1, database_name, database_name_restored, - # datetime.utcnow().isoformat()), + # datetime.now(UTC).isoformat()), # checks=[ # JMESPathCheck('resourceGroup', resource_group_1), # JMESPathCheck('name', database_name_restored), @@ -7870,8 +7870,8 @@ def test_sql_midb_logreplay_mgmt(self, mi, rg): 'last_backup_name': 'full.bak' }) - from datetime import datetime, timedelta - self.kwargs['expiry'] = (datetime.utcnow() + timedelta(hours=12)).strftime('%Y-%m-%dT%H:%MZ') + from datetime import UTC, datetime, timedelta + self.kwargs['expiry'] = (datetime.now(UTC) + timedelta(hours=12)).strftime('%Y-%m-%dT%H:%MZ') self.kwargs['storage_key'] = str(self.cmd( 'az storage account keys list -n {storage_account} -g {resource_group} --query "[0].value"').output) diff --git a/src/azure-cli/azure/cli/command_modules/storage/_validators.py b/src/azure-cli/azure/cli/command_modules/storage/_validators.py index 0caee9ed3d5..2ba271842b9 100644 --- a/src/azure-cli/azure/cli/command_modules/storage/_validators.py +++ b/src/azure-cli/azure/cli/command_modules/storage/_validators.py @@ -506,9 +506,9 @@ def validate_source_url(cmd, namespace): # pylint: disable=too-many-statements, if valid_blob_source: client = cf_blob_service(cmd.cli_ctx, client_kwargs) - from datetime import datetime, timedelta - start = datetime.utcnow() - expiry = datetime.utcnow() + timedelta(days=1) + from datetime import UTC, datetime, timedelta + start = datetime.now(UTC) + expiry = datetime.now(UTC) + timedelta(days=1) source_user_delegation_key = client.get_user_delegation_key(start, expiry) # Both source account name and either key or sas (or both) are now available @@ -1559,8 +1559,8 @@ def as_user_validator(namespace): expiry = get_datetime_type(False)(namespace.expiry) - from datetime import datetime, timedelta - if expiry > datetime.utcnow() + timedelta(days=7): + from datetime import UTC, datetime, timedelta + if expiry.replace(tzinfo=UTC) > datetime.now(UTC) + timedelta(days=7): raise argparse.ArgumentError( None, 'incorrect usage: --expiry should be within 7 days from now') diff --git a/src/azure-cli/azure/cli/command_modules/storage/operations/blob.py b/src/azure-cli/azure/cli/command_modules/storage/operations/blob.py index fef4f163baf..7973adb1e4a 100644 --- a/src/azure-cli/azure/cli/command_modules/storage/operations/blob.py +++ b/src/azure-cli/azure/cli/command_modules/storage/operations/blob.py @@ -5,7 +5,7 @@ import os import sys -from datetime import datetime, timedelta +from datetime import UTC, datetime, timedelta from azure.cli.core.profiles import ResourceType, get_sdk from azure.cli.core.util import sdk_no_wait @@ -888,7 +888,7 @@ def generate_sas_blob_uri(cmd, client, permission=None, expiry=None, start=None, account_key = None if as_user: user_delegation_key = client.get_user_delegation_key( - get_datetime_from_string(start) if start else datetime.utcnow(), get_datetime_from_string(expiry), + get_datetime_from_string(start) if start else datetime.now(UTC), get_datetime_from_string(expiry), delegated_user_tid=user_delegation_tid) else: account_key = client.credential.account_key @@ -938,7 +938,7 @@ def generate_container_shared_access_signature(cmd, client, container_name, perm account_key = None if as_user: user_delegation_key = client.get_user_delegation_key( - get_datetime_from_string(start) if start else datetime.utcnow(), get_datetime_from_string(expiry), + get_datetime_from_string(start) if start else datetime.now(UTC), get_datetime_from_string(expiry), delegated_user_tid=user_delegation_tid) else: account_key = client.credential.account_key @@ -974,8 +974,8 @@ def _copy_blob_to_blob_container(cmd, blob_service, source_blob_service, destina # generate sas for oauth copy source if not source_sas: from ..util import create_short_lived_blob_sas_v2 - start = datetime.utcnow() - expiry = datetime.utcnow() + timedelta(days=1) + start = datetime.now(UTC) + expiry = datetime.now(UTC) + timedelta(days=1) source_user_delegation_key = source_blob_service.get_user_delegation_key(start, expiry) source_sas = create_short_lived_blob_sas_v2(cmd, source_blob_service.account_name, source_container, source_blob_name, @@ -1010,8 +1010,8 @@ def _copy_file_to_blob_container(cmd, blob_service, source_file_service, destina '_shared_access_signature#generate_share_sas') t_file_permissions = get_sdk(cmd.cli_ctx, ResourceType.DATA_STORAGE_FILESHARE, '_models#FileSasPermissions') - start = datetime.utcnow() - expiry = datetime.utcnow() + timedelta(days=1) + start = datetime.now(UTC) + expiry = datetime.now(UTC) + timedelta(days=1) source_sas = t_generate_share_sas(account_name=t_file_client.account_name, share_name=source_share, account_key=t_file_client.credential.account_key, permission=t_file_permissions(read=True), @@ -1164,7 +1164,7 @@ def copy_blob(cmd, client, source_url, metadata=None, **kwargs): as_user = True if hasattr(blob_service_client.credential, 'account_key'): as_user = False - expiry = (datetime.utcnow() + timedelta(hours=1)).strftime('%Y-%m-%dT%H:%MZ') + expiry = (datetime.now(UTC) + timedelta(hours=1)).strftime('%Y-%m-%dT%H:%MZ') source_url = generate_sas_blob_uri(cmd, blob_service_client, full_uri=True, blob_url=source_url, blob_name=None, container_name=None, as_user=as_user, expiry=expiry, permission='r') diff --git a/src/azure-cli/azure/cli/command_modules/storage/operations/file.py b/src/azure-cli/azure/cli/command_modules/storage/operations/file.py index f664cfd0ec2..ec45b5624ab 100644 --- a/src/azure-cli/azure/cli/command_modules/storage/operations/file.py +++ b/src/azure-cli/azure/cli/command_modules/storage/operations/file.py @@ -419,9 +419,9 @@ def _create_file_and_directory_from_blob(cmd, file_service, blob_service, share, '_shared_access_signature#generate_blob_sas') t_blob_permissions = get_sdk(cmd.cli_ctx, ResourceType.DATA_STORAGE_BLOB, '_models#BlobSasPermissions') - from datetime import datetime, timedelta - start = datetime.utcnow() - expiry = datetime.utcnow() + timedelta(days=1) + from datetime import UTC, datetime, timedelta + start = datetime.now(UTC) + expiry = datetime.now(UTC) + timedelta(days=1) source_sas = t_generate_blob_sas(account_name=blob_service.account_name, container_name=container, blob_name=blob_name, account_key=blob_service.credential.account_key, @@ -468,9 +468,9 @@ def _create_file_and_directory_from_file(cmd, file_service, source_file_service, '_shared_access_signature#generate_share_sas') t_file_permissions = get_sdk(cmd.cli_ctx, ResourceType.DATA_STORAGE_FILESHARE, '_models#FileSasPermissions') - from datetime import datetime, timedelta - start = datetime.utcnow() - expiry = datetime.utcnow() + timedelta(days=1) + from datetime import UTC, datetime, timedelta + start = datetime.now(UTC) + expiry = datetime.now(UTC) + timedelta(days=1) source_sas = t_generate_share_sas(account_name=source_file_service.account_name, share_name=source_share, account_key=source_file_service.credential.account_key, permission=t_file_permissions(read=True), @@ -554,9 +554,9 @@ def generate_sas_file(cmd, client, share_name=None, directory_name=None, file_na account_key = None user_delegation_key = None if as_user: - from datetime import datetime + from datetime import UTC, datetime user_delegation_key = client.get_user_delegation_key( - start=get_datetime_from_string(start) if start else datetime.utcnow(), + start=get_datetime_from_string(start) if start else datetime.now(UTC), expiry=get_datetime_from_string(expiry), delegated_user_tid=user_delegation_tid) else: diff --git a/src/azure-cli/azure/cli/command_modules/storage/operations/fileshare.py b/src/azure-cli/azure/cli/command_modules/storage/operations/fileshare.py index 7cce7e02f2f..75f535dbbd7 100644 --- a/src/azure-cli/azure/cli/command_modules/storage/operations/fileshare.py +++ b/src/azure-cli/azure/cli/command_modules/storage/operations/fileshare.py @@ -3,7 +3,7 @@ # Licensed under the MIT License. See License.txt in the project root for license information. # -------------------------------------------------------------------------------------------- import math -from datetime import datetime +from datetime import UTC, datetime from knack.log import get_logger @@ -67,7 +67,7 @@ def generate_share_sas(cmd, client, share_name=None, permission=None, expiry=Non user_delegation_key = None if as_user: user_delegation_key = client.get_user_delegation_key( - start=get_datetime_from_string(start) if start else datetime.utcnow(), + start=get_datetime_from_string(start) if start else datetime.now(UTC), expiry=get_datetime_from_string(expiry), delegated_user_tid=user_delegation_tid) else: diff --git a/src/azure-cli/azure/cli/command_modules/storage/operations/filesystem.py b/src/azure-cli/azure/cli/command_modules/storage/operations/filesystem.py index b63b2a5294d..c2ac3ee5566 100644 --- a/src/azure-cli/azure/cli/command_modules/storage/operations/filesystem.py +++ b/src/azure-cli/azure/cli/command_modules/storage/operations/filesystem.py @@ -3,7 +3,7 @@ # Licensed under the MIT License. See License.txt in the project root for license information. # -------------------------------------------------------------------------------------------- -from datetime import datetime +from datetime import UTC, datetime from azure.cli.core.profiles import ResourceType from ..util import get_datetime_from_string @@ -45,7 +45,7 @@ def generate_sas_fs_uri(client, cmd, file_system, permission=None, sas_kwargs = {} if as_user: user_delegation_key = client.get_user_delegation_key( - get_datetime_from_string(start) if start else datetime.utcnow(), + get_datetime_from_string(start) if start else datetime.now(UTC), get_datetime_from_string(expiry), delegated_user_tid=user_delegation_tid) diff --git a/src/azure-cli/azure/cli/command_modules/storage/operations/fs_directory.py b/src/azure-cli/azure/cli/command_modules/storage/operations/fs_directory.py index ac1f0d3dabd..8c3a39b6290 100644 --- a/src/azure-cli/azure/cli/command_modules/storage/operations/fs_directory.py +++ b/src/azure-cli/azure/cli/command_modules/storage/operations/fs_directory.py @@ -6,7 +6,7 @@ """Custom operations for storage file datalake""" # pylint: disable=too-many-locals -from datetime import datetime +from datetime import UTC, datetime from azure.cli.core.profiles import ResourceType from knack.util import todict @@ -106,7 +106,7 @@ def generate_sas_directory_uri(client, cmd, file_system_name, directory_name, pe sas_kwargs = {} if as_user: user_delegation_key = client.get_user_delegation_key( - get_datetime_from_string(start) if start else datetime.utcnow(), + get_datetime_from_string(start) if start else datetime.now(UTC), get_datetime_from_string(expiry), delegated_user_tid=user_delegation_tid) diff --git a/src/azure-cli/azure/cli/command_modules/storage/operations/fs_file.py b/src/azure-cli/azure/cli/command_modules/storage/operations/fs_file.py index 110b9a1a5e1..8657e078745 100644 --- a/src/azure-cli/azure/cli/command_modules/storage/operations/fs_file.py +++ b/src/azure-cli/azure/cli/command_modules/storage/operations/fs_file.py @@ -7,7 +7,7 @@ # pylint: disable=too-many-locals import os -from datetime import datetime +from datetime import UTC, datetime from azure.core.exceptions import HttpResponseError from azure.cli.core.profiles import ResourceType @@ -160,7 +160,7 @@ def generate_sas_file_uri(client, cmd, file_system_name, path, permission=None, sas_kwargs = {} if as_user: user_delegation_key = client.get_user_delegation_key( - get_datetime_from_string(start) if start else datetime.utcnow(), + get_datetime_from_string(start) if start else datetime.now(UTC), get_datetime_from_string(expiry), delegated_user_tid=user_delegation_tid) diff --git a/src/azure-cli/azure/cli/command_modules/storage/operations/queue.py b/src/azure-cli/azure/cli/command_modules/storage/operations/queue.py index 23a43637bc3..4043b46cbae 100644 --- a/src/azure-cli/azure/cli/command_modules/storage/operations/queue.py +++ b/src/azure-cli/azure/cli/command_modules/storage/operations/queue.py @@ -52,9 +52,9 @@ def generate_queue_sas(cmd, client, queue_name=None, permission=None, expiry=Non account_key = None user_delegation_key = None if as_user: - from datetime import datetime + from datetime import UTC, datetime user_delegation_key = client.get_user_delegation_key( - start=get_datetime_from_string(start) if start else datetime.utcnow(), + start=get_datetime_from_string(start) if start else datetime.now(UTC), expiry=get_datetime_from_string(expiry), delegated_user_tid=user_delegation_tid) else: diff --git a/src/azure-cli/azure/cli/command_modules/storage/tests/latest/test_storage_account_scenarios.py b/src/azure-cli/azure/cli/command_modules/storage/tests/latest/test_storage_account_scenarios.py index f0cacf85baf..1dec5ab13aa 100644 --- a/src/azure-cli/azure/cli/command_modules/storage/tests/latest/test_storage_account_scenarios.py +++ b/src/azure-cli/azure/cli/command_modules/storage/tests/latest/test_storage_account_scenarios.py @@ -13,7 +13,7 @@ from azure.cli.core.profiles import ResourceType from ..storage_test_util import StorageScenarioMixin from knack.util import CLIError -from datetime import datetime, timedelta +from datetime import UTC, datetime, timedelta from azure.cli.testsdk.scenario_tests import AllowLargeResponse @@ -2004,10 +2004,10 @@ class RevokeStorageAccountTests(StorageScenarioMixin, RoleScenarioTest, LiveScen def test_storage_account_revoke_delegation_keys(self, resource_group, storage_account): if self.run_under_service_principal(): return # this test delete users which are beyond a SP's capacity, so quit... - from datetime import datetime, timedelta + from datetime import UTC, datetime, timedelta import time - expiry = (datetime.utcnow() + timedelta(hours=1)).strftime('%Y-%m-%dT%H:%MZ') + expiry = (datetime.now(UTC) + timedelta(hours=1)).strftime('%Y-%m-%dT%H:%MZ') account_info = self.get_account_info(resource_group, storage_account) c = self.create_container(account_info) b = self.create_random_name('blob', 24) diff --git a/src/azure-cli/azure/cli/command_modules/storage/tests/latest/test_storage_adls_gen2_scenarios.py b/src/azure-cli/azure/cli/command_modules/storage/tests/latest/test_storage_adls_gen2_scenarios.py index 7ede509158b..197b363f72c 100644 --- a/src/azure-cli/azure/cli/command_modules/storage/tests/latest/test_storage_adls_gen2_scenarios.py +++ b/src/azure-cli/azure/cli/command_modules/storage/tests/latest/test_storage_adls_gen2_scenarios.py @@ -5,7 +5,7 @@ import unittest import os import time -from datetime import datetime, timedelta +from datetime import UTC, datetime, timedelta from azure.cli.testsdk import (ScenarioTest, LiveScenarioTest, ResourceGroupPreparer, StorageAccountPreparer, JMESPathCheck, JMESPathCheckExists, RoleBasedServicePrincipalPreparer) @@ -503,14 +503,14 @@ def test_adls_file_scenarios(self, resource_group, storage_account): @ResourceGroupPreparer() @StorageAccountPreparer(kind="StorageV2", hns=True) def test_adls_file_set_expiry_scenarios(self, resource_group, storage_account): - from datetime import datetime, timedelta + from datetime import UTC, datetime, timedelta account_info = self.get_account_info(resource_group, storage_account) filesystem = self.create_file_system(account_info) file = self.create_random_name(prefix="file", length=12) self.storage_cmd('storage fs file create -p {} -f {} --content-type "application/json"', account_info, file, filesystem) - expiry = (datetime.utcnow() + timedelta(seconds=5)).strftime('%Y-%m-%dT%H:%M:%SZ') + expiry = (datetime.now(UTC) + timedelta(seconds=5)).strftime('%Y-%m-%dT%H:%M:%SZ') self.storage_cmd("storage fs file set-expiry --expiry-options Absolute --expires-on {} -p {} -f {}", account_info, expiry, file, filesystem) self.storage_cmd("storage fs file exists -p {} -f {}", account_info, file, filesystem)\ @@ -565,7 +565,7 @@ def test_storage_fs_generate_sas_full_uri(self, resource_group, storage_account) account_info = self.get_account_info(resource_group, storage_account) f = self.create_file_system(account_info) - expiry = (datetime.utcnow() + timedelta(hours=1)).strftime('%Y-%m-%dT%H:%MZ') + expiry = (datetime.now(UTC) + timedelta(hours=1)).strftime('%Y-%m-%dT%H:%MZ') fs_uri = self.storage_cmd('storage fs generate-sas -n {} --expiry {} --permissions ' 'r --https-only --full-uri', account_info, f, expiry).output self.assertTrue(fs_uri) @@ -578,7 +578,7 @@ def test_storage_fs_generate_sas_as_user(self, resource_group, storage_account): account_info = self.get_account_info(resource_group, storage_account) f = self.create_file_system(account_info) - expiry = (datetime.utcnow() + timedelta(hours=1)).strftime('%Y-%m-%dT%H:%MZ') + expiry = (datetime.now(UTC) + timedelta(hours=1)).strftime('%Y-%m-%dT%H:%MZ') with self.assertRaisesRegex(CLIError, "incorrect usage: specify --as-user when --auth-mode login"): self.cmd('storage fs generate-sas --account-name {} -n {} --expiry {} --permissions r --https-only ' @@ -606,7 +606,7 @@ def test_storage_fs_generate_sas_with_user_delegation_oid(self, resource_group, current_tenant = self.cmd('account show --query tenantId').get_output_in_json() current_tenant = current_tenant if current_tenant is not None else '544a7a2e-697f-487c-b2b0-a13df7f346b6' - expiry = (datetime.utcnow() + timedelta(hours=1)).strftime('%Y-%m-%dT%H:%MZ') + expiry = (datetime.now(UTC) + timedelta(hours=1)).strftime('%Y-%m-%dT%H:%MZ') fs_sas = self.cmd('storage fs generate-sas --account-name {} -n {} --expiry {} --permissions ' 'dlrwop --https-only --as-user --auth-mode login --user-delegation-oid ' @@ -636,7 +636,7 @@ def test_storage_fs_directory_generate_sas_full_uri(self, resource_group, storag self.storage_cmd('storage fs directory create -n {} -f {}', account_info, directory, filesystem) - expiry = (datetime.utcnow() + timedelta(hours=1)).strftime('%Y-%m-%dT%H:%MZ') + expiry = (datetime.now(UTC) + timedelta(hours=1)).strftime('%Y-%m-%dT%H:%MZ') fs_uri = self.storage_cmd('storage fs directory generate-sas -n {} -f {} --expiry {} --permissions ' 'r --https-only --full-uri', account_info, directory, filesystem, expiry).output self.assertTrue(fs_uri) @@ -653,7 +653,7 @@ def test_storage_fs_directory_generate_sas_as_user(self, resource_group, storage self.storage_cmd('storage fs directory create -n {} -f {}', account_info, directory, filesystem) - expiry = (datetime.utcnow() + timedelta(hours=1)).strftime('%Y-%m-%dT%H:%MZ') + expiry = (datetime.now(UTC) + timedelta(hours=1)).strftime('%Y-%m-%dT%H:%MZ') with self.assertRaisesRegex(CLIError, "incorrect usage: specify --as-user when --auth-mode login"): self.cmd('storage fs directory generate-sas --account-name {} -n {} -f {} --expiry {} --permissions r ' @@ -685,7 +685,7 @@ def test_storage_fs_directory_generate_sas_with_user_delegation_oid(self, resour self.storage_cmd('storage fs directory create -n {} -f {}', account_info, directory, filesystem) - expiry = (datetime.utcnow() + timedelta(hours=1)).strftime('%Y-%m-%dT%H:%MZ') + expiry = (datetime.now(UTC) + timedelta(hours=1)).strftime('%Y-%m-%dT%H:%MZ') fs_sas = self.cmd('storage fs directory generate-sas --account-name {} -n {} -f {} --expiry {} --permissions ' 'dlrwop --https-only --as-user --auth-mode login --user-delegation-oid ' @@ -719,7 +719,7 @@ def test_storage_fs_file_generate_sas_as_user(self, resource_group, storage_acco self.storage_cmd('storage fs directory create -n {} -f {}', account_info, directory, filesystem) self.storage_cmd('storage fs file create -p {} -f {}', account_info, file_path, filesystem) - expiry = (datetime.utcnow() + timedelta(hours=1)).strftime('%Y-%m-%dT%H:%MZ') + expiry = (datetime.now(UTC) + timedelta(hours=1)).strftime('%Y-%m-%dT%H:%MZ') with self.assertRaisesRegex(CLIError, "incorrect usage: specify --as-user when --auth-mode login"): self.cmd('storage fs file generate-sas --account-name {} -p {} -f {} --expiry {} --permissions r ' @@ -755,7 +755,7 @@ def test_storage_fs_file_generate_sas_with_user_delegation_oid(self, resource_gr self.storage_cmd('storage fs file create -p {} -f {}', account_info, file_path, filesystem) - expiry = (datetime.utcnow() + timedelta(hours=1)).strftime('%Y-%m-%dT%H:%MZ') + expiry = (datetime.now(UTC) + timedelta(hours=1)).strftime('%Y-%m-%dT%H:%MZ') file_sas = self.cmd('storage fs file generate-sas --account-name {} -p {} -f {} --expiry {} --permissions ' 'dlrwop --https-only --as-user --auth-mode login --user-delegation-oid ' @@ -790,8 +790,8 @@ def test_adls_directory_upload(self, resource_group, storage_account, test_dir): self.storage_cmd('storage fs create -n {}', account_info, filesystem) self.storage_cmd('storage fs directory create -n {} -f {}', account_info, directory, filesystem) - from datetime import datetime, timedelta - expiry = (datetime.utcnow() + timedelta(hours=1)).strftime('%Y-%m-%dT%H:%MZ') + from datetime import UTC, datetime, timedelta + expiry = (datetime.now(UTC) + timedelta(hours=1)).strftime('%Y-%m-%dT%H:%MZ') sas = self.storage_cmd('storage container generate-sas -n {} --https-only ' '--permissions acdlrw --expiry {} -otsv', account_info, filesystem, expiry).output.strip() @@ -855,8 +855,8 @@ def test_adls_directory_download(self, resource_group, storage_account, test_dir self.storage_cmd('storage fs directory upload -f {} -d {} -s "{}" --recursive', account_info, filesystem, directory, os.path.join(test_dir, 'apple')) - from datetime import datetime, timedelta - expiry = (datetime.utcnow() + timedelta(hours=1)).strftime('%Y-%m-%dT%H:%MZ') + from datetime import UTC, datetime, timedelta + expiry = (datetime.now(UTC) + timedelta(hours=1)).strftime('%Y-%m-%dT%H:%MZ') sas = self.storage_cmd('storage container generate-sas -n {} --https-only ' '--permissions lr --expiry {} -otsv', account_info, filesystem, expiry).output.strip() diff --git a/src/azure-cli/azure/cli/command_modules/storage/tests/latest/test_storage_azcopy_scenarios.py b/src/azure-cli/azure/cli/command_modules/storage/tests/latest/test_storage_azcopy_scenarios.py index 95480cbbba3..f1efcec5129 100644 --- a/src/azure-cli/azure/cli/command_modules/storage/tests/latest/test_storage_azcopy_scenarios.py +++ b/src/azure-cli/azure/cli/command_modules/storage/tests/latest/test_storage_azcopy_scenarios.py @@ -4,7 +4,7 @@ # -------------------------------------------------------------------------------------------- import os import shutil -from datetime import datetime, timedelta +from datetime import UTC, datetime, timedelta from azure.cli.testsdk import (StorageAccountPreparer, LiveScenarioTest, JMESPathCheck, ResourceGroupPreparer) from ..storage_test_util import StorageScenarioMixin, StorageTestFilesPreparer from knack.util import CLIError @@ -192,8 +192,8 @@ def test_storage_blob_azcopy_sync_oauth(self, storage_account, test_dir): @StorageAccountPreparer() @StorageTestFilesPreparer() def test_storage_blob_azcopy_sync_sas(self, resource_group, storage_account, test_dir): - from datetime import datetime, timedelta - expiry = (datetime.utcnow() + timedelta(hours=1)).strftime('%Y-%m-%dT%H:%MZ') + from datetime import UTC, datetime, timedelta + expiry = (datetime.now(UTC) + timedelta(hours=1)).strftime('%Y-%m-%dT%H:%MZ') account_info = self.get_account_info(resource_group, storage_account) container = self.create_random_name(prefix='container', length=20) @@ -485,8 +485,8 @@ def test_storage_file_azcopy_remove_oauth(self, resource_group, storage_account) @StorageAccountPreparer() @StorageTestFilesPreparer() def test_storage_azcopy_remove_sas(self, resource_group, storage_account, test_dir): - from datetime import datetime, timedelta - expiry = (datetime.utcnow() + timedelta(hours=1)).strftime('%Y-%m-%dT%H:%MZ') + from datetime import UTC, datetime, timedelta + expiry = (datetime.now(UTC) + timedelta(hours=1)).strftime('%Y-%m-%dT%H:%MZ') account_info = self.get_account_info(resource_group, storage_account) container = self.create_container(account_info, prefix='container', length=20) @@ -763,7 +763,7 @@ def test_storage_azcopy_blob_source_with_credential(self, resource_group, first_ self.assertEqual(11, sum(len(f) for r, d, f in os.walk(local_folder))) # Download a set of files with source sas token - expiry = (datetime.utcnow() + timedelta(hours=1)).strftime('%Y-%m-%dT%H:%MZ') + expiry = (datetime.now(UTC) + timedelta(hours=1)).strftime('%Y-%m-%dT%H:%MZ') first_sas_token = self.cmd( 'storage container generate-sas --connection-string {} -n {} --expiry {} --permissions {} -o tsv'.format( first_connection_string, first_container, expiry, 'rwalcd')).output.strip() @@ -774,7 +774,7 @@ def test_storage_azcopy_blob_source_with_credential(self, resource_group, first_ self.assertEqual(21, sum(len(f) for r, d, f in os.walk(local_folder))) # Copy a single blob to another single blob with sas token - expiry = (datetime.utcnow() + timedelta(hours=1)).strftime('%Y-%m-%dT%H:%MZ') + expiry = (datetime.now(UTC) + timedelta(hours=1)).strftime('%Y-%m-%dT%H:%MZ') second_sas_token = self.storage_cmd( 'storage container generate-sas -n {} --expiry {} --permissions {} -o tsv', second_account_info, second_container, expiry, 'acdlrw').output.strip() diff --git a/src/azure-cli/azure/cli/command_modules/storage/tests/latest/test_storage_batch_operations.py b/src/azure-cli/azure/cli/command_modules/storage/tests/latest/test_storage_batch_operations.py index 8e40c2be78b..acb2b8160d3 100644 --- a/src/azure-cli/azure/cli/command_modules/storage/tests/latest/test_storage_batch_operations.py +++ b/src/azure-cli/azure/cli/command_modules/storage/tests/latest/test_storage_batch_operations.py @@ -4,7 +4,7 @@ # -------------------------------------------------------------------------------------------- import os -from datetime import datetime +from datetime import UTC, datetime from azure.cli.testsdk import ScenarioTest, StorageAccountPreparer, ResourceGroupPreparer, JMESPathCheck, live_only from ..storage_test_util import StorageScenarioMixin, StorageTestFilesPreparer @@ -353,8 +353,8 @@ def test_storage_file_batch_upload_scenarios(self, test_dir, storage_account_inf @StorageAccountPreparer(parameter_name='dst_account') @StorageTestFilesPreparer() def test_storage_blob_batch_copy(self, src_account_info, dst_account_info, test_dir): - from datetime import datetime, timedelta - expiry = (datetime.utcnow() + timedelta(hours=1)).strftime('%Y-%m-%dT%H:%MZ') + from datetime import UTC, datetime, timedelta + expiry = (datetime.now(UTC) + timedelta(hours=1)).strftime('%Y-%m-%dT%H:%MZ') src_container = 'container1' self.storage_cmd('storage container create -n {}', src_account_info, src_container) @@ -443,8 +443,8 @@ def test_storage_blob_batch_copy(self, src_account_info, dst_account_info, test_ @StorageAccountPreparer(parameter_name='dst_account') @StorageTestFilesPreparer() def test_storage_file_batch_copy(self, src_account_info, dst_account_info, test_dir): - from datetime import datetime, timedelta - expiry = (datetime.utcnow() + timedelta(hours=1)).strftime('%Y-%m-%dT%H:%MZ') + from datetime import UTC, datetime, timedelta + expiry = (datetime.now(UTC) + timedelta(hours=1)).strftime('%Y-%m-%dT%H:%MZ') src_container = 'container1' self.storage_cmd('storage container create -n {}', src_account_info, src_container) @@ -648,12 +648,12 @@ def create_and_populate_share(src_share): @StorageTestFilesPreparer() @live_only() def test_storage_blob_batch_sas_scenarios(self, test_dir, resource_group, storage_account_info): - from datetime import datetime, timedelta + from datetime import UTC, datetime, timedelta container_name = 'container1' self.storage_cmd('storage container create -n {}', storage_account_info, container_name) temp_dir = self.create_temp_dir() - expiry = (datetime.utcnow() + timedelta(hours=1)).strftime('%Y-%m-%dT%H:%MZ') + expiry = (datetime.now(UTC) + timedelta(hours=1)).strftime('%Y-%m-%dT%H:%MZ') sas_token = self.storage_cmd('storage container generate-sas -n {} --permissions dwrl --expiry {}', storage_account_info, container_name, expiry).get_output_in_json() diff --git a/src/azure-cli/azure/cli/command_modules/storage/tests/latest/test_storage_blob_copy_scenarios.py b/src/azure-cli/azure/cli/command_modules/storage/tests/latest/test_storage_blob_copy_scenarios.py index cbf65713799..1059b10fd21 100644 --- a/src/azure-cli/azure/cli/command_modules/storage/tests/latest/test_storage_blob_copy_scenarios.py +++ b/src/azure-cli/azure/cli/command_modules/storage/tests/latest/test_storage_blob_copy_scenarios.py @@ -214,9 +214,9 @@ def test_storage_blob_copy_with_sas_and_snapshot(self, resource_group, source_ac self.storage_cmd('storage blob upload -c {} -f "{}" -n src --overwrite', source_account_info, source_container, source_file) - from datetime import datetime, timedelta - start = datetime.utcnow().strftime('%Y-%m-%dT%H:%MZ') - expiry = (datetime.utcnow() + timedelta(minutes=5)).strftime('%Y-%m-%dT%H:%MZ') + from datetime import UTC, datetime, timedelta + start = datetime.now(UTC).strftime('%Y-%m-%dT%H:%MZ') + expiry = (datetime.now(UTC) + timedelta(minutes=5)).strftime('%Y-%m-%dT%H:%MZ') sas = self.storage_cmd('storage blob generate-sas -c {} -n src --permissions r --start {}' ' --expiry {}', source_account_info, source_container, start, expiry).output.strip() @@ -260,9 +260,9 @@ def test_storage_blob_copy_same_account_sas(self, resource_group, storage_accoun self.storage_cmd('storage blob upload -c {} -f "{}" -n src', account_info, source_container, source_file) - from datetime import datetime, timedelta - start = datetime.utcnow().strftime('%Y-%m-%dT%H:%MZ') - expiry = (datetime.utcnow() + timedelta(hours=1)).strftime('%Y-%m-%dT%H:%MZ') + from datetime import UTC, datetime, timedelta + start = datetime.now(UTC).strftime('%Y-%m-%dT%H:%MZ') + expiry = (datetime.now(UTC) + timedelta(hours=1)).strftime('%Y-%m-%dT%H:%MZ') sas = self.storage_cmd('storage blob generate-sas -c {} -n src --permissions r --start {}' ' --expiry {}', account_info, source_container, start, expiry).output.strip() diff --git a/src/azure-cli/azure/cli/command_modules/storage/tests/latest/test_storage_blob_live_scenarios.py b/src/azure-cli/azure/cli/command_modules/storage/tests/latest/test_storage_blob_live_scenarios.py index 8e63766fb5a..c567a51d10c 100644 --- a/src/azure-cli/azure/cli/command_modules/storage/tests/latest/test_storage_blob_live_scenarios.py +++ b/src/azure-cli/azure/cli/command_modules/storage/tests/latest/test_storage_blob_live_scenarios.py @@ -4,7 +4,7 @@ # -------------------------------------------------------------------------------------------- import os -from datetime import datetime, timedelta +from datetime import UTC, datetime, timedelta from azure.cli.testsdk import (LiveScenarioTest, ResourceGroupPreparer, StorageAccountPreparer, JMESPathCheck, JMESPathCheckExists, NoneCheck) from azure.cli.core.profiles import ResourceType @@ -145,7 +145,7 @@ def test_storage_blob_query_scenario(self, resource_group, storage_account): account_info, container, csv_blob, query_string).output self.assertIsNotNone(result) #test blob-url - expiry = (datetime.utcnow() + timedelta(hours=1)).strftime('%Y-%m-%dT%H:%MZ') + expiry = (datetime.now(UTC) + timedelta(hours=1)).strftime('%Y-%m-%dT%H:%MZ') blob_uri = self.storage_cmd('storage blob generate-sas -n {} -c {} --expiry {} --permissions ' 'rwad --https-only --full-uri -o tsv', account_info, csv_blob, container, expiry).output.strip() @@ -193,7 +193,7 @@ def test_storage_blob_url_scenarios(self, resource_group, storage_account): self.storage_cmd('storage blob upload -c {} -f "{}" -n {} ', account_info, container, local_file1, blob_name1) - expiry = (datetime.utcnow() + timedelta(hours=1)).strftime('%Y-%m-%dT%H:%MZ') + expiry = (datetime.now(UTC) + timedelta(hours=1)).strftime('%Y-%m-%dT%H:%MZ') blob_uri = self.storage_cmd('storage blob generate-sas -n {} -c {} --expiry {} --permissions ' 'rwad --https-only --full-uri -o tsv', account_info, blob_name1, container, expiry).output.strip() diff --git a/src/azure-cli/azure/cli/command_modules/storage/tests/latest/test_storage_blob_rewrite_scenarios.py b/src/azure-cli/azure/cli/command_modules/storage/tests/latest/test_storage_blob_rewrite_scenarios.py index 4103c4cab49..7be520d6fc5 100644 --- a/src/azure-cli/azure/cli/command_modules/storage/tests/latest/test_storage_blob_rewrite_scenarios.py +++ b/src/azure-cli/azure/cli/command_modules/storage/tests/latest/test_storage_blob_rewrite_scenarios.py @@ -3,7 +3,7 @@ # Licensed under the MIT License. See License.txt in the project root for license information. # -------------------------------------------------------------------------------------------- -from datetime import datetime, timedelta +from datetime import UTC, datetime, timedelta from azure.cli.testsdk import LiveScenarioTest, ResourceGroupPreparer, StorageAccountPreparer,\ JMESPathCheck from azure.cli.testsdk.decorators import serial_test @@ -77,7 +77,7 @@ def test_storage_blob_rewrite_encryption_scope(self, group, account, file_size_k ]) # Update blob encryption - expiry = (datetime.utcnow() + timedelta(days=1)).strftime('%Y-%m-%dT%H:%MZ') + expiry = (datetime.now(UTC) + timedelta(days=1)).strftime('%Y-%m-%dT%H:%MZ') source_url = self.storage_cmd( 'storage blob generate-sas -c {} -n {} --https-only --permissions r --expiry {} --full-uri -otsv', account_info, container, blob, expiry).output.strip() diff --git a/src/azure-cli/azure/cli/command_modules/storage/tests/latest/test_storage_blob_scenarios.py b/src/azure-cli/azure/cli/command_modules/storage/tests/latest/test_storage_blob_scenarios.py index d9b6fa13def..255d2dc8f19 100644 --- a/src/azure-cli/azure/cli/command_modules/storage/tests/latest/test_storage_blob_scenarios.py +++ b/src/azure-cli/azure/cli/command_modules/storage/tests/latest/test_storage_blob_scenarios.py @@ -6,7 +6,7 @@ import os import re import unittest -from datetime import datetime, timedelta +from datetime import UTC, datetime, timedelta from azure.cli.testsdk import (ScenarioTest, ResourceGroupPreparer, StorageAccountPreparer, JMESPathCheck, JMESPathCheckExists, NoneCheck) from knack.util import CLIError @@ -103,7 +103,7 @@ def verify_blob_upload_and_download(self, group, account, file_size_kb, blob_typ else: self.assertEqual(show_result.get('properties').get('pageRanges'), None) - expiry = (datetime.utcnow() + timedelta(hours=1)).strftime('%Y-%m-%dT%H:%MZ') + expiry = (datetime.now(UTC) + timedelta(hours=1)).strftime('%Y-%m-%dT%H:%MZ') sas = self.storage_cmd('storage blob generate-sas -n {} -c {} --expiry {} --permissions ' 'r --https-only', account_info, blob_name, container, expiry).output self.assertTrue(sas) @@ -280,8 +280,8 @@ def test_storage_blob_container_operations(self, resource_group, storage_account .assert_with_checks(JMESPathCheck('publicAccess', 'off')) # test case with access policy set - from datetime import datetime, timedelta - expiry = (datetime.utcnow() + timedelta(hours=1)).strftime('%Y-%m-%dT%H:%MZ') + from datetime import UTC, datetime, timedelta + expiry = (datetime.now(UTC) + timedelta(hours=1)).strftime('%Y-%m-%dT%H:%MZ') policy = self.create_random_name('policy', 16) self.storage_cmd('storage container policy create -c {} -n {} --expiry {} --permissions racwdxyltfmei', account_info, c, policy, expiry) @@ -336,8 +336,8 @@ def test_storage_blob_container_operations(self, resource_group, storage_account JMESPathCheck('properties.lease.state', 'available'), JMESPathCheck('properties.lease.status', 'unlocked')) - from datetime import datetime, timedelta - expiry = (datetime.utcnow() + timedelta(hours=1)).strftime('%Y-%m-%dT%H:%MZ') + from datetime import UTC, datetime, timedelta + expiry = (datetime.now(UTC) + timedelta(hours=1)).strftime('%Y-%m-%dT%H:%MZ') self.assertIn('sig=', self.storage_cmd('storage container generate-sas -n {} --permissions r --expiry {}', account_info, c, expiry).output) @@ -518,7 +518,7 @@ def test_storage_blob_generate_sas_full_uri(self, resource_group, storage_accoun c = self.create_container(account_info) b = self.create_random_name('blob', 24) - expiry = (datetime.utcnow() + timedelta(hours=1)).strftime('%Y-%m-%dT%H:%MZ') + expiry = (datetime.now(UTC) + timedelta(hours=1)).strftime('%Y-%m-%dT%H:%MZ') blob_uri = self.storage_cmd('storage blob generate-sas -n {} -c {} --expiry {} --permissions ' 'r --https-only --full-uri', account_info, b, c, expiry).output self.assertTrue(blob_uri) @@ -532,7 +532,7 @@ def test_storage_blob_generate_sas_as_user(self, resource_group, storage_account c = self.create_container(account_info) b = self.create_random_name('blob', 24) - expiry = (datetime.utcnow() + timedelta(hours=1)).strftime('%Y-%m-%dT%H:%MZ') + expiry = (datetime.now(UTC) + timedelta(hours=1)).strftime('%Y-%m-%dT%H:%MZ') with self.assertRaisesRegex(CLIError, "incorrect usage: specify --as-user when --auth-mode login"): self.cmd('storage blob generate-sas --account-name {} -n {} -c {} --expiry {} --permissions r --https-only ' @@ -572,7 +572,7 @@ def test_storage_blob_generate_sas_with_user_delegation_oid(self, resource_group current_tenant = self.cmd('account show --query tenantId').get_output_in_json() current_tenant = current_tenant if current_tenant is not None else '544a7a2e-697f-487c-b2b0-a13df7f346b6' - expiry = (datetime.utcnow() + timedelta(hours=1)).strftime('%Y-%m-%dT%H:%MZ') + expiry = (datetime.now(UTC) + timedelta(hours=1)).strftime('%Y-%m-%dT%H:%MZ') self.storage_cmd('storage blob upload -c {} -n {} -f "{}"', account_info, c, b, local_file) @@ -892,8 +892,8 @@ def test_storage_blob_vlm(self, resource_group, storage_account_info): self.storage_cmd('storage blob upload -c {} -f "{}" -n {} ', storage_account_info, container, file, blob) # Test set immutability policy - from datetime import datetime, timedelta - expiry = (datetime.utcnow() + timedelta(hours=1)).strftime('%Y-%m-%dT%H:%MZ') + from datetime import UTC, datetime, timedelta + expiry = (datetime.now(UTC) + timedelta(hours=1)).strftime('%Y-%m-%dT%H:%MZ') result = self.storage_cmd('storage blob immutability-policy set -n {} -c {} ' '--expiry-time {} --policy-mode Unlocked', storage_account_info, blob, container, expiry).get_output_in_json() @@ -1042,7 +1042,7 @@ def test_storage_blob_restore(self, resource_group, storage_account): JMESPathCheck('restorePolicy.days', 1), JMESPathCheckExists('restorePolicy.minRestoreTime')) - time_to_restore = (datetime.utcnow() + timedelta(seconds=-5)).strftime('%Y-%m-%dT%H:%MZ') + time_to_restore = (datetime.now(UTC) + timedelta(seconds=-5)).strftime('%Y-%m-%dT%H:%MZ') # c1/b1 -> c1/b2 start_range = '/'.join([c1, b1]) @@ -1059,7 +1059,7 @@ def test_storage_blob_restore(self, resource_group, storage_account): if self.is_live: time.sleep(300) - time_to_restore = (datetime.utcnow() + timedelta(seconds=-5)).strftime('%Y-%m-%dT%H:%MZ') + time_to_restore = (datetime.now(UTC) + timedelta(seconds=-5)).strftime('%Y-%m-%dT%H:%MZ') # c1/b2 -> c2/b3 start_range = '/'.join([c1, b2]) end_range = '/'.join([c2, b3]) diff --git a/src/azure-cli/azure/cli/command_modules/storage/tests/latest/test_storage_container_access_policy.py b/src/azure-cli/azure/cli/command_modules/storage/tests/latest/test_storage_container_access_policy.py index 2641f94381d..d9860864899 100644 --- a/src/azure-cli/azure/cli/command_modules/storage/tests/latest/test_storage_container_access_policy.py +++ b/src/azure-cli/azure/cli/command_modules/storage/tests/latest/test_storage_container_access_policy.py @@ -8,13 +8,13 @@ JMESPathCheck, JMESPathCheckExists, NoneCheck, live_only) from ..storage_test_util import StorageScenarioMixin from knack.util import CLIError -from datetime import datetime, timedelta +from datetime import UTC, datetime, timedelta class StorageSASScenario(StorageScenarioMixin, ScenarioTest): @ResourceGroupPreparer() @StorageAccountPreparer(allow_blob_public_access=True) def test_storage_container_access_policy_scenario(self, resource_group, storage_account): - expiry = (datetime.utcnow() + timedelta(hours=1)).strftime('%Y-%m-%dT%H:%MZ') + expiry = (datetime.now(UTC) + timedelta(hours=1)).strftime('%Y-%m-%dT%H:%MZ') account_info = self.get_account_info(resource_group, storage_account) container = self.create_random_name(prefix='cont', length=24) @@ -40,8 +40,8 @@ def test_storage_container_access_policy_scenario(self, resource_group, storage_ container, local_file, sas) - new_start = (datetime.utcnow() - timedelta(hours=1)).strftime('%Y-%m-%dT%H:%MZ') - new_expiry = (datetime.utcnow() + timedelta(hours=1)).strftime('%Y-%m-%dT%H:%MZ') + new_start = (datetime.now(UTC) - timedelta(hours=1)).strftime('%Y-%m-%dT%H:%MZ') + new_expiry = (datetime.now(UTC) + timedelta(hours=1)).strftime('%Y-%m-%dT%H:%MZ') self.storage_cmd('storage container policy update -c {} -n {} --permissions rwdxl --start {} --expiry {}', account_info, container, policy, new_start, new_expiry) diff --git a/src/azure-cli/azure/cli/command_modules/storage/tests/latest/test_storage_file_scenarios.py b/src/azure-cli/azure/cli/command_modules/storage/tests/latest/test_storage_file_scenarios.py index 8e3af8b0c30..b2e4023e7b2 100644 --- a/src/azure-cli/azure/cli/command_modules/storage/tests/latest/test_storage_file_scenarios.py +++ b/src/azure-cli/azure/cli/command_modules/storage/tests/latest/test_storage_file_scenarios.py @@ -361,8 +361,8 @@ def validate_file_scenario(self, account_info, share): file_list = self.storage_cmd('storage file list -s {}', account_info, share).get_output_in_json() self.assertIsNotNone(file_list[0]['lastAccessTime']) - from datetime import datetime, timedelta - expiry = (datetime.utcnow() + timedelta(hours=1)).strftime('%Y-%m-%dT%H:%MZ') + from datetime import UTC, datetime, timedelta + expiry = (datetime.now(UTC) + timedelta(hours=1)).strftime('%Y-%m-%dT%H:%MZ') self.storage_cmd('storage file generate-sas -s {} -p {} --permissions r --expiry {}', account_info, share, filename, expiry).assert_with_checks(StringContainCheck('sig=')) @@ -691,8 +691,8 @@ def test_storage_file_generate_sas_as_user(self, resource_group, storage_account local_file = self.create_temp_file(1024) file_name = os.path.basename(local_file) - from datetime import datetime, timedelta - expiry = (datetime.utcnow() + timedelta(hours=1)).strftime('%Y-%m-%dT%H:%MZ') + from datetime import UTC, datetime, timedelta + expiry = (datetime.now(UTC) + timedelta(hours=1)).strftime('%Y-%m-%dT%H:%MZ') with self.assertRaisesRegex(CLIError, "incorrect usage: specify --as-user when --auth-mode login"): self.cmd('storage file generate-sas --account-name {} -p {} -s {} --expiry {} --permissions r --https-only ' @@ -725,8 +725,8 @@ def test_storage_file_generate_sas_with_user_delegation_oid(self, resource_group current_tenant = self.cmd('account show --query tenantId').get_output_in_json() current_tenant = current_tenant if current_tenant is not None else '544a7a2e-697f-487c-b2b0-a13df7f346b6' - from datetime import datetime, timedelta - expiry = (datetime.utcnow() + timedelta(hours=1)).strftime('%Y-%m-%dT%H:%MZ') + from datetime import UTC, datetime, timedelta + expiry = (datetime.now(UTC) + timedelta(hours=1)).strftime('%Y-%m-%dT%H:%MZ') file_sas = self.cmd('storage file generate-sas --account-name {} -p {} -s {} --expiry {} --permissions crwd ' '--https-only --as-user --auth-mode login --backup-intent --user-delegation-oid {} ' diff --git a/src/azure-cli/azure/cli/command_modules/storage/tests/latest/test_storage_file_share_scenarios.py b/src/azure-cli/azure/cli/command_modules/storage/tests/latest/test_storage_file_share_scenarios.py index 147a09fc168..8672a3cee77 100644 --- a/src/azure-cli/azure/cli/command_modules/storage/tests/latest/test_storage_file_share_scenarios.py +++ b/src/azure-cli/azure/cli/command_modules/storage/tests/latest/test_storage_file_share_scenarios.py @@ -13,7 +13,7 @@ class StorageShareScenarioTests(StorageScenarioMixin, ScenarioTest): @ResourceGroupPreparer(name_prefix='clitest') @StorageAccountPreparer(name_prefix='share', kind='StorageV2', location='eastus2', sku='Standard_RAGRS') def test_storage_file_share_scenario(self, resource_group, storage_account): - from datetime import datetime, timedelta + from datetime import UTC, datetime, timedelta account_info = self.get_account_info(resource_group, storage_account) connection_string = self.get_connection_string(resource_group, storage_account) @@ -47,8 +47,8 @@ def test_storage_file_share_scenario(self, resource_group, storage_account): self.assertIn(share_name, share_list, 'The newly created share is not listed.') # Test generate-sas with start, expiry and permissions - start = (datetime.utcnow() + timedelta(hours=1)).strftime('%Y-%m-%dT%H:%MZ') - expiry = (datetime.utcnow() + timedelta(hours=2)).strftime('%Y-%m-%dT%H:%MZ') + start = (datetime.now(UTC) + timedelta(hours=1)).strftime('%Y-%m-%dT%H:%MZ') + expiry = (datetime.now(UTC) + timedelta(hours=2)).strftime('%Y-%m-%dT%H:%MZ') sas = self.storage_cmd('storage share generate-sas -n {} --permissions r --start {} --expiry {}', account_info, share_name, start, expiry).output self.assertIn('sig', sas, 'The sig segment is not in the sas {}'.format(sas)) @@ -164,8 +164,8 @@ def test_storage_share_generate_sas_as_user(self, resource_group, storage_accoun share = self.create_share(account_info) local_file = self.create_temp_file(1024) - from datetime import datetime, timedelta - expiry = (datetime.utcnow() + timedelta(hours=1)).strftime('%Y-%m-%dT%H:%MZ') + from datetime import UTC, datetime, timedelta + expiry = (datetime.now(UTC) + timedelta(hours=1)).strftime('%Y-%m-%dT%H:%MZ') with self.assertRaisesRegex(CLIError, "incorrect usage: specify --as-user when --auth-mode login"): self.cmd('storage share generate-sas --account-name {} -n {} --expiry {} --permissions r --https-only ' @@ -197,8 +197,8 @@ def test_storage_share_generate_sas_with_user_delegation_oid(self, resource_grou current_tenant = self.cmd('account show --query tenantId').get_output_in_json() current_tenant = current_tenant if current_tenant is not None else '544a7a2e-697f-487c-b2b0-a13df7f346b6' - from datetime import datetime, timedelta - expiry = (datetime.utcnow() + timedelta(hours=1)).strftime('%Y-%m-%dT%H:%MZ') + from datetime import UTC, datetime, timedelta + expiry = (datetime.now(UTC) + timedelta(hours=1)).strftime('%Y-%m-%dT%H:%MZ') share_sas = self.cmd('storage share generate-sas --account-name {} -n {} --expiry {} --permissions crwld ' '--https-only --as-user --auth-mode login --backup-intent --user-delegation-oid ' diff --git a/src/azure-cli/azure/cli/command_modules/storage/tests/latest/test_storage_queue_scenarios.py b/src/azure-cli/azure/cli/command_modules/storage/tests/latest/test_storage_queue_scenarios.py index 1e029b40733..600ec3075a3 100644 --- a/src/azure-cli/azure/cli/command_modules/storage/tests/latest/test_storage_queue_scenarios.py +++ b/src/azure-cli/azure/cli/command_modules/storage/tests/latest/test_storage_queue_scenarios.py @@ -14,7 +14,7 @@ class StorageQueueScenarioTests(StorageScenarioMixin, ScenarioTest): @ResourceGroupPreparer(name_prefix='clitest') @StorageAccountPreparer(name_prefix='queue', kind='StorageV2', location='eastus2', sku='Standard_RAGRS') def test_storage_queue_general_scenario(self, resource_group, storage_account): - from datetime import datetime, timedelta + from datetime import UTC, datetime, timedelta account_key = self.get_account_key(resource_group, storage_account) connection_string = self.get_connection_string(resource_group, storage_account) @@ -45,8 +45,8 @@ def test_storage_queue_general_scenario(self, resource_group, storage_account): self.assertIn(queue, [x['name'] for x in res], 'The newly created queue is not listed.') # Test generate-sas with start, expiry and permissions - start = (datetime.utcnow() + timedelta(hours=1)).strftime('%Y-%m-%dT%H:%MZ') - expiry = (datetime.utcnow() + timedelta(hours=2)).strftime('%Y-%m-%dT%H:%MZ') + start = (datetime.now(UTC) + timedelta(hours=1)).strftime('%Y-%m-%dT%H:%MZ') + expiry = (datetime.now(UTC) + timedelta(hours=2)).strftime('%Y-%m-%dT%H:%MZ') sas = self.cmd('storage queue generate-sas -n {} --permissions r --start {} --expiry {}' .format(queue, start, expiry)).output self.assertIn('sig', sas, 'The sig segment is not in the sas {}'.format(sas)) @@ -271,8 +271,8 @@ def test_storage_queue_generate_sas_as_user(self, resource_group, storage_accoun self.cmd('storage queue create --account-name {} -n {}'.format(storage_account, queue)) - from datetime import datetime, timedelta - expiry = (datetime.utcnow() + timedelta(hours=1)).strftime('%Y-%m-%dT%H:%MZ') + from datetime import UTC, datetime, timedelta + expiry = (datetime.now(UTC) + timedelta(hours=1)).strftime('%Y-%m-%dT%H:%MZ') with self.assertRaisesRegex(CLIError, "incorrect usage: specify --as-user when --auth-mode login"): self.cmd('storage queue generate-sas --account-name {} -n {} --expiry {} --permissions r --https-only ' @@ -306,8 +306,8 @@ def test_storage_queue_generate_sas_with_user_delegation_oid(self, resource_grou current_tenant = self.cmd('account show --query tenantId').get_output_in_json() current_tenant = current_tenant if current_tenant is not None else '544a7a2e-697f-487c-b2b0-a13df7f346b6' - from datetime import datetime, timedelta - expiry = (datetime.utcnow() + timedelta(hours=1)).strftime('%Y-%m-%dT%H:%MZ') + from datetime import UTC, datetime, timedelta + expiry = (datetime.now(UTC) + timedelta(hours=1)).strftime('%Y-%m-%dT%H:%MZ') queue_sas = self.cmd('storage queue generate-sas --account-name {} -n {} --expiry {} --permissions raup ' '--https-only --as-user --user-delegation-oid {} ' diff --git a/src/azure-cli/azure/cli/command_modules/storage/tests/latest/test_storage_sas_scenarios.py b/src/azure-cli/azure/cli/command_modules/storage/tests/latest/test_storage_sas_scenarios.py index fa168e7abc2..2f5991049ad 100644 --- a/src/azure-cli/azure/cli/command_modules/storage/tests/latest/test_storage_sas_scenarios.py +++ b/src/azure-cli/azure/cli/command_modules/storage/tests/latest/test_storage_sas_scenarios.py @@ -13,8 +13,8 @@ class StorageSASScenario(StorageScenarioMixin, LiveScenarioTest): @ResourceGroupPreparer() @StorageAccountPreparer(location='EastUS2') def test_storage_file_sas_scenario(self, resource_group, storage_account): - from datetime import datetime, timedelta - expiry = (datetime.utcnow() + timedelta(hours=1)).strftime('%Y-%m-%dT%H:%MZ') + from datetime import UTC, datetime, timedelta + expiry = (datetime.now(UTC) + timedelta(hours=1)).strftime('%Y-%m-%dT%H:%MZ') account_info = self.get_account_info(resource_group, storage_account) share = self.create_share(account_info) @@ -66,8 +66,8 @@ def test_storage_file_sas_scenario(self, resource_group, storage_account): @ResourceGroupPreparer() @StorageAccountPreparer() def test_storage_blob_sas_scenario(self, resource_group, storage_account): - from datetime import datetime, timedelta - expiry = (datetime.utcnow() + timedelta(hours=1)).strftime('%Y-%m-%dT%H:%MZ') + from datetime import UTC, datetime, timedelta + expiry = (datetime.now(UTC) + timedelta(hours=1)).strftime('%Y-%m-%dT%H:%MZ') account_info = self.get_account_info(resource_group, storage_account) container = self.create_container(account_info) @@ -131,8 +131,8 @@ def test_storage_blob_sas_scenario(self, resource_group, storage_account): @ResourceGroupPreparer(name_prefix='clitest') @StorageAccountPreparer(name_prefix='queuesas', kind='StorageV2', location='eastus2', sku='Standard_RAGRS') def test_storage_queue_sas_scenario(self, resource_group, storage_account): - from datetime import datetime, timedelta - expiry = (datetime.utcnow() + timedelta(hours=1)).strftime('%Y-%m-%dT%H:%MZ') + from datetime import UTC, datetime, timedelta + expiry = (datetime.now(UTC) + timedelta(hours=1)).strftime('%Y-%m-%dT%H:%MZ') account_info = self.get_account_info(resource_group, storage_account) queue = self.create_random_name('queue', 24) @@ -157,8 +157,8 @@ def test_storage_queue_sas_scenario(self, resource_group, storage_account): @ResourceGroupPreparer() @StorageAccountPreparer() def test_storage_account_sas_scenario(self, resource_group, storage_account): - from datetime import datetime, timedelta - expiry = (datetime.utcnow() + timedelta(hours=1)).strftime('%Y-%m-%dT%H:%MZ') + from datetime import UTC, datetime, timedelta + expiry = (datetime.now(UTC) + timedelta(hours=1)).strftime('%Y-%m-%dT%H:%MZ') connection_str = self.cmd('storage account show-connection-string -n {} -otsv'.format(storage_account)).output scope_name = self.create_random_name('scope', 16) @@ -206,8 +206,8 @@ def test_storage_account_sas_scenario(self, resource_group, storage_account): @ResourceGroupPreparer() @StorageAccountPreparer(hns=True, kind='StorageV2') def test_storage_fs_sas_scenario(self, resource_group, storage_account): - from datetime import datetime, timedelta - expiry = (datetime.utcnow() + timedelta(hours=1)).strftime('%Y-%m-%dT%H:%MZ') + from datetime import UTC, datetime, timedelta + expiry = (datetime.now(UTC) + timedelta(hours=1)).strftime('%Y-%m-%dT%H:%MZ') account_info = self.get_account_info(resource_group, storage_account) file_system = self.create_file_system(account_info) @@ -314,8 +314,8 @@ def test_storage_fs_sas_scenario(self, resource_group, storage_account): @ResourceGroupPreparer() @StorageAccountPreparer(hns=True, kind='StorageV2') def test_storage_fs_sas_with_encryption_scope(self, resource_group, storage_account_info): - from datetime import datetime, timedelta - expiry = (datetime.utcnow() + timedelta(hours=1)).strftime('%Y-%m-%dT%H:%MZ') + from datetime import UTC, datetime, timedelta + expiry = (datetime.now(UTC) + timedelta(hours=1)).strftime('%Y-%m-%dT%H:%MZ') storage_account, account_key = storage_account_info file_system = self.create_file_system(storage_account_info) @@ -345,8 +345,8 @@ def test_storage_table_entity_sas(self, resource_group, storage_account): self.storage_cmd('storage table create -n {} --fail-on-exist', account_info, table_name) \ .assert_with_checks(JMESPathCheck('created', True)) - from datetime import datetime, timedelta - expiry = (datetime.utcnow() + timedelta(hours=1)).strftime('%Y-%m-%dT%H:%MZ') + from datetime import UTC, datetime, timedelta + expiry = (datetime.now(UTC) + timedelta(hours=1)).strftime('%Y-%m-%dT%H:%MZ') sas_token_a = self.storage_cmd('storage table generate-sas -n {} --expiry {} --permissions a', account_info, table_name, expiry).get_output_in_json() account_name = account_info[0] diff --git a/src/azure-cli/azure/cli/command_modules/storage/tests/latest/test_storage_sas_tokan_url_scenarios.py b/src/azure-cli/azure/cli/command_modules/storage/tests/latest/test_storage_sas_tokan_url_scenarios.py index e36fedc7c8b..5add177a934 100644 --- a/src/azure-cli/azure/cli/command_modules/storage/tests/latest/test_storage_sas_tokan_url_scenarios.py +++ b/src/azure-cli/azure/cli/command_modules/storage/tests/latest/test_storage_sas_tokan_url_scenarios.py @@ -5,7 +5,7 @@ import os import requests -from datetime import datetime, timedelta +from datetime import UTC, datetime, timedelta from azure.cli.testsdk import (LiveScenarioTest, ResourceGroupPreparer, StorageAccountPreparer, JMESPathCheck, NoneCheck, StringCheck, StringContainCheck) from ..storage_test_util import StorageScenarioMixin @@ -19,7 +19,7 @@ def test_storage_file_url_sas_token_scenario(self, resource_group, storage_accou share = self.create_share(account_info) source_file = self.create_temp_file(128, full_random=False) filename = "sample_file.bin" - expiry = (datetime.utcnow() + timedelta(hours=1)).strftime('%Y-%m-%dT%H:%MZ') + expiry = (datetime.now(UTC) + timedelta(hours=1)).strftime('%Y-%m-%dT%H:%MZ') self.storage_cmd('storage share exists -n {}', account_info, share) \ .assert_with_checks(JMESPathCheck('exists', True)) @@ -48,7 +48,7 @@ def test_storage_blob_url_sas_token_scenario(self, resource_group, storage_accou container = self.create_container(account_info) source_file = self.create_temp_file(128, full_random=False) blob = "blob 1" - expiry = (datetime.utcnow() + timedelta(hours=1)).strftime('%Y-%m-%dT%H:%MZ') + expiry = (datetime.now(UTC) + timedelta(hours=1)).strftime('%Y-%m-%dT%H:%MZ') self.storage_cmd('storage container exists -n {}', account_info, container) \ .assert_with_checks(JMESPathCheck('exists', True)) diff --git a/src/azure-cli/azure/cli/command_modules/storage/tests/latest/test_storage_table_scenarios.py b/src/azure-cli/azure/cli/command_modules/storage/tests/latest/test_storage_table_scenarios.py index f3e9d35ecc3..2394624f9b7 100644 --- a/src/azure-cli/azure/cli/command_modules/storage/tests/latest/test_storage_table_scenarios.py +++ b/src/azure-cli/azure/cli/command_modules/storage/tests/latest/test_storage_table_scenarios.py @@ -12,7 +12,7 @@ class StorageTableScenarioTests(StorageScenarioMixin, ScenarioTest): @ResourceGroupPreparer() @StorageAccountPreparer(sku='Standard_RAGRS') def test_storage_table_main_scenario(self, resource_group, storage_account): - from datetime import datetime, timedelta + from datetime import UTC, datetime, timedelta account_info = self.get_account_info(resource_group, storage_account) table_name = self.create_random_name('table', 24) @@ -24,7 +24,7 @@ def test_storage_table_main_scenario(self, resource_group, storage_account): self.assertIn(table_name, self.storage_cmd('storage table list --query "[].name"', account_info).get_output_in_json()) - expiry = (datetime.utcnow() + timedelta(hours=1)).strftime('%Y-%m-%dT%H:%MZ') + expiry = (datetime.now(UTC) + timedelta(hours=1)).strftime('%Y-%m-%dT%H:%MZ') sas = self.storage_cmd('storage table generate-sas -n {} --permissions r --expiry {}', account_info, table_name, expiry).output self.assertIn('sig=', sas) diff --git a/src/azure-cli/azure/cli/command_modules/storage/tests/latest/test_storage_validators.py b/src/azure-cli/azure/cli/command_modules/storage/tests/latest/test_storage_validators.py index fba10572ff3..f293eb37700 100644 --- a/src/azure-cli/azure/cli/command_modules/storage/tests/latest/test_storage_validators.py +++ b/src/azure-cli/azure/cli/command_modules/storage/tests/latest/test_storage_validators.py @@ -442,6 +442,11 @@ def test_as_user_validator(self): ns = self._create_namespace(as_user=True, expiry='2019-01-01', token_credential='token') as_user_validator(ns) + def test_as_user_validator_rejects_expiry_beyond_seven_days(self): + ns = self._create_namespace(as_user=True, expiry='2999-01-01', token_credential='token') + with self.assertRaisesRegex(ArgumentError, 'within 7 days'): + as_user_validator(ns) + def _create_namespace(self, **kwargs): ns = Namespace(account_key='my_storage_key', account_name='storage_name', connection_string=None, dryrun=False, pattern='*', sas_token=None, source_account_key=None, source_account_name=None, diff --git a/src/azure-cli/azure/cli/command_modules/storage/util.py b/src/azure-cli/azure/cli/command_modules/storage/util.py index af8700b831c..49fa2ef1976 100644 --- a/src/azure-cli/azure/cli/command_modules/storage/util.py +++ b/src/azure-cli/azure/cli/command_modules/storage/util.py @@ -6,7 +6,7 @@ import os from azure.cli.core.profiles import ResourceType -from datetime import datetime +from datetime import UTC, datetime def collect_blobs(blob_service, container, pattern=None): @@ -109,7 +109,7 @@ def create_short_lived_blob_sas_v2(cmd, account_name, container, blob, account_k resource_type=ResourceType.DATA_STORAGE_BLOB) t_blob_permissions = cmd.get_models('_models#BlobSasPermissions', resource_type=ResourceType.DATA_STORAGE_BLOB) - expiry = (datetime.utcnow() + timedelta(days=1)).strftime('%Y-%m-%dT%H:%M:%SZ') + expiry = (datetime.now(UTC) + timedelta(days=1)).strftime('%Y-%m-%dT%H:%M:%SZ') if account_key: sas = t_sas(account_name, account_key=account_key) elif user_delegation_key: @@ -126,7 +126,7 @@ def create_short_lived_file_sas_v2(cmd, account_name, account_key, share, direct resource_type=ResourceType.DATA_STORAGE_FILESHARE) t_file_permissions = cmd.get_models('_models#FileSasPermissions', resource_type=ResourceType.DATA_STORAGE_FILESHARE) - expiry = (datetime.utcnow() + timedelta(days=1)).strftime('%Y-%m-%dT%H:%M:%SZ') + expiry = (datetime.now(UTC) + timedelta(days=1)).strftime('%Y-%m-%dT%H:%M:%SZ') sas = t_sas(account_name, account_key) return sas.generate_file(share, directory_name=directory_name, file_name=file_name, permission=t_file_permissions(read=True), expiry=expiry, protocol='https') @@ -136,7 +136,7 @@ def create_short_lived_container_sas_track2(cmd, account_name, account_key, cont from datetime import timedelta t_generate_container_sas = cmd.get_models('_shared_access_signature#generate_container_sas', resource_type=ResourceType.DATA_STORAGE_BLOB) - expiry = (datetime.utcnow() + timedelta(days=1)).strftime('%Y-%m-%dT%H:%M:%SZ') + expiry = (datetime.now(UTC) + timedelta(days=1)).strftime('%Y-%m-%dT%H:%M:%SZ') return t_generate_container_sas(account_name, container, account_key, permission='r', expiry=expiry, protocol='https') @@ -144,7 +144,7 @@ def create_short_lived_container_sas_track2(cmd, account_name, account_key, cont def create_short_lived_share_sas_track2(cmd, account_name, account_key, share): from datetime import timedelta t_generate_share_sas = cmd.get_models('#generate_share_sas', resource_type=ResourceType.DATA_STORAGE_FILESHARE) - expiry = (datetime.utcnow() + timedelta(days=1)).strftime('%Y-%m-%dT%H:%M:%SZ') + expiry = (datetime.now(UTC) + timedelta(days=1)).strftime('%Y-%m-%dT%H:%M:%SZ') return t_generate_share_sas(account_name, share, account_key, permission='r', expiry=expiry, protocol='https') @@ -152,7 +152,7 @@ def create_short_lived_share_sas_track2(cmd, account_name, account_key, share): def create_short_lived_blob_service_sas_track2(cmd, account_name, account_key, resource_types, permission='r'): from datetime import timedelta t_generate_blob_service_sas = cmd.get_models('#generate_account_sas', resource_type=ResourceType.DATA_STORAGE_BLOB) - expiry = (datetime.utcnow() + timedelta(days=1)).strftime('%Y-%m-%dT%H:%M:%SZ') + expiry = (datetime.now(UTC) + timedelta(days=1)).strftime('%Y-%m-%dT%H:%M:%SZ') return t_generate_blob_service_sas(account_name, account_key, resource_types, permission=permission, expiry=expiry) @@ -160,7 +160,7 @@ def create_short_lived_file_service_sas_track2(cmd, account_name, account_key, r from datetime import timedelta t_generate_file_service_sas = cmd.get_models('#generate_account_sas', resource_type=ResourceType.DATA_STORAGE_FILESHARE) - expiry = (datetime.utcnow() + timedelta(days=1)).strftime('%Y-%m-%dT%H:%M:%SZ') + expiry = (datetime.now(UTC) + timedelta(days=1)).strftime('%Y-%m-%dT%H:%M:%SZ') return t_generate_file_service_sas(account_name, account_key, resource_types, permission=permission, expiry=expiry) diff --git a/src/azure-cli/pyproject.toml b/src/azure-cli/pyproject.toml index fc3095d385c..70a56f71444 100644 --- a/src/azure-cli/pyproject.toml +++ b/src/azure-cli/pyproject.toml @@ -7,7 +7,7 @@ name = "azure-cli" version = "2.91.0" description = "Microsoft Azure Command-Line Tools" readme = { file = "README.rst", content-type = "text/x-rst" } -requires-python = ">=3.10.0" +requires-python = ">=3.11.0" license = "MIT" license-files = ["LICENSE.txt"] authors = [{ name = "Microsoft Corporation", email = "azpycli@microsoft.com" }] @@ -17,7 +17,6 @@ classifiers = [ "Intended Audience :: System Administrators", "Programming Language :: Python", "Programming Language :: Python :: 3", - "Programming Language :: Python :: 3.10", "Programming Language :: Python :: 3.11", "Programming Language :: Python :: 3.12", "Programming Language :: Python :: 3.13", From 5b35c11184dd5df103a51ef0c57910f2f0c3f5bd Mon Sep 17 00:00:00 2001 From: NostraDavid <55331731+NostraDavid@users.noreply.github.com> Date: Wed, 14 Oct 2026 20:35:03 +0200 Subject: [PATCH 02/13] datetime: use the dt module alias consistently --- scripts/ci/codegen_report.py | 4 +- .../azure/cli/core/commands/__init__.py | 20 +++---- .../azure/cli/core/intercept_survey.py | 13 +++-- .../azure/cli/core/telemetry.py | 12 ++-- .../azure/cli/testsdk/preparers.py | 6 +- .../azure/cli/command_modules/acr/_format.py | 4 +- .../azure/cli/command_modules/acr/_utils.py | 6 +- .../acs/managed_cluster_decorator.py | 4 +- .../acs/tests/latest/custom_preparers.py | 4 +- .../acs/tests/latest/test_custom.py | 20 +++---- .../latest/test_managed_cluster_decorator.py | 6 +- .../appservice/_github_oauth.py | 6 +- .../appservice/appservice_domains.py | 4 +- .../cli/command_modules/appservice/custom.py | 52 ++++++++--------- .../appservice/static_sites.py | 4 +- .../cli/command_modules/backup/custom.py | 10 ++-- .../backup/tests/latest/preparers.py | 6 +- .../backup/tests/latest/test_afs_commands.py | 6 +- .../tests/latest/test_backup_commands.py | 8 +-- .../cli/command_modules/batchai/custom.py | 6 +- .../containerapp/_github_oauth.py | 6 +- .../command_modules/containerapp/_utils.py | 18 +++--- .../cli/command_modules/cosmosdb/custom.py | 58 +++++++++---------- .../test_cosmosdb_backuprestore_scenario.py | 14 ++--- .../tests/latest/test_cosmosdb_commands.py | 51 ++++++++-------- .../tests/latest/test_eventgrid_commands.py | 10 ++-- .../cli/command_modules/feedback/custom.py | 20 +++---- .../feedback/tests/latest/test_feedback.py | 6 +- .../azure/cli/command_modules/iot/_utils.py | 6 +- .../iot/tests/latest/_test_utils.py | 10 ++-- .../tests/latest/test_keyvault_commands.py | 28 ++++----- .../cli/command_modules/lab/validators.py | 6 +- .../command_modules/monitor/_legacy/custom.py | 8 +-- .../cli/command_modules/monitor/custom.py | 8 +-- .../azure/cli/command_modules/mysql/_util.py | 8 +-- .../azure/cli/command_modules/mysql/custom.py | 6 +- .../mysql/tests/latest/test_mysql_scenario.py | 14 ++--- .../tests/latest/test_bucket_commands.py | 6 +- .../tests/latest/test_network_commands.py | 36 ++++++------ .../postgresql/commands/backup_commands.py | 4 +- .../commands/server_logs_commands.py | 6 +- .../postgresql/utils/_flexible_server_util.py | 4 +- .../rdbms/_flexible_server_util.py | 4 +- .../azure/cli/command_modules/rdbms/custom.py | 4 +- .../rdbms/flexible_server_custom_common.py | 6 +- .../rdbms/tests/latest/test_rdbms_commands.py | 6 +- .../redis/tests/latest/test_redis_scenario.py | 6 +- .../resource/tests/latest/test_resource.py | 8 +-- .../azure/cli/command_modules/role/custom.py | 35 ++++++----- .../role/tests/latest/test_role.py | 8 +-- .../sql/tests/latest/test_sql_commands.py | 36 ++++++------ .../command_modules/storage/_validators.py | 20 +++---- .../storage/operations/blob.py | 21 ++++--- .../storage/operations/file.py | 24 ++++---- .../storage/operations/fileshare.py | 4 +- .../storage/operations/filesystem.py | 4 +- .../storage/operations/fs_directory.py | 4 +- .../storage/operations/fs_file.py | 4 +- .../storage/operations/queue.py | 4 +- .../latest/test_storage_account_scenarios.py | 5 +- .../test_storage_adls_gen2_scenarios.py | 27 ++++----- .../latest/test_storage_azcopy_scenarios.py | 12 ++-- .../latest/test_storage_batch_operations.py | 18 +++--- .../test_storage_blob_copy_scenarios.py | 12 ++-- .../test_storage_blob_live_scenarios.py | 6 +- .../test_storage_blob_rewrite_scenarios.py | 4 +- .../latest/test_storage_blob_scenarios.py | 26 ++++----- .../test_storage_container_access_policy.py | 8 +-- .../latest/test_storage_file_scenarios.py | 12 ++-- .../test_storage_file_share_scenarios.py | 14 ++--- .../latest/test_storage_queue_scenarios.py | 14 ++--- .../latest/test_storage_sas_scenarios.py | 28 ++++----- .../test_storage_sas_tokan_url_scenarios.py | 6 +- .../latest/test_storage_table_scenarios.py | 4 +- .../tests/latest/test_storage_validators.py | 4 +- .../azure/cli/command_modules/storage/util.py | 22 +++---- 76 files changed, 463 insertions(+), 491 deletions(-) diff --git a/scripts/ci/codegen_report.py b/scripts/ci/codegen_report.py index f0b1a552549..2b2fe9d9092 100644 --- a/scripts/ci/codegen_report.py +++ b/scripts/ci/codegen_report.py @@ -5,7 +5,7 @@ # Licensed under the MIT License. See License.txt in the project root for license information. # -------------------------------------------------------------------------------------------- -import datetime +import datetime as dt import json import logging import os @@ -111,7 +111,7 @@ def generate_csv(): total = ref['total'] manual = total - codegenv1 - codegenv2 is_release = True if BUILD_BRANCH == 'release' else False - date = (datetime.datetime.now(datetime.UTC) + datetime.timedelta(hours=8)).strftime("%Y-%m-%d") + date = (dt.datetime.now(dt.UTC) + dt.timedelta(hours=8)).strftime("%Y-%m-%d") data.append([BUILD_ID, manual, codegenv1, codegenv2, total, is_release, date]) logger.info('Finish generate data for codegen report:') logger.info("BUILD_ID, manual, codegenv1, codegenv2, total, is_release, date") diff --git a/src/azure-cli-core/azure/cli/core/commands/__init__.py b/src/azure-cli-core/azure/cli/core/commands/__init__.py index 11dd3cb98d8..bda0f37cc0d 100644 --- a/src/azure-cli-core/azure/cli/core/commands/__init__.py +++ b/src/azure-cli-core/azure/cli/core/commands/__init__.py @@ -6,7 +6,7 @@ # pylint: disable=too-many-lines import argparse -import datetime +import datetime as dt import json import logging as logs import os @@ -218,7 +218,7 @@ def save(self, args, kwargs): "Caching %s '%s' as: %s", self._model_name, self._resource_name, os.path.join(directory, filename) ) - self.last_saved = str(datetime.datetime.now()) + self.last_saved = str(dt.datetime.now()) self._dump_to_file(f) def result(self): @@ -396,9 +396,9 @@ def _is_stale(cli_ctx, cache_obj): cache_ttl = DEFAULT_CACHE_TTL else: raise ex - time_now = datetime.datetime.now() - time_cache = datetime.datetime.strptime(cache_obj.last_saved, '%Y-%m-%d %H:%M:%S.%f') - return time_now - time_cache > datetime.timedelta(minutes=int(cache_ttl)) + time_now = dt.datetime.now() + time_cache = dt.datetime.strptime(cache_obj.last_saved, '%Y-%m-%d %H:%M:%S.%f') + return time_now - time_cache > dt.timedelta(minutes=int(cache_ttl)) def cached_get(cmd_obj, operation, *args, **kwargs): @@ -1032,7 +1032,7 @@ def __init__(self, cli_ctx, start_msg='', finish_msg='', poller_done_interval_ms self.finish_msg = finish_msg self.poller_done_interval_ms = poller_done_interval_ms self.deploy_dict = {} - self.last_progress_report = datetime.datetime.now() + self.last_progress_report = dt.datetime.now() self._progress_bar = None disable_progress_bar = self.cli_ctx.config.getboolean('core', 'disable_progress_bar', False) @@ -1060,8 +1060,8 @@ def _generate_template_progress(self, correlation_id): # pylint: disable=no-sel if correlation_id is not None: # pylint: disable=too-many-nested-blocks formatter = "eventTimestamp ge {}" - end_time = datetime.datetime.now(datetime.UTC) - start_time = end_time - datetime.timedelta(seconds=DEFAULT_QUERY_TIME_RANGE) + end_time = dt.datetime.now(dt.UTC) + start_time = end_time - dt.timedelta(seconds=DEFAULT_QUERY_TIME_RANGE) odata_filters = formatter.format(start_time.strftime('%Y-%m-%dT%H:%M:%SZ')) odata_filters = "{} and {} eq '{}'".format(odata_filters, 'correlationId', correlation_id) @@ -1141,8 +1141,8 @@ def __call__(self, poller): # pylint: disable=too-many-statements except: # pylint: disable=bare-except pass - current_time = datetime.datetime.now() - if is_verbose and current_time - self.last_progress_report >= datetime.timedelta(seconds=10): + current_time = dt.datetime.now() + if is_verbose and current_time - self.last_progress_report >= dt.timedelta(seconds=10): self.last_progress_report = current_time try: self._generate_template_progress(correlation_id) diff --git a/src/azure-cli-core/azure/cli/core/intercept_survey.py b/src/azure-cli-core/azure/cli/core/intercept_survey.py index 1f4b4db363f..f7267494c21 100644 --- a/src/azure-cli-core/azure/cli/core/intercept_survey.py +++ b/src/azure-cli-core/azure/cli/core/intercept_survey.py @@ -6,7 +6,7 @@ import os import sys import json -from datetime import UTC, datetime, timedelta +import datetime as dt from azure.cli.core._config import GLOBAL_CONFIG_DIR from azure.cli.core._profile import Profile from azure.cli.core.style import print_styled_text @@ -56,7 +56,7 @@ def should_prompt(cli): # using their installationId installation_id = Profile(cli_ctx=cli).get_installation_id() prompt_period = EXPERIENCE_PERIOD_IN_DAYS + (uuid.UUID(installation_id).int & 127) - next_prompt_time = datetime.now(UTC) + timedelta(days=prompt_period) + next_prompt_time = dt.datetime.now(dt.UTC) + dt.timedelta(days=prompt_period) survey_note = { 'last_prompt_time': '', 'next_prompt_time': next_prompt_time.strftime('%Y-%m-%dT%H:%M:%S') @@ -67,8 +67,9 @@ def should_prompt(cli): with open(GLOBAL_SURVEY_NOTE_PATH, 'r', encoding='utf-8-sig') as f: survey_note = json.load(f) - next_prompt_time = datetime.strptime(survey_note['next_prompt_time'], '%Y-%m-%dT%H:%M:%S').replace(tzinfo=UTC) - if datetime.now(UTC) < next_prompt_time: + next_prompt_time = dt.datetime.strptime( + survey_note['next_prompt_time'], '%Y-%m-%dT%H:%M:%S').replace(tzinfo=dt.UTC) + if dt.datetime.now(dt.UTC) < next_prompt_time: return False return True @@ -92,9 +93,9 @@ def prompt_survey_message(cli): telemetry.set_survey_info(show_survey_message=True) # log prompt time - next_prompt_time = datetime.now(UTC) + timedelta(days=PROMPT_INTERVAL_IN_DAYS) + next_prompt_time = dt.datetime.now(dt.UTC) + dt.timedelta(days=PROMPT_INTERVAL_IN_DAYS) survey_note = { - 'last_prompt_time': datetime.now(UTC).strftime('%Y-%m-%dT%H:%M:%S'), + 'last_prompt_time': dt.datetime.now(dt.UTC).strftime('%Y-%m-%dT%H:%M:%S'), 'next_prompt_time': next_prompt_time.strftime('%Y-%m-%dT%H:%M:%S') } with open(GLOBAL_SURVEY_NOTE_PATH, 'w') as f: diff --git a/src/azure-cli-core/azure/cli/core/telemetry.py b/src/azure-cli-core/azure/cli/core/telemetry.py index c65611bffd4..5aa2de06de2 100644 --- a/src/azure-cli-core/azure/cli/core/telemetry.py +++ b/src/azure-cli-core/azure/cli/core/telemetry.py @@ -3,7 +3,7 @@ # Licensed under the MIT License. See License.txt in the project root for license information. # -------------------------------------------------------------------------------------------- -import datetime +import datetime as dt import json import locale import os @@ -292,7 +292,7 @@ def _wrapper(*args, **kwargs): def start(mode=None): if mode: _session.mode = mode - _session.start_time = datetime.datetime.now(datetime.UTC) + _session.start_time = dt.datetime.now(dt.UTC) @decorators.suppress_all_exceptions() @@ -307,12 +307,12 @@ def set_invoke_time_elapsed(invoke_time_elapsed): @decorators.suppress_all_exceptions() def poll_start(): - _session.poll_start_time = datetime.datetime.now(datetime.UTC) + _session.poll_start_time = dt.datetime.now(dt.UTC) @decorators.suppress_all_exceptions() def poll_end(): - _session.poll_end_time = datetime.datetime.now(datetime.UTC) + _session.poll_end_time = dt.datetime.now(dt.UTC) @_user_agrees_to_telemetry @@ -322,7 +322,7 @@ def flush(): from azure.cli.telemetry import save # flush out current information - _session.end_time = datetime.datetime.now(datetime.UTC) + _session.end_time = dt.datetime.now(dt.UTC) save(get_config_dir(), _session.generate_payload()) # reset session fields, retaining correlation id and application @@ -335,7 +335,7 @@ def conclude(): from azure.cli.core._environment import get_config_dir from azure.cli.telemetry import save - _session.end_time = datetime.datetime.now(datetime.UTC) + _session.end_time = dt.datetime.now(dt.UTC) save(get_config_dir(), _session.generate_payload()) diff --git a/src/azure-cli-testsdk/azure/cli/testsdk/preparers.py b/src/azure-cli-testsdk/azure/cli/testsdk/preparers.py index 92d8588c2be..8be44f3000b 100644 --- a/src/azure-cli-testsdk/azure/cli/testsdk/preparers.py +++ b/src/azure-cli-testsdk/azure/cli/testsdk/preparers.py @@ -5,7 +5,7 @@ import os import sys -from datetime import UTC, datetime +import datetime as dt from .scenario_tests import AbstractPreparer, SingleValueReplacer from .base import LiveScenarioTest @@ -85,7 +85,7 @@ def create_resource(self, name, **kwargs): module = test_class_path[index_of_module] if index_of_module >= 0 else 'unknown' tags = {'product': 'azurecli', 'cause': 'automation test', - 'date': datetime.now(UTC).strftime('%Y-%m-%dT%H:%M:%SZ'), + 'date': dt.datetime.now(dt.UTC).strftime('%Y-%m-%dT%H:%M:%SZ'), 'test': self.test_class_instance._testMethodName, 'module': module} if 'ENV_JOB_NAME' in os.environ: @@ -419,7 +419,7 @@ def create_resource(self, name, **kwargs): return {self.parameter_name: self.dev_setting_name, } tags = {'product': 'azurecli', 'cause': 'automation', - 'date': datetime.now(UTC).strftime('%Y-%m-%dT%H:%M:%SZ')} + 'date': dt.datetime.now(dt.UTC).strftime('%Y-%m-%dT%H:%M:%SZ')} if 'ENV_JOB_NAME' in os.environ: tags['job'] = os.environ['ENV_JOB_NAME'] tags = ' '.join(['{}={}'.format(key, value) diff --git a/src/azure-cli/azure/cli/command_modules/acr/_format.py b/src/azure-cli/azure/cli/command_modules/acr/_format.py index 7df8ad10c69..2d6d5ff2da8 100644 --- a/src/azure-cli/azure/cli/command_modules/acr/_format.py +++ b/src/azure-cli/azure/cli/command_modules/acr/_format.py @@ -4,7 +4,7 @@ # -------------------------------------------------------------------------------------------- from collections import OrderedDict -from datetime import UTC, datetime +import datetime as dt from knack.log import get_logger from ._constants import ( REF_KEY @@ -530,4 +530,4 @@ def _get_duration(start_time, finish_time): def add_timestamp(message): - return "{} {}".format(datetime.now(UTC), message) + return "{} {}".format(dt.datetime.now(dt.UTC), message) diff --git a/src/azure-cli/azure/cli/command_modules/acr/_utils.py b/src/azure-cli/azure/cli/command_modules/acr/_utils.py index 7a2d175445a..36a198d3e0a 100644 --- a/src/azure-cli/azure/cli/command_modules/acr/_utils.py +++ b/src/azure-cli/azure/cli/command_modules/acr/_utils.py @@ -415,11 +415,11 @@ def remove_timer_trigger(task_name, def add_days_to_now(days): if days <= 0: raise CLIError('Days must be positive.') - from datetime import UTC, datetime, timedelta + import datetime as dt try: - return datetime.now(UTC) + timedelta(days=days) + return dt.datetime.now(dt.UTC) + dt.timedelta(days=days) except OverflowError: - return datetime.max.replace(tzinfo=UTC) + return dt.datetime.max.replace(tzinfo=dt.UTC) def is_vault_secret(cmd, credential): diff --git a/src/azure-cli/azure/cli/command_modules/acs/managed_cluster_decorator.py b/src/azure-cli/azure/cli/command_modules/acs/managed_cluster_decorator.py index fb007b9763d..a8c7bb07abc 100644 --- a/src/azure-cli/azure/cli/command_modules/acs/managed_cluster_decorator.py +++ b/src/azure-cli/azure/cli/command_modules/acs/managed_cluster_decorator.py @@ -11,7 +11,7 @@ import time from types import SimpleNamespace from typing import Dict, List, Optional, Tuple, TypeVar, Union -import datetime +import datetime as dt from dateutil.parser import parse from azure.mgmt.containerservice.models import KubernetesSupportPlan @@ -9601,7 +9601,7 @@ def update_upgrade_settings(self, mc: ManagedCluster) -> ManagedCluster: f"{override_until} is not a valid datatime format." ) elif force_upgrade: - default_extended_until = datetime.datetime.now(datetime.UTC) + datetime.timedelta(days=3) + default_extended_until = dt.datetime.now(dt.UTC) + dt.timedelta(days=3) if existing_until is None or existing_until.timestamp() < default_extended_until.timestamp(): mc.upgrade_settings.override_settings.until = default_extended_until diff --git a/src/azure-cli/azure/cli/command_modules/acs/tests/latest/custom_preparers.py b/src/azure-cli/azure/cli/command_modules/acs/tests/latest/custom_preparers.py index fe8db67be60..60c826174f7 100644 --- a/src/azure-cli/azure/cli/command_modules/acs/tests/latest/custom_preparers.py +++ b/src/azure-cli/azure/cli/command_modules/acs/tests/latest/custom_preparers.py @@ -3,7 +3,7 @@ # Licensed under the MIT License. See License.txt in the project root for license information. # -------------------------------------------------------------------------------------------- -from datetime import UTC, datetime +import datetime as dt import os import unittest @@ -126,7 +126,7 @@ def create_resource(self, name, **kwargs): tags = { "product": "azurecli", "cause": "automation", - "date": datetime.now(UTC).strftime("%Y-%m-%dT%H:%M:%SZ"), + "date": dt.datetime.now(dt.UTC).strftime("%Y-%m-%dT%H:%M:%SZ"), } if "ENV_JOB_NAME" in os.environ: tags["job"] = os.environ["ENV_JOB_NAME"] diff --git a/src/azure-cli/azure/cli/command_modules/acs/tests/latest/test_custom.py b/src/azure-cli/azure/cli/command_modules/acs/tests/latest/test_custom.py index efaa08a39aa..ce9218b7070 100644 --- a/src/azure-cli/azure/cli/command_modules/acs/tests/latest/test_custom.py +++ b/src/azure-cli/azure/cli/command_modules/acs/tests/latest/test_custom.py @@ -14,7 +14,7 @@ import unittest from unittest import mock from urllib.error import HTTPError, URLError -import datetime +import datetime as dt from contextlib import contextmanager from dateutil.parser import parse @@ -2140,8 +2140,8 @@ def test_update_upgrade_settings(self, rg_def, get_resource_groups_client, get_r instance.upgrade_settings = None instance = _update_upgrade_settings(MockCmd(self.cli), instance, enable_force_upgrade=True, disable_force_upgrade=False, upgrade_override_until=None) self.assertTrue(instance.upgrade_settings.override_settings.force_upgrade) - self.assertGreater(instance.upgrade_settings.override_settings.until.timestamp(), (datetime.datetime.now(datetime.UTC) + datetime.timedelta(days=2)).timestamp()) - self.assertLess(instance.upgrade_settings.override_settings.until.timestamp(), (datetime.datetime.now(datetime.UTC) + datetime.timedelta(days=4)).timestamp()) + self.assertGreater(instance.upgrade_settings.override_settings.until.timestamp(), (dt.datetime.now(dt.UTC) + dt.timedelta(days=2)).timestamp()) + self.assertLess(instance.upgrade_settings.override_settings.until.timestamp(), (dt.datetime.now(dt.UTC) + dt.timedelta(days=4)).timestamp()) instance.upgrade_settings = self.models.ClusterUpgradeSettings( override_settings = self.models.UpgradeOverrideSettings( @@ -2150,8 +2150,8 @@ def test_update_upgrade_settings(self, rg_def, get_resource_groups_client, get_r ) instance = _update_upgrade_settings(MockCmd(self.cli), instance, enable_force_upgrade=True, disable_force_upgrade=False, upgrade_override_until=None) self.assertTrue(instance.upgrade_settings.override_settings.force_upgrade) - self.assertGreater(instance.upgrade_settings.override_settings.until.timestamp(), (datetime.datetime.now(datetime.UTC) + datetime.timedelta(days=2)).timestamp()) - self.assertLess(instance.upgrade_settings.override_settings.until.timestamp(), (datetime.datetime.now(datetime.UTC) + datetime.timedelta(days=4)).timestamp()) + self.assertGreater(instance.upgrade_settings.override_settings.until.timestamp(), (dt.datetime.now(dt.UTC) + dt.timedelta(days=2)).timestamp()) + self.assertLess(instance.upgrade_settings.override_settings.until.timestamp(), (dt.datetime.now(dt.UTC) + dt.timedelta(days=4)).timestamp()) # force_upgrade False instance.upgrade_settings = None @@ -2987,7 +2987,7 @@ def test_aks_agentpool_get_rollback_versions_returns_recently_used_versions(self mock.Mock( orchestrator_version="1.32.1", node_image_version="AKSUbuntu-2204gen2containerd-202605.12.0", - timestamp=datetime.datetime(2026, 5, 1), + timestamp=dt.datetime(2026, 5, 1), ) ] upgrade_profile = mock.Mock(recently_used_versions=versions) @@ -3005,12 +3005,12 @@ def test_aks_agentpool_rollback_uses_most_recent_version(self, mock_sdk_no_wait, older_version = mock.Mock( orchestrator_version="1.31.9", node_image_version="AKSUbuntu-2204gen2containerd-202604.10.0", - timestamp=datetime.datetime(2026, 4, 1), + timestamp=dt.datetime(2026, 4, 1), ) most_recent_version = mock.Mock( orchestrator_version="1.32.1", node_image_version="AKSUbuntu-2204gen2containerd-202605.12.0", - timestamp=datetime.datetime(2026, 5, 1), + timestamp=dt.datetime(2026, 5, 1), ) upgrade_profile = mock.Mock(recently_used_versions=[older_version, most_recent_version]) agentpool = mock.Mock() @@ -3064,7 +3064,7 @@ def test_aks_agentpool_rollback_warns_node_image_only_for_node_os_channel( rollback_version = mock.Mock( orchestrator_version="1.34.8", node_image_version="AKSUbuntu-2204gen2containerd-202607.20.0", - timestamp=datetime.datetime(2026, 8, 5), + timestamp=dt.datetime(2026, 8, 5), ) client = mock.Mock() client.get_upgrade_profile.return_value = mock.Mock(recently_used_versions=[rollback_version]) @@ -3094,7 +3094,7 @@ def test_aks_agentpool_rollback_prioritizes_upgrade_channel_warning( rollback_version = mock.Mock( orchestrator_version="1.34.8", node_image_version="AKSUbuntu-2204gen2containerd-202607.20.0", - timestamp=datetime.datetime(2026, 8, 5), + timestamp=dt.datetime(2026, 8, 5), ) client = mock.Mock() client.get_upgrade_profile.return_value = mock.Mock(recently_used_versions=[rollback_version]) diff --git a/src/azure-cli/azure/cli/command_modules/acs/tests/latest/test_managed_cluster_decorator.py b/src/azure-cli/azure/cli/command_modules/acs/tests/latest/test_managed_cluster_decorator.py index 68c0e808603..9ecf2795dc4 100644 --- a/src/azure-cli/azure/cli/command_modules/acs/tests/latest/test_managed_cluster_decorator.py +++ b/src/azure-cli/azure/cli/command_modules/acs/tests/latest/test_managed_cluster_decorator.py @@ -103,7 +103,7 @@ from azure.cli.core.profiles import ResourceType from azure.core.exceptions import HttpResponseError from knack.prompting import NoTTYException -import datetime +import datetime as dt from dateutil.parser import parse @@ -10556,8 +10556,8 @@ def test_update_upgrade_settings(self): dec_3.context.attach_mc(mc_3) dec_mc_3 = dec_3.update_upgrade_settings(mc_3) self.assertEqual(dec_mc_3.upgrade_settings.override_settings.force_upgrade, True) - self.assertGreater(dec_mc_3.upgrade_settings.override_settings.until.timestamp(), (datetime.datetime.now(datetime.UTC) + datetime.timedelta(days=2)).timestamp()) - self.assertLess(dec_mc_3.upgrade_settings.override_settings.until.timestamp(), (datetime.datetime.now(datetime.UTC) + datetime.timedelta(days=4)).timestamp()) + self.assertGreater(dec_mc_3.upgrade_settings.override_settings.until.timestamp(), (dt.datetime.now(dt.UTC) + dt.timedelta(days=2)).timestamp()) + self.assertLess(dec_mc_3.upgrade_settings.override_settings.until.timestamp(), (dt.datetime.now(dt.UTC) + dt.timedelta(days=4)).timestamp()) # Set Until dec_4 = AKSManagedClusterUpdateDecorator( diff --git a/src/azure-cli/azure/cli/command_modules/appservice/_github_oauth.py b/src/azure-cli/azure/cli/command_modules/appservice/_github_oauth.py index fca076c29b1..d3136b6048f 100644 --- a/src/azure-cli/azure/cli/command_modules/appservice/_github_oauth.py +++ b/src/azure-cli/azure/cli/command_modules/appservice/_github_oauth.py @@ -6,7 +6,7 @@ import os import sys -from datetime import UTC, datetime +import datetime as dt from knack.log import get_logger from azure.cli.core.util import open_page_in_browser @@ -49,10 +49,10 @@ def cache_github_token(cmd, token, repo): if isinstance(entry, dict) and entry.get("value") == token: if repo not in entry.get("repos", []): entry["repos"] = [*entry.get("repos", []), repo] - entry["last_modified_timestamp"] = datetime.now(UTC).timestamp() + entry["last_modified_timestamp"] = dt.datetime.now(dt.UTC).timestamp() break else: - cache_entry = {"last_modified_timestamp": datetime.now(UTC).timestamp(), "value": token, "repos": [repo]} + cache_entry = {"last_modified_timestamp": dt.datetime.now(dt.UTC).timestamp(), "value": token, "repos": [repo]} cache = [cache_entry, *cache] secret_store.save(cache) diff --git a/src/azure-cli/azure/cli/command_modules/appservice/appservice_domains.py b/src/azure-cli/azure/cli/command_modules/appservice/appservice_domains.py index b81cc880396..95e2abae620 100644 --- a/src/azure-cli/azure/cli/command_modules/appservice/appservice_domains.py +++ b/src/azure-cli/azure/cli/command_modules/appservice/appservice_domains.py @@ -21,7 +21,7 @@ def create_domain(cmd, resource_group_name, hostname, contact_info, privacy=True accept_terms=False, tags=None, dryrun=False, no_wait=False): from azure.cli.core.commands.arm import ArmTemplateBuilder from azure.cli.command_modules.appservice._template_builder import (build_dns_zone, build_domain) - from datetime import UTC, datetime + import datetime as dt import socket import json @@ -41,7 +41,7 @@ def create_domain(cmd, resource_group_name, hostname, contact_info, privacy=True '/blob/master/contact_info.json') contact_info = verify_contact_info_and_format(contact_info) - current_time = str(datetime.now(UTC)).replace('+00:00', 'Z') + current_time = str(dt.datetime.now(dt.UTC)).replace('+00:00', 'Z') local_ip_address = '' try: local_ip_address = socket.gethostbyname(socket.gethostname()) diff --git a/src/azure-cli/azure/cli/command_modules/appservice/custom.py b/src/azure-cli/azure/cli/command_modules/appservice/custom.py index 1e91400bd5b..7791653eb9d 100644 --- a/src/azure-cli/azure/cli/command_modules/appservice/custom.py +++ b/src/azure-cli/azure/cli/command_modules/appservice/custom.py @@ -16,7 +16,7 @@ from binascii import hexlify from collections.abc import Mapping from os import urandom -import datetime +import datetime as dt import json import ssl import sys @@ -2193,7 +2193,7 @@ def upload_zip_to_storage(cmd, resource_group_name, name, src, slot=None): storage_connection = str(keyval['value']) container_name = "function-releases" - blob_name = "{}-{}.zip".format(datetime.datetime.today().strftime('%Y%m%d%H%M%S'), str(uuid.uuid4())) + blob_name = "{}-{}.zip".format(dt.datetime.today().strftime('%Y%m%d%H%M%S'), str(uuid.uuid4())) BlobServiceClient = get_sdk(cmd.cli_ctx, ResourceType.DATA_STORAGE_BLOB, '_blob_service_client#BlobServiceClient') blob_service_client = BlobServiceClient.from_connection_string(conn_str=storage_connection) @@ -2217,9 +2217,9 @@ def progress_callback(current, total): blob_client = container_client.upload_blob(blob_name, zip_content, validate_content=True, progress_hook=progress_callback) - now = datetime.datetime.now(datetime.UTC) - blob_start = now - datetime.timedelta(minutes=10) - blob_end = now + datetime.timedelta(weeks=520) + now = dt.datetime.now(dt.UTC) + blob_start = now - dt.timedelta(minutes=10) + blob_end = now + dt.timedelta(weeks=520) BlobSharedAccessSignature = get_sdk(cmd.cli_ctx, ResourceType.DATA_STORAGE_BLOB, '_shared_access_signature#BlobSharedAccessSignature') @@ -6482,7 +6482,7 @@ def update_backup_schedule(cmd, resource_group_name, webapp_name, storage_accoun if backup_name and backup_name.lower().endswith('.zip'): backup_name = backup_name[:-4] if not backup_name: - backup_name = '{0}_{1}'.format(webapp_name, datetime.datetime.now(datetime.UTC).strftime('%Y%m%d%H%M')) + backup_name = '{0}_{1}'.format(webapp_name, dt.datetime.now(dt.UTC).strftime('%Y%m%d%H%M')) try: configuration = _generic_site_operation(cmd.cli_ctx, resource_group_name, webapp_name, @@ -7369,16 +7369,16 @@ def _runtime_error_is_recent(runtime_error, minutes=_RUNTIME_ERROR_FRESHNESS_MIN ts = str(raw).strip() if ts.endswith('Z'): ts = ts[:-1] + '+00:00' - parsed = datetime.datetime.fromisoformat(ts) + parsed = dt.datetime.fromisoformat(ts) if parsed.tzinfo is None: - parsed = parsed.replace(tzinfo=datetime.timezone.utc) + parsed = parsed.replace(tzinfo=dt.timezone.utc) except (ValueError, TypeError): return False - delta = datetime.datetime.now(datetime.timezone.utc) - parsed + delta = dt.datetime.now(dt.timezone.utc) - parsed # Reject future timestamps: a clock-skewed or malformed value would # produce a negative delta, which still satisfies `<= 15min` and would # incorrectly mark stale errors as recent. - return datetime.timedelta(0) <= delta <= datetime.timedelta(minutes=minutes) + return dt.timedelta(0) <= delta <= dt.timedelta(minutes=minutes) def _ensure_linux_webapp_for_troubleshoot(cmd, resource_group_name, name, slot=None): @@ -8804,10 +8804,10 @@ def _compute_support_status(eol_date_str): if not eol_date_str: return 'n/a' try: - eol_date = datetime.datetime.strptime(eol_date_str, "%Y-%m-%d").replace( - tzinfo=datetime.timezone.utc) - now = datetime.datetime.now(datetime.timezone.utc) - near_threshold = now + datetime.timedelta(days=365) + eol_date = dt.datetime.strptime(eol_date_str, "%Y-%m-%d").replace( + tzinfo=dt.timezone.utc) + now = dt.datetime.now(dt.timezone.utc) + near_threshold = now + dt.timedelta(days=365) if eol_date <= now: return 'EOL' if eol_date <= near_threshold: @@ -8824,11 +8824,11 @@ def _format_eol_date(eol_date): if isinstance(eol_date, str): for fmt in ("%Y-%m-%dT%H:%M:%S.%fZ", "%Y-%m-%dT%H:%M:%SZ"): try: - return datetime.datetime.strptime(eol_date, fmt).strftime("%Y-%m-%d") + return dt.datetime.strptime(eol_date, fmt).strftime("%Y-%m-%d") except ValueError: continue return eol_date - if isinstance(eol_date, datetime.datetime): + if isinstance(eol_date, dt.datetime): return eol_date.strftime("%Y-%m-%d") return None @@ -9093,20 +9093,20 @@ def _is_valid_runtime_setting(cls, runtime_setting, include_eol=False): try: if isinstance(end_of_life, str): try: - end_of_life_dt = datetime.datetime.strptime( + end_of_life_dt = dt.datetime.strptime( end_of_life, "%Y-%m-%dT%H:%M:%S.%fZ" - ).replace(tzinfo=datetime.timezone.utc) + ).replace(tzinfo=dt.timezone.utc) except ValueError: - end_of_life_dt = datetime.datetime.strptime( + end_of_life_dt = dt.datetime.strptime( end_of_life, "%Y-%m-%dT%H:%M:%SZ" - ).replace(tzinfo=datetime.timezone.utc) + ).replace(tzinfo=dt.timezone.utc) else: # If already a datetime, ensure it's timezone-aware if end_of_life.tzinfo is None: - end_of_life_dt = end_of_life.replace(tzinfo=datetime.timezone.utc) + end_of_life_dt = end_of_life.replace(tzinfo=dt.timezone.utc) else: end_of_life_dt = end_of_life - now_utc = datetime.datetime.now(datetime.UTC) + now_utc = dt.datetime.now(dt.UTC) if now_utc >= end_of_life_dt: return False except (ValueError, AttributeError): @@ -9633,11 +9633,11 @@ def validate_end_of_life_date(self, runtime, version, is_linux): if runtime == 'custom': return - today = datetime.datetime.now(datetime.timezone.utc) + today = dt.datetime.now(dt.timezone.utc) six_months = today + relativedelta(months=+6) runtimes = [r for r in self.stacks if r.linux == is_linux and runtime == r.name] runtimes.sort(key=lambda r: r.end_of_life_date or - datetime.datetime.min.replace(tzinfo=datetime.timezone.utc), reverse=True) + dt.datetime.min.replace(tzinfo=dt.timezone.utc), reverse=True) matched_runtime = next((r for r in runtimes if r.version == version), None) if matched_runtime: eol = matched_runtime.end_of_life_date @@ -9716,7 +9716,7 @@ def get_default_version(self, runtime, functions_version, is_linux=False): runtimes = [r for r in self.stacks if r.linux == is_linux and r.name == runtime] # sort runtimes by end of life date runtimes.sort(key=lambda r: r.end_of_life_date or - datetime.datetime.min.replace(tzinfo=datetime.timezone.utc), reverse=True) + dt.datetime.min.replace(tzinfo=dt.timezone.utc), reverse=True) for r in runtimes: if functions_version in r.supported_func_versions: return r @@ -11203,7 +11203,7 @@ def _check_runtimestatus_with_deploymentstatusapi(cmd, resource_group_name, name response_body = None logger.warning('Polling the status of %s deployment. Start Time: %s UTC', "async" if is_async else "sync", - datetime.datetime.now(datetime.timezone.utc)) + dt.datetime.now(dt.timezone.utc)) # verify if the app is a linux web app if deploy_params is not None: # Reuse the Site fetched (or cached) during the publish leg. Saves diff --git a/src/azure-cli/azure/cli/command_modules/appservice/static_sites.py b/src/azure-cli/azure/cli/command_modules/appservice/static_sites.py index d4aa68ff669..e0d4bff9e8e 100644 --- a/src/azure-cli/azure/cli/command_modules/appservice/static_sites.py +++ b/src/azure-cli/azure/cli/command_modules/appservice/static_sites.py @@ -14,7 +14,7 @@ from knack.log import get_logger from urllib.parse import urlparse import uuid -from datetime import UTC, datetime, timedelta +import datetime as dt from .utils import normalize_sku_for_staticapp, raise_missing_token_suggestion, raise_missing_ado_token_suggestion from .custom import show_app, _build_identities_info @@ -406,7 +406,7 @@ def _get_ado_token(cmd, source): body = { "displayName": f"SWA Deployment Token (created via the Azure CLI) - {uuid.uuid4().hex}", "scope": "vso.build_execute vso.code_full vso.variablegroups_manage", - "validTo": (datetime.now(UTC) + timedelta(days=6 * 30)).isoformat(), + "validTo": (dt.datetime.now(dt.UTC) + dt.timedelta(days=6 * 30)).isoformat(), "allOrgs": False, "targetAccounts": [org_id], } diff --git a/src/azure-cli/azure/cli/command_modules/backup/custom.py b/src/azure-cli/azure/cli/command_modules/backup/custom.py index 9e41769ab73..00ad234002a 100644 --- a/src/azure-cli/azure/cli/command_modules/backup/custom.py +++ b/src/azure-cli/azure/cli/command_modules/backup/custom.py @@ -5,7 +5,7 @@ import time import os -from datetime import UTC, datetime, timedelta, timezone +import datetime as dt # pylint: disable=too-many-lines from knack.log import get_logger from knack.prompting import prompt_y_n @@ -1215,7 +1215,7 @@ def update_policy_for_item(cmd, client, resource_group_name, vault_name, item, p def backup_now(cmd, client, resource_group_name, vault_name, item, retain_until): if retain_until is None: - retain_until = datetime.now(timezone.utc) + timedelta(days=30) + retain_until = dt.datetime.now(dt.timezone.utc) + dt.timedelta(days=30) # Get container and item URIs container_uri = cust_help.get_protection_container_uri_from_id(item.id) @@ -1860,7 +1860,7 @@ def stop_job(client, resource_group_name, vault_name, name, use_secondary_region def wait_for_job(cmd, client, resource_group_name, vault_name, name, timeout=None, use_secondary_region=None): logger.warning("Waiting for job '%s' ...", name) - start_timestamp = datetime.now(UTC) + start_timestamp = dt.datetime.now(dt.UTC) if use_secondary_region: vault = vaults_cf(cmd.cli_ctx).get(resource_group_name, vault_name) vault_location = vault.location @@ -1869,7 +1869,7 @@ def wait_for_job(cmd, client, resource_group_name, vault_name, name, timeout=Non job_details = client.get(azure_region, CrrJobRequest(resource_id=vault.id, job_name=name)) while cust_help.job_in_progress(job_details.properties.status): if timeout: - elapsed_time = datetime.now(UTC) - start_timestamp + elapsed_time = dt.datetime.now(dt.UTC) - start_timestamp if elapsed_time.seconds > timeout: logger.warning("Command timed out while waiting for job '%s'", name) break @@ -1879,7 +1879,7 @@ def wait_for_job(cmd, client, resource_group_name, vault_name, name, timeout=Non job_details = client.get(vault_name, resource_group_name, name) while cust_help.job_in_progress(job_details.properties.status): if timeout: - elapsed_time = datetime.now(UTC) - start_timestamp + elapsed_time = dt.datetime.now(dt.UTC) - start_timestamp if elapsed_time.seconds > timeout: logger.warning("Command timed out while waiting for job '%s'", name) break diff --git a/src/azure-cli/azure/cli/command_modules/backup/tests/latest/preparers.py b/src/azure-cli/azure/cli/command_modules/backup/tests/latest/preparers.py index a06a09fcdec..99aae08be2b 100644 --- a/src/azure-cli/azure/cli/command_modules/backup/tests/latest/preparers.py +++ b/src/azure-cli/azure/cli/command_modules/backup/tests/latest/preparers.py @@ -5,7 +5,7 @@ import json import os -from datetime import UTC, datetime, timedelta +import datetime as dt from azure.cli.testsdk import CliTestError from azure.cli.testsdk.preparers import AbstractPreparer, SingleValueReplacer, KeyVaultPreparer @@ -395,7 +395,7 @@ def create_resource(self, name, **kwargs): vault = self._get_vault(**kwargs) vm = self._get_vm(**kwargs) - retain_date = datetime.now(UTC) + timedelta(days=30) + retain_date = dt.datetime.now(dt.UTC) + dt.timedelta(days=30) command_string = 'az backup protection backup-now --backup-management-type AzureIaasVM --workload-type VM -g {} -v {} -c {} -i {} --retain-until {} --query name' command_string = command_string.format(self.resource_group, vault, vm, vm, retain_date.strftime('%d-%m-%Y')) @@ -817,7 +817,7 @@ def create_resource(self, name, **kwargs): vault = self._get_vault(**kwargs) afs = self._get_file_share(**kwargs) - retain_date = datetime.now(UTC) + timedelta(days=30) + retain_date = dt.datetime.now(dt.UTC) + dt.timedelta(days=30) retain_date = retain_date.strftime('%d-%m-%Y') command_string = 'az backup protection backup-now' command_string += ' -g {} -v {} -i {} -c {} --backup-management-type AzureStorage --retain-until {} --query name' diff --git a/src/azure-cli/azure/cli/command_modules/backup/tests/latest/test_afs_commands.py b/src/azure-cli/azure/cli/command_modules/backup/tests/latest/test_afs_commands.py index ed24a3cda3f..89906bbbbe6 100644 --- a/src/azure-cli/azure/cli/command_modules/backup/tests/latest/test_afs_commands.py +++ b/src/azure-cli/azure/cli/command_modules/backup/tests/latest/test_afs_commands.py @@ -4,7 +4,7 @@ # -------------------------------------------------------------------------------------------- import json -from datetime import UTC, datetime, timedelta +import datetime as dt import unittest import time from azure.cli.testsdk import ScenarioTest, JMESPathCheckExists, \ @@ -42,7 +42,7 @@ def test_afs_backup_scenario(self, resource_group, vault_name, storage_account, }) self.cmd('backup protection enable-for-azurefileshare -g {rg} -v {vault} --storage-account {container} --azure-file-share {item} -p {policy}') - self.kwargs['retain_date'] = (datetime.now(UTC) + timedelta(days=30)).strftime('%d-%m-%Y') + self.kwargs['retain_date'] = (dt.datetime.now(dt.UTC) + dt.timedelta(days=30)).strftime('%d-%m-%Y') self.kwargs['job'] = self.cmd('backup protection backup-now -g {rg} -v {vault} -c {container} -i {item} --retain-until {retain_date} --backup-management-type AzureStorage --query name').get_output_in_json() self.cmd('backup job wait -g {rg} -v {vault} -n {job}') @@ -384,7 +384,7 @@ def test_afs_backup_rp(self, resource_group, vault_name, storage_account, afs_na rp_list = self.cmd('backup recoverypoint list -g {rg} -v {vault} -c {container} -i {item1} --backup-management-type {type}').get_output_in_json() rp_count1 = len(rp_list) - self.kwargs['retain_date'] = (datetime.now(UTC) + timedelta(days=30)).strftime('%d-%m-%Y') + self.kwargs['retain_date'] = (dt.datetime.now(dt.UTC) + dt.timedelta(days=30)).strftime('%d-%m-%Y') self.kwargs['job'] = self.cmd('backup protection backup-now -g {rg} -v {vault} -c {container} -i {item1} --retain-until {retain_date} --backup-management-type AzureStorage --query name').get_output_in_json() self.cmd('backup job wait -g {rg} -v {vault} -n {job}') diff --git a/src/azure-cli/azure/cli/command_modules/backup/tests/latest/test_backup_commands.py b/src/azure-cli/azure/cli/command_modules/backup/tests/latest/test_backup_commands.py index 06bf14528df..712137a8f3b 100644 --- a/src/azure-cli/azure/cli/command_modules/backup/tests/latest/test_backup_commands.py +++ b/src/azure-cli/azure/cli/command_modules/backup/tests/latest/test_backup_commands.py @@ -4,7 +4,7 @@ # -------------------------------------------------------------------------------------------- import json -from datetime import UTC, datetime, timedelta +import datetime as dt from types import SimpleNamespace import unittest import time @@ -107,7 +107,7 @@ def test_backup_scenario(self, resource_group, vault_name, vm_name, storage_acco self.kwargs['item'] = self.cmd('backup item list -g {rg} -v {vault} -c {container} --backup-management-type AzureIaasVM --workload-type VM --query [0].properties.friendlyName').get_output_in_json() # Trigger Backup - self.kwargs['retain_date'] = (datetime.now(UTC) + timedelta(days=30)).strftime('%d-%m-%Y') + self.kwargs['retain_date'] = (dt.datetime.now(dt.UTC) + dt.timedelta(days=30)).strftime('%d-%m-%Y') self.kwargs['job'] = self.cmd('backup protection backup-now -g {rg} -v {vault} -c {container} -i {item} --backup-management-type AzureIaasVM --workload-type VM --retain-until {retain_date} --query name').get_output_in_json() self.cmd('backup job wait -g {rg} -v {vault} -n {job}') @@ -1028,7 +1028,7 @@ def _free_csb_vm(): self.assertEqual(item['properties']['containerSubscriptionId'], self.kwargs['vm_sub']) # Take an on-demand backup so there is a recovery point to restore from. - self.kwargs['retain_date'] = (datetime.now(UTC) + timedelta(days=30)).strftime('%d-%m-%Y') + self.kwargs['retain_date'] = (dt.datetime.now(dt.UTC) + dt.timedelta(days=30)).strftime('%d-%m-%Y') self.kwargs['backup_job'] = self.cmd('backup protection backup-now --backup-management-type AzureIaasVM ' '--workload-type VM -g {rg} -v {vault} -c {vm_name} -i {vm_name} ' '--retain-until {retain_date} --query name').get_output_in_json() @@ -1510,7 +1510,7 @@ def test_backup_disk_exclusion(self, resource_group, vault_name, vm_name, storag self.check('properties.extendedProperties.diskExclusionProperties.isInclusionList', False), ]) - self.kwargs['retain_date'] = (datetime.now(UTC) + timedelta(days=30)).strftime('%d-%m-%Y') + self.kwargs['retain_date'] = (dt.datetime.now(dt.UTC) + dt.timedelta(days=30)).strftime('%d-%m-%Y') self.kwargs['job'] = self.cmd('backup protection backup-now -g {rg} -v {vault} -c {vm} -i {vm} --backup-management-type AzureIaasVM --workload-type VM --retain-until {retain_date} --query name').get_output_in_json() self.cmd('backup job wait -g {rg} -v {vault} -n {job}') diff --git a/src/azure-cli/azure/cli/command_modules/batchai/custom.py b/src/azure-cli/azure/cli/command_modules/batchai/custom.py index 56ffea22c4c..663352ccd85 100644 --- a/src/azure-cli/azure/cli/command_modules/batchai/custom.py +++ b/src/azure-cli/azure/cli/command_modules/batchai/custom.py @@ -6,7 +6,7 @@ # pylint: disable=too-many-lines import collections import copy -import datetime +import datetime as dt import json import os import signal @@ -794,7 +794,7 @@ def _get_files_from_bfs(cli_ctx, bfs, path, expiry): if b.properties.content_settings.contentMd5 is not None: name = os.path.basename(b.name) sas = sas_client.generate_blob(bfs.container_name, b.name, permission=BlobSasPermissions(read=True), - expiry=datetime.datetime.now(datetime.UTC) + datetime.timedelta(minutes=expiry)) + expiry=dt.datetime.now(dt.UTC) + dt.timedelta(minutes=expiry)) blob_url = b.url if '?' not in blob_url: blob_url += '?' + sas @@ -843,7 +843,7 @@ def _get_files_from_afs(cli_ctx, afs, path, expiry): if not f.is_directory: sas = sas_client.generate_file(share_name, directory_name=effective_path, file_name=f.name, permission=FileSasPermissions(read=True), - expiry=datetime.datetime.now(datetime.UTC) + datetime.timedelta(minutes=expiry)) + expiry=dt.datetime.now(dt.UTC) + dt.timedelta(minutes=expiry)) file_url = share_client.url + '/' + effective_path + '/' + f.name if '?' not in file_url: file_url += '?' + sas diff --git a/src/azure-cli/azure/cli/command_modules/containerapp/_github_oauth.py b/src/azure-cli/azure/cli/command_modules/containerapp/_github_oauth.py index 0e2583c4cd7..4070ba89008 100644 --- a/src/azure-cli/azure/cli/command_modules/containerapp/_github_oauth.py +++ b/src/azure-cli/azure/cli/command_modules/containerapp/_github_oauth.py @@ -6,7 +6,7 @@ import os import sys -from datetime import UTC, datetime +import datetime as dt from knack.log import get_logger from azure.cli.core.util import open_page_in_browser @@ -49,10 +49,10 @@ def cache_github_token(cmd, token, repo): if isinstance(entry, dict) and entry.get("value") == token: if repo not in entry.get("repos", []): entry["repos"] = [*entry.get("repos", []), repo] - entry["last_modified_timestamp"] = datetime.now(UTC).timestamp() + entry["last_modified_timestamp"] = dt.datetime.now(dt.UTC).timestamp() break else: - cache_entry = {"last_modified_timestamp": datetime.now(UTC).timestamp(), "value": token, "repos": [repo]} + cache_entry = {"last_modified_timestamp": dt.datetime.now(dt.UTC).timestamp(), "value": token, "repos": [repo]} cache = [cache_entry, *cache] secret_store.save(cache) diff --git a/src/azure-cli/azure/cli/command_modules/containerapp/_utils.py b/src/azure-cli/azure/cli/command_modules/containerapp/_utils.py index 7a5af84a314..d4b1652a710 100644 --- a/src/azure-cli/azure/cli/command_modules/containerapp/_utils.py +++ b/src/azure-cli/azure/cli/command_modules/containerapp/_utils.py @@ -21,7 +21,7 @@ from urllib.parse import urlparse from urllib.request import urlopen -from datetime import UTC, datetime +import datetime as dt from dateutil.relativedelta import relativedelta from azure.cli.core.azclierror import (ValidationError, RequiredArgumentMissingError, CLIInternalError, ResourceNotFoundError, FileOperationError, CLIError, UnauthorizedError, @@ -161,7 +161,7 @@ def create_service_principal_for_github_action(cmd, scopes=None, role="contribut RETRY_LIMIT = 36 client = graph_client_factory(cmd.cli_ctx) - now = datetime.now(UTC) + now = dt.datetime.now(dt.UTC) app_display_name = 'azure-cli-' + now.strftime('%Y-%m-%d-%H-%M-%S') app = retry_until_success(_create_application, SP_CREATION_ERR_TXT, RETRY_LIMIT, client, display_name=app_display_name) sp = retry_until_success(_create_service_principal, SP_CREATION_ERR_TXT, RETRY_LIMIT, client, app_id=app["appId"]) @@ -221,7 +221,7 @@ def trigger_workflow(token, repo, workflow_name, branch): def await_github_action(token, repo, workflow_name, timeout_secs=1200): from ._clients import PollingAnimation - start = datetime.now(UTC) + start = dt.datetime.now(dt.UTC) animation = PollingAnimation() animation.tick() @@ -237,14 +237,14 @@ def await_github_action(token, repo, workflow_name, timeout_secs=1200): pass animation.flush() - if (datetime.now(UTC) - start).seconds >= timeout_secs: + if (dt.datetime.now(dt.UTC) - start).seconds >= timeout_secs: raise CLIInternalError("Timed out while waiting for the Github action to start.") runs = workflow.get_runs() while runs is None or not [r for r in runs if r.status in ('queued', 'in_progress')]: time.sleep(SHORT_POLLING_INTERVAL_SECS) runs = workflow.get_runs() - if (datetime.now(UTC) - start).seconds >= timeout_secs: + if (dt.datetime.now(dt.UTC) - start).seconds >= timeout_secs: raise CLIInternalError("Timed out while waiting for the Github action to be started.") runs = [r for r in runs if r.status in ('queued', 'in_progress')] runs.sort(key=lambda r: r.created_at, reverse=True) @@ -258,7 +258,7 @@ def await_github_action(token, repo, workflow_name, timeout_secs=1200): animation.tick() status = github_repo.get_workflow_run(run_id).status animation.flush() - if (datetime.now(UTC) - start).seconds >= timeout_secs: + if (dt.datetime.now(dt.UTC) - start).seconds >= timeout_secs: raise CLIInternalError("Timed out while waiting for the Github action to complete.") animation.flush() # needed to clear the animation from the terminal @@ -294,11 +294,11 @@ def _register_resource_provider(cmd, resource_provider): # wait for registration to finish timeout_secs = 120 registration = _is_resource_provider_registered(cmd, resource_provider) - start = datetime.now(UTC) + start = dt.datetime.now(dt.UTC) while not registration: registration = _is_resource_provider_registered(cmd, resource_provider) time.sleep(SHORT_POLLING_INTERVAL_SECS) - if (datetime.now(UTC) - start).seconds >= timeout_secs: + if (dt.datetime.now(dt.UTC) - start).seconds >= timeout_secs: raise CLIInternalError(f"Timed out while waiting for the {resource_provider} resource provider to be registered.") except Exception as e: @@ -909,7 +909,7 @@ def _add_or_update_tags(containerapp_def, tags): def _object_to_dict(obj): def default_handler(x): - if isinstance(x, datetime): + if isinstance(x, dt.datetime): return x.isoformat() return x.__dict__ diff --git a/src/azure-cli/azure/cli/command_modules/cosmosdb/custom.py b/src/azure-cli/azure/cli/command_modules/cosmosdb/custom.py index 2274aff75b4..20e69a11fab 100644 --- a/src/azure-cli/azure/cli/command_modules/cosmosdb/custom.py +++ b/src/azure-cli/azure/cli/command_modules/cosmosdb/custom.py @@ -5,7 +5,7 @@ # pylint: disable=too-many-lines -import datetime +import datetime as dt from enum import Enum from knack.log import get_logger from knack.util import CLIError @@ -1900,7 +1900,7 @@ def cli_cosmosdb_restore(cmd, target_restorable_account = None restore_timestamp_datetime_utc = _convert_to_utc_timestamp(restore_timestamp) - current_dateTime = datetime.datetime.now(datetime.UTC) + current_dateTime = dt.datetime.now(dt.UTC) # Fail if provided restoretimesamp is greater than current timestamp if restore_timestamp_datetime_utc > current_dateTime: @@ -1950,9 +1950,9 @@ def _convert_to_utc_timestamp(timestamp_string): timestamp_datetime_utc = None # Treat timestamps without an offset as UTC. if timestamp_datetime.tzinfo is not None and timestamp_datetime.tzinfo.utcoffset(timestamp_datetime) is not None: - timestamp_datetime_utc = timestamp_datetime.astimezone(datetime.timezone.utc) + timestamp_datetime_utc = timestamp_datetime.astimezone(dt.timezone.utc) else: - timestamp_datetime_utc = timestamp_datetime.replace(tzinfo=datetime.UTC) + timestamp_datetime_utc = timestamp_datetime.replace(tzinfo=dt.UTC) return timestamp_datetime_utc @@ -2808,15 +2808,15 @@ def cli_cosmosdb_managed_cassandra_datacenter_update(client, def process_restorable_databases(restorable_databases, database_name): - latest_database_delete_time = datetime.datetime.utcfromtimestamp(0) - latest_database_create_or_recreate_time = datetime.datetime.utcfromtimestamp(0) + latest_database_delete_time = dt.datetime.utcfromtimestamp(0) + latest_database_create_or_recreate_time = dt.datetime.utcfromtimestamp(0) database_rid = None for restorable_database in restorable_databases: resource = restorable_database.resource if resource.owner_id == database_name: database_rid = resource.owner_resource_id - event_timestamp = datetime.datetime.strptime(resource.event_timestamp, "%Y-%m-%dT%H:%M:%SZ") + event_timestamp = dt.datetime.strptime(resource.event_timestamp, "%Y-%m-%dT%H:%M:%SZ") if resource.operation_type == "Delete" and latest_database_delete_time < event_timestamp: latest_database_delete_time = event_timestamp @@ -2827,7 +2827,7 @@ def process_restorable_databases(restorable_databases, database_name): raise CLIError("No restorable database found with name: {}".format(database_name)) # Database never deleted then reset it to max time - latest_database_delete_time = datetime.datetime.max if latest_database_delete_time == datetime.datetime.utcfromtimestamp(0) else latest_database_delete_time + latest_database_delete_time = dt.datetime.max if latest_database_delete_time == dt.datetime.utcfromtimestamp(0) else latest_database_delete_time logger.debug('process_restorable_databases: latest_database_delete_time {} latest_database_create_or_recreate_time {} database_name {}'.format(latest_database_delete_time, latest_database_create_or_recreate_time, database_name)) # pylint: disable=logging-format-interpolation @@ -2835,15 +2835,15 @@ def process_restorable_databases(restorable_databases, database_name): def process_restorable_collections(restorable_collections, collection_name, database_name): - latest_collection_delete_time = datetime.datetime.utcfromtimestamp(0) - latest_collection_create_or_recreate_time = datetime.datetime.utcfromtimestamp(0) + latest_collection_delete_time = dt.datetime.utcfromtimestamp(0) + latest_collection_create_or_recreate_time = dt.datetime.utcfromtimestamp(0) collection_rid = None for restorable_collection in restorable_collections: resource = restorable_collection.resource if resource.owner_id == collection_name: collection_rid = resource.owner_resource_id - event_timestamp = datetime.datetime.strptime(resource.event_timestamp, "%Y-%m-%dT%H:%M:%SZ") + event_timestamp = dt.datetime.strptime(resource.event_timestamp, "%Y-%m-%dT%H:%M:%SZ") if resource.operation_type == "Delete" and latest_collection_delete_time < event_timestamp: latest_collection_delete_time = event_timestamp @@ -2854,7 +2854,7 @@ def process_restorable_collections(restorable_collections, collection_name, data raise CLIError("No restorable collection with name: {} found in the database with name: {}".format(collection_name, database_name)) # Collection never deleted then reset it to max time - latest_collection_delete_time = datetime.datetime.max if latest_collection_delete_time == datetime.datetime.utcfromtimestamp(0) else latest_collection_delete_time + latest_collection_delete_time = dt.datetime.max if latest_collection_delete_time == dt.datetime.utcfromtimestamp(0) else latest_collection_delete_time logger.debug('process_restorable_databases: latest_collection_delete_time {} latest_collection_create_or_recreate_time {} database_name {} collection_name {}'.format(latest_collection_delete_time, latest_collection_create_or_recreate_time, database_name, collection_name)) # pylint: disable=logging-format-interpolation @@ -2912,13 +2912,13 @@ def cli_cosmosdb_sql_database_restore(cmd, latest_database_delete_time, latest_database_create_or_recreate_time, database_rid = process_restorable_databases(restorable_databases, database_name) # pylint: disable=unused-variable # Database is alive if create or recreate timestamp is later than latest delete timestamp - database_alive = latest_database_create_or_recreate_time > latest_database_delete_time or latest_database_delete_time == datetime.datetime.max + database_alive = latest_database_create_or_recreate_time > latest_database_delete_time or latest_database_delete_time == dt.datetime.max if database_alive: raise CLIError("Database with name {} already exists in this account with name {} in location {}".format(database_name, account_name, restorable_database_account.location)) # """Subtracting -1 second from the deleted timestamp to restore till end of logchain""" - restore_time = latest_database_delete_time + datetime.timedelta(seconds=-1) + restore_time = latest_database_delete_time + dt.timedelta(seconds=-1) restore_timestamp = restore_time.strftime("%Y-%m-%dT%H:%M:%S%Z") except ResourceNotFoundError: raise CLIError("Cannot find a database account with name {} that is online in location {}".format(account_name, restorable_database_account.location)) @@ -3000,7 +3000,7 @@ def cli_cosmosdb_sql_container_restore(cmd, latest_database_delete_time, latest_database_create_or_recreate_time, database_rid = process_restorable_databases(restorable_databases, database_name) # Database is alive if create or recreate timestamp is later than latest delete timestamp - database_alive = latest_database_delete_time == datetime.datetime.max or latest_database_create_or_recreate_time > latest_database_delete_time + database_alive = latest_database_delete_time == dt.datetime.max or latest_database_create_or_recreate_time > latest_database_delete_time if not database_alive: raise CLIError("No active database with name {} found that contains the collection {}".format(database_name, container_name)) @@ -3015,13 +3015,13 @@ def cli_cosmosdb_sql_container_restore(cmd, latest_container_delete_time, latest_container_create_or_recreate_time = process_restorable_collections(restorable_containers, container_name, database_name) # Container is alive if create or recreate timestamp is later than latest delete timestamp - container_alive = latest_container_create_or_recreate_time > latest_container_delete_time or latest_container_delete_time == datetime.datetime.max + container_alive = latest_container_create_or_recreate_time > latest_container_delete_time or latest_container_delete_time == dt.datetime.max if container_alive: raise CLIError("The collection {} is currently online. Please delete the collection and provide a restore timestamp for restoring different instance of the collection.".format(container_name)) # """Subtracting -1 second from the deleted timestamp to restore till end of logchain""" - restore_time = latest_container_delete_time + datetime.timedelta(seconds=-1) + restore_time = latest_container_delete_time + dt.timedelta(seconds=-1) restore_timestamp = restore_time.strftime("%Y-%m-%dT%H:%M:%S%Z") except ResourceNotFoundError: raise CLIError("Cannot find a database account with name {} that is online in location {}".format(account_name, restorable_database_account.location)) @@ -3104,13 +3104,13 @@ def cli_cosmosdb_mongodb_database_restore(cmd, latest_database_delete_time, latest_database_create_or_recreate_time, database_rid = process_restorable_databases(restorable_databases, database_name) # pylint: disable=unused-variable # Database is alive if create or recreate timestamp is later than latest delete timestamp - database_alive = latest_database_create_or_recreate_time > latest_database_delete_time or latest_database_delete_time == datetime.datetime.max + database_alive = latest_database_create_or_recreate_time > latest_database_delete_time or latest_database_delete_time == dt.datetime.max if database_alive: raise CLIError("Database with name {} already exists in this account with name {} in location {}".format(database_name, account_name, restorable_database_account.location)) # """Subtracting -1 second from the deleted timestamp to restore till end of logchain""" - restore_time = latest_database_delete_time + datetime.timedelta(seconds=-1) + restore_time = latest_database_delete_time + dt.timedelta(seconds=-1) restore_timestamp = restore_time.strftime("%Y-%m-%dT%H:%M:%S%Z") except ResourceNotFoundError: raise CLIError("Cannot find a database account with name {} that is online in location {}".format(account_name, restorable_database_account.location)) @@ -3191,7 +3191,7 @@ def cli_cosmosdb_mongodb_collection_restore(cmd, latest_database_delete_time, latest_database_create_or_recreate_time, database_rid = process_restorable_databases(restorable_databases, database_name) # Database is alive if create or recreate timestamp is later than latest delete timestamp - database_alive = latest_database_delete_time == datetime.datetime.max or latest_database_create_or_recreate_time > latest_database_delete_time + database_alive = latest_database_delete_time == dt.datetime.max or latest_database_create_or_recreate_time > latest_database_delete_time if not database_alive: raise CLIError("Cannot find a database account with name {} that is online when latest collection instance was deleted".format(account_name)) @@ -3206,13 +3206,13 @@ def cli_cosmosdb_mongodb_collection_restore(cmd, latest_collection_delete_time, latest_collection_create_or_recreate_time = process_restorable_collections(restorable_collections, collection_name, database_name) # Collection is alive if create or recreate timestamp is later than latest delete timestamp - collection_alive = latest_collection_create_or_recreate_time > latest_collection_delete_time or latest_collection_delete_time == datetime.datetime.max + collection_alive = latest_collection_create_or_recreate_time > latest_collection_delete_time or latest_collection_delete_time == dt.datetime.max if collection_alive: raise CLIError("The collection {} is currently online. Please delete the collection and provide a restore timestamp for restoring different instance of the collection.".format(collection_name)) # """Subtracting -1 second from the deleted timestamp to restore till end of logchain""" - restore_time = latest_collection_delete_time + datetime.timedelta(seconds=-1) + restore_time = latest_collection_delete_time + dt.timedelta(seconds=-1) restore_timestamp = restore_time.strftime("%Y-%m-%dT%H:%M:%S%Z") except ResourceNotFoundError: raise CLIError("Cannot find a database account with name {} that is online in location {}".format(account_name, restorable_database_account.location)) @@ -3294,13 +3294,13 @@ def cli_cosmosdb_gremlin_database_restore(cmd, latest_database_delete_time, latest_database_create_or_recreate_time, database_rid = process_restorable_databases(restorable_databases, database_name) # pylint: disable=unused-variable # Database is alive if create or recreate timestamp is later than latest delete timestamp - database_alive = latest_database_create_or_recreate_time > latest_database_delete_time or latest_database_delete_time == datetime.datetime.max + database_alive = latest_database_create_or_recreate_time > latest_database_delete_time or latest_database_delete_time == dt.datetime.max if database_alive: raise CLIError("Database with name {} already exists in this account with name {} in location {}".format(database_name, account_name, restorable_database_account.location)) # """Subtracting -1 second from the deleted timestamp to restore till end of logchain""" - restore_time = latest_database_delete_time + datetime.timedelta(seconds=-1) + restore_time = latest_database_delete_time + dt.timedelta(seconds=-1) restore_timestamp = restore_time.strftime("%Y-%m-%dT%H:%M:%S%Z") except ResourceNotFoundError: raise CLIError("Cannot find a Gremlin database account with name {} that is online in location {}".format(account_name, restorable_database_account.location)) @@ -3382,7 +3382,7 @@ def cli_cosmosdb_gremlin_graph_restore(cmd, latest_database_delete_time, latest_database_create_or_recreate_time, database_rid = process_restorable_databases(restorable_databases, database_name) # Database is alive if create or recreate timestamp is later than latest delete timestamp - database_alive = latest_database_delete_time == datetime.datetime.max or latest_database_create_or_recreate_time > latest_database_delete_time + database_alive = latest_database_delete_time == dt.datetime.max or latest_database_create_or_recreate_time > latest_database_delete_time if not database_alive: raise CLIError("No active database with name {} found that contains the graph {}".format(database_name, graph_name)) @@ -3397,13 +3397,13 @@ def cli_cosmosdb_gremlin_graph_restore(cmd, latest_graph_delete_time, latest_graph_create_or_recreate_time = process_restorable_collections(restorable_graphs, graph_name, database_name) # Graph is alive if create or recreate timestamp is later than latest delete timestamp - graph_alive = latest_graph_create_or_recreate_time > latest_graph_delete_time or latest_graph_delete_time == datetime.datetime.max + graph_alive = latest_graph_create_or_recreate_time > latest_graph_delete_time or latest_graph_delete_time == dt.datetime.max if graph_alive: raise CLIError("The graph {} is currently online. Please delete the graph and provide a restore timestamp for restoring different instance of the graph.".format(graph_name)) # """Subtracting -1 second from the deleted timestamp to restore till end of logchain""" - restore_time = latest_graph_delete_time + datetime.timedelta(seconds=-1) + restore_time = latest_graph_delete_time + dt.timedelta(seconds=-1) restore_timestamp = restore_time.strftime("%Y-%m-%dT%H:%M:%S%Z") except ResourceNotFoundError: raise CLIError("Cannot find a database account with name {} that is online in location {}".format(account_name, restorable_database_account.location)) @@ -3486,13 +3486,13 @@ def cli_cosmosdb_table_restore(cmd, latest_table_delete_time, latest_table_create_or_recreate_time, table_rid = process_restorable_databases(restorable_tables, table_name) # pylint: disable=unused-variable # Table is alive if create or recreate timestamp is later than latest delete timestamp - table_alive = latest_table_create_or_recreate_time > latest_table_delete_time or latest_table_delete_time == datetime.datetime.max + table_alive = latest_table_create_or_recreate_time > latest_table_delete_time or latest_table_delete_time == dt.datetime.max if table_alive: raise CLIError("Table with name {} already exists in this account with name {} in location {}".format(table_name, account_name, restorable_database_account.location)) # """Subtracting -1 second from the deleted timestamp to restore till end of logchain""" - restore_time = latest_table_delete_time + datetime.timedelta(seconds=-1) + restore_time = latest_table_delete_time + dt.timedelta(seconds=-1) restore_timestamp = restore_time.strftime("%Y-%m-%dT%H:%M:%S%Z") except ResourceNotFoundError: raise CLIError("Cannot find a table account with name {} that is online in location {}".format(account_name, restorable_database_account.location)) diff --git a/src/azure-cli/azure/cli/command_modules/cosmosdb/tests/latest/test_cosmosdb_backuprestore_scenario.py b/src/azure-cli/azure/cli/command_modules/cosmosdb/tests/latest/test_cosmosdb_backuprestore_scenario.py index a86521137e0..7045ff4aa66 100644 --- a/src/azure-cli/azure/cli/command_modules/cosmosdb/tests/latest/test_cosmosdb_backuprestore_scenario.py +++ b/src/azure-cli/azure/cli/command_modules/cosmosdb/tests/latest/test_cosmosdb_backuprestore_scenario.py @@ -10,7 +10,7 @@ from azure.cli.testsdk import (ScenarioTest, ResourceGroupPreparer, live_only) from azure.cli.testsdk.scenario_tests import AllowLargeResponse -from datetime import UTC, datetime, timedelta +import datetime as dt from dateutil import parser @@ -288,7 +288,7 @@ def test_cosmosdb_system_identity_restore(self, resource_group): account_creation_time = restorable_database_account['creationTime'] creation_timestamp_datetime = parser.parse(account_creation_time) - restore_ts = creation_timestamp_datetime + timedelta(minutes=4) + restore_ts = creation_timestamp_datetime + dt.timedelta(minutes=4) import time time.sleep(240) restore_ts_string = restore_ts.isoformat() @@ -364,7 +364,7 @@ def test_cosmosdb_public_network_access_restore(self, resource_group): account_creation_time = restorable_database_account['creationTime'] creation_timestamp_datetime = parser.parse(account_creation_time) - restore_ts = creation_timestamp_datetime + timedelta(minutes=4) + restore_ts = creation_timestamp_datetime + dt.timedelta(minutes=4) import time time.sleep(240) restore_ts_string = restore_ts.isoformat() @@ -438,7 +438,7 @@ def test_cosmosdb_xrr(self, resource_group): # So, we need to wait one hour in order to get the master backup for a restore to be performed. account_creation_time = restorable_database_account['creationTime'] creation_timestamp_datetime = parser.parse(account_creation_time) - restore_ts = creation_timestamp_datetime + timedelta(minutes=61) + restore_ts = creation_timestamp_datetime + dt.timedelta(minutes=61) import time # Cross region restore does not have a forced master backup during restore, so we need # to wait one hour to get the master backup for a restore to be performed. This wait is @@ -509,7 +509,7 @@ def test_cosmosdb_xrr_single_region_account(self, resource_group): # So, we need to wait one hour in order to get the master backup for a restore to be performed. account_creation_time = restorable_database_account['creationTime'] creation_timestamp_datetime = parser.parse(account_creation_time) - restore_ts = creation_timestamp_datetime + timedelta(minutes=61) + restore_ts = creation_timestamp_datetime + dt.timedelta(minutes=61) import time time.sleep(3662) restore_ts_string = restore_ts.isoformat() @@ -533,7 +533,7 @@ class CosmosDBRestoreUnitTests(unittest.TestCase): def test_restore_timestamp_is_utc(self): from azure.cli.command_modules.cosmosdb.custom import _convert_to_utc_timestamp - expected = datetime(2020, 1, 1, tzinfo=UTC) + expected = dt.datetime(2020, 1, 1, tzinfo=dt.UTC) for timestamp in ['2020-01-01T00:00:00', '2020-01-01T00:00:00Z', '2020-01-01T02:00:00+02:00', '2019-12-31T19:00:00-05:00']: with self.subTest(timestamp=timestamp): @@ -545,7 +545,7 @@ def test_restore_accepts_timestamp_without_offset(self, get_accounts, create_acc from azure.cli.command_modules.cosmosdb.custom import cli_cosmosdb_restore account = mock.Mock(account_name='source', deletion_time=None, - creation_time=datetime(2000, 1, 1, tzinfo=UTC)) + creation_time=dt.datetime(2000, 1, 1, tzinfo=dt.UTC)) get_accounts.return_value.list.return_value = [account] cli_cosmosdb_restore(mock.Mock(), mock.Mock(), 'rg', 'source', 'target', '2020-01-01T00:00:00', 'westus') diff --git a/src/azure-cli/azure/cli/command_modules/cosmosdb/tests/latest/test_cosmosdb_commands.py b/src/azure-cli/azure/cli/command_modules/cosmosdb/tests/latest/test_cosmosdb_commands.py index ed6be07b4dc..783e6e0fe3b 100644 --- a/src/azure-cli/azure/cli/command_modules/cosmosdb/tests/latest/test_cosmosdb_commands.py +++ b/src/azure-cli/azure/cli/command_modules/cosmosdb/tests/latest/test_cosmosdb_commands.py @@ -10,7 +10,7 @@ from azure.cli.testsdk import JMESPathCheck, ScenarioTest, ResourceGroupPreparer, KeyVaultPreparer, record_only from knack.util import CLIError from azure.cli.testsdk.scenario_tests import AllowLargeResponse -from datetime import UTC, datetime, timedelta, timezone +import datetime as dt from dateutil import parser class CosmosDBTests(ScenarioTest): @@ -2083,9 +2083,8 @@ def test_cosmosdb_restore_using_create(self, resource_group, location): account_creation_time = restorable_database_account['creationTime'] import dateutil.parser - from datetime import timedelta creation_timestamp_datetime = dateutil.parser.parse(account_creation_time) - restore_ts = creation_timestamp_datetime + timedelta(minutes=4) + restore_ts = creation_timestamp_datetime + dt.timedelta(minutes=4) restore_ts_string = restore_ts.isoformat() import time time.sleep(300) @@ -2125,17 +2124,16 @@ def test_cosmosdb_restore_command(self, resource_group, location): import dateutil.parser import time - from datetime import timedelta, datetime # This should fail as restore time is before account creation time - invalid_restore_time = dateutil.parser.parse(restorable_database_account['creationTime']) - timedelta(days=30) + invalid_restore_time = dateutil.parser.parse(restorable_database_account['creationTime']) - dt.timedelta(days=30) self.kwargs.update({ 'invalid_restore_time': invalid_restore_time.isoformat() }) self.assertRaises(Exception, lambda: self.cmd('az cosmosdb restore --account-name {acc} -g {rg} --restore-timestamp {invalid_restore_time} --location {loc} --target-database-account-name {restored_acc}')) # This should fail as restore time is in future - invalid_restore_time = datetime.now() + timedelta(days=30) + invalid_restore_time = dt.datetime.now() + dt.timedelta(days=30) self.kwargs.update({ 'invalid_restore_time': invalid_restore_time.isoformat() }) @@ -2144,7 +2142,7 @@ def test_cosmosdb_restore_command(self, resource_group, location): # Get correct restore ts account_creation_time = restorable_database_account['creationTime'] creation_timestamp_datetime = dateutil.parser.parse(account_creation_time) - restore_ts = creation_timestamp_datetime + timedelta(minutes=4) + restore_ts = creation_timestamp_datetime + dt.timedelta(minutes=4) restore_ts_string = restore_ts.isoformat() self.kwargs.update({ @@ -2204,12 +2202,11 @@ def test_cosmosdb_deleted_account_restore(self, resource_group, location): import dateutil.parser import time - from datetime import timedelta # Get correct restore ts account_creation_time = restorable_database_account['creationTime'] creation_timestamp_datetime = dateutil.parser.parse(account_creation_time) - restore_ts = creation_timestamp_datetime + timedelta(minutes=4) + restore_ts = creation_timestamp_datetime + dt.timedelta(minutes=4) restore_ts_string = restore_ts.isoformat() self.kwargs.update({ @@ -2267,9 +2264,8 @@ def test_cosmosdb_sql_restorable_commands(self, resource_group, location): account_creation_time = restorable_database_account['creationTime'] import dateutil.parser - from datetime import timedelta creation_timestamp_datetime = dateutil.parser.parse(account_creation_time) - restore_ts = creation_timestamp_datetime + timedelta(minutes=2) + restore_ts = creation_timestamp_datetime + dt.timedelta(minutes=2) import time time.sleep(200) restore_ts_string = restore_ts.isoformat() @@ -2324,9 +2320,8 @@ def test_cosmosdb_mongodb_restorable_commands(self, resource_group, location): account_creation_time = restorable_database_account['creationTime'] import dateutil.parser - from datetime import timedelta creation_timestamp_datetime = dateutil.parser.parse(account_creation_time) - restore_ts = creation_timestamp_datetime + timedelta(minutes=2) + restore_ts = creation_timestamp_datetime + dt.timedelta(minutes=2) import time time.sleep(200) restore_ts_string = restore_ts.isoformat() @@ -2505,7 +2500,7 @@ def test_cosmosdb_gremlin_account_restore_using_create(self, resource_group): account_creation_time = restorable_database_account['creationTime'] creation_timestamp_datetime = parser.parse(account_creation_time) - restore_ts = creation_timestamp_datetime + timedelta(minutes=4) + restore_ts = creation_timestamp_datetime + dt.timedelta(minutes=4) restore_ts_string = restore_ts.isoformat() import time time.sleep(240) @@ -2547,7 +2542,7 @@ def test_cosmosdb_gremlin_account_restore_command(self, resource_group): account_creation_time = restorable_database_account['creationTime'] creation_timestamp_datetime = parser.parse(account_creation_time) - restore_ts = creation_timestamp_datetime + timedelta(minutes=4) + restore_ts = creation_timestamp_datetime + dt.timedelta(minutes=4) import time time.sleep(240) restore_ts_string = restore_ts.isoformat() @@ -2601,7 +2596,7 @@ def test_cosmosdb_gremlin_restorable_commands(self, resource_group): account_creation_time = restorable_database_account['creationTime'] creation_timestamp_datetime = parser.parse(account_creation_time) - restore_ts = creation_timestamp_datetime + timedelta(minutes=2) + restore_ts = creation_timestamp_datetime + dt.timedelta(minutes=2) import time time.sleep(120) restore_ts_string = restore_ts.isoformat() @@ -2636,7 +2631,7 @@ def test_cosmosdb_table_account_restore_using_create(self, resource_group): account_creation_time = restorable_database_account['creationTime'] creation_timestamp_datetime = parser.parse(account_creation_time) - restore_ts = creation_timestamp_datetime + timedelta(minutes=4) + restore_ts = creation_timestamp_datetime + dt.timedelta(minutes=4) restore_ts_string = restore_ts.isoformat() import time time.sleep(240) @@ -2676,7 +2671,7 @@ def test_cosmosdb_table_account_restore_command(self, resource_group): account_creation_time = restorable_database_account['creationTime'] creation_timestamp_datetime = parser.parse(account_creation_time) - restore_ts = creation_timestamp_datetime + timedelta(minutes=4) + restore_ts = creation_timestamp_datetime + dt.timedelta(minutes=4) import time time.sleep(240) restore_ts_string = restore_ts.isoformat() @@ -2723,7 +2718,7 @@ def test_cosmosdb_table_restorable_commands(self, resource_group): account_creation_time = restorable_database_account['creationTime'] creation_timestamp_datetime = parser.parse(account_creation_time) - restore_ts = creation_timestamp_datetime + timedelta(minutes=2) + restore_ts = creation_timestamp_datetime + dt.timedelta(minutes=2) import time time.sleep(120) restore_ts_string = restore_ts.isoformat() @@ -2934,7 +2929,7 @@ def test_cosmosdb_sql_normal_database_restore(self, resource_group): database_list = self.cmd('az cosmosdb sql database list -g {rg} -a {acc}').get_output_in_json() assert len(database_list) == 1 - restore_ts_string = datetime.now(UTC).isoformat() + restore_ts_string = dt.datetime.now(dt.UTC).isoformat() self.kwargs.update({ 'rts': restore_ts_string @@ -3019,7 +3014,7 @@ def test_cosmosdb_sql_shared_database_restore(self, resource_group): container_list = self.cmd('az cosmosdb sql container list -g {rg} -a {acc} -d {db_name}').get_output_in_json() assert len(container_list) == 1 - restore_ts_string = datetime.now(UTC).isoformat() + restore_ts_string = dt.datetime.now(dt.UTC).isoformat() self.kwargs.update({ 'rts': restore_ts_string, @@ -3125,7 +3120,7 @@ def test_cosmosdb_sql_normal_database_prov_container_restore(self, resource_grou container_list = self.cmd('az cosmosdb sql container list -g {rg} -a {acc} -d {db_name}').get_output_in_json() assert len(container_list) == 1 - restore_ts_string = datetime.now(UTC).isoformat() + restore_ts_string = dt.datetime.now(dt.UTC).isoformat() self.kwargs.update({ 'rts': restore_ts_string @@ -3209,7 +3204,7 @@ def test_cosmosdb_sql_normal_database_prov_container_restore_nt(self, resource_g container_list = self.cmd('az cosmosdb sql container list -g {rg} -a {acc} -d {db_name}').get_output_in_json() assert len(container_list) == 1 - restore_ts_string = datetime.now(UTC).isoformat() + restore_ts_string = dt.datetime.now(dt.UTC).isoformat() self.kwargs.update({ 'rts': restore_ts_string @@ -3301,7 +3296,7 @@ def test_cosmosdb_sql_shared_database_prov_container_restore(self, resource_grou container_list = self.cmd('az cosmosdb sql container list -g {rg} -a {acc} -d {db_name}').get_output_in_json() assert len(container_list) == 1 - restore_ts_string = datetime.now(UTC).isoformat() + restore_ts_string = dt.datetime.now(dt.UTC).isoformat() self.kwargs.update({ 'rts': restore_ts_string @@ -3381,7 +3376,7 @@ def test_cosmosdb_mongodb_normal_database_prov_collection_restore(self, resource assert self.cmd('az cosmosdb mongodb collection exists -g {rg} -a {acc} -d {db_name} -n {col_name}').get_output_in_json() - restore_ts_string = datetime.now(UTC).isoformat() + restore_ts_string = dt.datetime.now(dt.UTC).isoformat() self.kwargs.update({ 'rts': restore_ts_string @@ -3508,7 +3503,7 @@ def test_cosmosdb_mongodb_shared_database_prov_collection_restore(self, resource assert self.cmd('az cosmosdb mongodb collection exists -g {rg} -a {acc} -d {db_name} -n {col_name}').get_output_in_json() - restore_ts_string = datetime.now(UTC).isoformat() + restore_ts_string = dt.datetime.now(dt.UTC).isoformat() self.kwargs.update({ 'rts': restore_ts_string @@ -3595,7 +3590,7 @@ def test_cosmosdb_gremlin_database_graph_restore(self, resource_group): ]).get_output_in_json() #restore time - restore_ts_string = datetime.now(UTC).isoformat() + restore_ts_string = dt.datetime.now(dt.UTC).isoformat() self.kwargs.update({ 'rts': restore_ts_string }) @@ -3729,7 +3724,7 @@ def test_cosmosdb_table(self, resource_group): assert self.cmd('az cosmosdb table exists -g {rg} -a {acc} -n {table_name}').get_output_in_json() - restore_ts_string = datetime.now(UTC).isoformat() + restore_ts_string = dt.datetime.now(dt.UTC).isoformat() self.kwargs.update({ 'rts': restore_ts_string diff --git a/src/azure-cli/azure/cli/command_modules/eventgrid/tests/latest/test_eventgrid_commands.py b/src/azure-cli/azure/cli/command_modules/eventgrid/tests/latest/test_eventgrid_commands.py index ba2a2009000..07d77a692aa 100644 --- a/src/azure-cli/azure/cli/command_modules/eventgrid/tests/latest/test_eventgrid_commands.py +++ b/src/azure-cli/azure/cli/command_modules/eventgrid/tests/latest/test_eventgrid_commands.py @@ -10,7 +10,7 @@ from azure.cli.testsdk import ScenarioTest, ResourceGroupPreparer, StorageAccountPreparer from knack.util import CLIError -from datetime import UTC, datetime, timedelta +import datetime as dt EVENTGRID_TRIGGER_ENDPOINT_URL = "https://devexpfuncappdestination.azurewebsites.net/runtime/webhooks/EventGrid?functionName=EventGridTrigger1&code=" EVENTGRID_TRIGGER_ENDPOINT_BASE_URL = "https://devexpfuncappdestination.azurewebsites.net/runtime/webhooks/EventGrid" @@ -1480,9 +1480,9 @@ def test_Partner_scenarios(self, resource_group): guid3 = uuid.uuid1() destination_subscription_id = '5b4b650e-28b9-4790-b3ab-ddbd88d727c4' source = self.create_random_name(prefix='cli', length=40) - exp_time = datetime.now(UTC) + timedelta(hours=24) + exp_time = dt.datetime.now(dt.UTC) + dt.timedelta(hours=24) exp_time = exp_time.isoformat() - creation_time = datetime.now(UTC).isoformat() + creation_time = dt.datetime.now(dt.UTC).isoformat() long_description = 'This is long description sample' customer_service_number1 = '+1 800 123 4567' @@ -1865,9 +1865,9 @@ def test_Partner_scenarios_v2(self, resource_group): destination_endpoint_url = SECURED_WEBBOOK_DESTINATION_ENDPOINT_URL source = self.create_random_name(prefix='cli', length=40) - exp_time = datetime.now(UTC) + timedelta(hours=24) + exp_time = dt.datetime.now(dt.UTC) + dt.timedelta(hours=24) exp_time = exp_time.isoformat() - creation_time = datetime.now(UTC).isoformat() + creation_time = dt.datetime.now(dt.UTC).isoformat() default_maximum_expiration_time_in_days = 5 updated_default_max_expiration_time_in_days = 15 diff --git a/src/azure-cli/azure/cli/command_modules/feedback/custom.py b/src/azure-cli/azure/cli/command_modules/feedback/custom.py index 21fc5227bae..09496388db9 100644 --- a/src/azure-cli/azure/cli/command_modules/feedback/custom.py +++ b/src/azure-cli/azure/cli/command_modules/feedback/custom.py @@ -3,7 +3,7 @@ # Licensed under the MIT License. See License.txt in the project root for license information. # -------------------------------------------------------------------------------------------- -import datetime +import datetime as dt import math import os import platform @@ -106,8 +106,8 @@ class CommandLogFile: def __init__(self, log_file_path, time_now=None): - if (time_now is not None) and (not isinstance(time_now, datetime.datetime)): - raise TypeError("Expected type {} for time_now, instead received {}.".format(datetime.datetime, type(time_now))) # pylint: disable=line-too-long + if (time_now is not None) and (not isinstance(time_now, dt.datetime)): + raise TypeError("Expected type {} for time_now, instead received {}.".format(dt.datetime, type(time_now))) # pylint: disable=line-too-long if not os.path.isfile(log_file_path): raise ValueError("File {} is not an existing file.".format(log_file_path)) @@ -116,7 +116,7 @@ def __init__(self, log_file_path, time_now=None): self._log_file_path = log_file_path if time_now is None: - self._time_now = datetime.datetime.now() + self._time_now = dt.datetime.now() else: self._time_now = time_now @@ -179,7 +179,7 @@ def get_command_time_str(self): total_seconds = self.metadata_tup.seconds_ago - time_delta = datetime.timedelta(seconds=total_seconds) + time_delta = dt.timedelta(seconds=total_seconds) logger.debug("%s time_delta", time_delta) if time_delta.days > 0: @@ -201,12 +201,12 @@ def _get_command_metadata_from_file(self): if not self._log_file_path: return None - time_now = datetime.datetime.now() if not self._time_now else self._time_now + time_now = dt.datetime.now() if not self._time_now else self._time_now try: _, file_name = os.path.split(self._log_file_path) poss_date, poss_time, poss_command, poss_pid, _ = file_name.split(".") - date_time_stamp = datetime.datetime.strptime("{}-{}".format(poss_date, poss_time), "%Y-%m-%d-%H-%M-%S") + date_time_stamp = dt.datetime.strptime("{}-{}".format(poss_date, poss_time), "%Y-%m-%d-%H-%M-%S") command = "az " + poss_command.replace("_", " ") if poss_command != _UNKNOWN_COMMAND else self.UNKNOWN_CMD # pylint: disable=line-too-long except ValueError as e: logger.debug("Could not load metadata from file name %s.", self._log_file_path) @@ -490,7 +490,7 @@ def _pad_string(my_str, pad_len): my_str += " " return my_str - time_now = datetime.datetime.now() + time_now = dt.datetime.now() command_log_files = _get_command_log_files(cmd.cli_ctx, time_now) @@ -605,8 +605,8 @@ def handle_survey(cmd): with open(GLOBAL_SURVEY_NOTE_PATH, 'r') as f: survey_note = json.load(f) if survey_note['last_prompt_time']: - last_prompt_time = datetime.datetime.strptime(survey_note['last_prompt_time'], '%Y-%m-%dT%H:%M:%S').replace(tzinfo=datetime.UTC) - use_duration = datetime.datetime.now(datetime.UTC) - last_prompt_time + last_prompt_time = dt.datetime.strptime(survey_note['last_prompt_time'], '%Y-%m-%dT%H:%M:%S').replace(tzinfo=dt.UTC) + use_duration = dt.datetime.now(dt.UTC) - last_prompt_time url = _SURVEY_URL.format(installation_id=Profile(cli_ctx=cmd.cli_ctx).get_installation_id(), version=core_version, diff --git a/src/azure-cli/azure/cli/command_modules/feedback/tests/latest/test_feedback.py b/src/azure-cli/azure/cli/command_modules/feedback/tests/latest/test_feedback.py index 986c3e5a889..c4e7d66f365 100644 --- a/src/azure-cli/azure/cli/command_modules/feedback/tests/latest/test_feedback.py +++ b/src/azure-cli/azure/cli/command_modules/feedback/tests/latest/test_feedback.py @@ -3,7 +3,7 @@ # Licensed under the MIT License. See License.txt in the project root for license information. # -------------------------------------------------------------------------------------------- -import datetime +import datetime as dt import logging import os import time @@ -102,7 +102,7 @@ def _helper_test_get_repository_url_with_no_extension_match_raw(self): self.assertEqual(_RAW_EXTENSIONS_ISSUES_URL, repo_url) def _helper_test_log_metadata_correct(self): - time_now = datetime.datetime.now() + time_now = dt.datetime.now() command_log_files = _get_command_log_files(self.cli_ctx, time_now) p_id = os.getpid() self.assertEqual(len(command_log_files), 4) @@ -123,7 +123,7 @@ def _helper_test_log_metadata_correct(self): self.assertEqual("az alias list", command_log_files[3].metadata_tup.cmd) def _helper_test_log_contents_correct(self): - time_now = datetime.datetime.now() + time_now = dt.datetime.now() command_log_files = _get_command_log_files(self.cli_ctx, time_now) self.assertEqual(len(command_log_files), 4) diff --git a/src/azure-cli/azure/cli/command_modules/iot/_utils.py b/src/azure-cli/azure/cli/command_modules/iot/_utils.py index 9799c91f697..5b4ac2efd03 100644 --- a/src/azure-cli/azure/cli/command_modules/iot/_utils.py +++ b/src/azure-cli/azure/cli/command_modules/iot/_utils.py @@ -3,7 +3,7 @@ # Licensed under the MIT License. See License.txt in the project root for license information. # -------------------------------------------------------------------------------------------- -import datetime +import datetime as dt from os.path import exists, join import base64 from cryptography import x509 @@ -31,9 +31,9 @@ def create_self_signed_certificate(device_id, valid_days, cert_output_dir): .issuer_name(subject_name) .public_key(key.public_key()) .serial_number(x509.random_serial_number()) - .not_valid_before(datetime.datetime.now(datetime.UTC)) + .not_valid_before(dt.datetime.now(dt.UTC)) .not_valid_after( - datetime.datetime.now(datetime.UTC) + datetime.timedelta(days=valid_days) + dt.datetime.now(dt.UTC) + dt.timedelta(days=valid_days) ) .sign(key, hashes.SHA256()) ) diff --git a/src/azure-cli/azure/cli/command_modules/iot/tests/latest/_test_utils.py b/src/azure-cli/azure/cli/command_modules/iot/tests/latest/_test_utils.py index 917401f1943..e6d065ef2f4 100644 --- a/src/azure-cli/azure/cli/command_modules/iot/tests/latest/_test_utils.py +++ b/src/azure-cli/azure/cli/command_modules/iot/tests/latest/_test_utils.py @@ -3,7 +3,7 @@ # Licensed under the MIT License. See License.txt in the project root for license information. # -------------------------------------------------------------------------------------------- -import datetime +import datetime as dt from os.path import exists import os from cryptography import x509 @@ -29,9 +29,9 @@ def _create_test_cert(cert_file, key_file, subject, valid_days, serial_number): .issuer_name(subject_name) .public_key(key.public_key()) .serial_number(serial_number) - .not_valid_before(datetime.datetime.now(datetime.UTC)) + .not_valid_before(dt.datetime.now(dt.UTC)) .not_valid_after( - datetime.datetime.now(datetime.UTC) + datetime.timedelta(days=valid_days) + dt.datetime.now(dt.UTC) + dt.timedelta(days=valid_days) ) .sign(key, hashes.SHA256()) ) @@ -92,9 +92,9 @@ def _create_verification_cert(cert_file, key_file, verification_file, nonce, val .issuer_name(signing_cert.subject) .public_key(key.public_key()) .serial_number(serial_number) - .not_valid_before(datetime.datetime.now(datetime.UTC)) + .not_valid_before(dt.datetime.now(dt.UTC)) .not_valid_after( - datetime.datetime.now(datetime.UTC) + datetime.timedelta(days=valid_days) + dt.datetime.now(dt.UTC) + dt.timedelta(days=valid_days) ) .sign(k, hashes.SHA256()) ) diff --git a/src/azure-cli/azure/cli/command_modules/keyvault/tests/latest/test_keyvault_commands.py b/src/azure-cli/azure/cli/command_modules/keyvault/tests/latest/test_keyvault_commands.py index 8406e98afd6..dcc9f56b8b9 100644 --- a/src/azure-cli/azure/cli/command_modules/keyvault/tests/latest/test_keyvault_commands.py +++ b/src/azure-cli/azure/cli/command_modules/keyvault/tests/latest/test_keyvault_commands.py @@ -11,7 +11,7 @@ import time from unittest import mock import unittest -from datetime import UTC, datetime, timedelta +import datetime as dt from dateutil import tz from ipaddress import ip_network @@ -97,13 +97,13 @@ def _clear_hsm(test, hsm_url): class DateTimeParseTest(unittest.TestCase): def test_parse_asn1_date(self): - expected = datetime(year=2017, - month=4, - day=24, - hour=16, - minute=37, - second=20, - tzinfo=tz.tzutc()) + expected = dt.datetime(year=2017, + month=4, + day=24, + hour=16, + minute=37, + second=20, + tzinfo=tz.tzutc()) self.assertEqual(_asn1_to_iso8601("20170424163720Z"), expected) @@ -975,8 +975,8 @@ def test_keyvault_hsm_selective_key_restore(self): 'key_name': self.create_random_name('selective-restore-', 24), 'storage_account': self.create_random_name('clitesthsmsa', 24), 'blob': self.create_random_name('clitesthsmblob', 24), - 'sas_start': (datetime.now(UTC) - timedelta(minutes=30)).strftime('%Y-%m-%dT%H:%M:%SZ'), - 'sas_expiry': (datetime.now(UTC) + timedelta(minutes=30)).strftime('%Y-%m-%dT%H:%M:%SZ') + 'sas_start': (dt.datetime.now(dt.UTC) - dt.timedelta(minutes=30)).strftime('%Y-%m-%dT%H:%M:%SZ'), + 'sas_expiry': (dt.datetime.now(dt.UTC) + dt.timedelta(minutes=30)).strftime('%Y-%m-%dT%H:%M:%SZ') }) _clear_hsm(self, hsm_url=self.kwargs['hsm_url']) @@ -1031,8 +1031,8 @@ def test_keyvault_hsm_full_backup_restore(self, resource_group, managed_hsm, sto 'hsm_name': managed_hsm, 'storage_account': storage_account, 'blob': self.create_random_name('clitesthsmblob', 24), - 'sas_start': (datetime.now(UTC) - timedelta(minutes=30)).strftime('%Y-%m-%dT%H:%M:%SZ'), - 'sas_expiry': (datetime.now(UTC) + timedelta(minutes=30)).strftime('%Y-%m-%dT%H:%M:%SZ') + 'sas_start': (dt.datetime.now(dt.UTC) - dt.timedelta(minutes=30)).strftime('%Y-%m-%dT%H:%M:%SZ'), + 'sas_expiry': (dt.datetime.now(dt.UTC) + dt.timedelta(minutes=30)).strftime('%Y-%m-%dT%H:%M:%SZ') }) self.cmd('az storage container create -n {blob} --account-name {storage_account} -g {rg}') @@ -2662,8 +2662,8 @@ def _generate_certificate(path, keyfile=None, password=None): .issuer_name(issuer) \ .public_key(key.public_key()) \ .serial_number(x509.random_serial_number()) \ - .not_valid_before(datetime.now(UTC)) \ - .not_valid_after(datetime.now(UTC) + timedelta(days=10)) \ + .not_valid_before(dt.datetime.now(dt.UTC)) \ + .not_valid_after(dt.datetime.now(dt.UTC) + dt.timedelta(days=10)) \ .sign(key, hashes.SHA256(), default_backend()) # Write the cert out to disk diff --git a/src/azure-cli/azure/cli/command_modules/lab/validators.py b/src/azure-cli/azure/cli/command_modules/lab/validators.py index 77ef72a7536..c376c7acb56 100644 --- a/src/azure-cli/azure/cli/command_modules/lab/validators.py +++ b/src/azure-cli/azure/cli/command_modules/lab/validators.py @@ -113,11 +113,11 @@ def _validate_expiration_date(args): """ Validates expiration date if provided. """ if has_value(args.expiration_date): - import datetime + import datetime as dt import dateutil.parser expiration_date = dateutil.parser.parse(args.expiration_date.to_serialized_data()) - expiration_date = expiration_date.replace(tzinfo=expiration_date.tzinfo or datetime.UTC) - if datetime.datetime.now(datetime.UTC) >= expiration_date: + expiration_date = expiration_date.replace(tzinfo=expiration_date.tzinfo or dt.UTC) + if dt.datetime.now(dt.UTC) >= expiration_date: raise ArgumentUsageError( "Expiration date '{}' must be in future.".format(args.expiration_date.to_serialized_data())) diff --git a/src/azure-cli/azure/cli/command_modules/monitor/_legacy/custom.py b/src/azure-cli/azure/cli/command_modules/monitor/_legacy/custom.py index 8bbff592c6f..f49b5759c7d 100644 --- a/src/azure-cli/azure/cli/command_modules/monitor/_legacy/custom.py +++ b/src/azure-cli/azure/cli/command_modules/monitor/_legacy/custom.py @@ -33,12 +33,12 @@ def list_activity_log(cmd, correlation_id=None, resource_group=None, resource_id def _build_activity_log_odata_filter(correlation_id=None, resource_group=None, resource_id=None, resource_provider=None, start_time=None, end_time=None, caller=None, status=None, offset=None): - from datetime import UTC, datetime + import datetime as dt import dateutil.parser if not start_time and not end_time: # if neither value provided, end_time is now - end_time = datetime.now(UTC).isoformat() + end_time = dt.datetime.now(dt.UTC).isoformat() if not start_time: # if no start_time, apply offset backwards from end_time start_time = (dateutil.parser.parse(end_time) - offset).isoformat() @@ -108,13 +108,13 @@ def list_metrics(cmd, resource, metadata=None, dimension=None, aggregation=None, metrics=None, filters=None, metric_namespace=None, orderby=None, top=10): - from datetime import UTC, datetime + import datetime as dt import dateutil.parser from urllib.parse import quote_plus if not start_time and not end_time: # if neither value provided, end_time is now - end_time = datetime.now(UTC).isoformat() + end_time = dt.datetime.now(dt.UTC).isoformat() if not start_time: # if no start_time, apply offset backwards from end_time start_time = (dateutil.parser.parse(end_time) - offset).isoformat() diff --git a/src/azure-cli/azure/cli/command_modules/monitor/custom.py b/src/azure-cli/azure/cli/command_modules/monitor/custom.py index 1d6bf1680e9..34ba567323f 100644 --- a/src/azure-cli/azure/cli/command_modules/monitor/custom.py +++ b/src/azure-cli/azure/cli/command_modules/monitor/custom.py @@ -33,12 +33,12 @@ def list_activity_log(cmd, correlation_id=None, resource_group=None, resource_id def _build_activity_log_odata_filter(correlation_id=None, resource_group=None, resource_id=None, resource_provider=None, start_time=None, end_time=None, caller=None, status=None, offset=None): - from datetime import UTC, datetime + import datetime as dt import dateutil.parser if not start_time and not end_time: # if neither value provided, end_time is now - end_time = datetime.now(UTC).isoformat() + end_time = dt.datetime.now(dt.UTC).isoformat() if not start_time: # if no start_time, apply offset backwards from end_time start_time = (dateutil.parser.parse(end_time) - offset).isoformat() @@ -108,13 +108,13 @@ def list_metrics(cmd, resource, metadata=None, dimension=None, aggregation=None, metrics=None, filters=None, metric_namespace=None, orderby=None, top=10): - from datetime import UTC, datetime + import datetime as dt import dateutil.parser from urllib.parse import quote_plus if not start_time and not end_time: # if neither value provided, end_time is now - end_time = datetime.now(UTC).isoformat() + end_time = dt.datetime.now(dt.UTC).isoformat() if not start_time: # if no start_time, apply offset backwards from end_time start_time = (dateutil.parser.parse(end_time) - offset).isoformat() diff --git a/src/azure-cli/azure/cli/command_modules/mysql/_util.py b/src/azure-cli/azure/cli/command_modules/mysql/_util.py index 064ef9df6a5..a1c3cd2a20e 100644 --- a/src/azure-cli/azure/cli/command_modules/mysql/_util.py +++ b/src/azure-cli/azure/cli/command_modules/mysql/_util.py @@ -15,7 +15,7 @@ import sys import yaml from time import sleep -from datetime import UTC, datetime +import datetime as dt from knack.log import get_logger from knack.arguments import ignore_type from knack.prompting import prompt_pass, prompt_y_n, NoTTYException @@ -325,7 +325,7 @@ def _map_maintenance_window(day_of_week): def get_current_time(): - return datetime.now(UTC).replace(microsecond=0).isoformat() + return dt.datetime.now(dt.UTC).replace(microsecond=0).isoformat() def get_id_components(rid): @@ -615,7 +615,7 @@ def get_firewall_rules_from_paged_response(firewall_rules): def get_current_utc_time(): - return datetime.now(UTC) + return dt.datetime.now(dt.UTC) class ImportFromStorageState(Enum): @@ -648,7 +648,7 @@ def get_progress_message(self): return msg def _get_eta_time_duration_in_user_readable_string(self): - time_remaining = datetime.fromisoformat(self._import_estimated_completion_time) - get_current_utc_time() + time_remaining = dt.datetime.fromisoformat(self._import_estimated_completion_time) - get_current_utc_time() msg = " ETA : " if time_remaining.total_seconds() < 60: return msg + "Few seconds remaining" diff --git a/src/azure-cli/azure/cli/command_modules/mysql/custom.py b/src/azure-cli/azure/cli/command_modules/mysql/custom.py index 7ac6286c76c..60c9c38dc4c 100644 --- a/src/azure-cli/azure/cli/command_modules/mysql/custom.py +++ b/src/azure-cli/azure/cli/command_modules/mysql/custom.py @@ -8,7 +8,7 @@ import re import math -from datetime import UTC, datetime, timedelta +import datetime as dt from knack.log import get_logger from urllib.request import urlretrieve from importlib import import_module @@ -106,7 +106,7 @@ def firewall_rule_create_func(cmd, client, resource_group_name, server_name, fir start_ip_address = end_ip_address if firewall_rule_name is None: - now = datetime.now() + now = dt.datetime.now() firewall_rule_name = 'FirewallIPAddress_{}-{}-{}_{}-{}-{}'.format(now.year, now.month, now.day, now.hour, now.minute, now.second) if start_ip_address == '0.0.0.0' and end_ip_address == '0.0.0.0': @@ -272,7 +272,7 @@ def flexible_server_log_list(client, resource_group_name, server_name, filename_ if file_last_written is None: file_last_written = 72 - time_line = datetime.now(UTC) - timedelta(hours=file_last_written) + time_line = dt.datetime.now(dt.UTC) - dt.timedelta(hours=file_last_written) for f in all_files: if f.last_modified_time < time_line: diff --git a/src/azure-cli/azure/cli/command_modules/mysql/tests/latest/test_mysql_scenario.py b/src/azure-cli/azure/cli/command_modules/mysql/tests/latest/test_mysql_scenario.py index 369936f086d..414852a62a3 100644 --- a/src/azure-cli/azure/cli/command_modules/mysql/tests/latest/test_mysql_scenario.py +++ b/src/azure-cli/azure/cli/command_modules/mysql/tests/latest/test_mysql_scenario.py @@ -7,7 +7,7 @@ import uuid import unittest -from datetime import UTC, datetime, timedelta +import datetime as dt from time import sleep from dateutil import parser from azure.cli.testsdk.scenario_tests import AllowLargeResponse @@ -457,7 +457,7 @@ def _test_flexible_server_restore_mgmt(self, database_engine, resource_group): result = self.cmd('{} flexible-server show -g {} -n {}'.format(database_engine, resource_group, source_server)).get_output_in_json() # Wait until snapshot is created - current_time = datetime.now(UTC).isoformat() + current_time = dt.datetime.now(dt.UTC).isoformat() earliest_restore_time = result['backup']['earliestRestoreDate'] seconds_to_wait = (parser.isoparse(earliest_restore_time) - parser.isoparse(current_time)).total_seconds() os.environ.get(ENV_LIVE_TEST, False) and sleep(max(0, seconds_to_wait) + 180) @@ -689,7 +689,7 @@ def _test_flexible_server_georestore_update_mgmt(self, database_engine, resource .format(database_engine, resource_group, source_server), checks=[JMESPathCheck('backup.geoRedundantBackup', 'Enabled')]).get_output_in_json() - current_time = datetime.now(UTC).isoformat() + current_time = dt.datetime.now(dt.UTC).isoformat() earliest_restore_time = result['backup']['earliestRestoreDate'] seconds_to_wait = (parser.isoparse(earliest_restore_time) - parser.isoparse(current_time)).total_seconds() os.environ.get(ENV_LIVE_TEST, False) and sleep(max(0, seconds_to_wait) + 1200) @@ -935,7 +935,7 @@ def main_tests(geo_redundant_backup): expect_failure=True) # restore backup - current_time = datetime.now(UTC).isoformat() + current_time = dt.datetime.now(dt.UTC).isoformat() earliest_restore_time = result['backup']['earliestRestoreDate'] seconds_to_wait = (parser.isoparse(earliest_restore_time) - parser.isoparse(current_time)).total_seconds() sleep(max(0, seconds_to_wait)) @@ -1518,7 +1518,7 @@ def _test_mysql_flexible_server_public_access_georestore(self, database_engine, restore_server = self.cmd('resource show --id {} --api-version {}'.format(result['id'], api_version)).get_output_in_json() - current_time = datetime.now(UTC).isoformat() + current_time = dt.datetime.now(dt.UTC).isoformat() earliest_restore_time = restore_server['properties']['backup']['earliestRestoreDate'] seconds_to_wait = (parser.isoparse(earliest_restore_time) - parser.isoparse(current_time)).total_seconds() time.sleep(max(0, seconds_to_wait) + 180) @@ -1541,7 +1541,7 @@ def _test_mysql_flexible_server_public_access_restore(self, database_engine, res restore_server = self.cmd('resource show --id {} --api-version {}'.format(result['id'], api_version)).get_output_in_json() - current_time = datetime.now(UTC).isoformat() + current_time = dt.datetime.now(dt.UTC).isoformat() earliest_restore_time = restore_server['properties']['backup']['earliestRestoreDate'] seconds_to_wait = (parser.isoparse(earliest_restore_time) - parser.isoparse(current_time)).total_seconds() print(current_time) @@ -2407,7 +2407,7 @@ def _test_flexible_server_export_create_mgmt(self, database_engine, resource_gro # Create the blob container container = self.create_container(target_account_info) - expiry = (datetime.now(UTC) + timedelta(hours=1)).strftime('%Y-%m-%dT%H:%MZ') + expiry = (dt.datetime.now(dt.UTC) + dt.timedelta(hours=1)).strftime('%Y-%m-%dT%H:%MZ') self.kwargs.update({ 'expiry': expiry, diff --git a/src/azure-cli/azure/cli/command_modules/netappfiles/tests/latest/test_bucket_commands.py b/src/azure-cli/azure/cli/command_modules/netappfiles/tests/latest/test_bucket_commands.py index c9816fa2737..0bfeb791352 100644 --- a/src/azure-cli/azure/cli/command_modules/netappfiles/tests/latest/test_bucket_commands.py +++ b/src/azure-cli/azure/cli/command_modules/netappfiles/tests/latest/test_bucket_commands.py @@ -4,7 +4,7 @@ # -------------------------------------------------------------------------------------------- import base64 -import datetime +import datetime as dt import time import unittest @@ -39,8 +39,8 @@ def _generate_self_signed_cert(fqdn): .issuer_name(issuer) .public_key(key.public_key()) .serial_number(x509.random_serial_number()) - .not_valid_before(datetime.datetime.now(datetime.UTC)) - .not_valid_after(datetime.datetime.now(datetime.UTC) + datetime.timedelta(days=365)) + .not_valid_before(dt.datetime.now(dt.UTC)) + .not_valid_after(dt.datetime.now(dt.UTC) + dt.timedelta(days=365)) .sign(key, hashes.SHA256()) ) pem_data = cert.public_bytes(serialization.Encoding.PEM) + key.private_bytes( diff --git a/src/azure-cli/azure/cli/command_modules/network/tests/latest/test_network_commands.py b/src/azure-cli/azure/cli/command_modules/network/tests/latest/test_network_commands.py index fdd94694d76..b98c81c9184 100644 --- a/src/azure-cli/azure/cli/command_modules/network/tests/latest/test_network_commands.py +++ b/src/azure-cli/azure/cli/command_modules/network/tests/latest/test_network_commands.py @@ -6595,8 +6595,8 @@ class VpnConnectionAuthenticationScenarioTest(ScenarioTest): @staticmethod def _get_now_utc(): - import datetime - return datetime.datetime.now(datetime.UTC) + import datetime as dt + return dt.datetime.now(dt.UTC) @staticmethod def _format_subject_dn(common_name: str): @@ -6615,7 +6615,7 @@ def generate_ca_chain(root_cert_path, intermediate_cert_path): from cryptography.hazmat.primitives import hashes, serialization from cryptography.hazmat.primitives.asymmetric import rsa from cryptography.hazmat.backends import default_backend - import datetime + import datetime as dt now = VpnConnectionAuthenticationScenarioTest._get_now_utc() @@ -6647,9 +6647,9 @@ def generate_ca_chain(root_cert_path, intermediate_cert_path): ).serial_number( x509.random_serial_number() ).not_valid_before( - now - datetime.timedelta(minutes=5) + now - dt.timedelta(minutes=5) ).not_valid_after( - now + datetime.timedelta(days=3650) + now + dt.timedelta(days=3650) ).add_extension( x509.BasicConstraints(ca=True, path_length=1), critical=True, @@ -6702,9 +6702,9 @@ def generate_ca_chain(root_cert_path, intermediate_cert_path): ).serial_number( x509.random_serial_number() ).not_valid_before( - now - datetime.timedelta(minutes=5) + now - dt.timedelta(minutes=5) ).not_valid_after( - now + datetime.timedelta(days=1825) + now + dt.timedelta(days=1825) ).add_extension( x509.BasicConstraints(ca=True, path_length=0), critical=True, @@ -6750,7 +6750,7 @@ def generate_leaf_certificate(common_name, signer_key, signer_cert, is_ca=False, from cryptography.hazmat.primitives import hashes from cryptography.hazmat.primitives.asymmetric import rsa from cryptography.hazmat.backends import default_backend - import datetime + import datetime as dt now = VpnConnectionAuthenticationScenarioTest._get_now_utc() @@ -6782,9 +6782,9 @@ def generate_leaf_certificate(common_name, signer_key, signer_cert, is_ca=False, ).serial_number( x509.random_serial_number() ).not_valid_before( - now - datetime.timedelta(minutes=5) + now - dt.timedelta(minutes=5) ).not_valid_after( - now + datetime.timedelta(days=days) + now + dt.timedelta(days=days) ) # Leaf cert constraints @@ -7711,7 +7711,7 @@ def __init__(self, method_name): @ResourceGroupPreparer(name_prefix='cli_test_active_active_vnet_vnet_connection') @StorageAccountPreparer(name_prefix='clitestvpncnn') def test_network_active_active_vnet_connection(self, resource_group, storage_account): - from datetime import UTC, datetime, timedelta + import datetime as dt self.kwargs.update({ 'subnet': 'GatewaySubnet', 'vnet1': 'vnet1', @@ -7735,7 +7735,7 @@ def test_network_active_active_vnet_connection(self, resource_group, storage_acc 'bgp_peer2': '10.53.255.253', 'storage_account': storage_account, 'ctn': self.create_random_name(prefix='clitestvpngw', length=24), - 'expiry': (datetime.now(UTC) + timedelta(hours=3)).strftime('%Y-%m-%dT%H:%MZ') + 'expiry': (dt.datetime.now(dt.UTC) + dt.timedelta(hours=3)).strftime('%Y-%m-%dT%H:%MZ') }) self.cmd('storage container create --account-name {storage_account} --name {ctn}') @@ -7989,7 +7989,7 @@ def test_network_vpn_gateway_disconnect_connects(self, resource_group): @ResourceGroupPreparer(name_prefix='cli_test_vpn_gateway_package_capture', location='westus') @StorageAccountPreparer(name_prefix='clitestvpngw') def test_network_vpn_gateway_package_capture(self, resource_group, storage_account): - from datetime import UTC, datetime, timedelta + import datetime as dt self.kwargs.update({ 'vnet1': 'myvnet1', 'gw1': 'gateway1', @@ -7997,7 +7997,7 @@ def test_network_vpn_gateway_package_capture(self, resource_group, storage_accou 'ip1': 'pubip1', 'storage_account': storage_account, 'ctn': self.create_random_name(prefix='clitestvpngw', length=24), - 'expiry': (datetime.now(UTC) + timedelta(hours=3)).strftime('%Y-%m-%dT%H:%MZ') + 'expiry': (dt.datetime.now(dt.UTC) + dt.timedelta(hours=3)).strftime('%Y-%m-%dT%H:%MZ') }) self.cmd('storage container create --account-name {storage_account} --name {ctn}') @@ -8031,9 +8031,9 @@ def _generate_p2s_root_cert_public_file(cert_path, common_name="VpnGatewayAuthCe from cryptography.hazmat.primitives import hashes, serialization from cryptography.hazmat.primitives.asymmetric import rsa from cryptography.hazmat.backends import default_backend - import datetime + import datetime as dt - now = datetime.datetime.now(datetime.UTC) + now = dt.datetime.now(dt.UTC) # Root key (private key not exported; we only write public cert) root_key = rsa.generate_private_key( @@ -8060,9 +8060,9 @@ def _generate_p2s_root_cert_public_file(cert_path, common_name="VpnGatewayAuthCe ).serial_number( x509.random_serial_number() ).not_valid_before( - now - datetime.timedelta(minutes=5) + now - dt.timedelta(minutes=5) ).not_valid_after( - now + datetime.timedelta(days=3650) + now + dt.timedelta(days=3650) ).add_extension( x509.BasicConstraints(ca=True, path_length=1), critical=True, diff --git a/src/azure-cli/azure/cli/command_modules/postgresql/commands/backup_commands.py b/src/azure-cli/azure/cli/command_modules/postgresql/commands/backup_commands.py index 4aef2082653..d546d43095a 100644 --- a/src/azure-cli/azure/cli/command_modules/postgresql/commands/backup_commands.py +++ b/src/azure-cli/azure/cli/command_modules/postgresql/commands/backup_commands.py @@ -4,7 +4,7 @@ # -------------------------------------------------------------------------------------------- # pylint: disable=unused-argument, line-too-long -from datetime import UTC, datetime +import datetime as dt from azure.cli.core.util import user_confirmation from knack.log import get_logger from ..utils.validators import validate_resource_group, validate_backup_name @@ -20,7 +20,7 @@ def _generate_backup_name(client, resource_group_name, server_name): on_demand_count = sum(1 for name in existing_names if name.startswith(_BACKUP_NAME_PREFIX)) - date_str = datetime.now(UTC).strftime("%m%d%Y") + date_str = dt.datetime.now(dt.UTC).strftime("%m%d%Y") suffix = on_demand_count + 1 backup_name = f"{_BACKUP_NAME_PREFIX}-{date_str}-{suffix}" while backup_name in existing_names: diff --git a/src/azure-cli/azure/cli/command_modules/postgresql/commands/server_logs_commands.py b/src/azure-cli/azure/cli/command_modules/postgresql/commands/server_logs_commands.py index d90b96817a5..c0a1150494e 100644 --- a/src/azure-cli/azure/cli/command_modules/postgresql/commands/server_logs_commands.py +++ b/src/azure-cli/azure/cli/command_modules/postgresql/commands/server_logs_commands.py @@ -7,7 +7,7 @@ import re from azure.cli.core.util import CLIError, user_confirmation -from datetime import UTC, datetime, timedelta +import datetime as dt from knack.log import get_logger from urllib.request import urlretrieve from .._client_factory import cf_postgres_flexible_replica @@ -61,7 +61,7 @@ def flexible_server_list_log_files_with_filter(client, resource_group_name, serv if file_last_written is None: file_last_written = 72 - time_line = datetime.now(UTC) - timedelta(hours=file_last_written) + time_line = dt.datetime.now(dt.UTC) - dt.timedelta(hours=file_last_written) for f in all_files: if f.last_modified_time < time_line: @@ -85,7 +85,7 @@ def flexible_server_log_list(client, resource_group_name, server_name, filename_ if file_last_written is None: file_last_written = 72 - time_line = datetime.now(UTC) - timedelta(hours=file_last_written) + time_line = dt.datetime.now(dt.UTC) - dt.timedelta(hours=file_last_written) for f in all_files: if f.last_modified_time < time_line: diff --git a/src/azure-cli/azure/cli/command_modules/postgresql/utils/_flexible_server_util.py b/src/azure-cli/azure/cli/command_modules/postgresql/utils/_flexible_server_util.py index 198d655fbb3..b689d848c9b 100644 --- a/src/azure-cli/azure/cli/command_modules/postgresql/utils/_flexible_server_util.py +++ b/src/azure-cli/azure/cli/command_modules/postgresql/utils/_flexible_server_util.py @@ -4,7 +4,7 @@ # -------------------------------------------------------------------------------------------- # pylint: disable=unused-argument, line-too-long, import-outside-toplevel, raise-missing-from -from datetime import UTC, datetime +import datetime as dt import os import random import subprocess @@ -200,7 +200,7 @@ def _map_maintenance_window(day_of_week): def get_current_time(): - return datetime.now(UTC).replace(microsecond=0).isoformat() + return dt.datetime.now(dt.UTC).replace(microsecond=0).isoformat() def get_id_components(rid): diff --git a/src/azure-cli/azure/cli/command_modules/rdbms/_flexible_server_util.py b/src/azure-cli/azure/cli/command_modules/rdbms/_flexible_server_util.py index 6bc966762b5..737ae9e67d0 100644 --- a/src/azure-cli/azure/cli/command_modules/rdbms/_flexible_server_util.py +++ b/src/azure-cli/azure/cli/command_modules/rdbms/_flexible_server_util.py @@ -4,7 +4,7 @@ # -------------------------------------------------------------------------------------------- # pylint: disable=unused-argument, line-too-long, import-outside-toplevel, raise-missing-from -from datetime import UTC, datetime +import datetime as dt import os import random import subprocess @@ -245,7 +245,7 @@ def _map_maintenance_window(day_of_week): def get_current_time(): - return datetime.now(UTC).replace(microsecond=0).isoformat() + return dt.datetime.now(dt.UTC).replace(microsecond=0).isoformat() def get_id_components(rid): diff --git a/src/azure-cli/azure/cli/command_modules/rdbms/custom.py b/src/azure-cli/azure/cli/command_modules/rdbms/custom.py index 3c787b8b91b..1b1e660e435 100644 --- a/src/azure-cli/azure/cli/command_modules/rdbms/custom.py +++ b/src/azure-cli/azure/cli/command_modules/rdbms/custom.py @@ -4,7 +4,7 @@ # -------------------------------------------------------------------------------------------- # pylint: disable=unused-argument, line-too-long -from datetime import UTC, datetime, timedelta +import datetime as dt from importlib import import_module import re from knack.log import get_logger @@ -602,7 +602,7 @@ def _list_log_files_with_filter(client, resource_group_name, server_name, filena if file_last_written is None: file_last_written = 72 - time_line = datetime.now(UTC) - timedelta(hours=file_last_written) + time_line = dt.datetime.now(dt.UTC) - dt.timedelta(hours=file_last_written) for f in all_files: if f.last_modified_time < time_line: diff --git a/src/azure-cli/azure/cli/command_modules/rdbms/flexible_server_custom_common.py b/src/azure-cli/azure/cli/command_modules/rdbms/flexible_server_custom_common.py index 6aa639b21f4..519146b4e70 100644 --- a/src/azure-cli/azure/cli/command_modules/rdbms/flexible_server_custom_common.py +++ b/src/azure-cli/azure/cli/command_modules/rdbms/flexible_server_custom_common.py @@ -6,7 +6,7 @@ # pylint: disable=unused-argument, line-too-long import re -from datetime import UTC, datetime, timedelta +import datetime as dt from knack.log import get_logger from knack.util import CLIError from urllib.request import urlretrieve @@ -87,7 +87,7 @@ def firewall_rule_create_func(cmd, client, resource_group_name, server_name, fir raise CLIError("Incorrect Usage : Need to pass in value for either \'--start-ip-address\' or \'--end-ip-address\'.") if firewall_rule_name is None: - now = datetime.now() + now = dt.datetime.now() firewall_rule_name = 'FirewallIPAddress_{}-{}-{}_{}-{}-{}'.format(now.year, now.month, now.day, now.hour, now.minute, now.second) if start_ip_address == '0.0.0.0' and end_ip_address == '0.0.0.0': @@ -227,7 +227,7 @@ def flexible_server_log_list(client, resource_group_name, server_name, filename_ if file_last_written is None: file_last_written = 72 - time_line = datetime.now(UTC) - timedelta(hours=file_last_written) + time_line = dt.datetime.now(dt.UTC) - dt.timedelta(hours=file_last_written) for f in all_files: if f.last_modified_time < time_line: diff --git a/src/azure-cli/azure/cli/command_modules/rdbms/tests/latest/test_rdbms_commands.py b/src/azure-cli/azure/cli/command_modules/rdbms/tests/latest/test_rdbms_commands.py index a9883870a8d..91839ab2841 100644 --- a/src/azure-cli/azure/cli/command_modules/rdbms/tests/latest/test_rdbms_commands.py +++ b/src/azure-cli/azure/cli/command_modules/rdbms/tests/latest/test_rdbms_commands.py @@ -4,7 +4,7 @@ # -------------------------------------------------------------------------------------------- import time -from datetime import UTC, datetime +import datetime as dt from azure.cli.testsdk.scenario_tests import AllowLargeResponse from azure.core.exceptions import HttpResponseError from azure.cli.core.util import CLIError @@ -222,12 +222,12 @@ def _test_server_mgmt(self, database_engine, resource_group_1, resource_group_2) JMESPathCheck('administratorLogin', admin_login)]) # Before we do restore we will have to check whether the current time is less than earliest restore time - current_time = datetime.now(UTC).isoformat() + current_time = dt.datetime.now(dt.UTC).isoformat() earliest_restore_time = result['earliestRestoreDate'] date_format = '%Y-%m-%dT%H:%M:%S.%f+00:00' if current_time < earliest_restore_time: - time.sleep((datetime.strptime(earliest_restore_time, date_format) - datetime.strptime(current_time, + time.sleep((dt.datetime.strptime(earliest_restore_time, date_format) - dt.datetime.strptime(current_time, date_format)).total_seconds()) self.cmd('{} server restore -g {} --name {} ' diff --git a/src/azure-cli/azure/cli/command_modules/redis/tests/latest/test_redis_scenario.py b/src/azure-cli/azure/cli/command_modules/redis/tests/latest/test_redis_scenario.py index 9e174d43195..48ee98a823f 100644 --- a/src/azure-cli/azure/cli/command_modules/redis/tests/latest/test_redis_scenario.py +++ b/src/azure-cli/azure/cli/command_modules/redis/tests/latest/test_redis_scenario.py @@ -6,7 +6,7 @@ import os from azure.cli.testsdk import ScenarioTest, ResourceGroupPreparer import time -import datetime +import datetime as dt location = 'WestEurope' seclocation = 'EastUS' @@ -309,8 +309,8 @@ def test_redis_cache_export_import(self, resource_group): 'containerName': "testcontainer", 'userIdentity': user_identity, 'storageSubscriptionId': "6364f508-1150-4431-b973-c3f133466e56", # replace it with your subscription id while running tests in live mode - 'startTime': (datetime.datetime.now(datetime.UTC) - datetime.timedelta(minutes=60)).strftime("%Y-%m-%dT%H:%MZ"), - 'expiryTime': (datetime.datetime.now(datetime.UTC) + datetime.timedelta(minutes=200)).strftime("%Y-%m-%dT%H:%MZ") + 'startTime': (dt.datetime.now(dt.UTC) - dt.timedelta(minutes=60)).strftime("%Y-%m-%dT%H:%MZ"), + 'expiryTime': (dt.datetime.now(dt.UTC) + dt.timedelta(minutes=200)).strftime("%Y-%m-%dT%H:%MZ") } if self.is_live : diff --git a/src/azure-cli/azure/cli/command_modules/resource/tests/latest/test_resource.py b/src/azure-cli/azure/cli/command_modules/resource/tests/latest/test_resource.py index 2c947da715b..cac8ade0838 100644 --- a/src/azure-cli/azure/cli/command_modules/resource/tests/latest/test_resource.py +++ b/src/azure-cli/azure/cli/command_modules/resource/tests/latest/test_resource.py @@ -1331,8 +1331,8 @@ def test_resource_group_level_deployment_with_query_string(self, resource_group, self.cmd('storage blob upload -c {container_name} -f "{tf}" -n mainTemplate --account-name {storage_account} --account-key {storage_key}') self.cmd('storage blob upload -c {container_name} -f "{linked_tf}" -n storage_account_linked_template.json --account-name {storage_account} --account-key {storage_key}') - from datetime import UTC, datetime, timedelta - self.kwargs['expiry'] = (datetime.now(UTC) + timedelta(hours=12)).strftime('%Y-%m-%dT%H:%MZ') + import datetime as dt + self.kwargs['expiry'] = (dt.datetime.now(dt.UTC) + dt.timedelta(hours=12)).strftime('%Y-%m-%dT%H:%MZ') self.kwargs['sas_token'] = self.cmd( 'storage container generate-sas --account-name {storage_account} --account-key {storage_key} --name {container_name} --permissions rw --expiry {expiry} -otsv').output.strip() @@ -1379,8 +1379,8 @@ def test_subscription_level_deployment_with_query_string(self, resource_group, r self.cmd('storage blob upload -c {container_name} -f "{linked_tf1}" -n createKeyVault.json --account-name {storage_account} --account-key {storage_key}') self.cmd('storage blob upload -c {container_name} -f "{linked_tf2}" -n createKeyVaultWithSecret.json --account-name {storage_account} --account-key {storage_key}') - from datetime import UTC, datetime, timedelta - self.kwargs['expiry'] = (datetime.now(UTC) + timedelta(hours=12)).strftime('%Y-%m-%dT%H:%MZ') + import datetime as dt + self.kwargs['expiry'] = (dt.datetime.now(dt.UTC) + dt.timedelta(hours=12)).strftime('%Y-%m-%dT%H:%MZ') self.kwargs['sas_token'] = self.cmd( 'storage container generate-sas --account-name {storage_account} --name {container_name} --permissions dlrw --expiry {expiry} --https-only -otsv').output.strip() diff --git a/src/azure-cli/azure/cli/command_modules/role/custom.py b/src/azure-cli/azure/cli/command_modules/role/custom.py index 687bdd7c80d..fb92d0cc7f8 100644 --- a/src/azure-cli/azure/cli/command_modules/role/custom.py +++ b/src/azure-cli/azure/cli/command_modules/role/custom.py @@ -12,7 +12,7 @@ """ import base64 -import datetime +import datetime as dt import itertools import json import os @@ -52,7 +52,7 @@ def _coerce(value): return value.value if isinstance(value, list): return [_coerce(v) for v in value] - if isinstance(value, datetime.datetime): + if isinstance(value, dt.datetime): return value.isoformat() return value @@ -489,21 +489,21 @@ def _get_assignment_events(cli_ctx, start_time=None, end_time=None): DATE_TIME_FORMAT = '%Y-%m-%dT%H:%M:%SZ' if end_time: try: - end_time = datetime.datetime.strptime(end_time, DATE_TIME_FORMAT).replace(tzinfo=datetime.UTC) + end_time = dt.datetime.strptime(end_time, DATE_TIME_FORMAT).replace(tzinfo=dt.UTC) except ValueError: raise CLIError("Input '{}' is not valid datetime. Valid example: 2000-12-31T12:59:59Z".format(end_time)) else: - end_time = datetime.datetime.now(datetime.UTC) + end_time = dt.datetime.now(dt.UTC) if start_time: try: - start_time = datetime.datetime.strptime(start_time, DATE_TIME_FORMAT).replace(tzinfo=datetime.UTC) + start_time = dt.datetime.strptime(start_time, DATE_TIME_FORMAT).replace(tzinfo=dt.UTC) if start_time >= end_time: raise CLIError("Start time cannot be later than end time.") except ValueError: raise CLIError("Input '{}' is not valid datetime. Valid example: 2000-12-31T12:59:59Z".format(start_time)) else: - start_time = end_time - datetime.timedelta(hours=1) + start_time = end_time - dt.timedelta(hours=1) time_filter = 'eventTimestamp ge {} and eventTimestamp le {}'.format(_datetime_to_utc(start_time), _datetime_to_utc(end_time)) @@ -1493,14 +1493,14 @@ def create_service_principal_for_rbac( if not display_name: # No display name is provided, create a new one - app_display_name = 'azure-cli-' + datetime.datetime.now(datetime.UTC).strftime('%Y-%m-%d-%H-%M-%S') + app_display_name = 'azure-cli-' + dt.datetime.now(dt.UTC).strftime('%Y-%m-%d-%H-%M-%S') else: app_display_name = display_name # patch existing app with the same displayName to make the command idempotent query_exp = "displayName eq '{}'".format(display_name) existing_sps = list(graph_client.service_principal_list(filter=query_exp)) - app_start_date = datetime.datetime.now(datetime.timezone.utc) + app_start_date = dt.datetime.now(dt.timezone.utc) app_end_date = app_start_date + relativedelta(years=years or 1) use_cert = False @@ -1680,12 +1680,12 @@ def _validate_app_dates(app_start_date, app_end_date, cert_start_date, cert_end_ if cert_start_date > app_start_date: logger.warning('Certificate is not valid until %s. Adjusting key credential start date to match.', cert_start_date) - app_start_date = cert_start_date + datetime.timedelta(seconds=1) + app_start_date = cert_start_date + dt.timedelta(seconds=1) if cert_end_date < app_end_date: logger.warning('Certificate expires %s. Adjusting key credential end date to match.', cert_end_date) - app_end_date = cert_end_date - datetime.timedelta(seconds=1) + app_end_date = cert_end_date - dt.timedelta(seconds=1) return app_start_date, app_end_date, cert_start_date, cert_end_date @@ -1700,7 +1700,6 @@ def _create_self_signed_cert(start_date, end_date): # pylint: disable=too-many- from os import path import tempfile from OpenSSL import crypto - from datetime import timedelta # Create a PEM file ~/tmpxxxxxxxx.pem with both PRIVATE KEY & CERTIFICATE so users can use to log in. # The PEM file looks like @@ -1729,8 +1728,8 @@ def _create_self_signed_cert(start_date, end_date): # pylint: disable=too-many- subject.CN = 'CLI-Login' cert.set_serial_number(1000) asn1_format = '%Y%m%d%H%M%SZ' - cert_start_date = start_date - timedelta(seconds=1) - cert_end_date = end_date + timedelta(seconds=1) + cert_start_date = start_date - dt.timedelta(seconds=1) + cert_end_date = end_date + dt.timedelta(seconds=1) cert.set_notBefore(cert_start_date.strftime(asn1_format).encode('utf-8')) cert.set_notAfter(cert_end_date.strftime(asn1_format).encode('utf-8')) cert.set_issuer(cert.get_subject()) @@ -1928,8 +1927,8 @@ def _set_application_properties(body, **kwargs): set_object_properties('application', body, **kwargs) -def _datetime_to_utc(dt): - return dt.strftime('%Y-%m-%dT%H:%M:%SZ') +def _datetime_to_utc(value): + return value.strftime('%Y-%m-%dT%H:%M:%SZ') def _build_required_resource_accesses(required_resource_accesses): @@ -1980,7 +1979,7 @@ def _build_key_credentials(key_value=None, key_type=None, key_usage=None, return [] if not start_date: - start_date = datetime.datetime.now(datetime.UTC) + start_date = dt.datetime.now(dt.UTC) elif isinstance(start_date, str): start_date = dateutil.parser.parse(start_date) @@ -2026,7 +2025,7 @@ def _reset_credential(cmd, graph_object, add_password_func, remove_password_func # For keyCredential, customKeyIdentifier is automatically computed by Graph service as certificate thumbprint. # https://github.com/Azure/azure-cli/issues/20561 - app_start_date = datetime.datetime.now(datetime.timezone.utc) + app_start_date = dt.datetime.now(dt.timezone.utc) if years is not None and end_date is not None: raise CLIError('usage error: --years | --end-date') if end_date is None: @@ -2035,7 +2034,7 @@ def _reset_credential(cmd, graph_object, add_password_func, remove_password_func else: app_end_date = dateutil.parser.parse(end_date) if app_end_date.tzinfo is None: - app_end_date = app_end_date.replace(tzinfo=datetime.timezone.utc) + app_end_date = app_end_date.replace(tzinfo=dt.timezone.utc) years = (app_end_date - app_start_date).days / 365 # Created password diff --git a/src/azure-cli/azure/cli/command_modules/role/tests/latest/test_role.py b/src/azure-cli/azure/cli/command_modules/role/tests/latest/test_role.py index 79f576dbf43..075db671c57 100644 --- a/src/azure-cli/azure/cli/command_modules/role/tests/latest/test_role.py +++ b/src/azure-cli/azure/cli/command_modules/role/tests/latest/test_role.py @@ -9,7 +9,7 @@ import sys import tempfile import time -import datetime +import datetime as dt from unittest import mock import unittest @@ -698,9 +698,9 @@ def test_role_assignment_audits(self, resource_group): self.cmd('role assignment create --assignee {upn} --role reader --scope {rg_id}') if self.is_live or self.in_recording: - now = datetime.datetime.now(datetime.UTC) - start = now - datetime.timedelta(minutes=1) - end = now + datetime.timedelta(minutes=1) + now = dt.datetime.now(dt.UTC) + start = now - dt.timedelta(minutes=1) + end = now + dt.timedelta(minutes=1) start_time = '{}-{}-{}T{}:{}:{}Z'.format(start.year, start.month, start.day, start.hour, start.minute, start.second) end_time = '{}-{}-{}T{}:{}:{}Z'.format(end.year, end.month, end.day, end.hour, diff --git a/src/azure-cli/azure/cli/command_modules/sql/tests/latest/test_sql_commands.py b/src/azure-cli/azure/cli/command_modules/sql/tests/latest/test_sql_commands.py index 8ea906b9467..40a7cb4137e 100644 --- a/src/azure-cli/azure/cli/command_modules/sql/tests/latest/test_sql_commands.py +++ b/src/azure-cli/azure/cli/command_modules/sql/tests/latest/test_sql_commands.py @@ -33,7 +33,7 @@ ClientType, ComputeModelType, ResourceIdType) -from datetime import UTC, datetime, timedelta +import datetime as dt # Constants @@ -2018,15 +2018,15 @@ def test_sql_db_copy_with_perdb_cmk(self, resource_group_1, resource_group_2, def _get_earliest_restore_date(db): - return datetime.strptime(db['earliestRestoreDate'], "%Y-%m-%dT%H:%M:%S+00:00") + return dt.datetime.strptime(db['earliestRestoreDate'], "%Y-%m-%dT%H:%M:%S+00:00") def _get_earliest_restore_date_for_deleted_db(deleted_db): - return datetime.strptime(deleted_db['earliestRestoreDate'], "%Y-%m-%dT%H:%M:%S+00:00") + return dt.datetime.strptime(deleted_db['earliestRestoreDate'], "%Y-%m-%dT%H:%M:%S+00:00") def _get_deleted_date(deleted_db): - return datetime.strptime(deleted_db['deletionDate'], "%Y-%m-%dT%H:%M:%S.%f+00:00") + return dt.datetime.strptime(deleted_db['deletionDate'], "%Y-%m-%dT%H:%M:%S.%f+00:00") def _create_db_wait_for_first_backup(test, resource_group, server, database_name): @@ -2045,12 +2045,12 @@ def _create_db_wait_for_first_backup(test, resource_group, server, database_name db = test.cmd('sql db show -g {} -s {} -n {}' .format(resource_group, server, database_name)).get_output_in_json() - earliest_restore_date = _get_earliest_restore_date(db).replace(tzinfo=UTC) + earliest_restore_date = _get_earliest_restore_date(db).replace(tzinfo=dt.UTC) - if datetime.now(UTC) <= earliest_restore_date: + if dt.datetime.now(dt.UTC) <= earliest_restore_date: print('Waiting until earliest restore date', earliest_restore_date) - while datetime.now(UTC) <= earliest_restore_date: + while dt.datetime.now(dt.UTC) <= earliest_restore_date: time.sleep(10) return db @@ -2155,8 +2155,8 @@ def test_sql_db_restore_deleted(self, resource_group, resource_group_location, s # Wait for deleted database to become visible. When run live, this will take around # 5-10 minutes. Unforunately there's no way to speed this up. Use timeout to ensure # test doesn't loop forever if there's a bug. - start_time = datetime.now() - timeout = timedelta(0, 15 * 60) # 15 minutes timeout + start_time = dt.datetime.now() + timeout = dt.timedelta(0, 15 * 60) # 15 minutes timeout while True: deleted_dbs = list( @@ -2168,7 +2168,7 @@ def test_sql_db_restore_deleted(self, resource_group, resource_group_location, s # Deleted db not found, sleep (if running live) and then poll again. if self.is_live: - self.assertTrue(datetime.now() < start_time + timeout, 'Deleted db not found before timeout expired.') + self.assertTrue(dt.datetime.now() < start_time + timeout, 'Deleted db not found before timeout expired.') time.sleep(10) # seconds deleted_db = deleted_dbs[0] @@ -5378,7 +5378,7 @@ def test_sql_zone_resilient_restore_hyperscale_database(self, resource_group, se # Verify restored vldb has correct values (specifically zone redundancy == true and backup storage redundancy == Zone) self.cmd('sql db restore -g {} --server {} --name {} --dest-name {} --time {} ' '--edition {} --family {} --capacity {} --backup-storage-redundancy {} --z' - .format(resource_group, server, source_non_zr_db_name, restore_source_non_zr_true_param_db_name, datetime.now(UTC).isoformat(), + .format(resource_group, server, source_non_zr_db_name, restore_source_non_zr_true_param_db_name, dt.datetime.now(dt.UTC).isoformat(), "Hyperscale", 'Gen5', 2, 'zone'), checks=[ JMESPathCheck('resourceGroup', resource_group), @@ -5389,7 +5389,7 @@ def test_sql_zone_resilient_restore_hyperscale_database(self, resource_group, se # Restore zone redundant source vldb with zone redundancy == false # Verify restored vldb has correct values (specifically zone redundancy == false and backup storage redundancy == Zone) self.cmd('sql db restore -g {} --server {} --name {} --dest-name {} --time {} --z {}' - .format(resource_group, server, source_zr_db_name, restore_source_zr_false_param_db_name, datetime.now(UTC).isoformat(), False), + .format(resource_group, server, source_zr_db_name, restore_source_zr_false_param_db_name, dt.datetime.now(dt.UTC).isoformat(), False), checks=[ JMESPathCheck('resourceGroup', resource_group), JMESPathCheck('name', restore_source_zr_false_param_db_name), @@ -5400,7 +5400,7 @@ def test_sql_zone_resilient_restore_hyperscale_database(self, resource_group, se # Restore non zone redundant source vldb with no parameters passed in # Verify restored vldb has correct values (specifically zone redundancy == false and backup storage redundancy == Geo) self.cmd('sql db restore -g {} --server {} --name {} --dest-name {} --time {}' - .format(resource_group, server, source_non_zr_db_name, restore_source_non_zr_no_param_db_name, datetime.now(UTC).isoformat()), + .format(resource_group, server, source_non_zr_db_name, restore_source_non_zr_no_param_db_name, dt.datetime.now(dt.UTC).isoformat()), checks=[ JMESPathCheck('resourceGroup', resource_group), JMESPathCheck('name', restore_source_non_zr_no_param_db_name), @@ -5410,7 +5410,7 @@ def test_sql_zone_resilient_restore_hyperscale_database(self, resource_group, se # Restore zone redundant source vldb with no parameters passed in # Verify restored vldb has correct values (specifically zone redundancy == true and backup storage redundancy == Zone) self.cmd('sql db restore -g {} --server {} --name {} --dest-name {} --time {}' - .format(resource_group, server, source_zr_db_name, restore_source_zr_no_param_db_name, datetime.now(UTC).isoformat()), + .format(resource_group, server, source_zr_db_name, restore_source_zr_no_param_db_name, dt.datetime.now(dt.UTC).isoformat()), checks=[ JMESPathCheck('resourceGroup', resource_group), JMESPathCheck('name', restore_source_zr_no_param_db_name), @@ -6582,7 +6582,7 @@ def test_sql_managed_db_mgmt(self, mi, rg): # test sql db restore command # db1 = self.cmd('sql midb restore -g {} --mi {} -n {} --dest-name {} --time {}' # .format(resource_group_1, managed_instance_name_1, database_name, database_name_restored, - # datetime.now(UTC).isoformat()), + # dt.datetime.now(dt.UTC).isoformat()), # checks=[ # JMESPathCheck('resourceGroup', resource_group_1), # JMESPathCheck('name', database_name_restored), @@ -7870,8 +7870,7 @@ def test_sql_midb_logreplay_mgmt(self, mi, rg): 'last_backup_name': 'full.bak' }) - from datetime import UTC, datetime, timedelta - self.kwargs['expiry'] = (datetime.now(UTC) + timedelta(hours=12)).strftime('%Y-%m-%dT%H:%MZ') + self.kwargs['expiry'] = (dt.datetime.now(dt.UTC) + dt.timedelta(hours=12)).strftime('%Y-%m-%dT%H:%MZ') self.kwargs['storage_key'] = str(self.cmd( 'az storage account keys list -n {storage_account} -g {resource_group} --query "[0].value"').output) @@ -8904,7 +8903,6 @@ class SqlManagedInstanceRestoreCrossSubscriptionScenarioTest(ScenarioTest): @live_only() @ManagedInstancePreparer() def test_sql_managed_deleted_cross_subscription_restore(self, mi, rg): - from datetime import datetime, timezone, timedelta resource_prefix = 'MIRestoreCrossSubDB' self.kwargs.update({ @@ -8915,7 +8913,7 @@ def test_sql_managed_deleted_cross_subscription_restore(self, mi, rg): 'source_rg': "gen4-testing-RG", 'source_mi': "filiptanic-gen4-on-gen7-different-subnet", 'source_db': "cross-sub-restored", - 'pit': (datetime.now(timezone.utc) + timedelta(minutes=-15)).isoformat() + 'pit': (dt.datetime.now(dt.timezone.utc) + dt.timedelta(minutes=-15)).isoformat() }) # test restore deleted database diff --git a/src/azure-cli/azure/cli/command_modules/storage/_validators.py b/src/azure-cli/azure/cli/command_modules/storage/_validators.py index 2ba271842b9..a0cb5b65ae9 100644 --- a/src/azure-cli/azure/cli/command_modules/storage/_validators.py +++ b/src/azure-cli/azure/cli/command_modules/storage/_validators.py @@ -506,9 +506,9 @@ def validate_source_url(cmd, namespace): # pylint: disable=too-many-statements, if valid_blob_source: client = cf_blob_service(cmd.cli_ctx, client_kwargs) - from datetime import UTC, datetime, timedelta - start = datetime.now(UTC) - expiry = datetime.now(UTC) + timedelta(days=1) + import datetime as dt + start = dt.datetime.now(dt.UTC) + expiry = dt.datetime.now(dt.UTC) + dt.timedelta(days=1) source_user_delegation_key = client.get_user_delegation_key(start, expiry) # Both source account name and either key or sas (or both) are now available @@ -1278,7 +1278,7 @@ def validate_subnet(cmd, namespace): def get_datetime_type(to_string): """ Validates UTC datetime. Examples of accepted forms: 2017-12-31T01:11:59Z,2017-12-31T01:11Z or 2017-12-31T01Z or 2017-12-31 """ - from datetime import datetime + import datetime as dt def datetime_type(string): """ Validates UTC datetime. Examples of accepted forms: @@ -1288,9 +1288,9 @@ def datetime_type(string): for form in accepted_date_formats: try: if to_string: - return datetime.strptime(string, form).strftime(form) + return dt.datetime.strptime(string, form).strftime(form) - return datetime.strptime(string, form) + return dt.datetime.strptime(string, form) except ValueError: continue raise ValueError("Input '{}' not valid. Valid example: 2000-12-31T12:59:59Z".format(string)) @@ -1300,13 +1300,13 @@ def datetime_type(string): def get_api_version_type(): """ Examples of accepted forms: 2017-12-31 """ - from datetime import datetime + import datetime as dt def api_version_type(string): """ Validates api version format. Examples of accepted form: 2017-12-31 """ accepted_format = '%Y-%m-%d' try: - return datetime.strptime(string, accepted_format).strftime(accepted_format) + return dt.datetime.strptime(string, accepted_format).strftime(accepted_format) except ValueError: from azure.cli.core.azclierror import InvalidArgumentValueError raise InvalidArgumentValueError("Input '{}' not valid. Valid example: 2008-10-27.".format(string)) @@ -1559,8 +1559,8 @@ def as_user_validator(namespace): expiry = get_datetime_type(False)(namespace.expiry) - from datetime import UTC, datetime, timedelta - if expiry.replace(tzinfo=UTC) > datetime.now(UTC) + timedelta(days=7): + import datetime as dt + if expiry.replace(tzinfo=dt.UTC) > dt.datetime.now(dt.UTC) + dt.timedelta(days=7): raise argparse.ArgumentError( None, 'incorrect usage: --expiry should be within 7 days from now') diff --git a/src/azure-cli/azure/cli/command_modules/storage/operations/blob.py b/src/azure-cli/azure/cli/command_modules/storage/operations/blob.py index 7973adb1e4a..ef7eacd711a 100644 --- a/src/azure-cli/azure/cli/command_modules/storage/operations/blob.py +++ b/src/azure-cli/azure/cli/command_modules/storage/operations/blob.py @@ -5,7 +5,7 @@ import os import sys -from datetime import UTC, datetime, timedelta +import datetime as dt from azure.cli.core.profiles import ResourceType, get_sdk from azure.cli.core.util import sdk_no_wait @@ -825,11 +825,10 @@ def storage_blob_delete_batch(client, source, source_container_name, pattern=Non if_none_match=None, timeout=None, dryrun=False): container_client = client.get_container_client(source_container_name) - from datetime import timezone if if_modified_since and not if_modified_since.tzinfo: - if_modified_since = if_modified_since.replace(tzinfo=timezone.utc) + if_modified_since = if_modified_since.replace(tzinfo=dt.timezone.utc) if if_unmodified_since and not if_unmodified_since.tzinfo: - if_unmodified_since = if_unmodified_since.replace(tzinfo=timezone.utc) + if_unmodified_since = if_unmodified_since.replace(tzinfo=dt.timezone.utc) @check_precondition_success def _delete_blob(blob_name): @@ -888,7 +887,7 @@ def generate_sas_blob_uri(cmd, client, permission=None, expiry=None, start=None, account_key = None if as_user: user_delegation_key = client.get_user_delegation_key( - get_datetime_from_string(start) if start else datetime.now(UTC), get_datetime_from_string(expiry), + get_datetime_from_string(start) if start else dt.datetime.now(dt.UTC), get_datetime_from_string(expiry), delegated_user_tid=user_delegation_tid) else: account_key = client.credential.account_key @@ -938,7 +937,7 @@ def generate_container_shared_access_signature(cmd, client, container_name, perm account_key = None if as_user: user_delegation_key = client.get_user_delegation_key( - get_datetime_from_string(start) if start else datetime.now(UTC), get_datetime_from_string(expiry), + get_datetime_from_string(start) if start else dt.datetime.now(dt.UTC), get_datetime_from_string(expiry), delegated_user_tid=user_delegation_tid) else: account_key = client.credential.account_key @@ -974,8 +973,8 @@ def _copy_blob_to_blob_container(cmd, blob_service, source_blob_service, destina # generate sas for oauth copy source if not source_sas: from ..util import create_short_lived_blob_sas_v2 - start = datetime.now(UTC) - expiry = datetime.now(UTC) + timedelta(days=1) + start = dt.datetime.now(dt.UTC) + expiry = dt.datetime.now(dt.UTC) + dt.timedelta(days=1) source_user_delegation_key = source_blob_service.get_user_delegation_key(start, expiry) source_sas = create_short_lived_blob_sas_v2(cmd, source_blob_service.account_name, source_container, source_blob_name, @@ -1010,8 +1009,8 @@ def _copy_file_to_blob_container(cmd, blob_service, source_file_service, destina '_shared_access_signature#generate_share_sas') t_file_permissions = get_sdk(cmd.cli_ctx, ResourceType.DATA_STORAGE_FILESHARE, '_models#FileSasPermissions') - start = datetime.now(UTC) - expiry = datetime.now(UTC) + timedelta(days=1) + start = dt.datetime.now(dt.UTC) + expiry = dt.datetime.now(dt.UTC) + dt.timedelta(days=1) source_sas = t_generate_share_sas(account_name=t_file_client.account_name, share_name=source_share, account_key=t_file_client.credential.account_key, permission=t_file_permissions(read=True), @@ -1164,7 +1163,7 @@ def copy_blob(cmd, client, source_url, metadata=None, **kwargs): as_user = True if hasattr(blob_service_client.credential, 'account_key'): as_user = False - expiry = (datetime.now(UTC) + timedelta(hours=1)).strftime('%Y-%m-%dT%H:%MZ') + expiry = (dt.datetime.now(dt.UTC) + dt.timedelta(hours=1)).strftime('%Y-%m-%dT%H:%MZ') source_url = generate_sas_blob_uri(cmd, blob_service_client, full_uri=True, blob_url=source_url, blob_name=None, container_name=None, as_user=as_user, expiry=expiry, permission='r') diff --git a/src/azure-cli/azure/cli/command_modules/storage/operations/file.py b/src/azure-cli/azure/cli/command_modules/storage/operations/file.py index ec45b5624ab..cb92b402a69 100644 --- a/src/azure-cli/azure/cli/command_modules/storage/operations/file.py +++ b/src/azure-cli/azure/cli/command_modules/storage/operations/file.py @@ -419,9 +419,9 @@ def _create_file_and_directory_from_blob(cmd, file_service, blob_service, share, '_shared_access_signature#generate_blob_sas') t_blob_permissions = get_sdk(cmd.cli_ctx, ResourceType.DATA_STORAGE_BLOB, '_models#BlobSasPermissions') - from datetime import UTC, datetime, timedelta - start = datetime.now(UTC) - expiry = datetime.now(UTC) + timedelta(days=1) + import datetime as dt + start = dt.datetime.now(dt.UTC) + expiry = dt.datetime.now(dt.UTC) + dt.timedelta(days=1) source_sas = t_generate_blob_sas(account_name=blob_service.account_name, container_name=container, blob_name=blob_name, account_key=blob_service.credential.account_key, @@ -468,9 +468,9 @@ def _create_file_and_directory_from_file(cmd, file_service, source_file_service, '_shared_access_signature#generate_share_sas') t_file_permissions = get_sdk(cmd.cli_ctx, ResourceType.DATA_STORAGE_FILESHARE, '_models#FileSasPermissions') - from datetime import UTC, datetime, timedelta - start = datetime.now(UTC) - expiry = datetime.now(UTC) + timedelta(days=1) + import datetime as dt + start = dt.datetime.now(dt.UTC) + expiry = dt.datetime.now(dt.UTC) + dt.timedelta(days=1) source_sas = t_generate_share_sas(account_name=source_file_service.account_name, share_name=source_share, account_key=source_file_service.credential.account_key, permission=t_file_permissions(read=True), @@ -554,9 +554,9 @@ def generate_sas_file(cmd, client, share_name=None, directory_name=None, file_na account_key = None user_delegation_key = None if as_user: - from datetime import UTC, datetime + import datetime as dt user_delegation_key = client.get_user_delegation_key( - start=get_datetime_from_string(start) if start else datetime.now(UTC), + start=get_datetime_from_string(start) if start else dt.datetime.now(dt.UTC), expiry=get_datetime_from_string(expiry), delegated_user_tid=user_delegation_tid) else: @@ -650,25 +650,25 @@ def pre_operations(self): def _transform_share_rm_output(result): - from datetime import datetime + import datetime as dt try: if hasattr(result, 'properties'): if hasattr(result.properties, 'next_allowed_quota_downgrade_time') \ and result.properties.next_allowed_quota_downgrade_time: time_str = str(result.properties.next_allowed_quota_downgrade_time) - time_obj = datetime.strptime(time_str, '%a, %d %b %Y %H:%M:%S %Z') + time_obj = dt.datetime.strptime(time_str, '%a, %d %b %Y %H:%M:%S %Z') result.properties.next_allowed_quota_downgrade_time = \ time_obj.strftime("%Y-%m-%dT%H:%M:%S.%f0Z") if hasattr(result.properties, 'next_allowed_provisioned_iops_downgrade_time') \ and result.properties.next_allowed_provisioned_iops_downgrade_time: time_str = str(result.properties.next_allowed_provisioned_iops_downgrade_time) - time_obj = datetime.strptime(time_str, '%a, %d %b %Y %H:%M:%S %Z') + time_obj = dt.datetime.strptime(time_str, '%a, %d %b %Y %H:%M:%S %Z') result.properties.next_allowed_provisioned_iops_downgrade_time = \ time_obj.strftime("%Y-%m-%dT%H:%M:%S.%f0Z") if hasattr(result.properties, 'next_allowed_provisioned_bandwidth_downgrade_time') \ and result.properties.next_allowed_provisioned_bandwidth_downgrade_time: time_str = str(result.properties.next_allowed_provisioned_bandwidth_downgrade_time) - time_obj = datetime.strptime(time_str, '%a, %d %b %Y %H:%M:%S %Z') + time_obj = dt.datetime.strptime(time_str, '%a, %d %b %Y %H:%M:%S %Z') result.properties.next_allowed_provisioned_bandwidth_downgrade_time = \ time_obj.strftime("%Y-%m-%dT%H:%M:%S.%f0Z") return result diff --git a/src/azure-cli/azure/cli/command_modules/storage/operations/fileshare.py b/src/azure-cli/azure/cli/command_modules/storage/operations/fileshare.py index 75f535dbbd7..38e31873bc0 100644 --- a/src/azure-cli/azure/cli/command_modules/storage/operations/fileshare.py +++ b/src/azure-cli/azure/cli/command_modules/storage/operations/fileshare.py @@ -3,7 +3,7 @@ # Licensed under the MIT License. See License.txt in the project root for license information. # -------------------------------------------------------------------------------------------- import math -from datetime import UTC, datetime +import datetime as dt from knack.log import get_logger @@ -67,7 +67,7 @@ def generate_share_sas(cmd, client, share_name=None, permission=None, expiry=Non user_delegation_key = None if as_user: user_delegation_key = client.get_user_delegation_key( - start=get_datetime_from_string(start) if start else datetime.now(UTC), + start=get_datetime_from_string(start) if start else dt.datetime.now(dt.UTC), expiry=get_datetime_from_string(expiry), delegated_user_tid=user_delegation_tid) else: diff --git a/src/azure-cli/azure/cli/command_modules/storage/operations/filesystem.py b/src/azure-cli/azure/cli/command_modules/storage/operations/filesystem.py index c2ac3ee5566..3e5646e6254 100644 --- a/src/azure-cli/azure/cli/command_modules/storage/operations/filesystem.py +++ b/src/azure-cli/azure/cli/command_modules/storage/operations/filesystem.py @@ -3,7 +3,7 @@ # Licensed under the MIT License. See License.txt in the project root for license information. # -------------------------------------------------------------------------------------------- -from datetime import UTC, datetime +import datetime as dt from azure.cli.core.profiles import ResourceType from ..util import get_datetime_from_string @@ -45,7 +45,7 @@ def generate_sas_fs_uri(client, cmd, file_system, permission=None, sas_kwargs = {} if as_user: user_delegation_key = client.get_user_delegation_key( - get_datetime_from_string(start) if start else datetime.now(UTC), + get_datetime_from_string(start) if start else dt.datetime.now(dt.UTC), get_datetime_from_string(expiry), delegated_user_tid=user_delegation_tid) diff --git a/src/azure-cli/azure/cli/command_modules/storage/operations/fs_directory.py b/src/azure-cli/azure/cli/command_modules/storage/operations/fs_directory.py index 8c3a39b6290..ada9d4c794f 100644 --- a/src/azure-cli/azure/cli/command_modules/storage/operations/fs_directory.py +++ b/src/azure-cli/azure/cli/command_modules/storage/operations/fs_directory.py @@ -6,7 +6,7 @@ """Custom operations for storage file datalake""" # pylint: disable=too-many-locals -from datetime import UTC, datetime +import datetime as dt from azure.cli.core.profiles import ResourceType from knack.util import todict @@ -106,7 +106,7 @@ def generate_sas_directory_uri(client, cmd, file_system_name, directory_name, pe sas_kwargs = {} if as_user: user_delegation_key = client.get_user_delegation_key( - get_datetime_from_string(start) if start else datetime.now(UTC), + get_datetime_from_string(start) if start else dt.datetime.now(dt.UTC), get_datetime_from_string(expiry), delegated_user_tid=user_delegation_tid) diff --git a/src/azure-cli/azure/cli/command_modules/storage/operations/fs_file.py b/src/azure-cli/azure/cli/command_modules/storage/operations/fs_file.py index 8657e078745..a56fbbaf0ac 100644 --- a/src/azure-cli/azure/cli/command_modules/storage/operations/fs_file.py +++ b/src/azure-cli/azure/cli/command_modules/storage/operations/fs_file.py @@ -7,7 +7,7 @@ # pylint: disable=too-many-locals import os -from datetime import UTC, datetime +import datetime as dt from azure.core.exceptions import HttpResponseError from azure.cli.core.profiles import ResourceType @@ -160,7 +160,7 @@ def generate_sas_file_uri(client, cmd, file_system_name, path, permission=None, sas_kwargs = {} if as_user: user_delegation_key = client.get_user_delegation_key( - get_datetime_from_string(start) if start else datetime.now(UTC), + get_datetime_from_string(start) if start else dt.datetime.now(dt.UTC), get_datetime_from_string(expiry), delegated_user_tid=user_delegation_tid) diff --git a/src/azure-cli/azure/cli/command_modules/storage/operations/queue.py b/src/azure-cli/azure/cli/command_modules/storage/operations/queue.py index 4043b46cbae..d6161872727 100644 --- a/src/azure-cli/azure/cli/command_modules/storage/operations/queue.py +++ b/src/azure-cli/azure/cli/command_modules/storage/operations/queue.py @@ -52,9 +52,9 @@ def generate_queue_sas(cmd, client, queue_name=None, permission=None, expiry=Non account_key = None user_delegation_key = None if as_user: - from datetime import UTC, datetime + import datetime as dt user_delegation_key = client.get_user_delegation_key( - start=get_datetime_from_string(start) if start else datetime.now(UTC), + start=get_datetime_from_string(start) if start else dt.datetime.now(dt.UTC), expiry=get_datetime_from_string(expiry), delegated_user_tid=user_delegation_tid) else: diff --git a/src/azure-cli/azure/cli/command_modules/storage/tests/latest/test_storage_account_scenarios.py b/src/azure-cli/azure/cli/command_modules/storage/tests/latest/test_storage_account_scenarios.py index 1dec5ab13aa..ae0ddec0b21 100644 --- a/src/azure-cli/azure/cli/command_modules/storage/tests/latest/test_storage_account_scenarios.py +++ b/src/azure-cli/azure/cli/command_modules/storage/tests/latest/test_storage_account_scenarios.py @@ -13,7 +13,7 @@ from azure.cli.core.profiles import ResourceType from ..storage_test_util import StorageScenarioMixin from knack.util import CLIError -from datetime import UTC, datetime, timedelta +import datetime as dt from azure.cli.testsdk.scenario_tests import AllowLargeResponse @@ -2004,10 +2004,9 @@ class RevokeStorageAccountTests(StorageScenarioMixin, RoleScenarioTest, LiveScen def test_storage_account_revoke_delegation_keys(self, resource_group, storage_account): if self.run_under_service_principal(): return # this test delete users which are beyond a SP's capacity, so quit... - from datetime import UTC, datetime, timedelta import time - expiry = (datetime.now(UTC) + timedelta(hours=1)).strftime('%Y-%m-%dT%H:%MZ') + expiry = (dt.datetime.now(dt.UTC) + dt.timedelta(hours=1)).strftime('%Y-%m-%dT%H:%MZ') account_info = self.get_account_info(resource_group, storage_account) c = self.create_container(account_info) b = self.create_random_name('blob', 24) diff --git a/src/azure-cli/azure/cli/command_modules/storage/tests/latest/test_storage_adls_gen2_scenarios.py b/src/azure-cli/azure/cli/command_modules/storage/tests/latest/test_storage_adls_gen2_scenarios.py index 197b363f72c..b1ab5cefb37 100644 --- a/src/azure-cli/azure/cli/command_modules/storage/tests/latest/test_storage_adls_gen2_scenarios.py +++ b/src/azure-cli/azure/cli/command_modules/storage/tests/latest/test_storage_adls_gen2_scenarios.py @@ -5,7 +5,7 @@ import unittest import os import time -from datetime import UTC, datetime, timedelta +import datetime as dt from azure.cli.testsdk import (ScenarioTest, LiveScenarioTest, ResourceGroupPreparer, StorageAccountPreparer, JMESPathCheck, JMESPathCheckExists, RoleBasedServicePrincipalPreparer) @@ -503,14 +503,13 @@ def test_adls_file_scenarios(self, resource_group, storage_account): @ResourceGroupPreparer() @StorageAccountPreparer(kind="StorageV2", hns=True) def test_adls_file_set_expiry_scenarios(self, resource_group, storage_account): - from datetime import UTC, datetime, timedelta account_info = self.get_account_info(resource_group, storage_account) filesystem = self.create_file_system(account_info) file = self.create_random_name(prefix="file", length=12) self.storage_cmd('storage fs file create -p {} -f {} --content-type "application/json"', account_info, file, filesystem) - expiry = (datetime.now(UTC) + timedelta(seconds=5)).strftime('%Y-%m-%dT%H:%M:%SZ') + expiry = (dt.datetime.now(dt.UTC) + dt.timedelta(seconds=5)).strftime('%Y-%m-%dT%H:%M:%SZ') self.storage_cmd("storage fs file set-expiry --expiry-options Absolute --expires-on {} -p {} -f {}", account_info, expiry, file, filesystem) self.storage_cmd("storage fs file exists -p {} -f {}", account_info, file, filesystem)\ @@ -565,7 +564,7 @@ def test_storage_fs_generate_sas_full_uri(self, resource_group, storage_account) account_info = self.get_account_info(resource_group, storage_account) f = self.create_file_system(account_info) - expiry = (datetime.now(UTC) + timedelta(hours=1)).strftime('%Y-%m-%dT%H:%MZ') + expiry = (dt.datetime.now(dt.UTC) + dt.timedelta(hours=1)).strftime('%Y-%m-%dT%H:%MZ') fs_uri = self.storage_cmd('storage fs generate-sas -n {} --expiry {} --permissions ' 'r --https-only --full-uri', account_info, f, expiry).output self.assertTrue(fs_uri) @@ -578,7 +577,7 @@ def test_storage_fs_generate_sas_as_user(self, resource_group, storage_account): account_info = self.get_account_info(resource_group, storage_account) f = self.create_file_system(account_info) - expiry = (datetime.now(UTC) + timedelta(hours=1)).strftime('%Y-%m-%dT%H:%MZ') + expiry = (dt.datetime.now(dt.UTC) + dt.timedelta(hours=1)).strftime('%Y-%m-%dT%H:%MZ') with self.assertRaisesRegex(CLIError, "incorrect usage: specify --as-user when --auth-mode login"): self.cmd('storage fs generate-sas --account-name {} -n {} --expiry {} --permissions r --https-only ' @@ -606,7 +605,7 @@ def test_storage_fs_generate_sas_with_user_delegation_oid(self, resource_group, current_tenant = self.cmd('account show --query tenantId').get_output_in_json() current_tenant = current_tenant if current_tenant is not None else '544a7a2e-697f-487c-b2b0-a13df7f346b6' - expiry = (datetime.now(UTC) + timedelta(hours=1)).strftime('%Y-%m-%dT%H:%MZ') + expiry = (dt.datetime.now(dt.UTC) + dt.timedelta(hours=1)).strftime('%Y-%m-%dT%H:%MZ') fs_sas = self.cmd('storage fs generate-sas --account-name {} -n {} --expiry {} --permissions ' 'dlrwop --https-only --as-user --auth-mode login --user-delegation-oid ' @@ -636,7 +635,7 @@ def test_storage_fs_directory_generate_sas_full_uri(self, resource_group, storag self.storage_cmd('storage fs directory create -n {} -f {}', account_info, directory, filesystem) - expiry = (datetime.now(UTC) + timedelta(hours=1)).strftime('%Y-%m-%dT%H:%MZ') + expiry = (dt.datetime.now(dt.UTC) + dt.timedelta(hours=1)).strftime('%Y-%m-%dT%H:%MZ') fs_uri = self.storage_cmd('storage fs directory generate-sas -n {} -f {} --expiry {} --permissions ' 'r --https-only --full-uri', account_info, directory, filesystem, expiry).output self.assertTrue(fs_uri) @@ -653,7 +652,7 @@ def test_storage_fs_directory_generate_sas_as_user(self, resource_group, storage self.storage_cmd('storage fs directory create -n {} -f {}', account_info, directory, filesystem) - expiry = (datetime.now(UTC) + timedelta(hours=1)).strftime('%Y-%m-%dT%H:%MZ') + expiry = (dt.datetime.now(dt.UTC) + dt.timedelta(hours=1)).strftime('%Y-%m-%dT%H:%MZ') with self.assertRaisesRegex(CLIError, "incorrect usage: specify --as-user when --auth-mode login"): self.cmd('storage fs directory generate-sas --account-name {} -n {} -f {} --expiry {} --permissions r ' @@ -685,7 +684,7 @@ def test_storage_fs_directory_generate_sas_with_user_delegation_oid(self, resour self.storage_cmd('storage fs directory create -n {} -f {}', account_info, directory, filesystem) - expiry = (datetime.now(UTC) + timedelta(hours=1)).strftime('%Y-%m-%dT%H:%MZ') + expiry = (dt.datetime.now(dt.UTC) + dt.timedelta(hours=1)).strftime('%Y-%m-%dT%H:%MZ') fs_sas = self.cmd('storage fs directory generate-sas --account-name {} -n {} -f {} --expiry {} --permissions ' 'dlrwop --https-only --as-user --auth-mode login --user-delegation-oid ' @@ -719,7 +718,7 @@ def test_storage_fs_file_generate_sas_as_user(self, resource_group, storage_acco self.storage_cmd('storage fs directory create -n {} -f {}', account_info, directory, filesystem) self.storage_cmd('storage fs file create -p {} -f {}', account_info, file_path, filesystem) - expiry = (datetime.now(UTC) + timedelta(hours=1)).strftime('%Y-%m-%dT%H:%MZ') + expiry = (dt.datetime.now(dt.UTC) + dt.timedelta(hours=1)).strftime('%Y-%m-%dT%H:%MZ') with self.assertRaisesRegex(CLIError, "incorrect usage: specify --as-user when --auth-mode login"): self.cmd('storage fs file generate-sas --account-name {} -p {} -f {} --expiry {} --permissions r ' @@ -755,7 +754,7 @@ def test_storage_fs_file_generate_sas_with_user_delegation_oid(self, resource_gr self.storage_cmd('storage fs file create -p {} -f {}', account_info, file_path, filesystem) - expiry = (datetime.now(UTC) + timedelta(hours=1)).strftime('%Y-%m-%dT%H:%MZ') + expiry = (dt.datetime.now(dt.UTC) + dt.timedelta(hours=1)).strftime('%Y-%m-%dT%H:%MZ') file_sas = self.cmd('storage fs file generate-sas --account-name {} -p {} -f {} --expiry {} --permissions ' 'dlrwop --https-only --as-user --auth-mode login --user-delegation-oid ' @@ -790,8 +789,7 @@ def test_adls_directory_upload(self, resource_group, storage_account, test_dir): self.storage_cmd('storage fs create -n {}', account_info, filesystem) self.storage_cmd('storage fs directory create -n {} -f {}', account_info, directory, filesystem) - from datetime import UTC, datetime, timedelta - expiry = (datetime.now(UTC) + timedelta(hours=1)).strftime('%Y-%m-%dT%H:%MZ') + expiry = (dt.datetime.now(dt.UTC) + dt.timedelta(hours=1)).strftime('%Y-%m-%dT%H:%MZ') sas = self.storage_cmd('storage container generate-sas -n {} --https-only ' '--permissions acdlrw --expiry {} -otsv', account_info, filesystem, expiry).output.strip() @@ -855,8 +853,7 @@ def test_adls_directory_download(self, resource_group, storage_account, test_dir self.storage_cmd('storage fs directory upload -f {} -d {} -s "{}" --recursive', account_info, filesystem, directory, os.path.join(test_dir, 'apple')) - from datetime import UTC, datetime, timedelta - expiry = (datetime.now(UTC) + timedelta(hours=1)).strftime('%Y-%m-%dT%H:%MZ') + expiry = (dt.datetime.now(dt.UTC) + dt.timedelta(hours=1)).strftime('%Y-%m-%dT%H:%MZ') sas = self.storage_cmd('storage container generate-sas -n {} --https-only ' '--permissions lr --expiry {} -otsv', account_info, filesystem, expiry).output.strip() diff --git a/src/azure-cli/azure/cli/command_modules/storage/tests/latest/test_storage_azcopy_scenarios.py b/src/azure-cli/azure/cli/command_modules/storage/tests/latest/test_storage_azcopy_scenarios.py index f1efcec5129..6cf95b0dc86 100644 --- a/src/azure-cli/azure/cli/command_modules/storage/tests/latest/test_storage_azcopy_scenarios.py +++ b/src/azure-cli/azure/cli/command_modules/storage/tests/latest/test_storage_azcopy_scenarios.py @@ -4,7 +4,7 @@ # -------------------------------------------------------------------------------------------- import os import shutil -from datetime import UTC, datetime, timedelta +import datetime as dt from azure.cli.testsdk import (StorageAccountPreparer, LiveScenarioTest, JMESPathCheck, ResourceGroupPreparer) from ..storage_test_util import StorageScenarioMixin, StorageTestFilesPreparer from knack.util import CLIError @@ -192,8 +192,7 @@ def test_storage_blob_azcopy_sync_oauth(self, storage_account, test_dir): @StorageAccountPreparer() @StorageTestFilesPreparer() def test_storage_blob_azcopy_sync_sas(self, resource_group, storage_account, test_dir): - from datetime import UTC, datetime, timedelta - expiry = (datetime.now(UTC) + timedelta(hours=1)).strftime('%Y-%m-%dT%H:%MZ') + expiry = (dt.datetime.now(dt.UTC) + dt.timedelta(hours=1)).strftime('%Y-%m-%dT%H:%MZ') account_info = self.get_account_info(resource_group, storage_account) container = self.create_random_name(prefix='container', length=20) @@ -485,8 +484,7 @@ def test_storage_file_azcopy_remove_oauth(self, resource_group, storage_account) @StorageAccountPreparer() @StorageTestFilesPreparer() def test_storage_azcopy_remove_sas(self, resource_group, storage_account, test_dir): - from datetime import UTC, datetime, timedelta - expiry = (datetime.now(UTC) + timedelta(hours=1)).strftime('%Y-%m-%dT%H:%MZ') + expiry = (dt.datetime.now(dt.UTC) + dt.timedelta(hours=1)).strftime('%Y-%m-%dT%H:%MZ') account_info = self.get_account_info(resource_group, storage_account) container = self.create_container(account_info, prefix='container', length=20) @@ -763,7 +761,7 @@ def test_storage_azcopy_blob_source_with_credential(self, resource_group, first_ self.assertEqual(11, sum(len(f) for r, d, f in os.walk(local_folder))) # Download a set of files with source sas token - expiry = (datetime.now(UTC) + timedelta(hours=1)).strftime('%Y-%m-%dT%H:%MZ') + expiry = (dt.datetime.now(dt.UTC) + dt.timedelta(hours=1)).strftime('%Y-%m-%dT%H:%MZ') first_sas_token = self.cmd( 'storage container generate-sas --connection-string {} -n {} --expiry {} --permissions {} -o tsv'.format( first_connection_string, first_container, expiry, 'rwalcd')).output.strip() @@ -774,7 +772,7 @@ def test_storage_azcopy_blob_source_with_credential(self, resource_group, first_ self.assertEqual(21, sum(len(f) for r, d, f in os.walk(local_folder))) # Copy a single blob to another single blob with sas token - expiry = (datetime.now(UTC) + timedelta(hours=1)).strftime('%Y-%m-%dT%H:%MZ') + expiry = (dt.datetime.now(dt.UTC) + dt.timedelta(hours=1)).strftime('%Y-%m-%dT%H:%MZ') second_sas_token = self.storage_cmd( 'storage container generate-sas -n {} --expiry {} --permissions {} -o tsv', second_account_info, second_container, expiry, 'acdlrw').output.strip() diff --git a/src/azure-cli/azure/cli/command_modules/storage/tests/latest/test_storage_batch_operations.py b/src/azure-cli/azure/cli/command_modules/storage/tests/latest/test_storage_batch_operations.py index acb2b8160d3..670881ae43a 100644 --- a/src/azure-cli/azure/cli/command_modules/storage/tests/latest/test_storage_batch_operations.py +++ b/src/azure-cli/azure/cli/command_modules/storage/tests/latest/test_storage_batch_operations.py @@ -4,7 +4,7 @@ # -------------------------------------------------------------------------------------------- import os -from datetime import UTC, datetime +import datetime as dt from azure.cli.testsdk import ScenarioTest, StorageAccountPreparer, ResourceGroupPreparer, JMESPathCheck, live_only from ..storage_test_util import StorageScenarioMixin, StorageTestFilesPreparer @@ -154,9 +154,8 @@ def test_storage_blob_batch_upload_scenarios(self, test_dir, storage_account_inf self.storage_cmd('storage blob upload-batch -d {} -s "{}"', storage_account_info, container, test_dir) import time - from datetime import datetime time.sleep(1) - current = datetime.now().strftime("%Y-%m-%dT%H:%M:%SZ") + current = dt.datetime.now().strftime("%Y-%m-%dT%H:%M:%SZ") time.sleep(1) result = self.storage_cmd('storage blob upload-batch -d {} -s "{}" --if-modified-since {}', storage_account_info, container, test_dir, current).get_output_in_json() @@ -353,8 +352,7 @@ def test_storage_file_batch_upload_scenarios(self, test_dir, storage_account_inf @StorageAccountPreparer(parameter_name='dst_account') @StorageTestFilesPreparer() def test_storage_blob_batch_copy(self, src_account_info, dst_account_info, test_dir): - from datetime import UTC, datetime, timedelta - expiry = (datetime.now(UTC) + timedelta(hours=1)).strftime('%Y-%m-%dT%H:%MZ') + expiry = (dt.datetime.now(dt.UTC) + dt.timedelta(hours=1)).strftime('%Y-%m-%dT%H:%MZ') src_container = 'container1' self.storage_cmd('storage container create -n {}', src_account_info, src_container) @@ -443,8 +441,7 @@ def test_storage_blob_batch_copy(self, src_account_info, dst_account_info, test_ @StorageAccountPreparer(parameter_name='dst_account') @StorageTestFilesPreparer() def test_storage_file_batch_copy(self, src_account_info, dst_account_info, test_dir): - from datetime import UTC, datetime, timedelta - expiry = (datetime.now(UTC) + timedelta(hours=1)).strftime('%Y-%m-%dT%H:%MZ') + expiry = (dt.datetime.now(dt.UTC) + dt.timedelta(hours=1)).strftime('%Y-%m-%dT%H:%MZ') src_container = 'container1' self.storage_cmd('storage container create -n {}', src_account_info, src_container) @@ -580,9 +577,9 @@ def create_and_populate_container(src_container): src_container = 'container7' create_and_populate_container(src_container) self.storage_cmd('storage blob delete-batch -s {} --if-unmodified-since {} --dryrun', - storage_account_info, src_container, datetime.max.strftime('%Y-%m-%dT%H:%MZ')) + storage_account_info, src_container, dt.datetime.max.strftime('%Y-%m-%dT%H:%MZ')) self.storage_cmd('storage blob delete-batch -s {} --if-unmodified-since {}', - storage_account_info, src_container, datetime.max.strftime('%Y-%m-%dT%H:%MZ')) + storage_account_info, src_container, dt.datetime.max.strftime('%Y-%m-%dT%H:%MZ')) self.storage_cmd('storage blob list -c {}', storage_account_info, src_container).assert_with_checks( JMESPathCheck('length(@)', 0)) @@ -648,12 +645,11 @@ def create_and_populate_share(src_share): @StorageTestFilesPreparer() @live_only() def test_storage_blob_batch_sas_scenarios(self, test_dir, resource_group, storage_account_info): - from datetime import UTC, datetime, timedelta container_name = 'container1' self.storage_cmd('storage container create -n {}', storage_account_info, container_name) temp_dir = self.create_temp_dir() - expiry = (datetime.now(UTC) + timedelta(hours=1)).strftime('%Y-%m-%dT%H:%MZ') + expiry = (dt.datetime.now(dt.UTC) + dt.timedelta(hours=1)).strftime('%Y-%m-%dT%H:%MZ') sas_token = self.storage_cmd('storage container generate-sas -n {} --permissions dwrl --expiry {}', storage_account_info, container_name, expiry).get_output_in_json() diff --git a/src/azure-cli/azure/cli/command_modules/storage/tests/latest/test_storage_blob_copy_scenarios.py b/src/azure-cli/azure/cli/command_modules/storage/tests/latest/test_storage_blob_copy_scenarios.py index 1059b10fd21..4a1335396d4 100644 --- a/src/azure-cli/azure/cli/command_modules/storage/tests/latest/test_storage_blob_copy_scenarios.py +++ b/src/azure-cli/azure/cli/command_modules/storage/tests/latest/test_storage_blob_copy_scenarios.py @@ -214,9 +214,9 @@ def test_storage_blob_copy_with_sas_and_snapshot(self, resource_group, source_ac self.storage_cmd('storage blob upload -c {} -f "{}" -n src --overwrite', source_account_info, source_container, source_file) - from datetime import UTC, datetime, timedelta - start = datetime.now(UTC).strftime('%Y-%m-%dT%H:%MZ') - expiry = (datetime.now(UTC) + timedelta(minutes=5)).strftime('%Y-%m-%dT%H:%MZ') + import datetime as dt + start = dt.datetime.now(dt.UTC).strftime('%Y-%m-%dT%H:%MZ') + expiry = (dt.datetime.now(dt.UTC) + dt.timedelta(minutes=5)).strftime('%Y-%m-%dT%H:%MZ') sas = self.storage_cmd('storage blob generate-sas -c {} -n src --permissions r --start {}' ' --expiry {}', source_account_info, source_container, start, expiry).output.strip() @@ -260,9 +260,9 @@ def test_storage_blob_copy_same_account_sas(self, resource_group, storage_accoun self.storage_cmd('storage blob upload -c {} -f "{}" -n src', account_info, source_container, source_file) - from datetime import UTC, datetime, timedelta - start = datetime.now(UTC).strftime('%Y-%m-%dT%H:%MZ') - expiry = (datetime.now(UTC) + timedelta(hours=1)).strftime('%Y-%m-%dT%H:%MZ') + import datetime as dt + start = dt.datetime.now(dt.UTC).strftime('%Y-%m-%dT%H:%MZ') + expiry = (dt.datetime.now(dt.UTC) + dt.timedelta(hours=1)).strftime('%Y-%m-%dT%H:%MZ') sas = self.storage_cmd('storage blob generate-sas -c {} -n src --permissions r --start {}' ' --expiry {}', account_info, source_container, start, expiry).output.strip() diff --git a/src/azure-cli/azure/cli/command_modules/storage/tests/latest/test_storage_blob_live_scenarios.py b/src/azure-cli/azure/cli/command_modules/storage/tests/latest/test_storage_blob_live_scenarios.py index c567a51d10c..f01166258e1 100644 --- a/src/azure-cli/azure/cli/command_modules/storage/tests/latest/test_storage_blob_live_scenarios.py +++ b/src/azure-cli/azure/cli/command_modules/storage/tests/latest/test_storage_blob_live_scenarios.py @@ -4,7 +4,7 @@ # -------------------------------------------------------------------------------------------- import os -from datetime import UTC, datetime, timedelta +import datetime as dt from azure.cli.testsdk import (LiveScenarioTest, ResourceGroupPreparer, StorageAccountPreparer, JMESPathCheck, JMESPathCheckExists, NoneCheck) from azure.cli.core.profiles import ResourceType @@ -145,7 +145,7 @@ def test_storage_blob_query_scenario(self, resource_group, storage_account): account_info, container, csv_blob, query_string).output self.assertIsNotNone(result) #test blob-url - expiry = (datetime.now(UTC) + timedelta(hours=1)).strftime('%Y-%m-%dT%H:%MZ') + expiry = (dt.datetime.now(dt.UTC) + dt.timedelta(hours=1)).strftime('%Y-%m-%dT%H:%MZ') blob_uri = self.storage_cmd('storage blob generate-sas -n {} -c {} --expiry {} --permissions ' 'rwad --https-only --full-uri -o tsv', account_info, csv_blob, container, expiry).output.strip() @@ -193,7 +193,7 @@ def test_storage_blob_url_scenarios(self, resource_group, storage_account): self.storage_cmd('storage blob upload -c {} -f "{}" -n {} ', account_info, container, local_file1, blob_name1) - expiry = (datetime.now(UTC) + timedelta(hours=1)).strftime('%Y-%m-%dT%H:%MZ') + expiry = (dt.datetime.now(dt.UTC) + dt.timedelta(hours=1)).strftime('%Y-%m-%dT%H:%MZ') blob_uri = self.storage_cmd('storage blob generate-sas -n {} -c {} --expiry {} --permissions ' 'rwad --https-only --full-uri -o tsv', account_info, blob_name1, container, expiry).output.strip() diff --git a/src/azure-cli/azure/cli/command_modules/storage/tests/latest/test_storage_blob_rewrite_scenarios.py b/src/azure-cli/azure/cli/command_modules/storage/tests/latest/test_storage_blob_rewrite_scenarios.py index 7be520d6fc5..188639aa143 100644 --- a/src/azure-cli/azure/cli/command_modules/storage/tests/latest/test_storage_blob_rewrite_scenarios.py +++ b/src/azure-cli/azure/cli/command_modules/storage/tests/latest/test_storage_blob_rewrite_scenarios.py @@ -3,7 +3,7 @@ # Licensed under the MIT License. See License.txt in the project root for license information. # -------------------------------------------------------------------------------------------- -from datetime import UTC, datetime, timedelta +import datetime as dt from azure.cli.testsdk import LiveScenarioTest, ResourceGroupPreparer, StorageAccountPreparer,\ JMESPathCheck from azure.cli.testsdk.decorators import serial_test @@ -77,7 +77,7 @@ def test_storage_blob_rewrite_encryption_scope(self, group, account, file_size_k ]) # Update blob encryption - expiry = (datetime.now(UTC) + timedelta(days=1)).strftime('%Y-%m-%dT%H:%MZ') + expiry = (dt.datetime.now(dt.UTC) + dt.timedelta(days=1)).strftime('%Y-%m-%dT%H:%MZ') source_url = self.storage_cmd( 'storage blob generate-sas -c {} -n {} --https-only --permissions r --expiry {} --full-uri -otsv', account_info, container, blob, expiry).output.strip() diff --git a/src/azure-cli/azure/cli/command_modules/storage/tests/latest/test_storage_blob_scenarios.py b/src/azure-cli/azure/cli/command_modules/storage/tests/latest/test_storage_blob_scenarios.py index 255d2dc8f19..eafa55ae5b4 100644 --- a/src/azure-cli/azure/cli/command_modules/storage/tests/latest/test_storage_blob_scenarios.py +++ b/src/azure-cli/azure/cli/command_modules/storage/tests/latest/test_storage_blob_scenarios.py @@ -6,7 +6,7 @@ import os import re import unittest -from datetime import UTC, datetime, timedelta +import datetime as dt from azure.cli.testsdk import (ScenarioTest, ResourceGroupPreparer, StorageAccountPreparer, JMESPathCheck, JMESPathCheckExists, NoneCheck) from knack.util import CLIError @@ -103,7 +103,7 @@ def verify_blob_upload_and_download(self, group, account, file_size_kb, blob_typ else: self.assertEqual(show_result.get('properties').get('pageRanges'), None) - expiry = (datetime.now(UTC) + timedelta(hours=1)).strftime('%Y-%m-%dT%H:%MZ') + expiry = (dt.datetime.now(dt.UTC) + dt.timedelta(hours=1)).strftime('%Y-%m-%dT%H:%MZ') sas = self.storage_cmd('storage blob generate-sas -n {} -c {} --expiry {} --permissions ' 'r --https-only', account_info, blob_name, container, expiry).output self.assertTrue(sas) @@ -280,8 +280,7 @@ def test_storage_blob_container_operations(self, resource_group, storage_account .assert_with_checks(JMESPathCheck('publicAccess', 'off')) # test case with access policy set - from datetime import UTC, datetime, timedelta - expiry = (datetime.now(UTC) + timedelta(hours=1)).strftime('%Y-%m-%dT%H:%MZ') + expiry = (dt.datetime.now(dt.UTC) + dt.timedelta(hours=1)).strftime('%Y-%m-%dT%H:%MZ') policy = self.create_random_name('policy', 16) self.storage_cmd('storage container policy create -c {} -n {} --expiry {} --permissions racwdxyltfmei', account_info, c, policy, expiry) @@ -336,8 +335,7 @@ def test_storage_blob_container_operations(self, resource_group, storage_account JMESPathCheck('properties.lease.state', 'available'), JMESPathCheck('properties.lease.status', 'unlocked')) - from datetime import UTC, datetime, timedelta - expiry = (datetime.now(UTC) + timedelta(hours=1)).strftime('%Y-%m-%dT%H:%MZ') + expiry = (dt.datetime.now(dt.UTC) + dt.timedelta(hours=1)).strftime('%Y-%m-%dT%H:%MZ') self.assertIn('sig=', self.storage_cmd('storage container generate-sas -n {} --permissions r --expiry {}', account_info, c, expiry).output) @@ -518,7 +516,7 @@ def test_storage_blob_generate_sas_full_uri(self, resource_group, storage_accoun c = self.create_container(account_info) b = self.create_random_name('blob', 24) - expiry = (datetime.now(UTC) + timedelta(hours=1)).strftime('%Y-%m-%dT%H:%MZ') + expiry = (dt.datetime.now(dt.UTC) + dt.timedelta(hours=1)).strftime('%Y-%m-%dT%H:%MZ') blob_uri = self.storage_cmd('storage blob generate-sas -n {} -c {} --expiry {} --permissions ' 'r --https-only --full-uri', account_info, b, c, expiry).output self.assertTrue(blob_uri) @@ -532,7 +530,7 @@ def test_storage_blob_generate_sas_as_user(self, resource_group, storage_account c = self.create_container(account_info) b = self.create_random_name('blob', 24) - expiry = (datetime.now(UTC) + timedelta(hours=1)).strftime('%Y-%m-%dT%H:%MZ') + expiry = (dt.datetime.now(dt.UTC) + dt.timedelta(hours=1)).strftime('%Y-%m-%dT%H:%MZ') with self.assertRaisesRegex(CLIError, "incorrect usage: specify --as-user when --auth-mode login"): self.cmd('storage blob generate-sas --account-name {} -n {} -c {} --expiry {} --permissions r --https-only ' @@ -572,7 +570,7 @@ def test_storage_blob_generate_sas_with_user_delegation_oid(self, resource_group current_tenant = self.cmd('account show --query tenantId').get_output_in_json() current_tenant = current_tenant if current_tenant is not None else '544a7a2e-697f-487c-b2b0-a13df7f346b6' - expiry = (datetime.now(UTC) + timedelta(hours=1)).strftime('%Y-%m-%dT%H:%MZ') + expiry = (dt.datetime.now(dt.UTC) + dt.timedelta(hours=1)).strftime('%Y-%m-%dT%H:%MZ') self.storage_cmd('storage blob upload -c {} -n {} -f "{}"', account_info, c, b, local_file) @@ -697,7 +695,6 @@ def md5(fname): @ResourceGroupPreparer() @StorageAccountPreparer() def test_storage_blob_upload_precondition_scenarios(self, resource_group, storage_account_info): - from datetime import datetime import time account_info = storage_account_info container = self.create_container(account_info) @@ -709,7 +706,7 @@ def test_storage_blob_upload_precondition_scenarios(self, resource_group, storag self.storage_cmd('storage blob upload -c {} -f "{}" -n {}', account_info, container, local_file, blob_name) time.sleep(1) - current = datetime.now().strftime("%Y-%m-%dT%H:%M:%SZ") + current = dt.datetime.now().strftime("%Y-%m-%dT%H:%M:%SZ") time.sleep(1) with self.assertRaises(Exception): self.storage_cmd('storage blob upload -c {} -f "{}" -n {}', account_info, @@ -892,8 +889,7 @@ def test_storage_blob_vlm(self, resource_group, storage_account_info): self.storage_cmd('storage blob upload -c {} -f "{}" -n {} ', storage_account_info, container, file, blob) # Test set immutability policy - from datetime import UTC, datetime, timedelta - expiry = (datetime.now(UTC) + timedelta(hours=1)).strftime('%Y-%m-%dT%H:%MZ') + expiry = (dt.datetime.now(dt.UTC) + dt.timedelta(hours=1)).strftime('%Y-%m-%dT%H:%MZ') result = self.storage_cmd('storage blob immutability-policy set -n {} -c {} ' '--expiry-time {} --policy-mode Unlocked', storage_account_info, blob, container, expiry).get_output_in_json() @@ -1042,7 +1038,7 @@ def test_storage_blob_restore(self, resource_group, storage_account): JMESPathCheck('restorePolicy.days', 1), JMESPathCheckExists('restorePolicy.minRestoreTime')) - time_to_restore = (datetime.now(UTC) + timedelta(seconds=-5)).strftime('%Y-%m-%dT%H:%MZ') + time_to_restore = (dt.datetime.now(dt.UTC) + dt.timedelta(seconds=-5)).strftime('%Y-%m-%dT%H:%MZ') # c1/b1 -> c1/b2 start_range = '/'.join([c1, b1]) @@ -1059,7 +1055,7 @@ def test_storage_blob_restore(self, resource_group, storage_account): if self.is_live: time.sleep(300) - time_to_restore = (datetime.now(UTC) + timedelta(seconds=-5)).strftime('%Y-%m-%dT%H:%MZ') + time_to_restore = (dt.datetime.now(dt.UTC) + dt.timedelta(seconds=-5)).strftime('%Y-%m-%dT%H:%MZ') # c1/b2 -> c2/b3 start_range = '/'.join([c1, b2]) end_range = '/'.join([c2, b3]) diff --git a/src/azure-cli/azure/cli/command_modules/storage/tests/latest/test_storage_container_access_policy.py b/src/azure-cli/azure/cli/command_modules/storage/tests/latest/test_storage_container_access_policy.py index d9860864899..7de526ec655 100644 --- a/src/azure-cli/azure/cli/command_modules/storage/tests/latest/test_storage_container_access_policy.py +++ b/src/azure-cli/azure/cli/command_modules/storage/tests/latest/test_storage_container_access_policy.py @@ -8,13 +8,13 @@ JMESPathCheck, JMESPathCheckExists, NoneCheck, live_only) from ..storage_test_util import StorageScenarioMixin from knack.util import CLIError -from datetime import UTC, datetime, timedelta +import datetime as dt class StorageSASScenario(StorageScenarioMixin, ScenarioTest): @ResourceGroupPreparer() @StorageAccountPreparer(allow_blob_public_access=True) def test_storage_container_access_policy_scenario(self, resource_group, storage_account): - expiry = (datetime.now(UTC) + timedelta(hours=1)).strftime('%Y-%m-%dT%H:%MZ') + expiry = (dt.datetime.now(dt.UTC) + dt.timedelta(hours=1)).strftime('%Y-%m-%dT%H:%MZ') account_info = self.get_account_info(resource_group, storage_account) container = self.create_random_name(prefix='cont', length=24) @@ -40,8 +40,8 @@ def test_storage_container_access_policy_scenario(self, resource_group, storage_ container, local_file, sas) - new_start = (datetime.now(UTC) - timedelta(hours=1)).strftime('%Y-%m-%dT%H:%MZ') - new_expiry = (datetime.now(UTC) + timedelta(hours=1)).strftime('%Y-%m-%dT%H:%MZ') + new_start = (dt.datetime.now(dt.UTC) - dt.timedelta(hours=1)).strftime('%Y-%m-%dT%H:%MZ') + new_expiry = (dt.datetime.now(dt.UTC) + dt.timedelta(hours=1)).strftime('%Y-%m-%dT%H:%MZ') self.storage_cmd('storage container policy update -c {} -n {} --permissions rwdxl --start {} --expiry {}', account_info, container, policy, new_start, new_expiry) diff --git a/src/azure-cli/azure/cli/command_modules/storage/tests/latest/test_storage_file_scenarios.py b/src/azure-cli/azure/cli/command_modules/storage/tests/latest/test_storage_file_scenarios.py index b2e4023e7b2..137fdffd13e 100644 --- a/src/azure-cli/azure/cli/command_modules/storage/tests/latest/test_storage_file_scenarios.py +++ b/src/azure-cli/azure/cli/command_modules/storage/tests/latest/test_storage_file_scenarios.py @@ -361,8 +361,8 @@ def validate_file_scenario(self, account_info, share): file_list = self.storage_cmd('storage file list -s {}', account_info, share).get_output_in_json() self.assertIsNotNone(file_list[0]['lastAccessTime']) - from datetime import UTC, datetime, timedelta - expiry = (datetime.now(UTC) + timedelta(hours=1)).strftime('%Y-%m-%dT%H:%MZ') + import datetime as dt + expiry = (dt.datetime.now(dt.UTC) + dt.timedelta(hours=1)).strftime('%Y-%m-%dT%H:%MZ') self.storage_cmd('storage file generate-sas -s {} -p {} --permissions r --expiry {}', account_info, share, filename, expiry).assert_with_checks(StringContainCheck('sig=')) @@ -691,8 +691,8 @@ def test_storage_file_generate_sas_as_user(self, resource_group, storage_account local_file = self.create_temp_file(1024) file_name = os.path.basename(local_file) - from datetime import UTC, datetime, timedelta - expiry = (datetime.now(UTC) + timedelta(hours=1)).strftime('%Y-%m-%dT%H:%MZ') + import datetime as dt + expiry = (dt.datetime.now(dt.UTC) + dt.timedelta(hours=1)).strftime('%Y-%m-%dT%H:%MZ') with self.assertRaisesRegex(CLIError, "incorrect usage: specify --as-user when --auth-mode login"): self.cmd('storage file generate-sas --account-name {} -p {} -s {} --expiry {} --permissions r --https-only ' @@ -725,8 +725,8 @@ def test_storage_file_generate_sas_with_user_delegation_oid(self, resource_group current_tenant = self.cmd('account show --query tenantId').get_output_in_json() current_tenant = current_tenant if current_tenant is not None else '544a7a2e-697f-487c-b2b0-a13df7f346b6' - from datetime import UTC, datetime, timedelta - expiry = (datetime.now(UTC) + timedelta(hours=1)).strftime('%Y-%m-%dT%H:%MZ') + import datetime as dt + expiry = (dt.datetime.now(dt.UTC) + dt.timedelta(hours=1)).strftime('%Y-%m-%dT%H:%MZ') file_sas = self.cmd('storage file generate-sas --account-name {} -p {} -s {} --expiry {} --permissions crwd ' '--https-only --as-user --auth-mode login --backup-intent --user-delegation-oid {} ' diff --git a/src/azure-cli/azure/cli/command_modules/storage/tests/latest/test_storage_file_share_scenarios.py b/src/azure-cli/azure/cli/command_modules/storage/tests/latest/test_storage_file_share_scenarios.py index 8672a3cee77..d9c780ebe34 100644 --- a/src/azure-cli/azure/cli/command_modules/storage/tests/latest/test_storage_file_share_scenarios.py +++ b/src/azure-cli/azure/cli/command_modules/storage/tests/latest/test_storage_file_share_scenarios.py @@ -13,7 +13,7 @@ class StorageShareScenarioTests(StorageScenarioMixin, ScenarioTest): @ResourceGroupPreparer(name_prefix='clitest') @StorageAccountPreparer(name_prefix='share', kind='StorageV2', location='eastus2', sku='Standard_RAGRS') def test_storage_file_share_scenario(self, resource_group, storage_account): - from datetime import UTC, datetime, timedelta + import datetime as dt account_info = self.get_account_info(resource_group, storage_account) connection_string = self.get_connection_string(resource_group, storage_account) @@ -47,8 +47,8 @@ def test_storage_file_share_scenario(self, resource_group, storage_account): self.assertIn(share_name, share_list, 'The newly created share is not listed.') # Test generate-sas with start, expiry and permissions - start = (datetime.now(UTC) + timedelta(hours=1)).strftime('%Y-%m-%dT%H:%MZ') - expiry = (datetime.now(UTC) + timedelta(hours=2)).strftime('%Y-%m-%dT%H:%MZ') + start = (dt.datetime.now(dt.UTC) + dt.timedelta(hours=1)).strftime('%Y-%m-%dT%H:%MZ') + expiry = (dt.datetime.now(dt.UTC) + dt.timedelta(hours=2)).strftime('%Y-%m-%dT%H:%MZ') sas = self.storage_cmd('storage share generate-sas -n {} --permissions r --start {} --expiry {}', account_info, share_name, start, expiry).output self.assertIn('sig', sas, 'The sig segment is not in the sas {}'.format(sas)) @@ -164,8 +164,8 @@ def test_storage_share_generate_sas_as_user(self, resource_group, storage_accoun share = self.create_share(account_info) local_file = self.create_temp_file(1024) - from datetime import UTC, datetime, timedelta - expiry = (datetime.now(UTC) + timedelta(hours=1)).strftime('%Y-%m-%dT%H:%MZ') + import datetime as dt + expiry = (dt.datetime.now(dt.UTC) + dt.timedelta(hours=1)).strftime('%Y-%m-%dT%H:%MZ') with self.assertRaisesRegex(CLIError, "incorrect usage: specify --as-user when --auth-mode login"): self.cmd('storage share generate-sas --account-name {} -n {} --expiry {} --permissions r --https-only ' @@ -197,8 +197,8 @@ def test_storage_share_generate_sas_with_user_delegation_oid(self, resource_grou current_tenant = self.cmd('account show --query tenantId').get_output_in_json() current_tenant = current_tenant if current_tenant is not None else '544a7a2e-697f-487c-b2b0-a13df7f346b6' - from datetime import UTC, datetime, timedelta - expiry = (datetime.now(UTC) + timedelta(hours=1)).strftime('%Y-%m-%dT%H:%MZ') + import datetime as dt + expiry = (dt.datetime.now(dt.UTC) + dt.timedelta(hours=1)).strftime('%Y-%m-%dT%H:%MZ') share_sas = self.cmd('storage share generate-sas --account-name {} -n {} --expiry {} --permissions crwld ' '--https-only --as-user --auth-mode login --backup-intent --user-delegation-oid ' diff --git a/src/azure-cli/azure/cli/command_modules/storage/tests/latest/test_storage_queue_scenarios.py b/src/azure-cli/azure/cli/command_modules/storage/tests/latest/test_storage_queue_scenarios.py index 600ec3075a3..a5338c52794 100644 --- a/src/azure-cli/azure/cli/command_modules/storage/tests/latest/test_storage_queue_scenarios.py +++ b/src/azure-cli/azure/cli/command_modules/storage/tests/latest/test_storage_queue_scenarios.py @@ -14,7 +14,7 @@ class StorageQueueScenarioTests(StorageScenarioMixin, ScenarioTest): @ResourceGroupPreparer(name_prefix='clitest') @StorageAccountPreparer(name_prefix='queue', kind='StorageV2', location='eastus2', sku='Standard_RAGRS') def test_storage_queue_general_scenario(self, resource_group, storage_account): - from datetime import UTC, datetime, timedelta + import datetime as dt account_key = self.get_account_key(resource_group, storage_account) connection_string = self.get_connection_string(resource_group, storage_account) @@ -45,8 +45,8 @@ def test_storage_queue_general_scenario(self, resource_group, storage_account): self.assertIn(queue, [x['name'] for x in res], 'The newly created queue is not listed.') # Test generate-sas with start, expiry and permissions - start = (datetime.now(UTC) + timedelta(hours=1)).strftime('%Y-%m-%dT%H:%MZ') - expiry = (datetime.now(UTC) + timedelta(hours=2)).strftime('%Y-%m-%dT%H:%MZ') + start = (dt.datetime.now(dt.UTC) + dt.timedelta(hours=1)).strftime('%Y-%m-%dT%H:%MZ') + expiry = (dt.datetime.now(dt.UTC) + dt.timedelta(hours=2)).strftime('%Y-%m-%dT%H:%MZ') sas = self.cmd('storage queue generate-sas -n {} --permissions r --start {} --expiry {}' .format(queue, start, expiry)).output self.assertIn('sig', sas, 'The sig segment is not in the sas {}'.format(sas)) @@ -271,8 +271,8 @@ def test_storage_queue_generate_sas_as_user(self, resource_group, storage_accoun self.cmd('storage queue create --account-name {} -n {}'.format(storage_account, queue)) - from datetime import UTC, datetime, timedelta - expiry = (datetime.now(UTC) + timedelta(hours=1)).strftime('%Y-%m-%dT%H:%MZ') + import datetime as dt + expiry = (dt.datetime.now(dt.UTC) + dt.timedelta(hours=1)).strftime('%Y-%m-%dT%H:%MZ') with self.assertRaisesRegex(CLIError, "incorrect usage: specify --as-user when --auth-mode login"): self.cmd('storage queue generate-sas --account-name {} -n {} --expiry {} --permissions r --https-only ' @@ -306,8 +306,8 @@ def test_storage_queue_generate_sas_with_user_delegation_oid(self, resource_grou current_tenant = self.cmd('account show --query tenantId').get_output_in_json() current_tenant = current_tenant if current_tenant is not None else '544a7a2e-697f-487c-b2b0-a13df7f346b6' - from datetime import UTC, datetime, timedelta - expiry = (datetime.now(UTC) + timedelta(hours=1)).strftime('%Y-%m-%dT%H:%MZ') + import datetime as dt + expiry = (dt.datetime.now(dt.UTC) + dt.timedelta(hours=1)).strftime('%Y-%m-%dT%H:%MZ') queue_sas = self.cmd('storage queue generate-sas --account-name {} -n {} --expiry {} --permissions raup ' '--https-only --as-user --user-delegation-oid {} ' diff --git a/src/azure-cli/azure/cli/command_modules/storage/tests/latest/test_storage_sas_scenarios.py b/src/azure-cli/azure/cli/command_modules/storage/tests/latest/test_storage_sas_scenarios.py index 2f5991049ad..6a8126e7159 100644 --- a/src/azure-cli/azure/cli/command_modules/storage/tests/latest/test_storage_sas_scenarios.py +++ b/src/azure-cli/azure/cli/command_modules/storage/tests/latest/test_storage_sas_scenarios.py @@ -13,8 +13,8 @@ class StorageSASScenario(StorageScenarioMixin, LiveScenarioTest): @ResourceGroupPreparer() @StorageAccountPreparer(location='EastUS2') def test_storage_file_sas_scenario(self, resource_group, storage_account): - from datetime import UTC, datetime, timedelta - expiry = (datetime.now(UTC) + timedelta(hours=1)).strftime('%Y-%m-%dT%H:%MZ') + import datetime as dt + expiry = (dt.datetime.now(dt.UTC) + dt.timedelta(hours=1)).strftime('%Y-%m-%dT%H:%MZ') account_info = self.get_account_info(resource_group, storage_account) share = self.create_share(account_info) @@ -66,8 +66,8 @@ def test_storage_file_sas_scenario(self, resource_group, storage_account): @ResourceGroupPreparer() @StorageAccountPreparer() def test_storage_blob_sas_scenario(self, resource_group, storage_account): - from datetime import UTC, datetime, timedelta - expiry = (datetime.now(UTC) + timedelta(hours=1)).strftime('%Y-%m-%dT%H:%MZ') + import datetime as dt + expiry = (dt.datetime.now(dt.UTC) + dt.timedelta(hours=1)).strftime('%Y-%m-%dT%H:%MZ') account_info = self.get_account_info(resource_group, storage_account) container = self.create_container(account_info) @@ -131,8 +131,8 @@ def test_storage_blob_sas_scenario(self, resource_group, storage_account): @ResourceGroupPreparer(name_prefix='clitest') @StorageAccountPreparer(name_prefix='queuesas', kind='StorageV2', location='eastus2', sku='Standard_RAGRS') def test_storage_queue_sas_scenario(self, resource_group, storage_account): - from datetime import UTC, datetime, timedelta - expiry = (datetime.now(UTC) + timedelta(hours=1)).strftime('%Y-%m-%dT%H:%MZ') + import datetime as dt + expiry = (dt.datetime.now(dt.UTC) + dt.timedelta(hours=1)).strftime('%Y-%m-%dT%H:%MZ') account_info = self.get_account_info(resource_group, storage_account) queue = self.create_random_name('queue', 24) @@ -157,8 +157,8 @@ def test_storage_queue_sas_scenario(self, resource_group, storage_account): @ResourceGroupPreparer() @StorageAccountPreparer() def test_storage_account_sas_scenario(self, resource_group, storage_account): - from datetime import UTC, datetime, timedelta - expiry = (datetime.now(UTC) + timedelta(hours=1)).strftime('%Y-%m-%dT%H:%MZ') + import datetime as dt + expiry = (dt.datetime.now(dt.UTC) + dt.timedelta(hours=1)).strftime('%Y-%m-%dT%H:%MZ') connection_str = self.cmd('storage account show-connection-string -n {} -otsv'.format(storage_account)).output scope_name = self.create_random_name('scope', 16) @@ -206,8 +206,8 @@ def test_storage_account_sas_scenario(self, resource_group, storage_account): @ResourceGroupPreparer() @StorageAccountPreparer(hns=True, kind='StorageV2') def test_storage_fs_sas_scenario(self, resource_group, storage_account): - from datetime import UTC, datetime, timedelta - expiry = (datetime.now(UTC) + timedelta(hours=1)).strftime('%Y-%m-%dT%H:%MZ') + import datetime as dt + expiry = (dt.datetime.now(dt.UTC) + dt.timedelta(hours=1)).strftime('%Y-%m-%dT%H:%MZ') account_info = self.get_account_info(resource_group, storage_account) file_system = self.create_file_system(account_info) @@ -314,8 +314,8 @@ def test_storage_fs_sas_scenario(self, resource_group, storage_account): @ResourceGroupPreparer() @StorageAccountPreparer(hns=True, kind='StorageV2') def test_storage_fs_sas_with_encryption_scope(self, resource_group, storage_account_info): - from datetime import UTC, datetime, timedelta - expiry = (datetime.now(UTC) + timedelta(hours=1)).strftime('%Y-%m-%dT%H:%MZ') + import datetime as dt + expiry = (dt.datetime.now(dt.UTC) + dt.timedelta(hours=1)).strftime('%Y-%m-%dT%H:%MZ') storage_account, account_key = storage_account_info file_system = self.create_file_system(storage_account_info) @@ -345,8 +345,8 @@ def test_storage_table_entity_sas(self, resource_group, storage_account): self.storage_cmd('storage table create -n {} --fail-on-exist', account_info, table_name) \ .assert_with_checks(JMESPathCheck('created', True)) - from datetime import UTC, datetime, timedelta - expiry = (datetime.now(UTC) + timedelta(hours=1)).strftime('%Y-%m-%dT%H:%MZ') + import datetime as dt + expiry = (dt.datetime.now(dt.UTC) + dt.timedelta(hours=1)).strftime('%Y-%m-%dT%H:%MZ') sas_token_a = self.storage_cmd('storage table generate-sas -n {} --expiry {} --permissions a', account_info, table_name, expiry).get_output_in_json() account_name = account_info[0] diff --git a/src/azure-cli/azure/cli/command_modules/storage/tests/latest/test_storage_sas_tokan_url_scenarios.py b/src/azure-cli/azure/cli/command_modules/storage/tests/latest/test_storage_sas_tokan_url_scenarios.py index 5add177a934..25af58cf3a1 100644 --- a/src/azure-cli/azure/cli/command_modules/storage/tests/latest/test_storage_sas_tokan_url_scenarios.py +++ b/src/azure-cli/azure/cli/command_modules/storage/tests/latest/test_storage_sas_tokan_url_scenarios.py @@ -5,7 +5,7 @@ import os import requests -from datetime import UTC, datetime, timedelta +import datetime as dt from azure.cli.testsdk import (LiveScenarioTest, ResourceGroupPreparer, StorageAccountPreparer, JMESPathCheck, NoneCheck, StringCheck, StringContainCheck) from ..storage_test_util import StorageScenarioMixin @@ -19,7 +19,7 @@ def test_storage_file_url_sas_token_scenario(self, resource_group, storage_accou share = self.create_share(account_info) source_file = self.create_temp_file(128, full_random=False) filename = "sample_file.bin" - expiry = (datetime.now(UTC) + timedelta(hours=1)).strftime('%Y-%m-%dT%H:%MZ') + expiry = (dt.datetime.now(dt.UTC) + dt.timedelta(hours=1)).strftime('%Y-%m-%dT%H:%MZ') self.storage_cmd('storage share exists -n {}', account_info, share) \ .assert_with_checks(JMESPathCheck('exists', True)) @@ -48,7 +48,7 @@ def test_storage_blob_url_sas_token_scenario(self, resource_group, storage_accou container = self.create_container(account_info) source_file = self.create_temp_file(128, full_random=False) blob = "blob 1" - expiry = (datetime.now(UTC) + timedelta(hours=1)).strftime('%Y-%m-%dT%H:%MZ') + expiry = (dt.datetime.now(dt.UTC) + dt.timedelta(hours=1)).strftime('%Y-%m-%dT%H:%MZ') self.storage_cmd('storage container exists -n {}', account_info, container) \ .assert_with_checks(JMESPathCheck('exists', True)) diff --git a/src/azure-cli/azure/cli/command_modules/storage/tests/latest/test_storage_table_scenarios.py b/src/azure-cli/azure/cli/command_modules/storage/tests/latest/test_storage_table_scenarios.py index 2394624f9b7..26cc4426904 100644 --- a/src/azure-cli/azure/cli/command_modules/storage/tests/latest/test_storage_table_scenarios.py +++ b/src/azure-cli/azure/cli/command_modules/storage/tests/latest/test_storage_table_scenarios.py @@ -12,7 +12,7 @@ class StorageTableScenarioTests(StorageScenarioMixin, ScenarioTest): @ResourceGroupPreparer() @StorageAccountPreparer(sku='Standard_RAGRS') def test_storage_table_main_scenario(self, resource_group, storage_account): - from datetime import UTC, datetime, timedelta + import datetime as dt account_info = self.get_account_info(resource_group, storage_account) table_name = self.create_random_name('table', 24) @@ -24,7 +24,7 @@ def test_storage_table_main_scenario(self, resource_group, storage_account): self.assertIn(table_name, self.storage_cmd('storage table list --query "[].name"', account_info).get_output_in_json()) - expiry = (datetime.now(UTC) + timedelta(hours=1)).strftime('%Y-%m-%dT%H:%MZ') + expiry = (dt.datetime.now(dt.UTC) + dt.timedelta(hours=1)).strftime('%Y-%m-%dT%H:%MZ') sas = self.storage_cmd('storage table generate-sas -n {} --permissions r --expiry {}', account_info, table_name, expiry).output self.assertIn('sig=', sas) diff --git a/src/azure-cli/azure/cli/command_modules/storage/tests/latest/test_storage_validators.py b/src/azure-cli/azure/cli/command_modules/storage/tests/latest/test_storage_validators.py index f293eb37700..94bf14ad8d3 100644 --- a/src/azure-cli/azure/cli/command_modules/storage/tests/latest/test_storage_validators.py +++ b/src/azure-cli/azure/cli/command_modules/storage/tests/latest/test_storage_validators.py @@ -83,10 +83,10 @@ def test_datetime_string_type(self): get_datetime_type(True)(input) def test_datetime_type(self): - import datetime + import datetime as dt input = "2017-01-01T12:30Z" actual = get_datetime_type(False)(input) - expected = datetime.datetime(2017, 1, 1, 12, 30, 0) + expected = dt.datetime(2017, 1, 1, 12, 30, 0) self.assertEqual(actual, expected) input = "2017-01-01 12:30" diff --git a/src/azure-cli/azure/cli/command_modules/storage/util.py b/src/azure-cli/azure/cli/command_modules/storage/util.py index 49fa2ef1976..a365bc68fa2 100644 --- a/src/azure-cli/azure/cli/command_modules/storage/util.py +++ b/src/azure-cli/azure/cli/command_modules/storage/util.py @@ -6,7 +6,7 @@ import os from azure.cli.core.profiles import ResourceType -from datetime import UTC, datetime +import datetime as dt def collect_blobs(blob_service, container, pattern=None): @@ -103,13 +103,12 @@ def glob_files_remotely_track2(client, share_name, pattern, snapshot=None, is_sh def create_short_lived_blob_sas_v2(cmd, account_name, container, blob, account_key=None, user_delegation_key=None): - from datetime import timedelta t_sas = cmd.get_models('_shared_access_signature#BlobSharedAccessSignature', resource_type=ResourceType.DATA_STORAGE_BLOB) t_blob_permissions = cmd.get_models('_models#BlobSasPermissions', resource_type=ResourceType.DATA_STORAGE_BLOB) - expiry = (datetime.now(UTC) + timedelta(days=1)).strftime('%Y-%m-%dT%H:%M:%SZ') + expiry = (dt.datetime.now(dt.UTC) + dt.timedelta(days=1)).strftime('%Y-%m-%dT%H:%M:%SZ') if account_key: sas = t_sas(account_name, account_key=account_key) elif user_delegation_key: @@ -120,47 +119,42 @@ def create_short_lived_blob_sas_v2(cmd, account_name, container, blob, account_k def create_short_lived_file_sas_v2(cmd, account_name, account_key, share, directory_name, file_name): - from datetime import timedelta t_sas = cmd.get_models('_shared_access_signature#FileSharedAccessSignature', resource_type=ResourceType.DATA_STORAGE_FILESHARE) t_file_permissions = cmd.get_models('_models#FileSasPermissions', resource_type=ResourceType.DATA_STORAGE_FILESHARE) - expiry = (datetime.now(UTC) + timedelta(days=1)).strftime('%Y-%m-%dT%H:%M:%SZ') + expiry = (dt.datetime.now(dt.UTC) + dt.timedelta(days=1)).strftime('%Y-%m-%dT%H:%M:%SZ') sas = t_sas(account_name, account_key) return sas.generate_file(share, directory_name=directory_name, file_name=file_name, permission=t_file_permissions(read=True), expiry=expiry, protocol='https') def create_short_lived_container_sas_track2(cmd, account_name, account_key, container): - from datetime import timedelta t_generate_container_sas = cmd.get_models('_shared_access_signature#generate_container_sas', resource_type=ResourceType.DATA_STORAGE_BLOB) - expiry = (datetime.now(UTC) + timedelta(days=1)).strftime('%Y-%m-%dT%H:%M:%SZ') + expiry = (dt.datetime.now(dt.UTC) + dt.timedelta(days=1)).strftime('%Y-%m-%dT%H:%M:%SZ') return t_generate_container_sas(account_name, container, account_key, permission='r', expiry=expiry, protocol='https') def create_short_lived_share_sas_track2(cmd, account_name, account_key, share): - from datetime import timedelta t_generate_share_sas = cmd.get_models('#generate_share_sas', resource_type=ResourceType.DATA_STORAGE_FILESHARE) - expiry = (datetime.now(UTC) + timedelta(days=1)).strftime('%Y-%m-%dT%H:%M:%SZ') + expiry = (dt.datetime.now(dt.UTC) + dt.timedelta(days=1)).strftime('%Y-%m-%dT%H:%M:%SZ') return t_generate_share_sas(account_name, share, account_key, permission='r', expiry=expiry, protocol='https') def create_short_lived_blob_service_sas_track2(cmd, account_name, account_key, resource_types, permission='r'): - from datetime import timedelta t_generate_blob_service_sas = cmd.get_models('#generate_account_sas', resource_type=ResourceType.DATA_STORAGE_BLOB) - expiry = (datetime.now(UTC) + timedelta(days=1)).strftime('%Y-%m-%dT%H:%M:%SZ') + expiry = (dt.datetime.now(dt.UTC) + dt.timedelta(days=1)).strftime('%Y-%m-%dT%H:%M:%SZ') return t_generate_blob_service_sas(account_name, account_key, resource_types, permission=permission, expiry=expiry) def create_short_lived_file_service_sas_track2(cmd, account_name, account_key, resource_types, permission='r'): - from datetime import timedelta t_generate_file_service_sas = cmd.get_models('#generate_account_sas', resource_type=ResourceType.DATA_STORAGE_FILESHARE) - expiry = (datetime.now(UTC) + timedelta(days=1)).strftime('%Y-%m-%dT%H:%M:%SZ') + expiry = (dt.datetime.now(dt.UTC) + dt.timedelta(days=1)).strftime('%Y-%m-%dT%H:%M:%SZ') return t_generate_file_service_sas(account_name, account_key, resource_types, permission=permission, expiry=expiry) @@ -258,7 +252,7 @@ def get_datetime_from_string(dt_str): '%Y-%m-%dT%HZ', '%Y-%m-%d'] for form in accepted_date_formats: try: - return datetime.strptime(dt_str, form) + return dt.datetime.strptime(dt_str, form) except ValueError: continue raise ValueError("datetime string '{}' not valid. Valid example: 2000-12-31T12:59:59Z".format(dt_str)) From 41195c3770a285b6516fc7bc4fc8c8b51a1392d0 Mon Sep 17 00:00:00 2001 From: NostraDavid <55331731+NostraDavid@users.noreply.github.com> Date: Wed, 14 Oct 2026 20:42:16 +0200 Subject: [PATCH 03/13] packaging: add Python 3.15 to supported versions and CI --- .../messaging-cmdlet-gen.md | 4 +-- azure-pipelines-full-tests.yml | 34 +++++++++++++++++++ azure-pipelines.yml | 14 ++++++++ doc/command_guidelines.md | 2 +- doc/extensions/authoring.md | 2 +- doc/install_linux_prerequisites.md | 2 +- scripts/ci/build.sh | 3 ++ src/azure-cli-core/pyproject.toml | 1 + src/azure-cli-core/tox.ini | 2 +- src/azure-cli-telemetry/pyproject.toml | 1 + src/azure-cli-telemetry/tox.ini | 2 +- src/azure-cli-testsdk/pyproject.toml | 1 + src/azure-cli/pyproject.toml | 1 + 13 files changed, 62 insertions(+), 7 deletions(-) diff --git a/.github/agents/Messaging-cmdlets-Generation/messaging-cmdlet-gen.md b/.github/agents/Messaging-cmdlets-Generation/messaging-cmdlet-gen.md index 70ad53134e4..1eaeb218026 100644 --- a/.github/agents/Messaging-cmdlets-Generation/messaging-cmdlet-gen.md +++ b/.github/agents/Messaging-cmdlets-Generation/messaging-cmdlet-gen.md @@ -83,7 +83,7 @@ $VENV = "$SPEC_ROOT\.venv-aaz" ## 2. Environment -**Python 3.11-3.14 required.** Verify with the explicit path, not `PATH`; do not install it yourself. +**Python 3.11-3.15 required.** Verify with the explicit path, not `PATH`; do not install it yourself. * Windows: `& "$env:LOCALAPPDATA\Programs\Python\Python312\python.exe" --version` (installer from ; path form `C:\Users\{xxxx}\AppData\Local\Programs\Python\Python3{xxxx}`) @@ -734,7 +734,7 @@ Further reading: azure-cli `doc/` (extensions, command guidelines, authoring tes - [ ] Plan echoed back and confirmed (service, `` -> ``, swagger path, branch, scope). - [ ] **Section 4.1 pre-flight**: `az provider show` confirms whether ARM serves `` yet. - [ ] **Section 4.2**: pre-existing aaz<->azure-cli drift isolated and listed before generating. -- [ ] Python 3.11-3.14 venv active; `aaz-dev` + `azdev` installed. +- [ ] Python 3.11-3.15 venv active; `aaz-dev` + `azdev` installed. - [ ] Repos synced from upstream; `aaz` and `azure-cli` on identically named `feature-*` branches. - [ ] `azdev setup --cli $CLI --repo $EXT` clean. - [ ] `aaz-dev run` generation complete for the agreed scope. diff --git a/azure-pipelines-full-tests.yml b/azure-pipelines-full-tests.yml index 8015a713518..09d01703286 100644 --- a/azure-pipelines-full-tests.yml +++ b/azure-pipelines-full-tests.yml @@ -83,3 +83,37 @@ jobs: instance_idx: '$(Instance_idx)' fullTest: true jobName: 'FullTest' + +- job: AutomationFullTestPython315ProfileLatest + displayName: Automation Full Test Python315 Profile Latest + timeoutInMinutes: 9999 + strategy: + maxParallel: 8 + matrix: + instance1: + Instance_idx: 1 + instance2: + Instance_idx: 2 + instance3: + Instance_idx: 3 + instance4: + Instance_idx: 4 + instance5: + Instance_idx: 5 + instance6: + Instance_idx: 6 + instance7: + Instance_idx: 7 + instance8: + Instance_idx: 8 + pool: + name: ${{ variables.ubuntu_pool }} + steps: + - template: .azure-pipelines/templates/automation_test.yml + parameters: + pythonVersion: '3.15' + profile: 'latest' + instance_cnt: '8' + instance_idx: '$(Instance_idx)' + fullTest: true + jobName: 'FullTest' diff --git a/azure-pipelines.yml b/azure-pipelines.yml index 550a02fd604..a274b63b3cf 100644 --- a/azure-pipelines.yml +++ b/azure-pipelines.yml @@ -497,6 +497,8 @@ jobs: python.version: '3.12' Python314: python.version: '3.14' + Python315: + python.version: '3.15' dependsOn: BuildPythonWheel condition: succeeded() pool: @@ -539,6 +541,8 @@ jobs: python.version: '3.12' Python314: python.version: '3.14' + Python315: + python.version: '3.15' steps: - template: .azure-pipelines/templates/automation_test.yml parameters: @@ -556,6 +560,8 @@ jobs: python.version: '3.11' Python314: python.version: '3.14' + Python315: + python.version: '3.15' steps: - task: UsePythonVersion@0 inputs: @@ -578,6 +584,8 @@ jobs: python.version: '3.12' Python314: python.version: '3.14' + Python315: + python.version: '3.15' steps: - template: .azure-pipelines/templates/automation_test.yml parameters: @@ -598,6 +606,8 @@ jobs: python.version: '3.12' Python314: python.version: '3.14' + Python315: + python.version: '3.15' steps: - task: UsePythonVersion@0 displayName: 'Use Python $(python.version)' @@ -619,6 +629,8 @@ jobs: matrix: Python314: python.version: '3.14' + Python315: + python.version: '3.15' steps: - task: UsePythonVersion@0 displayName: 'Use Python $(python.version)' @@ -1066,6 +1078,8 @@ jobs: python.version: '3.12' Python314: python.version: '3.14' + Python315: + python.version: '3.15' pool: name: ${{ variables.ubuntu_pool }} steps: diff --git a/doc/command_guidelines.md b/doc/command_guidelines.md index 3aa07544da6..fd85c90a856 100644 --- a/doc/command_guidelines.md +++ b/doc/command_guidelines.md @@ -426,7 +426,7 @@ Follow the [Error Handling Guidelines](https://github.com/Azure/azure-cli/blob/d ## Coding Practices -- All code must support Python 3.11 ~ 3.14 +- All code must support Python 3.11 ~ 3.15 - PRs to Azure/azure-cli and Azure/azure-cli-extensions must pass CI - Code must pass style checks with pylint and pep8 - (*) All commands should have tests diff --git a/doc/extensions/authoring.md b/doc/extensions/authoring.md index ec2805e7afd..7b1db6ae554 100644 --- a/doc/extensions/authoring.md +++ b/doc/extensions/authoring.md @@ -130,7 +130,7 @@ See [Extension Metadata](metadata.md) for more information. ### Test your extension on Python 3 -- The Azure CLI supports Python 3.11 ~ 3.14 so verify that your extension does the same. +- The Azure CLI supports Python 3.11 ~ 3.15 so verify that your extension does the same. - You can create virtual environments for different versions and run your extension in them. - e.g. `python3.14 -m venv env314`. diff --git a/doc/install_linux_prerequisites.md b/doc/install_linux_prerequisites.md index 5503c9b8377..29b2b11fc79 100644 --- a/doc/install_linux_prerequisites.md +++ b/doc/install_linux_prerequisites.md @@ -6,7 +6,7 @@ Some native Linux packages are required when installing the CLI with: - Interactive installation script - `pip` -Current supported Python versions are Python 3.11 ~ 3.14. +Current supported Python versions are Python 3.11 ~ 3.15. The commands to run to install the dependencies for some common distributions are listed below. diff --git a/scripts/ci/build.sh b/scripts/ci/build.sh index 6699b2dbce3..fed0de996bb 100755 --- a/scripts/ci/build.sh +++ b/scripts/ci/build.sh @@ -112,6 +112,9 @@ CLASSIFIERS = [ 'Programming Language :: Python :: 3', 'Programming Language :: Python :: 3.11', 'Programming Language :: Python :: 3.12', + 'Programming Language :: Python :: 3.13', + 'Programming Language :: Python :: 3.14', + 'Programming Language :: Python :: 3.15', 'License :: OSI Approved :: MIT License', ] diff --git a/src/azure-cli-core/pyproject.toml b/src/azure-cli-core/pyproject.toml index b166313eb41..d96ba507beb 100644 --- a/src/azure-cli-core/pyproject.toml +++ b/src/azure-cli-core/pyproject.toml @@ -21,6 +21,7 @@ classifiers = [ "Programming Language :: Python :: 3.12", "Programming Language :: Python :: 3.13", "Programming Language :: Python :: 3.14", + "Programming Language :: Python :: 3.15", ] dependencies = [ 'argcomplete~=3.5.2', diff --git a/src/azure-cli-core/tox.ini b/src/azure-cli-core/tox.ini index 8c6aec2e1ce..2d693e48373 100644 --- a/src/azure-cli-core/tox.ini +++ b/src/azure-cli-core/tox.ini @@ -1,5 +1,5 @@ [tox] -envlist = py36,py37,py38 +envlist = py311,py312,py313,py314,py315 skip_missing_interpreters = True [testenv] diff --git a/src/azure-cli-telemetry/pyproject.toml b/src/azure-cli-telemetry/pyproject.toml index 00f99b256ad..5903aca9814 100644 --- a/src/azure-cli-telemetry/pyproject.toml +++ b/src/azure-cli-telemetry/pyproject.toml @@ -21,6 +21,7 @@ classifiers = [ "Programming Language :: Python :: 3.12", "Programming Language :: Python :: 3.13", "Programming Language :: Python :: 3.14", + "Programming Language :: Python :: 3.15", ] dependencies = [ 'applicationinsights>=0.11.1,<0.12', diff --git a/src/azure-cli-telemetry/tox.ini b/src/azure-cli-telemetry/tox.ini index 29a621711ee..10bc42465d1 100644 --- a/src/azure-cli-telemetry/tox.ini +++ b/src/azure-cli-telemetry/tox.ini @@ -1,5 +1,5 @@ [tox] -envlist = py36,py37,py38 +envlist = py311,py312,py313,py314,py315 skip_missing_interpreters = True [testenv] diff --git a/src/azure-cli-testsdk/pyproject.toml b/src/azure-cli-testsdk/pyproject.toml index d51b9cd4c57..09561b7ee23 100644 --- a/src/azure-cli-testsdk/pyproject.toml +++ b/src/azure-cli-testsdk/pyproject.toml @@ -20,6 +20,7 @@ classifiers = [ "Programming Language :: Python :: 3.12", "Programming Language :: Python :: 3.13", "Programming Language :: Python :: 3.14", + "Programming Language :: Python :: 3.15", ] dependencies = [ 'ConfigArgParse>=0.12.0', diff --git a/src/azure-cli/pyproject.toml b/src/azure-cli/pyproject.toml index 70a56f71444..7bef17d34e0 100644 --- a/src/azure-cli/pyproject.toml +++ b/src/azure-cli/pyproject.toml @@ -21,6 +21,7 @@ classifiers = [ "Programming Language :: Python :: 3.12", "Programming Language :: Python :: 3.13", "Programming Language :: Python :: 3.14", + "Programming Language :: Python :: 3.15", ] dependencies = [ "antlr4-python3-runtime~=4.13.1", From a6f53b6426dd75261af61e07ba7f5e830ba0b735 Mon Sep 17 00:00:00 2001 From: NostraDavid <55331731+NostraDavid@users.noreply.github.com> Date: Wed, 14 Oct 2026 20:47:54 +0200 Subject: [PATCH 04/13] core: explain the existing survey note regression test --- .../azure/cli/core/tests/test_intercept_survey.py | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/src/azure-cli-core/azure/cli/core/tests/test_intercept_survey.py b/src/azure-cli-core/azure/cli/core/tests/test_intercept_survey.py index a684f32b205..b42b67d61ef 100644 --- a/src/azure-cli-core/azure/cli/core/tests/test_intercept_survey.py +++ b/src/azure-cli-core/azure/cli/core/tests/test_intercept_survey.py @@ -12,6 +12,14 @@ class TestInterceptSurvey(unittest.TestCase): def test_should_prompt_with_existing_utc_note(self): + """Preserve scheduling for existing UTC notes stored without an offset. + + Past dates allow a prompt; future dates suppress it. This regression + test also passes before the migration. Replacing utcnow() alone would + compare a naive saved date with aware now(UTC) and raise TypeError, + so the reader must attach UTC to the parsed date. + Only the decision is checked, not prompt rendering or note creation. + """ cli = mock.Mock() cli.config.getboolean.return_value = True for timestamp, expected in [('2000-01-01T00:00:00', True), ('2999-01-01T00:00:00', False)]: From 0c8c43800ff8485fa8a94ed5cd17d3efd0560693 Mon Sep 17 00:00:00 2001 From: NostraDavid <55331731+NostraDavid@users.noreply.github.com> Date: Wed, 14 Oct 2026 20:55:49 +0200 Subject: [PATCH 05/13] cosmosdb: keep restore test fixtures before test methods --- .../test_cosmosdb_backuprestore_scenario.py | 43 +++++++++---------- 1 file changed, 21 insertions(+), 22 deletions(-) diff --git a/src/azure-cli/azure/cli/command_modules/cosmosdb/tests/latest/test_cosmosdb_backuprestore_scenario.py b/src/azure-cli/azure/cli/command_modules/cosmosdb/tests/latest/test_cosmosdb_backuprestore_scenario.py index 7045ff4aa66..f97a497e91e 100644 --- a/src/azure-cli/azure/cli/command_modules/cosmosdb/tests/latest/test_cosmosdb_backuprestore_scenario.py +++ b/src/azure-cli/azure/cli/command_modules/cosmosdb/tests/latest/test_cosmosdb_backuprestore_scenario.py @@ -530,27 +530,6 @@ def test_cosmosdb_xrr_single_region_account(self, resource_group): class CosmosDBRestoreUnitTests(unittest.TestCase): - def test_restore_timestamp_is_utc(self): - from azure.cli.command_modules.cosmosdb.custom import _convert_to_utc_timestamp - - expected = dt.datetime(2020, 1, 1, tzinfo=dt.UTC) - for timestamp in ['2020-01-01T00:00:00', '2020-01-01T00:00:00Z', - '2020-01-01T02:00:00+02:00', '2019-12-31T19:00:00-05:00']: - with self.subTest(timestamp=timestamp): - self.assertEqual(_convert_to_utc_timestamp(timestamp), expected) - - @mock.patch('azure.cli.command_modules.cosmosdb.custom._create_database_account') - @mock.patch('azure.cli.command_modules.cosmosdb._client_factory.cf_restorable_database_accounts') - def test_restore_accepts_timestamp_without_offset(self, get_accounts, create_account): - from azure.cli.command_modules.cosmosdb.custom import cli_cosmosdb_restore - - account = mock.Mock(account_name='source', deletion_time=None, - creation_time=dt.datetime(2000, 1, 1, tzinfo=dt.UTC)) - get_accounts.return_value.list.return_value = [account] - cli_cosmosdb_restore(mock.Mock(), mock.Mock(), 'rg', 'source', 'target', - '2020-01-01T00:00:00', 'westus') - self.assertEqual(create_account.call_args.kwargs['restore_timestamp'], '2020-01-01T00:00:00+00:00') - def setUp(self): # Only mock dependencies that cannot actually be imported. Never replace a # real, importable package in sys.modules: these unit tests share a @@ -812,4 +791,24 @@ def test_normal_create_aligns_arm_location_with_priority_zero(self): mock_params.assert_called_once() kwargs = mock_params.call_args.kwargs # Non-restore path: priority-0 location overrides arm_location. - self.assertEqual(kwargs.get('location'), "westus2") \ No newline at end of file + self.assertEqual(kwargs.get('location'), "westus2") + def test_restore_timestamp_is_utc(self): + from azure.cli.command_modules.cosmosdb.custom import _convert_to_utc_timestamp + + expected = dt.datetime(2020, 1, 1, tzinfo=dt.UTC) + for timestamp in ['2020-01-01T00:00:00', '2020-01-01T00:00:00Z', + '2020-01-01T02:00:00+02:00', '2019-12-31T19:00:00-05:00']: + with self.subTest(timestamp=timestamp): + self.assertEqual(_convert_to_utc_timestamp(timestamp), expected) + + @mock.patch('azure.cli.command_modules.cosmosdb.custom._create_database_account') + @mock.patch('azure.cli.command_modules.cosmosdb._client_factory.cf_restorable_database_accounts') + def test_restore_accepts_timestamp_without_offset(self, get_accounts, create_account): + from azure.cli.command_modules.cosmosdb.custom import cli_cosmosdb_restore + + account = mock.Mock(account_name='source', deletion_time=None, + creation_time=dt.datetime(2000, 1, 1, tzinfo=dt.UTC)) + get_accounts.return_value.list.return_value = [account] + cli_cosmosdb_restore(mock.Mock(), mock.Mock(), 'rg', 'source', 'target', + '2020-01-01T00:00:00', 'westus') + self.assertEqual(create_account.call_args.kwargs['restore_timestamp'], '2020-01-01T00:00:00+00:00') From d55192f680883acde3bfef06be5b46e634edd0ac Mon Sep 17 00:00:00 2001 From: NostraDavid <55331731+NostraDavid@users.noreply.github.com> Date: Wed, 14 Oct 2026 21:02:37 +0200 Subject: [PATCH 06/13] cosmosdb: separate restore test methods with a blank line --- .../tests/latest/test_cosmosdb_backuprestore_scenario.py | 1 + 1 file changed, 1 insertion(+) diff --git a/src/azure-cli/azure/cli/command_modules/cosmosdb/tests/latest/test_cosmosdb_backuprestore_scenario.py b/src/azure-cli/azure/cli/command_modules/cosmosdb/tests/latest/test_cosmosdb_backuprestore_scenario.py index f97a497e91e..221acee8013 100644 --- a/src/azure-cli/azure/cli/command_modules/cosmosdb/tests/latest/test_cosmosdb_backuprestore_scenario.py +++ b/src/azure-cli/azure/cli/command_modules/cosmosdb/tests/latest/test_cosmosdb_backuprestore_scenario.py @@ -792,6 +792,7 @@ def test_normal_create_aligns_arm_location_with_priority_zero(self): kwargs = mock_params.call_args.kwargs # Non-restore path: priority-0 location overrides arm_location. self.assertEqual(kwargs.get('location'), "westus2") + def test_restore_timestamp_is_utc(self): from azure.cli.command_modules.cosmosdb.custom import _convert_to_utc_timestamp From 2aef274b0e00f1925818d268d78462534ac4392f Mon Sep 17 00:00:00 2001 From: NostraDavid <55331731+NostraDavid@users.noreply.github.com> Date: Wed, 14 Oct 2026 21:06:43 +0200 Subject: [PATCH 07/13] feedback: clarify the saved survey timestamp test name --- .../cli/command_modules/feedback/tests/latest/test_feedback.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/azure-cli/azure/cli/command_modules/feedback/tests/latest/test_feedback.py b/src/azure-cli/azure/cli/command_modules/feedback/tests/latest/test_feedback.py index c4e7d66f365..71a19fd5e6a 100644 --- a/src/azure-cli/azure/cli/command_modules/feedback/tests/latest/test_feedback.py +++ b/src/azure-cli/azure/cli/command_modules/feedback/tests/latest/test_feedback.py @@ -208,7 +208,7 @@ def _handle_exception_with_log(ex, *args, **kwargs): class TestSurvey(unittest.TestCase): - def test_existing_utc_note(self): + def test_handle_survey_accepts_saved_timestamp_without_offset(self): from azure.cli.command_modules.feedback import custom note = '{"last_prompt_time": "2000-01-01T00:00:00"}' From 55cccb49c68ce7b75296f58d8c31d0678295fe57 Mon Sep 17 00:00:00 2001 From: NostraDavid <55331731+NostraDavid@users.noreply.github.com> Date: Wed, 14 Oct 2026 21:10:59 +0200 Subject: [PATCH 08/13] lab: keep the parsed expiration date separate --- src/azure-cli/azure/cli/command_modules/lab/validators.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/azure-cli/azure/cli/command_modules/lab/validators.py b/src/azure-cli/azure/cli/command_modules/lab/validators.py index c376c7acb56..733667f9f78 100644 --- a/src/azure-cli/azure/cli/command_modules/lab/validators.py +++ b/src/azure-cli/azure/cli/command_modules/lab/validators.py @@ -115,8 +115,8 @@ def _validate_expiration_date(args): if has_value(args.expiration_date): import datetime as dt import dateutil.parser - expiration_date = dateutil.parser.parse(args.expiration_date.to_serialized_data()) - expiration_date = expiration_date.replace(tzinfo=expiration_date.tzinfo or dt.UTC) + parsed_expiration_date = dateutil.parser.parse(args.expiration_date.to_serialized_data()) + expiration_date = parsed_expiration_date.replace(tzinfo=parsed_expiration_date.tzinfo or dt.UTC) if dt.datetime.now(dt.UTC) >= expiration_date: raise ArgumentUsageError( "Expiration date '{}' must be in future.".format(args.expiration_date.to_serialized_data())) From 7d320ccf07158c6f76cf9d9314ca36e9f5f57922 Mon Sep 17 00:00:00 2001 From: NostraDavid <55331731+NostraDavid@users.noreply.github.com> Date: Wed, 14 Oct 2026 21:26:42 +0200 Subject: [PATCH 09/13] pipeline: run the full suite on Python 3.11 --- azure-pipelines-full-tests.yml | 34 ++++++++++++++++++++++++++++++++++ 1 file changed, 34 insertions(+) diff --git a/azure-pipelines-full-tests.yml b/azure-pipelines-full-tests.yml index 09d01703286..51383644be2 100644 --- a/azure-pipelines-full-tests.yml +++ b/azure-pipelines-full-tests.yml @@ -16,6 +16,40 @@ variables: - template: ${{ variables.Pipeline.Workspace }}/.azure-pipelines/templates/variables.yml jobs: +- job: AutomationFullTestPython311ProfileLatest + displayName: Automation Full Test Python311 Profile Latest + timeoutInMinutes: 9999 + strategy: + maxParallel: 8 + matrix: + instance1: + Instance_idx: 1 + instance2: + Instance_idx: 2 + instance3: + Instance_idx: 3 + instance4: + Instance_idx: 4 + instance5: + Instance_idx: 5 + instance6: + Instance_idx: 6 + instance7: + Instance_idx: 7 + instance8: + Instance_idx: 8 + pool: + name: ${{ variables.ubuntu_pool }} + steps: + - template: .azure-pipelines/templates/automation_test.yml + parameters: + pythonVersion: '3.11' + profile: 'latest' + instance_cnt: '8' + instance_idx: '$(Instance_idx)' + fullTest: true + jobName: 'FullTest' + - job: AutomationFullTestPython312ProfileLatest displayName: Automation Full Test Python312 Profile Latest timeoutInMinutes: 9999 From b6f52e993ca9f79903e8aea7ebb4f2e32496618b Mon Sep 17 00:00:00 2001 From: NostraDavid <55331731+NostraDavid@users.noreply.github.com> Date: Wed, 14 Oct 2026 21:35:48 +0200 Subject: [PATCH 10/13] pipeline: balance Python version coverage by test cost --- azure-pipelines.yml | 26 ++++++++++++++++---------- 1 file changed, 16 insertions(+), 10 deletions(-) diff --git a/azure-pipelines.yml b/azure-pipelines.yml index a274b63b3cf..c620cc53be6 100644 --- a/azure-pipelines.yml +++ b/azure-pipelines.yml @@ -495,6 +495,8 @@ jobs: python.version: '3.11' Python312: python.version: '3.12' + Python313: + python.version: '3.13' Python314: python.version: '3.14' Python315: @@ -539,6 +541,8 @@ jobs: python.version: '3.11' Python312: python.version: '3.12' + Python313: + python.version: '3.13' Python314: python.version: '3.14' Python315: @@ -558,6 +562,10 @@ jobs: matrix: Python311: python.version: '3.11' + Python312: + python.version: '3.12' + Python313: + python.version: '3.13' Python314: python.version: '3.14' Python315: @@ -580,8 +588,12 @@ jobs: name: ${{ variables.ubuntu_pool }} strategy: matrix: + Python311: + python.version: '3.11' Python312: python.version: '3.12' + Python313: + python.version: '3.13' Python314: python.version: '3.14' Python315: @@ -602,10 +614,8 @@ jobs: name: ${{ variables.ubuntu_pool }} strategy: matrix: - Python312: - python.version: '3.12' - Python314: - python.version: '3.14' + Python311: + python.version: '3.11' Python315: python.version: '3.15' steps: @@ -627,8 +637,8 @@ jobs: name: ${{ variables.ubuntu_pool }} strategy: matrix: - Python314: - python.version: '3.14' + Python311: + python.version: '3.11' Python315: python.version: '3.15' steps: @@ -1074,12 +1084,8 @@ jobs: displayName: "PerformanceCheck" strategy: matrix: - Python312: - python.version: '3.12' Python314: python.version: '3.14' - Python315: - python.version: '3.15' pool: name: ${{ variables.ubuntu_pool }} steps: From 978013b81bcae5477f905d7eac2df29a35a09788 Mon Sep 17 00:00:00 2001 From: NostraDavid <55331731+NostraDavid@users.noreply.github.com> Date: Wed, 14 Oct 2026 21:39:13 +0200 Subject: [PATCH 11/13] pipeline: shift compatibility checks to Python 3.11 and 3.15 --- azure-pipelines-full-tests.yml | 68 ---------------------------------- azure-pipelines.yml | 26 ------------- 2 files changed, 94 deletions(-) diff --git a/azure-pipelines-full-tests.yml b/azure-pipelines-full-tests.yml index 51383644be2..9c1a841edef 100644 --- a/azure-pipelines-full-tests.yml +++ b/azure-pipelines-full-tests.yml @@ -50,74 +50,6 @@ jobs: fullTest: true jobName: 'FullTest' -- job: AutomationFullTestPython312ProfileLatest - displayName: Automation Full Test Python312 Profile Latest - timeoutInMinutes: 9999 - strategy: - maxParallel: 8 - matrix: - instance1: - Instance_idx: 1 - instance2: - Instance_idx: 2 - instance3: - Instance_idx: 3 - instance4: - Instance_idx: 4 - instance5: - Instance_idx: 5 - instance6: - Instance_idx: 6 - instance7: - Instance_idx: 7 - instance8: - Instance_idx: 8 - pool: - name: ${{ variables.ubuntu_pool }} - steps: - - template: .azure-pipelines/templates/automation_test.yml - parameters: - pythonVersion: '3.12' - profile: 'latest' - instance_cnt: '8' - instance_idx: '$(Instance_idx)' - fullTest: true - jobName: 'FullTest' - -- job: AutomationFullTestPython314ProfileLatest - displayName: Automation Full Test Python314 Profile Latest - timeoutInMinutes: 9999 - strategy: - maxParallel: 8 - matrix: - instance1: - Instance_idx: 1 - instance2: - Instance_idx: 2 - instance3: - Instance_idx: 3 - instance4: - Instance_idx: 4 - instance5: - Instance_idx: 5 - instance6: - Instance_idx: 6 - instance7: - Instance_idx: 7 - instance8: - Instance_idx: 8 - pool: - name: ${{ variables.ubuntu_pool }} - steps: - - template: .azure-pipelines/templates/automation_test.yml - parameters: - pythonVersion: '3.14' - profile: 'latest' - instance_cnt: '8' - instance_idx: '$(Instance_idx)' - fullTest: true - jobName: 'FullTest' - - job: AutomationFullTestPython315ProfileLatest displayName: Automation Full Test Python315 Profile Latest timeoutInMinutes: 9999 diff --git a/azure-pipelines.yml b/azure-pipelines.yml index c620cc53be6..a59d845be0c 100644 --- a/azure-pipelines.yml +++ b/azure-pipelines.yml @@ -493,12 +493,6 @@ jobs: matrix: Python311: python.version: '3.11' - Python312: - python.version: '3.12' - Python313: - python.version: '3.13' - Python314: - python.version: '3.14' Python315: python.version: '3.15' dependsOn: BuildPythonWheel @@ -539,12 +533,6 @@ jobs: matrix: Python311: python.version: '3.11' - Python312: - python.version: '3.12' - Python313: - python.version: '3.13' - Python314: - python.version: '3.14' Python315: python.version: '3.15' steps: @@ -562,12 +550,6 @@ jobs: matrix: Python311: python.version: '3.11' - Python312: - python.version: '3.12' - Python313: - python.version: '3.13' - Python314: - python.version: '3.14' Python315: python.version: '3.15' steps: @@ -590,12 +572,6 @@ jobs: matrix: Python311: python.version: '3.11' - Python312: - python.version: '3.12' - Python313: - python.version: '3.13' - Python314: - python.version: '3.14' Python315: python.version: '3.15' steps: @@ -637,8 +613,6 @@ jobs: name: ${{ variables.ubuntu_pool }} strategy: matrix: - Python311: - python.version: '3.11' Python315: python.version: '3.15' steps: From e0dd5746812a19e779d4812e9ac4a92082b18754 Mon Sep 17 00:00:00 2001 From: NostraDavid <55331731+NostraDavid@users.noreply.github.com> Date: Wed, 14 Oct 2026 22:15:35 +0200 Subject: [PATCH 12/13] datetime: cover UTC boundaries and test performance on Python 3.11 and 3.15 --- azure-pipelines.yml | 6 ++++-- .../tests/latest/test_acr_commands_mock.py | 8 ++++++++ .../test_cosmosdb_backuprestore_scenario.py | 4 +++- .../lab/tests/latest/test_lab_validators.py | 19 +++++++++++++++++++ .../role/tests/latest/test_role_custom.py | 12 ++++++++++++ .../tests/latest/test_storage_validators.py | 15 +++++++++++++++ 6 files changed, 61 insertions(+), 3 deletions(-) diff --git a/azure-pipelines.yml b/azure-pipelines.yml index a59d845be0c..31edd1e0e7c 100644 --- a/azure-pipelines.yml +++ b/azure-pipelines.yml @@ -1058,8 +1058,10 @@ jobs: displayName: "PerformanceCheck" strategy: matrix: - Python314: - python.version: '3.14' + Python311: + python.version: '3.11' + Python315: + python.version: '3.15' pool: name: ${{ variables.ubuntu_pool }} steps: diff --git a/src/azure-cli/azure/cli/command_modules/acr/tests/latest/test_acr_commands_mock.py b/src/azure-cli/azure/cli/command_modules/acr/tests/latest/test_acr_commands_mock.py index 95c8e3c34db..54cd7bfcd97 100644 --- a/src/azure-cli/azure/cli/command_modules/acr/tests/latest/test_acr_commands_mock.py +++ b/src/azure-cli/azure/cli/command_modules/acr/tests/latest/test_acr_commands_mock.py @@ -4,6 +4,7 @@ # -------------------------------------------------------------------------------------------- from urllib.parse import urlencode +import datetime as dt import json import unittest from unittest import mock @@ -64,6 +65,13 @@ class AcrMockCommandsTests(unittest.TestCase): + def test_add_days_to_now_returns_utc_max_on_overflow(self): + from azure.cli.command_modules.acr._utils import add_days_to_now + + result = add_days_to_now(999999999) + self.assertEqual(result, dt.datetime.max.replace(tzinfo=dt.UTC)) + self.assertIs(result.tzinfo, dt.UTC) + @mock.patch('azure.cli.command_modules.acr.repository.get_access_credentials', autospec=True) @mock.patch('requests.request', autospec=True) def test_repository_list(self, mock_requests_get, mock_get_access_credentials): diff --git a/src/azure-cli/azure/cli/command_modules/cosmosdb/tests/latest/test_cosmosdb_backuprestore_scenario.py b/src/azure-cli/azure/cli/command_modules/cosmosdb/tests/latest/test_cosmosdb_backuprestore_scenario.py index 221acee8013..81dad3b27d6 100644 --- a/src/azure-cli/azure/cli/command_modules/cosmosdb/tests/latest/test_cosmosdb_backuprestore_scenario.py +++ b/src/azure-cli/azure/cli/command_modules/cosmosdb/tests/latest/test_cosmosdb_backuprestore_scenario.py @@ -800,7 +800,9 @@ def test_restore_timestamp_is_utc(self): for timestamp in ['2020-01-01T00:00:00', '2020-01-01T00:00:00Z', '2020-01-01T02:00:00+02:00', '2019-12-31T19:00:00-05:00']: with self.subTest(timestamp=timestamp): - self.assertEqual(_convert_to_utc_timestamp(timestamp), expected) + result = _convert_to_utc_timestamp(timestamp) + self.assertEqual(result, expected) + self.assertEqual(result.utcoffset(), dt.timedelta(0)) @mock.patch('azure.cli.command_modules.cosmosdb.custom._create_database_account') @mock.patch('azure.cli.command_modules.cosmosdb._client_factory.cf_restorable_database_accounts') diff --git a/src/azure-cli/azure/cli/command_modules/lab/tests/latest/test_lab_validators.py b/src/azure-cli/azure/cli/command_modules/lab/tests/latest/test_lab_validators.py index 343c1524787..4cf3fdf464a 100644 --- a/src/azure-cli/azure/cli/command_modules/lab/tests/latest/test_lab_validators.py +++ b/src/azure-cli/azure/cli/command_modules/lab/tests/latest/test_lab_validators.py @@ -3,6 +3,7 @@ # Licensed under the MIT License. See License.txt in the project root for license information. # -------------------------------------------------------------------------------------------- +import datetime as dt import unittest from unittest import mock from azure.mgmt.core.tools import is_valid_resource_id @@ -76,3 +77,21 @@ def test_expiration_date_with_and_without_timezone(self): args.expiration_date.to_serialized_data.return_value = '2000-01-01T00:00:00' + suffix with self.assertRaises(ArgumentUsageError): _validate_expiration_date(args) + + def test_expiration_date_respects_offsets_near_current_time(self): + now = dt.datetime(2026, 10, 14, 12, tzinfo=dt.UTC) + cases = [('2026-10-14T11:30:00-01:00', False), + ('2026-10-14T13:30:00+02:00', True), + ('2026-10-14T12:30:00', False), + ('2026-10-14T12:00:00', True)] + with mock.patch('datetime.datetime', wraps=dt.datetime) as mock_datetime: + mock_datetime.now.return_value = now + for timestamp, expired in cases: + with self.subTest(timestamp=timestamp): + args = mock.Mock() + args.expiration_date.to_serialized_data.return_value = timestamp + if expired: + with self.assertRaises(ArgumentUsageError): + _validate_expiration_date(args) + else: + _validate_expiration_date(args) diff --git a/src/azure-cli/azure/cli/command_modules/role/tests/latest/test_role_custom.py b/src/azure-cli/azure/cli/command_modules/role/tests/latest/test_role_custom.py index f89ec64703a..550abc919cc 100644 --- a/src/azure-cli/azure/cli/command_modules/role/tests/latest/test_role_custom.py +++ b/src/azure-cli/azure/cli/command_modules/role/tests/latest/test_role_custom.py @@ -35,3 +35,15 @@ def test_assignment_events_with_start_time_and_default_end_time(self, get_client _get_assignment_events(mock.Mock(), start_time='2000-01-01T00:00:00Z') event_filter = get_client.return_value.activity_logs.list.call_args.kwargs['filter'] self.assertIn('eventTimestamp ge 2000-01-01T00:00:00Z', event_filter) + + @mock.patch('azure.cli.core.commands.client_factory.get_mgmt_service_client') + def test_assignment_events_with_explicit_end_time(self, get_client): + from azure.cli.command_modules.role.custom import _get_assignment_events + + get_client.return_value.activity_logs.list.return_value = [] + for start_time in [None, '2000-01-01T00:00:00Z']: + with self.subTest(start_time=start_time): + _get_assignment_events(mock.Mock(), start_time=start_time, end_time='2000-01-01T01:00:00Z') + event_filter = get_client.return_value.activity_logs.list.call_args.kwargs['filter'] + self.assertIn('eventTimestamp ge 2000-01-01T00:00:00Z and ' + 'eventTimestamp le 2000-01-01T01:00:00Z', event_filter) diff --git a/src/azure-cli/azure/cli/command_modules/storage/tests/latest/test_storage_validators.py b/src/azure-cli/azure/cli/command_modules/storage/tests/latest/test_storage_validators.py index 94bf14ad8d3..955b605353b 100644 --- a/src/azure-cli/azure/cli/command_modules/storage/tests/latest/test_storage_validators.py +++ b/src/azure-cli/azure/cli/command_modules/storage/tests/latest/test_storage_validators.py @@ -3,6 +3,7 @@ # Licensed under the MIT License. See License.txt in the project root for license information. # -------------------------------------------------------------------------------------------- +import datetime as dt import unittest from unittest import mock from argparse import (Namespace, ArgumentError) @@ -447,6 +448,20 @@ def test_as_user_validator_rejects_expiry_beyond_seven_days(self): with self.assertRaisesRegex(ArgumentError, 'within 7 days'): as_user_validator(ns) + def test_as_user_validator_enforces_exact_seven_day_limit(self): + now = dt.datetime(2026, 10, 14, 12, tzinfo=dt.UTC) + with mock.patch('datetime.datetime', wraps=dt.datetime) as mock_datetime: + mock_datetime.now.return_value = now + for seconds in [-1, 0, 1]: + with self.subTest(seconds=seconds): + expiry = (now + dt.timedelta(days=7, seconds=seconds)).strftime('%Y-%m-%dT%H:%M:%SZ') + ns = self._create_namespace(as_user=True, expiry=expiry, token_credential='token') + if seconds > 0: + with self.assertRaisesRegex(ArgumentError, 'within 7 days'): + as_user_validator(ns) + else: + as_user_validator(ns) + def _create_namespace(self, **kwargs): ns = Namespace(account_key='my_storage_key', account_name='storage_name', connection_string=None, dryrun=False, pattern='*', sas_token=None, source_account_key=None, source_account_name=None, From 4a666e51b60f87c3c80c3c1b9c8b2f3ea272dc69 Mon Sep 17 00:00:00 2001 From: NostraDavid <55331731+NostraDavid@users.noreply.github.com> Date: Wed, 14 Oct 2026 22:53:32 +0200 Subject: [PATCH 13/13] feedback: wrap UTC survey timestamp parsing for pylint --- src/azure-cli/azure/cli/command_modules/feedback/custom.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/azure-cli/azure/cli/command_modules/feedback/custom.py b/src/azure-cli/azure/cli/command_modules/feedback/custom.py index 09496388db9..32f1910cd02 100644 --- a/src/azure-cli/azure/cli/command_modules/feedback/custom.py +++ b/src/azure-cli/azure/cli/command_modules/feedback/custom.py @@ -605,7 +605,8 @@ def handle_survey(cmd): with open(GLOBAL_SURVEY_NOTE_PATH, 'r') as f: survey_note = json.load(f) if survey_note['last_prompt_time']: - last_prompt_time = dt.datetime.strptime(survey_note['last_prompt_time'], '%Y-%m-%dT%H:%M:%S').replace(tzinfo=dt.UTC) + last_prompt_time = dt.datetime.strptime( + survey_note['last_prompt_time'], '%Y-%m-%dT%H:%M:%S').replace(tzinfo=dt.UTC) use_duration = dt.datetime.now(dt.UTC) - last_prompt_time url = _SURVEY_URL.format(installation_id=Profile(cli_ctx=cmd.cli_ctx).get_installation_id(),