Commit 354f0bcc authored by Clement Ho's avatar Clement Ho

Merge branch '50111-improve-design-of-cluster-apps-to-handle-larger-quantity' into 'master'

Improve cluster apps installation flow

Closes #50111

See merge request gitlab-org/gitlab-ce!21567
parents 187b8e74 583cc364
...@@ -2,6 +2,7 @@ ...@@ -2,6 +2,7 @@
/* eslint-disable vue/require-default-prop */ /* eslint-disable vue/require-default-prop */
import { s__, sprintf } from '../../locale'; import { s__, sprintf } from '../../locale';
import eventHub from '../event_hub'; import eventHub from '../event_hub';
import identicon from '../../vue_shared/components/identicon.vue';
import loadingButton from '../../vue_shared/components/loading_button.vue'; import loadingButton from '../../vue_shared/components/loading_button.vue';
import { import {
APPLICATION_STATUS, APPLICATION_STATUS,
...@@ -13,6 +14,7 @@ ...@@ -13,6 +14,7 @@
export default { export default {
components: { components: {
loadingButton, loadingButton,
identicon,
}, },
props: { props: {
id: { id: {
...@@ -31,6 +33,16 @@ ...@@ -31,6 +33,16 @@
type: String, type: String,
required: false, required: false,
}, },
logoUrl: {
type: String,
required: false,
default: null,
},
disabled: {
type: Boolean,
required: false,
default: false,
},
status: { status: {
type: String, type: String,
required: false, required: false,
...@@ -60,6 +72,18 @@ ...@@ -60,6 +72,18 @@
isKnownStatus() { isKnownStatus() {
return Object.values(APPLICATION_STATUS).includes(this.status); return Object.values(APPLICATION_STATUS).includes(this.status);
}, },
isInstalled() {
return (
this.status === APPLICATION_STATUS.INSTALLED || this.status === APPLICATION_STATUS.UPDATED
);
},
hasLogo() {
return !!this.logoUrl;
},
identiconId() {
// generate a deterministic integer id for the identicon background
return this.id.charCodeAt(0);
},
rowJsClass() { rowJsClass() {
return `js-cluster-application-row-${this.id}`; return `js-cluster-application-row-${this.id}`;
}, },
...@@ -128,37 +152,81 @@ ...@@ -128,37 +152,81 @@
<template> <template>
<div <div
:class="rowJsClass" :class="[
class="gl-responsive-table-row gl-responsive-table-row-col-span" rowJsClass,
isInstalled && 'cluster-application-installed',
disabled && 'cluster-application-disabled'
]"
class="cluster-application-row gl-responsive-table-row gl-responsive-table-row-col-span"
> >
<div <div
class="gl-responsive-table-row-layout" class="gl-responsive-table-row-layout"
role="row" role="row"
> >
<div
class="table-section append-right-8 section-align-top"
role="gridcell"
>
<img
v-if="hasLogo"
:src="logoUrl"
:alt="`${title} logo`"
class="cluster-application-logo avatar s40"
/>
<identicon
v-else
:entity-id="identiconId"
:entity-name="title"
size-class="s40"
/>
</div>
<div
class="table-section cluster-application-description section-wrap"
role="gridcell"
>
<strong>
<a <a
v-if="titleLink" v-if="titleLink"
:href="titleLink" :href="titleLink"
target="blank" target="blank"
rel="noopener noreferrer" rel="noopener noreferrer"
role="gridcell" class="js-cluster-application-title"
class="table-section section-15 section-align-top js-cluster-application-title"
> >
{{ title }} {{ title }}
</a> </a>
<span <span
v-else v-else
class="table-section section-15 section-align-top js-cluster-application-title" class="js-cluster-application-title"
> >
{{ title }} {{ title }}
</span> </span>
</strong>
<slot name="description"></slot>
<div <div
class="table-section section-wrap" v-if="hasError || isUnknownStatus"
role="gridcell" class="cluster-application-error text-danger prepend-top-10"
> >
<slot name="description"></slot> <p class="js-cluster-application-general-error-message append-bottom-0">
{{ generalErrorDescription }}
</p>
<ul v-if="statusReason || requestReason">
<li
v-if="statusReason"
class="js-cluster-application-status-error-message"
>
{{ statusReason }}
</li>
<li
v-if="requestReason"
class="js-cluster-application-request-error-message"
>
{{ requestReason }}
</li>
</ul>
</div>
</div> </div>
<div <div
:class="{ 'section-20': showManageButton, 'section-15': !showManageButton }" :class="{ 'section-25': showManageButton, 'section-15': !showManageButton }"
class="table-section table-button-footer section-align-top" class="table-section table-button-footer section-align-top"
role="gridcell" role="gridcell"
> >
...@@ -168,6 +236,7 @@ ...@@ -168,6 +236,7 @@
> >
<a <a
:href="manageLink" :href="manageLink"
:class="{ disabled: disabled }"
class="btn" class="btn"
> >
{{ manageButtonLabel }} {{ manageButtonLabel }}
...@@ -176,7 +245,7 @@ ...@@ -176,7 +245,7 @@
<div class="btn-group table-action-buttons"> <div class="btn-group table-action-buttons">
<loading-button <loading-button
:loading="installButtonLoading" :loading="installButtonLoading"
:disabled="installButtonDisabled" :disabled="disabled || installButtonDisabled"
:label="installButtonLabel" :label="installButtonLabel"
class="js-cluster-application-install-button" class="js-cluster-application-install-button"
@click="installClicked" @click="installClicked"
...@@ -184,35 +253,5 @@ ...@@ -184,35 +253,5 @@
</div> </div>
</div> </div>
</div> </div>
<div
v-if="hasError || isUnknownStatus"
class="gl-responsive-table-row-layout"
role="row"
>
<div
class="alert alert-danger alert-block append-bottom-0 clusters-error-alert"
role="gridcell"
>
<div>
<p class="js-cluster-application-general-error-message">
{{ generalErrorDescription }}
</p>
<ul v-if="statusReason || requestReason">
<li
v-if="statusReason"
class="js-cluster-application-status-error-message"
>
{{ statusReason }}
</li>
<li
v-if="requestReason"
class="js-cluster-application-request-error-message"
>
{{ requestReason }}
</li>
</ul>
</div>
</div>
</div>
</div> </div>
</template> </template>
<script> <script>
import _ from 'underscore'; import _ from 'underscore';
import helmInstallIllustration from '@gitlab-org/gitlab-svgs/illustrations/kubernetes-installation.svg';
import elasticsearchLogo from 'images/cluster_app_logos/elasticsearch.png';
import gitlabLogo from 'images/cluster_app_logos/gitlab.png';
import helmLogo from 'images/cluster_app_logos/helm.png';
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 meltanoLogo from 'images/cluster_app_logos/meltano.png';
import prometheusLogo from 'images/cluster_app_logos/prometheus.png';
import { s__, sprintf } from '../../locale'; import { s__, sprintf } from '../../locale';
import applicationRow from './application_row.vue'; import applicationRow from './application_row.vue';
import clipboardButton from '../../vue_shared/components/clipboard_button.vue'; import clipboardButton from '../../vue_shared/components/clipboard_button.vue';
...@@ -37,21 +46,21 @@ export default { ...@@ -37,21 +46,21 @@ export default {
default: '', default: '',
}, },
}, },
data: () => ({
elasticsearchLogo,
gitlabLogo,
helmLogo,
jeagerLogo,
jupyterhubLogo,
kubernetesLogo,
meltanoLogo,
prometheusLogo,
}),
computed: { computed: {
generalApplicationDescription() { helmInstalled() {
return sprintf( return (
_.escape( this.applications.helm.status === APPLICATION_STATUS.INSTALLED ||
s__( this.applications.helm.status === APPLICATION_STATUS.UPDATED
`ClusterIntegration|Install applications on your Kubernetes cluster.
Read more about %{helpLink}`,
),
),
{
helpLink: `<a href="${this.helpPath}">
${_.escape(s__('ClusterIntegration|installing applications'))}
</a>`,
},
false,
); );
}, },
ingressId() { ingressId() {
...@@ -128,34 +137,35 @@ export default { ...@@ -128,34 +137,35 @@ export default {
return this.applications.jupyter.hostname; return this.applications.jupyter.hostname;
}, },
}, },
created() {
this.helmInstallIllustration = helmInstallIllustration;
},
}; };
</script> </script>
<template> <template>
<section <section id="cluster-applications">
id="cluster-applications"
class="settings no-animate expanded"
>
<div class="settings-header">
<h4> <h4>
{{ s__('ClusterIntegration|Applications') }} {{ s__('ClusterIntegration|Applications') }}
</h4> </h4>
<p <p class="append-bottom-0">
class="append-bottom-0" {{ s__(`ClusterIntegration|Choose which applications to install on your Kubernetes cluster.
v-html="generalApplicationDescription" Helm Tiller is required to install any of the following applications.`) }}
> <a :href="helpPath">
{{ __('More information') }}
</a>
</p> </p>
</div>
<div class="settings-content"> <div class="cluster-application-list prepend-top-10">
<div class="append-bottom-20">
<application-row <application-row
id="helm" id="helm"
:logo-url="helmLogo"
:title="applications.helm.title" :title="applications.helm.title"
:status="applications.helm.status" :status="applications.helm.status"
:status-reason="applications.helm.statusReason" :status-reason="applications.helm.statusReason"
:request-status="applications.helm.requestStatus" :request-status="applications.helm.requestStatus"
:request-reason="applications.helm.requestReason" :request-reason="applications.helm.requestReason"
class="rounded-top"
title-link="https://docs.helm.sh/" title-link="https://docs.helm.sh/"
> >
<div slot="description"> <div slot="description">
...@@ -165,13 +175,27 @@ export default { ...@@ -165,13 +175,27 @@ export default {
and manages releases of your charts.`) }} and manages releases of your charts.`) }}
</div> </div>
</application-row> </application-row>
<div
v-show="!helmInstalled"
class="cluster-application-warning"
>
<div
class="svg-container"
v-html="helmInstallIllustration"
>
</div>
{{ s__(`ClusterIntegration|You must first install Helm Tiller before
installing the applications below`) }}
</div>
<application-row <application-row
:id="ingressId" :id="ingressId"
:logo-url="kubernetesLogo"
:title="applications.ingress.title" :title="applications.ingress.title"
:status="applications.ingress.status" :status="applications.ingress.status"
:status-reason="applications.ingress.statusReason" :status-reason="applications.ingress.statusReason"
:request-status="applications.ingress.requestStatus" :request-status="applications.ingress.requestStatus"
:request-reason="applications.ingress.requestReason" :request-reason="applications.ingress.requestReason"
:disabled="!helmInstalled"
title-link="https://kubernetes.io/docs/concepts/services-networking/ingress/" title-link="https://kubernetes.io/docs/concepts/services-networking/ingress/"
> >
<div slot="description"> <div slot="description">
...@@ -246,7 +270,6 @@ export default { ...@@ -246,7 +270,6 @@ export default {
</template> </template>
<div <div
v-else
v-html="ingressDescription" v-html="ingressDescription"
> >
</div> </div>
...@@ -254,12 +277,14 @@ export default { ...@@ -254,12 +277,14 @@ export default {
</application-row> </application-row>
<application-row <application-row
id="prometheus" id="prometheus"
:logo-url="prometheusLogo"
:title="applications.prometheus.title" :title="applications.prometheus.title"
:manage-link="managePrometheusPath" :manage-link="managePrometheusPath"
:status="applications.prometheus.status" :status="applications.prometheus.status"
:status-reason="applications.prometheus.statusReason" :status-reason="applications.prometheus.statusReason"
:request-status="applications.prometheus.requestStatus" :request-status="applications.prometheus.requestStatus"
:request-reason="applications.prometheus.requestReason" :request-reason="applications.prometheus.requestReason"
:disabled="!helmInstalled"
title-link="https://prometheus.io/docs/introduction/overview/" title-link="https://prometheus.io/docs/introduction/overview/"
> >
<div <div
...@@ -270,11 +295,13 @@ export default { ...@@ -270,11 +295,13 @@ export default {
</application-row> </application-row>
<application-row <application-row
id="runner" id="runner"
:logo-url="gitlabLogo"
:title="applications.runner.title" :title="applications.runner.title"
:status="applications.runner.status" :status="applications.runner.status"
:status-reason="applications.runner.statusReason" :status-reason="applications.runner.statusReason"
:request-status="applications.runner.requestStatus" :request-status="applications.runner.requestStatus"
:request-reason="applications.runner.requestReason" :request-reason="applications.runner.requestReason"
:disabled="!helmInstalled"
title-link="https://docs.gitlab.com/runner/" title-link="https://docs.gitlab.com/runner/"
> >
<div slot="description"> <div slot="description">
...@@ -286,12 +313,15 @@ export default { ...@@ -286,12 +313,15 @@ export default {
</application-row> </application-row>
<application-row <application-row
id="jupyter" id="jupyter"
:logo-url="jupyterhubLogo"
:title="applications.jupyter.title" :title="applications.jupyter.title"
:status="applications.jupyter.status" :status="applications.jupyter.status"
:status-reason="applications.jupyter.statusReason" :status-reason="applications.jupyter.statusReason"
:request-status="applications.jupyter.requestStatus" :request-status="applications.jupyter.requestStatus"
:request-reason="applications.jupyter.requestReason" :request-reason="applications.jupyter.requestReason"
:install-application-request-params="{ hostname: applications.jupyter.hostname }" :install-application-request-params="{ hostname: applications.jupyter.hostname }"
:disabled="!helmInstalled"
class="hide-bottom-border rounded-bottom"
title-link="https://jupyterhub.readthedocs.io/en/stable/" title-link="https://jupyterhub.readthedocs.io/en/stable/"
> >
<div slot="description"> <div slot="description">
...@@ -341,11 +371,6 @@ export default { ...@@ -341,11 +371,6 @@ export default {
</template> </template>
</div> </div>
</application-row> </application-row>
<!--
NOTE: Don't forget to update `clusters.scss`
min-height for this block and uncomment `application_spec` tests
-->
</div>
</div> </div>
</section> </section>
</template> </template>
...@@ -4,9 +4,60 @@ ...@@ -4,9 +4,60 @@
} }
} }
.cluster-applications-table { .cluster-application-row {
// Wait for the Vue to kick-in and render the applications block background: $gray-lighter;
min-height: 628px;
&.cluster-application-installed {
background: none;
}
.settings-message {
padding: $gl-vert-padding $gl-padding-8;
}
}
@media (min-width: map-get($grid-breakpoints, md)) {
.cluster-application-list {
border: 1px solid $border-color;
border-radius: $border-radius-default;
}
.cluster-application-row {
border-bottom: 1px solid $border-color;
padding: $gl-padding;
}
}
.cluster-application-logo {
border: 3px solid $white-light;
box-shadow: 0 0 0 1px $gray-normal;
&.avatar:hover {
border-color: $white-light;
}
}
.cluster-application-warning {
font-weight: bold;
text-align: center;
padding: $gl-padding;
border-bottom: 1px solid $white-normal;
.svg-container {
display: inline-block;
vertical-align: middle;
margin-right: $gl-padding-8;
width: 40px;
height: 40px;
}
}
.cluster-application-description {
flex: 1;
}
.cluster-application-disabled {
opacity: 0.5;
} }
.clusters-dropdown-menu { .clusters-dropdown-menu {
......
---
title: Improve install flow of Kubernetes cluster apps
merge_request: 21567
author:
type: changed
...@@ -1353,6 +1353,9 @@ msgstr "" ...@@ -1353,6 +1353,9 @@ msgstr ""
msgid "ClusterIntegration|Certificate Authority bundle (PEM format)" msgid "ClusterIntegration|Certificate Authority bundle (PEM format)"
msgstr "" msgstr ""
msgid "ClusterIntegration|Choose which applications to install on your Kubernetes cluster. Helm Tiller is required to install any of the following applications."
msgstr ""
msgid "ClusterIntegration|Choose which of your environments will use this cluster." msgid "ClusterIntegration|Choose which of your environments will use this cluster."
msgstr "" msgstr ""
...@@ -1443,9 +1446,6 @@ msgstr "" ...@@ -1443,9 +1446,6 @@ msgstr ""
msgid "ClusterIntegration|Install" msgid "ClusterIntegration|Install"
msgstr "" msgstr ""
msgid "ClusterIntegration|Install applications on your Kubernetes cluster. Read more about %{helpLink}"
msgstr ""
msgid "ClusterIntegration|Installed" msgid "ClusterIntegration|Installed"
msgstr "" msgstr ""
...@@ -1653,6 +1653,9 @@ msgstr "" ...@@ -1653,6 +1653,9 @@ msgstr ""
msgid "ClusterIntegration|With a Kubernetes 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 Kubernetes 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 ""
msgid "ClusterIntegration|You must first install Helm Tiller before installing the applications below"
msgstr ""
msgid "ClusterIntegration|Your account must have %{link_to_kubernetes_engine}" msgid "ClusterIntegration|Your account must have %{link_to_kubernetes_engine}"
msgstr "" msgstr ""
...@@ -1671,9 +1674,6 @@ msgstr "" ...@@ -1671,9 +1674,6 @@ 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 ""
......
...@@ -86,7 +86,6 @@ describe 'User Cluster', :js do ...@@ -86,7 +86,6 @@ describe 'User Cluster', :js do
context 'when user disables the cluster' do context 'when user disables the cluster' do
before do before do
page.find(:css, '.js-cluster-enable-toggle-area .js-project-feature-toggle').click page.find(:css, '.js-cluster-enable-toggle-area .js-project-feature-toggle').click
fill_in 'cluster_name', with: 'dev-cluster'
page.within('#cluster-integration') { click_button 'Save changes' } page.within('#cluster-integration') { click_button 'Save changes' }
end end
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment