fog-core 1.42.0 → 1.45.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.gitignore +2 -2
- data/.travis.yml +16 -9
- data/Gemfile +0 -2
- data/Gemfile.1.9.3 +8 -0
- data/README.md +12 -1
- data/changelog.md +44 -0
- data/fog-core.gemspec +5 -1
- data/lib/fog/compute/models/server.rb +39 -2
- data/lib/fog/compute.rb +3 -3
- data/lib/fog/core/cache.rb +290 -0
- data/lib/fog/core/collection.rb +0 -1
- data/lib/fog/core/model.rb +5 -0
- data/lib/fog/core/version.rb +1 -1
- data/spec/compute/models/server_spec.rb +229 -0
- data/spec/core/cache_spec.rb +191 -0
- data/spec/fog_attribute_spec.rb +5 -12
- data/spec/service_spec.rb +2 -1
- metadata +26 -7
- /data/{Gemfile18 → Gemfile.1.8.7} +0 -0
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 52a37f8e1b168c74d0d23ab2748a5217a99e2686
|
4
|
+
data.tar.gz: 97606f8e95a6969a93b597e4018d73a28476a966
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: d9f52e3fd097a95f90d8a9419499fd1ce5cb013c1ee2816aafacc150eb9903684eb72db7fe41243fa49fa7be43e2eba8f5d23282b3fa3e4eb8fdc773b7e1c528
|
7
|
+
data.tar.gz: 5133413d6aa3dc0004a70448ba63dc7042769cfb8609e8bcab0155ce2df7e02d0fa00c31672f64304029426a73011f8390a4145f07ac008ea04600c83b928845
|
data/.gitignore
CHANGED
data/.travis.yml
CHANGED
@@ -1,26 +1,33 @@
|
|
1
1
|
language: ruby
|
2
2
|
|
3
|
-
gemfile:
|
4
|
-
- Gemfile
|
5
|
-
|
6
3
|
rvm:
|
7
|
-
- 1
|
8
|
-
- 2.
|
9
|
-
- 2.
|
10
|
-
-
|
4
|
+
- 2.1
|
5
|
+
- 2.2
|
6
|
+
- 2.3
|
7
|
+
- 2.4
|
11
8
|
- jruby-head
|
12
9
|
|
13
10
|
sudo: false
|
11
|
+
dist: trusty
|
14
12
|
|
15
13
|
script: bundle exec rake travis
|
16
14
|
|
17
15
|
matrix:
|
16
|
+
fast_finish: true
|
18
17
|
include:
|
19
18
|
- rvm: 1.8.7
|
20
|
-
gemfile:
|
21
|
-
- rvm:
|
19
|
+
gemfile: Gemfile.1.8.7
|
20
|
+
- rvm: jruby-18mode
|
21
|
+
gemfile: Gemfile.1.8.7
|
22
|
+
- rvm: 1.9.3
|
23
|
+
gemfile: Gemfile.1.9.3
|
24
|
+
- rvm: jruby-19mode
|
25
|
+
gemfile: Gemfile.1.9.3
|
26
|
+
- rvm: 2.1
|
22
27
|
gemfile: Gemfile
|
23
28
|
env: COVERAGE=true
|
29
|
+
- rvm: jruby-head
|
30
|
+
gemfile: Gemfile
|
24
31
|
allow_failures:
|
25
32
|
- rvm: jruby-head
|
26
33
|
- rvm: jruby9k
|
data/Gemfile
CHANGED
data/Gemfile.1.9.3
ADDED
data/README.md
CHANGED
@@ -2,7 +2,7 @@
|
|
2
2
|
|
3
3
|
Shared classes and tests for fog providers and services.
|
4
4
|
|
5
|
-
[![Build Status](https://
|
5
|
+
[![Build Status](https://travis-ci.org/fog/fog-core.svg?branch=master)](https://travis-ci.org/fog/fog-core)
|
6
6
|
|
7
7
|
## Ruby version
|
8
8
|
|
@@ -25,6 +25,17 @@ Or install it yourself as:
|
|
25
25
|
|
26
26
|
$ gem install fog-core
|
27
27
|
|
28
|
+
### Ruby 1.9.3
|
29
|
+
|
30
|
+
Some of `fog-core`'s dependencies have dropped support for Ruby 1.9.3 in later
|
31
|
+
versions. Rather than limit all `fog` users to older but compatible versions,
|
32
|
+
if you are using 1.9.3 you will need to declare a compatible version in your
|
33
|
+
application's `Gemfile` like:
|
34
|
+
|
35
|
+
gem "net-ssh", "< 3.0"
|
36
|
+
|
37
|
+
See `Gemfile.1.9.3` for the definitive list as tested by Travis.
|
38
|
+
|
28
39
|
## Usage
|
29
40
|
|
30
41
|
TODO: Write usage instructions here
|
data/changelog.md
CHANGED
@@ -1,3 +1,47 @@
|
|
1
|
+
1.45.0 08/01/2017
|
2
|
+
==========================================================
|
3
|
+
|
4
|
+
remove xmlrpc requirement/usage
|
5
|
+
fix for nested const across ruby versions
|
6
|
+
remove array#sample usage for legacy ruby compatibility
|
7
|
+
simplify uniq for cache and fix for legacy ruby
|
8
|
+
remove debugging puts from cache
|
9
|
+
tweak tins version for 1.9
|
10
|
+
loosen 2.1.x travis config to 2.1
|
11
|
+
add 1.9 compatible term-ansicolor
|
12
|
+
fix rubocop for 1.9.3
|
13
|
+
enable metadata for cache
|
14
|
+
add specs for server#sshable
|
15
|
+
add exponential backoff for server#sshable?
|
16
|
+
add server#ready? to base server for clarity
|
17
|
+
bump excon dependency
|
18
|
+
|
19
|
+
1.44.3 05/25/2017
|
20
|
+
==========================================================
|
21
|
+
|
22
|
+
fix cache when no home directory defined
|
23
|
+
|
24
|
+
1.44.2 05/18/2017
|
25
|
+
==========================================================
|
26
|
+
|
27
|
+
fix homedirectory usage for caching
|
28
|
+
|
29
|
+
1.44.1 05/01/2017
|
30
|
+
==========================================================
|
31
|
+
|
32
|
+
remove xml-rpc dependency (as it is causing issues)
|
33
|
+
|
34
|
+
1.44.0 04/28/2017
|
35
|
+
==========================================================
|
36
|
+
|
37
|
+
add basic caching support
|
38
|
+
|
39
|
+
1.43.0 09/28/2016
|
40
|
+
==========================================================
|
41
|
+
|
42
|
+
fix digitalocean compatibility
|
43
|
+
update README badges
|
44
|
+
|
1
45
|
1.42.0 07/05/2016
|
2
46
|
==========================================================
|
3
47
|
|
data/fog-core.gemspec
CHANGED
@@ -19,9 +19,13 @@ Gem::Specification.new do |spec|
|
|
19
19
|
spec.require_paths = ["lib"]
|
20
20
|
|
21
21
|
spec.add_dependency("builder")
|
22
|
-
spec.add_dependency("excon", "~> 0.
|
22
|
+
spec.add_dependency("excon", "~> 0.58")
|
23
23
|
spec.add_dependency("formatador", "~> 0.2")
|
24
24
|
|
25
|
+
# https://github.com/fog/fog-core/issues/206
|
26
|
+
# spec.add_dependency("xmlrpc") if RUBY_VERSION.to_s >= "2.4"
|
27
|
+
|
28
|
+
spec.add_development_dependency("tins") if RUBY_VERSION.to_s > "2.0"
|
25
29
|
spec.add_development_dependency("coveralls")
|
26
30
|
spec.add_development_dependency("minitest")
|
27
31
|
spec.add_development_dependency("minitest-stub-const")
|
@@ -3,6 +3,8 @@ require "fog/core/model"
|
|
3
3
|
module Fog
|
4
4
|
module Compute
|
5
5
|
class Server < Fog::Model
|
6
|
+
INITIAL_SSHABLE_TIMEOUT = 8
|
7
|
+
|
6
8
|
attr_writer :username, :private_key, :private_key_path, :public_key, :public_key_path, :ssh_port, :ssh_options
|
7
9
|
# Sets the proc used to determine the IP Address used for ssh/scp interactions.
|
8
10
|
# @example
|
@@ -87,10 +89,45 @@ module Fog
|
|
87
89
|
end
|
88
90
|
|
89
91
|
def sshable?(options = {})
|
90
|
-
ready? && !ssh_ip_address.nil? && !!Timeout.timeout(
|
91
|
-
|
92
|
+
result = ready? && !ssh_ip_address.nil? && !!Timeout.timeout(sshable_timeout) { ssh("pwd", options) }
|
93
|
+
@sshable_timeout = nil
|
94
|
+
result
|
95
|
+
rescue SystemCallError
|
96
|
+
false
|
97
|
+
rescue Net::SSH::AuthenticationFailed, Net::SSH::Disconnect
|
98
|
+
@sshable_timeout = nil
|
99
|
+
false
|
100
|
+
rescue Timeout::Error
|
101
|
+
increase_sshable_timeout
|
102
|
+
false
|
103
|
+
end
|
104
|
+
|
105
|
+
# Is the server ready to receive connections?
|
106
|
+
#
|
107
|
+
# Returns false by default.
|
108
|
+
#
|
109
|
+
# Subclasses should implement #ready? appropriately.
|
110
|
+
def ready?
|
92
111
|
false
|
93
112
|
end
|
113
|
+
|
114
|
+
private
|
115
|
+
|
116
|
+
def sshable_timeout
|
117
|
+
@sshable_timeout ||= increase_sshable_timeout
|
118
|
+
end
|
119
|
+
|
120
|
+
def increase_sshable_timeout
|
121
|
+
if defined?(@sshable_timeout) && @sshable_timeout
|
122
|
+
if @sshable_timeout >= 40
|
123
|
+
@sshable_timeout = 60
|
124
|
+
else
|
125
|
+
@sshable_timeout *= 1.5
|
126
|
+
end
|
127
|
+
else
|
128
|
+
@sshable_timeout = INITIAL_SSHABLE_TIMEOUT
|
129
|
+
end
|
130
|
+
end
|
94
131
|
end
|
95
132
|
end
|
96
133
|
end
|
data/lib/fog/compute.rb
CHANGED
@@ -32,11 +32,11 @@ module Fog
|
|
32
32
|
version = attributes.delete(:version)
|
33
33
|
version = version.to_s.downcase.to_sym unless version.nil?
|
34
34
|
if version == :v1
|
35
|
-
error_message =
|
35
|
+
error_message = 'DigitalOcean V1 is deprecated.Please use `:version => :v2` attribute to use Next Gen Cloud Servers.'
|
36
36
|
raise error_message
|
37
37
|
else
|
38
|
-
require
|
39
|
-
Fog::Compute::
|
38
|
+
require 'fog/digitalocean/compute'
|
39
|
+
Fog::Compute::DigitalOcean.new(attributes)
|
40
40
|
end
|
41
41
|
when :stormondemand
|
42
42
|
require "fog/compute/storm_on_demand"
|
@@ -0,0 +1,290 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
require "fileutils"
|
3
|
+
require "yaml"
|
4
|
+
require "tmpdir"
|
5
|
+
|
6
|
+
module Fog
|
7
|
+
# A generic cache mechanism for fog resources. This can be for a server, security group, etc.
|
8
|
+
#
|
9
|
+
# Currently this is a on-disk cache using yml files per-model instance, however
|
10
|
+
# there is nothing in the way of extending this to use various other cache
|
11
|
+
# backends.
|
12
|
+
#
|
13
|
+
# == Basic functionality
|
14
|
+
#
|
15
|
+
# set the namespace where this cache will be stored:
|
16
|
+
#
|
17
|
+
# Fog::Cache.namespace_prefix = "service-account-foo-region-bar"
|
18
|
+
#
|
19
|
+
# cache to disk:
|
20
|
+
#
|
21
|
+
# # after dumping, there will be a yml file on disk:
|
22
|
+
# resouce.cache.dump
|
23
|
+
#
|
24
|
+
# # you can load cached data in from a different session
|
25
|
+
# Fog::Cache.load(Fog::Compute::AWS::Server, compute)
|
26
|
+
#
|
27
|
+
# # you can also expire cache (removes cached data assocaited with the resources of this model associated to the service passed in).
|
28
|
+
# Fog::Cache.expire_cache!(Fog::Compute::AWS::Server, compute)
|
29
|
+
#
|
30
|
+
# == More detailed flow/usage
|
31
|
+
#
|
32
|
+
# Normally, you would have a bunch of resources you want to cache/reload from disk.
|
33
|
+
# Every fog model has a +cache+ object injected to accomplish this. So in order to cache a server for exmaple
|
34
|
+
# you would do something like this:
|
35
|
+
#
|
36
|
+
# # note this is necessary in order to segregate usage of cache between various providers regions and accounts.
|
37
|
+
# # if you are using one account/region/etc only, you still must set it. 'default' will do.
|
38
|
+
# Fog::Cache.namespace_prefix = "prod-emea-eu-west-1"
|
39
|
+
#
|
40
|
+
# s = security_groups.sample; s.name # => "default"
|
41
|
+
# s.cache.dump # => 2371
|
42
|
+
#
|
43
|
+
# Now it is on disk:
|
44
|
+
#
|
45
|
+
# shai@adsk-lappy ~ % tree ~/.fog-cache/prod-emea-eu-west-1/
|
46
|
+
#
|
47
|
+
# /Users/shai/.fog-cache/prod-emea-eu-west-1/
|
48
|
+
# └── fog_compute_aws_real
|
49
|
+
# └── fog_compute_aws_securitygroup
|
50
|
+
# ├── default-90928073d9d5d9b4e7545e88aee7ec4f.yml
|
51
|
+
#
|
52
|
+
# You can do the same with a SecurityGroup, Instances, Elbs, etc.
|
53
|
+
#
|
54
|
+
# Note that when loading cache from disk, you need to pass the appropriate model class, and service associated with it.
|
55
|
+
# +Service+ is passed in is so that the service/connection details can be loaded into the loaded instances so they can be re-queried, etc.
|
56
|
+
# +Model+ is passed in so we can find the cache data associated to that model in the namespace of cache this session is using:
|
57
|
+
# Will try to load all resources associated to those. If you had 1 yml file, or 100, it would load whatever it could find.
|
58
|
+
# As such, the normal usage of dumping would be do it on a collection:
|
59
|
+
#
|
60
|
+
# load_balancers.each {|elb| elb.cache.dump }
|
61
|
+
#
|
62
|
+
# In order to load the cache into a different session with nothing but the service set up, use like so:
|
63
|
+
# As mentioned, will load all resources associated to the +model_klass+ and +service+ passed in.
|
64
|
+
#
|
65
|
+
# instances = Fog::Cache.load(Fog::Compute::AWS::Server, compute)
|
66
|
+
# instances.first.id # => "i-0569a70ae6f47d229"
|
67
|
+
#
|
68
|
+
# Note that if there is no cache located for the +model+ class and +service+ passed to `Fog::Cache.load`
|
69
|
+
# you will get an exception you can handle (for example, to load the resources for the fisrt time):
|
70
|
+
#
|
71
|
+
# Fog::Cache.expire_cache!(Fog::Compute::AWS::SecurityGroup, compute)
|
72
|
+
# # ... now there is no SecurityGroup cache data. So, if you tried to load it, you would get an exception:
|
73
|
+
#
|
74
|
+
# Fog::Cache.load(Fog::Compute::AWS::SecurityGroup, compute)
|
75
|
+
# rescue Fog::Cache::CacheNotFound => e
|
76
|
+
# puts "could not find any cache data for security groups on #{compute}"
|
77
|
+
# get_resources_and_dump
|
78
|
+
#
|
79
|
+
# == Extending cache backends
|
80
|
+
#
|
81
|
+
# Currently this is on-disk using yml. If need be, this could be extended to other cache backends:
|
82
|
+
#
|
83
|
+
# Find references of yaml in this file, split out to strategy objects/diff backends etc.
|
84
|
+
class Cache
|
85
|
+
|
86
|
+
# cache associated not found
|
87
|
+
class CacheNotFound < StandardError; end
|
88
|
+
|
89
|
+
# cache directory problem
|
90
|
+
class CacheDir < StandardError; end
|
91
|
+
|
92
|
+
# where different caches per +service+ api keys, regions etc, are stored
|
93
|
+
# see the +namespace_prefix=+ method.
|
94
|
+
SANDBOX = ENV["HOME"] ? File.expand_path("~/.fog-cache") : File.expand_path(".fog-cache")
|
95
|
+
|
96
|
+
# when a resource is used such as `server.cache.dump` the model klass is passed in
|
97
|
+
# so that it can be identified from a different session.
|
98
|
+
attr_reader :model
|
99
|
+
|
100
|
+
# Loads cache associated to the +model_klass+ and +service+ into memory.
|
101
|
+
#
|
102
|
+
# If no cache is found, it will raise an error for handling:
|
103
|
+
#
|
104
|
+
# rescue Fog::Cache::CacheNotFound
|
105
|
+
# set_initial_cache
|
106
|
+
#
|
107
|
+
def self.load(model_klass, service)
|
108
|
+
cache_files = Dir.glob("#{namespace(model_klass, service)}/*")
|
109
|
+
|
110
|
+
raise CacheNotFound if cache_files.empty?
|
111
|
+
|
112
|
+
# collection_klass and model_klass should be the same across all instances
|
113
|
+
# choose a valid cache record from the dump to use as a sample to deterine
|
114
|
+
# which collection/model to instantiate.
|
115
|
+
sample_path = cache_files.detect{ |path| valid_for_load?(path) }
|
116
|
+
model_klass = const_get_compat(load_cache(sample_path)[:model_klass])
|
117
|
+
collection_klass = const_get_compat(load_cache(sample_path)[:collection_klass]) if load_cache(sample_path)[:collection_klass]
|
118
|
+
|
119
|
+
# Load the cache data into actual ruby instances
|
120
|
+
loaded = cache_files.map do |path|
|
121
|
+
model_klass.new(load_cache(path)[:attrs]) if valid_for_load?(path)
|
122
|
+
end.compact
|
123
|
+
|
124
|
+
# Set the collection and service so they can be reloaded/connection is set properly.
|
125
|
+
# See https://github.com/fog/fog-aws/issues/354#issuecomment-286789702
|
126
|
+
loaded.each do |i|
|
127
|
+
i.collection = collection_klass.new(:service => service) if collection_klass
|
128
|
+
i.instance_variable_set(:@service, service)
|
129
|
+
end
|
130
|
+
|
131
|
+
# uniqe-ify based on the total of attributes. duplicate cache can exist due to
|
132
|
+
# `model#identity` not being unique. but if all attributes match, they are unique
|
133
|
+
# and shouldn't be loaded again.
|
134
|
+
uniq_loaded = uniq_w_block(loaded) { |i| i.attributes }
|
135
|
+
if uniq_loaded.size != loaded.size
|
136
|
+
Fog::Logger.warning("Found duplicate items in the cache. Expire all & refresh cache soon.")
|
137
|
+
end
|
138
|
+
|
139
|
+
# Fog models created, free memory of cached data used for creation.
|
140
|
+
@memoized = nil
|
141
|
+
|
142
|
+
uniq_loaded
|
143
|
+
end
|
144
|
+
|
145
|
+
# :nodoc: compatability for 1.8.7 1.9.3
|
146
|
+
def self.const_get_compat(strklass)
|
147
|
+
# https://stackoverflow.com/questions/3163641/get-a-class-by-name-in-ruby
|
148
|
+
strklass.split('::').inject(Object) do |mod, class_name|
|
149
|
+
mod.const_get(class_name)
|
150
|
+
end
|
151
|
+
end
|
152
|
+
|
153
|
+
# :nodoc: compatability for 1.8.7 1.9.3
|
154
|
+
def self.uniq_w_block(arr)
|
155
|
+
ret, keys = [], []
|
156
|
+
arr.each do |x|
|
157
|
+
key = block_given? ? yield(x) : x
|
158
|
+
unless keys.include? key
|
159
|
+
ret << x
|
160
|
+
keys << key
|
161
|
+
end
|
162
|
+
end
|
163
|
+
ret
|
164
|
+
end
|
165
|
+
|
166
|
+
# method to determine if a path can be loaded and is valid fog cache format.
|
167
|
+
def self.valid_for_load?(path)
|
168
|
+
data = load_cache(path)
|
169
|
+
if data && data.is_a?(Hash)
|
170
|
+
if [:identity, :model_klass, :collection_klass, :attrs].all? { |k| data.keys.include?(k) }
|
171
|
+
return true
|
172
|
+
else
|
173
|
+
Fog::Logger.warning("Found corrupt items in the cache: #{path}. Expire all & refresh cache soon.\n\nData:#{File.read(path)}")
|
174
|
+
return false
|
175
|
+
end
|
176
|
+
end
|
177
|
+
end
|
178
|
+
|
179
|
+
# creates on-disk cache of this specific +model_klass+ and +@service+
|
180
|
+
def self.create_namespace(model_klass, service)
|
181
|
+
FileUtils.mkdir_p(self.namespace(model_klass, service))
|
182
|
+
end
|
183
|
+
|
184
|
+
# Expires cache - this does not expire all cache associated.
|
185
|
+
# Instead, this will remove all on-disk cache of this specific +model_klass+ and and +@service+
|
186
|
+
def self.expire_cache!(model_klass, service)
|
187
|
+
FileUtils.rm_rf(namespace(model_klass, service))
|
188
|
+
end
|
189
|
+
|
190
|
+
# cleans the `SANDBOX` - specific any resource cache of any namespace,
|
191
|
+
# and any metadata associated to any.
|
192
|
+
def self.clean!
|
193
|
+
FileUtils.rm_rf(SANDBOX)
|
194
|
+
end
|
195
|
+
|
196
|
+
# loads yml cache from path on disk, used
|
197
|
+
# to initialize Fog models.
|
198
|
+
def self.load_cache(path)
|
199
|
+
@memoized ||= {}
|
200
|
+
return @memoized[path] if @memoized[path]
|
201
|
+
@memoized[path] = YAML.load(File.read(path))
|
202
|
+
end
|
203
|
+
|
204
|
+
def self.namespace_prefix=(name)
|
205
|
+
@namespace_prefix = name
|
206
|
+
end
|
207
|
+
|
208
|
+
def self.namespace_prefix
|
209
|
+
@namespace_prefix
|
210
|
+
end
|
211
|
+
|
212
|
+
# write any metadata - +hash+ information - specific to the namespaced cache in the session.
|
213
|
+
#
|
214
|
+
# you can retrieve this in other sessions, as long as +namespace_prefix+ is set
|
215
|
+
# you can overwrite metadata over time. see test cases as examples.
|
216
|
+
def self.write_metadata(h)
|
217
|
+
if namespace_prefix.nil?
|
218
|
+
raise CacheDir.new("Must set an explicit identifier/name for this cache. Example: 'serviceX-regionY'") unless namespace_prefix
|
219
|
+
elsif !h.is_a?(Hash)
|
220
|
+
raise CacheDir.new("metadta must be a hash of information like {:foo => 'bar'}")
|
221
|
+
end
|
222
|
+
|
223
|
+
mpath = File.join(SANDBOX, namespace_prefix, "metadata.yml")
|
224
|
+
to_write = if File.exist?(mpath)
|
225
|
+
YAML.dump(YAML.load(File.read(mpath)).merge!(h))
|
226
|
+
else
|
227
|
+
YAML.dump(h)
|
228
|
+
end
|
229
|
+
|
230
|
+
mdir = File.join(SANDBOX, namespace_prefix)
|
231
|
+
FileUtils.mkdir_p(mdir) if !File.exist?(mdir)
|
232
|
+
|
233
|
+
File.open(mpath, "w") { |f| f.write(to_write) }
|
234
|
+
end
|
235
|
+
|
236
|
+
# retrive metadata for this +namespace+ of cache. returns empty {} if none found.
|
237
|
+
def self.metadata
|
238
|
+
mpath = File.join(SANDBOX, namespace_prefix, "metadata.yml")
|
239
|
+
if File.exist?(mpath)
|
240
|
+
metadata = YAML.load(File.read(mpath))
|
241
|
+
return metadata
|
242
|
+
else
|
243
|
+
return {}
|
244
|
+
end
|
245
|
+
end
|
246
|
+
|
247
|
+
# The path/namespace where the cache is stored for a specific +model_klass+ and +@service+.
|
248
|
+
def self.namespace(model_klass, service)
|
249
|
+
|
250
|
+
raise CacheDir.new("Must set an explicit identifier/name for this cache. Example: 'serviceX-regionY'") unless namespace_prefix
|
251
|
+
|
252
|
+
ns = File.join(SANDBOX, namespace_prefix, service.class.to_s, model_klass.to_s)
|
253
|
+
ns = safe_path(ns)
|
254
|
+
end
|
255
|
+
|
256
|
+
def self.safe_path(klass)
|
257
|
+
klass.to_s.gsub("::", "_").downcase
|
258
|
+
end
|
259
|
+
|
260
|
+
def initialize(model)
|
261
|
+
@model = model
|
262
|
+
end
|
263
|
+
|
264
|
+
# Dump a Fog::Model resource. Every fog model/instance now has a +cache+ method/object injected in.
|
265
|
+
# as such you can use the #dump method to save the attributes and metadata of that instance as cache
|
266
|
+
# which can be re-used in some other session.
|
267
|
+
def dump
|
268
|
+
if !File.exist?(self.class.namespace(model.class, model.service))
|
269
|
+
self.class.create_namespace(model.class, model.service)
|
270
|
+
end
|
271
|
+
|
272
|
+
data = { :identity => model.identity,
|
273
|
+
:model_klass => model.class.to_s,
|
274
|
+
:collection_klass => model.collection && model.collection.class.to_s,
|
275
|
+
:attrs => model.attributes }
|
276
|
+
|
277
|
+
File.open(dump_to, "w") { |f| f.write(YAML.dump(data)) }
|
278
|
+
end
|
279
|
+
|
280
|
+
# the location of where to save this fog model/instance to.
|
281
|
+
def dump_to
|
282
|
+
# some fog models have an identity field that is duplicate.
|
283
|
+
# duplicate identities can mean the cache for that already exists.
|
284
|
+
# this means cache duplication is possible.
|
285
|
+
#
|
286
|
+
# see "dumping two models that have duplicate identity" test case.
|
287
|
+
"#{self.class.namespace(model.class, model.service)}/#{model.identity}-#{SecureRandom.hex}.yml"
|
288
|
+
end
|
289
|
+
end
|
290
|
+
end
|
data/lib/fog/core/collection.rb
CHANGED
data/lib/fog/core/model.rb
CHANGED
@@ -1,4 +1,5 @@
|
|
1
1
|
require "fog/core/deprecated_connection_accessors"
|
2
|
+
require "fog/core/cache"
|
2
3
|
|
3
4
|
module Fog
|
4
5
|
class Model
|
@@ -20,6 +21,10 @@ module Fog
|
|
20
21
|
merge_attributes(attribs)
|
21
22
|
end
|
22
23
|
|
24
|
+
def cache
|
25
|
+
Fog::Cache.new(self)
|
26
|
+
end
|
27
|
+
|
23
28
|
def inspect
|
24
29
|
Fog::Formatador.format(self)
|
25
30
|
end
|
data/lib/fog/core/version.rb
CHANGED
@@ -0,0 +1,229 @@
|
|
1
|
+
require "spec_helper"
|
2
|
+
require "fog/compute/models/server"
|
3
|
+
|
4
|
+
describe Fog::Compute::Server do
|
5
|
+
before do
|
6
|
+
@server = Fog::Compute::Server.new
|
7
|
+
end
|
8
|
+
|
9
|
+
describe "#sshable?" do
|
10
|
+
describe "when the server is not ready" do
|
11
|
+
it "is false" do
|
12
|
+
@server.stub(:ready?, false) do
|
13
|
+
refute @server.sshable?
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
describe "when the server is ready" do
|
19
|
+
describe "when the ssh_ip_address is nil" do
|
20
|
+
it "is false" do
|
21
|
+
@server.stub(:ready?, true) do
|
22
|
+
@server.stub(:ssh_ip_address, nil) do
|
23
|
+
refute @server.sshable?
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
|
30
|
+
describe "when the ssh_ip_address exists" do
|
31
|
+
# Define these constants which would be imported by net-ssh once loaded
|
32
|
+
module Net
|
33
|
+
module SSH
|
34
|
+
class AuthenticationFailed < RuntimeError
|
35
|
+
end
|
36
|
+
class Disconnect < RuntimeError
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
describe "and ssh times out" do
|
42
|
+
it "is false" do
|
43
|
+
@server.stub(:ready?, true) do
|
44
|
+
@server.stub(:ssh_ip_address, "10.0.0.1") do
|
45
|
+
raises_timeout = lambda { |_time| raise Timeout::Error.new }
|
46
|
+
Timeout.stub(:timeout, raises_timeout) do
|
47
|
+
refute @server.sshable?
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
describe "and it raises Net::SSH::AuthenticationFailed" do
|
55
|
+
it "is false" do
|
56
|
+
@server.stub(:ready?, true) do
|
57
|
+
@server.stub(:ssh_ip_address, "10.0.0.1") do
|
58
|
+
raise_error = lambda { |_cmd, _options| raise Net::SSH::AuthenticationFailed.new }
|
59
|
+
@server.stub(:ssh, raise_error) do
|
60
|
+
refute @server.sshable?
|
61
|
+
end
|
62
|
+
end
|
63
|
+
end
|
64
|
+
end
|
65
|
+
|
66
|
+
it "resets SSH timeout" do
|
67
|
+
@server.instance_variable_set(:@sshable_timeout, 8)
|
68
|
+
@server.stub(:ready?, true) do
|
69
|
+
@server.stub(:ssh_ip_address, "10.0.0.1") do
|
70
|
+
raise_error = lambda { |_cmd, _options| raise Net::SSH::AuthenticationFailed.new }
|
71
|
+
@server.stub(:ssh, raise_error) do
|
72
|
+
@server.sshable?
|
73
|
+
assert_nil @server.instance_variable_get(:@sshable_timeout), nil
|
74
|
+
end
|
75
|
+
end
|
76
|
+
end
|
77
|
+
end
|
78
|
+
end
|
79
|
+
|
80
|
+
describe "and it raises Net::SSH::Disconnect" do
|
81
|
+
it "is false" do
|
82
|
+
@server.stub(:ready?, true) do
|
83
|
+
@server.stub(:ssh_ip_address, "10.0.0.1") do
|
84
|
+
raise_error = lambda { |_cmd, _options| raise Net::SSH::Disconnect.new }
|
85
|
+
@server.stub(:ssh, raise_error) do
|
86
|
+
refute @server.sshable?
|
87
|
+
end
|
88
|
+
end
|
89
|
+
end
|
90
|
+
end
|
91
|
+
|
92
|
+
it "resets SSH timeout" do
|
93
|
+
@server.instance_variable_set(:@sshable_timeout, 8)
|
94
|
+
@server.stub(:ready?, true) do
|
95
|
+
@server.stub(:ssh_ip_address, "10.0.0.1") do
|
96
|
+
raise_error = lambda { |_cmd, _options| raise Net::SSH::Disconnect.new }
|
97
|
+
@server.stub(:ssh, raise_error) do
|
98
|
+
@server.sshable?
|
99
|
+
assert_nil @server.instance_variable_get(:@sshable_timeout), nil
|
100
|
+
end
|
101
|
+
end
|
102
|
+
end
|
103
|
+
end
|
104
|
+
end
|
105
|
+
|
106
|
+
describe "and it raises SystemCallError" do
|
107
|
+
it "is false" do
|
108
|
+
@server.stub(:ready?, true) do
|
109
|
+
@server.stub(:ssh_ip_address, "10.0.0.1") do
|
110
|
+
raise_error = lambda { |_cmd, _options| raise SystemCallError.new("message, 0") }
|
111
|
+
@server.stub(:ssh, raise_error) do
|
112
|
+
refute @server.sshable?
|
113
|
+
end
|
114
|
+
end
|
115
|
+
end
|
116
|
+
end
|
117
|
+
|
118
|
+
it "does not increase SSH timeout" do
|
119
|
+
@server.stub(:ready?, true) do
|
120
|
+
@server.stub(:ssh_ip_address, "10.0.0.1") do
|
121
|
+
raise_error = lambda { |_cmd, _options| raise SystemCallError.new("message, 0") }
|
122
|
+
@server.stub(:ssh, raise_error) do
|
123
|
+
@server.sshable?
|
124
|
+
assert_equal @server.instance_variable_get(:@sshable_timeout), 8
|
125
|
+
end
|
126
|
+
end
|
127
|
+
end
|
128
|
+
end
|
129
|
+
end
|
130
|
+
|
131
|
+
describe "and ssh completes within the designated timeout" do
|
132
|
+
it "is true" do
|
133
|
+
@server.stub(:ready?, true) do
|
134
|
+
@server.stub(:ssh_ip_address, "10.0.0.1") do
|
135
|
+
@server.stub(:ssh, "datum") do
|
136
|
+
assert @server.sshable?
|
137
|
+
end
|
138
|
+
end
|
139
|
+
end
|
140
|
+
end
|
141
|
+
end
|
142
|
+
|
143
|
+
describe "when called successively" do
|
144
|
+
describe "and ssh times out" do
|
145
|
+
it "increases the timeout factor by 1.5" do
|
146
|
+
@server.stub(:ready?, true) do
|
147
|
+
@server.stub(:ssh_ip_address, "10.0.0.1") do
|
148
|
+
raises_timeout = lambda do |time|
|
149
|
+
assert(time == 8)
|
150
|
+
raise Timeout::Error.new
|
151
|
+
end
|
152
|
+
Timeout.stub(:timeout, raises_timeout) do
|
153
|
+
refute @server.sshable?
|
154
|
+
end
|
155
|
+
|
156
|
+
raises_timeout = lambda do |time|
|
157
|
+
assert_equal(12, time)
|
158
|
+
raise Timeout::Error.new
|
159
|
+
end
|
160
|
+
Timeout.stub(:timeout, raises_timeout) do
|
161
|
+
refute @server.sshable?
|
162
|
+
end
|
163
|
+
end
|
164
|
+
end
|
165
|
+
end
|
166
|
+
|
167
|
+
it "does not increase timeout beyond 60s" do
|
168
|
+
@server.stub(:ready?, true) do
|
169
|
+
@server.stub(:ssh_ip_address, "10.0.0.1") do
|
170
|
+
raises_timeout = lambda { |_time| raise Timeout::Error.new }
|
171
|
+
Timeout.stub(:timeout, raises_timeout) do
|
172
|
+
5.times { refute @server.sshable? }
|
173
|
+
end
|
174
|
+
|
175
|
+
raises_timeout = lambda do |time|
|
176
|
+
assert_equal(60, time)
|
177
|
+
raise Timeout::Error.new
|
178
|
+
end
|
179
|
+
Timeout.stub(:timeout, raises_timeout) do
|
180
|
+
refute @server.sshable?
|
181
|
+
end
|
182
|
+
|
183
|
+
raises_timeout = lambda do |time|
|
184
|
+
assert_equal(60, time)
|
185
|
+
raise Timeout::Error.new
|
186
|
+
end
|
187
|
+
Timeout.stub(:timeout, raises_timeout) do
|
188
|
+
refute @server.sshable?
|
189
|
+
end
|
190
|
+
end
|
191
|
+
end
|
192
|
+
end
|
193
|
+
|
194
|
+
describe "when ssh eventually succeeds" do
|
195
|
+
it "resets the timeout to the initial value" do
|
196
|
+
@server.stub(:ready?, true) do
|
197
|
+
@server.stub(:ssh_ip_address, "10.0.0.1") do
|
198
|
+
raises_timeout = lambda do |time|
|
199
|
+
assert(time == 8)
|
200
|
+
raise Timeout::Error.new
|
201
|
+
end
|
202
|
+
Timeout.stub(:timeout, raises_timeout) do
|
203
|
+
refute @server.sshable?
|
204
|
+
end
|
205
|
+
|
206
|
+
@server.stub(:ssh, "datum") do
|
207
|
+
assert @server.sshable?
|
208
|
+
end
|
209
|
+
|
210
|
+
raises_timeout = lambda do |time|
|
211
|
+
assert_equal(8, time)
|
212
|
+
raise Timeout::Error.new
|
213
|
+
end
|
214
|
+
Timeout.stub(:timeout, raises_timeout) do
|
215
|
+
refute @server.sshable?
|
216
|
+
end
|
217
|
+
end
|
218
|
+
end
|
219
|
+
end
|
220
|
+
end
|
221
|
+
|
222
|
+
end
|
223
|
+
end
|
224
|
+
|
225
|
+
end
|
226
|
+
end
|
227
|
+
|
228
|
+
end
|
229
|
+
end
|
@@ -0,0 +1,191 @@
|
|
1
|
+
require "spec_helper"
|
2
|
+
require "securerandom"
|
3
|
+
require "tmpdir"
|
4
|
+
|
5
|
+
module Fog
|
6
|
+
class SubFogTestModel < Fog::Model
|
7
|
+
identity :id
|
8
|
+
end
|
9
|
+
end
|
10
|
+
|
11
|
+
module Fog
|
12
|
+
class SubFogTestService < Fog::Service
|
13
|
+
|
14
|
+
class Mock
|
15
|
+
attr_reader :options
|
16
|
+
|
17
|
+
def initialize(opts = {})
|
18
|
+
@options = opts
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
describe Fog::Cache do
|
25
|
+
before(:each) do
|
26
|
+
Fog.mock!
|
27
|
+
@service = Fog::SubFogTestService.new
|
28
|
+
Fog::Cache.namespace_prefix = "test-dir"
|
29
|
+
end
|
30
|
+
|
31
|
+
it "has a namespace_prefix configurable" do
|
32
|
+
Fog::Cache.namespace_prefix = "for-service-user-region-foo"
|
33
|
+
|
34
|
+
# Expand path does not downcase. case insensitive platform tests.
|
35
|
+
example_cache = File.expand_path(Fog::Cache.namespace(Fog::SubFogTestModel, @service)).downcase
|
36
|
+
expected_namespace = File.expand_path("~/.fog-cache/for-service-user-region-foo").downcase
|
37
|
+
|
38
|
+
assert_equal example_cache.include?(expected_namespace), true
|
39
|
+
end
|
40
|
+
|
41
|
+
it "has metadata associated to the namespace that you can save to" do
|
42
|
+
Fog::Cache.clean!
|
43
|
+
Fog::Cache.namespace_prefix = "for-service-user-region-foo"
|
44
|
+
# nothing exists, nothing comes back
|
45
|
+
assert_equal Fog::Cache.metadata, {}
|
46
|
+
# write/read
|
47
|
+
Fog::Cache.write_metadata({:last_dumped => "Tuesday, November 8, 2016"})
|
48
|
+
assert_equal Fog::Cache.metadata[:last_dumped], "Tuesday, November 8, 2016"
|
49
|
+
|
50
|
+
# diff namespace, diff metadata
|
51
|
+
Fog::Cache.namespace_prefix = "different-namespace"
|
52
|
+
assert_nil Fog::Cache.metadata[:last_dumped]
|
53
|
+
# still accessible per namespace
|
54
|
+
Fog::Cache.namespace_prefix = "for-service-user-region-foo"
|
55
|
+
assert_equal Fog::Cache.metadata[:last_dumped], "Tuesday, November 8, 2016"
|
56
|
+
# can overwrite
|
57
|
+
Fog::Cache.write_metadata({:last_dumped => "Diff date"})
|
58
|
+
assert_equal Fog::Cache.metadata[:last_dumped], "Diff date"
|
59
|
+
|
60
|
+
# can't write a non-hash/data entry.
|
61
|
+
assert_raises Fog::Cache::CacheDir do
|
62
|
+
Fog::Cache.write_metadata("boo")
|
63
|
+
end
|
64
|
+
|
65
|
+
# namespace must be set as well.
|
66
|
+
assert_raises Fog::Cache::CacheDir do
|
67
|
+
Fog::Cache.namespace_prefix = nil
|
68
|
+
Fog::Cache.write_metadata({:a => "b"})
|
69
|
+
end
|
70
|
+
|
71
|
+
end
|
72
|
+
|
73
|
+
it "can load cache data from disk" do
|
74
|
+
path = File.expand_path("~/.fog-cache-test-#{Time.now.to_i}.yml")
|
75
|
+
data = "--- ok\n...\n"
|
76
|
+
File.open(path, "w") { |f|
|
77
|
+
f.write(data)
|
78
|
+
}
|
79
|
+
|
80
|
+
assert_equal "ok", Fog::Cache.load_cache(path)
|
81
|
+
end
|
82
|
+
|
83
|
+
it "load bad cache data - empty file, from disk" do
|
84
|
+
path = File.expand_path("~/.fog-cache-test-2-#{Time.now.to_i}.yml")
|
85
|
+
data = ""
|
86
|
+
File.open(path, "w") { |f|
|
87
|
+
f.write(data)
|
88
|
+
}
|
89
|
+
|
90
|
+
assert_equal false, Fog::Cache.load_cache(path)
|
91
|
+
end
|
92
|
+
|
93
|
+
it "must have a namespace_prefix configurable" do
|
94
|
+
Fog::Cache.namespace_prefix = nil
|
95
|
+
assert_raises Fog::Cache::CacheDir do
|
96
|
+
Fog::Cache.load(Fog::SubFogTestModel, @service)
|
97
|
+
end
|
98
|
+
end
|
99
|
+
|
100
|
+
it "can create a namespace" do
|
101
|
+
Fog::Cache.expire_cache!(Fog::SubFogTestModel, @service)
|
102
|
+
assert_equal File.exist?(Fog::Cache.namespace(Fog::SubFogTestModel, @service)), false
|
103
|
+
|
104
|
+
Fog::Cache.create_namespace(Fog::SubFogTestModel, @service)
|
105
|
+
assert_equal File.exist?(Fog::Cache.namespace(Fog::SubFogTestModel, @service)), true
|
106
|
+
end
|
107
|
+
|
108
|
+
it "will raise if no cache data found" do
|
109
|
+
Fog::Cache.expire_cache!(Fog::SubFogTestModel, @service)
|
110
|
+
|
111
|
+
assert_raises Fog::Cache::CacheNotFound do
|
112
|
+
Fog::Cache.load(Fog::SubFogTestModel, @service)
|
113
|
+
end
|
114
|
+
end
|
115
|
+
|
116
|
+
it "Fog cache ignores bad cache data - empty file, from disk" do
|
117
|
+
Fog::Cache.expire_cache!(Fog::SubFogTestModel, @service)
|
118
|
+
id = SecureRandom.hex
|
119
|
+
a = Fog::SubFogTestModel.new(:id => id, :service => @service)
|
120
|
+
a.cache.dump
|
121
|
+
|
122
|
+
# input bad data
|
123
|
+
path_dir = File.expand_path(Fog::Cache.namespace(Fog::SubFogTestModel, @service))
|
124
|
+
path = File.join(path_dir, "foo.yml")
|
125
|
+
data = ""
|
126
|
+
File.open(path, "w") { |f|
|
127
|
+
f.write(data)
|
128
|
+
}
|
129
|
+
|
130
|
+
assert_equal 1, Fog::Cache.load(Fog::SubFogTestModel, @service).size
|
131
|
+
end
|
132
|
+
|
133
|
+
|
134
|
+
it "can be dumped and reloaded back in" do
|
135
|
+
|
136
|
+
Fog::Cache.expire_cache!(Fog::SubFogTestModel, @service)
|
137
|
+
|
138
|
+
id = SecureRandom.hex
|
139
|
+
a = Fog::SubFogTestModel.new(:id => id, :service => @service)
|
140
|
+
|
141
|
+
assert_equal File.exist?(Fog::Cache.namespace(Fog::SubFogTestModel, @service)), false
|
142
|
+
a.cache.dump
|
143
|
+
assert_equal File.exist?(Fog::Cache.namespace(Fog::SubFogTestModel, @service)), true
|
144
|
+
|
145
|
+
instances = Fog::Cache.load(Fog::SubFogTestModel, @service)
|
146
|
+
|
147
|
+
assert_equal instances.first.id, a.id
|
148
|
+
assert_equal instances.first.class, a.class
|
149
|
+
end
|
150
|
+
|
151
|
+
it "dumping two models that have a duplicate identity" do
|
152
|
+
Fog::Cache.expire_cache!(Fog::SubFogTestModel, @service)
|
153
|
+
|
154
|
+
id = SecureRandom.hex
|
155
|
+
|
156
|
+
# security groups on aws for eg can have the same identity group name 'default'.
|
157
|
+
# there are no restrictions on `identity` fog attributes to be uniq.
|
158
|
+
a = Fog::SubFogTestModel.new(:id => id, :service => @service, :bar => 'bar')
|
159
|
+
b = Fog::SubFogTestModel.new(:id => id, :service => @service, :foo => 'foo')
|
160
|
+
|
161
|
+
a.cache.dump
|
162
|
+
b.cache.dump
|
163
|
+
|
164
|
+
instances = Fog::Cache.load(Fog::SubFogTestModel, @service)
|
165
|
+
|
166
|
+
assert_equal instances.size, 2
|
167
|
+
end
|
168
|
+
|
169
|
+
it "dumping two models that have a duplicate identity twice" do
|
170
|
+
Fog::Cache.expire_cache!(Fog::SubFogTestModel, @service)
|
171
|
+
|
172
|
+
id = SecureRandom.hex
|
173
|
+
|
174
|
+
# security groups on aws for eg can have the same identity group name 'default'.
|
175
|
+
# there are no restrictions on `identity` fog attributes to be uniq.
|
176
|
+
a = Fog::SubFogTestModel.new(:id => id, :service => @service, :bar => 'bar')
|
177
|
+
b = Fog::SubFogTestModel.new(:id => id, :service => @service, :foo => 'foo')
|
178
|
+
|
179
|
+
a.cache.dump
|
180
|
+
b.cache.dump
|
181
|
+
|
182
|
+
# and then again, w/out expiring cache
|
183
|
+
a.cache.dump
|
184
|
+
b.cache.dump
|
185
|
+
|
186
|
+
instances = Fog::Cache.load(Fog::SubFogTestModel, @service)
|
187
|
+
|
188
|
+
# unique-ify based on the attributes...
|
189
|
+
assert_equal instances.size, 2
|
190
|
+
end
|
191
|
+
end
|
data/spec/fog_attribute_spec.rb
CHANGED
@@ -1,5 +1,4 @@
|
|
1
1
|
require "spec_helper"
|
2
|
-
require "xmlrpc/datetime"
|
3
2
|
|
4
3
|
class Service
|
5
4
|
def single_associations
|
@@ -114,12 +113,6 @@ describe "Fog::Attributes" do
|
|
114
113
|
model.merge_attributes(:time => string)
|
115
114
|
assert_equal Time.parse(string), model.time
|
116
115
|
end
|
117
|
-
|
118
|
-
it "returns a Time object when passed a XMLRPC::DateTime object" do
|
119
|
-
now = XMLRPC::DateTime.new(2000, 7, 8, 10, 20, 34)
|
120
|
-
model.merge_attributes(:time => now)
|
121
|
-
assert_equal now.to_time, model.time
|
122
|
-
end
|
123
116
|
end
|
124
117
|
|
125
118
|
describe ":type => :boolean" do
|
@@ -238,7 +231,7 @@ describe "Fog::Attributes" do
|
|
238
231
|
|
239
232
|
describe ":default => 'default_value'" do
|
240
233
|
it "should return nil when default is not defined on a new object" do
|
241
|
-
|
234
|
+
assert_nil model.bool
|
242
235
|
end
|
243
236
|
|
244
237
|
it "should return the value of the object when default is not defined" do
|
@@ -266,18 +259,18 @@ describe "Fog::Attributes" do
|
|
266
259
|
|
267
260
|
it "should return nil on a persisted object without a value" do
|
268
261
|
model.merge_attributes(:id => "some-crazy-id")
|
269
|
-
|
262
|
+
assert_nil model.default
|
270
263
|
end
|
271
264
|
|
272
265
|
it "should return nil when an attribute with default value is setted to nil" do
|
273
266
|
model.default = nil
|
274
|
-
|
267
|
+
assert_nil model.default
|
275
268
|
end
|
276
269
|
end
|
277
270
|
|
278
271
|
describe ".has_one" do
|
279
272
|
it "should create an instance_variable to save the association object" do
|
280
|
-
|
273
|
+
assert_nil model.one_object
|
281
274
|
end
|
282
275
|
|
283
276
|
it "should create a getter to save the association model" do
|
@@ -299,7 +292,7 @@ describe "Fog::Attributes" do
|
|
299
292
|
|
300
293
|
describe ".has_one_identity" do
|
301
294
|
it "should create an instance_variable to save the association identity" do
|
302
|
-
|
295
|
+
assert_nil model.one_identity
|
303
296
|
end
|
304
297
|
|
305
298
|
it "should create a getter to load the association model" do
|
data/spec/service_spec.rb
CHANGED
@@ -171,9 +171,10 @@ describe Fog::Service do
|
|
171
171
|
end
|
172
172
|
|
173
173
|
describe "when config object can configure the service itself" do
|
174
|
-
it "ignores the global and
|
174
|
+
it "ignores the global and its values" do
|
175
175
|
@config = MiniTest::Mock.new
|
176
176
|
def @config.config_service?; true; end
|
177
|
+
def @config.nil?; false; end
|
177
178
|
def @config.==(other); object_id == other.object_id; end
|
178
179
|
|
179
180
|
unexpected_usage = lambda { raise "Accessing global!" }
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: fog-core
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.
|
4
|
+
version: 1.45.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Evan Light
|
@@ -9,7 +9,7 @@ authors:
|
|
9
9
|
autorequire:
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
|
-
date:
|
12
|
+
date: 2017-08-01 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: builder
|
@@ -31,14 +31,14 @@ dependencies:
|
|
31
31
|
requirements:
|
32
32
|
- - "~>"
|
33
33
|
- !ruby/object:Gem::Version
|
34
|
-
version: '0.
|
34
|
+
version: '0.58'
|
35
35
|
type: :runtime
|
36
36
|
prerelease: false
|
37
37
|
version_requirements: !ruby/object:Gem::Requirement
|
38
38
|
requirements:
|
39
39
|
- - "~>"
|
40
40
|
- !ruby/object:Gem::Version
|
41
|
-
version: '0.
|
41
|
+
version: '0.58'
|
42
42
|
- !ruby/object:Gem::Dependency
|
43
43
|
name: formatador
|
44
44
|
requirement: !ruby/object:Gem::Requirement
|
@@ -53,6 +53,20 @@ dependencies:
|
|
53
53
|
- - "~>"
|
54
54
|
- !ruby/object:Gem::Version
|
55
55
|
version: '0.2'
|
56
|
+
- !ruby/object:Gem::Dependency
|
57
|
+
name: tins
|
58
|
+
requirement: !ruby/object:Gem::Requirement
|
59
|
+
requirements:
|
60
|
+
- - ">="
|
61
|
+
- !ruby/object:Gem::Version
|
62
|
+
version: '0'
|
63
|
+
type: :development
|
64
|
+
prerelease: false
|
65
|
+
version_requirements: !ruby/object:Gem::Requirement
|
66
|
+
requirements:
|
67
|
+
- - ">="
|
68
|
+
- !ruby/object:Gem::Version
|
69
|
+
version: '0'
|
56
70
|
- !ruby/object:Gem::Dependency
|
57
71
|
name: coveralls
|
58
72
|
requirement: !ruby/object:Gem::Requirement
|
@@ -179,7 +193,8 @@ files:
|
|
179
193
|
- CONTRIBUTING.md
|
180
194
|
- CONTRIBUTORS.md
|
181
195
|
- Gemfile
|
182
|
-
-
|
196
|
+
- Gemfile.1.8.7
|
197
|
+
- Gemfile.1.9.3
|
183
198
|
- LICENSE.md
|
184
199
|
- README.md
|
185
200
|
- Rakefile
|
@@ -207,6 +222,7 @@ files:
|
|
207
222
|
- lib/fog/core/attributes/string.rb
|
208
223
|
- lib/fog/core/attributes/time.rb
|
209
224
|
- lib/fog/core/attributes/timestamp.rb
|
225
|
+
- lib/fog/core/cache.rb
|
210
226
|
- lib/fog/core/collection.rb
|
211
227
|
- lib/fog/core/connection.rb
|
212
228
|
- lib/fog/core/credentials.rb
|
@@ -261,8 +277,10 @@ files:
|
|
261
277
|
- lib/fog/volume.rb
|
262
278
|
- lib/fog/vpn.rb
|
263
279
|
- lib/tasks/test_task.rb
|
280
|
+
- spec/compute/models/server_spec.rb
|
264
281
|
- spec/compute_spec.rb
|
265
282
|
- spec/connection_spec.rb
|
283
|
+
- spec/core/cache_spec.rb
|
266
284
|
- spec/core/model_spec.rb
|
267
285
|
- spec/core/stringify_keys_spec.rb
|
268
286
|
- spec/core/whitelist_keys_spec.rb
|
@@ -305,13 +323,15 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
305
323
|
version: '0'
|
306
324
|
requirements: []
|
307
325
|
rubyforge_project:
|
308
|
-
rubygems_version: 2.
|
326
|
+
rubygems_version: 2.6.11
|
309
327
|
signing_key:
|
310
328
|
specification_version: 4
|
311
329
|
summary: Shared classes and tests for fog providers and services.
|
312
330
|
test_files:
|
331
|
+
- spec/compute/models/server_spec.rb
|
313
332
|
- spec/compute_spec.rb
|
314
333
|
- spec/connection_spec.rb
|
334
|
+
- spec/core/cache_spec.rb
|
315
335
|
- spec/core/model_spec.rb
|
316
336
|
- spec/core/stringify_keys_spec.rb
|
317
337
|
- spec/core/whitelist_keys_spec.rb
|
@@ -334,4 +354,3 @@ test_files:
|
|
334
354
|
- spec/utils_spec.rb
|
335
355
|
- spec/uuid_spec.rb
|
336
356
|
- spec/wait_for_spec.rb
|
337
|
-
has_rdoc:
|
File without changes
|