mortar 0.15.26 → 0.15.27

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.
@@ -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