Add latest changes from gitlab-org/gitlab@master
This commit is contained in:
parent
10213bf3b2
commit
733befe96a
|
@ -24,6 +24,7 @@ const Api = {
|
|||
projectMergeRequestChangesPath: '/api/:version/projects/:id/merge_requests/:mrid/changes',
|
||||
projectMergeRequestVersionsPath: '/api/:version/projects/:id/merge_requests/:mrid/versions',
|
||||
projectRunnersPath: '/api/:version/projects/:id/runners',
|
||||
projectProtectedBranchesPath: '/api/:version/projects/:id/protected_branches',
|
||||
mergeRequestsPath: '/api/:version/merge_requests',
|
||||
groupLabelsPath: '/groups/:namespace_path/-/labels',
|
||||
issuableTemplatePath: '/:namespace_path/:project_path/templates/:type/:key',
|
||||
|
@ -220,6 +221,22 @@ const Api = {
|
|||
return axios.get(url, config);
|
||||
},
|
||||
|
||||
projectProtectedBranches(id, query = '') {
|
||||
const url = Api.buildUrl(Api.projectProtectedBranchesPath).replace(
|
||||
':id',
|
||||
encodeURIComponent(id),
|
||||
);
|
||||
|
||||
return axios
|
||||
.get(url, {
|
||||
params: {
|
||||
search: query,
|
||||
per_page: DEFAULT_PER_PAGE,
|
||||
},
|
||||
})
|
||||
.then(({ data }) => data);
|
||||
},
|
||||
|
||||
mergeRequests(params = {}) {
|
||||
const url = Api.buildUrl(Api.mergeRequestsPath);
|
||||
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
import _ from 'underscore';
|
||||
import { escape as esc } from 'lodash';
|
||||
import axios from '../lib/utils/axios_utils';
|
||||
import { s__ } from '../locale';
|
||||
import Flash from '../flash';
|
||||
|
@ -10,7 +10,7 @@ function generateErrorBoxContent(errors) {
|
|||
const errorList = [].concat(errors).map(
|
||||
errorString => `
|
||||
<li>
|
||||
${_.escape(errorString)}
|
||||
${esc(errorString)}
|
||||
</li>
|
||||
`,
|
||||
);
|
||||
|
|
|
@ -4,6 +4,6 @@ import 'jquery';
|
|||
import 'jquery-ujs';
|
||||
import 'vendor/jquery.endless-scroll';
|
||||
import 'jquery.caret'; // must be imported before at.js
|
||||
import 'at.js';
|
||||
import '@gitlab/at.js';
|
||||
import 'vendor/jquery.scrollTo';
|
||||
import 'jquery.waitforimages';
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
<script>
|
||||
import _ from 'underscore';
|
||||
import { debounce, uniq } from 'lodash';
|
||||
import { mapActions, mapState, mapGetters } from 'vuex';
|
||||
import { GlLoadingIcon } from '@gitlab/ui';
|
||||
import { GlAreaChart } from '@gitlab/ui/dist/charts';
|
||||
|
@ -120,7 +120,7 @@ export default {
|
|||
return this.xAxisRange[this.xAxisRange.length - 1];
|
||||
},
|
||||
charts() {
|
||||
return _.uniq(this.individualCharts);
|
||||
return uniq(this.individualCharts);
|
||||
},
|
||||
},
|
||||
mounted() {
|
||||
|
@ -171,7 +171,7 @@ export default {
|
|||
});
|
||||
})
|
||||
.catch(() => {});
|
||||
this.masterChart.on('datazoom', _.debounce(this.setIndividualChartsZoom, 200));
|
||||
this.masterChart.on('datazoom', debounce(this.setIndividualChartsZoom, 200));
|
||||
},
|
||||
onIndividualChartCreated(chart) {
|
||||
this.individualCharts.push(chart);
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
import $ from 'jquery';
|
||||
import 'at.js';
|
||||
import '@gitlab/at.js';
|
||||
import _ from 'underscore';
|
||||
import SidebarMediator from '~/sidebar/sidebar_mediator';
|
||||
import glRegexp from './lib/utils/regexp';
|
||||
|
|
|
@ -12,6 +12,11 @@ export default {
|
|||
type: Object,
|
||||
required: true,
|
||||
},
|
||||
deploymentCluster: {
|
||||
type: Object,
|
||||
required: false,
|
||||
default: null,
|
||||
},
|
||||
iconStatus: {
|
||||
type: Object,
|
||||
required: true,
|
||||
|
@ -61,14 +66,14 @@ export default {
|
|||
: '';
|
||||
},
|
||||
hasCluster() {
|
||||
return this.hasLastDeployment && this.lastDeployment.cluster;
|
||||
return Boolean(this.deploymentCluster) && Boolean(this.deploymentCluster.name);
|
||||
},
|
||||
clusterNameOrLink() {
|
||||
if (!this.hasCluster) {
|
||||
return '';
|
||||
}
|
||||
|
||||
const { name, path } = this.lastDeployment.cluster;
|
||||
const { name, path } = this.deploymentCluster;
|
||||
const escapedName = _.escape(name);
|
||||
const escapedPath = _.escape(path);
|
||||
|
||||
|
@ -86,6 +91,9 @@ export default {
|
|||
false,
|
||||
);
|
||||
},
|
||||
kubernetesNamespace() {
|
||||
return this.hasCluster ? this.deploymentCluster.kubernetes_namespace : null;
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
deploymentLink(name) {
|
||||
|
@ -109,75 +117,153 @@ export default {
|
|||
);
|
||||
},
|
||||
lastEnvironmentMessage() {
|
||||
const { environmentLink, clusterNameOrLink, hasCluster } = this;
|
||||
|
||||
const message = hasCluster
|
||||
? __('This job is deployed to %{environmentLink} using cluster %{clusterNameOrLink}.')
|
||||
: __('This job is deployed to %{environmentLink}.');
|
||||
|
||||
return sprintf(message, { environmentLink, clusterNameOrLink }, false);
|
||||
},
|
||||
outOfDateEnvironmentMessage() {
|
||||
const { hasLastDeployment, hasCluster, environmentLink, clusterNameOrLink } = this;
|
||||
|
||||
if (hasLastDeployment) {
|
||||
const message = hasCluster
|
||||
? __(
|
||||
'This job is an out-of-date deployment to %{environmentLink} using cluster %{clusterNameOrLink}. View the %{deploymentLink}.',
|
||||
)
|
||||
: __(
|
||||
'This job is an out-of-date deployment to %{environmentLink}. View the %{deploymentLink}.',
|
||||
);
|
||||
|
||||
const { environmentLink, clusterNameOrLink, hasCluster, kubernetesNamespace } = this;
|
||||
if (hasCluster) {
|
||||
if (kubernetesNamespace) {
|
||||
return sprintf(
|
||||
__(
|
||||
'This job is deployed to %{environmentLink} using cluster %{clusterNameOrLink} and namespace %{kubernetesNamespace}.',
|
||||
),
|
||||
{ environmentLink, clusterNameOrLink, kubernetesNamespace },
|
||||
false,
|
||||
);
|
||||
}
|
||||
// we know the cluster but not the namespace
|
||||
return sprintf(
|
||||
message,
|
||||
{
|
||||
environmentLink,
|
||||
clusterNameOrLink,
|
||||
deploymentLink: this.deploymentLink(__('most recent deployment')),
|
||||
},
|
||||
__('This job is deployed to %{environmentLink} using cluster %{clusterNameOrLink}.'),
|
||||
{ environmentLink, clusterNameOrLink },
|
||||
false,
|
||||
);
|
||||
}
|
||||
// not a cluster deployment
|
||||
return sprintf(__('This job is deployed to %{environmentLink}.'), { environmentLink }, false);
|
||||
},
|
||||
outOfDateEnvironmentMessage() {
|
||||
const {
|
||||
hasLastDeployment,
|
||||
hasCluster,
|
||||
environmentLink,
|
||||
clusterNameOrLink,
|
||||
kubernetesNamespace,
|
||||
} = this;
|
||||
|
||||
const message = hasCluster
|
||||
? __(
|
||||
if (hasLastDeployment) {
|
||||
const deploymentLink = this.deploymentLink(__('most recent deployment'));
|
||||
if (hasCluster) {
|
||||
if (kubernetesNamespace) {
|
||||
return sprintf(
|
||||
__(
|
||||
'This job is an out-of-date deployment to %{environmentLink} using cluster %{clusterNameOrLink} and namespace %{kubernetesNamespace}. View the %{deploymentLink}.',
|
||||
),
|
||||
{ environmentLink, clusterNameOrLink, kubernetesNamespace, deploymentLink },
|
||||
false,
|
||||
);
|
||||
}
|
||||
// we know the cluster but not the namespace
|
||||
return sprintf(
|
||||
__(
|
||||
'This job is an out-of-date deployment to %{environmentLink} using cluster %{clusterNameOrLink}. View the %{deploymentLink}.',
|
||||
),
|
||||
{ environmentLink, clusterNameOrLink, deploymentLink },
|
||||
false,
|
||||
);
|
||||
}
|
||||
// not a cluster deployment
|
||||
return sprintf(
|
||||
__(
|
||||
'This job is an out-of-date deployment to %{environmentLink}. View the %{deploymentLink}.',
|
||||
),
|
||||
{ environmentLink, deploymentLink },
|
||||
false,
|
||||
);
|
||||
}
|
||||
// no last deployment, i.e. this is the first deployment
|
||||
if (hasCluster) {
|
||||
if (kubernetesNamespace) {
|
||||
return sprintf(
|
||||
__(
|
||||
'This job is an out-of-date deployment to %{environmentLink} using cluster %{clusterNameOrLink} and namespace %{kubernetesNamespace}.',
|
||||
),
|
||||
{ environmentLink, clusterNameOrLink, kubernetesNamespace },
|
||||
false,
|
||||
);
|
||||
}
|
||||
// we know the cluster but not the namespace
|
||||
return sprintf(
|
||||
__(
|
||||
'This job is an out-of-date deployment to %{environmentLink} using cluster %{clusterNameOrLink}.',
|
||||
)
|
||||
: __('This job is an out-of-date deployment to %{environmentLink}.');
|
||||
|
||||
),
|
||||
{ environmentLink, clusterNameOrLink },
|
||||
false,
|
||||
);
|
||||
}
|
||||
// not a cluster deployment
|
||||
return sprintf(
|
||||
message,
|
||||
{
|
||||
environmentLink,
|
||||
clusterNameOrLink,
|
||||
},
|
||||
__('This job is an out-of-date deployment to %{environmentLink}.'),
|
||||
{ environmentLink },
|
||||
false,
|
||||
);
|
||||
},
|
||||
creatingEnvironmentMessage() {
|
||||
const { hasLastDeployment, hasCluster, environmentLink, clusterNameOrLink } = this;
|
||||
const {
|
||||
hasLastDeployment,
|
||||
hasCluster,
|
||||
environmentLink,
|
||||
clusterNameOrLink,
|
||||
kubernetesNamespace,
|
||||
} = this;
|
||||
|
||||
if (hasLastDeployment) {
|
||||
const message = hasCluster
|
||||
? __(
|
||||
'This job is creating a deployment to %{environmentLink} using cluster %{clusterNameOrLink}. This will overwrite the %{deploymentLink}.',
|
||||
)
|
||||
: __(
|
||||
'This job is creating a deployment to %{environmentLink}. This will overwrite the %{deploymentLink}.',
|
||||
const deploymentLink = this.deploymentLink(__('latest deployment'));
|
||||
if (hasCluster) {
|
||||
if (kubernetesNamespace) {
|
||||
return sprintf(
|
||||
__(
|
||||
'This job is creating a deployment to %{environmentLink} using cluster %{clusterNameOrLink} and namespace %{kubernetesNamespace}. This will overwrite the %{deploymentLink}.',
|
||||
),
|
||||
{ environmentLink, clusterNameOrLink, kubernetesNamespace, deploymentLink },
|
||||
false,
|
||||
);
|
||||
|
||||
}
|
||||
// we know the cluster but not the namespace
|
||||
return sprintf(
|
||||
__(
|
||||
'This job is creating a deployment to %{environmentLink} using cluster %{clusterNameOrLink}. This will overwrite the %{deploymentLink}.',
|
||||
),
|
||||
{ environmentLink, clusterNameOrLink, deploymentLink },
|
||||
false,
|
||||
);
|
||||
}
|
||||
// not a cluster deployment
|
||||
return sprintf(
|
||||
message,
|
||||
{
|
||||
environmentLink,
|
||||
clusterNameOrLink,
|
||||
deploymentLink: this.deploymentLink(__('latest deployment')),
|
||||
},
|
||||
__(
|
||||
'This job is creating a deployment to %{environmentLink}. This will overwrite the %{deploymentLink}.',
|
||||
),
|
||||
{ environmentLink, deploymentLink },
|
||||
false,
|
||||
);
|
||||
}
|
||||
|
||||
// no last deployment, i.e. this is the first deployment
|
||||
if (hasCluster) {
|
||||
if (kubernetesNamespace) {
|
||||
return sprintf(
|
||||
__(
|
||||
'This job is creating a deployment to %{environmentLink} using cluster %{clusterNameOrLink} and namespace %{kubernetesNamespace}.',
|
||||
),
|
||||
{ environmentLink, clusterNameOrLink, kubernetesNamespace },
|
||||
false,
|
||||
);
|
||||
}
|
||||
// we know the cluster but not the namespace
|
||||
return sprintf(
|
||||
__(
|
||||
'This job is creating a deployment to %{environmentLink} using cluster %{clusterNameOrLink}.',
|
||||
),
|
||||
{ environmentLink, clusterNameOrLink },
|
||||
false,
|
||||
);
|
||||
}
|
||||
// not a cluster deployment
|
||||
return sprintf(
|
||||
__('This job is creating a deployment to %{environmentLink}.'),
|
||||
{ environmentLink },
|
||||
|
|
|
@ -256,6 +256,7 @@ export default {
|
|||
v-if="hasEnvironment"
|
||||
class="js-job-environment"
|
||||
:deployment-status="job.deployment_status"
|
||||
:deployment-cluster="job.deployment_cluster"
|
||||
:icon-status="job.status"
|
||||
/>
|
||||
|
||||
|
|
|
@ -15,7 +15,7 @@ import { escape, uniqueId } from 'lodash';
|
|||
import Cookies from 'js-cookie';
|
||||
import Autosize from 'autosize';
|
||||
import 'jquery.caret'; // required by at.js
|
||||
import 'at.js';
|
||||
import '@gitlab/at.js';
|
||||
import Vue from 'vue';
|
||||
import { GlSkeletonLoading } from '@gitlab/ui';
|
||||
import AjaxCache from '~/lib/utils/ajax_cache';
|
||||
|
|
|
@ -26,18 +26,18 @@ export default class UsagePingPayload {
|
|||
requestPayload() {
|
||||
if (this.isInserted) return this.showPayload();
|
||||
|
||||
this.spinner.classList.add('d-inline');
|
||||
this.spinner.classList.add('d-inline-flex');
|
||||
|
||||
return axios
|
||||
.get(this.container.dataset.endpoint, {
|
||||
responseType: 'text',
|
||||
})
|
||||
.then(({ data }) => {
|
||||
this.spinner.classList.remove('d-inline');
|
||||
this.spinner.classList.remove('d-inline-flex');
|
||||
this.insertPayload(data);
|
||||
})
|
||||
.catch(() => {
|
||||
this.spinner.classList.remove('d-inline');
|
||||
this.spinner.classList.remove('d-inline-flex');
|
||||
flash(__('Error fetching usage ping data.'));
|
||||
});
|
||||
}
|
||||
|
|
|
@ -11,7 +11,7 @@
|
|||
// like a table or typography then make changes in the framework/ directory.
|
||||
// If you need to add unique style that should affect only one page - use pages/
|
||||
// directory.
|
||||
@import "at.js/dist/css/jquery.atwho";
|
||||
@import "@gitlab/at.js/dist/css/jquery.atwho";
|
||||
@import "dropzone/dist/basic";
|
||||
@import "select2/select2";
|
||||
|
||||
|
|
|
@ -63,7 +63,8 @@
|
|||
display: block;
|
||||
}
|
||||
|
||||
.select2-choices {
|
||||
.select2-choices,
|
||||
.select2-choice {
|
||||
border-color: $red-500;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -49,3 +49,9 @@
|
|||
@include spinner-color($white);
|
||||
}
|
||||
}
|
||||
|
||||
.btn {
|
||||
.spinner {
|
||||
vertical-align: text-bottom;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -30,6 +30,7 @@ class Deployment < ApplicationRecord
|
|||
validate :valid_ref, on: :create
|
||||
|
||||
delegate :name, to: :environment, prefix: true
|
||||
delegate :kubernetes_namespace, to: :deployment_cluster, allow_nil: true
|
||||
|
||||
scope :for_environment, -> (environment) { where(environment_id: environment) }
|
||||
scope :for_environment_name, -> (name) do
|
||||
|
|
|
@ -22,6 +22,12 @@ class BuildDetailsEntity < JobEntity
|
|||
end
|
||||
end
|
||||
|
||||
expose :deployment_cluster, if: -> (build) { build&.deployment&.cluster } do |build, options|
|
||||
# Until data is copied over from deployments.cluster_id, this entity must represent Deployment instead of DeploymentCluster
|
||||
# https://gitlab.com/gitlab-org/gitlab/issues/202628
|
||||
DeploymentClusterEntity.represent(build.deployment, options)
|
||||
end
|
||||
|
||||
expose :artifact, if: -> (*) { can?(current_user, :read_build, build) } do
|
||||
expose :download_path, if: -> (*) { build.artifacts? } do |build|
|
||||
download_project_job_artifacts_path(project, build)
|
||||
|
|
|
@ -1,10 +0,0 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
class ClusterBasicEntity < Grape::Entity
|
||||
include RequestAwareEntity
|
||||
|
||||
expose :name
|
||||
expose :path, if: -> (cluster) { can?(request.current_user, :read_cluster, cluster) } do |cluster|
|
||||
cluster.present(current_user: request.current_user).show_path
|
||||
end
|
||||
end
|
|
@ -0,0 +1,20 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
class DeploymentClusterEntity < Grape::Entity
|
||||
include RequestAwareEntity
|
||||
|
||||
# Until data is copied over from deployments.cluster_id, this entity must represent Deployment instead of DeploymentCluster
|
||||
# https://gitlab.com/gitlab-org/gitlab/issues/202628
|
||||
|
||||
expose :name do |deployment|
|
||||
deployment.cluster.name
|
||||
end
|
||||
|
||||
expose :path, if: -> (deployment) { can?(request.current_user, :read_cluster, deployment.cluster) } do |deployment|
|
||||
deployment.cluster.present(current_user: request.current_user).show_path
|
||||
end
|
||||
|
||||
expose :kubernetes_namespace, if: -> (deployment) { can?(request.current_user, :read_cluster, deployment.cluster) } do |deployment|
|
||||
deployment.kubernetes_namespace
|
||||
end
|
||||
end
|
|
@ -41,7 +41,11 @@ class DeploymentEntity < Grape::Entity
|
|||
JobEntity.represent(deployment.playable_build, options.merge(only: [:play_path, :retry_path]))
|
||||
end
|
||||
|
||||
expose :cluster, using: ClusterBasicEntity
|
||||
expose :cluster do |deployment, options|
|
||||
# Until data is copied over from deployments.cluster_id, this entity must represent Deployment instead of DeploymentCluster
|
||||
# https://gitlab.com/gitlab-org/gitlab/issues/202628
|
||||
DeploymentClusterEntity.represent(deployment, options) unless deployment.cluster.nil?
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
|
|
|
@ -26,7 +26,7 @@
|
|||
%p.mb-2= s_('%{usage_ping_link_start}Learn more%{usage_ping_link_end} about what information is shared with GitLab Inc.').html_safe % { usage_ping_link_start: usage_ping_link_start, usage_ping_link_end: '</a>'.html_safe }
|
||||
|
||||
%button.btn.js-usage-ping-payload-trigger{ type: 'button' }
|
||||
.js-spinner.d-none= icon('spinner spin')
|
||||
.spinner.js-spinner.d-none
|
||||
.js-text.d-inline= _('Preview payload')
|
||||
%pre.usage-data.js-usage-ping-payload.js-syntax-highlight.code.highlight.mt-2.d-none{ data: { endpoint: usage_data_admin_application_settings_path(format: :html) } }
|
||||
- else
|
||||
|
|
|
@ -9,6 +9,6 @@
|
|||
milestone_merge_request_count: @milestone.merge_requests.count },
|
||||
disabled: true }
|
||||
= _('Delete')
|
||||
= icon('spin spinner', class: 'js-loading-icon hidden' )
|
||||
.spinner.js-loading-icon.hidden
|
||||
|
||||
#delete-milestone-modal
|
||||
|
|
|
@ -98,10 +98,6 @@
|
|||
human_time_estimate: @milestone.human_total_issue_time_estimate,
|
||||
human_time_spent: @milestone.human_total_issue_time_spent,
|
||||
limit_to_hours: Gitlab::CurrentSettings.time_tracking_limit_to_hours.to_s } }
|
||||
// Fallback while content is loading
|
||||
.title.hide-collapsed
|
||||
= _('Time tracking')
|
||||
= icon('spinner spin')
|
||||
|
||||
= render_if_exists 'shared/milestones/weight', milestone: milestone
|
||||
|
||||
|
|
|
@ -1,2 +1,2 @@
|
|||
.text-center.prepend-top-default
|
||||
= icon('spin spinner 2x', 'aria-hidden': 'true', 'aria-label': 'Loading tab content')
|
||||
.spinner.spinner-md
|
||||
|
|
|
@ -0,0 +1,5 @@
|
|||
---
|
||||
title: Replaced underscore with lodash for spec/javascripts/badges
|
||||
merge_request: 25135
|
||||
author: Shubham Pandey
|
||||
type: other
|
|
@ -0,0 +1,5 @@
|
|||
---
|
||||
title: Add experimental --queue-selector option to sidekiq-cluster
|
||||
merge_request: 18877
|
||||
author:
|
||||
type: changed
|
|
@ -0,0 +1,5 @@
|
|||
---
|
||||
title: Fix autocomplete limitation bug
|
||||
merge_request: 25167
|
||||
author:
|
||||
type: fixed
|
|
@ -0,0 +1,5 @@
|
|||
---
|
||||
title: Separate provider, platform and post receive entities into own class files
|
||||
merge_request: 25119
|
||||
author: Rajendra Kadam
|
||||
type: added
|
|
@ -0,0 +1,5 @@
|
|||
---
|
||||
title: Show Kubernetes namespace on job show page
|
||||
merge_request: 20983
|
||||
author:
|
||||
type: added
|
|
@ -29,7 +29,7 @@ module.exports = {
|
|||
'vuex',
|
||||
'pikaday',
|
||||
'vue/dist/vue.esm.js',
|
||||
'at.js',
|
||||
'@gitlab/at.js',
|
||||
'jed',
|
||||
'mermaid',
|
||||
'katex',
|
||||
|
|
|
@ -82,6 +82,93 @@ you list:
|
|||
sudo gitlab-ctl reconfigure
|
||||
```
|
||||
|
||||
## Queue selector (experimental)
|
||||
|
||||
> [Introduced](https://gitlab.com/gitlab-com/gl-infra/scalability/issues/45) in [GitLab Starter](https://about.gitlab.com/pricing/) 12.8.
|
||||
|
||||
CAUTION: **Caution:**
|
||||
As this is marked as **experimental**, it is subject to change at any
|
||||
time, including **breaking backwards compatibility**. This is so that we
|
||||
can react to changes we need for our GitLab.com deployment. We have a
|
||||
tracking issue open to [remove the experimental
|
||||
designation](https://gitlab.com/gitlab-com/gl-infra/scalability/issues/147)
|
||||
from this feature; please comment there if you are interested in using
|
||||
this in your own deployment.
|
||||
|
||||
In addition to selecting queues by name, as above, the
|
||||
`experimental_queue_selector` option allows queue groups to be selected
|
||||
in a more general way using the following components:
|
||||
|
||||
- Attributes that can be selected.
|
||||
- Operators used to construct a query.
|
||||
|
||||
### Available attributes
|
||||
|
||||
From the [list of all available
|
||||
attributes](https://gitlab.com/gitlab-org/gitlab/-/blob/master/app/workers/all_queues.yml),
|
||||
`experimental_queue_selector` allows selecting of queues by the
|
||||
following attributes:
|
||||
|
||||
- `feature_category` - the [GitLab feature
|
||||
category](https://about.gitlab.com/direction/maturity/#category-maturity) the
|
||||
queue belongs to. For example, the `merge` queue belongs to the
|
||||
`source_code_management` category.
|
||||
- `has_external_dependencies` - whether or not the queue connects to external
|
||||
services. For example, all importers have this set to `true`.
|
||||
- `latency_sensitive` - whether or not the queue is particularly sensitive to
|
||||
latency, which also means that its jobs should run quickly. For example, the
|
||||
`authorized_projects` queue is used to refresh user permissions, and is
|
||||
latency sensitive.
|
||||
- `name` - the queue name. The other attributes are typically more useful as
|
||||
they are more general, but this is available in case a particular queue needs
|
||||
to be selected.
|
||||
- `resource_boundary` - if the worker is bound by `cpu`, `memory`, or
|
||||
`unknown`. For example, the `project_export` queue is memory bound as it has
|
||||
to load data in memory before saving it for export.
|
||||
|
||||
Both `has_external_dependencies` and `latency_sensitive` are boolean attributes:
|
||||
only the exact string `true` is considered true, and everything else is
|
||||
considered false.
|
||||
|
||||
### Available operators
|
||||
|
||||
`experimental_queue_selector` supports the following operators, listed
|
||||
from highest to lowest precedence:
|
||||
|
||||
- `|` - the logical OR operator. For example, `query_a|query_b` (where `query_a`
|
||||
and `query_b` are queries made up of the other operators here) will include
|
||||
queues that match either query.
|
||||
- `&` - the logical AND operator. For example, `query_a&query_b` (where
|
||||
`query_a` and `query_b` are queries made up of the other operators here) will
|
||||
only include queues that match both queries.
|
||||
- `!=` - the NOT IN operator. For example, `feature_category!=issue_tracking`
|
||||
excludes all queues from the `issue_tracking` feature category.
|
||||
- `=` - the IN operator. For example, `resource_boundary=cpu` includes all
|
||||
queues that are CPU bound.
|
||||
- `,` - the concatenate set operator. For example,
|
||||
`feature_category=continuous_integration,pages` includes all queues from
|
||||
either the `continuous_integration` category or the `pages` category. This
|
||||
example is also possible using the OR operator, but allows greater brevity, as
|
||||
well as being lower precedence.
|
||||
|
||||
The operator precedence for this syntax is fixed: it's not possible to make AND
|
||||
have higher precedence than OR.
|
||||
|
||||
### Example queries
|
||||
|
||||
In `/etc/gitlab/gitlab.rb`:
|
||||
|
||||
```ruby
|
||||
sidekiq_cluster['enable'] = true
|
||||
sidekiq_cluster['experimental_queue_selector'] = true
|
||||
sidekiq_cluster['queue_groups'] = [
|
||||
# Run all non-CPU-bound queues that are latency sensitive
|
||||
'resource_boundary!=cpu&latency_sensitive=true',
|
||||
# Run all continuous integration and pages queues that are not latency sensitive
|
||||
'feature_category=continuous_integration,pages&latency_sensitive=false'
|
||||
]
|
||||
```
|
||||
|
||||
## Ignore all GitHub import queues
|
||||
|
||||
When [importing from GitHub](../../user/project/import/github.md), Sidekiq might
|
||||
|
|
|
@ -198,11 +198,11 @@ The Windows Shared Runners are currently in
|
|||
[beta](https://about.gitlab.com/handbook/product/#beta) and should not be used
|
||||
for production workloads.
|
||||
|
||||
During the beta period for groups and private projects the use of
|
||||
Windows Shared Runners will count towards the [shared runner pipeline
|
||||
quota](https://docs.gitlab.com/ee/user/admin_area/settings/continuous_integration.html#shared-runners-pipeline-minutes-quota-starter-only)
|
||||
as if they are Linux Runners, we do have plans to change this in
|
||||
[#30835](https://gitlab.com/gitlab-org/gitlab/issues/30834).
|
||||
During the beta period, the
|
||||
[shared runner pipeline quota](../admin_area/settings/continuous_integration.md#shared-runners-pipeline-minutes-quota-starter-only)
|
||||
will apply for groups and projects in the same way as Linux Runners.
|
||||
This may change when the beta period ends, as discussed in this
|
||||
[related issue](https://gitlab.com/gitlab-org/gitlab/issues/30834).
|
||||
|
||||
Windows Shared Runners on GitLab.com automatically autoscale by
|
||||
launching virtual machines on the Google Cloud Platform. This solution uses
|
||||
|
@ -321,18 +321,17 @@ test:
|
|||
- All the limitations mentioned in our [beta
|
||||
definition](https://about.gitlab.com/handbook/product/#beta).
|
||||
- The average provisioning time for a new Windows VM is 5 minutes.
|
||||
This means that for the beta you will notice slower build start times
|
||||
on the Windows Shared Runner fleet compared to Linux. In a future
|
||||
release we will add the ability to the autoscaler which will enable
|
||||
the pre-warming of virtual machines. This will significantly reduce
|
||||
This means that you may notice slower build start times
|
||||
on the Windows Shared Runner fleet during the beta. In a future
|
||||
release we will update the autoscaler to enable
|
||||
the pre-provisioning of virtual machines. This will significantly reduce
|
||||
the time it takes to provision a VM on the Windows fleet. You can
|
||||
follow along in this
|
||||
[issue](https://gitlab.com/gitlab-org/ci-cd/custom-executor-drivers/autoscaler/issues/32).
|
||||
follow along in the [related issue](https://gitlab.com/gitlab-org/ci-cd/custom-executor-drivers/autoscaler/issues/32).
|
||||
- The Windows Shared Runner fleet may be unavailable occasionally
|
||||
for maintenance or updates.
|
||||
- The Windows Shared Runner virtual machine instances do not use the
|
||||
GitLab Docker executor. This means that unlike the Linux Shared
|
||||
Runners, you will not be able to specify `image` and `services` in
|
||||
GitLab Docker executor. This means that you will not be able to specify
|
||||
[`image`](../../ci/yaml/README.md#image) or [`services`](../../ci/yaml/README.md#services) in
|
||||
your pipeline configuration.
|
||||
- For the beta release, we have included a set of software packages in
|
||||
the base VM image. If your CI job requires additional software that's
|
||||
|
|
|
@ -130,7 +130,7 @@ Once selected, click the **Delete selected** button to confirm the deletion:
|
|||
|
||||
![Delete multiple designs](img/delete_multiple_designs_v12_4.png)
|
||||
|
||||
NOTE: **Note:**
|
||||
**Note:**
|
||||
Only the latest version of the designs can be deleted.
|
||||
Deleted designs are not permanently lost; they can be
|
||||
viewed by browsing previous versions.
|
||||
|
@ -144,6 +144,9 @@ which you can start a new discussion:
|
|||
|
||||
![Starting a new discussion on design](img/adding_note_to_design_1.png)
|
||||
|
||||
From GitLab 12.8 on, when you are starting a new discussion, you can adjust the badge's position by
|
||||
dragging it around the image.
|
||||
|
||||
Different discussions have different badge numbers:
|
||||
|
||||
![Discussions on design annotations](img/adding_note_to_design_2.png)
|
||||
|
|
|
@ -243,39 +243,6 @@ module API
|
|||
expose :startline
|
||||
expose :project_id
|
||||
end
|
||||
|
||||
module Platform
|
||||
class Kubernetes < Grape::Entity
|
||||
expose :api_url
|
||||
expose :namespace
|
||||
expose :authorization_type
|
||||
expose :ca_cert
|
||||
end
|
||||
end
|
||||
|
||||
module Provider
|
||||
class Gcp < Grape::Entity
|
||||
expose :cluster_id
|
||||
expose :status_name
|
||||
expose :gcp_project_id
|
||||
expose :zone
|
||||
expose :machine_type
|
||||
expose :num_nodes
|
||||
expose :endpoint
|
||||
end
|
||||
end
|
||||
|
||||
module InternalPostReceive
|
||||
class Message < Grape::Entity
|
||||
expose :message
|
||||
expose :type
|
||||
end
|
||||
|
||||
class Response < Grape::Entity
|
||||
expose :messages, using: Message
|
||||
expose :reference_counter_decreased
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
|
|
|
@ -0,0 +1,12 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
module API
|
||||
module Entities
|
||||
module InternalPostReceive
|
||||
class Message < Grape::Entity
|
||||
expose :message
|
||||
expose :type
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
|
@ -0,0 +1,12 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
module API
|
||||
module Entities
|
||||
module InternalPostReceive
|
||||
class Response < Grape::Entity
|
||||
expose :messages, using: Entities::InternalPostReceive::Message
|
||||
expose :reference_counter_decreased
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
|
@ -0,0 +1,14 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
module API
|
||||
module Entities
|
||||
module Platform
|
||||
class Kubernetes < Grape::Entity
|
||||
expose :api_url
|
||||
expose :namespace
|
||||
expose :authorization_type
|
||||
expose :ca_cert
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
|
@ -0,0 +1,17 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
module API
|
||||
module Entities
|
||||
module Provider
|
||||
class Gcp < Grape::Entity
|
||||
expose :cluster_id
|
||||
expose :status_name
|
||||
expose :gcp_project_id
|
||||
expose :zone
|
||||
expose :machine_type
|
||||
expose :num_nodes
|
||||
expose :endpoint
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
|
@ -18,17 +18,39 @@ module Gitlab
|
|||
result
|
||||
end.freeze
|
||||
|
||||
def worker_queues(rails_path = Rails.root.to_s)
|
||||
QUERY_OR_OPERATOR = '|'
|
||||
QUERY_AND_OPERATOR = '&'
|
||||
QUERY_CONCATENATE_OPERATOR = ','
|
||||
QUERY_TERM_REGEX = %r{^(\w+)(!?=)([\w#{QUERY_CONCATENATE_OPERATOR}]+)}.freeze
|
||||
|
||||
QUERY_PREDICATES = {
|
||||
feature_category: :to_sym,
|
||||
has_external_dependencies: lambda { |value| value == 'true' },
|
||||
latency_sensitive: lambda { |value| value == 'true' },
|
||||
name: :to_s,
|
||||
resource_boundary: :to_sym
|
||||
}.freeze
|
||||
|
||||
QueryError = Class.new(StandardError)
|
||||
InvalidTerm = Class.new(QueryError)
|
||||
UnknownOperator = Class.new(QueryError)
|
||||
UnknownPredicate = Class.new(QueryError)
|
||||
|
||||
def all_queues(rails_path = Rails.root.to_s)
|
||||
@worker_queues ||= {}
|
||||
|
||||
@worker_queues[rails_path] ||= QUEUE_CONFIG_PATHS.flat_map do |path|
|
||||
full_path = File.join(rails_path, path)
|
||||
queues = File.exist?(full_path) ? YAML.load_file(full_path) : []
|
||||
|
||||
# https://gitlab.com/gitlab-org/gitlab/issues/199230
|
||||
queues.map { |queue| queue.is_a?(Hash) ? queue[:name] : queue }
|
||||
File.exist?(full_path) ? YAML.load_file(full_path) : []
|
||||
end
|
||||
end
|
||||
# rubocop:enable Gitlab/ModuleWithInstanceVariables
|
||||
|
||||
def worker_queues(rails_path = Rails.root.to_s)
|
||||
# https://gitlab.com/gitlab-org/gitlab/issues/199230
|
||||
worker_names(all_queues(rails_path))
|
||||
end
|
||||
|
||||
def expand_queues(queues, all_queues = self.worker_queues)
|
||||
return [] if queues.empty?
|
||||
|
@ -40,12 +62,64 @@ module Gitlab
|
|||
end
|
||||
end
|
||||
|
||||
def query_workers(query_string, queues)
|
||||
worker_names(queues.select(&query_string_to_lambda(query_string)))
|
||||
end
|
||||
|
||||
def clear_memoization!
|
||||
if instance_variable_defined?('@worker_queues')
|
||||
remove_instance_variable('@worker_queues')
|
||||
end
|
||||
end
|
||||
# rubocop:enable Gitlab/ModuleWithInstanceVariables
|
||||
|
||||
private
|
||||
|
||||
def worker_names(workers)
|
||||
workers.map { |queue| queue.is_a?(Hash) ? queue[:name] : queue }
|
||||
end
|
||||
|
||||
def query_string_to_lambda(query_string)
|
||||
or_clauses = query_string.split(QUERY_OR_OPERATOR).map do |and_clauses_string|
|
||||
and_clauses_predicates = and_clauses_string.split(QUERY_AND_OPERATOR).map do |term|
|
||||
predicate_for_term(term)
|
||||
end
|
||||
|
||||
lambda { |worker| and_clauses_predicates.all? { |predicate| predicate.call(worker) } }
|
||||
end
|
||||
|
||||
lambda { |worker| or_clauses.any? { |predicate| predicate.call(worker) } }
|
||||
end
|
||||
|
||||
def predicate_for_term(term)
|
||||
match = term.match(QUERY_TERM_REGEX)
|
||||
|
||||
raise InvalidTerm.new("Invalid term: #{term}") unless match
|
||||
|
||||
_, lhs, op, rhs = *match
|
||||
|
||||
predicate_for_op(op, predicate_factory(lhs, rhs.split(QUERY_CONCATENATE_OPERATOR)))
|
||||
end
|
||||
|
||||
def predicate_for_op(op, predicate)
|
||||
case op
|
||||
when '='
|
||||
predicate
|
||||
when '!='
|
||||
lambda { |worker| !predicate.call(worker) }
|
||||
else
|
||||
# This is unreachable because InvalidTerm will be raised instead, but
|
||||
# keeping it allows to guard against that changing in future.
|
||||
raise UnknownOperator.new("Unknown operator: #{op}")
|
||||
end
|
||||
end
|
||||
|
||||
def predicate_factory(lhs, values)
|
||||
values_block = QUERY_PREDICATES[lhs.to_sym]
|
||||
|
||||
raise UnknownPredicate.new("Unknown predicate: #{lhs}") unless values_block
|
||||
|
||||
lambda { |queue| values.map(&values_block).include?(queue[lhs.to_sym]) }
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -1958,6 +1958,9 @@ msgstr ""
|
|||
msgid "Any Milestone"
|
||||
msgstr ""
|
||||
|
||||
msgid "Any branch"
|
||||
msgstr ""
|
||||
|
||||
msgid "Any eligible user"
|
||||
msgstr ""
|
||||
|
||||
|
@ -2033,6 +2036,9 @@ msgstr ""
|
|||
msgid "Apply template"
|
||||
msgstr ""
|
||||
|
||||
msgid "Apply this approval rule to any branch or a specific protected branch."
|
||||
msgstr ""
|
||||
|
||||
msgid "Applying a template will replace the existing issue description. Any changes you have made will be lost."
|
||||
msgstr ""
|
||||
|
||||
|
@ -2086,6 +2092,9 @@ msgstr ""
|
|||
msgid "ApprovalRule|Rule name"
|
||||
msgstr ""
|
||||
|
||||
msgid "ApprovalRule|Target branch"
|
||||
msgstr ""
|
||||
|
||||
msgid "ApprovalRule|e.g. QA, Security, etc."
|
||||
msgstr ""
|
||||
|
||||
|
@ -13999,6 +14008,9 @@ msgstr ""
|
|||
msgid "Please select a group."
|
||||
msgstr ""
|
||||
|
||||
msgid "Please select a valid target branch"
|
||||
msgstr ""
|
||||
|
||||
msgid "Please select and add a member"
|
||||
msgstr ""
|
||||
|
||||
|
@ -19587,6 +19599,12 @@ msgstr ""
|
|||
msgid "This job has not started yet"
|
||||
msgstr ""
|
||||
|
||||
msgid "This job is an out-of-date deployment to %{environmentLink} using cluster %{clusterNameOrLink} and namespace %{kubernetesNamespace}."
|
||||
msgstr ""
|
||||
|
||||
msgid "This job is an out-of-date deployment to %{environmentLink} using cluster %{clusterNameOrLink} and namespace %{kubernetesNamespace}. View the %{deploymentLink}."
|
||||
msgstr ""
|
||||
|
||||
msgid "This job is an out-of-date deployment to %{environmentLink} using cluster %{clusterNameOrLink}."
|
||||
msgstr ""
|
||||
|
||||
|
@ -19602,6 +19620,15 @@ msgstr ""
|
|||
msgid "This job is archived. Only the complete pipeline can be retried."
|
||||
msgstr ""
|
||||
|
||||
msgid "This job is creating a deployment to %{environmentLink} using cluster %{clusterNameOrLink} and namespace %{kubernetesNamespace}."
|
||||
msgstr ""
|
||||
|
||||
msgid "This job is creating a deployment to %{environmentLink} using cluster %{clusterNameOrLink} and namespace %{kubernetesNamespace}. This will overwrite the %{deploymentLink}."
|
||||
msgstr ""
|
||||
|
||||
msgid "This job is creating a deployment to %{environmentLink} using cluster %{clusterNameOrLink}."
|
||||
msgstr ""
|
||||
|
||||
msgid "This job is creating a deployment to %{environmentLink} using cluster %{clusterNameOrLink}. This will overwrite the %{deploymentLink}."
|
||||
msgstr ""
|
||||
|
||||
|
@ -19611,6 +19638,9 @@ msgstr ""
|
|||
msgid "This job is creating a deployment to %{environmentLink}. This will overwrite the %{deploymentLink}."
|
||||
msgstr ""
|
||||
|
||||
msgid "This job is deployed to %{environmentLink} using cluster %{clusterNameOrLink} and namespace %{kubernetesNamespace}."
|
||||
msgstr ""
|
||||
|
||||
msgid "This job is deployed to %{environmentLink} using cluster %{clusterNameOrLink}."
|
||||
msgstr ""
|
||||
|
||||
|
|
|
@ -39,8 +39,9 @@
|
|||
"@babel/plugin-syntax-dynamic-import": "^7.2.0",
|
||||
"@babel/plugin-syntax-import-meta": "^7.2.0",
|
||||
"@babel/preset-env": "^7.6.2",
|
||||
"@gitlab/at.js": "^1.5.5",
|
||||
"@gitlab/svgs": "^1.96.0",
|
||||
"@gitlab/ui": "^9.6.0",
|
||||
"@gitlab/ui": "^9.8.0",
|
||||
"@gitlab/visual-review-tools": "1.5.1",
|
||||
"@sentry/browser": "^5.10.2",
|
||||
"@sourcegraph/code-host-integration": "0.0.30",
|
||||
|
@ -49,7 +50,6 @@
|
|||
"apollo-link": "^1.2.11",
|
||||
"apollo-link-batch-http": "^1.2.11",
|
||||
"apollo-upload-client": "^10.0.0",
|
||||
"at.js": "^1.5.4",
|
||||
"autosize": "^4.0.0",
|
||||
"aws-sdk": "^2.526.0",
|
||||
"axios": "^0.19.0",
|
||||
|
|
|
@ -0,0 +1,9 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
FactoryBot.define do
|
||||
factory :deployment_cluster, class: 'DeploymentCluster' do
|
||||
cluster
|
||||
deployment
|
||||
kubernetes_namespace { 'the-namespace' }
|
||||
end
|
||||
end
|
|
@ -50,7 +50,7 @@
|
|||
"cluster": {
|
||||
"oneOf": [
|
||||
{ "type": "null" },
|
||||
{ "$ref": "cluster_basic.json" }
|
||||
{ "$ref": "deployment_cluster.json" }
|
||||
]
|
||||
},
|
||||
"manual_actions": {
|
||||
|
|
|
@ -10,6 +10,12 @@
|
|||
{ "type": "null" },
|
||||
{ "type": "string" }
|
||||
]
|
||||
},
|
||||
"kubernetes_namespace": {
|
||||
"oneOf": [
|
||||
{ "type": "null" },
|
||||
{ "type": "string" }
|
||||
]
|
||||
}
|
||||
},
|
||||
"additionalProperties": false
|
|
@ -15,6 +15,12 @@
|
|||
"terminal_path": { "type": "string" },
|
||||
"trigger": { "$ref": "trigger.json" },
|
||||
"deployment_status": { "$ref": "deployment_status.json" },
|
||||
"deployment_cluster": {
|
||||
"oneOf": [
|
||||
{ "$ref": "../deployment_cluster.json" },
|
||||
{ "type": "null" }
|
||||
]
|
||||
},
|
||||
"runner": { "$ref": "runner.json" },
|
||||
"runners": { "$ref": "runners.json" },
|
||||
"has_trace": { "type": "boolean" },
|
||||
|
|
|
@ -4,7 +4,7 @@ import $ from 'jquery';
|
|||
import GfmAutoComplete, { membersBeforeSave } from 'ee_else_ce/gfm_auto_complete';
|
||||
|
||||
import 'jquery.caret';
|
||||
import 'at.js';
|
||||
import '@gitlab/at.js';
|
||||
|
||||
import { TEST_HOST } from 'helpers/test_constants';
|
||||
import { getJSONFixture } from 'helpers/fixtures';
|
||||
|
|
|
@ -1,9 +1,9 @@
|
|||
import _ from 'underscore';
|
||||
import { uniqueId } from 'lodash';
|
||||
import { DUMMY_IMAGE_URL, TEST_HOST } from 'spec/test_constants';
|
||||
import { PROJECT_BADGE } from '~/badges/constants';
|
||||
|
||||
export const createDummyBadge = () => {
|
||||
const id = _.uniqueId();
|
||||
const id = uniqueId();
|
||||
return {
|
||||
id,
|
||||
name: 'TestBadge',
|
||||
|
|
|
@ -4,6 +4,7 @@ import mountComponent from '../../helpers/vue_mount_component_helper';
|
|||
|
||||
const TEST_CLUSTER_NAME = 'test_cluster';
|
||||
const TEST_CLUSTER_PATH = 'path/to/test_cluster';
|
||||
const TEST_KUBERNETES_NAMESPACE = 'this-is-a-kubernetes-namespace';
|
||||
|
||||
describe('Environments block', () => {
|
||||
const Component = Vue.extend(component);
|
||||
|
@ -28,17 +29,18 @@ describe('Environments block', () => {
|
|||
last_deployment: { ...lastDeployment },
|
||||
});
|
||||
|
||||
const createEnvironmentWithCluster = () => ({
|
||||
...environment,
|
||||
last_deployment: {
|
||||
...lastDeployment,
|
||||
cluster: { name: TEST_CLUSTER_NAME, path: TEST_CLUSTER_PATH },
|
||||
},
|
||||
const createDeploymentWithCluster = () => ({ name: TEST_CLUSTER_NAME, path: TEST_CLUSTER_PATH });
|
||||
|
||||
const createDeploymentWithClusterAndKubernetesNamespace = () => ({
|
||||
name: TEST_CLUSTER_NAME,
|
||||
path: TEST_CLUSTER_PATH,
|
||||
kubernetes_namespace: TEST_KUBERNETES_NAMESPACE,
|
||||
});
|
||||
|
||||
const createComponent = (deploymentStatus = {}) => {
|
||||
const createComponent = (deploymentStatus = {}, deploymentCluster = {}) => {
|
||||
vm = mountComponent(Component, {
|
||||
deploymentStatus,
|
||||
deploymentCluster,
|
||||
iconStatus: status,
|
||||
});
|
||||
};
|
||||
|
@ -62,15 +64,36 @@ describe('Environments block', () => {
|
|||
expect(findText()).toEqual('This job is deployed to environment.');
|
||||
});
|
||||
|
||||
it('renders info with cluster', () => {
|
||||
createComponent({
|
||||
status: 'last',
|
||||
environment: createEnvironmentWithCluster(),
|
||||
describe('when there is a cluster', () => {
|
||||
it('renders info with cluster', () => {
|
||||
createComponent(
|
||||
{
|
||||
status: 'last',
|
||||
environment: createEnvironmentWithLastDeployment(),
|
||||
},
|
||||
createDeploymentWithCluster(),
|
||||
);
|
||||
|
||||
expect(findText()).toEqual(
|
||||
`This job is deployed to environment using cluster ${TEST_CLUSTER_NAME}.`,
|
||||
);
|
||||
});
|
||||
|
||||
expect(findText()).toEqual(
|
||||
`This job is deployed to environment using cluster ${TEST_CLUSTER_NAME}.`,
|
||||
);
|
||||
describe('when there is a kubernetes namespace', () => {
|
||||
it('renders info with cluster', () => {
|
||||
createComponent(
|
||||
{
|
||||
status: 'last',
|
||||
environment: createEnvironmentWithLastDeployment(),
|
||||
},
|
||||
createDeploymentWithClusterAndKubernetesNamespace(),
|
||||
);
|
||||
|
||||
expect(findText()).toEqual(
|
||||
`This job is deployed to environment using cluster ${TEST_CLUSTER_NAME} and namespace ${TEST_KUBERNETES_NAMESPACE}.`,
|
||||
);
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
|
@ -89,15 +112,36 @@ describe('Environments block', () => {
|
|||
expect(findJobDeploymentLink().getAttribute('href')).toEqual('bar');
|
||||
});
|
||||
|
||||
it('renders info with cluster', () => {
|
||||
createComponent({
|
||||
status: 'out_of_date',
|
||||
environment: createEnvironmentWithCluster(),
|
||||
describe('when there is a cluster', () => {
|
||||
it('renders info with cluster', () => {
|
||||
createComponent(
|
||||
{
|
||||
status: 'out_of_date',
|
||||
environment: createEnvironmentWithLastDeployment(),
|
||||
},
|
||||
createDeploymentWithCluster(),
|
||||
);
|
||||
|
||||
expect(findText()).toEqual(
|
||||
`This job is an out-of-date deployment to environment using cluster ${TEST_CLUSTER_NAME}. View the most recent deployment.`,
|
||||
);
|
||||
});
|
||||
|
||||
expect(findText()).toEqual(
|
||||
`This job is an out-of-date deployment to environment using cluster ${TEST_CLUSTER_NAME}. View the most recent deployment.`,
|
||||
);
|
||||
describe('when there is a kubernetes namespace', () => {
|
||||
it('renders info with cluster', () => {
|
||||
createComponent(
|
||||
{
|
||||
status: 'out_of_date',
|
||||
environment: createEnvironmentWithLastDeployment(),
|
||||
},
|
||||
createDeploymentWithClusterAndKubernetesNamespace(),
|
||||
);
|
||||
|
||||
expect(findText()).toEqual(
|
||||
`This job is an out-of-date deployment to environment using cluster ${TEST_CLUSTER_NAME} and namespace ${TEST_KUBERNETES_NAMESPACE}. View the most recent deployment.`,
|
||||
);
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
|
@ -143,7 +187,7 @@ describe('Environments block', () => {
|
|||
});
|
||||
|
||||
describe('without last deployment', () => {
|
||||
it('renders info about failed deployment', () => {
|
||||
it('renders info about deployment being created', () => {
|
||||
createComponent({
|
||||
status: 'creating',
|
||||
environment,
|
||||
|
@ -151,6 +195,22 @@ describe('Environments block', () => {
|
|||
|
||||
expect(findText()).toEqual('This job is creating a deployment to environment.');
|
||||
});
|
||||
|
||||
describe('when there is a cluster', () => {
|
||||
it('inclues information about the cluster', () => {
|
||||
createComponent(
|
||||
{
|
||||
status: 'creating',
|
||||
environment,
|
||||
},
|
||||
createDeploymentWithCluster(),
|
||||
);
|
||||
|
||||
expect(findText()).toEqual(
|
||||
`This job is creating a deployment to environment using cluster ${TEST_CLUSTER_NAME}.`,
|
||||
);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('without environment', () => {
|
||||
|
@ -167,10 +227,13 @@ describe('Environments block', () => {
|
|||
|
||||
describe('with a cluster', () => {
|
||||
it('renders the cluster link', () => {
|
||||
createComponent({
|
||||
status: 'last',
|
||||
environment: createEnvironmentWithCluster(),
|
||||
});
|
||||
createComponent(
|
||||
{
|
||||
status: 'last',
|
||||
environment: createEnvironmentWithLastDeployment(),
|
||||
},
|
||||
createDeploymentWithCluster(),
|
||||
);
|
||||
|
||||
expect(findText()).toEqual(
|
||||
`This job is deployed to environment using cluster ${TEST_CLUSTER_NAME}.`,
|
||||
|
@ -181,18 +244,13 @@ describe('Environments block', () => {
|
|||
|
||||
describe('when the cluster is missing the path', () => {
|
||||
it('renders the name without a link', () => {
|
||||
const cluster = {
|
||||
name: 'the-cluster',
|
||||
};
|
||||
createComponent({
|
||||
status: 'last',
|
||||
environment: Object.assign({}, environment, {
|
||||
last_deployment: {
|
||||
...lastDeployment,
|
||||
cluster,
|
||||
},
|
||||
}),
|
||||
});
|
||||
createComponent(
|
||||
{
|
||||
status: 'last',
|
||||
environment: createEnvironmentWithLastDeployment(),
|
||||
},
|
||||
{ name: 'the-cluster' },
|
||||
);
|
||||
|
||||
expect(findText()).toContain('using cluster the-cluster.');
|
||||
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
require 'fast_spec_helper'
|
||||
require 'rspec-parameterized'
|
||||
|
||||
describe Gitlab::SidekiqConfig::CliMethods do
|
||||
let(:dummy_root) { '/tmp/' }
|
||||
|
@ -82,7 +83,7 @@ describe Gitlab::SidekiqConfig::CliMethods do
|
|||
end
|
||||
|
||||
describe '.expand_queues' do
|
||||
let(:all_queues) do
|
||||
let(:worker_queues) do
|
||||
['cronjob:stuck_import_jobs', 'cronjob:stuck_merge_jobs', 'post_receive']
|
||||
end
|
||||
|
||||
|
@ -92,25 +93,125 @@ describe Gitlab::SidekiqConfig::CliMethods do
|
|||
expect(described_class.expand_queues(['cronjob']))
|
||||
.to contain_exactly('cronjob')
|
||||
|
||||
allow(described_class).to receive(:worker_queues).and_return(all_queues)
|
||||
allow(described_class).to receive(:worker_queues).and_return(worker_queues)
|
||||
|
||||
expect(described_class.expand_queues(['cronjob']))
|
||||
.to contain_exactly('cronjob', 'cronjob:stuck_import_jobs', 'cronjob:stuck_merge_jobs')
|
||||
end
|
||||
|
||||
it 'expands queue namespaces to concrete queue names' do
|
||||
expect(described_class.expand_queues(['cronjob'], all_queues))
|
||||
expect(described_class.expand_queues(['cronjob'], worker_queues))
|
||||
.to contain_exactly('cronjob', 'cronjob:stuck_import_jobs', 'cronjob:stuck_merge_jobs')
|
||||
end
|
||||
|
||||
it 'lets concrete queue names pass through' do
|
||||
expect(described_class.expand_queues(['post_receive'], all_queues))
|
||||
expect(described_class.expand_queues(['post_receive'], worker_queues))
|
||||
.to contain_exactly('post_receive')
|
||||
end
|
||||
|
||||
it 'lets unknown queues pass through' do
|
||||
expect(described_class.expand_queues(['unknown'], all_queues))
|
||||
expect(described_class.expand_queues(['unknown'], worker_queues))
|
||||
.to contain_exactly('unknown')
|
||||
end
|
||||
end
|
||||
|
||||
describe '.query_workers' do
|
||||
using RSpec::Parameterized::TableSyntax
|
||||
|
||||
let(:queues) do
|
||||
[
|
||||
{
|
||||
name: 'a',
|
||||
feature_category: :category_a,
|
||||
has_external_dependencies: false,
|
||||
latency_sensitive: false,
|
||||
resource_boundary: :cpu
|
||||
},
|
||||
{
|
||||
name: 'a_2',
|
||||
feature_category: :category_a,
|
||||
has_external_dependencies: false,
|
||||
latency_sensitive: true,
|
||||
resource_boundary: :none
|
||||
},
|
||||
{
|
||||
name: 'b',
|
||||
feature_category: :category_b,
|
||||
has_external_dependencies: true,
|
||||
latency_sensitive: true,
|
||||
resource_boundary: :memory
|
||||
},
|
||||
{
|
||||
name: 'c',
|
||||
feature_category: :category_c,
|
||||
has_external_dependencies: false,
|
||||
latency_sensitive: false,
|
||||
resource_boundary: :memory
|
||||
}
|
||||
]
|
||||
end
|
||||
|
||||
context 'with valid input' do
|
||||
where(:query, :selected_queues) do
|
||||
# feature_category
|
||||
'feature_category=category_a' | %w(a a_2)
|
||||
'feature_category=category_a,category_c' | %w(a a_2 c)
|
||||
'feature_category=category_a|feature_category=category_c' | %w(a a_2 c)
|
||||
'feature_category!=category_a' | %w(b c)
|
||||
|
||||
# has_external_dependencies
|
||||
'has_external_dependencies=true' | %w(b)
|
||||
'has_external_dependencies=false' | %w(a a_2 c)
|
||||
'has_external_dependencies=true,false' | %w(a a_2 b c)
|
||||
'has_external_dependencies=true|has_external_dependencies=false' | %w(a a_2 b c)
|
||||
'has_external_dependencies!=true' | %w(a a_2 c)
|
||||
|
||||
# latency_sensitive
|
||||
'latency_sensitive=true' | %w(a_2 b)
|
||||
'latency_sensitive=false' | %w(a c)
|
||||
'latency_sensitive=true,false' | %w(a a_2 b c)
|
||||
'latency_sensitive=true|latency_sensitive=false' | %w(a a_2 b c)
|
||||
'latency_sensitive!=true' | %w(a c)
|
||||
|
||||
# name
|
||||
'name=a' | %w(a)
|
||||
'name=a,b' | %w(a b)
|
||||
'name=a,a_2|name=b' | %w(a a_2 b)
|
||||
'name!=a,a_2' | %w(b c)
|
||||
|
||||
# resource_boundary
|
||||
'resource_boundary=memory' | %w(b c)
|
||||
'resource_boundary=memory,cpu' | %w(a b c)
|
||||
'resource_boundary=memory|resource_boundary=cpu' | %w(a b c)
|
||||
'resource_boundary!=memory,cpu' | %w(a_2)
|
||||
|
||||
# combinations
|
||||
'feature_category=category_a&latency_sensitive=true' | %w(a_2)
|
||||
'feature_category=category_a&latency_sensitive=true|feature_category=category_c' | %w(a_2 c)
|
||||
end
|
||||
|
||||
with_them do
|
||||
it do
|
||||
expect(described_class.query_workers(query, queues))
|
||||
.to match_array(selected_queues)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
context 'with invalid input' do
|
||||
where(:query, :error) do
|
||||
'feature_category="category_a"' | described_class::InvalidTerm
|
||||
'feature_category=' | described_class::InvalidTerm
|
||||
'feature_category~category_a' | described_class::InvalidTerm
|
||||
'worker_name=a' | described_class::UnknownPredicate
|
||||
end
|
||||
|
||||
with_them do
|
||||
it do
|
||||
expect { described_class.query_workers(query, queues) }
|
||||
.to raise_error(error)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -18,6 +18,7 @@ describe Deployment do
|
|||
it { is_expected.to delegate_method(:commit).to(:project) }
|
||||
it { is_expected.to delegate_method(:commit_title).to(:commit).as(:try) }
|
||||
it { is_expected.to delegate_method(:manual_actions).to(:deployable).as(:try) }
|
||||
it { is_expected.to delegate_method(:kubernetes_namespace).to(:deployment_cluster).as(:kubernetes_namespace) }
|
||||
|
||||
it { is_expected.to validate_presence_of(:ref) }
|
||||
it { is_expected.to validate_presence_of(:sha) }
|
||||
|
|
|
@ -2,9 +2,9 @@
|
|||
|
||||
require 'spec_helper'
|
||||
|
||||
describe ClusterBasicEntity do
|
||||
describe DeploymentClusterEntity do
|
||||
describe '#as_json' do
|
||||
subject { described_class.new(cluster, request: request).as_json }
|
||||
subject { described_class.new(deployment, request: request).as_json }
|
||||
|
||||
let(:maintainer) { create(:user) }
|
||||
let(:developer) { create(:user) }
|
||||
|
@ -12,26 +12,30 @@ describe ClusterBasicEntity do
|
|||
let(:request) { double(:request, current_user: current_user) }
|
||||
let(:project) { create(:project) }
|
||||
let(:cluster) { create(:cluster, name: 'the-cluster', projects: [project]) }
|
||||
let(:deployment) { create(:deployment, cluster: cluster) }
|
||||
let!(:deployment_cluster) { create(:deployment_cluster, cluster: cluster, deployment: deployment) }
|
||||
|
||||
before do
|
||||
project.add_maintainer(maintainer)
|
||||
project.add_developer(developer)
|
||||
end
|
||||
|
||||
it 'matches cluster_basic entity schema' do
|
||||
expect(subject.as_json).to match_schema('cluster_basic')
|
||||
it 'matches deployment_cluster entity schema' do
|
||||
expect(subject.as_json).to match_schema('deployment_cluster')
|
||||
end
|
||||
|
||||
it 'exposes the cluster details' do
|
||||
expect(subject[:name]).to eq('the-cluster')
|
||||
expect(subject[:path]).to eq("/#{project.full_path}/-/clusters/#{cluster.id}")
|
||||
expect(subject[:kubernetes_namespace]).to eq(deployment_cluster.kubernetes_namespace)
|
||||
end
|
||||
|
||||
context 'when the user does not have permission to view the cluster' do
|
||||
let(:current_user) { developer }
|
||||
|
||||
it 'does not include the path' do
|
||||
it 'does not include the path nor the namespace' do
|
||||
expect(subject[:path]).to be_nil
|
||||
expect(subject[:kubernetes_namespace]).to be_nil
|
||||
end
|
||||
end
|
||||
end
|
18
yarn.lock
18
yarn.lock
|
@ -705,6 +705,11 @@
|
|||
exec-sh "^0.3.2"
|
||||
minimist "^1.2.0"
|
||||
|
||||
"@gitlab/at.js@^1.5.5":
|
||||
version "1.5.5"
|
||||
resolved "https://registry.yarnpkg.com/@gitlab/at.js/-/at.js-1.5.5.tgz#5f6bfe6baaef360daa9b038fa78798d7a6a916b4"
|
||||
integrity sha512-282Dn3SPVsUHVDhMsXgfnv+Rzog0uxecjttxGRQvxh25es1+xvkGQFsvJfkSKJ3X1kHVkSjKf+Tt5Rra+Jhp9g==
|
||||
|
||||
"@gitlab/eslint-config@^2.1.2":
|
||||
version "2.1.2"
|
||||
resolved "https://registry.yarnpkg.com/@gitlab/eslint-config/-/eslint-config-2.1.2.tgz#9f4011d3bf15f3e2668a1faa754f0b9804f23f8f"
|
||||
|
@ -740,10 +745,10 @@
|
|||
resolved "https://registry.yarnpkg.com/@gitlab/svgs/-/svgs-1.96.0.tgz#1d32730389e94358dc245e8336912523446d1269"
|
||||
integrity sha512-mhg6kndxDhwjWChKhs5utO6PowlOyFdaCXUrkkxxe2H3cd8DYa40QOEcJeUrSIhkmgIMVesUawesx5tt4Bnnnw==
|
||||
|
||||
"@gitlab/ui@^9.6.0":
|
||||
version "9.6.0"
|
||||
resolved "https://registry.yarnpkg.com/@gitlab/ui/-/ui-9.6.0.tgz#13119a56a34be34fd07e761cab0af3c00462159d"
|
||||
integrity sha512-R0pUa30l/JX/+1K/rZGAjDvCLLoQuodwCxBNzQ5U1ylnnfGclVrM2rBlZT3UlWnMkb9BRhTPn6uoC/HBOAo37g==
|
||||
"@gitlab/ui@^9.8.0":
|
||||
version "9.8.0"
|
||||
resolved "https://registry.yarnpkg.com/@gitlab/ui/-/ui-9.8.0.tgz#b1a0b5f1f6ac9fdb19b64d74f0f729e3ec182495"
|
||||
integrity sha512-0VjSTjCCtevdoeByxf5o/OimzV3zt1MMH5DlZSqakML38uoOM0WpgXI/4xAipzfYwiKUW+IWbuyZGJ3ucaJnhQ==
|
||||
dependencies:
|
||||
"@babel/standalone" "^7.0.0"
|
||||
"@gitlab/vue-toasted" "^1.3.0"
|
||||
|
@ -1774,11 +1779,6 @@ asynckit@^0.4.0:
|
|||
resolved "https://registry.yarnpkg.com/asynckit/-/asynckit-0.4.0.tgz#c79ed97f7f34cb8f2ba1bc9790bcc366474b4b79"
|
||||
integrity sha1-x57Zf380y48robyXkLzDZkdLS3k=
|
||||
|
||||
at.js@^1.5.4:
|
||||
version "1.5.4"
|
||||
resolved "https://registry.yarnpkg.com/at.js/-/at.js-1.5.4.tgz#8fc60cc80eadbe4874449b166a818e7ae1d784c1"
|
||||
integrity sha512-G8mgUb/PqShPoH8AyjuxsTGvIr1o716BtQUKDM44C8qN2W615y7KGJ68MlTGamd0J0D/m28emUkzagaHTdrGZw==
|
||||
|
||||
atob@^2.1.1:
|
||||
version "2.1.2"
|
||||
resolved "https://registry.yarnpkg.com/atob/-/atob-2.1.2.tgz#6d9517eb9e030d2436666651e86bd9f6f13533c9"
|
||||
|
|
Loading…
Reference in New Issue