class Asteroid::Instance

Attributes

id[R]
ip_address[R]
logger[RW]
name[R]
namespace[R]
provider[R]
server[R]
type[R]

Public Class Methods

all() click to toggle source
# File lib/asteroid/instance.rb, line 76
def self.all
  instances = Provider.all.instances
  instances = instances.map do |info|
    new info
  end

  if Config.namespace
    instances = instances.delete_if do |i|
      i.namespace != Config.namespace
    end
  end

  instances
end
all_with_type(type) click to toggle source
# File lib/asteroid/instance.rb, line 95
def self.all_with_type(type)
  all.select{|i| i.type == type}
end
find(id) click to toggle source
# File lib/asteroid/instance.rb, line 119
def self.find(id)
  all.select{|i| i.id == id || i.id.to_s == id.to_s}.first
end
first(type) click to toggle source
# File lib/asteroid/instance.rb, line 91
def self.first(type)
  all_with_type(type).first
end
new(options = {}) click to toggle source
# File lib/asteroid/instance.rb, line 34
def initialize(options = {})
  options = options.dup

  o = options.dup

  @server = options.delete :server
  @type = options.delete :type
  @id = options.delete :id
  @name = options.delete :name
  @ip_address = options.delete :ip_address

  if @name =~ /:/
    @namespace, @name = @name.split(':', 2)
  end

  if @name && @type.nil?
    @type = name.split('-').first.to_sym
  end

  if @server.nil? && @type.nil?
    raise "Instance must have a server type :type attribute"
  elsif @server.nil?
    @server = Server.named @type
  end

  if @provider

  end

  @attributes = options

 
  server.provider.required_instance_attributes.each do |att|
    require_attribute att
  end

  # The type is added on the name
  @attributes[:type] ||= if @attributes[:name]
    @attributes[:name].match(/[a-z]+/)[0].to_sym
  end
end

Public Instance Methods

[](k) click to toggle source
# File lib/asteroid/instance/vars.rb, line 7
def [](k)
  val = config_get k
  val.nil? ? server_defaults[k] : val
end
[]=(k, v) click to toggle source
# File lib/asteroid/instance/vars.rb, line 12
def []=(k, v)
  config_set k, v
end
aster_environment() click to toggle source
# File lib/asteroid/instance/command.rb, line 82
def aster_environment
  @aster_environment ||= Aster::Environment.new.tap do |e|

    self.server.commands.each_pair do |name, data|
      data = case data
      when Array
        {args: [], steps: data}
      when Object
        data
      else
        raise "Commands should be Arrays or arguments or Objects"
      end
      
      commands = Aster::Parser.new.send :parse_lines, data[:steps]
      e.define_function name, data[:args], commands
    end


    e.define_function :run, [:"..."] do |arguments|
      command_run(arguments.join(' '))
    end

    e.define_function :upload, [:"..."] do |(from, to, _)|
      if command_upload from, to
        "yes"
      else
        "no"
      end
    end

    e.define_function :exec, [:"..."] do |arguments|
      command_exec(arguments.join(' '))
    end
  end    
end
command_command(cmd, args, env) click to toggle source
# File lib/asteroid/instance/command.rb, line 46
def command_command(cmd, args, env)
  raise "Not here"
  env ||= template_data
  command = server.commands[cmd]

  env = if command["args"]
    command["args"].inject({}) do |r, k|
      r[k] = args.shift
      r
    end
  end

  env = template_data.merge({
    args: env
  })

  command["steps"].map do |step|
    eval_command step, env
  end.last
end
command_config(set_or_get, var, val) click to toggle source
# File lib/asteroid/instance/command.rb, line 172
def command_config(set_or_get, var, val)
  if set_or_get.to_s == "set"
    self.config_set(var, val)
  elsif set_or_get == "get"
    self.config_get(var)
  end      
end
command_exec(command, env = nil) click to toggle source
# File lib/asteroid/instance/command.rb, line 168
def command_exec(command, env = nil)
  self.ssh_exec command
end
command_run(script_name, env = nil) click to toggle source
# File lib/asteroid/instance/command.rb, line 124
def command_run(script_name, env = nil)
  env ||= template_data
  script = Script.named(script_name)

  # touch ssh
  self.ssh

  if script.nil?
    raise "No script named #{script_name}"
  end
  
  # Return early if we're running a script
  if script.yml?
    # TODO: send the fucking script obj
    command_run_yml_script(script_name)
    return 
  elsif script.aster?
    command_run_aster_script(script_name)
    return
  end


  if script.template?
    script.set_data env
  end

  rendered_script = script.render
  puts rendered_script
  puts self.ssh_exec rendered_script
end
command_run_aster_script(aster_script, env = nil) click to toggle source
# File lib/asteroid/instance/command.rb, line 118
def command_run_aster_script(aster_script, env = nil)
  filename = File.join(Asteroid::Config.script_dir, '/', aster_script)
  script = File.read(filename)
  aster_environment.eval script
end
command_run_yml_script(yml_script, env = nil) click to toggle source
# File lib/asteroid/instance/command.rb, line 67
def command_run_yml_script(yml_script, env = nil)
  env ||= template_data
  filename = File.join(Asteroid::Config.script_dir, '/', yml_script)
  script = File.read(filename)
  yml = Template.new(:erb, script).render(env)
  data = YAML::load yml
  unless data["steps"]
    raise "No steps in #{filenbame}"
  else
    data["steps"].each do |step|
      eval_command step, env
    end
  end
end
command_upload(from, to, env = nil) click to toggle source
# File lib/asteroid/instance/command.rb, line 155
def command_upload(from, to, env = nil)
  env ||= template_data
  from = FileReference.new(from)

  if from.template?
    from.data = env
  end

  puts from.rendered_filename

  self.scp_upload from.rendered_filename, to
end
config_filename() click to toggle source
# File lib/asteroid/instance/vars.rb, line 28
def config_filename
  unless File.directory?(Asteroid::Config.secret_instance_dir)
    FileUtils.mkdir Asteroid::Config.secret_instance_dir
  end
  File.join(Asteroid::Config.secret_instance_dir, "/instance_#{@id}.yml")
end
config_get(k) click to toggle source
# File lib/asteroid/instance/vars.rb, line 16
def config_get(k)
  data = load_config_data
  data && data[k]
end
config_set(k, v) click to toggle source
# File lib/asteroid/instance/vars.rb, line 21
def config_set(k, v)
  data = load_config_data
  data[k] = v
  save_config_data data
  v
end
destroy() click to toggle source
# File lib/asteroid/instance.rb, line 133
def destroy
  server.provider.destroy_instance self
end
env() click to toggle source
# File lib/asteroid/instance.rb, line 123
def env
  {
    "INSTANCE_PRIVATE_KEY" => self.server.ssh_key_filename,
    "INSTANCE_SSH_PORT" => self["ssh.port"].to_i,
    "INSTANCE_SSH_LOGIN" => self["login.username"],
    "INSTANCE_IP_ADDRESS" => self.ip_address,
    "INSTANCE_ID" => self.id,
  }
end
eval_command(cmd) click to toggle source
# File lib/asteroid/instance/command.rb, line 9
def eval_command(cmd)


  return aster_environment.eval(cmd)

  # env ||= template_data
  # cmd = Template.new(:erb, cmd).render(env)
  # cmd, *rest = cmd.split(" ")
  # case cmd.to_sym
  # when :run
  #   command_run(rest.first, env)
  # when :upload
  #   from, to, _ = rest
  #   command_upload(from, to, env)
  # when :"upload-private-key"
  #   name, _ = rest
  #   command_upload_private_key(name, env)
  # when :exec
  #   script = rest.join(" ")
  #   command_exec(script, env)
  # when :config
  #   set_or_get, var, val, _ = rest
  #   command_config(set_or_get, var, val)
  # else
  #   if server.commands[cmd]
  #     command_command(cmd, rest, env)
  #   else
  #   end
  # end
end
load_config_data() click to toggle source
# File lib/asteroid/instance/vars.rb, line 35
def load_config_data
  begin
    YAML::load_file config_filename
  rescue
    {}
  end
end
mock_file_system() click to toggle source
# File lib/asteroid/instance/scp.rb, line 23
def mock_file_system
  @mock_file_system ||= {}
end
password(username = nil) click to toggle source
# File lib/asteroid/instance.rb, line 99
def password(username = nil)
  username ||= self["login.username"]
  password_filename = File.join(Asteroid::Config.password_dir, "/instance_#{@id}-user_#{username}")
  if File.exists? password_filename
    File.read(password_filename)
  else
    password = SecureRandom.hex(64)
    File.open(password_filename, 'w'){|f| f.write password}
    password
  end
end
render_erb(string, env) click to toggle source
# File lib/asteroid/instance/command.rb, line 5
def render_erb(string, env)
  Template.new(:erb, string).render(env)
end
save_config_data(data) click to toggle source
# File lib/asteroid/instance/vars.rb, line 43
def save_config_data(data)
  File.open(config_filename, 'w') {|f| f.write data.to_yaml }
end
scp() click to toggle source
# File lib/asteroid/instance/scp.rb, line 27
def scp
  if server.provider.type == :mock
    return @mock_scp ||= MockSCP.new(mock_file_system)
  end

  if @scp && (@scp.session.transport.port.to_s != self["ssh.port"] || (@scp.session.transport.options[:user] != self["login.username"]))
    @scp = nil
  end
  @scp ||= Net::SCP.start(
    ip_address, 
    self["login.username"],
    port: self["ssh.port"].to_i,
    :keys => [server.ssh_key_filename]
  )
end
scp_upload(from, to) click to toggle source
# File lib/asteroid/instance/scp.rb, line 19
def scp_upload(from, to)
  scp.upload! from, to
end
server_defaults() click to toggle source
# File lib/asteroid/instance.rb, line 111
def server_defaults
  server.instance_config
end
ssh() click to toggle source
# File lib/asteroid/instance/ssh.rb, line 16
def ssh
  if @ssh && (@ssh.transport.port.to_s != self["ssh.port"] || (@ssh.transport.options[:user] != self["login.username"]))
    @ssh = nil
  end

  @ssh ||= Net::SSH.start(
    ip_address, 
    self["login.username"], 
    port: self["ssh.port"].to_i,
    :keys => [server.ssh_key.private.filename]
  )
end
ssh_exec(command) click to toggle source
# File lib/asteroid/instance/ssh.rb, line 6
def ssh_exec(command)
  ssh.exec!(command) do |channel, stream, data|
    if stream == :stdout
      puts data
    else
      puts data
    end
  end
end
template_data() click to toggle source
# File lib/asteroid/instance/command.rb, line 40
def template_data
  {
    instance: self, server: server
  }
end

Private Instance Methods

require_attribute(name) click to toggle source
# File lib/asteroid/instance.rb, line 139
def require_attribute(name)
  if @attributes[name].nil?
    raise MissingAttributeError.new(name)
  end
end