2018年1月31日水曜日

mineo.jp のメールを Mac の mail.app で見る方法

概要

Mineo に登録すると mineo.jp ドメインのメールアドレスが 1 つもらえます
Mac の mail.app でも普通にメールの送受信ができるので設定方法を紹介します

環境

  • macOS 10.13.2
  • mail.app 11.2 (3445.5.20)

アカウントの追加を開く

mail.app を開いたら

メール -> 環境設定 -> アカウント

と進みます

mineo_mail1.png
左下に「+」があるので選択しましょう

mineo_mail2.png
その他のメールアカウントを選択して次に進みます

mineo.jp のアカウント情報を入力する

新規メールアカウント登録画面になるのでメールアドレスとパスワードを入力します
ここで入力するアドレスとパスワードは Mineo から払い出されたものを使いましょう
メールアカウントとパスワードは Mineo のマイページから「ご契約内容照会」で確認できます
mineo_mail3.png

一旦サインインしましょう
たぶんエラーになり受信メールサーバと送信メールサーバを設定する画面になるので入力していきます

  • アカウントの種類 -> IMAP
  • 受信用メールサーバ -> imaps.mineo.jp
  • 送信用メールサーバ -> smtps.mineo.jp

mineo_mail4.png

これで再度サインインするとメールサーバにログインできメールの送受信ができるようになります

動作確認

こんな感じでメールが表示されると思います
メールサーバ側に残っているメールのみ受信できるのでサーバから削除されているメールは受信できません
mineo_mail5.png

最後に

mail.app で mineo.jp のメールを受信する方法を紹介しました
IMAP と SMTP が設定できるメールソフトであれば何でも受信できると思います

2018年1月30日火曜日

Ruby で Amazon ECS (Advertising API) をサクっと使ってみる

概要

Amazon ECS (ECommerceService) はショッピングサイトの Amazon の情報を検索することができる API です
今回は Ruby から API をコールして商品情報を取得してみました

環境

  • macOS 10.13.2
  • Ruby 2.4.1p111
  • amazon-ecs 2.5.0

ライブラリインストール

  • bundle init
  • vim Gemfile
gem "amazon-ecs"
  • bundle install --path vendor

Nokogiri のインストールでこける場合は以下のコマンドを実行後、再度 bundle install してください

  • brew install libxml2
  • bundle config build.nokogiri --use-system-libraries --with-xml2-include=$(brew --prefix libxml2)/include/libxml2

必要な情報の取得

AWS のアクセスキー/シークレットアクセスキーおよび Amazon アソシエイト用のトラッキング ID を取得します

アクセスキーの取得

https://affiliate.amazon.co.jp/assoc_credentials/home にアクセスして「認証キーを管理する」を選択します
advertising_api1.png

すると AWS の認証画面になるので Amazon アソシエイトにログインした ID/PW でログインします
ログインが完了すると IAM と呼ばれる AWS のアカウントを管理する画面になるので、そこで「アクセスキー (アクセスキー ID とシークレットアクセスキー) 」を選択します
すでにキーがあればそれを使っても OK ですがない場合は作成してください
ここで表示されるアクセスキーとシークレットアクセスキーは忘れないようにしましょう
シークレットアクセスキーはこの画面からは確認できないので忘れると再発行する必要があります
advertising_api2.png

トラッキング ID の取得

トラッキング ID は商品したのがどのアカウントか紐付けるための情報です
Amazon アソシエイトにログインして上段のアカウントから「トラッキング ID の管理」を選択します
advertising_api3.png

するとトラッキング ID の一覧が表示されます
まだない場合は新規で作成すれば OK です
advertising_api4.png

サンプルコード

  • vim test_ecs.rb
require 'amazon/ecs'

Amazon::Ecs.configure do |options|
  options[:AWS_access_key_id] = 'your access key'
  options[:AWS_secret_key] = 'your secret key'
  options[:associate_tag] = 'traking ID'
end

res = Amazon::Ecs.item_search(
  'figma',
  :response_group => 'Large',
  :search_index => 'All',
  :country => 'jp'
)

res.items.each do |item|
  puts item
end
  • bundle exec ruby test_ecs.rb

すると XML がづらーっと表示されます
デフォルトだと 10 件分取得できるようです
例えばタイトルと価格を取得したい場合は以下のようにします

res.items.each do |item|
  puts item.get('ItemAttributes/Title')
  puts item.get('ItemAttributes/ListPrice/Amount')
end

またアフィリエイトリンクを生成する場合は以下のようにするとできるようです

res.items.each do |item|
  puts item.get('DetailPageURL')
end

検索に関するオプション :response_group に関して指定できる値はこちらを参照してください
また、:search_index に関してはこちらを参照してください

503 You are submitting requests too quickly. Please retry your requests at a slower rate. 対応

これは単純にリクエストしすぎだよーというエラーになります
公式を見ると 1req/1sec まで OK とあるようですが、その間隔でリクエストすると当然のようにエラーになります
詳細な理由はわかりませんがエラーが発生する場合は 1 分ほどおいてからリクエストすると良いと思います

最後に

Ruby で Amazon Advertising API を試してみました
試すのは非常に簡単かなと思います
ただ頻繁に 503 エラーが発生するのでむしろ 503 エラーと格闘する時間のほうが長いかなと思います

参考サイト

2018年1月29日月曜日

VMware Admiral で API を使ってテンプレートを docker-compose.yml としてエクスポートする方法

概要

Admiral は作成したテンプレートをエクスポートできる機能があります
管理画面からはテンプレートの一覧でエクスポートボタンを押せば YAML ファイルがダウンロードできます
API でも取得することができたのでその方法を紹介します

環境

  • Ubuntu 16.04.3
  • Admiral v1.3.0

API でテンプレートを取得する

docker-compose.yml を取得する場合はこんな感じでコールします

  • curl -H 'localhost:8282/resources/composite-templates/?selfLink=/resources/composite-descriptions/877dd4190ad5dc75563935eeacdb0&format=docker'

877dd4190ad5dc75563935eeacdb0 の部分はテンプレートの ID を入力してください
これで以下のような YAML 情報がテキストで取得できます

---
version: "2"
services:
  web:
    image: "registry.hub.docker.com/library/nginx"
    restart: "no"

上記のテンプレートは nginx イメージで作成したコンテナ定義を 1 つ追加した場合の YAML になります
ちなみにユーザ認証を有効にした場合は x-xenon-auth-token ヘッダを付与してコールしてください

また最後の format=docker を外すとテンプレート自体の定義をエクスポートすることができます
エクスポートした定義は Admiral 上にインポートすることでテンプレートとして使うことができます

最後に

Admiral でテンプレートから docker-compose.yml をエクスポートする方法を紹介しました
結構便利な機能かなと思います
ただ docker-compose のバージョンが 2 までしか対応してないのでバージョン 3 以上に対応する場合は取得したあと自分でコンバートする必要があります

参考サイト

2018年1月26日金曜日

ruby の RestClient でクエリストリング中に配列を含める方法

概要

前回 curl を使ってクエリストリング中に配列を入れてサーバに送信しました
今回は ruby の RestClient を使って配列を送信してみました

環境

  • maxOS 10.13.2
  • Ruby 2.4.1p111
  • RestClient 1.8.0

curl

以下の curl でのリクエストを RestClient からもやってみたいと思います

リクエスト

  • curl 'localhost:4567/?Hoge[]=hoge1&Hoge[]=hoge2' --globoff

レスポンス

hoge1 hoge2

RestClient

RestClient でやる場合はこんな感じです

  • vim app.rb
require 'rest-client'

query = '/?Hoge[]=hoge1&Hoge[]=hoge2'
ret = RestClient.get "localhost:4567#{query}"

puts ret

これで curl と同じ結果を取得することができます
文字列でクエリストリングを作成して、それをそのまま URI 中に含めているだけです
文字列として扱うので若干面倒なケースもありますがシンプルな方法かなとは思います

params: を使って送信することもできるのですがハッシュで宣言する必要がありキーが重複してしまうため 1 つしか値を送信することができません

p = {
  :params => {
    'Hoge[]' => 'hoge1',
    'Hoge[]' => 'hoge2',
  }
}
ret = RestClient.get 'localhost:4567', p

これだと hoge2 だけしか送信してくれません

最後に

RestClient で配列を送信する方法を紹介しました
POST や PUT であればリクエストボディを設定できるのでそっちで配列なりを送信してあげれば OK です

今回は GET + クエリストリングの場合に配列を送信する方法を紹介しました

2018年1月25日木曜日

VMware Admiral で docker-compose 的なことをしてみる

概要

VMware Admiral には docker-compose 的なことを実現するためにテンプレートという機能があります
今回はテンプレートを使った複数コンテナの起動方法を紹介します

環境

  • Ubuntu 16.04.3
  • docker 17.12.0-ce
  • Admiral v1.3.0

プロジェクトの作成

Admiral にはデフォルトで「default-project」というプロジェクトが用意されています
今回はそのプロジェクトを使って薦めます
なので、プロジェクトは必要であれば作成してください

作成の仕方は

管理 -> プロジェクト -> プロジェクトの追加

から新規でプロジェクトを作成できます

クラスタの登録

次にクラスタを登録しましょう
クラスタの登録には docker host or VCH が必要になります
今回は事前に Ubuntu 上に docker をインストールし tcp でのリモートコントロールを有効にしました

追加するには左メニューから

インフラストラクチャ -> クラスタ -> 新規

から追加できます
admiral_docker_compose1.png

テンプレートの登録

とりあえず空のテンプレートを作成しましょう
左メニューから

ライブラリ -> テンプレート -> テンプレートの作成

で好きな名前を入力しましょう
とりあえずこれで作成できれば OK です
admiral_docker_compose2.png

コンテナ定義の追加

テンプレートが作成できたらその中にコンテナ定義を追加していきましょう
docker-compose 的なことがしたいのでコンテナ定義はそれぞれのコンテナ分作成します
そして今回は redis + golang で書かれた Web アプリを作成したいと思います

redis

まずは redis イメージを使ってコンテナ定義を追加します
作成したテンプレート tpl1 を選択して「コンテナ追加」を選択しましょう
admiral_docker_compose3.png

次のイメージの選択です
Admiral ではデフォルトで DockerHub で公開されているイメージを検索することができます
「redis」で検索すると公式の redis イメージが表示されるのでそれを選択しましょう
admiral_docker_compose4.png

すると更にコンテナ定義の詳細を設定する画面になります
ここは所謂 docker-compose.yml に定義する link や volume、環境変数などを設定する画面になります
redis の定義では 1 つだけ設定を追加します

ネットワーク -> ホスト名

に「redis」と入力しましょう
これを設定することで別のコンテナからホスト名 redis で参照することができるようになります
admiral_docker_compose5.png

あとは「追加」すれば OK です

memo

今度は golang の Web アプリのコンテナ定義を追加します
先ほどと同様にコンテナの追加を選択しましょう
admiral_docker_compose6.png

イメージはこれを使います
該当のイメージを検索して選択しましょう
admiral_docker_compose7.png

そしてコンテナ定義の詳細設定です
まず先ほど作成した redis とリンクさせます

基本 -> リンク-> サービス

で事前に作成しておいた redis 定義を選択します
ちなみにタグですが今回のイメージに ver_golang があるのでこれを選択します
admiral_docker_compose8.png

次のコンテナホスト上で LISTEN するポートを設定します

ネットワーク -> ポートバインド

でホストポートを 80 番にしコンテナポートを 8080 番にします
Web アプリ側はホスト名の設定は不要です
admiral_docker_compose9.png

更に環境変数を設定します
今回の Web アプリは redis の URI を環境変数で設定できるので、そのための環境変数を設定します

環境 -> 環境変数

で名前に REDIS_URL を入力し値に redis://redis:6379/0 と入力します
admiral_docker_compose10.png
これで Web アプリ側の設定は完了です
「追加」で定義を作成しましょう

プロビジョニングする

tpl1 を選択すると作成した 2 つの定義があると思います
このページの右上にプロビジョニングがあるので選択してみましょう
すると作成したコンテナ定義の情報を元にクラスタ上にコンテナを作成してくれます
admiral_docker_compose11.png

動作確認

しばらくするとプロビジョニングが完了します
左メニューのコンテナを選択するとプロビジョニングしたコンテナの一覧が表示されます
admiral_agent はクラスタとして追加したホスト上に必ず作成されるコンテナになります
admiral_docker_compose12.png

memo 側のコンテナを選択するとアプリの URL にアクセスできるのでちゃんとアプリが起動しているか確認してみましょう
admiral_docker_compose13.png

ちなみに追加したクラスタ内のコンテナホストで docker ps を実行するとちゃんとコンテナが起動していることがわかります

CONTAINER ID        IMAGE                        COMMAND                  CREATED              STATUS              PORTS                     NAMES
c9be7f0cff40        kakakikikeke/memo:latest     "/bin/sh -c 'bundle …"   About a minute ago   Up About a minute   0.0.0.0:80->8080/tcp      memo-mcm410-65242387561
e4107fe01381        redis:latest                 "docker-entrypoint.s…"   4 minutes ago        Up 4 minutes        0.0.0.0:32769->6379/tcp   redis-mcm409-65242385176
cad21f08519f        vmware/admiral_agent:1.3.0   "/bin/bash /entrypoi…"   2 hours ago          Up 2 hours          0.0.0.0:32768->4200/tcp   admiral_agent

最後に

VMware Admiral で docker-compose のように複数のコンテナを一度に立ち上げて Web アプリを構築してみました
docker-compose.yml の情報を Admiral に移植するだけなので、すでに docker-compose で動作するものがあれば簡単に Admiral 上で動かすことができると思います

Admiral ではイメージのビルドまでは行えないので作成したイメージは今回のように事前に Dockerhub で公開しておきましょう

いきなりこっちで docker-compose の定義を作成することも可能ですが、まずは手元で docker-compose.yml を作成して Admiral に移植すると良いと思います

Admiral を使うメリットしては

  • コンテナがダウンしたときに自動で新しいコンテナを作成してくれる
  • 最低起動コンテナ数を指定することができ、その数までは自動でスケールアウトしてくれる
  • コンテナホストを追加すれば swarm のようなクラスタが簡単に組める

あたりかなと思います

2018年1月23日火曜日

Sinatra で 500 エラーをハンドリングする方法

概要

前回 not_found を使ってルーティングしていないパスのエラーハンドリングをしました
今回は予期しないエラーが発生したときにすべてのエラーをハンドリングすることができる方法を紹介します

環境

  • macOS 10.13.2
  • Ruby 2.4.1p111
  • Sinatra 2.0.0

サンプルコード

  • vim error.rb
require 'sinatra'
set :show_exceptions, false

get '/' do
  raise
end

error do
  'error'
end

これでアクセスするとクライアント側にはステータスコード 500 で error という文字列だけが返ります
サーバ側には Ruby のエラースタックトレースが表示されます
set :show_exceptions, false これを指定しないとクライアント側にもエラースタックトレースが表示されてしまい本来返却したいレスポンスボディにならないので必ず設定してください

サンプルコード2

  • vim error2.rb
require 'sinatra'
require './my_class.rb'

set :show_exceptions, false

get '/' do
  mc = MyClass.new
  mc.error
end

error do
  'error'
end
  • vim my_class.rb
class MyClass
  def error
    a = nil
    a.each {}
  end
end

MyClass では必ずエラーが発生します
これを Sinatra のコントローラ側でコールしてもちゃんと error が拾ってくれます

最後に

Sinatra で 500 エラーをハンドリングしてみました
とりあえずこれを入れておけば Ruby のエラースタックトレースが返ることはなくなると思います

参考サイト

2018年1月21日日曜日

Sinatra でリクエストヘッダを取得する方法

概要

ボディやクエリストリングは params で取得できます
ヘッダは少し変わった方法でアクセスするのでやり方を紹介します

環境

  • macOS 10.13.2
  • Ruby 2.4.1p111
  • Sinatra 2.0.0

取得方法

  • vim app.rb
require 'sinatra'

get '/' do
  headers = request.env.select { |k, v| k.start_with?('HTTP_') }
  headers.each do |k, v|
    puts "#{k} -> #{v}"
  end
  'ok'
end

こんな感じです
request.env にいろいろと突っ込まれておりその中の HTTP_ から始まる変数がヘッダ情報になります
試しにこれで動かしてヘッダを適当に付与してみると付与したヘッダが出力されると思います

もともとは Sinatra は Rack ベースで作られているのでそれの Rack::Request を使って取得している感じです

2018年1月20日土曜日

Sinatra で @env に instance_variable_set してはいけない

概要

タイトルの通りです
Sinatra::Base ですでに使われているインスタンス変数になるので、これに instance_variable_set を実行しようとするとエラーとなります

環境

  • macOS 10.13.2
  • Ruby 2.4.1p111
  • Sinatra 2.0.0

現象

  • vim app.rb
require 'sinatra'

get '/' do
  val = '@env'
  instance_variable_set("#{val}", [])
end

これでアプリを起動して localhost:4567 にアクセスすると TypeError: no implicit conversion of String into Integer エラーが発生します

対応方法

val = '@env2' などとして別の変数名を使うようにしましょう

最後に

すごいハマリました、、、

2018年1月19日金曜日

curl で配列を送信して Sinatra 側で処理する

概要

タイトルの通りです
使い方がよくわからなかったのですが判明したので紹介します

環境

  • macOS 10.13.2
  • Ruby 2.4.1p111
  • Sinatra 2.0.0

curl で配列を送信する方法

curl 'localhost:4567?array[]=1&array[]=2' --globoff

こんな感じです
ポイントは globoff オプションを付与しないとエラーになる点です

Sinatra 側で受け取る

  • vim app.rb
require 'sinatra'
require 'json'

get '/' do
  ary = params['array']
  ary.join(',')
end

ary 変数はすでに配列として扱うことができます
配列として扱う場合は配列ではない可能性も考慮する必要があります

参考サイト

2018年1月18日木曜日

docker で 2376 ポートを LISTEN する方法

概要

docker で 2376 ポートを LISTEN させることでリモートなコンテナホストとして扱うことができます
いろんな OS で dockerd をリモートからアクセスできるようにしたのでそれぞれの OS でやり方を紹介します

環境

  • Ubuntu 16.04.3
  • CentOS 7.3.1611
  • Linux Photon 3.19.2

CentOS7

  • mkdir /etc/systemd/system/docker.service.d/
  • vim /etc/systemd/system/docker.service.d/docker.conf
[Service]
ExecStart=
ExecStart=/usr/bin/dockerd -H tcp://0.0.0.0:2376 -H unix:///var/run/docker.sock
  • systemctl daemon-reload
  • systemctl restart docker

Ubuntu16.04

  • vim /lib/systemd/system/docker.service

ExecStart の部分を以下のように変更する

ExecStart=/usr/bin/dockerd -H tcp://0.0.0.0:2376 -H unix:///var/run/docker.sock
  • systemctl daemon-reload
  • systemctl restart docker

PhotonOS

  • vi /lib/systemd/system/docker.service

ExecStart の部分を以下のように変更する

ExecStart=/bin/docker -d -s overlay -H tcp://0.0.0.0:2376 -H unix:///var/run/docker.sock
  • systemctl daemon-reload
  • systemctl restart docker
  • ss -an | grep 2376

最後に

とりあえず自分が試した OS だけですが docker で 2376 ポートを LISTEN する方法を紹介しました
基本は設定ファイルを編集する感じです
DOCKER_OPTS 環境変数でハンドリングできる記事の紹介もちらほらありました
その方法でうまくいく場合はそっちのほうが良いかなと思います

環境変数を使う方法でうまくいかない場合は今回紹介した方法を試してみてください

2018年1月17日水曜日

サーバ側で JSON 情報がほしい場合にクエリストリングで JSON を送信させるのはどうなのだろうか

概要

例えばサーバ側で {"array":[{"key1":"value1"},{"key2","value2"]} みたいな情報を扱い場合に、この情報をクライアントからどうやって送ってもらうかを考えます

環境

  • macOS 10.13.2
  • Ruby 2.4.1p111
  • Sinatra 2.0.0

クライアントに JSON を URL エンコードしてもらいそのまま送ってもらう

サーバ側のサンプルコードは以下の通りです

  • vim app.rb
require 'sinatra'
require 'json'

get '/' do
  begin
    ret = JSON.parse(params['json'])
    "parsed json #{ret}"
  rescue
    status 500
    'json parse error'
  end
end

クライアントから送信する JSON 情報を json というパラメータに含めクエリストリングで送信します
もちろん JSON 情報は URL エンコードを掛けてもらいます

  • curl 'localhost:4567?json=%7B%22array%22%3A%5B%7B%22key1%22%3A%22value1%22%7D%2C%7B%22key2%22%3A%22value2%22%7D%5D%7D'

これでサーバ側では受け取った情報をそのまま JSON として扱うことができます
サーバ側の都合にはなりますがコードもすっきりしています
ただ、当然パースできない JSON が飛んでくる可能性もあるので、その場合の rescue 処理は必須になると思います

辛いのはクライアント側になるのでユーザフレンドリな仕様ではないかなとも思います

ほしい値をパラメータ化してサーバ側で JSON を組み立てる

今回の例で言えば key1, value1, key2, value2 をクエリストリングのパラメータとしてそれぞれ送信してもらい、それをサーバ側で受け取って JSON を組み立てます
サーバ側のコードは以下のような感じになります

  • vim app.rb
require 'sinatra'
require 'json'

get '/' do
  key1 = params['key1']
  value1 = params['value1']
  key2 = params['key2']
  value2 = params['value2']
  json = %{{"array":[{"#{key1}":"#{value1}"},{"#{key2}":"#{value2}"}]}}
  ret = JSON.parse(json)
  "parsed json #{ret}"
end

そしてクライアント側から送信する情報は以下のようになります

  • curl 'localhost:4567/?key1=key1&value1=value1&key2=key2&value2=value2'

JSON に含まれるブラケットやコロンが無いのでその部分を URL エンコードする必要はなくなります
そもそもサーバ側のサンプルコードが良くないのもありますが、この場合はパラメータを取得して自分でハッシュを組み立てる感じになると思います

クライアント側からのリクエストはあらかじめ決められたパラメータに値をセットして送信する感じになるのでキレイと言えばキレイかなと思います

それぞれのメリットデメリットを考える

どちらも一長一短かなーという気はしますが一応メリットデメリットを考えます

前者は好きな JSON を自分で組み立ててサーバ側に送信することができます
つまり可変長な値の場合には前者のほうが柔軟に対応できます
一応後者でも可変長なパラメータを持つことはできますがクライアントもサーバサイドもやたら複雑になるだけかなと思うので、その場合には素直に前者を採用したほうが良いかなと思います

ただ前者の場合 URL エンコードすることが必須になります
なので URL が全体として非常に長くなる可能性があります
GET で送信できるパラメータに制限を設けているサービスだともしかしたら上限にぶつかる可能性があります
後者もパラメータが増えれば URL が長くなる可能性があるのはあります

ドキュメントという観点からも考えると後者のほうが少し有利かなと思います
前者の場合は JSON として設定できるフォーマットもちゃんとドキュメントに載せる必要があります
後者の場合は単純に key, value になるので value 側のフォーマットを細かく考える必要がありません (型などは考える必要があるかもしれませんが)
なのでドキュメントという面では前者のほうが少し手間になるかなと思いますが、やり方次第で何とでもなるのでそこまでデメリットではないかなと思います

一番良いのはリクエストボディで JSON を送信する

これは当然の考えだと思います
最近だと REST で実装されている API は多くパラメータなどはリクエストボディに含めて送信するケースがほとんどです
その場合は Content-Type: application/json にして JSON をそのまま送信すれば良いのでクライアント側もサーバ側も簡単です

ただ REST と言えど GET の場合はクエリストリングで処理するケースがほとんどなので、その場合に JSON を送信する必要がある場合は今回のケースを考える必要があるかもしれません

最後に

クエリストリングで JSON 情報を送信しなければいけない場合にどう送信するべきかを考えてみました
最近だと GraphQL など DSL というか key, value ではなくクエリをそのまま投げて検索できるような仕様もあるのでそれを使うのも手だと思います
というか代替方法はいくらでもあると思います

今回の場合はクエリストリングという仕様に縛られた場合にどうするかというケースであることを考慮して貰えればと思います

2018年1月16日火曜日

Sinatra の erb で trim_mode を使う方法

概要

タイトルの通りです
使い方がよくわからなかったのですが判明したので紹介します

環境

  • macOS 10.13.2
  • Ruby 2.4.1p111
  • Sinatra 2.0.0

trim_mode を使った場合

  • vim views/hoge.erb
<a>
  <%- ['a','b','c'].each do |i| -%>
  <hoge><%= i %></hoge>
  <%- end -%>
</a>
  • vim app.rb
require 'sinatra'

get '/' do
  erb :hoge, :trim => '-'
end

結果は以下のようになります

<a>
  <hoge>a</hoge>
  <hoge>b</hoge>
  <hoge>c</hoge>
</a>

ちゃんと 1 行ずつ改行されて表示されています
行末を -%> にすることで改行が無視されます
先頭を <%- にすることでインデントがなくなります

trim_mode を使わない場合

  • vim views/fuga.erb
<a>
  <% ['a','b','c'].each do |i| %>
  <hoge><%= i %></hoge>
  <% end %>
</a>
  • vim app.rb
require 'sinatra'

get '/' do
  erb :hoge
end

こんな感じで改行が trim されずに表示されてしまいます

<a>

  <hoge>a</hoge>

  <hoge>b</hoge>

  <hoge>c</hoge>

</a>

trim を使うタイミング erb 内でスペースやインデントを使用する場合かなと思います
erb で表示する内容がすべて先頭から始まる内容であればわざわざ改行する必要はありません

2018年1月15日月曜日

一度 run したコンテナを削除せずに環境変数を設定する方法

概要

docker run をするときに -e で環境変数を設定することができます
しかし stop -> start や restart するときに -e オプションを使って環境変数を設定することができません
そうするとわざわざコンテナを rm する必要が出てきます
rm しないでも環境変数を設定することができたのでその方法を紹介します

環境

  • Ubuntu 16.04.3
  • docker 17.05.0-ce
  • Admiral v1.3.0

とりあえず起動する

  • docker run -d -p 80:80 --name web nginx

これで HOGE という環境変数を確認してみます

  • docker exec web /bin/bash -c 'echo "${HOGE}"'

当然ですが空文字が返ってきます

コンテナを一旦停止する

起動した nginx コンテナを一旦停止しましょう

  • docker stop web

dockerd も停止する

デーモンも停止する必要があります
(これをしなければいけないのがちょっと残念な感じがします)

  • systemctl stop docker

config.json を直接編集する

コンテナを編集したらコンテナの設定ファイルを直接編集して環境変数を設定しましょう

  • vim /var/lib/docker/containers/837764235c60d5035ec5ad7848b03724e1245d99802edd6cb72a9d8895a453b0/config.v2.json

コンテナ ID の部分は適宜変更してください
この JSON ファイルに Env という定義の部分があるのでそこに環境変数を記載します

"Env":["PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin","NGINX_VERSION=1.13.8-1~stretch","NJS_VERSION=1.13.8.0.1.15-1~stretch","HOGE=hoge"]

改行などインデントなど整形がされていないので少し見づらいですが Env の箇所を見つけて上記のように編集しました

dockerd とコンテナを起動する

停止していたプロセスとコンテナを起動していきます

  • systemctl start docker
  • docker start web

環境変数が設定されているか確認する

先ほどの環境変数を表示するコマンドを再度実行してみましょう

  • docker exec web /bin/bash -c 'echo "${HOGE}"'

すると hoge が返ってくるようになっていると思います

最後に

一度 run で起動したコンテナに対して削除せずに環境変数を設定する方法を紹介しました
もしかするとこんなことをしないでも今後のアップデートで restart 時に設定できるようになるかもしれません

dockerd も停止する必要があるのでプロダクション環境だと他のコンテナにも影響するので使えない場合が多いかもしれません
一応知っておいて損はないと思うので紹介しました

また Docker for Mac の場合は一度 moby にログインしてからファイルを編集する必要があるので注意してください
moby に関してはこちらで紹介しています

参考サイト

2018年1月13日土曜日

VMware Admiral で API を使ってテンプレートを操作する方法

概要

前回 VMware Admiral の REST API を試してみました

今回は更にテンプレートの操作も API でやってみたのでその方法を紹介します

環境

  • macOS 10.13.2
  • docker 17.12.0-ce
  • Admiral v1.3.0

テンプレートを追加する

  • リクエスト
curl -X POST \
'http://localhost:8282/resources/composite-descriptions' \
-H 'x-project: /projects/default-project' \
-H 'content-type: application/json' \
-H 'x-xenon-auth-token: eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJpc3MiOiJ4biIsInN1YiI6Ii9jb3JlL2F1dGh6L3VzZXJzL1lXUnRhVzVBWVdSdGFYSmhiQzVzYjJOaGJBXHUwMDNkXHUwMDNkIiwiZXhwIjoxNTE1NzM1Mjg2fQ.j03awiWf4Esmc_Wb9Tar6fZFVv3g3i48k_JqytPevoA' \
-d '{"name":"test","descriptionLinks":[]}'
  • レスポンス
{
  "name": "test",
  "descriptionLinks": [],
  "tenantLinks": [
    "/projects/default-project"
  ],
  "documentVersion": 0,
  "documentEpoch": 0,
  "documentKind": "com:vmware:admiral:compute:container:CompositeDescriptionService:CompositeDescription",
  "documentSelfLink": "/resources/composite-descriptions/4d9e8d32664228755628ccbb0b7c8",
  "documentUpdateTimeMicros": 1515731735853001,
  "documentUpdateAction": "POST",
  "documentExpirationTimeMicros": 0,
  "documentOwner": "3d0223ea-5e52-4dd6-8c68-5e9ddfb8a443",
  "documentAuthPrincipalLink": "/core/authz/users/YWRtaW5AYWRtaXJhbC5sb2NhbA=="
}

name は実は任意パラメータです
指定しないとランダムの文字列でテンプレート名を作成します
プロジェクトやクラスタは name が必須なのですがテンプレートは違うようです

テンプレートの一覧を取得する

  • リクエスト
curl -X GET \
'http://localhost:8282/templates?q=*' \
-H 'x-project: /projects/default-project' \
-H 'x-xenon-auth-token: eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJpc3MiOiJ4biIsInN1YiI6Ii9jb3JlL2F1dGh6L3VzZXJzL1lXUnRhVzVBWVdSdGFYSmhiQzVzYjJOaGJBXHUwMDNkXHUwMDNkIiwiZXhwIjoxNTE1NzM1Mjg2fQ.j03awiWf4Esmc_Wb9Tar6fZFVv3g3i48k_JqytPevoA'
  • レスポンス
{
  "results": [
    {
      "templateType": "COMPOSITE_DESCRIPTION",
      "name": "test",
      "descriptionLinks": [],
      "tenantLinks": [
        "/projects/default-project"
      ],
      "documentVersion": 0,
      "documentEpoch": 0,
      "documentKind": "com:vmware:admiral:compute:container:CompositeDescriptionService:CompositeDescription",
      "documentSelfLink": "/resources/composite-descriptions/4d9e8d32664228755628ccbb0b7c8",
      "documentUpdateTimeMicros": 1515732089628001,
      "documentUpdateAction": "POST",
      "documentExpirationTimeMicros": 0,
      "documentOwner": "3d0223ea-5e52-4dd6-8c68-5e9ddfb8a443",
      "documentAuthPrincipalLink": "/core/authz/users/YWRtaW5AYWRtaXJhbC5sb2NhbA=="
    }
  ],
  "isPartialResult": false
}

q は必須パラメータです
x-project でプロジェクトを指定しなかった場合はすべてのプロジェクトからテンプレートを取得します

GET /resources/composite-descriptions でも取得できるようですが name などの情報は取得できません

テンプレートの詳細を取得する

  • リクエスト
curl -X GET \
'http://localhost:8282/resources/composite-descriptions/4d9e8d32664228755628ccbb0b7c8' \
-H 'x-xenon-auth-token: eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJpc3MiOiJ4biIsInN1YiI6Ii9jb3JlL2F1dGh6L3VzZXJzL1lXUnRhVzVBWVdSdGFYSmhiQzVzYjJOaGJBXHUwMDNkXHUwMDNkIiwiZXhwIjoxNTE1NzM1Mjg2fQ.j03awiWf4Esmc_Wb9Tar6fZFVv3g3i48k_JqytPevoA'
  • レスポンス
{
  "name": "test",
  "descriptionLinks": [],
  "tenantLinks": [
    "/projects/default-project"
  ],
  "documentVersion": 0,
  "documentEpoch": 0,
  "documentKind": "com:vmware:admiral:compute:container:CompositeDescriptionService:CompositeDescription",
  "documentSelfLink": "/resources/composite-descriptions/4d9e8d32664228755628ccbb0b7c8",
  "documentUpdateTimeMicros": 1515732089628001,
  "documentUpdateAction": "POST",
  "documentExpirationTimeMicros": 0,
  "documentOwner": "3d0223ea-5e52-4dd6-8c68-5e9ddfb8a443",
  "documentAuthPrincipalLink": "/core/authz/users/YWRtaW5AYWRtaXJhbC5sb2NhbA=="
}

テンプレートを削除する

  • リクエスト
curl -X DELETE \
'http://localhost:8282/resources/composite-descriptions/4d9e8d32664228755628ccbb0b7c8' \
-H 'x-xenon-auth-token: eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJpc3MiOiJ4biIsInN1YiI6Ii9jb3JlL2F1dGh6L3VzZXJzL1lXUnRhVzVBWVdSdGFYSmhiQzVzYjJOaGJBXHUwMDNkXHUwMDNkIiwiZXhwIjoxNTE1NzM1Mjg2fQ.j03awiWf4Esmc_Wb9Tar6fZFVv3g3i48k_JqytPevoA'
  • レスポンス
{
  "name": "test",
  "descriptionLinks": [],
  "tenantLinks": ["/projects/default-project"],
  "documentVersion": 1,
  "documentEpoch": 0,
  "documentKind": "com:vmware:admiral:compute:container:CompositeDescriptionService:CompositeDescription",
  "documentSelfLink": "/resources/composite-descriptions/4d9e8d32664228755628ccbb0b7c8",
  "documentUpdateTimeMicros": 1515731950045000,
  "documentUpdateAction": "DELETE",
  "documentExpirationTimeMicros": 0,
  "documentOwner": "3d0223ea-5e52-4dd6-8c68-5e9ddfb8a443",
  "documentAuthPrincipalLink": "/core/authz/users/YWRtaW5AYWRtaXJhbC5sb2NhbA=="
}

コンテナ定義の追加

テンプレートは作成しただけではコンテナのプロビジョニングができません
コンテナ定義を追加する必要があります
他にもボリューム定義やネットワーク定義、クロージャ定義の追加があります
以下はコンテナ定義を追加するリクエストです

  • リクエスト
curl -X POST 'http://localhost:8282/resources/container-descriptions' \
-H 'content-type:application/json' \
-H 'x-xenon-auth-token: ' \
-H 'x-project:/projects/default-project' \
-d '{"image":"registry.hub.docker.com/library/nginx","name":"nginx","command":[],"links":[],"deploymentPolicyId":null,"portBindings":[{"hostPort":"","containerPort":"80"}],"publishAll":true,"hostname":null,"networkMode":null,"networks":{},"volumes":[],"volumesFrom":[],"workingDir":null,"_cluster":1,"restartPolicy":"no","maximumRetryCount":null,"cpuShares":null,"memoryLimit":null,"memorySwapLimit":null,"affinity":[],"env":[],"customProperties":{},"healthConfig":{},"logConfig":{"type":null,"config":{}}}'

基本的にはコンテナを run するための種となる情報を定義します
イメージの情報やポートの情報、環境変数などが定義できます

  • レスポンス
{
  "image": "registry.hub.docker.com/library/nginx",
  "command": [],
  "instanceAdapterReference": "/adapters/docker-service",
  "affinity": [],
  "_cluster": 1,
  "env": [],
  "volumes": [],
  "publishAll": true,
  "portBindings": [{
    "hostPort": "",
    "containerPort": "80"
  }],
  "logConfig": {
    "config": {}
  },
  "links": [],
  "volumesFrom": [],
  "restartPolicy": "no",
  "networks": {},
  "name": "nginx",
  "customProperties": {},
  "tenantLinks": ["/projects/default-project"],
  "documentVersion": 0,
  "documentEpoch": 0,
  "documentKind": "com:vmware:admiral:compute:container:ContainerDescriptionService:ContainerDescription",
  "documentSelfLink": "/resources/container-descriptions/4d9e8d32664228755628f1dad72a8",
  "documentUpdateTimeMicros": 1515741701305001,
  "documentUpdateAction": "POST",
  "documentExpirationTimeMicros": 0,
  "documentOwner": "3d0223ea-5e52-4dd6-8c68-5e9ddfb8a443",
  "documentAuthPrincipalLink": "/core/authz/users/YWRtaW5AYWRtaXJhbC5sb2NhbA=="
}

_cluster の値を大きくすることで最低限存在しなければならないコンテナ数を設定することができます

コンテナ定義とテンプレートを紐付ける

  • リクエスト
curl -X PATCH 'http://localhost:8282/resources/composite-descriptions/4d9e8d32664228755628dff8ade3a' \
-H 'x-xenon-auth-token: eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJpc3MiOiJ4biIsInN1YiI6Ii9jb3JlL2F1dGh6L3VzZXJzL1lXUnRhVzVBWVdSdGFYSmhiQzVzYjJOaGJBXHUwMDNkXHUwMDNkIiwiZXhwIjoxNTE1NzQ1Mjc4fQ.u8_SThTzX0CiiFGVq6NeqDUBKcMmmKOr_7ql5FufZng' \
-H 'x-project:/projects/default-project' \
-H 'content-type:application/json' \
-d '{"name":"","descriptionLinks":["/resources/container-descriptions/4d9e8d32664228755628f1dad72a8"],"tenantLinks":["/projects/default-project"],"documentVersion":8,"documentEpoch":0,"documentKind":"com:vmware:admiral:compute:container:CompositeDescriptionService:CompositeDescription","documentSelfLink":"/resources/composite-descriptions/4d9e8d32664228755628dff8ade3a","documentUpdateTimeMicros":1515741517583001,"documentUpdateAction":"PATCH","documentExpirationTimeMicros":0,"documentOwner":"3d0223ea-5e52-4dd6-8c68-5e9ddfb8a443","documentAuthPrincipalLink":"/core/authz/users/YWRtaW5AYWRtaXJhbC5sb2NhbA=="}'
  • レスポンス
{
  "name": "",
  "descriptionLinks": [
    "/resources/container-descriptions/4d9e8d32664228755628f1dad72a8"
  ],
  "tenantLinks": [
    "/projects/default-project"
  ],
  "documentVersion": 11,
  "documentEpoch": 0,
  "documentKind": "com:vmware:admiral:compute:container:CompositeDescriptionService:CompositeDescription",
  "documentSelfLink": "/resources/composite-descriptions/4d9e8d32664228755628dff8ade3a",
  "documentUpdateTimeMicros": 1515743075171001,
  "documentUpdateAction": "PATCH",
  "documentExpirationTimeMicros": 0,
  "documentOwner": "3d0223ea-5e52-4dd6-8c68-5e9ddfb8a443",
  "documentAuthPrincipalLink": "/core/authz/users/YWRtaW5AYWRtaXJhbC5sb2NhbA=="
}

テンプレートが作成された段階では descriptionLinks が空の状態です
ここを作成したコンテナ定義の ID (4d9e8d32664228755628f1dad72a8) で更新してあげることで紐付けを行うことができます
この作業をしないと admiral の管理画面でテンプレートからコンテナ定義を確認することができません

コンテナ定義の一覧の取得

  • リクエスト
curl -X GET 'http://localhost:8282/resources/container-descriptions' \
-H 'x-xenon-auth-token: eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJpc3MiOiJ4biIsInN1YiI6Ii9jb3JlL2F1dGh6L3VzZXJzL1lXUnRhVzVBWVdSdGFYSmhiQzVzYjJOaGJBXHUwMDNkXHUwMDNkIiwiZXhwIjoxNTE1NzQ1Mjc4fQ.u8_SThTzX0CiiFGVq6NeqDUBKcMmmKOr_7ql5FufZng' \
-H 'x-project:/projects/default-project'
  • レスポンス
{
  "totalCount": 1,
  "documentLinks": [
    "/resources/container-descriptions/4d9e8d32664228755628f1dad72a8"
  ],
  "documents": {
    "/resources/container-descriptions/4d9e8d32664228755628f1dad72a8": {
      "image": "registry.hub.docker.com/library/nginx",
      "command": [],
      "instanceAdapterReference": "/adapters/docker-service",
      "affinity": [],
      "_cluster": 1,
      "env": [],
      "volumes": [],
      "publishAll": true,
      "portBindings": [
        {
          "hostPort": "",
          "containerPort": "80"
        }
      ],
      "logConfig": {
        "config": {}
      },
      "links": [],
      "volumesFrom": [],
      "restartPolicy": "no",
      "networks": {},
      "name": "nginx",
      "customProperties": {},
      "tenantLinks": [
        "/projects/default-project"
      ],
      "documentVersion": 0,
      "documentEpoch": 0,
      "documentKind": "com:vmware:admiral:compute:container:ContainerDescriptionService:ContainerDescription",
      "documentSelfLink": "/resources/container-descriptions/4d9e8d32664228755628f1dad72a8",
      "documentUpdateTimeMicros": 1515741701305001,
      "documentUpdateAction": "POST",
      "documentExpirationTimeMicros": 0,
      "documentOwner": "3d0223ea-5e52-4dd6-8c68-5e9ddfb8a443",
      "documentAuthPrincipalLink": "/core/authz/users/YWRtaW5AYWRtaXJhbC5sb2NhbA=="
    }
  },
  "documentCount": 1,
  "queryTimeMicros": 997,
  "documentVersion": 0,
  "documentUpdateTimeMicros": 0,
  "documentExpirationTimeMicros": 0,
  "documentOwner": "3d0223ea-5e52-4dd6-8c68-5e9ddfb8a443"
}

コンテナ定義の削除

  • リクエスト
curl -X DELETE 'http://localhost:8282/resources/container-descriptions/4d9e8d32664228755628f1dad72a8' \
-H 'x-xenon-auth-token: eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJpc3MiOiJ4biIsInN1YiI6Ii9jb3JlL2F1dGh6L3VzZXJzL1lXUnRhVzVBWVdSdGFYSmhiQzVzYjJOaGJBXHUwMDNkXHUwMDNkIiwiZXhwIjoxNTE1NzQ1Mjc4fQ.u8_SThTzX0CiiFGVq6NeqDUBKcMmmKOr_7ql5FufZng' \
-H 'x-project:/projects/default-project'
  • レスポンス
{
  "image": "registry.hub.docker.com/library/nginx",
  "command": [],
  "instanceAdapterReference": "/adapters/docker-service",
  "affinity": [],
  "_cluster": 1,
  "env": [],
  "volumes": [],
  "publishAll": true,
  "portBindings": [
    {
      "hostPort": "",
      "containerPort": "80"
    }
  ],
  "logConfig": {
    "config": {}
  },
  "links": [],
  "volumesFrom": [],
  "restartPolicy": "no",
  "networks": {},
  "name": "nginx",
  "customProperties": {},
  "tenantLinks": [
    "/projects/default-project"
  ],
  "documentVersion": 1,
  "documentEpoch": 0,
  "documentKind": "com:vmware:admiral:compute:container:ContainerDescriptionService:ContainerDescription",
  "documentSelfLink": "/resources/container-descriptions/4d9e8d32664228755628f1dad72a8",
  "documentUpdateTimeMicros": 1515742131728000,
  "documentUpdateAction": "DELETE",
  "documentExpirationTimeMicros": 0,
  "documentOwner": "3d0223ea-5e52-4dd6-8c68-5e9ddfb8a443",
  "documentAuthPrincipalLink": "/core/authz/users/YWRtaW5AYWRtaXJhbC5sb2NhbA=="
}

最後に

VMware admiral の API を使ってテンプレートの操作をしてみました
/resources/composite-descriptions/templates を使い分けるようです
ざっと見た感じだと templates は DockerHub で公開されているイメージも検索することが可能で composite-descriptions は自信で作成したテンプレートに関しての操作かなと思います
あとはテンプレートは作成しただけでは特に効果はなく、その中にコンテナやボリュームの定義を追加することでアプリのデプロイなどが行なえます

admiral の UI でリクエストしている情報をベースに作成しているので実際にはどちらかだけを使えば OK なのかもしれません

2018年1月12日金曜日

VMware VIC v1.3.0 の REST API を試してみた

概要

VMware VIC v1.3.0 で VCH を REST API で作成できる機能が追加されました
とりあえず触ってみたので備忘録として残しておきます

環境

  • Ubuntu 16.04.3
  • VIC v1.3.0

インストール

実は執筆時点ではまだバイナリを直接ダウンロードすることはできません
My VMware から OVA をダウンロードすることはできます
今回はソースをダウンロードして自分でビルドしました
過去にやり方を紹介しているのでビルド方法はそちらを参照してください

最終的に vic-machine-server というバイナリがあれば OK です

API サーバを起動する

先程のビルドで生成されたものを使います

  • cd bin
  • ./vic-machine-server --scheme=http --host=0.0.0.0 --port 18080

--port は好きなポートに変更してください

動作確認

curl を使っていろいろとコールしてみました

Hello World

とりあえずプロセスが正常に起動しているか確認してみましょう

  • curl -u 'vcenter-user:pass' 'http://localhost:18080/container/hello'

で「You have successfully accessed the VCH Management API.」と出れば起動成功です
ベースパスが /container になっているようなので必ず先頭に付与してください
今回認証はベーシック認証を使っています
vSphere Client などを使ってログインするユーザ名とパスワードを使ってログインしてください

vch を作成する

まずは vch を作成してみます
POST /taraget/{vcenterIP}/datacenter/{datacenterId}/vch を使います
必要な情報はリクエストボディに JSON を設定して送信します

curl \
-X POST \
-u 'vcenter-user:vcenter-pass' \
'http://localhost:18080/container/target/192.168.100.10/datacenter/datacenter-21/vch?thumbprint=F9%3A03%3A2C%3A25%3A45%3A07%3ACE%3A24%3A4F%3A14%3A70%3A95%3A7E%3AB4%3A68%3A87%3AB1%3A2E%3AC1%3AEC' \
-H 'content-type: application/json' \
-d @- << EOF
{
  "name": "test_vch",
  "compute": {
    "resource": {
      "name": "Cluster001"
    }
  },
  "storage": {
    "volume_stores": [{
      "datastore": "datastore1/test",
      "label": "default"
    }],
    "image_stores": ["datastore1"]
  },
  "network": {
    "bridge": {
      "ip_range": "172.16.0.0/12",
      "port_group": {
        "name": "vic-bridge-001"
      }
    },
    "public": {
      "port_group": {
        "name": "public_net"
      }
    }
  }
}
EOF

192.168.100.10 の部分は vCenter の IP を入力してください
また今回は vCenter が IP ベースの通信になるので thumbprint を指定します
compute-resource はクラスタ名を入力してください

ここでポイントなのがデータセンターの指定方法ですが vSphere Client で表示される名前ではないことに注意してください
dcli や mob で確認することができるデータセンター ID になります
vSphere Client のブラウザ版であれば URL の部分に「objectId=urn:vmomi:Datacenter:datacenter-21」みたいな部分があるのでその値を使ってください
(クラスタ名は名前でいいのにデータセンター名は ID でなければいけない理由がよくわかりませんでした、、、)

そして重要なリクエストボディです
まず name で作成する vch 名を指定します
次に compute -> resource -> name でクラスタ名を指定します
上述の通りクラスタ名は mobID ではなく名前で OK です
あとは storage と network を指定します
storage は volume_storesimage_stores を指定します
image_stores は文字列でデータストア名を指定するだけなのですが、volume_stores は少し指定の仕方が違うので注意してください
network は bridge と public を指定します
bridge に関してはコンテナが起動した際に接続するネットワークのレンジを指定する必要があるので設定しましょう

これで VCH を作成することができます
他にも別のネットワークを設定したり証明書を設定したりすることができるパラメータがあります
詳しくは後述するドキュメントを参照してみてください

vch の一覧を取得する

次に作成した vch の情報を取得してみます
GET /taraget/{vcenterIP}/datacenter/{datacenterId}/vch/ を使います

curl \
-X GET \
-u 'vcenter-user:vcenter-pass' \
'http://localhost:18080/container/target/192.168.100.10/datacenter/datacenter-21/vch/vm-1304?thumbprint=F9%3A03%3A2C%3A25%3A45%3A07%3ACE%3A24%3A4F%3A14%3A70%3A95%3A7E%3AB4%3A68%3A87%3AB1%3A2E%3AC1%3AEC'

先ほどの登録とほぼ同じです
vchId を指定しないことで一覧を取得することができます
vchId を指定することで特定に vch の詳細情報を取得することができます
vic-machine コマンドで言うところの inspect 相当の情報が取得できます (後述)

レスポンスは以下の通りです

{
 "vchs": [
   {
     "admin_portal": "https://192.168.200.10:2378",
     "docker_host": "192.168.200.10:2375",
     "id": "vm-1293",
     "name": "test_vch",
     "upgrade_status": "VCH has newer version",
     "version": "v1.2.1-13858-c3db65f"
   }
 ]
}

ここで気がついたんですが version のところが v1.2.1 になっていました
My VMware でも v1.2 は配布していたのでもしかすると Github のバージョンは v1.3.0 でしたが、公式では v1.2 でも API 機能が含まれているかもしれません

単なる勘違いでした
API 経由で作成すれば問題なく v1.3.0 になっています

vch の詳細を取得する

指定した vch のみ取得してみましょう
GET /taraget/{vcenterIP}/datacenter/{datacenterId}/vch/{vchId} を使います

curl \
-X GET \
-u 'vcenter-user:vcenter-pass' \
'http://localhost:18080/container/target/192.168.100.10/datacenter/datacenter-21/vch/vm-1304?thumbprint=F9%3A03%3A2C%3A25%3A45%3A07%3ACE%3A24%3A4F%3A14%3A70%3A95%3A7E%3AB4%3A68%3A87%3AB1%3A2E%3AC1%3AEC'

ここでまたポイントなのですが vch を作成するときは vchId の部分に名前を指定しましたが詳細を取得する場合には ID を指定します
datacenter 同様 vch も ID を指定しないとそんな vch 見つからないと言われてエラーとなります

vch を削除する

最後に vch を削除してみます
先ほどの詳細を取得する API を DELETE でコールするだけです

curl \
-X DELETE \
-u 'vcenter-user:vcenter-pass' \
'http://localhost:18080/container/target/192.168.100.10/datacenter/datacenter-21/vch/vm-1302?thumbprint=F9%3A03%3A2C%3A25%3A45%3A07%3ACE%3A24%3A4F%3A14%3A70%3A95%3A7E%3AB4%3A68%3A87%3AB1%3A2E%3AC1%3AEC'

ただ以下のエラーが返ってきました

{
 "message": "VCH version \"v1.2.1-13858-c3db65f\" is different than API version v1.3.0-rc1-0-4c17da5"
}

おそらくバグかなと思います
vic-machine-linux であれば --force オプションを付与すれば削除することができました
My VMware からダウンロードできる公式版であればこのエラーは出ないのかもしれません
これも勘違いでした
問題なく削除できます

(おまけ) Swagger UI でドキュメントを表示する

  • swagger serve "http://127.0.0.1:18080/swagger.json" --port=28080 --no-open --host=0.0.0.0

--port で LISTEN するポートは好きに変更してください
これで http://127.0.0.1:28080/docs にブラウザでアクセスすると go-swagger の UI が表示されます

最後に

VMware VIC v1.3.0 で追加された API 機能を試してみました
バイナリを動かすだけなので簡単です
ドキュメントがまだ整備されていないので何ともですが swagger UI を見ながら頑張れば何とかなると思います

参考サイト

2018年1月11日木曜日

VMware Admiral にユーザ認証機能を追加してみる

概要

Admiral はデフォルトでは認証機能がありません
ユーザ認証を追加したい場合は別途自分でユーザを追加する必要があります
今回はその方法を紹介したいと思います

環境

  • macOS 10.13.2
  • docker 17.12.0-ce
  • Admiral v1.3.0

※ Ubunt 16.04.3 + docker 17.05.0-ce だとうまく動作しない機能がありました

local-users.json を作成する

  • cd /root/work/admiral
  • vim local-users.json
{
 "users": [{
   "email": "admin@admiral.local",
   "password": "Password1!"
 }, {
   "email": "user1@admiral.local",
   "password": "secret1"
 }, {
   "email": "user2@admiral.local",
   "password": "secret2"
 }]
}

users プロパティに email と password プロパティを持つオブジェクトを配列で追加していきます

ファイルを指定して admiral を起動する

  • docker run -d -p 8282:8282 --name admiral -e XENON_OPTS="--localUsers=/tmp/local-users.json" -v /root/work/admiral/local-users.json:/tmp/local-users.json vmware/admiral

作成した JSON ファイルをコンテナにマウントします
そして XENON_OPTS の --localUsers オプショションでコンテナ上に配置した JSON ファイルを指定することで認証を有効にすることができます

ログインする

http://localhost:8282 にアクセスするとログイン画面が表示されるようになるので local-users.json に記載されたユーザ情報でログインできるか確認してみましょう
admiral_enable_auth1.jpg

ユーザの管理者権限を制御する

デフォルトだと JSON に定義されたユーザはみな管理者権限を持っています
このままだと全員がすべてのプロジェクトにアクセスすることができ、かつプロジェクトの追加やユーザコントロールができてしまいます

なのでまずは管理者権限が不要なユーザから権限を削除しましょう

管理 -> ID 管理 -> ユーザおよびグループ

で検索ボックスで何でもいいので検索するとユーザが出てきます
ユーザが表示されたらチェックボックスをチェックし「管理者ロールの割り当てを解除」を選択することで権限を削除できます
admiral_enable_auth2.png

こんな感じで他のユーザにも権限の解除 or 付与を行います

プロジェクトにユーザ情報を紐付ける

では、実際に ACL が効いているか確認してみましょう
default-project に触れるユーザを設定します

管理 -> プロジェクト -> default-project -> メンバー -> 追加

でユーザを追加しましょう
追加する際にロールを決められるので必要なロールを設定してください
コンテナやテンプレートの作成ができるようにするには「開発者」ロールを選択しましょう
admiral_enable_auth3.png

プロジェクトの内容だけ確認させたい場合は「閲覧者」ロールを選択すると Read only なユーザを作成することができます

ACL の動作確認

admin, user1, user2 でそれぞれログインしてみましょう

admin の場合は default-project も見え管理のタブも表示されていると思います
user1 の場合は default-project が見えテンプレートの追加やコンテナの操作はできるものの管理のタグが非表示になっています
user2 の場合はそもそもログインがうまくいかずに何もできないと思います
admiral_enable_auth4.png

こんな感じでプロジェクトに対して ACL を設定することができます

おまけ 認証を有効にした場合の API を実行する方法

POST /core/authn/basic に JSON に記載したメールアドレスとパスワードをベーシック認証として送信します
その際に Body に requestType を付与します

  • curl -u 'admin@admiral.local:Password1!' -v -X POST -d'{"requestType":"LOGIN"}' http://localhost:8282/core/authn/basic -H "Content-Type: application/json"

そしてレスポンスに含まれる x-xenon-auth-token を付与して API をコールすることで各種 API をコールすることができるようになります
以下はログインしたユーザでプロジェクトの一覧を取得する方法です

  • curl -v -H "x-xenon-auth-token: eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJpc3MiOiJ4biIsInN1YiI6Ii9jb3JlL2F1dGh6L3VzZXJzL1lXUnRhVzVBWVdSdGFYSmhiQzVzYjJOaGJBXHUwMDNkXHUwMDNkIiwiZXhwIjoxNTE1NTkxNjg1fQ.xb0X5SLb-pYplnYOXFjdKUscFt4mQT8-E8XXCZcW5Dw" -X GET http://localhost:8282/projects

最後に

VMware Admiral でユーザ認証を使ってみました
平文の JSON ファイルベースの認証なので少し不安な感じはします
またユーザを追加する場合はファイルを更新してコンテナを再起動する必要があるので少し面倒です (コンテナを削除する必要はありませんが、認証なしからありにする場合は削除しないとダメです)
デフォルトでは管理者権限が付与されてしまっているのでユーザの登録や更新をしたら必ず ID 管理から権限のコントロールをするようにしましょう

一応これを使えばプロジェクトベースの ACL 的なことはできるようになります
個人的にはファイル管理も良いですが LDAP 連携など外部のユーザ管理システムと連携してほしいかなと思います
また JSON ファイルで管理者権限の設定もできると嬉しいかなと思います

参考サイト

2018年1月10日水曜日

VMware Admiral v1.3.0 で REST API を使ってみた

概要

VMware Admiral には swagger ベースの API が実装されています
専用の UI でもいろいろと操作できますが API を使うこともできるので試してみました
Admiral のバージョンは最新の v1.3.0 を使っています

環境

  • Ubuntu 16.04.3
  • docker 17.05.0-ce
  • Admiral v1.3.0

Admiral の起動

  • docker run -d -p 8282:8282 --name admiral vmware/admiral

http://localhost:8282/ にアクセスすると Admiral の管理 UI が表示されると思います

Swagger UI でドキュメントを確認する

まずは API のドキュメントを確認しましょう
Admiral にはすでに Swagger UI が組み込まれているので以下の URL にアクセスするだけでドキュメントが表示されます

http://localhost:8282/discovery/swagger/ui/

このドキュメントを元に API をコールしてみます

が実は、、、

Swagger はドキュメントの生成にのみ使われているようで更新も滞っているようです
https://github.com/vmware/admiral/issues/245
Wikiに最新版を作成中とのことなので現状はブラウザのネットワークコンソールを使ってリクエスト情報を確認しながらやるのがベストとのことでした
なので、今回もブラウザを使いながら curl リクエストを作成しています

プロジェクトの一覧を取得する

GET /projects を使って取得します
curl で以下のようにコールしましょう

  • curl -X GET http://localhost:8282/projects
{
  "documentLinks": [
    "/projects/default-project"
  ],
  "documentCount": 1,
  "queryTimeMicros": 1,
  "documentVersion": 0,
  "documentUpdateTimeMicros": 0,
  "documentExpirationTimeMicros": 0,
  "documentOwner": "a2f3cdc3-6d79-48e5-98ba-c88281cecff9"
}

Admiral はデフォルトで default-project というプロジェクトを持っているのでそれが表示されると思います
またデフォルトだと認証がないので REST API を認証なしでコールすることができます

プロジェクトを登録する

新規でプロジェクト作成してみます
POST /projects を使用します

  • curl -X POST http://localhost:8282/projects -H "Content-Type: application/json" -d '{"name":"test"}'

これでプロジェクトの追加ができます
Public のフラグなどは適宜変更してください

{
  "isPublic": false,
  "administratorsUserGroupLinks": [
    "/core/authz/user-groups/9cdba64fae7cbc75562520652cfa0_project-admins"
  ],
  "membersUserGroupLinks": [
    "/core/authz/user-groups/9cdba64fae7cbc75562520652cfa0_project-members"
  ],
  "viewersUserGroupLinks": [
    "/core/authz/user-groups/9cdba64fae7cbc75562520652cfa0_project-viewers"
  ],
  "id": "050ead14-58f7-4d1c-9795-336c604e4168",
  "name": "test3",
  "customProperties": {
    "__projectIndex": "550951311"
  },
  "tenantLinks": [
    "/projects/9cdba64fae7cbc75562520652cfa0"
  ],
  "creationTimeMicros": 1515479316485,
  "documentVersion": 0,
  "documentEpoch": 0,
  "documentKind": "com:vmware:admiral:auth:project:ProjectService:ProjectState",
  "documentSelfLink": "/projects/9cdba64fae7cbc75562520652cfa0",
  "documentUpdateTimeMicros": 1515479316484001,
  "documentUpdateAction": "POST",
  "documentExpirationTimeMicros": 0,
  "documentOwner": "a2f3cdc3-6d79-48e5-98ba-c88281cecff9"
}

プロジェクトを削除する

登録したテスト用のプロジェクトを削除してみましょう
DELETE /projects/{projectId} を使います

  • curl -X DELETE http://localhost:8282/projects/9cdba64fae7cbc755624ef84d4938

が、現状は 503 になり削除できませんでした
おそらくバグだと思います

クラスタを登録してみる

プロジェクト内にクラスタを登録してみましょう
POST /resources/clusters を使います

少し長いですがリクエストは以下の通りです

  • curl -X POST http://localhost:8282/resources/clusters -H "Content-Type: application/json" -H "x-project: /projects/default-project" -d '{"hostState":{"address":"http://192.168.100.11:2376","customProperties":{"__containerHostType":"DOCKER","__adapterDockerType":"API","__clusterName":"cluster001","__clusterDetails":"test","acceptCertificate":"false"}}}'

192.168.100.11 は TCP 2376 を LISTEN にした docker サーバです
(2376 ポートを有効にして docker をリモートから操作できるようにする方法はこちらの記事を参考にしてください)

クラスタ名は cluster001 でプロジェクト default-project にクラスタとホストを追加します
ポイントとしてはプロジェクトは x-project というヘッダで指定します
また body 側ではホストの情報とコンテナホストが docker or vch を指定する必要があります
成功すると以下のレスポンスが返ってきます

{
 "name": "cluster001",
 "type": "DOCKER",
 "status": "ON",
 "clusterCreationTimeMicros": 1515543281334,
 "details": "test",
 "containerCount": 0,
 "systemContainersCount": 0,
 "totalMemory": 0,
 "memoryUsage": 0,
 "nodeLinks": [
   "/resources/compute/9cdba64fae7cbc7556260eaed6c20"
 ],
 "nodes": {
   "/resources/compute/9cdba64fae7cbc7556260eaed6c20": {
     "address": "http://192.168.100.11:2376",
     "powerState": "ON",
     "documentVersion": 1,
     "documentEpoch": 0,
     "documentKind": "com:vmware:photon:controller:model:resources:ComputeService:ComputeState",
     "documentSelfLink": "/resources/compute/9cdba64fae7cbc7556260eaed6c20",
     "documentUpdateTimeMicros": 1515543281381009,
     "documentUpdateAction": "PATCH",
     "documentExpirationTimeMicros": 0,
     "documentOwner": "a2f3cdc3-6d79-48e5-98ba-c88281cecff9"
   }
 },
 "totalCpu": 0,
 "cpuUsage": 0,
 "documentVersion": 0,
 "documentSelfLink": "/resources/clusters/9cdba64fae7cbc7556260eaecfad8",
 "documentUpdateTimeMicros": 0,
 "documentExpirationTimeMicros": 0
}

Admiral の管理画面で確認すると以下のようになっています
admiral_api1.jpg

クラスタを取得する

登録したクラスタ情報を API で取得してみます
GET /resources/clusters を使います

  • curl -X GET -H "x-project: /projects/default-project" http://localhost:8282/resources/clusters

指定のプロジェクト配下にあるクラスタを取得する場合は POST 時と同様にヘッダでプロジェクトを指定します
ヘッダの指定がない場合はすべてのクラスタを取得します
レスポンスは以下のように取得できます

{
 "documentLinks": [
   "/resources/clusters/9cdba64fae7cbc7556260eaecfad8"
 ],
 "documentCount": 1,
 "documentVersion": 0,
 "documentUpdateTimeMicros": 0,
 "documentExpirationTimeMicros": 0
}

更にクラスタの詳細を取得したい場合はクラスタの ID を URI に含めるだけで OK です

  • curl -X GET http://localhost:8282/resources/clusters/9cdba64fae7cbc7556260eaecfad8

クラスタを削除する

登録したクラスタを削除してみましょう
DELETE /resources/cluster/{clusterId} を使います
先ほどのクラスタの詳細を取得するリクエストのメソッドを DELETE に変更するだけです

  • curl -X DELETE http://localhost:8282/resources/clusters/9cdba64fae7cbc7556260eaecfad8

成功時のレスポンスボディはないのでレスポンスステータスで削除できたかどうか確認しましょう

最後に

VMware Admiral の REST API を curl から操作してみました
ドキュメントが整備されていないので UI を元に自分でリクエストを解析する必要がありますが、それほど難しくはないと思います

今回はプロジェクトとクラスタの操作しか紹介しませんでしたが他にもユーザ操作やレジストリ操作、クラスタへのホスト追加などいろいろな操作があるので必要に応じてブラウザとにらめっこしながらやれば同じようにリクエストを生成できると思います