Module: LeapCli::Remote::LeapPlugin

Defined in:
lib/leap_cli/remote/leap_plugin.rb

Instance Method Summary collapse

Instance Method Details

#assert_initializedObject

echos “ok” if the node has been initialized and the required packages are installed, bails out otherwise.



27
28
29
30
31
32
33
34
35
36
37
38
39
40
# File 'lib/leap_cli/remote/leap_plugin.rb', line 27

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)'"
    run "#{test_initialized_file} && #{check_required_packages} && echo ok"
  rescue Capistrano::CommandError => exc
    LeapCli::Util.bail! do
      exc.hosts.each do |host|
        node = host.to_s.split('.').first
        LeapCli::Util.log :error, "running deploy: node not initialized. Run 'leap node init #{node}'", :host => host
      end
    end
  end
end

#capture(cmd, &block) ⇒ Object

like stream, but capture all the output before returning



123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
# File 'lib/leap_cli/remote/leap_plugin.rb', line 123

def capture(cmd, &block)
  command = '%s 2>&1; echo "exitcode=$?" 2>&1;' % cmd
  host_data = {}
  run(command) do |channel, stream, data|
    host_data[channel[:host]] ||= ""
    if data =~ /exitcode=(\d+)\n/
      exitcode = $1.to_i
      data.sub!(/exitcode=(\d+)\n/,'')
      host_data[channel[:host]] += data
      yield({:host => channel[:host], :data => host_data[channel[:host]], :exitcode => exitcode})
    else
      host_data[channel[:host]] += data
    end
  end
end

#check_for_no_deployObject

bails out the deploy if the file /etc/leap/no-deploy exists. This kind of sucks, because it would be better to skip over nodes that have no-deploy set instead halting the entire deploy. As far as I know, with capistrano, there is no way to close one of the ssh connections in the pool and make sure it gets no further commands.



48
49
50
51
52
53
54
55
56
57
58
# File 'lib/leap_cli/remote/leap_plugin.rb', line 48

def check_for_no_deploy
  begin
    run "test ! -f /etc/leap/no-deploy"
  rescue Capistrano::CommandError => exc
    LeapCli::Util.bail! do
      exc.hosts.each do |host|
        LeapCli::Util.log "Can't continue because file /etc/leap/no-deploy exists", :host => host
      end
    end
  end
end

#debugObject

dumps debugging information #



63
64
65
# File 'lib/leap_cli/remote/leap_plugin.rb', line 63

def debug
  run "#{Leap::Platform.leap_dir}/bin/debug.sh"
end

#history(lines) ⇒ Object

dumps the recent deploy history to the console



70
71
72
73
# File 'lib/leap_cli/remote/leap_plugin.rb', line 70

def history(lines)
  command = "(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')"
  run command
end

#log(*args, &block) ⇒ Object



12
13
14
# File 'lib/leap_cli/remote/leap_plugin.rb', line 12

def log(*args, &block)
  LeapCli::Util::log(*args, &block)
end

#mkdirs(*dirs) ⇒ Object

creates directories that are owned by root and 700 permissions

Raises:

  • (ArgumentError)


19
20
21
22
# File 'lib/leap_cli/remote/leap_plugin.rb', line 19

def mkdirs(*dirs)
  raise ArgumentError.new('illegal dir name') if dirs.grep(/[\' ]/).any?
  run dirs.collect{|dir| "mkdir -m 700 -p #{dir}; "}.join
end

#required_packagesObject



8
9
10
# File 'lib/leap_cli/remote/leap_plugin.rb', line 8

def required_packages
  "puppet rsync lsb-release locales"
end

#run_with_progress(cmd, &block) ⇒ Object

Run a command, with a nice status report and progress indicator. Only successful results are returned, errors are printed.

For each successful run on each host, block is yielded with a hash like so:

=> ‘bluejay’, :exitcode => 0, :data => ‘shell output’



147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
# File 'lib/leap_cli/remote/leap_plugin.rb', line 147

def run_with_progress(cmd, &block)
  ssh_failures = []
  exitcode_failures = []
  succeeded = []
  task = LeapCli.log_level > 1 ? :standard_task : :skip_errors_task
  with_task(task) do
    log :querying, 'facts' do
      progress "   "
      call_on_failure do |host|
        ssh_failures << host
        progress 'F'
      end
      capture(cmd) do |response|
        if response[:exitcode] == 0
          progress '.'
          yield response
        else
          exitcode_failures << response
          progress 'F'
        end
      end
    end
  end
  puts "done"
  if ssh_failures.any?
    log :failed, 'to connect to nodes: ' + ssh_failures.join(' ')
  end
  if exitcode_failures.any?
    log :failed, 'to run successfully:' do
      exitcode_failures.each do |response|
        log "[%s] exit %s - %s" % [response[:host], response[:exitcode], response[:data].strip]
      end
    end
  end
rescue Capistrano::RemoteError => err
  log :error, err.to_s
end

#stream(cmd, &block) ⇒ Object

similar to run(cmd, &block), but with:

  • exit codes

  • stdout and stderr are combined



108
109
110
111
112
113
114
115
116
117
118
# File 'lib/leap_cli/remote/leap_plugin.rb', line 108

def stream(cmd, &block)
  command = '%s 2>&1; echo "exitcode=$?"' % cmd
  run(command) do |channel, stream, data|
    exitcode = nil
    if data =~ /exitcode=(\d+)\n/
      exitcode = $1.to_i
      data.sub!(/exitcode=(\d+)\n/,'')
    end
    yield({:host => channel[:host], :data => data, :exitcode => exitcode})
  end
end

#with_task(name) ⇒ Object

This is a hairy ugly hack, exactly the kind of stuff that makes ruby dangerous and too much fun for its own good.

In most places, we run remote ssh without a current ‘task’. This works fine, except that in a few places, the behavior of capistrano ssh is controlled by the options of the current task.

We don’t want to create an actual current task, because tasks are no fun and can’t take arguments or return values. So, when we need to configure things that can only be configured in a task, we use this handy hack to fake the current task.

This is NOT thread safe, but could be made to be so with some extra work.



90
91
92
93
94
95
96
97
98
99
100
# File 'lib/leap_cli/remote/leap_plugin.rb', line 90

def with_task(name)
  task = @config.tasks[name]
  @config.class.send(:alias_method, :original_current_task, :current_task)
  @config.class.send(:define_method, :current_task, Proc.new(){ task })
  begin
    yield
  ensure
    @config.class.send(:remove_method, :current_task)
    @config.class.send(:alias_method, :current_task, :original_current_task)
  end
end