commit 38b7b20d774d7642bfaaa49d3c70e6dc76872a50 Author: MSVSphere Packaging Team Date: Fri Nov 29 09:40:17 2024 +0300 import awscli2-2.15.31-2.el10 diff --git a/.awscli2.metadata b/.awscli2.metadata new file mode 100644 index 0000000..990cf8a --- /dev/null +++ b/.awscli2.metadata @@ -0,0 +1 @@ +225e5e6a2aa2145d106cc20a4dacee47789b8830 SOURCES/aws-cli-2.15.31.tar.gz diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..f5e05a3 --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +SOURCES/aws-cli-2.15.31.tar.gz diff --git a/SOURCES/assertions.patch b/SOURCES/assertions.patch new file mode 100644 index 0000000..3545005 --- /dev/null +++ b/SOURCES/assertions.patch @@ -0,0 +1,40 @@ +diff --git a/tests/functional/eks/test_kubeconfig.py b/tests/functional/eks/test_kubeconfig.py +index 3d1bcf8..687eef2 100644 +--- a/tests/functional/eks/test_kubeconfig.py ++++ b/tests/functional/eks/test_kubeconfig.py +@@ -121,8 +121,9 @@ class TestKubeconfigLoader(unittest.TestCase): + ]) + loaded_config = self._loader.load_kubeconfig(simple_path) + self.assertEqual(loaded_config.content, content) +- self._validator.validate_config.called_with(Kubeconfig(simple_path, +- content)) ++ validated_config = self._validator.validate_config.call_args.args[0] ++ self.assertEqual(validated_config.path, simple_path) ++ self.assertEqual(validated_config.content, content) + + def test_load_noexist(self): + no_exist_path = os.path.join(self._temp_directory, +@@ -130,17 +131,18 @@ class TestKubeconfigLoader(unittest.TestCase): + loaded_config = self._loader.load_kubeconfig(no_exist_path) + self.assertEqual(loaded_config.content, + _get_new_kubeconfig_content()) +- self._validator.validate_config.called_with( +- Kubeconfig(no_exist_path, _get_new_kubeconfig_content())) ++ validated_config = self._validator.validate_config.call_args.args[0] ++ self.assertEqual(validated_config.path, no_exist_path) ++ self.assertEqual(validated_config.content, _get_new_kubeconfig_content()) + + def test_load_empty(self): + empty_path = self._clone_config("valid_empty_existing") + loaded_config = self._loader.load_kubeconfig(empty_path) + self.assertEqual(loaded_config.content, + _get_new_kubeconfig_content()) +- self._validator.validate_config.called_with( +- Kubeconfig(empty_path, +- _get_new_kubeconfig_content())) ++ validated_config = self._validator.validate_config.call_args.args[0] ++ self.assertEqual(validated_config.path, empty_path) ++ self.assertEqual(validated_config.content, _get_new_kubeconfig_content()) + + def test_load_directory(self): + current_directory = self._temp_directory diff --git a/SOURCES/python312.patch b/SOURCES/python312.patch new file mode 100644 index 0000000..40ba119 --- /dev/null +++ b/SOURCES/python312.patch @@ -0,0 +1,1165 @@ +diff --git a/awscli/bcdoc/docstringparser.py b/awscli/bcdoc/docstringparser.py +index 868bd5d..3eaf662 100644 +--- a/awscli/bcdoc/docstringparser.py ++++ b/awscli/bcdoc/docstringparser.py +@@ -11,9 +11,10 @@ + # ANY KIND, either express or implied. See the License for the specific + # language governing permissions and limitations under the License. + from botocore.compat import six ++from html import parser as html_parser + + +-class DocStringParser(six.moves.html_parser.HTMLParser): ++class DocStringParser(html_parser.HTMLParser): + """ + A simple HTML parser. Focused on converting the subset of HTML + that appears in the documentation strings of the JSON models into +@@ -23,20 +24,20 @@ class DocStringParser(six.moves.html_parser.HTMLParser): + def __init__(self, doc): + self.tree = None + self.doc = doc +- six.moves.html_parser.HTMLParser.__init__(self) ++ html_parser.HTMLParser.__init__(self) + + def reset(self): +- six.moves.html_parser.HTMLParser.reset(self) ++ html_parser.HTMLParser.reset(self) + self.tree = HTMLTree(self.doc) + + def feed(self, data): + # HTMLParser is an old style class, so the super() method will not work. +- six.moves.html_parser.HTMLParser.feed(self, data) ++ html_parser.HTMLParser.feed(self, data) + self.tree.write() + self.tree = HTMLTree(self.doc) + + def close(self): +- six.moves.html_parser.HTMLParser.close(self) ++ html_parser.HTMLParser.close(self) + # Write if there is anything remaining. + self.tree.write() + self.tree = HTMLTree(self.doc) +diff --git a/awscli/botocore/auth.py b/awscli/botocore/auth.py +index e8c83ad..e50be2a 100644 +--- a/awscli/botocore/auth.py ++++ b/awscli/botocore/auth.py +@@ -396,7 +396,7 @@ class SigV4Auth(BaseSigner): + def add_auth(self, request): + if self.credentials is None: + raise NoCredentialsError() +- datetime_now = datetime.datetime.utcnow() ++ datetime_now = datetime.datetime.now(datetime.timezone.utc) + request.context['timestamp'] = datetime_now.strftime(SIGV4_TIMESTAMP) + # This could be a retry. Make sure the previous + # authorization header is removed first. +@@ -440,7 +440,7 @@ class SigV4Auth(BaseSigner): + if 'Date' in request.headers: + del request.headers['Date'] + datetime_timestamp = datetime.datetime.strptime( +- request.context['timestamp'], SIGV4_TIMESTAMP) ++ request.context['timestamp'], SIGV4_TIMESTAMP[:-1] + '%z') + request.headers['Date'] = formatdate( + int(calendar.timegm(datetime_timestamp.timetuple()))) + if 'X-Amz-Date' in request.headers: +@@ -528,7 +528,7 @@ class S3ExpressPostAuth(S3ExpressAuth): + REQUIRES_IDENTITY_CACHE = True + + def add_auth(self, request): +- datetime_now = datetime.datetime.utcnow() ++ datetime_now = datetime.datetime.now(datetime.timezone.utc) + request.context['timestamp'] = datetime_now.strftime(SIGV4_TIMESTAMP) + + fields = {} +@@ -781,7 +781,7 @@ class S3SigV4PostAuth(SigV4Auth): + http://docs.aws.amazon.com/AmazonS3/latest/API/sigv4-UsingHTTPPOST.html + """ + def add_auth(self, request): +- datetime_now = datetime.datetime.utcnow() ++ datetime_now = datetime.datetime.now(datetime.timezone.utc) + request.context['timestamp'] = datetime_now.strftime(SIGV4_TIMESTAMP) + + fields = {} +diff --git a/awscli/botocore/compat.py b/awscli/botocore/compat.py +index b189271..de182f4 100644 +--- a/awscli/botocore/compat.py ++++ b/awscli/botocore/compat.py +@@ -33,7 +33,7 @@ from urllib3 import exceptions + logger = logging.getLogger(__name__) + + +-from botocore.vendored.six.moves import http_client ++from http import client as http_client + + class HTTPHeaders(http_client.HTTPMessage): + pass +diff --git a/awscli/botocore/configloader.py b/awscli/botocore/configloader.py +index 3d1de2f..d8120c6 100644 +--- a/awscli/botocore/configloader.py ++++ b/awscli/botocore/configloader.py +@@ -18,6 +18,7 @@ import sys + + import botocore.exceptions + from botocore.compat import six ++import configparser + + + def multi_file_load_config(*filenames): +@@ -143,10 +144,10 @@ def raw_config_parse(config_filename, parse_subsections=True): + path = os.path.expanduser(path) + if not os.path.isfile(path): + raise botocore.exceptions.ConfigNotFound(path=_unicode_path(path)) +- cp = six.moves.configparser.RawConfigParser() ++ cp = configparser.RawConfigParser() + try: + cp.read([path]) +- except (six.moves.configparser.Error, UnicodeDecodeError): ++ except (configparser.Error, UnicodeDecodeError): + raise botocore.exceptions.ConfigParseError( + path=_unicode_path(path)) + else: +diff --git a/awscli/botocore/crt/auth.py b/awscli/botocore/crt/auth.py +index 534a7f8..5046b35 100644 +--- a/awscli/botocore/crt/auth.py ++++ b/awscli/botocore/crt/auth.py +@@ -55,10 +55,7 @@ class CrtSigV4Auth(BaseSigner): + if self.credentials is None: + raise NoCredentialsError() + +- # Use utcnow() because that's what gets mocked by tests, but set +- # timezone because CRT assumes naive datetime is local time. +- datetime_now = datetime.datetime.utcnow().replace( +- tzinfo=datetime.timezone.utc) ++ datetime_now = datetime.datetime.now(datetime.timezone.utc) + + # Use existing 'X-Amz-Content-SHA256' header if able + existing_sha256 = self._get_existing_sha256(request) +@@ -245,10 +242,7 @@ class CrtSigV4AsymAuth(BaseSigner): + if self.credentials is None: + raise NoCredentialsError() + +- # Use utcnow() because that's what gets mocked by tests, but set +- # timezone because CRT assumes naive datetime is local time. +- datetime_now = datetime.datetime.utcnow().replace( +- tzinfo=datetime.timezone.utc) ++ datetime_now = datetime.datetime.now(datetime.timezone.utc) + + # Use existing 'X-Amz-Content-SHA256' header if able + existing_sha256 = self._get_existing_sha256(request) +diff --git a/awscli/botocore/docs/bcdoc/docstringparser.py b/awscli/botocore/docs/bcdoc/docstringparser.py +index 868bd5d..3eaf662 100644 +--- a/awscli/botocore/docs/bcdoc/docstringparser.py ++++ b/awscli/botocore/docs/bcdoc/docstringparser.py +@@ -11,9 +11,10 @@ + # ANY KIND, either express or implied. See the License for the specific + # language governing permissions and limitations under the License. + from botocore.compat import six ++from html import parser as html_parser + + +-class DocStringParser(six.moves.html_parser.HTMLParser): ++class DocStringParser(html_parser.HTMLParser): + """ + A simple HTML parser. Focused on converting the subset of HTML + that appears in the documentation strings of the JSON models into +@@ -23,20 +24,20 @@ class DocStringParser(six.moves.html_parser.HTMLParser): + def __init__(self, doc): + self.tree = None + self.doc = doc +- six.moves.html_parser.HTMLParser.__init__(self) ++ html_parser.HTMLParser.__init__(self) + + def reset(self): +- six.moves.html_parser.HTMLParser.reset(self) ++ html_parser.HTMLParser.reset(self) + self.tree = HTMLTree(self.doc) + + def feed(self, data): + # HTMLParser is an old style class, so the super() method will not work. +- six.moves.html_parser.HTMLParser.feed(self, data) ++ html_parser.HTMLParser.feed(self, data) + self.tree.write() + self.tree = HTMLTree(self.doc) + + def close(self): +- six.moves.html_parser.HTMLParser.close(self) ++ html_parser.HTMLParser.close(self) + # Write if there is anything remaining. + self.tree.write() + self.tree = HTMLTree(self.doc) +diff --git a/awscli/botocore/httpsession.py b/awscli/botocore/httpsession.py +index 29b2103..d51dfdd 100644 +--- a/awscli/botocore/httpsession.py ++++ b/awscli/botocore/httpsession.py +@@ -59,7 +59,7 @@ from botocore.exceptions import ( + ReadTimeoutError, + SSLError, + ) +-from botocore.vendored.six.moves.urllib_parse import unquote ++from urllib.parse import unquote + + filter_ssl_warnings() + logger = logging.getLogger(__name__) +diff --git a/awscli/botocore/parsers.py b/awscli/botocore/parsers.py +index ffca974..367c867 100644 +--- a/awscli/botocore/parsers.py ++++ b/awscli/botocore/parsers.py +@@ -119,7 +119,8 @@ import json + import logging + import re + +-from botocore.compat import ETree, XMLParseError, six ++from botocore.compat import ETree, XMLParseError ++from http import client as http_client + from botocore.eventstream import EventStream, NoInitialResponseError + from botocore.utils import ( + is_json_value_header, +@@ -300,7 +301,7 @@ class ResponseParser(object): + "service, unable to populate error code and message.") + return { + 'Error': {'Code': str(response['status_code']), +- 'Message': six.moves.http_client.responses.get( ++ 'Message': http_client.responses.get( + response['status_code'], '')}, + 'ResponseMetadata': {}, + } +@@ -1035,7 +1036,7 @@ class RestXMLParser(BaseRestParser, BaseXMLResponseParser): + return { + 'Error': { + 'Code': str(response['status_code']), +- 'Message': six.moves.http_client.responses.get( ++ 'Message': http_client.responses.get( + response['status_code'], ''), + }, + 'ResponseMetadata': { +diff --git a/awscli/botocore/signers.py b/awscli/botocore/signers.py +index 57df023..7ff87e4 100644 +--- a/awscli/botocore/signers.py ++++ b/awscli/botocore/signers.py +@@ -549,7 +549,7 @@ class S3PostPresigner(object): + policy = {} + + # Create an expiration date for the policy +- datetime_now = datetime.datetime.utcnow() ++ datetime_now = datetime.datetime.now(datetime.timezone.utc) + expire_date = datetime_now + datetime.timedelta(seconds=expires_in) + policy['expiration'] = expire_date.strftime(botocore.auth.ISO8601) + +diff --git a/awscli/botocore/utils.py b/awscli/botocore/utils.py +index a408d9d..f4d7ece 100644 +--- a/awscli/botocore/utils.py ++++ b/awscli/botocore/utils.py +@@ -72,7 +72,7 @@ from botocore.exceptions import ( + UnsupportedS3ControlArnError, + UnsupportedS3ControlConfigurationError, + ) +-from botocore.vendored.six.moves.urllib.request import getproxies, proxy_bypass ++from urllib.request import getproxies, proxy_bypass + from dateutil.tz import tzutc + from urllib3.exceptions import LocationParseError + +@@ -583,13 +583,13 @@ class InstanceMetadataFetcher(IMDSFetcher): + return + try: + expiration = datetime.datetime.strptime( +- expiration, "%Y-%m-%dT%H:%M:%SZ" ++ expiration, "%Y-%m-%dT%H:%M:%S%z" + ) + refresh_interval = self._config.get( + "ec2_credential_refresh_window", 60 * 10 + ) + refresh_interval_with_jitter = refresh_interval + random.randint(120, 600) +- current_time = datetime.datetime.utcnow() ++ current_time = datetime.datetime.now(datetime.timezone.utc) + refresh_offset = datetime.timedelta(seconds=refresh_interval_with_jitter) + extension_time = expiration - refresh_offset + if current_time >= extension_time: +diff --git a/awscli/compat.py b/awscli/compat.py +index 4ea633b..1031356 100644 +--- a/awscli/compat.py ++++ b/awscli/compat.py +@@ -25,6 +25,8 @@ from functools import partial + import urllib.parse as urlparse + from urllib.error import URLError + ++import queue ++import urllib.request + from botocore.compat import six + from botocore.compat import OrderedDict + +@@ -33,11 +35,10 @@ from botocore.compat import OrderedDict + # This may get large. + advance_iterator = six.advance_iterator + PY3 = six.PY3 +-queue = six.moves.queue +-shlex_quote = six.moves.shlex_quote ++shlex_quote = shlex.quote + StringIO = six.StringIO + BytesIO = six.BytesIO +-urlopen = six.moves.urllib.request.urlopen ++urlopen = urllib.request.urlopen + binary_type = six.binary_type + + # Most, but not all, python installations will have zlib. This is required to +diff --git a/awscli/customizations/cloudformation/deployer.py b/awscli/customizations/cloudformation/deployer.py +index 3733c55..8236d33 100644 +--- a/awscli/customizations/cloudformation/deployer.py ++++ b/awscli/customizations/cloudformation/deployer.py +@@ -20,7 +20,7 @@ import collections + from awscli.customizations.cloudformation import exceptions + from awscli.customizations.cloudformation.artifact_exporter import mktempfile, parse_s3_url + +-from datetime import datetime ++from datetime import datetime, timezone + + LOG = logging.getLogger(__name__) + +@@ -85,7 +85,7 @@ class Deployer(object): + :return: + """ + +- now = datetime.utcnow().isoformat() ++ now = datetime.now(timezone.utc).isoformat() + description = "Created by AWS CLI at {0} UTC".format(now) + + # Each changeset will get a unique name based on time +diff --git a/awscli/customizations/cloudtrail/validation.py b/awscli/customizations/cloudtrail/validation.py +index 78e2540..ad13507 100644 +--- a/awscli/customizations/cloudtrail/validation.py ++++ b/awscli/customizations/cloudtrail/validation.py +@@ -19,7 +19,7 @@ import re + import sys + import zlib + from zlib import error as ZLibError +-from datetime import datetime, timedelta ++from datetime import datetime, timedelta, timezone + from dateutil import tz, parser + + import cryptography +@@ -401,7 +401,7 @@ class DigestTraverser(object): + :param end_date: Date to stop validating at (inclusive). + """ + if end_date is None: +- end_date = datetime.utcnow() ++ end_date = datetime.now(timezone.utc) + end_date = normalize_date(end_date) + start_date = normalize_date(start_date) + bucket = self.starting_bucket +@@ -703,7 +703,7 @@ class CloudTrailValidateLogs(BasicCommand): + if args.end_time: + self.end_time = normalize_date(parse_date(args.end_time)) + else: +- self.end_time = normalize_date(datetime.utcnow()) ++ self.end_time = datetime.now(timezone.utc) + if self.start_time > self.end_time: + raise ParamValidationError( + 'Invalid time range specified: start-time must ' +diff --git a/awscli/customizations/codecommit.py b/awscli/customizations/codecommit.py +index 6b30e83..7859fb8 100644 +--- a/awscli/customizations/codecommit.py ++++ b/awscli/customizations/codecommit.py +@@ -150,7 +150,7 @@ class CodeCommitGetCommand(BasicCommand): + request = AWSRequest() + request.url = url_to_sign + request.method = 'GIT' +- now = datetime.datetime.utcnow() ++ now = datetime.datetime.now(datetime.timezone.utc) + request.context['timestamp'] = now.strftime('%Y%m%dT%H%M%S') + split = urlsplit(request.url) + # we don't want to include the port number in the signature +diff --git a/awscli/customizations/codedeploy/push.py b/awscli/customizations/codedeploy/push.py +index 4c08664..6d97c8c 100644 +--- a/awscli/customizations/codedeploy/push.py ++++ b/awscli/customizations/codedeploy/push.py +@@ -16,7 +16,7 @@ import sys + import zipfile + import tempfile + import contextlib +-from datetime import datetime ++from datetime import datetime, timezone + + from botocore.exceptions import ClientError + +@@ -133,7 +133,7 @@ class Push(BasicCommand): + if not parsed_args.description: + parsed_args.description = ( + 'Uploaded by AWS CLI {0} UTC'.format( +- datetime.utcnow().isoformat() ++ datetime.now(timezone.utc).isoformat() + ) + ) + +diff --git a/awscli/customizations/configure/__init__.py b/awscli/customizations/configure/__init__.py +index ab06305..55f11a9 100644 +--- a/awscli/customizations/configure/__init__.py ++++ b/awscli/customizations/configure/__init__.py +@@ -11,7 +11,7 @@ + # ANY KIND, either express or implied. See the License for the specific + # language governing permissions and limitations under the License. + import string +-from botocore.vendored.six.moves import shlex_quote ++from shlex import quote as shlex_quote + + NOT_SET = '' + PREDEFINED_SECTION_NAMES = ('plugins') +diff --git a/awscli/customizations/datapipeline/__init__.py b/awscli/customizations/datapipeline/__init__.py +index c47ca94..0c12c39 100644 +--- a/awscli/customizations/datapipeline/__init__.py ++++ b/awscli/customizations/datapipeline/__init__.py +@@ -12,7 +12,7 @@ + # language governing permissions and limitations under the License. + + import json +-from datetime import datetime, timedelta ++from datetime import datetime, timedelta, timezone + + from awscli.formatter import get_formatter + from awscli.arguments import CustomArgument +@@ -186,7 +186,7 @@ class QueryArgBuilder(object): + """ + def __init__(self, current_time=None): + if current_time is None: +- current_time = datetime.utcnow() ++ current_time = datetime.now(timezone.utc) + self.current_time = current_time + + def build_query(self, parsed_args): +diff --git a/awscli/customizations/ec2/bundleinstance.py b/awscli/customizations/ec2/bundleinstance.py +index 96d9ece..ad4dca0 100644 +--- a/awscli/customizations/ec2/bundleinstance.py ++++ b/awscli/customizations/ec2/bundleinstance.py +@@ -120,7 +120,7 @@ def _generate_policy(params): + # Called if there is no policy supplied by the user. + # Creates a policy that provides access for 24 hours. + delta = datetime.timedelta(hours=24) +- expires = datetime.datetime.utcnow() + delta ++ expires = datetime.datetime.now(datetime.timezone.utc) + delta + expires_iso = expires.strftime("%Y-%m-%dT%H:%M:%S.%fZ") + policy = POLICY.format(expires=expires_iso, + bucket=params['Bucket'], +diff --git a/awscli/customizations/eks/get_token.py b/awscli/customizations/eks/get_token.py +index c85b86d..9812be4 100644 +--- a/awscli/customizations/eks/get_token.py ++++ b/awscli/customizations/eks/get_token.py +@@ -16,7 +16,7 @@ import json + import os + import sys + +-from datetime import datetime, timedelta ++from datetime import datetime, timedelta, timezone + from botocore.signers import RequestSigner + from botocore.model import ServiceId + +@@ -106,7 +106,7 @@ class GetTokenCommand(BasicCommand): + ] + + def get_expiration_time(self): +- token_expiration = datetime.utcnow() + timedelta( ++ token_expiration = datetime.now(timezone.utc) + timedelta( + minutes=TOKEN_EXPIRATION_MINS + ) + return token_expiration.strftime('%Y-%m-%dT%H:%M:%SZ') +diff --git a/awscli/customizations/logs/tail.py b/awscli/customizations/logs/tail.py +index cb31510..623e027 100644 +--- a/awscli/customizations/logs/tail.py ++++ b/awscli/customizations/logs/tail.py +@@ -11,7 +11,8 @@ + # ANY KIND, either express or implied. See the License for the specific + # language governing permissions and limitations under the License. + from collections import defaultdict +-from datetime import datetime, timedelta ++from datetime import datetime, timedelta, timezone ++import functools + import json + import re + import time +@@ -261,7 +262,7 @@ class TimestampUtils(object): + def __init__(self, now=None): + self._now = now + if now is None: +- self._now = datetime.utcnow ++ self._now = functools.partial(datetime.now, timezone.utc) + + def to_epoch_millis(self, timestamp): + re_match = self._RELATIVE_TIMESTAMP_REGEX.match(timestamp) +diff --git a/awscli/customizations/opsworks.py b/awscli/customizations/opsworks.py +index a2a0c1e..ff73132 100644 +--- a/awscli/customizations/opsworks.py ++++ b/awscli/customizations/opsworks.py +@@ -507,7 +507,7 @@ class OpsWorksRegister(BasicCommand): + "Resource": arn, + } + if timeout is not None: +- valid_until = datetime.datetime.utcnow() + timeout ++ valid_until = datetime.datetime.now(datetime.timezone.utc) + timeout + statement["Condition"] = { + "DateLessThan": { + "aws:CurrentTime": +diff --git a/tests/functional/botocore/test_credentials.py b/tests/functional/botocore/test_credentials.py +index 843be90..0349eb4 100644 +--- a/tests/functional/botocore/test_credentials.py ++++ b/tests/functional/botocore/test_credentials.py +@@ -19,7 +19,7 @@ import time + import mock + import tempfile + import shutil +-from datetime import datetime, timedelta ++from datetime import datetime, timedelta, timezone + import sys + + import pytest +@@ -47,8 +47,8 @@ from botocore.stub import Stubber + from botocore.tokens import SSOTokenProvider + from botocore.utils import datetime2timestamp + +-TIME_IN_ONE_HOUR = datetime.utcnow() + timedelta(hours=1) +-TIME_IN_SIX_MONTHS = datetime.utcnow() + timedelta(hours=4320) ++TIME_IN_ONE_HOUR = datetime.now(timezone.utc) + timedelta(hours=1) ++TIME_IN_SIX_MONTHS = datetime.now(timezone.utc) + timedelta(hours=4320) + + + class TestCredentialRefreshRaces(unittest.TestCase): +diff --git a/tests/functional/botocore/test_ec2.py b/tests/functional/botocore/test_ec2.py +index 795094e..3ff22c4 100644 +--- a/tests/functional/botocore/test_ec2.py ++++ b/tests/functional/botocore/test_ec2.py +@@ -86,13 +86,13 @@ class TestCopySnapshotCustomization(BaseSessionTest): + '%s\n' + '\n' + ) +- self.now = datetime.datetime(2011, 9, 9, 23, 36) ++ self.now = datetime.datetime(2011, 9, 9, 23, 36, tzinfo=datetime.timezone.utc) + self.datetime_patch = mock.patch.object( + botocore.auth.datetime, 'datetime', + mock.Mock(wraps=datetime.datetime) + ) + self.mocked_datetime = self.datetime_patch.start() +- self.mocked_datetime.utcnow.return_value = self.now ++ self.mocked_datetime.now.return_value = self.now + + def tearDown(self): + super(TestCopySnapshotCustomization, self).tearDown() +diff --git a/tests/functional/botocore/test_lex.py b/tests/functional/botocore/test_lex.py +index 7e7f619..dbdf329 100644 +--- a/tests/functional/botocore/test_lex.py ++++ b/tests/functional/botocore/test_lex.py +@@ -11,7 +11,7 @@ + # ANY KIND, either express or implied. See the License for the specific + # language governing permissions and limitations under the License. + import mock +-from datetime import datetime ++from datetime import datetime, timezone + + from tests import BaseSessionTest, ClientHTTPStubber + +@@ -32,10 +32,10 @@ class TestLex(BaseSessionTest): + 'inputStream': b'' + } + +- timestamp = datetime(2017, 3, 22, 0, 0) ++ timestamp = datetime(2017, 3, 22, 0, 0, tzinfo=timezone.utc) + + with mock.patch('botocore.auth.datetime') as _datetime: +- _datetime.datetime.utcnow.return_value = timestamp ++ _datetime.datetime.now.return_value = timestamp + self.http_stubber.add_response(body=b'{}') + with self.http_stubber: + self.client.post_content(**params) +diff --git a/tests/functional/botocore/test_s3express.py b/tests/functional/botocore/test_s3express.py +index 390721e..ebb89b0 100644 +--- a/tests/functional/botocore/test_s3express.py ++++ b/tests/functional/botocore/test_s3express.py +@@ -108,7 +108,6 @@ class TestS3ExpressAuth: + class TestS3ExpressIdentityCache: + def test_default_s3_express_cache(self, default_s3_client, mock_datetime): + mock_datetime.now.return_value = DATE +- mock_datetime.utcnow.return_value = DATE + + identity_cache = S3ExpressIdentityCache( + default_s3_client, +@@ -126,7 +125,6 @@ class TestS3ExpressIdentityCache: + self, default_s3_client, mock_datetime + ): + mock_datetime.now.return_value = DATE +- mock_datetime.utcnow.return_value = DATE + bucket = 'my_bucket' + + identity_cache = S3ExpressIdentityCache( +@@ -151,7 +149,6 @@ class TestS3ExpressIdentityCache: + self, default_s3_client, mock_datetime + ): + mock_datetime.now.return_value = DATE +- mock_datetime.utcnow.return_value = DATE + bucket = 'my_bucket' + other_bucket = 'other_bucket' + +@@ -204,7 +201,7 @@ class TestS3ExpressRequests: + ) + + def test_create_bucket(self, default_s3_client, mock_datetime): +- mock_datetime.utcnow.return_value = DATE ++ mock_datetime.now.return_value = DATE + + with ClientHTTPStubber(default_s3_client) as stubber: + stubber.add_response() +@@ -228,7 +225,6 @@ class TestS3ExpressRequests: + self._assert_standard_sigv4_signature(stubber.requests[0].headers) + + def test_get_object(self, default_s3_client, mock_datetime): +- mock_datetime.utcnow.return_value = DATE + mock_datetime.now.return_value = DATE + + with ClientHTTPStubber(default_s3_client) as stubber: +@@ -250,7 +246,6 @@ class TestS3ExpressRequests: + def test_cache_with_multiple_requests( + self, default_s3_client, mock_datetime + ): +- mock_datetime.utcnow.return_value = DATE + mock_datetime.now.return_value = DATE + + with ClientHTTPStubber(default_s3_client) as stubber: +@@ -275,7 +270,6 @@ class TestS3ExpressRequests: + def test_delete_objects_injects_correct_checksum( + self, default_s3_client, mock_datetime + ): +- mock_datetime.utcnow.return_value = DATE + mock_datetime.now.return_value = DATE + + with ClientHTTPStubber(default_s3_client) as stubber: +diff --git a/tests/functional/botocore/test_sts.py b/tests/functional/botocore/test_sts.py +index cdc212a..a279881 100644 +--- a/tests/functional/botocore/test_sts.py ++++ b/tests/functional/botocore/test_sts.py +@@ -10,7 +10,7 @@ + # distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF + # ANY KIND, either express or implied. See the License for the specific + # language governing permissions and limitations under the License. +-from datetime import datetime ++from datetime import datetime, timezone + import re + + import mock +@@ -38,9 +38,9 @@ class TestSTSPresignedUrl(BaseSessionTest): + self.stubber.activate() + + def test_presigned_url_contains_no_content_type(self): +- timestamp = datetime(2017, 3, 22, 0, 0) ++ timestamp = datetime(2017, 3, 22, 0, 0, tzinfo=timezone.utc) + with mock.patch('botocore.auth.datetime') as _datetime: +- _datetime.datetime.utcnow.return_value = timestamp ++ _datetime.datetime.now.return_value = timestamp + url = self.client.generate_presigned_url('get_caller_identity', {}) + + # There should be no 'content-type' in x-amz-signedheaders +diff --git a/tests/functional/ec2/test_bundle_instance.py b/tests/functional/ec2/test_bundle_instance.py +index 5bc3204..67015f0 100644 +--- a/tests/functional/ec2/test_bundle_instance.py ++++ b/tests/functional/ec2/test_bundle_instance.py +@@ -34,7 +34,7 @@ class TestBundleInstance(BaseAWSCommandParamsTest): + + def setUp(self): + super(TestBundleInstance, self).setUp() +- # This mocks out datetime.datetime.utcnow() so that it always ++ # This mocks out datetime.datetime.now() so that it always + # returns the same datetime object. This is because this value + # is embedded into the policy file that is generated and we + # don't what the policy or its signature to change each time +@@ -44,7 +44,7 @@ class TestBundleInstance(BaseAWSCommandParamsTest): + mock.Mock(wraps=datetime.datetime) + ) + mocked_datetime = self.datetime_patcher.start() +- mocked_datetime.utcnow.return_value = datetime.datetime(2013, 8, 9) ++ mocked_datetime.now.return_value = datetime.datetime(2013, 8, 9, tzinfo=datetime.timezone.utc) + + def tearDown(self): + super(TestBundleInstance, self).tearDown() +diff --git a/tests/functional/ec2instanceconnect/test_opentunnel.py b/tests/functional/ec2instanceconnect/test_opentunnel.py +index 83f824d..ddefc47 100644 +--- a/tests/functional/ec2instanceconnect/test_opentunnel.py ++++ b/tests/functional/ec2instanceconnect/test_opentunnel.py +@@ -310,10 +310,10 @@ def request_params_for_describe_eice(): + + + @pytest.fixture +-def datetime_utcnow_patch(): ++def datetime_now_patch(): + clock = datetime.datetime(2020, 1, 1, 1, 1, 1, tzinfo=tzutc()) + with mock.patch('datetime.datetime') as dt: +- dt.utcnow.return_value = clock ++ dt.now.return_value = clock + yield dt + + +@@ -393,7 +393,7 @@ class TestOpenTunnel: + describe_eice_response, + request_params_for_describe_instance, + request_params_for_describe_eice, +- datetime_utcnow_patch, ++ datetime_now_patch, + ): + cli_runner.env["AWS_USE_FIPS_ENDPOINT"] = "false" + cmdline = [ +diff --git a/tests/functional/eks/test_get_token.py b/tests/functional/eks/test_get_token.py +index 89801f9..cdf51f7 100644 +--- a/tests/functional/eks/test_get_token.py ++++ b/tests/functional/eks/test_get_token.py +@@ -11,7 +11,7 @@ + # ANY KIND, either express or implied. See the License for the specific + # language governing permissions and limitations under the License. + import base64 +-from datetime import datetime ++from datetime import datetime, timezone + import json + import os + +@@ -80,7 +80,7 @@ class TestGetTokenCommand(BaseAWSCommandParamsTest): + + @mock.patch('awscli.customizations.eks.get_token.datetime') + def test_get_token(self, mock_datetime): +- mock_datetime.utcnow.return_value = datetime(2019, 10, 23, 23, 0, 0, 0) ++ mock_datetime.now.return_value = datetime(2019, 10, 23, 23, 0, 0, 0, tzinfo=timezone.utc) + cmd = 'eks get-token --cluster-name %s' % self.cluster_name + response = self.run_get_token(cmd) + self.assertEqual( +@@ -98,7 +98,7 @@ class TestGetTokenCommand(BaseAWSCommandParamsTest): + + @mock.patch('awscli.customizations.eks.get_token.datetime') + def test_query_nested_object(self, mock_datetime): +- mock_datetime.utcnow.return_value = datetime(2019, 10, 23, 23, 0, 0, 0) ++ mock_datetime.now.return_value = datetime(2019, 10, 23, 23, 0, 0, 0, tzinfo=timezone.utc) + cmd = 'eks get-token --cluster-name %s' % self.cluster_name + cmd += ' --query status' + response = self.run_get_token(cmd) +@@ -120,7 +120,7 @@ class TestGetTokenCommand(BaseAWSCommandParamsTest): + + @mock.patch('awscli.customizations.eks.get_token.datetime') + def test_output_text(self, mock_datetime): +- mock_datetime.utcnow.return_value = datetime(2019, 10, 23, 23, 0, 0, 0) ++ mock_datetime.now.return_value = datetime(2019, 10, 23, 23, 0, 0, 0, tzinfo=timezone.utc) + cmd = 'eks get-token --cluster-name %s' % self.cluster_name + cmd += ' --output text' + stdout, _, _ = self.run_cmd(cmd) +@@ -130,7 +130,7 @@ class TestGetTokenCommand(BaseAWSCommandParamsTest): + + @mock.patch('awscli.customizations.eks.get_token.datetime') + def test_output_table(self, mock_datetime): +- mock_datetime.utcnow.return_value = datetime(2019, 10, 23, 23, 0, 0, 0) ++ mock_datetime.now.return_value = datetime(2019, 10, 23, 23, 0, 0, 0, tzinfo=timezone.utc) + cmd = 'eks get-token --cluster-name %s' % self.cluster_name + cmd += ' --output table' + stdout, _, _ = self.run_cmd(cmd) +diff --git a/tests/functional/logs/test_tail.py b/tests/functional/logs/test_tail.py +index 6049a7f..5707da9 100644 +--- a/tests/functional/logs/test_tail.py ++++ b/tests/functional/logs/test_tail.py +@@ -152,7 +152,7 @@ class TestTailCommand(BaseAWSCommandParamsTest): + + def test_tail_defaults_to_10m(self): + datetime_mock = mock.Mock(wraps=datetime) +- datetime_mock.utcnow = mock.Mock( ++ datetime_mock.now = mock.Mock( + return_value=datetime(1970, 1, 1, 0, 10, 1, tzinfo=tz.tzutc())) + with mock.patch('awscli.customizations.logs.tail.datetime', + new=datetime_mock): +@@ -177,7 +177,7 @@ class TestTailCommand(BaseAWSCommandParamsTest): + + def test_tail_with_relative_since(self): + datetime_mock = mock.Mock(wraps=datetime) +- datetime_mock.utcnow = mock.Mock( ++ datetime_mock.now = mock.Mock( + return_value=datetime(1970, 1, 1, 0, 0, 2, tzinfo=tz.tzutc())) + with mock.patch('awscli.customizations.logs.tail.datetime', + new=datetime_mock): +diff --git a/tests/functional/rds/test_generate_db_auth_token.py b/tests/functional/rds/test_generate_db_auth_token.py +index 795b939..4562b92 100644 +--- a/tests/functional/rds/test_generate_db_auth_token.py ++++ b/tests/functional/rds/test_generate_db_auth_token.py +@@ -53,7 +53,7 @@ class TestGenerateDBAuthToken(BaseAWSCommandParamsTest): + clock = datetime.datetime(2016, 11, 7, 17, 39, 33, tzinfo=tzutc()) + + with mock.patch('datetime.datetime') as dt: +- dt.utcnow.return_value = clock ++ dt.now.return_value = clock + stdout, _, _ = self.run_cmd(command, expected_rc=0) + + expected = ( +diff --git a/tests/functional/s3/test_presign_command.py b/tests/functional/s3/test_presign_command.py +index 2db338a..03741d1 100644 +--- a/tests/functional/s3/test_presign_command.py ++++ b/tests/functional/s3/test_presign_command.py +@@ -18,13 +18,13 @@ from awscli.testutils import BaseAWSCommandParamsTest, mock, temporary_file + from awscli.testutils import create_clidriver + + +-# Values used to fix time.time() and datetime.datetime.utcnow() ++# Values used to fix time.time() and datetime.datetime.now() + # so we know the exact values of the signatures generated. + FROZEN_TIMESTAMP = 1471305652 + DEFAULT_EXPIRES = 3600 + FROZEN_TIME = mock.Mock(return_value=FROZEN_TIMESTAMP) + FROZEN_DATETIME = mock.Mock( +- return_value=datetime.datetime(2016, 8, 18, 14, 33, 3, 0)) ++ return_value=datetime.datetime(2016, 8, 18, 14, 33, 3, 0, tzinfo=datetime.timezone.utc)) + + + class TestPresignCommand(BaseAWSCommandParamsTest): +@@ -78,7 +78,7 @@ class TestPresignCommand(BaseAWSCommandParamsTest): + def get_presigned_url_for_cmd(self, cmdline): + with mock.patch('time.time', FROZEN_TIME): + with mock.patch('datetime.datetime') as d: +- d.utcnow = FROZEN_DATETIME ++ d.now = FROZEN_DATETIME + stdout = self.assert_params_for_cmd(cmdline, None)[0].strip() + return stdout + +diff --git a/tests/integration/botocore/test_client_http.py b/tests/integration/botocore/test_client_http.py +index d6b0902..4e66fdf 100644 +--- a/tests/integration/botocore/test_client_http.py ++++ b/tests/integration/botocore/test_client_http.py +@@ -8,7 +8,8 @@ from contextlib import contextmanager + + import botocore.session + from botocore.config import Config +-from botocore.vendored.six.moves import BaseHTTPServer, socketserver ++from http import server as BaseHTTPServer ++import socketserver + from botocore.exceptions import ( + ConnectTimeoutError, ReadTimeoutError, EndpointConnectionError, + ConnectionClosedError, ClientError, ProxyConnectionError +diff --git a/tests/integration/customizations/test_codecommit.py b/tests/integration/customizations/test_codecommit.py +index 751e051..fc4d2c1 100644 +--- a/tests/integration/customizations/test_codecommit.py ++++ b/tests/integration/customizations/test_codecommit.py +@@ -14,7 +14,7 @@ + import awscli + import os + +-from datetime import datetime ++from datetime import datetime, timezone + + from six import StringIO + from botocore.session import Session +@@ -59,7 +59,7 @@ class TestCodeCommitCredentialHelper(unittest.TestCase): + @mock.patch('sys.stdout', new_callable=StringIOWithFileNo) + @mock.patch.object(awscli.customizations.codecommit.datetime, 'datetime') + def test_integration_using_cli_driver(self, dt_mock, stdout_mock): +- dt_mock.utcnow.return_value = datetime(2010, 10, 8) ++ dt_mock.now.return_value = datetime(2010, 10, 8, tzinfo=timezone.utc) + driver = create_clidriver() + entry_point = AWSCLIEntryPoint(driver) + rc = entry_point.main('codecommit credential-helper get'.split()) +@@ -75,7 +75,7 @@ class TestCodeCommitCredentialHelper(unittest.TestCase): + @mock.patch('sys.stdout', new_callable=StringIOWithFileNo) + @mock.patch.object(awscli.customizations.codecommit.datetime, 'datetime') + def test_integration_fips_using_cli_driver(self, dt_mock, stdout_mock): +- dt_mock.utcnow.return_value = datetime(2010, 10, 8) ++ dt_mock.now.return_value = datetime(2010, 10, 8, tzinfo=timezone.utc) + driver = create_clidriver() + entry_point = AWSCLIEntryPoint(driver) + rc = entry_point.main('codecommit credential-helper get'.split()) +@@ -91,7 +91,7 @@ class TestCodeCommitCredentialHelper(unittest.TestCase): + @mock.patch('sys.stdout', new_callable=StringIOWithFileNo) + @mock.patch.object(awscli.customizations.codecommit.datetime, 'datetime') + def test_integration_vpc_using_cli_driver(self, dt_mock, stdout_mock): +- dt_mock.utcnow.return_value = datetime(2010, 10, 8) ++ dt_mock.now.return_value = datetime(2010, 10, 8, tzinfo=timezone.utc) + driver = create_clidriver() + entry_point = AWSCLIEntryPoint(driver) + rc = entry_point.main('codecommit credential-helper get'.split()) +diff --git a/tests/unit/botocore/auth/test_signers.py b/tests/unit/botocore/auth/test_signers.py +index 73d3b12..25ca21f 100644 +--- a/tests/unit/botocore/auth/test_signers.py ++++ b/tests/unit/botocore/auth/test_signers.py +@@ -28,10 +28,10 @@ from botocore.awsrequest import AWSRequest + + class BaseTestWithFixedDate(unittest.TestCase): + def setUp(self): +- self.fixed_date = datetime.datetime(2014, 3, 10, 17, 2, 55, 0) ++ self.fixed_date = datetime.datetime(2014, 3, 10, 17, 2, 55, 0, tzinfo=datetime.timezone.utc) + self.datetime_patch = mock.patch('botocore.auth.datetime.datetime') + self.datetime_mock = self.datetime_patch.start() +- self.datetime_mock.utcnow.return_value = self.fixed_date ++ self.datetime_mock.now.return_value = self.fixed_date + self.datetime_mock.strptime.return_value = self.fixed_date + + def tearDown(self): +@@ -358,9 +358,9 @@ class TestSigV4(unittest.TestCase): + with mock.patch.object( + botocore.auth.datetime, 'datetime', + mock.Mock(wraps=datetime.datetime)) as mock_datetime: +- original_utcnow = datetime.datetime(2014, 1, 1, 0, 0) ++ original_now = datetime.datetime(2014, 1, 1, 0, 0, tzinfo=datetime.timezone.utc) + +- mock_datetime.utcnow.return_value = original_utcnow ++ mock_datetime.now.return_value = original_now + # Go through the add_auth process once. This will attach + # a timestamp to the request at the beginning of auth. + auth.add_auth(request) +@@ -368,8 +368,8 @@ class TestSigV4(unittest.TestCase): + # Ensure the date is in the Authorization header + self.assertIn('20140101', request.headers['Authorization']) + # Now suppose the utc time becomes the next day all of a sudden +- mock_datetime.utcnow.return_value = datetime.datetime( +- 2014, 1, 2, 0, 0) ++ mock_datetime.now.return_value = datetime.datetime( ++ 2014, 1, 2, 0, 0, tzinfo=datetime.timezone.utc) + # Smaller methods like the canonical request and string_to_sign + # should have the timestamp attached to the request in their + # body and not what the time is now mocked as. This is to ensure +@@ -535,8 +535,8 @@ class TestSigV4Presign(BasePresignTest): + mock.Mock(wraps=datetime.datetime) + ) + mocked_datetime = self.datetime_patcher.start() +- mocked_datetime.utcnow.return_value = datetime.datetime( +- 2014, 1, 1, 0, 0) ++ mocked_datetime.now.return_value = datetime.datetime( ++ 2014, 1, 1, 0, 0, tzinfo=datetime.timezone.utc) + + def tearDown(self): + self.datetime_patcher.stop() +@@ -730,8 +730,8 @@ class TestS3SigV4Post(BaseS3PresignPostTest): + mock.Mock(wraps=datetime.datetime) + ) + mocked_datetime = self.datetime_patcher.start() +- mocked_datetime.utcnow.return_value = datetime.datetime( +- 2014, 1, 1, 0, 0) ++ mocked_datetime.now.return_value = datetime.datetime( ++ 2014, 1, 1, 0, 0, tzinfo=datetime.timezone.utc) + + def tearDown(self): + self.datetime_patcher.stop() +diff --git a/tests/unit/botocore/test_client.py b/tests/unit/botocore/test_client.py +index f28b959..1b3d56b 100644 +--- a/tests/unit/botocore/test_client.py ++++ b/tests/unit/botocore/test_client.py +@@ -960,7 +960,7 @@ class TestAutoGeneratedClient(unittest.TestCase): + lines = [ + (' Creates an iterator that will paginate through responses ' + 'from :py:meth:`MyService.Client.test_operation`.'), +- ' **Request Syntax** ', ++ ' **Request Syntax**', + ' ::', + ' response_iterator = paginator.paginate(', + " Foo='string',", +@@ -976,17 +976,17 @@ class TestAutoGeneratedClient(unittest.TestCase): + ' :type Bar: string', + ' :param Bar: Documents Bar', + ' :type PaginationConfig: dict', +- ' :param PaginationConfig: ', ++ ' :param PaginationConfig:', + (' A dictionary that provides parameters to control ' + 'pagination.'), +- ' - **MaxItems** *(integer) --* ', ++ ' - **MaxItems** *(integer) --*', + (' The total number of items to return. If the total ' + 'number of items available is more than the value specified ' + 'in max-items then a ``NextToken`` will be provided in the ' + 'output that you can use to resume pagination.'), +- ' - **PageSize** *(integer) --* ', ++ ' - **PageSize** *(integer) --*', + ' The size of each page.', +- ' - **StartingToken** *(string) --* ', ++ ' - **StartingToken** *(string) --*', + (' A token to specify where to start paginating. This is ' + 'the ``NextToken`` from a previous response.'), + ' :returns: None', +diff --git a/tests/unit/botocore/test_credentials.py b/tests/unit/botocore/test_credentials.py +index c59f278..c50f593 100644 +--- a/tests/unit/botocore/test_credentials.py ++++ b/tests/unit/botocore/test_credentials.py +@@ -11,7 +11,7 @@ + # distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF + # ANY KIND, either express or implied. See the License for the specific + # language governing permissions and limitations under the License. +-from datetime import datetime, timedelta ++from datetime import datetime, timedelta, timezone + import subprocess + import mock + import os +@@ -111,7 +111,7 @@ class TestRefreshableCredentials(TestCredentials): + + def test_refresh_needed(self): + # The expiry time was set for 30 minutes ago, so if we +- # say the current time is utcnow(), then we should need ++ # say the current time is now(), then we should need + # a refresh. + self.mock_time.return_value = datetime.now(tzlocal()) + self.assertTrue(self.creds.refresh_needed()) +@@ -291,8 +291,8 @@ class TestAssumeRoleCredentialFetcher(BaseEnvVar): + self.assertEqual(response, expected_response) + + def test_retrieves_from_cache(self): +- date_in_future = datetime.utcnow() + timedelta(seconds=1000) +- utc_timestamp = date_in_future.isoformat() + 'Z' ++ date_in_future = datetime.now(timezone.utc) + timedelta(seconds=1000) ++ utc_timestamp = date_in_future.isoformat() + cache_key = ( + '793d6e2f27667ab2da104824407e486bfec24a47' + ) +@@ -703,8 +703,8 @@ class TestAssumeRoleWithWebIdentityCredentialFetcher(BaseEnvVar): + self.assertEqual(response, expected_response) + + def test_retrieves_from_cache(self): +- date_in_future = datetime.utcnow() + timedelta(seconds=1000) +- utc_timestamp = date_in_future.isoformat() + 'Z' ++ date_in_future = datetime.now(timezone.utc) + timedelta(seconds=1000) ++ utc_timestamp = date_in_future.isoformat() + cache_key = ( + '793d6e2f27667ab2da104824407e486bfec24a47' + ) +@@ -823,8 +823,8 @@ class TestAssumeRoleWithWebIdentityCredentialProvider(unittest.TestCase): + mock_loader_cls.assert_called_with('/some/path/token.jwt') + + def test_assume_role_retrieves_from_cache(self): +- date_in_future = datetime.utcnow() + timedelta(seconds=1000) +- utc_timestamp = date_in_future.isoformat() + 'Z' ++ date_in_future = datetime.now(timezone.utc) + timedelta(seconds=1000) ++ utc_timestamp = date_in_future.isoformat() + + cache_key = ( + 'c29461feeacfbed43017d20612606ff76abc073d' +@@ -1961,8 +1961,8 @@ class TestAssumeRoleCredentialProvider(unittest.TestCase): + self.assertEqual(expiry_time, '2016-11-06T01:30:00UTC') + + def test_assume_role_retrieves_from_cache(self): +- date_in_future = datetime.utcnow() + timedelta(seconds=1000) +- utc_timestamp = date_in_future.isoformat() + 'Z' ++ date_in_future = datetime.now(timezone.utc) + timedelta(seconds=1000) ++ utc_timestamp = date_in_future.isoformat() + self.fake_config['profiles']['development']['role_arn'] = 'myrole' + + cache_key = ( +@@ -1989,8 +1989,8 @@ class TestAssumeRoleCredentialProvider(unittest.TestCase): + self.assertEqual(creds.token, 'baz-cached') + + def test_chain_prefers_cache(self): +- date_in_future = datetime.utcnow() + timedelta(seconds=1000) +- utc_timestamp = date_in_future.isoformat() + 'Z' ++ date_in_future = datetime.now(timezone.utc) + timedelta(seconds=1000) ++ utc_timestamp = date_in_future.isoformat() + + # The profile we will be using has a cache entry, but the profile it + # is sourcing from does not. This should result in the cached +diff --git a/tests/unit/botocore/test_signers.py b/tests/unit/botocore/test_signers.py +index e06e0c1..507e2ea 100644 +--- a/tests/unit/botocore/test_signers.py ++++ b/tests/unit/botocore/test_signers.py +@@ -609,9 +609,9 @@ class TestS3PostPresigner(BaseSignerTest): + + self.datetime_patch = mock.patch('botocore.signers.datetime') + self.datetime_mock = self.datetime_patch.start() +- self.fixed_date = datetime.datetime(2014, 3, 10, 17, 2, 55, 0) ++ self.fixed_date = datetime.datetime(2014, 3, 10, 17, 2, 55, 0, tzinfo=datetime.timezone.utc) + self.fixed_delta = datetime.timedelta(seconds=3600) +- self.datetime_mock.datetime.utcnow.return_value = self.fixed_date ++ self.datetime_mock.datetime.now.return_value = self.fixed_date + self.datetime_mock.timedelta.return_value = self.fixed_delta + + def tearDown(self): +@@ -979,7 +979,7 @@ class TestGenerateDBAuthToken(BaseSignerTest): + clock = datetime.datetime(2016, 11, 7, 17, 39, 33, tzinfo=tzutc()) + + with mock.patch('datetime.datetime') as dt: +- dt.utcnow.return_value = clock ++ dt.now.return_value = clock + result = generate_db_auth_token( + self.client, hostname, port, username) + +diff --git a/tests/unit/botocore/test_utils.py b/tests/unit/botocore/test_utils.py +index 1f27e25..8116070 100644 +--- a/tests/unit/botocore/test_utils.py ++++ b/tests/unit/botocore/test_utils.py +@@ -98,7 +98,7 @@ from botocore.stub import Stubber + from botocore.config import Config + from botocore.endpoint_provider import RuleSetEndpoint + +-DATE = datetime.datetime(2021, 12, 10, 00, 00, 00) ++DATE = datetime.datetime(2021, 12, 10, 00, 00, 00, tzinfo=datetime.timezone.utc) + DT_FORMAT = "%Y-%m-%dT%H:%M:%SZ" + + +@@ -1004,7 +1004,7 @@ class TestSwitchToChunkedEncodingForNonSeekableObjects(unittest.TestCase): + def test_switch_to_chunked_encodeing_for_stream_like_object(self): + request = AWSRequest( + method='POST', headers={}, +- data=io.BufferedIOBase(b"some initial binary data"), ++ data=io.BufferedIOBase(), + url='https://foo.amazonaws.com/bucket/key.txt' + ) + prepared_request = request.prepare() +@@ -2819,7 +2819,7 @@ class TestInstanceMetadataFetcher(unittest.TestCase): + self, dt=None, offset=None, offset_func=operator.add + ): + if dt is None: +- dt = datetime.datetime.utcnow() ++ dt = datetime.datetime.now(datetime.timezone.utc) + if offset is not None: + dt = offset_func(dt, offset) + +diff --git a/tests/unit/botocore/test_waiters.py b/tests/unit/botocore/test_waiters.py +index 27fbe1f..b79e173 100644 +--- a/tests/unit/botocore/test_waiters.py ++++ b/tests/unit/botocore/test_waiters.py +@@ -648,7 +648,7 @@ class TestCreateWaiter(unittest.TestCase): + (' Polls :py:meth:`MyService.Client.foo` every 1 ' + 'seconds until a successful state is reached. An error ' + 'is returned after 1 failed checks.'), +- ' **Request Syntax** ', ++ ' **Request Syntax**', + ' ::', + ' waiter.wait(', + " bar='string'", +diff --git a/tests/unit/customizations/eks/test_get_token.py b/tests/unit/customizations/eks/test_get_token.py +index 17c07b0..0d1b7fc 100644 +--- a/tests/unit/customizations/eks/test_get_token.py ++++ b/tests/unit/customizations/eks/test_get_token.py +@@ -49,6 +49,6 @@ class TestGetTokenCommand(BaseTokenTest): + cmd = GetTokenCommand(self._session) + timestamp = cmd.get_expiration_time() + try: +- datetime.strptime(timestamp, '%Y-%m-%dT%H:%M:%SZ') ++ datetime.strptime(timestamp, '%Y-%m-%dT%H:%M:%S%z') + except ValueError: + raise ValueError("Incorrect data format, should be %Y-%m-%dT%H:%M:%SZ") +diff --git a/tests/unit/customizations/test_opsworks.py b/tests/unit/customizations/test_opsworks.py +index 1471ba9..f6816b9 100644 +--- a/tests/unit/customizations/test_opsworks.py ++++ b/tests/unit/customizations/test_opsworks.py +@@ -34,8 +34,8 @@ class TestOpsWorksBase(unittest.TestCase): + mock.Mock(wraps=datetime.datetime) + ) + mocked_datetime = self.datetime_patcher.start() +- mocked_datetime.utcnow.return_value = datetime.datetime( +- 2013, 8, 9, 23, 42) ++ mocked_datetime.now.return_value = datetime.datetime( ++ 2013, 8, 9, 23, 42, tzinfo=datetime.timezone.utc) + + def tearDown(self): + self.datetime_patcher.stop() +diff --git a/tests/utils/botocore/__init__.py b/tests/utils/botocore/__init__.py +index 9b8f205..abedac8 100644 +--- a/tests/utils/botocore/__init__.py ++++ b/tests/utils/botocore/__init__.py +@@ -560,12 +560,12 @@ class FreezeTime(contextlib.ContextDecorator): + :param module: reference to imported module to patch (e.g. botocore.auth.datetime) + + :type date: datetime.datetime +- :param date: datetime object specifying the output for utcnow() ++ :param date: datetime object specifying the output for now() + """ + + def __init__(self, module, date=None): + if date is None: +- date = datetime.datetime.utcnow() ++ date = datetime.datetime.now(datetime.timezone.utc) + self.date = date + self.datetime_patcher = mock.patch.object( + module, 'datetime', +@@ -574,7 +574,7 @@ class FreezeTime(contextlib.ContextDecorator): + + def __enter__(self, *args, **kwargs): + mock = self.datetime_patcher.start() +- mock.utcnow.return_value = self.date ++ mock.now.return_value = self.date + + def __exit__(self, *args, **kwargs): + self.datetime_patcher.stop() diff --git a/SOURCES/ruamel-yaml-0.17.32.patch b/SOURCES/ruamel-yaml-0.17.32.patch new file mode 100644 index 0000000..471dd9f --- /dev/null +++ b/SOURCES/ruamel-yaml-0.17.32.patch @@ -0,0 +1,95 @@ +diff --git a/awscli/customizations/cloudformation/yamlhelper.py b/awscli/customizations/cloudformation/yamlhelper.py +index abdc749..9cf9496 100644 +--- a/awscli/customizations/cloudformation/yamlhelper.py ++++ b/awscli/customizations/cloudformation/yamlhelper.py +@@ -92,8 +92,14 @@ def yaml_dump(dict_to_dump): + yaml.Representer = FlattenAliasRepresenter + _add_yaml_1_1_boolean_resolvers(yaml.Resolver) + yaml.Representer.add_representer(OrderedDict, _dict_representer) ++ yaml.Representer.add_representer(dict, _dict_representer) + +- return dump_yaml_to_str(yaml, dict_to_dump) ++ result = dump_yaml_to_str(yaml, dict_to_dump) ++ ++ # let other YAML instances use the default dict representer ++ yaml.Representer.add_representer(dict, ruamel.yaml.representer.SafeRepresenter.represent_dict) ++ ++ return result + + + def _dict_constructor(loader, node): +diff --git a/awscli/customizations/eks/kubeconfig.py b/awscli/customizations/eks/kubeconfig.py +index 5130f7f..64526a7 100644 +--- a/awscli/customizations/eks/kubeconfig.py ++++ b/awscli/customizations/eks/kubeconfig.py +@@ -44,7 +44,7 @@ def _get_new_kubeconfig_content(): + ("contexts", []), + ("current-context", ""), + ("kind", "Config"), +- ("preferences", OrderedDict()), ++ ("preferences", {}), + ("users", []) + ]) + +@@ -121,7 +121,7 @@ class KubeconfigValidator(object): + if (key in config.content and + type(config.content[key]) == list): + for element in config.content[key]: +- if not isinstance(element, OrderedDict): ++ if not isinstance(element, dict): + raise KubeconfigCorruptedError( + f"Entry in {key} not a {dict}. ") + +diff --git a/awscli/customizations/eks/ordered_yaml.py b/awscli/customizations/eks/ordered_yaml.py +index 23834e0..5c0f92a 100644 +--- a/awscli/customizations/eks/ordered_yaml.py ++++ b/awscli/customizations/eks/ordered_yaml.py +@@ -46,10 +46,18 @@ def ordered_yaml_dump(to_dump, stream=None): + :type stream: file + """ + yaml = ruamel.yaml.YAML(typ="safe", pure=True) ++ yaml.width = 99999 + yaml.default_flow_style = False + yaml.Representer.add_representer(OrderedDict, _ordered_representer) ++ yaml.Representer.add_representer(dict, _ordered_representer) + + if stream is None: +- return dump_yaml_to_str(yaml, to_dump) ++ result = dump_yaml_to_str(yaml, to_dump) ++ else: ++ result = None ++ yaml.dump(to_dump, stream) + +- yaml.dump(to_dump, stream) ++ # let other YAML instances use the default dict representer ++ yaml.Representer.add_representer(dict, ruamel.yaml.representer.SafeRepresenter.represent_dict) ++ ++ return result +diff --git a/tests/unit/customizations/cloudformation/test_yamlhelper.py b/tests/unit/customizations/cloudformation/test_yamlhelper.py +index 466ae2e..1adad4e 100644 +--- a/tests/unit/customizations/cloudformation/test_yamlhelper.py ++++ b/tests/unit/customizations/cloudformation/test_yamlhelper.py +@@ -139,10 +139,10 @@ class TestYaml(BaseYAMLTest): + ' Name: name1\n' + ) + output_dict = yaml_parse(input_template) +- expected_dict = OrderedDict([ +- ('B_Resource', OrderedDict([('Key2', {'Name': 'name2'}), ('Key1', {'Name': 'name1'})])), +- ('A_Resource', OrderedDict([('Key2', {'Name': 'name2'}), ('Key1', {'Name': 'name1'})])) +- ]) ++ expected_dict = { ++ 'B_Resource': {'Key2': {'Name': 'name2'}, 'Key1': {'Name': 'name1'}}, ++ 'A_Resource': {'Key2': {'Name': 'name2'}, 'Key1': {'Name': 'name1'}} ++ } + self.assertEqual(expected_dict, output_dict) + + output_template = yaml_dump(output_dict) +@@ -156,7 +156,7 @@ class TestYaml(BaseYAMLTest): + <<: *base + """ + output = yaml_parse(test_yaml) +- self.assertTrue(isinstance(output, OrderedDict)) ++ self.assertTrue(isinstance(output, dict)) + self.assertEqual(output.get('test').get('property'), 'value') + + def test_unroll_yaml_anchors(self): diff --git a/SPECS/awscli2.spec b/SPECS/awscli2.spec new file mode 100644 index 0000000..718eca8 --- /dev/null +++ b/SPECS/awscli2.spec @@ -0,0 +1,270 @@ +## START: Set by rpmautospec +## (rpmautospec version 0.6.1) +## RPMAUTOSPEC: autorelease, autochangelog +%define autorelease(e:s:pb:n) %{?-p:0.}%{lua: + release_number = 2; + base_release_number = tonumber(rpm.expand("%{?-b*}%{!?-b:1}")); + print(release_number + base_release_number - 1); +}%{?-e:.%{-e*}}%{?-s:.%{-s*}}%{!?-n:%{?dist}} +## END: Set by rpmautospec + +%global pkgname aws-cli + +Name: awscli2 +Version: 2.15.31 +Release: %autorelease + +Summary: Universal Command Line Environment for AWS, version 2 +# all files are licensed under Apache-2.0, except: +# - awscli/topictags.py is MIT +# - awscli/botocore/vendored/six.py is MIT +License: Apache-2.0 AND MIT +URL: https://github.com/aws/aws-cli/tree/v2 + +Source0: https://github.com/aws/aws-cli/archive/%{version}/%{pkgname}-%{version}.tar.gz + +# adapt to whitespace formatting changes and removal of OrderedDict in ruamel-yaml +Patch0: ruamel-yaml-0.17.32.patch +# fix Python 3.12 incompatibilities +Patch1: python312.patch +# fix incorrect assertions in TestKubeconfigLoader +Patch2: assertions.patch + +BuildArch: noarch + +BuildRequires: python%{python3_pkgversion}-devel +BuildRequires: python-unversioned-command +BuildRequires: procps-ng + +Recommends: groff + +Provides: bundled(python3dist(botocore)) = 2.0.0 +Provides: bundled(python3dist(s3transfer)) = 0.5.1 + +Provides: awscli = %{version}-%{release} +Obsoletes: awscli < 2 + +# provide an upgrade path from awscli-2 (Amazon Linux) +Provides: awscli-2 = %{version}-%{release} +Obsoletes: awscli-2 < %{version}-%{release} + +# python-awscrt does not build on s390x +ExcludeArch: s390x + + +%description +This package provides version 2 of the unified command line +interface to Amazon Web Services. + + +%prep +%autosetup -p1 -n %{pkgname}-%{version} + +# fix permissions +find awscli/examples/ -type f -name '*.rst' -executable -exec chmod -x '{}' + + +# remove version caps on dependencies +sed -i 's/,<=\?[^"]*"/"/' pyproject.toml + +# use unittest.mock +find -type f -name '*.py' -exec sed \ + -e 's/^\( *\)import mock$/\1from unittest import mock/' \ + -e 's/^\( *\)from mock import mock/\1from unittest import mock/' \ + -e 's/^\( *\)from mock import/\1from unittest.mock import/' \ + -i '{}' + + +# Fedora does not run coverage tests. +# mock is deprecated in Fedora. We use unittest.mock. +# pip-tools is not used directly by the unit tests. +# pytest-xdist is unwanted in RHEL. +sed \ + -e 's|==.*||' \ + -e '/coverage/d' \ + -e '/mock/d' \ + -e '/pip-tools/d' \ + -e '/pytest-cov/d' \ + %{?rhel:-e '/pytest-xdist/d'} \ + requirements-test.txt > _requirements-test.txt + + +%generate_buildrequires +%pyproject_buildrequires _requirements-test.txt + + +%build +%pyproject_wheel + + +%install +%pyproject_install +%pyproject_save_files awscli + +# remove unnecessary scripts +rm -vf %{buildroot}%{_bindir}/{aws_bash_completer,aws_zsh_completer.sh,aws.cmd} + +# install shell completion +install -Dpm0644 bin/aws_bash_completer \ + %{buildroot}%{bash_completions_dir}/aws +install -Dpm0644 bin/aws_zsh_completer.sh \ + %{buildroot}%{zsh_completions_dir}/_awscli + + +%check +# it appears that some tests modify the environment and remove PYTHONPATH +# so it's not passed to botocore cmd-runner, inject it here +sed -i '/self.driver.start(env=env)/i \ \ \ \ \ \ \ \ env["PYTHONPATH"] = "%{buildroot}%{python3_sitelib}"' \ + tests/utils/botocore/__init__.py + +export TESTS_REMOVE_REPO_ROOT_FROM_PATH=1 TZ=UTC +%if 0%{?rhel} +export OPENSSL_ENABLE_SHA1_SIGNATURES=yes +%endif +%pytest --verbose %{!?rhel:--numprocesses=auto --dist=loadfile --maxprocesses=4} tests/unit tests/functional + + +%files -f %{pyproject_files} +%license LICENSE.txt +%doc README.rst +%{_bindir}/aws +%{_bindir}/aws_completer +%{bash_completions_dir}/aws +%{zsh_completions_dir}/_awscli + + +%changelog +## START: Generated by rpmautospec +* Wed Jun 12 2024 Major Hayden - 2.15.31-2 +- Add gating.yaml + +* Mon Jun 10 2024 Major Hayden - 2.15.31-1 +- Update to 2.15.31 + +* Tue Jan 23 2024 Fedora Release Engineering - 2.15.10-3 +- Rebuilt for https://fedoraproject.org/wiki/Fedora_40_Mass_Rebuild + +* Fri Jan 19 2024 Fedora Release Engineering - 2.15.10-2 +- Rebuilt for https://fedoraproject.org/wiki/Fedora_40_Mass_Rebuild + +* Wed Jan 17 2024 Packit - 2.15.10-1 +- [packit] 2.15.10 upstream release +- Resolves rhbz#2255621 + +* Sat Dec 16 2023 Packit - 2.15.2-1 +- [packit] 2.15.2 upstream release +- Resolves rhbz#2254838 + +* Fri Dec 15 2023 Packit - 2.15.1-1 +- [packit] 2.15.1 upstream release +- Resolves rhbz#2251124 + +* Thu Dec 07 2023 Nikola Forró - 2.13.37-2 +- Fix Python 3.12 patch + +* Sat Nov 18 2023 Packit - 2.13.37-1 +- [packit] 2.13.37 upstream release +- Resolves rhbz#2250398 + +* Wed Nov 15 2023 Packit - 2.13.36-1 +- [packit] 2.13.36 upstream release +- Resolves rhbz#2247595 + +* Fri Oct 27 2023 Packit - 2.13.30-1 +- [packit] 2.13.30 upstream release +- Resolves rhbz#2203325 Upstream tag: 2.13.30 Upstream commit: 580ceb06 + +* Tue Oct 24 2023 Packit - 2.13.28-1 +- [packit] 2.13.28 upstream release + +* Tue Oct 24 2023 Nikola Forró - 2.13.26-4 +- Fix ruamel-yaml patch and improve Python version compatibility + +* Wed Oct 18 2023 Yaakov Selkowitz - 2.13.26-3 +- Skip flaky yaml output test + +* Mon Oct 16 2023 Miro Hrončok - 2.13.26-2 +- Don't use pytest-xdist in ELN + +* Sat Oct 14 2023 Packit - 2.13.26-1 +- [packit] 2.13.26 upstream release + +* Wed Oct 04 2023 Packit - 2.13.23-1 +- [packit] 2.13.23 upstream release + +* Sat Sep 09 2023 Packit - 2.13.17-1 +- [packit] 2.13.17 upstream release + +* Thu Aug 24 2023 Packit - 2.13.12-1 +- [packit] 2.13.12 upstream release + +* Tue Aug 22 2023 Packit - 2.13.11-1 +- [packit] 2.13.11 upstream release + +* Thu Aug 17 2023 Nikola Forró - 2.13.9-3 +- Fix Packit config + +* Mon Aug 14 2023 Nikola Forró - 2.13.9-2 +- Make pull-from-upstream react only to v2 tags + +* Fri Aug 11 2023 Packit - 2.13.9-1 +- [packit] 2.13.9 upstream release + +* Sat Aug 05 2023 Packit - 2.13.7-1 +- [packit] 2.13.7 upstream release + +* Fri Aug 04 2023 Packit - 2.13.6-1 +- [packit] 2.13.6 upstream release + +* Fri Jul 28 2023 Davide Cavalca - 2.13.4-2 +- Fix build on ELN + +* Thu Jul 27 2023 Packit - 2.13.4-1 +- [packit] 2.13.4 upstream release + +* Wed Jul 26 2023 Nikola Forró - 2.13.3-1 +- Update to 2.13.3 and fix FTBFS + +* Wed Jul 19 2023 Fedora Release Engineering - 2.12.0-3 +- Rebuilt for https://fedoraproject.org/wiki/Fedora_39_Mass_Rebuild + +* Thu Jun 15 2023 Python Maint - 2.12.0-2 +- Rebuilt for Python 3.12 + +* Thu Jun 15 2023 Packit - 2.12.0-1 +- [packit] 2.12.0 upstream release + +* Fri Jun 09 2023 Packit - 2.11.27-1 +- [packit] 2.11.27 upstream release + +* Thu Jun 08 2023 Packit - 2.11.26-1 +- [packit] 2.11.26 upstream release + +* Mon Jun 05 2023 Packit - 2.11.25-1 +- [packit] 2.11.25 upstream release + +* Thu Jun 01 2023 Packit - 2.11.24-1 +- [packit] 2.11.24 upstream release + +* Sat May 27 2023 Packit - 2.11.23-1 +- [packit] 2.11.23 upstream release + +* Fri May 26 2023 Packit - 2.11.22-1 +- [packit] 2.11.22 upstream release + +* Sat May 20 2023 Packit - 2.11.21-1 +- [packit] 2.11.21 upstream release + +* Sat May 13 2023 Packit - 2.11.20-1 +- [packit] 2.11.20 upstream release + +* Thu May 11 2023 Packit - 2.11.19-1 +- [packit] 2.11.19 upstream release + +* Wed May 10 2023 Nikola Forró - 2.11.18-1 +- Update to 2.11.18 and add missing Provides + +* Mon May 08 2023 Maxwell G - 2.11.17-4 +- fix typo in comment + +* Fri May 05 2023 Nikola Forró - 2.11.17-1 +- Initial package +## END: Generated by rpmautospec