Skip to content
Snippets Groups Projects
Commit 0d454802 authored by Mayra Cabrera's avatar Mayra Cabrera Committed by Kamil Trzciński
Browse files

Extend Cluster Applications to allow installation of Prometheus

parent 79cbfedf
No related branches found
No related tags found
No related merge requests found
Showing
with 295 additions and 130 deletions
Loading
Loading
@@ -30,6 +30,7 @@ export default class Clusters {
installHelmPath,
installIngressPath,
installRunnerPath,
installPrometheusPath,
clusterStatus,
clusterStatusReason,
helpPath,
Loading
Loading
@@ -44,6 +45,7 @@ export default class Clusters {
installHelmEndpoint: installHelmPath,
installIngressEndpoint: installIngressPath,
installRunnerEndpoint: installRunnerPath,
installPrometheusEndpoint: installPrometheusPath,
});
 
this.toggle = this.toggle.bind(this);
Loading
Loading
Loading
Loading
@@ -67,6 +67,16 @@ export default {
and send the results back to GitLab.`,
));
},
prometheusDescription() {
return sprintf(
_.escape(s__('ClusterIntegration|Prometheus is an open-source monitoring system with %{gitlabIntegrationLink} to monitor deployed applications.')), {
gitlabIntegrationLink: `<a href="https://docs.gitlab.com/ce/user/project/integrations/prometheus.html", target="_blank" rel="noopener noreferrer">
${_.escape(s__('ClusterIntegration|Gitlab Integration'))}
</a>`,
},
false,
);
},
},
};
</script>
Loading
Loading
@@ -105,6 +115,16 @@ export default {
:status-reason="applications.ingress.statusReason"
:request-status="applications.ingress.requestStatus"
:request-reason="applications.ingress.requestReason"
/>
<application-row
id="prometheus"
:title="applications.prometheus.title"
title-link="https://prometheus.io/docs/introduction/overview/"
:description="prometheusDescription"
:status="applications.prometheus.status"
:status-reason="applications.prometheus.statusReason"
:request-status="applications.prometheus.requestStatus"
:request-reason="applications.prometheus.requestReason"
/>
<!-- NOTE: Don't forget to update `clusters.scss` min-height for this block and uncomment `application_spec` tests -->
<!-- Add GitLab Runner row, all other plumbing is complete -->
Loading
Loading
Loading
Loading
@@ -7,6 +7,7 @@ export default class ClusterService {
helm: this.options.installHelmEndpoint,
ingress: this.options.installIngressEndpoint,
runner: this.options.installRunnerEndpoint,
prometheus: this.options.installPrometheusEndpoint,
};
}
 
Loading
Loading
Loading
Loading
@@ -28,6 +28,13 @@ export default class ClusterStore {
requestStatus: null,
requestReason: null,
},
prometheus: {
title: s__('ClusterIntegration|Prometheus'),
status: null,
statusReason: null,
requestStatus: null,
requestReason: null,
},
},
};
}
Loading
Loading
Loading
Loading
@@ -6,7 +6,7 @@
 
.cluster-applications-table {
// Wait for the Vue to kick-in and render the applications block
min-height: 302px;
min-height: 400px;
}
 
.clusters-dropdown-menu {
Loading
Loading
Loading
Loading
@@ -3,32 +3,19 @@ module Clusters
class Helm < ActiveRecord::Base
self.table_name = 'clusters_applications_helm'
 
include ::Clusters::Concerns::ApplicationCore
include ::Clusters::Concerns::ApplicationStatus
 
belongs_to :cluster, class_name: 'Clusters::Cluster', foreign_key: :cluster_id
default_value_for :version, Gitlab::Kubernetes::Helm::HELM_VERSION
 
validates :cluster, presence: true
after_initialize :set_initial_status
def self.application_name
self.to_s.demodulize.underscore
end
def set_initial_status
return unless not_installable?
 
self.status = 'installable' if cluster&.platform_kubernetes_active?
end
 
def name
self.class.application_name
end
def install_command
Gitlab::Kubernetes::Helm::InstallCommand.new(name, true)
Gitlab::Kubernetes::Helm::InstallCommand.new(name, install_helm: true)
end
end
end
Loading
Loading
Loading
Loading
@@ -3,41 +3,22 @@ module Clusters
class Ingress < ActiveRecord::Base
self.table_name = 'clusters_applications_ingress'
 
include ::Clusters::Concerns::ApplicationCore
include ::Clusters::Concerns::ApplicationStatus
 
belongs_to :cluster, class_name: 'Clusters::Cluster', foreign_key: :cluster_id
validates :cluster, presence: true
default_value_for :ingress_type, :nginx
default_value_for :version, :nginx
 
after_initialize :set_initial_status
enum ingress_type: {
nginx: 1
}
 
def self.application_name
self.to_s.demodulize.underscore
end
def set_initial_status
return unless not_installable?
self.status = 'installable' if cluster&.application_helm_installed?
end
def name
self.class.application_name
end
def chart
'stable/nginx-ingress'
end
 
def install_command
Gitlab::Kubernetes::Helm::InstallCommand.new(name, false, chart)
Gitlab::Kubernetes::Helm::InstallCommand.new(name, chart: chart)
end
end
end
Loading
Loading
module Clusters
module Applications
class Prometheus < ActiveRecord::Base
VERSION = "2.0.0".freeze
self.table_name = 'clusters_applications_prometheus'
include ::Clusters::Concerns::ApplicationCore
include ::Clusters::Concerns::ApplicationStatus
default_value_for :version, VERSION
def chart
'stable/prometheus'
end
def chart_values_file
"#{Rails.root}/vendor/#{name}/values.yaml"
end
def install_command
Gitlab::Kubernetes::Helm::InstallCommand.new(name, chart: chart, chart_values_file: chart_values_file)
end
end
end
end
Loading
Loading
@@ -6,7 +6,8 @@ module Clusters
 
APPLICATIONS = {
Applications::Helm.application_name => Applications::Helm,
Applications::Ingress.application_name => Applications::Ingress
Applications::Ingress.application_name => Applications::Ingress,
Applications::Prometheus.application_name => Applications::Prometheus
}.freeze
 
belongs_to :user
Loading
Loading
@@ -21,6 +22,7 @@ module Clusters
 
has_one :application_helm, class_name: 'Clusters::Applications::Helm'
has_one :application_ingress, class_name: 'Clusters::Applications::Ingress'
has_one :application_prometheus, class_name: 'Clusters::Applications::Prometheus'
 
accepts_nested_attributes_for :provider_gcp, update_only: true
accepts_nested_attributes_for :platform_kubernetes, update_only: true
Loading
Loading
@@ -62,7 +64,8 @@ module Clusters
def applications
[
application_helm || build_application_helm,
application_ingress || build_application_ingress
application_ingress || build_application_ingress,
application_prometheus || build_application_prometheus
]
end
 
Loading
Loading
module Clusters
module Concerns
module ApplicationCore
extend ActiveSupport::Concern
included do
belongs_to :cluster, class_name: 'Clusters::Cluster', foreign_key: :cluster_id
validates :cluster, presence: true
after_initialize :set_initial_status
def set_initial_status
return unless not_installable?
self.status = 'installable' if cluster&.application_helm_installed?
end
def self.application_name
self.to_s.demodulize.underscore
end
def name
self.class.application_name
end
end
end
end
end
Loading
Loading
@@ -18,7 +18,7 @@ module Clusters
end
 
def helm_api
@helm_api ||= Gitlab::Kubernetes::Helm.new(kubeclient)
@helm_api ||= Gitlab::Kubernetes::Helm::Api.new(kubeclient)
end
 
def install_command
Loading
Loading
Loading
Loading
@@ -9,6 +9,7 @@
.edit-cluster-form.js-edit-cluster-form{ data: { status_path: status_path,
install_helm_path: install_applications_namespace_project_cluster_path(@cluster.project.namespace, @cluster.project, @cluster, :helm),
install_ingress_path: install_applications_namespace_project_cluster_path(@cluster.project.namespace, @cluster.project, @cluster, :ingress),
install_prometheus_path: install_applications_namespace_project_cluster_path(@cluster.project.namespace, @cluster.project, @cluster, :prometheus),
toggle_status: @cluster.enabled? ? 'true': 'false',
cluster_status: @cluster.status_name,
cluster_status_reason: @cluster.status_reason,
Loading
Loading
---
title: Add Prometheus to available Cluster applications
merge_request: 15895
author:
type: added
class CreateClustersApplicationsPrometheus < ActiveRecord::Migration
include Gitlab::Database::MigrationHelpers
DOWNTIME = false
def change
create_table :clusters_applications_prometheus do |t|
t.references :cluster, null: false, unique: true, foreign_key: { on_delete: :cascade }
t.integer :status, null: false
t.string :version, null: false
t.text :status_reason
t.timestamps_with_timezone null: false
end
end
end
Loading
Loading
@@ -568,6 +568,15 @@ ActiveRecord::Schema.define(version: 20171220191323) do
t.text "status_reason"
end
 
create_table "clusters_applications_prometheus", force: :cascade do |t|
t.integer "cluster_id", null: false
t.integer "status", null: false
t.string "version", null: false
t.text "status_reason"
t.datetime_with_timezone "created_at", null: false
t.datetime_with_timezone "updated_at", null: false
end
create_table "container_repositories", force: :cascade do |t|
t.integer "project_id", null: false
t.string "name", null: false
Loading
Loading
module Gitlab
module Kubernetes
class Helm
module Helm
HELM_VERSION = '2.7.0'.freeze
NAMESPACE = 'gitlab-managed-apps'.freeze
INSTALL_DEPS = <<-EOS.freeze
set -eo pipefail
apk add -U ca-certificates openssl >/dev/null
wget -q -O - https://kubernetes-helm.storage.googleapis.com/helm-v${HELM_VERSION}-linux-amd64.tar.gz | tar zxC /tmp >/dev/null
mv /tmp/linux-amd64/helm /usr/bin/
EOS
InstallCommand = Struct.new(:name, :install_helm, :chart) do
def pod_name
"install-#{name}"
end
end
def initialize(kubeclient)
@kubeclient = kubeclient
@namespace = Gitlab::Kubernetes::Namespace.new(NAMESPACE, kubeclient)
end
def install(command)
@namespace.ensure_exists!
@kubeclient.create_pod(pod_resource(command))
end
##
# Returns Pod phase
#
# https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle/#pod-phase
#
# values: "Pending", "Running", "Succeeded", "Failed", "Unknown"
#
def installation_status(pod_name)
@kubeclient.get_pod(pod_name, @namespace.name).status.phase
end
def installation_log(pod_name)
@kubeclient.get_pod_log(pod_name, @namespace.name).body
end
def delete_installation_pod!(pod_name)
@kubeclient.delete_pod(pod_name, @namespace.name)
end
private
def pod_resource(command)
labels = { 'gitlab.org/action': 'install', 'gitlab.org/application': command.name }
metadata = { name: command.pod_name, namespace: @namespace.name, labels: labels }
container = {
name: 'helm',
image: 'alpine:3.6',
env: generate_pod_env(command),
command: %w(/bin/sh),
args: %w(-c $(COMMAND_SCRIPT))
}
spec = { containers: [container], restartPolicy: 'Never' }
::Kubeclient::Resource.new(metadata: metadata, spec: spec)
end
def generate_pod_env(command)
{
HELM_VERSION: HELM_VERSION,
TILLER_NAMESPACE: @namespace.name,
COMMAND_SCRIPT: generate_script(command)
}.map { |key, value| { name: key, value: value } }
end
def generate_script(command)
[
INSTALL_DEPS,
helm_init_command(command),
helm_install_command(command)
].join("\n")
end
def helm_init_command(command)
if command.install_helm
'helm init >/dev/null'
else
'helm init --client-only >/dev/null'
end
end
def helm_install_command(command)
return if command.chart.nil?
"helm install #{command.chart} --name #{command.name} --namespace #{@namespace.name} >/dev/null"
end
end
end
end
module Gitlab
module Kubernetes
module Helm
class Api
def initialize(kubeclient)
@kubeclient = kubeclient
@namespace = Gitlab::Kubernetes::Namespace.new(Gitlab::Kubernetes::Helm::NAMESPACE, kubeclient)
end
def install(command)
@namespace.ensure_exists!
@kubeclient.create_pod(pod_resource(command))
end
##
# Returns Pod phase
#
# https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle/#pod-phase
#
# values: "Pending", "Running", "Succeeded", "Failed", "Unknown"
#
def installation_status(pod_name)
@kubeclient.get_pod(pod_name, @namespace.name).status.phase
end
def installation_log(pod_name)
@kubeclient.get_pod_log(pod_name, @namespace.name).body
end
def delete_installation_pod!(pod_name)
@kubeclient.delete_pod(pod_name, @namespace.name)
end
private
def pod_resource(command)
Pod.new(command, @namespace.name, @kubeclient).generate
end
end
end
end
end
module Gitlab
module Kubernetes
module Helm
class InstallCommand
attr_reader :name, :install_helm, :chart, :chart_values_file
def initialize(name, install_helm: false, chart: false, chart_values_file: false)
@name = name
@install_helm = install_helm
@chart = chart
@chart_values_file = chart_values_file
end
def pod_name
"install-#{name}"
end
def generate_script(namespace_name)
[
install_dps_command,
init_command,
complete_command(namespace_name)
].join("\n")
end
private
def init_command
if install_helm
'helm init >/dev/null'
else
'helm init --client-only >/dev/null'
end
end
def complete_command(namespace_name)
return unless chart
"helm install #{chart} --name #{name} --namespace #{namespace_name} >/dev/null"
end
def install_dps_command
<<~HEREDOC
set -eo pipefail
apk add -U ca-certificates openssl >/dev/null
wget -q -O - https://kubernetes-helm.storage.googleapis.com/helm-v#{Gitlab::Kubernetes::Helm::HELM_VERSION}-linux-amd64.tar.gz | tar zxC /tmp >/dev/null
mv /tmp/linux-amd64/helm /usr/bin/
HEREDOC
end
end
end
end
end
module Gitlab
module Kubernetes
module Helm
class Pod
def initialize(command, namespace_name, kubeclient)
@command = command
@namespace_name = namespace_name
@kubeclient = kubeclient
end
def generate
spec = { containers: [container_specification], restartPolicy: 'Never' }
if command.chart_values_file
generate_config_map
spec['volumes'] = volumes_specification
end
::Kubeclient::Resource.new(metadata: metadata, spec: spec)
end
private
attr_reader :command, :namespace_name, :kubeclient
def container_specification
container = {
name: 'helm',
image: 'alpine:3.6',
env: generate_pod_env(command),
command: %w(/bin/sh),
args: %w(-c $(COMMAND_SCRIPT))
}
container[:volumeMounts] = volume_mounts_specification if command.chart_values_file
container
end
def labels
{ 'gitlab.org/action': 'install', 'gitlab.org/application': command.name }
end
def metadata
{ name: command.pod_name, namespace: namespace_name, labels: labels }
end
def volume_mounts_specification
[{ name: 'config-volume', mountPath: '/etc/config' }]
end
def volumes_specification
[{ name: 'config-volume', configMap: { name: 'values-config' } }]
end
def generate_pod_env(command)
{
HELM_VERSION: Gitlab::Kubernetes::Helm::HELM_VERSION,
TILLER_NAMESPACE: namespace_name,
COMMAND_SCRIPT: command.generate_script(namespace_name)
}.map { |key, value| { name: key, value: value } }
end
def generate_config_map
resource = ::Kubeclient::Resource.new
resource.metadata = { name: 'values-config', namespace: namespace_name }
resource.data = YAML.load_file(command.chart_values_file)
kubeclient.create_config_map(resource)
end
end
end
end
end
Loading
Loading
@@ -52,7 +52,7 @@ describe Projects::Clusters::ApplicationsController do
 
context 'when application is already installing' do
before do
create(:cluster_applications_helm, :installing, cluster: cluster)
create(:clusters_applications_helm, :installing, cluster: cluster)
end
 
it 'returns 400' do
Loading
Loading
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment