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:
authorGitLab Bot <gitlab-bot@gitlab.com>2021-12-20 16:37:47 +0300
committerGitLab Bot <gitlab-bot@gitlab.com>2021-12-20 16:37:47 +0300
commitaee0a117a889461ce8ced6fcf73207fe017f1d99 (patch)
tree891d9ef189227a8445d83f35c1b0fc99573f4380 /app/assets/javascripts/runner
parent8d46af3258650d305f53b819eabf7ab18d22f59e (diff)
Add latest changes from gitlab-org/gitlab@14-6-stable-eev14.6.0-rc42
Diffstat (limited to 'app/assets/javascripts/runner')
-rw-r--r--app/assets/javascripts/runner/admin_runners/admin_runners_app.vue18
-rw-r--r--app/assets/javascripts/runner/components/cells/runner_actions_cell.vue83
-rw-r--r--app/assets/javascripts/runner/components/cells/runner_status_cell.vue12
-rw-r--r--app/assets/javascripts/runner/components/cells/runner_summary_cell.vue2
-rw-r--r--app/assets/javascripts/runner/components/runner_delete_modal.vue51
-rw-r--r--app/assets/javascripts/runner/components/runner_filtered_search_bar.vue32
-rw-r--r--app/assets/javascripts/runner/components/runner_list.vue36
-rw-r--r--app/assets/javascripts/runner/components/runner_status_badge.vue (renamed from app/assets/javascripts/runner/components/runner_contacted_state_badge.vue)35
-rw-r--r--app/assets/javascripts/runner/components/search_tokens/status_token_config.js2
-rw-r--r--app/assets/javascripts/runner/components/search_tokens/tag_token.vue1
-rw-r--r--app/assets/javascripts/runner/components/stat/runner_online_stat.vue17
-rw-r--r--app/assets/javascripts/runner/constants.js17
-rw-r--r--app/assets/javascripts/runner/graphql/get_group_runners.query.graphql1
-rw-r--r--app/assets/javascripts/runner/graphql/get_runner.query.graphql2
-rw-r--r--app/assets/javascripts/runner/graphql/runner_node.fragment.graphql3
-rw-r--r--app/assets/javascripts/runner/graphql/runner_update.mutation.graphql2
-rw-r--r--app/assets/javascripts/runner/group_runners/group_runners_app.vue10
17 files changed, 214 insertions, 110 deletions
diff --git a/app/assets/javascripts/runner/admin_runners/admin_runners_app.vue b/app/assets/javascripts/runner/admin_runners/admin_runners_app.vue
index 3edb658eaf5..f8220553db6 100644
--- a/app/assets/javascripts/runner/admin_runners/admin_runners_app.vue
+++ b/app/assets/javascripts/runner/admin_runners/admin_runners_app.vue
@@ -3,12 +3,12 @@ import { GlBadge, GlLink } from '@gitlab/ui';
import createFlash from '~/flash';
import { fetchPolicies } from '~/lib/graphql';
import { updateHistory } from '~/lib/utils/url_utility';
-import { sprintf, __ } from '~/locale';
import RegistrationDropdown from '../components/registration/registration_dropdown.vue';
import RunnerFilteredSearchBar from '../components/runner_filtered_search_bar.vue';
import RunnerList from '../components/runner_list.vue';
import RunnerName from '../components/runner_name.vue';
+import RunnerOnlineStat from '../components/stat/runner_online_stat.vue';
import RunnerPagination from '../components/runner_pagination.vue';
import RunnerTypeTabs from '../components/runner_type_tabs.vue';
@@ -38,6 +38,7 @@ export default {
RunnerFilteredSearchBar,
RunnerList,
RunnerName,
+ RunnerOnlineStat,
RunnerPagination,
RunnerTypeTabs,
},
@@ -110,17 +111,12 @@ export default {
noRunnersFound() {
return !this.runnersLoading && !this.runners.items.length;
},
- activeRunnersMessage() {
- return sprintf(__('Runners currently online: %{active_runners_count}'), {
- active_runners_count: this.activeRunnersCount,
- });
- },
searchTokens() {
return [
statusTokenConfig,
{
...tagTokenConfig,
- recentTokenValuesStorageKey: `${this.$options.filteredSearchNamespace}-recent-tags`,
+ recentSuggestionsStorageKey: `${this.$options.filteredSearchNamespace}-recent-tags`,
},
];
},
@@ -165,6 +161,8 @@ export default {
</script>
<template>
<div>
+ <runner-online-stat class="gl-py-6 gl-px-5" :value="activeRunnersCount" />
+
<div
class="gl-display-flex gl-align-items-center gl-flex-direction-column-reverse gl-md-flex-direction-row gl-mt-3 gl-md-mt-0"
>
@@ -194,11 +192,7 @@ export default {
v-model="search"
:tokens="searchTokens"
:namespace="$options.filteredSearchNamespace"
- >
- <template #runner-count>
- {{ activeRunnersMessage }}
- </template>
- </runner-filtered-search-bar>
+ />
<div v-if="noRunnersFound" class="gl-text-center gl-p-5">
{{ __('No runners found') }}
diff --git a/app/assets/javascripts/runner/components/cells/runner_actions_cell.vue b/app/assets/javascripts/runner/components/cells/runner_actions_cell.vue
index c4bddb7b398..33f7a67aba4 100644
--- a/app/assets/javascripts/runner/components/cells/runner_actions_cell.vue
+++ b/app/assets/javascripts/runner/components/cells/runner_actions_cell.vue
@@ -1,27 +1,29 @@
<script>
-import { GlButton, GlButtonGroup, GlTooltipDirective } from '@gitlab/ui';
+import { GlButton, GlButtonGroup, GlModalDirective, GlTooltipDirective } from '@gitlab/ui';
import createFlash from '~/flash';
-import { __, s__ } from '~/locale';
+import { __, s__, sprintf } from '~/locale';
import runnerDeleteMutation from '~/runner/graphql/runner_delete.mutation.graphql';
import runnerActionsUpdateMutation from '~/runner/graphql/runner_actions_update.mutation.graphql';
import { captureException } from '~/runner/sentry_utils';
+import { getIdFromGraphQLId } from '~/graphql_shared/utils';
+import RunnerDeleteModal from '../runner_delete_modal.vue';
-const i18n = {
- I18N_EDIT: __('Edit'),
- I18N_PAUSE: __('Pause'),
- I18N_RESUME: __('Resume'),
- I18N_REMOVE: __('Remove'),
- I18N_REMOVE_CONFIRMATION: s__('Runners|Are you sure you want to delete this runner?'),
-};
+const I18N_EDIT = __('Edit');
+const I18N_PAUSE = __('Pause');
+const I18N_RESUME = __('Resume');
+const I18N_DELETE = s__('Runners|Delete runner');
+const I18N_DELETED_TOAST = s__('Runners|Runner %{name} was deleted');
export default {
name: 'RunnerActionsCell',
components: {
GlButton,
GlButtonGroup,
+ RunnerDeleteModal,
},
directives: {
GlTooltip: GlTooltipDirective,
+ GlModal: GlModalDirective,
},
props: {
runner: {
@@ -48,21 +50,29 @@ export default {
// mouseout listeners don't run leaving the tooltip stuck
return '';
}
- return this.isActive ? i18n.I18N_PAUSE : i18n.I18N_RESUME;
+ return this.isActive ? I18N_PAUSE : I18N_RESUME;
},
deleteTitle() {
- // Prevent a "sticky" tooltip: If element gets removed,
- // mouseout listeners don't run and leaving the tooltip stuck
- return this.deleting ? '' : i18n.I18N_REMOVE;
+ if (this.deleting) {
+ // Prevent a "sticky" tooltip: If this button is disabled,
+ // mouseout listeners don't run leaving the tooltip stuck
+ return '';
+ }
+ return I18N_DELETE;
+ },
+ runnerId() {
+ return getIdFromGraphQLId(this.runner.id);
+ },
+ runnerName() {
+ return `#${this.runnerId} (${this.runner.shortSha})`;
+ },
+ runnerDeleteModalId() {
+ return `delete-runner-modal-${this.runnerId}`;
},
},
methods: {
async onToggleActive() {
this.updating = true;
- // TODO In HAML iteration we had a confirmation modal via:
- // data-confirm="_('Are you sure?')"
- // this may not have to ported, this is an easily reversible operation
-
try {
const toggledActive = !this.runner.active;
@@ -91,12 +101,8 @@ export default {
},
async onDelete() {
- // TODO Replace confirmation with gl-modal
- // eslint-disable-next-line no-alert
- if (!window.confirm(i18n.I18N_REMOVE_CONFIRMATION)) {
- return;
- }
-
+ // Deleting stays "true" until this row is removed,
+ // should only change back if the operation fails.
this.deleting = true;
try {
const {
@@ -115,11 +121,13 @@ export default {
});
if (errors && errors.length) {
throw new Error(errors.join(' '));
+ } else {
+ // Use $root to have the toast message stay after this element is removed
+ this.$root.$toast?.show(sprintf(I18N_DELETED_TOAST, { name: this.runnerName }));
}
} catch (e) {
- this.onError(e);
- } finally {
this.deleting = false;
+ this.onError(e);
}
},
@@ -133,14 +141,15 @@ export default {
captureException({ error, component: this.$options.name });
},
},
- i18n,
+ I18N_EDIT,
+ I18N_DELETE,
};
</script>
<template>
<gl-button-group>
<!--
- This button appears for administratos: those with
+ This button appears for administrators: those with
access to the adminUrl. More advanced permissions policies
will allow more granular permissions.
@@ -148,16 +157,14 @@ export default {
-->
<gl-button
v-if="runner.adminUrl"
- v-gl-tooltip.hover.viewport
+ v-gl-tooltip.hover.viewport="$options.I18N_EDIT"
:href="runner.adminUrl"
- :title="$options.i18n.I18N_EDIT"
- :aria-label="$options.i18n.I18N_EDIT"
+ :aria-label="$options.I18N_EDIT"
icon="pencil"
data-testid="edit-runner"
/>
<gl-button
- v-gl-tooltip.hover.viewport
- :title="toggleActiveTitle"
+ v-gl-tooltip.hover.viewport="toggleActiveTitle"
:aria-label="toggleActiveTitle"
:icon="toggleActiveIcon"
:loading="updating"
@@ -165,14 +172,20 @@ export default {
@click="onToggleActive"
/>
<gl-button
- v-gl-tooltip.hover.viewport
- :title="deleteTitle"
+ v-gl-tooltip.hover.viewport="deleteTitle"
+ v-gl-modal="runnerDeleteModalId"
:aria-label="deleteTitle"
icon="close"
:loading="deleting"
variant="danger"
data-testid="delete-runner"
- @click="onDelete"
+ />
+
+ <runner-delete-modal
+ :ref="runnerDeleteModalId"
+ :modal-id="runnerDeleteModalId"
+ :runner-name="runnerName"
+ @primary="onDelete"
/>
</gl-button-group>
</template>
diff --git a/app/assets/javascripts/runner/components/cells/runner_status_cell.vue b/app/assets/javascripts/runner/components/cells/runner_status_cell.vue
index 9ba1192bc8c..473cd7e9794 100644
--- a/app/assets/javascripts/runner/components/cells/runner_status_cell.vue
+++ b/app/assets/javascripts/runner/components/cells/runner_status_cell.vue
@@ -1,14 +1,12 @@
<script>
import { GlTooltipDirective } from '@gitlab/ui';
-import RunnerContactedStateBadge from '../runner_contacted_state_badge.vue';
+import RunnerStatusBadge from '../runner_status_badge.vue';
import RunnerPausedBadge from '../runner_paused_badge.vue';
-import { I18N_LOCKED_RUNNER_DESCRIPTION, I18N_PAUSED_RUNNER_DESCRIPTION } from '../../constants';
-
export default {
components: {
- RunnerContactedStateBadge,
+ RunnerStatusBadge,
RunnerPausedBadge,
},
directives: {
@@ -25,16 +23,12 @@ export default {
return !this.runner.active;
},
},
- i18n: {
- I18N_LOCKED_RUNNER_DESCRIPTION,
- I18N_PAUSED_RUNNER_DESCRIPTION,
- },
};
</script>
<template>
<div>
- <runner-contacted-state-badge :runner="runner" size="sm" />
+ <runner-status-badge :runner="runner" size="sm" />
<runner-paused-badge v-if="paused" size="sm" />
</div>
</template>
diff --git a/app/assets/javascripts/runner/components/cells/runner_summary_cell.vue b/app/assets/javascripts/runner/components/cells/runner_summary_cell.vue
index 3b476997915..937ec631633 100644
--- a/app/assets/javascripts/runner/components/cells/runner_summary_cell.vue
+++ b/app/assets/javascripts/runner/components/cells/runner_summary_cell.vue
@@ -1,7 +1,7 @@
<script>
import { GlIcon, GlTooltipDirective } from '@gitlab/ui';
-import TooltipOnTruncate from '~/vue_shared/components/tooltip_on_truncate.vue';
+import TooltipOnTruncate from '~/vue_shared/components/tooltip_on_truncate/tooltip_on_truncate.vue';
import RunnerName from '../runner_name.vue';
import RunnerTypeBadge from '../runner_type_badge.vue';
diff --git a/app/assets/javascripts/runner/components/runner_delete_modal.vue b/app/assets/javascripts/runner/components/runner_delete_modal.vue
new file mode 100644
index 00000000000..8be216a7eb5
--- /dev/null
+++ b/app/assets/javascripts/runner/components/runner_delete_modal.vue
@@ -0,0 +1,51 @@
+<script>
+import { GlModal } from '@gitlab/ui';
+import { __, s__, sprintf } from '~/locale';
+
+const I18N_TITLE = s__('Runners|Delete runner %{name}?');
+const I18N_BODY = s__(
+ 'Runners|The runner will be permanently deleted and no longer available for projects or groups in the instance. Are you sure you want to continue?',
+);
+const I18N_PRIMARY = s__('Runners|Delete runner');
+const I18N_CANCEL = __('Cancel');
+
+export default {
+ components: {
+ GlModal,
+ },
+ props: {
+ runnerName: {
+ type: String,
+ required: true,
+ },
+ },
+ computed: {
+ title() {
+ return sprintf(I18N_TITLE, { name: this.runnerName });
+ },
+ },
+ methods: {
+ onPrimary() {
+ this.$refs.modal.hide();
+ },
+ },
+ actionPrimary: { text: I18N_PRIMARY, attributes: { variant: 'danger' } },
+ actionCancel: { text: I18N_CANCEL },
+ I18N_BODY,
+};
+</script>
+
+<template>
+ <gl-modal
+ ref="modal"
+ size="sm"
+ :title="title"
+ :action-primary="$options.actionPrimary"
+ :action-cancel="$options.actionCancel"
+ v-bind="$attrs"
+ v-on="$listeners"
+ @primary="onPrimary"
+ >
+ {{ $options.I18N_BODY }}
+ </gl-modal>
+</template>
diff --git a/app/assets/javascripts/runner/components/runner_filtered_search_bar.vue b/app/assets/javascripts/runner/components/runner_filtered_search_bar.vue
index a9dfec35479..f0f8bbdf5df 100644
--- a/app/assets/javascripts/runner/components/runner_filtered_search_bar.vue
+++ b/app/assets/javascripts/runner/components/runner_filtered_search_bar.vue
@@ -76,24 +76,18 @@ export default {
};
</script>
<template>
- <div
+ <filtered-search
class="gl-bg-gray-10 gl-p-5 gl-border-solid gl-border-gray-100 gl-border-0 gl-border-t-1 gl-border-b-1"
- >
- <filtered-search
- v-bind="$attrs"
- :namespace="namespace"
- recent-searches-storage-key="runners-search"
- :sort-options="$options.sortOptions"
- :initial-filter-value="initialFilterValue"
- :tokens="tokens"
- :initial-sort-by="initialSortBy"
- :search-input-placeholder="__('Search or filter results...')"
- data-testid="runners-filtered-search"
- @onFilter="onFilter"
- @onSort="onSort"
- />
- <div class="gl-text-right" data-testid="runner-count">
- <slot name="runner-count"></slot>
- </div>
- </div>
+ v-bind="$attrs"
+ :namespace="namespace"
+ recent-searches-storage-key="runners-search"
+ :sort-options="$options.sortOptions"
+ :initial-filter-value="initialFilterValue"
+ :tokens="tokens"
+ :initial-sort-by="initialSortBy"
+ :search-input-placeholder="__('Search or filter results...')"
+ data-testid="runners-filtered-search"
+ @onFilter="onFilter"
+ @onSort="onSort"
+ />
</template>
diff --git a/app/assets/javascripts/runner/components/runner_list.vue b/app/assets/javascripts/runner/components/runner_list.vue
index f8dbc469c22..023308dbac2 100644
--- a/app/assets/javascripts/runner/components/runner_list.vue
+++ b/app/assets/javascripts/runner/components/runner_list.vue
@@ -1,27 +1,26 @@
<script>
import { GlTable, GlTooltipDirective, GlSkeletonLoader } from '@gitlab/ui';
+import TooltipOnTruncate from '~/vue_shared/components/tooltip_on_truncate/tooltip_on_truncate.vue';
import { getIdFromGraphQLId } from '~/graphql_shared/utils';
-import { __, s__ } from '~/locale';
+import { formatNumber, __, s__ } from '~/locale';
import TimeAgo from '~/vue_shared/components/time_ago_tooltip.vue';
+import { RUNNER_JOB_COUNT_LIMIT } from '../constants';
import RunnerActionsCell from './cells/runner_actions_cell.vue';
import RunnerSummaryCell from './cells/runner_summary_cell.vue';
import RunnerStatusCell from './cells/runner_status_cell.vue';
import RunnerTags from './runner_tags.vue';
-const tableField = ({ key, label = '', width = 10 }) => {
+const tableField = ({ key, label = '', thClasses = [] }) => {
return {
key,
label,
thClass: [
- `gl-w-${width}p`,
'gl-bg-transparent!',
'gl-border-b-solid!',
'gl-border-b-gray-100!',
- 'gl-py-5!',
- 'gl-px-0!',
'gl-border-b-1!',
+ ...thClasses,
],
- tdClass: ['gl-py-5!', 'gl-px-1!'],
tdAttr: {
'data-testid': `td-${key}`,
},
@@ -32,6 +31,7 @@ export default {
components: {
GlTable,
GlSkeletonLoader,
+ TooltipOnTruncate,
TimeAgo,
RunnerActionsCell,
RunnerSummaryCell,
@@ -53,6 +53,12 @@ export default {
},
},
methods: {
+ formatJobCount(jobCount) {
+ if (jobCount > RUNNER_JOB_COUNT_LIMIT) {
+ return `${formatNumber(RUNNER_JOB_COUNT_LIMIT)}+`;
+ }
+ return formatNumber(jobCount);
+ },
runnerTrAttr(runner) {
if (runner) {
return {
@@ -64,10 +70,11 @@ export default {
},
fields: [
tableField({ key: 'status', label: s__('Runners|Status') }),
- tableField({ key: 'summary', label: s__('Runners|Runner ID'), width: 30 }),
+ tableField({ key: 'summary', label: s__('Runners|Runner ID'), thClasses: ['gl-lg-w-25p'] }),
tableField({ key: 'version', label: __('Version') }),
tableField({ key: 'ipAddress', label: __('IP Address') }),
- tableField({ key: 'tagList', label: __('Tags'), width: 20 }),
+ tableField({ key: 'jobCount', label: __('Jobs') }),
+ tableField({ key: 'tagList', label: __('Tags'), thClasses: ['gl-lg-w-25p'] }),
tableField({ key: 'contactedAt', label: __('Last contact') }),
tableField({ key: 'actions', label: '' }),
],
@@ -82,6 +89,7 @@ export default {
:tbody-tr-attr="runnerTrAttr"
data-testid="runner-list"
stacked="md"
+ primary-key="id"
fixed
>
<template v-if="!runners.length" #table-busy>
@@ -101,11 +109,19 @@ export default {
</template>
<template #cell(version)="{ item: { version } }">
- {{ version }}
+ <tooltip-on-truncate class="gl-display-block gl-text-truncate" :title="version">
+ {{ version }}
+ </tooltip-on-truncate>
</template>
<template #cell(ipAddress)="{ item: { ipAddress } }">
- {{ ipAddress }}
+ <tooltip-on-truncate class="gl-display-block gl-text-truncate" :title="ipAddress">
+ {{ ipAddress }}
+ </tooltip-on-truncate>
+ </template>
+
+ <template #cell(jobCount)="{ item: { jobCount } }">
+ {{ formatJobCount(jobCount) }}
</template>
<template #cell(tagList)="{ item: { tagList } }">
diff --git a/app/assets/javascripts/runner/components/runner_contacted_state_badge.vue b/app/assets/javascripts/runner/components/runner_status_badge.vue
index b4727f832f8..0823876a187 100644
--- a/app/assets/javascripts/runner/components/runner_contacted_state_badge.vue
+++ b/app/assets/javascripts/runner/components/runner_status_badge.vue
@@ -1,14 +1,17 @@
<script>
import { GlBadge, GlTooltipDirective } from '@gitlab/ui';
-import { s__, sprintf } from '~/locale';
+import { __, s__, sprintf } from '~/locale';
import { getTimeago } from '~/lib/utils/datetime_utility';
import {
- I18N_ONLINE_RUNNER_DESCRIPTION,
- I18N_OFFLINE_RUNNER_DESCRIPTION,
+ I18N_ONLINE_RUNNER_TIMEAGO_DESCRIPTION,
I18N_NOT_CONNECTED_RUNNER_DESCRIPTION,
+ I18N_OFFLINE_RUNNER_TIMEAGO_DESCRIPTION,
+ I18N_STALE_RUNNER_DESCRIPTION,
STATUS_ONLINE,
- STATUS_OFFLINE,
STATUS_NOT_CONNECTED,
+ STATUS_NEVER_CONTACTED,
+ STATUS_OFFLINE,
+ STATUS_STALE,
} from '../constants';
export default {
@@ -29,31 +32,39 @@ export default {
if (this.runner.contactedAt) {
return getTimeago().format(this.runner.contactedAt);
}
- return null;
+ // Prevent "just now" from being rendered, in case data is missing.
+ return __('n/a');
},
badge() {
- switch (this.runner.status) {
+ switch (this.runner?.status) {
case STATUS_ONLINE:
return {
variant: 'success',
label: s__('Runners|online'),
- tooltip: sprintf(I18N_ONLINE_RUNNER_DESCRIPTION, {
+ tooltip: sprintf(I18N_ONLINE_RUNNER_TIMEAGO_DESCRIPTION, {
timeAgo: this.contactedAtTimeAgo,
}),
};
+ case STATUS_NOT_CONNECTED:
+ case STATUS_NEVER_CONTACTED:
+ return {
+ variant: 'muted',
+ label: s__('Runners|not connected'),
+ tooltip: I18N_NOT_CONNECTED_RUNNER_DESCRIPTION,
+ };
case STATUS_OFFLINE:
return {
variant: 'muted',
label: s__('Runners|offline'),
- tooltip: sprintf(I18N_OFFLINE_RUNNER_DESCRIPTION, {
+ tooltip: sprintf(I18N_OFFLINE_RUNNER_TIMEAGO_DESCRIPTION, {
timeAgo: this.contactedAtTimeAgo,
}),
};
- case STATUS_NOT_CONNECTED:
+ case STATUS_STALE:
return {
- variant: 'muted',
- label: s__('Runners|not connected'),
- tooltip: I18N_NOT_CONNECTED_RUNNER_DESCRIPTION,
+ variant: 'warning',
+ label: s__('Runners|stale'),
+ tooltip: I18N_STALE_RUNNER_DESCRIPTION,
};
default:
return null;
diff --git a/app/assets/javascripts/runner/components/search_tokens/status_token_config.js b/app/assets/javascripts/runner/components/search_tokens/status_token_config.js
index 9963048ae1d..4b356fa47ed 100644
--- a/app/assets/javascripts/runner/components/search_tokens/status_token_config.js
+++ b/app/assets/javascripts/runner/components/search_tokens/status_token_config.js
@@ -7,6 +7,7 @@ import {
STATUS_ONLINE,
STATUS_OFFLINE,
STATUS_NOT_CONNECTED,
+ STATUS_STALE,
PARAM_KEY_STATUS,
} from '../../constants';
@@ -16,6 +17,7 @@ const options = [
{ value: STATUS_ONLINE, title: s__('Runners|Online') },
{ value: STATUS_OFFLINE, title: s__('Runners|Offline') },
{ value: STATUS_NOT_CONNECTED, title: s__('Runners|Not connected') },
+ { value: STATUS_STALE, title: s__('Runners|Stale') },
];
export const statusTokenConfig = {
diff --git a/app/assets/javascripts/runner/components/search_tokens/tag_token.vue b/app/assets/javascripts/runner/components/search_tokens/tag_token.vue
index ab67ac608e2..7461308ab91 100644
--- a/app/assets/javascripts/runner/components/search_tokens/tag_token.vue
+++ b/app/assets/javascripts/runner/components/search_tokens/tag_token.vue
@@ -68,7 +68,6 @@ export default {
:config="config"
:suggestions-loading="loading"
:suggestions="tags"
- :recent-suggestions-storage-key="config.recentTokenValuesStorageKey"
@fetch-suggestions="fetchTags"
v-on="$listeners"
>
diff --git a/app/assets/javascripts/runner/components/stat/runner_online_stat.vue b/app/assets/javascripts/runner/components/stat/runner_online_stat.vue
new file mode 100644
index 00000000000..b92b9badef0
--- /dev/null
+++ b/app/assets/javascripts/runner/components/stat/runner_online_stat.vue
@@ -0,0 +1,17 @@
+<script>
+import { GlSingleStat } from '@gitlab/ui/dist/charts';
+
+export default {
+ components: {
+ GlSingleStat,
+ },
+};
+</script>
+<template>
+ <gl-single-stat
+ v-bind="$attrs"
+ variant="success"
+ :title="s__('Runners|Online Runners')"
+ :meta-text="s__('Runners|online')"
+ />
+</template>
diff --git a/app/assets/javascripts/runner/constants.js b/app/assets/javascripts/runner/constants.js
index 3952e2398e0..355f3054917 100644
--- a/app/assets/javascripts/runner/constants.js
+++ b/app/assets/javascripts/runner/constants.js
@@ -1,6 +1,7 @@
import { s__ } from '~/locale';
export const RUNNER_PAGE_SIZE = 20;
+export const RUNNER_JOB_COUNT_LIMIT = 1000;
export const GROUP_RUNNER_COUNT_LIMIT = 1000;
export const I18N_FETCH_ERROR = s__('Runners|Something went wrong while fetching runner data.');
@@ -14,15 +15,18 @@ export const I18N_GROUP_RUNNER_DESCRIPTION = s__(
export const I18N_PROJECT_RUNNER_DESCRIPTION = s__('Runners|Associated with one or more projects');
// Status
-export const I18N_ONLINE_RUNNER_DESCRIPTION = s__(
+export const I18N_ONLINE_RUNNER_TIMEAGO_DESCRIPTION = s__(
'Runners|Runner is online; last contact was %{timeAgo}',
);
-export const I18N_OFFLINE_RUNNER_DESCRIPTION = s__(
- 'Runners|No recent contact from this runner; last contact was %{timeAgo}',
-);
export const I18N_NOT_CONNECTED_RUNNER_DESCRIPTION = s__(
'Runners|This runner has never connected to this instance',
);
+export const I18N_OFFLINE_RUNNER_TIMEAGO_DESCRIPTION = s__(
+ 'Runners|No recent contact from this runner; last contact was %{timeAgo}',
+);
+export const I18N_STALE_RUNNER_DESCRIPTION = s__(
+ 'Runners|No contact from this runner in over 3 months',
+);
export const I18N_LOCKED_RUNNER_DESCRIPTION = s__('Runners|You cannot assign to other projects');
export const I18N_PAUSED_RUNNER_DESCRIPTION = s__('Runners|Not available to run jobs');
@@ -54,9 +58,12 @@ export const PROJECT_TYPE = 'PROJECT_TYPE';
export const STATUS_ACTIVE = 'ACTIVE';
export const STATUS_PAUSED = 'PAUSED';
+
export const STATUS_ONLINE = 'ONLINE';
-export const STATUS_OFFLINE = 'OFFLINE';
export const STATUS_NOT_CONNECTED = 'NOT_CONNECTED';
+export const STATUS_NEVER_CONTACTED = 'NEVER_CONTACTED';
+export const STATUS_OFFLINE = 'OFFLINE';
+export const STATUS_STALE = 'STALE';
// CiRunnerAccessLevel
diff --git a/app/assets/javascripts/runner/graphql/get_group_runners.query.graphql b/app/assets/javascripts/runner/graphql/get_group_runners.query.graphql
index 3e5109b1ac4..6da9e276f74 100644
--- a/app/assets/javascripts/runner/graphql/get_group_runners.query.graphql
+++ b/app/assets/javascripts/runner/graphql/get_group_runners.query.graphql
@@ -13,6 +13,7 @@ query getGroupRunners(
$sort: CiRunnerSort
) {
group(fullPath: $groupFullPath) {
+ id
runners(
membership: DESCENDANTS
before: $before
diff --git a/app/assets/javascripts/runner/graphql/get_runner.query.graphql b/app/assets/javascripts/runner/graphql/get_runner.query.graphql
index c294cb9bf22..59c55eae060 100644
--- a/app/assets/javascripts/runner/graphql/get_runner.query.graphql
+++ b/app/assets/javascripts/runner/graphql/get_runner.query.graphql
@@ -1,6 +1,8 @@
#import "ee_else_ce/runner/graphql/runner_details.fragment.graphql"
query getRunner($id: CiRunnerID!) {
+ # We have an id in deeply nested fragment
+ # eslint-disable-next-line @graphql-eslint/require-id-when-available
runner(id: $id) {
...RunnerDetails
}
diff --git a/app/assets/javascripts/runner/graphql/runner_node.fragment.graphql b/app/assets/javascripts/runner/graphql/runner_node.fragment.graphql
index 98f2dab26ca..169f6ffd2ea 100644
--- a/app/assets/javascripts/runner/graphql/runner_node.fragment.graphql
+++ b/app/assets/javascripts/runner/graphql/runner_node.fragment.graphql
@@ -8,7 +8,8 @@ fragment RunnerNode on CiRunner {
ipAddress
active
locked
+ jobCount
tagList
contactedAt
- status
+ status(legacyMode: null)
}
diff --git a/app/assets/javascripts/runner/graphql/runner_update.mutation.graphql b/app/assets/javascripts/runner/graphql/runner_update.mutation.graphql
index ea622fd4958..8d1b75828be 100644
--- a/app/assets/javascripts/runner/graphql/runner_update.mutation.graphql
+++ b/app/assets/javascripts/runner/graphql/runner_update.mutation.graphql
@@ -5,6 +5,8 @@
mutation runnerUpdate($input: RunnerUpdateInput!) {
runnerUpdate(input: $input) {
+ # We have an id in deep nested fragment
+ # eslint-disable-next-line @graphql-eslint/require-id-when-available
runner {
...RunnerDetails
}
diff --git a/app/assets/javascripts/runner/group_runners/group_runners_app.vue b/app/assets/javascripts/runner/group_runners/group_runners_app.vue
index c3dfa885f27..a58a53a6a0d 100644
--- a/app/assets/javascripts/runner/group_runners/group_runners_app.vue
+++ b/app/assets/javascripts/runner/group_runners/group_runners_app.vue
@@ -9,6 +9,7 @@ import RegistrationDropdown from '../components/registration/registration_dropdo
import RunnerFilteredSearchBar from '../components/runner_filtered_search_bar.vue';
import RunnerList from '../components/runner_list.vue';
import RunnerName from '../components/runner_name.vue';
+import RunnerOnlineStat from '../components/stat/runner_online_stat.vue';
import RunnerPagination from '../components/runner_pagination.vue';
import RunnerTypeTabs from '../components/runner_type_tabs.vue';
@@ -35,6 +36,7 @@ export default {
RunnerFilteredSearchBar,
RunnerList,
RunnerName,
+ RunnerOnlineStat,
RunnerPagination,
RunnerTypeTabs,
},
@@ -145,6 +147,8 @@ export default {
<template>
<div>
+ <runner-online-stat class="gl-py-6 gl-px-5" :value="groupRunnersCount" />
+
<div class="gl-display-flex gl-align-items-center">
<runner-type-tabs
v-model="search"
@@ -164,11 +168,7 @@ export default {
v-model="search"
:tokens="searchTokens"
:namespace="filteredSearchNamespace"
- >
- <template #runner-count>
- {{ runnerCountMessage }}
- </template>
- </runner-filtered-search-bar>
+ />
<div v-if="noRunnersFound" class="gl-text-center gl-p-5">
{{ __('No runners found') }}