spec/tunnel_spec.rb (215 lines of code) (raw):
# vim: syntax=ruby:expandtab:shiftwidth=2:softtabstop=2:tabstop=2
# Copyright 2020-present Facebook
#
# Licensed under the Apache License, Version 2.0 (the 'License');
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an 'AS IS' BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
require 'logger'
require 'base64'
require 'between_meals/util'
require_relative '../lib/taste_tester/logging'
require_relative '../lib/taste_tester/exceptions'
require_relative '../lib/taste_tester/ssh_util'
require_relative '../lib/taste_tester/tunnel'
require_relative '../lib/taste_tester/config'
require_relative '../lib/taste_tester/server'
describe TasteTester::Tunnel do
let(:logger) do
Logger.new('/dev/null')
end
let(:config_hash) do
TasteTester::Config.save(true)
end
before do
TasteTester::Config.restore(config_hash)
end
before do
allow_any_instance_of(
TasteTester::Server,
).to receive(:port).and_return(1234)
end
let(:mock_server) do
TasteTester::Server.new
end
let(:tt_tunnel) do
TasteTester::Tunnel.new('mock_host', mock_server)
end
before do
# THIS IS VERY IMPORTANT!!! If we don't mock **every** call to
# Mixlibh::ShellOut we can nuke the data on any machine that runs the unit
# tests so we mock it to return BS, this ensures if we miss mocking a call
# the tests fail rather than nuking the host
allow_any_instance_of(Mixlib::ShellOut).to receive(:run_command).
and_return(nil)
end
let(:mock_so) do
double('shellout')
end
context 'test base configs' do
it 'test build tunnel command' do
expect(tt_tunnel.cmd).to include(
'ssh ' +
'-T ' +
'-o BatchMode=yes ' +
'-o UserKnownHostsFile=/dev/null ' +
'-o StrictHostKeyChecking=no ' +
'-o ConnectTimeout=5 ' +
'-o ServerAliveInterval=10 ' +
'-o ServerAliveCountMax=6 ' +
'-f ' +
'-R 4001:localhost:1234 ' +
'root@mock_host',
)
expect(tt_tunnel.cmd).to include(
'| base64 --decode | bash -x',
)
end
after do
TasteTester::Config.restore(config_hash)
end
end
context 'test custom configs linux - vanilla ssh command' do
before do
TasteTester::Config.ssh_connect_timeout 10
TasteTester::Config.jumps 'mock_jump_user@mock_jump_host'
TasteTester::Config.ssh_command 'mock_ssh_command'
TasteTester::Config.user 'rossi'
end
it 'test build tunnel command' do
expect(tt_tunnel.cmd).to include(
'mock_ssh_command ' +
'-J mock_jump_user@mock_jump_host ' +
'-T ' +
'-o BatchMode=yes ' +
'-o UserKnownHostsFile=/dev/null ' +
'-o StrictHostKeyChecking=no ' +
'-o ConnectTimeout=10 ' +
'-o ServerAliveInterval=10 ' +
'-o ServerAliveCountMax=6 ' +
'-f ' +
'-R 4001:localhost:1234 ' +
'rossi@mock_host',
)
expect(tt_tunnel.cmd).to include(
'| base64 --decode | sudo bash -x',
)
end
after do
TasteTester::Config.restore(config_hash)
end
end
context 'test custom configs linux - generated ssh command' do
let(:mock_generated_cmd) do
'mock_ssh_cmd_bin -o option1 -o option2 mock_user@mock_host'
end
before do
TasteTester::Config.ssh_connect_timeout 10
TasteTester::Config.jumps 'mock_jump_user@mock_jump_host'
TasteTester::Config.user 'rossi'
TasteTester::Config.ssh_cmd_gen_template 'mock_generator_cmd ' +
'mock_arg1 mock_arg2 %{jumps} %{host} --user %{user} --get-command'
TasteTester::Config.use_ssh_tunnels true
allow(mock_so).to receive(:run_command).and_return(mock_so)
allow(mock_so).to receive(:error?).and_return(false)
allow(mock_so).to receive(:error!).and_return(mock_generated_cmd)
allow(mock_so).to receive(:stderr).and_return('')
allow(mock_so).to receive(:stdout).and_return(mock_generated_cmd)
end
it 'test ssh generated command' do
expect(tt_tunnel.ssh_cmd_generator).to eq(
'mock_generator_cmd mock_arg1 mock_arg2 ' +
'-J mock_jump_user@mock_jump_host mock_host ' +
'--user rossi --get-command',
)
end
it 'test ssh base command' do
allow(Mixlib::ShellOut).to receive(:new).and_return(mock_so)
expect(tt_tunnel.ssh_base_cmd).to eq(
"#{mock_generated_cmd} " +
'-f -R 4001:localhost:1234',
)
end
it 'test ssh base command exception' do
error_message = 'command failure message'
allow(mock_so).to receive(:error!).and_raise(
Mixlib::ShellOut::ShellCommandFailed, error_message
)
allow(Mixlib::ShellOut).to receive(:new).and_return(mock_so)
allow(tt_tunnel).to receive(:exit).and_raise(StandardError)
regex_msg = error_message
expect(
TasteTester::Logging.logger,
).to receive(
:error,
).with(
/#{regex_msg}/m,
)
regex_msg = 'mock_generator_cmd.*failed during execution'
expect(
TasteTester::Logging.logger,
).to receive(
:error,
).with(
/#{regex_msg}/m,
)
expect { tt_tunnel.ssh_base_cmd }.to raise_error(
StandardError,
)
end
it 'test build ssh command' do
expect(
tt_tunnel.build_ssh_cmd(
mock_generated_cmd,
['cmd1', 'cmd2'],
),
).to eq(
"#{mock_generated_cmd} " +
"\"echo 'Y21kMSAmJiBjbWQy' | base64 --decode | sudo bash -x\"",
)
end
it 'test ssh exception message' do
allow(Mixlib::ShellOut).to receive(:new).and_return(mock_so)
expect(tt_tunnel.ssh_base_cmd).to eq(
"#{mock_generated_cmd} " +
'-f -R 4001:localhost:1234',
)
regex_msg = 'SSH returned error ' +
"while connecting to mock_host.*#{mock_generated_cmd}" +
".*The above command was generated.*#{tt_tunnel.ssh_cmd_generator}"
expect(
TasteTester::Logging.logger,
).to receive(
:error,
).with(
/#{regex_msg}/m,
)
expect { tt_tunnel.error! }.to raise_error(
TasteTester::Exceptions::SshError,
)
end
after do
TasteTester::Config.restore(config_hash)
end
end
context 'test custom configs windows' do
before do
TasteTester::Config.ssh_connect_timeout 10
TasteTester::Config.jumps 'mock_jump_user@mock_jump_host'
TasteTester::Config.ssh_command 'mock_ssh_command'
TasteTester::Config.user 'rossi'
TasteTester::Config.windows_target true
end
it 'test build tunnel command' do
expect(tt_tunnel.cmd).to include(
'mock_ssh_command ' +
'-J mock_jump_user@mock_jump_host ' +
'-T ' +
'-o BatchMode=yes ' +
'-o UserKnownHostsFile=/dev/null ' +
'-o StrictHostKeyChecking=no ' +
'-o ConnectTimeout=10 ' +
'-o ServerAliveInterval=10 ' +
'-o ServerAliveCountMax=6 ' +
'-f ' +
'-R 4001:localhost:1234 ' +
'rossi@mock_host',
)
expect(tt_tunnel.cmd).to include(
'powershell.exe -c -; exit $LASTEXITCODE',
)
end
after do
TasteTester::Config.restore(config_hash)
end
end
end