Created
February 9, 2017 04:47
-
-
Save excalq/664c7577b0e244332c10ac5b822f1e24 to your computer and use it in GitHub Desktop.
DevClusters - Build/Start/Stop an AWS cluster for a user
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/usr/bin/env ruby | |
USER="arthur" | |
AWS_KEY="/Users/arthur/.ssh/DevClusters.pem" | |
CLUSTER_ROLES={ # Build in this order: | |
nfs: {image: 'ami-a0951573', name: 'devcluster-nfs', secgroups: ['sg-b37b7886','sg-aa7a697f'], count: 1}, | |
redis: {image: 'ami-10201b69', name: 'devcluster-redis', secgroups: ['sg-b37b7886','sg-aa7a697f'], count: 1}, | |
databrain: {image: 'ami-8a30206d', name: 'devcluster-databrain', secgroups: ['sg-b37b7886','sg-aa7a697f'], count: 1}, | |
mysql: {image: 'ami-cc5b10fd', name: 'devcluster-mysql', secgroups: ['sg-b37b7886','sg-aa7a697f'], count: 2}, | |
mongodb: {image: 'ami-56102071', name: 'devcluster-mongodb', secgroups: ['sg-b37b7886','sg-aa7a697f'], count: 2}, | |
app: {image: 'ami-2b415b75', name: 'devcluster-app', secgroups: ['sg-b37b7886','sg-aa7a697f','sg-b57c68a0'], count: 1} | |
} | |
SEC_GROUPS=[] | |
DRY_RUN=false | |
require 'aws-sdk-core' | |
require 'json' | |
require 'awesome_print' | |
class InstanceManager | |
@ec2 = nil | |
def initialize(region) | |
@ec2 = Aws::EC2::Client.new(region: region) | |
end | |
def build_instance(config_params) | |
secgroups = config_params.delete(:secgroups) || [] | |
config_params = {min_count: 1, max_count: 1, instance_type: 't2.micro', | |
dry_run: DRY_RUN, | |
key_name: 'DevClusters', | |
network_interfaces: [ | |
device_index: 0, | |
subnet_id: 'subnet-9b97890a', | |
associate_public_ip_address: true, | |
groups: ['sg-941af7e1'] # The default sec-group. More are appended below | |
] | |
}.merge(config_params) | |
config_params[:network_interfaces].each{|ni| ni[:groups] += secgroups} unless secgroups.empty? | |
begin | |
return @ec2.run_instances(config_params) | |
rescue Aws::EC2::Errors::DryRunOperation | |
puts "Dry run succeeded for #{config_params}" | |
end | |
end | |
# Returns array of AwsInstances | |
def build_all_instances | |
started_instance_ids = [] | |
cluster_instances = [] | |
CLUSTER_ROLES.each do |role, cfg| | |
cfg[:count].times do |i| | |
suffix = '' | |
suffix = i.to_s if cfg[:count] > 1 | |
cluster_instances << AwsInstance.new(cfg.merge({role: role, role_sequence: i, suffix: suffix})) | |
end | |
end | |
cluster_instances.each do |instance| | |
result = build_instance({image_id: instance.image, secgroups: instance.secgroups}) | |
instance.id = result[:instances].first[:instance_id] | |
started_instance_ids << instance.id | |
end | |
wait_for_new_instances(started_instance_ids) | |
puts "New instances are now online." | |
ap @ec2.describe_instances(instance_ids: started_instance_ids) | |
cluster_instances.each do |instance| | |
unless DRY_RUN | |
# Set ip address info | |
instance_data = @ec2.describe_instances(instance_ids: [instance.id]) | |
instance.public_ip_address = instance_data[:reservations].first[:instances].first[:public_ip_address] | |
instance.private_ip_address = instance_data[:reservations].first[:instances].first[:private_ip_address] | |
# Set tags (Name, Owner) | |
tag_instances([instance.id], "Name", "#{instance.name}#{instance.suffix}") | |
tag_instances([instance.id], "Owner", USER) | |
end | |
end | |
puts "Created new instances: \n\t#{cluster_instances.map(&:to_s).join("\n\t")}" | |
cluster_instances | |
end | |
def wait_for_new_instances(started_instance_ids) | |
# Wait until they're running... | |
puts "Waiting for new instances to become ready..." | |
@ec2.wait_until(:instance_running, {instance_ids: started_instance_ids}) | |
end | |
def tag_instances(ids, tag_name, tag_value) | |
@ec2.create_tags( | |
resources: ids, | |
tags: [ | |
{key: tag_name, value: tag_value} | |
]) | |
end | |
def configure_instances(cluster) | |
# Writes internal IP addresses to specific configuration files | |
end | |
def get_instance_ips | |
ap instance[:network_interfaces][0][:association][:public_ip] | |
end | |
end | |
class AwsInstance | |
attr_accessor :name | |
attr_accessor :id | |
attr_accessor :role | |
attr_accessor :image | |
attr_accessor :secgroups | |
attr_accessor :role_sequence # when multiple instances exist per role | |
attr_accessor :suffix # number at end of multiple-instance names | |
attr_accessor :public_ip_address | |
attr_accessor :private_ip_address | |
attr_accessor :errors | |
def initialize(*attrs) | |
if attrs.length == 1 && attrs.first.kind_of?(Hash) | |
attrs.first.each { |k,v| send("#{k}=",v) if self.respond_to? k } | |
end | |
end | |
def to_s | |
{ | |
name: name, | |
id: id, | |
role: role, | |
role_sequence: role_sequence, | |
public_ip_address: public_ip_address, | |
private_ip_address: private_ip_address | |
} | |
end | |
end | |
###################### | |
# Read API Credentials for IAM user | |
unless File.exists?('secrets.json') | |
puts 'You must first create a secrets.json file, containing this data: | |
{ | |
"region": "us-west-2", | |
"AccessKeyId": "your-aws-access-key", | |
"SecretAccessKey": "your-aws-secret-key" | |
}' | |
puts 'Exiting for now.' | |
exit(1) | |
end | |
creds = JSON.load(File.read('secrets.json')) | |
Aws.config[:credentials] = Aws::Credentials.new(creds['AccessKeyId'], creds['SecretAccessKey']) | |
im = InstanceManager.new(creds['region']) | |
###################### | |
# build Cluster | |
if ARGV.nil? or ARGV.count < 1 | |
puts "Usage: #{$0} [operation] | |
Where [operation] is one of: | |
build_cluster | |
start_cluster | |
stop_cluster" | |
exit 1 | |
end | |
case ARGV[0] | |
when 'build_cluster' | |
cluster = im.build_all_instances | |
im.configure_instances(cluster) | |
else | |
puts "Sorry #{ARGV[0]} is not supported yet (or is invalid)." | |
end |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add a secrets.json file as follows: