Commit a4dbeeb0 authored by Thong Kuah's avatar Thong Kuah

Merge branch 'ee-55095-show-cluster-on-job' into 'master'

EE: Show cluster link on job page

See merge request gitlab-org/gitlab-ee!16361
parents 01e12b05 fdc89367
......@@ -79,7 +79,9 @@ export default {
default:
break;
}
return environmentText;
return environmentText && this.hasCluster
? `${environmentText} ${this.clusterText}`
: environmentText;
},
environmentLink() {
if (this.hasEnvironment) {
......@@ -109,6 +111,37 @@ export default {
? this.lastDeployment.deployable.build_path
: '';
},
hasCluster() {
return this.hasLastDeployment && this.lastDeployment.cluster;
},
clusterNameOrLink() {
if (!this.hasCluster) {
return '';
}
const { name, path } = this.lastDeployment.cluster;
const escapedName = _.escape(name);
const escapedPath = _.escape(path);
if (!escapedPath) {
return escapedName;
}
return sprintf(
'%{startLink}%{name}%{endLink}',
{
startLink: `<a href="${escapedPath}" class="js-job-cluster-link">`,
name: escapedName,
endLink: '</a>',
},
false,
);
},
clusterText() {
return this.hasCluster
? sprintf(__('Cluster %{cluster} was used.'), { cluster: this.clusterNameOrLink }, false)
: '';
},
},
methods: {
deploymentLink(name) {
......
# frozen_string_literal: true
class ClusterBasicEntity < Grape::Entity
include RequestAwareEntity
expose :name
expose :path, if: -> (cluster) { can?(request.current_user, :read_cluster, cluster) } do |cluster|
cluster.present(current_user: request.current_user).show_path
end
end
......@@ -38,6 +38,8 @@ class DeploymentEntity < Grape::Entity
expose :manual_actions, using: JobEntity, if: -> (*) { include_details? && can_create_deployment? }
expose :scheduled_actions, using: JobEntity, if: -> (*) { include_details? && can_create_deployment? }
expose :cluster, using: ClusterBasicEntity
private
def include_details?
......
---
title: Show link to cluster used on job page
merge_request: 32446
author:
type: added
......@@ -3184,6 +3184,9 @@ msgstr ""
msgid "Closes this %{quick_action_target}."
msgstr ""
msgid "Cluster %{cluster} was used."
msgstr ""
msgid "Cluster Health"
msgstr ""
......
......@@ -265,7 +265,8 @@ describe Projects::JobsController, :clean_gitlab_redis_shared_state do
let(:job) { create(:ci_build, :running, environment: environment.name, pipeline: pipeline) }
before do
create(:deployment, :success, environment: environment, project: project)
create(:deployment, :success, :on_cluster, environment: environment, project: project)
project.add_maintainer(user) # Need to be a maintianer to view cluster.path
end
it 'exposes the deployment information' do
......@@ -276,8 +277,9 @@ describe Projects::JobsController, :clean_gitlab_redis_shared_state do
expect(json_response.dig('deployment_status', 'status')).to eq 'creating'
expect(json_response.dig('deployment_status', 'environment')).not_to be_nil
expect(json_response.dig('deployment_status', 'environment', 'last_deployment')).not_to be_nil
expect(json_response.dig('deployment_status', 'environment', 'last_deployment'))
.not_to include('commit')
expect(json_response.dig('deployment_status', 'environment', 'last_deployment')).not_to include('commit')
expect(json_response.dig('deployment_status', 'environment', 'last_deployment', 'cluster', 'name')).to eq('test-cluster')
expect(json_response.dig('deployment_status', 'environment', 'last_deployment', 'cluster', 'path')).to be_present
end
end
......
......@@ -17,6 +17,10 @@ FactoryBot.define do
unless deployment.project.repository_exists?
allow(deployment.project.repository).to receive(:create_ref)
end
if deployment.cluster && deployment.cluster.project_type? && deployment.cluster.project.nil?
deployment.cluster.projects << deployment.project
end
end
trait :review_app do
......
......@@ -534,9 +534,32 @@ describe 'Jobs', :clean_gitlab_redis_shared_state do
end
it 'shows deployment message' do
expect(page).to have_content 'This job is the most recent deployment'
expect(page).to have_content 'This job is the most recent deployment to production'
expect(find('.js-environment-link')['href']).to match("environments/#{environment.id}")
end
context 'when there is a cluster used for the deployment' do
let(:cluster) { create(:cluster, name: 'the-cluster') }
let(:deployment) { create(:deployment, :success, cluster: cluster, environment: environment, project: environment.project) }
let(:user_access_level) { :maintainer }
it 'shows a link to the cluster' do
expect(page).to have_link 'the-cluster'
end
it 'shows the name of the cluster' do
expect(page).to have_content 'Cluster the-cluster was used'
end
context 'when the user is not able to view the cluster' do
let(:user_access_level) { :developer }
it 'includes only the name of the cluster without a link' do
expect(page).to have_content 'Cluster the-cluster was used'
expect(page).not_to have_link 'the-cluster'
end
end
end
end
context 'job is complete and not successful' do
......
{
"type": "object",
"required": [
"name"
],
"properties": {
"name": { "type": "string" },
"path": {
"oneOf": [
{ "type": "null" },
{ "type": "string" }
]
}
},
"additionalProperties": false
}
......@@ -47,6 +47,12 @@
{ "$ref": "job/job.json" }
]
},
"cluster": {
"oneOf": [
{ "type": "null" },
{ "$ref": "cluster_basic.json" }
]
},
"manual_actions": {
"type": "array",
"items": { "$ref": "job/job.json" }
......
......@@ -28,6 +28,8 @@
"created_at": { "type": "string", "format": "date-time" },
"updated_at": { "type": "string", "format": "date-time" },
"can_stop": { "type": "boolean" },
"cluster_type": { "type": "types/nullable_string.json" },
"terminal_path": { "type": "types/nullable_string.json" },
"last_deployment": {
"oneOf": [
{ "type": "null" },
......
......@@ -18,6 +18,8 @@ describe('Environments block', () => {
name: 'environment',
};
const lastDeployment = { iid: 'deployment', deployable: { build_path: 'bar' } };
afterEach(() => {
vm.$destroy();
});
......@@ -45,7 +47,7 @@ describe('Environments block', () => {
deploymentStatus: {
status: 'out_of_date',
environment: Object.assign({}, environment, {
last_deployment: { iid: 'deployment', deployable: { build_path: 'bar' } },
last_deployment: lastDeployment,
}),
},
iconStatus: status,
......@@ -99,10 +101,7 @@ describe('Environments block', () => {
deploymentStatus: {
status: 'creating',
environment: Object.assign({}, environment, {
last_deployment: {
iid: 'deployment',
deployable: { build_path: 'foo' },
},
last_deployment: lastDeployment,
}),
},
iconStatus: status,
......@@ -112,7 +111,7 @@ describe('Environments block', () => {
'This job is creating a deployment to environment and will overwrite the latest deployment.',
);
expect(vm.$el.querySelector('.js-job-deployment-link').getAttribute('href')).toEqual('foo');
expect(vm.$el.querySelector('.js-job-deployment-link').getAttribute('href')).toEqual('bar');
});
});
......@@ -146,4 +145,71 @@ describe('Environments block', () => {
});
});
});
describe('with a cluster', () => {
it('renders the cluster link', () => {
const cluster = {
name: 'the-cluster',
path: '/the-cluster-path',
};
vm = mountComponent(Component, {
deploymentStatus: {
status: 'last',
environment: Object.assign({}, environment, {
last_deployment: {
...lastDeployment,
cluster,
},
}),
},
iconStatus: status,
});
expect(vm.$el.textContent.trim()).toContain('Cluster the-cluster was used.');
expect(vm.$el.querySelector('.js-job-cluster-link').getAttribute('href')).toEqual(
'/the-cluster-path',
);
});
describe('when the cluster is missing the path', () => {
it('renders the name without a link', () => {
const cluster = {
name: 'the-cluster',
};
vm = mountComponent(Component, {
deploymentStatus: {
status: 'last',
environment: Object.assign({}, environment, {
last_deployment: {
...lastDeployment,
cluster,
},
}),
},
iconStatus: status,
});
expect(vm.$el.textContent.trim()).toContain('Cluster the-cluster was used.');
expect(vm.$el.querySelector('.js-job-cluster-link')).toBeNull();
});
});
});
describe('without a cluster', () => {
it('does not render a cluster link', () => {
vm = mountComponent(Component, {
deploymentStatus: {
status: 'last',
environment: Object.assign({}, environment, {
last_deployment: lastDeployment,
}),
},
iconStatus: status,
});
expect(vm.$el.querySelector('.js-job-cluster-link')).toBeNull();
});
});
});
require 'spec_helper'
describe ClusterBasicEntity do
describe '#as_json' do
subject { described_class.new(cluster, request: request).as_json }
let(:maintainer) { create(:user) }
let(:developer) { create(:user) }
let(:current_user) { maintainer }
let(:request) { double(:request, current_user: current_user) }
let(:project) { create(:project) }
let(:cluster) { create(:cluster, name: 'the-cluster', projects: [project]) }
before do
project.add_maintainer(maintainer)
project.add_developer(developer)
end
it 'matches cluster_basic entity schema' do
expect(subject.as_json).to match_schema('cluster_basic')
end
it 'exposes the cluster details' do
expect(subject[:name]).to eq('the-cluster')
expect(subject[:path]).to eq("/#{project.full_path}/clusters/#{cluster.id}")
end
context 'when the user does not have permission to view the cluster' do
let(:current_user) { developer }
it 'does not include the path' do
expect(subject[:path]).to be_nil
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