diff --git a/app/models/district.rb b/app/models/district.rb index 3c92f70b..92e15101 100644 --- a/app/models/district.rb +++ b/app/models/district.rb @@ -12,6 +12,9 @@ class District < ActiveRecord::Base has_many :endpoints, inverse_of: :district, dependent: :destroy has_many :notifications, inverse_of: :district, dependent: :destroy + has_many :services, through: :heritages + has_many :service_deployments, through: :services + validates :name, presence: true, uniqueness: true, immutable: true validates :region, :s3_bucket_name, :stack_name, :cidr_block, presence: true, immutable: true validates :nat_type, inclusion: {in: %w[instance managed_gateway managed_gateway_multi_az]}, allow_nil: true diff --git a/app/models/service.rb b/app/models/service.rb index d26cc44b..c1e2ca88 100644 --- a/app/models/service.rb +++ b/app/models/service.rb @@ -5,6 +5,7 @@ class Service < ActiveRecord::Base belongs_to :heritage, inverse_of: :services has_many :listeners, inverse_of: :service, dependent: :destroy has_many :port_mappings, inverse_of: :service, dependent: :destroy + has_many :service_deployments, inverse_of: :service, dependent: :destroy serialize :hosts, JSON serialize :health_check, JSON diff --git a/app/models/service_deployment.rb b/app/models/service_deployment.rb new file mode 100644 index 00000000..881eac7d --- /dev/null +++ b/app/models/service_deployment.rb @@ -0,0 +1,37 @@ +class ServiceDeployment < ApplicationRecord + belongs_to :service + + validates :service, presence: true + validate :finished_cannot_both_be_true + + def finished_cannot_both_be_true + if completed_at.present? && failed_at.present? + errors.add(:completed_at, "can't be true with failed_at") + end + end + + scope :unfinished, -> { where('completed_at is null and failed_at is null') } + + def finished? + completed? || failed? + end + + def completed? + !completed_at.nil? + end + + def failed? + !failed_at.nil? + end + + def complete! + self.completed_at = Time.now + self.save! + end + + def fail! + self.failed_at = Time.now + self.save! + end + +end diff --git a/db/migrate/20221208045503_create_service_deployments.rb b/db/migrate/20221208045503_create_service_deployments.rb new file mode 100644 index 00000000..79e3e025 --- /dev/null +++ b/db/migrate/20221208045503_create_service_deployments.rb @@ -0,0 +1,13 @@ +class CreateServiceDeployments < ActiveRecord::Migration[5.2] + def change + create_table :service_deployments do |t| + t.references :service, foreign_key: true, null: false + t.datetime :completed_at + t.datetime :failed_at + + t.timestamps + end + + add_index :service_deployments, [:completed_at, :failed_at] + end +end diff --git a/db/schema.rb b/db/schema.rb index badcc818..355dc116 100644 --- a/db/schema.rb +++ b/db/schema.rb @@ -10,7 +10,7 @@ # # It's strongly recommended that you check this file into your version control system. -ActiveRecord::Schema.define(version: 2020_11_13_090000) do +ActiveRecord::Schema.define(version: 2022_12_08_045503) do # These are extensions that must be enabled in order to support this database enable_extension "plpgsql" @@ -182,6 +182,16 @@ t.index ["token"], name: "index_review_groups_on_token", unique: true end + create_table "service_deployments", force: :cascade do |t| + t.bigint "service_id", null: false + t.datetime "completed_at" + t.datetime "failed_at" + t.datetime "created_at", null: false + t.datetime "updated_at", null: false + t.index ["completed_at", "failed_at"], name: "index_service_deployments_on_completed_at_and_failed_at" + t.index ["service_id"], name: "index_service_deployments_on_service_id" + end + create_table "services", force: :cascade do |t| t.string "name", null: false t.integer "cpu" @@ -236,6 +246,7 @@ add_foreign_key "review_apps", "heritages" add_foreign_key "review_apps", "review_groups" add_foreign_key "review_groups", "endpoints" + add_foreign_key "service_deployments", "services" add_foreign_key "services", "heritages" add_foreign_key "users_districts", "districts" add_foreign_key "users_districts", "users" diff --git a/spec/factories/service_deployments.rb b/spec/factories/service_deployments.rb new file mode 100644 index 00000000..50c15b37 --- /dev/null +++ b/spec/factories/service_deployments.rb @@ -0,0 +1,5 @@ +FactoryBot.define do + factory :service_deployment do + service + end +end diff --git a/spec/models/district_spec.rb b/spec/models/district_spec.rb index 2991c7bf..5c0fab47 100644 --- a/spec/models/district_spec.rb +++ b/spec/models/district_spec.rb @@ -148,4 +148,61 @@ district.publish_sns("message") end end + + describe '#services' do + it 'finds the services for the current district' do + d1 = create :district, name: 'd1' + d1h1 = create :heritage, district: d1, name: 'd1h1' + d1h1s1 = create :service, heritage: d1h1, name: 'd1h1s1' + d1h1s2 = create :service, heritage: d1h1, name: 'd1h1s2' + d1h1s3 = create :service, heritage: d1h1, name: 'd1h1s3' + + d1h2 = create :heritage, district: d1, name: 'd1h2' + d1h2s1 = create :service, heritage: d1h2, name: 'd1h2s1' + d1h2s2 = create :service, heritage: d1h2, name: 'd1h2s2' + + d2 = create :district, name: 'd2' + d2h1 = create :heritage, district: d2, name: 'd2h1' + d2h1s1 = create :service, heritage: d2h1, name: 'd2h1s1' + d2h1s2 = create :service, heritage: d2h1, name: 'd2h1s2' + + d2h2 = create :heritage, district: d2, name: 'd2h2' + d2h2s1 = create :service, heritage: d2h2, name: 'd2h2s1' + d2h2s2 = create :service, heritage: d2h2, name: 'd2h2s2' + + expect(d1.services).to eq [d1h1s1, d1h1s2, d1h1s3, d1h2s1, d1h2s2] + expect(d2.services).to eq [d2h1s1, d2h1s2, d2h2s1, d2h2s2] + end + end + + describe '#service_deployments' do + it 'finds the service deployments for the current district' do + d1 = create :district, name: 'd1' + d1h1 = create :heritage, district: d1, name: 'd1h1' + d1h1s1 = create :service, heritage: d1h1, name: 'd1h1s1' + + dep1 = create :service_deployment, service: d1h1s1 + + d1h1s2 = create :service, heritage: d1h1, name: 'd1h1s2' + d1h1s3 = create :service, heritage: d1h1, name: 'd1h1s3' + + dep2 = create :service_deployment, service: d1h1s3 + + d1h2 = create :heritage, district: d1, name: 'd1h2' + d1h2s1 = create :service, heritage: d1h2, name: 'd1h2s1' + d1h2s2 = create :service, heritage: d1h2, name: 'd1h2s2' + + d2 = create :district, name: 'd2' + d2h1 = create :heritage, district: d2, name: 'd2h1' + d2h1s1 = create :service, heritage: d2h1, name: 'd2h1s1' + d2h1s2 = create :service, heritage: d2h1, name: 'd2h1s2' + + d2h2 = create :heritage, district: d2, name: 'd2h2' + d2h2s1 = create :service, heritage: d2h2, name: 'd2h2s1' + d2h2s2 = create :service, heritage: d2h2, name: 'd2h2s2' + + expect(d1.service_deployments).to eq [dep1, dep2] + end + end + end diff --git a/spec/models/service_deployment_spec.rb b/spec/models/service_deployment_spec.rb new file mode 100644 index 00000000..384d4ebd --- /dev/null +++ b/spec/models/service_deployment_spec.rb @@ -0,0 +1,100 @@ +require 'rails_helper' + +describe ServiceDeployment do + + it { should validate_presence_of :service } + + describe 'both completed and failed true' do + it 'will not be valid' do + sd = build :service_deployment, completed_at: DateTime.now, failed_at: DateTime.now + expect(sd).to_not be_valid + end + end + + describe '#completed?' do + it 'returns true if completed' do + sd = create :service_deployment, completed_at: DateTime.now + expect(sd).to be_completed + end + + it 'returns false if not completed' do + sd = create :service_deployment, completed_at: nil + expect(sd).to_not be_completed + end + end + + describe '#failed?' do + it 'returns true if failed' do + sd = create :service_deployment, failed_at: DateTime.now + expect(sd).to be_failed + end + + it 'returns false if not failed' do + sd = create :service_deployment, failed_at: nil + expect(sd).to_not be_failed + end + end + + describe '#finished?' do + it 'returns true if completed' do + sd = create :service_deployment, completed_at: DateTime.now + expect(sd).to be_finished + end + + it 'returns true if failed' do + sd = create :service_deployment, failed_at: DateTime.now + expect(sd).to be_finished + end + + it 'returns false if not failed or completed' do + sd = create :service_deployment, failed_at: nil, completed_at: nil + expect(sd).to_not be_finished + end + end + + let(:example_time) { Time.zone.local(2004, 11, 24, 01, 04, 44) } + describe 'fail!' do + it 'sets failed' do + sd = create :service_deployment + expect(sd).to_not be_failed + sd.fail! + expect(sd).to be_failed + end + + it 'sets failed_at to current time' do + travel_to example_time do + sd = create :service_deployment + expect(sd.failed_at).to be_nil + sd.fail! + expect(sd.failed_at).to eq example_time + end + end + end + + describe 'complete!' do + it 'sets completed' do + sd = create :service_deployment + expect(sd).to_not be_completed + sd.complete! + expect(sd).to be_completed + end + + it 'sets completed_at to current time' do + travel_to example_time do + sd = create :service_deployment + expect(sd.completed_at).to be_nil + sd.complete! + expect(sd.completed_at).to eq example_time + end + end + end + + describe '.unfinished' do + it 'returns unfinished deployments' do + sd1 = create :service_deployment, completed_at: nil + sd2 = create :service_deployment, completed_at: DateTime.now + + expect(ServiceDeployment.unfinished).to eq [sd1] + end + end +end diff --git a/spec/models/service_spec.rb b/spec/models/service_spec.rb index 5ce09feb..610aff99 100644 --- a/spec/models/service_spec.rb +++ b/spec/models/service_spec.rb @@ -196,4 +196,18 @@ end end end + + describe '#service_deployments' do + it 'finds the service deployments for the current district' do + s1 = create :service + s1d1 = create :service_deployment, service: s1 + s1d2 = create :service_deployment, service: s1 + + s2 = create :service + s2d1 = create :service_deployment, service: s2 + + expect(s1.service_deployments).to eq [s1d1, s1d2] + expect(s2.service_deployments).to eq [s2d1] + end + end end