Skip to content

Instantly share code, notes, and snippets.

@Miciah
Last active August 25, 2016 15:07
Show Gist options
  • Save Miciah/d149f18d873b79763b72 to your computer and use it in GitHub Desktop.
Save Miciah/d149f18d873b79763b72 to your computer and use it in GitHub Desktop.
#!/usr/bin/ruby
require 'aws-sdk'
require 'getoptlong'
require 'net/ssh'
require 'parseconfig'
REGIONS = {
'us-east-1' => 'N. Virginia',
'us-west-2' => 'Oregon',
'us-west-1' => 'N. California',
'eu-west-1' => 'Ireland',
'eu-central-1' => 'Frankfurt',
'ap-southeast-1' => 'Singapore',
'ap-southeast-2' => 'Sydney',
'ap-northeast-1' => 'Tokyo',
'sa-east-1' => 'Sao Paulo',
}
def print_usage
puts <<USAGE
== Synopsis
launch-ose3-ami: Launch, configure, or terminate an OpenShift Enterprise v3 AMI.
== Usage
launch-ose3-ami launch <instance_name> [options]
launch-ose3-ami configure <instance_name> [options]
launch-ose3-ami terminate <instance_name>
== List of options for "launch" and "configure":
-c|--credentials file AWS credentials file (optional, default: ~/.awscred)
-i|--image image Image (AMI) to launch (optional, default: latest)
-k|--key key SSH key to inject into the instance (optional, default: libra)
-r|--region region Region to launch the instance in (optional, default: us-east-1)
-b|--bucket bucket S3 bucket for image registry (optional, default: none)
-s|--size size Size of instance to launch (optional, default: t2.large)
-v|--vpc vpc VPC to use for new instance (optional, default: devenv)
-g|--security-group secgroup Security group(s) for new instance (optional, default: default)
-u|--subnet subnet Subnet to use for new instance (optional, default: subnet-cf57c596)
-?|--help Print this message
USAGE
end
def fail *message
puts message
exit 1
end
ARGS = {}
$AWS_CONF = nil
$COMMAND = nil
$INSTANCE = nil
def parse_args
opts = GetoptLong.new(
['--credentials', '-c', GetoptLong::OPTIONAL_ARGUMENT],
['--image', '-i', GetoptLong::OPTIONAL_ARGUMENT],
['--key', '-k', GetoptLong::OPTIONAL_ARGUMENT],
['--name', '-n', GetoptLong::REQUIRED_ARGUMENT],
['--region', '-r', GetoptLong::OPTIONAL_ARGUMENT],
['--bucket', '-b', GetoptLong::OPTIONAL_ARGUMENT],
['--size', '-s', GetoptLong::OPTIONAL_ARGUMENT],
['--vpc', '-v', GetoptLong::OPTIONAL_ARGUMENT],
['--security-group', '-g', GetoptLong::OPTIONAL_ARGUMENT],
['--subnet', '-u', GetoptLong::OPTIONAL_ARGUMENT],
['--help', '-?', GetoptLong::NO_ARGUMENT]
)
begin
opts.each{|k,v| ARGS[k] = v}
rescue GetoptLong::Error
print_usage
exit 255
end
if ARGS['--help']
print_usage
exit 0
end
ARGS['--credentials'] ||= '~/.awscred'
ARGS['--key'] ||= 'libra'
ARGS['--region'] ||= 'us-east-1'
#ARGS['--bucket'] ||= 'mmasters'
ARGS['--size'] ||= 't2.large'
ARGS['--security-group'] ||= 'default'
ARGS['--subnet'] ||= 'subnet-cf57c596'
ARGS['--vpc'] ||= 'devenv'
# TODO: Validate ARGS['--size'].
if !REGIONS.include?(ARGS['--region'])
fail "Invalid region: #{ARGS['--region']}.",
'The following regions are recognised:',
REGIONS.map {|k,v| "#{k} (#{v})"}
end
ARGS['--credentials'] = File.expand_path(ARGS['--credentials'])
if !File.exists?(ARGS['--credentials'])
fail "Credentials file not found: #{ARGS['--credentials']}."
end
$AWS_CONF = ParseConfig.new(ARGS['--credentials'])
$COMMAND = ARGV.shift
$INSTANCE = ARGV.shift
if $INSTANCE.nil? || $INSTANCE.empty?
fail 'You must specify a non-empty instance name.'
end
if !ARGV.empty?
print_usage
exit 255
end
end
def get_ec2_client
puts 'Setting up AWS connection...'
client = Aws::EC2::Client.new(
access_key_id: $AWS_CONF['AWSAccessKeyId'],
secret_access_key: $AWS_CONF['AWSSecretKey'],
region: ARGS['--region']
)
resource = Aws::EC2::Resource.new(client: client)
[client, resource]
end
def launch_instance instance_name
client, resource = get_ec2_client
if ARGS['--image'].nil?
puts 'Determining image name...'
images = []
resource.images({filters: [{name: 'name', values: ['devenv-rhel7_*']}]}).
each do |image|
images << image
end
ARGS['--image'] = images.
select {|image| image.state == 'available'}.
sort_by {|image| image.name[/.*_(\d)+/, 1]}.
last.id
end
if ARGS['--vpc'][0,4] != 'vpc-'
resource.vpcs.each do |vpc|
if vpc.tags.any? {|tag| tag.key == 'Name' && tag.value == ARGS['--vpc']}
ARGS['--vpc'] = vpc.id
break
end
end
end
if ARGS['--vpc'][0,4] != 'vpc-'
fail "Unrecognised VPC: #{ARGS['--vpc']}"
end
secgroups, resolve = ARGS['--security-group'].split(',').partition do |sg|
sg[0,3] == 'sg-'
end
if !resolve.empty?
resource.
security_groups({filters: [{name: 'vpc-id', values: [ARGS['--vpc']]}]}).
each do |sg|
secgroups << sg.id if resolve.delete sg.group_name
end
end
if !resolve.empty?
fail 'Unrecognised security groups:', resolve
end
puts "Launching instance #{instance_name} from image #{ARGS['--image']}" +
" with size #{ARGS['--size']} and key #{ARGS['--key']}" +
" in region #{ARGS['--region']}" +
(ARGS['--bucket'] ? " using bucket #{ARGS['--bucket']}" : '') +
" and VPC #{ARGS['--vpc']}" +
" with security groups #{secgroups.join(', ')}..."
instances = resource.create_instances({
min_count: 1,
max_count: 1,
image_id: ARGS['--image'],
instance_type: ARGS['--size'],
network_interfaces: [
{
subnet_id: ARGS['--subnet'],
groups: secgroups,
device_index: 0,
}
],
key_name: ARGS['--key'],
})
puts 'Tagging instance names...'
instances.each do |instance|
instance.create_tags({tags: [key: 'Name', value: instance_name]})
end
puts 'Waiting for instances to be available...'
begin
client.wait_until(:instance_running,
instance_ids: instances.map {|instance| instance.id})
rescue Aws::Waiters::Errors::WaiterFailed
$stderr.puts 'Failed to create instance.'
exit 1
end
puts 'Waiting for instances to have a public DNS entry...'
instances.each {|instance| sleep 1 while instance.public_dns_name.nil?}
instances.each {|instance| instance.reload}
instances
end
def find_instance instance_name
_, resource = get_ec2_client
puts "Looking up instance #{instance_name}..."
instances = []
resource.instances({filters: [{name: 'tag:Name', values: [instance_name]}]}).
each {|instance| instances << instance}
instances
end
def configure_instance instance
puts "Configuring instance #{instance.public_dns_name} (#{instance.id})..."
docker_configuration = "version: 0.1
log:
level: debug
http:
addr: :5000
storage:
cache:
layerinfo: inmemory
s3:
accesskey: #{$AWS_CONF['AWSAccessKeyId']}
secretkey: #{$AWS_CONF['AWSSecretKey']}
region: #{ARGS['--region']}
bucket: #{ARGS['--bucket']}
encrypt: true
secure: true
v4auth: true
rootdirectory: /registry
middleware:
repository:
- name: openshift
" if ARGS['--bucket']
retries = 0
begin
Net::SSH.start(instance.public_dns_name, 'root') do |session|
puts ' Creating the master configuration...'
session.exec! 'openshift start master --write-config=/openshift.local.config/master'
puts ' Creating the node configuration...'
session.exec! "oadm create-node-config --node-dir=/openshift.local.config/node-#{instance.private_dns_name} --node=#{instance.private_dns_name} --hostnames=#{instance.private_dns_name},#{instance.private_ip_address},#{instance.public_dns_name},#{instance.public_ip_address} --certificate-authority=/openshift.local.config/master/ca.crt --signer-cert=/openshift.local.config/master/ca.crt --signer-key=/openshift.local.config/master/ca.key --signer-serial=/openshift.local.config/master/ca.serial.txt --node-client-certificate-authority=/openshift.local.config/master/ca.crt"
puts ' Modifying openshift unit file to use master and node configuration...'
session.exec! "sed -i -e '/ExecStart/ s|$| --node-config=/openshift.local.config/node-#{instance.private_dns_name}/node-config.yaml --master-config=/openshift.local.config/master/master-config.yaml|' /usr/lib/systemd/system/openshift.service"
puts ' Fixing hostname in the openshift unit file...'
session.exec! "sed -i -e 's/ec2\\(-[0-9]\\+\\)\\{4\\}.compute-1.amazonaws.com/#{instance.public_dns_name}/' /usr/lib/systemd/system/openshift.service"
puts ' Reloading systemd...'
session.exec! 'systemctl daemon-reload'
puts ' Starting the openshift service...'
session.exec! 'systemctl start openshift'
puts ' Waiting for the openshift service to start...'
session.exec! 'while ! oc status >& /dev/null; do sleep 1; done'
puts ' Configuring roles...'
session.exec! 'oadm policy add-role-to-user system:registry reguser'
puts ' Deploying the image registry...'
session.exec! 'oadm registry --config=/openshift.local.config/master/admin.kubeconfig --credentials=/openshift.local.config/master/openshift-registry.kubeconfig'
puts ' Creating service account for the registry...'
session.exec! 'oc create sa registry'
puts ' Adding registry service account to privileged SCC...'
session.exec! 'oc patch scc/privileged --patch=\'[{"op":"add","path":"/users/-","value":"system:serviceaccount:default:registry"}]\' --type=json'
if ARGS['--bucket'].nil?
puts ' Skipping image-registry configuration because no bucket was specified.'
else
puts ' Scaling image registry down...'
session.exec! 'oc scale --replicas=0 dc/docker-registry'
puts ' Writing out image-registry configuration file...'
session.exec! "cat > /root/config.yml <<EOF
#{docker_configuration}
EOF"
puts ' Adding secret for image-registry configuration...'
session.exec! 'oc secrets new dockerregistry /root/config.yml'
puts ' Adding secrets volume with new secret to the image registry...'
session.exec! 'oc volume dc/docker-registry --add --name=dockersecrets -m /etc/registryconfig --type=secret --secret-name=dockerregistry'
puts ' Configuring image registry to use new configuration file...'
session.exec! 'oc env dc/docker-registry REGISTRY_CONFIGURATION_PATH=/etc/registryconfig/config.yml'
puts ' Scaling image registry back up...'
session.exec! 'oc scale --replicas=1 dc/docker-registry'
end
puts ' Exposing the registry...'
session.exec! 'oc expose service docker-registry && oc patch routes/docker-registry -p \'{"spec":{"tls":{"termination":"edge","insecureEdgeTerminationPolicy":"Allow"}}}\''
puts ' Importing Centos 7 image streams...'
session.exec! 'oc create -f https://raw.githubusercontent.com/openshift/origin/master/examples/image-streams/image-streams-centos7.json -n openshift'
puts ' Creating service account for the router...'
session.exec! 'oc create sa router'
puts ' Adding router service account to privileged SCC...'
#session.exec! 'oc patch scc/privileged --patch=\'[{"op":"add","path":"/users/-","value":"system:serviceaccount:default:router"}]\' --type=json'
session.exec! 'oadm policy add-scc-to-user privileged system:serviceaccount:default:router'
puts ' Fixing builder service account...'
#session.exec! 'export OC_EDITOR=\'sed -i -e "/system:serviceaccount:openshift-infra:build-controller/ s/openshift-infra/default/"\'; oc edit scc privileged'
#session.exec! 'oc patch scc/privileged --patch=\'[{"op":"test","path":"/users/0","value":"system:serviceaccount:openshift-infra:build-controller"},{"op":"replace","path":"/users/0","value":"system:serviceaccount:default:build-controller"}]\' --type=json'
session.exec! 'oadm policy add-scc-to-user privileged system:serviceaccount:openshift-infra:build-controller'
session.exec! 'oadm policy add-scc-to-user privileged system:serviceaccount:default:build-controller'
puts ' Creating key and certificate for router...'
session.exec! 'CA=/openshift.local.config/master; oadm ca create-server-cert --signer-cert=$CA/ca.crt --signer-key=$CA/ca.key --signer-serial=$CA/ca.serial.txt --hostnames="*.cloudapps.example.com" --cert=$CA/cloudapps.crt --key=$CA/cloudapps.key; cat $CA/cloudapps.crt $CA/cloudapps.key $CA/ca.crt > $CA/cloudapps.router.pem'
puts ' Starting router...'
session.exec! 'oadm router --replicas=1 --default-cert=/openshift.local.config/master/cloudapps.router.pem --credentials=/openshift.local.config/master/openshift-router.kubeconfig --service-account=router'
puts ' Adding "alice" and "joe" users...'
session.exec! 'useradd alice; useradd joe'
puts ' Fixing KUBECONFIG setting in /etc/profile.d/openshift.sh...'
session.exec! 'sed -i -e \'/KUBECONFIG=/ s/^/[[ $EUID -eq 0 ]] \\&\\& /\' /etc/profile.d/openshift.sh'
puts ' Logging alice and joe in...'
session.exec! 'su - alice -c \'oc login -u alice -p alice --certificate-authority=/openshift.local.config/master/ca.crt --server=https://localhost:8443/\''
session.exec! 'su - joe -c \'oc login -u joe -p joe --certificate-authority=/openshift.local.config/master/ca.crt --server=https://localhost:8443/\''
puts ' Creating aliceproject and joeproject projects...'
session.exec! 'su - alice -c \'oc new-project aliceproject\''
session.exec! 'su - joe -c \'oc new-project joeproject\''
puts ' Adding aliceproject and joeproject builder service accounts to privileged SCC...'
session.exec! 'oc patch scc/privileged --patch=\'[{"op":"add","path":"/users/-","value":"system:serviceaccount:aliceproject:builder"},{"op":"add","path":"/users/-","value":"system:serviceaccount:joeproject:builder"}]\' --type=json'
end
rescue Errno::ECONNREFUSED, Errno::ETIMEDOUT => e
if (retries+=1) < 12
$stderr.puts "Error connecting to #{instance.public_dns_name}: #{e.message}. Retrying..."
sleep 5
retry
end
$stderr.puts "Unable to connect to #{instance.public_dns_name}."
end
end
parse_args
case $COMMAND
when 'list'
instances = find_instance $INSTANCE
instances.each do |instance|
puts 'Found ' + instance.tags.find{|t| t.key == 'Name'}.value,
instance.tags.inspect, ''
end
when 'launch'
instances = launch_instance $INSTANCE
instances.each {|instance| configure_instance instance}
when 'configure'
instances = find_instance $INSTANCE
instances.each {|instance| configure_instance instance}
when 'terminate'
instances = find_instance $INSTANCE
instances.each do |instance|
puts "Terminating instance #{instance.public_dns_name} (#{instance.id})..."
instance.terminate
end
end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment