diff options
author | Kamil Trzciński <ayufan@ayufan.eu> | 2017-11-07 20:23:21 +0000 |
---|---|---|
committer | Kamil Trzciński <ayufan@ayufan.eu> | 2017-11-07 20:23:21 +0000 |
commit | 673b6be1fecedd3a4e7126134f3a764694fcf327 (patch) | |
tree | ccb16f8b2e27ba9dbb2de3329c680e6fe5013983 | |
parent | 244ced4856c8ea335d36edac950265efa4ba55e7 (diff) | |
parent | 6a8a3f250dbae03f9fd7cab77a361887ed926719 (diff) | |
download | gitlab-ce-673b6be1fecedd3a4e7126134f3a764694fcf327.tar.gz |
Merge branch 'add-ingress-to-cluster-applications' into 'master'
Add Ingress to cluster applications section
See merge request gitlab-org/gitlab-ce!15185
-rw-r--r-- | app/assets/javascripts/clusters/clusters_bundle.js | 2 | ||||
-rw-r--r-- | app/assets/javascripts/clusters/components/applications.vue | 11 | ||||
-rw-r--r-- | app/assets/stylesheets/pages/clusters.scss | 2 | ||||
-rw-r--r-- | app/models/clusters/applications/ingress.rb | 44 | ||||
-rw-r--r-- | app/models/clusters/cluster.rb | 7 | ||||
-rw-r--r-- | app/views/projects/clusters/show.html.haml | 1 | ||||
-rw-r--r-- | changelogs/unreleased/add-ingress-to-cluster-applications.yml | 5 | ||||
-rw-r--r-- | db/migrate/20171106101200_create_clusters_kubernetes_ingress_apps.rb | 21 | ||||
-rw-r--r-- | db/schema.rb | 13 | ||||
-rw-r--r-- | spec/factories/clusters/applications/ingress.rb | 35 | ||||
-rw-r--r-- | spec/features/projects/clusters_spec.rb | 76 | ||||
-rw-r--r-- | spec/javascripts/clusters/components/applications_spec.js | 2 | ||||
-rw-r--r-- | spec/models/clusters/applications/ingress_spec.rb | 108 | ||||
-rw-r--r-- | spec/models/clusters/cluster_spec.rb | 21 |
14 files changed, 325 insertions, 23 deletions
diff --git a/app/assets/javascripts/clusters/clusters_bundle.js b/app/assets/javascripts/clusters/clusters_bundle.js index 5f421ea58ba..dc443475952 100644 --- a/app/assets/javascripts/clusters/clusters_bundle.js +++ b/app/assets/javascripts/clusters/clusters_bundle.js @@ -42,7 +42,7 @@ export default class Clusters { this.service = new ClustersService({ endpoint: statusPath, installHelmEndpoint: installHelmPath, - installIngresEndpoint: installIngressPath, + installIngressEndpoint: installIngressPath, installRunnerEndpoint: installRunnerPath, }); diff --git a/app/assets/javascripts/clusters/components/applications.vue b/app/assets/javascripts/clusters/components/applications.vue index 28da4cbe89d..e5ae439d26e 100644 --- a/app/assets/javascripts/clusters/components/applications.vue +++ b/app/assets/javascripts/clusters/components/applications.vue @@ -96,8 +96,17 @@ export default { :request-status="applications.helm.requestStatus" :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 --> - <!-- Add Ingress row, all other plumbing is complete --> <!-- Add GitLab Runner row, all other plumbing is complete --> </div> </div> diff --git a/app/assets/stylesheets/pages/clusters.scss b/app/assets/stylesheets/pages/clusters.scss index b9bfae9356c..e5b9e1f2de6 100644 --- a/app/assets/stylesheets/pages/clusters.scss +++ b/app/assets/stylesheets/pages/clusters.scss @@ -6,5 +6,5 @@ .cluster-applications-table { // Wait for the Vue to kick-in and render the applications block - min-height: 179px; + min-height: 302px; } diff --git a/app/models/clusters/applications/ingress.rb b/app/models/clusters/applications/ingress.rb new file mode 100644 index 00000000000..44bd979741e --- /dev/null +++ b/app/models/clusters/applications/ingress.rb @@ -0,0 +1,44 @@ +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 diff --git a/app/models/clusters/cluster.rb b/app/models/clusters/cluster.rb index 68759ebb6df..185d9473aab 100644 --- a/app/models/clusters/cluster.rb +++ b/app/models/clusters/cluster.rb @@ -5,7 +5,8 @@ module Clusters self.table_name = 'clusters' APPLICATIONS = { - Applications::Helm.application_name => Applications::Helm + Applications::Helm.application_name => Applications::Helm, + Applications::Ingress.application_name => Applications::Ingress }.freeze belongs_to :user @@ -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 :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 :platform_kubernetes, update_only: true @@ -62,7 +64,8 @@ module Clusters def applications [ - application_helm || build_application_helm + application_helm || build_application_helm, + application_ingress || build_application_ingress ] end diff --git a/app/views/projects/clusters/show.html.haml b/app/views/projects/clusters/show.html.haml index be6784058ae..b7671f5e3c4 100644 --- a/app/views/projects/clusters/show.html.haml +++ b/app/views/projects/clusters/show.html.haml @@ -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) .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), toggle_status: @cluster.enabled? ? 'true': 'false', cluster_status: @cluster.status_name, cluster_status_reason: @cluster.status_reason, diff --git a/changelogs/unreleased/add-ingress-to-cluster-applications.yml b/changelogs/unreleased/add-ingress-to-cluster-applications.yml new file mode 100644 index 00000000000..0064e8672f8 --- /dev/null +++ b/changelogs/unreleased/add-ingress-to-cluster-applications.yml @@ -0,0 +1,5 @@ +--- +title: Add Ingress to available Cluster applications +merge_request: +author: +type: added diff --git a/db/migrate/20171106101200_create_clusters_kubernetes_ingress_apps.rb b/db/migrate/20171106101200_create_clusters_kubernetes_ingress_apps.rb new file mode 100644 index 00000000000..21f48b1d1b4 --- /dev/null +++ b/db/migrate/20171106101200_create_clusters_kubernetes_ingress_apps.rb @@ -0,0 +1,21 @@ +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 diff --git a/db/schema.rb b/db/schema.rb index 82e71a4dfe9..c60cb729b75 100644 --- a/db/schema.rb +++ b/db/schema.rb @@ -11,7 +11,7 @@ # # 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 enable_extension "plpgsql" @@ -528,6 +528,17 @@ ActiveRecord::Schema.define(version: 20171101134435) do t.text "status_reason" 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| t.integer "project_id", null: false t.string "name", null: false diff --git a/spec/factories/clusters/applications/ingress.rb b/spec/factories/clusters/applications/ingress.rb new file mode 100644 index 00000000000..b103a980655 --- /dev/null +++ b/spec/factories/clusters/applications/ingress.rb @@ -0,0 +1,35 @@ +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 diff --git a/spec/features/projects/clusters_spec.rb b/spec/features/projects/clusters_spec.rb index 03d866f8bc1..197e6df4997 100644 --- a/spec/features/projects/clusters_spec.rb +++ b/spec/features/projects/clusters_spec.rb @@ -51,8 +51,10 @@ feature 'Clusters', :js do expect(page).to have_content('Cluster is being created on Google Container Engine...') # Application Installation buttons - 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') + 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').text).to eq('Install') + end Clusters::Cluster.last.provider.make_created! @@ -92,36 +94,78 @@ feature 'Clusters', :js do expect(page.find(:css, '.cluster-name').value).to eq(cluster.name) # Application Installation buttons - 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') + 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')).to have_content('Install') + end end - context 'when user installs application: tiller' do + context 'when user installs application: Helm Tiller' do before do allow(ClusterInstallAppWorker).to receive(:perform_async).and_return(nil) - page.find(:css, '.js-cluster-application-install-button').click + page.within('.js-cluster-application-row-helm') do + page.find(:css, '.js-cluster-application-install-button').click + end end it 'user sees status transition' do - # FE sends request and gets the responce, 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') + 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')).to have_content('Install') + + Clusters::Cluster.last.application_helm.make_installing! - Clusters::Cluster.last.application_helm.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') - # FE starts pooling 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_helm.make_installed! - Clusters::Cluster.last.application_helm.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.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).to have_content('Helm Tiller was successfully installed on your cluster') 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 before do page.find(:css, '.js-toggle-cluster').click diff --git a/spec/javascripts/clusters/components/applications_spec.js b/spec/javascripts/clusters/components/applications_spec.js index 5f59a00dc65..7460da031c4 100644 --- a/spec/javascripts/clusters/components/applications_spec.js +++ b/spec/javascripts/clusters/components/applications_spec.js @@ -29,11 +29,11 @@ describe('Applications', () => { expect(vm.$el.querySelector('.js-cluster-application-row-helm')).toBeDefined(); }); - /* * / it('renders a row for Ingress', () => { expect(vm.$el.querySelector('.js-cluster-application-row-ingress')).toBeDefined(); }); + /* * / it('renders a row for GitLab Runner', () => { expect(vm.$el.querySelector('.js-cluster-application-row-runner')).toBeDefined(); }); diff --git a/spec/models/clusters/applications/ingress_spec.rb b/spec/models/clusters/applications/ingress_spec.rb new file mode 100644 index 00000000000..b83472e1944 --- /dev/null +++ b/spec/models/clusters/applications/ingress_spec.rb @@ -0,0 +1,108 @@ +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 diff --git a/spec/models/clusters/cluster_spec.rb b/spec/models/clusters/cluster_spec.rb index 12123a3d753..b91a5e7a272 100644 --- a/spec/models/clusters/cluster_spec.rb +++ b/spec/models/clusters/cluster_spec.rb @@ -178,4 +178,25 @@ describe Clusters::Cluster do it { is_expected.to be_nil } 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 |