aeolus-image 0.0.1 → 0.4.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.
Files changed (63) hide show
  1. data/COPYING +161 -0
  2. data/Rakefile +32 -19
  3. data/lib/aeolus_image.rb +35 -0
  4. data/lib/aeolus_image/active_resource_oauth_client.rb +62 -0
  5. data/lib/aeolus_image/import.rb +44 -0
  6. data/lib/aeolus_image/model/factory/base.rb +93 -0
  7. data/lib/aeolus_image/model/factory/build.rb +23 -0
  8. data/lib/aeolus_image/model/factory/builder.rb +46 -0
  9. data/lib/aeolus_image/model/factory/image.rb +23 -0
  10. data/lib/aeolus_image/model/factory/provider_image.rb +35 -0
  11. data/lib/aeolus_image/model/factory/target_image.rb +45 -0
  12. data/lib/aeolus_image/model/warehouse/icicle.rb +60 -0
  13. data/lib/aeolus_image/model/warehouse/image.rb +135 -0
  14. data/lib/aeolus_image/model/warehouse/image_build.rb +60 -0
  15. data/lib/aeolus_image/model/warehouse/provider_image.rb +36 -0
  16. data/lib/aeolus_image/model/warehouse/target_image.rb +53 -0
  17. data/lib/aeolus_image/model/warehouse/template.rb +35 -0
  18. data/lib/aeolus_image/model/warehouse/warehouse_client.rb +201 -0
  19. data/lib/aeolus_image/model/warehouse/warehouse_model.rb +236 -0
  20. data/spec/aeolus_image/model/factory/provider_image_spec.rb +12 -0
  21. data/spec/models/factory/base_spec.rb +94 -0
  22. data/spec/models/factory/builder_spec.rb +31 -0
  23. data/spec/models/factory/provider_image_spec.rb +21 -0
  24. data/spec/models/factory/target_image_spec.rb +21 -0
  25. data/spec/models/warehouse/image_build_spec.rb +189 -0
  26. data/spec/models/warehouse/image_spec.rb +241 -0
  27. data/spec/models/warehouse/provider_image_spec.rb +115 -0
  28. data/spec/models/warehouse/target_image_spec.rb +180 -0
  29. data/spec/models/warehouse/template_spec.rb +76 -0
  30. data/spec/models/warehouse/warehouse_client_spec.rb +445 -0
  31. data/spec/models/warehouse/warehouse_model_spec.rb +94 -0
  32. data/spec/spec_helper.rb +34 -47
  33. data/spec/vcr/cassettes/builder.yml +24 -0
  34. data/spec/vcr/cassettes/oauth.yml +80 -0
  35. data/spec/vcr/cassettes/oauth_fail_invalid.yml +30 -0
  36. data/spec/vcr/cassettes/oauth_fail_no.yml +22 -0
  37. data/spec/vcr/cassettes/oauth_success_valid.yml +30 -0
  38. data/spec/vcr_setup.rb +26 -0
  39. metadata +70 -46
  40. data/bin/aeolus-image +0 -6
  41. data/examples/aeolus-cli +0 -9
  42. data/examples/custom_repo.tdl +0 -18
  43. data/examples/image_description.xml +0 -3
  44. data/examples/tdl.rng +0 -207
  45. data/lib/base_command.rb +0 -134
  46. data/lib/build_command.rb +0 -68
  47. data/lib/config_parser.rb +0 -212
  48. data/lib/delete_command.rb +0 -9
  49. data/lib/import_command.rb +0 -44
  50. data/lib/list_command.rb +0 -141
  51. data/lib/push_command.rb +0 -72
  52. data/man/aeolus-image-build.1 +0 -36
  53. data/man/aeolus-image-import.1 +0 -57
  54. data/man/aeolus-image-list.1 +0 -80
  55. data/man/aeolus-image-push.1 +0 -40
  56. data/man/aeolus-image.1 +0 -16
  57. data/spec/base_command_spec.rb +0 -76
  58. data/spec/build_command_spec.rb +0 -63
  59. data/spec/config_parser_spec.rb +0 -82
  60. data/spec/import_command_spec.rb +0 -43
  61. data/spec/list_command_spec.rb +0 -21
  62. data/spec/push_command_spec.rb +0 -56
  63. data/spec/spec.opts +0 -3
@@ -0,0 +1,53 @@
1
+ # Copyright 2011 Red Hat, Inc.
2
+ #
3
+ # Licensed under the Apache License, Version 2.0 (the "License");
4
+ # you may not use this file except in compliance with the License.
5
+ # You may obtain a copy of the License at
6
+ #
7
+ # http://www.apache.org/licenses/LICENSE-2.0
8
+ #
9
+ # Unless required by applicable law or agreed to in writing, software
10
+ # distributed under the License is distributed on an "AS IS" BASIS,
11
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
+ # See the License for the specific language governing permissions and
13
+ # limitations under the License.
14
+
15
+ module Aeolus
16
+ module Image
17
+ module Warehouse
18
+ class TargetImage < WarehouseModel
19
+ @bucket_name = 'target_images'
20
+
21
+ def build
22
+ ImageBuild.find(@build) if @build
23
+ end
24
+
25
+ def provider_images
26
+ ProviderImage.where("($target_image == \"" + @uuid.to_s + "\")")
27
+ end
28
+
29
+ def target_template
30
+ Template.find(@template) if @template
31
+ end
32
+
33
+ # Deletes this targetimage and all child objects
34
+ def delete!
35
+ begin
36
+ provider_images.each do |pi|
37
+ pi.delete!
38
+ end
39
+ rescue NoMethodError
40
+ end
41
+ TargetImage.delete(@uuid)
42
+ end
43
+
44
+ def find_provider_image_by_provider_and_account(provider, provider_account)
45
+ conditions = ["$target_image == \"#{@uuid}\""]
46
+ conditions << "$provider == \"#{provider}\"" if provider
47
+ conditions << "$provider_account_identifier == \"#{provider_account}\"" if provider_account
48
+ ProviderImage.where('(' + conditions.join(' && ') + ')')
49
+ end
50
+ end
51
+ end
52
+ end
53
+ end
@@ -0,0 +1,35 @@
1
+ # Copyright 2011 Red Hat, Inc.
2
+ #
3
+ # Licensed under the Apache License, Version 2.0 (the "License");
4
+ # you may not use this file except in compliance with the License.
5
+ # You may obtain a copy of the License at
6
+ #
7
+ # http://www.apache.org/licenses/LICENSE-2.0
8
+ #
9
+ # Unless required by applicable law or agreed to in writing, software
10
+ # distributed under the License is distributed on an "AS IS" BASIS,
11
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
+ # See the License for the specific language governing permissions and
13
+ # limitations under the License.
14
+
15
+ module Aeolus
16
+ module Image
17
+ module Warehouse
18
+ class Template < WarehouseModel
19
+ attr_reader :xml_body
20
+
21
+ @bucket_name = 'templates'
22
+
23
+ def initialize(obj)
24
+ super
25
+ @body = obj.body
26
+ @xml_body = Nokogiri::XML @body
27
+ end
28
+
29
+ def name
30
+ @name ||= @xml_body.xpath("/template/name").text
31
+ end
32
+ end
33
+ end
34
+ end
35
+ end
@@ -0,0 +1,201 @@
1
+ # Copyright 2011 Red Hat, Inc.
2
+ #
3
+ # Licensed under the Apache License, Version 2.0 (the "License");
4
+ # you may not use this file except in compliance with the License.
5
+ # You may obtain a copy of the License at
6
+ #
7
+ # http://www.apache.org/licenses/LICENSE-2.0
8
+ #
9
+ # Unless required by applicable law or agreed to in writing, software
10
+ # distributed under the License is distributed on an "AS IS" BASIS,
11
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
+ # See the License for the specific language governing permissions and
13
+ # limitations under the License.
14
+
15
+ require 'rubygems'
16
+
17
+ require 'rest-client'
18
+ require 'nokogiri'
19
+ require 'oauth'
20
+
21
+ #TODO: perform iwhd version-dependent URI mapping
22
+ module Aeolus
23
+ module Image
24
+ module Warehouse
25
+ class BucketObject
26
+ attr_reader :key
27
+
28
+ def initialize(connection, key, bucket)
29
+ @connection = connection
30
+ @key = key
31
+ @bucket = bucket
32
+ @path = "/#{@bucket.name}/#{@key}"
33
+ end
34
+
35
+ def self.create(connection, key, bucket, body, attrs = {})
36
+ obj = new(connection, key, bucket)
37
+ obj.set_body(body)
38
+ obj.set_attrs(attrs)
39
+ obj
40
+ end
41
+
42
+ def body
43
+ @connection.do_request @path, :plain => true
44
+ end
45
+
46
+ def set_body(body)
47
+ @connection.do_request @path, :content => body, :method => :put
48
+ end
49
+
50
+ def attr_list
51
+ result = @connection.do_request @path, :content => 'op=parts', :method => :post
52
+ return result.xpath('/object/object_attr/@name').to_a.map {|item| item.value}
53
+ end
54
+
55
+ def attrs(list)
56
+ attrs = {}
57
+ list.each do |att|
58
+ next if att.match('-')
59
+ attrs[att] = (@connection.do_request("#{@path}/#{att}", :plain => true) rescue nil)
60
+ end
61
+ attrs
62
+ end
63
+
64
+ def attr(name)
65
+ attrs([name])[name]
66
+ end
67
+
68
+ def set_attrs(hash)
69
+ hash.each do |name, content|
70
+ set_attr(name, content)
71
+ end
72
+ end
73
+
74
+ def set_attr(name, content)
75
+ path = "#{@path}/#{name}"
76
+ @connection.do_request path, :content => content, :method => :put
77
+ end
78
+
79
+ def delete!
80
+ @connection.do_request @path, :method => :delete
81
+ true
82
+ end
83
+
84
+ end
85
+
86
+ class Bucket
87
+ attr_accessor :name
88
+
89
+ def initialize(name, connection)
90
+ @name = name
91
+ @connection = connection
92
+ end
93
+
94
+ def to_s
95
+ "Bucket: #{@name}"
96
+ end
97
+
98
+ def object_names
99
+ result = @connection.do_request "/#{@name}"
100
+ result.xpath('/objects/object').map do |obj|
101
+ obj.at_xpath('./key/text()').to_s
102
+ end
103
+ end
104
+
105
+ def objects
106
+ object_names.map do |name|
107
+ object(name)
108
+ end
109
+ end
110
+
111
+ def object(key)
112
+ BucketObject.new @connection, key, self
113
+ end
114
+
115
+ def create_object(key, body, attrs)
116
+ BucketObject.create(@connection, key, self, body, attrs)
117
+ end
118
+
119
+ def include?(key)
120
+ object_names.include?(key)
121
+ end
122
+ end
123
+
124
+ class Connection
125
+ attr_accessor :uri
126
+
127
+ def initialize(uri)
128
+ @uri = uri
129
+ if WarehouseModel.use_oauth? && WarehouseModel.iwhd_url && uri.match(WarehouseModel.iwhd_url)
130
+ @consumer = OAuth::Consumer.new(
131
+ WarehouseModel.oauth_consumer_key,
132
+ WarehouseModel.oauth_consumer_secret,
133
+ :site => WarehouseModel.iwhd_url
134
+ )
135
+ @token = OAuth::AccessToken.new(@consumer)
136
+ end
137
+ end
138
+
139
+ def do_request(path = '/', opts={})
140
+ opts[:method] ||= :get
141
+ opts[:content] ||= ''
142
+ opts[:plain] ||= false
143
+ opts[:headers] ||= {}
144
+
145
+ # Using restclient seems to break OAuth POSTs, so use the oauth gem directly if we're using OAuth:
146
+ if @token
147
+ # TODO - I'm not sure how to pass :headers through here, but we don't actually use them anywhere
148
+ response = @token.request(opts[:method], (@uri + path).to_s, opts[:content].to_s)
149
+ # Errors don't cause exceptions like they do with RestClient, so detect and raise them:
150
+ if response.is_a?(Net::HTTPSuccess)
151
+ result = response.body
152
+ # Translate a few common errors to their RestClient counterparts that we're used to checking for:
153
+ elsif response.is_a?(Net::HTTPNotFound)
154
+ raise RestClient::ResourceNotFound
155
+ elsif response.is_a?(Net::HTTPInternalServerError)
156
+ raise RestClient::InternalServerError
157
+ # Otherwise, raise the error:
158
+ else
159
+ response.error!
160
+ end
161
+ else # no @token -- use RestClient
162
+ result = RestClient::Request.execute(:method => opts[:method], :url => @uri + path, :payload => opts[:content], :headers => opts[:headers])
163
+ end
164
+ return opts[:plain] ? result : Nokogiri::XML(result)
165
+ end
166
+ end
167
+
168
+ class Client
169
+
170
+ def initialize(uri)
171
+ @connection = Connection.new(uri)
172
+ end
173
+
174
+ def create_bucket(bucket)
175
+ @connection.do_request("/#{bucket}", :method => :put) rescue RestClient::InternalServerError
176
+ Bucket.new(bucket, @connection)
177
+ end
178
+
179
+ def bucket(bucket)
180
+ Bucket.new bucket, @connection
181
+ end
182
+
183
+ def buckets
184
+ @connection.do_request.xpath('/api/link[@rel="bucket"]').map do |obj|
185
+ obj.at_xpath('./@href').to_s.gsub(/.*\//, '')
186
+ end
187
+ end
188
+
189
+ def get_iwhd_version
190
+ result = @connection.do_request.at_xpath('/api[@service="image_warehouse"]/@version')
191
+ raise "Response does not contain <api> tag or version information" if result == nil
192
+ return result.value
193
+ end
194
+
195
+ def query(bucket_name, query_string)
196
+ @connection.do_request "/#{bucket_name}/_query", {:method => :post, :content => query_string}
197
+ end
198
+ end
199
+ end
200
+ end
201
+ end
@@ -0,0 +1,236 @@
1
+ # Copyright 2011 Red Hat, Inc.
2
+ #
3
+ # Licensed under the Apache License, Version 2.0 (the "License");
4
+ # you may not use this file except in compliance with the License.
5
+ # You may obtain a copy of the License at
6
+ #
7
+ # http://www.apache.org/licenses/LICENSE-2.0
8
+ #
9
+ # Unless required by applicable law or agreed to in writing, software
10
+ # distributed under the License is distributed on an "AS IS" BASIS,
11
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
+ # See the License for the specific language governing permissions and
13
+ # limitations under the License.
14
+
15
+ module Aeolus
16
+ module Image
17
+ module Warehouse
18
+ class BucketObjectNotFound < Exception;end
19
+ class BucketNotFound < Exception;end
20
+
21
+ class WarehouseModel
22
+ attr_writer :body
23
+
24
+ def initialize(obj)
25
+ @obj = obj
26
+ @attrs = obj.attrs(obj.attr_list)
27
+ @attrs.each do |k,v|
28
+ self.class.send(:attr_writer, k.to_sym) unless respond_to?(:"#{k}=")
29
+ self.class.send(:attr_reader, k.to_sym) unless respond_to?(k.to_sym)
30
+ send(:"#{k}=", v)
31
+ end
32
+ end
33
+
34
+ def body
35
+ @obj.body
36
+ end
37
+
38
+ def ==(other_obj)
39
+ # If the objects have different instance variables defined, they're definitely not ==
40
+ return false unless instance_variables.sort == other_obj.instance_variables.sort
41
+ # Otherwise, ensure that they're all the same
42
+ instance_variables.each do |iv|
43
+ next if iv == "@obj" || iv == :@obj
44
+ return false unless other_obj.instance_variable_get(iv) == instance_variable_get(iv)
45
+ return false unless other_obj.body == body
46
+ end
47
+ # They have the same instance variables and values, so they're equal
48
+ true
49
+ end
50
+
51
+ def id
52
+ uuid
53
+ end
54
+
55
+ # Returns the bucket object represending this object
56
+ def bucket_object
57
+ @obj
58
+ end
59
+
60
+ # Set (and immediately update) an attribute on the object
61
+ # TODO: It might be nicer to offer a .save! that iterates over each attribute
62
+ # and calls this, to better match ActiveResource
63
+ def set_attr(key, value)
64
+ bucket_object.set_attr(key, value)
65
+ end
66
+
67
+
68
+ class << self
69
+ attr_accessor :warehouse, :bucket, :bucket_name
70
+
71
+ def set_warehouse_and_bucket
72
+ begin
73
+ @@config ||= load_config
74
+ self.warehouse = Warehouse::Client.new(@@config[:iwhd][:url])
75
+ self.bucket = self.warehouse.bucket(@bucket_name)
76
+ rescue
77
+ raise BucketNotFound
78
+ end
79
+ end
80
+
81
+ def bucket_objects
82
+ self.set_warehouse_and_bucket if self.bucket.nil?
83
+
84
+ begin
85
+ self.bucket.objects
86
+ rescue RestClient::ResourceNotFound
87
+ []
88
+ end
89
+ end
90
+
91
+ def first
92
+ obj = bucket_objects.first
93
+ obj ? self.new(obj) : nil
94
+ end
95
+
96
+ def last
97
+ obj = bucket_objects.last
98
+ obj ? self.new(obj) : nil
99
+ end
100
+
101
+ def all
102
+ bucket_objects.map do |wh_object|
103
+ self.new(wh_object)
104
+ end
105
+ end
106
+
107
+ def find(uuid)
108
+ self.set_warehouse_and_bucket if self.bucket.nil?
109
+ begin
110
+ if self.bucket.include?(uuid)
111
+ self.new(self.bucket.object(uuid))
112
+ else
113
+ nil
114
+ end
115
+ rescue RestClient::ResourceNotFound
116
+ nil
117
+ end
118
+ end
119
+
120
+ def where(query_string)
121
+ begin
122
+ self.set_warehouse_and_bucket if self.bucket.nil?
123
+ self.warehouse.query(@bucket_name, query_string).xpath('/objects/object').map do |obj|
124
+ self.new(self.bucket.object(obj.at_xpath('./key/text()').to_s))
125
+ end
126
+ rescue RestClient::ResourceNotFound
127
+ []
128
+ end
129
+ end
130
+
131
+ def delete(uuid)
132
+ self.set_warehouse_and_bucket if self.bucket.nil?
133
+ begin
134
+ if self.bucket.include?(uuid)
135
+ self.bucket.object(uuid).delete!
136
+ else
137
+ false
138
+ end
139
+ rescue RestClient::ResourceNotFound
140
+ false
141
+ end
142
+ end
143
+
144
+ def config
145
+ defined?(@@config) ? @@config : nil
146
+ end
147
+
148
+ def config=(conf)
149
+ @@config = conf
150
+ end
151
+
152
+ def use_oauth?
153
+ !!oauth_consumer_key && !!oauth_consumer_secret
154
+ end
155
+
156
+ def oauth_consumer_key
157
+ config[:iwhd][:oauth][:consumer_key] rescue nil
158
+ end
159
+
160
+ def oauth_consumer_secret
161
+ config[:iwhd][:oauth][:consumer_secret] rescue nil
162
+ end
163
+
164
+ def iwhd_url
165
+ config[:iwhd][:url]
166
+ end
167
+
168
+ def create!(key, body, attributes)
169
+ self.set_warehouse_and_bucket if self.bucket.nil?
170
+ unless self.warehouse.buckets.include?(self.bucket.name)
171
+ self.bucket = self.warehouse.create_bucket(self.bucket.name)
172
+ end
173
+ obj = self.bucket.create_object(key, body, attributes)
174
+ self.new(obj)
175
+ end
176
+
177
+ protected
178
+
179
+ # Copy over entirely too much code to load the config file
180
+ def load_config
181
+ # TODO - Is this always the case? We should probably have /etc/aeolus-cli or something too?
182
+ # Or allow Rails to override this
183
+ @config_location ||= "~/.aeolus-cli"
184
+ begin
185
+ file_str = read_file(@config_location)
186
+ if is_file?(@config_location) && !file_str.include?(":url")
187
+ lines = File.readlines(File.expand_path(@config_location)).map do |line|
188
+ "#" + line
189
+ end
190
+ File.open(File.expand_path(@config_location), 'w') do |file|
191
+ file.puts lines
192
+ end
193
+ write_file
194
+ end
195
+ write_file unless is_file?(@config_location)
196
+ YAML::load(File.open(File.expand_path(@config_location)))
197
+ rescue Errno::ENOENT
198
+ #TODO: Create a custom exception to wrap CLI Exceptions
199
+ raise "Unable to locate or write configuration file: \"" + @config_location + "\""
200
+ end
201
+ end
202
+
203
+ def write_file
204
+ example = File.read(File.expand_path(File.dirname(__FILE__) + "/../../examples/aeolus-cli"))
205
+ File.open(File.expand_path(@config_location), 'a+') do |f|
206
+ f.write(example)
207
+ end
208
+ end
209
+
210
+ def read_file(path)
211
+ begin
212
+ full_path = File.expand_path(path)
213
+ if is_file?(path)
214
+ File.read(full_path)
215
+ else
216
+ return nil
217
+ end
218
+ rescue
219
+ nil
220
+ end
221
+ end
222
+
223
+ def is_file?(path)
224
+ full_path = File.expand_path(path)
225
+ if File.exist?(full_path) && !File.directory?(full_path)
226
+ return true
227
+ end
228
+ false
229
+ end
230
+
231
+ end
232
+
233
+ end
234
+ end
235
+ end
236
+ end