| # |
| # Licensed to the Apache Software Foundation (ASF) under one or more |
| # contributor license agreements. See the NOTICE file distributed with |
| # this work for additional information regarding copyright ownership. The |
| # ASF licenses this file to you under the Apache License, Version 2.0 (the |
| # "License"); you may not use this file except in compliance with the |
| # License. You may obtain a copy of the License at |
| # |
| # http://www.apache.org/licenses/LICENSE-2.0 |
| # |
| # Unless required by applicable law or agreed to in writing, software |
| # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT |
| # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the |
| # License for the specific language governing permissions and limitations |
| # under the License. |
| |
| require 'rubygems' |
| require 'require_relative' if RUBY_VERSION < '1.9' |
| require_relative '../helpers/common.rb' |
| require_relative 'common_tests_collections.rb' |
| |
| require 'singleton' |
| |
| module Deltacloud |
| module Test |
| |
| class Config |
| |
| include Singleton |
| |
| def initialize |
| @hash = Deltacloud::Test::yaml_config |
| end |
| |
| def url |
| @hash["api_url"] |
| end |
| |
| def basic_auth(u = nil, p = nil) |
| u ||= @hash[driver]["user"] |
| p ||= @hash[driver]["password"] |
| "Basic #{Base64.encode64("#{u}:#{p}")}" |
| end |
| |
| def bucket_locations |
| @hash[driver]["bucket_locations"] |
| end |
| |
| def preferred |
| @hash[driver]["preferred"] || {} |
| end |
| |
| def driver |
| xml.root[:driver] |
| end |
| |
| def drivers |
| @drivers ||= RestClient.get(url+"/drivers").xml.xpath("//driver/name").map { |c| c.text.downcase } |
| end |
| |
| def version |
| xml.root[:version] |
| end |
| |
| def collections |
| xml.xpath("//api/link").map { |c| c[:rel].to_sym } |
| end |
| |
| def features |
| result = {} |
| xml.xpath("//api/link").each do |coll| |
| result[coll[:rel].to_sym] = coll.xpath("feature").map { |c| c[:name].to_sym } |
| end |
| result |
| end |
| |
| private |
| def xml |
| unless @xml |
| begin |
| @xml = RestClient.get(url).xml |
| drv = @xml.root[:driver] |
| rescue RestClient::Unauthorized => e |
| #need to do this by hand - RestClient only return exception for 4XX |
| #(e.g. Openstack needs creds for /api) - but headers contain driver |
| uri = URI.parse(url) |
| http = Net::HTTP.new(uri.host, uri.port) |
| request = Net::HTTP::Get.new(uri.path) |
| res = http.request(request) |
| drv = res["X-Deltacloud-Driver"] |
| u,p = [@hash[drv]["user"], @hash[drv]["password"]] |
| @xml = RestClient.get(url, {'Authorization' => "Basic #{Base64.encode64("#{u}:#{p}")}"}).xml |
| end |
| unless @hash[drv] |
| raise "No config for #{drv} driver in config.yaml used by #{url}" |
| end |
| unless @hash[drv]["user"] && @hash[drv]["password"] |
| raise "No user or password in config.yaml for #{drv} driver used by #{url}" |
| end |
| end |
| @xml |
| end |
| end |
| |
| def self.config |
| Config::instance |
| end |
| end |
| end |
| |
| module Deltacloud::Test::Methods |
| |
| module Global |
| # Return the current test config; we call that 'api' since it looks a |
| # little prettier in the tests |
| def api |
| Deltacloud::Test::config |
| end |
| |
| # Make a GET request for +path+ and return the +RestClient::Response+. The |
| # query string for the request is generated from +params+, with the |
| # exception of a few special entries in params, which are used to set some |
| # headers, and will not appear in the query string: |
| # |
| # :noauth : do not send an auth header |
| # :user, :password : use these for the auth header |
| # :accept : can be :xml or :json, and sets the Accept header |
| # :driver, :provider : set driver and/or provider with the appropriate header |
| # :content_type : set content_type for upload (e.g. put blob) |
| # :x_deltacloud_blobmeta-X : set deltacloud blob metadata |
| # |
| # If none of the auth relevant params are set, use the username and |
| # password for the current driver from the config |
| def get(path, params={}) |
| url, headers = process_url_params(path, params) |
| RestClient.get url, headers |
| end |
| |
| def post(path, post_body, params={}) |
| if api.preferred["provider"] and not params[:provider] |
| params[:provider] = api.preferred["provider"] |
| end |
| url, headers = process_url_params(path, params) |
| RestClient.post url, post_body, headers |
| end |
| |
| def put(path, body, params={}) |
| url, headers = process_url_params(path, params) |
| if body.is_a?(File) |
| #set timeouts http://rdoc.info/github/archiloque/rest-client/RestClient/Resource |
| resource = RestClient::Resource.new(url, :open_timeout => 120, :timeout=> 9999) |
| resource.put body.read, headers |
| else |
| RestClient.put url, body, headers |
| end |
| end |
| |
| def delete(path, params={}) |
| url, headers = process_url_params(path, params) |
| RestClient.delete url, headers |
| end |
| |
| def options(path, params={}) |
| url, headers = process_url_params(path, params) |
| RestClient.options url, headers |
| end |
| |
| def head(path, params={}) |
| url, headers = process_url_params(path, params) |
| RestClient.head url, headers |
| end |
| |
| def random_name |
| name = rand(36**10).to_s(36) |
| name.insert(0, "dcapitest") |
| end |
| |
| def get_a(item) |
| if api.preferred[item] |
| item_id = api.preferred[item] |
| else |
| item_list = get("/#{item.pluralize}") |
| item_id = (item_list.xml/"#{item.pluralize}/#{item}").to_a.choice[:id] |
| end |
| end |
| |
| private |
| def process_url_params(path, params) |
| path = "" if path == "/" |
| headers = {} |
| unless params.delete(:noauth) |
| if params[:user] |
| u = params.delete(:user) |
| p = params.delete(:password) |
| headers['Authorization'] = api.basic_auth(u, p) |
| else |
| headers['Authorization'] = api.basic_auth |
| end |
| end |
| headers["X-Deltacloud-Driver"] = params.delete(:driver) if params[:driver] |
| headers["X-Deltacloud-Provider"] = params.delete(:provider) if params[:provider] |
| if params[:accept] |
| headers["Accept"] = "application/#{params.delete(:accept)}" if params[:accept] |
| else #default to xml |
| headers["Accept"] = "application/xml" |
| end |
| headers[:content_type] = params.delete(:content_type) if params[:content_type] |
| #grab X-Deltacloud-Blobmeta headers for blob metadata: |
| params.inject({}) do |res, (cur_k, cur_v)| |
| headers[cur_k] = params.delete(cur_k) if cur_k =~ /X-Deltacloud-Blobmeta/i |
| res |
| end |
| if path =~ /^https?:/ |
| url = path |
| else |
| url = path.start_with?("/", ";") ? api.url + path : api.url+"/"+ path |
| end |
| url += "?" + params.map { |k,v| "#{k}=#{v}" }.join("&") unless params.empty? |
| if ENV["LOG"] && ENV["LOG"].include?("requests") |
| puts "GET #{url}" |
| headers.each { |k, v| puts "#{k}: #{v}" } |
| end |
| [url, headers] |
| end |
| |
| end |
| |
| module ClassMethods |
| # Only run tests if collection +name+ is supported by current |
| # driver. Use inside a 'describe' block. Tests that are not run because |
| # of a missing collection are marked as skipped |
| def need_collection(name) |
| before :each do |
| unless api.collections.include?(name.to_sym) |
| skip "#{api.driver} doesn't support #{name}" |
| end |
| end |
| end |
| |
| #convenience method for checking if collection :foo is supported: |
| def collection_supported(name) |
| api.collections.include?(name.to_sym) |
| end |
| |
| |
| # Only run tests if collection +collection+ supports feature +name+ in |
| # the current driver. Use inside a 'describe' block. Tests that are not |
| # run because of a missing collection are marked as skipped |
| def need_feature(collection, name) |
| before :each do |
| f = api.features[collection.to_sym] |
| unless f && f.include?(name.to_sym) |
| skip "#{collection} for #{api.driver} doesn't support #{name}" |
| end |
| end |
| end |
| end |
| |
| |
| def self.included(base) |
| base.extend ClassMethods |
| base.extend Global |
| base.send(:include, Global) |
| end |
| |
| end |