store_agent 1.0.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.
Files changed (52) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +14 -0
  3. data/.rspec +3 -0
  4. data/Gemfile +4 -0
  5. data/Guardfile +5 -0
  6. data/LICENSE +202 -0
  7. data/README.md +433 -0
  8. data/Rakefile +2 -0
  9. data/lib/store_agent.rb +31 -0
  10. data/lib/store_agent/config.rb +152 -0
  11. data/lib/store_agent/data_encoder.rb +32 -0
  12. data/lib/store_agent/data_encoder/gzip_encoder.rb +50 -0
  13. data/lib/store_agent/data_encoder/openssl_aes_256_cbc_encoder.rb +65 -0
  14. data/lib/store_agent/exceptions.rb +87 -0
  15. data/lib/store_agent/node.rb +26 -0
  16. data/lib/store_agent/node/attachment.rb +93 -0
  17. data/lib/store_agent/node/attachment/metadata.rb +120 -0
  18. data/lib/store_agent/node/attachment/permission.rb +121 -0
  19. data/lib/store_agent/node/object.rb +233 -0
  20. data/lib/store_agent/node/object/directory_object.rb +264 -0
  21. data/lib/store_agent/node/object/file_object.rb +197 -0
  22. data/lib/store_agent/node/object/virtual_object.rb +25 -0
  23. data/lib/store_agent/node/prepend_module/locker.rb +125 -0
  24. data/lib/store_agent/node/prepend_module/path_validator.rb +138 -0
  25. data/lib/store_agent/node/prepend_module/permission_checker.rb +96 -0
  26. data/lib/store_agent/user.rb +111 -0
  27. data/lib/store_agent/validator.rb +60 -0
  28. data/lib/store_agent/version.rb +19 -0
  29. data/lib/store_agent/version_manager.rb +101 -0
  30. data/lib/store_agent/version_manager/ruby_git.rb +100 -0
  31. data/lib/store_agent/version_manager/rugged_git.rb +133 -0
  32. data/lib/store_agent/workspace.rb +88 -0
  33. data/spec/spec_helper.rb +47 -0
  34. data/spec/store_agent/data_encoder/encoder_shared_context.rb +74 -0
  35. data/spec/store_agent/data_encoder/gzip_encoder_spec.rb +41 -0
  36. data/spec/store_agent/data_encoder/openssl_aes_256_cbc_encoder_spec.rb +42 -0
  37. data/spec/store_agent/data_encoder_spec.rb +78 -0
  38. data/spec/store_agent/node/directory_object_spec.rb +563 -0
  39. data/spec/store_agent/node/file_object_spec.rb +379 -0
  40. data/spec/store_agent/node/locker_spec.rb +191 -0
  41. data/spec/store_agent/node/metadata_spec.rb +339 -0
  42. data/spec/store_agent/node/object_spec.rb +73 -0
  43. data/spec/store_agent/node/path_validator_spec.rb +121 -0
  44. data/spec/store_agent/node/permission_spec.rb +232 -0
  45. data/spec/store_agent/user_spec.rb +127 -0
  46. data/spec/store_agent/version_manager/git_shared_context.rb +286 -0
  47. data/spec/store_agent/version_manager/ruby_git_spec.rb +32 -0
  48. data/spec/store_agent/version_manager/rugged_git_spec.rb +32 -0
  49. data/spec/store_agent/workspace_spec.rb +107 -0
  50. data/spec/store_agent_spec.rb +53 -0
  51. data/store_agent.gemspec +33 -0
  52. metadata +252 -0
@@ -0,0 +1,264 @@
1
+ #--
2
+ # Copyright 2015 realglobe, 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
+ module StoreAgent
18
+ module Node
19
+ # ディレクトリ
20
+ class DirectoryObject < Object
21
+ def initialize(params) # :nodoc:
22
+ super
23
+ if !@path.end_with?("/")
24
+ @path = "#{@path}/"
25
+ end
26
+ end
27
+
28
+ def create
29
+ super do
30
+ if block_given?
31
+ yield self
32
+ end
33
+ FileUtils.mkdir(storage_object_path)
34
+ workspace.version_manager.add("#{storage_object_path}.keep")
35
+ end
36
+ end
37
+
38
+ # TODO
39
+ def read(revision: nil)
40
+ super do
41
+ filenames =
42
+ if revision.nil?
43
+ current_children_filenames
44
+ else
45
+ workspace.version_manager.read(path: storage_object_path, revision: revision)
46
+ end
47
+ filenames - StoreAgent.reserved_filenames
48
+ end
49
+ end
50
+
51
+ def update
52
+ raise "cannot update directory"
53
+ end
54
+
55
+ def delete(*)
56
+ super do
57
+ success, errors = call_for_children do |child|
58
+ child.delete(recursive: false)
59
+ end
60
+ if success
61
+ FileUtils.remove_dir(storage_object_path)
62
+ else
63
+ raise StoreAgent::PermissionDeniedError.new(errors: errors)
64
+ end
65
+ workspace.version_manager.remove(storage_object_path, directory: true)
66
+ end
67
+ end
68
+
69
+ def touch(*, recursive: false)
70
+ super do
71
+ FileUtils.touch("#{storage_object_path}.keep")
72
+ workspace.version_manager.add("#{storage_object_path}.keep")
73
+ if recursive
74
+ success, errors = call_for_children do |child|
75
+ child.touch(recursive: true)
76
+ end
77
+ end
78
+ end
79
+ end
80
+
81
+ def copy(dest_path = nil, *)
82
+ super do
83
+ dest_directory = build_dest_directory(dest_path).create
84
+ success, errors = call_for_children do |child|
85
+ child.copy("#{dest_directory.path}#{File.basename(child.path)}")
86
+ end
87
+ end
88
+ end
89
+
90
+ def move(dest_path = nil, *)
91
+ super do
92
+ dest_directory = build_dest_directory(dest_path)
93
+ disk_usage = metadata.disk_usage
94
+ file_count = directory_file_count
95
+ FileUtils.mv(storage_object_path, dest_directory.storage_object_path)
96
+ FileUtils.mv(metadata.base_path, dest_directory.metadata.base_path)
97
+ FileUtils.mv(permission.base_path, dest_directory.permission.base_path)
98
+ dest_directory.touch(recursive: true)
99
+ dest_directory.parent_directory.metadata.update(disk_usage: disk_usage, directory_file_count: 1, tree_file_count: file_count + 1, recursive: true)
100
+ parent_directory.metadata.update(disk_usage: -disk_usage, directory_file_count: -1, tree_file_count: -(file_count + 1), recursive: true)
101
+
102
+ [storage_object_path, metadata.base_path, permission.base_path].each do |dir_path|
103
+ workspace.version_manager.remove(dir_path, directory: true)
104
+ end
105
+ end
106
+ end
107
+
108
+ def get_metadata(*) # :nodoc:
109
+ super do
110
+ end
111
+ end
112
+
113
+ def get_permissions(*) # :nodoc:
114
+ super do
115
+ end
116
+ end
117
+
118
+ def chown(*, identifier: nil, recursive: false)
119
+ super do
120
+ if recursive
121
+ success, errors = call_for_children do |child|
122
+ child.chown(identifier: identifier, recursive: recursive)
123
+ end
124
+ end
125
+ end
126
+ end
127
+
128
+ def set_permission(identifier: nil, permission_values: {}, recursive: false)
129
+ super do
130
+ if recursive
131
+ success, errors = call_for_children do |child|
132
+ child.set_permission(identifier: identifier, permission_values: permission_values)
133
+ end
134
+ end
135
+ end
136
+ end
137
+
138
+ def unset_permission(identifier: nil, permission_names: [], recursive: false)
139
+ super do
140
+ if recursive
141
+ success, errors = call_for_children do |child|
142
+ child.unset_permission(identifier: identifier, permission_names: permission_names)
143
+ end
144
+ end
145
+ end
146
+ end
147
+
148
+ # 引数を現在のパスからの相対パスとして解釈し、オブジェクトのインスタンスを返す
149
+ def find_object(path)
150
+ object = StoreAgent::Node::Object.new(workspace: workspace, path: namespaced_absolute_path(path))
151
+ case object.exists? && object.filetype
152
+ when false
153
+ virtual(path)
154
+ when "directory"
155
+ directory(path)
156
+ when "file"
157
+ file(path)
158
+ else
159
+ raise "unknown filetype"
160
+ end
161
+ end
162
+
163
+ def virtual(path) # :nodoc:
164
+ StoreAgent::Node::VirtualObject.new(workspace: workspace, path: namespaced_absolute_path(path))
165
+ end
166
+
167
+ # 現在のパスからの相対パスで、ディレクトリオブジェクトのインスタンスを返す
168
+ def directory(path)
169
+ StoreAgent::Node::DirectoryObject.new(workspace: workspace, path: namespaced_absolute_path(path))
170
+ end
171
+
172
+ # 現在のパスからの相対パスで、ファイルオブジェクトのインスタンスを返す
173
+ def file(path)
174
+ StoreAgent::Node::FileObject.new(workspace: workspace, path: namespaced_absolute_path(path))
175
+ end
176
+
177
+ # 現在のディレクトリの直下にあるオブジェクトの一覧を返す
178
+ def children
179
+ (current_children_filenames - StoreAgent.reserved_filenames).map{|filename|
180
+ find_object(filename)
181
+ }
182
+ end
183
+
184
+ def default_metadata # :nodoc:
185
+ super.merge(directory_metadata)
186
+ end
187
+
188
+ def directory_metadata # :nodoc:
189
+ {
190
+ "is_dir" => true,
191
+ "directory_size" => StoreAgent::Node::Metadata.datasize_format(initial_bytesize),
192
+ "directory_bytes" => initial_bytesize,
193
+ "directory_size_limit" => StoreAgent::Node::Metadata.datasize_format(StoreAgent.config.default_directory_bytesize_limit),
194
+ "directory_bytes_limit" => StoreAgent.config.default_directory_bytesize_limit,
195
+ "directory_file_count" => 0,
196
+ "tree_file_count" => 0
197
+ }
198
+ end
199
+
200
+ # ディレクトリ直下にあるファイル数
201
+ def directory_file_count
202
+ metadata["directory_file_count"]
203
+ end
204
+
205
+ # ディレクトリ以下のツリー全体でのファイル数
206
+ def tree_file_count
207
+ metadata["tree_file_count"]
208
+ end
209
+
210
+ # TODO auth
211
+ def update_limit=(limit)
212
+ metadata["directory_bytes_limit"] = limit
213
+ end
214
+
215
+ def directory? # :nodoc:
216
+ true
217
+ end
218
+
219
+ private
220
+
221
+ def namespaced_absolute_path(path)
222
+ "#{@path}#{sanitize_path(path)}"
223
+ end
224
+
225
+ def initial_bytesize
226
+ File.size(storage_object_path)
227
+ end
228
+
229
+ def build_dest_directory(dest_path)
230
+ dest_object = workspace.find_object(dest_path)
231
+ case
232
+ when dest_object.file?
233
+ raise InvalidNodeTypeError.new(src_object: self, dest_object: dest_object)
234
+ when dest_object.directory?
235
+ sub_directory_object = dest_object.find_object(File.basename(path))
236
+ if sub_directory_object.exists?
237
+ raise InvalidPathError, "object already exists: #{sub_directory_object.path}"
238
+ end
239
+ dest_object.directory(File.basename(path))
240
+ else
241
+ workspace.directory(dest_path)
242
+ end
243
+ end
244
+
245
+ def current_children_filenames
246
+ FileUtils.cd(storage_object_path) do
247
+ return Dir.glob("*", File::FNM_DOTMATCH)
248
+ end
249
+ end
250
+
251
+ def call_for_children
252
+ errors = []
253
+ children.each do |child|
254
+ begin
255
+ yield child
256
+ rescue StoreAgent::PermissionDeniedError => e
257
+ errors << e
258
+ end
259
+ end
260
+ return errors.empty?, errors
261
+ end
262
+ end
263
+ end
264
+ end
@@ -0,0 +1,197 @@
1
+ #--
2
+ # Copyright 2015 realglobe, 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
+ module StoreAgent
18
+ module Node
19
+ # ファイル
20
+ class FileObject < Object
21
+ attr_writer :body
22
+
23
+ def create(*params, &block)
24
+ super do
25
+ set_body(*params, &block)
26
+ save
27
+ workspace.version_manager.add(storage_object_path)
28
+ end
29
+ end
30
+
31
+ def read(revision: nil)
32
+ super do
33
+ if revision.nil?
34
+ encoded_data = open(storage_object_path) do |f|
35
+ f.read
36
+ end
37
+ else
38
+ encoded_data = workspace.version_manager.read(path: storage_object_path, revision: revision)
39
+ end
40
+ StoreAgent.config.attachment_data_encoders.reverse.inject(encoded_data) do |data, encoder|
41
+ encoder.decode(data)
42
+ end
43
+ end
44
+ end
45
+
46
+ def update(*params, &block)
47
+ super do
48
+ set_body(*params, &block)
49
+ if @body.nil?
50
+ raise "file body required"
51
+ end
52
+ save
53
+ disk_usage_diff = @body.length - metadata.disk_usage
54
+ metadata.update(disk_usage: disk_usage_diff, recursive: true)
55
+ workspace.version_manager.add(storage_object_path)
56
+ end
57
+ end
58
+
59
+ def delete(*)
60
+ super do
61
+ FileUtils.rm(storage_object_path)
62
+ workspace.version_manager.remove(storage_object_path)
63
+ end
64
+ end
65
+
66
+ def touch(*)
67
+ super do
68
+ FileUtils.touch(storage_object_path)
69
+ workspace.version_manager.add(storage_object_path)
70
+ end
71
+ end
72
+
73
+ def copy(dest_path = nil, *)
74
+ super do
75
+ file_body = read
76
+ dest_file = build_dest_file(dest_path)
77
+ if dest_file.exists?
78
+ dest_file.update(file_body)
79
+ else
80
+ dest_file.create(read)
81
+ end
82
+ end
83
+ end
84
+
85
+ def move(dest_path = nil, *)
86
+ super do
87
+ dest_file = build_dest_file(dest_path)
88
+ if dest_file.exists?
89
+ disk_usage_diff = metadata.disk_usage - dest_file.metadata.disk_usage
90
+ file_count = 0
91
+ else
92
+ disk_usage_diff = metadata.disk_usage
93
+ file_count = 1
94
+ end
95
+ FileUtils.mv(storage_object_path, dest_file.storage_object_path)
96
+ FileUtils.mv(metadata.file_path, dest_file.metadata.file_path)
97
+ FileUtils.mv(permission.file_path, dest_file.permission.file_path)
98
+ dest_file.touch
99
+ dest_file.parent_directory.metadata.update(disk_usage: disk_usage_diff, directory_file_count: file_count, tree_file_count: file_count, recursive: true)
100
+ parent_directory.metadata.update(disk_usage: -dest_file.metadata.disk_usage, directory_file_count: -1, tree_file_count: -1, recursive: true)
101
+
102
+ [storage_object_path, metadata.file_path, permission.file_path].each do |file_path|
103
+ workspace.version_manager.remove(file_path)
104
+ end
105
+ end
106
+ end
107
+
108
+ def get_metadata(*) # :nodoc:
109
+ super do
110
+ end
111
+ end
112
+
113
+ def get_permissions(*) # :nodoc:
114
+ super do
115
+ end
116
+ end
117
+
118
+ def chown(*)
119
+ super do
120
+ end
121
+ end
122
+
123
+ def set_permission(*)
124
+ super do
125
+ end
126
+ end
127
+
128
+ def unset_permission(*)
129
+ super do
130
+ end
131
+ end
132
+
133
+ def find_object(_) # :nodoc:
134
+ raise "#{@path} is not directory"
135
+ end
136
+
137
+ def directory(_) # :nodoc:
138
+ raise "#{@path} is not directory"
139
+ end
140
+
141
+ def file(_) # :nodoc:
142
+ raise "#{@path} is not directory"
143
+ end
144
+
145
+ def save
146
+ encoded_data = StoreAgent.config.storage_data_encoders.inject(@body) do |data, encoder|
147
+ encoder.encode(data)
148
+ end
149
+ open(storage_object_path, "w") do |f|
150
+ f.write encoded_data
151
+ end
152
+ end
153
+
154
+ def children # :nodoc:
155
+ []
156
+ end
157
+
158
+ def file? # :nodoc:
159
+ true
160
+ end
161
+
162
+ private
163
+
164
+ def initial_bytesize
165
+ (@body || "").size
166
+ end
167
+
168
+ def set_body(*params)
169
+ case
170
+ when block_given?
171
+ yield self
172
+ when (options = params.first).is_a?(String)
173
+ @body = options
174
+ when options.is_a?(Symbol)
175
+ @body = options.to_s
176
+ when options.is_a?(Hash)
177
+ @body = options["body"] || options[:body]
178
+ else
179
+ @body = nil
180
+ end
181
+ end
182
+
183
+ def build_dest_file(dest_path)
184
+ dest_object = workspace.find_object(dest_path)
185
+ if dest_object.directory?
186
+ sub_directory_object = dest_object.find_object(File.basename(path))
187
+ if sub_directory_object.directory?
188
+ raise InvalidNodeTypeError.new(src_object: self, dest_object: sub_directory_object)
189
+ end
190
+ dest_object.file(File.basename(path))
191
+ else
192
+ workspace.file(dest_path)
193
+ end
194
+ end
195
+ end
196
+ end
197
+ end