pax_global_header00006660000000000000000000000064131401301410014476gustar00rootroot0000000000000052 comment=13979be5237d7cd7d9a99f2dccbe2fe138ff69aa fog-core-1.45.0/000077500000000000000000000000001314013014100132665ustar00rootroot00000000000000fog-core-1.45.0/.gitignore000066400000000000000000000002371314013014100152600ustar00rootroot00000000000000*.gem *.rbc .bundle .config .yardoc Gemfile.*lock InstalledFiles _yardoc coverage doc/ lib/bundler/man pkg rdoc spec/reports test/tmp test/version_tmp tmp .#* fog-core-1.45.0/.rubocop.yml000066400000000000000000000004741314013014100155450ustar00rootroot00000000000000Metrics/LineLength: Enabled: false Style/EachWithObject: Enabled: false Style/Encoding: EnforcedStyle: when_needed Style/FormatString: Enabled: false Style/HashSyntax: EnforcedStyle: hash_rockets Style/SignalException: EnforcedStyle: only_raise Style/StringLiterals: EnforcedStyle: double_quotes fog-core-1.45.0/.travis.yml000066400000000000000000000015321314013014100154000ustar00rootroot00000000000000language: ruby rvm: - 2.1 - 2.2 - 2.3 - 2.4 - jruby-head sudo: false dist: trusty script: bundle exec rake travis matrix: fast_finish: true include: - rvm: 1.8.7 gemfile: Gemfile.1.8.7 - rvm: jruby-18mode gemfile: Gemfile.1.8.7 - rvm: 1.9.3 gemfile: Gemfile.1.9.3 - rvm: jruby-19mode gemfile: Gemfile.1.9.3 - rvm: 2.1 gemfile: Gemfile env: COVERAGE=true - rvm: jruby-head gemfile: Gemfile allow_failures: - rvm: jruby-head - rvm: jruby9k notifications: email: false irc: channels: - "irc.freenode.org#ruby-fog" template: - "[#%{build_number}] %{message} %{build_url}" - "[#%{build_number}] %{commit} on %{branch} by %{author}" - "[#%{build_number}] %{compare_url}" on_success: always on_failure: always use_notice: false fog-core-1.45.0/CONTRIBUTING.md000066400000000000000000000021741314013014100155230ustar00rootroot00000000000000## Getting Involved New contributors are always welcome, when it doubt please ask questions. We strive to be an open and welcoming community. Please be nice to one another. ### Coding * Pick a task: * Offer feedback on open [pull requests](https://github.com/fog/fog-core/pulls). * Review open [issues](https://github.com/fog/fog-core/issues) for things to help on. * [Create an issue](https://github.com/fog/fog-core/issues/new) to start a discussion on additions or features. * Fork the project, add your changes and tests to cover them in a topic branch. * Commit your changes and rebase against `fog/fog-core` to ensure everything is up to date. * [Submit a pull request](https://github.com/fog/fog-core/compare/) ### Non-Coding * Offer feedback on open [issues](https://github.com/fog/fog-core/issues). * Write and help edit [documentation](https://github.com/fog/fog.github.com). * Translate [documentation](https://github.com/fog/fog.github.com) in to other languages. * Organize or volunteer at events. * [Donate](https://www.gittip.com/geemus/) * Discuss other ideas for contribution with [geemus](mailto:geemus+fog@gmail.com). fog-core-1.45.0/CONTRIBUTORS.md000066400000000000000000000034111314013014100155440ustar00rootroot00000000000000* Aaron Patterson * Akira Matsuda * Alexandru Calinoiu * Anatol * Artem * Artem Yakimenko * Brandon Dunne * Bryan Paxton * Chris Johnson * Chris Sinjakli * Dominic Cleal * Evan Light * Frederick Cheung * Gilles Dubreuil * Griffin Smith * Harry Maclean * Isaac Hollander McCreery * Jake Bell * Josh Kalderimis * Josh Lane * Kensuke Nagae * Kevin Menard * Ladislav Smola * Matheus Mina * Matt Bostock * Matt Darby * Matt Darby * Matt Eldridge * Mike Hagedorn * Naoto TAKAHASHI * Patrick McAfee * Paul Thornthwaite * Paul Thornthwaite * Paulo Henrique Lopes Ribeiro * Peter Drake * Suraj Shirvankar * Wesley Beary * Wesley Beary * cristhiano * geemus * mountkin * ozroc * schneems * starbelly * zhitongLBN fog-core-1.45.0/Gemfile000066400000000000000000000000461314013014100145610ustar00rootroot00000000000000source 'https://rubygems.org' gemspec fog-core-1.45.0/Gemfile.1.8.7000066400000000000000000000001001314013014100152220ustar00rootroot00000000000000source 'https://rubygems.org' gemspec gem 'rake', '~> 10.1.1' fog-core-1.45.0/Gemfile.1.9.3000066400000000000000000000002221314013014100152240ustar00rootroot00000000000000source "https://rubygems.org" gemspec gem 'net-ssh', '< 3.0' gem 'rubocop', '~> 0.41.2' gem 'term-ansicolor', '~> 1.3.2' gem 'tins', '~> 1.6.0' fog-core-1.45.0/LICENSE.md000066400000000000000000000021711314013014100146730ustar00rootroot00000000000000The MIT License (MIT) Copyright (c) 2014-2016 [CONTRIBUTORS.md](https://github.com/fog/fog/blob/master/CONTRIBUTORS.md) Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. fog-core-1.45.0/README.md000066400000000000000000000024101314013014100145420ustar00rootroot00000000000000# Fog::Core Shared classes and tests for fog providers and services. [![Build Status](https://travis-ci.org/fog/fog-core.svg?branch=master)](https://travis-ci.org/fog/fog-core) ## Ruby version Fog-core requires Ruby `2.0.0` or later. Ruby `1.8` and `1.9` support was dropped in `fog-v2.0.0` as a backwards incompatible change. Please use the later fog `1.x` versions if you require `1.8.7` or `1.9.x` support. ## Installation Add this line to your application's Gemfile: gem 'fog-core' And then execute: $ bundle Or install it yourself as: $ gem install fog-core ### Ruby 1.9.3 Some of `fog-core`'s dependencies have dropped support for Ruby 1.9.3 in later versions. Rather than limit all `fog` users to older but compatible versions, if you are using 1.9.3 you will need to declare a compatible version in your application's `Gemfile` like: gem "net-ssh", "< 3.0" See `Gemfile.1.9.3` for the definitive list as tested by Travis. ## Usage TODO: Write usage instructions here ## Contributing 1. Fork it ( http://github.com/fog/fog-core/fork ) 2. Create your feature branch (`git checkout -b my-new-feature`) 3. Commit your changes (`git commit -am 'Add some feature'`) 4. Push to the branch (`git push origin my-new-feature`) 5. Create new Pull Request fog-core-1.45.0/Rakefile000066400000000000000000000006231314013014100147340ustar00rootroot00000000000000require 'bundler/setup' task :default => [:test] require "rake/testtask" Rake::TestTask.new do |t| t.libs << "lib" t.libs << "spec" t.pattern = "spec/**/*_spec.rb" end task :travis do mock = 'true' || ENV['FOG_MOCK'] sh("export FOG_MOCK=#{mock} && rake") if ENV['COVERAGE'] require 'coveralls/rake/task' Coveralls::RakeTask.new Rake::Task["coveralls:push"].invoke end endfog-core-1.45.0/changelog.md000066400000000000000000000157751314013014100155560ustar00rootroot000000000000001.45.0 08/01/2017 ========================================================== remove xmlrpc requirement/usage fix for nested const across ruby versions remove array#sample usage for legacy ruby compatibility simplify uniq for cache and fix for legacy ruby remove debugging puts from cache tweak tins version for 1.9 loosen 2.1.x travis config to 2.1 add 1.9 compatible term-ansicolor fix rubocop for 1.9.3 enable metadata for cache add specs for server#sshable add exponential backoff for server#sshable? add server#ready? to base server for clarity bump excon dependency 1.44.3 05/25/2017 ========================================================== fix cache when no home directory defined 1.44.2 05/18/2017 ========================================================== fix homedirectory usage for caching 1.44.1 05/01/2017 ========================================================== remove xml-rpc dependency (as it is causing issues) 1.44.0 04/28/2017 ========================================================== add basic caching support 1.43.0 09/28/2016 ========================================================== fix digitalocean compatibility update README badges 1.42.0 07/05/2016 ========================================================== make namespace detection fix 1.8.x compatible 1.41.0 07/01/2016 ========================================================== bump as 1.40.1 is not showing up in some cases 1.40.1 06/28/2016 ========================================================== fix namespace constant detection 1.40.0 05/19/2016 ========================================================== add minitest helpers for schema (parity to shindo) 1.39.0 05/11/2016 ========================================================== cleanup warnings add NFV module only dup frozen strings 1.38.0 04/20/2016 ========================================================== more specific service not found error fix string freeze issue for ruby 2.3 bump excon dep 1.37.0 03/31/2016 ========================================================== remove hp from providers re-raise mime-type error, rather than exiting fix tests add introspection module 1.36.0 02/23/2016 ========================================================== default digitalocean to v2 fix eager/auto-loading add cloud-at-cost 1.35.0 11/24/2015 ========================================================== make mime/types require optional fix warnings about net-ssh vs net-cp 1.34.0 11/16/2015 ========================================================== make net/ssh and net/scp requires optional 1.33.0 11/15/2015 ========================================================== relax net/ssh and net/scp requirement 1.32.1 08/12/2015 ========================================================== expose identities in models 1.32.0 07/02/2015 ========================================================== fix/refactor service initializers 1.31.1 06/17/2015 ========================================================== fixes around unknown providers/services 1.31.0 06/17/2015 ========================================================== use relative paths add digital ocean examples reinstate baremetal add softlayer examples add digital ocean v2 support setup fog model equality to check identities (if available) use Fog.interval in wait_for reduce memory footprint fix account handling 1.30.0 04/02/2015 ========================================================== bump excon dep use float times, instead of integers for Fog::Time don't raise if final wait_for yield true fix bug around formatador and #map on models fix around `to_time` to avoid conflicts with Rails monkey patches update specs update style fix `WhitelistKeys` for 1.8.7 remove unreachable code convert hash helpers to minispec fix require order for coverage fix ruby 2.2 warning bump excon dependency fix readme link 1.29.0 02/19/2015 ========================================================== minor refactoring add ability to add additional user agent info to requests 1.28.0 01/30/2015 ========================================================== add Fog::Baremetal 1.27.4 01/26/2015 ========================================================== model fix for new formatador usage fixes around formatador delegation 1.27.3 12/01/2014 ========================================================== rubocop fixes for fog collection simpler ruby version checking/skipping fix requires_one 1.27.2 18/12/2014 ========================================================== fix several requires in service abstraction code 1.27.1 12/12/2014 ========================================================== fix typo in model load paths fix 1.27.0 12/12/2014 ========================================================== return fog/bin stuff to fog/fog add support for multiple request/model load paths 1.26.0 12/02/2014 ========================================================== remove rackspace logic use new travis builds fix error handling around credential fetch move fog/bin stuff to fog-core fix circular reference in collection.rb 1.25.0 11/18/2014 ========================================================== add alias options for associations improve spec message add feature to overwrite keys on hash of attributes generation remove method_missing from model add rubocop fix rubocop warnings return collections on association getters fix require bug in service put fog and fog-core versions in user agent don't mutate/destroy encoding in get_body_size fix error output in from const_get usage separate to have distinct version from fog 1.24.0 08/26/2014 ========================================================== fixes for defaulting attributes add method for getting all attributes add methods for associations add all_attributes, all_associations and all_associations_and_attributes helper methods remove no-longer-needed gem update on travis add all_values fixes to avoid path conflicts with fog/fog 1.23.0 07/16/2014 ========================================================== attribute whitelisting abstract out stringify for possible reuse more specific naming reorg add path_prefix fix time conversion to work with XMLRPC add more specific per-type attribute tests lock down rest-client for 1.8.7 allow namespace flipflop for dns fix identity lookup better default attribute value setting bump excon 1.22.0 04/17/2014 1c086852e40e4c1ad7ed138834e4a1505ddb1416 ========================================================== attribute whitelisting abstract out stringify for possible reuse more specific naming reorg add path_prefix fix time conversion to work with XMLRPC add more specific per-type attribute tests lock down rest-client for 1.8.7 allow namespace flipflop for dns fix identity lookup better default attribute value setting bump excon 1.22.0 04/17/2014 1c086852e40e4c1ad7ed138834e4a1505ddb1416 ========================================================== tests/cleanup/fixes 1.21.1 03/18/2014 3a803405ba60ded421f4bd14677cd3c76cb7e6ab ========================================================== remove json/xml modules and code add travis/coveralls update from upstream bump/loosen excon dependency fog-core-1.45.0/fog-core.gemspec000066400000000000000000000030451314013014100163360ustar00rootroot00000000000000# coding: utf-8 lib = File.expand_path("../lib", __FILE__) $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib) require "fog/core/version" Gem::Specification.new do |spec| spec.name = "fog-core" spec.version = Fog::Core::VERSION spec.authors = ["Evan Light", "Wesley Beary"] spec.email = ["evan@tripledogdare.net", "geemus@gmail.com"] spec.summary = "Shared classes and tests for fog providers and services." spec.description = "Shared classes and tests for fog providers and services." spec.homepage = "https://github.com/fog/fog-core" spec.license = "MIT" spec.files = `git ls-files`.split($INPUT_RECORD_SEPARATOR) spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) } spec.test_files = spec.files.grep(%r{^(test|spec|features)/}) spec.require_paths = ["lib"] spec.add_dependency("builder") spec.add_dependency("excon", "~> 0.58") spec.add_dependency("formatador", "~> 0.2") # https://github.com/fog/fog-core/issues/206 # spec.add_dependency("xmlrpc") if RUBY_VERSION.to_s >= "2.4" spec.add_development_dependency("tins") if RUBY_VERSION.to_s > "2.0" spec.add_development_dependency("coveralls") spec.add_development_dependency("minitest") spec.add_development_dependency("minitest-stub-const") spec.add_development_dependency("pry") spec.add_development_dependency("rake") spec.add_development_dependency("rubocop") if RUBY_VERSION.to_s >= "1.9.3" spec.add_development_dependency("thor") spec.add_development_dependency("yard") end fog-core-1.45.0/lib/000077500000000000000000000000001314013014100140345ustar00rootroot00000000000000fog-core-1.45.0/lib/fog/000077500000000000000000000000001314013014100146075ustar00rootroot00000000000000fog-core-1.45.0/lib/fog/account.rb000066400000000000000000000007271314013014100165760ustar00rootroot00000000000000module Fog module Account extend Fog::ServicesMixin def self.new(orig_attributes) attributes = orig_attributes.dup provider = attributes.delete(:provider).to_s.downcase.to_sym if provider == :stormondemand require "fog/account/storm_on_demand" Fog::Account::StormOnDemand.new(attributes) else super(orig_attributes) end end def self.providers Fog.services[:account] || [] end end end fog-core-1.45.0/lib/fog/baremetal.rb000066400000000000000000000001061314013014100170650ustar00rootroot00000000000000module Fog module Baremetal extend Fog::ServicesMixin end end fog-core-1.45.0/lib/fog/billing.rb000066400000000000000000000006231314013014100165550ustar00rootroot00000000000000module Fog module Billing extend Fog::ServicesMixin def self.new(orig_attributes) attributes = orig_attributes.dup provider = attributes.delete(:provider).to_s.downcase.to_sym if provider == :stormondemand require "fog/billing/storm_on_demand" Fog::Billing::StormOnDemand.new(attributes) else super(orig_attributes) end end end end fog-core-1.45.0/lib/fog/cdn.rb000066400000000000000000000001001314013014100156670ustar00rootroot00000000000000module Fog module CDN extend Fog::ServicesMixin end end fog-core-1.45.0/lib/fog/compute.rb000066400000000000000000000046271314013014100166210ustar00rootroot00000000000000module Fog module Compute extend Fog::ServicesMixin def self.new(orig_attributes) attributes = orig_attributes.dup # prevent delete from having side effects provider = attributes.delete(:provider).to_s.downcase.to_sym case provider when :gogrid require "fog/go_grid/compute" Fog::Compute::GoGrid.new(attributes) when :new_servers require "fog/bare_metal_cloud/compute" Fog::Logger.deprecation "`new_servers` is deprecated. Please use `bare_metal_cloud` instead." Fog::Compute::BareMetalCloud.new(attributes) when :baremetalcloud require "fog/bare_metal_cloud/compute" Fog::Compute::BareMetalCloud.new(attributes) when :rackspace version = attributes.delete(:version) version = version.to_s.downcase.to_sym unless version.nil? if version == :v1 Fog::Logger.deprecation "First Gen Cloud Servers are deprecated. Please use `:version => :v2` attribute to use Next Gen Cloud Servers." require "fog/rackspace/compute" Fog::Compute::Rackspace.new(attributes) else require "fog/rackspace/compute_v2" Fog::Compute::RackspaceV2.new(attributes) end when :digitalocean version = attributes.delete(:version) version = version.to_s.downcase.to_sym unless version.nil? if version == :v1 error_message = 'DigitalOcean V1 is deprecated.Please use `:version => :v2` attribute to use Next Gen Cloud Servers.' raise error_message else require 'fog/digitalocean/compute' Fog::Compute::DigitalOcean.new(attributes) end when :stormondemand require "fog/compute/storm_on_demand" Fog::Compute::StormOnDemand.new(attributes) when :vcloud require "fog/vcloud/compute" Fog::Vcloud::Compute.new(attributes) when :vclouddirector require "fog/vcloud_director/compute" Fog::Compute::VcloudDirector.new(attributes) when :cloudatcost require "fog/cloudatcost/compute" Fog::Compute::CloudAtCost.new(attributes) else super(orig_attributes) end end def self.servers servers = [] providers.each do |provider| begin servers.concat(self[provider].servers) rescue # ignore any missing credentials/etc end end servers end end end fog-core-1.45.0/lib/fog/compute/000077500000000000000000000000001314013014100162635ustar00rootroot00000000000000fog-core-1.45.0/lib/fog/compute/models/000077500000000000000000000000001314013014100175465ustar00rootroot00000000000000fog-core-1.45.0/lib/fog/compute/models/server.rb000066400000000000000000000101171314013014100214010ustar00rootroot00000000000000require "fog/core/model" module Fog module Compute class Server < Fog::Model INITIAL_SSHABLE_TIMEOUT = 8 attr_writer :username, :private_key, :private_key_path, :public_key, :public_key_path, :ssh_port, :ssh_options # Sets the proc used to determine the IP Address used for ssh/scp interactions. # @example # service.servers.bootstrap :name => "bootstrap-server", # :flavor_id => service.flavors.first.id, # :image_id => service.images.find {|img| img.name =~ /Ubuntu/}.id, # :public_key_path => "~/.ssh/fog_rsa.pub", # :private_key_path => "~/.ssh/fog_rsa", # :ssh_ip_address => Proc.new {|server| server.private_ip_address } # # @note By default scp/ssh will use the public_ip_address if this proc is not set. attr_writer :ssh_ip_address def username @username ||= "root" end def private_key_path @private_key_path ||= Fog.credentials[:private_key_path] @private_key_path &&= File.expand_path(@private_key_path) end def private_key @private_key ||= private_key_path && File.read(private_key_path) end def public_key_path @public_key_path ||= Fog.credentials[:public_key_path] @public_key_path &&= File.expand_path(@public_key_path) end def public_key @public_key ||= public_key_path && File.read(public_key_path) end # Port used for ssh/scp interactions with server. # @return [Integer] IP port # @note By default this returns 22 def ssh_port @ssh_port ||= 22 end # IP Address used for ssh/scp interactions with server. # @return [String] IP Address # @note By default this returns the public_ip_address def ssh_ip_address return public_ip_address unless @ssh_ip_address return @ssh_ip_address.call(self) if @ssh_ip_address.is_a?(Proc) @ssh_ip_address end def ssh_options @ssh_options ||= {} ssh_options = @ssh_options.merge(:port => ssh_port) if private_key ssh_options[:key_data] = [private_key] ssh_options[:auth_methods] = %w(publickey) end ssh_options end def scp(local_path, remote_path, upload_options = {}) requires :ssh_ip_address, :username Fog::SCP.new(ssh_ip_address, username, ssh_options).upload(local_path, remote_path, upload_options) end alias_method :scp_upload, :scp def scp_download(remote_path, local_path, download_options = {}) requires :ssh_ip_address, :username Fog::SCP.new(ssh_ip_address, username, ssh_options).download(remote_path, local_path, download_options) end def ssh(commands, options = {}, &blk) requires :ssh_ip_address, :username options = ssh_options.merge(options) Fog::SSH.new(ssh_ip_address, username, options).run(commands, &blk) end def sshable?(options = {}) result = ready? && !ssh_ip_address.nil? && !!Timeout.timeout(sshable_timeout) { ssh("pwd", options) } @sshable_timeout = nil result rescue SystemCallError false rescue Net::SSH::AuthenticationFailed, Net::SSH::Disconnect @sshable_timeout = nil false rescue Timeout::Error increase_sshable_timeout false end # Is the server ready to receive connections? # # Returns false by default. # # Subclasses should implement #ready? appropriately. def ready? false end private def sshable_timeout @sshable_timeout ||= increase_sshable_timeout end def increase_sshable_timeout if defined?(@sshable_timeout) && @sshable_timeout if @sshable_timeout >= 40 @sshable_timeout = 60 else @sshable_timeout *= 1.5 end else @sshable_timeout = INITIAL_SSHABLE_TIMEOUT end end end end end fog-core-1.45.0/lib/fog/core.rb000066400000000000000000000065611314013014100160740ustar00rootroot00000000000000# external core dependencies require "base64" require "cgi" require "uri" require "excon" require "fileutils" require "formatador" require "openssl" require "time" require "timeout" require "ipaddr" # internal core dependencies require File.expand_path('../core/version', __FILE__) # Mixins require File.expand_path('../core/services_mixin', __FILE__) require File.expand_path('../core/attributes', __FILE__) require File.expand_path('../core/attributes/default', __FILE__) require File.expand_path('../core/attributes/array', __FILE__) require File.expand_path('../core/attributes/boolean', __FILE__) require File.expand_path('../core/attributes/float', __FILE__) require File.expand_path('../core/attributes/integer', __FILE__) require File.expand_path('../core/attributes/string', __FILE__) require File.expand_path('../core/attributes/time', __FILE__) require File.expand_path('../core/attributes/timestamp', __FILE__) require File.expand_path('../core/associations/default', __FILE__) require File.expand_path('../core/associations/many_identities', __FILE__) require File.expand_path('../core/associations/many_models', __FILE__) require File.expand_path('../core/associations/one_model', __FILE__) require File.expand_path('../core/associations/one_identity', __FILE__) require File.expand_path('../core/collection', __FILE__) require File.expand_path('../core/association', __FILE__) require File.expand_path('../core/connection', __FILE__) require File.expand_path('../core/credentials', __FILE__) require File.expand_path('../core/current_machine', __FILE__) require File.expand_path('../core/deprecation', __FILE__) require File.expand_path('../core/errors', __FILE__) require File.expand_path('../core/hmac', __FILE__) require File.expand_path('../core/logger', __FILE__) require File.expand_path('../core/model', __FILE__) require File.expand_path('../core/mock', __FILE__) require File.expand_path('../core/provider', __FILE__) require File.expand_path('../core/service', __FILE__) require File.expand_path('../core/ssh', __FILE__) require File.expand_path('../core/scp', __FILE__) require File.expand_path('../core/time', __FILE__) require File.expand_path('../core/utils', __FILE__) require File.expand_path('../core/wait_for', __FILE__) require File.expand_path('../core/wait_for_defaults', __FILE__) require File.expand_path('../core/uuid', __FILE__) require File.expand_path('../core/stringify_keys', __FILE__) require File.expand_path('../core/whitelist_keys', __FILE__) require File.expand_path('../account', __FILE__) require File.expand_path('../baremetal', __FILE__) require File.expand_path('../billing', __FILE__) require File.expand_path('../cdn', __FILE__) require File.expand_path('../compute', __FILE__) require File.expand_path('../dns', __FILE__) require File.expand_path('../identity', __FILE__) require File.expand_path('../image', __FILE__) require File.expand_path('../introspection', __FILE__) require File.expand_path('../metering', __FILE__) require File.expand_path('../monitoring', __FILE__) require File.expand_path('../nfv', __FILE__) require File.expand_path('../network', __FILE__) require File.expand_path('../orchestration', __FILE__) require File.expand_path('../storage', __FILE__) require File.expand_path('../support', __FILE__) require File.expand_path('../volume', __FILE__) require File.expand_path('../vpn', __FILE__) # Utility require File.expand_path('../formatador', __FILE__) fog-core-1.45.0/lib/fog/core/000077500000000000000000000000001314013014100155375ustar00rootroot00000000000000fog-core-1.45.0/lib/fog/core/association.rb000066400000000000000000000004661314013014100204060ustar00rootroot00000000000000module Fog class Association < Collection def initialize(associations = []) @loaded = true load(associations) end def load(associations) return unless associations.kind_of?(Array) associations.each do |association| self << association end end end end fog-core-1.45.0/lib/fog/core/associations/000077500000000000000000000000001314013014100202365ustar00rootroot00000000000000fog-core-1.45.0/lib/fog/core/associations/default.rb000066400000000000000000000015421314013014100222110ustar00rootroot00000000000000module Fog module Associations # = Fog Default Association # # This class has the shared behavior between all association models. class Default attr_reader :model, :name, :aliases, :as, :association_class def initialize(model, name, collection_name, options) @model = model @name = name model.associations[name] = collection_name @aliases = options.fetch(:aliases, []) @as = options.fetch(:as, name) @association_class = options.fetch(:association_class, Fog::Association) create_setter create_getter create_aliases create_mask end def create_aliases Array(aliases).each do |alias_name| model.aliases[alias_name] = name end end def create_mask model.masks[name] = as end end end end fog-core-1.45.0/lib/fog/core/associations/many_identities.rb000066400000000000000000000021151314013014100237470ustar00rootroot00000000000000module Fog module Associations # = Fog Multiple Association # # This class handles multiple association between the models. # It expects the provider to return a collection of ids. # The association models will be loaded based on the collection of ids. class ManyIdentities < Default def create_setter model.class_eval <<-EOS, __FILE__, __LINE__ def #{name}=(new_#{name}) associations[:#{name}] = Array(new_#{name}).map do |association| association.respond_to?(:identity) ? association.identity : association end end EOS end def create_getter model.class_eval <<-EOS, __FILE__, __LINE__ def #{name} return [] if associations[:#{name}].nil? data = Array(associations[:#{name}]).map do |association| service.send(self.class.associations[:#{name}]).get(association) end #{association_class}.new(data) end EOS end end end end fog-core-1.45.0/lib/fog/core/associations/many_models.rb000066400000000000000000000012641314013014100230750ustar00rootroot00000000000000module Fog module Associations # = Fog Multiple Association # # This class handles multiple association between the models. # It expects the provider to map the attribute with a collection of objects. class ManyModels < Default def create_setter model.class_eval <<-EOS, __FILE__, __LINE__ def #{name}=(new_#{name}) associations[:#{name}] = Array(new_#{name}) end EOS end def create_getter model.class_eval <<-EOS, __FILE__, __LINE__ def #{name} data = associations[:#{name}] #{association_class}.new(data) end EOS end end end end fog-core-1.45.0/lib/fog/core/associations/one_identity.rb000066400000000000000000000015361314013014100232620ustar00rootroot00000000000000module Fog module Associations # = Fog Single Association # # This class handles single association between the models. # It expects the provider to return only the id of the association. # The association model will be loaded based on the id initialized. class OneIdentity < Default def create_setter model.class_eval <<-EOS, __FILE__, __LINE__ def #{name}=(new_#{name}) associations[:#{name}] = new_#{name}.respond_to?(:identity) ? new_#{name}.identity : new_#{name} end EOS end def create_getter model.class_eval <<-EOS, __FILE__, __LINE__ def #{name} return nil if associations[:#{name}].nil? service.send(self.class.associations[:#{name}]).get(associations[:#{name}]) end EOS end end end end fog-core-1.45.0/lib/fog/core/associations/one_model.rb000066400000000000000000000011631314013014100225250ustar00rootroot00000000000000module Fog module Associations # = Fog Single Association # # This class handles single association between the models. # It expects the provider to map the attribute with an initialized object. class OneModel < Default def create_setter model.class_eval <<-EOS, __FILE__, __LINE__ def #{name}=(new_#{name}) associations[:#{name}] = new_#{name} end EOS end def create_getter model.class_eval <<-EOS, __FILE__, __LINE__ def #{name} associations[:#{name}] end EOS end end end end fog-core-1.45.0/lib/fog/core/attributes.rb000066400000000000000000000114611314013014100202550ustar00rootroot00000000000000module Fog module Attributes module ClassMethods def _load(marshalled) new(Marshal.load(marshalled)) end def aliases @aliases ||= {} end def associations @associations ||= {} end def attributes @attributes ||= [] end def default_values @default_values ||= {} end def masks @masks ||= {} end def attribute(name, options = {}) type = options.fetch(:type, "default").to_s.capitalize Fog::Attributes.const_get(type).new(self, name, options) end def has_one(name, collection_name, options = {}) Fog::Associations::OneModel.new(self, name, collection_name, options) end def has_many(name, collection_name, options = {}) Fog::Associations::ManyModels.new(self, name, collection_name, options) end def has_one_identity(name, collection_name, options = {}) Fog::Associations::OneIdentity.new(self, name, collection_name, options) end def has_many_identities(name, collection_name, options = {}) Fog::Associations::ManyIdentities.new(self, name, collection_name, options) end def identity(name, options = {}) @identity = name attribute(name, options) end def ignore_attributes(*args) @ignored_attributes = args.map(&:to_s) end def ignored_attributes @ignored_attributes ||= [] end end module InstanceMethods def _dump(_level) Marshal.dump(attributes) end def attributes @attributes ||= {} end def associations @associations ||= {} end def masks self.class.masks end def all_attributes self.class.attributes.reduce({}) do |hash, attribute| hash[masks[attribute]] = send(attribute) hash end end def all_associations self.class.associations.keys.reduce({}) do |hash, association| hash[masks[association]] = associations[association] || send(association) hash end end def all_associations_and_attributes all_attributes.merge(all_associations) end def dup copy = super copy.dup_attributes! copy end def identity_name self.class.instance_variable_get("@identity") end def identity send(identity_name) end def identity=(new_identity) send("#{identity_name}=", new_identity) end def merge_attributes(new_attributes = {}) new_attributes.each_pair do |key, value| next if self.class.ignored_attributes.include?(key) if self.class.aliases[key] send("#{self.class.aliases[key]}=", value) elsif self.respond_to?("#{key}=", true) send("#{key}=", value) else attributes[key] = value end end self end # Returns true if a remote resource has been assigned an # identity and we can assume it has been persisted. # # @return [Boolean] def persisted? !!identity end # Returns true if a remote resource has not been assigned an # identity. # # This was added for a ActiveRecord like feel but has been # outdated by ActiveModel API using {#persisted?} # # @deprecated Use inverted form of {#persisted?} # @return [Boolean] def new_record? Fog::Logger.deprecation("#new_record? is deprecated, use !persisted? instead [light_black](#{caller.first})[/]") !persisted? end # check that the attributes specified in args exist and is not nil def requires(*args) missing = missing_attributes(args) if missing.length == 1 raise(ArgumentError, "#{missing.first} is required for this operation") elsif missing.any? raise(ArgumentError, "#{missing[0...-1].join(", ")} and #{missing[-1]} are required for this operation") end end def requires_one(*args) missing = missing_attributes(args) return unless missing.length == args.length raise(ArgumentError, "#{missing[0...-1].join(", ")} or #{missing[-1]} are required for this operation") end protected def missing_attributes(args) missing = [] ([:service] | args).each do |arg| missing << arg unless send("#{arg}") || attributes.key?(arg) end missing end def dup_attributes! @attributes = @attributes.dup if @attributes end private def remap_attributes(attributes, mapping) mapping.each_pair do |key, value| attributes[value] = attributes.delete(key) if attributes.key?(key) end end end end end fog-core-1.45.0/lib/fog/core/attributes/000077500000000000000000000000001314013014100177255ustar00rootroot00000000000000fog-core-1.45.0/lib/fog/core/attributes/array.rb000066400000000000000000000011151314013014100213660ustar00rootroot00000000000000module Fog module Attributes # = Fog Array Attribute # # This class handles Array attributes from the providers, # converting values to Array objects class Array < Default def create_setter model.class_eval <<-EOS, __FILE__, __LINE__ def #{name}=(new_#{name}) attributes[:#{name}] = Array(new_#{name}) end EOS end def create_getter model.class_eval <<-EOS, __FILE__, __LINE__ def #{name} Array(attributes[:#{name}]) end EOS end end end end fog-core-1.45.0/lib/fog/core/attributes/boolean.rb000066400000000000000000000010471314013014100216730ustar00rootroot00000000000000module Fog module Attributes # = Fog Boolean Attribute # # This class handles Boolean attributes from the providers, # converting values to Boolean objects class Boolean < Default def create_setter model.class_eval <<-EOS, __FILE__, __LINE__ def #{name}=(new_#{name}) attributes[:#{name}] = case new_#{name} when true,'true' true when false,'false' false end end EOS end end end end fog-core-1.45.0/lib/fog/core/attributes/default.rb000066400000000000000000000043541314013014100217040ustar00rootroot00000000000000module Fog module Attributes # = Fog Default Attribute # # This class handles the attributes without a type force. # The attributes returned from the provider will keep its original values. class Default attr_reader :model, :name, :squash, :aliases, :default, :as def initialize(model, name, options) @model = model @model.attributes << name @name = name @squash = options.fetch(:squash, false) @aliases = options.fetch(:aliases, []) @default = options[:default] @as = options.fetch(:as, name) create_setter create_getter create_aliases set_defaults create_mask end def create_setter if squash model.class_eval <<-EOS, __FILE__, __LINE__ def #{name}=(new_data) if new_data.is_a?(Hash) if new_data.has_key?(:'#{squash}') attributes[:#{name}] = new_data[:'#{squash}'] elsif new_data.has_key?("#{squash}") attributes[:#{name}] = new_data["#{squash}"] else attributes[:#{name}] = [ new_data ] end else attributes[:#{name}] = new_data end end EOS else model.class_eval <<-EOS, __FILE__, __LINE__ def #{name}=(new_#{name}) attributes[:#{name}] = new_#{name} end EOS end end def create_getter model.class_eval <<-EOS, __FILE__, __LINE__ def #{name} return attributes[:#{name}] unless attributes[:#{name}].nil? if !attributes.key?(:#{name}) && !self.class.default_values[:#{name}].nil? && !persisted? return self.class.default_values[:#{name}] end attributes[:#{name}] end EOS end def create_aliases Array(aliases).each do |alias_name| model.aliases[alias_name] = name end end def set_defaults model.default_values[name] = default unless default.nil? end def create_mask model.masks[name] = as end end end end fog-core-1.45.0/lib/fog/core/attributes/float.rb000066400000000000000000000006421314013014100213610ustar00rootroot00000000000000module Fog module Attributes # = Fog Float Attribute # # This class handles Float attributes from the providers, # converting values to Float objects class Float < Default def create_setter model.class_eval <<-EOS, __FILE__, __LINE__ def #{name}=(new_#{name}) attributes[:#{name}] = new_#{name}.to_f end EOS end end end end fog-core-1.45.0/lib/fog/core/attributes/integer.rb000066400000000000000000000006521314013014100217120ustar00rootroot00000000000000module Fog module Attributes # = Fog Integer Attribute # # This class handles Integer attributes from the providers, # converting values to Integer objects class Integer < Default def create_setter model.class_eval <<-EOS, __FILE__, __LINE__ def #{name}=(new_#{name}) attributes[:#{name}] = new_#{name}.to_i end EOS end end end end fog-core-1.45.0/lib/fog/core/attributes/string.rb000066400000000000000000000006461314013014100215660ustar00rootroot00000000000000module Fog module Attributes # = Fog String Attribute # # This class handles String attributes from the providers, # converting values to String objects class String < Default def create_setter model.class_eval <<-EOS, __FILE__, __LINE__ def #{name}=(new_#{name}) attributes[:#{name}] = new_#{name}.to_s end EOS end end end end fog-core-1.45.0/lib/fog/core/attributes/time.rb000066400000000000000000000013571314013014100212160ustar00rootroot00000000000000module Fog module Attributes # = Fog Time Attribute # # This class handles Time attributes from the providers, # converting values to Time objects class Time < Default def create_setter model.class_eval <<-EOS, __FILE__, __LINE__ def #{name}=(new_#{name}) attributes[:#{name}] = if new_#{name}.nil? || new_#{name} == "" || new_#{name}.is_a?(::Time) new_#{name} elsif ::String === new_#{name} ::Time.parse(new_#{name}) elsif new_#{name}.respond_to?(:to_time) new_#{name}.to_time else ::Time.parse(new_#{name}) end end EOS end end end end fog-core-1.45.0/lib/fog/core/attributes/timestamp.rb000066400000000000000000000011721314013014100222560ustar00rootroot00000000000000module Fog module Attributes # = Fog Timestamp Attribute # # This class handles Timestamp attributes from the providers, # converting Integer and String values as a real Timestamp objects class Timestamp < Default def create_setter model.class_eval <<-EOS, __FILE__, __LINE__ def #{name}=(new_#{name}) if new_#{name}.respond_to?(:to_i) attributes[:#{name}] = Fog::Time.at(new_#{name}.to_i) else attributes[:#{name}] = Fog::Time.parse(new_#{name}.to_s) end end EOS end end end end fog-core-1.45.0/lib/fog/core/cache.rb000066400000000000000000000263151314013014100171360ustar00rootroot00000000000000# coding: utf-8 require "fileutils" require "yaml" require "tmpdir" module Fog # A generic cache mechanism for fog resources. This can be for a server, security group, etc. # # Currently this is a on-disk cache using yml files per-model instance, however # there is nothing in the way of extending this to use various other cache # backends. # # == Basic functionality # # set the namespace where this cache will be stored: # # Fog::Cache.namespace_prefix = "service-account-foo-region-bar" # # cache to disk: # # # after dumping, there will be a yml file on disk: # resouce.cache.dump # # # you can load cached data in from a different session # Fog::Cache.load(Fog::Compute::AWS::Server, compute) # # # you can also expire cache (removes cached data assocaited with the resources of this model associated to the service passed in). # Fog::Cache.expire_cache!(Fog::Compute::AWS::Server, compute) # # == More detailed flow/usage # # Normally, you would have a bunch of resources you want to cache/reload from disk. # Every fog model has a +cache+ object injected to accomplish this. So in order to cache a server for exmaple # you would do something like this: # # # note this is necessary in order to segregate usage of cache between various providers regions and accounts. # # if you are using one account/region/etc only, you still must set it. 'default' will do. # Fog::Cache.namespace_prefix = "prod-emea-eu-west-1" # # s = security_groups.sample; s.name # => "default" # s.cache.dump # => 2371 # # Now it is on disk: # # shai@adsk-lappy ~ % tree ~/.fog-cache/prod-emea-eu-west-1/ # # /Users/shai/.fog-cache/prod-emea-eu-west-1/ # └── fog_compute_aws_real # └── fog_compute_aws_securitygroup # ├── default-90928073d9d5d9b4e7545e88aee7ec4f.yml # # You can do the same with a SecurityGroup, Instances, Elbs, etc. # # Note that when loading cache from disk, you need to pass the appropriate model class, and service associated with it. # +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. # +Model+ is passed in so we can find the cache data associated to that model in the namespace of cache this session is using: # Will try to load all resources associated to those. If you had 1 yml file, or 100, it would load whatever it could find. # As such, the normal usage of dumping would be do it on a collection: # # load_balancers.each {|elb| elb.cache.dump } # # In order to load the cache into a different session with nothing but the service set up, use like so: # As mentioned, will load all resources associated to the +model_klass+ and +service+ passed in. # # instances = Fog::Cache.load(Fog::Compute::AWS::Server, compute) # instances.first.id # => "i-0569a70ae6f47d229" # # Note that if there is no cache located for the +model+ class and +service+ passed to `Fog::Cache.load` # you will get an exception you can handle (for example, to load the resources for the fisrt time): # # Fog::Cache.expire_cache!(Fog::Compute::AWS::SecurityGroup, compute) # # ... now there is no SecurityGroup cache data. So, if you tried to load it, you would get an exception: # # Fog::Cache.load(Fog::Compute::AWS::SecurityGroup, compute) # rescue Fog::Cache::CacheNotFound => e # puts "could not find any cache data for security groups on #{compute}" # get_resources_and_dump # # == Extending cache backends # # Currently this is on-disk using yml. If need be, this could be extended to other cache backends: # # Find references of yaml in this file, split out to strategy objects/diff backends etc. class Cache # cache associated not found class CacheNotFound < StandardError; end # cache directory problem class CacheDir < StandardError; end # where different caches per +service+ api keys, regions etc, are stored # see the +namespace_prefix=+ method. SANDBOX = ENV["HOME"] ? File.expand_path("~/.fog-cache") : File.expand_path(".fog-cache") # when a resource is used such as `server.cache.dump` the model klass is passed in # so that it can be identified from a different session. attr_reader :model # Loads cache associated to the +model_klass+ and +service+ into memory. # # If no cache is found, it will raise an error for handling: # # rescue Fog::Cache::CacheNotFound # set_initial_cache # def self.load(model_klass, service) cache_files = Dir.glob("#{namespace(model_klass, service)}/*") raise CacheNotFound if cache_files.empty? # collection_klass and model_klass should be the same across all instances # choose a valid cache record from the dump to use as a sample to deterine # which collection/model to instantiate. sample_path = cache_files.detect{ |path| valid_for_load?(path) } model_klass = const_get_compat(load_cache(sample_path)[:model_klass]) collection_klass = const_get_compat(load_cache(sample_path)[:collection_klass]) if load_cache(sample_path)[:collection_klass] # Load the cache data into actual ruby instances loaded = cache_files.map do |path| model_klass.new(load_cache(path)[:attrs]) if valid_for_load?(path) end.compact # Set the collection and service so they can be reloaded/connection is set properly. # See https://github.com/fog/fog-aws/issues/354#issuecomment-286789702 loaded.each do |i| i.collection = collection_klass.new(:service => service) if collection_klass i.instance_variable_set(:@service, service) end # uniqe-ify based on the total of attributes. duplicate cache can exist due to # `model#identity` not being unique. but if all attributes match, they are unique # and shouldn't be loaded again. uniq_loaded = uniq_w_block(loaded) { |i| i.attributes } if uniq_loaded.size != loaded.size Fog::Logger.warning("Found duplicate items in the cache. Expire all & refresh cache soon.") end # Fog models created, free memory of cached data used for creation. @memoized = nil uniq_loaded end # :nodoc: compatability for 1.8.7 1.9.3 def self.const_get_compat(strklass) # https://stackoverflow.com/questions/3163641/get-a-class-by-name-in-ruby strklass.split('::').inject(Object) do |mod, class_name| mod.const_get(class_name) end end # :nodoc: compatability for 1.8.7 1.9.3 def self.uniq_w_block(arr) ret, keys = [], [] arr.each do |x| key = block_given? ? yield(x) : x unless keys.include? key ret << x keys << key end end ret end # method to determine if a path can be loaded and is valid fog cache format. def self.valid_for_load?(path) data = load_cache(path) if data && data.is_a?(Hash) if [:identity, :model_klass, :collection_klass, :attrs].all? { |k| data.keys.include?(k) } return true else Fog::Logger.warning("Found corrupt items in the cache: #{path}. Expire all & refresh cache soon.\n\nData:#{File.read(path)}") return false end end end # creates on-disk cache of this specific +model_klass+ and +@service+ def self.create_namespace(model_klass, service) FileUtils.mkdir_p(self.namespace(model_klass, service)) end # Expires cache - this does not expire all cache associated. # Instead, this will remove all on-disk cache of this specific +model_klass+ and and +@service+ def self.expire_cache!(model_klass, service) FileUtils.rm_rf(namespace(model_klass, service)) end # cleans the `SANDBOX` - specific any resource cache of any namespace, # and any metadata associated to any. def self.clean! FileUtils.rm_rf(SANDBOX) end # loads yml cache from path on disk, used # to initialize Fog models. def self.load_cache(path) @memoized ||= {} return @memoized[path] if @memoized[path] @memoized[path] = YAML.load(File.read(path)) end def self.namespace_prefix=(name) @namespace_prefix = name end def self.namespace_prefix @namespace_prefix end # write any metadata - +hash+ information - specific to the namespaced cache in the session. # # you can retrieve this in other sessions, as long as +namespace_prefix+ is set # you can overwrite metadata over time. see test cases as examples. def self.write_metadata(h) if namespace_prefix.nil? raise CacheDir.new("Must set an explicit identifier/name for this cache. Example: 'serviceX-regionY'") unless namespace_prefix elsif !h.is_a?(Hash) raise CacheDir.new("metadta must be a hash of information like {:foo => 'bar'}") end mpath = File.join(SANDBOX, namespace_prefix, "metadata.yml") to_write = if File.exist?(mpath) YAML.dump(YAML.load(File.read(mpath)).merge!(h)) else YAML.dump(h) end mdir = File.join(SANDBOX, namespace_prefix) FileUtils.mkdir_p(mdir) if !File.exist?(mdir) File.open(mpath, "w") { |f| f.write(to_write) } end # retrive metadata for this +namespace+ of cache. returns empty {} if none found. def self.metadata mpath = File.join(SANDBOX, namespace_prefix, "metadata.yml") if File.exist?(mpath) metadata = YAML.load(File.read(mpath)) return metadata else return {} end end # The path/namespace where the cache is stored for a specific +model_klass+ and +@service+. def self.namespace(model_klass, service) raise CacheDir.new("Must set an explicit identifier/name for this cache. Example: 'serviceX-regionY'") unless namespace_prefix ns = File.join(SANDBOX, namespace_prefix, service.class.to_s, model_klass.to_s) ns = safe_path(ns) end def self.safe_path(klass) klass.to_s.gsub("::", "_").downcase end def initialize(model) @model = model end # Dump a Fog::Model resource. Every fog model/instance now has a +cache+ method/object injected in. # as such you can use the #dump method to save the attributes and metadata of that instance as cache # which can be re-used in some other session. def dump if !File.exist?(self.class.namespace(model.class, model.service)) self.class.create_namespace(model.class, model.service) end data = { :identity => model.identity, :model_klass => model.class.to_s, :collection_klass => model.collection && model.collection.class.to_s, :attrs => model.attributes } File.open(dump_to, "w") { |f| f.write(YAML.dump(data)) } end # the location of where to save this fog model/instance to. def dump_to # some fog models have an identity field that is duplicate. # duplicate identities can mean the cache for that already exists. # this means cache duplication is possible. # # see "dumping two models that have duplicate identity" test case. "#{self.class.namespace(model.class, model.service)}/#{model.identity}-#{SecureRandom.hex}.yml" end end end fog-core-1.45.0/lib/fog/core/collection.rb000066400000000000000000000061071314013014100202230ustar00rootroot00000000000000require "fog/core/deprecated_connection_accessors" module Fog # Fog::Collection class Collection < Array extend Fog::Attributes::ClassMethods include Fog::Attributes::InstanceMethods include Fog::Core::DeprecatedConnectionAccessors attr_reader :service Array.public_instance_methods(false).each do |method| next if [:reject, :select, :slice, :clear, :inspect].include?(method.to_sym) class_eval <<-EOS, __FILE__, __LINE__ def #{method}(*args) unless @loaded lazy_load end super end EOS end %w(reject select slice).each do |method| class_eval <<-EOS, __FILE__, __LINE__ def #{method}(*args) unless @loaded lazy_load end data = super self.clone.clear.concat(data) end EOS end def self.model(new_model = nil) if new_model.nil? @model else @model = new_model end end def clear @loaded = super end def create(attributes = {}) object = new(attributes) object.save object end def destroy(identity) new(:identity => identity).destroy end # Creates a new Fog::Collection based around the passed service # # @param [Hash] attributes # @option attributes [Fog::Service] service Instance of a service # # @return [Fog::Collection] # def initialize(attributes = {}) @service = attributes.delete(:service) @loaded = false merge_attributes(attributes) end def inspect Fog::Formatador.format(self) end def load(objects) clear && objects.each { |object| self << new(object) } self end def model self.class.instance_variable_get("@model") end def new(attributes = {}) unless attributes.is_a?(::Hash) raise ArgumentError, "Initialization parameters must be an attributes hash, got #{attributes.class} #{attributes.inspect}" end model.new( { :collection => self, :service => service }.merge(attributes) ) end def reload clear && lazy_load self end def table(attributes = nil) Fog::Formatador.display_table(map(&:attributes), attributes) end def to_json(_options = {}) Fog::JSON.encode(map(&:attributes)) end private def lazy_load all end end # Base class for collection classes whose 'all' method returns only a single # page of results and passes the 'Marker' option along as # self.filters[:marker] class PagedCollection < Collection def each(collection_filters = filters) if block_given? Kernel.loop do break unless filters[:marker] page = all(collection_filters) # We need to explicitly use the base 'each' method here on the page, # otherwise we get infinite recursion base_each = Fog::Collection.instance_method(:each) base_each.bind(page).call { |item| yield item } end end self end end end fog-core-1.45.0/lib/fog/core/connection.rb000066400000000000000000000112771314013014100202330ustar00rootroot00000000000000module Fog module Core # Fog::Core::Connection is a generic class to contain a HTTP link to an API. # # It is intended to be subclassed by providers who can then add their own # modifications such as authentication or response object. # class Connection class << self @@user_agents = [] def add_user_agent(str) if /\S+\/[\d|.]+/.match(str) @@user_agents << str else raise "User Agent must be in / notation." end end def user_agents agents = @@user_agents.dup agents << "fog/#{Fog::VERSION}" if defined?(Fog::VERSION) agents << "fog-core/#{Fog::Core::VERSION}" agents.uniq.compact.join(" ") end end # Prepares the connection and sets defaults for any future requests. # # @param [String] url The destination URL # @param persistent [Boolean] # @param [Hash] params # @option params [String] :body Default text to be sent over a socket. Only used if :body absent in Connection#request params # @option params [Hash] :headers The default headers to supply in a request. Only used if params[:headers] is not supplied to Connection#request # @option params [String] :host The destination host's reachable DNS name or IP, in the form of a String # @option params [String] :path Default path; appears after 'scheme://host:port/'. Only used if params[:path] is not supplied to Connection#request # @option params [String] :path_prefix Sticky version of the "path" arg. :XSpath_prefix => "foo/bar" with a request with :path => "blech" sends a request to path "foo/bar/blech" # @option params [Fixnum] :port The port on which to connect, to the destination host # @option params [Hash] :query Default query; appended to the 'scheme://host:port/path/' in the form of '?key=value'. Will only be used if params[:query] is not supplied to Connection#request # @option params [String] :scheme The protocol; 'https' causes OpenSSL to be used # @option params [String] :proxy Proxy server; e.g. 'http://myproxy.com:8888' # @option params [Fixnum] :retry_limit Set how many times we'll retry a failed request. (Default 4) # @option params [Class] :instrumentor Responds to #instrument as in ActiveSupport::Notifications # @option params [String] :instrumentor_name Name prefix for #instrument events. Defaults to 'excon' def initialize(url, persistent = false, params = {}) if params[:path_prefix] if params[:path] raise ArgumentError, "optional arg 'path' is invalid when 'path_prefix' is provided" end @path_prefix = params.delete(:path_prefix) end params[:debug_response] = true unless params.key?(:debug_response) params[:headers] ||= {} params.merge!(:persistent => params.fetch(:persistent, persistent)) params[:headers]["User-Agent"] ||= user_agent @excon = Excon.new(url, params) end # Makes a request using the connection using Excon # # @param [Hash] params # @option params [String] :body text to be sent over a socket # @option params [Hash] :headers The default headers to supply in a request # @option params [String] :host The destination host's reachable DNS name or IP, in the form of a String # @option params [String] :path appears after 'scheme://host:port/' # @option params [Fixnum] :port The port on which to connect, to the destination host # @option params [Hash] :query appended to the 'scheme://host:port/path/' in the form of '?key=value' # @option params [String] :scheme The protocol; 'https' causes OpenSSL to be used # @option params [Proc] :response_block # # @return [Excon::Response] # # @raise [Excon::Errors::StubNotFound] # @raise [Excon::Errors::Timeout] # @raise [Excon::Errors::SocketError] # def request(params, &block) @excon.request(handle_path_prefix_for(params), &block) end # Make {#request} available even when it has been overidden by a subclass # to allow backwards compatibility. # alias_method :original_request, :request protected :original_request # Closes the connection # def reset @excon.reset end private def user_agent self.class.user_agents end def handle_path_prefix_for(params) return params unless @path_prefix params[:path] = params[:path].sub(/^\//, "") params[:path] = "#{@path_prefix}/#{params[:path]}" params end end end end fog-core-1.45.0/lib/fog/core/credentials.rb000066400000000000000000000052341314013014100203650ustar00rootroot00000000000000require "yaml" module Fog require "fog/core/deprecation" # Sets the global configuration up from a Hash rather than using background loading from a file # # @example # Fog.credentials = { # :default => { # :example_url => "https://example.com/" # :example_username => "bob", # :example_password => "obo" # }, # :production => { # :example_username => "bob", # :example_password => "obo" # } # } # # @return [Hash] The newly assigned credentials class << self attr_writer :credentials end # Assign a new credential to use from configuration file # # @param [String, Symbol] new_credential name of new credential to use # @return [Symbol] name of the new credential def self.credential=(new_credential) @credentials = nil @credential = new_credential && new_credential.to_sym end # This is the named credential from amongst the configuration file being used or +:default+ # # @note This can be set using the +FOG_CREDENTIAL+ environment variable # # @return [Symbol] The credential to use in Fog def self.credential @credential ||= (ENV["FOG_CREDENTIAL"] && ENV["FOG_CREDENTIAL"].to_sym) || :default end # This returns the path to the configuration file being used globally to look for sets of # credentials # # @note This can be set using the +FOG_RC+ environment variable or defaults to +$HOME/.fog+ # # @return [String] The path for configuration_file def self.credentials_path @credential_path ||= begin path = ENV["FOG_RC"] || (ENV["HOME"] && File.directory?(ENV["HOME"]) && "~/.fog") File.expand_path(path) if path rescue nil end end # @return [String] The new path for credentials file def self.credentials_path=(new_credentials_path) @credentials = nil @credential_path = new_credentials_path end # @return [Hash] The credentials pulled from the configuration file # @raise [LoadError] Configuration unavailable in configuration file def self.credentials @credentials ||= begin if credentials_path && File.exist?(credentials_path) credentials = Fog::Core::Utils.prepare_service_settings(YAML.load_file(credentials_path)) (credentials && credentials[credential]) || Fog::Errors.missing_credentials else {} end end end # @deprecated Don't use! # @param [Object] key # @return [true] if key == :headers def self.symbolize_credential?(key) ![:headers].include?(key) end # @deprecated Use {Fog::Core::Utils.prepare_service_settings} instead def self.symbolize_credentials(hash) Fog::Core::Utils.prepare_service_settings(hash) end end fog-core-1.45.0/lib/fog/core/current_machine.rb000066400000000000000000000017331314013014100212360ustar00rootroot00000000000000require "thread" module Fog class CurrentMachine @lock = Mutex.new AMAZON_AWS_CHECK_IP = "http://checkip.amazonaws.com" def self.ip_address=(ip_address) @lock.synchronize do @ip_address = ip_address end end # Get the ip address of the machine from which this command is run. It is # recommended that you surround calls to this function with a timeout block # to ensure optimum performance in the case where the amazonaws checkip # service is unavailable. # # @example Get the current ip address # begin # Timeout::timeout(5) do # puts "Your ip address is #{Fog::CurrentMachine.ip_address}" # end # rescue Timeout::Error # puts "Service timeout" # end # # @raise [Excon::Errors::Error] if the net/http request fails. def self.ip_address @lock.synchronize do @ip_address ||= Excon.get(AMAZON_AWS_CHECK_IP).body.chomp end end end end fog-core-1.45.0/lib/fog/core/deprecated_connection_accessors.rb000066400000000000000000000027661314013014100244630ustar00rootroot00000000000000module Fog module Core # This module covers the shared code used by models and collections # that deprecates the confusing usage of 'connection' which was # actually intended to be an instance of Fog::Service module DeprecatedConnectionAccessors # Sets the Service but using the wrong name! # # @deprecated The connection name was wrong and confusing since it refered to the service # @param [Fog::Service] service An instance of a Fog service this collection is for # def connection=(service) Fog::Logger.deprecation("#connection= is deprecated, pass :service in at creation [light_black](#{caller.first})[/]") @service = service end # Returns the Service the collection is part of # # @deprecated #connection is deprecated due to confusing name, use #service instead # @return [Fog::Service] # def connection Fog::Logger.deprecation("#connection is deprecated, use #service instead [light_black](#{caller.first})[/]") @service end # Prepares the value of the service based on the passed attributes # # @note Intended for use where the service is required before the normal # initializer runs. The logic is run there with deprecation warnings. # # @param [Hash] attributes # @return [Fog::Service] # def prepare_service_value(attributes) @service = attributes[:service] || attributes[:connection] end end end end fog-core-1.45.0/lib/fog/core/deprecation.rb000066400000000000000000000012071314013014100203610ustar00rootroot00000000000000module Fog module Deprecation def deprecate(older, newer) module_eval <<-EOS, __FILE__, __LINE__ def #{older}(*args) Fog::Logger.deprecation("#{self} => ##{older} is deprecated, use ##{newer} instead [light_black](#{caller.first})[/]") send(:#{newer}, *args) end EOS end def self_deprecate(older, newer) module_eval <<-EOS, __FILE__, __LINE__ def self.#{older}(*args) Fog::Logger.deprecation("#{self} => ##{older} is deprecated, use ##{newer} instead [light_black](#{caller.first})[/]") send(:#{newer}, *args) end EOS end end end fog-core-1.45.0/lib/fog/core/errors.rb000066400000000000000000000057341314013014100174110ustar00rootroot00000000000000module Fog module Errors class Error < StandardError attr_accessor :verbose def self.slurp(error, message = nil) new_error = new(message || error.message) new_error.set_backtrace(error.backtrace) new_error.verbose = error.message new_error end end class MockNotImplemented < Fog::Errors::Error; end class NotFound < Fog::Errors::Error; end class LoadError < Fog::Errors::Error; end class TimeoutError < Fog::Errors::Error; end class NotImplemented < Fog::Errors::Error; end # @return [String] The error message that will be raised, if credentials cannot be found def self.missing_credentials missing_credentials_message = <<-YML Missing Credentials To run as '#{Fog.credential}', add the following to your resource config file: #{Fog.credentials_path} An alternate file may be used by placing its path in the FOG_RC environment variable ####################################################### # Fog Credentials File # # Key-value pairs should look like: # :aws_access_key_id: 022QF06E7MXBSAMPLE :#{Fog.credential}: :aws_access_key_id: :aws_secret_access_key: :bluebox_api_key: :bluebox_customer_id: :brightbox_client_id: :brightbox_secret: :clodo_api_key: :clodo_username: :digitalocean_api_key: :digitalocean_client_id: :go_grid_api_key: :go_grid_shared_secret: :google_client_email: :google_key_location: :google_project: :google_storage_access_key_id: :google_storage_secret_access_key: :hp_access_key: :hp_secret_key: :hp_tenant_id: :hp_avl_zone: :linode_api_key: :local_root: :bare_metal_cloud_password: :bare_metal_cloud_username: :public_key_path: :private_key_path: :openstack_api_key: :openstack_username: :openstack_auth_url: :openstack_tenant: :openstack_region: :ovirt_username: :ovirt_password: :ovirt_url: :libvirt_uri: :rackspace_api_key: :rackspace_username: :rackspace_servicenet: :rackspace_cdn_ssl: :rage4_email: :rage4_password: :riakcs_access_key_id: :riakcs_secret_access_key: :softlayer_username: :softlayer_api_key: :softlayer_default_datacenter: :softlayer_cluster: :softlayer_default_domain: :stormondemand_username: :stormondemand_password: :terremark_username: :terremark_password: :voxel_api_key: :voxel_api_secret: :zerigo_email: :zerigo_token: :dnsimple_email: :dnsimple_password: :dnsmadeeasy_api_key: :dnsmadeeasy_secret_key: :dreamhost_api_key: :cloudstack_host: :cloudstack_api_key: :cloudstack_secret_access_key: :vsphere_server: :vsphere_username: :vsphere_password: :libvirt_username: :libvirt_password: :libvirt_uri: :libvirt_ip_command: :ibm_username: :ibm_password: :vcloud_director_host: :vcloud_director_username: :vcloud_director_password: # # End of Fog Credentials File ####################################################### YML raise Fog::Errors::LoadError, missing_credentials_message end end end fog-core-1.45.0/lib/fog/core/hmac.rb000066400000000000000000000011211314013014100167670ustar00rootroot00000000000000module Fog class HMAC def initialize(type, key) @key = key case type when "sha1" setup_sha1 when "sha256" setup_sha256 end end def sign(data) @signer.call(data) end private def setup_sha1 @digest = OpenSSL::Digest.new("sha1") @signer = lambda do |data| OpenSSL::HMAC.digest(@digest, @key, data) end end def setup_sha256 @digest = OpenSSL::Digest.new("sha256") @signer = lambda do |data| OpenSSL::HMAC.digest(@digest, @key, data) end end end end fog-core-1.45.0/lib/fog/core/logger.rb000066400000000000000000000021151314013014100173420ustar00rootroot00000000000000module Fog class Logger @channels = { :deprecation => ::STDERR, :warning => ::STDERR } @channels[:debug] = ::STDERR if ENV["DEBUG"] def self.[](channel) @channels[channel] end def self.[]=(channel, value) @channels[channel] = value end def self.debug(message) write(:debug, "[light_black][fog][DEBUG] #{message}[/]\n") end def self.deprecation(message) write(:deprecation, "[yellow][fog][DEPRECATION] #{message}[/]\n") end def self.warning(message) write(:warning, "[yellow][fog][WARNING] #{message}[/]\n") end def self.write(key, value) channel = @channels[key] if channel message = if channel.tty? value.gsub(Fog::Formatador::PARSE_REGEX) { "\e[#{Fog::Formatador::STYLES[$1.to_sym]}m" }.gsub(Fog::Formatador::INDENT_REGEX, "") else value.gsub(Fog::Formatador::PARSE_REGEX, "").gsub(Fog::Formatador::INDENT_REGEX, "") end channel.write(message) end nil end end end fog-core-1.45.0/lib/fog/core/mock.rb000066400000000000000000000050331314013014100170160ustar00rootroot00000000000000module Fog @mocking = false def self.mock! @mocking = true end def self.unmock! @mocking = false end class << self attr_reader :mocking alias_method :mock?, :mocking alias_method :mocking?, :mocking end module Mock @delay = 1 class << self attr_reader :delay end def self.delay=(new_delay) raise ArgumentError, "delay must be non-negative" unless new_delay >= 0 @delay = new_delay end def self.not_implemented(message = "Contributions welcome!") raise Fog::Errors::MockNotImplemented, message end def self.random_ip(opts = { :version => :v4 }) version = opts[:version] if version == :v6 bit_length = 128 family = Socket::AF_INET6 elsif version == :v4 bit_length = 32 family = Socket::AF_INET else raise ArgumentError, "Unknown IP version: #{version}" end seed = 1 + rand((2**bit_length) - 1) IPAddr.new(seed, family).to_s end def self.random_base64(length) random_selection( "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/", length ) end def self.random_hex(length) max = ("f" * length).to_i(16) rand(max).to_s(16).rjust(length, "0") end def self.random_letters(length) random_selection( "abcdefghijklmnopqrstuvwxyz", length ) end def self.random_numbers(length) max = ("9" * length).to_i rand(max).to_s end def self.random_letters_and_numbers(length) random_selection( "ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789", length ) end def self.random_selection(characters, length) selection = "" length.times do position = rand(characters.length) selection << characters[position..position] end selection end def self.reset mocked_services = [] Fog.constants.map do |x| next if Fog.autoload?(x) x_const = Fog.const_get(x) x_const.respond_to?(:constants) && x_const.constants.map do |y| next if x_const.autoload?(y) y_const = x_const.const_get(y) y_const.respond_to?(:constants) && y_const.constants.map do |z| next if y_const.autoload?(z) mocked_services << y_const.const_get(z) if z.to_sym == :Mock end end end mocked_services.each do |mocked_service| next unless mocked_service.respond_to?(:reset) mocked_service.reset end end end end fog-core-1.45.0/lib/fog/core/model.rb000066400000000000000000000043371314013014100171730ustar00rootroot00000000000000require "fog/core/deprecated_connection_accessors" require "fog/core/cache" module Fog class Model extend Fog::Attributes::ClassMethods include Fog::Attributes::InstanceMethods include Fog::Core::DeprecatedConnectionAccessors attr_accessor :collection attr_reader :service def initialize(new_attributes = {}) # TODO: Remove compatibility with old connection option attribs = new_attributes.clone @service = attribs.delete(:service) if @service.nil? && attribs[:connection] Fog::Logger.deprecation("Passing :connection option is deprecated, use :service instead [light_black](#{caller.first})[/]") @service = attribs[:connection] end merge_attributes(attribs) end def cache Fog::Cache.new(self) end def inspect Fog::Formatador.format(self) end def ==(o) unless o.is_a?(Fog::Model) super else if (o.identity.nil? and self.identity.nil?) o.object_id == self.object_id else o.class == self.class and o.identity == self.identity end end end def reload requires :identity data = begin collection.get(identity) rescue Excon::Errors::SocketError nil end return unless data new_attributes = data.attributes merge_attributes(new_attributes) self end def to_json(_options = {}) Fog::JSON.encode(attributes) end def symbolize_keys(hash) return nil if hash.nil? hash.reduce({}) do |options, (key, value)| options[(key.to_sym rescue key) || key] = value options end end def wait_for(timeout = Fog.timeout, interval = Fog.interval, &block) reload_has_succeeded = false duration = Fog.wait_for(timeout, interval) do # Note that duration = false if it times out if reload reload_has_succeeded = true instance_eval(&block) else false end end if reload_has_succeeded return duration # false if timeout; otherwise {:duration => elapsed time } else raise Fog::Errors::Error, "Reload failed, #{self.class} #{identity} not present." end end end end fog-core-1.45.0/lib/fog/core/provider.rb000066400000000000000000000012611314013014100177160ustar00rootroot00000000000000module Fog class << self attr_writer :providers end def self.providers @providers ||= {} end module Provider def self.extended(base) provider = base.to_s.split("::").last Fog.providers[provider.downcase.to_sym] = provider end def [](service_key) eval(@services_registry[service_key]).new end def service(new_service, constant_string) Fog.services[new_service] ||= [] Fog.services[new_service] |= [to_s.split("::").last.downcase.to_sym] @services_registry ||= {} @services_registry[new_service] = [to_s, constant_string].join("::") end def services @services_registry.keys end end end fog-core-1.45.0/lib/fog/core/scp.rb000066400000000000000000000062551314013014100166610ustar00rootroot00000000000000module Fog module SCP def self.new(address, username, options = {}) if Fog.mocking? Fog::SCP::Mock.new(address, username, options) else Fog::SCP::Real.new(address, username, options) end end class Mock def self.data @data ||= Hash.new do |hash, key| hash[key] = [] end end def initialize(address, username, options) @address = address @username = username @options = options end def upload(local_path, remote_path, upload_options = {}) self.class.data[@address] << { :username => @username, :options => @options, :local_path => local_path, :remote_path => remote_path, :upload_options => upload_options } end def download(remote_path, local_path, download_options = {}) self.class.data[@address] << { :username => @username, :options => @options, :remote_path => remote_path, :local_path => local_path, :download_options => download_options } end end class Real def initialize(address, username, options) begin require "net/scp" rescue LoadError Fog::Logger.warning("'net/scp' missing, please install and try again.") exit(1) end key_manager = Net::SSH::Authentication::KeyManager.new(nil, options) unless options[:key_data] || options[:keys] || options[:password] || key_manager.agent raise ArgumentError, ":key_data, :keys, :password or a loaded ssh-agent is required to initialize SSH" end options[:timeout] = 30 if options[:key_data] || options[:keys] options[:keys_only] = true # Explicitly set these so net-ssh doesn't add the default keys # as seen at https://github.com/net-ssh/net-ssh/blob/master/lib/net/ssh/authentication/session.rb#L131-146 options[:keys] = [] unless options[:keys] options[:key_data] = [] unless options[:key_data] end @address = address @username = username @options = { :paranoid => false }.merge(options) end def upload(local_path, remote_path, upload_options = {}, &block) Net::SCP.start(@address, @username, @options) do |scp| scp.upload!(local_path, remote_path, upload_options) do |ch, name, sent, total| block.call(ch, name, sent, total) if block end end rescue Exception => error raise error end def download(remote_path, local_path, download_options = {}, &block) Net::SCP.start(@address, @username, @options) do |scp| scp.download!(remote_path, local_path, download_options) do |ch, name, sent, total| block.call(ch, name, sent, total) if block end end rescue Exception => error raise error end end end end fog-core-1.45.0/lib/fog/core/service.rb000066400000000000000000000236451314013014100175360ustar00rootroot00000000000000require "fog/core/utils" module Fog def self.services @services ||= {} end class Service class Error < Fog::Errors::Error; end class NotFound < Fog::Errors::NotFound; end module NoLeakInspector def inspect "#<#{self.class}:#{object_id} #{(instance_variables - service.secrets).map { |iv| [iv, instance_variable_get(iv).inspect].join("=") }.join(" ")}>" end end module Collections def collections service.collections end def mocked_requests service.mocked_requests end def requests service.requests end end class << self def inherited(child) child.class_eval <<-EOS, __FILE__, __LINE__ class Error < Fog::Service::Error; end class NotFound < Fog::Service::NotFound; end module Collections include Fog::Service::Collections def service #{child} end end def self.service #{child} end EOS end # {Fog::Service} is (unfortunately) both a builder class and the subclass for any fog service. # # Creating a {new} instance using the builder will return either an instance of # +Fog::::::Real+ or +Fog::::::Mock+ based on the value # of {Fog.mock?} when the builder is used. # # Each provider can require or recognize different settings (often prefixed with the providers # name). These settings map to keys in the +~/.fog+ file. # # Settings can be passed as either a Hash or an object that responds to +config_service?+ with # +true+. This object will be passed through unchanged to the +Real+ or +Mock+ service that is # created. It is up to providers to adapt services to use these config objects. # # @abstract Subclass and implement real or mock code # # @param [Hash,#config_service?] config # Settings or an object used to build a service instance # @option config [Hash] :headers # Passed to the underlying {Fog::Core::Connection} unchanged # # @return [Fog::Service::Provider::Real] if created while mocking is disabled # @return [Fog::Service::Provider::Mock] if created while mocking is enabled # @raise [ArgumentError] if a setting required by the provider was not passed in # # @example Minimal options (dependent on ~/.fog) # @service = Fog::Compute::Example.new # => <#Fog::Compute::Example::Real> # # @example Mocked service # Fog.mock! # @service = Fog::Compute::Example.new # => <#Fog::Compute::Example::Mock> # # @example Configured using many options (options merged into ~/.fog) # @options = { # :example_username => "fog", # :example_password => "fog" # } # @service = Fog::Compute::Example.new(@options) # # @example Configured using external config object (~/.fog ignored completely) # @config = Fog::Example::Config.new(...) # @service = Fog::Compute::Example.new(@config) # def new(config = {}) if config.respond_to?(:config_service?) && config.config_service? cleaned_settings = config else cleaned_settings = handle_settings(config) end setup_requirements svc = service if Fog.mocking? while svc != Fog::Service service::Mock.send(:include, svc::Collections) svc = svc.superclass end service::Mock.new(cleaned_settings) else while svc != Fog::Service service::Real.send(:include, svc::Collections) svc = svc.superclass end service::Real.send(:include, service::NoLeakInspector) service::Real.new(cleaned_settings) end end # @deprecated def fetch_credentials(_options) # attempt to load credentials from config file Fog.credentials.reject { |key, _value| !(recognized | requirements).include?(key) } rescue ::Fog::Errors::LoadError # if there are no configured credentials, do nothing {} end def setup_requirements if superclass.respond_to?(:setup_requirements) superclass.setup_requirements end @required ||= false return false if @required require_models require_collections_and_define require_requests_and_mock @required = true end # @note This path is used to require model and collection files def model_path(new_path) @model_path = new_path end def collection(new_collection, path = nil) collection_files << [path, new_collection] collections << new_collection end def collection_files @collection_files ||= [] end def collections @collections ||= [] end def coerce_options(options) options.each do |key, value| value_string = value.to_s.downcase if value.nil? options.delete(key) elsif value_string.to_i.to_s == value options[key] = value.to_i else options[key] = case value_string when "false" false when "true" true else value end end end end def mocked_requests @mocked_requests ||= [] end def model(new_model, path = nil) model_files << [path, new_model] models << [new_model] end def model_files @model_files ||= [] end def models @models ||= [] end def request_path(new_path) @request_path = new_path end def request(new_request, path = nil) requests << [path, new_request] end def requests @requests ||= [] end def secrets(*args) if args.empty? @secrets ||= [] else args.reduce(secrets) do |secrets, secret| secrets << "@#{secret}".to_sym end end end def requires(*args) requirements.concat(args) end def requirements @requirements ||= [] end def recognizes(*args) recognized.concat(args) end def recognized @recognized ||= [:connection_options] end def validate_options(options) keys = [] options.each_pair do |key, value| keys << key unless value.nil? end missing = requirements - keys unless missing.empty? raise ArgumentError, "Missing required arguments: #{missing.join(", ")}" end unless recognizes.empty? unrecognized = options.keys - requirements - recognized unless unrecognized.empty? Fog::Logger.warning("Unrecognized arguments: #{unrecognized.join(", ")}") end end end private # This is the original way service settings were handled. Settings from +~/.fog+ were merged # together with the passed options, keys are turned to symbols and coerced into Boolean or # Fixnums. # # If the class has declared any required settings then {ArgumentError} will be raised. # # Any setting that is not whitelisted will cause a warning to be output. # def handle_settings(settings) combined_settings = fetch_credentials(settings).merge(settings) prepared_settings = Fog::Core::Utils.prepare_service_settings(combined_settings) validate_options(prepared_settings) coerce_options(prepared_settings) end # This will attempt to require all model files declared by the service using fog"s DSL def require_models model_files.each { |model| require_item(model, @model_path) } end def require_collections_and_define collection_files.each do |collection| require_item(collection, @model_path) constant = camel_case_collection_name(collection.last) service::Collections.module_eval <<-EOS, __FILE__, __LINE__ def #{collection.last}(attributes = {}) #{service}::#{constant}.new({ :service => self }.merge(attributes)) end EOS end end # This converts names of collections from Symbols as defined in the DSL (+:database_server+) # into CamelCase version (+DatabaseServer+) for metaprogramming skulduggery. # # @param [String,Symbol] collection The name of the collection broken with underscores # @return [String] in camel case def camel_case_collection_name(collection) collection.to_s.split("_").map(&:capitalize).join end # This will attempt to require all request files declared in the service using fog"s DSL def require_requests_and_mock requests.each do |request| require_item(request, @request_path) if service::Mock.method_defined?(request.last) mocked_requests << request.last else service::Mock.module_eval <<-EOS, __FILE__, __LINE__ def #{request.last}(*args) Fog::Mock.not_implemented end EOS end end end # Requires the correct file for an item (collection, model, or request). # # @param [Array] item # An item to require. Should be an array in the form of [path, file]. # @param [String] fallback_dir # The directory to look for the file in if the first element of `item` # is nil. # @return [Boolean] Returns the same as `Kernel#require`. def require_item(item, fallback_dir) path, file = item require File.join(path || fallback_dir, file.to_s) end end end end fog-core-1.45.0/lib/fog/core/services_mixin.rb000066400000000000000000000032761314013014100211230ustar00rootroot00000000000000module Fog module ServicesMixin def [](provider) new(:provider => provider) end def new(attributes) attributes = attributes.dup # Prevent delete from having side effects provider = attributes.delete(:provider).to_s.downcase.to_sym provider_name = Fog.providers[provider] raise ArgumentError, "#{provider} is not a recognized provider" unless providers.include?(provider) require_service_provider_library(service_name.downcase, provider) spc = service_provider_constant(service_name, provider_name) spc.new(attributes) rescue LoadError, NameError # Only rescue errors in finding the libraries, allow connection errors through to the caller raise Fog::Service::NotFound, "#{provider} has no #{service_name.downcase} service" end def providers Fog.services[service_name.downcase.to_sym] || [] end private def require_service_provider_library(service, provider) require "fog/#{provider}/#{service}" rescue LoadError # Try to require the service provider in an alternate location require "fog/#{service}/#{provider}" end def service_provider_constant(service_name, provider_name) Fog.const_get(service_name).const_get(*const_get_args(provider_name)) rescue NameError # Try to find the constant from in an alternate location Fog.const_get(provider_name).const_get(*const_get_args(service_name)) end # Ruby 1.8 does not support the second 'inherit' argument to #const_get def const_get_args(*args) if RUBY_VERSION < '1.9' args else args + [false] end end def service_name name.split("Fog::").last end end end fog-core-1.45.0/lib/fog/core/ssh.rb000066400000000000000000000073751314013014100166750ustar00rootroot00000000000000require "delegate" module Fog module SSH def self.new(address, username, options = {}) if Fog.mocking? Fog::SSH::Mock.new(address, username, options) else Fog::SSH::Real.new(address, username, options) end end class Mock def self.data @data ||= Hash.new do |hash, key| hash[key] = [] end end def self.reset @data = nil end def initialize(address, username, options) @address = address @username = username @options = options end def run(commands, &_blk) self.class.data[@address] << { :commands => commands, :username => @username, :options => @options } end end class Real def initialize(address, username, options) begin require "net/ssh" rescue LoadError Fog::Logger.warning("'net/ssh' missing, please install and try again.") exit(1) end key_manager = Net::SSH::Authentication::KeyManager.new(nil, options) unless options[:key_data] || options[:keys] || options[:password] || key_manager.agent raise ArgumentError, ":key_data, :keys, :password or a loaded ssh-agent is required to initialize SSH" end options[:timeout] ||= 30 if options[:key_data] || options[:keys] options[:keys_only] = true # Explicitly set these so net-ssh doesn"t add the default keys # as seen at https://github.com/net-ssh/net-ssh/blob/master/lib/net/ssh/authentication/session.rb#L131-146 options[:keys] = [] unless options[:keys] options[:key_data] = [] unless options[:key_data] end @address = address @username = username @options = { :paranoid => false }.merge(options) end def run(commands, &blk) commands = [*commands] results = [] begin Net::SSH.start(@address, @username, @options) do |ssh| commands.each do |command| result = Result.new(command) ssh.open_channel do |ssh_channel| ssh_channel.request_pty ssh_channel.exec(command) do |channel, success| unless success raise "Could not execute command: #{command.inspect}" end channel.on_data do |_ch, data| result.stdout << data yield [data, ""] if blk end channel.on_extended_data do |_ch, type, data| next unless type == 1 result.stderr << data yield ["", data] if blk end channel.on_request("exit-status") do |_ch, data| result.status = data.read_long end channel.on_request("exit-signal") do |_ch, _data| result.status = 255 end end end ssh.loop results << result end end rescue Net::SSH::HostKeyMismatch => exception exception.remember_host! sleep 0.2 retry end results end end class Result attr_accessor :command, :stderr, :stdout, :status def display_stdout data = stdout.split("\r\n") if data.is_a?(String) Fog::Formatador.display_line(data) elsif data.is_a?(Array) Fog::Formatador.display_lines(data) end end def display_stderr Fog::Formatador.display_line(stderr.split("\r\n")) end def initialize(command) @command = command @stderr = "" @stdout = "" end end end end fog-core-1.45.0/lib/fog/core/stringify_keys.rb000066400000000000000000000007641314013014100211440ustar00rootroot00000000000000module Fog module StringifyKeys # Returns a new hash with all keys converted to strings. def self.stringify(original_hash) transform_hash(original_hash) do |hash, key, value| hash[key.to_s] = value end end private # http://devblog.avdi.org/2009/11/20/hash-transforms-in-ruby/ def self.transform_hash(original, &block) original.reduce({}) do |result, (key, value)| block.call(result, key, value) result end end end end fog-core-1.45.0/lib/fog/core/time.rb000066400000000000000000000010751314013014100170250ustar00rootroot00000000000000require "time" module Fog class Time < ::Time DAYS = %w(Sun Mon Tue Wed Thu Fri Sat) MONTHS = %w(Jan Feb Mar Apr May Jun Jul Aug Sep Oct Nov Dec) def self.now at(::Time.now - offset) end def self.now=(new_now) old_now = ::Time.now @offset = old_now - new_now new_now end def self.offset @offset ||= 0 end def to_date_header utc.strftime("#{DAYS[utc.wday]}, %d #{MONTHS[utc.month - 1]} %Y %H:%M:%S +0000") end def to_iso8601_basic utc.strftime("%Y%m%dT%H%M%SZ") end end end fog-core-1.45.0/lib/fog/core/utils.rb000066400000000000000000000017011314013014100172230ustar00rootroot00000000000000module Fog module Core module Utils # This helper prepares a Hash of settings for passing into {Fog::Service.new}. # # The only special consideration is if +:header+ key is passed in the contents are unchanged. This # allows the headers to be passed through to requests to customise HTTP headers without them being # broken by the +#to_sym+ calls. # # @param [Hash] settings The String based Hash to prepare # @option settings [Hash] :headers Passed to the underlying {Fog::Core::Connection} unchanged # @return [Hash] # def self.prepare_service_settings(settings) if settings.is_a? Hash copy = [] settings.each do |key, value| obj = ![:headers].include?(key) ? prepare_service_settings(value) : value copy.push(key.to_sym, obj) end Hash[*copy] else settings end end end end end fog-core-1.45.0/lib/fog/core/uuid.rb000066400000000000000000000007121314013014100170320ustar00rootroot00000000000000require "securerandom" module Fog class UUID class << self def uuid if supported? SecureRandom.uuid else ary = SecureRandom.random_bytes(16).unpack("NnnnnN") ary[2] = (ary[2] & 0x0fff) | 0x4000 ary[3] = (ary[3] & 0x3fff) | 0x8000 "%08x-%04x-%04x-%04x-%04x%08x" % ary end end def supported? SecureRandom.respond_to?(:uuid) end end end end fog-core-1.45.0/lib/fog/core/version.rb000066400000000000000000000000721314013014100175500ustar00rootroot00000000000000module Fog module Core VERSION = "1.45.0" end end fog-core-1.45.0/lib/fog/core/wait_for.rb000066400000000000000000000007521314013014100177020ustar00rootroot00000000000000module Fog def self.wait_for(timeout = Fog.timeout, interval = Fog.interval, &_block) duration = 0 start = Time.now retries = 0 loop do break if yield if duration > timeout raise Errors::TimeoutError, "The specified wait_for timeout (#{timeout} seconds) was exceeded" end sleep(interval.respond_to?(:call) ? interval.call(retries += 1).to_f : interval.to_f) duration = Time.now - start end { :duration => duration } end end fog-core-1.45.0/lib/fog/core/wait_for_defaults.rb000066400000000000000000000015151314013014100215670ustar00rootroot00000000000000module Fog @interval = lambda { |retries| [2**(retries - 1), @max_interval].min } class << self attr_reader :interval end def self.interval=(interval) if interval.is_a?(Proc) raise ArgumentError, "interval proc must return a positive" unless interval.call(1) >= 0 else raise ArgumentError, "interval must be non-negative" unless interval >= 0 end @interval = interval end @timeout = 600 class << self attr_reader :timeout end def self.timeout=(timeout) raise ArgumentError, "timeout must be non-negative" unless timeout >= 0 @timeout = timeout end @max_interval = 60 class << self attr_reader :max_interval end def self.max_interval=(interval) raise ArgumentError, "interval must be non-negative" unless interval >= 0 @max_interval = interval end end fog-core-1.45.0/lib/fog/core/whitelist_keys.rb000066400000000000000000000003171314013014100211340ustar00rootroot00000000000000module Fog module WhitelistKeys def self.whitelist(hash, valid_keys) valid_hash = StringifyKeys.stringify(hash) Hash[valid_hash.select { |k, _v| valid_keys.include?(k) }] end end end fog-core-1.45.0/lib/fog/dns.rb000066400000000000000000000012021314013014100157130ustar00rootroot00000000000000module Fog module DNS extend Fog::ServicesMixin def self.new(orig_attributes) attributes = orig_attributes.dup # prevent delete from having side effects case attributes.delete(:provider).to_s.downcase.to_sym when :stormondemand require "fog/dns/storm_on_demand" Fog::DNS::StormOnDemand.new(attributes) else super(orig_attributes) end end def self.zones zones = [] providers.each do |provider| begin zones.concat(self[provider].zones) rescue # ignore any missing credentials/etc end end zones end end end fog-core-1.45.0/lib/fog/formatador.rb000066400000000000000000000045341314013014100173000ustar00rootroot00000000000000module Fog # Fog::Formatador module Formatador PARSE_REGEX = ::Formatador::PARSE_REGEX STYLES = ::Formatador::STYLES INDENT_REGEX = ::Formatador::INDENT_REGEX def self.formatador Thread.current[:formatador] ||= ::Formatador.new end def self.format(object, opts = { :include_nested => true }) string = init_string(object) indent { string << object_string(object, opts) } string << "#{indentation}>" end def self.display_line(data) ::Formatador.display_line(data) end def self.display_lines(data) ::Formatador.display_lines(data) end def self.display_compact_table(hashes, keys = nil, &block) ::Formatador.display_compact_table(hashes, keys, &block) end def self.display_table(hashes, keys = nil, &block) ::Formatador.display_table(hashes, keys, &block) end def self.redisplay_progressbar(current, total, options = {}) ::Formatador.redisplay_progressbar(current, total, options = {}) end private def self.indent(&block) formatador.indent(&block) end def self.indentation formatador.indentation end def self.init_string(object) "#{indentation}<#{object.class.name}\n" end def self.object_string(object, opts) string = "#{attribute_string(object)}" string << "#{nested_objects_string(object)}" if opts[:include_nested] string end def self.attribute_string(object) return "" unless object.class.respond_to?(:attributes) if object.class.attributes.empty? "" else "#{indentation}#{object_attributes(object)}\n" end end def self.nested_objects_string(object) nested = "" return nested if object.respond_to?(:empty) and object.empty? return nested unless object.is_a?(Enumerable) nested = "#{indentation}[\n" indent { nested << indentation + inspect_object(object) } nested << "#{indentation}\n#{indentation}]\n" end def self.object_attributes(object) attrs = object.class.attributes.map do |attr| "#{attr}=#{object.send(attr).inspect}" end attrs.join(",\n#{indentation}") end def self.inspect_object(object) return "" unless object.is_a?(Enumerable) object.map { |o| indentation + o.inspect }.join(", \n#{indentation}") end end end fog-core-1.45.0/lib/fog/identity.rb000066400000000000000000000001051314013014100167610ustar00rootroot00000000000000module Fog module Identity extend Fog::ServicesMixin end end fog-core-1.45.0/lib/fog/image.rb000066400000000000000000000001021314013014100162070ustar00rootroot00000000000000module Fog module Image extend Fog::ServicesMixin end end fog-core-1.45.0/lib/fog/introspection.rb000066400000000000000000000001121314013014100200260ustar00rootroot00000000000000module Fog module Introspection extend Fog::ServicesMixin end end fog-core-1.45.0/lib/fog/metering.rb000066400000000000000000000001051314013014100167420ustar00rootroot00000000000000module Fog module Metering extend Fog::ServicesMixin end end fog-core-1.45.0/lib/fog/monitoring.rb000066400000000000000000000006341314013014100173240ustar00rootroot00000000000000module Fog module Monitoring extend Fog::ServicesMixin def self.new(orig_attributes) attributes = orig_attributes.dup provider = attributes.delete(:provider).to_s.downcase.to_sym if provider == :stormondemand require "fog/monitoring/storm_on_demand" Fog::Monitoring::StormOnDemand.new(attributes) else super(orig_attributes) end end end end fog-core-1.45.0/lib/fog/network.rb000066400000000000000000000007051314013014100166270ustar00rootroot00000000000000module Fog module Network extend Fog::ServicesMixin def self.new(orig_attributes) attributes = orig_attributes.dup # Prevent delete from having side effects provider = attributes.delete(:provider).to_s.downcase.to_sym if provider == :stormondemand require "fog/network/storm_on_demand" return Fog::Network::StormOnDemand.new(attributes) else super(orig_attributes) end end end end fog-core-1.45.0/lib/fog/nfv.rb000066400000000000000000000001001314013014100157140ustar00rootroot00000000000000module Fog module NFV extend Fog::ServicesMixin end end fog-core-1.45.0/lib/fog/orchestration.rb000066400000000000000000000001121314013014100200120ustar00rootroot00000000000000module Fog module Orchestration extend Fog::ServicesMixin end end fog-core-1.45.0/lib/fog/schema/000077500000000000000000000000001314013014100160475ustar00rootroot00000000000000fog-core-1.45.0/lib/fog/schema/data_validator.rb000066400000000000000000000131221314013014100213510ustar00rootroot00000000000000module Fog module Schema # This validates a data object against a Ruby based schema to see # if they match. # # * An object matches the schema if +==+ or +===+ returns +true+ # * Hashes match if all the key's values match the classes given # in the schema as well. This can be configured in the options # * Arrays match when every element in the data matches the case # given in the schema. # # The schema and validation are very simple and probably not # suitable for some cases. # # The following classes can be used to check for special behaviour # # * Fog::Boolean - value may be +true+ or +false+ # * Fog::Nullable::Boolean - value may be +true+, +false+ or +nil+ # * Fog::Nullable::Integer - value may be an Integer or +nil+ # * Fog::Nullable::String # * Fog::Nullable::Time # * Fog::Nullable::Float # * Fog::Nullable::Hash # * Fog::Nullable::Array # # All the "nullable" objects will pass if the value is of the class # or if it is +nil+. This allows you to match APIs that may include # keys when the value is not available in some cases but will # always be a String. Such as an password that is only displayed # on the reset action. # # The keys for "nullable" resources should always be present but # original matcher had a bug that allowed these to also appear to # work as optional keys/values. # # If you need the original behaviour, data with a missing key is # still valid, then you may pass the +:allow_optional_rules+ # option to the #validate method. # # That is not recommended because you are describing a schema # with optional keys in a format that does not support it. # # Setting +:allow_extra_keys+ as +true+ allows the data to # contain keys not declared by the schema and still pass. This # is useful if new attributes appear in the API in a backwards # compatible manner and can be ignored. # # This is the behaviour you would have seen with +strict+ being # +false+ in the original test helper. # # @example Schema example # { # "id" => String, # "ram" => Integer, # "disks" => [ # "size" => Float # ], # "dns_name" => Fog::Nullable::String, # "active" => Fog::Boolean, # "created" => DateTime # } # class DataValidator # This returns the last message set by the validator # # @return [String] attr_reader :message def initialize @message = nil end # Checks if the data structure matches the schema passed in and # returns +true+ if it fits. # # @param [Object] data Hash or Array to check # @param [Object] schema Schema pattern to check against # @param [Boolean] options # @option options [Boolean] :allow_extra_keys # If +true+ does not fail if extra keys are in the data # that are not in the schema. # @option options [Boolean] :allow_optional_rules # If +true+ does not fail if extra keys are in the schema # that do not match the data. Not recommended! # # @return [Boolean] Did the data fit the schema? def validate(data, schema, options = {}) valid = validate_value(schema, data, options) unless valid @message = "#{data.inspect} does not match #{schema.inspect}" end valid end private # This contains a slightly modified version of the Hashidator gem # but unfortunately the gem does not cope with Array schemas. # # @see https://github.com/vangberg/hashidator/blob/master/lib/hashidator.rb # def validate_value(validator, value, options) Fog::Logger.write :debug, "[yellow][DEBUG] #{value.inspect} against #{validator.inspect}[/]\n" case validator when Array return false if value.is_a?(Hash) value.respond_to?(:all?) && value.all? { |x| validate_value(validator[0], x, options) } when Symbol value.respond_to? validator when Hash return false if value.is_a?(Array) # When being strict values not specified in the schema are fails # Validator is empty but values are not return false if !options[:allow_extra_keys] && value.respond_to?(:empty?) && !value.empty? && validator.empty? # Validator has rules left but no more values return false if !options[:allow_optional_rules] && value.respond_to?(:empty?) && value.empty? && !validator.empty? validator.all? do |key, sub_validator| Fog::Logger.write :debug, "[blue][DEBUG] #{key.inspect} against #{sub_validator.inspect}[/]\n" validate_value(sub_validator, value[key], options) end else result = validator == value result = validator === value unless result # Repeat unless we have a Boolean already unless result.is_a?(TrueClass) || result.is_a?(FalseClass) result = validate_value(result, value, options) end if result Fog::Logger.write :debug, "[green][DEBUG] Validation passed: #{value.inspect} against #{validator.inspect}[/]\n" else Fog::Logger.write :debug, "[red][DEBUG] Validation failed: #{value.inspect} against #{validator.inspect}[/]\n" end result end end end end end fog-core-1.45.0/lib/fog/storage.rb000066400000000000000000000043271314013014100166060ustar00rootroot00000000000000module Fog module Storage extend Fog::ServicesMixin def self.new(orig_attributes) begin # Use mime/types/columnar if available, for reduced memory usage require 'mime/types/columnar' rescue LoadError begin require 'mime/types' rescue LoadError Fog::Logger.warning("'mime-types' missing, please install and try again.") raise end end attributes = orig_attributes.dup # prevent delete from having side effects case attributes.delete(:provider).to_s.downcase.to_sym when :internetarchive require "fog/internet_archive/storage" Fog::Storage::InternetArchive.new(attributes) when :stormondemand require "fog/storage/storm_on_demand" Fog::Storage::StormOnDemand.new(attributes) else super(orig_attributes) end end def self.directories directories = [] providers.each do |provider| begin directories.concat(self[provider].directories) rescue # ignore any missing credentials/etc end end directories end def self.get_body_size(body) if body.respond_to?(:encoding) original_encoding = body.encoding body = body.dup if body.frozen? body = body.force_encoding('BINARY') end size = if body.respond_to?(:bytesize) body.bytesize elsif body.respond_to?(:size) body.size elsif body.respond_to?(:stat) body.stat.size else 0 end if body.respond_to?(:encoding) body.force_encoding(original_encoding) end size end def self.get_content_type(data) if data.respond_to?(:path) && !data.path.nil? filename = ::File.basename(data.path) unless (mime_types = MIME::Types.of(filename)).empty? mime_types.first.content_type end end end def self.parse_data(data) { :body => data, :headers => { "Content-Length" => get_body_size(data), "Content-Type" => get_content_type(data) # "Content-MD5" => Base64.encode64(Digest::MD5.digest(metadata[:body])).strip } } end end end fog-core-1.45.0/lib/fog/support.rb000066400000000000000000000006241314013014100166520ustar00rootroot00000000000000module Fog module Support extend Fog::ServicesMixin def self.new(orig_attributes) attributes = orig_attributes.dup provider = attributes.delete(:provider).to_s.downcase.to_sym if provider == :stormondemand require "fog/support/storm_on_demand" Fog::Support::StormOnDemand.new(attributes) else super(orig_attributes) end end end end fog-core-1.45.0/lib/fog/test_helpers.rb000066400000000000000000000010161314013014100176330ustar00rootroot00000000000000# So downstream users can use the test_helpers require "fog/test_helpers/helper" require "fog/test_helpers/collection_helper" require "fog/test_helpers/formats_helper" require "fog/test_helpers/mock_helper" require "fog/test_helpers/model_helper" require "fog/test_helpers/responds_to_helper" require "fog/test_helpers/types_helper" require "fog/test_helpers/succeeds_helper" require "fog/test_helpers/compute/flavors_helper" require "fog/test_helpers/compute/server_helper" require "fog/test_helpers/compute/servers_helper" fog-core-1.45.0/lib/fog/test_helpers/000077500000000000000000000000001314013014100173105ustar00rootroot00000000000000fog-core-1.45.0/lib/fog/test_helpers/collection_helper.rb000066400000000000000000000046371314013014100233410ustar00rootroot00000000000000def collection_tests(collection, params = {}, mocks_implemented = true) tests("success") do tests("#new(#{params.inspect})").succeeds do pending if Fog.mocking? && !mocks_implemented collection.new(params) end tests("#create(#{params.inspect})").succeeds do pending if Fog.mocking? && !mocks_implemented @instance = collection.create(params) end # FIXME: work around for timing issue on AWS describe_instances mocks if Fog.mocking? && @instance.respond_to?(:ready?) @instance.wait_for { ready? } end tests("#all").succeeds do pending if Fog.mocking? && !mocks_implemented collection.all end @identity = @instance.identity if !Fog.mocking? || mocks_implemented tests("#get(#{@identity})").succeeds do pending if Fog.mocking? && !mocks_implemented collection.get(@identity) end tests("Enumerable") do pending if Fog.mocking? && !mocks_implemented methods = %w(all? any? find detect collect map find_index flat_map collect_concat group_by none? one?) # JRuby 1.7.5+ issue causes a SystemStackError: stack level too deep # https://github.com/jruby/jruby/issues/1265 if RUBY_PLATFORM == "java" && JRUBY_VERSION =~ /1\.7\.[5-8]/ methods.delete("all?") end methods.each do |enum_method| next unless collection.respond_to?(enum_method) tests("##{enum_method}").succeeds do block_called = false collection.send(enum_method) { block_called = true } block_called end end %w(max_by min_by).each do |enum_method| next unless collection.respond_to?(enum_method) tests("##{enum_method}").succeeds do block_called = false collection.send(enum_method) do block_called = true 0 end block_called end end end yield if block_given? @instance.destroy if !Fog.mocking? || mocks_implemented end tests("failure") do if !Fog.mocking? || mocks_implemented @identity = @identity.to_s @identity = @identity.gsub(/[a-zA-Z]/) { Fog::Mock.random_letters(1) } @identity = @identity.gsub(/\d/) { Fog::Mock.random_numbers(1) } @identity end tests("#get('#{@identity}')").returns(nil) do pending if Fog.mocking? && !mocks_implemented collection.get(@identity) end end end fog-core-1.45.0/lib/fog/test_helpers/compute/000077500000000000000000000000001314013014100207645ustar00rootroot00000000000000fog-core-1.45.0/lib/fog/test_helpers/compute/flavors_helper.rb000066400000000000000000000014601314013014100243250ustar00rootroot00000000000000def flavors_tests(connection, _params = {}, mocks_implemented = true) tests("success") do tests("#all").succeeds do pending if Fog.mocking? && !mocks_implemented connection.flavors.all end if !Fog.mocking? || mocks_implemented @identity = connection.flavors.first.identity end tests("#get('#{@identity}')").succeeds do pending if Fog.mocking? && !mocks_implemented connection.flavors.get(@identity) end end tests("failure") do if !Fog.mocking? || mocks_implemented invalid_flavor_identity = connection.flavors.first.identity.to_s.gsub(/\w/, "0") end tests("#get('#{invalid_flavor_identity}')").returns(nil) do pending if Fog.mocking? && !mocks_implemented connection.flavors.get(invalid_flavor_identity) end end end fog-core-1.45.0/lib/fog/test_helpers/compute/server_helper.rb000066400000000000000000000012261314013014100241570ustar00rootroot00000000000000def server_tests(connection, params = {}, mocks_implemented = true) model_tests(connection.servers, params, mocks_implemented) do tests("#reload").returns(true) do pending if Fog.mocking? && !mocks_implemented @instance.wait_for { ready? } identity = @instance.identity !identity.nil? && identity == @instance.reload.identity end responds_to([:ready?, :state]) yield if block_given? tests("#reboot").succeeds do pending if Fog.mocking? && !mocks_implemented @instance.wait_for { ready? } @instance.reboot end @instance.wait_for { ready? } if !Fog.mocking? || mocks_implemented end end fog-core-1.45.0/lib/fog/test_helpers/compute/servers_helper.rb000066400000000000000000000004061314013014100243410ustar00rootroot00000000000000def servers_tests(connection, params = {}, mocks_implemented = true) collection_tests(connection.servers, params, mocks_implemented) do if !Fog.mocking? || mocks_implemented @instance.wait_for { ready? } yield if block_given? end end end fog-core-1.45.0/lib/fog/test_helpers/formats_helper.rb000066400000000000000000000050461314013014100226540ustar00rootroot00000000000000require "fog/schema/data_validator" module Shindo class Tests # Generates a Shindo test that compares a hash schema to the result # of the passed in block returning true if they match. # # The schema that is passed in is a Hash or Array of hashes that # have Classes in place of values. When checking the schema the # value should match the Class. # # Strict mode will fail if the data has additional keys. Setting # +strict+ to +false+ will allow additional keys to appear. # # @param [Hash] schema A Hash schema # @param [Hash] options Options to change validation rules # @option options [Boolean] :allow_extra_keys # If +true+ does not fail when keys are in the data that are # not specified in the schema. This allows new values to # appear in API output without breaking the check. # @option options [Boolean] :allow_optional_rules # If +true+ does not fail if extra keys are in the schema # that do not match the data. Not recommended! # @yield Data to check with schema # # @example Using in a test # Shindo.tests("comparing welcome data against schema") do # data = {:welcome => "Hello" } # data_matches_schema(:welcome => String) { data } # end # # comparing welcome data against schema # + data matches schema # # @example Example schema # { # "id" => String, # "ram" => Integer, # "disks" => [ # { # "size" => Float # } # ], # "dns_name" => Fog::Nullable::String, # "active" => Fog::Boolean, # "created" => DateTime # } # # @return [Boolean] def data_matches_schema(schema, options = {}) test("data matches schema") do validator = Fog::Schema::DataValidator.new valid = validator.validate(yield, schema, options) @message = validator.message unless valid valid end end # @deprecated #formats is deprecated. Use #data_matches_schema instead def formats(format, strict = true) test("has proper format") do if strict options = { :allow_extra_keys => false, :allow_optional_rules => true } else options = { :allow_extra_keys => true, :allow_optional_rules => true } end validator = Fog::Schema::DataValidator.new valid = validator.validate(yield, format, options) @message = validator.message unless valid valid end end end end fog-core-1.45.0/lib/fog/test_helpers/helper.rb000066400000000000000000000016121314013014100211140ustar00rootroot00000000000000require "excon" ENV["FOG_RC"] = ENV["FOG_RC"] || File.expand_path("../.fog", __FILE__) ENV["FOG_CREDENTIAL"] = ENV["FOG_CREDENTIAL"] || "default" Excon.defaults.merge!(:debug_request => true, :debug_response => true) LOREM = < "aws_access_key_id", :aws_secret_access_key => "aws_secret_access_key", :ia_access_key_id => "aws_access_key_id", :ia_secret_access_key => "aws_secret_access_key", :bluebox_api_key => "bluebox_api_key", :bluebox_customer_id => "bluebox_customer_id", :brightbox_client_id => "brightbox_client_id", :brightbox_secret => "brightbox_secret", :cloudstack_disk_offering_id => "", :cloudstack_host => "http://cloudstack.example.org", :cloudstack_network_ids => "", :cloudstack_service_offering_id => "4437ac6c-9fe3-477a-57ec-60a5a45896a4", :cloudstack_template_id => "8a31cf9c-f248-0588-256e-9dbf58785216", :cloudstack_zone_id => "c554c592-e09c-9df5-7688-4a32754a4305", :clodo_api_key => "clodo_api_key", :clodo_username => "clodo_username", :digitalocean_api_key => "digitalocean_api_key", :digitalocean_client_id => "digitalocean_client_id", :dnsimple_email => "dnsimple_email", :dnsimple_password => "dnsimple_password", :dnsmadeeasy_api_key => "dnsmadeeasy_api_key", :dnsmadeeasy_secret_key => "dnsmadeeasy_secret_key", :glesys_username => "glesys_username", :glesys_api_key => "glesys_api_key", :go_grid_api_key => "go_grid_api_key", :go_grid_shared_secret => "go_grid_shared_secret", :google_storage_access_key_id => "google_storage_access_key_id", :google_storage_secret_access_key => "google_storage_secret_access_key", :google_project => "google_project_name", :google_client_email => "fake@developer.gserviceaccount.com", :google_key_location => "~/fake.p12", :hp_access_key => "hp_access_key", :hp_secret_key => "hp_secret_key", :hp_tenant_id => "hp_tenant_id", :hp_avl_zone => "hp_avl_zone", :os_account_meta_temp_url_key => "os_account_meta_temp_url_key", :ibm_username => "ibm_username", :ibm_password => "ibm_password", :joyent_username => "joyentuser", :joyent_password => "joyentpass", :linode_api_key => "linode_api_key", :local_root => "~/.fog", :bare_metal_cloud_password => "bare_metal_cloud_password", :bare_metal_cloud_username => "bare_metal_cloud_username", :ninefold_compute_key => "ninefold_compute_key", :ninefold_compute_secret => "ninefold_compute_secret", :ninefold_storage_secret => "ninefold_storage_secret", :ninefold_storage_token => "ninefold_storage_token", # :public_key_path => "~/.ssh/id_rsa.pub", # :private_key_path => "~/.ssh/id_rsa", :openstack_api_key => "openstack_api_key", :openstack_username => "openstack_username", :openstack_tenant => "openstack_tenant", :openstack_auth_url => "http://openstack:35357/v2.0/tokens", :ovirt_url => "http://ovirt:8080/api", :ovirt_username => "admin@internal", :ovirt_password => "123123", :libvirt_uri => "qemu://libvirt/system", :rackspace_api_key => "rackspace_api_key", :rackspace_username => "rackspace_username", :riakcs_access_key_id => "riakcs_access_key_id", :riakcs_secret_access_key => "riakcs_secret_access_key", :vcloud_host => "vcloud_host", :vcloud_password => "vcloud_password", :vcloud_username => "vcloud_username", :vcloud_director_host => "vcloud-director-host", :vcloud_director_password => "vcloud_director_password", :vcloud_director_username => "vcd_user@vcd_org_name", :voxel_api_key => "voxel_api_key", :voxel_api_secret => "voxel_api_secret", :zerigo_email => "zerigo_email", :zerigo_token => "zerigo_token", :dynect_customer => "dynect_customer", :dynect_username => "dynect_username", :dynect_password => "dynect_password", :vsphere_server => "virtualcenter.lan", :vsphere_username => "apiuser", :vsphere_password => "apipassword", :vsphere_expected_pubkey_hash => "abcdef1234567890", :libvirt_uri => "qemu:///system", :libvirt_username => "root", :libvirt_password => "password", :cloudsigma_username => "csuname", :cloudsigma_password => "cspass" }.merge(Fog.credentials) end fog-core-1.45.0/lib/fog/test_helpers/model_helper.rb000066400000000000000000000013661314013014100223020ustar00rootroot00000000000000def model_tests(collection, params = {}, mocks_implemented = true) tests("success") do @instance = collection.new(params) tests("#save").succeeds do pending if Fog.mocking? && !mocks_implemented @instance.save end yield if block_given? tests("#destroy").succeeds do pending if Fog.mocking? && !mocks_implemented @instance.destroy end end end # Generates a unique identifier with a random differentiator. # Useful when rapidly re-running tests, so we don"t have to wait # serveral minutes for deleted objects to disappear from the API # E.g. "fog-test-1234" def uniq_id(base_name = "fog-test") # random_differentiator suffix = rand(65_536).to_s(16).rjust(4, "0") [base_name, suffix].join("-") end fog-core-1.45.0/lib/fog/test_helpers/responds_to_helper.rb000066400000000000000000000003741314013014100235370ustar00rootroot00000000000000module Shindo class Tests def responds_to(method_names) method_names.each do |method_name| tests("#respond_to?(:#{method_name})").returns(true) do @instance.respond_to?(method_name) end end end end end fog-core-1.45.0/lib/fog/test_helpers/succeeds_helper.rb000066400000000000000000000002061314013014100227700ustar00rootroot00000000000000module Shindo class Tests def succeeds test("succeeds") do !!instance_eval(&Proc.new) end end end end fog-core-1.45.0/lib/fog/test_helpers/types_helper.rb000066400000000000000000000020141314013014100223350ustar00rootroot00000000000000# Format related hackery # allows both true.is_a?(Fog::Boolean) and false.is_a?(Fog::Boolean) # allows both nil.is_a?(Fog::Nullable::String) and ''.is_a?(Fog::Nullable::String) module Fog module Boolean; end module Nullable module Boolean; end module Integer; end module String; end module Time; end module Float; end module Hash; end module Array; end end end [FalseClass, TrueClass].each {|klass| klass.send(:include, Fog::Boolean)} [FalseClass, TrueClass, NilClass, Fog::Boolean].each {|klass| klass.send(:include, Fog::Nullable::Boolean)} [NilClass, String].each {|klass| klass.send(:include, Fog::Nullable::String)} [NilClass, Time].each {|klass| klass.send(:include, Fog::Nullable::Time)} [Integer, NilClass].each {|klass| klass.send(:include, Fog::Nullable::Integer)} [Float, NilClass].each {|klass| klass.send(:include, Fog::Nullable::Float)} [Hash, NilClass].each {|klass| klass.send(:include, Fog::Nullable::Hash)} [Array, NilClass].each {|klass| klass.send(:include, Fog::Nullable::Array)} fog-core-1.45.0/lib/fog/volume.rb000066400000000000000000000001031314013014100164350ustar00rootroot00000000000000module Fog module Volume extend Fog::ServicesMixin end end fog-core-1.45.0/lib/fog/vpn.rb000066400000000000000000000006101314013014100157340ustar00rootroot00000000000000module Fog module VPN extend Fog::ServicesMixin def self.new(orig_attributes) attributes = orig_attributes.dup provider = attributes.delete(:provider).to_s.downcase.to_sym if provider == :stormondemand require "fog/vpn/storm_on_demand" Fog::VPN::StormOnDemand.new(attributes) else super(orig_attributes) end end end end fog-core-1.45.0/lib/tasks/000077500000000000000000000000001314013014100151615ustar00rootroot00000000000000fog-core-1.45.0/lib/tasks/test_task.rb000066400000000000000000000021541314013014100175110ustar00rootroot00000000000000require "rake" require "rake/tasklib" module Fog module Rake class TestTask < ::Rake::TaskLib def initialize desc "Run the mocked tests" task :test do ::Rake::Task[:mock_tests].invoke end task :mock_tests do tests(true) end task :real_tests do tests(false) end end def tests(mocked) Fog::Formatador.display_line start = Time.now.to_i threads = [] Thread.main[:results] = [] Fog.providers.each do |key, value| threads << Thread.new do Thread.main[:results] << { :provider => value, :success => sh("export FOG_MOCK=#{mocked} && bundle exec shindont +#{key}") } end end threads.each(&:join) Fog::Formatador.display_table(Thread.main[:results].sort { |x, y| x[:provider] <=> y[:provider] }) Fog::Formatador.display_line("[bold]FOG_MOCK=#{mocked}[/] tests completed in [bold]#{Time.now.to_i - start}[/] seconds") Fog::Formatador.display_line end end end end fog-core-1.45.0/spec/000077500000000000000000000000001314013014100142205ustar00rootroot00000000000000fog-core-1.45.0/spec/compute/000077500000000000000000000000001314013014100156745ustar00rootroot00000000000000fog-core-1.45.0/spec/compute/models/000077500000000000000000000000001314013014100171575ustar00rootroot00000000000000fog-core-1.45.0/spec/compute/models/server_spec.rb000066400000000000000000000167741314013014100220430ustar00rootroot00000000000000require "spec_helper" require "fog/compute/models/server" describe Fog::Compute::Server do before do @server = Fog::Compute::Server.new end describe "#sshable?" do describe "when the server is not ready" do it "is false" do @server.stub(:ready?, false) do refute @server.sshable? end end end describe "when the server is ready" do describe "when the ssh_ip_address is nil" do it "is false" do @server.stub(:ready?, true) do @server.stub(:ssh_ip_address, nil) do refute @server.sshable? end end end end describe "when the ssh_ip_address exists" do # Define these constants which would be imported by net-ssh once loaded module Net module SSH class AuthenticationFailed < RuntimeError end class Disconnect < RuntimeError end end end describe "and ssh times out" do it "is false" do @server.stub(:ready?, true) do @server.stub(:ssh_ip_address, "10.0.0.1") do raises_timeout = lambda { |_time| raise Timeout::Error.new } Timeout.stub(:timeout, raises_timeout) do refute @server.sshable? end end end end end describe "and it raises Net::SSH::AuthenticationFailed" do it "is false" do @server.stub(:ready?, true) do @server.stub(:ssh_ip_address, "10.0.0.1") do raise_error = lambda { |_cmd, _options| raise Net::SSH::AuthenticationFailed.new } @server.stub(:ssh, raise_error) do refute @server.sshable? end end end end it "resets SSH timeout" do @server.instance_variable_set(:@sshable_timeout, 8) @server.stub(:ready?, true) do @server.stub(:ssh_ip_address, "10.0.0.1") do raise_error = lambda { |_cmd, _options| raise Net::SSH::AuthenticationFailed.new } @server.stub(:ssh, raise_error) do @server.sshable? assert_nil @server.instance_variable_get(:@sshable_timeout), nil end end end end end describe "and it raises Net::SSH::Disconnect" do it "is false" do @server.stub(:ready?, true) do @server.stub(:ssh_ip_address, "10.0.0.1") do raise_error = lambda { |_cmd, _options| raise Net::SSH::Disconnect.new } @server.stub(:ssh, raise_error) do refute @server.sshable? end end end end it "resets SSH timeout" do @server.instance_variable_set(:@sshable_timeout, 8) @server.stub(:ready?, true) do @server.stub(:ssh_ip_address, "10.0.0.1") do raise_error = lambda { |_cmd, _options| raise Net::SSH::Disconnect.new } @server.stub(:ssh, raise_error) do @server.sshable? assert_nil @server.instance_variable_get(:@sshable_timeout), nil end end end end end describe "and it raises SystemCallError" do it "is false" do @server.stub(:ready?, true) do @server.stub(:ssh_ip_address, "10.0.0.1") do raise_error = lambda { |_cmd, _options| raise SystemCallError.new("message, 0") } @server.stub(:ssh, raise_error) do refute @server.sshable? end end end end it "does not increase SSH timeout" do @server.stub(:ready?, true) do @server.stub(:ssh_ip_address, "10.0.0.1") do raise_error = lambda { |_cmd, _options| raise SystemCallError.new("message, 0") } @server.stub(:ssh, raise_error) do @server.sshable? assert_equal @server.instance_variable_get(:@sshable_timeout), 8 end end end end end describe "and ssh completes within the designated timeout" do it "is true" do @server.stub(:ready?, true) do @server.stub(:ssh_ip_address, "10.0.0.1") do @server.stub(:ssh, "datum") do assert @server.sshable? end end end end end describe "when called successively" do describe "and ssh times out" do it "increases the timeout factor by 1.5" do @server.stub(:ready?, true) do @server.stub(:ssh_ip_address, "10.0.0.1") do raises_timeout = lambda do |time| assert(time == 8) raise Timeout::Error.new end Timeout.stub(:timeout, raises_timeout) do refute @server.sshable? end raises_timeout = lambda do |time| assert_equal(12, time) raise Timeout::Error.new end Timeout.stub(:timeout, raises_timeout) do refute @server.sshable? end end end end it "does not increase timeout beyond 60s" do @server.stub(:ready?, true) do @server.stub(:ssh_ip_address, "10.0.0.1") do raises_timeout = lambda { |_time| raise Timeout::Error.new } Timeout.stub(:timeout, raises_timeout) do 5.times { refute @server.sshable? } end raises_timeout = lambda do |time| assert_equal(60, time) raise Timeout::Error.new end Timeout.stub(:timeout, raises_timeout) do refute @server.sshable? end raises_timeout = lambda do |time| assert_equal(60, time) raise Timeout::Error.new end Timeout.stub(:timeout, raises_timeout) do refute @server.sshable? end end end end describe "when ssh eventually succeeds" do it "resets the timeout to the initial value" do @server.stub(:ready?, true) do @server.stub(:ssh_ip_address, "10.0.0.1") do raises_timeout = lambda do |time| assert(time == 8) raise Timeout::Error.new end Timeout.stub(:timeout, raises_timeout) do refute @server.sshable? end @server.stub(:ssh, "datum") do assert @server.sshable? end raises_timeout = lambda do |time| assert_equal(8, time) raise Timeout::Error.new end Timeout.stub(:timeout, raises_timeout) do refute @server.sshable? end end end end end end end end end end end fog-core-1.45.0/spec/compute_spec.rb000066400000000000000000000043111314013014100172320ustar00rootroot00000000000000require "spec_helper" module Fog module Compute def self.require(*_args); end end end describe "Fog::Compute" do describe "#new" do module Fog module TheRightWay extend Provider service(:compute, "Compute") end end module Fog module Compute class TheRightWay def initialize(_args); end end end end it "instantiates an instance of Fog::Compute:: from the :provider keyword arg" do compute = Fog::Compute.new(:provider => :therightway) assert_instance_of(Fog::Compute::TheRightWay, compute) end module Fog module TheWrongWay extend Provider service(:compute, "Compute") end end module Fog module TheWrongWay class Compute def initialize(_args); end end end end it "instantiates an instance of Fog::::Compute from the :provider keyword arg" do compute = Fog::Compute.new(:provider => :thewrongway) assert_instance_of(Fog::TheWrongWay::Compute, compute) end module Fog module BothWays extend Provider service(:compute, "Compute") end end module Fog module BothWays class Compute def initialize(_args); end end end end module Fog module Compute class BothWays attr_reader :args def initialize(args) @args = args end end end end describe "when both Fog::Compute:: and Fog::::Compute exist" do it "prefers Fog::Compute::" do compute = Fog::Compute.new(:provider => :bothways) assert_instance_of(Fog::Compute::BothWays, compute) end end it "passes the supplied keyword args less :provider to Fog::Compute::#new" do compute = Fog::Compute.new(:provider => :bothways, :extra => :stuff) assert_equal({ :extra => :stuff }, compute.args) end it "raises ArgumentError when given a :provider where a Fog::Compute::Provider that does not exist" do assert_raises(ArgumentError) do Fog::Compute.new(:provider => :wat) end end end end fog-core-1.45.0/spec/connection_spec.rb000066400000000000000000000062231314013014100177210ustar00rootroot00000000000000require "spec_helper" describe Fog::Core::Connection do it "raises ArgumentError when no arguments given" do assert_raises(ArgumentError) do Fog::Core::Connection.new end end [:request, :reset].each do |method| it "responds to #{method}" do connection = Fog::Core::Connection.new("http://example.com") assert connection.respond_to?(method) end end it "adds custom user-agents to Fog requests" do Fog::VERSION = 'Version' Fog::Core::Connection.add_user_agent("my-app/1.2") connection = Fog::Core::Connection.new("http://example.com") assert_equal "my-app/1.2 fog/Version fog-core/#{Fog::Core::VERSION}", connection.instance_variable_get(:@excon).data[:headers]["User-Agent"] Fog.send(:remove_const, :VERSION) end it "doesn't error when persistence is enabled" do Fog::Core::Connection.new("http://example.com", true) end it "doesn't error when persistence is enabled and debug_response is disabled" do options = { :debug_response => false } Fog::Core::Connection.new("http://example.com", true, options) end describe ":path_prefix" do it "does not emit a warning when provided this argument in the initializer" do $stderr = StringIO.new Fog::Core::Connection.new("http://example.com", false, :path_prefix => "foo") assert_empty($stderr.string) end it "raises when the 'path' arg is present and this arg is supplied" do assert_raises(ArgumentError) do Fog::Core::Connection.new("http://example.com", false, :path_prefix => "foo", :path => "bar") end end end describe "#request" do describe "default behavior" do it "supplies the 'path' arg directly to Excon" do spy = Object.new spy.instance_eval do def params @params end def new(_, params) @params = params end end Object.stub_const("Excon", spy) do Fog::Core::Connection.new("http://example.com", false, :path => "bar") assert_equal("bar", spy.params[:path]) end end end describe "with path_prefix supplied to the initializer" do let(:spy) do Object.new.tap do |spy| spy.instance_eval do def new(*_args); self; end def params; @params; end def request(params) @params = params end end end end it "uses the initializer-supplied :path_prefix arg with #request :arg to formulate a path to send to Excon.request" do Object.stub_const("Excon", spy) do c = Fog::Core::Connection.new("http://example.com", false, :path_prefix => "foo") c.request(:path => "bar") assert_equal("foo/bar", spy.params[:path]) end end it "does not introduce consecutive '/'s into the path if 'path' starts with a '/'" do Object.stub_const("Excon", spy) do c = Fog::Core::Connection.new("http://example.com", false, :path_prefix => "foo") c.request(:path => "/bar") assert_equal("foo/bar", spy.params[:path]) end end end end end fog-core-1.45.0/spec/core/000077500000000000000000000000001314013014100151505ustar00rootroot00000000000000fog-core-1.45.0/spec/core/cache_spec.rb000066400000000000000000000133311314013014100175530ustar00rootroot00000000000000require "spec_helper" require "securerandom" require "tmpdir" module Fog class SubFogTestModel < Fog::Model identity :id end end module Fog class SubFogTestService < Fog::Service class Mock attr_reader :options def initialize(opts = {}) @options = opts end end end end describe Fog::Cache do before(:each) do Fog.mock! @service = Fog::SubFogTestService.new Fog::Cache.namespace_prefix = "test-dir" end it "has a namespace_prefix configurable" do Fog::Cache.namespace_prefix = "for-service-user-region-foo" # Expand path does not downcase. case insensitive platform tests. example_cache = File.expand_path(Fog::Cache.namespace(Fog::SubFogTestModel, @service)).downcase expected_namespace = File.expand_path("~/.fog-cache/for-service-user-region-foo").downcase assert_equal example_cache.include?(expected_namespace), true end it "has metadata associated to the namespace that you can save to" do Fog::Cache.clean! Fog::Cache.namespace_prefix = "for-service-user-region-foo" # nothing exists, nothing comes back assert_equal Fog::Cache.metadata, {} # write/read Fog::Cache.write_metadata({:last_dumped => "Tuesday, November 8, 2016"}) assert_equal Fog::Cache.metadata[:last_dumped], "Tuesday, November 8, 2016" # diff namespace, diff metadata Fog::Cache.namespace_prefix = "different-namespace" assert_nil Fog::Cache.metadata[:last_dumped] # still accessible per namespace Fog::Cache.namespace_prefix = "for-service-user-region-foo" assert_equal Fog::Cache.metadata[:last_dumped], "Tuesday, November 8, 2016" # can overwrite Fog::Cache.write_metadata({:last_dumped => "Diff date"}) assert_equal Fog::Cache.metadata[:last_dumped], "Diff date" # can't write a non-hash/data entry. assert_raises Fog::Cache::CacheDir do Fog::Cache.write_metadata("boo") end # namespace must be set as well. assert_raises Fog::Cache::CacheDir do Fog::Cache.namespace_prefix = nil Fog::Cache.write_metadata({:a => "b"}) end end it "can load cache data from disk" do path = File.expand_path("~/.fog-cache-test-#{Time.now.to_i}.yml") data = "--- ok\n...\n" File.open(path, "w") { |f| f.write(data) } assert_equal "ok", Fog::Cache.load_cache(path) end it "load bad cache data - empty file, from disk" do path = File.expand_path("~/.fog-cache-test-2-#{Time.now.to_i}.yml") data = "" File.open(path, "w") { |f| f.write(data) } assert_equal false, Fog::Cache.load_cache(path) end it "must have a namespace_prefix configurable" do Fog::Cache.namespace_prefix = nil assert_raises Fog::Cache::CacheDir do Fog::Cache.load(Fog::SubFogTestModel, @service) end end it "can create a namespace" do Fog::Cache.expire_cache!(Fog::SubFogTestModel, @service) assert_equal File.exist?(Fog::Cache.namespace(Fog::SubFogTestModel, @service)), false Fog::Cache.create_namespace(Fog::SubFogTestModel, @service) assert_equal File.exist?(Fog::Cache.namespace(Fog::SubFogTestModel, @service)), true end it "will raise if no cache data found" do Fog::Cache.expire_cache!(Fog::SubFogTestModel, @service) assert_raises Fog::Cache::CacheNotFound do Fog::Cache.load(Fog::SubFogTestModel, @service) end end it "Fog cache ignores bad cache data - empty file, from disk" do Fog::Cache.expire_cache!(Fog::SubFogTestModel, @service) id = SecureRandom.hex a = Fog::SubFogTestModel.new(:id => id, :service => @service) a.cache.dump # input bad data path_dir = File.expand_path(Fog::Cache.namespace(Fog::SubFogTestModel, @service)) path = File.join(path_dir, "foo.yml") data = "" File.open(path, "w") { |f| f.write(data) } assert_equal 1, Fog::Cache.load(Fog::SubFogTestModel, @service).size end it "can be dumped and reloaded back in" do Fog::Cache.expire_cache!(Fog::SubFogTestModel, @service) id = SecureRandom.hex a = Fog::SubFogTestModel.new(:id => id, :service => @service) assert_equal File.exist?(Fog::Cache.namespace(Fog::SubFogTestModel, @service)), false a.cache.dump assert_equal File.exist?(Fog::Cache.namespace(Fog::SubFogTestModel, @service)), true instances = Fog::Cache.load(Fog::SubFogTestModel, @service) assert_equal instances.first.id, a.id assert_equal instances.first.class, a.class end it "dumping two models that have a duplicate identity" do Fog::Cache.expire_cache!(Fog::SubFogTestModel, @service) id = SecureRandom.hex # security groups on aws for eg can have the same identity group name 'default'. # there are no restrictions on `identity` fog attributes to be uniq. a = Fog::SubFogTestModel.new(:id => id, :service => @service, :bar => 'bar') b = Fog::SubFogTestModel.new(:id => id, :service => @service, :foo => 'foo') a.cache.dump b.cache.dump instances = Fog::Cache.load(Fog::SubFogTestModel, @service) assert_equal instances.size, 2 end it "dumping two models that have a duplicate identity twice" do Fog::Cache.expire_cache!(Fog::SubFogTestModel, @service) id = SecureRandom.hex # security groups on aws for eg can have the same identity group name 'default'. # there are no restrictions on `identity` fog attributes to be uniq. a = Fog::SubFogTestModel.new(:id => id, :service => @service, :bar => 'bar') b = Fog::SubFogTestModel.new(:id => id, :service => @service, :foo => 'foo') a.cache.dump b.cache.dump # and then again, w/out expiring cache a.cache.dump b.cache.dump instances = Fog::Cache.load(Fog::SubFogTestModel, @service) # unique-ify based on the attributes... assert_equal instances.size, 2 end end fog-core-1.45.0/spec/core/model_spec.rb000066400000000000000000000016371314013014100176160ustar00rootroot00000000000000require "spec_helper" require "securerandom" class FogTestModel < Fog::Model identity :id end describe Fog::Model do describe "#==" do it "is not equal if one is not a Fog::Model" do a = FogTestModel.new b = 2 refute_equal a, b refute_equal b, a end it "is equal if it is the same object" do a = b = FogTestModel.new assert_equal a, b end it "is equal if it has the same non-nil identity and the same class" do id = SecureRandom.hex assert_equal FogTestModel.new(:id => id), FogTestModel.new(:id => id) end it "is not equal if both have nil identity, but are different objects" do refute_equal FogTestModel.new, FogTestModel.new end it "is not equal if it has a different identity" do refute_equal FogTestModel.new(:id => SecureRandom.hex), FogTestModel.new(:id => SecureRandom.hex) end end end fog-core-1.45.0/spec/core/stringify_keys_spec.rb000066400000000000000000000017441314013014100215660ustar00rootroot00000000000000require "spec_helper" describe "Fog::StringifyKeys" do describe ".stringify" do describe "when key is a Symbol" do it "replaces key with String" do input = { :key => "value" } output = Fog::StringifyKeys.stringify(input) assert(output.key?("key")) end end describe "when key is a String" do it "keeps key as String" do input = { "key" => "value" } output = Fog::StringifyKeys.stringify(input) assert(output.key?("key")) end end describe "when Hash is empty" do it "returns empty Hash" do assert_equal({}, Fog::StringifyKeys.stringify({})) end end describe "when keys are deeply nested" do it "updates only top level key" do input = { :key1 => { :key2 => { :key3 => nil }}} output = Fog::StringifyKeys.stringify(input) expected = { "key1" => { :key2 => { :key3 => nil }}} assert_equal(expected, output) end end end end fog-core-1.45.0/spec/core/whitelist_keys_spec.rb000066400000000000000000000016531314013014100215630ustar00rootroot00000000000000require "spec_helper" describe "Fog::WhitelistKeys" do describe ".whitelist" do describe "when other keys are present" do it "returns Hash with only allowed keys" do input = { :name => "name", :type => "type", :size => 80 } valid_keys = %w(name size) output = Fog::WhitelistKeys.whitelist(input, valid_keys) expected = { "name" => "name", "size" => 80 } assert_equal(expected, output) end end describe "when key is a Symbol" do it "returns a String" do input = { :name => "name" } valid_keys = %w(name) output = Fog::WhitelistKeys.whitelist(input, valid_keys) expected = { "name" => "name" } assert(expected, output) end end describe "when Hash is empty" do it "returns empty Hash" do output = Fog::WhitelistKeys.whitelist({}, []) assert_equal({}, output) end end end end fog-core-1.45.0/spec/credentials_spec.rb000066400000000000000000000046541314013014100200650ustar00rootroot00000000000000require "spec_helper" describe "credentials" do before do @old_home = ENV["HOME"] @old_rc = ENV["FOG_RC"] @old_credential = ENV["FOG_CREDENTIAL"] @old_credentials = Fog.credentials Fog.instance_variable_set("@credential_path", nil) # kill memoization Fog.instance_variable_set("@credential", nil) # kill memoization end after do ENV["HOME"] = @old_home ENV["FOG_RC"] = @old_rc ENV["FOG_CREDENTIAL"] = @old_credential Fog.credentials = @old_credentials end describe "credential" do it "returns :default for default credentials" do assert_equal :default, Fog.credential end it "returns the to_sym of the assigned value" do Fog.credential = "foo" assert_equal :foo, Fog.credential end it "can set credentials throught the FOG_CREDENTIAL env va" do ENV["FOG_CREDENTIAL"] = "bar" assert_equal :bar, Fog.credential end end describe "credentials_path" do it "has FOG_RC takes precedence over HOME" do ENV["HOME"] = "/home/path" ENV["FOG_RC"] = "/rc/path" assert_equal "/rc/path", Fog.credentials_path end it "properly expands paths" do ENV["FOG_RC"] = "/expanded/subdirectory/../path" assert_equal "/expanded/path", Fog.credentials_path end it "falls back to home path if FOG_RC not set" do ENV.delete("FOG_RC") assert_equal File.join(ENV["HOME"], ".fog"), Fog.credentials_path end it "ignores home path if it does not exist" do ENV.delete("FOG_RC") ENV["HOME"] = "/no/such/path" assert_nil Fog.credentials_path end it "File.expand_path raises because of non-absolute path" do ENV.delete("FOG_RC") ENV["HOME"] = "." if RUBY_PLATFORM == "java" Fog::Logger.warning("Stubbing out non-absolute path credentials test due to JRuby bug: https://github.com/jruby/jruby/issues/1163") else assert_nil Fog.credentials_path end end it "returns nil when neither FOG_RC or HOME are set" do ENV.delete("HOME") ENV.delete("FOG_RC") assert_nil Fog.credentials_path end end describe "symbolize_credential?" do it "returns false if the value is :headers" do refute Fog.symbolize_credential?(:headers) end it "returns true if the value is not :headers" do assert Fog.symbolize_credential?(:foo) assert Fog.symbolize_credential?(:liberate_me_ex_inheris) end end end fog-core-1.45.0/spec/current_machine_spec.rb000066400000000000000000000015311314013014100207250ustar00rootroot00000000000000require "spec_helper" describe Fog::CurrentMachine do before do @was_mocking = Fog.mock? Fog.mock! @old_excon_defaults_mock = Excon.defaults[:mock] Excon.defaults[:mock] = true end after do Fog.unmock! unless @was_mocking Fog::CurrentMachine.ip_address = nil Excon.stubs.clear Excon.defaults[:mock] = @old_excon_defaults_mock end describe "ip_address" do it "should be thread safe" do (1..10).map do Thread.new do Excon.stub({ :method => :get, :path => "/" }, { :body => "" }) Fog::CurrentMachine.ip_address end end.each(&:join) end it "should remove trailing endline characters" do Excon.stub({ :method => :get, :path => "/" }, { :body => "192.168.0.1\n" }) assert_equal "192.168.0.1", Fog::CurrentMachine.ip_address end end end fog-core-1.45.0/spec/fake_app/000077500000000000000000000000001314013014100157665ustar00rootroot00000000000000fog-core-1.45.0/spec/fake_app/fake_service.rb000066400000000000000000000005101314013014100207350ustar00rootroot00000000000000class FakeService < Fog::Service class Real def initialize(_options) end end class Mock def initialize(_options) end end model_path File.join(File.dirname(__FILE__), "models") model :model collection :collection request_path File.join(File.dirname(__FILE__), "requests") request :request endfog-core-1.45.0/spec/fake_app/models/000077500000000000000000000000001314013014100172515ustar00rootroot00000000000000fog-core-1.45.0/spec/fake_app/models/collection.rb000066400000000000000000000001501314013014100217250ustar00rootroot00000000000000require File.join(File.dirname(__FILE__), "model") class Collection < Fog::Collection model Model endfog-core-1.45.0/spec/fake_app/models/model.rb000066400000000000000000000000341314013014100206730ustar00rootroot00000000000000class Model < Fog::Model endfog-core-1.45.0/spec/fake_app/requests/000077500000000000000000000000001314013014100176415ustar00rootroot00000000000000fog-core-1.45.0/spec/fake_app/requests/request.rb000066400000000000000000000001731314013014100216570ustar00rootroot00000000000000class FakeService < Fog::Service class Real def request end end class Mock def request end end endfog-core-1.45.0/spec/fog_attribute_spec.rb000066400000000000000000000501561314013014100204240ustar00rootroot00000000000000require "spec_helper" class Service def single_associations FogSingleAssociationCollection.new end def multiple_associations FogMultipleAssociationsCollection.new end end class FogSingleAssociationModel < Fog::Model identity :id attribute :name, :type => :string end class FogMultipleAssociationsModel < Fog::Model identity :id attribute :name, :type => :string end class FogSingleAssociationCollection def get(id) FogSingleAssociationModel.new(:id => id) end end class FogMultipleAssociationsCollection < Fog::Association model FogMultipleAssociationsModel def get(id) FogMultipleAssociationsModel.new(:id => id) end end class FogAttributeTestModel < Fog::Model identity :id attribute :key, :aliases => "keys", :squash => "id" attribute :time, :type => :time attribute :bool, :type => :boolean attribute :float, :type => :float attribute :integer, :type => :integer attribute :string, :type => :string attribute :timestamp, :type => :timestamp attribute :array, :type => :array attribute :default, :default => "default_value", :aliases => :some_name attribute :another_default, :default => false attribute :good_name, :as => :Badname has_one :one_object, :single_associations, :aliases => :single has_many :many_objects, :multiple_associations has_many :objects, :multiple_associations, :association_class => FogMultipleAssociationsCollection has_one_identity :one_identity, :single_associations, :as => :Crazyname has_many_identities :many_identities, :multiple_associations, :aliases => :multiple has_many_identities :identities, :multiple_associations, :association_class => FogMultipleAssociationsCollection def service Service.new end def requires_one_test requires_one :key, :time end def requires_test requires :string, :integer end end describe "Fog::Attributes" do let(:model) { FogAttributeTestModel.new } it "should not create alias for nil" do refute FogAttributeTestModel.aliases.keys.include?(nil) end describe "squash 'id'" do it "squashes if the key is a String" do model.merge_attributes("keys" => { :id => "value" }) assert_equal "value", model.key end it "squashes if the key is a Symbol" do model.merge_attributes("keys" => { "id" => "value" }) assert_equal "value", model.key end end describe ":type => time" do it "returns nil when provided nil" do model.merge_attributes(:time => nil) refute model.time end it "returns '' when provided ''" do model.merge_attributes(:time => "") assert_equal "", model.time end it "returns a Time object when passed a Time object" do now = Time.now model.merge_attributes(:time => now.to_s) assert_equal Time.parse(now.to_s), model.time end it "returns a Time object when passed a string that is monkeypatched" do now = Time.now string = now.to_s def string.to_time "<3 <3 <3" end model.merge_attributes(:time => string) assert_equal Time.parse(string), model.time end end describe ":type => :boolean" do it "returns the String 'true' as a boolean" do model.merge_attributes(:bool => "true") assert_equal true, model.bool end it "returns true as true" do model.merge_attributes(:bool => true) assert_equal true, model.bool end it "returns the String 'false' as a boolean" do model.merge_attributes(:bool => "false") assert_equal false, model.bool end it "returns false as false" do model.merge_attributes(:bool => false) assert_equal false, model.bool end it "returns a non-true/false value as nil" do model.merge_attributes(:bool => "foo") refute model.bool end end describe ":type => :float" do it "returns an integer as float" do model.merge_attributes(:float => 1) assert_in_delta 1.0, model.float end it "returns a string as float" do model.merge_attributes(:float => "1") assert_in_delta 1.0, model.float end end describe ":type => :integer" do it "returns a float as integer" do model.merge_attributes(:integer => 1.5) assert_in_delta 1, model.integer end it "returns a string as integer" do model.merge_attributes(:integer => "1") assert_in_delta 1, model.integer end end describe ":type => :string" do it "returns a float as string" do model.merge_attributes(:string => 1.5) assert_equal "1.5", model.string end it "returns a integer as string" do model.merge_attributes(:string => 1) assert_equal "1", model.string end end describe ":type => :timestamp" do it "returns a date as time" do model.merge_attributes(:timestamp => Date.new(2008, 10, 12)) assert_equal "2008-10-12 00:00", model.timestamp.strftime("%Y-%m-%d %M:%S") assert_instance_of Fog::Time, model.timestamp end it "returns a time as time" do model.merge_attributes(:timestamp => Time.mktime(2007, 11, 1, 15, 25)) assert_equal "2007-11-01 25:00", model.timestamp.strftime("%Y-%m-%d %M:%S") assert_instance_of Fog::Time, model.timestamp end it "returns a date_time as time" do model.merge_attributes(:timestamp => DateTime.new(2007, 11, 1, 15, 25, 0)) assert_equal "2007-11-01 25:00", model.timestamp.strftime("%Y-%m-%d %M:%S") assert_instance_of Fog::Time, model.timestamp end end describe ":type => :array" do it "returns an empty array when not initialized" do assert_equal [], model.array end it "returns an empty array as an empty array" do model.merge_attributes(:array => []) assert_equal [], model.array end it "returns nil as an empty array" do model.merge_attributes(:array => nil) assert_equal [], model.array end it "returns an array with nil as an array with nil" do model.merge_attributes(:array => [nil]) assert_equal [nil], model.array end it "returns a single element as array" do model.merge_attributes(:array => 1.5) assert_equal [1.5], model.array end it "returns an array as array" do model.merge_attributes(:array => [1, 2]) assert_equal [1, 2], model.array end end describe ":default => 'default_value'" do it "should return nil when default is not defined on a new object" do assert_nil model.bool end it "should return the value of the object when default is not defined" do model.merge_attributes(:bool => false) assert_equal model.bool, false end it "should return the default value on a new object with value equal nil" do assert_equal model.default, "default_value" end it "should return the value on a new object with value not equal nil" do model.default = "not default" assert_equal model.default, "not default" end it "should return false when default value is false on a new object" do assert_equal model.another_default, false end it "should return the value of the persisted object" do model.merge_attributes(:id => "some-crazy-id", :default => 23) assert_equal model.default, 23 end it "should return nil on a persisted object without a value" do model.merge_attributes(:id => "some-crazy-id") assert_nil model.default end it "should return nil when an attribute with default value is setted to nil" do model.default = nil assert_nil model.default end end describe ".has_one" do it "should create an instance_variable to save the association object" do assert_nil model.one_object end it "should create a getter to save the association model" do model.merge_attributes(:one_object => FogSingleAssociationModel.new(:id => "123")) assert_instance_of FogSingleAssociationModel, model.one_object assert_equal model.one_object.attributes, :id => "123" end it "should create a setter that accept an object as param" do model.one_object = FogSingleAssociationModel.new(:id => "123") assert_equal model.one_object.attributes, :id => "123" end it "should create an alias to single" do model.merge_attributes(:single => FogSingleAssociationModel.new(:id => "123")) assert_equal model.one_object.attributes, :id => "123" end end describe ".has_one_identity" do it "should create an instance_variable to save the association identity" do assert_nil model.one_identity end it "should create a getter to load the association model" do model.merge_attributes(:one_identity => "123") assert_instance_of FogSingleAssociationModel, model.one_identity assert_equal model.one_identity.attributes, :id => "123" end describe "should create a setter that accept" do it "an id as param" do model.one_identity = "123" assert_equal model.one_identity.attributes, :id => "123" end it "a model as param" do model.one_identity = FogSingleAssociationModel.new(:id => "123") assert_equal model.one_identity.attributes, :id => "123" end end end describe ".has_many" do it "should return an instance of Fog::Association" do model.many_objects = [FogMultipleAssociationsModel.new(:id => "456")] assert_instance_of Fog::Association, model.many_objects end it "should create an instance_variable to save the associated objects" do assert_equal model.many_objects, [] end it "should create a getter to save all associated models" do model.merge_attributes(:many_objects => [FogMultipleAssociationsModel.new(:id => "456")]) assert_instance_of Fog::Association, model.many_objects assert_equal model.many_objects.size, 1 assert_instance_of FogMultipleAssociationsModel, model.many_objects.first assert_equal model.many_objects.first.attributes, :id => "456" end it "should create a setter that accept an array of objects as param" do model.many_objects = [FogMultipleAssociationsModel.new(:id => "456")] assert_equal model.many_objects.first.attributes, :id => "456" end describe "with a custom collection class" do it "should return an instance of that collection class" do model.objects = [FogMultipleAssociationsModel.new(:id => "456")] assert_instance_of FogMultipleAssociationsCollection, model.objects end end end describe "#requires_one" do it "should require at least one attribute is supplied" do FogAttributeTestModel.new(:key => :key, :time => Time.now).requires_one_test FogAttributeTestModel.new(:time => Time.now).requires_one_test FogAttributeTestModel.new(:key => :key).requires_one_test FogAttributeTestModel.new(:key => :key, :integer => 1).requires_one_test assert_raises ArgumentError do FogAttributeTestModel.new(:integer => 1).requires_one_test end end end describe "#requires" do it "should require all attributes are supplied" do FogAttributeTestModel.new(:string => "string", :integer => 1).requires_test assert_raises ArgumentError do FogAttributeTestModel.new(:integer => 1).requires_test end assert_raises ArgumentError do FogAttributeTestModel.new(:string => "string").requires_test end assert_raises ArgumentError do FogAttributeTestModel.new(:key => :key).requires_test end end end describe ".has_many_identities" do it "should return an instance of Fog::Association" do model.many_identities = ["456"] assert_instance_of Fog::Association, model.many_identities end it "should create an instance_variable to save the associations identities" do assert_equal model.many_identities, [] end it "should create a getter to load all association models" do model.merge_attributes(:many_identities => ["456"]) assert_instance_of Fog::Association, model.many_identities assert_equal model.many_identities.size, 1 assert_instance_of FogMultipleAssociationsModel, model.many_identities.first assert_equal model.many_identities.first.attributes, :id => "456" end describe "should create a setter that accept an array of" do it "ids as param" do model.many_identities = ["456"] assert_equal model.many_identities.first.attributes, :id => "456" end it "models as param" do model.many_identities = [FogMultipleAssociationsModel.new(:id => "456")] assert_equal model.many_identities.first.attributes, :id => "456" end end it "should create an alias to multiple" do model.merge_attributes(:multiple => ["456"]) assert_equal model.many_identities.first.attributes, :id => "456" end describe "with a custom collection class" do it "should return an instance of that collection class" do model.identities = ["456"] assert_instance_of FogMultipleAssociationsCollection, model.identities end end end describe "#all_attributes" do describe "on a persisted object" do it "should return all attributes without default values" do model.merge_attributes(:id => 2, :float => 3.2, :integer => 55_555_555) assert model.persisted? assert_equal model.all_attributes, :id => 2, :key => nil, :time => nil, :bool => nil, :float => 3.2, :integer => 55_555_555, :string => nil, :timestamp => nil, :array => [], :default => nil, :another_default => nil, :Badname => nil end end describe "on a new object" do it "should return all attributes including default values for empty attributes" do model.merge_attributes(:float => 3.2, :integer => 55_555_555) refute model.persisted? assert_equal model.all_attributes, :id => nil, :key => nil, :time => nil, :bool => nil, :float => 3.2, :integer => 55_555_555, :string => nil, :timestamp => nil, :array => [], :default => "default_value", :another_default => false, :Badname => nil end end end describe "#all_associations" do describe "without any association" do it "should return all associations empty" do assert_equal model.all_associations, :one_object => nil, :many_objects => [], :Crazyname => nil, :many_identities => [], :objects => [], :identities => [] end end describe "with associations" do it "should return all association objects" do @one_object = FogMultipleAssociationsModel.new @many_objects = [@one_object] model.merge_attributes(:one_object => @one_object, :many_objects => @many_objects) model.merge_attributes(:one_identity => "XYZ", :many_identities => %w(ABC)) assert_equal model.all_associations, :one_object => @one_object, :many_objects => @many_objects, :Crazyname => "XYZ", :many_identities => %w(ABC), :objects => [], :identities => [] end end end describe "#all_associations_and_attributes" do describe "on a persisted object" do it "should return all association and attributes but no default values" do @one_object = FogMultipleAssociationsModel.new @many_objects = [@one_object] model.merge_attributes(:id => 2, :float => 3.2, :integer => 55_555_555) model.merge_attributes(:one_object => @one_object, :many_objects => @many_objects) model.merge_attributes(:one_identity => "XYZ", :many_identities => %w(ABC)) assert model.persisted? assert_equal model.all_associations_and_attributes, :id => 2, :key => nil, :time => nil, :bool => nil, :float => 3.2, :integer => 55_555_555, :string => nil, :timestamp => nil, :array => [], :default => nil, :another_default => nil, :Badname => nil, :one_object => @one_object, :many_objects => @many_objects, :objects => [], :identities => [], :Crazyname => "XYZ", :many_identities => %w(ABC) end end describe "on a non persisted object" do it "should return all association and attributes and the default value for blank attributes" do @one_object = FogMultipleAssociationsModel.new @many_objects = [@one_object] model.merge_attributes(:float => 3.2, :integer => 55_555_555) model.merge_attributes(:one_object => @one_object, :many_objects => @many_objects) model.merge_attributes(:one_identity => "XYZ", :many_identities => %w(ABC)) refute model.persisted? assert_equal model.all_associations_and_attributes, :id => nil, :key => nil, :time => nil, :bool => nil, :float => 3.2, :integer => 55_555_555, :string => nil, :timestamp => nil, :array => [], :default => "default_value", :another_default => false, :Badname => nil, :one_object => @one_object, :many_objects => @many_objects, :objects => [], :identities => [], :Crazyname => "XYZ", :many_identities => %w(ABC) end end end end fog-core-1.45.0/spec/formatador_spec.rb000066400000000000000000000064701314013014100177240ustar00rootroot00000000000000require "spec_helper" describe Fog::Formatador do describe "when object is Fog::Collection instance" do before do @member_class = Class.new(Fog::Model) do attribute :name def self.name "MemberGadget" end end @collection_class = Class.new(Fog::Collection) do model @member_class attribute :attr_one attribute :attr_two def self.name "InspectionGadget" end def all self end end @collection = @collection_class.new(:attr_one => "String", :attr_two => 5) @collection << @member_class.new(:name => "Member name") @expected = <<-EOS.gsub(/^ {6}/, "").chomp! ] > EOS end it "returns formatted representation" do Fog::Formatador.format(@collection).must_equal @expected end end describe "when object is Fog::Collection without attributes" do before do @collection_class = Class.new(Fog::Collection) do def all self end end @collection = @collection_class.new @expected = <<-EOS.gsub(/^ {6}/, "").chomp! < [ ] > EOS end it "returns formatted representation" do Fog::Formatador.format(@collection).must_equal @expected end end describe "when object has is Fog::Collection but ignoring nested objects" do before do @collection_class = Class.new(Fog::Collection) do attribute :name def all self end end @collection = @collection_class.new(:name => "Name") @collection << "this" end it "returns formatted representation" do @expected = <<-EOS.gsub(/^ {6}/, "").chomp! < name=\"Name\" > EOS opts = { :include_nested => false } Fog::Formatador.format(@collection, opts).must_equal @expected end end describe "when object is not enumerable" do before do @class = Class.new @subject = @class.new @expected = <<-EOS.gsub(/^ {6}/, "").chomp! < > EOS end it "returns formatted representation" do Fog::Formatador.format(@subject).must_equal @expected end end describe "when object responds to non-enumerable '#map'" do before do @member = Class.new(Fog::Model) do def self.name "IPAddress" end # This map action is unrelated to Enumerable (See GH-138) def map raise "Do not call me when inspecting!" end end @collection_class = Class.new(Fog::Collection) do model @member def self.name "IPAddressCollection" end def all self end end @collection = @collection_class.new @collection << @member.new @expected = <<-EOS.gsub(/^ {6}/, "").chomp! ] > EOS end it "returns formatted representation" do Fog::Formatador.format(@collection).must_equal @expected end end end fog-core-1.45.0/spec/identity_spec.rb000066400000000000000000000043441314013014100174150ustar00rootroot00000000000000require "spec_helper" module Fog module Identity def self.require(*_args); end end end describe "Fog::Identity" do describe "#new" do module Fog module TheRightWay extend Provider service(:identity, "Identity") end end module Fog module Identity class TheRightWay def initialize(_args); end end end end it "instantiates an instance of Fog::Identity:: from the :provider keyword arg" do identity = Fog::Identity.new(:provider => :therightway) assert_instance_of(Fog::Identity::TheRightWay, identity) end module Fog module Rackspace extend Provider service(:identity, "Identity") end end module Fog module Rackspace class Identity def initialize(_args); end end end end it "instantiates an instance of Fog::::Identity from the :provider keyword arg" do identity = Fog::Identity.new(:provider => :rackspace) assert_instance_of(Fog::Rackspace::Identity, identity) end module Fog module BothWays extend Provider service(:identity, "Identity") end end module Fog module BothWays class Identity def initialize(_args); end end end end module Fog module Identity class BothWays attr_reader :args def initialize(args) @args = args end end end end describe "when both Fog::Identity:: and Fog::::Identity exist" do it "prefers Fog::Identity::" do identity = Fog::Identity.new(:provider => :bothways) assert_instance_of(Fog::Identity::BothWays, identity) end end it "passes the supplied keyword args less :provider to Fog::Identity::#new" do identity = Fog::Identity.new(:provider => :bothways, :extra => :stuff) assert_equal({ :extra => :stuff }, identity.args) end it "raises ArgumentError when given a :provider where a Fog::Identity::Provider that does not exist" do assert_raises(ArgumentError) do Fog::Identity.new(:provider => :wat) end end end end fog-core-1.45.0/spec/mocking_spec.rb000066400000000000000000000035261314013014100172140ustar00rootroot00000000000000require "spec_helper" describe "Fog mocking" do before do @fog_was_mocked = Fog.mock? Fog.unmock! if @fog_was_mocked end after do Fog.mock! if @fog_was_mocked end describe "Fog.mock!" do it "Fog.mock! returns true" do assert_equal true, Fog.mock! end it "Fog.mock? without Fog.mock! returns false" do assert_equal false, Fog.mock? end it "Fog.mock? with Fog.mock!" do Fog.mock! assert_equal true, Fog.mock? end it "Fog.mocking? without Fog.mock!" do assert_equal false, Fog.mocking? end it "Fog.mocking? with Fog.mock!" do Fog.mock! assert_equal true, Fog.mocking? end end describe "Fog::Mock.delay" do it "Fog::Mock.delay defaults to 0" do assert_equal 1, Fog::Mock.delay end it "handles reassignment" do Fog::Mock.delay = 2 assert_equal 2, Fog::Mock.delay Fog::Mock.delay = 1 assert_equal 1, Fog::Mock.delay end it "raises when given an illegal delay" do assert_raises(ArgumentError) do Fog::Mock.delay = -1 end end end describe "Fog::Mock.random_ip" do it "defaults to ipv4" do assert IPAddr.new(Fog::Mock.random_ip).ipv4? end it "supports explicit request for v4" do assert IPAddr.new(Fog::Mock.random_ip(:version => :v4)).ipv4? end it "supports explicit request for v6" do assert IPAddr.new(Fog::Mock.random_ip(:version => :v6)).ipv6? end it "raises when supplied an illegal IP version" do assert_raises(ArgumentError) do IPAddr.new(Fog::Mock.random_ip(:version => :v5)).ipv4? end end end describe "Fog::Mock.not_implemented" do it "raises MockNotImplemented when called" do assert_raises(Fog::Errors::MockNotImplemented) do Fog::Mock.not_implemented end end end end fog-core-1.45.0/spec/service_spec.rb000066400000000000000000000142651314013014100172270ustar00rootroot00000000000000require "spec_helper" describe Fog::Service do class TestService < Fog::Service requires :generic_api_key recognizes :generic_user class Real attr_reader :options def initialize(opts = {}) @options = opts end end class Mock attr_reader :options def initialize(opts = {}) @options = opts end end end class ChildOfTestService < TestService class Real; def initialize(*_args); end; end class Mock; def initialize(*_args); end; end end it "properly passes headers" do user_agent_hash = { "User-Agent" => "Generic Fog Client" } params = { :generic_user => "bob", :generic_api_key => "1234", :connection_options => { :headers => user_agent_hash } } service = TestService.new(params) assert_equal user_agent_hash, service.options[:connection_options][:headers] end describe "when created with a Hash" do it "raises for required argument that are missing" do assert_raises(ArgumentError) { TestService.new({}) } end it "converts String keys to be Symbols" do service = TestService.new "generic_api_key" => "abc" assert_includes service.options.keys, :generic_api_key end it "removes keys with `nil` values" do service = TestService.new :generic_api_key => "abc", :generic_user => nil refute_includes service.options.keys, :generic_user end it "converts number String values with to_i" do service = TestService.new :generic_api_key => "3421" assert_equal 3421, service.options[:generic_api_key] end it "converts 'true' String values to TrueClass" do service = TestService.new :generic_api_key => "true" assert_equal true, service.options[:generic_api_key] end it "converts 'false' String values to FalseClass" do service = TestService.new :generic_api_key => "false" assert_equal false, service.options[:generic_api_key] end it "warns for unrecognised options" do bad_options = { :generic_api_key => "abc", :bad_option => "bad value" } logger = Minitest::Mock.new logger.expect :warning, nil, ["Unrecognized arguments: bad_option"] Fog.stub_const :Logger, logger do TestService.new(bad_options) end logger.verify end end describe "when creating and mocking is disabled" do it "returns the real service" do Fog.stub :mocking?, false do service = TestService.new(:generic_api_key => "abc") service.must_be_instance_of TestService::Real end end it "TestService::Real has TestService::Collections mixed into the mocked service" do Fog.stub :mocking?, false do service = TestService.new(:generic_api_key => "abc") assert_includes(service.class.ancestors, TestService::Collections) assert_includes(service.class.ancestors, Fog::Service::Collections) refute_includes(service.class.ancestors, ChildOfTestService::Collections) end end it "ChildOfTestService::Real has ChildOfTestService::Collections and TestService::Collections mixed in" do Fog.stub :mocking?, true do service = ChildOfTestService.new assert_includes(service.class.ancestors, Fog::Service::Collections) assert_includes(service.class.ancestors, TestService::Collections) assert_includes(service.class.ancestors, ChildOfTestService::Collections) end end end describe "when creating and mocking is enabled" do it "returns mocked service" do Fog.stub :mocking?, true do service = TestService.new(:generic_api_key => "abc") service.must_be_instance_of TestService::Mock end end it "TestService::Mock has TestService::Collections mixed into the mocked service" do Fog.stub :mocking?, true do service = TestService.new(:generic_api_key => "abc") assert_includes(service.class.ancestors, Fog::Service::Collections) assert_includes(service.class.ancestors, TestService::Collections) refute_includes(service.class.ancestors, ChildOfTestService::Collections) end end it "ChildOfTestService::Mock has ChildOfTestService::Collections and TestService::Collections mixed in" do Fog.stub :mocking?, true do service = ChildOfTestService.new assert_includes(service.class.ancestors, Fog::Service::Collections) assert_includes(service.class.ancestors, TestService::Collections) assert_includes(service.class.ancestors, ChildOfTestService::Collections) end end end describe "when no credentials are provided" do it "uses the global values" do @global_credentials = { :generic_user => "fog", :generic_api_key => "fog" } Fog.stub :credentials, @global_credentials do @service = TestService.new assert_equal @service.options, @global_credentials end end end describe "when credentials are provided as settings" do it "merges the global values into settings" do @settings = { :generic_user => "fog" } @global_credentials = { :generic_user => "bob", :generic_api_key => "fog" } Fog.stub :credentials, @global_credentials do @service = TestService.new(@settings) assert_equal @service.options[:generic_user], "fog" assert_equal @service.options[:generic_api_key], "fog" end end end describe "when config object can configure the service itself" do it "ignores the global and its values" do @config = MiniTest::Mock.new def @config.config_service?; true; end def @config.nil?; false; end def @config.==(other); object_id == other.object_id; end unexpected_usage = lambda { raise "Accessing global!" } Fog.stub :credentials, unexpected_usage do @service = TestService.new(@config) assert_equal @config, @service.options end end end describe "#setup_requirements" do before :each do @service = FakeService.new end it "should require collections" do assert @service.respond_to?(:collection) end it "should mock" do assert_includes @service.mocked_requests, :request end end end fog-core-1.45.0/spec/spec_helper.rb000066400000000000000000000005371314013014100170430ustar00rootroot00000000000000if ENV["COVERAGE"] require "coveralls" require "simplecov" SimpleCov.start do add_filter "/spec/" end end require "minitest/autorun" require "minitest/spec" require "minitest/stub_const" $LOAD_PATH.unshift "lib" require "fog/core" Dir["spec/fake_app/**/*.rb"].each do |file| require File.join(File.dirname(__FILE__), "..", file) end fog-core-1.45.0/spec/storage_spec.rb000066400000000000000000000053731314013014100172330ustar00rootroot00000000000000require "spec_helper" module Fog module Storage def self.require(*_args); end end end describe "Fog::Storage" do describe "#new" do module Fog module TheRightWay extend Provider service(:storage, "Storage") end end module Fog module Storage class TheRightWay def initialize(_args); end end end end it "instantiates an instance of Fog::Storage:: from the :provider keyword arg" do compute = Fog::Storage.new(:provider => :therightway) assert_instance_of(Fog::Storage::TheRightWay, compute) end module Fog module TheWrongWay extend Provider service(:storage, "Storage") end end module Fog module TheWrongWay class Storage def initialize(_args); end end end end it "instantiates an instance of Fog::::Storage from the :provider keyword arg" do compute = Fog::Storage.new(:provider => :thewrongway) assert_instance_of(Fog::TheWrongWay::Storage, compute) end module Fog module BothWays extend Provider service(:storage, "Storage") end end module Fog module BothWays class Storage def initialize(_args); end end end end module Fog module Storage class BothWays attr_reader :args def initialize(args) @args = args end end end end describe "when both Fog::Storage:: and Fog::::Storage exist" do it "prefers Fog::Storage::" do compute = Fog::Storage.new(:provider => :bothways) assert_instance_of(Fog::Storage::BothWays, compute) end end it "passes the supplied keyword args less :provider to Fog::Storage::#new" do compute = Fog::Storage.new(:provider => :bothways, :extra => :stuff) assert_equal({ :extra => :stuff }, compute.args) end it "raises ArgumentError when given a :provider where a Fog::Storage::Provider that does not exist" do assert_raises(ArgumentError) do Fog::Storage.new(:provider => :wat) end end end describe ".get_body_size" do it "doesn't alter the encoding of the string passed to it" do # Ruby 1.8 doesn't support string encodings, so we can't test that if RUBY_VERSION >= "1.9.3" body = "foo".encode("UTF-8") Fog::Storage.get_body_size(body) assert_equal("UTF-8", body.encoding.to_s) else skip end end it "respects frozen strings" do if RUBY_VERSION >= "2.3.0" body = "foo".freeze Fog::Storage.get_body_size(body) else skip end end end endfog-core-1.45.0/spec/test_helpers/000077500000000000000000000000001314013014100167215ustar00rootroot00000000000000fog-core-1.45.0/spec/test_helpers/formats_helper_spec.rb000066400000000000000000000100141314013014100232660ustar00rootroot00000000000000require "spec_helper" require "fog/test_helpers/formats_helper" require "fog/test_helpers/types_helper" module Shindo class Tests def test(_str, &_block) yield end end end describe "formats_helper" do let(:shindo) { Shindo::Tests.new } it "comparing welcome data against schema" do data = { :welcome => "Hello" } assert shindo.data_matches_schema(:welcome => String) { data } end describe "#data_matches_schema" do it "when value matches schema expectation" do assert shindo.data_matches_schema("key" => String) { { "key" => "Value" } } end it "when values within an array all match schema expectation" do assert shindo.data_matches_schema("key" => [Integer]) { { "key" => [1, 2] } } end it "when nested values match schema expectation" do assert shindo.data_matches_schema("key" => { :nested_key => String }) { { "key" => { :nested_key => "Value" } } } end it "when collection of values all match schema expectation" do assert shindo.data_matches_schema([{ "key" => String }]) { [{ "key" => "Value" }, { "key" => "Value" }] } end it "when collection is empty although schema covers optional members" do assert shindo.data_matches_schema([{ "key" => String }], :allow_optional_rules => true) { [] } end it "when additional keys are passed and not strict" do assert shindo.data_matches_schema({ "key" => String }, { :allow_extra_keys => true }) { { "key" => "Value", :extra => "Bonus" } } end it "when value is nil and schema expects NilClass" do assert shindo.data_matches_schema("key" => NilClass) { { "key" => nil } } end it "when value and schema match as hashes" do assert shindo.data_matches_schema({}) { {} } end it "when value and schema match as arrays" do assert shindo.data_matches_schema([]) { [] } end it "when value is a Time" do assert shindo.data_matches_schema("time" => Time) { { "time" => Time.now } } end it "when key is missing but value should be NilClass (#1477)" do assert shindo.data_matches_schema({ "key" => NilClass }, { :allow_optional_rules => true }) { {} } end it "when key is missing but value is nullable (#1477)" do assert shindo.data_matches_schema({ "key" => Fog::Nullable::String }, { :allow_optional_rules => true }) { {} } end end describe "#formats backwards compatible changes" do it "when value matches schema expectation" do assert shindo.formats("key" => String) { { "key" => "Value" } } end it "when values within an array all match schema expectation" do assert shindo.formats("key" => [Integer]) { { "key" => [1, 2] } } end it "when nested values match schema expectation" do assert shindo.formats("key" => { :nested_key => String }) { { "key" => { :nested_key => "Value" } } } end it "when collection of values all match schema expectation" do assert shindo.formats([{ "key" => String }]) { [{ "key" => "Value" }, { "key" => "Value" }] } end it "when collection is empty although schema covers optional members" do assert shindo.formats([{ "key" => String }]) { [] } end it "when additional keys are passed and not strict" do assert shindo.formats({ "key" => String }, false) { { "key" => "Value", :extra => "Bonus" } } end it "when value is nil and schema expects NilClass" do assert shindo.formats("key" => NilClass) { { "key" => nil } } end it "when value and schema match as hashes" do assert shindo.formats({}) { {} } end it "when value and schema match as arrays" do assert shindo.formats([]) { [] } end it "when value is a Time" do assert shindo.formats("time" => Time) { { "time" => Time.now } } end it "when key is missing but value should be NilClass (#1477)" do assert shindo.formats("key" => NilClass) { {} } end it "when key is missing but value is nullable (#1477)" do assert shindo.formats("key" => Fog::Nullable::String) { {} } end end end fog-core-1.45.0/spec/test_helpers/schema_validator_spec.rb000066400000000000000000000074431314013014100235750ustar00rootroot00000000000000require "spec_helper" require "fog/test_helpers/formats_helper" require "fog/schema/data_validator" describe "SchemaValidator" do let(:validator) { Fog::Schema::DataValidator.new } describe "#validate" do it "returns true when value matches schema expectation" do assert validator.validate({ "key" => "Value" }, { "key" => String }) end it "returns true when values within an array all match schema expectation" do assert validator.validate({ "key" => [1, 2] }, { "key" => [Integer] }) end it "returns true when nested values match schema expectation" do assert validator.validate({ "key" => { :nested_key => "Value" } }, { "key" => { :nested_key => String } }) end it "returns true when collection of values all match schema expectation" do assert validator.validate([{ "key" => "Value" }, { "key" => "Value" }], [{ "key" => String }]) end it "returns true when collection is empty although schema covers optional members" do assert validator.validate([], [{ "key" => String }]) end it "returns true when additional keys are passed and not strict" do assert validator.validate({ "key" => "Value", :extra => "Bonus" }, { "key" => String }, { :allow_extra_keys => true }) end it "returns true when value is nil and schema expects NilClass" do assert validator.validate({ "key" => nil }, { "key" => NilClass }) end it "returns true when value and schema match as hashes" do assert validator.validate({}, {}) end it "returns true when value and schema match as arrays" do assert validator.validate([], []) end it "returns true when value is a Time" do assert validator.validate({ "time" => Time.now }, { "time" => Time }) end it "returns true when key is missing but value should be NilClass (#1477)" do assert validator.validate({}, { "key" => NilClass }, { :allow_optional_rules => true }) end it "returns true when key is missing but value is nullable (#1477)" do assert validator.validate({}, { "key" => Fog::Nullable::String }, { :allow_optional_rules => true }) end it "returns false when value does not match schema expectation" do refute validator.validate({ "key" => nil }, { "key" => String }) end it "returns false when key formats do not match" do refute validator.validate({ "key" => "Value" }, { :key => String }) end it "returns false when additional keys are passed and strict" do refute validator.validate({ "key" => "Missing" }, {}) end it "returns false when some keys do not appear" do refute validator.validate({}, { "key" => String }) end it "returns false when collection contains a member that does not match schema" do refute validator.validate([{ "key" => "Value" }, { "key" => 5 }], [{ "key" => String }]) end it "returns false when collection has multiple schema patterns" do refute validator.validate([{ "key" => "Value" }], [{ "key" => Integer }, { "key" => String }]) end it "returns false when hash and array are compared" do refute validator.validate({}, []) end it "returns false when array and hash are compared" do refute validator.validate([], {}) end it "returns false when a hash is expected but another data type is found" do refute validator.validate({ "key" => { :nested_key => [] } }, { "key" => { :nested_key => {} } }) end it "returns false when key is missing but value should be NilClass (#1477)" do refute validator.validate({}, { "key" => NilClass }, { :allow_optional_rules => false }) end it "returns false when key is missing but value is nullable (#1477)" do refute validator.validate({}, { "key" => Fog::Nullable::String }, { :allow_optional_rules => false }) end end end fog-core-1.45.0/spec/timeout_spec.rb000066400000000000000000000005121314013014100172430ustar00rootroot00000000000000require "spec_helper" describe "Fog#timeout" do before do @old_timeout = Fog.timeout end after do Fog.timeout = @old_timeout end it "defaults to 600" do assert_equal 600, Fog.timeout end it "can be reassigned through Fog#timeout=" do Fog.timeout = 300 assert_equal 300, Fog.timeout end end fog-core-1.45.0/spec/utils_spec.rb000066400000000000000000000020241314013014100167150ustar00rootroot00000000000000require "spec_helper" describe Fog::Core::Utils do describe "prepare_service_settings" do it "changes String keys to be Symbols" do settings = { "a" => 3 } expected = { :a => 3 } assert_equal expected, Fog::Core::Utils.prepare_service_settings(settings) end it "leaves Symbol keys unchanged" do settings = { :something => 2 } expected = { :something => 2 } assert_equal expected, Fog::Core::Utils.prepare_service_settings(settings) end it "changes nested String keys to Symbols" do settings = { "connection_options" => { "val" => 5 } } expected = { :connection_options => { :val => 5 } } assert_equal expected, Fog::Core::Utils.prepare_service_settings(settings) end it "does not change the :header key or contents" do settings = { :headers => { "User-Agent" => "my user agent" } } expected = { :headers => { "User-Agent" => "my user agent" } } assert_equal expected, Fog::Core::Utils.prepare_service_settings(settings) end end end fog-core-1.45.0/spec/uuid_spec.rb000066400000000000000000000004101314013014100165200ustar00rootroot00000000000000require "spec_helper" describe "Fog::UUID" do it "#supported?" do Fog::UUID.supported? == SecureRandom.respond_to?(:uuid) end it "generates a valid UUID" do Fog::UUID.uuid =~ /[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}/ end end fog-core-1.45.0/spec/wait_for_spec.rb000066400000000000000000000013711314013014100173730ustar00rootroot00000000000000require "spec_helper" describe "Fog#wait_for" do it "returns a Hash indicating the wait duration if successful" do assert_equal({ :duration => 0 }, Fog.wait_for(1) { true }) end it "raises if the wait timeout is exceeded" do assert_raises(Fog::Errors::TimeoutError) do Fog.wait_for(2) { false } end end it "does not raise if successful when the wait timeout is exceeded" do timeout = 2 i = 0 ret = Fog.wait_for(timeout) { i = i + 1; i > 2 } assert_operator(ret[:duration], :>, timeout) end it "accepts a proc to determine the sleep interval" do i = 0 ret = Fog.wait_for(1, lambda { |_t| 1 }) do i += 1 i > 1 end assert(1 <= ret[:duration]) assert(ret[:duration] < 2) end end