require "cfoundry/upload_helpers" require "cfoundry/chatty_hash" require "cfoundry/v1/model" module CFoundry::V1 class App < Model include CFoundry::UploadHelpers attribute :name, :string, :guid => true attribute :instances, :integer attribute :state, :string attribute :created, :integer, :at => [:meta, :created], :read_only => true attribute :version, :integer, :at => [:meta, :version], :read_only => true attribute :framework, :string, :at => [:staging, :model] attribute :runtime, :string, :at => [:staging, :stack] attribute :command, :string, :at => [:staging, :command] attribute :memory, :integer, :at => [:resources, :memory] attribute :disk, :integer, :at => [:resources, :disk] attribute :fds, :integer, :at => [:resources, :fds] attribute :env, [:string], :default => [] attribute :uris, [:string], :default => [] attribute :services, [:string], :default => [] attribute :console, :boolean, :default => false, :read => [:meta, :console], :write => :console attribute :debug, :string, :default => nil, :read => [:meta, :debug], :write => :debug attribute :running_instances, :integer, :read => :runningInstances, :read_only => true define_client_methods alias_method :total_instances, :instances alias_method :total_instances=, :instances= alias_method :debug_mode, :debug alias_method :debug_mode=, :debug= alias_method :framework_name, :framework alias_method :framework_name=, :framework= alias_method :runtime_name, :runtime alias_method :runtime_name=, :runtime= alias_method :service_names, :services alias_method :service_names=, :services= alias_method :status, :state alias_method :status=, :state= alias_method :urls, :uris alias_method :urls=, :uris= alias_method :env_array, :env alias_method :env_array=, :env= def framework @client.framework(framework_name) end def framework=(obj) set_named(:framework, obj) end def runtime @client.runtime(runtime_name) end def runtime=(obj) set_named(:runtime, obj) end def services service_names.collect { |name| @client.service_instance(name) } end def services=(objs) set_many_named(:service, objs) end # Retrieve all of the instances of the app, as Instance objects. def instances @client.base.instances(@guid).collect do |m| Instance.new(self, m[:index].to_s, @client, m) end end # Retrieve crashed instances def crashes @client.base.crashes(@guid).collect do |i| Instance.new(self, i[:instance].to_s, @client, i) end end # Retrieve application statistics, e.g. CPU load and memory usage. def stats stats = {} @client.base.stats(@guid).each do |idx, info| stats[idx.to_s] = info end stats end # Stop the application. def stop! self.state = "STOPPED" update! end # Start the application. def start!(async = false) self.state = "STARTED" update! end # Restart the application. def restart!(async = false) stop! start! end def update!(async = false) super() end # Determine application health. # # If all instances are running, returns "RUNNING". If only some are # started, returns the precentage of them that are healthy. # # Otherwise, returns application's status. def health s = state if s == "STARTED" healthy_count = running_instances expected = total_instances if healthy_count && expected > 0 ratio = healthy_count / expected.to_f if ratio == 1.0 "RUNNING" else "#{(ratio * 100).to_i}%" end else "N/A" end else s end end # Check that all application instances are running. def healthy? # invalidate cache so the check is fresh invalidate! health == "RUNNING" end alias_method :running?, :healthy? # Is the application stopped? def stopped? state == "STOPPED" end # Is the application started? # # Note that this does not imply that all instances are running. See # #healthy? def started? state == "STARTED" end # Shortcut for uris[0] def uri uris[0] end # Shortcut for uris = [x] def uri=(x) self.uris = [x] end alias_method :url, :uri alias_method :url=, :uri= def env e = env_array || [] env = {} e.each do |pair| name, val = pair.split("=", 2) env[name] = val end CFoundry::ChattyHash.new(method(:env=), env) end def env=(hash) unless hash.is_a?(Array) hash = hash.collect { |k, v| "#{k}=#{v}" } end self.env_array = hash end def services service_names.collect do |name| @client.service_instance(name) end end def services=(instances) self.service_names = instances.collect(&:name) end # Bind services to application. def bind(*instances) self.services += instances update! end # Unbind services from application. def unbind(*instances) self.services -= instances update! end def binds?(instance) services.include? instance end # Retrieve file listing under path for the first instance of the application. # # [path] # A sequence of strings representing path segments. # # For example, files("foo", "bar") for +foo/bar+. def files(*path) Instance.new(self, "0", @client).files(*path) end # Retrieve file contents for the first instance of the application. # # [path] # A sequence of strings representing path segments. # # For example, files("foo", "bar") for +foo/bar+. def file(*path) Instance.new(self, "0", @client).file(*path) end private def set_named(attr, val) res = send(:"#{attr}_name=", val.name) if @changes.key?(attr) old, new = @changes[attr] @changes[attr] = [@client.send(attr, old), val] end res end def set_many_named(attr, vals) res = send(:"#{attr}_names=", val.collect(&:name)) if @changes.key?(attr) old, new = @changes[attr] @changes[attr] = [old.collect { |o| @client.send(attr, o) }, vals] end vals end # Class represnting a running instance of an application. class Instance # The application this instance belongs to. attr_reader :app # Application instance number. attr_reader :id # Create an Instance object. # # You'll usually call App#instances instead def initialize(app, id, client, manifest = {}) @app = app @id = id @client = client @manifest = manifest end # Show string representing the application instance. def inspect "#" end # Instance state. def state @manifest[:state] end alias_method :status, :state # Instance start time. def since Time.at(@manifest[:since]) end # Instance debugger data. If instance is in debug mode, returns a hash # containing :ip and :port keys. def debugger return unless @manifest[:debug_ip] and @manifest[:debug_port] { :ip => @manifest[:debug_ip], :port => @manifest[:debug_port] } end # Instance console data. If instance has a console, returns a hash # containing :ip and :port keys. def console return unless @manifest[:console_ip] and @manifest[:console_port] { :ip => @manifest[:console_ip], :port => @manifest[:console_port] } end # True if instance is starting or running, false if it's down or # flapping. def healthy? case state when "STARTING", "RUNNING" true when "DOWN", "FLAPPING" false end end def files(*path) @client.base.files(@app.name, @id, *path).split("\n").collect do |entry| path + [entry.split(/\s+/, 2)[0]] end end def file(*path) @client.base.files(@app.name, @id, *path) end end end end