Commit 673b6be1 authored by Kamil Trzciński's avatar Kamil Trzciński

Merge branch 'add-ingress-to-cluster-applications' into 'master'

Add Ingress to cluster applications section

See merge request gitlab-org/gitlab-ce!15185
parents 244ced48 6a8a3f25
...@@ -42,7 +42,7 @@ export default class Clusters { ...@@ -42,7 +42,7 @@ export default class Clusters {
this.service = new ClustersService({ this.service = new ClustersService({
endpoint: statusPath, endpoint: statusPath,
installHelmEndpoint: installHelmPath, installHelmEndpoint: installHelmPath,
installIngresEndpoint: installIngressPath, installIngressEndpoint: installIngressPath,
installRunnerEndpoint: installRunnerPath, installRunnerEndpoint: installRunnerPath,
}); });
......
...@@ -95,9 +95,18 @@ export default { ...@@ -95,9 +95,18 @@ export default {
:status-reason="applications.helm.statusReason" :status-reason="applications.helm.statusReason"
:request-status="applications.helm.requestStatus" :request-status="applications.helm.requestStatus"
:request-reason="applications.helm.requestReason" :request-reason="applications.helm.requestReason"
/>
<application-row
id="ingress"
:title="applications.ingress.title"
title-link="https://kubernetes.io/docs/concepts/services-networking/ingress/"
:description="ingressDescription"
:status="applications.ingress.status"
:status-reason="applications.ingress.statusReason"
:request-status="applications.ingress.requestStatus"
:request-reason="applications.ingress.requestReason"
/> />
<!-- NOTE: Don't forget to update `clusters.scss` min-height for this block and uncomment `application_spec` tests --> <!-- NOTE: Don't forget to update `clusters.scss` min-height for this block and uncomment `application_spec` tests -->
<!-- Add Ingress row, all other plumbing is complete -->
<!-- Add GitLab Runner row, all other plumbing is complete --> <!-- Add GitLab Runner row, all other plumbing is complete -->
</div> </div>
</div> </div>
......
...@@ -6,5 +6,5 @@ ...@@ -6,5 +6,5 @@
.cluster-applications-table { .cluster-applications-table {
// Wait for the Vue to kick-in and render the applications block // Wait for the Vue to kick-in and render the applications block
min-height: 179px; min-height: 302px;
} }
module Clusters
module Applications
class Ingress < ActiveRecord::Base
self.table_name = 'clusters_applications_ingress'
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)
end
end
end
end
...@@ -5,7 +5,8 @@ module Clusters ...@@ -5,7 +5,8 @@ module Clusters
self.table_name = 'clusters' self.table_name = 'clusters'
APPLICATIONS = { APPLICATIONS = {
Applications::Helm.application_name => Applications::Helm Applications::Helm.application_name => Applications::Helm,
Applications::Ingress.application_name => Applications::Ingress
}.freeze }.freeze
belongs_to :user belongs_to :user
...@@ -20,6 +21,7 @@ module Clusters ...@@ -20,6 +21,7 @@ module Clusters
has_one :platform_kubernetes, class_name: 'Clusters::Platforms::Kubernetes', autosave: true, dependent: :destroy # rubocop:disable Cop/ActiveRecordDependent has_one :platform_kubernetes, class_name: 'Clusters::Platforms::Kubernetes', autosave: true, dependent: :destroy # rubocop:disable Cop/ActiveRecordDependent
has_one :application_helm, class_name: 'Clusters::Applications::Helm' has_one :application_helm, class_name: 'Clusters::Applications::Helm'
has_one :application_ingress, class_name: 'Clusters::Applications::Ingress'
accepts_nested_attributes_for :provider_gcp, update_only: true accepts_nested_attributes_for :provider_gcp, update_only: true
accepts_nested_attributes_for :platform_kubernetes, update_only: true accepts_nested_attributes_for :platform_kubernetes, update_only: true
...@@ -62,7 +64,8 @@ module Clusters ...@@ -62,7 +64,8 @@ module Clusters
def applications def applications
[ [
application_helm || build_application_helm application_helm || build_application_helm,
application_ingress || build_application_ingress
] ]
end end
......
...@@ -7,6 +7,7 @@ ...@@ -7,6 +7,7 @@
- status_path = status_namespace_project_cluster_path(@cluster.project.namespace, @cluster.project, @cluster.id, format: :json) if can?(current_user, :admin_cluster, @cluster) - status_path = status_namespace_project_cluster_path(@cluster.project.namespace, @cluster.project, @cluster.id, format: :json) if can?(current_user, :admin_cluster, @cluster)
.edit-cluster-form.js-edit-cluster-form{ data: { status_path: status_path, .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_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),
toggle_status: @cluster.enabled? ? 'true': 'false', toggle_status: @cluster.enabled? ? 'true': 'false',
cluster_status: @cluster.status_name, cluster_status: @cluster.status_name,
cluster_status_reason: @cluster.status_reason, cluster_status_reason: @cluster.status_reason,
......
---
title: Add Ingress to available Cluster applications
merge_request:
author:
type: added
class CreateClustersKubernetesIngressApps < ActiveRecord::Migration
include Gitlab::Database::MigrationHelpers
DOWNTIME = false
def change
create_table :clusters_applications_ingress do |t|
t.references :cluster, null: false, unique: true, foreign_key: { on_delete: :cascade }
t.datetime_with_timezone :created_at, null: false
t.datetime_with_timezone :updated_at, null: false
t.integer :status, null: false
t.integer :ingress_type, null: false
t.string :version, null: false
t.string :cluster_ip
t.text :status_reason
end
end
end
...@@ -11,7 +11,7 @@ ...@@ -11,7 +11,7 @@
# #
# It's strongly recommended that you check this file into your version control system. # It's strongly recommended that you check this file into your version control system.
ActiveRecord::Schema.define(version: 20171101134435) do ActiveRecord::Schema.define(version: 20171106101200) do
# These are extensions that must be enabled in order to support this database # These are extensions that must be enabled in order to support this database
enable_extension "plpgsql" enable_extension "plpgsql"
...@@ -528,6 +528,17 @@ ActiveRecord::Schema.define(version: 20171101134435) do ...@@ -528,6 +528,17 @@ ActiveRecord::Schema.define(version: 20171101134435) do
t.text "status_reason" t.text "status_reason"
end end
create_table "clusters_applications_ingress", force: :cascade do |t|
t.integer "cluster_id", null: false
t.datetime_with_timezone "created_at", null: false
t.datetime_with_timezone "updated_at", null: false
t.integer "status", null: false
t.integer "ingress_type", null: false
t.string "version", null: false
t.string "cluster_ip"
t.text "status_reason"
end
create_table "container_repositories", force: :cascade do |t| create_table "container_repositories", force: :cascade do |t|
t.integer "project_id", null: false t.integer "project_id", null: false
t.string "name", null: false t.string "name", null: false
......
FactoryGirl.define do
factory :cluster_applications_ingress, class: Clusters::Applications::Ingress do
cluster factory: %i(cluster provided_by_gcp)
trait :not_installable do
status(-2)
end
trait :installable do
status 0
end
trait :scheduled do
status 1
end
trait :installing do
status 2
end
trait :installed do
status 3
end
trait :errored do
status(-1)
status_reason 'something went wrong'
end
trait :timeouted do
installing
updated_at ClusterWaitForAppInstallationWorker::TIMEOUT.ago
end
end
end
...@@ -51,8 +51,10 @@ feature 'Clusters', :js do ...@@ -51,8 +51,10 @@ feature 'Clusters', :js do
expect(page).to have_content('Cluster is being created on Google Container Engine...') expect(page).to have_content('Cluster is being created on Google Container Engine...')
# Application Installation buttons # Application Installation buttons
page.within('.js-cluster-application-row-helm') do
expect(page.find(:css, '.js-cluster-application-install-button')['disabled']).to eq('true') expect(page.find(:css, '.js-cluster-application-install-button')['disabled']).to eq('true')
expect(page.find(:css, '.js-cluster-application-install-button').text).to eq('Install') expect(page.find(:css, '.js-cluster-application-install-button').text).to eq('Install')
end
Clusters::Cluster.last.provider.make_created! Clusters::Cluster.last.provider.make_created!
...@@ -92,25 +94,30 @@ feature 'Clusters', :js do ...@@ -92,25 +94,30 @@ feature 'Clusters', :js do
expect(page.find(:css, '.cluster-name').value).to eq(cluster.name) expect(page.find(:css, '.cluster-name').value).to eq(cluster.name)
# Application Installation buttons # Application Installation buttons
page.within('.js-cluster-application-row-helm') do
expect(page.find(:css, '.js-cluster-application-install-button')['disabled']).to be_nil expect(page.find(:css, '.js-cluster-application-install-button')['disabled']).to be_nil
expect(page.find(:css, '.js-cluster-application-install-button')).to have_content('Install') expect(page.find(:css, '.js-cluster-application-install-button')).to have_content('Install')
end end
end
context 'when user installs application: tiller' do context 'when user installs application: Helm Tiller' do
before do before do
allow(ClusterInstallAppWorker).to receive(:perform_async).and_return(nil) allow(ClusterInstallAppWorker).to receive(:perform_async).and_return(nil)
page.within('.js-cluster-application-row-helm') do
page.find(:css, '.js-cluster-application-install-button').click page.find(:css, '.js-cluster-application-install-button').click
end end
end
it 'user sees status transition' do it 'user sees status transition' do
# FE sends request and gets the responce, then the buttons is "Install" page.within('.js-cluster-application-row-helm') do
# FE sends request and gets the response, then the buttons is "Install"
expect(page.find(:css, '.js-cluster-application-install-button')['disabled']).to eq('true') expect(page.find(:css, '.js-cluster-application-install-button')['disabled']).to eq('true')
expect(page.find(:css, '.js-cluster-application-install-button')).to have_content('Install') expect(page.find(:css, '.js-cluster-application-install-button')).to have_content('Install')
Clusters::Cluster.last.application_helm.make_installing! Clusters::Cluster.last.application_helm.make_installing!
# FE starts pooling and update the buttons to "Installing" # FE starts polling and update the buttons to "Installing"
expect(page.find(:css, '.js-cluster-application-install-button')['disabled']).to eq('true') expect(page.find(:css, '.js-cluster-application-install-button')['disabled']).to eq('true')
expect(page.find(:css, '.js-cluster-application-install-button')).to have_content('Installing') expect(page.find(:css, '.js-cluster-application-install-button')).to have_content('Installing')
...@@ -118,10 +125,47 @@ feature 'Clusters', :js do ...@@ -118,10 +125,47 @@ feature 'Clusters', :js do
expect(page.find(:css, '.js-cluster-application-install-button')['disabled']).to eq('true') expect(page.find(:css, '.js-cluster-application-install-button')['disabled']).to eq('true')
expect(page.find(:css, '.js-cluster-application-install-button')).to have_content('Installed') expect(page.find(:css, '.js-cluster-application-install-button')).to have_content('Installed')
end
expect(page).to have_content('Helm Tiller was successfully installed on your cluster') expect(page).to have_content('Helm Tiller was successfully installed on your cluster')
end end
end end
context 'when user installs application: Ingress' do
before do
allow(ClusterInstallAppWorker).to receive(:perform_async).and_return(nil)
# Helm Tiller needs to be installed before you can install Ingress
create(:cluster_applications_helm, :installed, cluster: cluster)
visit project_clusters_path(project)
page.within('.js-cluster-application-row-ingress') do
page.find(:css, '.js-cluster-application-install-button').click
end
end
it 'user sees status transition' do
page.within('.js-cluster-application-row-ingress') do
# FE sends request and gets the response, then the buttons is "Install"
expect(page.find(:css, '.js-cluster-application-install-button')['disabled']).to eq('true')
expect(page.find(:css, '.js-cluster-application-install-button')).to have_content('Install')
Clusters::Cluster.last.application_ingress.make_installing!
# FE starts polling and update the buttons to "Installing"
expect(page.find(:css, '.js-cluster-application-install-button')['disabled']).to eq('true')
expect(page.find(:css, '.js-cluster-application-install-button')).to have_content('Installing')
Clusters::Cluster.last.application_ingress.make_installed!
expect(page.find(:css, '.js-cluster-application-install-button')['disabled']).to eq('true')
expect(page.find(:css, '.js-cluster-application-install-button')).to have_content('Installed')
end
expect(page).to have_content('Ingress was successfully installed on your cluster')
end
end
context 'when user disables the cluster' do context 'when user disables the cluster' do
before do before do
page.find(:css, '.js-toggle-cluster').click page.find(:css, '.js-toggle-cluster').click
......
...@@ -29,11 +29,11 @@ describe('Applications', () => { ...@@ -29,11 +29,11 @@ describe('Applications', () => {
expect(vm.$el.querySelector('.js-cluster-application-row-helm')).toBeDefined(); expect(vm.$el.querySelector('.js-cluster-application-row-helm')).toBeDefined();
}); });
/* * /
it('renders a row for Ingress', () => { it('renders a row for Ingress', () => {
expect(vm.$el.querySelector('.js-cluster-application-row-ingress')).toBeDefined(); expect(vm.$el.querySelector('.js-cluster-application-row-ingress')).toBeDefined();
}); });
/* * /
it('renders a row for GitLab Runner', () => { it('renders a row for GitLab Runner', () => {
expect(vm.$el.querySelector('.js-cluster-application-row-runner')).toBeDefined(); expect(vm.$el.querySelector('.js-cluster-application-row-runner')).toBeDefined();
}); });
......
require 'rails_helper'
describe Clusters::Applications::Ingress do
it { is_expected.to belong_to(:cluster) }
it { is_expected.to validate_presence_of(:cluster) }
describe '#name' do
it 'is .application_name' do
expect(subject.name).to eq(described_class.application_name)
end
it 'is recorded in Clusters::Cluster::APPLICATIONS' do
expect(Clusters::Cluster::APPLICATIONS[subject.name]).to eq(described_class)
end
end
describe '#status' do
let(:cluster) { create(:cluster, :provided_by_gcp) }
subject { described_class.new(cluster: cluster) }
it 'defaults to :not_installable' do
expect(subject.status_name).to be(:not_installable)
end
context 'when application helm is scheduled' do
before do
create(:cluster_applications_helm, :scheduled, cluster: cluster)
end
it 'defaults to :not_installable' do
expect(subject.status_name).to be(:not_installable)
end
end
context 'when application helm is installed' do
before do
create(:cluster_applications_helm, :installed, cluster: cluster)
end
it 'defaults to :installable' do
expect(subject.status_name).to be(:installable)
end
end
end
describe '#install_command' do
it 'has all the needed information' do
expect(subject.install_command).to have_attributes(name: subject.name, install_helm: false, chart: subject.chart)
end
end
describe 'status state machine' do
describe '#make_installing' do
subject { create(:cluster_applications_ingress, :scheduled) }
it 'is installing' do
subject.make_installing!
expect(subject).to be_installing
end
end
describe '#make_installed' do
subject { create(:cluster_applications_ingress, :installing) }
it 'is installed' do
subject.make_installed
expect(subject).to be_installed
end
end
describe '#make_errored' do
subject { create(:cluster_applications_ingress, :installing) }
let(:reason) { 'some errors' }
it 'is errored' do
subject.make_errored(reason)
expect(subject).to be_errored
expect(subject.status_reason).to eq(reason)
end
end
describe '#make_scheduled' do
subject { create(:cluster_applications_ingress, :installable) }
it 'is scheduled' do
subject.make_scheduled
expect(subject).to be_scheduled
end
describe 'when was errored' do
subject { create(:cluster_applications_ingress, :errored) }
it 'clears #status_reason' do
expect(subject.status_reason).not_to be_nil
subject.make_scheduled!
expect(subject.status_reason).to be_nil
end
end
end
end
end
...@@ -178,4 +178,25 @@ describe Clusters::Cluster do ...@@ -178,4 +178,25 @@ describe Clusters::Cluster do
it { is_expected.to be_nil } it { is_expected.to be_nil }
end end
end end
describe '#applications' do
set(:cluster) { create(:cluster) }
subject { cluster.applications }
context 'when none of applications are created' do
it 'returns a list of a new objects' do
is_expected.not_to be_empty
end
end
context 'when applications are created' do
let!(:helm) { create(:cluster_applications_helm, cluster: cluster) }
let!(:ingress) { create(:cluster_applications_ingress, cluster: cluster) }
it 'returns a list of created applications' do
is_expected.to contain_exactly(helm, ingress)
end
end
end
end end
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