opensecret 0.0.962 → 0.0.988
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 +4 -4
- data/README.md +16 -10
- data/bin/opensecret +3 -4
- data/bin/ops +5 -0
- data/lib/extension/string.rb +114 -0
- data/lib/factbase/facts.opensecret.io.ini +9 -21
- data/lib/interprete/begin.rb +232 -0
- data/lib/interprete/cmd.rb +621 -0
- data/lib/{plugins/usecases/unlock.rb → interprete/export.rb} +25 -70
- data/lib/interprete/init.rb +205 -0
- data/lib/interprete/key.rb +119 -0
- data/lib/interprete/open.rb +148 -0
- data/lib/{plugins/usecases → interprete}/put.rb +19 -6
- data/lib/{plugins/usecases → interprete}/safe.rb +2 -1
- data/lib/{plugins/usecases/lock.rb → interprete/seal.rb} +24 -34
- data/lib/interprete/set.rb +46 -0
- data/lib/interprete/use.rb +43 -0
- data/lib/interpreter.rb +165 -0
- data/lib/keytools/binary.map.rb +245 -0
- data/lib/keytools/digester.rb +245 -0
- data/lib/keytools/doc.conversion.to.ones.and.zeroes.ruby +179 -0
- data/lib/keytools/doc.rsa.radix.binary-mapping.ruby +190 -0
- data/lib/keytools/doc.star.schema.strategy.txt +77 -0
- data/lib/keytools/doc.using.pbkdf2.kdf.ruby +95 -0
- data/lib/keytools/doc.using.pbkdf2.pkcs.ruby +266 -0
- data/lib/keytools/kdf.bcrypt.rb +180 -0
- data/lib/keytools/kdf.pbkdf2.rb +164 -0
- data/lib/keytools/key.data.rb +227 -0
- data/lib/keytools/key.derivation.rb +341 -0
- data/lib/keytools/key.module.rb +140 -0
- data/lib/keytools/key.rb +481 -0
- data/lib/logging/gem.logging.rb +1 -2
- data/lib/modules/cryptology.md +43 -0
- data/lib/{plugins/ciphers → modules/cryptology}/aes-256.rb +6 -0
- data/lib/{crypto → modules/cryptology}/amalgam.rb +6 -0
- data/lib/modules/cryptology/blowfish.rb +130 -0
- data/lib/modules/cryptology/cipher.rb +207 -0
- data/lib/modules/cryptology/collect.rb +118 -0
- data/lib/{plugins → modules/cryptology}/crypt.io.rb +5 -0
- data/lib/{crypto → modules/cryptology}/engineer.rb +7 -1
- data/lib/{crypto → modules/cryptology}/open.bcrypt.rb +0 -0
- data/lib/modules/mappers/collateral.rb +282 -0
- data/lib/modules/mappers/dictionary.rb +288 -0
- data/lib/modules/mappers/envelope.rb +127 -0
- data/lib/modules/mappers/settings.rb +170 -0
- data/lib/modules/storage/coldstore.rb +186 -0
- data/lib/{opensecret/plugins.io/git/git.flow.rb → modules/storage/git.store.rb} +11 -0
- data/lib/notepad/scratch.pad.rb +17 -0
- data/lib/session/fact.finder.rb +13 -0
- data/lib/session/require.gem.rb +5 -0
- data/lib/store-commands.txt +180 -0
- data/lib/version.rb +1 -1
- data/opensecret.gemspec +5 -6
- metadata +74 -29
- data/lib/crypto/blowfish.rb +0 -85
- data/lib/crypto/collect.rb +0 -140
- data/lib/crypto/verify.rb +0 -33
- data/lib/opensecret.rb +0 -236
- data/lib/plugins/cipher.rb +0 -203
- data/lib/plugins/ciphers/blowfish.rb +0 -126
- data/lib/plugins/coldstore.rb +0 -181
- data/lib/plugins/envelope.rb +0 -116
- data/lib/plugins/secrets.uc.rb +0 -94
- data/lib/plugins/usecase.rb +0 -239
- data/lib/plugins/usecases/init.rb +0 -145
- data/lib/plugins/usecases/open.rb +0 -108
- data/lib/session/attributes.rb +0 -279
- data/lib/session/dictionary.rb +0 -191
- data/lib/session/file.path.rb +0 -53
- data/lib/session/session.rb +0 -80
@@ -0,0 +1,127 @@
|
|
1
|
+
#!/usr/bin/ruby
|
2
|
+
# coding: utf-8
|
3
|
+
|
4
|
+
module OpenSecret
|
5
|
+
|
6
|
+
module Mapper
|
7
|
+
|
8
|
+
require 'json'
|
9
|
+
|
10
|
+
# An envelope knows how to manipulate a JSON backed data structure
|
11
|
+
# (put, add etc) <b>after reading and then decrypting it</b> from a
|
12
|
+
# file and <b>before encrypting and then writing it</b> to a file.
|
13
|
+
#
|
14
|
+
# It provides behaviour to which we can create, append (add), update
|
15
|
+
# (change), read parts and delete essentially two structures
|
16
|
+
#
|
17
|
+
# - a collection of name/value pairs
|
18
|
+
# - an ordered list of values
|
19
|
+
#
|
20
|
+
# == JSON is Not Exposed in the Interface
|
21
|
+
#
|
22
|
+
# An envelope doesn't expose the data format used in the implementation
|
23
|
+
# allowing this to be changed seamlessly to YAMl or other formats.
|
24
|
+
#
|
25
|
+
# == Symmetric Encryption and Decryption
|
26
|
+
#
|
27
|
+
# An envelope supports operations to <b>read from</b> and <b>write to</b>
|
28
|
+
# a known filepath and with a symmetric key it can
|
29
|
+
#
|
30
|
+
# - decrypt <b>after reading from</b> a file and
|
31
|
+
# - encrypt <b>before writing to</b> a (the same) file
|
32
|
+
#
|
33
|
+
# == Hashes as the Primary Data Structure
|
34
|
+
#
|
35
|
+
# Envelope extends {Hash} as the core data structure for holding
|
36
|
+
#
|
37
|
+
# - strings
|
38
|
+
# - arrays
|
39
|
+
# - other hashes
|
40
|
+
# - booleans
|
41
|
+
# - integers and floats
|
42
|
+
class Envelope < Hash
|
43
|
+
|
44
|
+
|
45
|
+
# Read and inject into this envelope, the data structure found in a
|
46
|
+
# file at the path specified in the first parameter.
|
47
|
+
#
|
48
|
+
# Symmetric cryptography is mandatory for the envelope so we must
|
49
|
+
# <b>encrypt before writing</b> and <b>decrypt after reading</b>.
|
50
|
+
#
|
51
|
+
# An argument error will result if a suitable key is not provided.
|
52
|
+
#
|
53
|
+
# If the file does not exist (denoting the first read) all this method
|
54
|
+
# does is to stash the filepath as an instance variable and igore the
|
55
|
+
# decryption key which can be nil (or ommitted).
|
56
|
+
#
|
57
|
+
# @param the_filepath [String]
|
58
|
+
# absolute path to the file which acts as the persistent mirror to
|
59
|
+
# this data structure envelope.
|
60
|
+
#
|
61
|
+
# @param decryption_key [String]
|
62
|
+
# encryption at rest is a given so this mandatory parameter must
|
63
|
+
# contain a robust symmetric decryption key. The key will be used
|
64
|
+
# for decryption after the read and it will not linger (ie not cached
|
65
|
+
# as an instance variable).
|
66
|
+
#
|
67
|
+
# @raise [ArgumentError] if the decryption key is not robust enough.
|
68
|
+
def read the_filepath, decryption_key = nil
|
69
|
+
|
70
|
+
@filepath = the_filepath
|
71
|
+
return unless File.exists? @filepath
|
72
|
+
|
73
|
+
cipher_text = Base64.decode64( File.read( @filepath ).strip )
|
74
|
+
plain_text = ToolBelt::Blowfish.decryptor( cipher_text, decryption_key )
|
75
|
+
|
76
|
+
data_structure = JSON.parse plain_text
|
77
|
+
self.merge! data_structure
|
78
|
+
|
79
|
+
end
|
80
|
+
|
81
|
+
|
82
|
+
# Write the data in this envelope hash map into a file-system
|
83
|
+
# backed mirror whose path was specified in the {self.read} method.
|
84
|
+
#
|
85
|
+
# Technology for encryption at rest is supported by this dictionary
|
86
|
+
# and to this aim, please endeavour to post a robust symmetric
|
87
|
+
# encryption key.
|
88
|
+
#
|
89
|
+
# Calling this {self.write} method when the file at the prescribed path
|
90
|
+
# does not exist results in the directory structure being created
|
91
|
+
# (if necessary) and then the encrypted file being written.
|
92
|
+
#
|
93
|
+
# @param encryption_key [String]
|
94
|
+
# encryption at rest is a given so this mandatory parameter must
|
95
|
+
# contain a robust symmetric encryption key. The symmetric key will
|
96
|
+
# be used for the decryption after the read. Note that the decryption
|
97
|
+
# key does not linger meaning it isn't cached in an instance variable.
|
98
|
+
def write encryption_key
|
99
|
+
|
100
|
+
FileUtils.mkdir_p(File.dirname(@filepath))
|
101
|
+
cipher_text = Base64.encode64 ToolBelt::Blowfish.encryptor( self.to_json, encryption_key )
|
102
|
+
File.write @filepath, cipher_text
|
103
|
+
|
104
|
+
puts ""
|
105
|
+
puts "=== ============================"
|
106
|
+
puts "=== Envelope State ============="
|
107
|
+
puts "=== ============================"
|
108
|
+
|
109
|
+
a_ini_file = IniFile.new
|
110
|
+
self.each_key do |section_name|
|
111
|
+
a_ini_file[section_name] = self[section_name]
|
112
|
+
end
|
113
|
+
puts a_ini_file.to_s
|
114
|
+
|
115
|
+
puts "=== ============================"
|
116
|
+
puts ""
|
117
|
+
|
118
|
+
end
|
119
|
+
|
120
|
+
|
121
|
+
end
|
122
|
+
|
123
|
+
|
124
|
+
end
|
125
|
+
|
126
|
+
|
127
|
+
end
|
@@ -0,0 +1,170 @@
|
|
1
|
+
#!/usr/bin/ruby
|
2
|
+
# coding: utf-8
|
3
|
+
|
4
|
+
module OpenSecret
|
5
|
+
|
6
|
+
module Mapper
|
7
|
+
|
8
|
+
require 'inifile'
|
9
|
+
require 'singleton'
|
10
|
+
|
11
|
+
# This class contains basic behaviour for managing a client only
|
12
|
+
# (serverless) session. Configuration directives are read and written
|
13
|
+
# from an INI off the home directory that is created when the session
|
14
|
+
# is first initiated.
|
15
|
+
class Settings
|
16
|
+
|
17
|
+
# Stash the setting directive and its value into the configuration file
|
18
|
+
# using the default settings group.
|
19
|
+
#
|
20
|
+
# The default settings group is resolved via {Collateral::CONTEXT_NAME}
|
21
|
+
#
|
22
|
+
# @param key_name [String] the name of the key whose value is to be written
|
23
|
+
# @param key_value [String] the data item value of the key specified
|
24
|
+
def self.stash key_name, key_value
|
25
|
+
write Collateral::CONTEXT_NAME, key_name, key_value
|
26
|
+
end
|
27
|
+
|
28
|
+
|
29
|
+
# Stash the setting directive and its value into the configuration file
|
30
|
+
# using the default settings group.
|
31
|
+
#
|
32
|
+
# The default settings group is resolved via {Collateral::CONTEXT_NAME}
|
33
|
+
#
|
34
|
+
# @param key_name [String] the name of the key whose value is to be written
|
35
|
+
# @return [String]
|
36
|
+
# return the value of the configuration directive in the default group
|
37
|
+
def self.grab key_name
|
38
|
+
read Collateral::CONTEXT_NAME, key_name
|
39
|
+
end
|
40
|
+
|
41
|
+
|
42
|
+
# Write the key/value pair in the parameter into the session's
|
43
|
+
# configuration INI file that lives in a context-named folder
|
44
|
+
# off the home directory.
|
45
|
+
#
|
46
|
+
# The session file will be in a folder whose name is simply
|
47
|
+
# the dot prefixed context_name. The session file itself will
|
48
|
+
# be named using context_name + @@filename_tail
|
49
|
+
#
|
50
|
+
# @example ~/.openbox/openbox-session.ini is the filepath for context "openbox"
|
51
|
+
#
|
52
|
+
# If neither the folder nor file exist, both are created.
|
53
|
+
# If the file did not exist a new one will with the contents
|
54
|
+
# (if the key is length and the value is 2m).
|
55
|
+
#
|
56
|
+
# [openbox]
|
57
|
+
# length = 2m
|
58
|
+
#
|
59
|
+
# If the file does already exist, an appropriate merge will be
|
60
|
+
# performed to create or update the section name, key name and
|
61
|
+
# value. The file may end up looking like
|
62
|
+
#
|
63
|
+
# [closedbox]
|
64
|
+
# shape = cuboid
|
65
|
+
# color = blue
|
66
|
+
#
|
67
|
+
# [openbox]
|
68
|
+
# length = 2m
|
69
|
+
# width = 3m
|
70
|
+
#
|
71
|
+
# @param section_name [String] name grouping the section of config values
|
72
|
+
# @param key [String] the key name of config directive to be written into the file
|
73
|
+
# @param value [String] value of the config directive to be written into the file
|
74
|
+
#
|
75
|
+
def self.write section_name, key, value
|
76
|
+
|
77
|
+
config_filepath = Collateral.instance.config_file
|
78
|
+
config_directory = Collateral.instance.config_directory
|
79
|
+
|
80
|
+
FileUtils.mkdir_p config_directory unless File.exists? config_directory
|
81
|
+
|
82
|
+
config_map = IniFile.new( :filename => config_filepath, :encoding => 'UTF-8' )
|
83
|
+
config_map = IniFile.load( config_filepath ) if File.exists? config_filepath
|
84
|
+
|
85
|
+
config_map[section_name][key] = value
|
86
|
+
config_map.write
|
87
|
+
|
88
|
+
end
|
89
|
+
|
90
|
+
|
91
|
+
# Given the configuration key name and the context name, get the
|
92
|
+
# corresponding key value from the configuration file whose path
|
93
|
+
# is acquired using the {self#get_filepath} method.
|
94
|
+
#
|
95
|
+
# @param key_name [String] the key whose value is to be retrieved
|
96
|
+
#
|
97
|
+
# @return [String] the value configured for the parameter key
|
98
|
+
#
|
99
|
+
# @raise ArgumentError for any one of a long list of reasons that
|
100
|
+
# cause the key value to not be retrieved. This can range from
|
101
|
+
# non-existent directories and files, non readable files, incorrect
|
102
|
+
# configurations right down to missing keys or even missing values.
|
103
|
+
def self.read section_name, key_name
|
104
|
+
|
105
|
+
the_data = get_inifile_data
|
106
|
+
|
107
|
+
section_exists = the_data.has_section?( section_name )
|
108
|
+
raise ArgumentError.new "Section [#{section_name}] not found in INI file => #{the_data.to_s}" unless section_exists
|
109
|
+
|
110
|
+
key_exists = the_data[ section_name ].has_key?( key_name )
|
111
|
+
raise ArgumentError.new "Key [#{key_name}] not found in section [#{section_name}] => #{the_data.to_s}" unless key_exists
|
112
|
+
|
113
|
+
rawvalue = the_data[section_name][key_name]
|
114
|
+
raise ArgumentError.new "Empty value 4 key [#{section_name}][#{key_name}] => #{the_data.to_s}" if rawvalue.empty?
|
115
|
+
|
116
|
+
keyvalue = rawvalue.chomp.strip
|
117
|
+
raise ArgumentError.new "Whitespace value 4 key [#{section_name}][#{key_name}] => #{the_data.to_s}" if keyvalue.empty?
|
118
|
+
|
119
|
+
return keyvalue
|
120
|
+
|
121
|
+
end
|
122
|
+
|
123
|
+
|
124
|
+
# Return true if the settings configuration file contains the specified
|
125
|
+
# key (in the 2nd parameter) within the section name specified in the
|
126
|
+
# first parameter.
|
127
|
+
#
|
128
|
+
# This method does not check the contents (value) of the key. Even if it
|
129
|
+
# is an empty string, this method returns true so long as the section
|
130
|
+
# exists and the key exists within that.
|
131
|
+
#
|
132
|
+
# @param section_name [String] the name of the section to search under
|
133
|
+
# @param key_name [String] the name of the key to test for existence
|
134
|
+
# @return [Boolean]
|
135
|
+
# return true if both the section and the key exists within it.
|
136
|
+
# return false if the section specified does not exist.
|
137
|
+
#
|
138
|
+
# raise [ArgumentError]
|
139
|
+
# if the INI file does not exist
|
140
|
+
def self.contains_key? section_name, key_name
|
141
|
+
|
142
|
+
the_data = get_inifile_data
|
143
|
+
|
144
|
+
return false unless the_data.has_section?( section_name )
|
145
|
+
return the_data[ section_name ].has_key?( key_name )
|
146
|
+
|
147
|
+
end
|
148
|
+
|
149
|
+
|
150
|
+
private
|
151
|
+
|
152
|
+
|
153
|
+
def self.get_inifile_data
|
154
|
+
|
155
|
+
the_file = Collateral.instance.config_file
|
156
|
+
raise ArgumentError.new "No configuration file found => [ #{the_file} ]" unless File.exists? the_file
|
157
|
+
|
158
|
+
the_text = File.read the_file
|
159
|
+
raise ArgumentError.new "Configuration file is empty => [ #{the_file} ]" if the_text.empty?
|
160
|
+
|
161
|
+
return IniFile.load the_file
|
162
|
+
|
163
|
+
end
|
164
|
+
|
165
|
+
end
|
166
|
+
|
167
|
+
end
|
168
|
+
|
169
|
+
|
170
|
+
end
|
@@ -0,0 +1,186 @@
|
|
1
|
+
#!/usr/bin/ruby
|
2
|
+
# coding: utf-8
|
3
|
+
|
4
|
+
module OpenSecret
|
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. opensecret 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
|
+
# opensecret embodies concepts like an owner who rents as opposed to a change
|
40
|
+
# in ownership.
|
41
|
+
#
|
42
|
+
# == trade secrets | commoditizing secrets
|
43
|
+
#
|
44
|
+
# opensecret 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 opensecret
|
58
|
+
# "account" is being accessed. opensecret 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
|