Merge "gitlab: Add access token name, Update docs, Fix webhook"

This commit is contained in:
Zuul 2021-07-27 18:47:33 +00:00 committed by Gerrit Code Review
commit e6b27dd8c0
5 changed files with 137 additions and 10 deletions

View File

@ -17,20 +17,35 @@ Zuul needs to interact with projects by:
- receiving events via web-hooks - receiving events via web-hooks
- performing actions via the API - performing actions via the API
The Zuul user's API token configured in zuul.conf must have the web-hooks
following ACL rights: "api". The API token must be created in user Settings, ^^^^^^^^^
Access tokens.
Each project to be integrated with Zuul needs in "Settings/Webhooks": Projects to be integrated with Zuul needs to send events using webhooks.
This can be enabled at Group level or Project level in "Settings/Webhooks"
- "URL" set to - "URL" set to
``http://<zuul-web>/zuul/api/connection/<conn-name>/payload`` ``http://<zuul-web>/api/connection/<conn-name>/payload``
- "Merge request events" set to "on" - "Merge request events" set to "on"
- "Push events" set to "on" - "Push events" set to "on"
- "Tag push events" set to "on" - "Tag push events" set to "on"
- "Comments" set to "on" - "Comments" set to "on"
- Define a "Secret Token" - Define a "Secret Token"
API
^^^
| Even though bot users exist: https://docs.gitlab.com/ce/user/project/settings/project_access_tokens.html#project-bot-users
| They are only available at project level.
In order to manage multiple projects using a single connection, Zuul needs a
global access to projects, which can only be achieved by creating a specific
Zuul user. This user counts as a licensed seat.
The API token must be created in user Settings, Access tokens. The Zuul user's
API token configured in zuul.conf must have the following ACL rights: "api".
Connection Configuration Connection Configuration
------------------------ ------------------------
@ -45,13 +60,18 @@ The supported options in ``zuul.conf`` connections are:
The connection must set ``driver=gitlab`` for GitLab connections. The connection must set ``driver=gitlab`` for GitLab connections.
.. attr:: api_token_name
The user's personal access token name (Used if **cloneurl** is http(s))
Set this parameter if authentication to clone projects is required
.. attr:: api_token .. attr:: api_token
The user's API token. The user's personal access token
.. attr:: webhook_token .. attr:: webhook_token
The project's webhook secret token. The webhook secret token.
.. attr:: server .. attr:: server
:default: gitlab.com :default: gitlab.com
@ -75,10 +95,19 @@ The supported options in ``zuul.conf`` connections are:
Path to the GitLab web and API interface. Path to the GitLab web and API interface.
.. attr:: sshkey
Path to SSH key to use (Used if **cloneurl** is ssh)
.. attr:: cloneurl .. attr:: cloneurl
:default: {baseurl} :default: {baseurl}
Path to the GitLab Git repositories. Used to clone. Omit to clone using http(s) or set to ``ssh://git@{server}``.
If **api_token_name** is set and **cloneurl** is either omitted or is
set without credentials, **cloneurl** will be modified to use credentials
as this: ``http(s)://<api_token_name>:<api_token>@<server>``.
If **cloneurl** is defined with credentials, it will be used as is,
without modification from the driver.
Trigger Configuration Trigger Configuration

View File

@ -1836,6 +1836,9 @@ class FakeGitlabConnection(gitlabconnection.GitlabConnection):
def getGitUrl(self, project): def getGitUrl(self, project):
return 'file://' + os.path.join(self.upstream_root, project.name) return 'file://' + os.path.join(self.upstream_root, project.name)
def real_getGitUrl(self, project):
return super(FakeGitlabConnection, self).getGitUrl(project)
def openFakeMergeRequest(self, project, def openFakeMergeRequest(self, project,
branch, title, description='', files=[]): branch, title, description='', files=[]):
self.mr_number += 1 self.mr_number += 1

View File

@ -20,3 +20,28 @@ api_token=0000000000000000000000000000000000000000
[database] [database]
dburi=$MYSQL_FIXTURE_DBURI$ dburi=$MYSQL_FIXTURE_DBURI$
[connection gitlab2]
driver=gitlab
server=gitlabtwo
api_token=2222
cloneurl=http://myusername:2222@gitlab
[connection gitlab3]
driver=gitlab
server=gitlabthree
api_token_name=tokenname3
api_token=3333
cloneurl=http://myusername:2222@gitlabthree
[connection gitlab4]
driver=gitlab
server=gitlabfour
api_token_name=tokenname4
api_token=444
[connection gitlab5]
driver=gitlab
server=gitlabfive
api_token_name=tokenname5
api_token=555
cloneurl=http://gitlabfivvve

View File

@ -703,6 +703,61 @@ class TestGitlabDriver(ZuulTestCase):
self.assertTrue(A.is_merged) self.assertTrue(A.is_merged)
self.assertTrue(B.is_merged) self.assertTrue(B.is_merged)
@simple_layout('layouts/crd-gitlab.yaml', driver='gitlab')
def test_api_token(self):
tenant = self.scheds.first.sched.abide.tenants.get('tenant-one')
_, project = tenant.getProject('org/project1')
project_git_url = self.fake_gitlab.real_getGitUrl(project)
# cloneurl created from config 'server' should be used
# without credentials
self.assertEqual("https://gitlab/org/project1.git", project_git_url)
@simple_layout('layouts/crd-gitlab.yaml', driver='gitlab2')
def test_api_token_cloneurl(self):
tenant = self.scheds.first.sched.abide.tenants.get('tenant-one')
_, project = tenant.getProject('org/project1')
project_git_url = self.fake_gitlab2.real_getGitUrl(project)
# cloneurl from config file should be used as it defines token name and
# secret
self.assertEqual("http://myusername:2222@gitlab/org/project1.git",
project_git_url)
@simple_layout('layouts/crd-gitlab.yaml', driver='gitlab3')
def test_api_token_name_cloneurl(self):
tenant = self.scheds.first.sched.abide.tenants.get('tenant-one')
_, project = tenant.getProject('org/project1')
project_git_url = self.fake_gitlab3.real_getGitUrl(project)
# cloneurl from config file should be used as it defines token name and
# secret, even if token name and token secret are defined
self.assertEqual("http://myusername:2222@gitlabthree/org/project1.git",
project_git_url)
@simple_layout('layouts/crd-gitlab.yaml', driver='gitlab4')
def test_api_token_name(self):
tenant = self.scheds.first.sched.abide.tenants.get('tenant-one')
_, project = tenant.getProject('org/project1')
project_git_url = self.fake_gitlab4.real_getGitUrl(project)
# cloneurl is not set, generate one from token name, token secret and
# server
self.assertEqual("https://tokenname4:444@gitlabfour/org/project1.git",
project_git_url)
@simple_layout('layouts/crd-gitlab.yaml', driver='gitlab5')
def test_api_token_name_cloneurl_server(self):
tenant = self.scheds.first.sched.abide.tenants.get('tenant-one')
_, project = tenant.getProject('org/project1')
project_git_url = self.fake_gitlab5.real_getGitUrl(project)
# cloneurl defines a url, without credentials. As token name is
# set, include token name and secret in cloneurl, 'server' is
# overwritten
self.assertEqual("http://tokenname5:555@gitlabfivvve/org/project1.git",
project_git_url)
class TestGitlabUnprotectedBranches(ZuulTestCase): class TestGitlabUnprotectedBranches(ZuulTestCase):
config_file = 'zuul-gitlab-driver.conf' config_file = 'zuul-gitlab-driver.conf'

View File

@ -19,6 +19,7 @@ import cherrypy
import voluptuous as v import voluptuous as v
import time import time
import uuid import uuid
import re
import requests import requests
import dateutil.parser import dateutil.parser
@ -396,6 +397,8 @@ class GitlabConnection(CachedBranchConnection):
'canonical_hostname', self.server) 'canonical_hostname', self.server)
self.webhook_token = self.connection_config.get( self.webhook_token = self.connection_config.get(
'webhook_token', '') 'webhook_token', '')
self.api_token_name = self.connection_config.get(
'api_token_name', '')
self.api_token = self.connection_config.get( self.api_token = self.connection_config.get(
'api_token', '') 'api_token', '')
self.gl_client = GitlabAPIClient(self.baseurl, self.api_token) self.gl_client = GitlabAPIClient(self.baseurl, self.api_token)
@ -458,7 +461,19 @@ class GitlabConnection(CachedBranchConnection):
return '%s/%s/merge_requests/%s' % (self.baseurl, project, number) return '%s/%s/merge_requests/%s' % (self.baseurl, project, number)
def getGitUrl(self, project): def getGitUrl(self, project):
return '%s/%s.git' % (self.cloneurl, project.name) cloneurl = '%s/%s.git' % (self.cloneurl, project.name)
# https://gitlab.com/gitlab-org/gitlab/-/issues/212953
# any login name can be used, but it's likely going to be reduce to
# username/token-name
if (cloneurl.startswith('http') and self.api_token_name != '' and
not re.match("http?://.+:.+@.+", cloneurl)):
cloneurl = '%s://%s:%s@%s/%s.git' % (
self.cloneurl.split('://')[0],
self.api_token_name,
self.api_token,
self.cloneurl.split('://')[1],
project.name)
return cloneurl
def getChange(self, event, refresh=False): def getChange(self, event, refresh=False):
project = self.source.getProject(event.project_name) project = self.source.getProject(event.project_name)