swift 2.32.1__py2.py3-none-any.whl → 2.33.1__py2.py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- swift/account/server.py +1 -11
- swift/cli/info.py +28 -1
- swift-2.32.1.data/scripts/swift-recon-cron → swift/cli/recon_cron.py +4 -13
- swift/cli/reload.py +141 -0
- swift/common/daemon.py +12 -2
- swift/common/db.py +12 -8
- swift/common/http_protocol.py +76 -3
- swift/common/manager.py +18 -5
- swift/common/memcached.py +18 -12
- swift/common/middleware/proxy_logging.py +35 -27
- swift/common/middleware/s3api/acl_handlers.py +1 -1
- swift/common/middleware/s3api/controllers/__init__.py +3 -0
- swift/common/middleware/s3api/controllers/acl.py +3 -2
- swift/common/middleware/s3api/controllers/logging.py +2 -2
- swift/common/middleware/s3api/controllers/multi_upload.py +30 -6
- swift/common/middleware/s3api/controllers/object_lock.py +44 -0
- swift/common/middleware/s3api/s3api.py +4 -0
- swift/common/middleware/s3api/s3request.py +19 -12
- swift/common/middleware/s3api/s3response.py +13 -2
- swift/common/middleware/s3api/utils.py +1 -1
- swift/common/middleware/slo.py +395 -298
- swift/common/middleware/staticweb.py +45 -14
- swift/common/middleware/tempurl.py +132 -91
- swift/common/request_helpers.py +32 -8
- swift/common/storage_policy.py +1 -1
- swift/common/swob.py +5 -2
- swift/common/utils/__init__.py +230 -135
- swift/common/utils/timestamp.py +23 -2
- swift/common/wsgi.py +8 -0
- swift/container/backend.py +126 -21
- swift/container/replicator.py +42 -6
- swift/container/server.py +264 -145
- swift/container/sharder.py +50 -30
- swift/container/updater.py +1 -0
- swift/obj/auditor.py +2 -1
- swift/obj/diskfile.py +55 -19
- swift/obj/expirer.py +1 -13
- swift/obj/mem_diskfile.py +2 -1
- swift/obj/mem_server.py +1 -0
- swift/obj/replicator.py +2 -2
- swift/obj/server.py +12 -23
- swift/obj/updater.py +1 -0
- swift/obj/watchers/dark_data.py +72 -34
- swift/proxy/controllers/account.py +3 -2
- swift/proxy/controllers/base.py +217 -127
- swift/proxy/controllers/container.py +274 -289
- swift/proxy/controllers/obj.py +98 -141
- swift/proxy/server.py +2 -12
- {swift-2.32.1.data → swift-2.33.1.data}/scripts/swift-container-info +3 -0
- swift-2.33.1.data/scripts/swift-recon-cron +24 -0
- {swift-2.32.1.dist-info → swift-2.33.1.dist-info}/AUTHORS +3 -1
- {swift-2.32.1.dist-info → swift-2.33.1.dist-info}/METADATA +4 -3
- {swift-2.32.1.dist-info → swift-2.33.1.dist-info}/RECORD +94 -91
- {swift-2.32.1.dist-info → swift-2.33.1.dist-info}/WHEEL +1 -1
- {swift-2.32.1.dist-info → swift-2.33.1.dist-info}/entry_points.txt +1 -0
- swift-2.33.1.dist-info/pbr.json +1 -0
- swift-2.32.1.dist-info/pbr.json +0 -1
- {swift-2.32.1.data → swift-2.33.1.data}/scripts/swift-account-audit +0 -0
- {swift-2.32.1.data → swift-2.33.1.data}/scripts/swift-account-auditor +0 -0
- {swift-2.32.1.data → swift-2.33.1.data}/scripts/swift-account-info +0 -0
- {swift-2.32.1.data → swift-2.33.1.data}/scripts/swift-account-reaper +0 -0
- {swift-2.32.1.data → swift-2.33.1.data}/scripts/swift-account-replicator +0 -0
- {swift-2.32.1.data → swift-2.33.1.data}/scripts/swift-account-server +0 -0
- {swift-2.32.1.data → swift-2.33.1.data}/scripts/swift-config +0 -0
- {swift-2.32.1.data → swift-2.33.1.data}/scripts/swift-container-auditor +0 -0
- {swift-2.32.1.data → swift-2.33.1.data}/scripts/swift-container-reconciler +0 -0
- {swift-2.32.1.data → swift-2.33.1.data}/scripts/swift-container-replicator +0 -0
- {swift-2.32.1.data → swift-2.33.1.data}/scripts/swift-container-server +0 -0
- {swift-2.32.1.data → swift-2.33.1.data}/scripts/swift-container-sharder +0 -0
- {swift-2.32.1.data → swift-2.33.1.data}/scripts/swift-container-sync +0 -0
- {swift-2.32.1.data → swift-2.33.1.data}/scripts/swift-container-updater +0 -0
- {swift-2.32.1.data → swift-2.33.1.data}/scripts/swift-dispersion-populate +0 -0
- {swift-2.32.1.data → swift-2.33.1.data}/scripts/swift-dispersion-report +0 -0
- {swift-2.32.1.data → swift-2.33.1.data}/scripts/swift-drive-audit +0 -0
- {swift-2.32.1.data → swift-2.33.1.data}/scripts/swift-form-signature +0 -0
- {swift-2.32.1.data → swift-2.33.1.data}/scripts/swift-get-nodes +0 -0
- {swift-2.32.1.data → swift-2.33.1.data}/scripts/swift-init +0 -0
- {swift-2.32.1.data → swift-2.33.1.data}/scripts/swift-object-auditor +0 -0
- {swift-2.32.1.data → swift-2.33.1.data}/scripts/swift-object-expirer +0 -0
- {swift-2.32.1.data → swift-2.33.1.data}/scripts/swift-object-info +0 -0
- {swift-2.32.1.data → swift-2.33.1.data}/scripts/swift-object-reconstructor +0 -0
- {swift-2.32.1.data → swift-2.33.1.data}/scripts/swift-object-relinker +0 -0
- {swift-2.32.1.data → swift-2.33.1.data}/scripts/swift-object-replicator +0 -0
- {swift-2.32.1.data → swift-2.33.1.data}/scripts/swift-object-server +0 -0
- {swift-2.32.1.data → swift-2.33.1.data}/scripts/swift-object-updater +0 -0
- {swift-2.32.1.data → swift-2.33.1.data}/scripts/swift-oldies +0 -0
- {swift-2.32.1.data → swift-2.33.1.data}/scripts/swift-orphans +0 -0
- {swift-2.32.1.data → swift-2.33.1.data}/scripts/swift-proxy-server +0 -0
- {swift-2.32.1.data → swift-2.33.1.data}/scripts/swift-recon +0 -0
- {swift-2.32.1.data → swift-2.33.1.data}/scripts/swift-reconciler-enqueue +0 -0
- {swift-2.32.1.data → swift-2.33.1.data}/scripts/swift-ring-builder +0 -0
- {swift-2.32.1.data → swift-2.33.1.data}/scripts/swift-ring-builder-analyzer +0 -0
- {swift-2.32.1.data → swift-2.33.1.data}/scripts/swift-ring-composer +0 -0
- {swift-2.32.1.dist-info → swift-2.33.1.dist-info}/LICENSE +0 -0
- {swift-2.32.1.dist-info → swift-2.33.1.dist-info}/top_level.txt +0 -0
@@ -27,19 +27,28 @@ The logging format implemented below is as follows::
|
|
27
27
|
start_time end_time policy_index
|
28
28
|
|
29
29
|
These values are space-separated, and each is url-encoded, so that they can
|
30
|
-
be separated with a simple
|
31
|
-
|
32
|
-
* remote_addr is the contents of the REMOTE_ADDR environment variable,
|
33
|
-
client_ip is swift's best guess at the end-user IP, extracted
|
34
|
-
from the X-Forwarded-For header, X-Cluster-Ip header, or the
|
35
|
-
environment variable.
|
36
|
-
|
37
|
-
*
|
30
|
+
be separated with a simple ``.split()``.
|
31
|
+
|
32
|
+
* ``remote_addr`` is the contents of the REMOTE_ADDR environment variable,
|
33
|
+
while ``client_ip`` is swift's best guess at the end-user IP, extracted
|
34
|
+
variously from the X-Forwarded-For header, X-Cluster-Ip header, or the
|
35
|
+
REMOTE_ADDR environment variable.
|
36
|
+
|
37
|
+
* ``status_int`` is the integer part of the ``status`` string passed to this
|
38
|
+
middleware's start_response function, unless the WSGI environment has an item
|
39
|
+
with key ``swift.proxy_logging_status``, in which case the value of that item
|
40
|
+
is used. Other middleware's may set ``swift.proxy_logging_status`` to
|
41
|
+
override the logging of ``status_int``. In either case, the logged
|
42
|
+
``status_int`` value is forced to 499 if a client disconnect is detected
|
43
|
+
while this middleware is handling a request, or 500 if an exception is caught
|
44
|
+
while handling a request.
|
45
|
+
|
46
|
+
* ``source`` (``swift.source`` in the WSGI environment) indicates the code
|
38
47
|
that generated the request, such as most middleware. (See below for
|
39
48
|
more detail.)
|
40
49
|
|
41
|
-
* log_info (swift.log_info in the WSGI environment) is for additional
|
42
|
-
information that could prove quite useful, such as any x-delete-at
|
50
|
+
* ``log_info`` (``swift.log_info`` in the WSGI environment) is for additional
|
51
|
+
information that could prove quite useful, such as any ``x-delete-at``
|
43
52
|
value or other "behind the scenes" activity that might not
|
44
53
|
otherwise be detectable from the plain log information. Code that
|
45
54
|
wishes to add additional log information should use code like
|
@@ -62,18 +71,18 @@ For example, with staticweb, the middleware might intercept a request to
|
|
62
71
|
/v1/AUTH_acc/cont/, make a subrequest to the proxy to retrieve
|
63
72
|
/v1/AUTH_acc/cont/index.html and, in effect, respond to the client's original
|
64
73
|
request using the 2nd request's body. In this instance the subrequest will be
|
65
|
-
logged by the rightmost middleware (with a swift.source set) and the
|
66
|
-
request (with body overridden) will be logged by leftmost middleware.
|
74
|
+
logged by the rightmost middleware (with a ``swift.source`` set) and the
|
75
|
+
outgoing request (with body overridden) will be logged by leftmost middleware.
|
67
76
|
|
68
77
|
Requests that follow the normal pipeline (use the same wsgi environment
|
69
78
|
throughout) will not be double logged because an environment variable
|
70
|
-
(swift.proxy_access_log_made) is checked/set when a log is made.
|
79
|
+
(``swift.proxy_access_log_made``) is checked/set when a log is made.
|
71
80
|
|
72
|
-
All middleware making subrequests should take care to set swift.source when
|
81
|
+
All middleware making subrequests should take care to set ``swift.source`` when
|
73
82
|
needed. With the doubled proxy logs, any consumer/processor of swift's proxy
|
74
|
-
logs should look at the swift.source field, the rightmost log value, to
|
75
|
-
if this is a middleware subrequest or not. A log processor calculating
|
76
|
-
bandwidth usage will want to only sum up logs with no swift.source
|
83
|
+
logs should look at the ``swift.source`` field, the rightmost log value, to
|
84
|
+
decide if this is a middleware subrequest or not. A log processor calculating
|
85
|
+
bandwidth usage will want to only sum up logs with no ``swift.source``.
|
77
86
|
"""
|
78
87
|
|
79
88
|
import os
|
@@ -389,11 +398,11 @@ class ProxyLoggingMiddleware(object):
|
|
389
398
|
def my_start_response(status, headers, exc_info=None):
|
390
399
|
start_response_args[0] = (status, list(headers), exc_info)
|
391
400
|
|
392
|
-
def status_int_for_logging(
|
401
|
+
def status_int_for_logging():
|
393
402
|
# log disconnected clients as '499' status code
|
394
|
-
if
|
403
|
+
if input_proxy.client_disconnect:
|
395
404
|
return 499
|
396
|
-
return
|
405
|
+
return env.get('swift.proxy_logging_status')
|
397
406
|
|
398
407
|
def iter_response(iterable):
|
399
408
|
iterator = reiterate(iterable)
|
@@ -438,21 +447,19 @@ class ProxyLoggingMiddleware(object):
|
|
438
447
|
metric_name_policy + '.first-byte.timing', ttfb * 1000)
|
439
448
|
|
440
449
|
bytes_sent = 0
|
441
|
-
client_disconnect = False
|
442
|
-
start_status = wire_status_int
|
443
450
|
try:
|
444
451
|
for chunk in iterator:
|
445
452
|
bytes_sent += len(chunk)
|
446
453
|
yield chunk
|
447
454
|
except GeneratorExit: # generator was closed before we finished
|
448
|
-
|
455
|
+
env['swift.proxy_logging_status'] = 499
|
449
456
|
raise
|
450
457
|
except Exception:
|
451
|
-
|
458
|
+
env['swift.proxy_logging_status'] = 500
|
452
459
|
raise
|
453
460
|
finally:
|
454
|
-
|
455
|
-
|
461
|
+
env.setdefault('swift.proxy_logging_status', wire_status_int)
|
462
|
+
status_int = status_int_for_logging()
|
456
463
|
self.log_request(
|
457
464
|
req, status_int, input_proxy.bytes_received, bytes_sent,
|
458
465
|
start_time, time.time(), resp_headers=resp_headers,
|
@@ -463,7 +470,8 @@ class ProxyLoggingMiddleware(object):
|
|
463
470
|
iterable = self.app(env, my_start_response)
|
464
471
|
except Exception:
|
465
472
|
req = Request(env)
|
466
|
-
|
473
|
+
env['swift.proxy_logging_status'] = 500
|
474
|
+
status_int = status_int_for_logging()
|
467
475
|
self.log_request(
|
468
476
|
req, status_int, input_proxy.bytes_received, 0, start_time,
|
469
477
|
time.time())
|
@@ -168,7 +168,7 @@ class BaseAclHandler(object):
|
|
168
168
|
elem = fromstring(body, ACL.root_tag)
|
169
169
|
acl = ACL.from_elem(
|
170
170
|
elem, True, self.req.conf.allow_no_owner)
|
171
|
-
except(XMLSyntaxError, DocumentInvalid):
|
171
|
+
except (XMLSyntaxError, DocumentInvalid):
|
172
172
|
raise MalformedACLError()
|
173
173
|
except Exception as e:
|
174
174
|
self.logger.error(e)
|
@@ -33,6 +33,8 @@ from swift.common.middleware.s3api.controllers.versioning import \
|
|
33
33
|
VersioningController
|
34
34
|
from swift.common.middleware.s3api.controllers.tagging import \
|
35
35
|
TaggingController
|
36
|
+
from swift.common.middleware.s3api.controllers.object_lock import \
|
37
|
+
ObjectLockController
|
36
38
|
|
37
39
|
__all__ = [
|
38
40
|
'Controller',
|
@@ -50,6 +52,7 @@ __all__ = [
|
|
50
52
|
'LoggingStatusController',
|
51
53
|
'VersioningController',
|
52
54
|
'TaggingController',
|
55
|
+
'ObjectLockController',
|
53
56
|
|
54
57
|
'UnsupportedController',
|
55
58
|
]
|
@@ -19,8 +19,9 @@ from swift.common.utils import public
|
|
19
19
|
|
20
20
|
from swift.common.middleware.s3api.exception import ACLError
|
21
21
|
from swift.common.middleware.s3api.controllers.base import Controller
|
22
|
-
from swift.common.middleware.s3api.s3response import
|
23
|
-
MalformedACLError, UnexpectedContent,
|
22
|
+
from swift.common.middleware.s3api.s3response import (
|
23
|
+
HTTPOk, S3NotImplemented, MalformedACLError, UnexpectedContent,
|
24
|
+
MissingSecurityHeader)
|
24
25
|
from swift.common.middleware.s3api.etree import Element, SubElement, tostring
|
25
26
|
from swift.common.middleware.s3api.acl_utils import swift_acl_translate, \
|
26
27
|
XMLNS_XSI
|
@@ -18,8 +18,8 @@ from swift.common.utils import public
|
|
18
18
|
from swift.common.middleware.s3api.controllers.base import Controller, \
|
19
19
|
bucket_operation
|
20
20
|
from swift.common.middleware.s3api.etree import Element, tostring
|
21
|
-
from swift.common.middleware.s3api.s3response import
|
22
|
-
NoLoggingStatusForKey
|
21
|
+
from swift.common.middleware.s3api.s3response import (
|
22
|
+
HTTPOk, S3NotImplemented, NoLoggingStatusForKey)
|
23
23
|
|
24
24
|
|
25
25
|
class LoggingStatusController(Controller):
|
@@ -68,8 +68,9 @@ import time
|
|
68
68
|
import six
|
69
69
|
|
70
70
|
from swift.common import constraints
|
71
|
-
from swift.common.swob import Range, bytes_to_wsgi, normalize_etag,
|
72
|
-
|
71
|
+
from swift.common.swob import Range, bytes_to_wsgi, normalize_etag, \
|
72
|
+
wsgi_to_str
|
73
|
+
from swift.common.utils import json, public, reiterate, md5, Timestamp
|
73
74
|
from swift.common.db import utf8encode
|
74
75
|
from swift.common.request_helpers import get_container_update_override_key, \
|
75
76
|
get_param
|
@@ -82,7 +83,7 @@ from swift.common.middleware.s3api.s3response import InvalidArgument, \
|
|
82
83
|
ErrorResponse, MalformedXML, BadDigest, KeyTooLongError, \
|
83
84
|
InvalidPart, BucketAlreadyExists, EntityTooSmall, InvalidPartOrder, \
|
84
85
|
InvalidRequest, HTTPOk, HTTPNoContent, NoSuchKey, NoSuchUpload, \
|
85
|
-
NoSuchBucket, BucketAlreadyOwnedByYou
|
86
|
+
NoSuchBucket, BucketAlreadyOwnedByYou, ServiceUnavailable
|
86
87
|
from swift.common.middleware.s3api.utils import unique_id, \
|
87
88
|
MULTIUPLOAD_SUFFIX, S3Timestamp, sysmeta_header
|
88
89
|
from swift.common.middleware.s3api.etree import Element, SubElement, \
|
@@ -96,6 +97,17 @@ MAX_COMPLETE_UPLOAD_BODY_SIZE = 2048 * 1024
|
|
96
97
|
|
97
98
|
|
98
99
|
def _get_upload_info(req, app, upload_id):
|
100
|
+
"""
|
101
|
+
Make a HEAD request for existing upload object metadata. Tries the upload
|
102
|
+
marker first, and then falls back to the manifest object.
|
103
|
+
|
104
|
+
:param req: an S3Request object.
|
105
|
+
:param app: the wsgi app.
|
106
|
+
:param upload_id: the upload id.
|
107
|
+
:returns: a tuple of (S3Response, boolean) where the boolean is True if the
|
108
|
+
response is from the upload marker and False otherwise.
|
109
|
+
:raises: NoSuchUpload if neither the marker nor the manifest were found.
|
110
|
+
"""
|
99
111
|
|
100
112
|
container = req.container_name + MULTIUPLOAD_SUFFIX
|
101
113
|
obj = '%s/%s' % (req.object_name, upload_id)
|
@@ -106,14 +118,17 @@ def _get_upload_info(req, app, upload_id):
|
|
106
118
|
# it off for now...
|
107
119
|
copy_source = req.headers.pop('X-Amz-Copy-Source', None)
|
108
120
|
try:
|
109
|
-
|
121
|
+
resp = req.get_response(app, 'HEAD', container=container, obj=obj)
|
122
|
+
return resp, True
|
110
123
|
except NoSuchKey:
|
124
|
+
# ensure consistent path and policy are logged despite manifest HEAD
|
111
125
|
upload_marker_path = req.environ.get('s3api.backend_path')
|
126
|
+
policy_index = req.policy_index
|
112
127
|
try:
|
113
128
|
resp = req.get_response(app, 'HEAD')
|
114
129
|
if resp.sysmeta_headers.get(sysmeta_header(
|
115
130
|
'object', 'upload-id')) == upload_id:
|
116
|
-
return resp
|
131
|
+
return resp, False
|
117
132
|
except NoSuchKey:
|
118
133
|
pass
|
119
134
|
finally:
|
@@ -121,6 +136,8 @@ def _get_upload_info(req, app, upload_id):
|
|
121
136
|
# path, so put it back
|
122
137
|
if upload_marker_path is not None:
|
123
138
|
req.environ['s3api.backend_path'] = upload_marker_path
|
139
|
+
if policy_index is not None:
|
140
|
+
req.policy_index = policy_index
|
124
141
|
raise NoSuchUpload(upload_id=upload_id)
|
125
142
|
finally:
|
126
143
|
# ...making sure to restore any copy-source before returning
|
@@ -651,7 +668,14 @@ class UploadController(Controller):
|
|
651
668
|
Handles Complete Multipart Upload.
|
652
669
|
"""
|
653
670
|
upload_id = get_param(req, 'uploadId')
|
654
|
-
resp = _get_upload_info(req, self.app, upload_id)
|
671
|
+
resp, is_marker = _get_upload_info(req, self.app, upload_id)
|
672
|
+
if (is_marker and
|
673
|
+
resp.sw_headers.get('X-Backend-Timestamp') >= Timestamp.now()):
|
674
|
+
# Somehow the marker was created in the future w.r.t. this thread's
|
675
|
+
# clock. The manifest PUT may succeed but the subsequent marker
|
676
|
+
# DELETE will fail, so don't attempt either.
|
677
|
+
raise ServiceUnavailable
|
678
|
+
|
655
679
|
headers = {'Accept': 'application/json',
|
656
680
|
sysmeta_header('object', 'upload-id'): upload_id}
|
657
681
|
for key, val in resp.headers.items():
|
@@ -0,0 +1,44 @@
|
|
1
|
+
# Copyright (c) 2010-2023 OpenStack Foundation
|
2
|
+
#
|
3
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
4
|
+
# you may not use this file except in compliance with the License.
|
5
|
+
# You may obtain a copy of the License at
|
6
|
+
#
|
7
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
8
|
+
#
|
9
|
+
# Unless required by applicable law or agreed to in writing, software
|
10
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
11
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
12
|
+
# implied.
|
13
|
+
# See the License for the specific language governing permissions and
|
14
|
+
# limitations under the License.
|
15
|
+
|
16
|
+
from swift.common.utils import public
|
17
|
+
|
18
|
+
from swift.common.middleware.s3api.controllers.base import Controller, \
|
19
|
+
bucket_operation, S3NotImplemented
|
20
|
+
from swift.common.middleware.s3api.s3response import \
|
21
|
+
ObjectLockConfigurationNotFoundError
|
22
|
+
|
23
|
+
|
24
|
+
class ObjectLockController(Controller):
|
25
|
+
"""
|
26
|
+
Handles GET object-lock request, which always returns
|
27
|
+
<ObjectLockEnabled>Disabled</ObjectLockEnabled>
|
28
|
+
"""
|
29
|
+
@public
|
30
|
+
@bucket_operation
|
31
|
+
def GET(self, req):
|
32
|
+
"""
|
33
|
+
Handles GET object-lock param calls.
|
34
|
+
"""
|
35
|
+
raise ObjectLockConfigurationNotFoundError(req.container_name)
|
36
|
+
|
37
|
+
@public
|
38
|
+
@bucket_operation
|
39
|
+
def PUT(self, req):
|
40
|
+
"""
|
41
|
+
Handles PUT object-lock param calls.
|
42
|
+
"""
|
43
|
+
# Basically we don't support it, so return a 501
|
44
|
+
raise S3NotImplemented('The requested resource is not implemented')
|
@@ -366,6 +366,7 @@ class S3ApiMiddleware(object):
|
|
366
366
|
|
367
367
|
if 's3api.backend_path' in env and 'swift.backend_path' not in env:
|
368
368
|
env['swift.backend_path'] = env['s3api.backend_path']
|
369
|
+
|
369
370
|
return resp(env, start_response)
|
370
371
|
|
371
372
|
def handle_request(self, req):
|
@@ -390,6 +391,9 @@ class S3ApiMiddleware(object):
|
|
390
391
|
raise MethodNotAllowed(req.method,
|
391
392
|
req.controller.resource_type())
|
392
393
|
|
394
|
+
if req.policy_index is not None:
|
395
|
+
res.headers.setdefault('X-Backend-Storage-Policy-Index',
|
396
|
+
req.policy_index)
|
393
397
|
return res
|
394
398
|
|
395
399
|
def check_pipeline(self, wsgi_conf):
|
@@ -26,7 +26,7 @@ from six.moves.urllib.parse import quote, unquote, parse_qsl
|
|
26
26
|
import string
|
27
27
|
|
28
28
|
from swift.common.utils import split_path, json, close_if_possible, md5, \
|
29
|
-
streq_const_time
|
29
|
+
streq_const_time, get_policy_index
|
30
30
|
from swift.common.registry import get_swift_info
|
31
31
|
from swift.common import swob
|
32
32
|
from swift.common.http import HTTP_OK, HTTP_CREATED, HTTP_ACCEPTED, \
|
@@ -47,7 +47,7 @@ from swift.common.middleware.s3api.controllers import ServiceController, \
|
|
47
47
|
LocationController, LoggingStatusController, PartController, \
|
48
48
|
UploadController, UploadsController, VersioningController, \
|
49
49
|
UnsupportedController, S3AclController, BucketController, \
|
50
|
-
TaggingController
|
50
|
+
TaggingController, ObjectLockController
|
51
51
|
from swift.common.middleware.s3api.s3response import AccessDenied, \
|
52
52
|
InvalidArgument, InvalidDigest, BucketAlreadyOwnedByYou, \
|
53
53
|
RequestTimeTooSkewed, S3Response, SignatureDoesNotMatch, \
|
@@ -74,7 +74,8 @@ ALLOWED_SUB_RESOURCES = sorted([
|
|
74
74
|
'versionId', 'versioning', 'versions', 'website',
|
75
75
|
'response-cache-control', 'response-content-disposition',
|
76
76
|
'response-content-encoding', 'response-content-language',
|
77
|
-
'response-content-type', 'response-expires', 'cors', 'tagging', 'restore'
|
77
|
+
'response-content-type', 'response-expires', 'cors', 'tagging', 'restore',
|
78
|
+
'object-lock'
|
78
79
|
])
|
79
80
|
|
80
81
|
|
@@ -103,6 +104,7 @@ def _header_acl_property(resource):
|
|
103
104
|
"""
|
104
105
|
Set and retrieve the acl in self.headers
|
105
106
|
"""
|
107
|
+
|
106
108
|
def getter(self):
|
107
109
|
return getattr(self, '_%s' % resource)
|
108
110
|
|
@@ -121,6 +123,7 @@ class HashingInput(object):
|
|
121
123
|
"""
|
122
124
|
wsgi.input wrapper to verify the hash of the input as it's read.
|
123
125
|
"""
|
126
|
+
|
124
127
|
def __init__(self, reader, content_length, hasher, expected_hex_hash):
|
125
128
|
self._input = reader
|
126
129
|
self._to_read = content_length
|
@@ -549,6 +552,7 @@ class S3Request(swob.Request):
|
|
549
552
|
}
|
550
553
|
self.account = None
|
551
554
|
self.user_id = None
|
555
|
+
self.policy_index = None
|
552
556
|
|
553
557
|
# Avoids that swift.swob.Response replaces Location header value
|
554
558
|
# by full URL when absolute path given. See swift.swob for more detail.
|
@@ -919,8 +923,6 @@ class S3Request(swob.Request):
|
|
919
923
|
src_resp = self.get_response(app, 'HEAD', src_bucket,
|
920
924
|
swob.str_to_wsgi(src_obj),
|
921
925
|
headers=headers, query=query)
|
922
|
-
# we can't let this HEAD req spoil our COPY
|
923
|
-
self.headers.pop('x-backend-storage-policy-index')
|
924
926
|
if src_resp.status_int == 304: # pylint: disable-msg=E1101
|
925
927
|
raise PreconditionFailed()
|
926
928
|
|
@@ -1051,6 +1053,8 @@ class S3Request(swob.Request):
|
|
1051
1053
|
return VersioningController
|
1052
1054
|
if 'tagging' in self.params:
|
1053
1055
|
return TaggingController
|
1056
|
+
if 'object-lock' in self.params:
|
1057
|
+
return ObjectLockController
|
1054
1058
|
|
1055
1059
|
unsupported = ('notification', 'policy', 'requestPayment', 'torrent',
|
1056
1060
|
'website', 'cors', 'restore')
|
@@ -1365,11 +1369,11 @@ class S3Request(swob.Request):
|
|
1365
1369
|
2, 3, True)
|
1366
1370
|
# Update s3.backend_path from the response environ
|
1367
1371
|
self.environ['s3api.backend_path'] = sw_resp.environ['PATH_INFO']
|
1368
|
-
# Propogate backend headers back into our req headers for logging
|
1369
|
-
for k, v in sw_req.headers.items():
|
1370
|
-
if k.lower().startswith('x-backend-'):
|
1371
|
-
self.headers.setdefault(k, v)
|
1372
1372
|
|
1373
|
+
# keep a record of the backend policy index so that the s3api can add
|
1374
|
+
# it to the headers of whatever response it returns, which may not
|
1375
|
+
# necessarily be this resp.
|
1376
|
+
self.policy_index = get_policy_index(sw_req.headers, sw_resp.headers)
|
1373
1377
|
resp = S3Response.from_swift_resp(sw_resp)
|
1374
1378
|
status = resp.status_int # pylint: disable-msg=E1101
|
1375
1379
|
|
@@ -1428,8 +1432,10 @@ class S3Request(swob.Request):
|
|
1428
1432
|
raise SlowDown(status='429 Slow Down')
|
1429
1433
|
raise SlowDown()
|
1430
1434
|
if resp.status_int == HTTP_CONFLICT:
|
1431
|
-
|
1432
|
-
|
1435
|
+
if self.method == 'GET':
|
1436
|
+
raise BrokenMPU()
|
1437
|
+
else:
|
1438
|
+
raise ServiceUnavailable()
|
1433
1439
|
|
1434
1440
|
raise InternalError('unexpected status code %d' % status)
|
1435
1441
|
|
@@ -1497,7 +1503,7 @@ class S3Request(swob.Request):
|
|
1497
1503
|
|
1498
1504
|
_, self.account, _ = split_path(sw_resp.environ['PATH_INFO'],
|
1499
1505
|
2, 3, True)
|
1500
|
-
sw_req = self.to_swift_req(
|
1506
|
+
sw_req = self.to_swift_req('TEST', self.container_name, None)
|
1501
1507
|
info = get_container_info(sw_req.environ, app, swift_source='S3')
|
1502
1508
|
if is_success(info['status']):
|
1503
1509
|
return info
|
@@ -1536,6 +1542,7 @@ class S3AclRequest(S3Request):
|
|
1536
1542
|
"""
|
1537
1543
|
S3Acl request object.
|
1538
1544
|
"""
|
1545
|
+
|
1539
1546
|
def __init__(self, env, app=None, conf=None):
|
1540
1547
|
super(S3AclRequest, self).__init__(env, app, conf)
|
1541
1548
|
self.authenticate(app)
|
@@ -64,7 +64,7 @@ def translate_swift_to_s3(key, val):
|
|
64
64
|
|
65
65
|
if _key.startswith('x-object-meta-'):
|
66
66
|
return translate_meta_key(_key), val
|
67
|
-
elif _key in ('content-length', 'content-type',
|
67
|
+
elif _key in ('accept-ranges', 'content-length', 'content-type',
|
68
68
|
'content-range', 'content-encoding',
|
69
69
|
'content-disposition', 'content-language',
|
70
70
|
'etag', 'last-modified', 'x-robots-tag',
|
@@ -111,6 +111,7 @@ class S3Response(S3ResponseBase, swob.Response):
|
|
111
111
|
headers instead of Swift's HeaderKeyDict. This also translates Swift
|
112
112
|
specific headers to S3 headers.
|
113
113
|
"""
|
114
|
+
|
114
115
|
def __init__(self, *args, **kwargs):
|
115
116
|
swob.Response.__init__(self, *args, **kwargs)
|
116
117
|
|
@@ -239,7 +240,7 @@ class ErrorResponse(S3ResponseBase, swob.HTTPException):
|
|
239
240
|
self.info = kwargs.copy()
|
240
241
|
for reserved_key in ('headers', 'body'):
|
241
242
|
if self.info.get(reserved_key):
|
242
|
-
del(self.info[reserved_key])
|
243
|
+
del (self.info[reserved_key])
|
243
244
|
|
244
245
|
swob.HTTPException.__init__(
|
245
246
|
self, status=kwargs.pop('status', self._status),
|
@@ -613,6 +614,16 @@ class NoSuchKey(ErrorResponse):
|
|
613
614
|
ErrorResponse.__init__(self, msg, key=key, *args, **kwargs)
|
614
615
|
|
615
616
|
|
617
|
+
class ObjectLockConfigurationNotFoundError(ErrorResponse):
|
618
|
+
_status = '404 Not found'
|
619
|
+
_msg = 'Object Lock configuration does not exist for this bucket'
|
620
|
+
|
621
|
+
def __init__(self, bucket, msg=None, *args, **kwargs):
|
622
|
+
if not bucket:
|
623
|
+
raise InternalError()
|
624
|
+
ErrorResponse.__init__(self, msg, bucket_name=bucket, *args, **kwargs)
|
625
|
+
|
626
|
+
|
616
627
|
class NoSuchLifecycleConfiguration(ErrorResponse):
|
617
628
|
_status = '404 Not Found'
|
618
629
|
_msg = 'The lifecycle configuration does not exist. .'
|
@@ -113,7 +113,7 @@ class S3Timestamp(utils.Timestamp):
|
|
113
113
|
|
114
114
|
@property
|
115
115
|
def s3xmlformat(self):
|
116
|
-
dt = datetime.datetime.
|
116
|
+
dt = datetime.datetime.fromtimestamp(self.ceil(), utils.UTC)
|
117
117
|
return dt.strftime(self.S3_XML_FORMAT)
|
118
118
|
|
119
119
|
@classmethod
|