Commit ecb16107 authored by Adrien Kohlbecker's avatar Adrien Kohlbecker Committed by Jan Provaznik

Add elastic stack as cluster app

Add a new gitlab-managed app for the elastic stack
parent 9071b7fa
...@@ -37,6 +37,7 @@ export default class Clusters { ...@@ -37,6 +37,7 @@ export default class Clusters {
installJupyterPath, installJupyterPath,
installKnativePath, installKnativePath,
updateKnativePath, updateKnativePath,
installElasticStackPath,
installPrometheusPath, installPrometheusPath,
managePrometheusPath, managePrometheusPath,
clusterEnvironmentsPath, clusterEnvironmentsPath,
...@@ -86,6 +87,7 @@ export default class Clusters { ...@@ -86,6 +87,7 @@ export default class Clusters {
installJupyterEndpoint: installJupyterPath, installJupyterEndpoint: installJupyterPath,
installKnativeEndpoint: installKnativePath, installKnativeEndpoint: installKnativePath,
updateKnativeEndpoint: updateKnativePath, updateKnativeEndpoint: updateKnativePath,
installElasticStackEndpoint: installElasticStackPath,
clusterEnvironmentsEndpoint: clusterEnvironmentsPath, clusterEnvironmentsEndpoint: clusterEnvironmentsPath,
}); });
......
...@@ -12,6 +12,7 @@ import certManagerLogo from 'images/cluster_app_logos/cert_manager.png'; ...@@ -12,6 +12,7 @@ import certManagerLogo from 'images/cluster_app_logos/cert_manager.png';
import knativeLogo from 'images/cluster_app_logos/knative.png'; import knativeLogo from 'images/cluster_app_logos/knative.png';
import meltanoLogo from 'images/cluster_app_logos/meltano.png'; import meltanoLogo from 'images/cluster_app_logos/meltano.png';
import prometheusLogo from 'images/cluster_app_logos/prometheus.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 { 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';
...@@ -91,6 +92,7 @@ export default { ...@@ -91,6 +92,7 @@ export default {
knativeLogo, knativeLogo,
meltanoLogo, meltanoLogo,
prometheusLogo, prometheusLogo,
elasticStackLogo,
}), }),
computed: { computed: {
isProjectCluster() { isProjectCluster() {
...@@ -114,6 +116,9 @@ export default { ...@@ -114,6 +116,9 @@ export default {
certManagerInstalled() { certManagerInstalled() {
return this.applications.cert_manager.status === APPLICATION_STATUS.INSTALLED; return this.applications.cert_manager.status === APPLICATION_STATUS.INSTALLED;
}, },
enableClusterApplicationElasticStack() {
return gon.features && gon.features.enableClusterApplicationElasticStack;
},
ingressDescription() { ingressDescription() {
return sprintf( return sprintf(
_.escape( _.escape(
...@@ -168,6 +173,12 @@ export default { ...@@ -168,6 +173,12 @@ export default {
jupyterHostname() { jupyterHostname() {
return this.applications.jupyter.hostname; return this.applications.jupyter.hostname;
}, },
elasticStackInstalled() {
return this.applications.elastic_stack.status === APPLICATION_STATUS.INSTALLED;
},
elasticStackKibanaHostname() {
return this.applications.elastic_stack.kibana_hostname;
},
knative() { knative() {
return this.applications.knative; return this.applications.knative;
}, },
...@@ -542,6 +553,75 @@ export default { ...@@ -542,6 +553,75 @@ export default {
/> />
</div> </div>
</application-row> </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> </div>
</section> </section>
</template> </template>
...@@ -2,7 +2,16 @@ ...@@ -2,7 +2,16 @@
import { GlModal } from '@gitlab/ui'; import { GlModal } from '@gitlab/ui';
import { sprintf, s__ } from '~/locale'; import { sprintf, s__ } from '~/locale';
import trackUninstallButtonClickMixin from 'ee_else_ce/clusters/mixins/track_uninstall_button_click'; 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 = { const CUSTOM_APP_WARNING_TEXT = {
[HELM]: sprintf( [HELM]: sprintf(
...@@ -28,6 +37,7 @@ const CUSTOM_APP_WARNING_TEXT = { ...@@ -28,6 +37,7 @@ const CUSTOM_APP_WARNING_TEXT = {
[JUPYTER]: s__( [JUPYTER]: s__(
'ClusterIntegration|All data not committed to GitLab will be deleted and cannot be restored.', '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 { export default {
......
...@@ -51,7 +51,17 @@ export const KNATIVE = 'knative'; ...@@ -51,7 +51,17 @@ export const KNATIVE = 'knative';
export const RUNNER = 'runner'; export const RUNNER = 'runner';
export const CERT_MANAGER = 'cert_manager'; export const CERT_MANAGER = 'cert_manager';
export const PROMETHEUS = 'prometheus'; 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'; export const INGRESS_DOMAIN_SUFFIX = '.nip.io';
...@@ -11,6 +11,7 @@ export default class ClusterService { ...@@ -11,6 +11,7 @@ export default class ClusterService {
prometheus: this.options.installPrometheusEndpoint, prometheus: this.options.installPrometheusEndpoint,
jupyter: this.options.installJupyterEndpoint, jupyter: this.options.installJupyterEndpoint,
knative: this.options.installKnativeEndpoint, knative: this.options.installKnativeEndpoint,
elastic_stack: this.options.installElasticStackEndpoint,
}; };
this.appUpdateEndpointMap = { this.appUpdateEndpointMap = {
knative: this.options.updateKnativeEndpoint, knative: this.options.updateKnativeEndpoint,
......
...@@ -5,6 +5,7 @@ import { ...@@ -5,6 +5,7 @@ import {
JUPYTER, JUPYTER,
KNATIVE, KNATIVE,
CERT_MANAGER, CERT_MANAGER,
ELASTIC_STACK,
RUNNER, RUNNER,
APPLICATION_INSTALLED_STATUSES, APPLICATION_INSTALLED_STATUSES,
APPLICATION_STATUS, APPLICATION_STATUS,
...@@ -85,6 +86,11 @@ export default class ClusterStore { ...@@ -85,6 +86,11 @@ export default class ClusterStore {
updateSuccessful: false, updateSuccessful: false,
updateFailed: false, updateFailed: false,
}, },
elastic_stack: {
...applicationInitialState,
title: s__('ClusterIntegration|Elastic Stack'),
kibana_hostname: null,
},
}, },
environments: [], environments: [],
fetchingEnvironments: false, fetchingEnvironments: false,
...@@ -198,12 +204,11 @@ export default class ClusterStore { ...@@ -198,12 +204,11 @@ export default class ClusterStore {
this.state.applications.cert_manager.email = this.state.applications.cert_manager.email =
this.state.applications.cert_manager.email || serverAppEntry.email; this.state.applications.cert_manager.email || serverAppEntry.email;
} else if (appId === JUPYTER) { } else if (appId === JUPYTER) {
this.state.applications.jupyter.hostname = this.state.applications.jupyter.hostname = this.updateHostnameIfUnset(
this.state.applications.jupyter.hostname || this.state.applications.jupyter.hostname,
serverAppEntry.hostname || serverAppEntry.hostname,
(this.state.applications.ingress.externalIp 'jupyter',
? `jupyter.${this.state.applications.ingress.externalIp}.nip.io` );
: '');
} else if (appId === KNATIVE) { } else if (appId === KNATIVE) {
if (!this.state.applications.knative.isEditingHostName) { if (!this.state.applications.knative.isEditingHostName) {
this.state.applications.knative.hostname = this.state.applications.knative.hostname =
...@@ -216,10 +221,26 @@ export default class ClusterStore { ...@@ -216,10 +221,26 @@ export default class ClusterStore {
} else if (appId === RUNNER) { } else if (appId === RUNNER) {
this.state.applications.runner.version = version; this.state.applications.runner.version = version;
this.state.applications.runner.updateAvailable = updateAvailable; 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) { toggleFetchEnvironments(isFetching) {
this.state.fetchingEnvironments = isFetching; this.state.fetchingEnvironments = isFetching;
} }
......
...@@ -47,7 +47,7 @@ class Clusters::ApplicationsController < Clusters::BaseController ...@@ -47,7 +47,7 @@ class Clusters::ApplicationsController < Clusters::BaseController
end end
def cluster_application_params def cluster_application_params
params.permit(:application, :hostname, :email) params.permit(:application, :hostname, :kibana_hostname, :email)
end end
def cluster_application_destroy_params def cluster_application_destroy_params
......
...@@ -15,6 +15,9 @@ class Clusters::ClustersController < Clusters::BaseController ...@@ -15,6 +15,9 @@ class Clusters::ClustersController < Clusters::BaseController
before_action only: [:new, :create_gcp] do before_action only: [:new, :create_gcp] do
push_frontend_feature_flag(:create_eks_clusters) push_frontend_feature_flag(:create_eks_clusters)
end end
before_action only: [:show] do
push_frontend_feature_flag(:enable_cluster_application_elastic_stack)
end
helper_method :token_in_session helper_method :token_in_session
......
# frozen_string_literal: true
module Clusters
module Applications
class ElasticStack < ApplicationRecord
VERSION = '1.8.0'
self.table_name = 'clusters_applications_elastic_stacks'
include ::Clusters::Concerns::ApplicationCore
include ::Clusters::Concerns::ApplicationStatus
include ::Clusters::Concerns::ApplicationVersion
include ::Clusters::Concerns::ApplicationData
default_value_for :version, VERSION
def set_initial_status
return unless not_installable?
return unless cluster&.application_ingress_available?
ingress = cluster.application_ingress
self.status = status_states[:installable] if ingress.external_ip_or_hostname?
end
def chart
'stable/elastic-stack'
end
def values
content_values.to_yaml
end
def install_command
Gitlab::Kubernetes::Helm::InstallCommand.new(
name: 'elastic-stack',
version: VERSION,
rbac: cluster.platform_kubernetes_rbac?,
chart: chart,
files: files
)
end
def uninstall_command
Gitlab::Kubernetes::Helm::DeleteCommand.new(
name: 'elastic-stack',
rbac: cluster.platform_kubernetes_rbac?,
files: files,
postdelete: post_delete_script
)
end
private
def specification
{
"kibana" => {
"ingress" => {
"hosts" => [kibana_hostname],
"tls" => [{
"hosts" => [kibana_hostname],
"secretName" => "kibana-cert"
}]
}
}
}
end
def content_values
YAML.load_file(chart_values_file).deep_merge!(specification)
end
def post_delete_script
[
Gitlab::Kubernetes::KubectlCmd.delete("pvc", "--selector", "release=elastic-stack")
].compact
end
end
end
end
...@@ -40,7 +40,7 @@ module Clusters ...@@ -40,7 +40,7 @@ module Clusters
end end
def allowed_to_uninstall? def allowed_to_uninstall?
external_ip_or_hostname? && application_jupyter_nil_or_installable? external_ip_or_hostname? && application_jupyter_nil_or_installable? && application_elastic_stack_nil_or_installable?
end end
def install_command def install_command
...@@ -91,6 +91,10 @@ module Clusters ...@@ -91,6 +91,10 @@ module Clusters
def application_jupyter_nil_or_installable? def application_jupyter_nil_or_installable?
cluster.application_jupyter.nil? || cluster.application_jupyter&.installable? cluster.application_jupyter.nil? || cluster.application_jupyter&.installable?
end end
def application_elastic_stack_nil_or_installable?
cluster.application_elastic_stack.nil? || cluster.application_elastic_stack&.installable?
end
end end
end end
end end
...@@ -18,7 +18,8 @@ module Clusters ...@@ -18,7 +18,8 @@ module Clusters
Applications::Prometheus.application_name => Applications::Prometheus, Applications::Prometheus.application_name => Applications::Prometheus,
Applications::Runner.application_name => Applications::Runner, Applications::Runner.application_name => Applications::Runner,
Applications::Jupyter.application_name => Applications::Jupyter, Applications::Jupyter.application_name => Applications::Jupyter,
Applications::Knative.application_name => Applications::Knative Applications::Knative.application_name => Applications::Knative,
Applications::ElasticStack.application_name => Applications::ElasticStack
}.merge(PROJECT_ONLY_APPLICATIONS).freeze }.merge(PROJECT_ONLY_APPLICATIONS).freeze
DEFAULT_ENVIRONMENT = '*' DEFAULT_ENVIRONMENT = '*'
KUBE_INGRESS_BASE_DOMAIN = 'KUBE_INGRESS_BASE_DOMAIN' KUBE_INGRESS_BASE_DOMAIN = 'KUBE_INGRESS_BASE_DOMAIN'
...@@ -51,6 +52,7 @@ module Clusters ...@@ -51,6 +52,7 @@ module Clusters
has_one_cluster_application :runner has_one_cluster_application :runner
has_one_cluster_application :jupyter has_one_cluster_application :jupyter
has_one_cluster_application :knative has_one_cluster_application :knative
has_one_cluster_application :elastic_stack
has_many :kubernetes_namespaces has_many :kubernetes_namespaces
......
...@@ -8,6 +8,7 @@ class ClusterApplicationEntity < Grape::Entity ...@@ -8,6 +8,7 @@ class ClusterApplicationEntity < Grape::Entity
expose :external_ip, if: -> (e, _) { e.respond_to?(:external_ip) } expose :external_ip, if: -> (e, _) { e.respond_to?(:external_ip) }
expose :external_hostname, if: -> (e, _) { e.respond_to?(:external_hostname) } expose :external_hostname, if: -> (e, _) { e.respond_to?(:external_hostname) }
expose :hostname, if: -> (e, _) { e.respond_to?(:hostname) } 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 :email, if: -> (e, _) { e.respond_to?(:email) }
expose :update_available?, as: :update_available, if: -> (e, _) { e.respond_to?(:update_available?) } expose :update_available?, as: :update_available, if: -> (e, _) { e.respond_to?(:update_available?) }
expose :can_uninstall?, as: :can_uninstall expose :can_uninstall?, as: :can_uninstall
......
...@@ -19,6 +19,10 @@ module Clusters ...@@ -19,6 +19,10 @@ module Clusters
application.hostname = params[:hostname] application.hostname = params[:hostname]
end end
if application.has_attribute?(:kibana_hostname)
application.kibana_hostname = params[:kibana_hostname]
end
if application.has_attribute?(:email) if application.has_attribute?(:email)
application.email = params[:email] application.email = params[:email]
end end
...@@ -60,7 +64,7 @@ module Clusters ...@@ -60,7 +64,7 @@ module Clusters
end end
def invalid_application? def invalid_application?
unknown_application? || (!cluster.project_type? && project_only_application?) unknown_application? || (!cluster.project_type? && project_only_application?) || (application_name == Applications::ElasticStack.application_name && !Feature.enabled?(:enable_cluster_application_elastic_stack))
end end
def unknown_application? def unknown_application?
......
...@@ -17,6 +17,7 @@ ...@@ -17,6 +17,7 @@
install_jupyter_path: clusterable.install_applications_cluster_path(@cluster, :jupyter), install_jupyter_path: clusterable.install_applications_cluster_path(@cluster, :jupyter),
install_knative_path: clusterable.install_applications_cluster_path(@cluster, :knative), install_knative_path: clusterable.install_applications_cluster_path(@cluster, :knative),
update_knative_path: clusterable.update_applications_cluster_path(@cluster, :knative), update_knative_path: clusterable.update_applications_cluster_path(@cluster, :knative),
install_elastic_stack_path: clusterable.install_applications_cluster_path(@cluster, :elastic_stack),
cluster_environments_path: cluster_environments_path, cluster_environments_path: cluster_environments_path,
toggle_status: @cluster.enabled? ? 'true': 'false', toggle_status: @cluster.enabled? ? 'true': 'false',
has_rbac: has_rbac_enabled?(@cluster) ? 'true': 'false', has_rbac: has_rbac_enabled?(@cluster) ? 'true': 'false',
......
# frozen_string_literal: true
# See http://doc.gitlab.com/ce/development/migration_style_guide.html
# for more information on how to write migrations for GitLab.
class CreateClustersApplicationsElasticStack < ActiveRecord::Migration[5.2]
include Gitlab::Database::MigrationHelpers
DOWNTIME = false
def change
create_table :clusters_applications_elastic_stacks do |t|
t.timestamps_with_timezone null: false
t.references :cluster, null: false, index: false, foreign_key: { on_delete: :cascade }
t.integer :status, null: false
t.string :version, null: false, limit: 255
t.string :kibana_hostname, limit: 255
t.text :status_reason
t.index :cluster_id, unique: true
end
end
end
...@@ -1055,6 +1055,17 @@ ActiveRecord::Schema.define(version: 2019_10_17_045817) do ...@@ -1055,6 +1055,17 @@ ActiveRecord::Schema.define(version: 2019_10_17_045817) do
t.index ["cluster_id"], name: "index_clusters_applications_cert_managers_on_cluster_id", unique: true t.index ["cluster_id"], name: "index_clusters_applications_cert_managers_on_cluster_id", unique: true
end end
create_table "clusters_applications_elastic_stacks", force: :cascade do |t|
t.datetime_with_timezone "created_at", null: false
t.datetime_with_timezone "updated_at", null: false
t.bigint "cluster_id", null: false
t.integer "status", null: false
t.string "version", limit: 255, null: false
t.string "kibana_hostname", limit: 255
t.text "status_reason"
t.index ["cluster_id"], name: "index_clusters_applications_elastic_stacks_on_cluster_id", unique: true
end
create_table "clusters_applications_helm", id: :serial, force: :cascade do |t| create_table "clusters_applications_helm", id: :serial, force: :cascade do |t|
t.integer "cluster_id", null: false t.integer "cluster_id", null: false
t.datetime "created_at", null: false t.datetime "created_at", null: false
...@@ -4109,6 +4120,7 @@ ActiveRecord::Schema.define(version: 2019_10_17_045817) do ...@@ -4109,6 +4120,7 @@ ActiveRecord::Schema.define(version: 2019_10_17_045817) do
add_foreign_key "clusters", "projects", column: "management_project_id", name: "fk_f05c5e5a42", on_delete: :nullify add_foreign_key "clusters", "projects", column: "management_project_id", name: "fk_f05c5e5a42", on_delete: :nullify
add_foreign_key "clusters", "users", on_delete: :nullify add_foreign_key "clusters", "users", on_delete: :nullify
add_foreign_key "clusters_applications_cert_managers", "clusters", on_delete: :cascade add_foreign_key "clusters_applications_cert_managers", "clusters", on_delete: :cascade
add_foreign_key "clusters_applications_elastic_stacks", "clusters", on_delete: :cascade
add_foreign_key "clusters_applications_helm", "clusters", on_delete: :cascade add_foreign_key "clusters_applications_helm", "clusters", on_delete: :cascade
add_foreign_key "clusters_applications_ingress", "clusters", on_delete: :cascade add_foreign_key "clusters_applications_ingress", "clusters", on_delete: :cascade
add_foreign_key "clusters_applications_jupyter", "clusters", on_delete: :cascade add_foreign_key "clusters_applications_jupyter", "clusters", on_delete: :cascade
......
...@@ -40,7 +40,7 @@ module Gitlab ...@@ -40,7 +40,7 @@ module Gitlab
private private
def repository_update_command def repository_update_command
'helm repo update' if repository 'helm repo update'
end end
# Uses `helm upgrade --install` which means we can use this for both # Uses `helm upgrade --install` which means we can use this for both
......
...@@ -74,6 +74,7 @@ module Gitlab ...@@ -74,6 +74,7 @@ module Gitlab
clusters_applications_prometheus: count(::Clusters::Applications::Prometheus.available), clusters_applications_prometheus: count(::Clusters::Applications::Prometheus.available),
clusters_applications_runner: count(::Clusters::Applications::Runner.available), clusters_applications_runner: count(::Clusters::Applications::Runner.available),
clusters_applications_knative: count(::Clusters::Applications::Knative.available), clusters_applications_knative: count(::Clusters::Applications::Knative.available),
clusters_applications_elastic_stack: count(::Clusters::Applications::ElasticStack.available),
in_review_folder: count(::Environment.in_review_folder), in_review_folder: count(::Environment.in_review_folder),
groups: count(Group), groups: count(Group),
issues: count(Issue), issues: count(Issue),
......
...@@ -3510,6 +3510,9 @@ msgstr "" ...@@ -3510,6 +3510,9 @@ msgstr ""
msgid "ClusterIntegration|Copy Jupyter Hostname" msgid "ClusterIntegration|Copy Jupyter Hostname"
msgstr "" msgstr ""
msgid "ClusterIntegration|Copy Kibana Hostname"
msgstr ""
msgid "ClusterIntegration|Copy Knative Endpoint" msgid "ClusterIntegration|Copy Knative Endpoint"
msgstr "" msgstr ""
...@@ -3555,6 +3558,9 @@ msgstr "" ...@@ -3555,6 +3558,9 @@ msgstr ""
msgid "ClusterIntegration|Did you know?" msgid "ClusterIntegration|Did you know?"
msgstr "" msgstr ""
msgid "ClusterIntegration|Elastic Stack"
msgstr ""
msgid "ClusterIntegration|Enable Cloud Run on GKE (beta)" msgid "ClusterIntegration|Enable Cloud Run on GKE (beta)"
msgstr "" msgstr ""
...@@ -3675,6 +3681,9 @@ msgstr "" ...@@ -3675,6 +3681,9 @@ msgstr ""
msgid "ClusterIntegration|Key pair name" msgid "ClusterIntegration|Key pair name"
msgstr "" msgstr ""
msgid "ClusterIntegration|Kibana Hostname"
msgstr ""
msgid "ClusterIntegration|Knative" msgid "ClusterIntegration|Knative"
msgstr "" msgstr ""
...@@ -3978,6 +3987,9 @@ msgstr "" ...@@ -3978,6 +3987,9 @@ msgstr ""
msgid "ClusterIntegration|The associated private key will be deleted and cannot be restored." msgid "ClusterIntegration|The associated private key will be deleted and cannot be restored."
msgstr "" msgstr ""
msgid "ClusterIntegration|The elastic stack collects logs from all pods in your cluster"
msgstr ""
msgid "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." msgid "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."
msgstr "" msgstr ""
......
...@@ -79,6 +79,10 @@ FactoryBot.define do ...@@ -79,6 +79,10 @@ FactoryBot.define do
cluster factory: %i(cluster with_installed_helm provided_by_gcp) cluster factory: %i(cluster with_installed_helm provided_by_gcp)
end end
factory :clusters_applications_elastic_stack, class: Clusters::Applications::ElasticStack do
cluster factory: %i(cluster with_installed_helm provided_by_gcp)
end
factory :clusters_applications_prometheus, class: Clusters::Applications::Prometheus do factory :clusters_applications_prometheus, class: Clusters::Applications::Prometheus do
cluster factory: %i(cluster with_installed_helm provided_by_gcp) cluster factory: %i(cluster with_installed_helm provided_by_gcp)
end end
......
...@@ -178,6 +178,37 @@ shared_examples "installing applications on a cluster" do ...@@ -178,6 +178,37 @@ shared_examples "installing applications on a cluster" do
end end
end end
context 'when user installs Elastic Stack' do
before do
allow(ClusterInstallAppWorker).to receive(:perform_async)
allow(ClusterWaitForIngressIpAddressWorker).to receive(:perform_in)
allow(ClusterWaitForIngressIpAddressWorker).to receive(:perform_async)
create(:clusters_applications_helm, :installed, cluster: cluster)
create(:clusters_applications_ingress, :installed, external_ip: '127.0.0.1', cluster: cluster)
page.within('.js-cluster-application-row-elastic_stack') do
click_button 'Install'
end
end
it 'shows status transition' do
page.within('.js-cluster-application-row-elastic_stack') do
expect(page).to have_css('.js-cluster-application-install-button', exact_text: 'Installing')
Clusters::Cluster.last.application_elastic_stack.make_installing!
expect(page).to have_css('.js-cluster-application-install-button', exact_text: 'Installing')
Clusters::Cluster.last.application_elastic_stack.make_installed!
expect(page).to have_css('.js-cluster-application-uninstall-button', exact_text: 'Uninstall')
end
expect(page).to have_content('Elastic Stack was successfully installed on your Kubernetes cluster')
end
end
context 'when user installs Ingress' do context 'when user installs Ingress' do
before do before do
allow(ClusterInstallAppWorker).to receive(:perform_async) allow(ClusterInstallAppWorker).to receive(:perform_async)
......
...@@ -35,6 +35,7 @@ ...@@ -35,6 +35,7 @@
"external_ip": { "type": ["string", "null"] }, "external_ip": { "type": ["string", "null"] },
"external_hostname": { "type": ["string", "null"] }, "external_hostname": { "type": ["string", "null"] },
"hostname": { "type": ["string", "null"] }, "hostname": { "type": ["string", "null"] },
"kibana_hostname": { "type": ["string", "null"] },
"email": { "type": ["string", "null"] }, "email": { "type": ["string", "null"] },
"update_available": { "type": ["boolean", "null"] }, "update_available": { "type": ["boolean", "null"] },
"can_uninstall": { "type": "boolean" } "can_uninstall": { "type": "boolean" }
......
...@@ -13,6 +13,9 @@ describe('Applications', () => { ...@@ -13,6 +13,9 @@ describe('Applications', () => {
beforeEach(() => { beforeEach(() => {
Applications = Vue.extend(applications); Applications = Vue.extend(applications);
gon.features = gon.features || {};
gon.features.enableClusterApplicationElasticStack = true;
}); });
afterEach(() => { afterEach(() => {
...@@ -54,6 +57,10 @@ describe('Applications', () => { ...@@ -54,6 +57,10 @@ describe('Applications', () => {
it('renders a row for Knative', () => { it('renders a row for Knative', () => {
expect(vm.$el.querySelector('.js-cluster-application-row-knative')).not.toBeNull(); expect(vm.$el.querySelector('.js-cluster-application-row-knative')).not.toBeNull();
}); });
it('renders a row for Elastic Stack', () => {
expect(vm.$el.querySelector('.js-cluster-application-row-elastic_stack')).not.toBeNull();
});
}); });
describe('Group cluster applications', () => { describe('Group cluster applications', () => {
...@@ -91,6 +98,10 @@ describe('Applications', () => { ...@@ -91,6 +98,10 @@ describe('Applications', () => {
it('renders a row for Knative', () => { it('renders a row for Knative', () => {
expect(vm.$el.querySelector('.js-cluster-application-row-knative')).not.toBeNull(); expect(vm.$el.querySelector('.js-cluster-application-row-knative')).not.toBeNull();
}); });
it('renders a row for Elastic Stack', () => {
expect(vm.$el.querySelector('.js-cluster-application-row-elastic_stack')).not.toBeNull();
});
}); });
describe('Instance cluster applications', () => { describe('Instance cluster applications', () => {
...@@ -128,6 +139,10 @@ describe('Applications', () => { ...@@ -128,6 +139,10 @@ describe('Applications', () => {
it('renders a row for Knative', () => { it('renders a row for Knative', () => {
expect(vm.$el.querySelector('.js-cluster-application-row-knative')).not.toBeNull(); expect(vm.$el.querySelector('.js-cluster-application-row-knative')).not.toBeNull();
}); });
it('renders a row for Elastic Stack', () => {
expect(vm.$el.querySelector('.js-cluster-application-row-elastic_stack')).not.toBeNull();
});
}); });
describe('Ingress application', () => { describe('Ingress application', () => {
...@@ -168,6 +183,7 @@ describe('Applications', () => { ...@@ -168,6 +183,7 @@ describe('Applications', () => {
prometheus: { title: 'Prometheus' }, prometheus: { title: 'Prometheus' },
jupyter: { title: 'JupyterHub', hostname: '' }, jupyter: { title: 'JupyterHub', hostname: '' },
knative: { title: 'Knative', hostname: '' }, knative: { title: 'Knative', hostname: '' },
elastic_stack: { title: 'Elastic Stack', kibana_hostname: '' },
}, },
}); });
...@@ -260,7 +276,11 @@ describe('Applications', () => { ...@@ -260,7 +276,11 @@ describe('Applications', () => {
}, },
}); });
expect(vm.$el.querySelector('.js-hostname').getAttribute('readonly')).toEqual(null); expect(
vm.$el
.querySelector('.js-cluster-application-row-jupyter .js-hostname')
.getAttribute('readonly'),
).toEqual(null);
}); });
}); });
...@@ -273,7 +293,9 @@ describe('Applications', () => { ...@@ -273,7 +293,9 @@ describe('Applications', () => {
}, },
}); });
expect(vm.$el.querySelector('.js-hostname')).toBe(null); expect(vm.$el.querySelector('.js-cluster-application-row-jupyter .js-hostname')).toBe(
null,
);
}); });
}); });
...@@ -287,7 +309,11 @@ describe('Applications', () => { ...@@ -287,7 +309,11 @@ describe('Applications', () => {
}, },
}); });
expect(vm.$el.querySelector('.js-hostname').getAttribute('readonly')).toEqual('readonly'); expect(
vm.$el
.querySelector('.js-cluster-application-row-jupyter .js-hostname')
.getAttribute('readonly'),
).toEqual('readonly');
}); });
}); });
...@@ -299,7 +325,9 @@ describe('Applications', () => { ...@@ -299,7 +325,9 @@ describe('Applications', () => {
}); });
it('does not render input', () => { it('does not render input', () => {
expect(vm.$el.querySelector('.js-hostname')).toBe(null); expect(vm.$el.querySelector('.js-cluster-application-row-jupyter .js-hostname')).toBe(
null,
);
}); });
it('renders disabled install button', () => { it('renders disabled install button', () => {
...@@ -361,4 +389,84 @@ describe('Applications', () => { ...@@ -361,4 +389,84 @@ describe('Applications', () => {
}); });
}); });
}); });
describe('Elastic Stack application', () => {
describe('with ingress installed with ip & elastic stack installable', () => {
it('renders hostname active input', () => {
vm = mountComponent(Applications, {
applications: {
...APPLICATIONS_MOCK_STATE,
ingress: {
title: 'Ingress',
status: 'installed',
externalIp: '1.1.1.1',
},
},
});
expect(
vm.$el
.querySelector('.js-cluster-application-row-elastic_stack .js-hostname')
.getAttribute('readonly'),
).toEqual(null);
});
});
describe('with ingress installed without external ip', () => {
it('does not render hostname input', () => {
vm = mountComponent(Applications, {
applications: {
...APPLICATIONS_MOCK_STATE,
ingress: { title: 'Ingress', status: 'installed' },
},
});
expect(vm.$el.querySelector('.js-cluster-application-row-elastic_stack .js-hostname')).toBe(
null,
);
});
});
describe('with ingress & elastic stack installed', () => {
it('renders readonly input', () => {
vm = mountComponent(Applications, {
applications: {
...APPLICATIONS_MOCK_STATE,
ingress: { title: 'Ingress', status: 'installed', externalIp: '1.1.1.1' },
elastic_stack: { title: 'Elastic Stack', status: 'installed', kibana_hostname: '' },
},
});
expect(
vm.$el
.querySelector('.js-cluster-application-row-elastic_stack .js-hostname')
.getAttribute('readonly'),
).toEqual('readonly');
});
});
describe('without ingress installed', () => {
beforeEach(() => {
vm = mountComponent(Applications, {
applications: APPLICATIONS_MOCK_STATE,
});
});
it('does not render input', () => {
expect(vm.$el.querySelector('.js-cluster-application-row-elastic_stack .js-hostname')).toBe(
null,
);
});
it('renders disabled install button', () => {
expect(
vm.$el
.querySelector(
'.js-cluster-application-row-elastic_stack .js-cluster-application-install-button',
)
.getAttribute('disabled'),
).toEqual('disabled');
});
});
});
}); });
...@@ -52,6 +52,12 @@ const CLUSTERS_MOCK_DATA = { ...@@ -52,6 +52,12 @@ const CLUSTERS_MOCK_DATA = {
email: 'test@example.com', email: 'test@example.com',
can_uninstall: false, can_uninstall: false,
}, },
{
name: 'elastic_stack',
status: APPLICATION_STATUS.INSTALLING,
status_reason: 'Cannot connect',
can_uninstall: false,
},
], ],
}, },
}, },
...@@ -98,6 +104,11 @@ const CLUSTERS_MOCK_DATA = { ...@@ -98,6 +104,11 @@ const CLUSTERS_MOCK_DATA = {
status_reason: 'Cannot connect', status_reason: 'Cannot connect',
email: 'test@example.com', email: 'test@example.com',
}, },
{
name: 'elastic_stack',
status: APPLICATION_STATUS.ERROR,
status_reason: 'Cannot connect',
},
], ],
}, },
}, },
...@@ -110,6 +121,7 @@ const CLUSTERS_MOCK_DATA = { ...@@ -110,6 +121,7 @@ const CLUSTERS_MOCK_DATA = {
'/gitlab-org/gitlab-shell/clusters/1/applications/prometheus': {}, '/gitlab-org/gitlab-shell/clusters/1/applications/prometheus': {},
'/gitlab-org/gitlab-shell/clusters/1/applications/jupyter': {}, '/gitlab-org/gitlab-shell/clusters/1/applications/jupyter': {},
'/gitlab-org/gitlab-shell/clusters/1/applications/knative': {}, '/gitlab-org/gitlab-shell/clusters/1/applications/knative': {},
'/gitlab-org/gitlab-shell/clusters/1/applications/elastic_stack': {},
}, },
}; };
...@@ -131,6 +143,7 @@ const APPLICATIONS_MOCK_STATE = { ...@@ -131,6 +143,7 @@ const APPLICATIONS_MOCK_STATE = {
prometheus: { title: 'Prometheus' }, prometheus: { title: 'Prometheus' },
jupyter: { title: 'JupyterHub', status: 'installable', hostname: '' }, jupyter: { title: 'JupyterHub', status: 'installable', hostname: '' },
knative: { title: 'Knative ', status: 'installable', hostname: '' }, knative: { title: 'Knative ', status: 'installable', hostname: '' },
elastic_stack: { title: 'Elastic Stack', status: 'installable', kibana_hostname: '' },
}; };
export { CLUSTERS_MOCK_DATA, DEFAULT_APPLICATION_STATE, APPLICATIONS_MOCK_STATE }; export { CLUSTERS_MOCK_DATA, DEFAULT_APPLICATION_STATE, APPLICATIONS_MOCK_STATE };
...@@ -153,6 +153,18 @@ describe('Clusters Store', () => { ...@@ -153,6 +153,18 @@ describe('Clusters Store', () => {
uninstallSuccessful: false, uninstallSuccessful: false,
uninstallFailed: false, uninstallFailed: false,
}, },
elastic_stack: {
title: 'Elastic Stack',
status: mockResponseData.applications[7].status,
installFailed: false,
statusReason: mockResponseData.applications[7].status_reason,
requestReason: null,
kibana_hostname: '',
installed: false,
uninstallable: false,
uninstallSuccessful: false,
uninstallFailed: false,
},
}, },
environments: [], environments: [],
fetchingEnvironments: false, fetchingEnvironments: false,
...@@ -183,5 +195,16 @@ describe('Clusters Store', () => { ...@@ -183,5 +195,16 @@ describe('Clusters Store', () => {
`jupyter.${store.state.applications.ingress.externalIp}.nip.io`, `jupyter.${store.state.applications.ingress.externalIp}.nip.io`,
); );
}); });
it('sets default hostname for elastic stack when ingress has a ip address', () => {
const mockResponseData =
CLUSTERS_MOCK_DATA.GET['/gitlab-org/gitlab-shell/clusters/2/status.json'].data;
store.updateStateFromServer(mockResponseData);
expect(store.state.applications.elastic_stack.kibana_hostname).toEqual(
`kibana.${store.state.applications.ingress.externalIp}.nip.io`,
);
});
}); });
}); });
...@@ -86,33 +86,6 @@ describe Gitlab::Kubernetes::Helm::InstallCommand do ...@@ -86,33 +86,6 @@ describe Gitlab::Kubernetes::Helm::InstallCommand do
end end
end end
context 'when there is no repository' do
let(:repository) { nil }
it_behaves_like 'helm commands' do
let(:commands) do
<<~EOS
helm init --upgrade
for i in $(seq 1 30); do helm version #{tls_flags} && s=0 && break || s=$?; sleep 1s; echo \"Retrying ($i)...\"; done; (exit $s)
#{helm_install_command}
EOS
end
let(:helm_install_command) do
<<~EOS.squish
helm upgrade app-name chart-name
--install
--reset-values
#{tls_flags}
--version 1.2.3
--set rbac.create\\=false,rbac.enabled\\=false
--namespace gitlab-managed-apps
-f /data/helm/app-name/config/values.yaml
EOS
end
end
end
context 'when there is a pre-install script' do context 'when there is a pre-install script' do
let(:preinstall) { ['/bin/date', '/bin/true'] } let(:preinstall) { ['/bin/date', '/bin/true'] }
......
...@@ -32,6 +32,7 @@ describe Gitlab::UsageData do ...@@ -32,6 +32,7 @@ describe Gitlab::UsageData do
create(:clusters_applications_prometheus, :installed, cluster: gcp_cluster) create(:clusters_applications_prometheus, :installed, cluster: gcp_cluster)
create(:clusters_applications_runner, :installed, cluster: gcp_cluster) create(:clusters_applications_runner, :installed, cluster: gcp_cluster)
create(:clusters_applications_knative, :installed, cluster: gcp_cluster) create(:clusters_applications_knative, :installed, cluster: gcp_cluster)
create(:clusters_applications_elastic_stack, :installed, cluster: gcp_cluster)
ProjectFeature.first.update_attribute('repository_access_level', 0) ProjectFeature.first.update_attribute('repository_access_level', 0)
end end
...@@ -120,6 +121,7 @@ describe Gitlab::UsageData do ...@@ -120,6 +121,7 @@ describe Gitlab::UsageData do
clusters_applications_prometheus clusters_applications_prometheus
clusters_applications_runner clusters_applications_runner
clusters_applications_knative clusters_applications_knative
clusters_applications_elastic_stack
in_review_folder in_review_folder
groups groups
issues issues
...@@ -190,6 +192,7 @@ describe Gitlab::UsageData do ...@@ -190,6 +192,7 @@ describe Gitlab::UsageData do
expect(count_data[:clusters_applications_prometheus]).to eq(1) expect(count_data[:clusters_applications_prometheus]).to eq(1)
expect(count_data[:clusters_applications_runner]).to eq(1) expect(count_data[:clusters_applications_runner]).to eq(1)
expect(count_data[:clusters_applications_knative]).to eq(1) expect(count_data[:clusters_applications_knative]).to eq(1)
expect(count_data[:clusters_applications_elastic_stack]).to eq(1)
end end
it 'works when queries time out' do it 'works when queries time out' do
......
# frozen_string_literal: true
require 'spec_helper'
describe Clusters::Applications::ElasticStack do
include_examples 'cluster application core specs', :clusters_applications_elastic_stack
include_examples 'cluster application status specs', :clusters_applications_elastic_stack
include_examples 'cluster application version specs', :clusters_applications_elastic_stack
include_examples 'cluster application helm specs', :clusters_applications_elastic_stack
describe '#can_uninstall?' do
let(:ingress) { create(:clusters_applications_ingress, :installed, external_hostname: 'localhost.localdomain') }
let(:elastic_stack) { create(:clusters_applications_elastic_stack, cluster: ingress.cluster) }
subject { elastic_stack.can_uninstall? }
it { is_expected.to be_truthy }
end
describe '#set_initial_status' do
before do
elastic_stack.set_initial_status
end
context 'when ingress is not installed' do
let(:cluster) { create(:cluster, :provided_by_gcp) }
let(:elastic_stack) { create(:clusters_applications_elastic_stack, cluster: cluster) }
it { expect(elastic_stack).to be_not_installable }
end
context 'when ingress is installed and external_ip is assigned' do
let(:ingress) { create(:clusters_applications_ingress, :installed, external_ip: '127.0.0.1') }
let(:elastic_stack) { create(:clusters_applications_elastic_stack, cluster: ingress.cluster) }
it { expect(elastic_stack).to be_installable }
end
context 'when ingress is installed and external_hostname is assigned' do
let(:ingress) { create(:clusters_applications_ingress, :installed, external_hostname: 'localhost.localdomain') }
let(:elastic_stack) { create(:clusters_applications_elastic_stack, cluster: ingress.cluster) }
it { expect(elastic_stack).to be_installable }
end
end
describe '#install_command' do
let!(:ingress) { create(:clusters_applications_ingress, :installed, external_ip: '127.0.0.1') }
let!(:elastic_stack) { create(:clusters_applications_elastic_stack, cluster: ingress.cluster) }
subject { elastic_stack.install_command }
it { is_expected.to be_an_instance_of(Gitlab::Kubernetes::Helm::InstallCommand) }
it 'is initialized with elastic stack arguments' do
expect(subject.name).to eq('elastic-stack')
expect(subject.chart).to eq('stable/elastic-stack')
expect(subject.version).to eq('1.8.0')
expect(subject).to be_rbac
expect(subject.files).to eq(elastic_stack.files)
end
context 'on a non rbac enabled cluster' do
before do
elastic_stack.cluster.platform_kubernetes.abac!
end
it { is_expected.not_to be_rbac }
end
context 'application failed to install previously' do
let(:elastic_stack) { create(:clusters_applications_elastic_stack, :errored, version: '0.0.1') }
it 'is initialized with the locked version' do
expect(subject.version).to eq('1.8.0')
end
end
end
describe '#uninstall_command' do
let!(:ingress) { create(:clusters_applications_ingress, :installed, external_ip: '127.0.0.1') }
let!(:elastic_stack) { create(:clusters_applications_elastic_stack, cluster: ingress.cluster) }
subject { elastic_stack.uninstall_command }
it { is_expected.to be_an_instance_of(Gitlab::Kubernetes::Helm::DeleteCommand) }
it 'is initialized with elastic stack arguments' do
expect(subject.name).to eq('elastic-stack')
expect(subject).to be_rbac
expect(subject.files).to eq(elastic_stack.files)
end
it 'specifies a post delete command to remove custom resource definitions' do
expect(subject.postdelete).to eq([
'kubectl delete pvc --selector release\\=elastic-stack'
])
end
end
describe '#files' do
let!(:ingress) { create(:clusters_applications_ingress, :installed, external_ip: '127.0.0.1') }
let!(:elastic_stack) { create(:clusters_applications_elastic_stack, cluster: ingress.cluster) }
let(:values) { subject[:'values.yaml'] }
subject { elastic_stack.files }
it 'includes elastic stack specific keys in the values.yaml file' do
expect(values).to include('ELASTICSEARCH_HOSTS')
end
end
end
...@@ -21,7 +21,7 @@ describe Clusters::Applications::Ingress do ...@@ -21,7 +21,7 @@ describe Clusters::Applications::Ingress do
describe '#can_uninstall?' do describe '#can_uninstall?' do
subject { ingress.can_uninstall? } subject { ingress.can_uninstall? }
it 'returns true if application_jupyter_nil_or_installable? AND external_ip_or_hostname? are true' do it 'returns true if external ip is set and no application exists' do
ingress.external_ip = 'IP' ingress.external_ip = 'IP'
is_expected.to be_truthy is_expected.to be_truthy
...@@ -33,6 +33,12 @@ describe Clusters::Applications::Ingress do ...@@ -33,6 +33,12 @@ describe Clusters::Applications::Ingress do
is_expected.to be_falsey is_expected.to be_falsey
end end
it 'returns false if application_elastic_stack_nil_or_installable? is false' do
create(:clusters_applications_elastic_stack, :installed, cluster: ingress.cluster)
is_expected.to be_falsey
end
it 'returns false if external_ip_or_hostname? is false' do it 'returns false if external_ip_or_hostname? is false' do
is_expected.to be_falsey is_expected.to be_falsey
end end
......
...@@ -508,9 +508,10 @@ describe Clusters::Cluster, :use_clean_rails_memory_store_caching do ...@@ -508,9 +508,10 @@ describe Clusters::Cluster, :use_clean_rails_memory_store_caching do
let!(:runner) { create(:clusters_applications_runner, cluster: cluster) } let!(:runner) { create(:clusters_applications_runner, cluster: cluster) }
let!(:jupyter) { create(:clusters_applications_jupyter, cluster: cluster) } let!(:jupyter) { create(:clusters_applications_jupyter, cluster: cluster) }
let!(:knative) { create(:clusters_applications_knative, cluster: cluster) } let!(:knative) { create(:clusters_applications_knative, cluster: cluster) }
let!(:elastic_stack) { create(:clusters_applications_elastic_stack, cluster: cluster) }
it 'returns a list of created applications' do it 'returns a list of created applications' do
is_expected.to contain_exactly(helm, ingress, cert_manager, prometheus, runner, jupyter, knative) is_expected.to contain_exactly(helm, ingress, cert_manager, prometheus, runner, jupyter, knative, elastic_stack)
end end
end end
end end
......
...@@ -132,6 +132,34 @@ describe Clusters::Applications::CreateService do ...@@ -132,6 +132,34 @@ describe Clusters::Applications::CreateService do
expect(subject.hostname).to eq('example.com') expect(subject.hostname).to eq('example.com')
end end
end end
context 'elastic stack application' do
let(:params) do
{
application: 'elastic_stack',
kibana_hostname: 'example.com'
}
end
before do
create(:clusters_applications_ingress, :installed, external_ip: "127.0.0.0", cluster: cluster)
expect_any_instance_of(Clusters::Applications::ElasticStack)
.to receive(:make_scheduled!)
.and_call_original
end
it 'creates the application' do
expect do
subject
cluster.reload
end.to change(cluster, :application_elastic_stack)
end
it 'sets the kibana_hostname' do
expect(subject.kibana_hostname).to eq('example.com')
end
end
end end
context 'invalid application' do context 'invalid application' do
......
...@@ -8,10 +8,6 @@ shared_examples 'cluster application helm specs' do |application_name| ...@@ -8,10 +8,6 @@ shared_examples 'cluster application helm specs' do |application_name|
it { is_expected.to be_an_instance_of(Gitlab::Kubernetes::Helm::DeleteCommand) } it { is_expected.to be_an_instance_of(Gitlab::Kubernetes::Helm::DeleteCommand) }
it 'has the application name' do
expect(subject.name).to eq(application.name)
end
it 'has files' do it 'has files' do
expect(subject.files).to eq(application.files) expect(subject.files).to eq(application.files)
end end
......
elasticsearch:
enabled: true
cluster:
env:
MINIMUM_MASTER_NODES: "1"
master:
replicas: 2
client:
replicas: 1
data:
replicas: 1
kibana:
enabled: true
env:
ELASTICSEARCH_HOSTS: http://elastic-stack-elasticsearch-client:9200
ingress:
enabled: true
annotations:
kubernetes.io/ingress.class: "nginx"
kubernetes.io/tls-acme: "true"
logstash:
enabled: false
filebeat:
enabled: true
config:
output.file.enabled: false
output.elasticsearch:
enabled: true
hosts: ["http://elastic-stack-elasticsearch-client:9200"]
fluentd:
enabled: false
fluent-bit:
enabled: false
nginx-ldapauth-proxy:
enabled: false
elasticsearch-curator:
enabled: false
elasticsearch-exporter:
enabled: false
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