Welcome to mirror list, hosted at ThFree Co, Russian Federation.

gitlab.com/gitlab-org/gitlab-foss.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorGrzegorz Bizon <grzegorz@gitlab.com>2019-04-05 10:05:32 +0300
committerGrzegorz Bizon <grzegorz@gitlab.com>2019-04-05 10:05:32 +0300
commitff648879642f81ccb5eda7c70b9738916a185630 (patch)
tree281ed755c60e4dbfa4f698540210aa39fb917af4
parent07388b30b11f92e45da31d9a96a9a64dfd1a97c2 (diff)
parentbd750af785037a105dc3347d3bd38cb49a003dc2 (diff)
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
-rw-r--r--app/assets/javascripts/jobs/components/empty_state.vue2
-rw-r--r--app/assets/javascripts/jobs/components/job_app.vue19
-rw-r--r--app/assets/javascripts/jobs/components/unmet_prerequisites_block.vue30
-rw-r--r--app/assets/javascripts/jobs/index.js1
-rw-r--r--app/assets/javascripts/jobs/store/getters.js3
-rw-r--r--app/assets/stylesheets/framework/callout.scss4
-rw-r--r--app/serializers/build_details_entity.rb2
-rw-r--r--app/views/projects/jobs/show.html.haml1
-rw-r--r--changelogs/unreleased/54506-show-error-when-namespace-svc-missing.yml5
-rw-r--r--doc/user/project/clusters/index.md2
-rw-r--r--lib/gitlab/ci/status/build/factory.rb3
-rw-r--r--lib/gitlab/ci/status/build/failed_unmet_prerequisites.rb24
-rw-r--r--locale/gitlab.pot9
-rw-r--r--spec/factories/ci/builds.rb5
-rw-r--r--spec/javascripts/jobs/components/job_app_spec.js36
-rw-r--r--spec/javascripts/jobs/components/unmet_prerequisites_block_spec.js37
-rw-r--r--spec/lib/gitlab/ci/status/build/factory_spec.rb29
-rw-r--r--spec/lib/gitlab/ci/status/build/failed_unmet_prerequisites_spec.rb37
-rw-r--r--spec/serializers/build_details_entity_spec.rb10
19 files changed, 255 insertions, 4 deletions
diff --git a/app/assets/javascripts/jobs/components/empty_state.vue b/app/assets/javascripts/jobs/components/empty_state.vue
index 668fcf3d673..04f910b6b80 100644
--- a/app/assets/javascripts/jobs/components/empty_state.vue
+++ b/app/assets/javascripts/jobs/components/empty_state.vue
@@ -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
diff --git a/app/assets/javascripts/jobs/components/job_app.vue b/app/assets/javascripts/jobs/components/job_app.vue
index dbadd224251..0670e2b06b9 100644
--- a/app/assets/javascripts/jobs/components/job_app.vue
+++ b/app/assets/javascripts/jobs/components/job_app.vue
@@ -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"
diff --git a/app/assets/javascripts/jobs/components/unmet_prerequisites_block.vue b/app/assets/javascripts/jobs/components/unmet_prerequisites_block.vue
new file mode 100644
index 00000000000..25a8da84873
--- /dev/null
+++ b/app/assets/javascripts/jobs/components/unmet_prerequisites_block.vue
@@ -0,0 +1,30 @@
+<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>
diff --git a/app/assets/javascripts/jobs/index.js b/app/assets/javascripts/jobs/index.js
index a32e945627c..25132449458 100644
--- a/app/assets/javascripts/jobs/index.js
+++ b/app/assets/javascripts/jobs/index.js
@@ -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,
diff --git a/app/assets/javascripts/jobs/store/getters.js b/app/assets/javascripts/jobs/store/getters.js
index 73c1cbc3a99..406b1a2e375 100644
--- a/app/assets/javascripts/jobs/store/getters.js
+++ b/app/assets/javascripts/jobs/store/getters.js
@@ -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);
diff --git a/app/assets/stylesheets/framework/callout.scss b/app/assets/stylesheets/framework/callout.scss
index 0d8e4afa76f..643b20c56bc 100644
--- a/app/assets/stylesheets/framework/callout.scss
+++ b/app/assets/stylesheets/framework/callout.scss
@@ -28,6 +28,10 @@
background-color: $red-100;
border-color: $red-200;
color: $red-700;
+
+ a {
+ color: $red-700;
+ }
}
.bs-callout-warning {
diff --git a/app/serializers/build_details_entity.rb b/app/serializers/build_details_entity.rb
index 9ddce0d2c80..62c26809eeb 100644
--- a/app/serializers/build_details_entity.rb
+++ b/app/serializers/build_details_entity.rb
@@ -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
diff --git a/app/views/projects/jobs/show.html.haml b/app/views/projects/jobs/show.html.haml
index 475bae887ec..81a53f22f67 100644
--- a/app/views/projects/jobs/show.html.haml
+++ b/app/views/projects/jobs/show.html.haml
@@ -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 } }
diff --git a/changelogs/unreleased/54506-show-error-when-namespace-svc-missing.yml b/changelogs/unreleased/54506-show-error-when-namespace-svc-missing.yml
new file mode 100644
index 00000000000..3e3784d5413
--- /dev/null
+++ b/changelogs/unreleased/54506-show-error-when-namespace-svc-missing.yml
@@ -0,0 +1,5 @@
+---
+title: Show error when namespace/svc account missing
+merge_request: 26362
+author:
+type: added
diff --git a/doc/user/project/clusters/index.md b/doc/user/project/clusters/index.md
index c94a3f4d3b5..878d30dddaa 100644
--- a/doc/user/project/clusters/index.md
+++ b/doc/user/project/clusters/index.md
@@ -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).
diff --git a/lib/gitlab/ci/status/build/factory.rb b/lib/gitlab/ci/status/build/factory.rb
index f7d0715e617..96d05842838 100644
--- a/lib/gitlab/ci/status/build/factory.rb
+++ b/lib/gitlab/ci/status/build/factory.rb
@@ -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,
diff --git a/lib/gitlab/ci/status/build/failed_unmet_prerequisites.rb b/lib/gitlab/ci/status/build/failed_unmet_prerequisites.rb
new file mode 100644
index 00000000000..eaad3969a4c
--- /dev/null
+++ b/lib/gitlab/ci/status/build/failed_unmet_prerequisites.rb
@@ -0,0 +1,24 @@
+# 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
diff --git a/locale/gitlab.pot b/locale/gitlab.pot
index c9c7cbce95a..63a4fe9c52b 100644
--- a/locale/gitlab.pot
+++ b/locale/gitlab.pot
@@ -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 ""
diff --git a/spec/factories/ci/builds.rb b/spec/factories/ci/builds.rb
index 067391c1179..f8c494c159e 100644
--- a/spec/factories/ci/builds.rb
+++ b/spec/factories/ci/builds.rb
@@ -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')
diff --git a/spec/javascripts/jobs/components/job_app_spec.js b/spec/javascripts/jobs/components/job_app_spec.js
index ba5d672f189..cef40117304 100644
--- a/spec/javascripts/jobs/components/job_app_spec.js
+++ b/spec/javascripts/jobs/components/job_app_spec.js
@@ -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(
diff --git a/spec/javascripts/jobs/components/unmet_prerequisites_block_spec.js b/spec/javascripts/jobs/components/unmet_prerequisites_block_spec.js
new file mode 100644
index 00000000000..68fcb321214
--- /dev/null
+++ b/spec/javascripts/jobs/components/unmet_prerequisites_block_spec.js
@@ -0,0 +1,37 @@
+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);
+ });
+});
diff --git a/spec/lib/gitlab/ci/status/build/factory_spec.rb b/spec/lib/gitlab/ci/status/build/factory_spec.rb
index b379b08ad62..b6231510b91 100644
--- a/spec/lib/gitlab/ci/status/build/factory_spec.rb
+++ b/spec/lib/gitlab/ci/status/build/factory_spec.rb
@@ -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
diff --git a/spec/lib/gitlab/ci/status/build/failed_unmet_prerequisites_spec.rb b/spec/lib/gitlab/ci/status/build/failed_unmet_prerequisites_spec.rb
new file mode 100644
index 00000000000..a4854bdc6b9
--- /dev/null
+++ b/spec/lib/gitlab/ci/status/build/failed_unmet_prerequisites_spec.rb
@@ -0,0 +1,37 @@
+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
diff --git a/spec/serializers/build_details_entity_spec.rb b/spec/serializers/build_details_entity_spec.rb
index f6bd6e9ede4..1edf69dc290 100644
--- a/spec/serializers/build_details_entity_spec.rb
+++ b/spec/serializers/build_details_entity_spec.rb
@@ -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