具体的なストレージアドオンの例として、AWS S3互換のMinIOと接続するストレージアドオンを実装します。このアドオンは特定のMinIOサーバーと接続するものとし、 My MinIOアドオンと名付けることとします。
基本的なアドオンの概要はスケルトンの作成を参照してください。
開発環境の準備のガイドに従い、開発環境にてRDMを起動しているものとします。
ストレージアドオンは以下の2つの要素から構成されます。
- OSF.ioサービスで動作するAddon: ユーザ・プロジェクト設定の管理
- WaterButlerサービスで動作するProvider: ストレージへのアクセスの仲介
Addonによりユーザからの認証情報の受領や各種設定を行い、Providerはこの認証情報・設定情報をAddonから譲渡してもらい、実際のストレージへのアクセスを行います。
典型的なクラス構成とファイル配置は以下のようになります。
(*)が付いているファイルはスケルトン アドオンには存在しないファイルです。
/addons/アドオン名/
├── __init__.py ... モジュールの定義
├── apps.py ... アプリケーションの定義
├── models.py ... モデルの定義
├── provider.py ... (*) 認証プロバイダの定義
├── requirements.txt ... 利用するPythonモジュールの定義
├── routes.py ... View(Routes)の定義
├── serializer.py ... (*) モデル-ビュー(JavaScript)間の情報交換用シリアライザの定義
├── settings ... 設定を定義するモジュール
│ ├── defaults.py ... デフォルト設定の定義
│ ├── __init__.py ... 設定の定義
│ └── local-dist.py ... (*) local.pyのサンプルファイル
├── static ... Webブラウザから読み込むことを想定した静的ファイル
│ ├── comicon.png ... アドオンのアイコン
│ ├── myminioAnonymousLogActionList.json ... (*) 変更履歴メッセージ定義
│ ├── myminioLogActionList.json ... (*) 変更履歴メッセージ定義
│ ├── myminioNodeConfig.js ... (*) Node設定の定義
│ ├── myminioUserConfig.js ... (*) User設定の定義
│ ├── node-cfg.js ... Node設定のエントリとなるJavaScriptファイル
│ └── user-cfg.js ... (*) User設定のエントリとなるJavaScriptファイル
├── templates ... テンプレートディレクトリ
│ ├── credentials_modal.mako ... (*) 認証情報の設定用ダイアログ
│ ├── node_settings.mako ... Node設定パネル
│ └── user_settings.mako ... (*) User設定パネル
├── tests ... テストコード
│ ├── __init__.py
│ ├── conftest.py
│ ├── factories.py
│ ├── test_model.py
│ ├── test_serializer.py ... (*)
│ ├── test_view.py
│ └── utils.py
├── utils.py ... (*) ユーティリティ関数の定義
└── views.py ... View(Views)の定義
waterbutler/providers/アドオン名/
├── __init__.py ... Providerクラスの参照
├── metadata.py ... Metadataの定義
├── provider.py ... Providerの定義
└── settings.py ... デフォルト設定の定義
tests/providers/アドオン名/
├── __init__.py
└── provider.py ... Providerのテストコード
スケルトン アドオンとの違いを中心に説明していきます。
models.py に、以下のModelを定義します。
UserSettings: ユーザーに関する情報(認証情報等)NodeSettings: プロジェクトに関する情報アドオン名FileNode: ファイル・フォルダオブジェクトの親定義アドオン名File: ファイルオブジェクトの定義アドオン名Folder: ファイルオブジェクトの定義
provider.py に アドオン名Provider を、 serializer.py に アドオン名Serializer を定義します。OAuthを用いる場合は osf.models.external.ExternalProvider を継承し、必要なメンバを定義します。実装は GitHubの例 などを参考にしてください(GitHubアドオンは アドオン名Provider を models.py に定義しています)。
アドオン名Serializer にはビュー(JavaScript)にURLリストや接続先フォルダなどを渡すための関数を定義します。
OAuth認証をしない場合であっても、ストレージアドオンの UserSettings と NodeSettings は、 BaseOAuthUserSettings と BaseOAuthNodeSettings をそれぞれ継承して定義し、 oauth_provider にアドオン名Provider を指定します。こうすることで、統一的な構造で簡単に認証の仕組みを実装することができます。
My MinIOアドオンの場合は、以下のように定義します。
views.pyには、おおよそ以下の関数を定義します。
| 関数名 | 処理 |
|---|---|
| set_config | プロジェクトの設定を保存する。デフォルトでは接続先のフォルダとクライアント用のAPIのURLリストのみ。 |
| get_config | プロジェクトの設定を取得する。 |
| import_auth | ログイン中のユーザの認証情報をプロジェクトにインポートする。 |
| deauthorize_node | プロジェクトの認証情報を取り消す。 |
| add_user_account | ユーザの認証情報を追加する。 |
| account_list | ユーザの認証情報リストを取得する。 |
| create_folder | ストレージサービスにフォルダを追加する。プロジェクトのアドオン設定ページでフォルダを作る機能を提供しない場合は不要。 |
| folder_list | ストレージサービスのフォルダリストを取得する。 |
シリアライザ(serializer.py)を定義し、 addons.base.generic_views を利用することで、以下のように一部のView処理を簡単に定義することができます。
import_auth = addons.base.generic_views.import_auth(
SHORT_NAME,
Serializer
)
generic_views で提供していないView処理を追加したい場合や、View処理をカスタマイズしたい場合は、以下のように views.py に個別の関数を定義します。
@must_have_addon(SHORT_NAME, 'node')
@must_be_addon_authorizer(SHORT_NAME)
def folder_list(node_addon, **kwargs):
return node_addon.get_folders()
フォルダの作成やフォルダリストの取得をするために、Addonでもストレージサービスへ接続する必要があります。
My MinIOアドオンの場合は、以下のように定義します。
アドオンが持つ利用者用設定画面(user_settings.mako)とプロジェクト用設定画面(node_settings.mako)のテンプレートをそれぞれ定義します。認証情報の設定用ダイアログ(credentials_modal.mako)はどちらの画面でも利用するので、別のファイルで定義し、それぞれから参照します。
My MinIOアドオンの場合は、以下のように定義します。
利用者用設定画面のJavaScriptファイル(user-cfg.js, myminioUserConfig.js)と、プロジェクト用設定画面のJavaScriptファイル(node-cfg.js, myminioNodeConfig.js)をそれぞれ定義します。今回は、エントリとなるJavaScriptファイル(*-cfg.js)と定義ファイル(myminio*Config.js)を分けましたが、スケルトン アドオンのように *-cfg.js に定義を書いても構いません。
My MinIOアドオンの場合は、以下のように定義します。
また、変更履歴メッセージの定義ファイルも追加します。
My MinIOアドオンの場合は、以下のように定義します。
ストレージ操作UIであるFileViewerはFangornを使って実装されています。アイテム選択時に表示するボタンをカスタマイズしたい場合は、 Fangorn.config.アドオン名 を定義し、 files.js ファイルで読み込みます。
My MinIOアドオンではカスタマイズせずデフォルト動作を使用しています。カスタマイズ例は、GitHubアドオンやIQB-RIMSアドオンを参照してください。
環境ごとの設定ファイル local.py の雛形として local-dist.py ファイルを定義します。サービス管理者は local-dist.py を local.py にコピーして、適宜設定値を書き換えます。
My MinIOアドオンの場合は、以下のように定義します。接続するMinIOサービスのホスト名を HOST プロパティに設定します。
シリアライザのテスト(test_serializer.py)を定義します。
My MinIOアドオンの場合は、以下のように定義します。
WaterButler Providerは、WaterButlerプロジェクトの中でPythonモジュールとして実装されます。
metadata.py には、フォルダやファイルのメタデータと、リビジョンを持つクラスを定義します。
My MinIOアドオンの場合は、以下のように定義します。
provider.pyには、ストレージサービスと接続しCRUD操作などを行うProviderクラスを定義します。
Providerが提供するメソッドには以下のようなものがあります。
| 関数名 | 引数 | 戻り値 | 処理 |
|---|---|---|---|
| validate_v1_path | path, **kwargs | WaterButlerPath | 文字列で与えられたパス情報(path)を検証し、属性付きのWaterButlerPathオブジェクトを返す。 |
| validate_path | path, **kwargs | WaterButlerPath | 同上(廃止予定のv0仕様との互換性維持のため、2つのメソッドに分かれている)。 |
| download | path, accept_url=False, version=None, range=None, **kwargs | Stream | 指定されたパス(path)のデータをダウンロードする。戻り値にはデータアクセス用のStreamを返す。 |
| upload | stream, path, conflict='replace', **kwargs | Metadata | 指定されたパス(path)に指定されたデータ(stream)をアップロードする。戻り値にはアップロードしたファイルを示すMetadataを返す。 |
| delete | path, confirm_delete=0, **kwargs | なし | 指定されたパス(path)のファイル・フォルダを削除する。 |
| revisions | path, **kwargs | List(Revision) | 指定されたパス(path)のリビジョン情報を取得する。 |
| metadata | path, revision=None, **kwargs | Metadata or List(Metadata) | 指定されたパス(path)のメタデータを取得する。path がfileの場合 Metadata , directoryの場合 List(Metadata) を返す。 |
| create_folder | path, folder_precheck=True, **kwargs | Metadata | 指定されたパスにフォルダを作成する。戻り値には作成したフォルダを示すMetadataを返す。 |
| can_intra_copy | dest_provider, path=None | Bool | 指定された送信先Provider(dest_provider), パス(path)に対してintra_copy(内部コピー: ストレージサービス上でのコピー)が可能かどうかを判定する。これがFalseの場合、いったん一時ディレクトリにdownloadして、destにuploadするという操作となる。 |
| can_intra_move | dest_provider, path=None | Bool | 指定された送信先Provider(dest_provider), パス(path)に対してintra_move(内部移動: ストレージサービス中での移動)が可能かどうかを判定する。これがFalseの場合、いったん一時ディレクトリにdownloadして、destにupload、コピー元ファイルをdeleteするという操作となる。 |
| intra_copy | dest_provider, src_path, dest_path | Bool | 内部コピーを実施する。成功すればTrueを返す。 |
| intra_move | dest_provider, src_path, dest_path | Bool | 内部移動を実施する。成功すればTrueを返す。 |
My MinIOアドオンの場合は、以下のように定義します。
OSF.io Addonは、WaterButler Providerが必要なサービスに接続できるよう、認証情報を委譲します。この認証情報の形式はAddonにより異なるため、AddonとProviderのバージョンを合わせるなど依存関係に配慮する必要があります。
Addonにおける委譲設定は、 addons.アドオン名.models.NodeSettings.serialize_waterbutler_credentials() 関数で設定します。接続先のフォルダなどの情報は、同クラスの serialize_waterbutler_settings() 関数で設定します。例えば IQB-RIMSアドオンの serialize_waterbutler_settings() 関数 では、フォルダごとの権限設定を渡します。
My MinIOアドオンの場合は、以下のように定義します。認証情報として、アクセス先のホスト名、アクセスキー、シークレットキーを渡します。設定情報として、接続先のバケットIDを渡します。
def serialize_waterbutler_credentials(self):
if not self.has_auth:
raise exceptions.AddonError('Cannot serialize credentials for {} addon'.format(FULL_NAME))
return {
'host': settings.HOST,
'access_key': self.external_account.oauth_key,
'secret_key': self.external_account.oauth_secret,
}
def serialize_waterbutler_settings(self):
if not self.folder_id:
raise exceptions.AddonError('Cannot serialize settings for {} addon'.format(FULL_NAME))
return {
'bucket': self.folder_id
}
Provider側では、認証情報と設定情報を waterbutler.providers.アドオン名.アドオン名Provider クラスのコンストラクタ(__init__())の引数 credentials と settings にDictionary型でそれぞれ受け取ります。
My MinIOアドオンの場合は、以下のように定義します。認証情報を使ってMy MinIOサービスとの接続を確立し、設定情報を使って接続先バケットを取得します。
class MyMinIOProvider(provider.BaseProvider):
def __init__(self, auth, credentials, settings):
super().__init__(auth, credentials, settings)
host = credentials['host']
port = 443
m = re.match(r'^(.+)\:([0-9]+)$', host)
if m is not None:
host = m.group(1)
port = int(m.group(2))
self.connection = MyMinIOConnection(credentials['access_key'],
credentials['secret_key'],
calling_format=OrdinaryCallingFormat(),
host=host,
port=port,
is_secure=port == 443)
self.bucket = self.connection.get_bucket(settings['bucket'], validate=False)
何らかのユーザ操作を契機としてアドオンに対して行われた操作は、Recent Activityという形で記録することができます。
Recent ActivityはNodeLogモデルにより表現されます。 ログの追加はNode(プロジェクトに対応するモデル)の add_logメソッド により行うことができます。
self.owner.add_log(
'{0}_{1}'.format(SHORT_NAME, action),
auth=auth,
params={
'project': self.owner.parent_id,
'node': self.owner._id,
'path': metadata['materialized'],
'bucket': self.folder_id,
'urls': {
'view': url,
'download': url + '?action=download'
}
},
)
この例では、NodeSettingsモデルのowner(Node)に対してログの追加を指示しています。 パラメータには以下の値を指定することができます。
action... ログのアクション種別を示す名前。アドオン名_アクション名となる。本サンプルにより記録されるアクション名には以下のものがある。node_authorized,node_deauthorized,node_deauthorized_no_user... プロジェクトに本アドオンが設定あるいは解除された場合に記録されるログbucket_linked,bucket_unlinked... プロジェクト設定画面により、バケットが設定あるいは解除された場合に記録されるログfile_added,file_removed,file_updated,folder_created... WaterButlerによるファイル操作が行われた場合に記録されるログ
params... ログのパラメータ。任意のdictオブジェクトを指定することができるauth... 操作を実施したユーザの情報。framework.auth.Authクラスのインスタンスを与えることができる
記録されたログをどのように表示するかは、以下のJSONファイルにより定義します。
"myminio_bucket_linked" : "${user} linked the My MinIO bucket ${bucket} to ${node}",
myminioAnonymousLogActionList.json
"myminio_bucket_linked" : "A user linked an My MinIO bucket to a project",
アドオン名LogActionList.jsonはログインした状態でのプロジェクト表示の際に利用され、アドオン名AnonymousLogActionList.jsonはパブリックなプロジェクト(RDMでは利用を想定していません)に利用されます。
どのメッセージがログ表示に利用されるかは、add_logメソッドの action 引数に与えられた文字列がキーとして使用されます。また、メッセージ定義中の ${パラメータ名} にはadd_logメソッドの params 引数に与えられたパラメータ中のキーを指定することができます。
メッセージの国際化はpybabelコマンドを用いて行うことができます。定義したアドオンのメッセージ(英語で記載する)に対応する日本語メッセージの定義ファイルを生成するためには、 以下のコマンドを実行します。
# メッセージ定義JSONなどをJavaScriptファイルへと変換する
$ docker compose run --rm web invoke assets
# メッセージ定義テンプレートファイル website/translations/js_messages.pot を更新する
$ docker compose run --rm web pybabel extract -F ./website/settings/babel_js.cfg -o ./website/translations/js_messages.pot .
# メッセージ定義ファイル website/translations/ja/LC_MESSAGES/js_messages.po を更新する
$ docker compose run --rm web pybabel update -i ./website/translations/js_messages.pot -o ./website/translations/en/LC_MESSAGES/js_messages.po -l en
$ docker compose run --rm web pybabel update -i ./website/translations/js_messages.pot -o ./website/translations/ja/LC_MESSAGES/js_messages.po -l ja
すると、website/translations/ja/LC_MESSAGES/js_messages.poファイルに、以下のような空の項目が追加されます。
#: website/static/js/logActionsList_extract.js:246
msgid "${user} linked the My MinIO bucket ${bucket} to ${node}"
msgstr ""
この msgstr に日本語によるメッセージ定義を追加することで、メッセージを国際化することができます。
#: website/static/js/logActionsList_extract.js:246
msgid "${user} linked the My MinIO bucket ${bucket} to ${node}"
msgstr "${user}が My MinIOバケット(${bucket})を接続しました"
js_messages.po を変更したら、assetsサービスを再起動してください。最新のjs_messages.poファイルがメッセージの表示に使用されるようになります。
$ docker compose restart assets
RDMではユーザが任意のタイミングで、プロジェクト中のファイルに対してタイムスタンプを打つことができます。タイムスタンプは、ファイルに関してその時点での内容を証明するもので、研究の証跡としてのデータを考える上で非常に重要です。
なお、docker-composeで起動した状態では FreeTSA Project のサーバを用いてタイムスタンプを付与します。実環境への配備時はUPKI電子証明書発行サービスによりタイムスタンプ付与することを想定しています。
タイムスタンプは、ファイルの内容から計算したハッシュ値を署名する形で作成されます。ハッシュの取得は以下のいずれかの方法でおこないます。
osf.models.files.Fileサブクラスのget_hash_for_timestampにより取得する- WaterButlerを経由してファイルをダウンロードし、ハッシュ計算を行う
osf.models.files.File サブクラスはアドオンのモデルとしてハッシュ値の取得方法を定義するものです。本サンプルでは models.py にあります。
ストレージにより容易にハッシュ相当の値を取得・管理する方法がある場合は、このモデルにget_hash_for_timestamp(self)メソッドを定義します。
タイムスタンプ処理はこのメソッドを通じてハッシュ値を取得することができます。
get_hash_for_timestamp(self)メソッドの実装は dropboxbusinessアドオンの DropboxBusinessFileクラス を参考にしてください。
osf.models.files.Fileモデルにget_hash_for_timestamp(self)が定義されていない場合は、タイムスタンプ処理はWaterButlerを経由してファイルをダウンロードする方法を試行します。
RDM以外でのファイル変更時に実施する場合も、ファイルが変更されたことを示すためにタイムスタンプを付加したい場合があります。
このような処理を実行するためには website.util.timestamp モジュールを使用します。
例えばDropbox Businessアドオンでは以下のように実装しています。
...
from website.util import timestamp
...
file_info = {
'file_id': file_node._id,
'file_name': attrs.get('name'),
'file_path': attrs.get('materialized'),
'size': attrs.get('size'),
'created': attrs.get('created_utc'),
'modified': attrs.get('modified_utc'),
'file_version': '',
'provider': PROVIDER_NAME
}
# verified by admin
verify_result = timestamp.check_file_timestamp(
admin.id, node, file_info, verify_external_only=True)
check_file_timestamp(uid, node, data, verify_external_only=False)関数は以下のパラメータを指定することができます。
uid... タイムスタンプ操作を行うユーザを示すOSFUserのIDnode... ファイルが所属するプロジェクトを示すNodedata... タイムスタンプにより署名検証するデータを示す辞書型データverify_external_only... タイムスタンプ検証情報の格納にosf.models.RdmFileTimestamptokenVerifyResultを使う場合はFalse, 使わない場合(osf.models.files.Fileサブクラスのdef set_timestamp(self, timestamp_data, timestamp_status, context)に格納する場合)はTrueとする (デフォルトはFalse)
なお、def set_timestamp(self, timestamp_data, timestamp_status, context) の定義方法は dropboxbusinessアドオンの DropboxBusinessFileクラス を参考にしてください。
また、タイムスタンプの付加処理はストレージのAPI呼び出しやタイムスタンプの付加処理などI/Oを伴うため、viewsモジュール内の関数など、リクエストハンドラとして振る舞う関数中で処理を行ってしまうと、他のハンドラが待たされる要因になります。 このような状況に対応するため、RDMではCeleryによるワーカーが用意されています。 関数をCeleryタスクとして定義することで、時間がかかる処理はワーカーに委譲することができます。 例えばDropbox Businessアドオンでは以下のように定義しています。
...
from framework.celery_tasks import app as celery_app
...
@celery_app.task(bind=True, base=AbortableTask)
def celery_check_updated_files(self, team_ids):
...
呼び出し側では以下のように関数を実行することで、ワーカーに処理を任せ、自身の処理を続行することができます。
utils.celery_check_updated_files.delay(team_ids)
ここでは、 myminio という識別名のアドオンの実装を例に説明します。アドオンの完全名は My MinIO とします。
アドオン名は様々な場所に埋め込まれています。アドオン名を変更したい場合は、以降で追加・変更するファイル名やコードの myminio 、 My MinIO 、 MyMinIO という文字列を変更してください。
My MinIOアドオンは、AWS S3互換サービスと接続するアドオンなので、Amazon S3アドオンやS3 Compatible Storageアドオンを参考に実装することができます。Amazon S3アドオンやS3 Compatible Storageアドオンとの違いは以下の通りです。
- My MinIOアドオンは簡単のため、アップロードの暗号化機能を実装しない。
- Amazon S3アドオンやS3 Compatible Storageアドオンは、アカウントごとに接続するサービスやLocationなどを選択できるが、My MinIOはサービス側で指定した特定のMinIOサービスのみを扱う。
スケルトン アドオンと同様に、アドオンの実装例(osf.io/addon/)を addons/myminio ディレクトリにコピーします。
スケルトン アドオンと同様に、RDMのコードをいくつか変更します。
- addons.json
- 変更例のサンプル: addons.json
- framework/addons/data/addons.json
- 変更例のサンプル: addons.json
- Dockerfile
- 変更例のサンプル: Dockerfile
- api/base/settings/defaults.py
- 変更例のサンプル: defaults.py
api/base/settings/defaults.py は、 INSTALLED_APPS の他に、 ADDONS_FOLDER_CONFIGURABLE 、 ADDONS_OAUTH にもアドオン名を追加します。変更例サンプルでは、Amazon S3アドオンに合わせて、スケルトン アドオンとは異なる方法で設定しています。
INSTALLED_APPS += ('addons.myminio',)
ADDONS_FOLDER_CONFIGURABLE.append('myminio')
ADDONS_OAUTH.append('myminio')
他にもストレージアドオンでは、 website/static/storageAddons.json にも設定を追加する必要があります。
変更例は storageAddons.json を参照してください。
"myminio": {
"fullName": "My MinIO",
"externalView": false
},
externalViewをtrueに設定すると、FileViewerでファイルの外部ページリンクボタンが表示されるようになります。リンクを正しく動作させるには、WaterButlerのProviderを修正する必要があります。詳細はGoogleDriveの実装などを参照してください。
FileViewerで、フォルダの操作はできるがファイルの操作ができない場合は、
storageAddons.jsonの設定が漏れている可能性があります。
makemigrations コマンドを実行して、Migrationsファイルを作成します。
$ docker compose run --rm web python3 manage.py makemigrations
上記の出力中に以下のような出力が現れれば成功です。ストレージアドオンの場合、 osf/migrations と addons/myminio/migrations の2つのディレクトリ内にPythonファイルが作成されます。
osf/migrations配下に作成されるファイル名は、作成日時やRDMのバージョンによって異なります。
Migrations for 'osf':
osf/migrations/0214_auto_20201001_0007.py
- Create proxy model MyMinIOFileNode
- Alter field type on basefilenode
- Create proxy model MyMinIOFile
- Create proxy model MyMinIOFolder
Migrations for 'addons_myminio':
addons/myminio/migrations/0001_initial.py
- Create model NodeSettings
- Create model UserSettings
- Add field user_settings to nodesettings
国際化メッセージファイルの定義ファイルは以下のコマンドで生成することができます。
このセクションの実施時は各サービスを停止状態にしてください。
# メッセージ定義JSONなどをJavaScriptファイルへと変換する。各サービスが停止している状態で実施する。
$ docker compose run --rm web invoke assets
# メッセージ定義テンプレートファイル website/translations/js_messages.pot を更新する
$ docker compose run --rm web pybabel extract -F ./website/settings/babel_js.cfg -o ./website/translations/js_messages.pot .
# メッセージ定義ファイル website/translations/ja/LC_MESSAGES/js_messages.po を更新する
$ docker compose run --rm web pybabel update -i ./website/translations/js_messages.pot -o ./website/translations/en/LC_MESSAGES/js_messages.po -l en
$ docker compose run --rm web pybabel update -i ./website/translations/js_messages.pot -o ./website/translations/ja/LC_MESSAGES/js_messages.po -l ja
変更例のサンプル js_messages.po を参考に日本語メッセージを追加してください。
#: website/static/js/logActionsList_extract.js:246
msgid "${user} linked the My MinIO bucket ${bucket} to ${node}"
msgstr "${user}がMy MinIOバケット(${bucket})を${node}にリンクしました"
#: website/static/js/logActionsList_extract.js:247
msgid "${user} unselected the My MinIO bucket ${bucket} in ${node}"
msgstr "${user}が${node}のMy MinIOバケット(${bucket})の選択を解除しました"
...
このメッセージを追加後、assetsサービスの再起動時にメッセージ定義が反映されます。
以下のコマンドで、OSF.ioに追加したMy MinIOアドオンのユニットテストを実行できます。
$ docker compose run --rm web invoke test_module -m addons/myminio/tests/
Providerの実装例(waterbutler/provider/) を waterbutler/providers/myminio にコピーします。また、Providerのテストコード例(waterbutler/tests) を tests/providers/myminio にコピーします。
setup.py に、アドオンのエントリポイント定義を追加します。setup() 関数の引数 entry_points に指定するDictionaryの waterbutler.providers キーに指定する配列に、以下を追加します。
'myminio = waterbutler.providers.myminio:MyMinIOProvider',
変更例はサンプル setup.py を参照してください。
変更したコードを使うよう、RDMの docker-compose.override.yml で WaterButler のサービスの volumes を指定します。
例えば、以下のようなディレクトリ階層の場合は、
.
├── RDM-osf.io
│ ├── docker-compose.override.yml
│ └── docker-compose.yml
└── RDM-waterbutler
RDM-osf.io/docker-compose.override.yml の内容を以下のようにします。
version: "3.4"
services:
wb:
volumes:
- ../RDM-waterbutler:/code
wb_worker:
volumes:
- ../RDM-waterbutler:/code
wb_requirements:
volumes:
- ../RDM-waterbutler:/code
以下のコマンドで、WaterButlerに追加したMy MinIOアドオン Providerのユニットテストを実行できます。
$ docker compose run --rm wb invoke test --provider myminio
My MinIOアドオンの動作確認をしてみましょう。
接続先のMinIOサービスを起動します。
docker run -p 9001:9000 \
-e "MINIO_ACCESS_KEY=minioadmin" \
-e "MINIO_SECRET_KEY=minioadmin" \
minio/minio server /data
RDMと同じコンピュータで実行する場合、9000番ポートが競合してしまうので、MinIOサービスのホストへの割り当てポートは9001などに設定します。
MINIO_ACCESS_KEY と MINIO_SECRET_KEY は認証に使うキーです。適宜書き換えてください。
その他MinIOに関する詳しい説明はMinIOのドキュメントを参照してください。
OSF.io Addonの設定をします。 addons/myminio/settings/local-dist.py を addons/myminio/settings/local.py にコピーし、 HOST プロパティに先ほど起動したMinIOサービスのホスト名を指定します。
HOST = '192.168.168.167:9001'
RDMとMinIOサービスを同じ環境で実行している場合は、ループバックエイリアスを指定します。
マイグレーションを実行し、Migrations定義をPostgreSQLサービスに反映します。
$ docker compose run --rm web python3 manage.py migrate
WaterButlerに追加したProviderを有効にするために、 wb_requirements を起動します。
$ docker compose up wb_requirements
変更したファイルに関連するサービスを再起動します。
$ docker compose restart assets web api wb
これでサービスへの反映は完了です。
My MinIOアドオンを試すには、以下のような操作を実施します。
- RDM Web UIにアクセスする
http://localhost:5000 - ユーザ設定ページを開く
- Configure add-on accountsページを開く
- My MinIOアドオンの認証情報を設定をする
認証情報の設定ダイアログが表示されるので、MinIOサービスの MINIO_ACCESS_KEYとMINIO_SECRET_KEYを、それぞれ、Access KeyとSecret Keyフォームに入力して保存します。
成功すれば、切断ボタンと利用しているプロジェクトリストが表示されるエリアが表示されます。

- 適当なプロジェクトを作成する
- Add-onsページを開く
- My MinIOアドオンを有効化する

- My MinIOアドオンの設定をする
Import Account from Profileボタンから、アドオンの設定を行います。 接続に成功すると、ルート直下のフォルダリストが表示されるので、プロジェクトに紐付けるフォルダを選択し、保存します。このページでフォルダを作成することもできます。
これで、作成したプロジェクトでMy MinIOアドオンが使えるようになりました。 プロジェクトページのFiles ウィジェットやFilesページから、My MinIOサービスに対してフォルダの作成やファイルのアップロード、削除、ダウンロードなどができるようになるはずです。
以上でMy MinIOアドオンの動作確認は完了です!



