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:
-rw-r--r--.gitlab/merge_request_templates/Stable Branch.md18
-rw-r--r--app/assets/javascripts/ci/runner/admin_new_runner/admin_new_runner_app.vue34
-rw-r--r--app/assets/javascripts/ci/runner/admin_register_runner/index.js5
-rw-r--r--app/assets/javascripts/ci/runner/components/runner_create_form.vue71
-rw-r--r--app/assets/javascripts/ci/runner/constants.js2
-rw-r--r--app/assets/javascripts/ci/runner/graphql/new/runner_create.mutation.graphql9
-rw-r--r--app/assets/javascripts/pages/admin/runners/register/index.js3
-rw-r--r--app/assets/javascripts/token_access/components/token_access_app.vue9
-rw-r--r--app/controllers/projects/settings/ci_cd_controller.rb4
-rw-r--r--app/graphql/mutations/ci/project_ci_cd_settings_update.rb2
-rw-r--r--app/models/ci/job_token/scope.rb3
-rw-r--r--app/models/project.rb2
-rw-r--r--app/models/project_ci_cd_setting.rb4
-rw-r--r--app/services/ci/job_token_scope/add_project_service.rb2
-rw-r--r--config/feature_flags/development/ci_inbound_job_token_scope.yml8
-rw-r--r--danger/z_metadata/Dangerfile6
-rw-r--r--db/post_migrate/20230216233937_remove_application_settings_send_user_confirmation_email_column.rb7
-rw-r--r--db/schema_migrations/202302162339371
-rw-r--r--db/structure.sql1
-rw-r--r--doc/user/application_security/api_security/api_discovery/index.md10
-rw-r--r--lib/api/protected_branches.rb6
-rw-r--r--lib/gitlab/import_export/project/object_builder.rb1
-rw-r--r--locale/gitlab.pot3
-rw-r--r--spec/frontend/ci/runner/admin_new_runner_app/admin_new_runner_app_spec.js89
-rw-r--r--spec/frontend/ci/runner/components/runner_create_form_spec.js170
-rw-r--r--spec/frontend/ci/runner/mock_data.js4
-rw-r--r--spec/frontend/fixtures/runner.rb18
-rw-r--r--spec/frontend/token_access/token_access_app_spec.js14
-rw-r--r--spec/lib/gitlab/import_export/project/object_builder_spec.rb7
-rw-r--r--spec/lib/gitlab/import_export/project/tree_restorer_spec.rb1
-rw-r--r--spec/models/ci/job_token/scope_spec.rb8
-rw-r--r--spec/models/project_ci_cd_setting_spec.rb18
-rw-r--r--spec/models/project_spec.rb2
-rw-r--r--spec/requests/api/graphql/mutations/ci/project_ci_cd_settings_update_spec.rb15
-rw-r--r--spec/requests/api/protected_branches_spec.rb9
-rw-r--r--spec/support/shared_examples/models/project_ci_cd_settings_shared_examples.rb4
-rw-r--r--tooling/lib/tooling/kubernetes_client.rb26
-rw-r--r--workhorse/go.mod2
-rw-r--r--workhorse/go.sum4
39 files changed, 445 insertions, 157 deletions
diff --git a/.gitlab/merge_request_templates/Stable Branch.md b/.gitlab/merge_request_templates/Stable Branch.md
new file mode 100644
index 00000000000..e584296cbb1
--- /dev/null
+++ b/.gitlab/merge_request_templates/Stable Branch.md
@@ -0,0 +1,18 @@
+<!--
+Merging into stable branches is reserved for GitLab patch releases
+https://docs.gitlab.com/ee/policy/maintenance.html#patch-releases
+-->
+
+## What does this MR do and why?
+
+_Describe in detail what merge request is being backported and why_
+
+## MR acceptance checklist
+
+This checklist encourages us to confirm any changes have been analyzed to reduce risks in quality, performance, reliability, security, and maintainability.
+
+* [ ] This MR is backporting a bug fix, documentation update, or spec fix, previously merged in the default branch.
+* [ ] The original MR has been deployed to GitLab.com (not applicable for documentation or spec changes).
+* [ ] Ensure the `e2e:package-and-test` job has either succeeded or been approved by a Software Engineer in Test.
+
+/assign me
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 5401c7c1c28..6130b15a3bc 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
@@ -1,9 +1,13 @@
<script>
import { GlSprintf, GlLink, GlModalDirective } from '@gitlab/ui';
+import { createAlert, VARIANT_SUCCESS } from '~/flash';
+import { redirectTo, setUrlParams } from '~/lib/utils/url_utility';
+import { __ } 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 RunnerFormFields from '~/ci/runner/components/runner_form_fields.vue';
-import { DEFAULT_PLATFORM, DEFAULT_ACCESS_LEVEL } from '../constants';
+import RunnerCreateForm from '~/ci/runner/components/runner_create_form.vue';
+import { DEFAULT_PLATFORM, PARAM_KEY_PLATFORM } from '../constants';
+import { saveAlertToLocalStorage } from '../local_storage_alert/save_alert_to_local_storage';
export default {
name: 'AdminNewRunnerApp',
@@ -12,7 +16,7 @@ export default {
GlSprintf,
RunnerInstructionsModal,
RunnerPlatformsRadioGroup,
- RunnerFormFields,
+ RunnerCreateForm,
},
directives: {
GlModal: GlModalDirective,
@@ -26,17 +30,21 @@ export default {
data() {
return {
platform: DEFAULT_PLATFORM,
- runner: {
- description: '',
- maintenanceNote: '',
- paused: false,
- accessLevel: DEFAULT_ACCESS_LEVEL,
- runUntagged: false,
- tagList: '',
- maximumTimeout: ' ',
- },
};
},
+ methods: {
+ onSaved(runner) {
+ const registerUrl = setUrlParams(
+ { [PARAM_KEY_PLATFORM]: this.platform },
+ runner.registerAdminUrl,
+ );
+ saveAlertToLocalStorage({ message: __('Runner created.'), variant: VARIANT_SUCCESS });
+ redirectTo(registerUrl);
+ },
+ onError(error) {
+ createAlert({ message: error.message });
+ },
+ },
modalId: 'runners-legacy-registration-instructions-modal',
};
</script>
@@ -73,6 +81,6 @@ export default {
<hr aria-hidden="true" />
- <runner-form-fields v-model="runner" />
+ <runner-create-form @saved="onSaved" @error="onError" />
</div>
</template>
diff --git a/app/assets/javascripts/ci/runner/admin_register_runner/index.js b/app/assets/javascripts/ci/runner/admin_register_runner/index.js
new file mode 100644
index 00000000000..edb2ec65e98
--- /dev/null
+++ b/app/assets/javascripts/ci/runner/admin_register_runner/index.js
@@ -0,0 +1,5 @@
+import { showAlertFromLocalStorage } from '../local_storage_alert/show_alert_from_local_storage';
+
+export const initAdminRegisterRunner = () => {
+ showAlertFromLocalStorage();
+};
diff --git a/app/assets/javascripts/ci/runner/components/runner_create_form.vue b/app/assets/javascripts/ci/runner/components/runner_create_form.vue
new file mode 100644
index 00000000000..5d2a3c53842
--- /dev/null
+++ b/app/assets/javascripts/ci/runner/components/runner_create_form.vue
@@ -0,0 +1,71 @@
+<script>
+import { GlForm, GlButton } from '@gitlab/ui';
+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 } from '../constants';
+
+export default {
+ name: 'RunnerCreateForm',
+ components: {
+ GlForm,
+ GlButton,
+ RunnerFormFields,
+ },
+ data() {
+ return {
+ saving: false,
+ runner: {
+ description: '',
+ maintenanceNote: '',
+ paused: false,
+ accessLevel: DEFAULT_ACCESS_LEVEL,
+ runUntagged: false,
+ tagList: '',
+ maximumTimeout: '',
+ },
+ };
+ },
+ methods: {
+ async onSubmit() {
+ this.saving = true;
+ try {
+ const {
+ data: {
+ runnerCreate: { errors, runner },
+ },
+ } = await this.$apollo.mutate({
+ mutation: runnerCreateMutation,
+ variables: modelToUpdateMutationVariables(this.runner),
+ });
+
+ if (errors?.length) {
+ this.$emit('error', new Error(errors.join(' ')));
+ } else {
+ this.onSuccess(runner);
+ }
+ } catch (error) {
+ captureException({ error, component: this.$options.name });
+ this.$emit('error', error);
+ } finally {
+ this.saving = false;
+ }
+ },
+ onSuccess(runner) {
+ this.$emit('saved', runner);
+ },
+ },
+};
+</script>
+<template>
+ <gl-form @submit.prevent="onSubmit">
+ <runner-form-fields v-model="runner" />
+
+ <div class="gl-display-flex">
+ <gl-button type="submit" variant="confirm" class="js-no-auto-disable" :loading="saving">
+ {{ __('Submit') }}
+ </gl-button>
+ </div>
+ </gl-form>
+</template>
diff --git a/app/assets/javascripts/ci/runner/constants.js b/app/assets/javascripts/ci/runner/constants.js
index 318eb7e74bd..27c02420036 100644
--- a/app/assets/javascripts/ci/runner/constants.js
+++ b/app/assets/javascripts/ci/runner/constants.js
@@ -129,6 +129,8 @@ export const PARAM_KEY_SORT = 'sort';
export const PARAM_KEY_AFTER = 'after';
export const PARAM_KEY_BEFORE = 'before';
+export const PARAM_KEY_PLATFORM = 'platform';
+
// CiRunnerType
export const INSTANCE_TYPE = 'INSTANCE_TYPE';
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
new file mode 100644
index 00000000000..d14a594e378
--- /dev/null
+++ b/app/assets/javascripts/ci/runner/graphql/new/runner_create.mutation.graphql
@@ -0,0 +1,9 @@
+mutation runnerCreate($input: RunnerCreateInput!) {
+ runnerCreate(input: $input) {
+ runner {
+ id
+ registerAdminUrl
+ }
+ errors
+ }
+}
diff --git a/app/assets/javascripts/pages/admin/runners/register/index.js b/app/assets/javascripts/pages/admin/runners/register/index.js
new file mode 100644
index 00000000000..d7ee2ee369a
--- /dev/null
+++ b/app/assets/javascripts/pages/admin/runners/register/index.js
@@ -0,0 +1,3 @@
+import { initAdminRegisterRunner } from '~/ci/runner/admin_register_runner';
+
+initAdminRegisterRunner();
diff --git a/app/assets/javascripts/token_access/components/token_access_app.vue b/app/assets/javascripts/token_access/components/token_access_app.vue
index 59d59757735..089159ac87b 100644
--- a/app/assets/javascripts/token_access/components/token_access_app.vue
+++ b/app/assets/javascripts/token_access/components/token_access_app.vue
@@ -1,5 +1,4 @@
<script>
-import glFeatureFlagMixin from '~/vue_shared/mixins/gl_feature_flags_mixin';
import OutboundTokenAccess from './outbound_token_access.vue';
import InboundTokenAccess from './inbound_token_access.vue';
import OptInJwt from './opt_in_jwt.vue';
@@ -10,17 +9,11 @@ export default {
InboundTokenAccess,
OptInJwt,
},
- mixins: [glFeatureFlagMixin()],
- computed: {
- inboundTokenAccessEnabled() {
- return this.glFeatures.ciInboundJobTokenScope;
- },
- },
};
</script>
<template>
<div>
- <inbound-token-access v-if="inboundTokenAccessEnabled" class="gl-pb-5" />
+ <inbound-token-access class="gl-pb-5" />
<outbound-token-access class="gl-py-5" />
<opt-in-jwt />
</div>
diff --git a/app/controllers/projects/settings/ci_cd_controller.rb b/app/controllers/projects/settings/ci_cd_controller.rb
index 4ca665679c0..b330aacf3e9 100644
--- a/app/controllers/projects/settings/ci_cd_controller.rb
+++ b/app/controllers/projects/settings/ci_cd_controller.rb
@@ -12,10 +12,6 @@ module Projects
before_action :check_builds_available!
before_action :define_variables
- before_action do
- push_frontend_feature_flag(:ci_inbound_job_token_scope, @project)
- end
-
helper_method :highlight_badge
feature_category :continuous_integration
diff --git a/app/graphql/mutations/ci/project_ci_cd_settings_update.rb b/app/graphql/mutations/ci/project_ci_cd_settings_update.rb
index d214aa46cfc..fcba729d460 100644
--- a/app/graphql/mutations/ci/project_ci_cd_settings_update.rb
+++ b/app/graphql/mutations/ci/project_ci_cd_settings_update.rb
@@ -39,8 +39,6 @@ module Mutations
def resolve(full_path:, **args)
project = authorized_find!(full_path)
- args.delete(:inbound_job_token_scope_enabled) unless Feature.enabled?(:ci_inbound_job_token_scope, project)
-
settings = project.ci_cd_settings
settings.update(args)
diff --git a/app/models/ci/job_token/scope.rb b/app/models/ci/job_token/scope.rb
index 20775077bd8..f389c642fd8 100644
--- a/app/models/ci/job_token/scope.rb
+++ b/app/models/ci/job_token/scope.rb
@@ -58,8 +58,7 @@ module Ci
end
def inbound_accessible?(accessed_project)
- # if the flag or setting is disabled any project is considered to be in scope.
- return true unless Feature.enabled?(:ci_inbound_job_token_scope, accessed_project)
+ # if the setting is disabled any project is considered to be in scope.
return true unless accessed_project.ci_inbound_job_token_scope_enabled?
inbound_linked_as_accessible?(accessed_project)
diff --git a/app/models/project.rb b/app/models/project.rb
index b1bb0ff2bbb..c88ef4fb9db 100644
--- a/app/models/project.rb
+++ b/app/models/project.rb
@@ -2979,7 +2979,7 @@ class Project < ApplicationRecord
end
def ci_inbound_job_token_scope_enabled?
- return false unless ci_cd_settings
+ return true unless ci_cd_settings
ci_cd_settings.inbound_job_token_scope_enabled?
end
diff --git a/app/models/project_ci_cd_setting.rb b/app/models/project_ci_cd_setting.rb
index 8741a341ad3..cc9003423be 100644
--- a/app/models/project_ci_cd_setting.rb
+++ b/app/models/project_ci_cd_setting.rb
@@ -20,10 +20,6 @@ class ProjectCiCdSetting < ApplicationRecord
attribute :forward_deployment_enabled, default: true
attribute :separated_caches, default: true
- default_value_for :inbound_job_token_scope_enabled do |settings|
- Feature.enabled?(:ci_inbound_job_token_scope, settings.project)
- end
-
chronic_duration_attr :runner_token_expiration_interval_human_readable, :runner_token_expiration_interval
def keep_latest_artifacts_available?
diff --git a/app/services/ci/job_token_scope/add_project_service.rb b/app/services/ci/job_token_scope/add_project_service.rb
index 15553ad6e92..4f745042f07 100644
--- a/app/services/ci/job_token_scope/add_project_service.rb
+++ b/app/services/ci/job_token_scope/add_project_service.rb
@@ -6,8 +6,6 @@ module Ci
include EditScopeValidations
def execute(target_project, direction: :outbound)
- direction = :outbound if Feature.disabled?(:ci_inbound_job_token_scope)
-
validate_edit!(project, target_project, current_user)
link = allowlist(direction)
diff --git a/config/feature_flags/development/ci_inbound_job_token_scope.yml b/config/feature_flags/development/ci_inbound_job_token_scope.yml
deleted file mode 100644
index a0e2e09dde5..00000000000
--- a/config/feature_flags/development/ci_inbound_job_token_scope.yml
+++ /dev/null
@@ -1,8 +0,0 @@
----
-name: ci_inbound_job_token_scope
-introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/99165
-rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/376063
-milestone: '15.5'
-type: development
-group: group::pipeline execution
-default_enabled: true
diff --git a/danger/z_metadata/Dangerfile b/danger/z_metadata/Dangerfile
index 9140bb7d988..1c36c02b69e 100644
--- a/danger/z_metadata/Dangerfile
+++ b/danger/z_metadata/Dangerfile
@@ -17,9 +17,3 @@ has_milestone = !gitlab.mr_json["milestone"].nil?
unless has_milestone || (helper.security_mr? && helper.mr_target_branch == default_branch)
warn "This merge request does not refer to an existing milestone.", sticky: false
end
-
-has_pick_into_stable_label = helper.mr_labels.find { |label| label.start_with?('Pick into') }
-
-if helper.mr_target_branch != default_branch && !has_pick_into_stable_label && !helper.security_mr?
- warn "Most of the time, merge requests should target `#{default_branch}`. Otherwise, please set the relevant `Pick into X.Y` label."
-end
diff --git a/db/post_migrate/20230216233937_remove_application_settings_send_user_confirmation_email_column.rb b/db/post_migrate/20230216233937_remove_application_settings_send_user_confirmation_email_column.rb
new file mode 100644
index 00000000000..d7720ebccbd
--- /dev/null
+++ b/db/post_migrate/20230216233937_remove_application_settings_send_user_confirmation_email_column.rb
@@ -0,0 +1,7 @@
+# frozen_string_literal: true
+
+class RemoveApplicationSettingsSendUserConfirmationEmailColumn < Gitlab::Database::Migration[2.1]
+ def change
+ remove_column :application_settings, :send_user_confirmation_email, :boolean, default: false
+ end
+end
diff --git a/db/schema_migrations/20230216233937 b/db/schema_migrations/20230216233937
new file mode 100644
index 00000000000..d3c85c7c981
--- /dev/null
+++ b/db/schema_migrations/20230216233937
@@ -0,0 +1 @@
+5088eccec1327f61cb80c5fca4f7e7710534179c2d6bf820f7021dfd079d51a5 \ No newline at end of file
diff --git a/db/structure.sql b/db/structure.sql
index 96c344f6fd9..bc18ea90ba6 100644
--- a/db/structure.sql
+++ b/db/structure.sql
@@ -11293,7 +11293,6 @@ CREATE TABLE application_settings (
metrics_packet_size integer DEFAULT 1,
disabled_oauth_sign_in_sources text,
health_check_access_token character varying,
- send_user_confirmation_email boolean DEFAULT false,
container_registry_token_expire_delay integer DEFAULT 5,
after_sign_up_text text,
user_default_external boolean DEFAULT false NOT NULL,
diff --git a/doc/user/application_security/api_security/api_discovery/index.md b/doc/user/application_security/api_security/api_discovery/index.md
index 12e67737293..e916bf879cf 100644
--- a/doc/user/application_security/api_security/api_discovery/index.md
+++ b/doc/user/application_security/api_security/api_discovery/index.md
@@ -39,16 +39,6 @@ API Discovery is tested with and officially supports LTS versions of the Java ru
Only applications that are built as Spring Boot [executable JARs](https://docs.spring.io/spring-boot/docs/current/reference/html/executable-jar.html#appendix.executable-jar.nested-jars.jar-structure) are supported.
-### Example configurations
-
-The following are working example projects:
-
-- [API Discovery configured as a pipeline job](https://gitlab.com/gitlab-org/security-products/demos/api-discovery/spring-boot-pipeline)
-<!--
-- [API Discovery integrated into a Maven build process](http://...TODO)
-- [API Discovery integrated into a Gradle build process](http://...TODO)
--->
-
### Configure as pipeline job
The easiest way to run API Discovery is through a pipeline job based on our CI template.
diff --git a/lib/api/protected_branches.rb b/lib/api/protected_branches.rb
index 786045684b8..a50208d78d7 100644
--- a/lib/api/protected_branches.rb
+++ b/lib/api/protected_branches.rb
@@ -109,13 +109,15 @@ module API
failure [
{ code: 422, message: 'Push access levels access level has already been taken' },
{ code: 404, message: '404 Project Not Found' },
- { code: 401, message: '401 Unauthorized' }
+ { code: 401, message: '401 Unauthorized' },
+ { code: 400, message: '400 Bad request' }
]
end
params do
requires :name, type: String, desc: 'The name of the branch', documentation: { example: 'main' }
optional :allow_force_push, type: Boolean,
- desc: 'Allow force push for all users with push access.'
+ desc: 'Allow force push for all users with push access.',
+ allow_blank: false
use :optional_params_ee
end
diff --git a/lib/gitlab/import_export/project/object_builder.rb b/lib/gitlab/import_export/project/object_builder.rb
index 50a67a746f8..0962ad9f028 100644
--- a/lib/gitlab/import_export/project/object_builder.rb
+++ b/lib/gitlab/import_export/project/object_builder.rb
@@ -64,6 +64,7 @@ module Gitlab
if label?
atts['type'] = 'ProjectLabel' # Always create project labels
+ atts.delete('group_id')
elsif milestone?
if atts['group_id'] # Transform new group milestones into project ones
atts['iid'] = nil
diff --git a/locale/gitlab.pot b/locale/gitlab.pot
index 76fbb839683..d67ea731230 100644
--- a/locale/gitlab.pot
+++ b/locale/gitlab.pot
@@ -36883,6 +36883,9 @@ msgstr ""
msgid "Runner API"
msgstr ""
+msgid "Runner created."
+msgstr ""
+
msgid "Runner tokens"
msgstr ""
diff --git a/spec/frontend/ci/runner/admin_new_runner_app/admin_new_runner_app_spec.js b/spec/frontend/ci/runner/admin_new_runner_app/admin_new_runner_app_spec.js
index dd7b0796f6e..1d8ae6ebd3f 100644
--- a/spec/frontend/ci/runner/admin_new_runner_app/admin_new_runner_app_spec.js
+++ b/spec/frontend/ci/runner/admin_new_runner_app/admin_new_runner_app_spec.js
@@ -3,30 +3,42 @@ import VueApollo from 'vue-apollo';
import { GlSprintf } from '@gitlab/ui';
import { shallowMountExtended } from 'helpers/vue_test_utils_helper';
import { createMockDirective, getBinding } from 'helpers/vue_mock_directive';
+import { createAlert, VARIANT_SUCCESS } from '~/flash';
import AdminNewRunnerApp from '~/ci/runner/admin_new_runner/admin_new_runner_app.vue';
+import { saveAlertToLocalStorage } from '~/ci/runner/local_storage_alert/save_alert_to_local_storage';
import RunnerInstructionsModal from '~/vue_shared/components/runner_instructions/runner_instructions_modal.vue';
import RunnerPlatformsRadioGroup from '~/ci/runner/components/runner_platforms_radio_group.vue';
-import RunnerFormFields from '~/ci/runner/components/runner_form_fields.vue';
-import { DEFAULT_PLATFORM } from '~/ci/runner/constants';
+import { PARAM_KEY_PLATFORM, DEFAULT_PLATFORM, WINDOWS_PLATFORM } from '~/ci/runner/constants';
+import RunnerCreateForm from '~/ci/runner/components/runner_create_form.vue';
+import { redirectTo } from '~/lib/utils/url_utility';
+import { runnerCreateResult } from '../mock_data';
const mockLegacyRegistrationToken = 'LEGACY_REGISTRATION_TOKEN';
Vue.use(VueApollo);
+jest.mock('~/ci/runner/local_storage_alert/save_alert_to_local_storage');
+jest.mock('~/flash');
+jest.mock('~/lib/utils/url_utility', () => ({
+ ...jest.requireActual('~/lib/utils/url_utility'),
+ redirectTo: jest.fn(),
+}));
+
+const mockCreatedRunner = runnerCreateResult.data.runnerCreate.runner;
+
describe('AdminNewRunnerApp', () => {
let wrapper;
const findLegacyInstructionsLink = () => wrapper.findByTestId('legacy-instructions-link');
const findRunnerInstructionsModal = () => wrapper.findComponent(RunnerInstructionsModal);
const findRunnerPlatformsRadioGroup = () => wrapper.findComponent(RunnerPlatformsRadioGroup);
- const findRunnerFormFields = () => wrapper.findComponent(RunnerFormFields);
+ const findRunnerCreateForm = () => wrapper.findComponent(RunnerCreateForm);
- const createComponent = ({ props = {}, mountFn = shallowMountExtended, ...options } = {}) => {
- wrapper = mountFn(AdminNewRunnerApp, {
+ const createComponent = () => {
+ wrapper = shallowMountExtended(AdminNewRunnerApp, {
propsData: {
legacyRegistrationToken: mockLegacyRegistrationToken,
- ...props,
},
directives: {
GlModal: createMockDirective('gl-modal'),
@@ -34,7 +46,6 @@ describe('AdminNewRunnerApp', () => {
stubs: {
GlSprintf,
},
- ...options,
});
};
@@ -56,25 +67,59 @@ describe('AdminNewRunnerApp', () => {
});
});
- describe('New runner form fields', () => {
- describe('Platform', () => {
- it('shows the platforms radio group', () => {
- expect(findRunnerPlatformsRadioGroup().props('value')).toBe(DEFAULT_PLATFORM);
- });
+ describe('Platform', () => {
+ it('shows the platforms radio group', () => {
+ expect(findRunnerPlatformsRadioGroup().props('value')).toBe(DEFAULT_PLATFORM);
+ });
+ });
+
+ describe('Runner form', () => {
+ it('shows the runner create form', () => {
+ expect(findRunnerCreateForm().exists()).toBe(true);
});
- describe('Runner', () => {
- it('shows the runners fields', () => {
- expect(findRunnerFormFields().props('value')).toEqual({
- accessLevel: 'NOT_PROTECTED',
- paused: false,
- description: '',
- maintenanceNote: '',
- maximumTimeout: ' ',
- runUntagged: false,
- tagList: '',
+ describe('When a runner is saved', () => {
+ beforeEach(() => {
+ findRunnerCreateForm().vm.$emit('saved', mockCreatedRunner);
+ });
+
+ it('pushes an alert to be shown after redirection', () => {
+ expect(saveAlertToLocalStorage).toHaveBeenCalledWith({
+ message: expect.any(String),
+ variant: VARIANT_SUCCESS,
});
});
+
+ it('redirects to the registration page', () => {
+ const url = `${mockCreatedRunner.registerAdminUrl}?${PARAM_KEY_PLATFORM}=${DEFAULT_PLATFORM}`;
+
+ expect(redirectTo).toHaveBeenCalledWith(url);
+ });
+ });
+
+ describe('When another platform is selected and a runner is saved', () => {
+ beforeEach(() => {
+ findRunnerPlatformsRadioGroup().vm.$emit('input', WINDOWS_PLATFORM);
+ findRunnerCreateForm().vm.$emit('saved', mockCreatedRunner);
+ });
+
+ it('redirects to the registration page with the platform', () => {
+ const url = `${mockCreatedRunner.registerAdminUrl}?${PARAM_KEY_PLATFORM}=${WINDOWS_PLATFORM}`;
+
+ expect(redirectTo).toHaveBeenCalledWith(url);
+ });
+ });
+
+ describe('When runner fails to save', () => {
+ const ERROR_MSG = 'Cannot save!';
+
+ beforeEach(() => {
+ findRunnerCreateForm().vm.$emit('error', new Error(ERROR_MSG));
+ });
+
+ it('shows an error message', () => {
+ expect(createAlert).toHaveBeenCalledWith({ message: ERROR_MSG });
+ });
});
});
});
diff --git a/spec/frontend/ci/runner/components/runner_create_form_spec.js b/spec/frontend/ci/runner/components/runner_create_form_spec.js
new file mode 100644
index 00000000000..1123a026a4d
--- /dev/null
+++ b/spec/frontend/ci/runner/components/runner_create_form_spec.js
@@ -0,0 +1,170 @@
+import Vue from 'vue';
+import { GlForm } from '@gitlab/ui';
+import VueApollo from 'vue-apollo';
+import createMockApollo from 'helpers/mock_apollo_helper';
+import { shallowMountExtended } from 'helpers/vue_test_utils_helper';
+import waitForPromises from 'helpers/wait_for_promises';
+import RunnerCreateForm from '~/ci/runner/components/runner_create_form.vue';
+import RunnerFormFields from '~/ci/runner/components/runner_form_fields.vue';
+import { DEFAULT_ACCESS_LEVEL } from '~/ci/runner/constants';
+import runnerCreateMutation from '~/ci/runner/graphql/new/runner_create.mutation.graphql';
+import { captureException } from '~/ci/runner/sentry_utils';
+import { runnerCreateResult } from '../mock_data';
+
+jest.mock('~/ci/runner/sentry_utils');
+
+const mockCreatedRunner = runnerCreateResult.data.runnerCreate.runner;
+
+const defaultRunnerModel = {
+ description: '',
+ accessLevel: DEFAULT_ACCESS_LEVEL,
+ paused: false,
+ maintenanceNote: '',
+ maximumTimeout: '',
+ runUntagged: false,
+ tagList: '',
+};
+
+Vue.use(VueApollo);
+
+describe('RunnerCreateForm', () => {
+ let wrapper;
+ let runnerCreateHandler;
+
+ const findForm = () => wrapper.findComponent(GlForm);
+ const findRunnerFormFields = () => wrapper.findComponent(RunnerFormFields);
+ const findSubmitBtn = () => wrapper.find('[type="submit"]');
+
+ const createComponent = () => {
+ wrapper = shallowMountExtended(RunnerCreateForm, {
+ apolloProvider: createMockApollo([[runnerCreateMutation, runnerCreateHandler]]),
+ });
+ };
+
+ beforeEach(() => {
+ runnerCreateHandler = jest.fn().mockResolvedValue(runnerCreateResult);
+
+ createComponent();
+ });
+
+ it('shows default runner values', () => {
+ expect(findRunnerFormFields().props('value')).toEqual(defaultRunnerModel);
+ });
+
+ it('shows a submit button', () => {
+ expect(findSubmitBtn().exists()).toBe(true);
+ });
+
+ describe('when user submits', () => {
+ let preventDefault;
+
+ beforeEach(() => {
+ preventDefault = jest.fn();
+
+ findRunnerFormFields().vm.$emit('input', {
+ ...defaultRunnerModel,
+ description: 'My runner',
+ maximumTimeout: 0,
+ tagList: 'tag1, tag2',
+ });
+ });
+
+ describe('immediately after submit', () => {
+ beforeEach(() => {
+ findForm().vm.$emit('submit', { preventDefault });
+ });
+
+ it('prevents default form submission', () => {
+ expect(preventDefault).toHaveBeenCalledTimes(1);
+ });
+
+ it('shows a saving state', () => {
+ expect(findSubmitBtn().props('loading')).toBe(true);
+ });
+
+ it('saves runner', async () => {
+ expect(runnerCreateHandler).toHaveBeenCalledWith({
+ input: {
+ ...defaultRunnerModel,
+ description: 'My runner',
+ maximumTimeout: 0,
+ tagList: ['tag1', 'tag2'],
+ },
+ });
+ });
+ });
+
+ describe('when saved successfully', () => {
+ beforeEach(async () => {
+ findForm().vm.$emit('submit', { preventDefault });
+ await waitForPromises();
+ });
+
+ it('emits "saved" result', async () => {
+ expect(wrapper.emitted('saved')[0]).toEqual([mockCreatedRunner]);
+ });
+
+ it('does not show a saving state', () => {
+ expect(findSubmitBtn().props('loading')).toBe(false);
+ });
+ });
+
+ describe('when a server error occurs', () => {
+ const error = new Error('Error!');
+
+ beforeEach(async () => {
+ runnerCreateHandler.mockRejectedValue(error);
+
+ findForm().vm.$emit('submit', { preventDefault });
+ await waitForPromises();
+ });
+
+ it('emits "error" result', async () => {
+ expect(wrapper.emitted('error')[0]).toEqual([error]);
+ });
+
+ it('does not show a saving state', () => {
+ expect(findSubmitBtn().props('loading')).toBe(false);
+ });
+
+ it('reports error', () => {
+ expect(captureException).toHaveBeenCalledTimes(1);
+ expect(captureException).toHaveBeenCalledWith({
+ component: 'RunnerCreateForm',
+ error,
+ });
+ });
+ });
+
+ describe('when a validation error occurs', () => {
+ const errorMsg1 = 'Issue1!';
+ const errorMsg2 = 'Issue2!';
+
+ beforeEach(async () => {
+ runnerCreateHandler.mockResolvedValue({
+ data: {
+ runnerCreate: {
+ errors: [errorMsg1, errorMsg2],
+ runner: null,
+ },
+ },
+ });
+
+ findForm().vm.$emit('submit', { preventDefault });
+ await waitForPromises();
+ });
+
+ it('emits "error" results', async () => {
+ expect(wrapper.emitted('error')[0]).toEqual([new Error(`${errorMsg1} ${errorMsg2}`)]);
+ });
+
+ it('does not show a saving state', () => {
+ expect(findSubmitBtn().props('loading')).toBe(false);
+ });
+
+ it('does not report error', () => {
+ expect(captureException).not.toHaveBeenCalled();
+ });
+ });
+ });
+});
diff --git a/spec/frontend/ci/runner/mock_data.js b/spec/frontend/ci/runner/mock_data.js
index 5cdf0ea4e3b..e16f4fbd3a5 100644
--- a/spec/frontend/ci/runner/mock_data.js
+++ b/spec/frontend/ci/runner/mock_data.js
@@ -9,6 +9,9 @@ import runnerJobsData from 'test_fixtures/graphql/ci/runner/show/runner_jobs.que
// Edit runner queries
import runnerFormData from 'test_fixtures/graphql/ci/runner/edit/runner_form.query.graphql.json';
+// New runner queries
+import runnerCreateResult from 'test_fixtures/graphql/ci/runner/new/runner_create.mutation.graphql.json';
+
// List queries
import allRunnersData from 'test_fixtures/graphql/ci/runner/list/all_runners.query.graphql.json';
import allRunnersDataPaginated from 'test_fixtures/graphql/ci/runner/list/all_runners.query.graphql.paginated.json';
@@ -321,4 +324,5 @@ export {
runnerProjectsData,
runnerJobsData,
runnerFormData,
+ runnerCreateResult,
};
diff --git a/spec/frontend/fixtures/runner.rb b/spec/frontend/fixtures/runner.rb
index f60e4991292..25e97334504 100644
--- a/spec/frontend/fixtures/runner.rb
+++ b/spec/frontend/fixtures/runner.rb
@@ -145,6 +145,24 @@ RSpec.describe 'Runner (JavaScript fixtures)' do
expect_graphql_errors_to_be_empty
end
end
+
+ describe 'runner_create.mutation.graphql', type: :request do
+ runner_create_mutation = 'new/runner_create.mutation.graphql'
+
+ let_it_be(:query) do
+ get_graphql_query_as_string("#{query_path}#{runner_create_mutation}")
+ end
+
+ it "#{fixtures_path}#{runner_create_mutation}.json" do
+ post_graphql(query, current_user: admin, variables: {
+ input: {
+ description: 'My dummy runner'
+ }
+ })
+
+ expect_graphql_errors_to_be_empty
+ end
+ end
end
describe 'as group owner', GraphQL::Query do
diff --git a/spec/frontend/token_access/token_access_app_spec.js b/spec/frontend/token_access/token_access_app_spec.js
index 7f269ee5fda..cff16fd125c 100644
--- a/spec/frontend/token_access/token_access_app_spec.js
+++ b/spec/frontend/token_access/token_access_app_spec.js
@@ -11,12 +11,8 @@ describe('TokenAccessApp component', () => {
const findInboundTokenAccess = () => wrapper.findComponent(InboundTokenAccess);
const findOptInJwt = () => wrapper.findComponent(OptInJwt);
- const createComponent = (flagState = false) => {
- wrapper = shallowMount(TokenAccessApp, {
- provide: {
- glFeatures: { ciInboundJobTokenScope: flagState },
- },
- });
+ const createComponent = () => {
+ wrapper = shallowMount(TokenAccessApp);
};
describe('default', () => {
@@ -32,12 +28,6 @@ describe('TokenAccessApp component', () => {
expect(findOutboundTokenAccess().exists()).toBe(true);
});
- it('does not render the inbound token access component', () => {
- expect(findInboundTokenAccess().exists()).toBe(false);
- });
- });
-
- describe('with feature flag enabled', () => {
it('renders the inbound token access component', () => {
createComponent(true);
diff --git a/spec/lib/gitlab/import_export/project/object_builder_spec.rb b/spec/lib/gitlab/import_export/project/object_builder_spec.rb
index 189b798c2e8..5fa8590e8fd 100644
--- a/spec/lib/gitlab/import_export/project/object_builder_spec.rb
+++ b/spec/lib/gitlab/import_export/project/object_builder_spec.rb
@@ -86,13 +86,16 @@ RSpec.describe Gitlab::ImportExport::Project::ObjectBuilder do
'group' => group)).to eq(group_label)
end
- it 'creates a new label' do
+ it 'creates a new project label' do
label = described_class.build(Label,
'title' => 'group label',
'project' => project,
- 'group' => project.group)
+ 'group' => project.group,
+ 'group_id' => project.group.id)
expect(label.persisted?).to be true
+ expect(label).to be_an_instance_of(ProjectLabel)
+ expect(label.group_id).to be_nil
end
end
diff --git a/spec/lib/gitlab/import_export/project/tree_restorer_spec.rb b/spec/lib/gitlab/import_export/project/tree_restorer_spec.rb
index bb735d6689e..a07fe4fd29c 100644
--- a/spec/lib/gitlab/import_export/project/tree_restorer_spec.rb
+++ b/spec/lib/gitlab/import_export/project/tree_restorer_spec.rb
@@ -295,6 +295,7 @@ RSpec.describe Gitlab::ImportExport::Project::TreeRestorer, feature_category: :i
it 'has project labels' do
expect(ProjectLabel.count).to eq(3)
+ expect(ProjectLabel.pluck(:group_id).compact).to be_empty
end
it 'has merge request approvals' do
diff --git a/spec/models/ci/job_token/scope_spec.rb b/spec/models/ci/job_token/scope_spec.rb
index 9ae061a3702..51f0f4878e7 100644
--- a/spec/models/ci/job_token/scope_spec.rb
+++ b/spec/models/ci/job_token/scope_spec.rb
@@ -160,13 +160,5 @@ RSpec.describe Ci::JobToken::Scope, feature_category: :continuous_integration, f
include_examples 'enforces outbound scope only'
end
-
- context 'when inbound scope flag disabled' do
- before do
- stub_feature_flags(ci_inbound_job_token_scope: false)
- end
-
- include_examples 'enforces outbound scope only'
- end
end
end
diff --git a/spec/models/project_ci_cd_setting_spec.rb b/spec/models/project_ci_cd_setting_spec.rb
index 2c490c33747..0a818147bfc 100644
--- a/spec/models/project_ci_cd_setting_spec.rb
+++ b/spec/models/project_ci_cd_setting_spec.rb
@@ -27,22 +27,8 @@ RSpec.describe ProjectCiCdSetting do
end
end
- describe '#set_default_for_inbound_job_token_scope_enabled' do
- context 'when feature flag ci_inbound_job_token_scope is enabled' do
- before do
- stub_feature_flags(ci_inbound_job_token_scope: true)
- end
-
- it { is_expected.to be_inbound_job_token_scope_enabled }
- end
-
- context 'when feature flag ci_inbound_job_token_scope is disabled' do
- before do
- stub_feature_flags(ci_inbound_job_token_scope: false)
- end
-
- it { is_expected.not_to be_inbound_job_token_scope_enabled }
- end
+ describe '#default_for_inbound_job_token_scope_enabled' do
+ it { is_expected.to be_inbound_job_token_scope_enabled }
end
describe '#default_git_depth' do
diff --git a/spec/models/project_spec.rb b/spec/models/project_spec.rb
index 9db1dde6294..ab50a3aa480 100644
--- a/spec/models/project_spec.rb
+++ b/spec/models/project_spec.rb
@@ -1162,7 +1162,7 @@ RSpec.describe Project, factory_default: :keep, feature_category: :projects do
end
describe '#ci_inbound_job_token_scope_enabled?' do
- it_behaves_like 'a ci_cd_settings predicate method', prefix: 'ci_' do
+ it_behaves_like 'a ci_cd_settings predicate method', prefix: 'ci_', default: true do
let(:delegated_method) { :inbound_job_token_scope_enabled? }
end
end
diff --git a/spec/requests/api/graphql/mutations/ci/project_ci_cd_settings_update_spec.rb b/spec/requests/api/graphql/mutations/ci/project_ci_cd_settings_update_spec.rb
index 99e55c44773..0951d165d46 100644
--- a/spec/requests/api/graphql/mutations/ci/project_ci_cd_settings_update_spec.rb
+++ b/spec/requests/api/graphql/mutations/ci/project_ci_cd_settings_update_spec.rb
@@ -101,21 +101,6 @@ RSpec.describe 'ProjectCiCdSettingsUpdate', feature_category: :continuous_integr
expect(response).to have_gitlab_http_status(:success)
expect(project.ci_inbound_job_token_scope_enabled).to eq(true)
end
-
- context 'when ci_inbound_job_token_scope disabled' do
- before do
- stub_feature_flags(ci_inbound_job_token_scope: false)
- end
-
- it 'does not update inbound_job_token_scope_enabled' do
- post_graphql_mutation(mutation, current_user: user)
-
- project.reload
-
- expect(response).to have_gitlab_http_status(:success)
- expect(project.ci_inbound_job_token_scope_enabled).to eq(true)
- end
- end
end
it 'updates ci_opt_in_jwt' do
diff --git a/spec/requests/api/protected_branches_spec.rb b/spec/requests/api/protected_branches_spec.rb
index 8e8a25a8dc2..463893afd13 100644
--- a/spec/requests/api/protected_branches_spec.rb
+++ b/spec/requests/api/protected_branches_spec.rb
@@ -266,6 +266,15 @@ RSpec.describe API::ProtectedBranches, feature_category: :source_code_management
end.to change { protected_branch.reload.allow_force_push }.from(false).to(true)
expect(response).to have_gitlab_http_status(:ok)
end
+
+ context 'when allow_force_push is not set' do
+ it 'responds with a bad request error' do
+ patch api(route, user), params: { allow_force_push: nil }
+
+ expect(response).to have_gitlab_http_status(:bad_request)
+ expect(json_response['error']).to eq 'allow_force_push is empty'
+ end
+ end
end
context 'when returned protected branch is invalid' do
diff --git a/spec/support/shared_examples/models/project_ci_cd_settings_shared_examples.rb b/spec/support/shared_examples/models/project_ci_cd_settings_shared_examples.rb
index 3caf58da4d2..f1af1760e8d 100644
--- a/spec/support/shared_examples/models/project_ci_cd_settings_shared_examples.rb
+++ b/spec/support/shared_examples/models/project_ci_cd_settings_shared_examples.rb
@@ -19,7 +19,7 @@ RSpec.shared_examples 'ci_cd_settings delegation' do
end
end
-RSpec.shared_examples 'a ci_cd_settings predicate method' do |prefix: ''|
+RSpec.shared_examples 'a ci_cd_settings predicate method' do |prefix: '', default: false|
using RSpec::Parameterized::TableSyntax
context 'when ci_cd_settings is nil' do
@@ -28,7 +28,7 @@ RSpec.shared_examples 'a ci_cd_settings predicate method' do |prefix: ''|
end
it 'returns false' do
- expect(project.send("#{prefix}#{delegated_method}")).to be(false)
+ expect(project.send("#{prefix}#{delegated_method}")).to be(default)
end
end
diff --git a/tooling/lib/tooling/kubernetes_client.rb b/tooling/lib/tooling/kubernetes_client.rb
index ab914db5777..27eb4c8151e 100644
--- a/tooling/lib/tooling/kubernetes_client.rb
+++ b/tooling/lib/tooling/kubernetes_client.rb
@@ -35,6 +35,19 @@ module Tooling
delete_namespaces_by_exact_names(resource_names: namespaces, wait: wait)
end
+ def delete_namespaces_by_exact_names(resource_names:, wait:)
+ command = [
+ 'delete',
+ 'namespace',
+ '--now',
+ '--ignore-not-found',
+ %(--wait=#{wait}),
+ resource_names.join(' ')
+ ]
+
+ run_command(command)
+ end
+
private
def delete_by_selector(release_name:, wait:)
@@ -74,19 +87,6 @@ module Tooling
run_command(command)
end
- def delete_namespaces_by_exact_names(resource_names:, wait:)
- command = [
- 'delete',
- 'namespace',
- '--now',
- '--ignore-not-found',
- %(--wait=#{wait}),
- resource_names.join(' ')
- ]
-
- run_command(command)
- end
-
def delete_by_matching_name(release_name:)
resource_names = raw_resource_names
command = [
diff --git a/workhorse/go.mod b/workhorse/go.mod
index 87e8a844d5c..5fc0d1c1679 100644
--- a/workhorse/go.mod
+++ b/workhorse/go.mod
@@ -7,7 +7,7 @@ require (
github.com/BurntSushi/toml v1.2.1
github.com/FZambia/sentinel v1.1.1
github.com/alecthomas/chroma/v2 v2.5.0
- github.com/aws/aws-sdk-go v1.44.201
+ github.com/aws/aws-sdk-go v1.44.202
github.com/disintegration/imaging v1.6.2
github.com/getsentry/raven-go v0.2.0
github.com/golang-jwt/jwt/v4 v4.5.0
diff --git a/workhorse/go.sum b/workhorse/go.sum
index e68da6ed585..cb20a361be5 100644
--- a/workhorse/go.sum
+++ b/workhorse/go.sum
@@ -544,8 +544,8 @@ github.com/aws/aws-sdk-go v1.43.11/go.mod h1:y4AeaBuwd2Lk+GepC1E9v0qOiTws0MIWAX4
github.com/aws/aws-sdk-go v1.43.31/go.mod h1:y4AeaBuwd2Lk+GepC1E9v0qOiTws0MIWAX4oIKwKHZo=
github.com/aws/aws-sdk-go v1.44.128/go.mod h1:y4AeaBuwd2Lk+GepC1E9v0qOiTws0MIWAX4oIKwKHZo=
github.com/aws/aws-sdk-go v1.44.151/go.mod h1:aVsgQcEevwlmQ7qHE9I3h+dtQgpqhFB+i8Phjh7fkwI=
-github.com/aws/aws-sdk-go v1.44.201 h1:gKtyFyiVGh/uTW7sCQaoyU6XCUsnI8+WWKmbEaABCfw=
-github.com/aws/aws-sdk-go v1.44.201/go.mod h1:aVsgQcEevwlmQ7qHE9I3h+dtQgpqhFB+i8Phjh7fkwI=
+github.com/aws/aws-sdk-go v1.44.202 h1:nk/DtYoAS7zX4SbfiQEJO+C0GBN8ZxXrkD+BozwLvZk=
+github.com/aws/aws-sdk-go v1.44.202/go.mod h1:aVsgQcEevwlmQ7qHE9I3h+dtQgpqhFB+i8Phjh7fkwI=
github.com/aws/aws-sdk-go-v2 v0.18.0/go.mod h1:JWVYvqSMppoMJC0x5wdwiImzgXTI9FuZwxzkQq9wy+g=
github.com/aws/aws-sdk-go-v2 v1.17.1 h1:02c72fDJr87N8RAC2s3Qu0YuvMRZKNZJ9F+lAehCazk=
github.com/aws/aws-sdk-go-v2 v1.17.1/go.mod h1:JLnGeGONAyi2lWXI1p0PCIOIy333JMVK1U7Hf0aRFLw=