Object
Public: Return an enumerator which provides an ApplicationContainer object for every OpenShift gear in the system.
Caveat: the quota information will not be populated.
# File lib/openshift-origin-node/model/application_container.rb, line 678 def self.all(hourglass=nil, loadenv=true) Enumerator.new do |yielder| config = OpenShift::Config.new gecos = config.get("GEAR_GECOS") || "OO application container" # Some duplication with from_uuid; it may be expensive to keep re-parsing passwd. # Etc is not reentrent. Capture the password table in one shot. pwents = [] Etc.passwd do |pwent| if pwent.gecos == gecos pwents << pwent.clone end end pwents.each do |pwent| # The path is a performance hack to load only the variables we need if loadenv env = ::OpenShift::Runtime::Utils::Environ.load(File.join(pwent.dir, '.env', 'OPENSHIFT_{APP,GEAR}_{UUID,NAME,DNS}*')) else env = {} end if env['OPENSHIFT_GEAR_DNS'] == nil namespace = nil else namespace = env['OPENSHIFT_GEAR_DNS'].sub(/\..*$/,"").sub(/^.*\-/,"") end begin a=ApplicationContainer.new(env["OPENSHIFT_APP_UUID"], pwent.name, pwent, env["OPENSHIFT_APP_NAME"], env["OPENSHIFT_GEAR_NAME"], namespace, nil, nil, hourglass) rescue => e NodeLogger.logger.error("Failed to instantiate ApplicationContainer for uid #{pwent.uid}/uuid #{env["OPENSHIFT_APP_UUID"]}: #{e}") NodeLogger.logger.error("Backtrace: #{e.backtrace}") else yielder.yield(a) end end end end
Public: Return an enumerator which provides a list of uuids for every OpenShift gear in the system.
# File lib/openshift-origin-node/model/application_container.rb, line 656 def self.all_uuids(hourglass=nil) Enumerator.new do |yielder| config = OpenShift::Config.new gecos = config.get("GEAR_GECOS") || "OO application container" uuids = [] Etc.passwd do |pwent| uuids << pwent.name if pwent.gecos == gecos end uuids.each do |uuid| yielder.yield(uuid) end end end
# File lib/openshift-origin-node/model/application_container.rb, line 166 def self.exists?(container_uuid) passwd_for(container_uuid) rescue false end
Public: Return a ApplicationContainer object loaded from the gear_uuid on the system
Caveat: the quota information will not be populated.
# File lib/openshift-origin-node/model/application_container.rb, line 136 def self.from_uuid(container_uuid, hourglass=nil) raise ArgumentError, "container_uuid is required!" if container_uuid.nil? or container_uuid.empty? pwent = passwd_for(container_uuid) env = ::OpenShift::Runtime::Utils::Environ.load(File.join(pwent.dir, '.env', 'OPENSHIFT_{APP,GEAR}_{UUID,NAME,DNS}*')) if env['OPENSHIFT_GEAR_DNS'] == nil namespace = nil else namespace = env['OPENSHIFT_GEAR_DNS'].sub(/\..*$/,"").sub(/^.*\-/,"") end raise "OPENSHIFT_APP_UUID is missing!" if env["OPENSHIFT_APP_UUID"].nil? raise "OPENSHIFT_APP_NAME is missing!" if env["OPENSHIFT_APP_NAME"].nil? raise "OPENSHIFT_GEAR_NAME is missing!" if env["OPENSHIFT_GEAR_NAME"].nil? ApplicationContainer.new(env["OPENSHIFT_APP_UUID"], container_uuid, pwent, env["OPENSHIFT_APP_NAME"], env["OPENSHIFT_GEAR_NAME"], namespace, nil, nil, hourglass) end
# File lib/openshift-origin-node/model/application_container.rb, line 91 def initialize(application_uuid, container_uuid, user_uid = nil, application_name = nil, container_name = nil, namespace = nil, quota_blocks = nil, quota_files = nil, hourglass = nil) @config = ::OpenShift::Config.new @uuid = container_uuid @application_uuid = application_uuid @container_name = container_name @application_name = application_name @namespace = namespace @quota_blocks = quota_blocks @quota_files = quota_files @base_dir = @config.get("GEAR_BASE_DIR") @skel_dir = @config.get("GEAR_SKEL_DIR") || DEFAULT_SKEL_DIR @supplementary_groups = @config.get("GEAR_SUPPLEMENTARY_GROUPS") @hourglass = hourglass || ::OpenShift::Runtime::Utils::Hourglass.new(3600) begin user_info = user_uid [:uid, :gid, :gecos, :dir].each do |meth| if not user_info.respond_to?(meth) user_info = Etc.getpwnam(@uuid) break end end @uid = user_info.uid @gid = user_info.gid @gecos = user_info.gecos @container_dir = "#{user_info.dir}/" @container_plugin = Containerization::Plugin.new(self) rescue ArgumentError => e @uid = user_uid @gid = user_uid @gecos = @config.get("GEAR_GECOS") || "OO application container" @container_dir = Containerization::Plugin.container_dir(self) @container_plugin = nil end @state = ::OpenShift::Runtime::Utils::ApplicationState.new(self) @cartridge_model = V2CartridgeModel.new(@config, self, @state, @hourglass) end
# File lib/openshift-origin-node/model/application_container.rb, line 158 def self.passwd_for(container_uuid) config = ::OpenShift::Config.new gecos = config.get("GEAR_GECOS") || "OO application container" pwent = Etc.getpwnam(container_uuid) raise ArgumentError, "Not an OpenShift gear: #{container_uuid}" if pwent.gecos != gecos pwent end
# File lib/openshift-origin-node/model/application_container.rb, line 791 def address_bound?(ip, port, hourglass) @container_plugin.address_bound?(ip, port, hourglass) end
# File lib/openshift-origin-node/model/application_container.rb, line 795 def addresses_bound?(addresses, hourglass) @container_plugin.addresses_bound?(addresses, hourglass) end
create gear
model/unix_user.rb
context: root @param secret_token value of OPENSHIFT_SECRET_TOKEN for application
# File lib/openshift-origin-node/model/application_container.rb, line 183 def create(secret_token = nil) output = '' notify_observers(:before_container_create) # lock to prevent race condition between create and delete of gear uuid_lock_file = "/var/lock/oo-create.#{@uuid}" File.open(uuid_lock_file, File::RDWR|File::CREAT|File::TRUNC, 0600) do | uuid_lock | uuid_lock.fcntl(Fcntl::F_SETFD, Fcntl::FD_CLOEXEC) uuid_lock.flock(File::LOCK_EX) resource = OpenShift::Runtime::Node.resource_limits no_overcommit_active = resource.get_bool('no_overcommit_active', false) overcommit_lock_file = "/var/lock/oo-create.overcommit" File.open(overcommit_lock_file, File::RDWR|File::CREAT|File::TRUNC, 0600) do | overcommit_lock | overcommit_lock.fcntl(Fcntl::F_SETFD, Fcntl::FD_CLOEXEC) if no_overcommit_active overcommit_lock.flock(File::LOCK_EX) nu = OpenShift::Runtime::Node.node_utilization if (nu['gears_active_usage_pct'] >= 100) raise GearCreationException.new("ERROR: Node capacity exceeded, unable to create container #{@uuid}") end end @container_plugin = Containerization::Plugin.new(self) @container_plugin.create overcommit_lock.flock(File::LOCK_UN) if no_overcommit_active add_env_var('SECRET_TOKEN', secret_token, true) if secret_token output = generate_ssh_key end if @config.get("CREATE_APP_SYMLINKS").to_i == 1 unobfuscated = PathUtils.join(File.dirname(@container_dir),"#{@container_name}-#{@namespace}") if not File.exists? unobfuscated FileUtils.ln_s File.basename(@container_dir), unobfuscated, :force=>true end end uuid_lock.flock(File::LOCK_UN) end notify_observers(:after_container_create) output end
Destroy gear
model/unix_user.rb
context: root @param skip_hooks should destroy call the gear's hooks before destroying the gear
# File lib/openshift-origin-node/model/application_container.rb, line 235 def destroy(skip_hooks=false) notify_observers(:before_container_destroy) if @uid.nil? or (@container_plugin.nil? or !File.directory?(@container_dir.to_s)) # gear seems to have been deleted already... suppress any error # TODO : remove remaining stuff if it exists, e.g. .httpd/#{uuid}* etc return ['', '', 0] end notify_endpoint_delete = '' output = '' errout = '' retcode = -1 # Don't try to delete a gear that is being scaled-up|created|deleted uuid_lock_file = "/var/lock/oo-create.#{@uuid}" File.open(uuid_lock_file, File::RDWR|File::CREAT|File::TRUNC, 0600) do | lock | lock.fcntl(Fcntl::F_SETFD, Fcntl::FD_CLOEXEC) lock.flock(File::LOCK_EX) @cartridge_model.each_cartridge do |cart| env = ::OpenShift::Runtime::Utils::Environ::for_gear(@container_dir) cart.public_endpoints.each do |endpoint| notify_endpoint_delete << "NOTIFY_ENDPOINT_DELETE: #{@config.get('PUBLIC_IP')} #{env[endpoint.public_port_name]}\n" end end # possible mismatch across cart model versions output, errout, retcode = @cartridge_model.destroy(skip_hooks) raise UserDeletionException.new("ERROR: unable to delete user account #{@uuid}") if @uuid.nil? @container_plugin.destroy if @config.get("CREATE_APP_SYMLINKS").to_i == 1 Dir.foreach(File.dirname(@container_dir)) do |dent| unobfuscate = PathUtils.join(File.dirname(@container_dir), dent) if (File.symlink?(unobfuscate)) && (File.readlink(unobfuscate) == File.basename(@container_dir)) File.unlink(unobfuscate) end end end lock.flock(File::LOCK_UN) end output += notify_endpoint_delete notify_observers(:after_container_destroy) return output, errout, retcode end
Returns true if the user's disk block usage meets or exceeds max_percent of the configured block limit, otherwise false.
# File lib/openshift-origin-node/model/application_container.rb, line 722 def disk_usage_exceeds?(max_percent) raise "Percent must be between 1-100 (inclusive)" unless (1..100).member?(max_percent) quota = OpenShift::Runtime::Node.get_quota(@uuid) used_percent = ((quota[:blocks_used].to_f / quota[:blocks_limit].to_f) * 100.00).to_i return used_percent >= max_percent end
Public: Sets the app state to "stopped" and causes an immediate forced termination of all gear processes.
TODO: exception handling
# File lib/openshift-origin-node/model/application_container.rb, line 292 def force_stop(options={}) @state.value = State::STOPPED @cartridge_model.create_stop_lock @container_plugin.stop(options) end
# File lib/openshift-origin-node/model/application_container.rb, line 488 def gear_level_tidy_git(gear_repo_dir) # Git pruning tidy_action do run_in_container_context('git prune', chdir: gear_repo_dir, expected_exitstatus: 0, timeout: @hourglass.remaining) logger.debug("Pruned git directory at #{gear_repo_dir}") end # Git GC tidy_action do run_in_container_context('git gc --aggressive', chdir: gear_repo_dir, expected_exitstatus: 0, timeout: @hourglass.remaining) logger.debug("Executed git gc for repo #{gear_repo_dir}") end end
# File lib/openshift-origin-node/model/application_container.rb, line 480 def gear_level_tidy_tmp(gear_tmp_dir) # Temp dir cleanup tidy_action do FileUtils.rm_rf(Dir.glob(PathUtils.join(gear_tmp_dir, "*"))) logger.debug("Cleaned gear temp dir at #{gear_tmp_dir}") end end
# File lib/openshift-origin-node/model/application_container.rb, line 799 def gear_registry if @gear_registry.nil? and @cartridge_model.web_proxy @gear_registry = ::OpenShift::Runtime::GearRegistry.new(self) end @gear_registry end
# File lib/openshift-origin-node/model/application_container.rb, line 603 def get_cartridge(cart_name) @cartridge_model.get_cartridge(cart_name) end
Get the gear groups for the application this gear is part of.
Returns the parsed JSON for the response.
# File lib/openshift-origin-node/model/application_container.rb, line 520 def get_gear_groups(gear_env) broker_addr = @config.get('BROKER_HOST') domain = gear_env['OPENSHIFT_NAMESPACE'] app_name = gear_env['OPENSHIFT_APP_NAME'] url = "https://#{broker_addr}/broker/rest/domains/#{domain}/applications/#{app_name}/gear_groups.json" params = broker_auth_params request = RestClient::Request.new(:method => :get, :url => url, :timeout => 30, :headers => { :accept => 'application/json;version=1.0', :user_agent => 'OpenShift' }, :payload => params) response = request.execute if 300 <= response.code raise response end gear_groups = JSON.parse(response) gear_groups end
# File lib/openshift-origin-node/model/application_container.rb, line 174 def get_ip_addr(host_id) @container_plugin.get_ip_addr(host_id) end
Given a list of gear groups, return the secondary gear groups
# File lib/openshift-origin-node/model/application_container.rb, line 547 def get_secondary_gear_groups(groups) secondary_groups = {} groups['data'].each do |group| group['cartridges'].each do |cartridge| cartridge['tags'].each do |tag| if tag == 'database' secondary_groups[cartridge['name']] = group end end end end secondary_groups end
Idles the gear if there is no stop lock and state is not already STOPPED.
# File lib/openshift-origin-node/model/application_container.rb, line 407 def idle_gear(options={}) if not stop_lock? and (state.value != State::STOPPED) frontend = FrontendHttpServer.new(self) frontend.idle begin output = stop_gear(force: true, term_delay: 30, init_owned: true) ensure state.value = State::IDLE end output end end
Kill processes belonging to this app container.
Options:
init_owned: Only kill processes trees that start with a daemon (rooted in init). term_delay: Send SIGTERM first, wait term_delay seconds then send SIGKILL.
Note: The init_owned and term_delay parameters are combined to safely kill daemons running in the gear without touching processes being run from outside the gear (ex: git push, cron, node API).
# File lib/openshift-origin-node/model/application_container.rb, line 310 def kill_procs(options={}) # Give it a good try to delete all processes. # This abuse is necessary to release locks on polyinstantiated # directories by pam_namespace. procfilter="-u #{uid}" if options[:init_owned] procfilter << " -P 1" end # If the terminate delay is specified, try to terminate processes nicely # first and wait for them to die. if options[:term_delay] ::OpenShift::Runtime::Utils::oo_spawn("/usr/bin/pkill #{procfilter}") etime = Time.now + options[:term_delay].to_i while (Time.now <= etime) out,err,rc = ::OpenShift::Runtime::Utils::oo_spawn("/usr/bin/pgrep #{procfilter}") break unless rc == 0 sleep 0.5 end end oldproclist="" stuckcount=0 while stuckcount <= 10 out,err,rc = ::OpenShift::Runtime::Utils::oo_spawn("/usr/bin/pkill -9 #{procfilter}") break unless rc == 0 sleep 0.5 out,err,rc = ::OpenShift::Runtime::Utils::oo_spawn("/usr/bin/pgrep #{procfilter}") if oldproclist == out stuckcount += 1 else oldproclist = out stuckcount = 0 end end out,err,rc = ::OpenShift::Runtime::Utils::oo_spawn("/usr/bin/pgrep #{procfilter}") if rc == 0 procset = out.split.join(' ') logger.error "ERROR: failed to kill all processes for #{uid}: PIDs #{procset}" end end
# File lib/openshift-origin-node/model/application_container.rb, line 648 def list_proxy_mappings @cartridge_model.list_proxy_mappings end
# File lib/openshift-origin-node/model/application_container.rb, line 787 def memory_in_bytes @container_plugin.memory_in_bytes(@uuid) end
# File lib/openshift-origin-node/model/application_container.rb, line 170 def name @container_name end
Send a fire-and-forget request to the broker to report build analytics.
# File lib/openshift-origin-node/model/application_container.rb, line 614 def report_build_analytics broker_addr = @config.get('BROKER_HOST') url = "https://#{broker_addr}/broker/analytics" payload = { "json_data" => { "app_uuid" => @application_uuid, "action" => "push" }.to_json } request = RestClient::Request.new(:method => :post, :url => url, :timeout => 30, :open_timeout => 30, :headers => { :user_agent => 'OpenShift' }, :payload => payload) pid = fork do Process.daemon begin Timeout::timeout(60) do response = request.execute() end rescue # ignore it end exit! end Process.detach(pid) end
Send the deployments to the broker
# File lib/openshift-origin-node/model/application_container.rb, line 566 def report_deployments(gear_env) broker_addr = @config.get('BROKER_HOST') domain = gear_env['OPENSHIFT_NAMESPACE'] app_name = gear_env['OPENSHIFT_APP_NAME'] app_uuid = gear_env['OPENSHIFT_APP_UUID'] url = "https://#{broker_addr}/broker/rest/domain/#{domain}/application/#{app_name}/deployments" params = broker_auth_params if params deployments = calculate_deployments params['deployments[]'] = deployments params[:application_id] = app_uuid request = RestClient::Request.new(:method => :post, :url => url, :timeout => 30, :headers => { :accept => 'application/json;version=1.6', :user_agent => 'OpenShift' }, :payload => params) response = request.execute if 300 <= response.code raise response end end end
# File lib/openshift-origin-node/model/application_container.rb, line 763 def reset_permission(paths) @container_plugin.reset_permission(paths) end
# File lib/openshift-origin-node/model/application_container.rb, line 767 def reset_permission_R(paths) @container_plugin.reset_permission_R(paths) end
run_in_container_context(command, [, options]) -> [stdout, stderr, exit status]
Executes specified command and return its stdout, stderr and exit status. Or, raise exceptions if certain conditions are not met. The command is as container user in a SELinux context using runuser/runcon. The environment variables are cleared and mys be specified by :env.
command: command line string which is passed to the standard shell
options: hash
:env: hash name => val : set the environment variable name => nil : unset the environment variable :chdir => path : set current directory when running command :expected_exitstatus : An Integer value for the expected return code of command : If not set spawn() returns exitstatus from command otherwise : raise an error if exitstatus is not expected_exitstatus :timeout : Maximum number of seconds to wait for command to finish. default: 3600 : stdin for the command is /dev/null :out : If specified, STDOUT from the child process will be redirected to the provided +IO+ object. :err : If specified, STDERR from the child process will be redirected to the provided +IO+ object.
NOTE: If the out or err options are specified, the corresponding return value from run_in_container_context will be the incoming/provided IO objects instead of the buffered String output. It's the responsibility of the caller to correctly handle the resulting data type.
# File lib/openshift-origin-node/model/application_container.rb, line 759 def run_in_container_context(command, options = {}) @container_plugin.run_in_container_context(command, options) end
# File lib/openshift-origin-node/model/application_container.rb, line 775 def set_ro_permission(paths) @container_plugin.set_ro_permission(paths) end
# File lib/openshift-origin-node/model/application_container.rb, line 771 def set_ro_permission_R(paths) @container_plugin.set_ro_permission_R(paths) end
# File lib/openshift-origin-node/model/application_container.rb, line 783 def set_rw_permission(paths) @container_plugin.set_rw_permission(paths) end
# File lib/openshift-origin-node/model/application_container.rb, line 779 def set_rw_permission_R(paths) @container_plugin.set_rw_permission_R(paths) end
Sets the application state to STARTED and starts the gear. Gear state implementation is model specific, but options is provided to the implementation.
# File lib/openshift-origin-node/model/application_container.rb, line 442 def start_gear(options={}) @cartridge_model.start_gear(options) end
Sets the application state to STOPPED and stops the gear. Gear stop implementation is model specific, but options is provided to the implementation.
Options:
force Forcibly kill gear processes after cartridges have been stopped. hot_deploy if true, don't rotate-out from the web proxy init if true, don't rotate-out from the web proxy
# File lib/openshift-origin-node/model/application_container.rb, line 455 def stop_gear(options={}) buffer = '' if proxy_cartridge = @cartridge_model.web_proxy unless options[:hot_deploy] == true or options[:init] result = update_proxy_status(cartridge: proxy_cartridge, action: :disable, gear_uuid: self.uuid, persist: false) result[:proxy_results].each do |proxy_gear_uuid, result| buffer << result[:messages].join("\n") end end end buffer << @cartridge_model.stop_gear(options) unless buffer.empty? buffer.chomp! buffer << "\n" end if options[:force] kill_procs(options) end buffer << stopped_status_attr buffer end
# File lib/openshift-origin-node/model/application_container.rb, line 607 def stop_lock? @cartridge_model.stop_lock? end
# File lib/openshift-origin-node/model/application_container.rb, line 593 def stopped_status_attr if state.value == State::STOPPED || stop_lock? "ATTR: status=ALREADY_STOPPED\n" elsif state.value == State::IDLE "ATTR: status=ALREADY_IDLED\n" else '' end end
Public: Cleans up the gear, providing any installed cartridges with the opportunity to perform their own cleanup operations via the tidy hook.
The generic gear-level cleanup flow is:
Stop the gear
Gear temp dir cleanup
Cartridge tidy hook executions
Git cleanup
Start the gear
Raises an Exception if an internal error occurs, and ignores failed cartridge tidy hook executions.
# File lib/openshift-origin-node/model/application_container.rb, line 369 def tidy logger.debug("Starting tidy on gear #{@uuid}") env = ::OpenShift::Runtime::Utils::Environ::for_gear(@container_dir) gear_dir = env['OPENSHIFT_HOMEDIR'] app_name = env['OPENSHIFT_APP_NAME'] raise 'Missing required env var OPENSHIFT_HOMEDIR' unless gear_dir raise 'Missing required env var OPENSHIFT_APP_NAME' unless app_name gear_repo_dir = PathUtils.join(gear_dir, 'git', "#{app_name}.git") gear_tmp_dir = PathUtils.join(gear_dir, '.tmp') stop_gear(user_initiated: false) # Perform the gear- and cart- level tidy actions. At this point, the gear has # been stopped; we'll attempt to start the gear no matter what tidy operations fail. begin # clear out the tmp dir gear_level_tidy_tmp(gear_tmp_dir) # Delegate to cartridge model to perform cart-level tidy operations for all installed carts. @cartridge_model.tidy # git gc - do this last to maximize room for git to write changes gear_level_tidy_git(gear_repo_dir) rescue Exception => e logger.warn("An unknown exception occured during tidy for gear #{@uuid}: #{e.message}\n#{e.backtrace}") ensure start_gear(user_initiated: false) end logger.debug("Completed tidy for gear #{@uuid}") end
Executes a block, trapping ShellExecutionExceptions and treating them as warnings. Any other exceptions are unexpected and will bubble out.
# File lib/openshift-origin-node/model/application_container.rb, line 504 def tidy_action begin yield rescue ::OpenShift::Runtime::Utils::ShellExecutionException => e logger.warn(%{ Tidy operation failed on gear #{@uuid}: #{e.message} --- stdout ---\n#{e.stdout} --- stderr ---\n#{e.stderr} }) end end
Unidles the gear.
# File lib/openshift-origin-node/model/application_container.rb, line 423 def unidle_gear(options={}) output = "" OpenShift::Runtime::Utils::Cgroups.new(@uuid).boost do if stop_lock? and (state.value == State::IDLE) state.value = State::STARTED output = start_gear end frontend = FrontendHttpServer.new(self) if frontend.idle? frontend.unidle end end output end
# File lib/openshift-origin-node/model/application_container.rb, line 809 def broker_auth_params auth_token = PathUtils.join(@config.get('GEAR_BASE_DIR'), uuid, '.auth', 'token') auth_iv = PathUtils.join(@config.get('GEAR_BASE_DIR'), uuid, '.auth', 'iv') if File.exist?(auth_token) && File.exist?(auth_iv) params = { 'broker_auth_key' => File.read(auth_token).chomp, 'broker_auth_iv' => File.read(auth_iv).chomp } else params = nil end params end
Generated with the Darkfish Rdoc Generator 2.