miasma 0.0.1 → 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/CHANGELOG.md +2 -0
- data/README.md +179 -0
- data/lib/miasma.rb +52 -0
- data/lib/miasma/contrib/aws.rb +390 -0
- data/lib/miasma/contrib/aws/auto_scale.rb +85 -0
- data/lib/miasma/contrib/aws/compute.rb +112 -0
- data/lib/miasma/contrib/aws/load_balancer.rb +185 -0
- data/lib/miasma/contrib/aws/orchestration.rb +338 -0
- data/lib/miasma/contrib/rackspace.rb +164 -0
- data/lib/miasma/contrib/rackspace/auto_scale.rb +84 -0
- data/lib/miasma/contrib/rackspace/compute.rb +104 -0
- data/lib/miasma/contrib/rackspace/load_balancer.rb +117 -0
- data/lib/miasma/contrib/rackspace/orchestration.rb +255 -0
- data/lib/miasma/error.rb +89 -0
- data/lib/miasma/models.rb +14 -0
- data/lib/miasma/models/auto_scale.rb +55 -0
- data/lib/miasma/models/auto_scale/group.rb +64 -0
- data/lib/miasma/models/auto_scale/groups.rb +34 -0
- data/lib/miasma/models/block_storage.rb +0 -0
- data/lib/miasma/models/compute.rb +70 -0
- data/lib/miasma/models/compute/server.rb +71 -0
- data/lib/miasma/models/compute/servers.rb +35 -0
- data/lib/miasma/models/dns.rb +0 -0
- data/lib/miasma/models/load_balancer.rb +55 -0
- data/lib/miasma/models/load_balancer/balancer.rb +72 -0
- data/lib/miasma/models/load_balancer/balancers.rb +34 -0
- data/lib/miasma/models/monitoring.rb +0 -0
- data/lib/miasma/models/orchestration.rb +127 -0
- data/lib/miasma/models/orchestration/event.rb +38 -0
- data/lib/miasma/models/orchestration/events.rb +64 -0
- data/lib/miasma/models/orchestration/resource.rb +79 -0
- data/lib/miasma/models/orchestration/resources.rb +55 -0
- data/lib/miasma/models/orchestration/stack.rb +144 -0
- data/lib/miasma/models/orchestration/stacks.rb +46 -0
- data/lib/miasma/models/queues.rb +0 -0
- data/lib/miasma/models/storage.rb +60 -0
- data/lib/miasma/models/storage/bucket.rb +36 -0
- data/lib/miasma/models/storage/buckets.rb +41 -0
- data/lib/miasma/models/storage/file.rb +45 -0
- data/lib/miasma/models/storage/files.rb +52 -0
- data/lib/miasma/types.rb +13 -0
- data/lib/miasma/types/api.rb +145 -0
- data/lib/miasma/types/collection.rb +116 -0
- data/lib/miasma/types/data.rb +53 -0
- data/lib/miasma/types/model.rb +118 -0
- data/lib/miasma/types/thin_model.rb +76 -0
- data/lib/miasma/utils.rb +12 -0
- data/lib/miasma/utils/animal_strings.rb +29 -0
- data/lib/miasma/utils/immutable.rb +36 -0
- data/lib/miasma/utils/lazy.rb +231 -0
- data/lib/miasma/utils/memoization.rb +55 -0
- data/lib/miasma/utils/smash.rb +149 -0
- data/lib/miasma/version.rb +4 -0
- data/miasma.gemspec +18 -0
- metadata +57 -3
@@ -0,0 +1,36 @@
|
|
1
|
+
require 'miasma'
|
2
|
+
|
3
|
+
module Miasma
|
4
|
+
module Models
|
5
|
+
class Storage
|
6
|
+
# Abstract bucket
|
7
|
+
class Bucket < Types::Model
|
8
|
+
|
9
|
+
attribute :metadata, Hash, :coerce => lambda{|o| o.to_smash}
|
10
|
+
|
11
|
+
# Filter buckets
|
12
|
+
#
|
13
|
+
# @param filter [Hash]
|
14
|
+
# @return [Array<Bucket>]
|
15
|
+
def filter(filter={})
|
16
|
+
end
|
17
|
+
|
18
|
+
# Rename bucket
|
19
|
+
#
|
20
|
+
# @param new_name [String]
|
21
|
+
# @return [self]
|
22
|
+
def rename(new_name)
|
23
|
+
perform_rename(new_name)
|
24
|
+
end
|
25
|
+
|
26
|
+
protected
|
27
|
+
|
28
|
+
def perform_rename(new_name)
|
29
|
+
api.bucket_rename(self, new_name)
|
30
|
+
end
|
31
|
+
|
32
|
+
end
|
33
|
+
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
@@ -0,0 +1,41 @@
|
|
1
|
+
require 'miasma'
|
2
|
+
|
3
|
+
module Miasma
|
4
|
+
module Models
|
5
|
+
# Abstract storage API
|
6
|
+
class Storage
|
7
|
+
|
8
|
+
# Abstract bucket collection
|
9
|
+
class Buckets < Types::Collection
|
10
|
+
|
11
|
+
# Return buckets matching given filter
|
12
|
+
#
|
13
|
+
# @param options [Hash] filter options
|
14
|
+
# @return [Array<Bucket>]
|
15
|
+
# @option options [String] :prefix key prefix
|
16
|
+
def filter(options={})
|
17
|
+
raise NotImplementedError
|
18
|
+
end
|
19
|
+
|
20
|
+
# @return [Bucket] new unsaved instance
|
21
|
+
def build(args={})
|
22
|
+
Bucket.new(api, args.to_smash)
|
23
|
+
end
|
24
|
+
|
25
|
+
# @return [Bucket] collection item class
|
26
|
+
def model
|
27
|
+
Bucket
|
28
|
+
end
|
29
|
+
|
30
|
+
protected
|
31
|
+
|
32
|
+
# @return [Array<Bucket>]
|
33
|
+
def perform_population
|
34
|
+
api.bucket_all
|
35
|
+
end
|
36
|
+
|
37
|
+
end
|
38
|
+
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
@@ -0,0 +1,45 @@
|
|
1
|
+
require 'miasma'
|
2
|
+
require 'stringio'
|
3
|
+
|
4
|
+
module Miasma
|
5
|
+
module Models
|
6
|
+
class Storage
|
7
|
+
|
8
|
+
# Abstract file
|
9
|
+
class File < Types::Collection
|
10
|
+
|
11
|
+
attribute :body, IO, :coerce => lambda{|v| StringIO.new(v.to_s) }
|
12
|
+
attribute :content_type, String
|
13
|
+
attribute :content_disposition, String
|
14
|
+
attribute :etag, String
|
15
|
+
attribute :last_modified, DateTime
|
16
|
+
attribute :size, Integer
|
17
|
+
attribute :metadata, Hash, :coerce => lambda{|o| o.to_smash}
|
18
|
+
|
19
|
+
# @return [Bucket] parent bucket
|
20
|
+
attr_reader :bucket
|
21
|
+
|
22
|
+
# Create a new instance
|
23
|
+
#
|
24
|
+
# @param bucket [Bucket]
|
25
|
+
# @param args [Hash]
|
26
|
+
# @return [self]
|
27
|
+
def initialize(bucket, args={})
|
28
|
+
@bucket = bucket
|
29
|
+
super bucket.api, args
|
30
|
+
end
|
31
|
+
|
32
|
+
# Return files matching given filter
|
33
|
+
#
|
34
|
+
# @param options [Hash] filter options
|
35
|
+
# @return [Array<File>]
|
36
|
+
# @option options [String] :prefix key prefix
|
37
|
+
def filter(options={})
|
38
|
+
raise NotImplementedError
|
39
|
+
end
|
40
|
+
|
41
|
+
end
|
42
|
+
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
@@ -0,0 +1,52 @@
|
|
1
|
+
require 'miasma'
|
2
|
+
|
3
|
+
module Miasma
|
4
|
+
module Models
|
5
|
+
class Storage
|
6
|
+
|
7
|
+
# Abstract file collection
|
8
|
+
class Files < Types::Collection
|
9
|
+
|
10
|
+
# @return [Bucket] parent bucket
|
11
|
+
attr_reader :bucket
|
12
|
+
|
13
|
+
# Create new instance
|
14
|
+
#
|
15
|
+
# @param bucket [Bucket] parent bucket
|
16
|
+
# @return [self]
|
17
|
+
def initialize(bucket)
|
18
|
+
@bucket = bucket
|
19
|
+
super bucket.api
|
20
|
+
end
|
21
|
+
|
22
|
+
# Return files matching given filter
|
23
|
+
#
|
24
|
+
# @param options [Hash] filter options
|
25
|
+
# @return [Array<File>]
|
26
|
+
# @option options [String] :prefix key prefix
|
27
|
+
def filter(options={})
|
28
|
+
raise NotImplementedError
|
29
|
+
end
|
30
|
+
|
31
|
+
# @return [File] new unsaved instance
|
32
|
+
def build(args={})
|
33
|
+
File.new(api, args.to_smash)
|
34
|
+
end
|
35
|
+
|
36
|
+
# @return [File] collection item class
|
37
|
+
def model
|
38
|
+
File
|
39
|
+
end
|
40
|
+
|
41
|
+
protected
|
42
|
+
|
43
|
+
# @return [Array<File>]
|
44
|
+
def perform_population
|
45
|
+
api.file_all(bucket)
|
46
|
+
end
|
47
|
+
|
48
|
+
end
|
49
|
+
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
data/lib/miasma/types.rb
ADDED
@@ -0,0 +1,13 @@
|
|
1
|
+
require 'miasma'
|
2
|
+
|
3
|
+
module Miasma
|
4
|
+
module Types
|
5
|
+
|
6
|
+
autoload :Api, 'miasma/types/api'
|
7
|
+
autoload :Collection, 'miasma/types/collection'
|
8
|
+
autoload :Model, 'miasma/types/model'
|
9
|
+
autoload :ThinModel, 'miasma/types/thin_model'
|
10
|
+
autoload :Data, 'miasma/types/data'
|
11
|
+
|
12
|
+
end
|
13
|
+
end
|
@@ -0,0 +1,145 @@
|
|
1
|
+
require 'miasma'
|
2
|
+
|
3
|
+
module Miasma
|
4
|
+
module Types
|
5
|
+
|
6
|
+
# Remote API connection
|
7
|
+
class Api
|
8
|
+
|
9
|
+
include Miasma::Utils::Lazy
|
10
|
+
include Miasma::Utils::Memoization
|
11
|
+
|
12
|
+
# Create new API connection
|
13
|
+
#
|
14
|
+
# @param creds [Smash] credentials
|
15
|
+
# @return [self]
|
16
|
+
def initialize(creds)
|
17
|
+
if(creds.is_a?(Hash))
|
18
|
+
load_data(creds)
|
19
|
+
else
|
20
|
+
raise TypeError.new "Expecting `credentials` to be of type `Hash`. Received: `#{creds.class}`"
|
21
|
+
end
|
22
|
+
connect
|
23
|
+
end
|
24
|
+
|
25
|
+
# @return [Symbol] name of provider
|
26
|
+
def provider
|
27
|
+
Utils.snake(self.class.to_s.split('::').last).to_sym
|
28
|
+
end
|
29
|
+
|
30
|
+
# Connect to the remote API
|
31
|
+
#
|
32
|
+
# @return [self]
|
33
|
+
def connect
|
34
|
+
self
|
35
|
+
end
|
36
|
+
|
37
|
+
# Build new API for specified type using current provider / creds
|
38
|
+
#
|
39
|
+
# @param type [Symbol] api type
|
40
|
+
# @return [Api]
|
41
|
+
def api_for(type)
|
42
|
+
memoize(type) do
|
43
|
+
Miasma.api(
|
44
|
+
Smash.new(
|
45
|
+
:type => type,
|
46
|
+
:provider => provider,
|
47
|
+
:credentials => attributes
|
48
|
+
)
|
49
|
+
)
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
# @return [HTTP]
|
54
|
+
def connection
|
55
|
+
HTTP.with_headers('User-Agent' => "miasma/v#{Miasma::VERSION}")
|
56
|
+
end
|
57
|
+
|
58
|
+
# @return [String] url endpoint
|
59
|
+
def endpoint
|
60
|
+
'http://api.example.com'
|
61
|
+
end
|
62
|
+
|
63
|
+
# Perform request to remote API
|
64
|
+
#
|
65
|
+
# @param args [Hash] options
|
66
|
+
# @option args [String, Symbol] :method HTTP request method
|
67
|
+
# @option args [String] :path request path
|
68
|
+
# @option args [Integer] :expects expected response status code
|
69
|
+
# @return [Smash] {:result => HTTP::Response, :headers => Smash, :body => Object}
|
70
|
+
# @raises [Error::ApiError::RequestError]
|
71
|
+
def request(args)
|
72
|
+
args = args.to_smash
|
73
|
+
http_method = args.fetch(:method, 'get').to_s.downcase.to_sym
|
74
|
+
unless(HTTP::Request::METHODS.include?(http_method))
|
75
|
+
raise ArgumentError.new 'Invalid request method provided!'
|
76
|
+
end
|
77
|
+
request_args = [].tap do |ary|
|
78
|
+
ary.push(
|
79
|
+
File.join(endpoint, args[:path].to_s)
|
80
|
+
)
|
81
|
+
options = {}.tap do |opts|
|
82
|
+
[:form, :params, :json, :body].each do |key|
|
83
|
+
opts[key] = args[key] if args[key]
|
84
|
+
end
|
85
|
+
end
|
86
|
+
ary.push(options) unless options.empty?
|
87
|
+
end
|
88
|
+
if(args[:headers])
|
89
|
+
_connection = connection.with_headers(args[:headers])
|
90
|
+
args.delete(:headers)
|
91
|
+
else
|
92
|
+
_connection = connection
|
93
|
+
end
|
94
|
+
result = make_request(_connection, http_method, request_args)
|
95
|
+
unless(result.code == args.fetch(:expects, 200).to_i)
|
96
|
+
raise Error::ApiError::RequestError.new(result.reason, :response => result)
|
97
|
+
end
|
98
|
+
format_response(result)
|
99
|
+
end
|
100
|
+
|
101
|
+
# Perform request
|
102
|
+
#
|
103
|
+
# @param connection [HTTP]
|
104
|
+
# @param http_method [Symbol]
|
105
|
+
# @param request_args [Array]
|
106
|
+
# @return [HTTP::Response]
|
107
|
+
# @note this is mainly here for concrete APIs to
|
108
|
+
# override if things need to be done prior to
|
109
|
+
# the actual request (like signature generation)
|
110
|
+
def make_request(connection, http_method, request_args)
|
111
|
+
connection.send(http_method, *request_args)
|
112
|
+
end
|
113
|
+
|
114
|
+
# Makes best attempt at formatting response
|
115
|
+
#
|
116
|
+
# @param result [HTTP::Response]
|
117
|
+
# @return [Smash]
|
118
|
+
def format_response(result)
|
119
|
+
extracted_headers = Smash[result.headers.map{|k,v| [Utils.snake(k), v]}]
|
120
|
+
if(extracted_headers[:content_type].include?('application/json'))
|
121
|
+
begin
|
122
|
+
extracted_body = MultiJson.load(result.body.to_s).to_smash
|
123
|
+
rescue MultiJson::ParseError
|
124
|
+
extracted_body = result.body.to_s
|
125
|
+
end
|
126
|
+
elsif(extracted_headers[:content_type].include?('text/xml'))
|
127
|
+
begin
|
128
|
+
extracted_body = MultiXml.parse(result.body.to_s).to_smash
|
129
|
+
rescue MultiXml::ParseError
|
130
|
+
extracted_body = result.body.to_s
|
131
|
+
end
|
132
|
+
else
|
133
|
+
extracted_body = result.body.to_s
|
134
|
+
end
|
135
|
+
Smash.new(
|
136
|
+
:response => result,
|
137
|
+
:headers => extracted_headers,
|
138
|
+
:body => extracted_body
|
139
|
+
)
|
140
|
+
end
|
141
|
+
|
142
|
+
end
|
143
|
+
|
144
|
+
end
|
145
|
+
end
|
@@ -0,0 +1,116 @@
|
|
1
|
+
require 'miasma'
|
2
|
+
|
3
|
+
module Miasma
|
4
|
+
module Types
|
5
|
+
|
6
|
+
# Base collection
|
7
|
+
class Collection
|
8
|
+
|
9
|
+
include Utils::Memoization
|
10
|
+
|
11
|
+
# @return [Miasma::Api] underlying service API
|
12
|
+
attr_reader :api
|
13
|
+
|
14
|
+
def initialize(api)
|
15
|
+
@api = api
|
16
|
+
@collection = nil
|
17
|
+
end
|
18
|
+
|
19
|
+
# @return [Array<Model>]
|
20
|
+
def all
|
21
|
+
memoize(:collection) do
|
22
|
+
perform_population
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
# Reload the collection
|
27
|
+
#
|
28
|
+
# @return [self]
|
29
|
+
def reload
|
30
|
+
clear_memoizations!
|
31
|
+
self
|
32
|
+
end
|
33
|
+
|
34
|
+
# Return model with given name or ID
|
35
|
+
#
|
36
|
+
# @param ident [String, Symbol] model identifier
|
37
|
+
# @return [Model, NilClass]
|
38
|
+
def get(ident)
|
39
|
+
memoize(ident) do
|
40
|
+
perform_get(ident)
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
# Return models matching given filter
|
45
|
+
#
|
46
|
+
# @param args [Hash] filter options
|
47
|
+
# @return [Array<Model>]
|
48
|
+
# @todo need to add helper to deep sort args, convert to string
|
49
|
+
# and hash to use as memoization key
|
50
|
+
def filter(args={})
|
51
|
+
memoize(args.to_s)
|
52
|
+
raise NotImplementedError
|
53
|
+
end
|
54
|
+
|
55
|
+
# Build a new model
|
56
|
+
#
|
57
|
+
# @param args [Hash] creation options
|
58
|
+
# @return [Model]
|
59
|
+
def build(args={})
|
60
|
+
instance = self.model.new(self.api)
|
61
|
+
args.each do |m_name, m_value|
|
62
|
+
m_name = "#{m_name}="
|
63
|
+
instance.send(m_name, m_value)
|
64
|
+
end
|
65
|
+
instance
|
66
|
+
end
|
67
|
+
|
68
|
+
# @return [String] collection of models
|
69
|
+
def to_json(*_)
|
70
|
+
self.all.to_json
|
71
|
+
end
|
72
|
+
|
73
|
+
# Load collection via JSON
|
74
|
+
#
|
75
|
+
# @param json [String]
|
76
|
+
# @return [self]
|
77
|
+
def from_json(json)
|
78
|
+
loaded = MultiJson.load(json)
|
79
|
+
unless(loaded.is_a?(Array))
|
80
|
+
raise TypeError.new "Expecting type `Array` but received `#{loaded.class}`"
|
81
|
+
end
|
82
|
+
unmemoize(:collection)
|
83
|
+
memoize(:collection) do
|
84
|
+
loaded.map do |item|
|
85
|
+
model.from_json(self.api, MultiJson.dump(item))
|
86
|
+
end
|
87
|
+
end
|
88
|
+
self
|
89
|
+
end
|
90
|
+
|
91
|
+
# @return [Miasma::Types::Model] model class within collection
|
92
|
+
def model
|
93
|
+
raise NotImplementedError
|
94
|
+
end
|
95
|
+
|
96
|
+
protected
|
97
|
+
|
98
|
+
# Return model with given name or ID
|
99
|
+
#
|
100
|
+
# @param ident [String, Symbol] model identifier
|
101
|
+
# @return [Model, NilClass]
|
102
|
+
def perform_get(ident)
|
103
|
+
all.detect do |obj|
|
104
|
+
obj.id == ident ||
|
105
|
+
(obj.respond_to?(:name) && obj.name == ident)
|
106
|
+
end
|
107
|
+
end
|
108
|
+
|
109
|
+
# @return [Array<Model>]
|
110
|
+
def perform_population
|
111
|
+
raise NotImplementedError
|
112
|
+
end
|
113
|
+
|
114
|
+
end
|
115
|
+
end
|
116
|
+
end
|