lib/bosh-bootstrap/cli.rb in bosh-bootstrap-0.7.1 vs lib/bosh-bootstrap/cli.rb in bosh-bootstrap-0.8.0

- old
+ new

@@ -21,17 +21,17 @@ attr_reader :fog_credentials attr_reader :server desc "deploy", "Bootstrap Micro BOSH, and optionally an Inception VM" method_option :fog, :type => :string, :desc => "fog config file (default: ~/.fog)" - method_option :"private-key", :type => :string, :desc => "Local passphrase-less private key path" method_option :"upgrade-deps", :type => :boolean, :desc => "Force upgrade dependencies, packages & gems" method_option :"edge-deployer", :type => :boolean, :desc => "Install bosh deployer from git instead of rubygems" method_option :"stable-stemcell", :type => :boolean, :desc => "Use recent stable microbosh stemcell" method_option :"latest-stemcell", :type => :boolean, :desc => "Use latest microbosh stemcell; possibly not tagged stable [default]" method_option :"edge-stemcell", :type => :boolean, :desc => "Create custom stemcell from BOSH git source" def deploy + migrate_old_settings load_deploy_options # from method_options above deploy_stage_1_choose_infrastructure_provider deploy_stage_2_bosh_configuration deploy_stage_3_create_allocate_inception_vm @@ -41,10 +41,11 @@ end desc "upgrade-inception", "Upgrade inception VM with latest packages, gems, security group ports" method_option :"edge-deployer", :type => :boolean, :desc => "Install bosh deployer from git instead of rubygems" def upgrade_inception + migrate_old_settings load_deploy_options # from method_options above setup_server upgrade_inception_stage_1_prepare_inception_vm end @@ -67,28 +68,31 @@ long_desc <<-DESC If a command is supplied, it will be run, otherwise a session will be opened. DESC def ssh(cmd=nil) + migrate_old_settings run_ssh_command_or_open_tunnel(cmd) end desc "tmux", "Open an ssh (with tmux) session to the inception VM [do nothing if local machine is inception VM]" long_desc <<-DESC Opens a connection using ssh and attaches to the most recent tmux session; giving you persistance across disconnects. DESC def tmux + migrate_old_settings run_ssh_command_or_open_tunnel(["-t", "tmux attach || tmux new-session"]) end desc "mosh", "Open an mosh session to the inception VM [do nothing if local machine is inception VM]" long_desc <<-DESC Opens a connection using MOSH (http://mosh.mit.edu/); ideal for those with slow or flakey internet connections. Requires outgoing UDP port 60001 to the Inception VM DESC def mosh + migrate_old_settings open_mosh_session end no_tasks do DEFAULT_INCEPTION_VOLUME_SIZE = 32 # Gb @@ -232,10 +236,14 @@ end end save_settings! if settings["inception"]["create_new"] && !settings["inception"]["host"] + unless settings["inception"]["key_pair"] + create_inception_key_pair + end + recreate_local_ssh_keys_for_inception_vm aws? ? boot_aws_inception_vm : boot_openstack_inception_vm end # If successfully validate inception VM, then save those settings. save_settings! @@ -252,10 +260,12 @@ end def deploy_stage_4_prepare_inception_vm unless settings["inception"] && settings["inception"]["prepared"] && !settings["upgrade_deps"] header "Stage 4: Preparing the Inception VM" + recreate_local_ssh_keys_for_inception_vm + unless run_server(Bosh::Bootstrap::Stages::StagePrepareInceptionVm.new(settings).commands) error "Failed to complete Stage 4: Preparing the Inception VM" end # Settings are updated by this stage # it generates a salted password from settings.bosh.password @@ -267,10 +277,12 @@ end end def deploy_stage_5_deploy_micro_bosh header "Stage 5: Deploying micro BOSH" + recreate_local_ssh_keys_for_inception_vm + unless run_server(Bosh::Bootstrap::Stages::MicroBoshDeploy.new(settings).commands) error "Failed to complete Stage 5: Deploying micro BOSH" end confirm "Successfully built micro BOSH" @@ -327,11 +339,12 @@ end def setup_server if settings["inception"]["host"] - @server = Commander::RemoteServer.new(settings.inception.host, settings.local.private_key_path) + private_key_path = settings["inception"]["local_private_key_path"] + @server = Commander::RemoteServer.new(settings.inception.host, private_key_path) confirm "Using inception VM #{settings.inception.username}@#{settings.inception.host}" else @server = Commander::LocalServer.new confirm "Using this server as the inception VM" end @@ -356,15 +369,15 @@ end def run_ssh_command_or_open_tunnel(cmd) ensure_inception_vm ensure_inception_vm_has_launched + recreate_local_ssh_keys_for_inception_vm - username = 'vcap' - host = settings.inception[:host] - _, private_key_path = local_ssh_key_paths - result = system Escape.shell_command(['ssh', "-i", "#{private_key_path}", "#{username}@#{host}", cmd].flatten.compact) + username = "vcap" + host = settings["inception"]["host"] + result = system Escape.shell_command(["ssh", "-i", inception_vm_private_key_path, "#{username}@#{host}", cmd].flatten.compact) exit result end def ensure_inception_vm unless settings[:inception] @@ -380,10 +393,11 @@ def open_mosh_session ensure_mosh_installed ensure_inception_vm ensure_inception_vm_has_launched + recreate_local_ssh_keys_for_inception_vm ensure_security_group_allows_mosh username = 'vcap' host = settings.inception[:host] exit system Escape.shell_command(['mosh', "#{username}@#{host}"]) @@ -470,25 +484,10 @@ # once a stemcell is downloaded or created; these fields above should # be uploaded with values such as: # -> settings["micro_bosh_stemcell_name"] = "micro-bosh-stemcell-aws-0.8.1.tgz" - if options["private-key"] - private_key_path = File.expand_path(options["private-key"]) - unless File.exists?(private_key_path) - error "Cannot find a file at #{private_key_path}" - end - public_key_path = "#{private_key_path}.pub" - unless File.exists?(public_key_path) - error "Cannot find a file at #{public_key_path}" - end - - settings["local"] ||= {} - settings["local"]["private_key_path"] = private_key_path - settings["local"]["public_key_path"] = public_key_path - end - if options["upgrade-deps"] settings["upgrade_deps"] = options["upgrade-deps"] else settings.delete("upgrade_deps") end @@ -804,36 +803,75 @@ else error "Key pair '#{key_pair_name}' already exists. Rename BOSH or delete old key pair manually and re-run CLI." end end + # Creates a key pair with the provider for the inception VM. + # Stores the private & public key in settings manifest. + # + # If provider already has a key pair of the same name, it re-creates it. + # + # Adds settings: + # * inception.key_pair.name + # * inception.key_pair.public_key + # * inception.key_pair.private_key + # * inception.key_pair.fingerprint + def create_inception_key_pair + say "Creating ssh key pair for Inception VM..." + create_key_pair_store_in_settings("inception") + end + + # Creates a key pair with the provider. + # Stores the private & public key in settings manifest. + # + # If provider already has a key pair of the same name, it re-creates it. + # + # Adds settings: + # * <settings_key>.key_pair.name # defaults to settings_key value + # * <settings_key>.key_pair.public_key + # * <settings_key>.key_pair.private_key + # * <settings_key>.key_pair.fingerprint + def create_key_pair_store_in_settings(settings_key, default_key_pair_name = settings_key) + settings[settings_key] ||= {} + settings[settings_key]["key_pair"] ||= {} + key_pair_settings = settings[settings_key]["key_pair"] + key_pair_settings["name"] ||= default_key_pair_name + key_pair_name = key_pair_settings["name"] + + provider.delete_key_pair_if_exists(key_pair_name) + fog_key_pair = provider.create_key_pair(key_pair_name) + + key_pair_settings["private_key"] = fog_key_pair.private_key + key_pair_settings["public_key"] = fog_key_pair.public_key + key_pair_settings["fingerprint"] = fog_key_pair.fingerprint + save_settings! + end + # Provisions an AWS m1.small VM as the inception VM # Updates settings.inception.host/username # # NOTE: if any stage fails, when the CLI is re-run # and "create new server" is selected again, the process should # complete - # - # Assumes that local CLI user has public/private keys at ~/.ssh/id_rsa.pub def boot_aws_inception_vm say "" # glowing whitespace unless settings["inception"]["ip_address"] say "Provisioning IP address for inception VM..." settings["inception"]["ip_address"] = acquire_ip_address save_settings! end - public_key_path, private_key_path = local_ssh_key_paths unless settings["inception"] && settings["inception"]["server_id"] username = "ubuntu" size = "m1.small" ip_address = settings["inception"]["ip_address"] + key_name = settings["inception"]["key_pair"]["name"] say "Provisioning #{size} for inception VM..." inception_vm_attributes = { - :public_key_path => public_key_path, - :private_key_path => private_key_path, + :key_name => key_name, + :private_key_path => inception_vm_private_key_path, :flavor_id => size, :bits => 64, :username => "ubuntu", :public_ip_address => ip_address } @@ -845,11 +883,11 @@ server = provider.bootstrap(inception_vm_attributes) unless server error "Something mysteriously cloudy happened and fog could not provision a VM. Please check your limits." end - settings["inception"] = {} + settings["inception"].delete("create_new") settings["inception"]["server_id"] = server.id settings["inception"]["username"] = username save_settings! end @@ -883,29 +921,13 @@ # Updates settings.inception.host/username # # NOTE: if any stage fails, when the CLI is re-run # and "create new server" is selected again, the process should # complete - # - # Assumes that local CLI user has public/private keys at ~/.ssh/id_rsa.pub def boot_openstack_inception_vm say "" # glowing whitespace - public_key_path, private_key_path = local_ssh_key_paths - - # make sure we've a fog key pair - key_pair_name = Fog.respond_to?(:credential) && Fog.credential || :default - unless key_pair = fog_compute.key_pairs.get("fog_#{key_pair_name}") - say "creating key pair fog_#{key_pair_name}..." - public_key = File.open(public_key_path, 'rb') { |f| f.read } - key_pair = fog_compute.key_pairs.create( - :name => "fog_#{key_pair_name}", - :public_key => public_key - ) - end - confirm "Using key pair #{key_pair.name} for Inception VM" - unless settings["inception"] && settings["inception"]["server_id"] username = "ubuntu" say "Provisioning server for inception VM..." settings["inception"] ||= {} @@ -949,16 +971,16 @@ end end say "" confirm "Using image #{inception_image.name} for Inception VM" + key_name = settings["inception"]["key_pair"]["name"] + # Boot OpenStack server server = fog_compute.servers.create( :name => "Inception VM", - :key_name => key_pair.name, - :public_key_path => public_key_path, - :private_key_path => private_key_path, + :key_name => key_name, :flavor_ref => inception_flavor.id, :image_ref => inception_image.id, :username => username ) unless server @@ -1072,33 +1094,40 @@ end end end def display_inception_ssh_access - _, private_key_path = local_ssh_key_paths - say "SSH access: ssh -i #{private_key_path} #{settings["inception"]["username"]}@#{settings["inception"]["host"]}" + say "SSH access: ssh -i #{inception_vm_private_key_path} #{settings["inception"]["username"]}@#{settings["inception"]["host"]}" end def run_server(server_commands) server.run(server_commands) end - # Discover/create local passphrase-less SSH keys to allow - # communication with Inception VM - # - # Returns [public_key_path, private_key_path] - def local_ssh_key_paths - unless settings["local"] && settings["local"]["private_key_path"] - settings["local"] = {} - public_key_path = File.expand_path("~/.ssh/id_rsa.pub") - private_key_path = File.expand_path("~/.ssh/id_rsa") - raise "Please create public keys at ~/.ssh/id_rsa.pub or use --private-key flag" unless File.exists?(public_key_path) - - settings["local"]["public_key_path"] = public_key_path - settings["local"]["private_key_path"] = private_key_path + def inception_vm_private_key_path + unless settings["inception"] && settings["inception"]["local_private_key_path"] + settings["inception"] ||= {} + settings["inception"]["local_private_key_path"] = File.join(settings_ssh_dir, "inception") save_settings! end - [settings.local.public_key_path, settings.local.private_key_path] + settings["inception"]["local_private_key_path"] + end + + # The keys for the inception VM originate from the provider and are cached in + # the manifest. The private key is stored locally; the public key is placed + # on the inception VM. + def recreate_local_ssh_keys_for_inception_vm + unless settings["inception"] && (key_pair = settings["inception"]["key_pair"]) + raise "please run create_inception_key_pair first" + end + private_key_contents = key_pair["private_key"] + unless File.exist?(inception_vm_private_key_path) && File.read(inception_vm_private_key_path) == private_key_contents + say "Creating missing inception VM private key..." + mkdir_p(File.dirname(inception_vm_private_key_path)) + File.chmod(0700, File.dirname(inception_vm_private_key_path)) + File.open(inception_vm_private_key_path, "w") { |file| file << private_key_contents } + File.chmod(0600, inception_vm_private_key_path) + end end def aws? settings.fog_credentials.provider == "AWS" end