主に備忘録

基本的には自分用の備忘録っぽいもの。

AnsibleでMySQLの環境を作成する(おまけ付き)

やりたいこと

Ansibleでこの辺を自動化する
- MySQLのインストール
- DBユーザの作成
- DBの作成
- 別のDBからデータを取得してリストア

実施環境

1. 各ツールの準備(インストール済みの場合不要)

(省略) この辺に書いてあります

step1. Homebrewをインストールする

step2. Homebrew Caskをインストールする

step3. VirtualBoxをインストールする

step4. Vagrantをインストールする

step5. Vagrant-vbguest pluginをインストールする

step6. Ansibleをインストールする

2. Vagrantfileの作成

(省略) この辺とかこの辺に書いてあります

ここまででこんな感じ

Vagrant.configure("2") do |config|

  config.vm.box = "puppetlabs/centos-6.6-64-nocm"
  config.vm.box_url = "https://atlas.hashicorp.com/puppetlabs/boxes/centos-6.6-64-nocm/versions/1.0.3/providers/virtualbox.box"

  config.ssh.insert_key = false
  config.hostmanager.enabled = true
  config.hostmanager.manage_host = true

  # create db server
  config.vm.define :"db" do |host|
    host.vm.hostname = "db"
    host.vm.network :private_network, ip: "192.168.34.21", netmask: "255.255.255.0"
    host.vm.network :private_network, ip: "192.168.33.21", virtualbox__intnet: "mv"
    # ansible
    host.vm.provision "ansible" do |ansible|
      ansible.playbook = "provisioning/dbservers.yml"
      ansible.inventory_path = "provisioning/hosts"
      ansible.limit = 'all'
    end
  end
end

3. Ansibleのタスクを作成

step1. ベストプラクティスを参考にディレクトリとファイルを作成する

roles/
   hosts
   dbservers.yml
   common/
      tasks/
         main.yml
   database/
      handlers/
         main.yml
      tasks/
         main.yml
         mysql.yml
         restore.yml
      vars/
         main.yml

4. AnsibleのPlaybookを記述する

step1. hosts

[dbservers]
127.0.0.1 ansible_ssh_private_key_file=.vagrant/machines/db/virtualbox/private_key ansible_ssh_user=vagrant

step2. dbservers.yml

---
- hosts: dbservers
  become: true
  roles:
    - common
    - database

step3. common/tasks/main.yml

---
- name: change timezone
  command: cp -p /usr/share/zoneinfo/Japan /etc/localtime
- name: remove all rules from iptables
  command: /sbin/iptables -F
- name: iptables stop
  service: name=iptables state=stopped
- name: iptables off
  command: /sbin/chkconfig iptables off

step4. database/handlers/main.yml

---
- name: enable mysql launch settings
  command: /sbin/chkconfig mysqld on

step5. database/tasks/main.yml

---
- include: mysql.yml
- include: restore.yml

step6. database/tasks/mysql.yml

---
- name: MySQL5.1(デフォルト)削除
  yum: name=mysql* state=absent
- name: install repository
  yum: name=http://dev.mysql.com/get/mysql-community-release-el6-5.noarch.rpm state=present
- name: MySQL5.6インストール
  yum: name={{ item }} state=present
  with_items:
    - mysql
    - mysql-devel
    - mysql-server
    - mysql-utilities
    - MySQL-python
  notify:
    - enable mysql launch settings
- name: start MySQL
  service: name=mysqld state=started enabled=yes
- name: データベース作成
  mysql_db: db={{ item }} state=present encoding=utf8
  with_items: "{{ dbnames }}"
- name: データベースのユーザ作成(権限も付与する)
  mysql_user: >
    name={{ dbuser }}
    password="{{ dbpass }}"
    host={{ item[0] }}
    priv={{ item[1] }}.*:ALL
    append_privs=yes
    state=present
  with_nested:
    - "{{ hosts }}"
    - "{{ dbnames }}"

step7. database/tasks/restore.yml

---
- name: ダンプファイル作成(データは別のDBから取ってくる)
  mysql_db: >
    login_host={{ remotehost }}
    login_user={{ dbuser }}
    login_password={{ dbpass }}
    name={{ item }}
    target=/tmp/{{ item }}.dump
    state=dump
  with_items: "{{ dbnames }}"
- name: リストア
  mysql_db: name={{ item }} target=/tmp/{{ item }}.dump state=import
  with_items: "{{ dbnames }}"
- name: ダンプファイル削除
  file: path=/tmp/{{ item }}.dump state=absent
  with_items: "{{ dbnames }}"

step7. database/vars/main.yml

---
dbuser: <DBユーザ名>
dbpass: <DBユーザのパスワード>
remotehost: <データ取得元のIP>
hosts:
  - "localhost"
  - "192.168.33.20"
dbnames:
  - <restoreするDB名>
  - <restoreするDB名>

5. 動作確認

step1. 仮想マシンを作成する

$ vagrant up

step2. 動作確認

DBに接続できればOK

6. おまけ1 --no-data(-d)を併用する

mysql_dbモジュールはdumpのオプションが指定できないようなのでshellで実行する
commandだと>が使えないので注意

- name: create no-data dump files
  shell: mysqldump -h {{ remotehost }} -u {{ dbuser }} -p{{ dbpass }} {{ item }} -d > /tmp/{{ item }}.dump
  with_items: "{{ nodatadbs }}"

リストアとかする時は「Jinja2 filters」のunionを使うと良い感じに書けます

- name: restore databases
  mysql_db: name={{ item }} target=/tmp/{{ item }}.dump state=import
  with_items: "{{ dbnames | union(nodatadbs) }}"

7. おまけ2 Viewを含むDBに対応する

対象のDBにViewが含まれている場合、DEFINERの設定によってはアクセスできなくなるので、リストア前に置換しておく(置換しても問題ない場合に限る)

- name: edit dump files
  replace: dest=/tmp/{{ item }}.dump regexp='<置換前>`@`%' replace={{ dbuser }}`@`localhost
  with_items: "{{ dbnames }}"

8. おまけ3 Windowsに対応する

WindowsにはAnsibleがインストールできないのでVM側で実行する

step1. $scriptを追記

$script = <<SCRIPT

if ! [ `which ansible` ]; then
    yum update -y
    yum install -y http://ftp.iij.ad.jp/pub/linux/fedora/epel/6/x86_64/epel-release-6-8.noarch.rpm
    sed -i -e "s/enabled *= *1/enabled=0/g" /etc/yum.repos.d/epel.repo
    yum install --enablerepo=epel -y ansible
fi

ansible-playbook -i /vagrant/provisioning/hosts /vagrant/provisioning/dbservers.yml

SCRIPT

step2. provisionを編集する

編集前

host.vm.provision "ansible" do |ansible|
  ansible.playbook = "provisioning/dbservers.yml"
  ansible.inventory_path = "provisioning/hosts"
  ansible.limit = 'all'
end

編集後

config.vm.provision "shell", inline: $script

mysql_dbモジュール便利ですね。

【Android】角丸ボタンにRipple Effectをつける

1. 角丸ボタン用のxmlを作る

drawable配下に角丸ボタン用のxmlを作成する。

<?xml version="1.0" encoding="utf-8"?>
<shape
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:shape="rectangle">
    <solid
        android:color="@color/colorWhite"/>
    <stroke
        android:width="@dimen/line_small"
        android:color="@color/colorGrey"/>
    <corners
        android:radius="@dimen/corner_radius_normal"/>
</shape>

2. Ripple Effect用の色を定義する

<?xml version="1.0" encoding="utf-8"?>
<resources>
    <color name="colorWhite.focused">#CCCCCC</color>
</resources>

3. Ripple Effect用のxmlを作成する

drawable配下にRipple Effect用のxmlを作成する。

<?xml version="1.0" encoding="utf-8"?>
<ripple
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:color="@color/colorWhite.focused">
    <item android:drawable="@drawable/button_bg_white"/>
</ripple>

4. ボタンを作成する

ボタンの背景に作成したRipple Effect用のxmlを設定する。

<Button
    android:background="@drawable/button_frame_white"
    />

【CakePHP】リダイレクト先を動的に設定する

動作環境が結構古くて2.5.6なので3系でもそのままいけるかは不明です。

  1. メルマガ等にコンテンツのリンクを貼る
  2. 未ログインの場合はログイン画面に飛ばす
  3. ログイン完了後にコンテンツに飛ばす

といった動きをしたい時なんかに使えます。

1. リダイレクトURL(パス)を保存するfunctionを用意

親クラス(AppControllerとか)に実装すると良さげです。

private function storeRedirectPath() {
  // このパスは保存しない
  $exclude = ['/login', '/aaa', '/bbb'];
  $currentPath = Router::url();
  if (!in_array($currentPath, $allow)) {
      $this->Auth->redirectUrl($currentPath);
  }
}

2. 1.のfunctionを呼び出す処理を追加

このへん

public function beforeFilter()
{
  $this->storeRedirectPath();
}

あるいは

public function beforeRender()
{
  $this->storeRedirectPath();
}

※この辺のfunctionは初期表示時以外にも呼ばれるので確認してからの方が良いです。

3. 保存したパスにリダイレクトする処理を追加

protected function redirectToStoredPath() {
  $this->redirect($this->Auth->redirectUrl());
}
public function index()
{
  // ログイン済みの場合はリダイレクト
  if ($this->Auth->login()) {
    $this->redirectToStoredPath();
  }
}

【PostgreSQL】1:Nのテーブルのカラムを配列で取得する

SQL一発で取れるのは良いですね。

SELECT
  id,
  column1,
  column2,
  array(SELECT column3 FROM table2 WHERE table2.table1_id = table1.id) as culumn3
FROM
  table1;

【PostgreSQL】特定のカラムに一括で同じ文字列操作をする小技

使う機会はあまりなさそうだけど、せっかくなのでメモ。
なんらかの理由で一時的にキーワードを追加したい時とかに使えそう。

  • 同じ文字列を追加する
UPDATE
  table1 AS t1
SET
  column1 = '追加する文字列 ' || t2.column1 FROM (SELECT id, column1 FROM table1) AS t2
WHERE
  t1.id = t2.id 
  • 追加した文字列を削除する
UPDATE
  table1 AS t1
SET
  column1 = t2.column1 FROM (SELECT id, REPLACE(column1, '追加する文字列 ', '') FROM table1) AS t2
WHERE
  t1.id = t2.id 

t2の抽出に条件を追加すれば、もっと細かい指定も可能。

StoryboardでTableViewの区切り線を左に詰める

Storyboardだけでできたのでメモ。

1. SeparatorのInsetsを変更する。

  1. Custom Insetsにする
    f:id:ita3y:20160131184504p:plain

  2. Leftを0にする
    f:id:ita3y:20160131184516p:plain

2. Layout Maginsを変更する

  1. Explicitにする
    f:id:ita3y:20160131184522p:plain

  2. Leftを0にする
    f:id:ita3y:20160131184527p:plain

変更前

f:id:ita3y:20160131185220p:plain

変更後

f:id:ita3y:20160131185336p:plain

xibを使って簡易ローディングを作成する

今回の成果物

こちらに置いてあります
negibouze/SimpleLoadingSample

1. LoadingView.xibを作成する

1. 背景を設定する

alphaで透過させるとsubviewも一緒に透過されてしまうので、opacityで透過させる。
f:id:ita3y:20151223194138p:plain

2. Activity Indicatorを配置する

中心に来るようにする。
f:id:ita3y:20151223194144p:plain

2. LoadingView.swiftを作成する

class LoadingView: UIView {
    
    @IBOutlet var contentView: UIView!
    @IBOutlet weak var indicator: UIActivityIndicatorView!

3. .xibと.swiftを紐づける

1. File's OwnerにLoadingView.swiftを設定する

f:id:ita3y:20151223194150p:plain

2. 各要素を紐づける

f:id:ita3y:20151223194156p:plain

4. LoadingViewを実装する

class LoadingView: UIView {

    private static let sharedInstance = LoadingView()
    private var isShow = false
    
    private override init(frame: CGRect) {
        super.init(frame: (UIApplication.sharedApplication().delegate?.window??.bounds)!)
        self.setup()
    }

    class func show(duration: Double = 0.5) {
        if sharedInstance.isShow {
            return
        }
        guard let win = UIApplication.sharedApplication().delegate?.window, let window = win else {
            return
        }
        sharedInstance.isShow = true
        sharedInstance.indicator.startAnimating()
        window.addSubview(sharedInstance)
        UIView.animateWithDuration(duration,
            animations: {
                sharedInstance.alpha = 1.0
            },
            completion: nil
        )
    }

    class func hide(duration: Double = 0.5) {
        if !sharedInstance.isShow {
            return
        }
        UIView.animateWithDuration(duration,
            animations: {
                sharedInstance.alpha = 0
            },
            completion: { b in
                sharedInstance.removeFromSuperview()
                sharedInstance.indicator.stopAnimating()
                sharedInstance.isShow = false
            }
        )
    }