new_cfoundry gemを使って、Cloud Foundryの情報を操作する

この記事は[Cloud Foundry Advent Calendar](https://qiita.com/advent-calendar/2017/cloudfoundry) 21日目の記事です。

# この話は何か

Advent Calendarもいよいよ残り5日となり、終わりに近づいてきましたね。
この記事では、new_cfoundry gemというRubyのGemについて解説します。

# new_cfoundry gemとは

　まだCloud Foundryの内部がGoに置き換わっていく前、Cloud FoundryやそのクライアントであるcfコマンドはRubyで書かれていました。
　今はRuby版のcloud foundryやcfコマンドはメンテナンスが終了しており、屋根裏（attic）を意味する[cloudfoundry-attic](https://github.com/cloudfoundry-attic/)というところに置かれています。

　Ruby版cfでは、Cloud Foundryから情報を取得するために[cfoundry](https://github.com/cloudfoundry-attic/cfoundry)というCloud FoundryのAPIをRailsのActiveRecord風に操作できるGemが使われていました。

　私はRailsからActiveRecord風にCloud Foundryを操作したいと思い、cfoundryを利用しています。使っている最中に[OAuth2のトークンの方式が変更された](https://github.com/kirikak2/cfoundry/commit/c87b4af1da30538a0b182fb9d65927f68132ac82)り、[オリジナルのcfoundryには実装されていないAPIを呼び出すメソッドを追加する](https://github.com/kirikak2/cfoundry/commit/329f634d5b4b8f1c307ad5b9947006891e56775f)など、いくつか変更をする必要があったため、Forkして現在も継続して使っています。Forkしたリポジトリは[ここ](https://github.com/kirikak2/cfoundry)にあります。

　また、GitHubだけでなく、rubygemsでも[new_cfoundry](https://rubygems.org/gems/new_cfoundry)という名前で公開しています。

# new_cfoundry gemの使い方

　new_cfoundryを使うにはrubyが必要です。どこまで古いRubyで行けるのかは確認していませんが、Ruby 2.2以降であれば大丈夫だと思います。

## インストール

```
$ gem install new_cfoundry --no-ri --no-rdoc
（中略）
Successfully installed new_cfoundry-4.9.2
```

## 呼び出し

　今回はお試しなのでirbから操作してみましょう。もちろんrubyスクリプトを書いて実行することもできますし、Railsに組み込んでWebアプリにすることもできます。requireするときは、new_cfoundryではなく`cfoundry`で呼び出します。ないとは思いますが、既存のcfoundry gemと名前が被るので、インストールしている人はcfoundry gemを削除しておくか、bundlerを使いましょう。

```ruby
require 'cfoundry'
=> true
```

## Credentialの設定

　Cloud Foundryにアクセスするためには、当然アクセス権限が必要です。
　最終的にOAuth2のトークンがあればよいのですが、cfoundry内でOAuth2トークンを取得することもできます。
　今回はローカルにおなじみのPCFdev環境を構築したので、`https://api.local.pcfdev.io`や`https://uaa.local.pcfdev.io`でアクセスします。これらのアドレスは正式な証明書を持っていないため、`skip_ssl_validation`のオプションを付けて呼び出します。正式な証明書を持っている場合は、この作業は必要ありません。

### cfoundry内でトークンを作成する手順

```ruby
# 第2引数の"cf"は、uaaに登録されているOAuth2のclient_idを指定します。
# cfoundryは元々cf-cliで利用されることを前提としているので、デフォルトは"cf"という名前を使いますが、
# 自分で好きなIDを使ってアクセスすることもできます。
# その場合はuaacなどのツールを使って、事前にclient_idとclient_secretを登録しておく必要があります。
uaa = CFoundry::UAAClient.new("https://uaa.local.pcfdev.io", "cf", skip_ssl_validation: true)
token_info = uaa.authorize(username: "admin", password: "admin")
token = CFoundry::AuthToken.from_uaa_token_info(token_info)
```

また、Omniauth-OAuth2などを使って、事前にトークンを払い出しておき、そのトークンを利用することもできます。
その場合は以下の手順でトークンを作成します。

### 作成済みのトークンを利用する手順

```ruby
# このトークンはOAuth2のフローで取得できるほか、
# ログイン済みのcf-cliからcf oauth-tokenで取得することもできます。
token_text = "bearer eyJhbGciOiJSUzI1NiIsImtp（～中略～）JdfQ.wu9llwJlGU5uQW-EH3JqkBehiP7jBGlRsDZXDTykMvKml76ej-IUXf6kNNJ8LQFGIMlaRIYHFdXRUkifj8aDNUXJLQk"
token = CFoundry::AuthToken.new(token_text)
```

## Clientの作成

　tokenが準備できたら、クライアントを作成します。

```ruby
client = CFoundry::Client.get("https://api.local.pcfdev.io", token, skip_ssl_validation: true)
=> #<CFoundry::V2::Client:0x00007fffd37aaa28 @base=#<CFoundry::V2::Base:0x00007fffd37aaa00 @rest_client=#<CFoundry::RestClient:0x00007fffd37aa9d8 @target="https://api.local.pcfdev.io", @token=#<CFoundry::AuthToken:0x00007fffd37bb968 @auth_header="bearer (中略) fj8aDNUXJLQ", @refresh_token=nil>, @trace=false, @backtrace=false, @log=false>, @client_id=nil, @client_secret=nil>>
```

## Cloud Foundryの情報を操作する

　それでは準備も整ったので、早速情報を読み取ってみましょう。
　cfoundryは元々cfコマンドの内部にあったものですので、当然ですがcfコマンドで出来ることは大体できます。

　取得できる情報はクラス単位でまとめられているので、[ここ](https://github.com/kirikak2/cfoundry/tree/master/lib/cfoundry/v2)から、どのような情報が取得できるかを推察してください。大抵の情報は同じインターフェースで取得・操作できます。
　ここでは、OrgのCRUD操作で一例を示します。

### 一覧取得

```ruby
client.organizations
=> [#<CFoundry::V2::Organization 'a0d120f2-fe31-4b53-a6a8-fdd82c8b8042'>, #<CFoundry::V2::Organization 'aa803c34-766f-4391-88ce-02aae5d42a98'>]
```

　一覧取得では、OAuth2トークンの所有者から見える情報が取得できます。
　上記の例ではadminなので、全ての組織が取得できますが、一般ユーザのトークンはset-org-roleで関連付けられている組織に限定されます。

### 詳細情報の参照

　一覧取得で取得できたインスタンスを1個取り出して、詳細情報を確認してみましょう。

```ruby
org = client.organizations.first
=> #<CFoundry::V2::Organization 'a0d120f2-fe31-4b53-a6a8-fdd82c8b8042'>
org.name
=> "pcfdev-org"

# orgインスタンスの取得は直接GUIDを指定してもOK、第2引数にclientを入れる
org = CFoundry::V2::Organization.new("a0d120f2-fe31-4b53-a6a8-fdd82c8b8042", client)
=> #<CFoundry::V2::Organization 'a0d120f2-fe31-4b53-a6a8-fdd82c8b8042'>
```

　名前が取れました。cfoundryはto_manyというActiveRecordでいうところのhas_manyのような構造が[各モデルに定義されていて](https://github.com/kirikak2/cfoundry/blob/master/lib/cfoundry/v2/organization.rb)、各モデルの関連も取得することができます。例えば、上のpcfdev-orgに関連付けられているユーザを取得する場合は以下のようにします。

```ruby
org.users
=> [#<CFoundry::V2::User '6a0d4de5-a464-4121-a67f-f64f0f8d7ea1'>, #<CFoundry::V2::User 'e219aac9-584a-4ef6-a8fc-12dfecef3489'>]
```

　Railsに馴染みがある人なら、この辺りは感覚的に操作できるのではないかと思います。

### 新規作成

　次は新規作成です。Railsが分かる人なら、ここから先の説明は不要ですよね？（笑）

```ruby
org = CFoundry::V2::Organization.new(nil, client)
=> #<CFoundry::V2::Organization ''>
org.name = "Sample-Org"
=> "Sample-Org"
org.create
=> true
```

 cf-cliの方で確認してみましょう。

```
$ cf orgs
Getting orgs as admin...

名前
pcfdev-org
Sample-Org
system
```

　バッチリ追加されてます！

### 更新

　名前を変更してみましょう。cfoundryでは更新にはupdate!メソッドを使います。
　updateメソッドは存在しないので注意してください。

```ruby
org.name = "Sample-Org-Renamed"
=> "Sample-Org-Renamed"
org.update!
=> true
```

```
$ cf orgs
Getting orgs as admin...

名前
pcfdev-org
Sample-Org-Renamed
system
```

　無事変更されましたね。


### 削除

　最後に削除です。

```ruby
org.delete
=> true
```

```
$ cf orgs
Getting orgs as admin...

名前
pcfdev-org
system
```

　消えました。

## まとめと今後の話

　cfoundryは元々Cloud Foundryコミュニティによってメンテナンスされていましたが、現在はcf-cliがGoに置き換わったため、メンテナンスされないライブラリとなりました。しかし、RubyでCloud Foundryの操作をしたいという人は、私以外にもいるらしく、時々Forkされたり、新しいバージョンのGemをリリースしてほしい、という要求を受けています。

　現在は私の個人リポジトリでメンテナンスしていますが、将来的にはどこかに移管して、もう少ししっかりとした開発体制でやった方がいいかもしれません。new_cfoundryは現在はCloud Foundry v2 APIのみのサポートですが、V2 APIが廃止される前にはV3 APIへの対応もやりたいと考えています。ご協力していただける方を募集中です。