docker-provider 0.0.2 → 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
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)