Skip to content
Snippets Groups Projects
Commit 5a8431fe authored by GitLab Bot's avatar GitLab Bot
Browse files

Add latest changes from gitlab-org/gitlab@12-5-stable-ee

parent 4d477238
No related branches found
No related tags found
No related merge requests found
Showing
with 732 additions and 206 deletions
Loading
Loading
@@ -168,7 +168,7 @@ export default {
}
 
const recentBoardsPromise = new Promise((resolve, reject) =>
gl.boardService
boardsStore
.recentBoards()
.then(resolve)
.catch(err => {
Loading
Loading
@@ -184,7 +184,7 @@ export default {
}),
);
 
Promise.all([gl.boardService.allBoards(), recentBoardsPromise])
Promise.all([boardsStore.allBoards(), recentBoardsPromise])
.then(([allBoards, recentBoards]) => [allBoards.data, recentBoards.data])
.then(([allBoardsJson, recentBoardsJson]) => {
this.loading = false;
Loading
Loading
<script>
import _ from 'underscore';
import { mapState } from 'vuex';
import { GlTooltipDirective } from '@gitlab/ui';
import { sprintf, __ } from '~/locale';
import Icon from '~/vue_shared/components/icon.vue';
Loading
Loading
@@ -63,6 +64,7 @@ export default {
};
},
computed: {
...mapState(['isShowingLabels']),
numberOverLimit() {
return this.issue.assignees.length - this.limitBeforeCounter;
},
Loading
Loading
@@ -92,7 +94,7 @@ export default {
return false;
},
showLabelFooter() {
return this.issue.labels.find(l => this.showLabel(l)) !== undefined;
return this.isShowingLabels && this.issue.labels.find(this.showLabel);
},
issueReferencePath() {
const { referencePath, groupId } = this.issue;
Loading
Loading
<script>
/* global ListIssue */
import { urlParamsToObject } from '~/lib/utils/common_utils';
import boardsStore from '~/boards/stores/boards_store';
import ModalHeader from './header.vue';
import ModalList from './list.vue';
import ModalFooter from './footer.vue';
Loading
Loading
@@ -109,7 +110,7 @@ export default {
loadIssues(clearIssues = false) {
if (!this.showAddIssuesModal) return false;
 
return gl.boardService
return boardsStore
.getBacklog({
...urlParamsToObject(this.filter.path),
page: this.page,
Loading
Loading
Loading
Loading
@@ -13,6 +13,7 @@ import 'ee_else_ce/boards/models/issue';
import 'ee_else_ce/boards/models/list';
import '~/boards/models/milestone';
import '~/boards/models/project';
import store from '~/boards/stores';
import boardsStore from '~/boards/stores/boards_store';
import ModalStore from '~/boards/stores/modal_store';
import BoardService from 'ee_else_ce/boards/services/board_service';
Loading
Loading
@@ -29,6 +30,7 @@ import {
} from '~/lib/utils/common_utils';
import boardConfigToggle from 'ee_else_ce/boards/config_toggle';
import toggleFocusMode from 'ee_else_ce/boards/toggle_focus';
import toggleLabels from 'ee_else_ce/boards/toggle_labels';
import {
setPromotionState,
setWeigthFetchingState,
Loading
Loading
@@ -67,6 +69,7 @@ export default () => {
BoardSidebar,
BoardAddIssuesModal,
},
store,
data: {
state: boardsStore.state,
loading: true,
Loading
Loading
@@ -314,5 +317,6 @@ export default () => {
}
 
toggleFocusMode(ModalStore, boardsStore, $boardApp);
toggleLabels();
mountMultipleBoardsSwitcher();
};
Loading
Loading
@@ -50,8 +50,8 @@ class List {
this.page = 1;
this.loading = true;
this.loadingMore = false;
this.issues = [];
this.issuesSize = 0;
this.issues = obj.issues || [];
this.issuesSize = obj.issuesSize ? obj.issuesSize : 0;
this.defaultAvatar = defaultAvatar;
 
if (obj.label) {
Loading
Loading
export default {
getLabelToggleState: state => (state.isShowingLabels ? 'on' : 'off'),
};
import Vue from 'vue';
import Vuex from 'vuex';
import state from 'ee_else_ce/boards/stores/state';
import getters from 'ee_else_ce/boards/stores/getters';
import actions from 'ee_else_ce/boards/stores/actions';
import mutations from 'ee_else_ce/boards/stores/mutations';
 
Vue.use(Vuex);
 
export default () =>
export const createStore = () =>
new Vuex.Store({
state,
getters,
actions,
mutations,
});
export default createStore();
export default () => ({
// ...
isShowingLabels: true,
});
export default () => {};
Loading
Loading
@@ -8,11 +8,12 @@ import Flash from '../flash';
import Poll from '../lib/utils/poll';
import initSettingsPanels from '../settings_panels';
import eventHub from './event_hub';
import { APPLICATION_STATUS, INGRESS, INGRESS_DOMAIN_SUFFIX } from './constants';
import { APPLICATION_STATUS, INGRESS, INGRESS_DOMAIN_SUFFIX, CROSSPLANE } from './constants';
import ClustersService from './services/clusters_service';
import ClustersStore from './stores/clusters_store';
import Applications from './components/applications.vue';
import setupToggleButtons from '../toggle_buttons';
import initProjectSelectDropdown from '~/project_select';
 
const Environments = () => import('ee_component/clusters/components/environments.vue');
 
Loading
Loading
@@ -37,6 +38,8 @@ export default class Clusters {
installJupyterPath,
installKnativePath,
updateKnativePath,
installElasticStackPath,
installCrossplanePath,
installPrometheusPath,
managePrometheusPath,
clusterEnvironmentsPath,
Loading
Loading
@@ -81,11 +84,13 @@ export default class Clusters {
installHelmEndpoint: installHelmPath,
installIngressEndpoint: installIngressPath,
installCertManagerEndpoint: installCertManagerPath,
installCrossplaneEndpoint: installCrossplanePath,
installRunnerEndpoint: installRunnerPath,
installPrometheusEndpoint: installPrometheusPath,
installJupyterEndpoint: installJupyterPath,
installKnativeEndpoint: installKnativePath,
updateKnativeEndpoint: updateKnativePath,
installElasticStackEndpoint: installElasticStackPath,
clusterEnvironmentsEndpoint: clusterEnvironmentsPath,
});
 
Loading
Loading
@@ -108,8 +113,10 @@ export default class Clusters {
this.ingressDomainHelpText &&
this.ingressDomainHelpText.querySelector('.js-ingress-domain-snippet');
 
initProjectSelectDropdown();
Clusters.initDismissableCallout();
initSettingsPanels();
const toggleButtonsContainer = document.querySelector('.js-cluster-enable-toggle-area');
if (toggleButtonsContainer) {
setupToggleButtons(toggleButtonsContainer);
Loading
Loading
@@ -222,6 +229,7 @@ export default class Clusters {
eventHub.$on('saveKnativeDomain', data => this.saveKnativeDomain(data));
eventHub.$on('setKnativeHostname', data => this.setKnativeHostname(data));
eventHub.$on('uninstallApplication', data => this.uninstallApplication(data));
eventHub.$on('setCrossplaneProviderStack', data => this.setCrossplaneProviderStack(data));
// Add event listener to all the banner close buttons
this.addBannerCloseHandler(this.unreachableContainer, 'unreachable');
this.addBannerCloseHandler(this.authenticationFailureContainer, 'authentication_failure');
Loading
Loading
@@ -233,6 +241,7 @@ export default class Clusters {
eventHub.$off('updateApplication', this.updateApplication);
eventHub.$off('saveKnativeDomain');
eventHub.$off('setKnativeHostname');
eventHub.$off('setCrossplaneProviderStack');
eventHub.$off('uninstallApplication');
}
 
Loading
Loading
@@ -399,18 +408,33 @@ export default class Clusters {
}
 
installApplication({ id: appId, params }) {
this.store.updateAppProperty(appId, 'requestReason', null);
this.store.updateAppProperty(appId, 'statusReason', null);
return Clusters.validateInstallation(appId, params)
.then(() => {
this.store.updateAppProperty(appId, 'requestReason', null);
this.store.updateAppProperty(appId, 'statusReason', null);
this.store.installApplication(appId);
// eslint-disable-next-line promise/no-nesting
this.service.installApplication(appId, params).catch(() => {
this.store.notifyInstallFailure(appId);
this.store.updateAppProperty(
appId,
'requestReason',
s__('ClusterIntegration|Request to begin installing failed'),
);
});
})
.catch(error => this.store.updateAppProperty(appId, 'validationError', error));
}
 
this.store.installApplication(appId);
static validateInstallation(appId, params) {
return new Promise((resolve, reject) => {
if (appId === CROSSPLANE && !params.stack) {
reject(s__('ClusterIntegration|Select a stack to install Crossplane.'));
return;
}
 
return this.service.installApplication(appId, params).catch(() => {
this.store.notifyInstallFailure(appId);
this.store.updateAppProperty(
appId,
'requestReason',
s__('ClusterIntegration|Request to begin installing failed'),
);
resolve();
});
}
 
Loading
Loading
@@ -458,6 +482,12 @@ export default class Clusters {
this.store.updateAppProperty(appId, 'hostname', data.hostname);
}
 
setCrossplaneProviderStack(data) {
const appId = data.id;
this.store.updateAppProperty(appId, 'stack', data.stack.code);
this.store.updateAppProperty(appId, 'validationError', null);
}
destroy() {
this.destroyed = true;
 
Loading
Loading
Loading
Loading
@@ -9,9 +9,11 @@ import jeagerLogo from 'images/cluster_app_logos/jeager.png';
import jupyterhubLogo from 'images/cluster_app_logos/jupyterhub.png';
import kubernetesLogo from 'images/cluster_app_logos/kubernetes.png';
import certManagerLogo from 'images/cluster_app_logos/cert_manager.png';
import crossplaneLogo from 'images/cluster_app_logos/crossplane.png';
import knativeLogo from 'images/cluster_app_logos/knative.png';
import meltanoLogo from 'images/cluster_app_logos/meltano.png';
import prometheusLogo from 'images/cluster_app_logos/prometheus.png';
import elasticStackLogo from 'images/cluster_app_logos/elastic_stack.png';
import { s__, sprintf } from '../../locale';
import applicationRow from './application_row.vue';
import clipboardButton from '../../vue_shared/components/clipboard_button.vue';
Loading
Loading
@@ -19,6 +21,7 @@ import KnativeDomainEditor from './knative_domain_editor.vue';
import { CLUSTER_TYPE, PROVIDER_TYPE, APPLICATION_STATUS, INGRESS } from '../constants';
import LoadingButton from '~/vue_shared/components/loading_button.vue';
import eventHub from '~/clusters/event_hub';
import CrossplaneProviderStack from './crossplane_provider_stack.vue';
 
export default {
components: {
Loading
Loading
@@ -27,6 +30,7 @@ export default {
LoadingButton,
GlLoadingIcon,
KnativeDomainEditor,
CrossplaneProviderStack,
},
props: {
type: {
Loading
Loading
@@ -88,9 +92,11 @@ export default {
jupyterhubLogo,
kubernetesLogo,
certManagerLogo,
crossplaneLogo,
knativeLogo,
meltanoLogo,
prometheusLogo,
elasticStackLogo,
}),
computed: {
isProjectCluster() {
Loading
Loading
@@ -114,6 +120,15 @@ export default {
certManagerInstalled() {
return this.applications.cert_manager.status === APPLICATION_STATUS.INSTALLED;
},
crossplaneInstalled() {
return this.applications.crossplane.status === APPLICATION_STATUS.INSTALLED;
},
enableClusterApplicationCrossplane() {
return gon.features && gon.features.enableClusterApplicationCrossplane;
},
enableClusterApplicationElasticStack() {
return gon.features && gon.features.enableClusterApplicationElasticStack;
},
ingressDescription() {
return sprintf(
_.escape(
Loading
Loading
@@ -146,6 +161,24 @@ export default {
false,
);
},
crossplaneDescription() {
return sprintf(
_.escape(
s__(
`ClusterIntegration|Crossplane enables declarative provisioning of managed services from your cloud of choice using %{kubectl} or %{gitlabIntegrationLink}.
Crossplane runs inside your Kubernetes cluster and supports secure connectivity and secrets management between app containers and the cloud services they depend on.`,
),
),
{
gitlabIntegrationLink: `<a href="https://docs.gitlab.com/ce/user/project/integrations/crossplane.html"
target="_blank" rel="noopener noreferrer">
${_.escape(s__('ClusterIntegration|Gitlab Integration'))}</a>`,
kubectl: `<code>kubectl</code>`,
},
false,
);
},
prometheusDescription() {
return sprintf(
_.escape(
Loading
Loading
@@ -168,9 +201,18 @@ export default {
jupyterHostname() {
return this.applications.jupyter.hostname;
},
elasticStackInstalled() {
return this.applications.elastic_stack.status === APPLICATION_STATUS.INSTALLED;
},
elasticStackKibanaHostname() {
return this.applications.elastic_stack.kibana_hostname;
},
knative() {
return this.applications.knative;
},
crossplane() {
return this.applications.crossplane;
},
cloudRun() {
return this.providerType === PROVIDER_TYPE.GCP && this.preInstalledKnative;
},
Loading
Loading
@@ -207,6 +249,12 @@ export default {
hostname,
});
},
setCrossplaneProviderStack(stack) {
eventHub.$emit('setCrossplaneProviderStack', {
id: 'crossplane',
stack,
});
},
},
};
</script>
Loading
Loading
@@ -217,7 +265,7 @@ export default {
<p class="append-bottom-0">
{{
s__(`ClusterIntegration|Choose which applications to install on your Kubernetes cluster.
Helm Tiller is required to install any of the following applications.`)
Helm Tiller is required to install any of the following applications.`)
}}
<a :href="helpPath">{{ __('More information') }}</a>
</p>
Loading
Loading
@@ -242,9 +290,9 @@ export default {
<div slot="description">
{{
s__(`ClusterIntegration|Helm streamlines installing
and managing Kubernetes applications.
Tiller runs inside of your Kubernetes Cluster,
and manages releases of your charts.`)
and managing Kubernetes applications.
Tiller runs inside of your Kubernetes Cluster,
and manages releases of your charts.`)
}}
</div>
</application-row>
Loading
Loading
@@ -252,7 +300,7 @@ export default {
<div class="svg-container" v-html="helmInstallIllustration"></div>
{{
s__(`ClusterIntegration|You must first install Helm Tiller before
installing the applications below`)
installing the applications below`)
}}
</div>
<application-row
Loading
Loading
@@ -275,8 +323,8 @@ export default {
<p>
{{
s__(`ClusterIntegration|Ingress gives you a way to route
requests to services based on the request host or path,
centralizing a number of services into a single entrypoint.`)
requests to services based on the request host or path,
centralizing a number of services into a single entrypoint.`)
}}
</p>
 
Loading
Loading
@@ -308,8 +356,8 @@ export default {
<p class="form-text text-muted">
{{
s__(`ClusterIntegration|Point a wildcard DNS to this
generated endpoint in order to access
your application after it has been deployed.`)
generated endpoint in order to access
your application after it has been deployed.`)
}}
<a :href="ingressDnsHelpPath" target="_blank" rel="noopener noreferrer">
{{ __('More information') }}
Loading
Loading
@@ -320,8 +368,8 @@ export default {
<p v-if="!ingressExternalEndpoint" class="settings-message js-no-endpoint-message">
{{
s__(`ClusterIntegration|The endpoint is in
the process of being assigned. Please check your Kubernetes
cluster or Quotas on Google Kubernetes Engine if it takes a long time.`)
the process of being assigned. Please check your Kubernetes
cluster or Quotas on Google Kubernetes Engine if it takes a long time.`)
}}
<a :href="ingressDnsHelpPath" target="_blank" rel="noopener noreferrer">
{{ __('More information') }}
Loading
Loading
@@ -368,7 +416,7 @@ export default {
<p class="form-text text-muted">
{{
s__(`ClusterIntegration|Issuers represent a certificate authority.
You must provide an email address for your Issuer. `)
You must provide an email address for your Issuer. `)
}}
<a
href="http://docs.cert-manager.io/en/latest/reference/issuers.html?highlight=email"
Loading
Loading
@@ -424,12 +472,40 @@ export default {
<div slot="description">
{{
s__(`ClusterIntegration|GitLab Runner connects to the
repository and executes CI/CD jobs,
pushing results back and deploying
applications to production.`)
repository and executes CI/CD jobs,
pushing results back and deploying
applications to production.`)
}}
</div>
</application-row>
<application-row
v-if="enableClusterApplicationCrossplane"
id="crossplane"
:logo-url="crossplaneLogo"
:title="applications.crossplane.title"
:status="applications.crossplane.status"
:status-reason="applications.crossplane.statusReason"
:request-status="applications.crossplane.requestStatus"
:request-reason="applications.crossplane.requestReason"
:installed="applications.crossplane.installed"
:install-failed="applications.crossplane.installFailed"
:uninstallable="applications.crossplane.uninstallable"
:uninstall-successful="applications.crossplane.uninstallSuccessful"
:uninstall-failed="applications.crossplane.uninstallFailed"
:install-application-request-params="{ stack: applications.crossplane.stack }"
:disabled="!helmInstalled"
title-link="https://crossplane.io"
>
<template>
<div slot="description">
<p v-html="crossplaneDescription"></p>
<div class="form-group">
<CrossplaneProviderStack :crossplane="crossplane" @set="setCrossplaneProviderStack" />
</div>
</div>
</template>
</application-row>
<application-row
id="jupyter"
:logo-url="jupyterhubLogo"
Loading
Loading
@@ -451,10 +527,10 @@ export default {
<p>
{{
s__(`ClusterIntegration|JupyterHub, a multi-user Hub, spawns,
manages, and proxies multiple instances of the single-user
Jupyter notebook server. JupyterHub can be used to serve
notebooks to a class of students, a corporate data science group,
or a scientific research group.`)
manages, and proxies multiple instances of the single-user
Jupyter notebook server. JupyterHub can be used to serve
notebooks to a class of students, a corporate data science group,
or a scientific research group.`)
}}
</p>
 
Loading
Loading
@@ -481,7 +557,7 @@ export default {
<p v-if="ingressInstalled" class="form-text text-muted">
{{
s__(`ClusterIntegration|Replace this with your own hostname if you want.
If you do so, point hostname to Ingress IP Address from above.`)
If you do so, point hostname to Ingress IP Address from above.`)
}}
<a :href="ingressDnsHelpPath" target="_blank" rel="noopener noreferrer">
{{ __('More information') }}
Loading
Loading
@@ -527,9 +603,9 @@ export default {
<p>
{{
s__(`ClusterIntegration|Knative extends Kubernetes to provide
a set of middleware components that are essential to build modern,
source-centric, and container-based applications that can run
anywhere: on premises, in the cloud, or even in a third-party data center.`)
a set of middleware components that are essential to build modern,
source-centric, and container-based applications that can run
anywhere: on premises, in the cloud, or even in a third-party data center.`)
}}
</p>
 
Loading
Loading
@@ -542,6 +618,75 @@ export default {
/>
</div>
</application-row>
<application-row
v-if="enableClusterApplicationElasticStack"
id="elastic_stack"
:logo-url="elasticStackLogo"
:title="applications.elastic_stack.title"
:status="applications.elastic_stack.status"
:status-reason="applications.elastic_stack.statusReason"
:request-status="applications.elastic_stack.requestStatus"
:request-reason="applications.elastic_stack.requestReason"
:version="applications.elastic_stack.version"
:chart-repo="applications.elastic_stack.chartRepo"
:update-available="applications.elastic_stack.updateAvailable"
:installed="applications.elastic_stack.installed"
:install-failed="applications.elastic_stack.installFailed"
:update-successful="applications.elastic_stack.updateSuccessful"
:update-failed="applications.elastic_stack.updateFailed"
:uninstallable="applications.elastic_stack.uninstallable"
:uninstall-successful="applications.elastic_stack.uninstallSuccessful"
:uninstall-failed="applications.elastic_stack.uninstallFailed"
:disabled="!helmInstalled"
:install-application-request-params="{
kibana_hostname: applications.elastic_stack.kibana_hostname,
}"
title-link="https://github.com/helm/charts/tree/master/stable/elastic-stack"
>
<div slot="description">
<p>
{{
s__(
`ClusterIntegration|The elastic stack collects logs from all pods in your cluster`,
)
}}
</p>
<template v-if="ingressExternalEndpoint">
<div class="form-group">
<label for="elastic-stack-kibana-hostname">{{
s__('ClusterIntegration|Kibana Hostname')
}}</label>
<div class="input-group">
<input
v-model="applications.elastic_stack.kibana_hostname"
:readonly="elasticStackInstalled"
type="text"
class="form-control js-hostname"
/>
<span class="input-group-btn">
<clipboard-button
:text="elasticStackKibanaHostname"
:title="s__('ClusterIntegration|Copy Kibana Hostname')"
class="js-clipboard-btn"
/>
</span>
</div>
<p v-if="ingressInstalled" class="form-text text-muted">
{{
s__(`ClusterIntegration|Replace this with your own hostname if you want.
If you do so, point hostname to Ingress IP Address from above.`)
}}
<a :href="ingressDnsHelpPath" target="_blank" rel="noopener noreferrer">
{{ __('More information') }}
</a>
</p>
</div>
</template>
</div>
</application-row>
</div>
</section>
</template>
<script>
import { GlDropdown, GlDropdownItem } from '@gitlab/ui';
import Icon from '~/vue_shared/components/icon.vue';
import { s__ } from '../../locale';
export default {
name: 'CrossplaneProviderStack',
components: {
GlDropdown,
GlDropdownItem,
Icon,
},
props: {
stacks: {
type: Array,
required: false,
default: () => [
{
name: s__('Google Cloud Platform'),
code: 'gcp',
},
{
name: s__('Amazon Web Services'),
code: 'aws',
},
{
name: s__('Microsoft Azure'),
code: 'azure',
},
{
name: s__('Rook'),
code: 'rook',
},
],
},
crossplane: {
type: Object,
required: true,
},
},
computed: {
dropdownText() {
const result = this.stacks.reduce((map, obj) => {
// eslint-disable-next-line no-param-reassign
map[obj.code] = obj.name;
return map;
}, {});
const { stack } = this.crossplane;
if (stack !== '') {
return result[stack];
}
return s__('Select Stack');
},
validationError() {
return this.crossplane.validationError;
},
},
methods: {
selectStack(stack) {
this.$emit('set', stack);
},
},
};
</script>
<template>
<div>
<label>
{{ s__('ClusterIntegration|Enabled stack') }}
</label>
<gl-dropdown
:disabled="crossplane.installed"
:text="dropdownText"
toggle-class="dropdown-menu-toggle gl-field-error-outline"
class="w-100"
:class="{ 'gl-show-field-errors': validationError }"
>
<gl-dropdown-item v-for="stack in stacks" :key="stack.code" @click="selectStack(stack)">
<span class="ml-1">{{ stack.name }}</span>
</gl-dropdown-item>
</gl-dropdown>
<span v-if="validationError" class="gl-field-error">{{ validationError }}</span>
<p class="form-text text-muted">
{{ s__(`You must select a stack for configuring your cloud provider. Learn more about`) }}
<a
href="https://crossplane.io/docs/master/stacks-guide.html"
target="_blank"
rel="noopener noreferrer"
>{{ __('Crossplane') }}</a
>
</p>
</div>
</template>
Loading
Loading
@@ -2,7 +2,16 @@
import { GlModal } from '@gitlab/ui';
import { sprintf, s__ } from '~/locale';
import trackUninstallButtonClickMixin from 'ee_else_ce/clusters/mixins/track_uninstall_button_click';
import { HELM, INGRESS, CERT_MANAGER, PROMETHEUS, RUNNER, KNATIVE, JUPYTER } from '../constants';
import {
HELM,
INGRESS,
CERT_MANAGER,
PROMETHEUS,
RUNNER,
KNATIVE,
JUPYTER,
ELASTIC_STACK,
} from '../constants';
 
const CUSTOM_APP_WARNING_TEXT = {
[HELM]: sprintf(
Loading
Loading
@@ -28,6 +37,7 @@ const CUSTOM_APP_WARNING_TEXT = {
[JUPYTER]: s__(
'ClusterIntegration|All data not committed to GitLab will be deleted and cannot be restored.',
),
[ELASTIC_STACK]: s__('ClusterIntegration|All data will be deleted and cannot be restored.'),
};
 
export default {
Loading
Loading
Loading
Loading
@@ -50,8 +50,19 @@ export const JUPYTER = 'jupyter';
export const KNATIVE = 'knative';
export const RUNNER = 'runner';
export const CERT_MANAGER = 'cert_manager';
export const CROSSPLANE = 'crossplane';
export const PROMETHEUS = 'prometheus';
export const ELASTIC_STACK = 'elastic_stack';
 
export const APPLICATIONS = [HELM, INGRESS, JUPYTER, KNATIVE, RUNNER, CERT_MANAGER, PROMETHEUS];
export const APPLICATIONS = [
HELM,
INGRESS,
JUPYTER,
KNATIVE,
RUNNER,
CERT_MANAGER,
PROMETHEUS,
ELASTIC_STACK,
];
 
export const INGRESS_DOMAIN_SUFFIX = '.nip.io';
Loading
Loading
@@ -7,10 +7,12 @@ export default class ClusterService {
helm: this.options.installHelmEndpoint,
ingress: this.options.installIngressEndpoint,
cert_manager: this.options.installCertManagerEndpoint,
crossplane: this.options.installCrossplaneEndpoint,
runner: this.options.installRunnerEndpoint,
prometheus: this.options.installPrometheusEndpoint,
jupyter: this.options.installJupyterEndpoint,
knative: this.options.installKnativeEndpoint,
elastic_stack: this.options.installElasticStackEndpoint,
};
this.appUpdateEndpointMap = {
knative: this.options.updateKnativeEndpoint,
Loading
Loading
Loading
Loading
@@ -5,6 +5,8 @@ import {
JUPYTER,
KNATIVE,
CERT_MANAGER,
ELASTIC_STACK,
CROSSPLANE,
RUNNER,
APPLICATION_INSTALLED_STATUSES,
APPLICATION_STATUS,
Loading
Loading
@@ -25,6 +27,7 @@ const applicationInitialState = {
uninstallable: false,
uninstallFailed: false,
uninstallSuccessful: false,
validationError: null,
};
 
export default class ClusterStore {
Loading
Loading
@@ -57,6 +60,11 @@ export default class ClusterStore {
title: s__('ClusterIntegration|Cert-Manager'),
email: null,
},
crossplane: {
...applicationInitialState,
title: s__('ClusterIntegration|Crossplane'),
stack: null,
},
runner: {
...applicationInitialState,
title: s__('ClusterIntegration|GitLab Runner'),
Loading
Loading
@@ -85,6 +93,11 @@ export default class ClusterStore {
updateSuccessful: false,
updateFailed: false,
},
elastic_stack: {
...applicationInitialState,
title: s__('ClusterIntegration|Elastic Stack'),
kibana_hostname: null,
},
},
environments: [],
fetchingEnvironments: false,
Loading
Loading
@@ -197,13 +210,15 @@ export default class ClusterStore {
} else if (appId === CERT_MANAGER) {
this.state.applications.cert_manager.email =
this.state.applications.cert_manager.email || serverAppEntry.email;
} else if (appId === CROSSPLANE) {
this.state.applications.crossplane.stack =
this.state.applications.crossplane.stack || serverAppEntry.stack;
} else if (appId === JUPYTER) {
this.state.applications.jupyter.hostname =
this.state.applications.jupyter.hostname ||
serverAppEntry.hostname ||
(this.state.applications.ingress.externalIp
? `jupyter.${this.state.applications.ingress.externalIp}.nip.io`
: '');
this.state.applications.jupyter.hostname = this.updateHostnameIfUnset(
this.state.applications.jupyter.hostname,
serverAppEntry.hostname,
'jupyter',
);
} else if (appId === KNATIVE) {
if (!this.state.applications.knative.isEditingHostName) {
this.state.applications.knative.hostname =
Loading
Loading
@@ -216,10 +231,26 @@ export default class ClusterStore {
} else if (appId === RUNNER) {
this.state.applications.runner.version = version;
this.state.applications.runner.updateAvailable = updateAvailable;
} else if (appId === ELASTIC_STACK) {
this.state.applications.elastic_stack.kibana_hostname = this.updateHostnameIfUnset(
this.state.applications.elastic_stack.kibana_hostname,
serverAppEntry.kibana_hostname,
'kibana',
);
}
});
}
 
updateHostnameIfUnset(current, updated, fallback) {
return (
current ||
updated ||
(this.state.applications.ingress.externalIp
? `${fallback}.${this.state.applications.ingress.externalIp}.nip.io`
: '')
);
}
toggleFetchEnvironments(isFetching) {
this.state.fetchingEnvironments = isFetching;
}
Loading
Loading
/* eslint-disable func-names, no-var, no-else-return, consistent-return, one-var, no-return-assign, no-unused-expressions, no-sequences */
/* eslint-disable func-names, no-var, no-else-return, consistent-return, one-var, no-return-assign */
 
import $ from 'jquery';
 
Loading
Loading
@@ -9,40 +9,29 @@ const viewModes = ['two-up', 'swipe'];
export default class ImageFile {
constructor(file) {
this.file = file;
this.requestImageInfo(
$('.two-up.view .frame.deleted img', this.file),
(function(_this) {
return function() {
return _this.requestImageInfo($('.two-up.view .frame.added img', _this.file), () => {
_this.initViewModes();
// Load two-up view after images are loaded
// so that we can display the correct width and height information
const $images = $('.two-up.view img', _this.file);
$images.waitForImages(() => {
_this.initView('two-up');
});
});
};
})(this),
this.requestImageInfo($('.two-up.view .frame.deleted img', this.file), () =>
this.requestImageInfo($('.two-up.view .frame.added img', this.file), () => {
this.initViewModes();
// Load two-up view after images are loaded
// so that we can display the correct width and height information
const $images = $('.two-up.view img', this.file);
$images.waitForImages(() => {
this.initView('two-up');
});
}),
);
}
 
initViewModes() {
const viewMode = viewModes[0];
$('.view-modes', this.file).removeClass('hide');
$('.view-modes-menu', this.file).on(
'click',
'li',
(function(_this) {
return function(event) {
if (!$(event.currentTarget).hasClass('active')) {
return _this.activateViewMode(event.currentTarget.className);
}
};
})(this),
);
$('.view-modes-menu', this.file).on('click', 'li', event => {
if (!$(event.currentTarget).hasClass('active')) {
return this.activateViewMode(event.currentTarget.className);
}
});
return this.activateViewMode(viewMode);
}
 
Loading
Loading
@@ -51,15 +40,10 @@ export default class ImageFile {
.removeClass('active')
.filter(`.${viewMode}`)
.addClass('active');
return $(`.view:visible:not(.${viewMode})`, this.file).fadeOut(
200,
(function(_this) {
return function() {
$(`.view.${viewMode}`, _this.file).fadeIn(200);
return _this.initView(viewMode);
};
})(this),
);
return $(`.view:visible:not(.${viewMode})`, this.file).fadeOut(200, () => {
$(`.view.${viewMode}`, this.file).fadeIn(200);
return this.initView(viewMode);
});
}
 
initView(viewMode) {
Loading
Loading
@@ -103,22 +87,18 @@ export default class ImageFile {
.on('touchmove', dragMove);
}
 
prepareFrames(view) {
static prepareFrames(view) {
var maxHeight, maxWidth;
maxWidth = 0;
maxHeight = 0;
$('.frame', view)
.each(
(function() {
return function(index, frame) {
var height, width;
width = $(frame).width();
height = $(frame).height();
maxWidth = width > maxWidth ? width : maxWidth;
return (maxHeight = height > maxHeight ? height : maxHeight);
};
})(this),
)
.each((index, frame) => {
var height, width;
width = $(frame).width();
height = $(frame).height();
maxWidth = width > maxWidth ? width : maxWidth;
return (maxHeight = height > maxHeight ? height : maxHeight);
})
.css({
width: maxWidth,
height: maxHeight,
Loading
Loading
@@ -128,104 +108,95 @@ export default class ImageFile {
 
views = {
'two-up': function() {
return $('.two-up.view .wrap', this.file).each(
(function(_this) {
return function(index, wrap) {
$('img', wrap).each(function() {
var currentWidth;
currentWidth = $(this).width();
if (currentWidth > availWidth / 2) {
return $(this).width(availWidth / 2);
}
});
return _this.requestImageInfo($('img', wrap), (width, height) => {
$('.image-info .meta-width', wrap).text(`${width}px`);
$('.image-info .meta-height', wrap).text(`${height}px`);
return $('.image-info', wrap).removeClass('hide');
});
};
})(this),
);
return $('.two-up.view .wrap', this.file).each((index, wrap) => {
$('img', wrap).each(function() {
var currentWidth;
currentWidth = $(this).width();
if (currentWidth > availWidth / 2) {
return $(this).width(availWidth / 2);
}
});
return this.requestImageInfo($('img', wrap), (width, height) => {
$('.image-info .meta-width', wrap).text(`${width}px`);
$('.image-info .meta-height', wrap).text(`${height}px`);
return $('.image-info', wrap).removeClass('hide');
});
});
},
swipe() {
var maxHeight, maxWidth;
maxWidth = 0;
maxHeight = 0;
return $('.swipe.view', this.file).each(
(function(_this) {
return function(index, view) {
var $swipeWrap, $swipeBar, $swipeFrame, wrapPadding, ref;
(ref = _this.prepareFrames(view)), ([maxWidth, maxHeight] = ref);
$swipeFrame = $('.swipe-frame', view);
$swipeWrap = $('.swipe-wrap', view);
$swipeBar = $('.swipe-bar', view);
$swipeFrame.css({
width: maxWidth + 16,
height: maxHeight + 28,
});
$swipeWrap.css({
width: maxWidth + 1,
height: maxHeight + 2,
});
// Set swipeBar left position to match image frame
$swipeBar.css({
left: 1,
});
wrapPadding = parseInt($swipeWrap.css('right').replace('px', ''), 10);
_this.initDraggable($swipeBar, wrapPadding, (e, left) => {
if (left > 0 && left < $swipeFrame.width() - wrapPadding * 2) {
$swipeWrap.width(maxWidth + 1 - left);
$swipeBar.css('left', left);
}
});
};
})(this),
);
return $('.swipe.view', this.file).each((index, view) => {
var $swipeWrap, $swipeBar, $swipeFrame, wrapPadding;
const ref = ImageFile.prepareFrames(view);
[maxWidth, maxHeight] = ref;
$swipeFrame = $('.swipe-frame', view);
$swipeWrap = $('.swipe-wrap', view);
$swipeBar = $('.swipe-bar', view);
$swipeFrame.css({
width: maxWidth + 16,
height: maxHeight + 28,
});
$swipeWrap.css({
width: maxWidth + 1,
height: maxHeight + 2,
});
// Set swipeBar left position to match image frame
$swipeBar.css({
left: 1,
});
wrapPadding = parseInt($swipeWrap.css('right').replace('px', ''), 10);
this.initDraggable($swipeBar, wrapPadding, (e, left) => {
if (left > 0 && left < $swipeFrame.width() - wrapPadding * 2) {
$swipeWrap.width(maxWidth + 1 - left);
$swipeBar.css('left', left);
}
});
});
},
'onion-skin': function() {
var dragTrackWidth, maxHeight, maxWidth;
maxWidth = 0;
maxHeight = 0;
dragTrackWidth = $('.drag-track', this.file).width() - $('.dragger', this.file).width();
return $('.onion-skin.view', this.file).each(
(function(_this) {
return function(index, view) {
var $frame, $track, $dragger, $frameAdded, framePadding, ref;
(ref = _this.prepareFrames(view)), ([maxWidth, maxHeight] = ref);
$frame = $('.onion-skin-frame', view);
$frameAdded = $('.frame.added', view);
$track = $('.drag-track', view);
$dragger = $('.dragger', $track);
$frame.css({
width: maxWidth + 16,
height: maxHeight + 28,
});
$('.swipe-wrap', view).css({
width: maxWidth + 1,
height: maxHeight + 2,
});
$dragger.css({
left: dragTrackWidth,
});
$frameAdded.css('opacity', 1);
framePadding = parseInt($frameAdded.css('right').replace('px', ''), 10);
_this.initDraggable($dragger, framePadding, (e, left) => {
var opacity = left / dragTrackWidth;
if (opacity >= 0 && opacity <= 1) {
$dragger.css('left', left);
$frameAdded.css('opacity', opacity);
}
});
};
})(this),
);
return $('.onion-skin.view', this.file).each((index, view) => {
var $frame, $track, $dragger, $frameAdded, framePadding;
const ref = ImageFile.prepareFrames(view);
[maxWidth, maxHeight] = ref;
$frame = $('.onion-skin-frame', view);
$frameAdded = $('.frame.added', view);
$track = $('.drag-track', view);
$dragger = $('.dragger', $track);
$frame.css({
width: maxWidth + 16,
height: maxHeight + 28,
});
$('.swipe-wrap', view).css({
width: maxWidth + 1,
height: maxHeight + 2,
});
$dragger.css({
left: dragTrackWidth,
});
$frameAdded.css('opacity', 1);
framePadding = parseInt($frameAdded.css('right').replace('px', ''), 10);
this.initDraggable($dragger, framePadding, (e, left) => {
var opacity = left / dragTrackWidth;
if (opacity >= 0 && opacity <= 1) {
$dragger.css('left', left);
$frameAdded.css('opacity', opacity);
}
});
});
},
};
 
Loading
Loading
@@ -235,14 +206,7 @@ export default class ImageFile {
if (domImg.complete) {
return callback.call(this, domImg.naturalWidth, domImg.naturalHeight);
} else {
return img.on(
'load',
(function(_this) {
return function() {
return callback.call(_this, domImg.naturalWidth, domImg.naturalHeight);
};
})(this),
);
return img.on('load', () => callback.call(this, domImg.naturalWidth, domImg.naturalHeight));
}
}
}
Loading
Loading
/* eslint-disable func-names, one-var, no-var, no-else-return */
/* eslint-disable func-names, no-else-return */
 
import $ from 'jquery';
import { __ } from './locale';
Loading
Loading
@@ -8,9 +8,8 @@ import { capitalizeFirstCharacter } from './lib/utils/text_utility';
 
export default function initCompareAutocomplete(limitTo = null, clickHandler = () => {}) {
$('.js-compare-dropdown').each(function() {
var $dropdown, selected;
$dropdown = $(this);
selected = $dropdown.data('selected');
const $dropdown = $(this);
const selected = $dropdown.data('selected');
const $dropdownContainer = $dropdown.closest('.dropdown');
const $fieldInput = $(`input[name="${$dropdown.data('fieldName')}"]`, $dropdownContainer);
const $filterInput = $('input[type="search"]', $dropdownContainer);
Loading
Loading
@@ -44,17 +43,16 @@ export default function initCompareAutocomplete(limitTo = null, clickHandler = (
fieldName: $dropdown.data('fieldName'),
filterInput: 'input[type="search"]',
renderRow(ref) {
var link;
const link = $('<a />')
.attr('href', '#')
.addClass(ref === selected ? 'is-active' : '')
.text(ref)
.attr('data-ref', ref);
if (ref.header != null) {
return $('<li />')
.addClass('dropdown-header')
.text(ref.header);
} else {
link = $('<a />')
.attr('href', '#')
.addClass(ref === selected ? 'is-active' : '')
.text(ref)
.attr('data-ref', ref);
return $('<li />').append(link);
}
},
Loading
Loading
Loading
Loading
@@ -41,7 +41,7 @@ export default {
noForkText() {
return sprintf(
__(
"To protect this issue's confidentiality, %{link_start}fork the project%{link_end} and set the forks visiblity to private.",
"To protect this issue's confidentiality, %{link_start}fork the project%{link_end} and set the forks visibility to private.",
),
{ link_start: `<a href="${this.newForkPath}" class="help-link">`, link_end: '</a>' },
false,
Loading
Loading
<script>
import { __ } from '~/locale';
import _ from 'underscore';
import { mapActions, mapState, mapGetters } from 'vuex';
import { GlLoadingIcon } from '@gitlab/ui';
import { GlAreaChart } from '@gitlab/ui/dist/charts';
import { getSvgIconPathContent } from '~/lib/utils/icon_utils';
import { getDatesInRange } from '~/lib/utils/datetime_utility';
import { xAxisLabelFormatter, dateFormatter } from '../utils';
export default {
components: {
GlAreaChart,
GlLoadingIcon,
},
props: {
endpoint: {
type: String,
required: true,
},
branch: {
type: String,
required: true,
},
},
data() {
return {
masterChart: null,
individualCharts: [],
svgs: {},
masterChartHeight: 264,
individualChartHeight: 216,
};
},
computed: {
...mapState(['chartData', 'loading']),
...mapGetters(['showChart', 'parsedData']),
masterChartData() {
const data = {};
this.xAxisRange.forEach(date => {
data[date] = this.parsedData.total[date] || 0;
});
return [
{
name: __('Commits'),
data: Object.entries(data),
},
];
},
masterChartOptions() {
return {
...this.getCommonChartOptions(true),
yAxis: {
name: __('Number of commits'),
},
grid: {
bottom: 64,
left: 64,
right: 20,
top: 20,
},
};
},
individualChartsData() {
const maxNumberOfIndividualContributorsCharts = 100;
return Object.keys(this.parsedData.byAuthor)
.map(name => {
const author = this.parsedData.byAuthor[name];
return {
name,
email: author.email,
commits: author.commits,
dates: [
{
name: __('Commits'),
data: this.xAxisRange.map(date => [date, author.dates[date] || 0]),
},
],
};
})
.sort((a, b) => b.commits - a.commits)
.slice(0, maxNumberOfIndividualContributorsCharts);
},
individualChartOptions() {
return {
...this.getCommonChartOptions(false),
yAxis: {
name: __('Commits'),
max: this.individualChartYAxisMax,
},
grid: {
bottom: 27,
left: 64,
right: 20,
top: 8,
},
};
},
individualChartYAxisMax() {
return this.individualChartsData.reduce((acc, item) => {
const values = item.dates[0].data.map(value => value[1]);
return Math.max(acc, ...values);
}, 0);
},
xAxisRange() {
const dates = Object.keys(this.parsedData.total).sort((a, b) => new Date(a) - new Date(b));
const firstContributionDate = new Date(dates[0]);
const lastContributionDate = new Date(dates[dates.length - 1]);
return getDatesInRange(firstContributionDate, lastContributionDate, dateFormatter);
},
firstContributionDate() {
return this.xAxisRange[0];
},
lastContributionDate() {
return this.xAxisRange[this.xAxisRange.length - 1];
},
charts() {
return _.uniq(this.individualCharts);
},
},
mounted() {
this.fetchChartData(this.endpoint);
},
methods: {
...mapActions(['fetchChartData']),
getCommonChartOptions(isMasterChart) {
return {
xAxis: {
type: 'time',
name: '',
data: this.xAxisRange,
axisLabel: {
formatter: xAxisLabelFormatter,
showMaxLabel: false,
showMinLabel: false,
},
boundaryGap: false,
splitNumber: isMasterChart ? 24 : 18,
// 28 days
minInterval: 28 * 86400 * 1000,
min: this.firstContributionDate,
max: this.lastContributionDate,
},
};
},
setSvg(name) {
return getSvgIconPathContent(name)
.then(path => {
if (path) {
this.$set(this.svgs, name, `path://${path}`);
}
})
.catch(() => {});
},
onMasterChartCreated(chart) {
this.masterChart = chart;
this.setSvg('scroll-handle')
.then(() => {
this.masterChart.setOption({
dataZoom: [
{
type: 'slider',
handleIcon: this.svgs['scroll-handle'],
},
],
});
})
.catch(() => {});
this.masterChart.on('datazoom', _.debounce(this.setIndividualChartsZoom, 200));
},
onIndividualChartCreated(chart) {
this.individualCharts.push(chart);
},
setIndividualChartsZoom(options) {
this.charts.forEach(chart =>
chart.setOption(
{
dataZoom: {
start: options.start,
end: options.end,
show: false,
},
},
{ lazyUpdate: true },
),
);
},
},
};
</script>
<template>
<div>
<div v-if="loading" class="contributors-loader text-center">
<gl-loading-icon :inline="true" :size="4" />
</div>
<div v-else-if="showChart" class="contributors-charts">
<h4>{{ __('Commits to') }} {{ branch }}</h4>
<span>{{ __('Excluding merge commits. Limited to 6,000 commits.') }}</span>
<div>
<gl-area-chart
:data="masterChartData"
:option="masterChartOptions"
:height="masterChartHeight"
@created="onMasterChartCreated"
/>
</div>
<div class="row">
<div v-for="contributor in individualChartsData" :key="contributor.name" class="col-6">
<h4>{{ contributor.name }}</h4>
<p>{{ n__('%d commit', '%d commits', contributor.commits) }} ({{ contributor.email }})</p>
<gl-area-chart
:data="contributor.dates"
:option="individualChartOptions"
:height="individualChartHeight"
@created="onIndividualChartCreated"
/>
</div>
</div>
</div>
</div>
</template>
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment