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

GH-690 Joyent Cloud Provider

Squashed from fog/joyent
This commit is contained in:
Kevin Chan 2012-02-10 23:10:49 +08:00 committed by geemus
parent 9926b99a6f
commit e403854446
54 changed files with 1464 additions and 1 deletions

177
lib/fog/joyent/compute.rb Normal file
View file

@ -0,0 +1,177 @@
require File.expand_path(File.join(File.dirname(__FILE__), '..', 'brightbox'))
require 'fog/compute'
require 'multi_json'
module Fog
module Compute
class Joyent < Fog::Service
requires :cloudapi_username
recognizes :cloudapi_password
recognizes :cloudapi_url
recognizes :cloudapi_keyname
recognizes :cloudapi_keyfile
model_path 'fog/joyent/models/compute'
request_path 'fog/joyent/requests/compute'
# request :list_datacenters
# request :get_datacenter
# Keys
collection :keys
model :key
request :list_keys
request :get_key
request :create_key
request :delete_key
# Images
collection :images
model :image
request :list_datasets
request :get_dataset
# Flavors
collection :flavors
model :flavor
request :list_packages
request :get_package
# Servers
collection :servers
model :server
request :list_machines
request :get_machine
request :create_machine
request :start_machine
request :stop_machine
request :reboot_machine
request :resize_machine
request :delete_machine
# Snapshots
collection :snapshots
model :snapshot
request :create_machine_snapshot
request :start_machine_from_snapshot
request :list_machine_snapshots
request :get_machine_snapshot
request :delete_machine_snapshot
request :update_machine_metadata
request :get_machine_metadata
request :delete_machine_metadata
request :delete_all_machine_metadata
# MachineTags
request :add_machine_tags
request :list_machine_tags
request :get_machine_tag
request :delete_machine_tag
request :delete_all_machine_tags
class Mock
def self.data
@data ||= Hash.new do |hash, key|
hash[key] = {}
end
end
def data
self.class.data
end
def initialize(options = {})
@cloudapi_username = options[:cloudapi_username] || Fog.credentials[:cloudapi_username]
@cloudapi_password = options[:cloudapi_password] || Fog.credentials[:cloudapi_password]
end
def request(opts)
raise "Not Implemented"
end
end # Mock
class Real
def initialize(options = {})
@connection_options = options[:connection_options] || {}
@persistent = options[:persistent] || false
@cloudapi_url = options[:cloudapi_url] || 'https://us-sw-1.api.joyentcloud.com'
@cloudapi_version = options[:cloudapi_version] || '~6.5'
@cloudapi_username = options[:cloudapi_username]
unless @cloudapi_username
raise ArgumentError, "options[:cloudapi_username] required"
end
if options[:cloudapi_keyname] && options[:cloudapi_keyfile]
if File.exists?(options[:cloudapi_keyfile])
@cloudapi_keyname = options[:cloudapi_keyname]
@cloudapi_key = File.read(options[:cloudapi_keyfile])
@rsa = OpenSSL::PKey::RSA.new(@cloudapi_key)
@header_method = method(:header_for_signature)
else
raise ArgumentError, "options[:cloudapi_keyfile] provided does not exist."
end
elsif options[:cloudapi_password]
@cloudapi_password = options[:cloudapi_password]
@header_method = method(:header_for_basic)
else
raise ArgumentError, "Must provide either a cloudapi_password or cloudapi_keyname and cloudapi_keyfile pair"
end
@connection = Fog::Connection.new(
@cloudapi_url,
@persistent,
@connection_options
)
end
def request(request_options = {})
(request_options[:headers] ||= {}).merge!({
"X-Api-Version" => @cloudapi_version,
"Content-Type" => "application/json",
"Accept" => "application/json"
}).merge!(@header_method.call)
if request_options[:body]
request_options[:body] = MultiJson.encode(request_options[:body])
end
response = @connection.request(request_options)
if response.headers["Content-Type"] == "application/json"
response.body = MultiJson.decode(response.body)
end
response
end
private
def header_for_basic
{
"Authorization" => "Basic #{Base64.encode64("#{@cloudapi_username}:#{@cloudapi_password}").delete("\r\n")}"
}
end
def header_for_signature
date = Time.now.utc.httpdate
signature = Base64.encode64(@rsa.sign("sha256", date)).delete("\r\n")
key_id = "/#{@cloudapi_username}/keys/#{@cloudapi_keyname}"
{
"Date" => date,
"Authorization" => "Signature keyId=\"#{key_id}\",algorithm=\"rsa-sha256\" #{signature}"
}
end
end # Real
end
end
end

View file

@ -0,0 +1,17 @@
module Fog
module Compute
class Joyent
class Flavor < Fog::Model
identity :name
attribute :name
attribute :memory
attribute :swap
attribute :disk
attribute :default
end
end
end
end

View file

@ -0,0 +1,25 @@
require 'fog/core/collection'
require 'fog/joyent/models/compute/flavor'
module Fog
module Compute
class Joyent
class Flavors < Fog::Collection
model Fog::Compute::Joyent::Flavor
def all
load(connection.list_packages().body)
end
def get(id)
data = connection.get_package(id).body
new(data)
end
end
end # Joyent
end # Compute
end # Fog

View file

@ -0,0 +1,18 @@
module Fog
module Compute
class Joyent
class Image < Fog::Model
identity :id
attribute :name
attribute :os
attribute :type
attribute :version
attribute :created, :type => :time
attribute :default
end
end
end
end

View file

@ -0,0 +1,25 @@
require 'fog/core/collection'
require 'fog/joyent/models/compute/image'
module Fog
module Compute
class Joyent
class Images < Fog::Collection
model Fog::Compute::Joyent::Image
def all
load(connection.list_datasets().body)
end
def get(id)
data = connection.get_dataset(id).body
new(data)
end
end # Images
end # Joyent
end # Compute
end # Fog

View file

@ -0,0 +1,19 @@
module Fog
module Compute
class Joyent
class Key < Fog::Model
identity :name
attribute :name
attribute :key
attribute :created, :type => :time
def destroy
requires :name
self.connection.delete_key(name)
end
end
end
end
end

View file

@ -0,0 +1,34 @@
require 'fog/joyent/models/compute/key'
module Fog
module Compute
class Joyent
class Keys < Fog::Collection
model Fog::Compute::Joyent::Key
def all
data = connection.list_keys.body
load(data)
end
def get(keyname)
data = connection.get_key(keyname).body
if data
new(data)
else
nil
end
end
def create(params = {})
raise ArgumentError, "Key name required" unless params.key?(:name)
raise ArgumentError, "Key body required" unless params.key?(:body)
self.connection.create_key(params)
end
end
end
end
end

View file

@ -0,0 +1,117 @@
require 'fog/compute/models/server'
module Fog
module Compute
class Joyent
class Server < Fog::Compute::Server
identity :id
attribute :name
attribute :state
attribute :type
attribute :dataset
attribute :ips
attribute :memory
attribute :ips
attribute :disk
attribute :metadata
attribute :created, :type => :time
attribute :updated, :type => :time
def ready?
self.state == 'running'
end
def stopped?
requires :id
self.state == 'stopped'
end
def destroy
requires :id
self.connection.delete_machine(id)
true
end
def start
requires :id
self.connection.start_machine(id)
self.wait_for { ready? }
true
end
def stop
requires :id
self.connection.stop_machine(id)
self.wait_for { stopped? }
true
end
def resize(flavor)
requires :id
self.connection.resize(id, flavor)
true
end
def reboot
requires :id
self.connection.reboot_machine(id)
true
end
def snapshots
requires :id
self.connection.snapshots.all(id)
end
def update_metadata(data = {})
requires :id
self.connection.update_machine_metadata(self.id, data)
self.reload
true
end
def delete_metadata(keyname)
raise ArgumentError, "Must provide a key name to delete" if keyname.nil? || keyname.empty?
requires :id
self.connection.delete_machine_metadata(self.id, keyname)
true
end
def delete_all_metadata
requires :id
self.connection.delete_all_machine_metadata(self.id)
true
end
def tags
requires :id
self.connection.list_machine_tags(id).body
end
def add_tags(tags_hash = {})
requires :id
self.connection.add_machine_tags(self.id, tags_hash).body
end
def delete_tag(tagname)
requires :id
raise ArgumentError, "Must provide a tag name to delete" if tagname.nil? || tagname.empty?
self.connection.delete_machine_tag(self.id, tagname)
true
end
def delete_all_tags
requires :id
self.connection.delete_all_machine_tags(self.id)
true
end
end
end
end
end

View file

@ -0,0 +1,35 @@
require 'fog/core/collection'
require 'fog/joyent/models/compute/server'
module Fog
module Compute
class Joyent
class Servers < Fog::Collection
model Fog::Compute::Joyent::Server
def all
load(self.connection.list_machines().body)
end
def create(params = {})
data = self.connection.create_machine(params).body
server = new(data)
server.wait_for { ready? }
server
end
def bootstrap
# XXX TOXO
end
def get(machine_id)
data = self.connection.get_machine(machine_id).body
new(data)
end
end
end # Joyent
end # Compute
end # Fog

View file

@ -0,0 +1,45 @@
module Fog
module Compute
class Joyent
class Snapshot < Fog::Model
identity :name
attribute :name
attribute :state
attribute :machine_id
attribute :created, :type => :time
attribute :updated, :type => :time
def reload
requires :name
requires :machine_id
self.connection.snapshots.get(self.machine_id, self.name)
end
def start
requires :name
requires :machine_id
self.connection.start_machine_from_snapshot(self.machine_id, self.name)
true
end
def destroy
requires :name
requires :machine_id
self.connection.delete_machine_snapshot(self.machine_id, self.name)
true
end
def machine
requires :machine_id
self.connection.servers.get(self.machine_id)
end
end
end
end
end

View file

@ -0,0 +1,37 @@
require 'fog/joyent/models/compute/snapshot'
module Fog
module Compute
class Joyent
class Snapshots < Fog::Collection
model Fog::Compute::Joyent::Snapshot
def create(machine_id, snapshot_name)
data = self.connection.create_machine_snapshot(machine_id, snapshot_name).body
data['machine_id'] = machine_id
new(data)
end
def all(machine_id)
data = self.connection.list_machine_snapshots(machine_id).body.map do |m|
m["machine_id"] = machine_id
m
end
load(data)
end
def get(machine_id, snapshot_name)
data = self.connection.get_machine_snapshot(machine_id, snapshot_name).body
if data
data["machine_id"] = machine_id
new(data)
else
nil
end
end
end
end
end
end

View file

@ -0,0 +1,20 @@
module Fog
module Compute
class Joyent
class Real
# https://us-west-1.api.joyentcloud.com/docs#AddMachineTags
def add_machine_tags(machine_id, tags={})
raise ArgumentError, "tags must be a Hash of tags" unless tags.is_a?(Hash)
request(
:path => "/my/machines/#{machine_id}/tags",
:method => "POST",
:body => tags,
:expects => 200
)
end
end
end
end
end

View file

@ -0,0 +1,54 @@
module Fog
module Compute
class Joyent
class Mock
#
# https://us-west-1.api.joyentcloud.com/docs#CreateKey
#
def create_key(params)
name = params[:name]
key = params[:key]
record = {
"name" => name,
"key" => key,
"created" => Time.now.utc
}
self.data[:keys][name] = record
response = Excon::Response.new
response.status = 201
response.body = record
response
end
end # Mock
class Real
# Creates a new SSH Key
# ==== Parameters
# * name<~String> - Name to assign to this key
# * key<~String> - OpenSSH formatted public key
#
# ==== Returns
# * response<~Excon::Response>:
# * body<~Hash>:
# * 'name'<~String> - Name for this key
# * 'key'<~String> - OpenSSH formatted public key
#
def create_key(params={})
raise ArgumentError, "error creating key: [name] is required" unless params[:name]
raise ArgumentError, "error creating key: [key] is required" unless params[:key]
request(
:method => "POST",
:path => "/my/keys",
:body => { "name" => params[:name], "body" => params[:body] },
:expects => 201
)
end
end
end
end
end

View file

@ -0,0 +1,16 @@
module Fog
module Compute
class Joyent
class Real
def create_machine(params = {})
request(
:method => "POST",
:path => "/my/machines",
:body => params,
:expects => [200, 201, 202]
)
end
end
end
end
end

View file

@ -0,0 +1,18 @@
module Fog
module Compute
class Joyent
class Real
def create_machine_snapshot(machine_id, snapshot_name)
request(
:method => "POST",
:path => "/my/machines/#{machine_id}/snapshots",
:body => {"name" => snapshot_name },
:expects => [201]
)
end
end
end
end
end

View file

@ -0,0 +1,16 @@
module Fog
module Compute
class Joyent
class Real
# https://us-west-1.api.joyentcloud.com/docs#DeleteMachineMetadata
def delete_all_machine_metadata(machine_id)
request(
:method => "DELETE",
:path => "/my/machines/#{machine_id}/metadata",
:expects => 204
)
end
end
end
end
end

View file

@ -0,0 +1,15 @@
module Fog
module Compute
class Joyent
class Real
def delete_all_machine_tags(machine_id)
request(
:path => "/my/machines/#{machine_id}/tags",
:method => "DELETE",
:expects => 204
)
end
end
end
end
end

View file

@ -0,0 +1,29 @@
module Fog
module Compute
class Joyent
class Mock
def delete_key(keyname)
if self.data[:keys].delete(keyname)
response = Excon::Response.new
response.status = 200
response
else
raise Excon::Errors::NotFound, "Not Found"
end
end
end
class Real
def delete_key(keyname)
request(
:method => "DELETE",
:path => "/my/keys/#{name}",
:expects => 200
)
end
end # Real
end
end
end

View file

@ -0,0 +1,15 @@
module Fog
module Compute
class Joyent
class Real
def delete_machine(machine_id)
request(
:path => "/my/machines/#{machine_id}",
:method => "DELETE",
:expects => [200, 204]
)
end
end
end
end
end

View file

@ -0,0 +1,18 @@
module Fog
module Compute
class Joyent
class Real
# https://us-west-1.api.joyentcloud.com/docs#DeleteAllMachineMetadata
def delete_machine_metadata(machine_id, key)
request(
:method => "DELETE",
:path => "/my/machines/#{machine_id}/metadata/#{key}",
:expects => [200, 204]
)
end
end
end
end
end

View file

@ -0,0 +1,21 @@
module Fog
module Compute
class Joyent
class Real
def delete_machine_snapshot(machine_id, snapshot)
request(
:method => "DELETE",
:path => "/my/machines/#{machine_id}/snapshots/#{snapshot}",
:expects => [200, 204]
)
end
end
class Mock
end
end
end
end

View file

@ -0,0 +1,15 @@
module Fog
module Compute
class Joyent
class Real
def delete_machine_tag(machine_id, tagname)
request(
:path => "/my/machines/#{machine_id}/tags/#{tagname}",
:method => "DELETE",
:expects => 204
)
end
end
end
end
end

View file

@ -0,0 +1,28 @@
module Fog
module Compute
class Joyent
class Mock
def get_dataset(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_dataset
request(
:method => "GET",
:path => "/my/datasets"
)
end
end
end
end
end

View file

@ -0,0 +1,29 @@
module Fog
module Compute
class Joyent
class Mock
def get_key(keyid)
if key = self.data[:keys][keyid]
response = Excon::Response.new
response.status = 200
response.body = key
response
else
raise Excon::Errors::NotFound
end
end
end
class Real
def get_key(keyid)
request(
:method => "GET",
:path => "/my/keys/#{keyid}",
:expects => 200
)
end
end
end
end
end

View file

@ -0,0 +1,27 @@
module Fog
module Compute
class Joyent
class Mock
def get_machine(uuid)
if machine = self.data[:machines][uuid]
res = Excon::Response.new
res.status = 200
res.body = machine
else
raise Excon::Errors::NotFound, "Not Found"
end
end
end
class Real
def get_machine(uuid)
request(
:method => "GET",
:path => "/my/machines/#{uuid}"
)
end
end
end
end
end

View file

@ -0,0 +1,25 @@
module Fog
module Compute
class Joyent
class Real
def get_machine_metadata(machine_id, options = {})
query = {}
if options[:credentials]
if options[:credentials].is_a?(Boolean)
query[:credentials] = options[:credentials]
else
raise ArgumentError, "options[:credentials] must be a Boolean or nil"
end
end
request(
:path => "/my/machines/#{machine_id}/metadata",
:query => query
)
end
end
end
end
end

View file

@ -0,0 +1,14 @@
module Fog
module Compute
class Joyent
class Real
def get_machine_snapshot(machine_id, snapshot_name)
request(
:path => "/my/machines/#{machine_id}/snapshots/#{snapshot_name}",
:method => "GET"
)
end
end
end
end
end

View file

@ -0,0 +1,19 @@
module Fog
module Compute
class Joyent
class Real
# https://us-west-1.api.joyentcloud.com/docs#GetMachineTag
def get_machine_tag(machine_id, tagname)
request(
:path => "/my/machines/#{machine_id}/tags/#{tagname}",
:method => "GET",
:headers => {"Accept" => "text/plain"},
:expects => 200
)
end
end
end
end
end

View file

@ -0,0 +1,29 @@
module Fog
module Compute
class Joyent
class Mock
def get_package(name)
if pkg = self.data[:packages][name]
response = Excon::Response.new
response.body = pkg
response.status = 200
response
else
raise Excon::Errors::NotFound
end
end
end
class Real
def get_package(name)
request(
:method => "GET",
:path => "/my/packages/#{name}"
)
end
end
end
end
end

View file

@ -0,0 +1,24 @@
module Fog
module Compute
class Joyent
class Mock
def list_datasets
res = Excon::Response.new
res.status = 200
res.body = self.data[:datasets].values
res
end
end
class Real
def list_datasets
request(
:method => "GET",
:path => "/my/datasets"
)
end
end
end
end
end

View file

@ -0,0 +1,25 @@
module Fog
module Compute
class Joyent
class Mock
def list_keys
response = Excon::Response.new
response.status = 200
response.body = self.data[:keys].values
response
end
end
class Real
def list_keys
request(
:expects => 200,
:method => :'GET',
:path => '/my/keys'
)
end
end # Real
end
end
end

View file

@ -0,0 +1,14 @@
module Fog
module Compute
class Joyent
class Real
def list_machine_snapshots(machine_id)
request(
:method => "GET",
:path => "/my/machines/#{machine_id}/snapshots"
)
end
end
end
end
end

View file

@ -0,0 +1,20 @@
module Fog
module Compute
class Joyent
class Real
# https://us-west-1.api.joyentcloud.com/docs#ListMachineTags
def list_machine_tags(machine_id)
request(
:path => "/my/machines/#{machine_id}/tags",
:method => "GET",
:expects => 200
)
end
end
class Mock
end
end
end
end

View file

@ -0,0 +1,25 @@
module Fog
module Compute
class Joyent
class Mock
def list_machines(options={})
res = Excon::Response.new
res.status = 200
res.body = self.data[:machines].values
res
end
end
class Real
def list_machines(options={})
request(
:path => "/my/machines",
:method => "GET",
:query => options
)
end
end
end
end
end

View file

@ -0,0 +1,35 @@
module Fog
module Compute
class Joyent
class Mock
def list_packages
response = Excon::Response.new()
response.status = 200
response.body = self.data[:packages].values
response
end
end
class Real
# Lists all the packages available to the authenticated user
# ==== Returns
# Exon::Response<Array>
# * name<~String> The "friendly name for this package
# * memory<~Number> How much memory will by available (in Mb)
# * disk<~Number> How much disk space will be available (in Gb)
# * swap<~Number> How much swap memory will be available (in Mb)
# * default<~Boolean> Whether this is the default package in this datacenter"
#
def list_packages
request(
:path => "/my/packages",
:method => "GET"
)
end
end # Real
end
end
end

View file

@ -0,0 +1,15 @@
module Fog
module Compute
class Joyent
class Real
def reboot_machine(id)
request(
:method => "POST",
:query => {"action" => "reboot"},
:path => "/my/machines/#{id}"
)
end
end
end
end
end

View file

@ -0,0 +1,13 @@
module Fog
module Compute
class Joyent
def resize_machine(id, package)
request(
:method => "POST",
:path => "/my/machines/#{id}",
:query => {"action" => "resize", "package" => package}
)
end
end
end
end

View file

@ -0,0 +1,16 @@
module Fog
module Compute
class Joyent
class Real
def start_machine(id)
request(
:method => "POST",
:path => "/my/machines/#{id}",
:query => {"action" => "start"},
:expects => 202
)
end
end
end
end
end

View file

@ -0,0 +1,15 @@
module Fog
module Compute
class Joyent
class Real
def start_machine_from_snapshot(machine_id, snapshot_name)
request(
:method => "POST",
:path => "/my/machines/#{machine_id}/snapshots/#{snapshot_name}",
:expects => 202
)
end
end
end
end
end

View file

@ -0,0 +1,16 @@
module Fog
module Compute
class Joyent
class Real
def stop_machine(uuid)
request(
:method => "POST",
:path => "/my/machines/#{uuid}",
:query => {"action" => "stop"},
:expects => 202
)
end
end
end
end
end

View file

@ -0,0 +1,15 @@
module Fog
module Compute
class Joyent
class Real
def update_machine_metadata(machine_id, metadata)
request(
:method => "POST",
:path => "/my/machines/#{machine_id}/metadata",
:body => metadata
)
end
end
end
end
end