Skip to content

Instantly share code, notes, and snippets.

@morika-t
Created November 29, 2013 10:22
Show Gist options
  • Save morika-t/7703915 to your computer and use it in GitHub Desktop.
Save morika-t/7703915 to your computer and use it in GitHub Desktop.
第15回 Cloud Foundry 輪読会 gcfの簡単なカスタマイズと構成

go-cf改めgcfをカスタムしつつソースを学ぶ

自己紹介

  • 普段はCloud Foundryの業務
  • すきなもの: Music Movie Game

本日はブラックフライデーなのでテンションアップ

Photo: The Birth of Canadian ‘Black Friday’ By blazzzinred [vancityhotshots] http://www.flickr.com/photos/blazinred/11096185214

gcfとは

  • Cloud Foundryにアプリをデプロイする際などに利用するツール
  • 元々はvmcやcfコマンドというコマンドでruby実装だった
  • gcfはGo言語実装
  • つい先日までgo-cfというコマンド名でした
  • 最新版は公式がビルドしたバイナリファイルがダウンロード可能
    • 上記の最新版はversion 6.0.0.rc1
  • cliのtracker

gcfのカスタム

背景

  • 開発をしている際にどのdea上でアプリが動いているか知りたい
  • cfコマンドやvmcではrubyの部分をちょこちょこっと直すだけでOKだった
  • gcfでも同じ部分を直すことでgcfの構成を把握

ゴール

  • gcfのgcf app hogehogeにDEAのIPとPortを表示するようにする

準備

git clone https://github.com/cloudfoundry/cli.git
  • go言語のインストールは、gvmなりで1.1以上のgoを入れておいてください

既存のコマンド挙動を把握

  • cf コマンドの場合
cf stats hogehoge --trace
  • gcfコマンドの場合
CF_TRACE=true ./gcf app hogehoge

前提:各インスタンス毎に起動するDEAのIPとPortがある

CF_TRACE結果

RESPONSE:
HTTP/1.1 200 OK
Content-Length: 282
Content-Type: application/json;charset=utf-8
Date: Fri, 29 Nov 2013 03:01:51 GMT
Server: nginx
X-Content-Type-Options: nosniff
X-Vcap-Request-Id: 8ed08e07-f6e9-40bb-a339-27fb8e08a4c2

{"0":{"state":"RUNNING","stats":{"name":"sinatra","uris":[],"host":"192.168.12.129","port":61001,"uptime":50482,"mem_quota":268435456,"disk_quota":1073741824,"fds_quota":16384,"usage":{"time":"2013-11-29 12:01:51 +0900","cpu":2.894267113919665e-05,"mem":18698240,"disk":56041472}}}}

src/cf/domain.go

domain.goとは

Cloud Foundryに関連したデータ構造の定義がされている

type ApplicationInstance

  • HostとPortの宣言追加
 98 type ApplicationInstance struct {
 99         State     InstanceState
100         Since     time.Time
101         CpuUsage  float64 // percentage
102         DiskQuota uint64  // in bytes
103         Host      string
104         Port      int
105         DiskUsage uint64
106         MemQuota  uint64
107         MemUsage  uint64
108 }

src/cf/api/app_summary.go

app_summary.goとは

src/cf/apiの下が各コマンドを実行した際のapiとやり取りする部分
trace結果の並びでdisk_quotaと近い並びになっているので同じように記載

type InstanceStatsApiResponse

  • 132と133行目をDiskQuotaの真似をして記述
128 type StatsApiResponse map[string]InstanceStatsApiResponse
129
130 type InstanceStatsApiResponse struct {
131         Stats struct {
132                 Host      string `json:"host"`
133                 Port      int    `json:"port"`
134                 DiskQuota uint64 `json:"disk_quota"`
135                 MemQuota  uint64 `json:"mem_quota"`
136                 Usage     struct {
137                         Cpu  float64
138                         Disk uint64
139                         Mem  uint64
140                 }
141         }
142 }

func (repo CloudControllerAppSummaryRepository) updateInstancesWithStats

  • v2/apps/アプリ名/statsで情報を取得
  • domainで定義したHostとPortで値をとれるようにstatsの結果を格納
    • instance.Hostとinstance.Portが domainのApplicationInstanceで宣言したもの
    • v.Stats.Hostとv.Stats.Portがtype InstanceStatsApiResponse struct {Stats struct {

前半処理部分

144 func (repo CloudControllerAppSummaryRepository) updateInstancesWithStats(app cf.Application, instances []cf.ApplicationInstance) (updatedInst []cf.Applicati    onInstance, apiResponse net.ApiResponse) {
145         path := fmt.Sprintf("%s/v2/apps/%s/stats", repo.config.Target, app.Guid)
146         statsResponse := StatsApiResponse{}
147         apiResponse = repo.gateway.GetResource(path, repo.config.AccessToken, &statsResponse)
148         if apiResponse.IsNotSuccessful() {
149                 return
150         }

後半処理部分

152         updatedInst = make([]cf.ApplicationInstance, len(statsResponse), len(statsResponse))
153         for k, v := range statsResponse {
154                 index, err := strconv.Atoi(k)
155                 if err != nil {
156                         continue
157                 }
158
159                 instance := instances[index]
160                 instance.CpuUsage = v.Stats.Usage.Cpu
161                 instance.DiskQuota = v.Stats.DiskQuota
162                 instance.DiskUsage = v.Stats.Usage.Disk
163                 instance.MemQuota = v.Stats.MemQuota
164                 instance.MemUsage = v.Stats.Usage.Mem
165                 instance.Host = v.Stats.Host
166                 instance.Port = v.Stats.Port

src/cf/commands/application/show_app.go

show_app.goとは

コマンドを実際にたたいた際の結果表示部分

show_app.go テーブルのヘッダー部分

  • tableのdiskの後に"host:port"を追加
 80         table := [][]string{
 81                 []string{"", "status", "since", "cpu", "memory", "disk", "host:port"},
 82         }

show_app.go 実際の値部分

  • fmt.Sprintfのinstance.DiskUsageの下に追加
 84         for index, instance := range summary.Instances {
 85                 table = append(table, []string{
 86                         fmt.Sprintf("#%d", index),
 87                         coloredInstanceState(instance),
 88                         instance.Since.Format("2006-01-02 03:04:05 PM"),
 89                         fmt.Sprintf("%.1f%%", instance.CpuUsage),
 90                         fmt.Sprintf("%s of %s", formatters.ByteSize(instance.MemUsage), formatters.ByteSize(instance.MemQuota)),
 91                         fmt.Sprintf("%s of %s", formatters.ByteSize(instance.DiskUsage), formatters.ByteSize(instance.DiskQuota)),
 92                         fmt.Sprintf("%s:%d", instance.Host, instance.Port),
 93                 })
 94         }	

テストも直してみる

src/cf/commands/application/show_app_test.go

show_app_test.goについて

  • src/cf/commands/application/show_app.goと同じパス
  • テスト対象_test.goという命名
  • 今回は表示部分のみにテストを追加

変更箇所(テストのダミーデータ設定部分)

65         instances := []cf.ApplicationInstance{
 66                 cf.ApplicationInstance{
 67                         State:     cf.InstanceRunning,
 68                         Since:     time1,
 69                         CpuUsage:  1.0,
 70                         DiskQuota: 1 * formatters.GIGABYTE,
 71                         DiskUsage: 32 * formatters.MEGABYTE,
 72                         Host:      "192.168.1.200",
 73                         Port:      12345,
 74                         MemQuota:  64 * formatters.MEGABYTE,
 75                         MemUsage:  13 * formatters.BYTE,

変更箇所(expect部分)

105         assert.Contains(t, ui.Outputs[7], "#0")
106         assert.Contains(t, ui.Outputs[7], "running")
107         assert.Contains(t, ui.Outputs[7], "2012-01-02 03:04:05 PM")
108         assert.Contains(t, ui.Outputs[7], "1.0%")
109         assert.Contains(t, ui.Outputs[7], "13 of 64M")
110         assert.Contains(t, ui.Outputs[7], "32M of 1G")
111         assert.Contains(t, ui.Outputs[7], "192.168.1.200:12345")

テストの実行の仕方

  • cli直下でbin/testを実行
    • 中でgo fmtが入っているのでコミット後に実施するとソースが変わる可能性があるので注意
    • インデントはタブで入れる等を調べずに書いていたのではまった
git submodule update --init --recursive
./bin/test

テスト結果

ok      cf      0.027s
ok      cf/api  13.024s
ok      cf/app  7.787s
ok      cf/commands     0.039s
ok      cf/commands/application 4.058s
ok      cf/commands/buildpack   0.033s
ok      cf/commands/domain      0.033s
ok      cf/commands/organization        0.033s
ok      cf/commands/route       0.041s
ok      cf/commands/service     0.039s
ok      cf/commands/serviceauthtoken    0.035s
ok      cf/commands/servicebroker       0.034s
ok      cf/commands/space       0.033s
ok      cf/commands/user        0.038s
ok      cf/configuration        0.015s
ok      cf/formatters   0.011s
ok      cf/net  0.324s
ok      cf/requirements 0.025s
ok      cf/terminal     0.015s

 Vetting packages for potential issues...

その他

  • 現状の構成概要とコマンド実装追加の良いサンプル
    • src/cf/commands/space/create_space.goとのこと
  • ファイルの命名等
    • コマンドファイルとメソッドはCRUDに合わせて
    • spaceコマンドの場合はCreateSpace, ListSpaces等

階層構造

src/
 cf/
  api/ ccng等との実際のapiのやり取り部分
  app/
  commands/ リソース毎にコマンドのファイルが分かれている
  configuration/
  formatters/
  net/
  requirements
  terminal

今回のサンプル

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment