mortar 0.7.1 → 0.7.2

Sign up to get free protection for your applications and to get access to all the features.
@@ -18,13 +18,14 @@ require "mortar/generators/project_generator"
18
18
  require "mortar/generators/udf_generator"
19
19
  require "mortar/generators/pigscript_generator"
20
20
  require "mortar/generators/macro_generator"
21
+ require "mortar/generators/controlscript_generator"
21
22
  require "mortar/command/base"
22
23
 
23
24
  # generate mortar code (project, pigscript, python_udf, macro)
24
25
  #
25
26
  class Mortar::Command::Generate < Mortar::Command::Base
26
27
 
27
- # generate:project [PROJECTNAME]
28
+ # generate:project PROJECTNAME
28
29
  #
29
30
  # Generate the files and directory structure necessary for a Mortar project.
30
31
  #
@@ -39,7 +40,7 @@ class Mortar::Command::Generate < Mortar::Command::Base
39
40
  end
40
41
  alias_command "generate:project", "generate:_project"
41
42
 
42
- # generate:python_udf [UDFNAME]
43
+ # generate:python_udf UDFNAME
43
44
  #
44
45
  # Generate a new python user defined function
45
46
  #
@@ -52,7 +53,24 @@ class Mortar::Command::Generate < Mortar::Command::Base
52
53
  udf_generator.generate_python_udf(udf_name, project, options)
53
54
  end
54
55
 
55
- # generate:pigscript [SCRIPTNAME]
56
+ # generate:controlscript SCRIPTNAME
57
+ #
58
+ # Generate new control script.
59
+ #
60
+ def controlscript
61
+ script_name = shift_argument
62
+ unless script_name
63
+ error("Usage: mortar generate:controlscript SCRIPTNAME\nMust specify SCRIPTNAME.")
64
+ end
65
+
66
+ # Validates the we're in a mortar project
67
+ project
68
+
69
+ script_generator = Mortar::Generators::ControlscriptGenerator.new
70
+ script_generator.generate_controlscript(script_name, options)
71
+ end
72
+
73
+ # generate:pigscript SCRIPTNAME
56
74
  #
57
75
  # Generate new pig script.
58
76
  #
@@ -69,7 +87,7 @@ class Mortar::Command::Generate < Mortar::Command::Base
69
87
  script_generator.generate_pigscript(script_name, project, options)
70
88
  end
71
89
 
72
- # generate:macro [MACRONAME]
90
+ # generate:macro MACRONAME
73
91
  #
74
92
  # Generate a new pig macro.
75
93
  #
@@ -189,12 +189,13 @@ class Mortar::Command::Jobs < Mortar::Command::Base
189
189
  end
190
190
 
191
191
  if job_status["outputs"] && job_status["outputs"].length > 0
192
- job_display_entries["outputs"] = Hash[job_status["outputs"].select{|o| o["alias"]}.collect do |output|
192
+ job_display_entries["outputs"] = Hash.new { |h,k| h[k] = [] }
193
+ job_status["outputs"].select{|o| o["alias"]}.collect{ |output|
193
194
  output_hash = {}
194
195
  output_hash["location"] = output["location"] if output["location"]
195
196
  output_hash["records"] = output["records"] if output["records"]
196
197
  [output['alias'], output_hash]
197
- end]
198
+ }.each{ |k,v| job_display_entries["outputs"][k] << v }
198
199
  end
199
200
 
200
201
  if job_status["controlscript_name"]
@@ -32,7 +32,7 @@ class Mortar::Command::Local < Mortar::Command::Base
32
32
  ctrl.install_and_configure
33
33
  end
34
34
 
35
- # local:run PIGSCRIPT
35
+ # local:run SCRIPT
36
36
  #
37
37
  # Run a job on your local machine
38
38
  #
@@ -44,14 +44,14 @@ class Mortar::Command::Local < Mortar::Command::Base
44
44
  # Run the generate_regression_model_coefficients script locally.
45
45
  # $ mortar local:run generate_regression_model_coefficients
46
46
  def run
47
- pigscript_name = shift_argument
48
- unless pigscript_name
49
- error("Usage: mortar local:run PIGSCRIPT\nMust specify PIGSCRIPT.")
47
+ script_name = shift_argument
48
+ unless script_name
49
+ error("Usage: mortar local:run SCRIPT\nMust specify SCRIPT.")
50
50
  end
51
51
  validate_arguments!
52
- pigscript = validate_pigscript!(pigscript_name)
52
+ script = validate_script!(script_name)
53
53
  ctrl = Mortar::Local::Controller.new
54
- ctrl.run(pigscript, pig_parameters)
54
+ ctrl.run(script, pig_parameters)
55
55
  end
56
56
 
57
57
  # illustrate [PIGSCRIPT] [ALIAS]
@@ -71,7 +71,6 @@ class Mortar::Command::Projects < Mortar::Command::Base
71
71
  git.git("add .")
72
72
  git.git("commit -m \"Mortar project scaffolding\"")
73
73
  Mortar::Command::run("projects:register", [name])
74
- git.git("push mortar master")
75
74
  end
76
75
  alias_command "new", "projects:create"
77
76
 
@@ -137,6 +136,7 @@ class Mortar::Command::Projects < Mortar::Command::Base
137
136
  error("Project registration failed.\nError message: #{project_result['error_message']}")
138
137
  when Mortar::API::Projects::STATUS_ACTIVE
139
138
  git.remote_add("mortar", project_result['git_url'])
139
+ git.push_master
140
140
  display "Your project is ready for use. Type 'mortar help' to see the commands you can perform on the project.\n\n"
141
141
  else
142
142
  raise RuntimeError, "Unknown project status: #{project_status} for project_id: #{project_id}"
@@ -0,0 +1,37 @@
1
+ #
2
+ # Copyright 2012 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/generators/generator_base"
18
+ module Mortar
19
+ module Generators
20
+ class ControlscriptGenerator < Base
21
+
22
+ def generate_controlscript(script_name, options)
23
+ set_script_binding(script_name, options)
24
+
25
+ generate_file "controlscript.py", "controlscripts/#{script_name}.py", :recursive => true
26
+ end
27
+
28
+ protected
29
+
30
+ def set_script_binding(script_name, options)
31
+ options = options
32
+ script_name = script_name
33
+ @script_binding = binding
34
+ end
35
+ end
36
+ end
37
+ end
@@ -38,6 +38,12 @@ module Mortar
38
38
  inside "pigscripts" do
39
39
  generate_file "pigscript.pig", "#{project_name}.pig"
40
40
  end
41
+
42
+ mkdir "controlscripts"
43
+
44
+ inside "controlscripts" do
45
+ copy_file "gitkeep", ".gitkeep"
46
+ end
41
47
 
42
48
  mkdir "macros"
43
49
 
data/lib/mortar/git.rb CHANGED
@@ -81,12 +81,45 @@ module Mortar
81
81
  end
82
82
  output
83
83
  end
84
+
85
+ def push_master
86
+ unless has_commits?
87
+ raise GitError, "No commits found in repository. You must do an initial commit to initialize the repository."
88
+ end
89
+
90
+ safe_copy do
91
+ did_stash_changes = stash_working_dir("Stash for push to master")
92
+ git('push mortar master')
93
+ end
94
+
95
+ end
96
+
97
+ #
98
+ # Create a safe copy of the git directory
99
+ #
100
+
101
+ def safe_copy(&block)
102
+ # Copy code into a temp directory so we don't confuse editors while snapshotting
103
+ curdir = Dir.pwd
104
+ tmpdir = Dir.mktmpdir
105
+ FileUtils.cp_r(Dir.glob('*', File::FNM_DOTMATCH) - ['.', '..'], tmpdir)
106
+ Dir.chdir(tmpdir)
107
+
108
+ if block
109
+ yield
110
+ FileUtils.remove_entry_secure(tmpdir)
111
+ Dir.chdir(curdir)
112
+ else
113
+ return tmpdir
114
+ end
115
+ end
84
116
 
85
117
  #
86
118
  # snapshot
87
119
  #
88
120
 
89
121
  def create_snapshot_branch
122
+
90
123
  # TODO: handle Ctrl-C in the middle
91
124
  # TODO: can we do the equivalent of stash without changing the working directory
92
125
  unless has_commits?
@@ -95,9 +128,7 @@ module Mortar
95
128
 
96
129
  # Copy code into a temp directory so we don't confuse editors while snapshotting
97
130
  curdir = Dir.pwd
98
- tmpdir = Dir.mktmpdir
99
- FileUtils.cp_r(Dir.glob('*', File::FNM_DOTMATCH) - ['.', '..'], tmpdir)
100
- Dir.chdir(tmpdir)
131
+ tmpdir = safe_copy
101
132
 
102
133
  starting_branch = current_branch
103
134
  snapshot_branch = "mortar-snapshot-#{Mortar::UUID.create_random.to_s}"
@@ -422,10 +422,14 @@ module Mortar
422
422
  next
423
423
  else
424
424
  elements = value.sort {|x,y| x.to_s <=> y.to_s}
425
- display_with_indent(indent, "#{key}: ".ljust(max_key_length), false)
426
- display_with_indent(indent, elements[0])
427
- elements[1..-1].each do |element|
428
- display_with_indent(indent, "#{' ' * max_key_length}#{element}")
425
+ display_with_indent(indent, "#{key}: ".ljust(max_key_length))
426
+ elements[0..-1].each do |element|
427
+ case element
428
+ when Hash
429
+ styled_hash(element, nil, indent + 2)
430
+ else
431
+ display_with_indent(indent, "#{' ' * max_key_length}#{element}")
432
+ end
429
433
  end
430
434
  if elements.length > 1
431
435
  display
@@ -495,7 +499,7 @@ module Mortar
495
499
  end
496
500
 
497
501
  def ensure_dir_exists(dir)
498
- unless Dir.exists? dir
502
+ unless File.directory? dir
499
503
  Dir.mkdir(dir)
500
504
  end
501
505
  end
@@ -18,6 +18,7 @@ require "mortar/helpers"
18
18
  require "mortar/local/pig"
19
19
  require "mortar/local/java"
20
20
  require "mortar/local/python"
21
+ require "mortar/local/jython"
21
22
 
22
23
 
23
24
  class Mortar::Local::Controller
@@ -34,7 +35,7 @@ Linux systems please consult the documentation on your relevant package manager.
34
35
  EOF
35
36
 
36
37
  NO_PYTHON_ERROR_MESSAGE = <<EOF
37
- pA suitable python installation with virtualenv could not be located. Please ensure
38
+ A suitable python installation with virtualenv could not be located. Please ensure
38
39
  you have python 2.6+ installed on your local system. If you need to obtain a copy
39
40
  of virtualenv it can be located here:
40
41
  https://pypi.python.org/pypi/virtualenv
@@ -75,7 +76,7 @@ EOF
75
76
  end
76
77
 
77
78
  pig = Mortar::Local::Pig.new()
78
- pig.install()
79
+ pig.install_or_update()
79
80
 
80
81
  py = Mortar::Local::Python.new()
81
82
  unless py.check_or_install
@@ -87,6 +88,9 @@ EOF
87
88
  msg += "see #{py.pip_error_log_path} for more details"
88
89
  error(msg)
89
90
  end
91
+
92
+ jy = Mortar::Local::Jython.new()
93
+ jy.install_or_update()
90
94
  end
91
95
 
92
96
  # Main entry point for user running a pig script
@@ -16,6 +16,7 @@
16
16
 
17
17
  require 'zlib'
18
18
  require 'excon'
19
+ require 'time'
19
20
  require 'rbconfig'
20
21
  require 'rubygems/package'
21
22
 
@@ -33,6 +34,13 @@ module Mortar
33
34
  File.join(Dir.getwd, ".mortar-local")
34
35
  end
35
36
 
37
+ def jython_directory
38
+ local_install_directory + "/jython"
39
+ end
40
+
41
+ def jython_cache_directory
42
+ jython_directory + "/cachedir"
43
+ end
36
44
 
37
45
  # Drops a marker file for an installed package, used
38
46
  # to help determine if updates should be performed
@@ -89,6 +97,28 @@ module Mortar
89
97
  return os_platform_name.start_with?('darwin')
90
98
  end
91
99
 
100
+ def http_date_to_epoch(date_str)
101
+ return Time.httpdate(date_str).to_i
102
+ end
103
+
104
+ def url_date(url)
105
+ result = Excon.head(url)
106
+ http_date_to_epoch(result.get_header('Last-Modified'))
107
+ end
108
+
109
+ # Given a subdirectory where we have installed some software
110
+ # and a url to the tgz file it's sourced from, check if the
111
+ # remote version is newer than the installed version
112
+ def is_newer_version(subdir, url)
113
+ existing_install_date = install_date(subdir)
114
+ if not existing_install_date then
115
+ # There is no existing install
116
+ return true
117
+ end
118
+ remote_archive_date = url_date(url)
119
+ return existing_install_date < remote_archive_date
120
+ end
121
+
92
122
  end
93
123
  end
94
124
  end
@@ -0,0 +1,63 @@
1
+ #
2
+ # Copyright 2012 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/local/installutil"
18
+
19
+ class Mortar::Local::Jython
20
+ include Mortar::Local::InstallUtil
21
+
22
+ JYTHON_VERSION = '2.5.2'
23
+ JYTHON_JAR_NAME = 'jython_installer-' + JYTHON_VERSION + '.jar'
24
+ JYTHON_JAR_DIR = "http://s3.amazonaws.com/hawk-dev-software-mirror/jython/jython-2.5.2/"
25
+
26
+ def install_or_update
27
+ if should_install
28
+ action("Installing jython") do
29
+ install
30
+ end
31
+ elsif should_update
32
+ action("Updating jython") do
33
+ update
34
+ end
35
+ end
36
+ end
37
+
38
+ def should_install
39
+ not File.exists?(jython_directory)
40
+ end
41
+
42
+ def install
43
+ unless File.exists?(local_install_directory + '/' + JYTHON_JAR_NAME)
44
+ download_file(JYTHON_JAR_DIR + JYTHON_JAR_NAME, local_install_directory)
45
+ end
46
+
47
+ `$JAVA_HOME/bin/java -jar #{local_install_directory + '/' + JYTHON_JAR_NAME} -s -d #{jython_directory}`
48
+ FileUtils.mkdir_p jython_cache_directory
49
+ FileUtils.chmod_R 0777, jython_cache_directory
50
+
51
+ FileUtils.rm(local_install_directory + '/' + JYTHON_JAR_NAME)
52
+ note_install('jython')
53
+ end
54
+
55
+ def should_update
56
+ return is_newer_version('jython', JYTHON_JAR_DIR + JYTHON_JAR_NAME)
57
+ end
58
+
59
+ def update
60
+ FileUtils.rm_r(jython_directory)
61
+ install
62
+ end
63
+ end
@@ -23,7 +23,7 @@ class Mortar::Local::Pig
23
23
  include Mortar::Local::InstallUtil
24
24
 
25
25
  PIG_LOG_FORMAT = "humanreadable"
26
- PIG_TAR_DEFAULT_URL = "https://s3.amazonaws.com/mortar-public-artifacts/pig.tgz"
26
+ PIG_TAR_DEFAULT_URL = "https://s3.amazonaws.com/mhc-software-mirror/cli/pig.tgz"
27
27
 
28
28
  # Tempfile objects have a hook to delete the file when the object is
29
29
  # destroyed by the garbage collector. In practice this means that a
@@ -84,34 +84,48 @@ class Mortar::Local::Pig
84
84
  File.basename(pig_archive_url)
85
85
  end
86
86
 
87
- # Determines if a pig install needs to occur, true if no
88
- # pig install present or a newer version is available
87
+ # Determines if a pig install needs to occur, true if no pig install present
89
88
  def should_do_pig_install?
90
89
  not (File.exists?(pig_directory))
91
90
  end
92
91
 
93
- # Installs pig for this project if it is not already present
94
- def install
92
+ # Determines if a pig install needs to occur, true if server side
93
+ # pig tgz is newer than date of the existing install
94
+ def should_do_pig_update?
95
+ return is_newer_version('pig', pig_archive_url)
96
+ end
97
+
98
+ def install_or_update()
99
+ call_install = false
95
100
  if should_do_pig_install?
96
- FileUtils.mkdir_p(local_install_directory)
97
101
  action "Installing pig" do
98
- download_file(pig_archive_url, local_install_directory)
99
- local_tgz = File.join(local_install_directory, pig_archive_file)
100
- extract_tgz(local_tgz, local_install_directory)
101
-
102
- # This has been seening coming out of the tgz w/o +x so we do
103
- # here to be sure it has the necessary permissions
104
- FileUtils.chmod(0755, command)
105
-
106
- File.delete(local_tgz)
107
- note_install("pig")
102
+ install()
103
+ end
104
+ elsif should_do_pig_update?
105
+ action "Updating to latest pig" do
106
+ install()
108
107
  end
109
108
  end
110
109
  end
111
110
 
111
+ # Installs pig for this project if it is not already present
112
+ def install
113
+ FileUtils.mkdir_p(local_install_directory)
114
+ download_file(pig_archive_url, local_install_directory)
115
+ local_tgz = File.join(local_install_directory, pig_archive_file)
116
+ extract_tgz(local_tgz, local_install_directory)
117
+
118
+ # This has been seening coming out of the tgz w/o +x so we do
119
+ # here to be sure it has the necessary permissions
120
+ FileUtils.chmod(0755, command)
121
+
122
+ File.delete(local_tgz)
123
+ note_install("pig")
124
+ end
125
+
112
126
  # run the pig script with user supplied pig parameters
113
127
  def run_script(pig_script, pig_parameters)
114
- run_pig_command(" -f #{pig_script.path}", pig_parameters)
128
+ run_pig_command(" -f #{pig_script.path}", pig_parameters, true)
115
129
  end
116
130
 
117
131
  # Create a temp file to be used for writing the illustrate
@@ -146,8 +160,8 @@ class Mortar::Local::Pig
146
160
  ensure_dir_exists("illustrate-output/resources/js")
147
161
  ensure_dir_exists("illustrate-output/resources/flash")
148
162
 
149
- ["illustrate_css",
150
- "jquery", "jquery_transit", "jquery_stylestack",
163
+ ["illustrate_css",
164
+ "jquery", "jquery_transit", "jquery_stylestack",
151
165
  "mortar_table", "zeroclipboard", "zeroclipboard_swf"].each { |resource|
152
166
  copy_if_not_present_at_dest(@resource_locations[resource], @resource_destinations[resource])
153
167
  }
@@ -199,14 +213,17 @@ class Mortar::Local::Pig
199
213
  end
200
214
  cmd += " #{pig_alias} '"
201
215
 
202
- run_pig_command(cmd, [])
203
- show_illustrate_output(illustrate_outpath)
216
+ result = run_pig_command(cmd, [], false)
217
+ if result
218
+ show_illustrate_output(illustrate_outpath)
219
+ end
204
220
  end
205
221
 
206
222
  # Run pig with the specified command ('command' is anything that
207
223
  # can be appended to the command line invocation of Pig that will
208
224
  # get it to do something interesting, such as '-f some-file.pig'
209
- def run_pig_command(cmd, parameters = nil)
225
+ def run_pig_command(cmd, parameters = nil, jython_output = true)
226
+ unset_hadoop_env_vars
210
227
  # Generate the script for running the command, then
211
228
  # write it to a temp script which will be exectued
212
229
  script_text = script_for_command(cmd, parameters)
@@ -216,12 +233,21 @@ class Mortar::Local::Pig
216
233
  FileUtils.chmod(0755, script.path)
217
234
  system(script.path)
218
235
  script.unlink
236
+ return (0 == $?.to_i)
237
+ end
238
+
239
+ # so Pig doesn't try to load the wrong hadoop jar/configuration
240
+ # this doesn't mess up the env vars in the terminal, just this process (ruby)
241
+ def unset_hadoop_env_vars
242
+ ENV['HADOOP_HOME'] = ''
243
+ ENV['HADOOP_CONF_DIR'] = ''
219
244
  end
220
245
 
221
246
  # Generates a bash script which sets up the necessary environment and
222
247
  # then runs the pig command
223
- def script_for_command(cmd, parameters)
248
+ def script_for_command(cmd, parameters, jython_output = true)
224
249
  template_params = pig_command_script_template_parameters(cmd, parameters)
250
+ template_params['pig_opts']['jython.output'] = jython_output
225
251
  erb = ERB.new(File.read(pig_command_script_template_path), 0, "%<>")
226
252
  erb.result(BindingClazz.new(template_params).get_binding)
227
253
  end
@@ -236,8 +262,8 @@ class Mortar::Local::Pig
236
262
  template_params = {}
237
263
  template_params['pig_params_file'] = make_pig_param_file(pig_parameters)
238
264
  template_params['pig_home'] = pig_directory
239
- template_params['pig_classpath'] = "#{pig_directory}/lib-pig/*"
240
- template_params['classpath'] = "#{pig_directory}/lib/*:#{pig_directory}/conf/jets3t.properties"
265
+ template_params['pig_classpath'] = "#{pig_directory}/lib-pig/*:#{jython_directory}/jython.jar"
266
+ template_params['classpath'] = "#{pig_directory}/lib/*:#{jython_directory}/jython.jar:#{pig_directory}/conf/jets3t.properties"
241
267
  template_params['project_home'] = File.expand_path("..", local_install_directory)
242
268
  template_params['local_install_dir'] = local_install_directory
243
269
  template_params['pig_sub_command'] = cmd
@@ -252,6 +278,10 @@ class Mortar::Local::Pig
252
278
  opts['fs.s3n.awsAccessKeyId'] = ENV['AWS_ACCESS_KEY']
253
279
  opts['fs.s3n.awsSecretAccessKey'] = ENV['AWS_SECRET_KEY']
254
280
  opts['pig.events.logformat'] = PIG_LOG_FORMAT
281
+ opts['python.verbose'] = 'error'
282
+ opts['jython.output'] = true
283
+ opts['python.home'] = jython_directory
284
+ opts['python.cachedir'] = jython_cache_directory
255
285
  return opts
256
286
  end
257
287
 
@@ -19,7 +19,7 @@ require "mortar/local/installutil"
19
19
  class Mortar::Local::Python
20
20
  include Mortar::Local::InstallUtil
21
21
 
22
- PYTHON_DEFAULT_TGZ_URL = "https://s3.amazonaws.com/mortar-public-artifacts/mortar-python-osx.tgz"
22
+ PYTHON_DEFAULT_TGZ_URL = "https://s3.amazonaws.com/mhc-software-mirror/cli/mortar-python-osx.tgz"
23
23
 
24
24
  # Path to the python binary that should be used
25
25
  # for running UDFs
@@ -31,33 +31,45 @@ class Mortar::Local::Python
31
31
  def check_or_install
32
32
  if osx?
33
33
  # We currently only install python for osx
34
- install_python_osx
34
+ install_or_update_osx
35
35
  else
36
36
  # Otherwise we check that the system supplied python will be sufficient
37
37
  check_system_python
38
38
  end
39
39
  end
40
40
 
41
+ def should_do_update?
42
+ return is_newer_version('python', python_archive_url)
43
+ end
44
+
41
45
  # Performs an installation of python specific to this project, this
42
46
  # install includes pip and virtualenv
43
- def install_python_osx
47
+ def install_or_update_osx
44
48
  @command = "#{local_install_directory}/python/bin/python"
45
49
  if should_do_python_install?
46
- FileUtils.mkdir_p(local_install_directory)
47
50
  action "Installing python" do
48
- download_file(python_archive_url, local_install_directory)
49
- extract_tgz(local_install_directory + "/" + python_archive_file, local_install_directory)
50
-
51
- # This has been seening coming out of the tgz w/o +x so we do
52
- # here to be sure it has the necessary permissions
53
- FileUtils.chmod(0755, @command)
54
- File.delete(local_install_directory + "/" + python_archive_file)
55
- note_install("python")
51
+ install_osx
52
+ end
53
+ elsif should_do_update?
54
+ action "Updating to latest python" do
55
+ install_osx
56
56
  end
57
57
  end
58
58
  true
59
59
  end
60
60
 
61
+ def install_osx
62
+ FileUtils.mkdir_p(local_install_directory)
63
+ download_file(python_archive_url, local_install_directory)
64
+ extract_tgz(local_install_directory + "/" + python_archive_file, local_install_directory)
65
+
66
+ # This has been seening coming out of the tgz w/o +x so we do
67
+ # here to be sure it has the necessary permissions
68
+ FileUtils.chmod(0755, @command)
69
+ File.delete(local_install_directory + "/" + python_archive_file)
70
+ note_install("python")
71
+ end
72
+
61
73
  # Determines if a python install needs to occur, true if no
62
74
  # python install present or a newer version is available
63
75
  def should_do_python_install?
@@ -0,0 +1,11 @@
1
+ def run_script():
2
+ import os
3
+ from org.apache.pig.scripting import Pig
4
+
5
+ # compile the pig code
6
+ P = Pig.compileFromFile("../pigscripts/#{script_name}.pig")
7
+ bound = P.bind()
8
+ bound.runSingle()
9
+
10
+ if __name__ == '__main__':
11
+ run_script()
@@ -6,7 +6,7 @@
6
6
  * Parameters - set default values here; you can override with -p on the command-line.
7
7
  */
8
8
 
9
- <%= '%default'%> INPUT_PATH 's3n://hawk-example-data/tutorial/excite.log.bz2'
9
+ <%= '%default'%> INPUT_PATH 's3n://mortar-example-data/tutorial/excite.log.bz2'
10
10
  <%= '%default'%> OUTPUT_PATH 's3n://my-output-bucket/$MORTAR_EMAIL_S3_ESCAPED/<%= script_name %>'
11
11
 
12
12
  <% if not options[:skip_udf] %>
@@ -6,7 +6,7 @@
6
6
  * Parameters - set default values here; you can override with -p on the command-line.
7
7
  */
8
8
 
9
- <%= '%default'%> INPUT_PATH 's3n://hawk-example-data/tutorial/excite.log.bz2'
9
+ <%= '%default'%> INPUT_PATH 's3n://mortar-example-data/tutorial/excite.log.bz2'
10
10
  <%= '%default'%> OUTPUT_PATH 's3n://my-output-bucket/$MORTAR_EMAIL_S3_ESCAPED/<%= project_name %>'
11
11
 
12
12
  /**
@@ -16,5 +16,5 @@
16
16
 
17
17
  module Mortar
18
18
  # see http://semver.org/
19
- VERSION = "0.7.1"
19
+ VERSION = "0.7.2"
20
20
  end
@@ -34,6 +34,7 @@ describe Mortar::Command::Generate do
34
34
  File.exists?("Test/macros").should be_true
35
35
  File.exists?("Test/fixtures").should be_true
36
36
  File.exists?("Test/pigscripts").should be_true
37
+ File.exists?("Test/controlscripts").should be_true
37
38
  File.exists?("Test/udfs").should be_true
38
39
  File.exists?("Test/README.md").should be_true
39
40
  File.exists?("Test/Gemfile").should be_false
@@ -99,6 +100,26 @@ STDERR
99
100
  end
100
101
  end
101
102
 
103
+ describe "generate:controlscript" do
104
+ it "Generate a new controlscript in a project" do
105
+ with_blank_project do |p|
106
+ stderr, stdout = execute("generate:controlscript Oink", p)
107
+ File.exists?(File.join(p.root_path, "controlscripts/Oink.py")).should be_true
108
+ File.read("controlscripts/Oink.py").each_line { |line| line.match(/<%.*%>/).should be_nil }
109
+ end
110
+ end
111
+
112
+ it "error when controlscript name isn't provided" do
113
+ with_blank_project do |p|
114
+ stderr, stdout = execute("generate:controlscript")
115
+ stderr.should == <<-STDERR
116
+ ! Usage: mortar generate:controlscript SCRIPTNAME
117
+ ! Must specify SCRIPTNAME.
118
+ STDERR
119
+ end
120
+ end
121
+ end
122
+
102
123
  describe "generate:python_udf" do
103
124
  it "Generate a new python udf in a project" do
104
125
  with_blank_project do |p|
@@ -337,7 +337,6 @@ STDERR
337
337
  end
338
338
 
339
339
  context("status") do
340
-
341
340
  it "gets status for a completed, successful job" do
342
341
  with_git_initialized_project do |p|
343
342
  job_id = "c571a8c7f76a4fd4a67c103d753e2dd5"
@@ -69,11 +69,16 @@ STDERR
69
69
  it "errors when the script doesn't exist" do
70
70
  with_git_initialized_project do |p|
71
71
  write_file(File.join(p.pigscripts_path, "my_other_script.pig"))
72
+ write_file(File.join(p.controlscripts_path, "my_control_script.py"))
72
73
  stderr, stdout = execute("local:run my_script", p)
73
74
  stderr.should == <<-STDERR
74
- ! Unable to find pigscript my_script
75
- ! Available scripts:
75
+ ! Unable to find a pigscript or controlscript for my_script
76
+ !
77
+ ! Available pigscripts:
76
78
  ! my_other_script
79
+ !
80
+ ! Available controlscripts:
81
+ ! my_control_script
77
82
  STDERR
78
83
  end
79
84
  end
@@ -132,6 +137,9 @@ STDERR
132
137
  any_instance_of(Mortar::Local::Python) do |j|
133
138
  mock(j).setup_project_python_environment.returns(true)
134
139
  end
140
+ any_instance_of(Mortar::Local::Jython) do |j|
141
+ mock(j).install_or_update.returns(true)
142
+ end
135
143
  stderr, stdout = execute("local:configure")
136
144
  stderr.should == ""
137
145
  end
@@ -104,7 +104,7 @@ STDOUT
104
104
  # if the git stuff doesn't work, the registration will fail, so we can pretend it does work here
105
105
  mock(@git).git("add .").returns(true)
106
106
  mock(@git).git("commit -m \"Mortar project scaffolding\"").returns(true)
107
- mock(@git).git("push mortar master").returns(true)
107
+ mock(@git).push_master
108
108
 
109
109
  stderr, stdout = execute("projects:create #{project_name}", nil, @git)
110
110
  Dir.pwd.end_with?("some_new_project").should be_true
@@ -127,6 +127,8 @@ STDOUT
127
127
  \e[1;32m create\e[0m .gitignore
128
128
  \e[1;32m create\e[0m pigscripts
129
129
  \e[1;32m create\e[0m pigscripts/some_new_project.pig
130
+ \e[1;32m create\e[0m controlscripts
131
+ \e[1;32m create\e[0m controlscripts/.gitkeep
130
132
  \e[1;32m create\e[0m macros
131
133
  \e[1;32m create\e[0m macros/.gitkeep
132
134
  \e[1;32m create\e[0m fixtures
@@ -215,6 +217,7 @@ STDERR
215
217
  mock(@git).has_dot_git?().returns(true)
216
218
  mock(@git).remotes.with_any_args.returns({})
217
219
  mock(@git).remote_add("mortar", project_git_url)
220
+ mock(@git).push_master
218
221
 
219
222
  stderr, stdout = execute("projects:register #{project_name} --polling_interval 0.05", nil, @git)
220
223
  stdout.should == <<-STDOUT
@@ -236,6 +239,7 @@ STDOUT
236
239
  mock(@git).has_dot_git?().returns(true)
237
240
  mock(@git).remotes.with_any_args.returns({})
238
241
  mock(@git).remote_add("mortar", project_git_url)
242
+ mock(@git).push_master
239
243
 
240
244
  stderr, stdout = execute("projects:register #{project_name} --polling_interval 0.05", nil, @git)
241
245
  stdout.should == <<-STDOUT
@@ -19,6 +19,7 @@
19
19
 
20
20
  require "spec_helper"
21
21
  require "mortar/helpers"
22
+ require 'fakefs/spec_helpers'
22
23
 
23
24
  module Mortar
24
25
  describe Helpers do
@@ -78,5 +79,29 @@ OUT
78
79
  infile.close()
79
80
  end
80
81
  end
82
+
83
+ context "ensure_dir_exists" do
84
+
85
+ it "does not have an existing directory" do
86
+ FakeFS do
87
+ dir_name = "./foo-bar-dir"
88
+ expect(File.directory?(dir_name)).to be_false
89
+ ensure_dir_exists(dir_name)
90
+ expect(File.directory?(dir_name)).to be_true
91
+ end
92
+ end
93
+
94
+ it "does have an existing directory" do
95
+ FakeFS do
96
+ dir_name = "./foo-bar-dir"
97
+ FileUtils.mkdir_p(dir_name)
98
+ expect(File.directory?(dir_name)).to be_true
99
+ ensure_dir_exists(dir_name)
100
+ expect(File.directory?(dir_name)).to be_true
101
+ end
102
+ end
103
+
104
+ end
105
+
81
106
  end
82
107
  end
@@ -49,6 +49,17 @@ module Mortar::Local
49
49
  end
50
50
  end
51
51
 
52
+ it "works with file created by note-install" do
53
+ install_file_path = @installutil.install_file_for("foo")
54
+ install_date = 1234568
55
+ stub(Time).now.returns(install_date)
56
+ FakeFS do
57
+ FileUtils.mkdir_p(File.dirname(install_file_path))
58
+ @installutil.note_install('foo')
59
+ expect(@installutil.install_date('foo')).to eq(install_date)
60
+ end
61
+ end
62
+
52
63
  end
53
64
 
54
65
  context("note-install") do
@@ -66,5 +77,50 @@ module Mortar::Local
66
77
 
67
78
  end
68
79
 
80
+ context "is_newer_version" do
81
+
82
+ it "is if remote file is newer" do
83
+ stub(@installutil).install_date.returns(1)
84
+ stub(@installutil).url_date.returns(2)
85
+ expect(@installutil.is_newer_version('foo', 'http://bar')).to be_true
86
+ end
87
+
88
+ it "is not if remote file is older" do
89
+ stub(@installutil).install_date.returns(2)
90
+ stub(@installutil).url_date.returns(1)
91
+ expect(@installutil.is_newer_version('foo', 'http://bar')).to be_false
92
+ end
93
+
94
+ it "if no version is present" do
95
+ install_file_path = @installutil.install_file_for("foo")
96
+ stub(@installutil).url_date.returns(1)
97
+ FakeFS do
98
+ FileUtils.rm_rf(File.dirname(install_file_path), :force => true)
99
+ expect(@installutil.is_newer_version('foo', 'http://bar')).to be_true
100
+ end
101
+ end
102
+
103
+ end
104
+
105
+ context "url_date" do
106
+
107
+ it "returns an epoch" do
108
+ excon_response = Excon::Response.new(:headers => {"Last-Modified" => "Mon, 11 Mar 2013 15:03:55 GMT"})
109
+ mock(Excon).head("http://foo/bar").returns(excon_response)
110
+ actual_epoch = @installutil.url_date("http://foo/bar")
111
+ expect(actual_epoch).to eq(1363014235)
112
+ end
113
+
114
+ end
115
+
116
+ context "parse_http_date" do
117
+
118
+ it "returns the appropriate epoch" do
119
+ epoch = @installutil.http_date_to_epoch("Mon, 11 Mar 2013 15:03:55 GMT")
120
+ expect(epoch).to eq(1363014235)
121
+ end
122
+
123
+ end
124
+
69
125
  end
70
126
  end
@@ -0,0 +1,68 @@
1
+ #
2
+ # Copyright 2012 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 'spec_helper'
18
+ require 'fakefs/spec_helpers'
19
+ require 'mortar/local/jython'
20
+
21
+
22
+
23
+ module Mortar::Local
24
+ describe Jython do
25
+
26
+ context("update") do
27
+
28
+ it "removes existing install and does install" do
29
+ install_dir = "/foo/bar/jython"
30
+ jython = Mortar::Local::Jython.new
31
+ mock(jython).jython_directory.returns(install_dir)
32
+ mock(jython).install
33
+ FakeFS do
34
+ FileUtils.mkdir_p(install_dir)
35
+ expect(File.directory?(install_dir)).to be_true
36
+ jython.update
37
+ expect(File.directory?(install_dir)).to be_false
38
+ end
39
+ end
40
+
41
+ end
42
+
43
+ context "should_install" do
44
+
45
+ it "is true if the directory does not exist" do
46
+ install_dir = "/foo/bar/jython"
47
+ jython = Mortar::Local::Jython.new
48
+ mock(jython).jython_directory.returns(install_dir)
49
+ FakeFS do
50
+ FileUtils.mkdir_p(install_dir)
51
+ expect(jython.should_install).to be_false
52
+ end
53
+ end
54
+
55
+ it "is false if the directory already exists" do
56
+ install_dir = "/foo/bar/jython"
57
+ jython = Mortar::Local::Jython.new
58
+ mock(jython).jython_directory.returns(install_dir)
59
+ FakeFS do
60
+ FileUtils.rm_rf(install_dir, :force => true)
61
+ expect(jython.should_install).to be_true
62
+ end
63
+ end
64
+
65
+ end
66
+
67
+ end
68
+ end
@@ -30,24 +30,12 @@ module Mortar::Local
30
30
 
31
31
  context("install") do
32
32
 
33
- it "does nothing if told not to" do
34
- pig = Mortar::Local::Pig.new
35
- mock(pig).should_do_pig_install?.returns(false)
36
- FakeFS do
37
- FileUtils.mkdir_p(File.dirname(pig.local_install_directory))
38
- FileUtils.rm_rf(pig.local_install_directory, :force => true)
39
- pig.install
40
- expect(File.exists?(pig.local_install_directory)).to be_false
41
- end
42
- end
43
-
44
33
  it "handles necessary installation steps" do
45
34
  # creates the parent directory, downloads the tgz, extracts it,
46
35
  # chmods bin/pig, removes tgz, and notes the installation
47
36
  FakeFS do
48
37
  pig = Mortar::Local::Pig.new
49
38
  local_pig_archive = File.join(pig.local_install_directory, pig.pig_archive_file)
50
- mock(pig).should_do_pig_install?.returns(true)
51
39
  mock(pig).download_file(pig.pig_archive_url, pig.local_install_directory) do
52
40
  # Simulate the tgz file being downloaded, this should be deleted
53
41
  # before the method finishes executing
@@ -67,6 +55,42 @@ module Mortar::Local
67
55
 
68
56
  end
69
57
 
58
+ context "install_or_update" do
59
+
60
+ it "does nothing if existing install and no update available" do
61
+ pig = Mortar::Local::Pig.new
62
+ mock(pig).should_do_pig_install?.returns(false)
63
+ mock(pig).should_do_pig_update?.returns(false)
64
+ FakeFS do
65
+ FileUtils.mkdir_p(File.dirname(pig.local_install_directory))
66
+ FileUtils.rm_rf(pig.local_install_directory, :force => true)
67
+ pig.install_or_update
68
+ expect(File.exists?(pig.local_install_directory)).to be_false
69
+ end
70
+ end
71
+
72
+ it "does install if none has been done before" do
73
+ pig = Mortar::Local::Pig.new
74
+ mock(pig).should_do_pig_install?.returns(true)
75
+ mock(pig).install
76
+ capture_stdout do
77
+ pig.install_or_update
78
+ end
79
+ end
80
+
81
+ it "does install if one was done before but there is an update" do
82
+ pig = Mortar::Local::Pig.new
83
+ mock(pig).should_do_pig_install?.returns(false)
84
+ mock(pig).should_do_pig_update?.returns(true)
85
+ mock(pig).install
86
+ capture_stdout do
87
+ pig.install_or_update
88
+ end
89
+ end
90
+
91
+ end
92
+
93
+
70
94
  context "show_illustrate_output" do
71
95
 
72
96
  it "takes a path to a json file, renders the html template, and opens it" do
@@ -153,5 +177,29 @@ module Mortar::Local
153
177
 
154
178
  end
155
179
 
180
+ context "illustrate_alias" do
181
+
182
+ it "displays html results if illustrate was successful" do
183
+ any_instance_of(Mortar::Project::PigScripts, :elements => nil, :path => "/foo/bar/baz.pig")
184
+ script = Mortar::Project::PigScripts.new('/foo/bar/baz.pig', 'baz', 'pig')
185
+ pig = Mortar::Local::Pig.new
186
+ mock(pig).run_pig_command.with_any_args.returns(true)
187
+ mock(pig).show_illustrate_output.with_any_args
188
+ stub(pig).make_pig_param_file.returns('param.file')
189
+ pig.illustrate_alias(script, 'my_alias', false, [])
190
+ end
191
+
192
+ it "skips results if illustrate was unsuccessful" do
193
+ any_instance_of(Mortar::Project::PigScripts, :elements => nil, :path => "/foo/bar/baz.pig")
194
+ script = Mortar::Project::PigScripts.new('/foo/bar/baz.pig', 'baz', 'pig')
195
+ pig = Mortar::Local::Pig.new
196
+ mock(pig).run_pig_command.with_any_args.returns(false)
197
+ mock(pig).show_illustrate_output.with_any_args.never
198
+ stub(pig).make_pig_param_file.returns('param.file')
199
+ pig.illustrate_alias(script, 'my_alias', false, [])
200
+ end
201
+
202
+ end
203
+
156
204
  end
157
205
  end
@@ -0,0 +1,68 @@
1
+ #
2
+ # Copyright 2012 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 'spec_helper'
18
+ require 'fakefs/spec_helpers'
19
+ require 'mortar/local/python'
20
+ require 'launchy'
21
+
22
+
23
+ module Mortar::Local
24
+ describe Python do
25
+
26
+ context("check_or_install") do
27
+
28
+ it "checks for python system requirements if not osx" do
29
+ python = Mortar::Local::Python.new
30
+ mock(python).osx?.returns(true)
31
+ mock(python).install_or_update_osx.returns(true)
32
+ python.check_or_install
33
+ end
34
+
35
+ it "installs python on osx" do
36
+ python = Mortar::Local::Python.new
37
+ mock(python).osx?.returns(false)
38
+ mock(python).check_system_python.returns(true)
39
+ python.check_or_install
40
+ end
41
+
42
+ end
43
+
44
+ context("install_or_update_osx") do
45
+
46
+ it "does install if none present" do
47
+ python = Mortar::Local::Python.new
48
+ mock(python).should_do_python_install?.returns(true)
49
+ mock(python).install_osx.returns(true)
50
+ capture_stdout do
51
+ python.install_or_update_osx
52
+ end
53
+ end
54
+
55
+ it "does install if an update is available" do
56
+ python = Mortar::Local::Python.new
57
+ mock(python).should_do_python_install?.returns(false)
58
+ mock(python).should_do_update?.returns(true)
59
+ mock(python).install_osx.returns(true)
60
+ capture_stdout do
61
+ python.install_or_update_osx
62
+ end
63
+ end
64
+
65
+ end
66
+
67
+ end
68
+ end
metadata CHANGED
@@ -1,13 +1,13 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: mortar
3
3
  version: !ruby/object:Gem::Version
4
- hash: 1
4
+ hash: 7
5
5
  prerelease:
6
6
  segments:
7
7
  - 0
8
8
  - 7
9
- - 1
10
- version: 0.7.1
9
+ - 2
10
+ version: 0.7.2
11
11
  platform: ruby
12
12
  authors:
13
13
  - Mortar Data
@@ -15,7 +15,7 @@ autorequire:
15
15
  bindir: bin
16
16
  cert_chain: []
17
17
 
18
- date: 2013-03-14 00:00:00 Z
18
+ date: 2013-03-26 00:00:00 Z
19
19
  dependencies:
20
20
  - !ruby/object:Gem::Dependency
21
21
  name: mortar-api-ruby
@@ -187,6 +187,7 @@ files:
187
187
  - lib/mortar/command/validate.rb
188
188
  - lib/mortar/command/version.rb
189
189
  - lib/mortar/errors.rb
190
+ - lib/mortar/generators/controlscript_generator.rb
190
191
  - lib/mortar/generators/generator_base.rb
191
192
  - lib/mortar/generators/macro_generator.rb
192
193
  - lib/mortar/generators/pigscript_generator.rb
@@ -197,13 +198,16 @@ files:
197
198
  - lib/mortar/local/controller.rb
198
199
  - lib/mortar/local/installutil.rb
199
200
  - lib/mortar/local/java.rb
201
+ - lib/mortar/local/jython.rb
200
202
  - lib/mortar/local/pig.rb
201
203
  - lib/mortar/local/python.rb
202
204
  - lib/mortar/project.rb
205
+ - lib/mortar/templates/controlscript/controlscript.py
203
206
  - lib/mortar/templates/macro/macro.pig
204
207
  - lib/mortar/templates/pigscript/pigscript.pig
205
208
  - lib/mortar/templates/pigscript/python_udf.py
206
209
  - lib/mortar/templates/project/README.md
210
+ - lib/mortar/templates/project/controlscripts/gitkeep
207
211
  - lib/mortar/templates/project/fixtures/gitkeep
208
212
  - lib/mortar/templates/project/gitignore
209
213
  - lib/mortar/templates/project/macros/gitkeep
@@ -235,7 +239,9 @@ files:
235
239
  - spec/mortar/local/controller_spec.rb
236
240
  - spec/mortar/local/installutil_spec.rb
237
241
  - spec/mortar/local/java_spec.rb
242
+ - spec/mortar/local/jython_spec.rb
238
243
  - spec/mortar/local/pig_spec.rb
244
+ - spec/mortar/local/python_spec.rb
239
245
  - spec/mortar/project_spec.rb
240
246
  - spec/mortar/updater_spec.rb
241
247
  - spec/spec.opts