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:
Diffstat (limited to 'app/assets/javascripts/runner')
-rw-r--r--app/assets/javascripts/runner/admin_runner_edit/admin_runner_edit_app.vue21
-rw-r--r--app/assets/javascripts/runner/admin_runner_edit/index.js3
-rw-r--r--app/assets/javascripts/runner/admin_runner_show/admin_runner_show_app.vue20
-rw-r--r--app/assets/javascripts/runner/admin_runner_show/index.js6
-rw-r--r--app/assets/javascripts/runner/admin_runners/admin_runners_app.vue104
-rw-r--r--app/assets/javascripts/runner/admin_runners/index.js3
-rw-r--r--app/assets/javascripts/runner/components/registration/registration_dropdown.vue2
-rw-r--r--app/assets/javascripts/runner/components/registration/registration_token_reset_dropdown_item.vue8
-rw-r--r--app/assets/javascripts/runner/components/runner_delete_button.vue2
-rw-r--r--app/assets/javascripts/runner/components/runner_details.vue3
-rw-r--r--app/assets/javascripts/runner/components/runner_jobs.vue2
-rw-r--r--app/assets/javascripts/runner/components/runner_pagination.vue4
-rw-r--r--app/assets/javascripts/runner/components/runner_projects.vue2
-rw-r--r--app/assets/javascripts/runner/components/runner_update_form.vue210
-rw-r--r--app/assets/javascripts/runner/graphql/details/runner.query.graphql9
-rw-r--r--app/assets/javascripts/runner/graphql/details/runner_details.fragment.graphql5
-rw-r--r--app/assets/javascripts/runner/graphql/details/runner_details_shared.fragment.graphql35
-rw-r--r--app/assets/javascripts/runner/graphql/details/runner_update.mutation.graphql15
-rw-r--r--app/assets/javascripts/runner/graphql/edit/runner_fields.fragment.graphql5
-rw-r--r--app/assets/javascripts/runner/graphql/edit/runner_fields_shared.fragment.graphql15
-rw-r--r--app/assets/javascripts/runner/graphql/edit/runner_form.query.graphql7
-rw-r--r--app/assets/javascripts/runner/graphql/edit/runner_update.mutation.graphql13
-rw-r--r--app/assets/javascripts/runner/graphql/show/runner.query.graphql41
-rw-r--r--app/assets/javascripts/runner/graphql/show/runner_jobs.query.graphql (renamed from app/assets/javascripts/runner/graphql/details/runner_jobs.query.graphql)0
-rw-r--r--app/assets/javascripts/runner/graphql/show/runner_projects.query.graphql (renamed from app/assets/javascripts/runner/graphql/details/runner_projects.query.graphql)0
-rw-r--r--app/assets/javascripts/runner/group_runners/group_runners_app.vue111
-rw-r--r--app/assets/javascripts/runner/local_storage_alert/constants.js1
-rw-r--r--app/assets/javascripts/runner/local_storage_alert/save_alert_to_local_storage.js8
-rw-r--r--app/assets/javascripts/runner/local_storage_alert/show_alert_from_local_storage.js18
-rw-r--r--app/assets/javascripts/runner/runner_search_utils.js6
-rw-r--r--app/assets/javascripts/runner/utils.js3
31 files changed, 389 insertions, 293 deletions
diff --git a/app/assets/javascripts/runner/admin_runner_edit/admin_runner_edit_app.vue b/app/assets/javascripts/runner/admin_runner_edit/admin_runner_edit_app.vue
index c2db3b9facd..40787cf72da 100644
--- a/app/assets/javascripts/runner/admin_runner_edit/admin_runner_edit_app.vue
+++ b/app/assets/javascripts/runner/admin_runner_edit/admin_runner_edit_app.vue
@@ -5,7 +5,7 @@ import { convertToGraphQLId } from '~/graphql_shared/utils';
import RunnerHeader from '../components/runner_header.vue';
import RunnerUpdateForm from '../components/runner_update_form.vue';
import { I18N_FETCH_ERROR } from '../constants';
-import runnerQuery from '../graphql/details/runner.query.graphql';
+import runnerFormQuery from '../graphql/edit/runner_form.query.graphql';
import { captureException } from '../sentry_utils';
export default {
@@ -19,6 +19,11 @@ export default {
type: String,
required: true,
},
+ runnerPath: {
+ type: String,
+ required: false,
+ default: null,
+ },
},
data() {
return {
@@ -27,7 +32,7 @@ export default {
},
apollo: {
runner: {
- query: runnerQuery,
+ query: runnerFormQuery,
variables() {
return {
id: convertToGraphQLId(TYPE_CI_RUNNER, this.runnerId),
@@ -40,6 +45,11 @@ export default {
},
},
},
+ computed: {
+ loading() {
+ return this.$apollo.queries.runner.loading;
+ },
+ },
errorCaptured(error) {
this.reportToSentry(error);
},
@@ -53,6 +63,11 @@ export default {
<template>
<div>
<runner-header v-if="runner" :runner="runner" />
- <runner-update-form :runner="runner" class="gl-my-5" />
+ <runner-update-form
+ :loading="loading"
+ :runner="runner"
+ :runner-path="runnerPath"
+ class="gl-my-5"
+ />
</div>
</template>
diff --git a/app/assets/javascripts/runner/admin_runner_edit/index.js b/app/assets/javascripts/runner/admin_runner_edit/index.js
index adb420f9963..a2ac5731a62 100644
--- a/app/assets/javascripts/runner/admin_runner_edit/index.js
+++ b/app/assets/javascripts/runner/admin_runner_edit/index.js
@@ -12,7 +12,7 @@ export const initAdminRunnerEdit = (selector = '#js-admin-runner-edit') => {
return null;
}
- const { runnerId } = el.dataset;
+ const { runnerId, runnerPath } = el.dataset;
const apolloProvider = new VueApollo({
defaultClient: createDefaultClient(),
@@ -25,6 +25,7 @@ export const initAdminRunnerEdit = (selector = '#js-admin-runner-edit') => {
return h(AdminRunnerEditApp, {
props: {
runnerId,
+ runnerPath,
},
});
},
diff --git a/app/assets/javascripts/runner/admin_runner_show/admin_runner_show_app.vue b/app/assets/javascripts/runner/admin_runner_show/admin_runner_show_app.vue
index 86ad912f017..c3f317b40b0 100644
--- a/app/assets/javascripts/runner/admin_runner_show/admin_runner_show_app.vue
+++ b/app/assets/javascripts/runner/admin_runner_show/admin_runner_show_app.vue
@@ -1,19 +1,23 @@
<script>
import { GlTooltipDirective } from '@gitlab/ui';
-import { createAlert } from '~/flash';
+import { createAlert, VARIANT_SUCCESS } from '~/flash';
import { TYPE_CI_RUNNER } from '~/graphql_shared/constants';
import { convertToGraphQLId } from '~/graphql_shared/utils';
+import { redirectTo } from '~/lib/utils/url_utility';
+import RunnerDeleteButton from '../components/runner_delete_button.vue';
import RunnerEditButton from '../components/runner_edit_button.vue';
import RunnerPauseButton from '../components/runner_pause_button.vue';
import RunnerHeader from '../components/runner_header.vue';
import RunnerDetails from '../components/runner_details.vue';
import { I18N_FETCH_ERROR } from '../constants';
-import runnerQuery from '../graphql/details/runner.query.graphql';
+import runnerQuery from '../graphql/show/runner.query.graphql';
import { captureException } from '../sentry_utils';
+import { saveAlertToLocalStorage } from '../local_storage_alert/save_alert_to_local_storage';
export default {
name: 'AdminRunnerShowApp',
components: {
+ RunnerDeleteButton,
RunnerEditButton,
RunnerPauseButton,
RunnerHeader,
@@ -27,6 +31,10 @@ export default {
type: String,
required: true,
},
+ runnersPath: {
+ type: String,
+ required: true,
+ },
},
data() {
return {
@@ -52,6 +60,9 @@ export default {
canUpdate() {
return this.runner.userPermissions?.updateRunner;
},
+ canDelete() {
+ return this.runner.userPermissions?.deleteRunner;
+ },
},
errorCaptured(error) {
this.reportToSentry(error);
@@ -60,6 +71,10 @@ export default {
reportToSentry(error) {
captureException({ error, component: this.$options.name });
},
+ onDeleted({ message }) {
+ saveAlertToLocalStorage({ message, variant: VARIANT_SUCCESS });
+ redirectTo(this.runnersPath);
+ },
},
};
</script>
@@ -69,6 +84,7 @@ export default {
<template #actions>
<runner-edit-button v-if="canUpdate && runner.editAdminUrl" :href="runner.editAdminUrl" />
<runner-pause-button v-if="canUpdate" :runner="runner" />
+ <runner-delete-button v-if="canDelete" :runner="runner" @deleted="onDeleted" />
</template>
</runner-header>
diff --git a/app/assets/javascripts/runner/admin_runner_show/index.js b/app/assets/javascripts/runner/admin_runner_show/index.js
index a781898cf8d..ea455416648 100644
--- a/app/assets/javascripts/runner/admin_runner_show/index.js
+++ b/app/assets/javascripts/runner/admin_runner_show/index.js
@@ -1,18 +1,21 @@
import Vue from 'vue';
import VueApollo from 'vue-apollo';
import createDefaultClient from '~/lib/graphql';
+import { showAlertFromLocalStorage } from '../local_storage_alert/show_alert_from_local_storage';
import AdminRunnerShowApp from './admin_runner_show_app.vue';
Vue.use(VueApollo);
export const initAdminRunnerShow = (selector = '#js-admin-runner-show') => {
+ showAlertFromLocalStorage();
+
const el = document.querySelector(selector);
if (!el) {
return null;
}
- const { runnerId } = el.dataset;
+ const { runnerId, runnersPath } = el.dataset;
const apolloProvider = new VueApollo({
defaultClient: createDefaultClient(),
@@ -25,6 +28,7 @@ export const initAdminRunnerShow = (selector = '#js-admin-runner-show') => {
return h(AdminRunnerShowApp, {
props: {
runnerId,
+ runnersPath,
},
});
},
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 accc9926a57..c2bb635e056 100644
--- a/app/assets/javascripts/runner/admin_runners/admin_runners_app.vue
+++ b/app/assets/javascripts/runner/admin_runners/admin_runners_app.vue
@@ -38,7 +38,7 @@ import {
} from '../runner_search_utils';
import { captureException } from '../sentry_utils';
-const runnersCountSmartQuery = {
+const countSmartQuery = () => ({
query: runnersAdminCountQuery,
fetchPolicy: fetchPolicies.NETWORK_ONLY,
update(data) {
@@ -47,6 +47,39 @@ const runnersCountSmartQuery = {
error(error) {
this.reportToSentry(error);
},
+});
+
+const tabCountSmartQuery = ({ type }) => {
+ return {
+ ...countSmartQuery(),
+ variables() {
+ return {
+ ...this.countVariables,
+ type,
+ };
+ },
+ };
+};
+
+const statusCountSmartQuery = ({ status, name }) => {
+ return {
+ ...countSmartQuery(),
+ skip() {
+ // skip if filtering by status and not using _this_ status as filter
+ if (this.countVariables.status && this.countVariables.status !== status) {
+ // reset count for given status
+ this[name] = null;
+ return true;
+ }
+ return false;
+ },
+ variables() {
+ return {
+ ...this.countVariables,
+ status,
+ };
+ },
+ };
};
export default {
@@ -101,65 +134,30 @@ export default {
this.reportToSentry(error);
},
},
+
+ // Tabs counts
allRunnersCount: {
- ...runnersCountSmartQuery,
- variables() {
- return {
- ...this.countVariables,
- type: null,
- };
- },
+ ...tabCountSmartQuery({ type: null }),
},
instanceRunnersCount: {
- ...runnersCountSmartQuery,
- variables() {
- return {
- ...this.countVariables,
- type: INSTANCE_TYPE,
- };
- },
+ ...tabCountSmartQuery({ type: INSTANCE_TYPE }),
},
groupRunnersCount: {
- ...runnersCountSmartQuery,
- variables() {
- return {
- ...this.countVariables,
- type: GROUP_TYPE,
- };
- },
+ ...tabCountSmartQuery({ type: GROUP_TYPE }),
},
projectRunnersCount: {
- ...runnersCountSmartQuery,
- variables() {
- return {
- ...this.countVariables,
- type: PROJECT_TYPE,
- };
- },
+ ...tabCountSmartQuery({ type: PROJECT_TYPE }),
},
+
+ // Runner stats
onlineRunnersTotal: {
- ...runnersCountSmartQuery,
- variables() {
- return {
- status: STATUS_ONLINE,
- };
- },
+ ...statusCountSmartQuery({ status: STATUS_ONLINE, name: 'onlineRunnersTotal' }),
},
offlineRunnersTotal: {
- ...runnersCountSmartQuery,
- variables() {
- return {
- status: STATUS_OFFLINE,
- };
- },
+ ...statusCountSmartQuery({ status: STATUS_OFFLINE, name: 'offlineRunnersTotal' }),
},
staleRunnersTotal: {
- ...runnersCountSmartQuery,
- variables() {
- return {
- status: STATUS_STALE,
- };
- },
+ ...statusCountSmartQuery({ status: STATUS_STALE, name: 'staleRunnersTotal' }),
},
},
computed: {
@@ -263,12 +261,6 @@ export default {
</script>
<template>
<div>
- <runner-stats
- :online-runners-count="onlineRunnersTotal"
- :offline-runners-count="offlineRunnersTotal"
- :stale-runners-count="staleRunnersTotal"
- />
-
<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"
>
@@ -300,6 +292,12 @@ export default {
:namespace="$options.filteredSearchNamespace"
/>
+ <runner-stats
+ :online-runners-count="onlineRunnersTotal"
+ :offline-runners-count="offlineRunnersTotal"
+ :stale-runners-count="staleRunnersTotal"
+ />
+
<div v-if="noRunnersFound" class="gl-text-center gl-p-5">
{{ __('No runners found') }}
</div>
diff --git a/app/assets/javascripts/runner/admin_runners/index.js b/app/assets/javascripts/runner/admin_runners/index.js
index 12e2cb2ee9f..b1d8442bb32 100644
--- a/app/assets/javascripts/runner/admin_runners/index.js
+++ b/app/assets/javascripts/runner/admin_runners/index.js
@@ -5,12 +5,15 @@ import { visitUrl } from '~/lib/utils/url_utility';
import { updateOutdatedUrl } from '~/runner/runner_search_utils';
import createDefaultClient from '~/lib/graphql';
import { createLocalState } from '../graphql/list/local_state';
+import { showAlertFromLocalStorage } from '../local_storage_alert/show_alert_from_local_storage';
import AdminRunnersApp from './admin_runners_app.vue';
Vue.use(GlToast);
Vue.use(VueApollo);
export const initAdminRunners = (selector = '#js-admin-runners') => {
+ showAlertFromLocalStorage();
+
const el = document.querySelector(selector);
if (!el) {
diff --git a/app/assets/javascripts/runner/components/registration/registration_dropdown.vue b/app/assets/javascripts/runner/components/registration/registration_dropdown.vue
index 3fbe3c1be74..bb2a8ddf151 100644
--- a/app/assets/javascripts/runner/components/registration/registration_dropdown.vue
+++ b/app/assets/javascripts/runner/components/registration/registration_dropdown.vue
@@ -96,7 +96,7 @@ export default {
<runner-instructions-modal
v-if="instructionsModalOpened"
ref="runnerInstructionsModal"
- :registration-token="registrationToken"
+ :registration-token="currentRegistrationToken"
data-testid="runner-instructions-modal"
/>
</gl-dropdown-item>
diff --git a/app/assets/javascripts/runner/components/registration/registration_token_reset_dropdown_item.vue b/app/assets/javascripts/runner/components/registration/registration_token_reset_dropdown_item.vue
index 1234054c660..09d46ce3e66 100644
--- a/app/assets/javascripts/runner/components/registration/registration_token_reset_dropdown_item.vue
+++ b/app/assets/javascripts/runner/components/registration/registration_token_reset_dropdown_item.vue
@@ -115,14 +115,14 @@ export default {
<gl-modal
size="sm"
:modal-id="$options.modalId"
- :action-primary="{
+ :action-primary="/* eslint-disable @gitlab/vue-no-new-non-primitive-in-template */ {
text: $options.i18n.modalAction,
attributes: [{ variant: 'danger' }],
- }"
- :action-secondary="{
+ } /* eslint-enable @gitlab/vue-no-new-non-primitive-in-template */"
+ :action-secondary="/* eslint-disable @gitlab/vue-no-new-non-primitive-in-template */ {
text: $options.i18n.modalCancel,
attributes: [{ variant: 'default' }],
- }"
+ } /* eslint-enable @gitlab/vue-no-new-non-primitive-in-template */"
:title="$options.i18n.modalTitle"
@primary="handleModalPrimary"
>
diff --git a/app/assets/javascripts/runner/components/runner_delete_button.vue b/app/assets/javascripts/runner/components/runner_delete_button.vue
index b58665ecbc9..62382891df0 100644
--- a/app/assets/javascripts/runner/components/runner_delete_button.vue
+++ b/app/assets/javascripts/runner/components/runner_delete_button.vue
@@ -165,6 +165,8 @@ export default {
:loading="deleting"
:disabled="disabled"
variant="danger"
+ category="secondary"
+ v-bind="$attrs"
>
{{ buttonContent }}
</gl-button>
diff --git a/app/assets/javascripts/runner/components/runner_details.vue b/app/assets/javascripts/runner/components/runner_details.vue
index b6a5ffc7a64..3734f436034 100644
--- a/app/assets/javascripts/runner/components/runner_details.vue
+++ b/app/assets/javascripts/runner/components/runner_details.vue
@@ -84,6 +84,9 @@ export default {
</runner-detail>
<runner-detail :label="s__('Runners|Version')" :value="runner.version" />
<runner-detail :label="s__('Runners|IP Address')" :value="runner.ipAddress" />
+ <runner-detail :label="s__('Runners|Executor')" :value="runner.executorName" />
+ <runner-detail :label="s__('Runners|Architecture')" :value="runner.architectureName" />
+ <runner-detail :label="s__('Runners|Platform')" :value="runner.platformName" />
<runner-detail :label="s__('Runners|Configuration')">
<template #value>
<gl-intersperse v-if="configTextProtected || configTextUntagged">
diff --git a/app/assets/javascripts/runner/components/runner_jobs.vue b/app/assets/javascripts/runner/components/runner_jobs.vue
index b25d92d049e..4eb1312b204 100644
--- a/app/assets/javascripts/runner/components/runner_jobs.vue
+++ b/app/assets/javascripts/runner/components/runner_jobs.vue
@@ -1,7 +1,7 @@
<script>
import { GlDeprecatedSkeletonLoading as GlSkeletonLoading } from '@gitlab/ui';
import { createAlert } from '~/flash';
-import runnerJobsQuery from '../graphql/details/runner_jobs.query.graphql';
+import runnerJobsQuery from '../graphql/show/runner_jobs.query.graphql';
import { I18N_FETCH_ERROR, I18N_NO_JOBS_FOUND, RUNNER_DETAILS_JOBS_PAGE_SIZE } from '../constants';
import { captureException } from '../sentry_utils';
import { getPaginationVariables } from '../utils';
diff --git a/app/assets/javascripts/runner/components/runner_pagination.vue b/app/assets/javascripts/runner/components/runner_pagination.vue
index b683a7f2330..cfc21d1407b 100644
--- a/app/assets/javascripts/runner/components/runner_pagination.vue
+++ b/app/assets/javascripts/runner/components/runner_pagination.vue
@@ -21,10 +21,10 @@ export default {
},
computed: {
prevPage() {
- return this.pageInfo?.hasPreviousPage ? this.value?.page - 1 : null;
+ return this.pageInfo?.hasPreviousPage ? this.value.page - 1 : null;
},
nextPage() {
- return this.pageInfo?.hasNextPage ? this.value?.page + 1 : null;
+ return this.pageInfo?.hasNextPage ? this.value.page + 1 : null;
},
},
methods: {
diff --git a/app/assets/javascripts/runner/components/runner_projects.vue b/app/assets/javascripts/runner/components/runner_projects.vue
index d080d34fdd3..daca718e2b5 100644
--- a/app/assets/javascripts/runner/components/runner_projects.vue
+++ b/app/assets/javascripts/runner/components/runner_projects.vue
@@ -2,7 +2,7 @@
import { GlDeprecatedSkeletonLoading as GlSkeletonLoading } from '@gitlab/ui';
import { sprintf, formatNumber } from '~/locale';
import { createAlert } from '~/flash';
-import runnerProjectsQuery from '../graphql/details/runner_projects.query.graphql';
+import runnerProjectsQuery from '../graphql/show/runner_projects.query.graphql';
import {
I18N_ASSIGNED_PROJECTS,
I18N_NONE,
diff --git a/app/assets/javascripts/runner/components/runner_update_form.vue b/app/assets/javascripts/runner/components/runner_update_form.vue
index 119e5236f85..56c9007a781 100644
--- a/app/assets/javascripts/runner/components/runner_update_form.vue
+++ b/app/assets/javascripts/runner/components/runner_update_form.vue
@@ -1,10 +1,12 @@
<script>
import {
GlButton,
+ GlIcon,
GlForm,
GlFormCheckbox,
GlFormGroup,
GlFormInputGroup,
+ GlSkeletonLoader,
GlTooltipDirective,
} from '@gitlab/ui';
import {
@@ -12,19 +14,23 @@ import {
runnerToModel,
} from 'ee_else_ce/runner/runner_update_form_utils';
import { createAlert, VARIANT_SUCCESS } from '~/flash';
+import { redirectTo } from '~/lib/utils/url_utility';
import { __ } from '~/locale';
import { captureException } from '~/runner/sentry_utils';
import { ACCESS_LEVEL_NOT_PROTECTED, ACCESS_LEVEL_REF_PROTECTED, PROJECT_TYPE } from '../constants';
-import runnerUpdateMutation from '../graphql/details/runner_update.mutation.graphql';
+import runnerUpdateMutation from '../graphql/edit/runner_update.mutation.graphql';
+import { saveAlertToLocalStorage } from '../local_storage_alert/save_alert_to_local_storage';
export default {
name: 'RunnerUpdateForm',
components: {
GlButton,
+ GlIcon,
GlForm,
GlFormCheckbox,
GlFormGroup,
GlFormInputGroup,
+ GlSkeletonLoader,
RunnerUpdateCostFactorFields: () =>
import('ee_component/runner/components/runner_update_cost_factor_fields.vue'),
},
@@ -37,6 +43,16 @@ export default {
required: false,
default: null,
},
+ loading: {
+ type: Boolean,
+ required: false,
+ default: false,
+ },
+ runnerPath: {
+ type: String,
+ required: false,
+ default: null,
+ },
},
data() {
return {
@@ -48,9 +64,6 @@ export default {
canBeLockedToProject() {
return this.runner?.runnerType === PROJECT_TYPE;
},
- readonlyIpAddress() {
- return this.runner?.ipAddress;
- },
},
watch: {
runner(newVal, oldVal) {
@@ -74,24 +87,23 @@ export default {
});
if (errors?.length) {
- // Validation errors need not be thrown
- createAlert({ message: errors[0] });
- return;
+ this.onError(errors[0]);
+ } else {
+ this.onSuccess();
}
-
- this.onSuccess();
} catch (error) {
const { message } = error;
-
- createAlert({ message });
+ this.onError(message);
captureException({ error, component: this.$options.name });
- } finally {
- this.saving = false;
}
},
onSuccess() {
- createAlert({ message: __('Changes saved.'), variant: VARIANT_SUCCESS });
- this.model = runnerToModel(this.runner);
+ saveAlertToLocalStorage({ message: __('Changes saved.'), variant: VARIANT_SUCCESS });
+ redirectTo(this.runnerPath);
+ },
+ onError(message) {
+ this.saving = false;
+ createAlert({ message });
},
},
ACCESS_LEVEL_NOT_PROTECTED,
@@ -100,104 +112,108 @@ export default {
</script>
<template>
<gl-form @submit.prevent="onSubmit">
- <gl-form-checkbox
- v-model="model.active"
- data-testid="runner-field-paused"
- :value="false"
- :unchecked-value="true"
- >
- {{ __('Paused') }}
- <template #help>
- {{ s__('Runners|Stop the runner from accepting new jobs.') }}
- </template>
- </gl-form-checkbox>
+ <h4 class="gl-font-lg gl-my-5">{{ s__('Runners|Details') }}</h4>
- <gl-form-checkbox
- v-model="model.accessLevel"
- data-testid="runner-field-protected"
- :value="$options.ACCESS_LEVEL_REF_PROTECTED"
- :unchecked-value="$options.ACCESS_LEVEL_NOT_PROTECTED"
- >
- {{ __('Protected') }}
- <template #help>
- {{ s__('Runners|Use the runner on pipelines for protected branches only.') }}
- </template>
- </gl-form-checkbox>
+ <gl-skeleton-loader v-if="loading" />
+ <gl-form-group v-else :label="__('Description')" data-testid="runner-field-description">
+ <gl-form-input-group v-model="model.description" />
+ </gl-form-group>
- <gl-form-checkbox v-model="model.runUntagged" data-testid="runner-field-run-untagged">
- {{ __('Run untagged jobs') }}
- <template #help>
- {{ s__('Runners|Use the runner for jobs without tags, in addition to tagged jobs.') }}
- </template>
- </gl-form-checkbox>
+ <hr />
- <gl-form-checkbox
- v-if="canBeLockedToProject"
- v-model="model.locked"
- data-testid="runner-field-locked"
- >
- {{ __('Lock to current projects') }}
- <template #help>
- {{
- s__(
- 'Runners|Use the runner for the currently assigned projects only. Only administrators can change the assigned projects.',
- )
- }}
- </template>
- </gl-form-checkbox>
+ <h4 class="gl-font-lg gl-my-5">{{ s__('Runners|Configuration') }}</h4>
- <gl-form-group :label="__('IP Address')" data-testid="runner-field-ip-address">
- <gl-form-input-group :value="readonlyIpAddress" readonly select-on-click>
- <template #append>
- <gl-button
- v-gl-tooltip.hover
- :title="__('Copy IP Address')"
- :aria-label="__('Copy IP Address')"
- :data-clipboard-text="readonlyIpAddress"
- icon="copy-to-clipboard"
- class="d-inline-flex"
- />
- </template>
- </gl-form-input-group>
- </gl-form-group>
+ <template v-if="loading">
+ <gl-skeleton-loader v-for="i in 3" :key="i" />
+ </template>
+ <template v-else>
+ <div class="gl-mb-5">
+ <gl-form-checkbox
+ v-model="model.active"
+ data-testid="runner-field-paused"
+ :value="false"
+ :unchecked-value="true"
+ >
+ {{ __('Paused') }}
+ <template #help>
+ {{ s__('Runners|Stop the runner from accepting new jobs.') }}
+ </template>
+ </gl-form-checkbox>
- <gl-form-group :label="__('Description')" data-testid="runner-field-description">
- <gl-form-input-group v-model="model.description" />
- </gl-form-group>
+ <gl-form-checkbox
+ v-model="model.accessLevel"
+ data-testid="runner-field-protected"
+ :value="$options.ACCESS_LEVEL_REF_PROTECTED"
+ :unchecked-value="$options.ACCESS_LEVEL_NOT_PROTECTED"
+ >
+ {{ __('Protected') }}
+ <template #help>
+ {{ s__('Runners|Use the runner on pipelines for protected branches only.') }}
+ </template>
+ </gl-form-checkbox>
- <gl-form-group
- data-testid="runner-field-max-timeout"
- :label="__('Maximum job timeout')"
- :description="
- s__(
- 'Runners|Enter the number of seconds. This timeout takes precedence over lower timeouts set for the project.',
- )
- "
- >
- <gl-form-input-group v-model.number="model.maximumTimeout" type="number" />
- </gl-form-group>
+ <gl-form-checkbox v-model="model.runUntagged" data-testid="runner-field-run-untagged">
+ {{ __('Run untagged jobs') }}
+ <template #help>
+ {{ s__('Runners|Use the runner for jobs without tags, in addition to tagged jobs.') }}
+ </template>
+ </gl-form-checkbox>
- <gl-form-group
- data-testid="runner-field-tags"
- :label="__('Tags')"
- :description="
- __('You can set up jobs to only use runners with specific tags. Separate tags with commas.')
- "
- >
- <gl-form-input-group v-model="model.tagList" />
- </gl-form-group>
+ <gl-form-checkbox
+ v-if="canBeLockedToProject"
+ v-model="model.locked"
+ data-testid="runner-field-locked"
+ >
+ {{ __('Lock to current projects') }} <gl-icon name="lock" />
+ <template #help>
+ {{
+ s__(
+ 'Runners|Use the runner for the currently assigned projects only. Only administrators can change the assigned projects.',
+ )
+ }}
+ </template>
+ </gl-form-checkbox>
+ </div>
- <runner-update-cost-factor-fields v-model="model" />
+ <gl-form-group
+ data-testid="runner-field-max-timeout"
+ :label="__('Maximum job timeout')"
+ :description="
+ s__(
+ 'Runners|Enter the number of seconds. This timeout takes precedence over lower timeouts set for the project.',
+ )
+ "
+ >
+ <gl-form-input-group v-model.number="model.maximumTimeout" type="number" />
+ </gl-form-group>
+
+ <gl-form-group
+ data-testid="runner-field-tags"
+ :label="__('Tags')"
+ :description="
+ __(
+ 'You can set up jobs to only use runners with specific tags. Separate tags with commas.',
+ )
+ "
+ >
+ <gl-form-input-group v-model="model.tagList" />
+ </gl-form-group>
+
+ <runner-update-cost-factor-fields v-model="model" />
+ </template>
- <div class="form-actions">
+ <div class="gl-mt-6">
<gl-button
type="submit"
variant="confirm"
class="js-no-auto-disable"
- :loading="saving || !runner"
+ :loading="loading || saving"
>
{{ __('Save changes') }}
</gl-button>
+ <gl-button :href="runnerPath">
+ {{ __('Cancel') }}
+ </gl-button>
</div>
</gl-form>
</template>
diff --git a/app/assets/javascripts/runner/graphql/details/runner.query.graphql b/app/assets/javascripts/runner/graphql/details/runner.query.graphql
deleted file mode 100644
index 4792a186160..00000000000
--- a/app/assets/javascripts/runner/graphql/details/runner.query.graphql
+++ /dev/null
@@ -1,9 +0,0 @@
-#import "ee_else_ce/runner/graphql/details/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/details/runner_details.fragment.graphql b/app/assets/javascripts/runner/graphql/details/runner_details.fragment.graphql
deleted file mode 100644
index 2449ee0fc0f..00000000000
--- a/app/assets/javascripts/runner/graphql/details/runner_details.fragment.graphql
+++ /dev/null
@@ -1,5 +0,0 @@
-#import "./runner_details_shared.fragment.graphql"
-
-fragment RunnerDetails on CiRunner {
- ...RunnerDetailsShared
-}
diff --git a/app/assets/javascripts/runner/graphql/details/runner_details_shared.fragment.graphql b/app/assets/javascripts/runner/graphql/details/runner_details_shared.fragment.graphql
deleted file mode 100644
index d8c67728fac..00000000000
--- a/app/assets/javascripts/runner/graphql/details/runner_details_shared.fragment.graphql
+++ /dev/null
@@ -1,35 +0,0 @@
-fragment RunnerDetailsShared on CiRunner {
- __typename
- id
- runnerType
- active
- accessLevel
- runUntagged
- locked
- ipAddress
- description
- maximumTimeout
- jobCount
- tagList
- createdAt
- status(legacyMode: null)
- contactedAt
- version
- editAdminUrl
- userPermissions {
- updateRunner
- deleteRunner
- }
- groups {
- # Only a single group can be loaded here, while projects
- # are loaded separately using the query with pagination
- # parameters `runner_projects.query.graphql`.
- nodes {
- id
- avatarUrl
- name
- fullName
- webUrl
- }
- }
-}
diff --git a/app/assets/javascripts/runner/graphql/details/runner_update.mutation.graphql b/app/assets/javascripts/runner/graphql/details/runner_update.mutation.graphql
deleted file mode 100644
index e4bf51e2c30..00000000000
--- a/app/assets/javascripts/runner/graphql/details/runner_update.mutation.graphql
+++ /dev/null
@@ -1,15 +0,0 @@
-#import "ee_else_ce/runner/graphql/details/runner_details.fragment.graphql"
-
-# Mutation for updates from the runner form, loads
-# attributes shown in the runner details.
-
-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
- }
- errors
- }
-}
diff --git a/app/assets/javascripts/runner/graphql/edit/runner_fields.fragment.graphql b/app/assets/javascripts/runner/graphql/edit/runner_fields.fragment.graphql
new file mode 100644
index 00000000000..b732d587d70
--- /dev/null
+++ b/app/assets/javascripts/runner/graphql/edit/runner_fields.fragment.graphql
@@ -0,0 +1,5 @@
+#import "./runner_fields_shared.fragment.graphql"
+
+fragment RunnerFields on CiRunner {
+ ...RunnerFieldsShared
+}
diff --git a/app/assets/javascripts/runner/graphql/edit/runner_fields_shared.fragment.graphql b/app/assets/javascripts/runner/graphql/edit/runner_fields_shared.fragment.graphql
new file mode 100644
index 00000000000..f900a0450e5
--- /dev/null
+++ b/app/assets/javascripts/runner/graphql/edit/runner_fields_shared.fragment.graphql
@@ -0,0 +1,15 @@
+fragment RunnerFieldsShared on CiRunner {
+ __typename
+ id
+ shortSha
+ runnerType
+ active
+ accessLevel
+ runUntagged
+ locked
+ description
+ maximumTimeout
+ tagList
+ createdAt
+ status(legacyMode: null)
+}
diff --git a/app/assets/javascripts/runner/graphql/edit/runner_form.query.graphql b/app/assets/javascripts/runner/graphql/edit/runner_form.query.graphql
new file mode 100644
index 00000000000..0bf66c223fc
--- /dev/null
+++ b/app/assets/javascripts/runner/graphql/edit/runner_form.query.graphql
@@ -0,0 +1,7 @@
+#import "ee_else_ce/runner/graphql/edit/runner_fields.fragment.graphql"
+
+query getRunnerForm($id: CiRunnerID!) {
+ runner(id: $id) {
+ ...RunnerFields
+ }
+}
diff --git a/app/assets/javascripts/runner/graphql/edit/runner_update.mutation.graphql b/app/assets/javascripts/runner/graphql/edit/runner_update.mutation.graphql
new file mode 100644
index 00000000000..8694a51b5a4
--- /dev/null
+++ b/app/assets/javascripts/runner/graphql/edit/runner_update.mutation.graphql
@@ -0,0 +1,13 @@
+#import "ee_else_ce/runner/graphql/edit/runner_fields.fragment.graphql"
+
+# Mutation for updates from the runner form, loads
+# attributes shown in the runner details.
+
+mutation runnerUpdate($input: RunnerUpdateInput!) {
+ runnerUpdate(input: $input) {
+ runner {
+ ...RunnerFields
+ }
+ errors
+ }
+}
diff --git a/app/assets/javascripts/runner/graphql/show/runner.query.graphql b/app/assets/javascripts/runner/graphql/show/runner.query.graphql
new file mode 100644
index 00000000000..178816b58bd
--- /dev/null
+++ b/app/assets/javascripts/runner/graphql/show/runner.query.graphql
@@ -0,0 +1,41 @@
+query getRunner($id: CiRunnerID!) {
+ runner(id: $id) {
+ __typename
+ id
+ shortSha
+ runnerType
+ active
+ accessLevel
+ runUntagged
+ locked
+ ipAddress
+ executorName
+ architectureName
+ platformName
+ description
+ maximumTimeout
+ jobCount
+ tagList
+ createdAt
+ status(legacyMode: null)
+ contactedAt
+ version
+ editAdminUrl
+ userPermissions {
+ updateRunner
+ deleteRunner
+ }
+ groups {
+ # Only a single group can be loaded here, while projects
+ # are loaded separately using the query with pagination
+ # parameters `runner_projects.query.graphql`.
+ nodes {
+ id
+ avatarUrl
+ name
+ fullName
+ webUrl
+ }
+ }
+ }
+}
diff --git a/app/assets/javascripts/runner/graphql/details/runner_jobs.query.graphql b/app/assets/javascripts/runner/graphql/show/runner_jobs.query.graphql
index 14585e62bf2..14585e62bf2 100644
--- a/app/assets/javascripts/runner/graphql/details/runner_jobs.query.graphql
+++ b/app/assets/javascripts/runner/graphql/show/runner_jobs.query.graphql
diff --git a/app/assets/javascripts/runner/graphql/details/runner_projects.query.graphql b/app/assets/javascripts/runner/graphql/show/runner_projects.query.graphql
index cb27de7c200..cb27de7c200 100644
--- a/app/assets/javascripts/runner/graphql/details/runner_projects.query.graphql
+++ b/app/assets/javascripts/runner/graphql/show/runner_projects.query.graphql
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 b299d7c40fe..b5bd4b111fd 100644
--- a/app/assets/javascripts/runner/group_runners/group_runners_app.vue
+++ b/app/assets/javascripts/runner/group_runners/group_runners_app.vue
@@ -34,7 +34,7 @@ import {
} from '../runner_search_utils';
import { captureException } from '../sentry_utils';
-const runnersCountSmartQuery = {
+const countSmartQuery = () => ({
query: groupRunnersCountQuery,
fetchPolicy: fetchPolicies.NETWORK_ONLY,
update(data) {
@@ -43,6 +43,39 @@ const runnersCountSmartQuery = {
error(error) {
this.reportToSentry(error);
},
+});
+
+const tabCountSmartQuery = ({ type }) => {
+ return {
+ ...countSmartQuery(),
+ variables() {
+ return {
+ ...this.countVariables,
+ type,
+ };
+ },
+ };
+};
+
+const statusCountSmartQuery = ({ status, name }) => {
+ return {
+ ...countSmartQuery(),
+ skip() {
+ // skip if filtering by status and not using _this_ status as filter
+ if (this.countVariables.status && this.countVariables.status !== status) {
+ // reset count for given status
+ this[name] = null;
+ return true;
+ }
+ return false;
+ },
+ variables() {
+ return {
+ ...this.countVariables,
+ status,
+ };
+ },
+ };
};
export default {
@@ -116,59 +149,27 @@ export default {
this.reportToSentry(error);
},
},
- onlineRunnersTotal: {
- ...runnersCountSmartQuery,
- variables() {
- return {
- groupFullPath: this.groupFullPath,
- status: STATUS_ONLINE,
- };
- },
- },
- offlineRunnersTotal: {
- ...runnersCountSmartQuery,
- variables() {
- return {
- groupFullPath: this.groupFullPath,
- status: STATUS_OFFLINE,
- };
- },
- },
- staleRunnersTotal: {
- ...runnersCountSmartQuery,
- variables() {
- return {
- groupFullPath: this.groupFullPath,
- status: STATUS_STALE,
- };
- },
- },
+
+ // Tabs counts
allRunnersCount: {
- ...runnersCountSmartQuery,
- variables() {
- return {
- ...this.countVariables,
- type: null,
- };
- },
+ ...tabCountSmartQuery({ type: null }),
},
groupRunnersCount: {
- ...runnersCountSmartQuery,
- variables() {
- return {
- ...this.countVariables,
- type: GROUP_TYPE,
- };
- },
+ ...tabCountSmartQuery({ type: GROUP_TYPE }),
},
projectRunnersCount: {
- ...runnersCountSmartQuery,
- variables() {
- return {
- ...this.countVariables,
- type: PROJECT_TYPE,
- };
- },
+ ...tabCountSmartQuery({ type: PROJECT_TYPE }),
+ },
+
+ // Runner status summary
+ onlineRunnersTotal: {
+ ...statusCountSmartQuery({ status: STATUS_ONLINE, name: 'onlineRunnersTotal' }),
+ },
+ offlineRunnersTotal: {
+ ...statusCountSmartQuery({ status: STATUS_OFFLINE, name: 'offlineRunnersTotal' }),
+ },
+ staleRunnersTotal: {
+ ...statusCountSmartQuery({ status: STATUS_STALE, name: 'staleRunnersTotal' }),
},
},
computed: {
@@ -263,12 +264,6 @@ export default {
<template>
<div>
- <runner-stats
- :online-runners-count="onlineRunnersTotal"
- :offline-runners-count="offlineRunnersTotal"
- :stale-runners-count="staleRunnersTotal"
- />
-
<div class="gl-display-flex gl-align-items-center">
<runner-type-tabs
v-model="search"
@@ -298,6 +293,12 @@ export default {
:namespace="filteredSearchNamespace"
/>
+ <runner-stats
+ :online-runners-count="onlineRunnersTotal"
+ :offline-runners-count="offlineRunnersTotal"
+ :stale-runners-count="staleRunnersTotal"
+ />
+
<div v-if="noRunnersFound" class="gl-text-center gl-p-5">
{{ __('No runners found') }}
</div>
diff --git a/app/assets/javascripts/runner/local_storage_alert/constants.js b/app/assets/javascripts/runner/local_storage_alert/constants.js
new file mode 100644
index 00000000000..69b7418f898
--- /dev/null
+++ b/app/assets/javascripts/runner/local_storage_alert/constants.js
@@ -0,0 +1 @@
+export const LOCAL_STORAGE_ALERT_KEY = 'local-storage-alert';
diff --git a/app/assets/javascripts/runner/local_storage_alert/save_alert_to_local_storage.js b/app/assets/javascripts/runner/local_storage_alert/save_alert_to_local_storage.js
new file mode 100644
index 00000000000..ca7c627459a
--- /dev/null
+++ b/app/assets/javascripts/runner/local_storage_alert/save_alert_to_local_storage.js
@@ -0,0 +1,8 @@
+import AccessorUtilities from '~/lib/utils/accessor';
+import { LOCAL_STORAGE_ALERT_KEY } from './constants';
+
+export const saveAlertToLocalStorage = (alertOptions) => {
+ if (AccessorUtilities.canUseLocalStorage()) {
+ localStorage.setItem(LOCAL_STORAGE_ALERT_KEY, JSON.stringify(alertOptions));
+ }
+};
diff --git a/app/assets/javascripts/runner/local_storage_alert/show_alert_from_local_storage.js b/app/assets/javascripts/runner/local_storage_alert/show_alert_from_local_storage.js
new file mode 100644
index 00000000000..d768a06494a
--- /dev/null
+++ b/app/assets/javascripts/runner/local_storage_alert/show_alert_from_local_storage.js
@@ -0,0 +1,18 @@
+import AccessorUtilities from '~/lib/utils/accessor';
+import { LOCAL_STORAGE_ALERT_KEY } from './constants';
+
+export const showAlertFromLocalStorage = async () => {
+ if (AccessorUtilities.canUseLocalStorage()) {
+ const alertOptions = localStorage.getItem(LOCAL_STORAGE_ALERT_KEY);
+
+ if (alertOptions) {
+ try {
+ const { createAlert } = await import('~/flash');
+ createAlert(JSON.parse(alertOptions));
+ } catch {
+ // ignore when the alert data cannot be parsed
+ }
+ }
+ localStorage.removeItem(LOCAL_STORAGE_ALERT_KEY);
+ }
+};
diff --git a/app/assets/javascripts/runner/runner_search_utils.js b/app/assets/javascripts/runner/runner_search_utils.js
index 5e3c412ddb6..0d688ed65ef 100644
--- a/app/assets/javascripts/runner/runner_search_utils.js
+++ b/app/assets/javascripts/runner/runner_search_utils.js
@@ -18,7 +18,6 @@ import {
PARAM_KEY_BEFORE,
DEFAULT_SORT,
RUNNER_PAGE_SIZE,
- STATUS_NEVER_CONTACTED,
} from './constants';
import { getPaginationVariables } from './utils';
@@ -84,7 +83,6 @@ const getPaginationFromParams = (params) => {
};
// Outdated URL parameters
-const STATUS_NOT_CONNECTED = 'NOT_CONNECTED';
const STATUS_ACTIVE = 'ACTIVE';
const STATUS_PAUSED = 'PAUSED';
@@ -116,10 +114,6 @@ export const updateOutdatedUrl = (url = window.location.href) => {
const status = params[PARAM_KEY_STATUS]?.[0] || null;
switch (status) {
- case STATUS_NOT_CONNECTED:
- return updateUrlParams(url, {
- [PARAM_KEY_STATUS]: [STATUS_NEVER_CONTACTED],
- });
case STATUS_ACTIVE:
return updateUrlParams(url, {
[PARAM_KEY_PAUSED]: ['false'],
diff --git a/app/assets/javascripts/runner/utils.js b/app/assets/javascripts/runner/utils.js
index 1f7794720de..cb2917a92fd 100644
--- a/app/assets/javascripts/runner/utils.js
+++ b/app/assets/javascripts/runner/utils.js
@@ -1,5 +1,4 @@
import { formatNumber } from '~/locale';
-import { DEFAULT_TH_CLASSES } from '~/lib/utils/constants';
import { RUNNER_JOB_COUNT_LIMIT } from './constants';
/**
@@ -28,7 +27,7 @@ export const tableField = ({ key, label = '', thClasses = [], ...options }) => {
return {
key,
label,
- thClass: [DEFAULT_TH_CLASSES, ...thClasses],
+ thClass: thClasses,
tdAttr: {
'data-testid': `td-${key}`,
},