haveapi-fs 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (89) hide show
  1. checksums.yaml +7 -0
  2. data/.yardopts +1 -0
  3. data/CHANGELOG +2 -0
  4. data/Gemfile +3 -0
  5. data/LICENSE.txt +21 -0
  6. data/README.md +338 -0
  7. data/Rakefile +1 -0
  8. data/assets/css/bootstrap.min.css +6 -0
  9. data/assets/css/local.css +11 -0
  10. data/bin/haveapi-fs +5 -0
  11. data/haveapi-fs.gemspec +30 -0
  12. data/lib/core_ext/string.rb +9 -0
  13. data/lib/haveapi/fs/auth/base.rb +56 -0
  14. data/lib/haveapi/fs/auth/basic.rb +29 -0
  15. data/lib/haveapi/fs/auth/noauth.rb +9 -0
  16. data/lib/haveapi/fs/auth/token.rb +39 -0
  17. data/lib/haveapi/fs/cache.rb +71 -0
  18. data/lib/haveapi/fs/cleaner.rb +56 -0
  19. data/lib/haveapi/fs/component.rb +237 -0
  20. data/lib/haveapi/fs/components/action_dir.rb +106 -0
  21. data/lib/haveapi/fs/components/action_errors.rb +49 -0
  22. data/lib/haveapi/fs/components/action_exec.rb +12 -0
  23. data/lib/haveapi/fs/components/action_exec_edit.rb +90 -0
  24. data/lib/haveapi/fs/components/action_input.rb +40 -0
  25. data/lib/haveapi/fs/components/action_message.rb +19 -0
  26. data/lib/haveapi/fs/components/action_output.rb +79 -0
  27. data/lib/haveapi/fs/components/action_status.rb +32 -0
  28. data/lib/haveapi/fs/components/cache_stats.rb +19 -0
  29. data/lib/haveapi/fs/components/component_list.rb +24 -0
  30. data/lib/haveapi/fs/components/create_action_dir.rb +15 -0
  31. data/lib/haveapi/fs/components/delete_action_dir.rb +22 -0
  32. data/lib/haveapi/fs/components/directory.rb +45 -0
  33. data/lib/haveapi/fs/components/directory_reset.rb +8 -0
  34. data/lib/haveapi/fs/components/executable.rb +75 -0
  35. data/lib/haveapi/fs/components/file.rb +43 -0
  36. data/lib/haveapi/fs/components/groff_help_file.rb +9 -0
  37. data/lib/haveapi/fs/components/help_file.rb +28 -0
  38. data/lib/haveapi/fs/components/html_help_file.rb +24 -0
  39. data/lib/haveapi/fs/components/index_filter.rb +63 -0
  40. data/lib/haveapi/fs/components/info_files.rb +19 -0
  41. data/lib/haveapi/fs/components/instance_create.rb +20 -0
  42. data/lib/haveapi/fs/components/instance_edit.rb +49 -0
  43. data/lib/haveapi/fs/components/list_item.rb +28 -0
  44. data/lib/haveapi/fs/components/md_help_file.rb +24 -0
  45. data/lib/haveapi/fs/components/meta_dir.rb +42 -0
  46. data/lib/haveapi/fs/components/meta_file.rb +21 -0
  47. data/lib/haveapi/fs/components/parameter.rb +132 -0
  48. data/lib/haveapi/fs/components/pry.rb +9 -0
  49. data/lib/haveapi/fs/components/remote_control_file.rb +92 -0
  50. data/lib/haveapi/fs/components/resource_action_dir.rb +72 -0
  51. data/lib/haveapi/fs/components/resource_dir.rb +161 -0
  52. data/lib/haveapi/fs/components/resource_id.rb +15 -0
  53. data/lib/haveapi/fs/components/resource_instance_dir.rb +146 -0
  54. data/lib/haveapi/fs/components/rfuse_check.rb +3 -0
  55. data/lib/haveapi/fs/components/root.rb +75 -0
  56. data/lib/haveapi/fs/components/save_instance.rb +11 -0
  57. data/lib/haveapi/fs/components/unsaved_list.rb +24 -0
  58. data/lib/haveapi/fs/components/update_action_dir.rb +31 -0
  59. data/lib/haveapi/fs/context.rb +43 -0
  60. data/lib/haveapi/fs/exceptions.rb +0 -0
  61. data/lib/haveapi/fs/factory.rb +59 -0
  62. data/lib/haveapi/fs/fs.rb +198 -0
  63. data/lib/haveapi/fs/help.rb +91 -0
  64. data/lib/haveapi/fs/main.rb +134 -0
  65. data/lib/haveapi/fs/remote_control.rb +29 -0
  66. data/lib/haveapi/fs/version.rb +5 -0
  67. data/lib/haveapi/fs/worker.rb +77 -0
  68. data/lib/haveapi/fs.rb +65 -0
  69. data/templates/help/html/action_dir.erb +33 -0
  70. data/templates/help/html/action_errors.erb +4 -0
  71. data/templates/help/html/action_input.erb +40 -0
  72. data/templates/help/html/action_output.erb +21 -0
  73. data/templates/help/html/index_filter.erb +16 -0
  74. data/templates/help/html/layout.erb +45 -0
  75. data/templates/help/html/resource_action_dir.erb +18 -0
  76. data/templates/help/html/resource_dir.erb +18 -0
  77. data/templates/help/html/resource_instance_dir.erb +64 -0
  78. data/templates/help/html/root.erb +42 -0
  79. data/templates/help/md/action_dir.erb +29 -0
  80. data/templates/help/md/action_errors.erb +2 -0
  81. data/templates/help/md/action_input.erb +23 -0
  82. data/templates/help/md/action_output.erb +11 -0
  83. data/templates/help/md/index_filter.erb +11 -0
  84. data/templates/help/md/layout.erb +14 -0
  85. data/templates/help/md/resource_action_dir.erb +10 -0
  86. data/templates/help/md/resource_dir.erb +15 -0
  87. data/templates/help/md/resource_instance_dir.erb +42 -0
  88. data/templates/help/md/root.erb +34 -0
  89. 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,5 @@
1
+ module HaveAPI
2
+ module Fs
3
+ VERSION = '0.1.0'
4
+ end
5
+ 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>
@@ -0,0 +1,4 @@
1
+ <p>
2
+ This directory contains a list of errors for every parameter, where a parameter
3
+ is represented by a file, one error per line.
4
+ </p>