Merge branch 'list-multiple-clusters' into 'master'
List multiple clusters See merge request gitlab-org/gitlab-ce!15403
This commit is contained in:
commit
29c243b08d
|
@ -150,8 +150,8 @@ export default class Clusters {
|
||||||
}
|
}
|
||||||
|
|
||||||
toggle() {
|
toggle() {
|
||||||
this.toggleButton.classList.toggle('checked');
|
this.toggleButton.classList.toggle('is-checked');
|
||||||
this.toggleInput.setAttribute('value', this.toggleButton.classList.contains('checked').toString());
|
this.toggleInput.setAttribute('value', this.toggleButton.classList.contains('is-checked').toString());
|
||||||
}
|
}
|
||||||
|
|
||||||
showToken() {
|
showToken() {
|
||||||
|
|
|
@ -0,0 +1,58 @@
|
||||||
|
import Flash from '../flash';
|
||||||
|
import { s__ } from '../locale';
|
||||||
|
import ClustersService from './services/clusters_service';
|
||||||
|
/**
|
||||||
|
* Toggles loading and disabled classes.
|
||||||
|
* @param {HTMLElement} button
|
||||||
|
*/
|
||||||
|
const toggleLoadingButton = (button) => {
|
||||||
|
if (button.getAttribute('disabled')) {
|
||||||
|
button.removeAttribute('disabled');
|
||||||
|
} else {
|
||||||
|
button.setAttribute('disabled', true);
|
||||||
|
}
|
||||||
|
|
||||||
|
button.classList.toggle('is-loading');
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Toggles checked class for the given button
|
||||||
|
* @param {HTMLElement} button
|
||||||
|
*/
|
||||||
|
const toggleValue = (button) => {
|
||||||
|
button.classList.toggle('is-checked');
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Handles toggle buttons in the cluster's table.
|
||||||
|
*
|
||||||
|
* When the user clicks the toggle button for each cluster, it:
|
||||||
|
* - toggles the button
|
||||||
|
* - shows a loading and disables button
|
||||||
|
* - Makes a put request to the given endpoint
|
||||||
|
* Once we receive the response, either:
|
||||||
|
* 1) Show updated status in case of successfull response
|
||||||
|
* 2) Show initial status in case of failed response
|
||||||
|
*/
|
||||||
|
export default function setClusterTableToggles() {
|
||||||
|
document.querySelectorAll('.js-toggle-cluster-list')
|
||||||
|
.forEach(button => button.addEventListener('click', (e) => {
|
||||||
|
const toggleButton = e.currentTarget;
|
||||||
|
const endpoint = toggleButton.getAttribute('data-endpoint');
|
||||||
|
|
||||||
|
toggleValue(toggleButton);
|
||||||
|
toggleLoadingButton(toggleButton);
|
||||||
|
|
||||||
|
const value = toggleButton.classList.contains('is-checked');
|
||||||
|
|
||||||
|
ClustersService.updateCluster(endpoint, { cluster: { enabled: value } })
|
||||||
|
.then(() => {
|
||||||
|
toggleLoadingButton(toggleButton);
|
||||||
|
})
|
||||||
|
.catch(() => {
|
||||||
|
toggleLoadingButton(toggleButton);
|
||||||
|
toggleValue(toggleButton);
|
||||||
|
Flash(s__('ClusterIntegration|Something went wrong on our end.'));
|
||||||
|
});
|
||||||
|
}));
|
||||||
|
}
|
|
@ -17,4 +17,8 @@ export default class ClusterService {
|
||||||
installApplication(appId) {
|
installApplication(appId) {
|
||||||
return axios.post(this.appInstallEndpointMap[appId]);
|
return axios.post(this.appInstallEndpointMap[appId]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static updateCluster(endpoint, data) {
|
||||||
|
return axios.put(endpoint, data);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -562,7 +562,15 @@ import ProjectVariables from './project_variables';
|
||||||
import(/* webpackChunkName: "clusters" */ './clusters/clusters_bundle')
|
import(/* webpackChunkName: "clusters" */ './clusters/clusters_bundle')
|
||||||
.then(cluster => new cluster.default()) // eslint-disable-line new-cap
|
.then(cluster => new cluster.default()) // eslint-disable-line new-cap
|
||||||
.catch((err) => {
|
.catch((err) => {
|
||||||
Flash(s__('ClusterIntegration|Problem setting up the cluster JavaScript'));
|
Flash(s__('ClusterIntegration|Problem setting up the cluster'));
|
||||||
|
throw err;
|
||||||
|
});
|
||||||
|
break;
|
||||||
|
case 'projects:clusters:index':
|
||||||
|
import(/* webpackChunkName: "clusters_index" */ './clusters/clusters_index')
|
||||||
|
.then(clusterIndex => clusterIndex.default())
|
||||||
|
.catch((err) => {
|
||||||
|
Flash(s__('ClusterIntegration|Problem setting up the clusters list'));
|
||||||
throw err;
|
throw err;
|
||||||
});
|
});
|
||||||
break;
|
break;
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
<script>
|
<script>
|
||||||
import projectFeatureToggle from './project_feature_toggle.vue';
|
import projectFeatureToggle from '../../../vue_shared/components/toggle_button.vue';
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
props: {
|
props: {
|
||||||
|
|
|
@ -1,51 +0,0 @@
|
||||||
<script>
|
|
||||||
export default {
|
|
||||||
props: {
|
|
||||||
name: {
|
|
||||||
type: String,
|
|
||||||
required: false,
|
|
||||||
default: '',
|
|
||||||
},
|
|
||||||
value: {
|
|
||||||
type: Boolean,
|
|
||||||
required: true,
|
|
||||||
},
|
|
||||||
disabledInput: {
|
|
||||||
type: Boolean,
|
|
||||||
required: false,
|
|
||||||
default: false,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
|
|
||||||
model: {
|
|
||||||
prop: 'value',
|
|
||||||
event: 'change',
|
|
||||||
},
|
|
||||||
|
|
||||||
methods: {
|
|
||||||
toggleFeature() {
|
|
||||||
if (!this.disabledInput) this.$emit('change', !this.value);
|
|
||||||
},
|
|
||||||
},
|
|
||||||
};
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<template>
|
|
||||||
<label class="toggle-wrapper">
|
|
||||||
<input
|
|
||||||
v-if="name"
|
|
||||||
type="hidden"
|
|
||||||
:name="name"
|
|
||||||
:value="value"
|
|
||||||
/>
|
|
||||||
<button
|
|
||||||
type="button"
|
|
||||||
aria-label="Toggle"
|
|
||||||
class="project-feature-toggle"
|
|
||||||
data-enabled-text="Enabled"
|
|
||||||
data-disabled-text="Disabled"
|
|
||||||
:class="{ checked: value, disabled: disabledInput }"
|
|
||||||
@click="toggleFeature"
|
|
||||||
/>
|
|
||||||
</label>
|
|
||||||
</template>
|
|
|
@ -1,6 +1,6 @@
|
||||||
<script>
|
<script>
|
||||||
import projectFeatureSetting from './project_feature_setting.vue';
|
import projectFeatureSetting from './project_feature_setting.vue';
|
||||||
import projectFeatureToggle from './project_feature_toggle.vue';
|
import projectFeatureToggle from '../../../vue_shared/components/toggle_button.vue';
|
||||||
import projectSettingRow from './project_setting_row.vue';
|
import projectSettingRow from './project_setting_row.vue';
|
||||||
import { visibilityOptions, visibilityLevelDescriptions } from '../constants';
|
import { visibilityOptions, visibilityLevelDescriptions } from '../constants';
|
||||||
import { toggleHiddenClassBySelector } from '../external';
|
import { toggleHiddenClassBySelector } from '../external';
|
||||||
|
|
|
@ -0,0 +1,77 @@
|
||||||
|
<script>
|
||||||
|
import loadingIcon from './loading_icon.vue';
|
||||||
|
|
||||||
|
export default {
|
||||||
|
props: {
|
||||||
|
name: {
|
||||||
|
type: String,
|
||||||
|
required: false,
|
||||||
|
default: '',
|
||||||
|
},
|
||||||
|
value: {
|
||||||
|
type: Boolean,
|
||||||
|
required: true,
|
||||||
|
},
|
||||||
|
disabledInput: {
|
||||||
|
type: Boolean,
|
||||||
|
required: false,
|
||||||
|
default: false,
|
||||||
|
},
|
||||||
|
isLoading: {
|
||||||
|
type: Boolean,
|
||||||
|
required: false,
|
||||||
|
default: false,
|
||||||
|
},
|
||||||
|
enabledText: {
|
||||||
|
type: String,
|
||||||
|
required: false,
|
||||||
|
default: 'Enabled',
|
||||||
|
},
|
||||||
|
disabledText: {
|
||||||
|
type: String,
|
||||||
|
required: false,
|
||||||
|
default: 'Disabled',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
|
||||||
|
components: {
|
||||||
|
loadingIcon,
|
||||||
|
},
|
||||||
|
|
||||||
|
model: {
|
||||||
|
prop: 'value',
|
||||||
|
event: 'change',
|
||||||
|
},
|
||||||
|
|
||||||
|
methods: {
|
||||||
|
toggleFeature() {
|
||||||
|
if (!this.disabledInput) this.$emit('change', !this.value);
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<label class="toggle-wrapper">
|
||||||
|
<input
|
||||||
|
type="hidden"
|
||||||
|
:name="name"
|
||||||
|
:value="value"
|
||||||
|
/>
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
aria-label="Toggle"
|
||||||
|
class="project-feature-toggle"
|
||||||
|
:data-enabled-text="enabledText"
|
||||||
|
:data-disabled-text="disabledText"
|
||||||
|
:class="{
|
||||||
|
'is-checked': value,
|
||||||
|
'is-disabled': disabledInput,
|
||||||
|
'is-loading': isLoading
|
||||||
|
}"
|
||||||
|
@click="toggleFeature"
|
||||||
|
>
|
||||||
|
<loadingIcon class="loading-icon" />
|
||||||
|
</button>
|
||||||
|
</label>
|
||||||
|
</template>
|
|
@ -44,6 +44,7 @@
|
||||||
@import "framework/tabs";
|
@import "framework/tabs";
|
||||||
@import "framework/timeline";
|
@import "framework/timeline";
|
||||||
@import "framework/tooltips";
|
@import "framework/tooltips";
|
||||||
|
@import "framework/toggle";
|
||||||
@import "framework/typography";
|
@import "framework/typography";
|
||||||
@import "framework/zen";
|
@import "framework/zen";
|
||||||
@import "framework/blank";
|
@import "framework/blank";
|
||||||
|
|
|
@ -0,0 +1,138 @@
|
||||||
|
/**
|
||||||
|
* Toggle button
|
||||||
|
*
|
||||||
|
* @usage
|
||||||
|
* ### Active and Inactive text should be provided as data attributes:
|
||||||
|
* <button type="button" class="project-feature-toggle" data-enabled-text="Enabled" data-disabled-text="Disabled">
|
||||||
|
* <i class="fa fa-spinner fa-spin loading-icon hidden"></i>
|
||||||
|
* </button>
|
||||||
|
|
||||||
|
* ### Checked should have `is-checked` class
|
||||||
|
* <button type="button" class="project-feature-toggle is-checked" data-enabled-text="Enabled" data-disabled-text="Disabled">
|
||||||
|
* <i class="fa fa-spinner fa-spin loading-icon hidden"></i>
|
||||||
|
* </button>
|
||||||
|
|
||||||
|
* ### Disabled should have `is-disabled` class
|
||||||
|
* <button type="button" class="project-feature-toggle is-disabled" data-enabled-text="Enabled" data-disabled-text="Disabled" disabled="true">
|
||||||
|
* <i class="fa fa-spinner fa-spin loading-icon hidden"></i>
|
||||||
|
* </button>
|
||||||
|
|
||||||
|
* ### Loading should have `is-loading` and an icon with `loading-icon` class
|
||||||
|
* <button type="button" class="project-feature-toggle is-loading" data-enabled-text="Enabled" data-disabled-text="Disabled">
|
||||||
|
* <i class="fa fa-spinner fa-spin loading-icon"></i>
|
||||||
|
* </button>
|
||||||
|
*/
|
||||||
|
.project-feature-toggle {
|
||||||
|
position: relative;
|
||||||
|
border: 0;
|
||||||
|
outline: 0;
|
||||||
|
display: block;
|
||||||
|
width: 100px;
|
||||||
|
height: 24px;
|
||||||
|
cursor: pointer;
|
||||||
|
user-select: none;
|
||||||
|
background: $feature-toggle-color-disabled;
|
||||||
|
border-radius: 12px;
|
||||||
|
padding: 3px;
|
||||||
|
transition: all .4s ease;
|
||||||
|
|
||||||
|
&::selection,
|
||||||
|
&::before::selection,
|
||||||
|
&::after::selection {
|
||||||
|
background: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
&::before {
|
||||||
|
color: $feature-toggle-text-color;
|
||||||
|
font-size: 12px;
|
||||||
|
line-height: 24px;
|
||||||
|
position: absolute;
|
||||||
|
top: 0;
|
||||||
|
left: 25px;
|
||||||
|
right: 5px;
|
||||||
|
text-align: center;
|
||||||
|
overflow: hidden;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
animation: animate-disabled .2s ease-in;
|
||||||
|
content: attr(data-disabled-text);
|
||||||
|
}
|
||||||
|
|
||||||
|
&::after {
|
||||||
|
position: relative;
|
||||||
|
display: block;
|
||||||
|
content: "";
|
||||||
|
width: 22px;
|
||||||
|
height: 18px;
|
||||||
|
left: 0;
|
||||||
|
border-radius: 9px;
|
||||||
|
background: $feature-toggle-color;
|
||||||
|
transition: all .2s ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
.loading-icon {
|
||||||
|
display: none;
|
||||||
|
font-size: 12px;
|
||||||
|
color: $white-light;
|
||||||
|
position: absolute;
|
||||||
|
top: 50%;
|
||||||
|
left: 50%;
|
||||||
|
transform: translate(-50%, -50%);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
&.is-loading {
|
||||||
|
&::before {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.loading-icon {
|
||||||
|
display: block;
|
||||||
|
|
||||||
|
&::before {
|
||||||
|
position: absolute;
|
||||||
|
top: 50%;
|
||||||
|
left: 50%;
|
||||||
|
transform: translate(-50%, -50%);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&.is-checked {
|
||||||
|
background: $feature-toggle-color-enabled;
|
||||||
|
|
||||||
|
&::before {
|
||||||
|
left: 5px;
|
||||||
|
right: 25px;
|
||||||
|
animation: animate-enabled .2s ease-in;
|
||||||
|
content: attr(data-enabled-text);
|
||||||
|
}
|
||||||
|
|
||||||
|
&::after {
|
||||||
|
left: calc(100% - 22px);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&.is-disabled {
|
||||||
|
opacity: 0.4;
|
||||||
|
cursor: not-allowed;
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (max-width: $screen-xs-min) {
|
||||||
|
width: 50px;
|
||||||
|
|
||||||
|
&::before,
|
||||||
|
&.is-checked::before {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes animate-enabled {
|
||||||
|
0%, 35% { opacity: 0; }
|
||||||
|
100% { opacity: 1; }
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes animate-disabled {
|
||||||
|
0%, 35% { opacity: 0; }
|
||||||
|
100% { opacity: 1; }
|
||||||
|
}
|
||||||
|
}
|
|
@ -14,3 +14,17 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
@include new-style-dropdown('.clusters-dropdown ');
|
@include new-style-dropdown('.clusters-dropdown ');
|
||||||
|
|
||||||
|
.clusters-container {
|
||||||
|
.nav-bar-right {
|
||||||
|
padding: $gl-padding-top $gl-padding;
|
||||||
|
}
|
||||||
|
|
||||||
|
.empty-state .svg-content img {
|
||||||
|
width: 145px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.top-area .nav-controls > .btn.btn-add-cluster {
|
||||||
|
margin-right: 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -126,93 +126,6 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.project-feature-toggle {
|
|
||||||
position: relative;
|
|
||||||
border: 0;
|
|
||||||
outline: 0;
|
|
||||||
display: block;
|
|
||||||
width: 100px;
|
|
||||||
height: 24px;
|
|
||||||
cursor: pointer;
|
|
||||||
user-select: none;
|
|
||||||
background: $feature-toggle-color-disabled;
|
|
||||||
border-radius: 12px;
|
|
||||||
padding: 3px;
|
|
||||||
transition: all .4s ease;
|
|
||||||
|
|
||||||
&::selection,
|
|
||||||
&::before::selection,
|
|
||||||
&::after::selection {
|
|
||||||
background: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
&::before {
|
|
||||||
color: $feature-toggle-text-color;
|
|
||||||
font-size: 12px;
|
|
||||||
line-height: 24px;
|
|
||||||
position: absolute;
|
|
||||||
top: 0;
|
|
||||||
left: 25px;
|
|
||||||
right: 5px;
|
|
||||||
text-align: center;
|
|
||||||
overflow: hidden;
|
|
||||||
text-overflow: ellipsis;
|
|
||||||
animation: animate-disabled .2s ease-in;
|
|
||||||
content: attr(data-disabled-text);
|
|
||||||
}
|
|
||||||
|
|
||||||
&::after {
|
|
||||||
position: relative;
|
|
||||||
display: block;
|
|
||||||
content: "";
|
|
||||||
width: 22px;
|
|
||||||
height: 18px;
|
|
||||||
left: 0;
|
|
||||||
border-radius: 9px;
|
|
||||||
background: $feature-toggle-color;
|
|
||||||
transition: all .2s ease;
|
|
||||||
}
|
|
||||||
|
|
||||||
&.checked {
|
|
||||||
background: $feature-toggle-color-enabled;
|
|
||||||
|
|
||||||
&::before {
|
|
||||||
left: 5px;
|
|
||||||
right: 25px;
|
|
||||||
animation: animate-enabled .2s ease-in;
|
|
||||||
content: attr(data-enabled-text);
|
|
||||||
}
|
|
||||||
|
|
||||||
&::after {
|
|
||||||
left: calc(100% - 22px);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
&.disabled {
|
|
||||||
opacity: 0.4;
|
|
||||||
cursor: not-allowed;
|
|
||||||
}
|
|
||||||
|
|
||||||
@media (max-width: $screen-xs-min) {
|
|
||||||
width: 50px;
|
|
||||||
|
|
||||||
&::before,
|
|
||||||
&.checked::before {
|
|
||||||
display: none;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@keyframes animate-enabled {
|
|
||||||
0%, 35% { opacity: 0; }
|
|
||||||
100% { opacity: 1; }
|
|
||||||
}
|
|
||||||
|
|
||||||
@keyframes animate-disabled {
|
|
||||||
0%, 35% { opacity: 0; }
|
|
||||||
100% { opacity: 1; }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.project-home-panel,
|
.project-home-panel,
|
||||||
.group-home-panel {
|
.group-home-panel {
|
||||||
padding-top: 24px;
|
padding-top: 24px;
|
||||||
|
|
|
@ -8,11 +8,11 @@ class Projects::ClustersController < Projects::ApplicationController
|
||||||
STATUS_POLLING_INTERVAL = 10_000
|
STATUS_POLLING_INTERVAL = 10_000
|
||||||
|
|
||||||
def index
|
def index
|
||||||
if project.cluster
|
@scope = params[:scope] || 'all'
|
||||||
redirect_to project_cluster_path(project, project.cluster)
|
@clusters = ClustersFinder.new(project, current_user, @scope).execute.page(params[:page])
|
||||||
else
|
@active_count = ClustersFinder.new(project, current_user, :active).execute.count
|
||||||
redirect_to new_project_cluster_path(project)
|
@inactive_count = ClustersFinder.new(project, current_user, :inactive).execute.count
|
||||||
end
|
@all_count = @active_count + @inactive_count
|
||||||
end
|
end
|
||||||
|
|
||||||
def new
|
def new
|
||||||
|
@ -39,10 +39,20 @@ class Projects::ClustersController < Projects::ApplicationController
|
||||||
.execute(cluster)
|
.execute(cluster)
|
||||||
|
|
||||||
if cluster.valid?
|
if cluster.valid?
|
||||||
flash[:notice] = "Cluster was successfully updated."
|
respond_to do |format|
|
||||||
redirect_to project_cluster_path(project, project.cluster)
|
format.json do
|
||||||
|
head :no_content
|
||||||
|
end
|
||||||
|
format.html do
|
||||||
|
flash[:notice] = "Cluster was successfully updated."
|
||||||
|
redirect_to project_cluster_path(project, cluster)
|
||||||
|
end
|
||||||
|
end
|
||||||
else
|
else
|
||||||
render :show
|
respond_to do |format|
|
||||||
|
format.json { head :bad_request }
|
||||||
|
format.html { render :show }
|
||||||
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -63,6 +73,19 @@ class Projects::ClustersController < Projects::ApplicationController
|
||||||
.present(current_user: current_user)
|
.present(current_user: current_user)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def create_params
|
||||||
|
params.require(:cluster).permit(
|
||||||
|
:enabled,
|
||||||
|
:name,
|
||||||
|
:provider_type,
|
||||||
|
provider_gcp_attributes: [
|
||||||
|
:gcp_project_id,
|
||||||
|
:zone,
|
||||||
|
:num_nodes,
|
||||||
|
:machine_type
|
||||||
|
])
|
||||||
|
end
|
||||||
|
|
||||||
def update_params
|
def update_params
|
||||||
if cluster.managed?
|
if cluster.managed?
|
||||||
params.require(:cluster).permit(
|
params.require(:cluster).permit(
|
||||||
|
|
|
@ -0,0 +1,29 @@
|
||||||
|
class ClustersFinder
|
||||||
|
def initialize(project, user, scope)
|
||||||
|
@project = project
|
||||||
|
@user = user
|
||||||
|
@scope = scope || :active
|
||||||
|
end
|
||||||
|
|
||||||
|
def execute
|
||||||
|
clusters = project.clusters
|
||||||
|
filter_by_scope(clusters)
|
||||||
|
end
|
||||||
|
|
||||||
|
private
|
||||||
|
|
||||||
|
attr_reader :project, :user, :scope
|
||||||
|
|
||||||
|
def filter_by_scope(clusters)
|
||||||
|
case scope.to_sym
|
||||||
|
when :all
|
||||||
|
clusters
|
||||||
|
when :inactive
|
||||||
|
clusters.disabled
|
||||||
|
when :active
|
||||||
|
clusters.enabled
|
||||||
|
else
|
||||||
|
raise "Invalid scope #{scope}"
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
|
@ -55,6 +55,10 @@ module Clusters
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def created?
|
||||||
|
status_name == :created
|
||||||
|
end
|
||||||
|
|
||||||
def applications
|
def applications
|
||||||
[
|
[
|
||||||
application_helm || build_application_helm,
|
application_helm || build_application_helm,
|
||||||
|
|
|
@ -189,7 +189,6 @@ class Project < ActiveRecord::Base
|
||||||
has_one :statistics, class_name: 'ProjectStatistics'
|
has_one :statistics, class_name: 'ProjectStatistics'
|
||||||
|
|
||||||
has_one :cluster_project, class_name: 'Clusters::Project'
|
has_one :cluster_project, class_name: 'Clusters::Project'
|
||||||
has_one :cluster, through: :cluster_project, class_name: 'Clusters::Cluster'
|
|
||||||
has_many :clusters, through: :cluster_project, class_name: 'Clusters::Cluster'
|
has_many :clusters, through: :cluster_project, class_name: 'Clusters::Cluster'
|
||||||
|
|
||||||
# Container repositories need to remove data from the container registry,
|
# Container repositories need to remove data from the container registry,
|
||||||
|
|
|
@ -5,5 +5,9 @@ module Clusters
|
||||||
def gke_cluster_url
|
def gke_cluster_url
|
||||||
"https://console.cloud.google.com/kubernetes/clusters/details/#{provider.zone}/#{name}" if gcp?
|
"https://console.cloud.google.com/kubernetes/clusters/details/#{provider.zone}/#{name}" if gcp?
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def can_toggle_cluster?
|
||||||
|
can?(current_user, :update_cluster, cluster) && created?
|
||||||
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -5,6 +5,8 @@ module Clusters
|
||||||
def execute(access_token = nil)
|
def execute(access_token = nil)
|
||||||
@access_token = access_token
|
@access_token = access_token
|
||||||
|
|
||||||
|
raise ArgumentError.new('Instance does not support multiple clusters') unless can_create_cluster?
|
||||||
|
|
||||||
create_cluster.tap do |cluster|
|
create_cluster.tap do |cluster|
|
||||||
ClusterProvisionWorker.perform_async(cluster.id) if cluster.persisted?
|
ClusterProvisionWorker.perform_async(cluster.id) if cluster.persisted?
|
||||||
end
|
end
|
||||||
|
@ -25,5 +27,9 @@ module Clusters
|
||||||
|
|
||||||
@cluster_params = params.merge(user: current_user, projects: [project])
|
@cluster_params = params.merge(user: current_user, projects: [project])
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def can_create_cluster?
|
||||||
|
project.clusters.empty?
|
||||||
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -187,7 +187,7 @@
|
||||||
= nav_link(controller: [:clusters, :user, :gcp]) do
|
= nav_link(controller: [:clusters, :user, :gcp]) do
|
||||||
= link_to project_clusters_path(@project), title: 'Cluster', class: 'shortcuts-cluster' do
|
= link_to project_clusters_path(@project), title: 'Cluster', class: 'shortcuts-cluster' do
|
||||||
%span
|
%span
|
||||||
Cluster
|
Clusters
|
||||||
|
|
||||||
- if @project.feature_available?(:builds, current_user) && !@project.empty_repo?
|
- if @project.feature_available?(:builds, current_user) && !@project.empty_repo?
|
||||||
= nav_link(path: 'pipelines#charts') do
|
= nav_link(path: 'pipelines#charts') do
|
||||||
|
|
|
@ -0,0 +1,22 @@
|
||||||
|
.gl-responsive-table-row
|
||||||
|
.table-section.section-30
|
||||||
|
.table-mobile-header{ role: "rowheader" }= s_("ClusterIntegration|Cluster")
|
||||||
|
.table-mobile-content
|
||||||
|
= link_to cluster.name, namespace_project_cluster_path(@project.namespace, @project, cluster)
|
||||||
|
.table-section.section-30
|
||||||
|
.table-mobile-header{ role: "rowheader" }= s_("ClusterIntegration|Environment pattern")
|
||||||
|
.table-mobile-content= cluster.environment_scope
|
||||||
|
.table-section.section-30
|
||||||
|
.table-mobile-header{ role: "rowheader" }= s_("ClusterIntegration|Project namespace")
|
||||||
|
.table-mobile-content= cluster.platform_kubernetes&.actual_namespace
|
||||||
|
.table-section.section-10
|
||||||
|
.table-mobile-header{ role: "rowheader" }
|
||||||
|
.table-mobile-content
|
||||||
|
%button{ type: "button",
|
||||||
|
class: "js-toggle-cluster-list project-feature-toggle #{'is-checked' if cluster.enabled?} #{'is-disabled' if !cluster.can_toggle_cluster?}",
|
||||||
|
"aria-label": s_("ClusterIntegration|Toggle Cluster"),
|
||||||
|
disabled: !cluster.can_toggle_cluster?,
|
||||||
|
data: { "enabled-text": s_("ClusterIntegration|Active"),
|
||||||
|
"disabled-text": s_("ClusterIntegration|Inactive"),
|
||||||
|
endpoint: namespace_project_cluster_path(@project.namespace, @project, cluster, format: :json) } }
|
||||||
|
= icon("spinner spin", class: "loading-icon")
|
|
@ -0,0 +1,12 @@
|
||||||
|
.row.empty-state
|
||||||
|
.col-xs-12
|
||||||
|
.svg-content= image_tag 'illustrations/clusters_empty.svg'
|
||||||
|
.col-xs-12.text-center
|
||||||
|
.text-content
|
||||||
|
%h4= s_('ClusterIntegration|Integrate cluster automation')
|
||||||
|
- link_to_help_page = link_to(s_('ClusterIntegration|Learn more about Clusters'), help_page_path('user/project/clusters/index'), target: '_blank', rel: 'noopener noreferrer')
|
||||||
|
%p= s_('ClusterIntegration|Clusters allow you to use review apps, deploy your applications, run your pipelines, and much more in an easy way. %{link_to_help_page}').html_safe % { link_to_help_page: link_to_help_page}
|
||||||
|
|
||||||
|
%p
|
||||||
|
= link_to s_('ClusterIntegration|Add cluster'), new_project_cluster_path(@project), class: 'btn btn-success'
|
||||||
|
|
|
@ -5,12 +5,11 @@
|
||||||
= field.hidden_field :enabled, { class: 'js-toggle-input'}
|
= field.hidden_field :enabled, { class: 'js-toggle-input'}
|
||||||
|
|
||||||
%button{ type: 'button',
|
%button{ type: 'button',
|
||||||
class: "js-toggle-cluster project-feature-toggle #{'checked' unless !@cluster.enabled?} #{'disabled' unless can?(current_user, :update_cluster, @cluster)}",
|
class: "js-toggle-cluster project-feature-toggle #{'is-checked' unless !@cluster.enabled?} #{'is-disabled' unless can?(current_user, :update_cluster, @cluster)}",
|
||||||
'aria-label': s_('ClusterIntegration|Toggle Cluster'),
|
"aria-label": s_("ClusterIntegration|Toggle Cluster"),
|
||||||
disabled: !can?(current_user, :update_cluster, @cluster),
|
disabled: !can?(current_user, :update_cluster, @cluster),
|
||||||
data: { 'enabled-text': 'Enabled', 'disabled-text': 'Disabled' } }
|
data: { "enabled-text": s_("ClusterIntegration|Active"), "disabled-text": s_("ClusterIntegration|Inactive"), } }
|
||||||
|
|
||||||
- if can?(current_user, :update_cluster, @cluster)
|
- if can?(current_user, :update_cluster, @cluster)
|
||||||
.form-group
|
.form-group
|
||||||
= field.submit _('Save'), class: 'btn btn-success'
|
= field.submit _('Save'), class: 'btn btn-success'
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,16 @@
|
||||||
|
.top-area.scrolling-tabs-container.inner-page-scroll-tabs
|
||||||
|
.fade-left= icon("angle-left")
|
||||||
|
.fade-right= icon("angle-right")
|
||||||
|
%ul.nav-links.scrolling-tabs
|
||||||
|
%li{ class: ('active' if @scope == 'active') }>
|
||||||
|
= link_to project_clusters_path(@project, scope: :active), class: "js-active-tab" do
|
||||||
|
= s_("ClusterIntegration|Active")
|
||||||
|
%span.badge= @active_count
|
||||||
|
%li{ class: ('active' if @scope == 'inactive') }>
|
||||||
|
= link_to project_clusters_path(@project, scope: :inactive), class: "js-inactive-tab" do
|
||||||
|
= s_("ClusterIntegration|Inactive")
|
||||||
|
%span.badge= @inactive_count
|
||||||
|
%li{ class: ('active' if @scope.nil? || @scope == 'all') }>
|
||||||
|
= link_to project_clusters_path(@project), class: "js-all-tab" do
|
||||||
|
= s_("ClusterIntegration|All")
|
||||||
|
%span.badge= @all_count
|
|
@ -0,0 +1,24 @@
|
||||||
|
- breadcrumb_title "Clusters"
|
||||||
|
- page_title "Clusters"
|
||||||
|
|
||||||
|
.clusters-container
|
||||||
|
- if !@clusters.empty?
|
||||||
|
= render "tabs"
|
||||||
|
.ci-table.js-clusters-list
|
||||||
|
.gl-responsive-table-row.table-row-header{ role: "row" }
|
||||||
|
.table-section.section-30{ role: "rowheader" }
|
||||||
|
= s_("ClusterIntegration|Cluster")
|
||||||
|
.table-section.section-30{ role: "rowheader" }
|
||||||
|
= s_("ClusterIntegration|Environment pattern")
|
||||||
|
.table-section.section-30{ role: "rowheader" }
|
||||||
|
= s_("ClusterIntegration|Project namespace")
|
||||||
|
.table-section.section-10{ role: "rowheader" }
|
||||||
|
- @clusters.each do |cluster|
|
||||||
|
= render "cluster", cluster: cluster.present(current_user: current_user)
|
||||||
|
= paginate @clusters, theme: "gitlab"
|
||||||
|
- elsif @scope == 'all'
|
||||||
|
= render "empty_state"
|
||||||
|
- else
|
||||||
|
= render "tabs"
|
||||||
|
.prepend-top-20.text-center
|
||||||
|
= s_("ClusterIntegration|There are no clusters to show")
|
|
@ -1,5 +1,6 @@
|
||||||
- @content_class = "limit-container-width" unless fluid_layout
|
- @content_class = "limit-container-width" unless fluid_layout
|
||||||
- breadcrumb_title "Cluster"
|
- add_to_breadcrumbs "Clusters", project_clusters_path(@project)
|
||||||
|
- breadcrumb_title @cluster.id
|
||||||
- page_title _("Cluster")
|
- page_title _("Cluster")
|
||||||
|
|
||||||
- expanded = Rails.env.test?
|
- expanded = Rails.env.test?
|
||||||
|
@ -28,7 +29,6 @@
|
||||||
%button.btn.js-settings-toggle
|
%button.btn.js-settings-toggle
|
||||||
= expanded ? 'Collapse' : 'Expand'
|
= expanded ? 'Collapse' : 'Expand'
|
||||||
%p= s_('ClusterIntegration|See and edit the details for your cluster')
|
%p= s_('ClusterIntegration|See and edit the details for your cluster')
|
||||||
|
|
||||||
.settings-content
|
.settings-content
|
||||||
- if @cluster.managed?
|
- if @cluster.managed?
|
||||||
= render 'projects/clusters/gcp/show'
|
= render 'projects/clusters/gcp/show'
|
||||||
|
|
|
@ -8,8 +8,8 @@ msgid ""
|
||||||
msgstr ""
|
msgstr ""
|
||||||
"Project-Id-Version: gitlab 1.0.0\n"
|
"Project-Id-Version: gitlab 1.0.0\n"
|
||||||
"Report-Msgid-Bugs-To: \n"
|
"Report-Msgid-Bugs-To: \n"
|
||||||
"POT-Creation-Date: 2017-10-22 16:40+0300\n"
|
"POT-Creation-Date: 2017-12-05 20:31+0100\n"
|
||||||
"PO-Revision-Date: 2017-10-22 16:40+0300\n"
|
"PO-Revision-Date: 2017-12-05 20:31+0100\n"
|
||||||
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
|
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
|
||||||
"Language-Team: LANGUAGE <LL@li.org>\n"
|
"Language-Team: LANGUAGE <LL@li.org>\n"
|
||||||
"Language: \n"
|
"Language: \n"
|
||||||
|
@ -36,6 +36,11 @@ msgstr[1] ""
|
||||||
msgid "%{commit_author_link} committed %{commit_timeago}"
|
msgid "%{commit_author_link} committed %{commit_timeago}"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
|
msgid "%{count} participant"
|
||||||
|
msgid_plural "%{count} participants"
|
||||||
|
msgstr[0] ""
|
||||||
|
msgstr[1] ""
|
||||||
|
|
||||||
msgid "%{number_commits_behind} commits behind %{default_branch}, %{number_commits_ahead} commits ahead"
|
msgid "%{number_commits_behind} commits behind %{default_branch}, %{number_commits_ahead} commits ahead"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
|
@ -53,9 +58,18 @@ msgid_plural "%{storage_name}: %{failed_attempts} failed storage access attempts
|
||||||
msgstr[0] ""
|
msgstr[0] ""
|
||||||
msgstr[1] ""
|
msgstr[1] ""
|
||||||
|
|
||||||
|
msgid "%{text} is available"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
msgid "(checkout the %{link} for information on how to install it)."
|
msgid "(checkout the %{link} for information on how to install it)."
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
|
msgid "+ %{moreCount} more"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
msgid "- show less"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
msgid "1 pipeline"
|
msgid "1 pipeline"
|
||||||
msgid_plural "%d pipelines"
|
msgid_plural "%d pipelines"
|
||||||
msgstr[0] ""
|
msgstr[0] ""
|
||||||
|
@ -100,9 +114,6 @@ msgstr ""
|
||||||
msgid "Add License"
|
msgid "Add License"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
msgid "Add an SSH key to your profile to pull or push via SSH."
|
|
||||||
msgstr ""
|
|
||||||
|
|
||||||
msgid "Add new directory"
|
msgid "Add new directory"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
|
@ -115,6 +126,12 @@ msgstr ""
|
||||||
msgid "All"
|
msgid "All"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
|
msgid "An error occurred when toggling the notification subscription"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
msgid "An error occurred while fetching sidebar data"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
msgid "An error occurred. Please try again."
|
msgid "An error occurred. Please try again."
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
|
@ -124,6 +141,12 @@ msgstr ""
|
||||||
msgid "Applications"
|
msgid "Applications"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
|
msgid "Apr"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
msgid "April"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
msgid "Archived project! Repository is read-only"
|
msgid "Archived project! Repository is read-only"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
|
@ -151,6 +174,12 @@ msgstr ""
|
||||||
msgid "Attach a file by drag & drop or %{upload_link}"
|
msgid "Attach a file by drag & drop or %{upload_link}"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
|
msgid "Aug"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
msgid "August"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
msgid "Authentication Log"
|
msgid "Authentication Log"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
|
@ -184,6 +213,9 @@ msgstr ""
|
||||||
msgid "AutoDevOps|You can activate %{link_to_settings} for this project."
|
msgid "AutoDevOps|You can activate %{link_to_settings} for this project."
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
|
msgid "Available"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
msgid "Branch"
|
msgid "Branch"
|
||||||
msgid_plural "Branches"
|
msgid_plural "Branches"
|
||||||
msgstr[0] ""
|
msgstr[0] ""
|
||||||
|
@ -195,6 +227,12 @@ msgstr ""
|
||||||
msgid "Branch has changed"
|
msgid "Branch has changed"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
|
msgid "Branch is already taken"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
msgid "Branch name"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
msgid "BranchSwitcherPlaceholder|Search branches"
|
msgid "BranchSwitcherPlaceholder|Search branches"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
|
@ -330,6 +368,12 @@ msgstr ""
|
||||||
msgid "Chat"
|
msgid "Chat"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
|
msgid "Checking %{text} availability…"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
msgid "Checking branch availability..."
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
msgid "Cherry-pick this commit"
|
msgid "Cherry-pick this commit"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
|
@ -399,7 +443,40 @@ msgstr ""
|
||||||
msgid "Cluster"
|
msgid "Cluster"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
msgid "ClusterIntegration|This account must have permissions to create a cluster in the %{link_to_container_project} specified below"
|
msgid "ClusterIntegration|%{appList} was successfully installed on your cluster"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
msgid "ClusterIntegration|%{boldNotice} This will add some extra resources like a load balancer, which incur additional costs. See %{pricingLink}"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
msgid "ClusterIntegration|API URL"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
msgid "ClusterIntegration|Active"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
msgid "ClusterIntegration|Add an existing cluster"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
msgid "ClusterIntegration|Add cluster"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
msgid "ClusterIntegration|All"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
msgid "ClusterIntegration|Applications"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
msgid "ClusterIntegration|CA Certificate"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
msgid "ClusterIntegration|Certificate Authority bundle (PEM format)"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
msgid "ClusterIntegration|Choose how to set up cluster integration"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
msgid "ClusterIntegration|Cluster"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
msgid "ClusterIntegration|Cluster details"
|
msgid "ClusterIntegration|Cluster details"
|
||||||
|
@ -423,21 +500,54 @@ msgstr ""
|
||||||
msgid "ClusterIntegration|Cluster name"
|
msgid "ClusterIntegration|Cluster name"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
msgid "ClusterIntegration|Cluster was successfully created on Google Container Engine"
|
msgid "ClusterIntegration|Cluster was successfully created on Google Container Engine. Refresh the page to see cluster's details"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
msgid "ClusterIntegration|Clusters allow you to use review apps, deploy your applications, run your pipelines, and much more in an easy way. %{link_to_help_page}"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
msgid "ClusterIntegration|Copy API URL"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
msgid "ClusterIntegration|Copy CA Certificate"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
msgid "ClusterIntegration|Copy Token"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
msgid "ClusterIntegration|Copy cluster name"
|
msgid "ClusterIntegration|Copy cluster name"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
|
msgid "ClusterIntegration|Create a new cluster on Google Engine right from GitLab"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
msgid "ClusterIntegration|Create cluster"
|
msgid "ClusterIntegration|Create cluster"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
msgid "ClusterIntegration|Create new cluster on Google Container Engine"
|
msgid "ClusterIntegration|Create cluster on Google Container Engine"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
msgid "ClusterIntegration|Create on GKE"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
msgid "ClusterIntegration|Enable cluster integration"
|
msgid "ClusterIntegration|Enable cluster integration"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
|
msgid "ClusterIntegration|Enter the details for an existing Kubernetes cluster"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
msgid "ClusterIntegration|Enter the details for your cluster"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
msgid "ClusterIntegration|Environment pattern"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
msgid "ClusterIntegration|GKE pricing"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
msgid "ClusterIntegration|GitLab Runner"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
msgid "ClusterIntegration|Google Cloud Platform project ID"
|
msgid "ClusterIntegration|Google Cloud Platform project ID"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
|
@ -447,27 +557,75 @@ msgstr ""
|
||||||
msgid "ClusterIntegration|Google Container Engine project"
|
msgid "ClusterIntegration|Google Container Engine project"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
|
msgid "ClusterIntegration|Helm Tiller"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
msgid "ClusterIntegration|Inactive"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
msgid "ClusterIntegration|Ingress"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
msgid "ClusterIntegration|Install"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
msgid "ClusterIntegration|Install applications on your cluster. Read more about %{helpLink}"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
msgid "ClusterIntegration|Installed"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
msgid "ClusterIntegration|Installing"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
msgid "ClusterIntegration|Integrate cluster automation"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
msgid "ClusterIntegration|Learn more about %{link_to_documentation}"
|
msgid "ClusterIntegration|Learn more about %{link_to_documentation}"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
|
msgid "ClusterIntegration|Learn more about Clusters"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
msgid "ClusterIntegration|Machine type"
|
msgid "ClusterIntegration|Machine type"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
msgid "ClusterIntegration|Make sure your account %{link_to_requirements} to create clusters"
|
msgid "ClusterIntegration|Make sure your account %{link_to_requirements} to create clusters"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
msgid "ClusterIntegration|Manage Cluster integration on your GitLab project"
|
msgid "ClusterIntegration|Manage cluster integration on your GitLab project"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
msgid "ClusterIntegration|Manage your cluster by visiting %{link_gke}"
|
msgid "ClusterIntegration|Manage your cluster by visiting %{link_gke}"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
|
msgid "ClusterIntegration|Multiple clusters are available in GitLab Entreprise Edition Premium and Ultimate"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
msgid "ClusterIntegration|Note:"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
msgid "ClusterIntegration|Number of nodes"
|
msgid "ClusterIntegration|Number of nodes"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
|
msgid "ClusterIntegration|Please enter access information for your cluster. If you need help, you can read our %{link_to_help_page} on clusters"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
msgid "ClusterIntegration|Please make sure that your Google account meets the following requirements:"
|
msgid "ClusterIntegration|Please make sure that your Google account meets the following requirements:"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
|
msgid "ClusterIntegration|Problem setting up the cluster"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
msgid "ClusterIntegration|Problem setting up the clusters list"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
msgid "ClusterIntegration|Project ID"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
msgid "ClusterIntegration|Project namespace"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
msgid "ClusterIntegration|Project namespace (optional, unique)"
|
msgid "ClusterIntegration|Project namespace (optional, unique)"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
|
@ -483,6 +641,12 @@ msgstr ""
|
||||||
msgid "ClusterIntegration|Removing cluster integration will remove the cluster configuration you have added to this project. It will not delete your cluster on Google Container Engine."
|
msgid "ClusterIntegration|Removing cluster integration will remove the cluster configuration you have added to this project. It will not delete your cluster on Google Container Engine."
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
|
msgid "ClusterIntegration|Request to begin installing failed"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
msgid "ClusterIntegration|Save changes"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
msgid "ClusterIntegration|See and edit the details for your cluster"
|
msgid "ClusterIntegration|See and edit the details for your cluster"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
|
@ -495,15 +659,33 @@ msgstr ""
|
||||||
msgid "ClusterIntegration|See zones"
|
msgid "ClusterIntegration|See zones"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
|
msgid "ClusterIntegration|Service token"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
msgid "ClusterIntegration|Show"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
msgid "ClusterIntegration|Something went wrong on our end."
|
msgid "ClusterIntegration|Something went wrong on our end."
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
msgid "ClusterIntegration|Something went wrong while creating your cluster on Google Container Engine"
|
msgid "ClusterIntegration|Something went wrong while creating your cluster on Google Container Engine"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
|
msgid "ClusterIntegration|Something went wrong while installing %{title}"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
msgid "ClusterIntegration|There are no clusters to show"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
msgid "ClusterIntegration|This account must have permissions to create a cluster in the %{link_to_container_project} specified below"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
msgid "ClusterIntegration|Toggle Cluster"
|
msgid "ClusterIntegration|Toggle Cluster"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
|
msgid "ClusterIntegration|Token"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
msgid "ClusterIntegration|With a cluster associated to this project, you can use review apps, deploy your applications, run your pipelines, and much more in an easy way."
|
msgid "ClusterIntegration|With a cluster associated to this project, you can use review apps, deploy your applications, run your pipelines, and much more in an easy way."
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
|
@ -519,9 +701,15 @@ msgstr ""
|
||||||
msgid "ClusterIntegration|cluster"
|
msgid "ClusterIntegration|cluster"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
|
msgid "ClusterIntegration|documentation"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
msgid "ClusterIntegration|help page"
|
msgid "ClusterIntegration|help page"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
|
msgid "ClusterIntegration|installing applications"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
msgid "ClusterIntegration|meets the requirements"
|
msgid "ClusterIntegration|meets the requirements"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
|
@ -617,6 +805,15 @@ msgstr ""
|
||||||
msgid "Contributors"
|
msgid "Contributors"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
|
msgid "ContributorsPage|Building repository graph."
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
msgid "ContributorsPage|Commits to %{branch_name}, excluding merge commits. Limited to 6,000 commits."
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
msgid "ContributorsPage|Please wait a moment, this page will automatically refresh when ready."
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
msgid "Copy URL to clipboard"
|
msgid "Copy URL to clipboard"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
|
@ -635,12 +832,21 @@ msgstr ""
|
||||||
msgid "Create empty bare repository"
|
msgid "Create empty bare repository"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
|
msgid "Create file"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
msgid "Create merge request"
|
msgid "Create merge request"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
msgid "Create new branch"
|
msgid "Create new branch"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
|
msgid "Create new directory"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
msgid "Create new file"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
msgid "Create new..."
|
msgid "Create new..."
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
|
@ -698,6 +904,12 @@ msgstr ""
|
||||||
msgid "DashboardProjects|Personal"
|
msgid "DashboardProjects|Personal"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
|
msgid "Dec"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
msgid "December"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
msgid "Define a custom pattern with cron syntax"
|
msgid "Define a custom pattern with cron syntax"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
|
@ -766,6 +978,60 @@ msgstr ""
|
||||||
msgid "Emails"
|
msgid "Emails"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
|
msgid "Environments|An error occurred while fetching the environments."
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
msgid "Environments|An error occurred while making the request."
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
msgid "Environments|Commit"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
msgid "Environments|Deployment"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
msgid "Environments|Environment"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
msgid "Environments|Environments"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
msgid "Environments|Environments are places where code gets deployed, such as staging or production."
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
msgid "Environments|Job"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
msgid "Environments|New environment"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
msgid "Environments|No deployments yet"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
msgid "Environments|Open"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
msgid "Environments|Re-deploy"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
msgid "Environments|Read more about environments"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
msgid "Environments|Rollback"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
msgid "Environments|Show all"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
msgid "Environments|Updated"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
msgid "Environments|You don't have any environments right now."
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
msgid "Error occurred when toggling the notification subscription"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
msgid "EventFilterBy|Filter by all"
|
msgid "EventFilterBy|Filter by all"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
|
@ -805,6 +1071,15 @@ msgstr ""
|
||||||
msgid "Failed to remove the pipeline schedule"
|
msgid "Failed to remove the pipeline schedule"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
|
msgid "Feb"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
msgid "February"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
msgid "File name"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
msgid "Files"
|
msgid "Files"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
|
@ -981,6 +1256,24 @@ msgstr ""
|
||||||
msgid "Issues"
|
msgid "Issues"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
|
msgid "Jan"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
msgid "January"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
msgid "Jul"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
msgid "July"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
msgid "Jun"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
msgid "June"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
msgid "LFSStatus|Disabled"
|
msgid "LFSStatus|Disabled"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
|
@ -1048,9 +1341,18 @@ msgstr ""
|
||||||
msgid "Login"
|
msgid "Login"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
|
msgid "Mar"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
msgid "March"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
msgid "Maximum git storage failures"
|
msgid "Maximum git storage failures"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
|
msgid "May"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
msgid "Median"
|
msgid "Median"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
|
@ -1092,6 +1394,9 @@ msgstr ""
|
||||||
msgid "New branch"
|
msgid "New branch"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
|
msgid "New branch unavailable"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
msgid "New directory"
|
msgid "New directory"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
|
@ -1131,6 +1436,12 @@ msgstr ""
|
||||||
msgid "No schedules"
|
msgid "No schedules"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
|
msgid "No time spent"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
msgid "None"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
msgid "Not available"
|
msgid "Not available"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
|
@ -1194,6 +1505,24 @@ msgstr ""
|
||||||
msgid "Notifications"
|
msgid "Notifications"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
|
msgid "Nov"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
msgid "November"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
msgid "Number of access attempts"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
msgid "Number of failures before backing off"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
msgid "Oct"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
msgid "October"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
msgid "OfSearchInADropdown|Filter"
|
msgid "OfSearchInADropdown|Filter"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
|
@ -1431,6 +1760,12 @@ msgstr ""
|
||||||
msgid "ProjectNetworkGraph|Graph"
|
msgid "ProjectNetworkGraph|Graph"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
|
msgid "ProjectSettings|Immediately run a pipeline on the default branch"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
msgid "ProjectSettings|Problem setting up the CI/CD settings JavaScript"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
msgid "Projects"
|
msgid "Projects"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
|
@ -1455,6 +1790,39 @@ msgstr ""
|
||||||
msgid "ProjectsDropdown|This feature requires browser localStorage support"
|
msgid "ProjectsDropdown|This feature requires browser localStorage support"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
|
msgid "PrometheusService|By default, Prometheus listens on ‘http://localhost:9090’. It’s not recommended to change the default address and port as this might affect or conflict with other services running on the GitLab server."
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
msgid "PrometheusService|Finding and configuring metrics..."
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
msgid "PrometheusService|Metrics"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
msgid "PrometheusService|Metrics are automatically configured and monitored based on a library of metrics from popular exporters."
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
msgid "PrometheusService|Missing environment variable"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
msgid "PrometheusService|Monitored"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
msgid "PrometheusService|More information"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
msgid "PrometheusService|No metrics are being monitored. To start monitoring, deploy to an environment."
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
msgid "PrometheusService|Prometheus API Base URL, like http://prometheus.example.com/"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
msgid "PrometheusService|Prometheus monitoring"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
msgid "PrometheusService|View environments"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
msgid "Public - The group and any public projects can be viewed without any authentication."
|
msgid "Public - The group and any public projects can be viewed without any authentication."
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
|
@ -1563,6 +1931,12 @@ msgstr ""
|
||||||
msgid "Select target branch"
|
msgid "Select target branch"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
|
msgid "Sep"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
msgid "September"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
msgid "Service Templates"
|
msgid "Service Templates"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
|
@ -1601,7 +1975,7 @@ msgstr ""
|
||||||
msgid "Something went wrong on our end."
|
msgid "Something went wrong on our end."
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
msgid "Something went wrong trying to change the locked state of this ${this.issuableDisplayName(this.issuableType)}"
|
msgid "Something went wrong trying to change the locked state of this ${this.issuableDisplayName}"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
msgid "Something went wrong while fetching the projects."
|
msgid "Something went wrong while fetching the projects."
|
||||||
|
@ -1700,9 +2074,15 @@ msgstr ""
|
||||||
msgid "SortOptions|Start soon"
|
msgid "SortOptions|Start soon"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
|
msgid "Source"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
msgid "Source code"
|
msgid "Source code"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
|
msgid "Source is not available"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
msgid "Spam Logs"
|
msgid "Spam Logs"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
|
@ -1721,9 +2101,15 @@ msgstr ""
|
||||||
msgid "Start the Runner!"
|
msgid "Start the Runner!"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
|
msgid "Stopped"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
msgid "Subgroups"
|
msgid "Subgroups"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
|
msgid "Subscribe"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
msgid "Switch branch/tag"
|
msgid "Switch branch/tag"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
|
@ -1738,12 +2124,84 @@ msgstr[1] ""
|
||||||
msgid "Tags"
|
msgid "Tags"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
|
msgid "TagsPage|Browse commits"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
msgid "TagsPage|Browse files"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
msgid "TagsPage|Can't find HEAD commit for this tag"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
msgid "TagsPage|Cancel"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
msgid "TagsPage|Create tag"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
msgid "TagsPage|Delete tag"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
msgid "TagsPage|Deleting the %{tag_name} tag cannot be undone. Are you sure?"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
msgid "TagsPage|Edit release notes"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
msgid "TagsPage|Existing branch name, tag, or commit SHA"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
msgid "TagsPage|Filter by tag name"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
msgid "TagsPage|New Tag"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
msgid "TagsPage|New tag"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
msgid "TagsPage|Optionally, add a message to the tag."
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
msgid "TagsPage|Optionally, add release notes to the tag. They will be stored in the GitLab database and displayed on the tags page."
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
msgid "TagsPage|Release notes"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
msgid "TagsPage|Repository has no tags yet."
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
msgid "TagsPage|Sort by"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
msgid "TagsPage|Tags"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
msgid "TagsPage|Tags give the ability to mark specific points in history as being important"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
msgid "TagsPage|This tag has no release notes."
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
msgid "TagsPage|Use git tag command to add a new one:"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
msgid "TagsPage|Write your release notes or drag files here..."
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
msgid "TagsPage|protected"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
msgid "Target Branch"
|
msgid "Target Branch"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
msgid "Team"
|
msgid "Team"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
|
msgid "The circuitbreaker backoff threshold should be lower than the failure count threshold"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
msgid "The coding stage shows the time from the first commit to creating the merge request. The data will automatically be added here once you create your first merge request."
|
msgid "The coding stage shows the time from the first commit to creating the merge request. The data will automatically be added here once you create your first merge request."
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
|
@ -1756,6 +2214,12 @@ msgstr ""
|
||||||
msgid "The issue stage shows the time it takes from creating an issue to assigning the issue to a milestone, or add the issue to a list on your Issue Board. Begin creating issues to see data for this stage."
|
msgid "The issue stage shows the time it takes from creating an issue to assigning the issue to a milestone, or add the issue to a list on your Issue Board. Begin creating issues to see data for this stage."
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
|
msgid "The number of attempts GitLab will make to access a storage."
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
msgid "The number of failures after which GitLab will start temporarily disabling access to a storage shard on a host"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
msgid "The number of failures of after which GitLab will completely prevent access to the storage. The number of failures can be reset in the admin interface: %{link_to_health_page} or using the %{api_documentation_link}."
|
msgid "The number of failures of after which GitLab will completely prevent access to the storage. The number of failures can be reset in the admin interface: %{link_to_health_page} or using the %{api_documentation_link}."
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
|
@ -1976,6 +2440,9 @@ msgstr ""
|
||||||
msgid "Total Time"
|
msgid "Total Time"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
|
msgid "Total issue time spent"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
msgid "Total test time for all commits/merges"
|
msgid "Total test time for all commits/merges"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
|
@ -1988,6 +2455,9 @@ msgstr ""
|
||||||
msgid "Unstar"
|
msgid "Unstar"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
|
msgid "Unsubscribe"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
msgid "Upload New File"
|
msgid "Upload New File"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
|
@ -2189,6 +2659,9 @@ msgstr ""
|
||||||
msgid "You won't be able to pull or push project code via SSH until you %{add_ssh_key_link} to your profile"
|
msgid "You won't be able to pull or push project code via SSH until you %{add_ssh_key_link} to your profile"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
|
msgid "You won't be able to pull or push project code via SSH until you add an SSH key to your profile"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
msgid "Your comment will not be visible to the public."
|
msgid "Your comment will not be visible to the public."
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
|
@ -2201,6 +2674,9 @@ msgstr ""
|
||||||
msgid "Your projects"
|
msgid "Your projects"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
|
msgid "branch name"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
msgid "day"
|
msgid "day"
|
||||||
msgid_plural "days"
|
msgid_plural "days"
|
||||||
msgstr[0] ""
|
msgstr[0] ""
|
||||||
|
@ -2223,5 +2699,8 @@ msgstr ""
|
||||||
msgid "personal access token"
|
msgid "personal access token"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
|
msgid "source"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
msgid "username"
|
msgid "username"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
|
@ -143,9 +143,9 @@ describe Projects::Clusters::GcpController do
|
||||||
expect(ClusterProvisionWorker).to receive(:perform_async)
|
expect(ClusterProvisionWorker).to receive(:perform_async)
|
||||||
expect { go }.to change { Clusters::Cluster.count }
|
expect { go }.to change { Clusters::Cluster.count }
|
||||||
.and change { Clusters::Providers::Gcp.count }
|
.and change { Clusters::Providers::Gcp.count }
|
||||||
expect(response).to redirect_to(project_cluster_path(project, project.cluster))
|
expect(response).to redirect_to(project_cluster_path(project, project.clusters.first))
|
||||||
expect(project.cluster).to be_gcp
|
expect(project.clusters.first).to be_gcp
|
||||||
expect(project.cluster).to be_kubernetes
|
expect(project.clusters.first).to be_kubernetes
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -64,7 +64,9 @@ describe Projects::Clusters::UserController do
|
||||||
expect(ClusterProvisionWorker).to receive(:perform_async)
|
expect(ClusterProvisionWorker).to receive(:perform_async)
|
||||||
expect { go }.to change { Clusters::Cluster.count }
|
expect { go }.to change { Clusters::Cluster.count }
|
||||||
.and change { Clusters::Platforms::Kubernetes.count }
|
.and change { Clusters::Platforms::Kubernetes.count }
|
||||||
expect(response).to redirect_to(project_cluster_path(project, project.cluster))
|
expect(response).to redirect_to(project_cluster_path(project, project.clusters.first))
|
||||||
|
expect(project.clusters.first).to be_user
|
||||||
|
expect(project.clusters.first).to be_kubernetes
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -15,14 +15,72 @@ describe Projects::ClustersController do
|
||||||
sign_in(user)
|
sign_in(user)
|
||||||
end
|
end
|
||||||
|
|
||||||
context 'when project has a cluster' do
|
context 'when project has one or more clusters' do
|
||||||
let!(:cluster) { create(:cluster, :provided_by_gcp, projects: [project]) }
|
let(:project) { create(:project) }
|
||||||
|
let!(:enabled_cluster) { create(:cluster, :provided_by_gcp, projects: [project]) }
|
||||||
|
let!(:disabled_cluster) { create(:cluster, :disabled, :provided_by_gcp, projects: [project]) }
|
||||||
|
it 'lists available clusters' do
|
||||||
|
go
|
||||||
|
|
||||||
it { expect(go).to redirect_to(project_cluster_path(project, project.cluster)) }
|
expect(response).to have_gitlab_http_status(:ok)
|
||||||
|
expect(response).to render_template(:index)
|
||||||
|
expect(assigns(:clusters)).to match_array([enabled_cluster, disabled_cluster])
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'assigns counters to correct values' do
|
||||||
|
go
|
||||||
|
|
||||||
|
expect(assigns(:active_count)).to eq(1)
|
||||||
|
expect(assigns(:inactive_count)).to eq(1)
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'when page is specified' do
|
||||||
|
let(:last_page) { project.clusters.page.total_pages }
|
||||||
|
|
||||||
|
before do
|
||||||
|
allow(Clusters::Cluster).to receive(:paginates_per).and_return(1)
|
||||||
|
create_list(:cluster, 2, :provided_by_gcp, projects: [project])
|
||||||
|
get :index, namespace_id: project.namespace, project_id: project, page: last_page
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'redirects to the page' do
|
||||||
|
expect(response).to have_gitlab_http_status(:ok)
|
||||||
|
expect(assigns(:clusters).current_page).to eq(last_page)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'when only enabled clusters are requested' do
|
||||||
|
it 'returns only enabled clusters' do
|
||||||
|
get :index, namespace_id: project.namespace, project_id: project, scope: 'active'
|
||||||
|
expect(assigns(:clusters)).to all(have_attributes(enabled: true))
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'when only disabled clusters are requested' do
|
||||||
|
it 'returns only disabled clusters' do
|
||||||
|
get :index, namespace_id: project.namespace, project_id: project, scope: 'inactive'
|
||||||
|
expect(assigns(:clusters)).to all(have_attributes(enabled: false))
|
||||||
|
end
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
context 'when project does not have a cluster' do
|
context 'when project does not have a cluster' do
|
||||||
it { expect(go).to redirect_to(new_project_cluster_path(project)) }
|
let(:project) { create(:project) }
|
||||||
|
|
||||||
|
it 'returns an empty state page' do
|
||||||
|
go
|
||||||
|
|
||||||
|
expect(response).to have_gitlab_http_status(:ok)
|
||||||
|
expect(response).to render_template(:index, partial: :empty_state)
|
||||||
|
expect(assigns(:clusters)).to eq([])
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'assigns counters to zero' do
|
||||||
|
go
|
||||||
|
|
||||||
|
expect(assigns(:active_count)).to eq(0)
|
||||||
|
expect(assigns(:inactive_count)).to eq(0)
|
||||||
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -146,7 +204,7 @@ describe Projects::ClustersController do
|
||||||
go
|
go
|
||||||
|
|
||||||
cluster.reload
|
cluster.reload
|
||||||
expect(response).to redirect_to(project_cluster_path(project, project.cluster))
|
expect(response).to redirect_to(project_cluster_path(project, cluster))
|
||||||
expect(flash[:notice]).to eq('Cluster was successfully updated.')
|
expect(flash[:notice]).to eq('Cluster was successfully updated.')
|
||||||
expect(cluster.enabled).to be_falsey
|
expect(cluster.enabled).to be_falsey
|
||||||
end
|
end
|
||||||
|
@ -180,28 +238,77 @@ describe Projects::ClustersController do
|
||||||
sign_in(user)
|
sign_in(user)
|
||||||
end
|
end
|
||||||
|
|
||||||
context 'when changing parameters' do
|
context 'when format is json' do
|
||||||
let(:params) do
|
context 'when changing parameters' do
|
||||||
{
|
context 'when valid parameters are used' do
|
||||||
cluster: {
|
let(:params) do
|
||||||
enabled: false,
|
{
|
||||||
name: 'my-new-cluster-name',
|
cluster: {
|
||||||
platform_kubernetes_attributes: {
|
enabled: false,
|
||||||
namespace: 'my-namespace'
|
name: 'my-new-cluster-name',
|
||||||
|
platform_kubernetes_attributes: {
|
||||||
|
namespace: 'my-namespace'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
end
|
||||||
|
|
||||||
|
it "updates and redirects back to show page" do
|
||||||
|
go_json
|
||||||
|
|
||||||
|
cluster.reload
|
||||||
|
expect(response).to have_http_status(:no_content)
|
||||||
|
expect(cluster.enabled).to be_falsey
|
||||||
|
expect(cluster.name).to eq('my-new-cluster-name')
|
||||||
|
expect(cluster.platform_kubernetes.namespace).to eq('my-namespace')
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'when invalid parameters are used' do
|
||||||
|
let(:params) do
|
||||||
|
{
|
||||||
|
cluster: {
|
||||||
|
enabled: false,
|
||||||
|
platform_kubernetes_attributes: {
|
||||||
|
namespace: 'my invalid namespace #@'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
end
|
||||||
|
|
||||||
|
it "rejects changes" do
|
||||||
|
go_json
|
||||||
|
|
||||||
|
expect(response).to have_http_status(:bad_request)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'when format is html' do
|
||||||
|
context 'when update enabled' do
|
||||||
|
let(:params) do
|
||||||
|
{
|
||||||
|
cluster: {
|
||||||
|
enabled: false,
|
||||||
|
name: 'my-new-cluster-name',
|
||||||
|
platform_kubernetes_attributes: {
|
||||||
|
namespace: 'my-namespace'
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
end
|
||||||
end
|
|
||||||
|
|
||||||
it "updates and redirects back to show page" do
|
it "updates and redirects back to show page" do
|
||||||
go
|
go
|
||||||
|
|
||||||
cluster.reload
|
cluster.reload
|
||||||
expect(response).to redirect_to(project_cluster_path(project, project.cluster))
|
expect(response).to redirect_to(project_cluster_path(project, cluster))
|
||||||
expect(flash[:notice]).to eq('Cluster was successfully updated.')
|
expect(flash[:notice]).to eq('Cluster was successfully updated.')
|
||||||
expect(cluster.enabled).to be_falsey
|
expect(cluster.enabled).to be_falsey
|
||||||
expect(cluster.name).to eq('my-new-cluster-name')
|
expect(cluster.name).to eq('my-new-cluster-name')
|
||||||
expect(cluster.platform_kubernetes.namespace).to eq('my-namespace')
|
expect(cluster.platform_kubernetes.namespace).to eq('my-namespace')
|
||||||
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
@ -228,6 +335,13 @@ describe Projects::ClustersController do
|
||||||
project_id: project,
|
project_id: project,
|
||||||
id: cluster)
|
id: cluster)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def go_json
|
||||||
|
put :update, params.merge(namespace_id: project.namespace,
|
||||||
|
project_id: project,
|
||||||
|
id: cluster,
|
||||||
|
format: :json)
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
describe 'DELETE destroy' do
|
describe 'DELETE destroy' do
|
||||||
|
|
|
@ -28,5 +28,9 @@ FactoryGirl.define do
|
||||||
provider_type :gcp
|
provider_type :gcp
|
||||||
provider_gcp factory: [:cluster_provider_gcp, :creating]
|
provider_gcp factory: [:cluster_provider_gcp, :creating]
|
||||||
end
|
end
|
||||||
|
|
||||||
|
trait :disabled do
|
||||||
|
enabled false
|
||||||
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -24,6 +24,7 @@ feature 'Gcp Cluster', :js do
|
||||||
before do
|
before do
|
||||||
visit project_clusters_path(project)
|
visit project_clusters_path(project)
|
||||||
|
|
||||||
|
click_link 'Add cluster'
|
||||||
click_link 'Create on GKE'
|
click_link 'Create on GKE'
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -116,7 +117,7 @@ feature 'Gcp Cluster', :js do
|
||||||
|
|
||||||
it 'user sees creation form with the successful message' do
|
it 'user sees creation form with the successful message' do
|
||||||
expect(page).to have_content('Cluster integration was successfully removed.')
|
expect(page).to have_content('Cluster integration was successfully removed.')
|
||||||
expect(page).to have_link('Create on GKE')
|
expect(page).to have_link('Add cluster')
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
@ -126,6 +127,7 @@ feature 'Gcp Cluster', :js do
|
||||||
before do
|
before do
|
||||||
visit project_clusters_path(project)
|
visit project_clusters_path(project)
|
||||||
|
|
||||||
|
click_link 'Add cluster'
|
||||||
click_link 'Create on GKE'
|
click_link 'Create on GKE'
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
|
@ -16,6 +16,7 @@ feature 'User Cluster', :js do
|
||||||
before do
|
before do
|
||||||
visit project_clusters_path(project)
|
visit project_clusters_path(project)
|
||||||
|
|
||||||
|
click_link 'Add cluster'
|
||||||
click_link 'Add an existing cluster'
|
click_link 'Add an existing cluster'
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -94,7 +95,7 @@ feature 'User Cluster', :js do
|
||||||
|
|
||||||
it 'user sees creation form with the successful message' do
|
it 'user sees creation form with the successful message' do
|
||||||
expect(page).to have_content('Cluster integration was successfully removed.')
|
expect(page).to have_content('Cluster integration was successfully removed.')
|
||||||
expect(page).to have_link('Add an existing cluster')
|
expect(page).to have_link('Add cluster')
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -14,12 +14,78 @@ feature 'Clusters', :js do
|
||||||
context 'when user does not have a cluster and visits cluster index page' do
|
context 'when user does not have a cluster and visits cluster index page' do
|
||||||
before do
|
before do
|
||||||
visit project_clusters_path(project)
|
visit project_clusters_path(project)
|
||||||
|
|
||||||
click_link 'Create on GKE'
|
|
||||||
end
|
end
|
||||||
|
|
||||||
it 'user sees a new page' do
|
it 'sees empty state' do
|
||||||
expect(page).to have_button('Create cluster')
|
expect(page).to have_link('Add cluster')
|
||||||
|
expect(page).to have_selector('.empty-state')
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'when user has a cluster and visits cluster index page' do
|
||||||
|
let!(:cluster) { create(:cluster, :project, :provided_by_gcp) }
|
||||||
|
let(:project) { cluster.project }
|
||||||
|
|
||||||
|
before do
|
||||||
|
visit project_clusters_path(project)
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'user sees a table with one cluster' do
|
||||||
|
# One is the header row, the other the cluster row
|
||||||
|
expect(page).to have_selector('.gl-responsive-table-row', count: 2)
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'user sees navigation tabs' do
|
||||||
|
expect(page.find('.js-active-tab').text).to include('Active')
|
||||||
|
expect(page.find('.js-active-tab .badge').text).to include('1')
|
||||||
|
|
||||||
|
expect(page.find('.js-inactive-tab').text).to include('Inactive')
|
||||||
|
expect(page.find('.js-inactive-tab .badge').text).to include('0')
|
||||||
|
|
||||||
|
expect(page.find('.js-all-tab').text).to include('All')
|
||||||
|
expect(page.find('.js-all-tab .badge').text).to include('1')
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'inline update of cluster' do
|
||||||
|
it 'user can update cluster' do
|
||||||
|
expect(page).to have_selector('.js-toggle-cluster-list')
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'with sucessfull request' do
|
||||||
|
it 'user sees updated cluster' do
|
||||||
|
expect do
|
||||||
|
page.find('.js-toggle-cluster-list').click
|
||||||
|
wait_for_requests
|
||||||
|
end.to change { cluster.reload.enabled }
|
||||||
|
|
||||||
|
expect(page).not_to have_selector('.is-checked')
|
||||||
|
expect(cluster.reload).not_to be_enabled
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'with failed request' do
|
||||||
|
it 'user sees not update cluster and error message' do
|
||||||
|
expect_any_instance_of(Clusters::UpdateService).to receive(:execute).and_call_original
|
||||||
|
allow_any_instance_of(Clusters::Cluster).to receive(:valid?) { false }
|
||||||
|
|
||||||
|
page.find('.js-toggle-cluster-list').click
|
||||||
|
|
||||||
|
expect(page).to have_content('Something went wrong on our end.')
|
||||||
|
expect(page).to have_selector('.is-checked')
|
||||||
|
expect(cluster.reload).to be_enabled
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'when user clicks on a cluster' do
|
||||||
|
before do
|
||||||
|
click_link cluster.name
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'user sees a cluster details page' do
|
||||||
|
expect(page).to have_button('Save')
|
||||||
|
expect(page.find(:css, '.cluster-name').value).to eq(cluster.name)
|
||||||
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -177,7 +177,7 @@ describe 'Edit Project Settings' do
|
||||||
click_button "Save changes"
|
click_button "Save changes"
|
||||||
end
|
end
|
||||||
|
|
||||||
expect(find(".sharing-permissions")).to have_selector(".project-feature-toggle.disabled", count: 2)
|
expect(find(".sharing-permissions")).to have_selector(".project-feature-toggle.is-disabled", count: 2)
|
||||||
end
|
end
|
||||||
|
|
||||||
it "shows empty features project homepage" do
|
it "shows empty features project homepage" do
|
||||||
|
@ -272,10 +272,10 @@ describe 'Edit Project Settings' do
|
||||||
end
|
end
|
||||||
|
|
||||||
def toggle_feature_off(feature_name)
|
def toggle_feature_off(feature_name)
|
||||||
find(".project-feature-controls[data-for=\"#{feature_name}\"] .project-feature-toggle.checked").click
|
find(".project-feature-controls[data-for=\"#{feature_name}\"] .project-feature-toggle.is-checked").click
|
||||||
end
|
end
|
||||||
|
|
||||||
def toggle_feature_on(feature_name)
|
def toggle_feature_on(feature_name)
|
||||||
find(".project-feature-controls[data-for=\"#{feature_name}\"] .project-feature-toggle:not(.checked)").click
|
find(".project-feature-controls[data-for=\"#{feature_name}\"] .project-feature-toggle:not(.is-checked)").click
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -0,0 +1,31 @@
|
||||||
|
require 'spec_helper'
|
||||||
|
|
||||||
|
describe ClustersFinder do
|
||||||
|
let(:project) { create(:project) }
|
||||||
|
set(:user) { create(:user) }
|
||||||
|
|
||||||
|
describe '#execute' do
|
||||||
|
let(:enabled_cluster) { create(:cluster, :provided_by_gcp, projects: [project]) }
|
||||||
|
let(:disabled_cluster) { create(:cluster, :disabled, :provided_by_gcp, projects: [project]) }
|
||||||
|
|
||||||
|
subject { described_class.new(project, user, scope).execute }
|
||||||
|
|
||||||
|
context 'when scope is all' do
|
||||||
|
let(:scope) { :all }
|
||||||
|
|
||||||
|
it { is_expected.to match_array([enabled_cluster, disabled_cluster]) }
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'when scope is active' do
|
||||||
|
let(:scope) { :active }
|
||||||
|
|
||||||
|
it { is_expected.to match_array([enabled_cluster]) }
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'when scope is inactive' do
|
||||||
|
let(:scope) { :inactive }
|
||||||
|
|
||||||
|
it { is_expected.to match_array([disabled_cluster]) }
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
|
@ -28,7 +28,7 @@ describe('Clusters', () => {
|
||||||
|
|
||||||
expect(
|
expect(
|
||||||
cluster.toggleButton.classList,
|
cluster.toggleButton.classList,
|
||||||
).not.toContain('checked');
|
).not.toContain('is-checked');
|
||||||
|
|
||||||
expect(
|
expect(
|
||||||
cluster.toggleInput.getAttribute('value'),
|
cluster.toggleInput.getAttribute('value'),
|
||||||
|
|
|
@ -0,0 +1,58 @@
|
||||||
|
import MockAdapter from 'axios-mock-adapter';
|
||||||
|
import axios from '~/lib/utils/axios_utils';
|
||||||
|
import setClusterTableToggles from '~/clusters/clusters_index';
|
||||||
|
import { setTimeout } from 'core-js/library/web/timers';
|
||||||
|
|
||||||
|
describe('Clusters table', () => {
|
||||||
|
preloadFixtures('clusters/index_cluster.html.raw');
|
||||||
|
let mock;
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
loadFixtures('clusters/index_cluster.html.raw');
|
||||||
|
mock = new MockAdapter(axios);
|
||||||
|
setClusterTableToggles();
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('update cluster', () => {
|
||||||
|
it('renders loading state while request is made', () => {
|
||||||
|
const button = document.querySelector('.js-toggle-cluster-list');
|
||||||
|
|
||||||
|
button.click();
|
||||||
|
|
||||||
|
expect(button.classList).toContain('is-loading');
|
||||||
|
expect(button.getAttribute('disabled')).toEqual('true');
|
||||||
|
});
|
||||||
|
|
||||||
|
afterEach(() => {
|
||||||
|
mock.restore();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('shows updated state after sucessfull request', (done) => {
|
||||||
|
mock.onPut().reply(200, {}, {});
|
||||||
|
const button = document.querySelector('.js-toggle-cluster-list');
|
||||||
|
button.click();
|
||||||
|
|
||||||
|
expect(button.classList).toContain('is-loading');
|
||||||
|
|
||||||
|
setTimeout(() => {
|
||||||
|
expect(button.classList).not.toContain('is-loading');
|
||||||
|
expect(button.classList).not.toContain('is-checked');
|
||||||
|
done();
|
||||||
|
}, 0);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('shows inital state after failed request', (done) => {
|
||||||
|
mock.onPut().reply(500, {}, {});
|
||||||
|
const button = document.querySelector('.js-toggle-cluster-list');
|
||||||
|
|
||||||
|
button.click();
|
||||||
|
expect(button.classList).toContain('is-loading');
|
||||||
|
|
||||||
|
setTimeout(() => {
|
||||||
|
expect(button.classList).not.toContain('is-loading');
|
||||||
|
expect(button.classList).toContain('is-checked');
|
||||||
|
done();
|
||||||
|
}, 0);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
|
@ -31,4 +31,19 @@ describe Projects::ClustersController, '(JavaScript fixtures)', type: :controlle
|
||||||
expect(response).to be_success
|
expect(response).to be_success
|
||||||
store_frontend_fixture(response, example.description)
|
store_frontend_fixture(response, example.description)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
context 'rendering non-empty state' do
|
||||||
|
before do
|
||||||
|
cluster
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'clusters/index_cluster.html.raw' do |example|
|
||||||
|
get :index,
|
||||||
|
namespace_id: namespace,
|
||||||
|
project_id: project
|
||||||
|
|
||||||
|
expect(response).to be_success
|
||||||
|
store_frontend_fixture(response, example.description)
|
||||||
|
end
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -0,0 +1,91 @@
|
||||||
|
import Vue from 'vue';
|
||||||
|
import toggleButton from '~/vue_shared/components/toggle_button.vue';
|
||||||
|
import mountComponent from '../../helpers/vue_mount_component_helper';
|
||||||
|
|
||||||
|
describe('Toggle Button', () => {
|
||||||
|
let vm;
|
||||||
|
let Component;
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
Component = Vue.extend(toggleButton);
|
||||||
|
});
|
||||||
|
|
||||||
|
afterEach(() => {
|
||||||
|
vm.$destroy();
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('render output', () => {
|
||||||
|
beforeEach(() => {
|
||||||
|
vm = mountComponent(Component, {
|
||||||
|
value: true,
|
||||||
|
name: 'foo',
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('renders input with provided name', () => {
|
||||||
|
expect(vm.$el.querySelector('input').getAttribute('name')).toEqual('foo');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('renders input with provided value', () => {
|
||||||
|
expect(vm.$el.querySelector('input').getAttribute('value')).toEqual('true');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('renders Enabled and Disabled text data attributes', () => {
|
||||||
|
expect(vm.$el.querySelector('button').getAttribute('data-enabled-text')).toEqual('Enabled');
|
||||||
|
expect(vm.$el.querySelector('button').getAttribute('data-disabled-text')).toEqual('Disabled');
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('is-checked', () => {
|
||||||
|
beforeEach(() => {
|
||||||
|
vm = mountComponent(Component, {
|
||||||
|
value: true,
|
||||||
|
});
|
||||||
|
|
||||||
|
spyOn(vm, '$emit');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('renders is checked class', () => {
|
||||||
|
expect(vm.$el.querySelector('button').classList.contains('is-checked')).toEqual(true);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('emits change event when clicked', () => {
|
||||||
|
vm.$el.querySelector('button').click();
|
||||||
|
|
||||||
|
expect(vm.$emit).toHaveBeenCalledWith('change', false);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('is-disabled', () => {
|
||||||
|
beforeEach(() => {
|
||||||
|
vm = mountComponent(Component, {
|
||||||
|
value: true,
|
||||||
|
disabledInput: true,
|
||||||
|
});
|
||||||
|
spyOn(vm, '$emit');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('renders disabled button', () => {
|
||||||
|
expect(vm.$el.querySelector('button').classList.contains('is-disabled')).toEqual(true);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('does not emit change event when clicked', () => {
|
||||||
|
vm.$el.querySelector('button').click();
|
||||||
|
|
||||||
|
expect(vm.$emit).not.toHaveBeenCalled();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('is-loading', () => {
|
||||||
|
beforeEach(() => {
|
||||||
|
vm = mountComponent(Component, {
|
||||||
|
value: true,
|
||||||
|
isLoading: true,
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('renders loading class', () => {
|
||||||
|
expect(vm.$el.querySelector('button').classList.contains('is-loading')).toEqual(true);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
|
@ -57,7 +57,7 @@ describe MigrateGcpClustersToNewClustersArchitectures, :migration do
|
||||||
expect(cluster.platform_type).to eq('kubernetes')
|
expect(cluster.platform_type).to eq('kubernetes')
|
||||||
|
|
||||||
expect(cluster.project).to eq(project)
|
expect(cluster.project).to eq(project)
|
||||||
expect(project.cluster).to eq(cluster)
|
expect(project.clusters).to include(cluster)
|
||||||
|
|
||||||
expect(cluster.provider_gcp.cluster).to eq(cluster)
|
expect(cluster.provider_gcp.cluster).to eq(cluster)
|
||||||
expect(cluster.provider_gcp.status).to eq(status)
|
expect(cluster.provider_gcp.status).to eq(status)
|
||||||
|
@ -134,7 +134,7 @@ describe MigrateGcpClustersToNewClustersArchitectures, :migration do
|
||||||
expect(cluster.platform_type).to eq('kubernetes')
|
expect(cluster.platform_type).to eq('kubernetes')
|
||||||
|
|
||||||
expect(cluster.project).to eq(project)
|
expect(cluster.project).to eq(project)
|
||||||
expect(project.cluster).to eq(cluster)
|
expect(project.clusters).to include(cluster)
|
||||||
|
|
||||||
expect(cluster.provider_gcp.cluster).to eq(cluster)
|
expect(cluster.provider_gcp.cluster).to eq(cluster)
|
||||||
expect(cluster.provider_gcp.status).to eq(status)
|
expect(cluster.provider_gcp.status).to eq(status)
|
||||||
|
|
|
@ -198,4 +198,26 @@ describe Clusters::Cluster do
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
describe '#created?' do
|
||||||
|
let(:cluster) { create(:cluster, :provided_by_gcp) }
|
||||||
|
|
||||||
|
subject { cluster.created? }
|
||||||
|
|
||||||
|
context 'when status_name is :created' do
|
||||||
|
before do
|
||||||
|
allow(cluster).to receive_message_chain(:provider, :status_name).and_return(:created)
|
||||||
|
end
|
||||||
|
|
||||||
|
it { is_expected.to eq(true) }
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'when status_name is not :created' do
|
||||||
|
before do
|
||||||
|
allow(cluster).to receive_message_chain(:provider, :status_name).and_return(:creating)
|
||||||
|
end
|
||||||
|
|
||||||
|
it { is_expected.to eq(false) }
|
||||||
|
end
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -78,7 +78,7 @@ describe Project do
|
||||||
it { is_expected.to have_many(:uploads).dependent(:destroy) }
|
it { is_expected.to have_many(:uploads).dependent(:destroy) }
|
||||||
it { is_expected.to have_many(:pipeline_schedules) }
|
it { is_expected.to have_many(:pipeline_schedules) }
|
||||||
it { is_expected.to have_many(:members_and_requesters) }
|
it { is_expected.to have_many(:members_and_requesters) }
|
||||||
it { is_expected.to have_one(:cluster) }
|
it { is_expected.to have_many(:clusters) }
|
||||||
it { is_expected.to have_many(:custom_attributes).class_name('ProjectCustomAttribute') }
|
it { is_expected.to have_many(:custom_attributes).class_name('ProjectCustomAttribute') }
|
||||||
|
|
||||||
context 'after initialized' do
|
context 'after initialized' do
|
||||||
|
|
|
@ -31,4 +31,44 @@ describe Clusters::ClusterPresenter do
|
||||||
it { is_expected.to include(cluster.provider.zone) }
|
it { is_expected.to include(cluster.provider.zone) }
|
||||||
it { is_expected.to include(cluster.name) }
|
it { is_expected.to include(cluster.name) }
|
||||||
end
|
end
|
||||||
|
|
||||||
|
describe '#can_toggle_cluster' do
|
||||||
|
let(:user) { create(:user) }
|
||||||
|
|
||||||
|
before do
|
||||||
|
allow(cluster).to receive(:current_user).and_return(user)
|
||||||
|
end
|
||||||
|
|
||||||
|
subject { described_class.new(cluster).can_toggle_cluster? }
|
||||||
|
|
||||||
|
context 'when user can update' do
|
||||||
|
before do
|
||||||
|
allow_any_instance_of(described_class).to receive(:can?).with(user, :update_cluster, cluster).and_return(true)
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'when cluster is created' do
|
||||||
|
before do
|
||||||
|
allow(cluster).to receive(:created?).and_return(true)
|
||||||
|
end
|
||||||
|
|
||||||
|
it { is_expected.to eq(true) }
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'when cluster is not created' do
|
||||||
|
before do
|
||||||
|
allow(cluster).to receive(:created?).and_return(false)
|
||||||
|
end
|
||||||
|
|
||||||
|
it { is_expected.to eq(false) }
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'when user can not update' do
|
||||||
|
before do
|
||||||
|
allow_any_instance_of(described_class).to receive(:can?).with(user, :update_cluster, cluster).and_return(false)
|
||||||
|
end
|
||||||
|
|
||||||
|
it { is_expected.to eq(false) }
|
||||||
|
end
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -4,10 +4,11 @@ describe Clusters::CreateService do
|
||||||
let(:access_token) { 'xxx' }
|
let(:access_token) { 'xxx' }
|
||||||
let(:project) { create(:project) }
|
let(:project) { create(:project) }
|
||||||
let(:user) { create(:user) }
|
let(:user) { create(:user) }
|
||||||
let(:result) { described_class.new(project, user, params).execute(access_token) }
|
|
||||||
|
subject { described_class.new(project, user, params).execute(access_token) }
|
||||||
|
|
||||||
context 'when provider is gcp' do
|
context 'when provider is gcp' do
|
||||||
context 'when correct params' do
|
shared_context 'valid params' do
|
||||||
let(:params) do
|
let(:params) do
|
||||||
{
|
{
|
||||||
name: 'test-cluster',
|
name: 'test-cluster',
|
||||||
|
@ -20,27 +21,9 @@ describe Clusters::CreateService do
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
end
|
end
|
||||||
|
|
||||||
it 'creates a cluster object and performs a worker' do
|
|
||||||
expect(ClusterProvisionWorker).to receive(:perform_async)
|
|
||||||
|
|
||||||
expect { result }
|
|
||||||
.to change { Clusters::Cluster.count }.by(1)
|
|
||||||
.and change { Clusters::Providers::Gcp.count }.by(1)
|
|
||||||
|
|
||||||
expect(result.name).to eq('test-cluster')
|
|
||||||
expect(result.user).to eq(user)
|
|
||||||
expect(result.project).to eq(project)
|
|
||||||
expect(result.provider.gcp_project_id).to eq('gcp-project')
|
|
||||||
expect(result.provider.zone).to eq('us-central1-a')
|
|
||||||
expect(result.provider.num_nodes).to eq(1)
|
|
||||||
expect(result.provider.machine_type).to eq('machine_type-a')
|
|
||||||
expect(result.provider.access_token).to eq(access_token)
|
|
||||||
expect(result.platform).to be_nil
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
|
|
||||||
context 'when invalid params' do
|
shared_context 'invalid params' do
|
||||||
let(:params) do
|
let(:params) do
|
||||||
{
|
{
|
||||||
name: 'test-cluster',
|
name: 'test-cluster',
|
||||||
|
@ -53,11 +36,57 @@ describe Clusters::CreateService do
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
end
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
shared_examples 'create cluster' do
|
||||||
|
it 'creates a cluster object and performs a worker' do
|
||||||
|
expect(ClusterProvisionWorker).to receive(:perform_async)
|
||||||
|
|
||||||
|
expect { subject }
|
||||||
|
.to change { Clusters::Cluster.count }.by(1)
|
||||||
|
.and change { Clusters::Providers::Gcp.count }.by(1)
|
||||||
|
|
||||||
|
expect(subject.name).to eq('test-cluster')
|
||||||
|
expect(subject.user).to eq(user)
|
||||||
|
expect(subject.project).to eq(project)
|
||||||
|
expect(subject.provider.gcp_project_id).to eq('gcp-project')
|
||||||
|
expect(subject.provider.zone).to eq('us-central1-a')
|
||||||
|
expect(subject.provider.num_nodes).to eq(1)
|
||||||
|
expect(subject.provider.machine_type).to eq('machine_type-a')
|
||||||
|
expect(subject.provider.access_token).to eq(access_token)
|
||||||
|
expect(subject.platform).to be_nil
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
shared_examples 'error' do
|
||||||
it 'returns an error' do
|
it 'returns an error' do
|
||||||
expect(ClusterProvisionWorker).not_to receive(:perform_async)
|
expect(ClusterProvisionWorker).not_to receive(:perform_async)
|
||||||
expect { result }.to change { Clusters::Cluster.count }.by(0)
|
expect { subject }.to change { Clusters::Cluster.count }.by(0)
|
||||||
expect(result.errors[:"provider_gcp.gcp_project_id"]).to be_present
|
expect(subject.errors[:"provider_gcp.gcp_project_id"]).to be_present
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'when project has no clusters' do
|
||||||
|
context 'when correct params' do
|
||||||
|
include_context 'valid params'
|
||||||
|
|
||||||
|
include_examples 'create cluster'
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'when invalid params' do
|
||||||
|
include_context 'invalid params'
|
||||||
|
|
||||||
|
include_examples 'error'
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'when project has a cluster' do
|
||||||
|
include_context 'valid params'
|
||||||
|
let!(:cluster) { create(:cluster, :provided_by_gcp, projects: [project]) }
|
||||||
|
|
||||||
|
it 'does not create a cluster' do
|
||||||
|
expect(ClusterProvisionWorker).not_to receive(:perform_async)
|
||||||
|
expect { subject }.to raise_error(ArgumentError).and change { Clusters::Cluster.count }.by(0)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
Loading…
Reference in New Issue