diff options
Diffstat (limited to 'qa')
38 files changed, 557 insertions, 138 deletions
diff --git a/qa/Dockerfile b/qa/Dockerfile index ed2ee73bea0..77cee9c5461 100644 --- a/qa/Dockerfile +++ b/qa/Dockerfile @@ -1,4 +1,4 @@ -FROM ruby:2.4 +FROM ruby:2.4-stretch LABEL maintainer "Grzegorz Bizon <grzegorz@gitlab.com>" ENV DEBIAN_FRONTEND noninteractive diff --git a/qa/Gemfile b/qa/Gemfile index c3e61568f3d..d69c71003ae 100644 --- a/qa/Gemfile +++ b/qa/Gemfile @@ -6,5 +6,4 @@ gem 'capybara-screenshot', '~> 1.0.18' gem 'rake', '~> 12.3.0' gem 'rspec', '~> 3.7' gem 'selenium-webdriver', '~> 3.8.0' -gem 'net-ssh', require: false gem 'airborne', '~> 0.2.13' diff --git a/qa/Gemfile.lock b/qa/Gemfile.lock index 51d2e4d7a10..1bc424335f8 100644 --- a/qa/Gemfile.lock +++ b/qa/Gemfile.lock @@ -46,9 +46,8 @@ GEM mini_mime (1.0.0) mini_portile2 (2.3.0) minitest (5.11.1) - net-ssh (4.1.0) netrc (0.11.0) - nokogiri (1.8.1) + nokogiri (1.8.2) mini_portile2 (~> 2.3.0) pry (0.11.3) coderay (~> 1.1.0) @@ -98,7 +97,6 @@ DEPENDENCIES airborne (~> 0.2.13) capybara (~> 2.16.1) capybara-screenshot (~> 1.0.18) - net-ssh pry-byebug (~> 3.5.1) rake (~> 12.3.0) rspec (~> 3.7) @@ -11,9 +11,15 @@ module QA autoload :Scenario, 'qa/runtime/scenario' autoload :Browser, 'qa/runtime/browser' autoload :Env, 'qa/runtime/env' - autoload :RSAKey, 'qa/runtime/rsa_key' autoload :Address, 'qa/runtime/address' autoload :API, 'qa/runtime/api' + + module Key + autoload :Base, 'qa/runtime/key/base' + autoload :RSA, 'qa/runtime/key/rsa' + autoload :ECDSA, 'qa/runtime/key/ecdsa' + autoload :ED25519, 'qa/runtime/key/ed25519' + end end ## @@ -31,6 +37,7 @@ module QA autoload :Project, 'qa/factory/resource/project' autoload :MergeRequest, 'qa/factory/resource/merge_request' autoload :DeployKey, 'qa/factory/resource/deploy_key' + autoload :Branch, 'qa/factory/resource/branch' autoload :SecretVariable, 'qa/factory/resource/secret_variable' autoload :Runner, 'qa/factory/resource/runner' autoload :PersonalAccessToken, 'qa/factory/resource/personal_access_token' @@ -132,6 +139,7 @@ module QA autoload :Repository, 'qa/page/project/settings/repository' autoload :CICD, 'qa/page/project/settings/ci_cd' autoload :DeployKeys, 'qa/page/project/settings/deploy_keys' + autoload :ProtectedBranches, 'qa/page/project/settings/protected_branches' autoload :SecretVariables, 'qa/page/project/settings/secret_variables' autoload :Runners, 'qa/page/project/settings/runners' autoload :MergeRequest, 'qa/page/project/settings/merge_request' diff --git a/qa/qa/factory/base.rb b/qa/qa/factory/base.rb index afaa96b4541..7a532ce534b 100644 --- a/qa/qa/factory/base.rb +++ b/qa/qa/factory/base.rb @@ -22,7 +22,7 @@ module QA factory.fabricate!(*args) - return Factory::Product.populate!(factory) + break Factory::Product.populate!(factory) end end diff --git a/qa/qa/factory/repository/push.rb b/qa/qa/factory/repository/push.rb index 6e8905cde78..795f1f9cb1a 100644 --- a/qa/qa/factory/repository/push.rb +++ b/qa/qa/factory/repository/push.rb @@ -2,7 +2,10 @@ module QA module Factory module Repository class Push < Factory::Base - attr_writer :file_name, :file_content, :commit_message, :branch_name, :new_branch + attr_accessor :file_name, :file_content, :commit_message, + :branch_name, :new_branch + + attr_writer :remote_branch dependency Factory::Resource::Project, as: :project do |project| project.name = 'project-with-code' @@ -17,23 +20,32 @@ module QA @new_branch = true end + def remote_branch + @remote_branch ||= branch_name + end + def fabricate! project.visit! Git::Repository.perform do |repository| - repository.location = Page::Project::Show.act do + repository.uri = Page::Project::Show.act do choose_repository_clone_http - repository_location + repository_location.uri end repository.use_default_credentials repository.clone repository.configure_identity('GitLab QA', 'root@gitlab.com') - repository.checkout(@branch_name) unless @new_branch - repository.add_file(@file_name, @file_content) - repository.commit(@commit_message) - repository.push_changes(@branch_name) + if new_branch + repository.checkout_new_branch(branch_name) + else + repository.checkout(branch_name) + end + + repository.add_file(file_name, file_content) + repository.commit(commit_message) + repository.push_changes("#{branch_name}:#{remote_branch}") end end end diff --git a/qa/qa/factory/resource/branch.rb b/qa/qa/factory/resource/branch.rb new file mode 100644 index 00000000000..1785441f5a8 --- /dev/null +++ b/qa/qa/factory/resource/branch.rb @@ -0,0 +1,92 @@ +module QA + module Factory + module Resource + class Branch < Factory::Base + attr_accessor :project, :branch_name, + :allow_to_push, :allow_to_merge, :protected + + dependency Factory::Resource::Project, as: :project do |project| + project.name = 'protected-branch-project' + end + + product :name do + Page::Project::Settings::Repository.act do + expand_protected_branches(&:last_branch_name) + end + end + + product :push_allowance do + Page::Project::Settings::Repository.act do + expand_protected_branches(&:last_push_allowance) + end + end + + def initialize + @branch_name = 'test/branch' + @allow_to_push = true + @allow_to_merge = true + @protected = false + end + + def fabricate! + project.visit! + + Factory::Repository::Push.fabricate! do |resource| + resource.project = project + resource.file_name = 'kick-off.txt' + resource.commit_message = 'First commit' + end + + branch = Factory::Repository::Push.fabricate! do |resource| + resource.project = project + resource.file_name = 'README.md' + resource.commit_message = 'Add readme' + resource.branch_name = 'master' + resource.new_branch = false + resource.remote_branch = @branch_name + end + + Page::Project::Show.act { wait_for_push } + + # The upcoming process will make it access the Protected Branches page, + # select the already created branch and protect it according + # to `allow_to_push` variable. + return branch unless @protected + + Page::Menu::Side.act do + click_repository_settings + end + + Page::Project::Settings::Repository.perform do |setting| + setting.expand_protected_branches do |page| + page.select_branch(branch_name) + + if allow_to_push + page.allow_devs_and_masters_to_push + else + page.allow_no_one_to_push + end + + if allow_to_merge + page.allow_devs_and_masters_to_merge + else + page.allow_no_one_to_merge + end + + page.wait(reload: false) do + !page.first('.btn-create').disabled? + end + + page.protect_branch + + # Wait for page load, which resets the expanded sections + page.wait(reload: false) do + !page.has_content?('Collapse') + end + end + end + end + end + end + end +end diff --git a/qa/qa/factory/resource/deploy_key.rb b/qa/qa/factory/resource/deploy_key.rb index ff0b4a46b77..ea8a3ad687d 100644 --- a/qa/qa/factory/resource/deploy_key.rb +++ b/qa/qa/factory/resource/deploy_key.rb @@ -4,15 +4,15 @@ module QA class DeployKey < Factory::Base attr_accessor :title, :key - product :title do + product :fingerprint do |resource| Page::Project::Settings::Repository.act do - expand_deploy_keys(&:key_title) - end - end + expand_deploy_keys do |key| + key_offset = key.key_titles.index do |title| + title.text == resource.title + end - product :fingerprint do - Page::Project::Settings::Repository.act do - expand_deploy_keys(&:key_fingerprint) + key.key_fingerprints[key_offset].text + end end end diff --git a/qa/qa/factory/resource/merge_request.rb b/qa/qa/factory/resource/merge_request.rb index 539fe6b8a70..7588ac5735d 100644 --- a/qa/qa/factory/resource/merge_request.rb +++ b/qa/qa/factory/resource/merge_request.rb @@ -24,12 +24,14 @@ module QA dependency Factory::Repository::Push, as: :target do |push, factory| factory.project.visit! push.project = factory.project - push.branch_name = "master:#{factory.target_branch}" + push.branch_name = 'master' + push.remote_branch = factory.target_branch end dependency Factory::Repository::Push, as: :source do |push, factory| push.project = factory.project - push.branch_name = "#{factory.target_branch}:#{factory.source_branch}" + push.branch_name = factory.target_branch + push.remote_branch = factory.source_branch push.file_name = "added_file.txt" push.file_content = "File Added" end diff --git a/qa/qa/factory/resource/project.rb b/qa/qa/factory/resource/project.rb index 7df2dc6618c..cda1b35ba6a 100644 --- a/qa/qa/factory/resource/project.rb +++ b/qa/qa/factory/resource/project.rb @@ -17,6 +17,13 @@ module QA Page::Project::Show.act { project_name } end + product :repository_ssh_location do + Page::Project::Show.act do + choose_repository_clone_ssh + repository_location + end + end + def fabricate! group.visit! diff --git a/qa/qa/factory/resource/secret_variable.rb b/qa/qa/factory/resource/secret_variable.rb index c734d739b4a..12a830da116 100644 --- a/qa/qa/factory/resource/secret_variable.rb +++ b/qa/qa/factory/resource/secret_variable.rb @@ -16,8 +16,7 @@ module QA Page::Project::Settings::CICD.perform do |setting| setting.expand_secret_variables do |page| - page.fill_variable_key(key) - page.fill_variable_value(value) + page.fill_variable(key, value) page.save_variables end diff --git a/qa/qa/git/location.rb b/qa/qa/git/location.rb index 30538388530..b74f38f3ae3 100644 --- a/qa/qa/git/location.rb +++ b/qa/qa/git/location.rb @@ -14,7 +14,7 @@ module QA def initialize(git_uri) @git_uri = git_uri @uri = - if git_uri.start_with?('ssh://') + if git_uri =~ %r{\A(?:ssh|http|https)://} URI.parse(git_uri) else *rest, path = git_uri.split(':') diff --git a/qa/qa/git/repository.rb b/qa/qa/git/repository.rb index b3150e8f3fa..1367671e3ca 100644 --- a/qa/qa/git/repository.rb +++ b/qa/qa/git/repository.rb @@ -1,19 +1,21 @@ require 'cgi' require 'uri' +require 'open3' module QA module Git class Repository include Scenario::Actable + attr_reader :push_error + def self.perform(*args) Dir.mktmpdir do |dir| Dir.chdir(dir) { super } end end - def location=(address) - @location = address + def uri=(address) @uri = URI(address) end @@ -40,6 +42,10 @@ module QA `git checkout "#{branch_name}"` end + def checkout_new_branch(branch_name) + `git checkout -b "#{branch_name}"` + end + def shallow_clone clone('--depth 1') end @@ -65,7 +71,8 @@ module QA end def push_changes(branch = 'master') - `git push #{@uri.to_s} #{branch} #{suppress_output}` + # capture3 returns stdout, stderr and status. + _, @push_error, _ = Open3.capture3("git push #{@uri} #{branch} #{suppress_output}") end def commits diff --git a/qa/qa/page/README.md b/qa/qa/page/README.md index d38223f690d..2dbc59846e7 100644 --- a/qa/qa/page/README.md +++ b/qa/qa/page/README.md @@ -115,8 +115,8 @@ from within the `qa` directory. ## Where to ask for help? -If you need more information, ask for help on `#qa` channel on Slack (GitLab -Team only). +If you need more information, ask for help on `#quality` channel on Slack +(internal, GitLab Team only). If you are not a Team Member, and you still need help to contribute, please open an issue in GitLab QA issue tracker. diff --git a/qa/qa/page/base.rb b/qa/qa/page/base.rb index a313d46205d..0a69af88570 100644 --- a/qa/qa/page/base.rb +++ b/qa/qa/page/base.rb @@ -64,6 +64,10 @@ module QA find(element_selector_css(name)) end + def all_elements(name) + all(element_selector_css(name)) + end + def click_element(name) find_element(name).click end diff --git a/qa/qa/page/group/show.rb b/qa/qa/page/group/show.rb index d215518d316..89125bd2e59 100644 --- a/qa/qa/page/group/show.rb +++ b/qa/qa/page/group/show.rb @@ -29,7 +29,7 @@ module QA filter_by_name(name) wait(reload: false) do - return false if page.has_content?('Sorry, no groups or projects matched your search') + break false if page.has_content?('Sorry, no groups or projects matched your search') page.has_link?(name) end diff --git a/qa/qa/page/merge_request/show.rb b/qa/qa/page/merge_request/show.rb index 2f2506f08fb..166861e6c4a 100644 --- a/qa/qa/page/merge_request/show.rb +++ b/qa/qa/page/merge_request/show.rb @@ -2,7 +2,7 @@ module QA module Page module MergeRequest class Show < Page::Base - view 'app/assets/javascripts/vue_merge_request_widget/components/states/mr_widget_ready_to_merge.js' do + view 'app/assets/javascripts/vue_merge_request_widget/components/states/ready_to_merge.vue' do element :merge_button element :fast_forward_message, 'Fast-forward merge without a merge commit' end diff --git a/qa/qa/page/project/pipeline/show.rb b/qa/qa/page/project/pipeline/show.rb index b183552d46c..ec61c47b3bb 100644 --- a/qa/qa/page/project/pipeline/show.rb +++ b/qa/qa/page/project/pipeline/show.rb @@ -20,14 +20,14 @@ module QA::Page def running? within('.ci-header-container') do - return page.has_content?('running') + page.has_content?('running') end end def has_build?(name, status: :success) within('.pipeline-graph') do within('.ci-job-component', text: name) do - return has_selector?(".ci-status-icon-#{status}") + has_selector?(".ci-status-icon-#{status}") end end end diff --git a/qa/qa/page/project/settings/deploy_keys.rb b/qa/qa/page/project/settings/deploy_keys.rb index 332e84724c7..4428e263bbb 100644 --- a/qa/qa/page/project/settings/deploy_keys.rb +++ b/qa/qa/page/project/settings/deploy_keys.rb @@ -42,6 +42,18 @@ module QA end end + def key_titles + within_project_deploy_keys do + all_elements(:key_title) + end + end + + def key_fingerprints + within_project_deploy_keys do + all_elements(:key_fingerprint) + end + end + private def within_project_deploy_keys diff --git a/qa/qa/page/project/settings/protected_branches.rb b/qa/qa/page/project/settings/protected_branches.rb new file mode 100644 index 00000000000..63bc3aaa2bc --- /dev/null +++ b/qa/qa/page/project/settings/protected_branches.rb @@ -0,0 +1,88 @@ +module QA + module Page + module Project + module Settings + class ProtectedBranches < Page::Base + view 'app/views/projects/protected_branches/shared/_dropdown.html.haml' do + element :protected_branch_select + element :protected_branch_dropdown + end + + view 'app/views/projects/protected_branches/_create_protected_branch.html.haml' do + element :allowed_to_push_select + element :allowed_to_push_dropdown + element :allowed_to_merge_select + element :allowed_to_merge_dropdown + end + + view 'app/views/projects/protected_branches/_update_protected_branch.html.haml' do + element :allowed_to_push + element :allowed_to_merge + end + + view 'app/views/projects/protected_branches/shared/_branches_list.html.haml' do + element :protected_branches_list + end + + view 'app/views/projects/protected_branches/shared/_protected_branch.html.haml' do + element :protected_branch_name + end + + def select_branch(branch_name) + click_element :protected_branch_select + + within_element(:protected_branch_dropdown) do + click_on branch_name + end + end + + def allow_no_one_to_push + click_allow(:push, 'No one') + end + + def allow_devs_and_masters_to_push + click_allow(:push, 'Developers + Masters') + end + + def allow_no_one_to_merge + click_allow(:merge, 'No one') + end + + def allow_devs_and_masters_to_merge + click_allow(:merge, 'Developers + Masters') + end + + def protect_branch + click_on 'Protect' + end + + def last_branch_name + within_element(:protected_branches_list) do + all('.qa-protected-branch-name').last + end + end + + def last_push_allowance + within_element(:protected_branches_list) do + all('.qa-allowed-to-push').last + end + end + + private + + def click_allow(action, text) + click_element :"allowed_to_#{action}_select" + + within_element(:"allowed_to_#{action}_dropdown") do + click_on text + + wait(reload: false) do + has_css?('.is-active') + end + end + end + end + end + end + end +end diff --git a/qa/qa/page/project/settings/repository.rb b/qa/qa/page/project/settings/repository.rb index 22362164a1a..30900e74e90 100644 --- a/qa/qa/page/project/settings/repository.rb +++ b/qa/qa/page/project/settings/repository.rb @@ -14,6 +14,12 @@ module QA DeployKeys.perform(&block) end end + + def expand_protected_branches(&block) + expand_section('Protected Branches') do + ProtectedBranches.perform(&block) + end + end end end end diff --git a/qa/qa/page/project/settings/secret_variables.rb b/qa/qa/page/project/settings/secret_variables.rb index c95c79f137d..d2f5d5a9060 100644 --- a/qa/qa/page/project/settings/secret_variables.rb +++ b/qa/qa/page/project/settings/secret_variables.rb @@ -7,10 +7,8 @@ module QA view 'app/views/ci/variables/_variable_row.html.haml' do element :variable_row, '.ci-variable-row-body' - element :variable_key, '.js-ci-variable-input-key' - element :variable_value, '.js-ci-variable-input-value' - element :key_placeholder, 'Input variable key' - element :value_placeholder, 'Input variable value' + element :variable_key, '.qa-ci-variable-input-key' + element :variable_value, '.qa-ci-variable-input-value' end view 'app/views/ci/variables/_index.html.haml' do @@ -18,12 +16,14 @@ module QA element :reveal_values, '.js-secret-value-reveal-button' end - def fill_variable_key(key) - fill_in('Input variable key', with: key, match: :first) - end + def fill_variable(key, value) + keys = all_elements(:ci_variable_input_key) + index = keys.size - 1 - def fill_variable_value(value) - fill_in('Input variable value', with: value, match: :first) + # After we fill the key, JS would generate another field so + # we need to use the same index to find the corresponding one. + keys[index].set(key) + all_elements(:ci_variable_input_value)[index].set(value) end def save_variables @@ -36,7 +36,7 @@ module QA def variable_value(key) within('.ci-variable-row-body', text: key) do - find('.js-ci-variable-input-value').value + find('.qa-ci-variable-input-value').value end end end diff --git a/qa/qa/page/project/show.rb b/qa/qa/page/project/show.rb index 0c7ad46d36b..5bbef040330 100644 --- a/qa/qa/page/project/show.rb +++ b/qa/qa/page/project/show.rb @@ -21,6 +21,11 @@ module QA element :new_issue_link, "link_to 'New issue', new_project_issue_path(@project)" end + view 'app/views/shared/_ref_switcher.html.haml' do + element :branches_select + element :branches_dropdown + end + def choose_repository_clone_http choose_repository_clone('HTTP', 'http') end @@ -33,17 +38,25 @@ module QA end def repository_location - find('#project_clone').value - end - - def repository_location_uri - Git::Location.new(repository_location) + Git::Location.new(find('#project_clone').value) end def project_name find('.qa-project-name').text end + def switch_to_branch(branch_name) + find_element(:branches_select).click + + within_element(:branches_dropdown) do + click_on branch_name + end + end + + def last_commit_content + find_element(:commit_content).text + end + def new_merge_request wait(reload: true) do has_css?(element_selector_css(:create_merge_request)) @@ -74,7 +87,7 @@ module QA end # Ensure git clone textbox was updated - repository_location.include?(detect_text) + repository_location.git_uri.include?(detect_text) end end end diff --git a/qa/qa/runtime/key/base.rb b/qa/qa/runtime/key/base.rb new file mode 100644 index 00000000000..c7e5ebada7b --- /dev/null +++ b/qa/qa/runtime/key/base.rb @@ -0,0 +1,36 @@ +module QA + module Runtime + module Key + class Base + attr_reader :name, :bits, :private_key, :public_key, :fingerprint + + def initialize(name, bits) + @name = name + @bits = bits + + Dir.mktmpdir do |dir| + path = "#{dir}/id_#{name}" + + ssh_keygen(name, bits, path) + populate_key_data(path) + end + end + + private + + def ssh_keygen(name, bits, path) + cmd = %W[ssh-keygen -t #{name} -b #{bits} -f #{path} -N] << '' + + Service::Shellout.shell(cmd) + end + + def populate_key_data(path) + @private_key = File.binread(path) + @public_key = File.binread("#{path}.pub") + @fingerprint = + `ssh-keygen -l -E md5 -f #{path} | cut -d' ' -f2 | cut -d: -f2-`.chomp + end + end + end + end +end diff --git a/qa/qa/runtime/key/ecdsa.rb b/qa/qa/runtime/key/ecdsa.rb new file mode 100644 index 00000000000..20adad45913 --- /dev/null +++ b/qa/qa/runtime/key/ecdsa.rb @@ -0,0 +1,12 @@ +# rubocop:disable Naming/FileName +module QA + module Runtime + module Key + class ECDSA < Base + def initialize(bits = 521) + super('ecdsa', bits) + end + end + end + end +end diff --git a/qa/qa/runtime/key/ed25519.rb b/qa/qa/runtime/key/ed25519.rb new file mode 100644 index 00000000000..63865c1cee5 --- /dev/null +++ b/qa/qa/runtime/key/ed25519.rb @@ -0,0 +1,12 @@ +# rubocop:disable Naming/FileName +module QA + module Runtime + module Key + class ED25519 < Base + def initialize + super('ed25519', 256) + end + end + end + end +end diff --git a/qa/qa/runtime/key/rsa.rb b/qa/qa/runtime/key/rsa.rb new file mode 100644 index 00000000000..d94bde52325 --- /dev/null +++ b/qa/qa/runtime/key/rsa.rb @@ -0,0 +1,11 @@ +module QA + module Runtime + module Key + class RSA < Base + def initialize(bits = 4096) + super('rsa', bits) + end + end + end + end +end diff --git a/qa/qa/runtime/rsa_key.rb b/qa/qa/runtime/rsa_key.rb deleted file mode 100644 index fcd7dcc4f02..00000000000 --- a/qa/qa/runtime/rsa_key.rb +++ /dev/null @@ -1,21 +0,0 @@ -require 'net/ssh' -require 'forwardable' - -module QA - module Runtime - class RSAKey - extend Forwardable - - attr_reader :key - def_delegators :@key, :fingerprint, :to_pem - - def initialize(bits = 4096) - @key = OpenSSL::PKey::RSA.new(bits) - end - - def public_key - @public_key ||= "#{key.ssh_type} #{[key.to_blob].pack('m0')}" - end - end - end -end diff --git a/qa/qa/scenario/template.rb b/qa/qa/scenario/template.rb index 341998af160..d21a9d52997 100644 --- a/qa/qa/scenario/template.rb +++ b/qa/qa/scenario/template.rb @@ -4,7 +4,7 @@ module QA def self.perform(*args) new.tap do |scenario| yield scenario if block_given? - return scenario.perform(*args) + break scenario.perform(*args) end end diff --git a/qa/qa/scenario/test/sanity/selectors.rb b/qa/qa/scenario/test/sanity/selectors.rb index c87eb5f3dfb..cff320cb751 100644 --- a/qa/qa/scenario/test/sanity/selectors.rb +++ b/qa/qa/scenario/test/sanity/selectors.rb @@ -31,7 +31,7 @@ module QA current changes in this merge request. For more help see documentation in `qa/page/README.md` file or - ask for help on #qa channel on Slack (GitLab Team only). + ask for help on #quality channel on Slack (GitLab Team only). If you are not a Team Member, and you still need help to contribute, please open an issue in GitLab QA issue tracker. diff --git a/qa/qa/service/shellout.rb b/qa/qa/service/shellout.rb index 76fb2af6319..1ca9504bb33 100644 --- a/qa/qa/service/shellout.rb +++ b/qa/qa/service/shellout.rb @@ -5,6 +5,8 @@ module QA module Shellout CommandError = Class.new(StandardError) + module_function + ## # TODO, make it possible to use generic QA framework classes # as a library - gitlab-org/gitlab-qa#94 @@ -12,7 +14,7 @@ module QA def shell(command) puts "Executing `#{command}`" - Open3.popen2e(command) do |_in, out, wait| + Open3.popen2e(*command) do |_in, out, wait| out.each { |line| puts line } if wait.value.exited? && wait.value.exitstatus.nonzero? diff --git a/qa/qa/specs/features/project/add_deploy_key_spec.rb b/qa/qa/specs/features/project/add_deploy_key_spec.rb index b9998dda895..de53613dee1 100644 --- a/qa/qa/specs/features/project/add_deploy_key_spec.rb +++ b/qa/qa/specs/features/project/add_deploy_key_spec.rb @@ -4,7 +4,7 @@ module QA Runtime::Browser.visit(:gitlab, Page::Main::Login) Page::Main::Login.act { sign_in_using_credentials } - key = Runtime::RSAKey.new + key = Runtime::Key::RSA.new deploy_key_title = 'deploy key title' deploy_key_value = key.public_key @@ -13,7 +13,6 @@ module QA resource.key = deploy_key_value end - expect(deploy_key.title).to eq(deploy_key_title) expect(deploy_key.fingerprint).to eq(key.fingerprint) end end diff --git a/qa/qa/specs/features/project/deploy_key_clone_spec.rb b/qa/qa/specs/features/project/deploy_key_clone_spec.rb index 19d3c83758a..98ea86bf75e 100644 --- a/qa/qa/specs/features/project/deploy_key_clone_spec.rb +++ b/qa/qa/specs/features/project/deploy_key_clone_spec.rb @@ -2,79 +2,103 @@ require 'digest/sha1' module QA feature 'cloning code using a deploy key', :core, :docker do - let(:runner_name) { "qa-runner-#{Time.now.to_i}" } - let(:key) { Runtime::RSAKey.new } + def login + Runtime::Browser.visit(:gitlab, Page::Main::Login) + Page::Main::Login.act { sign_in_using_credentials } + end - given(:project) do - Factory::Resource::Project.fabricate! do |resource| + before(:all) do + login + + @runner_name = "qa-runner-#{Time.now.to_i}" + + @project = Factory::Resource::Project.fabricate! do |resource| resource.name = 'deploy-key-clone-project' end - end - after do - Service::Runner.new(runner_name).remove! - end - - scenario 'user sets up a deploy key to clone code using pipelines' do - Runtime::Browser.visit(:gitlab, Page::Main::Login) - Page::Main::Login.act { sign_in_using_credentials } + @repository_location = @project.repository_ssh_location Factory::Resource::Runner.fabricate! do |resource| - resource.project = project - resource.name = runner_name + resource.project = @project + resource.name = @runner_name resource.tags = %w[qa docker] resource.image = 'gitlab/gitlab-runner:ubuntu' end - Factory::Resource::DeployKey.fabricate! do |resource| - resource.project = project - resource.title = 'deploy key title' - resource.key = key.public_key - end + Page::Menu::Main.act { sign_out } + end - Factory::Resource::SecretVariable.fabricate! do |resource| - resource.project = project - resource.key = 'DEPLOY_KEY' - resource.value = key.to_pem - end + after(:all) do + Service::Runner.new(@runner_name).remove! + end - project.visit! + keys = [ + Runtime::Key::RSA.new(8192), + Runtime::Key::ECDSA.new(521), + Runtime::Key::ED25519.new + ] - repository_uri = Page::Project::Show.act do - choose_repository_clone_ssh - repository_location_uri - end + keys.each do |key| + scenario "user sets up a deploy key with #{key.name}(#{key.bits}) to clone code using pipelines" do + login - gitlab_ci = <<~YAML - cat-config: - script: - - mkdir -p ~/.ssh - - ssh-keyscan -p #{repository_uri.port} #{repository_uri.host} >> ~/.ssh/known_hosts - - eval $(ssh-agent -s) - - echo "$DEPLOY_KEY" | ssh-add - - - git clone #{repository_uri.git_uri} - - sha1sum #{project.name}/.gitlab-ci.yml - tags: - - qa - - docker - YAML - - Factory::Repository::Push.fabricate! do |resource| - resource.project = project - resource.file_name = '.gitlab-ci.yml' - resource.commit_message = 'Add .gitlab-ci.yml' - resource.file_content = gitlab_ci - end + Factory::Resource::DeployKey.fabricate! do |resource| + resource.project = @project + resource.title = "deploy key #{key.name}(#{key.bits})" + resource.key = key.public_key + end + + deploy_key_name = "DEPLOY_KEY_#{key.name}_#{key.bits}" + + Factory::Resource::SecretVariable.fabricate! do |resource| + resource.project = @project + resource.key = deploy_key_name + resource.value = key.private_key + end + + gitlab_ci = <<~YAML + cat-config: + script: + - mkdir -p ~/.ssh + - ssh-keyscan -p #{@repository_location.port} #{@repository_location.host} >> ~/.ssh/known_hosts + - eval $(ssh-agent -s) + - ssh-add -D + - echo "$#{deploy_key_name}" | ssh-add - + - git clone #{@repository_location.git_uri} + - cd #{@project.name} + - git checkout #{deploy_key_name} + - sha1sum .gitlab-ci.yml + tags: + - qa + - docker + YAML + + Factory::Repository::Push.fabricate! do |resource| + resource.project = @project + resource.file_name = '.gitlab-ci.yml' + resource.commit_message = 'Add .gitlab-ci.yml' + resource.file_content = gitlab_ci + resource.branch_name = deploy_key_name + resource.new_branch = true + end + + sha1sum = Digest::SHA1.hexdigest(gitlab_ci) + + Page::Project::Show.act { wait_for_push } + Page::Menu::Side.act { click_ci_cd_pipelines } + Page::Project::Pipeline::Index.act { go_to_latest_pipeline } - sha1sum = Digest::SHA1.hexdigest(gitlab_ci) + Page::Project::Pipeline::Show.act do + go_to_first_job - Page::Project::Show.act { wait_for_push } - Page::Menu::Side.act { click_ci_cd_pipelines } - Page::Project::Pipeline::Index.act { go_to_latest_pipeline } - Page::Project::Pipeline::Show.act { go_to_first_job } + wait do + !has_content?('running') + end + end - Page::Project::Job::Show.perform do |job| - expect(job.output).to include(sha1sum) + Page::Project::Job::Show.perform do |job| + expect(job.output).to include(sha1sum) + end end end end diff --git a/qa/qa/specs/features/repository/clone_spec.rb b/qa/qa/specs/features/repository/clone_spec.rb index 2adb7524a46..bc9eb57bdb4 100644 --- a/qa/qa/specs/features/repository/clone_spec.rb +++ b/qa/qa/specs/features/repository/clone_spec.rb @@ -18,7 +18,7 @@ module QA end Git::Repository.perform do |repository| - repository.location = location + repository.uri = location.uri repository.use_default_credentials repository.act do @@ -33,7 +33,7 @@ module QA scenario 'user performs a deep clone' do Git::Repository.perform do |repository| - repository.location = location + repository.uri = location.uri repository.use_default_credentials repository.act { clone } @@ -44,7 +44,7 @@ module QA scenario 'user performs a shallow clone' do Git::Repository.perform do |repository| - repository.location = location + repository.uri = location.uri repository.use_default_credentials repository.act { shallow_clone } diff --git a/qa/qa/specs/features/repository/protected_branches_spec.rb b/qa/qa/specs/features/repository/protected_branches_spec.rb new file mode 100644 index 00000000000..406b2772b64 --- /dev/null +++ b/qa/qa/specs/features/repository/protected_branches_spec.rb @@ -0,0 +1,70 @@ +module QA + feature 'branch protection support', :core do + given(:branch_name) { 'protected-branch' } + given(:commit_message) { 'Protected push commit message' } + given(:project) do + Factory::Resource::Project.fabricate! do |resource| + resource.name = 'protected-branch-project' + end + end + given(:location) do + Page::Project::Show.act do + choose_repository_clone_http + repository_location + end + end + + before do + Runtime::Browser.visit(:gitlab, Page::Main::Login) + Page::Main::Login.act { sign_in_using_credentials } + end + + after do + # We need to clear localStorage because we're using it for the dropdown, + # and capybara doesn't do this for us. + # https://github.com/teamcapybara/capybara/issues/1702 + Capybara.execute_script 'localStorage.clear()' + end + + scenario 'user is able to protect a branch' do + protected_branch = Factory::Resource::Branch.fabricate! do |resource| + resource.branch_name = branch_name + resource.project = project + resource.allow_to_push = true + resource.protected = true + end + + expect(protected_branch.name).to have_content(branch_name) + expect(protected_branch.push_allowance).to have_content('Developers + Masters') + end + + scenario 'users without authorization cannot push to protected branch' do + Factory::Resource::Branch.fabricate! do |resource| + resource.branch_name = branch_name + resource.project = project + resource.allow_to_push = false + resource.protected = true + end + + project.visit! + + Git::Repository.perform do |repository| + repository.uri = location.uri + repository.use_default_credentials + + repository.act do + clone + configure_identity('GitLab QA', 'root@gitlab.com') + checkout('protected-branch') + commit_file('README.md', 'readme content', 'Add a readme') + push_changes('protected-branch') + end + + expect(repository.push_error) + .to match(/remote\: GitLab\: You are not allowed to push code to protected branches on this project/) + expect(repository.push_error) + .to match(/\[remote rejected\] #{branch_name} -> #{branch_name} \(pre-receive hook declined\)/) + end + end + end +end diff --git a/qa/spec/runtime/key/ecdsa_spec.rb b/qa/spec/runtime/key/ecdsa_spec.rb new file mode 100644 index 00000000000..8951e82b9bb --- /dev/null +++ b/qa/spec/runtime/key/ecdsa_spec.rb @@ -0,0 +1,18 @@ +describe QA::Runtime::Key::ECDSA do + describe '#public_key' do + [256, 384, 521].each do |bits| + it "generates a public #{bits}-bits ECDSA key" do + subject = described_class.new(bits).public_key + + expect(subject).to match(%r{\Aecdsa\-sha2\-\w+ AAAA[0-9A-Za-z+/]+={0,3}}) + end + end + end + + describe '#new' do + it 'does not support arbitrary bits' do + expect { described_class.new(123) } + .to raise_error(QA::Service::Shellout::CommandError) + end + end +end diff --git a/qa/spec/runtime/key/ed25519_spec.rb b/qa/spec/runtime/key/ed25519_spec.rb new file mode 100644 index 00000000000..4844e7affdf --- /dev/null +++ b/qa/spec/runtime/key/ed25519_spec.rb @@ -0,0 +1,9 @@ +describe QA::Runtime::Key::ED25519 do + describe '#public_key' do + subject { described_class.new.public_key } + + it 'generates a public ED25519 key' do + expect(subject).to match(%r{\Assh\-ed25519 AAAA[0-9A-Za-z+/]}) + end + end +end diff --git a/qa/spec/runtime/rsa_key.rb b/qa/spec/runtime/key/rsa_spec.rb index 6d7ab4dcd2e..fbcc7ffdcb4 100644 --- a/qa/spec/runtime/rsa_key.rb +++ b/qa/spec/runtime/key/rsa_spec.rb @@ -1,9 +1,9 @@ -describe QA::Runtime::RSAKey do +describe QA::Runtime::Key::RSA do describe '#public_key' do subject { described_class.new.public_key } it 'generates a public RSA key' do - expect(subject).to match(%r{\Assh\-rsa AAAA[0-9A-Za-z+/]+={0,3}\z}) + expect(subject).to match(%r{\Assh\-rsa AAAA[0-9A-Za-z+/]+={0,3}}) end end end |
