safedb 0.01.0001
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/.gitignore +8 -0
- data/.yardopts +3 -0
- data/Gemfile +10 -0
- data/LICENSE +21 -0
- data/README.md +793 -0
- data/Rakefile +16 -0
- data/bin/safe +5 -0
- data/lib/configs/README.md +58 -0
- data/lib/extension/array.rb +162 -0
- data/lib/extension/dir.rb +35 -0
- data/lib/extension/file.rb +123 -0
- data/lib/extension/hash.rb +33 -0
- data/lib/extension/string.rb +572 -0
- data/lib/factbase/facts.safedb.net.ini +38 -0
- data/lib/interprete.rb +462 -0
- data/lib/keytools/PRODUCE_RAND_SEQ_USING_DEV_URANDOM.txt +0 -0
- data/lib/keytools/kdf.api.rb +243 -0
- data/lib/keytools/kdf.bcrypt.rb +265 -0
- data/lib/keytools/kdf.pbkdf2.rb +262 -0
- data/lib/keytools/kdf.scrypt.rb +190 -0
- data/lib/keytools/key.64.rb +326 -0
- data/lib/keytools/key.algo.rb +109 -0
- data/lib/keytools/key.api.rb +1391 -0
- data/lib/keytools/key.db.rb +330 -0
- data/lib/keytools/key.docs.rb +195 -0
- data/lib/keytools/key.error.rb +110 -0
- data/lib/keytools/key.id.rb +271 -0
- data/lib/keytools/key.ident.rb +243 -0
- data/lib/keytools/key.iv.rb +107 -0
- data/lib/keytools/key.local.rb +259 -0
- data/lib/keytools/key.now.rb +402 -0
- data/lib/keytools/key.pair.rb +259 -0
- data/lib/keytools/key.pass.rb +120 -0
- data/lib/keytools/key.rb +585 -0
- data/lib/logging/gem.logging.rb +132 -0
- data/lib/modules/README.md +43 -0
- data/lib/modules/cryptology/aes-256.rb +154 -0
- data/lib/modules/cryptology/amalgam.rb +70 -0
- data/lib/modules/cryptology/blowfish.rb +130 -0
- data/lib/modules/cryptology/cipher.rb +207 -0
- data/lib/modules/cryptology/collect.rb +138 -0
- data/lib/modules/cryptology/crypt.io.rb +225 -0
- data/lib/modules/cryptology/engineer.rb +99 -0
- data/lib/modules/mappers/dictionary.rb +288 -0
- data/lib/modules/storage/coldstore.rb +186 -0
- data/lib/modules/storage/git.store.rb +399 -0
- data/lib/session/fact.finder.rb +334 -0
- data/lib/session/require.gem.rb +112 -0
- data/lib/session/time.stamp.rb +340 -0
- data/lib/session/user.home.rb +49 -0
- data/lib/usecase/cmd.rb +487 -0
- data/lib/usecase/config/README.md +57 -0
- data/lib/usecase/docker/README.md +146 -0
- data/lib/usecase/docker/docker.rb +49 -0
- data/lib/usecase/edit/README.md +43 -0
- data/lib/usecase/edit/delete.rb +46 -0
- data/lib/usecase/export.rb +40 -0
- data/lib/usecase/files/README.md +37 -0
- data/lib/usecase/files/eject.rb +56 -0
- data/lib/usecase/files/file_me.rb +78 -0
- data/lib/usecase/files/read.rb +169 -0
- data/lib/usecase/files/write.rb +89 -0
- data/lib/usecase/goto.rb +57 -0
- data/lib/usecase/id.rb +36 -0
- data/lib/usecase/import.rb +157 -0
- data/lib/usecase/init.rb +63 -0
- data/lib/usecase/jenkins/README.md +146 -0
- data/lib/usecase/jenkins/jenkins.rb +208 -0
- data/lib/usecase/login.rb +71 -0
- data/lib/usecase/logout.rb +28 -0
- data/lib/usecase/open.rb +71 -0
- data/lib/usecase/print.rb +40 -0
- data/lib/usecase/put.rb +81 -0
- data/lib/usecase/set.rb +44 -0
- data/lib/usecase/show.rb +138 -0
- data/lib/usecase/terraform/README.md +91 -0
- data/lib/usecase/terraform/terraform.rb +121 -0
- data/lib/usecase/token.rb +35 -0
- data/lib/usecase/update/README.md +55 -0
- data/lib/usecase/update/rename.rb +180 -0
- data/lib/usecase/use.rb +41 -0
- data/lib/usecase/verse.rb +20 -0
- data/lib/usecase/view.rb +71 -0
- data/lib/usecase/vpn/README.md +150 -0
- data/lib/usecase/vpn/vpn.ini +31 -0
- data/lib/usecase/vpn/vpn.rb +54 -0
- data/lib/version.rb +3 -0
- data/safedb.gemspec +34 -0
- metadata +193 -0
@@ -0,0 +1,288 @@
|
|
1
|
+
#!/usr/bin/ruby
|
2
|
+
# coding: utf-8
|
3
|
+
|
4
|
+
module SafeDb
|
5
|
+
|
6
|
+
require 'inifile'
|
7
|
+
|
8
|
+
# An OpenSession dictionary is a <b>2D (two dimensional) hash</b> data
|
9
|
+
# structure backed by an encrypted file.
|
10
|
+
#
|
11
|
+
# It supports operations to <b>read from</b> and <b>write to</b> a known
|
12
|
+
# filepath and given the correct symmetric encryption key it will
|
13
|
+
#
|
14
|
+
# - decrypt <b>after reading from</b> the file and
|
15
|
+
# - encrypt <b>before writing to</b> the file
|
16
|
+
#
|
17
|
+
# This dictionary extends {Hash} in order to deliver on its core key value
|
18
|
+
# storage and retrieve use cases. Extend this dictionary and provide
|
19
|
+
# context specific methods through constants to read and write context
|
20
|
+
# specific data.
|
21
|
+
#
|
22
|
+
# == The <em>Current</em> Dictionary Section
|
23
|
+
#
|
24
|
+
# This Dictionary is <b>two-dimensional</b> so all key-value pairs are stored
|
25
|
+
# under the auspices of a section.
|
26
|
+
#
|
27
|
+
# The Dictionary can track the <b>current section</b> for you and all data
|
28
|
+
# exchanges can occur in lieu of a single section if you so wish by using
|
29
|
+
# the provided {put} and {get} methods.
|
30
|
+
#
|
31
|
+
# To employ section management functionality you should pass in a current
|
32
|
+
# <b>section id</b> when creating the dictionary.
|
33
|
+
#
|
34
|
+
# @example
|
35
|
+
# To use the dictionary in the raw (unextended) format you create
|
36
|
+
# write and read it like this.
|
37
|
+
#
|
38
|
+
# ----------------------------------------------------------------------
|
39
|
+
#
|
40
|
+
# my_dictionary = Dictionary.create( "/path/to/backing/file" )
|
41
|
+
#
|
42
|
+
# my_dictionary["user23"] = {}
|
43
|
+
# my_dictionary["user23"]["Name"] = "Joe Bloggs"
|
44
|
+
# my_dictionary["user23"]["Email"] = "joebloggs@example.com"
|
45
|
+
# my_dictionary["user23"]["Phone"] = "+44 07342 800080"
|
46
|
+
#
|
47
|
+
# my_dictionary.write( "crypt-key-1234-wxyz" )
|
48
|
+
#
|
49
|
+
# ----------------------------------------------------------------------
|
50
|
+
#
|
51
|
+
# my_dictionary = Dictionary.create( "/path/to/backing/file", "crypt-key-1234-wxyz" )
|
52
|
+
# puts my_dictionary.has_key? "user23" # => true
|
53
|
+
# puts my_dictionary["user23"].length # => 3
|
54
|
+
# puts my_dictionary["user23"]["Email"] # => "joebloggs@example.com"
|
55
|
+
#
|
56
|
+
# ----------------------------------------------------------------------
|
57
|
+
class Dictionary < Hash
|
58
|
+
|
59
|
+
attr_accessor :backing_filepath, :section_id
|
60
|
+
|
61
|
+
|
62
|
+
# Create either a new empty dictionary or unmarshal (deserialize) the
|
63
|
+
# dictionary from an encrypted file depending on whether a file exists
|
64
|
+
# at the backing_file parameter location.
|
65
|
+
#
|
66
|
+
# If the backing file indeed exists, the crypt key will be employed to
|
67
|
+
# decode and then decrypt the contents before the unmarshal operation.
|
68
|
+
#
|
69
|
+
# The filepath is stored as an instance variable hence the {write}
|
70
|
+
# operation does not need to be told <b>where to?</b>
|
71
|
+
#
|
72
|
+
# @example
|
73
|
+
# # Create Dictionary the first time
|
74
|
+
# my_dictionary = Dictionary.create( "/path/to/backing/file" )
|
75
|
+
#
|
76
|
+
# # Create Dictionary from an Encrypted Backing File
|
77
|
+
# my_dictionary = Dictionary.create( "/path/to/backing/file", "crypt-key-1234-wxyz" )
|
78
|
+
#
|
79
|
+
# @param backing_file [String]
|
80
|
+
# the backing file is the filepath to this Dictionary's encrypted
|
81
|
+
# backing file when serialized. If no file exists at this path the
|
82
|
+
# operation will instantiate and return a new empty {Hash} object.
|
83
|
+
#
|
84
|
+
# @param crypt_key [String]
|
85
|
+
# if the backing file exists then this parameter must contain a
|
86
|
+
# robust symmetric decryption key. The symmetric key will be used
|
87
|
+
# for decryption after the base64 encoded file is read.
|
88
|
+
#
|
89
|
+
# Note that the decryption key is never part of the dictionary object.
|
90
|
+
# This class method knows it but the new Dictionary has no crypt key
|
91
|
+
# instance variable. Another crypt key must then be introduced when
|
92
|
+
# serializing (writing) the dictionary back into a file.
|
93
|
+
#
|
94
|
+
# @return [Dictionary]
|
95
|
+
# return a new Dictionary that knows where to go if it needs
|
96
|
+
# to read (deserialize) or write (serialize) itself.
|
97
|
+
#
|
98
|
+
# If no file exists at the path a new empty {Hash} object is
|
99
|
+
# returned.
|
100
|
+
#
|
101
|
+
# If a file exists, then the crypt_key parameter is expected
|
102
|
+
# to be the decryption and key and the dictionary will be based
|
103
|
+
# on the decrypted contents of the file.
|
104
|
+
#
|
105
|
+
# @raise [ArgumentError]
|
106
|
+
# An {ArgumentError} is raised if either no decryption key is provided
|
107
|
+
# or one that is unsuitable (ie was not used within the encryption).
|
108
|
+
# Errors can also arise if the block coding and decoding has not been
|
109
|
+
# done satisfactorily.
|
110
|
+
def self.create backing_file, crypt_key = nil
|
111
|
+
|
112
|
+
key_missing = File.file?( backing_file ) && crypt_key.nil?
|
113
|
+
raise ArgumentError, "No crypt key provided for file #{backing_file}" if key_missing
|
114
|
+
|
115
|
+
dictionary = Dictionary.new
|
116
|
+
dictionary.backing_filepath = backing_file
|
117
|
+
|
118
|
+
return dictionary unless File.file? backing_file
|
119
|
+
|
120
|
+
file_contents = File.read( backing_file ).strip
|
121
|
+
plaintext_str = file_contents.block_decode_decrypt( crypt_key )
|
122
|
+
dictionary.ingest_contents( plaintext_str )
|
123
|
+
|
124
|
+
return dictionary
|
125
|
+
|
126
|
+
end
|
127
|
+
|
128
|
+
|
129
|
+
# Create either a new dictionary containing the specified section or unmarshal
|
130
|
+
# (deserialize) the dictionary from an encrypted file depending on whether a
|
131
|
+
# file exists at the backing_file parameter location and then <b>create</b> the
|
132
|
+
# section <b>only if it does not exist</b>.
|
133
|
+
#
|
134
|
+
# If the backing file indeed exists, the crypt key will be employed to
|
135
|
+
# decode and then decrypt the contents before the unmarshal operation.
|
136
|
+
#
|
137
|
+
# The filepath is stored as an instance variable hence the {write}
|
138
|
+
# operation does not need to be told <b>where to?</b>
|
139
|
+
#
|
140
|
+
# This dictionary will also know which <b>"section"</b> should be used to
|
141
|
+
# put, add, update and delete key/value data. You can employ this dictionary
|
142
|
+
# such that <b>each instance only creates, updates, removes and/or reads</b>
|
143
|
+
# from a single section.
|
144
|
+
#
|
145
|
+
# @example
|
146
|
+
# # Create Dictionary the first time with a section.
|
147
|
+
# my_dictionary = Dictionary.create( "/path/to/file", "Europe" )
|
148
|
+
#
|
149
|
+
# # Create Dictionary from an Encrypted Backing File
|
150
|
+
# my_dictionary = Dictionary.create( "/path/to/file", "Europe", "1234-wxyz" )
|
151
|
+
#
|
152
|
+
# @param backing_file [String]
|
153
|
+
# the backing file is the filepath to this Dictionary's encrypted
|
154
|
+
# backing file when serialized.
|
155
|
+
#
|
156
|
+
# @param section_id [String]
|
157
|
+
# the created dictionary know which <b>section</b> should be used to
|
158
|
+
# put, add, update and delete key/value data. If the backing file
|
159
|
+
# does not exist a new section is created in the empty dictionary.
|
160
|
+
#
|
161
|
+
# If the file exists a new section is created only if it is not
|
162
|
+
# already present inside the dictionary.
|
163
|
+
#
|
164
|
+
# @param crypt_key [String]
|
165
|
+
# if the backing file exists then this parameter must contain a
|
166
|
+
# robust symmetric decryption key. The symmetric key will be used
|
167
|
+
# for decryption after the base64 encoded file is read.
|
168
|
+
#
|
169
|
+
# Note that the decryption key is never part of the dictionary object.
|
170
|
+
# This class method knows it but the new Dictionary has no crypt key
|
171
|
+
# instance variable. Another crypt key must then be introduced when
|
172
|
+
# serializing (writing) the dictionary back into a file.
|
173
|
+
#
|
174
|
+
# @return [Dictionary]
|
175
|
+
# return a new Dictionary that knows where to go if it needs
|
176
|
+
# to read (deserialize) or write (serialize) itself.
|
177
|
+
#
|
178
|
+
# If no file exists at the path a new empty {Hash} object is
|
179
|
+
# returned.
|
180
|
+
#
|
181
|
+
# If a file exists, then the crypt_key parameter is expected
|
182
|
+
# to be the decryption and key and the dictionary will be based
|
183
|
+
# on the decrypted contents of the file.
|
184
|
+
#
|
185
|
+
# @raise [ArgumentError]
|
186
|
+
# An {ArgumentError} is raised if either no decryption key is provided
|
187
|
+
# or one that is unsuitable (ie was not used within the encryption).
|
188
|
+
# Errors can also arise if the block coding and decoding has not been
|
189
|
+
# done satisfactorily.
|
190
|
+
def self.create_with_section backing_file, section_id, crypt_key = nil
|
191
|
+
|
192
|
+
dictionary = create( backing_file, crypt_key = nil )
|
193
|
+
dictionary.section_id = section_id
|
194
|
+
dictionary[section_id] = {} unless dictionary.has_key?( section_id )
|
195
|
+
|
196
|
+
return dictionary
|
197
|
+
|
198
|
+
end
|
199
|
+
|
200
|
+
|
201
|
+
# Write the data in this dictionary hash map into a file-system
|
202
|
+
# backend mirror whose path was specified in the {Dictionary.create}
|
203
|
+
# factory method.
|
204
|
+
#
|
205
|
+
# Technology for encryption at rest is mandatory when using this
|
206
|
+
# Dictionary to write and read files from the filesystem.
|
207
|
+
#
|
208
|
+
# Calling this {self.write} method when the file at the prescribed path
|
209
|
+
# does not exist results in the directory structure being created
|
210
|
+
# (if necessary) and then the (possibly encrypted) file being written.
|
211
|
+
#
|
212
|
+
# @param crypt_key [String]
|
213
|
+
# this parameter must contain a robust symmetric crypt key to use for
|
214
|
+
# the encryption before writing to the filesystem.
|
215
|
+
#
|
216
|
+
# Note that the decryption key is never part of the dictionary object.
|
217
|
+
# For uncrackable security this key must be changed every time the
|
218
|
+
# file is written.
|
219
|
+
def write crypt_key
|
220
|
+
|
221
|
+
ini_file = IniFile.new
|
222
|
+
self.each_key do |section_name|
|
223
|
+
ini_file[section_name] = self[section_name]
|
224
|
+
end
|
225
|
+
|
226
|
+
crypted_text = ini_file.to_s.encrypt_block_encode( crypt_key )
|
227
|
+
|
228
|
+
FileUtils.mkdir_p File.dirname(@backing_filepath)
|
229
|
+
File.write @backing_filepath, crypted_text
|
230
|
+
|
231
|
+
end
|
232
|
+
|
233
|
+
|
234
|
+
|
235
|
+
def get key_name
|
236
|
+
return self[@section_id][key_name]
|
237
|
+
end
|
238
|
+
|
239
|
+
|
240
|
+
|
241
|
+
def put key_name, key_value
|
242
|
+
self[@section_id][key_name] = key_value
|
243
|
+
end
|
244
|
+
|
245
|
+
|
246
|
+
|
247
|
+
|
248
|
+
# Ingest the contents of the INI string and merge it into a
|
249
|
+
# this object which is a {Hash}.
|
250
|
+
#
|
251
|
+
# @param the_contents [String]
|
252
|
+
# the INI string that will be ingested and morphed into
|
253
|
+
# this dictionary.
|
254
|
+
#
|
255
|
+
# @raise [ArgumentError]
|
256
|
+
# if the content contains any nil section name, key name
|
257
|
+
# or key value.
|
258
|
+
def ingest_contents the_contents
|
259
|
+
|
260
|
+
ini_file = IniFile.new( :content => the_contents )
|
261
|
+
ini_file.each do | data_group, data_key, data_value |
|
262
|
+
ingest_entry data_group, data_key, data_value
|
263
|
+
end
|
264
|
+
|
265
|
+
end
|
266
|
+
|
267
|
+
|
268
|
+
private
|
269
|
+
|
270
|
+
|
271
|
+
def ingest_entry section_name, key_name, value
|
272
|
+
|
273
|
+
msg = "A NIL object detected during ingestion of file [#{@filepath}]."
|
274
|
+
raise ArgumentError.new msg if section_name.nil? || key_name.nil? || value.nil?
|
275
|
+
|
276
|
+
if self.has_key? section_name then
|
277
|
+
self[section_name][key_name] = value
|
278
|
+
else
|
279
|
+
self.store section_name, { key_name => value }
|
280
|
+
end
|
281
|
+
|
282
|
+
end
|
283
|
+
|
284
|
+
|
285
|
+
end
|
286
|
+
|
287
|
+
|
288
|
+
end
|
@@ -0,0 +1,186 @@
|
|
1
|
+
#!/usr/bin/ruby
|
2
|
+
# coding: utf-8
|
3
|
+
|
4
|
+
module SafeDb
|
5
|
+
|
6
|
+
module Store
|
7
|
+
|
8
|
+
# Cold storage can sync repositories with a <b>bias during conflicts</b>
|
9
|
+
# either to the <em>remote repository</em> <b>when pulling</b>, and then
|
10
|
+
# conversely to the <em>local reposiory</em> <b>when pushing</b>.
|
11
|
+
#
|
12
|
+
# In between the sync operations a ColdStore can create, read, update and
|
13
|
+
# delete to and from the local mirror.
|
14
|
+
#
|
15
|
+
# == ColdStore | Use Cases
|
16
|
+
#
|
17
|
+
# Any <b>self-respecting coldstore</b> must, after initialization, provide
|
18
|
+
# some basic (and mandatory) behaviour.
|
19
|
+
#
|
20
|
+
# These include
|
21
|
+
#
|
22
|
+
# - <b>read</b> - reading text from a (possibly unavailable) frozen path
|
23
|
+
# - <b>write</b> - writing text (effectively freezing it) to a path
|
24
|
+
# - <b>pull</b> - sync with a <b>collision bias</b> that favours the remote mirror
|
25
|
+
# - <b>push</b> - sync with a <b>collision bias</b> that favours the local mirror
|
26
|
+
#
|
27
|
+
# <b>Cold Storage</b> is borrowed from BitCoin and represents offline storage
|
28
|
+
# for keys and crypts. safe separates keys and crypts so that you can
|
29
|
+
# transfer and share secrets by moving keys (not the crypts).
|
30
|
+
#
|
31
|
+
# == Houses and Gold Bullion
|
32
|
+
#
|
33
|
+
# You don't carry houses or gold bullion around to rent, share or transfer
|
34
|
+
# their ownership.
|
35
|
+
#
|
36
|
+
# You copy keys to rent secrets and when the tenure is up (or you change your
|
37
|
+
# mind) you revoke access with a metaphorical lock change.
|
38
|
+
#
|
39
|
+
# safe embodies concepts like an owner who rents as opposed to a change
|
40
|
+
# in ownership.
|
41
|
+
#
|
42
|
+
# == trade secrets | commoditizing secrets
|
43
|
+
#
|
44
|
+
# safe is a conduit through which secrets can be bought and sold.
|
45
|
+
#
|
46
|
+
# It commoditizes secrets so that they can be owned, traded, leased and
|
47
|
+
# auctioned. Options to acquire or relinquish them at set prices can easily
|
48
|
+
# be taken out.
|
49
|
+
class ColdStore
|
50
|
+
|
51
|
+
# @param base_path [String]
|
52
|
+
# path to the store's (mirror) base directory.
|
53
|
+
# If the denoted directory does not exist an attempt will be made to
|
54
|
+
# create it. If a file exists at this path an error will be thrown.
|
55
|
+
#
|
56
|
+
# @param domain [String]
|
57
|
+
# the domain is an identifier (and namespace) denoting which safe
|
58
|
+
# "account" is being accessed. safe allows the creation and use of
|
59
|
+
# multiple domains.
|
60
|
+
def initialize local_path
|
61
|
+
|
62
|
+
@store_path = local_path
|
63
|
+
FileUtils.mkdir_p @store_path
|
64
|
+
|
65
|
+
end
|
66
|
+
|
67
|
+
|
68
|
+
# Read the file frozen (in this store mirror) at this path and
|
69
|
+
# return its contents.
|
70
|
+
#
|
71
|
+
# Coldstores are usually frozen offline (offmachine) so for this
|
72
|
+
# to work the {ColdStore.pull} behaviour must have executed to
|
73
|
+
# create a local store mirror. This method reads from that mirror.
|
74
|
+
#
|
75
|
+
# @param from_path [String]
|
76
|
+
# read the file frozen at this path and return its contents
|
77
|
+
# so that the defreeze process can begin.
|
78
|
+
#
|
79
|
+
# This path is relative to the base of the store defined in
|
80
|
+
# the constructor.
|
81
|
+
#
|
82
|
+
# @return [String]
|
83
|
+
# return the text frozen in a file at the denoted local path
|
84
|
+
#
|
85
|
+
# nil is reurned if no file can be found in the local mirror
|
86
|
+
# at the configured path
|
87
|
+
#
|
88
|
+
# @raise [RuntimeError]
|
89
|
+
# unless the path exists in this coldstore and that path is
|
90
|
+
# a directory (as opposed to a file).
|
91
|
+
#
|
92
|
+
# @raise [ArgumentError]
|
93
|
+
# if more than one file match is made at the path specified.
|
94
|
+
def read from_path
|
95
|
+
|
96
|
+
frozen_filepath = File.join @store_path, from_path
|
97
|
+
frozen_dir_path = File.dirname(frozen_filepath)
|
98
|
+
|
99
|
+
log.info(x) { "Coldstore will search in folder [#{frozen_dir_path.hr_path}]" }
|
100
|
+
|
101
|
+
exists_msg = "Directory #{frozen_dir_path} does not exist in store."
|
102
|
+
is_dir_msg = "Path #{frozen_dir_path} should be a directory (not a file)."
|
103
|
+
raise RuntimeError, exists_msg unless File.exists? frozen_dir_path
|
104
|
+
raise RuntimeError, is_dir_msg unless File.directory? frozen_dir_path
|
105
|
+
|
106
|
+
full_filepath = ""
|
107
|
+
file_matched = false
|
108
|
+
|
109
|
+
Dir.glob("#{frozen_dir_path}/**/*.os.txt").each do |matched_path|
|
110
|
+
|
111
|
+
log.info(x) { "Coldstore search with [#{from_path}] has matched [#{matched_path.hr_path}]" }
|
112
|
+
log.info(x) { "Ignore directory at [#{matched_path.hr_path}]." } if File.directory? matched_path
|
113
|
+
next if File.directory? matched_path
|
114
|
+
|
115
|
+
two_match_msg = "More than one file matched. The second is #{matched_path}."
|
116
|
+
raise ArgumentError, two_match_msg if file_matched
|
117
|
+
file_matched = true
|
118
|
+
|
119
|
+
full_filepath = matched_path
|
120
|
+
|
121
|
+
end
|
122
|
+
|
123
|
+
no_file_msg = "Coldstore could not find path [#{from_path}] from [#{@store_path}]."
|
124
|
+
raise RuntimeError, no_file_msg unless file_matched
|
125
|
+
|
126
|
+
log.info(x) { "Coldstore matched exactly one envelope at [#{full_filepath.hr_path}]." }
|
127
|
+
return File.read full_filepath
|
128
|
+
|
129
|
+
end
|
130
|
+
|
131
|
+
|
132
|
+
# Write (freeze) the text into a file at the denoted path. The
|
133
|
+
# folder path will be created if need be.
|
134
|
+
#
|
135
|
+
# Coldstores are usually frozen offline (offmachine) so after
|
136
|
+
# this method completes the {ColdStore.push} behaviour must be
|
137
|
+
# executed to synchronize the local coldstore freezer with the
|
138
|
+
# remote mirror.
|
139
|
+
#
|
140
|
+
# @param this_text [String]
|
141
|
+
# this is the text that needs to be frozen into the local and
|
142
|
+
# subsequently the remote coldstore freezer.
|
143
|
+
#
|
144
|
+
# @param to_path [String]
|
145
|
+
# write the text (effectively freezing it) into the file at
|
146
|
+
# this path. An attempt will be made to put down the necessary
|
147
|
+
# directory structure.
|
148
|
+
#
|
149
|
+
# This path is relative to the base of the store defined in
|
150
|
+
# the constructor.
|
151
|
+
def write this_text, to_path
|
152
|
+
|
153
|
+
freeze_filepath = File.join @store_path, to_path
|
154
|
+
|
155
|
+
log.info(x) { "ColdStore freezing #{this_text.length} characters of worthless text."}
|
156
|
+
log.info(x) { "ColdStore freeze file path => #{freeze_filepath.hr_path}"}
|
157
|
+
|
158
|
+
FileUtils.mkdir_p(File.dirname(freeze_filepath))
|
159
|
+
File.write freeze_filepath, this_text
|
160
|
+
|
161
|
+
end
|
162
|
+
|
163
|
+
|
164
|
+
private
|
165
|
+
|
166
|
+
# @todo - write sync (with a local bias during conflicts)
|
167
|
+
# The open up to the public (published) api.
|
168
|
+
def push
|
169
|
+
|
170
|
+
|
171
|
+
end
|
172
|
+
|
173
|
+
# @todo - write sync (with a rmote bias during conflicts)
|
174
|
+
# The open up to the public (published) api.
|
175
|
+
def pull
|
176
|
+
|
177
|
+
end
|
178
|
+
|
179
|
+
|
180
|
+
end
|
181
|
+
|
182
|
+
|
183
|
+
end
|
184
|
+
|
185
|
+
|
186
|
+
end
|