summaryrefslogtreecommitdiff
path: root/lib/leap_cli/ssh/scripts.rb
blob: 9fef6240ddbbf82227dd54abd0f21af20297ec5d (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
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
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
#
# Common commands that we would like to run on remote servers.
#
# These scripts are available via:
#
# SSH.remote_command(nodes) do |ssh, host|
#   ssh.script.custom_script_name
# end
#

module LeapCli
  module SSH
    class Scripts

      REQUIRED_PACKAGES = "puppet rsync lsb-release locales"

      attr_reader :ssh, :host
      def initialize(backend, hostname)
        @ssh = backend
        @host = hostname
      end

      #
      # creates directories that are owned by root and 700 permissions
      #
      def mkdirs(*dirs)
        raise ArgumentError.new('illegal dir name') if dirs.grep(/[\' ]/).any?
        ssh.stream dirs.collect{|dir| "mkdir -m 700 -p #{dir}; "}.join
      end

      #
      # echos "ok" if the node has been initialized and the required packages are installed, bails out otherwise.
      #
      def assert_initialized
        begin
          test_initialized_file = "test -f #{Leap::Platform.init_path}"
          check_required_packages = "! dpkg-query -W --showformat='${Status}\n' #{REQUIRED_PACKAGES} 2>&1 | grep -q -E '(deinstall|no packages)'"
          ssh.stream "#{test_initialized_file} && #{check_required_packages} && echo ok", :raise_error => true
        rescue SSH::ExecuteError
          ssh.log :error, "running deploy: node not initialized. Run `leap node init #{host}`.", :host => host
          raise # will skip further action on this node
        end
      end

      #
      # bails out the deploy if the file /etc/leap/no-deploy exists.
      #
      def check_for_no_deploy
        begin
          ssh.stream "test ! -f /etc/leap/no-deploy", :raise_error => true, :log_output => false
        rescue SSH::TimeoutError
          raise
        rescue SSH::ExecuteError
          ssh.log :warning, "can't continue because file /etc/leap/no-deploy exists", :host => host
          raise # will skip further action on this node
        end
      end

      #
      # dumps debugging information
      #
      def debug
        output = ssh.capture "#{Leap::Platform.leap_dir}/bin/debug.sh"
        ssh.log(output, :wrap => true, :host => host, :color => :cyan)
      end

      #
      # dumps the recent deploy history to the console
      #
      def history(lines)
        cmd = "(test -s /var/log/leap/deploy-summary.log && tail -n #{lines} /var/log/leap/deploy-summary.log) || (test -s /var/log/leap/deploy-summary.log.1 && tail -n #{lines} /var/log/leap/deploy-summary.log.1) || (echo 'no history')"
        history = ssh.capture(cmd, :log_output => false)
        if history
          ssh.log host, :color => :cyan, :style => :bold do
            ssh.log history, :wrap => true
          end
        end
      end

      #
      # apply puppet! weeeeeee
      #
      def puppet_apply(options)
        cmd = "#{Leap::Platform.leap_dir}/bin/puppet_command set_hostname apply #{flagize(options)}"
        ssh.stream cmd, :log_finish => true
      end

      def install_authorized_keys
        ssh.log :updating, "authorized_keys" do
          mkdirs '/root/.ssh'
          ssh.upload! LeapCli::Path.named_path(:authorized_keys), '/root/.ssh/authorized_keys', :mode => 0600
        end
      end

      #
      # for vagrant nodes, we install insecure vagrant key to authorized_keys2, since deploy
      # will overwrite authorized_keys.
      #
      # why force the insecure vagrant key?
      #   if we don't do this, then first time initialization might fail if the user has many keys
      #   (ssh will bomb out before it gets to the vagrant key).
      #   and it really doesn't make sense to ask users to pin the insecure vagrant key in their
      #   .ssh/config files.
      #
      def install_insecure_vagrant_key
        ssh.log :installing, "insecure vagrant key" do
          mkdirs '/root/.ssh'
          ssh.upload! LeapCli::Path.vagrant_ssh_pub_key_file, '/root/.ssh/authorized_keys2', :mode => 0600
        end
      end

      def install_prerequisites
        bin_dir = File.join(Leap::Platform.leap_dir, 'bin')
        node_init_path = File.join(bin_dir, 'node_init')
        ssh.log :running, "node_init script" do
          mkdirs bin_dir
          ssh.upload! LeapCli::Path.node_init_script, node_init_path, :mode => 0700
          ssh.stream node_init_path, :log_wrap => true
        end
      end

      #
      # AWS debian images only allow you to login as admin. This is done with a
      # custom command in /root/.ssh/authorized_keys, instead of by modifying
      # /etc/ssh/sshd_config.
      #
      # We need to be able to ssh as root for scp and rsync to work.
      #
      # This command is run as 'admin', with a sudo wrapper. In order for the
      # sudo to work, the command must be specified as separate arguments with
      # no spaces (that is how ssh-kit works).
      #
      def allow_root_ssh
        ssh.execute 'cp', '/home/admin/.ssh/authorized_keys', '/root/.ssh/authorized_keys'
      end

      private

      def flagize(hsh)
        hsh.inject([]) {|str, item|
          if item[1] === false
            str
          elsif item[1] === true
            str << "--" + item[0].to_s
          else
            str << "--" + item[0].to_s + " " + item[1].inspect
          end
        }.join(' ')
      end

    end
  end
end