Class: Awsborn::Server

Inherits:
Object
  • Object
show all
Defined in:
lib/awsborn/server.rb

Constant Summary collapse

AVAILABILITY_ZONES =
%w[
  us-east-1a us-east-1b us-east-1c us-east-1d
  us-west-1a us-west-1b
  eu-west-1a eu-west-1b
  ap-southeast-1a ap-southeast-1b
]
INSTANCE_TYPES_32_BIT =
%w[m1.small c1.medium t1.micro]
INSTANCE_TYPES_64_BIT =
%w[m1.large m1.xlarge m2.xlarge m2.2xlarge m2.4xlarge c1.xlarge cc1.4xlarge t1.micro]
INSTANCE_TYPES =
(INSTANCE_TYPES_32_BIT + INSTANCE_TYPES_64_BIT).uniq
SYMBOL_CONSTANT_MAP =
(AVAILABILITY_ZONES + INSTANCE_TYPES).inject({}) { |memo,str| memo[str.tr('-.','_').to_sym] = str; memo }

Class Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(name, options = {}) ⇒ Server

Returns a new instance of Server.



4
5
6
7
8
# File 'lib/awsborn/server.rb', line 4

def initialize (name, options = {})
  @name = name
  @options = options.dup
  self.host_name = elastic_ip
end

Class Attribute Details

.childrenObject

Returns the value of attribute children.



11
12
13
# File 'lib/awsborn/server.rb', line 11

def children
  @children
end

.clustersObject

Returns the value of attribute clusters.



11
12
13
# File 'lib/awsborn/server.rb', line 11

def clusters
  @clusters
end

.loggerObject

Returns the value of attribute logger.



11
12
13
# File 'lib/awsborn/server.rb', line 11

def logger
  @logger
end

Class Method Details

.bootstrap_script(*args) ⇒ Object



43
44
45
46
# File 'lib/awsborn/server.rb', line 43

def bootstrap_script (*args)
  @bootstrap_script = args.first unless args.empty?
  @bootstrap_script
end

.cluster(&block) ⇒ Object



52
53
54
55
56
# File 'lib/awsborn/server.rb', line 52

def cluster (&block)
  @clusters ||= []
  @clusters << ServerCluster.build(self, &block)
  @clusters.last
end

.image_id(*args) ⇒ Object

Set image_id. Examples:

image_id 'ami-123123'
image_id 'ami-123123', :sudo_user => 'ubuntu'
image_id :i386 => 'ami-323232', :x64 => 'ami-646464', :sudo_user => 'ubuntu'


20
21
22
23
24
25
26
# File 'lib/awsborn/server.rb', line 20

def image_id (*args)
  unless args.empty?
    @image_id = args.first
    @sudo_user = args.last[:sudo_user] if args.last.is_a?(Hash)
  end
  @image_id
end

.inherited(klass) ⇒ Object



12
13
14
15
# File 'lib/awsborn/server.rb', line 12

def inherited (klass)
  @children ||= []
  @children << klass
end

.instance_type(*args) ⇒ Object



27
28
29
30
# File 'lib/awsborn/server.rb', line 27

def instance_type (*args)
  @instance_type = args.first unless args.empty?
  @instance_type
end

.keys(*args) ⇒ Object



35
36
37
38
# File 'lib/awsborn/server.rb', line 35

def keys (*args)
  @keys = args unless args.empty?
  @keys
end

.monitor(*args) ⇒ Object



47
48
49
50
# File 'lib/awsborn/server.rb', line 47

def monitor (*args)
  @monitor = args.first unless args.empty?
  @monitor
end

.security_group(*args) ⇒ Object



31
32
33
34
# File 'lib/awsborn/server.rb', line 31

def security_group (*args)
  @security_group = args.first unless args.empty?
  @security_group
end

.sudo_user(*args) ⇒ Object



39
40
41
42
# File 'lib/awsborn/server.rb', line 39

def sudo_user (*args)
  @sudo_user = args.first unless args.empty?
  @sudo_user
end

Instance Method Details

#associate_addressObject



150
151
152
153
154
# File 'lib/awsborn/server.rb', line 150

def associate_address
  logger.debug "Associating address #{elastic_ip} to #{name}"
  ec2.associate_address(elastic_ip)
  self.host_name = elastic_ip
end

#attach_volumesObject



164
165
166
167
168
169
170
171
# File 'lib/awsborn/server.rb', line 164

def attach_volumes
  logger.debug "Attaching volumes #{disk.values.join(', ')} to #{name}"
  disk.each_pair do |device, str_or_ary|
    volume = str_or_ary.is_a?(Array) ? str_or_ary.first : str_or_ary
    device = "/dev/#{device}" if device.is_a?(Symbol) || ! device.match('/')
    res = ec2.attach_volume(volume, device)
  end
end

#bootstrapObject



156
157
158
159
160
161
162
# File 'lib/awsborn/server.rb', line 156

def bootstrap
  logger.debug "Bootstrapping #{name}"
  script = bootstrap_script
  basename = File.basename(script)
  system "scp #{script} root@#{host_name}:/tmp"
  system "ssh root@#{host_name} 'cd /tmp && chmod 700 #{basename} && ./#{basename}'"
end

#constant(symbol) ⇒ Object



299
300
301
# File 'lib/awsborn/server.rb', line 299

def constant (symbol)
  SYMBOL_CONSTANT_MAP[symbol]
end

#cookObject



173
174
175
176
# File 'lib/awsborn/server.rb', line 173

def cook
  upload_cookbooks
  run_chef
end

#copy_sudo_users_keys_to_rootObject



146
147
148
# File 'lib/awsborn/server.rb', line 146

def copy_sudo_users_keys_to_root
  system("ssh #{sudo_user}@#{host_name} 'sudo cp .ssh/authorized_keys /root/.ssh/authorized_keys'")
end

#ec2Object



198
199
200
# File 'lib/awsborn/server.rb', line 198

def ec2
  @ec2 ||= Ec2.new(zone)
end

#install_ssh_keys(temp_key_pair = nil) ⇒ Object

Raises:

  • (ArgumentError)


126
127
128
129
130
131
# File 'lib/awsborn/server.rb', line 126

def install_ssh_keys (temp_key_pair = nil)
  logger.debug "Installing ssh keys on #{name}"
  raise ArgumentError, "No host_name for #{name}" unless host_name
  install_ssh_keys_for_sudo_user_or_root (temp_key_pair)
  copy_sudo_users_keys_to_root if sudo_user
end

#install_ssh_keys_for_sudo_user_or_root(temp_key_pair) ⇒ Object



133
134
135
136
137
138
# File 'lib/awsborn/server.rb', line 133

def install_ssh_keys_for_sudo_user_or_root (temp_key_pair)
  current_key = "-i #{temp_key_pair.path}" if temp_key_pair
  IO.popen("ssh #{current_key} #{sudo_user || 'root'}@#{host_name} 'cat > .ssh/authorized_keys'", "w") do |pipe|
    pipe.puts key_data
  end
end

#key_dataObject



140
141
142
143
144
# File 'lib/awsborn/server.rb', line 140

def key_data
  Dir[*keys].inject([]) do |memo, file_name|
    memo + File.readlines(file_name).map { |line| line.chomp }
  end.join("\n")
end

#launch_instance(key_pair) ⇒ Object



108
109
110
111
112
113
114
115
116
117
118
119
120
# File 'lib/awsborn/server.rb', line 108

def launch_instance (key_pair)
  @launch_response = ec2.launch_instance(image_id,
    :instance_type => constant(instance_type),
    :availability_zone => constant(zone),
    :key_name => key_pair.name,
    :group_ids => security_group,
    :monitoring_enabled => monitor
  )
  logger.debug @launch_response

  Awsborn.wait_for("instance #{instance_id} (#{name}) to start", 10) { instance_running? }
  self.host_name = aws_dns_name
end

#loggerObject



303
304
305
# File 'lib/awsborn/server.rb', line 303

def logger
  @logger ||= self.class.logger
end

#refreshObject



72
73
74
75
76
77
78
79
80
81
82
83
# File 'lib/awsborn/server.rb', line 72

def refresh
  start_or_stop_monitoring unless monitor.nil?
  associate_address if elastic_ip
  
  begin
    update_known_hosts
    install_ssh_keys if keys
  rescue SecurityError => e
    logger.warn "Could not update known_hosts for #{name}:"
    logger.warn e
  end
end

#run_chefObject



192
193
194
195
196
# File 'lib/awsborn/server.rb', line 192

def run_chef
  logger.info "Running chef on #{host_name}"
  # Absolute path to config files to avoid a nasty irrational bug.
  sh "ssh root@#{host_name} \"cd #{Awsborn.remote_chef_path}; chef-solo -l #{Awsborn.chef_log_level} -c #{Awsborn.remote_chef_path}/config/solo.rb -j #{Awsborn.remote_chef_path}/config/dna.json\""
end

#running?Boolean

Returns:

  • (Boolean)


62
63
64
65
66
67
68
69
70
# File 'lib/awsborn/server.rb', line 62

def running?
  map = {}
  disk_volume_ids.each { |vol_id| map[vol_id] = ec2.instance_id_for_volume(vol_id) }
  ids = map.values.uniq
  if ids.size > 1
    raise ServerError, "Volumes for #{self.class.name}:#{name} are connected to several instances: #{map.inspect}"
  end
  ec2.instance_id = ids.first
end

#start(key_pair) ⇒ Object



93
94
95
96
97
98
99
100
101
102
103
104
105
106
# File 'lib/awsborn/server.rb', line 93

def start (key_pair)
  launch_instance(key_pair)

  update_known_hosts
  install_ssh_keys(key_pair) if keys

  if elastic_ip
    associate_address
    update_known_hosts
  end

  bootstrap if bootstrap_script
  attach_volumes
end

#start_or_stop_monitoringObject



85
86
87
88
89
90
91
# File 'lib/awsborn/server.rb', line 85

def start_or_stop_monitoring
  if monitor && ! ec2.monitoring?
    ec2.monitor
  elsif ec2.monitoring? && ! monitor
    ec2.unmonitor
  end
end

#update_known_hostsObject



122
123
124
# File 'lib/awsborn/server.rb', line 122

def update_known_hosts
  KnownHostsUpdater.update_for_server self
end

#upload_cookbooksObject



178
179
180
181
182
183
184
185
186
187
188
189
190
# File 'lib/awsborn/server.rb', line 178

def upload_cookbooks
  logger.info "Uploading cookbooks to #{host_name}"

  cookbooks_dir = '../cookbooks' # Hard coded for now
  temp_link = File.directory?(cookbooks_dir) && ! File.directory?('cookbooks')
  File.symlink(cookbooks_dir, 'cookbooks') if temp_link

  File.open("config/dna.json", "w") { |f| f.write(chef_dna.to_json) }
  system "rsync -rL --delete --exclude '.*' ./ root@#{host_name}:#{Awsborn.remote_chef_path}"
ensure
  File.delete("config/dna.json")
  File.delete("cookbooks") if temp_link
end