Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Support
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in / Register
Toggle navigation
G
gitlab-ce
Project overview
Project overview
Details
Activity
Releases
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Issues
0
Issues
0
List
Boards
Labels
Milestones
Merge Requests
1
Merge Requests
1
Analytics
Analytics
Repository
Value Stream
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Create a new issue
Commits
Issue Boards
Open sidebar
nexedi
gitlab-ce
Commits
12063651
Commit
12063651
authored
Apr 15, 2021
by
Emily Ring
Committed by
Mark Chao
Apr 15, 2021
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
New /job/allowed_agents REST endpoint
parent
760d6c5f
Changes
12
Hide whitespace changes
Inline
Side-by-side
Showing
12 changed files
with
316 additions
and
15 deletions
+316
-15
doc/api/jobs.md
doc/api/jobs.md
+80
-0
ee/app/finders/clusters/deployable_agents_finder.rb
ee/app/finders/clusters/deployable_agents_finder.rb
+23
-0
ee/changelogs/unreleased/324269-allowed-agents.yml
ee/changelogs/unreleased/324269-allowed-agents.yml
+5
-0
ee/lib/ee/api/jobs.rb
ee/lib/ee/api/jobs.rb
+35
-0
ee/spec/finders/clusters/deployable_agents_finder_spec.rb
ee/spec/finders/clusters/deployable_agents_finder_spec.rb
+29
-0
ee/spec/requests/api/jobs_spec.rb
ee/spec/requests/api/jobs_spec.rb
+84
-0
lib/api/entities/clusters/agent.rb
lib/api/entities/clusters/agent.rb
+12
-0
lib/api/entities/job_request/job_info.rb
lib/api/entities/job_request/job_info.rb
+1
-1
lib/api/jobs.rb
lib/api/jobs.rb
+17
-13
spec/lib/api/entities/clusters/agent_spec.rb
spec/lib/api/entities/clusters/agent_spec.rb
+16
-0
spec/requests/api/ci/runner/jobs_request_post_spec.rb
spec/requests/api/ci/runner/jobs_request_post_spec.rb
+2
-1
spec/requests/api/jobs_spec.rb
spec/requests/api/jobs_spec.rb
+12
-0
No files found.
doc/api/jobs.md
View file @
12063651
...
@@ -459,6 +459,86 @@ Example of response
...
@@ -459,6 +459,86 @@ Example of response
}
}
```
```
## Get Kubernetes Agents by `CI_JOB_TOKEN` **(PREMIUM)**
> [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/324269) in [GitLab Premium](https://about.gitlab.com/pricing/) 13.11.
Retrieve the job that generated the
`CI_JOB_TOKEN`
, along with a list of allowed GitLab
Kubernetes Agents.
```
plaintext
GET /job/allowed_agents
```
Supported attributes:
| Attribute | Type | Required | Description |
|:------------ |:---------|:---------|:----------------------|
|
`CI_JOB_TOKEN`
| string | yes | Token value associated with the GitLab-provided
`CI_JOB_TOKEN`
variable. |
Example request:
```
shell
curl
--header
"JOB-TOKEN: <CI_JOB_TOKEN>"
"https://gitlab.example.com/api/v4/job/allowed_agents"
curl
"https://gitlab.example.com/api/v4/job/allowed_agents?job_token=<CI_JOB_TOKEN>"
```
Example response:
```
json
{
"allowed_agents"
:
[
{
"id"
:
1
,
"config_project"
:
{
"id"
:
1
,
"description"
:
null
,
"name"
:
"project1"
,
"name_with_namespace"
:
"John Doe2 / project1"
,
"path"
:
"project1"
,
"path_with_namespace"
:
"namespace1/project1"
,
"created_at"
:
"2021-03-26T14:51:50.579Z"
}
}
],
"job"
:
{
"id"
:
1
,
"name"
:
"test"
,
"stage"
:
"test"
,
"project_id"
:
1
,
"project_name"
:
"project1"
},
"pipeline"
:
{
"id"
:
1
,
"project_id"
:
1
,
"sha"
:
"b83d6e391c22777fca1ed3012fce84f633d7fed0"
,
"ref"
:
"master"
,
"status"
:
"pending"
,
"created_at"
:
"2021-03-26T14:51:51.107Z"
,
"updated_at"
:
"2021-03-26T14:51:51.107Z"
,
"web_url"
:
"http://localhost/namespace1/project1/-/pipelines/1"
},
"project"
:
{
"id"
:
1
,
"description"
:
null
,
"name"
:
"project1"
,
"name_with_namespace"
:
"John Doe2 / project1"
,
"path"
:
"project1"
,
"path_with_namespace"
:
"namespace1/project1"
,
"created_at"
:
"2021-03-26T14:51:50.579Z"
},
"user"
:
{
"id"
:
2
,
"name"
:
"John Doe3"
,
"username"
:
"user2"
,
"state"
:
"active"
,
"avatar_url"
:
"https://www.gravatar.com/avatar/10fc7f102b"
,
"web_url"
:
"http://localhost/user2"
}
}
```
## Get a single job
## Get a single job
Get a single job of a project
Get a single job of a project
...
...
ee/app/finders/clusters/deployable_agents_finder.rb
0 → 100644
View file @
12063651
# frozen_string_literal: true
module
Clusters
class
DeployableAgentsFinder
def
initialize
(
project
)
@project
=
project
end
def
execute
return
::
Clusters
::
Agent
.
none
unless
allowed?
project
.
cluster_agents
.
ordered_by_name
end
private
attr_reader
:project
def
allowed?
project
.
licensed_feature_available?
(
:cluster_agents
)
end
end
end
ee/changelogs/unreleased/324269-allowed-agents.yml
0 → 100644
View file @
12063651
---
title
:
New allowed_agents REST endpoint
merge_request
:
56700
author
:
type
:
added
ee/lib/ee/api/jobs.rb
0 → 100644
View file @
12063651
# frozen_string_literal: true
module
EE
module
API
module
Jobs
extend
ActiveSupport
::
Concern
prepended
do
resource
:job
do
desc
'Get current agents'
do
detail
'Retrieves a list of agents for the given job token'
end
route_setting
:authentication
,
job_token_allowed:
true
get
'/allowed_agents'
,
feature_category: :kubernetes_management
do
validate_current_authenticated_job
status
200
pipeline
=
current_authenticated_job
.
pipeline
project
=
current_authenticated_job
.
project
allowed_agents
=
::
Clusters
::
DeployableAgentsFinder
.
new
(
project
).
execute
{
allowed_agents:
::
API
::
Entities
::
Clusters
::
Agent
.
represent
(
allowed_agents
),
job:
::
API
::
Entities
::
JobRequest
::
JobInfo
.
represent
(
current_authenticated_job
),
pipeline:
::
API
::
Entities
::
Ci
::
PipelineBasic
.
represent
(
pipeline
),
project:
::
API
::
Entities
::
ProjectIdentity
.
represent
(
project
),
user:
::
API
::
Entities
::
UserBasic
.
represent
(
current_user
)
}
end
end
end
end
end
end
ee/spec/finders/clusters/deployable_agents_finder_spec.rb
0 → 100644
View file @
12063651
# frozen_string_literal: true
require
'spec_helper'
RSpec
.
describe
Clusters
::
DeployableAgentsFinder
do
describe
'#execute'
do
let_it_be
(
:agent
)
{
create
(
:cluster_agent
)
}
let
(
:project
)
{
agent
.
project
}
subject
{
described_class
.
new
(
project
).
execute
}
before
do
stub_licensed_features
(
cluster_agents:
feature_available
)
end
context
'feature is available'
do
let
(
:feature_available
)
{
true
}
it
{
is_expected
.
to
contain_exactly
(
agent
)
}
end
context
'feature is not available'
do
let
(
:feature_available
)
{
false
}
it
{
is_expected
.
to
be_empty
}
end
end
end
ee/spec/requests/api/jobs_spec.rb
View file @
12063651
...
@@ -25,6 +25,90 @@ RSpec.describe API::Jobs do
...
@@ -25,6 +25,90 @@ RSpec.describe API::Jobs do
project
.
add_developer
(
developer
)
project
.
add_developer
(
developer
)
end
end
describe
'GET /job/allowed_agents'
do
let_it_be
(
:agent
)
{
create
(
:cluster_agent
,
project:
project
)
}
let
(
:api_user
)
{
developer
}
let
(
:headers
)
{
{
API
::
Helpers
::
Runner
::
JOB_TOKEN_HEADER
=>
job
.
token
}
}
let
(
:job
)
{
create
(
:ci_build
,
:artifacts
,
pipeline:
pipeline
,
user:
api_user
,
status:
job_status
)
}
let
(
:job_status
)
{
'running'
}
let
(
:params
)
{
{}
}
subject
do
get
api
(
'/job/allowed_agents'
),
headers:
headers
,
params:
params
end
before
do
stub_licensed_features
(
cluster_agents:
true
)
agent
subject
end
context
'when token is valid and user is authorized'
do
it
'returns agent info'
,
:aggregate_failures
do
expect
(
response
).
to
have_gitlab_http_status
(
:ok
)
expect
(
json_response
.
dig
(
'job'
,
'id'
)).
to
eq
(
job
.
id
)
expect
(
json_response
.
dig
(
'pipeline'
,
'id'
)).
to
eq
(
job
.
pipeline_id
)
expect
(
json_response
.
dig
(
'project'
,
'id'
)).
to
eq
(
job
.
project_id
)
expect
(
json_response
.
dig
(
'user'
,
'username'
)).
to
eq
(
api_user
.
username
)
expect
(
json_response
[
'allowed_agents'
]).
to
match_array
([
{
'id'
=>
agent
.
id
,
'config_project'
=>
a_hash_including
(
'id'
=>
agent
.
project_id
)
}
])
end
context
'when passing the token as params'
do
let
(
:headers
)
{
{}
}
let
(
:params
)
{
{
job_token:
job
.
token
}
}
it
'returns agent info'
,
:aggregate_failures
do
expect
(
response
).
to
have_gitlab_http_status
(
:ok
)
expect
(
json_response
.
dig
(
'job'
,
'id'
)).
to
eq
(
job
.
id
)
expect
(
json_response
.
dig
(
'pipeline'
,
'id'
)).
to
eq
(
job
.
pipeline_id
)
expect
(
json_response
.
dig
(
'project'
,
'id'
)).
to
eq
(
job
.
project_id
)
expect
(
json_response
.
dig
(
'user'
,
'username'
)).
to
eq
(
api_user
.
username
)
expect
(
json_response
[
'allowed_agents'
]).
to
match_array
([
{
'id'
=>
agent
.
id
,
'config_project'
=>
a_hash_including
(
'id'
=>
agent
.
project_id
)
}
])
end
end
end
context
'when user is anonymous'
do
let
(
:api_user
)
{
nil
}
it
'returns unauthorized'
do
expect
(
response
).
to
have_gitlab_http_status
(
:unauthorized
)
end
end
context
'when token is invalid because job has finished'
do
let
(
:job_status
)
{
'success'
}
it
'returns unauthorized'
do
expect
(
response
).
to
have_gitlab_http_status
(
:unauthorized
)
end
end
context
'when token is invalid'
do
let
(
:headers
)
{
{
API
::
Helpers
::
Runner
::
JOB_TOKEN_HEADER
=>
'bad_token'
}
}
it
'returns unauthorized'
do
expect
(
response
).
to
have_gitlab_http_status
(
:unauthorized
)
end
end
context
'when token is valid but not CI_JOB_TOKEN'
do
let
(
:token
)
{
create
(
:personal_access_token
,
user:
developer
)
}
let
(
:headers
)
{
{
'Private-Token'
=>
token
.
token
}
}
it
'returns not found'
do
expect
(
response
).
to
have_gitlab_http_status
(
:not_found
)
end
end
end
describe
'GET /projects/:id/jobs/:job_id/artifacts'
do
describe
'GET /projects/:id/jobs/:job_id/artifacts'
do
let
(
:job
)
{
create
(
:ci_build
,
:artifacts
,
pipeline:
pipeline
,
user:
api_user
,
status: :running
)
}
let
(
:job
)
{
create
(
:ci_build
,
:artifacts
,
pipeline:
pipeline
,
user:
api_user
,
status: :running
)
}
...
...
lib/api/entities/clusters/agent.rb
0 → 100644
View file @
12063651
# frozen_string_literal: true
module
API
module
Entities
module
Clusters
class
Agent
<
Grape
::
Entity
expose
:id
expose
:project
,
with:
Entities
::
ProjectIdentity
,
as: :config_project
end
end
end
end
lib/api/entities/job_request/job_info.rb
View file @
12063651
...
@@ -4,7 +4,7 @@ module API
...
@@ -4,7 +4,7 @@ module API
module
Entities
module
Entities
module
JobRequest
module
JobRequest
class
JobInfo
<
Grape
::
Entity
class
JobInfo
<
Grape
::
Entity
expose
:name
,
:stage
expose
:
id
,
:
name
,
:stage
expose
:project_id
,
:project_name
expose
:project_id
,
:project_name
end
end
end
end
...
...
lib/api/jobs.rb
View file @
12063651
...
@@ -6,8 +6,6 @@ module API
...
@@ -6,8 +6,6 @@ module API
before
{
authenticate!
}
before
{
authenticate!
}
feature_category
:continuous_integration
resource
:projects
,
requirements:
API
::
NAMESPACE_OR_PROJECT_REQUIREMENTS
do
resource
:projects
,
requirements:
API
::
NAMESPACE_OR_PROJECT_REQUIREMENTS
do
params
do
params
do
requires
:id
,
type:
String
,
desc:
'The ID of a project'
requires
:id
,
type:
String
,
desc:
'The ID of a project'
...
@@ -40,7 +38,7 @@ module API
...
@@ -40,7 +38,7 @@ module API
use
:pagination
use
:pagination
end
end
# rubocop: disable CodeReuse/ActiveRecord
# rubocop: disable CodeReuse/ActiveRecord
get
':id/jobs'
do
get
':id/jobs'
,
feature_category: :continuous_integration
do
authorize_read_builds!
authorize_read_builds!
builds
=
user_project
.
builds
.
order
(
'id DESC'
)
builds
=
user_project
.
builds
.
order
(
'id DESC'
)
...
@@ -57,7 +55,7 @@ module API
...
@@ -57,7 +55,7 @@ module API
params
do
params
do
requires
:job_id
,
type:
Integer
,
desc:
'The ID of a job'
requires
:job_id
,
type:
Integer
,
desc:
'The ID of a job'
end
end
get
':id/jobs/:job_id'
do
get
':id/jobs/:job_id'
,
feature_category: :continuous_integration
do
authorize_read_builds!
authorize_read_builds!
build
=
find_build!
(
params
[
:job_id
])
build
=
find_build!
(
params
[
:job_id
])
...
@@ -72,7 +70,7 @@ module API
...
@@ -72,7 +70,7 @@ module API
params
do
params
do
requires
:job_id
,
type:
Integer
,
desc:
'The ID of a job'
requires
:job_id
,
type:
Integer
,
desc:
'The ID of a job'
end
end
get
':id/jobs/:job_id/trace'
do
get
':id/jobs/:job_id/trace'
,
feature_category: :continuous_integration
do
authorize_read_builds!
authorize_read_builds!
build
=
find_build!
(
params
[
:job_id
])
build
=
find_build!
(
params
[
:job_id
])
...
@@ -94,7 +92,7 @@ module API
...
@@ -94,7 +92,7 @@ module API
params
do
params
do
requires
:job_id
,
type:
Integer
,
desc:
'The ID of a job'
requires
:job_id
,
type:
Integer
,
desc:
'The ID of a job'
end
end
post
':id/jobs/:job_id/cancel'
do
post
':id/jobs/:job_id/cancel'
,
feature_category: :continuous_integration
do
authorize_update_builds!
authorize_update_builds!
build
=
find_build!
(
params
[
:job_id
])
build
=
find_build!
(
params
[
:job_id
])
...
@@ -111,7 +109,7 @@ module API
...
@@ -111,7 +109,7 @@ module API
params
do
params
do
requires
:job_id
,
type:
Integer
,
desc:
'The ID of a build'
requires
:job_id
,
type:
Integer
,
desc:
'The ID of a build'
end
end
post
':id/jobs/:job_id/retry'
do
post
':id/jobs/:job_id/retry'
,
feature_category: :continuous_integration
do
authorize_update_builds!
authorize_update_builds!
build
=
find_build!
(
params
[
:job_id
])
build
=
find_build!
(
params
[
:job_id
])
...
@@ -129,7 +127,7 @@ module API
...
@@ -129,7 +127,7 @@ module API
params
do
params
do
requires
:job_id
,
type:
Integer
,
desc:
'The ID of a build'
requires
:job_id
,
type:
Integer
,
desc:
'The ID of a build'
end
end
post
':id/jobs/:job_id/erase'
do
post
':id/jobs/:job_id/erase'
,
feature_category: :continuous_integration
do
authorize_update_builds!
authorize_update_builds!
build
=
find_build!
(
params
[
:job_id
])
build
=
find_build!
(
params
[
:job_id
])
...
@@ -148,7 +146,7 @@ module API
...
@@ -148,7 +146,7 @@ module API
requires
:job_id
,
type:
Integer
,
desc:
'The ID of a Job'
requires
:job_id
,
type:
Integer
,
desc:
'The ID of a Job'
end
end
post
":id/jobs/:job_id/play"
do
post
":id/jobs/:job_id/play"
,
feature_category: :continuous_integration
do
authorize_read_builds!
authorize_read_builds!
job
=
find_job!
(
params
[
:job_id
])
job
=
find_job!
(
params
[
:job_id
])
...
@@ -174,10 +172,8 @@ module API
...
@@ -174,10 +172,8 @@ module API
success
Entities
::
Ci
::
Job
success
Entities
::
Ci
::
Job
end
end
route_setting
:authentication
,
job_token_allowed:
true
route_setting
:authentication
,
job_token_allowed:
true
get
do
get
''
,
feature_category: :continuous_integration
do
# current_authenticated_job will be nil if user is using
validate_current_authenticated_job
# a valid authentication that is not CI_JOB_TOKEN
not_found!
(
'Job'
)
unless
current_authenticated_job
present
current_authenticated_job
,
with:
Entities
::
Ci
::
Job
present
current_authenticated_job
,
with:
Entities
::
Ci
::
Job
end
end
...
@@ -196,6 +192,14 @@ module API
...
@@ -196,6 +192,14 @@ module API
builds
.
where
(
status:
available_statuses
&&
scope
)
builds
.
where
(
status:
available_statuses
&&
scope
)
end
end
# rubocop: enable CodeReuse/ActiveRecord
# rubocop: enable CodeReuse/ActiveRecord
def
validate_current_authenticated_job
# current_authenticated_job will be nil if user is using
# a valid authentication (like PRIVATE-TOKEN) that is not CI_JOB_TOKEN
not_found!
(
'Job'
)
unless
current_authenticated_job
end
end
end
end
end
end
end
API
::
Jobs
.
prepend_if_ee
(
'EE::API::Jobs'
)
spec/lib/api/entities/clusters/agent_spec.rb
0 → 100644
View file @
12063651
# frozen_string_literal: true
require
'spec_helper'
RSpec
.
describe
API
::
Entities
::
Clusters
::
Agent
do
let_it_be
(
:cluster_agent
)
{
create
(
:cluster_agent
)
}
subject
{
described_class
.
new
(
cluster_agent
).
as_json
}
it
'includes basic fields'
do
expect
(
subject
).
to
include
(
id:
cluster_agent
.
id
,
config_project:
a_hash_including
(
id:
cluster_agent
.
project_id
)
)
end
end
spec/requests/api/ci/runner/jobs_request_post_spec.rb
View file @
12063651
...
@@ -143,7 +143,8 @@ RSpec.describe API::Ci::Runner, :clean_gitlab_redis_shared_state do
...
@@ -143,7 +143,8 @@ RSpec.describe API::Ci::Runner, :clean_gitlab_redis_shared_state do
context
'when there is a pending job'
do
context
'when there is a pending job'
do
let
(
:expected_job_info
)
do
let
(
:expected_job_info
)
do
{
'name'
=>
job
.
name
,
{
'id'
=>
job
.
id
,
'name'
=>
job
.
name
,
'stage'
=>
job
.
stage
,
'stage'
=>
job
.
stage
,
'project_id'
=>
job
.
project
.
id
,
'project_id'
=>
job
.
project
.
id
,
'project_name'
=>
job
.
project
.
name
}
'project_name'
=>
job
.
project
.
name
}
...
...
spec/requests/api/jobs_spec.rb
View file @
12063651
...
@@ -100,6 +100,18 @@ RSpec.describe API::Jobs do
...
@@ -100,6 +100,18 @@ RSpec.describe API::Jobs do
end
end
end
end
context
'when token is valid but not CI_JOB_TOKEN'
do
let
(
:token
)
{
create
(
:personal_access_token
,
user:
user
)
}
include_context
'with auth headers'
do
let
(
:header
)
{
{
'Private-Token'
=>
token
.
token
}
}
end
it
'returns not found'
do
expect
(
response
).
to
have_gitlab_http_status
(
:not_found
)
end
end
context
'with job token authentication header'
do
context
'with job token authentication header'
do
include_context
'with auth headers'
do
include_context
'with auth headers'
do
let
(
:header
)
{
{
API
::
Helpers
::
Runner
::
JOB_TOKEN_HEADER
=>
running_job
.
token
}
}
let
(
:header
)
{
{
API
::
Helpers
::
Runner
::
JOB_TOKEN_HEADER
=>
running_job
.
token
}
}
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment