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.
data/lib/mortar/auth.rb CHANGED
@@ -72,16 +72,24 @@ class Mortar::Auth
72
72
  @credentials = ask_for_and_save_credentials
73
73
  end
74
74
 
75
- def user # :nodoc:
76
- get_credentials[0]
75
+ def user(local=false) # :nodoc:
76
+ if (local && !has_credentials)
77
+ "notloggedin@user.org"
78
+ else
79
+ get_credentials[0]
80
+ end
77
81
  end
78
82
 
79
- def password # :nodoc:
80
- get_credentials[1]
83
+ def password(local=false) # :nodoc:
84
+ if (local && !has_credentials)
85
+ "notloggedin"
86
+ else
87
+ get_credentials[1]
88
+ end
81
89
  end
82
90
 
83
91
  def user_s3_safe(local = false)
84
- user_email = (local && !has_credentials) ? "notloggedin@user.org" : user
92
+ user_email = user(local)
85
93
  return user_email.gsub(/[^0-9a-zA-Z]/i, '-')
86
94
  end
87
95
 
@@ -104,6 +104,26 @@ class Mortar::Command::Base
104
104
  param_list
105
105
  end
106
106
 
107
+ def luigi_parameters
108
+ parameters = []
109
+ invalid_arguments.each_slice(2) do |arg_pair|
110
+ name_with_dashes = arg_pair[0]
111
+ unless (name_with_dashes.start_with? "--") && (name_with_dashes.length > 2)
112
+ error("Luigi parameter #{name_with_dashes} must begin with --")
113
+ end
114
+
115
+ unless arg_pair.length == 2
116
+ error("No value provided for luigi parameter #{name_with_dashes}")
117
+ end
118
+
119
+ name = name_with_dashes[2..name_with_dashes.length-1]
120
+ value = arg_pair[1]
121
+ parameters << {"name" => name, "value" => value}
122
+ end
123
+
124
+ parameters
125
+ end
126
+
107
127
  def pig_parameters
108
128
  paramfile_params = {}
109
129
  if options[:param_file]
@@ -23,7 +23,7 @@ require "mortar/command/base"
23
23
  #
24
24
  class Mortar::Command::Help < Mortar::Command::Base
25
25
 
26
- PRIMARY_NAMESPACES = %w( auth clusters generate illustrate jobs pigscripts projects )
26
+ PRIMARY_NAMESPACES = %w( auth config clusters generate jobs local luigi projects )
27
27
 
28
28
  # help [COMMAND]
29
29
  #
@@ -164,7 +164,7 @@ class Mortar::Command::Jobs < Mortar::Command::Base
164
164
  cluster_type = CLUSTER_TYPE__PERMANENT
165
165
  end
166
166
  use_spot_instances = options[:spot] || false
167
- api.post_job_new_cluster(project_name, script_name, git_ref, cluster_size,
167
+ api.post_pig_job_new_cluster(project_name, script_name, git_ref, cluster_size,
168
168
  :pig_version => pig_version.version,
169
169
  :project_script_path => script.rel_path,
170
170
  :parameters => pig_parameters,
@@ -174,7 +174,7 @@ class Mortar::Command::Jobs < Mortar::Command::Base
174
174
  :use_spot_instances => use_spot_instances).body
175
175
  else
176
176
  cluster_id = options[:clusterid]
177
- api.post_job_existing_cluster(project_name, script_name, git_ref, cluster_id,
177
+ api.post_pig_job_existing_cluster(project_name, script_name, git_ref, cluster_id,
178
178
  :pig_version => pig_version.version,
179
179
  :project_script_path => script.rel_path,
180
180
  :parameters => pig_parameters,
@@ -225,12 +225,12 @@ class Mortar::Command::Local < Mortar::Command::Base
225
225
 
226
226
  # local:luigi SCRIPT
227
227
  #
228
- # Run a luigi workflow on your local machine in local scheduler mode.
228
+ # Run a luigi pipeline script on your local machine in local scheduler mode.
229
229
  # Any additional command line arguments will be passed directly to the luigi script.
230
230
  #
231
- # -p, --parameter NAME=VALUE # Set a pig parameter value in your script.
232
- # -f, --param-file PARAMFILE # Load pig parameter values from a file.
233
231
  # --project-root PROJECTDIR # The root directory of the project if not the CWD
232
+ # -p, --parameter NAME=VALUE # [deprecated] Instead, pass luigi parameters directly as options (see below)
233
+ # -f, --param-file PARAMFILE # [deprecated] Instead, pass luigi parameters directly as options (see below)
234
234
  #
235
235
  #Examples:
236
236
  #
@@ -241,8 +241,7 @@ class Mortar::Command::Local < Mortar::Command::Base
241
241
  unless script_name
242
242
  error("Usage: mortar local:luigi SCRIPT\nMust specify SCRIPT.")
243
243
  end
244
- validate_arguments!
245
-
244
+
246
245
  # cd into the project root
247
246
  project_root = options[:project_root] ||= Dir.getwd
248
247
  unless File.directory?(project_root)
@@ -257,10 +256,24 @@ class Mortar::Command::Local < Mortar::Command::Base
257
256
  git_ref = sync_code_with_cloud()
258
257
  ENV['MORTAR_LUIGI_GIT_REF'] = git_ref
259
258
 
259
+ # pick up standard luigi-style params provided by the user
260
+ luigi_cli_parameters = luigi_parameters()
261
+
262
+ # pick up old pig-style parameters (included for backwards compatibility)
263
+ pig_style_parameters = pig_parameters()
264
+ if pig_style_parameters.length > 0
265
+ warn "[DEPRECATION] Passing luigi parameters with -p is deprecated. Please pass them directly (e.g. --myparam myvalue)"
266
+ end
267
+
268
+ luigi_cli_parameters.concat(pig_style_parameters)
269
+ cli_parameters = \
270
+ luigi_cli_parameters.sort_by { |p| p['name'] }.map { |arg| ["--#{arg['name']}", "#{arg['value']}"] }.flatten
271
+
272
+ # get project configuration parameters
273
+ project_config_params = config_parameters()
274
+
260
275
  ctrl = Mortar::Local::Controller.new
261
- luigi_params = pig_parameters.sort_by { |p| p['name'] }
262
- luigi_params = luigi_params.map { |arg| ["--#{arg['name']}", "#{arg['value']}"] }.flatten
263
- ctrl.run_luigi(pig_version, script, luigi_params)
276
+ ctrl.run_luigi(pig_version, script, cli_parameters, project_config_params)
264
277
  end
265
278
 
266
279
  # local:sqoop_table dbtype database-name table s3-destination
@@ -0,0 +1,86 @@
1
+ #
2
+ # Copyright 2014 Mortar Data Inc.
3
+ #
4
+ # Licensed under the Apache License, Version 2.0 (the "License");
5
+ # you may not use this file except in compliance with the License.
6
+ # You may obtain a copy of the License at
7
+ #
8
+ # http://www.apache.org/licenses/LICENSE-2.0
9
+ #
10
+ # Unless required by applicable law or agreed to in writing, software
11
+ # distributed under the License is distributed on an "AS IS" BASIS,
12
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
+ # See the License for the specific language governing permissions and
14
+ # limitations under the License.
15
+ #
16
+
17
+ require "mortar/command/base"
18
+ require "time"
19
+
20
+ # run luigi pipeline jobs
21
+ #
22
+ class Mortar::Command::Luigi < Mortar::Command::Base
23
+
24
+ include Mortar::Git
25
+
26
+ # luigi SCRIPT
27
+ #
28
+ # Run a luigi pipeline.
29
+ #
30
+ # -P, --project PROJECTNAME # Use a project that is not checked out in the current directory. Runs code from project's master branch in GitHub rather than snapshotting local code.
31
+ # -B, --branch BRANCHNAME # Used with --project to specify a non-master branch
32
+ #
33
+ # Examples:
34
+ #
35
+ # Run the nightly_rollup luigiscript:
36
+ # $ mortar luigi luigiscripts/nightly_rollup.py
37
+ #
38
+ # Run the nightly_rollup luigiscript with two parameters:
39
+ # $ mortar luigi luigiscripts/nightly_rollup.py --data-date 2012-02-01 --my-param myval
40
+ #
41
+ def index
42
+ script_name = shift_argument
43
+ unless script_name
44
+ error("Usage: mortar luigi SCRIPT\nMust specify SCRIPT.")
45
+ end
46
+
47
+ if options[:project]
48
+ project_name = options[:project]
49
+ if File.extname(script_name) == ".py"
50
+ script_name = File.basename(script_name, ".*")
51
+ end
52
+ else
53
+ project_name = project.name
54
+ script = validate_luigiscript!(script_name)
55
+ script_name = script.name
56
+ end
57
+
58
+ parameters = luigi_parameters()
59
+
60
+ if options[:project]
61
+ if options[:branch]
62
+ git_ref = options[:branch]
63
+ else
64
+ git_ref = "master"
65
+ end
66
+ else
67
+ git_ref = sync_code_with_cloud()
68
+ end
69
+
70
+ # post job to API
71
+ response = action("Requesting job execution") do
72
+ api.post_luigi_job(project_name, script_name, git_ref,
73
+ :project_script_path => script.rel_path,
74
+ :parameters => parameters).body
75
+ end
76
+
77
+ display("job_id: #{response['job_id']}")
78
+ display
79
+ display("Job status can be viewed on the web at:\n\n #{response['web_job_url']}")
80
+ display
81
+
82
+ response['job_id']
83
+ end
84
+
85
+ alias_command "luigi:run", "luigi"
86
+ end
@@ -72,6 +72,8 @@ module Mortar
72
72
 
73
73
  inside "luigiscripts" do
74
74
  copy_file "README", "README"
75
+ generate_file "luigiscript.py", "#{project_name}_luigi.py"
76
+ generate_file "client.cfg.template", "client.cfg.template"
75
77
  end
76
78
 
77
79
  mkdir "lib"
data/lib/mortar/git.rb CHANGED
@@ -160,6 +160,19 @@ module Mortar
160
160
  manifest_pathlist
161
161
  end
162
162
 
163
+ def add_entry_to_mortar_project_manifest(path, entry)
164
+ contents = File.open(path, "r") do |manifest|
165
+ manifest.read.strip
166
+ end
167
+
168
+ if contents && (! contents.include? entry)
169
+ new_contents = "#{contents}\n#{entry}\n"
170
+ File.open(path, "w") do |manifest|
171
+ manifest.write new_contents
172
+ end
173
+ end
174
+ end
175
+
163
176
  def add_newline_to_file(path)
164
177
  File.open(path, "r+") do |manifest|
165
178
  contents = manifest.read()
@@ -189,12 +202,25 @@ module Mortar
189
202
  #
190
203
  def ensure_valid_mortar_project_manifest()
191
204
  if File.exists? project_manifest_name
205
+ ensure_luigiscripts_in_project_manifest()
192
206
  add_newline_to_file(project_manifest_name)
193
207
  else
194
208
  create_mortar_project_manifest('.')
195
209
  end
196
210
  end
197
211
 
212
+ #
213
+ # Ensure that the luigiscripts directory,
214
+ # which was added after some project manifests were
215
+ # created, is in the manifest (if luigiscripts exists).
216
+ #
217
+ def ensure_luigiscripts_in_project_manifest
218
+ luigiscripts_path = "luigiscripts"
219
+ if File.directory? luigiscripts_path
220
+ add_entry_to_mortar_project_manifest(project_manifest_name, luigiscripts_path)
221
+ end
222
+ end
223
+
198
224
  #
199
225
  # Create a project manifest file
200
226
  #
@@ -207,6 +233,10 @@ module Mortar
207
233
  if File.directory? "#{path}/lib"
208
234
  manifest.puts "lib"
209
235
  end
236
+
237
+ if File.directory? "#{path}/luigiscripts"
238
+ manifest.puts "luigiscripts"
239
+ end
210
240
  end
211
241
  end
212
242
 
@@ -230,10 +230,14 @@ README
230
230
  pig.launch_repl(pig_version, pig_parameters)
231
231
  end
232
232
 
233
- def run_luigi(pig_version, luigi_script, user_script_args)
233
+ def run_luigi(pig_version, luigi_script, luigi_script_parameters, project_config_parameters)
234
+ require_aws_keys
234
235
  install_and_configure(pig_version, 'luigi')
235
236
  py = Mortar::Local::Python.new()
236
- py.run_luigi_script(luigi_script, user_script_args)
237
+ unless py.run_stillson_luigi_client_cfg_expansion(luigi_script, project_config_parameters)
238
+ error("Unable to expand your configuration template [luigiscripts/client.cfg.template] to [luigiscripts/client.cfg]")
239
+ end
240
+ py.run_luigi_script(luigi_script, luigi_script_parameters)
237
241
  end
238
242
 
239
243
  def sqoop_export_table(pig_version, connstr, dbtable, s3dest, options)
@@ -198,7 +198,12 @@ module Mortar
198
198
 
199
199
  def url_date(url, command=nil)
200
200
  result = head_resource(url, command)
201
- http_date_to_epoch(result.get_header('Last-Modified'))
201
+ last_modified = result.get_header('Last-Modified')
202
+ if last_modified
203
+ http_date_to_epoch(last_modified)
204
+ else
205
+ nil
206
+ end
202
207
  end
203
208
 
204
209
  # Given a subdirectory where we have installed some software
@@ -211,6 +216,9 @@ module Mortar
211
216
  return true
212
217
  end
213
218
  remote_archive_date = url_date(url, command)
219
+ if not remote_archive_date
220
+ return false
221
+ end
214
222
  return existing_install_date < remote_archive_date
215
223
  end
216
224
 
@@ -0,0 +1,68 @@
1
+ #
2
+ # Copyright 2014 Mortar Data Inc.
3
+ #
4
+ # Licensed under the Apache License, Version 2.0 (the "License");
5
+ # you may not use this file except in compliance with the License.
6
+ # You may obtain a copy of the License at
7
+ #
8
+ # http://www.apache.org/licenses/LICENSE-2.0
9
+ #
10
+ # Unless required by applicable law or agreed to in writing, software
11
+ # distributed under the License is distributed on an "AS IS" BASIS,
12
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
+ # See the License for the specific language governing permissions and
14
+ # limitations under the License.
15
+ #
16
+
17
+ require 'set'
18
+ require 'mortar/auth'
19
+
20
+ module Mortar
21
+ module Local
22
+ module Params
23
+
24
+ # Job parameters that are supplied automatically from Mortar when
25
+ # running on the server side. We duplicate these here.
26
+ def automatic_parameters()
27
+ params = {}
28
+
29
+ params['MORTAR_EMAIL'] = Mortar::Auth.user(true)
30
+ params['MORTAR_API_KEY'] = Mortar::Auth.password(true)
31
+
32
+ if ENV['MORTAR_EMAIL_S3_ESCAPED']
33
+ params['MORTAR_EMAIL_S3_ESCAPED'] = ENV['MORTAR_EMAIL_S3_ESCAPED']
34
+ else
35
+ params['MORTAR_EMAIL_S3_ESCAPED'] = Mortar::Auth.user_s3_safe(true)
36
+ end
37
+
38
+ if ENV['MORTAR_PROJECT_ROOT']
39
+ params['MORTAR_PROJECT_ROOT'] = ENV['MORTAR_PROJECT_ROOT']
40
+ else
41
+ params['MORTAR_PROJECT_ROOT'] = project_root
42
+ ENV['MORTAR_PROJECT_ROOT'] = params['MORTAR_PROJECT_ROOT']
43
+ end
44
+
45
+ params['AWS_ACCESS_KEY'] = ENV['AWS_ACCESS_KEY']
46
+ params['AWS_ACCESS_KEY_ID'] = ENV['AWS_ACCESS_KEY']
47
+ params['aws_access_key_id'] = ENV['AWS_ACCESS_KEY']
48
+
49
+ params['AWS_SECRET_KEY'] = ENV['AWS_SECRET_KEY']
50
+ params['AWS_SECRET_ACCESS_KEY'] = ENV['AWS_SECRET_KEY']
51
+ params['aws_secret_access_key'] = ENV['AWS_SECRET_KEY']
52
+
53
+ param_list = params.map do |k,v|
54
+ {"name" => k, "value" => v}
55
+ end
56
+ end
57
+
58
+ # Merge two lists of parameters, removing dupes.
59
+ # Parameters in param_list_1 override those in param_list_2
60
+ def merge_parameters(param_list_0, param_list_1)
61
+ param_list_1_keys = Set.new(param_list_1.map{|item| item["name"]})
62
+ merged = param_list_1.clone
63
+ merged.concat(param_list_0.select{|item| (! param_list_1_keys.include? item["name"]) })
64
+ merged
65
+ end
66
+ end
67
+ end
68
+ end
@@ -18,9 +18,11 @@ require "erb"
18
18
  require 'tempfile'
19
19
  require "mortar/helpers"
20
20
  require "mortar/local/installutil"
21
+ require "mortar/local/params"
21
22
 
22
23
  class Mortar::Local::Pig
23
24
  include Mortar::Local::InstallUtil
25
+ include Mortar::Local::Params
24
26
 
25
27
  PIG_LOG_FORMAT = "humanreadable"
26
28
  LIB_TGZ_NAME = "lib-common.tar.gz"
@@ -399,39 +401,11 @@ class Mortar::Local::Pig
399
401
  return opts
400
402
  end
401
403
 
402
- # Pig Paramenters that are supplied directly from Mortar when
403
- # running on the server side. We duplicate these here.
404
- def automatic_pig_parameters
405
- params = {}
406
-
407
- if ENV['MORTAR_EMAIL_S3_ESCAPED']
408
- params['MORTAR_EMAIL_S3_ESCAPED'] = ENV['MORTAR_EMAIL_S3_ESCAPED']
409
- else
410
- params['MORTAR_EMAIL_S3_ESCAPED'] = Mortar::Auth.user_s3_safe(true)
411
- end
412
-
413
- if ENV['MORTAR_PROJECT_ROOT']
414
- params['MORTAR_PROJECT_ROOT'] = ENV['MORTAR_PROJECT_ROOT']
415
- else
416
- params['MORTAR_PROJECT_ROOT'] = project_root
417
- ENV['MORTAR_PROJECT_ROOT'] = params['MORTAR_PROJECT_ROOT']
418
- end
419
-
420
-
421
- # Coerce into the same format as pig parameters that were
422
- # passed in via the command line or a parameter file
423
- param_list = []
424
- params.each{ |k,v|
425
- param_list.push({"name" => k, "value" => v})
426
- }
427
- return param_list
428
- end
429
-
430
404
  # Given a set of user specified pig parameters, combine with the
431
405
  # automatic mortar parameters and write out to a tempfile, returning
432
406
  # it's path so it may be referenced later in the process
433
407
  def make_pig_param_file(pig_parameters)
434
- mortar_pig_params = automatic_pig_parameters
408
+ mortar_pig_params = automatic_parameters()
435
409
  all_parameters = mortar_pig_params.concat(pig_parameters)
436
410
  param_file = Tempfile.new("mortar-pig-parameters")
437
411
  all_parameters.each { |p|
@@ -447,4 +421,9 @@ class Mortar::Local::Pig
447
421
  param_file.path
448
422
  end
449
423
 
424
+ def automatic_pig_parameters
425
+ warn "[DEPRECATION] Please call automatic_parameters instead"
426
+ automatic_parameters
427
+ end
428
+
450
429
  end