openshift-origin-controller 1.3.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (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
+