haveapi-fs 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.
- checksums.yaml +7 -0
- data/.yardopts +1 -0
- data/CHANGELOG +2 -0
- data/Gemfile +3 -0
- data/LICENSE.txt +21 -0
- data/README.md +338 -0
- data/Rakefile +1 -0
- data/assets/css/bootstrap.min.css +6 -0
- data/assets/css/local.css +11 -0
- data/bin/haveapi-fs +5 -0
- data/haveapi-fs.gemspec +30 -0
- data/lib/core_ext/string.rb +9 -0
- data/lib/haveapi/fs/auth/base.rb +56 -0
- data/lib/haveapi/fs/auth/basic.rb +29 -0
- data/lib/haveapi/fs/auth/noauth.rb +9 -0
- data/lib/haveapi/fs/auth/token.rb +39 -0
- data/lib/haveapi/fs/cache.rb +71 -0
- data/lib/haveapi/fs/cleaner.rb +56 -0
- data/lib/haveapi/fs/component.rb +237 -0
- data/lib/haveapi/fs/components/action_dir.rb +106 -0
- data/lib/haveapi/fs/components/action_errors.rb +49 -0
- data/lib/haveapi/fs/components/action_exec.rb +12 -0
- data/lib/haveapi/fs/components/action_exec_edit.rb +90 -0
- data/lib/haveapi/fs/components/action_input.rb +40 -0
- data/lib/haveapi/fs/components/action_message.rb +19 -0
- data/lib/haveapi/fs/components/action_output.rb +79 -0
- data/lib/haveapi/fs/components/action_status.rb +32 -0
- data/lib/haveapi/fs/components/cache_stats.rb +19 -0
- data/lib/haveapi/fs/components/component_list.rb +24 -0
- data/lib/haveapi/fs/components/create_action_dir.rb +15 -0
- data/lib/haveapi/fs/components/delete_action_dir.rb +22 -0
- data/lib/haveapi/fs/components/directory.rb +45 -0
- data/lib/haveapi/fs/components/directory_reset.rb +8 -0
- data/lib/haveapi/fs/components/executable.rb +75 -0
- data/lib/haveapi/fs/components/file.rb +43 -0
- data/lib/haveapi/fs/components/groff_help_file.rb +9 -0
- data/lib/haveapi/fs/components/help_file.rb +28 -0
- data/lib/haveapi/fs/components/html_help_file.rb +24 -0
- data/lib/haveapi/fs/components/index_filter.rb +63 -0
- data/lib/haveapi/fs/components/info_files.rb +19 -0
- data/lib/haveapi/fs/components/instance_create.rb +20 -0
- data/lib/haveapi/fs/components/instance_edit.rb +49 -0
- data/lib/haveapi/fs/components/list_item.rb +28 -0
- data/lib/haveapi/fs/components/md_help_file.rb +24 -0
- data/lib/haveapi/fs/components/meta_dir.rb +42 -0
- data/lib/haveapi/fs/components/meta_file.rb +21 -0
- data/lib/haveapi/fs/components/parameter.rb +132 -0
- data/lib/haveapi/fs/components/pry.rb +9 -0
- data/lib/haveapi/fs/components/remote_control_file.rb +92 -0
- data/lib/haveapi/fs/components/resource_action_dir.rb +72 -0
- data/lib/haveapi/fs/components/resource_dir.rb +161 -0
- data/lib/haveapi/fs/components/resource_id.rb +15 -0
- data/lib/haveapi/fs/components/resource_instance_dir.rb +146 -0
- data/lib/haveapi/fs/components/rfuse_check.rb +3 -0
- data/lib/haveapi/fs/components/root.rb +75 -0
- data/lib/haveapi/fs/components/save_instance.rb +11 -0
- data/lib/haveapi/fs/components/unsaved_list.rb +24 -0
- data/lib/haveapi/fs/components/update_action_dir.rb +31 -0
- data/lib/haveapi/fs/context.rb +43 -0
- data/lib/haveapi/fs/exceptions.rb +0 -0
- data/lib/haveapi/fs/factory.rb +59 -0
- data/lib/haveapi/fs/fs.rb +198 -0
- data/lib/haveapi/fs/help.rb +91 -0
- data/lib/haveapi/fs/main.rb +134 -0
- data/lib/haveapi/fs/remote_control.rb +29 -0
- data/lib/haveapi/fs/version.rb +5 -0
- data/lib/haveapi/fs/worker.rb +77 -0
- data/lib/haveapi/fs.rb +65 -0
- data/templates/help/html/action_dir.erb +33 -0
- data/templates/help/html/action_errors.erb +4 -0
- data/templates/help/html/action_input.erb +40 -0
- data/templates/help/html/action_output.erb +21 -0
- data/templates/help/html/index_filter.erb +16 -0
- data/templates/help/html/layout.erb +45 -0
- data/templates/help/html/resource_action_dir.erb +18 -0
- data/templates/help/html/resource_dir.erb +18 -0
- data/templates/help/html/resource_instance_dir.erb +64 -0
- data/templates/help/html/root.erb +42 -0
- data/templates/help/md/action_dir.erb +29 -0
- data/templates/help/md/action_errors.erb +2 -0
- data/templates/help/md/action_input.erb +23 -0
- data/templates/help/md/action_output.erb +11 -0
- data/templates/help/md/index_filter.erb +11 -0
- data/templates/help/md/layout.erb +14 -0
- data/templates/help/md/resource_action_dir.erb +10 -0
- data/templates/help/md/resource_dir.erb +15 -0
- data/templates/help/md/resource_instance_dir.erb +42 -0
- data/templates/help/md/root.erb +34 -0
- metadata +231 -0
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
module HaveAPI::Fs
|
|
2
|
+
# The Factory is used to create instances of new components and can be used
|
|
3
|
+
# to replace specific components by different ones, thus allowing to change
|
|
4
|
+
# their behaviour.
|
|
5
|
+
class Factory
|
|
6
|
+
class << self
|
|
7
|
+
# @!attribute replacements
|
|
8
|
+
# @return [Hash] collection of all replacements
|
|
9
|
+
attr_accessor :replacements
|
|
10
|
+
|
|
11
|
+
# Replace a component class by a different class. This method has two
|
|
12
|
+
# forms. Either call it with a hash of replacements or with two arguments,
|
|
13
|
+
# where the first is the class to be replaced and the second the class
|
|
14
|
+
# to replace it with.
|
|
15
|
+
def replace(*args)
|
|
16
|
+
@replacements ||= {}
|
|
17
|
+
|
|
18
|
+
if args.size == 2
|
|
19
|
+
@replacements[args[0]] = args[1]
|
|
20
|
+
|
|
21
|
+
else
|
|
22
|
+
@replacements.update(args.first)
|
|
23
|
+
end
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
# Resolves the class for component `klass`, i.e. it checks if there is a
|
|
27
|
+
# replacement for `klass` to return instead.
|
|
28
|
+
#
|
|
29
|
+
# @return [Class]
|
|
30
|
+
def component(klass)
|
|
31
|
+
if @replacements && @replacements.has_key?(klass)
|
|
32
|
+
@replacements[klass]
|
|
33
|
+
|
|
34
|
+
else
|
|
35
|
+
klass
|
|
36
|
+
end
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
# Create a new component with `klass` and its constructor's arguments
|
|
40
|
+
# in `args`.
|
|
41
|
+
#
|
|
42
|
+
# @param [HaveAPI::Fs::Context] context
|
|
43
|
+
# @param [Symbol] name
|
|
44
|
+
# @param [Class] klass
|
|
45
|
+
# @param [Array] args
|
|
46
|
+
# @return [Component]
|
|
47
|
+
def create(context, name, klass, *args)
|
|
48
|
+
child = component(klass).new(*args)
|
|
49
|
+
c_name = klass.component || klass.name.split('::').last.underscore.to_sym
|
|
50
|
+
|
|
51
|
+
child.context = context.clone
|
|
52
|
+
child.context[c_name] = child
|
|
53
|
+
child.context.file_path << name.to_s if name
|
|
54
|
+
child.setup
|
|
55
|
+
child
|
|
56
|
+
end
|
|
57
|
+
end
|
|
58
|
+
end
|
|
59
|
+
end
|
|
@@ -0,0 +1,198 @@
|
|
|
1
|
+
require 'thread'
|
|
2
|
+
require 'rfusefs'
|
|
3
|
+
require 'haveapi/client'
|
|
4
|
+
|
|
5
|
+
module HaveAPI::Fs
|
|
6
|
+
# Interface with RFuseFS API. Methods called by RFuseFS are redirected to
|
|
7
|
+
# appropriate components.
|
|
8
|
+
class Fs
|
|
9
|
+
CHECK_FILE = FuseFS::Fuse::Root::CHECK_FILE[1..-1].to_sym
|
|
10
|
+
|
|
11
|
+
attr_reader :api
|
|
12
|
+
|
|
13
|
+
def initialize(api, opts)
|
|
14
|
+
@api = api
|
|
15
|
+
|
|
16
|
+
@mutex = Mutex.new
|
|
17
|
+
|
|
18
|
+
@path_cache = Cache.new(self)
|
|
19
|
+
@context = Context.new
|
|
20
|
+
@context.opts = opts
|
|
21
|
+
@context.url = opts[:device]
|
|
22
|
+
@context.mountpoint = ::File.realpath(opts[:mountpoint])
|
|
23
|
+
@context.cache = @path_cache
|
|
24
|
+
@context[:fs] = self
|
|
25
|
+
|
|
26
|
+
@root = Factory.create(@context, nil, Components::Root)
|
|
27
|
+
# @root.context = @context.clone
|
|
28
|
+
# @root.context[:root] = @root
|
|
29
|
+
@root.setup
|
|
30
|
+
|
|
31
|
+
Thread.abort_on_exception = true
|
|
32
|
+
@cleaner = Cleaner.new(self, @root)
|
|
33
|
+
@cleaner.start
|
|
34
|
+
@path_cache.start
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
def contents(path)
|
|
38
|
+
puts "contents"
|
|
39
|
+
p path
|
|
40
|
+
|
|
41
|
+
guard { find_component(path).contents }
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
def directory?(path)
|
|
45
|
+
puts "directory?"
|
|
46
|
+
p path
|
|
47
|
+
|
|
48
|
+
guard { find_component(path).directory? }
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
def file?(path)
|
|
52
|
+
puts "file?"
|
|
53
|
+
p path
|
|
54
|
+
|
|
55
|
+
guard {find_component(path).file? }
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
def can_read?(path)
|
|
59
|
+
puts "can_read?"
|
|
60
|
+
p path
|
|
61
|
+
|
|
62
|
+
guard { find_component(path).readable? }
|
|
63
|
+
end
|
|
64
|
+
|
|
65
|
+
def can_write?(path)
|
|
66
|
+
puts "can_write?"
|
|
67
|
+
p path
|
|
68
|
+
|
|
69
|
+
guard { find_component(path).writable? }
|
|
70
|
+
end
|
|
71
|
+
|
|
72
|
+
def executable?(path)
|
|
73
|
+
puts "executable?"
|
|
74
|
+
p path
|
|
75
|
+
|
|
76
|
+
guard { find_component(path).executable? }
|
|
77
|
+
end
|
|
78
|
+
|
|
79
|
+
def times(path)
|
|
80
|
+
puts "times"
|
|
81
|
+
p path
|
|
82
|
+
|
|
83
|
+
guard { find_component(path).times }
|
|
84
|
+
end
|
|
85
|
+
|
|
86
|
+
def size(path)
|
|
87
|
+
puts "size"
|
|
88
|
+
p path
|
|
89
|
+
|
|
90
|
+
guard { find_component(path).size }
|
|
91
|
+
end
|
|
92
|
+
|
|
93
|
+
def read_file(path)
|
|
94
|
+
puts "read_file"
|
|
95
|
+
p path
|
|
96
|
+
|
|
97
|
+
guard { find_component(path).read }
|
|
98
|
+
end
|
|
99
|
+
|
|
100
|
+
def write_to(path, str)
|
|
101
|
+
puts "write_to"
|
|
102
|
+
p path
|
|
103
|
+
|
|
104
|
+
guard { find_component(path).write(str) }
|
|
105
|
+
end
|
|
106
|
+
|
|
107
|
+
def raw_open(path, *args)
|
|
108
|
+
puts "raw_open"
|
|
109
|
+
p path
|
|
110
|
+
|
|
111
|
+
guard { find_component(path).raw_open(path, *args) }
|
|
112
|
+
end
|
|
113
|
+
|
|
114
|
+
def raw_read(path, *args)
|
|
115
|
+
puts "raw_read"
|
|
116
|
+
p path
|
|
117
|
+
|
|
118
|
+
guard { find_component(path).raw_read(path, *args) }
|
|
119
|
+
end
|
|
120
|
+
|
|
121
|
+
def raw_write(path, *args)
|
|
122
|
+
puts "raw_write"
|
|
123
|
+
p path
|
|
124
|
+
|
|
125
|
+
guard { find_component(path).raw_write(path, *args) }
|
|
126
|
+
end
|
|
127
|
+
|
|
128
|
+
def raw_sync(path, *args)
|
|
129
|
+
puts "raw_sync"
|
|
130
|
+
p path
|
|
131
|
+
|
|
132
|
+
guard { find_component(path).raw_sync(path, *args) }
|
|
133
|
+
end
|
|
134
|
+
|
|
135
|
+
def raw_truncate(path, *args)
|
|
136
|
+
puts "raw_truncate"
|
|
137
|
+
p path
|
|
138
|
+
|
|
139
|
+
guard { find_component(path).raw_truncate(path, *args) }
|
|
140
|
+
end
|
|
141
|
+
|
|
142
|
+
def raw_close(path, *args)
|
|
143
|
+
puts "raw_close"
|
|
144
|
+
p path
|
|
145
|
+
|
|
146
|
+
guard { find_component(path).raw_close(path, *args) }
|
|
147
|
+
end
|
|
148
|
+
|
|
149
|
+
def unmounted
|
|
150
|
+
puts "unmounted"
|
|
151
|
+
|
|
152
|
+
@cleaner.stop
|
|
153
|
+
@path_cache.stop
|
|
154
|
+
end
|
|
155
|
+
|
|
156
|
+
def synchronize
|
|
157
|
+
@mutex.synchronize { yield }
|
|
158
|
+
end
|
|
159
|
+
|
|
160
|
+
protected
|
|
161
|
+
def find_component(path)
|
|
162
|
+
@path_cache.get(path) do
|
|
163
|
+
t = Time.now
|
|
164
|
+
|
|
165
|
+
names = path.split('/').map { |v| v.to_sym }[1..-1]
|
|
166
|
+
tmp = @root
|
|
167
|
+
|
|
168
|
+
next(tmp) unless names
|
|
169
|
+
|
|
170
|
+
names.each do |n|
|
|
171
|
+
tmp = tmp.find(n)
|
|
172
|
+
|
|
173
|
+
if tmp.nil?
|
|
174
|
+
raise Errno::ENOENT, "'#{path}' not found"
|
|
175
|
+
|
|
176
|
+
else
|
|
177
|
+
tmp.atime = t
|
|
178
|
+
end
|
|
179
|
+
end
|
|
180
|
+
|
|
181
|
+
next(tmp)
|
|
182
|
+
end
|
|
183
|
+
end
|
|
184
|
+
|
|
185
|
+
def guard
|
|
186
|
+
synchronize { yield }
|
|
187
|
+
|
|
188
|
+
rescue => e
|
|
189
|
+
raise e if e.is_a?(::SystemCallError)
|
|
190
|
+
|
|
191
|
+
warn "Exception #{e.class}"
|
|
192
|
+
warn e.backtrace.join("\n")
|
|
193
|
+
warn e.message
|
|
194
|
+
|
|
195
|
+
raise Errno::EIO, e.message
|
|
196
|
+
end
|
|
197
|
+
end
|
|
198
|
+
end
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
module HaveAPI::Fs
|
|
2
|
+
# Included this module to a {Component} to provide help files. All components
|
|
3
|
+
# based on {Components::Directory} already have this module included.
|
|
4
|
+
module Help
|
|
5
|
+
# When searching for a help file, all directories in this list are checked.
|
|
6
|
+
# Add paths to this list for help files of third-party components.
|
|
7
|
+
SEARCH_PATH = [
|
|
8
|
+
::File.realpath(::File.join(
|
|
9
|
+
::File.dirname(__FILE__),
|
|
10
|
+
'..', '..', '..',
|
|
11
|
+
'templates',
|
|
12
|
+
'help',
|
|
13
|
+
))
|
|
14
|
+
]
|
|
15
|
+
|
|
16
|
+
module ClassMethods
|
|
17
|
+
# Specify a name of a help file for this component. By default, the class
|
|
18
|
+
# name is used.
|
|
19
|
+
# @param [Symbol, nil] name
|
|
20
|
+
# @return [Symbol] if name is nil
|
|
21
|
+
def help_file(name = nil)
|
|
22
|
+
if name
|
|
23
|
+
@help_file = name
|
|
24
|
+
|
|
25
|
+
else
|
|
26
|
+
@help_file
|
|
27
|
+
end
|
|
28
|
+
end
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
module InstanceMethods
|
|
32
|
+
protected
|
|
33
|
+
# List of help files in all formats as symbols.
|
|
34
|
+
# @return [Array<Symbol>]
|
|
35
|
+
def help_files
|
|
36
|
+
%i(html txt md man).map { |v| :"help.#{v}" }
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
# List of help files in all formats as strings.
|
|
40
|
+
# @return [Array<String>]
|
|
41
|
+
def help_contents
|
|
42
|
+
help_files.map(&:to_s)
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
# Check if `name` is a help file.
|
|
46
|
+
# @param [Symbol] name
|
|
47
|
+
def help_file?(name)
|
|
48
|
+
help_files.include?(name)
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
# @return [Components::HelpFile] the recipe for a subclass, as
|
|
52
|
+
# {Component#new_child}
|
|
53
|
+
def help_file(name)
|
|
54
|
+
format = name.to_s.split('.').last.to_sym
|
|
55
|
+
|
|
56
|
+
case format
|
|
57
|
+
when :html
|
|
58
|
+
[Components::HtmlHelpFile, self, format]
|
|
59
|
+
|
|
60
|
+
when :txt, :md
|
|
61
|
+
[Components::MdHelpFile, self, :md]
|
|
62
|
+
|
|
63
|
+
when :man
|
|
64
|
+
[Components::GroffHelpFile, self, :md]
|
|
65
|
+
|
|
66
|
+
else
|
|
67
|
+
return nil
|
|
68
|
+
end
|
|
69
|
+
end
|
|
70
|
+
end
|
|
71
|
+
|
|
72
|
+
class << self
|
|
73
|
+
def included(klass)
|
|
74
|
+
klass.send(:extend, ClassMethods)
|
|
75
|
+
klass.send(:include, InstanceMethods)
|
|
76
|
+
end
|
|
77
|
+
|
|
78
|
+
# Search for `name` in paths defined in {SEARCH_PATH}.
|
|
79
|
+
# @return [String]
|
|
80
|
+
def find!(name)
|
|
81
|
+
SEARCH_PATH.each do |s|
|
|
82
|
+
path = ::File.join(s, name)
|
|
83
|
+
|
|
84
|
+
return path if ::File.exists?(path)
|
|
85
|
+
end
|
|
86
|
+
|
|
87
|
+
raise Errno::ENOENT, "help file '#{name}' not found"
|
|
88
|
+
end
|
|
89
|
+
end
|
|
90
|
+
end
|
|
91
|
+
end
|
|
@@ -0,0 +1,134 @@
|
|
|
1
|
+
require 'uri'
|
|
2
|
+
require 'yaml'
|
|
3
|
+
|
|
4
|
+
module HaveAPI::Fs
|
|
5
|
+
# A list of accepted mount options
|
|
6
|
+
OPTIONS = %i(api version auth_method user password token nodaemonize log
|
|
7
|
+
index_limit)
|
|
8
|
+
USAGE = <<END
|
|
9
|
+
version=VERSION API version to use
|
|
10
|
+
auth_method=METHOD Authentication method (basic, token, noauth)
|
|
11
|
+
user Username
|
|
12
|
+
password Password
|
|
13
|
+
token Authentication token
|
|
14
|
+
nodaemonize Stay in the foreground
|
|
15
|
+
log Enable logging while daemonized
|
|
16
|
+
index_limit=LIMIT Limit number of objects in resource directory
|
|
17
|
+
END
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
# Every authentication provider must register using this method.
|
|
21
|
+
# @param [Symbol] name
|
|
22
|
+
# @param [Class] klass
|
|
23
|
+
def self.register_auth(name, klass)
|
|
24
|
+
@auth_methods ||= {}
|
|
25
|
+
@auth_methods[name] = klass
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
# Return authentication method based on mount options or return the default
|
|
29
|
+
# one.
|
|
30
|
+
# @param [Hash] opts mount options
|
|
31
|
+
# @param [Symbol] default name of the default authentication method
|
|
32
|
+
def self.auth_method(opts, default)
|
|
33
|
+
return @auth_methods[opts[:auth_method].to_sym] if opts[:auth_method]
|
|
34
|
+
|
|
35
|
+
@auth_methods.each_value do |m|
|
|
36
|
+
return m if m.use?(opts)
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
default ? @auth_methods[default] : @auth_methods.values.first
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
# @return [Hash] if the config exists
|
|
43
|
+
# @return [nil] if the config does not exist
|
|
44
|
+
def self.read_config
|
|
45
|
+
config_path = "#{Dir.home}/.haveapi-client.yml"
|
|
46
|
+
|
|
47
|
+
if File.exists?(config_path)
|
|
48
|
+
YAML.load_file(config_path)
|
|
49
|
+
|
|
50
|
+
else
|
|
51
|
+
nil
|
|
52
|
+
end
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
# Return configuration of a particular server from the config hash.
|
|
56
|
+
# @param [String] url URL of the API server
|
|
57
|
+
def self.server_config(url)
|
|
58
|
+
cfg = read_config
|
|
59
|
+
return nil if cfg.nil? || cfg[:servers].nil?
|
|
60
|
+
|
|
61
|
+
cfg[:servers].detect { |s| s[:url] == url }
|
|
62
|
+
end
|
|
63
|
+
|
|
64
|
+
# Perform a double-fork to make the process independent. Stdout and stderr
|
|
65
|
+
# are redirected either to a log file or to /dev/null.
|
|
66
|
+
#
|
|
67
|
+
# @param [Hash] opts mount options
|
|
68
|
+
def self.daemonize(opts)
|
|
69
|
+
home = ::File.join(Dir.home, '.haveapi-fs', URI(opts[:device]).host)
|
|
70
|
+
FileUtils.mkpath(home)
|
|
71
|
+
|
|
72
|
+
pid = Process.fork
|
|
73
|
+
|
|
74
|
+
if pid
|
|
75
|
+
exit # Parent 1
|
|
76
|
+
|
|
77
|
+
else
|
|
78
|
+
pid = Process.fork
|
|
79
|
+
exit if pid # Parent 2
|
|
80
|
+
end
|
|
81
|
+
|
|
82
|
+
# Only the child gets here
|
|
83
|
+
STDIN.close
|
|
84
|
+
|
|
85
|
+
f = File.open(
|
|
86
|
+
opts[:log] ? File.join(home, 'haveapi-fs.log') : '/dev/null',
|
|
87
|
+
'w'
|
|
88
|
+
)
|
|
89
|
+
|
|
90
|
+
STDOUT.reopen(f)
|
|
91
|
+
STDERR.reopen(f)
|
|
92
|
+
end
|
|
93
|
+
|
|
94
|
+
# Create and setup an instance of HaveAPI::Client::Client based on the mount
|
|
95
|
+
# options, calls self.daemonize if not configured otherwise.
|
|
96
|
+
#
|
|
97
|
+
# @param [Hash] opts mount options
|
|
98
|
+
# @return [HaveAPI::Client::Client]
|
|
99
|
+
def self.client(opts)
|
|
100
|
+
cfg = server_config(opts[:device])
|
|
101
|
+
client = HaveAPI::Client::Client.new(
|
|
102
|
+
opts[:device],
|
|
103
|
+
opts[:version],
|
|
104
|
+
identity: 'haveapi-fs',
|
|
105
|
+
)
|
|
106
|
+
|
|
107
|
+
auth_klass = auth_method(opts, cfg && cfg[:last_auth])
|
|
108
|
+
|
|
109
|
+
auth = auth_klass.new(
|
|
110
|
+
(cfg && cfg[:auth][auth_klass.method_name]) || {},
|
|
111
|
+
opts,
|
|
112
|
+
)
|
|
113
|
+
auth.validate
|
|
114
|
+
auth.authenticate(client)
|
|
115
|
+
|
|
116
|
+
# Fetch API description, must be done especially after authentication
|
|
117
|
+
client.setup
|
|
118
|
+
|
|
119
|
+
# Verify that authentication works
|
|
120
|
+
auth.check(client)
|
|
121
|
+
|
|
122
|
+
daemonize(opts) unless opts[:nodaemonize]
|
|
123
|
+
client
|
|
124
|
+
end
|
|
125
|
+
|
|
126
|
+
# Calls FuseFS.main with an instance of {HaveAPI::Fs::Fs}.
|
|
127
|
+
def self.main(options = OPTIONS, usage = USAGE)
|
|
128
|
+
FuseFS.main(ARGV, OPTIONS, USAGE, 'api_url') do |opts|
|
|
129
|
+
fail "provide argument 'api_url'" unless opts[:device]
|
|
130
|
+
|
|
131
|
+
HaveAPI::Fs.new(client(opts), opts)
|
|
132
|
+
end
|
|
133
|
+
end
|
|
134
|
+
end
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
module HaveAPI::Fs
|
|
2
|
+
# The purpose of this class is to handle commands received via the
|
|
3
|
+
# {Components::RemoteControlFile}.
|
|
4
|
+
class RemoteControl
|
|
5
|
+
# Call method #exec of a component at `path`.
|
|
6
|
+
#
|
|
7
|
+
# @param [Context] context
|
|
8
|
+
# @param [String] path
|
|
9
|
+
def self.execute(context, path)
|
|
10
|
+
c = context.fs.send(:find_component, path)
|
|
11
|
+
|
|
12
|
+
ret = c.exec
|
|
13
|
+
|
|
14
|
+
case ret
|
|
15
|
+
when true
|
|
16
|
+
{status: true}
|
|
17
|
+
|
|
18
|
+
when HaveAPI::Client::Response
|
|
19
|
+
{status: ret.ok?, message: ret.message, errors: ret.errors}
|
|
20
|
+
|
|
21
|
+
when HaveAPI::Client::ValidationError
|
|
22
|
+
{status: false, message: ret.message, errors: ret.errors}
|
|
23
|
+
|
|
24
|
+
else
|
|
25
|
+
{status: false, message: 'unknown response'}
|
|
26
|
+
end
|
|
27
|
+
end
|
|
28
|
+
end
|
|
29
|
+
end
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
require 'thread'
|
|
2
|
+
|
|
3
|
+
module HaveAPI::Fs
|
|
4
|
+
# Base class for classes that perform some regular work in a separate thread.
|
|
5
|
+
class Worker
|
|
6
|
+
attr_reader :runs
|
|
7
|
+
|
|
8
|
+
# @param [HaveAPI::Fs::Fs] fs
|
|
9
|
+
def initialize(fs)
|
|
10
|
+
@fs = fs
|
|
11
|
+
@run = true
|
|
12
|
+
@pipe_r, @pipe_w = IO.pipe
|
|
13
|
+
@runs = 0
|
|
14
|
+
@mutex = Mutex.new
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
# Start the work thread.
|
|
18
|
+
def start
|
|
19
|
+
@thread = Thread.new do
|
|
20
|
+
@mutex.synchronize { @next_time = Time.now + start_delay }
|
|
21
|
+
wait(start_delay)
|
|
22
|
+
|
|
23
|
+
while @run do
|
|
24
|
+
@fs.synchronize { work }
|
|
25
|
+
|
|
26
|
+
@runs += 1
|
|
27
|
+
@mutex.synchronize do
|
|
28
|
+
@last_time = Time.now
|
|
29
|
+
@next_time = @last_time + work_period
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
wait(work_period)
|
|
33
|
+
end
|
|
34
|
+
end
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
# Stop and join the work thread.
|
|
38
|
+
def stop
|
|
39
|
+
@run = false
|
|
40
|
+
@pipe_w.write('CLOSE')
|
|
41
|
+
@thread.join
|
|
42
|
+
|
|
43
|
+
@pipe_r.close
|
|
44
|
+
@pipe_w.close
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
# The time when the work method was last run.
|
|
48
|
+
def last_time
|
|
49
|
+
@mutex.synchronize { @last_time }
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
# The time when the work method will be run next.
|
|
53
|
+
def next_time
|
|
54
|
+
@mutex.synchronize { @next_time }
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
protected
|
|
58
|
+
def wait(n)
|
|
59
|
+
IO.select([@pipe_r], [], [], n)
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
# @return [Integer] number of seconds to wait before the first work
|
|
63
|
+
def start_delay
|
|
64
|
+
raise NotImplementedError
|
|
65
|
+
end
|
|
66
|
+
|
|
67
|
+
# @return [Integer] number of seconds to wait between working
|
|
68
|
+
def work_period
|
|
69
|
+
raise NotImplementedError
|
|
70
|
+
end
|
|
71
|
+
|
|
72
|
+
# This method is regularly called to perform the work.
|
|
73
|
+
def work
|
|
74
|
+
raise NotImplementedError
|
|
75
|
+
end
|
|
76
|
+
end
|
|
77
|
+
end
|
data/lib/haveapi/fs.rb
ADDED
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
module HaveAPI
|
|
2
|
+
module Fs
|
|
3
|
+
module Auth ; end
|
|
4
|
+
|
|
5
|
+
def self.new(*args)
|
|
6
|
+
HaveAPI::Fs::Fs.new(*args)
|
|
7
|
+
end
|
|
8
|
+
end
|
|
9
|
+
end
|
|
10
|
+
|
|
11
|
+
require_relative '../core_ext/string'
|
|
12
|
+
require_relative 'fs/main'
|
|
13
|
+
require_relative 'fs/fs'
|
|
14
|
+
require_relative 'fs/context'
|
|
15
|
+
require_relative 'fs/worker'
|
|
16
|
+
require_relative 'fs/cleaner'
|
|
17
|
+
require_relative 'fs/cache'
|
|
18
|
+
require_relative 'fs/component'
|
|
19
|
+
require_relative 'fs/factory'
|
|
20
|
+
require_relative 'fs/help'
|
|
21
|
+
require_relative 'fs/remote_control'
|
|
22
|
+
require_relative 'fs/version'
|
|
23
|
+
require_relative 'fs/auth/base'
|
|
24
|
+
require_relative 'fs/auth/basic'
|
|
25
|
+
require_relative 'fs/auth/token'
|
|
26
|
+
require_relative 'fs/auth/noauth'
|
|
27
|
+
require_relative 'fs/components/directory'
|
|
28
|
+
require_relative 'fs/components/file'
|
|
29
|
+
require_relative 'fs/components/executable'
|
|
30
|
+
require_relative 'fs/components/remote_control_file'
|
|
31
|
+
require_relative 'fs/components/directory_reset'
|
|
32
|
+
require_relative 'fs/components/root'
|
|
33
|
+
require_relative 'fs/components/resource_dir'
|
|
34
|
+
require_relative 'fs/components/index_filter'
|
|
35
|
+
require_relative 'fs/components/resource_instance_dir'
|
|
36
|
+
require_relative 'fs/components/save_instance'
|
|
37
|
+
require_relative 'fs/components/resource_action_dir'
|
|
38
|
+
require_relative 'fs/components/action_dir'
|
|
39
|
+
require_relative 'fs/components/create_action_dir'
|
|
40
|
+
require_relative 'fs/components/delete_action_dir'
|
|
41
|
+
require_relative 'fs/components/update_action_dir'
|
|
42
|
+
require_relative 'fs/components/action_input'
|
|
43
|
+
require_relative 'fs/components/action_output'
|
|
44
|
+
require_relative 'fs/components/list_item'
|
|
45
|
+
require_relative 'fs/components/action_status'
|
|
46
|
+
require_relative 'fs/components/action_message'
|
|
47
|
+
require_relative 'fs/components/action_errors'
|
|
48
|
+
require_relative 'fs/components/action_exec'
|
|
49
|
+
require_relative 'fs/components/action_exec_edit'
|
|
50
|
+
require_relative 'fs/components/parameter'
|
|
51
|
+
require_relative 'fs/components/resource_id'
|
|
52
|
+
require_relative 'fs/components/help_file'
|
|
53
|
+
require_relative 'fs/components/html_help_file'
|
|
54
|
+
require_relative 'fs/components/md_help_file'
|
|
55
|
+
require_relative 'fs/components/groff_help_file'
|
|
56
|
+
require_relative 'fs/components/instance_create'
|
|
57
|
+
require_relative 'fs/components/instance_edit'
|
|
58
|
+
require_relative 'fs/components/unsaved_list'
|
|
59
|
+
require_relative 'fs/components/component_list'
|
|
60
|
+
require_relative 'fs/components/meta_dir'
|
|
61
|
+
require_relative 'fs/components/meta_file'
|
|
62
|
+
require_relative 'fs/components/info_files'
|
|
63
|
+
require_relative 'fs/components/cache_stats'
|
|
64
|
+
require_relative 'fs/components/pry'
|
|
65
|
+
require_relative 'fs/components/rfuse_check'
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
<p><%= @c.action.description %></p>
|
|
2
|
+
<h2>Contents</h2>
|
|
3
|
+
<p>
|
|
4
|
+
This directory is used to prepare input parameters, execute the action and retrieve
|
|
5
|
+
output parameters.
|
|
6
|
+
</p>
|
|
7
|
+
<dl>
|
|
8
|
+
<dt><a href="input/help.html">input/</a></dt>
|
|
9
|
+
<dd>Stage area for input parameters.</dd>
|
|
10
|
+
|
|
11
|
+
<dt><a href="output/help.html">output/</a></dt>
|
|
12
|
+
<dd>Contains output parameters after the action was executed.</dd>
|
|
13
|
+
|
|
14
|
+
<dt>exec</dt>
|
|
15
|
+
<dd>Write <code>1</code> to this file to execute the action.</dd>
|
|
16
|
+
|
|
17
|
+
<dt>exec.yml</dt>
|
|
18
|
+
<dd>Editable YAML file with input parameters. The action is executed when the file
|
|
19
|
+
is saved and closed.</dd>
|
|
20
|
+
|
|
21
|
+
<dt>status</dt>
|
|
22
|
+
<dd>Read action status. <code>1</code> if successful, <code>0</code> if not, empty
|
|
23
|
+
if not yet run.</dd>
|
|
24
|
+
|
|
25
|
+
<dt>message</dt>
|
|
26
|
+
<dd>Error message returned from the API.</dd>
|
|
27
|
+
|
|
28
|
+
<dt><a href="errors/help.html">errors/</a></dt>
|
|
29
|
+
<dd>List of parameter errors.</dd>
|
|
30
|
+
|
|
31
|
+
<dt>reset</dt>
|
|
32
|
+
<dd>Reset input parameters.</dd>
|
|
33
|
+
</dl>
|