ConoHaオブジェクトストレージ(OpenStack Swift)にファイルをアップロードする(Ruby編)

#ConoHaオブジェクトストレージ概要
VPSサービスであるConoHaにオブジェクトストレージのサービスがリリースされました。

https://www.conoha.jp/blog/tech/2642.html

ConoHa VPSはAWS互換のOpenStackで作られておりそのOpenStackのSwiftサーバー(AWS S3相当の機能)部分が一般ユーザーに公開されたことになります。

ConoHa VPSとは独立して立っているため、自宅PCからConoHaオブジェクトストレージにアップロードしたり、ConoHaVPSからConoHaオブジェクトストレージにアップロードしたりできます。

![CBIMG001.png](https://qiita-image-store.s3.amazonaws.com/0/36931/9fa4a1d5-973b-6342-f4ad-6d5fbde189b5.png)

AWSに対するアドバンテージは**450円 / 100GBでリクエスト数、転送量無料**な所でしょうか。


#OpenStack Swift概要
OpenStackはオープンソースなのですでに資料が相当出回っています。
http://www.valinux.co.jp/technologylibrary/document/cloud/openstack0001/

Swiftの機能はREST APIで公開されており丁寧なマニュアル(curlコマンド付！)も公開されています。
http://docs.openstack.org/api/openstack-object-storage/1.0/content/large-object-creation.html

# RubyでOpenStack用のSwiftモジュールを作ってみる

すでにOpenStack用のライブラリやらgemサイトに接続モジュールはありますがREST API程度の接続ライブラリであれば書いたほうが早いので書いてみました。

https://github.com/AKB428/rabbit_swift

## まずConoHaオブジェクトストレージの接続情報を用意する
![CBIMGsfdsf003.png](https://qiita-image-store.s3.amazonaws.com/0/36931/750acfcd-b1b5-5c65-a7bf-d0647d32f8cf.png)

ConoHaオブジェクトストレージ(というかOpenStack Swift）には以下の情報が必要になります。

- テナントID
- テナント名
- ユーザー名
- API Auth URL
- オブジェクトストレージエンドポイント
- パスワード

##認証サーバー(API Auth URL)に接続しトークンを取得する

```rb
def initialize(opt)
  @auth_url = opt['auth_url']
  @tenantName = opt['tenantName']
  @username = opt['username']
  @password = opt['password']
end

def get_token
  body = build_auth_json
  http_client = HTTPClient.new
  response = http_client.post_content(@auth_url, body,   'Content-Type' => 'application/json')
  response_json_body = JSON.load(response)
  response_json_body['access']['token']['id']
end

 def build_auth_json
  auth_json = {
    auth: {
      tenantName: @tenantName,
      passwordCredentials: {
      username: @username,
      password: @password
    }
   }
 }
 JSON.dump(auth_json)
end
```

解説すると
initializeの引数に認証情報を渡す。
build_auth_json関数でリクエストに必要なJSONに整形する。
認証URLから返却されたJSONをハッシュ化し**['access']['token']['id']**を取得する。（これがトークンID）
認証サーバーから返却されるJSONにはトークンの有効期限などトークンをリフレッシュする時のための情報も入っているため、ソフトウェアにリフレッシュトークンの機能を実装する時はそれを利用する。

##トークンを利用してサーバーにファイルをアップロードする

###ファイルをOpenStack Swiftサーバーにアップロードする時

```rb
     file = "アップロードするファイルのフルパス"
     target_url = "オブジェクトストレージエンドポイント/コンテナ名" + "ファイル名"
     auth_header = {
          'X-Auth-Token' => token #取得したトークン
      }
      http_client = HTTPClient.new
        File.open(file_path) do |file|
          @res = http_client.put(URI.parse(URI.encode(target_url)), file, auth_header)
        end
```

ファイルをアップロードする時のポイントは
1. HTTPヘッダーに ** X-Auth-Token ** 情報を付与する
2. 送信先のオブジェクトストレージエンドポイントにはアップロード先のファイル名も付与する必要がある

特にオブジェクトストレージのエンドポイントにファイル名を付与しないとアップロードは成功するがHTTP Status 202が返却され、ConoHaオブジェクトストレージのコントロールパネルから見てもファイルが生成されてない珍現象になる。

###フォルダをOpenStack Swiftサーバにアップロードする時
```rb
      target_url = "オブジェクトストレージエンドポイント" + "作成したいフォルダ名"
      if File::ftype(file_path) == 'directory'
        auth_header = {
            'X-Auth-Token' => token,
            'Content-Type' => 'application/directory', #必要！
            'Content-Length' => 0 #必要！
        }
        @res = http_client.put(URI.parse(URI.encode(target_url)), file_path, auth_header)
      }
```

フォルダをアップロードする際のポイントは
1. HTTP ヘッダーのContent-Type'を**'application/directory'**にする
2. HTTP ヘッダーのContent-Length'を**0**にする

これを実行するとフォルダだけサーバーに作成される。
フォルダの中身までアップロードしたい場合はまずフォルダをSwiftサーバーに作成しその後、中のファイルリストを再帰でぐるぐるアップロードする形になる。

## 作ったモジュールを利用してファイルをアップロードしてみる。
https://rubygems.org/gems/rabbit_swift

Gemfileに`rabbit_swift`を記述してbundle install。

コマンドラインソフトを想定して以下のように記述

```rb:swift_client.rb
   src_file_path = ARGV[0]
   dest_url = ARGV[1]

   swift_conf = {
     auth_url: "https://ident-r1nd9999.cnode.jp/v2.0/tokens",
     tenantName: "1234567",
     username: "chino",
     password: "password"
    }

    #Swiftクライアントを作成
    rabbit_swift_client = RabbitSwift::Client.new(swift_conf);
    #トークンを取得
    token = rabbit_swift_client.get_token

    #ファイルをアップロード
    status = rabbit_swift_client.upload(token, dest_url, src_file_path)

    #ステータスをチェック
    if (status == RabbitSwift::Client::UPLOAD_SUCCESS_HTTP_STATUS_CODE) 
      puts "upload success!"
    end 
```

設定ファイルがベタ書きなのがいけてないですが、上記のコードで
``swift_client.rb kawaii.mp4 https://objectstore-r9nd9999.cnode.jp/v1/1231314i12313123/my_folder_name``

などのようにすればkawaii.mp4がmy_folder_nameにアップロードされます(my_folder_nameコンテナは予めConoHaのコントロールパネルで作成する必要があります)。

### ファイルがアップロードされたか確認する。
ConoHaのコントロールパネルかGUIソフトウェアの**Cyberduck**を使用します。
https://cyberduck.io/

ConoHaのコントロールパネルではファイル数がある一定数を超えると表示されなくなる仕様っぽいのでConoHaのオブジェクトストレージを利用する方はしばらくはCyberduckが必須だと思われます。


![CBIMGsfdsf002.png](https://qiita-image-store.s3.amazonaws.com/0/36931/d5f7cf5c-bb14-0e58-81e5-2eb05719dd32.png)


こういう感じでFTPソフトウェアのようなインターフェースでSwiftサーバーのファイルのアップロードダウンロードができるソフトです。

バッチでいっきにファイルをアップロード/ダウンロードする時はプログラムを書いて、ちまちまファイルを上げたり落としたりする時は**Cyberduck**を使用するのがいいでしょう。


##作成したコンソールソフトでいっきにファイルをアップロードする

https://github.com/AKB428/Chino

前述したフォルダ内のファイルを再帰でアップロードする機能を加えたものを作りました。
以下のような感じで打てばファイルをいっきにアップロードできます。


``bundle exec ruby chino.rb -s /Users/Image/Nico/Directory/ -d https://objectstore-r1nd1111.cnode.jp/v1/99a2200c0a1e4c68b766f5e46527199c/test``