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.
Files changed (48) hide show
  1. checksums.yaml +8 -8
  2. data/.travis.yml +37 -0
  3. data/CONTRIBUTING.md +30 -0
  4. data/README.md +8 -5
  5. data/docs/cla-corporate.md +133 -0
  6. data/docs/cla-individual.md +84 -0
  7. data/docs/fog-softlayer-CCLA.pdf +0 -0
  8. data/docs/fog-softlayer-CLA.pdf +0 -0
  9. data/examples/storage.md +107 -0
  10. data/fog-softlayer.gemspec +1 -0
  11. data/gemfiles/Gemfile-edge +14 -0
  12. data/gemfiles/Gemfile-ruby-1.8.7 +14 -0
  13. data/lib/fog/softlayer.rb +1 -0
  14. data/lib/fog/softlayer/compute.rb +1 -1
  15. data/lib/fog/softlayer/compute/shared.rb +1 -1
  16. data/lib/fog/softlayer/core.rb +17 -1
  17. data/lib/fog/softlayer/models/storage/directories.rb +40 -0
  18. data/lib/fog/softlayer/models/storage/directory.rb +50 -0
  19. data/lib/fog/softlayer/models/storage/file.rb +166 -0
  20. data/lib/fog/softlayer/models/storage/files.rb +99 -0
  21. data/lib/fog/softlayer/requests/storage/copy_object.rb +42 -0
  22. data/lib/fog/softlayer/requests/storage/delete_container.rb +38 -0
  23. data/lib/fog/softlayer/requests/storage/delete_multiple_objects.rb +67 -0
  24. data/lib/fog/softlayer/requests/storage/delete_object.rb +40 -0
  25. data/lib/fog/softlayer/requests/storage/delete_static_large_object.rb +43 -0
  26. data/lib/fog/softlayer/requests/storage/get_container.rb +68 -0
  27. data/lib/fog/softlayer/requests/storage/get_containers.rb +51 -0
  28. data/lib/fog/softlayer/requests/storage/get_object.rb +45 -0
  29. data/lib/fog/softlayer/requests/storage/get_object_https_url.rb +88 -0
  30. data/lib/fog/softlayer/requests/storage/head_container.rb +28 -0
  31. data/lib/fog/softlayer/requests/storage/head_containers.rb +25 -0
  32. data/lib/fog/softlayer/requests/storage/head_object.rb +23 -0
  33. data/lib/fog/softlayer/requests/storage/post_set_meta_temp_url_key.rb +37 -0
  34. data/lib/fog/softlayer/requests/storage/put_container.rb +32 -0
  35. data/lib/fog/softlayer/requests/storage/put_dynamic_obj_manifest.rb +43 -0
  36. data/lib/fog/softlayer/requests/storage/put_object.rb +61 -0
  37. data/lib/fog/softlayer/requests/storage/put_object_manifest.rb +16 -0
  38. data/lib/fog/softlayer/requests/storage/put_static_obj_manifest.rb +57 -0
  39. data/lib/fog/softlayer/storage.rb +283 -0
  40. data/lib/fog/softlayer/version.rb +1 -1
  41. data/tests/helper.rb +0 -29
  42. data/tests/helpers/mock_helper.rb +4 -98
  43. data/tests/softlayer/models/storage/directory_tests.rb +49 -0
  44. data/tests/softlayer/models/storage/file_tests.rb +56 -0
  45. data/tests/softlayer/requests/storage/auth_tests.rb +17 -0
  46. data/tests/softlayer/requests/storage/container_tests.rb +52 -0
  47. data/tests/softlayer/requests/storage/object_tests.rb +68 -0
  48. metadata +53 -2
@@ -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
@@ -7,3 +7,4 @@
7
7
  require 'fog'
8
8
  require 'fog/softlayer'
9
9
  require 'fog/softlayer/compute'
10
+ require 'fog/softlayer/storage'
@@ -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::API_URL
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] || 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
@@ -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
- API_URL = 'api.softlayer.com/rest/v3' unless defined? API_URL
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