requirettyが有効なイメージを使用する

基本的にVagrantはプロビジョニングを行う際sudoをつけてコマンドを実行しますが、Boxでrequirettyが有効になっていると作成した仮想マシン上でsudoをつけてコマンドが実行できずエラーが発生してしまいます。

クラウド系のプラグインはサービスプロバイダが提供しているイメージ・テンプレートをボックスとして使用するものが多いので、そのままではrequirettyが有効になっている場合があります。自分でrequirettyを無効化したイメージを作成し直すこともできますが、
既存のイメージを使用することができればサービスプロバイダが提供している様々なイメージが利用しやすくなります。

そこで今回はrequirettyが有効なイメージを使用する方法を調べてみます。

今回調べた方法はあくまで一時的にrequirettyを回避する方法です。何度も使用するイメージについてはrequirettyを無効化しておいた方が後で問題がでないと思います。

# TL;DR

* requirettyが有効なテンプレートでも```config.ssh.pty=true```とすることでsudoをすることができる。
* ログインユーザーがrootであれば```config.ssh.sudo_command```を使ってsudo無しでコマンドを実行するように設定できる
* Rsync Synced Folderを使う場合```rsync__rsync_path```でsudo無しでrsyncを実行するよう設定できる

# 環境

動作の確認は以下の環境で行いました。

* Vagrant (1.7.4)
* vagrnt-cloudstack (1.1.0)
* IDCFクラウド

# Vagrantfile

以下のVagrntfileをテスト用に使用します。

vagrant-cloudstackを使用してIDCFクラウド上にVMを作成しています。IDCFクラウドにはrequirettyが無効化されたVagrant用のテンプレートが用意されているのですが、ここではあえてVagrant用でないrequirettyが有効なテンプレートを使用します。

IDCFクラウドのテンプレートの仕様でデフォルトのログインユーザーがrootのため```override.ssh.username = "root"```でログインユーザーをrootとしてあります。また、後述するようにrequirettyが有効な場合Rsync Synced Folderにも影響があるのでいったん無効化しておきます。

```rb:Vagrantfile
Vagrant.configure("2") do |config|                                                 
  config.vm.provider :cloudstack do |cloudstack, override|                         
    override.vm.box = "dummy"                                                      
    override.vm.box_url = "https://github.com/schubergphilis/vagrant-cloudstack/raw/master/dummy.box"
                                                                                   
    cloudstack.host    = "compute.jp-east.idcfcloud.com"                           
    cloudstack.path    = "/client/api"                                             
    cloudstack.port    = "443"                                                     
    cloudstack.scheme  = "https"                                                   
                                                                                   
    cloudstack.api_key    = "#{ENV['CLOUDSTACK_API_KEY']}"                         
    cloudstack.secret_key = "#{ENV['CLOUDSTACK_SECRET_KEY']}"                      
                                                                                   
    cloudstack.zone_name             = "pascal"                                    
    cloudstack.template_name         = "CentOS 6.6 64-bit"                         
    cloudstack.service_offering_name = "light.S1"                                  
                                                                                   
    cloudstack.pf_ip_address   = "#{ENV['PUBLIC_IP_ADDRESS']}"                     
    cloudstack.pf_public_port  = 2222                                              
    cloudstack.pf_private_port = 22                                                
                                                                                   
    cloudstack.keypair            = "#{ENV['CLOUDSTACK_SSH_KEYPAIR']}"             
                                                                                   
    override.ssh.username         = "root"                                         
    override.ssh.host             = "#{ENV['PUBLIC_IP_ADDRESS']}"                  
    override.ssh.private_key_path = "#{ENV['VAGRANT_SSH_PRIVATE_KEY']}"                                         
  end                                                                                                                            
  
  # Synced Folderを無効化                                                                                 
  config.vm.synced_folder ".", "/vagrant", disabled: true
  
  # Shell Provisionerの実行                                                                                 
  config.vm.provision "shell",                                  
        inline: "echo Hello, World"                                                
end                                                                              
```

# Shell Provisionerを使う

## requiretty有効時のShell Provisionerのエラー

環境変数をセットして```vagrant up```（```vagrant provision```）を実行すると次のエラーが発生します。これはShell Provisionerがsudoでコマンドを実行しようした際にttyがないため発生しているエラーです。

```
==> default: Running provisioner: shell...
    default: Running: inline script
==> default: sudo
==> default: : 
==> default: sudo を実行するには tty がなければいけません。すみません
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.
```

## config.ssh.ptyをtrueにしてptyを使用してエラーを回避する

このエラーの回避方法の一つは、```config.ssh.pty```を```true```として、ptyを使用するようにすることです。

[config.ssh - Vagrantfile - Vagrant Documentation](http://docs.vagrantup.com/v2/vagrantfile/ssh_settings.html)

```rb:Vagrantfile
Vagrant.configure("2") do |config|                                                 
  config.vm.provider :cloudstack do |cloudstack, override|                         
    override.vm.box = "dummy"                                                      
    override.vm.box_url = "https://github.com/schubergphilis/vagrant-cloudstack/raw/master/dummy.box"
                                                                                   
    cloudstack.host    = "compute.jp-east.idcfcloud.com"                           
    cloudstack.path    = "/client/api"                                             
    cloudstack.port    = "443"                                                     
    cloudstack.scheme  = "https"                                                   
                                                                                   
    cloudstack.api_key    = "#{ENV['CLOUDSTACK_API_KEY']}"                         
    cloudstack.secret_key = "#{ENV['CLOUDSTACK_SECRET_KEY']}"                      
                                                                                   
    cloudstack.zone_name             = "pascal"                                    
    cloudstack.template_name         = "CentOS 6.6 64-bit"                         
    cloudstack.service_offering_name = "light.S1"                                  
                                                                                   
    cloudstack.pf_ip_address   = "#{ENV['PUBLIC_IP_ADDRESS']}"                     
    cloudstack.pf_public_port  = 2222                                              
    cloudstack.pf_private_port = 22                                                
                                                                                   
    cloudstack.keypair            = "#{ENV['CLOUDSTACK_SSH_KEYPAIR']}"             
                                                                                   
    override.ssh.username         = "root"                                         
    override.ssh.host             = "#{ENV['PUBLIC_IP_ADDRESS']}"                  
    override.ssh.private_key_path = "#{ENV['VAGRANT_SSH_PRIVATE_KEY']}" 
    override.ssh.pty              = true                                        
  end                                                                                                                            
  
  # Synced Folderを無効化                                                                                 
  config.vm.synced_folder ".", "/vagrant", disabled: true
  
  # Shell Provisionerの実行                                                                                 
  config.vm.provision "shell",                                  
        inline: "echo Hello, World"                                                
end                                                                              
```

```
==> default: Running provisioner: shell...
    default: Running: inline script
==> default: Hello, World
```

## config.ssh.sudo_commandでsudoを無効化する

今回の例ではrootでログインしているのでコマンドにsudoをつける必要がありません。
したがって、```config.ssh.sudo_command```を上書きしてsudo無しでコマンドを実行するように設定することでエラーを回避することもできます。

[config.ssh - Vagrantfile - Vagrant Documentation](http://docs.vagrantup.com/v2/vagrantfile/ssh_settings.html)

```rb:Vagrantfile
Vagrant.configure("2") do |config|                                                 
  config.vm.provider :cloudstack do |cloudstack, override|                         
    override.vm.box = "dummy"                                                      
    override.vm.box_url = "https://github.com/schubergphilis/vagrant-cloudstack/raw/master/dummy.box"
                                                                                   
    cloudstack.host    = "compute.jp-east.idcfcloud.com"                           
    cloudstack.path    = "/client/api"                                             
    cloudstack.port    = "443"                                                     
    cloudstack.scheme  = "https"                                                   
                                                                                   
    cloudstack.api_key    = "#{ENV['CLOUDSTACK_API_KEY']}"                         
    cloudstack.secret_key = "#{ENV['CLOUDSTACK_SECRET_KEY']}"                      
                                                                                   
    cloudstack.zone_name             = "pascal"                                    
    cloudstack.template_name         = "CentOS 6.6 64-bit"                         
    cloudstack.service_offering_name = "light.S1"                                  
                                                                                   
    cloudstack.pf_ip_address   = "#{ENV['PUBLIC_IP_ADDRESS']}"                     
    cloudstack.pf_public_port  = 2222                                              
    cloudstack.pf_private_port = 22                                                
                                                                                   
    cloudstack.keypair            = "#{ENV['CLOUDSTACK_SSH_KEYPAIR']}"             
                                                                                   
    override.ssh.username         = "root"                                         
    override.ssh.host             = "#{ENV['PUBLIC_IP_ADDRESS']}"                  
    override.ssh.private_key_path = "#{ENV['VAGRANT_SSH_PRIVATE_KEY']}"
    override.ssh.sudo_command     = "%c"                                      
  end                                                                                                                            
  
  # Synced Folderを無効化                                                                                 
  config.vm.synced_folder ".", "/vagrant", disabled: true
  
  # Shell Provisionerの実行                                                                                 
  config.vm.provision "shell",                             
        inline: "echo Hello, World"                                                
end 
```

## privilegedオプションをfalseにしてコマンドをsudo無しで実行する

sudo無しでコマンドを実行するように設定することは、Shell Provisionerの```privileged```オプションを```false```にすることによっても可能です。この方法でもエラーを回避することができます。

この設定はShell Provisionerのみに影響します。他のVagrantコマンドがsudoを使用している場合にはこの方法で対応することはできません。

```rb:Vagrantfile
Vagrant.configure("2") do |config|                                                 
  config.vm.provider :cloudstack do |cloudstack, override|                         
    override.vm.box = "dummy"                                                      
    override.vm.box_url = "https://github.com/schubergphilis/vagrant-cloudstack/raw/master/dummy.box"
                                                                                   
    cloudstack.host    = "compute.jp-east.idcfcloud.com"                           
    cloudstack.path    = "/client/api"                                             
    cloudstack.port    = "443"                                                     
    cloudstack.scheme  = "https"                                                   
                                                                                   
    cloudstack.api_key    = "#{ENV['CLOUDSTACK_API_KEY']}"                         
    cloudstack.secret_key = "#{ENV['CLOUDSTACK_SECRET_KEY']}"                      
                                                                                   
    cloudstack.zone_name             = "pascal"                                    
    cloudstack.template_name         = "CentOS 6.6 64-bit"                         
    cloudstack.service_offering_name = "light.S1"                                  
                                                                                   
    cloudstack.pf_ip_address   = "#{ENV['PUBLIC_IP_ADDRESS']}"                     
    cloudstack.pf_public_port  = 2222                                              
    cloudstack.pf_private_port = 22                                                
                                                                                   
    cloudstack.keypair            = "#{ENV['CLOUDSTACK_SSH_KEYPAIR']}"             
                                                                                   
    override.ssh.username         = "root"                                         
    override.ssh.host             = "#{ENV['PUBLIC_IP_ADDRESS']}"                  
    override.ssh.private_key_path = "#{ENV['VAGRANT_SSH_PRIVATE_KEY']}"                                        
  end                                                                                                                            
  
  # Synced Folderを無効化                                                                                 
  config.vm.synced_folder ".", "/vagrant", disabled: true
  
  # Shell Provisionerの実行                                                                                 
  config.vm.provision "shell", privileged: false,                              
        inline: "echo Hello, World"                                                
end                                                                              
```

# Rsync Synced Folderを使う

Rsync Synced Folderを使うためには作成した仮想マシンにディレクトリを作成し、ローカルのデータをコピーできるようにする必要があります。

vagrantユーザーで/vagrantを作成する等、一般ユーザーでは権限が不足している場合があるためディレクトリの作成等のコマンドはsudoを付けて実行されます。したがって、Shell Provisionerの場合と同様```config.ssh.pty```や```config.ssh.sudo_command```を設定しrequiretty有効時でもコマンドを実行できるようにしておく必要があります。(Shell ProvisionernのprivilegedオプションはShell Provisionerの動作にのみ影響するのでSynced Folderを使うためには使用できません。）

例えば、```config.ssh.pty=true```とした状態でSynced Folderを有効化してみます。

```rb:Vagrantfile
Vagrant.configure("2") do |config|                                                 
  config.vm.provider :cloudstack do |cloudstack, override|                         
    override.vm.box = "dummy"                                                      
    override.vm.box_url = "https://github.com/schubergphilis/vagrant-cloudstack/raw/master/dummy.box"
                                                                                   
    cloudstack.host    = "compute.jp-east.idcfcloud.com"                           
    cloudstack.path    = "/client/api"                                             
    cloudstack.port    = "443"                                                     
    cloudstack.scheme  = "https"                                                   
                                                                                   
    cloudstack.api_key    = "#{ENV['CLOUDSTACK_API_KEY']}"                         
    cloudstack.secret_key = "#{ENV['CLOUDSTACK_SECRET_KEY']}"                      
                                                                                   
    cloudstack.zone_name             = "pascal"                                    
    cloudstack.template_name         = "CentOS 6.6 64-bit"                         
    cloudstack.service_offering_name = "light.S1"                                  
                                                                                   
    cloudstack.pf_ip_address   = "#{ENV['PUBLIC_IP_ADDRESS']}"                     
    cloudstack.pf_public_port  = 2222                                              
    cloudstack.pf_private_port = 22                                                
                                                                                   
    cloudstack.keypair            = "#{ENV['CLOUDSTACK_SSH_KEYPAIR']}"             
                                                                                   
    override.ssh.username         = "root"                                         
    override.ssh.host             = "#{ENV['PUBLIC_IP_ADDRESS']}"                  
    override.ssh.private_key_path = "#{ENV['VAGRANT_SSH_PRIVATE_KEY']}" 
    override.ssh.pty              = true                                        
  end                                                                                                                            
  
  # Shell Provisionerの実行                                                                                 
  config.vm.provision "shell",                                  
        inline: "echo Hello, World"                                                
end                                                                              
```

しかし、これを実行すると次のようなエラーとなります。

```
==> default: Rsyncing folder: /Users/atsaki/gitrepos/vagrant-examples/centos/ => /vagrant
There was an error when attempting to rsync a synced folder.
Please inspect the error message below for more info.

Host path: /Users/atsaki/gitrepos/vagrant-examples/centos/
Guest path: /vagrant
Command: rsync --verbose --archive --delete -z --copy-links --no-owner --no-group --rsync-path sudo rsync -e ssh -p 2222 -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null -i '/Users/atsaki/.ssh/id_rsa_idcfcloud' --exclude .vagrant/ /Users/atsaki/gitrepos/vagrant-examples/centos/ root@xxx.xxx.xxx.xxx:/vagrant
Error: Warning: Permanently added '[xxx.xxx.xxx.xxx]:2222' (RSA) to the list of known hosts.
sudo: sudo を実行するには tty がなければいけません。すみません
rsync: connection unexpectedly closed (0 bytes received so far) [sender]
rsync error: error in rsync protocol data stream (code 12) at /SourceCache/rsync/rsync-42/rsync/io.c(452) [sender=2.6.9]
```

このエラーはrsyncコマンドの```--rsync-path "sudo rsync"```のsudoがttyなしで実行されるため発生しています。

## rsync__rsync_pathで--rsync-pathからsudoを除く

ログインユーザがrootの場合、--rsync-pathのsudoは不要なので```rsync__rsync_path```オプションで```--rsync-path```からsudoを除くことでエラーを回避することができます。

ログインユーザーがrootでない場合でも権限があるディレクトリをSynced Folderにする場合には同様の方法で対応できると思います。

```rb:Vagrantfile
Vagrant.configure("2") do |config|                                                 
  config.vm.provider :cloudstack do |cloudstack, override|                         
    override.vm.box = "dummy"                                                      
    override.vm.box_url = "https://github.com/schubergphilis/vagrant-cloudstack/raw/master/dummy.box"
                                                                                   
    cloudstack.host    = "compute.jp-east.idcfcloud.com"                           
    cloudstack.path    = "/client/api"                                             
    cloudstack.port    = "443"                                                     
    cloudstack.scheme  = "https"                                                   
                                                                                   
    cloudstack.api_key    = "#{ENV['CLOUDSTACK_API_KEY']}"                         
    cloudstack.secret_key = "#{ENV['CLOUDSTACK_SECRET_KEY']}"                      
                                                                                   
    cloudstack.zone_name             = "pascal"                                    
    cloudstack.template_name         = "CentOS 6.6 64-bit"                         
    cloudstack.service_offering_name = "light.S1"                                  
                                                                                   
    cloudstack.pf_ip_address   = "#{ENV['PUBLIC_IP_ADDRESS']}"                     
    cloudstack.pf_public_port  = 2222                                              
    cloudstack.pf_private_port = 22                                                
                                                                                   
    cloudstack.keypair            = "#{ENV['CLOUDSTACK_SSH_KEYPAIR']}"             
                                                                                   
    override.ssh.username         = "root"                                         
    override.ssh.host             = "#{ENV['PUBLIC_IP_ADDRESS']}"                  
    override.ssh.private_key_path = "#{ENV['VAGRANT_SSH_PRIVATE_KEY']}"            
    override.ssh.pty              = true                                           
  end                                                                              
                                                                                   
  # rsync__rsync_pathで--rsync-pathからsudoを除く                                                  
  config.vm.synced_folder ".", "/vagrant", type: "rsync",                          
    rsync__rsync_path: "rsync"                                                     
                                                                                   
  # Shell Provisionerの実行                                                        
  config.vm.provision "shell",                                                     
        inline: "echo Hello, World"                                                
end                           
```

# 参考資料

* [Creating a Base Box - Vagrant Documentation](http://docs.vagrantup.com/v2/boxes/base.html)
* [VagrantでIDCFクラウド、Cloud n、ALTUSを使ってみる - Qiita](http://qiita.com/atsaki/items/f12e82f109193321fb13)