2018年6月30日土曜日

fabric2 入門

概要

fabric は Python 製のデプロイツールです
簡単に言うとパラレルでシェルを実行することができるツールです
Ruby でいうところの Capistrano です
今回はインストールから簡単なサンプルを実行するところまでやってみました

環境

  • macOS 10.13.5
  • Python 3.6.5
  • fabric 2.1.3

インストール

  • pip3 install fabric

サンプル集

いろいろと実行してみます
サーバへのアクセスは ID/PW 認証を想定しています

とりあえず SSH してコマンド実行

  • vim first.py
from fabric import Connection

conn = Connection("root@172.28.128.3", connect_kwargs = { "password": "xxxxxxxx" })
result = conn.run('uname -s')
print(result)

いきなりポイントですが認証情報を fabric のコード上に記載する場合は connect_kwargs オプションを使います
fabric1 では env を使うらしいのですが、fabric2 では使えません
代わりに Connection のインスタンスを生成する際に引数で指定します
ここで渡した connect_kwargs の dict はそのまま paramiko.client.SSHClient.connect のオプションとして扱われます

また、ホスト情報とユーザ情報だけは connect_kwargs には含めず、Connection の第一引数で指定します
connect_kwargs 側で指定すると

ValueError: Refusing to be ambiguous: connect() kwarg 'hostname' was given both via regular arg and via connect_kwargs!

というエラーになります

ファイルをアップロードする

  • vim upload.py
from fabric import Connection

conn = Connection("root@172.28.128.3", connect_kwargs = { "password": "xxxxxxxx" })
result = conn.put("./first.py", remote="/tmp/first.py")
print(result)

これまたポイントですが remote 側はディレクトリを指定するだけではアウトです
IOError が発生します
ちゃんと remote 側で配置するファイル名まで指定しましょう

シェルスクリプトを実行する

  • vim shell.py
  • chmod 755 test.sh
from fabric import Connection

conn = Connection("root@172.28.128.3", connect_kwargs = { "password": "xxxxxxxx" })
result = conn.put("./test.sh", remote="/tmp/test.sh", preserve_mode = True)
result = conn.run("/tmp/test.sh")
print(result)

事前に権限を変更しておきます
そして preserve_mode = True を指定することでローカルの権限情報を引き継いでリモートにコピーします
False にすると実行権限がなくなり実行できません
また、fabric2 では内部的に SFTP を使っているようです
preserve_mode = True でアップロード後に False にして再度実行しても権限はなくならないので、ターゲットサーバ上のファイルを一度削除してから再度アップロードしてください

テンプレートファイルアップロード

どうやら upload_template は fabric2 では使えないようです

エラーハンドリング 

  • vim error.py
from fabric import Connection
from invoke import UnexpectedExit

conn = Connection("root@172.28.128.3", connect_kwargs = { "password": "xxxxxxxx" })
try: 
    ret = conn.run("hoge")
    print(ret)
except UnexpectedExit as e:
    print(e)

UnexpectedExit を except する必要があります
fabric2 はステータスが 0 以外の場合エラーを発生させます
それを回避するためには上記のような対応をする必要があるようです
単純にコマンドでエラーになった場合は特に不要な処理だと思います

最後に

fabric2 に入門してみました
Web 上にはまだまだ fabric 1 系の情報が多いようです
機能的にも fabric1 のほうが豊富な印象です

API が全く互換していないので 1 系のドキュメントは役に立ちません
Python3 で動かしたい場合は fabric2 を使うしかないですが、そうでない場合にはまだ移行は微妙かもしれないです

2 系へのアップグレードに関してはこちらが参考になると思います

参考サイト

2018年6月29日金曜日

素の Python3 で jinja2 を使ってみる

概要

jinja2 はテンプレートエンジンです
Ruby でいうところの erb です
今回は Python3 で jinja2 を使ってみました

環境

  • macOS 10.13.5
  • Python 3.6.5
  • jinja2 2.10

サンプルコード

Getting Started

  • vim test_jinja2.py
from jinja2 import Template
template = Template('Hello {{ name }}!')
ret = template.render(name='John Doe')
print(ret)

ファイルから読み込み

  • python3 test_jinja2.py
from jinja2 import Template, Environment, FileSystemLoader

env = Environment(loader=FileSystemLoader('.'))
template = env.get_template('test.tpl')
data = {'name': 'hawksnowlog', 'items': ['blog', 'twitter', 'youtube']}
disp_text = template.render(data)
print(disp_text)
  • test.tpl
String
{{ name }}
List
{% for item in items -%}
* {{ item }}
{% endfor %}

for 文の最後のマイナスは trim でこれがある場合は自動で改行しません

タグをそのまま表示

  • vim raw_jinja2.py
from jinja2 import Template, Environment, FileSystemLoader

env = Environment(loader=FileSystemLoader('.'))
template = env.get_template('test2.tpl')
disp_text = template.render()
print(disp_text)
  • vim test2.tpl
{% raw %}
<ul>
   {% for item in seq %}
       <li>{{ item }}</li>
   {% endfor %}
</ul>
{% endraw %}

raw セクションを使います

最後に

jinja2 を Python3 から使ってみました
簡単な機能しか紹介してませんが他にも機能 (block, child template, macro) はたくさんあります

かなり強力なテンプレートエンジンなのでこれ 1 つでほとんどまかなえると思います

参考サイト

2018年6月28日木曜日

Ansible から docker-compose を使ってみた

概要

docker_service というモジュールを使うことで docker-compose.yml を制御することができます

環境

クライアント

  • macOS 10.13.5
  • Ansible 2.5.5

ターゲットホスト

  • Ubuntu 16.04
  • docker 1.13.1, build 092cba3

レシピ作成

必要なファイルを作成していきます

site.yml

実行メインです

- hosts: all
  roles:
    - role: git
    - role: docker

production

インベントリファイルです

[all]
172.28.128.3

[all:vars]
ansible_user = root
ansible_ssh_pass = xxxxxxxxx
ansible_connection = paramiko

git

docker-compose をターゲットのサーバに配置します
今回は Github で公開されている docker-compose を使うので clone するレシピを作成します
ローカルからコピーなどしても OK です

  • mkdir -p roles/git/tasks
  • vim roles/git/tasks/main.yml
- include: zabbix.yml
  • touch roles/git/tasks/zabbix.yml
- include: zabbix.ymlyoshi1:try_zabbix_docker kakakikikeke$ cat roles/git/tasks/zabbix.yml 
- name: Clone
  git:
    repo: 'https://github.com/zabbix/zabbix-docker.git'
    dest: /root/zabbix-docker

zabbix

今回は Zabbix の docker-compose を使ってみます

  • mkdir -p roles/git/tasks
  • vim roles/docker/tasks/main.yml
- include: zabbix.yml
  • vim roles/docker/tasks/zabbix.yml
- name: Run zabbix
  docker_service:
    project_src: /root/zabbix-docker
    files:
      - docker-compose_v2_ubuntu_mysql_latest.yaml
    state: present

git clone したディレクトリを project_src で指定します
もし docker-compose.yaml or docker-compose.yml という名前のファイルがある場合は自動的にそれを読み込んで up してくれます
しかしファイル名が違う場合は明示的に指定する必要があります
そのために files パラメータを使って docker-compose の YAML ファイルを指定する必要があります

state は present と absent がありそれぞれ up と down に対応しています

動作確認

  • ansible-playbook -i production site.yml

で実行できます
成功するとターゲットのホストに 9 つのコンテナが起動しているのが確認できると思います

  • docker ps
CONTAINER ID        IMAGE                                              COMMAND                  CREATED              STATUS    
         PORTS                                         NAMES
452c148e3d8b        zabbix/zabbix-proxy-mysql:ubuntu-3.4-latest        "docker-entrypoint.sh"   About a minute ago   Up About a
minute   0.0.0.0:10071->10051/tcp                      zabbixdocker_zabbix-proxy-mysql_1                                       
29646c521ab7        zabbix/zabbix-proxy-sqlite3:ubuntu-3.4-latest      "docker-entrypoint.sh"   About a minute ago   Up About a
minute   0.0.0.0:10061->10051/tcp                      zabbixdocker_zabbix-proxy-sqlite3_1                                     
b020130836c3        zabbix/zabbix-web-apache-mysql:ubuntu-3.4-latest   "docker-entrypoint.sh"   About a minute ago   Up About a
minute   0.0.0.0:80->80/tcp, 0.0.0.0:443->443/tcp      zabbixdocker_zabbix-web-apache-mysql_1                                  
daedb397f768        zabbix/zabbix-agent:ubuntu-3.4-latest              "docker-entrypoint.sh"   About a minute ago   Up About a
minute   0.0.0.0:10050->10050/tcp                      zabbixdocker_zabbix-agent_1                                             
c610d76ee1ea        zabbix/zabbix-web-nginx-mysql:ubuntu-3.4-latest    "docker-entrypoint.sh"   About a minute ago   Up About a
minute   0.0.0.0:8081->80/tcp, 0.0.0.0:8443->443/tcp   zabbixdocker_zabbix-web-nginx-mysql_1                                   
585bb7622693        zabbix/zabbix-server-mysql:ubuntu-3.4-latest       "docker-entrypoint.sh"   About a minute ago   Up About a
minute   0.0.0.0:10051->10051/tcp                      zabbixdocker_zabbix-server_1                                            
f856fba5aac2        mysql:5.7                                          "docker-entrypoint..."   About a minute ago   Up About a
minute   3306/tcp                                      zabbixdocker_mysql-server_1                                             
89a7a3bdc46c        zabbix/zabbix-snmptraps:ubuntu-3.4-latest          "/usr/bin/supervis..."   About a minute ago   Up About a
minute   0.0.0.0:162->162/udp                          zabbixdocker_zabbix-snmptraps_1                                         
8b5dafc3a00a        zabbix/zabbix-java-gateway:ubuntu-3.4-latest       "docker-entrypoint.sh"   About a minute ago   Up About a
minute   0.0.0.0:10052->10052/tcp                      zabbixdocker_zabbix-java-gateway_1

http://172.28.128.3 にアクセスすると Zabbix の画面が表示されます
Admin/zabbix でダッシュボードにログインすることができます

最後に

Ansible から docker-compose を使ってみました
今回は既存の docker-compose ファイルを使用しました
docker_service モジュールはレシピの YAML ファイルに直接 docker-compose の内容を記載することもできるようです

docker_service はまだ v3 には対応しておらず v2 形式の YAML ファイルを使用する必要があります

Ansible 経由で定義することで変数機能などを使うことができるのがメリットかなと思います

zabbix_server.conf など主要な設定ファイルはホストでマウントしている感じではないのでコンテンからコピーするかコンテナのファイルを直接編集する必要があります

参考サイト

2018年6月27日水曜日

Ansible から docker を制御してみた

概要

docker_container というモジュールがありこれを使うと docker の制御することができます
今回はコンテナの起動など行ってみました

環境

クライアント

  • macOS 10.13.5
  • Ansible 2.5.5

ターゲットホスト

  • Ubuntu 16.04
  • docker 1.13.1, build 092cba3

レシピ作成

必要なファイルを作成していきます

site.yml

メインのレシピです

- hosts: all
  roles:
    - role: docker

production

インベントリファイルです

[all]
172.28.128.3

[all:vars]
ansible_user = root
ansible_ssh_pass = xxxxxx
ansible_connection = paramiko

対象のホストは Vagrant で作成した Ubuntu16 ですでに docker を apt でインストール済みです

nginx.yml

メインとなるレシピを作成します

  • mkdir -p roles/docker/tasks/
  • vim roles/docker/tasks/main.yml
- include: zabbix.yml
  • vim roles/docker/tasks/nginx/yml
- name: Run nginx
  docker_container:
    name: web
    image: nginx:latest
    ports:
      - "81:80"

おそらく何を設定しているか予想できると思います
nginx コンテナを 80 番ポートで起動して 81 番ポートで EXPOSE しています
またコンテナの名前を web にしています

name は必須パラメータになっています

実行

  • ansible-playbook -i production site.yml

パスワード等はすべてインベントリファイルに記載したので上記で実行できるはずです

動作確認としては curl でも叩いて見ましょう

  • curl 172.28.128.3:81

これで nginx のデフォルト画面が返ってくれば OK です

それ以外の動作

コンテナを停止する

- name: Stop
  docker_container:
    name: web
    state: stopped

コンテナを削除する

- name: Remove
  docker_container:
    name: web
    state: absent

最後に

Ansible から docker_container モジュールを使ってコンテナ制御をしてみました
当然ですがターゲットとなるホストには docker がインストールされている必要があります

Ansible の 2.4 から docker というモジュールが DEPRECATED になりました
代わりに docker_containerdocker_image というモジュールを使うようになったのでご注意ください
今回はコンテナ操作だけだったので docker_container を使いました

最近だとコンテナスケジューラが充実しているので運用はそっちを使えばいいと思います
コンテナホストで初回のコンテナ起動などにこれを使う感じかなと思います

ドキュメントは以下で紹介している公式のドキュメントが一番参考になります

参考サイト

2018年6月26日火曜日

LPIC 101 に合格しました

概要

前回 202 に合格しました
今回はなぜかその下の 101 を受験しました

感想

受験の流れや方法は過去の記事を参考にしてください

まず勉強方法は参考書を使いました

  • Linux教科書 LPICレベル1 スピードマスター問題集 Version4.0対応

まだ 101 はバージョンが 4.0 なのでこれを使いました
もしこの記事を見ている方がこれから受けようという場合に 4.5 になっているのであれば 4.5 に対応した参考書を購入するようにしてください

自分はこの参考書だけを使いました
というかこの参考書以外勉強する必要はありません
というのも LPIC の 101 はなぜか Web 上に無料の過去問が出回っています
それをやっても良いのですが、テストでは全く出ませんでした
テストに出たのは紹介した参考書に出てくる問題がほとんどでした
感覚としては 90% くらいは参考書にあった問題と解説の中から出ていたんじゃないかと思います
最悪この参考書を丸暗記しても合格できるレベルです
101 はそこまで難しい問題が出ないというのもあるかなと思います

開始 15 分ほどですべて解答できました
自信もあったので見直しせず合格判定したところ 740/800 で合格でした

lpic101.jpg

相変わらずどこが間違えたかどうかは教えてくれないようです

最後に

無事 101 も取得できたので次は 102 でも取得しようと思います
4.5 になる前であれば参考書も今のものを使えるので早めに取得しようかなと思います

2018年6月25日月曜日

ansible-galaxy を使って Ubuntu に docker をインストールしてみた

概要

ansible galaxy は簡単に言えば自分が作成した playbook (ロール) を公開するための機能です
すでに公開されている playbook がある場合簡単にそれを使うことができます
今回は docker のインストールを ansible galaxy で公開されているロールを使ってやってみました

環境

  • macOS 10.13.5
  • Ansible 2.5.5

ansible-galaxy のインストール

  • pip install ansible

でインストールできます
Mac であれば Homebrew でもインストールできます

ロールのインストール

ansible galaxy で公開されている docker をインストールするためのロールは結構あります
今回はこれを使います

  • ansible-galaxy install angstwad.docker_ubuntu

で OK です
インストールは ~/.ansible/roles/angstwad.docker_ubuntu にされます

Tips

カレントディレクトリに .netrc ファイルがあるとインストールに失敗することがあるようです
一旦 rename などをして再度 install コマンドを実行してみてください

 [WARNING]: - angstwad.docker_ubuntu was NOT installed successfully: Failed to get data from the API server                    
(https://galaxy.ansible.com/api/): bad follower token 'method' (/Users/hawksnowlog/.netrc, line 4)

レシピ作成

  • vim site.yml
- hosts: all
  roles:
    - role: angstwad.docker_ubuntu

今回は特にカスタマイズせずデフォルトのロールを使っています
カスタマイズできる変数の一覧はこちらで定義されているので必要に応じて site.yml 内で上書きしてください

  • vim production
[worker]
192.168.100.10

[worker:vars]
ansible_user=root
ansible_connection=paramiko
ansible_ssh_pass=pass
ansible_ssh_private_key_file=./ssh_secret_key.pem

今回は SSH の鍵認証を使っています
認証情報は Inventory ファイルにすべて記載しました

また ansible-connection=paramiko にしないと you must install the sshpass program のエラーになるので明示的に指定します

ファイルはこれだけです
あとは実行してみましょう

実行

  • ansible-playbook -i production site.yml

で OK です
docker と docker-compose のインストールが始まります
ok=23, changed=9 で終了しました

インストールされていたバージョンはかなり最新のものがインストールされていました

root@ubuntu:~# docker -v
Docker version 18.03.1-ce, build 9ee9f40
root@ubuntu:~# docker-compose -v
docker-compose version 1.21.2, build a133471

冪等性を確認するために再度流してみましたが特にエラーになることはありませんでした

最後に

ansible-galaxy を使って公開済みのロールを使ってみました
自分で作り直す必要がないので便利です
が、公開されているロールが何をしているかは不明なので詳細はコードを読むしかありません

今回のロールはかなり簡単なものだったので良いのですが複雑なものになると読むのに一苦労なので自分作ったほうが良いケースもあるかもしれません

参考サイト

2018年6月24日日曜日

Python3 から docker を制御する

概要

試してみたのでサンプルを紹介します

環境

  • macOS 10.13.5
  • Python 3.6.5
  • docker-py 3.4.0
  • docker 18.03.1-ce

run する

import docker

client = docker.from_env()
res = client.containers.run("alpine", "echo hello world")
print(res)

バックグラウンド実行 (-d)

import docker

client = docker.from_env()
res = client.containers.run("nginx", detach=True)
print(res)

ポートを expose する (-p)

import docker

client = docker.from_env()
res = client.containers.run("nginx", detach=True, ports={'80/tcp': ('0.0.0.0', 81)})
print(res)

('0.0.0.0', 81) がホスト側のバインドアドレスとポートになります

環境変数を設定 (-e)

import docker

client = docker.from_env()
res = client.containers.run("alpine", environment=["NAME=hawksnowlog"], command="env")
print(res)

同時に削除 (--rm)

import docker

client = docker.from_env()
res = client.containers.run("alpine", environment=["NAME=hawksnowlog"], command="env", remove=True)
print(res)

コンテナの一覧を取得 (ps -a)

import docker

client = docker.from_env()
res = client.containers.list(all=True)
print(res)

ホストを指定する

import docker

docker.DockerClient(base_url='tcp://172.28.128.3:2375')
res = client.containers.list(all=True)
print(res)

もしくは docker.from_env() を使ってプログラムを実行する際に DOCKER_HOST 環境変数で指定する

最後に

Python3 から docker を操作していみました
かなり簡単に使えます

Swarm, Service あたりはカバーしているようです
Stack Deploy はまだのようです
https://github.com/docker/docker-py/issues/1173

参考サイト

2018年6月23日土曜日

ansible-playbook を Python からコールしてみた

概要

よくあるケースとしてはダイナミックにサーバをプロビジョニングしたい場合かなと思います
簡単な playbook を作成した後でプログラマブルに Ansible を実行してみます

環境

  • macOS 10.13.5
  • Python 2.7.15
  • Ansible 2.5.5

事前準備

今回は Mac で行っています
pip からインストールしました

  • pip install ansible

Homebrew でもインストールできるのでそれでも問題ないです

簡単な playbook の作成

非常に簡単なやつを作成します
ターゲットのサーバに対してファイルを touch するだけです
全体は以下の通りです

  • tree -a
.
├── production
├── roles
│   └── file
│       └── tasks
│           ├── main.yml
│           └── new.yml
└── site.yml
  • vim production
[ubuntu]
172.28.128.3

この Ubuntu サーバは vagrant を使ってローカルに立てています
サーバは何でも OK なので適当に用意してください

  • vim roles/file/tasks/main.yml
- include: new.yml
  • vim roles/file/tasks/new.yml
- file:
    path: /tmp/hoge
    state: touch

main.yml は参照するだけで本体は new.yml を作成してそっちに書いています
Ansible 側の内容はこれだけです

実行は

  • ansible-playbook -k -i production site.yml

です
-k オプションは SSH のパスワードを対話的に指定するためのオプションです

これで問題なく動作すれば playbook の準備は OK です

Tips

少し自分がはまった点を紹介します

fatal: [172.28.128.3]: FAILED! => {"changed": false, "module_stderr": "", "module_stdout": "/bin/sh: 1: /usr/bin/python: n
ot found\r\n", "msg": "MODULE FAILURE", "rc": 127}

で実行できない状態がありました
ターゲットのマシンである Ubuntu に Python をインストールしてあげることで解決しました
Vagrant の xenial を使っていると Python がデフォルトでインストールされていないようです
Ansible は基本的に 2.7 以上の Python が /usr/bin/python にインストールされている想定だそうです
https://docs.ansible.com/ansible/2.4/faq.html

一応 Python なしでも実行する方法はあるらしいのですが基本はいるようです

Ansible を Python からキックするスクリプトの作成

ここからが本番です
先ほど作成した playbook と同じことを Python から実行してみます
結構長いのであとで詳細を説明します
改行部分がまとまりとなっています

  • vim site.py
import json
import shutil
from collections import namedtuple
from ansible.parsing.dataloader import DataLoader
from ansible.vars.manager import VariableManager
from ansible.inventory.manager import InventoryManager
from ansible.playbook.play import Play
from ansible.executor.task_queue_manager import TaskQueueManager
from ansible.plugins.callback import CallbackBase
import ansible.constants as C

class ResultCallback(CallbackBase):
    def v2_runner_on_ok(self, result, **kwargs):
        host = result._host
        print(json.dumps({host.name: result._result}, indent=4))

    def v2_runner_on_failed(self, result, *args, **kwargs):
        host = result._host
        print(json.dumps({host.name: result._result}, indent=4))

    def v2_runner_on_unreachable(self, result):
        host = result._host
        print(json.dumps({host.name: result._result}, indent=4))

Options = namedtuple('Options', ['connection', 'module_path', 'forks', 'become', 'become_method', 'become_user', 'check', 'diff', 'remote_user'])
options = Options(connection='paramiko', module_path=['/usr/share/ansible'], forks=10, become=None, become_method=None, become_user=None, check=False, diff=False, remote_user='root')

loader = DataLoader()
results_callback = ResultCallback()
host_list = ['./production']
sources = ','.join(host_list)
inventory = InventoryManager(loader=loader, sources=host_list)
variable_manager = VariableManager(loader=loader, inventory=inventory)

play_source =  dict(
    name = "Ansible Play",
    hosts = ['ubuntu'],
    # gather_facts = 'yes',
    gather_facts = 'no',
    tasks = [
        dict(action=dict(module='file', args=dict(path='/tmp/hoge', state='touch')))
    ]
)
play = Play().load(play_source, variable_manager=variable_manager, loader=loader)

tqm = None
passwords = dict()
try:
    tqm = TaskQueueManager(
              inventory=inventory,
              variable_manager=variable_manager,
              loader=loader,
              options=options,
              passwords=passwords,
              stdout_callback=results_callback,
          )
    result = tqm.run(play)
finally:
    if tqm is not None:
        tqm.cleanup()
    shutil.rmtree(C.DEFAULT_LOCAL_TMP, True)

まず全体的な流れとして

  • 必要なライブラリの読み込み -> コールバック処理の定義 -> オプションの設定 -> インベントリ情報の生成 -> playbook の定義 -> 実行

という感じになります
それぞれ順を追って説明します

詳細説明

まず import 系ですがこれはほぼすべて必須かなと思います

import json
import shutil
from collections import namedtuple
from ansible.parsing.dataloader import DataLoader
from ansible.vars.manager import VariableManager
from ansible.inventory.manager import InventoryManager
from ansible.playbook.play import Play
from ansible.executor.task_queue_manager import TaskQueueManager
from ansible.plugins.callback import CallbackBase
import ansible.constants as C

各流れの中で使用するものになります
次にコールバック用のクラスを先に作成します

class ResultCallback(CallbackBase):
    def v2_runner_on_ok(self, result, **kwargs):
        host = result._host
        print(json.dumps({host.name: result._result}, indent=4))

    def v2_runner_on_failed(self, result, *args, **kwargs):
        host = result._host
        print(json.dumps({host.name: result._result}, indent=4))

    def v2_runner_on_unreachable(self, result):
        host = result._host
        print(json.dumps({host.name: result._result}, indent=4))

results_callback = ResultCallback()

コールバッククラスは何に使うかというと単純に playbook が終了した際に何をするか定義することができます
今回は「成功」「失敗」「アクセスできない」の 3 つのイベントをハンドリングして内容を表示しているだけです
ここで定義したコールバッククラスはあとで使用します

次にオプションの設定です
これは CLI を実行する際のオプションと同じですが Python からコールする際には必須のオプションがあります

Options = namedtuple('Options', ['connection', 'module_path', 'forks', 'become', 'become_method', 'become_user', 'check', 'diff', 'remote_user'])
options = Options(connection='paramiko', module_path=['/usr/share/ansible'], forks=10, become=None, become_method=None, become_user=None, check=False, diff=False, remote_user='root')

最後の remote_user だけ追加しました
それ以外は公式で紹介していた部分になるので必須かなと思います
例えば ansible-playbook コマンドでオプションを指定する必要がある場合はここで追加します
今回の場合であれば -k-i ですがこれらは InventroyManager で定義することになります (ややこしい)

次に InventoryManager になります

loader = DataLoader()
host_list = ['./production']
sources = ','.join(host_list)
inventory = InventoryManager(loader=loader, sources=host_list)
variable_manager = VariableManager(loader=loader, inventory=inventory)

ポイントは定義したインベントリファイル (production) を読み込んでいる部分です
実はファイルを使わないでも直接ここでインベントリ情報を定義することができます
が実はファイルを使ったほうが理由があります
それは SSH 時のパスワードを定義するためです
先ほどの production から実は以下のように変更しています

  • vim production
[ubuntu]
172.28.128.3

[ubuntu:vars]
ansible_ssh_user='root'
ansible_ssh_pass='xxxxxxxxxx'

Python から実行する際に -k を使って対話的に実行することはできません
なので、パスワード情報を事前にインベントリファイルに定義しておく必要があります
このパスワード情報を InventoryManager に食わせる方法がファイルからしかやり方がわからなかったため、既存のインベントリファイルを使うようにしています

次に playbook の定義です

play_source =  dict(
    name = "Ansible Play",
    hosts = ['ubuntu'],
    # gather_facts = 'yes',
    gather_facts = 'no',
    tasks = [
        dict(action=dict(module='file', args=dict(path='/tmp/hoge', state='touch')))
    ]
)
play = Play().load(play_source, variable_manager=variable_manager, loader=loader)

ポイントは hosts と tasks になります
hosts ではインベントリファイルに書かれているホスト情報のうちどれを実行するかを配列で定義できます
今回は 1 台だけなのでインベントリファイルに書かれているホストを 1 台定義しています
tasks ですが、ここにメインとなるプロビジョニング処理を定義する必要があります
tasks は先ほどのインベントリファイルとは違いファイルを流用することができません (今回の方法だとないだけで一応ファイルから実行する方法もあるっぽいです PlaybookExecutor ?)
なので、ここで同じ内容を定義し直す必要があります
今回であれば file モジュールを使って新規でファイルを touch しているだけなのでそこまでコードにするのは難しくありません
あとは Play().load() でこれまでに定義した情報を食わせれば playbook が出来上がります

最後に実行部分です

tqm = None
passwords = dict()
try:
    tqm = TaskQueueManager(
              inventory=inventory,
              variable_manager=variable_manager,
              loader=loader,
              options=options,
              passwords=passwords,
              stdout_callback=results_callback,
          )
    result = tqm.run(play)
finally:
    if tqm is not None:
        tqm.cleanup()
    shutil.rmtree(C.DEFAULT_LOCAL_TMP, True)

実行には TaskQueueManager を使います
これを使うことで実行結果をコールバックとして設定することができます
基本は定義してものを食わせていけば OK です
あとは run() するだけです

動作確認

  • python site.py

と実行すれば OK です
結果は以下のような感じで JSON で表示されます

{
    "172.28.128.3": {
        "_ansible_parsed": true, 
        "group": "root", 
        "uid": 0, 
        "dest": "/tmp/hoge", 
        "changed": true, 
        "state": "file", 
        "gid": 0, 
        "mode": "0644", 
        "invocation": {
            "module_args": {
                "directory_mode": null, 
                "force": false, 
                "remote_src": null, 
                "path": "/tmp/hoge", 
                "owner": null, 
                "follow": true, 
                "group": null, 
                "unsafe_writes": null, 
                "state": "touch", 
                "content": null, 
                "serole": null, 
                "diff_peek": null, 
                "setype": null, 
                "selevel": null, 
                "original_basename": null, 
                "regexp": null, 
                "validate": null, 
                "src": null, 
                "seuser": null, 
                "recurse": false, 
                "delimiter": null, 
                "mode": null, 
                "attributes": null, 
                "backup": null
            }
        }, 
        "owner": "root", 
        "diff": {
            "after": {
                "path": "/tmp/hoge", 
                "state": "touch"
            }, 
            "before": {
                "path": "/tmp/hoge", 
                "state": "absent"
            }
        }, 
        "size": 0, 
        "_ansible_no_log": false
    }
}

ターゲットのサーバに入ってファイルができているかも確認すると良いかもしれません
一応これで Python から同じ内容の playbook を実行することはできました

最後に

ansible-playbook を Python から実行してみました
正直辛い感じを覚えました
今回のやり方は公式に書いてあるサンプルを元にしているので、おそらく王道だと思います

一番辛いのは情報が少ないことです
タスクの定義の仕方などは各モジュールごとにいろいろと違うと思いますが、それをコードに落とす方法は Try&Error を繰り返すしかないかと思います
これをやりたいケースとしては既存の playbook をプログラミングから実行したい場合が一番多いかなと思います
その場合に今回の方法だと既存の playbook が使い回せないのが辛い点かなと思います

playbook を作る際にはじめからこの方法を取るのであれば何とかなるかもしれません
ただ今回紹介した構成はかなりミニマムな構成なのでここから group_vars やタスクの templates などが絡んでくると思うので、その場合にどうするかも調査しないとダメだと思います

他にやり方がないかもう少し見てみようと思います

参考サイト

2018年6月22日金曜日

Podcast のアートワークを変更する手順

概要

困っている人が多そうなのでメモがてら記事にしました

環境

  • macOS 10.13.5

手順

  1. 新しいファイル名でアートワークファイルを作成する (new.png)
    • 1400x1400 が最小で 3000x3000 が最長
  2. フィードの情報を更新する itunes:image で指定するファイルを 1 で作成した新しいファイルにする
  3. 古いアートワークファイルを削除する (大事、古いファイルをサイトで配信し続けるとそれを参照してしまうっぽい)
  4. Podcast Connect でフィードの更新リクエストをする

という感じです
3 の古いファイルを配信しないのがポイントかなと思います

この方法で iTunes および iOS の Podcast アプリでは変更するのを確認できたのですが Android の Podcast アプリ (Castbox など) では変更が確認できませんでした
アプリの作りがわからないと何とも言えないですがどこかでキャッシュしているのだと思います

2018年6月21日木曜日

favicon を作成するなら「Favicon & App Icon Generator」がおすすめ

概要

最近は iOS や Android、各ブラウザごとにいろいろなタイプの favicon を作成しないといけません
そんなときに便利なのが Favicon & App Icon Generator です
ファイル 1 つですべてのタイプの favicon を作成してくれます

環境

  • macOS 10.13.5
  • Favicon & App Icon Generator (2018/06/20 時点)

使い方

まずサイトにアクセスします
https://www.favicon-generator.org/

そして作成したい favicon の元画像を選択し「Create Favicon」を選択します
favicon_generator1.png

少し待つと favicon が詰め合わせされた zip ファイルが作成されるのでリンクをクリックしてダウンロードします
favicon_generator2.png

あとは含まれているファイルをドキュメントルートにコピーして、その下に表示される link タグやら meta タグの情報を自分のサイトのヘッダ部分にコピペすれば OK です

注意点

注意点としてはドキュメントルートではなくサブディレクトリを掘ってそこに favicon ファイル一式を配置したい場合などはちゃんとタグの情報も変更するようにしてください
また manifest.json と browserconfig.xml 内でも favicon 画像へのパスの定義があります
なので、そちらも必ず変更するようにしてください

2018年6月20日水曜日

MacOS High Sierra に GIMP をインストールしてみた

概要

タイトルの通りです
最新バージョンはインストール不可なので、古いバージョンをインストールします

環境

  • macOS 10.13.5
  • GIMP 2.8.22

インストール方法

  • brew cask install gimp

これでインストールするのが一番かなと思います
公式に言っても古いバージョンのページに行かないといけないので、brew を使うことで動くバージョンのものがインストールされます

初回起動時にいろいろと検索しますが 10 分ほど待っていれば無事起動しました
バージョンは最新ではないですが無料なので我慢しましょう

2018年6月19日火曜日

pipenv を使おう

概要

pipenv は ruby で言うところの bundler です
Python には virtualenv というサンドボックスツールがありますが、それよりも簡単に使える印象があります
今回はインストール方法から簡単な使い方を紹介します

環境

  • macOS 10.13.5
  • docker 18.03.1-ce
  • conductor 1.0.0
  • Python 3.6.5

インストール

  • brew install pipenv

Mac を使っている場合は上記で OK です
pip がすでに使えるのであればそれでもインストールできます

  • pip3 install pipenv

pipenv コマンドが使えるようになっていれば OK です

使ってみる

とりあえず使ってみましょう
素直に公式のチュートリアルを進めます

初期化

  • pipenv install requests

これで requests ライブラリをインストールすることができます
かつ Pipfile が作成されます、いわゆる Gemfile です
中身は以下のようになっていました

  • cat Pipfile
[[source]]
url = "https://pypi.org/simple"
verify_ssl = true
name = "pypi"

[packages]
requests = "*"

[dev-packages]

[requires]
python_version = "3.6"

先ほどインストールした request の定義もあります

インストールしたライブラリを使う

  • vim main.py
import requests

response = requests.get('https://httpbin.org/ip')

print('Your IP is {0}'.format(response.json()['origin']))

実行する際は pipenv run を使います
いわゆる bundle exec です

  • pipenv run python3 main.py

これで pipenv でインストールしたライブラリを使うことができます

別のライブラリを追加してみる

Pipfile を編集してライブラリを追加してみます
以下のように packages ディレクティブの部分に追記しましょう

[packages]
requests = "*"
twitter = "*"

これで

  • pipenv install

すると twitter ライブラリがインストールされます

その他

  • bundle console 的な Repl

pipenv shell を実行した後 python3 を実行して Repl を使えばライブラリを使える状態で Repl が起動します

  • ライブラリがインストールされているディレクトリを確認する方法

内部的には virtualenv が作られているようです
そのパスを確認する方法は以下のコマンドで可能です

  • pipenv --venv

ここの lib/python3.6/site-packages あたりにインストールされたライブラリがあります

最後に

pipenv を使って Python のサンドボックス環境を作成してみました
Pipfile を使っていろいろできるのが個人的には好きです

これ以外にもいろいろな機能があるようです
http://pipenv-ja.readthedocs.io/ja/translate-ja/advanced.html

デプロイや脆弱性チェック、エディタ連携などこれだけで開発に必要なツールがすべて揃ってしまう感じです

参考サイト

2018年6月18日月曜日

conductor のタスクの timeoutSeconds と responseTimeoutSeconds の挙動を確認してみた

概要

タスクにはタイムアウト機能が備わっているようで一定の時間タスクが完了にならない場合には自動的にタイムアウトのステータスにしてくれるようです
今回はその機能を試してみました
で、結果的にはデフォルトでは動作せる自分でコードを書き換えてあげることでタイムアウト機能が使えるようになりました
その辺りの対処方法を紹介します

環境

  • macOS 10.13.5
  • docker 18.03.1-ce
  • conductor 1.0.0
  • Python 3.6.5

事前準備

conductor サーバの準備および Python SDK が使えるようにしておいてください

timeoutSeconds を 60 に設定

公式 を見ると timeoutSeconds は指定の時間の間ステータスが IN_PROGRESS であった場合に TIMED_OUT のステータスに変更してくれる機能です

まずは timeoutSeconds を 60 に設定したタスクおよびそれを使うワークフローを登録します
また、作成したワークフローを開始するスクリプトと開始したワークフローをポーリングするスクリプトも作成します

  • タスク (sample1_task.json)
{
  "name": "Sample_1",
  "retryCount": 0,
  "timeoutSeconds": 60,
  "timeoutPolicy": "TIME_OUT_WF",
  "retryLogic": "FIXED",
  "retryDelaySeconds": 0,
  "responseTimeoutSeconds": 0
}
  • ワークフロー (sample1_wf.json)
{
  "name": "sample1",
  "description": "sample1 wf",
  "version": 1,
  "tasks": [
    {
      "name": "Sample_1",
      "taskReferenceName": "s1",
      "type": "SIMPLE",
      "start_delay": 0
    }
  ],
  "schemaVersion": 2
}

それぞれ conductor にインポートするには registerTaskDefscreateWorkflowDef メソッドを使ってください
使い方はこちらで紹介しています

ワークフローは以下のような感じで登録できれば OK です
conductor_timeout3.png

ワークフローまで登録できたらワークフローをスタートするスクリプトとポーリングするスクリプトを準備します

  • vim start_wf1.py
from conductor import conductor

def main():
    wfc = conductor.WorkflowClient('http://localhost:8080/api')
    wfName = 'sample1'
    workflowId = wfc.startWorkflow(wfName, {}, 1, None)
    print(workflowId)

if __name__ == '__main__':
    main()

スタートするワークフロー名を指定します

  • vim polling1.py
from conductor import conductor

def main():
    tc = conductor.TaskClient('http://localhost:8080/api')
    taskType = 'Sample_1'
    workerId = 'python_1'
    ret = tc.pollForTask(taskType, workerId)
    print(ret)

if __name__ == '__main__':
    main()

ポーリングするタスク名を指定します

responseTimeoutSeconds を 60 に設定

responseTimeoutSeconds は指定の時間ステータスに変化がない場合に再度ステータスを SCHEDULED に変更してくれる機能です

それぞれの定義ですが先ほどの timeoutResponse とほぼ同様です
違うのはタスク用の JSON だけです
あとは Sample_1 となっているところを Sample_2 などに変更して同様にインポートしましょう

以下のようにワークフローが作成されれば OK です
conductor_timeout5.png

動作確認

それぞれワークフローを開始して、タスクをポーリングしてみましょう
タスクの詳細が以下のような感じになります

  • timeoutSeconds 60

conductor_timeout1.png

  • responseTimeoutSeconds 60

conductor_timeout2.png

これの Last Polled/Updated の 60 秒後にタスクのステータスに何かしらの変化が起こるはずです

が、、、なんと何も起きません
待てど待てどステータスはずっと IN_PROGRESS のままで本来であれば TIMED_OUT などになるはずです

なぜか

どうやらデフォルトではタイムアウト機能は disable になっているようです (理由は全くわかりません)
タイムタウト機能を enable にするには直接コードを編集しないとダメそうです
https://github.com/Netflix/conductor/issues/362

で、やってみました

まずは ServiceModule.java を開きます

  • vim conductor/server/src/main/java/com/netflix/conductor/server/ServiceModule.java

まずヘッダ部分に import 文を 1 行追加します

import com.netflix.conductor.core.execution.WorkflowSweeper;

次に 103 行目あたり、configure() メソッドの最後の行に以下を追記します

bind(WorkflowSweeper.class).asEagerSingleton();

これで OK です
あとは再度ソースをビルドして、jar と war を作成しそこから docker のイメージを作成し docker-compose up すれば OK です
ソースのビルド方法はこちらで紹介しています

再度動作確認してみた

ソースを変更後に再度 timeoutSeconds を試してみたところ正常に動作しました
conductor_timeout4.png

メッセージを見るとぴったり 60 秒ではないようです
また単位はミリ秒で表示されるようです

ちなみに responseTimeoutSeconds はステータスは IN_PROGRESS ままだったのですが指定時間後に再度ポーリングすると取得できたのでうまく動作しているんだと思います

最後に

conductor の timeoutSeconds と responseTimeoutSeconds の挙動を試してみました
試してみたところデフォルトでは ON になっていないようで現状はソースコードを修正する必要があるようです
なぜデフォルトで OFF になっているかは不明です
現状 Pull Request もまだないようなので、出しちゃってもいいかなと思っています

2018年6月14日木曜日

swagger-codegen を使って Python のコードを生成してみよう

概要

前回 swagger-codegen を使って Ruby のコードを生成してみました
今回は Python のコードを生成しました
しかも今回は生成されたコードを修正して動くところまで実装してみます
Python2 で動作するコードも生成できますが今回は Python3 で動作するコードを生成します

環境

  • macOS 10.13.5
  • docker 18.03.1-ce
  • Python 3.6.5

サーバコードの生成

前回同様、使用する swagger.json は PetShop の JSON を使います
Python の場合 flask ベースのサーバコードを生成することができます

docker run --rm -v $(pwd):/local swaggerapi/swagger-codegen-cli generate -i http://petstore.swagger.io/v2/swagger.json -l python-flask -o /local/out/python_server

生成されたコードは以下の通り

  • ls -1 out/python_server/
Dockerfile
README.md
git_push.sh
requirements.txt
setup.py
swagger_server/
test-requirements.txt
tox.ini

Ruby の時とはだいぶことなっており Dockerfile もあります
requirements.txt があるのでそれを使って依存ライブラリをインストールします

  • pip3 install -r requirements.txt

グローバルインストールになるので必要であれば pipenv などを使って仮想環境を作ってください

クライアントコードの生成

クライアント側を生成するときは少し工夫が必要です
というのも Python の場合アクセスするホスト情報がハードコードされており、その元情報は swagger.json にある host になっています
なので一旦 swagger.json を手元にダウンロードしてから必要な部分を書き換えてコードを生成します

  • wget 'http://petstore.swagger.io/v2/swagger.json'
  • sed -i '.org' 's/petstore.swagger.io/localhost:8080/g' swagger.json
  • docker run --rm -v $(pwd):/local swaggerapi/swagger-codegen-cli generate -i /local/swagger.json -l python -o /local/out/python_client

こんな感じです
生成されたクライアントコードは以下の通りです

  • ls -1 out/python_client/
README.md
docs
git_push.sh
requirements.txt
sample.py
sample2.py
setup.py
swagger_client
test
test-requirements.txt
tox.ini

サーバの起動

  • cd out/python_server
  • python3 -m swagger_server

で OK です
localhost:8080 で起動します
localhost:8080/v2/ui で swagger ui が表示されます

サンプルコードの作成

ステータスを元にペットの情報を取得する API をコールしてみます

from __future__ import print_function
import swagger_client
from swagger_client.rest import ApiException

api_instance = swagger_client.PetApi()
status = ['available']

try:
    res = api_instance.find_pets_by_status(status)
    print(res)
except ApiException as e:
    print("Exception when calling PetApi->add_pet: %s\n" % e)

これで実行すると以下のようなエラーになります (一部省略)

ValueError: Invalid value for `name`, must not be `None`

原因はサーバサイドのコードが swagger.json の記載してある通りのレスポンスを返していないためです
今回はこれがちゃんと動くようにサーバ側のコードを修正してみたいと思います

サーバコードの修正

修正するコードは swagger_server/controllers/pet_controller.py になります
ここに find_pets_by_status(status) という関数があるのでこれを修正します

  • vim swagger_server/controllers/pet_controller.py
def find_pets_by_status(status):
    return [Pet(name='taro', photo_urls=['https://www.min-inuzukan.com/images/detailMain_pomeranian.png'])]

コメントなど関係ない部分はすべて削除しています
swagger.json を見るとわかりますが本来は Pet クラスの配列が返ってくるのが正しいです
なのでその通りになるようにレスポンスを返却します

これで再度サーバを起動してクライアントのサンプルコードを実行してみましょう
すると今度はエラーとならず正常にレスポンスが表示されると思います

[{'category': None,
 'id': None,
 'name': 'taro',
 'photo_urls': ['https://www.min-inuzukan.com/images/detailMain_pomeranian.png'],
 'status': None,
 'tags': None}]

少し解説

クライアントコード側の内部的な処理ですが、ざっくり説明するとサーバから取得した情報と swagger.json にある情報を元に必要な model or dictionary or Array or String etc… を生成します
find_pets_by_status の場合、swagger.json を見ると Pet の配列が返ってくることを想定しています
なので、クライアント側もサーバからのレスポンスを元に Pet モデルにバインドしようとします
Pet には namephoto_urls が必須パラメータとして定義されているためそれを含めた情報をサーバが返却する必要があります
また配列であることも想定しているのでたとえ要素が 1 つしかなくても配列で返却する必要があります

内部的には deserialize という関数がありそこでごにょごにょやっているので興味があれば見てください (swagger_client/api_client.py)
よくあるメタプログラミングを使っています

最後に

swagger-codegen を使って Python のコードをサーバ/クライアント側で生成してみました
また、実際にサーバを動作させてクライアントコードから問題なくコールできることを確認しました

Ruby のときもそうだったのですが、swagger-codegen は生成したコードを使って localhost で動作させるのに少し工夫が必要です
そもそも使用している swagger.json が外部のものなので localhost にアクセスするのを想定していないと言えばそれまでですが、テストなどではまずは localhost で動かしたくなります

今回は場合はクライアントコードの向き先とサーバコードのレスポンスの修正を行いました
実際は DB なども絡むので更に複雑になると思いますが最終的には swagger.json に定義されたレスポンス形式に落とし込む必要があるという点はしっかり抑えておきたい点かなと思います

2018年6月13日水曜日

swagger-codegen を試してみた

概要

swagger-codegen は 1 つの swagger ファイルから複数の言語のクライアントツールを生成することができるツールです
コマンドラインと と docker で使えるので試してみました
今回は Ruby のコードを生成します

環境

  • macOS 10.13.5
  • docker 18.03.1-ce

docker で生成する

docker run --rm -v $(pwd):/local swaggerapi/swagger-codegen-cli generate -i http://petstore.swagger.io/v2/swagger.json -l ruby -o /local/out/ruby
  • ls -1 out/ruby/
Gemfile
README.md
Rakefile
docs
git_push.sh
lib
spec
swagger_client.gemspec

こんな感じで生成されました

homebrew でバイナリをインストールして生成

Mac であれば homebrew で swagger-codegen コマンドをインストールして使うことができます
ただし Java のインストールも必要です

  • brew cask install java
  • brew install swagger-codegen

Java8 が必要だと言われて怒られた場合は

  • brew cask install homebrew/cask-versions/java8

を実行してください

これで swagger-codegen というコマンドがローカルで使えるようになります
先程の docker も内部で同じコマンドを実行しています

生成するコマンドを実行してみます

swagger-codegen generate -i http://petstore.swagger.io/v2/swagger.json -l ruby -o ./out/ruby

ほぼ同じです
パスの部分が若干違うだけです
生成されるファイルは全く同じなので割愛します

と思ったのですが docker と homebrew だと swagger-codegen のバージョンが異なるようです
また生成されるファイルも若干違っていました
docker 版には .rubocup.yml がありましたが、homebrew 側にはありませんでした
docker 側は 2.4.0-SNAPSHOT で homebrew が 2.3.1 なので docker 側の最新イメージには開発中の最新版が入っているようです

サーバ側のコードを生成する

上記のコマンドはクライアント側のツールを生成するだけです
とりあえずリクエストを投げてみたいのでサーバ側のコードも生成してみましょう
Ruby の場合 Rails5 or Sinatra が選択できるようです
個人的に Sinatra のほうが好きなので Sinatra を選択します

docker run --rm -v $(pwd):/local swaggerapi/swagger-codegen-cli generate -i http://petstore.swagger.io/v2/swagger.json -l sinatra -o /local/out/ruby_server

当然ですが swagger.json は同じものを指定してください
docker を使っていますが、ローカルでコールする場合もほぼ同じです

生成されるファイルは以下の通りです

  • ls -1
Gemfile
README.md
api
config.ru
lib
my_app.rb
swagger.yaml

使ってみる

このままでは絵に書いた餅です
実際に使ってみます
今回は docker で生成したコードを使います

サーバを立てる

先ほど生成したサーバ用のコードを使います
とりあえず Gemfile があるのでなすがままに bundle install してみましょう

  • bundle install --path vendor

いろいろとインストールされます
config.ru があるので、それを使って起動してみます

  • bundle exec rackup config.ru

これで localhost:9292 で起動します
もしバインドする IP を指定したい場合は -o 0.0.0.0 という感じで指定できます

クライアントからコールしてみる

サーバが起動したのでクライアントツールを使ってコールしてみます
こちらも Gemfile があるのでとりあえず bundle install しましょう

  • bundle install --path vendor

こちらもいろいろインストールされます
作成されたファイルを見ると swagger_client.gemspecRakefile があるのでどうやら専用の gem が作成できそうです

とりあえずそのまま作成してみましょう (ただ bundler/gem_tasks を使っていないので rake build することはできません)

  • bundle exec gem build swagger_client.gemspec

これで swagger_client-1.0.0.gem という gem ができあがります
あとはインストールして使いましょう

  • gem install swagger_client-1.0.0.gem

ethontyphoeus に依存していました
HTTP クライアントとして使っているので内部では libcurl を使っているっぽいです

サンプルコード

ではサーバ側のコードをコールしてみましょう
コール先は localhost:9292 なので SwaggerClient.configure で変更します
と言ってもサーバ側で何も実装していないので何も返ってきません

  • vim sample.rb
require 'swagger_client'

SwaggerClient.configure do |config|
  config.host = 'localhost:9292'
end

api_instance = SwaggerClient::PetApi.new
status = ['sample_status']

begin
  p api_instance.find_pets_by_status status
rescue SwaggerClient::ApiError => e
  puts "Exception when calling PetApi->add_pet: #{e}"
end

こんな感じでクライアントからコールすることができます
クライアント側のコードに README.md が出来ておりリファレンスも生成されているのでそれを参考にすると良いと思います

ちなみに localhost:9292/swagger.yml で定義ファイルを確認できます

最後に

swagger-codegen を使って Ruby のコードを生成し試してみました
正直まだまだ絶賛開発中な感じはします

また生成したコード (特にサーバ側) は本当に簡単なものだけなので、実際にサービス化するときはコーディングが必要になります

なので、生成されるコードをしっかりと読み解く時間も必要になるのは注意が必要です

参考サイト

2018年6月12日火曜日

TwitterKit を使って Twitter のログインページを作ってみた

概要

TwitterKit は Twitter が公式で提供している Swift 用の SDK です
今回は SpriteKit プロジェクトで TwitterKit を使ってみました
簡単なログイン画面を実装してみます

環境

  • macOS 10.13.5
  • Xcode Version 9.4 (9F1027a)
  • twitter-kit 3.3.0

事前準備

Twitter のアプリ作成画面からアプリを作成しておきましょう
作成後以下を確認してください

  • Consumer Key と Consumer Secret Key の取得
  • Callback URL の設定
  • Read, Write の設定

Consumer Key と Consumer Secret Key はアプリを作成すると取得できるのでアプリを作成すれば OK です
Callback URL は以下の画像にあるように twitterkit- から始まるアドレスを設定しましょう
こうすることでアプリで認証後に再度アプリの画面に戻ることができます
Read, Write の設定は Permissions タグから行うことができます

最終的に Details タブで確認して以下のようになっていれば OK です
twitterkit1.png

ライブラリインストール

  • pod init
  • vim Podfile
pod 'TwitterKit'
  • pod install

で作成された .xcworkspace のプロジェクトを Xcode で開きます

AppDelegate.swift の編集

まず didFinishLaunchingWithOptions で Consumer Key を登録します

func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool {
    TWTRTwitter.sharedInstance().start(withConsumerKey: "isQRxxxxxxxxxxxxxxxxxxxxx", consumerSecret: "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx")
    return true
}

Info.plist の編集

次に Info.plist を編集して先程アプリに設定したコールバック URL をこちらにも登録します
これを登録しないとコールバック URL がおかしいと言われてエラーが発生します
直接 XML を編集したほうが簡単なので Info.plist を選択した状態で

Open As -> Source Code

で開いて以下を貼り付けましょう

<array>
    <dict>
        <key>CFBundleURLSchemes</key>
        <array>
            <string>twitterkit-isQRxxxxxxxxxxxxxxxxxxxxx</string>
        </array>
    </dict>
</array>
<key>LSApplicationQueriesSchemes</key>
<array>
    <string>twitter</string>
    <string>twitterauth</string>
</array>

GameViewController.swift の編集

ログイン用のボタンを設置しましょう
viewDidLoad の先頭に以下を追記しましょう

override func viewDidLoad() {
    super.viewDidLoad()
    let logInButton = TWTRLogInButton(logInCompletion: { session, error in
        if let s = session {
            print("signed in as \(s.userName)");
        }
        if let e = error {
            print("error: \(e.localizedDescription)");
        }
    })
    logInButton.center = self.view.center
    self.view.addSubview(logInButton)
    // ...
}

... の部分は元々ある SKScene をロードするコードを書いておきます
TWTRLogInButton を作成して addSubview で GameViewController のど真ん中にボタンを配置しています
とりあえず今回はログイン後に何もせずデバッグのメッセージを表示します

あと最後に GameScene.sks ファイルの Hello World のラベルを削除すれば OK です

動作確認

アプリを起動して動作確認しましょう
ログインボタンを押すとログイン用の View が出てくるのでログインしましょう
twitterkit2.gif

コンソールを確認して以下のようにログイン OK のメッセージが出ていれば OK です

signed in as hawksnowlog

最後に

TwitterKit を使ってログイン画面を実装してみました
あとはログイン後の処理をデバッグ画面の表示ではなく画面遷移などに変更すれば OK だと思います
そしてツイートを取得したりツイートする処理を実装すればそれっぽい Twitter クライアントができると思います

参考サイト

2018年6月10日日曜日

ownCloud を docker で動かそう

概要

ownCloud は GoogleDrive や Dropbox といったクラウドストレージを簡単に構築できるサービスです
docker のイメージが公開されているのでコマンド一発で構築できます

環境

  • macOS 10.13.5
  • docker 18.03.1-ce

ownCloud を起動

  • docker run -d -p 80:80 owncloud:8.1

これだけで OK です

とりあえず動作確認

localhost にブラウザでアクセスしましょう
管理者用のアカウントを作成します
owncloud1.png

デフォルトでいくつかファイルがあるので不要であれば削除しましょう
あとはアップロードすれば普通に使えます
owncloud2.png

データを永続化する

このままだとコンテナを削除したらアップロードしたファイルがなくなります
テンポラリとして使うのであれば問題ないですが過去にアップロードしたファイルを確認したいときには永続化する必要があります
やり方は簡単で -v オプションでホスト側のストレージをマウントしてあげるだけです

  • docker run -d -p 80:80 -v $(pwd):/var/www/html owncloud:8.1

これでコンテナを削除してもファイルは削除されません
管理者用のユーザ/パスワードも引き継がれるので忘れないようにしましょう

Tips

ファイルを wget する際には以下のコマンドを投げます
セキュリティの観点で直リンクは使えないようになっています

wget \
--header="Host: localhost" \
--header="User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_13_5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/67.0.3396.62 Safari/537.36" \
--header="Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8" \
--header="Accept-Language: ja,en-US;q=0.9,en;q=0.8" \
--header="Cookie: _ga=GA1.2.273664293.1526454426; _gid=GA1.2.285044917.1528161957; ocztnxtfknvn=93abc983e77160bfbd5385e5a084f7b8" \
--header="Connection: keep-alive" \
"http://localhost/index.php/apps/files/ajax/download.php?dir=%2F&files=hoge.txt" \
-O "hoge.txt" -c

このコマンドは chrome の CurlWget という拡張を使って作成しています

最後に

ownCloud の使い方を紹介しました
個人的には別のサーバとファイルを交換するときに役に立つかと思います
自宅に 1 台これがあれば複数のマシンおよびデバイスでファイルのやり取りができるようになるので便利かなと思います

参考サイト

2018年6月8日金曜日

nginx for windows で簡単ファイルホスティング

概要

Windows から別のマシンにファイルを送るのが非常に面倒です
そんな場合に nginx を立ててファイルを配布すると簡単です

環境

  • Windows7 64bit HomeEdition
  • nginx for Windows 1.15.0

インストール

ここから最新版の zip ファイルをダウンロードしましょう
執筆時点では「nginx-1.15.0.zip」というファイルが最新版でした
ダウンロードしたら解凍して適当なフォルダに配置してください

基本はこれだけで OK です
いらなくなったらフォルダごと捨ててください

サーバを起動する

コマンドプロンプトを立ち上げましょう
あとは配置したディレクトリに移動して start するだけです

  • cd nginx-1.15.0
  • start nginx.exe
  • tasklist /fi "imagename eq nginx.exe"

tasklist コマンドでプロセスが起動しているか確認できます

ファイルをホスティングする

ドキュメントルートに配置してみましょう
デフォルトだと配置した nginx-1.15.0 配下の html というディレクトリが root になっています
ここに適当にテキスト (hoge.txt) を配置してください
nginx_win1.jpg

あとはブラウザで localhost/hoge.txt にアクセスするとファイルが表示できると思います
Mac などからダウンロードしたい場合は wget コマンドを使っても良いと思います

停止する

  • nginx.exe -s stop

で OK です

最後に

Windows で nginx を動かしてみました
Windows で生成したファイルをほかで共有したい場合に一時的に nginx を起動するだけで簡単に共有できるかなと思います

Mac と Windows, Linux と Windows でファイルを共有する場合にはいろいろな方法がありますがファイル共有機能などを使うといろいろと躓くポイントが多いのでとりあえずってことであればこの方法をお勧めします

参考サイト

2018年6月7日木曜日

フリーで使える Cakewalk を Windows にインストールしてみた

概要

SONAR と呼ばれる DAW (Desktop Audio Workspace) が Cakewalk by Bandlab としてフリーで使えるようになりました
使うまでが結構面倒だったので簡単に手順をまとめました

環境

  • Windows7 64bit HomeEdition
  • Cakewalk 2018.05 (build 19, 64bit)

BandLab のアカウントを作成

まずはここからアカウントを作成しましょう
calkwalk1.jpg

メールアドレスを使ってサインアップすれば OK です
確認のメールが来るのでメールをチェックしてアカウントをアクティベートします
calkwalk3.jpg

ログイン後に「作成する」を選択すると Bandlab の Web 版を使うこともできます
これは Cakewalk ではないのでご注意を
また Chrome でないと動作しないので使う場合は Chrome にしてください
calkwak4.jpg

BandLab Assistant アプリのインストール

理由はさっぱりわかりませんが BandLab Assistant というランチャーアプリをインストールしないと Cakewalk にはたどり着けません
ダウンロードはここから行います
calkwalk5.jpg

ここから Download を選択すると exe をダウンロードできるリンクに移動するのでダウンロードします
bandlab-assistant-windows-latest.exe というインストーラがダウンロードできれば OK です
あとは exe を実行してインストールすれば OK です

インストールが完了すると以下のようなランチャーが起動します
その前におそらくログイン画面が出るので先ほど作成した Bandlab のアカウントを使ってログインしましょう
calkwalk6.jpg

実はここから先ほどの Web 版を起動することも可能です
またアカウントの作成もこのランチャーを使ってできるので先にランチャーをインストールしても OK です

Cakewalk by BandLab アプリのインストール

Apps タブを開きましょう
すると Cakewalk のダウンロードボタンがあるので選択します
cakewalk7.jpg
※すでにダウンロードした後なのでボタンが Open になっています

すると目的の Cakewalk のダウンロードが始まります
結構時間がかかるので待ちます
ダウンロードが完了するとインストーラが自動的に起動してインストールが始まります
特につまづくことはないと思うのでポチポチ進めてください

これで Cakewalk のインストールが完了です

動作確認

先ほどのランチャーからでも良いですし、すべてのプログラムからでも Cakewalk を起動できます
あとは好きなように使えば OK です
enter image description here

最後に

Cakewalk by Bandlab を Windows にインストールしてみました
アカウントの取得が必要ですが、それをクリアするだけでフリーで使えるのは素晴らしいと思います

ちゃんと公式サイトを使ってインストールすることをお勧めします
Windows はノラアプリがいたるところで配布されているので間違って別のものをインストールしないように注意してください

2018年6月2日土曜日

itamae を使って docker イメージを作成してみた

概要

itamae は Ruby で作られたプロビジョニングツールです
docker イメージも作成できるということなので試してみました

環境

  • macOS 10.13.4
  • Ruby 2.5.1p57
  • itamae 1.9.11
  • docker 18.03.1-ce

itamae のインストール

今回は bundler を使っていますが、グローバルにインストールして良いと思います

  • bundle init
  • vim Gemfile
gem "itamae"
  • bundle install --path vendor

プロジェクトの作成

公式の wiki にあるようにディレクトリ構成を作成します
専用のコマンドも用意されているようなのでそれを使って構築します
もちろん今回の方法じゃなくても OK です

  • bundle exec itamae init test
      create  
      create  Gemfile
      create  cookbooks/.keep
      create  roles/.keep
         run  bundle install from "."

test という名前のプロジェクトを作成します
今回は docker イメージを作成するので gem を追加します

  • vim Gemfile
gem 'docker-api'

この配下に role と cookbook を作成します

role の作成

role はサーバの役割ごとに作成します
例えば Web サーバ、DB サーバに対してレシピを作成する場合は web, db などといった role を作成します
今回はテストサーバなので test_server という role を作成します

  • cd test
  • bundle install --path vendor
  • bundle exec itamae generate role test_server
      create  
      create  default.rb
      create  files/.keep
      create  templates/.keep

こんな感じで作成されます
あとで cookbook を作成するのでその cookbook を参照するように role のレシピに記載します

  • vim roles/test_server/default.rb
include_recipe '../../cookbooks/vim'

相対パスで cookbook ディレクトリの場所を指定します
vim cookbook はこの後作成します

cookbook の作成

上述の通り vim をインストールする cookbook を作成しましょう
この cookbook が実際にプロビジョニングする内容を記載するレシピになります

  • bundle exec itamae generate cookbook vim

今度は cookbook を generate します

      create  
      create  default.rb
      create  files/.keep
      create  templates/.keep

作成されたらレシピを記載します

  • vim cookbooks/vim/default.rb
execute 'update apt' do
        command 'apt -y update'
end

package 'vim' do
        options '--force-yes'
end

やっていることは簡単で apt update した後で vim をインストールしています
ではこれを使って docker イメージを作成してみましょう

docker イメージの作成

itame は作成したレシピを使って docker イメージを作成することが可能です
他にも localhost に適用したり SSH を使ってリモートホストに実行することも可能です

  • bundle exec itamae docker --image=nginx --tag nginx_with_vim roles/test_server/default.rb

実行すると nginx イメージの pull が始まりその後プロビジョニングが始まります

 INFO : Starting Itamae...
 INFO : Recipe: /Users/hawk/Documents/work/itamae/test/roles/test_server/default.rb
 INFO :   Recipe: /Users/hawk/Documents/work/itamae/test/cookbooks/vim/default.rb
 INFO :     execute[update apt] executed will change from 'false' to 'true'
 INFO :     package[vim] installed will change from 'false' to 'true'
 INFO : Image created: sha256:58b463f0c5730e230e5d6124c6b3112bdf5a3c0cf1347c629038f0af9d7c9c62

こんな感じでログが表示されれば成功です
細かいヘルプは bundle exec itamae help docker で確認できます

動作確認

docker images で nginx_with_vim というイメージが作成されていることを確認します
そして run で動作確認してみましょう

  • docker run --rm nginx_with_vim vim --version

これで vim のバージョン情報が表示されると思います

最後に

itame を使ってレシピを作成して、そこから docker イメージを作成してみました
今回は docker イメージを作成しましたが、作成したレシピは普通の Ubuntu サーバなどに対しても実行可能です

Ansible に比べて情報は少ないですが、簡単に使えると思います

参考サイト