Xtrabackup Install on Centos6.4

mysql
Xtrabackupのインストールでのちょっとしたメモです。

XtraBackupの最新版はこちら 

#rpm -Uvh percona-xtrabackup-2.1.5-680.rhel6.x86_64.rpm
警告: percona-xtrabackup-2.1.5-680.rhel6.x86_64.rpm: ヘッダ V4 DSA/SHA1
Signature, key ID cd2efd2a: NOKEY
エラー: 依存性の欠如:
        perl(DBD::mysql) は percona-xtrabackup-2.1.5-680.rhel6.x86_64に必要とされています
        perl(Time::HiRes) は percona-xtrabackup-2.1.5-680.rhel6.x86_64に必要とされています

こんな感じでインストールが出来なかったので

# yum install perl-DBD-MySQL
# yum install perl-Time-HiRes
上の二つを実行してインストールすることが出来ました。
 
実際にコマンドを実行したのですが、失敗しました。
# innobackupex --user=root /data/backup

InnoDB Backup Utility v1.5.1-xtrabackup; Copyright 2003, 2009 Innobase Oy and Percona LLC and/or its affiliates 2009-2013. All Rights Reserved. This software is published under the GNU GENERAL PUBLIC LICENSE Version 2, June 1991. innobackupex: Error: Failed to connect to MySQL server as DBD::mysql module is not installed at /usr/bin/innobackupex line 2913.

どうやらperlのDBD::mysqlをインストールする必要があるようです。
# cpan install DBD::mysql

終わり
 

mysqlでデータが存在するときinsertしないDML

今回はmysqlでinsertするデータが存在するとき、insert文を実行しないDMLを紹介します。

よくデータベースの作成時にもし存在しなかったら作成するSQL文をみたことがあると思います。
「 IF NOT EXISTS 」です。
CREATE TABLE IF NOT EXISTS `Kin` (
  `category` int(2) NOT NULL,
  `point` int(2) NOT NULL
) ENGINE=MyISAM DEFAULT CHARSET=utf8;
    

今回はもし存在しなかったらの「 INSERT 」文です。

① INSERT IGNORE
② INSERT ... SELECT


通常のINSERT文
INSERT INTO `Category` VALUES ( 1, 'TEST');

INSERT IGNORE

INSERT IGNORE INTO `Category` VALUES ( 1, 'TEST');
とても簡単ですね。
通常のINSERT文の前に「IGNORE」を追加すればすみます。
しかし、この場合はもしINSERT文がエラーでも正常終了するという
try and catch みたいなものです。

INSERT ... SELECT

INSERT INTO `Category`
SELECT * FROM (SELECT 1, 'TEST') AS tmp
WHERE NOT EXISTS (
    SELECT content FROM Category WHERE content = 'TEST'
) LIMIT 1;

複雑ですが、こちらはIGNOREとは異なり存在しているデータを検索し
該当するテーブルが存在しない場合のみデータをINSERTします。

2種類の違いについて

IGNOREはエラーの場合のみ挿入しない。つまりエラーでなかったら挿入されることになります。例としては、同じプライマリキーが挿入されたり、ユニークカラムにも関わらずデータが挿入されたり基本的な構文エラーになります。
一方でINSERT..SELECTはそのままのSELECT文になるため、自由に挿入の条件を決めることが出来ます。


参考ページ
insert-ignore-vs-insert-on-duplicate-key-update



きんちゃんシステムをChefで実装。

欽ちゃんシステムとは、自分が開発した投票システムです。


これをChefを使ってデプロイ出来るようにしたいと思います。

今回行うべきこと
PHP,Nginx,Mysqlのインストール
githubからコードをClone
・Nginxの設定
・データベースの作成

以上です。

下準備

まずcookbookの作成からです。今回はBreksfileのコマンドを利用します。
berks cookbook cookbook_kinchan
以下のファイルが作成されます。
32

これでおkです。

必要なパッケージをインストール

必要なBerksfileと同じ階層にはmetadata.rbが存在します。
ここに必要なパッケージ化されているcookbookを登録します。

デフォルトでは以下のようになっています。
Berksfile
site :opscode

metadata

metadata.rb
name             'cookbook_kinchan'
maintainer       'YOUR_NAME'
maintainer_email 'YOUR_EMAIL'
license          'All rights reserved'
description      'Installs/Configures myface'
long_description IO.read(File.join(File.dirname(__FILE__), 'README.md'))
version          '0.1.0'
Berksfileの方でmetadata.rbを読み込むように設定されているのが分かります。
metadata.rbを書き直します
name             'cookbook_kinchan'
maintainer       'Tomohisa Omagari'
maintainer_email 'ooma0301@gmail.com'
license          'All rights reserved'
description      'Installs/Configures cookbook_kinchan'
long_description IO.read(File.join(File.dirname(__FILE__), 'README.md'))
version          '0.1.0'

depends "mysql", "~> 3.0.0"
depends "nginx", "~> 1.8.0"
depends "php", "~> 1.2.4"
Berksfileと同じ階層にあるmetadata.rbはGemfileみたいなもので依存関係のcookbookを取得します。
しかし、取得できるのパッケージ化されたcookbookのみになりますので注意してください。
これに関してブログを書こうと思っています。

recipes/default.rb
include_recipe 'mysql::server'
include_recipe 'nginx'
include_recipe 'php'

Vagrantfile
Vagrant.configure("2") do |config|
  config.vm.hostname = "cookbook-kinchan-berkshelf"
  config.vm.box = "test"
  config.vm.box_url = "https://dl.dropbox.com/u/31081437/Berkshelf-CentOS-6.3-x86_64-minimal.box"
  config.vm.network :private_network, ip: "33.33.33.10"
  config.ssh.max_tries = 40
  config.ssh.timeout   = 120

  config.berkshelf.enabled = true

  config.vm.provision :chef_solo do |chef|
    chef.json = {
      mysql: {
        server_root_password: 'rootpass',
        server_debian_password: 'debpass',
        server_repl_password: 'replpass',
        bind_address: "127.0.0.1"
      }
    }

    chef.run_list = [
      "recipe[cookbook_kinchan::default]"
    ]
  end
end
これでうまくいきました。
ここまでやったことはnginx,mysql,phpのインストールのみです。

githubからコードをCloneする

githubからのコードのダウンロードを行います。
Chefのリソースには「git」があるためこちらを利用します。

Opscode_Resource_git
gitのリソースを利用するためにgitのパッケージでgitをインストールします。

metadata.rb 追記
depends "git", "~> 2.6.0"

attributes/default.rb
default['kinchan']['path'] = "/var/www/kif"

recipes/default.rb 追記
include_recipe 'git' 
git "#{node['kinchan']['path']}" do
repository "https://github.com/oomatomo/kif.git" action :sync end
これでおkです。

Nginxの設定

nginxの設定では先ほどcloneしたソースをnginxでアクセス出来るようにします。
行うこと
 ・iptablesの停止
 ・/etc/nginx/site-enabledの直下にcloneしたコードの設定ファイル

attributes/default.rb 追記
default['kinchan']['path'] = "/var/www/kif"

recipes/default.rb 追記
# setting nginx
template "#{node['nginx']['dir']}/sites-enabled/default" do
  source "default-site.erb"
end
service "iptables" do action :stop end 

template/default/default-site.rb 追記
server {
  listen   80;
  access_log  <%= node['nginx']['log_dir'] %>/localhost.access.log;
  location / {
    root   <%= node['kinchan']['path'] %>;
    index  index.html index.htm;
  }
}
これは仮想環境側では以下の通りになります。

/etc/nginx/sites-enabled/default
 server {
  listen   80;
  server_name  cookbook-kinchan-berkshelf;
  access_log  /var/log/nginx/localhost.access.log;
  location / {
    root   /var/www/kif;
    index  index.html index.htm;
  }
}
ではアクセスしてみます。
http://33.33.33.10/
49
nginxのデフォルトしか表示されません。

いろいろと探していたらStackOverFlowにありました。
how-to-disable-default-nginx-site-when-using-chef-and-vagrant

recipes/default.rb 変更
# setting nginx
nginx_site 'default' do
  enable false
end

##変更点 file "/etc/nginx/conf.d/default.conf" do action :delete end template "#{node['nginx']['dir']}/sites-enabled/default" do source 'default-site.erb' notifies :restart, 'service[nginx]' end ##ここまで
service "iptables" do action :stop end
 
12
表示されました。よかった。よかった。

データベースの設定 

最後にデータベースの設定をします。これをやらないと投票が出来ないからです。
mysqlのインストールはすでに完了してますがデータベースの作成は別のパッケージがあります。

それがdatabaseです。
こちらのREADMEを見ると。必要なのは以下の3つかなと思います
# Use mysql :socket key in connection info
mysql_connection_info = {
  :host => "localhost",
  :username => 'root',
  :password => node['mysql']['server_root_password'],
  :socket => '/db/mysql.sock'
}

mysql_database_user 'newrelic' do
  connection mysql_connection_info
  password newrelic_pass
  action :create
end

# same create commands, connection info as an external hash
mysql_database 'foo' do
  connection mysql_connection_info
  action :create
end
以下の通りでデータベースとユーザーが作成されます。
ついでにVagrantfileに直接書いていたchef.jsonをattributeに移して以下のようになりました。


attribute/default.rb
default['kinchan']['path'] = "/var/www/kif"
default['kinchan']['database']['host'] = '127.0.0.1'
default['kinchan']['database']['name'] = 'kif'
default['kinchan']['database']['user'] = 'kin'
default['kinchan']['database']['pass'] = 'kin'

default['mysql']['server_root_password'] = 'rootpass'
default['mysql']['server_debian_password'] = 'debpass'
default['mysql']['server_repl_password'] = 'replpass'
default['mysql']['bind_address'] = default['kinchan']['database']['host']
recipes/default.rb
include_recipe 'mysql::ruby'
include_recipe 'mysql::server'
include_recipe 'database::mysql'
# connect mysql
mysql_connection_info = {
  :host => node['kinchan']['database']['host'],
  :username => 'root',
  :password => node['mysql']['server_root_password']
}

# create database mysql_database node['kinchan']['database']['name'] do connection mysql_connection_info action :create end
# create mysql user mysql_database_user node['kinchan']['database']['user'] do connection mysql_connection_info password node['kinchan']['database']['pass'] database_name node['kinchan']['database']['name'] action [ :create, :grant ] end
次は実際のテーブルの作成を行います。
方法は以下の通りです。
 ①SQLのファイルをfiles/default/以下に作成する。
 ②chefでサーバにファイルを作成するコードを書く。
 ③mysqlで実行を行うchefを実装する。

①はすぐに終わると思います。
②cookbook_fileを利用します。
cookbook_fileはcookbookのfilesをサーバへおくことが出来ます。

cookbook_file [ 設置したいサーバのパス ] do
source [ cookbookのファイルパス ]
end

attribute/default.rb
default['kinchan']['database']['sql_file'] = '/tmp/kif.sql'
recipes/default.rb
cookbook_file node['kinchan']['database']['sql_file'] do
  source "kif.sql"
  owner "root"
  group "root"
  mode "0600"
end

③excuteを利用します
recipes/default.rb
execute "create mysql table" do
  command "mysql -h #{node['kinchan']['database']['host']} -u #{node['kinchan']['database']['user']} -p#{node['kinchan']['database']['pass']} -D #{node['kinchan']['database']['name']} < #{node['kinchan']['database']['sql_file']}"
  not_if  "mysql -h #{node['kinchan']['database']['host']} -u #{node['kinchan']['database']['user']} -p#{node['kinchan']['database']['pass']} -D #{node['kinchan']['database']['name']}  -e 'describe users;'"
end

よしこれで完了だ。動かしてみよう!!

動かない。
忘れてたphp-fpmが入ってない!!!

PHP-FPMの設定でNginxでPHPを動かす

NginxでPHPを動かすためにはPHP-FPMが必要になります。

attributes/default.rb 追加
default['php-fpm']['host'] = '127.0.0.1:9000'
default['php-fpm']['user'] = 'nginx'
default['php-fpm']['group'] = 'nginx'

templates/default/default-site.erb 変更
server {
  listen   80;
  access_log  <%= node['nginx']['log_dir'] %>/localhost.access.log;
  root   <%= node['kinchan']['path'] %>;

  location ~* \.php$ {
    fastcgi_pass  <%= node['php-fpm']['host'] %>;
    fastcgi_index index.php;
    fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
    fastcgi_param PATH_INFO $fastcgi_script_name;
    include     fastcgi_params;
  }
}

templates/default/php-fpm-www.conf.erb
[www]
listen  = <%= node['php-fpm']['host'] %>
user    = <%= node['php-fpm']['user'] %>
group   = <%= node['php-fpm']['group'] %>
listen.allowed_clients = 127.0.0.1
pm = dynamic
pm.max_children = 50
pm.start_servers = 5
pm.min_spare_servers = 5
pm.max_spare_servers = 35
slowlog = /var/log/php-fpm/www-slow.log
php_admin_value[error_log] = /var/log/php-fpm/www-error.log
php_admin_flag[log_errors] = on
php_value[session.save_handler] = files
php_value[session.save_path] = /var/lib/php/session
recipes/default.rb

# setting php-fpm package 'php-fpm' do action [:install, :upgrade] end file "/etc/php-fpm.d/www.conf" do action :delete end template "/etc/php-fpm.d/www.conf" do mode 0644 source "php-fpm-www.conf.erb" end service 'php-fpm' do action :start end

これでうまく動きました。よかったです。

コードをきれいにする

ちょっと長いので別の記事で。


感想
Chefは面白いですね。またパッケージが豊富なため楽であることとパッケージ化されたコードを見ることでかなりの勉強になると感じました。あとはdatabase←のコードを見ていて思ったことが実際にデータベースを作成しているのはRubyで書いているような気がします。こんな書き方もあるんですね。

さて、新人研修や配属後の仕事で半年ほど自分の勉強を怠ったため知識が
半年分遅れている気がするので今後は自主的な勉強の時間の確保を頑張ります。

参考にしたページ

berkshelf-application-cookbooks

getting-started-writing-chef-cookbooks-the-berkshelf-way

include_recipeで他のレシピを呼び出せることに便利さを感じました。
今後も活用したいと思います。


 

ShellScriptの特殊変数と比較

今回、oh-my-zshのテーマのカスタマイズで学んだシェルスクリプトについて書きます。
 
 ・特殊変数
 ・変数の比較(数値比較、文字列比較)  

特殊変数

$$・・・・・・現在のプロセス番号(PID)
$?・・・・・・直近のコマンドの実行結果のステータスコード
$#・・・・・・シェルスクリプト実行時の引数の数
$@,$*・・・シェルスクリプト実行時の全引数の変数


$?の種類について

0・・・・コマンド正常終了
1・・・・コマンド以上終了
2・・・・シェルスクリプトの異常終了(文法エラー、文字列の比較など)
126・・・権限のないファイルを実行する
127・・・シェルスクリプト内での"command not found"
130・・・Ctl-Cを使用した場合のスクリプトの強制停止

status code is 2
構文でのエラー
#!/bin/bash
for a in 1 2 3
do
  echo "test"
数値と文字列の計算でのエラー
#!/bin/bash
expr 12 + "test"

status code is 126
権限のないファイルを実行する


[root@oomatomo app]# chown root:root test.sh [root@oomatomo app]# chmod 700 test.sh [root@oomatomo app]# exit exit [tomo]$./test.sh zsh: 許可がありません: ./test.sh [tomo]$ echo $? 126 [tomo]$ ls -la test.sh -rwx------ 1 root root 44 10月 8 22:38 2013 test.sh

こんな感じですね。これらを利用するとより便利なシェルスクリプトが使えそうですね。

変数の比較(数値比較、文字列比較)

シェルスクリプトでの比較の方法はいくつか存在することが分かりました。

数値の比較
$ if COUNT=$( echo 1 )   && [ $COUNT = 1 ] ;then echo true; else echo false; fi
true
$ if COUNT=$( echo "1" ) && [ $COUNT = 1 ] ;then echo true; else echo false; fi
true
$ if COUNT=$( echo "" )  && [ $COUNT = 1 ] ;then echo true; else echo false; fi
zsh: parse error: condition expected: =
false
$ if COUNT=$( echo "" )  && [ "$COUNT" = 1 ] ;then echo true; else echo false; fi
false
$ if COUNT=$( echo "1" ) && [ $COUNT = "1" ] ;then echo true; else echo false; fi
true
$if COUNT=$( echo "1" )  && [ "$COUNT" = "1" ] ;then echo true; else echo false; fi
true

文字列の比較 
$ if COUNT=$( echo "hoge") && [ $COUNT = "hoge" ] ;then echo true; else echo false; fi
true
$ if COUNT=$( echo "hoge") && [ "$COUNT" = "hoge" ] ;then echo true; else echo false; fi
true
$ if COUNT=$( echo "") && [ $COUNT = "hoge" ] ;then echo true; else echo false; fi
zsh: parse error: condition expected: =
false
$ if COUNT=$( echo "hoge") && [ x$COUNT = "hoge" ] ;then echo true; else echo false; fi
false
$ if COUNT=$( echo "hoge") && [ x$COUNT = x"hoge" ] ;then echo true; else echo false; fi
true
$ if COUNT=$( echo "hoge") && [ $COUNT = x"hoge" ] ;then echo true; else echo false; fi
false
$ if COUNT=$( echo "hoge") && [ x$COUNT = "hoge" ] ;then echo true; else echo false; fi
false
$ if COUNT=$( echo "") && [ x$COUNT = x"hoge" ] ;then echo true; else echo false; fi
false
$ if COUNT=$( echo "") && [ "$COUNT" = "hoge" ] ;then echo true; else echo false; fi
false
文字列は x$変数名 もしくは x"hoge" で文字列扱いになるそうです。

結論
数値の比較も文字列の比較も空の値の場合に対応して ”” で囲ったほうが良いという結論です。

 

OpsWorksでインスタンス作成。

実際にアプリのインスタンスを作成したいと思います。
やったことをぺたぺた画像を貼っていきたいと思います。

概念的なことはこちらに。。。

 stack , layer, instance, appの順番で見ていきます。

Stack

Stackを作成します。適当に名前をつけて[ Add Stack ]を押します。
今回は「kinchan」にします。
55

作成後、こちらの画面になります。
なにやら利用するためのチュートリアルらしいです。
21
 
チュートリアルにしたがって進みたいと思います。
はじめはLayerの追加へ。

Layer

ここでは、何のLayerを追加できるか選択できます。
選択内容は、ロードバランサ、アプリ、データベースです。
今回はアプリを選択します。
37

ちなみに今現在は以下の通りです。今回はphpなので[ Static Web Server ]を選択します。28



Add Layerを押すとLayerが完成です。なんて簡単。
53

完成画面に[ Add an Instance ]がありますので、押してみます。

Instance

instanceを作成します。ここでLayerのタイプがAppServerであった場合、
こちらで作成したインスタンスはEC2のダッシュボードの方にも反映されます。
Sizeでサーバのスペックを決めることが出来ます。

39

04


作成したら、このように見やすい画面にいきます。
1つのインスタンスがstop状態であることが分かります。
[ start ]を押してみます。 
55

インスタンスがonline状態になったことが分かります。
どうやら正常に稼働しているみたいです。
53


App

では最後にAppです。
Appの登録画面ではアプリのコードの場所( Gitのリポジトリなど )
ドメインSSLの設定を登録します。
48

これですべての要素を完成することができました。
最後にdeployをやってみたいと思います。
deployでもインスタンスの作成と同じでGUIで設定できます。
さらにChefに送るJSONもこちら側で設定できます。
( OpswrokでのChefの動作はまだ分かっていません)



Deployのコマンドも以下の通りです。
50



いざdeployです。今回は[ Start Web Server ]をします。
deployするインスタンスを選択します。

43
Deployを押すとdeployが始まります。
01
deoply成功。

46

ログ見れるみたいなので見ました。
なげー。
07

感想
GUIでやると楽ですね。これにChefがどう絡むのか勉強したいです。 


 

Oh-my-zshのテーマをカスタマイズ

今回は、Oh-my-zshをインストールしthemaをカスタマイズしました。

curl -L https://github.com/robbyrussell/oh-my-zsh/raw/master/tools/install.sh | sh
 install.shでは特に難しいことやっていないため、手動で行っても良いかもしれません。

32

早くて簡単です。

.oh-my-zshが新しく作成されます。


.oh-my-zsh
├── MIT-LICENSE.txt
├── README.textile
├── custom
├── lib
├── log
├── oh-my-zsh.sh
├── plugins
├── templates
├── themes
└── tools
今回注目するのは、themesです。
oh-my-zshには多くのプロンプトのテーマが存在します。
Themes

これらのテーマを使うのもいいのですがやはり自分専用の
プロンプトがほしくなると思います。ということで作りました。
今回はプロンプトに表示したかったものとして
 ・Gitのリポジトリの情報
 ・バックグランドのプロセスの数
です。

Gitのリポジトリの情報


Gitの情報の取得の関数はすでに.oh-my-zsh/lib/git.zshにあります。
しかし、すでにある関数では主にファイルの変更、削除やリモートとのコミットの差分のチェックは
存在の有無であったため自分は数まで把握したいなと思いカスタマイズしました。

Gitのリポジトリの情報としてほしかったのが
 ・ローカルリポジトリの状態
 ・リモートリポジトリとの差分

ローカルリポジトリの状態

まず、ローカルのリポジトリの情報の取得です。

以下がコードです。(ほとんど.oh-my-zsh/lib/git.zshと同じかつ汚いコードになりました)
custom_git_prompt_status() {
  #INDEX=$(command git status --porcelain -b 2> /dev/null)
  # for git version 1.7.1
  INDEX=$(command git status --porcelain 2> /dev/null)
  STATUS=""
  if COUNT=$(echo "$INDEX" &> /dev/null | grep -E '^\?\? ' -c ) && [ $COUNT -gt 0 ]; then
    STATUS="$STATUS$ZSH_THEME_GIT_PROMPT_UNTRACKED$COUNT"
  fi

  # Staged
  STAGED=""
  if COUNT=$(echo "$INDEX" &> /dev/null | grep '^A  ' -c ) && [ $COUNT -gt 0 ]; then
    STAGED="$STAGED$ZSH_THEME_GIT_PROMPT_STAGED_ADDED$COUNT"
  fi
  if COUNT=$(echo "$INDEX" &> /dev/null | grep '^M  ' -c ) && [ $COUNT -gt 0 ]; then
    STAGED="$STAGED$ZSH_THEME_GIT_PROMPT_STAGED_MODIFIED$COUNT"
  fi
  if COUNT=$(echo "$INDEX" &> /dev/null | grep '^R  ' -c ) && [ $COUNT -gt 0 ]; then
    STAGED="$STAGED$ZSH_THEME_GIT_PROMPT_STAGED_RENAMED$COUNT"
  fi
  if COUNT=$(echo "$INDEX" &> /dev/null | grep '^D  ' -c ) && [ $COUNT -gt 0 ]; then
    STAGED="$STAGED$ZSH_THEME_GIT_PROMPT_STAGED_DELETED$COUNT"
  fi

  if [ "$STAGED" != "" ];then
    STATUS="$STATUS$ZSH_THEME_GIT_PROMPT_STAGED_PREFIX$STAGED$ZSH_THEME_GIT_PROMPT_STAGED_SUFFIX"
  fi

  # Unstaged
  UNSTAGED=""
  if COUNT=$(echo "$INDEX" &> /dev/null | grep '^AM \|^ T \|^ M ' -c ) && [ $COUNT -gt 0 ]; then
    UNSTAGED="$UNSTAGED$ZSH_THEME_GIT_PROMPT_MODIFIED$COUNT"
  fi
  if COUNT=$(echo "$INDEX" &> /dev/null | grep '^AD ' -c ) && [ $COUNT -gt 0 ]; then
    UNSTAGED="$UNSTAGED$ZSH_THEME_GIT_PROMPT_DELETED$COUNT"
  fi

  if [ "$UNSTAGED" != "" ];then
    STATUS="$STATUS$ZSH_THEME_GIT_PROMPT_UNSTAGED_PREFIX$UNSTAGED$ZSH_THEME_GIT_PROMPT_UNSTAGED_SUFFIX"
  fi

  if $(command git rev-parse --verify refs/stash >/dev/null 2>&1); then
    STATUS="$STATUS$ZSH_THEME_GIT_PROMPT_STASHED"
  fi
  if $(echo "$INDEX" | grep '^UU ' &> /dev/null); then
    STATUS="$STATUS$ZSH_THEME_GIT_PROMPT_UNMERGED"
  fi
  if $(echo "$INDEX" | grep '^## .*ahead' &> /dev/null); then
    STATUS="$STATUS$ZSH_THEME_GIT_PROMPT_AHEAD"
  fi
  if $(echo "$INDEX" | grep '^## .*behind' &> /dev/null); then
    STATUS="$STATUS$ZSH_THEME_GIT_PROMPT_BEHIND"
  fi
  if $(echo "$INDEX" | grep '^## .*diverged' &> /dev/null); then
    STATUS="$STATUS$ZSH_THEME_GIT_PROMPT_DIVERGED"
  fi

  echo $STATUS
}

まずGitの状態を取得するためにgit status --porcelainを使っています。
これは以下のようにステータス情報を簡単に表示してくれます。
$git status --porcelain
 M zsh/theme/ooma.zsh-theme
?? test
custom_git_prompt_statusでは表示された内容をgrepで検索を行い、
存在する行数をカウントして表示するという 簡単なメソッドです。
$git status --porcelain | grep '^ M'
 M zsh/theme/ooma.zsh-theme
$git status --porcelain | grep '^ M' -c
stashのチェックもここで行っています。
  if $(command git rev-parse --verify refs/stash >/dev/null 2>&1); then
    STATUS="$STATUS$ZSH_THEME_GIT_PROMPT_STASHED"
  fi
コマンドが私が見たことの無いrev-parseを使っていました。
おもにコミットのハッシュを表示してくれるコマンドでした。
さらにここで使用しているオプション --verifyは指定されたオブジェクトがGitのオブジェクトかどうかチェックしています。
what-does-git-rev-parse-do

でもなぜ refs/stash と思いませんか?
どこを指定しているのだろうと。
調べたら.git/refs/stash(ファイル)を指していました。
Gitの内側-Gitの参照 ← 詳しくはこちらでお願いします。
$tree .git/refs
.git/refs ├── heads │   ├── linux │   └── master ├── remotes │   └── origin │   ├── HEAD │   ├── linux │   └── master ├── stash └── tags
└── ver1.0
refs/stashにはpushされたときのHEADのコミットハッシュが書かれています。
さらにrefs/stashはstashが存在する時のみに作成されるファイルです。
不思議なことにstashには最新のstash時のみコミットハッシュしか保存されていませんでした。
他のstashの情報はどこにあるのだろうかと思い調べたのですが分からなかったです><
すいません。


リモートリポジトリとの差分

次はリモートとの差分です。
リモートとの差分といっても比較対象はgit fetchしてきたリポジトリの情報になります。
 custom_git_remote_status() {
    remote=${$(command git rev-parse --verify ${hook_com[branch]}@{upstream} --symbolic-full-name 2>/dev/null)/refs\/remotes\/}
    if  -n ${remote}  ; then
        ahead=$(command git rev-list ${hook_com[branch]}@{upstream}..HEAD 2>/dev/null | wc -l)
        behind=$(command git rev-list HEAD..${hook_com[branch]}@{upstream} 2>/dev/null | wc -l)

        if [ $ahead -eq 0 ] && [ $behind -gt 0 ]
        then
            echo "$ZSH_THEME_GIT_PROMPT_BEHIND_REMOTE"
        elif [ $ahead -gt 0 ] && [ $behind -eq 0 ]
        then
            echo "$ZSH_THEME_GIT_PROMPT_AHEAD_REMOTE"
        elif [ $ahead -gt 0 ] && [ $behind -gt 0 ]
        then
            echo "$ZSH_THEME_GIT_PROMPT_DIVERGED_REMOTE"
        fi
    fi
}
ここでも出ました。rev-parse --verify です。
${hook_com[branch]}にはブランチ名が入ってきます。
--symbolic-full-nameはrev-parseで出力するときにGitのオブジェクトのパスを渡します。

通常の場合・・コミットハッシュのみ
$git rev-parse --verify master
30c1615daa638a7442c2ab043c9ba7021ba81c3f
--symbolicの場合 ブランチ名(リモートの場合はリモート名も付く)
$git rev-parse --verify master --symbolic
master
--symbolic-full-nameの場合 (gitオブジェクトのパスが表示される)
$git rev-parse --verify master --symbolic-full-name
refs/heads/master
おおこれでローカルのgitのオブジェクトのパスが手に入りました。
でもほしいのはリモートです。そこで@{upstream}の出番です。git-rev-parse
@{upstream}は、指定されたブランチのupstreamのgitのオブジェクトのパスを取得してくれます。

$git rev-parse --verify master@{upstream} --symbolic-full-name
refs/remotes/origin/master
upstreamとは該当するブランチの本家のリポジトリ?みたいな感じで言い方が悪いかもしれません。
(リモートのブランチのどれかに該当します)definition-of-downstream-and-upstream

git branch -vv(vが二つ)で見ることが出来ます。下の実行結果ですとmasterブランチの
upstreamは「origin/master」となります。
how-do-i-get-git-to-show-me-which-branches-are-tracking-what

$git branch -vv
  linux  df0b61f remove tmp
* master 59a593a [origin/master: ahead 1] delete space

rev-listについて git-rev-list 
-- list commit object in reverse chronological order
新しい順にコミットハッシュを並べてくれます。
これによりリモートとのコミットの差分が存在するのかチェックしてくれます。
$git rev-list HEAD..refs/remotes/origin/master
471fff9b92650c5e4deaec584a3b6f62ad1543a6
あとはコミットハッシュの数をカウントすれば差分の数が分かります。

バックグラウンドのジョブ数確認

頑張ってコードを書いていました。
ZSH_THEME_BACKGROUND_JOB_PREFIX="%{$C%}"
ZSH_THEME_BACKGROUND_JOB_SUFFIX="%{$RE%}"

background_jobs() {
  jobs=$(jobs 2>/dev/null)
  if [ $jobs ];then
    echo "$ZSH_THEME_BACKGROUND_JOB_PREFIX"
    for i in $jobs; do
      echo $i
    done
    echo "$ZSH_THEME_BACKGROUND_JOB_SUFFIX"
  fi
}

でも zsh-prompt-checking-if-there-are-any-background-jobsを見て
「%j」で代用できることを知り開発をやめました。
バックグラウンドの種類まで表示しようとしましたが出来ませんでした。

完成形
通常
35

git 
48


色々いじった状態 ??←unstaged  [緑] ←インデックスの状態  [黄色]←作業ディレクトリの状態
05


感想
コードが汚い、きれいにする。バックグランドの種類までほしいので頑張る。
(周りにはそこまでしなくてもと言われたが)
カスタマイズはやっぱり面白い。Gitの知らないことが分かってよかった。
githubのコードです

VagrantのPackageでベースの仮想環境を使い回す

Vagrantでの仮想環境を作成する際の話です。
 
vagrant box add "追加したい仮想環境名" "取得先のURL"
vagrant init "追加したい仮想環境名"

これで構築できるのですが、毎回300MB以上のファイルのダウンロードを行うのは遅いので
取得したファイルを使い回したいと思います。
今回は、Centosを構築します。
vagrant box add default_centos https://s3.amazonaws.com/itmat-public/centos-6.3-chef-10.14.2.box
vagrant init defalt_centos
vagrant package
これだけでうまくいきます。
06

Vagrantfileと同じ階層にpackage.boxが作成されたのが確認できます。これでおkです。

次はパッケージ化したboxの利用です。
package.boxは「vagrant box add」の時に使用します。
27
追加されています。
これであとはいつも通りの流れで仮想環境と起動すればうまくいきます。
vagrant init test
vagrant up

詳細の設定などはしていません。あくまでパッケージ化を行っただけです。