Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
aws
GitHub Repository: aws/aws-cli
Path: blob/develop/awscli/paramfile.py
1566 views
1
# Copyright 2012-2013 Amazon.com, Inc. or its affiliates. All Rights Reserved.
2
#
3
# Licensed under the Apache License, Version 2.0 (the "License"). You
4
# may not use this file except in compliance with the License. A copy of
5
# the License is located at
6
#
7
# http://aws.amazon.com/apache2.0/
8
#
9
# or in the "license" file accompanying this file. This file is
10
# distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF
11
# ANY KIND, either express or implied. See the License for the specific
12
# language governing permissions and limitations under the License.
13
import copy
14
import logging
15
import os
16
17
from botocore.awsrequest import AWSRequest
18
from botocore.exceptions import ProfileNotFound
19
from botocore.httpsession import URLLib3Session
20
21
from awscli import argprocess
22
from awscli.compat import compat_open
23
24
logger = logging.getLogger(__name__)
25
26
# These are special cased arguments that do _not_ get the
27
# special param file processing. This is typically because it
28
# refers to an actual URI of some sort and we don't want to actually
29
# download the content (i.e TemplateURL in cloudformation).
30
PARAMFILE_DISABLED = set(
31
[
32
'api-gateway.put-integration.uri',
33
'api-gateway.create-integration.integration-uri',
34
'api-gateway.update-integration.integration-uri',
35
'api-gateway.create-api.target',
36
'api-gateway.update-api.target',
37
'appstream.create-stack.redirect-url',
38
'appstream.create-stack.feedback-url',
39
'appstream.update-stack.redirect-url',
40
'appstream.update-stack.feedback-url',
41
'cloudformation.create-stack.template-url',
42
'cloudformation.update-stack.template-url',
43
'cloudformation.create-stack-set.template-url',
44
'cloudformation.update-stack-set.template-url',
45
'cloudformation.create-change-set.template-url',
46
'cloudformation.validate-template.template-url',
47
'cloudformation.estimate-template-cost.template-url',
48
'cloudformation.get-template-summary.template-url',
49
'cloudformation.create-stack.stack-policy-url',
50
'cloudformation.update-stack.stack-policy-url',
51
'cloudformation.set-stack-policy.stack-policy-url',
52
# aws cloudformation package --template-file
53
'custom.package.template-file',
54
# aws cloudformation deploy --template-file
55
'custom.deploy.template-file',
56
'cloudformation.update-stack.stack-policy-during-update-url',
57
'cloudformation.register-type.schema-handler-package',
58
# We will want to change the event name to ``s3`` as opposed to
59
# custom in the near future along with ``s3`` to ``s3api``.
60
'custom.cp.website-redirect',
61
'custom.mv.website-redirect',
62
'custom.sync.website-redirect',
63
'guardduty.create-ip-set.location',
64
'guardduty.update-ip-set.location',
65
'guardduty.create-threat-intel-set.location',
66
'guardduty.update-threat-intel-set.location',
67
'comprehend.detect-dominant-language.text',
68
'comprehend.batch-detect-dominant-language.text-list',
69
'comprehend.detect-entities.text',
70
'comprehend.batch-detect-entities.text-list',
71
'comprehend.detect-key-phrases.text',
72
'comprehend.batch-detect-key-phrases.text-list',
73
'comprehend.detect-sentiment.text',
74
'comprehend.batch-detect-sentiment.text-list',
75
'emr.create-studio.idp-auth-url',
76
'iam.create-open-id-connect-provider.url',
77
'machine-learning.predict.predict-endpoint',
78
'mediatailor.put-playback-configuration.ad-decision-server-url',
79
'mediatailor.put-playback-configuration.slate-ad-url',
80
'mediatailor.put-playback-configuration.video-content-source-url',
81
'rds.copy-db-cluster-snapshot.pre-signed-url',
82
'rds.create-db-cluster.pre-signed-url',
83
'rds.copy-db-snapshot.pre-signed-url',
84
'rds.create-db-instance-read-replica.pre-signed-url',
85
'sagemaker.create-notebook-instance.default-code-repository',
86
'sagemaker.create-notebook-instance.additional-code-repositories',
87
'sagemaker.update-notebook-instance.default-code-repository',
88
'sagemaker.update-notebook-instance.additional-code-repositories',
89
'serverlessapplicationrepository.create-application.home-page-url',
90
'serverlessapplicationrepository.create-application.license-url',
91
'serverlessapplicationrepository.create-application.readme-url',
92
'serverlessapplicationrepository.create-application.source-code-url',
93
'serverlessapplicationrepository.create-application.template-url',
94
'serverlessapplicationrepository.create-application-version.source-code-url',
95
'serverlessapplicationrepository.create-application-version.template-url',
96
'serverlessapplicationrepository.update-application.home-page-url',
97
'serverlessapplicationrepository.update-application.readme-url',
98
'service-catalog.create-product.support-url',
99
'service-catalog.update-product.support-url',
100
'ses.create-custom-verification-email-template.failure-redirection-url',
101
'ses.create-custom-verification-email-template.success-redirection-url',
102
'ses.put-account-details.website-url',
103
'ses.update-custom-verification-email-template.failure-redirection-url',
104
'ses.update-custom-verification-email-template.success-redirection-url',
105
'sqs.add-permission.queue-url',
106
'sqs.change-message-visibility.queue-url',
107
'sqs.change-message-visibility-batch.queue-url',
108
'sqs.delete-message.queue-url',
109
'sqs.delete-message-batch.queue-url',
110
'sqs.delete-queue.queue-url',
111
'sqs.get-queue-attributes.queue-url',
112
'sqs.list-dead-letter-source-queues.queue-url',
113
'sqs.receive-message.queue-url',
114
'sqs.remove-permission.queue-url',
115
'sqs.send-message.queue-url',
116
'sqs.send-message-batch.queue-url',
117
'sqs.set-queue-attributes.queue-url',
118
'sqs.purge-queue.queue-url',
119
'sqs.list-queue-tags.queue-url',
120
'sqs.tag-queue.queue-url',
121
'sqs.untag-queue.queue-url',
122
's3.copy-object.website-redirect-location',
123
's3.create-multipart-upload.website-redirect-location',
124
's3.put-object.website-redirect-location',
125
# Double check that this has been renamed!
126
'sns.subscribe.notification-endpoint',
127
'iot.create-job.document-source',
128
'translate.translate-text.text',
129
'workdocs.create-notification-subscription.notification-endpoint',
130
]
131
)
132
133
134
class ResourceLoadingError(Exception):
135
pass
136
137
138
def register_uri_param_handler(session, **kwargs):
139
prefix_map = copy.deepcopy(LOCAL_PREFIX_MAP)
140
try:
141
fetch_url = (
142
session.get_scoped_config().get('cli_follow_urlparam', 'true')
143
== 'true'
144
)
145
except ProfileNotFound:
146
# If a --profile is provided that does not exist, loading
147
# a value from get_scoped_config will crash the CLI.
148
# This function can be called as the first handler for
149
# the session-initialized event, which happens before a
150
# profile can be created, even if the command would have
151
# successfully created a profile. Instead of crashing here
152
# on a ProfileNotFound the CLI should just use 'none'.
153
fetch_url = True
154
155
if fetch_url:
156
prefix_map.update(REMOTE_PREFIX_MAP)
157
158
handler = URIArgumentHandler(prefix_map)
159
session.register('load-cli-arg', handler)
160
161
162
class URIArgumentHandler:
163
def __init__(self, prefixes=None):
164
if prefixes is None:
165
prefixes = copy.deepcopy(LOCAL_PREFIX_MAP)
166
prefixes.update(REMOTE_PREFIX_MAP)
167
self._prefixes = prefixes
168
169
def __call__(self, event_name, param, value, **kwargs):
170
"""Handler that supports param values from URIs."""
171
cli_argument = param
172
qualified_param_name = '.'.join(event_name.split('.')[1:])
173
if qualified_param_name in PARAMFILE_DISABLED or getattr(
174
cli_argument, 'no_paramfile', None
175
):
176
return
177
else:
178
return self._check_for_uri_param(cli_argument, value)
179
180
def _check_for_uri_param(self, param, value):
181
if isinstance(value, list) and len(value) == 1:
182
value = value[0]
183
try:
184
return get_paramfile(value, self._prefixes)
185
except ResourceLoadingError as e:
186
raise argprocess.ParamError(param.cli_name, str(e))
187
188
189
def get_paramfile(path, cases):
190
"""Load parameter based on a resource URI.
191
192
It is possible to pass parameters to operations by referring
193
to files or URI's. If such a reference is detected, this
194
function attempts to retrieve the data from the file or URI
195
and returns it. If there are any errors or if the ``path``
196
does not appear to refer to a file or URI, a ``None`` is
197
returned.
198
199
:type path: str
200
:param path: The resource URI, e.g. file://foo.txt. This value
201
may also be a non resource URI, in which case ``None`` is returned.
202
203
:type cases: dict
204
:param cases: A dictionary of URI prefixes to function mappings
205
that a parameter is checked against.
206
207
:return: The loaded value associated with the resource URI.
208
If the provided ``path`` is not a resource URI, then a
209
value of ``None`` is returned.
210
211
"""
212
data = None
213
if isinstance(path, str):
214
for prefix, function_spec in cases.items():
215
if path.startswith(prefix):
216
function, kwargs = function_spec
217
data = function(prefix, path, **kwargs)
218
return data
219
220
221
def get_file(prefix, path, mode):
222
file_path = os.path.expandvars(os.path.expanduser(path[len(prefix) :]))
223
try:
224
with compat_open(file_path, mode) as f:
225
return f.read()
226
except UnicodeDecodeError:
227
raise ResourceLoadingError(
228
'Unable to load paramfile (%s), text contents could '
229
'not be decoded. If this is a binary file, please use the '
230
'fileb:// prefix instead of the file:// prefix.' % file_path
231
)
232
except OSError as e:
233
raise ResourceLoadingError(
234
f'Unable to load paramfile {path}: {e}'
235
)
236
237
238
def get_uri(prefix, uri):
239
try:
240
session = URLLib3Session()
241
r = session.send(AWSRequest('GET', uri).prepare())
242
if r.status_code == 200:
243
return r.text
244
else:
245
raise ResourceLoadingError(
246
f"received non 200 status code of {r.status_code}"
247
)
248
except Exception as e:
249
raise ResourceLoadingError('Unable to retrieve %s: %s' % (uri, e))
250
251
252
LOCAL_PREFIX_MAP = {
253
'file://': (get_file, {'mode': 'r'}),
254
'fileb://': (get_file, {'mode': 'rb'}),
255
}
256
257
258
REMOTE_PREFIX_MAP = {
259
'http://': (get_uri, {}),
260
'https://': (get_uri, {}),
261
}
262
263