Lambda Functionで任意のパラメータを受け取る

今回は前回の続きで、Lambda Functionに変更を加え、更新をAWS CLIから行ってみた際のログを晒します。
本当はAPI Gatewayのお話を書きたかったのですが、まずは順番にこのレベルから記事化することにしました。

Lambda Functionで任意のパラメータを受け取る

前回作成したサンプルコードを書き換えましょう。
具体的にはclient#post()で渡していた文字列を、handlerの第一引数から取得するように変更を行いました。
event.tweetが存在すればそれを利用し、存在しなければこれまで通り"I Love Twitter"とつぶやきます。

# vim index.js
var Twitter = require('twitter');

var client = new Twitter({
  consumer_key: 'xxxxxxxxxxxxxxxxxxxxxxxxx',
  consumer_secret: 'xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx',
  access_token_key: 'xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx',
  access_token_secret: 'xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx'
});

exports.handler = function(event, context) {
    var tweet = (event.tweet) ? event.tweet : 'I Love Twitter';
    client.post('statuses/update', {status: tweet},  function(error, tweet, response) {
        if (error) {
            context.done(null, error);
        }
        console.log(tweet);
        console.log(response);
        context.succeed();
    });
};

パラメータは、以下のようなjson文字列を受け取る想定で実装しています。

{
    "tweet" : "I Love Twitter"
}

Lambda Functionを更新

Lambda Functionはzip化してアップロードしていますので、今回修正を行ったindex.jsをzipに再圧縮してください。
その後、以下のコマンドでLambda Functionを更新しましょう。
※Lambdaのバージョニング機能やエイリアスを利用する場合は今回のコマンドでは事足りません。(publishオプションや、update-aliasサブコマンド等を利用します)

# aws lambda update-function-code --function-name lambda-twitter --zip-file fileb://lambda-twitter.zip
{
    "CodeSha256": "fnKOjOt+Am3dva1WCbL6/h+HatgAwsKo36Fl9S1DHow=",
    "FunctionName": "lambda-twitter",
    "CodeSize": 1273734,
    "MemorySize": 128,
    "FunctionArn": "arn:aws:lambda:ap-northeast-1:xxxxxxxxxxxx:function:lambda-twitter",
    "Version": "$LATEST",
    "Role": "arn:aws:iam::xxxxxxxxxxxx:role/lambda-exec-role",
    "Timeout": 3,
    "LastModified": "2016-04-24T14:18:37.816+0000",
    "Handler": "index.handler",
    "Runtime": "nodejs",
    "Description": ""
}

Lambda Functionを実行

それでは更新が反映されていることを確認するため、Lambda Functionを実行してみましょう。
今回は実行時にLambda Functionに渡したいパラメータ (--payload) を指定します。

# aws lambda invoke --function-name lambda-twitter --payload '{ "tweet" : "I Love AWS" }' output.txt
{
    "StatusCode": 200
}

無事、変更が反映されましたね。

f:id:demiglacesource:20160424235225j:plain

まとめ

Lambda Functionの更新をAWS CLIから行ってみました。
次回こそは、API Gateway(Lambda Function実行)に関する記事を書きたいと思います!

AWS CLIからLambda FunctionをアップロードしTwitterにTweetしてみた

今日はLambda FunctionをCLIからアップロードし、TwitterTweetしてみた時のログを晒します。
久々にまじめにLambdaを触っているので、記憶を呼び起こす+自分への自戒を込めて、ブログ記事化してみました。

事前準備

まずは作業フォルダを作成しましょう。

# mkdir lambda-twitter && cd $_

TwitterへのTweetは毎度おなじみのこちらのライブラリを利用しました。

# npm install twitter

Lambda Function

続いて実際のLambda Functionです。今回は特にパラメータなどは受け取らず、固定の文字列で「I Love Twitter」とつぶやいてみました。
はい、ほとんどtwitterライブラリのサンプルをそのままLambda Function化してみた、というのが実態です。

# vim index.js
var Twitter = require('twitter');

var client = new Twitter({
  consumer_key: 'xxxxxxxxxxxxxxxxxxxxxxxxx',
  consumer_secret: 'xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx',
  access_token_key: 'xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx',
  access_token_secret: 'xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx'
});

exports.handler = function(event, context) {
    client.post('statuses/update', {status: 'I Love Twitter'},  function(error, tweet, response){
        if (error) {
            context.done(null, error);
        }
        console.log(tweet);
        console.log(response);
        context.succeed();
    });
};

Lambda Functionを圧縮

twitterライブラリを利用するため、zip形式で圧縮してアップロードします。
そのため先ほど作成したLambda Functionを、zipで圧縮しましょう。

# zip -r lambda-twitter.zip index.js node_modules

IAM Roleの作成

Lambda Functionに紐付けるIAM Roleを作成します。
今回はこちらもCLIから実行してみましょう。

# mkdir roles
# vim roles/lambda-exec-role.json
{
  "Version": "2012-10-17",
  "Statement": [
     {
       "Action": "sts:AssumeRole",
       "Principal": {
         "Service": "lambda.amazonaws.com"
        },
        "Effect": "Allow",
        "Sid": ""
     }
  ]
}

# aws iam create-role --role-name lambda-exec-role --assume-role-policy-document file://roles/lambda-exec-role.json
{
    "Role": {
        "AssumeRolePolicyDocument": {
            "Version": "2012-10-17",
            "Statement": [
                {
                    "Action": "sts:AssumeRole",
                    "Sid": "",
                    "Effect": "Allow",
                    "Principal": {
                        "Service": "lambda.amazonaws.com"
                    }
                }
            ]
        },
        "RoleId": "xxxxxxxxxxxxxxxxxxxxx",
        "CreateDate": "2016-04-23T14:45:34.538Z",
        "RoleName": "lambda-exec-role",
        "Path": "/",
        "Arn": "arn:aws:iam::xxxxxxxxxxxx:role/lambda-exec-role"
    }
}

Lambda Functionを作成

先ほど作成したzipファイル、およびIAM Roleを指定しLambda Functionを作成します。

# aws lambda create-function --function-name lambda-twitter --runtime nodejs --role arn:aws:iam::xxxxxxxxxxxx:role/lambda-exec-role --handler index.handler --zip-file fileb://lambda-twitter.zip
{
    "CodeSha256": "ZdOQM2MN4GaYNU8MZa4s2wh4Ya195b2KryMf7oEhFq8=",
    "FunctionName": "lambda-twitter",
    "CodeSize": 1273693,
    "MemorySize": 128,
    "FunctionArn": "arn:aws:lambda:ap-northeast-1:xxxxxxxxxxxx:function:lambda-twitter",
    "Version": "$LATEST",
    "Role": "arn:aws:iam::xxxxxxxxxxxx:role/lambda-exec-role",
    "Timeout": 3,
    "LastModified": "2016-04-23T15:29:40.632+0000",
    "Handler": "index.handler",
    "Runtime": "nodejs",
    "Description": ""
}

Management Consoleを見ると、作成したLambda Functionが追加されていることを確認できるでしょう。

f:id:demiglacesource:20160424012858j:plain

Lambda Functionを実行

ではいよいよLambda Functionを実行します。

# aws lambda invoke --function-name lambda-twitter output.txt
{
    "StatusCode": 200
}

どうやら実行に成功したようです。
正しくTwitterにも投稿されていることが確認できますね。

f:id:demiglacesource:20160424013112j:plain

何度か実行すると、Management Consoleからもそれらしい実行ログを確認できるようになりました。
(何度かエラーになっているのは・・・察してください)

f:id:demiglacesource:20160424013429j:plain

まとめ

Lambda Functionの作成から実行までを、AWS CLIを用いて行ってみました。
次回は今回作成したLambda FunctionをAPI Gateway経由で実行してみたいと思います!

日本人の名前や、日本の住所をランダムに返すgimeiをportしてnode-gimei を作った

久々更新。。。

あれ、実は新年初更新ですね。笑 皆様新年明けましておめでとうございます。笑

というわけで今日は某ブログの会社のブログでも利用されていた gimei のNode.js版を作ってみました。 全機能を網羅しているわけではないですが・・・

Node.jsを使われていて、テストデータの作成に困った時なんかにどうぞ。

https://github.com/demiglacesource/node-gimei

otto devで使用されるVagrantのboxファイルを変更する

先日HashiCorp社の新プロダクト、ottoがリリースされました。
Vagrant の後継ツールという位置付けであるということで、Vagrant 好きの自分はすぐに飛びついてみます。

otto のインストール

まずは otto をインストールしましょう。
とはいえ otto もワンバイナリのツールであるため、ダウンロードしてきたモジュールをパスの通った場所に保存すれば全て完了です。

$ sudo mv otto /usr/local/bin/
$ otto
usage: otto [--version] [--help] <command> [<args>]

Available commands are:
    build      Build the deployable artifact for the app
    compile    Prepares your project for being run.
    deploy     Deploy the application
    dev        Start and manage a development environment
    infra      Builds the infrastructure for the Appfile
    status     Status of the stages of this application
    version    Prints the Otto version

何調べるん?

基本の使い方などはすでにいくつか記事があがっておりましたので割愛するとして、今回は otto dev された時に利用されるboxファイルの変更方法を調べてみました。
まずは環境の準備です。

$ git clone https://github.com/hashicorp/otto-getting-started.git
Cloning into 'otto-getting-started'...
remote: Counting objects: 23, done.
remote: Total 23 (delta 0), reused 0 (delta 0), pack-reused 23
Unpacking objects: 100% (23/23), done.
Checking connectivity... done.

次に otto compile で.ottoディレクトリを作成しましょう。
ここまではサンプルの実行手順と同じですね。

$ cd otto-getting-started
$ otto compile
==> Loading Appfile...
==> No Appfile found! Detecting project information...
    No Appfile was found. If there is no Appfile, Otto will do its best
    to detect the type of application this is and set reasonable defaults.
    This is a good way to get started with Otto, but over time we recommend
    writing a real Appfile since this will allow more complex customizations,
    the ability to reference dependencies, versioning, and more.
==> Fetching all Appfile dependencies...
==> Compiling...
    Application:    otto-getting-started (ruby)
    Project:        otto-getting-started
    Infrastructure: aws (simple)

    Compiling infra...
    Compiling foundation: consul
==> Compiling main application...
==> Compilation success!
    This means that Otto is now ready to start a development environment,
    deploy this application, build the supporting infastructure, and
    more. See the help for more information.

    Supporting files to enable Otto to manage your application from
    development to deployment have been placed in the output directory.
    These files can be manually inspected to determine what Otto will do.

作成した.ottoディレクトリの中身はこのような構成になっています。

$ tree .otto/
.otto/
├── appfile
│   ├── Appfile.compiled
│   └── version
├── compiled
│   ├── app
│   │   ├── build
│   │   │   ├── build-ruby.sh
│   │   │   └── template.json
│   │   ├── deploy
│   │   │   └── main.tf
│   │   ├── dev
│   │   │   └── Vagrantfile
│   │   └── foundation-consul
│   │       ├── app-build
│   │       │   ├── main.sh
│   │       │   └── upstart.conf
│   │       ├── app-deploy
│   │       │   └── main.sh
│   │       ├── app-dev
│   │       │   ├── main.sh
│   │       │   └── upstart.conf
│   │       ├── app-dev-dep
│   │       │   └── main.sh
│   │       └── deploy
│   │           ├── main.tf
│   │           ├── module-aws
│   │           │   ├── join.sh
│   │           │   ├── main.tf
│   │           │   ├── outputs.tf
│   │           │   └── variables.tf
│   │           ├── module-aws-simple
│   │           │   ├── main.tf
│   │           │   ├── outputs.tf
│   │           │   ├── setup.sh
│   │           │   └── variables.tf
│   │           └── variables.tf
│   ├── foundation-consul
│   │   ├── app-build
│   │   │   ├── main.sh
│   │   │   └── upstart.conf
│   │   ├── app-deploy
│   │   │   └── main.sh
│   │   ├── app-dev
│   │   │   ├── main.sh
│   │   │   └── upstart.conf
│   │   ├── app-dev-dep
│   │   │   └── main.sh
│   │   └── deploy
│   │       ├── main.tf
│   │       ├── module-aws
│   │       │   ├── join.sh
│   │       │   ├── main.tf
│   │       │   ├── outputs.tf
│   │       │   └── variables.tf
│   │       ├── module-aws-simple
│   │       │   ├── main.tf
│   │       │   ├── outputs.tf
│   │       │   ├── setup.sh
│   │       │   └── variables.tf
│   │       └── variables.tf
│   └── infra-otto-getting-started
│       ├── main.tf
│       └── outputs.tf
└── data
    └── dev_ip

24 directories, 41 files

Vagrant の後継ツールとは言われていますが、Vagrant + Packer + Consul + Terraform = ottoという感じですね。
そもそも後継ツールとか言っちゃうのが誤訳なのかも。
今回の主題である Vagrantfile は .otto/compiled/app/dev/Vagrantfile に保存されているようです。

boxファイルを変更

まずはローカルに保存済みのboxファイルの一覧を確認します。

$ vagrant box list
CentOS-7.1.1503-x86_64 (virtualbox, 0)

今回は CentOS7 の box を利用しましょう。
※これが裏目に出ます。

$ vim .otto/compiled/app/dev/Vagrantfile 
# Generated by Otto, do not edit!
#
# This is the Vagrantfile generated by Otto for the development of
# this application/service. It should not be hand-edited. To modify the
# Vagrantfile, use the Appfile.

Vagrant.configure("2") do |config|
  config.vm.box = "CentOS-7.1.1503-x86_64"    ←変更

[snip]

それでは otto dev します。

$ otto dev
==> Creating local development environment with Vagrant if it doesn't exist...
    Raw Vagrant output will begin streaming in below. Otto does
    not create this output. It is mirrored directly from Vagrant
    while the development environment is being created.

Bringing machine 'default' up with 'virtualbox' provider...
==> default: Importing base box 'CentOS-7.1.1503-x86_64'...
==> default: Matching MAC address for NAT networking...
==> default: Setting the name of the VM: dev_default_1444157288651_95865
==> default: Clearing any previously set forwarded ports...
==> default: Clearing any previously set network interfaces...
==> default: Preparing network interfaces based on configuration...
    default: Adapter 1: nat
    default: Adapter 2: hostonly
==> default: Forwarding ports...
    default: 22 => 2222 (adapter 1)
==> default: Booting VM...
==> default: Waiting for machine to boot. This may take a few minutes...
    default: SSH address: 127.0.0.1:2222
    default: SSH username: vagrant
    default: SSH auth method: private key
    default: Warning: Connection timeout. Retrying...
    default: 
    default: Vagrant insecure key detected. Vagrant will automatically replace
    default: this with a newly generated keypair for better security.
    default: 
    default: Inserting generated public key within guest...
    default: Removing insecure key from the guest if it's present...
    default: Key inserted! Disconnecting and reconnecting using new SSH key...
==> default: Machine booted and ready!
==> default: Checking for guest additions in VM...
==> default: Configuring and enabling network interfaces...
==> default: Mounting shared folders...
    default: /vagrant => /Users/demiglacesource/Develop/repository/otto-getting-started
    default: /otto/foundation-1 => /Users/demiglacesource/Develop/repository/otto-getting-started/.otto/compiled/app/foundation-consul/app-dev
==> default: Running provisioner: shell...
    default: Running: inline script
==> default: [otto] Installing Consul...
==> default: [otto] Installing dnsmasq for Consul...
==> default: [otto] Configuring consul service: otto-getting-started
==> default: Running provisioner: shell...
    default: Running: inline script
==> default: [otto] Adding apt repositories and updating...
==> default: ERROR at /tmp/vagrant-shell:12; Last logs:
==> default: grep: /var/log/syslog: No such file or directory
    The SSH command responded with a non-zero exit status. Vagrant
    assumes that this means the command failed. The output for this command
    should be in the log above. Please read the output to determine what
    went wrong.

    Error building dev environment: Error executing Vagrant: exit status 1

    The error messages from Vagrant are usually very informative.
    Please read it carefully and fix any issues it mentions. If
    the message isn't clear, please report this to the Otto project.

Error !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!

原因調査

恐る恐るエラーになったスクリプトの中を覗いてみます。

$ vim .otto/compiled/app/foundation-consul/app-dev/main.sh
#!/bin/bash
set -e

oe() { $@ 2>&1 | logger -t otto > /dev/null; }
ol() { echo "[otto] $@"; }

dir=$(pwd)

# Download and setup Consul directories
if ! command -v consul >/dev/null 2>&1; then
    ol "Installing Consul..."
    oe sudo apt-get update -y    ←あれ????
    oe sudo apt-get install -y unzip    ←あれ????
[snip]

これ apt-get !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
※今回 OS は CentOS をつこてる

パッチ

ダメ元で apt-get を yum に全置換してみた。ついでに unzip だけじゃなくて wget もインストールしておくように変更。
あとよくよく見るとスクリプトだけではなくVagrantfileにもapt-getが・・・
最終的にVagrantfileはこんな感じに修正。(だいぶ雑)

# Generated by Otto, do not edit!
#
# This is the Vagrantfile generated by Otto for the development of
# this application/service. It should not be hand-edited. To modify the
# Vagrantfile, use the Appfile.

Vagrant.configure("2") do |config|
  config.vm.box = "CentOS-7.1.1503-x86_64"

  # Host only network
  config.vm.network "private_network", ip: "172.16.1.202"

  # Setup a synced folder from our working directory to /vagrant
  config.vm.synced_folder '/Users/akio/Develop/repository/otto-getting-started', "/vagrant",
    owner: "vagrant", group: "vagrant"

  # Enable SSH agent forwarding so getting private dependencies works
  config.ssh.forward_agent = true

  # Foundation configuration (if any)

  dir = "/otto/foundation-1"
  config.vm.synced_folder '/Users/akio/Develop/repository/otto-getting-started/.otto/compiled/app/foundation-consul/app-dev', dir
  config.vm.provision "shell", inline: "cd #{dir} && bash #{dir}/main.sh"


  # Load all our fragments here for any dependencies.


  # Install Ruby build environment
  config.vm.provision "shell", inline: $script_ruby
end

$script_ruby = <<SCRIPT
set -o nounset -o errexit -o pipefail -o errtrace

error() {
   local sourcefile=$1
   local lineno=$2
   echo "ERROR at ${sourcefile}:${lineno}; Last logs:"
   grep otto /var/log/syslog | tail -n 20
}
trap 'error "${BASH_SOURCE}" "${LINENO}"' ERR

# otto-exec: execute command with output logged but not displayed
oe() { $@ 2>&1 | logger -t otto > /dev/null; }

# otto-log: output a prefixed message
ol() { echo "[otto] $@"; }

# Make it so that `vagrant ssh` goes directly to the correct dir
echo "cd /vagrant" >> /home/vagrant/.bashrc

# Configuring SSH for faster login
if ! grep "UseDNS no" /etc/ssh/sshd_config >/dev/null; then
  echo "UseDNS no" | sudo tee -a /etc/ssh/sshd_config >/dev/null
  oe sudo service ssh restart
fi

export DEBIAN_FRONTEND=noninteractive

ol "Adding apt repositories and updating..."
oe sudo yum update
oe sudo yum install -y epel-release

# TODO: parameterize ruby version as input
export RUBY_VERSION="2.2"

ol "Installing Ruby ${RUBY_VERSION} and supporting packages..."
export DEBIAN_FRONTEND=noninteractive
oe sudo yum install -y bzr git mercurial \
  nodejs \
  ruby ruby-devel

ol "Installing Bundler..."
oe gem install bundler --no-ri --no-rdoc

ol "Configuring Git to use SSH instead of HTTP so we can agent-forward private repo auth..."
oe git config --global url."git@github.com:".insteadOf "https://github.com/"
SCRIPT

リトライ

まずは otto dev destroy で作った環境を飛ばす。

$ otto dev destroy
==> Destroying the local development environment...
==> default: Forcing shutdown of VM...
==> default: Destroying VM and associated drives...
==> default: Running cleanup tasks for 'shell' provisioner...
==> default: Running cleanup tasks for 'shell' provisioner...


==> Deleting development environment metadata...
==> Development environment has been destroyed!

では、再構築。

$ otto dev
==> Creating local development environment with Vagrant if it doesn't exist...
    Raw Vagrant output will begin streaming in below. Otto does
    not create this output. It is mirrored directly from Vagrant
    while the development environment is being created.

Bringing machine 'default' up with 'virtualbox' provider...
==> default: Importing base box 'CentOS-7.1.1503-x86_64'...
==> default: Matching MAC address for NAT networking...
==> default: Setting the name of the VM: dev_default_1444159206592_75592
==> default: Clearing any previously set forwarded ports...
==> default: Clearing any previously set network interfaces...
==> default: Preparing network interfaces based on configuration...
    default: Adapter 1: nat
    default: Adapter 2: hostonly
==> default: Forwarding ports...
    default: 22 => 2222 (adapter 1)
==> default: Booting VM...
==> default: Waiting for machine to boot. This may take a few minutes...
    default: SSH address: 127.0.0.1:2222
    default: SSH username: vagrant
    default: SSH auth method: private key
    default: Warning: Connection timeout. Retrying...
    default: 
    default: Vagrant insecure key detected. Vagrant will automatically replace
    default: this with a newly generated keypair for better security.
    default: 
    default: Inserting generated public key within guest...
    default: Removing insecure key from the guest if it's present...
    default: Key inserted! Disconnecting and reconnecting using new SSH key...
==> default: Machine booted and ready!
==> default: Checking for guest additions in VM...
==> default: Configuring and enabling network interfaces...
==> default: Mounting shared folders...
    default: /vagrant => /Users/demiglacesource/Develop/repository/otto-getting-started
    default: /otto/foundation-1 => /Users/demiglacesource/Develop/repository/otto-getting-started/.otto/compiled/app/foundation-consul/app-dev
==> default: Running provisioner: shell...
    default: Running: inline script
==> default: [otto] Installing Consul...
==> default: [otto] Installing dnsmasq for Consul...
==> default: [otto] Configuring consul service: otto-getting-started
==> default: Running provisioner: shell...
    default: Running: inline script
==> default: [otto] Adding apt repositories and updating...
==> default: [otto] Installing Ruby 2.2 and supporting packages...
==> default: [otto] Installing Bundler...
==> default: [otto] Configuring Git to use SSH instead of HTTP so we can agent-forward private repo auth...

==> Caching SSH credentials from Vagrant...
==> Development environment successfully created!
    IP address: 172.16.1.202

    A development environment has been created for writing a generic
    Ruby-based app.

    Ruby is pre-installed. To work on your project, edit files locally on your
    own machine. The file changes will be synced to the development environment.

    When you're ready to build your project, run 'otto dev ssh' to enter
    the development environment. You'll be placed directly into the working
    directory where you can run 'bundle' and 'ruby' as you normally would.

    You can access any running web application using the IP above.

どうやら成功したようです。

動作確認

環境を確認してみます。

$ otto dev ssh
==> Executing SSH. This may take a few seconds...
Welcome to your Vagrant-built virtual machine.
$ cat /etc/redhat-release 
CentOS Linux release 7.1.1503 (Core) 

CentOS7が起動していますね。
次にサンプルを実行する前にfirewalldを無効化します。

$ sudo systemctl stop firewalld

それではサンプルを実行してみましょう。

$ bundle && rackup --host 0.0.0.0
Fetching gem metadata from https://rubygems.org/..........
Fetching version metadata from https://rubygems.org/..
Installing rack 1.6.4
Installing rack-protection 1.5.3
Installing tilt 2.0.1
Installing sinatra 1.4.6
Using bundler 1.10.6
Bundle complete! 1 Gemfile dependency, 5 gems now installed.
Use `bundle show [gemname]` to see where a bundled gem is installed.
[2015-10-07 15:00:44] INFO  WEBrick 1.3.1
[2015-10-07 15:00:44] INFO  ruby 2.0.0 (2014-11-13) [x86_64-linux]
[2015-10-07 15:00:44] INFO  WEBrick::HTTPServer#start: pid=22388 port=9292

この状態でブラウザから http://172.16.1.202:9292 にアクセスすると、サンプルアプリが表示されました。
アドレスがわからなければ otto dev address で確認することができます。

結論

boxファイルの変更はなんとかできる。

ただし otto が自動生成するスクリプト類が Ubuntu ( Debian もセーフかも?) 前提になっているので CentOS のboxファイルを使うようにすると内部で vagrant up されたときに死ぬ。
そういえば otto compile し直すと変更した内容が全部飛ぶので注意。

そもそもottoの思想的に環境の準備はottoが面倒見ますってスタンスなので、あまりこの辺りの設定はいじるべきではないのかな?
ただ本番にデプロイする時にUbuntuを使う勇気がない自分はどうすれば・・・

バージョンアップに期待。笑

HAProxyを気軽に試せるVagrantfileを書いた

先日GitHubに、HAProxyを気軽に試してもらえるVagrantfileを公開しました。

github.com

vagrant upを行うと、こんな構成の環境がすぐに立ち上がります。

https://pbs.twimg.com/media/CJZ--u5UYAABvcJ.png

※絵の作成はCacooさんを利用させて頂きました。いつも便利に利用させて頂いております!

Vagrantfile開けて $num_instances の値を変更頂ければ、backendのWebサーバ台数を簡単に変更することもできます。

是非お試しください!

HomebrewやMacPortsを利用せずにtreeコマンドをインストールする

今回はMacにtreeコマンドをインストールしてみます。
HomebrewやMacPortsを使えば簡単にインストールできるようなのですが、今回はあえてソースからインストールを行いたいと思います。

事前準備

makeコマンドが利用できることを確認してください。
まれにmakeが行方不明になっている人もいるらしいです。

$ make --version
GNU Make 3.81
Copyright (C) 2006  Free Software Foundation, Inc.
This is free software; see the source for copying conditions.
There is NO warranty; not even for MERCHANTABILITY or FITNESS FOR A
PARTICULAR PURPOSE.

This program built for i386-apple-darwin11.3.0

次にgccコマンドが利用できることを確認します。

$ gcc --version
Configured with: --prefix=/Applications/Xcode.app/Contents/Developer/usr --with-gxx-include-dir=/usr/include/c++/4.2.1
Apple LLVM version 6.1.0 (clang-602.0.53) (based on LLVM 3.6.0svn)
Target: x86_64-apple-darwin14.4.0
Thread model: posix

この辺りのコンパイル環境が初めから整っていることも、Macが優良な点ですね。

ソースのダウンロード

以下のコマンドでtreeのソースをダウンロードします。

$ curl -O http://pkgs.fedoraproject.org/repo/pkgs/tree/tree-1.7.0.tgz/abe3e03e469c542d8e157cdd93f4d8a6/tree-1.7.0.tgz

同時に展開もしておきましょう。

$ tar xvfz tree-1.7.0.tgz 
x tree-1.7.0/CHANGES
x tree-1.7.0/INSTALL
x tree-1.7.0/LICENSE
x tree-1.7.0/Makefile
x tree-1.7.0/README
x tree-1.7.0/TODO
x tree-1.7.0/color.c
x tree-1.7.0/hash.c
x tree-1.7.0/html.c
x tree-1.7.0/json.c
x tree-1.7.0/strverscmp.c
x tree-1.7.0/tree.c
x tree-1.7.0/tree.h
x tree-1.7.0/unix.c
x tree-1.7.0/xml.c
x tree-1.7.0/doc/tree.1
x tree-1.7.0/doc/tree.1.fr
x tree-1.7.0/doc/xml.dtd

コンパイル、そしてインストール

まず初めにコンパイルオプションを設定するためMakefileを一部編集します。
55行目あたりにOS Xのオプションが書かれていますので、アンコメントしていきましょう。

$ cd tree-1.7.0
$ vim Makefile
・・・
# Uncomment for OS X:
CC=cc
CFLAGS=-O2 -Wall -fomit-frame-pointer -no-cpp-precomp
LDFLAGS=
MANDIR=/usr/share/man/man1
OBJS+=strverscmp.o
・・・

それではコンパイルを行います。

$ make 
cc -O2 -Wall -fomit-frame-pointer -no-cpp-precomp -c -o strverscmp.o strverscmp.c
cc  -o tree tree.o unix.o html.o xml.o json.o hash.o color.o strverscmp.o

無事に成功しましたね。
ではビルドに成功したtreeコマンドをインストールします。

$ sudo make install
Password:
install -d /usr/bin
install -d /usr/share/man/man1
if [ -e tree ]; then \
        install tree /usr/bin/tree; \
    fi
install doc/tree.1 /usr/share/man/man1/tree.1

※デフォルトで/usr/binにtreeコマンドがインストールされますので、管理者権限が必要です。もしそれが嫌ならインストールパスも書き換え、pathさえ通せば問題ないでしょう。

動作確認

以下のコマンドを実行します。

$ tree --version
tree v1.7.0 (c) 1996 - 2014 by Steve Baker, Thomas Moore, Francesc Rocher, Florian Sesser, Kyosuke Tokoro 

無事にユーザーが参照できる場所にtreeコマンドがインストールされていそうです。
例えばこんな順番でコマンドを実行すると・・・

$ mkdir -p hoge/hogehoge/hogehogehoge
$ touch hoge/test.txt
$ tree hoge
hoge
├── hogehoge
│   └── hogehogehoge
└── test.txt

2 directories, 1 file

大変見やすくなりましたね!

まとめ

HomebrewやMacPortsをインストールしたくない、でもtreeコマンドを使いたい!
というかたは是非試してみてください!!! (あんまり、そんな人もいないかな・・・)

389DSにldapsで通信する

今回は前回の続きで、CentOS7にインストールした389DSにldapsで通信する際の設定をまとめました。
ここまでの操作はこちらの記事を参照してください。
demiglacesource.hatenablog.com

まずは現状確認

Listenポートの一覧を確認してみます。
現在636ポートはListenされていませんね。

# ss -lnt
State      Recv-Q Send-Q        Local Address:Port          Peer Address:Port 
LISTEN     0      100               127.0.0.1:25                       *:*     
LISTEN     0      128                       *:22                       *:*     
LISTEN     0      100                     ::1:25                      :::*     
LISTEN     0      128                      :::389                     :::*     
LISTEN     0      128                      :::22                      :::*     

当然、ldapsearchでもldapsで通信することはできません。

# ldapsearch -H ldaps://localhost -D "cn=Directory Manager" -w password -b dc=example,dc=demiglacesource,dc=net -ZZ *
ldap_start_tls: Can't contact LDAP server (-1)

それでは389DSにldapsで通信できるようにするための設定を行っていきましょう。

証明書を作成

今回はオレオレ証明書を作成します。
まずはサーバーのホスト名を確認してみましょう。

# hostname
example.demiglacesource.net

ではオレオレ証明書を作成します。
-subjCN=の後には先ほど確認したサーバーのホスト名を入力してください。
パスワードは、証明書のパスワードとして任意の文字列を入力してください。

# cd /tmp
# openssl req -x509 -nodes -days 3650 -subj '/C=XX/L=Default City/O=Default Company Ltd/CN=example.demiglacesource.net' -newkey rsa:2048 -keyout key.pem -out cert.pem
# openssl pkcs12 -export -inkey key.pem -in cert.pem -out crt.p12 -nodes -name Server-Cert
Enter Export Password: password
Verifying - Enter Export Password: password

後の手順では、ここでできあがった crt.p12 を使います。

証明書をインポート

389DSに、先ほど作成した証明書をインポートします。
パスワードなどは、逐次入力してください。

# pk12util -i /tmp/crt.p12 -d /etc/dirsrv/slapd-example
Enter a password which will be used to encrypt your keys.
The password should be at least 8 characters long,
and should contain at least one non-alphabetic character.

Enter new password: password
Re-enter password: password
Enter password for PKCS12 file: password
pk12util: PKCS12 IMPORT SUCCESSFUL

証明書がインポートされていることを確認します。
Server-Certという名前の証明書がインポートされているため、これで問題なさそうですね。

# certutil -d /etc/dirsrv/slapd-example -L
Certificate Nickname                                         Trust Attributes
                                                             SSL,S/MIME,JAR/XPI

Server-Cert                                                  u,u,u

TLS/SSLを有効化

389DSのTLS/SSLを有効化します。
設定はGUIのadminツールで行うか、CUIからldapmodifyで行うことができます。

今回はCUIで全てやってしまいましょう。

# vim /tmp/ssl_enable.ldif
dn: cn=encryption,cn=config
changetype: modify
replace: nsSSL3
nsSSL3: off
-
replace: nsSSLClientAuth
nsSSLClientAuth: allowed
-
add: nsSSL3Ciphers
nsSSL3Ciphers: +all

dn: cn=config
changetype: modify
add: nsslapd-security
nsslapd-security: on
-
replace: nsslapd-ssl-check-hostname
nsslapd-ssl-check-hostname: off

dn: cn=RSA,cn=encryption,cn=config
changetype: add
objectclass: top
objectclass: nsEncryptionModule
cn: RSA
nsSSLPersonalitySSL: Server-Cert
nsSSLToken: internal (software)
nsSSLActivation: on

このldifを使いldapmodifyを行います。

# ldapmodify -x -D "cn=Directory Manager" -w password -f /tmp/ssl_enable.ldif
modifying entry "cn=encryption,cn=config"

modifying entry "cn=config"

adding new entry "cn=RSA,cn=encryption,cn=config"

無事設定変更ができたようです。

パスワードファイルの作成

証明書のパスワードを書いたテキストファイルを作成します。
password の部分は証明書のパスワードが入ります。逐次読み替えてください。

# vim /etc/dirsrv/slapd-example/pin.txt
Internal (Software) Token:password

パスワードが書かれていますので、ファイルの権限を正しく設定しておきましょう。

# chmod 400 /etc/dirsrv/slapd-example/pin.txt

389DSを再起動

ここまでの設定を反映するために389DSを再起動します。

# systemctl restart dirsrv.target

動作確認

さっそくポートを確認してみましょう。

# ss -lnt
State      Recv-Q Send-Q        Local Address:Port          Peer Address:Port 
LISTEN     0      128                       *:22                       *:*     
LISTEN     0      10                127.0.0.1:25                       *:*     
LISTEN     0      128                      :::389                     :::*     
LISTEN     0      128                      :::22                      :::*     
LISTEN     0      128                      :::636                     :::*  

636がListenされていますね。
次にldapsearchはどうでしょうか。

# ldapsearch -D "cn=Directory Manager" -w password -b dc=example,dc=demiglacesource,dc=net -ZZ *
# ldap_start_tls: Connect error (-11)
#   additional info: TLS error -8172:Peer's certificate issuer has been marked as not trusted by the user.

bindできませんね。。。

調べてみると、どうやら実行ユーザーのホームディレクトリに .ldaprc というファイルを保存しTLSを許可する設定が必要なようです。

# vim ~/.ldaprc
TLS_REQCERT allow

改めてldapsearchを行ってみます。

# ldapsearch -D "cn=Directory Manager" -w password -b dc=example,dc=demiglacesource,dc=net -ZZ *
# extended LDIF
#
# LDAPv3
# base <dc=example,dc=demiglacesource,dc=net> with scope subtree
# filter: (objectclass=*)
# requesting: cert8.db certmap.conf dse.ldif dse.ldif.bak dse.ldif.startOK dse_original.ldif key3.db pin.txt schema secmod.db slapd-collations.conf 
#

# example.demiglacesource.net
dn: dc=example,dc=demiglacesource,dc=net

[snip]

bindできました。

付録

この状態でbind時にldapsを必須化してみます。
まずはldifを作成しましょう。

# vim /tmp/require_ldaps.ldif
dn: cn=config
changetype: modify
replace: nsslapd-require-secure-binds
nsslapd-require-secure-binds: on

こいつをldapmodifyです。

# ldapmodify -x -D "cn=Directory Manager" -w password -f /tmp/require_ldaps.ldif
modifying entry "cn=config"

念のため389DSの再起動も行っておきましょう。

# systemctl restart dirsrv.target

これでldapsの状態でしか、389DSに対する操作を行えなくなりました。