diff options
author | GitLab Bot <gitlab-bot@gitlab.com> | 2023-04-15 18:16:18 +0300 |
---|---|---|
committer | GitLab Bot <gitlab-bot@gitlab.com> | 2023-04-15 18:16:18 +0300 |
commit | 5e8d344de5658ace62c367fc19256488416cdc49 (patch) | |
tree | 5a54f9bdb0c0107d43e7d92181a753b908cfd173 /app | |
parent | 792ffb0daf235b6150f696fc1b5ea63fc9845b94 (diff) |
Add latest changes from gitlab-org/gitlab@master
Diffstat (limited to 'app')
17 files changed, 370 insertions, 15 deletions
diff --git a/app/assets/javascripts/ci/runner/admin_new_runner/admin_new_runner_app.vue b/app/assets/javascripts/ci/runner/admin_new_runner/admin_new_runner_app.vue index 79600012838..43d0dae6e78 100644 --- a/app/assets/javascripts/ci/runner/admin_new_runner/admin_new_runner_app.vue +++ b/app/assets/javascripts/ci/runner/admin_new_runner/admin_new_runner_app.vue @@ -6,7 +6,7 @@ import { s__ } from '~/locale'; import RunnerInstructionsModal from '~/vue_shared/components/runner_instructions/runner_instructions_modal.vue'; import RunnerPlatformsRadioGroup from '~/ci/runner/components/runner_platforms_radio_group.vue'; import RunnerCreateForm from '~/ci/runner/components/runner_create_form.vue'; -import { DEFAULT_PLATFORM, PARAM_KEY_PLATFORM } from '../constants'; +import { DEFAULT_PLATFORM, PARAM_KEY_PLATFORM, INSTANCE_TYPE } from '../constants'; import { saveAlertToLocalStorage } from '../local_storage_alert/save_alert_to_local_storage'; export default { @@ -34,21 +34,21 @@ export default { }, methods: { onSaved(runner) { - const registerUrl = setUrlParams( - { [PARAM_KEY_PLATFORM]: this.platform }, - runner.registerAdminUrl, - ); + const params = { [PARAM_KEY_PLATFORM]: this.platform }; + const ephemeralRegisterUrl = setUrlParams(params, runner.ephemeralRegisterUrl); + saveAlertToLocalStorage({ message: s__('Runners|Runner created.'), variant: VARIANT_SUCCESS, }); - redirectTo(registerUrl); + redirectTo(ephemeralRegisterUrl); }, onError(error) { createAlert({ message: error.message }); }, }, modalId: 'runners-legacy-registration-instructions-modal', + INSTANCE_TYPE, }; </script> @@ -84,6 +84,6 @@ export default { <hr aria-hidden="true" /> - <runner-create-form @saved="onSaved" @error="onError" /> + <runner-create-form :runner-type="$options.INSTANCE_TYPE" @saved="onSaved" @error="onError" /> </div> </template> diff --git a/app/assets/javascripts/ci/runner/components/runner_create_form.vue b/app/assets/javascripts/ci/runner/components/runner_create_form.vue index 2bad08b4f48..d3e02f5cd6e 100644 --- a/app/assets/javascripts/ci/runner/components/runner_create_form.vue +++ b/app/assets/javascripts/ci/runner/components/runner_create_form.vue @@ -4,7 +4,7 @@ import RunnerFormFields from '~/ci/runner/components/runner_form_fields.vue'; import runnerCreateMutation from '~/ci/runner/graphql/new/runner_create.mutation.graphql'; import { modelToUpdateMutationVariables } from 'ee_else_ce/ci/runner/runner_update_form_utils'; import { captureException } from '../sentry_utils'; -import { DEFAULT_ACCESS_LEVEL, INSTANCE_TYPE } from '../constants'; +import { RUNNER_TYPES, DEFAULT_ACCESS_LEVEL, GROUP_TYPE, INSTANCE_TYPE } from '../constants'; export default { name: 'RunnerCreateForm', @@ -13,11 +13,22 @@ export default { GlButton, RunnerFormFields, }, + props: { + runnerType: { + type: String, + required: true, + validator: (t) => RUNNER_TYPES.includes(t), + }, + groupId: { + type: String, + required: false, + default: null, + }, + }, data() { return { saving: false, runner: { - runnerType: INSTANCE_TYPE, description: '', maintenanceNote: '', paused: false, @@ -28,6 +39,23 @@ export default { }, }; }, + computed: { + mutationInput() { + const { input } = modelToUpdateMutationVariables(this.runner); + + if (this.runnerType === GROUP_TYPE) { + return { + ...input, + runnerType: GROUP_TYPE, + groupId: this.groupId, + }; + } + return { + ...input, + runnerType: INSTANCE_TYPE, + }; + }, + }, methods: { async onSubmit() { this.saving = true; @@ -38,7 +66,9 @@ export default { }, } = await this.$apollo.mutate({ mutation: runnerCreateMutation, - variables: modelToUpdateMutationVariables(this.runner), + variables: { + input: this.mutationInput, + }, }); if (errors?.length) { diff --git a/app/assets/javascripts/ci/runner/constants.js b/app/assets/javascripts/ci/runner/constants.js index 6237dcd0c03..6fcf4b1730a 100644 --- a/app/assets/javascripts/ci/runner/constants.js +++ b/app/assets/javascripts/ci/runner/constants.js @@ -141,6 +141,7 @@ export const PARAM_KEY_PLATFORM = 'platform'; export const INSTANCE_TYPE = 'INSTANCE_TYPE'; export const GROUP_TYPE = 'GROUP_TYPE'; export const PROJECT_TYPE = 'PROJECT_TYPE'; +export const RUNNER_TYPES = [INSTANCE_TYPE, GROUP_TYPE, PROJECT_TYPE]; // CiRunnerStatus diff --git a/app/assets/javascripts/ci/runner/graphql/new/runner_create.mutation.graphql b/app/assets/javascripts/ci/runner/graphql/new/runner_create.mutation.graphql index d14a594e378..07236808dca 100644 --- a/app/assets/javascripts/ci/runner/graphql/new/runner_create.mutation.graphql +++ b/app/assets/javascripts/ci/runner/graphql/new/runner_create.mutation.graphql @@ -2,7 +2,7 @@ mutation runnerCreate($input: RunnerCreateInput!) { runnerCreate(input: $input) { runner { id - registerAdminUrl + ephemeralRegisterUrl } errors } diff --git a/app/assets/javascripts/ci/runner/group_new_runner/group_new_runner_app.vue b/app/assets/javascripts/ci/runner/group_new_runner/group_new_runner_app.vue new file mode 100644 index 00000000000..35c75a917c7 --- /dev/null +++ b/app/assets/javascripts/ci/runner/group_new_runner/group_new_runner_app.vue @@ -0,0 +1,98 @@ +<script> +import { GlSprintf, GlLink, GlModalDirective } from '@gitlab/ui'; +import { createAlert, VARIANT_SUCCESS } from '~/alert'; +import { redirectTo, setUrlParams } from '~/lib/utils/url_utility'; +import { s__ } from '~/locale'; +import RunnerInstructionsModal from '~/vue_shared/components/runner_instructions/runner_instructions_modal.vue'; +import RunnerPlatformsRadioGroup from '~/ci/runner/components/runner_platforms_radio_group.vue'; +import RunnerCreateForm from '~/ci/runner/components/runner_create_form.vue'; +import { DEFAULT_PLATFORM, GROUP_TYPE, PARAM_KEY_PLATFORM } from '../constants'; +import { saveAlertToLocalStorage } from '../local_storage_alert/save_alert_to_local_storage'; + +export default { + name: 'GroupNewRunnerApp', + components: { + GlLink, + GlSprintf, + RunnerInstructionsModal, + RunnerPlatformsRadioGroup, + RunnerCreateForm, + }, + directives: { + GlModal: GlModalDirective, + }, + props: { + groupId: { + type: String, + required: true, + }, + legacyRegistrationToken: { + type: String, + required: true, + }, + }, + data() { + return { + platform: DEFAULT_PLATFORM, + }; + }, + methods: { + onSaved(runner) { + const params = { [PARAM_KEY_PLATFORM]: this.platform }; + const ephemeralRegisterUrl = setUrlParams(params, runner.ephemeralRegisterUrl); + + saveAlertToLocalStorage({ + message: s__('Runners|Runner created.'), + variant: VARIANT_SUCCESS, + }); + redirectTo(ephemeralRegisterUrl); + }, + onError(error) { + createAlert({ message: error.message }); + }, + }, + modalId: 'runners-legacy-registration-instructions-modal', + GROUP_TYPE, +}; +</script> + +<template> + <div> + <h1 class="gl-font-size-h2">{{ s__('Runners|New group runner') }}</h1> + <p> + <gl-sprintf + :message=" + s__( + 'Runners|Create a group runner to generate a command that registers the runner with all its configurations. %{linkStart}Prefer to use a registration token to create a runner?%{linkEnd}', + ) + " + > + <template #link="{ content }"> + <gl-link v-gl-modal="$options.modalId" data-testid="legacy-instructions-link">{{ + content + }}</gl-link> + <runner-instructions-modal + :modal-id="$options.modalId" + :registration-token="legacyRegistrationToken" + /> + </template> + </gl-sprintf> + </p> + + <hr aria-hidden="true" /> + + <h2 class="gl-font-weight-normal gl-font-lg gl-my-5"> + {{ s__('Runners|Platform') }} + </h2> + <runner-platforms-radio-group v-model="platform" /> + + <hr aria-hidden="true" /> + + <runner-create-form + :runner-type="$options.GROUP_TYPE" + :group-id="groupId" + @saved="onSaved" + @error="onError" + /> + </div> +</template> diff --git a/app/assets/javascripts/ci/runner/group_new_runner/index.js b/app/assets/javascripts/ci/runner/group_new_runner/index.js new file mode 100644 index 00000000000..b314c3aa1e7 --- /dev/null +++ b/app/assets/javascripts/ci/runner/group_new_runner/index.js @@ -0,0 +1,33 @@ +import Vue from 'vue'; +import VueApollo from 'vue-apollo'; +import createDefaultClient from '~/lib/graphql'; +import GroupNewRunnerApp from './group_new_runner_app.vue'; + +Vue.use(VueApollo); + +export const initGroupNewRunner = (selector = '#js-group-new-runner') => { + const el = document.querySelector(selector); + + if (!el) { + return null; + } + + const { legacyRegistrationToken, groupId } = el.dataset; + + const apolloProvider = new VueApollo({ + defaultClient: createDefaultClient(), + }); + + return new Vue({ + el, + apolloProvider, + render(h) { + return h(GroupNewRunnerApp, { + props: { + groupId, + legacyRegistrationToken, + }, + }); + }, + }); +}; diff --git a/app/assets/javascripts/ci/runner/group_register_runner/group_register_runner_app.vue b/app/assets/javascripts/ci/runner/group_register_runner/group_register_runner_app.vue new file mode 100644 index 00000000000..533d31b70a3 --- /dev/null +++ b/app/assets/javascripts/ci/runner/group_register_runner/group_register_runner_app.vue @@ -0,0 +1,69 @@ +<script> +import { GlButton } from '@gitlab/ui'; +import { getParameterByName, updateHistory, mergeUrlParams } from '~/lib/utils/url_utility'; +import { PARAM_KEY_PLATFORM, DEFAULT_PLATFORM } from '../constants'; +import RegistrationInstructions from '../components/registration/registration_instructions.vue'; +import PlatformsDrawer from '../components/registration/platforms_drawer.vue'; + +export default { + name: 'GroupRegisterRunnerApp', + components: { + GlButton, + RegistrationInstructions, + PlatformsDrawer, + }, + props: { + runnerId: { + type: String, + required: true, + }, + runnersPath: { + type: String, + required: true, + }, + }, + data() { + return { + platform: getParameterByName(PARAM_KEY_PLATFORM) || DEFAULT_PLATFORM, + isDrawerOpen: false, + }; + }, + watch: { + platform(platform) { + updateHistory({ + url: mergeUrlParams({ [PARAM_KEY_PLATFORM]: platform }, window.location.href), + }); + }, + }, + methods: { + onSelectPlatform(platform) { + this.platform = platform; + }, + onToggleDrawer(val = !this.isDrawerOpen) { + this.isDrawerOpen = val; + }, + }, +}; +</script> +<template> + <div> + <registration-instructions + :runner-id="runnerId" + :platform="platform" + @toggleDrawer="onToggleDrawer" + > + <template #runner-list-name>{{ s__('Runners|Group area › Runners') }}</template> + </registration-instructions> + + <platforms-drawer + :platform="platform" + :open="isDrawerOpen" + @selectPlatform="onSelectPlatform" + @close="onToggleDrawer(false)" + /> + + <gl-button :href="runnersPath" variant="confirm">{{ + s__('Runners|Go to runners page') + }}</gl-button> + </div> +</template> diff --git a/app/assets/javascripts/ci/runner/group_register_runner/index.js b/app/assets/javascripts/ci/runner/group_register_runner/index.js new file mode 100644 index 00000000000..a00db8853a2 --- /dev/null +++ b/app/assets/javascripts/ci/runner/group_register_runner/index.js @@ -0,0 +1,36 @@ +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 GroupRegisterRunnerApp from './group_register_runner_app.vue'; + +Vue.use(VueApollo); + +export const initGroupRegisterRunner = (selector = '#js-group-register-runner') => { + showAlertFromLocalStorage(); + + const el = document.querySelector(selector); + + if (!el) { + return null; + } + + const { runnerId, runnersPath } = el.dataset; + + const apolloProvider = new VueApollo({ + defaultClient: createDefaultClient(), + }); + + return new Vue({ + el, + apolloProvider, + render(h) { + return h(GroupRegisterRunnerApp, { + props: { + runnerId, + runnersPath, + }, + }); + }, + }); +}; diff --git a/app/assets/javascripts/ci/runner/group_runners/group_runners_app.vue b/app/assets/javascripts/ci/runner/group_runners/group_runners_app.vue index 294d06a66e7..f8386214698 100644 --- a/app/assets/javascripts/ci/runner/group_runners/group_runners_app.vue +++ b/app/assets/javascripts/ci/runner/group_runners/group_runners_app.vue @@ -1,5 +1,5 @@ <script> -import { GlLink } from '@gitlab/ui'; +import { GlButton, GlLink } from '@gitlab/ui'; import { createAlert } from '~/alert'; import { updateHistory } from '~/lib/utils/url_utility'; import { fetchPolicies } from '~/lib/graphql'; @@ -42,6 +42,7 @@ import { captureException } from '../sentry_utils'; export default { name: 'GroupRunnersApp', components: { + GlButton, GlLink, RegistrationDropdown, RunnerFilteredSearchBar, @@ -58,6 +59,11 @@ export default { mixins: [glFeatureFlagMixin()], inject: ['emptyStateSvgPath', 'emptyStateFilteredSvgPath'], props: { + newRunnerPath: { + type: String, + required: false, + default: null, + }, registrationToken: { type: String, required: false, @@ -150,6 +156,10 @@ export default { isSearchFiltered() { return isSearchFiltered(this.search); }, + shouldShowCreateRunnerWorkflow() { + // create_runner_workflow_for_namespace feature flag + return this.glFeatures.createRunnerWorkflowForNamespace; + }, }, watch: { search: { @@ -219,8 +229,13 @@ export default { nav-class="gl-border-none!" /> + <template v-if="shouldShowCreateRunnerWorkflow"> + <gl-button v-if="newRunnerPath" :href="newRunnerPath" variant="confirm"> + {{ s__('Runners|New group runner') }} + </gl-button> + </template> <registration-dropdown - v-if="registrationToken" + v-else-if="registrationToken" class="gl-ml-auto" :registration-token="registrationToken" :type="$options.GROUP_TYPE" diff --git a/app/assets/javascripts/ci/runner/group_runners/index.js b/app/assets/javascripts/ci/runner/group_runners/index.js index 46514d5afe8..4fcf484317d 100644 --- a/app/assets/javascripts/ci/runner/group_runners/index.js +++ b/app/assets/javascripts/ci/runner/group_runners/index.js @@ -18,6 +18,7 @@ export const initGroupRunners = (selector = '#js-group-runners') => { const { registrationToken, runnerInstallHelpPage, + newRunnerPath, groupId, groupFullPath, onlineContactTimeoutSecs, @@ -49,6 +50,7 @@ export const initGroupRunners = (selector = '#js-group-runners') => { props: { registrationToken, groupFullPath, + newRunnerPath, }, }); }, diff --git a/app/assets/javascripts/pages/groups/runners/new/index.js b/app/assets/javascripts/pages/groups/runners/new/index.js new file mode 100644 index 00000000000..318643d95a4 --- /dev/null +++ b/app/assets/javascripts/pages/groups/runners/new/index.js @@ -0,0 +1,3 @@ +import { initGroupNewRunner } from '~/ci/runner/group_new_runner'; + +initGroupNewRunner(); diff --git a/app/assets/javascripts/pages/groups/runners/register/index.js b/app/assets/javascripts/pages/groups/runners/register/index.js new file mode 100644 index 00000000000..b02e33e21f2 --- /dev/null +++ b/app/assets/javascripts/pages/groups/runners/register/index.js @@ -0,0 +1,3 @@ +import { initGroupRegisterRunner } from '~/ci/runner/group_register_runner'; + +initGroupRegisterRunner(); diff --git a/app/controllers/groups/runners_controller.rb b/app/controllers/groups/runners_controller.rb index 859bb0adb4e..f267c1cb857 100644 --- a/app/controllers/groups/runners_controller.rb +++ b/app/controllers/groups/runners_controller.rb @@ -2,14 +2,20 @@ class Groups::RunnersController < Groups::ApplicationController before_action :authorize_read_group_runners!, only: [:index, :show] + before_action :authorize_create_group_runners!, only: [:new, :register] before_action :authorize_update_runner!, only: [:edit, :update, :destroy, :pause, :resume] - before_action :runner, only: [:edit, :update, :destroy, :pause, :resume, :show] + before_action :runner, only: [:edit, :update, :destroy, :pause, :resume, :show, :register] + + before_action only: [:index] do + push_frontend_feature_flag(:create_runner_workflow_for_namespace, group) + end feature_category :runner urgency :low def index @group_runner_registration_token = @group.runners_token if can?(current_user, :register_group_runners, group) + @group_new_runner_path = new_group_runner_path(@group) if can?(current_user, :create_runner, group) Gitlab::Tracking.event(self.class.name, 'index', user: current_user, namespace: @group) end @@ -28,6 +34,16 @@ class Groups::RunnersController < Groups::ApplicationController end end + def new + render_404 unless create_runner_workflow_for_namespace_enabled? + + @group_runner_registration_token = @group.runners_token + end + + def register + render_404 unless create_runner_workflow_for_namespace_enabled? && runner.registration_available? + end + private def runner @@ -47,6 +63,16 @@ class Groups::RunnersController < Groups::ApplicationController render_404 end + + def authorize_create_group_runners! + return if can?(current_user, :create_runner, group) + + render_404 + end + + def create_runner_workflow_for_namespace_enabled? + Feature.enabled?(:create_runner_workflow_for_namespace, group) + end end Groups::RunnersController.prepend_mod diff --git a/app/graphql/types/ci/runner_type.rb b/app/graphql/types/ci/runner_type.rb index 8b0a969f7f5..20e8b506a3f 100644 --- a/app/graphql/types/ci/runner_type.rb +++ b/app/graphql/types/ci/runner_type.rb @@ -42,6 +42,9 @@ module Types description: 'Ephemeral authentication token used for runner manager registration. Only available for the creator of the runner for a limited time during registration.', authorize: :read_ephemeral_token, alpha: { milestone: '15.9' } + field :ephemeral_register_url, GraphQL::Types::String, null: true, + description: 'URL of the registration page of the runner manager. Only available for the creator of the runner for a limited time during registration.', + alpha: { milestone: '15.11' } field :executor_name, GraphQL::Types::String, null: true, description: 'Executor last advertised by the runner.', method: :executor_name @@ -147,6 +150,17 @@ module Types Gitlab::Routing.url_helpers.edit_admin_runner_url(runner) if can_admin_runners? end + def ephemeral_register_url + return unless ephemeral_register_url_access_allowed?(runner) + + case runner.runner_type + when 'instance_type' + Gitlab::Routing.url_helpers.register_admin_runner_url(runner) + when 'group_type' + Gitlab::Routing.url_helpers.register_group_runner_url(runner.groups[0], runner) + end + end + def register_admin_url return unless can_admin_runners? && runner.registration_available? @@ -187,6 +201,19 @@ module Types def can_admin_runners? context[:current_user]&.can_admin_all_resources? end + + def ephemeral_register_url_access_allowed?(runner) + return unless runner.registration_available? + + case runner.runner_type + when 'instance_type' + can_admin_runners? + when 'group_type' + group = runner.groups[0] + + group && context[:current_user]&.can?(:register_group_runners, group) + end + end end end end diff --git a/app/views/groups/runners/index.html.haml b/app/views/groups/runners/index.html.haml index 7e98f6035a6..d619635d3e0 100644 --- a/app/views/groups/runners/index.html.haml +++ b/app/views/groups/runners/index.html.haml @@ -1,3 +1,3 @@ - page_title s_('Runners|Runners') -#js-group-runners{ data: group_runners_data_attributes(@group).merge({ registration_token: @group_runner_registration_token }) } +#js-group-runners{ data: group_runners_data_attributes(@group).merge({ registration_token: @group_runner_registration_token, new_runner_path: @group_new_runner_path }) } diff --git a/app/views/groups/runners/new.html.haml b/app/views/groups/runners/new.html.haml new file mode 100644 index 00000000000..12e7e458a79 --- /dev/null +++ b/app/views/groups/runners/new.html.haml @@ -0,0 +1,5 @@ +- add_to_breadcrumbs _('Runners'), group_runners_path(@group) +- breadcrumb_title s_('Runners|New') +- page_title s_('Runners|Create a group runner') + +#js-group-new-runner{ data: { legacy_registration_token: @group_runner_registration_token, group_id: @group.to_global_id } } diff --git a/app/views/groups/runners/register.html.haml b/app/views/groups/runners/register.html.haml new file mode 100644 index 00000000000..fdee1675475 --- /dev/null +++ b/app/views/groups/runners/register.html.haml @@ -0,0 +1,7 @@ +- runner_name = "##{@runner.id} (#{@runner.short_sha})" +- breadcrumb_title s_('Runners|Register') +- page_title s_('Runners|Register'), "##{@runner.id} (#{@runner.short_sha})" +- add_to_breadcrumbs _('Runners'), group_runners_path(@group) +- add_to_breadcrumbs runner_name, register_group_runner_path(@runner) + +#js-group-register-runner{ data: { runner_id: @runner.id, runners_path: group_runners_path(@group) } } |