fog-softlayer 0.0.5 → 0.0.7
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 +8 -8
- data/.travis.yml +37 -0
- data/CONTRIBUTING.md +30 -0
- data/README.md +8 -5
- data/docs/cla-corporate.md +133 -0
- data/docs/cla-individual.md +84 -0
- data/docs/fog-softlayer-CCLA.pdf +0 -0
- data/docs/fog-softlayer-CLA.pdf +0 -0
- data/examples/storage.md +107 -0
- data/fog-softlayer.gemspec +1 -0
- data/gemfiles/Gemfile-edge +14 -0
- data/gemfiles/Gemfile-ruby-1.8.7 +14 -0
- data/lib/fog/softlayer.rb +1 -0
- data/lib/fog/softlayer/compute.rb +1 -1
- data/lib/fog/softlayer/compute/shared.rb +1 -1
- data/lib/fog/softlayer/core.rb +17 -1
- data/lib/fog/softlayer/models/storage/directories.rb +40 -0
- data/lib/fog/softlayer/models/storage/directory.rb +50 -0
- data/lib/fog/softlayer/models/storage/file.rb +166 -0
- data/lib/fog/softlayer/models/storage/files.rb +99 -0
- data/lib/fog/softlayer/requests/storage/copy_object.rb +42 -0
- data/lib/fog/softlayer/requests/storage/delete_container.rb +38 -0
- data/lib/fog/softlayer/requests/storage/delete_multiple_objects.rb +67 -0
- data/lib/fog/softlayer/requests/storage/delete_object.rb +40 -0
- data/lib/fog/softlayer/requests/storage/delete_static_large_object.rb +43 -0
- data/lib/fog/softlayer/requests/storage/get_container.rb +68 -0
- data/lib/fog/softlayer/requests/storage/get_containers.rb +51 -0
- data/lib/fog/softlayer/requests/storage/get_object.rb +45 -0
- data/lib/fog/softlayer/requests/storage/get_object_https_url.rb +88 -0
- data/lib/fog/softlayer/requests/storage/head_container.rb +28 -0
- data/lib/fog/softlayer/requests/storage/head_containers.rb +25 -0
- data/lib/fog/softlayer/requests/storage/head_object.rb +23 -0
- data/lib/fog/softlayer/requests/storage/post_set_meta_temp_url_key.rb +37 -0
- data/lib/fog/softlayer/requests/storage/put_container.rb +32 -0
- data/lib/fog/softlayer/requests/storage/put_dynamic_obj_manifest.rb +43 -0
- data/lib/fog/softlayer/requests/storage/put_object.rb +61 -0
- data/lib/fog/softlayer/requests/storage/put_object_manifest.rb +16 -0
- data/lib/fog/softlayer/requests/storage/put_static_obj_manifest.rb +57 -0
- data/lib/fog/softlayer/storage.rb +283 -0
- data/lib/fog/softlayer/version.rb +1 -1
- data/tests/helper.rb +0 -29
- data/tests/helpers/mock_helper.rb +4 -98
- data/tests/softlayer/models/storage/directory_tests.rb +49 -0
- data/tests/softlayer/models/storage/file_tests.rb +56 -0
- data/tests/softlayer/requests/storage/auth_tests.rb +17 -0
- data/tests/softlayer/requests/storage/container_tests.rb +52 -0
- data/tests/softlayer/requests/storage/object_tests.rb +68 -0
- metadata +53 -2
data/fog-softlayer.gemspec
CHANGED
@@ -34,5 +34,6 @@ Gem::Specification.new do |spec|
|
|
34
34
|
spec.add_development_dependency('shindo', '~> 0.3.4')
|
35
35
|
spec.add_development_dependency('fission')
|
36
36
|
spec.add_development_dependency('pry')
|
37
|
+
spec.add_development_dependency('pry-debugger')
|
37
38
|
spec.add_development_dependency('google-api-client', '~> 0.6', '>= 0.6.2')
|
38
39
|
end
|
@@ -0,0 +1,14 @@
|
|
1
|
+
source "https://rubygems.org"
|
2
|
+
|
3
|
+
# Shared components
|
4
|
+
gem "fog-core", :github => "fog/fog-core"
|
5
|
+
gem "fog-json", :github => "fog/fog-json"
|
6
|
+
|
7
|
+
group :development, :test do
|
8
|
+
# This is here because gemspec doesn't support require: false
|
9
|
+
gem "coveralls", :require => false
|
10
|
+
gem "netrc", :require => false
|
11
|
+
gem "octokit", :require => false
|
12
|
+
end
|
13
|
+
|
14
|
+
gemspec :path => "../"
|
@@ -0,0 +1,14 @@
|
|
1
|
+
source "https://rubygems.org"
|
2
|
+
|
3
|
+
gem 'nokogiri', '~>1.5.11'
|
4
|
+
gem 'mime-types', '~>1.16'
|
5
|
+
|
6
|
+
group :development, :test do
|
7
|
+
# This is here because gemspec doesn't support require: false
|
8
|
+
gem 'coveralls', :require => false
|
9
|
+
gem "netrc", :require => false
|
10
|
+
gem "octokit", '~> 2.7.2', :require => false
|
11
|
+
gem 'rake', '~> 10.1.0'
|
12
|
+
end
|
13
|
+
|
14
|
+
gemspec :path => "../"
|
data/lib/fog/softlayer.rb
CHANGED
@@ -101,7 +101,7 @@ module Fog
|
|
101
101
|
# default HTTP method to get if not passed
|
102
102
|
http_method = options[:http_method] || :get
|
103
103
|
# set the target base url
|
104
|
-
@request_url = options[:softlayer_api_url] || Fog::Softlayer::
|
104
|
+
@request_url = options[:softlayer_api_url] || Fog::Softlayer::SL_API_URL
|
105
105
|
# tack on the username and password
|
106
106
|
credentialize_url(@credentials[:username], @credentials[:api_key])
|
107
107
|
# set the SoftLayer Service name
|
@@ -25,7 +25,7 @@ module Fog
|
|
25
25
|
# Password for user based authentication
|
26
26
|
#
|
27
27
|
def initialize(options)
|
28
|
-
@api_url = options[:softlayer_api_url]
|
28
|
+
@api_url = options[:softlayer_api_url] || SL_API_URL
|
29
29
|
@credentials = { :username => options[:softlayer_username], :api_key => options[:softlayer_api_key] }
|
30
30
|
@default_domain = options[:softlayer_default_domain]
|
31
31
|
end
|
data/lib/fog/softlayer/core.rb
CHANGED
@@ -6,15 +6,18 @@
|
|
6
6
|
#
|
7
7
|
require 'fog/core'
|
8
8
|
require 'fog/json'
|
9
|
+
require 'fog/softlayer/version'
|
9
10
|
require 'time'
|
10
11
|
|
11
12
|
module Fog
|
12
13
|
module Softlayer
|
13
14
|
extend Fog::Provider
|
14
15
|
|
15
|
-
|
16
|
+
SL_API_URL = 'api.softlayer.com/rest/v3' unless defined? SL_API_URL
|
17
|
+
SL_STORAGE_AUTH_URL = 'objectstorage.softlayer.net/auth/v1.0' unless defined? SL_STORAGE_AUTH_URL
|
16
18
|
|
17
19
|
service(:compute, 'Compute')
|
20
|
+
service(:storage, 'Storage')
|
18
21
|
|
19
22
|
def self.mock_account_id
|
20
23
|
Fog.mocking? and @sl_account_id ||= Fog::Mock.random_numbers(7)
|
@@ -32,5 +35,18 @@ module Fog
|
|
32
35
|
required.all? {|k| k = k.to_sym; passed.key?(k) and !passed[k].nil?}
|
33
36
|
end
|
34
37
|
|
38
|
+
# CGI.escape, but without special treatment on spaces
|
39
|
+
def self.escape(str,extra_exclude_chars = '')
|
40
|
+
str.gsub(/([^a-zA-Z0-9_.-#{extra_exclude_chars}]+)/) do
|
41
|
+
'%' + $1.unpack('H2' * $1.bytesize).join('%').upcase
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
class Hash
|
48
|
+
def deep_merge(second)
|
49
|
+
merger = proc { |key, v1, v2| Hash === v1 && Hash === v2 ? v1.merge(v2, &merger) : v2 }
|
50
|
+
self.merge(second, &merger)
|
35
51
|
end
|
36
52
|
end
|
@@ -0,0 +1,40 @@
|
|
1
|
+
require 'fog/core/collection'
|
2
|
+
require 'fog/softlayer/models/storage/directory'
|
3
|
+
|
4
|
+
module Fog
|
5
|
+
module Storage
|
6
|
+
class Softlayer
|
7
|
+
|
8
|
+
class Directories < Fog::Collection
|
9
|
+
|
10
|
+
model Fog::Storage::Softlayer::Directory
|
11
|
+
|
12
|
+
def all
|
13
|
+
data = service.get_containers.body
|
14
|
+
load(data)
|
15
|
+
end
|
16
|
+
|
17
|
+
def get(key, options = {})
|
18
|
+
data = service.get_container(key, options)
|
19
|
+
directory = new(:key => key)
|
20
|
+
for key, value in data.headers
|
21
|
+
if ['X-Container-Bytes-Used', 'X-Container-Object-Count'].include?(key)
|
22
|
+
directory.merge_attributes(key => value)
|
23
|
+
end
|
24
|
+
end
|
25
|
+
directory.files.merge_attributes(options)
|
26
|
+
directory.files.instance_variable_set(:@loaded, true)
|
27
|
+
|
28
|
+
data.body.each do |file|
|
29
|
+
directory.files << directory.files.new(file)
|
30
|
+
end
|
31
|
+
directory
|
32
|
+
rescue Fog::Storage::Softlayer::NotFound
|
33
|
+
nil
|
34
|
+
end
|
35
|
+
|
36
|
+
end
|
37
|
+
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
@@ -0,0 +1,50 @@
|
|
1
|
+
require 'fog/core/model'
|
2
|
+
require 'fog/softlayer/models/storage/files'
|
3
|
+
|
4
|
+
module Fog
|
5
|
+
module Storage
|
6
|
+
class Softlayer
|
7
|
+
|
8
|
+
class Directory < Fog::Model
|
9
|
+
|
10
|
+
identity :key, :aliases => 'name'
|
11
|
+
|
12
|
+
attribute :bytes, :aliases => 'X-Container-Bytes-Used'
|
13
|
+
attribute :count, :aliases => 'X-Container-Object-Count'
|
14
|
+
|
15
|
+
def destroy
|
16
|
+
requires :key
|
17
|
+
service.delete_container(key)
|
18
|
+
true
|
19
|
+
rescue Excon::Errors::NotFound
|
20
|
+
false
|
21
|
+
end
|
22
|
+
|
23
|
+
def files
|
24
|
+
@files ||= begin
|
25
|
+
Fog::Storage::Softlayer::Files.new(
|
26
|
+
:directory => self,
|
27
|
+
:service => service
|
28
|
+
)
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
def public=(new_public)
|
33
|
+
@public = new_public
|
34
|
+
end
|
35
|
+
|
36
|
+
def public_url
|
37
|
+
raise NotImplementedError
|
38
|
+
end
|
39
|
+
|
40
|
+
def save
|
41
|
+
requires :key
|
42
|
+
service.put_container(key)
|
43
|
+
true
|
44
|
+
end
|
45
|
+
|
46
|
+
end
|
47
|
+
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
@@ -0,0 +1,166 @@
|
|
1
|
+
require 'fog/core/model'
|
2
|
+
|
3
|
+
module Fog
|
4
|
+
module Storage
|
5
|
+
class Softlayer
|
6
|
+
|
7
|
+
class File < Fog::Model
|
8
|
+
|
9
|
+
identity :key, :aliases => 'name'
|
10
|
+
|
11
|
+
attribute :content_length, :aliases => ['bytes', 'Content-Length'], :type => :integer
|
12
|
+
attribute :content_type, :aliases => ['content_type', 'Content-Type']
|
13
|
+
attribute :content_disposition, :aliases => ['content_disposition', 'Content-Disposition']
|
14
|
+
attribute :etag, :aliases => ['hash', 'Etag']
|
15
|
+
attribute :last_modified, :aliases => ['last_modified', 'Last-Modified'], :type => :time
|
16
|
+
attribute :access_control_allow_origin, :aliases => ['Access-Control-Allow-Origin']
|
17
|
+
attribute :origin, :aliases => ['Origin']
|
18
|
+
|
19
|
+
def body
|
20
|
+
attributes[:body] ||= if last_modified
|
21
|
+
collection.get(identity).body
|
22
|
+
else
|
23
|
+
''
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
def body=(new_body)
|
28
|
+
attributes[:body] = new_body
|
29
|
+
end
|
30
|
+
|
31
|
+
def directory
|
32
|
+
@directory
|
33
|
+
end
|
34
|
+
|
35
|
+
def copy(target_directory_key, target_file_key, options={})
|
36
|
+
requires :directory, :key
|
37
|
+
options['Content-Type'] ||= content_type if content_type
|
38
|
+
options['Access-Control-Allow-Origin'] ||= access_control_allow_origin if access_control_allow_origin
|
39
|
+
options['Origin'] ||= origin if origin
|
40
|
+
service.copy_object(directory.key, key, target_directory_key, target_file_key, options)
|
41
|
+
target_directory = service.directories.new(:key => target_directory_key)
|
42
|
+
target_directory.files.get(target_file_key)
|
43
|
+
end
|
44
|
+
|
45
|
+
def destroy
|
46
|
+
requires :directory, :key
|
47
|
+
service.delete_object(directory.key, key)
|
48
|
+
true
|
49
|
+
end
|
50
|
+
|
51
|
+
def metadata
|
52
|
+
@metadata ||= headers_to_metadata
|
53
|
+
end
|
54
|
+
|
55
|
+
def owner=(new_owner)
|
56
|
+
if new_owner
|
57
|
+
attributes[:owner] = {
|
58
|
+
:display_name => new_owner['DisplayName'],
|
59
|
+
:id => new_owner['ID']
|
60
|
+
}
|
61
|
+
end
|
62
|
+
end
|
63
|
+
|
64
|
+
def public=(new_public)
|
65
|
+
new_public
|
66
|
+
end
|
67
|
+
|
68
|
+
# Get a url for file.
|
69
|
+
#
|
70
|
+
# required attributes: key
|
71
|
+
#
|
72
|
+
# @param expires [String] number of seconds (since 1970-01-01 00:00) before url expires
|
73
|
+
# @param options [Hash]
|
74
|
+
# @return [String] url
|
75
|
+
#
|
76
|
+
def url(expires, options = {})
|
77
|
+
requires :directory, :key
|
78
|
+
self.service.create_temp_url(directory.key, key, expires, "GET", options)
|
79
|
+
end
|
80
|
+
|
81
|
+
def public_url
|
82
|
+
requires :key
|
83
|
+
self.collection.get_url(self.key)
|
84
|
+
end
|
85
|
+
|
86
|
+
|
87
|
+
def save(options = {})
|
88
|
+
requires :body, :directory, :key
|
89
|
+
options['Content-Type'] = content_type if content_type
|
90
|
+
options['Content-Disposition'] = content_disposition if content_disposition
|
91
|
+
options['Access-Control-Allow-Origin'] = access_control_allow_origin if access_control_allow_origin
|
92
|
+
options['Origin'] = origin if origin
|
93
|
+
options.merge!(metadata_to_headers)
|
94
|
+
|
95
|
+
data = service.put_object(directory.key, key, body, options)
|
96
|
+
update_attributes_from(data)
|
97
|
+
refresh_metadata
|
98
|
+
|
99
|
+
self.content_length = Fog::Storage.get_body_size(body)
|
100
|
+
self.content_type ||= Fog::Storage.get_content_type(body)
|
101
|
+
true
|
102
|
+
end
|
103
|
+
|
104
|
+
private
|
105
|
+
|
106
|
+
def directory=(new_directory)
|
107
|
+
@directory = new_directory
|
108
|
+
end
|
109
|
+
|
110
|
+
def refresh_metadata
|
111
|
+
metadata.reject! {|k, v| v.nil? }
|
112
|
+
end
|
113
|
+
|
114
|
+
def headers_to_metadata
|
115
|
+
key_map = key_mapping
|
116
|
+
Hash[metadata_attributes.map {|k, v| [key_map[k], v] }]
|
117
|
+
end
|
118
|
+
|
119
|
+
def key_mapping
|
120
|
+
key_map = metadata_attributes
|
121
|
+
key_map.each_pair {|k, v| key_map[k] = header_to_key(k)}
|
122
|
+
end
|
123
|
+
|
124
|
+
def header_to_key(opt)
|
125
|
+
opt.gsub(metadata_prefix, '').split('-').map {|k| k[0, 1].downcase + k[1..-1]}.join('_').to_sym
|
126
|
+
end
|
127
|
+
|
128
|
+
def metadata_to_headers
|
129
|
+
header_map = header_mapping
|
130
|
+
Hash[metadata.map {|k, v| [header_map[k], v] }]
|
131
|
+
end
|
132
|
+
|
133
|
+
def header_mapping
|
134
|
+
header_map = metadata.dup
|
135
|
+
header_map.each_pair {|k, v| header_map[k] = key_to_header(k)}
|
136
|
+
end
|
137
|
+
|
138
|
+
def key_to_header(key)
|
139
|
+
metadata_prefix + key.to_s.split(/[-_]/).map(&:capitalize).join('-')
|
140
|
+
end
|
141
|
+
|
142
|
+
def metadata_attributes
|
143
|
+
if last_modified
|
144
|
+
headers = service.head_object(directory.key, self.key).headers
|
145
|
+
headers.reject! {|k, v| !metadata_attribute?(k)}
|
146
|
+
else
|
147
|
+
{}
|
148
|
+
end
|
149
|
+
end
|
150
|
+
|
151
|
+
def metadata_attribute?(key)
|
152
|
+
key.to_s =~ /^#{metadata_prefix}/
|
153
|
+
end
|
154
|
+
|
155
|
+
def metadata_prefix
|
156
|
+
"X-Object-Meta-"
|
157
|
+
end
|
158
|
+
|
159
|
+
def update_attributes_from(data)
|
160
|
+
merge_attributes(data.headers.reject {|key, value| ['Content-Length', 'Content-Type'].include?(key)})
|
161
|
+
end
|
162
|
+
end
|
163
|
+
|
164
|
+
end
|
165
|
+
end
|
166
|
+
end
|
@@ -0,0 +1,99 @@
|
|
1
|
+
require 'fog/core/collection'
|
2
|
+
require 'fog/softlayer/models/storage/file'
|
3
|
+
|
4
|
+
module Fog
|
5
|
+
module Storage
|
6
|
+
class Softlayer
|
7
|
+
|
8
|
+
class Files < Fog::Collection
|
9
|
+
|
10
|
+
attribute :directory
|
11
|
+
attribute :limit
|
12
|
+
attribute :marker
|
13
|
+
attribute :path
|
14
|
+
attribute :prefix
|
15
|
+
|
16
|
+
model Fog::Storage::Softlayer::File
|
17
|
+
|
18
|
+
def all(options = {})
|
19
|
+
requires :directory
|
20
|
+
options = {
|
21
|
+
'limit' => limit,
|
22
|
+
'marker' => marker,
|
23
|
+
'path' => path,
|
24
|
+
'prefix' => prefix
|
25
|
+
}.merge!(options)
|
26
|
+
merge_attributes(options)
|
27
|
+
parent = directory.collection.get(
|
28
|
+
directory.key,
|
29
|
+
options
|
30
|
+
)
|
31
|
+
if parent
|
32
|
+
load(parent.files.map {|file| file.attributes})
|
33
|
+
else
|
34
|
+
nil
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
alias :each_file_this_page :each
|
39
|
+
def each
|
40
|
+
if !block_given?
|
41
|
+
self
|
42
|
+
else
|
43
|
+
subset = dup.all
|
44
|
+
|
45
|
+
subset.each_file_this_page {|f| yield f}
|
46
|
+
while subset.length == (subset.limit || 10000)
|
47
|
+
subset = subset.all(:marker => subset.last.key)
|
48
|
+
subset.each_file_this_page {|f| yield f}
|
49
|
+
end
|
50
|
+
|
51
|
+
self
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
def get(key, &block)
|
56
|
+
requires :directory
|
57
|
+
data = service.get_object(directory.key, key, &block)
|
58
|
+
file_data = data.headers.merge({
|
59
|
+
:body => data.body,
|
60
|
+
:key => key
|
61
|
+
})
|
62
|
+
new(file_data)
|
63
|
+
rescue Fog::Storage::Softlayer::NotFound
|
64
|
+
nil
|
65
|
+
end
|
66
|
+
|
67
|
+
def get_url(key)
|
68
|
+
requires :directory
|
69
|
+
if self.directory.public_url
|
70
|
+
"#{self.directory.public_url}/#{Fog::Softlayer.escape(key, '/')}"
|
71
|
+
end
|
72
|
+
end
|
73
|
+
|
74
|
+
def get_https_url(key, expires, options = {})
|
75
|
+
requires :directory
|
76
|
+
service.get_object_https_url(directory.key, key, expires, options)
|
77
|
+
end
|
78
|
+
|
79
|
+
def head(key, options = {})
|
80
|
+
requires :directory
|
81
|
+
data = service.head_object(directory.key, key)
|
82
|
+
file_data = data.headers.merge({
|
83
|
+
:key => key
|
84
|
+
})
|
85
|
+
new(file_data)
|
86
|
+
rescue Fog::Storage::Softlayer::NotFound
|
87
|
+
nil
|
88
|
+
end
|
89
|
+
|
90
|
+
def new(attributes = {})
|
91
|
+
requires :directory
|
92
|
+
super({ :directory => directory }.merge!(attributes))
|
93
|
+
end
|
94
|
+
|
95
|
+
end
|
96
|
+
|
97
|
+
end
|
98
|
+
end
|
99
|
+
end
|