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

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 @@ def delete_ssh_key
known_hosts_file.close(true)
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
attr_reader :uri, :username, :password, :known_hosts_file, :private_key_file
......@@ -117,8 +139,8 @@ def ssh_key_set?
!private_key_file.nil?
end
def run(command_str)
command = [env_vars, command_str, '2>&1'].compact.join(' ')
def run(command_str, *extra_env)
command = [env_vars, *extra_env, command_str, '2>&1'].compact.join(' ')
Runtime::Logger.debug "Git: command=[#{command}]"
output, _ = Open3.capture2(command)
......
......@@ -7,6 +7,16 @@ module Env
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?
enabled?(ENV['QA_DEBUG'], default: false)
end
......@@ -104,6 +114,15 @@ def require_github_access_token!
raise ArgumentError, "Please provide GITHUB_ACCESS_TOKEN"
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
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 @@ def perform
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(DEFAULT_TEST_PATH_ARGS) unless options.any? { |opt| opt =~ %r{/features/} }
......
......@@ -26,6 +26,36 @@
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
tmp_dir = 'tmp/git-repository-spec/'
FileUtils.rm_rf(tmp_dir) if ::File.exist?(tmp_dir)
......
......@@ -3,49 +3,62 @@
describe QA::Runtime::Env do
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
it 'returns false when falsey values specified' do
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')
expect(described_class.public_send(method)).to be_falsey
expect(described_class.public_send(method, *param)).to be_falsey
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
it 'returns true when anything else specified' do
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')
expect(described_class.public_send(method)).to be_truthy
expect(described_class.public_send(method, *param)).to be_truthy
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
context 'when there is no env variable set' do
it "returns the default, #{default}" do
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
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
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
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
describe '.running_in_ci?' do
......@@ -182,4 +195,16 @@
expect(described_class.log_destination).to eq('path/to_file')
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
......@@ -76,6 +76,20 @@
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)
expect(RSpec::Core::Runner).to receive(:run)
.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