foreman_deployments 0.0.1

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 (133) hide show
  1. checksums.yaml +15 -0
  2. data/LICENSE +619 -0
  3. data/README.md +52 -0
  4. data/Rakefile +30 -0
  5. data/app/controllers/foreman_deployments/api/v2/base_controller.rb +25 -0
  6. data/app/controllers/foreman_deployments/api/v2/deployments_controller.rb +102 -0
  7. data/app/controllers/foreman_deployments/api/v2/stacks_controller.rb +52 -0
  8. data/app/controllers/foreman_deployments/create_resources_controller.rb +62 -0
  9. data/app/controllers/foreman_deployments/deployments_controller.rb +5 -0
  10. data/app/controllers/foreman_deployments/stacks_controller.rb +5 -0
  11. data/app/lib/foreman_deployments/base_dereference_visitor.rb +55 -0
  12. data/app/lib/foreman_deployments/config.rb +16 -0
  13. data/app/lib/foreman_deployments/config/array.rb +91 -0
  14. data/app/lib/foreman_deployments/config/configurator.rb +23 -0
  15. data/app/lib/foreman_deployments/config/hash.rb +81 -0
  16. data/app/lib/foreman_deployments/config/load_visitor.rb +23 -0
  17. data/app/lib/foreman_deployments/config/merge_visitor.rb +23 -0
  18. data/app/lib/foreman_deployments/config/save_visitor.rb +23 -0
  19. data/app/lib/foreman_deployments/inputs/base_input_definition.rb +39 -0
  20. data/app/lib/foreman_deployments/inputs/value.rb +25 -0
  21. data/app/lib/foreman_deployments/planner_visitor.rb +26 -0
  22. data/app/lib/foreman_deployments/registry.rb +67 -0
  23. data/app/lib/foreman_deployments/stack_definition.rb +37 -0
  24. data/app/lib/foreman_deployments/stack_parser.rb +121 -0
  25. data/app/lib/foreman_deployments/task_reference.rb +48 -0
  26. data/app/lib/foreman_deployments/tasks/base_action.rb +6 -0
  27. data/app/lib/foreman_deployments/tasks/base_definition.rb +72 -0
  28. data/app/lib/foreman_deployments/tasks/creation_task_definition.rb +68 -0
  29. data/app/lib/foreman_deployments/tasks/host_creation_task_definition.rb +44 -0
  30. data/app/lib/foreman_deployments/tasks/search_task_definition.rb +55 -0
  31. data/app/lib/foreman_deployments/tasks/stack_deploy_action.rb +10 -0
  32. data/app/lib/foreman_deployments/tasks/wait_until_built_task_definition.rb +65 -0
  33. data/app/lib/foreman_deployments/validation/dereference_visitor.rb +18 -0
  34. data/app/lib/foreman_deployments/validation/remove_ids_visitor.rb +59 -0
  35. data/app/lib/foreman_deployments/validation/validation_error.rb +12 -0
  36. data/app/lib/foreman_deployments/validation/validation_result.rb +26 -0
  37. data/app/lib/foreman_deployments/validation/validation_visitor.rb +20 -0
  38. data/app/lib/foreman_deployments/validation/validator.rb +29 -0
  39. data/app/models/foreman_deployments/concerns/belongs_to_single_taxonomy.rb +42 -0
  40. data/app/models/foreman_deployments/concerns/belongs_to_stack_taxonomy.rb +24 -0
  41. data/app/models/foreman_deployments/configuration.rb +26 -0
  42. data/app/models/foreman_deployments/deployment.rb +57 -0
  43. data/app/models/foreman_deployments/resource_models/create_resource.rb +18 -0
  44. data/app/models/foreman_deployments/stack.rb +20 -0
  45. data/app/views/foreman_deployments/api/v2/deployments/base.json.rabl +3 -0
  46. data/app/views/foreman_deployments/api/v2/deployments/create.json.rabl +3 -0
  47. data/app/views/foreman_deployments/api/v2/deployments/index.json.rabl +3 -0
  48. data/app/views/foreman_deployments/api/v2/deployments/main.json.rabl +5 -0
  49. data/app/views/foreman_deployments/api/v2/deployments/merge_configuration.json.rabl +3 -0
  50. data/app/views/foreman_deployments/api/v2/deployments/replace_configuration.json.rabl +3 -0
  51. data/app/views/foreman_deployments/api/v2/deployments/run.json.rabl +3 -0
  52. data/app/views/foreman_deployments/api/v2/deployments/show.json.rabl +11 -0
  53. data/app/views/foreman_deployments/api/v2/deployments/update.json.rabl +3 -0
  54. data/app/views/foreman_deployments/api/v2/stacks/base.json.rabl +3 -0
  55. data/app/views/foreman_deployments/api/v2/stacks/create.json.rabl +3 -0
  56. data/app/views/foreman_deployments/api/v2/stacks/index.json.rabl +3 -0
  57. data/app/views/foreman_deployments/api/v2/stacks/main.json.rabl +3 -0
  58. data/app/views/foreman_deployments/api/v2/stacks/show.json.rabl +7 -0
  59. data/app/views/foreman_deployments/api/v2/stacks/update.json.rabl +3 -0
  60. data/app/views/foreman_deployments/create_resources/new.html.erb +6 -0
  61. data/config/routes.rb +41 -0
  62. data/db/migrate/20150623140612_create_stacks.rb +10 -0
  63. data/db/migrate/20150814092932_create_deployment.rb +20 -0
  64. data/db/migrate/20150916133305_add_task_to_deployments.rb +5 -0
  65. data/db/migrate/20150917130618_add_taxonomy_to_deployments.rb +8 -0
  66. data/db/seeds.d/03-permissions.rb +14 -0
  67. data/doc/deployment_process.md +112 -0
  68. data/doc/design/capsule_stack.puml +51 -0
  69. data/doc/design/complete_stack.puml +17 -0
  70. data/doc/design/config_resource_overview.puml +15 -0
  71. data/doc/design/design.md +230 -0
  72. data/doc/design/diagrams/capsule_stack.png +0 -0
  73. data/doc/design/diagrams/capsule_stack.svg +1 -0
  74. data/doc/design/diagrams/complete_stack.png +0 -0
  75. data/doc/design/diagrams/complete_stack.svg +1 -0
  76. data/doc/design/diagrams/config_resource_overview.png +0 -0
  77. data/doc/design/diagrams/config_resource_overview.svg +1 -0
  78. data/doc/design/diagrams/ordered_resource_overview.png +0 -0
  79. data/doc/design/diagrams/ordered_resource_overview.svg +1 -0
  80. data/doc/design/diagrams/overview.png +0 -0
  81. data/doc/design/diagrams/overview.svg +1 -0
  82. data/doc/design/diagrams/overview_class.png +0 -0
  83. data/doc/design/diagrams/overview_class.svg +1 -0
  84. data/doc/design/diagrams/sat_stack.png +0 -0
  85. data/doc/design/diagrams/sat_stack.svg +1 -0
  86. data/doc/design/diagrams/solr_usecase.png +0 -0
  87. data/doc/design/diagrams/solr_usecase.svg +1 -0
  88. data/doc/design/examples.md +192 -0
  89. data/doc/design/generate-diagrams.sh +7 -0
  90. data/doc/design/implementation.md +128 -0
  91. data/doc/design/ordered_resource_overview.puml +15 -0
  92. data/doc/design/overview.puml +42 -0
  93. data/doc/design/overview_class.puml +64 -0
  94. data/doc/design/resources.md +134 -0
  95. data/doc/design/sat_stack.puml +37 -0
  96. data/doc/design/shared.puml +171 -0
  97. data/doc/design/solr_usecase.puml +189 -0
  98. data/doc/design/tasks.md +74 -0
  99. data/doc/design/user_interfaces.md +189 -0
  100. data/doc/introduction.md +84 -0
  101. data/doc/writing_stacks.md +102 -0
  102. data/lib/foreman_deployments.rb +7 -0
  103. data/lib/foreman_deployments/engine.rb +94 -0
  104. data/lib/foreman_deployments/monkey_patches.rb +9 -0
  105. data/lib/foreman_deployments/version.rb +3 -0
  106. data/lib/tasks/foreman_deployments_tasks.rake +22 -0
  107. data/locale/Makefile +62 -0
  108. data/test/factories/foreman_deployments.rb +70 -0
  109. data/test/functional/api/v2/deployments_controller_test.rb +318 -0
  110. data/test/functional/api/v2/stacks_controller_test.rb +140 -0
  111. data/test/test_plugin_helper.rb +14 -0
  112. data/test/unit/lib/config/array_test.rb +217 -0
  113. data/test/unit/lib/config/configurator_test.rb +66 -0
  114. data/test/unit/lib/config/hash_test.rb +178 -0
  115. data/test/unit/lib/inputs/value_test.rb +47 -0
  116. data/test/unit/lib/registry_test.rb +117 -0
  117. data/test/unit/lib/stack_definition_test.rb +54 -0
  118. data/test/unit/lib/stack_parser_test.rb +129 -0
  119. data/test/unit/lib/task_reference_test.rb +63 -0
  120. data/test/unit/lib/tasks/base_definition_test.rb +137 -0
  121. data/test/unit/lib/tasks/creation_task_definition_test.rb +57 -0
  122. data/test/unit/lib/tasks/host_creation_task_definition_test.rb +10 -0
  123. data/test/unit/lib/tasks/search_task_definition_test.rb +49 -0
  124. data/test/unit/lib/tasks/stack_deploy_action_test.rb +83 -0
  125. data/test/unit/lib/tasks/wait_until_built_task_definition_test.rb +71 -0
  126. data/test/unit/lib/validation/dereference_visitor_test.rb +48 -0
  127. data/test/unit/lib/validation/remove_ids_visitor_test.rb +90 -0
  128. data/test/unit/lib/validation/validation_result_test.rb +40 -0
  129. data/test/unit/lib/validation/validation_visitor_test.rb +67 -0
  130. data/test/unit/model/configuration_test.rb +88 -0
  131. data/test/unit/model/deployment_test.rb +159 -0
  132. data/test/unit/model/stack_test.rb +33 -0
  133. metadata +241 -0
@@ -0,0 +1,12 @@
1
+ module ForemanDeployments
2
+ module Validation
3
+ class ValidationError < ::Foreman::Exception
4
+ attr_accessor :validation_result
5
+
6
+ def initialize(validation_result, message, *params)
7
+ @validation_result = validation_result
8
+ super(message, params)
9
+ end
10
+ end
11
+ end
12
+ end
@@ -0,0 +1,26 @@
1
+ module ForemanDeployments
2
+ module Validation
3
+ class ValidationResult
4
+ attr_accessor :messages
5
+
6
+ def initialize(messages = {})
7
+ fail('messages need to be either hash or array') if !messages.is_a?(Array) && !messages.is_a?(Hash)
8
+ @messages = messages
9
+ end
10
+
11
+ def valid?
12
+ @messages.empty?
13
+ end
14
+
15
+ def to_s
16
+ if @messages.is_a?(Array)
17
+ @messages.join("\n")
18
+ else
19
+ @messages.collect do |key, message|
20
+ "#{key}: #{message}"
21
+ end.join("\n")
22
+ end
23
+ end
24
+ end
25
+ end
26
+ end
@@ -0,0 +1,20 @@
1
+ module ForemanDeployments
2
+ module Validation
3
+ class ValidationVisitor
4
+ def visit(subject)
5
+ validate_task_definition(subject) if subject.is_a? ForemanDeployments::Tasks::BaseDefinition
6
+ end
7
+
8
+ def result
9
+ @result ||= ForemanDeployments::Validation::ValidationResult.new
10
+ end
11
+
12
+ private
13
+
14
+ def validate_task_definition(subject)
15
+ task_result = subject.validate
16
+ result.messages[subject.task_id] = task_result.messages unless task_result.valid?
17
+ end
18
+ end
19
+ end
20
+ end
@@ -0,0 +1,29 @@
1
+ module ForemanDeployments
2
+ module Validation
3
+ class Validator
4
+ def validate(stack_definition)
5
+ cloned = stack_definition.deep_clone
6
+
7
+ cloned.accept(ForemanDeployments::Validation::RemoveIdsVisitor.new)
8
+ cloned.accept(ForemanDeployments::Validation::DereferenceVisitor.new)
9
+ validation_visitor = ForemanDeployments::Validation::ValidationVisitor.new
10
+ cloned.accept(validation_visitor)
11
+
12
+ validation_visitor.result
13
+ end
14
+
15
+ def self.validate(stack_definition)
16
+ Validator.new.validate(stack_definition)
17
+ end
18
+
19
+ def self.validate!(stack_definition)
20
+ result = Validator.new.validate(stack_definition)
21
+ if result.valid?
22
+ result
23
+ else
24
+ fail ForemanDeployments::Validation::ValidationError.new(result, _('Stack definition is invalid'))
25
+ end
26
+ end
27
+ end
28
+ end
29
+ end
@@ -0,0 +1,42 @@
1
+ module ForemanDeployments
2
+ module Concerns
3
+ module BelongsToSingleTaxonomy
4
+ extend ActiveSupport::Concern
5
+
6
+ included do
7
+ validates :organization_id, :presence => true, :if => lambda { SETTINGS[:organizations_enabled] }
8
+ validates :location_id, :presence => true, :if => lambda { SETTINGS[:locations_enabled] }
9
+
10
+ belongs_to :location
11
+ belongs_to :organization
12
+
13
+ if SETTINGS[:locations_enabled]
14
+ scoped_search :in => :location, :on => :title, :rename => :location, :complete_value => true
15
+ scoped_search :on => :location_id, :complete_enabled => false, :only_explicit => true
16
+ end
17
+ if SETTINGS[:organizations_enabled]
18
+ scoped_search :in => :organization, :on => :title, :rename => :organization, :complete_value => true
19
+ scoped_search :on => :organization_id, :complete_enabled => false, :only_explicit => true
20
+ end
21
+
22
+ scope :no_location, lambda { where(:location_id => nil) }
23
+ scope :no_organization, lambda { where(:organization_id => nil) }
24
+
25
+ default_scope do
26
+ where(taxonomy_conditions)
27
+ end
28
+ end
29
+
30
+ module ClassMethods
31
+ def taxonomy_conditions
32
+ org = Organization.expand(Organization.current) if SETTINGS[:organizations_enabled]
33
+ loc = Location.expand(Location.current) if SETTINGS[:locations_enabled]
34
+ conditions = {}
35
+ conditions[:organization_id] = Array(org).map(&:subtree_ids).flatten.uniq if org.present?
36
+ conditions[:location_id] = Array(loc).map(&:subtree_ids).flatten.uniq if loc.present?
37
+ conditions
38
+ end
39
+ end
40
+ end
41
+ end
42
+ end
@@ -0,0 +1,24 @@
1
+ module ForemanDeployments
2
+ module Concerns
3
+ module BelongsToStackTaxonomy
4
+ extend ActiveSupport::Concern
5
+
6
+ included do
7
+ include BelongsToSingleTaxonomy
8
+
9
+ validates :organization_id,
10
+ :inclusion => {
11
+ :in => lambda { |d| d.stack.organizations.map(&:id) },
12
+ :message => _("Deployment's organization must be one of the stack's organizations.")
13
+ },
14
+ :if => lambda { |d| SETTINGS[:organizations_enabled] && !d.stack.nil? }
15
+ validates :location_id,
16
+ :inclusion => {
17
+ :in => lambda { |d| d.stack.locations.map(&:id) },
18
+ :message => _("Deployment's location must be one of the stack's locations.")
19
+ },
20
+ :if => lambda { |d| SETTINGS[:organizations_enabled] && !d.stack.nil? }
21
+ end
22
+ end
23
+ end
24
+ end
@@ -0,0 +1,26 @@
1
+ module ForemanDeployments
2
+ class Configuration < ActiveRecord::Base
3
+ belongs_to :stack, :class_name => 'ForemanDeployments::Stack'
4
+ has_one :deployment, :class_name => 'ForemanDeployments::Deployment'
5
+
6
+ validates :stack, :presence => true
7
+
8
+ serialize :values, Hash
9
+
10
+ def default_description
11
+ if !deployment.nil?
12
+ _('Configuration for %s') % deployment.name
13
+ elsif !stack.nil?
14
+ _('Saved configuration for %s') % stack.name
15
+ end
16
+ end
17
+
18
+ def set_config_for(task, config)
19
+ values[task.task_id] = (config || {})
20
+ end
21
+
22
+ def get_config_for(task)
23
+ values[task.task_id] || {}
24
+ end
25
+ end
26
+ end
@@ -0,0 +1,57 @@
1
+ module ForemanDeployments
2
+ class Deployment < ActiveRecord::Base
3
+ include Authorizable
4
+ include ForemanDeployments::Concerns::BelongsToStackTaxonomy
5
+
6
+ belongs_to :configuration, :class_name => 'ForemanDeployments::Configuration', :autosave => true
7
+ belongs_to :task, :class_name => 'ForemanTasks::Task'
8
+
9
+ validates :name, :presence => true
10
+ validates :configuration, :presence => true
11
+
12
+ scoped_search :on => :id, :complete_value => false
13
+ scoped_search :on => :name, :complete_value => true, :default_order => true
14
+
15
+ def stack
16
+ configuration.stack if configuration
17
+ end
18
+
19
+ def parsed_stack
20
+ @parsed_stack ||= ForemanDeployments::StackParser.parse(stack.definition) unless stack.nil?
21
+ end
22
+
23
+ def configurator
24
+ @configurator ||= ForemanDeployments::Config::Configurator.new(parsed_stack)
25
+ end
26
+
27
+ def run
28
+ fail(Foreman::Exception, _("You can't start a deployment that is already running!")) if status == :running
29
+
30
+ # configure with user input
31
+ configurator.configure(configuration)
32
+
33
+ # validate
34
+ parsed_stack.validate!
35
+
36
+ self.task = ForemanTasks.async_task(Tasks::StackDeployAction, parsed_stack)
37
+ save
38
+ end
39
+
40
+ def status
41
+ # configuration, running, deployed, failed
42
+ if task.nil?
43
+ :configuration
44
+ elsif task.state == 'paused'
45
+ :paused
46
+ elsif task.state == 'stopped'
47
+ if task.result == 'success'
48
+ :deployed
49
+ else
50
+ :failed
51
+ end
52
+ else
53
+ :running
54
+ end
55
+ end
56
+ end
57
+ end
@@ -0,0 +1,18 @@
1
+ module ForemanDeployments
2
+ module ResourceModels
3
+ #
4
+ class CreateResource < ActiveModel::Model
5
+ include ActiveModel::Dirty
6
+ include ActiveModel::Conversion
7
+ extend ActiveModel::Naming
8
+
9
+ attr_accessor :resource_type, :definition_hash, :computed_hash
10
+
11
+ def new_resource
12
+ computed_hash ||= definition_hash
13
+
14
+ resource_type.new computed_hash
15
+ end
16
+ end
17
+ end
18
+ end
@@ -0,0 +1,20 @@
1
+ module ForemanDeployments
2
+ class Stack < ActiveRecord::Base
3
+ include Authorizable
4
+ include Taxonomix
5
+
6
+ has_many :configurations, :class_name => 'ForemanDeployments::Configuration'
7
+
8
+ validates :name, :presence => true, :uniqueness => true
9
+ validates :definition, :presence => true
10
+
11
+ scoped_search :on => :id, :complete_value => false
12
+ scoped_search :on => :name, :complete_value => :true, :default_order => true
13
+
14
+ default_scope do
15
+ with_taxonomy_scope do
16
+ order('stacks.name')
17
+ end
18
+ end
19
+ end
20
+ end
@@ -0,0 +1,3 @@
1
+ object @deployment
2
+
3
+ attributes :name, :id
@@ -0,0 +1,3 @@
1
+ object @deployment
2
+
3
+ extends "foreman_deployments/api/v2/deployments/main"
@@ -0,0 +1,3 @@
1
+ object @deployments
2
+
3
+ extends "foreman_deployments/api/v2/deployments/main"
@@ -0,0 +1,5 @@
1
+ object @deployment
2
+
3
+ extends "foreman_deployments/api/v2/deployments/base"
4
+
5
+ attributes :task_id, :status
@@ -0,0 +1,3 @@
1
+ object @deployment
2
+
3
+ extends "foreman_deployments/api/v2/deployments/main"
@@ -0,0 +1,3 @@
1
+ object @deployment
2
+
3
+ extends "foreman_deployments/api/v2/deployments/main"
@@ -0,0 +1,3 @@
1
+ object @deployment
2
+
3
+ extends "foreman_deployments/api/v2/deployments/main"
@@ -0,0 +1,11 @@
1
+ object @deployment
2
+
3
+ extends "foreman_deployments/api/v2/deployments/main"
4
+
5
+ node :configuration do
6
+ @deployment.parsed_stack.to_hash
7
+ end
8
+
9
+ node :errors do
10
+ @validation_result.messages
11
+ end
@@ -0,0 +1,3 @@
1
+ object @deployment
2
+
3
+ extends "foreman_deployments/api/v2/deployments/main"
@@ -0,0 +1,3 @@
1
+ object @stack
2
+
3
+ attributes :name, :id
@@ -0,0 +1,3 @@
1
+ object @stack
2
+
3
+ extends "foreman_deployments/api/v2/stacks/main"
@@ -0,0 +1,3 @@
1
+ object @stacks
2
+
3
+ extends "foreman_deployments/api/v2/stacks/main"
@@ -0,0 +1,3 @@
1
+ object @stack
2
+
3
+ extends "foreman_deployments/api/v2/stacks/base"
@@ -0,0 +1,7 @@
1
+ object @stack
2
+
3
+ extends "foreman_deployments/api/v2/stacks/main"
4
+
5
+ node :definition do
6
+ @parsed_stack.to_hash
7
+ end
@@ -0,0 +1,3 @@
1
+ object @stack
2
+
3
+ extends "foreman_deployments/api/v2/stacks/main"
@@ -0,0 +1,6 @@
1
+ <% title _("New resource") %>
2
+
3
+ <%= form_for @resource, url: create_resources_path do |f| %>
4
+ <%= submit_or_cancel f, false, :cancel_path => create_resources_path
5
+ %>
6
+ <% end %>
@@ -0,0 +1,41 @@
1
+ Rails.application.routes.draw do
2
+ resources :create_resources, module: 'ForemanDeployments'
3
+
4
+ scope :module => :foreman_deployments do
5
+ resources :deployments, :only => [] do
6
+ get :auto_complete_search, :on => :collection
7
+ end
8
+ resources :stacks, :only => [] do
9
+ get :auto_complete_search, :on => :collection
10
+ end
11
+
12
+ namespace :api do
13
+ scope '(:apiv)',
14
+ :module => :v2,
15
+ :defaults => { :apiv => 'v2' },
16
+ :apiv => /v1|v2/,
17
+ :constraints => ApiConstraints.new(:version => 2) do
18
+ resources :stacks, :only => [:create, :update, :index, :show]
19
+ resources :deployments, :only => [:create, :index, :show] do
20
+ put :configuration, :on => :member, :to => :replace_configuration
21
+ post :configuration, :on => :member, :to => :merge_configuration
22
+ post :run, :on => :member
23
+ end
24
+
25
+ if SETTINGS[:organizations_enabled]
26
+ resources :organizations, :only => [] do
27
+ resources :stacks, :only => [:index]
28
+ resources :deployments, :only => [:index]
29
+ end
30
+ end
31
+
32
+ if SETTINGS[:locations_enabled]
33
+ resources :locations, :only => [] do
34
+ resources :stacks, :only => [:index]
35
+ resources :deployments, :only => [:index]
36
+ end
37
+ end
38
+ end
39
+ end
40
+ end
41
+ end
@@ -0,0 +1,10 @@
1
+ class CreateStacks < ActiveRecord::Migration
2
+ def change
3
+ create_table :stacks do |t|
4
+ t.string :name, :length => 254
5
+ t.text :definition
6
+
7
+ t.timestamps
8
+ end
9
+ end
10
+ end
@@ -0,0 +1,20 @@
1
+ class CreateDeployment < ActiveRecord::Migration
2
+ def change
3
+ create_table :configurations do |t|
4
+ t.string :description, :length => 254
5
+ t.text :values
6
+ t.references :stack, :null => false
7
+
8
+ t.timestamps
9
+ end
10
+ add_foreign_key :configurations, :stacks, :name => :configurations_stack_id_fk
11
+
12
+ create_table :deployments do |t|
13
+ t.string :name, :length => 254
14
+ t.references :configuration, :null => false
15
+
16
+ t.timestamps
17
+ end
18
+ add_foreign_key :deployments, :configurations, :name => :deployemnts_configuration_id_fk
19
+ end
20
+ end