docker-provider 0.0.2 → 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (49) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +25 -0
  3. data/Gemfile +7 -7
  4. data/Gemfile.lock +51 -40
  5. data/LICENSE.txt +1 -1
  6. data/README.md +130 -60
  7. data/Rakefile +6 -1
  8. data/boxes/dind/.gitignore +2 -0
  9. data/boxes/dind/Dockerfile +12 -0
  10. data/boxes/dind/README.md +10 -0
  11. data/boxes/{nginx → dind}/Vagrantfile.sample +2 -1
  12. data/boxes/{nginx → dind}/metadata.json +0 -0
  13. data/boxes/precise/Dockerfile +10 -9
  14. data/boxes/precise/Vagrantfile.sample +0 -1
  15. data/development/Vagrantfile +1 -9
  16. data/docker-provider.gemspec +1 -2
  17. data/example/Vagrantfile +11 -10
  18. data/lib/docker-provider.rb +2 -1
  19. data/lib/docker-provider/action.rb +17 -6
  20. data/lib/docker-provider/action/check_running.rb +1 -1
  21. data/lib/docker-provider/action/create.rb +14 -11
  22. data/lib/docker-provider/action/prepare_nfs_settings.rb +59 -0
  23. data/lib/docker-provider/action/prepare_nfs_valid_ids.rb +19 -0
  24. data/lib/docker-provider/config.rb +14 -7
  25. data/lib/docker-provider/driver.rb +30 -6
  26. data/lib/docker-provider/errors.rb +14 -0
  27. data/lib/docker-provider/plugin.rb +10 -3
  28. data/lib/docker-provider/provider.rb +1 -1
  29. data/lib/docker-provider/synced_folder.rb +20 -0
  30. data/lib/docker-provider/version.rb +1 -1
  31. data/locales/en.yml +13 -2
  32. data/spec/acceptance/provider/basic_spec.rb +94 -0
  33. data/spec/acceptance/provider/network_forwarded_port_spec.rb +29 -0
  34. data/spec/acceptance/provider/synced_folder_spec.rb +39 -0
  35. data/spec/acceptance/provisioner/chef_solo_spec.rb +37 -0
  36. data/spec/acceptance/provisioner/puppet_spec.rb +37 -0
  37. data/spec/acceptance/provisioner/shell_spec.rb +51 -0
  38. data/spec/acceptance/synced_folder/nfs_spec.rb +36 -0
  39. data/spec/unit/driver_spec.rb +70 -11
  40. data/vagrant-spec.config.rb +8 -0
  41. metadata +40 -29
  42. data/boxes/nginx/.gitignore +0 -1
  43. data/boxes/nginx/Dockerfile +0 -4
  44. data/boxes/nginx/README.md +0 -25
  45. data/boxes/nginx/start +0 -5
  46. data/lib/docker-provider/action/share_folders.rb +0 -63
  47. data/spec/acceptance/Vagrantfile +0 -25
  48. data/spec/acceptance/vagrant_ssh.bats +0 -34
  49. data/spec/acceptance/vagrant_up.bats +0 -35
data/Rakefile CHANGED
@@ -4,7 +4,12 @@ require 'rspec/core/rake_task'
4
4
  namespace :spec do
5
5
  desc 'Run acceptance specs using Bats'
6
6
  task :acceptance do
7
- sh 'bats spec/acceptance'
7
+ components = %w(
8
+ basic network/forwarded_port synced_folder synced_folder/nfs
9
+ provisioner/shell provisioner/puppet provisioner/chef-solo
10
+ ).map{|s| "provider/docker/#{s}" }
11
+
12
+ sh "bundle exec vagrant-spec test --components=#{components.join(' ')}"
8
13
  end
9
14
 
10
15
  require 'rspec/core/rake_task'
@@ -0,0 +1,2 @@
1
+ Vagrantfile
2
+ precise-dind.box
@@ -0,0 +1,12 @@
1
+ FROM fgrehm/vagrant-ubuntu:precise
2
+
3
+ RUN apt-get update && apt-get install lxc -yq --force-yes -o Dpkg::Options::='--force-confdef' -o Dpkg::Options::='--force-confold'
4
+
5
+ RUN curl -sLS https://get.docker.io | sh
6
+
7
+ RUN usermod -aG docker vagrant
8
+
9
+ RUN curl -sLS https://raw.github.com/dotcloud/docker/master/hack/dind -o /dind && \
10
+ chmod +x /dind
11
+
12
+ CMD ["/dind", "/sbin/init"]
@@ -0,0 +1,10 @@
1
+ # Ubuntu Precise "Docker in Docker" enabled base box
2
+
3
+ To turn this into a box:
4
+
5
+ ```
6
+ docker build -t myuser/vagrant-ubuntu:precise-dind .
7
+ docker push myuser/vagrant-ubuntu:precise-dind
8
+ sed 's/IMAGE/myuser\/vagrant-ubuntu:precise-dind/' Vagrantfile.sample > Vagrantfile
9
+ tar cvzf precise-dind.box ./metadata.json ./Vagrantfile
10
+ ```
@@ -1,6 +1,7 @@
1
1
  Vagrant.configure("2") do |config|
2
2
  config.vm.provider :docker do |docker|
3
3
  docker.image = "IMAGE"
4
- docker.cmd = ["/bin/start-container"]
4
+ docker.privileged = true
5
+ docker.volumes << '/var/lib/docker'
5
6
  end
6
7
  end
File without changes
@@ -1,16 +1,8 @@
1
1
  # Base Vagrant box
2
- #
3
- # VERSION 0.0.1
4
2
 
5
- FROM ubuntu:precise
3
+ FROM ubuntu-upstart:precise
6
4
  MAINTAINER Fabio Rehm "fgrehm@gmail.com"
7
5
 
8
- # Enable universe
9
- RUN echo "deb http://archive.ubuntu.com/ubuntu precise main universe" > /etc/apt/sources.list
10
-
11
- # Update things
12
- RUN apt-get update && apt-get install openssh-server sudo curl -y && apt-get upgrade -y && apt-get clean
13
-
14
6
  # Create and configure vagrant user
15
7
  RUN useradd --create-home -s /bin/bash vagrant
16
8
  WORKDIR /home/vagrant
@@ -27,9 +19,18 @@ RUN sed -i.bkp -e \
27
19
  's/%sudo\s\+ALL=(ALL\(:ALL\)\?)\s\+ALL/%sudo ALL=NOPASSWD:ALL/g' \
28
20
  /etc/sudoers
29
21
 
22
+ # Enable universe
23
+ RUN echo "deb http://archive.ubuntu.com/ubuntu precise main universe" > /etc/apt/sources.list
24
+
30
25
  # Thanks to http://docs.docker.io/en/latest/examples/running_ssh_service/
31
26
  RUN mkdir /var/run/sshd
32
27
 
28
+ # Update things and make sure the required packges are installed
29
+ RUN apt-get update && \
30
+ apt-get install openssh-server sudo curl nfs-common -y && \
31
+ apt-get upgrade -y && \
32
+ apt-get clean
33
+
33
34
  # Puppet
34
35
  RUN wget http://apt.puppetlabs.com/puppetlabs-release-stable.deb -O /tmp/puppetlabs-release-stable.deb && \
35
36
  dpkg -i /tmp/puppetlabs-release-stable.deb && \
@@ -1,6 +1,5 @@
1
1
  Vagrant.configure("2") do |config|
2
2
  config.vm.provider :docker do |docker|
3
3
  docker.image = "IMAGE"
4
- docker.cmd = ["/usr/sbin/sshd", "-D", "-e"]
5
4
  end
6
5
  end
@@ -53,7 +53,7 @@ STR
53
53
 
54
54
  # Configure Ruby so that we can test the plugin from within the VM
55
55
  config.vm.provision :ventriloquist do |env|
56
- env.platforms << 'ruby:1.9.3'
56
+ env.platforms << 'ruby:2.0.0'
57
57
  end
58
58
 
59
59
  config.vm.provision :shell, privileged: false, inline: %[
@@ -73,14 +73,6 @@ STR
73
73
  echo '{ "provider": "docker" }' > $HOME/.vagrant.d/boxes/dummy/docker/metadata.json
74
74
  fi
75
75
 
76
- # TODO: Get rid of this once Ventriloquist gets support for it:
77
- # https://github.com/fgrehm/ventriloquist/issues/32
78
- if ! [ -f /usr/local/bin/bats ]; then
79
- git clone https://github.com/sstephenson/bats.git /tmp/bats
80
- cd /tmp/bats && sudo ./install.sh /usr/local
81
- rm -rf /tmp/bats
82
- fi
83
-
84
76
  if ! $(which bsdtar > /dev/null 2>/dev/null); then
85
77
  sudo apt-get install bsdtar -y
86
78
  fi
@@ -8,7 +8,7 @@ Gem::Specification.new do |spec|
8
8
  spec.version = VagrantPlugins::DockerProvider::VERSION
9
9
  spec.authors = ["Fabio Rehm"]
10
10
  spec.email = ["fgrehm@gmail.com"]
11
- spec.description = %q{Experimental Docker provider for Vagrant}
11
+ spec.description = %q{Docker provider for Vagrant}
12
12
  spec.summary = spec.description
13
13
  spec.homepage = "https://github.com/fgrehm/docker-provider"
14
14
  spec.license = "MIT"
@@ -21,5 +21,4 @@ Gem::Specification.new do |spec|
21
21
  spec.add_development_dependency "bundler", "~> 1.3"
22
22
  spec.add_development_dependency "rake"
23
23
  spec.add_development_dependency "rspec"
24
- # TODO: spec.add_dependency 'docker-api', '~> 1.6.0'
25
24
  end
@@ -5,25 +5,33 @@ ENV['VAGRANT_DEFAULT_PROVIDER'] = 'docker'
5
5
 
6
6
  # This is only needed if you are using the plugin from sources with bundler
7
7
  Vagrant.require_plugin 'docker-provider'
8
+ Vagrant.require_plugin 'vagrant-cachier'
8
9
 
9
10
  # Vagrantfile API/syntax version. Don't touch unless you know what you're doing!
10
11
  VAGRANTFILE_API_VERSION = "2"
11
12
 
12
13
  Vagrant.configure(VAGRANTFILE_API_VERSION) do |config|
13
- # This just fires up a container with a SSH server
14
+ config.cache.scope = :machine
15
+
16
+ # This fires up a container with a SSH server
14
17
  config.vm.define 'dummy' do |node|
15
18
  node.vm.box = 'dummy'
16
19
  node.vm.box_url = 'http://bit.ly/vagrant-docker-dummy'
17
20
 
18
21
  node.vm.provider :docker do |docker|
19
22
  docker.image = 'fgrehm/vagrant-ubuntu:precise'
20
- docker.cmd = ["/usr/sbin/sshd", "-D", "-e"]
23
+ docker.privileged = true
24
+ docker.volumes << '/var/lib/docker'
21
25
  end
26
+
27
+ node.cache.synced_folder_opts = {
28
+ type: :nfs, mount_options: ['rw', 'vers=3', 'tcp', 'nolock']
29
+ }
22
30
  end
23
31
 
24
32
  # This is a container that gets provisioned with Puppet
25
33
  config.vm.define 'precise' do |node|
26
- node.vm.box = 'precise'
34
+ node.vm.box = 'precise64'
27
35
  node.vm.box_url = 'http://bit.ly/vagrant-docker-precise'
28
36
 
29
37
  node.vm.provision :puppet do |puppet|
@@ -31,11 +39,4 @@ Vagrant.configure(VAGRANTFILE_API_VERSION) do |config|
31
39
  puppet.manifest_file = "site.pp"
32
40
  end
33
41
  end
34
-
35
- # Custom start script
36
- config.vm.define 'nginx' do |node|
37
- node.vm.box = 'nginx'
38
- node.vm.box_url = 'http://bit.ly/vagrant-docker-nginx'
39
- node.vm.network "forwarded_port", guest: 80, host: 8080
40
- end
41
42
  end
@@ -1 +1,2 @@
1
- require 'docker-provider/plugin'
1
+ require_relative "docker-provider/version"
2
+ require_relative "docker-provider/plugin"
@@ -5,8 +5,9 @@ require_relative 'action/destroy'
5
5
  require_relative 'action/forward_ports'
6
6
  require_relative 'action/stop'
7
7
  require_relative 'action/message'
8
+ require_relative 'action/prepare_nfs_valid_ids'
9
+ require_relative 'action/prepare_nfs_settings'
8
10
  require_relative 'action/is_running'
9
- require_relative 'action/share_folders'
10
11
  require_relative 'action/start'
11
12
 
12
13
  module VagrantPlugins
@@ -28,14 +29,22 @@ module VagrantPlugins
28
29
  # b2.use Builtin::EnvSet, :port_collision_repair => true
29
30
  # b2.use Builtin::HandleForwardedPortCollisions
30
31
  b2.use Builtin::Provision
31
- b2.use ShareFolders
32
+ b2.use PrepareNFSValidIds
33
+ b2.use Builtin::SyncedFolderCleanup
34
+ b2.use Builtin::SyncedFolders
35
+ b2.use PrepareNFSSettings
32
36
  b2.use ForwardPorts
33
37
  # This will actually create and start, but that's fine
34
38
  b2.use Create
35
39
  b2.use action_boot
40
+ else
41
+ b2.use PrepareNFSValidIds
42
+ b2.use Builtin::SyncedFolderCleanup
43
+ b2.use Builtin::SyncedFolders
44
+ b2.use PrepareNFSSettings
45
+ b2.use action_start
36
46
  end
37
47
  end
38
- b.use action_start
39
48
  end
40
49
  end
41
50
 
@@ -67,9 +76,11 @@ module VagrantPlugins
67
76
  Builder.new.tap do |b|
68
77
  b.use Builtin::Call, Created do |env, b2|
69
78
  if env[:result]
70
- # TODO: Make use of this once we figure out how to run dockers in machine mode
71
- # b2.use Builtin::Call, Builtin::GracefulHalt, :stopped, :running do |env2, b3|
72
- b2.use Stop
79
+ b2.use Builtin::Call, Builtin::GracefulHalt, :stopped, :running do |env2, b3|
80
+ if !env2[:result]
81
+ b3.use Stop
82
+ end
83
+ end
73
84
  else
74
85
  b2.use Message, :not_created
75
86
  end
@@ -11,7 +11,7 @@ module VagrantPlugins
11
11
  raise Vagrant::Errors::VMNotCreatedError
12
12
  end
13
13
 
14
- if env[:machine].state.id == :created
14
+ if env[:machine].state.id == :stopped
15
15
  raise Vagrant::Errors::VMNotRunningError
16
16
  end
17
17
 
@@ -3,7 +3,7 @@ module VagrantPlugins
3
3
  module Action
4
4
  class Create
5
5
  def initialize(app, env)
6
- @app = app
6
+ @app = app
7
7
  @@mutex ||= Mutex.new
8
8
  end
9
9
 
@@ -14,6 +14,8 @@ module VagrantPlugins
14
14
  @machine_config = @machine.config
15
15
  @driver = @machine.provider.driver
16
16
 
17
+ guard_cmd_configured!
18
+
17
19
  cid = ''
18
20
  @@mutex.synchronize do
19
21
  cid = @driver.create(create_params)
@@ -29,12 +31,13 @@ module VagrantPlugins
29
31
  container_name << "_#{Time.now.to_i}"
30
32
 
31
33
  {
32
- image: @provider_config.image,
33
- cmd: @provider_config.cmd,
34
- ports: forwarded_ports,
35
- name: container_name,
36
- hostname: @machine_config.vm.hostname,
37
- volumes: synced_folders
34
+ image: @provider_config.image,
35
+ cmd: @provider_config.cmd,
36
+ ports: forwarded_ports,
37
+ name: container_name,
38
+ hostname: @machine_config.vm.hostname,
39
+ volumes: @provider_config.volumes,
40
+ privileged: @provider_config.privileged
38
41
  }
39
42
  end
40
43
 
@@ -45,10 +48,10 @@ module VagrantPlugins
45
48
  end.compact
46
49
  end
47
50
 
48
- def synced_folders
49
- @env[:synced_folders].map do |sf|
50
- "#{sf[:hostpath]}:#{sf[:guestpath]}"
51
- end.compact
51
+ def guard_cmd_configured!
52
+ if ! @provider_config.image
53
+ raise Errors::ImageNotConfiguredError, name: @machine.name
54
+ end
52
55
  end
53
56
  end
54
57
  end
@@ -0,0 +1,59 @@
1
+ require_relative '../errors'
2
+
3
+ module VagrantPlugins
4
+ module DockerProvider
5
+ module Action
6
+ class PrepareNFSSettings
7
+ include Vagrant::Util::Retryable
8
+
9
+ def initialize(app, env)
10
+ @app = app
11
+ @logger = Log4r::Logger.new("vagrant::action::vm::nfs")
12
+ end
13
+
14
+ def call(env)
15
+ @machine = env[:machine]
16
+
17
+ @app.call(env)
18
+
19
+ if using_nfs? && !privileged_container?
20
+ raise Errors::NfsWithoutPrivilegedError
21
+ end
22
+
23
+ if using_nfs?
24
+ @logger.info("Using NFS, preparing NFS settings by reading host IP and machine IP")
25
+ add_ips_to_env!(env)
26
+ end
27
+ end
28
+
29
+ # We're using NFS if we have any synced folder with NFS configured. If
30
+ # we are not using NFS we don't need to do the extra work to
31
+ # populate these fields in the environment.
32
+ def using_nfs?
33
+ @machine.config.vm.synced_folders.any? { |_, opts| opts[:type] == :nfs }
34
+ end
35
+
36
+ def privileged_container?
37
+ @machine.provider.driver.privileged?(@machine.id)
38
+ end
39
+
40
+ # Extracts the proper host and guest IPs for NFS mounts and stores them
41
+ # in the environment for the SyncedFolder action to use them in
42
+ # mounting.
43
+ #
44
+ # The ! indicates that this method modifies its argument.
45
+ def add_ips_to_env!(env)
46
+ provider = env[:machine].provider
47
+
48
+ host_ip = provider.driver.docker_bridge_ip
49
+ machine_ip = provider.ssh_info[:host]
50
+
51
+ raise Vagrant::Errors::NFSNoHostonlyNetwork if !host_ip || !machine_ip
52
+
53
+ env[:nfs_host_ip] = host_ip
54
+ env[:nfs_machine_ip] = machine_ip
55
+ end
56
+ end
57
+ end
58
+ end
59
+ end
@@ -0,0 +1,19 @@
1
+ module VagrantPlugins
2
+ module DockerProvider
3
+ module Action
4
+ class PrepareNFSValidIds
5
+ def initialize(app, env)
6
+ @app = app
7
+ @logger = Log4r::Logger.new("vagrant::action::vm::nfs")
8
+ end
9
+
10
+ def call(env)
11
+ machine = env[:machine]
12
+ env[:nfs_valid_ids] = machine.provider.driver.all_containers
13
+
14
+ @app.call(env)
15
+ end
16
+ end
17
+ end
18
+ end
19
+ end
@@ -1,28 +1,35 @@
1
1
  module VagrantPlugins
2
2
  module DockerProvider
3
3
  class Config < Vagrant.plugin("2", :config)
4
- attr_accessor :image, :cmd, :ports
4
+ attr_accessor :image, :cmd, :ports, :volumes, :privileged
5
5
 
6
6
  def initialize
7
- @image = UNSET_VALUE
8
- @cmd = UNSET_VALUE
9
- @ports = UNSET_VALUE
7
+ @image = nil
8
+ @cmd = UNSET_VALUE
9
+ @ports = []
10
+ @privileged = UNSET_VALUE
11
+ @volumes = []
10
12
  end
11
13
 
12
14
  def finalize!
13
- @ports = [] if @ports == UNSET_VALUE
14
- @cmd = [] if @cmd == UNSET_VALUE
15
+ @cmd = [] if @cmd == UNSET_VALUE
16
+ @privileged = false if @privileged == UNSET_VALUE
15
17
  end
16
18
 
17
19
  def validate(machine)
18
20
  errors = _detected_errors
19
21
 
20
- errors << I18n.t("docker_provider.errors.config.image_not_set") if @image == UNSET_VALUE
21
22
  # TODO: Detect if base image has a CMD / ENTRYPOINT set before erroring out
22
23
  errors << I18n.t("docker_provider.errors.config.cmd_not_set") if @cmd == UNSET_VALUE
23
24
 
24
25
  { "docker-provider" => errors }
25
26
  end
27
+
28
+ private
29
+
30
+ def using_nfs?(machine)
31
+ machine.config.vm.synced_folders.any? { |_, opts| opts[:type] == :nfs }
32
+ end
26
33
  end
27
34
  end
28
35
  end
@@ -24,6 +24,7 @@ module VagrantPlugins
24
24
  run_cmd = %W(docker run -name #{name} -d)
25
25
  run_cmd += ports.map { |p| ['-p', p.to_s] }
26
26
  run_cmd += volumes.map { |v| ['-v', v.to_s] }
27
+ run_cmd += %W(-privileged) if params[:privileged]
27
28
  run_cmd += %W(-h #{params[:hostname]}) if params[:hostname]
28
29
  run_cmd += [image, cmd]
29
30
 
@@ -44,38 +45,61 @@ module VagrantPlugins
44
45
  end
45
46
 
46
47
  def created?(cid)
47
- result = execute('docker', 'ps', '-a', '-q').to_s
48
+ result = execute('docker', 'ps', '-a', '-q', '-notrunc').to_s
48
49
  result =~ /^#{Regexp.escape cid}$/
49
50
  end
50
51
 
51
52
  def running?(cid)
52
- result = execute('docker', 'ps', '-q')
53
+ result = execute('docker', 'ps', '-q', '-notrunc')
53
54
  result =~ /^#{Regexp.escape cid}$/m
54
55
  end
55
56
 
57
+ def privileged?(cid)
58
+ inspect_container(cid)['HostConfig']['Privileged']
59
+ end
60
+
56
61
  def start(cid)
57
62
  unless running?(cid)
58
63
  execute('docker', 'start', cid)
64
+ # This resets the cached information we have around, allowing `vagrant reload`s
65
+ # to work properly
66
+ # TODO: Add spec to verify this behavior
67
+ @data = nil
59
68
  end
60
69
  end
61
70
 
62
71
  def stop(cid)
63
72
  if running?(cid)
64
- execute('docker', 'stop', cid)
73
+ execute('docker', 'stop', '-t', '1', cid)
65
74
  end
66
75
  end
67
76
 
68
77
  def rm(cid)
69
78
  if created?(cid)
70
- execute('docker', 'rm', cid)
79
+ execute('docker', 'rm', '-v', cid)
71
80
  end
72
81
  end
73
82
 
74
- def inspect(cid)
75
- # DISCUSS: Is there a chance that this will change?
83
+ def inspect_container(cid)
84
+ # DISCUSS: Is there a chance that this json will change after the container
85
+ # has been brought up?
76
86
  @data ||= JSON.parse(execute('docker', 'inspect', cid)).first
77
87
  end
78
88
 
89
+ def all_containers
90
+ execute('docker', 'ps', '-a', '-q', '-notrunc').to_s.split
91
+ end
92
+
93
+ def docker_bridge_ip
94
+ output = execute('/sbin/ip', '-4', 'addr', 'show', 'scope', 'global', 'docker0')
95
+ if output =~ /^\s+inet ([0-9.]+)\/[0-9]+\s+/
96
+ return $1.to_s
97
+ else
98
+ # TODO: Raise an user friendly message
99
+ raise 'Unable to fetch docker bridge IP!'
100
+ end
101
+ end
102
+
79
103
  private
80
104
 
81
105
  def execute(*cmd, &block)