Commit ff648879 authored by Grzegorz Bizon's avatar Grzegorz Bizon

Merge branch '54506-show-error-when-namespace-svc-missing' into 'master'

Show error when namespace/svc account missing

Closes #54506

See merge request gitlab-org/gitlab-ce!26362
parents 07388b30 bd750af7
......@@ -49,7 +49,7 @@ export default {
<div class="text-content">
<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">
<gl-link
......
......@@ -15,6 +15,7 @@ import ErasedBlock from './erased_block.vue';
import Log from './job_log.vue';
import LogTopBar from './job_log_controllers.vue';
import StuckBlock from './stuck_block.vue';
import UnmetPrerequisitesBlock from './unmet_prerequisites_block.vue';
import Sidebar from './sidebar.vue';
import { sprintf } from '~/locale';
import delayedJobMixin from '../mixins/delayed_job_mixin';
......@@ -32,6 +33,7 @@ export default {
Log,
LogTopBar,
StuckBlock,
UnmetPrerequisitesBlock,
Sidebar,
GlLoadingIcon,
SharedRunner: () => import('ee_component/jobs/components/shared_runner_limit_block.vue'),
......@@ -48,6 +50,11 @@ export default {
required: false,
default: null,
},
deploymentHelpUrl: {
type: String,
required: false,
default: null,
},
endpoint: {
type: String,
required: true,
......@@ -82,6 +89,7 @@ export default {
]),
...mapGetters([
'headerTime',
'hasUnmetPrerequisitesFailure',
'shouldRenderCalloutMessage',
'shouldRenderTriggeredLabel',
'hasEnvironment',
......@@ -210,7 +218,10 @@ export default {
/>
</div>
<callout v-if="shouldRenderCalloutMessage" :message="job.callout_message" />
<callout
v-if="shouldRenderCalloutMessage && !hasUnmetPrerequisitesFailure"
:message="job.callout_message"
/>
</header>
<!-- EO Header Section -->
......@@ -223,6 +234,12 @@ export default {
:runners-path="runnerSettingsUrl"
/>
<unmet-prerequisites-block
v-if="hasUnmetPrerequisitesFailure"
class="js-job-failed"
:help-path="deploymentHelpUrl"
/>
<shared-runner
v-if="shouldRenderSharedRunnerLimitWarning"
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 () => {
render(createElement) {
return createElement('job-app', {
props: {
deploymentHelpUrl: element.dataset.deploymentHelpUrl,
runnerHelpUrl: element.dataset.runnerHelpUrl,
runnerSettingsUrl: element.dataset.runnerSettingsUrl,
endpoint: element.dataset.endpoint,
......
......@@ -3,6 +3,9 @@ import { isScrolledToBottom } from '~/lib/utils/scroll_utils';
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 =>
!_.isEmpty(state.job.status) && !_.isEmpty(state.job.callout_message);
......
......@@ -28,6 +28,10 @@
background-color: $red-100;
border-color: $red-200;
color: $red-700;
a {
color: $red-700;
}
}
.bs-callout-warning {
......
......@@ -45,6 +45,8 @@ class BuildDetailsEntity < JobEntity
erase_project_job_path(project, build)
end
expose :failure_reason, if: -> (*) { build.failed? }
expose :terminal_path, if: -> (*) { can_create_build_terminal? } do |build|
terminal_project_job_path(project, build)
end
......
......@@ -8,6 +8,7 @@
%div{ class: container_class }
#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_settings_url: project_runners_path(@build.project, anchor: 'js-runners-settings'),
build_options: javascript_build_options } }
---
title: Show error when namespace/svc account missing
merge_request: 26362
author:
type: added
......@@ -570,7 +570,7 @@ deployment jobs, immediately before the jobs starts.
However, sometimes GitLab can not create them. In such instances, your job will fail with the message:
```text
The job failed to complete prerequisite tasks
This job failed because the necessary resources were not successfully created.
```
To find the cause of this error when creating a namespace and service account, check the [logs](../../../administration/logs.md#sidekiqlog).
......
......@@ -16,7 +16,8 @@ module Gitlab
Status::Build::Skipped],
[Status::Build::Cancelable,
Status::Build::Retryable],
[Status::Build::Failed],
[Status::Build::FailedUnmetPrerequisites,
Status::Build::Failed],
[Status::Build::FailedAllowed,
Status::Build::Unschedule,
Status::Build::Play,
......
# frozen_string_literal: true
module Gitlab
module Ci
module Status
module Build
class FailedUnmetPrerequisites < Status::Extended
def illustration
{
image: 'illustrations/pipelines_failed.svg',
size: 'svg-430',
title: _('Failed to create resources'),
content: _('Retry this job in order to create the necessary resources.')
}
end
def self.matches?(build, _)
build.unmet_prerequisites?
end
end
end
end
end
end
......@@ -3583,6 +3583,9 @@ msgstr ""
msgid "Failed to check related branches."
msgstr ""
msgid "Failed to create resources"
msgstr ""
msgid "Failed to deploy to"
msgstr ""
......@@ -4610,6 +4613,9 @@ msgstr ""
msgid "Job|The artifacts will be removed"
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."
msgstr ""
......@@ -6879,6 +6885,9 @@ msgstr ""
msgid "Retry this job"
msgstr ""
msgid "Retry this job in order to create the necessary resources."
msgstr ""
msgid "Retry verification"
msgstr ""
......
......@@ -336,6 +336,11 @@ FactoryBot.define do
failure_reason 2
end
trait :prerequisite_failure do
failed
failure_reason 10
end
trait :with_runner_session do
after(:build) do |build|
build.build_runner_session(url: 'https://localhost')
......
......@@ -17,6 +17,7 @@ describe('Job App ', () => {
const props = {
endpoint: `${gl.TEST_HOST}jobs/123.json`,
runnerHelpUrl: 'help/runner',
deploymentHelpUrl: 'help/deployment',
runnerSettingsUrl: 'settings/ci-cd/runners',
terminalPath: 'jobs/123/terminal',
pagePath: `${gl.TEST_HOST}jobs/123`,
......@@ -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',
},
},
failure_reason: 'unmet_prerequisites',
has_trace: false,
runners: {
available: true,
},
tags: [],
}),
);
vm = mountComponentWithStore(Component, { props, store });
setTimeout(() => {
expect(vm.$el.querySelector('.js-job-failed')).not.toBeNull();
done();
}, 0);
});
});
describe('environments block', () => {
it('renders environment block when job has environment', done => {
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);
});
});
......@@ -123,6 +123,35 @@ describe Gitlab::Ci::Status::Build::Factory do
expect(status.action_path).to include 'retry'
end
end
context 'when build has unmet prerequisites' do
let(:build) { create(:ci_build, :prerequisite_failure) }
it 'matches correct core status' do
expect(factory.core_status).to be_a Gitlab::Ci::Status::Failed
end
it 'matches correct extended statuses' do
expect(factory.extended_statuses)
.to eq [Gitlab::Ci::Status::Build::Retryable,
Gitlab::Ci::Status::Build::FailedUnmetPrerequisites]
end
it 'fabricates a failed with unmet prerequisites build status' do
expect(status).to be_a Gitlab::Ci::Status::Build::FailedUnmetPrerequisites
end
it 'fabricates status with correct details' do
expect(status.text).to eq 'failed'
expect(status.icon).to eq 'status_failed'
expect(status.favicon).to eq 'favicon_status_failed'
expect(status.label).to eq 'failed'
expect(status).to have_details
expect(status).to have_action
expect(status.action_title).to include 'Retry'
expect(status.action_path).to include 'retry'
end
end
end
context 'when build is a canceled' do
......
require 'spec_helper'
RSpec.describe Gitlab::Ci::Status::Build::FailedUnmetPrerequisites do
describe '#illustration' do
subject { described_class.new(double).illustration }
it { is_expected.to include(:image, :size, :title, :content) }
end
describe '.matches?' do
let(:build) { create(:ci_build, :created) }
subject { described_class.matches?(build, double) }
context 'when build has not failed' do
it { is_expected.to be_falsey }
end
context 'when build has failed' do
before do
build.drop!(failure_reason)
end
context 'with unmet prerequisites' do
let(:failure_reason) { :unmet_prerequisites }
it { is_expected.to be_truthy }
end
context 'with a different error' do
let(:failure_reason) { :runner_system_failure }
it { is_expected.to be_falsey }
end
end
end
end
......@@ -112,5 +112,15 @@ describe BuildDetailsEntity do
expect(subject['merge_request_path']).to be_nil
end
end
context 'when the build has failed' do
let(:build) { create(:ci_build, :created) }
before do
build.drop!(:unmet_prerequisites)
end
it { is_expected.to include(failure_reason: 'unmet_prerequisites') }
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