Browse Source
This implements the storage backend interface added in the previous commit using Swift. It also adds some example configuration to the sample config file, and some setup to initialise a storage backend if one is configured. Change-Id: I8467486ed42f8674e2b1db635789e88bf4113850changes/65/633365/5
5 changed files with 196 additions and 0 deletions
@ -0,0 +1,21 @@
|
||||
# Copyright (c) 2019 Adam Coldrick |
||||
# |
||||
# Licensed under the Apache License, Version 2.0 (the "License"); |
||||
# you may not use this file except in compliance with the License. |
||||
# You may obtain a copy of the License at |
||||
# |
||||
# http://www.apache.org/licenses/LICENSE-2.0 |
||||
# |
||||
# Unless required by applicable law or agreed to in writing, software |
||||
# distributed under the License is 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 storyboard.api.v1.storage.swift_impl import SwiftStorageImpl |
||||
|
||||
|
||||
STORAGE_IMPLS = { |
||||
"swift": SwiftStorageImpl |
||||
} |
@ -0,0 +1,116 @@
|
||||
# Copyright (c) 2019 Adam Coldrick |
||||
# |
||||
# Licensed under the Apache License, Version 2.0 (the "License"); |
||||
# you may not use this file except in compliance with the License. |
||||
# You may obtain a copy of the License at |
||||
# |
||||
# http://www.apache.org/licenses/LICENSE-2.0 |
||||
# |
||||
# Unless required by applicable law or agreed to in writing, software |
||||
# distributed under the License is 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 hashlib import sha1 |
||||
import hmac |
||||
from time import time |
||||
import uuid |
||||
|
||||
import openstack |
||||
from openstack import connection |
||||
from oslo_config import cfg |
||||
from six.moves.urllib import parse |
||||
|
||||
from storyboard.api.v1.storage.storage import StorageBackend |
||||
|
||||
|
||||
CONF = cfg.CONF |
||||
|
||||
SWIFT_OPTS = [ |
||||
cfg.StrOpt("cloud", |
||||
default="", |
||||
help="Name of the cloud which provides Swift as " |
||||
"used in `clouds.yaml`. Other auth-related " |
||||
"options are ignored if this is set."), |
||||
cfg.StrOpt("auth_url", |
||||
default="http://127.0.0.1:8888/auth/v1.0", |
||||
help="URL to use to obtain an auth token from swift."), |
||||
cfg.StrOpt("auth_type", |
||||
default="v1password", |
||||
help="Swift auth type, defaults to 'v1password' " |
||||
"(which is legacy auth)."), |
||||
cfg.StrOpt("user", |
||||
default="test:tester", |
||||
help="User to use when authenticating with Swift to obtain an " |
||||
"auth token."), |
||||
cfg.StrOpt("password", |
||||
default="testing", |
||||
help="Password to use when authenticating with Swift."), |
||||
cfg.StrOpt("container", |
||||
default="storyboard", |
||||
help="Swift container to store attachments in. Will be " |
||||
"created if it doesn't already exist."), |
||||
cfg.StrOpt("temp_url_key", |
||||
default="secret_key", |
||||
help="Temp URL secret key to set for the container if it " |
||||
"is created by StoryBoard."), |
||||
cfg.IntOpt("temp_url_timeout", |
||||
default=120, |
||||
help="Number of seconds that Swift tempurl signatures " |
||||
"are valid for after generation.") |
||||
] |
||||
|
||||
CONF.register_opts(SWIFT_OPTS, "swift") |
||||
|
||||
|
||||
class SwiftStorageImpl(StorageBackend): |
||||
"""Implementation of an attachment storage backend using swift.""" |
||||
|
||||
def _get_connection(self): |
||||
if CONF.swift.cloud: |
||||
return connection.Connection( |
||||
cloud=CONF.swift.cloud, |
||||
service_types={'object-store'}) |
||||
|
||||
return openstack.connect( |
||||
auth_type=CONF.swift.auth_type, |
||||
auth_url=CONF.swift.auth_url, |
||||
username=CONF.swift.user, |
||||
password=CONF.swift.password, |
||||
) |
||||
|
||||
def _ensure_container_exists(self, conn): |
||||
names = [container.name |
||||
for container in conn.object_store.containers()] |
||||
if CONF.swift.container not in names: |
||||
conn.object_store.create_container(CONF.swift.container) |
||||
conn.object_store.set_container_temp_url_key( |
||||
CONF.swift.container, CONF.swift.temp_url_key) |
||||
container = conn.object_store.set_container_metadata( |
||||
CONF.swift.container, read_ACL=".r:*") |
||||
|
||||
def get_upload_url(self): |
||||
conn = self._get_connection() |
||||
self._ensure_container_exists(conn) |
||||
|
||||
url = conn.object_store.get_endpoint() |
||||
return "%s/%s" % (url, CONF.swift.container) |
||||
|
||||
def get_auth(self): |
||||
conn = self._get_connection() |
||||
self._ensure_container_exists(conn) |
||||
|
||||
name = str(uuid.uuid4()) |
||||
endpoint = parse.urlparse(conn.object_store.get_endpoint()) |
||||
path = '/'.join((endpoint.path, CONF.swift.container, name)) |
||||
|
||||
method = 'PUT' |
||||
expires = int(time() + CONF.swift.temp_url_timeout) |
||||
hmac_body = '%s\n%s\n%s' % (method, expires, path) |
||||
hmac_body = hmac_body.encode('utf8') |
||||
|
||||
key = conn.object_store.get_temp_url_key(CONF.swift.container) |
||||
signature = hmac.new(key, hmac_body, sha1).hexdigest() |
||||
return expires, signature, name |
Loading…
Reference in new issue