vos 0.0.4 → 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
data/readme.md CHANGED
@@ -1,21 +1,122 @@
1
- # Rsh - Tiny wrapper over Net::SSH/SFTP
1
+ # Vfs - Virtual File System
2
2
 
3
- Because they are too hard to use and have terrible API design.
3
+ Handy and simple abstraction over any storage that can represent concept of File and Directory (or at least part of it).
4
+ The Vfs for File System Storages is the same as ActiveRecord is for Relational Databases.
4
5
 
5
- box = Rsh::Box.new host: 'webapp.com', ssh: {user: 'root', password: 'secret'}
6
+ Currently, there are following implementations available:
7
+
8
+ - local file system
9
+ - remote file system (over ssh)
10
+
11
+ ## Goals
12
+
13
+ - **handy, simple and clean** API.
14
+ - **high performance** - the same as by using low-level storage API, there should be no extra calls **.
15
+ - same API for different storages (Local FS, SSH, Hadoop, or any other , ...).
16
+ - should work **simultaneously with different storages**.
17
+ - small codebase, easy to extend by others.
18
+ - simple storage-driver implementation, easy add new storage types (Hadoop DFS, LDAP, Document Oriented DB, In-Memory, ...).
19
+
20
+ ** all methods should have the same performance as native system calls, except for :move and :rename. Right now they are implemented
21
+ ASAP by using copy+destroy approach, will be fixed as soon as I'll have free time to do it.
22
+
23
+ ## Installation
24
+
25
+ $ gem install vfs
26
+ $ gem install vos
27
+
28
+ ## Code samples:
29
+ gem 'vfs' # Virtual File System
30
+ require 'vfs'
31
+
32
+ gem 'vos' # Virtual Operating System
33
+ require 'vos'
34
+
35
+
36
+ # Connections, let's deploy our 'cool_app' project from our local box to remote server
37
+ server = Vfs::Box.new(host: 'cool_app.com', ssh: {user: 'me', password: 'secret'})
38
+ me = '~'.to_dir
39
+
40
+ cool_app = server['apps/cool_app']
41
+ projects = me['projects']
42
+
43
+
44
+ # Working with dirs, copying dir from any source to any destination (local/remote/custom_storage_type)
45
+ projects['cool_app'].copy_to cool_app
46
+
47
+
48
+ # Working with files
49
+ dbc = cool_app.file('config/database.yml') # <= the 'config' dir not exist yet
50
+ dbc.write("user: root\npassword: secret") # <= now the 'database.yml' and parent 'config' has been created
51
+ dbc.content =~ /database/ # => false, we forgot to add the database
52
+ dbc.append("\ndatabase: mysql") # let's do it
53
+
54
+ dbc.update do |content| # and add host info
55
+ content + "\nhost: cool_app.com "
56
+ end
57
+
58
+ projects['cool_app/config/database.yml']. # or just overwrite it with our local dev version
59
+ copy_to! dbc
60
+
61
+ # there are also streaming support (read/write/append), please go to specs for docs
6
62
 
7
- box.upload_directory '/my_project', '/apps/my_project'
8
- box.bash 'nohup /apps/my_project/server_start'
9
-
10
- Honestly my wrapper also not very good. I would like to make API looks like the ['rush'][rush] gem (made by Adam Wiggins)
11
- but it requires a lots of time, maybe I'll do it later.
12
- So, for now it's just a small wrapper to do ssh/io operations not so painfull.
13
63
 
64
+ # Checks
65
+ cool_app['config'].exist? # => true
66
+ cool_app.dir('config').exist? # => true
67
+ cool_app.file('config').exist? # => false
68
+
69
+ cool_app['config'].dir? # => true
70
+ cool_app['config'].file? # => false
71
+
72
+
73
+ # Navigation
74
+ config = cool_app['config']
75
+ config.parent # => </apps/cool_app>
76
+ config['../..'] # => </>
77
+ config['../..'].dir? # => true
78
+
79
+ cool_app.entries # => list of dirs and files, also support &block
80
+ cool_app.files # => list of files, also support &block
81
+ cool_app.dirs # => list of dirs, also support &block
82
+
83
+
84
+ # For more please go to specs (create/update/move/copy/destroy/...)
85
+
86
+ ## Integration with [Vos][vos] (Virtual Operating System)
87
+
88
+ server['apps/cool_app'].bash 'rails production'
89
+
90
+ For more details please go to [Vos][vos] project page.
91
+
92
+ # Why?
93
+
94
+ To easy my work: with local FS, remote FS (cluster management, deployment automation), and some specific systems like Hadoop DFS.
95
+
96
+ Because the API of standard File/Dir/FileUtils classes are just terrible. And there's the reason for it - the goal of thouse tools
97
+ is to provide 1-to-1 clone of underlying OS API, instead of provididing handy tool.
98
+
99
+ And if you want to use remote FS - things are getting even worse and more complicated (Net::SSH & Net::SFTP use a little
100
+ different API than local FS, and you has to remember all thouse little quirks).
101
+
14
102
  ## TODO
15
103
 
16
- - remove ssh.remote and use only open/close
17
- - introduce Entity/Dir/File (the same as in Rush)
18
- - allow to move files between any Boxes, not only between local and remote.
19
- - add support for moving dirs.
104
+ ### v 0.1 (all done)
105
+
106
+ - bash & basic support for
107
+ - File.append
108
+ - list of entries/files/dirs
109
+ - support for efficient copy for Local and SSH storages
110
+
111
+ ### v 0.2 (not started)
112
+
113
+ - efficient (not copy/destroy) versions of move_to, rename
114
+ - glob search for directories: Dir['**/*.yml']
115
+ - access via attributes and helpers for unix chmod
116
+ - add storages: remote FS over HTTP.
117
+
118
+ ### future
119
+
120
+ - add storages: Hadoop DFS, MongoDB, Amazon S3
20
121
 
21
- [rush]: http://github.com/adamwiggins/rush
122
+ [vfs]: http://github.com/alexeypetrushin/vfs
data/spec/box_spec.rb CHANGED
@@ -1,109 +1,56 @@
1
1
  require 'spec_helper'
2
2
 
3
- describe Rsh::Box do
4
- with_tmp_spec_dir before: :each
5
-
3
+ describe Vos::Box do
6
4
  before :each do
7
- @box = Rsh::Box.new
8
-
9
- @local_dir = spec_dir
10
- @remote_dir = @box.generate_tmp_dir_name
11
-
12
- @box.remove_directory @remote_dir if @box.directory_exist? @remote_dir
13
- @box.create_directory @remote_dir
14
- end
15
-
16
- after :each do
17
- @box.remove_directory @remote_dir if @box.directory_exist? @remote_dir
5
+ @box = Vos::Box.new
6
+ @box.stub :puts
18
7
  end
19
8
 
20
- describe "io" do
21
- describe "files" do
22
- before :each do
23
- @local_file = "#{@local_dir}/local_file"
24
- @check_file = "#{@local_dir}/check_file"
25
- @remote_file = "#{@remote_dir}/remote_file"
26
- end
27
-
28
- it "file_exist?" do
29
- @box.file_exist?(@remote_file).should be_false
30
- @box.upload_file(@local_file, @remote_file)
31
- @box.file_exist?(@remote_file).should be_true
32
- end
33
-
34
- it "upload_file" do
35
- @box.upload_file(@local_file, @remote_file)
36
- @box.file_exist?(@remote_file).should be_true
37
-
38
- lambda{@box.upload_file(@local_file, @remote_file)}.should raise_error(/exists/)
39
-
40
- # upload with override
41
- @box.upload_file(@local_file, @remote_file, override: true)
42
- @box.file_exist?(@remote_file).should be_true
43
- end
44
-
45
- it "download_file" do
46
- lambda{@box.download_file(@remote_file, @check_file)}.should raise_error(/not exists/)
47
- @box.upload_file(@local_file, @remote_file)
48
- @box.download_file(@remote_file, @check_file)
49
- File.read(@local_file).should == File.read(@check_file)
50
- end
51
-
52
- it "remove_file" do
53
- lambda{@box.remove_file(@remote_file)}.should raise_error(/not exists/)
54
- @box.upload_file(@local_file, @remote_file)
55
- @box.file_exist?(@remote_file).should be_true
56
- @box.remove_file(@remote_file)
57
- @box.file_exist?(@remote_file).should be_false
58
- end
9
+ describe 'vfs integration' do
10
+ it 'smoke test' do
11
+ @box['/'].exist?.should be_true
59
12
  end
60
13
 
61
- describe 'directories' do
62
- before :each do
63
- @from_local, @remote_path, @to_local = "#{@local_dir}/dir", "#{@remote_dir}/upload", "#{@local_dir}/download"
64
- end
65
-
66
- it "directory_exist?" do
67
- @box.file_exist?(@remote_path).should be_false
68
- @box.upload_directory(@from_local, @remote_path)
69
- @box.file_exist?(@remote_path).should be_true
70
- end
71
-
72
- it "upload_directory" do
73
- @box.upload_directory(@from_local, @remote_path)
74
- @box.directory_exist?(@remote_path).should be_true
75
-
76
- lambda{@box.upload_directory(@from_local, @remote_path)}.should raise_error(/exists/)
77
-
78
- # upload with override
79
- @box.upload_directory(@from_local, @remote_path, override: true)
80
- @box.directory_exist?(@remote_path).should be_true
81
- end
82
-
83
- it "download_directory" do
84
- lambda{@box.download_directory(@remote_path, @to_local)}.should raise_error(/not exists/)
85
- @box.upload_directory(@from_local, @remote_path)
86
- @box.download_directory(@remote_path, @to_local)
87
- File.exist?("#{@to_local}/dir2/file").should be_true
88
- end
89
-
90
- it "remove_directory" do
91
- lambda{@box.remove_directory(@remote_path)}.should raise_error(/not exists/)
92
- @box.upload_directory(@from_local, @remote_path)
93
- @box.directory_exist?(@remote_path).should be_true
94
- @box.remove_directory(@remote_path)
95
- @box.directory_exist?(@remote_path).should be_false
96
- end
14
+ it 'vfs integration' do
15
+ @box['/'].bash("echo 'ok'").should == "ok\n"
97
16
  end
98
17
  end
99
18
 
100
19
  describe "shell" do
101
20
  it 'bash' do
102
21
  @box.bash("echo 'ok'").should == "ok\n"
103
- end
22
+ end
23
+
24
+ it 'bash working dir should be /' do
25
+ @box.bash('pwd').should == "/\n"
26
+ end
27
+
28
+ it 'check with regex' do
29
+ @box.bash "echo 'ok'", /ok/
30
+ -> {@box.bash "echo 'ok'", /no/}.should raise_error(/not match/)
31
+ end
104
32
 
105
33
  it "exec" do
106
34
  @box.exec("echo 'ok'").should == [0, "ok\n", ""]
107
35
  end
36
+
37
+ it 'home' do
38
+ @box.home.should_not be_nil
39
+ end
40
+
41
+ it 'env' do
42
+ @box.env.should == {}
43
+ @box.env = {a: 'b'}
44
+
45
+ @box.env c: 'd' do
46
+ @box.env.should == {a: 'b', c: 'd'}
47
+ end
48
+ @box.env.should == {a: 'b'}
49
+
50
+ @box.env(c: 'd')
51
+ @box.env.should == {a: 'b', c: 'd'}
52
+
53
+ @box.env('ls').should == "a=b c=d && ls"
54
+ end
108
55
  end
109
56
  end
@@ -0,0 +1,10 @@
1
+ require 'drivers/spec_helper'
2
+
3
+ describe Vos::Drivers::Local do
4
+ it_should_behave_like "vos driver"
5
+ it_should_behave_like "vfs storage"
6
+
7
+ before :each do
8
+ @storage = @driver = Vos::Drivers::Local.new
9
+ end
10
+ end
@@ -0,0 +1,5 @@
1
+ require 'spec_helper'
2
+ require 'yaml'
3
+
4
+ require 'vfs/storages/specification'
5
+ require 'vos/drivers/specification'
@@ -0,0 +1,15 @@
1
+ require 'drivers/spec_helper'
2
+
3
+ describe Vos::Drivers::Ssh do
4
+ it_should_behave_like "vos driver"
5
+ it_should_behave_like "vfs storage"
6
+
7
+ before :all do
8
+ @storage = @driver = Vos::Drivers::Ssh.new(config[:remote_driver])
9
+ @driver.open
10
+ end
11
+
12
+ after :all do
13
+ @driver.close
14
+ end
15
+ end
data/spec/spec_helper.rb CHANGED
@@ -1,5 +1,6 @@
1
1
  require 'rspec_ext'
2
- require 'rsh'
2
+ require 'ruby_ext'
3
+ require 'vos'
3
4
 
4
5
  rspec do
5
6
  def config
metadata CHANGED
@@ -4,9 +4,9 @@ version: !ruby/object:Gem::Version
4
4
  prerelease: false
5
5
  segments:
6
6
  - 0
7
+ - 1
7
8
  - 0
8
- - 4
9
- version: 0.0.4
9
+ version: 0.1.0
10
10
  platform: ruby
11
11
  authors:
12
12
  - Alexey Petrushin
@@ -14,10 +14,22 @@ autorequire:
14
14
  bindir: bin
15
15
  cert_chain: []
16
16
 
17
- date: 2011-02-02 00:00:00 +03:00
17
+ date: 2011-02-08 00:00:00 +03:00
18
18
  default_executable:
19
- dependencies: []
20
-
19
+ dependencies:
20
+ - !ruby/object:Gem::Dependency
21
+ name: vfs
22
+ prerelease: false
23
+ requirement: &id001 !ruby/object:Gem::Requirement
24
+ none: false
25
+ requirements:
26
+ - - ">="
27
+ - !ruby/object:Gem::Version
28
+ segments:
29
+ - 0
30
+ version: "0"
31
+ type: :runtime
32
+ version_requirements: *id001
21
33
  description:
22
34
  email:
23
35
  executables: []
@@ -30,25 +42,25 @@ files:
30
42
  - Rakefile
31
43
  - readme.md
32
44
  - lib/old/ssh.rb
33
- - lib/rsh/box/marks.rb
34
- - lib/rsh/box.rb
35
- - lib/rsh/drivers/abstract.rb
36
- - lib/rsh/drivers/local.rb
37
- - lib/rsh/drivers/ssh.rb
38
- - lib/rsh/gems.rb
39
- - lib/rsh/support.rb
40
- - lib/rsh.rb
41
- - spec/abstract_driver/dir/dir2/file
42
- - spec/abstract_driver/local_file
43
- - spec/abstract_driver.rb
44
- - spec/box_spec/dir/dir2/file
45
- - spec/box_spec/local_file
45
+ - lib/vos/box/marks.rb
46
+ - lib/vos/box/shell.rb
47
+ - lib/vos/box/vfs.rb
48
+ - lib/vos/box.rb
49
+ - lib/vos/drivers/abstract.rb
50
+ - lib/vos/drivers/local.rb
51
+ - lib/vos/drivers/specification.rb
52
+ - lib/vos/drivers/ssh.rb
53
+ - lib/vos/gems.rb
54
+ - lib/vos/helpers/ubuntu.rb
55
+ - lib/vos/support.rb
56
+ - lib/vos.rb
46
57
  - spec/box_spec.rb
47
58
  - spec/config.example.yml
48
59
  - spec/config.yml
49
- - spec/local_driver_spec.rb
60
+ - spec/drivers/local_spec.rb
61
+ - spec/drivers/spec_helper.rb
62
+ - spec/drivers/ssh_spec.rb
50
63
  - spec/spec_helper.rb
51
- - spec/ssh_driver_spec.rb
52
64
  has_rdoc: true
53
65
  homepage: http://github.com/alexeypetrushin/vos
54
66
  licenses: []
@@ -80,6 +92,6 @@ rubyforge_project:
80
92
  rubygems_version: 1.3.7
81
93
  signing_key:
82
94
  specification_version: 3
83
- summary: Tiny wrapper over Net::SSH/SFTP + small rake addon for cluster configuration management
95
+ summary: Virtual Operating System
84
96
  test_files: []
85
97
 
data/lib/rsh/box/marks.rb DELETED
@@ -1,29 +0,0 @@
1
- module Rsh
2
- module Marks
3
- def mark key
4
- ensure_mark_requrements!
5
- bash "touch #{marks_dir}/#{key}"
6
- end
7
-
8
- def has_mark? key
9
- ensure_mark_requrements!
10
- file_exist? "#{marks_dir}/#{key}"
11
- end
12
-
13
- def clear_marks
14
- bash "rm -r #{marks_dir}"
15
- end
16
-
17
- protected
18
- def marks_dir
19
- home "/.marks"
20
- end
21
-
22
- def ensure_mark_requrements!
23
- unless @ensure_mark_requrements
24
- create_directory marks_dir unless directory_exist? marks_dir
25
- @ensure_mark_requrements = true
26
- end
27
- end
28
- end
29
- end
data/lib/rsh/box.rb DELETED
@@ -1,182 +0,0 @@
1
- module Rsh
2
- class Box
3
- include Marks
4
-
5
- attr_accessor :options
6
-
7
- def initialize options = {}
8
- @options = options
9
- options[:host] ||= 'localhost'
10
- end
11
-
12
- def driver
13
- unless @driver
14
- klass = options[:host] == 'localhost' ? Drivers::Local : Drivers::Ssh
15
- @driver = klass.new options
16
- end
17
- @driver
18
- end
19
-
20
- def local_driver
21
- @local_driver ||= Drivers::Local.new
22
- end
23
-
24
- def bulk &b
25
- driver.bulk &b
26
- end
27
-
28
- def upload_file from_local_path, to_remote_path, options = {}
29
- bulk do
30
- raise "file '#{from_local_path}' not exists!" unless local_driver.file_exist? from_local_path
31
- if driver.file_exist?(to_remote_path)
32
- if options[:override]
33
- driver.remove_file to_remote_path
34
- else
35
- raise "file '#{to_remote_path}' already exists!"
36
- end
37
- end
38
- driver.upload_file from_local_path, to_remote_path
39
- end
40
- end
41
-
42
- def download_file from_remote_path, to_local_path, options = {}
43
- bulk do
44
- raise "file #{from_remote_path} not exists!" unless driver.file_exist?(from_remote_path)
45
- if local_driver.file_exist? to_local_path
46
- if options[:override]
47
- local_driver.remove_file to_local_path
48
- else
49
- raise "file #{to_local_path} already exists!"
50
- end
51
- end
52
- driver.download_file from_remote_path, to_local_path
53
- end
54
- end
55
-
56
- def remove_file remote_file_path, options = {}
57
- bulk do
58
- if driver.file_exist? remote_file_path
59
- driver.remove_file remote_file_path
60
- else
61
- raise "file #{remote_file_path} not exists!" unless options[:silent]
62
- end
63
- end
64
- end
65
-
66
- def exist? remote_path
67
- driver.exist? remote_path
68
- end
69
-
70
- def file_exist? remote_file_path
71
- driver.file_exist? remote_file_path
72
- end
73
-
74
- def remove_directory remote_directory_path, options = {}
75
- bulk do
76
- if driver.directory_exist? remote_directory_path
77
- driver.remove_directory remote_directory_path
78
- else
79
- raise "directory #{remote_directory_path} not exists!" unless options[:silent]
80
- end
81
- end
82
- end
83
-
84
- def create_directory remote_path, options = {}
85
- bulk do
86
- if driver.directory_exist?(remote_path)
87
- if options[:override]
88
- driver.remove_directory remote_path
89
- driver.create_directory remote_path
90
- elsif options[:silent]
91
- # do nothing
92
- else
93
- raise "directory '#{remote_path}' already exists!"
94
- end
95
- else
96
- driver.create_directory remote_path
97
- end
98
- end
99
- end
100
-
101
- def directory_exist? remote_path
102
- driver.directory_exist? remote_path
103
- end
104
-
105
- def upload_directory from_local_path, to_remote_path, options = {}
106
- bulk do
107
- raise "directory '#{from_local_path}' not exists!" unless local_driver.directory_exist? from_local_path
108
- if driver.directory_exist?(to_remote_path)
109
- if options[:override]
110
- driver.remove_directory to_remote_path
111
- else
112
- raise "directory '#{to_remote_path}' already exists!"
113
- end
114
- end
115
- driver.upload_directory from_local_path, to_remote_path
116
- end
117
- end
118
-
119
- def download_directory from_remote_path, to_local_path, options = {}
120
- bulk do
121
- raise "directory #{from_remote_path} not exists!" unless driver.directory_exist?(from_remote_path)
122
- if local_driver.directory_exist? to_local_path
123
- if options[:override]
124
- local_driver.remove_directory to_local_path
125
- else
126
- raise "directory #{to_local_path} already exists!"
127
- end
128
- end
129
- driver.download_directory from_remote_path, to_local_path
130
- end
131
- end
132
-
133
- def with_tmp_dir &block
134
- bulk do
135
- tmp_dir = driver.generate_tmp_dir_name
136
- begin
137
- remove_directory tmp_dir if directory_exist? tmp_dir
138
- create_directory tmp_dir
139
- block.call
140
- ensure
141
- remove_directory tmp_dir if directory_exist? tmp_dir
142
- end
143
- end
144
- end
145
-
146
- def bash cmd, options = {}
147
- ignore_stderr = options.delete :ignore_stderr
148
- raise "invalid options :#{options.keys.join(', :')}" unless options.empty?
149
-
150
- code, stdout, stderr = exec cmd
151
- unless code == 0
152
- puts stdout
153
- puts stderr
154
- raise "can't execute '#{cmd}'!"
155
- end
156
- unless stderr.empty? or ignore_stderr
157
- puts stderr
158
- raise "stderr not empty for '#{cmd}'!"
159
- end
160
- stdout + stderr
161
- end
162
-
163
- def exec cmd
164
- driver.exec cmd
165
- end
166
-
167
- def home path = nil
168
- @home ||= bash('cd ~; pwd').gsub("\n", '')
169
- "#{@home}#{path}"
170
- end
171
-
172
- def inspect
173
- "<Box: #{options[:host]}>"
174
- end
175
- alias_method :to_s, :inspect
176
-
177
- protected
178
- def method_missing m, *a, &b
179
- driver.send m, *a, &b
180
- end
181
- end
182
- end
@@ -1,48 +0,0 @@
1
- module Rsh
2
- module Drivers
3
- class Local < Abstract
4
- def upload_file from_local_path, to_remote_path
5
- FileUtils.copy from_local_path, to_remote_path
6
- end
7
-
8
- def download_file from_remote_path, to_local_path
9
- FileUtils.copy from_remote_path, to_local_path
10
- end
11
-
12
- def exist? remote_file_path
13
- File.exist? remote_file_path
14
- end
15
-
16
- alias_method :directory_exist?, :exist?
17
- alias_method :file_exist?, :exist?
18
-
19
- def remove_file remote_file_path
20
- File.delete remote_file_path
21
- end
22
-
23
- def create_directory path
24
- Dir.mkdir path
25
- end
26
-
27
- def remove_directory path
28
- FileUtils.rm_r path
29
- end
30
-
31
- def upload_directory from_local_path, to_remote_path
32
- FileUtils.cp_r from_local_path, to_remote_path
33
- end
34
-
35
- def download_directory from_remote_path, to_local_path
36
- FileUtils.cp_r from_remote_path, to_local_path
37
- end
38
-
39
- def exec command
40
- code, stdout, stderr = Open3.popen3 command do |stdin, stdout, stderr, waitth|
41
- [waitth.value.to_i, stdout.read, stderr.read]
42
- end
43
-
44
- return code, stdout, stderr
45
- end
46
- end
47
- end
48
- end