Commit 11f2ddf0 authored by jerasmus's avatar jerasmus Committed by Tiger

Display error for unmet prerequisites

Added the ability to display an error for unmet prerequisites
parent 02b9b5fa
...@@ -49,7 +49,7 @@ export default { ...@@ -49,7 +49,7 @@ export default {
<div class="text-content"> <div class="text-content">
<h4 class="js-job-empty-state-title text-center">{{ title }}</h4> <h4 class="js-job-empty-state-title text-center">{{ title }}</h4>
<p v-if="content" class="js-job-empty-state-content">{{ content }}</p> <p v-if="content" class="js-job-empty-state-content text-center">{{ content }}</p>
<div v-if="action" class="text-center"> <div v-if="action" class="text-center">
<gl-link <gl-link
......
...@@ -15,6 +15,7 @@ import ErasedBlock from './erased_block.vue'; ...@@ -15,6 +15,7 @@ import ErasedBlock from './erased_block.vue';
import Log from './job_log.vue'; import Log from './job_log.vue';
import LogTopBar from './job_log_controllers.vue'; import LogTopBar from './job_log_controllers.vue';
import StuckBlock from './stuck_block.vue'; import StuckBlock from './stuck_block.vue';
import UnmetPrerequisitesBlock from './unmet_prerequisites_block.vue';
import Sidebar from './sidebar.vue'; import Sidebar from './sidebar.vue';
import { sprintf } from '~/locale'; import { sprintf } from '~/locale';
import delayedJobMixin from '../mixins/delayed_job_mixin'; import delayedJobMixin from '../mixins/delayed_job_mixin';
...@@ -32,6 +33,7 @@ export default { ...@@ -32,6 +33,7 @@ export default {
Log, Log,
LogTopBar, LogTopBar,
StuckBlock, StuckBlock,
UnmetPrerequisitesBlock,
Sidebar, Sidebar,
GlLoadingIcon, GlLoadingIcon,
SharedRunner: () => import('ee_component/jobs/components/shared_runner_limit_block.vue'), SharedRunner: () => import('ee_component/jobs/components/shared_runner_limit_block.vue'),
...@@ -48,6 +50,11 @@ export default { ...@@ -48,6 +50,11 @@ export default {
required: false, required: false,
default: null, default: null,
}, },
deploymentHelpUrl: {
type: String,
required: false,
default: null,
},
endpoint: { endpoint: {
type: String, type: String,
required: true, required: true,
...@@ -82,6 +89,7 @@ export default { ...@@ -82,6 +89,7 @@ export default {
]), ]),
...mapGetters([ ...mapGetters([
'headerTime', 'headerTime',
'hasUnmetPrerequisitesFailure',
'shouldRenderCalloutMessage', 'shouldRenderCalloutMessage',
'shouldRenderTriggeredLabel', 'shouldRenderTriggeredLabel',
'hasEnvironment', 'hasEnvironment',
...@@ -223,6 +231,12 @@ export default { ...@@ -223,6 +231,12 @@ export default {
:runners-path="runnerSettingsUrl" :runners-path="runnerSettingsUrl"
/> />
<unmet-prerequisites-block
v-if="hasUnmetPrerequisitesFailure"
class="js-job-failed"
:help-path="deploymentHelpUrl"
/>
<shared-runner <shared-runner
v-if="shouldRenderSharedRunnerLimitWarning" v-if="shouldRenderSharedRunnerLimitWarning"
class="js-shared-runner-limit" class="js-shared-runner-limit"
......
<script>
import { GlLink } from '@gitlab/ui';
/**
* Renders Unmet Prerequisites block for job's view.
*/
export default {
components: {
GlLink,
},
props: {
helpPath: {
type: String,
required: true,
},
},
};
</script>
<template>
<div class="bs-callout bs-callout-danger">
<p class="js-failed-unmet-prerequisites append-bottom-0">
{{
s__(`Job|This job failed because the necessary resources were not successfully created.`)
}}
<gl-link :href="helpPath" class="js-help-path">
<strong> {{ __('More information') }} </strong>
</gl-link>
</p>
</div>
</template>
...@@ -12,6 +12,7 @@ export default () => { ...@@ -12,6 +12,7 @@ export default () => {
render(createElement) { render(createElement) {
return createElement('job-app', { return createElement('job-app', {
props: { props: {
deploymentHelpUrl: element.dataset.deploymentHelpUrl,
runnerHelpUrl: element.dataset.runnerHelpUrl, runnerHelpUrl: element.dataset.runnerHelpUrl,
runnerSettingsUrl: element.dataset.runnerSettingsUrl, runnerSettingsUrl: element.dataset.runnerSettingsUrl,
endpoint: element.dataset.endpoint, endpoint: element.dataset.endpoint,
......
...@@ -3,8 +3,13 @@ import { isScrolledToBottom } from '~/lib/utils/scroll_utils'; ...@@ -3,8 +3,13 @@ import { isScrolledToBottom } from '~/lib/utils/scroll_utils';
export const headerTime = state => (state.job.started ? state.job.started : state.job.created_at); export const headerTime = state => (state.job.started ? state.job.started : state.job.created_at);
export const hasUnmetPrerequisitesFailure = state =>
state.job && state.job.failure_reason && state.job.failure_reason === 'unmet_prerequisites';
export const shouldRenderCalloutMessage = state => export const shouldRenderCalloutMessage = state =>
!_.isEmpty(state.job.status) && !_.isEmpty(state.job.callout_message); !_.isEmpty(state.job.status) &&
!_.isEmpty(state.job.callout_message) &&
!hasUnmetPrerequisitesFailure;
/** /**
* When job has not started the key will be null * When job has not started the key will be null
......
...@@ -28,6 +28,10 @@ ...@@ -28,6 +28,10 @@
background-color: $red-100; background-color: $red-100;
border-color: $red-200; border-color: $red-200;
color: $red-700; color: $red-700;
a {
color: $red-700;
}
} }
.bs-callout-warning { .bs-callout-warning {
......
...@@ -8,6 +8,7 @@ ...@@ -8,6 +8,7 @@
%div{ class: container_class } %div{ class: container_class }
#js-job-vue-app{ data: { endpoint: project_job_path(@project, @build, format: :json), #js-job-vue-app{ data: { endpoint: project_job_path(@project, @build, format: :json),
deployment_help_url: help_page_path('user/project/clusters/index.html', anchor: 'troubleshooting-failed-deployment-jobs'),
runner_help_url: help_page_path('ci/runners/README.html', anchor: 'setting-maximum-job-timeout-for-a-runner'), runner_help_url: help_page_path('ci/runners/README.html', anchor: 'setting-maximum-job-timeout-for-a-runner'),
runner_settings_url: project_runners_path(@build.project, anchor: 'js-runners-settings'), runner_settings_url: project_runners_path(@build.project, anchor: 'js-runners-settings'),
build_options: javascript_build_options } } build_options: javascript_build_options } }
---
title: Show error when namespace/svc account missing
merge_request: 26362
author:
type: added
...@@ -7,10 +7,10 @@ module Gitlab ...@@ -7,10 +7,10 @@ module Gitlab
class FailedUnmetPrerequisites < Status::Extended class FailedUnmetPrerequisites < Status::Extended
def illustration def illustration
{ {
image: 'illustrations/skipped-job_empty.svg', image: 'illustrations/pipelines_failed.svg',
size: 'svg-430', size: 'svg-430',
title: _('Failed to create resources'), title: _('Failed to create resources'),
content: _('Retry this job in order to create the necessary resources') content: _('Retry this job in order to create the necessary resources.')
} }
end end
......
...@@ -3568,6 +3568,9 @@ msgstr "" ...@@ -3568,6 +3568,9 @@ msgstr ""
msgid "Failed to check related branches." msgid "Failed to check related branches."
msgstr "" msgstr ""
msgid "Failed to create resources"
msgstr ""
msgid "Failed to deploy to" msgid "Failed to deploy to"
msgstr "" msgstr ""
...@@ -4589,6 +4592,9 @@ msgstr "" ...@@ -4589,6 +4592,9 @@ msgstr ""
msgid "Job|The artifacts will be removed" msgid "Job|The artifacts will be removed"
msgstr "" msgstr ""
msgid "Job|This job failed because the necessary resources were not successfully created."
msgstr ""
msgid "Job|This job is stuck because the project doesn't have any runners online assigned to it." msgid "Job|This job is stuck because the project doesn't have any runners online assigned to it."
msgstr "" msgstr ""
...@@ -6825,6 +6831,9 @@ msgstr "" ...@@ -6825,6 +6831,9 @@ msgstr ""
msgid "Retry this job" msgid "Retry this job"
msgstr "" msgstr ""
msgid "Retry this job in order to create the necessary resources."
msgstr ""
msgid "Retry verification" msgid "Retry verification"
msgstr "" msgstr ""
......
...@@ -17,6 +17,7 @@ describe('Job App ', () => { ...@@ -17,6 +17,7 @@ describe('Job App ', () => {
const props = { const props = {
endpoint: `${gl.TEST_HOST}jobs/123.json`, endpoint: `${gl.TEST_HOST}jobs/123.json`,
runnerHelpUrl: 'help/runner', runnerHelpUrl: 'help/runner',
deploymentHelpUrl: 'help/deployment',
runnerSettingsUrl: 'settings/ci-cd/runners', runnerSettingsUrl: 'settings/ci-cd/runners',
terminalPath: 'jobs/123/terminal', terminalPath: 'jobs/123/terminal',
pagePath: `${gl.TEST_HOST}jobs/123`, pagePath: `${gl.TEST_HOST}jobs/123`,
...@@ -253,6 +254,41 @@ describe('Job App ', () => { ...@@ -253,6 +254,41 @@ describe('Job App ', () => {
}); });
}); });
describe('unmet prerequisites block', () => {
it('renders unmet prerequisites block when there is an unmet prerequisites failure', done => {
mock.onGet(props.endpoint).replyOnce(
200,
Object.assign({}, job, {
status: {
group: 'failed',
icon: 'status_failed',
label: 'failed',
text: 'failed',
details_path: 'path',
illustration: {
content: 'Retry this job in order to create the necessary resources.',
image: 'path',
size: 'svg-430',
title: 'Failed to create resources',
},
},
has_trace: false,
unmet_prerequisites: true,
runners: {
available: true,
},
tags: [],
}),
);
vm = mountComponentWithStore(Component, { props, store });
setTimeout(() => {
expect(vm.$el.querySelector('.js-job-failed')).not.toBeNull();
done();
}, 0);
});
});
describe('environments block', () => { describe('environments block', () => {
it('renders environment block when job has environment', done => { it('renders environment block when job has environment', done => {
mock.onGet(props.endpoint).replyOnce( mock.onGet(props.endpoint).replyOnce(
......
import Vue from 'vue';
import component from '~/jobs/components/unmet_prerequisites_block.vue';
import mountComponent from '../../helpers/vue_mount_component_helper';
describe('Unmet Prerequisites Block Job component', () => {
const Component = Vue.extend(component);
let vm;
const helpPath = '/user/project/clusters/index.html#troubleshooting-failed-deployment-jobs';
beforeEach(() => {
vm = mountComponent(Component, {
hasNoRunnersForProject: true,
helpPath,
});
});
afterEach(() => {
vm.$destroy();
});
it('renders an alert with the correct message', () => {
const container = vm.$el.querySelector('.js-failed-unmet-prerequisites');
const alertMessage =
'This job failed because the necessary resources were not successfully created.';
expect(container).not.toBeNull();
expect(container.innerHTML).toContain(alertMessage);
});
it('renders link to help page', () => {
const helpLink = vm.$el.querySelector('.js-help-path');
expect(helpLink).not.toBeNull();
expect(helpLink.innerHTML).toContain('More information');
expect(helpLink.getAttribute('href')).toEqual(helpPath);
});
});
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