fluke/devops-service/providers/ec2.rb

319 lines
9.5 KiB
Ruby
Raw Normal View History

2015-08-04 09:56:03 +03:00
require "exceptions/conflict_exception"
2014-05-08 15:34:26 +04:00
require "providers/base_provider"
module Provider
# Provider for Amazon EC2
class Ec2 < BaseProvider
2014-05-08 15:34:26 +04:00
PROVIDER = "ec2"
2014-05-08 15:34:26 +04:00
attr_accessor :availability_zone
2014-05-08 15:34:26 +04:00
def initialize config
self.certificate_path = config[:aws_certificate]
self.ssh_key = config[:aws_ssh_key]
2015-08-07 11:36:50 +03:00
options = {
:provider => "aws",
:aws_access_key_id => config[:aws_access_key_id],
:aws_secret_access_key => config[:aws_secret_access_key]
}
2015-08-07 12:03:01 +03:00
if config[:aws_proxy] and config[:aws_no_proxy]
options[:connection_options] = {
:proxy => config[:aws_proxy],
:no_proxy => config[:no_proxy]
}
end
2015-08-07 11:36:50 +03:00
self.connection_options = options
self.availability_zone = config[:aws_availability_zone] || "us-east-1a"
2014-11-20 15:08:42 +03:00
self.run_list = config[:aws_integration_run_list] || []
end
2014-05-08 15:34:26 +04:00
def configured?
o = self.connection_options
super and !(empty_param?(o[:aws_access_key_id]) or empty_param?(o[:aws_secret_access_key]))
end
2014-05-08 15:34:26 +04:00
def name
PROVIDER
end
2014-05-08 15:34:26 +04:00
def flavors
self.compute.flavors.all.map do |f|
{
"id" => f.id,
"cores" => f.cores,
"disk" => f.disk,
"name" => f.name,
"ram" => f.ram
}
2014-05-08 15:34:26 +04:00
end
end
2014-05-08 15:34:26 +04:00
def groups filters=nil
buf = {}
buf = filters.select{|k,v| ["vpc-id"].include?(k)} unless filters.nil?
g = if buf.empty?
self.compute.describe_security_groups
else
self.compute.describe_security_groups(buf)
2014-05-08 15:34:26 +04:00
end
convert_groups(g.body["securityGroupInfo"])
end
2014-05-08 15:34:26 +04:00
def images filters
self.compute.describe_images({"image-id" => filters}).body["imagesSet"].map do |i|
{
"id" => i["imageId"],
"name" => i["name"],
"status" => i["imageState"]
}
2014-05-08 15:34:26 +04:00
end
end
2014-05-08 15:34:26 +04:00
def networks_detail
self.networks
end
2014-05-08 15:34:26 +04:00
def networks
self.compute.describe_subnets.body["subnetSet"].select{|n| n["state"] == "available"}.map do |n|
{
"cidr" => n["cidrBlock"],
"vpcId" => n["vpcId"],
"subnetId" => n["subnetId"],
"name" => n["subnetId"],
"zone" => n["availabilityZone"]
}
2014-05-08 15:34:26 +04:00
end
end
2014-05-08 15:34:26 +04:00
def servers
list = self.compute.describe_instances.body["reservationSet"]
list.select{|l| l["instancesSet"][0]["instanceState"]["name"].to_s != "terminated"}.map do |server|
convert_server server["instancesSet"][0]
2014-05-08 15:34:26 +04:00
end
end
2014-05-08 15:34:26 +04:00
def server id
list = self.compute.describe_instances('instance-id' => [id]).body["reservationSet"]
convert_server list[0]["instancesSet"][0]
end
2014-05-08 15:34:26 +04:00
2015-08-05 17:38:32 +03:00
def create_server s, image, flavor, subnets, groups, out, options={}
out << "Creating server for project '#{s.project} - #{s.deploy_env}'\n"
options = {
2015-08-05 14:05:14 +03:00
"InstanceType" => flavor,
# "Placement.AvailabilityZone" => s.options[:availability_zone],
"KeyName" => self.ssh_key
}
vpcId = nil
2015-08-05 14:05:14 +03:00
unless subnets.empty?
options["SubnetId"] = subnets[0]
vpcId = self.networks.detect{|n| n["name"] == options["SubnetId"]}["vpcId"]
if vpcId.nil?
out << "Can not get 'vpcId' by subnet name '#{options["SubnetId"]}'\n"
2014-05-08 15:34:26 +04:00
return false
end
end
2015-08-05 14:05:14 +03:00
options["SecurityGroupId"] = extract_group_ids(groups, vpcId).join(",")
aws_server = nil
compute = self.compute
begin
2015-08-05 14:05:14 +03:00
aws_server = compute.run_instances(image, 1, 1, options)
rescue Excon::Errors::Unauthorized => ue
#root = XML::Parser.string(ue.response.body).parse.root
#msg = root.children.find { |node| node.name == "Message" }
#code = root.children.find { |node| node.name == "Code" }
code = "TODO"
msg = ue.response.body
out << "\nERROR: Unauthorized (#{code}: #{msg})"
return false
rescue Fog::Compute::AWS::Error => e
out << e.message
return false
end
abody = aws_server.body
instance = abody["instancesSet"][0]
s.id = instance["instanceId"]
out << "\nWaiting for server..."
details, state = nil, instance["instanceState"]["name"]
until state == "running"
sleep(2)
details = compute.describe_instances("instance-id" => [s.id]).body["reservationSet"][0]["instancesSet"][0]
state = details["instanceState"]["name"].to_s
next if state == "pending" or state == "running"
out << "Server returns state '#{state}'"
return false
end
s.public_ip = details["ipAddress"]
s.private_ip = details["privateIpAddress"]
2015-08-07 13:06:59 +03:00
compute.create_tags(s.id, {"Name" => s.chef_node_name})
out << "\nDone\n\n"
out << s.info
true
end
2014-05-08 15:34:26 +04:00
def delete_server s
r = self.compute.terminate_instances(s.id)
i = r.body["instancesSet"][0]
old_state = i["previousState"]["name"]
state = i["currentState"]["name"]
return r.status == 200 ? "Server with id '#{s.id}' changed state '#{old_state}' to '#{state}'" : r.body
end
2014-05-08 15:34:26 +04:00
def pause_server s
es = self.server s.id
if es["state"] == "running"
self.compute.stop_instances [ s.id ]
return nil
else
return es["state"]
2014-05-08 15:34:26 +04:00
end
end
2014-05-08 15:34:26 +04:00
def unpause_server s
es = self.server s.id
if es["state"] == "stopped"
self.compute.start_instances [ s.id ]
return nil
else
return es["state"]
2014-05-08 15:34:26 +04:00
end
end
2014-05-08 15:34:26 +04:00
2015-08-03 18:01:50 +03:00
def set_tags instance_id, tags
2015-08-04 09:56:03 +03:00
raise ConflictException.new("You can not change 'Name' tag") if tags.key?("Name")
2015-08-03 18:01:50 +03:00
compute.create_tags(instance_id, tags)
end
def unset_tags instance_id, tags
2015-08-04 09:56:03 +03:00
raise ConflictException.new("You can not change 'Name' tag") if tags.key?("Name")
2015-08-03 18:01:50 +03:00
compute.delete_tags(instance_id, tags)
end
2014-06-20 12:59:17 +04:00
def compute
connection_compute(connection_options)
end
2015-08-17 11:25:20 +03:00
def cloud_formation
@cloud_formation ||= Fog::AWS::CloudFormation.new(connection_options)
end
def create_stack(stack, out)
begin
out << "Creating stack for project '#{stack.project}' and environment '#{stack.deploy_env}'...\n"
stack.name = create_default_stack_name(stack) unless stack.name
out << "Stack name: #{stack.name}\n"
out << "Stack template: #{stack.stack_template}\n"
out << "Stack parameters: #{stack.parameters}\n"
out << "Stack template: #{stack.template_body}\n"
response = cloud_formation.create_stack(stack.name,
{
'TemplateBody' => stack.template_body,
2015-08-18 16:09:41 +03:00
'Parameters' => stack.parameters || {},
'Capabilities' => ['CAPABILITY_IAM']
2015-08-17 11:25:20 +03:00
}
)
stack.id = response.body['StackId']
rescue Excon::Errors::Conflict => e
raise ProviderErrors::NameConflict
rescue Excon::Errors::BadRequest => br
response = ::Chef::JSONCompat.from_json(br.response.body)
if response['code'] == 400
out << "\nERROR: Bad request (400): #{response['explanation']}"
out << "\n"
raise InvalidRecord.new(response['explanation'])
else
out << "\nERROR: Unknown server error (#{response['code']}): #{response['explanation']}"
out << "\n"
raise InvalidRecord.new(response['explanation'])
end
end
end
def delete_stack(stack)
2015-08-17 17:50:24 +03:00
cloud_formation.delete_stack(stack.name)
2015-08-17 11:25:20 +03:00
end
def stack_details(stack)
2015-08-18 16:23:44 +03:00
body = cloud_formation.describe_stacks({'StackName' => stack.name}).body
d = body['Stacks'][0]['stack']
#d = cloud_formation.describe_stacks({'StackName' => stack.name}).body['Stacks'][0]['stack']
puts "Details body: #{body.inspect}"
2015-08-17 17:50:24 +03:00
puts "Details: #{d.inspect}"
2015-08-17 11:25:20 +03:00
end
def stack_resources(stack)
2015-08-17 17:50:24 +03:00
cloud_formation.describe_stack_resources({'StackName' => stack.name}).body['StackResources']
2015-08-17 11:25:20 +03:00
end
def stack_resource(stack, resource_id)
physical_id = fog_stack(stack).resources.get(resource_id).physical_resource_id
compute.servers.get(physical_id)
end
def stack_servers(stack)
2015-08-18 16:15:23 +03:00
stack_resources(stack).map{|r| compute.servers.get(r['physical_resource_id'])}
2015-08-17 11:25:20 +03:00
end
2015-08-18 16:02:55 +03:00
def create_default_stack_name s
"stack-#{self.ssh_key}-#{s.project}-#{s.deploy_env}-#{Time.now.to_i}"
end
private
def convert_groups list
res = {}
list.each do |g|
2014-11-20 15:08:42 +03:00
next if g["groupName"].nil?
res[g["groupName"]] = {
"description" => g["groupDescription"],
"id" => g["groupId"]
}
rules = []
g["ipPermissions"].each do |r|
cidr = r["ipRanges"][0] || {}
rules.push({
"protocol" => r["ipProtocol"],
"from" => r["fromPort"],
"to" => r["toPort"],
"cidr" => cidr["cidrIp"]
})
2014-05-08 15:34:26 +04:00
end
res[g["groupName"]]["rules"] = rules
2014-05-08 15:34:26 +04:00
end
res
end
2014-05-08 15:34:26 +04:00
def convert_server s
{
"state" => s["instanceState"]["name"],
"name" => s["tagSet"]["Name"],
"image" => s["imageId"],
"flavor" => s["instanceType"],
"keypair" => s["keyName"],
"instance_id" => s["instanceId"],
"dns_name" => s["dnsName"],
"zone" => s["placement"]["availabilityZone"],
"private_ip" => s["privateIpAddress"],
"public_ip" => s["ipAddress"],
"launched_at" => s["launchTime"]
}
end
2014-05-08 15:34:26 +04:00
def extract_group_ids names, vpcId
return [] if names.nil?
p = nil
p = {"vpc-id" => vpcId} unless vpcId.nil?
groups = self.groups(p)
r = names.map do |name|
groups[name]["id"]
2014-05-08 15:34:26 +04:00
end
r
end
2015-08-17 11:25:20 +03:00
2014-05-08 15:34:26 +04:00
end
end