openshift-origin-node 1.3.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.

Potentially problematic release.


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

Files changed (51) hide show
  1. data/COPYRIGHT +1 -0
  2. data/Gemfile +4 -0
  3. data/LICENSE +11 -0
  4. data/README.md +3 -0
  5. data/Rakefile +28 -0
  6. data/bin/oo-add-alias +93 -0
  7. data/bin/oo-app-create +110 -0
  8. data/bin/oo-app-destroy +100 -0
  9. data/bin/oo-app-state-show +74 -0
  10. data/bin/oo-authorized-ssh-key-add +83 -0
  11. data/bin/oo-authorized-ssh-key-remove +82 -0
  12. data/bin/oo-broker-auth-key-add +84 -0
  13. data/bin/oo-broker-auth-key-remove +72 -0
  14. data/bin/oo-cartridge-info +70 -0
  15. data/bin/oo-cartridge-list +70 -0
  16. data/bin/oo-connector-execute +94 -0
  17. data/bin/oo-env-var-add +81 -0
  18. data/bin/oo-env-var-remove +78 -0
  19. data/bin/oo-get-quota +64 -0
  20. data/bin/oo-remove-alias +93 -0
  21. data/bin/oo-set-quota +59 -0
  22. data/conf/node.conf +30 -0
  23. data/conf/resource_limits.template +67 -0
  24. data/lib/openshift-origin-node.rb +29 -0
  25. data/lib/openshift-origin-node/config.rb +21 -0
  26. data/lib/openshift-origin-node/environment.rb +26 -0
  27. data/lib/openshift-origin-node/model/application_container.rb +298 -0
  28. data/lib/openshift-origin-node/model/frontend_httpd.rb +346 -0
  29. data/lib/openshift-origin-node/model/node.rb +134 -0
  30. data/lib/openshift-origin-node/model/unix_user.rb +738 -0
  31. data/lib/openshift-origin-node/plugins/unix_user_observer.rb +86 -0
  32. data/lib/openshift-origin-node/utils/shell_exec.rb +115 -0
  33. data/lib/openshift-origin-node/version.rb +23 -0
  34. data/misc/bin/oo-admin-ctl-cgroups +482 -0
  35. data/misc/bin/oo-cgroup-read +25 -0
  36. data/misc/bin/oo-get-mcs-level +29 -0
  37. data/misc/bin/oo-trap-user +248 -0
  38. data/misc/bin/rhcsh +155 -0
  39. data/misc/bin/setup_pam_fs_limits.sh +146 -0
  40. data/misc/bin/teardown_pam_fs_limits.sh +73 -0
  41. data/misc/doc/cgconfig.conf +26 -0
  42. data/misc/etc/openshift-run.conf +1 -0
  43. data/misc/init/openshift-cgroups +56 -0
  44. data/misc/services/openshift-cgroups.service +14 -0
  45. data/openshift-origin-node.gemspec +31 -0
  46. data/rubygem-openshift-origin-node.spec +263 -0
  47. data/test/test_helper.rb +20 -0
  48. data/test/unit/frontend_httpd_test.rb +144 -0
  49. data/test/unit/unix_user_test.rb +95 -0
  50. data/test/unit/version_test.rb +45 -0
  51. metadata +230 -0
@@ -0,0 +1,29 @@
1
+ #--
2
+ # Copyright 2010 Red Hat, Inc.
3
+ #
4
+ # Licensed under the Apache License, Version 2.0 (the "License");
5
+ # you may not use this file except in compliance with the License.
6
+ # You may obtain a copy of the License at
7
+ #
8
+ # http://www.apache.org/licenses/LICENSE-2.0
9
+ #
10
+ # Unless required by applicable law or agreed to in writing, software
11
+ # distributed under the License is distributed on an "AS IS" BASIS,
12
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
+ # See the License for the specific language governing permissions and
14
+ # limitations under the License.
15
+ #++
16
+
17
+
18
+ require 'rubygems'
19
+
20
+ require 'fileutils'
21
+ require 'getoptlong'
22
+ require 'json'
23
+ require 'parseconfig'
24
+ require "openshift-origin-common"
25
+ require "openshift-origin-node/version"
26
+ require "openshift-origin-node/environment"
27
+ require "openshift-origin-node/model/application_container"
28
+ require "openshift-origin-node/model/node"
29
+ require "openshift-origin-node/model/frontend_httpd"
@@ -0,0 +1,21 @@
1
+ # openshift-origin-node/config has been moved to openshift-origin-common/config.
2
+ # This stub remains for code in li that still requires the former. Once
3
+ # everything has migrated to openshift-common/config, this file can be
4
+ # deleted. -- Miciah, 2012-10-02
5
+
6
+ require 'rubygems'
7
+
8
+ require 'openshift-origin-common'
9
+
10
+ module OpenShift
11
+ class Config
12
+ # This is a bit of a hack. The old OpenShift::Config was a singleton
13
+ # object, and so users would use OpenShift::Config.instance to get it.
14
+ # Here, we define .instance to return a new instance. Hopefully, nothing
15
+ # is relying on the standard singleton behavior whereby .instance always
16
+ # returns the same instance.
17
+ def self.instance
18
+ OpenShift::Config.new
19
+ end
20
+ end
21
+ end
@@ -0,0 +1,26 @@
1
+ #--
2
+ # Copyright 2010 Red Hat, Inc.
3
+ #
4
+ # Licensed under the Apache License, Version 2.0 (the "License");
5
+ # you may not use this file except in compliance with the License.
6
+ # You may obtain a copy of the License at
7
+ #
8
+ # http://www.apache.org/licenses/LICENSE-2.0
9
+ #
10
+ # Unless required by applicable law or agreed to in writing, software
11
+ # distributed under the License is distributed on an "AS IS" BASIS,
12
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
+ # See the License for the specific language governing permissions and
14
+ # limitations under the License.
15
+ #++
16
+
17
+ require 'rubygems'
18
+ require 'openshift-origin-common'
19
+
20
+ module OpenShift
21
+ #load OPENSHIFT_NODE_PLUGINS
22
+ plugin_list = Config.new.get('OPENSHIFT_NODE_PLUGINS').split(',')
23
+ plugin_list.each do |plugin|
24
+ require "#{plugin}" unless plugin.start_with?('#')
25
+ end
26
+ end
@@ -0,0 +1,298 @@
1
+ #--
2
+ # Copyright 2010 Red Hat, Inc.
3
+ #
4
+ # Licensed under the Apache License, Version 2.0 (the "License");
5
+ # you may not use this file except in compliance with the License.
6
+ # You may obtain a copy of the License at
7
+ #
8
+ # http://www.apache.org/licenses/LICENSE-2.0
9
+ #
10
+ # Unless required by applicable law or agreed to in writing, software
11
+ # distributed under the License is distributed on an "AS IS" BASIS,
12
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
+ # See the License for the specific language governing permissions and
14
+ # limitations under the License.
15
+ #++
16
+
17
+ require 'rubygems'
18
+ require 'openshift-origin-node/model/unix_user'
19
+ require 'openshift-origin-node/utils/shell_exec'
20
+ require 'openshift-origin-common'
21
+ require 'logger'
22
+
23
+ module OpenShift
24
+ # == Application Container
25
+ class ApplicationContainer < Model
26
+ include OpenShift::Utils::ShellExec
27
+ attr_reader :uuid, :application_uuid, :user
28
+
29
+ # Represents all possible application states.
30
+ module State
31
+ BUILDING = "building"
32
+ DEPLOYING = "deploying"
33
+ IDLE = "idle"
34
+ NEW = "new"
35
+ STARTED = "started"
36
+ STOPPED = "stopped"
37
+ UNKNOWN = "unknown"
38
+ end
39
+
40
+ def initialize(application_uuid, container_uuid, user_uid = nil,
41
+ app_name = nil, container_name = nil, namespace = nil, quota_blocks = nil, quota_files = nil, logger = nil)
42
+ @logger = logger ||= Logger.new(STDOUT)
43
+
44
+ @config = OpenShift::Config.new
45
+
46
+ @uuid = container_uuid
47
+ @application_uuid = application_uuid
48
+ @user = UnixUser.new(application_uuid, container_uuid, user_uid,
49
+ app_name, container_name, namespace, quota_blocks, quota_files)
50
+ end
51
+
52
+ def name
53
+ @uuid
54
+ end
55
+
56
+ # Create gear - model/unix_user.rb
57
+ def create
58
+ notify_observers(:before_container_create)
59
+ @user.create
60
+ notify_observers(:after_container_create)
61
+ end
62
+
63
+ # Destroy gear - model/unix_user.rb
64
+ def destroy(skip_hooks=false)
65
+ notify_observers(:before_container_destroy)
66
+
67
+ hook_timeout=30
68
+
69
+ output = ""
70
+ errout = ""
71
+ retcode = 0
72
+
73
+ hooks={}
74
+ ["pre", "post"].each do |hooktype|
75
+ if @user.homedir.nil? || ! File.exists?(@user.homedir)
76
+ hooks[hooktype]=[]
77
+ else
78
+ hooks[hooktype] = Dir.entries(@user.homedir).map { |cart|
79
+ [ File.join(@config.get("CARTRIDGE_BASE_PATH"),cart,"info","hooks","#{hooktype}-destroy"),
80
+ File.join(@config.get("CARTRIDGE_BASE_PATH"),"embedded",cart,"info","hooks","#{hooktype}-destroy"),
81
+ ].select { |hook| File.exists? hook }[0]
82
+ }.select { |hook|
83
+ not hook.nil?
84
+ }.map { |hook|
85
+ "#{hook} #{@user.container_name} #{@user.namespace} #{@user.container_uuid}"
86
+ }
87
+ end
88
+ end
89
+
90
+ unless skip_hooks
91
+ hooks["pre"].each do | cmd |
92
+ out,err,rc = shellCmd(cmd, "/", true, 0, hook_timeout)
93
+ errout << err if not err.nil?
94
+ output << out if not out.nil?
95
+ retcode = 121 if rc != 0
96
+ end
97
+ end
98
+
99
+ @user.destroy
100
+
101
+ unless skip_hooks
102
+ hooks["post"].each do | cmd |
103
+ out,err,rc = shellCmd(cmd, "/", true, 0, hook_timeout)
104
+ errout << err if not err.nil?
105
+ output << out if not out.nil?
106
+ retcode = 121 if rc != 0
107
+ end
108
+ end
109
+
110
+ notify_observers(:after_container_destroy)
111
+
112
+ return output, errout, retcode
113
+ end
114
+
115
+ # Public: Fetch application state from gear.
116
+ # Returns app state as string on Success and 'unknown' on Failure
117
+ def get_app_state
118
+ env = load_env
119
+ app_state_file=File.join(env[:OPENSHIFT_HOMEDIR], 'app-root', 'runtime', '.state')
120
+
121
+ if File.exists?(app_state_file)
122
+ app_state = nil
123
+ File.open(app_state_file) { |input| app_state = input.read.chomp }
124
+ else
125
+ app_state = :UNKNOWN
126
+ end
127
+ app_state
128
+ end
129
+
130
+ # Public: Sets the application state.
131
+ #
132
+ # new_state - The new state to assign. Must be an ApplicationContainer::State.
133
+ def set_app_state(new_state)
134
+ new_state_val = nil
135
+ begin
136
+ new_state_val = State.const_get(new_state)
137
+ rescue
138
+ raise ArgumentError, "Invalid state '#{new_state}' specified"
139
+ end
140
+
141
+ env = load_env
142
+ app_state_file = File.join(env[:OPENSHIFT_HOMEDIR], 'app-root', 'runtime', '.state')
143
+
144
+ raise "Couldn't find app state file at #{app_state_file}" unless File.exists?(app_state_file)
145
+
146
+ File.open(app_state_file, File::WRONLY|File::TRUNC|File::CREAT, 0o0660) {|file|
147
+ file.write "#{new_state_val}\n"
148
+ }
149
+ end
150
+
151
+ # Public: Sets the app state to "stopped" and causes an immediate forced
152
+ # termination of all gear processes.
153
+ #
154
+ # TODO: exception handling
155
+ def force_stop
156
+ set_app_state(:STOPPED)
157
+ UnixUser.kill_procs(@user.uid)
158
+ end
159
+
160
+
161
+ # Public: Cleans up the gear, providing any installed
162
+ # cartridges with the opportinity to perform their own
163
+ # cleanup operations via the tidy hook.
164
+ #
165
+ # The generic gear-level cleanup flow is:
166
+ # * Stop the gear
167
+ # * Git cleanup
168
+ # * Gear temp dir cleanup
169
+ # * Cartridge tidy hook executions
170
+ # * Start the gear
171
+ #
172
+ # Raises an Exception if an internal error occurs, and ignores
173
+ # failed cartridge tidy hook executions.
174
+ def tidy
175
+ @logger.debug("Starting tidy on gear #{@uuid}")
176
+
177
+ env = load_env
178
+ gear_dir = env[:OPENSHIFT_HOMEDIR]
179
+ app_name = env[:OPENSHIFT_APP_NAME]
180
+ gear_repo_dir = File.join(gear_dir, 'git', "#{app_name}.git")
181
+ gear_tmp_dir = File.join(gear_dir, '.tmp')
182
+
183
+ begin
184
+ # Stop the gear. If this fails, consider the tidy a failure.
185
+ out, err, rc = shellCmd("/usr/sbin/oo-admin-ctl-gears stopgear #{@user.uuid}", gear_dir, false, 0)
186
+ @logger.debug("Stopped gear #{@uuid}. Output:\n#{out}")
187
+ rescue OpenShift::Utils::ShellExecutionException => e
188
+ @logger.error(%Q{
189
+ Couldn't stop gear #{@uuid} for tidy: #{e.message}
190
+ --- stdout ---\n#{e.stdout}
191
+ --- stderr ---\n#{e.stderr}
192
+ })
193
+ raise "Tidy failed on gear #{@uuid}; the gear couldn't be stopped successfully"
194
+ end
195
+
196
+ # Perform the individual tidy actions. At this point, the gear has been stopped,
197
+ # and so we'll attempt to start the gear no matter what tidy operations fail.
198
+ begin
199
+ # Git pruning
200
+ tidy_action do
201
+ out, err, rc = shellCmd("git prune", gear_repo_dir, false, 0)
202
+ @logger.debug("Pruned git directory at #{gear_repo_dir}. Output:\n#{out}")
203
+ end
204
+
205
+ # Git GC
206
+ tidy_action do
207
+ out, err, rc = shellCmd("git gc --aggressive", gear_repo_dir, false, 0)
208
+ @logger.debug("Executed git gc for repo #{gear_repo_dir}. Output:\n#{out}")
209
+ end
210
+
211
+ # Temp dir cleanup
212
+ tidy_action do
213
+ FileUtils.rm_rf(Dir.glob(File.join(gear_tmp_dir, "*")))
214
+ @logger.debug("Cleaned gear temp dir at #{gear_tmp_dir}")
215
+ end
216
+
217
+ # Execute the tidy hooks in any installed carts, in the context
218
+ # of the gear user. For now, we detect cart installations by iterating
219
+ # over the gear subdirs and using the dir names to construct a path
220
+ # to cart scripts in the base cartridge directory. If such a file exists,
221
+ # it's assumed the cart is installed on the gear.
222
+ cart_tidy_timeout = 30
223
+ Dir.entries(gear_dir).each do |gear_subdir|
224
+ tidy_script = File.join(@config.get("CARTRIDGE_BASE_PATH"), gear_subdir, "info", "hooks", "tidy")
225
+
226
+ next unless File.exists?(tidy_script)
227
+
228
+ begin
229
+ # Execute the hook in the context of the gear user
230
+ @logger.debug("Executing cart tidy script #{tidy_script} in gear #{@uuid} as user #{@user.uid}:#{@user.gid}")
231
+ OpenShift::Utils::ShellExec.run_as(@user.uid, @user.gid, tidy_script, gear_dir, false, 0, cart_tidy_timeout)
232
+ rescue OpenShift::Utils::ShellExecutionException => e
233
+ @logger.warn("Cartridge tidy operation failed on gear #{@uuid} for cart #{gear_dir}: #{e.message} (rc=#{e.rc})")
234
+ end
235
+ end
236
+ rescue Exception => e
237
+ @logger.warn("An unknown exception occured during tidy for gear #{@uuid}: #{e.message}\n#{e.backtrace}")
238
+ ensure
239
+ begin
240
+ # Start the gear, and if that fails raise an exception, as the app is now
241
+ # in a bad state.
242
+ out, err, rc = shellCmd("/usr/sbin/oo-admin-ctl-gears startgear #{@user.uuid}", gear_dir)
243
+ @logger.debug("Started gear #{@uuid}. Output:\n#{out}")
244
+ rescue OpenShift::Utils::ShellExecutionException => e
245
+ @logger.error(%Q{
246
+ Failed to restart gear #{@uuid} following tidy: #{e.message}
247
+ --- stdout ---\n#{e.stdout}
248
+ --- stderr ---\n#{e.stderr}
249
+ })
250
+ raise "Tidy of gear #{@uuid} failed, and the gear was not successfuly restarted"
251
+ end
252
+ end
253
+
254
+ @logger.debug("Completed tidy for gear #{@uuid}")
255
+ end
256
+
257
+ # Executes a block, trapping ShellExecutionExceptions and treating them
258
+ # as warnings. Any other exceptions are unexpected and will bubble out.
259
+ def tidy_action
260
+ begin
261
+ yield
262
+ rescue OpenShift::Utils::ShellExecutionException => e
263
+ @logger.warn(%Q{
264
+ Tidy operation failed on gear #{@uuid}: #{e.message}
265
+ --- stdout ---\n#{e.stdout}
266
+ --- stderr ---\n#{e.stderr}
267
+ })
268
+ end
269
+ end
270
+
271
+ # Public: Load a gears environment variables into the environment
272
+ #
273
+ # Examples
274
+ #
275
+ # load_env
276
+ # # => {"OPENSHIFT_APP_NAME"=>"myapp"}
277
+ #
278
+ # Returns env Array
279
+ def load_env
280
+ env = {}
281
+ # Load environment variables into a hash
282
+
283
+ Dir["#{user.homedir}/.env/*"].each { | f |
284
+ next if File.directory?(f)
285
+ contents = nil
286
+ File.open(f) {|input|
287
+ contents = input.read.chomp
288
+ index = contents.index('=')
289
+ contents = contents[(index + 1)..-1]
290
+ contents = contents[/'(.*)'/, 1] if contents.start_with?("'")
291
+ contents = contents[/"(.*)"/, 1] if contents.start_with?('"')
292
+ }
293
+ env[File.basename(f).intern] = contents
294
+ }
295
+ env
296
+ end
297
+ end
298
+ end
@@ -0,0 +1,346 @@
1
+ #--
2
+ # Copyright 2010 Red Hat, Inc.
3
+ #
4
+ # Licensed under the Apache License, Version 2.0 (the "License");
5
+ # you may not use this file except in compliance with the License.
6
+ # You may obtain a copy of the License at
7
+ #
8
+ # http://www.apache.org/licenses/LICENSE-2.0
9
+ #
10
+ # Unless required by applicable law or agreed to in writing, software
11
+ # distributed under the License is distributed on an "AS IS" BASIS,
12
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
+ # See the License for the specific language governing permissions and
14
+ # limitations under the License.
15
+ #++
16
+
17
+ require 'rubygems'
18
+ require 'openshift-origin-node/utils/shell_exec'
19
+ require 'openshift-origin-common'
20
+ require 'syslog'
21
+ require 'fileutils'
22
+
23
+ module OpenShift
24
+
25
+ class FrontendHttpServerException < StandardError
26
+ attr_reader :container_uuid, :container_name
27
+ attr_reader :namespace
28
+
29
+ def initialize(msg=nil, container_uuid=nil, container_name=nil, namespace=nil)
30
+ @container_uuid = container_uuid
31
+ @container_name = container_name
32
+ @namespace = namespace
33
+ super(msg)
34
+ end
35
+
36
+ def to_s
37
+ m = super
38
+ m+= ": #{@container_name}" if not @container_name.nil?
39
+ m+= ": #{@namespace}" if not @namespace.nil?
40
+ m
41
+ end
42
+
43
+ end
44
+
45
+ class FrontendHttpServerNameException < FrontendHttpServerException
46
+ attr_reader :server_name
47
+
48
+ def initialize(msg=nil, container_uuid=nil, container_name=nil, namespace=nil, server_name=nil)
49
+ @server_name = server_name
50
+ super(msg, container_uuid, container_name, namespace)
51
+ end
52
+
53
+ def to_s
54
+ m = super
55
+ m+= ": #{@server_name}" if not @server_name.nil?
56
+ m
57
+ end
58
+
59
+ end
60
+
61
+ class FrontendHttpServerAliasException < FrontendHttpServerException
62
+ attr_reader :alias_name
63
+
64
+ def initialize(msg=nil, container_uuid=nil, container_name=nil, namespace=nil, alias_name=nil)
65
+ @alias_name = alias_name
66
+ super(msg, container_uuid, container_name, namespace)
67
+ end
68
+
69
+ def to_s
70
+ m = super
71
+ m+= ": #{@alias_name}" if not @alias_name.nil?
72
+ m
73
+ end
74
+
75
+ end
76
+
77
+ # == Frontend Http Server
78
+ #
79
+ # Represents the front-end HTTP server on the system.
80
+ #
81
+ # Note: This is the Apache VirtualHost implementation; other implementations may vary.
82
+ #
83
+ class FrontendHttpServer < Model
84
+ include OpenShift::Utils::ShellExec
85
+
86
+ attr_reader :container_uuid, :container_name
87
+ attr_reader :namespace
88
+
89
+ def initialize(container_uuid, container_name, namespace)
90
+ Syslog.open('openshift-origin-node', Syslog::LOG_PID, Syslog::LOG_LOCAL0) unless Syslog.opened?
91
+
92
+ @config = OpenShift::Config.new
93
+ @container_uuid = container_uuid
94
+ @container_name = container_name
95
+ @namespace = namespace
96
+ @cloud_domain = @config.get("CLOUD_DOMAIN")
97
+ end
98
+
99
+ # Public: Initialize an empty configuration for this gear
100
+ #
101
+ # Examples
102
+ #
103
+ # create
104
+ # # => nil
105
+ # # directory for httpd configuration.
106
+ #
107
+ # Returns nil on Success or raises on Failure
108
+ def create
109
+ basedir = @config.get("GEAR_BASE_DIR")
110
+
111
+ token = "#{@container_uuid}_#{@namespace}_#{@container_name}"
112
+ path = File.join(basedir, ".httpd.d", token)
113
+
114
+ FileUtils.rm_rf(path) if File.exist?(path)
115
+ FileUtils.mkdir_p(path)
116
+ end
117
+
118
+ # Public: Remove the frontend httpd configuration for a gear.
119
+ #
120
+ # Examples
121
+ #
122
+ # destroy
123
+ # # => nil
124
+ #
125
+ # Returns nil on Success or raises on Failure
126
+ def destroy(async=true)
127
+ basedir = @config.get("GEAR_BASE_DIR")
128
+
129
+ path = File.join(basedir, ".httpd.d", "#{container_uuid}_*")
130
+ FileUtils.rm_rf(Dir.glob(path))
131
+
132
+ reload_all(async)
133
+ end
134
+
135
+ # Public: Connect a path element to a back-end URI for this namespace.
136
+ #
137
+ # Examples
138
+ #
139
+ # connect('/', 'http://127.0.250.1:8080/')
140
+ # connect('/phpmyadmin, ''http://127.0.250.2:8080/')
141
+ # connect('/socket, 'http://127.0.250.3:8080/', {:websocket=>1}
142
+ #
143
+ # # => nil
144
+ #
145
+ # Returns nil on Success or raises on Failure
146
+ def connect(path, uri, options={})
147
+ raise NotImplementedError
148
+ end
149
+
150
+ # Public: Disconnect a path element from this namespace
151
+ #
152
+ # Examples
153
+ #
154
+ # disconnect('/')
155
+ # disconnect('/phpmyadmin)
156
+ #
157
+ # # => nil
158
+ #
159
+ # Returns nil on Success or raises on Failure
160
+ def disconnect(path)
161
+ raise NotImplementedError
162
+ end
163
+
164
+ # Public: Add an alias to this namespace
165
+ #
166
+ # Examples
167
+ #
168
+ # add_alias("foo.example.com")
169
+ # # => nil
170
+ #
171
+ # Returns nil on Success or raises on Failure
172
+ def add_alias(name)
173
+ dname = clean_server_name(name)
174
+ path = server_alias_path(dname)
175
+
176
+ # Aliases must be globally unique across all gears.
177
+ existing = server_alias_search(dname, true)
178
+ if not existing.empty?
179
+ raise FrontendHttpServerAliasException.new("Already exists", @container_uuid, \
180
+ @container_name, @namespace, dname )
181
+ end
182
+
183
+ File.open(path, "w") do |f|
184
+ f.write("ServerAlias #{dname}")
185
+ f.flush
186
+ end
187
+
188
+ create_routes_alias(dname)
189
+
190
+ reload_all
191
+ end
192
+
193
+ # Public: Removes an alias from this namespace
194
+ #
195
+ # Examples
196
+ #
197
+ # add_alias("foo.example.com")
198
+ # # => nil
199
+ #
200
+ # Returns nil on Success or raises on Failure
201
+ def remove_alias(name)
202
+ dname = clean_server_name(name)
203
+ routes_file_path = server_routes_alias_path(dname)
204
+
205
+ FileUtils.rm_f(routes_file_path) if File.exist?(routes_file_path)
206
+
207
+ server_alias_search(dname, false).each do |path|
208
+ FileUtils.rm_f(path)
209
+ end
210
+
211
+ reload_all
212
+ end
213
+
214
+ # Private: Validate the server name
215
+ #
216
+ # The name is validated against DNS host name requirements from
217
+ # RFC 1123 and RFC 952. Additionally, OpenShift does not allow
218
+ # names/aliases to be an IP address.
219
+ def clean_server_name(name)
220
+ dname = name.downcase
221
+
222
+ if not dname.index(/[^0-9a-z\-.]/).nil?
223
+ raise FrontendHttpServerNameException.new("Invalid characters", @container_uuid, \
224
+ @container_name, @namespace, dname )
225
+ end
226
+
227
+ if dname.length > 255
228
+ raise FrontendHttpServerNameException.new("Too long", @container_uuid, \
229
+ @container_name, @namespace, dname )
230
+ end
231
+
232
+ if dname.length == 0
233
+ raise FrontendHttpServerNameException.new("Name was blank", @container_uuid, \
234
+ @container_name, @namespace, dname )
235
+ end
236
+
237
+ if dname =~ /^\d+\.\d+\.\d+\.\d+$/
238
+ raise FrontendHttpServerNameException.new("IP addresses are not allowed", @container_uuid, \
239
+ @container_name, @namespace, dname )
240
+ end
241
+
242
+ return dname
243
+ end
244
+
245
+ # Private: Return path to alias file
246
+ def server_alias_path(name)
247
+ dname = clean_server_name(name)
248
+
249
+ basedir = @config.get("GEAR_BASE_DIR")
250
+ token = "#{@container_uuid}_#{@namespace}_#{@container_name}"
251
+ path = File.join(basedir, '.httpd.d', token, "server_alias-#{dname}.conf")
252
+ end
253
+
254
+ # Private: Return path to routes alias file name used by ws proxy server
255
+ def server_routes_alias_path(name)
256
+ dname = clean_server_name(name)
257
+
258
+ basedir = @config.get("GEAR_BASE_DIR")
259
+ token = "#{@container_uuid}_#{@namespace}_#{@container_name}"
260
+ path = File.join(basedir, '.httpd.d', token, "routes_alias-#{dname}.json")
261
+ end
262
+
263
+ # Get path to the default routes.json file created for the node web proxy
264
+ def default_routes_path
265
+ basedir = @config.get("GEAR_BASE_DIR")
266
+ token = "#{@container_uuid}_#{@namespace}_#{@container_name}"
267
+ File.join(basedir, '.httpd.d', token, "routes.json")
268
+ end
269
+
270
+ # Create an alias routing file for the node web proxy server
271
+ def create_routes_alias(alias_name)
272
+ route_file = default_routes_path
273
+ alias_file = File.join(File.dirname(route_file), "routes_alias-#{alias_name}.json")
274
+
275
+ begin
276
+ File.open(route_file, 'r') do |fin|
277
+ File.open(alias_file, 'w') do |fout|
278
+ fout.write(fin.read.gsub("#{@container_name}-#{@namespace}.#{@cloud_domain}", "#{alias_name}"))
279
+ end
280
+ end
281
+ rescue => e
282
+ Syslog.alert("ERROR: Failure trying to create routes alias json file: #{@uuid} Exception: #{e.inspect}")
283
+ end
284
+ end
285
+
286
+ # Reload both Apache and the proxy server and combine output/return
287
+ def reload_all(async=false)
288
+ output = ''
289
+ errout = ''
290
+ retc = 0
291
+
292
+ out, err, rc = reload_node_web_proxy
293
+ output << out
294
+ errout << err
295
+ retc = rc if rc != 0
296
+
297
+ out, err, rc = reload_httpd(async)
298
+ output << out
299
+ errout << err
300
+ retc = rc if rc != 0
301
+
302
+ return output, errout, retc
303
+ end
304
+
305
+ # Reload the Apache configuration
306
+ def reload_httpd(async=false)
307
+ async_opt="-b" if async
308
+ out, err, rc = shellCmd("/usr/sbin/oo-httpd-singular #{async_opt} graceful")
309
+ Syslog.alert("ERROR: failure from oo-httpd-singular(#{rc}): #{@uuid} stdout: #{out} stderr:#{err}") unless rc == 0
310
+ return out, err, rc
311
+ end
312
+
313
+ # Reload the configuration of the node web proxy server
314
+ def reload_node_web_proxy
315
+ out, err, rc = shellCmd("service openshift-node-web-proxy reload")
316
+ Syslog.alert("ERROR: failure from openshift-node-web-proxy(#{rc}): #{@uuid} stdout: #{out} stderr:#{err}") unless rc == 0
317
+ return out, err, rc
318
+ end
319
+
320
+ # Private: Search for matching alias files
321
+ # Previously, case sensitive matches were used
322
+ # checking for a duplicate and there are mixed
323
+ # case aliases in use.
324
+ def server_alias_search(name, all_gears=false)
325
+ dname = clean_server_name(name)
326
+ resp = []
327
+
328
+ basedir = @config.get("GEAR_BASE_DIR")
329
+ if all_gears
330
+ token = "*"
331
+ else
332
+ token = "#{@container_uuid}_#{@namespace}_#{@container_name}"
333
+ end
334
+ srch = File.join(basedir, ".httpd.d", token, "server_alias-*.conf")
335
+ Dir.glob(srch).each do |fn|
336
+ cmpname = fn.sub(/^.*\/server_alias-(.*)\.conf$/, '\\1')
337
+ if cmpname.casecmp(dname) == 0
338
+ resp << fn
339
+ end
340
+ end
341
+ resp
342
+ end
343
+
344
+ end
345
+
346
+ end