Add latest changes from gitlab-org/gitlab@master
This commit is contained in:
parent
8c4198cbe6
commit
b9bac6dbf7
46 changed files with 789 additions and 440 deletions
23
.gitlab/issue_templates/Technical Evaluation.md
Normal file
23
.gitlab/issue_templates/Technical Evaluation.md
Normal file
|
@ -0,0 +1,23 @@
|
|||
<!-- Instructions: Use this template for a proof of concept or when a deeper technical evaluation is required. Please weigh tech evaluation issues and follow the instructions below accordingly. -->
|
||||
|
||||
### Topic to Evaluate
|
||||
|
||||
<!-- Describe the related issue and challenge we need to establish a proof of concept for-->
|
||||
* [Link to other Issue](link)
|
||||
|
||||
### Tasks to Evaluate
|
||||
|
||||
<!-- Outline the tasks with issues that you need evaluate as a part of the implementation issue -->
|
||||
|
||||
- [ ] Add task
|
||||
- [ ] Add task
|
||||
- [ ] Add task
|
||||
|
||||
### Risks and Implementation Considerations
|
||||
|
||||
<!-- Idenitfy any risks found in the research, whether this is performance, impacts to other functionality or other bugs -->
|
||||
|
||||
### Team
|
||||
|
||||
- [ ] Add ~"workflow::planning breakdown" ~feature and the corresponding `~devops::<stage>` and `~group::<group>` labels.
|
||||
- [ ] Ping the PM and EM.
|
|
@ -90,11 +90,7 @@ export default {
|
|||
};
|
||||
</script>
|
||||
<template>
|
||||
<div v-gl-resize-observer-directive="onResize" class="prometheus-graph">
|
||||
<div class="prometheus-graph-header">
|
||||
<h5 ref="graphTitle" class="prometheus-graph-title">{{ graphData.title }}</h5>
|
||||
<div ref="graphWidgets" class="prometheus-graph-widgets"><slot></slot></div>
|
||||
</div>
|
||||
<div v-gl-resize-observer-directive="onResize">
|
||||
<gl-column-chart
|
||||
ref="columnChart"
|
||||
v-bind="$attrs"
|
||||
|
|
|
@ -27,10 +27,7 @@ export default {
|
|||
};
|
||||
</script>
|
||||
<template>
|
||||
<div class="prometheus-graph d-flex flex-column justify-content-center">
|
||||
<div class="prometheus-graph-header">
|
||||
<h5 ref="graphTitle" class="prometheus-graph-title">{{ graphTitle }}</h5>
|
||||
</div>
|
||||
<div class="d-flex flex-column justify-content-center">
|
||||
<div
|
||||
class="prepend-top-8 svg-w-100 d-flex align-items-center"
|
||||
:style="svgContainerStyle"
|
||||
|
|
|
@ -2,13 +2,11 @@
|
|||
import { GlResizeObserverDirective } from '@gitlab/ui';
|
||||
import { GlHeatmap } from '@gitlab/ui/dist/charts';
|
||||
import dateformat from 'dateformat';
|
||||
import PrometheusHeader from '../shared/prometheus_header.vue';
|
||||
import { graphDataValidatorForValues } from '../../utils';
|
||||
|
||||
export default {
|
||||
components: {
|
||||
GlHeatmap,
|
||||
PrometheusHeader,
|
||||
},
|
||||
directives: {
|
||||
GlResizeObserverDirective,
|
||||
|
@ -65,8 +63,7 @@ export default {
|
|||
};
|
||||
</script>
|
||||
<template>
|
||||
<div v-gl-resize-observer-directive="onResize" class="prometheus-graph col-12 col-lg-6">
|
||||
<prometheus-header :graph-title="graphData.title" />
|
||||
<div v-gl-resize-observer-directive="onResize" class="col-12 col-lg-6">
|
||||
<gl-heatmap
|
||||
ref="heatmapChart"
|
||||
v-bind="$attrs"
|
||||
|
|
|
@ -42,10 +42,7 @@ export default {
|
|||
};
|
||||
</script>
|
||||
<template>
|
||||
<div class="prometheus-graph">
|
||||
<div class="prometheus-graph-header">
|
||||
<h5 ref="graphTitle" class="prometheus-graph-title">{{ graphTitle }}</h5>
|
||||
</div>
|
||||
<div>
|
||||
<gl-single-stat :value="statValue" :title="graphTitle" variant="success" />
|
||||
</div>
|
||||
</template>
|
||||
|
|
|
@ -81,11 +81,7 @@ export default {
|
|||
};
|
||||
</script>
|
||||
<template>
|
||||
<div v-gl-resize-observer-directive="onResize" class="prometheus-graph">
|
||||
<div class="prometheus-graph-header">
|
||||
<h5 ref="graphTitle" class="prometheus-graph-title">{{ graphData.title }}</h5>
|
||||
<div ref="graphWidgets" class="prometheus-graph-widgets"><slot></slot></div>
|
||||
</div>
|
||||
<div v-gl-resize-observer-directive="onResize">
|
||||
<gl-stacked-column-chart
|
||||
ref="chart"
|
||||
v-bind="$attrs"
|
||||
|
|
|
@ -112,7 +112,6 @@ export default {
|
|||
isDeployment: false,
|
||||
sha: '',
|
||||
},
|
||||
showTitleTooltip: false,
|
||||
width: 0,
|
||||
height: chartHeight,
|
||||
svgs: {},
|
||||
|
@ -285,12 +284,6 @@ export default {
|
|||
return `${this.graphData.y_label}`;
|
||||
},
|
||||
},
|
||||
mounted() {
|
||||
const graphTitleEl = this.$refs.graphTitle;
|
||||
if (graphTitleEl && graphTitleEl.scrollWidth > graphTitleEl.offsetWidth) {
|
||||
this.showTitleTooltip = true;
|
||||
}
|
||||
},
|
||||
created() {
|
||||
this.setSvg('rocket');
|
||||
this.setSvg('scroll-handle');
|
||||
|
@ -387,24 +380,7 @@ export default {
|
|||
</script>
|
||||
|
||||
<template>
|
||||
<div v-gl-resize-observer-directive="onResize" class="prometheus-graph">
|
||||
<div class="prometheus-graph-header">
|
||||
<h5
|
||||
ref="graphTitle"
|
||||
class="prometheus-graph-title js-graph-title text-truncate append-right-8"
|
||||
>
|
||||
{{ graphData.title }}
|
||||
</h5>
|
||||
<gl-tooltip :target="() => $refs.graphTitle" :disabled="!showTitleTooltip">
|
||||
{{ graphData.title }}
|
||||
</gl-tooltip>
|
||||
<div
|
||||
class="prometheus-graph-widgets js-graph-widgets flex-fill"
|
||||
data-qa-selector="prometheus_graph_widgets"
|
||||
>
|
||||
<slot></slot>
|
||||
</div>
|
||||
</div>
|
||||
<div v-gl-resize-observer-directive="onResize">
|
||||
<component
|
||||
:is="glChartComponent"
|
||||
ref="chart"
|
||||
|
|
|
@ -3,10 +3,12 @@ import { mapState } from 'vuex';
|
|||
import { pickBy } from 'lodash';
|
||||
import invalidUrl from '~/lib/utils/invalid_url';
|
||||
import {
|
||||
GlResizeObserverDirective,
|
||||
GlDropdown,
|
||||
GlDropdownItem,
|
||||
GlModal,
|
||||
GlModalDirective,
|
||||
GlTooltip,
|
||||
GlTooltipDirective,
|
||||
} from '@gitlab/ui';
|
||||
import { __ } from '~/locale';
|
||||
|
@ -29,11 +31,13 @@ export default {
|
|||
MonitorStackedColumnChart,
|
||||
MonitorEmptyChart,
|
||||
Icon,
|
||||
GlTooltip,
|
||||
GlDropdown,
|
||||
GlDropdownItem,
|
||||
GlModal,
|
||||
},
|
||||
directives: {
|
||||
GlResizeObserver: GlResizeObserverDirective,
|
||||
GlModal: GlModalDirective,
|
||||
GlTooltip: GlTooltipDirective,
|
||||
TrackEvent: TrackEventDirective,
|
||||
|
@ -61,11 +65,15 @@ export default {
|
|||
},
|
||||
data() {
|
||||
return {
|
||||
showTitleTooltip: false,
|
||||
zoomedTimeRange: null,
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
...mapState('monitoringDashboard', ['deploymentData', 'projectPath', 'logsPath', 'timeRange']),
|
||||
title() {
|
||||
return this.graphData.title || '';
|
||||
},
|
||||
alertWidgetAvailable() {
|
||||
return IS_EE && this.prometheusAlertsAvailable && this.alertsEndpoint && this.graphData;
|
||||
},
|
||||
|
@ -97,12 +105,24 @@ export default {
|
|||
const data = new Blob([this.csvText], { type: 'text/plain' });
|
||||
return window.URL.createObjectURL(data);
|
||||
},
|
||||
monitorChartComponent() {
|
||||
timeChartComponent() {
|
||||
if (this.isPanelType('anomaly-chart')) {
|
||||
return MonitorAnomalyChart;
|
||||
}
|
||||
return MonitorTimeSeriesChart;
|
||||
},
|
||||
isContextualMenuShown() {
|
||||
return (
|
||||
this.graphDataHasMetrics &&
|
||||
!this.isPanelType('single-stat') &&
|
||||
!this.isPanelType('heatmap') &&
|
||||
!this.isPanelType('column') &&
|
||||
!this.isPanelType('stacked-column')
|
||||
);
|
||||
},
|
||||
},
|
||||
mounted() {
|
||||
this.refreshTitleTooltip();
|
||||
},
|
||||
methods: {
|
||||
getGraphAlerts(queries) {
|
||||
|
@ -119,9 +139,18 @@ export default {
|
|||
showToast() {
|
||||
this.$toast.show(__('Link copied'));
|
||||
},
|
||||
refreshTitleTooltip() {
|
||||
const { graphTitle } = this.$refs;
|
||||
this.showTitleTooltip =
|
||||
Boolean(graphTitle) && graphTitle.scrollWidth > graphTitle.offsetWidth;
|
||||
},
|
||||
|
||||
downloadCSVOptions,
|
||||
generateLinkToChartOptions,
|
||||
|
||||
onResize() {
|
||||
this.refreshTitleTooltip();
|
||||
},
|
||||
onDatazoom({ start, end }) {
|
||||
this.zoomedTimeRange = { start, end };
|
||||
},
|
||||
|
@ -129,88 +158,109 @@ export default {
|
|||
};
|
||||
</script>
|
||||
<template>
|
||||
<monitor-single-stat-chart
|
||||
v-if="isPanelType('single-stat') && graphDataHasMetrics"
|
||||
:graph-data="graphData"
|
||||
/>
|
||||
<monitor-heatmap-chart
|
||||
v-else-if="isPanelType('heatmap') && graphDataHasMetrics"
|
||||
:graph-data="graphData"
|
||||
/>
|
||||
<monitor-column-chart
|
||||
v-else-if="isPanelType('column') && graphDataHasMetrics"
|
||||
:graph-data="graphData"
|
||||
/>
|
||||
<monitor-stacked-column-chart
|
||||
v-else-if="isPanelType('stacked-column') && graphDataHasMetrics"
|
||||
:graph-data="graphData"
|
||||
/>
|
||||
<component
|
||||
:is="monitorChartComponent"
|
||||
v-else-if="graphDataHasMetrics"
|
||||
ref="timeChart"
|
||||
:graph-data="graphData"
|
||||
:deployment-data="deploymentData"
|
||||
:project-path="projectPath"
|
||||
:thresholds="getGraphAlertValues(graphData.metrics)"
|
||||
:group-id="groupId"
|
||||
@datazoom="onDatazoom"
|
||||
>
|
||||
<div class="d-flex align-items-center">
|
||||
<alert-widget
|
||||
v-if="alertWidgetAvailable && graphData"
|
||||
:modal-id="`alert-modal-${index}`"
|
||||
:alerts-endpoint="alertsEndpoint"
|
||||
:relevant-queries="graphData.metrics"
|
||||
:alerts-to-manage="getGraphAlerts(graphData.metrics)"
|
||||
@setAlerts="setAlerts"
|
||||
/>
|
||||
<gl-dropdown
|
||||
v-gl-tooltip
|
||||
class="ml-auto mx-3"
|
||||
toggle-class="btn btn-transparent border-0"
|
||||
data-qa-selector="prometheus_widgets_dropdown"
|
||||
:right="true"
|
||||
:no-caret="true"
|
||||
:title="__('More actions')"
|
||||
<div v-gl-resize-observer="onResize" class="prometheus-graph">
|
||||
<div class="prometheus-graph-header">
|
||||
<h5
|
||||
ref="graphTitle"
|
||||
class="prometheus-graph-title gl-font-size-large font-weight-bold text-truncate append-right-8"
|
||||
>
|
||||
<template slot="button-content">
|
||||
<icon name="ellipsis_v" class="text-secondary" />
|
||||
</template>
|
||||
{{ title }}
|
||||
</h5>
|
||||
<gl-tooltip :target="() => $refs.graphTitle" :disabled="!showTitleTooltip">
|
||||
{{ title }}
|
||||
</gl-tooltip>
|
||||
<div
|
||||
v-if="isContextualMenuShown"
|
||||
class="prometheus-graph-widgets js-graph-widgets flex-fill"
|
||||
data-qa-selector="prometheus_graph_widgets"
|
||||
>
|
||||
<div class="d-flex align-items-center">
|
||||
<alert-widget
|
||||
v-if="alertWidgetAvailable && graphData"
|
||||
:modal-id="`alert-modal-${index}`"
|
||||
:alerts-endpoint="alertsEndpoint"
|
||||
:relevant-queries="graphData.metrics"
|
||||
:alerts-to-manage="getGraphAlerts(graphData.metrics)"
|
||||
@setAlerts="setAlerts"
|
||||
/>
|
||||
<gl-dropdown
|
||||
v-gl-tooltip
|
||||
class="ml-auto mx-3"
|
||||
toggle-class="btn btn-transparent border-0"
|
||||
data-qa-selector="prometheus_widgets_dropdown"
|
||||
right
|
||||
no-caret
|
||||
:title="__('More actions')"
|
||||
>
|
||||
<template slot="button-content">
|
||||
<icon name="ellipsis_v" class="text-secondary" />
|
||||
</template>
|
||||
|
||||
<gl-dropdown-item
|
||||
v-if="logsPathWithTimeRange"
|
||||
ref="viewLogsLink"
|
||||
:href="logsPathWithTimeRange"
|
||||
>
|
||||
{{ s__('Metrics|View logs') }}
|
||||
</gl-dropdown-item>
|
||||
<gl-dropdown-item
|
||||
v-if="logsPathWithTimeRange"
|
||||
ref="viewLogsLink"
|
||||
:href="logsPathWithTimeRange"
|
||||
>
|
||||
{{ s__('Metrics|View logs') }}
|
||||
</gl-dropdown-item>
|
||||
|
||||
<gl-dropdown-item
|
||||
v-track-event="downloadCSVOptions(graphData.title)"
|
||||
:href="downloadCsv"
|
||||
download="chart_metrics.csv"
|
||||
>
|
||||
{{ __('Download CSV') }}
|
||||
</gl-dropdown-item>
|
||||
<gl-dropdown-item
|
||||
v-if="clipboardText"
|
||||
ref="copyChartLink"
|
||||
v-track-event="generateLinkToChartOptions(clipboardText)"
|
||||
:data-clipboard-text="clipboardText"
|
||||
@click="showToast(clipboardText)"
|
||||
>
|
||||
{{ __('Generate link to chart') }}
|
||||
</gl-dropdown-item>
|
||||
<gl-dropdown-item
|
||||
v-if="alertWidgetAvailable"
|
||||
v-gl-modal="`alert-modal-${index}`"
|
||||
data-qa-selector="alert_widget_menu_item"
|
||||
>
|
||||
{{ __('Alerts') }}
|
||||
</gl-dropdown-item>
|
||||
</gl-dropdown>
|
||||
<gl-dropdown-item
|
||||
v-if="csvText"
|
||||
ref="downloadCsvLink"
|
||||
v-track-event="downloadCSVOptions(title)"
|
||||
:href="downloadCsv"
|
||||
download="chart_metrics.csv"
|
||||
>
|
||||
{{ __('Download CSV') }}
|
||||
</gl-dropdown-item>
|
||||
<gl-dropdown-item
|
||||
v-if="clipboardText"
|
||||
ref="copyChartLink"
|
||||
v-track-event="generateLinkToChartOptions(clipboardText)"
|
||||
:data-clipboard-text="clipboardText"
|
||||
@click="showToast(clipboardText)"
|
||||
>
|
||||
{{ __('Generate link to chart') }}
|
||||
</gl-dropdown-item>
|
||||
<gl-dropdown-item
|
||||
v-if="alertWidgetAvailable"
|
||||
v-gl-modal="`alert-modal-${index}`"
|
||||
data-qa-selector="alert_widget_menu_item"
|
||||
>
|
||||
{{ __('Alerts') }}
|
||||
</gl-dropdown-item>
|
||||
</gl-dropdown>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</component>
|
||||
<monitor-empty-chart v-else :graph-title="graphData.title" />
|
||||
|
||||
<monitor-single-stat-chart
|
||||
v-if="isPanelType('single-stat') && graphDataHasMetrics"
|
||||
:graph-data="graphData"
|
||||
/>
|
||||
<monitor-heatmap-chart
|
||||
v-else-if="isPanelType('heatmap') && graphDataHasMetrics"
|
||||
:graph-data="graphData"
|
||||
/>
|
||||
<monitor-column-chart
|
||||
v-else-if="isPanelType('column') && graphDataHasMetrics"
|
||||
:graph-data="graphData"
|
||||
/>
|
||||
<monitor-stacked-column-chart
|
||||
v-else-if="isPanelType('stacked-column') && graphDataHasMetrics"
|
||||
:graph-data="graphData"
|
||||
/>
|
||||
<component
|
||||
:is="timeChartComponent"
|
||||
v-else-if="graphDataHasMetrics"
|
||||
ref="timeChart"
|
||||
:graph-data="graphData"
|
||||
:deployment-data="deploymentData"
|
||||
:project-path="projectPath"
|
||||
:thresholds="getGraphAlertValues(graphData.metrics)"
|
||||
:group-id="groupId"
|
||||
@datazoom="onDatazoom"
|
||||
/>
|
||||
<monitor-empty-chart v-else :graph-title="title" v-bind="$attrs" v-on="$listeners" />
|
||||
</div>
|
||||
</template>
|
||||
|
|
|
@ -1,15 +0,0 @@
|
|||
<script>
|
||||
export default {
|
||||
props: {
|
||||
graphTitle: {
|
||||
type: String,
|
||||
required: true,
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
<template>
|
||||
<div class="prometheus-graph-header">
|
||||
<h5 ref="title" class="prometheus-graph-title">{{ graphTitle }}</h5>
|
||||
</div>
|
||||
</template>
|
|
@ -91,10 +91,6 @@
|
|||
margin-bottom: $gl-padding-8;
|
||||
}
|
||||
|
||||
.prometheus-graph-title {
|
||||
font-size: $gl-font-size-large;
|
||||
}
|
||||
|
||||
.alert-current-setting {
|
||||
max-width: 240px;
|
||||
}
|
||||
|
|
35
app/finders/serverless_domain_finder.rb
Normal file
35
app/finders/serverless_domain_finder.rb
Normal file
|
@ -0,0 +1,35 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
class ServerlessDomainFinder
|
||||
attr_reader :match, :serverless_domain_cluster, :environment
|
||||
|
||||
def initialize(uri)
|
||||
@match = ::Serverless::Domain::REGEXP.match(uri)
|
||||
end
|
||||
|
||||
def execute
|
||||
return unless serverless?
|
||||
|
||||
@serverless_domain_cluster = ::Serverless::DomainCluster.for_uuid(serverless_domain_cluster_uuid)
|
||||
return unless serverless_domain_cluster
|
||||
|
||||
@environment = ::Environment.for_id_and_slug(match[:environment_id].to_i(16), match[:environment_slug])
|
||||
return unless environment
|
||||
|
||||
::Serverless::Domain.new(
|
||||
function_name: match[:function_name],
|
||||
serverless_domain_cluster: serverless_domain_cluster,
|
||||
environment: environment
|
||||
)
|
||||
end
|
||||
|
||||
def serverless_domain_cluster_uuid
|
||||
return unless serverless?
|
||||
|
||||
match[:cluster_left] + match[:cluster_middle] + match[:cluster_right]
|
||||
end
|
||||
|
||||
def serverless?
|
||||
!!match
|
||||
end
|
||||
end
|
|
@ -15,7 +15,7 @@ module Clusters
|
|||
def set_initial_status
|
||||
return unless not_installable?
|
||||
|
||||
self.status = status_states[:installable] if cluster&.application_helm_available? || Feature.enabled?(:managed_apps_local_tiller)
|
||||
self.status = status_states[:installable] if cluster&.application_helm_available? || ::Gitlab::Kubernetes::Helm.local_tiller_enabled?
|
||||
end
|
||||
|
||||
def can_uninstall?
|
||||
|
|
|
@ -23,7 +23,7 @@ module Clusters
|
|||
@files ||= begin
|
||||
files = { 'values.yaml': values }
|
||||
|
||||
files.merge!(certificate_files) if cluster.application_helm.has_ssl?
|
||||
files.merge!(certificate_files) if use_tiller_ssl?
|
||||
|
||||
files
|
||||
end
|
||||
|
@ -31,6 +31,12 @@ module Clusters
|
|||
|
||||
private
|
||||
|
||||
def use_tiller_ssl?
|
||||
return false if ::Gitlab::Kubernetes::Helm.local_tiller_enabled?
|
||||
|
||||
cluster.application_helm.has_ssl?
|
||||
end
|
||||
|
||||
def certificate_files
|
||||
{
|
||||
'ca.pem': ca_cert,
|
||||
|
|
|
@ -92,7 +92,10 @@ module Clusters
|
|||
# When installing any application we are also performing an update
|
||||
# of tiller (see Gitlab::Kubernetes::Helm::ClientCommand) so
|
||||
# therefore we need to reflect that in the database.
|
||||
application.cluster.application_helm.update!(version: Gitlab::Kubernetes::Helm::HELM_VERSION)
|
||||
|
||||
unless ::Gitlab::Kubernetes::Helm.local_tiller_enabled?
|
||||
application.cluster.application_helm.update!(version: Gitlab::Kubernetes::Helm::HELM_VERSION)
|
||||
end
|
||||
end
|
||||
|
||||
after_transition any => [:uninstalling], :use_transactions => false do |application, _|
|
||||
|
|
|
@ -95,6 +95,10 @@ class Environment < ApplicationRecord
|
|||
end
|
||||
end
|
||||
|
||||
def self.for_id_and_slug(id, slug)
|
||||
find_by(id: id, slug: slug)
|
||||
end
|
||||
|
||||
def self.max_deployment_id_sql
|
||||
Deployment.select(Deployment.arel_table[:id].maximum)
|
||||
.where(Deployment.arel_table[:environment_id].eq(arel_table[:id]))
|
||||
|
|
44
app/models/serverless/domain.rb
Normal file
44
app/models/serverless/domain.rb
Normal file
|
@ -0,0 +1,44 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
module Serverless
|
||||
class Domain
|
||||
include ActiveModel::Model
|
||||
|
||||
REGEXP = %r{^(?<scheme>https?://)?(?<function_name>[^.]+)-(?<cluster_left>\h{2})a1(?<cluster_middle>\h{10})f2(?<cluster_right>\h{2})(?<environment_id>\h+)-(?<environment_slug>[^.]+)\.(?<pages_domain_name>.+)}.freeze
|
||||
UUID_LENGTH = 14
|
||||
|
||||
attr_accessor :function_name, :serverless_domain_cluster, :environment
|
||||
|
||||
validates :function_name, presence: true, allow_blank: false
|
||||
validates :serverless_domain_cluster, presence: true
|
||||
validates :environment, presence: true
|
||||
|
||||
def self.generate_uuid
|
||||
SecureRandom.hex(UUID_LENGTH / 2)
|
||||
end
|
||||
|
||||
def uri
|
||||
URI("https://#{function_name}-#{serverless_domain_cluster_uuid}#{"%x" % environment.id}-#{environment.slug}.#{serverless_domain_cluster.domain}")
|
||||
end
|
||||
|
||||
def knative_uri
|
||||
URI("http://#{function_name}.#{namespace}.#{serverless_domain_cluster.knative.hostname}")
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def namespace
|
||||
serverless_domain_cluster.cluster.kubernetes_namespace_for(environment)
|
||||
end
|
||||
|
||||
def serverless_domain_cluster_uuid
|
||||
[
|
||||
serverless_domain_cluster.uuid[0..1],
|
||||
'a1',
|
||||
serverless_domain_cluster.uuid[2..-3],
|
||||
'f2',
|
||||
serverless_domain_cluster.uuid[-2..-1]
|
||||
].join
|
||||
end
|
||||
end
|
||||
end
|
|
@ -16,11 +16,18 @@ module Serverless
|
|||
algorithm: 'aes-256-gcm'
|
||||
|
||||
validates :pages_domain, :knative, presence: true
|
||||
validates :uuid, presence: true, uniqueness: true, length: { is: Gitlab::Serverless::Domain::UUID_LENGTH },
|
||||
validates :uuid, presence: true, uniqueness: true, length: { is: ::Serverless::Domain::UUID_LENGTH },
|
||||
format: { with: HEX_REGEXP, message: 'only allows hex characters' }
|
||||
|
||||
default_value_for(:uuid, allows_nil: false) { Gitlab::Serverless::Domain.generate_uuid }
|
||||
default_value_for(:uuid, allows_nil: false) { ::Serverless::Domain.generate_uuid }
|
||||
|
||||
delegate :domain, to: :pages_domain
|
||||
delegate :cluster, to: :knative
|
||||
|
||||
def self.for_uuid(uuid)
|
||||
joins(:pages_domain, :knative)
|
||||
.includes(:pages_domain, :knative)
|
||||
.find_by(uuid: uuid)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -1,4 +1,2 @@
|
|||
#badge-settings{ data: { api_endpoint_url: @badge_api_endpoint,
|
||||
docs_url: help_page_path('user/project/badges')} }
|
||||
.text-center.prepend-top-default
|
||||
= icon('spinner spin 2x')
|
||||
|
|
|
@ -0,0 +1,5 @@
|
|||
---
|
||||
title: Remove unused loading spinner from badge_settings partial
|
||||
merge_request: 25044
|
||||
author: nuwe1
|
||||
type: other
|
|
@ -13,7 +13,7 @@ The Packages feature allows GitLab to act as a repository for the following:
|
|||
| [Conan Repository](conan_repository/index.md) **(PREMIUM)** | The GitLab Conan Repository enables every project in GitLab to have its own space to store [Conan](https://conan.io/) packages. | 12.6+ |
|
||||
| [Maven Repository](maven_repository/index.md) **(PREMIUM)** | The GitLab Maven Repository enables every project in GitLab to have its own space to store [Maven](https://maven.apache.org/) packages. | 11.3+ |
|
||||
| [NPM Registry](npm_registry/index.md) **(PREMIUM)** | The GitLab NPM Registry enables every project in GitLab to have its own space to store [NPM](https://www.npmjs.com/) packages. | 11.7+ |
|
||||
| [NuGet Repository](nuget_repository/index.md) **(PREMIUM)** | *PLANNED* The GitLab NuGet Repository will enable every project in GitLab to have its own space to store [NuGet](https://www.nuget.org/) packages. | 12.8+ |
|
||||
| [NuGet Repository](nuget_repository/index.md) **(PREMIUM)** | The GitLab NuGet Repository will enable every project in GitLab to have its own space to store [NuGet](https://www.nuget.org/) packages. | 12.8+ |
|
||||
|
||||
## Suggested contributions
|
||||
|
||||
|
|
|
@ -280,7 +280,7 @@ page.
|
|||
To work with NPM commands within [GitLab CI](./../../../ci/README.md), you can use
|
||||
`CI_JOB_TOKEN` in place of the personal access token in your commands.
|
||||
|
||||
A simple example `gitlab-ci.yml` file for publishing NPM packages:
|
||||
A simple example `.gitlab-ci.yml` file for publishing NPM packages:
|
||||
|
||||
```yml
|
||||
image: node:latest
|
||||
|
|
|
@ -2,18 +2,21 @@
|
|||
|
||||
> [Introduced](https://gitlab.com/gitlab-org/gitlab/issues/20050) in [GitLab Premium](https://about.gitlab.com/pricing/) 12.8.
|
||||
|
||||
CAUTION: **Work in progress**
|
||||
This feature is in development, sections on uploading and installing packages will be coming soon, please follow along and help us make sure we're building the right solution for you in the [NuGet issue](https://gitlab.com/gitlab-org/gitlab/issues/20050).
|
||||
|
||||
With the GitLab NuGet Repository, every project can have its own space to store NuGet packages.
|
||||
|
||||
The GitLab NuGet Repository works with either [nuget CLI](https://www.nuget.org/) or [Visual Studio](https://visualstudio.microsoft.com/vs/).
|
||||
The GitLab NuGet Repository works with:
|
||||
|
||||
- [NuGet CLI](https://docs.microsoft.com/en-us/nuget/reference/nuget-exe-cli-reference)
|
||||
- [.NET Core CLI](https://docs.microsoft.com/en-us/dotnet/core/tools/)
|
||||
- [Visual Studio](https://visualstudio.microsoft.com/vs/)
|
||||
|
||||
## Setting up your development environment
|
||||
|
||||
You will need [nuget CLI](https://www.nuget.org/) 5.2 or above. Previous versions have not been tested against the GitLab NuGet Repository and might not work. You can install it by visiting the [downloads page](https://www.nuget.org/downloads).
|
||||
You will need [NuGet CLI 5.2 or later](https://www.nuget.org/downloads). Earlier versions have not been tested
|
||||
against the GitLab NuGet Repository and might not work. If you have [Visual Studio](https://visualstudio.microsoft.com/vs/),
|
||||
NuGet CLI is probably already installed.
|
||||
|
||||
If you have [Visual Studio](https://visualstudio.microsoft.com/vs/), [nuget CLI](https://www.nuget.org/) is probably already installed.
|
||||
Alternatively, you can use [.NET SDK 3.0 or later](https://dotnet.microsoft.com/download/dotnet-core/3.0), which installs NuGet CLI.
|
||||
|
||||
You can confirm that [nuget CLI](https://www.nuget.org/) is properly installed with:
|
||||
|
||||
|
@ -37,7 +40,7 @@ Available commands:
|
|||
|
||||
NOTE: **Note:**
|
||||
This option is available only if your GitLab administrator has
|
||||
[enabled support for the NuGet Repository](../../../administration/packages/index.md).**(PREMIUM ONLY)**
|
||||
[enabled support for the Package Registry](../../../administration/packages/index.md). **(PREMIUM ONLY)**
|
||||
|
||||
After the NuGet Repository is enabled, it will be available for all new projects
|
||||
by default. To enable it for existing projects, or if you want to disable it:
|
||||
|
@ -48,7 +51,7 @@ by default. To enable it for existing projects, or if you want to disable it:
|
|||
|
||||
You should then be able to see the **Packages** section on the left sidebar.
|
||||
|
||||
## Adding the GitLab NuGet Repository as a source to nuget
|
||||
## Adding the GitLab NuGet Repository as a source to NuGet
|
||||
|
||||
You will need the following:
|
||||
|
||||
|
@ -57,23 +60,23 @@ You will need the following:
|
|||
- A suitable name for your source.
|
||||
- Your project ID which can be found on the home page of your project.
|
||||
|
||||
You can now add a new source to nuget either using [nuget CLI](https://www.nuget.org/) or [Visual Studio](https://visualstudio.microsoft.com/vs/).
|
||||
You can now add a new source to NuGet with:
|
||||
|
||||
### Using nuget CLI
|
||||
- [NuGet CLI](#add-nuget-repository-source-with-nuget-cli)
|
||||
- [Visual Studio](#add-nuget-repository-source-with-visual-studio).
|
||||
- [.NET CLI](#add-nuget-repository-source-with-net-cli)
|
||||
|
||||
### Add NuGet Repository source with NuGet CLI
|
||||
|
||||
To add the GitLab NuGet Repository as a source with `nuget`:
|
||||
|
||||
```shell
|
||||
nuget source Add -Name <source_name> -Source "https://example.gitlab.com/api/v4/projects/<your_project_id>/packages/nuget/index.json" -UserName <gitlab_username> -Password <gitlab_token>
|
||||
nuget source Add -Name <source_name> -Source "https://gitlab-instance.example.com/api/v4/projects/<your_project_id>/packages/nuget/index.json" -UserName <gitlab_username> -Password <gitlab_personal_access_token>
|
||||
```
|
||||
|
||||
Replace:
|
||||
Where:
|
||||
|
||||
- `<source_name>` with your desired source name.
|
||||
- `<your_project_id>` with your project ID.
|
||||
- `<gitlab-username>` with your GitLab username.
|
||||
- `<gitlab-token>` with your personal access token.
|
||||
- `example.gitlab.com` with the URL of the GitLab instance you're using.
|
||||
- `<source_name>` is your desired source name.
|
||||
|
||||
For example:
|
||||
|
||||
|
@ -81,7 +84,7 @@ For example:
|
|||
nuget source Add -Name "GitLab" -Source "https//gitlab.example/api/v4/projects/10/packages/nuget/index.json" -UserName carol -Password 12345678asdf
|
||||
```
|
||||
|
||||
### Using Visual Studio
|
||||
### Add NuGet Repository source with Visual Studio
|
||||
|
||||
1. Open [Visual Studio](https://visualstudio.microsoft.com/vs/).
|
||||
1. Open the **FILE > OPTIONS** (Windows) or **Visual Studio > Preferences** (Mac OS).
|
||||
|
@ -102,3 +105,109 @@ nuget source Add -Name "GitLab" -Source "https//gitlab.example/api/v4/projects/1
|
|||
![Visual Studio NuGet source added](img/visual_studio_nuget_source_added.png)
|
||||
|
||||
In case of any warning, please make sure that the **Location**, **Username** and **Password** are correct.
|
||||
|
||||
### Add NuGet Repository source with .NET CLI
|
||||
|
||||
To add the GitLab NuGet Repository as a source for .NET, create a file named `nuget.config` in the root of your project with the following content:
|
||||
|
||||
```xml
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<configuration>
|
||||
<packageSources>
|
||||
<clear />
|
||||
<add key="gitlab" value="https://gitlab-instance.example.com/api/v4/projects/<your_project_id>/packages/nuget/index.json" />
|
||||
</packageSources>
|
||||
<packageSourceCredentials>
|
||||
<gitlab>
|
||||
<add key="Username" value="<gitlab_username>" />
|
||||
<add key="ClearTextPassword" value="<gitlab_personal_access_token>" />
|
||||
</gitlab>
|
||||
</packageSourceCredentials>
|
||||
</configuration>
|
||||
```
|
||||
|
||||
## Uploading packages
|
||||
|
||||
When uploading packages, note that:
|
||||
|
||||
- The maximum allowed size is 50 Megabytes.
|
||||
- If you upload the same package with the same version multiple times, each consecutive upload
|
||||
is saved as a separate file. When installing a package, GitLab will serve the most recent file.
|
||||
- When uploading packages to GitLab, they will not be displayed in the packages UI of your project
|
||||
immediately. It can take up to 10 minutes to process a package.
|
||||
|
||||
### Upload packages with NuGet CLI
|
||||
|
||||
This section assumes that your project is properly built and you already [created a NuGet package with NuGet CLI](https://docs.microsoft.com/en-us/nuget/create-packages/creating-a-package).
|
||||
Upload your package using the following command:
|
||||
|
||||
```shell
|
||||
nuget push <package_file> -Source <source_name>
|
||||
```
|
||||
|
||||
Where:
|
||||
|
||||
- `<package_file>` is your package filename, ending in `.nupkg`.
|
||||
- `<source_name>` is the [source name used during setup](#adding-the-gitlab-nuget-repository-as-a-source-to-nuget).
|
||||
|
||||
### Upload packages with .NET CLI
|
||||
|
||||
This section assumes that your project is properly built and you already [created a NuGet package with .NET CLI](https://docs.microsoft.com/en-us/nuget/create-packages/creating-a-package-dotnet-cli.).
|
||||
Upload your package using the following command:
|
||||
|
||||
```shell
|
||||
dotnet nuget push <package_file> --source <source_name>
|
||||
```
|
||||
|
||||
Where:
|
||||
|
||||
- `<package_file>` is your package filename, ending in `.nupkg`.
|
||||
- `<source_name>` is the [source name used during setup](#adding-the-gitlab-nuget-repository-as-a-source-to-nuget).
|
||||
|
||||
For example:
|
||||
|
||||
```shell
|
||||
dotnet nuget push MyPackage.1.0.0.nupkg --source gitlab
|
||||
```
|
||||
|
||||
## Install packages
|
||||
|
||||
### Install a package with NuGet CLI
|
||||
|
||||
CAUTION: **Warning:**
|
||||
By default, `nuget` checks the official source at `nuget.org` first. If you have a package in the
|
||||
GitLab NuGet Repository with the same name as a package at `nuget.org`, you must specify the source
|
||||
name or the wrong package will be installed.
|
||||
Install the latest version of a package using the following command:
|
||||
|
||||
```shell
|
||||
nuget install <package_id> -OutputDirectory <output_directory> \
|
||||
-Version <package_version> \
|
||||
-Source <source_name>
|
||||
```
|
||||
|
||||
Where:
|
||||
|
||||
- `<package_id>` is the package id.
|
||||
- `<output_directory>` is the output directory, where the package will be installed.
|
||||
- `<package_version>` (Optional) is the package version.
|
||||
- `<source_name>` (Optional) is the source name.
|
||||
|
||||
### Install a package with .NET CLI
|
||||
|
||||
CAUTION: **Warning:**
|
||||
If you have a package in the GitLab NuGet Repository with the same name as a package at a different source,
|
||||
you should verify the order in which `dotnet` checks sources during install. This is defined in the
|
||||
`nuget.config` file.
|
||||
|
||||
Install the latest version of a package using the following command:
|
||||
|
||||
```shell
|
||||
dotnet add package <package_id> \
|
||||
-v <package_version>
|
||||
```
|
||||
|
||||
Where:
|
||||
|
||||
- `<package_id>` is the package id.
|
||||
- `<package_version>` (Optional) is the package version.
|
||||
|
|
|
@ -10,6 +10,12 @@ module Gitlab
|
|||
SERVICE_ACCOUNT = 'tiller'
|
||||
CLUSTER_ROLE_BINDING = 'tiller-admin'
|
||||
CLUSTER_ROLE = 'cluster-admin'
|
||||
|
||||
MANAGED_APPS_LOCAL_TILLER_FEATURE_FLAG = :managed_apps_local_tiller
|
||||
|
||||
def self.local_tiller_enabled?
|
||||
Feature.enabled?(MANAGED_APPS_LOCAL_TILLER_FEATURE_FLAG)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -59,7 +59,7 @@ module Gitlab
|
|||
end
|
||||
|
||||
def local_tiller_enabled?
|
||||
Feature.enabled?(:managed_apps_local_tiller)
|
||||
::Gitlab::Kubernetes::Helm.local_tiller_enabled?
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -1,13 +0,0 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
module Gitlab
|
||||
module Serverless
|
||||
class Domain
|
||||
UUID_LENGTH = 14
|
||||
|
||||
def self.generate_uuid
|
||||
SecureRandom.hex(UUID_LENGTH / 2)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
|
@ -1,46 +0,0 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
module Gitlab
|
||||
module Serverless
|
||||
class FunctionURI < URI::HTTPS
|
||||
SERVERLESS_DOMAIN_REGEXP = %r{^(?<scheme>https?://)?(?<function>[^.]+)-(?<cluster_left>\h{2})a1(?<cluster_middle>\h{10})f2(?<cluster_right>\h{2})(?<environment_id>\h+)-(?<environment_slug>[^.]+)\.(?<domain>.+)}.freeze
|
||||
|
||||
attr_reader :function, :cluster, :environment
|
||||
|
||||
def initialize(function: nil, cluster: nil, environment: nil)
|
||||
initialize_required_argument(:function, function)
|
||||
initialize_required_argument(:cluster, cluster)
|
||||
initialize_required_argument(:environment, environment)
|
||||
|
||||
@host = "#{function}-#{cluster.uuid[0..1]}a1#{cluster.uuid[2..-3]}f2#{cluster.uuid[-2..-1]}#{"%x" % environment.id}-#{environment.slug}.#{cluster.domain}"
|
||||
|
||||
super('https', nil, host, nil, nil, nil, nil, nil, nil)
|
||||
end
|
||||
|
||||
def self.parse(uri)
|
||||
match = SERVERLESS_DOMAIN_REGEXP.match(uri)
|
||||
return unless match
|
||||
|
||||
cluster = ::Serverless::DomainCluster.find(match[:cluster_left] + match[:cluster_middle] + match[:cluster_right])
|
||||
return unless cluster
|
||||
|
||||
environment = ::Environment.find(match[:environment_id].to_i(16))
|
||||
return unless environment&.slug == match[:environment_slug]
|
||||
|
||||
new(
|
||||
function: match[:function],
|
||||
cluster: cluster,
|
||||
environment: environment
|
||||
)
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def initialize_required_argument(name, value)
|
||||
raise ArgumentError.new("missing argument: #{name}") unless value
|
||||
|
||||
instance_variable_set("@#{name}".to_sym, value)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
|
@ -60,7 +60,11 @@ class Gitlab::Serverless::Service
|
|||
|
||||
def proxy_url
|
||||
if cluster&.serverless_domain
|
||||
Gitlab::Serverless::FunctionURI.new(function: name, cluster: cluster.serverless_domain, environment: environment)
|
||||
::Serverless::Domain.new(
|
||||
function_name: name,
|
||||
serverless_domain_cluster: cluster.serverless_domain,
|
||||
environment: environment
|
||||
).uri.to_s
|
||||
end
|
||||
end
|
||||
|
||||
|
|
|
@ -12,11 +12,8 @@ module QA
|
|||
element :prometheus_graphs
|
||||
end
|
||||
|
||||
view 'app/assets/javascripts/monitoring/components/charts/time_series.vue' do
|
||||
element :prometheus_graph_widgets
|
||||
end
|
||||
|
||||
view 'app/assets/javascripts/monitoring/components/panel_type.vue' do
|
||||
element :prometheus_graph_widgets
|
||||
element :prometheus_widgets_dropdown
|
||||
element :alert_widget_menu_item
|
||||
end
|
||||
|
|
|
@ -135,7 +135,7 @@ describe Projects::Serverless::FunctionsController do
|
|||
|
||||
context 'when there is no serverless domain for a cluster' do
|
||||
it 'keeps function URL as it was' do
|
||||
expect(Gitlab::Serverless::Domain).not_to receive(:new)
|
||||
expect(::Serverless::Domain).not_to receive(:new)
|
||||
|
||||
get :index, params: params({ format: :json })
|
||||
expect(response).to have_gitlab_http_status(:ok)
|
||||
|
|
|
@ -73,39 +73,71 @@ FactoryBot.define do
|
|||
factory :clusters_applications_ingress, class: 'Clusters::Applications::Ingress' do
|
||||
modsecurity_enabled { false }
|
||||
cluster factory: %i(cluster with_installed_helm provided_by_gcp)
|
||||
|
||||
trait :no_helm_installed do
|
||||
cluster factory: %i(cluster provided_by_gcp)
|
||||
end
|
||||
end
|
||||
|
||||
factory :clusters_applications_cert_manager, class: 'Clusters::Applications::CertManager' do
|
||||
email { 'admin@example.com' }
|
||||
cluster factory: %i(cluster with_installed_helm provided_by_gcp)
|
||||
|
||||
trait :no_helm_installed do
|
||||
cluster factory: %i(cluster provided_by_gcp)
|
||||
end
|
||||
end
|
||||
|
||||
factory :clusters_applications_elastic_stack, class: 'Clusters::Applications::ElasticStack' do
|
||||
cluster factory: %i(cluster with_installed_helm provided_by_gcp)
|
||||
|
||||
trait :no_helm_installed do
|
||||
cluster factory: %i(cluster provided_by_gcp)
|
||||
end
|
||||
end
|
||||
|
||||
factory :clusters_applications_crossplane, class: 'Clusters::Applications::Crossplane' do
|
||||
stack { 'gcp' }
|
||||
cluster factory: %i(cluster with_installed_helm provided_by_gcp)
|
||||
|
||||
trait :no_helm_installed do
|
||||
cluster factory: %i(cluster provided_by_gcp)
|
||||
end
|
||||
end
|
||||
|
||||
factory :clusters_applications_prometheus, class: 'Clusters::Applications::Prometheus' do
|
||||
cluster factory: %i(cluster with_installed_helm provided_by_gcp)
|
||||
|
||||
trait :no_helm_installed do
|
||||
cluster factory: %i(cluster provided_by_gcp)
|
||||
end
|
||||
end
|
||||
|
||||
factory :clusters_applications_runner, class: 'Clusters::Applications::Runner' do
|
||||
runner factory: %i(ci_runner)
|
||||
cluster factory: %i(cluster with_installed_helm provided_by_gcp)
|
||||
|
||||
trait :no_helm_installed do
|
||||
cluster factory: %i(cluster provided_by_gcp)
|
||||
end
|
||||
end
|
||||
|
||||
factory :clusters_applications_knative, class: 'Clusters::Applications::Knative' do
|
||||
hostname { 'example.com' }
|
||||
cluster factory: %i(cluster with_installed_helm provided_by_gcp)
|
||||
|
||||
trait :no_helm_installed do
|
||||
cluster factory: %i(cluster provided_by_gcp)
|
||||
end
|
||||
end
|
||||
|
||||
factory :clusters_applications_jupyter, class: 'Clusters::Applications::Jupyter' do
|
||||
oauth_application factory: :oauth_application
|
||||
cluster factory: %i(cluster with_installed_helm provided_by_gcp project)
|
||||
|
||||
trait :no_helm_installed do
|
||||
cluster factory: %i(cluster provided_by_gcp)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
11
spec/factories/serverless/domain.rb
Normal file
11
spec/factories/serverless/domain.rb
Normal file
|
@ -0,0 +1,11 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
FactoryBot.define do
|
||||
factory :serverless_domain, class: '::Serverless::Domain' do
|
||||
function_name { 'test-function' }
|
||||
serverless_domain_cluster { create(:serverless_domain_cluster) }
|
||||
environment { create(:environment) }
|
||||
|
||||
skip_create
|
||||
end
|
||||
end
|
81
spec/finders/serverless_domain_finder_spec.rb
Normal file
81
spec/finders/serverless_domain_finder_spec.rb
Normal file
|
@ -0,0 +1,81 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
require 'spec_helper'
|
||||
|
||||
describe ServerlessDomainFinder do
|
||||
let(:function_name) { 'test-function' }
|
||||
let(:pages_domain_name) { 'serverless.gitlab.io' }
|
||||
let(:pages_domain) { create(:pages_domain, :instance_serverless, domain: pages_domain_name) }
|
||||
let!(:serverless_domain_cluster) { create(:serverless_domain_cluster, uuid: 'abcdef12345678', pages_domain: pages_domain) }
|
||||
let(:valid_cluster_uuid) { 'aba1cdef123456f278' }
|
||||
let(:invalid_cluster_uuid) { 'aba1cdef123456f178' }
|
||||
let!(:environment) { create(:environment, name: 'test') }
|
||||
|
||||
let(:valid_uri) { "https://#{function_name}-#{valid_cluster_uuid}#{"%x" % environment.id}-#{environment.slug}.#{pages_domain_name}" }
|
||||
let(:valid_fqdn) { "#{function_name}-#{valid_cluster_uuid}#{"%x" % environment.id}-#{environment.slug}.#{pages_domain_name}" }
|
||||
let(:invalid_uri) { "https://#{function_name}-#{invalid_cluster_uuid}#{"%x" % environment.id}-#{environment.slug}.#{pages_domain_name}" }
|
||||
|
||||
let(:valid_finder) { described_class.new(valid_uri) }
|
||||
let(:invalid_finder) { described_class.new(invalid_uri) }
|
||||
|
||||
describe '#serverless?' do
|
||||
context 'with a valid URI' do
|
||||
subject { valid_finder.serverless? }
|
||||
|
||||
it { is_expected.to be_truthy }
|
||||
end
|
||||
|
||||
context 'with an invalid URI' do
|
||||
subject { invalid_finder.serverless? }
|
||||
|
||||
it { is_expected.to be_falsy }
|
||||
end
|
||||
end
|
||||
|
||||
describe '#serverless_domain_cluster_uuid' do
|
||||
context 'with a valid URI' do
|
||||
subject { valid_finder.serverless_domain_cluster_uuid }
|
||||
|
||||
it { is_expected.to eq serverless_domain_cluster.uuid }
|
||||
end
|
||||
|
||||
context 'with an invalid URI' do
|
||||
subject { invalid_finder.serverless_domain_cluster_uuid }
|
||||
|
||||
it { is_expected.to be_nil }
|
||||
end
|
||||
end
|
||||
|
||||
describe '#execute' do
|
||||
context 'with a valid URI' do
|
||||
let(:serverless_domain) do
|
||||
create(
|
||||
:serverless_domain,
|
||||
function_name: function_name,
|
||||
serverless_domain_cluster: serverless_domain_cluster,
|
||||
environment: environment
|
||||
)
|
||||
end
|
||||
|
||||
subject { valid_finder.execute }
|
||||
|
||||
it 'has the correct function_name' do
|
||||
expect(subject.function_name).to eq function_name
|
||||
end
|
||||
|
||||
it 'has the correct serverless_domain_cluster' do
|
||||
expect(subject.serverless_domain_cluster).to eq serverless_domain_cluster
|
||||
end
|
||||
|
||||
it 'has the correct environment' do
|
||||
expect(subject.environment).to eq environment
|
||||
end
|
||||
end
|
||||
|
||||
context 'with an invalid URI' do
|
||||
subject { invalid_finder.execute }
|
||||
|
||||
it { is_expected.to be_nil }
|
||||
end
|
||||
end
|
||||
end
|
|
@ -11,7 +11,6 @@ import {
|
|||
} from '../../mock_data';
|
||||
import MonitorTimeSeriesChart from '~/monitoring/components/charts/time_series.vue';
|
||||
|
||||
const mockWidgets = 'mockWidgets';
|
||||
const mockProjectPath = `${TEST_HOST}${mockProjectDir}`;
|
||||
|
||||
jest.mock('~/lib/utils/icon_utils'); // mock getSvgIconPathContent
|
||||
|
@ -35,9 +34,6 @@ describe('Anomaly chart component', () => {
|
|||
const setupAnomalyChart = props => {
|
||||
wrapper = shallowMount(Anomaly, {
|
||||
propsData: { ...props },
|
||||
slots: {
|
||||
default: mockWidgets,
|
||||
},
|
||||
});
|
||||
};
|
||||
const findTimeSeries = () => wrapper.find(MonitorTimeSeriesChart);
|
||||
|
|
|
@ -13,14 +13,6 @@ describe('Empty Chart component', () => {
|
|||
});
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
emptyChart.destroy();
|
||||
});
|
||||
|
||||
it('render the chart title', () => {
|
||||
expect(emptyChart.find({ ref: 'graphTitle' }).text()).toBe(graphTitle);
|
||||
});
|
||||
|
||||
describe('Computed props', () => {
|
||||
it('sets the height for the svg container', () => {
|
||||
expect(emptyChart.vm.svgContainerStyle.height).toBe('300px');
|
||||
|
|
|
@ -16,8 +16,6 @@ import {
|
|||
} from '../../mock_data';
|
||||
import * as iconUtils from '~/lib/utils/icon_utils';
|
||||
|
||||
const mockWidgets = 'mockWidgets';
|
||||
|
||||
const mockSvgPathContent = 'mockSvgPathContent';
|
||||
|
||||
jest.mock('lodash/throttle', () =>
|
||||
|
@ -65,9 +63,6 @@ describe('Time series component', () => {
|
|||
deploymentData: store.state.monitoringDashboard.deploymentData,
|
||||
projectPath: `${mockHost}${mockProjectDir}`,
|
||||
},
|
||||
slots: {
|
||||
default: mockWidgets,
|
||||
},
|
||||
store,
|
||||
});
|
||||
});
|
||||
|
@ -82,14 +77,6 @@ describe('Time series component', () => {
|
|||
timeSeriesChart.vm.$nextTick(done);
|
||||
});
|
||||
|
||||
it('renders chart title', () => {
|
||||
expect(timeSeriesChart.find('.js-graph-title').text()).toBe(mockGraphData.title);
|
||||
});
|
||||
|
||||
it('contains graph widgets from slot', () => {
|
||||
expect(timeSeriesChart.find('.js-graph-widgets').text()).toBe(mockWidgets);
|
||||
});
|
||||
|
||||
it('allows user to override max value label text using prop', () => {
|
||||
timeSeriesChart.setProps({ legendMaxText: 'legendMaxText' });
|
||||
|
||||
|
|
|
@ -74,6 +74,18 @@ describe('Panel Type component', () => {
|
|||
glEmptyChart = wrapper.find(EmptyChart);
|
||||
});
|
||||
|
||||
it('renders the chart title', () => {
|
||||
expect(wrapper.find({ ref: 'graphTitle' }).text()).toBe(graphDataNoResult.title);
|
||||
});
|
||||
|
||||
it('renders the no download csv link', () => {
|
||||
expect(wrapper.find({ ref: 'downloadCsvLink' }).exists()).toBe(false);
|
||||
});
|
||||
|
||||
it('does not contain graph widgets', () => {
|
||||
expect(wrapper.find('.js-graph-widgets').exists()).toBe(false);
|
||||
});
|
||||
|
||||
it('is a Vue instance', () => {
|
||||
expect(glEmptyChart.isVueInstance()).toBe(true);
|
||||
});
|
||||
|
@ -97,6 +109,15 @@ describe('Panel Type component', () => {
|
|||
wrapper.destroy();
|
||||
});
|
||||
|
||||
it('renders the chart title', () => {
|
||||
expect(wrapper.find({ ref: 'graphTitle' }).text()).toBe(graphDataPrometheusQueryRange.title);
|
||||
});
|
||||
|
||||
it('contains graph widgets', () => {
|
||||
expect(wrapper.find('.js-graph-widgets').exists()).toBe(true);
|
||||
expect(wrapper.find({ ref: 'downloadCsvLink' }).exists()).toBe(true);
|
||||
});
|
||||
|
||||
it('sets no clipboard copy link on dropdown by default', () => {
|
||||
expect(findCopyLink().exists()).toBe(false);
|
||||
});
|
||||
|
|
|
@ -1,26 +0,0 @@
|
|||
import { shallowMount } from '@vue/test-utils';
|
||||
import PrometheusHeader from '~/monitoring/components/shared/prometheus_header.vue';
|
||||
|
||||
describe('Prometheus Header component', () => {
|
||||
let prometheusHeader;
|
||||
|
||||
beforeEach(() => {
|
||||
prometheusHeader = shallowMount(PrometheusHeader, {
|
||||
propsData: {
|
||||
graphTitle: 'graph header',
|
||||
},
|
||||
});
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
prometheusHeader.destroy();
|
||||
});
|
||||
|
||||
describe('Prometheus header component', () => {
|
||||
it('should show a title', () => {
|
||||
const title = prometheusHeader.find({ ref: 'title' }).text();
|
||||
|
||||
expect(title).toBe('graph header');
|
||||
});
|
||||
});
|
||||
});
|
|
@ -1,22 +0,0 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
require 'spec_helper'
|
||||
|
||||
describe Gitlab::Serverless::Domain do
|
||||
describe '.generate_uuid' do
|
||||
it 'has 14 characters' do
|
||||
expect(described_class.generate_uuid.length).to eq(described_class::UUID_LENGTH)
|
||||
end
|
||||
|
||||
it 'consists of only hexadecimal characters' do
|
||||
expect(described_class.generate_uuid).to match(/\A\h+\z/)
|
||||
end
|
||||
|
||||
it 'uses random characters' do
|
||||
uuid = 'abcd1234567890'
|
||||
|
||||
expect(SecureRandom).to receive(:hex).with(described_class::UUID_LENGTH / 2).and_return(uuid)
|
||||
expect(described_class.generate_uuid).to eq(uuid)
|
||||
end
|
||||
end
|
||||
end
|
|
@ -1,81 +0,0 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
require 'spec_helper'
|
||||
|
||||
describe Gitlab::Serverless::FunctionURI do
|
||||
let(:function) { 'test-function' }
|
||||
let(:domain) { 'serverless.gitlab.io' }
|
||||
let(:pages_domain) { create(:pages_domain, :instance_serverless, domain: domain) }
|
||||
let!(:cluster) { create(:serverless_domain_cluster, uuid: 'abcdef12345678', pages_domain: pages_domain) }
|
||||
let(:valid_cluster) { 'aba1cdef123456f278' }
|
||||
let(:invalid_cluster) { 'aba1cdef123456f178' }
|
||||
let!(:environment) { create(:environment, name: 'test') }
|
||||
|
||||
let(:valid_uri) { "https://#{function}-#{valid_cluster}#{"%x" % environment.id}-#{environment.slug}.#{domain}" }
|
||||
let(:valid_fqdn) { "#{function}-#{valid_cluster}#{"%x" % environment.id}-#{environment.slug}.#{domain}" }
|
||||
let(:invalid_uri) { "https://#{function}-#{invalid_cluster}#{"%x" % environment.id}-#{environment.slug}.#{domain}" }
|
||||
|
||||
shared_examples 'a valid FunctionURI class' do
|
||||
describe '#to_s' do
|
||||
it 'matches valid URI' do
|
||||
expect(subject.to_s).to eq valid_uri
|
||||
end
|
||||
end
|
||||
|
||||
describe '#function' do
|
||||
it 'returns function' do
|
||||
expect(subject.function).to eq function
|
||||
end
|
||||
end
|
||||
|
||||
describe '#cluster' do
|
||||
it 'returns cluster' do
|
||||
expect(subject.cluster).to eq cluster
|
||||
end
|
||||
end
|
||||
|
||||
describe '#environment' do
|
||||
it 'returns environment' do
|
||||
expect(subject.environment).to eq environment
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe '.new' do
|
||||
context 'with valid arguments' do
|
||||
subject { described_class.new(function: function, cluster: cluster, environment: environment) }
|
||||
|
||||
it_behaves_like 'a valid FunctionURI class'
|
||||
end
|
||||
|
||||
context 'with invalid arguments' do
|
||||
subject { described_class.new(function: function, environment: environment) }
|
||||
|
||||
it 'raises an exception' do
|
||||
expect { subject }.to raise_error(ArgumentError)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe '.parse' do
|
||||
context 'with valid URI' do
|
||||
subject { described_class.parse(valid_uri) }
|
||||
|
||||
it_behaves_like 'a valid FunctionURI class'
|
||||
end
|
||||
|
||||
context 'with valid FQDN' do
|
||||
subject { described_class.parse(valid_fqdn) }
|
||||
|
||||
it_behaves_like 'a valid FunctionURI class'
|
||||
end
|
||||
|
||||
context 'with invalid URI' do
|
||||
subject { described_class.parse(invalid_uri) }
|
||||
|
||||
it 'returns nil' do
|
||||
expect(subject).to be_nil
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
|
@ -94,17 +94,19 @@ describe Gitlab::Serverless::Service do
|
|||
end
|
||||
|
||||
describe '#url' do
|
||||
let(:serverless_domain) { instance_double(::Serverless::Domain, uri: URI('https://proxy.example.com')) }
|
||||
|
||||
it 'returns proxy URL if cluster has serverless domain' do
|
||||
# cluster = create(:cluster)
|
||||
knative = create(:clusters_applications_knative, :installed, cluster: cluster)
|
||||
create(:serverless_domain_cluster, clusters_applications_knative_id: knative.id)
|
||||
service = Gitlab::Serverless::Service.new(attributes.merge('cluster' => cluster))
|
||||
|
||||
expect(Gitlab::Serverless::FunctionURI).to receive(:new).with(
|
||||
function: service.name,
|
||||
cluster: service.cluster.serverless_domain,
|
||||
expect(::Serverless::Domain).to receive(:new).with(
|
||||
function_name: service.name,
|
||||
serverless_domain_cluster: service.cluster.serverless_domain,
|
||||
environment: service.environment
|
||||
).and_return('https://proxy.example.com')
|
||||
).and_return(serverless_domain)
|
||||
|
||||
expect(service.url).to eq('https://proxy.example.com')
|
||||
end
|
||||
|
|
|
@ -274,7 +274,8 @@ describe Clusters::Applications::Prometheus do
|
|||
subject { application.files_with_replaced_values({ hello: :world }) }
|
||||
|
||||
it 'does not modify #files' do
|
||||
expect(subject[:'values.yaml']).not_to eq(files)
|
||||
expect(subject[:'values.yaml']).not_to eq(files[:'values.yaml'])
|
||||
|
||||
expect(files[:'values.yaml']).to eq(application.values)
|
||||
end
|
||||
|
||||
|
@ -282,27 +283,17 @@ describe Clusters::Applications::Prometheus do
|
|||
expect(subject[:'values.yaml']).to eq({ hello: :world })
|
||||
end
|
||||
|
||||
it 'includes cert files' do
|
||||
expect(subject[:'ca.pem']).to be_present
|
||||
expect(subject[:'ca.pem']).to eq(application.cluster.application_helm.ca_cert)
|
||||
it 'uses values from #files, except for values.yaml' do
|
||||
allow(application).to receive(:files).and_return({
|
||||
'values.yaml': 'some value specific to files',
|
||||
'file_a.txt': 'file_a',
|
||||
'file_b.txt': 'file_b'
|
||||
})
|
||||
|
||||
expect(subject[:'cert.pem']).to be_present
|
||||
expect(subject[:'key.pem']).to be_present
|
||||
|
||||
cert = OpenSSL::X509::Certificate.new(subject[:'cert.pem'])
|
||||
expect(cert.not_after).to be < 60.minutes.from_now
|
||||
end
|
||||
|
||||
context 'when the helm application does not have a ca_cert' do
|
||||
before do
|
||||
application.cluster.application_helm.ca_cert = nil
|
||||
end
|
||||
|
||||
it 'does not include cert files' do
|
||||
expect(subject[:'ca.pem']).not_to be_present
|
||||
expect(subject[:'cert.pem']).not_to be_present
|
||||
expect(subject[:'key.pem']).not_to be_present
|
||||
end
|
||||
expect(subject.except(:'values.yaml')).to eq({
|
||||
'file_a.txt': 'file_a',
|
||||
'file_b.txt': 'file_b'
|
||||
})
|
||||
end
|
||||
end
|
||||
|
||||
|
|
|
@ -1264,6 +1264,14 @@ describe Environment, :use_clean_rails_memory_store_caching do
|
|||
end
|
||||
end
|
||||
|
||||
describe '.for_id_and_slug' do
|
||||
subject { described_class.for_id_and_slug(environment.id, environment.slug) }
|
||||
|
||||
let(:environment) { create(:environment) }
|
||||
|
||||
it { is_expected.not_to be_nil }
|
||||
end
|
||||
|
||||
describe '.find_or_create_by_name' do
|
||||
it 'finds an existing environment if it exists' do
|
||||
env = create(:environment)
|
||||
|
|
|
@ -10,7 +10,7 @@ describe ::Serverless::DomainCluster do
|
|||
it { is_expected.to validate_presence_of(:knative) }
|
||||
|
||||
it { is_expected.to validate_presence_of(:uuid) }
|
||||
it { is_expected.to validate_length_of(:uuid).is_equal_to(Gitlab::Serverless::Domain::UUID_LENGTH) }
|
||||
it { is_expected.to validate_length_of(:uuid).is_equal_to(::Serverless::Domain::UUID_LENGTH) }
|
||||
it { is_expected.to validate_uniqueness_of(:uuid) }
|
||||
|
||||
it 'validates that uuid has only hex characters' do
|
||||
|
@ -31,7 +31,7 @@ describe ::Serverless::DomainCluster do
|
|||
context 'when nil' do
|
||||
it 'generates a value by default' do
|
||||
attributes = build(:serverless_domain_cluster).attributes.merge(uuid: nil)
|
||||
expect(Gitlab::Serverless::Domain).to receive(:generate_uuid).and_call_original
|
||||
expect(::Serverless::Domain).to receive(:generate_uuid).and_call_original
|
||||
|
||||
subject = Serverless::DomainCluster.new(attributes)
|
||||
|
||||
|
@ -47,6 +47,10 @@ describe ::Serverless::DomainCluster do
|
|||
end
|
||||
end
|
||||
|
||||
describe 'cluster' do
|
||||
it { is_expected.to respond_to(:cluster) }
|
||||
end
|
||||
|
||||
describe 'domain' do
|
||||
it { is_expected.to respond_to(:domain) }
|
||||
end
|
||||
|
|
97
spec/models/serverless/domain_spec.rb
Normal file
97
spec/models/serverless/domain_spec.rb
Normal file
|
@ -0,0 +1,97 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
require 'spec_helper'
|
||||
|
||||
describe ::Serverless::Domain do
|
||||
let(:function_name) { 'test-function' }
|
||||
let(:pages_domain_name) { 'serverless.gitlab.io' }
|
||||
let(:pages_domain) { create(:pages_domain, :instance_serverless, domain: pages_domain_name) }
|
||||
let!(:serverless_domain_cluster) { create(:serverless_domain_cluster, uuid: 'abcdef12345678', pages_domain: pages_domain) }
|
||||
let(:valid_cluster_uuid) { 'aba1cdef123456f278' }
|
||||
let(:invalid_cluster_uuid) { 'aba1cdef123456f178' }
|
||||
let!(:environment) { create(:environment, name: 'test') }
|
||||
|
||||
let(:valid_uri) { "https://#{function_name}-#{valid_cluster_uuid}#{"%x" % environment.id}-#{environment.slug}.#{pages_domain_name}" }
|
||||
let(:valid_fqdn) { "#{function_name}-#{valid_cluster_uuid}#{"%x" % environment.id}-#{environment.slug}.#{pages_domain_name}" }
|
||||
let(:invalid_uri) { "https://#{function_name}-#{invalid_cluster_uuid}#{"%x" % environment.id}-#{environment.slug}.#{pages_domain_name}" }
|
||||
|
||||
shared_examples 'a valid Domain' do
|
||||
describe '#uri' do
|
||||
it 'matches valid URI' do
|
||||
expect(subject.uri.to_s).to eq valid_uri
|
||||
end
|
||||
end
|
||||
|
||||
describe '#function_name' do
|
||||
it 'returns function_name' do
|
||||
expect(subject.function_name).to eq function_name
|
||||
end
|
||||
end
|
||||
|
||||
describe '#serverless_domain_cluster' do
|
||||
it 'returns serverless_domain_cluster' do
|
||||
expect(subject.serverless_domain_cluster).to eq serverless_domain_cluster
|
||||
end
|
||||
end
|
||||
|
||||
describe '#environment' do
|
||||
it 'returns environment' do
|
||||
expect(subject.environment).to eq environment
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe '.new' do
|
||||
context 'with valid arguments' do
|
||||
subject do
|
||||
described_class.new(
|
||||
function_name: function_name,
|
||||
serverless_domain_cluster: serverless_domain_cluster,
|
||||
environment: environment
|
||||
)
|
||||
end
|
||||
|
||||
it_behaves_like 'a valid Domain'
|
||||
end
|
||||
|
||||
context 'with invalid arguments' do
|
||||
subject do
|
||||
described_class.new(
|
||||
function_name: function_name,
|
||||
environment: environment
|
||||
)
|
||||
end
|
||||
|
||||
it { is_expected.not_to be_valid }
|
||||
end
|
||||
|
||||
context 'with nil cluster argument' do
|
||||
subject do
|
||||
described_class.new(
|
||||
function_name: function_name,
|
||||
serverless_domain_cluster: nil,
|
||||
environment: environment
|
||||
)
|
||||
end
|
||||
|
||||
it { is_expected.not_to be_valid }
|
||||
end
|
||||
end
|
||||
|
||||
describe '.generate_uuid' do
|
||||
it 'has 14 characters' do
|
||||
expect(described_class.generate_uuid.length).to eq(described_class::UUID_LENGTH)
|
||||
end
|
||||
|
||||
it 'consists of only hexadecimal characters' do
|
||||
expect(described_class.generate_uuid).to match(/\A\h+\z/)
|
||||
end
|
||||
|
||||
it 'uses random characters' do
|
||||
uuid = 'abcd1234567890'
|
||||
|
||||
expect(SecureRandom).to receive(:hex).with(described_class::UUID_LENGTH / 2).and_return(uuid)
|
||||
expect(described_class.generate_uuid).to eq(uuid)
|
||||
end
|
||||
end
|
||||
end
|
|
@ -28,22 +28,46 @@ RSpec.shared_examples 'cluster application helm specs' do |application_name|
|
|||
describe '#files' do
|
||||
subject { application.files }
|
||||
|
||||
context 'when the helm application does not have a ca_cert' do
|
||||
context 'managed_apps_local_tiller feature flag is disabled' do
|
||||
before do
|
||||
application.cluster.application_helm.ca_cert = nil
|
||||
stub_feature_flags(managed_apps_local_tiller: false)
|
||||
end
|
||||
|
||||
it 'does not include cert files when there is no ca_cert entry' do
|
||||
expect(subject).not_to include(:'ca.pem', :'cert.pem', :'key.pem')
|
||||
context 'when the helm application does not have a ca_cert' do
|
||||
before do
|
||||
application.cluster.application_helm.ca_cert = nil
|
||||
end
|
||||
|
||||
it 'does not include cert files when there is no ca_cert entry' do
|
||||
expect(subject).not_to include(:'ca.pem', :'cert.pem', :'key.pem')
|
||||
end
|
||||
end
|
||||
|
||||
it 'includes cert files when there is a ca_cert entry' do
|
||||
expect(subject).to include(:'ca.pem', :'cert.pem', :'key.pem')
|
||||
expect(subject[:'ca.pem']).to eq(application.cluster.application_helm.ca_cert)
|
||||
|
||||
cert = OpenSSL::X509::Certificate.new(subject[:'cert.pem'])
|
||||
expect(cert.not_after).to be < 60.minutes.from_now
|
||||
end
|
||||
end
|
||||
|
||||
it 'includes cert files when there is a ca_cert entry' do
|
||||
expect(subject).to include(:'ca.pem', :'cert.pem', :'key.pem')
|
||||
expect(subject[:'ca.pem']).to eq(application.cluster.application_helm.ca_cert)
|
||||
context 'managed_apps_local_tiller feature flag is enabled' do
|
||||
before do
|
||||
stub_feature_flags(managed_apps_local_tiller: true)
|
||||
end
|
||||
|
||||
cert = OpenSSL::X509::Certificate.new(subject[:'cert.pem'])
|
||||
expect(cert.not_after).to be < 60.minutes.from_now
|
||||
it 'does not include cert files' do
|
||||
expect(subject).not_to include(:'ca.pem', :'cert.pem', :'key.pem')
|
||||
end
|
||||
|
||||
context 'when cluster does not have helm installed' do
|
||||
let(:application) { create(application_name, :no_helm_installed) }
|
||||
|
||||
it 'does not include cert files' do
|
||||
expect(subject).not_to include(:'ca.pem', :'cert.pem', :'key.pem')
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -48,14 +48,44 @@ RSpec.shared_examples 'cluster application status specs' do |application_name|
|
|||
expect(subject).to be_installed
|
||||
end
|
||||
|
||||
it 'updates helm version' do
|
||||
subject.cluster.application_helm.update!(version: '1.2.3')
|
||||
context 'managed_apps_local_tiller feature flag disabled' do
|
||||
before do
|
||||
stub_feature_flags(managed_apps_local_tiller: false)
|
||||
end
|
||||
|
||||
subject.make_installed!
|
||||
it 'updates helm version' do
|
||||
subject.cluster.application_helm.update!(version: '1.2.3')
|
||||
|
||||
subject.cluster.application_helm.reload
|
||||
subject.make_installed!
|
||||
|
||||
expect(subject.cluster.application_helm.version).to eq(Gitlab::Kubernetes::Helm::HELM_VERSION)
|
||||
subject.cluster.application_helm.reload
|
||||
|
||||
expect(subject.cluster.application_helm.version).to eq(Gitlab::Kubernetes::Helm::HELM_VERSION)
|
||||
end
|
||||
end
|
||||
|
||||
context 'managed_apps_local_tiller feature flag enabled' do
|
||||
before do
|
||||
stub_feature_flags(managed_apps_local_tiller: true)
|
||||
end
|
||||
|
||||
it 'does not update the helm version' do
|
||||
subject.cluster.application_helm.update!(version: '1.2.3')
|
||||
|
||||
expect do
|
||||
subject.make_installed!
|
||||
|
||||
subject.cluster.application_helm.reload
|
||||
end.not_to change { subject.cluster.application_helm.version }
|
||||
end
|
||||
|
||||
context 'the cluster has no helm installed' do
|
||||
subject { create(application_name, :installing, :no_helm_installed) }
|
||||
|
||||
it 'runs without errors' do
|
||||
expect { subject.make_installed! }.not_to raise_error
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
it 'sets the correct version of the application' do
|
||||
|
@ -77,14 +107,44 @@ RSpec.shared_examples 'cluster application status specs' do |application_name|
|
|||
expect(subject).to be_updated
|
||||
end
|
||||
|
||||
it 'updates helm version' do
|
||||
subject.cluster.application_helm.update!(version: '1.2.3')
|
||||
context 'managed_apps_local_tiller feature flag disabled' do
|
||||
before do
|
||||
stub_feature_flags(managed_apps_local_tiller: false)
|
||||
end
|
||||
|
||||
subject.make_installed!
|
||||
it 'updates helm version' do
|
||||
subject.cluster.application_helm.update!(version: '1.2.3')
|
||||
|
||||
subject.cluster.application_helm.reload
|
||||
subject.make_installed!
|
||||
|
||||
expect(subject.cluster.application_helm.version).to eq(Gitlab::Kubernetes::Helm::HELM_VERSION)
|
||||
subject.cluster.application_helm.reload
|
||||
|
||||
expect(subject.cluster.application_helm.version).to eq(Gitlab::Kubernetes::Helm::HELM_VERSION)
|
||||
end
|
||||
end
|
||||
|
||||
context 'managed_apps_local_tiller feature flag enabled' do
|
||||
before do
|
||||
stub_feature_flags(managed_apps_local_tiller: true)
|
||||
end
|
||||
|
||||
it 'does not update the helm version' do
|
||||
subject.cluster.application_helm.update!(version: '1.2.3')
|
||||
|
||||
expect do
|
||||
subject.make_installed!
|
||||
|
||||
subject.cluster.application_helm.reload
|
||||
end.not_to change { subject.cluster.application_helm.version }
|
||||
end
|
||||
|
||||
context 'the cluster has no helm installed' do
|
||||
subject { create(application_name, :updating, :no_helm_installed) }
|
||||
|
||||
it 'runs without errors' do
|
||||
expect { subject.make_installed! }.not_to raise_error
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
it 'updates the version of the application' do
|
||||
|
|
Loading…
Reference in a new issue