diff options
author | GitLab Bot <gitlab-bot@gitlab.com> | 2019-11-18 03:06:28 +0000 |
---|---|---|
committer | GitLab Bot <gitlab-bot@gitlab.com> | 2019-11-18 03:06:28 +0000 |
commit | c1f270b8ba4602952c36ce042e5eae439b22f9a6 (patch) | |
tree | 205917e3dc8dcaeaaa0de55e2618eaca4197c4c2 /app | |
parent | 7f4a1ba886819078d1fa0bfc348e3743f0e2b2f2 (diff) | |
download | gitlab-ce-c1f270b8ba4602952c36ce042e5eae439b22f9a6.tar.gz |
Add latest changes from gitlab-org/gitlab@master
Diffstat (limited to 'app')
-rw-r--r-- | app/assets/images/cluster_app_logos/crossplane.png | bin | 0 -> 1850 bytes | |||
-rw-r--r-- | app/assets/javascripts/clusters/clusters_bundle.js | 47 | ||||
-rw-r--r-- | app/assets/javascripts/clusters/components/applications.vue | 113 | ||||
-rw-r--r-- | app/assets/javascripts/clusters/components/crossplane_provider_stack.vue | 93 | ||||
-rw-r--r-- | app/assets/javascripts/clusters/constants.js | 1 | ||||
-rw-r--r-- | app/assets/javascripts/clusters/services/clusters_service.js | 1 | ||||
-rw-r--r-- | app/assets/javascripts/clusters/stores/clusters_store.js | 10 | ||||
-rw-r--r-- | app/controllers/clusters/applications_controller.rb | 2 | ||||
-rw-r--r-- | app/controllers/clusters/clusters_controller.rb | 1 | ||||
-rw-r--r-- | app/models/clusters/applications/crossplane.rb | 60 | ||||
-rw-r--r-- | app/models/clusters/cluster.rb | 2 | ||||
-rw-r--r-- | app/serializers/cluster_application_entity.rb | 1 | ||||
-rw-r--r-- | app/services/clusters/applications/base_service.rb | 6 | ||||
-rw-r--r-- | app/views/clusters/clusters/show.html.haml | 1 |
14 files changed, 301 insertions, 37 deletions
diff --git a/app/assets/images/cluster_app_logos/crossplane.png b/app/assets/images/cluster_app_logos/crossplane.png Binary files differnew file mode 100644 index 00000000000..32d8175108c --- /dev/null +++ b/app/assets/images/cluster_app_logos/crossplane.png 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, diff --git a/app/controllers/clusters/applications_controller.rb b/app/controllers/clusters/applications_controller.rb index 5364116a5f8..be68d0d0a1d 100644 --- a/app/controllers/clusters/applications_controller.rb +++ b/app/controllers/clusters/applications_controller.rb @@ -47,7 +47,7 @@ class Clusters::ApplicationsController < Clusters::BaseController 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 diff --git a/app/controllers/clusters/clusters_controller.rb b/app/controllers/clusters/clusters_controller.rb index 5d6ce4f342c..9a539cf7c24 100644 --- a/app/controllers/clusters/clusters_controller.rb +++ b/app/controllers/clusters/clusters_controller.rb @@ -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 diff --git a/app/models/clusters/applications/crossplane.rb b/app/models/clusters/applications/crossplane.rb new file mode 100644 index 00000000000..36246b26066 --- /dev/null +++ b/app/models/clusters/applications/crossplane.rb @@ -0,0 +1,60 @@ +# 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 diff --git a/app/models/clusters/cluster.rb b/app/models/clusters/cluster.rb index ac2b63b194f..f522f3f2fdb 100644 --- a/app/models/clusters/cluster.rb +++ b/app/models/clusters/cluster.rb @@ -14,6 +14,7 @@ module Clusters 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 @@ module Clusters 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 diff --git a/app/serializers/cluster_application_entity.rb b/app/serializers/cluster_application_entity.rb index a5b983d4074..218bdd21e37 100644 --- a/app/serializers/cluster_application_entity.rb +++ b/app/serializers/cluster_application_entity.rb @@ -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?) } expose :can_uninstall?, as: :can_uninstall end diff --git a/app/services/clusters/applications/base_service.rb b/app/services/clusters/applications/base_service.rb index 1a6f6e3aa6f..c9f7917938f 100644 --- a/app/services/clusters/applications/base_service.rb +++ b/app/services/clusters/applications/base_service.rb @@ -27,6 +27,10 @@ module Clusters application.email = params[:email] end + if application.has_attribute?(:stack) + application.stack = params[:stack] + end + if application.respond_to?(:oauth_application) application.oauth_application = create_oauth_application(application, request) end @@ -64,7 +68,7 @@ module Clusters end def invalid_application? - unknown_application? || (application_name == Applications::ElasticStack.application_name && !Feature.enabled?(:enable_cluster_application_elastic_stack)) + unknown_application? || (application_name == Applications::ElasticStack.application_name && !Feature.enabled?(:enable_cluster_application_elastic_stack)) || (application_name == Applications::Crossplane.application_name && !Feature.enabled?(:enable_cluster_application_crossplane)) end def unknown_application? diff --git a/app/views/clusters/clusters/show.html.haml b/app/views/clusters/clusters/show.html.haml index c5288c8d02d..5beeaf7259a 100644 --- a/app/views/clusters/clusters/show.html.haml +++ b/app/views/clusters/clusters/show.html.haml @@ -12,6 +12,7 @@ install_helm_path: clusterable.install_applications_cluster_path(@cluster, :helm), install_ingress_path: clusterable.install_applications_cluster_path(@cluster, :ingress), install_cert_manager_path: clusterable.install_applications_cluster_path(@cluster, :cert_manager), + install_crossplane_path: clusterable.install_applications_cluster_path(@cluster, :crossplane), install_prometheus_path: clusterable.install_applications_cluster_path(@cluster, :prometheus), install_runner_path: clusterable.install_applications_cluster_path(@cluster, :runner), install_jupyter_path: clusterable.install_applications_cluster_path(@cluster, :jupyter), |