vagrant#precise32:/vagrant$ rails s
=> Booting WEBrick
=> Rails 4.1.0 application starting in development on http://0.0.0.0:3000
=> Run `rails server -h` for more startup options
=> Notice: server is listening on all interfaces (0.0.0.0). Consider using 127.0.0.1 (--binding option)
=> Ctrl-C to shutdown server
[2014-08-24 23:33:39] INFO WEBrick 1.3.1
[2014-08-24 23:33:39] INFO ruby 2.1.2 (2014-05-08) [i686-linux]
[2014-08-24 23:33:39] INFO WEBrick::HTTPServer#start: pid=18812 port=3000
In chrome I'm getting the "This webpage is not available" error.
I follow this tutorial to setup Vagrant with Rails - http://tutorials.jumpstartlab.com/topics/vagrant_setup.html
What could be wrong here?
Edit:
Vagrantfile
# -*- mode: ruby -*-
# vi: set ft=ruby :
# Vagrantfile API/syntax version. Don't touch unless you know what you're doing!
VAGRANTFILE_API_VERSION = "2"
Vagrant.configure(VAGRANTFILE_API_VERSION) do |config|
# All Vagrant configuration is done here. The most common configuration
# options are documented and commented below. For a complete reference,
# please see the online documentation at vagrantup.com.
# Every Vagrant virtual environment requires a box to build off of.
config.vm.box = "hashicorp/precise32"
# Disable automatic box update checking. If you disable this, then
# boxes will only be checked for updates when the user runs
# `vagrant box outdated`. This is not recommended.
# config.vm.box_check_update = false
# Create a forwarded port mapping which allows access to a specific port
# within the machine from a port on the host machine. In the example below,
# accessing "localhost:8080" will access port 80 on the guest machine.
config.vm.network "forwarded_port", guest: 80, host: 3000
# Create a private network, which allows host-only access to the machine
# using a specific IP.
# config.vm.network "private_network", ip: "192.168.33.10"
# Create a public network, which generally matched to bridged network.
# Bridged networks make the machine appear as another physical device on
# your network.
# config.vm.network "public_network"
# If true, then any SSH connections made will enable agent forwarding.
# Default value: false
# config.ssh.forward_agent = true
# Share an additional folder to the guest VM. The first argument is
# the path on the host to the actual folder. The second argument is
# the path on the guest to mount the folder. And the optional third
# argument is a set of non-required options.
# config.vm.synced_folder "../data", "/vagrant_data"
# Provider-specific configuration so you can fine-tune various
# backing providers for Vagrant. These expose provider-specific options.
# Example for VirtualBox:
#
# config.vm.provider "virtualbox" do |vb|
# # Don't boot with headless mode
# vb.gui = true
#
# # Use VBoxManage to customize the VM. For example to change memory:
# vb.customize ["modifyvm", :id, "--memory", "1024"]
# end
#
# View the documentation for the provider you're using for more
# information on available options.
# Enable provisioning with CFEngine. CFEngine Community packages are
# automatically installed. For example, configure the host as a
# policy server and optionally a policy file to run:
#
# config.vm.provision "cfengine" do |cf|
# cf.am_policy_hub = true
# # cf.run_file = "motd.cf"
# end
#
# You can also configure and bootstrap a client to an existing
# policy server:
#
# config.vm.provision "cfengine" do |cf|
# cf.policy_server_address = "10.0.2.15"
# end
# Enable provisioning with Puppet stand alone. Puppet manifests
# are contained in a directory path relative to this Vagrantfile.
# You will need to create the manifests directory and a manifest in
# the file default.pp in the manifests_path directory.
#
# config.vm.provision "puppet" do |puppet|
# puppet.manifests_path = "manifests"
# puppet.manifest_file = "site.pp"
# end
# Enable provisioning with chef solo, specifying a cookbooks path, roles
# path, and data_bags path (all relative to this Vagrantfile), and adding
# some recipes and/or roles.
#
# config.vm.provision "chef_solo" do |chef|
# chef.cookbooks_path = "../my-recipes/cookbooks"
# chef.roles_path = "../my-recipes/roles"
# chef.data_bags_path = "../my-recipes/data_bags"
# chef.add_recipe "mysql"
# chef.add_role "web"
#
# # You may also specify custom JSON attributes:
# chef.json = { mysql_password: "foo" }
# end
# Enable provisioning with chef server, specifying the chef server URL,
# and the path to the validation key (relative to this Vagrantfile).
#
# The Opscode Platform uses HTTPS. Substitute your organization for
# ORGNAME in the URL and validation key.
#
# If you have your own Chef Server, use the appropriate URL, which may be
# HTTP instead of HTTPS depending on your configuration. Also change the
# validation key to validation.pem.
#
# config.vm.provision "chef_client" do |chef|
# chef.chef_server_url = "https://api.opscode.com/organizations/ORGNAME"
# chef.validation_key_path = "ORGNAME-validator.pem"
# end
#
# If you're using the Opscode platform, your validator client is
# ORGNAME-validator, replacing ORGNAME with your organization name.
#
# If you have your own Chef Server, the default validation client name is
# chef-validator, unless you changed the configuration.
#
# chef.validation_client_name = "ORGNAME-validator"
end
Post your Vagrantfile, but you're probably missing the port forwarding configuration for port 3000.
Update: not missing, just wrong. Change both port options to 3000 and you should be good.
Related
I've inherited a Rails application that isn't completely functional when running in production mode. GET requests to the server are resulting in a no route match found error; however, when the server is run in development mode all routes will work, giving an expected 200 status.
Reviewing the code reveals that the application expects a prefixed subdomain in addition to the domain used in a successful URL request.
class ApplicationContext
def initialize(subdomain, host_with_port)
if subdomain.present?
case subdomain
when Rails.application.config.workninja.admin_subdomain
#environment = :admin
when Rails.application.config.workninja.mobile_subdomain
#environment = :mobile
else
#environment = :customer
end
else
raise 'Could not initialize ApplicationContext without subdomain'
end
#url_options = {subdomain: subdomain, host: host_with_port}
setup_method = "setup_#{#environment.to_s}_environment".to_sym
if respond_to?(setup_method, true)
send(setup_method, subdomain, host_with_port)
else
raise 'Unknown context environment'
end
end
attr_reader :environment
attr_reader :url_options
attr_reader :agency
def self.current
Thread.current['workninja:tenant_context']
end
def admin?
#environment == :admin
end
def mobile?
#environment == :mobile
end
def customer?
#environment == :customer
end
def ui_url_for(path, subdomain = url_options[:subdomain])
base = "#{Rails.application.config.workninja.https ? 'https' :
'http'}://#{subdomain}.#{Rails.application.config.workninja.domain}"
if Rails.application.config.workninja.html5mode
puts URI.join(base, path).to_s
else
puts URI.join(base, "/#/#{path}".gsub(/\/+/, '/')).to_s
end
end
The original front end supplied with the application constructs the request's URLs depending on the environment the sever was launched in.
{
"environment": "development",
"url": "http://admin.workninja.local:3000"
}
{
"environment": "production",
"url": "/api"
}
To me the production URL doesn't make sense as all it does is append "/api" to the root domain that the front end is hosted on. I can only assume that it's just a placeholder that needs to be replaced with the domain name that the rails server is hosted on once it's running in a live environment. The "/api" path isn't used throughout the functional development version of the app which makes me further assume it's a placeholder.
Using the above as a guide I replaced "/api" with "http://admin.workninja.com.au". After hosting the application on a live domain I confirmed it was working by running:
curl http://admin.workninja.com.com.au/auth -X POST
This gave me an expected error about credentials not being supplied but it shows that the server is actually receiving something. If you haven't realised the rails server when launched in production mode will respond to a POST request but still not a GET.
This is where my understanding of the problem breaks down. If
http://admin.workninja.local:3000/roles
works ("/roles being one of the applications routes") in a development environment why doesn't
http://admin.workninja.com.au/roles
work in a production environment as well? Can you assume from this fact that something isn't broken in the ruby codebase?
Below are some of the files relating to the configuration of the rails application in a production environment.
/config/deploy/production.rb
set :branch, 'master'
server 'ec2-54-66-230-174.ap-southeast-2.compute.amazonaws.com', user: 'ubuntu', roles: %w{app web db worker}
/config/environments/production.rb
Rails.application.configure do
# Settings specified here will take precedence over those in config/application.rb.
# Code is not reloaded between requests.
config.cache_classes = true
# Eager load code on boot. This eager loads most of Rails and
# your application in memory, allowing both threaded web servers
# and those relying on copy on write to perform better.
# Rake tasks automatically ignore this option for performance.
# This needs to be set to true in order for rails to launch in a production environment
config.eager_load = false
# Full error reports are disabled and caching is turned on.
config.consider_all_requests_local = false
config.action_controller.perform_caching = true
# Enable Rack::Cache to put a simple HTTP cache in front of your application
# Add `rack-cache` to your Gemfile before enabling this.
# For large-scale production use, consider using a caching reverse proxy like
# NGINX, varnish or squid.
# config.action_dispatch.rack_cache = true
# Disable serving static files from the `/public` folder by default since
# Apache or NGINX already handles this.
config.serve_static_files = ENV['RAILS_SERVE_STATIC_FILES'].present?
# Specifies the header that your server uses for sending files.
# config.action_dispatch.x_sendfile_header = 'X-Sendfile' # for Apache
# config.action_dispatch.x_sendfile_header = 'X-Accel-Redirect' # for NGINX
# Force all access to the app over SSL, use Strict-Transport-Security, and use secure cookies.
# config.force_ssl = true
# Use the lowest log level to ensure availability of diagnostic information
# when problems arise.
config.log_level = :warn
# Prepend all log lines with the following tags.
# config.log_tags = [ :subdomain, :uuid ]
# Use a different logger for distributed setups.
# config.logger = ActiveSupport::TaggedLogging.new(SyslogLogger.new)
# Use a different cache store in production.
# config.cache_store = :mem_cache_store
# Enable serving of images, stylesheets, and JavaScripts from an asset server.
# config.action_controller.asset_host = 'http://assets.example.com'
# Ignore bad email addresses and do not raise email delivery errors.
# Set this to true and configure the email server for immediate delivery to raise delivery errors.
# config.action_mailer.raise_delivery_errors = false
# Enable locale fallbacks for I18n (makes lookups for any locale fall back to
# the I18n.default_locale when a translation cannot be found).
config.i18n.fallbacks = true
# Send deprecation notices to registered listeners.
config.active_support.deprecation = :notify
# Use default logging formatter so that PID and timestamp are not suppressed.
config.log_formatter = ::Logger::Formatter.new
# Do not dump schema after migrations.
config.active_record.dump_schema_after_migration = false
# Application hostname
config.surgeforce.domain = 'surgeforce.com.au'
config.surgeforce.https = false
config.surgeforce.html5mode = true
end
/config/puma.rb
threads 1, 6
workers Integer(ENV['PUMA_WORKERS'] || 3)
on_worker_boot do
require "active_record"
cwd = File.dirname(__FILE__)+"/.."
ActiveRecord::Base.connection.disconnect! rescue ActiveRecord::ConnectionNotEstablished
ActiveRecord::Base.establish_connection(ENV["DATABASE_URL"] || YAML.load_file("#{cwd}/config/database.yml")[ENV["RAILS_ENV"]])
end
If you believe any other piece of the applications code to be critical in the investigation let me know and I'll include it.
The problem is that Rails' subdomain method is very simple and doesn't know anything about the structure of com.au domains. For "admin.workninja.com.au" which you use on production, the subdomain method would return "admin.workninja". From the docs:
Returns all the \subdomains as a string, so "dev.www" would be
returned for "dev.www.rubyonrails.org". You can specify a different tld_length, such as 2 to catch "www" instead of "www.rubyonrails" in "www.rubyonrails.co.uk".
And – without knowing your configuration– "admin.workninja" will very likely not match your config.workninja.admin_subdomain configuration anymore.
The solution is to configure a tld length of 2 on production. Just add the following to the configuration block in your config/environments/production.rb:
config.action_dispatch.tld_length = 2 # Defaults to 1
I'm trying to build a simple redis cluster locally on OSX, using docker inside virtualbox VMS. Specifically, I want to create a master redis node (192.168.0.100) with two slaves (.101 and .102). This involves configuring each machine and issuing a command to each slave to tell it who the master is in the key-value-store relationship.
This worked great, until I decided to make my configuration more flexible by doing some programmatic stuff inside my Vagrantfile. The result is that I am unable to even ping any of the machines beside .102, let alone uses redis-cli commands (for example, redis-cli -h 192.168.0.100 INFO to see that the master has slaves set up correctly)
My goals/constraints:
Run docker inside a virtualbox VM: I saw several ways to do this when reading the docs. I chose the way I do it because I don't want to refer to a ton of external files.
Run a sequence of commands on each VM after configuration to set slaves and whatnot.
I'd prefer not to just use boot2docker as I have a bunch other stuff I'm gonna be adding... twemproxy, sentinel and I want to simulate a production environment at least a little bit.
What am I doing wrong in my new file?
EDIT: I've investigated a bit more. One thing I tried was destroying all images, then running the old vagrant file to build the machines, and then reloading them with the new vagrant file. In this scenario, the machines worked, though each time I'd see authentication failure messages like redis1: Warning: Authentication failure. Retrying... after the machine had gone up. I was able to ping the machine no problem though.
New Vagrantfile (uses config file below):
require './config.rb'
include RedisClusterConfig
Vagrant.configure(2) do |config|
config.vm.provider "virtualbox"
## Redis Nodes (Can define multiple triplets) ##
for node in RedisClusterConfig.redisNodes
config.vm.define node.name do |a|
a.vm.hostname = node.name
a.vm.box = "ubuntu/trusty64"
# Skip checking for an updated Vagrant box
a.vm.box_check_update = false
# Networking
a.vm.network :private_network, ip: node.address
a.ssh.forward_agent = true
a.ssh.insert_key = false
# Set up docker with 'redis' image
a.vm.provision "docker" do |d|
d.images = ["redis"]
d.run "redis", name: "redis", args: "-p "+node.port+":"+node.port
end
# Additional configuration
a.trigger.after [:up, :resume, :reload, :provision] do
# Master/slave
unless node.slaveOfAddress.nil?
system("redis-cli -h " + node.address + " SLAVEOF " + node.slaveOfAddress + " " + node.slaveOfPort)
puts "Finished setting up slave node: " + node.name
else
puts "Finished setting up master node: " + node.name
end
end
end
end
end
config.rb:
module RedisClusterConfig
attr_reader :redisNodes
RedisNode = Struct.new("RedisNode", :name, :address, :port, :slaveOfAddress, :slaveOfPort)
p = "6379" # Default redis port
baseAddr = "192.168.0."
# Define a triplet
redis0 = RedisNode.new("redis0", baseAddr + "100", p, nil, nil)
redis1 = RedisNode.new("redis1", baseAddr + "101", p, baseAddr + "100", p)
redis2 = RedisNode.new("redis2", baseAddr + "102", p, baseAddr + "100", p)
#redisNodes = [redis0, redis1, redis2]
end
Old Vagrantfile:
# Make sure you have triggers plugin:
# vagrant plugin install vagrant-triggers
#
# TODO:
# - We really would like to run some shell commands after everything is done, not after each machine is provisioned
Vagrant.configure(2) do |config|
config.vm.provider "virtualbox"
## Redis Nodes ##
### Master Node###
config.vm.define "redis0" do |a|
a.vm.hostname = "redis0"
a.vm.box = "ubuntu/trusty64"
# Skip checking for an updated Vagrant box
a.vm.box_check_update = false
# Networking
a.vm.network :private_network, ip: "192.168.0.100"
a.ssh.forward_agent = true
a.ssh.insert_key = false
a.vm.provision "docker" do |d|
d.images = ["redis"]
d.run "redis",
name: "redis",
args: "-p 6379:6379"
end
end
### Slave Nodes ###
config.vm.define "redis1" do |a|
a.vm.hostname = "redis1"
a.vm.box = "ubuntu/trusty64"
# Skip checking for an updated Vagrant box
a.vm.box_check_update = false
# Networking
a.vm.network :private_network, ip: "192.168.0.101"
a.ssh.forward_agent = true
a.ssh.insert_key = false
a.vm.provision "docker" do |d|
d.images = ["redis"]
d.run "redis",
name: "redis",
args: "-p 6379:6379"
end
end
config.vm.define "redis2" do |a|
a.vm.hostname = "redis2"
a.vm.box = "ubuntu/trusty64"
# Skip checking for an updated Vagrant box
a.vm.box_check_update = false
# Networking
a.vm.network :private_network, ip: "192.168.0.102"
a.ssh.forward_agent = true
a.ssh.insert_key = false
a.vm.provision "docker" do |d|
d.images = ["redis"]
d.run "redis",
name: "redis",
args: "-p 6379:6379"
end
end
# After everything else has finished provisioning...
config.trigger.after [:up, :resume, :reload] do
# Configuring redis master/slave triplet
system('echo Configuring redis master/slave triplet...')
system('redis-cli -h 192.168.0.101 SLAVEOF 192.168.0.100 6379')
system('redis-cli -h 192.168.0.102 SLAVEOF 192.168.0.100 6379')
end
end
Here's my Vagrantfile:
# -*- mode: ruby -*-
# vi: set ft=ruby :
Vagrant.configure(2) do |config|
config.vm.box = "ubuntu-14.04-x64"
# Sync'd folders
config.vm.synced_folder ".", "/vagrant", disabled: true
config.vm.synced_folder "~/work", "/home/vagrant/work", create: true
config.vm.synced_folder "~/apt-archives", "/var/cache/apt/archives/", create: true
# Ubuntu VM
config.vm.define "ubuntu" do |ubuntu|
ubuntu.vm.provision "shell", path: "provision.sh", privileged: false
ubuntu.vm.network "forwarded_port", guest: 3000, host: 8080 # http
ubuntu.vm.network "private_network", ip: "10.20.30.100"
ubuntu.vm.hostname = "ubuntu"
# VirtualBox Specific Stuff
# https://www.virtualbox.org/manual/ch08.html
config.vm.provider "virtualbox" do |vb|
# Set more RAM
vb.customize ["modifyvm", :id, "--memory", "2048"]
# More CPU Cores
vb.customize ["modifyvm", :id, "--cpus", "2"]
end # End config.vm.provider virtualbox
end # End config.vm.define ubuntu
end
For example, when I run rails app using port 3000, from the guest machine I would accessing http://localhost:3000.
But I'm trying to access the app via host's browser.
None of below worked:
http://10.20.30.100:8080
https://10.20.30.100:8080
http://10.20.30.100:3000
https://10.20.30.100:3000
Browser on the host's showing: ERR_CONNECTION_REFUSED
For security reasons, Rails 4.2 limits remote access while in development mode. This is done by binding the server to 'localhost' rather than '0.0.0.0' ....
To access Rails working on a VM (such as one created by Vagrant), you need to change the default Rails IP binding back to '0.0.0.0'.
See the answers on the following StackOverflow Question, there are a number of different approaches suggested.
The idea is to get Rails running either by forcing the following command:
rails s -b 0.0.0.0
Or by hardcoding the binding into the Rails app (which I found less desirable):
# add this to config/boot.rb
require 'rails/commands/server'
module Rails
class Server
def default_options
super.merge(Host: '0.0.0.0')
end
end
end
Personally, I would have probably gone with the suggestion to use foreman and a Procfile:
# Procfile in Rails application root
web: bundle exec rails s -b 0.0.0.0
This would allow, I believe, for better easier deployment synchronicity.
I have ruby 2.1.5 installed on Ubuntu with Vagrant 1.7
I ran:
~$ vagrant plugin install vagrant-env
as described at gosuri/vagrant-env.
I should also mention that this now only works when I add:
require 'dotenv'
Dotenv.load
which the doc (README) does not specify to implement into the Vagrantfile. If I leave this out then I get an error saying that the line with:
config.env.enable
is an unknown configuration section.
I have my .env file in the same directory as Vagrantfile. No matter what I have in the file, it errors on the first line. I can leave it blank or make it a comment and it will still say the first line is incorrect format.
I am wanting to use this because it is the only solution I can find that will allow me to pass ENV variables to the Vagrant file as well as the manifest files Puppet.
Here is a snippet of my Vagrantfile
# -*- mode: ruby -*-
# vi: set ft=ruby :
require 'dotenv'
Dotenv.load
Vagrant.configure("2") do |config|
config.env.enable
#config.dotenv.enabled = true
config.vm.provider "virtualbox" do |vb|
vb.customize ["modifyvm", :id, "--memory", "#{ENV['MEMORY_ALLOCATION']}"]
end
config.vm.network "public_network", ip: ENV['PUBLIC_IP']
end
Vagrant::Config.run do |config|
...
config.vm.define "devboxserver" do |app|
...
app.vm.provision :puppet do |puppet|
puppet.facter = {
"env" => ENV
}
puppet.module_path = "puppet/modules"
puppet.manifests_path = "puppet/manifests"
puppet.manifest_file = "site.pp"
puppet.options="--verbose --debug"
end
end
end
As you can see, I also tried vagrant-dotenv (which you see is commented out) but that was also causing me problems as it was trying to call on config variables before the Vagrantfile was even loaded. But I guess that is another issue. I had to remove that gem from the .vagrant.d directory.
But getting back to the vagrant-env issue, I wrote my .env file as with what I could tell from the github plugin site and the referenced article
USER_NAME=jason
PUBLIC_IP=192.168.1.00
MEMORY_ALLOCATION=1024
GITREPO_ACCOUNT_URL=https://github.com/me
GITREPO_URL=https://github.com/me/my_app
POSTGRES_PASSWORD=secret-pass
DEV_DB_NAME=my_app_development
PROD_DB_NAME=my_app_production
TEST_DB_NAME=my_app_test
And the error I get is:
jason#host:~/folder/vagrant$ vagrant up
There is a syntax error in the following Vagrantfile. The syntax error
message is reproduced below for convenience:
Line "USER_NAME=jason" doesn't match format
Since the goal is to create environmental variables throughout my vagrant execution and share with Puppet, I did what any sane rubyist (or programmer) would do. I created my own simple hack by grabbing the .env variables and turning them into a hash.
I added this hack to the top of my Vagrant file:
# -*- mode: ruby -*-
# vi: set ft=ruby :
env = {}
File.read(".env").split("\n").each do |ef|
env[ef.split("=")[0]] = ef.split("=")[1]
end
Vagrant.configure("2") do |config|
config.vm.provider "virtualbox" do |vb|
vb.customize ["modifyvm", :id, "--memory", env['MEMORY_ALLOCATION']]
end
config.vm.network "public_network", ip: env['PUBLIC_IP']
end
...
However, since the Puppet files aren't really ruby files, using puppet.facter to grab to set the "env" variable ($env) to a hash does not work since we are talking about two different language constructs.
Instead, I just extended the puppet.facter list and instead of one $env hash I now have a puppet.facter variable set for all the .env variables.
...
app.vm.provision :puppet do |puppet|
puppet.facter = {
"fqdn" => "#{env['BOX_NAME']}.local",
"user_name" => env['USER_NAME'],
"box_name" => env['BOX_NAME'],
"gitrepo_account_url" => env['GITREPO_ACCOUNT_URL'],
"gitrepo_url" => env['GITREPO_URL'],
"postgres_password" => env['POSTGRES_PASSWORD'],
"dev_db_name" => env['DEV_DB_NAME'],
"prod_db_name" => env['PROD_DB_NAME'],
"test_db_name" => env['TEST_DB_NAME'],
"my_email" => env['MY_EMAIL']
}
puppet.module_path = "puppet/modules"
puppet.manifests_path = "puppet/manifests"
puppet.manifest_file = "site.pp"
puppet.options="--verbose --debug"
end
...
In my puppet scripts I just call variables like normal. EX: $user_name, $dev_db_name, etc.
I then removed vagrant-env as it is no longer needed.
I am trying to package new vagrant base box. And I get this error while doing so.
C:/HashiCorp/Vagrant/embedded/gems/gems/vagrant-1.5.1/plugins/commands/package/command.rb:59:in `package_base': C:/HashiCorp/Vagrant/embedded/gems/gems/vagrant-1.5.1/lib/vagrant/machine.rb:358: syntax error, unexpected end-of-input, expecting keyword_end (SyntaxError)
from C:/HashiCorp/Vagrant/embedded/gems/gems/vagrant1.5.1/plugins/commands/package/command.rb:42:in `execute'
from C:/HashiCorp/Vagrant/embedded/gems/gems/vagrant-1.5.1/lib/vagrant/cli.rb:42:in `execute'
from C:/HashiCorp/Vagrant/embedded/gems/gems/vagrant-1.5.1/lib/vagrant/environment.rb:248:in `cli'
from C:/HashiCorp/Vagrant/embedded/gems/gems/vagrant-1.5.1/bin/vagrant:158:in `<main>'
The content of "machine.rb" is below.
require "thread"
require "log4r"
module Vagrant
# This represents a machine that Vagrant manages. This provides a singular
# API for querying the state and making state changes to the machine, which
# is backed by any sort of provider (VirtualBox, VMWare, etc.).
class Machine
# The box that is backing this machine.
#
# #return [Box]
attr_accessor :box
# Configuration for the machine.
#
# #return [Object]
attr_accessor :config
# Directory where machine-specific data can be stored.
#
# #return [Pathname]
attr_reader :data_dir
# The environment that this machine is a part of.
#
# #return [Environment]
attr_reader :env
# ID of the machine. This ID comes from the provider and is not
# guaranteed to be of any particular format except that it is
# a string.
#
# #return [String]
attr_reader :id
# Name of the machine. This is assigned by the Vagrantfile.
#
# #return [Symbol]
attr_reader :name
# The provider backing this machine.
#
# #return [Object]
attr_reader :provider
# The provider-specific configuration for this machine.
#
# #return [Object]
attr_accessor :provider_config
# The name of the provider.
#
# #return [Symbol]
attr_reader :provider_name
# The options given to the provider when registering the plugin.
#
# #return [Hash]
attr_reader :provider_options
# The UI for outputting in the scope of this machine.
#
# #return [UI]
attr_reader :ui
# The Vagrantfile that this machine is attached to.
#
# #return [Vagrantfile]
attr_reader :vagrantfile
# Initialize a new machine.
#
# #param [String] name Name of the virtual machine.
# #param [Class] provider The provider backing this machine. This is
# currently expected to be a V1 `provider` plugin.
# #param [Object] provider_config The provider-specific configuration for
# this machine.
# #param [Hash] provider_options The provider-specific options from the
# plugin definition.
# #param [Object] config The configuration for this machine.
# #param [Pathname] data_dir The directory where machine-specific data
# can be stored. This directory is ensured to exist.
# #param [Box] box The box that is backing this virtual machine.
# #param [Environment] env The environment that this machine is a
# part of.
def initialize(name, provider_name, provider_cls, provider_config, provider_options, config, data_dir, box, env, vagrantfile, base=false)
#logger = Log4r::Logger.new("vagrant::machine")
#logger.info("Initializing machine: #{name}")
#logger.info(" - Provider: #{provider_cls}")
#logger.info(" - Box: #{box}")
#logger.info(" - Data dir: #{data_dir}")
#box = box
#config = config
#data_dir = data_dir
#env = env
#vagrantfile = vagrantfile
#guest = Guest.new(
self,
Vagrant.plugin("2").manager.guests,
Vagrant.plugin("2").manager.guest_capabilities)
#name = name
#provider_config = provider_config
#provider_name = provider_name
#provider_options = provider_options
#ui = Vagrant::UI::Prefixed.new(#env.ui, #name)
#ui_mutex = Mutex.new
# Read the ID, which is usually in local storage
#id = nil
# XXX: This is temporary. This will be removed very soon.
if base
#id = name
else
# Read the id file from the data directory if it exists as the
# ID for the pre-existing physical representation of this machine.
id_file = #data_dir.join("id")
#id = id_file.read.chomp if id_file.file?
end
# Initializes the provider last so that it has access to all the
# state we setup on this machine.
#provider = provider_cls.new(self)
#provider._initialize(#provider_name, self)
end
# This calls an action on the provider. The provider may or may not
# actually implement the action.
#
# #param [Symbol] name Name of the action to run.
# #param [Hash] extra_env This data will be passed into the action runner
# as extra data set on the environment hash for the middleware
# runner.
def action(name, extra_env=nil)
#logger.info("Calling action: #{name} on provider #{#provider}")
# Get the callable from the provider.
callable = #provider.action(name)
# If this action doesn't exist on the provider, then an exception
# must be raised.
if callable.nil?
raise Errors::UnimplementedProviderAction,
:action => name,
:provider => #provider.to_s
end
# Run the action with the action runner on the environment
env = {
:action_name => "machine_action_#{name}".to_sym,
:machine => self,
:machine_action => name,
:ui => #ui
}.merge(extra_env || {})
#env.action_runner.run(callable, env)
end
# Returns a communication object for executing commands on the remote
# machine. Note that the _exact_ semantics of this are up to the
# communication provider itself. Despite this, the semantics are expected
# to be consistent across operating systems. For example, all linux-based
# systems should have similar communication (usually a shell). All
# Windows systems should have similar communication as well. Therefore,
# prior to communicating with the machine, users of this method are
# expected to check the guest OS to determine their behavior.
#
# This method will _always_ return some valid communication object.
# The `ready?` API can be used on the object to check if communication
# is actually ready.
#
# #return [Object]
def communicate
if !#communicator
# For now, we always return SSH. In the future, we'll abstract
# this and allow plugins to define new methods of communication.
klass = Vagrant.plugin("2").manager.communicators[:ssh]
#communicator = klass.new(self)
end
#communicator
end
# Returns a guest implementation for this machine. The guest implementation
# knows how to do guest-OS specific tasks, such as configuring networks,
# mounting folders, etc.
#
# #return [Guest]
def guest
raise Errors::MachineGuestNotReady if !communicate.ready?
#guest.detect! if !#guest.ready?
#guest
end
# This sets the unique ID associated with this machine. This will
# persist this ID so that in the future Vagrant will be able to find
# this machine again. The unique ID must be absolutely unique to the
# virtual machine, and can be used by providers for finding the
# actual machine associated with this instance.
#
# **WARNING:** Only providers should ever use this method.
#
# #param [String] value The ID.
def id=(value)
#logger.info("New machine ID: #{value.inspect}")
# The file that will store the id if we have one. This allows the
# ID to persist across Vagrant runs.
id_file = #data_dir.join("id")
if value
# Write the "id" file with the id given.
id_file.open("w+") do |f|
f.write(value)
end
else
# Delete the file, since the machine is now destroyed
id_file.delete if id_file.file?
# Delete the entire data directory contents since all state
# associated with the VM is now gone.
#data_dir.children.each do |child|
begin
child.rmtree
rescue Errno::EACCES
#logger.info("EACCESS deleting file: #{child}")
end
end
end
# Store the ID locally
#id = value.nil? ? nil : value.to_s
# Notify the provider that the ID changed in case it needs to do
# any accounting from it.
#provider.machine_id_changed
end
# This returns a clean inspect value so that printing the value via
# a pretty print (`p`) results in a readable value.
#
# #return [String]
def inspect
"#<#{self.class}: #{#name} (#{#provider.class})>"
end
# This returns the SSH info for accessing this machine. This SSH info
# is queried from the underlying provider. This method returns `nil` if
# the machine is not ready for SSH communication.
#
# The structure of the resulting hash is guaranteed to contain the
# following structure, although it may return other keys as well
# not documented here:
#
# {
# :host => "1.2.3.4",
# :port => "22",
# :username => "mitchellh",
# :private_key_path => "/path/to/my/key"
# }
#
# Note that Vagrant makes no guarantee that this info works or is
# correct. This is simply the data that the provider gives us or that
# is configured via a Vagrantfile. It is still possible after this
# point when attempting to connect via SSH to get authentication
# errors.
#
# #return [Hash] SSH information.
def ssh_info
# First, ask the provider for their information. If the provider
# returns nil, then the machine is simply not ready for SSH, and
# we return nil as well.
info = #provider.ssh_info
return nil if info.nil?
# Delete out the nil entries.
info.dup.each do |key, value|
info.delete(key) if value.nil?
end
# We set the defaults
info[:host] ||= #config.ssh.default.host
info[:port] ||= #config.ssh.default.port
info[:private_key_path] ||= #config.ssh.default.private_key_path
info[:username] ||= #config.ssh.default.username
# We set overrides if they are set. These take precedence over
# provider-returned data.
info[:host] = #config.ssh.host if #config.ssh.host
info[:port] = #config.ssh.port if #config.ssh.port
info[:username] = #config.ssh.username if #config.ssh.username
info[:password] = #config.ssh.password if #config.ssh.password
# We also set some fields that are purely controlled by Varant
info[:forward_agent] = #config.ssh.forward_agent
info[:forward_x11] = #config.ssh.forward_x11
# Add in provided proxy command config
info[:proxy_command] = #config.ssh.proxy_command if #config.ssh.proxy_command
# Set the private key path. If a specific private key is given in
# the Vagrantfile we set that. Otherwise, we use the default (insecure)
# private key, but only if the provider didn't give us one.
if !info[:private_key_path] && !info[:password]
if #config.ssh.private_key_path
info[:private_key_path] = #config.ssh.private_key_path
else
info[:private_key_path] = #env.default_private_key_path
end
end
# If we have a private key in our data dir, then use that
if !#data_dir.nil?
data_private_key = #data_dir.join("private_key")
if data_private_key.file?
info[:private_key_path] = [data_private_key.to_s]
end
# Setup the keys
info[:private_key_path] ||= []
if !info[:private_key_path].is_a?(Array)
info[:private_key_path] = [info[:private_key_path]]
end
# Expand the private key path relative to the root path
info[:private_key_path].map! do |path|
File.expand_path(path, #env.root_path)
end
# Return the final compiled SSH info data
info
end
# Returns the state of this machine. The state is queried from the
# backing provider, so it can be any arbitrary symbol.
#
# #return [MachineState]
def state
result = #provider.state
raise Errors::MachineStateInvalid if !result.is_a?(MachineState)
result
end
# Temporarily changes the machine UI. This is useful if you want
# to execute an {#action} with a different UI.
def with_ui(ui)
#ui_mutex.synchronize do
begin
old_ui = #ui
#ui = ui
yield
ensure
#ui = old_ui
end
end
end
end
end
In method ssh_info you are missing an end statement after line 318:
Your code:
# If we have a private key in our data dir, then use that
if !#data_dir.nil?
data_private_key = #data_dir.join("private_key")
if data_private_key.file?
info[:private_key_path] = [data_private_key.to_s]
end
What it should be:
# If we have a private key in our data dir, then use that
if !#data_dir.nil?
data_private_key = #data_dir.join("private_key")
if data_private_key.file?
info[:private_key_path] = [data_private_key.to_s]
end
end # <-- this is missing
This type of error always gets reported on the last line of the file because it's looking to close the block and reaches the end of the file without getting enough end statements.