Add latest changes from gitlab-org/gitlab@master
This commit is contained in:
parent
ae567e129f
commit
4136fdda4c
|
@ -3,8 +3,6 @@
|
|||
mutation createHttpIntegration($projectPath: ID!, $name: String!, $active: Boolean!) {
|
||||
httpIntegrationCreate(input: { projectPath: $projectPath, name: $name, active: $active }) {
|
||||
errors
|
||||
# We have ID in a deeply nested fragment
|
||||
# eslint-disable-next-line @graphql-eslint/require-id-when-available
|
||||
integration {
|
||||
...HttpIntegrationItem
|
||||
}
|
||||
|
|
|
@ -3,8 +3,6 @@
|
|||
mutation destroyHttpIntegration($id: ID!) {
|
||||
httpIntegrationDestroy(input: { id: $id }) {
|
||||
errors
|
||||
# We have ID in a deeply nested fragment
|
||||
# eslint-disable-next-line @graphql-eslint/require-id-when-available
|
||||
integration {
|
||||
...HttpIntegrationItem
|
||||
}
|
||||
|
|
|
@ -3,8 +3,6 @@
|
|||
mutation resetHttpIntegrationToken($id: ID!) {
|
||||
httpIntegrationResetToken(input: { id: $id }) {
|
||||
errors
|
||||
# We have ID in a deeply nested fragment
|
||||
# eslint-disable-next-line @graphql-eslint/require-id-when-available
|
||||
integration {
|
||||
...HttpIntegrationItem
|
||||
}
|
||||
|
|
|
@ -3,8 +3,6 @@
|
|||
mutation updateHttpIntegration($id: ID!, $name: String!, $active: Boolean!) {
|
||||
httpIntegrationUpdate(input: { id: $id, name: $name, active: $active }) {
|
||||
errors
|
||||
# We have ID in a deeply nested fragment
|
||||
# eslint-disable-next-line @graphql-eslint/require-id-when-available
|
||||
integration {
|
||||
...HttpIntegrationItem
|
||||
}
|
||||
|
|
|
@ -2,8 +2,6 @@
|
|||
|
||||
mutation createBoardList($boardId: BoardID!, $backlog: Boolean, $labelId: LabelID) {
|
||||
boardListCreate(input: { boardId: $boardId, backlog: $backlog, labelId: $labelId }) {
|
||||
# We have ID in a deeply nested fragment
|
||||
# eslint-disable-next-line @graphql-eslint/require-id-when-available
|
||||
list {
|
||||
...BoardListFragment
|
||||
}
|
||||
|
|
|
@ -2,8 +2,6 @@
|
|||
|
||||
mutation UpdateBoardList($listId: ID!, $position: Int, $collapsed: Boolean) {
|
||||
updateBoardList(input: { listId: $listId, position: $position, collapsed: $collapsed }) {
|
||||
# We have ID in a deeply nested fragment
|
||||
# eslint-disable-next-line @graphql-eslint/require-id-when-available
|
||||
list {
|
||||
...BoardListFragment
|
||||
}
|
||||
|
|
|
@ -13,8 +13,6 @@ query BoardLists(
|
|||
id
|
||||
hideBacklogList
|
||||
lists(issueFilters: $filters) {
|
||||
# We have ID in a deeply nested fragment
|
||||
# eslint-disable-next-line @graphql-eslint/require-id-when-available
|
||||
nodes {
|
||||
...BoardListFragment
|
||||
}
|
||||
|
@ -27,8 +25,6 @@ query BoardLists(
|
|||
id
|
||||
hideBacklogList
|
||||
lists(issueFilters: $filters) {
|
||||
# We have ID in a deeply nested fragment
|
||||
# eslint-disable-next-line @graphql-eslint/require-id-when-available
|
||||
nodes {
|
||||
...BoardListFragment
|
||||
}
|
||||
|
|
|
@ -9,11 +9,11 @@ import {
|
|||
} from '@gitlab/ui';
|
||||
import { BubbleMenu } from '@tiptap/vue-2';
|
||||
import { getParentByTagName } from '~/lib/utils/dom_utils';
|
||||
import codeBlockLanguageLoader from '../services/code_block_language_loader';
|
||||
import CodeBlockHighlight from '../extensions/code_block_highlight';
|
||||
import Diagram from '../extensions/diagram';
|
||||
import Frontmatter from '../extensions/frontmatter';
|
||||
import EditorStateObserver from './editor_state_observer.vue';
|
||||
import codeBlockLanguageLoader from '../../services/code_block_language_loader';
|
||||
import CodeBlockHighlight from '../../extensions/code_block_highlight';
|
||||
import Diagram from '../../extensions/diagram';
|
||||
import Frontmatter from '../../extensions/frontmatter';
|
||||
import EditorStateObserver from '../editor_state_observer.vue';
|
||||
|
||||
const CODE_BLOCK_NODE_TYPES = [CodeBlockHighlight.name, Diagram.name, Frontmatter.name];
|
||||
|
|
@ -1,13 +1,13 @@
|
|||
<script>
|
||||
import { GlButtonGroup } from '@gitlab/ui';
|
||||
import { BubbleMenu } from '@tiptap/vue-2';
|
||||
import { BUBBLE_MENU_TRACKING_ACTION } from '../constants';
|
||||
import trackUIControl from '../services/track_ui_control';
|
||||
import Code from '../extensions/code';
|
||||
import CodeBlockHighlight from '../extensions/code_block_highlight';
|
||||
import Diagram from '../extensions/diagram';
|
||||
import Frontmatter from '../extensions/frontmatter';
|
||||
import ToolbarButton from './toolbar_button.vue';
|
||||
import { BUBBLE_MENU_TRACKING_ACTION } from '../../constants';
|
||||
import trackUIControl from '../../services/track_ui_control';
|
||||
import Code from '../../extensions/code';
|
||||
import CodeBlockHighlight from '../../extensions/code_block_highlight';
|
||||
import Diagram from '../../extensions/diagram';
|
||||
import Frontmatter from '../../extensions/frontmatter';
|
||||
import ToolbarButton from '../toolbar_button.vue';
|
||||
|
||||
export default {
|
||||
components: {
|
|
@ -4,8 +4,8 @@ import { createContentEditor } from '../services/create_content_editor';
|
|||
import ContentEditorAlert from './content_editor_alert.vue';
|
||||
import ContentEditorProvider from './content_editor_provider.vue';
|
||||
import EditorStateObserver from './editor_state_observer.vue';
|
||||
import FormattingBubbleMenu from './formatting_bubble_menu.vue';
|
||||
import CodeBlockBubbleMenu from './code_block_bubble_menu.vue';
|
||||
import FormattingBubbleMenu from './bubble_menus/formatting.vue';
|
||||
import CodeBlockBubbleMenu from './bubble_menus/code_block.vue';
|
||||
import TopToolbar from './top_toolbar.vue';
|
||||
import LoadingIndicator from './loading_indicator.vue';
|
||||
|
||||
|
|
|
@ -1,3 +0,0 @@
|
|||
<template>
|
||||
<span class="gl-mx-3 gl-border-r-solid gl-border-r-1 gl-border-gray-200"></span>
|
||||
</template>
|
|
@ -42,7 +42,7 @@ export default {
|
|||
size: {
|
||||
type: String,
|
||||
required: false,
|
||||
default: 'small',
|
||||
default: 'medium',
|
||||
},
|
||||
},
|
||||
data() {
|
||||
|
|
|
@ -1,6 +1,5 @@
|
|||
<script>
|
||||
import trackUIControl from '../services/track_ui_control';
|
||||
import Divider from './divider.vue';
|
||||
import ToolbarButton from './toolbar_button.vue';
|
||||
import ToolbarImageButton from './toolbar_image_button.vue';
|
||||
import ToolbarLinkButton from './toolbar_link_button.vue';
|
||||
|
@ -14,7 +13,6 @@ export default {
|
|||
ToolbarLinkButton,
|
||||
ToolbarTableButton,
|
||||
ToolbarImageButton,
|
||||
Divider,
|
||||
},
|
||||
methods: {
|
||||
trackToolbarControlExecution({ contentType, value }) {
|
||||
|
@ -25,13 +23,13 @@ export default {
|
|||
</script>
|
||||
<template>
|
||||
<div
|
||||
class="gl-display-flex gl-justify-content-end gl-pb-3 gl-pt-0 gl-border-b-solid gl-border-b-1 gl-border-b-gray-200"
|
||||
class="gl-display-flex gl-flex-wrap gl-pb-3 gl-pt-0 gl-border-b-solid gl-border-b-1 gl-border-b-gray-200"
|
||||
>
|
||||
<toolbar-text-style-dropdown
|
||||
data-testid="text-styles"
|
||||
class="gl-mr-3"
|
||||
@execute="trackToolbarControlExecution"
|
||||
/>
|
||||
<divider />
|
||||
<toolbar-button
|
||||
data-testid="bold"
|
||||
content-type="bold"
|
||||
|
@ -69,7 +67,6 @@ export default {
|
|||
@execute="trackToolbarControlExecution"
|
||||
/>
|
||||
<toolbar-link-button data-testid="link" @execute="trackToolbarControlExecution" />
|
||||
<divider />
|
||||
<toolbar-image-button
|
||||
ref="imageButton"
|
||||
data-testid="image"
|
||||
|
|
|
@ -3,7 +3,6 @@
|
|||
|
||||
mutation uploadDesign($files: [Upload!]!, $projectPath: ID!, $iid: ID!) {
|
||||
designManagementUpload(input: { projectPath: $projectPath, iid: $iid, files: $files }) {
|
||||
# eslint-disable-next-line @graphql-eslint/require-id-when-available
|
||||
designs {
|
||||
...DesignItem
|
||||
versions {
|
||||
|
|
|
@ -13,7 +13,6 @@ query getDesign(
|
|||
id
|
||||
designCollection {
|
||||
designs(atVersion: $atVersion, filenames: $filenames) {
|
||||
# eslint-disable-next-line @graphql-eslint/require-id-when-available
|
||||
nodes {
|
||||
...DesignItem
|
||||
issue {
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
fragment TimelogFragment on Timelog {
|
||||
id
|
||||
timeSpent
|
||||
user {
|
||||
id
|
||||
|
|
|
@ -1,4 +1,3 @@
|
|||
# eslint-disable-next-line @graphql-eslint/require-id-when-available
|
||||
fragment UserAvailability on User {
|
||||
status {
|
||||
availability
|
||||
|
|
|
@ -1,4 +1,3 @@
|
|||
# eslint-disable-next-line @graphql-eslint/require-id-when-available
|
||||
fragment IncidentFields on Issue {
|
||||
severity
|
||||
escalationStatus
|
||||
|
|
|
@ -54,15 +54,15 @@ export default {
|
|||
return {
|
||||
message: this.defaultMessage,
|
||||
openMergeRequest: false,
|
||||
targetBranch: this.currentBranch,
|
||||
sourceBranch: this.currentBranch,
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
isCommitFormFilledOut() {
|
||||
return this.message && this.targetBranch;
|
||||
return this.message && this.sourceBranch;
|
||||
},
|
||||
isCurrentBranchTarget() {
|
||||
return this.targetBranch === this.currentBranch;
|
||||
isCurrentBranchSourceBranch() {
|
||||
return this.sourceBranch === this.currentBranch;
|
||||
},
|
||||
isSubmitDisabled() {
|
||||
return !this.isCommitFormFilledOut || (!this.hasUnsavedChanges && !this.isNewCiConfigFile);
|
||||
|
@ -79,7 +79,7 @@ export default {
|
|||
onSubmit() {
|
||||
this.$emit('submit', {
|
||||
message: this.message,
|
||||
targetBranch: this.targetBranch,
|
||||
sourceBranch: this.sourceBranch,
|
||||
openMergeRequest: this.openMergeRequest,
|
||||
});
|
||||
},
|
||||
|
@ -93,7 +93,7 @@ export default {
|
|||
},
|
||||
i18n: {
|
||||
commitMessage: __('Commit message'),
|
||||
targetBranch: __('Target Branch'),
|
||||
sourceBranch: __('Branch'),
|
||||
startMergeRequest: __('Start a %{new_merge_request} with these changes'),
|
||||
newMergeRequest: __('new merge request'),
|
||||
commitChanges: __('Commit changes'),
|
||||
|
@ -120,20 +120,20 @@ export default {
|
|||
/>
|
||||
</gl-form-group>
|
||||
<gl-form-group
|
||||
id="target-branch-group"
|
||||
:label="$options.i18n.targetBranch"
|
||||
id="source-branch-group"
|
||||
:label="$options.i18n.sourceBranch"
|
||||
label-cols-sm="2"
|
||||
label-for="target-branch-field"
|
||||
label-for="source-branch-field"
|
||||
>
|
||||
<gl-form-input
|
||||
id="target-branch-field"
|
||||
v-model="targetBranch"
|
||||
id="source-branch-field"
|
||||
v-model="sourceBranch"
|
||||
class="gl-font-monospace!"
|
||||
required
|
||||
data-qa-selector="target_branch_field"
|
||||
data-qa-selector="source_branch_field"
|
||||
/>
|
||||
<gl-form-checkbox
|
||||
v-if="!isCurrentBranchTarget"
|
||||
v-if="!isCurrentBranchSourceBranch"
|
||||
v-model="openMergeRequest"
|
||||
data-testid="new-mr-checkbox"
|
||||
data-qa-selector="new_mr_checkbox"
|
||||
|
|
|
@ -75,7 +75,7 @@ export default {
|
|||
},
|
||||
},
|
||||
methods: {
|
||||
async onCommitSubmit({ message, targetBranch, openMergeRequest }) {
|
||||
async onCommitSubmit({ message, sourceBranch, openMergeRequest }) {
|
||||
this.isSaving = true;
|
||||
|
||||
try {
|
||||
|
@ -88,7 +88,7 @@ export default {
|
|||
variables: {
|
||||
action: this.action,
|
||||
projectPath: this.projectFullPath,
|
||||
branch: targetBranch,
|
||||
branch: sourceBranch,
|
||||
startBranch: this.currentBranch,
|
||||
message,
|
||||
filePath: this.ciConfigPath,
|
||||
|
@ -104,12 +104,11 @@ export default {
|
|||
if (errors?.length) {
|
||||
this.$emit('showError', { type: COMMIT_FAILURE, reasons: errors });
|
||||
} else {
|
||||
const commitBranch = targetBranch;
|
||||
const params = openMergeRequest
|
||||
? {
|
||||
type: COMMIT_SUCCESS_WITH_REDIRECT,
|
||||
params: {
|
||||
sourceBranch: commitBranch,
|
||||
sourceBranch,
|
||||
targetBranch: this.currentBranch,
|
||||
},
|
||||
}
|
||||
|
@ -119,10 +118,10 @@ export default {
|
|||
...params,
|
||||
});
|
||||
|
||||
this.updateLastCommitBranch(targetBranch);
|
||||
this.updateCurrentBranch(targetBranch);
|
||||
this.updateLastCommitBranch(sourceBranch);
|
||||
this.updateCurrentBranch(sourceBranch);
|
||||
|
||||
if (this.currentBranch === targetBranch) {
|
||||
if (this.currentBranch === sourceBranch) {
|
||||
this.$emit('updateCommitSha');
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,8 +1,6 @@
|
|||
#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
|
||||
}
|
||||
|
|
|
@ -5,8 +5,6 @@
|
|||
|
||||
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
|
||||
}
|
||||
|
|
|
@ -87,6 +87,7 @@ export default {
|
|||
:get-active-token-value="getActiveAuthor"
|
||||
:default-suggestions="defaultAuthors"
|
||||
:preloaded-suggestions="preloadedAuthors"
|
||||
v-bind="$attrs"
|
||||
@fetch-suggestions="fetchAuthors"
|
||||
v-on="$listeners"
|
||||
>
|
||||
|
|
|
@ -65,6 +65,7 @@ export default {
|
|||
:suggestions="branches"
|
||||
:suggestions-loading="loading"
|
||||
:get-active-token-value="getActiveBranch"
|
||||
v-bind="$attrs"
|
||||
@fetch-suggestions="fetchBranches"
|
||||
v-on="$listeners"
|
||||
>
|
||||
|
|
|
@ -67,6 +67,7 @@ export default {
|
|||
:suggestions="emojis"
|
||||
:suggestions-loading="loading"
|
||||
:get-active-token-value="getActiveEmoji"
|
||||
v-bind="$attrs"
|
||||
@fetch-suggestions="fetchEmojis"
|
||||
v-on="$listeners"
|
||||
>
|
||||
|
|
|
@ -104,6 +104,7 @@ export default {
|
|||
:suggestions="labels"
|
||||
:get-active-token-value="getActiveLabel"
|
||||
:default-suggestions="defaultLabels"
|
||||
v-bind="$attrs"
|
||||
@fetch-suggestions="fetchLabels"
|
||||
v-on="$listeners"
|
||||
>
|
||||
|
|
|
@ -84,6 +84,7 @@ export default {
|
|||
:suggestions="milestones"
|
||||
:suggestions-loading="loading"
|
||||
:get-active-token-value="getActiveMilestone"
|
||||
v-bind="$attrs"
|
||||
@fetch-suggestions="fetchMilestones"
|
||||
v-on="$listeners"
|
||||
>
|
||||
|
|
|
@ -66,6 +66,7 @@ export default {
|
|||
:suggestions="releases"
|
||||
:suggestions-loading="loading"
|
||||
:get-active-token-value="getActiveRelease"
|
||||
v-bind="$attrs"
|
||||
@fetch-suggestions="fetchReleases"
|
||||
v-on="$listeners"
|
||||
>
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
class Profiles::GpgKeysController < Profiles::ApplicationController
|
||||
before_action :set_gpg_key, only: [:destroy, :revoke]
|
||||
|
||||
feature_category :users
|
||||
feature_category :source_code_management
|
||||
|
||||
def index
|
||||
@gpg_keys = current_user.gpg_keys.with_subkeys
|
||||
|
|
|
@ -23,19 +23,24 @@ module Mutations
|
|||
null: true,
|
||||
description: 'Runner token after mutation.'
|
||||
|
||||
def resolve(**args)
|
||||
def resolve(type:, id: nil)
|
||||
scope = authorized_find!(type: type, id: id)
|
||||
new_token = reset_token(scope)
|
||||
|
||||
{
|
||||
token: reset_token(**args),
|
||||
errors: []
|
||||
token: new_token,
|
||||
errors: errors_on_object(scope)
|
||||
}
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def find_object(type:, **args)
|
||||
id = args[:id]
|
||||
|
||||
def find_object(type:, id: nil)
|
||||
case type
|
||||
when 'instance_type'
|
||||
raise Gitlab::Graphql::Errors::ArgumentError, "id must not be specified for '#{type}' scope" if id.present?
|
||||
|
||||
ApplicationSetting.current
|
||||
when 'group_type'
|
||||
GitlabSchema.object_from_id(id, expected_type: ::Group)
|
||||
when 'project_type'
|
||||
|
@ -43,20 +48,7 @@ module Mutations
|
|||
end
|
||||
end
|
||||
|
||||
def reset_token(type:, **args)
|
||||
id = args[:id]
|
||||
scope = nil
|
||||
|
||||
case type
|
||||
when 'instance_type'
|
||||
raise Gitlab::Graphql::Errors::ArgumentError, "id must not be specified for '#{type}' scope" if id.present?
|
||||
|
||||
scope = ApplicationSetting.current
|
||||
authorize!(scope)
|
||||
when 'group_type', 'project_type'
|
||||
scope = authorized_find!(type: type, id: id)
|
||||
end
|
||||
|
||||
def reset_token(scope)
|
||||
::Ci::Runners::ResetRegistrationTokenService.new(scope, current_user).execute if scope
|
||||
end
|
||||
end
|
||||
|
|
|
@ -0,0 +1,35 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
module Mutations
|
||||
module Timelogs
|
||||
class Delete < Mutations::BaseMutation
|
||||
graphql_name 'TimelogDelete'
|
||||
|
||||
field :timelog,
|
||||
Types::TimelogType,
|
||||
null: true,
|
||||
description: 'Deleted timelog.'
|
||||
|
||||
argument :id,
|
||||
::Types::GlobalIDType[::Timelog],
|
||||
required: true,
|
||||
description: 'Global ID of the timelog.'
|
||||
|
||||
authorize :admin_timelog
|
||||
|
||||
def resolve(id:)
|
||||
timelog = authorized_find!(id: id)
|
||||
result = ::Timelogs::DeleteService.new(timelog, current_user).execute
|
||||
|
||||
# Return the result payload, not the loaded timelog, so that it returns null in case of unauthorized access
|
||||
{ timelog: result.payload, errors: result.errors }
|
||||
end
|
||||
|
||||
def find_object(id:)
|
||||
# TODO: Remove coercion when working on https://gitlab.com/gitlab-org/gitlab/-/issues/257883
|
||||
id = ::Types::GlobalIDType[::Timelog].coerce_isolated_input(id)
|
||||
GitlabSchema.find_by_gid(id)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
|
@ -88,6 +88,7 @@ module Types
|
|||
mount_mutation Mutations::Terraform::State::Delete
|
||||
mount_mutation Mutations::Terraform::State::Lock
|
||||
mount_mutation Mutations::Terraform::State::Unlock
|
||||
mount_mutation Mutations::Timelogs::Delete
|
||||
mount_mutation Mutations::Todos::Create
|
||||
mount_mutation Mutations::Todos::MarkDone
|
||||
mount_mutation Mutations::Todos::Restore
|
||||
|
|
|
@ -6,6 +6,11 @@ module Types
|
|||
|
||||
authorize :read_issue
|
||||
|
||||
field :id,
|
||||
GraphQL::Types::ID,
|
||||
null: false,
|
||||
description: 'Internal ID of the timelog.'
|
||||
|
||||
field :spent_at,
|
||||
Types::TimeType,
|
||||
null: true,
|
||||
|
|
|
@ -39,7 +39,7 @@ module ClustersHelper
|
|||
base_domain: cluster.base_domain,
|
||||
application_ingress_external_ip: cluster.application_ingress_external_ip,
|
||||
auto_devops_help_path: help_page_path('topics/autodevops/index'),
|
||||
external_endpoint_help_path: help_page_path('user/project/clusters/index.md', anchor: 'base-domain')
|
||||
external_endpoint_help_path: help_page_path('user/project/clusters/gitlab_managed_clusters.md', anchor: 'base-domain')
|
||||
}
|
||||
end
|
||||
|
||||
|
|
|
@ -2,4 +2,11 @@
|
|||
|
||||
class TimelogPolicy < BasePolicy
|
||||
delegate { @subject.issuable }
|
||||
|
||||
desc "User who created the timelog"
|
||||
condition(:is_author) { @user && @subject.user == @user }
|
||||
|
||||
rule { is_author | can?(:maintainer_access) }.policy do
|
||||
enable :admin_timelog
|
||||
end
|
||||
end
|
||||
|
|
|
@ -111,6 +111,21 @@ module SystemNoteService
|
|||
::SystemNotes::TimeTrackingService.new(noteable: noteable, project: project, author: author).change_time_spent
|
||||
end
|
||||
|
||||
# Called when a timelog is removed from a Noteable
|
||||
#
|
||||
# noteable - Noteable object
|
||||
# project - Project owning the noteable
|
||||
# author - User performing the change
|
||||
# timelog - The removed timelog
|
||||
#
|
||||
# Example Note text:
|
||||
# "deleted 2h 30m of time spent from 22-03-2022"
|
||||
#
|
||||
# Returns the created Note object
|
||||
def remove_timelog(noteable, project, author, timelog)
|
||||
::SystemNotes::TimeTrackingService.new(noteable: noteable, project: project, author: author).remove_timelog(timelog)
|
||||
end
|
||||
|
||||
def close_after_error_tracking_resolve(issue, project, author)
|
||||
::SystemNotes::IssuablesService.new(noteable: issue, project: project, author: author).close_after_error_tracking_resolve
|
||||
end
|
||||
|
|
|
@ -76,6 +76,18 @@ module SystemNotes
|
|||
create_note(NoteSummary.new(noteable, project, author, body, action: 'time_tracking'))
|
||||
end
|
||||
|
||||
def remove_timelog(timelog)
|
||||
time_spent = timelog.time_spent
|
||||
spent_at = timelog.spent_at&.to_date
|
||||
|
||||
parsed_time = Gitlab::TimeTrackingFormatter.output(time_spent)
|
||||
|
||||
body = "deleted #{parsed_time} of spent time"
|
||||
body += " from #{spent_at}" if spent_at
|
||||
|
||||
create_note(NoteSummary.new(noteable, project, author, body, action: 'time_tracking'))
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def issue_activity_counter
|
||||
|
|
|
@ -0,0 +1,15 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
module Timelogs
|
||||
class BaseService
|
||||
include BaseServiceUtility
|
||||
include Gitlab::Utils::StrongMemoize
|
||||
|
||||
attr_accessor :timelog, :current_user
|
||||
|
||||
def initialize(timelog, user)
|
||||
@timelog = timelog
|
||||
@current_user = user
|
||||
end
|
||||
end
|
||||
end
|
|
@ -0,0 +1,26 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
module Timelogs
|
||||
class DeleteService < Timelogs::BaseService
|
||||
def execute
|
||||
unless can?(current_user, :admin_timelog, timelog)
|
||||
return ServiceResponse.error(
|
||||
message: "Timelog doesn't exist or you don't have permission to delete it",
|
||||
http_status: 404)
|
||||
end
|
||||
|
||||
if timelog.destroy
|
||||
issuable = timelog.issuable
|
||||
|
||||
if issuable
|
||||
# Add a system note for the timelog removal
|
||||
SystemNoteService.remove_timelog(issuable, issuable.project, current_user, timelog)
|
||||
end
|
||||
|
||||
ServiceResponse.success(payload: timelog)
|
||||
else
|
||||
ServiceResponse.error(message: 'Failed to remove timelog', http_status: 400)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
|
@ -32,6 +32,7 @@
|
|||
- continuous_integration
|
||||
- continuous_integration_scaling
|
||||
- continuous_verification
|
||||
- credential_management
|
||||
- database
|
||||
- dataops
|
||||
- delivery
|
||||
|
@ -87,6 +88,7 @@
|
|||
- package_registry
|
||||
- pages
|
||||
- performance_testing
|
||||
- permissions
|
||||
- pipeline_authoring
|
||||
- planning_analytics
|
||||
- portfolio_management
|
||||
|
@ -118,8 +120,10 @@
|
|||
- static_application_security_testing
|
||||
- static_site_editor
|
||||
- subgroups
|
||||
- system_access
|
||||
- team_planning
|
||||
- tracing
|
||||
- user_management
|
||||
- users
|
||||
- utilization
|
||||
- value_stream_management
|
||||
|
|
|
@ -11,6 +11,8 @@
|
|||
[agent for Kubernetes](https://docs.gitlab.com/ee/user/clusters/agent/) to connect Kubernetes clusters with GitLab. [How do I migrate?](https://docs.gitlab.com/ee/user/infrastructure/clusters/migrate_to_gitlab_agent.html)
|
||||
|
||||
For updates and details about this deprecation, follow [this epic](https://gitlab.com/groups/gitlab-org/configure/-/epics/8).
|
||||
|
||||
GitLab self-managed customers can still use the feature [with a feature flag](https://docs.gitlab.com/ee/update/deprecations.html#self-managed-certificate-based-integration-with-kubernetes).
|
||||
stage: Configure
|
||||
tiers: [Free, Premium, Ultimate]
|
||||
issue_url: 'https://gitlab.com/groups/gitlab-org/configure/-/epics/8'
|
||||
|
|
|
@ -90,22 +90,22 @@
|
|||
image_url: https://img.youtube.com/vi/-1MuKzWJXKQ/hqdefault.jpg
|
||||
published_at: 2021-07-22
|
||||
release: 14.1
|
||||
- title: CI/CD Tunnel for Kubernetes clusters
|
||||
- title: CI/CD workflow for Kubernetes clusters
|
||||
body: |
|
||||
Until now, connecting Kubernetes clusters to GitLab CI/CD required users to open up their clusters towards GitLab. Some organizations do not encourage opening up their firewall externally due to security concerns.
|
||||
Until now, connecting Kubernetes clusters to GitLab CI/CD required you to open up your clusters towards GitLab. Some organizations do not encourage opening up their firewall externally due to security concerns.
|
||||
|
||||
GitLab now ships with a CI/CD Tunnel that connects GitLab Runners with your Kubernetes cluster using the [GitLab Kubernetes Agent](https://docs.gitlab.com/ee/user/clusters/agent/). This enables versatile GitOps workflows where the deployment logic can be coded in the pipeline.
|
||||
GitLab now ships with a CI/CD functionality that connects runners with your Kubernetes cluster by using the [GitLab agent for Kubernetes](https://docs.gitlab.com/ee/user/clusters/agent/). This enables versatile GitOps workflows where the deployment logic can be coded in the pipeline.
|
||||
|
||||
You and your team can safely use your preferred tool to run the deployment itself using `kubectl`, `helm`, `kpt`, `tanka`, or anything else without security concerns.
|
||||
|
||||
To use the tunnel, define the `kubecontext` in your CI/CD pipeline to connect with your agent. To simplify this process, we plan to [automatically inject the `kubecontext`](https://gitlab.com/gitlab-org/gitlab/-/issues/324275) into the CI/CD environment in a future iteration.
|
||||
Define the `kubecontext` in your CI/CD pipeline to connect with your agent. To simplify this process, we plan to [automatically inject the `kubecontext`](https://gitlab.com/gitlab-org/gitlab/-/issues/324275) into the CI/CD environment in a future iteration.
|
||||
|
||||
The CI/CD tunnel is currently supported only from the project where the agent was configured but we are working on [adding group-level support](https://gitlab.com/groups/gitlab-org/-/epics/5784). You can safely start using the tunnel on GitLab SaaS and self-managed instances.
|
||||
This type of connection is currently supported only from the project where the agent was configured but we are working on [adding group-level support](https://gitlab.com/groups/gitlab-org/-/epics/5784). You can safely start using CI/CD in your jobs on GitLab SaaS and self-managed instances.
|
||||
stage: Configure
|
||||
self-managed: true
|
||||
gitlab-com: true
|
||||
packages: [Premium, Ultimate]
|
||||
url: https://docs.gitlab.com/ee/user/clusters/agent/ci_cd_tunnel.html
|
||||
url: https://docs.gitlab.com/ee/user/clusters/agent/ci_cd_workflow.html
|
||||
image_url: https://img.youtube.com/vi/eXxM4ScqiJs/hqdefault.jpg
|
||||
published_at: 2021-07-22
|
||||
release: 14.1
|
||||
|
|
|
@ -0,0 +1,17 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
class DeleteFailedResetDuplicateCiRunnersTokenMigrationRecords < Gitlab::Database::Migration[1.0]
|
||||
def up
|
||||
# Delete remaining records of botched migrations before we start the new migrations
|
||||
Gitlab::Database::BackgroundMigrationJob
|
||||
.for_migration_class('ResetDuplicateCiRunnersTokenValuesOnProjects')
|
||||
.delete_all
|
||||
Gitlab::Database::BackgroundMigrationJob
|
||||
.for_migration_class('ResetDuplicateCiRunnersTokenEncryptedValuesOnProjects')
|
||||
.delete_all
|
||||
end
|
||||
|
||||
def down
|
||||
# no-op
|
||||
end
|
||||
end
|
|
@ -0,0 +1,31 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
class Schedule20220328ResetDuplicateCiRunnersTokenEncryptedValuesOnProjects < Gitlab::Database::Migration[1.0]
|
||||
MIGRATION = 'ResetDuplicateCiRunnersTokenEncryptedValuesOnProjects'
|
||||
BATCH_SIZE = 2_000
|
||||
DELAY_INTERVAL = 2.minutes
|
||||
|
||||
disable_ddl_transaction!
|
||||
|
||||
class Project < ActiveRecord::Base # rubocop:disable Style/Documentation
|
||||
include ::EachBatch
|
||||
|
||||
self.table_name = 'projects'
|
||||
|
||||
scope :base_query, -> { where.not(runners_token_encrypted: nil) }
|
||||
end
|
||||
|
||||
def up
|
||||
queue_background_migration_jobs_by_range_at_intervals(
|
||||
Project.base_query,
|
||||
MIGRATION,
|
||||
DELAY_INTERVAL,
|
||||
batch_size: BATCH_SIZE,
|
||||
track_jobs: true
|
||||
)
|
||||
end
|
||||
|
||||
def down
|
||||
# no-op
|
||||
end
|
||||
end
|
|
@ -0,0 +1,31 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
class Schedule20220328ResetDuplicateCiRunnersTokenValuesOnProjects < Gitlab::Database::Migration[1.0]
|
||||
MIGRATION = 'ResetDuplicateCiRunnersTokenValuesOnProjects'
|
||||
BATCH_SIZE = 2_000
|
||||
DELAY_INTERVAL = 2.minutes
|
||||
|
||||
disable_ddl_transaction!
|
||||
|
||||
class Project < ActiveRecord::Base # rubocop:disable Style/Documentation
|
||||
include ::EachBatch
|
||||
|
||||
self.table_name = 'projects'
|
||||
|
||||
scope :base_query, -> { where.not(runners_token: nil) }
|
||||
end
|
||||
|
||||
def up
|
||||
queue_background_migration_jobs_by_range_at_intervals(
|
||||
Project.base_query,
|
||||
MIGRATION,
|
||||
DELAY_INTERVAL,
|
||||
batch_size: BATCH_SIZE,
|
||||
track_jobs: true
|
||||
)
|
||||
end
|
||||
|
||||
def down
|
||||
# no-op
|
||||
end
|
||||
end
|
|
@ -0,0 +1 @@
|
|||
4d75e2180a30d3cdd4efa3b6a7d107e146b755faf0316e985a8813a85644af35
|
|
@ -0,0 +1 @@
|
|||
a27caa521761ff1f4513318eb4ce3ea0e29d101f260493598caf4c8cb0fcc931
|
|
@ -0,0 +1 @@
|
|||
954217de622b1ee360edbd89dd31c5a051001cf6879ce97c7b49c228321d48d7
|
|
@ -181,67 +181,6 @@ _The artifacts are stored by default in
|
|||
1. Save the file and [restart GitLab](restart_gitlab.md#installations-from-source) for the changes to take effect.
|
||||
1. [Migrate any existing local artifacts to the object storage](#migrating-to-object-storage).
|
||||
|
||||
### OpenStack example
|
||||
|
||||
See [the available connection settings for OpenStack](object_storage.md#openstack-compatible-connection-settings).
|
||||
|
||||
**In Omnibus installations:**
|
||||
|
||||
_The uploads are stored by default in
|
||||
`/var/opt/gitlab/gitlab-rails/shared/artifacts`._
|
||||
|
||||
1. Edit `/etc/gitlab/gitlab.rb` and add the following lines, substituting
|
||||
the values you want:
|
||||
|
||||
```ruby
|
||||
gitlab_rails['artifacts_enabled'] = true
|
||||
gitlab_rails['artifacts_object_store_enabled'] = true
|
||||
gitlab_rails['artifacts_object_store_remote_directory'] = "artifacts"
|
||||
gitlab_rails['artifacts_object_store_connection'] = {
|
||||
'provider' => 'OpenStack',
|
||||
'openstack_username' => 'OS_USERNAME',
|
||||
'openstack_api_key' => 'OS_PASSWORD',
|
||||
'openstack_temp_url_key' => 'OS_TEMP_URL_KEY',
|
||||
'openstack_auth_url' => 'https://auth.cloud.ovh.net',
|
||||
'openstack_region' => 'GRA',
|
||||
'openstack_tenant_id' => 'OS_TENANT_ID',
|
||||
}
|
||||
```
|
||||
|
||||
1. Save the file and [reconfigure GitLab](restart_gitlab.md#omnibus-gitlab-reconfigure) for the changes to take effect.
|
||||
1. [Migrate any existing local artifacts to the object storage](#migrating-to-object-storage).
|
||||
|
||||
---
|
||||
|
||||
**In installations from source:**
|
||||
|
||||
_The uploads are stored by default in
|
||||
`/home/git/gitlab/shared/artifacts`._
|
||||
|
||||
1. Edit `/home/git/gitlab/config/gitlab.yml` and add or amend the following
|
||||
lines:
|
||||
|
||||
```yaml
|
||||
uploads:
|
||||
object_store:
|
||||
enabled: true
|
||||
direct_upload: false
|
||||
background_upload: true
|
||||
proxy_download: false
|
||||
remote_directory: "artifacts"
|
||||
connection:
|
||||
provider: OpenStack
|
||||
openstack_username: OS_USERNAME
|
||||
openstack_api_key: OS_PASSWORD
|
||||
openstack_temp_url_key: OS_TEMP_URL_KEY
|
||||
openstack_auth_url: 'https://auth.cloud.ovh.net'
|
||||
openstack_region: GRA
|
||||
openstack_tenant_id: OS_TENANT_ID
|
||||
```
|
||||
|
||||
1. Save the file and [restart GitLab](restart_gitlab.md#installations-from-source) for the changes to take effect.
|
||||
1. [Migrate any existing local artifacts to the object storage](#migrating-to-object-storage).
|
||||
|
||||
### Migrating to object storage
|
||||
|
||||
After [configuring the object storage](#using-object-storage), use the following task to
|
||||
|
|
|
@ -19,7 +19,7 @@ GitLab has been tested by vendors and customers on a number of object storage pr
|
|||
- [Google Cloud Storage](https://cloud.google.com/storage)
|
||||
- [Digital Ocean Spaces](https://www.digitalocean.com/products/spaces)
|
||||
- [Oracle Cloud Infrastructure](https://docs.cloud.oracle.com/en-us/iaas/Content/Object/Tasks/s3compatibleapi.htm)
|
||||
- [OpenStack Swift](https://docs.openstack.org/swift/latest/s3_compat.html)
|
||||
- [OpenStack Swift (S3 compatible mode)](https://docs.openstack.org/swift/latest/s3_compat.html)
|
||||
- [Azure Blob storage](https://docs.microsoft.com/en-us/azure/storage/blobs/storage-blobs-introduction)
|
||||
- On-premises hardware and appliances from various storage vendors, whose list is not officially established.
|
||||
- MinIO. We have [a guide to deploying this](https://docs.gitlab.com/charts/advanced/external-object-storage/minio.html) within our Helm Chart documentation.
|
||||
|
@ -386,52 +386,6 @@ If you are using a custom Azure storage domain,
|
|||
configuration. This information is exchanged in an API call between
|
||||
GitLab Rails and Workhorse.
|
||||
|
||||
#### OpenStack-compatible connection settings
|
||||
|
||||
Although OpenStack Swift provides S3 compatibility, some users may want to use
|
||||
the [Swift API](https://docs.openstack.org/swift/latest/api/object_api_v1_overview.html).
|
||||
|
||||
This isn't compatible with the consolidated object storage form. OpenStack Swift
|
||||
is supported only with the storage-specific form. If you want to use the
|
||||
consolidated form, see the [S3 settings](#s3-compatible-connection-settings).
|
||||
|
||||
Here are the valid connection settings for the Swift API, provided by
|
||||
[fog-openstack](https://github.com/fog/fog-openstack):
|
||||
|
||||
| Setting | Description | Default |
|
||||
|--------------------------|----------------------|---------|
|
||||
| `provider` | Always `OpenStack` for compatible hosts. | `OpenStack` |
|
||||
| `openstack_username` | OpenStack username. | |
|
||||
| `openstack_api_key` | OpenStack API key. | |
|
||||
| `openstack_temp_url_key` | OpenStack key for generating temporary URLs | |
|
||||
| `openstack_auth_url` | OpenStack authentication endpoint | |
|
||||
| `openstack_region` | OpenStack region. | |
|
||||
| `openstack_tenant` | OpenStack tenant ID. | |
|
||||
|
||||
#### Rackspace Cloud Files
|
||||
|
||||
The following table describes the valid connection parameters for
|
||||
Rackspace Cloud, provided by [fog-rackspace](https://github.com/fog/fog-rackspace/).
|
||||
|
||||
This isn't compatible with the consolidated object storage form.
|
||||
Rackspace Cloud is supported only with the storage-specific form.
|
||||
|
||||
| Setting | Description | Example |
|
||||
|--------------------------|----------------|-------------|
|
||||
| `provider` | Provider name. | `Rackspace` |
|
||||
| `rackspace_username` | Username of the Rackspace account with access to the container. | `joe.smith` |
|
||||
| `rackspace_api_key` | API key of the Rackspace account with access to the container. | `ABC123DEF456ABC123DEF456ABC123DE` |
|
||||
| `rackspace_region` | Rackspace storage region to use, a three letter code from the [list of service access endpoints](https://docs.rackspace.com/docs/cloud-files/v1/general-api-info/service-access/). | `iad` |
|
||||
| `rackspace_temp_url_key` | Private key you set in the Rackspace API for [temporary URLs](https://docs.rackspace.com/docs/cloud-files/v1/use-cases/public-access-to-your-cloud-files-account/#tempurl). | `ABC123DEF456ABC123DEF456ABC123DE` |
|
||||
|
||||
Regardless of whether the container has public access enabled or disabled, Fog
|
||||
uses the TempURL method to grant access to LFS objects. If you see error
|
||||
messages in logs that refer to instantiating storage with a `temp-url-key`,
|
||||
be sure you have set the key properly both in the Rackspace API and in
|
||||
`gitlab.rb`. You can verify the value of the key Rackspace has set by sending a
|
||||
GET request with token header to the service access endpoint URL and comparing
|
||||
the output of the returned headers.
|
||||
|
||||
### Object-specific configuration
|
||||
|
||||
The following YAML shows how the `object_store` section defines
|
||||
|
@ -799,3 +753,46 @@ to run the following command:
|
|||
```ruby
|
||||
Feature.disable(:s3_multithreaded_uploads)
|
||||
```
|
||||
|
||||
## Migrate objects to a different object storage provider
|
||||
|
||||
You may need to migrate GitLab data in object storage to a different object storage provider. The following steps show you how do this using [Rclone](https://rclone.org/).
|
||||
|
||||
The steps assume you are moving the `uploads` bucket, but the same process works for other buckets.
|
||||
|
||||
Prerequisites:
|
||||
|
||||
- Choose the computer to run Rclone on. Depending on how much data you are migrating, Rclone may have to run for a long time so you should avoid using a laptop or desktop computer that can go into power saving. You can use your GitLab server to run Rclone.
|
||||
|
||||
1. [Install](https://rclone.org/downloads/) Rclone.
|
||||
1. Configure Rclone by running the following:
|
||||
|
||||
```shell
|
||||
rclone config
|
||||
```
|
||||
|
||||
The configuration process is interactive. Add at least two "remotes": one for the object storage provider your data is currently on (`old`), and one for the provider you are moving to (`new`).
|
||||
|
||||
1. Verify that you can read the old data. The following example refers to the `uploads` bucket , but your bucket may have a different name:
|
||||
|
||||
```shell
|
||||
rclone ls old:uploads | head
|
||||
```
|
||||
|
||||
This should print a partial list of the objects currently stored in your `uploads` bucket. If you get an error, or if
|
||||
the list is empty, go back and update your Rclone configuration using `rclone config`.
|
||||
|
||||
1. Perform an initial copy. You do not need to take your GitLab server offline for this step.
|
||||
|
||||
```shell
|
||||
rclone sync -P old:uploads new:uploads
|
||||
```
|
||||
|
||||
1. After the first sync completes, use the web UI or command-line interface of your new object storage provider to
|
||||
verify that there are objects in the new bucket. If there are none, or if you encounter an error while running `rclone
|
||||
sync`, check your Rclone configuration and try again.
|
||||
|
||||
After you have done at least one successful Rclone copy from the old location to the new location, schedule maintenance and take your GitLab server offline. During your maintenance window you must do two things:
|
||||
|
||||
1. Perform a final `rclone sync` run, knowing that your users cannot add new objects so you will not leave any behind in the old bucket.
|
||||
1. Update the object storage configuration of your GitLab server to use the new provider for `uploads`.
|
||||
|
|
|
@ -88,8 +88,8 @@ components:
|
|||
|
||||
> [Introduced](https://gitlab.com/gitlab-com/gl-infra/scalability/-/issues/261) in GitLab 13.1 (`tags`).
|
||||
|
||||
Queue matching query works upon the worker attributes, described in [Sidekiq
|
||||
style guide](../../development/sidekiq_style_guide.md). We support querying
|
||||
Queue matching query works upon the worker attributes, described in
|
||||
[Sidekiq style guide](../../development/sidekiq/index.md). We support querying
|
||||
based on a subset of worker attributes:
|
||||
|
||||
- `feature_category` - the [GitLab feature
|
||||
|
|
|
@ -2169,7 +2169,7 @@ GitLab has been tested on a number of object storage providers:
|
|||
- [Google Cloud Storage](https://cloud.google.com/storage)
|
||||
- [Digital Ocean Spaces](http://www.digitalocean.com/products/spaces)
|
||||
- [Oracle Cloud Infrastructure](https://docs.cloud.oracle.com/en-us/iaas/Content/Object/Tasks/s3compatibleapi.htm)
|
||||
- [OpenStack Swift](https://docs.openstack.org/swift/latest/s3_compat.html)
|
||||
- [OpenStack Swift (S3 compatibility mode)](https://docs.openstack.org/swift/latest/s3_compat.html)
|
||||
- [Azure Blob storage](https://docs.microsoft.com/en-us/azure/storage/blobs/storage-blobs-introduction)
|
||||
- On-premises hardware and appliances from various storage vendors.
|
||||
- MinIO. We have [a guide to deploying this](https://docs.gitlab.com/charts/advanced/external-object-storage/minio.html) within our Helm Chart documentation.
|
||||
|
|
|
@ -2173,7 +2173,7 @@ GitLab has been tested on a number of object storage providers:
|
|||
- [Google Cloud Storage](https://cloud.google.com/storage)
|
||||
- [Digital Ocean Spaces](http://www.digitalocean.com/products/spaces)
|
||||
- [Oracle Cloud Infrastructure](https://docs.cloud.oracle.com/en-us/iaas/Content/Object/Tasks/s3compatibleapi.htm)
|
||||
- [OpenStack Swift](https://docs.openstack.org/swift/latest/s3_compat.html)
|
||||
- [OpenStack Swift (S3 compatibility mode)](https://docs.openstack.org/swift/latest/s3_compat.html)
|
||||
- [Azure Blob storage](https://docs.microsoft.com/en-us/azure/storage/blobs/storage-blobs-introduction)
|
||||
- On-premises hardware and appliances from various storage vendors.
|
||||
- MinIO. We have [a guide to deploying this](https://docs.gitlab.com/charts/advanced/external-object-storage/minio.html) within our Helm Chart documentation.
|
||||
|
|
|
@ -891,7 +891,7 @@ GitLab has been tested on a number of object storage providers:
|
|||
- [Google Cloud Storage](https://cloud.google.com/storage)
|
||||
- [Digital Ocean Spaces](http://www.digitalocean.com/products/spaces)
|
||||
- [Oracle Cloud Infrastructure](https://docs.cloud.oracle.com/en-us/iaas/Content/Object/Tasks/s3compatibleapi.htm)
|
||||
- [OpenStack Swift](https://docs.openstack.org/swift/latest/s3_compat.html)
|
||||
- [OpenStack Swift (S3 compatibility mode)](https://docs.openstack.org/swift/latest/s3_compat.html)
|
||||
- [Azure Blob storage](https://docs.microsoft.com/en-us/azure/storage/blobs/storage-blobs-introduction)
|
||||
- On-premises hardware and appliances from various storage vendors.
|
||||
- MinIO. We have [a guide to deploying this](https://docs.gitlab.com/charts/advanced/external-object-storage/minio.html) within our Helm Chart documentation.
|
||||
|
|
|
@ -2108,7 +2108,7 @@ GitLab has been tested on a number of object storage providers:
|
|||
- [Google Cloud Storage](https://cloud.google.com/storage)
|
||||
- [Digital Ocean Spaces](http://www.digitalocean.com/products/spaces)
|
||||
- [Oracle Cloud Infrastructure](https://docs.cloud.oracle.com/en-us/iaas/Content/Object/Tasks/s3compatibleapi.htm)
|
||||
- [OpenStack Swift](https://docs.openstack.org/swift/latest/s3_compat.html)
|
||||
- [OpenStack Swift (S3 compatibility mode)](https://docs.openstack.org/swift/latest/s3_compat.html)
|
||||
- [Azure Blob storage](https://docs.microsoft.com/en-us/azure/storage/blobs/storage-blobs-introduction)
|
||||
- On-premises hardware and appliances from various storage vendors.
|
||||
- MinIO. We have [a guide to deploying this](https://docs.gitlab.com/charts/advanced/external-object-storage/minio.html) within our Helm Chart documentation.
|
||||
|
|
|
@ -2189,7 +2189,7 @@ GitLab has been tested on a number of object storage providers:
|
|||
- [Google Cloud Storage](https://cloud.google.com/storage)
|
||||
- [Digital Ocean Spaces](http://www.digitalocean.com/products/spaces)
|
||||
- [Oracle Cloud Infrastructure](https://docs.cloud.oracle.com/en-us/iaas/Content/Object/Tasks/s3compatibleapi.htm)
|
||||
- [OpenStack Swift](https://docs.openstack.org/swift/latest/s3_compat.html)
|
||||
- [OpenStack Swift (S3 compatibility mode)](https://docs.openstack.org/swift/latest/s3_compat.html)
|
||||
- [Azure Blob storage](https://docs.microsoft.com/en-us/azure/storage/blobs/storage-blobs-introduction)
|
||||
- On-premises hardware and appliances from various storage vendors.
|
||||
- MinIO. We have [a guide to deploying this](https://docs.gitlab.com/charts/advanced/external-object-storage/minio.html) within our Helm Chart documentation.
|
||||
|
|
|
@ -2108,7 +2108,7 @@ GitLab has been tested on a number of object storage providers:
|
|||
- [Google Cloud Storage](https://cloud.google.com/storage)
|
||||
- [Digital Ocean Spaces](http://www.digitalocean.com/products/spaces)
|
||||
- [Oracle Cloud Infrastructure](https://docs.cloud.oracle.com/en-us/iaas/Content/Object/Tasks/s3compatibleapi.htm)
|
||||
- [OpenStack Swift](https://docs.openstack.org/swift/latest/s3_compat.html)
|
||||
- [OpenStack Swift (S3 compatibility mode)](https://docs.openstack.org/swift/latest/s3_compat.html)
|
||||
- [Azure Blob storage](https://docs.microsoft.com/en-us/azure/storage/blobs/storage-blobs-introduction)
|
||||
- On-premises hardware and appliances from various storage vendors.
|
||||
- MinIO. We have [a guide to deploying this](https://docs.gitlab.com/charts/advanced/external-object-storage/minio.html) within our Helm Chart documentation.
|
||||
|
|
|
@ -798,6 +798,39 @@ To find features that can be toggled, run `pp p.project_feature`.
|
|||
Available permission levels are listed in
|
||||
[concerns/featurable.rb](https://gitlab.com/gitlab-org/gitlab/blob/master/app/models/concerns/featurable.rb).
|
||||
|
||||
### Get all error messages associated with groups, subgroups, members, and requesters
|
||||
|
||||
Collect error messages associated with groups, subgroups, members, and requesters. This
|
||||
captures error messages that may not appear in the Web interface. This can be especially helpful
|
||||
for troubleshooting issues with [LDAP group sync](../auth/ldap/ldap_synchronization.md#group-sync)
|
||||
and unexpected behavior with users and their membership in groups and subgroups.
|
||||
|
||||
```ruby
|
||||
# Find the group and subgroup
|
||||
group = Group.find_by_full_path("parent_group")
|
||||
subgroup = Group.find_by_full_path("parent_group/child_group")
|
||||
|
||||
# Group and subgroup errors
|
||||
group.valid?
|
||||
group.errors.map(&:full_messages)
|
||||
|
||||
subgroup.valid?
|
||||
subgroup.errors.map(&:full_messages)
|
||||
|
||||
# Group and subgroup errors for the members AND requesters
|
||||
group.requesters.map(&:valid?)
|
||||
group.requesters.map(&:errors).map(&:full_messages)
|
||||
group.members.map(&:valid?)
|
||||
group.members.map(&:errors).map(&:full_messages)
|
||||
group.members_and_requesters.map(&:errors).map(&:full_messages)
|
||||
|
||||
subgroup.requesters.map(&:valid?)
|
||||
subgroup.requesters.map(&:errors).map(&:full_messages)
|
||||
subgroup.members.map(&:valid?)
|
||||
subgroup.members.map(&:errors).map(&:full_messages)
|
||||
subgroup.members_and_requesters.map(&:errors).map(&:full_messages)
|
||||
```
|
||||
|
||||
## Authentication
|
||||
|
||||
### Re-enable standard web sign-in form
|
||||
|
|
|
@ -130,60 +130,3 @@ _The uploads are stored by default in
|
|||
|
||||
1. Save the file and [restart GitLab](restart_gitlab.md#installations-from-source) for the changes to take effect.
|
||||
1. Migrate any existing local uploads to the object storage using [`gitlab:uploads:migrate:all` Rake task](raketasks/uploads/migrate.md).
|
||||
|
||||
#### OpenStack example
|
||||
|
||||
**In Omnibus installations:**
|
||||
|
||||
_The uploads are stored by default in
|
||||
`/var/opt/gitlab/gitlab-rails/uploads`._
|
||||
|
||||
1. Edit `/etc/gitlab/gitlab.rb` and add the following lines by replacing with
|
||||
the values you want:
|
||||
|
||||
```ruby
|
||||
gitlab_rails['uploads_object_store_remote_directory'] = "OPENSTACK_OBJECT_CONTAINER_NAME"
|
||||
gitlab_rails['uploads_object_store_connection'] = {
|
||||
'provider' => 'OpenStack',
|
||||
'openstack_username' => 'OPENSTACK_USERNAME',
|
||||
'openstack_api_key' => 'OPENSTACK_PASSWORD',
|
||||
'openstack_temp_url_key' => 'OPENSTACK_TEMP_URL_KEY',
|
||||
'openstack_auth_url' => 'https://auth.cloud.ovh.net/v2.0/',
|
||||
'openstack_region' => 'DE1',
|
||||
'openstack_tenant' => 'TENANT_ID',
|
||||
}
|
||||
```
|
||||
|
||||
1. Save the file and [reconfigure GitLab](restart_gitlab.md#omnibus-gitlab-reconfigure) for the changes to take effect.
|
||||
1. Migrate any existing local uploads to the object storage using [`gitlab:uploads:migrate:all` Rake task](raketasks/uploads/migrate.md).
|
||||
|
||||
---
|
||||
|
||||
**In installations from source:**
|
||||
|
||||
_The uploads are stored by default in
|
||||
`/home/git/gitlab/public/uploads`._
|
||||
|
||||
1. Edit `/home/git/gitlab/config/gitlab.yml` and add or amend the following
|
||||
lines:
|
||||
|
||||
```yaml
|
||||
uploads:
|
||||
object_store:
|
||||
enabled: true
|
||||
direct_upload: false
|
||||
background_upload: true
|
||||
proxy_download: false
|
||||
remote_directory: OPENSTACK_OBJECT_CONTAINER_NAME
|
||||
connection:
|
||||
provider: OpenStack
|
||||
openstack_username: OPENSTACK_USERNAME
|
||||
openstack_api_key: OPENSTACK_PASSWORD
|
||||
openstack_temp_url_key: OPENSTACK_TEMP_URL_KEY
|
||||
openstack_auth_url: 'https://auth.cloud.ovh.net/v2.0/'
|
||||
openstack_region: DE1
|
||||
openstack_tenant: 'TENANT_ID'
|
||||
```
|
||||
|
||||
1. Save the file and [reconfigure GitLab](restart_gitlab.md#omnibus-gitlab-reconfigure) for the changes to take effect.
|
||||
1. Migrate any existing local uploads to the object storage using [`gitlab:uploads:migrate:all` Rake task](raketasks/uploads/migrate.md).
|
||||
|
|
|
@ -24,7 +24,7 @@ The following API resources are available in the project context:
|
|||
| Resource | Available endpoints |
|
||||
|:------------------------------------------------------------------------|:------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
|
||||
| [Access requests](access_requests.md) | `/projects/:id/access_requests` (also available for groups) |
|
||||
| [Access tokens](resource_access_tokens.md) | `/projects/:id/access_tokens` (also available for groups) |
|
||||
| [Access tokens](project_access_tokens.md) | `/projects/:id/access_tokens` (also available for groups) |
|
||||
| [Agents](cluster_agents.md) | `/projects/:id/cluster_agents` |
|
||||
| [Award emoji](award_emoji.md) | `/projects/:id/issues/.../award_emoji`, `/projects/:id/merge_requests/.../award_emoji`, `/projects/:id/snippets/.../award_emoji` |
|
||||
| [Branches](branches.md) | `/projects/:id/repository/branches/`, `/projects/:id/repository/merged_branches` |
|
||||
|
|
|
@ -4543,6 +4543,25 @@ Input type: `TimelineEventUpdateInput`
|
|||
| <a id="mutationtimelineeventupdateerrors"></a>`errors` | [`[String!]!`](#string) | Errors encountered during execution of the mutation. |
|
||||
| <a id="mutationtimelineeventupdatetimelineevent"></a>`timelineEvent` | [`TimelineEventType`](#timelineeventtype) | Timeline event. |
|
||||
|
||||
### `Mutation.timelogDelete`
|
||||
|
||||
Input type: `TimelogDeleteInput`
|
||||
|
||||
#### Arguments
|
||||
|
||||
| Name | Type | Description |
|
||||
| ---- | ---- | ----------- |
|
||||
| <a id="mutationtimelogdeleteclientmutationid"></a>`clientMutationId` | [`String`](#string) | A unique identifier for the client performing the mutation. |
|
||||
| <a id="mutationtimelogdeleteid"></a>`id` | [`TimelogID!`](#timelogid) | Global ID of the timelog. |
|
||||
|
||||
#### Fields
|
||||
|
||||
| Name | Type | Description |
|
||||
| ---- | ---- | ----------- |
|
||||
| <a id="mutationtimelogdeleteclientmutationid"></a>`clientMutationId` | [`String`](#string) | A unique identifier for the client performing the mutation. |
|
||||
| <a id="mutationtimelogdeleteerrors"></a>`errors` | [`[String!]!`](#string) | Errors encountered during execution of the mutation. |
|
||||
| <a id="mutationtimelogdeletetimelog"></a>`timelog` | [`Timelog`](#timelog) | Deleted timelog. |
|
||||
|
||||
### `Mutation.todoCreate`
|
||||
|
||||
Input type: `TodoCreateInput`
|
||||
|
@ -16591,6 +16610,7 @@ Describes an incident management timeline event.
|
|||
|
||||
| Name | Type | Description |
|
||||
| ---- | ---- | ----------- |
|
||||
| <a id="timelogid"></a>`id` | [`ID!`](#id) | Internal ID of the timelog. |
|
||||
| <a id="timelogissue"></a>`issue` | [`Issue`](#issue) | Issue that logged time was added to. |
|
||||
| <a id="timelogmergerequest"></a>`mergeRequest` | [`MergeRequest`](#mergerequest) | Merge request that logged time was added to. |
|
||||
| <a id="timelognote"></a>`note` | [`Note`](#note) | Note where the quick action was executed to add the logged time. |
|
||||
|
@ -19824,6 +19844,12 @@ For example: "2021-03-09T14:58:50+00:00".
|
|||
|
||||
See `https://www.iso.org/iso-8601-date-and-time-format.html`.
|
||||
|
||||
### `TimelogID`
|
||||
|
||||
A `TimelogID` is a global ID. It is encoded as a string.
|
||||
|
||||
An example `TimelogID` is: `"gid://gitlab/Timelog/1"`.
|
||||
|
||||
### `TodoID`
|
||||
|
||||
A `TodoID` is a global ID. It is encoded as a string.
|
||||
|
|
|
@ -1,11 +0,0 @@
|
|||
---
|
||||
redirect_to: 'project_access_tokens.md'
|
||||
remove_date: '2022-04-06'
|
||||
---
|
||||
|
||||
This document was moved to [another location](project_access_tokens.md).
|
||||
|
||||
<!-- This redirect file can be deleted after <2022-04-06>. -->
|
||||
<!-- Redirects that point to other docs in the same project expire in three months. -->
|
||||
<!-- Redirects that point to docs in a different project or site (for example, link is not relative and starts with `https:`) expire in one year. -->
|
||||
<!-- Before deletion, see: https://docs.gitlab.com/ee/development/documentation/redirects.html -->
|
|
@ -1,11 +0,0 @@
|
|||
---
|
||||
redirect_to: 'gitlab_experiment.md'
|
||||
remove_date: '2022-04-13'
|
||||
---
|
||||
|
||||
This document was moved to [another location](gitlab_experiment.md).
|
||||
|
||||
<!-- This redirect file can be deleted after <2022-04-13>. -->
|
||||
<!-- Redirects that point to other docs in the same project expire in three months. -->
|
||||
<!-- Redirects that point to docs in a different project or site (for example, link is not relative and starts with `https:`) expire in one year. -->
|
||||
<!-- Before deletion, see: https://docs.gitlab.com/ee/development/documentation/redirects.html -->
|
|
@ -4,13 +4,13 @@ group: Editor
|
|||
info: To determine the technical writer assigned to the Stage/Group associated with this page, see https://about.gitlab.com/handbook/engineering/ux/technical-writing/#assignments
|
||||
---
|
||||
|
||||
# Markdown developer documentation **(FREE)**
|
||||
# GitLab Flavored Markdown (GLFM) developer documentation **(FREE)**
|
||||
|
||||
This page contains the MVC for the developer documentation for GitLab Flavored Markdown.
|
||||
This page contains the MVC for the developer documentation for GitLab Flavored Markdown (GLFM).
|
||||
For the user documentation about Markdown in GitLab, refer to
|
||||
[GitLab Flavored Markdown](../../user/markdown.md).
|
||||
|
||||
## GitLab Flavored Markdown specification guide
|
||||
## GitLab Flavored Markdown (GLFM) specification guide
|
||||
|
||||
The [specification guide](specification_guide/index.md) includes:
|
||||
|
||||
|
@ -18,3 +18,4 @@ The [specification guide](specification_guide/index.md) includes:
|
|||
- [Parsing and rendering](specification_guide/index.md#parsing-and-rendering).
|
||||
- [Goals](specification_guide/index.md#goals).
|
||||
- [Implementation](specification_guide/index.md#implementation) of the spec.
|
||||
- [Workflows](specification_guide/index.md#workflows).
|
||||
|
|
|
@ -385,20 +385,57 @@ subgraph output:<br/>GLFM specification files
|
|||
end
|
||||
```
|
||||
|
||||
#### `canonicalize-html.rb` script
|
||||
|
||||
The `scripts/glfm/canonicalize-html.rb` handles the
|
||||
["canonicalization" of HTML](#canonicalization-of-html). It is a pipe-through
|
||||
helper script which takes as input a static or WYSIWYG HTML string containing
|
||||
extra HTML, and outputs a canonical HTML string.
|
||||
|
||||
It is implemented as a standalone, modular, single-purpose script, based on the
|
||||
[Unix philosophy](https://en.wikipedia.org/wiki/Unix_philosophy#:~:text=The%20Unix%20philosophy%20emphasizes%20building,developers%20other%20than%20its%20creators.).
|
||||
It's easy to use when running the standard CommonMark `spec_tests.py`
|
||||
script, which expects canonical HTML, against the GitLab renderer implementations.
|
||||
|
||||
#### `run-spec-tests.sh` script
|
||||
|
||||
`scripts/glfm/run-spec-tests.sh` is a convenience shell script which runs
|
||||
conformance specs via the CommonMark standard `spec_tests.py` script,
|
||||
which uses the `glfm_specification/output/spec.txt` file and `scripts/glfm/canonicalize-html.rb`
|
||||
helper script to test the GLFM renderer implementations' support for rendering Markdown
|
||||
specification examples to canonical HTML.
|
||||
|
||||
```mermaid
|
||||
graph LR
|
||||
subgraph scripts:
|
||||
A{run-spec-tests.sh} --> C
|
||||
subgraph specification testing process
|
||||
B[canonicalize-html.sh] --> C
|
||||
C[spec_tests.py]
|
||||
end
|
||||
end
|
||||
subgraph input
|
||||
D[spec.txt GLFM specification] --> C
|
||||
E((GLFM static<br/>renderer implementation)) --> B
|
||||
F((GLFM WYSIWYG<br/>renderer implementation)) --> B
|
||||
end
|
||||
subgraph output:<br/>test results/output
|
||||
C --> G[spec_tests.py output]
|
||||
end
|
||||
```
|
||||
|
||||
#### `update-example-snapshots.rb` script
|
||||
|
||||
The `scripts/glfm/update-example-snapshots.rb` script uses input specification
|
||||
files to update example snapshots:
|
||||
The `scripts/glfm/update-example-snapshots.rb` script uses the GLFM `spec.txt` specification
|
||||
file to update example snapshots:
|
||||
|
||||
```mermaid
|
||||
graph LR
|
||||
subgraph script:
|
||||
A{update-example-snapshots.rb}
|
||||
end
|
||||
subgraph input:<br/>input specification files
|
||||
B[downloaded gfm_spec_v_0.29.txt] --> A
|
||||
C[glfm_canonical_examples.txt] --> A
|
||||
D[glfm_example_status.yml] --> A
|
||||
subgraph input:<br/>input specification file
|
||||
B[spec.txt] --> A
|
||||
end
|
||||
subgraph output:<br/>example snapshot files
|
||||
A --> E[examples_index.yml]
|
||||
|
@ -437,7 +474,7 @@ code. It contains only shell scripting commands for the relevant
|
|||
graph LR
|
||||
subgraph script:
|
||||
A{run-snapshopt-tests.sh} --> B
|
||||
B[relevant rspec/jest test files]
|
||||
B[relevant rspec+jest test files]
|
||||
end
|
||||
subgraph input:<br/>YAML
|
||||
C[examples_index.yml] --> B
|
||||
|
@ -450,45 +487,6 @@ subgraph output:<br/>test results/output
|
|||
end
|
||||
```
|
||||
|
||||
#### `canonicalize-html.rb` script
|
||||
|
||||
The `scripts/glfm/canonicalize-html.rb` handles the
|
||||
["canonicalization" of HTML](#canonicalization-of-html). It is a pipe-through
|
||||
helper script which takes as input a static or WYSIWYG HTML string containing
|
||||
extra HTML, and outputs a canonical HTML string.
|
||||
|
||||
It is implemented as a standalone, modular, single-purpose script, based on the
|
||||
[Unix philosophy](https://en.wikipedia.org/wiki/Unix_philosophy#:~:text=The%20Unix%20philosophy%20emphasizes%20building,developers%20other%20than%20its%20creators.).
|
||||
It's easy to use when running the standard CommonMark `spec_tests.py`
|
||||
script, which expects canonical HTML, against the GitLab renderer implementations.
|
||||
|
||||
#### `run-spec-tests.sh` script
|
||||
|
||||
`scripts/glfm/run-spec-tests.sh` is a convenience shell script which runs
|
||||
conformance specs via the CommonMark standard `spec_tests.py` script,
|
||||
which uses the `glfm_specification/output/spec.txt` file and `scripts/glfm/canonicalize-html.rb`
|
||||
helper script to test the GLFM renderer implementations' support for rendering Markdown
|
||||
specification examples to canonical HTML.
|
||||
|
||||
```mermaid
|
||||
graph LR
|
||||
subgraph scripts:
|
||||
A{run-spec-tests.sh} --> C
|
||||
subgraph specification testing process
|
||||
B[canonicalize-html.sh] --> C
|
||||
C[spec_tests.py]
|
||||
end
|
||||
end
|
||||
subgraph input
|
||||
D[spec.txt GLFM specification] --> C
|
||||
E((GLFM static<br/>renderer implementation)) --> B
|
||||
F((GLFM WYSIWYG<br/>renderer implementation)) --> B
|
||||
end
|
||||
subgraph output:<br/>test results/output
|
||||
C --> G[spec_tests.py output]
|
||||
end
|
||||
```
|
||||
|
||||
### Specification files
|
||||
|
||||
These files represent the GLFM specification itself. They are all
|
||||
|
@ -528,12 +526,14 @@ updated, as in the case of all GFM files.
|
|||
|
||||
```yaml
|
||||
07_99_an_example_with_incomplete_wysiwyg_implementation_1:
|
||||
skip_update_example_snapshots: true
|
||||
skip_running_snapshot_static_html_tests: false
|
||||
skip_running_snapshot_wysiwyg_html_tests: true
|
||||
skip_running_snapshot_prosemirror_json_tests: true
|
||||
skip_update_example_snapshots: false
|
||||
skip_update_example_snapshot_html_static: false
|
||||
skip_update_example_snapshot_html_wysiwyg: false
|
||||
skip_running_conformance_static_tests: false
|
||||
skip_running_conformance_wysiwyg_tests: true
|
||||
skip_running_conformance_wysiwyg_tests: false
|
||||
skip_running_snapshot_static_html_tests: false
|
||||
skip_running_snapshot_wysiwyg_html_tests: false
|
||||
skip_running_snapshot_prosemirror_json_tests: false
|
||||
```
|
||||
|
||||
#### Output specification files
|
||||
|
@ -634,7 +634,7 @@ for each entry in `spec/fixtures/glfm/example_snapshots/examples_index.yml`
|
|||
`spec/fixtures/glfm/example_snapshots/markdown.yml` sample entry:
|
||||
|
||||
```yaml
|
||||
06_04_inlines_emphasis_and_strong_emphasis_1: |-
|
||||
06_04_inlines_emphasis_and_strong_emphasis_1: |
|
||||
*foo bar*
|
||||
```
|
||||
|
||||
|
@ -670,11 +670,11 @@ Any exceptions or failures which occur when generating HTML are replaced with an
|
|||
|
||||
```yaml
|
||||
06_04_inlines_emphasis_and_strong_emphasis_1:
|
||||
canonical: |-
|
||||
canonical: |
|
||||
<p><em>foo bar</em></p>
|
||||
static: |-
|
||||
static: |
|
||||
<p data-sourcepos="1:1-1:9" dir="auto"><strong>foo bar</strong></p>
|
||||
wysiwyg: |-
|
||||
wysiwyg: |
|
||||
<p><strong>foo bar</strong></p>
|
||||
```
|
||||
|
||||
|
@ -715,3 +715,28 @@ JSON for each entry in `spec/fixtures/glfm/example_snapshots/examples_index.yml`
|
|||
]
|
||||
}
|
||||
```
|
||||
|
||||
## Workflows
|
||||
|
||||
This section describes how the scripts can be used to manage the GLFM specification and tests.
|
||||
|
||||
### Update the GLFM specification and run conformance tests
|
||||
|
||||
1. Run [`update-specification.rb`](#update-specificationrb-script) to update the GLFM specification [output specification files](#output-specification-files).
|
||||
1. Visually inspect and confirm any resulting changes to the [output specification files](#output-specification-files).
|
||||
1. Run [`run-spec-tests.sh`](http://gdk.test:3005/ee/development/gitlab_flavored_markdown/specification_guide/index.html#run-spec-testssh-script) to run the conformance tests against the canonicalized GLFM specification.
|
||||
1. Commit any changes to the [output specification files](#output-specification-files).
|
||||
|
||||
### Update the example snapshots and run snapshot tests
|
||||
|
||||
1. If you are working on an in-progress feature or bug, make any necessary manual updates to the [input specification files](#input-specification-files). This may include:
|
||||
1. Updating the canonical Markdown or HTML examples in `glfm_specification/input/gitlab_flavored_markdown/glfm_canonical_examples.txt`.
|
||||
1. Updating `glfm_specification/input/gitlab_flavored_markdown/glfm_example_status.yml` to reflect the current status of the examples or tests.
|
||||
1. Run [`update-specification.rb`](#update-specificationrb-script) to update the `spec.txt` to reflect any changes which were made to the [input specification files](#input-specification-files).
|
||||
1. Visually inspect and confirm any resulting changes to the [output specification files](#output-specification-files).
|
||||
1. Run [`update-example-snapshots.rb`](#update-example-snapshotsrb-script) to update the [example snapshot files](#example-snapshot-files).
|
||||
1. Visually inspect and confirm any resulting changes to the [example snapshot files](#example-snapshot-files).
|
||||
1. Run [`run-snapshot-tests.sh`](#run-snapshot-testssh-script) as a convenience script to run all relevant frontend (RSpec) and backend (Jest) tests which use the example snapshots.
|
||||
1. Any frontend or backend snapshot test may also be run individually.
|
||||
1. All frontend and backend tests are also run as part of the continuous integration suite, as they normally are.
|
||||
1. Commit any changes to the [input specification files](#input-specification-files), [output specification files](#output-specification-files), or [example snapshot files](#example-snapshot-files).
|
||||
|
|
|
@ -54,7 +54,7 @@ webserver, and can lead to a denial-of-service (DoS) attack in GitLab as
|
|||
the Kubernetes cluster response times are outside of our control.
|
||||
|
||||
The easiest way to ensure your calls happen a background process is to
|
||||
delegate any such work to happen in a [Sidekiq worker](sidekiq_style_guide.md).
|
||||
delegate any such work to happen in a [Sidekiq worker](sidekiq/index.md).
|
||||
|
||||
You may want to make calls to Kubernetes and return the response, but a background
|
||||
worker isn't a good fit. Consider using
|
||||
|
|
|
@ -11,7 +11,7 @@ info: To determine the technical writer assigned to the Stage/Group associated w
|
|||
GitLab uses [Redis](https://redis.io) for the following distinct purposes:
|
||||
|
||||
- Caching (mostly via `Rails.cache`).
|
||||
- As a job processing queue with [Sidekiq](sidekiq_style_guide.md).
|
||||
- As a job processing queue with [Sidekiq](sidekiq/index.md).
|
||||
- To manage the shared application state.
|
||||
- To store CI trace chunks.
|
||||
- As a Pub/Sub queue backend for ActionCable.
|
||||
|
|
|
@ -1,11 +0,0 @@
|
|||
---
|
||||
redirect_to: 'sidekiq/index.md'
|
||||
remove_date: '2022-04-13'
|
||||
---
|
||||
|
||||
This document was moved to [another location](sidekiq/index.md).
|
||||
|
||||
<!-- This redirect file can be deleted after <2022-04-13>. -->
|
||||
<!-- Redirects that point to other docs in the same project expire in three months. -->
|
||||
<!-- Redirects that point to docs in a different project or site (for example, link is not relative and starts with `https:`) expire in one year. -->
|
||||
<!-- Before deletion, see: https://docs.gitlab.com/ee/development/documentation/redirects.html -->
|
|
@ -431,7 +431,7 @@ applications.
|
|||
| `HELM_UPGRADE_EXTRA_ARGS` | Allows extra options in `helm upgrade` commands when deploying the application. Note that using quotes doesn't prevent word splitting. |
|
||||
| `INCREMENTAL_ROLLOUT_MODE` | If present, can be used to enable an [incremental rollout](#incremental-rollout-to-production) of your application for the production environment. Set to `manual` for manual deployment jobs or `timed` for automatic rollout deployments with a 5 minute delay each one. |
|
||||
| `K8S_SECRET_*` | Any variable prefixed with [`K8S_SECRET_`](#application-secret-variables) is made available by Auto DevOps as environment variables to the deployed application. |
|
||||
| `KUBE_CONTEXT` | From GitLab 14.5, can be used to select a context to use from `KUBECONFIG`. When `KUBE_CONTEXT` is blank, the default context in `KUBECONFIG` (if any) is used. A context must be selected when used [with the agent for Kubernetes](../../user/clusters/agent/ci_cd_tunnel.md). |
|
||||
| `KUBE_CONTEXT` | From GitLab 14.5, can be used to select a context to use from `KUBECONFIG`. When `KUBE_CONTEXT` is blank, the default context in `KUBECONFIG` (if any) is used. A context must be selected when used [with the agent for Kubernetes](../../user/clusters/agent/ci_cd_workflow.md). |
|
||||
| `KUBE_INGRESS_BASE_DOMAIN` | Can be used to set a domain per cluster. See [cluster domains](../../user/project/clusters/gitlab_managed_clusters.md#base-domain) for more information. |
|
||||
| `KUBE_NAMESPACE` | The namespace used for deployments. When using certificate-based clusters, [this value should not be overwritten directly](../../user/project/clusters/deploy_to_cluster.md#custom-namespace). |
|
||||
| `KUBECONFIG` | The kubeconfig to use for deployments. User-provided values take priority over GitLab-provided values. |
|
||||
|
|
|
@ -37,7 +37,7 @@ approach to manage Kubernetes deployments.
|
|||
#### Deploy to Kubernetes from GitLab CI/CD
|
||||
|
||||
With the [GitLab agent for Kubernetes](../user/clusters/agent/install/index.md), you can perform [push-based
|
||||
deployments](../user/clusters/agent/ci_cd_tunnel.md) from GitLab CI/CD. The agent provides
|
||||
deployments](../user/clusters/agent/ci_cd_workflow.md) from GitLab CI/CD. The agent provides
|
||||
a secure and reliable connection between GitLab and your Kubernetes cluster.
|
||||
|
||||
### Deploy to AWS with GitLab CI/CD
|
||||
|
|
|
@ -20,7 +20,7 @@ and running quickly.
|
|||
| <i class="fa fa-youtube-play youtube" aria-hidden="true"></i> [Use GitLab for DevOps](https://www.youtube.com/watch?v=7q9Y1Cv-ib0) (12m 34s) | Use GitLab through the entire DevOps lifecycle, from planning to monitoring. | **{star}** |
|
||||
| [Use Markdown at GitLab](../user/markdown.md) | GitLab Flavored Markdown (GLFM) is used in many areas of GitLab, for example, in merge requests. | **{star}** |
|
||||
| [GitLab 201](https://gitlab.edcast.com/pathways/ECL-44010cf6-7a9c-4b9b-b684-fa08508a3252) | Go beyond the basics to learn more about using GitLab for your work. | |
|
||||
| Learn GitLab project | You might already have the **Learn GitLab** project, which has tutorial-style issues to help you learn GitLab. If not, download [this export file](https://gitlab.com/gitlab-org/gitlab/-/blob/master/vendor/project_templates/learn_gitlab_ultimate.tar.gz) and [import it to a new project](../user/project/settings/import_export.md#import-a-project-and-its-data). | |
|
||||
| <i class="fa fa-youtube-play youtube" aria-hidden="true"></i> [Learn GitLab project walkthrough](https://www.youtube.com/watch?v=-oaI2WEKdI4&list=PL05JrBw4t0KofkHq4GZJ05FnNGa11PQ4d) (59m 2s) | Step through the tutorial-style issues in the **Learn GitLab** project. If you don't have this project, download [the export file](https://gitlab.com/gitlab-org/gitlab/-/blob/master/vendor/project_templates/learn_gitlab_ultimate.tar.gz) and [import it to a new project](../user/project/settings/import_export.md#import-a-project-and-its-data). | |
|
||||
| [Productivity tips](https://about.gitlab.com/blog/2021/02/18/improve-your-gitlab-productivity-with-these-10-tips/) | Get tips to help make you a productive GitLab user. | |
|
||||
| <i class="fa fa-youtube-play youtube" aria-hidden="true"></i> [Structure a multi-team organization](https://www.youtube.com/watch?v=KmASFwSap7c) (37m 37s) | Learn to use issues, milestones, epics, labels, and more to plan and manage your work. | |
|
||||
|
||||
|
|
|
@ -1349,6 +1349,8 @@ For a more robust, secure, forthcoming, and reliable integration with Kubernetes
|
|||
|
||||
For updates and details about this deprecation, follow [this epic](https://gitlab.com/groups/gitlab-org/configure/-/epics/8).
|
||||
|
||||
GitLab self-managed customers can still use the feature [with a feature flag](https://docs.gitlab.com/ee/update/deprecations.html#self-managed-certificate-based-integration-with-kubernetes).
|
||||
|
||||
**Planned removal milestone: 15.0 (2022-05-22)**
|
||||
|
||||
### Self-managed certificate-based integration with Kubernetes
|
||||
|
|
|
@ -1,261 +1,11 @@
|
|||
---
|
||||
stage: Configure
|
||||
group: Configure
|
||||
info: To determine the technical writer assigned to the Stage/Group associated with this page, see https://about.gitlab.com/handbook/engineering/ux/technical-writing/#assignments
|
||||
redirect_to: 'ci_cd_workflow.md'
|
||||
remove_date: '2022-07-20'
|
||||
---
|
||||
|
||||
# Using a GitLab CI/CD workflow for Kubernetes **(FREE)**
|
||||
This document was moved to [another location](ci_cd_workflow.md).
|
||||
|
||||
> - [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/327409) in GitLab 14.1.
|
||||
> - The pre-configured `KUBECONFIG` was [introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/324275) in GitLab 14.2.
|
||||
> - [Introduced](https://gitlab.com/groups/gitlab-org/-/epics/5784) the `ci_access` attribute in GitLab 14.3.
|
||||
> - The ability to authorize groups was [introduced](https://gitlab.com/groups/gitlab-org/-/epics/5784) in GitLab 14.3.
|
||||
> - [Moved](https://gitlab.com/groups/gitlab-org/-/epics/6290) to GitLab Free in 14.5.
|
||||
> - Support for Omnibus installations was [introduced](https://gitlab.com/gitlab-org/omnibus-gitlab/-/merge_requests/5686) in GitLab 14.5.
|
||||
> - The ability to switch between certificate-based clusters and agents was [introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/335089) in GitLab 14.9. The certificate-based cluster context is always called `gitlab-deploy`.
|
||||
|
||||
You can use a GitLab CI/CD workflow to safely deploy to and update your Kubernetes clusters.
|
||||
|
||||
To do so, you must first [install an agent in your cluster](install/index.md). When done, you have a Kubernetes context and can
|
||||
run Kubernetes API commands in your GitLab CI/CD pipeline.
|
||||
|
||||
To ensure access to your cluster is safe:
|
||||
|
||||
- Each agent has a separate context (`kubecontext`).
|
||||
- Only the project where the agent is configured, and any additional projects you authorize, can access the agent in your cluster.
|
||||
|
||||
You do not need to have a runner in the cluster with the agent.
|
||||
|
||||
## GitLab CI/CD workflow steps
|
||||
|
||||
To update a Kubernetes cluster by using GitLab CI/CD, complete the following steps.
|
||||
|
||||
1. Ensure you have a working Kubernetes cluster and the manifests are in a GitLab project.
|
||||
1. In the same GitLab project, [register and install the GitLab agent](install/index.md).
|
||||
1. [Update your `.gitlab-ci.yml` file](#update-your-gitlab-ciyml-file-to-run-kubectl-commands) to
|
||||
select the agent's Kubernetes context and run the Kubernetes API commands.
|
||||
1. Run your pipeline to deploy to or update the cluster.
|
||||
|
||||
If you have multiple GitLab projects that contain Kubernetes manifests:
|
||||
|
||||
1. [Install the GitLab agent](install/index.md) in its own project, or in one of the
|
||||
GitLab projects where you keep Kubernetes manifests.
|
||||
1. [Authorize the agent](#authorize-the-agent) to access your GitLab projects.
|
||||
1. Optional. For added security, [use impersonation](#use-impersonation-to-restrict-project-and-group-access).
|
||||
1. [Update your `.gitlab-ci.yml` file](#update-your-gitlab-ciyml-file-to-run-kubectl-commands) to
|
||||
select the agent's Kubernetes context and run the Kubernetes API commands.
|
||||
1. Run your pipeline to deploy to or update the cluster.
|
||||
|
||||
## Authorize the agent
|
||||
|
||||
You must authorize the agent to access the project where you keep your Kubernetes manifests.
|
||||
You can authorize the agent to access individual projects, or authorize a group or subgroup,
|
||||
so all projects within have access. For added security, you can also
|
||||
[use impersonation](#use-impersonation-to-restrict-project-and-group-access).
|
||||
|
||||
### Authorize the agent to access your projects
|
||||
|
||||
> [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/327850) in GitLab 14.4.
|
||||
|
||||
To authorize the agent to access the GitLab project where you keep Kubernetes manifests:
|
||||
|
||||
1. On the top bar, select **Menu > Projects** and find the project that contains the agent configuration file (`config.yaml`).
|
||||
1. Edit the file. Under the `ci_access` keyword, add the `projects` attribute.
|
||||
1. For the `id`, add the path:
|
||||
|
||||
```yaml
|
||||
ci_access:
|
||||
projects:
|
||||
- id: path/to/project
|
||||
```
|
||||
|
||||
- The Kubernetes projects must be in the same group hierarchy as the project where the agent's configuration is.
|
||||
- You can install additional agents into the same cluster to accommodate additional hierarchies.
|
||||
- You can authorize up to 100 projects.
|
||||
|
||||
All CI/CD jobs now include a `KUBECONFIG` with contexts for every shared agent connection.
|
||||
Choose the context to run `kubectl` commands from your CI/CD scripts.
|
||||
|
||||
### Authorize the agent to access projects in your groups
|
||||
|
||||
> [Introduced](https://gitlab.com/groups/gitlab-org/-/epics/5784) in GitLab 14.3.
|
||||
|
||||
To authorize the agent to access all of the GitLab projects in a group or subgroup:
|
||||
|
||||
1. On the top bar, select **Menu > Projects** and find the project that contains the agent configuration file (`config.yaml`).
|
||||
1. Edit the file. Under the `ci_access` keyword, add the `groups` attribute.
|
||||
1. For the `id`, add the path:
|
||||
|
||||
```yaml
|
||||
ci_access:
|
||||
groups:
|
||||
- id: path/to/group/subgroup
|
||||
```
|
||||
|
||||
- The Kubernetes projects must be in the same group hierarchy as the project where the agent's configuration is.
|
||||
- You can install additional agents into the same cluster to accommodate additional hierarchies.
|
||||
- All of the subgroups of an authorized group also have access to the same agent (without being specified individually).
|
||||
- You can authorize up to 100 groups.
|
||||
|
||||
All the projects that belong to the group and its subgroups are now authorized to access the agent.
|
||||
All CI/CD jobs now include a `KUBECONFIG` with contexts for every shared agent connection.
|
||||
Choose the context to run `kubectl` commands from your CI/CD scripts.
|
||||
|
||||
## Update your `.gitlab-ci.yml` file to run `kubectl` commands
|
||||
|
||||
In the project where you want to run Kubernetes commands, edit your project's `.gitlab-ci.yml` file.
|
||||
|
||||
In the first command under the `script` keyword, set your agent's context.
|
||||
Use the format `path/to/agent/repository:agent-name`. For example:
|
||||
|
||||
```yaml
|
||||
deploy:
|
||||
image:
|
||||
name: bitnami/kubectl:latest
|
||||
entrypoint: [""]
|
||||
script:
|
||||
- kubectl config get-contexts
|
||||
- kubectl config use-context path/to/agent/repository:agent-name
|
||||
- kubectl get pods
|
||||
```
|
||||
|
||||
If you are not sure what your agent's context is, open a terminal and connect to your cluster.
|
||||
Run `kubectl config get-contexts`.
|
||||
|
||||
### Environments with both certificate-based and agent-based connections
|
||||
|
||||
When you deploy to an environment that has both a [certificate-based
|
||||
cluster](../../infrastructure/clusters/index.md) (deprecated) and an agent connection:
|
||||
|
||||
- The certificate-based cluster's context is called `gitlab-deploy`. This context
|
||||
is always selected by default.
|
||||
- In GitLab 14.9 and later, agent contexts are included in the
|
||||
`KUBECONFIG`. You can select them by using `kubectl config use-context
|
||||
path/to/agent/repository:agent-name`.
|
||||
- In GitLab 14.8 and earlier, you can still use agent connections, but for environments that
|
||||
already have a certificate-based cluster, the agent connections are not included in the `KUBECONFIG`.
|
||||
|
||||
To use an agent connection when certificate-based connections are present, you can manually configure a new `kubectl`
|
||||
configuration context. For example:
|
||||
|
||||
```yaml
|
||||
deploy:
|
||||
variables:
|
||||
KUBE_CONTEXT: my-context # The name to use for the new context
|
||||
AGENT_ID: 1234 # replace with your agent's numeric ID
|
||||
K8S_PROXY_URL: wss://kas.gitlab.com/k8s-proxy/ # replace with your agent server (KAS) Kubernetes proxy URL
|
||||
# ... any other variables you have configured
|
||||
before_script:
|
||||
- kubectl config set-credentials agent:$AGENT_ID --token="ci:${AGENT_ID}:${CI_JOB_TOKEN}"
|
||||
- kubectl config set-cluster gitlab --server="${K8S_PROXY_URL}"
|
||||
- kubectl config set-context "$KUBE_CONTEXT" --cluster=gitlab --user="agent:${AGENT_ID}"
|
||||
- kubectl config use-context "$KUBE_CONTEXT"
|
||||
# ... rest of your job configuration
|
||||
```
|
||||
|
||||
## Use impersonation to restrict project and group access **(PREMIUM)**
|
||||
|
||||
> [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/345014) in GitLab 14.5.
|
||||
|
||||
By default, your CI/CD job inherits all the permissions from the service account used to install the
|
||||
agent in the cluster.
|
||||
To restrict access to your cluster, you can use [impersonation](https://kubernetes.io/docs/reference/access-authn-authz/authentication/#user-impersonation).
|
||||
|
||||
To specify impersonations, use the `access_as` attribute in your agent configuration file and use Kubernetes RBAC rules to manage impersonated account permissions.
|
||||
|
||||
You can impersonate:
|
||||
|
||||
- The agent itself (default).
|
||||
- The CI/CD job that accesses the cluster.
|
||||
- A specific user or system account defined within the cluster.
|
||||
|
||||
### Impersonate the agent
|
||||
|
||||
The agent is impersonated by default. You don't need to do anything to impersonate it.
|
||||
|
||||
### Impersonate the CI/CD job that accesses the cluster
|
||||
|
||||
To impersonate the CI/CD job that accesses the cluster, under the `access_as` key, add the `ci_job: {}` key-value.
|
||||
|
||||
When the agent makes the request to the actual Kubernetes API, it sets the
|
||||
impersonation credentials in the following way:
|
||||
|
||||
- `UserName` is set to `gitlab:ci_job:<job id>`. Example: `gitlab:ci_job:1074499489`.
|
||||
- `Groups` is set to:
|
||||
- `gitlab:ci_job` to identify all requests coming from CI jobs.
|
||||
- The list of IDs of groups the project is in.
|
||||
- The project ID.
|
||||
- The slug of the environment this job belongs to.
|
||||
|
||||
Example: for a CI job in `group1/group1-1/project1` where:
|
||||
|
||||
- Group `group1` has ID 23.
|
||||
- Group `group1/group1-1` has ID 25.
|
||||
- Project `group1/group1-1/project1` has ID 150.
|
||||
- Job running in a prod environment.
|
||||
|
||||
Group list would be `[gitlab:ci_job, gitlab:group:23, gitlab:group:25, gitlab:project:150, gitlab:project_env:150:prod]`.
|
||||
|
||||
- `Extra` carries extra information about the request. The following properties are set on the impersonated identity:
|
||||
|
||||
| Property | Description |
|
||||
| -------- | ----------- |
|
||||
| `agent.gitlab.com/id` | Contains the agent ID. |
|
||||
| `agent.gitlab.com/config_project_id` | Contains the agent's configuration project ID. |
|
||||
| `agent.gitlab.com/project_id` | Contains the CI project ID. |
|
||||
| `agent.gitlab.com/ci_pipeline_id` | Contains the CI pipeline ID. |
|
||||
| `agent.gitlab.com/ci_job_id` | Contains the CI job ID. |
|
||||
| `agent.gitlab.com/username` | Contains the username of the user the CI job is running as. |
|
||||
| `agent.gitlab.com/environment_slug` | Contains the slug of the environment. Only set if running in an environment. |
|
||||
|
||||
Example to restrict access by the CI/CD job's identity:
|
||||
|
||||
```yaml
|
||||
ci_access:
|
||||
projects:
|
||||
- id: path/to/project
|
||||
access_as:
|
||||
ci_job: {}
|
||||
```
|
||||
|
||||
### Impersonate a static identity
|
||||
|
||||
For a given connection, you can use a static identity for the impersonation.
|
||||
|
||||
Under the `access_as` key, add the `impersonate` key to make the request using the provided identity.
|
||||
|
||||
The identity can be specified with the following keys:
|
||||
|
||||
- `username` (required)
|
||||
- `uid`
|
||||
- `groups`
|
||||
- `extra`
|
||||
|
||||
See the [official Kubernetes documentation for details](https://kubernetes.io/docs/reference/access-authn-authz/authentication/#user-impersonation).
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
### `kubectl` commands not supported
|
||||
|
||||
The commands `kubectl exec`, `kubectl cp`, and `kubectl attach` are not supported.
|
||||
Anything that uses these API endpoints does not work, because they use the deprecated
|
||||
SPDY protocol.
|
||||
[An issue exists](https://gitlab.com/gitlab-org/gitlab/-/issues/346248) to add support for these commands.
|
||||
|
||||
### Grant write permissions to `~/.kube/cache`
|
||||
|
||||
Tools like `kubectl`, Helm, `kpt`, and `kustomize` cache information about
|
||||
the cluster in `~/.kube/cache`. If this directory is not writable, the tool fetches information on each invocation,
|
||||
making interactions slower and creating unnecessary load on the cluster. For the best experience, in the
|
||||
image you use in your .`gitlab-ci.yml` file, ensure this directory is writable.
|
||||
|
||||
### Enable TLS
|
||||
|
||||
If you are on a self-managed GitLab instance, ensure your instance is configured with Transport Layer Security (TLS).
|
||||
|
||||
If you attempt to use `kubectl` without TLS, you might get an error like:
|
||||
|
||||
```shell
|
||||
$ kubectl get pods
|
||||
error: You must be logged in to the server (the server has asked for the client to provide credentials)
|
||||
```
|
||||
<!-- This redirect file can be deleted after <2022-07-20>. -->
|
||||
<!-- Redirects that point to other docs in the same project expire in three months. -->
|
||||
<!-- Redirects that point to docs in a different project or site (for example, link is not relative and starts with `https:`) expire in one year. -->
|
||||
<!-- Before deletion, see: https://docs.gitlab.com/ee/development/documentation/redirects.html -->
|
|
@ -0,0 +1,261 @@
|
|||
---
|
||||
stage: Configure
|
||||
group: Configure
|
||||
info: To determine the technical writer assigned to the Stage/Group associated with this page, see https://about.gitlab.com/handbook/engineering/ux/technical-writing/#assignments
|
||||
---
|
||||
|
||||
# Using a GitLab CI/CD workflow for Kubernetes **(FREE)**
|
||||
|
||||
> - [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/327409) in GitLab 14.1.
|
||||
> - The pre-configured `KUBECONFIG` was [introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/324275) in GitLab 14.2.
|
||||
> - [Introduced](https://gitlab.com/groups/gitlab-org/-/epics/5784) the `ci_access` attribute in GitLab 14.3.
|
||||
> - The ability to authorize groups was [introduced](https://gitlab.com/groups/gitlab-org/-/epics/5784) in GitLab 14.3.
|
||||
> - [Moved](https://gitlab.com/groups/gitlab-org/-/epics/6290) to GitLab Free in 14.5.
|
||||
> - Support for Omnibus installations was [introduced](https://gitlab.com/gitlab-org/omnibus-gitlab/-/merge_requests/5686) in GitLab 14.5.
|
||||
> - The ability to switch between certificate-based clusters and agents was [introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/335089) in GitLab 14.9. The certificate-based cluster context is always called `gitlab-deploy`.
|
||||
|
||||
You can use a GitLab CI/CD workflow to safely deploy to and update your Kubernetes clusters.
|
||||
|
||||
To do so, you must first [install an agent in your cluster](install/index.md). When done, you have a Kubernetes context and can
|
||||
run Kubernetes API commands in your GitLab CI/CD pipeline.
|
||||
|
||||
To ensure access to your cluster is safe:
|
||||
|
||||
- Each agent has a separate context (`kubecontext`).
|
||||
- Only the project where the agent is configured, and any additional projects you authorize, can access the agent in your cluster.
|
||||
|
||||
You do not need to have a runner in the cluster with the agent.
|
||||
|
||||
## GitLab CI/CD workflow steps
|
||||
|
||||
To update a Kubernetes cluster by using GitLab CI/CD, complete the following steps.
|
||||
|
||||
1. Ensure you have a working Kubernetes cluster and the manifests are in a GitLab project.
|
||||
1. In the same GitLab project, [register and install the GitLab agent](install/index.md).
|
||||
1. [Update your `.gitlab-ci.yml` file](#update-your-gitlab-ciyml-file-to-run-kubectl-commands) to
|
||||
select the agent's Kubernetes context and run the Kubernetes API commands.
|
||||
1. Run your pipeline to deploy to or update the cluster.
|
||||
|
||||
If you have multiple GitLab projects that contain Kubernetes manifests:
|
||||
|
||||
1. [Install the GitLab agent](install/index.md) in its own project, or in one of the
|
||||
GitLab projects where you keep Kubernetes manifests.
|
||||
1. [Authorize the agent](#authorize-the-agent) to access your GitLab projects.
|
||||
1. Optional. For added security, [use impersonation](#use-impersonation-to-restrict-project-and-group-access).
|
||||
1. [Update your `.gitlab-ci.yml` file](#update-your-gitlab-ciyml-file-to-run-kubectl-commands) to
|
||||
select the agent's Kubernetes context and run the Kubernetes API commands.
|
||||
1. Run your pipeline to deploy to or update the cluster.
|
||||
|
||||
## Authorize the agent
|
||||
|
||||
You must authorize the agent to access the project where you keep your Kubernetes manifests.
|
||||
You can authorize the agent to access individual projects, or authorize a group or subgroup,
|
||||
so all projects within have access. For added security, you can also
|
||||
[use impersonation](#use-impersonation-to-restrict-project-and-group-access).
|
||||
|
||||
### Authorize the agent to access your projects
|
||||
|
||||
> [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/327850) in GitLab 14.4.
|
||||
|
||||
To authorize the agent to access the GitLab project where you keep Kubernetes manifests:
|
||||
|
||||
1. On the top bar, select **Menu > Projects** and find the project that contains the agent configuration file (`config.yaml`).
|
||||
1. Edit the file. Under the `ci_access` keyword, add the `projects` attribute.
|
||||
1. For the `id`, add the path:
|
||||
|
||||
```yaml
|
||||
ci_access:
|
||||
projects:
|
||||
- id: path/to/project
|
||||
```
|
||||
|
||||
- The Kubernetes projects must be in the same group hierarchy as the project where the agent's configuration is.
|
||||
- You can install additional agents into the same cluster to accommodate additional hierarchies.
|
||||
- You can authorize up to 100 projects.
|
||||
|
||||
All CI/CD jobs now include a `KUBECONFIG` with contexts for every shared agent connection.
|
||||
Choose the context to run `kubectl` commands from your CI/CD scripts.
|
||||
|
||||
### Authorize the agent to access projects in your groups
|
||||
|
||||
> [Introduced](https://gitlab.com/groups/gitlab-org/-/epics/5784) in GitLab 14.3.
|
||||
|
||||
To authorize the agent to access all of the GitLab projects in a group or subgroup:
|
||||
|
||||
1. On the top bar, select **Menu > Projects** and find the project that contains the agent configuration file (`config.yaml`).
|
||||
1. Edit the file. Under the `ci_access` keyword, add the `groups` attribute.
|
||||
1. For the `id`, add the path:
|
||||
|
||||
```yaml
|
||||
ci_access:
|
||||
groups:
|
||||
- id: path/to/group/subgroup
|
||||
```
|
||||
|
||||
- The Kubernetes projects must be in the same group hierarchy as the project where the agent's configuration is.
|
||||
- You can install additional agents into the same cluster to accommodate additional hierarchies.
|
||||
- All of the subgroups of an authorized group also have access to the same agent (without being specified individually).
|
||||
- You can authorize up to 100 groups.
|
||||
|
||||
All the projects that belong to the group and its subgroups are now authorized to access the agent.
|
||||
All CI/CD jobs now include a `KUBECONFIG` with contexts for every shared agent connection.
|
||||
Choose the context to run `kubectl` commands from your CI/CD scripts.
|
||||
|
||||
## Update your `.gitlab-ci.yml` file to run `kubectl` commands
|
||||
|
||||
In the project where you want to run Kubernetes commands, edit your project's `.gitlab-ci.yml` file.
|
||||
|
||||
In the first command under the `script` keyword, set your agent's context.
|
||||
Use the format `path/to/agent/repository:agent-name`. For example:
|
||||
|
||||
```yaml
|
||||
deploy:
|
||||
image:
|
||||
name: bitnami/kubectl:latest
|
||||
entrypoint: [""]
|
||||
script:
|
||||
- kubectl config get-contexts
|
||||
- kubectl config use-context path/to/agent/repository:agent-name
|
||||
- kubectl get pods
|
||||
```
|
||||
|
||||
If you are not sure what your agent's context is, open a terminal and connect to your cluster.
|
||||
Run `kubectl config get-contexts`.
|
||||
|
||||
### Environments with both certificate-based and agent-based connections
|
||||
|
||||
When you deploy to an environment that has both a [certificate-based
|
||||
cluster](../../infrastructure/clusters/index.md) (deprecated) and an agent connection:
|
||||
|
||||
- The certificate-based cluster's context is called `gitlab-deploy`. This context
|
||||
is always selected by default.
|
||||
- In GitLab 14.9 and later, agent contexts are included in the
|
||||
`KUBECONFIG`. You can select them by using `kubectl config use-context
|
||||
path/to/agent/repository:agent-name`.
|
||||
- In GitLab 14.8 and earlier, you can still use agent connections, but for environments that
|
||||
already have a certificate-based cluster, the agent connections are not included in the `KUBECONFIG`.
|
||||
|
||||
To use an agent connection when certificate-based connections are present, you can manually configure a new `kubectl`
|
||||
configuration context. For example:
|
||||
|
||||
```yaml
|
||||
deploy:
|
||||
variables:
|
||||
KUBE_CONTEXT: my-context # The name to use for the new context
|
||||
AGENT_ID: 1234 # replace with your agent's numeric ID
|
||||
K8S_PROXY_URL: wss://kas.gitlab.com/k8s-proxy/ # replace with your agent server (KAS) Kubernetes proxy URL
|
||||
# ... any other variables you have configured
|
||||
before_script:
|
||||
- kubectl config set-credentials agent:$AGENT_ID --token="ci:${AGENT_ID}:${CI_JOB_TOKEN}"
|
||||
- kubectl config set-cluster gitlab --server="${K8S_PROXY_URL}"
|
||||
- kubectl config set-context "$KUBE_CONTEXT" --cluster=gitlab --user="agent:${AGENT_ID}"
|
||||
- kubectl config use-context "$KUBE_CONTEXT"
|
||||
# ... rest of your job configuration
|
||||
```
|
||||
|
||||
## Use impersonation to restrict project and group access **(PREMIUM)**
|
||||
|
||||
> [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/345014) in GitLab 14.5.
|
||||
|
||||
By default, your CI/CD job inherits all the permissions from the service account used to install the
|
||||
agent in the cluster.
|
||||
To restrict access to your cluster, you can use [impersonation](https://kubernetes.io/docs/reference/access-authn-authz/authentication/#user-impersonation).
|
||||
|
||||
To specify impersonations, use the `access_as` attribute in your agent configuration file and use Kubernetes RBAC rules to manage impersonated account permissions.
|
||||
|
||||
You can impersonate:
|
||||
|
||||
- The agent itself (default).
|
||||
- The CI/CD job that accesses the cluster.
|
||||
- A specific user or system account defined within the cluster.
|
||||
|
||||
### Impersonate the agent
|
||||
|
||||
The agent is impersonated by default. You don't need to do anything to impersonate it.
|
||||
|
||||
### Impersonate the CI/CD job that accesses the cluster
|
||||
|
||||
To impersonate the CI/CD job that accesses the cluster, under the `access_as` key, add the `ci_job: {}` key-value.
|
||||
|
||||
When the agent makes the request to the actual Kubernetes API, it sets the
|
||||
impersonation credentials in the following way:
|
||||
|
||||
- `UserName` is set to `gitlab:ci_job:<job id>`. Example: `gitlab:ci_job:1074499489`.
|
||||
- `Groups` is set to:
|
||||
- `gitlab:ci_job` to identify all requests coming from CI jobs.
|
||||
- The list of IDs of groups the project is in.
|
||||
- The project ID.
|
||||
- The slug of the environment this job belongs to.
|
||||
|
||||
Example: for a CI job in `group1/group1-1/project1` where:
|
||||
|
||||
- Group `group1` has ID 23.
|
||||
- Group `group1/group1-1` has ID 25.
|
||||
- Project `group1/group1-1/project1` has ID 150.
|
||||
- Job running in a prod environment.
|
||||
|
||||
Group list would be `[gitlab:ci_job, gitlab:group:23, gitlab:group:25, gitlab:project:150, gitlab:project_env:150:prod]`.
|
||||
|
||||
- `Extra` carries extra information about the request. The following properties are set on the impersonated identity:
|
||||
|
||||
| Property | Description |
|
||||
| -------- | ----------- |
|
||||
| `agent.gitlab.com/id` | Contains the agent ID. |
|
||||
| `agent.gitlab.com/config_project_id` | Contains the agent's configuration project ID. |
|
||||
| `agent.gitlab.com/project_id` | Contains the CI project ID. |
|
||||
| `agent.gitlab.com/ci_pipeline_id` | Contains the CI pipeline ID. |
|
||||
| `agent.gitlab.com/ci_job_id` | Contains the CI job ID. |
|
||||
| `agent.gitlab.com/username` | Contains the username of the user the CI job is running as. |
|
||||
| `agent.gitlab.com/environment_slug` | Contains the slug of the environment. Only set if running in an environment. |
|
||||
|
||||
Example to restrict access by the CI/CD job's identity:
|
||||
|
||||
```yaml
|
||||
ci_access:
|
||||
projects:
|
||||
- id: path/to/project
|
||||
access_as:
|
||||
ci_job: {}
|
||||
```
|
||||
|
||||
### Impersonate a static identity
|
||||
|
||||
For a given connection, you can use a static identity for the impersonation.
|
||||
|
||||
Under the `access_as` key, add the `impersonate` key to make the request using the provided identity.
|
||||
|
||||
The identity can be specified with the following keys:
|
||||
|
||||
- `username` (required)
|
||||
- `uid`
|
||||
- `groups`
|
||||
- `extra`
|
||||
|
||||
See the [official Kubernetes documentation for details](https://kubernetes.io/docs/reference/access-authn-authz/authentication/#user-impersonation).
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
### `kubectl` commands not supported
|
||||
|
||||
The commands `kubectl exec`, `kubectl cp`, and `kubectl attach` are not supported.
|
||||
Anything that uses these API endpoints does not work, because they use the deprecated
|
||||
SPDY protocol.
|
||||
[An issue exists](https://gitlab.com/gitlab-org/gitlab/-/issues/346248) to add support for these commands.
|
||||
|
||||
### Grant write permissions to `~/.kube/cache`
|
||||
|
||||
Tools like `kubectl`, Helm, `kpt`, and `kustomize` cache information about
|
||||
the cluster in `~/.kube/cache`. If this directory is not writable, the tool fetches information on each invocation,
|
||||
making interactions slower and creating unnecessary load on the cluster. For the best experience, in the
|
||||
image you use in your .`gitlab-ci.yml` file, ensure this directory is writable.
|
||||
|
||||
### Enable TLS
|
||||
|
||||
If you are on a self-managed GitLab instance, ensure your instance is configured with Transport Layer Security (TLS).
|
||||
|
||||
If you attempt to use `kubectl` without TLS, you might get an error like:
|
||||
|
||||
```shell
|
||||
$ kubectl get pods
|
||||
error: You must be logged in to the server (the server has asked for the client to provide credentials)
|
||||
```
|
|
@ -35,7 +35,7 @@ In a [**GitOps** workflow](gitops.md), you keep your Kubernetes manifests in Git
|
|||
any time you update your manifests, the agent updates the cluster. This workflow is fully driven with Git and is considered pull-based,
|
||||
because the cluster is pulling updates from your GitLab repository.
|
||||
|
||||
In a [**CI/CD** workflow](ci_cd_tunnel.md), you use GitLab CI/CD to query and update your cluster by using the Kubernetes API.
|
||||
In a [**CI/CD** workflow](ci_cd_workflow.md), you use GitLab CI/CD to query and update your cluster by using the Kubernetes API.
|
||||
This workflow is considered push-based, because GitLab is pushing requests from GitLab CI/CD to your cluster.
|
||||
|
||||
## Supported cluster versions
|
||||
|
@ -65,7 +65,7 @@ Read about how to [migrate to the agent for Kubernetes](../../infrastructure/clu
|
|||
## Related topics
|
||||
|
||||
- [GitOps workflow](gitops.md)
|
||||
- [GitLab CI/CD workflow](ci_cd_tunnel.md)
|
||||
- [GitLab CI/CD workflow](ci_cd_workflow.md)
|
||||
- [Install the agent](install/index.md)
|
||||
- [Work with the agent](repository.md)
|
||||
- [Troubleshooting](troubleshooting.md)
|
||||
|
|
|
@ -44,7 +44,7 @@ In GitLab 14.10, a [flag](../../../../administration/feature_flags.md) named `ce
|
|||
|
||||
Prerequisites:
|
||||
|
||||
- For a [GitLab CI/CD workflow](../ci_cd_tunnel.md), ensure that
|
||||
- For a [GitLab CI/CD workflow](../ci_cd_workflow.md), ensure that
|
||||
[GitLab CI/CD is enabled](../../../../ci/enable_or_disable_ci.md#enable-cicd-in-a-project).
|
||||
|
||||
To register an agent with GitLab:
|
||||
|
@ -68,7 +68,7 @@ The agent is configured through a configuration file. This file is optional. Wit
|
|||
You need a configuration file if:
|
||||
|
||||
- You want to use [a GitOps workflow](../gitops.md#gitops-configuration-reference).
|
||||
- You want to authorize a different project to use the agent for a [GitLab CI/CD workflow](../ci_cd_tunnel.md#authorize-the-agent).
|
||||
- You want to authorize a different project to use the agent for a [GitLab CI/CD workflow](../ci_cd_workflow.md#authorize-the-agent).
|
||||
|
||||
To create an agent configuration file, go to the GitLab project. In the repository, create a file called `config.yaml` at this path:
|
||||
|
||||
|
|
|
@ -32,7 +32,7 @@ If you have already configured the agent and connected a cluster with GitLab:
|
|||
|
||||
1. [Create a project from the cluster management project template](#create-a-project-based-on-the-cluster-management-project-template).
|
||||
1. In the project where you configured your agent,
|
||||
[grant the agent access to the new project](agent/ci_cd_tunnel.md#authorize-the-agent).
|
||||
[grant the agent access to the new project](agent/ci_cd_workflow.md#authorize-the-agent).
|
||||
1. In the new project, create an
|
||||
[environment variable](../../ci/variables/index.md#add-a-cicd-variable-to-a-project) named `$KUBE_CONTEXT`
|
||||
and set the value to `path/to/agent-configuration-project:your-agent-name`.
|
||||
|
|
|
@ -67,5 +67,5 @@ The concept of [project-level](../../project/clusters/index.md),
|
|||
[instance-level](../../instance/clusters/index.md) clusters becomes
|
||||
extinct in the new model, although the functionality remains to some extent.
|
||||
|
||||
The agent is always configured in a single GitLab project and you can expose the cluster connection to other projects and groups to [access it from GitLab CI/CD](../../clusters/agent/ci_cd_tunnel.md).
|
||||
The agent is always configured in a single GitLab project and you can expose the cluster connection to other projects and groups to [access it from GitLab CI/CD](../../clusters/agent/ci_cd_workflow.md).
|
||||
By doing so, you are granting these projects and groups access to the same cluster, which is similar to group-level clusters' use case.
|
||||
|
|
|
@ -9,7 +9,7 @@ info: To determine the technical writer assigned to the Stage/Group associated w
|
|||
To connect your Kubernetes cluster with GitLab, you can use:
|
||||
|
||||
- [A GitOps workflow](../../clusters/agent/gitops.md).
|
||||
- [A GitLab CI/CD workflow](../../clusters/agent/ci_cd_tunnel.md).
|
||||
- [A GitLab CI/CD workflow](../../clusters/agent/ci_cd_workflow.md).
|
||||
- [A certificate-based integration](index.md).
|
||||
|
||||
The certificate-based integration is
|
||||
|
@ -23,7 +23,7 @@ in GitLab 14.5. The removal dates are:
|
|||
If you are using the certificate-based integration, you should move to another workflow as soon as possible.
|
||||
|
||||
As a general rule, to migrate clusters that rely on GitLab CI/CD,
|
||||
you can use the [CI/CD workflow](../../clusters/agent/ci_cd_tunnel.md).
|
||||
you can use the [CI/CD workflow](../../clusters/agent/ci_cd_workflow.md).
|
||||
This workflow uses an agent to connect to your cluster. The agent:
|
||||
|
||||
- Is not exposed to the internet.
|
||||
|
@ -41,7 +41,7 @@ Some features are currently available only when using certificate-based integrat
|
|||
With GitLab-managed clusters, GitLab creates separate service accounts and namespaces
|
||||
for every branch and deploys by using these resources.
|
||||
|
||||
The GitLab agent uses [impersonation](../../clusters/agent/ci_cd_tunnel.md#use-impersonation-to-restrict-project-and-group-access)
|
||||
The GitLab agent uses [impersonation](../../clusters/agent/ci_cd_workflow.md#use-impersonation-to-restrict-project-and-group-access)
|
||||
strategies to deploy to your cluster with restricted account access. To do so:
|
||||
|
||||
1. Choose the impersonation strategy that suits your needs.
|
||||
|
@ -92,7 +92,7 @@ For an example, [view this project](https://gitlab.com/gitlab-examples/ops/gitop
|
|||
|
||||
### Migrate generic deployments
|
||||
|
||||
Follow the process for the [CI/CD workflow](../../clusters/agent/ci_cd_tunnel.md).
|
||||
Follow the process for the [CI/CD workflow](../../clusters/agent/ci_cd_workflow.md).
|
||||
|
||||
## Migrate from GitLab Managed applications
|
||||
|
||||
|
|
|
@ -11,7 +11,7 @@ info: To determine the technical writer assigned to the Stage/Group associated w
|
|||
WARNING:
|
||||
This feature was [deprecated](https://gitlab.com/groups/gitlab-org/configure/-/epics/8) in GitLab 14.5.
|
||||
To connect your cluster to GitLab, use the [GitLab agent](../../clusters/agent/index.md).
|
||||
To deploy with the agent, use the [CI/CD workflow](../../clusters/agent/ci_cd_tunnel.md).
|
||||
To deploy with the agent, use the [CI/CD workflow](../../clusters/agent/ci_cd_workflow.md).
|
||||
|
||||
A Kubernetes cluster can be the destination for a deployment job. If
|
||||
|
||||
|
|
|
@ -52,7 +52,7 @@ self-managed GitLab instance.
|
|||
pull requests) with matching GitLab users.
|
||||
- If you're importing to a self-managed GitLab instance, you can alternatively use the
|
||||
[GitHub Rake task](../../../administration/raketasks/github_import.md) to import
|
||||
projects without the constraints of a [Sidekiq](../../../development/sidekiq_style_guide.md) worker.
|
||||
projects without the constraints of a [Sidekiq](../../../development/sidekiq/index.md) worker.
|
||||
- If you're importing from GitHub Enterprise to your self-managed GitLab instance:
|
||||
- You must first enable [GitHub integration](../../../integration/github.md).
|
||||
- To import projects from GitHub Enterprise to GitLab.com, use the [Import API](../../../api/import.md).
|
||||
|
|
|
@ -114,6 +114,18 @@ so if you remove more time than already entered, GitLab ignores the subtraction.
|
|||
|
||||
To remove all the time spent at once, use the `/remove_time_spent` [quick action](quick_actions.md).
|
||||
|
||||
### Delete time spent
|
||||
|
||||
> [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/356796) in GitLab 15.0.
|
||||
|
||||
A timelog is a single entry of time spent, either positive or negative.
|
||||
|
||||
Prerequisites:
|
||||
|
||||
- You must be the author of the timelog or have at least the Maintainer role for the project.
|
||||
|
||||
You can [delete timelogs](../../api/graphql/reference/index.md#mutationtimelogdelete) using the GraphQL API.
|
||||
|
||||
## View a time tracking report
|
||||
|
||||
> [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/271409) in GitLab 13.12.
|
||||
|
|
|
@ -6,12 +6,14 @@ module API
|
|||
|
||||
before { authenticate! }
|
||||
|
||||
feature_category :authentication_and_authorization
|
||||
urgency :low
|
||||
|
||||
helpers ::API::Helpers::MembersHelpers
|
||||
|
||||
%w[group project].each do |source_type|
|
||||
{
|
||||
"group" => :subgroups,
|
||||
"project" => :projects
|
||||
}.each do |source_type, feature_category|
|
||||
params do
|
||||
requires :id, type: String, desc: "The #{source_type} ID"
|
||||
end
|
||||
|
@ -27,7 +29,7 @@ module API
|
|||
use :pagination
|
||||
end
|
||||
|
||||
get ":id/members" do
|
||||
get ":id/members", feature_category: feature_category do
|
||||
source = find_source(source_type, params[:id])
|
||||
|
||||
members = paginate(retrieve_members(source, params: params))
|
||||
|
@ -46,7 +48,7 @@ module API
|
|||
use :pagination
|
||||
end
|
||||
|
||||
get ":id/members/all" do
|
||||
get ":id/members/all", feature_category: feature_category do
|
||||
source = find_source(source_type, params[:id])
|
||||
|
||||
members = paginate(retrieve_members(source, params: params, deep: true))
|
||||
|
@ -61,7 +63,7 @@ module API
|
|||
requires :user_id, type: Integer, desc: 'The user ID of the member'
|
||||
end
|
||||
# rubocop: disable CodeReuse/ActiveRecord
|
||||
get ":id/members/:user_id" do
|
||||
get ":id/members/:user_id", feature_category: feature_category do
|
||||
source = find_source(source_type, params[:id])
|
||||
|
||||
members = source_members(source)
|
||||
|
@ -78,7 +80,7 @@ module API
|
|||
requires :user_id, type: Integer, desc: 'The user ID of the member'
|
||||
end
|
||||
# rubocop: disable CodeReuse/ActiveRecord
|
||||
get ":id/members/all/:user_id" do
|
||||
get ":id/members/all/:user_id", feature_category: feature_category do
|
||||
source = find_source(source_type, params[:id])
|
||||
|
||||
members = find_all_members(source)
|
||||
|
@ -100,7 +102,7 @@ module API
|
|||
optional :tasks_project_id, type: Integer, desc: 'The project ID in which to create the task issues'
|
||||
end
|
||||
|
||||
post ":id/members" do
|
||||
post ":id/members", feature_category: feature_category do
|
||||
source = find_source(source_type, params[:id])
|
||||
authorize_admin_source!(source_type, source)
|
||||
|
||||
|
@ -123,7 +125,7 @@ module API
|
|||
optional :expires_at, type: DateTime, desc: 'Date string in the format YEAR-MONTH-DAY'
|
||||
end
|
||||
# rubocop: disable CodeReuse/ActiveRecord
|
||||
put ":id/members/:user_id" do
|
||||
put ":id/members/:user_id", feature_category: feature_category do
|
||||
source = find_source(source_type, params.delete(:id))
|
||||
authorize_admin_source!(source_type, source)
|
||||
|
||||
|
@ -152,7 +154,7 @@ module API
|
|||
desc: 'Flag indicating if the removed member should be unassigned from any issues or merge requests within given group or project'
|
||||
end
|
||||
# rubocop: disable CodeReuse/ActiveRecord
|
||||
delete ":id/members/:user_id" do
|
||||
delete ":id/members/:user_id", feature_category: feature_category do
|
||||
source = find_source(source_type, params[:id])
|
||||
member = source_members(source).find_by!(user_id: params[:user_id])
|
||||
|
||||
|
|
|
@ -729,7 +729,7 @@ module API
|
|||
params do
|
||||
requires :id, type: String, desc: 'ID of a project'
|
||||
end
|
||||
get ':id/storage', feature_category: :projects do
|
||||
get ':id/storage', feature_category: :source_code_management do
|
||||
authenticated_as_admin!
|
||||
|
||||
present user_project, with: Entities::ProjectRepositoryStorage, current_user: current_user
|
||||
|
|
|
@ -5,24 +5,24 @@ module Gitlab
|
|||
# A job to nullify duplicate runners_token_encrypted values in projects table in batches
|
||||
class ResetDuplicateCiRunnersTokenEncryptedValuesOnProjects
|
||||
class Project < ActiveRecord::Base # rubocop:disable Style/Documentation
|
||||
include ::EachBatch
|
||||
include EachBatch
|
||||
|
||||
self.table_name = 'projects'
|
||||
|
||||
scope :base_query, -> do
|
||||
where.not(runners_token_encrypted: nil)
|
||||
end
|
||||
scope :base_query, -> { where.not(runners_token_encrypted: nil) }
|
||||
end
|
||||
|
||||
def perform(start_id, end_id)
|
||||
# Reset duplicate runner tokens that would prevent creating an unique index.
|
||||
batch_records = Project.base_query.where(id: start_id..end_id)
|
||||
|
||||
duplicate_tokens = Project.base_query
|
||||
.where(id: start_id..end_id)
|
||||
.where(runners_token_encrypted: batch_records.select(:runners_token_encrypted).distinct)
|
||||
.group(:runners_token_encrypted)
|
||||
.having('COUNT(*) > 1')
|
||||
.pluck(:runners_token_encrypted)
|
||||
|
||||
Project.where(runners_token_encrypted: duplicate_tokens).update_all(runners_token_encrypted: nil) if duplicate_tokens.any?
|
||||
batch_records.where(runners_token_encrypted: duplicate_tokens).update_all(runners_token_encrypted: nil) if duplicate_tokens.any?
|
||||
|
||||
mark_job_as_succeeded(start_id, end_id)
|
||||
end
|
||||
|
@ -30,7 +30,10 @@ module Gitlab
|
|||
private
|
||||
|
||||
def mark_job_as_succeeded(*arguments)
|
||||
Gitlab::Database::BackgroundMigrationJob.mark_all_as_succeeded('ResetDuplicateCiRunnersTokenEncryptedValuesOnProjects', arguments)
|
||||
::Gitlab::Database::BackgroundMigrationJob.mark_all_as_succeeded(
|
||||
self.class.name.demodulize,
|
||||
arguments
|
||||
)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -5,24 +5,24 @@ module Gitlab
|
|||
# A job to nullify duplicate ci_runners_token values in projects table in batches
|
||||
class ResetDuplicateCiRunnersTokenValuesOnProjects
|
||||
class Project < ActiveRecord::Base # rubocop:disable Style/Documentation
|
||||
include ::EachBatch
|
||||
include EachBatch
|
||||
|
||||
self.table_name = 'projects'
|
||||
|
||||
scope :base_query, -> do
|
||||
where.not(runners_token: nil)
|
||||
end
|
||||
scope :base_query, -> { where.not(runners_token: nil) }
|
||||
end
|
||||
|
||||
def perform(start_id, end_id)
|
||||
# Reset duplicate runner tokens that would prevent creating an unique index.
|
||||
batch_records = Project.base_query.where(id: start_id..end_id)
|
||||
|
||||
duplicate_tokens = Project.base_query
|
||||
.where(id: start_id..end_id)
|
||||
.where(runners_token: batch_records.select(:runners_token).distinct)
|
||||
.group(:runners_token)
|
||||
.having('COUNT(*) > 1')
|
||||
.pluck(:runners_token)
|
||||
|
||||
Project.where(runners_token: duplicate_tokens).update_all(runners_token: nil) if duplicate_tokens.any?
|
||||
batch_records.where(runners_token: duplicate_tokens).update_all(runners_token: nil) if duplicate_tokens.any?
|
||||
|
||||
mark_job_as_succeeded(start_id, end_id)
|
||||
end
|
||||
|
@ -30,7 +30,10 @@ module Gitlab
|
|||
private
|
||||
|
||||
def mark_job_as_succeeded(*arguments)
|
||||
Gitlab::Database::BackgroundMigrationJob.mark_all_as_succeeded('ResetDuplicateCiRunnerValuesTokensOnProjects', arguments)
|
||||
::Gitlab::Database::BackgroundMigrationJob.mark_all_as_succeeded(
|
||||
self.class.name.demodulize,
|
||||
arguments
|
||||
)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -57,7 +57,7 @@
|
|||
"@gitlab/at.js": "1.5.7",
|
||||
"@gitlab/favicon-overlay": "2.0.0",
|
||||
"@gitlab/svgs": "2.8.0",
|
||||
"@gitlab/ui": "38.8.1",
|
||||
"@gitlab/ui": "39.0.0",
|
||||
"@gitlab/visual-review-tools": "1.6.1",
|
||||
"@rails/actioncable": "6.1.4-7",
|
||||
"@rails/ujs": "6.1.4-7",
|
||||
|
@ -205,7 +205,7 @@
|
|||
"@babel/plugin-transform-modules-commonjs": "^7.10.1",
|
||||
"@gitlab/eslint-plugin": "12.0.1",
|
||||
"@gitlab/stylelint-config": "4.0.0",
|
||||
"@graphql-eslint/eslint-plugin": "3.0.0",
|
||||
"@graphql-eslint/eslint-plugin": "3.10.2",
|
||||
"@testing-library/dom": "^7.16.2",
|
||||
"@types/jest": "^26.0.24",
|
||||
"@vue/test-utils": "1.3.0",
|
||||
|
|
|
@ -12,7 +12,7 @@ module QA
|
|||
end
|
||||
|
||||
view 'app/assets/javascripts/pipeline_editor/components/commit/commit_form.vue' do
|
||||
element :target_branch_field, required: true
|
||||
element :source_branch_field, required: true
|
||||
end
|
||||
|
||||
view 'app/assets/javascripts/pipeline_editor/components/editor/ci_editor_header.vue' do
|
||||
|
@ -57,8 +57,8 @@ module QA
|
|||
wait_for_requests
|
||||
end
|
||||
|
||||
def target_branch_name
|
||||
find_element(:target_branch_field).value
|
||||
def source_branch_name
|
||||
find_element(:source_branch_field).value
|
||||
end
|
||||
|
||||
def editing_content
|
||||
|
@ -76,8 +76,8 @@ module QA
|
|||
wait_for_requests
|
||||
end
|
||||
|
||||
def set_target_branch(name)
|
||||
find_element(:target_branch_field).fill_in(with: name)
|
||||
def set_source_branch(name)
|
||||
find_element(:source_branch_field).fill_in(with: name)
|
||||
end
|
||||
|
||||
def current_branch
|
||||
|
|
|
@ -73,7 +73,7 @@ module QA
|
|||
show.select_branch_from_dropdown(random_test_string)
|
||||
|
||||
aggregate_failures do
|
||||
expect(show.target_branch_name).to eq(random_test_string), 'Target branch field is not showing expected branch name.'
|
||||
expect(show.source_branch_name).to eq(random_test_string), 'Branch field is not showing expected branch name.'
|
||||
expect(show.editing_content).to have_content(random_test_string), 'Editor content does not include expected test string.'
|
||||
end
|
||||
|
||||
|
@ -81,7 +81,7 @@ module QA
|
|||
show.select_branch_from_dropdown(project.default_branch)
|
||||
|
||||
aggregate_failures do
|
||||
expect(show.target_branch_name).to eq(project.default_branch), 'Target branch field is not showing expected branch name.'
|
||||
expect(show.source_branch_name).to eq(project.default_branch), 'Branch field is not showing expected branch name.'
|
||||
expect(show.editing_content).to have_content(project.default_branch), 'Editor content does not include expected test string.'
|
||||
end
|
||||
end
|
||||
|
|
|
@ -34,8 +34,8 @@ module QA
|
|||
expect(show).to have_no_new_mr_checkbox
|
||||
end
|
||||
|
||||
# The new MR checkbox is visible after a new target branch name is set
|
||||
show.set_target_branch(SecureRandom.hex(10))
|
||||
# The new MR checkbox is visible after a new branch name is set
|
||||
show.set_source_branch(SecureRandom.hex(10))
|
||||
expect(show).to have_new_mr_checkbox
|
||||
|
||||
show.select_new_mr_checkbox
|
||||
|
|
|
@ -53,13 +53,13 @@ module QA
|
|||
it 'creates new pipeline and target branch', testcase: 'https://gitlab.com/gitlab-org/gitlab/-/quality/test_cases/349005' do
|
||||
Page::Project::PipelineEditor::Show.perform do |show|
|
||||
show.write_to_editor(random_test_string)
|
||||
show.set_target_branch(random_test_string)
|
||||
show.set_source_branch(random_test_string)
|
||||
show.submit_changes
|
||||
|
||||
Support::Waiter.wait_until { project.pipelines.size > 1 }
|
||||
|
||||
aggregate_failures do
|
||||
expect(show.target_branch_name).to eq(random_test_string)
|
||||
expect(show.source_branch_name).to eq(random_test_string)
|
||||
expect(show.current_branch).to eq(random_test_string)
|
||||
expect(show.editing_content).to have_content(random_test_string)
|
||||
expect { show.pipeline_id }.to eventually_eq(project.pipelines.pluck(:id).max).within(max_duration: 60, sleep_interval: 3)
|
||||
|
|
|
@ -53,7 +53,7 @@ RSpec.describe 'Pipeline Editor', :js do
|
|||
end
|
||||
|
||||
it 'displays new branch as selected after commiting on a new branch' do
|
||||
find('#target-branch-field').set('new_branch', clear: :backspace)
|
||||
find('#source-branch-field').set('new_branch', clear: :backspace)
|
||||
|
||||
page.within('#source-editor-') do
|
||||
find('textarea').send_keys '123'
|
||||
|
@ -112,7 +112,7 @@ RSpec.describe 'Pipeline Editor', :js do
|
|||
it 'user who creates a MR is taken to the merge request page without warnings' do
|
||||
expect(page).not_to have_content('New merge request')
|
||||
|
||||
find_field('Target Branch').set 'new_branch'
|
||||
find_field('Branch').set 'new_branch'
|
||||
find_field('Start a new merge request with these changes').click
|
||||
|
||||
click_button 'Commit changes'
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||
|
||||
exports[`content_editor/components/toolbar_button displays tertiary, small button with a provided label and icon 1`] = `
|
||||
"<b-button-stub size=\\"sm\\" variant=\\"default\\" type=\\"button\\" tag=\\"button\\" aria-label=\\"Bold\\" title=\\"Bold\\" class=\\"gl-button btn-default-tertiary btn-icon\\">
|
||||
exports[`content_editor/components/toolbar_button displays tertiary, medium button with a provided label and icon 1`] = `
|
||||
"<b-button-stub size=\\"md\\" variant=\\"default\\" type=\\"button\\" tag=\\"button\\" aria-label=\\"Bold\\" title=\\"Bold\\" class=\\"gl-button btn-default-tertiary btn-icon\\">
|
||||
<!---->
|
||||
<gl-icon-stub name=\\"bold\\" size=\\"16\\" class=\\"gl-button-icon\\"></gl-icon-stub>
|
||||
<!---->
|
||||
|
|
|
@ -2,13 +2,13 @@ import { BubbleMenu } from '@tiptap/vue-2';
|
|||
import { GlDropdown, GlDropdownItem, GlSearchBoxByType } from '@gitlab/ui';
|
||||
import Vue from 'vue';
|
||||
import { mountExtended } from 'helpers/vue_test_utils_helper';
|
||||
import CodeBlockBubbleMenu from '~/content_editor/components/code_block_bubble_menu.vue';
|
||||
import CodeBlockBubbleMenu from '~/content_editor/components/bubble_menus/code_block.vue';
|
||||
import eventHubFactory from '~/helpers/event_hub_factory';
|
||||
import CodeBlockHighlight from '~/content_editor/extensions/code_block_highlight';
|
||||
import codeBlockLanguageLoader from '~/content_editor/services/code_block_language_loader';
|
||||
import { createTestEditor, emitEditorEvent } from '../test_utils';
|
||||
import { createTestEditor, emitEditorEvent } from '../../test_utils';
|
||||
|
||||
describe('content_editor/components/code_block_bubble_menu', () => {
|
||||
describe('content_editor/components/bubble_menus/code_block', () => {
|
||||
let wrapper;
|
||||
let tiptapEditor;
|
||||
let bubbleMenu;
|
|
@ -1,15 +1,15 @@
|
|||
import { BubbleMenu } from '@tiptap/vue-2';
|
||||
import { mockTracking } from 'helpers/tracking_helper';
|
||||
import { shallowMountExtended } from 'helpers/vue_test_utils_helper';
|
||||
import FormattingBubbleMenu from '~/content_editor/components/formatting_bubble_menu.vue';
|
||||
import FormattingBubbleMenu from '~/content_editor/components/bubble_menus/formatting.vue';
|
||||
|
||||
import {
|
||||
BUBBLE_MENU_TRACKING_ACTION,
|
||||
CONTENT_EDITOR_TRACKING_LABEL,
|
||||
} from '~/content_editor/constants';
|
||||
import { createTestEditor } from '../test_utils';
|
||||
import { createTestEditor } from '../../test_utils';
|
||||
|
||||
describe('content_editor/components/formatting_bubble_menu', () => {
|
||||
describe('content_editor/components/bubble_menus/formatting', () => {
|
||||
let wrapper;
|
||||
let trackingSpy;
|
||||
let tiptapEditor;
|
|
@ -4,7 +4,7 @@ import ContentEditor from '~/content_editor/components/content_editor.vue';
|
|||
import ContentEditorAlert from '~/content_editor/components/content_editor_alert.vue';
|
||||
import ContentEditorProvider from '~/content_editor/components/content_editor_provider.vue';
|
||||
import EditorStateObserver from '~/content_editor/components/editor_state_observer.vue';
|
||||
import FormattingBubbleMenu from '~/content_editor/components/formatting_bubble_menu.vue';
|
||||
import FormattingBubbleMenu from '~/content_editor/components/bubble_menus/formatting.vue';
|
||||
import TopToolbar from '~/content_editor/components/top_toolbar.vue';
|
||||
import LoadingIndicator from '~/content_editor/components/loading_indicator.vue';
|
||||
import { emitEditorEvent } from '../test_utils';
|
||||
|
|
|
@ -46,7 +46,7 @@ describe('content_editor/components/toolbar_button', () => {
|
|||
wrapper.destroy();
|
||||
});
|
||||
|
||||
it('displays tertiary, small button with a provided label and icon', () => {
|
||||
it('displays tertiary, medium button with a provided label and icon', () => {
|
||||
buildWrapper();
|
||||
|
||||
expect(findButton().html()).toMatchSnapshot();
|
||||
|
|
|
@ -21,6 +21,7 @@ describe('Job Status Token', () => {
|
|||
value: {
|
||||
data: '',
|
||||
},
|
||||
cursorPosition: 'start',
|
||||
};
|
||||
|
||||
const createComponent = () => {
|
||||
|
|
|
@ -11,7 +11,10 @@ describe('TokenWithLoadingState', () => {
|
|||
|
||||
const initWrapper = (props = {}, options) => {
|
||||
wrapper = shallowMount(TokenWithLoadingState, {
|
||||
propsData: props,
|
||||
propsData: {
|
||||
cursorPosition: 'start',
|
||||
...props,
|
||||
},
|
||||
...options,
|
||||
});
|
||||
};
|
||||
|
|
|
@ -11,7 +11,10 @@ describe('packages_filter', () => {
|
|||
|
||||
const mountComponent = ({ attrs, listeners } = {}) => {
|
||||
wrapper = shallowMount(component, {
|
||||
attrs,
|
||||
attrs: {
|
||||
cursorPosition: 'start',
|
||||
...attrs,
|
||||
},
|
||||
listeners,
|
||||
});
|
||||
};
|
||||
|
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue