Creates Clusterss::ApplciationsController update endpoint
- Creates new route - Creates new controller action - Creates call stack: Clusterss::ApplciationsController calls --> Clusters::Applications::UpdateService calls --> Clusters::Applications::ScheduleUpdateService calls --> ClusterUpdateAppWorker calls --> Clusters::Applications::PatchService --> ClusterWaitForAppInstallationWorker DRY req params Adds gcp_cluster:cluster_update_app queue Schedule_update_service is uneeded Extract common logic to a parent class (UpdateService will need it) Introduce new UpdateService Fix rescue class namespace Fix RuboCop offenses Adds BaseService for create and update services Remove request_handler code duplication Fixes update command Move update_command to ApplicationCore so all apps can use it Adds tests for Knative update_command Adds specs for PatchService Raise error if update receives an unistalled app Adds update_service spec Fix RuboCop offense Use subject in favor of go Adds update endpoint specs for project namespace Adds update endpoint specs for group namespace
This commit is contained in:
parent
cf1b85dd72
commit
f8234d9a08
|
@ -3,26 +3,41 @@
|
|||
class Clusters::ApplicationsController < Clusters::BaseController
|
||||
before_action :cluster
|
||||
before_action :authorize_create_cluster!, only: [:create]
|
||||
before_action :authorize_update_cluster!, only: [:update]
|
||||
|
||||
def create
|
||||
Clusters::Applications::CreateService
|
||||
.new(@cluster, current_user, create_cluster_application_params)
|
||||
.execute(request)
|
||||
request_handler do
|
||||
Clusters::Applications::CreateService
|
||||
.new(@cluster, current_user, cluster_application_params)
|
||||
.execute(request)
|
||||
end
|
||||
end
|
||||
|
||||
def update
|
||||
request_handler do
|
||||
Clusters::Applications::UpdateService
|
||||
.new(@cluster, current_user, cluster_application_params)
|
||||
.execute(request)
|
||||
end
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def request_handler
|
||||
yield
|
||||
|
||||
head :no_content
|
||||
rescue Clusters::Applications::CreateService::InvalidApplicationError
|
||||
rescue Clusters::Applications::BaseService::InvalidApplicationError
|
||||
render_404
|
||||
rescue StandardError
|
||||
head :bad_request
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def cluster
|
||||
@cluster ||= clusterable.clusters.find(params[:id]) || render_404
|
||||
end
|
||||
|
||||
def create_cluster_application_params
|
||||
def cluster_application_params
|
||||
params.permit(:application, :hostname, :email)
|
||||
end
|
||||
end
|
||||
|
|
|
@ -30,6 +30,12 @@ module Clusters
|
|||
# Override if you need extra data synchronized
|
||||
# from K8s after installation
|
||||
end
|
||||
|
||||
def update_command
|
||||
command = install_command
|
||||
command.version = version
|
||||
command
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -46,6 +46,10 @@ module Clusters
|
|||
@install_command ||= app.install_command
|
||||
end
|
||||
|
||||
def update_command
|
||||
@update_command ||= app.update_command
|
||||
end
|
||||
|
||||
def upgrade_command(new_values = "")
|
||||
app.upgrade_command(new_values)
|
||||
end
|
||||
|
|
|
@ -0,0 +1,76 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
module Clusters
|
||||
module Applications
|
||||
class BaseService
|
||||
InvalidApplicationError = Class.new(StandardError)
|
||||
|
||||
attr_reader :cluster, :current_user, :params
|
||||
|
||||
def initialize(cluster, user, params = {})
|
||||
@cluster = cluster
|
||||
@current_user = user
|
||||
@params = params.dup
|
||||
end
|
||||
|
||||
def execute(request)
|
||||
instantiate_application.tap do |application|
|
||||
if application.has_attribute?(:hostname)
|
||||
application.hostname = params[:hostname]
|
||||
end
|
||||
|
||||
if application.has_attribute?(:email)
|
||||
application.email = params[:email]
|
||||
end
|
||||
|
||||
if application.respond_to?(:oauth_application)
|
||||
application.oauth_application = create_oauth_application(application, request)
|
||||
end
|
||||
|
||||
worker = worker_class(application)
|
||||
|
||||
application.make_scheduled!
|
||||
|
||||
worker.perform_async(application.name, application.id)
|
||||
end
|
||||
end
|
||||
|
||||
protected
|
||||
|
||||
def worker_class(application)
|
||||
raise NotImplementedError
|
||||
end
|
||||
|
||||
def builders
|
||||
raise NotImplementedError
|
||||
end
|
||||
|
||||
def project_builders
|
||||
raise NotImplementedError
|
||||
end
|
||||
|
||||
def instantiate_application
|
||||
builder.call(@cluster) || raise(InvalidApplicationError, "invalid application: #{application_name}")
|
||||
end
|
||||
|
||||
def builder
|
||||
builders[application_name] || raise(InvalidApplicationError, "invalid application: #{application_name}")
|
||||
end
|
||||
|
||||
def application_name
|
||||
params[:application]
|
||||
end
|
||||
|
||||
def create_oauth_application(application, request)
|
||||
oauth_application_params = {
|
||||
name: params[:application],
|
||||
redirect_uri: application.callback_url,
|
||||
scopes: 'api read_user openid',
|
||||
owner: current_user
|
||||
}
|
||||
|
||||
::Applications::CreateService.new(current_user, oauth_application_params).execute(request)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
|
@ -2,47 +2,11 @@
|
|||
|
||||
module Clusters
|
||||
module Applications
|
||||
class CreateService
|
||||
InvalidApplicationError = Class.new(StandardError)
|
||||
|
||||
attr_reader :cluster, :current_user, :params
|
||||
|
||||
def initialize(cluster, user, params = {})
|
||||
@cluster = cluster
|
||||
@current_user = user
|
||||
@params = params.dup
|
||||
end
|
||||
|
||||
def execute(request)
|
||||
create_application.tap do |application|
|
||||
if application.has_attribute?(:hostname)
|
||||
application.hostname = params[:hostname]
|
||||
end
|
||||
|
||||
if application.has_attribute?(:email)
|
||||
application.email = params[:email]
|
||||
end
|
||||
|
||||
if application.respond_to?(:oauth_application)
|
||||
application.oauth_application = create_oauth_application(application, request)
|
||||
end
|
||||
|
||||
worker = application.updateable? ? ClusterUpgradeAppWorker : ClusterInstallAppWorker
|
||||
|
||||
application.make_scheduled!
|
||||
|
||||
worker.perform_async(application.name, application.id)
|
||||
end
|
||||
end
|
||||
|
||||
class CreateService < Clusters::Applications::BaseService
|
||||
private
|
||||
|
||||
def create_application
|
||||
builder.call(@cluster)
|
||||
end
|
||||
|
||||
def builder
|
||||
builders[application_name] || raise(InvalidApplicationError, "invalid application: #{application_name}")
|
||||
def worker_class(application)
|
||||
application.updateable? ? ClusterUpgradeAppWorker : ClusterInstallAppWorker
|
||||
end
|
||||
|
||||
def builders
|
||||
|
@ -65,21 +29,6 @@ module Clusters
|
|||
"knative" => -> (cluster) { cluster.application_knative || cluster.build_application_knative }
|
||||
}
|
||||
end
|
||||
|
||||
def application_name
|
||||
params[:application]
|
||||
end
|
||||
|
||||
def create_oauth_application(application, request)
|
||||
oauth_application_params = {
|
||||
name: params[:application],
|
||||
redirect_uri: application.callback_url,
|
||||
scopes: 'api read_user openid',
|
||||
owner: current_user
|
||||
}
|
||||
|
||||
::Applications::CreateService.new(current_user, oauth_application_params).execute(request)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -0,0 +1,26 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
module Clusters
|
||||
module Applications
|
||||
class PatchService < BaseHelmService
|
||||
def execute
|
||||
return unless app.scheduled?
|
||||
|
||||
begin
|
||||
app.make_updating!
|
||||
|
||||
helm_api.update(update_command)
|
||||
|
||||
ClusterWaitForAppInstallationWorker.perform_in(
|
||||
ClusterWaitForAppInstallationWorker::INTERVAL, app.name, app.id)
|
||||
rescue Kubeclient::HttpError => e
|
||||
log_error(e)
|
||||
app.make_update_errored!("Kubernetes error: #{e.error_code}")
|
||||
rescue StandardError => e
|
||||
log_error(e)
|
||||
app.make_update_errored!("Can't start update process.")
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
|
@ -0,0 +1,34 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
module Clusters
|
||||
module Applications
|
||||
class UpdateService < Clusters::Applications::BaseService
|
||||
private
|
||||
|
||||
def worker_class(application)
|
||||
ClusterUpdateAppWorker
|
||||
end
|
||||
|
||||
def builders
|
||||
{
|
||||
"helm" => -> (cluster) { cluster.application_helm },
|
||||
"ingress" => -> (cluster) { cluster.application_ingress },
|
||||
"cert_manager" => -> (cluster) { cluster.application_cert_manager }
|
||||
}.tap do |hash|
|
||||
hash.merge!(project_builders) if cluster.project_type?
|
||||
end
|
||||
end
|
||||
|
||||
# These applications will need extra configuration to enable them to work
|
||||
# with groups of projects
|
||||
def project_builders
|
||||
{
|
||||
"prometheus" => -> (cluster) { cluster.application_prometheus },
|
||||
"runner" => -> (cluster) { cluster.application_runner },
|
||||
"jupyter" => -> (cluster) { cluster.application_jupyter },
|
||||
"knative" => -> (cluster) { cluster.application_knative }
|
||||
}
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
|
@ -23,6 +23,7 @@
|
|||
- cronjob:prune_web_hook_logs
|
||||
|
||||
- gcp_cluster:cluster_install_app
|
||||
- gcp_cluster:cluster_update_app
|
||||
- gcp_cluster:cluster_upgrade_app
|
||||
- gcp_cluster:cluster_provision
|
||||
- gcp_cluster:cluster_wait_for_app_installation
|
||||
|
|
|
@ -0,0 +1,13 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
class ClusterUpdateAppWorker
|
||||
include ApplicationWorker
|
||||
include ClusterQueue
|
||||
include ClusterApplications
|
||||
|
||||
def perform(app_name, app_id)
|
||||
find_application(app_name, app_id) do |app|
|
||||
Clusters::Applications::PatchService.new(app).execute
|
||||
end
|
||||
end
|
||||
end
|
|
@ -101,6 +101,7 @@ Rails.application.routes.draw do
|
|||
member do
|
||||
scope :applications do
|
||||
post '/:application', to: 'clusters/applications#create', as: :install_applications
|
||||
patch '/:application', to: 'clusters/applications#update', as: :update_applications
|
||||
end
|
||||
|
||||
get :cluster_status, format: :json
|
||||
|
|
|
@ -7,7 +7,8 @@ module Gitlab
|
|||
include BaseCommand
|
||||
include ClientCommand
|
||||
|
||||
attr_reader :name, :files, :chart, :version, :repository, :preinstall, :postinstall
|
||||
attr_reader :name, :files, :chart, :repository, :preinstall, :postinstall
|
||||
attr_accessor :version
|
||||
|
||||
def initialize(name:, chart:, files:, rbac:, version: nil, repository: nil, preinstall: nil, postinstall: nil)
|
||||
@name = name
|
||||
|
|
|
@ -9,9 +9,25 @@ describe Groups::Clusters::ApplicationsController do
|
|||
Clusters::Cluster::APPLICATIONS[application]
|
||||
end
|
||||
|
||||
shared_examples 'a secure endpoint' do
|
||||
it { expect { subject }.to be_allowed_for(:admin) }
|
||||
it { expect { subject }.to be_allowed_for(:owner).of(group) }
|
||||
it { expect { subject }.to be_allowed_for(:maintainer).of(group) }
|
||||
it { expect { subject }.to be_denied_for(:developer).of(group) }
|
||||
it { expect { subject }.to be_denied_for(:reporter).of(group) }
|
||||
it { expect { subject }.to be_denied_for(:guest).of(group) }
|
||||
it { expect { subject }.to be_denied_for(:user) }
|
||||
it { expect { subject }.to be_denied_for(:external) }
|
||||
end
|
||||
|
||||
let(:cluster) { create(:cluster, :group, :provided_by_gcp) }
|
||||
let(:group) { cluster.group }
|
||||
|
||||
describe 'POST create' do
|
||||
let(:cluster) { create(:cluster, :group, :provided_by_gcp) }
|
||||
let(:group) { cluster.group }
|
||||
subject do
|
||||
post :create, params: params.merge(group_id: group)
|
||||
end
|
||||
|
||||
let(:application) { 'helm' }
|
||||
let(:params) { { application: application, id: cluster.id } }
|
||||
|
||||
|
@ -26,7 +42,7 @@ describe Groups::Clusters::ApplicationsController do
|
|||
it 'schedule an application installation' do
|
||||
expect(ClusterInstallAppWorker).to receive(:perform_async).with(application, anything).once
|
||||
|
||||
expect { go }.to change { current_application.count }
|
||||
expect { subject }.to change { current_application.count }
|
||||
expect(response).to have_http_status(:no_content)
|
||||
expect(cluster.application_helm).to be_scheduled
|
||||
end
|
||||
|
@ -37,7 +53,7 @@ describe Groups::Clusters::ApplicationsController do
|
|||
end
|
||||
|
||||
it 'return 404' do
|
||||
expect { go }.not_to change { current_application.count }
|
||||
expect { subject }.not_to change { current_application.count }
|
||||
expect(response).to have_http_status(:not_found)
|
||||
end
|
||||
end
|
||||
|
@ -46,9 +62,7 @@ describe Groups::Clusters::ApplicationsController do
|
|||
let(:application) { 'unkwnown-app' }
|
||||
|
||||
it 'return 404' do
|
||||
go
|
||||
|
||||
expect(response).to have_http_status(:not_found)
|
||||
is_expected.to have_http_status(:not_found)
|
||||
end
|
||||
end
|
||||
|
||||
|
@ -58,9 +72,7 @@ describe Groups::Clusters::ApplicationsController do
|
|||
end
|
||||
|
||||
it 'returns 400' do
|
||||
go
|
||||
|
||||
expect(response).to have_http_status(:bad_request)
|
||||
is_expected.to have_http_status(:bad_request)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
@ -70,18 +82,66 @@ describe Groups::Clusters::ApplicationsController do
|
|||
allow(ClusterInstallAppWorker).to receive(:perform_async)
|
||||
end
|
||||
|
||||
it { expect { go }.to be_allowed_for(:admin) }
|
||||
it { expect { go }.to be_allowed_for(:owner).of(group) }
|
||||
it { expect { go }.to be_allowed_for(:maintainer).of(group) }
|
||||
it { expect { go }.to be_denied_for(:developer).of(group) }
|
||||
it { expect { go }.to be_denied_for(:reporter).of(group) }
|
||||
it { expect { go }.to be_denied_for(:guest).of(group) }
|
||||
it { expect { go }.to be_denied_for(:user) }
|
||||
it { expect { go }.to be_denied_for(:external) }
|
||||
it_behaves_like 'a secure endpoint'
|
||||
end
|
||||
end
|
||||
|
||||
describe 'PATCH update' do
|
||||
subject do
|
||||
patch :update, params: params.merge(group_id: group)
|
||||
end
|
||||
|
||||
def go
|
||||
post :create, params: params.merge(group_id: group)
|
||||
let!(:application) { create(:clusters_applications_cert_managers, :installed, cluster: cluster) }
|
||||
let(:application_name) { application.name }
|
||||
let(:params) { { application: application_name, id: cluster.id, email: "new-email@example.com" } }
|
||||
|
||||
describe 'functionality' do
|
||||
let(:user) { create(:user) }
|
||||
|
||||
before do
|
||||
group.add_maintainer(user)
|
||||
sign_in(user)
|
||||
end
|
||||
|
||||
context "when cluster and app exists" do
|
||||
it "schedules an application update" do
|
||||
expect(ClusterUpdateAppWorker).to receive(:perform_async).with(application.name, anything).once
|
||||
|
||||
is_expected.to have_http_status(:no_content)
|
||||
|
||||
expect(cluster.application_cert_manager).to be_scheduled
|
||||
end
|
||||
end
|
||||
|
||||
context 'when cluster do not exists' do
|
||||
before do
|
||||
cluster.destroy!
|
||||
end
|
||||
|
||||
it { is_expected.to have_http_status(:not_found) }
|
||||
end
|
||||
|
||||
context 'when application is unknown' do
|
||||
let(:application_name) { 'unkwnown-app' }
|
||||
|
||||
it { is_expected.to have_http_status(:not_found) }
|
||||
end
|
||||
|
||||
context 'when application is already scheduled' do
|
||||
before do
|
||||
application.make_scheduled!
|
||||
end
|
||||
|
||||
it { is_expected.to have_http_status(:bad_request) }
|
||||
end
|
||||
end
|
||||
|
||||
describe 'security' do
|
||||
before do
|
||||
allow(ClusterUpdateAppWorker).to receive(:perform_async)
|
||||
end
|
||||
|
||||
it_behaves_like 'a secure endpoint'
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -9,7 +9,22 @@ describe Projects::Clusters::ApplicationsController do
|
|||
Clusters::Cluster::APPLICATIONS[application]
|
||||
end
|
||||
|
||||
shared_examples 'a secure endpoint' do
|
||||
it { expect { subject }.to be_allowed_for(:admin) }
|
||||
it { expect { subject }.to be_allowed_for(:owner).of(project) }
|
||||
it { expect { subject }.to be_allowed_for(:maintainer).of(project) }
|
||||
it { expect { subject }.to be_denied_for(:developer).of(project) }
|
||||
it { expect { subject }.to be_denied_for(:reporter).of(project) }
|
||||
it { expect { subject }.to be_denied_for(:guest).of(project) }
|
||||
it { expect { subject }.to be_denied_for(:user) }
|
||||
it { expect { subject }.to be_denied_for(:external) }
|
||||
end
|
||||
|
||||
describe 'POST create' do
|
||||
subject do
|
||||
post :create, params: params.merge(namespace_id: project.namespace, project_id: project)
|
||||
end
|
||||
|
||||
let(:cluster) { create(:cluster, :project, :provided_by_gcp) }
|
||||
let(:project) { cluster.project }
|
||||
let(:application) { 'helm' }
|
||||
|
@ -26,7 +41,7 @@ describe Projects::Clusters::ApplicationsController do
|
|||
it 'schedule an application installation' do
|
||||
expect(ClusterInstallAppWorker).to receive(:perform_async).with(application, anything).once
|
||||
|
||||
expect { go }.to change { current_application.count }
|
||||
expect { subject }.to change { current_application.count }
|
||||
expect(response).to have_http_status(:no_content)
|
||||
expect(cluster.application_helm).to be_scheduled
|
||||
end
|
||||
|
@ -37,7 +52,7 @@ describe Projects::Clusters::ApplicationsController do
|
|||
end
|
||||
|
||||
it 'return 404' do
|
||||
expect { go }.not_to change { current_application.count }
|
||||
expect { subject }.not_to change { current_application.count }
|
||||
expect(response).to have_http_status(:not_found)
|
||||
end
|
||||
end
|
||||
|
@ -46,9 +61,7 @@ describe Projects::Clusters::ApplicationsController do
|
|||
let(:application) { 'unkwnown-app' }
|
||||
|
||||
it 'return 404' do
|
||||
go
|
||||
|
||||
expect(response).to have_http_status(:not_found)
|
||||
is_expected.to have_http_status(:not_found)
|
||||
end
|
||||
end
|
||||
|
||||
|
@ -58,9 +71,7 @@ describe Projects::Clusters::ApplicationsController do
|
|||
end
|
||||
|
||||
it 'returns 400' do
|
||||
go
|
||||
|
||||
expect(response).to have_http_status(:bad_request)
|
||||
is_expected.to have_http_status(:bad_request)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
@ -70,18 +81,68 @@ describe Projects::Clusters::ApplicationsController do
|
|||
allow(ClusterInstallAppWorker).to receive(:perform_async)
|
||||
end
|
||||
|
||||
it { expect { go }.to be_allowed_for(:admin) }
|
||||
it { expect { go }.to be_allowed_for(:owner).of(project) }
|
||||
it { expect { go }.to be_allowed_for(:maintainer).of(project) }
|
||||
it { expect { go }.to be_denied_for(:developer).of(project) }
|
||||
it { expect { go }.to be_denied_for(:reporter).of(project) }
|
||||
it { expect { go }.to be_denied_for(:guest).of(project) }
|
||||
it { expect { go }.to be_denied_for(:user) }
|
||||
it { expect { go }.to be_denied_for(:external) }
|
||||
it_behaves_like 'a secure endpoint'
|
||||
end
|
||||
end
|
||||
|
||||
describe 'PATCH update' do
|
||||
subject do
|
||||
patch :update, params: params.merge(namespace_id: project.namespace, project_id: project)
|
||||
end
|
||||
|
||||
def go
|
||||
post :create, params: params.merge(namespace_id: project.namespace, project_id: project)
|
||||
let(:cluster) { create(:cluster, :project, :provided_by_gcp) }
|
||||
let(:project) { cluster.project }
|
||||
let!(:application) { create(:clusters_applications_knative, :installed, cluster: cluster) }
|
||||
let(:application_name) { application.name }
|
||||
let(:params) { { application: application_name, id: cluster.id, hostname: "new.example.com" } }
|
||||
|
||||
describe 'functionality' do
|
||||
let(:user) { create(:user) }
|
||||
|
||||
before do
|
||||
project.add_maintainer(user)
|
||||
sign_in(user)
|
||||
end
|
||||
|
||||
context "when cluster and app exists" do
|
||||
it "schedules an application update" do
|
||||
expect(ClusterUpdateAppWorker).to receive(:perform_async).with(application.name, anything).once
|
||||
|
||||
is_expected.to have_http_status(:no_content)
|
||||
|
||||
expect(cluster.application_knative).to be_scheduled
|
||||
end
|
||||
end
|
||||
|
||||
context 'when cluster do not exists' do
|
||||
before do
|
||||
cluster.destroy!
|
||||
end
|
||||
|
||||
it { is_expected.to have_http_status(:not_found) }
|
||||
end
|
||||
|
||||
context 'when application is unknown' do
|
||||
let(:application_name) { 'unkwnown-app' }
|
||||
|
||||
it { is_expected.to have_http_status(:not_found) }
|
||||
end
|
||||
|
||||
context 'when application is already scheduled' do
|
||||
before do
|
||||
application.make_scheduled!
|
||||
end
|
||||
|
||||
it { is_expected.to have_http_status(:bad_request) }
|
||||
end
|
||||
end
|
||||
|
||||
describe 'security' do
|
||||
before do
|
||||
allow(ClusterUpdateAppWorker).to receive(:perform_async)
|
||||
end
|
||||
|
||||
it_behaves_like 'a secure endpoint'
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -66,9 +66,7 @@ describe Clusters::Applications::Knative do
|
|||
end
|
||||
end
|
||||
|
||||
describe '#install_command' do
|
||||
subject { knative.install_command }
|
||||
|
||||
shared_examples 'a command' do
|
||||
it 'should be an instance of Helm::InstallCommand' do
|
||||
expect(subject).to be_an_instance_of(Gitlab::Kubernetes::Helm::InstallCommand)
|
||||
end
|
||||
|
@ -76,7 +74,6 @@ describe Clusters::Applications::Knative do
|
|||
it 'should be initialized with knative arguments' do
|
||||
expect(subject.name).to eq('knative')
|
||||
expect(subject.chart).to eq('knative/knative')
|
||||
expect(subject.version).to eq('0.2.2')
|
||||
expect(subject.files).to eq(knative.files)
|
||||
end
|
||||
|
||||
|
@ -98,6 +95,27 @@ describe Clusters::Applications::Knative do
|
|||
end
|
||||
end
|
||||
|
||||
describe '#install_command' do
|
||||
subject { knative.install_command }
|
||||
|
||||
it 'should be initialized with latest version' do
|
||||
expect(subject.version).to eq('0.2.2')
|
||||
end
|
||||
|
||||
it_behaves_like 'a command'
|
||||
end
|
||||
|
||||
describe '#update_command' do
|
||||
let!(:current_installed_version) { knative.version = '0.1.0' }
|
||||
subject { knative.update_command }
|
||||
|
||||
it 'should be initialized with current version' do
|
||||
expect(subject.version).to eq(current_installed_version)
|
||||
end
|
||||
|
||||
it_behaves_like 'a command'
|
||||
end
|
||||
|
||||
describe '#files' do
|
||||
let(:application) { knative }
|
||||
let(:values) { subject[:'values.yaml'] }
|
||||
|
|
|
@ -0,0 +1,128 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
require 'spec_helper'
|
||||
|
||||
describe Clusters::Applications::PatchService do
|
||||
describe '#execute' do
|
||||
let(:application) { create(:clusters_applications_knative, :scheduled) }
|
||||
let!(:update_command) { application.update_command }
|
||||
let(:service) { described_class.new(application) }
|
||||
let(:helm_client) { instance_double(Gitlab::Kubernetes::Helm::Api) }
|
||||
|
||||
before do
|
||||
allow(service).to receive(:update_command).and_return(update_command)
|
||||
allow(service).to receive(:helm_api).and_return(helm_client)
|
||||
end
|
||||
|
||||
context 'when there are no errors' do
|
||||
before do
|
||||
expect(helm_client).to receive(:update).with(update_command)
|
||||
allow(ClusterWaitForAppInstallationWorker).to receive(:perform_in).and_return(nil)
|
||||
end
|
||||
|
||||
it 'make the application updating' do
|
||||
expect(application.cluster).not_to be_nil
|
||||
service.execute
|
||||
|
||||
expect(application).to be_updating
|
||||
end
|
||||
|
||||
it 'schedule async installation status check' do
|
||||
expect(ClusterWaitForAppInstallationWorker).to receive(:perform_in).once
|
||||
|
||||
service.execute
|
||||
end
|
||||
end
|
||||
|
||||
context 'when kubernetes cluster communication fails' do
|
||||
let(:error) { Kubeclient::HttpError.new(500, 'system failure', nil) }
|
||||
|
||||
before do
|
||||
expect(helm_client).to receive(:update).with(update_command).and_raise(error)
|
||||
end
|
||||
|
||||
it 'make the application errored' do
|
||||
service.execute
|
||||
|
||||
expect(application).to be_update_errored
|
||||
expect(application.status_reason).to match('Kubernetes error: 500')
|
||||
end
|
||||
|
||||
it 'logs errors' do
|
||||
expect(service.send(:logger)).to receive(:error).with(
|
||||
{
|
||||
exception: 'Kubeclient::HttpError',
|
||||
message: 'system failure',
|
||||
service: 'Clusters::Applications::PatchService',
|
||||
app_id: application.id,
|
||||
project_ids: application.cluster.project_ids,
|
||||
group_ids: [],
|
||||
error_code: 500
|
||||
}
|
||||
)
|
||||
|
||||
expect(Gitlab::Sentry).to receive(:track_acceptable_exception).with(
|
||||
error,
|
||||
extra: {
|
||||
exception: 'Kubeclient::HttpError',
|
||||
message: 'system failure',
|
||||
service: 'Clusters::Applications::PatchService',
|
||||
app_id: application.id,
|
||||
project_ids: application.cluster.project_ids,
|
||||
group_ids: [],
|
||||
error_code: 500
|
||||
}
|
||||
)
|
||||
|
||||
service.execute
|
||||
end
|
||||
end
|
||||
|
||||
context 'a non kubernetes error happens' do
|
||||
let(:application) { create(:clusters_applications_knative, :scheduled) }
|
||||
let(:error) { StandardError.new('something bad happened') }
|
||||
|
||||
before do
|
||||
expect(application).to receive(:make_updating!).once.and_raise(error)
|
||||
end
|
||||
|
||||
it 'make the application errored' do
|
||||
expect(helm_client).not_to receive(:update)
|
||||
|
||||
service.execute
|
||||
|
||||
expect(application).to be_update_errored
|
||||
expect(application.status_reason).to eq("Can't start update process.")
|
||||
end
|
||||
|
||||
it 'logs errors' do
|
||||
expect(service.send(:logger)).to receive(:error).with(
|
||||
{
|
||||
exception: 'StandardError',
|
||||
error_code: nil,
|
||||
message: 'something bad happened',
|
||||
service: 'Clusters::Applications::PatchService',
|
||||
app_id: application.id,
|
||||
project_ids: application.cluster.projects.pluck(:id),
|
||||
group_ids: []
|
||||
}
|
||||
)
|
||||
|
||||
expect(Gitlab::Sentry).to receive(:track_acceptable_exception).with(
|
||||
error,
|
||||
extra: {
|
||||
exception: 'StandardError',
|
||||
error_code: nil,
|
||||
message: 'something bad happened',
|
||||
service: 'Clusters::Applications::PatchService',
|
||||
app_id: application.id,
|
||||
project_ids: application.cluster.projects.pluck(:id),
|
||||
group_ids: []
|
||||
}
|
||||
)
|
||||
|
||||
service.execute
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
|
@ -0,0 +1,72 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
require 'spec_helper'
|
||||
|
||||
describe Clusters::Applications::UpdateService do
|
||||
include TestRequestHelpers
|
||||
|
||||
let(:cluster) { create(:cluster, :project, :provided_by_gcp) }
|
||||
let(:user) { create(:user) }
|
||||
let(:params) { { application: 'knative', hostname: 'udpate.example.com' } }
|
||||
let(:service) { described_class.new(cluster, user, params) }
|
||||
|
||||
subject { service.execute(test_request) }
|
||||
|
||||
describe '#execute' do
|
||||
before do
|
||||
allow(ClusterUpdateAppWorker).to receive(:perform_async)
|
||||
end
|
||||
|
||||
context 'application is not installed' do
|
||||
it 'raises Clusters::Applications::BaseService::InvalidApplicationError' do
|
||||
expect(ClusterUpdateAppWorker).not_to receive(:perform_async)
|
||||
|
||||
expect { subject }
|
||||
.to raise_exception { Clusters::Applications::BaseService::InvalidApplicationError }
|
||||
.and not_change { Clusters::Applications::Knative.count }
|
||||
.and not_change { Clusters::Applications::Knative.with_status(:scheduled).count }
|
||||
end
|
||||
end
|
||||
|
||||
context 'application is installed' do
|
||||
context 'application is schedulable' do
|
||||
let!(:application) do
|
||||
create(:clusters_applications_knative, status: 3, cluster: cluster)
|
||||
end
|
||||
|
||||
it 'updates the application data' do
|
||||
expect do
|
||||
subject
|
||||
end.to change { application.reload.hostname }.to(params[:hostname])
|
||||
end
|
||||
|
||||
it 'makes application scheduled!' do
|
||||
subject
|
||||
|
||||
expect(application.reload).to be_scheduled
|
||||
end
|
||||
|
||||
it 'schedules ClusterUpdateAppWorker' do
|
||||
expect(ClusterUpdateAppWorker).to receive(:perform_async)
|
||||
|
||||
subject
|
||||
end
|
||||
end
|
||||
|
||||
context 'application is not schedulable' do
|
||||
let!(:application) do
|
||||
create(:clusters_applications_knative, status: 4, cluster: cluster)
|
||||
end
|
||||
|
||||
it 'raises StateMachines::InvalidTransition' do
|
||||
expect(ClusterUpdateAppWorker).not_to receive(:perform_async)
|
||||
|
||||
expect { subject }
|
||||
.to raise_exception { StateMachines::InvalidTransition }
|
||||
.and not_change { application.reload.hostname }
|
||||
.and not_change { Clusters::Applications::Knative.with_status(:scheduled).count }
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
Loading…
Reference in New Issue