cloud_powers 1.0.1 → 1.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 +4 -4
- data/.gitignore +2 -3
- data/.test.env.example +1 -0
- data/cloud_powers.gemspec +2 -2
- data/lib/cloud_powers/creatable.rb +15 -11
- data/lib/cloud_powers/example_objects.rb +88 -0
- data/lib/cloud_powers/helpers/lang_help.rb +33 -9
- data/lib/cloud_powers/helpers/logic_help.rb +28 -9
- data/lib/cloud_powers/helpers/path_help.rb +244 -34
- data/lib/cloud_powers/resource.rb +9 -5
- data/lib/cloud_powers/storage/bucket.rb +217 -0
- data/lib/cloud_powers/storage/local.rb +129 -0
- data/lib/cloud_powers/storage.rb +284 -12
- data/lib/cloud_powers/stubs/aws_stubs.rb +88 -10
- data/lib/cloud_powers/synapse/pipe/stream.rb +9 -11
- data/lib/cloud_powers/synapse/queue/board.rb +2 -1
- data/lib/cloud_powers/synapse/queue.rb +58 -1
- data/lib/cloud_powers/version.rb +1 -1
- data/lib/cloud_powers/zenv.rb +125 -60
- metadata +13 -11
- data/lib/cloud_powers/synapse/broadcast/broadcast.rb +0 -110
@@ -0,0 +1,217 @@
|
|
1
|
+
require 'cloud_powers/helpers'
|
2
|
+
require 'cloud_powers/storage'
|
3
|
+
|
4
|
+
module Smash
|
5
|
+
module CloudPowers
|
6
|
+
module Storage
|
7
|
+
class Bucket < Smash::CloudPowers::Resource
|
8
|
+
include Smash::CloudPowers::Storage
|
9
|
+
|
10
|
+
attr_accessor :origin, :delimiter, :tie_in_path
|
11
|
+
|
12
|
+
def initialize(name:, client: s3, **config)
|
13
|
+
super
|
14
|
+
@bucket = Aws::S3::Bucket.new(name: name, client: client)
|
15
|
+
build_storage(name: 'local', named_type: 'storage').link
|
16
|
+
@delimiter = config[:delimiter] || common_delimiter
|
17
|
+
# should be the 'lowest level' of storage this object is aware of
|
18
|
+
@origin = config[:origin] || project_root.split.last
|
19
|
+
@tie_in_path = paths_gcd(local_storage.tie_in_path, project_root)
|
20
|
+
end
|
21
|
+
|
22
|
+
def self.tie_in_config
|
23
|
+
# TODO make this able to use #to_snake so it can be dynamic
|
24
|
+
{ name: 'bucket', client: 's3' }
|
25
|
+
end
|
26
|
+
|
27
|
+
# def bucket_find(pattern, **config)
|
28
|
+
# bucket_select(pattern).first
|
29
|
+
# end
|
30
|
+
|
31
|
+
# def bucket_select(pattern, **config)
|
32
|
+
# client.list_buckets.buckets.select { |b| %r"#{name}" =~ b.name }
|
33
|
+
# end
|
34
|
+
|
35
|
+
def create_resource
|
36
|
+
@response = s3.create_bucket(bucket: name)
|
37
|
+
yield self if block_given?
|
38
|
+
self
|
39
|
+
end
|
40
|
+
|
41
|
+
def exists?
|
42
|
+
# TODO nil.exists? will asplode
|
43
|
+
!!(@bucket.exists? rescue nil)
|
44
|
+
end
|
45
|
+
|
46
|
+
# Find out if a job exists in the bucket
|
47
|
+
#
|
48
|
+
# Parameters
|
49
|
+
# * file_name +Pathname+|+String+ - the name of the file you want to pass
|
50
|
+
# * location +Pathname+|+String+ (optional) - this helps speed up the
|
51
|
+
# process by limiting any searching for the file
|
52
|
+
#
|
53
|
+
# Returns
|
54
|
+
# +IO+ -
|
55
|
+
def job_file_exists?(file_name, location = job_path)
|
56
|
+
normalized_location = paths_gcd(project_root, location)
|
57
|
+
!get_file_names(file_name, location: normalized_location).empty?
|
58
|
+
end
|
59
|
+
|
60
|
+
def link
|
61
|
+
if exists?
|
62
|
+
# do stuff
|
63
|
+
else
|
64
|
+
save!
|
65
|
+
end
|
66
|
+
|
67
|
+
@linked = @response.nil?
|
68
|
+
end
|
69
|
+
|
70
|
+
def select(*args, **opts)
|
71
|
+
pattern = args.pop
|
72
|
+
location = args.shift || origin
|
73
|
+
|
74
|
+
names = if filename?(pattern)
|
75
|
+
get_file_names(pattern, location: location)
|
76
|
+
else
|
77
|
+
get_bucket_names(pattern, location: location)
|
78
|
+
end
|
79
|
+
|
80
|
+
|
81
|
+
names = get_file_names(pattern, location: location)
|
82
|
+
if block_given?
|
83
|
+
names.map do |file_name|
|
84
|
+
new_file = to_realpath(local_storage.find(file_name))
|
85
|
+
File.open(new_file, 'r+') do |file|
|
86
|
+
data = get_file(file_name, location: location).get.body.read
|
87
|
+
yield file, data
|
88
|
+
end
|
89
|
+
end
|
90
|
+
else
|
91
|
+
names
|
92
|
+
end
|
93
|
+
end
|
94
|
+
|
95
|
+
def find(*args, location: origin, **opts)
|
96
|
+
path = select(*args, location: location, **opts).first
|
97
|
+
file_name = path.kind_of?(Pathname) ? path.split.last.to_s : path
|
98
|
+
file_permissions = opts[:file_permissions] || 'a+'
|
99
|
+
path = local_storage.tie_in_path + path
|
100
|
+
|
101
|
+
if block_given?
|
102
|
+
begin
|
103
|
+
File.open(path, file_permissions) do |file|
|
104
|
+
data = get_file(file_name, location: location).get.body.read
|
105
|
+
yield file, data
|
106
|
+
end
|
107
|
+
rescue Exception => e
|
108
|
+
if e =~ /no implicit conversion of nil into String/
|
109
|
+
logger.info "no file found from #{path} called #{file}"
|
110
|
+
nil
|
111
|
+
else
|
112
|
+
super
|
113
|
+
end
|
114
|
+
end
|
115
|
+
else
|
116
|
+
path
|
117
|
+
end
|
118
|
+
end
|
119
|
+
|
120
|
+
# Notes:
|
121
|
+
# * See https://docs.aws.amazon.com/sdkforruby/api/Aws/S3/Client.html#put_object-instance_method
|
122
|
+
def put(file_name, location = origin, **config)
|
123
|
+
body = get_local_file_body(file_name, location)
|
124
|
+
return if body.nil?
|
125
|
+
|
126
|
+
client.put_object(
|
127
|
+
put_config(body: body, key: file_name, **config)
|
128
|
+
)#.on_success { Thread.new { update_registry } }
|
129
|
+
true
|
130
|
+
end
|
131
|
+
|
132
|
+
private
|
133
|
+
# Get a local file using the file name and optional location
|
134
|
+
#
|
135
|
+
# Parameters
|
136
|
+
# * +Pathname+ - absolute path is preferable but a search for the file
|
137
|
+
# is initiated if it isn't found at <tt>local_storage.origin</tt>
|
138
|
+
#
|
139
|
+
# Returns
|
140
|
+
# +String+|+IO+
|
141
|
+
def get_local_file_body(file, location = origin)
|
142
|
+
scope = paths_gcd(tie_in_path, location)
|
143
|
+
local_storage.
|
144
|
+
find(file, location: scope, file_rights: 'r') { |data| data }
|
145
|
+
end
|
146
|
+
|
147
|
+
# Get object names from repeated
|
148
|
+
# <tt>Aws::S3::Client#list_objects_v2</tt> calls.
|
149
|
+
#
|
150
|
+
# Parameters
|
151
|
+
# * pattern +Regex+|+String+(glob) - the name of the object you are
|
152
|
+
# looking for
|
153
|
+
# * config +KeywordArgument+(s) - override options for the request to
|
154
|
+
# <tt>Aws::S3::Client.list_objects_v2</tt>
|
155
|
+
#
|
156
|
+
# Returns
|
157
|
+
# * +Array+ of Aws responses
|
158
|
+
#
|
159
|
+
# Notes:
|
160
|
+
# * See https://docs.aws.amazon.com/sdkforruby/api/Aws/S3/Client.html#list_objects_v2-instance_method
|
161
|
+
# * See https://docs.aws.amazon.com/sdkforruby/api/Aws/S3/Bucket.html#objects-instance_method
|
162
|
+
def get_file_names(pattern, location: origin)
|
163
|
+
objects = @bucket.objects(
|
164
|
+
delimiter: delimiter,
|
165
|
+
encoding_type: "url",
|
166
|
+
marker: "Marker",
|
167
|
+
prefix: location.to_s,
|
168
|
+
).map(&:key).grep(pattern)
|
169
|
+
end
|
170
|
+
|
171
|
+
def get_bucket_names(pattern, location: origin)
|
172
|
+
buckets = client.list_buckets.buckets.map(&:name)
|
173
|
+
end
|
174
|
+
|
175
|
+
# Get the actual object using S3::Bucket
|
176
|
+
def get_file(file_name, location: origin)
|
177
|
+
@bucket.object(file_name)
|
178
|
+
end
|
179
|
+
|
180
|
+
# Tweak the config
|
181
|
+
def put_config(**config)
|
182
|
+
{
|
183
|
+
acl: config[:acl] || "private", # accepts private, public-read, public-read-write, authenticated-read, aws-exec-read, bucket-owner-read, bucket-owner-full-control
|
184
|
+
body: config[:body], # file/IO object, or string data
|
185
|
+
bucket: name, # required
|
186
|
+
# cache_control: "CacheControl",
|
187
|
+
# content_disposition: "ContentDisposition",
|
188
|
+
# content_encoding: "ContentEncoding",
|
189
|
+
# content_language: "ContentLanguage",
|
190
|
+
# content_length: 1,
|
191
|
+
# content_md5: "ContentMD5",
|
192
|
+
# content_type: "ContentType",
|
193
|
+
# expires: Time.now,
|
194
|
+
# grant_full_control: "GrantFullControl",
|
195
|
+
# grant_read: "GrantRead",
|
196
|
+
# grant_read_acp: "GrantReadACP",
|
197
|
+
# grant_write_acp: "GrantWriteACP",
|
198
|
+
key: config[:file_name], # required
|
199
|
+
# metadata: {
|
200
|
+
# "MetadataKey" => "MetadataValue",
|
201
|
+
# },
|
202
|
+
server_side_encryption: "AES256", # accepts AES256, aws:kms
|
203
|
+
storage_class: "STANDARD", # accepts STANDARD, REDUCED_REDUNDANCY, STANDARD_IA
|
204
|
+
# website_redirect_location: "WebsiteRedirectLocation",
|
205
|
+
# sse_customer_algorithm: "SSECustomerAlgorithm",
|
206
|
+
# sse_customer_key: "SSECustomerKey",
|
207
|
+
# sse_customer_key_md5: "SSECustomerKeyMD5",
|
208
|
+
# ssekms_key_id: "SSEKMSKeyId",
|
209
|
+
# request_payer: "requester", # accepts requester
|
210
|
+
tagging: "TaggingHeader", # !! Relate this to the project !!
|
211
|
+
use_accelerate_endpoint: false,
|
212
|
+
}.merge(config)
|
213
|
+
end
|
214
|
+
end
|
215
|
+
end
|
216
|
+
end
|
217
|
+
end
|
@@ -0,0 +1,129 @@
|
|
1
|
+
require 'cloud_powers/storage'
|
2
|
+
|
3
|
+
module Smash
|
4
|
+
module CloudPowers
|
5
|
+
module Storage
|
6
|
+
class Local < Smash::CloudPowers::Resource
|
7
|
+
include Smash::CloudPowers::Storage
|
8
|
+
include Smash::CloudPowers::Zenv
|
9
|
+
|
10
|
+
attr_accessor :origin, :tie_in_path
|
11
|
+
|
12
|
+
def initialize(name:, origin: zlib_home, **config)
|
13
|
+
super
|
14
|
+
@origin = origin.kind_of?(Pathname) ? origin : to_pathname(origin)
|
15
|
+
@tie_in_path = to_pathname(@origin, to_camel(name))
|
16
|
+
end
|
17
|
+
|
18
|
+
def self.tie_in_config
|
19
|
+
# TODO make this able to use #to_snake so it can be dynamic
|
20
|
+
{ type: 'local', origin: 'zlib_path' }
|
21
|
+
end
|
22
|
+
|
23
|
+
# def self.config(*opts)
|
24
|
+
# {
|
25
|
+
# name: opts.select { |k| k.kind_of? String }.first || 'local',
|
26
|
+
# origin: opts.select { |k| k.kind_of? Pathname }.first,
|
27
|
+
# config: opts.select { |k| k.kind_of? Hash }.first
|
28
|
+
# }
|
29
|
+
# end
|
30
|
+
|
31
|
+
# Creates an actual directory or file using the standard format for
|
32
|
+
# <b><i>this</i></b> storage name (camel case)
|
33
|
+
#
|
34
|
+
# Returns
|
35
|
+
# <tt>Storage::Local</tt?
|
36
|
+
def create_resource
|
37
|
+
@response = to_realpath(tie_in_path)
|
38
|
+
|
39
|
+
yield self if block_given?
|
40
|
+
self
|
41
|
+
end
|
42
|
+
|
43
|
+
# Find out if this local directory exists. Because directories are one
|
44
|
+
# of the ways to scope in <tt>CloudPowers::Storage::Local</tt>, we can
|
45
|
+
# find out if this local object is already on disk by searching the
|
46
|
+
# system, up to and including the root directory. The search attempts
|
47
|
+
# to first search through a smaller scope by checking in the
|
48
|
+
# +project_root+, +origin+ and +./zlib+ directories before giving up
|
49
|
+
# and searching the whole machine from +/+ or +C:\+ etc
|
50
|
+
#
|
51
|
+
# Parameters
|
52
|
+
# * scope +Pathname+ - default is +project_root+|+origin+|+zlib+|+Root Dir+
|
53
|
+
#
|
54
|
+
# Returns
|
55
|
+
# * +Boolean+
|
56
|
+
#
|
57
|
+
# Notes:
|
58
|
+
# * See <tt>#initialize</tt> for +origin+
|
59
|
+
def exists?(scope: origin.parent)
|
60
|
+
search_results = path_search(name, scope)
|
61
|
+
!(search_results.empty?)
|
62
|
+
end
|
63
|
+
|
64
|
+
# Tie the object on disk with this object, in memory. If the object
|
65
|
+
# on disk doesn't exist, it will be created now.
|
66
|
+
#
|
67
|
+
# Returns
|
68
|
+
# * <tt>Smash::CloudPowers::Storage::Local</tt> - +self+
|
69
|
+
def link
|
70
|
+
@linked = exists? ? true : save!
|
71
|
+
self
|
72
|
+
end
|
73
|
+
|
74
|
+
def job_file_exists?(object_name = '')
|
75
|
+
!!(find(object_name, location: job_home))
|
76
|
+
end
|
77
|
+
|
78
|
+
def select(*patterns, **opts)
|
79
|
+
file_rights = opts[:file_rights] || 'a+'
|
80
|
+
scope = paths_lcd(
|
81
|
+
tie_in_path, to_pathname(opts[:location]).expand_path
|
82
|
+
)
|
83
|
+
|
84
|
+
regexs = patterns.map do |pattern|
|
85
|
+
if pattern.kind_of?(Pathname)
|
86
|
+
additional_scope, basename = pattern.split
|
87
|
+
scope = paths_lcd(additional_scope, scope)
|
88
|
+
%r"#{basename}$"
|
89
|
+
else
|
90
|
+
pattern.kind_of?(Regexp) ? pattern : %r"#{pattern.to_s}$"
|
91
|
+
end
|
92
|
+
end
|
93
|
+
|
94
|
+
paths = regexs.map do |regex|
|
95
|
+
Dir["#{scope}/**/*"].
|
96
|
+
grep(regex).
|
97
|
+
map { |path| to_realpath(path) }
|
98
|
+
end.flatten
|
99
|
+
|
100
|
+
if block_given?
|
101
|
+
paths.map { |path| yield File.open(path, file_rights) }
|
102
|
+
else
|
103
|
+
paths
|
104
|
+
end.flatten
|
105
|
+
end
|
106
|
+
|
107
|
+
def find(*args, location: origin, **opts)
|
108
|
+
# TODO:
|
109
|
+
# it's good to use select but this should use a stronger search or
|
110
|
+
# something, to provide something more than just `.first`-ing an array
|
111
|
+
path = select(*args, location: location, **opts).first
|
112
|
+
file_rights = opts[:file_rights] || 'a+'
|
113
|
+
if block_given?
|
114
|
+
begin
|
115
|
+
File.open(path, file_rights) do |file|
|
116
|
+
yield file.read
|
117
|
+
end
|
118
|
+
rescue TypeError => e
|
119
|
+
logger.info("file doesn't exist #{path}")
|
120
|
+
nil
|
121
|
+
end
|
122
|
+
else
|
123
|
+
path
|
124
|
+
end
|
125
|
+
end
|
126
|
+
end
|
127
|
+
end
|
128
|
+
end
|
129
|
+
end
|
data/lib/cloud_powers/storage.rb
CHANGED
@@ -1,16 +1,85 @@
|
|
1
1
|
require 'pathname'
|
2
|
-
|
2
|
+
require 'cloud_powers/aws_resources'
|
3
|
+
require 'cloud_powers/helpers'
|
4
|
+
require 'cloud_powers/storage/bucket'
|
5
|
+
require 'cloud_powers/storage/local'
|
3
6
|
|
4
7
|
module Smash
|
5
8
|
module CloudPowers
|
6
9
|
module Storage
|
7
10
|
include Smash::CloudPowers::AwsResources
|
11
|
+
include Smash::CloudPowers::Helpers
|
12
|
+
|
13
|
+
def existing_storage(types = all_storage_types)
|
14
|
+
normalized_types = types.map { |type| to_pascal(type) }
|
15
|
+
i_vars.select do |k,v|
|
16
|
+
normalized_types.include?(to_pascal(v.type)) rescue nil
|
17
|
+
end.values
|
18
|
+
end
|
19
|
+
|
20
|
+
# Get all existing Storage. If there isn't any existing Storage objects,
|
21
|
+
# get some delegates for the available types. If parameters are passed,
|
22
|
+
# narrow down the result set with the params.
|
23
|
+
#
|
24
|
+
# Parameters
|
25
|
+
# * queries +String+|+Symbol+|+Storage+ - a
|
26
|
+
# * +String+ will represent a name and gets searched for using
|
27
|
+
# delegates. If a location is found with no Storage object that
|
28
|
+
# matches, it builds it/them.
|
29
|
+
# * +Symbol+s are used to narrow down the types used in the search.
|
30
|
+
# * +Storage+ This method also allows you to pass in +Storage+ objects
|
31
|
+
# and have them passed into the list of returned +Storage+ objects.
|
32
|
+
# This is useful for allowing this method to be used in other methods
|
33
|
+
#
|
34
|
+
# Returns
|
35
|
+
# * +Array+ of <tt>Smash::CloudPowers::Storage[::?]</tt>
|
36
|
+
|
37
|
+
def all_storage(queries = [])
|
38
|
+
types, passed, names = sort_all_storage_params(queries)
|
39
|
+
# combine the search efforts from above
|
40
|
+
found = existing_storage(types) + passed
|
41
|
+
found_names = found.map(&:name)
|
42
|
+
|
43
|
+
# make sure all the names that were not found are represented as a new
|
44
|
+
# Storage object, if it isn't already
|
45
|
+
unmatched_names = names - found_names
|
46
|
+
|
47
|
+
# if all the names are represented, return the correct results
|
48
|
+
if unmatched_names.empty?
|
49
|
+
(found + storage_delegates(types)).uniq
|
50
|
+
else
|
51
|
+
# or build ones that should be and add it to the results, then return
|
52
|
+
# them all.
|
53
|
+
storage_delegates(types).map do |storage|
|
54
|
+
unlinked_storages = storage.select(*unmatched_names).reject do |n|
|
55
|
+
filename? n
|
56
|
+
end
|
57
|
+
|
58
|
+
unlinked_storages.map do |unlinked_storage|
|
59
|
+
# TODO create a method in path_help to separate. it can be used
|
60
|
+
# here and instead of doing it in to_snake, to_snake can use it
|
61
|
+
# too
|
62
|
+
clues = unlinked_storage.to_s.split(%r"#{common_delimiter}")
|
63
|
+
|
64
|
+
config = {
|
65
|
+
name: clues.pop,
|
66
|
+
type: storage.type,
|
67
|
+
origin: clues.join(common_delimiter)
|
68
|
+
}
|
69
|
+
|
70
|
+
config = storage_config(storage.type).merge(config)
|
71
|
+
build_storage(**config)
|
72
|
+
end
|
73
|
+
end.flatten
|
74
|
+
end
|
75
|
+
end
|
8
76
|
|
9
77
|
def local_job_file_exists?(file)
|
10
|
-
|
78
|
+
path = job_path(to_ruby_file_name(file))
|
79
|
+
storage_select(file, location: [:local, path]).count > 0
|
11
80
|
end
|
12
81
|
|
13
|
-
# Searches a local
|
82
|
+
# Searches a local jobs storage location for the given +file+ name
|
14
83
|
# if it exists - exit the method
|
15
84
|
# if it does <i>not</i> exist - get the file from s3 and place it in
|
16
85
|
# the directory that was just searched bucket using +#zfind()+
|
@@ -43,17 +112,22 @@ module Smash
|
|
43
112
|
# | |_foobar.rb
|
44
113
|
# | |_custom_greetings.js # could be an after effects JS script
|
45
114
|
def source_job(file)
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
objects = s3.list_objects(bucket: bucket).contents.select do |f|
|
50
|
-
/#{Regexp.escape file}/i =~ f.key
|
115
|
+
storage_delegates(:bucket).map do |delegate|
|
116
|
+
delegate.find(file, location: zfind('jobs storage')) do |f, data|
|
117
|
+
f.write data
|
51
118
|
end
|
119
|
+
end.flatten
|
120
|
+
# TODO: better path management
|
121
|
+
# bucket = zfind('jobs storage')
|
122
|
+
# if job_path(to_ruby_file_name(file)).nil?
|
123
|
+
# objects = s3.list_objects(bucket: bucket).contents.select do |f|
|
124
|
+
# /#{Regexp.escape file}/i =~ f.key
|
125
|
+
# end
|
52
126
|
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
end
|
127
|
+
# objects.each do |obj|
|
128
|
+
# s3.get_object(bucket: bucket, key: obj.key, response_target: job_path(file))
|
129
|
+
# end
|
130
|
+
# end
|
57
131
|
end
|
58
132
|
|
59
133
|
# Search through a bucket to find a file, based on a regex
|
@@ -69,6 +143,8 @@ module Smash
|
|
69
143
|
# matches.first.contents.size
|
70
144
|
# # => 238934 # integer representation of the file size
|
71
145
|
def search(bucket, pattern)
|
146
|
+
# this guard allows methods that return nil to be used as the 'bucket',
|
147
|
+
# like #zfind()
|
72
148
|
return [] if bucket.nil?
|
73
149
|
|
74
150
|
begin
|
@@ -82,6 +158,69 @@ module Smash
|
|
82
158
|
end
|
83
159
|
end
|
84
160
|
|
161
|
+
# This method builds a <tt>CloudPowers::Storage::</tt> object for you to
|
162
|
+
# use but doesn't invoke the <tt>#create!()</tt> method, so no API call is
|
163
|
+
# made, no directories are created, etc. This can be used even if the
|
164
|
+
# storage already exists.
|
165
|
+
#
|
166
|
+
# Parameters
|
167
|
+
# * name +String+ - name of the storage you want to interact with
|
168
|
+
#
|
169
|
+
# Returns
|
170
|
+
# Storage::[Local|Bucket]
|
171
|
+
#
|
172
|
+
# Example
|
173
|
+
# storage_object = build_queue('exampleQueue')
|
174
|
+
# storage_object.select('job')
|
175
|
+
# => job.rb
|
176
|
+
def build_storage(name:, type: nil, **config)
|
177
|
+
type = to_pascal(type || :local)
|
178
|
+
|
179
|
+
storage_resource = Smash::CloudPowers::Storage.const_get(type).
|
180
|
+
build(name: to_camel(name), type: (type || :storage), **config)
|
181
|
+
|
182
|
+
yield storage_resource if block_given?
|
183
|
+
|
184
|
+
attr_map(storage_resource.call_name => storage_resource) do |attribute, resource|
|
185
|
+
instance_attr_accessor attribute
|
186
|
+
resource
|
187
|
+
end
|
188
|
+
|
189
|
+
storage_resource
|
190
|
+
end
|
191
|
+
|
192
|
+
|
193
|
+
# Create a storage without explicitly creating an object, of whatever type
|
194
|
+
# we're working with, here. The types can be of any
|
195
|
+
# <tt>CloudPowers::Storage</tt> or any other class, as long as it follows
|
196
|
+
# the <tt>CloudPowers::Createable</tt> Interface
|
197
|
+
#
|
198
|
+
# Parameters
|
199
|
+
# * name +String+ - The name of the Queue to be created
|
200
|
+
#
|
201
|
+
# Returns
|
202
|
+
# CloudPowers::Storage::[Local|Bucket]
|
203
|
+
#
|
204
|
+
# Example
|
205
|
+
# create_queue('exampleQueue')
|
206
|
+
# get_queue_message_count
|
207
|
+
def create_storage(name:, type: nil, **config)
|
208
|
+
# default is :local
|
209
|
+
type, config[:type] = type.nil? ? [:local, :storage] : [type, type]
|
210
|
+
|
211
|
+
storage_resource =
|
212
|
+
Smash::CloudPowers::Storage.const_get(to_pascal(type)).create!(
|
213
|
+
name: to_camel(name), **config
|
214
|
+
)
|
215
|
+
|
216
|
+
attr_map(storage_resource.call_name => storage_resource) do |attribute, resource|
|
217
|
+
instance_attr_accessor attribute
|
218
|
+
resource
|
219
|
+
end
|
220
|
+
|
221
|
+
storage_resource
|
222
|
+
end
|
223
|
+
|
85
224
|
# Send the log files to the S3 log file bucket
|
86
225
|
#
|
87
226
|
# Returns
|
@@ -95,6 +234,139 @@ module Smash
|
|
95
234
|
)
|
96
235
|
end
|
97
236
|
end
|
237
|
+
|
238
|
+
# Search through all Storage or just the ones that fit your search
|
239
|
+
# parameters. The searching algorithm takes you through existing
|
240
|
+
# +Storage+ objects and the locations they represent, like local the file
|
241
|
+
# system and AWS S3 Buckets
|
242
|
+
#
|
243
|
+
# Parameters
|
244
|
+
# * list of +String+ - the name(s) of the file, direcotry, bucket, etc you
|
245
|
+
# are looking for
|
246
|
+
# * list of +KeyValue+ pairs - search options that are used for narrowing
|
247
|
+
# down your objects
|
248
|
+
# * * +:location+ +:local+|+:bucket+|+String+
|
249
|
+
#
|
250
|
+
# Returns
|
251
|
+
# * Array +Objects+ - can be the return value of whichever Storage object
|
252
|
+
# found something that matched the param(s)
|
253
|
+
#
|
254
|
+
# Notes:
|
255
|
+
# * Narrowing down the search results speeds up the search
|
256
|
+
# * If a block is passed to this method, it will be run in the
|
257
|
+
# <tt>#select</tt> method of the current Storage type
|
258
|
+
# * See the <tt>#select</tt> methods for all the Storage classes you are
|
259
|
+
# using
|
260
|
+
def storage_select(*names, **opts)
|
261
|
+
# make sure to process all possible types of names that can come in.
|
262
|
+
# symbols are reserved for types, not names.
|
263
|
+
locations = [opts.delete(:location) || opts.delete(:locations)].flatten
|
264
|
+
locations = (locations + extract!(names) { |n| n.kind_of? Symbol }).uniq
|
265
|
+
|
266
|
+
all_storage(locations).map do |storage|
|
267
|
+
if block_given?
|
268
|
+
storage.select(*names, **opts) do |object|
|
269
|
+
yield object
|
270
|
+
end
|
271
|
+
else
|
272
|
+
storage.select(*names, **opts)
|
273
|
+
end
|
274
|
+
end.flatten
|
275
|
+
end
|
276
|
+
|
277
|
+
private
|
278
|
+
# Get all types that are both +Storage+ and work with the +Creatable+ ->
|
279
|
+
# +Resource+ interface.
|
280
|
+
#
|
281
|
+
# Parameters
|
282
|
+
# * a list of types that both fit the requirements listed in this method's
|
283
|
+
# description and are in the list given as parameter(s).
|
284
|
+
#
|
285
|
+
# Returns
|
286
|
+
# * <tt>Array[Symbols]</tt>
|
287
|
+
def all_storage_types(*types)
|
288
|
+
# TODO - Death to harcoding!!!
|
289
|
+
# this should be able to query the Storage module or something to figure
|
290
|
+
# out all the storage types instead of hardcoding
|
291
|
+
[:local, :bucket] - types
|
292
|
+
end
|
293
|
+
|
294
|
+
# sort out the parameters that can be sent to the <tt>#all_storage</tt>
|
295
|
+
# method, to get rid of some of the bulk in that method.
|
296
|
+
#
|
297
|
+
# Parameters
|
298
|
+
# * queries +Array+ of +Symbol+|+String+|+Storage+ - see <tt>all_storage</tt>
|
299
|
+
# for a description of the params
|
300
|
+
#
|
301
|
+
# Returns
|
302
|
+
# +Array+ of +Array+ of segregated params
|
303
|
+
def sort_all_storage_params(queries)
|
304
|
+
# get all types from the queries parameter then normalize them so they
|
305
|
+
# can be compared to another array's terms
|
306
|
+
types = queries.select { |element| element.kind_of? Symbol }
|
307
|
+
types = all_storage_types if types.empty?
|
308
|
+
normalized_types = types.map { |element| to_pascal(element) }
|
309
|
+
|
310
|
+
# get all the Storage objects and pass them through, to the returned
|
311
|
+
# Storage objects
|
312
|
+
passed = queries.select do |element|
|
313
|
+
normalized_types.include?(to_pascal(element.type)) rescue nil
|
314
|
+
end
|
315
|
+
|
316
|
+
# get all the Strings and use them as names of Storage(s)
|
317
|
+
names = queries.select { |element| element.kind_of? String }
|
318
|
+
[types, passed, names]
|
319
|
+
end
|
320
|
+
|
321
|
+
# Get delegate Storage objects for every type available. A delegate is
|
322
|
+
# just a basic Storage object of a given type, that can access enough
|
323
|
+
# locations, on its own, that it can be used as a good, base representation
|
324
|
+
# of a Storage location, "out in the wild"; e.g. local, cloud, networked
|
325
|
+
# etc.
|
326
|
+
#
|
327
|
+
# Returns
|
328
|
+
# * +Array+ of <tt>Smash::CloudPowers::Storage[::?]</tt> - with access to
|
329
|
+
# all or most of the "root" of the storage out in the wild
|
330
|
+
def storage_delegates(types = all_storage_types)
|
331
|
+
types = [types].flatten
|
332
|
+
types.map do |type|
|
333
|
+
delegate_call_name = "delegate_#{type}"
|
334
|
+
if respond_to? delegate_call_name
|
335
|
+
public_send delegate_call_name
|
336
|
+
else
|
337
|
+
config = storage_config(type).merge({ name: 'delegate', type: type })
|
338
|
+
# set delegates up to search everywhere
|
339
|
+
build_storage(**config) do |storage|
|
340
|
+
storage.tie_in_path = storage.origin
|
341
|
+
end
|
342
|
+
end
|
343
|
+
end
|
344
|
+
end
|
345
|
+
|
346
|
+
# Get information directly from the class you will soon instantiate. This
|
347
|
+
# method gathers information from the class that the class itself deems
|
348
|
+
# important enough to tell you about, for instantiating it. The method
|
349
|
+
# will call 0..n methods on your current class to gain information from
|
350
|
+
# this context; that it may be used in the new storage class' context as
|
351
|
+
# well.
|
352
|
+
#
|
353
|
+
# The best example of this is the clients
|
354
|
+
def storage_config(type)
|
355
|
+
tie_in_config = begin
|
356
|
+
Smash::CloudPowers.const_get(to_pascal(type)).
|
357
|
+
tie_in_config.
|
358
|
+
inject({}) do |conf, (attribute, method)|
|
359
|
+
conf[attribute] = if self.respond_to? method
|
360
|
+
public_send(method)
|
361
|
+
else
|
362
|
+
method
|
363
|
+
end
|
364
|
+
conf
|
365
|
+
end
|
366
|
+
rescue Exception => e
|
367
|
+
{ name: to_snake(type) }
|
368
|
+
end
|
369
|
+
end
|
98
370
|
end
|
99
371
|
end
|
100
372
|
end
|