contained_mr 0.1.2 → 0.2.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.
@@ -0,0 +1,86 @@
1
+ # @see {ContainedMr::Runner}
2
+ class ContainedMr::Mock::Runner
3
+ # @return {Hash<String, Object>} the options passed to the constructor
4
+ attr_reader :_container_options
5
+ # @return {Hash<String, Object>} the time limit passed to the constructor
6
+ attr_reader :_time_limit
7
+ # @return {Hash<String, Object>} the output path passed to the constructor
8
+ attr_reader :_output_path
9
+
10
+ # @return {Boolean} true if {#perform} was called
11
+ def performed?
12
+ @performed
13
+ end
14
+
15
+ # @return {Boolean} true if {#destroy!} was called
16
+ def destroyed?
17
+ @destroyed
18
+ end
19
+
20
+ include ContainedMr::RunnerLogic
21
+
22
+ # @see {ContainedMr::Runner#initialize}
23
+ def initialize(container_options, time_limit, output_path)
24
+ @_container_options = container_options
25
+ @_time_limit = time_limit
26
+ @_output_path = output_path
27
+
28
+ @container_id = nil
29
+ @started_at = @ended_at = nil
30
+ @status_code = nil
31
+ @timed_out = nil
32
+ @stdout = @stderr = nil
33
+ @output = nil
34
+
35
+ @performed = false
36
+ @destroyed = false
37
+ end
38
+
39
+ # @see {ContainedMr::Runner#perform}
40
+ def perform
41
+ @performed = true
42
+ self
43
+ end
44
+
45
+ # @see {ContainedMr::Runner#destroy!}
46
+ def destroy!
47
+ @destroyed = true
48
+ self
49
+ end
50
+
51
+ # Sets the container execution data returned by the mock.
52
+ #
53
+ # @param {Hash<Symbol, Object>} attributes values describing the result of
54
+ # running the job
55
+ # @return {ContainedMr::Runner} self
56
+ def _mock_set(attributes)
57
+ @started_at = attributes[:started_at]
58
+ @ended_at = attributes[:ended_at]
59
+ @status_code = attributes[:status_code]
60
+ @timed_out = attributes[:timed_out]
61
+ @stdout = attributes[:stdout]
62
+ @stderr = attributes[:stderr]
63
+ @output = attributes[:output]
64
+ self
65
+ end
66
+
67
+ # Convenience method for looking up an ulimit in the container options.
68
+ #
69
+ # @param {String} name the ulimit's name, such as 'cpu' or 'rss'
70
+ # @return {Number} the ulimit's hard and soft value, or nil if the ulimit was
71
+ # not found
72
+ # @raise {RuntimeError} if the ulimit's hard and soft values don't match
73
+ def _ulimit(name)
74
+ return nil unless ulimits = @_container_options['Ulimits']
75
+
76
+ ulimits.each do |ulimit|
77
+ if ulimit['Name'] == name
78
+ if ulimit['Hard'] != ulimit['Soft']
79
+ raise RuntimeError, "Hard/soft ulimit mismatch for #{name}"
80
+ end
81
+ return ulimit['Hard']
82
+ end
83
+ end
84
+ nil
85
+ end
86
+ end
@@ -0,0 +1,62 @@
1
+ # @see {ContainedMr::Template}
2
+ class ContainedMr::Mock::Template
3
+ # @return {Hash<String, Object>} YAML-parsed mapreduced.yml
4
+ attr_reader :_definition
5
+
6
+ # @return {Hash<String, Symbol|String>} maps file names in the template .zip
7
+ # to their contents, and maps directory entries to the :directory symbol
8
+ attr_reader :_zip_contents
9
+
10
+ include ContainedMr::TemplateLogic
11
+
12
+ # @return {Boolean} true if {#destroy!} was called
13
+ def destroyed?
14
+ @destroyed
15
+ end
16
+
17
+ # @see {ContainedMr::Template#initialize}
18
+ def initialize(name_prefix, id, zip_io)
19
+ @name_prefix = name_prefix
20
+ @id = id
21
+ @image_id = 'mock-template-image-id'
22
+ @item_count = nil
23
+ @_definition = nil
24
+
25
+ @destroyed = false
26
+ @_zip_contents = {}
27
+
28
+ process_zip zip_io
29
+ end
30
+
31
+ # @see {ContainedMr::Template#destroy!}
32
+ def destroy!
33
+ @destroyed = true
34
+ self
35
+ end
36
+
37
+ # @see {ContainedMr::Template#new_job}
38
+ def job_class
39
+ ContainedMr::Mock::Job
40
+ end
41
+
42
+ # Reads the template .zip and parses the definition.
43
+ def process_zip(zip_io)
44
+ # TODO(pwnall): zip_io.read -> zip_io after rubyzip releases 1.1.8
45
+ Zip::File.open_buffer zip_io.read do |zip|
46
+ zip.each do |zip_entry|
47
+ file_name = zip_entry.name
48
+ if zip_entry.directory?
49
+ @_zip_contents[file_name] = :directory
50
+ elsif zip_entry.file?
51
+ if file_name == 'mapreduced.yml'
52
+ read_definition zip_entry.get_input_stream
53
+ next
54
+ end
55
+ @_zip_contents[file_name] = zip_entry.get_input_stream.read
56
+ end
57
+ end
58
+ end
59
+ @_zip_contents.freeze
60
+ end
61
+ private :process_zip
62
+ end
@@ -0,0 +1,3 @@
1
+ # Namespace for mock classes that facilitate writing tests against ContainedMr.
2
+ module ContainedMr::Mock
3
+ end
@@ -0,0 +1,24 @@
1
+ # Namespace and factory for templates.
2
+ module ContainedMr
3
+ # Sets up the template and builds its Docker base image.
4
+ #
5
+ # This method should be used instead of calling {ContainedMr::Template.new}
6
+ # directly. This way, tests can stub {ContainedMr.template_class} to have it
7
+ # return {ContainedMr::Mock::Template}.
8
+ #
9
+ # @param {String} name_prefix prepended to Docker objects, for identification
10
+ # purposes
11
+ # @param {String} id the template's unique identifier
12
+ # @param {String} zip_io IO implementation that produces the template .zip
13
+ def self.new_template(name_prefix, id, zip_io)
14
+ template_class.new name_prefix, id, zip_io
15
+ end
16
+
17
+ # The class instantiated by {ContainedMr.new_template}.
18
+ #
19
+ # @return {Class} by default {ContainedMr::Template}; tests should stub this
20
+ # method and have it return {ContainedMr::Mock::Template}
21
+ def self.template_class
22
+ ContainedMr::Template
23
+ end
24
+ end
@@ -5,15 +5,20 @@ require 'docker'
5
5
 
6
6
  # Handles running a single mapper or reducer.
7
7
  class ContainedMr::Runner
8
- attr_reader :container_id
9
- attr_reader :started_at, :ended_at, :status_code, :timed_out
10
- attr_reader :stdout, :stderr, :output
8
+ include ContainedMr::RunnerLogic
11
9
 
12
- # C
10
+ # Initialize a runner.
11
+ #
12
+ # @param {Hash<String, Object>} container_options docker container creation
13
+ # options, passed to {Docker::Container.create} without modification
14
+ # @param {Number} time_limit maximum number of seconds that the runner's
15
+ # container is allowed to execute before being terminated
16
+ # @param {String} output_path the location of the file inside the container
17
+ # whose output will be saved
13
18
  def initialize(container_options, time_limit, output_path)
14
- @container_options = container_options
15
- @time_limit = time_limit
16
- @output_path = output_path
19
+ @_container_options = container_options
20
+ @_time_limit = time_limit
21
+ @_output_path = output_path
17
22
 
18
23
  @container_id = nil
19
24
  @started_at = @ended_at = nil
@@ -23,8 +28,9 @@ class ContainedMr::Runner
23
28
  @output = nil
24
29
  end
25
30
 
26
-
27
31
  # Performs a full mapper / reducer step.
32
+ #
33
+ # @return {ContainedMr::Runner} self
28
34
  def perform
29
35
  container = create
30
36
  @container_id = container.id
@@ -32,20 +38,29 @@ class ContainedMr::Runner
32
38
  execute container
33
39
  fetch_console_output container
34
40
  fetch_file_output container
35
- destroy container
36
- self
41
+ destroy! container
37
42
  end
38
43
 
39
- # @return {Number} the container's running time, in seconds
40
- def ran_for
41
- @started_at && @ended_at && (@ended_at - @started_at)
44
+ # Removes the container used to run a mapper / reducer.
45
+ #
46
+ # @param {Docker::Container} container the mapper / reducer's container; if
47
+ # not supplied, an extra Docker API query is performed to obtain the
48
+ # container object
49
+ # @return {ContainedMr::Runner} self
50
+ def destroy!(container = nil)
51
+ unless @container_id.nil?
52
+ container ||= Docker::Container.get @container_id
53
+ container.delete force: true
54
+ @container_id = nil
55
+ end
56
+ self
42
57
  end
43
58
 
44
59
  # Creates a container for running a mapper or reducer.
45
60
  #
46
61
  # @return {Docker::Container} newly created container
47
62
  def create
48
- Docker::Container.create @container_options
63
+ Docker::Container.create @_container_options
49
64
  end
50
65
  private :create
51
66
 
@@ -56,7 +71,7 @@ class ContainedMr::Runner
56
71
  container.start
57
72
  @started_at = Time.now
58
73
  begin
59
- wait_status = container.wait @time_limit
74
+ wait_status = container.wait @_time_limit
60
75
  @status_code = wait_status['StatusCode']
61
76
  @timed_out = false
62
77
  rescue Docker::Error::TimeoutError
@@ -107,19 +122,11 @@ class ContainedMr::Runner
107
122
  # @return {IO} an IO implementation that sources the .tar data
108
123
  def fetch_tar_output(container)
109
124
  tar_buffer = StringIO.new
110
- container.copy @output_path do |data|
125
+ container.copy @_output_path do |data|
111
126
  tar_buffer << data
112
127
  end
113
128
  tar_buffer.rewind
114
129
  tar_buffer
115
130
  end
116
131
  private :fetch_tar_output
117
-
118
- # Removes the container used to run a mapper / reducer.
119
- #
120
- # @param {Docker::Container} container the mapper / reducer's container
121
- def destroy(container)
122
- container.delete
123
- @container_id = nil
124
- end
125
132
  end
@@ -0,0 +1,38 @@
1
+ # Logic shared by {ContainedMr::Runner} and {ContainedMr::Mock::Runner}.
2
+ module ContainedMr::RunnerLogic
3
+ # @return {Time} the time when the mapper or reducer starts running
4
+ attr_reader :started_at
5
+ # @return {Time} the time when the mapper or reducer stops running or is killed
6
+ attr_reader :ended_at
7
+ # @return {Number} the time
8
+ attr_reader :status_code
9
+ # @return {Boolean} true if the mapper or reducer was terminated due to
10
+ # running for too long
11
+ attr_reader :timed_out
12
+
13
+ # @return {String} the data written by the mapper or reducer to stdout
14
+ attr_reader :stdout
15
+ # @return {String} the data written by the mapper or reducer to stderr
16
+ attr_reader :stderr
17
+ # @return {String} the contents of the file
18
+ attr_reader :output
19
+
20
+ # @return {String} the unique ID of the Docker container used to run the
21
+ # mapper / reducer; this is nil
22
+ attr_reader :container_id
23
+
24
+ # @return {Number} the container's running time, in seconds
25
+ def ran_for
26
+ started_at && ended_at && (ended_at - started_at)
27
+ end
28
+
29
+ # The information written to the mapper status files given to the reducer.
30
+ #
31
+ # This is saved in files named 1.json, 2.json, ... provided to the reducer.
32
+ #
33
+ # @return {Hash<Symbol, Object>} JSON-compatible representation of the
34
+ # runner's information
35
+ def json_file
36
+ { ran_for: ran_for, exit_code: status_code, timed_out: timed_out }
37
+ end
38
+ end
@@ -7,20 +7,15 @@ require 'zip'
7
7
 
8
8
  # A template is used to spawn multiple Map-Reduce jobs.
9
9
  class ContainedMr::Template
10
- attr_reader :name_prefix, :item_count, :image_id
10
+ include ContainedMr::TemplateLogic
11
11
 
12
- # Sets up the template and builds its Docker base image.
13
- #
14
- # @param {String} name_prefix prepended to Docker objects, for identification
15
- # purposes
16
- # @param {String} id the job's unique identifier
17
- # @param {String} zip_io IO implementation that produces the template .zip
12
+ # @see {ContainedMr.new_template}
18
13
  def initialize(name_prefix, id, zip_io)
19
14
  @name_prefix = name_prefix
20
15
  @id = id
21
16
  @image_id = nil
22
- @definition = nil
23
17
  @item_count = nil
18
+ @_definition = nil
24
19
 
25
20
  tar_buffer = StringIO.new
26
21
  process_zip zip_io, tar_buffer
@@ -31,6 +26,8 @@ class ContainedMr::Template
31
26
  # Tears down the template's state.
32
27
  #
33
28
  # This removes the template's base Docker image.
29
+ #
30
+ # @return {ContainedMr::Template} self
34
31
  def destroy!
35
32
  unless @image_id.nil?
36
33
  # HACK(pwnall): Trick docker-api into issuing a DELETE request by tag.
@@ -38,63 +35,17 @@ class ContainedMr::Template
38
35
  image.remove
39
36
  @image_id = nil
40
37
  end
38
+ self
41
39
  end
42
40
 
43
- # Computes the Dockerfile used to build a job's mapper image.
44
- #
45
- # @return {String} the Dockerfile
46
- def mapper_dockerfile
47
- job_dockerfile @definition['mapper'] || {}, 'input'
48
- end
49
-
50
- # Computes the Dockerfile used to build a job's reducer image.
51
- #
52
- # @return {String} the Dockerfile
53
- def reducer_dockerfile
54
- job_dockerfile @definition['reducer'] || {}, '.'
55
- end
56
-
57
- # @return {String} tag applied to the template's base Docker image
58
- def image_tag
59
- "#{@name_prefix}/base.#{@id}"
60
- end
61
-
62
- # Computes the environment variables to be set in a mapper container.
41
+ # The class instantiated by {ContainedMr::TemplateLogic#new_job}.
63
42
  #
64
- # @param {Number} i the mapper number
65
- # @return {Array<String>} environment variables to be set in the mapper
66
- def mapper_env(i)
67
- [ "ITEM=#{i}", "ITEMS=#{@item_count.to_s}" ]
43
+ # @return {Class} by default {ContainedMr::Job}; tests might want to stub
44
+ # this method and have it return {ContainedMr::Mock::Job}
45
+ def job_class
46
+ ContainedMr::Job
68
47
  end
69
48
 
70
- # Computes the environment variables to be set in the reducer container.
71
- #
72
- # @return {Array<String>} environment variables to be set in the mapper
73
- def reducer_env
74
- [ "ITEMS=#{@item_count.to_s}" ]
75
- end
76
-
77
- # @return {String} the map output's path in the mapper Docker container
78
- def mapper_output_path
79
- (@definition['mapper'] || {})['output'] || '/output'
80
- end
81
-
82
- # @return {String} the reducer output's path in the reducer Docker container
83
- def reducer_output_path
84
- (@definition['reducer'] || {})['output'] || '/output'
85
- end
86
-
87
- # @private common code from mapper_dockerfile and reducer_dockerfile
88
- def job_dockerfile(job_definition, input_source)
89
- <<DOCKER_END
90
- FROM #{@image_id}
91
- COPY #{input_source} #{job_definition['input'] || '/input'}
92
- WORKDIR #{job_definition['chdir'] || '/'}
93
- ENTRYPOINT #{JSON.dump(job_definition['cmd'] || ['/bin/sh'])}
94
- DOCKER_END
95
- end
96
- private :job_dockerfile
97
-
98
49
  # Reads the template .zip and parses the definition.
99
50
  #
100
51
  # @param {IO} zip_io IO implementation that produces the .zip file
@@ -120,17 +71,7 @@ DOCKER_END
120
71
  end
121
72
  end
122
73
  end
123
-
124
- # Reads the template's definition, using data at the given path.
125
- #
126
- # @param {IO} yaml_io IO implementation that produces the .yaml file
127
- # containing the definition
128
- def read_definition(yaml_io)
129
- @definition = YAML.load yaml_io.read
130
-
131
- @item_count = @definition['items'] || 1
132
- end
133
- private :read_definition
74
+ private :process_zip
134
75
 
135
76
  # Builds the template's Docker image, using data at the given path.
136
77
  #
@@ -0,0 +1,91 @@
1
+ # Logic shared by {ContainedMr::Template} and {ContainedMr::Mock::Template}.
2
+ module ContainedMr::TemplateLogic
3
+ # @return {String} prepended to Docker objects, for identification purposes
4
+ attr_reader :name_prefix
5
+
6
+ # @return {String} the template's unique identifier
7
+ attr_reader :id
8
+
9
+ # @return {Number} the number of mapper jobs specified by this template
10
+ attr_reader :item_count
11
+
12
+ # @return {String} image_id the unique ID of the Docker image used as a base
13
+ # for images built by jobs derived from this template
14
+ attr_reader :image_id
15
+
16
+ # Creates a job using this template.
17
+ #
18
+ # @param {String} id the job's unique ID
19
+ # @param {Hash<String, Object>} json_options job options, extracted from JSON
20
+ # @return {ContainedMr::Job} a newly created job that uses this template
21
+ def new_job(id, json_options)
22
+ job_class.new self, id, json_options
23
+ end
24
+
25
+ # Computes the Dockerfile used to build a job's mapper image.
26
+ #
27
+ # @return {String} the Dockerfile
28
+ def mapper_dockerfile
29
+ job_dockerfile @_definition['mapper'] || {}, 'input'
30
+ end
31
+
32
+ # Computes the Dockerfile used to build a job's reducer image.
33
+ #
34
+ # @return {String} the Dockerfile
35
+ def reducer_dockerfile
36
+ job_dockerfile @_definition['reducer'] || {}, '.'
37
+ end
38
+
39
+ # @return {String} tag applied to the template's base Docker image
40
+ def image_tag
41
+ "#{@name_prefix}/base.#{@id}"
42
+ end
43
+
44
+ # Computes the environment variables to be set in a mapper container.
45
+ #
46
+ # @param {Number} i the mapper number
47
+ # @return {Array<String>} environment variables to be set in the mapper
48
+ def mapper_env(i)
49
+ [ "ITEM=#{i}", "ITEMS=#{@item_count.to_s}" ]
50
+ end
51
+
52
+ # Computes the environment variables to be set in the reducer container.
53
+ #
54
+ # @return {Array<String>} environment variables to be set in the mapper
55
+ def reducer_env
56
+ [ "ITEMS=#{@item_count.to_s}" ]
57
+ end
58
+
59
+ # @return {String} the map output's path in the mapper Docker container
60
+ def mapper_output_path
61
+ (@_definition['mapper'] || {})['output'] || '/output'
62
+ end
63
+
64
+ # @return {String} the reducer output's path in the reducer Docker container
65
+ def reducer_output_path
66
+ (@_definition['reducer'] || {})['output'] || '/output'
67
+ end
68
+
69
+ # @private common code from mapper_dockerfile and reducer_dockerfile
70
+ def job_dockerfile(job_definition, input_source)
71
+ <<DOCKER_END
72
+ FROM #{@image_id}
73
+ COPY #{input_source} #{job_definition['input'] || '/input'}
74
+ WORKDIR #{job_definition['chdir'] || '/'}
75
+ ENTRYPOINT #{JSON.dump(job_definition['cmd'] || ['/bin/sh'])}
76
+ DOCKER_END
77
+ end
78
+ private :job_dockerfile
79
+
80
+ # Reads the template's definition, using data at the given path.
81
+ #
82
+ # @param {IO} yaml_io IO implementation that produces the .yaml file
83
+ # containing the definition
84
+ def read_definition(yaml_io)
85
+ @_definition = YAML.load yaml_io.read
86
+ @_definition.freeze
87
+
88
+ @item_count = @_definition['items'] || 1
89
+ end
90
+ private :read_definition
91
+ end
data/lib/contained_mr.rb CHANGED
@@ -1,8 +1,15 @@
1
- # Namespace.
2
- module ContainedMr
3
- end
1
+ require_relative 'contained_mr/namespace.rb'
2
+
3
+ require_relative 'contained_mr/job_logic.rb'
4
+ require_relative 'contained_mr/runner_logic.rb'
5
+ require_relative 'contained_mr/template_logic.rb'
4
6
 
5
7
  require_relative 'contained_mr/cleaner.rb'
6
8
  require_relative 'contained_mr/job.rb'
7
9
  require_relative 'contained_mr/runner.rb'
8
10
  require_relative 'contained_mr/template.rb'
11
+
12
+ require_relative 'contained_mr/mock.rb'
13
+ require_relative 'contained_mr/mock/job.rb'
14
+ require_relative 'contained_mr/mock/runner.rb'
15
+ require_relative 'contained_mr/mock/template.rb'
@@ -0,0 +1,75 @@
1
+ module JobStateCases
2
+ def test_build_mapper_image_twice
3
+ @job.build_mapper_image File.read('testdata/input.hello')
4
+ begin
5
+ @job.build_mapper_image File.read('testdata/input.hello')
6
+ flunk 'No exception thrown'
7
+ rescue RuntimeError => e
8
+ assert_instance_of RuntimeError, e
9
+ assert_equal 'Mapper image already exists', e.message
10
+ end
11
+ end
12
+
13
+ def test_run_mapper_without_image
14
+ begin
15
+ @job.run_mapper 1
16
+ flunk 'No exception thrown'
17
+ rescue RuntimeError => e
18
+ assert_instance_of RuntimeError, e
19
+ assert_equal 'Mapper image does not exist', e.message
20
+ end
21
+ end
22
+
23
+ def test_run_invalid_mapper
24
+ begin
25
+ @job.run_mapper 4
26
+ flunk 'No exception thrown'
27
+ rescue ArgumentError => e
28
+ assert_instance_of ArgumentError, e
29
+ assert_equal 'Invalid mapper number 4', e.message
30
+ end
31
+ end
32
+
33
+ def test_invalid_mapper_runner
34
+ begin
35
+ @job.mapper_runner 4
36
+ flunk 'No exception thrown'
37
+ rescue ArgumentError => e
38
+ assert_instance_of ArgumentError, e
39
+ assert_equal 'Invalid mapper number 4', e.message
40
+ end
41
+ end
42
+
43
+ def test_build_reducer_image_without_mapper_results
44
+ begin
45
+ @job.build_reducer_image
46
+ flunk 'No exception thrown'
47
+ rescue RuntimeError => e
48
+ assert_instance_of RuntimeError, e
49
+ assert_equal 'Not all mappers ran', e.message
50
+ end
51
+ end
52
+
53
+ def test_build_reducer_image_twice
54
+ @job.build_mapper_image File.read('testdata/input.hello')
55
+ 1.upto(3) { |i| @job.run_mapper i }
56
+ @job.build_reducer_image
57
+ begin
58
+ @job.build_reducer_image
59
+ flunk 'No exception thrown'
60
+ rescue RuntimeError => e
61
+ assert_instance_of RuntimeError, e
62
+ assert_equal 'Reducer image already exists', e.message
63
+ end
64
+ end
65
+
66
+ def test_run_reducer_without_image
67
+ begin
68
+ @job.run_reducer
69
+ flunk 'No exception thrown'
70
+ rescue RuntimeError => e
71
+ assert_instance_of RuntimeError, e
72
+ assert_equal 'Reducer image does not exist', e.message
73
+ end
74
+ end
75
+ end
data/test/helper.rb CHANGED
@@ -24,6 +24,7 @@ rescue Bundler::BundlerError => e
24
24
  exit e.status_code
25
25
  end
26
26
  require 'minitest/autorun'
27
+ require 'mocha/mini_test'
27
28
 
28
29
  $LOAD_PATH.unshift(File.dirname(__FILE__))
29
30
  $LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
data/test/test_cleaner.rb CHANGED
@@ -2,10 +2,10 @@ require 'helper'
2
2
 
3
3
  class TestCleaner < MiniTest::Test
4
4
  def setup
5
- @template = ContainedMr::Template.new 'contained_mrtests', 'hello',
5
+ @template = ContainedMr.new_template 'contained_mrtests', 'hello',
6
6
  StringIO.new(File.binread('testdata/hello.zip'))
7
- @job = ContainedMr::Job.new @template, 'testjob',
8
- JSON.load(File.read('testdata/job.hello'))
7
+ @job = @template.new_job 'testjob',
8
+ JSON.load(File.read('testdata/job.hello'))
9
9
  @job.build_mapper_image File.read('testdata/input.hello')
10
10
 
11
11
  @cleaner = ContainedMr::Cleaner.new 'contained_mrtests'
@@ -22,10 +22,10 @@ class TestCleaner < MiniTest::Test
22
22
  end
23
23
 
24
24
  def test_destroy_all_with_duplicates
25
- template2 = ContainedMr::Template.new 'contained_mrtests', 'hello2',
25
+ template2 = ContainedMr.new_template 'contained_mrtests', 'hello2',
26
26
  StringIO.new(File.binread('testdata/hello.zip'))
27
- job2 = ContainedMr::Job.new template2, 'testjob2',
28
- JSON.load(File.read('testdata/job.hello'))
27
+ job2 = template2.new_job 'testjob2',
28
+ JSON.load(File.read('testdata/job.hello'))
29
29
  job2.build_mapper_image File.read('testdata/input.hello')
30
30
  @cleaner.destroy_all!
31
31
  assert_raises Docker::Error::NotFoundError do
@@ -0,0 +1,15 @@
1
+ require 'helper'
2
+
3
+ class TestContainedMr < MiniTest::Test
4
+ def test_template_class
5
+ assert_equal ContainedMr::Template, ContainedMr.template_class
6
+ end
7
+
8
+ def test_new_template
9
+ ContainedMr.stubs(:template_class).returns ContainedMr::Mock::Template
10
+
11
+ template = ContainedMr.new_template 'contained_mrtests', 'hello',
12
+ StringIO.new(File.binread('testdata/hello.zip'))
13
+ assert_instance_of ContainedMr::Mock::Template, template
14
+ end
15
+ end