Class: Asteroid::Instance

Inherits:
Object
  • Object
show all
Defined in:
lib/asteroid/instance.rb,
lib/asteroid/instance/scp.rb,
lib/asteroid/instance/ssh.rb,
lib/asteroid/instance/vars.rb,
lib/asteroid/instance/command.rb

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(options = {}) ⇒ Instance

Returns a new instance of Instance.



34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
# 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

Instance Attribute Details

#idObject (readonly)

Returns the value of attribute id.



25
26
27
# File 'lib/asteroid/instance.rb', line 25

def id
  @id
end

#ip_addressObject (readonly)

Returns the value of attribute ip_address.



25
26
27
# File 'lib/asteroid/instance.rb', line 25

def ip_address
  @ip_address
end

#loggerObject

Returns the value of attribute logger.



23
24
25
# File 'lib/asteroid/instance.rb', line 23

def logger
  @logger
end

#nameObject (readonly)

Returns the value of attribute name.



25
26
27
# File 'lib/asteroid/instance.rb', line 25

def name
  @name
end

#namespaceObject (readonly)

Returns the value of attribute namespace.



25
26
27
# File 'lib/asteroid/instance.rb', line 25

def namespace
  @namespace
end

#providerObject (readonly)

Returns the value of attribute provider.



25
26
27
# File 'lib/asteroid/instance.rb', line 25

def provider
  @provider
end

#serverObject (readonly)

Returns the value of attribute server.



25
26
27
# File 'lib/asteroid/instance.rb', line 25

def server
  @server
end

#typeObject (readonly)

Returns the value of attribute type.



25
26
27
# File 'lib/asteroid/instance.rb', line 25

def type
  @type
end

Class Method Details

.allObject



76
77
78
79
80
81
82
83
84
85
86
87
88
89
# 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) ⇒ Object



95
96
97
# File 'lib/asteroid/instance.rb', line 95

def self.all_with_type(type)
  all.select{|i| i.type == type}
end

.find(id) ⇒ Object



119
120
121
# 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) ⇒ Object



91
92
93
# File 'lib/asteroid/instance.rb', line 91

def self.first(type)
  all_with_type(type).first
end

Instance Method Details

#[](k) ⇒ Object



7
8
9
10
# File 'lib/asteroid/instance/vars.rb', line 7

def [](k)
  val = config_get k
  val.nil? ? server_defaults[k] : val
end

#[]=(k, v) ⇒ Object



12
13
14
# File 'lib/asteroid/instance/vars.rb', line 12

def []=(k, v)
  config_set k, v
end

#aster_environmentObject



82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
# 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) ⇒ Object



46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
# 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) ⇒ Object



172
173
174
175
176
177
178
# 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) ⇒ Object



168
169
170
# 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) ⇒ Object



124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
# 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) ⇒ Object



118
119
120
121
122
# 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) ⇒ Object



67
68
69
70
71
72
73
74
75
76
77
78
79
80
# 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) ⇒ Object



155
156
157
158
159
160
161
162
163
164
165
166
# 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_filenameObject



28
29
30
31
32
33
# 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) ⇒ Object



16
17
18
19
# File 'lib/asteroid/instance/vars.rb', line 16

def config_get(k)
  data = load_config_data
  data && data[k]
end

#config_set(k, v) ⇒ Object



21
22
23
24
25
26
# 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

#destroyObject



133
134
135
# File 'lib/asteroid/instance.rb', line 133

def destroy
  server.provider.destroy_instance self
end

#envObject



123
124
125
126
127
128
129
130
131
# 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) ⇒ Object



9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
# 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_dataObject



35
36
37
38
39
40
41
# File 'lib/asteroid/instance/vars.rb', line 35

def load_config_data
  begin
    YAML::load_file config_filename
  rescue
    {}
  end
end

#mock_file_systemObject



23
24
25
# File 'lib/asteroid/instance/scp.rb', line 23

def mock_file_system
  @mock_file_system ||= {}
end

#password(username = nil) ⇒ Object



99
100
101
102
103
104
105
106
107
108
109
# 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) ⇒ Object



5
6
7
# File 'lib/asteroid/instance/command.rb', line 5

def render_erb(string, env)
  Template.new(:erb, string).render(env)
end

#save_config_data(data) ⇒ Object



43
44
45
# 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

#scpObject



27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
# 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) ⇒ Object



19
20
21
# File 'lib/asteroid/instance/scp.rb', line 19

def scp_upload(from, to)
  scp.upload! from, to
end

#server_defaultsObject



111
112
113
# File 'lib/asteroid/instance.rb', line 111

def server_defaults
  server.instance_config
end

#sshObject



16
17
18
19
20
21
22
23
24
25
26
27
# 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) ⇒ Object



6
7
8
9
10
11
12
13
14
# 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_dataObject



40
41
42
43
44
# File 'lib/asteroid/instance/command.rb', line 40

def template_data
  {
    instance: self, server: server
  }
end