Commit 769d3e73 authored by GitLab Bot's avatar GitLab Bot
Browse files

Add latest changes from gitlab-org/gitlab@master

parent be0f1edbf833
......@@ -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;
......
......@@ -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,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"
......@@ -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') }}
......
<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>
......@@ -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';
......
......@@ -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,
......
......@@ -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,
......
......@@ -47,7 +47,7 @@ def cluster
end
def cluster_application_params
params.permit(:application, :hostname, :kibana_hostname, :email)
params.permit(:application, :hostname, :kibana_hostname, :email, :stack)
end
def cluster_application_destroy_params
......
......@@ -17,6 +17,7 @@ class Clusters::ClustersController < Clusters::BaseController
end
before_action only: [:show] do
push_frontend_feature_flag(:enable_cluster_application_elastic_stack)
push_frontend_feature_flag(:enable_cluster_application_crossplane)
end
helper_method :token_in_session
......
# frozen_string_literal: true
module Clusters
module Applications
class Crossplane < ApplicationRecord
VERSION = '0.4.1'
self.table_name = 'clusters_applications_crossplane'
include ::Clusters::Concerns::ApplicationCore
include ::Clusters::Concerns::ApplicationStatus
include ::Clusters::Concerns::ApplicationVersion
include ::Clusters::Concerns::ApplicationData
default_value_for :version, VERSION
default_value_for :stack do |crossplane|
''
end
validates :stack, presence: true
def chart
'crossplane/crossplane'
end
def repository
'https://charts.crossplane.io/alpha'
end
def install_command
Gitlab::Kubernetes::Helm::InstallCommand.new(
name: 'crossplane',
repository: repository,
version: VERSION,
rbac: cluster.platform_kubernetes_rbac?,
chart: chart,
files: files
)
end
def values
crossplane_values.to_yaml
end
private
def crossplane_values
{
"clusterStacks" => {
self.stack => {
"deploy" => true,
"version" => "alpha"
}
}
}
end
end
end
end
......@@ -14,6 +14,7 @@ class Cluster < ApplicationRecord
Applications::Helm.application_name => Applications::Helm,
Applications::Ingress.application_name => Applications::Ingress,
Applications::CertManager.application_name => Applications::CertManager,
Applications::Crossplane.application_name => Applications::Crossplane,
Applications::Prometheus.application_name => Applications::Prometheus,
Applications::Runner.application_name => Applications::Runner,
Applications::Jupyter.application_name => Applications::Jupyter,
......@@ -47,6 +48,7 @@ def self.has_one_cluster_application(name) # rubocop:disable Naming/PredicateNam
has_one_cluster_application :helm
has_one_cluster_application :ingress
has_one_cluster_application :cert_manager
has_one_cluster_application :crossplane
has_one_cluster_application :prometheus
has_one_cluster_application :runner
has_one_cluster_application :jupyter
......
......@@ -10,6 +10,7 @@ class ClusterApplicationEntity < Grape::Entity
expose :hostname, if: -> (e, _) { e.respond_to?(:hostname) }
expose :kibana_hostname, if: -> (e, _) { e.respond_to?(:kibana_hostname) }
expose :email, if: -> (e, _) { e.respond_to?(:email) }
expose :stack, if: -> (e, _) { e.respond_to?(:stack) }
expose :update_available?, as: :update_available, if: -> (e, _) { e.respond_to?(:update_available?) }