mortar 0.15.4 → 0.15.5

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.
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: []