dockly 1.2.1 → 1.3.1

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.
data/.gitignore CHANGED
@@ -13,3 +13,4 @@ build/
13
13
  dist/
14
14
 
15
15
  docker-export-ubuntu-latest.tar.gz
16
+ tmp
data/README.md CHANGED
@@ -2,8 +2,8 @@
2
2
  [![Build Status](https://travis-ci.org/swipely/dockly.png?branch=refactor_setup)](https://travis-ci.org/swipely/dockly)
3
3
  [![Dependency Status](https://gemnasium.com/swipely/dockly.png)](https://gemnasium.com/swipely/dockly)
4
4
 
5
- Dockly
6
- =======
5
+ ![Dockly](https://raw.github.com/swipely/dockly/master/img/dockly.png)
6
+ ======================================================================
7
7
 
8
8
  `dockly` is a gem made to ease the pain of packaging an application. For this gem to be useful, quite a few assumptions can be made about your stack:
9
9
 
@@ -74,10 +74,14 @@ The `build_cache` DSL is used to prevent rebuilding assets every build and used
74
74
  - description: the name prepended to the package; allows for namespacing your caches
75
75
  - `hash_command`
76
76
  - required: `true`
77
- - description: command run inside of the Docker image to determine if the build cache is up to date (eg. `md5sum ... | awk '{ print $1 }'`)
77
+ - description: command run to determine if the build cache is up to date (eg. `md5sum ... | awk '{ print $1 }'`)
78
+ - `parameter_command`
79
+ - required: `false`
80
+ - allows multiple
81
+ - description: command run to build specific versions of build caches -- useful for multiple operating systems (not required)
78
82
  - `build_command`
79
83
  - required: `true`
80
- - description: command run inside of the Docker image when the build cache is out of date
84
+ - description: command run when the build cache is out of date
81
85
  - `output_dir`
82
86
  - required: `true`
83
87
  - description: where the cache is located in the Docker image filesystem
@@ -135,6 +139,7 @@ In addition to the above attributes, `docker` has the following references:
135
139
 
136
140
  - `build_cache`
137
141
  - required: `false`
142
+ - allows multiple
138
143
  - class: `Dockly::BuildCache`
139
144
  - description: a caching system to stop rebuilding/compiling the same files every time
140
145
 
@@ -15,8 +15,8 @@ Gem::Specification.new do |gem|
15
15
  gem.require_paths = %w{lib}
16
16
  gem.version = Dockly::VERSION
17
17
  gem.add_dependency 'clamp', '~> 0.6'
18
- gem.add_dependency 'docker-api', '~> 1.7.2'
19
- gem.add_dependency 'dockly-util', '~> 0.0.5'
18
+ gem.add_dependency 'docker-api', '~> 1.7.3'
19
+ gem.add_dependency 'dockly-util', '~> 0.0.6'
20
20
  gem.add_dependency 'excon'
21
21
  gem.add_dependency 'fog', '~> 1.18.0'
22
22
  gem.add_dependency 'foreman'
Binary file
@@ -1,139 +1,16 @@
1
- require 'tempfile'
2
-
3
- class Dockly::BuildCache
4
- include Dockly::Util::DSL
5
- include Dockly::Util::Logger::Mixin
6
-
7
- logger_prefix '[dockly build_cache]'
8
-
9
- attr_accessor :image
10
- dsl_attribute :s3_bucket, :s3_object_prefix, :hash_command, :output_dir, :build_command,
11
- :use_latest, :tmp_dir
12
-
13
- default_value :use_latest, false
14
- default_value :tmp_dir, '/tmp'
15
-
16
- def execute!
17
- ensure_present! :image
18
- debug "Looking for cache for hash: #{hash_output}"
19
- if up_to_date?
20
- debug "build cache up to date, pulling from s3"
21
- insert_cache
22
- else
23
- insert_latest
24
- debug "build cache out of date, running build"
25
- run_build
26
- end
27
- debug "finished build cache"
28
- image
29
- end
30
-
31
- def insert_cache
32
- push_cache(hash_output)
33
- end
34
-
35
- def insert_latest
36
- if use_latest
37
- debug "attempting to push latest"
38
- if cache = push_cache("latest")
39
- debug "pushed latest, removing local file"
40
- File.delete(cache.path)
41
- end
42
- end
43
- end
44
-
45
- def run_build
46
- container = image.run(build_command)
47
- status = container.wait(3600)['StatusCode'] # 1 hour max timeout
48
- raise "Build Cache `#{build_command}` failed to run." unless status.zero?
49
- cache = copy_output_dir(container)
50
- debug "pushing #{output_dir} to s3"
51
- push_to_s3(cache)
52
- cache.close
53
- self.image = container.commit
54
- end
55
-
56
- def push_cache(version)
57
- ensure_present! :output_dir
58
- if cache = pull_from_s3(version)
59
- debug "inserting to #{output_dir}"
60
- container = image.run("mkdir -p #{File.dirname(output_dir)}")
61
- image_with_dir = container.tap { |c| c.wait }.commit
62
- self.image = image_with_dir.insert_local(
63
- 'localPath' => cache.path,
64
- 'outputPath' => File.dirname(output_dir)
65
- )
66
- cache.close
67
- else
68
- info "could not find #{s3_object(version)}"
69
- end
70
- end
71
-
72
- def up_to_date?
73
- ensure_present! :s3_bucket, :s3_object_prefix
74
- connection.head_object(s3_bucket, s3_object(hash_output))
75
- true
76
- rescue Excon::Errors::NotFound
77
- false
78
- end
79
-
80
- def pull_from_s3(version)
81
- ensure_present! :s3_bucket, :s3_object_prefix
1
+ module Dockly::BuildCache
2
+ end
82
3
 
83
- file_name = s3_object(version)
84
- file_path = File.join(tmp_dir,file_name)
4
+ require 'dockly/build_cache/base'
5
+ require 'dockly/build_cache/docker'
6
+ require 'dockly/build_cache/local'
85
7
 
86
- FileUtils.mkdir_p(File.dirname(file_path))
87
- unless File.exist?(file_path)
88
- object = connection.get_object(s3_bucket, file_name)
8
+ module Dockly::BuildCache
9
+ class << self
10
+ attr_writer :model
89
11
 
90
- file = File.open(file_path, 'w+b')
91
- file.write(object.body)
92
- file.tap(&:rewind)
93
- else
94
- File.open(file_path, 'rb')
12
+ def model
13
+ @mode ||= Dockly::BuildCache::Docker
95
14
  end
96
- rescue Excon::Errors::NotFound
97
- nil
98
- end
99
-
100
- def push_to_s3(file)
101
- ensure_present! :s3_bucket, :s3_object_prefix
102
- connection.put_object(s3_bucket, s3_object(hash_output), file.read)
103
- connection.copy_object(s3_bucket, s3_object(hash_output), s3_bucket, s3_object("latest"))
104
- end
105
-
106
- def copy_output_dir(container)
107
- ensure_present! :output_dir
108
- file_path = File.join(tmp_dir,s3_object(hash_output))
109
- FileUtils.mkdir_p(File.dirname(file_path))
110
- file = File.open(file_path, 'w+b')
111
- container.wait(3600) # 1 hour max timeout
112
- container.copy(output_dir) { |chunk| file.write(chunk) }
113
- file.tap(&:rewind)
114
- end
115
-
116
- def hash_output
117
- ensure_present! :image, :hash_command
118
- @hash_output ||= begin
119
- resp = ""
120
- container = image.run(hash_command)
121
- container.attach { |source,chunk| resp += chunk }
122
- status = container.wait['StatusCode']
123
- raise "Hash Command `#{hash_command} failed to run" unless status.zero?
124
- resp.strip
125
- end
126
- end
127
-
128
- def file_output(file)
129
- File.join(File.dirname(output_dir), File.basename(file.path))
130
- end
131
-
132
- def s3_object(file)
133
- "#{s3_object_prefix}#{file}"
134
- end
135
-
136
- def connection
137
- Dockly::AWS.s3
138
15
  end
139
16
  end
@@ -0,0 +1,117 @@
1
+ require 'tempfile'
2
+
3
+ class Dockly::BuildCache::Base
4
+ include Dockly::Util::DSL
5
+ include Dockly::Util::Logger::Mixin
6
+
7
+ logger_prefix '[dockly build_cache]'
8
+
9
+ dsl_attribute :s3_bucket, :s3_object_prefix, :use_latest,
10
+ :hash_command, :build_command, :parameter_commands,
11
+ :base_dir, :command_dir, :output_dir, :tmp_dir
12
+
13
+ default_value :use_latest, false
14
+ default_value :parameter_commands, {}
15
+ default_value :command_dir, '.'
16
+ default_value :output_dir, '.'
17
+ default_value :tmp_dir, '/tmp'
18
+
19
+ def execute!
20
+ debug "Looking for cache for hash: #{hash_output}"
21
+ if up_to_date?
22
+ debug "build cache up to date, pulling from s3"
23
+ insert_cache
24
+ else
25
+ insert_latest
26
+ debug "build cache out of date, running build"
27
+ run_build
28
+ end
29
+ debug "finished build cache"
30
+ end
31
+
32
+ def insert_cache
33
+ push_cache(hash_output)
34
+ end
35
+
36
+ def insert_latest
37
+ if use_latest
38
+ debug "attempting to push latest"
39
+ if cache = push_cache("latest")
40
+ debug "pushed latest, removing local file"
41
+ File.delete(cache.path)
42
+ end
43
+ end
44
+ end
45
+
46
+ def up_to_date?
47
+ ensure_present! :s3_bucket, :s3_object_prefix
48
+ connection.head_object(s3_bucket, s3_object(hash_output))
49
+ true
50
+ rescue Excon::Errors::NotFound
51
+ false
52
+ end
53
+
54
+ def pull_from_s3(version)
55
+ ensure_present! :s3_bucket, :s3_object_prefix
56
+
57
+ file_name = s3_object(version)
58
+ file_path = File.join(tmp_dir,file_name)
59
+
60
+ FileUtils.mkdir_p(File.dirname(file_path))
61
+ unless File.exist?(file_path)
62
+ object = connection.get_object(s3_bucket, file_name)
63
+
64
+ file = File.open(file_path, 'w+b')
65
+ file.write(object.body)
66
+ file.tap(&:rewind)
67
+ else
68
+ File.open(file_path, 'rb')
69
+ end
70
+ rescue Excon::Errors::NotFound
71
+ nil
72
+ end
73
+
74
+ def hash_output
75
+ end
76
+
77
+ def parameter_output(command)
78
+ end
79
+
80
+ def parameter_command(command)
81
+ parameter_commands[command] = nil
82
+ end
83
+
84
+ def push_to_s3(file)
85
+ ensure_present! :s3_bucket, :s3_object_prefix
86
+ connection.put_object(s3_bucket, s3_object(hash_output), file.read)
87
+ connection.copy_object(s3_bucket, s3_object(hash_output), s3_bucket, s3_object("latest"))
88
+ end
89
+
90
+ def file_output(file)
91
+ File.join(File.dirname(output_dir), File.basename(file.path))
92
+ end
93
+
94
+ def s3_object(file)
95
+ output = "#{s3_object_prefix}"
96
+ parameter_commands.each do |parameter_command, _|
97
+ output << "#{parameter_output(parameter_command)}_" unless parameter_output(parameter_command).nil?
98
+ end
99
+ output << "#{file}"
100
+ end
101
+
102
+ def command_directory
103
+ File.join(base_directory, command_dir)
104
+ end
105
+
106
+ def output_directory
107
+ File.join(base_directory, output_dir)
108
+ end
109
+
110
+ def base_directory
111
+ base_dir || docker.git_archive
112
+ end
113
+
114
+ def connection
115
+ Dockly::AWS.s3
116
+ end
117
+ end
@@ -0,0 +1,72 @@
1
+ class Dockly::BuildCache::Docker < Dockly::BuildCache::Base
2
+ attr_accessor :image
3
+
4
+ def execute!
5
+ ensure_present! :image
6
+ super
7
+ image
8
+ end
9
+
10
+ def run_build
11
+ status, body, container = run_command(build_command)
12
+ raise "Build Cache `#{build_command}` failed to run." unless status.zero?
13
+ cache = copy_output_dir(container)
14
+ debug "pushing #{output_directory} to s3"
15
+ push_to_s3(cache)
16
+ cache.close
17
+ self.image = container.commit
18
+ end
19
+
20
+ def push_cache(version)
21
+ ensure_present! :output_dir
22
+ if cache = pull_from_s3(version)
23
+ debug "inserting to #{output_directory}"
24
+ container = image.run("mkdir -p #{File.dirname(output_directory)}")
25
+ image_with_dir = container.tap { |c| c.wait }.commit
26
+ self.image = image_with_dir.insert_local(
27
+ 'localPath' => cache.path,
28
+ 'outputPath' => File.dirname(output_directory)
29
+ )
30
+ cache.close
31
+ else
32
+ info "could not find #{s3_object(version)}"
33
+ end
34
+ end
35
+
36
+ def copy_output_dir(container)
37
+ ensure_present! :output_dir
38
+ file_path = File.join(tmp_dir,s3_object(hash_output))
39
+ FileUtils.mkdir_p(File.dirname(file_path))
40
+ file = File.open(file_path, 'w+b')
41
+ container.wait(3600) # 1 hour max timeout
42
+ container.copy(output_directory) { |chunk| file.write(chunk) }
43
+ file.tap(&:rewind)
44
+ end
45
+
46
+ def hash_output
47
+ ensure_present! :image, :hash_command
48
+ @hash_output ||= begin
49
+ status, body, container = run_command(hash_command)
50
+ raise "Hash Command `#{hash_command}` failed to run" unless status.zero?
51
+ body
52
+ end
53
+ end
54
+
55
+ def parameter_output(command)
56
+ ensure_present! :image
57
+ raise "Parameter Command tried to run but not found" unless parameter_commands.keys.include?(command)
58
+ @parameter_commands[command] ||= begin
59
+ status, body, container = run_command(command)
60
+ raise "Parameter Command `#{command}` failed to run" unless status.zero?
61
+ body
62
+ end
63
+ end
64
+
65
+ def run_command(command)
66
+ resp = ""
67
+ container = image.run(["/bin/bash", "-lc", "cd #{command_directory} && #{command}"])
68
+ container.attach { |source,chunk| resp += chunk }
69
+ status = container.wait['StatusCode']
70
+ [status, resp.strip, container]
71
+ end
72
+ end
@@ -0,0 +1,56 @@
1
+ class Dockly::BuildCache::Local < Dockly::BuildCache::Base
2
+ def run_build
3
+ puts "Build command: #{build_command}"
4
+ status, body = run_command(build_command)
5
+ raise "Build Cache `#{build_command}` failed to run." unless status.success?
6
+ FileUtils.mkdir_p(File.dirname(save_file))
7
+ tar_file = Dockly::Util::Tar.tar(output_directory, save_file)
8
+ push_to_s3(tar_file)
9
+ end
10
+
11
+ def output_directory
12
+ File.expand_path(File.join(Dir.pwd, output_dir))
13
+ end
14
+
15
+ def save_file
16
+ File.expand_path("build/build_cache/#{s3_object_prefix}#{hash_output}")
17
+ end
18
+
19
+ def push_cache(version)
20
+ ensure_present! :output_dir
21
+ if cache = pull_from_s3(version)
22
+ dest = File.dirname(File.expand_path(output_dir))
23
+ Dockly::Util::Tar.untar(cache, dest)
24
+ else
25
+ info "could not find #{s3_object(output_dir)}"
26
+ end
27
+ end
28
+
29
+ def hash_output
30
+ ensure_present! :hash_command
31
+ @hash_output ||= begin
32
+ status, body = run_command(hash_command)
33
+ raise "Hash Command `#{hash_command} failed to run" unless status.success?
34
+ body
35
+ end
36
+ end
37
+
38
+ def parameter_output(command)
39
+ raise "Parameter Command tried to run but not found" unless parameter_commands.keys.include?(command)
40
+ @parameter_commands[command] ||= begin
41
+ status, body = run_command(command)
42
+ raise "Parameter Command `#{command} failed to run" unless status.success?
43
+ body
44
+ end
45
+ end
46
+
47
+ def run_command(command)
48
+ resp = ""
49
+ Bundler.with_clean_env do
50
+ IO.popen(command) do |io|
51
+ resp << io.read
52
+ end
53
+ end
54
+ [$?, resp.strip]
55
+ end
56
+ end
@@ -39,8 +39,47 @@ class Dockly::ListCommand < Dockly::AbstractCommand
39
39
  end
40
40
  end
41
41
 
42
+ class Dockly::BuildCacheCommand < Dockly::AbstractCommand
43
+ parameter 'DOCKER', 'the name of the docker image to build for', :attribute_name => :docker_name
44
+ option ['-l', '--list'], :flag, 'list the build caches', :default => false, :attribute_name => :list
45
+ option ['-L', '--local'], :flag, 'use local build caches', :default => false, :attribute_name => :local
46
+
47
+ def execute
48
+ Dockly::BuildCache.model = Dockly::BuildCache::Local
49
+ super
50
+ build_caches = Dockly.docker(docker_name.to_sym).build_cache || []
51
+ raise "No build cache for #{docker_name}" if build_caches.empty?
52
+ if list?
53
+ build_caches.each_with_index do |build_cache, index|
54
+ puts "#{index + 1}. Hash: #{build_cache.hash_command} Build: #{build_cache.build_command}"
55
+ end
56
+ else
57
+ bcs = if local?
58
+ convert_bc_to_local_bc(docker_name)
59
+ else
60
+ build_caches
61
+ end
62
+ bcs.each do |bc|
63
+ bc.execute!
64
+ end
65
+ end
66
+ end
67
+ end
68
+
42
69
  class Dockly::Cli < Dockly::AbstractCommand
43
70
  subcommand ['build', 'b'], 'Create package', Dockly::BuildCommand
44
71
  subcommand ['list', 'l'], 'List packages', Dockly::ListCommand
72
+ subcommand ['build_cache', 'bc'], 'Build Cache commands', Dockly::BuildCacheCommand
45
73
  end
46
74
 
75
+ def convert_bc_to_local_bc(docker_name)
76
+ lbcs = []
77
+ Dockly.docker(docker_name.to_sym).build_cache.each do |bc|
78
+ lbc = Dockly::BuildCache::Local.new! { name bc.name }
79
+ bc.instance_variables.each do |variable|
80
+ lbc.instance_variable_set(variable, bc.instance_variable_get(variable))
81
+ end
82
+ lbcs << lbc
83
+ end
84
+ lbcs
85
+ end
@@ -10,13 +10,15 @@ class Dockly::Docker
10
10
  include Dockly::Util::Logger::Mixin
11
11
 
12
12
  logger_prefix '[dockly docker]'
13
+
14
+ dsl_class_attribute :build_cache, Dockly::BuildCache.model, type: Array
15
+
13
16
  dsl_attribute :name, :import, :git_archive, :build, :tag, :build_dir, :package_dir,
14
- :timeout, :cleanup_images, :build_caches
17
+ :timeout, :cleanup_images
15
18
 
16
19
  default_value :tag, nil
17
20
  default_value :build_dir, 'build/docker'
18
21
  default_value :package_dir, '/opt/docker'
19
- default_value :build_caches, []
20
22
  default_value :cleanup_images, false
21
23
  default_value :timeout, 60
22
24
 
@@ -43,7 +45,7 @@ class Dockly::Docker
43
45
 
44
46
  def run_build_caches(image)
45
47
  info "starting build caches"
46
- build_caches.each do |cache|
48
+ (build_cache || []).each do |cache|
47
49
  cache.image = image
48
50
  image = cache.execute!
49
51
  end
@@ -158,10 +160,6 @@ class Dockly::Docker
158
160
  path
159
161
  end
160
162
 
161
- def build_cache(&block)
162
- build_caches << Dockly::BuildCache.new(&block)
163
- end
164
-
165
163
  def repository(value = nil)
166
164
  name(value)
167
165
  end
@@ -1,3 +1,5 @@
1
+ require 'fileutils'
2
+
1
3
  module Dockly::Util::Tar
2
4
  extend self
3
5
 
@@ -24,4 +26,33 @@ module Dockly::Util::Tar
24
26
  magic = magic.unpack('H*')[0]
25
27
  magic == "1f8b"
26
28
  end
29
+
30
+ # Creates a tar file in memory recursively
31
+ # from the given path.
32
+ #
33
+ # Returns a StringIO whose underlying String
34
+ # is the contents of the tar file.
35
+ def tar(path, output)
36
+ FileUtils.mkdir_p(File.dirname(output))
37
+ puts "tarring #{path} to #{output}"
38
+ tar_command = "tar -cf #{output} -C #{File.dirname(path)} #{File.basename(path)}"
39
+ puts "Tar Command: #{tar_command}"
40
+ IO.popen(tar_command) do |io|
41
+ puts io.read
42
+ end
43
+ File.open(output, 'rb+')
44
+ end
45
+
46
+ # untars the given IO into the specified
47
+ # directory
48
+ def untar(input_io, destination)
49
+ puts "untarring #{input_io.path} to #{destination}"
50
+ FileUtils.mkdir_p(destination)
51
+ untar_command = "tar -xf #{input_io.path} -C #{destination}"
52
+ puts "Untar command: #{untar_command}"
53
+ IO.popen(untar_command) do |io|
54
+ puts io.read
55
+ end
56
+ input_io
57
+ end
27
58
  end
@@ -1,3 +1,3 @@
1
1
  module Dockly
2
- VERSION = '1.2.1'
2
+ VERSION = '1.3.1'
3
3
  end
@@ -0,0 +1,74 @@
1
+ require 'spec_helper'
2
+
3
+ describe Dockly::BuildCache::Base do
4
+ subject { described_class.new(:name => :test_build_cache) }
5
+
6
+ before do
7
+ subject.s3_bucket 'lol'
8
+ subject.s3_object_prefix 'swag'
9
+ subject.hash_command 'md5sum /etc/vim/vimrc'
10
+ subject.build_command 'touch lol'
11
+ subject.output_dir '/'
12
+ end
13
+
14
+ describe '#up_to_date?' do
15
+ context 'when the object exists in s3' do
16
+ before { subject.connection.stub(:head_object) }
17
+
18
+ its(:up_to_date?) { should be_true }
19
+ end
20
+
21
+ context 'when the object does not exist in s3' do
22
+ before do
23
+ subject.connection.stub(:head_object)
24
+ .and_raise(Excon::Errors::NotFound.new('help'))
25
+ end
26
+
27
+ its(:up_to_date?) { should be_false }
28
+ end
29
+ end
30
+
31
+ describe '#pull_from_s3' do
32
+ let(:file) { subject.pull_from_s3('hey') }
33
+ let(:object) { double(:object) }
34
+
35
+ before do
36
+ subject.connection.stub(:get_object).and_return object
37
+ object.stub(:body).and_return 'hey dad'
38
+ end
39
+
40
+ after do
41
+ path = file.path
42
+ file.close
43
+ File.delete(path)
44
+ end
45
+
46
+ it 'returns a File with the data pulled' do
47
+ file.read.should == 'hey dad'
48
+ end
49
+ end
50
+
51
+ describe '#s3_object' do
52
+ before do
53
+ subject.stub(:s3_object_prefix) { 'lol' }
54
+ subject.stub(:hash_output) { 'lel' }
55
+ end
56
+
57
+ context "without an arch_output" do
58
+ it 'returns the s3_prefix merged with the hash_output' do
59
+ subject.s3_object(subject.hash_output).should == 'lollel'
60
+ end
61
+ end
62
+
63
+ context "with an arch_output" do
64
+ before do
65
+ subject.parameter_command "linux"
66
+ subject.stub(:parameter_output) { "linux" }
67
+ end
68
+
69
+ it 'returns the s3_prefix merged with the hash_output' do
70
+ subject.s3_object(subject.hash_output).should == 'lollinux_lel'
71
+ end
72
+ end
73
+ end
74
+ end
@@ -0,0 +1,181 @@
1
+ require 'spec_helper'
2
+
3
+ describe Dockly::BuildCache::Docker, :docker do
4
+ let!(:build_cache) { described_class.new!(:name => :test_build_cache) }
5
+ let!(:docker) do
6
+ Dockly::Docker.new!(:name => :test_docker) do
7
+ git_archive '/app'
8
+ end
9
+ end
10
+ let(:image) { ::Docker::Image.build('from base') }
11
+
12
+ before do
13
+ build_cache.s3_bucket 'lol'
14
+ build_cache.s3_object_prefix 'swag'
15
+ build_cache.image = image
16
+ build_cache.hash_command 'md5sum /etc/vim/vimrc'
17
+ build_cache.build_command 'touch lol'
18
+ build_cache.output_dir '/etc/vim'
19
+ build_cache.base_dir '/'
20
+ docker.build_cache :test_build_cache
21
+ end
22
+
23
+ describe "#initialize" do
24
+ context "base_dir is the docker git_archive" do
25
+ before do
26
+ build_cache.instance_variable_set(:@base_dir, nil)
27
+ end
28
+
29
+ it "should return the base_directory as the git_archive" do
30
+ expect(build_cache.base_directory).to eq(docker.git_archive)
31
+ end
32
+ end
33
+ end
34
+
35
+ describe '#execute!' do
36
+ before do
37
+ build_cache.stub(:up_to_date?).and_return(up_to_date)
38
+ build_cache.stub(:push_cache)
39
+ build_cache.stub(:push_to_s3)
40
+ end
41
+
42
+ context 'when the object is up to date' do
43
+ let(:up_to_date) { true }
44
+
45
+ it "does not have the file lol" do
46
+ i = build_cache.execute!
47
+ output = ""
48
+ i.run('ls').attach { |source,chunk| output += chunk }
49
+ output.should_not include('lol')
50
+ end
51
+ end
52
+
53
+ context 'when the object is not up to date' do
54
+ let(:up_to_date) { false }
55
+
56
+ before do
57
+ build_cache.stub(:copy_output_dir) { StringIO.new }
58
+ end
59
+
60
+ it "does have the file lol" do
61
+ i = build_cache.execute!
62
+ output = ""
63
+ i.run('ls /').attach { |source,chunk| output += chunk }
64
+ output.should include('lol')
65
+ end
66
+ end
67
+ end
68
+
69
+ describe "#run_build" do
70
+ before do
71
+ build_cache.stub(:push_to_s3)
72
+ end
73
+
74
+ context "when the build succeeds" do
75
+ it "does have the file lol" do
76
+ i = build_cache.run_build
77
+ output = ""
78
+ i.run('ls').attach { |source,chunk| output += chunk }
79
+ output.should include('lol')
80
+ end
81
+ end
82
+
83
+ context "when the build fails" do
84
+ let!(:image) { build_cache.image }
85
+ before do
86
+ build_cache.image = double(:image).stub(:run) {
87
+ stub(:container, { :wait => { 'StatusCode' => 1 } })
88
+ }
89
+ end
90
+
91
+ after do
92
+ build_cache.image = image
93
+ end
94
+
95
+ it "raises an error" do
96
+ expect { build_cache.run_build }.to raise_error
97
+ end
98
+ end
99
+ end
100
+
101
+ describe '#hash_output' do
102
+ let(:output) {
103
+ "682aa2a07693cc27756eee9751db3903 /etc/vim/vimrc"
104
+ }
105
+
106
+ context "when hash command returns successfully" do
107
+ before do
108
+ build_cache.image = image
109
+ end
110
+
111
+ it 'returns the output of the hash_command in the container' do
112
+ build_cache.hash_output.should == output
113
+ end
114
+ end
115
+
116
+ context "when hash command returns failure" do
117
+ before do
118
+ build_cache.image = double(:image).stub(:run, {
119
+ :wait => { 'StatusCode' => 1 }
120
+ })
121
+ end
122
+
123
+ it 'raises an error' do
124
+ expect { build_cache.hash_output }.to raise_error
125
+ end
126
+ end
127
+ end
128
+
129
+ describe '#copy_output_dir' do
130
+ let(:container) { Docker::Container.create('Image' => 'base', 'Cmd' => %w[true]) }
131
+ let(:file) { build_cache.copy_output_dir(container) }
132
+ let(:hash) { 'this_really_unique_hash' }
133
+ let(:path) { file.path }
134
+
135
+ before do
136
+ build_cache.stub(:hash_output).and_return(hash)
137
+ build_cache.output_dir '/root/'; container.wait
138
+ end
139
+ after do
140
+ file.close
141
+ File.delete(path)
142
+ end
143
+
144
+ it 'returns a File of the specified directory from the Container' do
145
+ expect(file.path).to include("#{hash}")
146
+ file.should be_a File
147
+ file.read.should include('root/.bashrc')
148
+ end
149
+ end
150
+
151
+ describe '#parameter_output' do
152
+ before do
153
+ build_cache.parameter_command command
154
+ end
155
+ let(:output) { "3.8.0-27-generic" }
156
+
157
+ context "when parameter command returns successfully" do
158
+ let(:command) { "uname -r" }
159
+ it 'returns the output of the parameter_command' do
160
+ expect(build_cache.parameter_output(command)).to eq(output)
161
+ end
162
+ end
163
+
164
+ context "when parameter command returns failure" do
165
+ let(:command) { 'md6sum' }
166
+
167
+ it 'raises an error' do
168
+ expect { build_cache.parameter_output(command) }.to raise_error
169
+ end
170
+ end
171
+
172
+ context "when a parameter command isn't previously added" do
173
+ let(:command) { "md5sum /etc/vim/vimrc" }
174
+
175
+ it 'raises an error' do
176
+ expect { build_cache.parameter_output("#{command}1") }.to raise_error
177
+ end
178
+ end
179
+ end
180
+ end
181
+
@@ -0,0 +1,141 @@
1
+ require 'spec_helper'
2
+
3
+ describe Dockly::BuildCache::Local do
4
+ let(:build_cache) { described_class.new!(:name => :test_local_build_cache) }
5
+
6
+ before do
7
+ build_cache.s3_bucket 'fake'
8
+ build_cache.s3_object_prefix 'object'
9
+ build_cache.hash_command "md5sum #{File.join(Dir.pwd, 'Gemfile')} | awk '{ print $1 }'"
10
+ build_cache.build_command 'mkdir -p tmp && touch tmp/lol'
11
+ build_cache.output_dir 'lib'
12
+ end
13
+
14
+ describe '#execute!' do
15
+ before do
16
+ build_cache.stub(:hash_output).and_return('abcdef')
17
+ build_cache.stub(:up_to_date?).and_return(up_to_date)
18
+ build_cache.stub(:push_cache)
19
+ build_cache.stub(:push_to_s3)
20
+
21
+ if File.exist?('tmp/lol')
22
+ File.delete('tmp/lol')
23
+ end
24
+ end
25
+
26
+ context 'when the object is up to date' do
27
+ let(:up_to_date) { true }
28
+
29
+ it "does not have the file lol" do
30
+ i = build_cache.execute!
31
+ output = ""
32
+ IO.popen('ls tmp') { |io| output += io.read }
33
+ output.should_not include('lol')
34
+ end
35
+ end
36
+
37
+ context 'when the object is not up to date' do
38
+ let(:up_to_date) { false }
39
+
40
+ before do
41
+ build_cache.stub(:copy_output_dir) { StringIO.new }
42
+ end
43
+
44
+ after do
45
+ if File.exist?('tmp/lol')
46
+ File.delete('tmp/lol')
47
+ end
48
+ end
49
+
50
+ it "does have the file lol" do
51
+ i = build_cache.execute!
52
+ output = ""
53
+ IO.popen('ls tmp') { |io| output << io.read }
54
+ output.should include('lol')
55
+ end
56
+ end
57
+ end
58
+
59
+ describe "#run_build" do
60
+ before do
61
+ build_cache.stub(:push_to_s3)
62
+ end
63
+
64
+ context "when the build succeeds" do
65
+ it "does have the file lol" do
66
+ i = build_cache.run_build
67
+ output = ""
68
+ IO.popen('ls tmp') { |io| output << io.read }
69
+ output.should include('lol')
70
+ end
71
+ end
72
+
73
+ context "when the build fails" do
74
+ before do
75
+ build_cache.build_command 'md6sum'
76
+ end
77
+
78
+ it "raises an error" do
79
+ expect { build_cache.run_build }.to raise_error
80
+ end
81
+ end
82
+ end
83
+
84
+ describe '#hash_output' do
85
+ let(:output) {
86
+ "f683463a09482287c33959ab71a87189"
87
+ }
88
+
89
+ context "when hash command returns successfully" do
90
+ it 'returns the output of the hash_command' do
91
+ build_cache.hash_output.should == output
92
+ end
93
+ end
94
+
95
+ context "when hash command returns failure" do
96
+ before do
97
+ build_cache.hash_command 'md6sum'
98
+ end
99
+
100
+ it 'raises an error' do
101
+ expect { build_cache.hash_output }.to raise_error
102
+ end
103
+ end
104
+ end
105
+
106
+ describe '#parameter_output' do
107
+ before do
108
+ build_cache.parameter_command command
109
+ end
110
+
111
+ let(:output) { "3.8.0-23-generic" }
112
+ context "when parameter command returns successfully" do
113
+ let(:command) { "uname -r" }
114
+ let(:status) { double(:status) }
115
+ before do
116
+ status.stub(:"success?") { true }
117
+ build_cache.stub(:run_command) { [status, output] }
118
+ end
119
+
120
+ it 'returns the output of the parameter_command' do
121
+ expect(build_cache.parameter_output(command)).to eq(output)
122
+ end
123
+ end
124
+
125
+ context "when parameter command returns failure" do
126
+ let(:command) { "md6sum" }
127
+
128
+ it 'raises an error' do
129
+ expect { build_cache.parameter_output(command) }.to raise_error
130
+ end
131
+ end
132
+
133
+ context "when a parameter command isn't previously added" do
134
+ let(:command) { "md5sum /etc/vim/vimrc" }
135
+
136
+ it 'raises an error' do
137
+ expect { build_cache.parameter_output("#{command}1") }.to raise_error
138
+ end
139
+ end
140
+ end
141
+ end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: dockly
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.2.1
4
+ version: 1.3.1
5
5
  prerelease:
6
6
  platform: ruby
7
7
  authors:
@@ -9,7 +9,7 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2013-11-15 00:00:00.000000000 Z
12
+ date: 2013-11-28 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: clamp
@@ -34,7 +34,7 @@ dependencies:
34
34
  requirements:
35
35
  - - ~>
36
36
  - !ruby/object:Gem::Version
37
- version: 1.7.2
37
+ version: 1.7.3
38
38
  type: :runtime
39
39
  prerelease: false
40
40
  version_requirements: !ruby/object:Gem::Requirement
@@ -42,7 +42,7 @@ dependencies:
42
42
  requirements:
43
43
  - - ~>
44
44
  - !ruby/object:Gem::Version
45
- version: 1.7.2
45
+ version: 1.7.3
46
46
  - !ruby/object:Gem::Dependency
47
47
  name: dockly-util
48
48
  requirement: !ruby/object:Gem::Requirement
@@ -50,7 +50,7 @@ dependencies:
50
50
  requirements:
51
51
  - - ~>
52
52
  - !ruby/object:Gem::Version
53
- version: 0.0.5
53
+ version: 0.0.6
54
54
  type: :runtime
55
55
  prerelease: false
56
56
  version_requirements: !ruby/object:Gem::Requirement
@@ -58,7 +58,7 @@ dependencies:
58
58
  requirements:
59
59
  - - ~>
60
60
  - !ruby/object:Gem::Version
61
- version: 0.0.5
61
+ version: 0.0.6
62
62
  - !ruby/object:Gem::Dependency
63
63
  name: excon
64
64
  requirement: !ruby/object:Gem::Requirement
@@ -256,9 +256,13 @@ files:
256
256
  - Rakefile
257
257
  - bin/dockly
258
258
  - dockly.gemspec
259
+ - img/dockly.png
259
260
  - lib/dockly.rb
260
261
  - lib/dockly/aws.rb
261
262
  - lib/dockly/build_cache.rb
263
+ - lib/dockly/build_cache/base.rb
264
+ - lib/dockly/build_cache/docker.rb
265
+ - lib/dockly/build_cache/local.rb
262
266
  - lib/dockly/cli.rb
263
267
  - lib/dockly/deb.rb
264
268
  - lib/dockly/docker.rb
@@ -270,7 +274,9 @@ files:
270
274
  - lib/foreman/cli_fix.rb
271
275
  - lib/foreman/export/base_fix.rb
272
276
  - spec/dockly/aws_spec.rb
273
- - spec/dockly/build_cache_spec.rb
277
+ - spec/dockly/build_cache/base_spec.rb
278
+ - spec/dockly/build_cache/docker_spec.rb
279
+ - spec/dockly/build_cache/local_spec.rb
274
280
  - spec/dockly/deb_spec.rb
275
281
  - spec/dockly/docker_spec.rb
276
282
  - spec/dockly/foreman_spec.rb
@@ -311,7 +317,9 @@ specification_version: 3
311
317
  summary: Packaging made easy
312
318
  test_files:
313
319
  - spec/dockly/aws_spec.rb
314
- - spec/dockly/build_cache_spec.rb
320
+ - spec/dockly/build_cache/base_spec.rb
321
+ - spec/dockly/build_cache/docker_spec.rb
322
+ - spec/dockly/build_cache/local_spec.rb
315
323
  - spec/dockly/deb_spec.rb
316
324
  - spec/dockly/docker_spec.rb
317
325
  - spec/dockly/foreman_spec.rb
@@ -1,175 +0,0 @@
1
- require 'spec_helper'
2
-
3
- describe Dockly::BuildCache, :docker do
4
- subject { described_class.new(:name => :test_build_cache) }
5
- let(:image) { ::Docker::Image.build('from base') }
6
-
7
- before do
8
- subject.s3_bucket 'lol'
9
- subject.s3_object_prefix 'swag'
10
- subject.image = image
11
- subject.hash_command 'md5sum /etc/vim/vimrc'
12
- subject.build_command 'touch lol'
13
- subject.output_dir '/'
14
- end
15
-
16
- describe '#execute!' do
17
- before do
18
- subject.stub(:up_to_date?).and_return(up_to_date)
19
- subject.stub(:push_cache)
20
- subject.stub(:push_to_s3)
21
- end
22
-
23
- context 'when the object is up to date' do
24
- let(:up_to_date) { true }
25
-
26
- it "does not have the file lol" do
27
- i = subject.execute!
28
- output = ""
29
- i.run('ls').attach { |source,chunk| output += chunk }
30
- output.should_not include('lol')
31
- end
32
- end
33
-
34
- context 'when the object is not up to date' do
35
- let(:up_to_date) { false }
36
-
37
- it "does have the file lol" do
38
- i = subject.execute!
39
- output = ""
40
- i.run('ls').attach { |source,chunk| output += chunk }
41
- output.should include('lol')
42
- end
43
- end
44
- end
45
-
46
- describe "#run_build" do
47
- before do
48
- subject.stub(:push_to_s3)
49
- end
50
-
51
- context "when the build succeeds" do
52
- it "does have the file lol" do
53
- i = subject.run_build
54
- output = ""
55
- i.run('ls').attach { |source,chunk| output += chunk }
56
- output.should include('lol')
57
- end
58
- end
59
-
60
- context "when the build fails" do
61
- let!(:image) { subject.image }
62
- before do
63
- subject.image = double(:image).stub(:run) {
64
- stub(:container, { :wait => { 'StatusCode' => 1 } })
65
- }
66
- end
67
-
68
- after do
69
- subject.image = image
70
- end
71
-
72
- it "raises an error" do
73
- expect { subject.run_build }.to raise_error
74
- end
75
- end
76
- end
77
-
78
- describe '#pull_from_s3' do
79
- let(:file) { subject.pull_from_s3('hey') }
80
- let(:object) { double(:object) }
81
-
82
- before do
83
- subject.connection.stub(:get_object).and_return object
84
- object.stub(:body).and_return 'hey dad'
85
- end
86
-
87
- after do
88
- path = file.path
89
- file.close
90
- File.delete(path)
91
- end
92
-
93
- it 'returns a File with the data pulled' do
94
- file.read.should == 'hey dad'
95
- end
96
- end
97
-
98
- describe '#up_to_date?' do
99
- context 'when the object exists in s3' do
100
- before { subject.connection.stub(:head_object) }
101
-
102
- its(:up_to_date?) { should be_true }
103
- end
104
-
105
- context 'when the object does not exist in s3' do
106
- before do
107
- subject.connection.stub(:head_object)
108
- .and_raise(Excon::Errors::NotFound.new('help'))
109
- end
110
-
111
- its(:up_to_date?) { should be_false }
112
- end
113
- end
114
-
115
- describe '#hash_output' do
116
- let(:output) {
117
- "682aa2a07693cc27756eee9751db3903 /etc/vim/vimrc"
118
- }
119
-
120
- context "when hash command returns successfully" do
121
- before do
122
- subject.image = image
123
- end
124
-
125
- it 'returns the output of the hash_command in the container' do
126
- subject.hash_output.should == output
127
- end
128
- end
129
-
130
- context "when hash command returns failure" do
131
- before do
132
- subject.image = double(:image).stub(:run, {
133
- :wait => { 'StatusCode' => 1 }
134
- })
135
- end
136
-
137
- it 'raises an error' do
138
- expect { subject.hash_output }.to raise_error
139
- end
140
- end
141
- end
142
-
143
- describe '#copy_output_dir' do
144
- let(:container) { Docker::Container.create('Image' => 'base', 'Cmd' => %w[true]) }
145
- let(:file) { subject.copy_output_dir(container) }
146
- let(:hash) { 'this_really_unique_hash' }
147
- let(:path) { file.path }
148
-
149
- before do
150
- subject.stub(:hash_output).and_return(hash)
151
- subject.output_dir '/root/'; container.wait
152
- end
153
- after do
154
- file.close
155
- File.delete(path)
156
- end
157
-
158
- it 'returns a File of the specified directory from the Container' do
159
- expect(file.path).to include("#{hash}")
160
- file.should be_a File
161
- file.read.should include('root/.bashrc')
162
- end
163
- end
164
-
165
- describe '#s3_object' do
166
- before do
167
- subject.stub(:s3_object_prefix) { 'lol' }
168
- subject.stub(:hash_output) { 'lel' }
169
- end
170
-
171
- it 'returns the s3_prefix merged with the hash_output' do
172
- subject.s3_object(subject.hash_output).should == 'lollel'
173
- end
174
- end
175
- end