2017年6月23日金曜日

Arduino でバイト配列を文字列に変換する方法

概要

タイトルの通り
MQTT の PubSubClient.h を使う時にペイロードが byte* だったので文字列に変更したかった感じです

環境

  • macOS 10.12.5
  • Arduino IDE 1.6.12

コード

void callback(char* topic, byte* payload, unsigned int length) {
  payload[length] = '\0';
  String msg = String((char*) payload);
  Serial.println(msg);
  if (msg == "start") {
    // something ...
  } else if (msg == "stop") {
    // something ...
  }
}

ポイントはバイト配列の最後に終端文字を入れてること
こうしないとうまく文字列に変換できません

最後に

*byte を String に変換する方法を紹介しました
久しぶりに Arduino に触るとよくわかんないですね

参考サイト

2017年6月22日木曜日

firebase-ruby の get, push, set, update, delete の挙動を確認してみた

概要

前回 は firebase-ruby を使った GettingStarted 的な記事を紹介しました
今回はリアルタイムデータベースを操作できるその他のメソッドについて挙動を確認してみました

環境

  • CentOS 7.3.1611
  • ruby 2.3.3p222
  • firebase-ruby 0.2.6

使用するコード

  • bundle init
  • vim Gemfile
gem "firebase"
  • bundle install
  • vim test.rb
require 'firebase'

class FB
  BASE_URI = 'https://project-id.firebaseio.com/'
  SECRET_KEY = 'your-database-secret-key'

  def initialize
    @client = Firebase::Client.new(BASE_URI, SECRET_KEY)
  end

  def get(path, query)
    @client.get(path, query)
  end

  def push(path, hash)
    @client.push(path, hash)
  end

  def set(path, hash)
    @client.set(path, hash)
  end

  def update(path, hash)
    @client.update(path, hash)
  end

  def delete(path, query)
    @client.delete(path, query)
  end
end

f = FB.new

res = f.push('test', { :name => "hoge" })
id = res.body['name']
res = f.get("test/#{id}", {})
p res.body['name'] # => "hoge"

f.set('test1', { :name => "fuga" })
res = f.get('test1', {})
p res.body['name'] # => "fuga"

f.update("test/#{id}", { :name => "hogehoge" })
res = f.get("test/#{id}", {})
p res.body['name'] # => "hogehoge"
f.update('test1', { :name => "fugafuga" })
res = f.get('test1', {})
p res.body['name'] # => "fuga"

f.delete("test/#{id}", {})
f.delete('test1', {})
  • bundle exec ruby test.rb

BASE_URI と SECRET_KEY は自分のプロジェクトの値を設定してください

挙動説明

firebase-ruby にはデータを登録するためのメソッドが 2 つ用意されています
push と set があります

push の場合、基本的に毎回新規オブジェクトの追加となります
追加したオブジェクトに自動で ID を振ってくれるため、その ID でオブジェクトを識別します
ID は push したレスポンス情報に含まれています

方や set は指定したパスにそのままデータを登録します
なので 2 回 set しても内容は変わりません、オブジェクトも増えません
get でアクセスする場合のパスにも ID が含まれることはありません

get は path を指定してデータを取得します
path 配下にあるオブジェクトに対して絞り込みを行いたい場合はクエリを使います
https://firebase.google.com/docs/reference/android/com/google/firebase/database/Query
例えば上位 10 件のみを取得したい場合は limit を使います

あとは update と delete ですが、これも path で取得したオブジェクト配下の情報を削除したり更新したりします
update に関しては上書き更新なのでデータが新規で登録されるということはありません
delete に関しては物理削除なので、一度削除したオブジェクトは再度戻すことはできません

最後に

firebase-ruby が用意している関数の挙動を確認してみました
個人的には push ではなく set を使ってデータ登録したほうが毎回 ID を走査しなくて済むので簡単かなと思います

あとは get, update, delete を使って登録したデータを path を指定して操作する感じかなと思います

Firebase のデータベース機能はリアルタイムデータベースなので push や set で登録したときのアプリ側の挙動も確認したいと思っています
また Firebase には他にもストレージやホスティング機能、スクリプト機能があるのですが、firebase-ruby ではデータベース機能に対する制御しかできません
ストレージなどを使いたい場合は直接 Google Storege を操作する感じになるみたいです

参考サイト

2017年6月21日水曜日

作成した gem ファイルを rubygems.org で公開する手順

概要

前回 独自の gem ファイルを作成する方法を紹介しました
今回は作成した gem ファイルを rubygems.org で公開する手順を紹介します

環境

  • CentOS 7.3.1611
  • ruby 2.3.3p222
  • gem 2.6.11
  • rake 10.4.2

サインアップ

https://rubygems.org/sign_up からアカウントを作成しましょう
メールアドレスがあれば簡単に作成できます

登録したメールアドレスに確認用の URL が送信されるのでクリックしてアカウントを承認します
問題なくログインできるか確認してください

gem の作成

とりあえず今回は何でも OK です
本記事では 前回 の記事を参考に gem を作成しています

とりあえずリリースしてみる

まず Github にソースをアップロードします
Github じゃなくても良いですが remote のリポジトリがないと怒られます

でコードを push しましょう
そして

  • bundle exec gem push

で rubygems.org に認証します
登録したメールアドレスとパスワードで認証しましょう
cat ~/.gem/credentials ができれば OK です
そして

  • bundle exec rake release

で gem ファイルアップロードできます

fstlib 0.1.0 built to pkg/fstlib-0.1.0.gem.
Tag v0.1.0 has already been created.
Pushed fstlib 0.1.0 to rubygems.org.

こんな感じになれば成功です
rubygems の自分のアカウントのページにアクセスするとアップロードした gem があると思います

バージョンアップしてみる

適当にコードを改修してバージョンアップしてみましょう

  • vim lib/fstlib.rb
require "fstlib/version"

module Fstlib
  def self.hello
    "My second rubygems"
  end
end
  • vim lib/fstlib/version.rb
module Fstlib
  VERSION = "0.1.1"
end

でコードを改修したら

  • git add .
  • git commit -m “Second commit”
  • git push -u origin master

でコミットして

  • bundle exec rake release

で再度アップロードします
アップロード完了後再度 gem のページを見てみるとバージョンが 0.1.1 に上がっていることが確認できると思います

使ってみる

新しいスクリプトを作成してアップロードした gem が使えるか確認してみましょう

  • bundle init
  • vim Gemfile
gem "fstlib"
  • bundle install

で rubygems.org からアップロードした gem がインストールできます

  • vim test.rb
require 'fstlib'

p Fstlib.hello
  • bundle exec ruby test.rb

実行すると "My second rubygems" が表示されると思います

最後に

作成した gem を rubygems.org で公開する手順を紹介しました
今回紹介した rake release の流れは「ビルド -> コミットがあるかチェック -> リモートに push -> rubygems.org にプッシュ」という流れをやってくれています
https://github.com/bundler/bundler/blob/master/lib/bundler/gem_helper.rb#L55

なので bundle exec gem push を実施しないでいきなり release しても初回であれば rubygems.org に対する認証を聞かれると思います
うまくログインができない場合は個別で gem push を実行すると良いと思います

また削除する場合はコマンドで行います

  • gem install gemcutter
  • gem yank fstlib -v 0.1.1
  • gem yank fstlib -v 0.1.0

という感じで全バージョン削除すると rubygems.org から消えます

参考サイト

2017年6月20日火曜日

RSpec で undefined method 'get' for が出たときの対処法

概要

エラーの詳細は以下の通り

undefined method `get' for #<RSpec::ExampleGroups::MyApp:0x00000002fc48e0>
Did you mean?  gets
               gem

Sinatra など get リクエストを送信するテストが実行できない状況です

環境

  • CentOS 7.3.1611
  • ruby 2.3.3p222
  • rspec 3.6.0

対処方法

  • rspec --init

で .rspec ファイルを作成します
中身は

--require spec_helper

となっているので .rspec ファイルを作成しないで spec_helper.rb で require しても大丈夫だと思います

2017年6月19日月曜日

Sinatra で設定ファイルを使う方法

概要

sinatra-contrib というパッケージを使うと簡単に設定ファイルを読み込むことができます
実際に設定ファイルを使ったサンプルの sinatra アプリケーションを紹介します

環境

  • CentOS 7.3.1611
  • ruby 2.3.3p222
  • sinatra 2.0.0
  • sinatra-contrib 2.0.0

ライブラリのインストール

  • bundle init
  • vim Gemfile
gem "sinatra"
gem "sinatra-contrib"
  • bundle install

また今回のディレクトリ構成は以下の通りです

.
├── app.rb
├── config.ru
├── config.yml
├── Gemfile
├── Gemfile.lock
└── views
    └── hello.erb

アプリの作成

  • vim app.rb
require "sinatra/base"
require "sinatra/config_file"

class MyApp < Sinatra::Base
  register Sinatra::ConfigFile
  config_file './config.yml'

  get '/' do
    @message = settings.message
    @friends = settings.friends
    erb :hello
  end
end

Sinatra::Base を継承して独自のコントローラクラスを定義します
そして、設定ファイルを読み込むために Sinatra::ConfigFile を登録します
読み込みに成功した後は settings というオブジェクトを参照することで値を取得することができます
今回は取得した値をテンプレートに渡してテンプレート側で値を確認します

設定ファイルの作成

  • vim config.yml
message: "My friends"
friends:
  - name: 'bob'
    age: 20
    favorites:
      - 'baseball'
      - 'soccer'
  - name: 'tom'
    age: 19
    favorites:
      - 'soccer'

app.rb と同じディレクトリに配置します
YAML 形式で記述します
今回は配列も使っています

テンプレートファイルの作成

  • mkdir views
  • vim views/hello.erb
<h2>"<%= @message %>"</h2>
<% @friends.each do |f| %>
<div>
  <div>name: <%= f['name'] %></div>
  <div>age: <%= f['age'] %></div>
  <div>favorites:</div>
  <% f['favorites'].each do |fa| %>
    <div>- <%= fa %></div>
  <% end %>
</div>
<% end %>

app.rb から呼び出されるテンプレートファイルです
設定ファイルの内容を出力しています
ハッシュとして渡ってくるので ruby のハッシュを参照する方法で値を取り出すことができます

アプリ起動用の rackup ファイル作成

  • vim config.ru
require 'bundler'
Bundler.require

require './app'
run MyApp

rackup コマンドでアプリを起動するためのスクリプトです

動作確認

  • bundle exec rackup
  • curl localhost:9292

で config.yml に書かれた情報が HTML で出力されると思います

最後に

sinatra で YAML で書かれた設定ファイルを読む込む方法を紹介しました
値の参照は settings オブジェクトを使って参照しますがスコープは sinatra アプリケーション内のみとなります
なので、自分で定義したライブラリの関数などに値を渡したい場合は引数などで渡す必要があります (ライブラリ側で settings を参照することはできません)

参考サイト

2017年6月17日土曜日

独自のコントローラクラスを作成している sinatra を rspec でテストする

概要

Sinatra::Base を継承して独自のコントローラクラスを作成している場合に rspec を使ってテストする方法を紹介します
少し工夫が必要でした

環境

  • CentOS 7.3.1611
  • ruby 2.3.3p222
  • rspec 3.6.0
  • sinatra 1.4.8

インストール

  • bundle init
  • vim Gemfile
gem "sinatra"
gem "rspec"
gem "rack-test"
  • bundle install

独自のコントローラクラスの作成

  • vim app.rb
require 'sinatra/base'

class MyApp < Sinatra::Base
  get '/' do
    'myapp'
  end
end

このクラス内でアプリを起動 (run) することはしません

rackup で起動させる

  • vim config.ru
require 'bundler'
Bundler.require

require './app'
run MyApp

これで rackup コマンドで MyApp アプリケーションが起動します
こうすることで独自のコントローラクラスを切り離しテストし易いようにします
もし MyApp 内で run! をしてしまうと rspec でテストを実行したときにアプリケーションが起動してしまいテストが進まなくなってしまいます

今回は直接アプリを起動しませんがもし起動させたい場合は

  • bundle exec rackup

で 127.0.0.1:9292 で起動します

テストの作成

  • rspec --init
  • vim spec/app_spec.rb
# -*- coding: utf-8 -*-
ENV['RACK_ENV'] = 'test'

require './app.rb'
require 'rspec'
require 'rack/test'

describe "MyApp のテスト" do
  include Rack::Test::Methods

  def app
    MyApp
  end

  it "root にアクセスしたときに myapp を返却する" do
    get '/'
    expect(last_response.status).to eq 200
    expect(last_response.body).to eq 'myapp'
  end
end
  • bundle exec rspec -f d spec/
MyApp のテスト
  root にアクセスしたときに myapp を返却する

Finished in 0.02609 seconds (files took 0.18599 seconds to load)
1 example, 0 failures

こんな感じになれば成功です

おまけ spec_helpe.rb を使って記述を楽にする

今のままだと describe するたびに include して def しなければなりません
そんなときに spec_helper.rb を使って設定しておくと省略することができます

  • vim spec/spec_helper.rb
ENV['RACK_ENV'] = 'test'

require File.expand_path '../../app.rb', __FILE__
require 'rack/test'
require 'rspec'

module RSpecMixin
  include Rack::Test::Methods

  def app
    MyApp
  end
end

RSpec.configure do |config|
  config.include RSpecMixin
  config.expect_with :rspec do |expectations|
    expectations.include_chain_clauses_in_custom_matcher_descriptions = true
  end

  config.mock_with :rspec do |mocks|
    mocks.verify_partial_doubles = true
  end

  config.shared_context_metadata_behavior = :apply_to_host_groups
end

デフォルトのコメントはすべて削除しました
要するにテスト内でやっていた処理を module 化して、それを Rspec.configure にあらかじめ設定してあげる感じです

テスト側は以下のように変更します

  • vim spec/app_helper.rb
# -*- coding: utf-8 -*-
require File.expand_path '../spec_helper.rb', __FILE__

describe "MyApp のテスト" do
  it "root にアクセスしたときに myapp を返却する" do
    get '/'
    expect(last_response.status).to eq 200
    expect(last_response.body).to eq 'myapp'
  end
end

かなりすっきりしました
冒頭で spec_helper.rb を require するだけになります

あとは同じようにテストを実行すれば OK です

  • bundle exec rspec -f d spec/

最後に

Sinatra で独自のコントローラクラスを定義している場合に rspec でテストする方法を紹介しました
ポイントはアプリケーションの起動を rackup を使うということでした

参考サイト

2017年6月16日金曜日

rspec のインストールとサンプルテストの実行

概要

rspec に入門しました
Getting Started の手順を紹介します

環境

  • CentOS 7.3.1611
  • ruby 2.3.3p222
  • rspec 3.6.0

インストール

  • bundle init
  • vim Gemfile
gem "rspec"
  • bundle install

テスト対象のクラス作成

  • vim car.rb
class Car
  def initialize(gas, kilo)
    @gas = gas
    @kilo = kilo
  end

  def available
    @gas / @kilo
  end
end

テスト作成

  • vim car_spec.rb
require './car.rb'

RSpec.describe '車のスペック' do
  it 'ガソリン 100 リットルなら 10 km 走れます' do
    c = Car.new(100, 10)
    expect(c.available).to eq 10
  end
end

テスト実行

  • rspec -f d car_spec.rb

もしくは

  • bundle exec rspec -f d car_spec.rb
車のスペック
  ガソリン 100 リットルなら 10 km 走れます

Finished in 0.00202 seconds (files took 0.08629 seconds to load)
1 example, 0 failures

おまけ spec_helper を使う

  • bundle exec rspec --init
  • mv car_spec.rb spec/
  • rspec -f d spec/
  • vim spec/spec_helper.rb

=begin から =end の部分を削除します

  • bundle exec rspec -f d spec/

そして再度テストを実行してみると spec_helper.rb の設定が反映され結果の表示が変わると思います

最後に

ゼロから rspec をとりあえず実行できるところまでやってみました
mock 系や他の Tips もあれば別記事で紹介したいと思います

参考サイト

2017年6月15日木曜日

Swift3 で画像を良い感じにキャッシュしてくれる Kingfisher を使ってみた

概要

UITableView の UITableViewCell 内に ImageView がありテーブルをスクロールするたびに画像をダウンロードするアプリがありました
Android と同様でセルが表示されるたびに画像をダウンロードしてしまう仕様でかなりいけていませんでした
そんなとき Kingfisher という素晴らしいライブラリに出会いこれがやりたいことを完璧にやってくれたので使い方を紹介します

環境

  • macOS 10.12.5
  • Xcode 8.3.3 (8E3004b)
  • Kingfisher 3.3.3

インストール

Podfile に以下を追記して install するだけです

pod 'Kingfisher'

使いたい .swift ファイルの先頭で import しましょう

import Kingfisher

画像を読み込む

今まで以下のようなコードで UITableViewCell 内の ImageView に画像をダウンロードして設定していました

let imageURL: NSURL? = NSURL(string: img["src"]!)
let req = NSURLRequest(url: imageURL! as URL)
NSURLConnection.sendAsynchronousRequest(req as URLRequest, queue: OperationQueue.main) { (res, data, err) in
    let image = UIImage(data: data!)
    imageView.image = image
}

これが cellForRowAt メソッドで実装されているためセルが表示されるたびに sendAsynchronousRequest が使われるため無駄に通信を行っていました

これを Kingfisher を使って以下のように書き換えます

imageView.kf.indicatorType = .activity
imageView.kf.setImage(with: URL(string: img["src"]!),
                      // キャッシュがある場合はキャッシュを使用する
                      completionHandler: {
                        (image, error, cacheType, imageURL) in
                        imageView.image = image
})

まだ画像がダウンロードできていない場合には URL から画像をダウンロードします
そして、ダウンロード中はインジケータを表示します
画像がダウンロードできているセルにスクロールした場合には completionHandler 側がコールされすでにキャッシュされている image を imageView に設定するだけでキャッシュが利用できます

動作確認

アプリをビルドして確認してみましょう
改修前はスクロールするたびに画像をダウンロードするため、回線状況の悪い場合だとセルに正しい画像が表示されなかたり、画像の表示が追いつかなかったりします

Kingfisher を使った改修後はセルをスクロールしまくってもダウンロード中にインジケータが表示されるし、ダウンロードが完了した画像はキャッシュから表示してくれるため、画像の表示が遅れたりすることが一切ありません

比較のスクリーンショットがないのが残念ですがかなり快適な感じになると思います

またアプリを kill して再度立ち上げてもキャッシュが残っている状態だったのですでにダウンロード済みの画像に関してはすぐに表示されました

最後に

Swift で Kingfisher を使ってみました
かなり良い感じのライブラリでビックリしました
Swift には他にも同じようなライブラリがあるようで Moa や Vincent, MapleBacon などあるようです
今回 Kingfisher を使ったのは単純に一番人気そうだったので使いましたが他のやつも代用可能だと思います

画像ダウンロード系はもう全部 Kingfisher を使っていこうと思います

参考サイト

2017年6月14日水曜日

ruby の標準 logger でクラスの変数情報を出力する方法

概要

答え -> inspect を使います

環境

  • CentOS 7.3.1611
  • ruby 2.3.3p222

サンプルコード

require 'logger'

class Sub
  def initialize(hoge)
    @hoge = hoge
  end
end


logger = Logger.new(STDOUT)
s = Sub.new 'hoge'
logger.info "#{s.inspect}"

出力

I, [2017-06-14T15:56:06.326706 #26690]  INFO -- : #<Sub:0x00000002454b90 @hoge="hoge">

2017年6月13日火曜日

個人的な動く LINE スタンプの作り方

概要

個人的に動く LINE スタンプを作るときの方法を紹介します
一応全部無料でできます
各ツールの詳細な使い方は説明しないので、公式サイト等を見て勉強してください
今回は Windows 環境のスクリーンショットで説明しています

環境

  • macOS 10.12.5 or Windows 10
  • FireAlpaca 1.7.4
  • アニメ画像に変換する君 1.2.0
  • LINE シミュレータ

FireAlpaca で絵を書く

全てはここから、絵を書かないことにはスタンプはできません
FireAlpaca を開いたら

ファイル -> 新規作成 -> 幅320pixel, 高さ270pixel -> OK

でレイヤを 1 つ作成します
このレイヤに適当に絵を書きます
ペンを選択してマウスの左クリックを押しながら移動すれば線が書けます

そして 1 つのレイヤを書き終えたら更にレイヤを追加します

レイヤ -> 追加

追加したレイヤに次のアニメーションの絵を書きます
こんな感じで画像をたくさん作りコマ送りすることでアニメーションを実現しています

アニメーションスタンプのガイドライン を見ると「イラストは最低5 - 最大20フレーム」とあるのでレイヤも最低限 5 つ以上、最高で 20 個以下にしましょう

とりあえず 5 レイヤ分で作成しました
line_animetaion_stamp1.png

各レイヤに丸を横に並べていって最後に四角が登場する謎のアニメーションです

FireAlpaca で png を連番で書き出す

絵が書けたらこのレイヤたちを連番 png で書き出します

表示 -> オニオンスキンモード

にします
オニオンスキンモードはレイヤの前後が薄緑と薄ピンクで表示される機能です
この機能を使うと前後のアニメーションでどういう動きをするか確認できるのでアニメーションが作成しやすいです

調整できたら、連番で書き出します

ファイル -> レイヤーを連番出力 (オニオンスキンモード)

で連番ファイルを保存するディレクトリを選択するダイアログが表示されるので選択して保存します
その後動画を見るかどうか聞かれますが No で OK です

これで連番ファイルが png で出力されました
ちなみに出力される連番は 000.png から出力されます
レイヤ的には番号の大きい方から 000 になるので注意してください
(図で言うと レイヤ5 の四角が 000.png になり、レイヤ 1 の左の丸が 0005.png になります)

アニメ画像に変換する君で apng を作成する

作成された連番の png を apng という動く LINE スタンプのファイル形式にします
ダウンロードしたアニメ画像に変換する君を開きます

そして「ファイルを選択」もしくはドラッグアンドドロップで作成された連番ファイルを開きます
左メニューの「フレームレート」と「ループ回数」を調整します

今回だとフレームレートを「5」ループ回数を「2」にするといい感じになります
いい感じというのは再生時間の調整の部分でこれもガイドラインに書いているのですが再生時間は「1, 2, 3, 4」秒のどれかでないとダメみたいです
再生する機能もあるので再生しながらどんな感じのアニメーションになるか確認すると良いと思います
line_animetaion_stamp2.png

フレームレートとループ回数を調整したら「アニメ画像を保存する」を選択して apng ファイルを作成します
名前と保存先を決定して出力しましょう

できあがった apng ファイルは拡張子は .png で普通に開いてもただの静止画が表示されるだけだと思います

LINE シミュレータで動作確認する

ただの静止画をシミュレータ上で表示してみましょう

Firefox で https://creator.line.me/ja/simulator/ からマイページにログインし左メニューの「作ったスタンプをチェックしよう!」を選択します
「スタンプシミュレータを使ってみる」を選択すると別ウィンドウでシミュレータが起動します
ここに先程作成した apng ファイルをドラッグアンドドロップで配置してみましょう
すると動く LINE スタンプのようにアニメーションすると思います
line_animetaion_stamp3.png

写真はアニメーション後の状況です

最後に

個人的に動く LINE スタンプを作成する方法を紹介しました
メリットはすべて無料で使えるという点でしょうか
フォトショップなどが使えるのであればそれだけで apng まで作成できると思います
また、絵自体もクオリティの高いものができあがると思います

また、絵を書くのにマウスだと正直辛いので本格的に書きたいのであればペンタブを購入するのをおすすめします
自分はまだそこまで本格的にやっていないのでペンタブを購入していませんが、もし購入してスタンプを作成することになったそのときにレビューや FireAlpaca との連携方法なども紹介したいと思います

参考サイト

2017年6月12日月曜日

Firebase で iOS にプッシュ通知を送信してみた

概要

プッシュ通知を Firebase に移行してみたので設定や流れをメモしておきます
ある程度端折っいる流れもあるのでその点はあしからず
基本は公式ドキュメントを見ることをおすすめします

環境

  • macOS 10.12.5
  • Xcode 8.3.2 (8E2002)
  • Firebase (2017/6/9 時点)
  • プッシュ対象端末 iPhone5 iOS 10.3.2

流れ

AppID にプッシュ通知用の証明書を登録する

作成するときは AppID の一覧から作成することをおすすめします
iOS Certificates にログインして

Identifiers -> App IDs -> 対象の AppID を選択 -> Edit

で編集画面に移動し「Push Notifications」の欄をチェックオンにします
そしてそこから「Create Certificate」でプッシュ通知用の証明書を作成します
作成には CSR が必要になるので事前に作成します
Development, Production 用の証明書の 2 つが作成できれば OK です
firebase_push1.png

作成したプッシュ通知用の証明書から p12 ファイルを書き出す

先ほどの画面から証明書を「Download」できるので Mac 上にダウンロードします
そして、ダブルクリックしてキーチェーンアクセスで開いて p12 ファイルを書き出しましょう
パスワードはなしで書き出しました
後から気づいたのですが、Firebase は p12 ファイルのパスワードも設定できるのでパスワードはありでも大丈夫だと思います

p12 ファイルを Firebase に登録する

Firebase のコンソールにログインしましょう

プロジェクトの設定 -> クラウドメッセージング -> 対象のアプリを選択 -> APNs 証明書

を選択します
同じような項目で「APNs 認証キー」という項目がありますが、そっちじゃないので注意してください

「開発用 APNs 証明書」「本番環境の APNs 証明書」という 2 つの欄があるので書き出した p12 ファイルをそれぞれアップロードしてください
ちゃんと Development 用の証明書から書き出した p12 ファイルは「開発用の APNs 証明書」Production 用の証明書から書き出し p12 ファイルは「本番環境の APNs 証明書」に設定しましょう
当然ですが、間違えるとプッシュ通知が送信されません

アプリ側修正

これで Firebase 側の設定は完了です
アプリ側の修正をしていきましょう

ライブラリのインストールは Cocoapods を使います

pod 'Firebase/Messaging'

プロジェクトのプッシュ通知を有効にします
プロジェクトを選択した状態で Capabilities を選択し「Push Notifications」を ON にします
firebase_push2.png

あとは AppDelete.swift を編集します
今回は Swift3 の文法を使っています
Swift2 だと若干コールするメソッドが違うので気をつけてください
全部紹介すると長いのでポイントを紹介します

import UserNotifications

を import しましょう

// Firebase の初期化
FIRApp.configure()
// プッシュ通知の許可を表示する
if #available(iOS 10.0, *) {
    let authOptions: UNAuthorizationOptions = [.alert, .badge, .sound]
    UNUserNotificationCenter.current().requestAuthorization(
        options: authOptions,
        completionHandler: {_, _ in })
    // For iOS 10 display notification (sent via APNS)
    UNUserNotificationCenter.current().delegate = self as? UNUserNotificationCenterDelegate
    // For iOS 10 data message (sent via FCM)
    FIRMessaging.messaging().remoteMessageDelegate = self as? FIRMessagingDelegate
} else {
    let settings: UIUserNotificationSettings = UIUserNotificationSettings(types: [.alert, .badge, .sound], categories: nil)
    application.registerUserNotificationSettings(settings)
}
application.registerForRemoteNotifications()

didFinishLaunchingWithOptions に追加します
これでアプリの初回起動時にプッシュ通知を許可するかどうかのダイアログが出るようになり、許可を押した人にプッシュ通知できるようになります

// メッセージ受信時の処理
func application(_ application: UIApplication, didReceiveRemoteNotification userInfo: [AnyHashable: Any]) {
    print(userInfo)
}

func application(_ application: UIApplication, didReceiveRemoteNotification userInfo: [AnyHashable: Any],
                 fetchCompletionHandler completionHandler: @escaping (UIBackgroundFetchResult) -> Void) {
    print(userInfo)
    completionHandler(UIBackgroundFetchResult.newData)
}

メッセージを受信したときの処理です
メッセージをカスタマイズしていて何か特別な処理をしたいときはここにロジックを記載します

動作確認

これで OK なはずです
あとは Firebase の Notifications からメッセージを作成して送信すればメッセージが届くはずです

当然ですが実機でないと届きません
なので、Mac に iPhone を接続して Firebase 対応したアプリをビルドしてインストールしてください
アプリ起動時にプッシュ通知の許可拒否のダイアログが表示されるのでそこでも必ず許可を選択してください

最後に

Firebase を使って iOS にプッシュ通知を送信してみました
自分が一番ハマったのは証明書の部分で始め Production 用の証明書と p12 ファイルしか登録しないでやっていたら全然プッシュ通知が届かなく、Development 用の証明書も作成したら届くようになりました

あとはコードの部分ですが、今回のコードは公式のドキュメントに記載されていることとほぼ同様です
でも「firebase ios push」あたりでググると公式ドキュメントが一番最初に出てきません
引っかかった記事のサンプルコードを使っていたのですが、単純にプッシュ通知したいだけなのに setAPNSToken を使っていたりしてうまく動作しませんでした
単純にプッシュ通知を受け取りたいだけなら上記のコードだけで十分だと思います
それ以外にもアプリがアクティブになった場合のプッシュ通知の処理や、通知の開封状況などを解析するためのコードがありますが単純にプッシュ通知したい場合であれば不要です

あともう一点良くわからなかったのは Firebase ではプッシュ通知を送信するときに単一の端末にだけ送信する機能があります
そのときに「FCM 登録トークン」というものを使うのですが、これが Firebase 上のどこで確認できるかさっぱりわかりませんでした
コード上でデバッグする方法はドキュメントに載っていたのですが、コンソール上で確認する方法はわかりませんでした
たぶん、個別配信をするときは FCM 登録トークンを使わないで、トピックを使ってやれということだとは思います
FCM 登録トークンは開発者が知りたい時に直接コードにデバッグコードを埋め込んで取得しましょうというスタンスなんだと思います
それともどっかで確認できるのかもしれませんが、、

参考サイト

2017年6月11日日曜日

Getting Started dozens rubygem

概要

dozens には API があります
Ruby の SDK が公開されているので使ってみました

環境

  • CentOS 7.3
  • Ruby 2.3.3p222
  • dozens 0.0.2

Getting Started

dozens のユーザ ID と API Key を事前に取得しておいてください

ライブラリのインストール

  • gem install dozens

認証

require 'dozens'

api = Dozens::API.new("your_dozens_id" , "your_dozens_api_key")
api.authenticate

ゾーンの一覧表示

p api.zones

ゾーン内のレコード一覧

rs = api.records "your.domain"
rs['record'].each {|r|
  p "id => #{r['id']}, name => #{r['name']}, type => #{r['type']}, prio => #{r['prio']}, content => #{r['content']}, ttl => #{r['ttl']}"
}

ゾーン内のレコードの更新

rs = api.records "your.domain"
rs['record'].each {|r|
  if r['name'] == "site.your.domain"
    target = r
    target['content'] = "1.1.1.1"
    p api.update_record(target['id'], target)
  end
}

とかとか

最後に

dozens の rubygem を試してみました
ドキュメントがほぼないので、ソースをみるか rubydoc を見るしかないですが簡単に使えました

dozens の API 自体に非常にシンプルなので自分で SDK を作ってもいいかもしれません

参考サイト

2017年6月10日土曜日

harbor に SSL 証明書を設定して https でレジストリを使うための手順

概要

harbor を https で使うための手順を紹介します
harbor はデフォルトで http なので、わざわざ docker クライアント側の insecure-registry に登録する処理が必要になります
ドメインと証明書が使える環境であればレジストリ自体 https を使うことが推奨されています
今回はドメインと証明書を用意して harbor を https で使えるようにしてみました

環境

  • Ubuntu 16.04
  • harbor 1.1.1
  • docker 17.05

事前準備

ドメインを取得して A レコードを登録して証明書を取得します
この流れは過去に紹介もしているので過去の手順でも OK です

ドメインの取得

何でも OK です
とりあえずドメインを取得してください

A レコードの追加

取得したドメインのサブドメインを A レコードとして DNS サーバに登録します
今回登録したレジストリのサブドメインは「reg01.harbor.gq」とします

証明書の作成

とりあえず A レコードで登録したサブドメインの証明書を 1 つ作成しましょう
同一ドメインでレジストリを何台も構築する場合はワイルドカード SSL 証明書で作成することをオススメします
そして作成した証明書は harbor をインストールするサーバに転送しておきましょう

  • scp -r /etc/letsencrypt/archive/reg01.harbor.gq 192.168.100.201:~

harbor の設定

事前準備

今回は Ubuntu 上に harbor を構築します
事前に docker と docker-compose が必要になるので準備しておきましょう (参考)

設定ファイルの編集

インストーラをダウンロードして展開し、設定ファイルの証明書を設定する部分を編集します

hostname = reg01.harbor.gq
ui_url_protocol = https
ssl_cert = /root/reg01.harbor.gq/fullchain1.pem
ssl_cert_key = /root/reg01.harbor.gq/privkey1.pem
  • ./prepare

customize_crt = on という項目がありますがこれは on のままにしておかないてください
証明書を転送したときの転送先は /root/reg01.harbor.gq/ に転送しているのでそのパスを指定しています

インストールと起動

設定が変更できたらインストールしましょう

  • ./install

ダラダラとログが流れて最終的にコンテナが上がってきます
同一ディレクトリ内に docker-compose.yml があるので docker-compose ps などでコンテナを確認することができます

これで証明書が設定されて https でアクセスできるようになっているはずです

動作確認

docker クライアントから https でアクセスできるか確認してみましょう

  • docker login reg01.harbor.gq

admin/Harbor12345 でログインしましょう
ちゃんと証明書が設定されていれば怒られないはずです

  • docker pull alpine
  • docker tag alpine reg01.harbor.gq/library/alpine
  • docker push reg01.harbor.gq/library/alpine

https://reg01.harbor.gq/harbor/tags/1/library%2Falpine にアクセスすると alpine の latest が存在することがわかります
pull もできるか確認してみましょう

  • docker rmi reg01.harbor.gq/library/alpine
  • docker pull reg01.harbor.gq/library/alpine:latest
  • docker logout reg01.harbor.gq

ログアウトしてから pull するとできないのが確認できると思います

最後に

harbor に https でアクセスできるようにしてみました
ドメインと証明書が取れれば簡単に設定することができます

以下の証明書のエラーが出た場合は再度コンテナを生成し直してみてください

Error response from daemon: Get https://reg01.harbor.gq/v1/users/: dial tcp 192.168.100.201:443: getsockopt: no route to host
  • docker-compose down
  • docker rmi $(docker images -q)
  • ./prepare
  • docker-compose up -d

参考サイト

2017年6月9日金曜日

govmomi で指定の VApp をフォルダ配下に移動させる

概要

VApp をフォルダ配下に移動する方法を紹介します
1 つの VApp をあるフォルダに移動します

環境

  • Ubuntu 16.04
  • golang 1.8
  • govmomi 0.14.0

ソースコード

  • vim main.go
package main

import (
        "context"
        "flag"
        "fmt"
        "net/url"
        "os"

        "github.com/vmware/govmomi"
        "github.com/vmware/govmomi/find"
        "github.com/vmware/govmomi/property"
        "github.com/vmware/govmomi/vim25/mo"
        "github.com/vmware/govmomi/vim25/types"
)

var envURL = "https://192.168.100.101/sdk"
var urlDescription = fmt.Sprintf("ESX or vCenter URL [%s]", envURL)
var urlFlag = flag.String("url", envURL, urlDescription)

var envInsecure = true
var insecureDescription = fmt.Sprintf("Don't verify the server's certificate chain [%s]", envInsecure)
var insecureFlag = flag.Bool("insecure", envInsecure, insecureDescription)

func main() {
        // vCenter への接続
        ctx, cancel := context.WithCancel(context.Background())
        defer cancel()
        flag.Parse()
        u, err := url.Parse(*urlFlag)
        if err != nil {
                fmt.Println(err)
                os.Exit(1)
        }
        u.User = url.UserPassword("vcenter_user", "vcenter_pass")
        c, err := govmomi.NewClient(ctx, u, *insecureFlag)
        if err != nil {
                fmt.Println(err)
                os.Exit(1)
        }
        fmt.Println(c.Client.Client.Version)

        // データセンターの取得
        f := find.NewFinder(c.Client, true)
        dc, err := f.DefaultDatacenter(ctx)
        if err != nil {
                fmt.Println(err)
                os.Exit(1)
        }
        f.SetDatacenter(dc)
        fmt.Println(dc)

        // フォルダの取得
        folder, err := f.Folder(ctx, "/dc/vm/folder/")
        if err != nil {
                fmt.Println(err)
                os.Exit(1)
        }
        fmt.Println(folder.Common.InventoryPath)
        // VirtualApp の取得
        varefs := []types.ManagedObjectReference{}
        va, err := f.VirtualApp(ctx, "/dc/vm/vapp")
        if err != nil {
                fmt.Println(err)
                os.Exit(1)
        }
        fmt.Println(va.Common.InventoryPath)
        varefs = append(varefs, va.Reference())
        // フォルダへ移動
        t, err := folder.MoveInto(ctx, varefs)
        if err != nil {
                fmt.Println(err)
                os.Exit(1)
        }
        info, err := t.WaitForResult(context.Background(), nil)
        if err != nil {
                fmt.Println(err)
                os.Exit(1)
        }
        fmt.Println(info.State)
}

ポイントはフォルダのオブジェクトに対して実行している MoveInto メソッドになります
この引数に moref を指定することで指定した moref をフォルダに移動するという処理を行っています
対象の VApp に対して移動せよという命令をするわけではなく、フォルダに対してこのフォルダに入れるという処理をしているのがポイントです

実行後に WaitForResult を使って処理が完了するのを待っています
完了後 info.State で結果を表示して終了です

今回のコードの場合、指定するフォルダと VApp のパスが存在しない場合はエラーになります
なので、一度実行したあと再度実行したい場合は対象の VApp を元のパスに戻す必要があります

最後に

govmomi を使って VApp を特定のフォルダに移動してみました
参照系よりか簡潔に書くことができました

参考サイト

2017年6月8日木曜日

govmomi で VApp の情報を取得する方法

概要

vCenter 配下で管理されている VApp の情報を govmomi を使って取得してみます

環境

  • Ubuntu 16.04
  • golang 1.8
  • govmomi 0.14.0

ソースコード

  • vim main.go
package main

import (
        "context"
        "flag"
        "fmt"
        "net/url"
        "os"

        "github.com/vmware/govmomi"
        "github.com/vmware/govmomi/find"
        "github.com/vmware/govmomi/property"
        "github.com/vmware/govmomi/vim25/mo"
        "github.com/vmware/govmomi/vim25/types"
)

var envURL = "https://192.168.100.101/sdk"
var urlDescription = fmt.Sprintf("ESX or vCenter URL [%s]", envURL)
var urlFlag = flag.String("url", envURL, urlDescription)

var envInsecure = true
var insecureDescription = fmt.Sprintf("Don't verify the server's certificate chain [%s]", envInsecure)
var insecureFlag = flag.Bool("insecure", envInsecure, insecureDescription)

func main() {
        // vCenter への接続
        ctx, cancel := context.WithCancel(context.Background())
        defer cancel()
        flag.Parse()
        u, err := url.Parse(*urlFlag)
        if err != nil {
                fmt.Println(err)
                os.Exit(1)
        }
        u.User = url.UserPassword("vcenter_user", "vcenter_pass")
        c, err := govmomi.NewClient(ctx, u, *insecureFlag)
        if err != nil {
                fmt.Println(err)
                os.Exit(1)
        }
        fmt.Println(c.Client.Client.Version)

        // データセンターの取得
        f := find.NewFinder(c.Client, true)
        dc, err := f.DefaultDatacenter(ctx)
        if err != nil {
                fmt.Println(err)
                os.Exit(1)
        }
        f.SetDatacenter(dc)
        fmt.Println(dc)

        // 全 VirtualApp の取得
        pc := property.DefaultCollector(c.Client)
        varefs := []types.ManagedObjectReference{}
        vas, err := f.VirtualAppList(ctx, "*")
        if err != nil {
                fmt.Println(err)
                os.Exit(1)
        }
        for _, va := range vas {
                fmt.Println(va.Common.InventoryPath)
                varefs = append(varefs, va.Reference())
        }
        var vadst []mo.VirtualApp
        err = pc.Retrieve(ctx, varefs, nil, &vadst)
        if err != nil {
                fmt.Println(err)
                os.Exit(1)
        }
        for _, va := range vadst {
                fmt.Println(va.VAppConfig.Annotation)
                fmt.Println(va.VAppConfig.EntityConfig) // VApp 内の VM の情報
                fmt.Println(va.Name)
        }
}

まず vCenter に接続しデータセンターの情報を取得します
その後 f.VirtualAppList を使ってすべての VApp の情報を取得します
取得後は types.ManagedObjectReference に突っ込んで pc.Retrieve することで VApp の情報を取得しています
あとは types.ManagedObjectReference に設定された値をループで回して参照しているだけです

VApp の場合 VApp 内に VM が存在することがあると思います
VApp 内の子の要素を参照するには EntityConfig を使えば参照できそうだったので、そこから VM の情報を取得できると思います

VApp の別の情報を参照したい場合は以下を参照してください
https://godoc.org/github.com/vmware/govmomi/vim25/mo#VirtualApp

最後に

govmomi を使って VApp の情報を参照していました
これまで紹介したフォルダや VM を取得する方法とほぼ同じなので特に躓くところはありませんでした

参考サイト

2017年6月7日水曜日

govmomi でフォルダの情報を取得する方法

概要

vCenter 配下で管理されているフォルダ情報を govmomi を使って取得してみます
すべてのフォルダを取得する方法と単体で取得する方法を紹介します

環境

  • Ubuntu 16.04
  • golang 1.8
  • govmomi 0.14.0

ソースコード

  • vim main.go
package main

import (
        "context"
        "flag"
        "fmt"
        "net/url"
        "os"

        "github.com/vmware/govmomi"
        "github.com/vmware/govmomi/find"
        "github.com/vmware/govmomi/property"
        "github.com/vmware/govmomi/vim25/mo"
        "github.com/vmware/govmomi/vim25/types"
)

var envURL = "https://192.168.100.101/sdk"
var urlDescription = fmt.Sprintf("ESX or vCenter URL [%s]", envURL)
var urlFlag = flag.String("url", envURL, urlDescription)

var envInsecure = true
var insecureDescription = fmt.Sprintf("Don't verify the server's certificate chain [%s]", envInsecure)
var insecureFlag = flag.Bool("insecure", envInsecure, insecureDescription)

func main() {
        // vCenter への接続
        ctx, cancel := context.WithCancel(context.Background())
        defer cancel()
        flag.Parse()
        u, err := url.Parse(*urlFlag)
        if err != nil {
                fmt.Println(err)
                os.Exit(1)
        }
        u.User = url.UserPassword("vcenter_user", "vcenter_pass")
        c, err := govmomi.NewClient(ctx, u, *insecureFlag)
        if err != nil {
                fmt.Println(err)
                os.Exit(1)
        }
        fmt.Println(c.Client.Client.Version)

        // データセンターの取得
        f := find.NewFinder(c.Client, true)
        dc, err := f.DefaultDatacenter(ctx)
        if err != nil {
                fmt.Println(err)
                os.Exit(1)
        }
        f.SetDatacenter(dc)
        fmt.Println(dc)

        // フォルダの取得
        pc := property.DefaultCollector(c.Client)
        frefs := []types.ManagedObjectReference{}
        folder, err := f.Folder(ctx, "/dc/vm/folder_name/")
        if err != nil {
                fmt.Println(err)
                os.Exit(1)
        }
        fmt.Println(folder.Common.InventoryPath)
        frefs = append(frefs, folder.Reference())
        var fdst []mo.Folder
        err = pc.Retrieve(ctx, frefs, nil, &fdst)
        if err != nil {
                fmt.Println(err)
                os.Exit(1)
        }
        for _, f := range fdst {
                fmt.Println(f.Name)
                fmt.Println(f.Tag)
        }
        // 全フォルダの取得
        fsrefs := []types.ManagedObjectReference{}
        folders, err := f.FolderList(ctx, "*")
        if err != nil {
                fmt.Println(err)
                os.Exit(1)
        }
        for _, folder := range folders {
                fmt.Println(folder.Common.InventoryPath)
                fsrefs = append(fsrefs, folder.Reference())
        }
        var fsdst []mo.Folder
        err = pc.Retrieve(ctx, fsrefs, nil, &fsdst)
        if err != nil {
                fmt.Println(err)
                os.Exit(1)
        }
        for _, f := range fsdst {
                fmt.Println(f.Name)
                fmt.Println(f.Tag)
        }
}

まず vCenter に接続しデータセンターの情報を取得します
その後 f.Folder にフォルダのパスを指定することでフォルダ単体を取得しています
取得後は types.ManagedObjectReference に突っ込んで pc.Retrieve することでフォルダの情報を取得しています
あとは types.ManagedObjectReference に設定された値をループで回して参照しているだけです

全件取得の場合には f.FolderList(ctx, "*") とパスの部分をアスタリスクにすることで実現できます
あとは同じ流れで moref に突っ込んで pc.Retrieve で設定し、moref をループで回して参照するだけです

今回は Name と Tag を参照していますが、フォルダの他の情報を参照したい場合は godoc を見てください
https://godoc.org/github.com/vmware/govmomi/vim25/mo#Folder

最後に

govmomi を使ってフォルダを参照する方法を紹介しました
フォルダのオブジェクトが取得できるようになればフォルダに対する操作もできるようになります
例えば削除や名前の変更 VM をフォルダに移動することなどができるようになります

その辺の操作周りに関しても別記事で紹介できればなと思います

参考サイト

2017年5月31日水曜日

ESXi に独自のファイアウォールルールを追加する方法

概要

例えばあるポートの Outgoing を許可したい場合などです
実は vSphere Client からできなく直接 ESXi にログインしてコマンドで作業する必要があります
今回はその方法を紹介したいと思います

環境

  • ESXi 6.5.0, 4887370

設定方法

事前に ESXi の SSH ログインの有効と root ユーザのパスワードの設定を実施しておいてください
参考

  • cp /etc/vmware/firewall/service.xml /etc/vmware/firewall/service.xml.back
  • chmod 644 /etc/vmware/firewall/service.xml
  • chmod +t /etc/vmware/firewall/service.xml
<service id='0044'>
  <id>vchAccess</id>
  <rule id='0000'>
    <direction>outbound</direction>
    <protocol>tcp</protocol>
    <porttype>dst</porttype>
    <port>2377</port>
  </rule>
  <enabled>true</enabled>
  <required>true</required>
</service>
  • chmod 444 /etc/vmware/firewall/service.xml
  • esxcli network firewall refresh

Firewall のルールを管理している service.xml というファイルがあるのでそれを直接編集します
そしてその後 esxcli コマンドで設定を反映し完了となります

動作確認として vSphere Client でちゃんとポートがオープンになっているか確認してみましょう

ESXi を選択 -> 設定タブ -> 左サブメニューからシステム -> セキュリティプロファイル

で確認できます
今回であれば 2377 ポートが一覧に表示されていれば OK です

2017年5月30日火曜日

go-swagger で XML なレスポンスを返却する方法

概要

デフォルトだと application/json としてレスポンスが返却されます
レスポンスを XML として返却できないか試してみたので紹介します

環境

  • CentOS 7.3.1611
  • golang 1.8
  • swagger 2.0
  • go-swagger 0.10.0

swagger.yml

  • vim swagger.yml
consumes:
- application/json
info:
  description: The product of a tutorial on goswagger.io
  title: A To Do list application
  version: 1.0.0
produces:
- application/xml
schemes:
- http
swagger: "2.0"
basePath: /v1
definitions:
  item:
    type: object
    required:
      - description
    properties:
      id:
        type: integer
        format: int64
        readOnly: true
      description:
        type: string
        minLength: 1
      completed:
        type: boolean
  error:
    type: object
    required:
      - message
    properties:
      code:
        type: integer
        format: int64
      message:
        type: string
paths:
  /:
    get:
      tags:
        - todos
      operationId: findTodos
      parameters:
        - name: since
          in: query
          type: integer
          format: int64
        - name: limit
          in: query
          type: integer
          format: int32
          default: 20
      responses:
        200:
          description: list the todo operations
          schema:
            type: array
            items:
              $ref: "#/definitions/item"
        default:
          description: generic error response
          schema:
            $ref: "#/definitions/error"

ポイントは produces の部分でここでレスポンス時の Content-Type を application/xml に固定します

produces:
- application/xml

コード生成

  • swagger generate server -f swagger.yml

メイン部分修正

  • vim configure_a_to_do_list_application.go

一部抜粋です
適当に値を返す処理を追加します

api.TodosFindTodosHandler = todos.FindTodosHandlerFunc(func(params todos.FindTodosParams) middleware.Responder {
        result := make([]*models.Item, 0)
        item := new(models.Item)
        item.Completed = true
        s := "hoge"
        item.Description = &s
        result = append(result, item)
        return todos.NewFindTodosOK().WithPayload(result)
})

アプリ起動

  • go install ./cmd/a-to-do-list-application-server/
  • a-to-do-list-application-server --host 0.0.0.0 --port 18080

動作確認

  • curl localhost:18080/v1 | xmllint --format -

とすると

<?xml version="1.0"?>
<Item>
  <Completed>true</Completed>
  <Description>hoge</Description>
  <ID>0</ID>
</Item>

という感じで XML が返ってきます
-v で詳細を見ると Content-Type も application/xml となっているのが確認できると思います

追加調査

とりあえずこれで自分が実装する API の部分に関しては XML で返却することができます
ただ、go-swagger がデフォルトで実装しているエラーハンドリングの部分に関しては json で返ってきてしまいます
例えば今回であれば「/」にアクセスした場合、Not Found のエラーになるのですが、それが

{"code":404,"message":"path / was not found"}

となってしまいます
他には Method Not Allowed なども json になってしまいます

{"code":405,"message":"method POST is not allowed, but [GET] are"}

このあたりのデフォルトエラーに関しても XML にしたいと思います
デフォルトエラーのカスタマイズ方法についてはまだ調査できていないので別途調査が必要かなと思います

最後に

go-swagger で XML のレスポンスを返却する方法を紹介しました
基本は consumes の設定を変更するだけで XML にすることができました

2017年5月26日金曜日

Xcode で Authenticating with the iTunes Store でアップロードが止まる

概要

久しぶりにアプリのアップデートしようとしたら Xcode でタイトルのエラーが出てしまいアプリをアップロードできなくなってしまいました
自分の対応方法を紹介します
xcode_upload_error1.png

環境

  • macOS 10.12.4
  • Xcode 8.3.2 (8E2002)

Application Loader に一度ログインしてみる

Application Loader は Xcode に付属する ipa ファイルを iTunesStore にアップロードするためのツールです
Xcode を開いた状態で

  • Xcode -> Open Devloper Tools -> Application Loader

と選択します
すると AppleID でログインを求められるのでログインします
xcode_upload_error2.png

ここでログインするパスワードは AppleID のパスワードではなくアプリケーションパスワードというワンタイムパスワードを使います
アプリケーションパスワードは AppleID の管理画面 から App 用パスワード -> パスワードを生成で作成することができます
ランダムの文字列のパスワードが取得できたらそれを使って Application Loader にログインします

今回は Application Loader にログインするのが目的なのでログインできたら Application Loader は閉じて OK です
これで再度 Xcode からアプリをアップロードしてみましょう

それでもダメなら iTMSTransporter を初期化してみる

iTMSTransporter は iTuneStore にアップロードするツールです
おそらく Xcode もバックエンドではこれを使っているはずです
一旦これの設定ファイルを作成することで対処することができます

  • rm -rf ~/.itmstransporter/
  • /Applications/Xcode.app/Contents/Applications/Application\ Loader.app/Contents/itms/bin/iTMSTransporter

デバッグログがつらつらと流れるので終了するまで待ちましょう
完了したら Xcode から再度アプリをアップロードしてみましょう

ちなみに自分はこっちの方法を試すことで対処することができました

最後に

Xcode で iTunesStore にアプリをアップロードする際に発生した Authenticating Error の対応をしてみました
他の Xcode のバージョンや macOS のバージョンだと発生しない可能性はありますが発生した場合は試してみると良いと思います

参考サイト

2017年5月25日木曜日

au SIM ロックの iPhone5 を復活させたのでメモ

概要

すごい昔に au で契約した SIM ロックな iPhone5 が眠りについていたので復活させてみました

環境

  • macOS 10.12.4
  • docker 17.05
  • docker registry 2.6.1

なぜ眠りについてしまったのか

iOS9 の時代に 2 年縛りが終了し au は解約しました
その後 Wifi のみで使い続けていたのですが、その時に iOS9 から iOS10 にアップグレードしました
iOS10 にアップグレードした際に iPhone のアクティベーションが必要でその際に実は SIM カードが必要でした
そのことを知らず au 解約時に SIM カードを捨ててしまったため iPhone5 は眠りについていました

その後いろいろと調べると解約済みでもいいので au の SIM カードがあればアクティベーションはできることを知り復活させることにしました
家族用の au SIM カードをありそれで SIM カードは代用しています

手順

電源が入らない

ずっと眠りについていたのでバッテリーが空になっていました
ライトニングでとりあえず充電していると勝手に起動し始めました

iPhone5 はバッテリーが空の場合、充電中であっても電源ボタンを押しても起動しません
なので、ある程度充電されるまで気長に待ちましょう

アクティベーション作業

SIM カードを挿しましょう
SIM カードは解約している SIM カードなので、通信はできません
なので変わりに Wifi を設定します
アクティベーションの途中で Wifi を設定するところがあるので設定します
すると Wifi で iPhone5 のアクティベーションが完了します

その後 AppleID でログインするように促されます
AppleID が 2 ファクタ認証になっている場合はここでパスワード以外に 6 桁のコードでの認証も必要になります
基本的には同じ AppleID で使用している iPhone や Mac にコードが送られてくるのでそのコードを使います
(このとき別の端末がない人はどうなるんだろうと思いました、実は別端末がなく 2 ファクタ認証をオンにしている人はここで詰んでしまうのだろうか、、、)

アクティベーションが完了し起動したあとは iPhone の初期化を行います

初期化

アクティベーション後は前回の iPhone の状態で起動します
今回自分は完全に初期化 (工場出荷時に初期化) したかったのでリセット作業を行いました

  • 設定 -> 一般 -> リセット -> 初期化 -> すべての設定をリセット
  • 設定 -> 一般 -> リセット -> 初期化 -> すべてのコンテンツと設定を消去

でリセットします
下が完全リセットなので、下だけ実行すればいいかもしれません
リセット後は起動したら iPhone 自体の初期化から始まります

iPhone の初期設定

以下指示に従い設定していきましょう
また、この段階でも SIM を挿しておきましょう
完全に初期化しているので再度アクティベーションが走ります

  • 言語、タイムゾーンの設定
  • Wifi の設定
  • アクティベーション (wifi で失敗する場合は何度か繰り返す)
  • GPS 設定
  • パスワード設定 (iPhone5 のためタッチ ID の設定はなし)
  • AppleID でログイン、2 ファクタ認証でログイン

であとは iPhone にログイン後完全に初期化した状態で普通に使うことができます

最後に

アクティベーションできずにいた iPhone5 を復活させてみました
ポイントは「解約済みでもいいので SIM カードを挿す」ことでした

当然自宅に Wifi 環境がないと何もできませんが、Wifi があえば iPod touch 的な使い方は全然できます

2017年5月24日水曜日

mackerel で docker コンテナを監視する

概要

簡単な手順を備忘録として残しておきます
起動中のコンテナから基本的なメトリックを取得したいのであれば十分かなとは思います

環境

  • CentOS 7.3.1611
  • docker 17.03

事前作業

  1. オーガニゼーションを作成
  2. サービスを作成
  3. サービス内にロールを作成
  4. エージェントのインストール
    • curl -fsSL https://mackerel.io/file/script/setup-all-yum.sh | MACKEREL_APIKEY='your-mackerel-apikey' sh

でホストの監視がスタートします

docker コンテナの監視

プラグインをインストールすることで実現します

  • yum install mackerel-agent-plugins

インストールできたら設定ファイルを編集します
docker plugin を有効にする設定を追記したらエージェントを再起動しましょう

  • vim /etc/mackerel-agent/mackerel-agent.conf
[plugin.metrics.docker]
command = "mackerel-plugin-docker -name-format name -method API"
  • systemctl restart mackerel-agent
  • tail -f /var/log/mackerel-agent.log

ログにエラーが吐かれていなければ OK です

動作確認

コンソールから Hosts -> localhost.localdomain -> カスタムメトリック に起動中のコンテナのメトリックが表示されると思います
表示される項目は

  • ブロック I/O バイト
  • ブロック I/O IOPS
  • CPU 使用率
  • メモリ使用率

が表示されます

mackerel-docker1.png

最後に

mackerel で docker コンテナのメトリックを取得してみました
手順がかなり簡単なので嬉しい点です

docker events 等の監視はしていないのでコンテナの死活監視は現状できないようです
ただ、削除されたコンテナのメトリックはコンテナが存在していた間は残るのでこれをうまく使えば死活監視的なことはできるかもしれません

参考サイト

2017年5月23日火曜日

VMware VIC 1.1.1 を触ってみたのでメモ

概要

知らぬ間に MyVMware からダウンロードできる VMware Integrated Container が 1.1.1 までバージョンアップしていたので検証してみました
1.1.1 から提供方式も ova になっているようです

環境

  • CentOS 7.3.1611
  • docker 17.05
  • VMware VIC 1.1.1

VIC 1.1.1 のダウンロード

以下の URL からダウンロードできます
https://my.vmware.com/group/vmware/details?downloadGroup=VIC110&productId=648&rPId=15925

自身のアカウントで MyVMware にログイン後ダウンロードしてください
vSphere Integrated Containers 1.1.1 OVA のサイズは 2.69GB あります

ova のデプロイ

基本的な方法はこちらを参照してください

デプロイ時に必要となるディスク際は 70 GB + 4.7 GB になります
ネットワークはデフォルトだと 1 つのみ設定できます
VIC の ova をデプロイする際に必須となるパラメータは以下の通りです

  • VM (Photon) に ssh ログインする際の root ユーザのパスワード
  • registry の admin ユーザのパスワード
  • MySQL の root ユーザのパスワード

デプロイ後の挙動の確認

ova デプロイが完了したら電源を ON にして VM を起動しましょう
設定したネットワークの IP が取得できれば起動完了です

管理画面が 2 つあり

Admiral 側は特にログイン用の ID/PW はありません
Harbor 側は ova デプロイ時に設定した registry の admin ユーザのパスワードでログインできます

また、VM は Photon OS で動作しています
SSH でログインできるのでログインしてみましょう
docker コマンドが使えるのでコンテナの一覧を確認すると以下が動作しています

  • docker ps
ONTAINER ID        IMAGE                                     COMMAND                  CREATED             STATUS              PORTS                                                  NAMES
6405166b4027        vmware/notary-photon:server-0.5.0         "/usr/bin/env sh -..."   55 minutes ago      Up 55 minutes                                                              notary-server
7414d1b3852b        vmware/nginx:1.11.5-patched               "nginx -g 'daemon ..."   55 minutes ago      Up 55 minutes       0.0.0.0:443->443/tcp, 80/tcp, 0.0.0.0:4443->4443/tcp   nginx
e6ae3a5f914a        vmware/harbor-jobservice:v1.1.1           "/harbor/harbor_jo..."   55 minutes ago      Up 55 minutes                                                              harbor-jobservice
b6e384167707        vmware/notary-photon:signer-0.5.0         "/usr/bin/env sh -..."   55 minutes ago      Up 55 minutes                                                              notary-signer
5b8ac98ebfab        vmware/harbor-ui:v1.1.1                   "/harbor/harbor_ui"      55 minutes ago      Up 55 minutes                                                              harbor-ui
647d830a9f49        vmware/registry:photon-2.6.0              "/entrypoint.sh se..."   55 minutes ago      Up 55 minutes       5000/tcp                                               registry
4dd9ef6abaed        vmware/harbor-db:v1.1.1                   "docker-entrypoint..."   55 minutes ago      Up 55 minutes       3306/tcp                                               harbor-db
904b46e34f20        vmware/harbor-adminserver:v1.1.1          "/harbor/harbor_ad..."   55 minutes ago      Up 55 minutes                                                              harbor-adminserver
f21e09a82015        vmware/harbor-notary-db:mariadb-10.1.10   "/docker-entrypoin..."   55 minutes ago      Up 55 minutes       3306/tcp                                               notary-db
07fa6ffcf209        vmware/harbor-log:v1.1.1                  "/bin/sh -c 'crond..."   55 minutes ago      Up 55 minutes       127.0.0.1:1514->514/tcp                                harbor-log
6ffc63081e6a        vmware/admiral:ova                        "/entrypoint.sh"         55 minutes ago      Up 55 minutes       0.0.0.0:8282->8282/tcp                                 vic-admiral

Admiral と Harbor 用のコンテナが起動しています

また network が全部で 7 つ作成されています

  • docker network ls
NETWORK ID          NAME                   DRIVER              SCOPE
816285492ff8        bridge                 bridge              local
4b1b23d24347        harbor_harbor          bridge              local
848a011f52ff        harbor_harbor-notary   bridge              local
b9a1717bb5e2        harbor_notary-mdb      bridge              local
091f255fbb90        harbor_notary-sig      bridge              local
832437bb1e05        host                   host                local
3f9ca92ef166        none                   null                local

どのコンテナがどのネットワークに属しているかは

  • docker network inspect harbor_harbor

とかで確認できます

registry を使ってみる

registry 機能には UI とコンテナレジストリの機能があります
UI はブラウザで 443 ポートにアクセスすれば OK です
IP ベースでレジストリを使用する際には先に証明書の配置が必要なので後述の手順を先に実施しておいてください

  • docker pull nginx
  • docker tag nginx 192.168.100.202/library/alpine
  • docker login
  • docker push nginx 192.168.100.202/library/alpine

docker login 時の ID/PW は管理 UI にログインしたものと同じになります
管理 UI で確認するとイメージが push されているのが確認できると思います

registry 用の証明書の配置

一点注意点としてデフォルトだと https でデプロイされてしまいます
証明書がちゃんと設定されていないレジストリにアクセスすることはできないので、自己証明書をダウンロードして dockerd の所定のパスに配置する必要があります
自己証明書をダウンロードするには管理画面にアクセスしてログインします
そして右上の Admin -> Download Root Cert から ca.crt を取得します

取得したら docker コマンドを実行ホストに配置します

  • mkdir -p /etc/docker/certs.d/192.168.100.202
  • cp ca.crt /etc/docker/certs.d/192.168.100.202

Admiral を使ってみる

Admiral はコンテナホストマシンのクラスタリングを実現するための機能です
管理 UI が提供されているので https://192.168.100.202:8282/ にアクセスします

Admiral は過去に試した使い方とほぼ同じです
VCH を作成したあとに Admiral に登録すると docker swarm のように VCH をクラスタリングすることができます

VCH に関して

これはどうやら従来通りコマンドを使って作成するようです
実は、今回デプロイした ova の中には含まれていて以下のリンクからダウンロードすることができます

すると申し訳ない程度のダウンロードページに遷移し、そこから vic_1.1.1.tar.gz というファイルをダウンロードすることができます
展開すると vic-machine-linux が含まれていました
しかもダウンロードページはコンテナで動作しておらず PhotonOS VM のローカルで 9443 が LISTEN しておりそこからダウンロードできるようになっていました

何か VCH だけ除け者扱いされている感がありますが、一応バイナリをダウンロードすることはできるようです

最後に

VMware VIC 1.1.1 では Harbor と Admiral が同じ ova となって配布されていました
VCH は従来通り vic-machine を使って作成する形式となっており、まだ管理 UI から VCH を作成することはできないようです
一応バイナリは ova に含まれているのでダウンロードをできることは確認しました

触ってみた感じコンテナ技術に必要なコンポーネント (コンテナホスト、レジストリ、クラスタリングツール) が 1 つになって提供されている感じでした
VMware vSphere 環境がある方は ova を 1 つデプロイするだけですぐにコンテナ環境が構築できるので便利かなと思いました

今後は VCH の作成も UI からできるようになるかもしれません

参考サイト