Ruby unexpected ':', expecting kEND - ruby

I want to set up vagrant on my Ubuntu, when "vagrant up", it always give me the following error
syntax error, unexpected ':', expecting kEND
config.vm.provision :shell, path: "vagrantprov.sh"
I checked the Vagrantfile, it should be OK, can anyone tell me where the error is? Thanks.
# -*- 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|
config.vm.box = "ubuntu/trusty64"
# 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"
config.vm.provider "virtualbox" do |vb|
vb.customize ["modifyvm", :id, "--memory", "1024"]
end
config.vm.provision :shell, path: "vagrantprov.sh"
end

Ruby <1.9? Old fashioned hash syntax style is required for old Ruby version
config.vm.provision :shell, :path => "vagrantprov.sh"

What version of Ruby are you running? The named args syntax (path: "...") is supported from on 1.9 and above, perhaps you have a lower Ruby version?
(1.8)
1.8.7 :001 > puts "a", b: 1
SyntaxError: compile error
(irb):1: syntax error, unexpected ':', expecting $end
(1.9)
1.9.3p429 :001 > puts "a", b: 1
a
{:b=>1}
=> nil

Ruby < 1.9 :
:a => 1
Ruby >= 1.9 :
a : 1

Related

How to write to a file provisioning from Vagrantfile

Hi I'm trying to add a Directory Index directive to the default VirtualHost for Apache from the Vagrantfile. I'm wondering if there is a way to edit a file from the Vagrantfile (I'm usung inline SHELL). I know I could copy an entire VH file to the guest machine, but I want to know how to write into files if possible.
Thanks!
You can do that with ansible like this:
config.vm.provision "ansible_local" do |ansible|
ansible.verbose = "vv"
ansible.become = true # execute as root
ansible.playbook = "relative_path_to_ansible_file/playbook.yml"
end
or with a shell
Vagrant.configure("2") do |config|
config.vm.provision "shell" do |s|
s.inline = "echo $1"
s.args = "'hello, world!'"
end
end
https://www.vagrantup.com/docs/provisioning/shell.html

Issue with vagrant while using vagrant up

on installing fresh vagrant instance, I am getting this error while using vagrant up. Here is the error
Vagrant failed to initialize at a very early stage:
There is a syntax error in the following Vagrantfile. The syntax error
message is reproduced below for convenience:
/mnt/c/Users/bilal/Homestead/scripts/homestead.rb:141: syntax error, unexpected tPOW
...ype: folder["type"] ||= nil, **options
... ^
/mnt/c/Users/bilal/Homestead/scripts/homestead.rb:157: syntax error, unexpected keyword_do_block, expecting keyword_end
settings["sites"].each do |site|
^
/mnt/c/Users/bilal/Homestead/scripts/homestead.rb:207: syntax error, unexpected keyword_do_block, expecting keyword_end
settings["databases"].each do |db|
^
/mnt/c/Users/bilal/Homestead/scripts/homestead.rb:208: syntax error, unexpected tSTRING_BEG, expecting keyword_end
config.vm.provision "shell" do |s|
^
/mnt/c/Users/bilal/Homestead/scripts/homestead.rb:208: syntax error, unexpected keyword_do_block, expecting keyword_end
config.vm.provision "shell" do |s|
^
/mnt/c/Users/bilal/Homestead/scripts/homestead.rb:220: syntax error, unexpected keyword_end, expecting $end
Here is my VagrantFile content
# -*- mode: ruby -*-
# vi: set ft=ruby :
require 'json'
require 'yaml'
VAGRANTFILE_API_VERSION ||= "2"
confDir = $confDir ||= File.expand_path("~/.homestead")
homesteadYamlPath = confDir + "/Homestead.yaml"
homesteadJsonPath = confDir + "/Homestead.json"
afterScriptPath = confDir + "/after.sh"
aliasesPath = confDir + "/aliases"
require File.expand_path(File.dirname(__FILE__) + '/scripts/homestead.rb')
Vagrant.require_version '>= 1.8.4'
Vagrant.configure(VAGRANTFILE_API_VERSION) do |config|
if File.exist? aliasesPath then
config.vm.provision "file", source: aliasesPath, destination: "~/.bash_aliases"
end
if File.exist? homesteadYamlPath then
settings = YAML::load(File.read(homesteadYamlPath))
elsif File.exist? homesteadJsonPath then
settings = JSON.parse(File.read(homesteadJsonPath))
end
Homestead.configure(config, settings)
if File.exist? afterScriptPath then
config.vm.provision "shell", path: afterScriptPath, privileged: false
end
if defined? VagrantPlugins::HostsUpdater
config.hostsupdater.aliases = settings['sites'].map { |site| site['map'] }
end
end
============================================================
And there is the YAML file
---
ip: "192.168.10.10"
memory: 2048
cpus: 1
provider: virtualbox
authorize: ~/.ssh/id_rsa.pub
keys:
- ~/.ssh/id_rsa
folders:
- map: ~/Code
to: /mnt/e/XAMPP/htdocs
sites:
- map: homestead.app
to: /mnt/e/XAMPP/htdocs/cap/web
databases:
- homestead
Solved that issue by upgrading Vagrant to 1.8.4.

vagrant provision for dev and production

We started using Vagrant to setup our development environment.
Now we would like to use the same Vagrantfile also in production/staging.
When I use the same vagrantfile that contains a virtualbox provider and gce I get the error
An active machine was found with a different provider. Vagrant
currently allows each machine to be brought up with only a single
provider at a time. A future version will remove this limitation.
Until then, please destroy the existing machine to up with a new
provider.
Machine name: default
Active provider: virtualbox
Requested provider: google
Is there a way I can vagrant up virtualbox and gce?
Vagrant.has_plugin?("nugrant")
Vagrant.require_version ">= 1.6.3"
Vagrant.configure("2") do |config|
config.vm.provider "virtualbox"
config.vm.box = "yungsang/boot2docker"
config.vm.provider :google do |google, override|
override.vm.box = "gce"
google.google_project_id = $GOOGLE_PROJECT_ID
google.google_client_email = $GOOGLE_CLIENT_EMAIL
google.google_key_location = $GOOGLE_KEY_LOCATION
# Override provider defaults
google.name = "name"
google.image = "ubuntu-1404-trusty-v20141212"
google.machine_type = "n1-standard-1"
google.zone = "europe-west1-c"
google.metadata = {'custom' => 'metadata', 'production' => 'app'}
google.tags = ['vagrantbox', 'prod']
override.ssh.username = $LOCAL_USER
override.ssh.private_key_path = $LOCAL_SSH_KEY
config.vm.define :prod do |prod|
prod.vm.provision :shell, inline: 'echo I am executed in prod only!!'
end
end
config.vm.synced_folder ".", "/vagrant"
# Fix busybox/udhcpc issue
config.vm.provision :shell do |s|
s.inline = <<-EOT
if ! grep -qs ^nameserver /etc/resolv.conf; then
sudo /sbin/udhcpc
fi
cat /etc/resolv.conf
EOT
end
# Adjust datetime after suspend and resume
config.vm.provision :shell do |s|
s.inline = <<-EOT
sudo /usr/local/bin/ntpclient -s -h pool.ntp.org
date
EOT
end
# Login docker hub
config.vm.provision :shell do |s|
s.inline = "/usr/bin/docker $#"
s.args = ["login", "-u", config.user.docker.username, "-p", config.user.docker.password, "-e", config.user.docker.email]
end
config.vm.provision :docker do |d|
d.pull_images "nginx"
d.pull_images "mongodb"
d.pull_images "java"
end
ACTICE_SPRING_PROFILE = "te"
config.vm.provision :docker do |d|
# provision docker stuff here
end
config.vm.network "forwarded_port", guest: 80, host: 8080
config.vm.network "forwarded_port", guest: 443, host: 8443
end
Update:
I tried to solve the problem with a multi vm setup, but now I am facing the problem that vagrant uses an outside-in provisioning, so my GCE specific scripts like gce.vm.provision :shell, inline: 'curl -sSL https://get.docker.com/ubuntu/ | sudo sh' are executed at the end not at the beginning.
Vagrant.configure("2") do |config|
config.vm.define "dev", primary: true do |dev|
dev.vm.provider "virtualbox"
dev.vm.box = "yungsang/boot2docker"
dev.vm.synced_folder ".", "/vagrant"
override.vm.provision :shell, inline: 'echo "Provision DEV"'
# Fix busybox/udhcpc issue
dev.vm.provision :shell do |s|
s.inline = <<-EOT
if ! grep -qs ^nameserver /etc/resolv.conf; then
sudo /sbin/udhcpc
fi
cat /etc/resolv.conf
EOT
end
# Adjust datetime after suspend and resume
dev.vm.provision :shell do |s|
s.inline = <<-EOT
sudo /usr/local/bin/ntpclient -s -h pool.ntp.org
date
EOT
end
dev.vm.network "forwarded_port", guest: 80, host: 8080
dev.vm.network "forwarded_port", guest: 443, host: 8443
end
config.vm.define "gce", autostart: false do |gce|
gce.vm.provider :google do |google, override|
override.vm.box = "gce"
google.google_project_id = $GOOGLE_PROJECT_ID
google.google_client_email = $GOOGLE_CLIENT_EMAIL
google.google_key_location = $GOOGLE_KEY_LOCATION
# Override provider defaults
google.name = "z-rechnung-#{TARGET_ENV}"
google.image = "ubuntu-1404-trusty-v20141212"
google.machine_type = "n1-standard-1"
google.zone = "europe-west1-c"
google.metadata = {'environment' => "#{TARGET_ENV}"}
google.tags = ['vagrantbox', "#{TARGET_ENV}"]
override.ssh.username = $LOCAL_USER
override.ssh.private_key_path = $LOCAL_SSH_KEY
gce.vm.provision :shell, inline: 'sudo apt-get update -y'
gce.vm.provision :shell, inline: 'sudo apt-get upgrade -y'
gce.vm.provision :shell, inline: 'curl -sSL https://get.docker.com/ubuntu/ | sudo sh'
end
end
# Login docker hub
config.vm.provision :shell do |s|
s.inline = "/usr/bin/docker $#"
s.args = ["login", "-u", config.user.docker.username, "-p", config.user.docker.password, "-e", config.user.docker.email]
end
config.vm.provision :docker do |d|
d.pull_images ....
end
config.vm.provision :docker do |d|
d.run "image" ....
end
The right answer is to remember that your Vagrantfile is just a Ruby program, first and foremost, and the execution of this program is to result in a datastructure that the CLI sub-commands traverse.
So, create functions that add provisioners to configuration, then call them in the "inside". For example,
def provisioner_one(config)
config.vm.provision :shell, 'echo hello'
end
Vagrant.configure('2') do |config|
# stuff here
config.vm.define 'dev' do |dev, override|
# whatever else here
provisioner_one(dev)
# other stuff here
end
# more other stuff here
end
This will DWIM.

How do I include variables in my VagrantFile?

Can anyone guide me to how do I include variables in my VagrantFile? I am trying to inject configs into the Vagrantfile from an external file so that I can distribute the config to my colleagues without having them to hardcode configs directly on the Vagrantfile.
I had thought that since it was Ruby based I could just include a Ruby file but I get an error
Message: unintialized constant MyVars
My VagrantFile simplified
# -*- mode: ruby -*-
# vi: set ft=ruby :
require 'vagrant.rb'
include MyVars
Vagrant.configure("2") do |config|
# Web
config.vm.define :joe do |joe|
joe.vm.box = "precise64_4.2.12"
joe.vm.hostname = WEBVMNAME
joe.vm.network :private_network, ip: "192.168.140.141"
# Port Forwarding
joe.vm.network :forwarded_port, guest: 22, host: 2201
joe.vm.network :forwarded_port, guest: 80, host: 8080
# Bootstrap Bash Script
joe.vm.provision :shell, :path => "bootstrap.sh"
end
end
And vagrant.rb contains
module MyVars
WEBVMNAME = "rex"
end
Do note that I am also a newbie at Ruby so I am not sure as well if its just the syntax I got wrong?
Edit: Updated code I am using
I use the approach of https://puphpet.com, I create a file config.yaml in the same directory of the Vagrantfile and...
In my Vagrantfile:
# encoding: utf-8
# -*- mode: ruby -*-
# vi: set ft=ruby :
require 'yaml'
current_dir = File.dirname(File.expand_path(__FILE__))
configs = YAML.load_file("#{current_dir}/config.yaml")
vagrant_config = configs['configs'][configs['configs']['use']]
Vagrant.configure('2') do |config|
config.vm.network 'public_network', ip: vagrant_config['public_ip']
...
In my config.yaml:
---
configs:
use: 'home'
office:
public_ip: '192.168.2.117'
<more variables>...
home:
public_ip: '192.168.1.117'
<more variables>...
Use require_relative:
require_relative 'vagrant.rb'
include MyVars
# ...
Try changing your require to this:
require './vagrant'
I created a library directory:
require './lib/cfpEnvironment.rb'
include CFPEnvironment
And then did the scripting of what I need to be dynamic, defining the variables in the module created...
CFPPorts.select{ |key, value| value.numeric? }.each { |key, value|
config.vm.network :forwarded_port, guest: value, host: value
}
Thanks to #Matt and #strager for their answers above!

Override Vagrant configuration settings locally (per-dev)

I'd like the question to be answered in general, but to illustrate it, here's a use case:
I'm using Vagrant for a simple LMAP project. I use standalone Puppet for provisioning. Now, there might be some developers who sit behind a proxy and they would need some additional configuration to be made to the VM. I have things working on the Puppet side: I can pass the proxy IP (if any) as a fact to puppet in the Vagrantfile and Puppet reacts accordingly if it's set.
The only issue I have is: how can developers specify/override this setting for their development environment without having to change the Vagrantfile (which is under version control and must remain dev-environment-neutral)?
If would be awesome if people could override some Vagrant settings in a file called e.g. Vagrantfile.local, which I would exclude via .gitignore.
Since a Vagrantfile is just Ruby, I tried the following:
# Also load per-dev custom vagrant config
custom_vagrantfile = 'Vagrantfile.local'
load custom_vagrantfile if File.exist?(custom_vagrantfile)
The file inclusion basically works, but it looks like in the included file, I'm not in the same Vagrant context anymore...
Vagrant::Config.run do |config|
config.vm.provision :puppet do |puppet|
puppet.facter = { "proxy" => "proxy.host:80" }
end
end
... also "resets" all other puppet config values I made in the main Vagrantfile, which makes me think I'm heading in the wrong direction here. I should note that I'm a total noob at Ruby ;)
Can anyone give me a hint or even a working solution for how per-dev customization could be done here in general?
The Vagrantfile is just Ruby, so YAML is another option.
For example, in the Vagrantfile I do this:
# -*- mode: ruby -*-
# vi: set ft=ruby :
require 'yaml'
settings = YAML.load_file 'vagrant.yml'
db_ip_address = settings['db']['ip_address']
api_ip_address = settings['api']['ip_address']
Vagrant.configure("2") do |config|
config.vm.box = "ffuenf/ubuntu-13.10-server-amd64"
config.vm.box_url = "https://vagrantcloud.com/ffuenf/ubuntu-13.10-server-amd64/version/4/provider/virtualbox.box"
config.vm.define "db" do |db|
db.vm.synced_folder settings['db']['artifacts_dir']['host'], settings['db']['artifacts_dir']['guest']
db.vm.network "private_network", ip: db_ip_address
... other stuff ...
end
config.vm.define "api" do |api|
api.vm.synced_folder settings['api']['artifacts_dir']['host'], settings['api']['artifacts_dir']['guest']
api.vm.network "private_network", ip: api_ip_address
api.vm.network "forwarded_port", guest: settings['api']['forwarded_port']['guest'], host: settings['api']['forwarded_port']['host']
end
end
Then I have a vagrant.yml file (I just made up the name; you can use whatever name you like) for the developer-specific configuration:
db:
ip_address: 192.168.4.14
artifacts_dir:
host: /Users/willie/myapp/db-scripts
guest: /opt/myapp/db
api:
ip_address: 192.168.4.15
forwarded_port:
host: 9080
guest: 8080
artifacts_dir:
host: /Users/willie/myapp/artifacts
guest: /opt/myapp/api
I would suggest using environment variables to dynamically change the behavior of the Vagrantfile without editing the file itself.
To give a real world example, here's how you could use an Ubuntu base box by default but have an environment variable define an alternative Linux distribution:
if ENV['OPERATINGSYSTEM']
if ENV['OPERATINGSYSTEM'].downcase == 'redhat'
os_name = 'centos'
config.vm.box = 'centos'
config.vm.box_url = 'https://dl.dropbox.com/u/7225008/Vagrant/CentOS-6.3-x86_64-minimal.box'
else
raise(Exception, "undefined operatingsystem: #{ENV['OPERATINGSYSTEM']}")
end
else
os_name = 'precise64'
config.vm.box = 'precise64'
config.vm.box_url = 'http://files.vagrantup.com/precise64.box'
end
This example comes from https://github.com/puppetlabs/puppetlabs-openstack_dev_env
If you are prepared to define settings that are applied to all your vagrant boxes it's worth noting that, "Vagrant actually loads a series of Vagrantfiles, merging the settings as it goes." (ref https://docs.vagrantup.com/v2/vagrantfile/)
So I have the following defined in ~/.vagrant.d/Vagrantfile to increase the amount of RAM for my Vagrant boxes:
Vagrant.configure(2) do |config|
config.vm.provider "virtualbox" do |vb|
vb.memory = 2048
end
end
Here's an idea. It may be "ugly" and "wrong", but, at least, it works :)
# file2.rb, this is your per-dev configuration file
puts "included external file which uses outer var: #{foo}"
# file1.rb, this would be your Vagrantfile
puts 'first'
foo = 'bar'
external = File.read 'file2.rb'
eval external
puts 'second'
Let's run that
$ ruby file1.rb
first
included external file which uses outer var: bar
second
Adapting to your example, file2.rb would contain only usage of config without defining it (config will be provided from outer context)
config.vm.provision :puppet do |puppet|
puppet.facter = { "proxy" => "proxy.host:80" }
end
And your Vagrant file may look like this:
Vagrant::Config.run do |config|
external = File.read 'Vagrantfile.local'
eval external
# proceed with general settings here
config.vm.provision :puppet do |puppet|
puppet.facter = { "proxy" => "proxy.host:80" }
end
end
Update (another, "data-driven" approach)
# Vagranfile.local
config_values[:puppet][:facter][:proxy] = 'proxy.host:80'
# Vargantfile
Vagrant::Config.run do |config|
config_values = {
puppet: {
facter: {
proxy: nil
},
manifests_file: 'my_manifest.pp'
}
}
external = File.read 'Vagrantfile.local'
eval external # this should overwrite proxy config
# proceed with general settings here
config.vm.provision :puppet do |puppet|
if config_values[:puppet][:facter][:proxy]
puppet.facter = { "proxy" => config_values[:puppet][:facter][:proxy] }
end
puppet.manifests_file = config_values[:puppet][:manifests_file]
end
end
I believe that's the exact use case that Nugrant plugin was created to solve. It allows each of your devs to have a .vagrantuser (which is a .gitignore-ed file) in YAML specifying custom configuration values then reference these values with ease in Vagrantfile.
In your case, a proxied developer would have their .vagrantuser file looking like this:
proxy: 'proxy.host:80'
And your Vagrantfile would look like this (pseudo code, I don't really know ruby):
Vagrant::Config.run do |config|
config.vm.provision :puppet do |puppet|
if config.user.has_key?('proxy')
puppet.facter = { "proxy" => config.user.proxy }
end
end
end
You should bundle a sample/reference vagrantuser (i.e. vagrantuser.example) file for your devs to copy and adjust to their environment.
To extend on #Willie Wheeler 's answer. My setup is:
Root
|-- defaults.yml
|-- env.yml
|-- Vagrantfile
Vagrantfile
# Load local env config
require 'yaml'
dir = File.dirname(File.expand_path(__FILE__))
# defaults
settings = YAML::load_file("#{dir}/defaults.yml")
if File.exist?("#{dir}/env.yml")
env_settings = YAML::load_file("#{dir}/env.yml")
settings.merge!(env_settings)
end
...
# Customize the amount of memory on the VM:
vb.memory = settings["vb"]["memory"]
defaults.yml
vb:
memory: 1024
env.yml
vb:
memory: 204
This will merge whatever defaults you have with your per-dev config. Also it is clear to developers what values they can actually change
Consider using vagrant-proxyconf plugin. It allows to set proxy for all Vagrant VMs globally.
Another solution is to run external shell script during provisioning. I use separate config.vm.provision section at the beginning of Vagrantfile to do it:
# reset: true below is needed to reset the connection to the VM so that new
# environment variables set in /etc/environment will be picked up in next
# provisioning steps
config.vm.provision "shell", reset: true, inline: <<-SHELL
if [ -f /vagrant/Vagrantfile-settings.sh ]
then
/vagrant/Vagrantfile-settings.sh
fi
SHELL
Then just put a Vagrantfile-settings.sh file next to Vagrantfile, add it to .gitignore (or whatever) and put any script inside, for example to set proxy for interactive terminal, all daemons and docker containers:
# Proxy for interactive terminals
echo "http_proxy=http://PROXY_ADDRESS:PROXY_PORT" >> /etc/environment
echo "https_proxy=http://PROXY_ADDRESS:PROXY_PORT" >> /etc/environment
echo "no_proxy=127.0.0.1,localhost" >> /etc/environment
# Proxy for daemons (e.g. Docker deamon - used to pull images, apt - run from default daily cron job)
mkdir /etc/systemd/system.conf.d
echo [Manager] > /etc/systemd/system.conf.d/01-http-proxy.conf
echo "DefaultEnvironment=\"http_proxy=PROXY_ADDRESS:PROXY_PORT\"" >> /etc/systemd/system.conf.d/01-http-proxy.conf
echo "DefaultEnvironment=\"https_proxy=PROXY_ADDRESS:PROXY_PORT\"" >> /etc/systemd/system.conf.d/01-http-proxy.conf
echo "DefaultEnvironment=\"no_proxy=127.0.0.1,localhost\"" >> /etc/systemd/system.conf.d/01-http-proxy.conf
echo "# Docker requires upper-case http proxy environment variables..." >> /etc/systemd/system.conf.d/01-http-proxy.conf
echo "DefaultEnvironment=\"HTTP_PROXY=http://PROXY_ADDRESS:PROXY_PORT2\"" >> /etc/systemd/system.conf.d/01-http-proxy.conf
echo "DefaultEnvironment=\"HTTPS_PROXY=http://PROXY_ADDRESS:PROXY_PORT\"" >> /etc/systemd/system.conf.d/01-http-proxy.conf
echo "DefaultEnvironment=\"NO_PROXY=127.0.0.1,localhost\"" >> /etc/systemd/system.conf.d/01-http-proxy.conf
# Proxy for docker containers started with `docker run`
mkdir /home/vagrant/.docker
cat <<EOF > /home/vagrant/.docker/config.json
{
"proxies": {
"default": {
"httpProxy": "http:/PROXY_ADDRESS:PROXY_PORT",
"httpsProxy": "http://PROXY_ADDRESS:PROXY_PORT",
"noProxy": "127.0.0.1,localhost"
}
}
}
EOF
chown -R vagrant:vagrant /home/vagrant/.docker
You can load the settings from YAML file. This is demonstrated in Drupal VM as below:
# Use config.yml for basic VM configuration.
require 'yaml'
dir = File.dirname(File.expand_path(__FILE__))
if !File.exist?("#{dir}/config.yml")
raise 'Configuration file not found! Please copy example.config.yml to config.yml and try again.'
end
vconfig = YAML::load_file("#{dir}/config.yml")
So then you can create config.yml like:
vagrant_box: geerlingguy/ubuntu1404
vagrant_user: vagrant
vagrant_ip: 192.168.88.88
and in Vagrantfile you can use variables as:
config.vm.box = vconfig['vagrant_box']
config.vm.network "private_network", ip: vconfig['vagrant_ip']

Resources