mortar 0.15.4 → 0.15.5

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml ADDED
@@ -0,0 +1,15 @@
1
+ ---
2
+ !binary "U0hBMQ==":
3
+ metadata.gz: !binary |-
4
+ Zjc5OGFiMTk0NDQxMjg2ZWZhNjI5ZDA2MDU3YTIwNTY4ZDg4ZGU5NQ==
5
+ data.tar.gz: !binary |-
6
+ MzZiOGExYzYwZjE0ZjU3MmUyY2JmY2QwNmM0YmFkNjliMGY4N2I1Zg==
7
+ SHA512:
8
+ metadata.gz: !binary |-
9
+ NWQ2ZTI2NTY1MmFjZDllOTVlZGFkNDExYTk1ODYxMjdhNTgyZDhjNzk2MDlm
10
+ YjU1MmZiNDU2ZDc0MmI3Mjc1ZDQ0NzM1MWU0ZGI1NGE3NDI0NjMwNzlmNjUx
11
+ NGFhNzM0ODQ2ZTZlNDQ0ZDcxMzc4YzBlN2U3MTdjNTY5MWZmOGE=
12
+ data.tar.gz: !binary |-
13
+ MDliMjQzZGI2YjkyYTg3Y2M3M2ZmOTNjYzI3ZGY0OGZlYzMwMzA2NDdiODE5
14
+ ZDJkNGVlYTc2MTg2NDA2M2VkYzU5ZmQwZjM1MjhlODY4NjUxMDA3NzkzMzdl
15
+ OTAyYWI5ZTAyYzY1Y2I2NDI0NTI5Y2QzNWM3NjU2MTg2YTgwYWM=
@@ -65,10 +65,20 @@ class Mortar::Command::Base
65
65
  end
66
66
 
67
67
  @project = Mortar::Project::Project.new(project_name, project_dir, remote)
68
+
69
+ #Every time we get the project, we're going to check if its a forked version and
70
+ #if it is we'll check for new updates to the base project.
71
+ begin
72
+ if git.is_fork_repo_updated(git_organization)
73
+ warning("The repository this project was forked from has been updated. To get the latest changes commit all of your work and do:\n\n\tgit merge #{git.fork_base_remote_name}/master\n\nYou may have conflicts that will need to be resolved manually.\n\n")
74
+ end
75
+ rescue
76
+ #Do nothing. We'll repeat this call often enough that we don't care if it fails.
77
+ end
68
78
  end
69
79
  @project
70
80
  end
71
-
81
+
72
82
  def api
73
83
  Mortar::Auth.api
74
84
  end
@@ -229,7 +229,7 @@ class Mortar::Command::Projects < Mortar::Command::Base
229
229
  end
230
230
  is_public = options[:public]
231
231
  ask_public(is_public)
232
- git.clone(git_url, name, "base")
232
+ git.clone(git_url, name, git.fork_base_remote_name)
233
233
  Dir.chdir(name)
234
234
  # register a nil project id because it hasn't been created yet
235
235
  register_project(name, is_public, nil) do |project_result|
@@ -0,0 +1,62 @@
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 "mortar/s3"
19
+
20
+ # Work with your data on Amazon S3
21
+ class Mortar::Command::S3 < Mortar::Command::Base
22
+
23
+ # s3:get S3_PATH OUTPUT_PATH
24
+ #
25
+ # Download files from a path in S3 to your local computer.
26
+ #
27
+ #
28
+ # Examples:
29
+ #
30
+ # Download from your bucket with:
31
+ # $ mortar s3:get s3://mortar-example/out out/
32
+ def get
33
+ do_get("get")
34
+ end
35
+
36
+ # s3:getmerge S3_PATH OUTPUT_PATH
37
+ #
38
+ # Download and concatenate files from a path in S3 to your local computer. Merges together all of the hadoop output into one file called 'output' at specified OUTPUT_PATH
39
+ #
40
+ # Examples:
41
+ #
42
+ # Download from your bucket with:
43
+ # $ mortar s3:getmerge s3://mortar-example/out out/
44
+ def getmerge
45
+ do_get("getmerge", true)
46
+ end
47
+
48
+ private
49
+
50
+ # get functionality
51
+ def do_get(command, concat = nil)
52
+ s3_path = shift_argument
53
+ output_path = shift_argument
54
+ unless s3_path and output_path
55
+ error("Usage: mortar s3:#{command} S3_PATH OUTPUT_PATH\nMust specify S3_PATH and OUTPUT_PATH.")
56
+ end
57
+ s3 = Mortar::S3::S3.new
58
+ bucket, key = s3.get_bucket_and_key(s3_path)
59
+ s3.do_download(bucket, key, output_path, concat)
60
+ end
61
+
62
+ end
@@ -53,7 +53,7 @@ class Mortar::Command::Version < Mortar::Command::Base
53
53
  shell_url = ENV.fetch("MORTAR_INSTALL", "http://install.mortardata.com")
54
54
  dir = "/opt/mortar/installer"
55
55
  begin
56
- cmd = "sudo mkdir -p #{dir} && sudo curl -sS -L -o #{dir}/install.sh #{shell_url} && sudo bash #{dir}/install.sh#{version_number}"
56
+ cmd = "echo 'Upgrading will prompt for your sudo password.\n' && sudo mkdir -p #{dir} && sudo curl -sS -L -o #{dir}/install.sh #{shell_url} && sudo bash #{dir}/install.sh#{version_number}"
57
57
  Kernel.system cmd
58
58
  ensure
59
59
  end
data/lib/mortar/git.rb CHANGED
@@ -536,6 +536,52 @@ module Mortar
536
536
  def clone(git_url, path="", remote_name="origin")
537
537
  git("clone -o %s %s \"%s\"" % [remote_name, git_url, path], true, false)
538
538
  end
539
+
540
+ def fork_base_remote_name
541
+ "base"
542
+ end
543
+
544
+ def is_fork_repo_updated(git_organization)
545
+ if remotes(git_organization).has_key?(fork_base_remote_name)
546
+ fetch(fork_base_remote_name)
547
+ latest_commit = get_latest_hash(fork_base_remote_name)
548
+ last_commit = get_last_recorded_fork_hash
549
+ unless latest_commit == last_commit || contains_hash(latest_commit)
550
+ File.open(mortar_fork_meta_file, "wb") do |f|
551
+ f.write(latest_commit)
552
+ end
553
+ return true
554
+ end
555
+ end
556
+ return false
557
+ end
558
+
559
+ def get_last_recorded_fork_hash
560
+ if File.exists?(mortar_fork_meta_file)
561
+ File.open(mortar_fork_meta_file, "r") do |f|
562
+ file_contents = f.read()
563
+ file_contents.strip
564
+ end
565
+ end
566
+ end
567
+
568
+ def mortar_fork_meta_file
569
+ ".mortar-fork"
570
+ end
571
+
572
+ def fetch(remote)
573
+ git("fetch #{remote}")
574
+ end
575
+
576
+ def get_latest_hash(remote)
577
+ git("log --pretty=\"%H\" -n 1 #{remote}")
578
+ end
579
+
580
+ def contains_hash(hash)
581
+ git("log --pretty=\"%H\"").include?(hash)
582
+ end
583
+
584
+
539
585
  end
540
586
  end
541
587
  end
data/lib/mortar/s3.rb ADDED
@@ -0,0 +1,177 @@
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
+ # Portions of this code from heroku (https://github.com/heroku/heroku/) Copyright Heroku 2008 - 2014,
17
+ # used under an MIT license (https://github.com/heroku/heroku/blob/master/LICENSE).
18
+ #
19
+ require 'mortar/local/controller'
20
+ require "mortar/command/base"
21
+ require "mortar/helpers"
22
+ require 'aws-sdk'
23
+ require 'uri'
24
+ require 'pathname'
25
+
26
+
27
+ module Mortar
28
+ module S3
29
+ class S3
30
+ include Helpers
31
+
32
+ # gets aws::s3 object
33
+ def get_s3
34
+ ctrl = Mortar::Local::Controller.new
35
+ ctrl.require_aws_keys
36
+ return AWS::S3.new(
37
+ :access_key_id => ENV['AWS_ACCESS_KEY'],
38
+ :secret_access_key => ENV['AWS_SECRET_KEY'])
39
+ end
40
+
41
+ # returns bucket and key from s3 path
42
+ def get_bucket_and_key(s3_path)
43
+ uri = ''
44
+ begin
45
+ uri = URI.parse(s3_path)
46
+ rescue URI::InvalidURIError => msg
47
+ error("#{msg}.\nIt is strongly suggested that your bucket name does not contain an underscore character.\nPlease see http://blog.mortardata.com/post/58920122308/s3-hadoop-performance at tip #4.")
48
+ end
49
+
50
+
51
+ unless uri != '' && is_valid_s3_path(uri)
52
+ error("Requested S3 path, #{s3_path}, is invalid. Please ensure that your S3 path begins with 's3://'. Example: s3://my-bucket/my-key.")
53
+ end
54
+ return uri.host,uri.path[1, uri.path.length]
55
+ end
56
+
57
+
58
+ # checks if string is a valid s3 path
59
+ def is_valid_s3_path(uri)
60
+ uri.scheme == 's3' && uri.host && !uri.path[1, uri.path.length].to_s.empty?
61
+ end
62
+
63
+
64
+ def remove_slash(str)
65
+ if str[-1, 1] == "/"
66
+ str = str[0, str.length-1]
67
+ end
68
+ return str
69
+ end
70
+
71
+ def get_file_name(key_str)
72
+ if !key_str.rindex("/")
73
+ return key_str
74
+ else
75
+ return Pathname.new(key_str).basename.to_s
76
+ end
77
+ end
78
+
79
+ def is_file(path, key)
80
+ if is_not_folder(path,key)
81
+ res = Pathname.new(path).basename.to_s
82
+ if !is_hidden_file(res)
83
+ return true
84
+ end
85
+ end
86
+ return false
87
+ end
88
+
89
+ def is_not_folder(path, key)
90
+ suffix = ["/", "_$folder$"]
91
+ # not a folder
92
+ path[-1,1] != suffix[0] && !path.index(suffix[1]) and !path[key.length+1, path.length].index("/")
93
+ #path[-1,1] != suffix[0] && !path.index(suffix[1]) and Pathname.new(path).basename.to_s != ''
94
+ end
95
+
96
+ def is_hidden_file(str)
97
+ str[0,1] == "." || str[0,1] == "_"
98
+ end
99
+
100
+ def remove_file(file_path)
101
+ if File.file?(file_path)
102
+ FileUtils.remove(file_path)
103
+ end
104
+ end
105
+
106
+ # checks s3 bucket to see if bucket and key exists for given aws keys
107
+ def check_bucket(s3, bucket)
108
+ s3.buckets[bucket].exists?
109
+ end
110
+
111
+
112
+ # gets s3 object, where each item is a file in bucket and key
113
+ def get_s3_objects(s3, bucket, key)
114
+ buck = s3.buckets[bucket]
115
+ # removes slash at end if it exists
116
+ key = remove_slash(key)
117
+ if buck.objects[key].exists?
118
+ [buck.objects[key]]
119
+ else
120
+ valid_items = Array.new
121
+ buck.objects.with_prefix(key).each do |obj|
122
+ if is_file(obj.key, key)
123
+ valid_items.push(obj)
124
+ end
125
+ end
126
+ return valid_items
127
+ end
128
+ end
129
+
130
+ def write_s3_to_file(s3_object, file_name, write_mode)
131
+ File.open(file_name, write_mode) do |file|
132
+ s3_object.read do |chunk|
133
+ file.write(chunk)
134
+ end
135
+ end
136
+ end
137
+
138
+
139
+ def download_s3(s3, bucket, key, output_path, concat = nil)
140
+ s3_objects = get_s3_objects(s3, bucket,key)
141
+ concat_file_name = "output"
142
+ concat_target = "#{output_path}/#{concat_file_name}"
143
+ if concat
144
+ remove_file(concat_target)
145
+ end
146
+ if !s3_objects.empty?
147
+ s3_objects.each do |s3_obj|
148
+ key_str = s3_obj.key
149
+ out_file = concat ? concat_file_name : get_file_name(key_str)
150
+ output_target = "#{output_path}/#{out_file}"
151
+ display "Writing s3://#{bucket}/#{key_str} to #{output_target}"
152
+ write_s3_to_file(s3_obj, output_target, concat ? "a" : "w+")
153
+ end
154
+ else
155
+ error("No contents were found at path s3://#{bucket}/#{key}. Please specify again.")
156
+ end
157
+ end
158
+
159
+ # Implementation of the download command
160
+ def do_download(bucket, key, output_path, concat=nil)
161
+ s3 = get_s3
162
+ # check and messages for concatting file
163
+ unless check_bucket(s3, bucket)
164
+ error("Requested S3 bucket #{bucket} in path, s3://#{bucket}/#{key}, does not exist.")
165
+ end
166
+ # creates output directory
167
+ unless File.directory?(output_path)
168
+ FileUtils.mkdir_p(output_path)
169
+ end
170
+ download_s3(s3, bucket, key, output_path, concat)
171
+
172
+ display "All done! "
173
+ end
174
+
175
+ end
176
+ end
177
+ end
@@ -3,6 +3,7 @@ Gemfile.lock
3
3
  *.class
4
4
  *.log
5
5
  tmp
6
+ .mortar-fork
6
7
  .mortar-local
7
8
  logs
8
9
  illustrate-output
@@ -3,6 +3,7 @@ Gemfile.lock
3
3
  *.class
4
4
  *.log
5
5
  tmp
6
+ .mortar-fork
6
7
  .mortar-local
7
8
  logs
8
9
  illustrate-output
@@ -16,5 +16,5 @@
16
16
 
17
17
  module Mortar
18
18
  # see http://semver.org/
19
- VERSION = "0.15.4"
19
+ VERSION = "0.15.5"
20
20
  end
@@ -83,6 +83,24 @@ other\tgit@github.com:other.git (push)
83
83
  @base.project.name.should == 'myproject-staging'
84
84
  end
85
85
 
86
+ it "errors out on checking for updates to forked project and nobody cares" do
87
+ stub(@base.git).has_dot_git? {true}
88
+ stub(@base.git).remotes {{ 'mortar' => 'myproject' }}
89
+ mock(@base.git).git("config mortar.remote", false).returns("")
90
+ mock(@base.git).is_fork_repo_updated.with_any_args.returns { raise StandardError.new("meessage") }
91
+ mock(@base).warning.times(0)
92
+ @base.project.name.should == 'myproject'
93
+ end
94
+
95
+ it "finds an updated forked project and displays warning" do
96
+ stub(@base.git).has_dot_git? {true}
97
+ stub(@base.git).remotes {{ 'mortar' => 'myproject' }}
98
+ mock(@base.git).git("config mortar.remote", false).returns("")
99
+ mock(@base.git).is_fork_repo_updated.with_any_args.returns(true)
100
+ mock(@base).warning.times(1).with_any_args
101
+ @base.project.name.should == 'myproject'
102
+ end
103
+
86
104
  end
87
105
 
88
106
  context "method_added" do
@@ -0,0 +1,92 @@
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 'spec_helper'
18
+ require 'fakefs/spec_helpers'
19
+ require 'mortar/command/s3'
20
+ require 'aws-sdk'
21
+ require "mortar/s3"
22
+ require 's3_faker'
23
+
24
+ module Mortar::Command
25
+ describe S3 do
26
+ context "s3:getmerge" do
27
+ good_bucket = "good-bucket"
28
+ bad_bucket = "bad-bucket"
29
+ key = "direc"
30
+ s3_path = "s3://#{good_bucket}/#{key}"
31
+ s3_path_bad = "s3://#{bad_bucket}/key"
32
+ output_path = "out"
33
+ concat_file = "#{output_path}/output"
34
+ keys = [
35
+ FakeObject.new("key1", true),
36
+ FakeObject.new("direc/key2", true),
37
+ FakeObject.new("direc/key3", true),
38
+ FakeObject.new("direc/another_direc/key", true)
39
+ ]
40
+ before(:each) do
41
+ ENV["AWS_ACCESS_KEY"] = "foo"
42
+ ENV["AWS_SECRET_KEY"] = "bar"
43
+ buckets = [
44
+ {:bucket => good_bucket, :keys =>keys, :does_exist => true},
45
+ {:bucket => bad_bucket, :keys=> [], :does_exist => false}]
46
+ stub_s3(buckets, ENV["AWS_ACCESS_KEY"], ENV["AWS_SECRET_KEY"])
47
+
48
+ end
49
+
50
+ context "s3:get" do
51
+ it "should specify an s3 bucket" do
52
+ stderr, stdout = execute("s3:get")
53
+ stderr.should == <<-STDERR
54
+ ! Usage: mortar s3:get S3_PATH OUTPUT_PATH
55
+ ! Must specify S3_PATH and OUTPUT_PATH.
56
+ STDERR
57
+ end
58
+
59
+ it "should throw errors at invalid s3_bucket and fetch urls" do
60
+ stderr, stdout = execute("s3:get s3://#{bad_bucket} out")
61
+ stderr.should eq(" ! Requested S3 path, s3://#{bad_bucket}, is invalid. Please ensure that your S3 path begins with 's3://'. Example: s3://my-bucket/my-key.\n")
62
+ end
63
+
64
+
65
+ it "should throw errors at invalid s3_bucket and fetch urls" do
66
+ stderr, stdout = execute("s3:get #{s3_path_bad} out")
67
+ stderr.should eq(" ! Requested S3 bucket #{bad_bucket} in path, #{s3_path_bad}, does not exist.\n")
68
+ end
69
+
70
+ it "should have no errors when ran" do
71
+ any_instance_of(Mortar::S3::S3) do |s3|
72
+ mock(s3).do_download(good_bucket, key, output_path, nil)
73
+ end
74
+ stderr, stdout = execute("s3:get #{s3_path} #{output_path}")
75
+ stderr.should eq("")
76
+ end
77
+ end
78
+
79
+ context "s3:getmerge" do
80
+ it "should have aws-sdk keys properly set for aws-sdk functionality" do
81
+ any_instance_of(Mortar::S3::S3) do |s3|
82
+ mock(s3).do_download(good_bucket, key, output_path, true)
83
+ end
84
+ stderr, stdout = execute("s3:getmerge #{s3_path} #{output_path}")
85
+ stderr.should eq("")
86
+ end
87
+ end
88
+ end
89
+ end
90
+ end
91
+
92
+
@@ -27,7 +27,7 @@ module Mortar::Command
27
27
  base_url = "http://install.mortardata.com"
28
28
  base_version = "0.15.1"
29
29
  tmp_dir_dumm = "/opt/mortar/installer"
30
- curl_command = "sudo mkdir -p #{tmp_dir_dumm} && sudo curl -sS -L -o #{tmp_dir_dumm}/install.sh #{base_url} && sudo bash #{tmp_dir_dumm}/install.sh"
30
+ curl_command = "echo 'Upgrading will prompt for your sudo password.\n' && sudo mkdir -p #{tmp_dir_dumm} && sudo curl -sS -L -o #{tmp_dir_dumm}/install.sh #{base_url} && sudo bash #{tmp_dir_dumm}/install.sh"
31
31
 
32
32
  context("version in prod") do
33
33
  mortar_install_env = ENV['MORTAR_INSTALL']
@@ -63,7 +63,7 @@ module Mortar::Command
63
63
 
64
64
  context("version dev") do
65
65
  dev_url = "dev_url.com"
66
- dev_curl = "sudo mkdir -p #{tmp_dir_dumm} && sudo curl -sS -L -o #{tmp_dir_dumm}/install.sh #{dev_url} && sudo bash #{tmp_dir_dumm}/install.sh"
66
+ dev_curl = "echo 'Upgrading will prompt for your sudo password.\n' && sudo mkdir -p #{tmp_dir_dumm} && sudo curl -sS -L -o #{tmp_dir_dumm}/install.sh #{dev_url} && sudo bash #{tmp_dir_dumm}/install.sh"
67
67
  before(:each) do
68
68
  ENV['MORTAR_INSTALL'] = dev_url
69
69
  end
@@ -405,7 +405,56 @@ STASH
405
405
  end
406
406
  end
407
407
  end
408
-
408
+
409
+ context "checking updates for forked projects" do
410
+ it "skips non forked projects" do
411
+ with_git_initialized_project do |p|
412
+ @git.is_fork_repo_updated("mortarcode-dev").should be_false
413
+ end
414
+ end
415
+
416
+ it "returns true with no .mortar-fork file and new commits" do
417
+ with_forked_git_project do |p|
418
+ mock(@git).fetch(@git.fork_base_remote_name)
419
+ new_commit_hash = "ANewCommitHash"
420
+ mock(@git).get_latest_hash(@git.fork_base_remote_name) {new_commit_hash}
421
+
422
+ @git.is_fork_repo_updated("mortarcode-dev").should be_true
423
+ File.exists?(@git.mortar_fork_meta_file).should be_true
424
+ File.open(@git.mortar_fork_meta_file, "r") do |f|
425
+ file_contents = f.read()
426
+ file_contents.strip
427
+ file_contents.eql?(new_commit_hash).should be_true
428
+ end
429
+ end
430
+ end
431
+
432
+ it "returns false with up to date .mortar-fork file and new commits" do
433
+ with_forked_git_project do |p|
434
+ mock(@git).fetch(@git.fork_base_remote_name)
435
+ new_commit_hash = "ANewCommitHash"
436
+ mock(@git).get_latest_hash(@git.fork_base_remote_name) {new_commit_hash}
437
+ File.open(@git.mortar_fork_meta_file, "wb") do |f|
438
+ f.write(new_commit_hash)
439
+ end
440
+ File.exists?(@git.mortar_fork_meta_file).should be_true
441
+
442
+ @git.is_fork_repo_updated("mortarcode-dev").should be_false
443
+ end
444
+ end
445
+
446
+ it "returns false with no .mortar-fork file and no new commits" do
447
+ with_forked_git_project do |p|
448
+ mock(@git).fetch(@git.fork_base_remote_name)
449
+ current_latest_hash = @git.get_latest_hash("")
450
+ mock(@git).get_latest_hash(@git.fork_base_remote_name) {current_latest_hash}
451
+
452
+ @git.is_fork_repo_updated("mortarcode-dev").should be_false
453
+ end
454
+ end
455
+ end
456
+
457
+
409
458
  =begin
410
459
  #TODO: Fix this.
411
460
 
@@ -0,0 +1,157 @@
1
+ require 'spec_helper'
2
+ require 'mortar/s3'
3
+ require 's3_faker'
4
+
5
+ module Mortar
6
+ describe S3 do
7
+ before(:each) do
8
+ @s3 = Mortar::S3::S3.new
9
+ end
10
+ context("download s3 commands") do
11
+ keys = [
12
+ FakeObject.new("key1", true),
13
+ FakeObject.new("direc/key2", true),
14
+ FakeObject.new("direc/key3", true),
15
+ FakeObject.new("direc/.hidden_key4", true),
16
+ FakeObject.new("direc/another_direc/key", true)
17
+ ]
18
+
19
+ before(:each) do
20
+ ENV["AWS_ACCESS_KEY"] = "foo"
21
+ ENV["AWS_SECRET_KEY"] = "bar"
22
+ buckets = [
23
+ {:bucket => "good_bucket", :keys =>keys, :does_exist => true},
24
+ {:bucket => "bad_bucket", :keys=> [], :does_exist => false}]
25
+ stub_s3(buckets, ENV["AWS_ACCESS_KEY"], ENV["AWS_SECRET_KEY"])
26
+
27
+ end
28
+
29
+ context("string functions") do
30
+ it "should check if the valid path" do
31
+ bucket, key = @s3.get_bucket_and_key('s3://bucket/key')
32
+ bucket.should eq('bucket')
33
+ key.should eq('key')
34
+ bucket, key = @s3.get_bucket_and_key('s3://bucket/deeper/level/key')
35
+ bucket.should eq('bucket')
36
+ key.should eq('deeper/level/key')
37
+ end
38
+
39
+ it "should throw appropriate error if path is not valid" do
40
+ bad_bucket = 'bad'
41
+ previous_stderr, $stderr = $stderr, StringIO.new
42
+ begin
43
+ expect { @s3.get_bucket_and_key(bad_bucket) }.to raise_error(SystemExit)
44
+ $stderr.string.should eq(" ! Requested S3 path, #{bad_bucket}, is invalid. Please ensure that your S3 path begins with 's3://'. Example: s3://my-bucket/my-key.\n")
45
+ ensure
46
+ $stderr = previous_stderr
47
+ end
48
+ end
49
+
50
+ it "should throw error if bucket has underscore" do
51
+ path = "s3://will_fail/key"
52
+ previous_stderr, $stderr = $stderr, StringIO.new
53
+ begin
54
+ expect { @s3.get_bucket_and_key(path) }.to raise_error(SystemExit)
55
+ $stderr.string.should eq(" ! the scheme s3 does not accept registry part: will_fail (or bad hostname?).\n ! It is strongly suggested that your bucket name does not contain an underscore character.\n ! Please see http://blog.mortardata.com/post/58920122308/s3-hadoop-performance at tip #4.\n")
56
+ ensure
57
+ $stderr = previous_stderr
58
+ end
59
+ end
60
+
61
+ it "should remove / if at the end" do
62
+ str = "with/"
63
+ str = @s3.remove_slash(str)
64
+ str.should eq("with")
65
+ end
66
+ it "should not remove / if not at the end" do
67
+ str = "without"
68
+ @s3.remove_slash(str)
69
+ str.should eq("without")
70
+ end
71
+
72
+ it "should get just the file" do
73
+ key = "folder"
74
+ path = "folder/file"
75
+ @s3.is_file(path, key).should eq(true)
76
+
77
+ end
78
+
79
+ it "should get nothing" do
80
+ key = "folder"
81
+ path = "folder/file_$folder$"
82
+ @s3.is_file(path, key).should eq(false)
83
+ path = "folder/folder/"
84
+ @s3.is_file(path, key).should eq(false)
85
+ end
86
+
87
+ end
88
+
89
+
90
+ context("s3 functions") do
91
+ s3 = nil
92
+ before (:each) do
93
+ any_instance_of(Mortar::Local::Controller) do |ctrl|
94
+ mock(ctrl).require_aws_keys
95
+ end
96
+ s3 = @s3.get_s3
97
+ end
98
+
99
+ it "should check if bucket and key exists" do
100
+ bucket = "good_bucket"
101
+ key = "key1"
102
+ @s3.check_bucket(s3, bucket).should eq(true)
103
+
104
+
105
+ bucket = "bad_bucket"
106
+ key = "bad_key"
107
+ @s3.check_bucket(s3, bucket).should eq(false)
108
+ end
109
+
110
+ it "should get s3_objects" do
111
+ bucket = "good_bucket"
112
+ key = "key1"
113
+ result = @s3.get_s3_objects(s3, bucket, key)
114
+ result.length.should eq(1)
115
+ result[0].key.should eq(key)
116
+
117
+ bucket = "good_bucket"
118
+ key = "direc"
119
+ result = @s3.get_s3_objects(s3, bucket, key)
120
+ result.length.should eq(2)
121
+ end
122
+
123
+ it "should return emtpy list if s3_objects are empty" do
124
+ bucket = "good_bucket"
125
+ key = "dire"
126
+ result = @s3.get_s3_objects(s3, bucket, key)
127
+ result.length.should eq(0)
128
+ end
129
+
130
+ it "should check content that is written" do
131
+ bucket = "good_bucket"
132
+ key = "key1"
133
+ mock(@s3).write_s3_to_file(keys[0], 'output/key1', 'w+')
134
+ @s3.download_s3(s3, bucket, key, 'output')
135
+ end
136
+
137
+ it "should check all files are written" do
138
+ bucket = "good_bucket"
139
+ key = "direc"
140
+ mock(@s3).write_s3_to_file(keys[1], 'output/key2', 'w+')
141
+ mock(@s3).write_s3_to_file(keys[2], 'output/key3', 'w+')
142
+ @s3.download_s3(s3, bucket, key, 'output')
143
+ end
144
+
145
+ it "should concat all files to one and delete out file when already exists" do
146
+ bucket = "good_bucket"
147
+ key = "direc"
148
+ mock(File).file?("output/output"){true}
149
+ mock(FileUtils).remove("output/output")
150
+ mock(@s3).write_s3_to_file(keys[1], 'output/output', 'a')
151
+ mock(@s3).write_s3_to_file(keys[2], 'output/output', 'a')
152
+ @s3.download_s3(s3, bucket, key, 'output', true)
153
+ end
154
+ end
155
+ end
156
+ end
157
+ end
data/spec/s3_faker.rb ADDED
@@ -0,0 +1,99 @@
1
+ require 'aws-sdk'
2
+ def stub_s3(buckets, access_key_id, secret_access_key)
3
+ stub(AWS::S3).new(
4
+ :access_key_id => access_key_id,
5
+ :secret_access_key => secret_access_key).returns(FakeS3.new(buckets))
6
+ end
7
+
8
+ class FakeS3
9
+ attr_accessor :buckets
10
+
11
+ def initialize(buckets)
12
+ @buckets = FakeS3Collection.new
13
+ buckets.each do |buck|
14
+ @buckets.add(buck[:bucket], FakeBucket.new(buck[:bucket],buck[:keys], buck[:does_exist]))
15
+ end
16
+
17
+ end
18
+
19
+ end
20
+
21
+ # class that mocks bucket and s3 object collection
22
+ class FakeS3Collection
23
+ include Enumerable
24
+ attr_accessor :objects
25
+ def initialize
26
+ @objects = {}
27
+ end
28
+ def [](key)
29
+ if @objects.has_key?(key)
30
+ return @objects[key]
31
+ else
32
+ return FakeObject.new("", false)
33
+ end
34
+ end
35
+
36
+ def add (key, val)
37
+ @objects[key] = val
38
+ end
39
+
40
+ def with_prefix(prefix)
41
+ res = FakeS3Collection.new
42
+ @objects.keys.each do |key|
43
+ if key.index(prefix) == 0
44
+ res.add(key, @objects[key])
45
+ end
46
+ end
47
+ return res
48
+ end
49
+
50
+ def each(options = {}, &block)
51
+ @objects.keys.each do |key|
52
+ if @objects[key].does_exist
53
+ block.call(@objects[key])
54
+ end
55
+ end
56
+ end
57
+
58
+ end
59
+
60
+
61
+ # Spec class to mock a fake bucket
62
+ class FakeBucket
63
+ attr_accessor :name, :keys, :does_exist, :objects
64
+
65
+ def initialize(name, keys, does_exist)
66
+ @name = name
67
+ @keys = keys
68
+ @objects = FakeS3Collection.new
69
+ keys.each do |k|
70
+ @objects.add(k.key,k)
71
+ end
72
+ @does_exist = does_exist
73
+ end
74
+
75
+
76
+ def exists?
77
+ return @does_exist
78
+ end
79
+
80
+ end
81
+
82
+
83
+
84
+ # Spec class to mock a fake s3 object
85
+ class FakeObject
86
+ attr_accessor :key, :does_exist
87
+ def initialize(key, does_exist)
88
+ @key = key
89
+ @does_exist = does_exist
90
+ end
91
+
92
+ def exists?
93
+ @does_exist
94
+ end
95
+
96
+ def read
97
+ return "Content #{@key}"
98
+ end
99
+ end
data/spec/spec_helper.rb CHANGED
@@ -200,6 +200,15 @@ def with_blank_project_with_name(name, &block)
200
200
  end
201
201
  end
202
202
 
203
+ def with_forked_git_project(&block)
204
+ with_git_initialized_project do |project|
205
+ git = Mortar::Git::Git.new
206
+ remote = git.fork_base_remote_name
207
+ `git remote add #{remote} git@github.com:mortarcode-dev/fork_#{project.name}.git`
208
+ block.call(project)
209
+ end
210
+ end
211
+
203
212
  def with_git_initialized_project(&block)
204
213
  remote_prefix = "4dbbd83cae8d5bf8a4000000_"
205
214
  with_git_initialized_project_with_remote_prefix(remote_prefix, &block)
metadata CHANGED
@@ -1,20 +1,18 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: mortar
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.15.4
5
- prerelease:
4
+ version: 0.15.5
6
5
  platform: ruby
7
6
  authors:
8
7
  - Mortar Data
9
8
  autorequire:
10
9
  bindir: bin
11
10
  cert_chain: []
12
- date: 2014-03-25 00:00:00.000000000 Z
11
+ date: 2014-04-04 00:00:00.000000000 Z
13
12
  dependencies:
14
13
  - !ruby/object:Gem::Dependency
15
14
  name: rdoc
16
15
  requirement: !ruby/object:Gem::Requirement
17
- none: false
18
16
  requirements:
19
17
  - - ! '>='
20
18
  - !ruby/object:Gem::Version
@@ -22,7 +20,6 @@ dependencies:
22
20
  type: :runtime
23
21
  prerelease: false
24
22
  version_requirements: !ruby/object:Gem::Requirement
25
- none: false
26
23
  requirements:
27
24
  - - ! '>='
28
25
  - !ruby/object:Gem::Version
@@ -30,7 +27,6 @@ dependencies:
30
27
  - !ruby/object:Gem::Dependency
31
28
  name: mortar-api-ruby
32
29
  requirement: !ruby/object:Gem::Requirement
33
- none: false
34
30
  requirements:
35
31
  - - ~>
36
32
  - !ruby/object:Gem::Version
@@ -38,7 +34,6 @@ dependencies:
38
34
  type: :runtime
39
35
  prerelease: false
40
36
  version_requirements: !ruby/object:Gem::Requirement
41
- none: false
42
37
  requirements:
43
38
  - - ~>
44
39
  - !ruby/object:Gem::Version
@@ -46,7 +41,6 @@ dependencies:
46
41
  - !ruby/object:Gem::Dependency
47
42
  name: netrc
48
43
  requirement: !ruby/object:Gem::Requirement
49
- none: false
50
44
  requirements:
51
45
  - - ~>
52
46
  - !ruby/object:Gem::Version
@@ -54,7 +48,6 @@ dependencies:
54
48
  type: :runtime
55
49
  prerelease: false
56
50
  version_requirements: !ruby/object:Gem::Requirement
57
- none: false
58
51
  requirements:
59
52
  - - ~>
60
53
  - !ruby/object:Gem::Version
@@ -62,7 +55,6 @@ dependencies:
62
55
  - !ruby/object:Gem::Dependency
63
56
  name: launchy
64
57
  requirement: !ruby/object:Gem::Requirement
65
- none: false
66
58
  requirements:
67
59
  - - ~>
68
60
  - !ruby/object:Gem::Version
@@ -70,7 +62,6 @@ dependencies:
70
62
  type: :runtime
71
63
  prerelease: false
72
64
  version_requirements: !ruby/object:Gem::Requirement
73
- none: false
74
65
  requirements:
75
66
  - - ~>
76
67
  - !ruby/object:Gem::Version
@@ -78,7 +69,6 @@ dependencies:
78
69
  - !ruby/object:Gem::Dependency
79
70
  name: parseconfig
80
71
  requirement: !ruby/object:Gem::Requirement
81
- none: false
82
72
  requirements:
83
73
  - - ~>
84
74
  - !ruby/object:Gem::Version
@@ -86,15 +76,41 @@ dependencies:
86
76
  type: :runtime
87
77
  prerelease: false
88
78
  version_requirements: !ruby/object:Gem::Requirement
89
- none: false
90
79
  requirements:
91
80
  - - ~>
92
81
  - !ruby/object:Gem::Version
93
82
  version: 1.0.2
83
+ - !ruby/object:Gem::Dependency
84
+ name: aws-sdk
85
+ requirement: !ruby/object:Gem::Requirement
86
+ requirements:
87
+ - - ~>
88
+ - !ruby/object:Gem::Version
89
+ version: '1.0'
90
+ type: :runtime
91
+ prerelease: false
92
+ version_requirements: !ruby/object:Gem::Requirement
93
+ requirements:
94
+ - - ~>
95
+ - !ruby/object:Gem::Version
96
+ version: '1.0'
97
+ - !ruby/object:Gem::Dependency
98
+ name: nokogiri
99
+ requirement: !ruby/object:Gem::Requirement
100
+ requirements:
101
+ - - ~>
102
+ - !ruby/object:Gem::Version
103
+ version: 1.5.0
104
+ type: :runtime
105
+ prerelease: false
106
+ version_requirements: !ruby/object:Gem::Requirement
107
+ requirements:
108
+ - - ~>
109
+ - !ruby/object:Gem::Version
110
+ version: 1.5.0
94
111
  - !ruby/object:Gem::Dependency
95
112
  name: excon
96
113
  requirement: !ruby/object:Gem::Requirement
97
- none: false
98
114
  requirements:
99
115
  - - ~>
100
116
  - !ruby/object:Gem::Version
@@ -102,7 +118,6 @@ dependencies:
102
118
  type: :development
103
119
  prerelease: false
104
120
  version_requirements: !ruby/object:Gem::Requirement
105
- none: false
106
121
  requirements:
107
122
  - - ~>
108
123
  - !ruby/object:Gem::Version
@@ -110,7 +125,6 @@ dependencies:
110
125
  - !ruby/object:Gem::Dependency
111
126
  name: fakefs
112
127
  requirement: !ruby/object:Gem::Requirement
113
- none: false
114
128
  requirements:
115
129
  - - ~>
116
130
  - !ruby/object:Gem::Version
@@ -118,7 +132,6 @@ dependencies:
118
132
  type: :development
119
133
  prerelease: false
120
134
  version_requirements: !ruby/object:Gem::Requirement
121
- none: false
122
135
  requirements:
123
136
  - - ~>
124
137
  - !ruby/object:Gem::Version
@@ -126,7 +139,6 @@ dependencies:
126
139
  - !ruby/object:Gem::Dependency
127
140
  name: gem-release
128
141
  requirement: !ruby/object:Gem::Requirement
129
- none: false
130
142
  requirements:
131
143
  - - ! '>='
132
144
  - !ruby/object:Gem::Version
@@ -134,7 +146,6 @@ dependencies:
134
146
  type: :development
135
147
  prerelease: false
136
148
  version_requirements: !ruby/object:Gem::Requirement
137
- none: false
138
149
  requirements:
139
150
  - - ! '>='
140
151
  - !ruby/object:Gem::Version
@@ -142,23 +153,20 @@ dependencies:
142
153
  - !ruby/object:Gem::Dependency
143
154
  name: rake
144
155
  requirement: !ruby/object:Gem::Requirement
145
- none: false
146
156
  requirements:
147
- - - ! '>='
157
+ - - ~>
148
158
  - !ruby/object:Gem::Version
149
- version: '0'
159
+ version: 10.1.1
150
160
  type: :development
151
161
  prerelease: false
152
162
  version_requirements: !ruby/object:Gem::Requirement
153
- none: false
154
163
  requirements:
155
- - - ! '>='
164
+ - - ~>
156
165
  - !ruby/object:Gem::Version
157
- version: '0'
166
+ version: 10.1.1
158
167
  - !ruby/object:Gem::Dependency
159
168
  name: rr
160
169
  requirement: !ruby/object:Gem::Requirement
161
- none: false
162
170
  requirements:
163
171
  - - ! '>='
164
172
  - !ruby/object:Gem::Version
@@ -166,7 +174,6 @@ dependencies:
166
174
  type: :development
167
175
  prerelease: false
168
176
  version_requirements: !ruby/object:Gem::Requirement
169
- none: false
170
177
  requirements:
171
178
  - - ! '>='
172
179
  - !ruby/object:Gem::Version
@@ -174,7 +181,6 @@ dependencies:
174
181
  - !ruby/object:Gem::Dependency
175
182
  name: rspec
176
183
  requirement: !ruby/object:Gem::Requirement
177
- none: false
178
184
  requirements:
179
185
  - - ! '>='
180
186
  - !ruby/object:Gem::Version
@@ -182,7 +188,6 @@ dependencies:
182
188
  type: :development
183
189
  prerelease: false
184
190
  version_requirements: !ruby/object:Gem::Requirement
185
- none: false
186
191
  requirements:
187
192
  - - ! '>='
188
193
  - !ruby/object:Gem::Version
@@ -222,6 +227,7 @@ files:
222
227
  - lib/mortar/command/pigscripts.rb
223
228
  - lib/mortar/command/plugins.rb
224
229
  - lib/mortar/command/projects.rb
230
+ - lib/mortar/command/s3.rb
225
231
  - lib/mortar/command/validate.rb
226
232
  - lib/mortar/command/version.rb
227
233
  - lib/mortar/conf/luigi/logging.ini
@@ -244,6 +250,7 @@ files:
244
250
  - lib/mortar/pigversion.rb
245
251
  - lib/mortar/plugin.rb
246
252
  - lib/mortar/project.rb
253
+ - lib/mortar/s3.rb
247
254
  - lib/mortar/templates/characterize/README.md
248
255
  - lib/mortar/templates/characterize/controlscripts/lib/__init__.py
249
256
  - lib/mortar/templates/characterize/controlscripts/lib/characterize_control.py
@@ -304,6 +311,7 @@ files:
304
311
  - spec/mortar/command/local_spec.rb
305
312
  - spec/mortar/command/pigscripts_spec.rb
306
313
  - spec/mortar/command/projects_spec.rb
314
+ - spec/mortar/command/s3_spec.rb
307
315
  - spec/mortar/command/validate_spec.rb
308
316
  - spec/mortar/command/version_spec.rb
309
317
  - spec/mortar/command_spec.rb
@@ -317,32 +325,33 @@ files:
317
325
  - spec/mortar/local/python_spec.rb
318
326
  - spec/mortar/plugin_spec.rb
319
327
  - spec/mortar/project_spec.rb
328
+ - spec/mortar/s3_spec.rb
320
329
  - spec/mortar/updater_spec.rb
330
+ - spec/s3_faker.rb
321
331
  - spec/spec.opts
322
332
  - spec/spec_helper.rb
323
333
  - spec/support/display_message_matcher.rb
324
334
  homepage: http://mortardata.com/
325
335
  licenses: []
336
+ metadata: {}
326
337
  post_install_message:
327
338
  rdoc_options: []
328
339
  require_paths:
329
340
  - lib
330
341
  required_ruby_version: !ruby/object:Gem::Requirement
331
- none: false
332
342
  requirements:
333
343
  - - ! '>='
334
344
  - !ruby/object:Gem::Version
335
345
  version: 1.8.7
336
346
  required_rubygems_version: !ruby/object:Gem::Requirement
337
- none: false
338
347
  requirements:
339
348
  - - ! '>='
340
349
  - !ruby/object:Gem::Version
341
350
  version: '0'
342
351
  requirements: []
343
352
  rubyforge_project:
344
- rubygems_version: 1.8.23
353
+ rubygems_version: 2.2.2
345
354
  signing_key:
346
- specification_version: 3
355
+ specification_version: 4
347
356
  summary: Client library and CLI to interact with the Mortar service.
348
357
  test_files: []