Hatena::ブログ(Diary)

podhmoの日記

 

2012-09-21

pushの権限を持っていないリモートリポジトリに更新内容を反映する

| 10:55

pushの権限を持っていない環境(ユーザ)で適用した更新を、リモートリポジトリに反映したい時がある。

例えば、本番環境でのhotfixなど。

今のところ以下のような手順で行っている。

(localとproductionという2つのホスト上で作業をしているとする。localが自分の環境。productionが本番環境)

大まかには

  1. productionでformat-patchでpatchの作成
  2. localでpatchを取り込んでリモートリポジトリにpush
  3. productionでリモートリポジトリの変更をpull

実際の内容

production
git add . #変更をaddして
git commit -m "this-is-hot-fix" #commit
git format-patch HEAD~ ## 0001-*.patchが作成される
local
scp production:/srv/this/is/production/env/<0001-*.patch> .
git am <0001-*.patch>
git push origin
production
git reset --hard HEAD~
git pull origin

2012-09-08

template lookup errorを見つけるスクリプト

| 21:55

pyramidは、template lookup errorが起き得る状態の設定でもアプリケーションが動作する。

もちろん、動的言語的な性質を考えるとruntime errorというのが正しいという気もしないではないけれど。

デプロイ前後にtemplate lookup errorが発生することが分かると結構へこむ。

直接アクセスして調べるのも手間がかかるので事前に検知したい。

use introspector

pyramid.1.3からintrospectionの機能が強化されている。http://pyramid.readthedocs.org/en/latest/narr/introspector.html

内部的には、add_viewやadd_routeなどで、config.action()が呼ばれる際に登録される。

introspectorは以下のようにして取得することが可能

request.registry.introspector

registryへの参照さえあればconfigからでもrequestからでもアクセスできる。

introspectorは

  • viewの情報
  • templateの情報
  • tweenの情報

など色々な情報を持っている。

introspectorの持っているtemplateの情報を利用することでtemplate lookup errorを検知することができるかもしれない。

実際にやってみた。

以下のようなスクリプトを書けば良かった。

from pyramid.paster import bootstrap
import sys

def detect(renderer):
    return renderer.lookup.get_template(renderer.path)

config_uri = sys.argv[1]
env = bootstrap(config_uri)
registry = env["registry"]


for t in registry.introspector.get_category("templates"):
    try:
        renderer = t["introspectable"]["renderer"]
        if renderer.type == ".mako":
            detect(renderer.renderer)
    except Exception as e:
        print str(e)

bootstrapは"develop.ini"や"production.ini"などの設定ファイルのパスを引数として取り、

渡された設定ファイルで立ち上がったアプリケーション(あるいは環境)を返す関数。

viewなどが登録された状態で返ってくるので、これを利用する。

動作チェック

hello0.mako,hello1.makoは存在していないテンプレート。

$ python detect.py development.ini 
Cant locate template for uri 'hello1.mako'
Cant locate template for uri 'hello0.mako'
Cant locate template for uri 'hello1.mako'
Cant locate template for uri 'hello0.mako'

上のスクリプトは、対応しているのがmakoのテンプレートだけだけれど。

真面目にやればtemplate lookup errorを事前にチェックすることができそう。

動作確認に使ったファイルたち

これらは単独では動かない。今日の別記事を参考に

http://d.hatena.ne.jp/podhmo/20120908/1347106193

1ファイルと1プロジェクトの中間

| 21:09

想定読者

  • pyramidの機能を色々試してみたい人
  • 特に、設定ファイル(development.ini)を要求するコマンドの作成などをしてみたい人

発端

pyramidの機能を試す際に、1ファイルでアプリケーションが作れるのは便利なのだけれど。

pyramidで提供されているpから始まるコマンド群*1を使おうとすると設定ファイルを要求される。

1ファイルアプリは設定ファイルを書かないので、このレイヤーの機能の実験をしようと思った場合には、

1プロジェクト作る必要があってめんどうだった。(ここでいう1プロジェクトとはsetup.pyを持つpython packageのこと)

ちょっとした機能を試してみる度に、setup.pyを定義していくのは面倒なのでどうすれば良いのか考えてみた。

ポイントは

  • 機能の実験の度にsetup.pyを作成するのが面倒くさい
  • それに付随して、管理対象のpackageが増えていくのが邪魔。
  • 少なくともsetup.pyを書くのは1回だけにしたい。

実際の方法

1. てきとうに親となるプロジェクト(experiment)を作成する

2. 親となるプロジェクトのトップレベルのモジュールのディレクトリの中にサブモジュールを切って作業

以下のようなイメージ

.
├── development.ini
├── experiment
│   ├── __init__.py
│   ├── hello
│   │   ├── __init__.py
│   │   └── hello.ini
│   ├── tests.py
│   └── views.py
├── production.ini
├── setup.cfg
└── setup.py

experimentというパッケージを作成して、実験用のモジュールはexperiment以下に作成する。

これで"python setup.py develop"は初回の一回だけで済むようになる。

1ファイルアプリケーションと実際のウェブアプリケーション開発の中間位の大きさの作業をする時この構成でやると良い気がする。

実際の利用方法

proutesを使って見るために、helloというアプリを作ることにする。

設定ファイルはhello.ini(development.iniという名前でも構わないけれど)

1ファイルアプリケーション(http://docs.pylonsproject.org/projects/pyramid_tutorials/en/latest/humans/creatingux/step01/index.html)との違いは

  • 直接httpserverを起動しないこと
  • 設定ファイル(.ini)から設定情報を取得する形式なこと

hello/__init__.py

from wsgiref.simple_server import make_server

from pyramid.config import Configurator
from pyramid.response import Response

# This acts as the view function
def hello_world(request):
    return Response('hello!')

def main(global_config, **settings):
    # Grab the config, add a view, and make a WSGI app
    config = Configurator(settings=settings)

    config.add_route("index", "/")
    config.add_view(hello_world, route_name="index")
    app = config.make_wsgi_app()
    return app

hello/hello.ini

[app:main]
paste.app_factory = experiment.hello:main

[server:main]
use = egg:waitress
port = 6543
host = 0.0.0.0
起動
# pwd => experiment/hello
$ pserve hello.ini
# Starting server in PID 12014.
# serving on http://0.0.0.0:6543

proutesを使ってみる。

$ proutes hello.ini
Name            Pattern                        View                     
----            -------                        ----                     
index           /                              <function hello_world at 0x1de28c0>

動いている。

注意

  • ここに書かれるものは基本的に使い捨て
  • 実際のwebアプリケーションとか作るのは止めましょう。

*1:pserve,pviews,proutes,pshell,..など

podhmopodhmo 2012/09/08 22:41 proutesが教えてくれるようになっていた。。

2012-09-02

templerのコマンドのbash completion作ってみる

| 02:13

はじめに

templer使おうとしたけれどコマンド入力するのが面倒になったのでbashの補完を作成してみる。

step0 about complete

completeという関数を利用して補完候補を設定するらしい。

bash: complete: -h: invalid option
complete: usage: complete [-abcdefgjksuv] [-pr] [-DE] [-o option] [-A action] [-G globpat] [-W wordlist]  [-F function] [-C command] [-X filterpat] [-P prefix] [-S suffix] [name ...]

ubuntuでは/etc/bash_completion.d以下に現在のbashの環境で補完に使われている設定が置かれてい模様。

覗いてて見たところ、補完の候補を表示する関数を定義し、complete -Fを使っているものが多かった。

completeの引数に渡せるのに以下のようなものがある。(詳しい情報はman bash)

-W直接指定した文字列の中から絞り込み
-F指定した関数を実行した結果から絞り込み

ためしに補完を作ってみる。

以下は、wheeというコマンドを定義し、その補完候補に[foo,bar,boo]を設定する場合の例。

touch ~/bin/whee #~/binに$PATHが通っている
chmod -c u+x ~/bin/whee 
complete -W "foo bar boo" whee

このようにすると、シェル上でwheeとタブを押下した際に、foo,bar,booを補完候補として表示してくれる。

step1 全候補表示

templerの候補は以下のようにして絞り込める

templer --list | grep ":" | cut -d ":" -f 1

というわけで、以下のようにしてtemplerの補完を追加できる

_templer(){
  COMPREPLY=($(templer --list | grep ":" | cut -d ":" -f 1 ))
}
complete -F _templer templer

関数の中でCOMPREPLYという変数に代入された値が補完候補として扱われるらしい。

ただ、現状では、補完候補として表示されるものの補完はしてくれない

step2 実際に補完してもらう。(compgen)

補完はcompgenがやってくれるらしい。以下のように変える。

_templer(){
  local cur
  cur=${COMP_WORDS[COMP_CWORD]}	
  COMPREPLY=($( compgen -W "`templer --list | grep ':' | cut -d ':' -f 1`" -- $cur))
}
complete -F _templer templer

ただ、現状では、-付きのオプションなどを補完してくれない。

step3 -の引数に対応

あとはbashのプログラミング

_templer(){
  local cur opts templates
  cur=${COMP_WORDS[COMP_CWORD]}	
  opts="--help --make-config-file --list --version"
  templates="`templer --list | grep ':' | cut -d ':' -f 1`"

  if [[ ${cur} == "" ]] ; then
    COMPREPLY=($( compgen -W "${opts} ${templates}" -- $cur))
  elif [[ ${cur} == -* ]] ; then
    COMPREPLY=($( compgen -W "${opts}" -- $cur))
  else   
    COMPREPLY=($( compgen -W "${templates}" -- $cur))
  fi
}
complete -F _templer templer
gist

https://gist.github.com/3580622

参考

2012-09-01

2012-07-31

リモートの閉じられた環境のサーバにアクセスする。

| 01:25

web applicationなどを作成しているとき、公開前の段階であるにもかかわらずwebブラウザなどでアクセスして

実際の動作をチェックしたいということがある。こんな時にトンネルを掘って作業する。

ちょっと調べたりして時間がかかったりしてしまったのでメモ。

だいたいひつような作業

大体必要な作業は以下のとおり

  1. sshでlogin(鍵の設定)
  2. アクセスできるか確認コマンドライン
  3. ブラウザの設定

sshでlogin

sshでloginしよう。同じ鍵を持ち回る時ssh-agentを使っても良い。

また、以下のような設定ファイルをかきトンネルを掘っても良い。

対象とするネットワーク

[local] --> [humidai] ---> [target.app]

humidaiは外部へ公開されていて、local(自分)からsshでアクセスすることは可能。

ここで、sshでトンネルを掘って直接target.appにつなげられるようにしてみる。*1

HOST target.app.net
     user podhmo
     HostName ap2
     ProxyCommand ssh podhmo@humidai.net -W %h:%p 
     IdentityFile ~/.ssh/id_rsa_your_key

HOST humidai.net
     User podhmo
     IdentityFile ~/.ssh/id_rsa_your_key

ProxyCommandは、sshでログインしようとした際に間で実行されるコマンド。%h,%pにはホスト名とポート番号が入る。

Dynamic forwarding

sshにはdynamic forwardingの機能がある。これを使うと簡単にsocksサーバとして動作するようになる。

これを使って、上図のtarget.appのような外部から閉じた環境へブラウザでアクセスするといったことができるようになる。

アクセスできるか確認コマンドライン

実際にログインできるかどうかただsshでアクセスしてみる。

ssh podhmo@target.app.net

これでログインできなかったら設定を見直す。(-v)とかつけて実行してみるとエラーの原因が分かるかもしれない。

sshでログインができるようになったら、前述したsocksサーバを立てる。

ssh -D 1080 -N podhmo@target.app.net

Dに渡したポート番号(省略すると1080)でsocksサーバが立ち上がる。(Nつけて良いと思う。-fはおこのみで)

これが実際に動いているかどうか確かめたい。GUIベースのブラウザは確認が面倒なのでコマンドラインで調べる。curlを使う。

## ちなみにこのオプションの指定方法は古い。あとsocks4ではなく、socks5かもしれない。
curl --socks4 localhost:1080 localhost:8000
curl --socks4a localhost:1080 localhost:8000 

200っぽいメッセージが返ってくるか調べてみる。

socks4aはホスト名の解決がリモート先のホストで行われるし。aなしのsock4はホスト名の解決がアクセス元(local)のホストで行われる

(というわけで、上のsocks4の例はあまり意味がない。)

curlが動いたことを確認したらブラウザで確認する

ブラウザでの確認作業

firefoxやchromeでproxy設定(socks)を選び、socks関係の選択を追加する。

firefoxは、

  • network.proxy.socks_remote_dns
  • network.proxy.socks_version

などを確認してみる。remote_dnsがtrueならば、curlのsocks4aと同様の動きをするはず*2

*1:昔はnetcat(nc)とか使ってました

*2:手元の環境では動かなかった。

2012-07-25

ssh-agent

| 20:24

ssh-agent

鍵をリモートホストを渡り歩く時に、鍵を持ち回ってくれる

local -> x -> y -> z

というサーバがある時

localで

$ ssh-add <key>
$ ssh <yourname>@<xのadresss>

とすると。

x から y, y から zへつなぐとき. localでaddしたkeyをssh loginに利用してくれるという理解。

ログインした x 上で

$ ssh-add -l

とすると。localでaddしたkeyが見えていれば正常。

x から yのときは-Aオプションいらない。

$ ssh <yourname>@<yのaddress>

でつなげることができる。

2012-07-20

ctypes入門してみた。

| 01:01

ctypesって?

pythonのFFI用のモジュールらしい。

pythonの枠からはみ出た関数をpythonから呼び出すための接触面といった感じのもの.

ドキュメントを見て、動くコードを書いてみた。

やってみたのは以下のこと

  • floatの配列を作ってこれをqsort
  • python側で構造体を作成してこれをqsort

qsortは関数ポインタを使っているし、だいたいの機能を網羅していると思う。((実際には、bitfieldの利用やunionなど使っていない機能はあるけれど、まぁ必要十分だと思う)

実際のコード

最初、qsortに渡す関数の戻り値をintにしていなくてハマったのはないしょ。

floatの配列をqsort

python側で作成した構造体(Point)をqsort

ldconfigとかpkgconfigなどのコマンドも久しぶりに使ってみてだいぶオプションの内容など忘れている。

2012-06-08

seq使うのむずかし。(Seq.skipおそい)

| 00:44

まとめ

  • seq式で遊ぼうとした
  • combinationsとか生成すれば良さそう。
  • 全部seqにすると遅い。

seq式で遊ぼうとした

seq式は値の列を返す計算を返してくれる。

> seq {for x in [1..10] do yield x*x} |> Seq.toList;;
val it : int list = [1; 4; 9; 16; 25; 36; 49; 64; 81; 100]
  • 要素を結果として与えてくれるyield
  • 列をconcatしてくれるyield!

の2つがある。

> seq {yield 1; yield 2; yield 3};;
val it : seq<int> = seq [1; 2; 3]

> seq {yield! [1;2;3]; yield 4; yield! [5]};;
val it : seq<int> = seq [1; 2; 3; 4; ...]

combinationsでも生成すれば良さそう

普通に再帰で書いたことはあるけれど、seq式のようなものでまとめて書いたことない。

yield!があるので、全部seq式で書けそう。

書いた。

全部seqにすると遅い。

[1..20]までの列から8個取る組み合わせを列挙してみる

$ time ./permtations.exe

  ...]./permtations.exe  39.82s user 0.81s system 176% cpu 23.075 total

すっごい遅い。

profilerを使ってみる。

monoなので以下のような感じでprofiler使う。

$ mono --profile=default:stat permtations.exe

prof counts: total/unmanaged: 2383/1115
   1014	42.57 % mono()
    349	14.65 % Microsoft.FSharp.Collections.SeqModule/Skip@1428<int>:GenerateNext (System.Collections.Generic.IEnumerable`1<int>&)
    267	11.21 % Microsoft.FSharp.Core.CompilerServices.GeneratedSequenceBase`1<int>:MoveNextImpl ()
    109	 4.58 % Microsoft.FSharp.Core.CompilerServices.GeneratedSequenceBase`1<int>:System-Collections-IEnumerator-MoveNext ()
     69	 2.90 % (wrapper alloc) object:Alloc (intptr)
     60	 2.52 % Microsoft.FSharp.Core.CompilerServices.GeneratedSequenceBase`1<int>:System-Collections-Generic-IEnumerator`1-get_Current ()
     53	 2.23 % Microsoft.FSharp.Core.Operators/OperatorIntrinsics/BaseRangeEnumerator`1<int>:System-Collections-IEnumerator-MoveNext ()
     43	 1.81 % Microsoft.FSharp.Collections.PrivateListHelpers/ListEnumerator`1<int>:System-Collections-IEnumerator-MoveNext ()
     34	 1.43 % (wrapper managed-to-native) object:__icall_wrapper_mono_object_new_fast (intptr)
     34	 1.43 % Microsoft.FSharp.Collections.SeqModule/Skip@1428<int>:Close ()

Seq.skipが遅い。Seq.skipが遅い。

Seq.skip

Seq.skipはdropみたいなもの。N個飛ばした列を返す

> seq {yield 1; yield 2} |> Seq.skip 1;;
val it : seq<int> = seq [2]
> [1;2;3] |> List.tail;;
val it : int list = [2; 3]

Seq.skip使っている箇所をlistに置き換えてみる。

$ time ./permtations2.exe

...]./permtations2.exe  2.63s user 0.11s system 173% cpu 1.575 total

早くなった。

ちなみに

pythonのitertolsを使った場合はこれくらいの速度([:20]はf#の出力に大体合わせるため)

 $ time python -c "import itertools as i; print [x for x in i.combinations(range(20),8)][:20]"                                                                                                                  [~/sandbox/fsharp]
[(0, 1, 2, 3, 4, 5, 6, 7), (0, 1, 2, 3, 4, 5, 6, 8), (0, 1, 2, 3, 4, 5, 6, 9), (0, 1, 2, 3, 4, 5, 6, 10), (0, 1, 2, 3, 4, 5, 6, 11), (0, 1, 2, 3, 4, 5, 6, 12), (0, 1, 2, 3, 4, 5, 6, 13), (0, 1, 2, 3, 4, 5, 6, 14), (0, 1, 2, 3, 4, 5, 6, 15), (0, 1, 2, 3, 4, 5, 6, 16), (0, 1, 2, 3, 4, 5, 6, 17), (0, 1, 2, 3, 4, 5, 6, 18), (0, 1, 2, 3, 4, 5, 6, 19), (0, 1, 2, 3, 4, 5, 7, 8), (0, 1, 2, 3, 4, 5, 7, 9), (0, 1, 2, 3, 4, 5, 7, 10), (0, 1, 2, 3, 4, 5, 7, 11), (0, 1, 2, 3, 4, 5, 7, 12), (0, 1, 2, 3, 4, 5, 7, 13), (0, 1, 2, 3, 4, 5, 7, 14)]
python -c   0.07s user 0.01s system 93% cpu 0.086 total

podhmopodhmo 2012/06/09 22:54 fsc.exe --optimize+ --warn:4などで最適化をかけると少し速くなるけれど。元々の実装に無駄があるし。

2012-06-06

cat

| 23:29

  • EntryPointというattributeをつけるとコマンドライン引数を受け取る関数として扱われる
  • openでmodule(namespace)のopen
  • (System.IO.File.Readlinesは環境が古くて無かったっぽい)
  • オーバーロードされたメソッドを利用する時には型注釈必要。
open System

let lines (name:string) = 
    seq { use sr = new IO.StreamReader(name)
          while not sr.EndOfStream do
          yield sr.ReadLine()
    }   

let cat (name:string) =
    lines name
    |> Seq.iter (fun (x:string) -> Console.WriteLine x)

[<EntryPoint>]
let main (args:string[]) =
    args |> Seq.iter cat
    0
$ fsc.exe cat.fs
$ cat.exe cat.fs

いげ太いげ太 2012/06/07 01:15 let cat (name:string) =
Seq.iter (fun (x:string) -> Console.WriteLine x) (lines name)

とした場合は x に型注釈が必要ですが、

let cat (name:string) =
lines name |> Seq.iter (fun x -> Console.WriteLine x)

とする場合は、lines name の段階で型が解決されるため、x に型注釈を書く必要はありません。

で、より短くは、

let cat name = lines name |> Seq.iter Console.WriteLine

とも書けますね。

podhmopodhmo 2012/06/09 00:48 おお。そういえば、パイプライン演算子の前段で型が解決された場合注釈要らないんでした

2012-05-30

f#の環境構築(ubuntu)

| 22:15

作業

  • f#のインストール
  • emacsの環境作成(fsharp-mode)
  • F#を利用してみる

f#のインストール

大体はこのURLのページのとおりに作成。

sudo aptitude install mono-devel mono-tools-devel
libmono-winforms2.0-cil libmono-system-runtime2.0-cil

pwd # /var/project
wget http://download.microsoft.com/download/6/B/6/6B6BFB83-3D3A-467C-8080-01F7A953A37F/fsharp.zip

unzip fsharp.zip
cd FSharp-2.0.0.0/bin

echo "export MONO_PATH=`pwd`:\$MONO_PATH" >> ~/.zshrc
echo "export PATH=`pwd`:\$PATH" >> ~/.zshrc
chmod u+x *.exe

インストールするF#のversionが古かったので、新しい方を利用することにした。

ダウンロードしたzipファイルを展開したディレクトリのREADMEに従う必要は無かった。

emacsの環境作成(fsharp-mode)

以下のリンクから取得する。((sourceforgeからのダウンロードは既に面倒な作業になってきている。(githubなどの方が楽)))

http://sourceforge.net/projects/fsharp-mode/files/latest/download

unzip fsharp-0.3.zip
mv fsharp fsharp-mode
editor fsharp-mode/init.el

fsharp-modeを有効にするための設定を追加(init.el)

;; if current-directory is not found. use `default-directory'
(add-to-list 'load-path (current-directory))
(add-to-list 'auto-mode-alist '("\\.fs[iylx]?$" . fsharp-mode))

(autoload 'fsharp-mode "fsharp" "Major mode for editing F# code." t)
(autoload 'run-fsharp "inf-fsharp" "Run an inferior F# process." t)

これでemacsの環境は整った。

f#を利用してみる。

hello world(compile)

echo 'printf "hey" ' >> hello.fs
fsc.exe hello.fs
./hello.exe

動いた。

 
Connection: close