1
0
Fork 0
mirror of https://github.com/fog/fog.git synced 2022-11-09 13:51:43 -05:00

Merge branch 'joyent-analytics' of git://github.com/6fusion/fog into 6fusion-joyent-analytics

Conflicts:
	lib/fog/joyent.rb
This commit is contained in:
Kevin Chan 2014-02-11 16:50:26 +08:00
commit 270fc4c0f4
60 changed files with 1189 additions and 35 deletions

View file

@ -5,6 +5,8 @@ class Joyent < Fog::Bin
case key
when :compute
Fog::Compute::Joyent
when :analytics
Fog::Joyent::Analytics
else
raise ArgumentError, "Unrecognized service: #{key}"
end
@ -16,6 +18,8 @@ class Joyent < Fog::Bin
when :compute
Fog::Logger.warning("Joyent[:compute] is not recommended, use Compute[:joyent] for portability")
Fog::Compute.new(:provider => 'Joyent')
when :analytics
Fog::Joyent::Analytics.new
else
raise ArgumentError, "Unrecognized service: #{key.inspect}"
end

View file

@ -60,6 +60,12 @@ module Fog
end
end
EOS
when :timestamp
class_eval <<-EOS, __FILE__, __LINE__
def #{name}=(new_#{name})
attributes[:#{name}] = Time.at(new_#{name}.to_i)
end
EOS
when :array
class_eval <<-EOS, __FILE__, __LINE__
def #{name}=(new_#{name})

View file

@ -1,2 +1,13 @@
require 'fog/joyent/compute'
require 'fog/joyent/errors'
require 'fog/core'
module Fog
module Joyent
extend Fog::Provider
service(:analytics, 'joyent/analytics', 'Analytics')
service(:compute, 'joyent/compute', 'Compute')
end
end

313
lib/fog/joyent/analytics.rb Normal file
View file

@ -0,0 +1,313 @@
require 'fog/joyent'
require 'fog/joyent/errors'
require 'thread'
module Fog
module Joyent
class Analytics < Fog::Service
requires :joyent_username
recognizes :joyent_password
recognizes :joyent_url
recognizes :joyent_keyname
recognizes :joyent_keyfile
recognizes :joyent_keydata
recognizes :joyent_keyphrase
recognizes :joyent_version
request_path 'fog/joyent/requests/analytics'
request :describe_analytics
request :list_instrumentations
request :get_instrumentation
request :create_instrumentation
request :delete_instrumentation
request :get_instrumentation_value
model_path 'fog/joyent/models/analytics'
collection :joyent_modules
model :joyent_module
collection :metrics
model :metric
collection :fields
model :field
collection :types
model :type
collection :transformations
model :transformation
collection :instrumentations
model :instrumentation
model :value
class Mock
def self.data
@data ||= Hash.new do |hash, key|
hash[key] =case key
when :instrumentation
{ 'module' => "cpu",
'stat' => "usage",
'predicate' => {},
'decomposition' => ["zonename"],
'value-dimension' => 2,
'value-arity' => "discrete-decomposition",
'enabled' => true,
'retention-time' => 86400,
'idle-max' => 86400,
'transformations' => [],
'nsources' => 3,
'granularity' => 30,
'persist-data' => false,
'crtime' => 1388690982000,
'value-scope' => "point",
'id' => "63",
'uris' =>
[{ "uri" => "/#{@joyent_username}/analytics/instrumentations/63/value/raw",
"name" => "value_raw" }] }
when :values
{
'value' => { 'zoneid' => 0 },
'transformations' => {},
'start_time' => Time.now.utc - 600,
'duration' => 30,
'end_time' => Time.now.utc - 570,
'nsources' => 1,
'minreporting' => 1,
'requested_start_time' => Time.now.utc - 600,
'requested_duration' => 30,
'requested_end_time' => Time.now.utc - 570
}
when :describe_analytics
{
'fields' => {
'zonename' => {
'label' => 'zone name',
'type' => 'string'
},
'pid' => {
'label' => 'process identifier',
'type' => 'string'
}
},
'modules' => {
'cpu' => {
'label' => 'CPU'
}
},
'transformations' => {
'geolocate' => {
'label' => 'geolocate IP addresses',
'fields' => ['raddr'] }
},
'metrics' => [{
"module" => "cpu",
"stat" => "thread_executions",
"label" => "thread executions",
"interval" => "interval",
"fields" => ["hostname", "zonename", "runtime"],
"unit" => "operations"
}],
'types' => {
'string' => {
'arity' => "discrete",
'unit' => ""
}
}
}
else
{}
end
end
end
def data
self.class.data
end
def initialize(options = {})
@joyent_username = options[:joyent_username] || Fog.credentials[:joyent_username]
@joyent_password = options[:joyent_password] || Fog.credentials[:joyent_password]
@joyent_url = 'https://us-sw-1.api.joyentcloud.com'
@joyent_version = '~7'
end
def request(opts)
raise "Not Implemented"
end
end # Mock
class Real
def initialize(options = {})
@mutex = Mutex.new
@connection_options = options[:connection_options] || {}
@persistent = options[:persistent] || false
@joyent_url = options[:joyent_url] || 'https://us-sw-1.api.joyentcloud.com'
@joyent_version = options[:joyent_version] || '~7'
@joyent_username = options[:joyent_username]
unless @joyent_username
raise ArgumentError, "options[:joyent_username] required"
end
if options[:joyent_keyname]
@joyent_keyname = options[:joyent_keyname]
@joyent_keyphrase = options[:joyent_keyphrase]
@key_manager = Net::SSH::Authentication::KeyManager.new(nil, {
:keys_only => true,
:passphrase => @joyent_keyphrase
})
@header_method = method(:header_for_signature_auth)
if options[:joyent_keyfile]
if File.exists?(options[:joyent_keyfile])
@joyent_keyfile = options[:joyent_keyfile]
@key_manager.add(@joyent_keyfile)
else
raise ArgumentError, "options[:joyent_keyfile] provided does not exist."
end
elsif options[:joyent_keydata]
if options[:joyent_keydata].to_s.empty?
raise ArgumentError, 'options[:joyent_keydata] must not be blank'
else
@joyent_keydata = options[:joyent_keydata]
@key_manager.add_key_data(@joyent_keydata)
end
end
elsif options[:joyent_password]
@joyent_password = options[:joyent_password]
@header_method = method(:header_for_basic_auth)
else
raise ArgumentError, "Must provide either a joyent_password or joyent_keyname and joyent_keyfile pair"
end
@connection = Fog::Connection.new(
@joyent_url,
@persistent,
@connection_options
)
end
def request(opts = {})
opts[:headers] = {
"X-Api-Version" => @joyent_version,
"Content-Type" => "application/json",
"Accept" => "application/json"
}.merge(opts[:headers] || {}).merge(@header_method.call)
if opts[:body]
opts[:body] = Fog::JSON.encode(opts[:body])
end
response = @connection.request(opts)
if response.headers["Content-Type"] == "application/json"
response.body = json_decode(response.body)
end
response
rescue Excon::Errors::HTTPStatusError => e
if e.response.headers["Content-Type"] == "application/json"
e.response.body = json_decode(e.response.body)
end
raise_if_error!(e.request, e.response)
end
private
def json_decode(body)
parsed = Fog::JSON.decode(body)
decode_time_attrs(parsed)
end
def header_for_basic_auth
{
"Authorization" => "Basic #{Base64.encode64("#{@joyent_username}:#{@joyent_password}").delete("\r\n")}"
}
end
def header_for_signature_auth
date = Time.now.utc.httpdate
# Force KeyManager to load the key(s)
@mutex.synchronize do
@key_manager.each_identity {}
end
key = @key_manager.known_identities.keys.first
sig = if key.kind_of? OpenSSL::PKey::RSA
@key_manager.sign(key, date)[15..-1]
else
key = OpenSSL::PKey::DSA.new(File.read(@joyent_keyfile), @joyent_keyphrase)
key.sign('sha1', date)
end
key_id = "/#{@joyent_username}/keys/#{@joyent_keyname}"
key_type = key.class.to_s.split('::').last.downcase.to_sym
unless [:rsa, :dsa].include? key_type
raise Joyent::Errors::Unauthorized.new('Invalid key type -- only rsa or dsa key is supported')
end
signature = Base64.encode64(sig).delete("\r\n")
{
"Date" => date,
"Authorization" => "Signature keyId=\"#{key_id}\",algorithm=\"#{key_type}-sha1\" #{signature}"
}
rescue Net::SSH::Authentication::KeyManagerError => e
raise Joyent::Errors::Unauthorized.new('SSH Signing Error: :#{e.message}', e)
end
def decode_time_attrs(obj)
if obj.kind_of?(Hash)
obj["created"] = Time.parse(obj["created"]) unless obj["created"].nil? or obj["created"] == ''
obj["updated"] = Time.parse(obj["updated"]) unless obj["updated"].nil? or obj["updated"] == ''
elsif obj.kind_of?(Array)
obj.map do |o|
decode_time_attrs(o)
end
end
obj
end
def raise_if_error!(request, response)
case response.status
when 401 then
raise Fog::Compute::Joyent::Errors::Unauthorized.new('Invalid credentials were used', request, response)
when 403 then
raise Fog::Compute::Joyent::Errors::Forbidden.new('No permissions to the specified resource', request, response)
when 404 then
raise Fog::Compute::Joyent::Errors::NotFound.new('Requested resource was not found', request, response)
when 405 then
raise Fog::Compute::Joyent::Errors::MethodNotAllowed.new('Method not supported for the given resource', request, response)
when 406 then
raise Fog::Compute::Joyent::Errors::NotAcceptable.new('Try sending a different Accept header', request, response)
when 409 then
raise Fog::Compute::Joyent::Errors::Conflict.new('Most likely invalid or missing parameters', request, response)
when 414 then
raise Fog::Compute::Joyent::Errors::RequestEntityTooLarge.new('You sent too much data', request, response)
when 415 then
raise Fog::Compute::Joyent::Errors::UnsupportedMediaType.new('You encoded your request in a format we don\'t understand', request, response)
when 420 then
raise Fog::Compute::Joyent::Errors::PolicyNotForfilled.new('You are sending too many requests', request, response)
when 449 then
raise Fog::Compute::Joyent::Errors::RetryWith.new('Invalid API Version requested; try with a different API Version', request, response)
when 503 then
raise Fog::Compute::Joyent::Errors::ServiceUnavailable.new('Either there\'s no capacity in this datacenter, or we\'re in a maintenance window', request, response)
end
end
end # Real
end
end
end

View file

@ -6,6 +6,7 @@ require 'net/ssh'
module Fog
module Compute
class Joyent < Fog::Service
requires :joyent_username
recognizes :joyent_password
@ -13,15 +14,22 @@ module Fog
recognizes :joyent_keyname
recognizes :joyent_keyfile
recognizes :joyent_keydata
recognizes :joyent_keyphrase
recognizes :joyent_version
secrets :joyent_password, :joyent_keydata, :joyent_keyphrase
model_path 'fog/joyent/models/compute'
request_path 'fog/joyent/requests/compute'
request :list_datacenters
# request :get_datacenter
# Datacenters
collection :datacenters
model :datacenter
# Keys
collection :keys
model :key
@ -36,6 +44,8 @@ module Fog
model :image
request :list_datasets
request :get_dataset
request :list_images
request :get_image
# Flavors
collection :flavors
@ -102,6 +112,9 @@ module Fog
end # Mock
class Real
attr_accessor :joyent_version
attr_accessor :joyent_url
def initialize(options = {})
@connection_options = options[:connection_options] || {}
@ -115,24 +128,30 @@ module Fog
raise ArgumentError, "options[:joyent_username] required"
end
if options[:joyent_keyname] && options[:joyent_keyfile]
if File.exists?(options[:joyent_keyfile])
@joyent_keyname = options[:joyent_keyname]
@joyent_keyfile = options[:joyent_keyfile]
@joyent_keyphrase = options[:joyent_keyphrase]
@key_manager = Net::SSH::Authentication::KeyManager.new(nil, {
if options[:joyent_keyname]
@joyent_keyname = options[:joyent_keyname]
@joyent_keyphrase = options[:joyent_keyphrase]
@key_manager = Net::SSH::Authentication::KeyManager.new(nil, {
:keys_only => true,
:passphrase => @joyent_keyphrase
})
})
@header_method = method(:header_for_signature_auth)
@key_manager.add(@joyent_keyfile)
@header_method = method(:header_for_signature_auth)
else
raise ArgumentError, "options[:joyent_keyfile] provided does not exist."
if options[:joyent_keyfile]
if File.exists?(options[:joyent_keyfile])
@joyent_keyfile = options[:joyent_keyfile]
@key_manager.add(@joyent_keyfile)
else
raise ArgumentError, "options[:joyent_keyfile] provided does not exist."
end
elsif options[:joyent_keydata]
if options[:joyent_keydata].to_s.empty?
raise ArgumentError, 'options[:joyent_keydata] must not be blank'
else
@joyent_keydata = options[:joyent_keydata]
@key_manager.add_key_data(@joyent_keydata)
end
end
elsif options[:joyent_password]
@joyent_password = options[:joyent_password]
@header_method = method(:header_for_basic_auth)
@ -166,6 +185,9 @@ module Fog
response
rescue Excon::Errors::HTTPStatusError => e
if e.response.headers["Content-Type"] == "application/json"
e.response.body = json_decode(e.response.body)
end
raise_if_error!(e.request, e.response)
end

View file

@ -0,0 +1,14 @@
require 'fog/core/model'
module Fog
module Joyent
class Analytics
class Field < Fog::Model
attribute :name
attribute :label
attribute :type
end
end
end
end

View file

@ -0,0 +1,26 @@
require 'fog/joyent/models/analytics/field'
module Fog
module Joyent
class Analytics
class Fields < Fog::Collection
model Fog::Joyent::Analytics::Field
def all
data = service.describe_analytics.body['fields']
load(data)
end
# Joyent returns an odd data structure like this:
# { 'apache' => {'label' => 'Apache'}}
# where the key is the name of the module
def new(attributes = {})
name, other_attributes = attributes
super(other_attributes.merge('name' => name))
end
end
end
end
end

View file

@ -0,0 +1,83 @@
require 'fog/core/model'
module Fog
module Joyent
class Analytics
class Instrumentation < Fog::Model
identity :id
attribute :joyent_module, :aliases => 'module'
attribute :stat
attribute :predicate
attribute :decomposition, :type => :array
attribute :value_dimension, :aliases => 'value-dimension', :type => :integer
attribute :value_arity, :aliases => 'value-arity'
attribute :retention_time, :aliases => 'retention-time', :type => :integer
attribute :granularity, :type => :integer
attribute :idle_max, :aliases => 'idle-max', :type => :integer
attribute :transformations, :type => :array
attribute :persist_data, :aliases => 'persist-data', :type => :boolean
attribute :crtime
attribute :value_scope, :aliases => 'value-scope'
attribute :uris, :type => :array
def initialize(attributes={})
self.decomposition = []
self.value_arity = 'scalar'
self.retention_time = 600
self.idle_max = 3600
self.persist_data = false
self.value_scope = 'interval'
super
end
def crtime=(new_crtime)
attributes[:crtime] = Time.at(new_crtime.to_i / 1000)
end
def decomposition=(value)
attributes[:decomposition] = value
self.value_dimension = self.decomposition.size + 1
self.decomposition
end
def save
requires :joyent_module, :stat
munged_attributes = self.attributes.dup
remap_attributes(munged_attributes, {
:joyent_module => 'module',
:value_dimension => 'value-dimension',
:value_arity => 'value-arity',
:retention_time => 'retention-time',
:idle_max => 'idle-max',
:persist_data => 'persist-data',
:value_scope => 'value-scope'
})
data = service.create_instrumentation(munged_attributes)
merge_attributes(data.body)
true
end
def destroy
requires :id
service.delete_instrumentation(self.identity)
true
end
# Get a set of datapoints back for an instrumentation
# use start_time and ndatapoints so we can get back a range of datapoints
# the interval between datapoints should correspond to the granularity of the instrumentation
# @param [Time] start_time
# @param [Integer] ndatapoints
def values(start_time, ndatapoints)
requires :id, :granularity
data = service.get_instrumentation_value(self.uris.find {|uri| uri['name'] == 'value_raw'}['uri'], start_time, ndatapoints, self.granularity).body
data.map do |datum|
Fog::Joyent::Analytics::Value.new(datum)
end
end
end
end
end
end

View file

@ -0,0 +1,26 @@
require 'fog/joyent/models/analytics/instrumentation'
module Fog
module Joyent
class Analytics
class Instrumentations < Fog::Collection
model Fog::Joyent::Analytics::Instrumentation
def all
data = service.list_instrumentations.body
load(data)
end
def get(id)
data = service.get_instrumentation(id).body
new(data)
rescue Fog::Compute::Joyent::Errors::NotFound
nil
end
end
end
end
end

View file

@ -0,0 +1,15 @@
require 'fog/core/model'
# named 'JoyentModule' to avoid name conflicts with ruby's 'Module'
module Fog
module Joyent
class Analytics
class JoyentModule < Fog::Model
attribute :name
attribute :label
end
end
end
end

View file

@ -0,0 +1,26 @@
require 'fog/joyent/models/analytics/joyent_module'
module Fog
module Joyent
class Analytics
class JoyentModules < Fog::Collection
model Fog::Joyent::Analytics::JoyentModule
def all
data = service.describe_analytics.body['modules']
load(data)
end
# Joyent returns an odd data structure like this:
# { 'apache' => {'label' => 'Apache'}}
# where the key is the name of the module
def new(attributes = {})
name, other_attributes = attributes
super(other_attributes.merge('name' => name))
end
end
end
end
end

View file

@ -0,0 +1,18 @@
require 'fog/core/model'
module Fog
module Joyent
class Analytics
class Metric < Fog::Model
attribute :module
attribute :stat
attribute :label
attribute :interval
attribute :fields
attribute :unit
attribute :type
end
end
end
end

View file

@ -0,0 +1,18 @@
require 'fog/joyent/models/analytics/metric'
module Fog
module Joyent
class Analytics
class Metrics < Fog::Collection
model Fog::Joyent::Analytics::Metric
def all
data = service.describe_analytics.body['metrics']
load(data)
end
end
end
end
end

View file

@ -0,0 +1,13 @@
require 'fog/core/model'
module Fog
module Joyent
class Analytics
class Transformation < Fog::Model
attribute :name
attribute :label
attribute :fields
end
end
end
end

View file

@ -0,0 +1,26 @@
require 'fog/joyent/models/analytics/transformation'
module Fog
module Joyent
class Analytics
class Transformations < Fog::Collection
model Fog::Joyent::Analytics::Transformation
def all
data = service.describe_analytics.body['transformations']
load(data)
end
# Joyent returns an odd data structure like this:
# { 'apache' => {'label' => 'Apache'}}
# where the key is the name of the module
def new(attributes = {})
name, other_attributes = attributes
super(other_attributes.merge('name' => name))
end
end
end
end
end

View file

@ -0,0 +1,16 @@
require 'fog/core/model'
module Fog
module Joyent
class Analytics
class Type < Fog::Model
attribute :name
attribute :arity
attribute :unit
attribute :abbr
attribute :base
attribute :power
end
end
end
end

View file

@ -0,0 +1,26 @@
require 'fog/joyent/models/analytics/type'
module Fog
module Joyent
class Analytics
class Types < Fog::Collection
model Fog::Joyent::Analytics::Type
def all
data = service.describe_analytics.body['types']
load(data)
end
# Joyent returns an odd data structure like this:
# { 'apache' => {'label' => 'Apache'}}
# where the key is the name of the module
def new(attributes = {})
name, other_attributes = attributes
super(other_attributes.merge('name' => name))
end
end
end
end
end

View file

@ -0,0 +1,20 @@
require 'fog/core/model'
module Fog
module Joyent
class Analytics
class Value < Fog::Model
attribute :value
attribute :transformations
attribute :start_time, :type => :timestamp
attribute :duration
attribute :end_time, :type => :timestamp
attribute :nsources
attribute :minreporting
attribute :requested_start_time, :type => :timestamp
attribute :requested_duration
attribute :requested_end_time, :type => :timestamp
end
end
end
end

View file

@ -0,0 +1,12 @@
module Fog
module Compute
class Joyent
class Datacenter < Fog::Model
identity :name
attribute :url
end
end
end
end

View file

@ -0,0 +1,25 @@
require 'fog/core/collection'
require 'fog/joyent/models/compute/datacenter'
module Fog
module Compute
class Joyent
class Datacenters < Fog::Collection
model Fog::Compute::Joyent::Datacenter
def all
data = service.list_datacenters().body.map {|k,v| {:name => k, :url => v}}
load(data)
end
def get(id)
all[id]
end
end
end # Joyent
end # Compute
end # Fog

View file

@ -3,13 +3,17 @@ module Fog
class Joyent
class Flavor < Fog::Model
identity :name
identity :id
attribute :name
attribute :memory
attribute :swap
attribute :disk
attribute :default
attribute :vcpus
attribute :default, :type => :boolean
attribute :description
attribute :version
attribute :group
end
end

View file

@ -7,10 +7,20 @@ module Fog
attribute :name
attribute :os
attribute :type
attribute :version
attribute :type
attribute :description
attribute :requirements
attribute :homepage
attribute :published_at, :type => :time
attribute :public, :type => :boolean
attribute :owner
attribute :state
attribute :tags
attribute :eula
attribute :acl
attribute :created, :type => :time
attribute :default
attribute :default, :type => :boolean
end
end

View file

@ -10,11 +10,20 @@ module Fog
model Fog::Compute::Joyent::Image
def all
load(service.list_datasets().body)
# the API call for getting images changed from 6.5 to 7.0. Joyent seems to still support the old url, but no idea for how long
if service.joyent_version.gsub(/[^0-9.]/,'').to_f < 7.0
load(service.list_datasets.body)
else
load(service.list_images.body)
end
end
def get(id)
data = service.get_dataset(id).body
data = if service.joyent_version.gsub(/[^0-9.]/,'').to_f < 7.0
service.get_dataset(id).body
else
service.get_image(id).body
end
new(data)
end

View file

@ -15,6 +15,9 @@ module Fog
attribute :disk
attribute :metadata
attribute :tags
attribute :package
attribute :image
attribute :primary_ip, :aliases => 'primaryIp'
attribute :created, :type => :time
attribute :updated, :type => :time

View file

@ -0,0 +1,25 @@
module Fog
module Joyent
class Analytics
class Real
def create_instrumentation(values = {})
request(
:path => "#{@joyent_username}/analytics/instrumentations",
:method => "POST",
:body => values,
:expects => [200,201]
)
end
end
class Mock
def create_instrumentation(values = {})
response = Excon::Response.new
response.status = 201
response.body = self.data[:instrumentation]
response
end
end
end
end
end

View file

@ -0,0 +1,23 @@
module Fog
module Joyent
class Analytics
class Real
def delete_instrumentation(id)
request(
:path => "#{@joyent_username}/analytics/instrumentations/#{id}",
:method => "DELETE",
:expects => 204
)
end
end
class Mock
def delete_instrumentation(id)
response = Excon::Response.new
response.status = 204
response
end
end
end
end
end

View file

@ -0,0 +1,26 @@
module Fog
module Joyent
class Analytics
class Real
def describe_analytics(force = false)
@describe_analytics = nil if force
@describe_analytics ||= request(
:path => "#{@joyent_username}/analytics",
:method => "GET",
:expects => 200,
:idempotent => true
)
end
end
class Mock
def describe_analytics(force = false)
response = Excon::Response.new
response.status = 200
response.body = self.data[:describe_analytics]
response
end
end
end
end
end

View file

@ -0,0 +1,26 @@
module Fog
module Joyent
class Analytics
class Real
def get_instrumentation(id)
request(
:path => "#{@joyent_username}/analytics/instrumentations/#{id}",
:method => "GET",
:expects => 200,
:idempotent => true
)
end
end
class Mock
def get_instrumentation(id)
raise Fog::Compute::Joyent::Errors::NotFound.new('not found') unless id == self.data[:instrumentation]['id']
response = Excon::Response.new
response.status = 200
response.body = self.data[:instrumentation]
response
end
end
end
end
end

View file

@ -0,0 +1,30 @@
module Fog
module Joyent
class Analytics
class Real
def get_instrumentation_value(url, requested_start_time, ndatapoints, duration)
request(
:path => url,
:method => 'GET',
:expects => 200,
:idempotent => true,
:query => {
:ndatapoints => ndatapoints,
:start_time => requested_start_time.to_i,
:duration => duration
}
)
end
end
class Mock
def get_instrumentation_value(url, requested_start_time, ndatapoints, duration)
response = Excon::Response.new
response.status = 200
response.body = [self.data[:values]]
response
end
end
end
end
end

View file

@ -0,0 +1,25 @@
module Fog
module Joyent
class Analytics
class Real
def list_instrumentations
request(
:path => "#{@joyent_username}/analytics/instrumentations",
:method => "GET",
:expects => 200,
:idempotent => true
)
end
end
class Mock
def list_instrumentations
response = Excon::Response.new
response.status = 200
response.body = [self.data[:instrumentation]]
response
end
end
end
end
end

View file

@ -18,7 +18,8 @@ module Fog
def get_dataset
request(
:method => "GET",
:path => "/my/datasets"
:path => "/my/datasets",
:idempotent => true
)
end
end

View file

@ -0,0 +1,30 @@
module Fog
module Compute
class Joyent
class Mock
def get_image(id)
if ds = self.data[:datasets][id]
res = Excon::Response.new
res.status = 200
res.body = ds
else
raise Excon::Errors::NotFound
end
end
end
class Real
def get_image(id)
request(
:method => "GET",
:path => "/#{@joyent_username}/images/#{id}",
:expects => 200,
:idempotent => true
)
end
end
end
end
end

View file

@ -20,7 +20,8 @@ module Fog
request(
:method => "GET",
:path => "/my/keys/#{keyid}",
:expects => 200
:expects => 200,
:idempotent => true
)
end
end

View file

@ -20,7 +20,8 @@ module Fog
request(
:method => "GET",
:path => "/my/machines/#{uuid}",
:expects => [200, 410]
:expects => [200, 410],
:idempotent => true
)
end
end

View file

@ -15,7 +15,8 @@ module Fog
request(
:path => "/my/machines/#{machine_id}/metadata",
:query => query
:query => query,
:idempotent => true
)
end

View file

@ -5,7 +5,8 @@ module Fog
def get_machine_snapshot(machine_id, snapshot_name)
request(
:path => "/my/machines/#{machine_id}/snapshots/#{snapshot_name}",
:method => "GET"
:method => "GET",
:idempotent => true
)
end
end

View file

@ -9,7 +9,8 @@ module Fog
:path => "/my/machines/#{machine_id}/tags/#{tagname}",
:method => "GET",
:headers => {"Accept" => "text/plain"},
:expects => 200
:expects => 200,
:idempotent => true
)
end

View file

@ -23,7 +23,8 @@ module Fog
request(
:method => "GET",
:path => "/my/packages/#{name}",
:expects => 200
:expects => 200,
:idempotent => true
)
end

View file

@ -7,7 +7,8 @@ module Fog
request(
:expects => 200,
:method => :'GET',
:path => '/my/datacenters'
:path => '/my/datacenters',
:idempotent => true
)
end
end # Real

View file

@ -15,7 +15,8 @@ module Fog
def list_datasets
request(
:method => "GET",
:path => "/my/datasets"
:path => "/my/datasets",
:idempotent => true
)
end
end

View file

@ -0,0 +1,26 @@
module Fog
module Compute
class Joyent
class Mock
def list_images
res = Excon::Response.new
res.status = 200
res.body = self.data[:datasets].values
res
end
end
class Real
def list_images
request(
:method => "GET",
:path => "/#{@joyent_username}/images",
:expects => 200,
:idempotent => true
)
end
end
end
end
end

View file

@ -15,7 +15,8 @@ module Fog
request(
:expects => 200,
:method => :'GET',
:path => '/my/keys'
:path => '/my/keys',
:idempotent => true
)
end
end # Real

View file

@ -5,7 +5,8 @@ module Fog
def list_machine_snapshots(machine_id)
request(
:method => "GET",
:path => "/my/machines/#{machine_id}/snapshots"
:path => "/my/machines/#{machine_id}/snapshots",
:idempotent => true
)
end
end

View file

@ -7,7 +7,8 @@ module Fog
request(
:path => "/my/machines/#{machine_id}/tags",
:method => "GET",
:expects => 200
:expects => 200,
:idempotent => true
)
end
end

View file

@ -17,7 +17,8 @@ module Fog
:path => "/my/machines",
:method => "GET",
:query => options,
:expects => 200
:expects => 200,
:idempotent => true
)
end
end

View file

@ -17,7 +17,8 @@ module Fog
:path => "/my/networks",
:method => "GET",
:query => options,
:expects => 200
:expects => 200,
:idempotent => true
)
end
end

View file

@ -25,7 +25,8 @@ module Fog
request(
:path => "/my/packages",
:method => "GET",
:expects => 200
:expects => 200,
:idempotent => true
)
end
end # Real

View file

@ -0,0 +1,10 @@
Shindo.tests("Fog::Joyent[:analytics] | field", %w{joyent}) do
@analytics = Fog::Joyent[:analytics]
@field = @analytics.fields.first
tests('read only') do
returns(false, 'should not save') { @field.respond_to?(:save)}
returns(false, 'should not allow creating a new one') { @field.respond_to?(:create)}
returns(false, 'should not allow destroying') { @field.respond_to?(:destroy)}
end
end

View file

@ -0,0 +1,13 @@
Shindo.tests("Fog::Joyent[:analytics] | fields", %w{joyent}) do
@analytics = Fog::Joyent[:analytics]
@fields = @analytics.fields
tests('#all').succeeds do
@fields.all
end
tests('#new').succeeds do
@fields.new(['apache', { 'label' => 'Apache', 'type' => 'string' }])
end
end

View file

@ -0,0 +1,13 @@
Shindo.tests("Fog::Joyent[:analytics] | instrumentation", %w{joyent }) do
model_tests(Fog::Joyent[:analytics].instrumentations, {:joyent_module => 'cpu', :stat => 'usage'}, true)
@analytics = Fog::Joyent[:analytics]
@instrumentation = @analytics.instrumentations.first
tests('#values') do
@values = @instrumentation.values(Time.now.utc.to_i - 600, 5)
returns(Array) { @values.class }
returns(Fog::Joyent::Analytics::Value) { @values.first.class }
end
end

View file

@ -0,0 +1,3 @@
Shindo.tests("Fog::Joyent[:analytics] | instrumentations", %w{joyent}) do
collection_tests(Fog::Joyent[:analytics].instrumentations, {:joyent_module => 'cpu', :stat => 'usage'}, true)
end

View file

@ -0,0 +1,10 @@
Shindo.tests("Fog::Joyent[:analytics] | joyent_module", %w{joyent}) do
@analytics = Fog::Joyent[:analytics]
@joyent_module = @analytics.joyent_modules.first
tests('read only') do
returns(false, 'should not save') { @joyent_module.respond_to?(:save)}
returns(false, 'should not allow creating a new one') { @joyent_module.respond_to?(:create)}
returns(false, 'should not allow destroying') { @joyent_module.respond_to?(:destroy)}
end
end

View file

@ -0,0 +1,13 @@
Shindo.tests("Fog::Joyent[:analytics] | joyent_modules", %w{joyent}) do
@analytics = Fog::Joyent[:analytics]
@joyent_modules = @analytics.joyent_modules
tests('#all').succeeds do
@joyent_modules.all
end
tests('#new').succeeds do
@joyent_modules.new(['cpu', { 'label' => 'CPU' }])
end
end

View file

@ -0,0 +1,10 @@
Shindo.tests("Fog::Joyent[:analytics] | metric", %w{joyent}) do
@analytics = Fog::Joyent[:analytics]
@metric = @analytics.metrics.first
tests('read only') do
returns(false, 'should not save') { @metric.respond_to?(:save)}
returns(false, 'should not allow creating a new one') { @metric.respond_to?(:create)}
returns(false, 'should not allow destroying') { @metric.respond_to?(:destroy)}
end
end

View file

@ -0,0 +1,20 @@
Shindo.tests("Fog::Joyent[:analytics] | metrics", %w{joyent}) do
@analytics = Fog::Joyent[:analytics]
@metrics = @analytics.metrics
tests('#all').succeeds do
@metrics.all
end
tests('#new').succeeds do
@metrics.new({
"module" => "cpu",
"stat" => "thread_executions",
"label" => "thread executions",
"interval" => "interval",
"fields" => ["hostname", "zonename", "runtime"],
"unit" => "operations"
})
end
end

View file

@ -0,0 +1,10 @@
Shindo.tests("Fog::Joyent[:analytics] | transformation", %w{joyent}) do
@analytics = Fog::Joyent[:analytics]
@transformation = @analytics.transformations.first
tests('read only') do
returns(false, 'should not save') { @transformation.respond_to?(:save)}
returns(false, 'should not allow creating a new one') { @transformation.respond_to?(:create)}
returns(false, 'should not allow destroying') { @transformation.respond_to?(:destroy)}
end
end

View file

@ -0,0 +1,13 @@
Shindo.tests("Fog::Joyent[:analytics] | transformations", %w{joyent}) do
@analytics = Fog::Joyent[:analytics]
@transformations = @analytics.transformations
tests('#all').succeeds do
@transformations.all
end
tests('#new').succeeds do
@transformations.new(['geolocate', { 'label' => 'geolocate IP addresses', "fields" => ["raddr"] }])
end
end

View file

@ -0,0 +1,10 @@
Shindo.tests("Fog::Joyent[:analytics] | type", %w{joyent}) do
@analytics = Fog::Joyent[:analytics]
@type = @analytics.types.first
tests('read only') do
returns(false, 'should not save') { @type.respond_to?(:save)}
returns(false, 'should not allow creating a new one') { @type.respond_to?(:create)}
returns(false, 'should not allow destroying') { @type.respond_to?(:destroy)}
end
end

View file

@ -0,0 +1,13 @@
Shindo.tests("Fog::Joyent[:analytics] | types", %w{joyent}) do
@analytics = Fog::Joyent[:analytics]
@types = @analytics.types
tests('#all').succeeds do
@types.all
end
tests('#new').succeeds do
@types.new(['string', {'arity' => 'discrete', 'unit' => ''}])
end
end

View file

@ -0,0 +1,44 @@
Shindo.tests("Fog::Joyent[:analytics] | instrumentation requests", %w{joyent}) do
@analytics = Fog::Joyent[:analytics]
@instrumentation_schema = {
'id' => String,
'module' => String,
'stat' => String,
'predicate' => Hash,
'decomposition' => [String],
'value-dimension' => Integer,
'value-arity' => String,
'retention-time' => Integer,
'granularity' => Integer,
'idle-max' => Integer,
'transformations' => [String],
'persist-data' => Fog::Boolean,
'crtime' => Integer,
'value-scope' => String,
'uris' => [
{
'uri' => String,
'name' => String
}
]
}
tests('#create_instrumentation').data_matches_schema(@instrumentation_schema) do
@analytics.create_instrumentation.body
end
tests('#list_instrumentations') do
data_matches_schema(@instrumentation_schema) do
@analytics.list_instrumentations.body.first
end
returns(Array) { @analytics.list_instrumentations.body.class }
end
tests('#delete_instrumentation') do
returns(204) { @analytics.delete_instrumentation(Fog::Joyent::Analytics::Mock.data[:instrumentation]['id']).status }
end
end