Class: Standup::Remoting

Inherits:
Object
  • Object
show all
Defined in:
lib/standup/remoting.rb

Instance Method Summary collapse

Constructor Details

#initialize(node) ⇒ Remoting

Returns a new instance of Remoting.



6
7
8
9
10
11
12
13
# File 'lib/standup/remoting.rb', line 6

def initialize node
  @node = node
  @host = @node.instance.external_ip
  @keypair_file = Settings.aws.keypair_file
  @user = @node.scripts.ec2.params.ssh_user
  @ssh = nil
  @context = {}
end

Instance Method Details

#as_user(user, &block) ⇒ Object



70
71
72
# File 'lib/standup/remoting.rb', line 70

def as_user user, &block
  with_context(:user => user, &block)
end

#closeObject



199
200
201
202
# File 'lib/standup/remoting.rb', line 199

def close
  @ssh.shutdown! if @ssh
  @ssh = nil
end

#download(*files) ⇒ Object



15
16
17
18
# File 'lib/standup/remoting.rb', line 15

def download *files
  options = files.pop
  rsync wrap_to_remote(files), options[:to], options[:sudo]
end

#exec(command, context = nil, timeout_sec = nil) ⇒ Object



132
133
134
# File 'lib/standup/remoting.rb', line 132

def exec command, context = nil, timeout_sec = nil
  with_context(context) { raw_exec(remote_command(command), timeout_sec) }
end

#file_exists?(path) ⇒ Boolean

Returns:

  • (Boolean)


160
161
162
# File 'lib/standup/remoting.rb', line 160

def file_exists? path
  exec("if [ -e #{path} ]; then echo 'true'; fi") =~ /true/
end

#in_dir(path, &block) ⇒ Object

Raises:

  • (ArgumentError)


63
64
65
66
67
68
# File 'lib/standup/remoting.rb', line 63

def in_dir path, &block
  raise ArgumentError, 'Only absolute paths allowed' unless path[0,1] == '/'
  with_context(:path => path) do |context|
    block.call(path, context)
  end
end

#in_temp_dir(&block) ⇒ Object



147
148
149
150
151
152
153
154
155
156
157
158
# File 'lib/standup/remoting.rb', line 147

def in_temp_dir &block
  tmp_dirname = "/tmp/standup_tmp_#{rand 10000}"
  exec "mkdir -m 777 #{tmp_dirname}"
  
  result = in_dir tmp_dirname do
    block.call tmp_dirname
  end
  
  sudo "rm -rf #{tmp_dirname}"
  
  result
end

#install_gem(name, version = nil) ⇒ Object



170
171
172
173
174
175
176
177
178
179
180
181
# File 'lib/standup/remoting.rb', line 170

def install_gem name, version = nil
  if version
    unless exec("gem list | grep #{name}").try(:[], version)
      sudo "gem install #{name} -v #{version} --no-ri --no-rdoc"
      return true
    end
  else
    sudo "gem install #{name} --no-ri --no-rdoc"
    return true
  end
  false
end

#install_packages(packages, opts = {}) ⇒ Object Also known as: install_package



164
165
166
167
# File 'lib/standup/remoting.rb', line 164

def install_packages packages, opts = {}
  input = opts[:input] ? "echo \"#{opts[:input].join("\n")}\" | sudo " : ''
  sudo "#{input}apt-get -qqy install #{packages}"
end

#raw_exec(command, timeout_sec = nil) ⇒ Object



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
# File 'lib/standup/remoting.rb', line 78

def raw_exec command, timeout_sec = nil
  bright_p command

  result = ''

  collect_output = lambda do |data|
    result << data
    print(data)
    STDOUT.flush
  end

  main_channel = ssh.open_channel do |ch|
    ch.exec("bash -l") do |ch2, _|
      ch2.on_data { |_, data| collect_output.call(data) }

      ch2.on_extended_data { |_, _, data| collect_output.call(data) }

      ch2.send_data "export TERM=vt100\n"

      ch2.send_data "#{command}\n"

      ch2.send_data "exit\n"
    end
  end

  if timeout_sec
    begin
      timeout(timeout_sec) { main_channel.wait }
    rescue Timeout::Error
      puts "Timeout of #{timeout_sec} happens, connection is closing."
    end
  else
    main_channel.wait
  end

  main_channel.close

  result
end

#remote_command(command) ⇒ Object



118
119
120
121
122
123
124
125
126
127
128
129
130
# File 'lib/standup/remoting.rb', line 118

def remote_command command
  command = "#{@context[:prefix].strip} #{command}" if @context[:prefix].present?
  command = "cd #{@context[:path]} && #{command}"   if @context[:path].present?
  command = "bash -c \"#{command.gsub(/"/, '\"')}\""

  if @context[:sudo]
    command = "sudo #{command}"
  elsif @context[:user].present?
    command = "sudo -u #{@context[:user]} #{command}"
  end

  command
end

#remote_update(file, body, opts = {}) ⇒ Object



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
# File 'lib/standup/remoting.rb', line 25

def remote_update file, body, opts = {}
  tmpfile = Tempfile.new('file')

  success = download file,
                     :to => tmpfile.path,
                     :sudo => opts[:sudo]
  unless success 
    bright_p 'error during file upload'
    return
  end
  
  opts[:delimiter] ||= '# standup remote_update fragment'
  
  initial = File.read(tmpfile.path)
  
  to_change = "#{opts[:delimiter]}\n#{body}#{opts[:delimiter]}\n"
  pattern = /#{opts[:delimiter]}.*#{opts[:delimiter]}\n?/m
  
  changed = if initial.match pattern
    initial.gsub pattern, to_change
  else
    "#{initial}\n#{to_change}"
  end
  
  File.open(tmpfile.path, 'w') {|f| f.write changed}
  
  upload tmpfile.path,
         :to => file,
         :sudo => opts[:sudo]
end

#su_exec(user, command) ⇒ Object



141
142
143
144
145
# File 'lib/standup/remoting.rb', line 141

def su_exec user, command
  as_user user do
    exec command
  end
end

#sudo(command = nil, &block) ⇒ Object



136
137
138
139
# File 'lib/standup/remoting.rb', line 136

def sudo command = nil, &block
  block = Proc.new { exec command } unless block_given?
  with_context(:sudo => true, &block)
end

#update_cron(schedule, commands, opts = {}) ⇒ Object

Raises:

  • (ArgumentError)


183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
# File 'lib/standup/remoting.rb', line 183

def update_cron schedule, commands, opts = {}
  raise ArgumentError, ":section option is required" unless opts[:section]
  user = opts[:user] || @node.scripts.ec2.params.ssh_user
  commands = commands.strip.split("\n") if commands.is_a? String
  commands = commands.map(&:strip).join(' && ').gsub(/%/, '\%')

  in_temp_dir do |dir|
    sudo "crontab -l -u #{user} > crontab.txt"
    remote_update "#{dir}/crontab.txt",
                  "#{schedule} (date && #{commands}) >> /var/log/cron.log 2>&1\n",
                  :delimiter => "# standup update_cron: #{opts[:section]}",
                  :sudo => true
    sudo "crontab -u #{user} - < crontab.txt"
  end
end

#upload(*files) ⇒ Object



20
21
22
23
# File 'lib/standup/remoting.rb', line 20

def upload *files
  options = files.pop
  rsync files, wrap_to_remote(options[:to]), options[:sudo]
end

#with_context(new_context, replace = false) ⇒ Object



56
57
58
59
60
61
# File 'lib/standup/remoting.rb', line 56

def with_context new_context, replace = false
  new_context ||= {}
  old_context = @context.dup
  @context = replace ? new_context : @context.merge(new_context).merge(:prefix => "#{old_context[:prefix]} #{new_context[:prefix]}")
  yield(@context).tap { @context = old_context }
end

#with_prefix(prefix, &block) ⇒ Object



74
75
76
# File 'lib/standup/remoting.rb', line 74

def with_prefix prefix, &block
  with_context(:prefix => prefix, &block)
end