openshift-origin-controller 1.3.2

Sign up to get free protection for your applications and to get access to all the features.

Potentially problematic release.


This version of openshift-origin-controller might be problematic. Click here for more details.

Files changed (180) hide show
  1. data/COPYRIGHT +1 -0
  2. data/Gemfile +4 -0
  3. data/LICENSE +12 -0
  4. data/README.md +3 -0
  5. data/Rakefile +9 -0
  6. data/app/controllers/app_events_controller.rb +115 -0
  7. data/app/controllers/application_templates_controller.rb +19 -0
  8. data/app/controllers/applications_controller.rb +214 -0
  9. data/app/controllers/base_controller.rb +367 -0
  10. data/app/controllers/cartridges_controller.rb +48 -0
  11. data/app/controllers/descriptors_controller.rb +23 -0
  12. data/app/controllers/dns_resolvable_controller.rb +35 -0
  13. data/app/controllers/domains_controller.rb +156 -0
  14. data/app/controllers/emb_cart_controller.rb +276 -0
  15. data/app/controllers/emb_cart_events_controller.rb +52 -0
  16. data/app/controllers/environment_controller.rb +11 -0
  17. data/app/controllers/estimates_controller.rb +71 -0
  18. data/app/controllers/gear_groups_controller.rb +53 -0
  19. data/app/controllers/gears_controller.rb +70 -0
  20. data/app/controllers/keys_controller.rb +96 -0
  21. data/app/controllers/legacy_broker_controller.rb +510 -0
  22. data/app/controllers/quickstarts_controller.rb +29 -0
  23. data/app/controllers/user_controller.rb +38 -0
  24. data/app/helpers/cartridge_helper.rb +25 -0
  25. data/app/helpers/legacy_broker_helper.rb +21 -0
  26. data/app/helpers/user_action_logger.rb +38 -0
  27. data/app/models/application.rb +1718 -0
  28. data/app/models/application_template.rb +27 -0
  29. data/app/models/cartridge_cache.rb +51 -0
  30. data/app/models/cloud_user.rb +334 -0
  31. data/app/models/component_instance.rb +228 -0
  32. data/app/models/connection_endpoint.rb +10 -0
  33. data/app/models/district.rb +210 -0
  34. data/app/models/domain.rb +234 -0
  35. data/app/models/gear.rb +376 -0
  36. data/app/models/group_instance.rb +306 -0
  37. data/app/models/key.rb +20 -0
  38. data/app/models/legacy_reply.rb +15 -0
  39. data/app/models/legacy_request.rb +126 -0
  40. data/app/models/link.rb +11 -0
  41. data/app/models/message.rb +10 -0
  42. data/app/models/name_server_cache.rb +46 -0
  43. data/app/models/optional_param.rb +12 -0
  44. data/app/models/param.rb +13 -0
  45. data/app/models/remote_job.rb +57 -0
  46. data/app/models/rest_application.rb +126 -0
  47. data/app/models/rest_application10.rb +106 -0
  48. data/app/models/rest_application12.rb +124 -0
  49. data/app/models/rest_application_estimate.rb +12 -0
  50. data/app/models/rest_application_template.rb +20 -0
  51. data/app/models/rest_cartridge10.rb +41 -0
  52. data/app/models/rest_cartridge11.rb +151 -0
  53. data/app/models/rest_domain.rb +43 -0
  54. data/app/models/rest_domain10.rb +42 -0
  55. data/app/models/rest_estimates.rb +16 -0
  56. data/app/models/rest_gear.rb +14 -0
  57. data/app/models/rest_gear_group.rb +26 -0
  58. data/app/models/rest_key.rb +24 -0
  59. data/app/models/rest_reply.rb +31 -0
  60. data/app/models/rest_user.rb +43 -0
  61. data/app/models/result_io.rb +67 -0
  62. data/app/models/usage_record.rb +37 -0
  63. data/app/models/validators/app_validator.rb +30 -0
  64. data/app/models/validators/key_validator.rb +30 -0
  65. data/app/models/validators/namespace_validator.rb +18 -0
  66. data/config/routes.rb +36 -0
  67. data/lib/controller_engine.rb +7 -0
  68. data/lib/openshift-origin-controller.rb +14 -0
  69. data/lib/openshift/application_container_proxy.rb +241 -0
  70. data/lib/openshift/auth_service.rb +101 -0
  71. data/lib/openshift/data_store.rb +33 -0
  72. data/lib/openshift/dns_service.rb +41 -0
  73. data/lib/openshift/mongo_data_store.rb +671 -0
  74. data/openshift-origin-controller.gemspec +42 -0
  75. data/rubygem-openshift-origin-controller.spec +274 -0
  76. data/test/cucumber/application-estimate.feature +25 -0
  77. data/test/cucumber/cartridge-10gen-mms-agent.feature +28 -0
  78. data/test/cucumber/cartridge-cron.feature +32 -0
  79. data/test/cucumber/cartridge-haproxy.feature +31 -0
  80. data/test/cucumber/cartridge-jenkins-build.feature +12 -0
  81. data/test/cucumber/cartridge-jenkins-client.feature +10 -0
  82. data/test/cucumber/cartridge-lifecycle-diy.feature +21 -0
  83. data/test/cucumber/cartridge-lifecycle-jbossas.feature +61 -0
  84. data/test/cucumber/cartridge-lifecycle-jbosseap.feature +61 -0
  85. data/test/cucumber/cartridge-lifecycle-jbossews10.feature +61 -0
  86. data/test/cucumber/cartridge-lifecycle-jenkins.feature +41 -0
  87. data/test/cucumber/cartridge-lifecycle-nodejs.feature +59 -0
  88. data/test/cucumber/cartridge-lifecycle-perl.feature +40 -0
  89. data/test/cucumber/cartridge-lifecycle-php.feature +106 -0
  90. data/test/cucumber/cartridge-lifecycle-python.feature +40 -0
  91. data/test/cucumber/cartridge-lifecycle-ruby18.feature +49 -0
  92. data/test/cucumber/cartridge-lifecycle-ruby19.feature +41 -0
  93. data/test/cucumber/cartridge-mongodb.feature +31 -0
  94. data/test/cucumber/cartridge-mysql.feature +30 -0
  95. data/test/cucumber/cartridge-php.feature +14 -0
  96. data/test/cucumber/cartridge-phpmyadmin.feature +32 -0
  97. data/test/cucumber/cartridge-postgresql.feature +32 -0
  98. data/test/cucumber/cartridge-runtime-extended-db.feature +64 -0
  99. data/test/cucumber/cartridge-runtime-extended-jboss.feature +24 -0
  100. data/test/cucumber/cartridge-runtime-extended-nodejs.feature +21 -0
  101. data/test/cucumber/cartridge-runtime-extended-perl.feature +18 -0
  102. data/test/cucumber/cartridge-runtime-extended-php.feature +19 -0
  103. data/test/cucumber/cartridge-runtime-extended-python.feature +18 -0
  104. data/test/cucumber/cartridge-runtime-extended-ruby.feature +22 -0
  105. data/test/cucumber/cartridge-runtime-standard-diy.feature +6 -0
  106. data/test/cucumber/cartridge-runtime-standard-jbossas.feature +7 -0
  107. data/test/cucumber/cartridge-runtime-standard-jbosseap.feature +7 -0
  108. data/test/cucumber/cartridge-runtime-standard-jbossews10.feature +7 -0
  109. data/test/cucumber/cartridge-runtime-standard-jenkins.feature +8 -0
  110. data/test/cucumber/cartridge-runtime-standard-nodejs.feature +7 -0
  111. data/test/cucumber/cartridge-runtime-standard-perl.feature +6 -0
  112. data/test/cucumber/cartridge-runtime-standard-php.feature +6 -0
  113. data/test/cucumber/cartridge-runtime-standard-python.feature +6 -0
  114. data/test/cucumber/cartridge-runtime-standard-ruby.feature +19 -0
  115. data/test/cucumber/cartridge-switchyard.feature +36 -0
  116. data/test/cucumber/descriptor.feature +40 -0
  117. data/test/cucumber/embedded.feature +44 -0
  118. data/test/cucumber/idler.feature +75 -0
  119. data/test/cucumber/misc/descriptor/manifest.yml +22 -0
  120. data/test/cucumber/misc/php/db_test.php +21 -0
  121. data/test/cucumber/openshift-node.feature +21 -0
  122. data/test/cucumber/rest-application-templates.feature +31 -0
  123. data/test/cucumber/rest-applications.feature +431 -0
  124. data/test/cucumber/rest-cartridge-types.feature +16 -0
  125. data/test/cucumber/rest-domains.feature +276 -0
  126. data/test/cucumber/rest-gears.feature +38 -0
  127. data/test/cucumber/rest-keys.feature +247 -0
  128. data/test/cucumber/rest-quickstarts.feature +27 -0
  129. data/test/cucumber/rest-workflow.feature +64 -0
  130. data/test/cucumber/step_definitions/api_steps.rb +369 -0
  131. data/test/cucumber/step_definitions/application-estimate-steps.rb +51 -0
  132. data/test/cucumber/step_definitions/application_steps.rb +215 -0
  133. data/test/cucumber/step_definitions/cartridge-10gen-mms-agent_steps.rb +11 -0
  134. data/test/cucumber/step_definitions/cartridge-cron_steps.rb +51 -0
  135. data/test/cucumber/step_definitions/cartridge-haproxy_steps.rb +30 -0
  136. data/test/cucumber/step_definitions/cartridge-jenkins_steps.rb +93 -0
  137. data/test/cucumber/step_definitions/cartridge-lifecycle-nodejs_steps.rb +30 -0
  138. data/test/cucumber/step_definitions/cartridge-mongodb_steps.rb +60 -0
  139. data/test/cucumber/step_definitions/cartridge-mysql_steps.rb +56 -0
  140. data/test/cucumber/step_definitions/cartridge-php_steps.rb +72 -0
  141. data/test/cucumber/step_definitions/cartridge-postgresql_steps.rb +59 -0
  142. data/test/cucumber/step_definitions/cartridge-switchyard_steps.rb +29 -0
  143. data/test/cucumber/step_definitions/client_steps.rb +12 -0
  144. data/test/cucumber/step_definitions/descriptor_step.rb +32 -0
  145. data/test/cucumber/step_definitions/idler_steps.rb +37 -0
  146. data/test/cucumber/step_definitions/node_steps.rb +203 -0
  147. data/test/cucumber/step_definitions/runtime_steps.rb +547 -0
  148. data/test/cucumber/step_definitions/runtime_url_steps.rb +46 -0
  149. data/test/cucumber/step_definitions/trap-user-extended_steps.rb +14 -0
  150. data/test/cucumber/step_definitions/trap-user_steps.rb +58 -0
  151. data/test/cucumber/support/00_setup_helper.rb +106 -0
  152. data/test/cucumber/support/app_helper.rb +243 -0
  153. data/test/cucumber/support/assertions.rb +52 -0
  154. data/test/cucumber/support/command_helper.rb +453 -0
  155. data/test/cucumber/support/dns_helper.rb +54 -0
  156. data/test/cucumber/support/env.rb +5 -0
  157. data/test/cucumber/support/process_helper.rb +44 -0
  158. data/test/cucumber/support/runtime_support.rb +440 -0
  159. data/test/cucumber/support/unused.rb +27 -0
  160. data/test/cucumber/support/user_helper.rb +37 -0
  161. data/test/cucumber/trap-user-extended.feature +53 -0
  162. data/test/cucumber/trap-user.feature +34 -0
  163. data/test/ddns/1.168.192-rev.db.init +13 -0
  164. data/test/ddns/HOWTO.txt +207 -0
  165. data/test/ddns/Kexample.com.+157+06142.key +1 -0
  166. data/test/ddns/Kexample.com.+157+06142.private +7 -0
  167. data/test/ddns/authconfig.rb +14 -0
  168. data/test/ddns/example.com.db.init +23 -0
  169. data/test/ddns/example.com.key +4 -0
  170. data/test/ddns/named.ca +52 -0
  171. data/test/ddns/named.conf +48 -0
  172. data/test/ddns/named.empty +10 -0
  173. data/test/ddns/named.localhost +10 -0
  174. data/test/ddns/named.loopback +11 -0
  175. data/test/ddns/named.rfc1912.zones +42 -0
  176. data/test/ddns/named.root.key +5 -0
  177. data/test/ddns/named_service.rb +127 -0
  178. data/test/unit/bind_dns_service_test.rb +167 -0
  179. data/test/unit/broker_auth_test.rb +28 -0
  180. metadata +545 -0
@@ -0,0 +1,29 @@
1
+ class QuickstartsController < BaseController
2
+ respond_to :json
3
+ before_filter :check_version
4
+
5
+ def index
6
+ render_success(:ok, "quickstarts", quickstarts, "LIST_QUICKSTARTS", "Showing all quickstarts")
7
+ end
8
+
9
+ def show
10
+ id = params[:id]
11
+ if quickstart = quickstarts.find{ |obj| obj['quickstart']['id'] == id }
12
+ render_success(:ok, "quickstarts", [quickstart], "SHOW_QUICKSTART", "Showing quickstart for '#{id}'")
13
+ else
14
+ render_error(:not_found, "Quickstart '#{id}' not found", 118, "SHOW_QUICKSTART")
15
+ end
16
+ end
17
+
18
+ protected
19
+ def quickstarts
20
+ if File.exists?(file)
21
+ ActiveSupport::JSON.decode(IO.read(file)) rescue []
22
+ else
23
+ []
24
+ end
25
+ end
26
+ def file
27
+ File.join(OpenShift::Config::CONF_DIR, 'quickstarts.json')
28
+ end
29
+ end
@@ -0,0 +1,38 @@
1
+ class UserController < BaseController
2
+ respond_to :json, :xml
3
+ before_filter :authenticate, :check_version
4
+
5
+ # GET /user
6
+ def show
7
+ unless @cloud_user
8
+ log_action(@request_id, 'nil', @login, "SHOW_USER", true, "User '#{@login}' not found")
9
+ return render_error(:not_found, "User '#{@login}' not found", 99)
10
+ end
11
+ render_success(:ok, "user", RestUser.new(@cloud_user, get_url, nolinks), "SHOW_USER")
12
+ end
13
+
14
+ # DELETE /user
15
+ # NOTE: Only applicable for subaccount users
16
+ def destroy
17
+ force = get_bool(params[:force])
18
+
19
+ unless @cloud_user
20
+ log_action(@request_id, 'nil', @login, "DELETE_USER", true, "User '#{@login}' not found")
21
+ return render_format_error(:not_found, "User '#{@login}' not found", 99)
22
+ end
23
+ return render_format_error(:forbidden, "User deletion not permitted. Only applicable for subaccount users.", 138, "DELETE_USER") unless @cloud_user.parent_user_login
24
+
25
+ begin
26
+ if force
27
+ @cloud_user.force_delete
28
+ else
29
+ return render_format_error(:unprocessable_entity, "User '#{@login}' has valid domain or applications. Either delete domain, applications and retry the operation or use 'force' option.",
30
+ 139, "DELETE_USER") if !@cloud_user.domains.empty? or !@cloud_user.applications.empty?
31
+ @cloud_user.delete
32
+ end
33
+ render_format_success(:no_content, nil, nil, "DELETE_USER", "User #{@login} deleted.", true)
34
+ rescue Exception => e
35
+ return render_format_exception(e, "DELETE_USER")
36
+ end
37
+ end
38
+ end
@@ -0,0 +1,25 @@
1
+ module CartridgeHelper
2
+ def get_cartridges(application)
3
+ cartridges = Array.new
4
+ cartridges.push(RestCartridge11.new("standalone", application.framework, application, get_url, nil, nolinks)) if $requested_api_version != 1.0
5
+
6
+ application.embedded.each_key do |key|
7
+ if $requested_api_version == 1.0
8
+ cartridge = RestCartridge10.new("embedded", key, application, get_url, nil, nolinks)
9
+ else
10
+ cartridge = RestCartridge11.new("embedded", key, application, get_url, nil, nolinks)
11
+ end
12
+ cartridges.push(cartridge)
13
+ end if application.embedded
14
+ return cartridges
15
+ end
16
+
17
+ def check_cartridge_type(framework, container, cart_type)
18
+ carts = CartridgeCache.cartridge_names(cart_type)
19
+ Rails.logger.debug "Available cartridges #{carts.join(', ')}"
20
+ unless carts.include? framework
21
+ return false
22
+ end
23
+ return true
24
+ end
25
+ end
@@ -0,0 +1,21 @@
1
+ module LegacyBrokerHelper
2
+ def get_cached(key, opts={})
3
+ unless Rails.configuration.action_controller.perform_caching
4
+ if block_given?
5
+ return yield
6
+ end
7
+ end
8
+
9
+ val = Rails.cache.read(key)
10
+ unless val
11
+ if block_given?
12
+ val = yield
13
+ if val
14
+ Rails.cache.write(key, val, opts)
15
+ end
16
+ end
17
+ end
18
+
19
+ return val
20
+ end
21
+ end
@@ -0,0 +1,38 @@
1
+ module UserActionLogger
2
+
3
+ @@action_logger = nil
4
+
5
+ def get_action_logger()
6
+ unless @@action_logger
7
+ log_file = nil
8
+ if Rails.configuration.user_action_logging[:logging_enabled]
9
+ log_file = Rails.configuration.user_action_logging[:log_filepath]
10
+ end
11
+ @@action_logger = Logger.new(log_file) unless log_file.nil?
12
+ end
13
+ @@action_logger
14
+ end
15
+
16
+ def log_action(request_id, user_id, login, action, success = true, description = "", args = {})
17
+ log_level = success ? Logger::DEBUG : Logger::ERROR
18
+ action_logger = get_action_logger()
19
+
20
+ if not action_logger.nil?
21
+ result = success ? "SUCCESS" : "FAILURE"
22
+ description = description.nil? ? "" : description.strip
23
+ time_obj = Time.new
24
+ date = time_obj.strftime("%Y-%m-%d")
25
+ time = time_obj.strftime("%H:%M:%S")
26
+
27
+ message = "#{result} DATE=#{date} TIME=#{time} ACTION=#{action} REQ_ID=#{request_id} USER_ID=#{user_id} LOGIN=#{login}"
28
+ args.each {|k,v| message += " #{k}=#{v}"}
29
+
30
+ action_logger.info("#{message} #{description}")
31
+ end
32
+
33
+ # Using a block prevents the message in the block from being executed
34
+ # if the log_level is lower than the one set for the logger
35
+ Rails.logger.add(log_level) {"[REQ_ID=#{request_id}] ACTION=#{action} #{description}"}
36
+ end
37
+
38
+ end
@@ -0,0 +1,1718 @@
1
+ require 'state_machine'
2
+ require 'syslog'
3
+ require 'shellwords'
4
+
5
+ class Application < OpenShift::Cartridge
6
+ attr_accessor :user, :creation_time, :uuid, :aliases, :cart_data,
7
+ :state, :group_instance_map, :comp_instance_map, :conn_endpoints_list,
8
+ :domain, :group_override_map, :working_comp_inst_hash,
9
+ :working_group_inst_hash, :configure_order, :start_order,
10
+ :scalable, :proxy_cartridge, :init_git_url, :node_profile,
11
+ :ssh_keys, :ngears, :usage_records, :destroyed_gears, :user_agent
12
+ primary_key :name
13
+ exclude_attributes :user, :comp_instance_map, :group_instance_map,
14
+ :working_comp_inst_hash, :working_group_inst_hash, :user_agent,
15
+ :group_override_map
16
+ include_attributes :comp_instances, :group_instances
17
+
18
+ APP_NAME_MAX_LENGTH = 32
19
+ DEFAULT_NODE_PROFILE = "small"
20
+ UNSCALABLE_FRAMEWORKS = ["jenkins-1.4", "diy-0.1"]
21
+ SCALABLE_EMBEDDED_CARTS = ["mysql-5.1", "mongodb-2.2", "postgresql-8.4", "jenkins-client-1.4"]
22
+
23
+ validate :extended_validator
24
+
25
+ validates_each :name, :allow_nil =>false do |record, attribute, val|
26
+ if !(val =~ /\A[A-Za-z0-9]+\z/)
27
+ record.errors.add attribute, {:message => "Invalid #{attribute} specified", :exit_code => 105}
28
+ end
29
+ if val and val.length > APP_NAME_MAX_LENGTH
30
+ record.errors.add attribute, {:message => "The supplied application name is too long. (Max permitted length: #{APP_NAME_MAX_LENGTH} characters)", :exit_code => 105}
31
+ end
32
+ Rails.logger.debug "Checking to see if application name is black listed"
33
+ if OpenShift::ApplicationContainerProxy.blacklisted?(val)
34
+ record.errors.add attribute, {:message => "The supplied application name is not allowed", :exit_code => 105}
35
+ end
36
+ end
37
+
38
+ validates_each :node_profile, :allow_nil =>true do |record, attribute, val|
39
+ allowed_sizes=OpenShift::ApplicationContainerProxy.valid_gear_sizes(record.user)
40
+ unless allowed_sizes.include? val
41
+ record.errors.add attribute, {:message => "Invalid Size: #{val}. Must be: #{allowed_sizes.join(', ')}. Please contact support for access to additional sizes.", :exit_code => 134}
42
+ end
43
+ end
44
+
45
+ def extended_validator
46
+ notify_observers(:validate_application)
47
+ end
48
+
49
+ # @param [CloudUser] user
50
+ # @param [String] app_name Application name
51
+ # @param [optional, String] uuid Unique identifier for the application
52
+ # @param [deprecated, String] node_profile Node profile for the first application gear
53
+ # @param [deprecated, String] framework Cartridge name to use as the framwwork of the application
54
+ def initialize(user=nil, app_name=nil, uuid=nil, node_profile=nil, framework=nil, template=nil, will_scale=false, domain=nil, init_git_url=nil)
55
+ self.user = user
56
+ self.domain = domain
57
+ self.node_profile = node_profile
58
+ self.creation_time = DateTime::now().strftime
59
+ self.uuid = uuid || OpenShift::Model.gen_uuid
60
+ self.scalable = will_scale
61
+ self.ngears = 0
62
+
63
+ if template.nil?
64
+ if self.scalable
65
+ descriptor_hash = YAML.load(template_scalable_app(app_name, framework))
66
+ from_descriptor(descriptor_hash)
67
+ self.proxy_cartridge = "haproxy-1.4"
68
+ else
69
+ from_descriptor({"Name"=>app_name})
70
+ self.requires_feature = []
71
+ self.requires_feature << framework unless framework.nil?
72
+ end
73
+ @init_git_url = init_git_url unless init_git_url.nil?
74
+ else
75
+ template_descriptor = YAML.load(template.descriptor_yaml)
76
+ template_descriptor["Name"] = app_name
77
+ if not template_descriptor["Configure-Order"]
78
+ requires_list = template_descriptor["Requires"] || []
79
+ template_descriptor["Configure-Order"] = requires_list
80
+ end
81
+ from_descriptor(template_descriptor)
82
+ @init_git_url = template.git_url
83
+ end
84
+ self.categories -= ["cartridge"]
85
+ end
86
+
87
+ def node_profile
88
+ # node_profile can be nil for older data. Should migrate everything to have a node_profile
89
+ # with the next major migration. Although technically node_profile shouldn't even be on application.
90
+ if @node_profile.nil?
91
+ return DEFAULT_NODE_PROFILE
92
+ else
93
+ return @node_profile
94
+ end
95
+ end
96
+
97
+ def add_to_requires_feature(feature)
98
+ prof = @profile_name_map[@default_profile]
99
+ if self.scalable
100
+ # add to the proxy component
101
+ comp_name = "proxy" if comp_name.nil?
102
+ prof = @profile_name_map[@default_profile]
103
+ cinst = ComponentInstance::find_component_in_cart(prof, self, comp_name, self.get_name_prefix)
104
+ raise OpenShift::NodeException.new("Cannot find component '#{comp_name}' in app #{self.name}.", 135, result_io) if cinst.nil?
105
+ comp,profile,cart = cinst.get_component_definition(self)
106
+ raise OpenShift::UserException.new("#{feature} already embedded in '#{@name}'", 136) if comp.depends.include? feature
107
+ fcart = self.framework
108
+ conn = OpenShift::Connection.new("#{feature}-web-#{fcart}")
109
+ conn.components = ["proxy/#{feature}", "web/#{fcart}"]
110
+ prof.add_connection(conn)
111
+ conn = OpenShift::Connection.new("#{feature}-proxy-#{fcart}")
112
+ conn.components = ["proxy/#{feature}", "proxy/#{fcart}"]
113
+ prof.add_connection(conn)
114
+
115
+ # FIXME: Booya - hacks galore -- fix this to be more generic when
116
+ # scalable apps allow more components in SCALABLE_EMBEDDED_CARTS
117
+ if feature == "jenkins-client-1.4"
118
+ conn = OpenShift::Connection.new("#{feature}-proxy-haproxy-1.4")
119
+ conn.components = ["proxy/#{feature}", "proxy/haproxy-1.4"]
120
+ prof.add_connection(conn)
121
+ end
122
+
123
+ comp.depends << feature
124
+ else
125
+ self.requires_feature.each { |cart|
126
+ conn = OpenShift::Connection.new("#{feature}-#{cart}")
127
+ conn.components = [cart, feature]
128
+ prof.add_connection(conn)
129
+ }
130
+ self.requires_feature << feature
131
+ end
132
+ end
133
+
134
+ def template_scalable_app(app_name, framework)
135
+ return "
136
+ Name: #{app_name}
137
+ Components:
138
+ proxy:
139
+ Dependencies: [#{framework}, \"haproxy-1.4\"]
140
+ web:
141
+ Dependencies: [#{framework}]
142
+ Groups:
143
+ proxy:
144
+ Components:
145
+ proxy: proxy
146
+ web:
147
+ Components:
148
+ web: web
149
+ GroupOverrides:
150
+ - [\"proxy\", \"proxy/haproxy-1.4\"]
151
+ - [\"proxy\", \"proxy/#{framework}\"]
152
+ - [\"web\", \"web/#{framework}\"]
153
+ Connections:
154
+ auto-scale:
155
+ Components: [\"proxy/haproxy-1.4\", \"web/#{framework}\"]
156
+ proxy-web:
157
+ Components: [\"proxy/#{framework}\", \"web/#{framework}\"]
158
+ Configure-Order: [\"proxy/#{framework}\", \"proxy/haproxy-1.4\"]
159
+ "
160
+ end
161
+
162
+ def remove_from_requires_feature(feature)
163
+ prof = @profile_name_map[@default_profile]
164
+ if prof.connection_name_map
165
+ prof.connection_name_map.delete_if {|k,v| v.components[0].include? feature or v.components[1].include? feature }
166
+ end
167
+ if self.scalable
168
+ comp_name = "proxy" if comp_name.nil?
169
+ prof = @profile_name_map[@default_profile]
170
+ cinst = ComponentInstance::find_component_in_cart(prof, self, comp_name, self.get_name_prefix)
171
+ raise OpenShift::NodeException.new("Cannot find component '#{comp_name}' in app #{self.name}.", 135, result_io) if cinst.nil?
172
+ comp,profile,cart = cinst.get_component_definition(self)
173
+ raise OpenShift::UserException.new("#{feature} not embedded in '#{@name}', try adding it first", 135) if not comp.depends.include? feature
174
+ comp.depends.delete(feature)
175
+ else
176
+ self.requires_feature.delete feature
177
+ end
178
+ end
179
+
180
+ # Find an application to which user has access
181
+ # @param [CloudUser] user
182
+ # @param [String] app_name
183
+ # @return [Application]
184
+ def self.find(user, app_name)
185
+ return nil if app_name.nil? or app_name.empty?
186
+ app = nil
187
+ if user.applications
188
+ user.applications.each do |next_app|
189
+ if next_app.name.downcase == app_name.downcase
190
+ app = next_app
191
+ break
192
+ end
193
+ end
194
+ else
195
+ app = super(user.login, app_name)
196
+ return nil unless app
197
+ app.user = user
198
+ app.reset_state
199
+ end
200
+ app
201
+ end
202
+
203
+ # Find an applications to which user has access
204
+ # @param [CloudUser] user
205
+ # @return [Array<Application>]
206
+ def self.find_all(user)
207
+ apps = nil
208
+ if user.applications
209
+ apps = user.applications
210
+ else
211
+ apps = super(user.login)
212
+ apps.each do |app|
213
+ app.user = user
214
+ app.reset_state
215
+ end
216
+ user.applications = apps
217
+ end
218
+ apps
219
+ end
220
+
221
+ def self.find_by_gear_uuid(gear_uuid)
222
+ hash = OpenShift::DataStore.instance.find_by_gear_uuid(gear_uuid)
223
+ return nil unless hash
224
+ user = CloudUser.hash_to_obj hash
225
+ user.applications.each do |next_app|
226
+ next_app.gears.each do |gear|
227
+ if gear.uuid == gear_uuid
228
+ return next_app,gear
229
+ end
230
+ end
231
+ end
232
+ return nil
233
+ end
234
+
235
+ def self.find_by_uuid(uuid)
236
+ hash = OpenShift::DataStore.instance.find_by_uuid(self.name,uuid)
237
+ return nil unless hash
238
+ user = CloudUser.hash_to_obj hash
239
+ app = nil
240
+ user.applications.each do |next_app|
241
+ if next_app.uuid == uuid
242
+ app = next_app
243
+ break
244
+ end
245
+ end
246
+ return app
247
+ end
248
+
249
+ def self.hash_to_obj(hash)
250
+ domain = nil
251
+ if hash["domain"]
252
+ domain = Domain.hash_to_obj(hash["domain"])
253
+ end
254
+ app = super(hash)
255
+ app.domain = domain
256
+ app
257
+ end
258
+
259
+ # @overload Application.get_available_cartridges(cart_type)
260
+ # @deprecated
261
+ # Returns List of names of available cartridges of specified type
262
+ # @param [String] cart_type Must be "standalone" or "embedded" or nil
263
+ # @return [Array<String>]
264
+ # @overload Application.get_available_cartridges
265
+ # @return [Array<String>]
266
+ # Returns List of names of all available cartridges
267
+ def self.get_available_cartridges(cart_type=nil)
268
+ cart_names = CartridgeCache.cartridge_names(cart_type)
269
+ end
270
+
271
+ # Saves the application object in the datastore
272
+ def save
273
+ super(user.login)
274
+ self.ngears = 0
275
+ self.usage_records = nil
276
+ self.destroyed_gears = []
277
+ end
278
+
279
+ # Deletes the application object from the datastore
280
+ def delete
281
+ super(user.login)
282
+ end
283
+
284
+ # Processes the application descriptor and creates all the gears necessary to host the application.
285
+ # Destroys application on all gears if any gear fails
286
+ # @return [ResultIO]
287
+ def create
288
+ result_io = ResultIO.new
289
+ gears_created = []
290
+ begin
291
+ self.node_profile = DEFAULT_NODE_PROFILE unless self.node_profile
292
+ elaborate_descriptor
293
+ self.class.notify_observers(:before_application_create, {:application => self, :reply => result_io})
294
+ if self.scalable
295
+ raise OpenShift::UserException.new("Scalable app cannot be of type #{UNSCALABLE_FRAMEWORKS.join(' ')}", "108", result_io) if UNSCALABLE_FRAMEWORKS.include? framework
296
+ min_gear_count = 0
297
+ group_instances.uniq.each { |gi|
298
+ min_gear_count += gi.min
299
+ }
300
+ if ((user.consumed_gears+min_gear_count) > user.max_gears)
301
+ raise OpenShift::UserException.new("Creating this application requires #{min_gear_count} gears, and you are using #{user.consumed_gears} of your #{user.max_gears} available gears.", 104)
302
+ end
303
+ end
304
+ user.applications = [] unless user.applications
305
+ user.applications << self
306
+ Rails.logger.debug "Creating gears"
307
+ group_instances.uniq.each do |ginst|
308
+ create_result, new_gear = ginst.add_gear(self)
309
+ result_io.append create_result
310
+ end
311
+
312
+ self.gear.name = self.name unless scalable
313
+ self.class.notify_observers(:application_creation_success, {:application => self, :reply => result_io})
314
+ rescue Exception => e
315
+ Rails.logger.debug e.message
316
+ Rails.logger.debug e.backtrace.join("\n")
317
+ Rails.logger.debug "Rolling back application gear creation"
318
+ result_io.append self.destroy(true)
319
+ self.class.notify_observers(:application_creation_failure, {:application => self, :reply => result_io})
320
+ raise
321
+ ensure
322
+ save
323
+ end
324
+ self.class.notify_observers(:after_application_create, {:application => self, :reply => result_io})
325
+ result_io
326
+ end
327
+
328
+ # Convenience method to cleanup an application
329
+ def cleanup_and_delete
330
+ reply = ResultIO.new
331
+ reply.append self.destroy_dns
332
+ reply.append self.destroy
333
+ self.delete
334
+ reply
335
+ end
336
+
337
+ # Destroys all gears.
338
+ def destroy(force=false)
339
+ reply = ResultIO.new
340
+ self.class.notify_observers(:before_application_destroy, {:application => self, :reply => reply})
341
+
342
+ # Only need to destroy if application has been elaborated first
343
+ unless self.configure_order.nil?
344
+ # Destroy in the reverse order of configure.
345
+ group_instances = []
346
+ self.configure_order.reverse.each do |comp_inst_name|
347
+ comp_inst = self.comp_instance_map[comp_inst_name]
348
+ next if comp_inst.parent_cart_name == self.name
349
+ group_inst = self.group_instance_map[comp_inst.group_instance_name]
350
+ group_instances.delete(group_inst)
351
+ group_instances << group_inst
352
+ end
353
+
354
+ failures = []
355
+ group_instances.each do |group_inst|
356
+ s,f = run_on_gears(group_inst.gears, reply, false) do |gear, r|
357
+ r.append group_inst.remove_gear(gear, force)
358
+ end
359
+ failures += f
360
+ end
361
+
362
+ begin
363
+ self.save if self.persisted?
364
+ rescue Exception => e
365
+ # pass on failure... because we maybe wanting a delete here instead anyway
366
+ end
367
+
368
+ failures.each do |data|
369
+ Rails.logger.debug("Unable to clean up application on gear #{data[:gear]} due to exception #{data[:exception].message}")
370
+ Rails.logger.debug(data[:exception].backtrace.inspect)
371
+ end
372
+
373
+ raise OpenShift::NodeException.new("Could not destroy all gears of application.", 1, reply) if failures.length > 0
374
+ end
375
+ self.class.notify_observers(:after_application_destroy, {:application => self, :reply => reply})
376
+ reply
377
+ end
378
+
379
+ def web_cart
380
+ return framework
381
+ end
382
+
383
+ def gears
384
+ self.group_instances.uniq.map{ |ginst| ginst.gears }.flatten
385
+ end
386
+
387
+ def scaleup(comp_name=nil)
388
+ result_io = ResultIO.new
389
+
390
+ if not self.scalable
391
+ raise OpenShift::UserException.new("Cannot scale a non-scalable application", 255, result_io)
392
+ end
393
+
394
+ comp_name = "web" if comp_name.nil?
395
+ prof = @profile_name_map[@default_profile]
396
+ cinst = ComponentInstance::find_component_in_cart(prof, self, comp_name, self.get_name_prefix)
397
+ raise OpenShift::NodeException.new("Cannot find #{comp_name} in app #{self.name}.", 1, result_io) if cinst.nil?
398
+ ginst = self.group_instance_map[cinst.group_instance_name]
399
+ raise OpenShift::NodeException.new("Cannot find group #{cinst.group_instance_name} for #{comp_name} in app #{self.name}.", 1, result_io) if ginst.nil?
400
+ raise OpenShift::UserException.new("Cannot scale up beyond maximum gear limit in app #{self.name}.", 104, result_io) if ginst.gears.length >= ginst.max and ginst.max > 0
401
+ raise OpenShift::UserException.new("Cannot scale up beyond gear limit '#{user.max_gears}'", 104, result_io) if user.consumed_gears >= user.max_gears
402
+ result, new_gear = ginst.add_gear(self)
403
+ result_io.append result
404
+ result_io.append self.configure_dependencies
405
+ self.execute_connections
406
+ result_io
407
+ end
408
+
409
+ def scaledown(comp_name=nil)
410
+ result_io = ResultIO.new
411
+ if not self.scalable
412
+ raise OpenShift::UserException.new("Cannot scale a non-scalable application", 255, result_io)
413
+ end
414
+ comp_name = "web" if comp_name.nil?
415
+ prof = @profile_name_map[@default_profile]
416
+ cinst = ComponentInstance::find_component_in_cart(prof, self, comp_name, self.get_name_prefix)
417
+ raise OpenShift::NodeException.new("Cannot find #{comp_name} in app #{self.name}.", 1, result_io) if cinst.nil?
418
+ ginst = self.group_instance_map[cinst.group_instance_name]
419
+ raise OpenShift::NodeException.new("Cannot find group #{cinst.group_instance_name} for #{comp_name} in app #{self.name}.", 1, result_io) if ginst.nil?
420
+ # remove any gear out of this ginst
421
+ raise OpenShift::UserException.new("Cannot scale below minimum gear requirements", 1, result_io) if ginst.gears.length <= ginst.min
422
+
423
+ gear = ginst.gears.last
424
+
425
+ dns = OpenShift::DnsService.instance
426
+ begin
427
+ dns.deregister_application(gear.name, @domain.namespace)
428
+ dns.publish
429
+ ensure
430
+ dns.close
431
+ end
432
+
433
+ result_io.append ginst.remove_gear(gear)
434
+
435
+ # inform anyone who needs to know that this gear is no more
436
+ self.configure_dependencies
437
+ self.execute_connections
438
+ result_io
439
+ end
440
+
441
+ # Elaborates the descriptor, configures cartridges that were added to the application dependencies.
442
+ # If a node is empty after removing components, then the gear is destroyed. Errors that occur while removing cartridges are logged but no exception is thrown.
443
+ # If an error occurs while configuring a cartridge, then the cartirdge is deconfigures on all nodes and an exception is thrown.
444
+ def configure_dependencies
445
+ reply = ResultIO.new
446
+ self.class.notify_observers(:before_application_configure, {:application => self, :reply => reply})
447
+
448
+ elaborate_descriptor
449
+
450
+ exceptions = []
451
+ Rails.logger.debug "Configure order is #{self.configure_order.inspect}"
452
+ #process new additions
453
+ #TODO: fix configure after framework cartridge is no longer a requirement for adding embedded cartridges
454
+ self.configure_order.each do |comp_inst_name|
455
+ comp_inst = self.comp_instance_map[comp_inst_name]
456
+ next if comp_inst.parent_cart_name == self.name
457
+ group_inst = self.group_instance_map[comp_inst.group_instance_name]
458
+ begin
459
+ group_inst.fulfil_requirements(self)
460
+ run_on_gears(group_inst.get_unconfigured_gears(comp_inst), reply) do |gear, r|
461
+ doExpose = false
462
+ if self.scalable and comp_inst.parent_cart_name!=self.proxy_cartridge
463
+ doExpose = true if not gear.configured_components.include? comp_inst.name
464
+ end
465
+ r.append gear.configure(comp_inst, @init_git_url)
466
+ begin
467
+ r.append gear.expose_port(comp_inst) if doExpose
468
+ rescue Exception=>e
469
+ end
470
+ process_cartridge_commands(r)
471
+ end
472
+ rescue Exception => e
473
+ Rails.logger.debug e.message
474
+ Rails.logger.debug e.backtrace.inspect
475
+
476
+ if e.kind_of?(OpenShift::GearsException)
477
+ successful_gears = []
478
+ successful_gears = e.successful.map{|g| g[:gear]} if e.successful
479
+ failed_gears = []
480
+ failed_gears = e.failed.map{|g| g[:gear]} if e.failed
481
+ gear_exception = e.exception
482
+
483
+ #remove failed component from all gears
484
+ run_on_gears(successful_gears, reply, false) do |gear, r|
485
+ r.append gear.deconfigure(comp_inst)
486
+ process_cartridge_commands(r)
487
+ end
488
+ run_on_gears(failed_gears, reply, false) do |gear, r|
489
+ r.append gear.deconfigure(comp_inst, true)
490
+ process_cartridge_commands(r)
491
+ end
492
+ else
493
+ gear_exception = e
494
+ end
495
+
496
+ # destroy any unused gears
497
+ # TODO : if the destroy fails below... the user still sees the error as configure failure
498
+ # Then to recover, if we re-elaborate (like in add_dependency), then the group instance will get lost
499
+ # and any failed gears below will leak (i.e. they exist on node, their destroy failed, but they do not have any handle in Mongo)
500
+ run_on_gears(group_inst.gears, reply, false) do |gear, r|
501
+ r.append group_inst.remove_gear(gear) if gear.configured_components.length == 0
502
+ end
503
+
504
+ self.save
505
+ exceptions << gear_exception
506
+ end
507
+ end
508
+
509
+ unless exceptions.empty?
510
+ raise exceptions.first
511
+ end
512
+
513
+ self.save
514
+ self.class.notify_observers(:after_application_configure, {:application => self, :reply => reply})
515
+ reply
516
+ end
517
+
518
+ def execute_connections
519
+ return if not self.scalable
520
+
521
+ self.conn_endpoints_list.each { |conn|
522
+ pub_inst = self.comp_instance_map[conn.from_comp_inst]
523
+ pub_ginst = self.group_instance_map[pub_inst.group_instance_name]
524
+
525
+ tag = ""
526
+ handle = RemoteJob.create_parallel_job
527
+ RemoteJob.run_parallel_on_gears(pub_ginst.gears, handle) { |exec_handle, gear|
528
+ appname = gear.name
529
+ connector_name = conn.from_connector.name
530
+ cart = pub_inst.parent_cart_name
531
+ input_args = [appname, self.domain.namespace, gear.uuid]
532
+
533
+ job = gear.get_execute_connector_job(cart, connector_name, input_args)
534
+ RemoteJob.add_parallel_job(exec_handle, tag, gear, job)
535
+ }
536
+ pub_out = []
537
+ RemoteJob.get_parallel_run_results(handle) { |tag, gear, output, status|
538
+ if status==0
539
+ pub_out.push("'#{gear}'='#{output}'")
540
+ end
541
+ }
542
+ input_to_subscriber = Shellwords::shellescape(pub_out.join(' '))
543
+ Rails.logger.debug "Output of publisher - '#{pub_out}'"
544
+
545
+ sub_inst = self.comp_instance_map[conn.to_comp_inst]
546
+ sub_ginst = self.group_instance_map[sub_inst.group_instance_name]
547
+ handle = RemoteJob.create_parallel_job
548
+ RemoteJob.run_parallel_on_gears(sub_ginst.gears, handle) { |exec_handle, gear|
549
+ appname = gear.name
550
+ connector_name = conn.to_connector.name
551
+ cart = sub_inst.parent_cart_name
552
+ input_args = [appname, self.domain.namespace, gear.uuid, input_to_subscriber]
553
+
554
+ job = gear.get_execute_connector_job(cart, connector_name, input_args)
555
+ RemoteJob.add_parallel_job(exec_handle, tag, gear, job)
556
+ }
557
+ # we dont care about subscriber's output/status
558
+ }
559
+ end
560
+
561
+ # Start a particular dependency on all gears that host it.
562
+ # If unable to start a component, the application is stopped on all gears
563
+ # @param [String] dependency Name of a cartridge to start. Set to nil for all dependencies.
564
+ # @param [Boolean] force_stop_on_failure
565
+ def start(dependency=nil, stop_on_failure=true)
566
+ reply = ResultIO.new
567
+ self.class.notify_observers(:before_start, {:application => self, :reply => reply, :dependency => dependency})
568
+ self.start_order.each do |comp_inst_name|
569
+ comp_inst = self.comp_instance_map[comp_inst_name]
570
+ next if !dependency.nil? and (comp_inst.parent_cart_name != dependency)
571
+ next if comp_inst.parent_cart_name == self.name
572
+
573
+ begin
574
+ group_inst = self.group_instance_map[comp_inst.group_instance_name]
575
+ run_on_gears(group_inst.gears, reply) do |gear, r|
576
+ r.append gear.start(comp_inst)
577
+ end
578
+ rescue Exception => e
579
+ gear_exception = e.message[:exception]
580
+ self.stop(dependency,false,false) if stop_on_failure
581
+ raise gear_exception
582
+ end
583
+ end
584
+ self.class.notify_observers(:after_start, {:application => self, :reply => reply, :dependency => dependency})
585
+ reply
586
+ end
587
+
588
+ # Stop a particular dependency on all gears that host it.
589
+ # @param [String] dependency Name of a cartridge to start. Set to nil for all dependencies.
590
+ # @param [Boolean] force_stop_on_failure
591
+ # @param [Boolean] throw_exception_on_failure
592
+ def stop(dependency=nil, force_stop_on_failure=true, throw_exception_on_failure=true)
593
+ reply = ResultIO.new
594
+ self.class.notify_observers(:before_stop, {:application => self, :reply => reply, :dependency => dependency})
595
+ self.start_order.reverse.each do |comp_inst_name|
596
+ comp_inst = self.comp_instance_map[comp_inst_name]
597
+ next if !dependency.nil? and (comp_inst.parent_cart_name != dependency)
598
+ next if comp_inst.parent_cart_name == self.name
599
+
600
+ group_inst = self.group_instance_map[comp_inst.group_instance_name]
601
+ s,f = run_on_gears(group_inst.gears, reply, false) do |gear, r|
602
+ r.append gear.stop(comp_inst)
603
+ end
604
+
605
+ if(f.length > 0)
606
+ self.force_stop(dependency,false) if(force_stop_on_failure)
607
+ raise f[0][:exception] if(throw_exception_on_failure)
608
+ end
609
+ end
610
+ self.class.notify_observers(:after_stop, {:application => self, :reply => reply, :dependency => dependency})
611
+ reply
612
+ end
613
+
614
+ # Force stop a particular dependency on all gears that host it.
615
+ # @param [String] dependency Name of a cartridge to stop. Set to nil for all dependencies.
616
+ # @param [Boolean] throw_exception_on_failure
617
+ def force_stop(dependency=nil, throw_exception_on_failure=true)
618
+ reply = ResultIO.new
619
+ self.class.notify_observers(:before_force_stop, {:application => self, :reply => reply, :dependency => dependency})
620
+ self.start_order.each do |comp_inst_name|
621
+ comp_inst = self.comp_instance_map[comp_inst_name]
622
+ next if !dependency.nil? and (comp_inst.parent_cart_name != dependency)
623
+
624
+ group_inst = self.group_instance_map[comp_inst.group_instance_name]
625
+ s,f = run_on_gears(group_inst.gears, reply, false) do |gear, r|
626
+ r.append gear.force_stop(comp_inst)
627
+ end
628
+
629
+ raise f[0][:exception] if(f.length > 0 and throw_exception_on_failure)
630
+ end
631
+ self.class.notify_observers(:after_force_stop, {:application => self, :reply => reply, :dependency => dependency})
632
+ reply
633
+ end
634
+
635
+ # Restart a particular dependency on all gears that host it.
636
+ # @param [String] dependency Name of a cartridge to restart. Set to nil for all dependencies.
637
+ def restart(dependency=nil)
638
+ reply = ResultIO.new
639
+ self.class.notify_observers(:before_restart, {:application => self, :reply => reply, :dependency => dependency})
640
+ self.start_order.each do |comp_inst_name|
641
+ comp_inst = self.comp_instance_map[comp_inst_name]
642
+ next if !dependency.nil? and (comp_inst.parent_cart_name != dependency)
643
+
644
+ group_inst = self.group_instance_map[comp_inst.group_instance_name]
645
+ s,f = run_on_gears(group_inst.gears, reply, false) do |gear, r|
646
+ r.append gear.restart(comp_inst)
647
+ end
648
+
649
+ raise f[0][:exception] if(f.length > 0)
650
+ end
651
+ self.class.notify_observers(:after_restart, {:application => self, :reply => reply, :dependency => dependency})
652
+ reply
653
+ end
654
+
655
+ # Reload a particular dependency on all gears that host it.
656
+ # @param [String] dependency Name of a cartridge to reload. Set to nil for all dependencies.
657
+ def reload(dependency=nil)
658
+ dependency = self.framework if dependency.nil?
659
+ reply = ResultIO.new
660
+ self.class.notify_observers(:before_reload, {:application => self, :reply => reply, :dependency => dependency})
661
+ self.start_order.each do |comp_inst_name|
662
+ comp_inst = self.comp_instance_map[comp_inst_name]
663
+ next if !dependency.nil? and (comp_inst.parent_cart_name != dependency)
664
+
665
+ group_inst = self.group_instance_map[comp_inst.group_instance_name]
666
+ s,f = run_on_gears(group_inst.gears, reply, false) do |gear, r|
667
+ r.append gear.reload(comp_inst)
668
+ end
669
+
670
+ raise f[0][:exception] if(f.length > 0)
671
+ end
672
+ self.class.notify_observers(:after_reload, {:application => self, :reply => reply, :dependency => dependency})
673
+ reply
674
+ end
675
+
676
+ # Retrieves status for a particular dependency on all gears that host it.
677
+ # @param [String] dependency Name of a cartridge
678
+ def status(dependency=nil, ret_reply=true)
679
+ reply = ResultIO.new
680
+ app_status = []
681
+ tag = ""
682
+ handle = RemoteJob.create_parallel_job
683
+
684
+ self.comp_instance_map.each do |comp_inst_name, comp_inst|
685
+ next if !dependency.nil? and (comp_inst.parent_cart_name != dependency)
686
+ next if comp_inst.parent_cart_name == self.name
687
+
688
+ group_inst = self.group_instance_map[comp_inst.group_instance_name]
689
+ group_inst.gears.each do |gear|
690
+ job = gear.status_job(comp_inst)
691
+ RemoteJob.add_parallel_job(handle, tag, gear, job)
692
+ end
693
+ end
694
+ if RemoteJob.has_jobs(handle)
695
+ RemoteJob.run_parallel_on_gears([], handle) { }
696
+ RemoteJob.get_parallel_run_results(handle) { |tag, gear, output, rc|
697
+ if rc != 0
698
+ Rails.logger.error "Error: Getting '#{dependency}' status from gear '#{gear}', errcode: '#{rc}' and output: #{output}"
699
+ raise OpenShift::UserException.new("Error: Getting '#{dependency}' status from gear '#{gear}', errcode: '#{rc}' and output: #{output}", 143)
700
+ else
701
+ r = ResultIO.new
702
+ r.resultIO << "#{output}\n"
703
+ reply.append r
704
+ app_status.push({"gear_id" => gear, "message" => output}) unless ret_reply
705
+ end
706
+ }
707
+ end
708
+ if ret_reply
709
+ return reply
710
+ else
711
+ return app_status
712
+ end
713
+ end
714
+
715
+ # Invokes tidy for a particular dependency on all gears that host it.
716
+ # @param [String] dependency Name of a cartridge
717
+ def tidy(dependency=nil)
718
+ dependency = self.framework if dependency.nil?
719
+ reply = ResultIO.new
720
+ self.comp_instance_map.each do |comp_inst_name, comp_inst|
721
+ next if !dependency.nil? and (comp_inst.parent_cart_name != dependency)
722
+
723
+ group_inst = self.group_instance_map[comp_inst.group_instance_name]
724
+ s,f = run_on_gears(group_inst.gears, reply, false) do |gear, r|
725
+ r.append gear.tidy(comp_inst)
726
+ end
727
+
728
+ raise f[0][:exception] if(f.length > 0)
729
+ end
730
+ reply
731
+ end
732
+
733
+ # Invokes threaddump for a particular dependency on all gears that host it.
734
+ # @param [String] dependency Name of a cartridge
735
+ def threaddump(dependency=nil)
736
+ reply = ResultIO.new
737
+ self.comp_instance_map.each do |comp_inst_name, comp_inst|
738
+ next if !dependency.nil? and (comp_inst.parent_cart_name != dependency)
739
+
740
+ group_inst = self.group_instance_map[comp_inst.group_instance_name]
741
+ s,f = run_on_gears(group_inst.gears, reply, false) do |gear, r|
742
+ r.append gear.threaddump(comp_inst)
743
+ end
744
+
745
+ raise f[0][:exception] if(f.length > 0)
746
+ end
747
+ reply
748
+ end
749
+
750
+ # Invokes system_messages for a particular dependency on all gears that host it.
751
+ # @param [String] dependency Name of a cartridge
752
+ def system_messages(dependency=nil)
753
+ reply = ResultIO.new
754
+ self.comp_instance_map.each do |comp_inst_name, comp_inst|
755
+ next if !dependency.nil? and (comp_inst.parent_cart_name != dependency)
756
+
757
+ group_inst = self.group_instance_map[comp_inst.group_instance_name]
758
+ s,f = run_on_gears(group_inst.gears, reply, false) do |gear, r|
759
+ r.append gear.system_messages(comp_inst)
760
+ end
761
+
762
+ raise f[0][:exception] if(f.length > 0)
763
+ end
764
+ reply
765
+ end
766
+
767
+ # Invokes expose_port for a particular dependency on all gears that host it.
768
+ # @param [String] dependency Name of a cartridge
769
+ def expose_port(dependency=nil)
770
+ reply = ResultIO.new
771
+ self.comp_instance_map.each do |comp_inst_name, comp_inst|
772
+ next if !dependency.nil? and (comp_inst.parent_cart_name != dependency)
773
+ next if comp_inst.name == "@@app"
774
+ next if comp_inst.parent_cart_name == self.name
775
+ next if comp_inst.parent_cart_name == self.proxy_cartridge
776
+
777
+ group_inst = self.group_instance_map[comp_inst.group_instance_name]
778
+ s,f = run_on_gears(group_inst.gears, reply, false) do |gear, r|
779
+ r.append gear.expose_port(comp_inst)
780
+ end
781
+
782
+ # Not all cartridges will have this hook.
783
+ f.each do |fail|
784
+ next if fail[:exception].resultIO.exitcode == 127
785
+ raise fail[:exception]
786
+ end
787
+
788
+ end
789
+ reply
790
+ end
791
+
792
+ def conceal_port(dependency=nil)
793
+ reply = ResultIO.new
794
+ self.comp_instance_map.each do |comp_inst_name, comp_inst|
795
+ next if !dependency.nil? and (comp_inst.parent_cart_name != dependency)
796
+ next if comp_inst.name == "@@app"
797
+ next if comp_inst.parent_cart_name == self.name
798
+ next if comp_inst.parent_cart_name == self.proxy_cartridge
799
+
800
+ group_inst = self.group_instance_map[comp_inst.group_instance_name]
801
+ s,f = run_on_gears(group_inst.gears, reply, false) do |gear, r|
802
+ r.append gear.conceal_port(comp_inst)
803
+ end
804
+
805
+ # Not all cartridges will have this hook.
806
+ f.each do |fail|
807
+ next if fail[:exception].resultIO.exitcode == 127
808
+ raise fail[:exception]
809
+ end
810
+
811
+ end
812
+ reply
813
+ end
814
+
815
+ def show_port(dependency=nil)
816
+ reply = ResultIO.new
817
+ self.comp_instance_map.each do |comp_inst_name, comp_inst|
818
+ next if !dependency.nil? and (comp_inst.parent_cart_name != dependency)
819
+ next if comp_inst.name == "@@app"
820
+ next if comp_inst.parent_cart_name == self.name
821
+ next if comp_inst.parent_cart_name == self.proxy_cartridge
822
+
823
+ Rails.logger.debug( comp_inst.inspect )
824
+ Rails.logger.debug( "\n" )
825
+
826
+ group_inst = self.group_instance_map[comp_inst.group_instance_name]
827
+ s,f = run_on_gears(group_inst.gears, reply, false) do |gear, r|
828
+ r.append gear.show_port(comp_inst)
829
+ end
830
+
831
+ # Not all cartridges will have this hook.
832
+ f.each do |fail|
833
+ next if fail[:exception].resultIO.exitcode == 127
834
+ raise fail[:exception]
835
+ end
836
+
837
+ end
838
+ reply
839
+ end
840
+
841
+ # Get the state of the application on all gears.
842
+ def show_state()
843
+ gear_states = {}
844
+ tag = ""
845
+ handle = RemoteJob.create_parallel_job
846
+ RemoteJob.run_parallel_on_gears(self.gears, handle) { |exec_handle, gear|
847
+ job = gear.app_state_job_show()
848
+ RemoteJob.add_parallel_job(exec_handle, tag, gear, job)
849
+ }
850
+ RemoteJob.get_parallel_run_results(handle) { |tag, gear, output, status|
851
+ if status != 0
852
+ Rails.logger.error("Error getting application state from gear: '#{gear}' with status: '#{status}' and output: #{output}")
853
+ gear_states[gear] = 'unknown'
854
+ else
855
+ gear_states[gear] = output
856
+ end
857
+ }
858
+ gear_states
859
+ end
860
+
861
+ def add_node_settings(gears=nil)
862
+ reply = ResultIO.new
863
+
864
+ gears = self.gears unless gears
865
+
866
+ self.ssh_keys = {} unless self.ssh_keys
867
+ if @user.env_vars || @user.ssh_keys || @user.system_ssh_keys
868
+ tag = ""
869
+ handle = RemoteJob.create_parallel_job
870
+ RemoteJob.run_parallel_on_gears(gears, handle) { |exec_handle, gear|
871
+ @user.env_vars.each do |key, value|
872
+ job = gear.env_var_job_add(key, value)
873
+ RemoteJob.add_parallel_job(exec_handle, tag, gear, job)
874
+ end if @user.env_vars
875
+ @user.ssh_keys.each do |key_name, key_info|
876
+ job = gear.ssh_key_job_add(key_info["key"], key_info["type"], key_name)
877
+ RemoteJob.add_parallel_job(exec_handle, tag, gear, job)
878
+ end if @user.ssh_keys
879
+ @user.system_ssh_keys.each do |key_name, key_info|
880
+ job = gear.ssh_key_job_add(key_info, nil, key_name)
881
+ RemoteJob.add_parallel_job(exec_handle, tag, gear, job)
882
+ end if @user.system_ssh_keys
883
+ self.ssh_keys.each do |key_name, key_info|
884
+ job = gear.ssh_key_job_add(key_info, nil, key_name)
885
+ RemoteJob.add_parallel_job(exec_handle, tag, gear, job)
886
+ end
887
+ }
888
+ RemoteJob.get_parallel_run_results(handle) { |tag, gear, output, status|
889
+ if status != 0
890
+ raise OpenShift::NodeException.new("Error applying settings to gear: #{gear} with status: #{status} and output: #{output}", 143)
891
+ end
892
+ }
893
+ end
894
+ reply
895
+ end
896
+
897
+ def add_dns(appname, namespace, public_hostname)
898
+ dns = OpenShift::DnsService.instance
899
+ begin
900
+ dns.register_application(appname, namespace, public_hostname)
901
+ dns.publish
902
+ ensure
903
+ dns.close
904
+ end
905
+ end
906
+
907
+ def create_dns
908
+ reply = ResultIO.new
909
+ self.class.notify_observers(:before_create_dns, {:application => self, :reply => reply})
910
+ public_hostname = self.container.get_public_hostname
911
+
912
+ add_dns(@name, @domain.namespace, public_hostname)
913
+
914
+ self.class.notify_observers(:after_create_dns, {:application => self, :reply => reply})
915
+ reply
916
+ end
917
+
918
+ def destroy_dns
919
+ reply = ResultIO.new
920
+ self.class.notify_observers(:before_destroy_dns, {:application => self, :reply => reply})
921
+ dns = OpenShift::DnsService.instance
922
+ begin
923
+ dns.deregister_application(@name,@domain.namespace)
924
+ if self.scalable
925
+ # find the group instance where the web-cartridge is residing
926
+ self.group_instance_map.keys.each { |ginst_name|
927
+ ginst = self.group_instance_map[ginst_name]
928
+ ginst.gears.each { |gear|
929
+ dns.deregister_application(gear.name,@domain.namespace)
930
+ }
931
+ }
932
+ end
933
+ dns.publish
934
+ ensure
935
+ dns.close
936
+ end
937
+ self.class.notify_observers(:after_destroy_dns, {:application => self, :reply => reply})
938
+ reply
939
+ end
940
+
941
+ def recreate_dns
942
+ reply = ResultIO.new
943
+ self.class.notify_observers(:before_recreate_dns, {:application => self, :reply => reply})
944
+ dns = OpenShift::DnsService.instance
945
+ begin
946
+ public_hostname = self.container.get_public_hostname
947
+ dns.modify_application(@name, @domain.namespace, public_hostname)
948
+ dns.publish
949
+ ensure
950
+ dns.close
951
+ end
952
+ self.class.notify_observers(:after_recreate_dns, {:application => self, :reply => reply})
953
+ reply
954
+ end
955
+
956
+ def get_user_min_max(cart_group_map)
957
+ sup_min = 0
958
+ sup_max = nil
959
+ cart_current_min = 0
960
+ cart_current_max = nil
961
+ cart_group_map.each do |group_name, component_instance_list|
962
+ ginst = self.group_instance_map[group_name]
963
+ sup_min += ginst.supported_min
964
+ cart_current_min += ginst.min
965
+ if sup_max.nil? or ginst.supported_max==-1
966
+ sup_max = ginst.supported_max
967
+ else
968
+ sup_max += ginst.supported_max unless sup_max==-1
969
+ end
970
+ if cart_current_max.nil? or ginst.max==-1
971
+ cart_current_max = ginst.max
972
+ else
973
+ cart_current_max += ginst.max unless cart_current_max==-1
974
+ end
975
+ end
976
+ return cart_current_min, cart_current_max, sup_min, sup_max
977
+ end
978
+
979
+ def set_user_min_max(cart_group_map, min_scale, max_scale)
980
+ if min_scale and max_scale and Integer(min_scale) > Integer(max_scale) and Integer(max_scale)!=-1
981
+ #raise OpenShift::UserException.new("Invalid scaling factors provided. Minimum (#{min_scale}) should always be less than maximum (#{max_scale}).", 170)
982
+ tmp = min_scale
983
+ min_scale = max_scale
984
+ max_scale = tmp
985
+ end
986
+ cart_current_min, cart_current_max, sup_min, sup_max = get_user_min_max(cart_group_map)
987
+ if min_scale and Integer(min_scale)-cart_current_min<0
988
+ # set min first
989
+ set_user_min(cart_group_map, min_scale)
990
+ set_user_max(cart_group_map, max_scale)
991
+ else
992
+ set_user_max(cart_group_map, max_scale)
993
+ set_user_min(cart_group_map, min_scale)
994
+ end
995
+
996
+ if self.scalable
997
+ prof = @profile_name_map[@default_profile]
998
+ cinst = ComponentInstance::find_component_in_cart(prof, self, self.proxy_cartridge, self.get_name_prefix)
999
+ if cinst
1000
+ group_inst = self.group_instance_map[cinst.group_instance_name]
1001
+ reply = ResultIO.new
1002
+ s,f = run_on_gears(group_inst.gears, reply, false) do |gear, r|
1003
+ gear.reload(cinst)
1004
+ end
1005
+ end
1006
+ end
1007
+
1008
+ end
1009
+
1010
+ def set_user_min(cart_group_map, min_scale)
1011
+ return if not min_scale
1012
+ cart_current_min, cart_current_max, sup_min, sup_max = get_user_min_max(cart_group_map)
1013
+ cart_current_max = 1000000 if cart_current_max==-1
1014
+ if (Integer(min_scale) < sup_min or Integer(min_scale) > cart_current_max)
1015
+ raise OpenShift::UserException.new("Invalid scales_from factor #{min_scale} provided. Value out of allowed range ( #{sup_min} : #{cart_current_max==1000000 ? -1 : cart_current_max} ).", 168)
1016
+ end
1017
+ target_min = Integer(min_scale) - cart_current_min
1018
+ iter = cart_group_map.keys.each
1019
+ while target_min != 0 do
1020
+ begin
1021
+ group_name = iter.next
1022
+ break if group_name.nil?
1023
+ rescue Exception=>e
1024
+ break
1025
+ end
1026
+ ginst = self.group_instance_map[group_name]
1027
+ ginst_max = ginst.max
1028
+ ginst_max = 1000000 if ginst.max==-1
1029
+ if target_min > 0
1030
+ if (ginst_max-ginst.min)>target_min
1031
+ ginst.min += target_min
1032
+ target_min = 0
1033
+ else
1034
+ target_min -= (ginst_max-ginst.min)
1035
+ ginst.min = ginst_max
1036
+ end
1037
+ else
1038
+ if (ginst.supported_min-ginst.min) < target_min
1039
+ ginst.min += target_min
1040
+ target_min = 0
1041
+ else
1042
+ target_min += (ginst.min-ginst.supported_min)
1043
+ ginst.min = ginst.supported_min
1044
+ end
1045
+ end
1046
+ end
1047
+ self.save
1048
+ if target_min != 0
1049
+ raise OpenShift::UserException.new("Could not completely distribute scales_from to all groups. Value constrained to #{Integer(min_scale)-target_min}", 169)
1050
+ end
1051
+ end
1052
+
1053
+ def set_user_max(cart_group_map, max_scale)
1054
+ return if not max_scale
1055
+ cart_current_min, cart_current_max, sup_min, sup_max = get_user_min_max(cart_group_map)
1056
+ sup_max = 1000000 if sup_max==-1
1057
+ max_scale_int = Integer(max_scale)
1058
+ max_scale_int = 1000000 if max_scale_int==-1
1059
+ if (max_scale_int and ( max_scale_int > sup_max or max_scale_int < cart_current_min) )
1060
+ raise OpenShift::UserException.new("Invalid scales_to factor #{max_scale} provided. Value out of allowed range ( #{cart_current_min} : #{sup_max==1000000 ? -1 : sup_max} ).", 168)
1061
+ end
1062
+ target_max = Integer(max_scale)
1063
+ cart_group_map.keys.each { |group_name, component_instances|
1064
+ gi = self.group_instance_map[group_name]
1065
+ if target_max==-1
1066
+ next if gi.supported_max!=-1
1067
+ gi.max = target_max
1068
+ break
1069
+ end
1070
+ if gi.supported_max==-1 or( (gi.supported_max-gi.min) > target_max )
1071
+ rest_total = 0
1072
+ cart_group_map.keys.each { |other_group_name|
1073
+ next if other_group_name==group_name
1074
+ other_gi = self.group_instance_map[other_group_name]
1075
+ if other_gi.max == -1
1076
+ other_gi.max==other_gi.min
1077
+ end
1078
+ rest_total += other_gi.max
1079
+ }
1080
+ gi.max = (target_max-rest_total)
1081
+ break
1082
+ end
1083
+ }
1084
+ self.save
1085
+ end
1086
+
1087
+ def prepare_namespace_update(dns_service, new_ns, old_ns)
1088
+ updated = true
1089
+ result_io = ResultIO.new
1090
+ begin
1091
+ self.gears.each do |gear|
1092
+ gear_result_io = gear.prepare_namespace_update(dns_service, new_ns, old_ns)
1093
+ updated = false unless gear_result_io.exitcode == 0
1094
+ result_io.append gear_result_io
1095
+ end
1096
+ rescue Exception => e
1097
+ updated = false
1098
+ Rails.logger.debug "Exception caught updating namespace: #{e.message}"
1099
+ Rails.logger.debug e.backtrace
1100
+ result_io.append e.resultIO if e.respond_to?('resultIO') and e.resultIO
1101
+ end
1102
+ return { :success => updated, :result_io => result_io }
1103
+ end
1104
+
1105
+ def complete_namespace_update(new_ns, old_ns)
1106
+ self.comp_instances.each do |comp_inst|
1107
+ comp_inst.cart_properties.each do |prop_key, prop_value|
1108
+ comp_inst.cart_properties[prop_key] = prop_value.gsub(/-#{old_ns}.#{Rails.configuration.openshift[:domain_suffix]}/, "-#{new_ns}.#{Rails.configuration.openshift[:domain_suffix]}")
1109
+ end
1110
+ end
1111
+ self.embedded.each_key do |framework|
1112
+ if self.embedded[framework].has_key?('info')
1113
+ info = self.embedded[framework]['info']
1114
+ info.gsub!(/-#{old_ns}.#{Rails.configuration.openshift[:domain_suffix]}/, "-#{new_ns}.#{Rails.configuration.openshift[:domain_suffix]}")
1115
+ self.embedded[framework]['info'] = info
1116
+ end
1117
+ end
1118
+
1119
+ # elaborate descriptor again to execute connections, because connections need to be renewed
1120
+ self.elaborate_descriptor
1121
+ self.execute_connections
1122
+ self.domain.namespace = new_ns
1123
+ self.save
1124
+ end
1125
+
1126
+ def add_alias(t_server_alias)
1127
+ # Server aliases validate as DNS host names in accordance with RFC
1128
+ # 1123 and RFC 952. Additionally, OpenShift does not allow an
1129
+ # Alias to be an IP address or a host in the service domain.
1130
+ # Since DNS is case insensitive, all names are downcased for
1131
+ # indexing/compares.
1132
+ server_alias = t_server_alias.downcase
1133
+ if !(server_alias =~ /\A[0-9a-zA-Z\-\.]+\z/) or
1134
+ (server_alias =~ /#{Rails.configuration.openshift[:domain_suffix]}$/) or
1135
+ (server_alias.length > 255 ) or
1136
+ (server_alias.length == 0 ) or
1137
+ (server_alias =~ /^\d+\.\d+\.\d+\.\d+$/)
1138
+ raise OpenShift::UserException.new("Invalid Server Alias '#{t_server_alias}' specified", 105)
1139
+ end
1140
+
1141
+ self.aliases = [] unless self.aliases
1142
+ raise OpenShift::UserException.new("Alias '#{server_alias}' already exists for '#{@name}'", 255) if self.aliases.include? server_alias
1143
+ reply = ResultIO.new
1144
+ begin
1145
+ self.aliases.push(server_alias)
1146
+ self.save
1147
+ reply.append self.container.add_alias(self, self.gear, server_alias)
1148
+ rescue Exception => e
1149
+ Rails.logger.debug e.message
1150
+ Rails.logger.debug e.backtrace.inspect
1151
+ reply.append self.container.remove_alias(self, self.gear, server_alias)
1152
+ self.aliases.delete(server_alias)
1153
+ self.save
1154
+ raise
1155
+ end
1156
+ reply
1157
+ end
1158
+
1159
+ def remove_alias(t_server_alias)
1160
+ server_alias = t_server_alias.downcase
1161
+ self.aliases = [] unless self.aliases
1162
+ reply = ResultIO.new
1163
+ begin
1164
+ reply.append self.container.remove_alias(self, self.gear, server_alias)
1165
+ rescue Exception => e
1166
+ Rails.logger.debug e.message
1167
+ Rails.logger.debug e.backtrace.inspect
1168
+ raise
1169
+ ensure
1170
+ if self.aliases.include? server_alias
1171
+ self.aliases.delete(server_alias)
1172
+ self.save
1173
+ else
1174
+ raise OpenShift::UserException.new("Alias '#{server_alias}' does not exist for '#{@name}'", 255, reply)
1175
+ end
1176
+ end
1177
+ reply
1178
+ end
1179
+
1180
+ def add_dependency(dep)
1181
+ reply = ResultIO.new
1182
+ self.class.notify_observers(:before_add_dependency, {:application => self, :dependency => dep, :reply => reply})
1183
+ # Create persistent storage app entry on configure (one of the first things)
1184
+ Rails.logger.debug "DEBUG: Adding embedded app info from persistent storage: #{@name}:#{dep}"
1185
+ self.cart_data = {} if @cart_data.nil?
1186
+
1187
+ raise OpenShift::UserException.new("#{dep} already embedded in '#{@name}'", 136) if self.embedded.include? dep
1188
+ if self.scalable
1189
+ allowed_cartridges = SCALABLE_EMBEDDED_CARTS & Application.get_available_cartridges.sort
1190
+ raise OpenShift::UserException.new("#{dep} cannot be embedded in scalable app '#{@name}'. Allowed cartridges: #{allowed_cartridges.join(', ')}", 108) if not SCALABLE_EMBEDDED_CARTS.include? dep
1191
+ end
1192
+ add_to_requires_feature(dep)
1193
+ begin
1194
+ reply.append self.configure_dependencies
1195
+ self.execute_connections
1196
+ rescue Exception => e
1197
+ remove_from_requires_feature(dep)
1198
+ self.elaborate_descriptor
1199
+ self.save
1200
+ raise e
1201
+ end
1202
+
1203
+ self.class.notify_observers(:after_add_dependency, {:application => self, :dependency => dep, :reply => reply})
1204
+ reply
1205
+ end
1206
+
1207
+ def remove_dependency(dep)
1208
+ reply = ResultIO.new
1209
+ self.class.notify_observers(:before_remove_dependency, {:application => self, :dependency => dep, :reply => reply})
1210
+ self.embedded = {} unless self.embedded
1211
+
1212
+ raise OpenShift::UserException.new("#{dep} not embedded in '#{@name}', try adding it first", 135) unless self.embedded.include? dep
1213
+ raise OpenShift::UserException.new("#{dep} is not allowed to be removed from '#{@name}'. It is a required dependency for a scalable application.", 137) if (self.scalable and self.proxy_cartridge==dep)
1214
+ remove_from_requires_feature(dep)
1215
+ elaborate_descriptor { |removed_component_instances|
1216
+ #remove unused components
1217
+ removed_component_instances.each do |comp_inst_name|
1218
+ comp_inst = self.comp_instance_map[comp_inst_name]
1219
+ next if comp_inst.parent_cart_name == self.name
1220
+ group_inst = self.group_instance_map[comp_inst.group_instance_name]
1221
+ s,f = run_on_gears(group_inst.gears, reply, false) do |gear, r|
1222
+ unless gear.configured_components.length == 1 && gear.configured_components.first == comp_inst.name
1223
+ reply.append gear.deconfigure(comp_inst)
1224
+ process_cartridge_commands(r)
1225
+ end
1226
+ end
1227
+
1228
+ f.each do |failed_data|
1229
+ Rails.logger.debug("Failed to deconfigure cartridge #{comp_inst.parent_cart_name} on gear #{failed_data[:gear].server_identity}:#{failed_data[:gear].uuid}")
1230
+ Rails.logger.debug("Exception #{failed_data[:exception].message}")
1231
+ Rails.logger.debug("#{failed_data[:exception].backtrace.inspect}")
1232
+ end
1233
+
1234
+ run_on_gears(group_inst.gears, reply, false) do |gear, r|
1235
+ if gear.configured_components.empty? || (gear.configured_components.length == 1 && gear.configured_components.first == comp_inst.name)
1236
+ reply.append group_inst.remove_gear(gear)
1237
+ end
1238
+ end
1239
+
1240
+ if f.length > 0
1241
+ raise Exception.new("Failed to remove #{dep} from application #{self.name}. Try again or report to OpenShift Support.")
1242
+ end
1243
+ end
1244
+ }
1245
+ self.save
1246
+ self.class.notify_observers(:after_remove_dependency, {:application => self, :dependency => dep, :reply => reply})
1247
+ reply
1248
+ end
1249
+
1250
+ def get_public_ip_address
1251
+ begin
1252
+ return self.container.get_public_ip_address
1253
+ rescue Exception=>e
1254
+ Rails.logger.debug e.backtrace.inspect
1255
+ return nil
1256
+ end
1257
+ end
1258
+
1259
+ # Returns the first Gear object on which the application is running
1260
+ # @return [Gear]
1261
+ # @deprecated
1262
+ def gear
1263
+ if self.group_instances.nil?
1264
+ elaborate_descriptor
1265
+ end
1266
+
1267
+ if scalable
1268
+ self.group_instance_map.keys.each { |ginst_name|
1269
+ return self.group_instance_map[ginst_name].gears.first if ginst_name.include? self.proxy_cartridge
1270
+ }
1271
+ end
1272
+
1273
+ group_instance = self.group_instances.first
1274
+ return nil unless group_instance
1275
+
1276
+ return group_instance.gears.first
1277
+ end
1278
+
1279
+ def scaling_limits(dependency=nil)
1280
+ if dependency.nil?
1281
+ if self.scalable
1282
+ dependency = "web"
1283
+ else
1284
+ dependency = self.framework
1285
+ end
1286
+ end
1287
+ prof = @profile_name_map[@default_profile]
1288
+ cinst = ComponentInstance::find_component_in_cart(prof, self, dependency, self.get_name_prefix)
1289
+ raise OpenShift::NodeException.new("Cannot find #{dependency} component in app #{self.name}.", 135, ResultIO.new) if cinst.nil?
1290
+
1291
+ ginst = self.group_instance_map[cinst.group_instance_name]
1292
+ return ginst.min,ginst.max
1293
+ end
1294
+
1295
+ # Get the ApplicationContainerProxy object for the first gear the application is running on
1296
+ # @return [ApplicationContainerProxy]
1297
+ # @deprecated
1298
+ def container
1299
+ return nil if self.gear.nil?
1300
+ return self.gear.get_proxy
1301
+ end
1302
+
1303
+ # Get the name of framework cartridge in use by the application without the version suffix
1304
+ # @return [String]
1305
+ # @deprecated
1306
+ def framework_cartridge
1307
+ fcart = self.framework
1308
+ return fcart.split('-')[0..-2].join('-') unless fcart.nil?
1309
+ return nil
1310
+ end
1311
+
1312
+ # Get the name of framework cartridge in use by the application
1313
+ # @return [String]
1314
+ # @deprecated
1315
+ def framework
1316
+ framework_carts = CartridgeCache.cartridge_names('standalone')
1317
+ self.comp_instance_map.each { |cname, cinst|
1318
+ cartname = cinst.parent_cart_name
1319
+ return cartname if framework_carts.include? cartname
1320
+ }
1321
+ return nil
1322
+ end
1323
+
1324
+ # Provide a list of direct dependencies of the application that are hosted on the same gear as the "framework" cartridge.
1325
+ # @return [Array<String>]
1326
+ # @deprecated
1327
+ def embedded
1328
+ embedded_carts = CartridgeCache.cartridge_names('embedded')
1329
+ retval = {}
1330
+ self.comp_instance_map.values.each do |comp_inst|
1331
+ if embedded_carts.include?(comp_inst.parent_cart_name)
1332
+ if comp_inst.cart_data.first.nil?
1333
+ retval[comp_inst.parent_cart_name] = comp_inst.cart_properties
1334
+ else
1335
+ retval[comp_inst.parent_cart_name] = comp_inst.cart_properties.merge({"info" => comp_inst.cart_data.first})
1336
+ end
1337
+ end
1338
+ end
1339
+ retval
1340
+ end
1341
+
1342
+ # Provide a way of updating the component information for a given cartridge
1343
+ # @deprecated
1344
+ def set_embedded_cart_info(cart_name, info)
1345
+ self.comp_instance_map.values.each do |comp_inst|
1346
+ comp_inst.cart_data = [info] if cart_name == comp_inst.parent_cart_name
1347
+ end
1348
+ end
1349
+
1350
+ # Provides an array version of the component instance map for saving in the datastore.
1351
+ # @return [Array<Hash>]
1352
+ def comp_instances
1353
+ @comp_instance_map = {} if @comp_instance_map.nil?
1354
+ @comp_instance_map.values
1355
+ end
1356
+
1357
+ # Rebuilds the component instance map from an array of hashes or objects
1358
+ # @param [Array<Hash>] data
1359
+ def comp_instances=(data)
1360
+ comp_instance_map_will_change!
1361
+ @comp_instance_map = {} if @comp_instance_map.nil?
1362
+ data.each do |value|
1363
+ if value.class == ComponentInstance
1364
+ @comp_instance_map[value.name] = value
1365
+ else
1366
+ key = value["name"]
1367
+ @comp_instance_map[key] = ComponentInstance.new
1368
+ @comp_instance_map[key].attributes=value
1369
+ end
1370
+ end
1371
+ end
1372
+
1373
+ # Provides an array version of the group instance map for saving in the datastore.
1374
+ # @return [Array<Hash>]
1375
+ def group_instances
1376
+ @group_instance_map = {} if @group_instance_map.nil?
1377
+ values = @group_instance_map.values.uniq
1378
+ keys = @group_instance_map.keys
1379
+
1380
+ values.each do |group_inst|
1381
+ group_inst.reused_by = keys.clone.delete_if{ |k| @group_instance_map[k] != group_inst }
1382
+ end
1383
+
1384
+ values
1385
+ end
1386
+
1387
+ # Rebuilds the group instance map from an array of hashes or objects
1388
+ # @param [Array<Hash>] data
1389
+ def group_instances=(data)
1390
+ group_instance_map_will_change!
1391
+ @group_instance_map = {} if @group_instance_map.nil?
1392
+ data.each do |value|
1393
+ if value.class == GroupInstance
1394
+ value.reused_by.each do |k|
1395
+ @group_instance_map[k] = value
1396
+ end
1397
+ else
1398
+ ginst = GroupInstance.new(self)
1399
+ ginst.attributes=value
1400
+ ginst.reused_by.each do |k|
1401
+ @group_instance_map[k] = ginst
1402
+ end
1403
+ end
1404
+ end
1405
+ end
1406
+
1407
+ def get_name_prefix
1408
+ return "@@app"
1409
+ end
1410
+
1411
+ def add_group_override(from, to)
1412
+ prof = @profile_name_map[@default_profile]
1413
+ prof.group_overrides = [] if prof.group_overrides.nil?
1414
+ prof.group_overrides << [from, to]
1415
+ end
1416
+
1417
+ # Parse the descriptor and build or update the runtime descriptor structure
1418
+ def elaborate_descriptor
1419
+ self.group_instance_map = {} if group_instance_map.nil?
1420
+ self.comp_instance_map = {} if comp_instance_map.nil?
1421
+ self.working_comp_inst_hash = {}
1422
+ self.working_group_inst_hash = {}
1423
+ self.group_override_map = {}
1424
+ self.conn_endpoints_list = []
1425
+ default_profile = @profile_name_map[@default_profile]
1426
+
1427
+ default_profile.groups.each { |g|
1428
+ #gpath = self.name + "." + g.name
1429
+ gpath = self.get_name_prefix + g.get_name_prefix
1430
+ gi = working_group_inst_hash[gpath]
1431
+ if gi.nil?
1432
+ gi = self.group_instance_map[gpath]
1433
+ if gi.nil?
1434
+ gi = GroupInstance.new(self, self.name, self.default_profile, g.name, gpath)
1435
+ else
1436
+ gi.merge(self.name, self.default_profile, g.name, gpath)
1437
+ end
1438
+ else
1439
+ gi.merge(self.name, self.default_profile, g.name, gpath)
1440
+ end
1441
+ self.group_instance_map[gpath] = gi
1442
+ self.working_group_inst_hash[gpath] = gi
1443
+ gi.elaborate(default_profile, g, self.get_name_prefix, self)
1444
+ }
1445
+
1446
+ # make connection_endpoints out of provided connections
1447
+ default_profile.connections.each { |conn|
1448
+ inst1 = ComponentInstance::find_component_in_cart(default_profile, self, conn.components[0], self.get_name_prefix)
1449
+ inst2 = ComponentInstance::find_component_in_cart(default_profile, self, conn.components[1], self.get_name_prefix)
1450
+ ComponentInstance::establish_connections(inst1, inst2, self)
1451
+ }
1452
+ # check self.comp_instance_map for component instances
1453
+ # check self.group_instance_map for group instances
1454
+ # check self.conn_endpoints_list for list of connection endpoints (fully resolved)
1455
+
1456
+ # resolve group co-locations
1457
+ colocate_groups
1458
+
1459
+ # get configure_order and start_order
1460
+ get_exec_order(default_profile)
1461
+
1462
+ deleted_components_list = []
1463
+ self.comp_instance_map.each { |k,v| deleted_components_list << k if self.working_comp_inst_hash[k].nil? }
1464
+
1465
+ yield deleted_components_list if block_given?
1466
+
1467
+ # delete entries in {group,comp}_instance_map that do
1468
+ # not exist in working_{group,comp}_inst_hash
1469
+ self.group_instance_map.delete_if { |k,v|
1470
+ v.component_instances.delete(k) if self.working_comp_inst_hash[k].nil? and v.component_instances.include?(k)
1471
+ self.working_group_inst_hash[k].nil?
1472
+ }
1473
+ self.comp_instance_map.delete_if { |k,v| self.working_comp_inst_hash[k].nil? }
1474
+ end
1475
+
1476
+ # Get path for checking application health
1477
+ # @return [String]
1478
+ def health_check_path
1479
+ case self.framework_cartridge
1480
+ when 'php'
1481
+ page = 'health_check.php'
1482
+ when 'zend'
1483
+ page = 'health_check.php'
1484
+ when 'perl'
1485
+ page = 'health_check.pl'
1486
+ else
1487
+ page = 'health'
1488
+ end
1489
+ end
1490
+
1491
+ def process_cartridge_commands(result)
1492
+ commands = result.cart_commands
1493
+ self.ssh_keys = {} unless self.ssh_keys
1494
+ app_jobs = { 'add_ssh_keys' => [], 'remove_ssh_keys' => [], 'remove_env_vars' => [] }
1495
+ commands.each do |command_item|
1496
+ case command_item[:command]
1497
+ when "SYSTEM_SSH_KEY_ADD"
1498
+ key = command_item[:args][0]
1499
+ self.user.add_system_ssh_key(self.name, key)
1500
+ when "SYSTEM_SSH_KEY_REMOVE"
1501
+ self.user.remove_system_ssh_key(self.name)
1502
+ when "APP_SSH_KEY_ADD"
1503
+ key_name = command_item[:args][0]
1504
+ key = command_item[:args][1]
1505
+ self.ssh_keys[key_name] = key
1506
+ app_jobs['add_ssh_keys'] << [key_name,key]
1507
+ when "APP_SSH_KEY_REMOVE"
1508
+ key_name = command_item[:args][0]
1509
+ key = self.ssh_keys.delete(key_name)
1510
+ app_jobs['remove_ssh_keys'] << key unless key.nil?
1511
+ when "ENV_VAR_ADD"
1512
+ key = command_item[:args][0]
1513
+ value = command_item[:args][1]
1514
+ self.user.add_env_var(key,value)
1515
+ when "ENV_VAR_REMOVE"
1516
+ key = command_item[:args][0]
1517
+ self.user.remove_env_var(key)
1518
+ when "APP_ENV_VAR_REMOVE"
1519
+ key = command_item[:args][0]
1520
+ app_jobs['remove_env_vars'] << key unless key.nil?
1521
+ when "BROKER_KEY_ADD"
1522
+ iv, token = OpenShift::AuthService.instance.generate_broker_key(self)
1523
+ self.user.add_save_job('adds', 'broker_auth_keys', [self.uuid, iv, token])
1524
+ when "BROKER_KEY_REMOVE"
1525
+ self.user.add_save_job('removes', 'broker_auth_keys', [self.uuid])
1526
+ end
1527
+ end
1528
+ if user.save_jobs
1529
+ user.save
1530
+ end
1531
+ handle = RemoteJob.create_parallel_job
1532
+ tag = ""
1533
+ RemoteJob.run_parallel_on_gears(self.gears, handle) { |exec_handle, gear|
1534
+ app_jobs.each do |action,value|
1535
+ case action
1536
+ when "remove_env_vars"
1537
+ value.each { |key|
1538
+ job = gear.env_var_job_remove(key)
1539
+ RemoteJob.add_parallel_job(exec_handle, tag, gear, job)
1540
+ }
1541
+ when "add_ssh_keys"
1542
+ value.each { |key_info|
1543
+ key_name,key = key_info
1544
+ job = gear.ssh_key_job_add(key, nil, key_name)
1545
+ RemoteJob.add_parallel_job(exec_handle, tag, gear, job)
1546
+ }
1547
+ when "remove_ssh_keys"
1548
+ value.each { |key|
1549
+ job = gear.ssh_key_job_remove(key, nil)
1550
+ RemoteJob.add_parallel_job(exec_handle, tag, gear, job)
1551
+ }
1552
+ end
1553
+ end
1554
+ }
1555
+ RemoteJob.get_parallel_run_results(handle) { |tag, gear, output, status|
1556
+ if status != 0
1557
+ raise OpenShift::NodeException.new("Error updating settings on gear: #{gear} with status: #{status} and output: #{output}", 143)
1558
+ end
1559
+ }
1560
+ commands.clear
1561
+ end
1562
+
1563
+ def track_usage(gear, event, usage_type=UsageRecord::USAGE_TYPES[:gear_usage])
1564
+ if Rails.configuration.usage_tracking[:datastore_enabled]
1565
+ now = Time.now.utc
1566
+ uuid = OpenShift::Model.gen_uuid
1567
+ self.usage_records = [] unless usage_records
1568
+ usage_record = UsageRecord.new(event, user, now, uuid, usage_type)
1569
+ case usage_type
1570
+ when UsageRecord::USAGE_TYPES[:gear_usage]
1571
+ usage_record.gear_uuid = gear.uuid
1572
+ usage_record.gear_size = gear.node_profile
1573
+ when UsageRecord::USAGE_TYPES[:addtl_fs_gb]
1574
+ usage_record.gear_uuid = gear.uuid
1575
+ usage_record.addtl_fs_gb = gear.group_instance.addtl_fs_gb
1576
+ end
1577
+ self.usage_records << usage_record
1578
+
1579
+ self.class.notify_observers(:track_usage, {:gear_uuid => gear.uuid, :login => gear.app.user.login, :event => event, :time => now, :uuid => uuid, :usage_type => usage_type, :gear_size => gear.node_profile, :addtl_fs_gb => gear.group_instance.addtl_fs_gb})
1580
+ end
1581
+ if Rails.configuration.usage_tracking[:syslog_enabled]
1582
+ usage_string = "User: #{user.login} Event: #{event}"
1583
+ case usage_type
1584
+ when UsageRecord::USAGE_TYPES[:gear_usage]
1585
+ usage_string += " Gear: #{gear.uuid} Gear Size: #{gear.node_profile}"
1586
+ when UsageRecord::USAGE_TYPES[:addtl_fs_gb]
1587
+ usage_string += " Gear: #{gear.uuid} Addtl File System GB: #{gear.group_instance.addtl_fs_gb}"
1588
+ end
1589
+ begin
1590
+ Syslog.open('openshift_usage', Syslog::LOG_PID) { |s| s.notice usage_string }
1591
+ rescue Exception => e
1592
+ # Can't fail because of a secondary logging error
1593
+ Rails.logger.error e.message
1594
+ Rails.logger.error e.backtrace
1595
+ end
1596
+ end
1597
+ end
1598
+
1599
+ private
1600
+
1601
+ def get_exec_order(default_profile)
1602
+ self.configure_order = []
1603
+ default_profile.configure_order.each { |raw_c_name|
1604
+ cinst = ComponentInstance::find_component_in_cart(default_profile, self, raw_c_name, self.get_name_prefix)
1605
+ next if cinst.nil?
1606
+ ComponentInstance::collect_exec_order(self, cinst, self.configure_order)
1607
+ self.configure_order << cinst.name if not self.configure_order.include? cinst.name
1608
+ }
1609
+ default_profile.groups.each { |g|
1610
+ g.component_refs.each { |cr|
1611
+ cpath = self.get_name_prefix + cr.get_name_prefix(default_profile)
1612
+ cinst = self.comp_instance_map[cpath]
1613
+ ComponentInstance::collect_exec_order(self, cinst, self.configure_order)
1614
+ self.configure_order << cpath if not self.configure_order.include? cpath
1615
+ }
1616
+ }
1617
+ self.start_order = self.configure_order
1618
+ end
1619
+
1620
+ def colocate_groups
1621
+ default_profile = @profile_name_map[@default_profile]
1622
+ self.conn_endpoints_list.each { |conn|
1623
+ if conn.from_connector.type.match(/^FILESYSTEM/) or conn.from_connector.type.match(/^AFUNIX/)
1624
+ cinst1 = self.comp_instance_map[conn.from_comp_inst]
1625
+ ginst1 = self.group_instance_map[cinst1.group_instance_name]
1626
+ cinst2 = self.comp_instance_map[conn.to_comp_inst]
1627
+ ginst2 = self.group_instance_map[cinst2.group_instance_name]
1628
+ next if ginst1==ginst2
1629
+ # these two group instances need to be colocated
1630
+ ginst1.merge_inst(ginst2)
1631
+ self.group_instance_map[cinst2.group_instance_name] = ginst1
1632
+ end
1633
+ }
1634
+ generate_group_overrides(default_profile)
1635
+ auto_merge_top_groups(default_profile)
1636
+ end
1637
+
1638
+ def generate_group_overrides(default_profile)
1639
+ default_profile.group_overrides.each do |go|
1640
+ go_copy = go.dup
1641
+ n = go_copy.pop
1642
+ go_copy.each { |v|
1643
+ from_cinst = ComponentInstance::find_component_in_cart(default_profile, self, v, self.get_name_prefix)
1644
+ to_cinst = ComponentInstance::find_component_in_cart(default_profile, self, n, self.get_name_prefix)
1645
+ next if from_cinst.nil? or to_cinst.nil?
1646
+ from_gpath = from_cinst.group_instance_name
1647
+ to_gpath = to_cinst.group_instance_name
1648
+ group_override_map[from_gpath] = to_gpath
1649
+ group_override_map[to_gpath] = from_gpath
1650
+ }
1651
+ end
1652
+ end
1653
+
1654
+ def auto_merge_top_groups(default_profile)
1655
+ if self.scalable
1656
+ group_name_list = self.group_instance_map.keys.dup
1657
+ group_name_list.each { |gname|
1658
+ mapped_to = group_override_map[gname]
1659
+ next if mapped_to.nil?
1660
+ ginst1 = self.group_instance_map[gname]
1661
+ ginst2 = self.group_instance_map[mapped_to]
1662
+ next if ginst1==ginst2
1663
+ ginst1.merge_inst(ginst2)
1664
+ self.group_instance_map[mapped_to] = ginst1
1665
+ }
1666
+ else
1667
+ first_group = default_profile.groups[0]
1668
+ gpath = self.get_name_prefix + first_group.get_name_prefix
1669
+ gi = self.group_instance_map[gpath]
1670
+ first_group.component_refs.each { |comp_ref|
1671
+ cpath = self.get_name_prefix + comp_ref.get_name_prefix(default_profile)
1672
+ ci = self.comp_instance_map[cpath]
1673
+ ci.dependencies.each { |cdep|
1674
+ cdepinst = self.comp_instance_map[cdep]
1675
+ ginst = self.group_instance_map[cdepinst.group_instance_name]
1676
+ next if ginst==gi
1677
+ Rails.logger.debug "Auto-merging group #{ginst.name} into #{gi.name}"
1678
+ # merge ginst into gi
1679
+ #gi.merge(ginst.cart_name, ginst.profile_name, ginst.group_name, ginst.name, ginst.component_instances)
1680
+ gi.merge_inst(ginst)
1681
+ self.group_instance_map[cdepinst.group_instance_name] = gi
1682
+ }
1683
+ }
1684
+ end
1685
+ end
1686
+
1687
+
1688
+ # Runs the provided block on a set of containers
1689
+ # @param [Array<Gear>] Array of containers to run the block on. If nil, will run on all containers.
1690
+ # @param [Boolean] fail_fast Stop running immediately if an exception is raised
1691
+ # @param [Block]
1692
+ # @return [<successful_runs, failed_runs>] List of containers where the runs succeeded/failed
1693
+ def run_on_gears(gears=nil, result_io = nil, fail_fast=true, &block)
1694
+ successful_runs = []
1695
+ failed_runs = []
1696
+ gears = self.gears if gears.nil?
1697
+
1698
+ gears.dup.each do |gear|
1699
+ begin
1700
+ retval = block.call(gear, result_io)
1701
+ successful_runs.push({:gear => gear, :return => retval})
1702
+ rescue Exception => e
1703
+ Rails.logger.error e.message
1704
+ Rails.logger.error e.inspect
1705
+ Rails.logger.error e.backtrace.inspect
1706
+ failed_runs.push({:gear => gear, :exception => e})
1707
+ if (!result_io.nil? && e.kind_of?(OpenShift::OOException) && !e.resultIO.nil?)
1708
+ result_io.append(e.resultIO)
1709
+ end
1710
+ if fail_fast
1711
+ raise OpenShift::GearsException.new(successful_runs, failed_runs, e)
1712
+ end
1713
+ end
1714
+ end
1715
+
1716
+ return successful_runs, failed_runs
1717
+ end
1718
+ end