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,54 @@
1
+ require 'rubygems'
2
+ require 'openshift-origin-controller'
3
+ require 'openshift-origin-dns-bind'
4
+
5
+ module DnsHelper
6
+ #
7
+ # Utility functions for checking namespace availability and removing dns entries
8
+ #
9
+
10
+ $dns_con = nil
11
+
12
+ def dns_service
13
+ if not $dns_con
14
+ $dns_con = OpenShift::BindPlugin.new({:server => "127.0.0.1",
15
+ :port => 53,
16
+ :keyname => "example.com",
17
+ :keyvalue => $bind_keyvalue,
18
+ :domain_suffix => $domain,
19
+ :zone => "example.com"})
20
+ end
21
+ $dns_con
22
+ end
23
+
24
+ def namespace_available?(namespace)
25
+ return dns_service.namespace_available?(namespace)
26
+ end
27
+
28
+ # Public: Removes DNS entries
29
+ #
30
+ # entries - The Array of entries to remove
31
+ #
32
+ # Examples
33
+ #
34
+ # remove_dns_entries(['myapp-test2.dev.rhcloud.com'])
35
+ # # => true
36
+ #
37
+ # Returns entries Array on success
38
+ def remove_dns_entries(entries=[])
39
+ if not entries.empty?
40
+ entries.each do |domain|
41
+ yes = dns_service.namespace_available?(domain)
42
+ if !yes
43
+ #puts "deregistering #{domain}"
44
+ dns_service.deregister_namespace(domain)
45
+ end
46
+ end
47
+
48
+ dns_service.publish
49
+ dns_service.close
50
+ end
51
+ end
52
+
53
+ end
54
+ World(DnsHelper)
@@ -0,0 +1,5 @@
1
+ require 'pp'
2
+
3
+ AfterConfiguration do |config|
4
+ SetupHelper::setup
5
+ end
@@ -0,0 +1,44 @@
1
+ require 'timeout'
2
+
3
+ module CommandHelper
4
+ def urls_from_files(pattern)
5
+ results = []
6
+ Dir.glob(pattern).each do |file|
7
+ File.new(file, "r").each {|line| results << line.chomp}
8
+ end
9
+ results
10
+ end
11
+
12
+ def add_failure(url, pid=$$)
13
+ system("flock /tmp/rhc/lock echo '#{url}' >> #{$temp}/#{pid}-failures.log")
14
+ end
15
+
16
+ def failures
17
+ urls_from_files("#{$temp}/*-failures.log")
18
+ end
19
+
20
+ def add_success(url, pid=$$)
21
+ system("echo '#{url}' >> #{$temp}/#{pid}-success.log")
22
+ end
23
+
24
+ def successes(pattern="*")
25
+ urls_from_files("#{$temp}/#{pattern.to_s}-success.log")
26
+ end
27
+
28
+ def wait(pid, expected_urls, timeout=300)
29
+ begin
30
+ Timeout::timeout(timeout) do
31
+ Process.wait(pid)
32
+ exit_status = $?.exitstatus
33
+ $logger.error("Process #{pid} failed with #{exit_status}") if exit_status != 0
34
+ end
35
+ rescue Timeout::Error
36
+ $logger.error("Process #{pid} timed out")
37
+ # Log the remaining url's as failures
38
+ failed_urls = expected_urls - successes(pid)
39
+ $logger.error("Recording the following urls as failed = #{failed_urls.pretty_inspect}")
40
+ failed_urls.each {|url| add_failure(url, pid)}
41
+ end
42
+ end
43
+ end
44
+ World(CommandHelper)
@@ -0,0 +1,440 @@
1
+ # A small support API for runtime-centric test cases. It provides
2
+ # abstractions for an Account, Application, Gear, and Cartridge.
3
+ # The gear implementation is backed by ApplicationContainer from
4
+ # the openshift-origin-node package.
5
+ #
6
+ # Parts of this might be flimsy and not quite aligned with certain
7
+ # realities (especially with regards to scaling), but it should
8
+ # provide a decent starting point for the runtime tests and give
9
+ # us a single place to refactor.
10
+
11
+ require 'openshift-origin-node'
12
+ require 'openshift-origin-node/utils/shell_exec'
13
+ require 'etc'
14
+ require 'timeout'
15
+
16
+ # Some constants which might be misplaced here. Perhaps they should
17
+ # go in 00_setup_helper.rb?
18
+ $home_root ||= "/var/lib/openshift"
19
+ $libra_httpd_conf_d ||= "/etc/httpd/conf.d/openshift"
20
+ $cartridge_root ||= "/usr/libexec/openshift/cartridges"
21
+ $embedded_cartridge_root ||= "/usr/libexec/openshift/cartridges/embedded"
22
+
23
+ $app_registry = {}
24
+
25
+ module OpenShift
26
+ TIMEOUT = 120
27
+
28
+
29
+ # Represents a user account. A name and domain will be automatically
30
+ # generated upon init,
31
+ class TestAccount
32
+ attr_reader :name, :domain, :apps
33
+
34
+ def initialize(domain=nil)
35
+ @name, @domain = gen_unique_login_and_domain(domain)
36
+ @apps = Array.new
37
+
38
+ # shouldn't do stuff in the constructor, but we'll live
39
+ $logger.info("Created new account #{@name} with domain #{@domain}")
40
+ end
41
+
42
+ # Lifted from another test script.
43
+ def gen_unique_login_and_domain(domain=nil)
44
+ if !domain
45
+ chars = ("1".."9").to_a
46
+ domain = "ci" + Array.new(8, '').collect{chars[rand(chars.size)]}.join
47
+ end
48
+ login = "cucumber-test_#{domain}@example.com"
49
+ [ login, domain ]
50
+ end
51
+
52
+ # Creates a new TestApplication instance associated with this account.
53
+ def create_app()
54
+ app = OpenShift::TestApplication.new(self)
55
+
56
+ $logger.info("Created new application #{app.name} for account #{@name}")
57
+
58
+ @apps << app
59
+ app
60
+ end
61
+
62
+ # Convenience function to get the first application for this account.
63
+ def default_app()
64
+ @apps[0]
65
+ end
66
+ end
67
+
68
+ # Represents an application associated with an account. The name and
69
+ # UUID for the application is automatically generated upon init.
70
+ class TestApplication
71
+ attr_reader :name, :uuid, :account, :gears
72
+ attr_accessor :hot_deploy_enabled
73
+
74
+ def initialize(account)
75
+ @name = gen_unique_app_name
76
+ @uuid = gen_small_uuid
77
+ @account = account
78
+ @gears = Array.new
79
+
80
+ # abstracts the hot_deploy marker file as a first class
81
+ # property of the application
82
+ @hot_deploy_enabled = false
83
+ end
84
+
85
+ # Lifted from another script.
86
+ def gen_unique_app_name
87
+ chars = ("1".."9").to_a
88
+ "app" + Array.new(4, '').collect{chars[rand(chars.size)]}.join
89
+ end
90
+
91
+ # Lifted from another script.
92
+ def gen_small_uuid
93
+ %x[/usr/bin/uuidgen].gsub('-', '').strip
94
+ end
95
+
96
+ # Creates a new empty gear associated with this application.
97
+ def create_gear
98
+ gear = OpenShift::TestGear.new(self)
99
+ gear.create
100
+ @gears << gear
101
+ gear
102
+ end
103
+
104
+ # Convenience function to get the first gear associated with
105
+ # this application.
106
+ def default_gear
107
+ @gears[0]
108
+ end
109
+
110
+ # Tears down the application by calling destroy for each gear.
111
+ def destroy
112
+ $logger.info("Destroying application #{@name}")
113
+ @gears.each do |gear|
114
+ gear.destroy
115
+ end
116
+ end
117
+
118
+ # Collects and returns the PIDs for every cartridge associated
119
+ # with this application as determined by the PID file in the
120
+ # cartridge instance run directory. The result is a Hash:
121
+ #
122
+ # PID file basename => PID
123
+ #
124
+ # NOTE: This doesn't currently take into account the possibility
125
+ # of duplicate PID filenames across gears (in a scaled instance).
126
+ def current_cart_pids
127
+ pids = {}
128
+
129
+ @gears.each do |gear|
130
+ gear.carts.values.each do |cart|
131
+ Dir.glob("#{$home_root}/#{gear.uuid}/#{cart.name}/{run,pid}/*.pid") do |pid_file|
132
+ $logger.info("Reading pid file #{pid_file} for cart #{cart.name}")
133
+ pid = IO.read(pid_file).chomp
134
+ proc_name = File.basename(pid_file, ".pid")
135
+
136
+ pids[proc_name] = pid
137
+ end
138
+ end
139
+ end
140
+
141
+ pids
142
+ end
143
+ end
144
+
145
+ # Represents a gear associated with an application. The UUID for the gear
146
+ # is automatically generated on init.
147
+ #
148
+ # NOTE: Instantiating the TestGear is not enough for it to be used; make
149
+ # sure to call TestGear.create before performing cartridge operations.
150
+ class TestGear
151
+ include CommandHelper
152
+
153
+ attr_reader :uuid, :carts, :app, :container
154
+
155
+ def initialize(app)
156
+ @uuid = gen_small_uuid
157
+ @carts = Hash.new # cart name => cart
158
+ @app = app
159
+ end
160
+
161
+ # Lifted from another script.
162
+ def gen_small_uuid()
163
+ %x[/usr/bin/uuidgen].gsub('-', '').strip
164
+ end
165
+
166
+ # Creates the physical gear on a node by delegating work to the
167
+ # ApplicationContainer class. Be sure to call this before attempting
168
+ # cartridge operations.
169
+ def create()
170
+ $logger.info("Creating new gear #{@uuid} for application #{@app.name}")
171
+
172
+ outbuf = []
173
+ cmd = "oo-app-create -a #{@app.uuid} -c #{@uuid} --with-app-name #{@app.name} --with-namespace #{@app.account.domain}"
174
+ exit_code = runcon(cmd, $selinux_user, $selinux_role, $selinux_type, outbuf)
175
+ if exit_code != 0
176
+ $logger.error(outbuf)
177
+ raise Exception.new(outbuf)
178
+ end
179
+
180
+ # Create the container object for use in the event listener later
181
+ @container = OpenShift::ApplicationContainer.new(@app.uuid, @uuid, nil, @app.name, @app.name, @app.account.domain, nil, nil)
182
+ end
183
+
184
+ # Destroys the gear via ApplicationContainer
185
+ def destroy()
186
+ $logger.info("Destroying gear #{@uuid} of application #{@app.name}")
187
+
188
+ outbuf = []
189
+ cmd = "oo-app-destroy -a #{@app.uuid} -c #{@uuid} --with-namespace #{@app.account.domain} --with-app-name #{@app.name}"
190
+ exit_code = runcon(cmd, $selinux_user, $selinux_role, $selinux_type, outbuf)
191
+ if exit_code != 0
192
+ $logger.error(outbuf)
193
+ raise Exception.new(outbuf)
194
+ end
195
+ end
196
+
197
+ # Adds an alias to the gear
198
+ def add_alias(alias_name)
199
+ $logger.info("Adding alias #{alias_name} to gear #{@uuid} of application #{@app.name}")
200
+
201
+ outbuf = []
202
+ cmd = "oo-add-alias --with-container-uuid #{@uuid} --with-container-name #{@app.name} --with-namespace #{@app.account.domain} --with-alias-name #{alias_name}"
203
+ exit_code = runcon(cmd, $selinux_user, $selinux_role, $selinux_type, outbuf)
204
+ if exit_code != 0
205
+ $logger.error(outbuf)
206
+ raise Exception.new(outbuf)
207
+ end
208
+ end
209
+
210
+ # Removes an alias from the gear
211
+ # Adds an alias to the gear
212
+ def remove_alias(alias_name)
213
+ $logger.info("Adding alias #{alias_name} to gear #{@uuid} of application #{@app.name}")
214
+
215
+ outbuf = []
216
+ cmd = "oo-remove-alias --with-container-uuid #{@uuid} --with-container-name #{@app.name} --with-namespace #{@app.account.domain} --with-alias-name #{alias_name}"
217
+ exit_code = runcon(cmd, $selinux_user, $selinux_role, $selinux_type, outbuf)
218
+ if exit_code != 0
219
+ $logger.error(outbuf)
220
+ raise Exception.new(outbuf)
221
+ end
222
+ end
223
+
224
+ # Creates a new TestCartridge and associates it with this gear.
225
+ #
226
+ # NOTE: The cartridge is instantiated, but no hooks (such as
227
+ # configure) are executed.
228
+ def add_cartridge(cart_name, type = TestCartridge::Standard)
229
+ cart = OpenShift::TestCartridge.new(cart_name, self, type)
230
+ @carts[cart.name] = cart
231
+ cart
232
+ end
233
+
234
+ # Convenience function to retrieve the first cartridge for the gear.
235
+ def default_cart()
236
+ @carts.values[0]
237
+ end
238
+ end
239
+
240
+ # Represents a cartridge associated with a gear. Supports firing events
241
+ # and listener registration for cartridge-specific concerns.
242
+ class TestCartridge
243
+ include CommandHelper
244
+
245
+ attr_reader :name, :gear, :path, :type, :metadata
246
+
247
+ # We need to differentiate between non/embedded carts, as they have
248
+ # different root paths on the node filesystem.
249
+ Standard = 0
250
+ Embedded = 1
251
+
252
+ def initialize(name, gear, type = Standard)
253
+ @name = name
254
+ @gear = gear
255
+ @type = type
256
+
257
+ @metadata = {} # a place for cart specific helpers to put stuff
258
+
259
+ local_root = (@type == Standard) ? $cartridge_root : $embedded_cartridge_root
260
+
261
+ @cart_path = "#{local_root}/#{@name}"
262
+ @hooks_path = "#{@cart_path}/info/hooks"
263
+
264
+ # Add new listener classes here for now
265
+ @listeners = [ OpenShift::TestCartridgeListeners::ConfigureCartListener.new,
266
+ OpenShift::TestCartridgeListeners::DatabaseCartListener.new
267
+ ]
268
+ end
269
+
270
+ # Convenience wrapper to invoke the configure hook.
271
+ def configure()
272
+ run_hook "configure"
273
+ end
274
+
275
+ # Convenience wrapper to invoke the deconfigure hook.
276
+ def deconfigure()
277
+ run_hook "deconfigure"
278
+ end
279
+
280
+ # Convenience wrapper to invoke the start hook.
281
+ def start()
282
+ run_hook "start"
283
+ end
284
+
285
+ # Convenience wrapper to invoke the stop hook.
286
+ def stop()
287
+ run_hook "stop"
288
+ end
289
+
290
+ # Convenience wrapper to invoke the status hook.
291
+ def status()
292
+ run_hook "status"
293
+ end
294
+
295
+ # Invokes an arbitrary hook on the cartridge, passing through
296
+ # any extra arguments specified by extra_args (if present). An
297
+ # exception will be raised if the exit code of the hook does
298
+ # not match expected_exitcode (which is 0 by default). The
299
+ # hook is executed in the selinux context of the user and role
300
+ # defined in the global constants provided in 00_setup_helper.
301
+ def run_hook(hook, expected_exitcode=0, extra_args=[])
302
+ exitcode, output = run_hook_output(hook, expected_exitcode, extra_args)
303
+ exitcode
304
+ end
305
+
306
+ def run_hook_output(hook, expected_exitcode=0, extra_args=[])
307
+ $logger.info("Running #{hook} hook for cartridge #{@name} in gear #{@gear.uuid} for application #{@gear.app.name}")
308
+
309
+ cmd = %Q{#{@hooks_path}/#{hook} '#{@gear.app.name}' '#{@gear.app.account.domain}' #{@gear.uuid} #{extra_args.join(" ")}}
310
+
311
+ output = Array.new
312
+ exitcode = runcon cmd, $selinux_user, $selinux_role, $selinux_type, output, TIMEOUT
313
+
314
+ # Sanitize the command output. For now, the only major problem we're aware
315
+ # of is colorized output containing escape characters. The sanitization should
316
+ # really be taken care of in downstream formatters, but we have no control over
317
+ # those at present.
318
+ output = output.collect {|s| s.gsub(/\e\[(\d+)m/, '')}
319
+
320
+ raise %Q{Error (#{exitcode}) running #{cmd}: #{output.join("\n")}} unless exitcode == expected_exitcode
321
+
322
+ notify_listeners "#{hook}_hook_completed", { :cart => self, :exitcode => exitcode, :output => output}
323
+
324
+ [exitcode, output]
325
+ end
326
+
327
+ # Notify cart listeners of an event
328
+ def notify_listeners(event, args={})
329
+ @listeners.each do |listener|
330
+ if listener.respond_to?(event) && listener.supports?(@name)
331
+ $logger.info("Notifying #{listener.class.name} of #{event} event")
332
+ listener.send(event, args)
333
+ end
334
+ end
335
+ end
336
+ end
337
+
338
+ # Cartridge listeners are intended to be invoked by TestCartridge instances
339
+ # at various points in their lifecycle, such as post-hook. The listeners
340
+ # are given an opportunity to respond to the results of cartridge actions
341
+ # and attach supplementary information to the cartridge instance which
342
+ # can be used to provide steps with cartridge specific context.
343
+ #
344
+ # Currently supported event patterns a listener may receive:
345
+ #
346
+ # {hook_name}_hook_completed(cart, exitcode, output)
347
+ # - invoked after a successful hook execution in the cartridge
348
+ module TestCartridgeListeners
349
+
350
+ # Pretend we're the broker's application model
351
+ class ConfigureCartListener
352
+ # We need to process all configure output for all cartridges
353
+ def supports?(cart_name)
354
+ true
355
+ end
356
+
357
+ def configure_hook_completed(args)
358
+ if args.key?(:output) && ! args[:output].empty?
359
+ homedir = args[:cart].gear.container.user.homedir
360
+
361
+ args[:output].first.split(/\n/).each { |line|
362
+ case line
363
+ when /^ENV_VAR_ADD: .*/
364
+ key, value = line['ENV_VAR_ADD: '.length..-1].chomp.split('=')
365
+ File.open(File.join(homedir, '.env', key),
366
+ File::WRONLY|File::TRUNC|File::CREAT) do |file|
367
+ file.write "export #{key}='#{value}'"
368
+ end
369
+ when /^ENV_VAR_REMOVE: .*/
370
+ key = line['ENV_VAR_REMOVE: '.length..-1].chomp
371
+ FileUtils.rm_f File.join(homedir, '.env', key)
372
+ end
373
+ }
374
+ end
375
+ end
376
+ end
377
+
378
+ class DatabaseCartListener
379
+ def supports?(cart_name)
380
+ ['mysql-5.1', 'mongodb-2.2', 'postgresql-8.4'].include?(cart_name)
381
+ end
382
+
383
+ class DbConnection
384
+ attr_accessor :username, :password, :ip, :port
385
+ end
386
+
387
+ # Processes the output of the cartridge configure script and scrapes
388
+ # it for connectivity details (such as IP and credentials).
389
+ #
390
+ # Adds a new attribute 'db' of type DbConnection to the cart with the
391
+ # scraped details.
392
+ def configure_hook_completed(args)
393
+ $logger.info("DatabaseCartListener is processing configure hook results")
394
+
395
+ cart = args[:cart]
396
+ output = args[:output]
397
+
398
+ my_username_pattern = /Root User: (\S+)/
399
+ my_password_pattern = /Root Password: (\S+)/
400
+
401
+ # make this smarter if there are more use cases
402
+ ip_pattern_prefix = cart.name[/[^-]*/]
403
+ my_ip_pattern = /#{ip_pattern_prefix}:\/\/(\d+\.\d+\.\d+\.\d+):(\d+)/
404
+
405
+ db = DbConnection.new
406
+
407
+ output.each do |line|
408
+ if line.match(my_username_pattern)
409
+ db.username = $1
410
+ end
411
+ if line.match(my_password_pattern)
412
+ db.password = $1
413
+ end
414
+ if line.match(my_ip_pattern)
415
+ db.ip = $1
416
+ db.port = $2
417
+ end
418
+ end
419
+
420
+ $logger.info("DatabaseCartListener is adding a DbConnection to cartridge #{cart.name}: #{db.inspect}")
421
+ cart.instance_variable_set(:@db, db)
422
+ cart.instance_eval('def db; @db; end')
423
+ end
424
+ end
425
+ end
426
+
427
+ #
428
+ # Only raise timeout exceptions...
429
+ def self.timeout(seconds, dflt = nil)
430
+ begin
431
+ Timeout::timeout(seconds) do
432
+ yield
433
+ end
434
+ rescue Timeout::Error
435
+ raise if dflt.instance_of? Timeout::Error
436
+ dflt
437
+ end
438
+ end
439
+ end
440
+