Commit 765ca40d authored by Mark Lapierre's avatar Mark Lapierre

Add e2e test of push over SSH over Git protocol v2

Adds a new end-to-end test to check that Git protocol v2 can be used to
push over SSH.

Includes a change in Git::Repository to use Runtime::Env.debug? to
enable logging instead of .verbose?
parent 912741cf
...@@ -109,6 +109,28 @@ module QA ...@@ -109,6 +109,28 @@ module QA
known_hosts_file.close(true) known_hosts_file.close(true)
end end
def push_with_git_protocol(version, file_name, file_content, commit_message = 'Initial commit')
self.git_protocol = version
add_file(file_name, file_content)
commit(commit_message)
push_changes
fetch_supported_git_protocol
end
def git_protocol=(value)
raise ArgumentError, "Please specify the protocol you would like to use: 0, 1, or 2" unless %w[0 1 2].include?(value.to_s)
run("git config protocol.version #{value}")
end
def fetch_supported_git_protocol
# ls-remote is one command known to respond to Git protocol v2 so we use
# it to get output including the version reported via Git tracing
output = run("git ls-remote #{uri}", "GIT_TRACE_PACKET=1")
output[/git< version (\d+)/, 1] || 'unknown'
end
private private
attr_reader :uri, :username, :password, :known_hosts_file, :private_key_file attr_reader :uri, :username, :password, :known_hosts_file, :private_key_file
...@@ -117,8 +139,8 @@ module QA ...@@ -117,8 +139,8 @@ module QA
!private_key_file.nil? !private_key_file.nil?
end end
def run(command_str) def run(command_str, *extra_env)
command = [env_vars, command_str, '2>&1'].compact.join(' ') command = [env_vars, *extra_env, command_str, '2>&1'].compact.join(' ')
Runtime::Logger.debug "Git: command=[#{command}]" Runtime::Logger.debug "Git: command=[#{command}]"
output, _ = Open3.capture2(command) output, _ = Open3.capture2(command)
......
...@@ -7,6 +7,16 @@ module QA ...@@ -7,6 +7,16 @@ module QA
attr_writer :personal_access_token attr_writer :personal_access_token
# The environment variables used to indicate if the environment under test
# supports the given feature
SUPPORTED_FEATURES = {
git_protocol_v2: 'QA_CAN_TEST_GIT_PROTOCOL_V2'
}.freeze
def supported_features
SUPPORTED_FEATURES
end
def debug? def debug?
enabled?(ENV['QA_DEBUG'], default: false) enabled?(ENV['QA_DEBUG'], default: false)
end end
...@@ -104,6 +114,15 @@ module QA ...@@ -104,6 +114,15 @@ module QA
raise ArgumentError, "Please provide GITHUB_ACCESS_TOKEN" raise ArgumentError, "Please provide GITHUB_ACCESS_TOKEN"
end end
# Returns true if there is an environment variable that indicates that
# the feature is supported in the environment under test.
# All features are supported by default.
def can_test?(feature)
raise ArgumentError, %Q(Unknown feature "#{feature}") unless SUPPORTED_FEATURES.include? feature
enabled?(ENV[SUPPORTED_FEATURES[feature]], default: true)
end
private private
def enabled?(value, default: true) def enabled?(value, default: true)
......
# frozen_string_literal: true
module QA
context 'Create' do
describe 'Push over SSH using Git protocol version 2', :requires_git_protocol_v2 do
# Note: If you run this test against GDK make sure you've enabled sshd and
# enabled setting the Git protocol by adding `AcceptEnv GIT_PROTOCOL` to
# `sshd_config`
# See: https://gitlab.com/gitlab-org/gitlab-qa/blob/master/docs/run_qa_against_gdk.md
let(:key_title) { "key for ssh tests #{Time.now.to_f}" }
let(:ssh_key) do
Factory::Resource::SSHKey.fabricate! do |resource|
resource.title = key_title
end
end
def login
Runtime::Browser.visit(:gitlab, Page::Main::Login)
Page::Main::Login.perform(&:sign_in_using_credentials)
end
around do |example|
# Create an SSH key to be used with Git
login
ssh_key
example.run
# Remove the SSH key
login
Page::Main::Menu.perform(&:go_to_profile_settings)
Page::Profile::Menu.perform(&:click_ssh_keys)
Page::Profile::SSHKeys.perform do |ssh_keys|
ssh_keys.remove_key(key_title)
end
end
it 'user pushes to the repository' do
# Create a project to push to
project = Factory::Resource::Project.fabricate! do |project|
project.name = 'git-protocol-project'
end
file_name = 'README.md'
file_content = 'Test Git protocol v2'
git_protocol = '2'
git_protocol_reported = nil
# Use Git to clone the project, push a file to it, and then check the
# supported Git protocol
Git::Repository.perform do |repository|
username = 'GitLab QA'
email = 'root@gitlab.com'
repository.uri = project.repository_ssh_location.uri
begin
repository.use_ssh_key(ssh_key)
repository.clone
repository.configure_identity(username, email)
git_protocol_reported = repository.push_with_git_protocol(
git_protocol,
file_name,
file_content)
ensure
repository.delete_ssh_key
end
end
project.visit!
Page::Project::Show.perform(&:wait_for_push)
# Check that the push worked
expect(page).to have_content(file_name)
expect(page).to have_content(file_content)
# And check that the correct Git protocol was used
expect(git_protocol_reported).to eq(git_protocol)
end
end
end
end
...@@ -25,6 +25,10 @@ module QA ...@@ -25,6 +25,10 @@ module QA
args.push(%w[--tag ~skip_signup_disabled]) if QA::Runtime::Env.signup_disabled? args.push(%w[--tag ~skip_signup_disabled]) if QA::Runtime::Env.signup_disabled?
QA::Runtime::Env.supported_features.each_key do |key|
args.push(["--tag", "~requires_#{key}"]) unless QA::Runtime::Env.can_test? key
end
args.push(options) args.push(options)
args.push(DEFAULT_TEST_PATH_ARGS) unless options.any? { |opt| opt =~ %r{/features/} } args.push(DEFAULT_TEST_PATH_ARGS) unless options.any? { |opt| opt =~ %r{/features/} }
......
...@@ -26,6 +26,36 @@ describe QA::Git::Repository do ...@@ -26,6 +26,36 @@ describe QA::Git::Repository do
end end
end end
describe '#git_protocol=' do
[0, 1, 2].each do |version|
it "configures git to use protocol version #{version}" do
expect(repository).to receive(:run).with("git config protocol.version #{version}")
repository.git_protocol = version
end
end
it 'raises an error if the version is unsupported' do
expect { repository.git_protocol = 'foo' }.to raise_error(ArgumentError, "Please specify the protocol you would like to use: 0, 1, or 2")
end
end
describe '#fetch_supported_git_protocol' do
it "reports the detected version" do
expect(repository).to receive(:run).and_return("packet: git< version 2")
expect(repository.fetch_supported_git_protocol).to eq('2')
end
it 'reports unknown if version is unknown' do
expect(repository).to receive(:run).and_return("packet: git< version -1")
expect(repository.fetch_supported_git_protocol).to eq('unknown')
end
it 'reports unknown if content does not identify a version' do
expect(repository).to receive(:run).and_return("foo")
expect(repository.fetch_supported_git_protocol).to eq('unknown')
end
end
def cd_empty_temp_directory def cd_empty_temp_directory
tmp_dir = 'tmp/git-repository-spec/' tmp_dir = 'tmp/git-repository-spec/'
FileUtils.rm_rf(tmp_dir) if ::File.exist?(tmp_dir) FileUtils.rm_rf(tmp_dir) if ::File.exist?(tmp_dir)
......
...@@ -3,49 +3,62 @@ ...@@ -3,49 +3,62 @@
describe QA::Runtime::Env do describe QA::Runtime::Env do
include Support::StubENV include Support::StubENV
shared_examples 'boolean method' do |method, env_key, default| shared_examples 'boolean method' do |**kwargs|
it_behaves_like 'boolean method with parameter', kwargs
end
shared_examples 'boolean method with parameter' do |method:, param: nil, env_key:, default:|
context 'when there is an env variable set' do context 'when there is an env variable set' do
it 'returns false when falsey values specified' do it 'returns false when falsey values specified' do
stub_env(env_key, 'false') stub_env(env_key, 'false')
expect(described_class.public_send(method)).to be_falsey expect(described_class.public_send(method, *param)).to be_falsey
stub_env(env_key, 'no') stub_env(env_key, 'no')
expect(described_class.public_send(method)).to be_falsey expect(described_class.public_send(method, *param)).to be_falsey
stub_env(env_key, '0') stub_env(env_key, '0')
expect(described_class.public_send(method)).to be_falsey expect(described_class.public_send(method, *param)).to be_falsey
end end
it 'returns true when anything else specified' do it 'returns true when anything else specified' do
stub_env(env_key, 'true') stub_env(env_key, 'true')
expect(described_class.public_send(method)).to be_truthy expect(described_class.public_send(method, *param)).to be_truthy
stub_env(env_key, '1') stub_env(env_key, '1')
expect(described_class.public_send(method)).to be_truthy expect(described_class.public_send(method, *param)).to be_truthy
stub_env(env_key, 'anything') stub_env(env_key, 'anything')
expect(described_class.public_send(method)).to be_truthy expect(described_class.public_send(method, *param)).to be_truthy
end end
end end
context 'when there is no env variable set' do context 'when there is no env variable set' do
it "returns the default, #{default}" do it "returns the default, #{default}" do
stub_env(env_key, nil) stub_env(env_key, nil)
expect(described_class.public_send(method)).to be(default) expect(described_class.public_send(method, *param)).to be(default)
end end
end end
end end
describe '.signup_disabled?' do describe '.signup_disabled?' do
it_behaves_like 'boolean method', :signup_disabled?, 'SIGNUP_DISABLED', false it_behaves_like 'boolean method',
method: :signup_disabled?,
env_key: 'SIGNUP_DISABLED',
default: false
end end
describe '.debug?' do describe '.debug?' do
it_behaves_like 'boolean method', :debug?, 'QA_DEBUG', false it_behaves_like 'boolean method',
method: :debug?,
env_key: 'QA_DEBUG',
default: false
end end
describe '.chrome_headless?' do describe '.chrome_headless?' do
it_behaves_like 'boolean method', :chrome_headless?, 'CHROME_HEADLESS', true it_behaves_like 'boolean method',
method: :chrome_headless?,
env_key: 'CHROME_HEADLESS',
default: true
end end
describe '.running_in_ci?' do describe '.running_in_ci?' do
...@@ -182,4 +195,16 @@ describe QA::Runtime::Env do ...@@ -182,4 +195,16 @@ describe QA::Runtime::Env do
expect(described_class.log_destination).to eq('path/to_file') expect(described_class.log_destination).to eq('path/to_file')
end end
end end
describe '.can_test?' do
it_behaves_like 'boolean method with parameter',
method: :can_test?,
param: :git_protocol_v2,
env_key: 'QA_CAN_TEST_GIT_PROTOCOL_V2',
default: true
it 'raises ArgumentError if feature is unknown' do
expect { described_class.can_test? :foo }.to raise_error(ArgumentError, 'Unknown feature "foo"')
end
end
end end
...@@ -76,6 +76,20 @@ describe QA::Specs::Runner do ...@@ -76,6 +76,20 @@ describe QA::Specs::Runner do
end end
end end
context 'when git protocol v2 is not supported' do
before do
allow(QA::Runtime::Env).to receive(:can_test?).with(:git_protocol_v2).and_return(false)
end
subject { described_class.new }
it 'it includes default args and excludes the requires_git_protocol_v2 tag' do
expect_rspec_runner_arguments(['--tag', '~orchestrated', '--tag', '~requires_git_protocol_v2', *described_class::DEFAULT_TEST_PATH_ARGS])
subject.perform
end
end
def expect_rspec_runner_arguments(arguments) def expect_rspec_runner_arguments(arguments)
expect(RSpec::Core::Runner).to receive(:run) expect(RSpec::Core::Runner).to receive(:run)
.with(arguments, $stderr, $stdout) .with(arguments, $stderr, $stdout)
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment