pax_global_header00006660000000000000000000000064124373555450014527gustar00rootroot0000000000000052 comment=aa9c8a3f5a470e9cc70929e67f5905c778690aec fog-atmos-0.1.0/000077500000000000000000000000001243735554500134215ustar00rootroot00000000000000fog-atmos-0.1.0/.gitignore000066400000000000000000000003101243735554500154030ustar00rootroot00000000000000*.gem *.rbc .bundle .config .yardoc Gemfile.lock InstalledFiles _yardoc coverage doc/ lib/bundler/man pkg rdoc spec/reports test/tmp test/version_tmp tmp *.bundle *.so *.o *.a mkmf.log gemfiles/*.lockfog-atmos-0.1.0/.rubocop.yml000066400000000000000000000004741243735554500157000ustar00rootroot00000000000000Metrics/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-atmos-0.1.0/.ruby-gemset000066400000000000000000000000111243735554500156550ustar00rootroot00000000000000fog-atmosfog-atmos-0.1.0/.ruby-version000066400000000000000000000000061243735554500160620ustar00rootroot000000000000002.1.5 fog-atmos-0.1.0/.travis.yml000066400000000000000000000006621243735554500155360ustar00rootroot00000000000000matrix: include: - rvm: 1.8.7 gemfile: gemfiles/Gemfile.1.9.2- - rvm: 1.9.2 gemfile: gemfiles/Gemfile.1.9.2- - rvm: 1.9.3 gemfile: gemfiles/Gemfile.1.9.3+ - rvm: 2.0.0 gemfile: gemfiles/Gemfile.1.9.3+ - rvm: 2.1.4 gemfile: gemfiles/Gemfile.1.9.3+ env: COVERAGE=true - rvm: ree gemfile: gemfiles/Gemfile.1.9.2- - rvm: jruby gemfile: gemfiles/Gemfile.1.9.3+ fog-atmos-0.1.0/CONTRIBUTING.md000066400000000000000000000015331243735554500156540ustar00rootroot00000000000000## 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-atmos/pulls). * Review open [issues](https://github.com/fog/fog-atmos/issues) for things to help on. * [Create an issue](https://github.com/fog/fog-atmos/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-atmos` to ensure everything is up to date. * [Submit a pull request](https://github.com/fog/fog-atmos/compare/) ### Non-Coding * Offer feedback on open [issues](https://github.com/fog/fog-atmos/issues). * Organize or volunteer at events.fog-atmos-0.1.0/CONTRIBUTORS.md000066400000000000000000000006351243735554500157040ustar00rootroot00000000000000* Brian D. Burns * David Prater * Lance Ivy * Michael Harrison * Paul Thornthwaite * Paulo Henrique Lopes Ribeiro * Peter Vawser * Tim * Timur Alperovich * Toby Hede fog-atmos-0.1.0/Gemfile000066400000000000000000000001361243735554500147140ustar00rootroot00000000000000source 'https://rubygems.org' # Specify your gem's dependencies in fog-atmos.gemspec gemspec fog-atmos-0.1.0/LICENSE.md000066400000000000000000000021761243735554500150330ustar00rootroot00000000000000The MIT License (MIT) Copyright (c) 2014-2014 [CONTRIBUTORS.md](https://github.com/fog/fog-atmos/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-atmos-0.1.0/README.md000066400000000000000000000023411243735554500147000ustar00rootroot00000000000000# Fog::Atmos ![Gem Version](https://badge.fury.io/rb/fog-atmos.svg) [![Build Status](https://travis-ci.org/fog/fog-atmos.svg?branch=master)](https://travis-ci.org/fog/fog-atmos) [![Dependency Status](https://gemnasium.com/fog/fog-atmos.svg)](https://gemnasium.com/fog/fog-atmos) [![Coverage Status](https://img.shields.io/coveralls/fog/fog-atmos.svg)](https://coveralls.io/r/fog/fog-atmos?branch=master) [![Code Climate](https://codeclimate.com/github/fog/fog-atmos.png)](https://codeclimate.com/github/fog/fog-atmos) Module for the 'fog' gem to support Atmos ## Help Needed This gem needs a maintainer. If you want to work on it, please contact [@geemus](mailto:geemus@gmail.com) or [@plribeiro3000](mailto:plribeiro3000@gmail.com) ## Installation Add this line to your application's Gemfile: ```ruby gem 'fog-atmos' ``` And then execute: $ bundle Or install it yourself as: $ gem install fog-atmos ## Usage TODO: Write usage instructions here ## Contributing 1. Fork it ( https://github.com/fog/fog-atmos/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 a new Pull Request fog-atmos-0.1.0/Rakefile000066400000000000000000000005701243735554500150700ustar00rootroot00000000000000require 'bundler/gem_tasks' require 'rake/testtask' Rake::TestTask.new do |t| t.libs.push %w(spec) t.test_files = FileList['spec/**/*_spec.rb'] t.verbose = true end desc 'Default Task' task :default => [ :test, 'test:travis' ] namespace :test do mock = ENV['FOG_MOCK'] || 'true' task :travis do sh("export FOG_MOCK=#{mock} && bundle exec shindont") end end fog-atmos-0.1.0/fog-atmos.gemspec000066400000000000000000000024041243735554500166620ustar00rootroot00000000000000# coding: utf-8 lib = File.expand_path('../lib', __FILE__) $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib) require 'fog/atmos/version' Gem::Specification.new do |spec| spec.name = "fog-atmos" spec.version = Fog::Atmos::VERSION spec.authors = ["Paulo Henrique Lopes Ribeiro"] spec.email = ["plribeiro3000@gmail.com"] spec.summary = %q{Module for the 'fog' gem to support Atmos.} spec.description = %q{This library can be used as a module for `fog` or as standalone provider to use the Atmos in applications.} spec.homepage = "" spec.license = "MIT" spec.files = `git ls-files -z`.split("\x0") 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 "fog-core" spec.add_dependency "fog-xml" spec.add_development_dependency "rake" spec.add_development_dependency "minitest" spec.add_development_dependency "shindo" spec.add_development_dependency "turn" spec.add_development_dependency "pry" if RUBY_VERSION.to_f > 1.9 spec.add_development_dependency "coveralls" spec.add_development_dependency "rubocop" end end fog-atmos-0.1.0/gemfiles/000077500000000000000000000000001243735554500152145ustar00rootroot00000000000000fog-atmos-0.1.0/gemfiles/Gemfile.1.9.2-000066400000000000000000000002101243735554500172230ustar00rootroot00000000000000source :rubygems gem 'mime-types', '< 2.0' gem 'nokogiri', '< 1.6' gem 'rest-client', '~> 1.6.8' gem 'fog-core' gemspec :path => '../'fog-atmos-0.1.0/gemfiles/Gemfile.1.9.3+000066400000000000000000000001671243735554500172350ustar00rootroot00000000000000source :rubygems gem 'activesupport', '>= 3.0', '< 4' gem 'mime-types', '< 2.0' gem 'fog-core' gemspec :path => '../'fog-atmos-0.1.0/lib/000077500000000000000000000000001243735554500141675ustar00rootroot00000000000000fog-atmos-0.1.0/lib/fog/000077500000000000000000000000001243735554500147425ustar00rootroot00000000000000fog-atmos-0.1.0/lib/fog/atmos.rb000066400000000000000000000003421243735554500164110ustar00rootroot00000000000000require 'fog/atmos/version' require 'fog/core' require 'fog/xml' module Fog module Atmos extend Fog::Provider service(:storage, 'Storage') end module Storage autoload :Atmos, 'fog/storage/atmos' end end fog-atmos-0.1.0/lib/fog/atmos/000077500000000000000000000000001243735554500160655ustar00rootroot00000000000000fog-atmos-0.1.0/lib/fog/atmos/storage.rb000066400000000000000000000000001243735554500200440ustar00rootroot00000000000000fog-atmos-0.1.0/lib/fog/atmos/version.rb000066400000000000000000000000721243735554500200760ustar00rootroot00000000000000module Fog module Atmos VERSION = "0.1.0" end end fog-atmos-0.1.0/lib/fog/bin/000077500000000000000000000000001243735554500155125ustar00rootroot00000000000000fog-atmos-0.1.0/lib/fog/bin/atmos.rb000066400000000000000000000014201243735554500171570ustar00rootroot00000000000000class Atmos < Fog::Bin class << self def class_for(key) case key when :storage Fog::Storage::Atmos else raise ArgumentError, "Unsupported #{self} service: #{key}" end end def [](service) @@connections ||= Hash.new do |hash, key| hash[key] = case key when :storage Fog::Logger.warning("Atmos[:storage] is not recommended, use Storage[:atmos] for portability") Fog::Storage.new(:provider => 'Atmos') else raise ArgumentError, "Unrecognized service: #{service}" end end @@connections[service] end def services Fog::Atmos.services end end endfog-atmos-0.1.0/lib/fog/storage/000077500000000000000000000000001243735554500164065ustar00rootroot00000000000000fog-atmos-0.1.0/lib/fog/storage/atmos.rb000066400000000000000000000131541243735554500200620ustar00rootroot00000000000000module Fog module Storage class Atmos < Fog::Service autoload :Directory, 'fog/storage/atmos/models/directory' autoload :Directories, 'fog/storage/atmos/models/directories' autoload :File, 'fog/storage/atmos/models/file' autoload :Files, 'fog/storage/atmos/models/files' requires :atmos_storage_endpoint, :atmos_storage_secret, :atmos_storage_token recognizes :persistent model_path 'fog/storage/atmos/models' model :directory collection :directories model :file collection :files request_path 'fog/storage/atmos/requests' # request :delete_container request :get_namespace request :head_namespace request :post_namespace request :put_namespace request :delete_namespace module Utils ENDPOINT_REGEX = /(https*):\/\/([a-zA-Z0-9\.\-]+)(:[0-9]+)?(\/.*)?/ def ssl? protocol = @endpoint.match(ENDPOINT_REGEX)[1] raise ArgumentError, 'Invalid endpoint URL' if protocol.nil? return true if protocol == 'https' return false if protocol == 'http' raise ArgumentError, "Unknown protocol #{protocol}" end def port port = @endpoint.match(ENDPOINT_REGEX)[3] return ssl? ? 443 : 80 if port.nil? port.split(':')[1].to_i end def host @endpoint.match(ENDPOINT_REGEX)[2] end def api_path @endpoint.match(ENDPOINT_REGEX)[4] end def setup_credentials(options) @storage_token = options[:atmos_storage_token] @storage_secret = options[:atmos_storage_secret] @storage_secret_decoded = Base64.decode64(@storage_secret) @endpoint = options[:atmos_storage_endpoint] @prefix = self.ssl? ? 'https' : 'http' @storage_host = self.host @storage_port = self.port @api_path = self.api_path end end class Mock include Utils def initialize(options={}) setup_credentials(options) end def request(options) raise "Atmos Storage mocks not implemented" end end class Real include Utils def initialize(options={}) setup_credentials(options) @connection_options = options[:connection_options] || {} @hmac = Fog::HMAC.new('sha1', @storage_secret_decoded) @persistent = options.fetch(:persistent, false) @connection = Fog::XML::Connection.new("#{@prefix}://#{@storage_host}:#{@storage_port}", @persistent, @connection_options) end def uid @storage_token#.split('/')[-1] end def sign(string) value = @hmac.sign(string) Base64.encode64( value ).chomp() end def reload @connection.reset end def request(params, &block) req_path = params[:path] # Force set host and port params.merge!({ :host => @storage_host, :path => "#{@api_path}/rest/#{params[:path]}", }) # Set default method and headers params = {:method => 'GET', :headers => {}}.merge params params[:headers]["Content-Type"] ||= "application/octet-stream" # Add request date params[:headers]["date"] = Time.now().httpdate() params[:headers]["x-emc-uid"] = @storage_token # Build signature string signstring = "" signstring += params[:method] signstring += "\n" signstring += params[:headers]["Content-Type"] signstring += "\n" if( params[:headers]["range"] ) signstring += params[:headers]["range"] end signstring += "\n" signstring += params[:headers]["date"] signstring += "\n" signstring += "/rest/" + URI.unescape( req_path ).downcase query_str = params[:query].map{|k,v| "#{k}=#{v}"}.join('&') signstring += '?' + query_str unless query_str.empty? signstring += "\n" customheaders = {} params[:headers].each { |key,value| case key when 'x-emc-date', 'x-emc-signature' #skip when /^x-emc-/ customheaders[ key.downcase ] = value end } header_arr = customheaders.sort() header_arr.each { |key,value| # Values are lowercase and whitespace-normalized signstring += key + ":" + value.strip.chomp.squeeze( " " ) + "\n" } digest = @hmac.sign(signstring.chomp()) signature = Base64.encode64( digest ).chomp() params[:headers]["x-emc-signature"] = signature params.delete(:host) #invalid excon request parameter parse = params.delete(:parse) begin response = @connection.request(params, &block) rescue Excon::Errors::HTTPStatusError => error raise case error when Excon::Errors::NotFound Fog::Storage::Atmos::NotFound.slurp(error) else error end end unless response.body.empty? if parse document = Fog::ToHashDocument.new parser = Nokogiri::XML::SAX::PushParser.new(document) parser << response.body parser.finish response.body = document.body end end response end end end end end fog-atmos-0.1.0/lib/fog/storage/atmos/000077500000000000000000000000001243735554500175315ustar00rootroot00000000000000fog-atmos-0.1.0/lib/fog/storage/atmos/models/000077500000000000000000000000001243735554500210145ustar00rootroot00000000000000fog-atmos-0.1.0/lib/fog/storage/atmos/models/directories.rb000066400000000000000000000025561243735554500236650ustar00rootroot00000000000000module Fog module Storage class Atmos class Directories < Fog::Collection model Fog::Storage::Atmos::Directory attribute :directory def all directory ? ns = directory.key : ns = '' ns = ns + '/' unless ns =~ /\/$/ data = service.get_namespace(ns).body[:DirectoryList] data = {:DirectoryEntry => []} if data.kind_of? String data[:DirectoryEntry] = [data[:DirectoryEntry]] if data[:DirectoryEntry].kind_of? Hash dirs = data[:DirectoryEntry].select {|de| de[:FileType] == 'directory'} dirs.each do |d| d[:Filename] = ns + d[:Filename] if directory d[:Filename] += '/' unless d[:Filename] =~ /\/$/ end load(dirs) end def get(key, _options = {}) return nil if key == '' # Root dir shouldn't be retrieved like this. key =~ /\/$/ ? ns = key : ns = key + '/' res = service.get_namespace ns emc_meta = res.headers['x-emc-meta'] obj_id = emc_meta.scan(/objectid=(\w+),/).flatten[0] new(:objectid => obj_id, :key => ns) rescue Fog::Storage::Atmos::NotFound nil end def new(attributes ={}) attributes = {:directory => directory}.merge(attributes) if directory super(attributes) end end end end end fog-atmos-0.1.0/lib/fog/storage/atmos/models/directory.rb000066400000000000000000000023711243735554500233500ustar00rootroot00000000000000module Fog module Storage class Atmos class Directory < Fog::Model identity :key, :aliases => :Filename attribute :objectid, :aliases => :ObjectID def files @files ||= begin Fog::Storage::Atmos::Files.new( :directory => self, :service => service ) end end def directories @directories ||= begin Fog::Storage::Atmos::Directories.new( :directory => self, :service => service ) end end def save self.key = attributes[:directory].key + key if attributes[:directory] self.key = key + '/' unless key =~ /\/$/ res = service.post_namespace key reload end def destroy(opts={}) if opts[:recursive] files.each {|f| f.destroy } directories.each do |d| d.files.each {|f| f.destroy } d.destroy(opts) end end service.delete_namespace key end end end end end fog-atmos-0.1.0/lib/fog/storage/atmos/models/file.rb000066400000000000000000000065421243735554500222670ustar00rootroot00000000000000module Fog module Storage class Atmos class File < Fog::Model identity :key, :aliases => :Filename attribute :content_length, :aliases => ['bytes', 'Content-Length'], :type => :integer attribute :content_type, :aliases => ['content_type', 'Content-Type'] attribute :objectid, :aliases => :ObjectID attribute :created_at, :aliases => :ctime def body attributes[:body] ||= if objectid collection.get(identity).body else '' end end def body=(new_body) attributes[:body] = new_body end def directory @directory end def copy(target_directory_key, target_file_key, options={}) target_directory = service.directories.new(:key => target_directory_key) target_directory.files.create( :key => target_file_key, :body => body ) end def destroy requires :directory, :key service.delete_namespace([directory.key, key].join('/')) true end def meta_data requires :directory, :key service.get_namespace([directory.key, key].join('/') + "?metadata/system") end def file_size data = meta_data meta_data.headers["x-emc-meta"].match(/size=\d+/).to_s.gsub(/size=/,"") end def public=(new_public) # NOOP - we don't need to flag files as public, getting the public URL for a file handles it. end # By default, expire in 5 years def public_url(expires = (Time.now + 5 * 365 * 24 * 60 * 60)) file = directory.files.head(key) self.objectid = if file && file.to_s.strip != "" then file.attributes['x-emc-meta'].scan(/objectid=(\w+),/).flatten[0] else nil end if self.objectid && self.objectid.to_s.strip != "" klass = service.ssl? ? URI::HTTPS : URI::HTTP uri = klass.build(:host => service.host, :port => service.port.to_i, :path => "/rest/objects/#{self.objectid}" ) sb = "GET\n" sb += uri.path.downcase + "\n" sb += service.uid + "\n" sb += String(expires.to_i()) signature = service.sign( sb ) uri.query = "uid=#{CGI::escape(service.uid)}&expires=#{expires.to_i()}&signature=#{CGI::escape(signature)}" uri.to_s else nil end end def save(options = {}) requires :body, :directory, :key directory.kind_of?(Directory) ? ns = directory.key : ns = directory ns += key options[:headers] ||= {} options[:headers]['Content-Type'] = content_type if content_type options[:body] = body begin data = service.post_namespace(ns, options) self.objectid = data.headers['location'].split('/')[-1] rescue => error if error.message =~ /The resource you are trying to create already exists./ data = service.put_namespace(ns, options) else raise error end end # merge_attributes(data.headers) true end private def directory=(new_directory) @directory = new_directory end end end end end fog-atmos-0.1.0/lib/fog/storage/atmos/models/files.rb000066400000000000000000000043321243735554500224450ustar00rootroot00000000000000module Fog module Storage class Atmos class Files < Fog::Collection attribute :directory attribute :limit attribute :marker attribute :path attribute :prefix model Fog::Storage::Atmos::File def all(options = {}) requires :directory directory ? ns = directory.key : ns = '' ns = ns + '/' unless ns =~ /\/$/ data = service.get_namespace(ns).body[:DirectoryList] data = {:DirectoryEntry => []} if data.kind_of? String data[:DirectoryEntry] = [data[:DirectoryEntry]] if data[:DirectoryEntry].kind_of? Hash files = data[:DirectoryEntry].select {|de| de[:FileType] == 'regular'} files.each do |s| data = service.head_namespace(directory.key + s[:Filename], :parse => false) headers = Hash[data.headers["x-emc-meta"].split(", ").map{|s|s.split("=")}] s[:content_length] = data.headers["Content-Length"] s[:content_type] = data.headers["Content-Type"] s[:created_at] = headers["ctime"] s[:directory] = directory end # TODO - Load additional file meta? load(files) end def get(key, &block) requires :directory data = service.get_namespace(directory.key + key, :parse => false, &block) file_data = data.headers.merge({ :body => data.body, :key => key }) new(file_data) rescue Fog::Storage::Atmos::NotFound nil end def get_url(key) requires :directory if self.directory.public_url "#{self.directory.public_url}/#{key}" end end def head(key, options = {}) requires :directory data = service.head_namespace(directory.key + key, :parse => false) file_data = data.headers.merge({ :body => data.body, :key => key }) new(file_data) rescue Fog::Storage::Atmos::NotFound nil end def new(attributes = {}) requires :directory super({ :directory => directory }.merge!(attributes)) end end end end end fog-atmos-0.1.0/lib/fog/storage/atmos/requests/000077500000000000000000000000001243735554500214045ustar00rootroot00000000000000fog-atmos-0.1.0/lib/fog/storage/atmos/requests/delete_namespace.rb000066400000000000000000000007121243735554500252070ustar00rootroot00000000000000module Fog module Storage class Atmos class Real def delete_namespace(namespace = '', options = {}) options = options.reject {|key, value| value.nil?} request({ :expects => 204, :method => 'DELETE', :path => "namespace/" + namespace, :query => options }.merge(options)) end end end end end fog-atmos-0.1.0/lib/fog/storage/atmos/requests/get_namespace.rb000066400000000000000000000011211243735554500245170ustar00rootroot00000000000000module Fog module Storage class Atmos class Real def get_namespace(namespace = '', options = {}, &block) options = options.reject {|key, value| value.nil?} if block_given? options[:response_block] = Proc.new end request({ :expects => 200, :method => 'GET', :path => "namespace/" + URI.escape(namespace), :query => {}, :parse => true }.merge(options)) end end end end end fog-atmos-0.1.0/lib/fog/storage/atmos/requests/head_namespace.rb000066400000000000000000000007611243735554500246520ustar00rootroot00000000000000module Fog module Storage class Atmos class Real def head_namespace(namespace = '', options = {}) options = options.reject {|key, value| value.nil?} request({ :expects => 200, :method => 'HEAD', :path => "namespace/" + URI.escape(namespace), :query => {}, :parse => true }.merge(options)) end end end end end fog-atmos-0.1.0/lib/fog/storage/atmos/requests/post_namespace.rb000066400000000000000000000007451243735554500247400ustar00rootroot00000000000000module Fog module Storage class Atmos class Real def post_namespace(namespace = '', options = {}) options = options.reject {|key, value| value.nil?} request({ :expects => 201, :method => 'POST', :path => "namespace/" + namespace, :query => {}, :parse => true }.merge(options)) end end end end end fog-atmos-0.1.0/lib/fog/storage/atmos/requests/put_namespace.rb000066400000000000000000000007431243735554500245610ustar00rootroot00000000000000module Fog module Storage class Atmos class Real def put_namespace(namespace = '', options = {}) options = options.reject {|key, value| value.nil?} request({ :expects => 200, :method => 'PUT', :path => "namespace/" + namespace, :query => {}, :parse => true }.merge(options)) end end end end end fog-atmos-0.1.0/tests/000077500000000000000000000000001243735554500145635ustar00rootroot00000000000000fog-atmos-0.1.0/tests/helper.rb000066400000000000000000000015701243735554500163720ustar00rootroot00000000000000require 'excon' if ENV['COVERAGE'] require 'coveralls' require 'simplecov' SimpleCov.start do add_filter '/spec/' add_filter '/test/' end end require File.expand_path(File.join(File.dirname(__FILE__), '../lib/fog/atmos')) Coveralls.wear! if ENV['COVERAGE'] Excon.defaults.merge!(:debug_request => true, :debug_response => true) require File.expand_path(File.join(File.dirname(__FILE__), 'helpers', 'mock_helper')) # This overrides the default 600 seconds timeout during live test runs if Fog.mocking? FOG_TESTING_TIMEOUT = ENV['FOG_TEST_TIMEOUT'] || 2000 Fog.timeout = 2000 Fog::Logger.warning "Setting default fog timeout to #{Fog.timeout} seconds" else FOG_TESTING_TIMEOUT = Fog.timeout end def lorem_file File.open(File.dirname(__FILE__) + '/lorem.txt', 'r') end def array_differences(array_a, array_b) (array_a - array_b) | (array_b - array_a) endfog-atmos-0.1.0/tests/helpers/000077500000000000000000000000001243735554500162255ustar00rootroot00000000000000fog-atmos-0.1.0/tests/helpers/collection_helper.rb000066400000000000000000000050271243735554500222500ustar00rootroot00000000000000def 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 if !Fog.mocking? || mocks_implemented @identity = @instance.identity end 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 = [ '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" and JRUBY_VERSION =~ /1\.7\.[5-8]/ methods.delete('all?') end methods.each do |enum_method| if collection.respond_to?(enum_method) tests("##{enum_method}").succeeds do block_called = false collection.send(enum_method) {|x| block_called = true } block_called end end end [ 'max_by','min_by' ].each do |enum_method| if collection.respond_to?(enum_method) tests("##{enum_method}").succeeds do block_called = false collection.send(enum_method) {|x| block_called = true; 0 } block_called end end end end if block_given? yield(@instance) end if !Fog.mocking? || mocks_implemented @instance.destroy end 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-atmos-0.1.0/tests/helpers/compute/000077500000000000000000000000001243735554500177015ustar00rootroot00000000000000fog-atmos-0.1.0/tests/helpers/compute/flavors_helper.rb000066400000000000000000000014631243735554500232450ustar00rootroot00000000000000def 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-atmos-0.1.0/tests/helpers/compute/server_helper.rb000066400000000000000000000012461243735554500230760ustar00rootroot00000000000000def 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 if !Fog.mocking? || mocks_implemented @instance.wait_for { ready? } end end end fog-atmos-0.1.0/tests/helpers/compute/servers_helper.rb000066400000000000000000000004101243735554500232510ustar00rootroot00000000000000def 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-atmos-0.1.0/tests/helpers/formats_helper.rb000066400000000000000000000070561243735554500215740ustar00rootroot00000000000000require "fog/schema/data_validator" # 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)} 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-atmos-0.1.0/tests/helpers/formats_helper_tests.rb000066400000000000000000000070201243735554500230050ustar00rootroot00000000000000Shindo.tests('test_helper', 'meta') do tests('comparing welcome data against schema') do data = {:welcome => "Hello" } data_matches_schema(:welcome => String) { data } end tests('#data_matches_schema') do tests('when value matches schema expectation') do data_matches_schema({"key" => String}) { {"key" => "Value"} } end tests('when values within an array all match schema expectation') do data_matches_schema({"key" => [Integer]}) { {"key" => [1, 2]} } end tests('when nested values match schema expectation') do data_matches_schema({"key" => {:nested_key => String}}) { {"key" => {:nested_key => "Value"}} } end tests('when collection of values all match schema expectation') do data_matches_schema([{"key" => String}]) { [{"key" => "Value"}, {"key" => "Value"}] } end tests('when collection is empty although schema covers optional members') do data_matches_schema([{"key" => String}], {:allow_optional_rules => true}) { [] } end tests('when additional keys are passed and not strict') do data_matches_schema({"key" => String}, {:allow_extra_keys => true}) { {"key" => "Value", :extra => "Bonus"} } end tests('when value is nil and schema expects NilClass') do data_matches_schema({"key" => NilClass}) { {"key" => nil} } end tests('when value and schema match as hashes') do data_matches_schema({}) { {} } end tests('when value and schema match as arrays') do data_matches_schema([]) { [] } end tests('when value is a Time') do data_matches_schema({"time" => Time}) { {"time" => Time.now} } end tests('when key is missing but value should be NilClass (#1477)') do data_matches_schema({"key" => NilClass}, {:allow_optional_rules => true}) { {} } end tests('when key is missing but value is nullable (#1477)') do data_matches_schema({"key" => Fog::Nullable::String}, {:allow_optional_rules => true}) { {} } end end tests('#formats backwards compatible changes') do tests('when value matches schema expectation') do formats({"key" => String}) { {"key" => "Value"} } end tests('when values within an array all match schema expectation') do formats({"key" => [Integer]}) { {"key" => [1, 2]} } end tests('when nested values match schema expectation') do formats({"key" => {:nested_key => String}}) { {"key" => {:nested_key => "Value"}} } end tests('when collection of values all match schema expectation') do formats([{"key" => String}]) { [{"key" => "Value"}, {"key" => "Value"}] } end tests('when collection is empty although schema covers optional members') do formats([{"key" => String}]) { [] } end tests('when additional keys are passed and not strict') do formats({"key" => String}, false) { {"key" => "Value", :extra => "Bonus"} } end tests('when value is nil and schema expects NilClass') do formats({"key" => NilClass}) { {"key" => nil} } end tests('when value and schema match as hashes') do formats({}) { {} } end tests('when value and schema match as arrays') do formats([]) { [] } end tests('when value is a Time') do formats({"time" => Time}) { {"time" => Time.now} } end tests('when key is missing but value should be NilClass (#1477)') do formats({"key" => NilClass}) { {} } end tests('when key is missing but value is nullable (#1477)') do formats({"key" => Fog::Nullable::String}) { {} } end end end fog-atmos-0.1.0/tests/helpers/mock_helper.rb000066400000000000000000000151131243735554500210430ustar00rootroot00000000000000# Use so you can run in mock mode from the command line # # FOG_MOCK=true fog if ENV["FOG_MOCK"] == "true" Fog.mock! end # if in mocked mode, fill in some fake credentials for us if Fog.mock? Fog.credentials = { :aws_access_key_id => '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', :atmos_storage_token => 'atmos_token', :atmos_storage_secret => 'atmos_secret', :atmos_storage_endpoint => 'http://atmos.is.cool:1000/test1.0', :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', :ecloud_username => 'ecloud_username', :ecloud_password => 'ecloud_password', :ecloud_versions_uri => 'http://ecloud.versions.uri', :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', :opennebula_endpoint => 'http://opennebula:2633/RPC2', :opennebula_username => 'oneadmin', :opennebula_password => 'oneadmin', :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', :profitbricks_username => 'profitbricks_username', :profitbricks_password => 'profitbricks_password', :libvirt_uri => 'qemu://libvirt/system', :rackspace_api_key => 'rackspace_api_key', :rackspace_region => 'dfw', :rackspace_username => 'rackspace_username', :riakcs_access_key_id => 'riakcs_access_key_id', :riakcs_secret_access_key => 'riakcs_secret_access_key', :sakuracloud_api_token => 'sakuracloud_api_token', :sakuracloud_api_token_secret => 'sakuracloud_api_token_secret', :storm_on_demand_username => 'storm_on_demand_username', :storm_on_demand_password => 'storm_on_demand_password', :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', :docker_username => 'docker-fan', :docker_password => 'i<3docker', :docker_email => 'dockerfan@gmail.com', :docker_url => 'unix://var/run/docker.sock' }.merge(Fog.credentials) end fog-atmos-0.1.0/tests/helpers/model_helper.rb000066400000000000000000000014141243735554500212110ustar00rootroot00000000000000def 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 if block_given? yield(@instance) end 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(65536).to_s(16).rjust(4, '0') [base_name, suffix] * '-' end fog-atmos-0.1.0/tests/helpers/responds_to_helper.rb000066400000000000000000000003741243735554500224540ustar00rootroot00000000000000module Shindo class Tests def responds_to(method_names) for method_name in [*method_names] tests("#respond_to?(:#{method_name})").returns(true) do @instance.respond_to?(method_name) end end end end end fog-atmos-0.1.0/tests/helpers/schema_validator_tests.rb000066400000000000000000000072311243735554500233040ustar00rootroot00000000000000Shindo.tests('Fog::Schema::DataValidator', 'meta') do validator = Fog::Schema::DataValidator.new tests('#validate') do tests('returns true') do returns(true, 'when value matches schema expectation') do validator.validate({"key" => "Value"}, {"key" => String}) end returns(true, 'when values within an array all match schema expectation') do validator.validate({"key" => [1, 2]}, {"key" => [Integer]}) end returns(true, 'when nested values match schema expectation') do validator.validate({"key" => {:nested_key => "Value"}}, {"key" => {:nested_key => String}}) end returns(true, 'when collection of values all match schema expectation') do validator.validate([{"key" => "Value"}, {"key" => "Value"}], [{"key" => String}]) end returns(true, 'when collection is empty although schema covers optional members') do validator.validate([], [{"key" => String}]) end returns(true, 'when additional keys are passed and not strict') do validator.validate({"key" => "Value", :extra => "Bonus"}, {"key" => String}, {:allow_extra_keys => true}) end returns(true, 'when value is nil and schema expects NilClass') do validator.validate({"key" => nil}, {"key" => NilClass}) end returns(true, 'when value and schema match as hashes') do validator.validate({}, {}) end returns(true, 'when value and schema match as arrays') do validator.validate([], []) end returns(true, 'when value is a Time') do validator.validate({"time" => Time.now}, {"time" => Time}) end returns(true, 'when key is missing but value should be NilClass (#1477)') do validator.validate({}, {"key" => NilClass}, {:allow_optional_rules => true}) end returns(true, 'when key is missing but value is nullable (#1477)') do validator.validate({}, {"key" => Fog::Nullable::String}, {:allow_optional_rules => true}) end end tests('returns false') do returns(false, 'when value does not match schema expectation') do validator.validate({"key" => nil}, {"key" => String}) end returns(false, 'when key formats do not match') do validator.validate({"key" => "Value"}, {:key => String}) end returns(false, 'when additional keys are passed and strict') do validator.validate({"key" => "Missing"}, {}) end returns(false, 'when some keys do not appear') do validator.validate({}, {"key" => String}) end returns(false, 'when collection contains a member that does not match schema') do validator.validate([{"key" => "Value"}, {"key" => 5}], [{"key" => String}]) end returns(false, 'when collection has multiple schema patterns') do validator.validate([{"key" => "Value"}], [{"key" => Integer}, {"key" => String}]) end returns(false, 'when hash and array are compared') do validator.validate({}, []) end returns(false, 'when array and hash are compared') do validator.validate([], {}) end returns(false, 'when a hash is expected but another data type is found') do validator.validate({"key" => {:nested_key => []}}, {"key" => {:nested_key => {}}}) end returns(false, 'when key is missing but value should be NilClass (#1477)') do validator.validate({}, {"key" => NilClass}, {:allow_optional_rules => false}) end returns(false, 'when key is missing but value is nullable (#1477)') do validator.validate({}, {"key" => Fog::Nullable::String}, {:allow_optional_rules => false}) end end end end fog-atmos-0.1.0/tests/helpers/succeeds_helper.rb000066400000000000000000000002061243735554500217050ustar00rootroot00000000000000module Shindo class Tests def succeeds test('succeeds') do !!instance_eval(&Proc.new) end end end end fog-atmos-0.1.0/tests/models/000077500000000000000000000000001243735554500160465ustar00rootroot00000000000000fog-atmos-0.1.0/tests/models/storage/000077500000000000000000000000001243735554500175125ustar00rootroot00000000000000fog-atmos-0.1.0/tests/models/storage/file_update_tests.rb000066400000000000000000000007341243735554500235460ustar00rootroot00000000000000Shindo.tests("Storage[:atmos] | nested directories", ['atmos']) do unless Fog.mocking? @directory = Fog::Storage[:atmos].directories.create(:key => 'updatefiletests') end atmos = Fog::Storage[:atmos] tests("update a file").succeeds do pending if Fog.mocking? file = @directory.files.create(:key => 'lorem.txt', :body => lorem_file) file.body = "xxxxxx" file.save end unless Fog.mocking? @directory.destroy(:recursive => true) end end fog-atmos-0.1.0/tests/models/storage/nested_directories_tests.rb000066400000000000000000000013431243735554500251400ustar00rootroot00000000000000Shindo.tests("Storage[:atmos] | nested directories", ['atmos']) do atmos = Fog::Storage[:atmos] tests("create a directory with a / character").succeeds do pending if Fog.mocking? atmos.directories.create(:key => 'sub/path') end tests("List of top directory returns sub dir").returns(1) do pending if Fog.mocking? atmos.directories.get('sub').directories.count end tests("create a directory in a sub dir").returns('sub/path/newdir/') do pending if Fog.mocking? atmos.directories.get('sub/path').directories.create(:key => 'newdir').identity end tests("Recursively destroy parent dir").succeeds do pending if Fog.mocking? atmos.directories.get('sub').destroy(:recursive => true) end end