Commit 6a135d60 authored by Alishan Ladhani's avatar Alishan Ladhani

Update cluster applications API to support serverless domains

- Serialize available domains and currently associated domain when
  presenting Knative
- When Knative is updated, create an association to the newly
  selected domain
parent 0b29e5a9
......@@ -41,6 +41,8 @@ module Clusters
scope :for_cluster, -> (cluster) { where(cluster: cluster) }
has_one :pages_domain, through: :serverless_domain_cluster
def chart
'knative/knative'
end
......@@ -49,6 +51,14 @@ module Clusters
{ "domain" => hostname }.to_yaml
end
def available_domains
PagesDomain.instance_serverless
end
def find_available_domain(pages_domain_id)
available_domains.find_by(id: pages_domain_id)
end
def allowed_to_uninstall?
!pre_installed?
end
......
......@@ -13,4 +13,6 @@ class ClusterApplicationEntity < Grape::Entity
expose :modsecurity_enabled, if: -> (e, _) { e.respond_to?(:modsecurity_enabled) }
expose :update_available?, as: :update_available, if: -> (e, _) { e.respond_to?(:update_available?) }
expose :can_uninstall?, as: :can_uninstall
expose :available_domains, using: Serverless::DomainEntity, if: -> (e, _) { e.respond_to?(:available_domains) }
expose :pages_domain, using: Serverless::DomainEntity, if: -> (e, _) { e.respond_to?(:pages_domain) }
end
# frozen_string_literal: true
module Serverless
class DomainEntity < Grape::Entity
expose :id
expose :domain
end
end
......@@ -35,6 +35,12 @@ module Clusters
application.oauth_application = create_oauth_application(application, request)
end
if application.instance_of?(Knative)
Serverless::AssociateDomainService
.new(application, pages_domain_id: params[:pages_domain_id], creator: current_user)
.execute
end
worker = worker_class(application)
application.make_scheduled!
......
# frozen_string_literal: true
module Serverless
class AssociateDomainService
PLACEHOLDER_HOSTNAME = 'example.com'.freeze
def initialize(knative, pages_domain_id:, creator:)
@knative = knative
@pages_domain_id = pages_domain_id
@creator = creator
end
def execute
return if unchanged?
knative.hostname ||= PLACEHOLDER_HOSTNAME
knative.pages_domain = knative.find_available_domain(pages_domain_id)
knative.serverless_domain_cluster.update(creator: creator) if knative.pages_domain
ClusterConfigureIstioWorker.perform_async(knative.cluster_id)
end
private
attr_reader :knative, :pages_domain_id, :creator
def unchanged?
knative.pages_domain&.id == pages_domain_id
end
end
end
......@@ -39,9 +39,15 @@
"stack": { "type": ["string", "null"] },
"modsecurity_enabled": { "type": ["boolean", "null"] },
"update_available": { "type": ["boolean", "null"] },
"can_uninstall": { "type": "boolean" }
"can_uninstall": { "type": "boolean" },
"available_domains": {
"type": "array",
"items": { "$ref": "#/definitions/domain" }
},
"pages_domain": { "type": [ { "$ref": "#/definitions/domain" }, "null"] }
},
"required" : [ "name", "status" ]
}
},
"domain": { "id": "integer", "domain": "string" }
}
}
......@@ -196,4 +196,34 @@ describe Clusters::Applications::Knative do
describe 'validations' do
it { is_expected.to validate_presence_of(:hostname) }
end
describe '#available_domains' do
let!(:domain) { create(:pages_domain, :instance_serverless) }
it 'returns all instance serverless domains' do
expect(PagesDomain).to receive(:instance_serverless).and_call_original
domains = subject.available_domains
expect(domains.length).to eq(1)
expect(domains).to include(domain)
end
end
describe '#find_available_domain' do
let!(:domain) { create(:pages_domain, :instance_serverless) }
it 'returns the domain scoped to available domains' do
expect(subject).to receive(:available_domains).and_call_original
expect(subject.find_available_domain(domain.id)).to eq(domain)
end
end
describe '#pages_domain' do
let!(:sdc) { create(:serverless_domain_cluster, knative: knative) }
it 'returns the the associated pages domain' do
expect(knative.reload.pages_domain).to eq(sdc.pages_domain)
end
end
end
......@@ -59,5 +59,23 @@ describe ClusterApplicationEntity do
expect(subject[:external_ip]).to eq('111.222.111.222')
end
end
context 'for knative application' do
let(:pages_domain) { create(:pages_domain, :instance_serverless) }
let(:application) { build(:clusters_applications_knative, :installed) }
before do
create(:serverless_domain_cluster, knative: application, pages_domain: pages_domain)
end
it 'includes available domains' do
expect(subject[:available_domains].length).to eq(1)
expect(subject[:available_domains].first).to eq(id: pages_domain.id, domain: pages_domain.domain)
end
it 'includes pages_domain' do
expect(subject[:pages_domain]).to eq(id: pages_domain.id, domain: pages_domain.domain)
end
end
end
end
# frozen_string_literal: true
require 'spec_helper'
describe Serverless::DomainEntity do
describe '#as_json' do
let(:domain) { create(:pages_domain, :instance_serverless) }
subject { described_class.new(domain).as_json }
it 'has an id' do
expect(subject[:id]).to eq(domain.id)
end
it 'has a domain' do
expect(subject[:domain]).to eq(domain.domain)
end
end
end
......@@ -137,10 +137,14 @@ describe Clusters::Applications::CreateService do
let(:params) do
{
application: 'knative',
hostname: 'example.com'
hostname: 'example.com',
pages_domain_id: domain.id
}
end
let(:domain) { create(:pages_domain, :instance_serverless) }
let(:associate_domain_service) { double('AssociateDomainService') }
before do
expect_any_instance_of(Clusters::Applications::Knative)
.to receive(:make_scheduled!)
......@@ -158,6 +162,20 @@ describe Clusters::Applications::CreateService do
it 'sets the hostname' do
expect(subject.hostname).to eq('example.com')
end
it 'executes AssociateDomainService' do
expect(Serverless::AssociateDomainService).to receive(:new) do |knative, args|
expect(knative).to be_a(Clusters::Applications::Knative)
expect(args[:pages_domain_id]).to eq(params[:pages_domain_id])
expect(args[:creator]).to eq(user)
associate_domain_service
end
expect(associate_domain_service).to receive(:execute)
subject
end
end
context 'elastic stack application' do
......
......@@ -7,8 +7,9 @@ describe Clusters::Applications::UpdateService do
let(:cluster) { create(:cluster, :project, :provided_by_gcp) }
let(:user) { create(:user) }
let(:params) { { application: 'knative', hostname: 'udpate.example.com' } }
let(:params) { { application: 'knative', hostname: 'update.example.com', pages_domain_id: domain.id } }
let(:service) { described_class.new(cluster, user, params) }
let(:domain) { create(:pages_domain, :instance_serverless) }
subject { service.execute(test_request) }
......@@ -51,6 +52,24 @@ describe Clusters::Applications::UpdateService do
subject
end
context 'knative application' do
let(:associate_domain_service) { double('AssociateDomainService') }
it 'executes AssociateDomainService' do
expect(Serverless::AssociateDomainService).to receive(:new) do |knative, args|
expect(knative.id).to eq(application.id)
expect(args[:pages_domain_id]).to eq(params[:pages_domain_id])
expect(args[:creator]).to eq(user)
associate_domain_service
end
expect(associate_domain_service).to receive(:execute)
subject
end
end
end
context 'application is not schedulable' do
......
# frozen_string_literal: true
require 'spec_helper'
describe Serverless::AssociateDomainService do
subject { described_class.new(knative, pages_domain_id: pages_domain_id, creator: creator) }
let(:sdc) { create(:serverless_domain_cluster, pages_domain: create(:pages_domain, :instance_serverless)) }
let(:knative) { sdc.knative }
let(:creator) { sdc.creator }
let(:pages_domain_id) { sdc.pages_domain_id }
context 'when the domain is unchanged' do
let(:creator) { create(:user) }
it 'does not schedule a ClusterConfigureIstioWorker' do
expect(ClusterConfigureIstioWorker).not_to receive(:perform_async)
subject.execute
end
it 'does not update creator' do
expect { subject.execute }.not_to change { sdc.reload.creator }
end
end
context 'when domain is changed to nil' do
let(:pages_domain_id) { nil }
let(:creator) { create(:user) }
it 'removes the association between knative and the domain' do
expect { subject.execute }.to change { knative.reload.pages_domain }.from(sdc.pages_domain).to(nil)
end
it 'does not attempt to update creator' do
expect { subject.execute }.not_to raise_error
end
it 'schedules a ClusterConfigureIstioWorker for the cluster' do
expect(ClusterConfigureIstioWorker).to receive(:perform_async).with(knative.cluster_id)
subject.execute
end
end
context 'when a new domain is associated' do
let(:pages_domain_id) { create(:pages_domain, :instance_serverless).id }
let(:creator) { create(:user) }
it 'creates an association with the domain' do
expect { subject.execute }.to change { knative.pages_domain.id }.from(sdc.pages_domain.id).to(pages_domain_id)
end
it 'schedules a ClusterConfigureIstioWorker for the cluster' do
expect(ClusterConfigureIstioWorker).to receive(:perform_async).with(knative.cluster_id)
subject.execute
end
it 'updates creator' do
expect { subject.execute }.to change { sdc.reload.creator }.from(sdc.creator).to(creator)
end
end
context 'when knative is not authorized to use the pages domain' do
let(:pages_domain_id) { create(:pages_domain).id }
before do
expect(knative).to receive(:available_domains).and_return(PagesDomain.none)
end
it 'sets pages_domain_id to nil' do
expect { subject.execute }.to change { knative.reload.pages_domain }.from(sdc.pages_domain).to(nil)
end
end
context 'when knative hostname is nil' do
let(:knative) { build(:clusters_applications_knative, hostname: nil) }
it 'sets hostname to a placeholder value' do
expect { subject.execute }.to change { knative.hostname }.to('example.com')
end
end
context 'when knative hostname exists' do
let(:knative) { build(:clusters_applications_knative, hostname: 'hostname.com') }
it 'does not change hostname' do
expect { subject.execute }.not_to change { knative.hostname }
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