builder_cartridge()
click to toggle source
Detects and returns a builder Cartridge in the gear if present,
otherwise nil.
def builder_cartridge
builder_cart = nil
each_cartridge do |c|
if c.categories.include? 'ci_builder'
builder_cart = c
break
end
end
builder_cart
end
cartridge_action(cartridge, action, software_version, render_erbs=false)
click to toggle source
cartridge_action(cartridge,
action, software_version, render_erbs) ->
buffer
Returns the results from calling a cartridge's action script. Includes
--version if provided. Raises exception if script fails
stdout = cartridge_action(cartridge_obj)
def cartridge_action(cartridge, action, software_version, render_erbs=false)
logger.info "Running #{action} for #{@container.uuid}/#{cartridge.directory}"
cartridge_home = PathUtils.join(@container.container_dir, cartridge.directory)
action = PathUtils.join(cartridge_home, 'bin', action)
return "" unless File.exists? action
gear_env = ::OpenShift::Runtime::Utils::Environ.for_gear(@container.container_dir)
cartridge_env_home = PathUtils.join(cartridge_home, 'env')
cartridge_env = Utils::Environ.load(cartridge_env_home)
cartridge_env.delete('PATH')
cartridge_env = gear_env.merge(cartridge_env)
if render_erbs
erbs = Dir.glob(cartridge_env_home + '/*.erb', File::FNM_DOTMATCH).select { |f| File.file?(f) }
render_erbs(cartridge_env, erbs)
cartridge_env = Utils::Environ.load(cartridge_env_home)
cartridge_env.delete('PATH')
cartridge_env = gear_env.merge(cartridge_env)
end
action << " --version #{software_version}"
out, _, _ = @container.run_in_container_context(action,
env: cartridge_env,
chdir: cartridge_home,
timeout: @hourglass.remaining,
expected_exitstatus: 0)
logger.info("Ran #{action} for #{@container.uuid}/#{cartridge.directory}\n#{Runtime::Utils.sanitize_credentials(out)}")
out
end
cartridge_directory(cart_name)
click to toggle source
def cartridge_directory(cart_name)
name, _ = map_cartridge_name(cart_name)
cart_dir = Dir.glob(PathUtils.join(@container.container_dir, "#{name}"))
raise "Ambiguous cartridge name #{cart_name}: found #{cart_dir}:#{cart_dir.size}" if 1 < cart_dir.size
raise "Cartridge directory not found for #{cart_name}" if 1 > cart_dir.size
File.basename(cart_dir.first)
end
cartridge_hooks(action_hooks, action, name, version)
click to toggle source
def cartridge_hooks(action_hooks, action, name, version)
hooks = {pre: [], post: []}
hooks.each_key do |key|
new_hook = PathUtils.join(action_hooks, "#{key}_#{action}_#{name}")
old_hook = PathUtils.join(action_hooks, "#{key}_#{action}_#{name}-#{version}")
hooks[key] << "source #{new_hook}" if File.exist? new_hook
hooks[key] << "source #{old_hook}" if File.exist? old_hook
end
hooks
end
cartridge_teardown(cartridge_name, remove_cartridge_dir=true)
click to toggle source
cartridge_teardown(cartridge_name,
remove_cartridge_dir) -> buffer
Returns the output from calling the cartridge's teardown script.
Raises exception if script fails
stdout = cartridge_teardown('php-5.3')
def cartridge_teardown(cartridge_name, remove_cartridge_dir=true)
cartridge_home = PathUtils.join(@container.container_dir, cartridge_name)
env = ::OpenShift::Runtime::Utils::Environ.for_gear(@container.container_dir, cartridge_home)
teardown = PathUtils.join(cartridge_home, 'bin', 'teardown')
return "" unless File.exists? teardown
return "#{teardown}: is not executable\n" unless File.executable? teardown
buffer, err, _ = @container.run_in_container_context(teardown,
env: env,
chdir: cartridge_home,
timeout: @hourglass.remaining,
expected_exitstatus: 0)
buffer << err
FileUtils.rm_r(cartridge_home) if remove_cartridge_dir
logger.info("Ran teardown for #{@container.uuid}/#{cartridge_name}")
buffer
end
connect_frontend(cartridge, rebuild=false)
click to toggle source
def connect_frontend(cartridge, rebuild=false)
frontend = FrontendHttpServer.new(@container)
gear_env = ::OpenShift::Runtime::Utils::Environ.for_gear(@container.container_dir)
web_proxy_cart = web_proxy
app_dns = gear_env["OPENSHIFT_APP_DNS"].to_s.downcase
output = ""
begin
cartridge.endpoints.each do |endpoint|
endpoint.mappings.each do |mapping|
private_ip = gear_env[endpoint.private_ip_name]
backend_uri = "#{private_ip}:#{endpoint.private_port}#{mapping.backend}"
options = mapping.options ||= {}
if endpoint.websocket_port
options["websocket_port"] = endpoint.websocket_port
end
options["protocols"]=endpoint.protocols
if mapping.frontend == "" and not cartridge.web_proxy? and web_proxy_cart and not rebuild
logger.info("Skipping default mapping as web proxy owns it for the application")
next
end
if mapping.frontend == "" && (!cartridge.web_proxy?) && (cartridge.name != primary_cartridge.name)
logger.info("Skipping default mapping as primary cartridge owns it for the application")
next
end
if options["ssl_to_gear"]
@container.add_env_var("SSL_TO_GEAR", 1)
logger.debug("Adding SSL_TO_GEAR env var")
end
logger.info("Connecting frontend mapping for #{@container.uuid}/#{cartridge.name}: " "[#{mapping.frontend}] => [#{backend_uri}] with options: #{mapping.options}")
reported_urls = frontend.connect(mapping.frontend, backend_uri, options)
if cartridge.web_proxy?
gear_fqdn = frontend.fqdn
if gear_fqdn != app_dns
frontend.set_fqdn(app_dns)
new_options = options.dup
new_options.delete("target_update")
reported_urls += frontend.connect(mapping.frontend, backend_uri, new_options)
frontend.set_fqdn(gear_fqdn)
end
end
if reported_urls
reported_urls.each do |url|
outstr = "Cartridge #{cartridge.name} exposed URL #{url}"
if endpoint.description
outstr << " for #{endpoint.description}"
end
output << "CLIENT_RESULT: #{outstr}\n"
output << "NOTIFY_MAPPING_CREATE #{outstr}\n"
end
end
end
end
rescue Exception => e
logger.warn("V2CartModel#connect_frontend: #{e.message}\n#{e.backtrace.join("\n")}")
raise
end
output
end
new(...).connector_execute(cartridge_name, connection_type, connector, args) => String
click to toggle source
def connector_execute(cart_name, pub_cart_name, connection_type, connector, args)
raise ArgumentError.new('cart_name cannot be nil') unless cart_name
cartridge = get_cartridge(cart_name)
env = ::OpenShift::Runtime::Utils::Environ.for_gear(@container.container_dir, PathUtils.join(@container.container_dir, cartridge.directory))
env_var_hook = connection_type.start_with?("ENV:") && pub_cart_name
if env_var_hook
set_connection_hook_env_vars(cart_name, pub_cart_name, args)
args = convert_to_shell_arguments(args)
end
conn = Runtime::PubSubConnector.new connection_type, connector
if conn.reserved?
begin
return send(conn.action_name, cartridge, args)
rescue NoMethodError => e
logger.debug "#{e.message}; falling back to script"
end
end
cartridge_home = PathUtils.join(@container.container_dir, cartridge.directory)
script = PathUtils.join(cartridge_home, 'hooks', conn.name)
unless File.executable?(script)
if env_var_hook
return "Set environment variables successfully"
else
msg = "ERROR: action '#{connector}' not found."
raise ::OpenShift::Runtime::Utils::ShellExecutionException.new(msg, 127, msg)
end
end
command = script << " " << args
out, err, rc = @container.run_in_container_context(command,
env: env,
chdir: cartridge_home,
timeout: @hourglass.remaining)
if 0 == rc
logger.info("(#{rc})\n------\n#{Runtime::Utils.sanitize_credentials(out)}\n------)")
return out
end
logger.info("ERROR: (#{rc})\n------\n#{Runtime::Utils.sanitize_credentials(out)}\n------)")
raise ::OpenShift::Runtime::Utils::ShellExecutionException.new(
"Control action '#{connector}' returned an error. rc=#{rc}\n#{out}", rc, out, err)
end
convert_to_shell_arguments(args)
click to toggle source
Convert env var hook arguments to shell arguments TODO: document expected
form of args
def convert_to_shell_arguments(args)
new_args = []
args[3].each do |k, v|
vstr = v.split("\n").map { |p| p + ";" }.join(' ')
new_args.push "'#{k}'='#{vstr}'"
end
(args[0, 2] << ::Shellwords::shellescape(new_args.join(' '))).join(' ')
end
create_cartridge_directory(cartridge, software_version)
click to toggle source
create_cartridge_directory(cartridge
name) -> nil
Create the cartridges home directory
v2_cart_model.create_cartridge_directory('php-5.3')
def create_cartridge_directory(cartridge, software_version)
logger.info("Creating cartridge directory #{@container.uuid}/#{cartridge.directory}")
target = PathUtils.join(@container.container_dir, cartridge.directory)
CartridgeRepository.instantiate_cartridge(cartridge, target)
ident = Runtime::Manifest.build_ident(cartridge.cartridge_vendor,
cartridge.name,
software_version,
cartridge.cartridge_version)
envs = {}
envs["#{cartridge.short_name}_DIR"] = target + File::SEPARATOR
envs["#{cartridge.short_name}_IDENT"] = ident
write_environment_variables(PathUtils.join(target, 'env'), envs)
envs.clear
envs['namespace'] = @container.namespace if @container.namespace
current_gear_env = ::OpenShift::Runtime::Utils::Environ.for_gear(@container.container_dir)
unless current_gear_env['OPENSHIFT_PRIMARY_CARTRIDGE_DIR']
envs['primary_cartridge_dir'] = target + File::SEPARATOR
logger.info("Cartridge #{cartridge.name} recorded as primary within gear #{@container.uuid}")
end
unless envs.empty?
write_environment_variables(PathUtils.join(@container.container_dir, '.env'), envs)
end
old_path = PathUtils.join(@container.container_dir, '.env', 'PATH')
File.delete(old_path) if File.file? old_path
secure_cartridge(cartridge.short_name, @container.uid, @container.gid, target)
logger.info("Created cartridge directory #{@container.uuid}/#{cartridge.directory}")
nil
end
create_dependency_directories(cartridge)
click to toggle source
Creates cartridge dependency directories listed in managed_files.yml.
The directories are created in ~/app-root/runtime/dependencies and/or
~/app-root/runtime/build-dependencies.
This method also creates symlinks from the cartridge directory to the
appropriate symlink in
~/app-root/runtime/{dependencies,build-dependencies}. For example:
~/php/phplib -> ~/app-root/runtime/dependencies/php/phplib
def create_dependency_directories(cartridge)
%(build-dependencies dependencies).each do |dependencies_dir_name|
if dependencies_dir_name == 'build-dependencies'
dirs = @container.build_dependency_dirs(cartridge)
else
dirs = @container.dependency_dirs(cartridge)
end
dirs.each do |entry|
if entry.is_a?(String)
link = target = entry
else
link = entry.keys[0]
target = entry.values[0]
end
dependencies_dir = PathUtils.join(@container.container_dir, 'app-root', 'runtime', dependencies_dir_name)
FileUtils.mkdir_p(PathUtils.join(dependencies_dir, target))
full_link = PathUtils.join(@container.container_dir, link)
if link.count('/') > 0
parts = link.split('/')
path = @container.container_dir
parts.each do |part|
path = PathUtils.join(path, part)
next if File.exist?(path)
break
end
FileUtils.mkdir_p(PathUtils.join(@container.container_dir, parts[0..-2]))
if path != full_link
PathUtils.oo_chown_R(@container.uid, @container.gid, path)
end
end
full_target = PathUtils.join(@container.container_dir, 'app-root', 'runtime', dependencies_dir_name, target)
if !File.exist?(full_link)
FileUtils.ln_s(full_target, full_link)
PathUtils.oo_lchown(@container.uid, @container.gid, full_link)
end
PathUtils.oo_chown_R(@container.uid, @container.gid, dependencies_dir)
end
end
end
create_private_endpoints(cartridge)
click to toggle source
Allocates and assigns private IP/port entries for a cartridge based on
endpoint metadata for the cartridge.
Returns nil on success, or raises an exception if any errors occur: all
errors here are considered fatal.
def create_private_endpoints(cartridge)
raise "Cartridge is required" unless cartridge
return unless cartridge.endpoints && cartridge.endpoints.length > 0
logger.info "Creating #{cartridge.endpoints.length} private endpoints for #{@container.uuid}/#{cartridge.directory}"
env = ::OpenShift::Runtime::Utils::Environ.for_gear(@container.container_dir, PathUtils.join(@container.container_dir, cartridge.directory))
allocated_ips = {}
allocated_endpoints = []
cartridge.endpoints.each do |endpoint|
unless allocated_ips.has_key?(endpoint.private_ip_name)
if env.has_key?(endpoint.private_ip_name)
allocated_ips[endpoint.private_ip_name] = env[endpoint.private_ip_name]
else
private_ip = find_open_ip(endpoint.private_port)
if private_ip.nil?
raise "No IP was available to create endpoint for cart #{cartridge.name} in gear #{@container.uuid}: " "#{endpoint.private_ip_name}(#{endpoint.private_port})"
end
@container.add_env_var(endpoint.private_ip_name, private_ip)
allocated_ips[endpoint.private_ip_name] = private_ip
end
end
private_ip = allocated_ips[endpoint.private_ip_name]
next if env[endpoint.private_port_name]
allocated_endpoints << endpoint
@container.add_env_var(endpoint.private_port_name, endpoint.private_port)
if endpoint.websocket_port_name && endpoint.websocket_port
@container.add_env_var(endpoint.websocket_port_name, endpoint.websocket_port)
end
logger.info("Created private endpoint for cart #{cartridge.name} in gear #{@container.uuid}: " "[#{endpoint.private_ip_name}=#{private_ip}, #{endpoint.private_port_name}=#{endpoint.private_port}]")
if endpoint.options and endpoint.options["ssl_to_gear"]
logger.info("ssl_to_gear option set for the endpoint")
create_public_endpoint(cartridge, endpoint, private_ip)
end
end
address_list = allocated_endpoints.map { |e| {ip: allocated_ips[e.private_ip_name], port: e.private_port} }
if !address_list.empty? && @container.addresses_bound?(address_list, @hourglass)
failures = ''
allocated_endpoints.each do |endpoint|
if @container.address_bound?(allocated_ips[endpoint.private_ip_name], endpoint.private_port, @hourglass, true)
failures << "#{endpoint.private_ip_name}(#{endpoint.private_port})=#{allocated_ips[endpoint.private_ip_name]};"
end
end
raise "Failed to create the following private endpoints due to existing process bindings: #{failures}" unless failures.empty?
end
end
create_public_endpoint(cartridge, endpoint, private_ip)
click to toggle source
Expose an endpoint for a cartridge through the port proxy.
Returns nil on success, or raises an exception if any errors occur: all
errors here are considered fatal.
def create_public_endpoint(cartridge, endpoint, private_ip)
public_port = @container.create_public_endpoint(private_ip, endpoint.private_port)
@container.add_env_var(endpoint.public_port_name, public_port)
logger.info("Created public endpoint for cart #{cartridge.name} in gear #{@container.uuid}: " "[#{endpoint.public_port_name}=#{public_port}]")
end
create_stop_lock()
click to toggle source
Writes the stop_lock file and changes its ownership to the gear
user.
def create_stop_lock
unless stop_lock?
File.new(stop_lock, File::CREAT|File::TRUNC|File::WRONLY, 0644).close()
@container.set_rw_permission(stop_lock)
end
end
delete_cartridge_directory(cartridge)
click to toggle source
def delete_cartridge_directory(cartridge)
logger.info("Deleting cartridge directory for #{@container.uuid}/#{cartridge.directory}")
FileUtils.rm_rf(PathUtils.join(@container.container_dir, cartridge.directory))
logger.info("Deleted cartridge directory for #{@container.uuid}/#{cartridge.directory}")
end
delete_private_endpoint(cartridge, endpoint, remove_private_ip=false)
click to toggle source
def delete_private_endpoint(cartridge, endpoint, remove_private_ip=false)
logger.info "Deleting private endpoint #{endpoint.private_ip_name}:#{endpoint.private_port_name} for #{@container.uuid}/#{cartridge.directory}"
@container.remove_env_var(endpoint.private_ip_name) if remove_private_ip
@container.remove_env_var(endpoint.private_port_name)
disconnect_frontend_for_endpoint(cartridge, endpoint)
logger.info "Deleted private endpoint #{endpoint.private_ip_name}:#{endpoint.private_port_name} for #{@container.uuid}/#{cartridge.directory}"
end
delete_private_endpoints(cartridge)
click to toggle source
def delete_private_endpoints(cartridge)
logger.info "Deleting private endpoints for #{@container.uuid}/#{cartridge.directory}"
cartridge.endpoints.each do |endpoint|
@container.remove_env_var(endpoint.private_ip_name)
@container.remove_env_var(endpoint.private_port_name)
end
logger.info "Deleted private endpoints for #{@container.uuid}/#{cartridge.directory}"
end
destroy(skip_hooks = false)
click to toggle source
destroy(skip_hooks = false) -> [buffer, ", 0]
Remove all cartridges from a gear and delete the gear. Accepts and
discards any parameters to comply with the signature of V1 require, which
accepted a single argument.
destroy() => [", ", 0]
def destroy(skip_hooks = false)
logger.info('V2 destroy')
buffer = ''
begin
unless skip_hooks
each_cartridge do |cartridge|
unlock_gear(cartridge, false) do |c|
begin
buffer << cartridge_teardown(c.directory, false)
rescue ::OpenShift::Runtime::Utils::ShellExecutionException => e
logger.warn("Cartridge teardown operation failed on gear #{@container.uuid} for cartridge #{c.directory}: #{e.message} (rc=#{e.rc})")
end
end
end
end
rescue Exception => e
logger.warn("Cartridge teardown operation failed on gear #{@container.uuid} for some cartridge: #{e.message}")
buffer << "CLIENT_ERROR: Abandoned cartridge teardowns. There may be extraneous data left on system."
end
[buffer, '', 0]
end
disconnect_frontend(cartridge)
click to toggle source
disconnect cartridge from frontend proxy
This is only called when a cartridge is removed from a cartridge not a gear
delete
def disconnect_frontend(cartridge)
gear_env = ::OpenShift::Runtime::Utils::Environ.for_gear(@container.container_dir)
app_dns = gear_env["OPENSHIFT_APP_DNS"].to_s.downcase
mappings = []
cartridge.endpoints.each do |endpoint|
endpoint.mappings.each do |mapping|
mappings << mapping.frontend
end
end
logger.info("Disconnecting frontend mapping for #{@container.uuid}/#{cartridge.name}: #{mappings.inspect}")
unless mappings.empty?
fe_server = FrontendHttpServer.new(@container)
fe_server.disconnect(*mappings)
if cartridge.web_proxy?
gear_fqdn = fe_server.fqdn
if gear_fqdn != app_dns
fe_server.set_fqdn(app_dns)
fe_server.disconnect(*mappings)
fe_server.set_fqdn(gear_fqdn)
end
end
end
end
disconnect_frontend_for_endpoint(cartridge, endpoint)
click to toggle source
def disconnect_frontend_for_endpoint(cartridge, endpoint)
mappings = []
endpoint.mappings.each do |mapping|
mappings << mapping.frontend
end
logger.info("Disconnecting frontend mapping for #{@container.uuid}/#{cartridge.name}: #{mappings.inspect}")
unless mappings.empty?
FrontendHttpServer.new(@container).disconnect(*mappings)
end
end
do_action_hook(action, env, options)
click to toggle source
Executes the named action from the user repo action_hooks
directory and returns the stdout of the execution, or raises a
ShellExecutionException if the action returns a non-zero return
code.
All hyphens in the action will be replaced with underscores.
def do_action_hook(action, env, options)
action = action.gsub(/-/, '_')
action_hooks_dir = PathUtils.join(env['OPENSHIFT_REPO_DIR'], %{.openshift action_hooks})
action_hook = PathUtils.join(action_hooks_dir, action)
buffer = ''
if File.executable?(action_hook)
out, err, rc = @container.run_in_container_context(action_hook,
env: env,
chdir: @container.container_dir,
timeout: @hourglass.remaining,
out: options[:out],
err: options[:err])
raise ::OpenShift::Runtime::Utils::ShellExecutionException.new(
"CLIENT_ERROR: Failed to execute action hook '#{action}' for #{@container.uuid} application #{@container.application_name}",
rc, out, err
) if rc != 0
end
buffer << out if out.is_a?(String)
buffer << err if err.is_a?(String)
buffer
end
do_control(action, cartridge, options={})
click to toggle source
def do_control(action, cartridge, options={})
case cartridge
when String
cartridge_dir = cartridge_directory(cartridge)
when Manifest
cartridge_dir = cartridge.directory
else
raise "Unsupported cartridge argument type: #{cartridge.class}"
end
options[:cartridge_dir] = cartridge_dir
do_control_with_directory(action, options)
end
new(...).do_control_with_directory(action, options) → output
click to toggle source
new(...).do_control_with_directory(action) → output
Call action on cartridge control script. Run all pre/post hooks if
found.
options: hash
:cartridge_dir => path : Process all cartridges (if +nil+) or the provided cartridge
:pre_action_hooks_enabled => true : Whether to process repo action hooks before +action+
:post_action_hooks_enabled => true : Whether to process repo action hooks after +action+
:prefix_action_hooks => true : If +true+, action hook names are automatically prefixed with
'pre' and 'post' depending on their execution order.
:out : An +IO+ object to which control script STDOUT should be directed. If
+nil+ (the default), output is logged.
:err : An +IO+ object to which control script STDERR should be directed. If
+nil+ (the default), output is logged.
:env_overrides : environment variable overrides
def do_control_with_directory(action, options={})
cartridge_dir = options[:cartridge_dir]
pre_action_hooks_enabled = options.has_key?(:pre_action_hooks_enabled) ? options[:pre_action_hooks_enabled] : true
post_action_hooks_enabled = options.has_key?(:post_action_hooks_enabled) ? options[:post_action_hooks_enabled] : true
prefix_action_hooks = options.has_key?(:prefix_action_hooks) ? options[:prefix_action_hooks] : true
logger.debug { "#{@container.uuid} #{action} against '#{cartridge_dir}'" }
buffer = ''
gear_env = ::OpenShift::Runtime::Utils::Environ.for_gear(@container.container_dir)
gear_env.merge!(options[:env_overrides]) if options[:env_overrides]
action_hooks = PathUtils.join(gear_env['OPENSHIFT_REPO_DIR'], %{.openshift action_hooks})
if pre_action_hooks_enabled
pre_action_hook = prefix_action_hooks ? "pre_#{action}" : action
hook_buffer = do_action_hook(pre_action_hook, gear_env, options)
buffer << hook_buffer if hook_buffer.is_a?(String)
end
process_cartridges(cartridge_dir) { |path|
cartridge_local_env = ::OpenShift::Runtime::Utils::Environ.load(PathUtils.join(path, 'env'))
cartridge_local_env.delete('PATH')
ident = cartridge_local_env.keys.grep(/^OPENSHIFT_.*_IDENT/)
_, software, software_version, _ = Runtime::Manifest.parse_ident(cartridge_local_env[ident.first])
hooks = cartridge_hooks(action_hooks, action, software, software_version)
cartridge_env = gear_env.merge(cartridge_local_env)
control = PathUtils.join(path, 'bin', 'control')
command = []
command << hooks[:pre] unless hooks[:pre].empty?
if File.executable? control
if options[:args]
args = Shellwords::shellescape(options[:args])
end
command << "#{control} #{action} #{args}"
end
command << hooks[:post] unless hooks[:post].empty?
unless command.empty?
command = ['set -e'] | command
out, err, rc = @container.run_in_container_context(command.join('; '),
env: cartridge_env,
chdir: path,
timeout: @hourglass.remaining,
out: options[:out],
err: options[:err])
buffer << out if out.is_a?(String)
buffer << err if err.is_a?(String)
raise ::OpenShift::Runtime::Utils::ShellExecutionException.new(
"CLIENT_ERROR: Failed to execute: 'control #{action}' for #{path}", rc, out, err
) if rc != 0
end
}
if post_action_hooks_enabled
post_action_hook = prefix_action_hooks ? "post_#{action}" : action
hook_buffer = do_action_hook(post_action_hook, gear_env, options)
buffer << hook_buffer if hook_buffer.is_a?(String)
end
buffer
end
do_lock(entries)
click to toggle source
do_lock_gear(array of file names) -> array
Take the given array of file system entries and prepare them for the
application developer
v2_cart_model.do_lock_gear(entries)
def do_lock(entries)
entries.each do |entry|
begin
@container.set_ro_permission(entry)
rescue Exception => e
raise OpenShift::Runtime::FileLockError.new("Failed to lock file system entry [#{entry}]: #{e}",
entry)
end
end
begin
@container.set_ro_permission(@container.container_dir)
rescue Exception => e
raise OpenShift::Runtime::FileLockError.new("Failed to lock gear home [#{@container.container_dir}]: #{e}",
@container.container_dir)
end
end
do_unlock(entries)
click to toggle source
do_unlock_gear(array of file names) -> array
Take the given array of file system entries and prepare them for the
cartridge author
v2_cart_model.do_unlock_gear(entries)
def do_unlock(entries)
entries.each do |entry|
if entry.end_with?('/')
entry.chomp!('/')
FileUtils.mkpath(entry, mode: 0755) unless File.exist? entry
else
File.new(entry, File::CREAT|File::TRUNC|File::WRONLY, 0644).close() unless File.exist?(entry)
end
begin
@container.set_rw_permission(entry)
rescue Exception => e
raise FileUnlockError.new("Failed to unlock file system entry [#{entry}]: #{e}",
entry)
end
end
begin
@container.set_rw_permission(@container.container_dir)
rescue Exception => e
raise FileUnlockError.new(
"Failed to unlock gear home [#{@container.container_dir}]: #{e}",
@container.container_dir)
end
end
each_cartridge()
click to toggle source
Yields a Cartridge instance for each cartridge in the gear.
def each_cartridge
process_cartridges do |cartridge_dir|
cartridge = get_cartridge_from_directory(File.basename(cartridge_dir))
yield cartridge
end
end
find_open_ip(port)
click to toggle source
Finds the next IP address available for binding of the given port for the
current gear user. The IP is assumed to be available only if the IP is not
already associated with an existing endpoint defined by any cartridge
within the gear.
Returns a string IP address in dotted-quad notation if one is available for
the given port, or returns nil if IP is available.
def find_open_ip(port)
allocated_ips = get_allocated_private_ips
logger.debug("IPs already allocated for #{port} in gear #{@container.uuid}: #{allocated_ips}")
open_ip = nil
for host_ip in 1..127
candidate_ip = @container.get_ip_addr(host_ip)
next if allocated_ips.include?(candidate_ip)
open_ip = candidate_ip
break
end
open_ip
end
get_allocated_private_ips()
click to toggle source
Returns an array containing all currently allocated endpoint private IP
addresses assigned to carts within the current gear, or an empty array if
none are currently defined.
def get_allocated_private_ips
env = ::OpenShift::Runtime::Utils::Environ::for_gear(@container.container_dir)
allocated_ips = []
process_cartridges do |cart_path|
cart_dir = File.basename(cart_path)
cart = get_cartridge_from_directory(cart_dir)
cart.endpoints.each do |endpoint|
ip = env[endpoint.private_ip_name]
allocated_ips << ip unless ip == nil
end
end
allocated_ips
end
get_cartridge(cart_name)
click to toggle source
Load the cartridge's local manifest from the Broker token 'name-version'
def get_cartridge(cart_name)
unless @cartridges.has_key? cart_name
cart_dir = ''
begin
cart_dir = cartridge_directory(cart_name)
@cartridges[cart_name] = get_cartridge_from_directory(cart_dir)
rescue Exception => e
logger.error e.message
logger.error e.backtrace.join("\n")
raise "Failed to get cartridge '#{cart_name}' from #{cart_dir} in gear #{@container.uuid}: #{e.message}"
end
end
@cartridges[cart_name]
end
get_cartridge_fallback(cart_name)
click to toggle source
Load the cartridge's local manifest from the Broker token 'name-version'
def get_cartridge_fallback(cart_name)
directory = cartridge_directory(cart_name)
_, version = map_cartridge_name(cart_name)
raise "Directory name is required" if (directory == nil || directory.empty?)
cartridge_path = PathUtils.join(@container.container_dir, directory)
manifest_path = PathUtils.join(cartridge_path, 'metadata', 'manifest.yml')
raise "Cartridge manifest not found: #{manifest_path} missing" unless File.exists?(manifest_path)
Manifest.new(manifest_path, version, :file, @container.container_dir)
end
get_cartridge_from_directory(directory)
click to toggle source
Load cartridge's local manifest from cartridge directory name
def get_cartridge_from_directory(directory)
raise "Directory name is required" if (directory == nil || directory.empty?)
unless @cartridges.has_key? directory
cartridge_path = PathUtils.join(@container.container_dir, directory)
manifest_path = PathUtils.join(cartridge_path, 'metadata', 'manifest.yml')
ident_path = Dir.glob(PathUtils.join(cartridge_path, 'env', "OPENSHIFT_*_IDENT")).first
raise "Cartridge manifest not found: #{manifest_path} missing" unless File.exists?(manifest_path)
raise MissingCartridgeIdentError, "Cartridge Ident not found in #{cartridge_path}" unless ident_path
_, _, version, _ = Runtime::Manifest.parse_ident(IO.read(ident_path))
@cartridges[directory] = Manifest.new(manifest_path, version, :file, @container.container_dir)
end
@cartridges[directory]
end
has_repository?()
click to toggle source
def has_repository?
ApplicationRepository.new(@container).exists?
end
list_proxy_mappings()
click to toggle source
def list_proxy_mappings
proxied_ports = []
gear_env = ::OpenShift::Runtime::Utils::Environ.for_gear(@container.container_dir)
each_cartridge do |cartridge|
cartridge.endpoints.each do |endpoint|
next if gear_env[endpoint.public_port_name].nil?
proxied_ports << {
:private_ip_name => endpoint.private_ip_name,
:public_port_name => endpoint.public_port_name,
:private_ip => gear_env[endpoint.private_ip_name],
:private_port => endpoint.private_port,
:proxy_port => gear_env[endpoint.public_port_name],
}
end
end
proxied_ports
end
map_cartridge_name(cartridge_name)
click to toggle source
FIXME: Once Broker/Node protocol updated to provided necessary information
this hack must go away
def map_cartridge_name(cartridge_name)
results = cartridge_name.split(/\-([0-9\.]+)$/)
if !Runtime::Manifest.valid_cartridge_name?(cartridge_name)
raise "Invalid cartridge identifier '#{cartridge_name}': expected name-version"
end
results
end
populate_gear_repo(cartridge name) => nil
click to toggle source
populate_gear_repo(cartridge name, application git template url) → nil
Populate the gear git repository with a sample application
model.populate_gear_repo('ruby-1.9')
model.populate_gear_repo('ruby-1.9', 'http://rails-example.example.com')
def populate_gear_repo(cartridge_name, template_url = nil)
logger.info "Creating gear repo for #{@container.uuid}/#{cartridge_name} from `#{template_url}`"
repo = ApplicationRepository.new(@container)
if template_url.nil?
repo.populate_from_cartridge(cartridge_name)
elsif OpenShift::Git.empty_clone_spec?(template_url)
repo.populate_empty(cartridge_name)
else
repo.populate_from_url(cartridge_name, template_url)
end
if repo.exist?
repo.archive(PathUtils.join(@container.container_dir, 'app-root', 'runtime', 'repo'), 'master')
end
""
end
post_configure(cartridge_name)
click to toggle source
def post_configure(cartridge_name)
output = ''
name, software_version = map_cartridge_name(cartridge_name)
cartridge = get_cartridge(name)
::OpenShift::Runtime::Utils::Cgroups.new(@container.uuid).boost do
if empty_repository?
output << "CLIENT_MESSAGE: An empty Git repository has been created for your application. Use 'git push' to add your code." if cartridge.name == primary_cartridge.name
else
output << start_cartridge('start', cartridge, user_initiated: true)
end
output << cartridge_action(cartridge, 'post_install', software_version)
end
logger.info("post-configure output: #{output}")
output
rescue ::OpenShift::Runtime::Utils::ShellExecutionException => e
raise ::OpenShift::Runtime::Utils::Sdk.translate_shell_ex_for_client(e, 157)
end
post_install(cartridge, software_version, options = {})
click to toggle source
def post_install(cartridge, software_version, options = {})
output = cartridge_action(cartridge, 'post_install', software_version)
options[:out].puts(output) if options[:out]
output
end
primary_cartridge()
click to toggle source
Returns the primary Cartridge in the gear as specified by the
OPENSHIFT_PRIMARY_CARTRIDGE_DIR environment variable, or
Nil if no primary cartridge is present.
def primary_cartridge
env = ::OpenShift::Runtime::Utils::Environ.for_gear(@container.container_dir)
primary_cart_dir = env['OPENSHIFT_PRIMARY_CARTRIDGE_DIR']
raise "No primary cartridge detected in gear #{@container.uuid}" unless primary_cart_dir and !primary_cart_dir.empty?
return get_cartridge_from_directory(File.basename(primary_cart_dir))
end
process_cartridges(cartridge_dir = nil)
click to toggle source
Run code block against each cartridge in gear
@param [block] Code block to process cartridge @yields [String] cartridge
directory for each cartridge in gear
def process_cartridges(cartridge_dir = nil)
if cartridge_dir
cart_dir = PathUtils.join(@container.container_dir, cartridge_dir)
yield cart_dir if File.exist?(cart_dir)
return
end
Dir[PathUtils.join(@container.container_dir, "*")].each do |cart_dir|
next if File.symlink?(cart_dir) || !File.exist?(PathUtils.join(cart_dir, "metadata", "manifest.yml"))
yield cart_dir
end if @container.container_dir and File.exist?(@container.container_dir)
end
process_erb_templates(cartridge)
click to toggle source
process_erb_templates(cartridge_name)
-> nil
Search cartridge for any remaining erb files render them
def process_erb_templates(cartridge)
buffer = ''
directory = PathUtils.join(@container.container_dir, cartridge.name)
logger.info "Processing ERB templates for #{cartridge.name}"
env = ::OpenShift::Runtime::Utils::Environ.for_gear(@container.container_dir, directory)
erbs = @container.processed_templates(cartridge).map { |x| PathUtils.join(@container.container_dir, x) }
erbs.delete_if do |erb_file|
reject = !File.exists?(erb_file)
if reject
buffer << "CLIENT_ERROR: File declared in processed_templates does not exist and will not be rendered: #{erb_file}"
end
reject
end
render_erbs(env, erbs)
buffer
end
render_erbs(env, erbs)
click to toggle source
render_erbs(program
environment as a hash, erbs) -> nil
Run erb against each template file submitted
v2_cart_model.render_erbs({HOMEDIR => '/home/no_place_like'}, ['/var/lib/openshift/user/cart/foo.erb', ...])
def render_erbs(env, erbs)
erbs.each do |file|
begin
@container.run_in_container_context(%{/usr/bin/oo-erb -S 2 -- #{file} > #{file.chomp('.erb')}},
env: env,
chdir: @container.container_dir,
timeout: @hourglass.remaining,
expected_exitstatus: 0)
rescue ::OpenShift::Runtime::Utils::ShellExecutionException => e
logger.info("Failed to render ERB #{file}: #{e.stderr}")
else
begin
File.delete(file)
rescue Errno::ENOENT
end
end
end
nil
end
restart_gear(options={})
click to toggle source
Restarts catridges in the gear by running the cartridge restart
control action for each cartridge in the gear.
By default, all cartridges in the gear are restarted. The selection of
cartridges to be restarted is configurable via options.
options: hash
:primary_only => [boolean] : If +true+, only the primary cartridge will be restarted.
Mutually exclusive with +secondary_only+.
:secondary_only => [boolean] : If +true+, all cartridges except the primary cartridge
will be restarted. Mutually exclusive with +primary_only+.
:user_initiated => [boolean] : Indicates whether the operation was user initated.
Default is +true+.
:exclude_web_proxy => [boolean] : Indicates whether to exclude restarting the web proxy cartridge.
Default is +false+
:out : An +IO+ object to which control script STDOUT should be directed. If
+nil+ (the default), output is logged.
:err : An +IO+ object to which control script STDERR should be directed. If
+nil+ (the default), output is logged.
Returns the combined output of all restart action executions as a
String.
def restart_gear(options={})
options[:user_initiated] = true if not options.has_key?(:user_initiated)
if options[:primary_only] && options[:secondary_only]
raise ArgumentError.new('The primary_only and secondary_only options are mutually exclusive options')
end
buffer = ''
if options[:primary_only] || options[:secondary_only]
each_cartridge do |cartridge|
next if options[:primary_only] and cartridge.name != primary_cartridge.name
next if options[:secondary_only] and cartridge.name == primary_cartridge.name
next if options[:exclude_web_proxy] and cartridge.web_proxy?
buffer << start_cartridge('restart', cartridge, options)
end
else
buffer << restart_gear(options.merge({secondary_only: true}))
buffer << restart_gear(options.merge({primary_only: true}))
end
buffer
end
secure_cartridge(short_name, uid, gid=uid, cartridge_home)
click to toggle source
def secure_cartridge(short_name, uid, gid=uid, cartridge_home)
@container.set_rw_permission_R(cartridge_home)
files = ManagedFiles::IMMUTABLE_FILES.collect do |file|
file = file.gsub('*', short_name)
file = PathUtils.join(cartridge_home, file)
file if File.exist?(file)
end || []
files.compact!
unless files.empty?
@container.set_ro_permission(files)
FileUtils.chmod(0644, files)
end
end
set_connection_hook_env_vars(cart_name, pub_cart_name, args)
click to toggle source
def set_connection_hook_env_vars(cart_name, pub_cart_name, args)
logger.info("Setting env vars for #{cart_name} from #{pub_cart_name}")
logger.info("ARGS: #{args.inspect}")
env_dir_path = PathUtils.join(@container.container_dir, '.env', short_name_from_full_cart_name(pub_cart_name))
FileUtils.mkpath(env_dir_path)
envs = {}
pairs = args[3].values[0].split("\n")
pairs.each do |pair|
k, v = pair.strip.split("=")
envs[k] = v
end
write_environment_variables(env_dir_path, envs, false)
end
short_name_from_full_cart_name(pub_cart_name)
click to toggle source
def short_name_from_full_cart_name(pub_cart_name)
raise ArgumentError.new('pub_cart_name cannot be nil') unless pub_cart_name
return pub_cart_name if pub_cart_name.index('-').nil?
tokens = pub_cart_name.split('-')
tokens.pop
tokens.join('-')
end
standalone_web_proxy?()
click to toggle source
Returns true if the primary Cartridge is also the
web_proxy. This occurs in the case of applications that have a web
cartridge that is deployed on a platform different than the web proxy's.
def standalone_web_proxy?
(web_proxy != nil) and (web_proxy.name == primary_cartridge.name)
end
start_cartridge(type, cartridge, options={})
click to toggle source
Starts a cartridge.
Both application state and the stop lock are managed during the operation.
If start of the primary cartridge is invoked and user_initiated is
true, the stop lock is created.
type : Type of start [start, restart, reload]
cartridge : A Cartridge instance or String name
of a cartridge. options : hash
:user_initiated => [boolean] : Indicates whether the operation was user initated.
Default is +true+.
:hot_deploy => [boolean] : If +true+ and if +cartridge+ is the primary cartridge in the gear, the
gear state will be set to +STARTED+ but the actual cartridge start operation
will be skipped. Non-primary cartridges will be skipped with no state change.
Default is +false+.
:out : An +IO+ object to which control script STDOUT should be directed. If
+nil+ (the default), output is logged.
:err : An +IO+ object to which control script STDERR should be directed. If
+nil+ (the default), output is logged.
Returns the output of the operation as a String or raises a
ShellExecutionException if the cartridge script fails.
def start_cartridge(type, cartridge, options={})
options[:user_initiated] = true if not options.has_key?(:user_initiated)
options[:hot_deploy] = false if not options.has_key?(:hot_deploy)
cartridge = get_cartridge(cartridge) if cartridge.is_a?(String)
if not options[:user_initiated] and stop_lock?
return "Not starting cartridge #{cartridge.name} because the application was explicitly stopped by the user"
end
if cartridge.name == primary_cartridge.name
FileUtils.rm_f(stop_lock) if options[:user_initiated]
frontend = FrontendHttpServer.new(@container)
if Process.uid == @container.uid
frontend.unprivileged_unidle
else
frontend.unidle
end
@state.value = State::STARTED
end
if options[:hot_deploy]
output = "Not starting cartridge #{cartridge.name} because hot deploy is enabled"
options[:out].puts(output) if options[:out]
return output
end
do_control(type, cartridge, options)
end
start_gear(options={})
click to toggle source
Starts up the gear by running the cartridge start control action
for each cartridge in the gear.
By default, all cartridges in the gear are started. The selection of
cartridges to be started is configurable via options.
options: hash
:primary_only => [boolean] : If +true+, only the primary cartridge will be started.
Mutually exclusive with +secondary_only+.
:secondary_only => [boolean] : If +true+, all cartridges except the primary cartridge
will be started. Mutually exclusive with +primary_only+.
:user_initiated => [boolean] : Indicates whether the operation was user initated.
Default is +true+.
:exclude_web_proxy => [boolean] : Indicates whether to exclude stopping the web proxy cartridge.
Default is +false+
:out : An +IO+ object to which control script STDOUT should be directed. If
+nil+ (the default), output is logged.
:err : An +IO+ object to which control script STDERR should be directed. If
+nil+ (the default), output is logged.
Returns the combined output of all start action executions as a
String.
def start_gear(options={})
options[:user_initiated] = true if not options.has_key?(:user_initiated)
if options[:primary_only] && options[:secondary_only]
raise ArgumentError.new('The primary_only and secondary_only options are mutually exclusive options')
end
buffer = ''
if options[:primary_only] || options[:secondary_only]
each_cartridge do |cartridge|
next if options[:primary_only] and cartridge.name != primary_cartridge.name
next if options[:secondary_only] and cartridge.name == primary_cartridge.name
next if options[:exclude_web_proxy] and cartridge.web_proxy?
buffer << start_cartridge('start', cartridge, options)
end
else
buffer << start_gear(options.merge({secondary_only: true}))
buffer << start_gear(options.merge({primary_only: true}))
end
buffer
end
stop_cartridge(cartridge, options={})
click to toggle source
Stops a cartridge.
Both application state and the stop lock are managed during the operation.
If stop of the primary cartridge is invoked and user_initiated is
true, the stop lock is removed.
cartridge : A Cartridge instance or String name
of a cartridge. options : hash
:user_initiated => [boolean] : Indicates whether the operation was user initated.
Default is +true+.
:hot_deploy => [boolean] : If +true+, the stop operation is skipped for all cartridge types,
the gear state is not modified, and the stop lock is never created.
Default is +false+.
:out : An +IO+ object to which control script STDOUT should be directed. If
+nil+ (the default), output is logged.
:err : An +IO+ object to which control script STDERR should be directed. If
+nil+ (the default), output is logged.
Returns the output of the operation as a String or raises a
ShellExecutionException if the cartridge script fails.
def stop_cartridge(cartridge, options={})
options[:user_initiated] = true if not options.has_key?(:user_initiated)
options[:hot_deploy] = false if not options.has_key?(:hot_deploy)
cartridge = get_cartridge(cartridge) if cartridge.is_a?(String)
if options[:hot_deploy]
output = "Not stopping cartridge #{cartridge.name} because hot deploy is enabled"
options[:out].puts(output) if options[:out]
return output
end
if not options[:user_initiated] and stop_lock?
return "Not stopping cartridge #{cartridge.name} because the application was explicitly stopped by the user\n"
end
if cartridge.name == primary_cartridge.name
create_stop_lock if options[:user_initiated]
@state.value = State::STOPPED
end
do_control('stop', cartridge, options)
end
stop_gear(options={})
click to toggle source
Shuts down the gear by running the cartridge stop control action
for each cartridge in the gear.
options: hash
:user_initiated => [boolean] : Indicates whether the operation was user initated.
Default is +true+.
:exclude_web_proxy => [boolean] : Indicates whether to exclude stopping the web proxy cartridge.
Default is +false+
:out : An +IO+ object to which control script STDOUT should be directed. If
+nil+ (the default), output is logged.
:err : An +IO+ object to which control script STDERR should be directed. If
+nil+ (the default), output is logged.
Returns the combined output of all stop action executions as a
String.
def stop_gear(options={})
options[:user_initiated] = true if not options.has_key?(:user_initiated)
buffer = ''
each_cartridge do |cartridge|
next if options[:exclude_web_proxy] and cartridge.web_proxy?
buffer << stop_cartridge(cartridge, options)
end
buffer
end
stop_lock()
click to toggle source
def stop_lock
PathUtils.join(@container.container_dir, 'app-root', 'runtime', '.stop_lock')
end
stop_lock?()
click to toggle source
def stop_lock?
File.exists?(stop_lock)
end
tidy()
click to toggle source
tidy() -> nil
Run tidy operation on each cartridge on a gear
tidy()
def tidy
each_cartridge do |cartridge|
begin
output = do_control('tidy', cartridge)
rescue ::OpenShift::Runtime::Utils::ShellExecutionException => e
logger.warn("Tidy operation failed for cartridge #{cartridge.name} on " "gear #{@container.uuid}: #{e.message} (rc=#{e.rc}), output=#{output}")
end
end
end
unlock_gear(cartridge, relock=true)
click to toggle source
unlock_gear(cartridge_name)
-> nil
Prepare the given cartridge for the cartridge author
v2_cart_model.unlock_gear('php-5.3')
def unlock_gear(cartridge, relock=true)
begin
do_unlock(@container.locked_files(cartridge))
yield cartridge
ensure
do_lock(relock ? @container.locked_files(cartridge) : [])
end
nil
end
unsubscribe(cart_name, pub_cart_name)
click to toggle source
Let a cart perform some action when another cart is being removed Today, it
is used to cleanup environment variables
def unsubscribe(cart_name, pub_cart_name)
env_dir_path = PathUtils.join(@container.container_dir, '.env', short_name_from_full_cart_name(pub_cart_name))
FileUtils.rm_rf(env_dir_path)
end
validate_cartridge(manifest)
click to toggle source
def validate_cartridge(manifest)
illegal_overrides = ::OpenShift::Runtime::Utils::Environ.load(PathUtils.join(@container.container_dir, '.env')).keys &
::OpenShift::Runtime::Utils::Environ.load(PathUtils.join(@container.container_dir, manifest.directory, 'env')).keys
illegal_overrides.delete('LD_LIBRARY_PATH')
illegal_overrides.delete('PATH')
unless illegal_overrides.empty?
raise RuntimeError.new(
"Cartridge attempted to override the following gear environment variables: #{illegal_overrides.join(', ')}")
end
end
web_proxy()
click to toggle source
Returns the Cartridge in the gear whose web_proxy flag is
set to true, nil otherwise
def web_proxy
each_cartridge do |cartridge|
return cartridge if cartridge.web_proxy?
end
nil
end
write_environment_variables(path, hash, prefix = true)
click to toggle source
Write out environment variables.
def write_environment_variables(path, hash, prefix = true)
FileUtils.mkpath(path) unless File.exist? path
hash.each_pair do |k, v|
name = k.to_s.upcase
if prefix
name = "OPENSHIFT_#{name}"
end
File.open(PathUtils.join(path, name), 'w', 0666) do |f|
f.write(v)
end
end
end