Module: Odysseus::CLI::SetupCommands
- Included in:
- CLI
- Defined in:
- lib/odysseus/cli/setup_commands.rb
Constant Summary collapse
- DEFAULT_IDENTITY =
The Ubuntu cloud image's own default user, so a stock image works untouched by the time --as would otherwise be needed.
'ubuntu'.freeze
Instance Method Summary collapse
-
#setup(options = {}) ⇒ Object
Prepares every host in the config so odysseus can deploy to it as a non-root user: creates the deploy user, its group, its authorized_keys and its state dir.
Instance Method Details
#setup(options = {}) ⇒ Object
Prepares every host in the config so odysseus can deploy to it as a
non-root user: creates the deploy user, its group, its
authorized_keys and its state dir. Its own command rather than a mode
of doctor, because doctor only reads; this one changes the host.
Connects as the bootstrap identity (--as, default 'ubuntu') rather than config[:user] -- that deploy user existing is the point of this command, so it cannot be assumed to exist yet, let alone be reachable, before setup has run.
The public key to install is resolved once, before any host is touched -- before even the first connection opens. A created user with no way to log in is the worst outcome this command has, and resolving first means that failure can never happen after host 1 of 3 is already changed: the whole run refuses, or none of it does.
29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 |
# File 'lib/odysseus/cli/setup_commands.rb', line 29 def setup( = {}) config_file = [:config] || 'deploy.yml' config = load_config(config_file) identity = [:as] || DEFAULT_IDENTITY # Same idiom deploy, build and pussh use: -v or --debug, either one. verbose = [:verbose] || @ui.debug? refuse_root_deploy_user!(config) keys = Odysseus::Setup::PublicKey.resolve(keys: config[:ssh][:keys], explicit: Array([:key])) @ui.header 'Odysseus Setup' @ui.info 'Service', config[:service] @ui.info 'Bootstrap identity', identity @ui.blank worst = :ok executor = Odysseus::Deployer::Executor.new(config_file) executor.host_roles.each_key do |host| @ui.section host ssh = connect_as(identity, host, config, verbose: verbose) begin escalation = Odysseus::Setup::Escalation.new(ssh: ssh, as: identity) preparer = Odysseus::Setup::Preparer.new(ssh: ssh, config: config, escalation: escalation, keys: keys) preparer.prepare.each do |result| worst = escalate(worst, result.status) render_result(result) end rescue Odysseus::SSHAuthenticationError => e # The default identity is a guess -- a good one on a stock cloud # image, wrong on anything else -- and the operator has no way to # know which flag fixes it unless this says so. The spec requires # a host with neither a usable ubuntu nor root SSH to be refused # with an error naming both flags. worst = escalate(worst, :fail) render_result( Odysseus::Setup::Preparer::Result.new( step: :connection, status: :fail, detail: "#{host}: #{e.} Setup connected as #{identity}; pass --as root " \ 'if root SSH is enabled, or --as USER for whichever identity can log in.' ) ) rescue StandardError => e # Mirrors DoctorCommands#doctor's rescue: a step failing is a # Result with status: :fail, produced by Preparer without # raising. This rescues the connection itself dying mid-run, # which can surface as IOError, Net::SSH::Disconnect (a # RuntimeError) or Errno::EPIPE/ECONNRESET (a SystemCallError) -- # three branches of StandardError with no narrower ancestor in # common. Narrower would also reintroduce the defect this fixes: # one host's failure aborting setup for every host after it. worst = escalate(worst, :fail) render_result( Odysseus::Setup::Preparer::Result.new( step: :connection, status: :fail, detail: "#{host}: #{e.class}: #{e.}" ) ) ensure ssh.close end end @ui.blank case worst when :fail then exit 1 when :warn then @ui.warn 'Setup finished, but read the warnings above.' else @ui.success 'Hosts are ready.' end rescue Odysseus::Error => e @ui.error e. exit 1 end |