Commit 8ee94ef1 authored by Ahmet DEMIR's avatar Ahmet DEMIR

Add api /job to identify job using ci job token

This endpoint retrieve the job that generated a job token
parent d10e408e
---
title: Add API endpoint for fetching a single job by CI_JOB_TOKEN
merge_request: 51727
author: ahmet2mir
type: added
...@@ -211,6 +211,7 @@ to authenticate with the API: ...@@ -211,6 +211,7 @@ to authenticate with the API:
- [Package Registry for PyPI](../user/packages/pypi_repository/index.md#authenticate-with-a-ci-job-token) - [Package Registry for PyPI](../user/packages/pypi_repository/index.md#authenticate-with-a-ci-job-token)
- [Package Registry for generic packages](../user/packages/generic_packages/index.md#publish-a-generic-package-by-using-cicd) - [Package Registry for generic packages](../user/packages/generic_packages/index.md#publish-a-generic-package-by-using-cicd)
- [Get job artifacts](job_artifacts.md#get-job-artifacts) - [Get job artifacts](job_artifacts.md#get-job-artifacts)
- [Get job token's job](jobs.md#get-job-tokens-job)
- [Pipeline triggers](pipeline_triggers.md) (using the `token=` parameter) - [Pipeline triggers](pipeline_triggers.md) (using the `token=` parameter)
- [Release creation](releases/index.md#create-a-release) - [Release creation](releases/index.md#create-a-release)
- [Terraform plan](../user/infrastructure/index.md) - [Terraform plan](../user/infrastructure/index.md)
......
...@@ -146,6 +146,7 @@ The following API resources are available outside of project and group contexts ...@@ -146,6 +146,7 @@ The following API resources are available outside of project and group contexts
| [Instance clusters](instance_clusters.md) | `/admin/clusters` | | [Instance clusters](instance_clusters.md) | `/admin/clusters` |
| [Issues](issues.md) | `/issues` (also available for groups and projects) | | [Issues](issues.md) | `/issues` (also available for groups and projects) |
| [Issues Statistics](issues_statistics.md) | `/issues_statistics` (also available for groups and projects) | | [Issues Statistics](issues_statistics.md) | `/issues_statistics` (also available for groups and projects) |
| [Jobs](jobs.md) | `/job` |
| [Keys](keys.md) | `/keys` | | [Keys](keys.md) | `/keys` |
| [License](license.md) **(FREE SELF)** | `/license` | | [License](license.md) **(FREE SELF)** | `/license` |
| [Markdown](markdown.md) | `/markdown` | | [Markdown](markdown.md) | `/markdown` |
......
...@@ -380,6 +380,76 @@ Example of response ...@@ -380,6 +380,76 @@ Example of response
] ]
``` ```
## Get job token's job
Retrieve the job that generated a job token.
```plaintext
GET /job
```
Examples
```shell
curl --header "JOB-TOKEN: <your_job_token>" "https://gitlab.example.com/api/v4/job"
curl "https://gitlab.example.com/api/v4/job?job_token=<your_job_token>"
```
Example of response
```json
{
"commit": {
"author_email": "admin@example.com",
"author_name": "Administrator",
"created_at": "2015-12-24T16:51:14.000+01:00",
"id": "0ff3ae198f8601a285adcf5c0fff204ee6fba5fd",
"message": "Test the CI integration.",
"short_id": "0ff3ae19",
"title": "Test the CI integration."
},
"coverage": null,
"allow_failure": false,
"created_at": "2015-12-24T15:51:21.880Z",
"started_at": "2015-12-24T17:54:30.733Z",
"finished_at": "2015-12-24T17:54:31.198Z",
"duration": 0.465,
"artifacts_expire_at": "2016-01-23T17:54:31.198Z",
"id": 8,
"name": "rubocop",
"pipeline": {
"id": 6,
"ref": "master",
"sha": "0ff3ae198f8601a285adcf5c0fff204ee6fba5fd",
"status": "pending"
},
"ref": "master",
"artifacts": [],
"runner": null,
"stage": "test",
"status": "failed",
"tag": false,
"web_url": "https://example.com/foo/bar/-/jobs/8",
"user": {
"id": 1,
"name": "Administrator",
"username": "root",
"state": "active",
"avatar_url": "http://www.gravatar.com/avatar/e64c7d89f26bd1972efa854d13d7dd61?s=80&d=identicon",
"web_url": "http://gitlab.dev/root",
"created_at": "2015-12-21T13:14:24.077Z",
"bio": null,
"location": null,
"public_email": "",
"skype": "",
"linkedin": "",
"twitter": "",
"website_url": "",
"organization": ""
}
}
```
## Get a single job ## Get a single job
Get a single job of a project Get a single job of a project
......
...@@ -8,10 +8,11 @@ module API ...@@ -8,10 +8,11 @@ module API
feature_category :continuous_integration feature_category :continuous_integration
params do
requires :id, type: String, desc: 'The ID of a project'
end
resource :projects, requirements: API::NAMESPACE_OR_PROJECT_REQUIREMENTS do resource :projects, requirements: API::NAMESPACE_OR_PROJECT_REQUIREMENTS do
params do
requires :id, type: String, desc: 'The ID of a project'
end
helpers do helpers do
params :optional_scope do params :optional_scope do
optional :scope, types: [String, Array[String]], desc: 'The scope of builds to show', optional :scope, types: [String, Array[String]], desc: 'The scope of builds to show',
...@@ -168,6 +169,20 @@ module API ...@@ -168,6 +169,20 @@ module API
end end
end end
resource :job do
desc 'Get current project using job token' do
success Entities::Ci::Job
end
route_setting :authentication, job_token_allowed: true
get do
# current_authenticated_job will be nil if user is using
# a valid authentication that is not CI_JOB_TOKEN
not_found!('Job') unless current_authenticated_job
present current_authenticated_job, with: Entities::Ci::Job
end
end
helpers do helpers do
# rubocop: disable CodeReuse/ActiveRecord # rubocop: disable CodeReuse/ActiveRecord
def filter_builds(builds, scope) def filter_builds(builds, scope)
......
...@@ -3,6 +3,9 @@ ...@@ -3,6 +3,9 @@
require 'spec_helper' require 'spec_helper'
RSpec.describe API::Jobs do RSpec.describe API::Jobs do
include HttpBasicAuthHelpers
include DependencyProxyHelpers
using RSpec::Parameterized::TableSyntax using RSpec::Parameterized::TableSyntax
include HttpIOHelpers include HttpIOHelpers
...@@ -16,20 +19,150 @@ RSpec.describe API::Jobs do ...@@ -16,20 +19,150 @@ RSpec.describe API::Jobs do
ref: project.default_branch) ref: project.default_branch)
end end
let!(:job) do
create(:ci_build, :success, :tags, pipeline: pipeline,
artifacts_expire_at: 1.day.since)
end
let(:user) { create(:user) } let(:user) { create(:user) }
let(:api_user) { user } let(:api_user) { user }
let(:reporter) { create(:project_member, :reporter, project: project).user } let(:reporter) { create(:project_member, :reporter, project: project).user }
let(:guest) { create(:project_member, :guest, project: project).user } let(:guest) { create(:project_member, :guest, project: project).user }
let(:running_job) do
create(:ci_build, :running, project: project,
user: user,
pipeline: pipeline,
artifacts_expire_at: 1.day.since)
end
let!(:job) do
create(:ci_build, :success, :tags, pipeline: pipeline,
artifacts_expire_at: 1.day.since)
end
before do before do
project.add_developer(user) project.add_developer(user)
end end
shared_examples 'returns common pipeline data' do
it 'returns common pipeline data' do
expect(json_response['pipeline']).not_to be_empty
expect(json_response['pipeline']['id']).to eq jobx.pipeline.id
expect(json_response['pipeline']['ref']).to eq jobx.pipeline.ref
expect(json_response['pipeline']['sha']).to eq jobx.pipeline.sha
expect(json_response['pipeline']['status']).to eq jobx.pipeline.status
end
end
shared_examples 'returns common job data' do
it 'returns common job data' do
expect(response).to have_gitlab_http_status(:ok)
expect(json_response['id']).to eq(jobx.id)
expect(json_response['status']).to eq(jobx.status)
expect(json_response['stage']).to eq(jobx.stage)
expect(json_response['name']).to eq(jobx.name)
expect(json_response['ref']).to eq(jobx.ref)
expect(json_response['tag']).to eq(jobx.tag)
expect(json_response['coverage']).to eq(jobx.coverage)
expect(json_response['allow_failure']).to eq(jobx.allow_failure)
expect(Time.parse(json_response['created_at'])).to be_like_time(jobx.created_at)
expect(Time.parse(json_response['started_at'])).to be_like_time(jobx.started_at)
expect(Time.parse(json_response['artifacts_expire_at'])).to be_like_time(jobx.artifacts_expire_at)
expect(json_response['artifacts_file']).to be_nil
expect(json_response['artifacts']).to be_an Array
expect(json_response['artifacts']).to be_empty
expect(json_response['web_url']).to be_present
end
end
shared_examples 'returns unauthorized' do
it 'returns unauthorized' do
expect(response).to have_gitlab_http_status(:unauthorized)
end
end
describe 'GET /job' do
shared_context 'with auth headers' do
let(:headers_with_token) { header }
let(:params_with_token) { {} }
end
shared_context 'with auth params' do
let(:headers_with_token) { {} }
let(:params_with_token) { param }
end
shared_context 'without auth' do
let(:headers_with_token) { {} }
let(:params_with_token) { {} }
end
before do |example|
unless example.metadata[:skip_before_request]
get api('/job'), headers: headers_with_token, params: params_with_token
end
end
context 'with job token authentication header' do
include_context 'with auth headers' do
let(:header) { { API::Helpers::Runner::JOB_TOKEN_HEADER => running_job.token } }
end
it_behaves_like 'returns common job data' do
let(:jobx) { running_job }
end
it 'returns specific job data' do
expect(json_response['finished_at']).to be_nil
end
it_behaves_like 'returns common pipeline data' do
let(:jobx) { running_job }
end
end
context 'with job token authentication params' do
include_context 'with auth params' do
let(:param) { { job_token: running_job.token } }
end
it_behaves_like 'returns common job data' do
let(:jobx) { running_job }
end
it 'returns specific job data' do
expect(json_response['finished_at']).to be_nil
end
it_behaves_like 'returns common pipeline data' do
let(:jobx) { running_job }
end
end
context 'with non running job' do
include_context 'with auth headers' do
let(:header) { { API::Helpers::Runner::JOB_TOKEN_HEADER => job.token } }
end
it_behaves_like 'returns unauthorized'
end
context 'with basic auth header' do
let(:personal_access_token) { create(:personal_access_token, user: user) }
let(:token) { personal_access_token.token}
include_context 'with auth headers' do
let(:header) { { Gitlab::Auth::AuthFinders::PRIVATE_TOKEN_HEADER => token } }
end
it 'does not return a job' do
expect(response).to have_gitlab_http_status(:not_found)
end
end
context 'without authentication' do
include_context 'without auth'
it_behaves_like 'returns unauthorized'
end
end
describe 'GET /projects/:id/jobs' do describe 'GET /projects/:id/jobs' do
let(:query) { {} } let(:query) { {} }
...@@ -150,39 +283,21 @@ RSpec.describe API::Jobs do ...@@ -150,39 +283,21 @@ RSpec.describe API::Jobs do
end end
context 'authorized user' do context 'authorized user' do
it_behaves_like 'returns common job data' do
let(:jobx) { job }
end
it 'returns specific job data' do it 'returns specific job data' do
expect(response).to have_gitlab_http_status(:ok)
expect(json_response['id']).to eq(job.id)
expect(json_response['status']).to eq(job.status)
expect(json_response['stage']).to eq(job.stage)
expect(json_response['name']).to eq(job.name)
expect(json_response['ref']).to eq(job.ref)
expect(json_response['tag']).to eq(job.tag)
expect(json_response['coverage']).to eq(job.coverage)
expect(json_response['allow_failure']).to eq(job.allow_failure)
expect(Time.parse(json_response['created_at'])).to be_like_time(job.created_at)
expect(Time.parse(json_response['started_at'])).to be_like_time(job.started_at)
expect(Time.parse(json_response['finished_at'])).to be_like_time(job.finished_at) expect(Time.parse(json_response['finished_at'])).to be_like_time(job.finished_at)
expect(Time.parse(json_response['artifacts_expire_at'])).to be_like_time(job.artifacts_expire_at)
expect(json_response['artifacts_file']).to be_nil
expect(json_response['artifacts']).to be_an Array
expect(json_response['artifacts']).to be_empty
expect(json_response['duration']).to eq(job.duration) expect(json_response['duration']).to eq(job.duration)
expect(json_response['web_url']).to be_present
end end
it_behaves_like 'a job with artifacts and trace', result_is_array: false do it_behaves_like 'a job with artifacts and trace', result_is_array: false do
let(:api_endpoint) { "/projects/#{project.id}/jobs/#{second_job.id}" } let(:api_endpoint) { "/projects/#{project.id}/jobs/#{second_job.id}" }
end end
it 'returns pipeline data' do it_behaves_like 'returns common pipeline data' do
json_job = json_response let(:jobx) { job }
expect(json_job['pipeline']).not_to be_empty
expect(json_job['pipeline']['id']).to eq job.pipeline.id
expect(json_job['pipeline']['ref']).to eq job.pipeline.ref
expect(json_job['pipeline']['sha']).to eq job.pipeline.sha
expect(json_job['pipeline']['status']).to eq job.pipeline.status
end 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