mortar 0.6.2 → 0.7.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,184 @@
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::Python
20
+ include Mortar::Local::InstallUtil
21
+
22
+ PYTHON_DEFAULT_TGZ_URL = "https://s3.amazonaws.com/mortar-public-artifacts/mortar-python-osx.tgz"
23
+
24
+ # Path to the python binary that should be used
25
+ # for running UDFs
26
+ @command = nil
27
+
28
+
29
+ # Execute either an installation of python or an inspection
30
+ # of the local system to see if a usable python is available
31
+ def check_or_install
32
+ if osx?
33
+ # We currently only install python for osx
34
+ install_python_osx
35
+ else
36
+ # Otherwise we check that the system supplied python will be sufficient
37
+ check_system_python
38
+ end
39
+ end
40
+
41
+ # Performs an installation of python specific to this project, this
42
+ # install includes pip and virtualenv
43
+ def install_python_osx
44
+ @command = "#{local_install_directory}/python/bin/python"
45
+ if should_do_python_install?
46
+ FileUtils.mkdir_p(local_install_directory)
47
+ 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")
56
+ end
57
+ end
58
+ true
59
+ end
60
+
61
+ # Determines if a python install needs to occur, true if no
62
+ # python install present or a newer version is available
63
+ def should_do_python_install?
64
+ return (osx? and (not (File.exists?(python_directory))))
65
+ end
66
+
67
+
68
+ # Checks if there is a usable versionpython already installed
69
+ def check_system_python
70
+ py_cmd = path_to_local_python()
71
+ if not py_cmd
72
+ false
73
+ else
74
+ @command = py_cmd
75
+ true
76
+ end
77
+ end
78
+
79
+ # Checks if the specified python command has
80
+ # virtualenv installed
81
+ def check_virtualenv_installed(python)
82
+ `#{python} -m virtualenv --help`
83
+ if (0 != $?.to_i)
84
+ false
85
+ else
86
+ true
87
+ end
88
+ end
89
+
90
+ def path_to_local_python
91
+ # Check several python commands in decending level of desirability
92
+ [ "python#{desired_python_minor_version}", "python" ].each{ |cmd|
93
+ path_to_python = `which #{cmd}`.to_s.strip
94
+ if path_to_python != ''
95
+ # todo: check for a minimum version (in the case of 'python')
96
+ if check_virtualenv_installed(path_to_python)
97
+ return path_to_python
98
+ end
99
+ end
100
+ }
101
+ return nil
102
+ end
103
+
104
+
105
+ def desired_python_minor_version
106
+ return "2.7"
107
+ end
108
+
109
+ def pip_requirements_path
110
+ return ENV.fetch('PIP_REQ_FILE', File.join(Dir.getwd, "udfs", "python", "requirements.txt"))
111
+ end
112
+
113
+ def has_python_requirements
114
+ return File.exists?(pip_requirements_path)
115
+ end
116
+
117
+ def python_env_dir
118
+ return "#{local_install_directory}/pythonenv"
119
+ end
120
+
121
+ def python_directory
122
+ return "#{local_install_directory}/python"
123
+ end
124
+
125
+ def python_archive_url
126
+ return ENV.fetch('PYTHON_DISTRO_URL', PYTHON_DEFAULT_TGZ_URL)
127
+ end
128
+
129
+ def python_archive_file
130
+ File.basename(python_archive_url)
131
+ end
132
+
133
+ # Creates a virtualenv in a well known location and installs any packages
134
+ # necessary for the users python udf
135
+ def setup_project_python_environment
136
+ `#{@command} -m virtualenv #{python_env_dir}`
137
+ if 0 != $?.to_i
138
+ return false
139
+ end
140
+ if should_do_requirements_install
141
+ action "Installing python UDF dependencies" do
142
+ pip_output = `. #{python_env_dir}/bin/activate &&
143
+ #{python_env_dir}/bin/pip install --requirement #{pip_requirements_path}`
144
+ if 0 != $?.to_i
145
+ File.open(pip_error_log_path, 'w') { |f|
146
+ f.write(pip_output)
147
+ }
148
+ return false
149
+ end
150
+ note_install("pythonenv")
151
+ end
152
+ end
153
+ return true
154
+ end
155
+
156
+ def pip_error_log_path
157
+ return ENV.fetch('PIP_ERROR_LOG', "dependency_install.log")
158
+ end
159
+
160
+ # Whether or not we need to do a `pip install -r requirements.txt` because
161
+ # we've never done one before or the dependencies have changed
162
+ def should_do_requirements_install
163
+ if has_python_requirements
164
+ if not install_date('pythonenv')
165
+ # We've never done an install from requirements.txt before
166
+ return true
167
+ else
168
+ return (requirements_edit_date > install_date('pythonenv'))
169
+ end
170
+ else
171
+ return false
172
+ end
173
+ end
174
+
175
+ # Date of last change to the requirements file
176
+ def requirements_edit_date
177
+ if has_python_requirements
178
+ return File.mtime(pip_requirements_path).to_i
179
+ else
180
+ return nil
181
+ end
182
+ end
183
+
184
+ end
@@ -57,6 +57,19 @@ module Mortar
57
57
  ".pig")
58
58
  @pigscripts
59
59
  end
60
+
61
+ def controlscripts_path
62
+ File.join(@root_path, "controlscripts")
63
+ end
64
+
65
+ def controlscripts
66
+ @controlscripts ||= ControlScripts.new(
67
+ controlscripts_path,
68
+ "controlscripts",
69
+ ".py",
70
+ :optional => true)
71
+ @controlscripts
72
+ end
60
73
 
61
74
  def tmp_path
62
75
  path = File.join(@root_path, "tmp")
@@ -75,10 +88,11 @@ module Mortar
75
88
 
76
89
  include Enumerable
77
90
 
78
- def initialize(path, name, filename_extension)
91
+ def initialize(path, name, filename_extension, optional=false)
79
92
  @path = path
80
93
  @name = name
81
94
  @filename_extension = filename_extension
95
+ @optional = optional
82
96
  @elements = elements
83
97
  end
84
98
 
@@ -107,14 +121,15 @@ module Mortar
107
121
  end
108
122
 
109
123
  def elements
110
- unless File.directory? @path
111
- raise ProjectError, "Unable to find #{@name} directory in project"
124
+ if File.directory? @path
125
+ # get {script_name => full_path}
126
+ file_paths = Dir[File.join(@path, "**", "*#{@filename_extension}")]
127
+ file_paths_hsh = file_paths.collect{|element_path| [element_name(element_path), element(element_name(element_path), element_path)]}.flatten
128
+ return Hash[*file_paths_hsh]
129
+ else
130
+ raise ProjectError, "Unable to find #{@name} directory in project" if not @optional
112
131
  end
113
-
114
- # get {script_name => full_path}
115
- file_paths = Dir[File.join(@path, "**", "*#{@filename_extension}")]
116
- file_paths_hsh = file_paths.collect{|element_path| [element_name(element_path), element(element_name(element_path), element_path)]}.flatten
117
- Hash[*file_paths_hsh]
132
+ return Hash[]
118
133
  end
119
134
 
120
135
  def element(path)
@@ -124,7 +139,13 @@ module Mortar
124
139
 
125
140
  class PigScripts < ProjectEntity
126
141
  def element(name, path)
127
- Script.new(name, path)
142
+ PigScript.new(name, path)
143
+ end
144
+ end
145
+
146
+ class ControlScripts < ProjectEntity
147
+ def element(name, path)
148
+ ControlScript.new(name, path)
128
149
  end
129
150
  end
130
151
 
@@ -156,5 +177,10 @@ module Mortar
156
177
  end
157
178
  end
158
179
 
180
+ class ControlScript < Script
181
+ end
182
+ class PigScript < Script
183
+ end
184
+
159
185
  end
160
186
  end
@@ -2,4 +2,6 @@ Gemfile.lock
2
2
  *.pyc
3
3
  *.class
4
4
  *.log
5
- tmp
5
+ tmp
6
+ log
7
+ .mortar-local
@@ -0,0 +1,96 @@
1
+
2
+ <html>
3
+ <head>
4
+ <title>Illustrate Report</title>
5
+ <link rel="stylesheet" href="resources/css/illustrate-output.css" type="text/css" />
6
+ </head>
7
+
8
+ <body>
9
+
10
+ <% @tables.each_with_index do |table, table_index| %>
11
+
12
+ <h3 class="illustrate_alias">
13
+ <% if (table["op"] == "LOStore") %>
14
+ Store :
15
+ <% end %>
16
+ <%= table["alias"] %>
17
+ <% if (table["notices"]) %>
18
+ &nbsp;
19
+ <% end %>
20
+ </h3>
21
+
22
+ <table class="table table-bordered illustrate_table expandable">
23
+ <tbody>
24
+ <tr>
25
+ <% table["fields"].each do |field| %>
26
+ <th><%= field %></th>
27
+ <% end %>
28
+ </tr>
29
+
30
+ <% table["data"].each_with_index do |row, row_index| %>
31
+ <tr>
32
+ <% row.each_with_index do |value, item_index| %>
33
+ <% if (value) %>
34
+ <td>
35
+ <div class="mortar-table-expandable-cell-wrapper">
36
+ <div class="mortar-table-expandable-cell-container">
37
+ <div class="mortar-table-expandable-cell-preview">
38
+ <%# <%- $.mortar_data.truncate.truncate_center(value, 30) %>
39
+ <%= value[0..30] %>
40
+ </div>
41
+ <div class="mortar-table-expandable-cell-content" hidden="true">
42
+ <!-- TODO: get clipboard working -->
43
+ <!-- <div data-clipboard-target="copy-<%- table_index %>-<%- row_index %>-<%- item_index %>" class="clipboard" data->Copy to Clipboard</div> -->
44
+ <div id="copy-<%- table_index %>-<%- row_index %>-<%- item_index %>" class="illustrate_content_wrapper"><%= value %></div>
45
+ </div>
46
+ </div>
47
+ </div>
48
+ </td>
49
+ <% else %>
50
+ <td><div>&nbsp;</div></td>
51
+ <% end %>
52
+ <% end %>
53
+ </tr>
54
+ <% end %>
55
+ </tbody>
56
+ </table>
57
+
58
+ <% if (table["notices"]) %>
59
+ <% table["notices"].each do |notice| %>
60
+ <div class="alert">
61
+ <a class="close" data-dismiss="alert">&times;</a>
62
+ * <%= notice %>
63
+ </div>
64
+ <% end %>
65
+ <% end %>
66
+
67
+ <% end %>
68
+
69
+ <% if (@udf_output and (not @udf_output.empty?)) %>
70
+ <h3 class="illustrate_alias">UDF Output: </h3>
71
+ </br>
72
+ <pre id="illustrate_udf_output">
73
+ <%= @udf_output %>
74
+ </pre>
75
+ <% end %>
76
+
77
+ <script src="resources/js/jquery-1.7.1.min.js"></script>
78
+ <script src="resources/js/jquery.transit.js"></script>
79
+ <script src="resources/js/jquery.stylestack.js"></script>
80
+ <script src="resources/js/zero_clipboard.js"></script>
81
+ <script src="resources/js/mortar-table.js"></script>
82
+ <script>
83
+ $('.illustrate_table td').mortarTableExpandableCell();
84
+ // TODO: get this working
85
+ /*
86
+ $('.illustrate_table td .clipboard').each(function() {
87
+ $(this).data('clippy', new ZeroClipboard( $(this), {
88
+ moviePath: "resources/flash/zeroclipboard.swf"
89
+ }));
90
+ });
91
+ */
92
+ </script>
93
+
94
+ </body>
95
+
96
+ </html>
@@ -0,0 +1,23 @@
1
+ #!/bin/bash
2
+
3
+ set -e
4
+
5
+ export PIG_HOME=<%= @pig_home %>
6
+ export PIG_CLASSPATH=<%= @pig_classpath %>
7
+ export CLASSPATH=<%= @classpath %>
8
+ export PIG_MAIN_CLASS=com.mortardata.hawk.HawkMain
9
+ export PIG_OPTS="<% @pig_opts.each do |k,v| %>-D<%= k %>=<%= v %> <% end %>"
10
+
11
+ # UDF paths are relative to this direectory
12
+ cd <%= @project_home %>/pigscripts
13
+
14
+ # Setup python environment
15
+ source <%= @local_install_dir %>/pythonenv/bin/activate
16
+
17
+ # Run Pig
18
+ <%= @local_install_dir %>/pig/bin/pig -exectype local \
19
+ -log4jconf <%= @local_install_dir %>/pig/conf/log4j-cli-local-dev.properties \
20
+ -propertyFile <%= @local_install_dir %>/pig/conf/pig-hawk-global.properties \
21
+ -propertyFile <%= @local_install_dir %>/pig/conf/pig-cli-local-dev.properties \
22
+ -param_file <%= @pig_params_file %> \
23
+ <%= @pig_sub_command %>
@@ -16,5 +16,5 @@
16
16
 
17
17
  module Mortar
18
18
  # see http://semver.org/
19
- VERSION = "0.6.2"
19
+ VERSION = "0.7.0"
20
20
  end
@@ -152,5 +152,13 @@ module Mortar
152
152
 
153
153
  lambda { @cli.check }.should raise_error(Mortar::CLI::Errors::InvalidGithubUsername)
154
154
  end
155
+
156
+ it "encodes the user email as s3 safe" do
157
+ user_email = "myemail+dontspam@somedomain.com"
158
+ stub(@cli).user.returns(user_email)
159
+ @cli.user().should == user_email
160
+ @cli.user_s3_safe.should == 'myemail-dontspam-somedomain-com'
161
+ end
162
+
155
163
  end
156
164
  end
@@ -57,8 +57,21 @@ STDERR
57
57
  with_git_initialized_project do |p|
58
58
  stderr, stdout = execute("describe does_not_exist my_alias", p, @git)
59
59
  stderr.should == <<-STDERR
60
- ! Unable to find pigscript does_not_exist
60
+ ! Unable to find a pigscript or controlscript for does_not_exist
61
+ !
61
62
  ! No pigscripts found
63
+ !
64
+ ! No controlscripts found
65
+ STDERR
66
+ end
67
+ end
68
+
69
+ it "errors when requested with controlscript" do
70
+ with_git_initialized_project do |p|
71
+ write_file(File.join(p.controlscripts_path, "my_script.py"))
72
+ stderr, stdout = execute("describe my_script my_alias", p, @git)
73
+ stderr.should == <<-STDERR
74
+ ! Currently Mortar does not support describing control scripts
62
75
  STDERR
63
76
  end
64
77
  end
@@ -57,8 +57,21 @@ STDERR
57
57
  with_git_initialized_project do |p|
58
58
  stderr, stdout = execute("illustrate does_not_exist my_alias", p, @git)
59
59
  stderr.should == <<-STDERR
60
- ! Unable to find pigscript does_not_exist
60
+ ! Unable to find a pigscript or controlscript for does_not_exist
61
+ !
61
62
  ! No pigscripts found
63
+ !
64
+ ! No controlscripts found
65
+ STDERR
66
+ end
67
+ end
68
+
69
+ it "errors when requested with controlscript" do
70
+ with_git_initialized_project do |p|
71
+ write_file(File.join(p.controlscripts_path, "my_script.py"))
72
+ stderr, stdout = execute("illustrate my_script my_alias", p, @git)
73
+ stderr.should == <<-STDERR
74
+ ! Currently Mortar does not support illustrating control scripts
62
75
  STDERR
63
76
  end
64
77
  end