vos 0.1.0 → 0.1.1
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/Rakefile +1 -1
- data/lib/vos/box.rb +28 -16
- data/lib/vos/drivers/local.rb +6 -0
- data/lib/vos/drivers/ssh.rb +22 -142
- data/lib/vos/drivers/ssh_vfs_storage.rb +135 -0
- data/lib/vos/helpers/ubuntu.rb +23 -14
- data/lib/vos.rb +1 -1
- data/readme.md +18 -96
- data/spec/config.example.yml +4 -2
- data/spec/config.yml +2 -5
- data/spec/drivers/ssh_spec.rb +1 -1
- metadata +4 -3
data/Rakefile
CHANGED
data/lib/vos/box.rb
CHANGED
@@ -4,22 +4,33 @@ module Vos
|
|
4
4
|
|
5
5
|
attr_accessor :options
|
6
6
|
|
7
|
-
def initialize
|
8
|
-
|
9
|
-
|
7
|
+
def initialize *args
|
8
|
+
first = args.first
|
9
|
+
if args.empty?
|
10
|
+
@driver = Drivers::Local.new
|
11
|
+
elsif first.is_a?(String) or first.is_a?(Symbol) or first.is_a?(Hash) and (args.size <= 2)
|
12
|
+
if first.is_a? Hash
|
13
|
+
options = first
|
14
|
+
options[:host] ||= 'localhost'
|
15
|
+
else
|
16
|
+
options = args[1] || {}
|
17
|
+
raise 'invalid arguments' unless options.is_a?(Hash)
|
18
|
+
options[:host] = first.to_s
|
19
|
+
end
|
20
|
+
|
21
|
+
@driver = options[:host] == 'localhost' ? Drivers::Local.new(options) : Drivers::Ssh.new(options)
|
22
|
+
elsif args.size == 1
|
23
|
+
@driver = first
|
24
|
+
else
|
25
|
+
raise 'invalid arguments'
|
26
|
+
end
|
10
27
|
end
|
11
28
|
|
12
29
|
|
13
30
|
#
|
14
31
|
# driver
|
15
32
|
#
|
16
|
-
|
17
|
-
unless @driver
|
18
|
-
klass = options[:host] == 'localhost' ? Drivers::Local : Drivers::Ssh
|
19
|
-
@driver = klass.new options
|
20
|
-
end
|
21
|
-
@driver
|
22
|
-
end
|
33
|
+
attr_reader :driver
|
23
34
|
|
24
35
|
def open &block
|
25
36
|
driver.open &block
|
@@ -33,12 +44,13 @@ module Vos
|
|
33
44
|
# Micelaneous
|
34
45
|
#
|
35
46
|
def inspect
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
47
|
+
driver.to_s
|
48
|
+
# host = options[:host]
|
49
|
+
# if host == 'localhost'
|
50
|
+
# ''
|
51
|
+
# else
|
52
|
+
# host
|
53
|
+
# end
|
42
54
|
end
|
43
55
|
alias_method :to_s, :inspect
|
44
56
|
end
|
data/lib/vos/drivers/local.rb
CHANGED
data/lib/vos/drivers/ssh.rb
CHANGED
@@ -4,142 +4,18 @@ require 'net/sftp'
|
|
4
4
|
module Vos
|
5
5
|
module Drivers
|
6
6
|
class Ssh < Abstract
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
#
|
11
|
-
def attributes path
|
12
|
-
|
13
|
-
stat = sftp.stat! fix_path(path)
|
14
|
-
attrs = {}
|
15
|
-
attrs[:file] = stat.file?
|
16
|
-
attrs[:dir] = stat.directory?
|
17
|
-
# stat.symlink?
|
18
|
-
attrs
|
19
|
-
rescue Net::SFTP::StatusException
|
20
|
-
{}
|
21
|
-
end
|
22
|
-
|
23
|
-
def set_attributes path, attrs
|
24
|
-
raise 'not supported'
|
25
|
-
end
|
26
|
-
|
27
|
-
#
|
28
|
-
# File
|
29
|
-
#
|
30
|
-
def read_file path, &block
|
31
|
-
sftp.file.open fix_path(path), 'r' do |is|
|
32
|
-
while buff = is.gets
|
33
|
-
block.call buff
|
34
|
-
end
|
35
|
-
end
|
36
|
-
end
|
37
|
-
|
38
|
-
def write_file path, append, &block
|
39
|
-
# there's no support for :append in Net::SFTP, so we just mimic it
|
40
|
-
if append
|
41
|
-
attrs = attributes(path)
|
42
|
-
data = if attrs
|
43
|
-
if attrs[:file]
|
44
|
-
os = ""
|
45
|
-
read_file(path){|buff| os << buff}
|
46
|
-
delete_file path
|
47
|
-
os
|
48
|
-
else
|
49
|
-
raise "can't append to dir!"
|
50
|
-
end
|
51
|
-
else
|
52
|
-
''
|
53
|
-
end
|
54
|
-
write_file path, false do |writer|
|
55
|
-
writer.call data
|
56
|
-
block.call writer
|
57
|
-
end
|
58
|
-
else
|
59
|
-
sftp.file.open fix_path(path), 'w' do |os|
|
60
|
-
writer = -> buff {os.write buff}
|
61
|
-
block.call writer
|
62
|
-
end
|
63
|
-
end
|
64
|
-
end
|
65
|
-
|
66
|
-
def delete_file remote_file_path
|
67
|
-
sftp.remove! fix_path(remote_file_path)
|
68
|
-
end
|
69
|
-
|
70
|
-
# def move_file path
|
71
|
-
# raise 'not supported'
|
72
|
-
# end
|
73
|
-
|
74
|
-
|
75
|
-
#
|
76
|
-
# Dir
|
77
|
-
#
|
78
|
-
def create_dir path
|
79
|
-
sftp.mkdir! path
|
80
|
-
end
|
81
|
-
|
82
|
-
def delete_dir path
|
83
|
-
exec "rm -r #{path}"
|
84
|
-
end
|
85
|
-
|
86
|
-
def each path, &block
|
87
|
-
sftp.dir.foreach path do |stat|
|
88
|
-
next if stat.name == '.' or stat.name == '..'
|
89
|
-
if stat.directory?
|
90
|
-
block.call stat.name, :dir
|
91
|
-
else
|
92
|
-
block.call stat.name, :file
|
93
|
-
end
|
94
|
-
end
|
95
|
-
end
|
96
|
-
|
97
|
-
def efficient_dir_copy from, to
|
98
|
-
from.storage.open_fs do |from_fs|
|
99
|
-
to.storage.open_fs do |to_fs|
|
100
|
-
if from_fs.local?
|
101
|
-
sftp.upload! from.path, fix_path(to.path)
|
102
|
-
true
|
103
|
-
elsif to_fs.local?
|
104
|
-
sftp.download! fix_path(to.path), from.path, :recursive => true
|
105
|
-
true
|
106
|
-
else
|
107
|
-
false
|
108
|
-
end
|
109
|
-
end
|
110
|
-
end
|
111
|
-
end
|
112
|
-
|
113
|
-
# def move_dir path
|
114
|
-
# raise 'not supported'
|
115
|
-
# end
|
116
|
-
|
117
|
-
|
118
|
-
#
|
119
|
-
# Special
|
120
|
-
#
|
121
|
-
def tmp &block
|
122
|
-
tmp_dir = "/tmp/vfs_#{rand(10**3)}"
|
123
|
-
if block
|
124
|
-
begin
|
125
|
-
create_dir tmp_dir
|
126
|
-
block.call tmp_dir
|
127
|
-
ensure
|
128
|
-
delete_dir tmp_dir
|
129
|
-
end
|
130
|
-
else
|
131
|
-
create_dir tmp_dir
|
132
|
-
tmp_dir
|
133
|
-
end
|
134
|
-
end
|
135
|
-
|
136
|
-
def local?; false end
|
137
|
-
end
|
7
|
+
DEFAULT_OPTIONS = {
|
8
|
+
config: true
|
9
|
+
}
|
138
10
|
|
139
11
|
def initialize options = {}
|
140
|
-
super
|
141
|
-
raise "
|
142
|
-
|
12
|
+
super
|
13
|
+
raise ":host not provided!" unless options[:host]
|
14
|
+
@options = DEFAULT_OPTIONS.merge options
|
15
|
+
|
16
|
+
# config_options = Net::SSH.configuration_for(options[:host])
|
17
|
+
# options = DEFAULT_OPTIONS.merge(config_options).merge options
|
18
|
+
# raise ":user not provided (provide explicitly or in .ssh/config)!" unless options[:user]
|
143
19
|
end
|
144
20
|
|
145
21
|
|
@@ -160,10 +36,11 @@ module Vos
|
|
160
36
|
end
|
161
37
|
else
|
162
38
|
unless @ssh
|
163
|
-
|
164
|
-
host =
|
165
|
-
user =
|
166
|
-
|
39
|
+
opt = self.options.clone
|
40
|
+
host = opt.delete :host #] || raise('host not provided!')
|
41
|
+
# user = options.delete(:user) || raise('user not provied!')
|
42
|
+
|
43
|
+
@ssh = Net::SSH.start(host, nil, opt)
|
167
44
|
@sftp = @ssh.sftp.connect
|
168
45
|
end
|
169
46
|
end
|
@@ -177,13 +54,11 @@ module Vos
|
|
177
54
|
end
|
178
55
|
end
|
179
56
|
|
180
|
-
|
181
|
-
|
182
|
-
|
57
|
+
|
183
58
|
#
|
184
59
|
# Vfs
|
185
60
|
#
|
186
|
-
include
|
61
|
+
include SshVfsStorage
|
187
62
|
alias_method :open_fs, :open
|
188
63
|
|
189
64
|
|
@@ -209,6 +84,11 @@ module Vos
|
|
209
84
|
end
|
210
85
|
|
211
86
|
|
87
|
+
#
|
88
|
+
# Micelaneous
|
89
|
+
#
|
90
|
+
def to_s; options[:host] end
|
91
|
+
|
212
92
|
protected
|
213
93
|
attr_accessor :ssh, :sftp
|
214
94
|
|
@@ -0,0 +1,135 @@
|
|
1
|
+
module Vos
|
2
|
+
module Drivers
|
3
|
+
module SshVfsStorage
|
4
|
+
#
|
5
|
+
# Attributes
|
6
|
+
#
|
7
|
+
def attributes path
|
8
|
+
|
9
|
+
stat = sftp.stat! fix_path(path)
|
10
|
+
attrs = {}
|
11
|
+
attrs[:file] = stat.file?
|
12
|
+
attrs[:dir] = stat.directory?
|
13
|
+
# stat.symlink?
|
14
|
+
attrs
|
15
|
+
rescue Net::SFTP::StatusException
|
16
|
+
{}
|
17
|
+
end
|
18
|
+
|
19
|
+
def set_attributes path, attrs
|
20
|
+
raise 'not supported'
|
21
|
+
end
|
22
|
+
|
23
|
+
#
|
24
|
+
# File
|
25
|
+
#
|
26
|
+
def read_file path, &block
|
27
|
+
sftp.file.open fix_path(path), 'r' do |is|
|
28
|
+
while buff = is.gets
|
29
|
+
block.call buff
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
def write_file path, append, &block
|
35
|
+
# there's no support for :append in Net::SFTP, so we just mimic it
|
36
|
+
if append
|
37
|
+
attrs = attributes(path)
|
38
|
+
data = if attrs
|
39
|
+
if attrs[:file]
|
40
|
+
os = ""
|
41
|
+
read_file(path){|buff| os << buff}
|
42
|
+
delete_file path
|
43
|
+
os
|
44
|
+
else
|
45
|
+
raise "can't append to dir!"
|
46
|
+
end
|
47
|
+
else
|
48
|
+
''
|
49
|
+
end
|
50
|
+
write_file path, false do |writer|
|
51
|
+
writer.call data
|
52
|
+
block.call writer
|
53
|
+
end
|
54
|
+
else
|
55
|
+
sftp.file.open fix_path(path), 'w' do |os|
|
56
|
+
writer = -> buff {os.write buff}
|
57
|
+
block.call writer
|
58
|
+
end
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
62
|
+
def delete_file remote_file_path
|
63
|
+
sftp.remove! fix_path(remote_file_path)
|
64
|
+
end
|
65
|
+
|
66
|
+
# def move_file path
|
67
|
+
# raise 'not supported'
|
68
|
+
# end
|
69
|
+
|
70
|
+
|
71
|
+
#
|
72
|
+
# Dir
|
73
|
+
#
|
74
|
+
def create_dir path
|
75
|
+
sftp.mkdir! path
|
76
|
+
end
|
77
|
+
|
78
|
+
def delete_dir path
|
79
|
+
exec "rm -r #{path}"
|
80
|
+
end
|
81
|
+
|
82
|
+
def each path, &block
|
83
|
+
sftp.dir.foreach path do |stat|
|
84
|
+
next if stat.name == '.' or stat.name == '..'
|
85
|
+
if stat.directory?
|
86
|
+
block.call stat.name, :dir
|
87
|
+
else
|
88
|
+
block.call stat.name, :file
|
89
|
+
end
|
90
|
+
end
|
91
|
+
end
|
92
|
+
|
93
|
+
def efficient_dir_copy from, to
|
94
|
+
from.storage.open_fs do |from_fs|
|
95
|
+
to.storage.open_fs do |to_fs|
|
96
|
+
if from_fs.local?
|
97
|
+
sftp.upload! from.path, fix_path(to.path)
|
98
|
+
true
|
99
|
+
elsif to_fs.local?
|
100
|
+
sftp.download! fix_path(to.path), from.path, :recursive => true
|
101
|
+
true
|
102
|
+
else
|
103
|
+
false
|
104
|
+
end
|
105
|
+
end
|
106
|
+
end
|
107
|
+
end
|
108
|
+
|
109
|
+
# def move_dir path
|
110
|
+
# raise 'not supported'
|
111
|
+
# end
|
112
|
+
|
113
|
+
|
114
|
+
#
|
115
|
+
# Special
|
116
|
+
#
|
117
|
+
def tmp &block
|
118
|
+
tmp_dir = "/tmp/vfs_#{rand(10**3)}"
|
119
|
+
if block
|
120
|
+
begin
|
121
|
+
create_dir tmp_dir
|
122
|
+
block.call tmp_dir
|
123
|
+
ensure
|
124
|
+
delete_dir tmp_dir
|
125
|
+
end
|
126
|
+
else
|
127
|
+
create_dir tmp_dir
|
128
|
+
tmp_dir
|
129
|
+
end
|
130
|
+
end
|
131
|
+
|
132
|
+
def local?; false end
|
133
|
+
end
|
134
|
+
end
|
135
|
+
end
|
data/lib/vos/helpers/ubuntu.rb
CHANGED
@@ -12,19 +12,19 @@ module Vos
|
|
12
12
|
file '/etc/profile' ## file '/etc/environment'
|
13
13
|
end
|
14
14
|
|
15
|
-
def append_to_environment file, reload = true
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
end
|
15
|
+
# def append_to_environment file, reload = true
|
16
|
+
# raise "#{file} must be an Entry" unless file.is_a? Vfs::Entry
|
17
|
+
#
|
18
|
+
# env_ext = dir '/etc/profile_ext'
|
19
|
+
#
|
20
|
+
# remote_file = env_ext[file.name]
|
21
|
+
# file.copy_to! remote_file
|
22
|
+
#
|
23
|
+
# require_clause = "source #{remote_file.path}"
|
24
|
+
# env_file.append "\n#{require_clause}\n" unless env_file.content.include? require_clause
|
25
|
+
#
|
26
|
+
# reload_env if reload
|
27
|
+
# end
|
28
28
|
|
29
29
|
def reload_env
|
30
30
|
bash ". #{env_file.path}"
|
@@ -35,6 +35,15 @@ end
|
|
35
35
|
|
36
36
|
module Vfs
|
37
37
|
class File
|
38
|
-
|
38
|
+
def append_to_environment_of box, reload = true
|
39
|
+
raise "#{box} must be an Vos::Box" unless file.is_a? Vos::Box
|
40
|
+
|
41
|
+
copy_to! box.dir('/etc/profile_ext').file(name)
|
42
|
+
|
43
|
+
require_clause = "source #{remote_file.path}"
|
44
|
+
box.env_file.append "\n#{require_clause}\n" unless env_file.content.include? require_clause
|
45
|
+
|
46
|
+
box.reload_env if reload
|
47
|
+
end
|
39
48
|
end
|
40
49
|
end
|
data/lib/vos.rb
CHANGED
data/readme.md
CHANGED
@@ -1,122 +1,44 @@
|
|
1
|
-
#
|
1
|
+
# Vos - Virtual Operating System
|
2
2
|
|
3
|
-
|
4
|
-
|
3
|
+
Small abstraction over Operating System, mainly it should be used in conjunction with [Virtual File System][vos] tool. Kind of
|
4
|
+
Capistrano but without extra stuff and more universal, not forcing You to follow 'The Rails Way'.
|
5
5
|
|
6
6
|
Currently, there are following implementations available:
|
7
7
|
|
8
|
-
- local
|
9
|
-
- remote
|
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.
|
8
|
+
- local os
|
9
|
+
- remote os (over ssh)
|
22
10
|
|
23
11
|
## Installation
|
24
12
|
|
25
|
-
$ gem install vfs
|
26
13
|
$ gem install vos
|
27
14
|
|
28
15
|
## Code samples:
|
29
|
-
gem 'vfs' # Virtual File System
|
30
|
-
require 'vfs'
|
31
|
-
|
32
16
|
gem 'vos' # Virtual Operating System
|
33
17
|
require 'vos'
|
34
18
|
|
35
|
-
|
36
19
|
# Connections, let's deploy our 'cool_app' project from our local box to remote server
|
37
20
|
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
|
62
|
-
|
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
21
|
|
22
|
+
server.bash 'ls'
|
88
23
|
server['apps/cool_app'].bash 'rails production'
|
89
24
|
|
90
|
-
For more details
|
91
|
-
|
92
|
-
|
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).
|
25
|
+
For more details look also to [Virtual File System][vos] project.
|
26
|
+
Or checkout configuration I use to control my production servers [My Cluster][my_cluster] in conjunction with small
|
27
|
+
configuration tool [Cluster Management][cluster_management].
|
101
28
|
|
102
29
|
## TODO
|
103
30
|
|
104
31
|
### v 0.1 (all done)
|
105
32
|
|
106
|
-
- bash
|
107
|
-
-
|
108
|
-
-
|
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.
|
33
|
+
- bash
|
34
|
+
- some handy shortcuts for ubuntu
|
35
|
+
- integration with Vos
|
117
36
|
|
118
|
-
###
|
37
|
+
### v 0.2
|
119
38
|
|
120
|
-
-
|
39
|
+
- process management (find/kill/filters/attributes)
|
40
|
+
- other os resources management (disk)
|
121
41
|
|
122
|
-
[
|
42
|
+
[vos]: http://github.com/alexeypetrushin/vos
|
43
|
+
[cluster_management]: http://github.com/alexeypetrushin/cluster_management
|
44
|
+
[my_cluster]: http://github.com/alexeypetrushin/my_cluster
|
data/spec/config.example.yml
CHANGED
data/spec/config.yml
CHANGED
data/spec/drivers/ssh_spec.rb
CHANGED
metadata
CHANGED
@@ -5,8 +5,8 @@ version: !ruby/object:Gem::Version
|
|
5
5
|
segments:
|
6
6
|
- 0
|
7
7
|
- 1
|
8
|
-
-
|
9
|
-
version: 0.1.
|
8
|
+
- 1
|
9
|
+
version: 0.1.1
|
10
10
|
platform: ruby
|
11
11
|
authors:
|
12
12
|
- Alexey Petrushin
|
@@ -14,7 +14,7 @@ autorequire:
|
|
14
14
|
bindir: bin
|
15
15
|
cert_chain: []
|
16
16
|
|
17
|
-
date: 2011-02-
|
17
|
+
date: 2011-02-09 00:00:00 +03:00
|
18
18
|
default_executable:
|
19
19
|
dependencies:
|
20
20
|
- !ruby/object:Gem::Dependency
|
@@ -50,6 +50,7 @@ files:
|
|
50
50
|
- lib/vos/drivers/local.rb
|
51
51
|
- lib/vos/drivers/specification.rb
|
52
52
|
- lib/vos/drivers/ssh.rb
|
53
|
+
- lib/vos/drivers/ssh_vfs_storage.rb
|
53
54
|
- lib/vos/gems.rb
|
54
55
|
- lib/vos/helpers/ubuntu.rb
|
55
56
|
- lib/vos/support.rb
|