mortar 0.15.26 → 0.15.27

Sign up to get free protection for your applications and to get access to all the features.
@@ -15,15 +15,17 @@
15
15
  #
16
16
 
17
17
  require "mortar/local/installutil"
18
+ require "mortar/local/params"
18
19
 
19
20
  class Mortar::Local::Python
20
21
  include Mortar::Local::InstallUtil
22
+ include Mortar::Local::Params
21
23
 
22
24
  PYTHON_OSX_TGZ_NAME = "mortar-python-osx.tgz"
23
25
  PYTHON_OSX_TGZ_DEFAULT_URL_PATH = "resource/python_osx"
24
26
  PYPI_URL_PATH = "resource/mortar_pypi"
25
27
 
26
- MORTAR_PYTHON_PACKAGES = ["luigi", "mortar-luigi"]
28
+ MORTAR_PYTHON_PACKAGES = ["luigi", "mortar-luigi", "stillson"]
27
29
 
28
30
  # Path to the python binary that should be used
29
31
  # for running UDFs
@@ -318,7 +320,19 @@ class Mortar::Local::Python
318
320
 
319
321
  def run_luigi_script(luigi_script, user_script_args)
320
322
  template_params = luigi_command_template_parameters(luigi_script, user_script_args)
321
- return run_templated_script(python_command_script_template_path, template_params)
323
+ run_templated_script(python_command_script_template_path, template_params)
324
+ end
325
+
326
+ def run_stillson_luigi_client_cfg_expansion(luigi_script, project_config_parameters)
327
+ # combine automatic mortar parameters with
328
+ # parameters provided in the project config
329
+ auto_params = automatic_parameters()
330
+ parameters = merge_parameters(auto_params, project_config_parameters)
331
+ stillson_template_params = {
332
+ :parameters => parameters,
333
+ :luigi_script => luigi_script.executable_path()
334
+ }
335
+ run_templated_script(stillson_command_script_template_path, stillson_template_params)
322
336
  end
323
337
 
324
338
  # Path to the template which generates the bash script for running python
@@ -326,6 +340,11 @@ class Mortar::Local::Python
326
340
  File.expand_path("../../templates/script/runpython.sh", __FILE__)
327
341
  end
328
342
 
343
+ # Path to the template which generates the bash script for running stillson
344
+ def stillson_command_script_template_path
345
+ File.expand_path("../../templates/script/runstillson.sh", __FILE__)
346
+ end
347
+
329
348
  def luigi_logging_config_file_path
330
349
  File.expand_path("../../conf/luigi/logging.ini", __FILE__)
331
350
  end
@@ -2,6 +2,7 @@ Gemfile.lock
2
2
  *.pyc
3
3
  *.class
4
4
  *.log
5
+ luigiscripts/client.cfg
5
6
  tmp
6
7
  .mortar-fork
7
8
  .mortar-local
@@ -0,0 +1,33 @@
1
+ #
2
+ # This is the configuration file your Luigi pipelines.
3
+ #
4
+ # When you run "mortar local:luigi" or "mortar luigi", Mortar will expand
5
+ # the variables in this file (e.g. MORTAR_EMAIL) to their actual values
6
+ # and store the result in "luigiscripts/client.cfg". You should not check
7
+ # "luigiscripts/client.cfg" into source control, as it will be generated
8
+ # on each new run.
9
+ #
10
+ # In this file, you can reference any internal Mortar configuration variables
11
+ # (e.g. MORTAR_EMAIL, AWS_ACCESS_KEY_ID), or any
12
+ # project configuration variables you've set with "mortar config:set".
13
+ #
14
+ # Read more at https://help.mortardata.com/technologies/luigi/luigi_configuration
15
+ #
16
+
17
+
18
+ #
19
+ # Mortar credentials to run jobs and query for
20
+ # state from the Mortar API.
21
+ #
22
+ [mortar]
23
+ email: ${MORTAR_EMAIL}
24
+ api_key: ${MORTAR_API_KEY}
25
+ host: api.mortardata.com
26
+
27
+ #
28
+ # AWS credentials to check for completed output
29
+ # and store job completion tokens in S3 from Luigi.
30
+ #
31
+ [s3]
32
+ aws_access_key_id: ${AWS_ACCESS_KEY_ID}
33
+ aws_secret_access_key: ${AWS_SECRET_ACCESS_KEY}
@@ -0,0 +1,134 @@
1
+ import luigi
2
+ from luigi import configuration
3
+ from luigi.s3 import S3Target, S3PathTask
4
+
5
+ from mortar.luigi import mortartask
6
+
7
+ """
8
+ Luigi is a powerful, easy-to-use framework for building data pipelines.
9
+
10
+ This is an example Luigi script to get you started. This script has a
11
+ 'fill in the blank' interaction. Feel free to alter it to build your pipeline.
12
+
13
+ In this example we will run a Pig script, and then shutdown any clusters associated
14
+ with that script. We will do that by running the ShutdownClusters Task,
15
+ which is dependent on RunMyExamplePigScript Task. This means the cluster will only
16
+ shutdown after RunMyExamplePigScript (where the data transformation happens)
17
+ Task is completed.
18
+
19
+ For full tutorials and in-depth Luigi documentation, visit:
20
+ https://help.mortardata.com/technologies/luigi
21
+
22
+ To Run:
23
+ mortar luigi luigiscripts/<%= project_name_alias %>_luigi.py \
24
+ --output-base-path "s3://mortar-example-output-data/<your_username_here>/<%= project_name_alias %>"
25
+ """
26
+
27
+ MORTAR_PROJECT = '<%= project_name_alias %>'
28
+
29
+ # helper function
30
+ def create_full_path(base_path, sub_path):
31
+ return '%s/%s' % (base_path, sub_path)
32
+
33
+ class RunMyExamplePigScript(mortartask.MortarProjectPigscriptTask):
34
+ """
35
+ This is a Luigi Task that extends MortarProjectPigscriptTask to run a Pig
36
+ script on Mortar.
37
+ """
38
+
39
+ """
40
+ The location in S3 where the output of the Mortar job will be written.
41
+ """
42
+ output_base_path = luigi.Parameter()
43
+
44
+ """
45
+ Default cluster size to use for running Mortar jobs. A cluster size of 0
46
+ will run in Mortar's local mode. This is a fast (and free!) way to run jobs
47
+ on small data samples. Cluster sizes >= 2 will run on a Hadoop cluster.
48
+ """
49
+ cluster_size = luigi.IntParameter(default=0)
50
+
51
+ """
52
+ Path to input data being analyzed using the Pig script.
53
+ """
54
+ input_path = luigi.Parameter(default ='s3://mortar-example-data/tutorial/excite.log.bz2')
55
+
56
+
57
+ def requires(self):
58
+ """
59
+ The requires method specifies a list of other Tasks that must be complete
60
+ for this Task to run. In this case, we want to require that our input
61
+ exists on S3 before we run the script. S3PathTask validates that the
62
+ specified file or directory exists on S3.
63
+ """
64
+ return [S3PathTask(self.input_path)]
65
+
66
+ def project(self):
67
+ """
68
+ Name of Mortar Project to run.
69
+ """
70
+ return MORTAR_PROJECT
71
+
72
+ def script_output(self):
73
+ """
74
+ The script_output method is how you define where the output from this task
75
+ will be stored. Luigi will check this output location before starting any
76
+ tasks that depend on this task.
77
+ """
78
+ return[S3Target(self.output_base_path + '/pigoutput')]
79
+
80
+ def token_path(self):
81
+ """
82
+ Luigi manages dependencies between tasks by checking for the existence of
83
+ files. When one task finishes it writes out a 'token' file that will
84
+ trigger the next task in the dependency graph. This is the base path for
85
+ where those tokens will be written.
86
+ """
87
+ return self.output_base_path
88
+
89
+ def parameters(self):
90
+ """
91
+ Parameters for this pig job.
92
+ """
93
+ return {'INPUT_PATH': self.input_path,
94
+ 'OUTPUT_PATH': self.output_base_path + '/pigoutput'}
95
+
96
+ def script(self):
97
+ """
98
+ Name of Pig script to run.
99
+ """
100
+ return '<%= project_name_alias %>'
101
+
102
+
103
+ class ShutdownClusters(mortartask.MortarClusterShutdownTask):
104
+ """
105
+ When the pipeline is completed, this task shuts down all active clusters not
106
+ currently running jobs. As this task is only shutting down clusters and not
107
+ generating any output data, this S3 location is used to store a 'token' file
108
+ indicating when the task has been completed.
109
+ """
110
+ output_base_path = luigi.Parameter()
111
+
112
+ def requires(self):
113
+ """
114
+ The ShutdownClusters task is dependent on RunMyExamplePigScript because a
115
+ cluster should not shut down until all the tasks are completed. You can
116
+ think of this as saying 'shut down my cluster after running my task'.
117
+ """
118
+ return RunMyExamplePigScript(output_base_path = self.output_base_path)
119
+
120
+ def output(self):
121
+ """
122
+ This output statement is needed because ShutdownClusters has no actual
123
+ output. We write a token with the class name to S3 to know that this task
124
+ has completed and it does not need to be run again.
125
+ """
126
+ return [S3Target((create_full_path(self.output_base_path, 'ShutdownClusters')))]
127
+
128
+ if __name__ == "__main__":
129
+ """
130
+ The final task in your pipeline, which will in turn pull in any dependencies
131
+ need to be run should be called in the main method. In this case ShutdownClusters
132
+ is being called.
133
+ """
134
+ luigi.run(main_task_cls= ShutdownClusters)
@@ -7,6 +7,7 @@
7
7
  # for more information.
8
8
 
9
9
  lib
10
+ luigiscripts
10
11
  macros
11
12
  pigscripts
12
13
  udfs
@@ -0,0 +1,25 @@
1
+ #!/bin/bash
2
+
3
+ set -e
4
+
5
+ # Setup python environment
6
+ source <%= @local_install_dir %>/pythonenv/bin/activate
7
+
8
+ # template config file
9
+ export LUIGI_CONFIG_TEMPLATE_PATH=`pwd`/`dirname <%= @luigi_script %>`/client.cfg.template
10
+
11
+ # expanded config file
12
+ export LUIGI_CONFIG_PATH=`pwd`/`dirname <%= @luigi_script %>`/client.cfg
13
+
14
+ # Setup parameters in environment variables
15
+ <% @parameters.each do |p| %>
16
+ export <%= p['name'] %>="<%= p['value'] %>";
17
+ <% end %>
18
+
19
+ # Run stillson to expand the configuration template
20
+ if [ -f "$LUIGI_CONFIG_TEMPLATE_PATH" ]
21
+ then
22
+ stillson "$LUIGI_CONFIG_TEMPLATE_PATH" -o $LUIGI_CONFIG_PATH
23
+ else
24
+ echo "No luigi client configuration template found in expected location $LUIGI_CONFIG_TEMPLATE_PATH. Not expanding."
25
+ fi
@@ -16,5 +16,5 @@
16
16
 
17
17
  module Mortar
18
18
  # see http://semver.org/
19
- VERSION = "0.15.26"
19
+ VERSION = "0.15.27"
20
20
  end
@@ -182,5 +182,13 @@ module Mortar
182
182
  end
183
183
  end
184
184
 
185
+ it "does not prompt for username in local mode if user not logged in" do
186
+ @cli.user(true).should == 'notloggedin@user.org'
187
+ @cli.user_s3_safe(true).should == 'notloggedin-user-org'
188
+ end
189
+
190
+ it "does not prompt for password in local mode if user not logged in" do
191
+ @cli.password(true).should == 'notloggedin'
192
+ end
185
193
  end
186
194
  end
@@ -39,6 +39,7 @@ describe Mortar::Command::Generate do
39
39
  File.exists?("Test/macros/.gitkeep").should be_true
40
40
  File.exists?("Test/pigscripts/Test.pig").should be_true
41
41
  File.exists?("Test/udfs/python/Test.py").should be_true
42
+ File.exists?("Test/luigiscripts/README").should be_true
42
43
 
43
44
  File.read("Test/pigscripts/Test.pig").each_line { |line| line.match(/<%.*%>/).should be_nil }
44
45
  end
@@ -46,7 +46,7 @@ module Mortar::Command
46
46
  job_url = "http://127.0.0.1:5000/jobs/job_detail?job_id=c571a8c7f76a4fd4a67c103d753e2dd5"
47
47
  cluster_size = 5
48
48
 
49
- mock(Mortar::Auth.api).post_job_new_cluster("myproject", "my_script", is_a(String), cluster_size,
49
+ mock(Mortar::Auth.api).post_pig_job_new_cluster("myproject", "my_script", is_a(String), cluster_size,
50
50
  :pig_version => "0.9",
51
51
  :project_script_path => be_a_kind_of(String),
52
52
  :parameters => match_array([{"name" => "FIRST_PARAM", "value" => "FOO"}, {"name" => "SECOND_PARAM", "value" => "BAR"}]),
@@ -82,7 +82,7 @@ STDOUT
82
82
  job_url = "http://127.0.0.1:5000/jobs/job_detail?job_id=c571a8c7f76a4fd4a67c103d753e2dd5"
83
83
  cluster_size = 5
84
84
 
85
- mock(Mortar::Auth.api).post_job_new_cluster("myproject", "my_script", is_a(String),cluster_size,
85
+ mock(Mortar::Auth.api).post_pig_job_new_cluster("myproject", "my_script", is_a(String),cluster_size,
86
86
  :pig_version => "0.9",
87
87
  :project_script_path => be_a_kind_of(String),
88
88
  :parameters => match_array([{"name" => "FIRST_PARAM", "value" => "FOO"}, {"name" => "SECOND_PARAM", "value" => "BAR"}]),
@@ -142,7 +142,7 @@ STDERR
142
142
  job_url = "http://127.0.0.1:5000/jobs/job_detail?job_id=c571a8c7f76a4fd4a67c103d753e2dd5"
143
143
  cluster_size = 5
144
144
 
145
- mock(Mortar::Auth.api).post_job_new_cluster("myproject", "my_script", is_a(String),cluster_size,
145
+ mock(Mortar::Auth.api).post_pig_job_new_cluster("myproject", "my_script", is_a(String),cluster_size,
146
146
  :pig_version => "0.9",
147
147
  :project_script_path => be_a_kind_of(String),
148
148
  :parameters => match_array([{"name" => "FIRST_PARAM", "value" => "FOO"}, {"name" => "SECOND_PARAM", "value" => "BAR"}]),
@@ -178,7 +178,7 @@ STDOUT
178
178
  job_url = "http://127.0.0.1:5000/jobs/job_detail?job_id=c571a8c7f76a4fd4a67c103d753e2dd5"
179
179
  cluster_size = 5
180
180
 
181
- mock(Mortar::Auth.api).post_job_new_cluster("myproject", "my_script", is_a(String),cluster_size,
181
+ mock(Mortar::Auth.api).post_pig_job_new_cluster("myproject", "my_script", is_a(String),cluster_size,
182
182
  :pig_version => "0.9",
183
183
  :project_script_path => be_a_kind_of(String),
184
184
  :parameters => match_array([{"name" => "FIRST_PARAM", "value" => "FOO"}, {"name" => "SECOND_PARAM", "value" => "BAR"}]),
@@ -214,7 +214,7 @@ STDOUT
214
214
  job_url = "http://127.0.0.1:5000/jobs/job_detail?job_id=c571a8c7f76a4fd4a67c103d753e2dd5"
215
215
  cluster_id = "e2790e7e8c7d48e39157238d58191346"
216
216
 
217
- mock(Mortar::Auth.api).post_job_existing_cluster("myproject", "my_script", is_a(String), cluster_id,
217
+ mock(Mortar::Auth.api).post_pig_job_existing_cluster("myproject", "my_script", is_a(String), cluster_id,
218
218
  :pig_version => "0.9",
219
219
  :project_script_path => be_a_kind_of(String),
220
220
  :parameters => [],
@@ -248,7 +248,7 @@ STDOUT
248
248
  job_url = "http://127.0.0.1:5000/jobs/job_detail?job_id=c571a8c7f76a4fd4a67c103d753e2dd5"
249
249
  cluster_id = "e2790e7e8c7d48e39157238d58191346"
250
250
 
251
- mock(Mortar::Auth.api).post_job_existing_cluster("myproject", "my_script", is_a(String), cluster_id,
251
+ mock(Mortar::Auth.api).post_pig_job_existing_cluster("myproject", "my_script", is_a(String), cluster_id,
252
252
  :pig_version => "0.9",
253
253
  :project_script_path => be_a_kind_of(String),
254
254
  :parameters => [],
@@ -282,7 +282,7 @@ STDOUT
282
282
  cluster_size = 2
283
283
 
284
284
  mock(Mortar::Auth.api).get_clusters() {Excon::Response.new(:body => {'clusters' => []})}
285
- mock(Mortar::Auth.api).post_job_new_cluster("myproject", "my_script", is_a(String), cluster_size,
285
+ mock(Mortar::Auth.api).post_pig_job_new_cluster("myproject", "my_script", is_a(String), cluster_size,
286
286
  :pig_version => "0.9",
287
287
  :project_script_path => be_a_kind_of(String),
288
288
  :parameters => [],
@@ -320,7 +320,7 @@ STDOUT
320
320
  cluster_size = 2
321
321
 
322
322
  mock(Mortar::Auth.api).get_clusters() {Excon::Response.new(:body => {'clusters' => []})}
323
- mock(Mortar::Auth.api).post_job_new_cluster("myproject", "my_script", is_a(String), cluster_size,
323
+ mock(Mortar::Auth.api).post_pig_job_new_cluster("myproject", "my_script", is_a(String), cluster_size,
324
324
  :pig_version => "0.9",
325
325
  :project_script_path => be_a_kind_of(String),
326
326
  :parameters => [],
@@ -358,7 +358,7 @@ STDOUT
358
358
  job_url = "http://127.0.0.1:5000/jobs/job_detail?job_id=c571a8c7f76a4fd4a67c103d753e2dd5"
359
359
  cluster_id = "e2790e7e8c7d48e39157238d58191346"
360
360
 
361
- mock(Mortar::Auth.api).post_job_existing_cluster("myproject", "my_script", is_a(String), cluster_id, :pig_version => "0.9", :project_script_path => be_a_kind_of(String), :parameters => [], :notify_on_job_finish => false, :is_control_script=>false) {Excon::Response.new(:body => {"job_id" => job_id, "web_job_url" => job_url})}
361
+ mock(Mortar::Auth.api).post_pig_job_existing_cluster("myproject", "my_script", is_a(String), cluster_id, :pig_version => "0.9", :project_script_path => be_a_kind_of(String), :parameters => [], :notify_on_job_finish => false, :is_control_script=>false) {Excon::Response.new(:body => {"job_id" => job_id, "web_job_url" => job_url})}
362
362
 
363
363
  write_file(File.join(p.pigscripts_path, "my_script.pig"))
364
364
  stderr, stdout = execute("jobs:run pigscripts/my_script.pig --clusterid e2790e7e8c7d48e39157238d58191346 -d", p, @git)
@@ -410,7 +410,7 @@ STDOUT
410
410
  'job_name' => "", 'start_timestamp' => ""} ], 'status_code' => huge_busy_cluster_status }
411
411
  ]})
412
412
  }
413
- mock(Mortar::Auth.api).post_job_existing_cluster("myproject", "my_script", is_a(String), large_cluster_id,
413
+ mock(Mortar::Auth.api).post_pig_job_existing_cluster("myproject", "my_script", is_a(String), large_cluster_id,
414
414
  :pig_version => "0.9",
415
415
  :project_script_path => be_a_kind_of(String),
416
416
  :parameters => [],
@@ -444,7 +444,7 @@ STDOUT
444
444
  job_id = "c571a8c7f76a4fd4a67c103d753e2dd5"
445
445
  cluster_size = 5
446
446
 
447
- mock(Mortar::Auth.api).post_job_new_cluster("myproject", "my_script", is_a(String), cluster_size,
447
+ mock(Mortar::Auth.api).post_pig_job_new_cluster("myproject", "my_script", is_a(String), cluster_size,
448
448
  :pig_version => "0.9",
449
449
  :project_script_path => be_a_kind_of(String),
450
450
  :parameters => match_array([{"name" => "FIRST", "value" => "FOO"}, {"name" => "SECOND", "value" => "BAR"}, {"name" => "THIRD", "value" => "BEAR"}]),
@@ -472,7 +472,7 @@ PARAMS
472
472
  job_id = "c571a8c7f76a4fd4a67c103d753e2dd5"
473
473
  cluster_size = 5
474
474
 
475
- mock(Mortar::Auth.api).post_job_new_cluster("myproject", "my_script", is_a(String), cluster_size,
475
+ mock(Mortar::Auth.api).post_pig_job_new_cluster("myproject", "my_script", is_a(String), cluster_size,
476
476
  :pig_version => "0.9",
477
477
  :project_script_path => be_a_kind_of(String),
478
478
  :parameters => match_array([{"name" => "FIRST", "value" => "FOO"}, {"name" => "SECOND", "value" => "BAR"}, {"name" => "THIRD", "value" => "BEAR"}]),
@@ -528,7 +528,7 @@ STDERR
528
528
 
529
529
  mock(@git).sync_embedded_project.with_any_args.times(1) { "somewhere_over_the_rainbow" }
530
530
 
531
- mock(Mortar::Auth.api).post_job_new_cluster("myproject", "my_script", is_a(String), cluster_size,
531
+ mock(Mortar::Auth.api).post_pig_job_new_cluster("myproject", "my_script", is_a(String), cluster_size,
532
532
  :pig_version => "0.9",
533
533
  :project_script_path => be_a_kind_of(String),
534
534
  :parameters => match_array([{"name" => "FIRST_PARAM", "value" => "FOO"}, {"name" => "SECOND_PARAM", "value" => "BAR"}]),
@@ -289,29 +289,67 @@ STDERR
289
289
  end
290
290
  end
291
291
 
292
- it "Runs script forwarding options to luigi script" do
292
+ it "Runs script with old pig-style parameters passed to luigi script" do
293
293
  with_git_initialized_project do |p|
294
+ ENV['MORTAR_LUIGI_GIT_REF'] = nil
294
295
  script_name = "some_luigi_script"
295
296
  script_path = File.join(p.luigiscripts_path, "#{script_name}.py")
296
297
  write_file(script_path)
297
298
  luigi_script = Mortar::Project::LuigiScript.new(script_name, script_path)
299
+ config_parameters = []
298
300
  mock(Mortar::Project::LuigiScript).new(script_name, script_path).returns(luigi_script)
301
+ any_instance_of(Mortar::Command::Local) do |u|
302
+ mock(u).config_parameters.returns(config_parameters)
303
+ end
299
304
  any_instance_of(Mortar::Local::Python) do |u|
300
305
  mock(u).run_luigi_script(luigi_script, %W{--myoption 2 --myotheroption 3})
306
+ mock(u).run_stillson_luigi_client_cfg_expansion(luigi_script, config_parameters).returns(true)
301
307
  end
302
308
  any_instance_of(Mortar::Local::Controller) do |u|
303
309
  mock(u).install_and_configure(is_a(Mortar::PigVersion::Pig09),'luigi')
310
+ mock(u).require_aws_keys()
304
311
  end
305
312
  any_instance_of(Mortar::Command::Local) do |u|
306
313
  mock(u).sync_code_with_cloud().returns("some-git-ref")
307
314
  end
308
315
  ENV['MORTAR_LUIGI_GIT_REF'].should be_nil
309
316
  stderr, stdout = execute("local:luigi some_luigi_script -p myoption=2 -p myotheroption=3", p)
310
- stderr.should == ""
311
- ENV['MORTAR_LUIGI_GIT_REF'].should == 'some-git-ref'
317
+ stderr.should == <<-STDERR
318
+ [DEPRECATION] Passing luigi parameters with -p is deprecated. Please pass them directly (e.g. --myparam myvalue)
319
+ STDERR
320
+ ENV['MORTAR_LUIGI_GIT_REF'].should == "some-git-ref"
312
321
  end
313
322
  end
314
323
 
324
+ it "Runs script with new luigi-style parameters passed to luigi script" do
325
+ with_git_initialized_project do |p|
326
+ ENV['MORTAR_LUIGI_GIT_REF'] = nil
327
+ script_name = "some_luigi_script"
328
+ script_path = File.join(p.luigiscripts_path, "#{script_name}.py")
329
+ write_file(script_path)
330
+ luigi_script = Mortar::Project::LuigiScript.new(script_name, script_path)
331
+ config_parameters = []
332
+ mock(Mortar::Project::LuigiScript).new(script_name, script_path).returns(luigi_script)
333
+ any_instance_of(Mortar::Command::Local) do |u|
334
+ mock(u).config_parameters.returns(config_parameters)
335
+ end
336
+ any_instance_of(Mortar::Local::Python) do |u|
337
+ mock(u).run_stillson_luigi_client_cfg_expansion(luigi_script, config_parameters).returns(true)
338
+ mock(u).run_luigi_script(luigi_script, %W{--myoption 2 --myotheroption 3})
339
+ end
340
+ any_instance_of(Mortar::Local::Controller) do |u|
341
+ mock(u).install_and_configure(is_a(Mortar::PigVersion::Pig09),'luigi')
342
+ mock(u).require_aws_keys()
343
+ end
344
+ any_instance_of(Mortar::Command::Local) do |u|
345
+ mock(u).sync_code_with_cloud().returns("some-git-ref")
346
+ end
347
+ ENV['MORTAR_LUIGI_GIT_REF'].should be_nil
348
+ stderr, stdout = execute("local:luigi some_luigi_script --myoption 2 --myotheroption 3", p)
349
+ stderr.should == ""
350
+ ENV['MORTAR_LUIGI_GIT_REF'].should == "some-git-ref"
351
+ end
352
+ end
315
353
 
316
354
  end
317
355