Skip to content

Commit

Permalink
Add {set,get}_bucket_versioning() APIs
Browse files Browse the repository at this point in the history
Fixes #974
  • Loading branch information
balamurugana committed Sep 16, 2020
1 parent 1dd0707 commit ff188de
Show file tree
Hide file tree
Showing 7 changed files with 107 additions and 45 deletions.
28 changes: 15 additions & 13 deletions docs/API.md
Original file line number Diff line number Diff line change
Expand Up @@ -37,8 +37,8 @@ s3Client = Minio(
| [`bucket_exists`](#bucket_exists) | [`copy_object`](#copy_object) | [`presigned_post_policy`](#presigned_post_policy) | [`delete_bucket_policy`](#delete_bucket_policy) |
| [`remove_bucket`](#remove_bucket) | [`stat_object`](#stat_object) | | [`get_bucket_notification`](#get_bucket_notification) |
| [`list_objects`](#list_objects) | [`remove_object`](#remove_object) | | [`set_bucket_notification`](#set_bucket_notification) |
| [`enable_bucket_versioning`](#enable_bucket_versioning) | [`remove_objects`](#remove_objects) | | [`remove_all_bucket_notification`](#remove_all_bucket_notification) |
| [`disable_bucket_versioning`](#disable_bucket_versioning) | [`fput_object`](#fput_object) | | [`listen_bucket_notification`](#listen_bucket_notification) |
| [`get_bucket_versioning`](#get_bucket_versioning) | [`remove_objects`](#remove_objects) | | [`remove_all_bucket_notification`](#remove_all_bucket_notification) |
| [`set_bucket_versioning`](#set_bucket_versioning) | [`fput_object`](#fput_object) | | [`listen_bucket_notification`](#listen_bucket_notification) |
| | [`fget_object`](#fget_object) | | [`get_bucket_encryption`](#get_bucket_encryption) |
| | [`select_object_content`](#select_object_content) | | [`put_bucket_encryption`](#put_bucket_encryption) |
| | | | [`delete_bucket_encryption`](#delete_bucket_encryption) |
Expand Down Expand Up @@ -570,11 +570,11 @@ __Example__
minio.delete_bucket_encryption("my-bucketname")
```

<a name="enable_bucket_versioning"></a>
<a name="get_bucket_versioning"></a>

### enable_bucket_versioning(bucket_name)
### get_bucket_versioning(bucket_name)

Enable object versioning feature in a bucket.
Get versioning configuration of a bucket.

__Parameters__

Expand All @@ -585,25 +585,27 @@ __Parameters__
__Example__

```py
minio.enable_bucket_versioning("my-bucketname")
config = minio.get_bucket_versioning("my-bucketname")
print(config.status)
```

<a name="enable_bucket_versioning"></a>
<a name="set_bucket_versioning"></a>

### disable_bucket_versioning(bucket_name)
### set_bucket_versioning(bucket_name, config)

Disable object versioning feature in a bucket.
Set versioning configuration to a bucket.

__Parameters__

| Param | Type | Description |
|:----------------|:------|:--------------------|
| ``bucket_name`` | _str_ | Name of the bucket. |
| Param | Type | Description |
|:----------------|:-------------------|:--------------------------|
| ``bucket_name`` | _str_ | Name of the bucket. |
| ``config`` | _VersioningConfig_ | Versioning configuration. |

__Example__

```py
minio.disable_bucket_versioning("my-bucketname")
minio.set_bucket_versioning("my-bucketname", VersioningConfig("Enabled"))
```

## 3. Object operations
Expand Down
2 changes: 1 addition & 1 deletion minio/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,6 @@
# pylint: disable=unused-import
from .api import Minio
from .copy_conditions import CopyConditions
from .definitions import Bucket, Object
from .definitions import Bucket, Object, VersioningConfig
from .error import InvalidResponseError, S3Error, ServerError
from .post_policy import PostPolicy
49 changes: 27 additions & 22 deletions minio/api.py
Original file line number Diff line number Diff line change
Expand Up @@ -58,14 +58,15 @@
parse_list_parts, parse_location_constraint,
parse_multi_delete_response,
parse_multipart_upload_result,
parse_new_multipart_upload)
parse_new_multipart_upload, parse_versioning_config)
from .select import SelectObjectReader
from .signer import (AMZ_DATE_FORMAT, SIGN_V4_ALGORITHM, get_credential_string,
post_presign_v4, presign_v4, sign_v4_s3)
from .sse import SseCustomerKey
from .thread_pool import ThreadPool
from .xml_marshal import (marshal_bucket_notifications,
marshal_complete_multipart,
marshal_versioning_config,
xml_marshal_bucket_constraint,
xml_marshal_bucket_encryption,
xml_marshal_delete_objects, xml_marshal_select,
Expand Down Expand Up @@ -864,14 +865,20 @@ def listen_bucket_notification(self, bucket_name, prefix='', suffix='',
response.close()
response.release_conn()

def _do_bucket_versioning(self, bucket_name, status):
"""Do versioning support in a bucket."""
def set_bucket_versioning(self, bucket_name, config):
"""
Set versioning configuration to a bucket.
:param bucket_name: Name of the bucket.
:param config: :class:`VersioningConfig <VersioningConfig>`.
Example::
minio.set_bucket_versioning(
"my-bucketname", VersioningConfig("Enabled"),
)
"""
check_bucket_name(bucket_name)
body = (
'<VersioningConfiguration '
'xmlns="http://s3.amazonaws.com/doc/2006-03-01/">'
'<Status>{0}</Status></VersioningConfiguration>'
).format(status).encode()
body = marshal_versioning_config(config)
self._execute(
"PUT",
bucket_name,
Expand All @@ -880,27 +887,25 @@ def _do_bucket_versioning(self, bucket_name, status):
query_params={"versioning": ""},
)

def enable_bucket_versioning(self, bucket_name):
def get_bucket_versioning(self, bucket_name):
"""
Enable object versioning feature in a bucket.
Get versioning configuration of a bucket.
:param bucket_name: Name of the bucket.
:return: :class:`VersioningConfig <VersioningConfig>`.
Example::
minio.enable_bucket_versioning("my-bucketname")
"""
self._do_bucket_versioning(bucket_name, "Enabled")

def disable_bucket_versioning(self, bucket_name):
config minio.get_bucket_versioning("my-bucketname")
print(config.status)
"""
Disable object versioning feature in a bucket.
:param bucket_name: Name of the bucket.
check_bucket_name(bucket_name)
response = self._execute(
"GET",
bucket_name,
query_params={"versioning": ""},
)

Example::
minio.disable_bucket_versioning("my-bucketname")
"""
self._do_bucket_versioning(bucket_name, "Suspended")
return parse_versioning_config(response.data)

def fput_object(self, bucket_name, object_name, file_path,
content_type='application/octet-stream',
Expand Down
22 changes: 22 additions & 0 deletions minio/definitions.py
Original file line number Diff line number Diff line change
Expand Up @@ -472,3 +472,25 @@ def __init__(self, root):
self.parts = [
Part(part_element) for part_element in root.findall("Part")
]


class VersioningConfig:
"""Bucket versioning configuration."""

def __init__(self, status, mfa_delete=None):
if status:
status = status.title()
if status not in ["", "Enabled", "Suspended"]:
raise ValueError("status must be empty, Enabled or Suspended.")
self._status = status
self._mfa_delete = mfa_delete

@property
def status(self):
"""Get status."""
return self._status or "Off"

@property
def mfa_delete(self):
"""Get MFA delete."""
return self._mfa_delete
12 changes: 11 additions & 1 deletion minio/parsers.py
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,8 @@
from xml.etree.ElementTree import ParseError

from .definitions import (Bucket, CopyObjectResult, ListMultipartUploadsResult,
ListPartsResult, MultipartUploadResult, Object)
ListPartsResult, MultipartUploadResult, Object,
VersioningConfig)
# minio specific.
from .error import MultiDeleteError, S3Error
from .helpers import RFC3339, RFC3339NANO
Expand Down Expand Up @@ -487,3 +488,12 @@ def parse_list_multipart_uploads(data):
def parse_list_parts(data):
"""Parse ListParts API resppnse XML."""
return ListPartsResult(S3Element.fromstring("ListPartsResult", data))


def parse_versioning_config(data):
"""Decode XML data into :class:`VersioningConfig <VersioningConfig>`"""
root = S3Element.fromstring("VersioningConfiguration", data)
return VersioningConfig(
root.get_child_text("Status"),
root.get_child_text("MFADelete"),
)
11 changes: 11 additions & 0 deletions minio/xml_marshal.py
Original file line number Diff line number Diff line change
Expand Up @@ -409,3 +409,14 @@ def xml_marshal_delete_objects(keys):
SubElement(element, "VersionId", version_id)

return _get_xml_data(root)


def marshal_versioning_config(config):
"""Encode versioning configuration to XML bytes."""
root = Element("VersioningConfiguration", with_namespace=True)
SubElement(root, "Status", config.status)
if config.mfa_delete is not None:
SubElement(
root, "MFADelete", "Enabled" if config.mfa_delete else "Disabled",
)
return _get_xml_data(root)
28 changes: 20 additions & 8 deletions tests/functional/tests.py
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@
import certifi
import urllib3

from minio import CopyConditions, Minio, PostPolicy
from minio import CopyConditions, Minio, PostPolicy, VersioningConfig
from minio.error import S3Error
from minio.select.helpers import calculate_crc
from minio.select.options import (CSVInput, CSVOutput, InputSerialization,
Expand Down Expand Up @@ -807,7 +807,9 @@ def _test_stat_object(log_entry, sse=None, version_check=False):
_CLIENT.make_bucket(bucket_name)
try:
if version_check:
_CLIENT.enable_bucket_versioning(bucket_name)
_CLIENT.set_bucket_versioning(
bucket_name, VersioningConfig("Enabled"),
)
# Put/Upload a streaming object of 1 MiB
reader = LimitedRandomReader(length)
_, version_id1 = _CLIENT.put_object(
Expand Down Expand Up @@ -875,7 +877,9 @@ def _test_remove_object(log_entry, version_check=False):
_CLIENT.make_bucket(bucket_name)
try:
if version_check:
_CLIENT.enable_bucket_versioning(bucket_name)
_CLIENT.set_bucket_versioning(
bucket_name, VersioningConfig("Enabled"),
)
_, version_id = _CLIENT.put_object(
bucket_name, object_name, LimitedRandomReader(length), length,
)
Expand Down Expand Up @@ -914,7 +918,9 @@ def _test_get_object(log_entry, sse=None, version_check=False):
version_id = None
try:
if version_check:
_CLIENT.enable_bucket_versioning(bucket_name)
_CLIENT.set_bucket_versioning(
bucket_name, VersioningConfig("Enabled"),
)
_, version_id = _CLIENT.put_object(
bucket_name, object_name, LimitedRandomReader(length),
length, sse=sse,
Expand Down Expand Up @@ -965,7 +971,9 @@ def _test_fget_object(log_entry, sse=None, version_check=False):
version_id = None
try:
if version_check:
_CLIENT.enable_bucket_versioning(bucket_name)
_CLIENT.set_bucket_versioning(
bucket_name, VersioningConfig("Enabled"),
)
_, version_id = _CLIENT.put_object(
bucket_name, object_name, LimitedRandomReader(length),
length, sse=sse,
Expand Down Expand Up @@ -1090,7 +1098,9 @@ def _test_list_objects(log_entry, use_api_v1=False, version_check=False):
version_id2 = None
try:
if version_check:
_CLIENT.enable_bucket_versioning(bucket_name)
_CLIENT.set_bucket_versioning(
bucket_name, VersioningConfig("Enabled"),
)
size = 1 * KB
_, version_id1 = _CLIENT.put_object(
bucket_name, object_name + "-1", LimitedRandomReader(size), size,
Expand Down Expand Up @@ -1469,7 +1479,7 @@ def test_presigned_get_object_version( # pylint: disable=invalid-name
_CLIENT.make_bucket(bucket_name)
version_id = None
try:
_CLIENT.enable_bucket_versioning(bucket_name)
_CLIENT.set_bucket_versioning(bucket_name, VersioningConfig("Enabled"))
size = 1 * KB
_, version_id = _CLIENT.put_object(
bucket_name, object_name, LimitedRandomReader(size), size,
Expand Down Expand Up @@ -1835,7 +1845,9 @@ def _test_remove_objects(log_entry, version_check=False):
object_names = []
try:
if version_check:
_CLIENT.enable_bucket_versioning(bucket_name)
_CLIENT.set_bucket_versioning(
bucket_name, VersioningConfig("Enabled"),
)
size = 1 * KB
# Upload some new objects to prepare for multi-object delete test.
for i in range(10):
Expand Down

0 comments on commit ff188de

Please sign in to comment.