2015/10/28

[Tomcat][鯖]Tomcat でクライアント認証

概要

環境構築

作業ディレクトリ作成

$ cd ~/tmp/ssl
$ mkdir ca
$ mkdir ca/newcerts ca/crl ca/private client
$ touch ca/index.txt
$ echo 00 > ca/serial
$ echo 00 > ca/crlnumber
$ cp /usr/ssl/openssl.cnf ./

dir を ca の絶対パスに修正

$ vim ./openssl.cnf

CA 環境構築

CA 秘密鍵を作成

$ openssl genrsa -out ca/private/cakey.pem 2048 -config openssl.cnf

CA 証明書要求を作成

$ openssl req -new -key ca/private/cakey.pem -out ca/cacsr.pem -config openssl.cnf
You are about to be asked to enter information that will be incorporated
into your certificate request.
What you are about to enter is what is called a Distinguished Name or a DN.
There are quite a few fields but you can leave some blank
For some fields there will be a default value,
If you enter '.', the field will be left blank.
-----
Country Name (2 letter code) [AU]:JP
State or Province Name (full name) [Some-State]:Tokyo
Locality Name (eg, city) []:Minato-ku
Organization Name (eg, company) [Internet Widgits Pty Ltd]:foo
Organizational Unit Name (eg, section) []:bar
Common Name (e.g. server FQDN or YOUR name) []:localhost
Email Address []:

Please enter the following 'extra' attributes
to be sent with your certificate request
A challenge password []:
An optional company name []:

CA 証明書を作成

$ openssl x509 -req -in ca/cacsr.pem -signkey ca/private/cakey.pem -out ca/cacert.pem

クライアント環境構築

クライアント秘密鍵作成

$ openssl genrsa -out client/clientkey.pem 2048 -config ./openssl.cnf

クライアント証明書要求を作成

$ openssl req -new -key client/clientkey.pem -out client/clientcsr.pem -config ./openssl.cnf
You are about to be asked to enter information that will be incorporated
into your certificate request.
What you are about to enter is what is called a Distinguished Name or a DN.
There are quite a few fields but you can leave some blank
For some fields there will be a default value,
If you enter '.', the field will be left blank.
-----
Country Name (2 letter code) [AU]:JP
State or Province Name (full name) [Some-State]:Tokyo
Locality Name (eg, city) []:Minato-ku
Organization Name (eg, company) [Internet Widgits Pty Ltd]:foo
Organizational Unit Name (eg, section) []:bar
Common Name (e.g. server FQDN or YOUR name) []:localhost
Email Address []:

Please enter the following 'extra' attributes
to be sent with your certificate request
A challenge password []:
An optional company name []:

クライアント証明書作成

クライアントから csr を受け取った CA がやる作業。

$ openssl ca -config ./openssl.cnf -out client/client.crt -infiles client/clientcsr.pem
Using configuration from ./openssl.cnf
Check that the request matches the signature
Signature ok
Certificate Details:
        Serial Number: 0 (0x0)
        Validity
            Not Before: Oct 27 17:33:35 2015 GMT
            Not After : Oct 26 17:33:35 2016 GMT
        Subject:
            countryName               = JP
            stateOrProvinceName       = Tokyo
            organizationName          = foo
            organizationalUnitName    = bar
            commonName                = localhost
        X509v3 extensions:
            X509v3 Basic Constraints:
                CA:FALSE
            Netscape Comment:
                OpenSSL Generated Certificate
            X509v3 Subject Key Identifier:
                E1:44:51:CF:68:FC:3D:E1:52:59:F3:C3:E9:09:B4:64:CC:81:47:7C
            X509v3 Authority Key Identifier:
                DirName:/C=JP/ST=Tokyo/L=Minato-ku/O=foo/OU=bar/CN=localhost
                serial:E3:E6:4D:2B:0C:20:5A:E2

Certificate is to be certified until Oct 26 17:33:35 2016 GMT (365 days)
Sign the certificate? [y/n]:y


1 out of 1 certificate requests certified, commit? [y/n]y
Write out database with 1 new entries
Data Base Updated

サーバー環境構築

サーバー秘密鍵とキーストア作成

$ keytool -genkey -alias tomcat -keyalg RSA -keystore ../../.keystore
キーストアのパスワードを入力してください:  changeit
新規パスワードを再入力してください: changeit
姓名は何ですか。
  [Unknown]:  localhost
組織単位名は何ですか。
  [Unknown]:  bar
組織名は何ですか。
  [Unknown]:  foo
都市名または地域名は何ですか。
  [Unknown]:  Minato-ku
都道府県名または州名は何ですか。
  [Unknown]:  Tokyo
この単位に該当する2文字の国コードは何ですか。
  [Unknown]:  JP
CN=localhost, OU=bar, O=foo, L=Minato-ku, ST=Tokyo, C=JPでよろしいですか。
  [いいえ]:  y

<tomcat>の鍵パスワードを入力してください
        (キーストアのパスワードと同じ場合はRETURNを押してください):

サーバー証明書要求作成

$ keytool -certreq -alias tomcat -file tomcat.csr -keystore ../../.keystore

サーバー証明書作成

サーバーから csr を受け取った CA がやる作業。

$ openssl ca -policy policy_anything -config ./openssl.cnf -out tomcat.crt -infiles tomcat.csr
Using configuration from ./openssl.cnf
Check that the request matches the signature
Signature ok
Certificate Details:
        Serial Number: 1 (0x1)
        Validity
            Not Before: Oct 27 17:47:23 2015 GMT
            Not After : Oct 26 17:47:23 2016 GMT
        Subject:
            countryName               = JP
            stateOrProvinceName       = Tokyo
            localityName              = Minato-ku
            organizationName          = foo
            organizationalUnitName    = bar
            commonName                = localhost
        X509v3 extensions:
            X509v3 Basic Constraints:
                CA:FALSE
            Netscape Comment:
                OpenSSL Generated Certificate
            X509v3 Subject Key Identifier:
                5A:E0:56:79:25:16:46:39:DD:33:89:59:67:91:0F:5F:96:49:CB:10
            X509v3 Authority Key Identifier:
                DirName:/C=JP/ST=Tokyo/L=Minato-ku/O=foo/OU=bar/CN=localhost
                serial:E3:E6:4D:2B:0C:20:5A:E2

Certificate is to be certified until Oct 26 17:47:23 2016 GMT (365 days)
Sign the certificate? [y/n]:y


1 out of 1 certificate requests certified, commit? [y/n]y
Write out database with 1 new entries
Data Base Updated

サーバーキーストアにCA証明書をインポート

$ keytool -import -alias ca -trustcacerts -file ./ca/cacert.pem -keystore ../../.keystore
キーストアのパスワードを入力してください:  changeit
所有者: CN=localhost, OU=bar, O=foo, L=Minato-ku, ST=Tokyo, C=JP
発行者: CN=localhost, OU=bar, O=foo, L=Minato-ku, ST=Tokyo, C=JP
シリアル番号: e3e64d2b0c205ae2
有効期間の開始日: Wed Oct 28 02:28:40 JST 2015終了日: Fri Nov 27 02:28:40 JST 2015
証明書のフィンガプリント:
         MD5:  5C:16:AC:D6:25:3D:59:23:1B:C1:CF:F1:51:A1:6A:4B
         SHA1: 4B:C2:E7:3D:21:71:6E:C7:29:5A:34:67:9A:0C:E1:30:E8:B2:C2:19
         SHA256: DA:4B:2C:07:86:D3:93:2C:A9:74:28:54:D5:5D:77:83:3F:85:D9:01:8D:34:98:99:BD:BF:51:AC:84:59:10:91
         署名アルゴリズム名: SHA256withRSA
         バージョン: 1
この証明書を信頼しますか。 [いいえ]:  y
証明書がキーストアに追加されました

サーバーキーストアにサーバー証明書をインポート

$ keytool -import -alias tomcat -trustcacerts -file ./tomcat.crt -keystore ../../.keystore
キーストアのパスワードを入力してください:  changeit
証明書応答がキーストアにインストールされました

Tomcat 設定

server.xml

redirectPort を削除。

    <Connector connectionTimeout="20000" port="8080" protocol="HTTP/1.1"/>

追加。

    <Connector port="8443" protocol="org.apache.coyote.http11.Http11Protocol"
               maxThreads="150" SSLEnabled="true" scheme="https" secure="true"
               clientAuth="true" sslProtocol="TLS" truststoreFile= "C:/Users/mikoto/.keystore"
               truststorePass="changeit"/>

web.xml

web-app タグの末尾に追加。

<login-config>
    <auth-method>CLIENT-CERT</auth-method>
</login-config>

wget してみる

$ # クライアント証明書による認証で失敗する
$ wget https://localhost:8443/
--2015-10-28 03:05:42--  https://localhost:8443/
localhost (localhost) をDNSに問いあわせています... ::1, 127.0.0.1
localhost (localhost)|::1|:8443 に接続しています... 接続しました。
OpenSSL: error:14094412:SSL routines:ssl3_read_bytes:sslv3 alert bad certificate
SSL による接続が確立できません。
$
$
$ # クライアント証明書を指定して接続しに行く。
$ # サーバ証明書が信用できない証明書なので失敗する
$ wget https://localhost:8443/ --private-key=client/clientkey.pem --certificate=client/client.crt
--2015-10-28 03:07:46--  https://localhost:8443/
localhost (localhost) をDNSに問いあわせています... ::1, 127.0.0.1
localhost (localhost)|::1|:8443 に接続しています... 接続しました。
エラー: localhost の証明書(発行者: `CN=localhost,OU=bar,O=foo,L=Minato-ku,ST=Tokyo,C=JP')の検証に失敗しました:
  自己署名証明書です。
localhost に安全の確認をしないで接続するには、`--no-check-certificate' を使ってください。
$
$
$ # クライアント証明書とCA証明書を指定して接続しに行く
$ # サーバ証明書がCAに信用されているので接続できる
$ wget https://localhost:8443/ --private-key=client/clientkey.pem --certificate=client/client.crt --ca-certificate=ca/cacert.pem
--2015-10-28 03:09:41--  https://localhost:8443/
localhost (localhost) をDNSに問いあわせています... ::1, 127.0.0.1
localhost (localhost)|::1|:8443 に接続しています... 接続しました。
HTTP による接続要求を送信しました、応答を待っています... 404 Not Found
2015-10-28 03:09:41 エラー 404: Not Found。

2015/08/30

[作業記録]curl を使って Web API の挙動を確かめる

Lingr の Web API の挙動を調べるために curl を使用したのでメモ。

cURL - How To Use (マニュアルページ日本語訳)

$ curl http://lingr.com/api/session/create -d user=mikoto2000@gmail.com -d password=MY_PASSWORD
{"status":"ok","session":"sVe3d6","public_id":"9a5w2p","nickname":"mikoto2000","user":{"username":"mikoto2000","name":"mikoto2000"}}

$ curl http://lingr.com/api/user/get_rooms -d session=5adYDm
{"status":"ok","rooms":["golang","vim"]}

$ curl http://lingr.com/api/room/show -d session=5adYDm -d rooms=vim
...(snip)

2015/08/29

[Git]fork したリポジトリに fork 元の変更を取り込む

この話題。 リモートを追加して、 fetch, merge --ff でいけるようだ。

git remote add upstream [fork元リポジトリ]
git fetch upstream
git merge upstream/master --ff

2015/08/24

[Embulk]embulk-input-salesforce_bulk の next config を試す

昨日の下記記事の続き。 next config がうまくいくかどうか確認していなかったのでそのあたりを確認。

親知らずの日記: [Embulk] Embulk で Salesforce から PostgreSQL にデータを転送してみた

設定変更

startRowMarkerName を追加。 更新するたびに増えていく値であれば何でもよい。 とはいえ一番簡単なのは最終更新日であろう。

in:
  type: salesforce_bulk
  userName: USERNAME
  password: PASSWORD+TOKEN
  authEndpointUrl: https://login.salesforce.com/services/Soap/u/34.0
  objectType: Account
  pollingIntervalMillisecond: 2500
  querySelectFrom: SELECT Id,Name,LastModifiedDate FROM Account
  columns:
  - {type: string, name: Id}
  - {type: string, name: Name}
  - {type: timestamp, name: LastModifiedDate, format: '%FT%T.%L%Z'}
  startRowMarkerName: LastModifiedDate
out: {type: postgresql, host: 192.168.11.207, user: from_sfdc, password: testing,
  database: from_sfdc, table: account_t, mode: merge}

初回実行

$ ./bin/embulk run -L ../embulk-input-salesforce_bulk/ ./sfdc_postgresql.yml -o ./sfdc_postgresql.yml
2015-08-24 22:39:52.733 +0900: Embulk v0.7.3
2015-08-24 22:39:54.269 +0900 [INFO] (transaction): Loaded plugin embulk-input-salesforce_bulk (0.1.0)
2015-08-24 22:39:54.309 +0900 [INFO] (transaction): Loaded plugin embulk-output-postgresql (0.4.1)
2015-08-24 22:39:54.372 +0900 [INFO] (transaction): Connecting to jdbc:postgresql://192.168.11.207:5432/from_sfdc options {user=from_sfdc, tcpKeepAlive=true, loginTimeout=300, socketTimeout=1800}
2015-08-24 22:39:54.425 +0900 [INFO] (transaction): SQL: SET search_path TO "public"
2015-08-24 22:39:54.427 +0900 [INFO] (transaction): > 0.00 seconds
2015-08-24 22:39:54.427 +0900 [INFO] (transaction): Using merge mode
2015-08-24 22:39:54.446 +0900 [INFO] (transaction): SQL: DROP TABLE IF EXISTS "account_t_aa0b9f76c0_bl_tmp000"
2015-08-24 22:39:54.447 +0900 [INFO] (transaction): > 0.00 seconds
2015-08-24 22:39:54.455 +0900 [INFO] (transaction): SQL: CREATE TABLE IF NOT EXISTS "account_t_aa0b9f76c0_bl_tmp000" ("Id" VARCHAR(20), "Name" VARCHAR(100), "LastModifiedDate" TIMESTAMP)
2015-08-24 22:39:54.457 +0900 [INFO] (transaction): > 0.00 seconds
2015-08-24 22:39:54.468 +0900 [INFO] (transaction): Using merge keys: [Id]
2015-08-24 22:39:54.488 +0900 [INFO] (transaction): {done:  0 / 1, running: 0}
2015-08-24 22:39:54.511 +0900 [INFO] (task-0000): Connecting to jdbc:postgresql://192.168.11.207:5432/from_sfdc options {user=from_sfdc, tcpKeepAlive=true, loginTimeout=300, socketTimeout=1800}
2015-08-24 22:39:54.521 +0900 [INFO] (task-0000): SQL: SET search_path TO "public"
2015-08-24 22:39:54.522 +0900 [INFO] (task-0000): > 0.00 seconds
2015-08-24 22:39:54.523 +0900 [INFO] (task-0000): Copy SQL: COPY "account_t_aa0b9f76c0_bl_tmp000" ("Id", "Name", "LastModifiedDate") FROM STDIN
2015-08-24 22:39:54.525 +0900 [WARN] (task-0000): An output plugin is compiled with old Embulk plugin API. Please update the plugin version using "embulk gem install" command, or contact a developer of the plugin to upgrade the plugin code using "embulk migrate" command: class org.embulk.output.jdbc.AbstractJdbcOutputPlugin$PluginPageOutput
2015-08-24 22:39:54.548 +0900 [INFO] (task-0000): Try login to 'https://login.salesforce.com/services/Soap/u/34.0'.
2015-08-24 22:39:55.016 +0900 [INFO] (task-0000): Login success.
2015-08-24 22:39:55.017 +0900 [INFO] (task-0000): Send request : 'SELECT Id,Name,LastModifiedDate FROM Account'
2015-08-24 22:39:58.396 +0900 [INFO] (task-0000): Loading 30 rows (1,903 bytes)
2015-08-24 22:39:58.399 +0900 [INFO] (task-0000): > 0.00 seconds (loaded 30 rows in total)
2015-08-24 22:39:58.524 +0900 [INFO] (transaction): {done:  1 / 1, running: 0}
2015-08-24 22:39:58.525 +0900 [INFO] (transaction): Connecting to jdbc:postgresql://192.168.11.207:5432/from_sfdc options {user=from_sfdc, tcpKeepAlive=true, loginTimeout=300, socketTimeout=28800}
2015-08-24 22:39:58.719 +0900 [INFO] (transaction): SQL: SET search_path TO "public"
2015-08-24 22:39:58.720 +0900 [INFO] (transaction): > 0.00 seconds
2015-08-24 22:39:58.720 +0900 [INFO] (transaction): SQL: WITH updated AS (UPDATE "account_t" SET "Id" = S."Id", "Name" = S."Name", "LastModifiedDate" = S."LastModifiedDate" FROM (SELECT "Id", "Name", "LastModifiedDate" FROM "account_t_aa0b9f76c0_bl_tmp000") S WHERE "account_t"."Id" = S."Id" RETURNING S."Id") INSERT INTO "account_t" ("Id", "Name", "LastModifiedDate") SELECT DISTINCT ON ("Id") * FROM (SELECT "Id", "Name", "LastModifiedDate" FROM "account_t_aa0b9f76c0_bl_tmp000") S WHERE NOT EXISTS (SELECT 1 FROM updated WHERE S."Id" = updated."Id")
2015-08-24 22:39:58.723 +0900 [INFO] (transaction): > 0.00 seconds
2015-08-24 22:39:58.729 +0900 [INFO] (transaction): Connecting to jdbc:postgresql://192.168.11.207:5432/from_sfdc options {user=from_sfdc, tcpKeepAlive=true, loginTimeout=300, socketTimeout=1800}
2015-08-24 22:39:58.735 +0900 [INFO] (transaction): SQL: SET search_path TO "public"
2015-08-24 22:39:58.736 +0900 [INFO] (transaction): > 0.00 seconds
2015-08-24 22:39:58.736 +0900 [INFO] (transaction): SQL: DROP TABLE IF EXISTS "account_t_aa0b9f76c0_bl_tmp000"
2015-08-24 22:39:58.738 +0900 [INFO] (transaction): > 0.00 seconds
2015-08-24 22:39:58.740 +0900 [INFO] (main): Committed.
2015-08-24 22:39:58.741 +0900 [INFO] (main): Next config diff: {"in":{"start_row_marker":"2015-08-24T13:31:58.000Z"},"out":{}}

ちゃんと 30 行ロードされたようだ。

2015-08-24 22:39:58.396 +0900 [INFO] (task-0000): Loading 30 rows (1,903 bytes)
2015-08-24 22:39:58.399 +0900 [INFO] (task-0000): > 0.00 seconds (loaded 30 rows in total)

そして更新された設定ファイルがこちら。

in:
  type: salesforce_bulk
  userName: USERNAME
  password: PASSWORD+TOKEN
  authEndpointUrl: https://login.salesforce.com/services/Soap/u/34.0
  objectType: Account
  pollingIntervalMillisecond: 2500
  querySelectFrom: SELECT Id,Name,LastModifiedDate FROM Account
  columns:
  - {type: string, name: Id}
  - {type: string, name: Name}
  - {type: timestamp, name: LastModifiedDate, format: '%FT%T.%L%Z'}
  startRowMarkerName: LastModifiedDate
  start_row_marker: '2015-08-24T13:31:58.000Z'
out: {type: postgresql, host: 192.168.11.207, user: from_sfdc, password: testing,
  database: from_sfdc, table: account_t, mode: merge}

2 回目実行

Salesforce 側を何も更新せずに実行。

$ ./bin/embulk run -L ../embulk-input-salesforce_bulk/ ./sfdc_postgresql.yml -o ./sfdc_postgresql.yml
2015-08-24 22:42:51.365 +0900: Embulk v0.7.3
2015-08-24 22:42:52.903 +0900 [INFO] (transaction): Loaded plugin embulk-input-salesforce_bulk (0.1.0)
2015-08-24 22:42:52.930 +0900 [INFO] (transaction): Loaded plugin embulk-output-postgresql (0.4.1)
2015-08-24 22:42:52.991 +0900 [INFO] (transaction): Connecting to jdbc:postgresql://192.168.11.207:5432/from_sfdc options {user=from_sfdc, tcpKeepAlive=true, loginTimeout=300, socketTimeout=1800}
2015-08-24 22:42:53.040 +0900 [INFO] (transaction): SQL: SET search_path TO "public"
2015-08-24 22:42:53.043 +0900 [INFO] (transaction): > 0.00 seconds
2015-08-24 22:42:53.043 +0900 [INFO] (transaction): Using merge mode
2015-08-24 22:42:53.072 +0900 [INFO] (transaction): SQL: DROP TABLE IF EXISTS "account_t_5c31b5d480_bl_tmp000"
2015-08-24 22:42:53.074 +0900 [INFO] (transaction): > 0.00 seconds
2015-08-24 22:42:53.081 +0900 [INFO] (transaction): SQL: CREATE TABLE IF NOT EXISTS "account_t_5c31b5d480_bl_tmp000" ("Id" VARCHAR(20), "Name" VARCHAR(100), "LastModifiedDate" TIMESTAMP)
2015-08-24 22:42:53.104 +0900 [INFO] (transaction): > 0.02 seconds
2015-08-24 22:42:53.121 +0900 [INFO] (transaction): Using merge keys: [Id]
2015-08-24 22:42:53.145 +0900 [INFO] (transaction): {done:  0 / 1, running: 0}
2015-08-24 22:42:53.170 +0900 [INFO] (task-0000): Connecting to jdbc:postgresql://192.168.11.207:5432/from_sfdc options {user=from_sfdc, tcpKeepAlive=true, loginTimeout=300, socketTimeout=1800}
2015-08-24 22:42:53.179 +0900 [INFO] (task-0000): SQL: SET search_path TO "public"
2015-08-24 22:42:53.180 +0900 [INFO] (task-0000): > 0.00 seconds
2015-08-24 22:42:53.181 +0900 [INFO] (task-0000): Copy SQL: COPY "account_t_5c31b5d480_bl_tmp000" ("Id", "Name", "LastModifiedDate") FROM STDIN
2015-08-24 22:42:53.183 +0900 [WARN] (task-0000): An output plugin is compiled with old Embulk plugin API. Please update the plugin version using "embulk gem install" command, or contact a developer of the plugin to upgrade the plugin code using "embulk migrate" command: class org.embulk.output.jdbc.AbstractJdbcOutputPlugin$PluginPageOutput
2015-08-24 22:42:53.203 +0900 [INFO] (task-0000): Try login to 'https://login.salesforce.com/services/Soap/u/34.0'.
2015-08-24 22:42:53.653 +0900 [INFO] (task-0000): Login success.
2015-08-24 22:42:53.654 +0900 [INFO] (task-0000): Send request : 'SELECT Id,Name,LastModifiedDate FROM Account WHERE LastModifiedDate > 2015-08-24T13:31:58.000Z'
2015-08-24 22:42:57.647 +0900 [INFO] (transaction): {done:  1 / 1, running: 0}
2015-08-24 22:42:57.648 +0900 [INFO] (transaction): Connecting to jdbc:postgresql://192.168.11.207:5432/from_sfdc options {user=from_sfdc, tcpKeepAlive=true, loginTimeout=300, socketTimeout=28800}
2015-08-24 22:42:57.651 +0900 [INFO] (transaction): SQL: SET search_path TO "public"
2015-08-24 22:42:57.652 +0900 [INFO] (transaction): > 0.00 seconds
2015-08-24 22:42:57.652 +0900 [INFO] (transaction): SQL: WITH updated AS (UPDATE "account_t" SET "Id" = S."Id", "Name" = S."Name", "LastModifiedDate" = S."LastModifiedDate" FROM (SELECT "Id", "Name", "LastModifiedDate" FROM "account_t_5c31b5d480_bl_tmp000") S WHERE "account_t"."Id" = S."Id" RETURNING S."Id") INSERT INTO "account_t" ("Id", "Name", "LastModifiedDate") SELECT DISTINCT ON ("Id") * FROM (SELECT "Id", "Name", "LastModifiedDate" FROM "account_t_5c31b5d480_bl_tmp000") S WHERE NOT EXISTS (SELECT 1 FROM updated WHERE S."Id" = updated."Id")
2015-08-24 22:42:57.654 +0900 [INFO] (transaction): > 0.00 seconds
2015-08-24 22:42:57.660 +0900 [INFO] (transaction): Connecting to jdbc:postgresql://192.168.11.207:5432/from_sfdc options {user=from_sfdc, tcpKeepAlive=true, loginTimeout=300, socketTimeout=1800}
2015-08-24 22:42:57.668 +0900 [INFO] (transaction): SQL: SET search_path TO "public"
2015-08-24 22:42:57.669 +0900 [INFO] (transaction): > 0.00 seconds
2015-08-24 22:42:57.669 +0900 [INFO] (transaction): SQL: DROP TABLE IF EXISTS "account_t_5c31b5d480_bl_tmp000"
2015-08-24 22:42:57.672 +0900 [INFO] (transaction): > 0.00 seconds
2015-08-24 22:42:57.675 +0900 [INFO] (main): Committed.
2015-08-24 22:42:57.676 +0900 [INFO] (main): Next config diff: {"in":{"start_row_marker":"2015-08-24T13:31:58.000Z"},"out":{}}

「Loading XXX rows」がない。何も読み込まれなかったようだ。

3 回目実行

取引先を一つ修正してから実行。

$ ./bin/embulk run -L ../embulk-input-salesforce_bulk/ ./sfdc_postgresql.yml -o ./sfdc_postgresql.yml
2015-08-24 22:45:26.724 +0900: Embulk v0.7.3
2015-08-24 22:45:28.289 +0900 [INFO] (transaction): Loaded plugin embulk-input-salesforce_bulk (0.1.0)
2015-08-24 22:45:28.321 +0900 [INFO] (transaction): Loaded plugin embulk-output-postgresql (0.4.1)
2015-08-24 22:45:28.392 +0900 [INFO] (transaction): Connecting to jdbc:postgresql://192.168.11.207:5432/from_sfdc options {user=from_sfdc, tcpKeepAlive=true, loginTimeout=300, socketTimeout=1800}
2015-08-24 22:45:28.448 +0900 [INFO] (transaction): SQL: SET search_path TO "public"
2015-08-24 22:45:28.450 +0900 [INFO] (transaction): > 0.00 seconds
2015-08-24 22:45:28.450 +0900 [INFO] (transaction): Using merge mode
2015-08-24 22:45:28.468 +0900 [INFO] (transaction): SQL: DROP TABLE IF EXISTS "account_t_f80c474f80_bl_tmp000"
2015-08-24 22:45:28.470 +0900 [INFO] (transaction): > 0.00 seconds
2015-08-24 22:45:28.476 +0900 [INFO] (transaction): SQL: CREATE TABLE IF NOT EXISTS "account_t_f80c474f80_bl_tmp000" ("Id" VARCHAR(20), "Name" VARCHAR(100), "LastModifiedDate" TIMESTAMP)
2015-08-24 22:45:28.477 +0900 [INFO] (transaction): > 0.00 seconds
2015-08-24 22:45:28.523 +0900 [INFO] (transaction): Using merge keys: [Id]
2015-08-24 22:45:28.542 +0900 [INFO] (transaction): {done:  0 / 1, running: 0}
2015-08-24 22:45:28.561 +0900 [INFO] (task-0000): Connecting to jdbc:postgresql://192.168.11.207:5432/from_sfdc options {user=from_sfdc, tcpKeepAlive=true, loginTimeout=300, socketTimeout=1800}
2015-08-24 22:45:28.569 +0900 [INFO] (task-0000): SQL: SET search_path TO "public"
2015-08-24 22:45:28.570 +0900 [INFO] (task-0000): > 0.00 seconds
2015-08-24 22:45:28.570 +0900 [INFO] (task-0000): Copy SQL: COPY "account_t_f80c474f80_bl_tmp000" ("Id", "Name", "LastModifiedDate") FROM STDIN
2015-08-24 22:45:28.572 +0900 [WARN] (task-0000): An output plugin is compiled with old Embulk plugin API. Please update the plugin version using "embulk gem install" command, or contact a developer of the plugin to upgrade the plugin code using "embulk migrate" command: class org.embulk.output.jdbc.AbstractJdbcOutputPlugin$PluginPageOutput
2015-08-24 22:45:28.587 +0900 [INFO] (task-0000): Try login to 'https://login.salesforce.com/services/Soap/u/34.0'.
2015-08-24 22:45:29.355 +0900 [INFO] (task-0000): Login success.
2015-08-24 22:45:29.355 +0900 [INFO] (task-0000): Send request : 'SELECT Id,Name,LastModifiedDate FROM Account WHERE LastModifiedDate > 2015-08-24T13:31:58.000Z'
2015-08-24 22:45:32.773 +0900 [INFO] (task-0000): Loading 1 rows (57 bytes)
2015-08-24 22:45:32.777 +0900 [INFO] (task-0000): > 0.00 seconds (loaded 1 rows in total)
2015-08-24 22:45:32.958 +0900 [INFO] (transaction): {done:  1 / 1, running: 0}
2015-08-24 22:45:32.959 +0900 [INFO] (transaction): Connecting to jdbc:postgresql://192.168.11.207:5432/from_sfdc options {user=from_sfdc, tcpKeepAlive=true, loginTimeout=300, socketTimeout=28800}
2015-08-24 22:45:32.964 +0900 [INFO] (transaction): SQL: SET search_path TO "public"
2015-08-24 22:45:32.965 +0900 [INFO] (transaction): > 0.00 seconds
2015-08-24 22:45:32.965 +0900 [INFO] (transaction): SQL: WITH updated AS (UPDATE "account_t" SET "Id" = S."Id", "Name" = S."Name", "LastModifiedDate" = S."LastModifiedDate" FROM (SELECT "Id", "Name", "LastModifiedDate" FROM "account_t_f80c474f80_bl_tmp000") S WHERE "account_t"."Id" = S."Id" RETURNING S."Id") INSERT INTO "account_t" ("Id", "Name", "LastModifiedDate") SELECT DISTINCT ON ("Id") * FROM (SELECT "Id", "Name", "LastModifiedDate" FROM "account_t_f80c474f80_bl_tmp000") S WHERE NOT EXISTS (SELECT 1 FROM updated WHERE S."Id" = updated."Id")
2015-08-24 22:45:32.967 +0900 [INFO] (transaction): > 0.00 seconds
2015-08-24 22:45:32.973 +0900 [INFO] (transaction): Connecting to jdbc:postgresql://192.168.11.207:5432/from_sfdc options {user=from_sfdc, tcpKeepAlive=true, loginTimeout=300, socketTimeout=1800}
2015-08-24 22:45:32.978 +0900 [INFO] (transaction): SQL: SET search_path TO "public"
2015-08-24 22:45:32.979 +0900 [INFO] (transaction): > 0.00 seconds
2015-08-24 22:45:32.980 +0900 [INFO] (transaction): SQL: DROP TABLE IF EXISTS "account_t_f80c474f80_bl_tmp000"
2015-08-24 22:45:32.993 +0900 [INFO] (transaction): > 0.01 seconds
2015-08-24 22:45:32.997 +0900 [INFO] (main): Committed.
2015-08-24 22:45:32.997 +0900 [INFO] (main): Next config diff: {"in":{"start_row_marker":"2015-08-24T13:45:28.000Z"},"out":{}}

うむ、良いのではないだろうか。

PostgreSQL のカラムが更新されていることも確認。embulk-input-postgresql の mode も merge で良いようだ。

2015/08/23

[Embulk] Embulk で Salesforce から PostgreSQL にデータを転送してみた

使用するプラグイン

salesforce_bulk は、自作プラグイン。設定方法などまだまだいろいろ変わりそうなので gem 化は考えていない。

Embulk に embulk-output-postgresql を入れる

$ ./bin/embulk gem install embulk-output-postgresql
2015-08-22 22:24:01.986 +0900: Embulk v0.7.2
Successfully installed embulk-output-postgresql-0.4.1
1 gem installed

PostgreSQL を準備する

VirtualBox に Debian minimal をインストール

...(略)

Debian に PostgreSQL 環境を構築

aptitude update
aptitude full-upgrade
aptitude install postgresql

su - postgres
psql

create database from_sfdc;
\c from_sfdc
create role from_sfdc with login;
alter user from_sfdc with password 'testing';

create table account_t("Id" varchar(20) primary key, "Name" varchar(100), "LastModifiedDate" timestamp);

exit
echo "listen_addresses = '*'" >> /etc/postgresql/9.4/main/postgres.conf
echo "host from_sfdc from_sfdc xxx.xxx.xxx.xxx/xx md5" >> /etc/postgresql/9.4/main/pg_hba.conf
service postgresql restart

Embulk 設定ファイルを作成

in:
  type: salesforce_bulk
  userName: USERNAME
  password: PASSWORD+TOKEN
  authEndpointUrl: https://login.salesforce.com/services/Soap/u/34.0
  objectType: Account
  pollingIntervalMillisecond: 2500
  querySelectFrom: SELECT Id,Name,LastModifiedDate FROM Account
  columns:
  - {type: string, name: Id}
  - {type: string, name: Name}
  - {type: timestamp, name: LastModifiedDate, format: '%FT%T.%L%Z'}
out:
  type: postgresql
  host: 192.168.11.207
  user: from_sfdc
  password: "testing"
  database: from_sfdc
  table: account_t
  mode: merge

実行

$ ./bin/embulk run -L ../embulk-input-salesforce_bulk/ ./sfdc_postgresql.yml
2015-08-23 03:08:56.728 +0900: Embulk v0.7.2
2015-08-23 03:08:58.254 +0900 [INFO] (transaction): Loaded plugin embulk-input-salesforce_bulk (0.1.0)
2015-08-23 03:08:58.289 +0900 [INFO] (transaction): Loaded plugin embulk-output-postgresql (0.4.1)
2015-08-23 03:08:58.356 +0900 [INFO] (transaction): Connecting to jdbc:postgresql://192.168.11.207:5432/from_sfdc options {user=from_sfdc, tcpKeepAlive=true, loginTimeout=300, socketTimeout=1800}
2015-08-23 03:08:58.406 +0900 [INFO] (transaction): SQL: SET search_path TO "public"
2015-08-23 03:08:58.408 +0900 [INFO] (transaction): > 0.00 seconds
2015-08-23 03:08:58.408 +0900 [INFO] (transaction): Using merge mode
2015-08-23 03:08:58.427 +0900 [INFO] (transaction): SQL: DROP TABLE IF EXISTS "account_t_ba0b162280_bl_tmp000"
2015-08-23 03:08:58.429 +0900 [INFO] (transaction): > 0.00 seconds
2015-08-23 03:08:58.435 +0900 [INFO] (transaction): SQL: CREATE TABLE IF NOT EXISTS "account_t_ba0b162280_bl_tmp000" ("Id" VARCHAR(20), "Name" VARCHAR(100), "LastModifiedDate" TIMESTAMP)
2015-08-23 03:08:58.436 +0900 [INFO] (transaction): > 0.00 seconds
2015-08-23 03:08:58.466 +0900 [INFO] (transaction): Using merge keys: [Id]
2015-08-23 03:08:58.482 +0900 [INFO] (transaction): {done:  0 / 1, running: 0}
2015-08-23 03:08:58.500 +0900 [INFO] (task-0000): Connecting to jdbc:postgresql://192.168.11.207:5432/from_sfdc options {user=from_sfdc, tcpKeepAlive=true, loginTimeout=300, socketTimeout=1800}
2015-08-23 03:08:58.506 +0900 [INFO] (task-0000): SQL: SET search_path TO "public"
2015-08-23 03:08:58.507 +0900 [INFO] (task-0000): > 0.00 seconds
2015-08-23 03:08:58.508 +0900 [INFO] (task-0000): Copy SQL: COPY "account_t_ba0b162280_bl_tmp000" ("Id", "Name", "LastModifiedDate") FROM STDIN
2015-08-23 03:08:58.509 +0900 [WARN] (task-0000): An output plugin is compiled with old Embulk plugin API. Please update the plugin version using "embulk gem install" command, or contact a developer of the plugin to upgrade the plugin code using "embulk migrate" command: class org.embulk.output.jdbc.AbstractJdbcOutputPlugin$PluginPageOutput
2015-08-23 03:08:58.524 +0900 [INFO] (task-0000): Try login to 'https://login.salesforce.com/services/Soap/u/34.0'.
2015-08-23 03:08:58.960 +0900 [INFO] (task-0000): Login success.
2015-08-23 03:08:58.960 +0900 [INFO] (task-0000): Send request : 'SELECT Id,Name,LastModifiedDate FROM Account'
2015-08-23 03:09:02.660 +0900 [INFO] (task-0000): Loading 30 rows (1,903 bytes)
2015-08-23 03:09:02.663 +0900 [INFO] (task-0000): > 0.00 seconds (loaded 30 rows in total)
2015-08-23 03:09:02.730 +0900 [INFO] (transaction): {done:  1 / 1, running: 0}
2015-08-23 03:09:02.731 +0900 [INFO] (transaction): Connecting to jdbc:postgresql://192.168.11.207:5432/from_sfdc options {user=from_sfdc, tcpKeepAlive=true, loginTimeout=300, socketTimeout=28800}
2015-08-23 03:09:02.735 +0900 [INFO] (transaction): SQL: SET search_path TO "public"
2015-08-23 03:09:02.736 +0900 [INFO] (transaction): > 0.00 seconds
2015-08-23 03:09:02.736 +0900 [INFO] (transaction): SQL: WITH updated AS (UPDATE "account_t" SET "Id" = S."Id", "Name" = S."Name", "LastModifiedDate" = S."LastModifiedDate" FROM (SELECT "Id", "Name", "LastModifiedDate" FROM "account_t_ba0b162280_bl_tmp000") S WHERE "account_t"."Id" = S."Id" RETURNING S."Id") INSERT INTO "account_t" ("Id", "Name", "LastModifiedDate") SELECT DISTINCT ON ("Id") * FROM (SELECT "Id", "Name", "LastModifiedDate" FROM "account_t_ba0b162280_bl_tmp000") S WHERE NOT EXISTS (SELECT 1 FROM updated WHERE S."Id" = updated."Id")
2015-08-23 03:09:02.739 +0900 [INFO] (transaction): > 0.00 seconds (30 rows)
2015-08-23 03:09:02.746 +0900 [INFO] (transaction): Connecting to jdbc:postgresql://192.168.11.207:5432/from_sfdc options {user=from_sfdc, tcpKeepAlive=true, loginTimeout=300, socketTimeout=1800}
2015-08-23 03:09:02.751 +0900 [INFO] (transaction): SQL: SET search_path TO "public"
2015-08-23 03:09:02.752 +0900 [INFO] (transaction): > 0.00 seconds
2015-08-23 03:09:02.752 +0900 [INFO] (transaction): SQL: DROP TABLE IF EXISTS "account_t_ba0b162280_bl_tmp000"
2015-08-23 03:09:02.755 +0900 [INFO] (transaction): > 0.00 seconds
2015-08-23 03:09:02.759 +0900 [INFO] (main): Committed.
2015-08-23 03:09:02.759 +0900 [INFO] (main): Next config diff: {"in":{},"out":{}}

無事、PostgreSQL に格納された。よかった。

こんな感じのこともありました。『中の処理がわからんとシンタックスエラーっていわれても...』ってなって割と苦戦してました。

2014/12/28

[Android][Arch] Xperia Z2 Tablet に chroot Arch Linux 環境を入れた

前提条件

  • Xperia Z2 Tablet の root 化済み
  • BusyBox インストール済み

自分が入れた BusyBox はこれ。BusyBox - Google Play の Android アプリ

やったこと

sd カードに arch 用のパーティションを作る

busybox の fdisk, mkfs を使用。詳細は略す。

arch 格納用のディレクトリ準備

# 読み書きできるように再マウント
mount -o rw,remount /

# マウントポイントを作成
mkdir /mnt/arch

# sd カードの arch 用パーティションをマウント
cd /mnt
mount -t ext2 /dev/block/mmcblk1p2 arch

arch のベースシステム(?)を配置

cd /mnt/arch

# ベースシステムが格納されたファイルをダウンロード
wget http://os.archlinuxarm.org/os/ArchLinuxARM-armv7-latest.tar.gz

# 解凍
tar zxfv ArchLinuxARM-armv7-latest.tar.gz

[2015/01/6更新]:armv7でなく、armv5だったので修正。

[2015/01/7更新]:やっぱりarmv7だったので修正。

chroot しても問題なく動くように設定を書き換える

# dns につながるように設定を書き換える
rm /mnt/arch/etc/resolv.conf
echo 'nameserver 8.8.8.8' > /mnt/arch/etc/resolv.conf

# chroot 環境から proc が見えるようにする
mount -t proc /proc /mnt/arch/proc

# /dev が必要なプログラムも多いので bind しておく
mount -o bind /dev /mnt/arch

arch 環境を整える

# 既存パッケージのアップグレード
pacman -Syu

# 必要なパッケージをインストール
pacman -S vim
pacman -S git
pacman -S ghc
pacman -S cabal-install
cabal update
cabal install cabal-install
cabal install pandoc
pacman -S graphviz
pacman -S go
pacman -S base-devel

pacman -S imagemagick
pacman -S otf-ipafont

とりあえず、 golang と imagemagick が動くことは確認できた。

2014/11/08

[Vim, Ubuntu]Ubuntu1204 での vim ビルドメモ

忘れないように。 Ubuntu1204 をインストールして、 aptitude で build-essential を入れたくらいのはず。

sudo aptitude install libncurses5-dev
sudo aptitude install liblua5.2-dev
sudo aptitude install liblua5.1-0-dev
sudo aptitude install luajit libluajit-5.1-dev

cd ~/project/vimsrc/

hg pull
hg update

./configure --enable-luainterp --with-luajit --prefix=/home/mikoto/app/vim/ --enable-fail-if-missing
make -j8
make install

2014/10/03

[Mikutter] mikutter の change_account プラグインの interactive 機能を使ってみる

mikutter で、クリック一発でイベント発行出来る方法無いかなぁと考えていたら、 mikutter-adventure を思い出したので、参照しながらボタンクリックでイベント発火の練習をしてみる。

こんな感じでメッセージ内に表示されたボタンに応じたイベントを発行できるようだ。

多分コマンドでも代用できるけど、いちいち右クリックして選択とかしたくない・させたくない場合に使えそう。 (多用してボタンが並ぶとちょうどよいタイミングで画面がスクロールしてひどいことになったりしそうですが...)

タスク管理プラグイン作って、完了ボタンを押すとみくったーちゃんが褒めてくれる感じのプラグインなんてどうだろうか?

参考文献

mikutter-adventure/mikutter-adventure.rb at master · moguno/mikutter-adventure

2014/09/27

[作業記録, Debian] tmux をビルドした

早速ダウンロード・ビルド

git clone git://git.code.sf.net/p/tmux/tmux-code tmux-tmux-code

./configure
-bash: ./configure: No such file or directory

あ、configure ない。 README 見よう。

...(抜粋)
    $ git clone git://git.code.sf.net/p/tmux/tmux-code tmux
    $ cd tmux
    $ sh autogen.sh
    $ ./configure && make

autogen が必要みたい。

sh autogen.sh
autogen.sh: 16: autogen.sh: aclocal: not found
aclocal failed

aclocal とな。 automake がいるのか。というか入れてなかったっけ?

sudo aptitude install automake
sh autogen.sh
./configure
checking for library containing event_init... !!!configure: error: "libevent not found"!!!

こんどは libevent だ。

sudo aptitude install libevent-dev
./configure --prefix=/home/mikoto/opt/tmux
make -j8
make install

うん、できたっぽい。作業記録なので動作確認は割愛。あと、ビルドのためのパッケージはほぼすべてインストール済みだったのでそれも省略。

2014/09/20

[Debian, Ruby]Debian(wheezy) に rbenv で ruby2.1.3 をインストール

これだけ。

# 必要パッケージ取得
sudo apt-get install autoconf bison build-essential libssl-dev libyaml-dev libreadline6-dev zlib1g-dev libncurses5-dev rbenv ruby-build

# rbenv 初期設定
rbenv init
echo eval "$(rbenv init -)" >> .bashrc

# 2.1.3 のパッケージ URL を指定したファイルを作成
vim 2.1.3
cat ./2.1.3
install_package "yaml-0.1.4" "http://pyyaml.org/download/libyaml/yaml-0.1.4.tar.gz"
install_package "ruby-2.1.3" "http://ftp.ruby-lang.org/pub/ruby/2.1/ruby-2.1.3.tar.gz"

# 2.1.3 インストール
rbenv install ./2.1.3

# ruby コマンドで 2.1.3 が呼び出されるようにする。
rbenv global 2.1.3

2014/09/19

[Mikutter] UserConfig に連想配列を格納して、設定画面から変更できるようにしたい

UserConfig に連想配列を格納して、設定画面から変更できるようにしたかったので挑戦してみた。

実現方針は下記。

  • 設定画面の設定値を連想配列に紐付ける
  • 連想配列に変更があったら UserConfig を更新する

設定画面の設定値を連想配列に紐付ける

Settings の各関数にリスナを渡せるので、リスナ内で連想配列に出し入れするよう頑張ってもらう。

select は、 3.0.5 の時点ではリスナを渡せない。 3.0.6 くらいでフィックスされるのではないだろうか。

連想配列に変更があったら UserConfig を更新する

UserConfig[key] で取得できる値はコピーなので、 格納した連想配列に変更があった場合には連想配列自体を入れ替える。

上述のリスナの set 処理の最後に、連想配列をごっそり入れ替える処理を入れる感じ。

コードと、その結果の設定は下記の通り。

設定項目がゴチャッとしてきたらそれなりにわかりよくなるのではないでしょうか。

書きながら思ったけど、何かやるたびに連想配列全体のコピーが走るんだな、これ。 性能が気になる場合は、更新・取得にさらなる工夫が必要ですね。

参考文献

2014/08/14

[Mikutter, 作業記録] mikutter を Windows8 にインストールした

Windows8 に mikutter をインストールしたい

まずは ggr.

MikutterInstallBattleForWindows - mikutter Wiki(http://yuzuki.hachune.net/wiki/MikutterInstallBattleForWindows)

ふむ。やるか。

ruby のインストール

wiki の指示通りに rubyinstaller-2.0/0-p481.exe をダウンロードしてインストールした。

mikutter のダウンロード

都合のよいことに、すでに git 導入済みだったので、リポジトリからクローンして とってきた。

vimshell% mkdir develop
vimshell% cd develop
vimshell% git clone git://toshia.dip.jp/mikutter.git
!!!Cloning into 'mikutter'...!!!
!!!Checking out files: 100% (672/672), done.!!!

windows 用パッチブラグインの展開

こちらも git で。

vimshell% cd ~/develop/mikutter/plugin
vimshell% git clone https://github.com/moguno/mikutter-windows.git mikutter-windows

セットアップスクリプトの実行

vimshell% cd ~/develop/mikutter/plugin/mikutter-windows/
vimshell% c:/Ruby200/bin/ruby.exe ./setup.rb

...何も起きなかった。さすがにだめか。普通にダブルクリックする。おぉ、bundler のフェッチとインストールがはじまった。

コマンドプロンプトが閉じないと思っていたら、ショートカットできてた。コマンドプロンプトは開きっぱなしなのかな? 反応なし。コマンドプロンプトで「ruby mikutter」やってみたら gem が足りないって怒られた。やはりあれは途中だったのか...。しまった。 setup.rb をやり直す。

今度はちゃんと complete って表示されるまで待ったよ。

そのあと、「ruby mikutter.rb」でできました。

デスクトップにできたショートカットでも問題なし。

2014/08/12

[Mikutter] データソースを作ってみたい!

データソースの作成がしてみたいので、ホームタイムラインと同じ物を表示するだけのデータソースを定義してみる。

そもそもデータソースって何?

「抽出タブ」で選択できるデータの取得元。抽出タブの設定でチェックを入れると、そのデータソースのメッセージがタブに表示されるようになる。

「抽出タブ」を全く使っていなかったので、自分にとって「得体のしれない何か」であったが、今回の勉強でこんなもんかと解った気がしますわ。

「抽出タブで対応できないような特定のツイートを抽出する」、「外から情報撮ってきて表示する」ような機能はデータソース使うのがスマートっぽいですね。

じゃあ作ってみる

はい、やっていきましょう。

前準備

勉強用プラグイン作成。

cd ~/.mikutter/plugin/
mikutter.rb generate study_datasource
mikutter.rb spec study_datasource

コーディング

データソースを作成するにあたり、最低限必要な作業は下記の通り。

  1. 提供するデータソースの宣言(登録)
  2. 1 で宣言したデータソースにメッセージを追加する

これだけ。以下、今回作った具体的なコード。

.mikutter.yml

---
slug: :study_datasource
depends:
  mikutter: 3.0.4
  plugin: []
version: '1.0'
author: 
name: study_datasource
description: データソースの勉強するよー

study_datasource.rb

# -*- coding: utf-8 -*-
# mikutter datasource の勉強。
# ホームタイムラインのクローンデータソースを作成する。
#
# データソース作成手順としては、
#     1. filter_extract_datasources で、「こんなデータソースを作るよー」と宣言
#     2. データソースにメッセージを追加したいタイミングで
#        「:extract_receive_message」を呼ぶ。
#         その際、引数に「データソースに紐付くシンボル」を渡す。
# という感じ良いみたい。
#
# 既存タイムラインからの抽出でない場合は、
#     1. 自前でデータ取得ループ作成
#     2. 自前でメッセージ作成
#     3. 「:extract_receive_message」を呼ぶ
# となる感じですね。

Plugin.create(:study_datasource) do
    DATASOURCE_NAME = 'clone_home_timeline'

    # このプラグインが提供するデータソースの定義を組み立てて返却
    def datasources
        ds = {clone_home_timeline: "ホームタイムラインのクローン".freeze}
        ds[DATASOURCE_NAME.to_sym] = DATASOURCE_NAME
        ds
    end

    # 提供するデータソースの登録
    # 登録は、このプラグインで定義したデータソースを
    # 引数で渡ってきたデータソースにマージすることで行う。
    filter_extract_datasources { |ds|
        p "Add My Datasources"
        [ds.merge(datasources)]
    }

    # データソースにメッセージを追加
    # on_update で受け取ったメッセージ全てをデータソースに突っ込んでいる。
    on_update do |service, messages|
        messages.each do |message|
            p "Add message to #{DATASOURCE_NAME}"
            Plugin.call(:extract_receive_message, DATASOURCE_NAME.to_sym, [message])
        end
    end
end

参考文献

2014/07/30

[Mikutter] イベント定義を試した

イベントを定義したい

リストに所属している人のツイートがあった場合に発火するイベントが欲しくなったので、勉強がてら作って見る。

イベント定義の手順

  1. defevent で下記項目を定義
    • イベント名
    • 優先度
    • 引数の型
  2. Plugin.call(イベント名, 引数) でイベント発火

これだけ。

確認用コード

.mikutter.yml

---
slug: :studyEvent
depends:
  mikutter: 3.0.3
  plugin: []
version: '1.0'
author: 
name: studyEvent
description: イベントの勉強。

studyEvent.rb

# -*- coding: utf-8 -*-

Plugin.create(:studyEvent) do
    defevent :list_tweet_receive,  priority: :routine_passive, prototype: [UserList, Message]

    onboot do |service|
        Open3.capture3("echo 'start' > ~/tmp/ttt.txt")
    end

    # リストのツイート受信毎に list_tweet_receive イベントを発行する
    on_appear do |messages|
        messages.each{ |message|
            Plugin[:list].timelines.each{ |slug, list|
                if list.related?(message)
                    Plugin.call(:list_tweet_receive, list, message)
                end
            }
        }
    end

    # イベント発火をトリガに処理を行う
    on_list_tweet_receive do |list, message|
        Open3.capture3("echo '#{list.user}/#{list[:name]}' >> ~/tmp/ttt.txt")
        if list.user === 'mikoto2000' && list[:name] === 'News' then
            Open3.capture3("echo '#{message}' >> ~/tmp/ttt.txt")
        end
    end
end

イベント発火の部分だけ抜き出すと以下な感じか?

# -*- coding: utf-8 -*-

Plugin.create(:studyEvent) do
    defevent :list_tweet_receive,  priority: :routine_passive, prototype: [UserList, Message]

    # リストのツイート受信毎に list_tweet_receive イベントを発行する
    on_appear do |messages|
        messages.each{ |message|
            Plugin[:list].timelines.each{ |slug, list|
                if list.related?(message)
                    Plugin.call(:list_tweet_receive, list, message)
                end
            }
        }
    end
end

「リストイベントを追加するプラグイン」として単体で提供して、 このイベントが欲しかったら依存関係に入れろみたいな感じでどうか?

依存関係に入れなくても、プラグインが入っている時だけリスト系の機能が使えます的な使い方できるか?

-> 公開した。

mikoto2000/mikutter-list-event : https://github.com/mikoto2000/mikutter-list-event

2014/07/21

[Ruby, Mikutter] mikutter プラグイン「ゆかりが読む」を作った

mikutter プラグイン「ゆかりが読む」を作った

mikoto2000/mikutter-yukari : https://github.com/mikoto2000/mikutter-yukari

requirements 厳しすぎるけどまぁそれはそれ。

とりあえず自分の環境で動いたので公開。 ツイートを、音声配信サーバに投げて音声化し、再生します。

音声配信サーバとしては「VOICEROID+ 結月ゆかり」がインストールされた Windows7 端末を想定。

TODO

  • 先頭の twitter id とか末尾のハッシュタグとかは、読み上げ的には必要ないので無視するのがよいかなぁ。
  • ニュースツイート(nhk_news とか) のリンクは、リンク先をスクレイピングして読みあげたい。

mikutter plugin tips

本プラグインを作るにあたって、設定のデフォルト値の指定方法について調べたのでメモ。

プラグインのデフォルト値

プラグインのデフォルト設定値を作りたい場合、「on_boot」内で空チェックして、空ならデフォルト値設定とするのが良いようだ。

参考資料

VLC の曲名を nowplaying できる mikutter プラグイン書いた - polamjaggy : http://polamjag.hatenablog.jp/entry/2013/11/18/035402

2014/07/19

[Ruby, Mikutter] Mikutter プラグインを作ってみる。

はじめに

プラグインの書き方解説ページの「5 STOT 形式でコピー」を写経。

参考資料

Writing mikutter plugin : http://toshia.github.io/writing-mikutter-plugin/#sec-5

$ cd ~/.mikutter/plugin
$ mikutter.rb generate stot
$ mikutter.rb spec stot/
$ cd stot
$ vim stot.rb

...で、こんな感じ。

$ cat .mikutter.yml
---
slug: :stot
depends:
  mikutter: 3.0.3
  plugin: []
version: '1.0'
author: 
name: stot
description: Copy to clipboard with STOT format.
$ cat stot.rb
# -*- coding: utf-8 -*-

Plugin.create(:stot) do
  command(:copy_as_stot,
    name: 'STOT形式でコピー',
    condition: Plugin::Command[:HasOneMessage],
    visible: true,
    role: :timeline) do |opt|
      message = opt.messages.first
      screen_name = message.user[:idname]
      Gtk::Clipboard.copy("#{screen_name}: #{message.to_s} [https://twitter.com/#{screen_name}/status/#{message.id}]")
  end
end

つぎに

ツイートの本文を外部プログラムに渡すプラグインを作ってみる。

探せば有りそうと考えてはいけない。

参考資料

Rubyから外部プログラムを起動 - None is None is None : http://doloopwhile.hatenablog.com/entry/2014/02/04/213641

$ cd ~/.mikutter/plugin
$ mikutter.rb generate runWith
$ mikutter.rb spec runWith/
$ cd runWith
$ vim runWith.rb

...で、こうなる。

$ cat .mikutter.yml 
---
slug: :runWith
depends:
  mikutter: 3.0.3
  plugin: []
version: '1.0'
author: 
name: runWith
description: ツイートの内容を外部プログラムに、実行する。
$ cat runWith.rb 
# -*- coding: utf-8 -*-

require "open3"

Plugin.create(:runWith) do
  command(:run_with,
    name: 'echo to ~/tmp/echo.txt',
    condition: Plugin::Command[:HasOneMessage],
    visible: true,
    role: :timeline) do |opt|
      message = opt.messages.first
      Open3.capture3("echo '#{message.to_s}' > ~/tmp/echo.txt")
    end
end

とりあえず echo で試した。 あとはコマンドと引数を変更すればよいだけ。 外部プログラムが呼べてしまえば、もう何でも出来ますね!

2014/05/11

[作業記録][Debian]ALminium を導入しようとしてみた


下準備

仮想マシン再作成

# ホストPCで
sudo virsh destroy alminium
sudo virsh undefine alminium
sudo virsh vol-delete --pool default alminium.img
sudo rm /var/lib/libvirt/images/alminium.img
sudo virt-clone -o debian_org -n alminium --file /var/lib/libvirt/images/alminium.img
sudo virsh setmaxmem alminium 2048000
sudo virsh start alminium
sudo virsh setmem alminium 2048000
# 下記確認・設定を行う
# ホスト名修正
# IP アドレス確認
sudo virsh console alminium

ベースは ssh, git 導入済みなので ssh で接続。 次に、 Jenkins のインストールと、ベース URL の設定を行う。

Jenkins のインストール・設定

# ゲストPCで
sudo aptitude update && sudo aptitude -y dist-upgrade
# Debian では Jenkins インストールしてくれなかったのでインストールする
wget -q -O - http://pkg.jenkins-ci.org/debian/jenkins-ci.org.key | sudo apt-key add -
echo 'deb http://pkg.jenkins-ci.org/debian binary/' | sudo tee -a /etc/apt/sources.list
sudo aptitude update && sudo aptitude install jenkins
sudo service jenkins restart

ベース URL の設定。

/etc/default/jenkins の JENKINS_ARGS の末尾に "--prefix=$PREFIX" を追加する。

本番

ALMinium のインストール

cd ~/work
git clone https://github.com/alminium/alminium.git
cd alminium
sudo bash ./smelt
Debian / Ubuntu が検出されました。
インストールに失敗するかも知れませんが、お許しくださいm(__)m

途中MySQLのパスワードを聞かれたら空のままエンターキーを押してください。
MySQLのパスワードを設定するとインストールに失敗します。
パスワードを設定したい場合は、ALMiniumのインストール完了後行ってください
*******************************************************
  ホスト名の設定
*******************************************************
ホスト名(IPアドレスもしくはDNS名)を入力してください。ホスト名はApacheのバーチャルホストで利用されます。
例えば、192.168.1.4をホスト名で入力すると、http://192.168.1.4/でアクセスすることになります。
(上記の設定ではhttp://localhost/では接続できないのでご注意ください)
ホスト名: 192.168.11.209


*******************************************************
  SSLのサポート
*******************************************************
SSLのサポートを有効にすると、httpsのみの接続を許可します。
httpでのアクセスは、全てhttpsのポートへ転送されるようになります。
SSLの証明書は認証機関により署名されたものではありません。通信の暗号化のみ
に利用します。
gitの利用では、

  $ git config --global http.sslVerify false

などのコマンドで、SSLの証明書を無効にする必要があります。

SSL(https)サポートを有効にしますか?(y/N) N


*******************************************************
  メール設定
*******************************************************
Redmineのシステムメールを送信するための設定を行います。

<利用する上での注意>
・利用したいメールシステムと通信可能なことをご確認ください。ファイアーウォールなどで遮断されている場合は管理者にお問い合わせください。
・Sendmailコマンドを利用する場合、別途Sendmailの設定を行う必要があります。
・G-Mail/Hotmailを利用する場合、ユーザー名とパスワードがあれば利用可能です。

次の中から、使いたいメールシステムを選択してください。
ここでメール設定を行わない場合は「N」を選択してください。

0. 独自のメールサーバーを設定する
1. Linux内のSendmailコマンドを使用する
2. G-Mail/Google Apps
3. Windows Live Hotmail

N. メール設定をしない

番号選択: N


メール設定を行いません。


継続的インテグレーションツールのJenkinsのインストール・設定を行うことができます。デフォルトはインストールしません。よく分からなければNを選択してください。Jenkinsをインストールしますか?[y/N]y

※ ここで、MySQL のルートパスワード入力になるが、空ですすめる。

... (snip)

Enabling site jenkins.
To activate the new configuration, you need to run:
  service apache2 reload
mkdir: cannot create directory `/var/lib/jenkins/plugins': File exists
[ ok ] Restarting Jenkins Continuous Integration Server: jenkins.
--2014-05-10 10:20:28--  http://localhost:8080/jenkins/jnlpJars/jenkins-cli.jar
Resolving localhost (localhost)... ::1, 127.0.0.1
Connecting to localhost (localhost)|::1|:8080... connected.
HTTP request sent, awaiting response... 503 Service Unavailable
2014-05-10 10:20:32 ERROR 503: Service Unavailable.
...(snip)

こんな感じのエラーで無限ループになるので、 Jenkins の設定を頑張る。

cd /var/lib/jenkins
mv config.xml _config.xml
sudo service jenkins restart

先には進むが、いろいろ残念なことになるので解消する。

[....] Starting web server: apache2apache2: Syntax error on line 268 of /etc/apache2/apache2.conf: Syntax error on line 1 of /etc/apache2/sites-enabled/redmine: Cannot load /usr/lib/ruby/gems/1.9.1/gems/passenger-4.0.42/buildout/apache2/mod_passenger.so into server: /usr/lib/ruby/gems/1.9.1/gems/passenger-4.0.42/buildout/apache2/mod_passenger.so: cannot open shared object file: No such file or directory
Action 'start' failed.
The Apache error log may have more information.
 failed!

Jenkins から。

cd /var/lib/jenkins
mv _config.xml config.xml
sudo service jenkins restart

その後、 Jenkins にアクセスし、アップデート等行う。

次、 apache2。これは、 mod_passenger が無いと言っているようなので apt でインストールする。

sudo aptitude install libapache2-mod-passenger
sudo service apache2 restart

apache2 のページを開くと、 gem 'thin (>=0) ruby' が無いって怒られた。 thin をインストールする。

sudo gem install thin rubu

...まだたりないらしい。

gem パッケージが足りないとすると、Gemfile.local あたり探せば良いかな。あった。

cd /opt/alminium
bundle update
...(snip)

いろいろインストールされた。 ALMinium のホーム画面は表示されたけれど、エラーだか警告だかのメッセージが表示されている。

バックログが設定されていません。設定画面 管理 > プラグイン, からリンク 設定 をクリックして設定してください。フィールドを設定後、このページへ戻りツールを利用してください。

ログを見てみる。 (/opt/alminium/log/production.log)

~ { .log } DEPRECATION WARNING: Passing a template handler in the template name is deprecated. You can simply remove the handler name or pass render :handlers => [:erb] instead. (called from realtime at /usr/lib/ruby/1.9.1/benchmark.rb:295) }

バックログの設定の警告とは関係ないログっぽい。 /opt/alminium/plugins/redmine_backlogs/app/controllers/rb_server_variables_controller.rb を修正。

---    format.js { render :file => 'rb_server_variables/show.js.erb', :layout => false }
+++    format.js { render :file => 'rb_server_variables/show', :layout => false }

バックログの DB マイグレーションがうまくいっていなかったらしい。

Support - "Plugin migrations have not been executed" after successfull installation

cd /opt/alminium
sudo rake redmine:plugins:migrate RAILS_ENV=production

とりあえずホーム画面は映った。

残課題

  • Jenkins の認証がおかしい。
    • ログインユーザが「(null)」になっている。
    • 「Jenkinsの管理」画面に「リバースプロキシーの設定がおかしいようです。」と表示される。

あと、インストールスクリプトちょっと見る限り、 Jenkins はリポジトリの登録さえしておけば apt-get でインストールはしてくれそう。その後の設定がどうなるかは読んでないけれど。

apache2, redmine, jenkins あたりの認証共通化の仕組みを勉強しないことにはこれ以上ダメそうね。どうしたものか。

2014/04/14

[普通の日記]今日勉強した雑多なもの

css でマウスオーバー時にカーソルの形を変える

/* マウスオーバー時にマウスカーソルをpointer(指)にする */
cursor: pointer;

DOM でテキストノードを追加する

var textNode = document.createTextNode("TEXT");
element.appendChild(textNode);

css セレクタで、要素直下の要素だけを指定する

PARENT_SELECTOR > CHILD_SELECTOR

Dojo Toolkit でにょきっと出たり入ったりする show/hide 処理

Reference の Example 3 あたりを見ると出来るよ!
dojo/fx/Toggler — The Dojo Toolkit - Reference Guide

dojo/_base/connect は古いらしい、 dojo/on とか dojo/aspect とか使うらしい

dojo.connect() — The Dojo Toolkit - Reference Guide

2013/12/22

[Debian]Ricty のインストール

Ricty のインストール

# 合成元のフォントパッケージをインストール
sudo aptitude install fontforge ttf-inconsolata fonts-migmix

# 作業用ディレクトリの作成と移動
mkdir -p ~/tmp/ricty
cd ~/tmp/ricty/

# 合成スクリプト取得
wget https://raw.github.com/yascentur/Ricty/master/ricty_generator.sh

# 合成開始
sh ricty_generator.sh auto

# フォントインストール
mkdir ~/.fonts
cp -f Ricty-\*.ttf ~/.fonts/
fc-cache -vf

2013/11/30

[KVM]KVM + libvirt でいろいろやったよ

KVM + libvirt でいろいろやったよ

半年くらい前のメモを発掘したのでとりあえずブログに保管する。


# パッケージインストール
sudo aptitude install qemu-kvm libvirt-bin virtinst

# OS 再起動(/dev/kvm の権限を更新するため)

# USER が kvm を使えるようにする
# 多分再起動とか必要。ログアウトでいいのかな?
sudo adduser USER kvm

# 仮想マシン作成 & Debian インストール
# 最小構成 + vim + ssh
sudo virt-install --connect qemu:///system --name debian_org --ram 512 --disk path=/var/lib/libvirt/images/debian_org.img,size=5 --network network=default,model=virtio --nographics --location='http://ftp.jp.debian.org/debian/dists/wheezy/main/installer-amd64/' -x console=ttyS0,115200

仮想マシン設定ファイルの場所

/etc/libvirt/qemu/DOMAIN_NAME.xml

イメージファイル等の場所

/var/lib/libvirt/*

仮想マシン以外の設定も、 /etc/libvirt の中にある。 autostart 内のもの(普通は設定ファイルへのシンボリックリンク)は libvirtd 起動時に実行される。

基本操作

sudo virt-install --connect qemu:///system --name DOMAIN_NAME --ram RAM_SIZE --disk /PATH/TO/DISK,size=GB --network network=default,model=virtio --vnc --cdrom /PATH/TO/CDROM
sudo virsh list --all
sudo virsh dominfo DOMAIN_NAME
sudo virsh start DOMAIN_NAME
sudo virsh suspend DOMAIN_NAME
sudo virsh resume DOMAIN_NAME
sudo virsh reboot DOMAIN_NAME #KVMでは使えないらしい
sudo virsh shutdown DOMAIN_NAME
sudo virsh destroy DOMAIN_NAME
sudo virsh undefine DOMAIN_NAME

ストレージ

libvirt にはストレージプールなるものがあるらしい。 まだ使ってないのでいつか調べる。

sudo virsh pool-list
sudo virsh vol-list POOL_NAME
sudo virsh vol-delete VOL_NAME

qemu-img create -f qcow2 IMG_NAME IMG_SIZE

Windows7 のインストール

virtio ドライバ

WindowsGuestDrivers/Download Drivers - KVM(http://www.linux-kvm.org/page/WindowsGuestDrivers/Download_Drivers)

VM 作成

sudo virt-install --connect qemu:///system --arch=x86_64 -n voice -r 24000 --vcpus 4 --vnc --vnclisten 0.0.0.0 --noautoconsole --os-type windows --os-variant win7 --disk path=/var/lib/libvirt/images/voice.img,size=10 --disk path=/home/mikoto/iso/virtio-win-0.1-59.iso,device=cdrom,perms=ro --cdrom /dev/cdrom --boot cdrom,hd --prompt

これで、サーバの 5900 ポートに VNC クライアントでアクセスすれば画面が表示される。 (windows7 の Real VNC では、 64bit color よりも上の色だと接続できなかった...)

インストール途中で、追加のドライバ(virtio)をインストールする。

これだと NIC も HDD も virtio として追加されないようだ。 disk のオプションに "bus=virtio" を追加するとか、 ネットワークオプションとして "--network bridge=br0,model=virtio" を追加するとかが必要なようだ。 いつか再インストールの機会があれば試す。

ボリュームのリサイズ

10GB は小さすぎた...。

# ボリュームの情報確認
sudo virsh vol-info --pool default voice.img
# リサイズ
# virsh vol-resize は未サポートらしい(2013/5/25,Debian7,KVM)
sudo qemu-img resize voice.img +20G

その後、コンピュータの管理からパーティションを拡張すれば OK.