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.



31
32
33
34
35
36
37
38
39
40
41
42
43
44
# File 'lib/leap_cli/remote/leap_plugin.rb', line 31

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.



52
53
54
55
56
57
58
59
60
61
62
# File 'lib/leap_cli/remote/leap_plugin.rb', line 52

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

#historyObject

dumps the recent deploy history to the console



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

def history
  run "(test -s /var/log/leap/deploy-summary.log && tail /var/log/leap/deploy-summary.log) || (test -s /var/log/leap/deploy-summary.log.1 && tail /var/log/leap/deploy-summary.log.1) || (echo 'no history')"
end

#log(*args, &block) ⇒ Object



16
17
18
# File 'lib/leap_cli/remote/leap_plugin.rb', line 16

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

#mark_initializedObject



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

def mark_initialized
  run "touch #{Leap::Platform.init_path}"
end

#mkdirs(*dirs) ⇒ Object

creates directories that are owned by root and 700 permissions

Raises:

  • (ArgumentError)


23
24
25
26
# File 'lib/leap_cli/remote/leap_plugin.rb', line 23

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

#required_wheezy_packagesObject



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

def required_wheezy_packages
  "puppet ruby-hiera-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