Merge branch 'ml-qa-spec-use-ssh-key' into 'master'
Add a new scenario to add an SSH key, perform Git actions with it, and then remove the key See merge request gitlab-org/gitlab-ce!19754
This commit is contained in:
commit
c44d8bab91
|
@ -158,7 +158,7 @@
|
|||
|
||||
- if project_nav_tab? :pipelines
|
||||
= nav_link(controller: [:pipelines, :builds, :jobs, :pipeline_schedules, :artifacts]) do
|
||||
= link_to project_pipelines_path(@project), class: 'shortcuts-pipelines' do
|
||||
= link_to project_pipelines_path(@project), class: 'shortcuts-pipelines qa-link-pipelines' do
|
||||
.nav-icon-container
|
||||
= sprite_icon('rocket')
|
||||
%span.nav-item-name
|
||||
|
|
|
@ -5,10 +5,10 @@
|
|||
.form-group
|
||||
= f.label :key, class: 'label-bold'
|
||||
%p= _("Paste your public SSH key, which is usually contained in the file '~/.ssh/id_rsa.pub' and begins with 'ssh-rsa'. Don't use your private SSH key.")
|
||||
= f.text_area :key, class: "form-control js-add-ssh-key-validation-input", rows: 8, required: true, placeholder: s_('Profiles|Typically starts with "ssh-rsa …"')
|
||||
= f.text_area :key, class: "form-control js-add-ssh-key-validation-input qa-key-public-key-field", rows: 8, required: true, placeholder: s_('Profiles|Typically starts with "ssh-rsa …"')
|
||||
.form-group
|
||||
= f.label :title, class: 'label-bold'
|
||||
= f.text_field :title, class: "form-control input-lg", required: true, placeholder: s_('Profiles|e.g. My MacBook key')
|
||||
= f.text_field :title, class: "form-control input-lg qa-key-title-field", required: true, placeholder: s_('Profiles|e.g. My MacBook key')
|
||||
%p.form-text.text-muted= _('Name your individual key via a title')
|
||||
|
||||
.js-add-ssh-key-validation-warning.hide
|
||||
|
@ -19,4 +19,4 @@
|
|||
%button.btn.btn-create.js-add-ssh-key-validation-confirm-submit= _("Yes, add it")
|
||||
|
||||
.prepend-top-default
|
||||
= f.submit s_('Profiles|Add key'), class: "btn btn-create js-add-ssh-key-validation-original-submit"
|
||||
= f.submit s_('Profiles|Add key'), class: "btn btn-create js-add-ssh-key-validation-original-submit qa-add-key-button"
|
||||
|
|
|
@ -24,4 +24,4 @@
|
|||
= @key.key
|
||||
.col-md-12
|
||||
.float-right
|
||||
= link_to 'Remove', path_to_key(@key, is_admin), data: {confirm: 'Are you sure?'}, method: :delete, class: "btn btn-remove delete-key"
|
||||
= link_to 'Remove', path_to_key(@key, is_admin), data: {confirm: 'Are you sure?'}, method: :delete, class: "btn btn-remove delete-key qa-delete-key-button"
|
||||
|
|
2
qa/qa.rb
2
qa/qa.rb
|
@ -57,6 +57,7 @@ module QA
|
|||
autoload :Wiki, 'qa/factory/resource/wiki'
|
||||
autoload :File, 'qa/factory/resource/file'
|
||||
autoload :Fork, 'qa/factory/resource/fork'
|
||||
autoload :SSHKey, 'qa/factory/resource/ssh_key'
|
||||
end
|
||||
|
||||
module Repository
|
||||
|
@ -217,6 +218,7 @@ module QA
|
|||
|
||||
module Profile
|
||||
autoload :PersonalAccessTokens, 'qa/page/profile/personal_access_tokens'
|
||||
autoload :SSHKeys, 'qa/page/profile/ssh_keys'
|
||||
end
|
||||
|
||||
module Issuable
|
||||
|
|
|
@ -11,7 +11,9 @@ module QA
|
|||
factory.output
|
||||
end
|
||||
|
||||
product(:project) { |factory| factory.project }
|
||||
product :project do |factory|
|
||||
factory.project
|
||||
end
|
||||
|
||||
def initialize
|
||||
@file_name = 'file.txt'
|
||||
|
@ -21,8 +23,8 @@ module QA
|
|||
@new_branch = true
|
||||
end
|
||||
|
||||
def repository_uri
|
||||
@repository_uri ||= begin
|
||||
def repository_http_uri
|
||||
@repository_http_uri ||= begin
|
||||
project.visit!
|
||||
Page::Project::Show.act do
|
||||
choose_repository_clone_http
|
||||
|
@ -30,6 +32,16 @@ module QA
|
|||
end
|
||||
end
|
||||
end
|
||||
|
||||
def repository_ssh_uri
|
||||
@repository_ssh_uri ||= begin
|
||||
project.visit!
|
||||
Page::Project::Show.act do
|
||||
choose_repository_clone_ssh
|
||||
repository_location.uri
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -5,8 +5,8 @@ module QA
|
|||
module Repository
|
||||
class Push < Factory::Base
|
||||
attr_accessor :file_name, :file_content, :commit_message,
|
||||
:branch_name, :new_branch, :output, :repository_uri,
|
||||
:user
|
||||
:branch_name, :new_branch, :output, :repository_http_uri,
|
||||
:repository_ssh_uri, :ssh_key, :user
|
||||
|
||||
attr_writer :remote_branch
|
||||
|
||||
|
@ -16,7 +16,8 @@ module QA
|
|||
@commit_message = "This is a test commit"
|
||||
@branch_name = 'master'
|
||||
@new_branch = true
|
||||
@repository_uri = ""
|
||||
@repository_http_uri = ""
|
||||
@ssh_key = nil
|
||||
end
|
||||
|
||||
def remote_branch
|
||||
|
@ -31,9 +32,14 @@ module QA
|
|||
|
||||
def fabricate!
|
||||
Git::Repository.perform do |repository|
|
||||
repository.uri = repository_uri
|
||||
if ssh_key
|
||||
repository.uri = repository_ssh_uri
|
||||
repository.use_ssh_key(ssh_key)
|
||||
else
|
||||
repository.uri = repository_http_uri
|
||||
repository.use_default_credentials
|
||||
end
|
||||
|
||||
repository.use_default_credentials
|
||||
username = 'GitLab QA'
|
||||
email = 'root@gitlab.com'
|
||||
|
||||
|
@ -63,6 +69,8 @@ module QA
|
|||
|
||||
repository.commit(commit_message)
|
||||
@output = repository.push_changes("#{branch_name}:#{remote_branch}")
|
||||
|
||||
repository.delete_ssh_key
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -16,8 +16,8 @@ module QA
|
|||
@new_branch = false
|
||||
end
|
||||
|
||||
def repository_uri
|
||||
@repository_uri ||= begin
|
||||
def repository_http_uri
|
||||
@repository_http_uri ||= begin
|
||||
wiki.visit!
|
||||
Page::Project::Wiki::Show.act do
|
||||
go_to_clone_repository
|
||||
|
|
|
@ -20,6 +20,13 @@ module QA
|
|||
end
|
||||
end
|
||||
|
||||
product :repository_http_location do
|
||||
Page::Project::Show.act do
|
||||
choose_repository_clone_http
|
||||
repository_location
|
||||
end
|
||||
end
|
||||
|
||||
def initialize
|
||||
@description = 'My awesome project'
|
||||
end
|
||||
|
|
|
@ -0,0 +1,40 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
module QA
|
||||
module Factory
|
||||
module Resource
|
||||
class SSHKey < Factory::Base
|
||||
extend Forwardable
|
||||
|
||||
attr_accessor :title
|
||||
attr_reader :private_key, :public_key, :fingerprint
|
||||
def_delegators :key, :private_key, :public_key, :fingerprint
|
||||
|
||||
product :private_key do |factory|
|
||||
factory.private_key
|
||||
end
|
||||
|
||||
product :title do |factory|
|
||||
factory.title
|
||||
end
|
||||
|
||||
product :fingerprint do |factory|
|
||||
factory.fingerprint
|
||||
end
|
||||
|
||||
def key
|
||||
@key ||= Runtime::Key::RSA.new
|
||||
end
|
||||
|
||||
def fabricate!
|
||||
Page::Menu::Main.act { go_to_profile_settings }
|
||||
Page::Menu::Profile.act { click_ssh_keys }
|
||||
|
||||
Page::Profile::SSHKeys.perform do |page|
|
||||
page.add_key(public_key, title)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
|
@ -7,6 +7,10 @@ module QA
|
|||
class Repository
|
||||
include Scenario::Actable
|
||||
|
||||
def initialize
|
||||
@ssh_cmd = ""
|
||||
end
|
||||
|
||||
def self.perform(*args)
|
||||
Dir.mktmpdir do |dir|
|
||||
Dir.chdir(dir) { super }
|
||||
|
@ -38,7 +42,7 @@ module QA
|
|||
end
|
||||
|
||||
def clone(opts = '')
|
||||
run_and_redact_credentials("git clone #{opts} #{@uri} ./")
|
||||
run_and_redact_credentials(build_git_command("git clone #{opts} #{@uri} ./"))
|
||||
end
|
||||
|
||||
def checkout(branch_name)
|
||||
|
@ -58,6 +62,10 @@ module QA
|
|||
`git config user.email #{email}`
|
||||
end
|
||||
|
||||
def configure_ssh_command(command)
|
||||
@ssh_cmd = "GIT_SSH_COMMAND='#{command}'"
|
||||
end
|
||||
|
||||
def commit_file(name, contents, message)
|
||||
add_file(name, contents)
|
||||
commit(message)
|
||||
|
@ -74,7 +82,7 @@ module QA
|
|||
end
|
||||
|
||||
def push_changes(branch = 'master')
|
||||
output, _ = run_and_redact_credentials("git push #{@uri} #{branch}")
|
||||
output, _ = run_and_redact_credentials(build_git_command("git push #{@uri} #{branch}"))
|
||||
|
||||
output
|
||||
end
|
||||
|
@ -83,6 +91,31 @@ module QA
|
|||
`git log --oneline`.split("\n")
|
||||
end
|
||||
|
||||
def use_ssh_key(key)
|
||||
@private_key_file = Tempfile.new("id_#{SecureRandom.hex(8)}")
|
||||
File.binwrite(@private_key_file, key.private_key)
|
||||
File.chmod(0700, @private_key_file)
|
||||
|
||||
@known_hosts_file = Tempfile.new("known_hosts_#{SecureRandom.hex(8)}")
|
||||
keyscan_params = ['-H']
|
||||
keyscan_params << "-p #{@uri.port}" if @uri.port
|
||||
keyscan_params << @uri.host
|
||||
run_and_redact_credentials("ssh-keyscan #{keyscan_params.join(' ')} >> #{@known_hosts_file.path}")
|
||||
|
||||
configure_ssh_command("ssh -i #{@private_key_file.path} -o UserKnownHostsFile=#{@known_hosts_file.path}")
|
||||
end
|
||||
|
||||
def delete_ssh_key
|
||||
return unless @private_key_file
|
||||
|
||||
@private_key_file.close(true)
|
||||
@known_hosts_file.close(true)
|
||||
end
|
||||
|
||||
def build_git_command(command_str)
|
||||
[@ssh_cmd, command_str].compact.join(' ')
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
# Since the remote URL contains the credentials, and git occasionally
|
||||
|
|
|
@ -6,6 +6,7 @@ module QA
|
|||
element :access_token_link, 'link_to profile_personal_access_tokens_path'
|
||||
element :access_token_title, 'Access Tokens'
|
||||
element :top_level_items, '.sidebar-top-level-items'
|
||||
element :ssh_keys, 'SSH Keys'
|
||||
end
|
||||
|
||||
def click_access_tokens
|
||||
|
@ -14,6 +15,12 @@ module QA
|
|||
end
|
||||
end
|
||||
|
||||
def click_ssh_keys
|
||||
within_sidebar do
|
||||
click_link('SSH Keys')
|
||||
end
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def within_sidebar
|
||||
|
|
|
@ -6,6 +6,7 @@ module QA
|
|||
element :settings_item
|
||||
element :settings_link, 'link_to edit_project_path'
|
||||
element :repository_link, "title: _('Repository')"
|
||||
element :link_pipelines
|
||||
element :pipelines_settings_link, "title: _('CI / CD')"
|
||||
element :operations_kubernetes_link, "title: _('Kubernetes')"
|
||||
element :issues_link, /link_to.*shortcuts-issues/
|
||||
|
@ -49,7 +50,7 @@ module QA
|
|||
|
||||
def click_ci_cd_pipelines
|
||||
within_sidebar do
|
||||
click_link('CI / CD')
|
||||
click_element :link_pipelines
|
||||
end
|
||||
end
|
||||
|
||||
|
|
|
@ -0,0 +1,34 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
module QA
|
||||
module Page
|
||||
module Profile
|
||||
class SSHKeys < Page::Base
|
||||
view 'app/views/profiles/keys/_form.html.haml' do
|
||||
element :key_title_field
|
||||
element :key_public_key_field
|
||||
element :add_key_button
|
||||
end
|
||||
|
||||
view 'app/views/profiles/keys/_key_details.html.haml' do
|
||||
element :delete_key_button
|
||||
end
|
||||
|
||||
def add_key(public_key, title)
|
||||
fill_element :key_public_key_field, public_key
|
||||
fill_element :key_title_field, title
|
||||
|
||||
click_element :add_key_button
|
||||
end
|
||||
|
||||
def remove_key(title)
|
||||
click_link(title)
|
||||
|
||||
accept_alert do
|
||||
click_element :delete_key_button
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
|
@ -0,0 +1,31 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
module QA
|
||||
context :create do
|
||||
describe 'SSH keys support' do
|
||||
let(:key_title) { "key for ssh tests #{Time.now.to_f}" }
|
||||
|
||||
it 'user adds and then removes an SSH key' do
|
||||
Runtime::Browser.visit(:gitlab, Page::Main::Login)
|
||||
Page::Main::Login.act { sign_in_using_credentials }
|
||||
|
||||
key = Factory::Resource::SSHKey.fabricate! do |resource|
|
||||
resource.title = key_title
|
||||
end
|
||||
|
||||
expect(page).to have_content("Title: #{key_title}")
|
||||
expect(page).to have_content(key.fingerprint)
|
||||
|
||||
Page::Menu::Main.act { go_to_profile_settings }
|
||||
Page::Menu::Profile.act { click_ssh_keys }
|
||||
|
||||
Page::Profile::SSHKeys.perform do |ssh_keys|
|
||||
ssh_keys.remove_key(key_title)
|
||||
end
|
||||
|
||||
expect(page).not_to have_content("Title: #{key_title}")
|
||||
expect(page).not_to have_content(key.fingerprint)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
|
@ -0,0 +1,40 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
module QA
|
||||
context :create do
|
||||
describe 'SSH key support' do
|
||||
# Note: If you run this test against GDK make sure you've enabled sshd
|
||||
# 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}" }
|
||||
|
||||
it 'user adds an ssh key and pushes code to the repository' do
|
||||
Runtime::Browser.visit(:gitlab, Page::Main::Login)
|
||||
Page::Main::Login.act { sign_in_using_credentials }
|
||||
|
||||
key = Factory::Resource::SSHKey.fabricate! do |resource|
|
||||
resource.title = key_title
|
||||
end
|
||||
|
||||
Factory::Repository::ProjectPush.fabricate! do |push|
|
||||
push.ssh_key = key
|
||||
push.file_name = 'README.md'
|
||||
push.file_content = '# Test Use SSH Key'
|
||||
push.commit_message = 'Add README.md'
|
||||
end
|
||||
|
||||
Page::Project::Show.act { wait_for_push }
|
||||
|
||||
expect(page).to have_content('README.md')
|
||||
expect(page).to have_content('Test Use SSH Key')
|
||||
|
||||
Page::Menu::Main.act { go_to_profile_settings }
|
||||
Page::Menu::Profile.act { click_ssh_keys }
|
||||
|
||||
Page::Profile::SSHKeys.perform do |ssh_keys|
|
||||
ssh_keys.remove_key(key_title)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
Loading…
Reference in New Issue