Commit ed0f6b3a authored by Shinya Maeda's avatar Shinya Maeda

Merge branch 'master' into 35616-move-k8-to-cluster-page

parents 216173d1 61392168
...@@ -400,7 +400,7 @@ group :ed25519 do ...@@ -400,7 +400,7 @@ group :ed25519 do
end end
# Gitaly GRPC client # Gitaly GRPC client
gem 'gitaly-proto', '~> 0.54.0', require: 'gitaly' gem 'gitaly-proto', '~> 0.58.0', require: 'gitaly'
gem 'toml-rb', '~> 0.3.15', require: false gem 'toml-rb', '~> 0.3.15', require: false
......
...@@ -276,7 +276,7 @@ GEM ...@@ -276,7 +276,7 @@ GEM
po_to_json (>= 1.0.0) po_to_json (>= 1.0.0)
rails (>= 3.2.0) rails (>= 3.2.0)
gherkin-ruby (0.3.2) gherkin-ruby (0.3.2)
gitaly-proto (0.54.0) gitaly-proto (0.58.0)
google-protobuf (~> 3.1) google-protobuf (~> 3.1)
grpc (~> 1.0) grpc (~> 1.0)
github-linguist (4.7.6) github-linguist (4.7.6)
...@@ -1037,7 +1037,7 @@ DEPENDENCIES ...@@ -1037,7 +1037,7 @@ DEPENDENCIES
gettext (~> 3.2.2) gettext (~> 3.2.2)
gettext_i18n_rails (~> 1.8.0) gettext_i18n_rails (~> 1.8.0)
gettext_i18n_rails_js (~> 1.2.0) gettext_i18n_rails_js (~> 1.2.0)
gitaly-proto (~> 0.54.0) gitaly-proto (~> 0.58.0)
github-linguist (~> 4.7.0) github-linguist (~> 4.7.0)
gitlab-flowdock-git-hook (~> 1.0.1) gitlab-flowdock-git-hook (~> 1.0.1)
gitlab-markup (~> 1.6.2) gitlab-markup (~> 1.6.2)
......
...@@ -3,3 +3,4 @@ import './polyfills'; ...@@ -3,3 +3,4 @@ import './polyfills';
import './jquery'; import './jquery';
import './bootstrap'; import './bootstrap';
import './vue'; import './vue';
import '../lib/utils/axios_utils';
...@@ -78,11 +78,13 @@ ...@@ -78,11 +78,13 @@
<div class="ci-job-component"> <div class="ci-job-component">
<a <a
v-tooltip v-tooltip
v-if="job.status.details_path" v-if="job.status.has_details"
:href="job.status.details_path" :href="job.status.details_path"
:title="tooltipText" :title="tooltipText"
:class="cssClassJobName" :class="cssClassJobName"
data-container="body"> data-container="body"
class="js-pipeline-graph-job-link"
>
<job-name-component <job-name-component
:name="job.name" :name="job.name"
...@@ -95,7 +97,8 @@ ...@@ -95,7 +97,8 @@
v-tooltip v-tooltip
:title="tooltipText" :title="tooltipText"
:class="cssClassJobName" :class="cssClassJobName"
data-container="body"> data-container="body"
>
<job-name-component <job-name-component
:name="job.name" :name="job.name"
......
---
title: Add axios to common file
merge_request:
author:
type: performance
---
title: Use custom user agent header in all GCP API requests.
merge_request: 15705
author:
type: changed
...@@ -5,20 +5,19 @@ module Gitlab ...@@ -5,20 +5,19 @@ module Gitlab
class Sequence class Sequence
def initialize(pipeline, command, sequence) def initialize(pipeline, command, sequence)
@pipeline = pipeline @pipeline = pipeline
@command = command
@sequence = sequence
@completed = [] @completed = []
@sequence = sequence.map do |chain|
chain.new(pipeline, command)
end
end end
def build! def build!
@sequence.each do |step| @sequence.each do |chain|
step.perform! step = chain.new(@pipeline, @command)
step.perform!
break if step.break? break if step.break?
@completed << step @completed.push(step)
end end
@pipeline.tap do @pipeline.tap do
......
...@@ -418,6 +418,20 @@ module Gitlab ...@@ -418,6 +418,20 @@ module Gitlab
parent_ids.size > 1 parent_ids.size > 1
end end
def to_gitaly_commit
return raw_commit if raw_commit.is_a?(Gitaly::GitCommit)
message_split = raw_commit.message.split("\n", 2)
Gitaly::GitCommit.new(
id: raw_commit.oid,
subject: message_split[0] ? message_split[0].chomp.b : "",
body: raw_commit.message.b,
parent_ids: raw_commit.parent_ids,
author: gitaly_commit_author_from_rugged(raw_commit.author),
committer: gitaly_commit_author_from_rugged(raw_commit.committer)
)
end
private private
def init_from_hash(hash) def init_from_hash(hash)
...@@ -463,6 +477,14 @@ module Gitlab ...@@ -463,6 +477,14 @@ module Gitlab
def serialize_keys def serialize_keys
SERIALIZE_KEYS SERIALIZE_KEYS
end end
def gitaly_commit_author_from_rugged(author_or_committer)
Gitaly::CommitAuthor.new(
name: author_or_committer[:name].b,
email: author_or_committer[:email].b,
date: Google::Protobuf::Timestamp.new(seconds: author_or_committer[:time].to_i)
)
end
end end
end end
end end
...@@ -809,42 +809,22 @@ module Gitlab ...@@ -809,42 +809,22 @@ module Gitlab
end end
def cherry_pick(user:, commit:, branch_name:, message:, start_branch_name:, start_repository:) def cherry_pick(user:, commit:, branch_name:, message:, start_branch_name:, start_repository:)
OperationService.new(user, self).with_branch( gitaly_migrate(:cherry_pick) do |is_enabled|
branch_name, args = {
user: user,
commit: commit,
branch_name: branch_name,
message: message,
start_branch_name: start_branch_name, start_branch_name: start_branch_name,
start_repository: start_repository start_repository: start_repository
) do |start_commit| }
Gitlab::Git.check_namespace!(commit, start_repository)
cherry_pick_tree_id = check_cherry_pick_content(commit, start_commit.sha)
raise CreateTreeError unless cherry_pick_tree_id
committer = user_to_committer(user)
create_commit(message: message, if is_enabled
author: { gitaly_operations_client.user_cherry_pick(args)
email: commit.author_email, else
name: commit.author_name, rugged_cherry_pick(args)
time: commit.authored_date
},
committer: committer,
tree: cherry_pick_tree_id,
parents: [start_commit.sha])
end end
end end
def check_cherry_pick_content(target_commit, source_sha)
args = [target_commit.sha, source_sha]
args << 1 if target_commit.merge_commit?
cherry_pick_index = rugged.cherrypick_commit(*args)
return false if cherry_pick_index.conflicts?
tree_id = cherry_pick_index.write_tree(rugged)
return false unless diff_exists?(source_sha, tree_id)
tree_id
end end
def diff_exists?(sha1, sha2) def diff_exists?(sha1, sha2)
...@@ -1673,6 +1653,45 @@ module Gitlab ...@@ -1673,6 +1653,45 @@ module Gitlab
raise InvalidRef, ex raise InvalidRef, ex
end end
def rugged_cherry_pick(user:, commit:, branch_name:, message:, start_branch_name:, start_repository:)
OperationService.new(user, self).with_branch(
branch_name,
start_branch_name: start_branch_name,
start_repository: start_repository
) do |start_commit|
Gitlab::Git.check_namespace!(commit, start_repository)
cherry_pick_tree_id = check_cherry_pick_content(commit, start_commit.sha)
raise CreateTreeError unless cherry_pick_tree_id
committer = user_to_committer(user)
create_commit(message: message,
author: {
email: commit.author_email,
name: commit.author_name,
time: commit.authored_date
},
committer: committer,
tree: cherry_pick_tree_id,
parents: [start_commit.sha])
end
end
def check_cherry_pick_content(target_commit, source_sha)
args = [target_commit.sha, source_sha]
args << 1 if target_commit.merge_commit?
cherry_pick_index = rugged.cherrypick_commit(*args)
return false if cherry_pick_index.conflicts?
tree_id = cherry_pick_index.write_tree(rugged)
return false unless diff_exists?(source_sha, tree_id)
tree_id
end
def local_fetch_ref(source_path, source_ref:, target_ref:) def local_fetch_ref(source_path, source_ref:, target_ref:)
args = %W(fetch --no-tags -f #{source_path} #{source_ref}:#{target_ref}) args = %W(fetch --no-tags -f #{source_path} #{source_ref}:#{target_ref})
run_git(args) run_git(args)
......
...@@ -122,6 +122,36 @@ module Gitlab ...@@ -122,6 +122,36 @@ module Gitlab
).branch_update ).branch_update
Gitlab::Git::OperationService::BranchUpdate.from_gitaly(branch_update) Gitlab::Git::OperationService::BranchUpdate.from_gitaly(branch_update)
end end
def user_cherry_pick(user:, commit:, branch_name:, message:, start_branch_name:, start_repository:)
request = Gitaly::UserCherryPickRequest.new(
repository: @gitaly_repo,
user: Gitlab::Git::User.from_gitlab(user).to_gitaly,
commit: commit.to_gitaly_commit,
branch_name: GitalyClient.encode(branch_name),
message: GitalyClient.encode(message),
start_branch_name: GitalyClient.encode(start_branch_name.to_s),
start_repository: start_repository.gitaly_repository
)
response = GitalyClient.call(
@repository.storage,
:operation_service,
:user_cherry_pick,
request,
remote_storage: start_repository.storage
)
if response.pre_receive_error.presence
raise Gitlab::Git::HooksService::PreReceiveError, response.pre_receive_error
elsif response.commit_error.presence
raise Gitlab::Git::CommitError, response.commit_error
elsif response.create_tree_error.presence
raise Gitlab::Git::Repository::CreateTreeError, response.create_tree_error
else
Gitlab::Git::OperationService::BranchUpdate.from_gitaly(response.branch_update)
end
end
end end
end end
end end
...@@ -44,7 +44,7 @@ module GoogleApi ...@@ -44,7 +44,7 @@ module GoogleApi
service = Google::Apis::ContainerV1::ContainerService.new service = Google::Apis::ContainerV1::ContainerService.new
service.authorization = access_token service.authorization = access_token
service.get_zone_cluster(project_id, zone, cluster_id) service.get_zone_cluster(project_id, zone, cluster_id, options: user_agent_header)
end end
def projects_zones_clusters_create(project_id, zone, cluster_name, cluster_size, machine_type:) def projects_zones_clusters_create(project_id, zone, cluster_name, cluster_size, machine_type:)
...@@ -62,14 +62,14 @@ module GoogleApi ...@@ -62,14 +62,14 @@ module GoogleApi
} }
} ) } )
service.create_cluster(project_id, zone, request_body) service.create_cluster(project_id, zone, request_body, options: user_agent_header)
end end
def projects_zones_operations(project_id, zone, operation_id) def projects_zones_operations(project_id, zone, operation_id)
service = Google::Apis::ContainerV1::ContainerService.new service = Google::Apis::ContainerV1::ContainerService.new
service.authorization = access_token service.authorization = access_token
service.get_zone_operation(project_id, zone, operation_id) service.get_zone_operation(project_id, zone, operation_id, options: user_agent_header)
end end
def parse_operation_id(self_link) def parse_operation_id(self_link)
...@@ -82,6 +82,12 @@ module GoogleApi ...@@ -82,6 +82,12 @@ module GoogleApi
def token_life_time(expires_at) def token_life_time(expires_at)
DateTime.strptime(expires_at, '%s').to_time.utc - Time.now.utc DateTime.strptime(expires_at, '%s').to_time.utc - Time.now.utc
end end
def user_agent_header
Google::Apis::RequestOptions.new.tap do |options|
options.header = { 'User-Agent': "GitLab/#{Gitlab::VERSION.match('(\d+\.\d+)').captures.first} (GPN:GitLab;)" }
end
end
end end
end end
end end
...@@ -185,6 +185,36 @@ describe 'Pipeline', :js do ...@@ -185,6 +185,36 @@ describe 'Pipeline', :js do
end end
end end
context 'when user does not have access to read jobs' do
before do
project.update(public_builds: false)
end
describe 'GET /:project/pipelines/:id' do
include_context 'pipeline builds'
let(:project) { create(:project, :repository) }
let(:pipeline) { create(:ci_pipeline, project: project, ref: 'master', sha: project.commit.id, user: user) }
before do
visit project_pipeline_path(project, pipeline)
end
it 'shows the pipeline graph' do
expect(page).to have_selector('.pipeline-visualization')
expect(page).to have_content('Build')
expect(page).to have_content('Test')
expect(page).to have_content('Deploy')
expect(page).to have_content('Retry')
expect(page).to have_content('Cancel running')
end
it 'should not link to job' do
expect(page).not_to have_selector('.js-pipeline-graph-job-link')
end
end
end
describe 'GET /:project/pipelines/:id/builds' do describe 'GET /:project/pipelines/:id/builds' do
include_context 'pipeline builds' include_context 'pipeline builds'
......
import Vue from 'vue'; import Vue from 'vue';
import jobComponent from '~/pipelines/components/graph/job_component.vue'; import jobComponent from '~/pipelines/components/graph/job_component.vue';
import mountComponent from '../../helpers/vue_mount_component_helper';
describe('pipeline graph job component', () => { describe('pipeline graph job component', () => {
let JobComponent; let JobComponent;
let component;
const mockJob = { const mockJob = {
id: 4256, id: 4256,
...@@ -13,6 +15,7 @@ describe('pipeline graph job component', () => { ...@@ -13,6 +15,7 @@ describe('pipeline graph job component', () => {
label: 'passed', label: 'passed',
group: 'success', group: 'success',
details_path: '/root/ci-mock/builds/4256', details_path: '/root/ci-mock/builds/4256',
has_details: true,
action: { action: {
icon: 'retry', icon: 'retry',
title: 'Retry', title: 'Retry',
...@@ -26,13 +29,13 @@ describe('pipeline graph job component', () => { ...@@ -26,13 +29,13 @@ describe('pipeline graph job component', () => {
JobComponent = Vue.extend(jobComponent); JobComponent = Vue.extend(jobComponent);
}); });
afterEach(() => {
component.$destroy();
});
describe('name with link', () => { describe('name with link', () => {
it('should render the job name and status with a link', (done) => { it('should render the job name and status with a link', (done) => {
const component = new JobComponent({ component = mountComponent(JobComponent, { job: mockJob });
propsData: {
job: mockJob,
},
}).$mount();
Vue.nextTick(() => { Vue.nextTick(() => {
const link = component.$el.querySelector('a'); const link = component.$el.querySelector('a');
...@@ -56,8 +59,7 @@ describe('pipeline graph job component', () => { ...@@ -56,8 +59,7 @@ describe('pipeline graph job component', () => {
describe('name without link', () => { describe('name without link', () => {
it('it should render status and name', () => { it('it should render status and name', () => {
const component = new JobComponent({ component = mountComponent(JobComponent, {
propsData: {
job: { job: {
id: 4256, id: 4256,
name: 'test', name: 'test',
...@@ -67,12 +69,13 @@ describe('pipeline graph job component', () => { ...@@ -67,12 +69,13 @@ describe('pipeline graph job component', () => {
label: 'passed', label: 'passed',
group: 'success', group: 'success',
details_path: '/root/ci-mock/builds/4256', details_path: '/root/ci-mock/builds/4256',
has_details: false,
}, },
}, },
}, });
}).$mount();
expect(component.$el.querySelector('.js-status-icon-success')).toBeDefined(); expect(component.$el.querySelector('.js-status-icon-success')).toBeDefined();
expect(component.$el.querySelector('a')).toBeNull();
expect( expect(
component.$el.querySelector('.ci-status-text').textContent.trim(), component.$el.querySelector('.ci-status-text').textContent.trim(),
...@@ -82,11 +85,7 @@ describe('pipeline graph job component', () => { ...@@ -82,11 +85,7 @@ describe('pipeline graph job component', () => {
describe('action icon', () => { describe('action icon', () => {
it('it should render the action icon', () => { it('it should render the action icon', () => {
const component = new JobComponent({ component = mountComponent(JobComponent, { job: mockJob });
propsData: {
job: mockJob,
},
}).$mount();
expect(component.$el.querySelector('a.ci-action-icon-container')).toBeDefined(); expect(component.$el.querySelector('a.ci-action-icon-container')).toBeDefined();
expect(component.$el.querySelector('i.ci-action-icon-wrapper')).toBeDefined(); expect(component.$el.querySelector('i.ci-action-icon-wrapper')).toBeDefined();
...@@ -95,24 +94,20 @@ describe('pipeline graph job component', () => { ...@@ -95,24 +94,20 @@ describe('pipeline graph job component', () => {
describe('dropdown', () => { describe('dropdown', () => {
it('should render the dropdown action icon', () => { it('should render the dropdown action icon', () => {
const component = new JobComponent({ component = mountComponent(JobComponent, {
propsData: {
job: mockJob, job: mockJob,
isDropdown: true, isDropdown: true,
}, });
}).$mount();
expect(component.$el.querySelector('a.ci-action-icon-wrapper')).toBeDefined(); expect(component.$el.querySelector('a.ci-action-icon-wrapper')).toBeDefined();
}); });
}); });
it('should render provided class name', () => { it('should render provided class name', () => {
const component = new JobComponent({ component = mountComponent(JobComponent, {
propsData: {
job: mockJob, job: mockJob,
cssClassJobName: 'css-class-job-name', cssClassJobName: 'css-class-job-name',
}, });
}).$mount();
expect( expect(
component.$el.querySelector('a').classList.contains('css-class-job-name'), component.$el.querySelector('a').classList.contains('css-class-job-name'),
......
...@@ -3,6 +3,7 @@ require 'spec_helper' ...@@ -3,6 +3,7 @@ require 'spec_helper'
describe GoogleApi::CloudPlatform::Client do describe GoogleApi::CloudPlatform::Client do
let(:token) { 'token' } let(:token) { 'token' }
let(:client) { described_class.new(token, nil) } let(:client) { described_class.new(token, nil) }
let(:user_agent_options) { client.instance_eval { user_agent_header } }
describe '.session_key_for_redirect_uri' do describe '.session_key_for_redirect_uri' do
let(:state) { 'random_string' } let(:state) { 'random_string' }
...@@ -55,7 +56,8 @@ describe GoogleApi::CloudPlatform::Client do ...@@ -55,7 +56,8 @@ describe GoogleApi::CloudPlatform::Client do
before do before do
allow_any_instance_of(Google::Apis::ContainerV1::ContainerService) allow_any_instance_of(Google::Apis::ContainerV1::ContainerService)
.to receive(:get_zone_cluster).and_return(gke_cluster) .to receive(:get_zone_cluster).with(any_args, options: user_agent_options)
.and_return(gke_cluster)
end end
it { is_expected.to eq(gke_cluster) } it { is_expected.to eq(gke_cluster) }
...@@ -74,7 +76,8 @@ describe GoogleApi::CloudPlatform::Client do ...@@ -74,7 +76,8 @@ describe GoogleApi::CloudPlatform::Client do
before do before do
allow_any_instance_of(Google::Apis::ContainerV1::ContainerService) allow_any_instance_of(Google::Apis::ContainerV1::ContainerService)
.to receive(:create_cluster).and_return(operation) .to receive(:create_cluster).with(any_args, options: user_agent_options)
.and_return(operation)
end end
it { is_expected.to eq(operation) } it { is_expected.to eq(operation) }
...@@ -102,7 +105,8 @@ describe GoogleApi::CloudPlatform::Client do ...@@ -102,7 +105,8 @@ describe GoogleApi::CloudPlatform::Client do
before do before do
allow_any_instance_of(Google::Apis::ContainerV1::ContainerService) allow_any_instance_of(Google::Apis::ContainerV1::ContainerService)
.to receive(:get_zone_operation).and_return(operation) .to receive(:get_zone_operation).with(any_args, options: user_agent_options)
.and_return(operation)
end end
it { is_expected.to eq(operation) } it { is_expected.to eq(operation) }
...@@ -125,4 +129,18 @@ describe GoogleApi::CloudPlatform::Client do ...@@ -125,4 +129,18 @@ describe GoogleApi::CloudPlatform::Client do
it { is_expected.to be_nil } it { is_expected.to be_nil }
end end
end end
describe '#user_agent_header' do
subject { client.instance_eval { user_agent_header } }
it 'returns a RequestOptions object' do
expect(subject).to be_instance_of(Google::Apis::RequestOptions)
end
it 'has the correct GitLab version in User-Agent header' do
stub_const('Gitlab::VERSION', '10.3.0-pre')
expect(subject.header).to eq({ 'User-Agent': 'GitLab/10.3 (GPN:GitLab;)' })
end
end
end end
...@@ -1408,6 +1408,7 @@ describe Repository do ...@@ -1408,6 +1408,7 @@ describe Repository do
end end
describe '#cherry_pick' do describe '#cherry_pick' do
shared_examples 'cherry-picking a commit' do
let(:conflict_commit) { repository.commit('c642fe9b8b9f28f9225d7ea953fe14e74748d53b') } let(:conflict_commit) { repository.commit('c642fe9b8b9f28f9225d7ea953fe14e74748d53b') }
let(:pickable_commit) { repository.commit('7d3b0f7cff5f37573aea97cebfd5692ea1689924') } let(:pickable_commit) { repository.commit('7d3b0f7cff5f37573aea97cebfd5692ea1689924') }
let(:pickable_merge) { repository.commit('e56497bb5f03a90a51293fc6d516788730953899') } let(:pickable_merge) { repository.commit('e56497bb5f03a90a51293fc6d516788730953899') }
...@@ -1446,6 +1447,15 @@ describe Repository do ...@@ -1446,6 +1447,15 @@ describe Repository do
end end
end end
context 'when Gitaly cherry_pick feature is enabled' do
it_behaves_like 'cherry-picking a commit'
end
context 'when Gitaly cherry_pick feature is disabled', :disable_gitaly do
it_behaves_like 'cherry-picking a commit'
end
end
describe '#before_delete' do describe '#before_delete' do
describe 'when a repository does not exist' do describe 'when a repository does not exist' do
before do before do
......
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