Commit 60206023 authored by Robert Speicher's avatar Robert Speicher

Merge branch 'fix-trailing-slash-in-teamcity_url-3515' into 'master'

Fix trailing slash in teamcity_url

Originally opened at !2309 by @ctmay4.

As described in #3515, if you have trailing spaces in the the Teamcity server name, the service
will not work properly.  Switching from `URI.parse` to `URI.join` fixes it so that it works with
or without a trailing slash.

Fixes #3515.

See merge request !3679
parents c414e434 3ea955a6
...@@ -22,6 +22,7 @@ v 8.7.0 (unreleased) ...@@ -22,6 +22,7 @@ v 8.7.0 (unreleased)
- API: Ability to update a group (Robert Schilling) - API: Ability to update a group (Robert Schilling)
- API: Ability to move issues (Robert Schilling) - API: Ability to move issues (Robert Schilling)
- Fix Error 500 after renaming a project path (Stan Hu) - Fix Error 500 after renaming a project path (Stan Hu)
- Fix a bug whith trailing slash in teamcity_url (Charles May)
- Fix avatar stretching by providing a cropping feature - Fix avatar stretching by providing a cropping feature
- API: Expose `subscribed` for issues and merge requests (Robert Schilling) - API: Expose `subscribed` for issues and merge requests (Robert Schilling)
- Allow SAML to handle external users based on user's information !3530 - Allow SAML to handle external users based on user's information !3530
......
...@@ -85,13 +85,15 @@ class TeamcityService < CiService ...@@ -85,13 +85,15 @@ class TeamcityService < CiService
end end
def build_info(sha) def build_info(sha)
url = URI.parse("#{teamcity_url}/httpAuth/app/rest/builds/"\ url = URI.join(
"branch:unspecified:any,number:#{sha}") teamcity_url,
"/httpAuth/app/rest/builds/branch:unspecified:any,number:#{sha}"
).to_s
auth = { auth = {
username: username, username: username,
password: password, password: password
} }
@response = HTTParty.get("#{url}", verify: false, basic_auth: auth) @response = HTTParty.get(url, verify: false, basic_auth: auth)
end end
def build_page(sha, ref) def build_page(sha, ref)
...@@ -100,12 +102,14 @@ class TeamcityService < CiService ...@@ -100,12 +102,14 @@ class TeamcityService < CiService
if @response.code != 200 if @response.code != 200
# If actual build link can't be determined, # If actual build link can't be determined,
# send user to build summary page. # send user to build summary page.
"#{teamcity_url}/viewLog.html?buildTypeId=#{build_type}" URI.join(teamcity_url, "/viewLog.html?buildTypeId=#{build_type}").to_s
else else
# If actual build link is available, go to build result page. # If actual build link is available, go to build result page.
built_id = @response['build']['id'] built_id = @response['build']['id']
"#{teamcity_url}/viewLog.html?buildId=#{built_id}"\ URI.join(
"&buildTypeId=#{build_type}" teamcity_url,
"/viewLog.html?buildId=#{built_id}&buildTypeId=#{build_type}"
).to_s
end end
end end
...@@ -140,12 +144,13 @@ class TeamcityService < CiService ...@@ -140,12 +144,13 @@ class TeamcityService < CiService
branch = Gitlab::Git.ref_name(data[:ref]) branch = Gitlab::Git.ref_name(data[:ref])
self.class.post("#{teamcity_url}/httpAuth/app/rest/buildQueue", self.class.post(
body: "<build branchName=\"#{branch}\">"\ URI.join(teamcity_url, '/httpAuth/app/rest/buildQueue').to_s,
"<buildType id=\"#{build_type}\"/>"\ body: "<build branchName=\"#{branch}\">"\
'</build>', "<buildType id=\"#{build_type}\"/>"\
headers: { 'Content-type' => 'application/xml' }, '</build>',
basic_auth: auth headers: { 'Content-type' => 'application/xml' },
) basic_auth: auth
)
end end
end end
...@@ -21,73 +21,220 @@ ...@@ -21,73 +21,220 @@
require 'spec_helper' require 'spec_helper'
describe TeamcityService, models: true do describe TeamcityService, models: true do
describe "Associations" do describe 'Associations' do
it { is_expected.to belong_to :project } it { is_expected.to belong_to :project }
it { is_expected.to have_one :service_hook } it { is_expected.to have_one :service_hook }
end end
describe "Execute" do describe 'Validations' do
let(:user) { create(:user) } describe '#teamcity_url' do
let(:project) { create(:project) } it 'does not validate the presence of teamcity_url if service is not active' do
teamcity_service = service
context "when a password was previously set" do teamcity_service.active = false
before do
@teamcity_service = TeamcityService.create( expect(teamcity_service).not_to validate_presence_of(:teamcity_url)
project: create(:project),
properties: {
teamcity_url: 'http://gitlab.com',
username: 'mic',
password: "password"
}
)
end end
it "reset password if url changed" do it 'validates the presence of teamcity_url if service is active' do
@teamcity_service.teamcity_url = 'http://gitlab1.com' teamcity_service = service
@teamcity_service.save teamcity_service.active = true
expect(@teamcity_service.password).to be_nil
expect(teamcity_service).to validate_presence_of(:teamcity_url)
end
end
describe '#build_type' do
it 'does not validate the presence of build_type if service is not active' do
teamcity_service = service
teamcity_service.active = false
expect(teamcity_service).not_to validate_presence_of(:build_type)
end
it 'validates the presence of build_type if service is active' do
teamcity_service = service
teamcity_service.active = true
expect(teamcity_service).to validate_presence_of(:build_type)
end end
end
it "does not reset password if username changed" do
@teamcity_service.username = "some_name" describe '#username' do
@teamcity_service.save it 'does not validate the presence of username if service is not active' do
expect(@teamcity_service.password).to eq("password") teamcity_service = service
teamcity_service.active = false
expect(teamcity_service).not_to validate_presence_of(:username)
end end
it "does not reset password if new url is set together with password, even if it's the same password" do it 'does not validate the presence of username if username is nil' do
@teamcity_service.teamcity_url = 'http://gitlab_edited.com' teamcity_service = service
@teamcity_service.password = 'password' teamcity_service.active = true
@teamcity_service.save teamcity_service.password = nil
expect(@teamcity_service.password).to eq("password")
expect(@teamcity_service.teamcity_url).to eq("http://gitlab_edited.com") expect(teamcity_service).not_to validate_presence_of(:username)
end end
it "should reset password if url changed, even if setter called multiple times" do it 'validates the presence of username if service is active and username is present' do
@teamcity_service.teamcity_url = 'http://gitlab1.com' teamcity_service = service
@teamcity_service.teamcity_url = 'http://gitlab1.com' teamcity_service.active = true
@teamcity_service.save teamcity_service.password = 'secret'
expect(@teamcity_service.password).to be_nil
expect(teamcity_service).to validate_presence_of(:username)
end end
end end
context "when no password was previously set" do describe '#password' do
before do it 'does not validate the presence of password if service is not active' do
@teamcity_service = TeamcityService.create( teamcity_service = service
project: create(:project), teamcity_service.active = false
properties: {
teamcity_url: 'http://gitlab.com', expect(teamcity_service).not_to validate_presence_of(:password)
username: 'mic' end
}
) it 'does not validate the presence of password if username is nil' do
teamcity_service = service
teamcity_service.active = true
teamcity_service.username = nil
expect(teamcity_service).not_to validate_presence_of(:password)
end end
it "saves password if new url is set together with password" do it 'validates the presence of password if service is active and username is present' do
@teamcity_service.teamcity_url = 'http://gitlab_edited.com' teamcity_service = service
@teamcity_service.password = 'password' teamcity_service.active = true
@teamcity_service.save teamcity_service.username = 'john'
expect(@teamcity_service.password).to eq("password")
expect(@teamcity_service.teamcity_url).to eq("http://gitlab_edited.com") expect(teamcity_service).to validate_presence_of(:password)
end end
end end
end end
describe 'Callbacks' do
describe 'before_update :reset_password' do
context 'when a password was previously set' do
it 'resets password if url changed' do
teamcity_service = service
teamcity_service.teamcity_url = 'http://gitlab1.com'
teamcity_service.save
expect(teamcity_service.password).to be_nil
end
it 'does not reset password if username changed' do
teamcity_service = service
teamcity_service.username = 'some_name'
teamcity_service.save
expect(teamcity_service.password).to eq('password')
end
it "does not reset password if new url is set together with password, even if it's the same password" do
teamcity_service = service
teamcity_service.teamcity_url = 'http://gitlab_edited.com'
teamcity_service.password = 'password'
teamcity_service.save
expect(teamcity_service.password).to eq('password')
expect(teamcity_service.teamcity_url).to eq('http://gitlab_edited.com')
end
end
it 'saves password if new url is set together with password when no password was previously set' do
teamcity_service = service
teamcity_service.password = nil
teamcity_service.teamcity_url = 'http://gitlab_edited.com'
teamcity_service.password = 'password'
teamcity_service.save
expect(teamcity_service.password).to eq('password')
expect(teamcity_service.teamcity_url).to eq('http://gitlab_edited.com')
end
end
end
describe '#build_page' do
it 'returns a specific URL when status is 500' do
stub_request(status: 500)
expect(service.build_page('123', 'unused')).to eq('http://gitlab.com/viewLog.html?buildTypeId=foo')
end
it 'returns a build URL when teamcity_url has no trailing slash' do
stub_request(body: %Q({"build":{"id":"666"}}))
expect(service(teamcity_url: 'http://gitlab.com').build_page('123', 'unused')).to eq('http://gitlab.com/viewLog.html?buildId=666&buildTypeId=foo')
end
it 'returns a build URL when teamcity_url has a trailing slash' do
stub_request(body: %Q({"build":{"id":"666"}}))
expect(service(teamcity_url: 'http://gitlab.com/').build_page('123', 'unused')).to eq('http://gitlab.com/viewLog.html?buildId=666&buildTypeId=foo')
end
end
describe '#commit_status' do
it 'sets commit status to :error when status is 500' do
stub_request(status: 500)
expect(service.commit_status('123', 'unused')).to eq(:error)
end
it 'sets commit status to "pending" when status is 404' do
stub_request(status: 404)
expect(service.commit_status('123', 'unused')).to eq('pending')
end
it 'sets commit status to "success" when build status contains SUCCESS' do
stub_request(build_status: 'YAY SUCCESS!')
expect(service.commit_status('123', 'unused')).to eq('success')
end
it 'sets commit status to "failed" when build status contains FAILURE' do
stub_request(build_status: 'NO FAILURE!')
expect(service.commit_status('123', 'unused')).to eq('failed')
end
it 'sets commit status to "pending" when build status contains Pending' do
stub_request(build_status: 'NO Pending!')
expect(service.commit_status('123', 'unused')).to eq('pending')
end
it 'sets commit status to :error when build status is unknown' do
stub_request(build_status: 'FOO BAR!')
expect(service.commit_status('123', 'unused')).to eq(:error)
end
end
def service(teamcity_url: 'http://gitlab.com')
described_class.create(
project: build_stubbed(:empty_project),
properties: {
teamcity_url: teamcity_url,
username: 'mic',
password: 'password',
build_type: 'foo'
}
)
end
def stub_request(status: 200, body: nil, build_status: 'success')
teamcity_full_url = 'http://mic:password@gitlab.com/httpAuth/app/rest/builds/branch:unspecified:any,number:123'
body ||= %Q({"build":{"status":"#{build_status}","id":"666"}})
WebMock.stub_request(:get, teamcity_full_url).to_return(
status: status,
headers: { 'Content-Type' => 'application/json' },
body: body
)
end
end end
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