tim 0.1.2 → 0.2.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 (74) hide show
  1. data/Gemfile +17 -0
  2. data/README.rdoc +231 -8
  3. data/app/controllers/tim/application_controller.rb +6 -3
  4. data/app/controllers/tim/base_images_controller.rb +10 -18
  5. data/app/controllers/tim/image_versions_controller.rb +10 -9
  6. data/app/controllers/tim/provider_images_controller.rb +13 -10
  7. data/app/controllers/tim/target_images_controller.rb +9 -7
  8. data/app/controllers/tim/templates_controller.rb +7 -7
  9. data/app/exceptions/tim/exceptions.rb +6 -0
  10. data/app/filters/tim/resource_link_filter.rb +56 -0
  11. data/app/models/tim/base_image.rb +7 -3
  12. data/app/models/tim/image_version.rb +4 -2
  13. data/app/models/tim/provider_image.rb +42 -6
  14. data/app/models/tim/target_image.rb +48 -9
  15. data/app/models/tim/template.rb +18 -3
  16. data/app/patches/rails/active_record/autosave_association.rb +29 -0
  17. data/app/validators/template_validator.rb +9 -5
  18. data/app/views/tim/base_images/_base_image.xml.haml +1 -0
  19. data/app/views/tim/target_images/_target_image.xml.haml +1 -0
  20. data/db/migrate/20121115151914_add_import_to_tim_base_images.rb +5 -0
  21. data/db/migrate/20121210131423_add_build_method_to_target_image.rb +5 -0
  22. data/db/migrate/20121216131814_rename_provider_account_id_attribute.rb +13 -0
  23. data/db/migrate/20121216134538_add_factory_base_image_id_to_image_versions.rb +5 -0
  24. data/lib/image_factory/model/base_image.rb +6 -0
  25. data/lib/tim/engine.rb +12 -0
  26. data/lib/tim/version.rb +1 -1
  27. data/spec/controllers/base_images_controller_spec.rb +24 -8
  28. data/spec/controllers/image_versions_controller_spec.rb +20 -7
  29. data/spec/controllers/provider_images_controller_spec.rb +19 -6
  30. data/spec/controllers/target_images_controller_spec.rb +22 -8
  31. data/spec/factories/tim/base_image.rb +5 -1
  32. data/spec/factories/tim/image_factory/target_image.rb +1 -0
  33. data/spec/factories/tim/image_version.rb +4 -0
  34. data/spec/factories/tim/provider_image.rb +4 -1
  35. data/spec/factories/tim/target_image.rb +6 -2
  36. data/spec/filters/resource_link_filter_spec.rb +158 -0
  37. data/spec/models/base_image_spec.rb +5 -5
  38. data/spec/models/dummy/pool_family_spec.rb +1 -1
  39. data/spec/models/dummy/provider_account_spec.rb +3 -1
  40. data/spec/models/dummy/provider_type_spec.rb +2 -1
  41. data/spec/models/dummy/user_spec.rb +1 -1
  42. data/spec/models/image_version_spec.rb +4 -3
  43. data/spec/models/provider_image_spec.rb +28 -5
  44. data/spec/models/target_image_spec.rb +74 -4
  45. data/spec/models/template_spec.rb +11 -2
  46. data/spec/validators/template_validator_spec.rb +10 -0
  47. data/spec/views/base_images_spec.rb +2 -1
  48. data/spec/views/provider_images_spec.rb +1 -0
  49. data/test/dummy/app/decorators/tim/controllers/base_images_controller_decorator.rb +13 -0
  50. data/test/dummy/config/database.yml +0 -3
  51. data/test/dummy/config/initializers/tim.rb +1 -1
  52. data/test/dummy/config/routes.rb +6 -1
  53. data/test/dummy/db/migrate/20121216133232_add_provider_account_id_to_provider_images.rb +5 -0
  54. data/test/dummy/db/schema.rb +28 -24
  55. data/tim.gemspec +32 -0
  56. metadata +46 -60
  57. data/test/dummy/db/development.sqlite3 +0 -0
  58. data/test/dummy/db/test.sqlite3 +0 -0
  59. data/test/dummy/log/development.log +0 -982
  60. data/test/dummy/log/test.log +0 -8629
  61. data/test/dummy/tmp/cache/assets/C7A/BB0/sprockets%2F13445f7a19078dd2df39517062aa6711 +0 -0
  62. data/test/dummy/tmp/cache/assets/C8C/CC0/sprockets%2F95d79f3b3096348427f3e4e38b5202e3 +0 -0
  63. data/test/dummy/tmp/cache/assets/CB0/2B0/sprockets%2F79106b90879c02a115d7f6f1c8390ac4 +0 -0
  64. data/test/dummy/tmp/cache/assets/CE0/690/sprockets%2F04c628c2a636286bfa92a4966b82b92a +0 -0
  65. data/test/dummy/tmp/cache/assets/D03/040/sprockets%2Fd9e94204d4b307145f12efc109b16c1f +0 -0
  66. data/test/dummy/tmp/cache/assets/D06/DC0/sprockets%2Fcd282851b6e4c463409ba3ece67e0510 +0 -0
  67. data/test/dummy/tmp/cache/assets/D23/5F0/sprockets%2F2a521f3183c6bbcd71bd26a5490b201e +0 -0
  68. data/test/dummy/tmp/cache/assets/D64/3A0/sprockets%2F56ac1aed10c39b12a88cb1b30f668f58 +0 -0
  69. data/test/dummy/tmp/cache/assets/D69/BD0/sprockets%2F0aaf75cf34556b33a9fec534fe4d0415 +0 -0
  70. data/test/dummy/tmp/cache/assets/D87/D80/sprockets%2Fefa3c8b210e87358c7add88cd6f49597 +0 -0
  71. data/test/dummy/tmp/cache/assets/D89/200/sprockets%2Ff9b4e953c874ed6a87de6490d055d9db +0 -0
  72. data/test/dummy/tmp/cache/assets/DF4/430/sprockets%2F403bb1de60cae4325cebd7e6c389b8ad +0 -0
  73. data/test/dummy/tmp/cache/assets/E00/500/sprockets%2Faaddc5b6f2cb6b98930cc54cf4c64a95 +0 -0
  74. data/test/dummy/tmp/cache/assets/EB3/CE0/sprockets%2Fd75a89c9fffacd99bce7eed96844eafc +0 -0
@@ -2,26 +2,28 @@ module Tim
2
2
  class TargetImagesController < Tim::ApplicationController
3
3
  respond_to :json, :only => :update
4
4
 
5
+ prepend_before_filter ResourceLinkFilter.new({ :target_image => :image_version }),
6
+ :only => [:create]
5
7
  before_filter :factory_keys, :only => :update
6
8
 
7
9
  def index
8
10
  @target_images = Tim::TargetImage.all unless defined? @target_images
9
- respond_with @target_images
11
+ respond_with(@target_images, @respond_options)
10
12
  end
11
13
 
12
14
  def show
13
15
  @target_image = Tim::TargetImage.find(params[:id]) unless defined? @target_image
14
- respond_with @target_image
16
+ respond_with(@target_image, @respond_options)
15
17
  end
16
18
 
17
19
  def new
18
20
  @target_image = Tim::TargetImage.new unless defined? @target_image
19
- respond_with @target_image
21
+ respond_with(@target_image, @respond_options)
20
22
  end
21
23
 
22
24
  def edit
23
25
  @target_image = Tim::TargetImage.find(params[:id]) unless defined? @target_image
24
- respond_with @target_image
26
+ respond_with(@target_image, @respond_options)
25
27
  end
26
28
 
27
29
  def create
@@ -29,7 +31,7 @@ module Tim
29
31
  if @target_image.save
30
32
  flash[:notice] = 'Image version was successfully created.'
31
33
  end
32
- respond_with @target_image
34
+ respond_with(@target_image, @respond_options)
33
35
  end
34
36
 
35
37
  def update
@@ -37,7 +39,7 @@ module Tim
37
39
  if @target_image.update_attributes(params[:target_image])
38
40
  flash[:notice] = 'Target image was successfully updated.'
39
41
  end
40
- respond_with @target_image
42
+ respond_with(@target_image, @respond_options)
41
43
  end
42
44
 
43
45
  # DELETE /target_images/1
@@ -45,7 +47,7 @@ module Tim
45
47
  def destroy
46
48
  @target_image = Tim::TargetImage.find(params[:id]) unless defined? @target_image
47
49
  @target_image.destroy
48
- respond_with @target_image
50
+ respond_with(@target_image, @respond_options)
49
51
  end
50
52
 
51
53
  # TODO Add factory permission check
@@ -3,22 +3,22 @@ module Tim
3
3
 
4
4
  def index
5
5
  @templates = Tim::Template.all unless defined? @templates
6
- respond_with @templates
6
+ respond_with(@templates, @respond_options)
7
7
  end
8
8
 
9
9
  def show
10
10
  @template = Tim::Template.find(params[:id]) unless defined? @template
11
- respond_with @template
11
+ respond_with(@template, @respond_options)
12
12
  end
13
13
 
14
14
  def new
15
15
  @template = Tim::Template.new unless defined? @template
16
- respond_with @template
16
+ respond_with(@template, @respond_options)
17
17
  end
18
18
 
19
19
  def edit
20
20
  @template = Tim::Template.find(params[:id])
21
- respond_with @template
21
+ respond_with(@template, @respond_options)
22
22
  end
23
23
 
24
24
  def create
@@ -26,7 +26,7 @@ module Tim
26
26
  if @template.save
27
27
  flash[:notice] = 'Template was successfully created.'
28
28
  end
29
- respond_with @template
29
+ respond_with(@template, @respond_options)
30
30
  end
31
31
 
32
32
  def update
@@ -34,13 +34,13 @@ module Tim
34
34
  if @template.update_attributes(params[:template])
35
35
  flash[:notice] = 'Template was successfully updated.'
36
36
  end
37
- respond_with @template
37
+ respond_with(@template, @respond_options)
38
38
  end
39
39
 
40
40
  def destroy
41
41
  @template = Tim::Template.find(params[:id]) unless defined? @template
42
42
  @template.destroy
43
- respond_with @template
43
+ respond_with(@template, @respond_options)
44
44
  end
45
45
 
46
46
  end
@@ -0,0 +1,6 @@
1
+ module Tim
2
+ module Error
3
+ class ImagefactoryConnectionRefused < ::StandardError
4
+ end
5
+ end
6
+ end
@@ -0,0 +1,56 @@
1
+ # Before filter for controllers that handle REST API requests. Conductor uses
2
+ # linking to resources like <pool id='1'/> (resulting in :pool => { :id => 1 }
3
+ # in request params hash) but ActiveRecord expects linking to resources like
4
+ # <pool_id>1</pool_id> (resluting in :pool_id => 1 in request params hash).
5
+ #
6
+ # This before filter operates on request params hash to rewrite it from our
7
+ # format to ActiveRecord-friendly format.
8
+ #
9
+ # The constructor of the filter accepts specification of what should be
10
+ # transformed. E.g.:
11
+ #
12
+ # before_filter ResourceLinkFilter.new({ :catalog => :pool }),
13
+ # :only => [:create, :update]
14
+ #
15
+ # would transform { :catalog => { :pool => { :id => 1 }}}
16
+ # into { :catalog => { :pool_id => 1 }}
17
+ #
18
+ # See the specs in spec/util/resource_link_filter_spec.rb for more examples.
19
+ #
20
+
21
+ # NOTE This filter was taken from the aeolus conductor project: https://github.com/aeolusproject/conductor/
22
+
23
+ class Tim::ResourceLinkFilter
24
+ def initialize(resource_links)
25
+ @resource_links = resource_links
26
+ end
27
+
28
+ def before(controller)
29
+ return unless controller.request.format == :xml
30
+
31
+ transform_resource_links_recursively(controller.params, @resource_links)
32
+ end
33
+
34
+
35
+ private
36
+
37
+ def transform_resource_links_recursively(subparams, sublinks)
38
+ return if subparams == nil
39
+
40
+ case sublinks
41
+ when Symbol # then transform the link (last level of recursion)
42
+ return if subparams[sublinks] == nil || subparams[sublinks][:id] == nil
43
+
44
+ subparams[:"#{sublinks}_id"] = subparams[sublinks][:id]
45
+ subparams.delete(sublinks)
46
+ when Array # then process each item
47
+ sublinks.each do |item|
48
+ transform_resource_links_recursively(subparams, item)
49
+ end
50
+ when Hash # then descend into each entry
51
+ sublinks.each_key do |key|
52
+ transform_resource_links_recursively(subparams[key], sublinks[key])
53
+ end
54
+ end
55
+ end
56
+ end
@@ -1,13 +1,17 @@
1
1
  module Tim
2
2
  class BaseImage < Tim::Base
3
- has_many :image_versions
4
- belongs_to :template
3
+ has_many :image_versions, :inverse_of => :base_image
4
+ belongs_to :template, :inverse_of => :base_images
5
+
5
6
  belongs_to :user, :class_name => Tim.user_class
6
7
 
7
8
  accepts_nested_attributes_for :template
8
9
  accepts_nested_attributes_for :image_versions
9
10
 
10
- attr_accessible :template, :name, :description
11
+ validates_presence_of :name
12
+ validates_presence_of :template, :unless => :import
13
+
14
+ attr_accessible :template, :name, :description, :import
11
15
  attr_accessible :template_attributes
12
16
  attr_accessible :image_versions_attributes, :as => :admin
13
17
  attr_protected :id
@@ -1,11 +1,13 @@
1
1
  module Tim
2
2
  class ImageVersion < Tim::Base
3
- belongs_to :base_image
4
- has_many :target_images
3
+ belongs_to :base_image, :inverse_of => :image_versions
4
+ has_many :target_images, :inverse_of => :image_version
5
5
 
6
6
  accepts_nested_attributes_for :base_image
7
7
  accepts_nested_attributes_for :target_images
8
8
 
9
+ validates_presence_of :base_image
10
+
9
11
  attr_accessible :base_image_attributes
10
12
  attr_accessible :target_images_versions_attributes
11
13
  attr_protected :id
@@ -1,37 +1,63 @@
1
1
  module Tim
2
2
  class ProviderImage < Tim::Base
3
- belongs_to :target_image
3
+ belongs_to :target_image, :inverse_of => :provider_images
4
4
  belongs_to :provider_account, :class_name => Tim.provider_account_class
5
5
 
6
6
  accepts_nested_attributes_for :target_image
7
7
 
8
8
  attr_accessible :target_image_attributes
9
+ attr_accessible :external_image_id, :if => :imported?
9
10
  attr_accessible :status, :status_detail, :progress #, :as => :image_factory
10
11
  attr_accessible :provider
11
12
  attr_writer :credentials
12
13
  attr_protected :id
13
14
 
14
- after_create :create_factory_provider_image
15
+ validates_presence_of :target_image
16
+ validates_presence_of :external_image_id, :if => :imported?
17
+
18
+ after_create :create_factory_provider_image, :unless => :imported?
19
+ after_create :create_import, :if => :imported?
20
+
21
+ def imported?
22
+ target_image.imported?
23
+ end
15
24
 
16
25
  private
26
+ def factory_provider_credentials
27
+ @credentials
28
+ end
29
+
30
+ def factory_provider
31
+ self.provider
32
+ end
33
+
17
34
  def create_factory_provider_image
18
35
  begin
19
- provider_image = ImageFactory::ProviderImage.new(:target_image_id => self.target_image.factory_id,
20
- :provider => self.provider,
21
- :credentials => @credentials,
36
+ provider_image = ImageFactory::ProviderImage.new(:credentials => factory_provider_credentials,
22
37
  # TODO Remove this when upgrading to 3.2
23
38
  # target conflicts with rails 3.0.10
24
39
  # ActiveRecord::Associations::Association#target
25
40
  :target => target_image.target,
26
41
  :parameters => "")
42
+ provider_image.provider = factory_provider
27
43
  # TODO There is a bug in ARes 3.0.10 that will add map name twice when setting in mass assign. So we set
28
44
  # parameters separately.
29
45
  # Setting parameters at mass assign results in json => {"target_image":"parameters":{"parameters":{"..."}}}"
30
46
  # This should be tested and removed if fixed in 3.2
31
47
  provider_image.parameters = { :callbacks => ["#{ImageFactory::ProviderImage.callback_url}/#{self.id}"] }
48
+ if target_image.snapshot?
49
+ provider_image.parameters[:snapshot] = true
50
+ provider_image.template = self.target_image.template.xml
51
+ else
52
+ provider_image.target_image_id = self.target_image.factory_id
53
+ end
54
+
32
55
  provider_image.save!
33
56
  populate_factory_fields(provider_image)
34
57
  self.save
58
+ rescue Errno::ECONNREFUSED
59
+ raise Tim::Error::ImagefactoryConnectionRefused.new("Unable to connect"\
60
+ " to Imagefactory server @ #{Tim::ImageFactory::Base.site}")
35
61
  rescue => e
36
62
  # TODO Add proper error handling
37
63
  raise e
@@ -45,7 +71,17 @@ module Tim
45
71
  self.progress = factory_provider_image.percent_complete
46
72
  self.provider = factory_provider_image.provider
47
73
  self.external_image_id = factory_provider_image.identifier_on_provider
48
- self.provider_account_id = factory_provider_image.provider_account_identifier
74
+ self.factory_provider_account_id = factory_provider_image.provider_account_identifier
75
+ end
76
+
77
+ # TODO At the moment this method simply sets fields to import defaults.
78
+ # We should investigate whether we can check the image exists and if the
79
+ # user can access it. Deltacloud?
80
+ def create_import
81
+ self.status = "COMPLETE"
82
+ self.progress = "COMPLETE"
83
+ self.status_detail = "Imported Image"
84
+ self.save
49
85
  end
50
86
  end
51
87
  end
@@ -1,12 +1,15 @@
1
1
  module Tim
2
2
  class TargetImage < Tim::Base
3
- belongs_to :image_version
3
+ belongs_to :image_version, :inverse_of => :target_images
4
+ has_many :provider_images, :inverse_of => :target_image
5
+
4
6
  belongs_to :provider_type, :class_name => Tim.provider_type_class
5
- has_many :provider_images
6
7
 
7
8
  accepts_nested_attributes_for :image_version
8
9
  accepts_nested_attributes_for :provider_images
9
10
 
11
+ validates_presence_of :image_version, :target
12
+
10
13
  attr_accessible :image_version_attributes
11
14
  attr_accessible :provider_images_attributes
12
15
  attr_accessible :status, :status_detail, :progress #, :as => :image_factory
@@ -14,25 +17,42 @@ module Tim
14
17
 
15
18
  attr_protected :id
16
19
 
17
- after_create :create_factory_target_image
20
+ after_create :create_factory_target_image, :if => :create_factory_target_image?
21
+ after_create :set_import_snapshot_status, :if => lambda { |t| t.imported? || t.snapshot? }
18
22
 
19
23
  def template
20
24
  image_version.base_image.template
21
25
  end
22
26
 
27
+ def imported?
28
+ image_version.base_image.import
29
+ end
30
+
31
+ def snapshot?
32
+ build_method == "SNAPSHOT"
33
+ end
34
+
23
35
  private
24
36
  def create_factory_target_image
25
37
  begin
26
- target_image = ImageFactory::TargetImage.new(:template => template.xml,
27
- :target => target,
28
- :parameters => nil)
29
- # A bug in ARes adds parameters twice to the resulting json
30
- # when a map is provided in mass assign
38
+ target_image = ImageFactory::TargetImage.new(:target => target,
39
+ :parameters => nil)
40
+ # A bug in ARes adds parameters twice to the resulting json when a map
41
+ # is provided in mass assign
31
42
  target_image.parameters = { :callbacks => ["#{ImageFactory::TargetImage.callback_url}/#{self.id}"] }
43
+
44
+ if image_version.factory_base_image_id
45
+ target_image.base_image_id = image_version.factory_base_image_id
46
+ else
47
+ target_image.template = template.xml
48
+ end
32
49
  target_image.save!
33
50
 
34
51
  populate_factory_fields(target_image)
35
52
  self.save
53
+ rescue Errno::ECONNREFUSED
54
+ raise Tim::Error::ImagefactoryConnectionRefused.new("Unable to connect"\
55
+ " to Imagefactory server @ #{Tim::ImageFactory::Base.site}")
36
56
  rescue => e
37
57
  # TODO Add proper error handling
38
58
  raise e
@@ -44,7 +64,26 @@ module Tim
44
64
  self.factory_id = factory_target_image.id
45
65
  self.status_detail = factory_target_image.status_detail.activity
46
66
  self.progress = factory_target_image.percent_complete
67
+ unless self.image_version.factory_base_image_id
68
+ image_version.factory_base_image_id = factory_target_image.base_image_id
69
+ image_version.save!
70
+ end
71
+ end
72
+
73
+ def set_import_snapshot_status
74
+ self.progress = "COMPLETE"
75
+ self.status = "COMPLETE"
76
+ if self.imported?
77
+ self.status_detail = "Imported Image"
78
+ elsif self.snapshot?
79
+ self.status_detail = "Snapshot Image"
80
+ end
81
+ self.save
47
82
  end
48
83
 
84
+ private
85
+ def create_factory_target_image?
86
+ !imported? && !snapshot?
87
+ end
49
88
  end
50
- end
89
+ end
@@ -5,15 +5,30 @@ module Tim
5
5
  include ActiveModel::Validations
6
6
  validates_with TemplateValidator
7
7
 
8
- has_many :base_images
8
+ has_many :base_images, :inverse_of => :template
9
9
 
10
10
  attr_accessible :xml
11
11
  attr_protected :id
12
12
 
13
+ OS = Struct.new(:name, :version, :arch)
14
+
13
15
  # Used in views to display the xml elements of this template
14
16
  def xml_elements
15
- ::Nokogiri::XML::Document.parse(xml).xpath("//template/*").to_xml
17
+ parsed_xml.xpath("//template/*").to_xml
18
+ end
19
+
20
+ def os
21
+ OS.new(
22
+ parsed_xml.xpath("//template/os/name").text,
23
+ parsed_xml.xpath("//template/os/version").text,
24
+ parsed_xml.xpath("//template/os/arch").text
25
+ )
16
26
  end
17
27
 
28
+ private
29
+
30
+ def parsed_xml
31
+ @parsed_xml ||= ::Nokogiri::XML::Document.parse(xml)
32
+ end
18
33
  end
19
- end
34
+ end
@@ -0,0 +1,29 @@
1
+ # There is a bug in Rails which causes issues when using accepts_nested_attrs
2
+ # and inverse_of on associations: https://github.com/rails/rails/issues/7809/
3
+ # This code was lifted from the pull request of the bug shown above. It solves
4
+ # the issue. Until this is merged into rails we will have to carry the patch
5
+ # ourselves.
6
+
7
+ module ActiveRecord
8
+ module AutosaveAssociation
9
+ extend ActiveSupport::Concern
10
+
11
+ # Returns whether or not this record has been changed in any way (including whether
12
+ # any of its nested autosave associations are likewise changed)
13
+ def changed_for_autosave?
14
+ @_changed_for_autosave_called ||= false
15
+ if @_changed_for_autosave_called
16
+ # traversing a cyclic graph of objects; stop it
17
+ result = false
18
+ else
19
+ begin
20
+ @_changed_for_autosave_called = true
21
+ result = new_record? || changed? || marked_for_destruction? || nested_records_changed_for_autosave?
22
+ ensure
23
+ @_changed_for_autosave_called = false
24
+ end
25
+ end
26
+ result
27
+ end
28
+ end
29
+ end