summaryrefslogtreecommitdiff
path: root/app/assets
diff options
context:
space:
mode:
Diffstat (limited to 'app/assets')
-rw-r--r--app/assets/images/cluster_app_logos/crossplane.pngbin0 -> 1850 bytes
-rw-r--r--app/assets/javascripts/clusters/clusters_bundle.js47
-rw-r--r--app/assets/javascripts/clusters/components/applications.vue113
-rw-r--r--app/assets/javascripts/clusters/components/crossplane_provider_stack.vue93
-rw-r--r--app/assets/javascripts/clusters/constants.js1
-rw-r--r--app/assets/javascripts/clusters/services/clusters_service.js1
-rw-r--r--app/assets/javascripts/clusters/stores/clusters_store.js10
7 files changed, 230 insertions, 35 deletions
diff --git a/app/assets/images/cluster_app_logos/crossplane.png b/app/assets/images/cluster_app_logos/crossplane.png
new file mode 100644
index 00000000000..32d8175108c
--- /dev/null
+++ b/app/assets/images/cluster_app_logos/crossplane.png
Binary files differ
diff --git a/app/assets/javascripts/clusters/clusters_bundle.js b/app/assets/javascripts/clusters/clusters_bundle.js
index d471dcb1b06..75909dd9d20 100644
--- a/app/assets/javascripts/clusters/clusters_bundle.js
+++ b/app/assets/javascripts/clusters/clusters_bundle.js
@@ -8,7 +8,7 @@ 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';
@@ -39,6 +39,7 @@ export default class Clusters {
installKnativePath,
updateKnativePath,
installElasticStackPath,
+ installCrossplanePath,
installPrometheusPath,
managePrometheusPath,
clusterEnvironmentsPath,
@@ -83,6 +84,7 @@ export default class Clusters {
installHelmEndpoint: installHelmPath,
installIngressEndpoint: installIngressPath,
installCertManagerEndpoint: installCertManagerPath,
+ installCrossplaneEndpoint: installCrossplanePath,
installRunnerEndpoint: installRunnerPath,
installPrometheusEndpoint: installPrometheusPath,
installJupyterEndpoint: installJupyterPath,
@@ -227,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');
@@ -238,6 +241,7 @@ export default class Clusters {
eventHub.$off('updateApplication', this.updateApplication);
eventHub.$off('saveKnativeDomain');
eventHub.$off('setKnativeHostname');
+ eventHub.$off('setCrossplaneProviderStack');
eventHub.$off('uninstallApplication');
}
@@ -404,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();
});
}
@@ -463,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;
diff --git a/app/assets/javascripts/clusters/components/applications.vue b/app/assets/javascripts/clusters/components/applications.vue
index 44d77277cc5..a951a6bfeea 100644
--- a/app/assets/javascripts/clusters/components/applications.vue
+++ b/app/assets/javascripts/clusters/components/applications.vue
@@ -9,6 +9,7 @@ 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';
@@ -20,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: {
@@ -28,6 +30,7 @@ export default {
LoadingButton,
GlLoadingIcon,
KnativeDomainEditor,
+ CrossplaneProviderStack,
},
props: {
type: {
@@ -89,6 +92,7 @@ export default {
jupyterhubLogo,
kubernetesLogo,
certManagerLogo,
+ crossplaneLogo,
knativeLogo,
meltanoLogo,
prometheusLogo,
@@ -116,6 +120,12 @@ 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;
},
@@ -151,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(
@@ -182,6 +210,9 @@ export default {
knative() {
return this.applications.knative;
},
+ crossplane() {
+ return this.applications.crossplane;
+ },
cloudRun() {
return this.providerType === PROVIDER_TYPE.GCP && this.preInstalledKnative;
},
@@ -218,6 +249,12 @@ export default {
hostname,
});
},
+ setCrossplaneProviderStack(stack) {
+ eventHub.$emit('setCrossplaneProviderStack', {
+ id: 'crossplane',
+ stack,
+ });
+ },
},
};
</script>
@@ -228,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>
@@ -253,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>
@@ -263,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
@@ -286,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>
@@ -319,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') }}
@@ -331,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') }}
@@ -379,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"
@@ -435,13 +472,41 @@ 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"
:title="applications.jupyter.title"
@@ -462,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>
@@ -492,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') }}
@@ -538,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>
@@ -612,7 +677,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') }}
diff --git a/app/assets/javascripts/clusters/components/crossplane_provider_stack.vue b/app/assets/javascripts/clusters/components/crossplane_provider_stack.vue
new file mode 100644
index 00000000000..966918ae636
--- /dev/null
+++ b/app/assets/javascripts/clusters/components/crossplane_provider_stack.vue
@@ -0,0 +1,93 @@
+<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>
diff --git a/app/assets/javascripts/clusters/constants.js b/app/assets/javascripts/clusters/constants.js
index d7152e32376..9f98f170fb0 100644
--- a/app/assets/javascripts/clusters/constants.js
+++ b/app/assets/javascripts/clusters/constants.js
@@ -50,6 +50,7 @@ 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';
diff --git a/app/assets/javascripts/clusters/services/clusters_service.js b/app/assets/javascripts/clusters/services/clusters_service.js
index dab4da04bf3..333fb293a15 100644
--- a/app/assets/javascripts/clusters/services/clusters_service.js
+++ b/app/assets/javascripts/clusters/services/clusters_service.js
@@ -7,6 +7,7 @@ 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,
diff --git a/app/assets/javascripts/clusters/stores/clusters_store.js b/app/assets/javascripts/clusters/stores/clusters_store.js
index 6304e81c296..35dbf951551 100644
--- a/app/assets/javascripts/clusters/stores/clusters_store.js
+++ b/app/assets/javascripts/clusters/stores/clusters_store.js
@@ -6,6 +6,7 @@ import {
KNATIVE,
CERT_MANAGER,
ELASTIC_STACK,
+ CROSSPLANE,
RUNNER,
APPLICATION_INSTALLED_STATUSES,
APPLICATION_STATUS,
@@ -26,6 +27,7 @@ const applicationInitialState = {
uninstallable: false,
uninstallFailed: false,
uninstallSuccessful: false,
+ validationError: null,
};
export default class ClusterStore {
@@ -58,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'),
@@ -203,6 +210,9 @@ 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.updateHostnameIfUnset(
this.state.applications.jupyter.hostname,